From 4c8d1eeb4411664c15192d640581316a3f19a0c0 Mon Sep 17 00:00:00 2001 From: Jeffrey Townsend Date: Wed, 13 Jun 2018 16:23:56 +0000 Subject: [PATCH] The 3.18.25 kernel for ARM64 is no longer in user. All ARM64 platforms have moved to 4.9 LTS. --- packages/base/any/kernels/3.18.25/.gitignore | 4 - .../3.18.25/configs/arm64-all/.gitignore | 1 - .../3.18.25/configs/arm64-all/Makefile | 28 - .../configs/arm64-all/arm64-all.config | 3037 - .../3.18.25/configs/x86_64-all/.gitignore | 1 - .../3.18.25/configs/x86_64-all/Makefile | 42 - .../configs/x86_64-all/x86_64-all.config | 3581 - packages/base/any/kernels/3.18.25/kconfig.mk | 31 - ...-set-for-booting-ls2088rdb-with-vfio.patch | 101360 --------------- ...-and-fsl-mc-support-based-on-3.18.25.patch | 35045 ----- ...es-for-nxp-arm64-ls2080ardb-based-on.patch | 17982 --- ...add-nxp-arm64-ls2088ardb-device-tree.patch | 1116 - .../any/kernels/3.18.25/patches/aufs.patch | 33877 ----- ...some-kernel-patches-based-on-3.18.25.patch | 11095 -- ...river-support-intel-igb-bcm54616-phy.patch | 67 - .../3.18.25/patches/ls2_mc_console.patch | 327 - .../base/any/kernels/3.18.25/patches/series | 3 - .../any/kernels/3.18.25/patches/series.arm64 | 2 - 18 files changed, 207599 deletions(-) delete mode 100644 packages/base/any/kernels/3.18.25/.gitignore delete mode 100644 packages/base/any/kernels/3.18.25/configs/arm64-all/.gitignore delete mode 100644 packages/base/any/kernels/3.18.25/configs/arm64-all/Makefile delete mode 100644 packages/base/any/kernels/3.18.25/configs/arm64-all/arm64-all.config delete mode 100644 packages/base/any/kernels/3.18.25/configs/x86_64-all/.gitignore delete mode 100644 packages/base/any/kernels/3.18.25/configs/x86_64-all/Makefile delete mode 100644 packages/base/any/kernels/3.18.25/configs/x86_64-all/x86_64-all.config delete mode 100644 packages/base/any/kernels/3.18.25/kconfig.mk delete mode 100644 packages/base/any/kernels/3.18.25/patches/0001-Patch-set-for-booting-ls2088rdb-with-vfio.patch delete mode 100644 packages/base/any/kernels/3.18.25/patches/add-fsl-dpaa2-and-fsl-mc-support-based-on-3.18.25.patch delete mode 100644 packages/base/any/kernels/3.18.25/patches/add-kernel-patches-for-nxp-arm64-ls2080ardb-based-on.patch delete mode 100644 packages/base/any/kernels/3.18.25/patches/add-nxp-arm64-ls2088ardb-device-tree.patch delete mode 100644 packages/base/any/kernels/3.18.25/patches/aufs.patch delete mode 100644 packages/base/any/kernels/3.18.25/patches/backport-some-kernel-patches-based-on-3.18.25.patch delete mode 100644 packages/base/any/kernels/3.18.25/patches/driver-support-intel-igb-bcm54616-phy.patch delete mode 100644 packages/base/any/kernels/3.18.25/patches/ls2_mc_console.patch delete mode 100644 packages/base/any/kernels/3.18.25/patches/series delete mode 100644 packages/base/any/kernels/3.18.25/patches/series.arm64 diff --git a/packages/base/any/kernels/3.18.25/.gitignore b/packages/base/any/kernels/3.18.25/.gitignore deleted file mode 100644 index b9ef4135..00000000 --- a/packages/base/any/kernels/3.18.25/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -linux-3.18.25 -linux-3.18.25-mbuild -linux-3.18.25-dtbs - diff --git a/packages/base/any/kernels/3.18.25/configs/arm64-all/.gitignore b/packages/base/any/kernels/3.18.25/configs/arm64-all/.gitignore deleted file mode 100644 index 6a57178c..00000000 --- a/packages/base/any/kernels/3.18.25/configs/arm64-all/.gitignore +++ /dev/null @@ -1 +0,0 @@ -kernel-* diff --git a/packages/base/any/kernels/3.18.25/configs/arm64-all/Makefile b/packages/base/any/kernels/3.18.25/configs/arm64-all/Makefile deleted file mode 100644 index f8c7663c..00000000 --- a/packages/base/any/kernels/3.18.25/configs/arm64-all/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -############################################################ -# -# Default 3.18.25 configuration for arm64 platforms. -# -############################################################ -THIS_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) -include $(ONL)/make/config.mk - -ifndef K_TARGET_DIR -K_TARGET_DIR := $(THIS_DIR) -endif - -MODSYNCLIST_EXTRA = arch/arm64/include arch/arm64/Makefile arch/arm64/lib -K_PATCH_SERIES=series.arm64 - -include ../../kconfig.mk -K_CONFIG := arm64-all.config -K_BUILD_TARGET := Image Image.gz arm64-nxp-ls2080ardb-r0.dtb arm64-nxp-ls2088ardb-r1.dtb -K_COPY_SRC := arch/arm64/boot/Image -K_COPY_GZIP := 1 -ifndef K_COPY_DST -K_COPY_DST := kernel-3.18.25-arm64-all.bin.gz -endif - -export ARCH=arm64 -DTS_LIST := arm64-nxp-ls2080ardb-r0 - -include $(ONL)/make/kbuild.mk diff --git a/packages/base/any/kernels/3.18.25/configs/arm64-all/arm64-all.config b/packages/base/any/kernels/3.18.25/configs/arm64-all/arm64-all.config deleted file mode 100644 index a023c63c..00000000 --- a/packages/base/any/kernels/3.18.25/configs/arm64-all/arm64-all.config +++ /dev/null @@ -1,3037 +0,0 @@ -# -# Automatically generated file; DO NOT EDIT. -# Linux/arm64 3.18.25 Kernel Configuration -# -CONFIG_ARM64=y -CONFIG_64BIT=y -CONFIG_ARCH_PHYS_ADDR_T_64BIT=y -CONFIG_MMU=y -CONFIG_STACKTRACE_SUPPORT=y -CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000 -CONFIG_LOCKDEP_SUPPORT=y -CONFIG_TRACE_IRQFLAGS_SUPPORT=y -CONFIG_RWSEM_XCHGADD_ALGORITHM=y -CONFIG_GENERIC_HWEIGHT=y -CONFIG_GENERIC_CSUM=y -CONFIG_GENERIC_CALIBRATE_DELAY=y -CONFIG_ZONE_DMA=y -CONFIG_HAVE_GENERIC_RCU_GUP=y -CONFIG_ARCH_DMA_ADDR_T_64BIT=y -CONFIG_NEED_DMA_MAP_STATE=y -CONFIG_NEED_SG_DMA_LENGTH=y -CONFIG_SWIOTLB=y -CONFIG_IOMMU_HELPER=y -CONFIG_KERNEL_MODE_NEON=y -CONFIG_FIX_EARLYCON_MEM=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="aarch64-linux-gnu-" -# CONFIG_COMPILE_TEST is not set -CONFIG_LOCALVERSION="" -CONFIG_LOCALVERSION_AUTO=y -CONFIG_DEFAULT_HOSTNAME="(none)" -CONFIG_SWAP=y -CONFIG_SYSVIPC=y -CONFIG_SYSVIPC_SYSCTL=y -CONFIG_POSIX_MQUEUE=y -CONFIG_POSIX_MQUEUE_SYSCTL=y -CONFIG_CROSS_MEMORY_ATTACH=y -# CONFIG_FHANDLE is not set -CONFIG_USELIB=y -CONFIG_AUDIT=y -CONFIG_HAVE_ARCH_AUDITSYSCALL=y -# CONFIG_AUDITSYSCALL 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_IRQ_DOMAIN_HIERARCHY=y -CONFIG_GENERIC_MSI_IRQ=y -CONFIG_GENERIC_MSI_IRQ_DOMAIN=y -CONFIG_HANDLE_DOMAIN_IRQ=y -# CONFIG_IRQ_DOMAIN_DEBUG is not set -CONFIG_SPARSE_IRQ=y -CONFIG_GENERIC_TIME_VSYSCALL=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 is not set -CONFIG_HIGH_RES_TIMERS=y - -# -# CPU/Task time and stats accounting -# -CONFIG_TICK_CPU_ACCOUNTING=y -# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set -CONFIG_BSD_PROCESS_ACCT=y -CONFIG_BSD_PROCESS_ACCT_V3=y -CONFIG_TASKSTATS=y -CONFIG_TASK_DELAY_ACCT=y -CONFIG_TASK_XACCT=y -CONFIG_TASK_IO_ACCOUNTING=y - -# -# RCU Subsystem -# -CONFIG_TREE_PREEMPT_RCU=y -CONFIG_PREEMPT_RCU=y -# CONFIG_TASKS_RCU is not set -CONFIG_RCU_STALL_COMMON=y -# CONFIG_RCU_USER_QS is not set -CONFIG_RCU_FANOUT=64 -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_BOOST is not set -# CONFIG_RCU_NOCB_CPU is not set -CONFIG_BUILD_BIN2C=y -CONFIG_IKCONFIG=y -CONFIG_IKCONFIG_PROC=y -CONFIG_LOG_BUF_SHIFT=14 -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=y -CONFIG_MEMCG=y -CONFIG_MEMCG_SWAP=y -CONFIG_MEMCG_SWAP_ENABLED=y -CONFIG_MEMCG_KMEM=y -CONFIG_CGROUP_HUGETLB=y -# CONFIG_CGROUP_PERF 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 is not set -# CONFIG_IPC_NS is not set -# CONFIG_USER_NS is not set -# CONFIG_PID_NS is not set -CONFIG_NET_NS=y -CONFIG_SCHED_AUTOGROUP=y -# 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_SYSCTL_EXCEPTION_TRACE=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=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_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=y -# CONFIG_DEBUG_PERF_USE_VMALLOC is not set -CONFIG_VM_EVENT_COUNTERS=y -# CONFIG_COMPAT_BRK is not set -CONFIG_SLAB=y -# CONFIG_SLUB is not set -# CONFIG_SYSTEM_TRUSTED_KEYRING is not set -CONFIG_PROFILING=y -CONFIG_JUMP_LABEL=y -# CONFIG_UPROBES is not set -# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set -CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y -CONFIG_HAVE_ARCH_TRACEHOOK=y -CONFIG_HAVE_DMA_ATTRS=y -CONFIG_HAVE_DMA_CONTIGUOUS=y -CONFIG_GENERIC_SMP_IDLE_THREAD=y -CONFIG_HAVE_CLK=y -CONFIG_HAVE_DMA_API_DEBUG=y -CONFIG_HAVE_HW_BREAKPOINT=y -CONFIG_HAVE_PERF_REGS=y -CONFIG_HAVE_PERF_USER_STACK_DUMP=y -CONFIG_HAVE_ARCH_JUMP_LABEL=y -CONFIG_HAVE_RCU_TABLE_FREE=y -CONFIG_ARCH_WANT_COMPAT_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_ARCH_TRANSPARENT_HUGEPAGE=y -CONFIG_MODULES_USE_ELF_RELA=y -CONFIG_CLONE_BACKWARDS=y -CONFIG_OLD_SIGSUSPEND3=y -CONFIG_COMPAT_OLD_SIGACTION=y - -# -# GCOV-based kernel profiling -# -# CONFIG_GCOV_KERNEL is not set -CONFIG_HAVE_GENERIC_DMA_COHERENT=y -CONFIG_SLABINFO=y -CONFIG_RT_MUTEXES=y -CONFIG_BASE_SMALL=0 -CONFIG_MODULES=y -CONFIG_MODULE_FORCE_LOAD=y -CONFIG_MODULE_UNLOAD=y -# CONFIG_MODULE_FORCE_UNLOAD is not set -CONFIG_MODVERSIONS=y -# 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_BLK_DEV_BSG is not set -# CONFIG_BLK_DEV_BSGLIB is not set -# CONFIG_BLK_DEV_INTEGRITY is not set -# CONFIG_BLK_CMDLINE_PARSER is not set - -# -# 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 is not set -CONFIG_BLOCK_COMPAT=y - -# -# IO Schedulers -# -CONFIG_IOSCHED_NOOP=y -# CONFIG_IOSCHED_DEADLINE is not set -CONFIG_IOSCHED_CFQ=y -CONFIG_DEFAULT_CFQ=y -# CONFIG_DEFAULT_NOOP is not set -CONFIG_DEFAULT_IOSCHED="cfq" -CONFIG_PREEMPT_NOTIFIERS=y -CONFIG_UNINLINE_SPIN_UNLOCK=y -CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y -CONFIG_MUTEX_SPIN_ON_OWNER=y -CONFIG_RWSEM_SPIN_ON_OWNER=y -CONFIG_FREEZER=y - -# -# Platform selection -# -CONFIG_ARCH_THUNDER=y -CONFIG_ARCH_VEXPRESS=y -CONFIG_ARCH_XGENE=y -CONFIG_ARCH_LAYERSCAPE=y - -# -# Bus support -# -CONFIG_ARM_AMBA=y -CONFIG_PCI=y -CONFIG_PCI_DOMAINS=y -CONFIG_PCI_DOMAINS_GENERIC=y -CONFIG_PCI_SYSCALL=y -CONFIG_PCI_MSI=y -CONFIG_PCI_MSI_IRQ_DOMAIN=y -# 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_PCIE_DW=y -# CONFIG_PCI_HOST_GENERIC is not set -CONFIG_PCI_XGENE=y -CONFIG_PCI_XGENE_MSI=y -CONFIG_PCI_LAYERSCAPE=y -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_HOTPLUG_PCI is not set - -# -# Kernel Features -# - -# -# ARM errata workarounds via the alternatives framework -# -CONFIG_ARM64_ERRATUM_826319=y -CONFIG_ARM64_ERRATUM_827319=y -CONFIG_ARM64_ERRATUM_824069=y -CONFIG_ARM64_ERRATUM_819472=y -CONFIG_ARM64_ERRATUM_832075=y -CONFIG_ARM64_ERRATUM_845719=y -CONFIG_ARM64_4K_PAGES=y -# CONFIG_ARM64_64K_PAGES is not set -# CONFIG_ARM64_VA_BITS_39 is not set -CONFIG_ARM64_VA_BITS_48=y -CONFIG_ARM64_VA_BITS=48 -CONFIG_ARM64_PGTABLE_LEVELS=4 -# CONFIG_CPU_BIG_ENDIAN is not set -CONFIG_SMP=y -# CONFIG_SCHED_MC is not set -# CONFIG_SCHED_SMT is not set -CONFIG_NR_CPUS=64 -CONFIG_HOTPLUG_CPU=y -# CONFIG_PREEMPT_NONE is not set -# CONFIG_PREEMPT_VOLUNTARY is not set -CONFIG_PREEMPT=y -CONFIG_PREEMPT_COUNT=y -CONFIG_HZ=100 -CONFIG_ARCH_HAS_HOLES_MEMORYMODEL=y -CONFIG_ARCH_SPARSEMEM_ENABLE=y -CONFIG_ARCH_SPARSEMEM_DEFAULT=y -CONFIG_ARCH_SELECT_MEMORY_MODEL=y -CONFIG_HAVE_ARCH_PFN_VALID=y -CONFIG_HW_PERF_EVENTS=y -CONFIG_SYS_SUPPORTS_HUGETLBFS=y -CONFIG_ARCH_WANT_GENERAL_HUGETLB=y -CONFIG_ARCH_WANT_HUGE_PMD_SHARE=y -CONFIG_ARCH_HAS_CACHE_LINE_SIZE=y -CONFIG_SELECT_MEMORY_MODEL=y -CONFIG_SPARSEMEM_MANUAL=y -CONFIG_SPARSEMEM=y -CONFIG_HAVE_MEMORY_PRESENT=y -CONFIG_SPARSEMEM_EXTREME=y -CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y -CONFIG_SPARSEMEM_VMEMMAP=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_MEMORY_BALLOON=y -CONFIG_BALLOON_COMPACTION=y -CONFIG_COMPACTION=y -CONFIG_MIGRATION=y -CONFIG_PHYS_ADDR_T_64BIT=y -CONFIG_ZONE_DMA_FLAG=1 -CONFIG_BOUNCE=y -CONFIG_MMU_NOTIFIER=y -CONFIG_KSM=y -CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 -CONFIG_TRANSPARENT_HUGEPAGE=y -CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y -# CONFIG_TRANSPARENT_HUGEPAGE_MADVISE is not set -# 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_GENERIC_EARLY_IOREMAP=y -# CONFIG_XEN is not set -CONFIG_FORCE_MAX_ZONEORDER=11 - -# -# Boot options -# -CONFIG_CMDLINE="console=ttyAMA0" -# CONFIG_CMDLINE_FORCE is not set -CONFIG_EFI_STUB=y -CONFIG_EFI=y - -# -# Userspace binary formats -# -CONFIG_BINFMT_ELF=y -CONFIG_COMPAT_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 -CONFIG_COMPAT=y -CONFIG_SYSVIPC_COMPAT=y - -# -# Power management options -# -CONFIG_SUSPEND=y -CONFIG_SUSPEND_FREEZER=y -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_PM_CLK=y -# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set -CONFIG_CPU_PM=y -CONFIG_ARCH_SUSPEND_POSSIBLE=y -CONFIG_ARM64_CPU_SUSPEND=y - -# -# CPU Power Management -# - -# -# CPU Idle -# -# CONFIG_CPU_IDLE is not set -# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set - -# -# 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=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_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_CONSERVATIVE=y -# CONFIG_CPUFREQ_DT is not set - -# -# ARM CPU frequency scaling drivers -# -# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set -CONFIG_ARM64_ERRATUM_843419=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_ALGO=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_XFRM_IPCOMP=y -# CONFIG_NET_KEY is not set -CONFIG_INET=y -CONFIG_IP_MULTICAST=y -CONFIG_IP_ADVANCED_ROUTER=y -CONFIG_IP_FIB_TRIE_STATS=y -CONFIG_IP_MULTIPLE_TABLES=y -CONFIG_IP_ROUTE_MULTIPATH=y -# CONFIG_IP_ROUTE_VERBOSE is not set -CONFIG_IP_PNP=y -CONFIG_IP_PNP_DHCP=y -CONFIG_IP_PNP_BOOTP=y -# 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=y -# CONFIG_IP_MROUTE_MULTIPLE_TABLES is not set -# CONFIG_IP_PIMSM_V1 is not set -CONFIG_IP_PIMSM_V2=y -# 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=y -CONFIG_INET_XFRM_MODE_TRANSPORT=y -CONFIG_INET_XFRM_MODE_TUNNEL=y -CONFIG_INET_XFRM_MODE_BEET=y -# CONFIG_INET_LRO is not set -CONFIG_INET_DIAG=y -CONFIG_INET_TCP_DIAG=y -# CONFIG_INET_UDP_DIAG is not set -CONFIG_TCP_CONG_ADVANCED=y -CONFIG_TCP_CONG_BIC=y -CONFIG_TCP_CONG_CUBIC=y -CONFIG_TCP_CONG_WESTWOOD=y -CONFIG_TCP_CONG_HTCP=y -# CONFIG_TCP_CONG_HSTCP is not set -# CONFIG_TCP_CONG_HYBLA is not set -# CONFIG_TCP_CONG_VEGAS is not set -# CONFIG_TCP_CONG_SCALABLE is not set -# CONFIG_TCP_CONG_LP is not set -# CONFIG_TCP_CONG_VENO is not set -# CONFIG_TCP_CONG_YEAH is not set -# CONFIG_TCP_CONG_ILLINOIS is not set -# CONFIG_TCP_CONG_DCTCP is not set -# CONFIG_DEFAULT_BIC is not set -CONFIG_DEFAULT_CUBIC=y -# CONFIG_DEFAULT_HTCP is not set -# CONFIG_DEFAULT_WESTWOOD is not set -# CONFIG_DEFAULT_RENO is not set -CONFIG_DEFAULT_TCP_CONG="cubic" -# CONFIG_TCP_MD5SIG is not set -CONFIG_IPV6=y -CONFIG_IPV6_ROUTER_PREF=y -CONFIG_IPV6_ROUTE_INFO=y -CONFIG_IPV6_OPTIMISTIC_DAD=y -CONFIG_INET6_AH=y -CONFIG_INET6_ESP=y -CONFIG_INET6_IPCOMP=y -CONFIG_IPV6_MIP6=y -CONFIG_INET6_XFRM_TUNNEL=y -CONFIG_INET6_TUNNEL=y -CONFIG_INET6_XFRM_MODE_TRANSPORT=y -CONFIG_INET6_XFRM_MODE_TUNNEL=y -CONFIG_INET6_XFRM_MODE_BEET=y -CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION=y -# CONFIG_IPV6_VTI is not set -CONFIG_IPV6_SIT=y -# CONFIG_IPV6_SIT_6RD is not set -CONFIG_IPV6_NDISC_NODETYPE=y -CONFIG_IPV6_TUNNEL=y -# CONFIG_IPV6_GRE is not set -CONFIG_IPV6_MULTIPLE_TABLES=y -CONFIG_IPV6_SUBTREES=y -# CONFIG_IPV6_MROUTE is not set -# CONFIG_NETLABEL is not set -# CONFIG_NETWORK_SECMARK is not set -CONFIG_NET_PTP_CLASSIFY=y -# 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_STP=m -CONFIG_BRIDGE=m -CONFIG_BRIDGE_IGMP_SNOOPING=y -# CONFIG_BRIDGE_VLAN_FILTERING is not set -CONFIG_HAVE_NET_DSA=y -CONFIG_VLAN_8021Q=y -# CONFIG_VLAN_8021Q_GVRP is not set -# CONFIG_VLAN_8021Q_MVRP is not set -# CONFIG_DECNET is not set -CONFIG_LLC=m -# 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=y - -# -# Queueing/Scheduling -# -# CONFIG_NET_SCH_CBQ is not set -# CONFIG_NET_SCH_HTB is not set -# CONFIG_NET_SCH_HFSC is not set -# CONFIG_NET_SCH_PRIO is not set -# CONFIG_NET_SCH_MULTIQ is not set -# CONFIG_NET_SCH_RED is not set -# CONFIG_NET_SCH_SFB is not set -# CONFIG_NET_SCH_SFQ is not set -# CONFIG_NET_SCH_TEQL is not set -# CONFIG_NET_SCH_TBF is not set -# CONFIG_NET_SCH_GRED is not set -# CONFIG_NET_SCH_DSMARK is not set -# CONFIG_NET_SCH_NETEM is not set -# CONFIG_NET_SCH_DRR is not set -# CONFIG_NET_SCH_MQPRIO is not set -# CONFIG_NET_SCH_CHOKE is not set -# CONFIG_NET_SCH_QFQ is not set -# CONFIG_NET_SCH_CODEL is not set -# CONFIG_NET_SCH_FQ_CODEL is not set -# CONFIG_NET_SCH_FQ is not set -# CONFIG_NET_SCH_HHF is not set -# CONFIG_NET_SCH_PIE is not set -# CONFIG_NET_SCH_PLUG is not set - -# -# Classification -# -# CONFIG_NET_CLS_BASIC is not set -# CONFIG_NET_CLS_TCINDEX is not set -# CONFIG_NET_CLS_ROUTE4 is not set -# CONFIG_NET_CLS_FW is not set -# CONFIG_NET_CLS_U32 is not set -# CONFIG_NET_CLS_RSVP is not set -# CONFIG_NET_CLS_RSVP6 is not set -# CONFIG_NET_CLS_FLOW is not set -# CONFIG_NET_CLS_CGROUP is not set -# CONFIG_NET_CLS_BPF is not set -# CONFIG_NET_EMATCH is not set -# CONFIG_NET_CLS_ACT is not set -CONFIG_NET_SCH_FIFO=y -CONFIG_DCB=y -CONFIG_DNS_RESOLVER=y -# 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=y -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_FIB_RULES=y -# CONFIG_WIRELESS is not set -# CONFIG_WIMAX is not set -# CONFIG_RFKILL is not set -# CONFIG_RFKILL_REGULATOR is not set -CONFIG_NET_9P=y -CONFIG_NET_9P_VIRTIO=y -# CONFIG_NET_9P_DEBUG 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=y -CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" -CONFIG_DEVTMPFS=y -CONFIG_DEVTMPFS_MOUNT=y -CONFIG_STANDALONE=y -CONFIG_PREVENT_FIRMWARE_BUILD=y -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_GENERIC_CPU_AUTOPROBE=y -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_ARM_CCN is not set -CONFIG_VEXPRESS_CONFIG=y -# 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_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=y -# 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 - -# -# RAM/ROM/Flash chip drivers -# -CONFIG_MTD_CFI=y -# CONFIG_MTD_JEDECPROBE is not set -CONFIG_MTD_GEN_PROBE=y -CONFIG_MTD_CFI_ADV_OPTIONS=y -CONFIG_MTD_CFI_NOSWAP=y -# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set -# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set -# CONFIG_MTD_CFI_GEOMETRY 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_OTP is not set -CONFIG_MTD_CFI_INTELEXT=y -CONFIG_MTD_CFI_AMDSTD=y -CONFIG_MTD_CFI_STAA=y -CONFIG_MTD_CFI_UTIL=y -CONFIG_MTD_RAM=y -# 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_PHYSMAP=y -# CONFIG_MTD_PHYSMAP_COMPAT is not set -CONFIG_MTD_PHYSMAP_OF=y -# CONFIG_MTD_INTEL_VR_NOR is not set -CONFIG_MTD_PLATRAM=y - -# -# Self-contained MTD device drivers -# -# CONFIG_MTD_PMC551 is not set -# CONFIG_MTD_DATAFLASH is not set -CONFIG_MTD_M25P80=y -# 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_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=y -# CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set -CONFIG_MTD_NAND_IDS=y -# 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_MTD_NAND_FSL_IFC=y -# CONFIG_MTD_ONENAND is not set - -# -# LPDDR & LPDDR2 PCM memory drivers -# -# CONFIG_MTD_LPDDR is not set -CONFIG_MTD_SPI_NOR=y -CONFIG_MTD_SPI_NOR_USE_4K_SECTORS=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_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=y -CONFIG_BLK_DEV_LOOP_MIN_COUNT=8 -# CONFIG_BLK_DEV_CRYPTOLOOP 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_SKD 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=262144 -# CONFIG_BLK_DEV_XIP is not set -# CONFIG_CDROM_PKTCDVD is not set -# CONFIG_ATA_OVER_ETH is not set -CONFIG_VIRTIO_BLK=y -# 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_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_VEXPRESS_SYSCFG=y -# CONFIG_C2PORT is not set - -# -# EEPROM support -# -CONFIG_EEPROM_AT24=y -CONFIG_EEPROM_AT25=y -# 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_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_GENWQE is not set -# CONFIG_ECHO is not set -# CONFIG_CXL_BASE 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_LOWLEVEL_PCMCIA is not set -# CONFIG_SCSI_DH is not set -# CONFIG_SCSI_OSD_INITIATOR is not set -CONFIG_HAVE_PATA_PLATFORM=y -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=y -CONFIG_SATA_AHCI_PLATFORM=y -CONFIG_AHCI_XGENE=y -# CONFIG_SATA_INIC162X is not set -# CONFIG_SATA_ACARD_AHCI is not set -# CONFIG_SATA_SIL24 is not set -CONFIG_ATA_SFF=y - -# -# SFF controllers with custom DMA interface -# -# CONFIG_PDC_ADMA is not set -# CONFIG_SATA_QSTOR is not set -# CONFIG_SATA_SX4 is not set -CONFIG_ATA_BMDMA=y - -# -# SATA SFF controllers with BMDMA -# -# CONFIG_ATA_PIIX is not set -# CONFIG_SATA_MV is not set -# CONFIG_SATA_NV is not set -# CONFIG_SATA_PROMISE is not set -# CONFIG_SATA_SIL is not set -# CONFIG_SATA_SIS is not set -# CONFIG_SATA_SVW is not set -# CONFIG_SATA_ULI is not set -# CONFIG_SATA_VIA is not set -# CONFIG_SATA_VITESSE is not set - -# -# PATA SFF controllers with BMDMA -# -# CONFIG_PATA_ALI is not set -# CONFIG_PATA_AMD is not set -# CONFIG_PATA_ARTOP is not set -# CONFIG_PATA_ATIIXP is not set -# CONFIG_PATA_ATP867X is not set -# CONFIG_PATA_CMD64X is not set -# CONFIG_PATA_CYPRESS is not set -# CONFIG_PATA_EFAR is not set -# CONFIG_PATA_HPT366 is not set -# CONFIG_PATA_HPT37X is not set -# CONFIG_PATA_HPT3X2N is not set -# CONFIG_PATA_HPT3X3 is not set -# CONFIG_PATA_IT8213 is not set -# CONFIG_PATA_IT821X is not set -# CONFIG_PATA_JMICRON is not set -# CONFIG_PATA_MARVELL is not set -# CONFIG_PATA_NETCELL is not set -# CONFIG_PATA_NINJA32 is not set -# CONFIG_PATA_NS87415 is not set -# CONFIG_PATA_OLDPIIX is not set -# CONFIG_PATA_OPTIDMA is not set -# CONFIG_PATA_PDC2027X is not set -# CONFIG_PATA_PDC_OLD is not set -# CONFIG_PATA_RADISYS is not set -# CONFIG_PATA_RDC is not set -# CONFIG_PATA_SCH is not set -# CONFIG_PATA_SERVERWORKS is not set -# CONFIG_PATA_SIL680 is not set -# CONFIG_PATA_SIS is not set -# CONFIG_PATA_TOSHIBA is not set -# CONFIG_PATA_TRIFLEX is not set -# CONFIG_PATA_VIA is not set -# CONFIG_PATA_WINBOND is not set - -# -# PIO-only SFF controllers -# -# CONFIG_PATA_CMD640_PCI is not set -# CONFIG_PATA_MPIIX is not set -# CONFIG_PATA_NS87410 is not set -# CONFIG_PATA_OPTI is not set -# CONFIG_PATA_PLATFORM is not set -# CONFIG_PATA_RZ1000 is not set - -# -# Generic fallback / legacy drivers -# -# CONFIG_ATA_GENERIC is not set -# CONFIG_PATA_LEGACY 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_MII=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=y -# CONFIG_MACVTAP 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=y -# CONFIG_VETH is not set -CONFIG_VIRTIO_NET=y -# 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=y -# CONFIG_VORTEX is not set -# CONFIG_TYPHOON is not set -CONFIG_NET_VENDOR_ADAPTEC=y -# CONFIG_ADAPTEC_STARFIRE is not set -CONFIG_NET_VENDOR_AGERE=y -# CONFIG_ET131X is not set -CONFIG_NET_VENDOR_ALTEON=y -# CONFIG_ACENIC is not set -# CONFIG_ALTERA_TSE is not set -CONFIG_NET_VENDOR_AMD=y -# CONFIG_AMD8111_ETH is not set -# CONFIG_PCNET32 is not set -# CONFIG_AMD_XGBE is not set -CONFIG_NET_XGENE=y -CONFIG_NET_VENDOR_ARC=y -# CONFIG_ARC_EMAC is not set -# CONFIG_EMAC_ROCKCHIP is not set -CONFIG_NET_VENDOR_ATHEROS=y -# CONFIG_ATL2 is not set -# CONFIG_ATL1 is not set -# CONFIG_ATL1E is not set -# CONFIG_ATL1C is not set -# CONFIG_ALX is not set -CONFIG_NET_VENDOR_BROADCOM=y -# CONFIG_B44 is not set -# CONFIG_BCMGENET is not set -# CONFIG_BNX2 is not set -# CONFIG_CNIC is not set -# CONFIG_TIGON3 is not set -# CONFIG_BNX2X is not set -# CONFIG_SYSTEMPORT is not set -CONFIG_NET_VENDOR_BROCADE=y -# CONFIG_BNA is not set -CONFIG_NET_VENDOR_CHELSIO=y -# CONFIG_CHELSIO_T1 is not set -# CONFIG_CHELSIO_T3 is not set -# CONFIG_CHELSIO_T4 is not set -# CONFIG_CHELSIO_T4VF is not set -CONFIG_NET_VENDOR_CISCO=y -# CONFIG_ENIC is not set -# CONFIG_DNET is not set -CONFIG_NET_VENDOR_DEC=y -# CONFIG_NET_TULIP is not set -CONFIG_NET_VENDOR_DLINK=y -# CONFIG_DL2K is not set -# CONFIG_SUNDANCE is not set -CONFIG_NET_VENDOR_EMULEX=y -# CONFIG_BE2NET is not set -CONFIG_NET_VENDOR_EXAR=y -# CONFIG_S2IO is not set -# CONFIG_VXGE is not set -CONFIG_NET_VENDOR_FREESCALE=y -# CONFIG_FSL_PQ_MDIO is not set -CONFIG_FSL_XGMAC_MDIO=y -CONFIG_NET_VENDOR_HP=y -# CONFIG_HP100 is not set -CONFIG_NET_VENDOR_INTEL=y -# CONFIG_E100 is not set -CONFIG_E1000=y -CONFIG_E1000E=y -# CONFIG_IGB is not set -# CONFIG_IGBVF is not set -# CONFIG_IXGB is not set -# CONFIG_IXGBE is not set -# CONFIG_IXGBEVF is not set -# CONFIG_I40E is not set -# CONFIG_I40EVF is not set -# CONFIG_FM10K is not set -CONFIG_NET_VENDOR_I825XX=y -# CONFIG_IP1000 is not set -# CONFIG_JME is not set -CONFIG_NET_VENDOR_MARVELL=y -# CONFIG_MVMDIO is not set -# CONFIG_SKGE is not set -# CONFIG_SKY2 is not set -CONFIG_NET_VENDOR_MELLANOX=y -# CONFIG_MLX4_EN is not set -# CONFIG_MLX4_CORE is not set -# CONFIG_MLX5_CORE is not set -CONFIG_NET_VENDOR_MICREL=y -# CONFIG_KS8842 is not set -# CONFIG_KS8851 is not set -# CONFIG_KS8851_MLL is not set -# CONFIG_KSZ884X_PCI is not set -CONFIG_NET_VENDOR_MICROCHIP=y -# CONFIG_ENC28J60 is not set -CONFIG_NET_VENDOR_MYRI=y -# CONFIG_MYRI10GE is not set -# CONFIG_FEALNX is not set -CONFIG_NET_VENDOR_NATSEMI=y -# CONFIG_NATSEMI is not set -# CONFIG_NS83820 is not set -CONFIG_NET_VENDOR_8390=y -# CONFIG_NE2K_PCI is not set -CONFIG_NET_VENDOR_NVIDIA=y -# CONFIG_FORCEDETH is not set -CONFIG_NET_VENDOR_OKI=y -# CONFIG_ETHOC is not set -CONFIG_NET_PACKET_ENGINE=y -# CONFIG_HAMACHI is not set -# CONFIG_YELLOWFIN is not set -CONFIG_NET_VENDOR_QLOGIC=y -# CONFIG_QLA3XXX is not set -# CONFIG_QLCNIC is not set -# CONFIG_QLGE is not set -# CONFIG_NETXEN_NIC is not set -CONFIG_NET_VENDOR_QUALCOMM=y -# CONFIG_QCA7000 is not set -CONFIG_NET_VENDOR_REALTEK=y -# CONFIG_8139CP is not set -# CONFIG_8139TOO is not set -# CONFIG_R8169 is not set -CONFIG_NET_VENDOR_RDC=y -# CONFIG_R6040 is not set -CONFIG_NET_VENDOR_SAMSUNG=y -# CONFIG_SXGBE_ETH is not set -CONFIG_NET_VENDOR_SEEQ=y -CONFIG_NET_VENDOR_SILAN=y -# CONFIG_SC92031 is not set -CONFIG_NET_VENDOR_SIS=y -# CONFIG_SIS900 is not set -# CONFIG_SIS190 is not set -# CONFIG_SFC is not set -CONFIG_NET_VENDOR_SMSC=y -CONFIG_SMC91X=y -# CONFIG_EPIC100 is not set -CONFIG_SMSC911X=y -# CONFIG_SMSC911X_ARCH_HOOKS is not set -# CONFIG_SMSC9420 is not set -CONFIG_NET_VENDOR_STMICRO=y -# CONFIG_STMMAC_ETH is not set -CONFIG_NET_VENDOR_SUN=y -# CONFIG_HAPPYMEAL is not set -# CONFIG_SUNGEM is not set -# CONFIG_CASSINI is not set -# CONFIG_NIU is not set -CONFIG_NET_VENDOR_TEHUTI=y -# CONFIG_TEHUTI is not set -CONFIG_NET_VENDOR_TI=y -# CONFIG_TLAN is not set -CONFIG_NET_VENDOR_VIA=y -# CONFIG_VIA_RHINE is not set -# CONFIG_VIA_VELOCITY is not set -CONFIG_NET_VENDOR_WIZNET=y -# CONFIG_WIZNET_W5100 is not set -# CONFIG_WIZNET_W5300 is not set -# CONFIG_FDDI is not set -# CONFIG_HIPPI is not set -CONFIG_PHYLIB=y - -# -# MII PHY device drivers -# -CONFIG_AQUANTIA_PHY=y -# 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=y -# CONFIG_TERANETICS_PHY is not set -CONFIG_SMSC_PHY=y -CONFIG_BROADCOM_PHY=y -# CONFIG_BCM7XXX_PHY is not set -# CONFIG_BCM87XX_PHY is not set -# CONFIG_ICPLUS_PHY is not set -CONFIG_REALTEK_PHY=y -# 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=y -# CONFIG_MDIO_BITBANG is not set -CONFIG_MDIO_BUS_MUX=y -# CONFIG_MDIO_BUS_MUX_GPIO is not set -CONFIG_MDIO_BUS_MUX_MMIOREG=y -# CONFIG_FSL_10GBASE_KR is not set -# CONFIG_MDIO_BCM_UNIMAC 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_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 is not set -CONFIG_INPUT_EVDEV=y -# CONFIG_INPUT_EVBUG 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 is not set -CONFIG_SERIO_AMBAKMI=y -# 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=y -CONFIG_UNIX98_PTYS=y -# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set -CONFIG_LEGACY_PTYS=y -CONFIG_LEGACY_PTY_COUNT=16 -# CONFIG_SERIAL_NONSTANDARD is not set -# CONFIG_NOZOMI is not set -# CONFIG_N_GSM is not set -# CONFIG_TRACE_SINK is not set -CONFIG_DEVKMEM=y - -# -# Serial drivers -# -CONFIG_SERIAL_EARLYCON=y -CONFIG_SERIAL_8250=y -CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y -CONFIG_SERIAL_8250_CONSOLE=y -CONFIG_SERIAL_8250_DMA=y -CONFIG_SERIAL_8250_PCI=y -CONFIG_SERIAL_8250_NR_UARTS=4 -CONFIG_SERIAL_8250_RUNTIME_UARTS=4 -# CONFIG_SERIAL_8250_EXTENDED is not set -# CONFIG_SERIAL_8250_DW 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_OF_PLATFORM=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_RP2 is not set -# CONFIG_SERIAL_FSL_LPUART is not set -CONFIG_HVC_DRIVER=y -CONFIG_VIRTIO_CONSOLE=y -# CONFIG_IPMI_HANDLER is not set -CONFIG_HW_RANDOM=y -# CONFIG_HW_RANDOM_TIMERIOMEM is not set -# CONFIG_HW_RANDOM_VIRTIO is not set -CONFIG_HW_RANDOM_XGENE=y -# CONFIG_R3964 is not set -# CONFIG_APPLICOM is not set - -# -# PCMCIA character devices -# -# CONFIG_RAW_DRIVER is not set -# CONFIG_TCG_TPM is not set -CONFIG_DEVPORT=y -# 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_ARB_GPIO_CHALLENGE is not set -# CONFIG_I2C_MUX_GPIO is not set -# CONFIG_I2C_MUX_PCA9541 is not set -CONFIG_I2C_MUX_PCA954x=y -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_CBUS_GPIO is not set -# CONFIG_I2C_DESIGNWARE_PLATFORM is not set -# CONFIG_I2C_DESIGNWARE_PCI is not set -# CONFIG_I2C_GPIO is not set -CONFIG_I2C_IMX=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_VERSATILE 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_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 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 is not set -# CONFIG_SPI_TLE62X0 is not set -# CONFIG_SPMI is not set -# CONFIG_HSI is not set - -# -# PPS support -# -CONFIG_PPS=y -# CONFIG_PPS_DEBUG is not set -# CONFIG_NTP_PPS is not set - -# -# PPS clients support -# -# CONFIG_PPS_CLIENT_KTIMER is not set -# CONFIG_PPS_CLIENT_LDISC is not set -# CONFIG_PPS_CLIENT_GPIO is not set - -# -# PPS generators support -# - -# -# PTP clock support -# -CONFIG_PTP_1588_CLOCK=y - -# -# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. -# -CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y -CONFIG_ARCH_REQUIRE_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 is not set -CONFIG_GPIO_GENERIC=y - -# -# Memory mapped GPIO drivers: -# -CONFIG_GPIO_GENERIC_PLATFORM=y -# CONFIG_GPIO_DWAPB is not set -CONFIG_GPIO_PL061=y -# CONFIG_GPIO_SCH311X is not set -# CONFIG_GPIO_SYSCON is not set -CONFIG_GPIO_XGENE=y -# CONFIG_GPIO_VX855 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: -# -# CONFIG_GPIO_BT8XX is not set -# CONFIG_GPIO_AMD8111 is not set -# CONFIG_GPIO_ML_IOH is not set -# CONFIG_GPIO_RDC321X is not set - -# -# 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_MANAGER 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_GPIO is not set -# CONFIG_POWER_RESET_GPIO_RESTART is not set -# CONFIG_POWER_RESET_LTC2952 is not set -CONFIG_POWER_RESET_VEXPRESS=y -# CONFIG_POWER_RESET_XGENE is not set -# CONFIG_POWER_RESET_SYSCON is not set -CONFIG_POWER_RESET_LAYERSCAPE=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_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_HTC_PASIC3 is not set -# CONFIG_HTC_I2CPLD 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_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_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_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_MFD_VEXPRESS_SYSREG=y -CONFIG_REGULATOR=y -# CONFIG_REGULATOR_DEBUG is not set -CONFIG_REGULATOR_FIXED_VOLTAGE=y -# 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_REGULATOR_VEXPRESS is not set -# CONFIG_MEDIA_SUPPORT is not set - -# -# Graphics support -# -CONFIG_VGA_ARB=y -CONFIG_VGA_ARB_MAX_GPUS=16 - -# -# 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=y -CONFIG_FB_CFB_COPYAREA=y -CONFIG_FB_CFB_IMAGEBLIT=y -# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set -# CONFIG_FB_SYS_FILLRECT is not set -# CONFIG_FB_SYS_COPYAREA is not set -# CONFIG_FB_SYS_IMAGEBLIT is not set -# CONFIG_FB_FOREIGN_ENDIAN is not set -# CONFIG_FB_SYS_FOPS is not set -# CONFIG_FB_SVGALIB is not set -# CONFIG_FB_MACMODES is not set -# CONFIG_FB_BACKLIGHT is not set -CONFIG_FB_MODE_HELPERS=y -# 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=y -# 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_FB_SSD1307 is not set -# CONFIG_BACKLIGHT_LCD_SUPPORT is not set -# CONFIG_VGASTATE is not set -CONFIG_VIDEOMODE_HELPERS=y - -# -# Console display driver support -# -CONFIG_DUMMY_CONSOLE=y -CONFIG_FRAMEBUFFER_CONSOLE=y -# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set -# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set -CONFIG_LOGO=y -# CONFIG_LOGO_LINUX_MONO is not set -# CONFIG_LOGO_LINUX_VGA16 is not set -CONFIG_LOGO_LINUX_CLUT224=y -# 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_PCI=y -CONFIG_USB_XHCI_PLATFORM=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=y -# 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=y -CONFIG_USB_DWC3_HOST=y - -# -# Platform Glue Driver Support -# -CONFIG_USB_DWC3_PCI=y - -# -# Debugging features -# -# CONFIG_USB_DWC3_DEBUG is not set -# CONFIG_DWC3_HOST_USB3_LPM_ENABLE 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_NOP_USB_XCEIV is not set -# CONFIG_USB_GPIO_VBUS is not set -# CONFIG_USB_ISP1301 is not set -CONFIG_USB_ULPI=y -# CONFIG_USB_GADGET is not set -# CONFIG_UWB is not set -CONFIG_MMC=y -# CONFIG_MMC_DEBUG is not set -# CONFIG_MMC_CLKGATE is not set - -# -# MMC/SD/SDIO Card Drivers -# -CONFIG_MMC_BLOCK=y -CONFIG_MMC_BLOCK_MINORS=8 -CONFIG_MMC_BLOCK_BOUNCE=y -# CONFIG_SDIO_UART is not set -# CONFIG_MMC_TEST is not set - -# -# MMC/SD/SDIO Host Controller Drivers -# -CONFIG_MMC_ARMMMCI=y -CONFIG_MMC_SDHCI=y -CONFIG_MMC_SDHCI_IO_ACCESSORS=y -# CONFIG_MMC_SDHCI_PCI is not set -CONFIG_MMC_SDHCI_PLTFM=y -# CONFIG_MMC_SDHCI_OF_ARASAN is not set -CONFIG_MMC_SDHCI_OF_ESDHC=y -# CONFIG_MMC_SDHCI_PXAV3 is not set -# CONFIG_MMC_SDHCI_PXAV2 is not set -# CONFIG_MMC_TIFM_SD is not set -CONFIG_MMC_SPI=y -# CONFIG_MMC_CB710 is not set -# CONFIG_MMC_VIA_SDMMC is not set -# CONFIG_MMC_VUB300 is not set -# CONFIG_MMC_USHC is not set -# CONFIG_MMC_USDHI6ROL0 is not set -# CONFIG_MEMSTICK is not set -# CONFIG_NEW_LEDS is not set -# CONFIG_ACCESSIBILITY is not set -# CONFIG_INFINIBAND 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=y -# 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_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_EFI=y -# 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=y - -# -# HID Sensor RTC drivers -# -# CONFIG_RTC_DRV_HID_SENSOR_TIME is not set -CONFIG_DMADEVICES=y -# CONFIG_DMADEVICES_DEBUG is not set - -# -# DMA Devices -# -# CONFIG_AMBA_PL08X is not set -# CONFIG_DW_DMAC_CORE is not set -# CONFIG_DW_DMAC is not set -# CONFIG_DW_DMAC_PCI is not set -# CONFIG_PL330_DMA is not set -# CONFIG_FSL_EDMA is not set -CONFIG_DMA_ENGINE=y -CONFIG_DMA_OF=y - -# -# DMA Clients -# -# CONFIG_ASYNC_TX_DMA is not set -# CONFIG_DMATEST is not set -# CONFIG_AUXDISPLAY is not set -# CONFIG_UIO is not set -# CONFIG_VIRT_DRIVERS is not set - -CONFIG_VFIO_IOMMU_TYPE1=y -CONFIG_VFIO=y -CONFIG_VFIO_PCI=y -CONFIG_VFIO_FSL_MC=y - -CONFIG_VIRTIO=y - -# -# Virtio drivers -# -CONFIG_VIRTIO_PCI=y -CONFIG_VIRTIO_BALLOON=y -CONFIG_VIRTIO_MMIO=y -# CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES is not set - -# -# Microsoft Hyper-V guest support -# -CONFIG_STAGING=y -# CONFIG_COMEDI is not set -# CONFIG_RTS5208 is not set -# CONFIG_FB_XGI is not set -# CONFIG_BCM_WIMAX is not set -# CONFIG_FT1000 is not set - -# -# Speakup console speech -# -# CONFIG_SPEAKUP is not set -# CONFIG_TOUCHSCREEN_CLEARPAD_TM1217 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_DGNC is not set -# CONFIG_DGAP is not set -# CONFIG_GS_FPGABOOT is not set -CONFIG_FSL_MC_BUS=y -CONFIG_FSL_MC_RESTOOL=y -CONFIG_FSL_MC_DPIO=y -# CONFIG_FSL_QBMAN_DEBUG is not set -CONFIG_FSL_DPAA2=y -CONFIG_FSL_DPAA2_ETH=y -# CONFIG_FSL_DPAA2_ETH_USE_ERR_QUEUE is not set -CONFIG_FSL_DPAA2_MAC=y -# CONFIG_FSL_DPAA2_MAC_NETDEVS is not set - -# -# SOC (System On Chip) specific Drivers -# -# CONFIG_SOC_TI is not set -CONFIG_FSL_SOC_DRIVERS=y -CONFIG_FSL_GUTS=y -CONFIG_LS_SOC_DRIVERS=y -CONFIG_CLKDEV_LOOKUP=y -CONFIG_HAVE_CLK_PREPARE=y -CONFIG_COMMON_CLK=y - -# -# Common Clock Framework -# -CONFIG_COMMON_CLK_VERSATILE=y -CONFIG_CLK_SP810=y -CONFIG_CLK_VEXPRESS_OSC=y -# CONFIG_COMMON_CLK_SI5351 is not set -# CONFIG_COMMON_CLK_SI570 is not set -CONFIG_CLK_QORIQ=y -CONFIG_COMMON_CLK_XGENE=y -# 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=y -# CONFIG_MAILBOX is not set -CONFIG_IOMMU_API=y -CONFIG_IOMMU_SUPPORT=y - -# -# Generic IOMMU Pagetable Support -# -CONFIG_IOMMU_IO_PGTABLE=y -CONFIG_IOMMU_IO_PGTABLE_LPAE=y -# CONFIG_IOMMU_IO_PGTABLE_LPAE_SELFTEST is not set -CONFIG_OF_IOMMU=y -CONFIG_ARM_SMMU=y - -# -# 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=y -CONFIG_FSL_IFC=y -# CONFIG_IIO is not set -# CONFIG_VME_BUS is not set -# CONFIG_PWM is not set -CONFIG_IRQCHIP=y -CONFIG_ARM_GIC=y -CONFIG_ARM_GIC_V2M=y -CONFIG_ARM_GIC_V3=y -CONFIG_ARM_GIC_V3_ITS=y -# 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_XGENE=y -# CONFIG_POWERCAP is not set -# CONFIG_MCB is not set -CONFIG_RAS=y -# CONFIG_THUNDERBOLT is not set - -# -# Firmware Drivers -# -# CONFIG_FIRMWARE_MEMMAP is not set - -# -# EFI (Extensible Firmware Interface) Support -# -# CONFIG_EFI_VARS is not set -CONFIG_EFI_PARAMS_FROM_FDT=y -CONFIG_EFI_RUNTIME_WRAPPERS=y -CONFIG_EFI_ARMSTUB=y - -# -# File systems -# -CONFIG_DCACHE_WORD_ACCESS=y -CONFIG_EXT2_FS=y -# CONFIG_EXT2_FS_XATTR is not set -# CONFIG_EXT2_FS_XIP is not set -CONFIG_EXT3_FS=y -# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set -# CONFIG_EXT3_FS_XATTR is not set -CONFIG_EXT4_FS=y -# CONFIG_EXT4_FS_POSIX_ACL is not set -# CONFIG_EXT4_FS_SECURITY is not set -# CONFIG_EXT4_DEBUG is not set -CONFIG_JBD=y -# CONFIG_JBD_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_BTRFS_FS is not set -# CONFIG_NILFS2_FS is not set -# CONFIG_FS_POSIX_ACL is not set -CONFIG_FILE_LOCKING=y -CONFIG_FSNOTIFY=y -CONFIG_DNOTIFY=y -CONFIG_INOTIFY_USER=y -CONFIG_FANOTIFY=y -CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y -# CONFIG_QUOTA is not set -# CONFIG_QUOTACTL is not set -# CONFIG_AUTOFS4_FS is not set -CONFIG_FUSE_FS=y -CONFIG_CUSE=y -CONFIG_OVERLAY_FS=y - -# -# 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_KCORE is not set -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=y -CONFIG_HUGETLBFS=y -CONFIG_HUGETLB_PAGE=y -# CONFIG_CONFIGFS_FS is not set -CONFIG_MISC_FILESYSTEMS=y -# CONFIG_ADFS_FS is not set -# CONFIG_AFFS_FS is not set -# CONFIG_ECRYPT_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_JFFS2_FS=y -CONFIG_JFFS2_FS_DEBUG=0 -CONFIG_JFFS2_FS_WRITEBUFFER=y -# CONFIG_JFFS2_FS_WBUF_VERIFY is not set -CONFIG_JFFS2_SUMMARY=y -# 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 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=y -CONFIG_SQUASHFS_ZLIB=y -CONFIG_SQUASHFS_LZO=y -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 is not set -# CONFIG_EFIVAR_FS is not set -# CONFIG_AUFS_FS is not set -CONFIG_NETWORK_FILESYSTEMS=y -CONFIG_NFS_FS=y -CONFIG_NFS_V2=y -CONFIG_NFS_V3=y -# CONFIG_NFS_V3_ACL is not set -CONFIG_NFS_V4=y -# CONFIG_NFS_SWAP is not set -# CONFIG_NFS_V4_1 is not set -CONFIG_ROOT_NFS=y -# CONFIG_NFS_USE_LEGACY_DNS is not set -CONFIG_NFS_USE_KERNEL_DNS=y -# CONFIG_NFSD is not set -CONFIG_GRACE_PERIOD=y -CONFIG_LOCKD=y -CONFIG_LOCKD_V4=y -CONFIG_NFS_COMMON=y -CONFIG_SUNRPC=y -CONFIG_SUNRPC_GSS=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_9P_FS=y -# CONFIG_9P_FS_POSIX_ACL is not set -# CONFIG_9P_FS_SECURITY 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=y -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_HAVE_KVM_IRQCHIP=y -CONFIG_KVM_MMIO=y -CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT=y -CONFIG_VIRTUALIZATION=y -CONFIG_KVM=y -CONFIG_KVM_ARM_HOST=y -CONFIG_KVM_ARM_MAX_VCPUS=8 -CONFIG_KVM_ARM_VGIC=y -CONFIG_KVM_ARM_TIMER=y - -# -# Kernel hacking -# - -# -# printk and dmesg options -# -CONFIG_PRINTK_TIME=y -CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 -# CONFIG_BOOT_PRINTK_DELAY is not set -# CONFIG_DYNAMIC_DEBUG is not set - -# -# Compile-time checks and compiler options -# -CONFIG_DEBUG_INFO=y -# CONFIG_DEBUG_INFO_REDUCED is not set -# CONFIG_DEBUG_INFO_SPLIT is not set -# CONFIG_DEBUG_INFO_DWARF4 is not set -CONFIG_ENABLE_WARN_DEPRECATED=y -CONFIG_ENABLE_MUST_CHECK=y -CONFIG_FRAME_WARN=2048 -# CONFIG_STRIP_ASM_SYMS is not set -# CONFIG_READABLE_ASM is not set -# CONFIG_UNUSED_SYMBOLS is not set -CONFIG_DEBUG_FS=y -# CONFIG_HEADERS_CHECK is not set -# CONFIG_DEBUG_SECTION_MISMATCH is not set -CONFIG_ARCH_WANT_FRAME_POINTERS=y -CONFIG_FRAME_POINTER=y -# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set -CONFIG_MAGIC_SYSRQ=y -CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x1 -CONFIG_DEBUG_KERNEL=y - -# -# Memory Debugging -# -# CONFIG_DEBUG_PAGEALLOC is not set -# CONFIG_DEBUG_OBJECTS is not set -# CONFIG_DEBUG_SLAB 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_SHIRQ is not set - -# -# Debug Lockups and Hangs -# -CONFIG_LOCKUP_DETECTOR=y -# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set -CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 -CONFIG_DETECT_HUNG_TASK=y -CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120 -# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set -CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 -# 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 -CONFIG_DEBUG_PREEMPT=y - -# -# 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 is not set -# CONFIG_DEBUG_KOBJECT is not set -CONFIG_HAVE_DEBUG_BUGVERBOSE=y -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_VERBOSE=y -# 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_LKDTM is not set -# 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_STRICT_DEVMEM is not set -CONFIG_PID_IN_CONTEXTIDR=y -# CONFIG_ARM64_RANDOMIZE_TEXT_OFFSET is not set -# CONFIG_DEBUG_SET_MODULE_RONX is not set - -# -# Security options -# -CONFIG_KEYS=y -# CONFIG_PERSISTENT_KEYRINGS is not set -# CONFIG_BIG_KEYS is not set -# CONFIG_ENCRYPTED_KEYS is not set -# CONFIG_KEYS_DEBUG_PROC_KEYS is not set -# CONFIG_SECURITY_DMESG_RESTRICT is not set -CONFIG_SECURITY=y -# CONFIG_SECURITYFS is not set -# CONFIG_SECURITY_NETWORK is not set -# CONFIG_SECURITY_PATH is not set -# CONFIG_SECURITY_SMACK is not set -# CONFIG_SECURITY_TOMOYO is not set -# CONFIG_SECURITY_APPARMOR is not set -# CONFIG_SECURITY_YAMA is not set -CONFIG_INTEGRITY=y -# CONFIG_INTEGRITY_SIGNATURE is not set -CONFIG_INTEGRITY_AUDIT=y -# CONFIG_IMA is not set -# CONFIG_EVM 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_PCRYPT is not set -CONFIG_CRYPTO_WORKQUEUE=y -CONFIG_CRYPTO_CRYPTD=y -# CONFIG_CRYPTO_MCRYPTD is not set -CONFIG_CRYPTO_AUTHENC=y -# CONFIG_CRYPTO_TEST is not set -CONFIG_CRYPTO_ABLK_HELPER=y - -# -# Authenticated Encryption with Associated Data -# -# CONFIG_CRYPTO_CCM is not set -# CONFIG_CRYPTO_GCM is not set -# CONFIG_CRYPTO_SEQIV is not set - -# -# Block modes -# -CONFIG_CRYPTO_CBC=y -# CONFIG_CRYPTO_CTR is not set -# CONFIG_CRYPTO_CTS is not set -# CONFIG_CRYPTO_ECB is not set -# CONFIG_CRYPTO_LRW is not set -# CONFIG_CRYPTO_PCBC is not set -# CONFIG_CRYPTO_XTS is not set - -# -# Hash modes -# -# CONFIG_CRYPTO_CMAC is not set -CONFIG_CRYPTO_HMAC=y -# 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=y -# CONFIG_CRYPTO_MICHAEL_MIC is not set -# CONFIG_CRYPTO_RMD128 is not set -# CONFIG_CRYPTO_RMD160 is not set -# CONFIG_CRYPTO_RMD256 is not set -# CONFIG_CRYPTO_RMD320 is not set -CONFIG_CRYPTO_SHA1=y -# 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_ANUBIS is not set -# CONFIG_CRYPTO_ARC4 is not set -# CONFIG_CRYPTO_BLOWFISH is not set -# CONFIG_CRYPTO_CAMELLIA is not set -# CONFIG_CRYPTO_CAST5 is not set -# CONFIG_CRYPTO_CAST6 is not set -CONFIG_CRYPTO_DES=y -# CONFIG_CRYPTO_FCRYPT is not set -# CONFIG_CRYPTO_KHAZAD is not set -# CONFIG_CRYPTO_SALSA20 is not set -# CONFIG_CRYPTO_SEED is not set -# CONFIG_CRYPTO_SERPENT is not set -# CONFIG_CRYPTO_TEA is not set -# CONFIG_CRYPTO_TWOFISH is not set - -# -# Compression -# -CONFIG_CRYPTO_DEFLATE=y -# 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=y -# 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_CCP is not set -# CONFIG_ASYMMETRIC_KEY_TYPE is not set -CONFIG_ARM64_CRYPTO=y -CONFIG_CRYPTO_SHA1_ARM64_CE=y -CONFIG_CRYPTO_SHA2_ARM64_CE=y -CONFIG_CRYPTO_GHASH_ARM64_CE=y -CONFIG_CRYPTO_AES_ARM64_CE=y -CONFIG_CRYPTO_AES_ARM64_CE_CCM=y -CONFIG_CRYPTO_AES_ARM64_CE_BLK=y -CONFIG_CRYPTO_AES_ARM64_NEON_BLK=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_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=y -# CONFIG_LIBCRC32C is not set -# CONFIG_CRC8 is not set -CONFIG_AUDIT_GENERIC=y -CONFIG_AUDIT_ARCH_COMPAT_GENERIC=y -CONFIG_AUDIT_COMPAT_GENERIC=y -# 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_ASSOCIATIVE_ARRAY=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_OID_REGISTRY=y -CONFIG_UCS2_STRING=y -CONFIG_FONT_SUPPORT=y -# CONFIG_FONTS is not set -CONFIG_FONT_8x8=y -CONFIG_FONT_8x16=y -CONFIG_ARCH_HAS_SG_CHAIN=y -CONFIG_FSL_LS2_CONSOLE=y diff --git a/packages/base/any/kernels/3.18.25/configs/x86_64-all/.gitignore b/packages/base/any/kernels/3.18.25/configs/x86_64-all/.gitignore deleted file mode 100644 index c785d46f..00000000 --- a/packages/base/any/kernels/3.18.25/configs/x86_64-all/.gitignore +++ /dev/null @@ -1 +0,0 @@ -kernel-3.18-x86_64-all diff --git a/packages/base/any/kernels/3.18.25/configs/x86_64-all/Makefile b/packages/base/any/kernels/3.18.25/configs/x86_64-all/Makefile deleted file mode 100644 index 8daf53d7..00000000 --- a/packages/base/any/kernels/3.18.25/configs/x86_64-all/Makefile +++ /dev/null @@ -1,42 +0,0 @@ -############################################################ -# -# -# Copyright 2015 Big Switch Networks, Inc. -# -# Licensed under the Eclipse Public License, Version 1.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.eclipse.org/legal/epl-v10.html -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, -# either express or implied. See the License for the specific -# language governing permissions and limitations under the -# License. -# -# -############################################################ -# -# Default 3.18.25 configuration for x86_64 platforms. -# -############################################################ -THIS_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) -include $(ONL)/make/config.mk - -export ARCH := x86_64 -ifndef K_TARGET_DIR -K_TARGET_DIR := $(THIS_DIR) -endif - -include ../../kconfig.mk -K_CONFIG := x86_64-all.config -K_BUILD_TARGET := bzImage -K_COPY_SRC := arch/x86/boot/bzImage -ifndef K_COPY_DST -K_COPY_DST := kernel-3.18-x86_64-all -endif - -include $(ONL)/make/kbuild.mk - diff --git a/packages/base/any/kernels/3.18.25/configs/x86_64-all/x86_64-all.config b/packages/base/any/kernels/3.18.25/configs/x86_64-all/x86_64-all.config deleted file mode 100644 index 40dd2323..00000000 --- a/packages/base/any/kernels/3.18.25/configs/x86_64-all/x86_64-all.config +++ /dev/null @@ -1,3581 +0,0 @@ -# -# Automatically generated file; DO NOT EDIT. -# Linux/x86_64 3.18.25 Kernel Configuration -# -CONFIG_64BIT=y -CONFIG_X86_64=y -CONFIG_X86=y -CONFIG_INSTRUCTION_DECODER=y -CONFIG_PERF_EVENTS_INTEL_UNCORE=y -CONFIG_OUTPUT_FORMAT="elf64-x86-64" -CONFIG_ARCH_DEFCONFIG="arch/x86/configs/x86_64_defconfig" -CONFIG_LOCKDEP_SUPPORT=y -CONFIG_STACKTRACE_SUPPORT=y -CONFIG_HAVE_LATENCYTOP_SUPPORT=y -CONFIG_MMU=y -CONFIG_NEED_DMA_MAP_STATE=y -CONFIG_NEED_SG_DMA_LENGTH=y -CONFIG_GENERIC_ISA_DMA=y -CONFIG_GENERIC_BUG=y -CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y -CONFIG_GENERIC_HWEIGHT=y -CONFIG_ARCH_MAY_HAVE_PC_FDC=y -CONFIG_RWSEM_XCHGADD_ALGORITHM=y -CONFIG_GENERIC_CALIBRATE_DELAY=y -CONFIG_ARCH_HAS_CPU_RELAX=y -CONFIG_ARCH_HAS_CACHE_LINE_SIZE=y -CONFIG_HAVE_SETUP_PER_CPU_AREA=y -CONFIG_NEED_PER_CPU_EMBED_FIRST_CHUNK=y -CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK=y -CONFIG_ARCH_HIBERNATION_POSSIBLE=y -CONFIG_ARCH_SUSPEND_POSSIBLE=y -CONFIG_ARCH_WANT_HUGE_PMD_SHARE=y -CONFIG_ARCH_WANT_GENERAL_HUGETLB=y -CONFIG_ZONE_DMA32=y -CONFIG_AUDIT_ARCH=y -CONFIG_ARCH_SUPPORTS_OPTIMIZED_INLINING=y -CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC=y -CONFIG_X86_64_SMP=y -CONFIG_X86_HT=y -CONFIG_ARCH_HWEIGHT_CFLAGS="-fcall-saved-rdi -fcall-saved-rsi -fcall-saved-rdx -fcall-saved-rcx -fcall-saved-r8 -fcall-saved-r9 -fcall-saved-r10 -fcall-saved-r11" -CONFIG_ARCH_SUPPORTS_UPROBES=y -CONFIG_FIX_EARLYCON_MEM=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="-OpenNetworkLinux" -# CONFIG_LOCALVERSION_AUTO is not set -CONFIG_HAVE_KERNEL_GZIP=y -CONFIG_HAVE_KERNEL_BZIP2=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_BZIP2=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=y -CONFIG_POSIX_MQUEUE_SYSCTL=y -CONFIG_CROSS_MEMORY_ATTACH=y -CONFIG_FHANDLE=y -CONFIG_USELIB=y -CONFIG_AUDIT=y -CONFIG_HAVE_ARCH_AUDITSYSCALL=y -CONFIG_AUDITSYSCALL=y -CONFIG_AUDIT_WATCH=y -CONFIG_AUDIT_TREE=y - -# -# IRQ subsystem -# -CONFIG_GENERIC_IRQ_PROBE=y -CONFIG_GENERIC_IRQ_SHOW=y -CONFIG_GENERIC_IRQ_LEGACY_ALLOC_HWIRQ=y -CONFIG_GENERIC_PENDING_IRQ=y -CONFIG_IRQ_DOMAIN=y -CONFIG_GENERIC_MSI_IRQ=y -# CONFIG_IRQ_DOMAIN_DEBUG is not set -CONFIG_IRQ_FORCED_THREADING=y -CONFIG_SPARSE_IRQ=y -CONFIG_CLOCKSOURCE_WATCHDOG=y -CONFIG_ARCH_CLOCKSOURCE_DATA=y -CONFIG_CLOCKSOURCE_VALIDATE_LAST_CYCLE=y -CONFIG_GENERIC_TIME_VSYSCALL=y -CONFIG_GENERIC_CLOCKEVENTS=y -CONFIG_GENERIC_CLOCKEVENTS_BUILD=y -CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y -CONFIG_GENERIC_CLOCKEVENTS_MIN_ADJUST=y -CONFIG_GENERIC_CMOS_UPDATE=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=y - -# -# 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=y -CONFIG_BSD_PROCESS_ACCT_V3=y -CONFIG_TASKSTATS=y -CONFIG_TASK_DELAY_ACCT=y -CONFIG_TASK_XACCT=y -CONFIG_TASK_IO_ACCOUNTING=y - -# -# 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=64 -CONFIG_RCU_FANOUT_LEAF=16 -# CONFIG_RCU_FANOUT_EXACT is not set -CONFIG_RCU_FAST_NO_HZ=y -# CONFIG_TREE_RCU_TRACE is not set -# CONFIG_RCU_NOCB_CPU is not set -CONFIG_BUILD_BIN2C=y -CONFIG_IKCONFIG=y -CONFIG_IKCONFIG_PROC=y -CONFIG_LOG_BUF_SHIFT=17 -CONFIG_LOG_CPU_MAX_BUF_SHIFT=12 -CONFIG_HAVE_UNSTABLE_SCHED_CLOCK=y -CONFIG_ARCH_SUPPORTS_NUMA_BALANCING=y -CONFIG_ARCH_SUPPORTS_INT128=y -CONFIG_CGROUPS=y -# CONFIG_CGROUP_DEBUG is not set -CONFIG_CGROUP_FREEZER=y -CONFIG_CGROUP_DEVICE=y -CONFIG_CPUSETS=y -CONFIG_PROC_PID_CPUSET=y -CONFIG_CGROUP_CPUACCT=y -CONFIG_RESOURCE_COUNTERS=y -CONFIG_MEMCG=y -CONFIG_MEMCG_SWAP=y -CONFIG_MEMCG_SWAP_ENABLED=y -CONFIG_MEMCG_KMEM=y -# CONFIG_CGROUP_HUGETLB is not set -CONFIG_CGROUP_PERF=y -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=y -# CONFIG_DEBUG_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=y -CONFIG_PID_NS=y -CONFIG_NET_NS=y -CONFIG_SCHED_AUTOGROUP=y -# CONFIG_SYSFS_DEPRECATED is not set -CONFIG_RELAY=y -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 is not set -CONFIG_CC_OPTIMIZE_FOR_SIZE=y -CONFIG_SYSCTL=y -CONFIG_ANON_INODES=y -CONFIG_HAVE_UID16=y -CONFIG_SYSCTL_EXCEPTION_TRACE=y -CONFIG_HAVE_PCSPKR_PLATFORM=y -CONFIG_BPF=y -CONFIG_EXPERT=y -CONFIG_UID16=y -CONFIG_SGETMASK_SYSCALL=y -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_PCSPKR_PLATFORM=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=y -CONFIG_HAVE_PERF_EVENTS=y - -# -# Kernel Performance Events And Counters -# -CONFIG_PERF_EVENTS=y -# CONFIG_DEBUG_PERF_USE_VMALLOC is not set -CONFIG_VM_EVENT_COUNTERS=y -# CONFIG_COMPAT_BRK is not set -CONFIG_SLAB=y -# CONFIG_SLUB is not set -# CONFIG_SLOB is not set -# CONFIG_SYSTEM_TRUSTED_KEYRING is not set -# CONFIG_PROFILING is not set -CONFIG_TRACEPOINTS=y -CONFIG_HAVE_OPROFILE=y -CONFIG_OPROFILE_NMI_TIMER=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_IOREMAP_PROT=y -CONFIG_HAVE_KPROBES=y -CONFIG_HAVE_KRETPROBES=y -CONFIG_HAVE_OPTPROBES=y -CONFIG_HAVE_KPROBES_ON_FTRACE=y -CONFIG_HAVE_ARCH_TRACEHOOK=y -CONFIG_HAVE_DMA_ATTRS=y -CONFIG_HAVE_DMA_CONTIGUOUS=y -CONFIG_GENERIC_SMP_IDLE_THREAD=y -CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y -CONFIG_HAVE_DMA_API_DEBUG=y -CONFIG_HAVE_HW_BREAKPOINT=y -CONFIG_HAVE_MIXED_BREAKPOINTS_REGS=y -CONFIG_HAVE_USER_RETURN_NOTIFIER=y -CONFIG_HAVE_PERF_EVENTS_NMI=y -CONFIG_HAVE_PERF_REGS=y -CONFIG_HAVE_PERF_USER_STACK_DUMP=y -CONFIG_HAVE_ARCH_JUMP_LABEL=y -CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG=y -CONFIG_HAVE_CMPXCHG_LOCAL=y -CONFIG_HAVE_CMPXCHG_DOUBLE=y -CONFIG_ARCH_WANT_COMPAT_IPC_PARSE_VERSION=y -CONFIG_ARCH_WANT_OLD_COMPAT_IPC=y -CONFIG_HAVE_ARCH_SECCOMP_FILTER=y -CONFIG_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_ARCH_TRANSPARENT_HUGEPAGE=y -CONFIG_HAVE_ARCH_SOFT_DIRTY=y -CONFIG_MODULES_USE_ELF_RELA=y -CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK=y -CONFIG_OLD_SIGSUSPEND3=y -CONFIG_COMPAT_OLD_SIGACTION=y - -# -# GCOV-based kernel profiling -# -# CONFIG_GCOV_KERNEL is not set -# CONFIG_HAVE_GENERIC_DMA_COHERENT is not set -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_BLK_DEV_BSG=y -CONFIG_BLK_DEV_BSGLIB=y -CONFIG_BLK_DEV_INTEGRITY=y -# CONFIG_BLK_DEV_THROTTLING is not set -# CONFIG_BLK_CMDLINE_PARSER is not set - -# -# Partition Types -# -CONFIG_PARTITION_ADVANCED=y -CONFIG_ACORN_PARTITION=y -CONFIG_ACORN_PARTITION_CUMANA=y -CONFIG_ACORN_PARTITION_EESOX=y -CONFIG_ACORN_PARTITION_ICS=y -CONFIG_ACORN_PARTITION_ADFS=y -CONFIG_ACORN_PARTITION_POWERTEC=y -CONFIG_ACORN_PARTITION_RISCIX=y -# CONFIG_AIX_PARTITION is not set -CONFIG_OSF_PARTITION=y -CONFIG_AMIGA_PARTITION=y -CONFIG_ATARI_PARTITION=y -CONFIG_MAC_PARTITION=y -CONFIG_MSDOS_PARTITION=y -CONFIG_BSD_DISKLABEL=y -CONFIG_MINIX_SUBPARTITION=y -CONFIG_SOLARIS_X86_PARTITION=y -CONFIG_UNIXWARE_DISKLABEL=y -CONFIG_LDM_PARTITION=y -# CONFIG_LDM_DEBUG is not set -CONFIG_SGI_PARTITION=y -CONFIG_ULTRIX_PARTITION=y -CONFIG_SUN_PARTITION=y -CONFIG_KARMA_PARTITION=y -CONFIG_EFI_PARTITION=y -# CONFIG_SYSV68_PARTITION is not set -# CONFIG_CMDLINE_PARTITION is not set -CONFIG_BLOCK_COMPAT=y - -# -# IO Schedulers -# -CONFIG_IOSCHED_NOOP=y -CONFIG_IOSCHED_DEADLINE=y -CONFIG_IOSCHED_CFQ=y -CONFIG_CFQ_GROUP_IOSCHED=y -# CONFIG_DEFAULT_DEADLINE is not set -CONFIG_DEFAULT_CFQ=y -# CONFIG_DEFAULT_NOOP is not set -CONFIG_DEFAULT_IOSCHED="cfq" -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_ARCH_USE_QUEUE_RWLOCK=y -CONFIG_QUEUE_RWLOCK=y -CONFIG_FREEZER=y - -# -# Processor type and features -# -CONFIG_ZONE_DMA=y -CONFIG_SMP=y -CONFIG_X86_FEATURE_NAMES=y -CONFIG_X86_MPPARSE=y -# CONFIG_X86_EXTENDED_PLATFORM is not set -# CONFIG_X86_INTEL_LPSS is not set -# CONFIG_IOSF_MBI is not set -CONFIG_X86_SUPPORTS_MEMORY_FAILURE=y -CONFIG_SCHED_OMIT_FRAME_POINTER=y -# CONFIG_HYPERVISOR_GUEST is not set -CONFIG_NO_BOOTMEM=y -CONFIG_MEMTEST=y -# CONFIG_MK8 is not set -# CONFIG_MPSC is not set -# CONFIG_MCORE2 is not set -# CONFIG_MATOM is not set -CONFIG_GENERIC_CPU=y -CONFIG_X86_INTERNODE_CACHE_SHIFT=6 -CONFIG_X86_L1_CACHE_SHIFT=6 -CONFIG_X86_TSC=y -CONFIG_X86_CMPXCHG64=y -CONFIG_X86_CMOV=y -CONFIG_X86_MINIMUM_CPU_FAMILY=64 -CONFIG_X86_DEBUGCTLMSR=y -# CONFIG_PROCESSOR_SELECT is not set -CONFIG_CPU_SUP_INTEL=y -CONFIG_CPU_SUP_AMD=y -CONFIG_CPU_SUP_CENTAUR=y -CONFIG_HPET_TIMER=y -CONFIG_HPET_EMULATE_RTC=y -CONFIG_DMI=y -CONFIG_GART_IOMMU=y -CONFIG_CALGARY_IOMMU=y -CONFIG_CALGARY_IOMMU_ENABLED_BY_DEFAULT=y -CONFIG_SWIOTLB=y -CONFIG_IOMMU_HELPER=y -# CONFIG_MAXSMP is not set -CONFIG_NR_CPUS=512 -CONFIG_SCHED_SMT=y -CONFIG_SCHED_MC=y -# CONFIG_PREEMPT_NONE is not set -CONFIG_PREEMPT_VOLUNTARY=y -# CONFIG_PREEMPT is not set -CONFIG_X86_UP_APIC_MSI=y -CONFIG_X86_LOCAL_APIC=y -CONFIG_X86_IO_APIC=y -CONFIG_X86_REROUTE_FOR_BROKEN_BOOT_IRQS=y -CONFIG_X86_MCE=y -CONFIG_X86_MCE_INTEL=y -CONFIG_X86_MCE_AMD=y -CONFIG_X86_MCE_THRESHOLD=y -# CONFIG_X86_MCE_INJECT is not set -CONFIG_X86_THERMAL_VECTOR=y -# CONFIG_X86_16BIT is not set -# CONFIG_I8K is not set -# CONFIG_MICROCODE is not set -# CONFIG_MICROCODE_INTEL_EARLY is not set -# CONFIG_MICROCODE_AMD_EARLY is not set -CONFIG_X86_MSR=y -CONFIG_X86_CPUID=y -CONFIG_ARCH_PHYS_ADDR_T_64BIT=y -CONFIG_ARCH_DMA_ADDR_T_64BIT=y -CONFIG_DIRECT_GBPAGES=y -# CONFIG_NUMA is not set -CONFIG_ARCH_SPARSEMEM_ENABLE=y -CONFIG_ARCH_SPARSEMEM_DEFAULT=y -CONFIG_ARCH_SELECT_MEMORY_MODEL=y -CONFIG_ARCH_MEMORY_PROBE=y -CONFIG_ARCH_PROC_KCORE_TEXT=y -CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000 -CONFIG_SELECT_MEMORY_MODEL=y -CONFIG_SPARSEMEM_MANUAL=y -CONFIG_SPARSEMEM=y -CONFIG_HAVE_MEMORY_PRESENT=y -CONFIG_SPARSEMEM_EXTREME=y -CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y -CONFIG_SPARSEMEM_ALLOC_MEM_MAP_TOGETHER=y -CONFIG_SPARSEMEM_VMEMMAP=y -CONFIG_HAVE_MEMBLOCK=y -CONFIG_HAVE_MEMBLOCK_NODE_MAP=y -CONFIG_ARCH_DISCARD_MEMBLOCK=y -CONFIG_MEMORY_ISOLATION=y -CONFIG_HAVE_BOOTMEM_INFO_NODE=y -CONFIG_MEMORY_HOTPLUG=y -CONFIG_MEMORY_HOTPLUG_SPARSE=y -CONFIG_MEMORY_HOTREMOVE=y -CONFIG_PAGEFLAGS_EXTENDED=y -CONFIG_SPLIT_PTLOCK_CPUS=4 -CONFIG_ARCH_ENABLE_SPLIT_PMD_PTLOCK=y -CONFIG_COMPACTION=y -CONFIG_MIGRATION=y -CONFIG_ARCH_ENABLE_HUGEPAGE_MIGRATION=y -CONFIG_PHYS_ADDR_T_64BIT=y -CONFIG_ZONE_DMA_FLAG=1 -CONFIG_BOUNCE=y -CONFIG_NEED_BOUNCE_POOL=y -CONFIG_VIRT_TO_BUS=y -CONFIG_KSM=y -CONFIG_DEFAULT_MMAP_MIN_ADDR=65536 -CONFIG_ARCH_SUPPORTS_MEMORY_FAILURE=y -CONFIG_MEMORY_FAILURE=y -CONFIG_HWPOISON_INJECT=y -CONFIG_TRANSPARENT_HUGEPAGE=y -# CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS is not set -CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=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_GENERIC_EARLY_IOREMAP=y -# CONFIG_X86_CHECK_BIOS_CORRUPTION is not set -CONFIG_X86_RESERVE_LOW=64 -CONFIG_MTRR=y -CONFIG_MTRR_SANITIZER=y -CONFIG_MTRR_SANITIZER_ENABLE_DEFAULT=0 -CONFIG_MTRR_SANITIZER_SPARE_REG_NR_DEFAULT=1 -CONFIG_X86_PAT=y -CONFIG_ARCH_USES_PG_UNCACHED=y -CONFIG_ARCH_RANDOM=y -CONFIG_X86_SMAP=y -# CONFIG_EFI is not set -CONFIG_SECCOMP=y -# CONFIG_HZ_100 is not set -CONFIG_HZ_250=y -# CONFIG_HZ_300 is not set -# CONFIG_HZ_1000 is not set -CONFIG_HZ=250 -CONFIG_SCHED_HRTICK=y -CONFIG_KEXEC=y -# CONFIG_KEXEC_FILE is not set -CONFIG_CRASH_DUMP=y -CONFIG_PHYSICAL_START=0x1000000 -CONFIG_RELOCATABLE=y -# CONFIG_RANDOMIZE_BASE is not set -CONFIG_PHYSICAL_ALIGN=0x1000000 -CONFIG_HOTPLUG_CPU=y -# CONFIG_BOOTPARAM_HOTPLUG_CPU0 is not set -# CONFIG_DEBUG_HOTPLUG_CPU0 is not set -# CONFIG_COMPAT_VDSO is not set -# CONFIG_CMDLINE_BOOL is not set -CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y -CONFIG_ARCH_ENABLE_MEMORY_HOTREMOVE=y - -# -# Power management and ACPI options -# -# CONFIG_SUSPEND is not set -# CONFIG_HIBERNATION is not set -# CONFIG_PM_RUNTIME is not set -CONFIG_ACPI=y -CONFIG_ACPI_LEGACY_TABLES_LOOKUP=y -CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC=y -# CONFIG_ACPI_PROCFS_POWER is not set -# CONFIG_ACPI_EC_DEBUGFS is not set -CONFIG_ACPI_AC=y -CONFIG_ACPI_BATTERY=y -CONFIG_ACPI_BUTTON=y -CONFIG_ACPI_FAN=y -# CONFIG_ACPI_DOCK is not set -CONFIG_ACPI_PROCESSOR=y -CONFIG_ACPI_HOTPLUG_CPU=y -# CONFIG_ACPI_PROCESSOR_AGGREGATOR is not set -CONFIG_ACPI_THERMAL=y -# CONFIG_ACPI_CUSTOM_DSDT is not set -# CONFIG_ACPI_INITRD_TABLE_OVERRIDE is not set -# CONFIG_ACPI_DEBUG is not set -# CONFIG_ACPI_PCI_SLOT is not set -CONFIG_X86_PM_TIMER=y -CONFIG_ACPI_CONTAINER=y -# CONFIG_ACPI_HOTPLUG_MEMORY is not set -# CONFIG_ACPI_SBS is not set -# CONFIG_ACPI_HED is not set -CONFIG_ACPI_CUSTOM_METHOD=y -# CONFIG_ACPI_REDUCED_HARDWARE_ONLY is not set -CONFIG_HAVE_ACPI_APEI=y -CONFIG_HAVE_ACPI_APEI_NMI=y -# CONFIG_ACPI_APEI is not set -# CONFIG_ACPI_EXTLOG is not set -# CONFIG_SFI is not set - -# -# 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_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_CONSERVATIVE=y - -# -# x86 CPU frequency scaling drivers -# -# CONFIG_X86_INTEL_PSTATE is not set -# CONFIG_X86_PCC_CPUFREQ is not set -# CONFIG_X86_ACPI_CPUFREQ is not set -# CONFIG_X86_SPEEDSTEP_CENTRINO is not set -CONFIG_X86_P4_CLOCKMOD=y - -# -# shared options -# -CONFIG_X86_SPEEDSTEP_LIB=y - -# -# CPU Idle -# -CONFIG_CPU_IDLE=y -CONFIG_CPU_IDLE_GOV_LADDER=y -CONFIG_CPU_IDLE_GOV_MENU=y -# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set -CONFIG_INTEL_IDLE=y - -# -# Memory power savings -# -CONFIG_I7300_IDLE_IOAT_CHANNEL=y -CONFIG_I7300_IDLE=y - -# -# Bus options (PCI etc.) -# -CONFIG_PCI=y -CONFIG_PCI_DIRECT=y -CONFIG_PCI_MMCONFIG=y -CONFIG_PCI_DOMAINS=y -# CONFIG_PCI_CNB20LE_QUIRK is not set -CONFIG_PCIEPORTBUS=y -CONFIG_HOTPLUG_PCI_PCIE=y -CONFIG_PCIEAER=y -# CONFIG_PCIE_ECRC is not set -CONFIG_PCIEAER_INJECT=y -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_PCI_MSI=y -# CONFIG_PCI_DEBUG is not set -# CONFIG_PCI_REALLOC_ENABLE_AUTO is not set -# CONFIG_PCI_STUB is not set -CONFIG_HT_IRQ=y -CONFIG_PCI_ATS=y -CONFIG_PCI_IOV=y -# CONFIG_PCI_PRI is not set -# CONFIG_PCI_PASID is not set -CONFIG_PCI_IOAPIC=y -CONFIG_PCI_LABEL=y - -# -# PCI host controller drivers -# -CONFIG_ISA_DMA_API=y -CONFIG_AMD_NB=y -CONFIG_PCCARD=y -CONFIG_PCMCIA=y -CONFIG_PCMCIA_LOAD_CIS=y -CONFIG_CARDBUS=y - -# -# PC-card bridges -# -# CONFIG_YENTA is not set -CONFIG_PD6729=y -CONFIG_I82092=y -CONFIG_PCCARD_NONSTATIC=y -CONFIG_HOTPLUG_PCI=y -# CONFIG_HOTPLUG_PCI_ACPI is not set -CONFIG_HOTPLUG_PCI_CPCI=y -CONFIG_HOTPLUG_PCI_CPCI_ZT5550=y -CONFIG_HOTPLUG_PCI_CPCI_GENERIC=y -CONFIG_HOTPLUG_PCI_SHPC=y -# CONFIG_RAPIDIO is not set -# CONFIG_X86_SYSFB is not set - -# -# Executable file formats / Emulations -# -CONFIG_BINFMT_ELF=y -CONFIG_COMPAT_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=y -CONFIG_COREDUMP=y -CONFIG_IA32_EMULATION=y -CONFIG_IA32_AOUT=y -# CONFIG_X86_X32 is not set -CONFIG_COMPAT=y -CONFIG_COMPAT_FOR_U64_ALIGNMENT=y -CONFIG_SYSVIPC_COMPAT=y -CONFIG_KEYS_COMPAT=y -CONFIG_X86_DEV_DMA_OPS=y -CONFIG_PMC_ATOM=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_ALGO=y -CONFIG_XFRM_USER=y -CONFIG_XFRM_SUB_POLICY=y -CONFIG_XFRM_MIGRATE=y -# CONFIG_XFRM_STATISTICS is not set -CONFIG_XFRM_IPCOMP=y -CONFIG_NET_KEY=y -CONFIG_NET_KEY_MIGRATE=y -CONFIG_INET=y -CONFIG_IP_MULTICAST=y -CONFIG_IP_ADVANCED_ROUTER=y -CONFIG_IP_FIB_TRIE_STATS=y -CONFIG_IP_MULTIPLE_TABLES=y -CONFIG_IP_ROUTE_MULTIPATH=y -CONFIG_IP_ROUTE_VERBOSE=y -CONFIG_IP_ROUTE_CLASSID=y -# CONFIG_IP_PNP 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 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=y -# 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=y -CONFIG_TCP_CONG_BIC=y -CONFIG_TCP_CONG_CUBIC=y -CONFIG_TCP_CONG_WESTWOOD=y -CONFIG_TCP_CONG_HTCP=y -CONFIG_TCP_CONG_HSTCP=y -CONFIG_TCP_CONG_HYBLA=y -CONFIG_TCP_CONG_VEGAS=y -CONFIG_TCP_CONG_SCALABLE=y -CONFIG_TCP_CONG_LP=y -CONFIG_TCP_CONG_VENO=y -CONFIG_TCP_CONG_YEAH=y -CONFIG_TCP_CONG_ILLINOIS=y -# CONFIG_TCP_CONG_DCTCP is not set -# CONFIG_DEFAULT_BIC is not set -CONFIG_DEFAULT_CUBIC=y -# CONFIG_DEFAULT_HTCP is not set -# CONFIG_DEFAULT_HYBLA is not set -# CONFIG_DEFAULT_VEGAS is not set -# CONFIG_DEFAULT_VENO is not set -# CONFIG_DEFAULT_WESTWOOD is not set -# CONFIG_DEFAULT_RENO is not set -CONFIG_DEFAULT_TCP_CONG="cubic" -CONFIG_TCP_MD5SIG=y -CONFIG_IPV6=y -CONFIG_IPV6_ROUTER_PREF=y -CONFIG_IPV6_ROUTE_INFO=y -CONFIG_IPV6_OPTIMISTIC_DAD=y -CONFIG_INET6_AH=y -CONFIG_INET6_ESP=y -CONFIG_INET6_IPCOMP=y -CONFIG_IPV6_MIP6=y -CONFIG_INET6_XFRM_TUNNEL=y -CONFIG_INET6_TUNNEL=y -CONFIG_INET6_XFRM_MODE_TRANSPORT=y -CONFIG_INET6_XFRM_MODE_TUNNEL=y -CONFIG_INET6_XFRM_MODE_BEET=y -CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION=y -# CONFIG_IPV6_VTI is not set -CONFIG_IPV6_SIT=y -CONFIG_IPV6_SIT_6RD=y -CONFIG_IPV6_NDISC_NODETYPE=y -CONFIG_IPV6_TUNNEL=y -# CONFIG_IPV6_GRE is not set -CONFIG_IPV6_MULTIPLE_TABLES=y -CONFIG_IPV6_SUBTREES=y -CONFIG_IPV6_MROUTE=y -CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=y -CONFIG_IPV6_PIMSM_V2=y -CONFIG_NETWORK_SECMARK=y -CONFIG_NET_PTP_CLASSIFY=y -# CONFIG_NETWORK_PHY_TIMESTAMPING is not set -CONFIG_NETFILTER=y -# CONFIG_NETFILTER_DEBUG is not set -CONFIG_NETFILTER_ADVANCED=y -CONFIG_BRIDGE_NETFILTER=y - -# -# Core Netfilter Configuration -# -CONFIG_NETFILTER_NETLINK=y -CONFIG_NETFILTER_NETLINK_ACCT=y -CONFIG_NETFILTER_NETLINK_QUEUE=y -CONFIG_NETFILTER_NETLINK_LOG=y -CONFIG_NF_CONNTRACK=y -CONFIG_NF_LOG_COMMON=y -CONFIG_NF_CONNTRACK_MARK=y -CONFIG_NF_CONNTRACK_SECMARK=y -CONFIG_NF_CONNTRACK_ZONES=y -CONFIG_NF_CONNTRACK_PROCFS=y -CONFIG_NF_CONNTRACK_EVENTS=y -# CONFIG_NF_CONNTRACK_TIMEOUT is not set -CONFIG_NF_CONNTRACK_TIMESTAMP=y -CONFIG_NF_CONNTRACK_LABELS=y -CONFIG_NF_CT_PROTO_DCCP=y -CONFIG_NF_CT_PROTO_GRE=y -CONFIG_NF_CT_PROTO_SCTP=y -CONFIG_NF_CT_PROTO_UDPLITE=y -CONFIG_NF_CONNTRACK_AMANDA=y -CONFIG_NF_CONNTRACK_FTP=y -CONFIG_NF_CONNTRACK_H323=y -CONFIG_NF_CONNTRACK_IRC=y -CONFIG_NF_CONNTRACK_BROADCAST=y -CONFIG_NF_CONNTRACK_NETBIOS_NS=y -CONFIG_NF_CONNTRACK_SNMP=y -CONFIG_NF_CONNTRACK_PPTP=y -CONFIG_NF_CONNTRACK_SANE=y -CONFIG_NF_CONNTRACK_SIP=y -CONFIG_NF_CONNTRACK_TFTP=y -CONFIG_NF_CT_NETLINK=y -CONFIG_NF_CT_NETLINK_TIMEOUT=y -CONFIG_NF_CT_NETLINK_HELPER=y -CONFIG_NETFILTER_NETLINK_QUEUE_CT=y -CONFIG_NF_NAT=y -CONFIG_NF_NAT_NEEDED=y -CONFIG_NF_NAT_PROTO_DCCP=y -CONFIG_NF_NAT_PROTO_UDPLITE=y -CONFIG_NF_NAT_PROTO_SCTP=y -CONFIG_NF_NAT_AMANDA=y -CONFIG_NF_NAT_FTP=y -CONFIG_NF_NAT_IRC=y -CONFIG_NF_NAT_SIP=y -CONFIG_NF_NAT_TFTP=y -# CONFIG_NF_TABLES is not set -CONFIG_NETFILTER_XTABLES=y - -# -# Xtables combined modules -# -CONFIG_NETFILTER_XT_MARK=y -CONFIG_NETFILTER_XT_CONNMARK=y -CONFIG_NETFILTER_XT_SET=y - -# -# Xtables targets -# -CONFIG_NETFILTER_XT_TARGET_AUDIT=y -CONFIG_NETFILTER_XT_TARGET_CHECKSUM=y -CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y -CONFIG_NETFILTER_XT_TARGET_CONNMARK=y -CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y -CONFIG_NETFILTER_XT_TARGET_CT=y -CONFIG_NETFILTER_XT_TARGET_DSCP=y -CONFIG_NETFILTER_XT_TARGET_HL=y -CONFIG_NETFILTER_XT_TARGET_HMARK=y -CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y -CONFIG_NETFILTER_XT_TARGET_LOG=y -CONFIG_NETFILTER_XT_TARGET_MARK=y -CONFIG_NETFILTER_XT_NAT=y -CONFIG_NETFILTER_XT_TARGET_NETMAP=y -CONFIG_NETFILTER_XT_TARGET_NFLOG=y -CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y -CONFIG_NETFILTER_XT_TARGET_NOTRACK=y -CONFIG_NETFILTER_XT_TARGET_RATEEST=y -CONFIG_NETFILTER_XT_TARGET_REDIRECT=y -CONFIG_NETFILTER_XT_TARGET_TEE=y -CONFIG_NETFILTER_XT_TARGET_TPROXY=y -CONFIG_NETFILTER_XT_TARGET_TRACE=y -CONFIG_NETFILTER_XT_TARGET_SECMARK=y -CONFIG_NETFILTER_XT_TARGET_TCPMSS=y -CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=y - -# -# Xtables matches -# -CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=y -CONFIG_NETFILTER_XT_MATCH_BPF=y -CONFIG_NETFILTER_XT_MATCH_CGROUP=y -CONFIG_NETFILTER_XT_MATCH_CLUSTER=y -CONFIG_NETFILTER_XT_MATCH_COMMENT=y -CONFIG_NETFILTER_XT_MATCH_CONNBYTES=y -CONFIG_NETFILTER_XT_MATCH_CONNLABEL=y -CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y -CONFIG_NETFILTER_XT_MATCH_CONNMARK=y -CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y -CONFIG_NETFILTER_XT_MATCH_CPU=y -CONFIG_NETFILTER_XT_MATCH_DCCP=y -CONFIG_NETFILTER_XT_MATCH_DEVGROUP=y -CONFIG_NETFILTER_XT_MATCH_DSCP=y -CONFIG_NETFILTER_XT_MATCH_ECN=y -CONFIG_NETFILTER_XT_MATCH_ESP=y -CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y -CONFIG_NETFILTER_XT_MATCH_HELPER=y -CONFIG_NETFILTER_XT_MATCH_HL=y -CONFIG_NETFILTER_XT_MATCH_IPCOMP=y -CONFIG_NETFILTER_XT_MATCH_IPRANGE=y -CONFIG_NETFILTER_XT_MATCH_IPVS=y -CONFIG_NETFILTER_XT_MATCH_L2TP=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_MULTIPORT=y -CONFIG_NETFILTER_XT_MATCH_NFACCT=y -CONFIG_NETFILTER_XT_MATCH_OSF=y -CONFIG_NETFILTER_XT_MATCH_OWNER=y -CONFIG_NETFILTER_XT_MATCH_POLICY=y -CONFIG_NETFILTER_XT_MATCH_PHYSDEV=y -CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y -CONFIG_NETFILTER_XT_MATCH_QUOTA=y -CONFIG_NETFILTER_XT_MATCH_RATEEST=y -CONFIG_NETFILTER_XT_MATCH_REALM=y -CONFIG_NETFILTER_XT_MATCH_RECENT=y -CONFIG_NETFILTER_XT_MATCH_SCTP=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_TCPMSS=y -CONFIG_NETFILTER_XT_MATCH_TIME=y -CONFIG_NETFILTER_XT_MATCH_U32=y -CONFIG_IP_SET=y -CONFIG_IP_SET_MAX=256 -CONFIG_IP_SET_BITMAP_IP=y -CONFIG_IP_SET_BITMAP_IPMAC=y -CONFIG_IP_SET_BITMAP_PORT=y -CONFIG_IP_SET_HASH_IP=y -# CONFIG_IP_SET_HASH_IPMARK is not set -CONFIG_IP_SET_HASH_IPPORT=y -CONFIG_IP_SET_HASH_IPPORTIP=y -CONFIG_IP_SET_HASH_IPPORTNET=y -# CONFIG_IP_SET_HASH_MAC is not set -# CONFIG_IP_SET_HASH_NETPORTNET is not set -CONFIG_IP_SET_HASH_NET=y -# CONFIG_IP_SET_HASH_NETNET is not set -CONFIG_IP_SET_HASH_NETPORT=y -CONFIG_IP_SET_HASH_NETIFACE=y -CONFIG_IP_SET_LIST_SET=y -CONFIG_IP_VS=y -CONFIG_IP_VS_IPV6=y -# CONFIG_IP_VS_DEBUG is not set -CONFIG_IP_VS_TAB_BITS=12 - -# -# IPVS transport protocol load balancing support -# -CONFIG_IP_VS_PROTO_TCP=y -CONFIG_IP_VS_PROTO_UDP=y -CONFIG_IP_VS_PROTO_AH_ESP=y -CONFIG_IP_VS_PROTO_ESP=y -CONFIG_IP_VS_PROTO_AH=y -CONFIG_IP_VS_PROTO_SCTP=y - -# -# IPVS scheduler -# -CONFIG_IP_VS_RR=y -CONFIG_IP_VS_WRR=y -CONFIG_IP_VS_LC=y -CONFIG_IP_VS_WLC=y -# CONFIG_IP_VS_FO is not set -CONFIG_IP_VS_LBLC=y -CONFIG_IP_VS_LBLCR=y -CONFIG_IP_VS_DH=y -CONFIG_IP_VS_SH=y -CONFIG_IP_VS_SED=y -CONFIG_IP_VS_NQ=y - -# -# IPVS SH scheduler -# -CONFIG_IP_VS_SH_TAB_BITS=8 - -# -# IPVS application helper -# -# CONFIG_IP_VS_FTP is not set -CONFIG_IP_VS_NFCT=y -CONFIG_IP_VS_PE_SIP=y - -# -# IP: Netfilter Configuration -# -CONFIG_NF_DEFRAG_IPV4=y -CONFIG_NF_CONNTRACK_IPV4=y -CONFIG_NF_CONNTRACK_PROC_COMPAT=y -# CONFIG_NF_LOG_ARP is not set -CONFIG_NF_LOG_IPV4=y -CONFIG_NF_REJECT_IPV4=y -CONFIG_NF_NAT_IPV4=y -CONFIG_NF_NAT_MASQUERADE_IPV4=y -CONFIG_NF_NAT_SNMP_BASIC=y -CONFIG_NF_NAT_PROTO_GRE=y -CONFIG_NF_NAT_PPTP=y -CONFIG_NF_NAT_H323=y -CONFIG_IP_NF_IPTABLES=y -CONFIG_IP_NF_MATCH_AH=y -CONFIG_IP_NF_MATCH_ECN=y -# CONFIG_IP_NF_MATCH_RPFILTER is not set -CONFIG_IP_NF_MATCH_TTL=y -CONFIG_IP_NF_FILTER=y -CONFIG_IP_NF_TARGET_REJECT=y -# CONFIG_IP_NF_TARGET_SYNPROXY is not set -CONFIG_IP_NF_NAT=y -CONFIG_IP_NF_TARGET_MASQUERADE=y -# CONFIG_IP_NF_TARGET_NETMAP is not set -# CONFIG_IP_NF_TARGET_REDIRECT is not set -CONFIG_IP_NF_MANGLE=y -CONFIG_IP_NF_TARGET_CLUSTERIP=y -CONFIG_IP_NF_TARGET_ECN=y -CONFIG_IP_NF_TARGET_TTL=y -CONFIG_IP_NF_RAW=y -CONFIG_IP_NF_ARPTABLES=y -CONFIG_IP_NF_ARPFILTER=y -CONFIG_IP_NF_ARP_MANGLE=y - -# -# IPv6: Netfilter Configuration -# -CONFIG_NF_DEFRAG_IPV6=y -CONFIG_NF_CONNTRACK_IPV6=y -CONFIG_NF_REJECT_IPV6=y -CONFIG_NF_LOG_IPV6=y -# CONFIG_NF_NAT_IPV6 is not set -CONFIG_IP6_NF_IPTABLES=y -CONFIG_IP6_NF_MATCH_AH=y -CONFIG_IP6_NF_MATCH_EUI64=y -CONFIG_IP6_NF_MATCH_FRAG=y -CONFIG_IP6_NF_MATCH_OPTS=y -CONFIG_IP6_NF_MATCH_HL=y -CONFIG_IP6_NF_MATCH_IPV6HEADER=y -CONFIG_IP6_NF_MATCH_MH=y -# CONFIG_IP6_NF_MATCH_RPFILTER is not set -CONFIG_IP6_NF_MATCH_RT=y -CONFIG_IP6_NF_TARGET_HL=y -CONFIG_IP6_NF_FILTER=y -CONFIG_IP6_NF_TARGET_REJECT=y -# CONFIG_IP6_NF_TARGET_SYNPROXY is not set -CONFIG_IP6_NF_MANGLE=y -CONFIG_IP6_NF_RAW=y -# CONFIG_IP6_NF_NAT is not set -CONFIG_BRIDGE_NF_EBTABLES=y -CONFIG_BRIDGE_EBT_BROUTE=y -CONFIG_BRIDGE_EBT_T_FILTER=y -CONFIG_BRIDGE_EBT_T_NAT=y -CONFIG_BRIDGE_EBT_802_3=y -CONFIG_BRIDGE_EBT_AMONG=y -CONFIG_BRIDGE_EBT_ARP=y -CONFIG_BRIDGE_EBT_IP=y -CONFIG_BRIDGE_EBT_IP6=y -CONFIG_BRIDGE_EBT_LIMIT=y -CONFIG_BRIDGE_EBT_MARK=y -CONFIG_BRIDGE_EBT_PKTTYPE=y -CONFIG_BRIDGE_EBT_STP=y -CONFIG_BRIDGE_EBT_VLAN=y -CONFIG_BRIDGE_EBT_ARPREPLY=y -CONFIG_BRIDGE_EBT_DNAT=y -CONFIG_BRIDGE_EBT_MARK_T=y -CONFIG_BRIDGE_EBT_REDIRECT=y -CONFIG_BRIDGE_EBT_SNAT=y -CONFIG_BRIDGE_EBT_LOG=y -CONFIG_BRIDGE_EBT_NFLOG=y -# CONFIG_IP_DCCP is not set -CONFIG_IP_SCTP=y -# CONFIG_SCTP_DBG_OBJCNT is not set -CONFIG_SCTP_DEFAULT_COOKIE_HMAC_MD5=y -# CONFIG_SCTP_DEFAULT_COOKIE_HMAC_SHA1 is not set -# CONFIG_SCTP_DEFAULT_COOKIE_HMAC_NONE is not set -CONFIG_SCTP_COOKIE_HMAC_MD5=y -# CONFIG_SCTP_COOKIE_HMAC_SHA1 is not set -# CONFIG_RDS is not set -# CONFIG_TIPC is not set -# CONFIG_ATM is not set -# CONFIG_L2TP is not set -CONFIG_STP=y -CONFIG_BRIDGE=y -CONFIG_BRIDGE_IGMP_SNOOPING=y -CONFIG_BRIDGE_VLAN_FILTERING=y -CONFIG_HAVE_NET_DSA=y -CONFIG_VLAN_8021Q=y -# CONFIG_VLAN_8021Q_GVRP is not set -# CONFIG_VLAN_8021Q_MVRP is not set -# CONFIG_DECNET is not set -CONFIG_LLC=y -CONFIG_LLC2=y -# 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_DNS_RESOLVER=y -# CONFIG_BATMAN_ADV is not set -# CONFIG_OPENVSWITCH is not set -# CONFIG_VSOCKETS is not set -CONFIG_NETLINK_MMAP=y -CONFIG_NETLINK_DIAG=y -# 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=y -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=y -CONFIG_NET_DROP_MONITOR=y -# CONFIG_HAMRADIO is not set -# CONFIG_CAN is not set -# CONFIG_IRDA is not set -# CONFIG_BT is not set -CONFIG_AF_RXRPC=y -# CONFIG_AF_RXRPC_DEBUG is not set -# CONFIG_RXKAD is not set -CONFIG_FIB_RULES=y -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=y -# CONFIG_CEPH_LIB_PRETTYDEBUG is not set -# CONFIG_CEPH_LIB_USE_DNS_RESOLVER is not set -# CONFIG_NFC is not set -CONFIG_HAVE_BPF_JIT=y - -# -# Device Drivers -# - -# -# Generic Driver Options -# -CONFIG_UEVENT_HELPER=y -CONFIG_UEVENT_HELPER_PATH="" -CONFIG_DEVTMPFS=y -# CONFIG_DEVTMPFS_MOUNT is not set -CONFIG_STANDALONE=y -CONFIG_PREVENT_FIRMWARE_BUILD=y -CONFIG_FW_LOADER=y -# CONFIG_FIRMWARE_IN_KERNEL is not set -CONFIG_EXTRA_FIRMWARE="" -CONFIG_FW_LOADER_USER_HELPER=y -# 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_GENERIC_CPU_AUTOPROBE=y -# CONFIG_DMA_SHARED_BUFFER is not set - -# -# Bus devices -# -CONFIG_CONNECTOR=y -CONFIG_PROC_EVENTS=y -# CONFIG_MTD is not set -CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y -# CONFIG_PARPORT is not set -CONFIG_PNP=y -CONFIG_PNP_DEBUG_MESSAGES=y - -# -# Protocols -# -CONFIG_PNPACPI=y -CONFIG_BLK_DEV=y -# CONFIG_BLK_DEV_NULL_BLK is not set -# CONFIG_BLK_DEV_FD 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=y -CONFIG_BLK_DEV_LOOP_MIN_COUNT=8 -# CONFIG_BLK_DEV_CRYPTOLOOP is not set -# CONFIG_BLK_DEV_DRBD is not set -CONFIG_BLK_DEV_NBD=y -# CONFIG_BLK_DEV_NVME is not set -# CONFIG_BLK_DEV_SKD is not set -# CONFIG_BLK_DEV_OSD is not set -CONFIG_BLK_DEV_SX8=y -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_HD 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=y -# CONFIG_IBM_ASM 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=y -# 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=y -CONFIG_EEPROM_AT25=y -# CONFIG_EEPROM_LEGACY is not set -# CONFIG_EEPROM_MAX6875 is not set -CONFIG_EEPROM_93CX6=y -# CONFIG_EEPROM_93XX46 is not set -CONFIG_CB710_CORE=y -# CONFIG_CB710_DEBUG is not set -CONFIG_CB710_DEBUG_ASSUMPTIONS=y - -# -# Texas Instruments shared transport line discipline -# -# CONFIG_TI_ST is not set -# CONFIG_SENSORS_LIS3_I2C is not set - -# -# Altera FPGA firmware download module -# -# CONFIG_ALTERA_STAPL is not set -# CONFIG_VMWARE_VMCI is not set - -# -# Intel MIC Bus Driver -# -# CONFIG_INTEL_MIC_BUS is not set - -# -# Intel MIC Host Driver -# - -# -# Intel MIC Card Driver -# -# CONFIG_GENWQE is not set -# CONFIG_ECHO is not set -# CONFIG_CXL_BASE is not set -CONFIG_HAVE_IDE=y -# CONFIG_IDE is not set - -# -# SCSI device support -# -CONFIG_SCSI_MOD=y -CONFIG_RAID_ATTRS=y -CONFIG_SCSI=y -CONFIG_SCSI_DMA=y -CONFIG_SCSI_NETLINK=y -# CONFIG_SCSI_MQ_DEFAULT is not set -# CONFIG_SCSI_PROC_FS is not set - -# -# 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=y -# 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=y -CONFIG_SCSI_FC_ATTRS=y -CONFIG_SCSI_ISCSI_ATTRS=y -CONFIG_SCSI_SAS_ATTRS=y -CONFIG_SCSI_SAS_LIBSAS=y -CONFIG_SCSI_SAS_ATA=y -CONFIG_SCSI_SAS_HOST_SMP=y -CONFIG_SCSI_SRP_ATTRS=y -CONFIG_SCSI_LOWLEVEL=y -CONFIG_ISCSI_TCP=y -CONFIG_ISCSI_BOOT_SYSFS=y -CONFIG_SCSI_CXGB3_ISCSI=y -CONFIG_SCSI_CXGB4_ISCSI=y -CONFIG_SCSI_BNX2_ISCSI=y -CONFIG_SCSI_BNX2X_FCOE=y -CONFIG_BE2ISCSI=y -CONFIG_BLK_DEV_3W_XXXX_RAID=y -CONFIG_SCSI_HPSA=y -CONFIG_SCSI_3W_9XXX=y -CONFIG_SCSI_3W_SAS=y -CONFIG_SCSI_ACARD=y -CONFIG_SCSI_AACRAID=y -CONFIG_SCSI_AIC7XXX=y -CONFIG_AIC7XXX_CMDS_PER_DEVICE=8 -CONFIG_AIC7XXX_RESET_DELAY_MS=15000 -CONFIG_AIC7XXX_DEBUG_ENABLE=y -CONFIG_AIC7XXX_DEBUG_MASK=0 -CONFIG_AIC7XXX_REG_PRETTY_PRINT=y -CONFIG_SCSI_AIC79XX=y -CONFIG_AIC79XX_CMDS_PER_DEVICE=32 -CONFIG_AIC79XX_RESET_DELAY_MS=15000 -CONFIG_AIC79XX_DEBUG_ENABLE=y -CONFIG_AIC79XX_DEBUG_MASK=0 -CONFIG_AIC79XX_REG_PRETTY_PRINT=y -CONFIG_SCSI_AIC94XX=y -# CONFIG_AIC94XX_DEBUG is not set -CONFIG_SCSI_MVSAS=y -# CONFIG_SCSI_MVSAS_DEBUG is not set -# CONFIG_SCSI_MVSAS_TASKLET is not set -CONFIG_SCSI_MVUMI=y -CONFIG_SCSI_DPT_I2O=y -CONFIG_SCSI_ADVANSYS=y -CONFIG_SCSI_ARCMSR=y -# CONFIG_SCSI_ESAS2R is not set -CONFIG_MEGARAID_NEWGEN=y -CONFIG_MEGARAID_MM=y -CONFIG_MEGARAID_MAILBOX=y -CONFIG_MEGARAID_LEGACY=y -CONFIG_MEGARAID_SAS=y -CONFIG_SCSI_MPT2SAS=y -CONFIG_SCSI_MPT2SAS_MAX_SGE=128 -# CONFIG_SCSI_MPT2SAS_LOGGING is not set -# CONFIG_SCSI_MPT3SAS is not set -# CONFIG_SCSI_UFSHCD is not set -CONFIG_SCSI_HPTIOP=y -CONFIG_SCSI_BUSLOGIC=y -# CONFIG_SCSI_FLASHPOINT is not set -CONFIG_VMWARE_PVSCSI=y -CONFIG_LIBFC=y -CONFIG_LIBFCOE=y -CONFIG_FCOE=y -CONFIG_FCOE_FNIC=y -CONFIG_SCSI_DMX3191D=y -CONFIG_SCSI_EATA=y -CONFIG_SCSI_EATA_TAGGED_QUEUE=y -CONFIG_SCSI_EATA_LINKED_COMMANDS=y -CONFIG_SCSI_EATA_MAX_TAGS=16 -CONFIG_SCSI_FUTURE_DOMAIN=y -CONFIG_SCSI_GDTH=y -CONFIG_SCSI_ISCI=y -CONFIG_SCSI_IPS=y -CONFIG_SCSI_INITIO=y -CONFIG_SCSI_INIA100=y -CONFIG_SCSI_STEX=y -CONFIG_SCSI_SYM53C8XX_2=y -CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=1 -CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16 -CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64 -CONFIG_SCSI_SYM53C8XX_MMIO=y -CONFIG_SCSI_IPR=y -# CONFIG_SCSI_IPR_TRACE is not set -# CONFIG_SCSI_IPR_DUMP is not set -CONFIG_SCSI_QLOGIC_1280=y -CONFIG_SCSI_QLA_FC=y -CONFIG_SCSI_QLA_ISCSI=y -CONFIG_SCSI_LPFC=y -# CONFIG_SCSI_LPFC_DEBUG_FS is not set -CONFIG_SCSI_DC395x=y -CONFIG_SCSI_DC390T=y -CONFIG_SCSI_DEBUG=y -CONFIG_SCSI_PMCRAID=y -CONFIG_SCSI_PM8001=y -CONFIG_SCSI_BFA_FC=y -# CONFIG_SCSI_CHELSIO_FCOE is not set -CONFIG_SCSI_LOWLEVEL_PCMCIA=y -# CONFIG_PCMCIA_AHA152X is not set -# CONFIG_PCMCIA_FDOMAIN is not set -# CONFIG_PCMCIA_QLOGIC is not set -# CONFIG_PCMCIA_SYM53C500 is not set -CONFIG_SCSI_DH=y -CONFIG_SCSI_DH_RDAC=y -CONFIG_SCSI_DH_HP_SW=y -CONFIG_SCSI_DH_EMC=y -CONFIG_SCSI_DH_ALUA=y -CONFIG_SCSI_OSD_INITIATOR=y -CONFIG_SCSI_OSD_ULD=y -CONFIG_SCSI_OSD_DPRINT_SENSE=1 -# CONFIG_SCSI_OSD_DEBUG is not set -CONFIG_ATA=y -# CONFIG_ATA_NONSTANDARD is not set -CONFIG_ATA_VERBOSE_ERROR=y -CONFIG_ATA_ACPI=y -CONFIG_SATA_PMP=y - -# -# Controllers with non-SFF native interface -# -CONFIG_SATA_AHCI=y -CONFIG_SATA_AHCI_PLATFORM=y -# CONFIG_SATA_INIC162X is not set -CONFIG_SATA_ACARD_AHCI=y -CONFIG_SATA_SIL24=y -CONFIG_ATA_SFF=y - -# -# SFF controllers with custom DMA interface -# -CONFIG_PDC_ADMA=y -CONFIG_SATA_QSTOR=y -CONFIG_SATA_SX4=y -CONFIG_ATA_BMDMA=y - -# -# SATA SFF controllers with BMDMA -# -CONFIG_ATA_PIIX=y -CONFIG_SATA_MV=y -CONFIG_SATA_NV=y -CONFIG_SATA_PROMISE=y -CONFIG_SATA_SIL=y -CONFIG_SATA_SIS=y -CONFIG_SATA_SVW=y -CONFIG_SATA_ULI=y -CONFIG_SATA_VIA=y -CONFIG_SATA_VITESSE=y - -# -# PATA SFF controllers with BMDMA -# -CONFIG_PATA_ALI=y -CONFIG_PATA_AMD=y -CONFIG_PATA_ARTOP=y -CONFIG_PATA_ATIIXP=y -CONFIG_PATA_ATP867X=y -CONFIG_PATA_CMD64X=y -# CONFIG_PATA_CYPRESS is not set -CONFIG_PATA_EFAR=y -CONFIG_PATA_HPT366=y -CONFIG_PATA_HPT37X=y -# CONFIG_PATA_HPT3X2N is not set -# CONFIG_PATA_HPT3X3 is not set -CONFIG_PATA_IT8213=y -CONFIG_PATA_IT821X=y -CONFIG_PATA_JMICRON=y -CONFIG_PATA_MARVELL=y -CONFIG_PATA_NETCELL=y -CONFIG_PATA_NINJA32=y -CONFIG_PATA_NS87415=y -CONFIG_PATA_OLDPIIX=y -# CONFIG_PATA_OPTIDMA is not set -CONFIG_PATA_PDC2027X=y -CONFIG_PATA_PDC_OLD=y -# CONFIG_PATA_RADISYS is not set -CONFIG_PATA_RDC=y -CONFIG_PATA_SCH=y -CONFIG_PATA_SERVERWORKS=y -CONFIG_PATA_SIL680=y -CONFIG_PATA_SIS=y -CONFIG_PATA_TOSHIBA=y -CONFIG_PATA_TRIFLEX=y -CONFIG_PATA_VIA=y -# CONFIG_PATA_WINBOND is not set - -# -# PIO-only SFF controllers -# -# CONFIG_PATA_CMD640_PCI is not set -CONFIG_PATA_MPIIX=y -CONFIG_PATA_NS87410=y -# CONFIG_PATA_OPTI is not set -CONFIG_PATA_PCMCIA=y -CONFIG_PATA_PLATFORM=y -CONFIG_PATA_RZ1000=y - -# -# Generic fallback / legacy drivers -# -# CONFIG_PATA_ACPI is not set -CONFIG_ATA_GENERIC=y -# CONFIG_PATA_LEGACY 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=y -CONFIG_FIREWIRE_OHCI=y -CONFIG_FIREWIRE_SBP2=y -CONFIG_FIREWIRE_NET=y -CONFIG_FIREWIRE_NOSY=y -# CONFIG_I2O is not set -# CONFIG_MACINTOSH_DRIVERS is not set -CONFIG_NETDEVICES=y -CONFIG_MII=y -CONFIG_NET_CORE=y -# CONFIG_BONDING is not set -CONFIG_DUMMY=y -# CONFIG_EQUALIZER is not set -# CONFIG_NET_FC is not set -# CONFIG_NET_TEAM is not set -CONFIG_MACVLAN=y -CONFIG_MACVTAP=y -# CONFIG_VXLAN is not set -# CONFIG_NETCONSOLE is not set -# CONFIG_NETPOLL is not set -# CONFIG_NET_POLL_CONTROLLER is not set -CONFIG_TUN=y -CONFIG_VETH=y -# 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_MDIO=y -# CONFIG_NET_VENDOR_3COM is not set -# CONFIG_NET_VENDOR_ADAPTEC is not set -CONFIG_NET_VENDOR_AGERE=y -# CONFIG_ET131X 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=y -# CONFIG_NET_VENDOR_ATHEROS is not set -CONFIG_NET_VENDOR_BROADCOM=y -CONFIG_B44=y -CONFIG_B44_PCI_AUTOSELECT=y -CONFIG_B44_PCICORE_AUTOSELECT=y -CONFIG_B44_PCI=y -CONFIG_BNX2=y -CONFIG_CNIC=y -CONFIG_TIGON3=y -CONFIG_BNX2X=y -CONFIG_BNX2X_SRIOV=y -# CONFIG_NET_VENDOR_BROCADE is not set -CONFIG_NET_VENDOR_CHELSIO=y -# CONFIG_CHELSIO_T1 is not set -CONFIG_CHELSIO_T3=y -CONFIG_CHELSIO_T4=y -CONFIG_CHELSIO_T4VF=y -# CONFIG_NET_VENDOR_CISCO is not set -# CONFIG_CX_ECAT 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_FUJITSU is not set -# CONFIG_NET_VENDOR_HP is not set -CONFIG_NET_VENDOR_INTEL=y -# CONFIG_E100 is not set -CONFIG_E1000=y -CONFIG_E1000E=y -CONFIG_IGB=y -CONFIG_IGB_HWMON=y -CONFIG_IGBVF=y -CONFIG_IXGB=y -CONFIG_IXGBE=y -CONFIG_IXGBE_HWMON=y -CONFIG_IXGBEVF=y -# CONFIG_I40E is not set -# CONFIG_I40EVF is not set -# CONFIG_FM10K is not set -CONFIG_NET_VENDOR_I825XX=y -# CONFIG_IP1000 is not set -# CONFIG_JME is not set -# CONFIG_NET_VENDOR_MARVELL is not set -CONFIG_NET_VENDOR_MELLANOX=y -# CONFIG_MLX4_EN is not set -# CONFIG_MLX4_CORE is not set -# CONFIG_MLX5_CORE is not set -# CONFIG_NET_VENDOR_MICREL is not set -CONFIG_NET_VENDOR_MICROCHIP=y -CONFIG_ENC28J60=y -CONFIG_ENC28J60_WRITEVERIFY=y -# 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=y -CONFIG_NET_VENDOR_REALTEK=y -# CONFIG_8139CP is not set -# CONFIG_8139TOO is not set -CONFIG_R8169=y -# CONFIG_NET_VENDOR_RDC is not set -CONFIG_NET_VENDOR_SAMSUNG=y -# CONFIG_SXGBE_ETH 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=y -# CONFIG_WIZNET_W5100 is not set -# CONFIG_WIZNET_W5300 is not set -# CONFIG_NET_VENDOR_XIRCOM is not set -# CONFIG_FDDI is not set -# CONFIG_HIPPI is not set -# CONFIG_NET_SB1000 is not set -CONFIG_PHYLIB=y - -# -# MII PHY device drivers -# -# CONFIG_AT803X_PHY is not set -# CONFIG_AMD_PHY is not set -CONFIG_MARVELL_PHY=y -CONFIG_DAVICOM_PHY=y -CONFIG_QSEMI_PHY=y -CONFIG_LXT_PHY=y -CONFIG_CICADA_PHY=y -CONFIG_VITESSE_PHY=y -CONFIG_SMSC_PHY=y -CONFIG_BROADCOM_PHY=y -# CONFIG_BCM7XXX_PHY is not set -# CONFIG_BCM87XX_PHY is not set -# CONFIG_ICPLUS_PHY is not set -CONFIG_REALTEK_PHY=y -CONFIG_NATIONAL_PHY=y -CONFIG_STE10XP=y -CONFIG_LSI_ET1011C_PHY=y -CONFIG_MICREL_PHY=y -CONFIG_FIXED_PHY=y -CONFIG_MDIO_BITBANG=y -# CONFIG_MDIO_GPIO is not set -# CONFIG_MDIO_BCM_UNIMAC is not set -# CONFIG_MICREL_KS8995MA is not set -CONFIG_PPP=y -# CONFIG_PPP_BSDCOMP is not set -# CONFIG_PPP_DEFLATE is not set -# CONFIG_PPP_FILTER is not set -# CONFIG_PPP_MPPE is not set -# CONFIG_PPP_MULTILINK is not set -# CONFIG_PPPOE is not set -# CONFIG_PPP_ASYNC is not set -# CONFIG_PPP_SYNC_TTY is not set -# CONFIG_SLIP is not set -CONFIG_SLHC=y -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=y -# CONFIG_USB_NET_AX8817X is not set -# CONFIG_USB_NET_AX88179_178A is not set -CONFIG_USB_NET_CDCETHER=y -# CONFIG_USB_NET_CDC_EEM is not set -CONFIG_USB_NET_CDC_NCM=y -# CONFIG_USB_NET_HUAWEI_CDC_NCM is not set -# CONFIG_USB_NET_CDC_MBIM is not set -# CONFIG_USB_NET_DM9601 is not set -# CONFIG_USB_NET_SR9700 is not set -# CONFIG_USB_NET_SR9800 is not set -# CONFIG_USB_NET_SMSC75XX is not set -# CONFIG_USB_NET_SMSC95XX is not set -# CONFIG_USB_NET_GL620A is not set -CONFIG_USB_NET_NET1080=y -# CONFIG_USB_NET_PLUSB is not set -# CONFIG_USB_NET_MCS7830 is not set -# CONFIG_USB_NET_RNDIS_HOST is not set -CONFIG_USB_NET_CDC_SUBSET=y -# CONFIG_USB_ALI_M5632 is not set -# CONFIG_USB_AN2720 is not set -CONFIG_USB_BELKIN=y -# CONFIG_USB_ARMLINUX is not set -# CONFIG_USB_EPSON2888 is not set -# CONFIG_USB_KC2190 is not set -CONFIG_USB_NET_ZAURUS=y -# CONFIG_USB_NET_CX82310_ETH is not set -# CONFIG_USB_NET_KALMIA is not set -# CONFIG_USB_NET_QMI_WWAN is not set -# CONFIG_USB_NET_INT51X1 is not set -# CONFIG_USB_IPHETH is not set -# CONFIG_USB_SIERRA_NET is not set -# CONFIG_USB_VL600 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=y -CONFIG_INPUT_POLLDEV=y -CONFIG_INPUT_SPARSEKMAP=y -# 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 - -# -# 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_ARCH_MIGHT_HAVE_PC_SERIO=y -# CONFIG_GAMEPORT is not set - -# -# Character devices -# -CONFIG_TTY=y -CONFIG_VT=y -CONFIG_CONSOLE_TRANSLATIONS=y -CONFIG_VT_CONSOLE=y -CONFIG_HW_CONSOLE=y -CONFIG_VT_HW_CONSOLE_BINDING=y -CONFIG_UNIX98_PTYS=y -CONFIG_DEVPTS_MULTIPLE_INSTANCES=y -# CONFIG_LEGACY_PTYS is not set -CONFIG_SERIAL_NONSTANDARD=y -# CONFIG_ROCKETPORT is not set -# CONFIG_CYCLADES is not set -# CONFIG_MOXA_INTELLIO is not set -# CONFIG_MOXA_SMARTIO is not set -# CONFIG_SYNCLINK is not set -# CONFIG_SYNCLINKMP is not set -# CONFIG_SYNCLINK_GT is not set -# CONFIG_NOZOMI is not set -# CONFIG_ISI is not set -# CONFIG_N_HDLC is not set -# CONFIG_N_GSM is not set -# CONFIG_TRACE_SINK is not set -# CONFIG_DEVKMEM is not set - -# -# Serial drivers -# -CONFIG_SERIAL_EARLYCON=y -CONFIG_SERIAL_8250=y -CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y -CONFIG_SERIAL_8250_PNP=y -CONFIG_SERIAL_8250_CONSOLE=y -CONFIG_SERIAL_8250_PCI=y -CONFIG_SERIAL_8250_CS=y -CONFIG_SERIAL_8250_NR_UARTS=32 -CONFIG_SERIAL_8250_RUNTIME_UARTS=4 -CONFIG_SERIAL_8250_EXTENDED=y -CONFIG_SERIAL_8250_MANY_PORTS=y -CONFIG_SERIAL_8250_SHARE_IRQ=y -# CONFIG_SERIAL_8250_DETECT_IRQ is not set -CONFIG_SERIAL_8250_RSA=y -# CONFIG_SERIAL_8250_DW is not set -# CONFIG_SERIAL_8250_FINTEK is not set - -# -# Non-8250 serial port support -# -# CONFIG_SERIAL_MAX3100 is not set -# CONFIG_SERIAL_MAX310X is not set -CONFIG_SERIAL_MFD_HSU=y -# CONFIG_SERIAL_MFD_HSU_CONSOLE is not set -CONFIG_SERIAL_CORE=y -CONFIG_SERIAL_CORE_CONSOLE=y -CONFIG_SERIAL_JSM=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_ARC is not set -# CONFIG_SERIAL_RP2 is not set -# CONFIG_SERIAL_FSL_LPUART is not set -# CONFIG_TTY_PRINTK is not set -# CONFIG_IPMI_HANDLER is not set -CONFIG_HW_RANDOM=y -CONFIG_HW_RANDOM_TIMERIOMEM=y -CONFIG_HW_RANDOM_INTEL=y -CONFIG_HW_RANDOM_AMD=y -CONFIG_HW_RANDOM_VIA=y -CONFIG_NVRAM=y -# CONFIG_R3964 is not set -# CONFIG_APPLICOM is not set - -# -# PCMCIA character devices -# -CONFIG_SYNCLINK_CS=y -CONFIG_CARDMAN_4000=y -CONFIG_CARDMAN_4040=y -CONFIG_IPWIRELESS=y -# CONFIG_MWAVE is not set -# CONFIG_RAW_DRIVER is not set -# CONFIG_HPET is not set -# CONFIG_HANGCHECK_TIMER is not set -# CONFIG_TCG_TPM is not set -# CONFIG_TELCLOCK is not set -CONFIG_DEVPORT=y -# CONFIG_XILLYBUS is not set - -# -# I2C support -# -CONFIG_I2C=y -CONFIG_ACPI_I2C_OPREGION=y -CONFIG_I2C_BOARDINFO=y -CONFIG_I2C_COMPAT=y -CONFIG_I2C_CHARDEV=y -CONFIG_I2C_MUX=y - -# -# Multiplexer I2C Chip support -# -CONFIG_I2C_MUX_GPIO=y -CONFIG_I2C_MUX_PCA9541=y -CONFIG_I2C_MUX_PCA954x=y -CONFIG_I2C_HELPER_AUTO=y -CONFIG_I2C_ALGOBIT=y -CONFIG_I2C_ALGOPCA=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=y -CONFIG_I2C_ISCH=y -CONFIG_I2C_ISMT=y -# 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 - -# -# ACPI drivers -# -# CONFIG_I2C_SCMI is not set - -# -# I2C system bus drivers (mostly embedded / system-on-chip) -# -# CONFIG_I2C_CBUS_GPIO is not set -# CONFIG_I2C_DESIGNWARE_PCI is not set -# CONFIG_I2C_GPIO is not set -# CONFIG_I2C_OCORES is not set -CONFIG_I2C_PCA_PLATFORM=y -# CONFIG_I2C_PXA_PCI 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_GPIO is not set -# CONFIG_SPI_OC_TINY is not set -# CONFIG_SPI_PXA2XX is not set -# CONFIG_SPI_PXA2XX_PCI 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 is not set -# CONFIG_SPI_TLE62X0 is not set -# CONFIG_SPMI is not set -# CONFIG_HSI is not set - -# -# PPS support -# -CONFIG_PPS=y -# CONFIG_PPS_DEBUG is not set - -# -# PPS clients support -# -# CONFIG_PPS_CLIENT_KTIMER is not set -# CONFIG_PPS_CLIENT_LDISC is not set -# CONFIG_PPS_CLIENT_GPIO is not set - -# -# PPS generators support -# - -# -# PTP clock support -# -CONFIG_PTP_1588_CLOCK=y - -# -# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. -# -CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y -CONFIG_GPIOLIB=y -CONFIG_GPIO_DEVRES=y -CONFIG_GPIO_ACPI=y -# CONFIG_DEBUG_GPIO is not set -CONFIG_GPIO_SYSFS=y -CONFIG_GPIO_GENERIC=y -CONFIG_GPIO_MAX730X=y - -# -# Memory mapped GPIO drivers: -# -CONFIG_GPIO_GENERIC_PLATFORM=y -# CONFIG_GPIO_DWAPB is not set -# CONFIG_GPIO_IT8761E is not set -# CONFIG_GPIO_F7188X is not set -# CONFIG_GPIO_SCH311X is not set -CONFIG_GPIO_SCH=y -# CONFIG_GPIO_ICH is not set -# CONFIG_GPIO_VX855 is not set -# CONFIG_GPIO_LYNXPOINT is not set - -# -# I2C GPIO expanders: -# -# CONFIG_GPIO_MAX7300 is not set -# CONFIG_GPIO_MAX732X is not set -CONFIG_GPIO_PCA953X=y -# CONFIG_GPIO_PCA953X_IRQ is not set -CONFIG_GPIO_PCF857X=y -# CONFIG_GPIO_SX150X is not set -# CONFIG_GPIO_ADP5588 is not set - -# -# PCI GPIO expanders: -# -# CONFIG_GPIO_BT8XX is not set -# CONFIG_GPIO_AMD8111 is not set -# CONFIG_GPIO_INTEL_MID is not set -# CONFIG_GPIO_ML_IOH is not set -# CONFIG_GPIO_RDC321X is not set - -# -# SPI GPIO expanders: -# -CONFIG_GPIO_MAX7301=y -# CONFIG_GPIO_MCP23S08 is not set -CONFIG_GPIO_MC33880=y - -# -# 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 is not set -# CONFIG_POWER_AVS is not set -CONFIG_HWMON=y -CONFIG_HWMON_VID=y -# CONFIG_HWMON_DEBUG_CHIP is not set - -# -# Native drivers -# -# CONFIG_SENSORS_ABITUGURU is not set -# CONFIG_SENSORS_ABITUGURU3 is not set -# CONFIG_SENSORS_AD7314 is not set -# CONFIG_SENSORS_AD7414 is not set -# CONFIG_SENSORS_AD7418 is not set -CONFIG_SENSORS_ADM1021=y -# CONFIG_SENSORS_ADM1025 is not set -# CONFIG_SENSORS_ADM1026 is not set -# CONFIG_SENSORS_ADM1029 is not set -# CONFIG_SENSORS_ADM1031 is not set -# CONFIG_SENSORS_ADM9240 is not set -# CONFIG_SENSORS_ADT7310 is not set -# CONFIG_SENSORS_ADT7410 is not set -# CONFIG_SENSORS_ADT7411 is not set -# CONFIG_SENSORS_ADT7462 is not set -# CONFIG_SENSORS_ADT7470 is not set -# CONFIG_SENSORS_ADT7475 is not set -# CONFIG_SENSORS_ASC7621 is not set -# CONFIG_SENSORS_K8TEMP is not set -# CONFIG_SENSORS_K10TEMP is not set -# CONFIG_SENSORS_FAM15H_POWER is not set -# CONFIG_SENSORS_APPLESMC is not set -# CONFIG_SENSORS_ASB100 is not set -# CONFIG_SENSORS_ATXP1 is not set -# CONFIG_SENSORS_DS620 is not set -# CONFIG_SENSORS_DS1621 is not set -# CONFIG_SENSORS_I5K_AMB is not set -# CONFIG_SENSORS_F71805F is not set -# CONFIG_SENSORS_F71882FG is not set -# CONFIG_SENSORS_F75375S is not set -# CONFIG_SENSORS_FSCHMD is not set -# CONFIG_SENSORS_GL518SM is not set -# CONFIG_SENSORS_GL520SM is not set -# CONFIG_SENSORS_G760A is not set -# CONFIG_SENSORS_G762 is not set -CONFIG_SENSORS_GPIO_FAN=y -# CONFIG_SENSORS_HIH6130 is not set -CONFIG_SENSORS_CORETEMP=y -# CONFIG_SENSORS_IT87 is not set -# CONFIG_SENSORS_JC42 is not set -# CONFIG_SENSORS_POWR1220 is not set -# CONFIG_SENSORS_LINEAGE is not set -# CONFIG_SENSORS_LTC2945 is not set -CONFIG_SENSORS_LTC4151=y -CONFIG_SENSORS_LTC4215=y -# CONFIG_SENSORS_LTC4222 is not set -CONFIG_SENSORS_LTC4245=y -# CONFIG_SENSORS_LTC4260 is not set -CONFIG_SENSORS_LTC4261=y -# CONFIG_SENSORS_MAX1111 is not set -# CONFIG_SENSORS_MAX16065 is not set -# CONFIG_SENSORS_MAX1619 is not set -# CONFIG_SENSORS_MAX1668 is not set -# CONFIG_SENSORS_MAX197 is not set -# CONFIG_SENSORS_MAX6639 is not set -# CONFIG_SENSORS_MAX6642 is not set -CONFIG_SENSORS_MAX6650=y -# CONFIG_SENSORS_MAX6697 is not set -# CONFIG_SENSORS_HTU21 is not set -# CONFIG_SENSORS_MCP3021 is not set -# CONFIG_SENSORS_ADCXX is not set -# CONFIG_SENSORS_LM63 is not set -# CONFIG_SENSORS_LM70 is not set -# CONFIG_SENSORS_LM73 is not set -CONFIG_SENSORS_LM75=y -# CONFIG_SENSORS_LM77 is not set -# CONFIG_SENSORS_LM78 is not set -# CONFIG_SENSORS_LM80 is not set -# CONFIG_SENSORS_LM83 is not set -CONFIG_SENSORS_LM85=y -# CONFIG_SENSORS_LM87 is not set -CONFIG_SENSORS_LM90=y -# CONFIG_SENSORS_LM92 is not set -# CONFIG_SENSORS_LM93 is not set -# CONFIG_SENSORS_LM95234 is not set -# CONFIG_SENSORS_LM95241 is not set -# CONFIG_SENSORS_LM95245 is not set -# CONFIG_SENSORS_PC87360 is not set -# CONFIG_SENSORS_PC87427 is not set -# CONFIG_SENSORS_NTC_THERMISTOR is not set -# CONFIG_SENSORS_NCT6683 is not set -# CONFIG_SENSORS_NCT6775 is not set -# CONFIG_SENSORS_PCF8591 is not set -# CONFIG_PMBUS is not set -# CONFIG_SENSORS_SHT15 is not set -# CONFIG_SENSORS_SHT21 is not set -# CONFIG_SENSORS_SHTC1 is not set -# CONFIG_SENSORS_SIS5595 is not set -# CONFIG_SENSORS_DME1737 is not set -# CONFIG_SENSORS_EMC1403 is not set -# CONFIG_SENSORS_EMC2103 is not set -# CONFIG_SENSORS_EMC6W201 is not set -# CONFIG_SENSORS_SMSC47M1 is not set -# CONFIG_SENSORS_SMSC47M192 is not set -# CONFIG_SENSORS_SMSC47B397 is not set -# CONFIG_SENSORS_SCH56XX_COMMON is not set -# CONFIG_SENSORS_SMM665 is not set -# CONFIG_SENSORS_ADC128D818 is not set -# CONFIG_SENSORS_ADS1015 is not set -# CONFIG_SENSORS_ADS7828 is not set -# CONFIG_SENSORS_ADS7871 is not set -# CONFIG_SENSORS_AMC6821 is not set -# CONFIG_SENSORS_INA209 is not set -# CONFIG_SENSORS_INA2XX is not set -# CONFIG_SENSORS_THMC50 is not set -# CONFIG_SENSORS_TMP102 is not set -# CONFIG_SENSORS_TMP103 is not set -# CONFIG_SENSORS_TMP401 is not set -# CONFIG_SENSORS_TMP421 is not set -# CONFIG_SENSORS_VIA_CPUTEMP is not set -# CONFIG_SENSORS_VIA686A is not set -# CONFIG_SENSORS_VT1211 is not set -# CONFIG_SENSORS_VT8231 is not set -CONFIG_SENSORS_W83781D=y -# CONFIG_SENSORS_W83791D is not set -# CONFIG_SENSORS_W83792D is not set -# CONFIG_SENSORS_W83793 is not set -# CONFIG_SENSORS_W83795 is not set -# CONFIG_SENSORS_W83L785TS is not set -# CONFIG_SENSORS_W83L786NG is not set -# CONFIG_SENSORS_W83627HF is not set -# CONFIG_SENSORS_W83627EHF is not set - -# -# ACPI drivers -# -# CONFIG_SENSORS_ACPI_POWER is not set -# CONFIG_SENSORS_ATK0110 is not set -CONFIG_THERMAL=y -CONFIG_THERMAL_HWMON=y -CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y -# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set -# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set -# CONFIG_THERMAL_GOV_FAIR_SHARE is not set -CONFIG_THERMAL_GOV_STEP_WISE=y -# CONFIG_THERMAL_GOV_BANG_BANG is not set -CONFIG_THERMAL_GOV_USER_SPACE=y -# CONFIG_THERMAL_EMULATION is not set -# CONFIG_INTEL_POWERCLAMP is not set -CONFIG_X86_PKG_TEMP_THERMAL=m -# CONFIG_INT340X_THERMAL is not set - -# -# Texas Instruments thermal drivers -# -# CONFIG_WATCHDOG is not set -CONFIG_SSB_POSSIBLE=y - -# -# Sonics Silicon Backplane -# -CONFIG_SSB=y -CONFIG_SSB_SPROM=y -CONFIG_SSB_PCIHOST_POSSIBLE=y -CONFIG_SSB_PCIHOST=y -# CONFIG_SSB_B43_PCI_BRIDGE is not set -CONFIG_SSB_PCMCIAHOST_POSSIBLE=y -CONFIG_SSB_PCMCIAHOST=y -CONFIG_SSB_SDIOHOST_POSSIBLE=y -CONFIG_SSB_SDIOHOST=y -# CONFIG_SSB_SILENT is not set -# CONFIG_SSB_DEBUG is not set -CONFIG_SSB_DRIVER_PCICORE_POSSIBLE=y -CONFIG_SSB_DRIVER_PCICORE=y -# CONFIG_SSB_DRIVER_GPIO is not set -CONFIG_BCMA_POSSIBLE=y - -# -# Broadcom specific AMBA -# -CONFIG_BCMA=y -CONFIG_BCMA_HOST_PCI_POSSIBLE=y -CONFIG_BCMA_HOST_PCI=y -# CONFIG_BCMA_HOST_SOC is not set -# CONFIG_BCMA_DRIVER_GMAC_CMN is not set -# CONFIG_BCMA_DRIVER_GPIO is not set -# CONFIG_BCMA_DEBUG is not set - -# -# Multifunction device drivers -# -CONFIG_MFD_CORE=y -# CONFIG_MFD_AS3711 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_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_HTC_PASIC3 is not set -# CONFIG_HTC_I2CPLD is not set -# CONFIG_LPC_ICH is not set -CONFIG_LPC_SCH=y -# 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_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_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_SYSCON is not set -# 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=y -# CONFIG_MFD_LM3533 is not set -# CONFIG_MFD_TC3589X is not set -# CONFIG_MFD_TMIO 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 is not set -# CONFIG_MEDIA_SUPPORT is not set - -# -# Graphics support -# -# CONFIG_AGP is not set -# CONFIG_VGA_ARB is not set -# CONFIG_VGA_SWITCHEROO is not set - -# -# Direct Rendering Manager -# -# CONFIG_DRM is not set - -# -# Frame buffer Devices -# -# CONFIG_FB is not set -# CONFIG_BACKLIGHT_LCD_SUPPORT is not set -# CONFIG_VGASTATE is not set - -# -# Console display driver support -# -CONFIG_VGA_CONSOLE=y -# CONFIG_VGACON_SOFT_SCROLLBACK is not set -CONFIG_DUMMY_CONSOLE=y -# 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_CP2112 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_GT683R 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_SONY 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_THINGM is not set -# CONFIG_HID_THRUSTMASTER is not set -# CONFIG_HID_WACOM is not set -# CONFIG_HID_WIIMOTE 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=y - -# -# 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_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=y -CONFIG_USB_XHCI_PCI=y -CONFIG_USB_EHCI_HCD=y -CONFIG_USB_EHCI_ROOT_HUB_TT=y -CONFIG_USB_EHCI_TT_NEWSCHED=y -CONFIG_USB_EHCI_PCI=y -# CONFIG_USB_EHCI_HCD_PLATFORM 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=y -CONFIG_USB_OHCI_HCD_PCI=y -# CONFIG_USB_OHCI_HCD_SSB is not set -# CONFIG_USB_OHCI_HCD_PLATFORM is not set -CONFIG_USB_UHCI_HCD=y -# CONFIG_USB_SL811_HCD is not set -# CONFIG_USB_R8A66597_HCD is not set -# CONFIG_USB_HCD_BCMA is not set -# CONFIG_USB_HCD_SSB 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=y -CONFIG_USB_SERIAL_CONSOLE=y -# CONFIG_USB_SERIAL_GENERIC is not set -# CONFIG_USB_SERIAL_SIMPLE is not set -# CONFIG_USB_SERIAL_AIRCABLE is not set -# CONFIG_USB_SERIAL_ARK3116 is not set -# CONFIG_USB_SERIAL_BELKIN is not set -# CONFIG_USB_SERIAL_CH341 is not set -# CONFIG_USB_SERIAL_WHITEHEAT is not set -# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set -# CONFIG_USB_SERIAL_CP210X is not set -# CONFIG_USB_SERIAL_CYPRESS_M8 is not set -# CONFIG_USB_SERIAL_EMPEG is not set -# CONFIG_USB_SERIAL_FTDI_SIO is not set -# CONFIG_USB_SERIAL_VISOR is not set -# CONFIG_USB_SERIAL_IPAQ is not set -# CONFIG_USB_SERIAL_IR is not set -# CONFIG_USB_SERIAL_EDGEPORT is not set -# CONFIG_USB_SERIAL_EDGEPORT_TI is not set -# CONFIG_USB_SERIAL_F81232 is not set -# CONFIG_USB_SERIAL_GARMIN is not set -# CONFIG_USB_SERIAL_IPW is not set -# CONFIG_USB_SERIAL_IUU is not set -# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set -# CONFIG_USB_SERIAL_KEYSPAN is not set -# CONFIG_USB_SERIAL_KLSI is not set -# CONFIG_USB_SERIAL_KOBIL_SCT is not set -# CONFIG_USB_SERIAL_MCT_U232 is not set -# CONFIG_USB_SERIAL_METRO is not set -# CONFIG_USB_SERIAL_MOS7720 is not set -# CONFIG_USB_SERIAL_MOS7840 is not set -# CONFIG_USB_SERIAL_MXUPORT is not set -# CONFIG_USB_SERIAL_NAVMAN is not set -# CONFIG_USB_SERIAL_PL2303 is not set -# CONFIG_USB_SERIAL_OTI6858 is not set -# CONFIG_USB_SERIAL_QCAUX is not set -# CONFIG_USB_SERIAL_QUALCOMM is not set -# CONFIG_USB_SERIAL_SPCP8X5 is not set -# CONFIG_USB_SERIAL_SAFE is not set -# CONFIG_USB_SERIAL_SIERRAWIRELESS is not set -# CONFIG_USB_SERIAL_SYMBOL is not set -# CONFIG_USB_SERIAL_TI is not set -# CONFIG_USB_SERIAL_CYBERJACK is not set -# CONFIG_USB_SERIAL_XIRCOM is not set -# CONFIG_USB_SERIAL_OPTION is not set -# CONFIG_USB_SERIAL_OMNINET is not set -# CONFIG_USB_SERIAL_OPTICON is not set -# CONFIG_USB_SERIAL_XSENS_MT is not set -# CONFIG_USB_SERIAL_WISHBONE is not set -# CONFIG_USB_SERIAL_SSU100 is not set -# CONFIG_USB_SERIAL_QT2 is not set -# CONFIG_USB_SERIAL_DEBUG 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_NOP_USB_XCEIV is not set -# CONFIG_USB_GPIO_VBUS is not set -# CONFIG_USB_ISP1301 is not set -# CONFIG_USB_GADGET is not set -# CONFIG_UWB is not set -CONFIG_MMC=y -# CONFIG_MMC_DEBUG is not set -# CONFIG_MMC_CLKGATE is not set - -# -# MMC/SD/SDIO Card Drivers -# -CONFIG_MMC_BLOCK=y -CONFIG_MMC_BLOCK_MINORS=8 -CONFIG_MMC_BLOCK_BOUNCE=y -# CONFIG_SDIO_UART is not set -# CONFIG_MMC_TEST is not set - -# -# MMC/SD/SDIO Host Controller Drivers -# -CONFIG_MMC_SDHCI=y -CONFIG_MMC_SDHCI_PCI=y -# CONFIG_MMC_RICOH_MMC is not set -# CONFIG_MMC_SDHCI_ACPI is not set -CONFIG_MMC_SDHCI_PLTFM=y -# CONFIG_MMC_WBSD is not set -# CONFIG_MMC_TIFM_SD is not set -CONFIG_MMC_SPI=y -# CONFIG_MMC_SDRICOH_CS is not set -# CONFIG_MMC_CB710 is not set -# CONFIG_MMC_VIA_SDMMC is not set -# CONFIG_MMC_VUB300 is not set -# CONFIG_MMC_USHC is not set -# CONFIG_MMC_USDHI6ROL0 is not set -# CONFIG_MEMSTICK is not set -CONFIG_NEW_LEDS=y -CONFIG_LEDS_CLASS=y - -# -# LED drivers -# -# CONFIG_LEDS_LM3530 is not set -# CONFIG_LEDS_LM3642 is not set -# CONFIG_LEDS_PCA9532 is not set -# CONFIG_LEDS_GPIO is not set -# CONFIG_LEDS_LP3944 is not set -# CONFIG_LEDS_LP5521 is not set -# CONFIG_LEDS_LP5523 is not set -# CONFIG_LEDS_LP5562 is not set -# CONFIG_LEDS_LP8501 is not set -# CONFIG_LEDS_PCA955X is not set -# CONFIG_LEDS_PCA963X is not set -# CONFIG_LEDS_DAC124S085 is not set -# CONFIG_LEDS_BD2802 is not set -# CONFIG_LEDS_INTEL_SS4200 is not set -# CONFIG_LEDS_LT3593 is not set -# CONFIG_LEDS_TCA6507 is not set -# CONFIG_LEDS_LM355x is not set - -# -# LED driver for blink(1) USB RGB LED is under Special HID drivers (HID_THINGM) -# -# CONFIG_LEDS_BLINKM is not set - -# -# LED Triggers -# -# CONFIG_LEDS_TRIGGERS 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=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=y -CONFIG_RTC_DRV_DS1374=y -CONFIG_RTC_DRV_DS1672=y -CONFIG_RTC_DRV_DS3232=y -CONFIG_RTC_DRV_MAX6900=y -CONFIG_RTC_DRV_RS5C372=y -CONFIG_RTC_DRV_ISL1208=y -CONFIG_RTC_DRV_ISL12022=y -# CONFIG_RTC_DRV_ISL12057 is not set -CONFIG_RTC_DRV_X1205=y -# CONFIG_RTC_DRV_PCF2127 is not set -# CONFIG_RTC_DRV_PCF8523 is not set -CONFIG_RTC_DRV_PCF8563=y -# CONFIG_RTC_DRV_PCF85063 is not set -CONFIG_RTC_DRV_PCF8583=y -CONFIG_RTC_DRV_M41T80=y -# CONFIG_RTC_DRV_M41T80_WDT is not set -CONFIG_RTC_DRV_BQ32K=y -CONFIG_RTC_DRV_S35390A=y -CONFIG_RTC_DRV_FM3130=y -CONFIG_RTC_DRV_RX8581=y -CONFIG_RTC_DRV_RX8025=y -# 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_CMOS=y -CONFIG_RTC_DRV_DS1286=y -CONFIG_RTC_DRV_DS1511=y -CONFIG_RTC_DRV_DS1553=y -CONFIG_RTC_DRV_DS1742=y -# CONFIG_RTC_DRV_DS2404 is not set -CONFIG_RTC_DRV_STK17TA8=y -CONFIG_RTC_DRV_M48T86=y -CONFIG_RTC_DRV_M48T35=y -CONFIG_RTC_DRV_M48T59=y -CONFIG_RTC_DRV_MSM6242=y -CONFIG_RTC_DRV_BQ4802=y -CONFIG_RTC_DRV_RP5C01=y -CONFIG_RTC_DRV_V3020=y - -# -# on-CPU RTC drivers -# -# 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=y -# CONFIG_UIO_CIF is not set -# CONFIG_UIO_PDRV_GENIRQ is not set -# CONFIG_UIO_DMEM_GENIRQ is not set -# CONFIG_UIO_AEC is not set -# CONFIG_UIO_SERCOS3 is not set -# CONFIG_UIO_PCI_GENERIC is not set -# CONFIG_UIO_NETX is not set -# CONFIG_UIO_MF624 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 -CONFIG_X86_PLATFORM_DEVICES=y -# CONFIG_ACERHDF is not set -# CONFIG_ASUS_LAPTOP is not set -# CONFIG_DELL_SMO8800 is not set -# CONFIG_FUJITSU_TABLET is not set -# CONFIG_HP_WIRELESS is not set -# CONFIG_THINKPAD_ACPI is not set -# CONFIG_SENSORS_HDAPS is not set -# CONFIG_INTEL_MENLOW is not set -# CONFIG_EEEPC_LAPTOP is not set -# CONFIG_ACPI_WMI is not set -# CONFIG_TOPSTAR_LAPTOP is not set -# CONFIG_TOSHIBA_BT_RFKILL is not set -# CONFIG_TOSHIBA_HAPS is not set -# CONFIG_ACPI_CMPC is not set -# CONFIG_INTEL_IPS is not set -# CONFIG_IBM_RTL is not set -# CONFIG_SAMSUNG_Q10 is not set -# CONFIG_INTEL_RST is not set -# CONFIG_INTEL_SMARTCONNECT is not set -# CONFIG_PVPANIC is not set -# CONFIG_CHROME_PLATFORMS is not set - -# -# SOC (System On Chip) specific Drivers -# -# CONFIG_SOC_TI is not set - -# -# Hardware Spinlock drivers -# - -# -# Clock Source drivers -# -CONFIG_CLKEVT_I8253=y -CONFIG_I8253_LOCK=y -CONFIG_CLKBLD_I8253=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_MAILBOX is not set -CONFIG_IOMMU_SUPPORT=y - -# -# Generic IOMMU Pagetable Support -# -# CONFIG_IOMMU_IO_PGTABLE_LPAE is not set -# CONFIG_AMD_IOMMU is not set -# CONFIG_INTEL_IOMMU is not set -# CONFIG_IRQ_REMAP 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_NTB is not set -# CONFIG_VME_BUS is not set -# CONFIG_PWM is not set -# CONFIG_IPACK_BUS is not set -# CONFIG_RESET_CONTROLLER is not set -# 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_RAS=y -# CONFIG_THUNDERBOLT is not set - -# -# Firmware Drivers -# -CONFIG_EDD=y -# CONFIG_EDD_OFF is not set -CONFIG_FIRMWARE_MEMMAP=y -CONFIG_DELL_RBU=y -CONFIG_DCDBAS=y -CONFIG_DMIID=y -CONFIG_DMI_SYSFS=y -CONFIG_DMI_SCAN_MACHINE_NON_EFI_FALLBACK=y -CONFIG_ISCSI_IBFT_FIND=y -CONFIG_ISCSI_IBFT=y -# CONFIG_GOOGLE_FIRMWARE is not set - -# -# File systems -# -CONFIG_DCACHE_WORD_ACCESS=y -CONFIG_EXT2_FS=y -CONFIG_EXT2_FS_XATTR=y -CONFIG_EXT2_FS_POSIX_ACL=y -CONFIG_EXT2_FS_SECURITY=y -# CONFIG_EXT2_FS_XIP is not set -CONFIG_EXT3_FS=y -CONFIG_EXT3_DEFAULTS_TO_ORDERED=y -CONFIG_EXT3_FS_XATTR=y -CONFIG_EXT3_FS_POSIX_ACL=y -CONFIG_EXT3_FS_SECURITY=y -CONFIG_EXT4_FS=y -CONFIG_EXT4_FS_POSIX_ACL=y -CONFIG_EXT4_FS_SECURITY=y -# CONFIG_EXT4_DEBUG is not set -CONFIG_JBD=y -# CONFIG_JBD_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=y -CONFIG_BTRFS_FS_POSIX_ACL=y -# CONFIG_BTRFS_FS_CHECK_INTEGRITY is not set -# CONFIG_BTRFS_FS_RUN_SANITY_TESTS is not set -# CONFIG_BTRFS_DEBUG is not set -# CONFIG_BTRFS_ASSERT is not set -# CONFIG_NILFS2_FS is not set -CONFIG_FS_POSIX_ACL=y -CONFIG_EXPORTFS=y -CONFIG_FILE_LOCKING=y -CONFIG_FSNOTIFY=y -CONFIG_DNOTIFY=y -CONFIG_INOTIFY_USER=y -CONFIG_FANOTIFY=y -# 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=y -CONFIG_FSCACHE_STATS=y -# CONFIG_FSCACHE_HISTOGRAM is not set -# CONFIG_FSCACHE_DEBUG is not set -# CONFIG_FSCACHE_OBJECT_LIST is not set -CONFIG_CACHEFILES=y -# CONFIG_CACHEFILES_DEBUG is not set -# CONFIG_CACHEFILES_HISTOGRAM is not set - -# -# CD-ROM/DVD Filesystems -# -CONFIG_ISO9660_FS=y -CONFIG_JOLIET=y -CONFIG_ZISOFS=y -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_KCORE=y -CONFIG_PROC_VMCORE=y -CONFIG_PROC_SYSCTL=y -CONFIG_PROC_PAGE_MONITOR=y -CONFIG_KERNFS=y -CONFIG_SYSFS=y -CONFIG_TMPFS=y -CONFIG_TMPFS_POSIX_ACL=y -CONFIG_TMPFS_XATTR=y -CONFIG_HUGETLBFS=y -CONFIG_HUGETLB_PAGE=y -CONFIG_CONFIGFS_FS=y -CONFIG_MISC_FILESYSTEMS=y -# CONFIG_ADFS_FS is not set -# CONFIG_AFFS_FS is not set -# CONFIG_ECRYPT_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_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=y -CONFIG_SQUASHFS_ZLIB=y -CONFIG_SQUASHFS_LZO=y -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_EXOFS_FS is not set -# CONFIG_F2FS_FS is not set -CONFIG_AUFS_FS=y -CONFIG_AUFS_BRANCH_MAX_127=y -# CONFIG_AUFS_BRANCH_MAX_511 is not set -# CONFIG_AUFS_BRANCH_MAX_1023 is not set -# CONFIG_AUFS_BRANCH_MAX_32767 is not set -CONFIG_AUFS_SBILIST=y -# CONFIG_AUFS_HNOTIFY is not set -# CONFIG_AUFS_EXPORT is not set -# CONFIG_AUFS_XATTR is not set -# CONFIG_AUFS_FHSM is not set -# CONFIG_AUFS_RDU is not set -# CONFIG_AUFS_SHWH is not set -# CONFIG_AUFS_BR_RAMFS is not set -CONFIG_AUFS_BDEV_LOOP=y -# CONFIG_AUFS_DEBUG is not set -CONFIG_ORE=y -CONFIG_NETWORK_FILESYSTEMS=y -CONFIG_NFS_FS=y -CONFIG_NFS_V2=y -CONFIG_NFS_V3=y -CONFIG_NFS_V3_ACL=y -CONFIG_NFS_V4=y -# CONFIG_NFS_SWAP is not set -CONFIG_NFS_V4_1=y -# CONFIG_NFS_V4_2 is not set -CONFIG_PNFS_FILE_LAYOUT=y -CONFIG_PNFS_OBJLAYOUT=y -CONFIG_NFS_V4_1_IMPLEMENTATION_ID_DOMAIN="kernel.org" -# CONFIG_NFS_V4_1_MIGRATION is not set -# CONFIG_NFS_FSCACHE is not set -# CONFIG_NFS_USE_LEGACY_DNS is not set -CONFIG_NFS_USE_KERNEL_DNS=y -CONFIG_NFSD=y -CONFIG_NFSD_V2_ACL=y -CONFIG_NFSD_V3=y -CONFIG_NFSD_V3_ACL=y -CONFIG_NFSD_V4=y -# CONFIG_NFSD_FAULT_INJECTION 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_GSS=y -CONFIG_SUNRPC_BACKCHANNEL=y -# CONFIG_RPCSEC_GSS_KRB5 is not set -# 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="utf8" -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=y -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 -# -CONFIG_TRACE_IRQFLAGS_SUPPORT=y - -# -# printk and dmesg options -# -# CONFIG_PRINTK_TIME is not set -CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 -CONFIG_BOOT_PRINTK_DELAY=y -# CONFIG_DYNAMIC_DEBUG is not set - -# -# Compile-time checks and compiler options -# -CONFIG_DEBUG_INFO=y -# CONFIG_DEBUG_INFO_REDUCED is not set -# CONFIG_DEBUG_INFO_SPLIT is not set -# CONFIG_DEBUG_INFO_DWARF4 is not set -CONFIG_ENABLE_WARN_DEPRECATED=y -CONFIG_ENABLE_MUST_CHECK=y -CONFIG_FRAME_WARN=2048 -CONFIG_STRIP_ASM_SYMS=y -# CONFIG_READABLE_ASM is not set -CONFIG_UNUSED_SYMBOLS=y -CONFIG_DEBUG_FS=y -# CONFIG_HEADERS_CHECK is not set -# CONFIG_DEBUG_SECTION_MISMATCH is not set -CONFIG_ARCH_WANT_FRAME_POINTERS=y -# CONFIG_FRAME_POINTER is not set -# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set -CONFIG_MAGIC_SYSRQ=y -CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x1 -CONFIG_DEBUG_KERNEL=y - -# -# Memory Debugging -# -# CONFIG_DEBUG_PAGEALLOC is not set -# CONFIG_DEBUG_OBJECTS is not set -# CONFIG_DEBUG_SLAB 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_VIRTUAL is not set -CONFIG_DEBUG_MEMORY_INIT=y -# CONFIG_DEBUG_PER_CPU_MAPS is not set -CONFIG_HAVE_DEBUG_STACKOVERFLOW=y -# CONFIG_DEBUG_STACKOVERFLOW is not set -CONFIG_HAVE_ARCH_KMEMCHECK=y -# CONFIG_DEBUG_SHIRQ is not set - -# -# Debug Lockups and Hangs -# -CONFIG_LOCKUP_DETECTOR=y -CONFIG_HARDLOCKUP_DETECTOR=y -# CONFIG_BOOTPARAM_HARDLOCKUP_PANIC is not set -CONFIG_BOOTPARAM_HARDLOCKUP_PANIC_VALUE=0 -# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set -CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 -CONFIG_DETECT_HUNG_TASK=y -CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120 -# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set -CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 -# 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=y - -# -# 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_LATENCYTOP is not set -CONFIG_ARCH_HAS_DEBUG_STRICT_USER_COPY_CHECKS=y -# CONFIG_DEBUG_STRICT_USER_COPY_CHECKS is not set -CONFIG_USER_STACKTRACE_SUPPORT=y -CONFIG_NOP_TRACER=y -CONFIG_HAVE_FUNCTION_TRACER=y -CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y -CONFIG_HAVE_FUNCTION_GRAPH_FP_TEST=y -CONFIG_HAVE_DYNAMIC_FTRACE=y -CONFIG_HAVE_DYNAMIC_FTRACE_WITH_REGS=y -CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y -CONFIG_HAVE_SYSCALL_TRACEPOINTS=y -CONFIG_HAVE_FENTRY=y -CONFIG_HAVE_C_RECORDMCOUNT=y -CONFIG_TRACE_CLOCK=y -CONFIG_RING_BUFFER=y -CONFIG_EVENT_TRACING=y -CONFIG_CONTEXT_SWITCH_TRACER=y -CONFIG_TRACING=y -CONFIG_GENERIC_TRACER=y -CONFIG_TRACING_SUPPORT=y -CONFIG_FTRACE=y -# CONFIG_FUNCTION_TRACER is not set -# CONFIG_IRQSOFF_TRACER is not set -# CONFIG_SCHED_TRACER is not set -# CONFIG_FTRACE_SYSCALLS is not set -# CONFIG_TRACER_SNAPSHOT is not set -CONFIG_BRANCH_PROFILE_NONE=y -# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set -# CONFIG_PROFILE_ALL_BRANCHES is not set -# CONFIG_STACK_TRACER is not set -CONFIG_BLK_DEV_IO_TRACE=y -# CONFIG_UPROBE_EVENT is not set -# CONFIG_PROBE_EVENTS is not set -# CONFIG_FTRACE_STARTUP_TEST is not set -# CONFIG_MMIOTRACE is not set -# CONFIG_TRACEPOINT_BENCHMARK is not set -# CONFIG_RING_BUFFER_BENCHMARK is not set -# CONFIG_RING_BUFFER_STARTUP_TEST is not set - -# -# Runtime Testing -# -# CONFIG_LKDTM is not set -# 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_PROVIDE_OHCI1394_DMA_INIT 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_STRICT_DEVMEM=y -CONFIG_X86_VERBOSE_BOOTUP=y -CONFIG_EARLY_PRINTK=y -# CONFIG_EARLY_PRINTK_DBGP is not set -# CONFIG_X86_PTDUMP is not set -CONFIG_DEBUG_RODATA=y -# CONFIG_DEBUG_RODATA_TEST is not set -# CONFIG_DEBUG_SET_MODULE_RONX is not set -# CONFIG_DEBUG_NX_TEST is not set -CONFIG_DOUBLEFAULT=y -# CONFIG_DEBUG_TLBFLUSH is not set -# CONFIG_IOMMU_DEBUG is not set -# CONFIG_IOMMU_STRESS is not set -CONFIG_HAVE_MMIOTRACE_SUPPORT=y -CONFIG_IO_DELAY_TYPE_0X80=0 -CONFIG_IO_DELAY_TYPE_0XED=1 -CONFIG_IO_DELAY_TYPE_UDELAY=2 -CONFIG_IO_DELAY_TYPE_NONE=3 -CONFIG_IO_DELAY_0X80=y -# CONFIG_IO_DELAY_0XED is not set -# CONFIG_IO_DELAY_UDELAY is not set -# CONFIG_IO_DELAY_NONE is not set -CONFIG_DEFAULT_IO_DELAY_TYPE=0 -# CONFIG_DEBUG_BOOT_PARAMS is not set -# CONFIG_CPA_DEBUG is not set -CONFIG_OPTIMIZE_INLINING=y -# CONFIG_DEBUG_NMI_SELFTEST is not set -# CONFIG_X86_DEBUG_STATIC_CPU_HAS is not set - -# -# Security options -# -CONFIG_KEYS=y -# CONFIG_PERSISTENT_KEYRINGS is not set -# CONFIG_BIG_KEYS is not set -# CONFIG_ENCRYPTED_KEYS is not set -# CONFIG_KEYS_DEBUG_PROC_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_XOR_BLOCKS=y -CONFIG_ASYNC_CORE=y -CONFIG_ASYNC_XOR=y -CONFIG_ASYNC_PQ=y -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_PCOMP=y -CONFIG_CRYPTO_PCOMP2=y -CONFIG_CRYPTO_MANAGER=y -CONFIG_CRYPTO_MANAGER2=y -# CONFIG_CRYPTO_USER is not set -# CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set -CONFIG_CRYPTO_GF128MUL=y -CONFIG_CRYPTO_NULL=y -# CONFIG_CRYPTO_PCRYPT is not set -CONFIG_CRYPTO_WORKQUEUE=y -CONFIG_CRYPTO_CRYPTD=y -# CONFIG_CRYPTO_MCRYPTD is not set -CONFIG_CRYPTO_AUTHENC=y -# CONFIG_CRYPTO_TEST is not set -CONFIG_CRYPTO_ABLK_HELPER=y -CONFIG_CRYPTO_GLUE_HELPER_X86=y - -# -# Authenticated Encryption with Associated Data -# -CONFIG_CRYPTO_CCM=y -CONFIG_CRYPTO_GCM=y -CONFIG_CRYPTO_SEQIV=y - -# -# Block modes -# -CONFIG_CRYPTO_CBC=y -CONFIG_CRYPTO_CTR=y -CONFIG_CRYPTO_CTS=y -CONFIG_CRYPTO_ECB=y -CONFIG_CRYPTO_LRW=y -CONFIG_CRYPTO_PCBC=y -CONFIG_CRYPTO_XTS=y - -# -# Hash modes -# -# CONFIG_CRYPTO_CMAC is not set -CONFIG_CRYPTO_HMAC=y -CONFIG_CRYPTO_XCBC=y -CONFIG_CRYPTO_VMAC=y - -# -# Digest -# -CONFIG_CRYPTO_CRC32C=y -CONFIG_CRYPTO_CRC32C_INTEL=y -# CONFIG_CRYPTO_CRC32 is not set -# CONFIG_CRYPTO_CRC32_PCLMUL is not set -CONFIG_CRYPTO_CRCT10DIF=y -# CONFIG_CRYPTO_CRCT10DIF_PCLMUL is not set -CONFIG_CRYPTO_GHASH=y -CONFIG_CRYPTO_MD4=y -CONFIG_CRYPTO_MD5=y -CONFIG_CRYPTO_MICHAEL_MIC=y -CONFIG_CRYPTO_RMD128=y -CONFIG_CRYPTO_RMD160=y -CONFIG_CRYPTO_RMD256=y -CONFIG_CRYPTO_RMD320=y -CONFIG_CRYPTO_SHA1=y -CONFIG_CRYPTO_SHA1_SSSE3=y -# CONFIG_CRYPTO_SHA256_SSSE3 is not set -# CONFIG_CRYPTO_SHA512_SSSE3 is not set -# CONFIG_CRYPTO_SHA1_MB is not set -CONFIG_CRYPTO_SHA256=y -CONFIG_CRYPTO_SHA512=y -CONFIG_CRYPTO_TGR192=y -CONFIG_CRYPTO_WP512=y -CONFIG_CRYPTO_GHASH_CLMUL_NI_INTEL=y - -# -# Ciphers -# -CONFIG_CRYPTO_AES=y -CONFIG_CRYPTO_AES_X86_64=y -CONFIG_CRYPTO_AES_NI_INTEL=y -CONFIG_CRYPTO_ANUBIS=y -CONFIG_CRYPTO_ARC4=y -CONFIG_CRYPTO_BLOWFISH=y -CONFIG_CRYPTO_BLOWFISH_COMMON=y -CONFIG_CRYPTO_BLOWFISH_X86_64=y -CONFIG_CRYPTO_CAMELLIA=y -# CONFIG_CRYPTO_CAMELLIA_X86_64 is not set -# CONFIG_CRYPTO_CAMELLIA_AESNI_AVX_X86_64 is not set -# CONFIG_CRYPTO_CAMELLIA_AESNI_AVX2_X86_64 is not set -CONFIG_CRYPTO_CAST_COMMON=y -CONFIG_CRYPTO_CAST5=y -# CONFIG_CRYPTO_CAST5_AVX_X86_64 is not set -CONFIG_CRYPTO_CAST6=y -# CONFIG_CRYPTO_CAST6_AVX_X86_64 is not set -CONFIG_CRYPTO_DES=y -# CONFIG_CRYPTO_DES3_EDE_X86_64 is not set -CONFIG_CRYPTO_FCRYPT=y -CONFIG_CRYPTO_KHAZAD=y -CONFIG_CRYPTO_SALSA20=y -CONFIG_CRYPTO_SALSA20_X86_64=y -CONFIG_CRYPTO_SEED=y -CONFIG_CRYPTO_SERPENT=y -# CONFIG_CRYPTO_SERPENT_SSE2_X86_64 is not set -# CONFIG_CRYPTO_SERPENT_AVX_X86_64 is not set -# CONFIG_CRYPTO_SERPENT_AVX2_X86_64 is not set -CONFIG_CRYPTO_TEA=y -CONFIG_CRYPTO_TWOFISH=y -CONFIG_CRYPTO_TWOFISH_COMMON=y -CONFIG_CRYPTO_TWOFISH_X86_64=y -CONFIG_CRYPTO_TWOFISH_X86_64_3WAY=y -# CONFIG_CRYPTO_TWOFISH_AVX_X86_64 is not set - -# -# Compression -# -CONFIG_CRYPTO_DEFLATE=y -CONFIG_CRYPTO_ZLIB=y -CONFIG_CRYPTO_LZO=y -# CONFIG_CRYPTO_LZ4 is not set -# CONFIG_CRYPTO_LZ4HC is not set - -# -# Random Number Generation -# -CONFIG_CRYPTO_ANSI_CPRNG=y -# CONFIG_CRYPTO_DRBG_MENU is not set -CONFIG_CRYPTO_USER_API=y -CONFIG_CRYPTO_USER_API_HASH=y -CONFIG_CRYPTO_USER_API_SKCIPHER=y -CONFIG_CRYPTO_HW=y -CONFIG_CRYPTO_DEV_PADLOCK=y -CONFIG_CRYPTO_DEV_PADLOCK_AES=y -CONFIG_CRYPTO_DEV_PADLOCK_SHA=y -# CONFIG_CRYPTO_DEV_CCP is not set -# CONFIG_CRYPTO_DEV_QAT_DH895xCC is not set -# CONFIG_ASYMMETRIC_KEY_TYPE is not set -CONFIG_HAVE_KVM=y -# CONFIG_VIRTUALIZATION is not set -CONFIG_BINARY_PRINTF=y - -# -# Library routines -# -CONFIG_RAID6_PQ=y -CONFIG_BITREVERSE=y -CONFIG_GENERIC_STRNCPY_FROM_USER=y -CONFIG_GENERIC_STRNLEN_USER=y -CONFIG_GENERIC_NET_UTILS=y -CONFIG_GENERIC_FIND_FIRST_BIT=y -CONFIG_GENERIC_PCI_IOMAP=y -CONFIG_GENERIC_IOMAP=y -CONFIG_GENERIC_IO=y -CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y -CONFIG_ARCH_HAS_FAST_MULTIPLIER=y -CONFIG_CRC_CCITT=y -CONFIG_CRC16=y -CONFIG_CRC_T10DIF=y -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=y -CONFIG_LIBCRC32C=y -CONFIG_CRC8=y -# 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_DECOMPRESS_GZIP=y -CONFIG_DECOMPRESS_BZIP2=y -CONFIG_DECOMPRESS_LZMA=y -CONFIG_DECOMPRESS_XZ=y -CONFIG_DECOMPRESS_LZO=y -CONFIG_TEXTSEARCH=y -CONFIG_TEXTSEARCH_KMP=y -CONFIG_TEXTSEARCH_BM=y -CONFIG_TEXTSEARCH_FSM=y -CONFIG_ASSOCIATIVE_ARRAY=y -CONFIG_HAS_IOMEM=y -CONFIG_HAS_IOPORT_MAP=y -CONFIG_HAS_DMA=y -CONFIG_CHECK_SIGNATURE=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=y -# CONFIG_DDR is not set -CONFIG_OID_REGISTRY=y -CONFIG_ARCH_HAS_SG_CHAIN=y diff --git a/packages/base/any/kernels/3.18.25/kconfig.mk b/packages/base/any/kernels/3.18.25/kconfig.mk deleted file mode 100644 index 0fab8c89..00000000 --- a/packages/base/any/kernels/3.18.25/kconfig.mk +++ /dev/null @@ -1,31 +0,0 @@ -############################################################ -# -# -# Copyright 2015 Big Switch Networks, Inc. -# -# Licensed under the Eclipse Public License, Version 1.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.eclipse.org/legal/epl-v10.html -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, -# either express or implied. See the License for the specific -# language governing permissions and limitations under the -# License. -# -# -############################################################ -# -# 3.18.25 Kernel Builds -# -############################################################ -THIS_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) -K_MAJOR_VERSION := 3 -K_PATCH_LEVEL := 18 -K_SUB_LEVEL := 25 -K_SUFFIX := -K_PATCH_DIR := $(THIS_DIR)/patches -K_ARCHIVE_URL := http://opennetlinux.org/tarballs/linux-3.18.25.tar.xz diff --git a/packages/base/any/kernels/3.18.25/patches/0001-Patch-set-for-booting-ls2088rdb-with-vfio.patch b/packages/base/any/kernels/3.18.25/patches/0001-Patch-set-for-booting-ls2088rdb-with-vfio.patch deleted file mode 100644 index ad0e4a06..00000000 --- a/packages/base/any/kernels/3.18.25/patches/0001-Patch-set-for-booting-ls2088rdb-with-vfio.patch +++ /dev/null @@ -1,101360 +0,0 @@ -From 7ab86f28bfb4d36d4d741a41941a0aa971124d88 Mon Sep 17 00:00:00 2001 -From: "Chenyin.Ha" -Date: Fri, 19 May 2017 13:44:09 +0800 -Subject: [PATCH] Patch set for booting ls2088rdb with vfio - ---- - Documentation/IRQ-domain.txt | 71 + - Documentation/devicetree/bindings/arm/fsl.txt | 15 + - Documentation/devicetree/bindings/arm/gic.txt | 8 +- - .../devicetree/bindings/clock/qoriq-clock.txt | 64 +- - Documentation/devicetree/bindings/i2c/i2c-imx.txt | 11 + - .../devicetree/bindings/i2c/i2c-mux-pca954x.txt | 3 + - .../bindings/memory-controllers/fsl/ifc.txt | 3 + - .../devicetree/bindings/pci/designware-pcie.txt | 3 +- - .../devicetree/bindings/powerpc/fsl/board.txt | 14 +- - Documentation/devicetree/bindings/usb/dwc3.txt | 3 +- - Documentation/devicetree/of_selftest.txt | 20 +- - Documentation/devicetree/todo.txt | 1 - - MAINTAINERS | 60 + - arch/arm/Kconfig | 3 + - arch/arm/Makefile | 8 +- - arch/arm/boot/dts/Makefile | 12 +- - arch/arm/include/asm/dma-mapping.h | 10 +- - arch/arm/include/asm/mach/pci.h | 12 +- - arch/arm/include/asm/pci.h | 7 - - arch/arm/kernel/bios32.c | 39 +- - arch/arm/mach-iop13xx/msi.c | 10 +- - arch/arm64/Kconfig | 8 +- - arch/arm64/Makefile | 11 +- - arch/arm64/boot/dts/Makefile | 2 +- - arch/arm64/boot/dts/Makefile.rej | 10 + - arch/arm64/boot/dts/arm64-nxp-ls2080ardb-r0.dts | 249 ++ - arch/arm64/boot/dts/arm64-nxp-ls2088ardb-r1.dts | 256 ++ - arch/arm64/boot/dts/fsl-ls2080a.dtsi | 729 +++++ - arch/arm64/boot/dts/fsl-ls2088a.dtsi | 833 ++++++ - arch/arm64/boot/dts/include/dt-bindings | 1 + - arch/arm64/boot/dts/thermal.h | 17 + - arch/arm64/configs/defconfig | 1 + - arch/arm64/configs/nxp_ls2088rdb_config | 3034 ++++++++++++++++++++ - arch/arm64/include/asm/device.h | 1 + - arch/arm64/include/asm/dma-mapping.h | 16 +- - arch/arm64/include/asm/io.h | 1 + - arch/arm64/include/asm/mmu_context.h | 43 + - arch/arm64/include/asm/page.h | 6 +- - arch/arm64/include/asm/pgtable-hwdef.h | 7 +- - arch/arm64/include/asm/pgtable.h | 8 + - arch/arm64/kernel/head.S | 37 + - arch/arm64/kernel/smp.c | 1 + - arch/arm64/mm/mmu.c | 7 +- - arch/arm64/mm/proc-macros.S | 10 + - arch/arm64/mm/proc.S | 3 + - arch/ia64/kernel/msi_ia64.c | 8 +- - arch/ia64/sn/kernel/msi_sn.c | 8 +- - arch/mips/pci/msi-octeon.c | 2 +- - arch/mips/pci/msi-xlp.c | 12 +- - arch/mips/pci/pci-xlr.c | 2 +- - arch/powerpc/include/asm/mpc85xx.h | 94 - - arch/powerpc/platforms/512x/mpc5121_ads_cpld.c | 3 +- - arch/powerpc/platforms/85xx/mpc85xx_mds.c | 2 +- - arch/powerpc/platforms/85xx/mpc85xx_rdb.c | 2 +- - arch/powerpc/platforms/85xx/p1022_ds.c | 2 +- - arch/powerpc/platforms/85xx/p1022_rdk.c | 2 +- - arch/powerpc/platforms/85xx/smp.c | 2 +- - arch/powerpc/platforms/85xx/twr_p102x.c | 2 +- - arch/powerpc/platforms/86xx/mpc8610_hpcd.c | 2 +- - arch/powerpc/platforms/cell/axon_msi.c | 8 +- - arch/powerpc/platforms/cell/interrupt.c | 3 +- - arch/powerpc/platforms/embedded6xx/flipper-pic.c | 3 +- - arch/powerpc/platforms/powermac/pic.c | 3 +- - arch/powerpc/platforms/powernv/pci.c | 2 +- - arch/powerpc/platforms/ps3/interrupt.c | 3 +- - arch/powerpc/platforms/pseries/msi.c | 2 +- - arch/powerpc/sysdev/ehv_pic.c | 3 +- - arch/powerpc/sysdev/fsl_msi.c | 6 +- - arch/powerpc/sysdev/i8259.c | 3 +- - arch/powerpc/sysdev/ipic.c | 3 +- - arch/powerpc/sysdev/mpic.c | 3 +- - arch/powerpc/sysdev/mpic_pasemi_msi.c | 6 +- - arch/powerpc/sysdev/mpic_u3msi.c | 6 +- - arch/powerpc/sysdev/ppc4xx_hsta_msi.c | 2 +- - arch/powerpc/sysdev/ppc4xx_msi.c | 2 +- - arch/powerpc/sysdev/qe_lib/qe_ic.c | 3 +- - arch/powerpc/sysdev/xics/ics-opal.c | 2 +- - arch/powerpc/sysdev/xics/ics-rtas.c | 2 +- - arch/powerpc/sysdev/xics/xics-common.c | 3 +- - arch/s390/pci/pci.c | 10 +- - arch/sparc/kernel/pci_msi.c | 10 +- - arch/tile/kernel/pci_gx.c | 8 +- - arch/x86/include/asm/x86_init.h | 3 - - arch/x86/kernel/apic/io_apic.c | 8 +- - arch/x86/kernel/x86_init.c | 10 - - arch/x86/pci/bus_numa.c | 4 +- - arch/x86/pci/xen.c | 23 +- - drivers/acpi/acpi_lpss.c | 8 +- - drivers/acpi/acpi_platform.c | 4 +- - drivers/acpi/resource.c | 17 +- - drivers/base/core.c | 3 + - drivers/base/platform.c | 1 + - drivers/block/loop.c | 18 + - drivers/clk/Kconfig | 10 +- - drivers/clk/Makefile | 2 +- - drivers/clk/clk-qoriq.c | 1256 ++++++++ - drivers/cpufreq/Kconfig.powerpc | 2 +- - drivers/dma/acpi-dma.c | 10 +- - drivers/i2c/busses/Kconfig | 4 +- - drivers/i2c/busses/i2c-imx.c | 373 ++- - drivers/i2c/muxes/i2c-mux-pca9541.c | 4 +- - drivers/i2c/muxes/i2c-mux-pca954x.c | 57 +- - drivers/iommu/Kconfig | 34 +- - drivers/iommu/Makefile | 2 + - drivers/iommu/amd_iommu.c | 6 +- - drivers/iommu/arm-smmu.c | 1382 +++++---- - drivers/iommu/exynos-iommu.c | 2 +- - drivers/iommu/fsl_pamu.c | 3 +- - drivers/iommu/intel-iommu.c | 1 + - drivers/iommu/io-pgtable-arm.c | 997 +++++++ - drivers/iommu/io-pgtable.c | 82 + - drivers/iommu/io-pgtable.h | 143 + - drivers/iommu/iommu.c | 111 +- - drivers/iommu/ipmmu-vmsa.c | 2 +- - drivers/iommu/irq_remapping.c | 8 - - drivers/iommu/msm_iommu.c | 1 + - drivers/iommu/of_iommu.c | 95 + - drivers/iommu/omap-iommu.c | 1 + - drivers/iommu/shmobile-iommu.c | 1 + - drivers/iommu/shmobile-ipmmu.c | 1 - - drivers/iommu/tegra-gart.c | 1 - - drivers/iommu/tegra-smmu.c | 2 +- - drivers/irqchip/Kconfig | 12 + - drivers/irqchip/Makefile | 2 + - drivers/irqchip/irq-armada-370-xp.c | 16 +- - drivers/irqchip/irq-atmel-aic.c | 40 +- - drivers/irqchip/irq-atmel-aic5.c | 65 +- - drivers/irqchip/irq-gic-common.c | 18 +- - drivers/irqchip/irq-gic-common.h | 2 +- - drivers/irqchip/irq-gic-v2m.c | 333 +++ - drivers/irqchip/irq-gic-v3-its.c | 1630 +++++++++++ - drivers/irqchip/irq-gic-v3.c | 180 +- - drivers/irqchip/irq-gic.c | 90 +- - drivers/irqchip/irq-hip04.c | 9 +- - drivers/irqchip/irq-sunxi-nmi.c | 4 +- - drivers/irqchip/irq-tb10x.c | 4 +- - drivers/memory/Kconfig | 2 +- - drivers/memory/fsl_ifc.c | 77 +- - drivers/mfd/vexpress-sysreg.c | 2 +- - drivers/mmc/card/block.c | 4 + - drivers/mmc/host/Kconfig | 10 +- - drivers/mmc/host/sdhci-esdhc.h | 9 +- - drivers/mmc/host/sdhci-of-esdhc.c | 680 ++++- - drivers/mmc/host/sdhci.c | 250 +- - drivers/mmc/host/sdhci.h | 42 + - drivers/mtd/nand/Kconfig | 2 +- - drivers/mtd/nand/fsl_ifc_nand.c | 301 +- - drivers/net/ethernet/freescale/Kconfig | 8 +- - drivers/net/ethernet/freescale/fec_mpc52xx.c | 2 +- - drivers/net/ethernet/freescale/fec_mpc52xx_phy.c | 2 +- - .../net/ethernet/freescale/fs_enet/fs_enet-main.c | 4 +- - .../net/ethernet/freescale/fs_enet/mii-bitbang.c | 2 +- - drivers/net/ethernet/freescale/fs_enet/mii-fec.c | 4 +- - drivers/net/ethernet/freescale/fsl_pq_mdio.c | 2 +- - drivers/net/ethernet/freescale/gianfar.c | 8 +- - drivers/net/ethernet/freescale/gianfar_ptp.c | 2 +- - drivers/net/ethernet/freescale/ucc_geth.c | 2 +- - drivers/net/ethernet/freescale/xgmac_mdio.c | 194 +- - drivers/net/ethernet/intel/igb/e1000_82575.c | 6 + - drivers/net/ethernet/intel/igb/e1000_defines.h | 1 + - drivers/net/ethernet/intel/igb/e1000_hw.h | 1 + - drivers/net/ethernet/intel/igb/igb_main.c | 1 + - drivers/net/phy/Kconfig | 19 +- - drivers/net/phy/Makefile | 5 +- - drivers/net/phy/aquantia.c | 201 ++ - drivers/net/phy/at803x.c | 4 + - drivers/net/phy/fixed.c | 336 --- - drivers/net/phy/fixed_phy.c | 370 +++ - drivers/net/phy/fsl_10gkr.c | 1467 ++++++++++ - drivers/net/phy/marvell.c | 11 + - drivers/net/phy/mdio_bus.c | 34 +- - drivers/net/phy/phy.c | 19 +- - drivers/net/phy/phy_device.c | 90 +- - drivers/net/phy/realtek.c | 82 +- - drivers/net/phy/teranetics.c | 135 + - drivers/of/base.c | 53 +- - drivers/of/device.c | 84 + - drivers/of/dynamic.c | 13 - - drivers/of/fdt.c | 30 +- - drivers/of/irq.c | 21 + - drivers/of/of_pci.c | 34 +- - drivers/of/pdt.c | 27 +- - drivers/of/platform.c | 139 +- - drivers/of/selftest.c | 71 +- - drivers/pci/Kconfig | 6 + - drivers/pci/Makefile | 1 + - drivers/pci/access.c | 87 + - drivers/pci/bus.c | 18 +- - drivers/pci/host-bridge.c | 22 +- - drivers/pci/host/Kconfig | 19 +- - drivers/pci/host/Makefile | 3 + - drivers/pci/host/pci-dra7xx.c | 8 +- - drivers/pci/host/pci-exynos.c | 5 +- - drivers/pci/host/pci-host-generic.c | 229 +- - drivers/pci/host/pci-keystone-dw.c | 37 +- - drivers/pci/host/pci-keystone.h | 4 +- - drivers/pci/host/pci-layerscape.c | 729 +++++ - drivers/pci/host/pci-layerscape.h | 13 + - drivers/pci/host/pci-mvebu.c | 17 +- - drivers/pci/host/pci-tegra.c | 22 +- - drivers/pci/host/pci-xgene-msi.c | 595 ++++ - drivers/pci/host/pci-xgene.c | 25 +- - drivers/pci/host/pcie-designware.c | 665 ++--- - drivers/pci/host/pcie-designware.h | 24 +- - drivers/pci/host/pcie-rcar.c | 22 +- - drivers/pci/host/pcie-xilinx.c | 64 +- - drivers/pci/msi.c | 533 ++-- - drivers/pci/pci.c | 1 + - drivers/pci/pci.h | 21 + - drivers/pci/pcie/portdrv_core.c | 31 +- - drivers/pci/probe.c | 29 +- - drivers/pci/quirks.c | 10 +- - drivers/pci/remove.c | 2 + - drivers/pci/search.c | 5 +- - drivers/pci/setup-bus.c | 1 + - drivers/pci/setup-irq.c | 1 + - drivers/pci/xen-pcifront.c | 2 +- - drivers/power/reset/Kconfig | 6 + - drivers/power/reset/Makefile | 1 + - drivers/power/reset/ls-reboot.c | 93 + - drivers/soc/Kconfig | 13 + - drivers/soc/Makefile | 1 + - drivers/soc/fsl/Kconfig | 6 + - drivers/soc/fsl/Kconfig.arm | 25 + - drivers/soc/fsl/Makefile | 6 + - drivers/soc/fsl/guts.c | 123 + - drivers/soc/fsl/ls1/Kconfig | 11 + - drivers/soc/fsl/ls1/Makefile | 1 + - drivers/soc/fsl/ls1/ftm_alarm.c | 274 ++ - drivers/staging/Kconfig | 4 + - drivers/staging/Makefile | 2 + - drivers/staging/fsl-dpaa2/Kconfig | 12 + - drivers/staging/fsl-dpaa2/Makefile | 6 + - drivers/staging/fsl-dpaa2/ethernet/Kconfig | 36 + - drivers/staging/fsl-dpaa2/ethernet/Makefile | 21 + - .../staging/fsl-dpaa2/ethernet/dpaa2-eth-debugfs.c | 317 ++ - .../staging/fsl-dpaa2/ethernet/dpaa2-eth-debugfs.h | 61 + - .../staging/fsl-dpaa2/ethernet/dpaa2-eth-trace.h | 185 ++ - drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c | 2957 +++++++++++++++++++ - drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.h | 397 +++ - drivers/staging/fsl-dpaa2/ethernet/dpaa2-ethtool.c | 732 +++++ - drivers/staging/fsl-dpaa2/ethernet/dpkg.h | 175 ++ - drivers/staging/fsl-dpaa2/ethernet/dpni-cmd.h | 1058 +++++++ - drivers/staging/fsl-dpaa2/ethernet/dpni.c | 1907 ++++++++++++ - drivers/staging/fsl-dpaa2/ethernet/dpni.h | 2581 +++++++++++++++++ - drivers/staging/fsl-dpaa2/mac/Kconfig | 24 + - drivers/staging/fsl-dpaa2/mac/Makefile | 10 + - drivers/staging/fsl-dpaa2/mac/dpmac-cmd.h | 195 ++ - drivers/staging/fsl-dpaa2/mac/dpmac.c | 422 +++ - drivers/staging/fsl-dpaa2/mac/dpmac.h | 593 ++++ - drivers/staging/fsl-dpaa2/mac/mac.c | 694 +++++ - drivers/staging/fsl-mc/Kconfig | 1 + - drivers/staging/fsl-mc/Makefile | 2 + - drivers/staging/fsl-mc/TODO | 13 + - drivers/staging/fsl-mc/bus/Kconfig | 45 + - drivers/staging/fsl-mc/bus/Makefile | 24 + - drivers/staging/fsl-mc/bus/dpbp.c | 459 +++ - drivers/staging/fsl-mc/bus/dpcon.c | 407 +++ - drivers/staging/fsl-mc/bus/dpio/Makefile | 9 + - drivers/staging/fsl-mc/bus/dpio/dpio-drv.c | 401 +++ - drivers/staging/fsl-mc/bus/dpio/dpio-drv.h | 33 + - drivers/staging/fsl-mc/bus/dpio/dpio.c | 468 +++ - drivers/staging/fsl-mc/bus/dpio/dpio_service.c | 801 ++++++ - drivers/staging/fsl-mc/bus/dpio/fsl_dpio.h | 460 +++ - drivers/staging/fsl-mc/bus/dpio/fsl_dpio_cmd.h | 184 ++ - drivers/staging/fsl-mc/bus/dpio/fsl_qbman_base.h | 123 + - drivers/staging/fsl-mc/bus/dpio/fsl_qbman_portal.h | 753 +++++ - drivers/staging/fsl-mc/bus/dpio/qbman_debug.c | 846 ++++++ - drivers/staging/fsl-mc/bus/dpio/qbman_debug.h | 136 + - drivers/staging/fsl-mc/bus/dpio/qbman_portal.c | 1212 ++++++++ - drivers/staging/fsl-mc/bus/dpio/qbman_portal.h | 261 ++ - drivers/staging/fsl-mc/bus/dpio/qbman_private.h | 173 ++ - drivers/staging/fsl-mc/bus/dpio/qbman_sys.h | 307 ++ - drivers/staging/fsl-mc/bus/dpio/qbman_sys_decl.h | 86 + - drivers/staging/fsl-mc/bus/dpio/qbman_test.c | 664 +++++ - drivers/staging/fsl-mc/bus/dpmcp-cmd.h | 56 + - drivers/staging/fsl-mc/bus/dpmcp.c | 318 ++ - drivers/staging/fsl-mc/bus/dpmcp.h | 323 +++ - drivers/staging/fsl-mc/bus/dpmng-cmd.h | 47 + - drivers/staging/fsl-mc/bus/dpmng.c | 85 + - drivers/staging/fsl-mc/bus/dprc-cmd.h | 87 + - drivers/staging/fsl-mc/bus/dprc-driver.c | 1084 +++++++ - drivers/staging/fsl-mc/bus/dprc.c | 1218 ++++++++ - drivers/staging/fsl-mc/bus/mc-allocator.c | 716 +++++ - drivers/staging/fsl-mc/bus/mc-bus.c | 1347 +++++++++ - drivers/staging/fsl-mc/bus/mc-ioctl.h | 25 + - drivers/staging/fsl-mc/bus/mc-restool.c | 312 ++ - drivers/staging/fsl-mc/bus/mc-sys.c | 677 +++++ - drivers/staging/fsl-mc/include/dpbp-cmd.h | 62 + - drivers/staging/fsl-mc/include/dpbp.h | 438 +++ - drivers/staging/fsl-mc/include/dpcon-cmd.h | 162 ++ - drivers/staging/fsl-mc/include/dpcon.h | 407 +++ - drivers/staging/fsl-mc/include/dpmac-cmd.h | 192 ++ - drivers/staging/fsl-mc/include/dpmac.h | 528 ++++ - drivers/staging/fsl-mc/include/dpmng.h | 80 + - drivers/staging/fsl-mc/include/dprc.h | 990 +++++++ - drivers/staging/fsl-mc/include/fsl_dpaa2_fd.h | 774 +++++ - drivers/staging/fsl-mc/include/fsl_dpaa2_io.h | 619 ++++ - drivers/staging/fsl-mc/include/mc-cmd.h | 133 + - drivers/staging/fsl-mc/include/mc-private.h | 168 ++ - drivers/staging/fsl-mc/include/mc-sys.h | 128 + - drivers/staging/fsl-mc/include/mc.h | 244 ++ - drivers/staging/fsl-mc/include/net.h | 481 ++++ - drivers/usb/core/config.c | 3 +- - drivers/usb/core/driver.c | 6 +- - drivers/usb/core/hcd-pci.c | 9 + - drivers/usb/core/hub.c | 66 +- - drivers/usb/core/quirks.c | 6 + - drivers/usb/dwc3/core.c | 76 +- - drivers/usb/dwc3/core.h | 8 + - drivers/usb/dwc3/host.c | 6 + - drivers/usb/host/xhci-pci.c | 114 +- - drivers/usb/host/xhci-ring.c | 6 +- - drivers/usb/host/xhci.c | 34 +- - drivers/usb/host/xhci.h | 3 + - drivers/vfio/Kconfig | 5 +- - drivers/vfio/Makefile | 1 + - drivers/vfio/fsl-mc/Kconfig | 9 + - drivers/vfio/fsl-mc/Makefile | 2 + - drivers/vfio/fsl-mc/vfio_fsl_mc.c | 603 ++++ - drivers/vfio/fsl-mc/vfio_fsl_mc_intr.c | 273 ++ - drivers/vfio/fsl-mc/vfio_fsl_mc_private.h | 43 + - drivers/vfio/pci/vfio_pci_intrs.c | 2 +- - drivers/vfio/vfio_iommu_type1.c | 5 +- - fs/Kconfig | 1 + - fs/Makefile | 1 + - fs/aufs/Kconfig | 185 ++ - fs/aufs/Makefile | 44 + - fs/aufs/aufs.h | 59 + - fs/aufs/branch.c | 1402 +++++++++ - fs/aufs/branch.h | 279 ++ - fs/aufs/conf.mk | 38 + - fs/aufs/cpup.c | 1368 +++++++++ - fs/aufs/cpup.h | 94 + - fs/aufs/dbgaufs.c | 432 +++ - fs/aufs/dbgaufs.h | 48 + - fs/aufs/dcsub.c | 224 ++ - fs/aufs/dcsub.h | 123 + - fs/aufs/debug.c | 436 +++ - fs/aufs/debug.h | 228 ++ - fs/aufs/dentry.c | 1129 ++++++++ - fs/aufs/dentry.h | 234 ++ - fs/aufs/dinfo.c | 544 ++++ - fs/aufs/dir.c | 756 +++++ - fs/aufs/dir.h | 131 + - fs/aufs/dynop.c | 379 +++ - fs/aufs/dynop.h | 76 + - fs/aufs/export.c | 831 ++++++ - fs/aufs/f_op.c | 781 +++++ - fs/aufs/fhsm.c | 426 +++ - fs/aufs/file.c | 857 ++++++ - fs/aufs/file.h | 291 ++ - fs/aufs/finfo.c | 156 + - fs/aufs/fstype.h | 400 +++ - fs/aufs/hfsnotify.c | 288 ++ - fs/aufs/hfsplus.c | 56 + - fs/aufs/hnotify.c | 714 +++++ - fs/aufs/i_op.c | 1460 ++++++++++ - fs/aufs/i_op_add.c | 930 ++++++ - fs/aufs/i_op_del.c | 506 ++++ - fs/aufs/i_op_ren.c | 1013 +++++++ - fs/aufs/iinfo.c | 277 ++ - fs/aufs/inode.c | 522 ++++ - fs/aufs/inode.h | 686 +++++ - fs/aufs/ioctl.c | 219 ++ - fs/aufs/loop.c | 146 + - fs/aufs/loop.h | 52 + - fs/aufs/magic.mk | 30 + - fs/aufs/module.c | 222 ++ - fs/aufs/module.h | 105 + - fs/aufs/mvdown.c | 703 +++++ - fs/aufs/opts.c | 1878 ++++++++++++ - fs/aufs/opts.h | 212 ++ - fs/aufs/plink.c | 506 ++++ - fs/aufs/poll.c | 52 + - fs/aufs/posix_acl.c | 98 + - fs/aufs/procfs.c | 169 ++ - fs/aufs/rdu.c | 388 +++ - fs/aufs/rwsem.h | 191 ++ - fs/aufs/sbinfo.c | 348 +++ - fs/aufs/spl.h | 111 + - fs/aufs/super.c | 1041 +++++++ - fs/aufs/super.h | 626 ++++ - fs/aufs/sysaufs.c | 104 + - fs/aufs/sysaufs.h | 101 + - fs/aufs/sysfs.c | 376 +++ - fs/aufs/sysrq.c | 157 + - fs/aufs/vdir.c | 888 ++++++ - fs/aufs/vfsub.c | 864 ++++++ - fs/aufs/vfsub.h | 315 ++ - fs/aufs/wbr_policy.c | 765 +++++ - fs/aufs/whout.c | 1061 +++++++ - fs/aufs/whout.h | 85 + - fs/aufs/wkq.c | 213 ++ - fs/aufs/wkq.h | 91 + - fs/aufs/xattr.c | 344 +++ - fs/aufs/xino.c | 1343 +++++++++ - fs/buffer.c | 2 +- - fs/dcache.c | 2 +- - fs/fcntl.c | 4 +- - fs/inode.c | 2 +- - fs/proc/base.c | 2 +- - fs/proc/nommu.c | 5 +- - fs/proc/task_mmu.c | 7 +- - fs/proc/task_nommu.c | 5 +- - fs/splice.c | 10 +- - include/asm-generic/msi.h | 32 + - include/asm-generic/vmlinux.lds.h | 2 + - include/linux/acpi.h | 6 +- - include/linux/device.h | 24 + - include/linux/dma-mapping.h | 13 +- - include/linux/file.h | 1 + - include/linux/fs.h | 3 + - include/linux/fsl/guts.h | 195 ++ - include/linux/fsl/svr.h | 95 + - include/linux/fsl_ifc.h | 116 +- - include/linux/interrupt.h | 14 + - include/linux/iommu.h | 76 +- - include/linux/iopoll.h | 144 + - include/linux/irq.h | 75 +- - include/linux/irqchip/arm-gic-v3.h | 165 ++ - include/linux/irqchip/arm-gic.h | 2 + - include/linux/irqdomain.h | 127 +- - include/linux/irqhandler.h | 14 + - include/linux/mm.h | 22 + - include/linux/mm_types.h | 2 + - include/linux/mmc/sdhci.h | 16 +- - include/linux/msi.h | 199 +- - include/linux/of.h | 11 +- - include/linux/of_device.h | 3 + - include/linux/of_iommu.h | 25 + - include/linux/of_irq.h | 1 + - include/linux/of_pci.h | 15 +- - include/linux/of_pdt.h | 3 +- - include/linux/of_platform.h | 6 + - include/linux/pci.h | 32 +- - include/linux/phy.h | 1 + - include/linux/phy_fixed.h | 11 +- - include/linux/resource_ext.h | 77 + - include/linux/splice.h | 6 + - include/linux/usb/quirks.h | 3 + - include/trace/events/iommu.h | 31 +- - include/uapi/linux/Kbuild | 1 + - include/uapi/linux/aufs_type.h | 419 +++ - include/uapi/linux/vfio.h | 5 + - kernel/fork.c | 2 +- - kernel/irq/Kconfig | 15 + - kernel/irq/Makefile | 1 + - kernel/irq/chip.c | 163 +- - kernel/irq/generic-chip.c | 36 +- - kernel/irq/irqdomain.c | 585 +++- - kernel/irq/manage.c | 93 + - kernel/irq/msi.c | 356 +++ - kernel/resource.c | 25 + - mm/Makefile | 2 +- - mm/filemap.c | 2 +- - mm/fremap.c | 16 +- - mm/memory.c | 2 +- - mm/mmap.c | 12 +- - mm/nommu.c | 10 +- - mm/prfile.c | 86 + - scripts/Kbuild.include | 6 + - scripts/Makefile.dtbinst | 51 + - scripts/Makefile.lib | 12 - - sound/soc/fsl/mpc8610_hpcd.c | 2 +- - sound/soc/fsl/p1022_ds.c | 2 +- - sound/soc/fsl/p1022_rdk.c | 2 +- - 467 files changed, 87181 insertions(+), 3457 deletions(-) - create mode 100644 arch/arm64/boot/dts/Makefile.rej - create mode 100644 arch/arm64/boot/dts/arm64-nxp-ls2080ardb-r0.dts - create mode 100644 arch/arm64/boot/dts/arm64-nxp-ls2088ardb-r1.dts - create mode 100644 arch/arm64/boot/dts/fsl-ls2080a.dtsi - create mode 100644 arch/arm64/boot/dts/fsl-ls2088a.dtsi - create mode 120000 arch/arm64/boot/dts/include/dt-bindings - create mode 100644 arch/arm64/boot/dts/thermal.h - create mode 100644 arch/arm64/configs/nxp_ls2088rdb_config - delete mode 100644 arch/powerpc/include/asm/mpc85xx.h - create mode 100644 drivers/clk/clk-qoriq.c - create mode 100644 drivers/iommu/io-pgtable-arm.c - create mode 100644 drivers/iommu/io-pgtable.c - create mode 100644 drivers/iommu/io-pgtable.h - create mode 100644 drivers/irqchip/irq-gic-v2m.c - create mode 100644 drivers/irqchip/irq-gic-v3-its.c - create mode 100644 drivers/net/phy/aquantia.c - delete mode 100644 drivers/net/phy/fixed.c - create mode 100644 drivers/net/phy/fixed_phy.c - create mode 100644 drivers/net/phy/fsl_10gkr.c - create mode 100644 drivers/net/phy/teranetics.c - create mode 100644 drivers/pci/host/pci-layerscape.c - create mode 100644 drivers/pci/host/pci-layerscape.h - create mode 100644 drivers/pci/host/pci-xgene-msi.c - create mode 100644 drivers/power/reset/ls-reboot.c - create mode 100644 drivers/soc/fsl/Kconfig - create mode 100644 drivers/soc/fsl/Kconfig.arm - create mode 100644 drivers/soc/fsl/Makefile - create mode 100644 drivers/soc/fsl/guts.c - create mode 100644 drivers/soc/fsl/ls1/Kconfig - create mode 100644 drivers/soc/fsl/ls1/Makefile - create mode 100644 drivers/soc/fsl/ls1/ftm_alarm.c - create mode 100644 drivers/staging/fsl-dpaa2/Kconfig - create mode 100644 drivers/staging/fsl-dpaa2/Makefile - create mode 100644 drivers/staging/fsl-dpaa2/ethernet/Kconfig - create mode 100644 drivers/staging/fsl-dpaa2/ethernet/Makefile - create mode 100644 drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-debugfs.c - create mode 100644 drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-debugfs.h - create mode 100644 drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-trace.h - create mode 100644 drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c - create mode 100644 drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.h - create mode 100644 drivers/staging/fsl-dpaa2/ethernet/dpaa2-ethtool.c - create mode 100644 drivers/staging/fsl-dpaa2/ethernet/dpkg.h - create mode 100644 drivers/staging/fsl-dpaa2/ethernet/dpni-cmd.h - create mode 100644 drivers/staging/fsl-dpaa2/ethernet/dpni.c - create mode 100644 drivers/staging/fsl-dpaa2/ethernet/dpni.h - create mode 100644 drivers/staging/fsl-dpaa2/mac/Kconfig - create mode 100644 drivers/staging/fsl-dpaa2/mac/Makefile - create mode 100644 drivers/staging/fsl-dpaa2/mac/dpmac-cmd.h - create mode 100644 drivers/staging/fsl-dpaa2/mac/dpmac.c - create mode 100644 drivers/staging/fsl-dpaa2/mac/dpmac.h - create mode 100644 drivers/staging/fsl-dpaa2/mac/mac.c - create mode 100644 drivers/staging/fsl-mc/Kconfig - create mode 100644 drivers/staging/fsl-mc/Makefile - create mode 100644 drivers/staging/fsl-mc/TODO - create mode 100644 drivers/staging/fsl-mc/bus/Kconfig - create mode 100644 drivers/staging/fsl-mc/bus/Makefile - create mode 100644 drivers/staging/fsl-mc/bus/dpbp.c - create mode 100644 drivers/staging/fsl-mc/bus/dpcon.c - create mode 100644 drivers/staging/fsl-mc/bus/dpio/Makefile - create mode 100644 drivers/staging/fsl-mc/bus/dpio/dpio-drv.c - create mode 100644 drivers/staging/fsl-mc/bus/dpio/dpio-drv.h - create mode 100644 drivers/staging/fsl-mc/bus/dpio/dpio.c - create mode 100644 drivers/staging/fsl-mc/bus/dpio/dpio_service.c - create mode 100644 drivers/staging/fsl-mc/bus/dpio/fsl_dpio.h - create mode 100644 drivers/staging/fsl-mc/bus/dpio/fsl_dpio_cmd.h - create mode 100644 drivers/staging/fsl-mc/bus/dpio/fsl_qbman_base.h - create mode 100644 drivers/staging/fsl-mc/bus/dpio/fsl_qbman_portal.h - create mode 100644 drivers/staging/fsl-mc/bus/dpio/qbman_debug.c - create mode 100644 drivers/staging/fsl-mc/bus/dpio/qbman_debug.h - create mode 100644 drivers/staging/fsl-mc/bus/dpio/qbman_portal.c - create mode 100644 drivers/staging/fsl-mc/bus/dpio/qbman_portal.h - create mode 100644 drivers/staging/fsl-mc/bus/dpio/qbman_private.h - create mode 100644 drivers/staging/fsl-mc/bus/dpio/qbman_sys.h - create mode 100644 drivers/staging/fsl-mc/bus/dpio/qbman_sys_decl.h - create mode 100644 drivers/staging/fsl-mc/bus/dpio/qbman_test.c - create mode 100644 drivers/staging/fsl-mc/bus/dpmcp-cmd.h - create mode 100644 drivers/staging/fsl-mc/bus/dpmcp.c - create mode 100644 drivers/staging/fsl-mc/bus/dpmcp.h - create mode 100644 drivers/staging/fsl-mc/bus/dpmng-cmd.h - create mode 100644 drivers/staging/fsl-mc/bus/dpmng.c - create mode 100644 drivers/staging/fsl-mc/bus/dprc-cmd.h - create mode 100644 drivers/staging/fsl-mc/bus/dprc-driver.c - create mode 100644 drivers/staging/fsl-mc/bus/dprc.c - create mode 100644 drivers/staging/fsl-mc/bus/mc-allocator.c - create mode 100644 drivers/staging/fsl-mc/bus/mc-bus.c - create mode 100644 drivers/staging/fsl-mc/bus/mc-ioctl.h - create mode 100644 drivers/staging/fsl-mc/bus/mc-restool.c - create mode 100644 drivers/staging/fsl-mc/bus/mc-sys.c - create mode 100644 drivers/staging/fsl-mc/include/dpbp-cmd.h - create mode 100644 drivers/staging/fsl-mc/include/dpbp.h - create mode 100644 drivers/staging/fsl-mc/include/dpcon-cmd.h - create mode 100644 drivers/staging/fsl-mc/include/dpcon.h - create mode 100644 drivers/staging/fsl-mc/include/dpmac-cmd.h - create mode 100644 drivers/staging/fsl-mc/include/dpmac.h - create mode 100644 drivers/staging/fsl-mc/include/dpmng.h - create mode 100644 drivers/staging/fsl-mc/include/dprc.h - create mode 100644 drivers/staging/fsl-mc/include/fsl_dpaa2_fd.h - create mode 100644 drivers/staging/fsl-mc/include/fsl_dpaa2_io.h - create mode 100644 drivers/staging/fsl-mc/include/mc-cmd.h - create mode 100644 drivers/staging/fsl-mc/include/mc-private.h - create mode 100644 drivers/staging/fsl-mc/include/mc-sys.h - create mode 100644 drivers/staging/fsl-mc/include/mc.h - create mode 100644 drivers/staging/fsl-mc/include/net.h - create mode 100644 drivers/vfio/fsl-mc/Kconfig - create mode 100644 drivers/vfio/fsl-mc/Makefile - create mode 100644 drivers/vfio/fsl-mc/vfio_fsl_mc.c - create mode 100644 drivers/vfio/fsl-mc/vfio_fsl_mc_intr.c - create mode 100644 drivers/vfio/fsl-mc/vfio_fsl_mc_private.h - create mode 100644 fs/aufs/Kconfig - create mode 100644 fs/aufs/Makefile - create mode 100644 fs/aufs/aufs.h - create mode 100644 fs/aufs/branch.c - create mode 100644 fs/aufs/branch.h - create mode 100644 fs/aufs/conf.mk - create mode 100644 fs/aufs/cpup.c - create mode 100644 fs/aufs/cpup.h - create mode 100644 fs/aufs/dbgaufs.c - create mode 100644 fs/aufs/dbgaufs.h - create mode 100644 fs/aufs/dcsub.c - create mode 100644 fs/aufs/dcsub.h - create mode 100644 fs/aufs/debug.c - create mode 100644 fs/aufs/debug.h - create mode 100644 fs/aufs/dentry.c - create mode 100644 fs/aufs/dentry.h - create mode 100644 fs/aufs/dinfo.c - create mode 100644 fs/aufs/dir.c - create mode 100644 fs/aufs/dir.h - create mode 100644 fs/aufs/dynop.c - create mode 100644 fs/aufs/dynop.h - create mode 100644 fs/aufs/export.c - create mode 100644 fs/aufs/f_op.c - create mode 100644 fs/aufs/fhsm.c - create mode 100644 fs/aufs/file.c - create mode 100644 fs/aufs/file.h - create mode 100644 fs/aufs/finfo.c - create mode 100644 fs/aufs/fstype.h - create mode 100644 fs/aufs/hfsnotify.c - create mode 100644 fs/aufs/hfsplus.c - create mode 100644 fs/aufs/hnotify.c - create mode 100644 fs/aufs/i_op.c - create mode 100644 fs/aufs/i_op_add.c - create mode 100644 fs/aufs/i_op_del.c - create mode 100644 fs/aufs/i_op_ren.c - create mode 100644 fs/aufs/iinfo.c - create mode 100644 fs/aufs/inode.c - create mode 100644 fs/aufs/inode.h - create mode 100644 fs/aufs/ioctl.c - create mode 100644 fs/aufs/loop.c - create mode 100644 fs/aufs/loop.h - create mode 100644 fs/aufs/magic.mk - create mode 100644 fs/aufs/module.c - create mode 100644 fs/aufs/module.h - create mode 100644 fs/aufs/mvdown.c - create mode 100644 fs/aufs/opts.c - create mode 100644 fs/aufs/opts.h - create mode 100644 fs/aufs/plink.c - create mode 100644 fs/aufs/poll.c - create mode 100644 fs/aufs/posix_acl.c - create mode 100644 fs/aufs/procfs.c - create mode 100644 fs/aufs/rdu.c - create mode 100644 fs/aufs/rwsem.h - create mode 100644 fs/aufs/sbinfo.c - create mode 100644 fs/aufs/spl.h - create mode 100644 fs/aufs/super.c - create mode 100644 fs/aufs/super.h - create mode 100644 fs/aufs/sysaufs.c - create mode 100644 fs/aufs/sysaufs.h - create mode 100644 fs/aufs/sysfs.c - create mode 100644 fs/aufs/sysrq.c - create mode 100644 fs/aufs/vdir.c - create mode 100644 fs/aufs/vfsub.c - create mode 100644 fs/aufs/vfsub.h - create mode 100644 fs/aufs/wbr_policy.c - create mode 100644 fs/aufs/whout.c - create mode 100644 fs/aufs/whout.h - create mode 100644 fs/aufs/wkq.c - create mode 100644 fs/aufs/wkq.h - create mode 100644 fs/aufs/xattr.c - create mode 100644 fs/aufs/xino.c - create mode 100644 include/asm-generic/msi.h - create mode 100644 include/linux/fsl/guts.h - create mode 100644 include/linux/fsl/svr.h - create mode 100644 include/linux/iopoll.h - create mode 100644 include/linux/irqhandler.h - create mode 100644 include/linux/resource_ext.h - create mode 100644 include/uapi/linux/aufs_type.h - create mode 100644 kernel/irq/msi.c - create mode 100644 mm/prfile.c - create mode 100644 scripts/Makefile.dtbinst - -diff --git a/Documentation/IRQ-domain.txt b/Documentation/IRQ-domain.txt -index 8a8b82c..39cfa72 100644 ---- a/Documentation/IRQ-domain.txt -+++ b/Documentation/IRQ-domain.txt -@@ -151,3 +151,74 @@ used and no descriptor gets allocated it is very important to make sure - that the driver using the simple domain call irq_create_mapping() - before any irq_find_mapping() since the latter will actually work - for the static IRQ assignment case. -+ -+==== Hierarchy IRQ domain ==== -+On some architectures, there may be multiple interrupt controllers -+involved in delivering an interrupt from the device to the target CPU. -+Let's look at a typical interrupt delivering path on x86 platforms: -+ -+Device --> IOAPIC -> Interrupt remapping Controller -> Local APIC -> CPU -+ -+There are three interrupt controllers involved: -+1) IOAPIC controller -+2) Interrupt remapping controller -+3) Local APIC controller -+ -+To support such a hardware topology and make software architecture match -+hardware architecture, an irq_domain data structure is built for each -+interrupt controller and those irq_domains are organized into hierarchy. -+When building irq_domain hierarchy, the irq_domain near to the device is -+child and the irq_domain near to CPU is parent. So a hierarchy structure -+as below will be built for the example above. -+ CPU Vector irq_domain (root irq_domain to manage CPU vectors) -+ ^ -+ | -+ Interrupt Remapping irq_domain (manage irq_remapping entries) -+ ^ -+ | -+ IOAPIC irq_domain (manage IOAPIC delivery entries/pins) -+ -+There are four major interfaces to use hierarchy irq_domain: -+1) irq_domain_alloc_irqs(): allocate IRQ descriptors and interrupt -+ controller related resources to deliver these interrupts. -+2) irq_domain_free_irqs(): free IRQ descriptors and interrupt controller -+ related resources associated with these interrupts. -+3) irq_domain_activate_irq(): activate interrupt controller hardware to -+ deliver the interrupt. -+3) irq_domain_deactivate_irq(): deactivate interrupt controller hardware -+ to stop delivering the interrupt. -+ -+Following changes are needed to support hierarchy irq_domain. -+1) a new field 'parent' is added to struct irq_domain; it's used to -+ maintain irq_domain hierarchy information. -+2) a new field 'parent_data' is added to struct irq_data; it's used to -+ build hierarchy irq_data to match hierarchy irq_domains. The irq_data -+ is used to store irq_domain pointer and hardware irq number. -+3) new callbacks are added to struct irq_domain_ops to support hierarchy -+ irq_domain operations. -+ -+With support of hierarchy irq_domain and hierarchy irq_data ready, an -+irq_domain structure is built for each interrupt controller, and an -+irq_data structure is allocated for each irq_domain associated with an -+IRQ. Now we could go one step further to support stacked(hierarchy) -+irq_chip. That is, an irq_chip is associated with each irq_data along -+the hierarchy. A child irq_chip may implement a required action by -+itself or by cooperating with its parent irq_chip. -+ -+With stacked irq_chip, interrupt controller driver only needs to deal -+with the hardware managed by itself and may ask for services from its -+parent irq_chip when needed. So we could achieve a much cleaner -+software architecture. -+ -+For an interrupt controller driver to support hierarchy irq_domain, it -+needs to: -+1) Implement irq_domain_ops.alloc and irq_domain_ops.free -+2) Optionally implement irq_domain_ops.activate and -+ irq_domain_ops.deactivate. -+3) Optionally implement an irq_chip to manage the interrupt controller -+ hardware. -+4) No need to implement irq_domain_ops.map and irq_domain_ops.unmap, -+ they are unused with hierarchy irq_domain. -+ -+Hierarchy irq_domain may also be used to support other architectures, -+such as ARM, ARM64 etc. -diff --git a/Documentation/devicetree/bindings/arm/fsl.txt b/Documentation/devicetree/bindings/arm/fsl.txt -index e935d7d..5c9f338 100644 ---- a/Documentation/devicetree/bindings/arm/fsl.txt -+++ b/Documentation/devicetree/bindings/arm/fsl.txt -@@ -74,3 +74,18 @@ Required root node properties: - i.MX6q generic board - Required root node properties: - - compatible = "fsl,imx6q"; -+ -++Freescale ARMv8 based Layerscape SoC family Device Tree Bindings -++---------------------------------------------------------------- -+ -+LS2080A ARMv8 based Simulator model -+Required root node properties: -+ - compatible = "fsl,ls2080a-simu", "fsl,ls2080a"; -+ -+LS2080A ARMv8 based QDS Board -+Required root node properties: -+ - compatible = "fsl,ls2080a-qds", "fsl,ls2080a"; -+ -+LS2080A ARMv8 based RDB Board -+Required root node properties: -+ - compatible = "fsl,ls2080a-rdb", "fsl,ls2080a"; -diff --git a/Documentation/devicetree/bindings/arm/gic.txt b/Documentation/devicetree/bindings/arm/gic.txt -index c7d2fa1..e87d3d7 100644 ---- a/Documentation/devicetree/bindings/arm/gic.txt -+++ b/Documentation/devicetree/bindings/arm/gic.txt -@@ -31,12 +31,16 @@ Main node required properties: - The 3rd cell is the flags, encoded as follows: - bits[3:0] trigger type and level flags. - 1 = low-to-high edge triggered -- 2 = high-to-low edge triggered -+ 2 = high-to-low edge triggered (invalid for SPIs) - 4 = active high level-sensitive -- 8 = active low level-sensitive -+ 8 = active low level-sensitive (invalid for SPIs). - bits[15:8] PPI interrupt cpu mask. Each bit corresponds to each of - the 8 possible cpus attached to the GIC. A bit set to '1' indicated - the interrupt is wired to that CPU. Only valid for PPI interrupts. -+ Also note that the configurability of PPI interrupts is IMPLEMENTATION -+ DEFINED and as such not guaranteed to be present (most SoC available -+ in 2014 seem to ignore the setting of this flag and use the hardware -+ default value). - - - reg : Specifies base physical address(s) and size of the GIC registers. The - first region is the GIC distributor register base and size. The 2nd region is -diff --git a/Documentation/devicetree/bindings/clock/qoriq-clock.txt b/Documentation/devicetree/bindings/clock/qoriq-clock.txt -index 5666812..128fc72 100644 ---- a/Documentation/devicetree/bindings/clock/qoriq-clock.txt -+++ b/Documentation/devicetree/bindings/clock/qoriq-clock.txt -@@ -1,6 +1,6 @@ --* Clock Block on Freescale CoreNet Platforms -+* Clock Block on Freescale QorIQ Platforms - --Freescale CoreNet chips take primary clocking input from the external -+Freescale QorIQ chips take primary clocking input from the external - SYSCLK signal. The SYSCLK input (frequency) is multiplied using - multiple phase locked loops (PLL) to create a variety of frequencies - which can then be passed to a variety of internal logic, including -@@ -13,14 +13,16 @@ which the chip complies. - Chassis Version Example Chips - --------------- ------------- - 1.0 p4080, p5020, p5040 --2.0 t4240, b4860, t1040 -+2.0 t4240, b4860 - - 1. Clock Block Binding - - Required properties: --- compatible: Should contain a specific clock block compatible string -- and a single chassis clock compatible string. -- Clock block strings include, but not limited to, one of the: -+- compatible: Should contain a chip-specific clock block compatible -+ string and (if applicable) may contain a chassis-version clock -+ compatible string. -+ -+ Chip-specific strings are of the form "fsl,-clockgen", such as: - * "fsl,p2041-clockgen" - * "fsl,p3041-clockgen" - * "fsl,p4080-clockgen" -@@ -29,15 +31,15 @@ Required properties: - * "fsl,t4240-clockgen" - * "fsl,b4420-clockgen" - * "fsl,b4860-clockgen" -- Chassis clock strings include: -+ * "fsl,ls1021a-clockgen" -+ Chassis-version clock strings include: - * "fsl,qoriq-clockgen-1.0": for chassis 1.0 clocks - * "fsl,qoriq-clockgen-2.0": for chassis 2.0 clocks - - reg: Describes the address of the device's resources within the - address space defined by its parent bus, and resource zero - represents the clock register set --- clock-frequency: Input system clock frequency - --Recommended properties: -+Optional properties: - - ranges: Allows valid translation between child's address space and - parent's. Must be present if the device has sub-nodes. - - #address-cells: Specifies the number of cells used to represent -@@ -46,8 +48,46 @@ Recommended properties: - - #size-cells: Specifies the number of cells used to represent - the size of an address. Must be present if the device has - sub-nodes and set to 1 if present -+- clock-frequency: Input system clock frequency (SYSCLK) -+- clocks: If clock-frequency is not specified, sysclk may be provided -+ as an input clock. Either clock-frequency or clocks must be -+ provided. -+ -+2. Clock Provider -+ -+The clockgen node should act as a clock provider, though in older device -+trees the children of the clockgen node are the clock providers. -+ -+When the clockgen node is a clock provider, #clock-cells = <2>. -+The first cell of the clock specifier is the clock type, and the -+second cell is the clock index for the specified type. -+ -+ Type# Name Index Cell -+ 0 sysclk must be 0 -+ 1 cmux index (n in CLKCnCSR) -+ 2 hwaccel index (n in CLKCGnHWACSR) -+ 3 fman 0 for fm1, 1 for fm2 -+ 4 platform pll 0=pll, 1=pll/2, 2=pll/3, 3=pll/4 -+ -+3. Example -+ -+ clockgen: global-utilities@e1000 { -+ compatible = "fsl,p5020-clockgen", "fsl,qoriq-clockgen-1.0"; -+ clock-frequency = <133333333>; -+ reg = <0xe1000 0x1000>; -+ #clock-cells = <2>; -+ }; -+ -+ fman@400000 { -+ ... -+ clocks = <&clockgen 3 0>; -+ ... -+ }; -+} -+4. Legacy Child Nodes - --2. Clock Provider/Consumer Binding -+NOTE: These nodes are deprecated. Kernels should continue to support -+device trees with these nodes, but new device trees should not use them. - - Most of the bindings are from the common clock binding[1]. - [1] Documentation/devicetree/bindings/clock/clock-bindings.txt -@@ -79,7 +119,7 @@ Recommended properties: - - reg: Should be the offset and length of clock block base address. - The length should be 4. - --Example for clock block and clock provider: -+Legacy Example: - / { - clockgen: global-utilities@e1000 { - compatible = "fsl,p5020-clockgen", "fsl,qoriq-clockgen-1.0"; -@@ -131,7 +171,7 @@ Example for clock block and clock provider: - }; - } - --Example for clock consumer: -+Example for legacy clock consumer: - - / { - cpu0: PowerPC,e5500@0 { -diff --git a/Documentation/devicetree/bindings/i2c/i2c-imx.txt b/Documentation/devicetree/bindings/i2c/i2c-imx.txt -index 4a8513e..52d37fd 100644 ---- a/Documentation/devicetree/bindings/i2c/i2c-imx.txt -+++ b/Documentation/devicetree/bindings/i2c/i2c-imx.txt -@@ -11,6 +11,8 @@ Required properties: - Optional properties: - - clock-frequency : Constains desired I2C/HS-I2C bus clock frequency in Hz. - The absence of the propoerty indicates the default frequency 100 kHz. -+- dmas: A list of two dma specifiers, one for each entry in dma-names. -+- dma-names: should contain "tx" and "rx". - - Examples: - -@@ -26,3 +28,12 @@ i2c@70038000 { /* HS-I2C on i.MX51 */ - interrupts = <64>; - clock-frequency = <400000>; - }; -+ -+i2c0: i2c@40066000 { /* i2c0 on vf610 */ -+ compatible = "fsl,vf610-i2c"; -+ reg = <0x40066000 0x1000>; -+ interrupts =<0 71 0x04>; -+ dmas = <&edma0 0 50>, -+ <&edma0 0 51>; -+ dma-names = "rx","tx"; -+}; -diff --git a/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.txt b/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.txt -index 34a3fb6..cf53d5f 100644 ---- a/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.txt -+++ b/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.txt -@@ -16,6 +16,9 @@ Required Properties: - Optional Properties: - - - reset-gpios: Reference to the GPIO connected to the reset input. -+ - i2c-mux-idle-disconnect: Boolean; if defined, forces mux to disconnect all -+ children in idle state. This is necessary for example, if there are several -+ multiplexers on the bus and the devices behind them use same I2C addresses. - - - Example: -diff --git a/Documentation/devicetree/bindings/memory-controllers/fsl/ifc.txt b/Documentation/devicetree/bindings/memory-controllers/fsl/ifc.txt -index d5e3704..89427b0 100644 ---- a/Documentation/devicetree/bindings/memory-controllers/fsl/ifc.txt -+++ b/Documentation/devicetree/bindings/memory-controllers/fsl/ifc.txt -@@ -18,6 +18,8 @@ Properties: - interrupt (NAND_EVTER_STAT). If there is only one, - that interrupt reports both types of event. - -+- little-endian : If this property is absent, the big-endian mode will -+ be in use as default for registers. - - - ranges : Each range corresponds to a single chipselect, and covers - the entire access window as configured. -@@ -34,6 +36,7 @@ Example: - #size-cells = <1>; - reg = <0x0 0xffe1e000 0 0x2000>; - interrupts = <16 2 19 2>; -+ little-endian; - - /* NOR, NAND Flashes and CPLD on board */ - ranges = <0x0 0x0 0x0 0xee000000 0x02000000 -diff --git a/Documentation/devicetree/bindings/pci/designware-pcie.txt b/Documentation/devicetree/bindings/pci/designware-pcie.txt -index 9f4faa8..0036ab3 100644 ---- a/Documentation/devicetree/bindings/pci/designware-pcie.txt -+++ b/Documentation/devicetree/bindings/pci/designware-pcie.txt -@@ -14,7 +14,6 @@ Required properties: - - interrupt-map-mask and interrupt-map: standard PCI properties - to define the mapping of the PCIe interface to interrupt - numbers. --- num-lanes: number of lanes to use - - clocks: Must contain an entry for each entry in clock-names. - See ../clocks/clock-bindings.txt for details. - - clock-names: Must include the following entries: -@@ -22,6 +21,8 @@ Required properties: - - "pcie_bus" - - Optional properties: -+- num-lanes: number of lanes to use (this property should be specified unless -+ the link is brought already up in BIOS) - - reset-gpio: gpio pin number of power good signal - - bus-range: PCI bus numbers covered (it is recommended for new devicetrees to - specify this property, to keep backwards compatibility a range of 0x00-0xff -diff --git a/Documentation/devicetree/bindings/powerpc/fsl/board.txt b/Documentation/devicetree/bindings/powerpc/fsl/board.txt -index cff38bd..89c90f4 100644 ---- a/Documentation/devicetree/bindings/powerpc/fsl/board.txt -+++ b/Documentation/devicetree/bindings/powerpc/fsl/board.txt -@@ -21,11 +21,14 @@ Example: - - This is the memory-mapped registers for on board FPGA. - --Required properities: -+Required properties: - - compatible: should be a board-specific string followed by a string - indicating the type of FPGA. Example: -- "fsl,-fpga", "fsl,fpga-pixis" -+ "fsl,-fpga", "fsl,fpga-pixis" or -+ "fsl,-fpga", "fsl,fpga-qixis" - - reg: should contain the address and the length of the FPGA register set. -+ -+Optional properties: - - interrupt-parent: should specify phandle for the interrupt controller. - - interrupts: should specify event (wakeup) IRQ. - -@@ -38,6 +41,13 @@ Example (P1022DS): - interrupts = <8 8 0 0>; - }; - -+Example (LS2080A-RDB): -+ -+ cpld@3,0 { -+ compatible = "fsl,ls2080ardb-fpga", "fsl,fpga-qixis"; -+ reg = <0x3 0 0x10000>; -+ }; -+ - * Freescale BCSR GPIO banks - - Some BCSR registers act as simple GPIO controllers, each such -diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt -index 471366d..1f9900c 100644 ---- a/Documentation/devicetree/bindings/usb/dwc3.txt -+++ b/Documentation/devicetree/bindings/usb/dwc3.txt -@@ -1,6 +1,7 @@ - synopsys DWC3 CORE - --DWC3- USB3 CONTROLLER -+DWC3- USB3 CONTROLLER. Complies to the generic USB binding properties -+ as described in 'usb/generic.txt' - - Required properties: - - compatible: must be "snps,dwc3" -diff --git a/Documentation/devicetree/of_selftest.txt b/Documentation/devicetree/of_selftest.txt -index 1e3d5c9..57a808b 100644 ---- a/Documentation/devicetree/of_selftest.txt -+++ b/Documentation/devicetree/of_selftest.txt -@@ -63,7 +63,6 @@ struct device_node { - struct device_node *parent; - struct device_node *child; - struct device_node *sibling; -- struct device_node *allnext; /* next in list of all nodes */ - ... - }; - -@@ -99,12 +98,6 @@ child11 -> sibling12 -> sibling13 -> sibling14 -> null - Figure 1: Generic structure of un-flattened device tree - - --*allnext: it is used to link all the nodes of DT into a list. So, for the -- above tree the list would be as follows: -- --root->child1->child11->sibling12->sibling13->child131->sibling14->sibling2-> --child21->sibling22->sibling23->sibling3->child31->sibling32->sibling4->null -- - Before executing OF selftest, it is required to attach the test data to - machine's device tree (if present). So, when selftest_data_add() is called, - at first it reads the flattened device tree data linked into the kernel image -@@ -131,11 +124,6 @@ root ('/') - test-child01 null null null - - --allnext list: -- --root->testcase-data->test-child0->test-child01->test-sibling1->test-sibling2 --->test-sibling3->null -- - Figure 2: Example test data tree to be attached to live tree. - - According to the scenario above, the live tree is already present so it isn't -@@ -204,8 +192,6 @@ detached and then moving up the parent nodes are removed, and eventually the - whole tree). selftest_data_remove() calls detach_node_and_children() that uses - of_detach_node() to detach the nodes from the live device tree. - --To detach a node, of_detach_node() first updates all_next linked list, by --attaching the previous node's allnext to current node's allnext pointer. And --then, it either updates the child pointer of given node's parent to its --sibling or attaches the previous sibling to the given node's sibling, as --appropriate. That is it :) -+To detach a node, of_detach_node() either updates the child pointer of given -+node's parent to its sibling or attaches the previous sibling to the given -+node's sibling, as appropriate. That is it :) -diff --git a/Documentation/devicetree/todo.txt b/Documentation/devicetree/todo.txt -index c3cf065..b5139d1 100644 ---- a/Documentation/devicetree/todo.txt -+++ b/Documentation/devicetree/todo.txt -@@ -2,7 +2,6 @@ Todo list for devicetree: - - === General structure === - - Switch from custom lists to (h)list_head for nodes and properties structure --- Remove of_allnodes list and iterate using list of child nodes alone - - === CONFIG_OF_DYNAMIC === - - Switch to RCU for tree updates and get rid of global spinlock -diff --git a/MAINTAINERS b/MAINTAINERS -index c721042..cb2296a 100644 ---- a/MAINTAINERS -+++ b/MAINTAINERS -@@ -1562,6 +1562,7 @@ M: Will Deacon - L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) - S: Maintained - F: drivers/iommu/arm-smmu.c -+F: drivers/iommu/io-pgtable-arm.c - - ARM64 PORT (AARCH64 ARCHITECTURE) - M: Catalin Marinas -@@ -1795,6 +1796,20 @@ F: include/linux/audit.h - F: include/uapi/linux/audit.h - F: kernel/audit* - -+AUFS (advanced multi layered unification filesystem) FILESYSTEM -+M: "J. R. Okajima" -+L: linux-unionfs@vger.kernel.org -+L: aufs-users@lists.sourceforge.net (members only) -+W: http://aufs.sourceforge.net -+T: git://git.code.sf.net/p/aufs/aufs3-linux -+T: git://github.com/sfjro/aufs3-linux.git -+S: Supported -+F: Documentation/filesystems/aufs/ -+F: Documentation/ABI/testing/debugfs-aufs -+F: Documentation/ABI/testing/sysfs-aufs -+F: fs/aufs/ -+F: include/uapi/linux/aufs_type.h -+ - AUXILIARY DISPLAY DRIVERS - M: Miguel Ojeda Sandonis - W: http://miguelojeda.es/auxdisplay.htm -@@ -3972,6 +3987,33 @@ F: sound/soc/fsl/fsl* - F: sound/soc/fsl/imx* - F: sound/soc/fsl/mpc8610_hpcd.c - -+FREESCALE QORIQ MANAGEMENT COMPLEX DRIVER -+M: J. German Rivera -+L: linux-kernel@vger.kernel.org -+S: Maintained -+F: drivers/staging/fsl-mc/ -+ -+FREESCALE DPAA2 ETH DRIVER -+M: Ioana Radulescu -+M: Bogdan Hamciuc -+M: Cristian Sovaiala -+L: linux-kernel@vger.kernel.org -+S: Maintained -+F: drivers/staging/fsl-dpaa2/ethernet/ -+ -+FREESCALE QORIQ MANAGEMENT COMPLEX RESTOOL DRIVER -+M: Lijun Pan -+L: linux-kernel@vger.kernel.org -+S: Maintained -+F: drivers/staging/fsl-mc/bus/mc-ioctl.h -+F: drivers/staging/fsl-mc/bus/mc-restool.c -+ -+FREESCALE DPAA2 MAC/PHY INTERFACE DRIVER -+M: Alex Marginean -+L: linux-kernel@vger.kernel.org -+S: Maintained -+F: drivers/staging/fsl-dpaa2/mac/ -+ - FREEVXFS FILESYSTEM - M: Christoph Hellwig - W: ftp://ftp.openlinux.org/pub/people/hch/vxfs -@@ -7047,6 +7089,16 @@ S: Maintained - F: Documentation/devicetree/bindings/pci/xgene-pci.txt - F: drivers/pci/host/pci-xgene.c - -+PCI DRIVER FOR FREESCALE LAYERSCAPE -+M: Minghuan Lian -+M: Mingkai Hu -+M: Roy Zang -+L: linuxppc-dev@lists.ozlabs.org -+L: linux-pci@vger.kernel.org -+L: linux-arm-kernel@lists.infradead.org -+S: Maintained -+F: drivers/pci/host/*layerscape* -+ - PCI DRIVER FOR IMX6 - M: Richard Zhu - M: Lucas Stach -@@ -7122,6 +7174,14 @@ L: linux-pci@vger.kernel.org - S: Maintained - F: drivers/pci/host/*spear* - -+PCI MSI DRIVER FOR APPLIEDMICRO XGENE -+M: Duc Dang -+L: linux-pci@vger.kernel.org -+L: linux-arm-kernel@lists.infradead.org -+S: Maintained -+F: Documentation/devicetree/bindings/pci/xgene-pci-msi.txt -+F: drivers/pci/host/pci-xgene-msi.c -+ - PCMCIA SUBSYSTEM - P: Linux PCMCIA Team - L: linux-pcmcia@lists.infradead.org -diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig -index 89c4b5c..29544f0 100644 ---- a/arch/arm/Kconfig -+++ b/arch/arm/Kconfig -@@ -1292,6 +1292,9 @@ config PCI_DOMAINS - bool - depends on PCI - -+config PCI_DOMAINS_GENERIC -+ def_bool PCI_DOMAINS -+ - config PCI_NANOENGINE - bool "BSE nanoEngine PCI support" - depends on SA1100_NANOENGINE -diff --git a/arch/arm/Makefile b/arch/arm/Makefile -index b5d7988..93a30a2 100644 ---- a/arch/arm/Makefile -+++ b/arch/arm/Makefile -@@ -320,8 +320,12 @@ $(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) - - # We use MRPROPER_FILES and CLEAN_FILES now - archclean: -diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index 38c89ca..6e784fa 100644 ---- a/arch/arm/boot/dts/Makefile -+++ b/arch/arm/boot/dts/Makefile -@@ -517,15 +517,7 @@ 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) - 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 -- --dtbs_install: $(addsuffix _dtbinst_, $(dtb-y)) -+always := $(dtb-y) -+clean-files := *.dtb -diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h -index 85738b2..f3c0d95 100644 ---- a/arch/arm/include/asm/dma-mapping.h -+++ b/arch/arm/include/asm/dma-mapping.h -@@ -121,12 +121,14 @@ static inline unsigned long dma_max_pfn(struct device *dev) - } - #define dma_max_pfn(dev) dma_max_pfn(dev) - --static inline int set_arch_dma_coherent_ops(struct device *dev) -+static inline void arch_setup_dma_ops(struct device *dev, u64 dma_base, -+ u64 size, struct iommu_ops *iommu, -+ bool coherent) - { -- set_dma_ops(dev, &arm_coherent_dma_ops); -- return 0; -+ if (coherent) -+ set_dma_ops(dev, &arm_coherent_dma_ops); - } --#define set_arch_dma_coherent_ops(dev) set_arch_dma_coherent_ops(dev) -+#define arch_setup_dma_ops arch_setup_dma_ops - - static inline dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr) - { -diff --git a/arch/arm/include/asm/mach/pci.h b/arch/arm/include/asm/mach/pci.h -index 7fc4278..c074e7a 100644 ---- a/arch/arm/include/asm/mach/pci.h -+++ b/arch/arm/include/asm/mach/pci.h -@@ -19,9 +19,7 @@ struct pci_bus; - struct device; - - struct hw_pci { --#ifdef CONFIG_PCI_DOMAINS -- int domain; --#endif -+ struct msi_controller *msi_ctrl; - struct pci_ops *ops; - int nr_controllers; - void **private_data; -@@ -36,16 +34,14 @@ struct hw_pci { - resource_size_t start, - resource_size_t size, - resource_size_t align); -- void (*add_bus)(struct pci_bus *bus); -- void (*remove_bus)(struct pci_bus *bus); - }; - - /* - * Per-controller structure - */ - struct pci_sys_data { --#ifdef CONFIG_PCI_DOMAINS -- int domain; -+#ifdef CONFIG_PCI_MSI -+ struct msi_controller *msi_ctrl; - #endif - struct list_head node; - int busnr; /* primary bus number */ -@@ -65,8 +61,6 @@ struct pci_sys_data { - resource_size_t start, - resource_size_t size, - resource_size_t align); -- void (*add_bus)(struct pci_bus *bus); -- void (*remove_bus)(struct pci_bus *bus); - void *private_data; /* platform controller private data */ - }; - -diff --git a/arch/arm/include/asm/pci.h b/arch/arm/include/asm/pci.h -index 7e95d85..585dc33 100644 ---- a/arch/arm/include/asm/pci.h -+++ b/arch/arm/include/asm/pci.h -@@ -18,13 +18,6 @@ static inline int pcibios_assign_all_busses(void) - } - - #ifdef CONFIG_PCI_DOMAINS --static inline int pci_domain_nr(struct pci_bus *bus) --{ -- struct pci_sys_data *root = bus->sysdata; -- -- return root->domain; --} -- - static inline int pci_proc_domain(struct pci_bus *bus) - { - return pci_domain_nr(bus); -diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c -index 17a26c1..a5cd259 100644 ---- a/arch/arm/kernel/bios32.c -+++ b/arch/arm/kernel/bios32.c -@@ -18,6 +18,15 @@ - - static int debug_pci; - -+#ifdef CONFIG_PCI_MSI -+struct msi_controller *pcibios_msi_controller(struct pci_dev *dev) -+{ -+ struct pci_sys_data *sysdata = dev->bus->sysdata; -+ -+ return sysdata->msi_ctrl; -+} -+#endif -+ - /* - * We can't use pci_get_device() here since we are - * called from interrupt context. -@@ -360,20 +369,6 @@ void pcibios_fixup_bus(struct pci_bus *bus) - } - EXPORT_SYMBOL(pcibios_fixup_bus); - --void pcibios_add_bus(struct pci_bus *bus) --{ -- struct pci_sys_data *sys = bus->sysdata; -- if (sys->add_bus) -- sys->add_bus(bus); --} -- --void pcibios_remove_bus(struct pci_bus *bus) --{ -- struct pci_sys_data *sys = bus->sysdata; -- if (sys->remove_bus) -- sys->remove_bus(bus); --} -- - /* - * Swizzle the device pin each time we cross a bridge. If a platform does - * not provide a swizzle function, we perform the standard PCI swizzling. -@@ -427,17 +422,16 @@ static int pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) - static int pcibios_init_resources(int busnr, struct pci_sys_data *sys) - { - int ret; -- struct pci_host_bridge_window *window; -+ struct resource_entry *window; - - if (list_empty(&sys->resources)) { - pci_add_resource_offset(&sys->resources, - &iomem_resource, sys->mem_offset); - } - -- list_for_each_entry(window, &sys->resources, list) { -+ resource_list_for_each_entry(window, &sys->resources) - if (resource_type(window->res) == IORESOURCE_IO) - return 0; -- } - - sys->io_res.start = (busnr * SZ_64K) ? : pcibios_min_io; - sys->io_res.end = (busnr + 1) * SZ_64K - 1; -@@ -468,15 +462,13 @@ static void pcibios_init_hw(struct device *parent, struct hw_pci *hw, - if (!sys) - panic("PCI: unable to allocate sys data!"); - --#ifdef CONFIG_PCI_DOMAINS -- sys->domain = hw->domain; -+#ifdef CONFIG_PCI_MSI -+ sys->msi_ctrl = hw->msi_ctrl; - #endif - sys->busnr = busnr; - sys->swizzle = hw->swizzle; - sys->map_irq = hw->map_irq; - sys->align_resource = hw->align_resource; -- sys->add_bus = hw->add_bus; -- sys->remove_bus = hw->remove_bus; - INIT_LIST_HEAD(&sys->resources); - - if (hw->private_data) -@@ -494,8 +486,9 @@ static void pcibios_init_hw(struct device *parent, struct hw_pci *hw, - if (hw->scan) - sys->bus = hw->scan(nr, sys); - else -- sys->bus = pci_scan_root_bus(parent, sys->busnr, -- hw->ops, sys, &sys->resources); -+ sys->bus = pci_scan_root_bus_msi(parent, -+ sys->busnr, hw->ops, sys, -+ &sys->resources, hw->msi_ctrl); - - if (!sys->bus) - panic("PCI: unable to scan bus!"); -diff --git a/arch/arm/mach-iop13xx/msi.c b/arch/arm/mach-iop13xx/msi.c -index e7730cf..9f89e76 100644 ---- a/arch/arm/mach-iop13xx/msi.c -+++ b/arch/arm/mach-iop13xx/msi.c -@@ -126,10 +126,10 @@ static void iop13xx_msi_nop(struct irq_data *d) - static struct irq_chip iop13xx_msi_chip = { - .name = "PCI-MSI", - .irq_ack = iop13xx_msi_nop, -- .irq_enable = unmask_msi_irq, -- .irq_disable = mask_msi_irq, -- .irq_mask = mask_msi_irq, -- .irq_unmask = unmask_msi_irq, -+ .irq_enable = pci_msi_unmask_irq, -+ .irq_disable = pci_msi_mask_irq, -+ .irq_mask = pci_msi_mask_irq, -+ .irq_unmask = pci_msi_unmask_irq, - }; - - int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc) -@@ -153,7 +153,7 @@ int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc) - id = iop13xx_cpu_id(); - msg.data = (id << IOP13XX_MU_MIMR_CORE_SELECT) | (irq & 0x7f); - -- write_msi_msg(irq, &msg); -+ pci_write_msi_msg(irq, &msg); - irq_set_chip_and_handler(irq, &iop13xx_msi_chip, handle_simple_irq); - - return 0; -diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig -index 00b9c48..329f5f4 100644 ---- a/arch/arm64/Kconfig -+++ b/arch/arm64/Kconfig -@@ -13,7 +13,9 @@ config ARM64 - select ARM_ARCH_TIMER - select ARM_GIC - select AUDIT_ARCH_COMPAT_GENERIC -+ select ARM_GIC_V2M if PCI_MSI - select ARM_GIC_V3 -+ select ARM_GIC_V3_ITS if PCI_MSI - select BUILDTIME_EXTABLE_SORT - select CLONE_BACKWARDS - select COMMON_CLK -@@ -166,6 +168,11 @@ config ARCH_XGENE - help - This enables support for AppliedMicro X-Gene SOC Family - -+config ARCH_LAYERSCAPE -+ bool "ARMv8 based Freescale Layerscape SoC family" -+ help -+ This enables support for the Freescale Layerscape SoC family. -+ - endmenu - - menu "Bus support" -@@ -366,7 +373,6 @@ config ARM64_VA_BITS_42 - - config ARM64_VA_BITS_48 - bool "48-bit" -- depends on !ARM_SMMU - - endchoice - -diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile -index 2d54c55..7cf8a29 100644 ---- a/arch/arm64/Makefile -+++ b/arch/arm64/Makefile -@@ -74,8 +74,13 @@ 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 - - PHONY += vdso_install - vdso_install: -@@ -84,11 +89,13 @@ vdso_install: - # We use MRPROPER_FILES and CLEAN_FILES now - archclean: - $(Q)$(MAKE) $(clean)=$(boot) -+ $(Q)$(MAKE) $(clean)=$(boot)/dts - - 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/dts/Makefile b/arch/arm64/boot/dts/Makefile -index f8001a6..2644389 100644 ---- a/arch/arm64/boot/dts/Makefile -+++ b/arch/arm64/boot/dts/Makefile -@@ -1,6 +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 -+dtb-$(CONFIG_ARCH_XGENE) += apm-mustang.dtb arm64-nxp-ls2088ardb-r1.dtb - - targets += dtbs - targets += $(dtb-y) -diff --git a/arch/arm64/boot/dts/Makefile.rej b/arch/arm64/boot/dts/Makefile.rej -new file mode 100644 -index 0000000..3610e7d ---- /dev/null -+++ b/arch/arm64/boot/dts/Makefile.rej -@@ -0,0 +1,10 @@ -+--- arch/arm64/boot/dts/Makefile -++++ arch/arm64/boot/dts/Makefile -+@@ -1,6 +1,7 @@ -+ 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 -++dtb-$(CONFIG_ARCH_LAYERSCAPE) += arm64-nxp-ls2080ardb-r0.dtb -+ -+ targets += dtbs -+ targets += $(dtb-y) -diff --git a/arch/arm64/boot/dts/arm64-nxp-ls2080ardb-r0.dts b/arch/arm64/boot/dts/arm64-nxp-ls2080ardb-r0.dts -new file mode 100644 -index 0000000..5da2834 ---- /dev/null -+++ b/arch/arm64/boot/dts/arm64-nxp-ls2080ardb-r0.dts -@@ -0,0 +1,249 @@ -+/* -+ * Device Tree file for NXP LS2080a RDB board -+ * -+ */ -+ -+/dts-v1/; -+ -+#include "fsl-ls2080a.dtsi" -+ -+/ { -+ model = "arm64-nxp-ls2080ardb-r0"; -+ compatible = "fsl,ls2080a-rdb", "fsl,ls2080a"; -+}; -+ -+&esdhc { -+ status = "okay"; -+}; -+ -+&ifc { -+ status = "okay"; -+ #address-cells = <2>; -+ #size-cells = <1>; -+ ranges = <0x0 0x0 0x5 0x80000000 0x08000000 -+ 0x2 0x0 0x5 0x30000000 0x00010000 -+ 0x3 0x0 0x5 0x20000000 0x00010000>; -+ -+ nor@0,0 { -+ #address-cells = <1>; -+ #size-cells = <1>; -+ compatible = "cfi-flash"; -+ reg = <0x0 0x0 0x8000000>; -+ bank-width = <2>; -+ device-width = <1>; -+ -+ partition@0 { -+ /* SoC RCW, this location must not be altered */ -+ reg = <0x0 0x100000>; -+ label = "rcw (RO)"; -+ read-only; -+ }; -+ -+ partition@1 { -+ /* U-Boot image */ -+ reg = <0x100000 0x100000>; -+ label = "uboot"; -+ }; -+ -+ partition@2 { -+ /* U-Boot environment varialbes, 1MB */ -+ reg = <0x200000 0x100000>; -+ label = "uboot-env"; -+ env_size = <0x20000>; -+ }; -+ -+ partition@3 { -+ /* MC firmware, 4MB*/ -+ reg = <0x300000 0x400000>; -+ label = "mc_firmware"; -+ }; -+ -+ partition@4 { -+ /* MC DPL Blob, 1MB */ -+ reg = <0x700000 0x100000>; -+ label = "mc_dpl_blob"; -+ }; -+ -+ partition@5 { -+ /* MC DPC Blob, 1MB */ -+ reg = <0x800000 0x100000>; -+ label = "mc_dpc_blob"; -+ }; -+ -+ partition@6 { -+ /* AIOP FW, 4MB */ -+ reg = <0x900000 0x400000>; -+ label = "aiop_fw"; -+ }; -+ -+ partition@7 { -+ /* DebugServerFW, 2MB */ -+ reg = <0xd00000 0x200000>; -+ label = "DebugServer_fw"; -+ }; -+ }; -+ -+ nand@2,0 { -+ #address-cells = <1>; -+ #size-cells = <1>; -+ compatible = "fsl,ifc-nand"; -+ reg = <0x2 0x0 0x10000>; -+ }; -+ -+ cpld@3,0 { -+ reg = <0x3 0x0 0x10000>; -+ compatible = "fsl,ls2080a-rdb-qixis", "fsl,fpga-qixis"; -+ }; -+ -+}; -+ -+&i2c0 { -+ status = "okay"; -+ pca9547@75 { -+ compatible = "nxp,pca9547"; -+ reg = <0x75>; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ i2c-mux-never-disable; -+ i2c@1 { -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0x01>; -+ rtc@68 { -+ compatible = "dallas,ds3232"; -+ reg = <0x68>; -+ }; -+ }; -+ -+ i2c@3 { -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0x3>; -+ -+ adt7481@4c { -+ compatible = "adi,adt7461"; -+ reg = <0x4c>; -+ }; -+ }; -+ }; -+}; -+ -+&i2c1 { -+ status = "disabled"; -+}; -+ -+&i2c2 { -+ status = "disabled"; -+}; -+ -+&i2c3 { -+ status = "disabled"; -+}; -+ -+&dspi { -+ status = "okay"; -+ dflash0: n25q512a { -+ #address-cells = <1>; -+ #size-cells = <1>; -+ compatible = "st,m25p80"; -+ spi-max-frequency = <3000000>; -+ reg = <0>; -+ }; -+}; -+ -+&qspi { -+ status = "disabled"; -+}; -+ -+&sata0 { -+ status = "okay"; -+}; -+ -+&sata1 { -+ status = "okay"; -+}; -+ -+&usb0 { -+ status = "okay"; -+}; -+ -+&usb1 { -+ status = "okay"; -+}; -+ -+&emdio1 { -+ status = "disabled"; -+ /* CS4340 PHYs */ -+ mdio1_phy1: emdio1_phy@1 { -+ reg = <0x10>; -+ phy-connection-type = "xfi"; -+ }; -+ mdio1_phy2: emdio1_phy@2 { -+ reg = <0x11>; -+ phy-connection-type = "xfi"; -+ }; -+ mdio1_phy3: emdio1_phy@3 { -+ reg = <0x12>; -+ phy-connection-type = "xfi"; -+ }; -+ mdio1_phy4: emdio1_phy@4 { -+ reg = <0x13>; -+ phy-connection-type = "xfi"; -+ }; -+}; -+ -+&emdio2 { -+ /* AQR405 PHYs */ -+ mdio2_phy1: emdio2_phy@1 { -+ compatible = "ethernet-phy-ieee802.3-c45"; -+ interrupts = <0 1 0x4>; /* Level high type */ -+ reg = <0x0>; -+ phy-connection-type = "xfi"; -+ }; -+ mdio2_phy2: emdio2_phy@2 { -+ compatible = "ethernet-phy-ieee802.3-c45"; -+ interrupts = <0 2 0x4>; /* Level high type */ -+ reg = <0x1>; -+ phy-connection-type = "xfi"; -+ }; -+ mdio2_phy3: emdio2_phy@3 { -+ compatible = "ethernet-phy-ieee802.3-c45"; -+ interrupts = <0 4 0x4>; /* Level high type */ -+ reg = <0x2>; -+ phy-connection-type = "xfi"; -+ }; -+ mdio2_phy4: emdio2_phy@4 { -+ compatible = "ethernet-phy-ieee802.3-c45"; -+ interrupts = <0 5 0x4>; /* Level high type */ -+ reg = <0x3>; -+ phy-connection-type = "xfi"; -+ }; -+}; -+ -+/* Update DPMAC connections to external PHYs, under the assumption of -+ * SerDes 0x2a_0x41. This is currently the only SerDes supported on the board. -+ */ -+&dpmac1 { -+ phy-handle = <&mdio1_phy1>; -+}; -+&dpmac2 { -+ phy-handle = <&mdio1_phy2>; -+}; -+&dpmac3 { -+ phy-handle = <&mdio1_phy3>; -+}; -+&dpmac4 { -+ phy-handle = <&mdio1_phy4>; -+}; -+&dpmac5 { -+ phy-handle = <&mdio2_phy1>; -+}; -+&dpmac6 { -+ phy-handle = <&mdio2_phy2>; -+}; -+&dpmac7 { -+ phy-handle = <&mdio2_phy3>; -+}; -+&dpmac8 { -+ phy-handle = <&mdio2_phy4>; -+}; -diff --git a/arch/arm64/boot/dts/arm64-nxp-ls2088ardb-r1.dts b/arch/arm64/boot/dts/arm64-nxp-ls2088ardb-r1.dts -new file mode 100644 -index 0000000..0433cf2 ---- /dev/null -+++ b/arch/arm64/boot/dts/arm64-nxp-ls2088ardb-r1.dts -@@ -0,0 +1,256 @@ -+/* -+ * Device Tree file for NXP LS2088a RDB board -+ * -+ * Copyright (C) 2016, Freescale Semiconductor -+ * -+ * This file is licensed under the terms of the GNU General Public -+ * License version 2. This program is licensed "as is" without any -+ * warranty of any kind, whether express or implied. -+ */ -+ -+/dts-v1/; -+ -+#include "fsl-ls2088a.dtsi" -+ -+/ { -+ model = "arm64-nxp-ls2088ardb-r1"; -+ compatible = "fsl,ls2088a-rdb", "fsl,ls2088a"; -+}; -+ -+&esdhc { -+ status = "okay"; -+}; -+ -+&ifc { -+ status = "okay"; -+ #address-cells = <2>; -+ #size-cells = <1>; -+ ranges = <0x0 0x0 0x5 0x80000000 0x08000000 -+ 0x2 0x0 0x5 0x30000000 0x00010000 -+ 0x3 0x0 0x5 0x20000000 0x00010000>; -+ -+ nor@0,0 { -+ #address-cells = <1>; -+ #size-cells = <1>; -+ compatible = "cfi-flash"; -+ reg = <0x0 0x0 0x8000000>; -+ bank-width = <2>; -+ device-width = <1>; -+ -+ partition@0 { -+ /* SoC RCW, this location must not be altered */ -+ reg = <0x0 0x100000>; -+ label = "rcw (RO)"; -+ read-only; -+ }; -+ -+ partition@1 { -+ /* U-Boot image */ -+ reg = <0x100000 0x100000>; -+ label = "uboot"; -+ }; -+ -+ partition@2 { -+ /* U-Boot environment varialbes, 1MB */ -+ reg = <0x200000 0x100000>; -+ label = "uboot-env"; -+ env_size = <0x20000>; -+ }; -+ -+ partition@3 { -+ /* MC firmware, 4MB*/ -+ reg = <0x300000 0x400000>; -+ label = "mc_firmware"; -+ }; -+ -+ partition@4 { -+ /* MC DPL Blob, 1MB */ -+ reg = <0x700000 0x100000>; -+ label = "mc_dpl_blob"; -+ }; -+ -+ partition@5 { -+ /* MC DPC Blob, 1MB */ -+ reg = <0x800000 0x100000>; -+ label = "mc_dpc_blob"; -+ }; -+ -+ partition@6 { -+ /* AIOP FW, 4MB */ -+ reg = <0x900000 0x400000>; -+ label = "aiop_fw"; -+ }; -+ -+ partition@7 { -+ /* DebugServerFW, 2MB */ -+ reg = <0xd00000 0x200000>; -+ label = "DebugServer_fw"; -+ }; -+ }; -+ -+ nand@2,0 { -+ #address-cells = <1>; -+ #size-cells = <1>; -+ compatible = "fsl,ifc-nand"; -+ reg = <0x2 0x0 0x10000>; -+ }; -+ -+ cpld@3,0 { -+ reg = <0x3 0x0 0x10000>; -+ compatible = "fsl,ls2088a-rdb-qixis", "fsl,fpga-qixis"; -+ }; -+}; -+ -+&ftm0 { -+ status = "okay"; -+}; -+ -+&i2c0 { -+ status = "okay"; -+ pca9547@75 { -+ compatible = "nxp,pca9547"; -+ reg = <0x75>; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ i2c-mux-never-disable; -+ i2c@1 { -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0x01>; -+ rtc@68 { -+ compatible = "dallas,ds3232"; -+ reg = <0x68>; -+ }; -+ }; -+ -+ i2c@3 { -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0x3>; -+ -+ adt7481@4c { -+ compatible = "adi,adt7461"; -+ reg = <0x4c>; -+ }; -+ }; -+ }; -+}; -+ -+&i2c1 { -+ status = "disabled"; -+}; -+ -+&i2c2 { -+ status = "disabled"; -+}; -+ -+&i2c3 { -+ status = "disabled"; -+}; -+ -+&dspi { -+ status = "okay"; -+ dflash0: n25q512a { -+ #address-cells = <1>; -+ #size-cells = <1>; -+ compatible = "st,m25p80"; -+ spi-max-frequency = <3000000>; -+ reg = <0>; -+ }; -+}; -+ -+&qspi { -+ status = "disabled"; -+}; -+ -+&sata0 { -+ status = "okay"; -+}; -+ -+&sata1 { -+ status = "okay"; -+}; -+ -+&usb0 { -+ status = "okay"; -+}; -+ -+&usb1 { -+ status = "okay"; -+}; -+ -+&emdio1 { -+ /* CS4340 PHYs */ -+ mdio1_phy1: emdio1_phy@1 { -+ reg = <0x10>; -+ phy-connection-type = "xfi"; -+ }; -+ mdio1_phy2: emdio1_phy@2 { -+ reg = <0x11>; -+ phy-connection-type = "xfi"; -+ }; -+ mdio1_phy3: emdio1_phy@3 { -+ reg = <0x12>; -+ phy-connection-type = "xfi"; -+ }; -+ mdio1_phy4: emdio1_phy@4 { -+ reg = <0x13>; -+ phy-connection-type = "xfi"; -+ }; -+}; -+ -+&emdio2 { -+ /* AQR405 PHYs */ -+ mdio2_phy1: emdio2_phy@1 { -+ compatible = "ethernet-phy-ieee802.3-c45"; -+ interrupts = <0 1 0x4>; /* Level high type */ -+ reg = <0x0>; -+ phy-connection-type = "xfi"; -+ }; -+ mdio2_phy2: emdio2_phy@2 { -+ compatible = "ethernet-phy-ieee802.3-c45"; -+ interrupts = <0 2 0x4>; /* Level high type */ -+ reg = <0x1>; -+ phy-connection-type = "xfi"; -+ }; -+ mdio2_phy3: emdio2_phy@3 { -+ compatible = "ethernet-phy-ieee802.3-c45"; -+ interrupts = <0 4 0x4>; /* Level high type */ -+ reg = <0x2>; -+ phy-connection-type = "xfi"; -+ }; -+ mdio2_phy4: emdio2_phy@4 { -+ compatible = "ethernet-phy-ieee802.3-c45"; -+ interrupts = <0 5 0x4>; /* Level high type */ -+ reg = <0x3>; -+ phy-connection-type = "xfi"; -+ }; -+}; -+ -+/* Update DPMAC connections to external PHYs, under the assumption of -+ * SerDes 0x2a_0x41. This is currently the only SerDes supported on the board. -+ */ -+&dpmac1 { -+ phy-handle = <&mdio1_phy1>; -+}; -+&dpmac2 { -+ phy-handle = <&mdio1_phy2>; -+}; -+&dpmac3 { -+ phy-handle = <&mdio1_phy3>; -+}; -+&dpmac4 { -+ phy-handle = <&mdio1_phy4>; -+}; -+&dpmac5 { -+ phy-handle = <&mdio2_phy1>; -+}; -+&dpmac6 { -+ phy-handle = <&mdio2_phy2>; -+}; -+&dpmac7 { -+ phy-handle = <&mdio2_phy3>; -+}; -+&dpmac8 { -+ phy-handle = <&mdio2_phy4>; -+}; -diff --git a/arch/arm64/boot/dts/fsl-ls2080a.dtsi b/arch/arm64/boot/dts/fsl-ls2080a.dtsi -new file mode 100644 -index 0000000..5e53b04 ---- /dev/null -+++ b/arch/arm64/boot/dts/fsl-ls2080a.dtsi -@@ -0,0 +1,729 @@ -+/* -+ * Device Tree Include file for Freescale Layerscape-2080A family SoC. -+ * -+ * Copyright (C) 2014-2015, Freescale Semiconductor -+ * -+ * Bhupesh Sharma -+ * Harninder Rai -+ * -+ * This file is licensed under the terms of the GNU General Public -+ * License version 2. This program is licensed "as is" without any -+ * warranty of any kind, whether express or implied. -+ */ -+ -+#include -+ -+/memreserve/ 0x80000000 0x00010000; -+ -+/ { -+ compatible = "fsl,ls2080a"; -+ interrupt-parent = <&gic>; -+ #address-cells = <2>; -+ #size-cells = <2>; -+ -+ cpus { -+ #address-cells = <2>; -+ #size-cells = <0>; -+ -+ /* We have 4 clusters having 2 Cortex-A57 cores each */ -+ cpu0: cpu@0 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a57"; -+ reg = <0x0 0x0>; -+ clocks = <&clockgen 1 0>; -+ #cooling-cells = <2>; -+ }; -+ -+ cpu1: cpu@1 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a57"; -+ reg = <0x0 0x1>; -+ clocks = <&clockgen 1 0>; -+ }; -+ -+ cpu2: cpu@100 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a57"; -+ reg = <0x0 0x100>; -+ clocks = <&clockgen 1 1>; -+ #cooling-cells = <2>; -+ }; -+ -+ cpu3: cpu@101 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a57"; -+ reg = <0x0 0x101>; -+ clocks = <&clockgen 1 1>; -+ }; -+ -+ cpu4: cpu@200 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a57"; -+ reg = <0x0 0x200>; -+ clocks = <&clockgen 1 2>; -+ #cooling-cells = <2>; -+ }; -+ -+ cpu5: cpu@201 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a57"; -+ reg = <0x0 0x201>; -+ clocks = <&clockgen 1 2>; -+ }; -+ -+ cpu6: cpu@300 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a57"; -+ reg = <0x0 0x300>; -+ clocks = <&clockgen 1 3>; -+ #cooling-cells = <2>; -+ }; -+ -+ cpu7: cpu@301 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a57"; -+ reg = <0x0 0x301>; -+ clocks = <&clockgen 1 3>; -+ }; -+ }; -+ -+ pmu { -+ compatible = "arm,armv8-pmuv3"; -+ interrupts = <1 7 0x8>; /* PMU PPI, Level low type */ -+ }; -+ -+ gic: interrupt-controller@6000000 { -+ compatible = "arm,gic-v3"; -+ reg = <0x0 0x06000000 0 0x10000>, /* GIC Dist */ -+ <0x0 0x06100000 0 0x100000>, /* GICR (RD_base + SGI_base) */ -+ <0x0 0x0c0c0000 0 0x2000>, /* GICC */ -+ <0x0 0x0c0d0000 0 0x1000>, /* GICH */ -+ <0x0 0x0c0e0000 0 0x20000>; /* GICV */ -+ #interrupt-cells = <3>; -+ #address-cells = <2>; -+ #size-cells = <2>; -+ ranges; -+ interrupt-controller; -+ interrupts = <1 9 0x4>; -+ -+ its: gic-its@6020000 { -+ compatible = "arm,gic-v3-its"; -+ msi-controller; -+ reg = <0x0 0x6020000 0 0x20000>; -+ }; -+ }; -+ -+ sysclk: sysclk { -+ compatible = "fixed-clock"; -+ #clock-cells = <0>; -+ clock-frequency = <100000000>; -+ clock-output-names = "sysclk"; -+ }; -+ -+ clockgen: clocking@1300000 { -+ compatible = "fsl,ls2080a-clockgen"; -+ reg = <0 0x1300000 0 0xa0000>; -+ #clock-cells = <2>; -+ clocks = <&sysclk>; -+ }; -+ -+ tmu: tmu@1f80000 { -+ compatible = "fsl,qoriq-tmu", "fsl,ls2080a-tmu"; -+ reg = <0x0 0x1f80000 0x0 0x10000>; -+ interrupts = <0 23 0x4>; -+ fsl,tmu-range = <0xb0000 0x9002a 0x6004c 0x30062>; -+ fsl,tmu-calibration = <0x00000000 0x00000026 -+ 0x00000001 0x0000002d -+ 0x00000002 0x00000032 -+ 0x00000003 0x00000039 -+ 0x00000004 0x0000003f -+ 0x00000005 0x00000046 -+ 0x00000006 0x0000004d -+ 0x00000007 0x00000054 -+ 0x00000008 0x0000005a -+ 0x00000009 0x00000061 -+ 0x0000000a 0x0000006a -+ 0x0000000b 0x00000071 -+ -+ 0x00010000 0x00000025 -+ 0x00010001 0x0000002c -+ 0x00010002 0x00000035 -+ 0x00010003 0x0000003d -+ 0x00010004 0x00000045 -+ 0x00010005 0x0000004e -+ 0x00010006 0x00000057 -+ 0x00010007 0x00000061 -+ 0x00010008 0x0000006b -+ 0x00010009 0x00000076 -+ -+ 0x00020000 0x00000029 -+ 0x00020001 0x00000033 -+ 0x00020002 0x0000003d -+ 0x00020003 0x00000049 -+ 0x00020004 0x00000056 -+ 0x00020005 0x00000061 -+ 0x00020006 0x0000006d -+ -+ 0x00030000 0x00000021 -+ 0x00030001 0x0000002a -+ 0x00030002 0x0000003c -+ 0x00030003 0x0000004e>; -+ little-endian; -+ #thermal-sensor-cells = <1>; -+ }; -+ -+ thermal-zones { -+ cpu_thermal: cpu-thermal { -+ polling-delay-passive = <1000>; -+ polling-delay = <5000>; -+ -+ thermal-sensors = <&tmu 4>; -+ -+ trips { -+ cpu_alert: cpu-alert { -+ temperature = <75000>; -+ hysteresis = <2000>; -+ type = "passive"; -+ }; -+ cpu_crit: cpu-crit { -+ temperature = <85000>; -+ hysteresis = <2000>; -+ type = "critical"; -+ }; -+ }; -+ -+ cooling-maps { -+ map0 { -+ trip = <&cpu_alert>; -+ cooling-device = -+ <&cpu0 THERMAL_NO_LIMIT -+ THERMAL_NO_LIMIT>; -+ }; -+ map1 { -+ trip = <&cpu_alert>; -+ cooling-device = -+ <&cpu2 THERMAL_NO_LIMIT -+ THERMAL_NO_LIMIT>; -+ }; -+ map2 { -+ trip = <&cpu_alert>; -+ cooling-device = -+ <&cpu4 THERMAL_NO_LIMIT -+ THERMAL_NO_LIMIT>; -+ }; -+ map3 { -+ trip = <&cpu_alert>; -+ cooling-device = -+ <&cpu6 THERMAL_NO_LIMIT -+ THERMAL_NO_LIMIT>; -+ }; -+ }; -+ }; -+ }; -+ -+ serial0: serial@21c0500 { -+ device_type = "serial"; -+ compatible = "fsl,ns16550", "ns16550a"; -+ reg = <0x0 0x21c0500 0x0 0x100>; -+ clocks = <&clockgen 4 3>; -+ interrupts = <0 32 0x4>; /* Level high type */ -+ }; -+ -+ serial1: serial@21c0600 { -+ device_type = "serial"; -+ compatible = "fsl,ns16550", "ns16550a"; -+ reg = <0x0 0x21c0600 0x0 0x100>; -+ clocks = <&clockgen 4 3>; -+ interrupts = <0 32 0x4>; /* Level high type */ -+ }; -+ -+ gpio0: gpio@2300000 { -+ compatible = "fsl,qoriq-gpio"; -+ reg = <0x0 0x2300000 0x0 0x10000>; -+ interrupts = <0 36 0x4>; /* Level high type */ -+ gpio-controller; -+ little-endian; -+ #gpio-cells = <2>; -+ interrupt-controller; -+ #interrupt-cells = <2>; -+ }; -+ -+ gpio1: gpio@2310000 { -+ compatible = "fsl,qoriq-gpio"; -+ reg = <0x0 0x2310000 0x0 0x10000>; -+ interrupts = <0 36 0x4>; /* Level high type */ -+ gpio-controller; -+ little-endian; -+ #gpio-cells = <2>; -+ interrupt-controller; -+ #interrupt-cells = <2>; -+ }; -+ -+ gpio2: gpio@2320000 { -+ compatible = "fsl,qoriq-gpio"; -+ reg = <0x0 0x2320000 0x0 0x10000>; -+ interrupts = <0 37 0x4>; /* Level high type */ -+ gpio-controller; -+ little-endian; -+ #gpio-cells = <2>; -+ interrupt-controller; -+ #interrupt-cells = <2>; -+ }; -+ -+ gpio3: gpio@2330000 { -+ compatible = "fsl,qoriq-gpio"; -+ reg = <0x0 0x2330000 0x0 0x10000>; -+ interrupts = <0 37 0x4>; /* Level high type */ -+ gpio-controller; -+ little-endian; -+ #gpio-cells = <2>; -+ interrupt-controller; -+ #interrupt-cells = <2>; -+ }; -+ -+ /* TODO: WRIOP (CCSR?) */ -+ emdio1: mdio@0x8B96000 { /* WRIOP0: 0x8B8_0000, E-MDIO1: 0x1_6000 */ -+ compatible = "fsl,fman-memac-mdio"; -+ reg = <0x0 0x8B96000 0x0 0x1000>; -+ device_type = "mdio"; /* TODO: is this necessary? */ -+ little-endian; /* force the driver in LE mode */ -+ -+ /* Not necessary on the QDS, but needed on the RDB */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ }; -+ -+ emdio2: mdio@0x8B97000 { /* WRIOP0: 0x8B8_0000, E-MDIO2: 0x1_7000 */ -+ compatible = "fsl,fman-memac-mdio"; -+ reg = <0x0 0x8B97000 0x0 0x1000>; -+ device_type = "mdio"; /* TODO: is this necessary? */ -+ little-endian; /* force the driver in LE mode */ -+ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ }; -+ -+ ifc: ifc@2240000 { -+ compatible = "fsl,ifc", "simple-bus"; -+ reg = <0x0 0x2240000 0x0 0x20000>; -+ interrupts = <0 21 0x4>; /* Level high type */ -+ little-endian; -+ #address-cells = <2>; -+ #size-cells = <1>; -+ -+ ranges = <0 0 0x5 0x80000000 0x08000000 -+ 2 0 0x5 0x30000000 0x00010000 -+ 3 0 0x5 0x20000000 0x00010000>; -+ }; -+ -+ esdhc: esdhc@2140000 { -+ compatible = "fsl,ls2080a-esdhc", "fsl,esdhc"; -+ reg = <0x0 0x2140000 0x0 0x10000>; -+ interrupts = <0 28 0x4>; /* Level high type */ -+ clock-frequency = <0>; -+ voltage-ranges = <1800 1800 3300 3300>; -+ sdhci,auto-cmd12; -+ little-endian; -+ bus-width = <4>; -+ }; -+ -+ ftm0: ftm0@2800000 { -+ compatible = "fsl,ftm-alarm"; -+ reg = <0x0 0x2800000 0x0 0x10000>; -+ interrupts = <0 44 4>; -+ }; -+ -+ reset: reset@1E60000 { -+ compatible = "fsl,ls-reset"; -+ reg = <0x0 0x1E60000 0x0 0x10000>; -+ }; -+ -+ dspi: dspi@2100000 { -+ compatible = "fsl,ls2085a-dspi", "fsl,ls2080a-dspi"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0x0 0x2100000 0x0 0x10000>; -+ interrupts = <0 26 0x4>; /* Level high type */ -+ clocks = <&clockgen 4 3>; -+ clock-names = "dspi"; -+ spi-num-chipselects = <5>; -+ bus-num = <0>; -+ }; -+ -+ i2c0: i2c@2000000 { -+ compatible = "fsl,vf610-i2c"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0x0 0x2000000 0x0 0x10000>; -+ interrupts = <0 34 0x4>; /* Level high type */ -+ clock-names = "i2c"; -+ clocks = <&clockgen 4 3>; -+ }; -+ -+ i2c1: i2c@2010000 { -+ compatible = "fsl,vf610-i2c"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0x0 0x2010000 0x0 0x10000>; -+ interrupts = <0 34 0x4>; /* Level high type */ -+ clock-names = "i2c"; -+ clocks = <&clockgen 4 3>; -+ }; -+ -+ i2c2: i2c@2020000 { -+ compatible = "fsl,vf610-i2c"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0x0 0x2020000 0x0 0x10000>; -+ interrupts = <0 35 0x4>; /* Level high type */ -+ clock-names = "i2c"; -+ clocks = <&clockgen 4 3>; -+ }; -+ -+ i2c3: i2c@2030000 { -+ compatible = "fsl,vf610-i2c"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0x0 0x2030000 0x0 0x10000>; -+ interrupts = <0 35 0x4>; /* Level high type */ -+ clock-names = "i2c"; -+ clocks = <&clockgen 4 3>; -+ }; -+ -+ qspi: quadspi@20c0000 { -+ compatible = "fsl,ls2080a-qspi"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0x0 0x20c0000 0x0 0x10000>, -+ <0x0 0x20000000 0x0 0x10000000>; -+ reg-names = "QuadSPI", "QuadSPI-memory"; -+ interrupts = <0 25 0x4>; /* Level high type */ -+ clocks = <&clockgen 4 3>, <&clockgen 4 3>; -+ clock-names = "qspi_en", "qspi"; -+ }; -+ -+ pcie@3400000 { -+ compatible = "fsl,ls2080a-pcie", "fsl,ls2085a-pcie", -+ "snps,dw-pcie"; -+ reg = <0x00 0x03400000 0x0 0x00100000 /* controller registers */ -+ 0x10 0x00000000 0x0 0x00001000>; /* configuration space */ -+ reg-names = "regs", "config"; -+ interrupts = <0 108 0x4>; /* Level high type */ -+ interrupt-names = "intr"; -+ #address-cells = <3>; -+ #size-cells = <2>; -+ device_type = "pci"; -+ num-lanes = <4>; -+ bus-range = <0x0 0xff>; -+ ranges = <0x81000000 0x0 0x00000000 0x10 0x00010000 0x0 0x00010000 /* downstream I/O */ -+ 0x82000000 0x0 0x40000000 0x10 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */ -+ msi-parent = <&its>; -+ #interrupt-cells = <1>; -+ interrupt-map-mask = <0 0 0 7>; -+ interrupt-map = <0000 0 0 1 &gic 0 0 0 109 4>, -+ <0000 0 0 2 &gic 0 0 0 110 4>, -+ <0000 0 0 3 &gic 0 0 0 111 4>, -+ <0000 0 0 4 &gic 0 0 0 112 4>; -+ }; -+ -+ pcie@3500000 { -+ compatible = "fsl,ls2080a-pcie", "fsl,ls2085a-pcie", -+ "snps,dw-pcie"; -+ reg = <0x00 0x03500000 0x0 0x00100000 /* controller registers */ -+ 0x12 0x00000000 0x0 0x00001000>; /* configuration space */ -+ reg-names = "regs", "config"; -+ interrupts = <0 113 0x4>; /* Level high type */ -+ interrupt-names = "intr"; -+ #address-cells = <3>; -+ #size-cells = <2>; -+ device_type = "pci"; -+ num-lanes = <4>; -+ bus-range = <0x0 0xff>; -+ ranges = <0x81000000 0x0 0x00000000 0x12 0x00010000 0x0 0x00010000 /* downstream I/O */ -+ 0x82000000 0x0 0x40000000 0x12 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */ -+ msi-parent = <&its>; -+ #interrupt-cells = <1>; -+ interrupt-map-mask = <0 0 0 7>; -+ interrupt-map = <0000 0 0 1 &gic 0 0 0 114 4>, -+ <0000 0 0 2 &gic 0 0 0 115 4>, -+ <0000 0 0 3 &gic 0 0 0 116 4>, -+ <0000 0 0 4 &gic 0 0 0 117 4>; -+ }; -+ -+ pcie@3600000 { -+ compatible = "fsl,ls2080a-pcie", "fsl,ls2085a-pcie", -+ "snps,dw-pcie"; -+ reg = <0x00 0x03600000 0x0 0x00100000 /* controller registers */ -+ 0x14 0x00000000 0x0 0x00001000>; /* configuration space */ -+ reg-names = "regs", "config"; -+ interrupts = <0 118 0x4>; /* Level high type */ -+ interrupt-names = "intr"; -+ #address-cells = <3>; -+ #size-cells = <2>; -+ device_type = "pci"; -+ num-lanes = <8>; -+ bus-range = <0x0 0xff>; -+ ranges = <0x81000000 0x0 0x00000000 0x14 0x00010000 0x0 0x00010000 /* downstream I/O */ -+ 0x82000000 0x0 0x40000000 0x14 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */ -+ msi-parent = <&its>; -+ #interrupt-cells = <1>; -+ interrupt-map-mask = <0 0 0 7>; -+ interrupt-map = <0000 0 0 1 &gic 0 0 0 119 4>, -+ <0000 0 0 2 &gic 0 0 0 120 4>, -+ <0000 0 0 3 &gic 0 0 0 121 4>, -+ <0000 0 0 4 &gic 0 0 0 122 4>; -+ }; -+ -+ pcie@3700000 { -+ compatible = "fsl,ls2080a-pcie", "fsl,ls2085a-pcie", -+ "snps,dw-pcie"; -+ reg = <0x00 0x03700000 0x0 0x00100000 /* controller registers */ -+ 0x16 0x00000000 0x0 0x00001000>; /* configuration space */ -+ reg-names = "regs", "config"; -+ interrupts = <0 123 0x4>; /* Level high type */ -+ interrupt-names = "intr"; -+ #address-cells = <3>; -+ #size-cells = <2>; -+ device_type = "pci"; -+ num-lanes = <4>; -+ bus-range = <0x0 0xff>; -+ ranges = <0x81000000 0x0 0x00000000 0x16 0x00010000 0x0 0x00010000 /* downstream I/O */ -+ 0x82000000 0x0 0x40000000 0x16 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */ -+ msi-parent = <&its>; -+ #interrupt-cells = <1>; -+ interrupt-map-mask = <0 0 0 7>; -+ interrupt-map = <0000 0 0 1 &gic 0 0 0 124 4>, -+ <0000 0 0 2 &gic 0 0 0 125 4>, -+ <0000 0 0 3 &gic 0 0 0 126 4>, -+ <0000 0 0 4 &gic 0 0 0 127 4>; -+ }; -+ -+ sata0: sata@3200000 { -+ compatible = "fsl,ls2080a-ahci", "fsl,ls1021a-ahci"; -+ reg = <0x0 0x3200000 0x0 0x10000>; -+ interrupts = <0 133 0x4>; /* Level high type */ -+ clocks = <&clockgen 4 3>; -+ }; -+ -+ sata1: sata@3210000 { -+ compatible = "fsl,ls2080a-ahci", "fsl,ls1021a-ahci"; -+ reg = <0x0 0x3210000 0x0 0x10000>; -+ interrupts = <0 136 0x4>; /* Level high type */ -+ clocks = <&clockgen 4 3>; -+ }; -+ -+ usb0: usb3@3100000 { -+ compatible = "snps,dwc3"; -+ reg = <0x0 0x3100000 0x0 0x10000>; -+ interrupts = <0 80 0x4>; /* Level high type */ -+ dr_mode = "host"; -+ configure-gfladj; -+ }; -+ -+ usb1: usb3@3110000 { -+ compatible = "snps,dwc3"; -+ reg = <0x0 0x3110000 0x0 0x10000>; -+ interrupts = <0 81 0x4>; /* Level high type */ -+ dr_mode = "host"; -+ configure-gfladj; -+ }; -+ -+ smmu: iommu@5000000 { -+ compatible = "arm,mmu-500"; -+ reg = <0 0x5000000 0 0x800000>; -+ #global-interrupts = <12>; -+ interrupts = <0 13 4>, /* global secure fault */ -+ <0 14 4>, /* combined secure interrupt */ -+ <0 15 4>, /* global non-secure fault */ -+ <0 16 4>, /* combined non-secure interrupt */ -+ /* performance counter interrupts 0-7 */ -+ <0 211 4>, -+ <0 212 4>, -+ <0 213 4>, -+ <0 214 4>, -+ <0 215 4>, -+ <0 216 4>, -+ <0 217 4>, -+ <0 218 4>, -+ /* per context interrupt, 64 interrupts */ -+ <0 146 4>, -+ <0 147 4>, -+ <0 148 4>, -+ <0 149 4>, -+ <0 150 4>, -+ <0 151 4>, -+ <0 152 4>, -+ <0 153 4>, -+ <0 154 4>, -+ <0 155 4>, -+ <0 156 4>, -+ <0 157 4>, -+ <0 158 4>, -+ <0 159 4>, -+ <0 160 4>, -+ <0 161 4>, -+ <0 162 4>, -+ <0 163 4>, -+ <0 164 4>, -+ <0 165 4>, -+ <0 166 4>, -+ <0 167 4>, -+ <0 168 4>, -+ <0 169 4>, -+ <0 170 4>, -+ <0 171 4>, -+ <0 172 4>, -+ <0 173 4>, -+ <0 174 4>, -+ <0 175 4>, -+ <0 176 4>, -+ <0 177 4>, -+ <0 178 4>, -+ <0 179 4>, -+ <0 180 4>, -+ <0 181 4>, -+ <0 182 4>, -+ <0 183 4>, -+ <0 184 4>, -+ <0 185 4>, -+ <0 186 4>, -+ <0 187 4>, -+ <0 188 4>, -+ <0 189 4>, -+ <0 190 4>, -+ <0 191 4>, -+ <0 192 4>, -+ <0 193 4>, -+ <0 194 4>, -+ <0 195 4>, -+ <0 196 4>, -+ <0 197 4>, -+ <0 198 4>, -+ <0 199 4>, -+ <0 200 4>, -+ <0 201 4>, -+ <0 202 4>, -+ <0 203 4>, -+ <0 204 4>, -+ <0 205 4>, -+ <0 206 4>, -+ <0 207 4>, -+ <0 208 4>, -+ <0 209 4>; -+ mmu-masters = <&fsl_mc 0x300 0>; -+ }; -+ -+ timer { -+ compatible = "arm,armv8-timer"; -+ interrupts = <1 13 0x1>, /* Physical Secure PPI, edge triggered */ -+ <1 14 0x1>, /* Physical Non-Secure PPI, edge triggered */ -+ <1 11 0x1>, /* Virtual PPI, edge triggered */ -+ <1 10 0x1>; /* Hypervisor PPI, edge triggered */ -+ arm,reread-timer; -+ }; -+ -+ fsl_mc: fsl-mc@80c000000 { -+ compatible = "fsl,qoriq-mc"; -+ #stream-id-cells = <2>; -+ reg = <0x00000008 0x0c000000 0 0x40>, /* MC portal base */ -+ <0x00000000 0x08340000 0 0x40000>; /* MC control reg */ -+ msi-parent = <&its>; -+ #address-cells = <3>; -+ #size-cells = <1>; -+ -+ /* -+ * Region type 0x0 - MC portals -+ * Region type 0x1 - QBMAN portals -+ */ -+ ranges = <0x0 0x0 0x0 0x8 0x0c000000 0x4000000 -+ 0x1 0x0 0x0 0x8 0x18000000 0x8000000>; -+ -+ /* -+ * Define the maximum number of MACs present on the SoC. -+ * They won't necessarily be all probed, since the -+ * Data Path Layout file and the MC firmware can put fewer -+ * actual DPMAC objects on the MC bus. -+ */ -+ dpmacs { -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ dpmac1: dpmac@1 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <1>; -+ }; -+ dpmac2: dpmac@2 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <2>; -+ }; -+ dpmac3: dpmac@3 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <3>; -+ }; -+ dpmac4: dpmac@4 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <4>; -+ }; -+ dpmac5: dpmac@5 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <5>; -+ }; -+ dpmac6: dpmac@6 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <6>; -+ }; -+ dpmac7: dpmac@7 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <7>; -+ }; -+ dpmac8: dpmac@8 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <8>; -+ }; -+ dpmac9: dpmac@9 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <9>; -+ }; -+ dpmac10: dpmac@10 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <0xa>; -+ }; -+ dpmac11: dpmac@11 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <0xb>; -+ }; -+ dpmac12: dpmac@12 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <0xc>; -+ }; -+ dpmac13: dpmac@13 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <0xd>; -+ }; -+ dpmac14: dpmac@14 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <0xe>; -+ }; -+ dpmac15: dpmac@15 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <0xf>; -+ }; -+ dpmac16: dpmac@16 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <0x10>; -+ }; -+ }; -+ }; -+ -+ ccn@4000000 { -+ compatible = "arm,ccn-504"; -+ reg = <0x0 0x04000000 0x0 0x01000000>; -+ interrupts = <0 12 4>; -+ }; -+ -+ memory@80000000 { -+ device_type = "memory"; -+ reg = <0x00000000 0x80000000 0 0x80000000>; -+ /* DRAM space 1 - 2 GB DRAM */ -+ }; -+}; -diff --git a/arch/arm64/boot/dts/fsl-ls2088a.dtsi b/arch/arm64/boot/dts/fsl-ls2088a.dtsi -new file mode 100644 -index 0000000..2e3529a ---- /dev/null -+++ b/arch/arm64/boot/dts/fsl-ls2088a.dtsi -@@ -0,0 +1,833 @@ -+/* -+ * Device Tree Include file for Freescale Layerscape-2088A family SoC. -+ * -+ * Copyright (C) 2016, Freescale Semiconductor -+ * -+ * Abhimanyu Saini -+ * -+ * This file is dual-licensed: you can use it either under the terms -+ * of the GPLv2 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. -+ * -+ * 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. -+ */ -+ -+#include "thermal.h" -+ -+/memreserve/ 0x80000000 0x00010000; -+ -+/ { -+ compatible = "fsl,ls2088a"; -+ interrupt-parent = <&gic>; -+ #address-cells = <2>; -+ #size-cells = <2>; -+ -+ cpus { -+ #address-cells = <2>; -+ #size-cells = <0>; -+ -+ cpu0: cpu@0 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a72"; -+ reg = <0x0 0x0>; -+ clocks = <&clockgen 1 0>; -+ #cooling-cells = <2>; -+ }; -+ -+ cpu1: cpu@1 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a72"; -+ reg = <0x0 0x1>; -+ clocks = <&clockgen 1 0>; -+ }; -+ -+ cpu2: cpu@100 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a72"; -+ reg = <0x0 0x100>; -+ clocks = <&clockgen 1 1>; -+ #cooling-cells = <2>; -+ }; -+ -+ cpu3: cpu@101 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a72"; -+ reg = <0x0 0x101>; -+ clocks = <&clockgen 1 1>; -+ }; -+ -+ cpu4: cpu@200 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a72"; -+ reg = <0x0 0x200>; -+ clocks = <&clockgen 1 2>; -+ #cooling-cells = <2>; -+ }; -+ -+ cpu5: cpu@201 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a72"; -+ reg = <0x0 0x201>; -+ clocks = <&clockgen 1 2>; -+ }; -+ -+ cpu6: cpu@300 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a72"; -+ reg = <0x0 0x300>; -+ clocks = <&clockgen 1 3>; -+ #cooling-cells = <2>; -+ }; -+ -+ cpu7: cpu@301 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a72"; -+ reg = <0x0 0x301>; -+ clocks = <&clockgen 1 3>; -+ }; -+ }; -+ -+ pmu { -+ compatible = "arm,armv8-pmuv3"; -+ interrupts = <1 7 0x8>; /* PMU PPI, Level low type */ -+ }; -+ -+ gic: interrupt-controller@6000000 { -+ compatible = "arm,gic-v3"; -+ reg = <0x0 0x06000000 0 0x10000>, /* GIC Dist */ -+ <0x0 0x06100000 0 0x100000>, /* GICR (RD_base + SGI_base) */ -+ <0x0 0x0c0c0000 0 0x2000>, /* GICC */ -+ <0x0 0x0c0d0000 0 0x1000>, /* GICH */ -+ <0x0 0x0c0e0000 0 0x20000>; /* GICV */ -+ #interrupt-cells = <3>; -+ #address-cells = <2>; -+ #size-cells = <2>; -+ ranges; -+ interrupt-controller; -+ interrupts = <1 9 0x4>; -+ -+ its: gic-its@6020000 { -+ compatible = "arm,gic-v3-its"; -+ msi-controller; -+ reg = <0x0 0x6020000 0 0x20000>; -+ }; -+ }; -+ -+ sysclk: sysclk { -+ compatible = "fixed-clock"; -+ #clock-cells = <0>; -+ clock-frequency = <100000000>; -+ clock-output-names = "sysclk"; -+ }; -+ -+ clockgen: clocking@1300000 { -+ compatible = "fsl,ls2088a-clockgen"; -+ reg = <0 0x1300000 0 0xa0000>; -+ #clock-cells = <2>; -+ clocks = <&sysclk>; -+ }; -+ -+ tmu: tmu@1f80000 { -+ compatible = "fsl,qoriq-tmu", "fsl,ls2080a-tmu", "fsl,ls2088a-tmu"; -+ reg = <0x0 0x1f80000 0x0 0x10000>; -+ interrupts = <0 23 0x4>; -+ fsl,tmu-range = <0xb0000 0x9002a 0x6004c 0x30062>; -+ fsl,tmu-calibration = <0x00000000 0x00000026 -+ 0x00000001 0x0000002d -+ 0x00000002 0x00000032 -+ 0x00000003 0x00000039 -+ 0x00000004 0x0000003f -+ 0x00000005 0x00000046 -+ 0x00000006 0x0000004d -+ 0x00000007 0x00000054 -+ 0x00000008 0x0000005a -+ 0x00000009 0x00000061 -+ 0x0000000a 0x0000006a -+ 0x0000000b 0x00000071 -+ -+ 0x00010000 0x00000025 -+ 0x00010001 0x0000002c -+ 0x00010002 0x00000035 -+ 0x00010003 0x0000003d -+ 0x00010004 0x00000045 -+ 0x00010005 0x0000004e -+ 0x00010006 0x00000057 -+ 0x00010007 0x00000061 -+ 0x00010008 0x0000006b -+ 0x00010009 0x00000076 -+ -+ 0x00020000 0x00000029 -+ 0x00020001 0x00000033 -+ 0x00020002 0x0000003d -+ 0x00020003 0x00000049 -+ 0x00020004 0x00000056 -+ 0x00020005 0x00000061 -+ 0x00020006 0x0000006d -+ -+ 0x00030000 0x00000021 -+ 0x00030001 0x0000002a -+ 0x00030002 0x0000003c -+ 0x00030003 0x0000004e>; -+ little-endian; -+ #thermal-sensor-cells = <1>; -+ }; -+ -+ thermal-zones { -+ cpu_thermal: cpu-thermal { -+ polling-delay-passive = <1000>; -+ polling-delay = <5000>; -+ -+ thermal-sensors = <&tmu 4>; -+ -+ trips { -+ cpu_alert: cpu-alert { -+ temperature = <75000>; -+ hysteresis = <2000>; -+ type = "passive"; -+ }; -+ cpu_crit: cpu-crit { -+ temperature = <85000>; -+ hysteresis = <2000>; -+ type = "critical"; -+ }; -+ }; -+ -+ cooling-maps { -+ map0 { -+ trip = <&cpu_alert>; -+ cooling-device = -+ <&cpu0 THERMAL_NO_LIMIT -+ THERMAL_NO_LIMIT>; -+ }; -+ map1 { -+ trip = <&cpu_alert>; -+ cooling-device = -+ <&cpu2 THERMAL_NO_LIMIT -+ THERMAL_NO_LIMIT>; -+ }; -+ map2 { -+ trip = <&cpu_alert>; -+ cooling-device = -+ <&cpu4 THERMAL_NO_LIMIT -+ THERMAL_NO_LIMIT>; -+ }; -+ map3 { -+ trip = <&cpu_alert>; -+ cooling-device = -+ <&cpu6 THERMAL_NO_LIMIT -+ THERMAL_NO_LIMIT>; -+ }; -+ }; -+ }; -+ }; -+ -+ serial0: serial@21c0500 { -+ device_type = "serial"; -+ compatible = "fsl,ns16550", "ns16550a"; -+ reg = <0x0 0x21c0500 0x0 0x100>; -+ clocks = <&clockgen 4 3>; -+ interrupts = <0 32 0x4>; /* Level high type */ -+ }; -+ -+ serial1: serial@21c0600 { -+ device_type = "serial"; -+ compatible = "fsl,ns16550", "ns16550a"; -+ reg = <0x0 0x21c0600 0x0 0x100>; -+ clocks = <&clockgen 4 3>; -+ interrupts = <0 32 0x4>; /* Level high type */ -+ }; -+ cluster1_core0_watchdog: wdt@c000000 { -+ compatible = "arm,sp805-wdt", "arm,primecell"; -+ reg = <0x0 0xc000000 0x0 0x1000>; -+ clocks = <&clockgen 4 3>, <&clockgen 4 3>; -+ clock-names = "apb_pclk", "wdog_clk"; -+ }; -+ -+ cluster1_core1_watchdog: wdt@c010000 { -+ compatible = "arm,sp805-wdt", "arm,primecell"; -+ reg = <0x0 0xc010000 0x0 0x1000>; -+ clocks = <&clockgen 4 3>, <&clockgen 4 3>; -+ clock-names = "apb_pclk", "wdog_clk"; -+ }; -+ -+ cluster2_core0_watchdog: wdt@c100000 { -+ compatible = "arm,sp805-wdt", "arm,primecell"; -+ reg = <0x0 0xc100000 0x0 0x1000>; -+ clocks = <&clockgen 4 3>, <&clockgen 4 3>; -+ clock-names = "apb_pclk", "wdog_clk"; -+ }; -+ -+ cluster2_core1_watchdog: wdt@c110000 { -+ compatible = "arm,sp805-wdt", "arm,primecell"; -+ reg = <0x0 0xc110000 0x0 0x1000>; -+ clocks = <&clockgen 4 3>, <&clockgen 4 3>; -+ clock-names = "apb_pclk", "wdog_clk"; -+ }; -+ -+ cluster3_core0_watchdog: wdt@c200000 { -+ compatible = "arm,sp805-wdt", "arm,primecell"; -+ reg = <0x0 0xc200000 0x0 0x1000>; -+ clocks = <&clockgen 4 3>, <&clockgen 4 3>; -+ clock-names = "apb_pclk", "wdog_clk"; -+ }; -+ -+ cluster3_core1_watchdog: wdt@c210000 { -+ compatible = "arm,sp805-wdt", "arm,primecell"; -+ reg = <0x0 0xc210000 0x0 0x1000>; -+ clocks = <&clockgen 4 3>, <&clockgen 4 3>; -+ clock-names = "apb_pclk", "wdog_clk"; -+ }; -+ -+ cluster4_core0_watchdog: wdt@c300000 { -+ compatible = "arm,sp805-wdt", "arm,primecell"; -+ reg = <0x0 0xc300000 0x0 0x1000>; -+ clocks = <&clockgen 4 3>, <&clockgen 4 3>; -+ clock-names = "apb_pclk", "wdog_clk"; -+ }; -+ -+ cluster4_core1_watchdog: wdt@c310000 { -+ compatible = "arm,sp805-wdt", "arm,primecell"; -+ reg = <0x0 0xc310000 0x0 0x1000>; -+ clocks = <&clockgen 4 3>, <&clockgen 4 3>; -+ clock-names = "apb_pclk", "wdog_clk"; -+ }; -+ -+ gpio0: gpio@2300000 { -+ compatible = "fsl,qoriq-gpio"; -+ reg = <0x0 0x2300000 0x0 0x10000>; -+ interrupts = <0 36 0x4>; /* Level high type */ -+ gpio-controller; -+ little-endian; -+ #gpio-cells = <2>; -+ interrupt-controller; -+ #interrupt-cells = <2>; -+ }; -+ -+ gpio1: gpio@2310000 { -+ compatible = "fsl,qoriq-gpio"; -+ reg = <0x0 0x2310000 0x0 0x10000>; -+ interrupts = <0 36 0x4>; /* Level high type */ -+ gpio-controller; -+ little-endian; -+ #gpio-cells = <2>; -+ interrupt-controller; -+ #interrupt-cells = <2>; -+ }; -+ -+ gpio2: gpio@2320000 { -+ compatible = "fsl,qoriq-gpio"; -+ reg = <0x0 0x2320000 0x0 0x10000>; -+ interrupts = <0 37 0x4>; /* Level high type */ -+ gpio-controller; -+ little-endian; -+ #gpio-cells = <2>; -+ interrupt-controller; -+ #interrupt-cells = <2>; -+ }; -+ -+ gpio3: gpio@2330000 { -+ compatible = "fsl,qoriq-gpio"; -+ reg = <0x0 0x2330000 0x0 0x10000>; -+ interrupts = <0 37 0x4>; /* Level high type */ -+ gpio-controller; -+ little-endian; -+ #gpio-cells = <2>; -+ interrupt-controller; -+ #interrupt-cells = <2>; -+ }; -+ -+ /* TODO: WRIOP (CCSR?) */ -+ emdio1: mdio@0x8B96000 { /* WRIOP0: 0x8B8_0000, E-MDIO1: 0x1_6000 */ -+ compatible = "fsl,fman-memac-mdio"; -+ reg = <0x0 0x8B96000 0x0 0x1000>; -+ device_type = "mdio"; /* TODO: is this necessary? */ -+ little-endian; /* force the driver in LE mode */ -+ -+ /* Not necessary on the QDS, but needed on the RDB */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ }; -+ -+ emdio2: mdio@0x8B97000 { /* WRIOP0: 0x8B8_0000, E-MDIO2: 0x1_7000 */ -+ compatible = "fsl,fman-memac-mdio"; -+ reg = <0x0 0x8B97000 0x0 0x1000>; -+ device_type = "mdio"; /* TODO: is this necessary? */ -+ little-endian; /* force the driver in LE mode */ -+ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ }; -+ -+ ifc: ifc@2240000 { -+ compatible = "fsl,ifc", "simple-bus"; -+ reg = <0x0 0x2240000 0x0 0x20000>; -+ interrupts = <0 21 0x4>; /* Level high type */ -+ little-endian; -+ #address-cells = <2>; -+ #size-cells = <1>; -+ -+ ranges = <0 0 0x5 0x80000000 0x08000000 -+ 2 0 0x5 0x30000000 0x00010000 -+ 3 0 0x5 0x20000000 0x00010000>; -+ }; -+ -+ esdhc: esdhc@2140000 { -+ compatible = "fsl,ls2088a-esdhc", "fsl,ls2080a-esdhc", -+ "fsl,esdhc"; -+ reg = <0x0 0x2140000 0x0 0x10000>; -+ interrupts = <0 28 0x4>; /* Level high type */ -+ clock-frequency = <0>; -+ voltage-ranges = <1800 1800 3300 3300>; -+ sdhci,auto-cmd12; -+ little-endian; -+ bus-width = <4>; -+ }; -+ -+ ftm0: ftm0@2800000 { -+ compatible = "fsl,ftm-alarm"; -+ reg = <0x0 0x2800000 0x0 0x10000>; -+ interrupts = <0 44 4>; -+ }; -+ -+ reset: reset@1E60000 { -+ compatible = "fsl,ls-reset"; -+ reg = <0x0 0x1E60000 0x0 0x10000>; -+ }; -+ -+ dspi: dspi@2100000 { -+ compatible = "fsl,ls2088a-dspi", "fsl,ls2085a-dspi", -+ "fsl,ls2080a-dspi"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0x0 0x2100000 0x0 0x10000>; -+ interrupts = <0 26 0x4>; /* Level high type */ -+ clocks = <&clockgen 4 3>; -+ clock-names = "dspi"; -+ spi-num-chipselects = <5>; -+ bus-num = <0>; -+ }; -+ -+ i2c0: i2c@2000000 { -+ compatible = "fsl,vf610-i2c"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0x0 0x2000000 0x0 0x10000>; -+ interrupts = <0 34 0x4>; /* Level high type */ -+ clock-names = "i2c"; -+ clocks = <&clockgen 4 3>; -+ }; -+ -+ i2c1: i2c@2010000 { -+ compatible = "fsl,vf610-i2c"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0x0 0x2010000 0x0 0x10000>; -+ interrupts = <0 34 0x4>; /* Level high type */ -+ clock-names = "i2c"; -+ clocks = <&clockgen 4 3>; -+ }; -+ -+ i2c2: i2c@2020000 { -+ compatible = "fsl,vf610-i2c"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0x0 0x2020000 0x0 0x10000>; -+ interrupts = <0 35 0x4>; /* Level high type */ -+ clock-names = "i2c"; -+ clocks = <&clockgen 4 3>; -+ }; -+ -+ i2c3: i2c@2030000 { -+ compatible = "fsl,vf610-i2c"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0x0 0x2030000 0x0 0x10000>; -+ interrupts = <0 35 0x4>; /* Level high type */ -+ clock-names = "i2c"; -+ clocks = <&clockgen 4 3>; -+ }; -+ -+ qspi: quadspi@20c0000 { -+ compatible = "fsl,ls2088a-qspi", "fsl,ls2080a-qspi"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0x0 0x20c0000 0x0 0x10000>, -+ <0x0 0x20000000 0x0 0x10000000>; -+ reg-names = "QuadSPI", "QuadSPI-memory"; -+ interrupts = <0 25 0x4>; /* Level high type */ -+ clocks = <&clockgen 4 3>, <&clockgen 4 3>; -+ clock-names = "qspi_en", "qspi"; -+ }; -+ -+ pcie1: pcie@3400000 { -+ compatible = "fsl,ls2088a-pcie", "fsl,ls2080a-pcie", -+ "fsl,ls2085a-pcie", "snps,dw-pcie"; -+ reg = <0x00 0x03400000 0x0 0x00100000 /* controller registers */ -+ 0x20 0x00000000 0x0 0x00001000>; /* configuration space */ -+ reg-names = "regs", "config"; -+ interrupts = <0 108 0x4>; /* Level high type */ -+ interrupt-names = "aer"; -+ #address-cells = <3>; -+ #size-cells = <2>; -+ device_type = "pci"; -+ dma-coherent; -+ fsl,lut_diff; -+ num-lanes = <4>; -+ bus-range = <0x0 0xff>; -+ ranges = <0x81000000 0x0 0x00000000 0x20 0x00010000 0x0 0x00010000 /* downstream I/O */ -+ 0x82000000 0x0 0x40000000 0x20 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */ -+ msi-parent = <&its>; -+ #interrupt-cells = <1>; -+ interrupt-map-mask = <0 0 0 7>; -+ interrupt-map = <0000 0 0 1 &gic 0 0 0 109 4>, -+ <0000 0 0 2 &gic 0 0 0 110 4>, -+ <0000 0 0 3 &gic 0 0 0 111 4>, -+ <0000 0 0 4 &gic 0 0 0 112 4>; -+ }; -+ -+ pcie2: pcie@3500000 { -+ compatible = "fsl,ls2080a-pcie", "fsl,ls2080a-pcie", -+ "fsl,ls2085a-pcie", "snps,dw-pcie"; -+ reg = <0x00 0x03500000 0x0 0x00100000 /* controller registers */ -+ 0x28 0x00000000 0x0 0x00001000>; /* configuration space */ -+ reg-names = "regs", "config"; -+ interrupts = <0 113 0x4>; /* Level high type */ -+ interrupt-names = "aer"; -+ #address-cells = <3>; -+ #size-cells = <2>; -+ device_type = "pci"; -+ dma-coherent; -+ fsl,lut_diff; -+ num-lanes = <4>; -+ bus-range = <0x0 0xff>; -+ ranges = <0x81000000 0x0 0x00000000 0x28 0x00010000 0x0 0x00010000 /* downstream I/O */ -+ 0x82000000 0x0 0x40000000 0x28 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */ -+ msi-parent = <&its>; -+ #interrupt-cells = <1>; -+ interrupt-map-mask = <0 0 0 7>; -+ interrupt-map = <0000 0 0 1 &gic 0 0 0 114 4>, -+ <0000 0 0 2 &gic 0 0 0 115 4>, -+ <0000 0 0 3 &gic 0 0 0 116 4>, -+ <0000 0 0 4 &gic 0 0 0 117 4>; -+ }; -+ -+ pcie3: pcie@3600000 { -+ compatible = "fsl,ls2088a-pcie", "fsl,ls2080a-pcie", -+ "fsl,ls2085a-pcie", "snps,dw-pcie"; -+ reg = <0x00 0x03600000 0x0 0x00100000 /* controller registers */ -+ 0x30 0x00000000 0x0 0x00001000>; /* configuration space */ -+ reg-names = "regs", "config"; -+ interrupts = <0 118 0x4>; /* Level high type */ -+ interrupt-names = "aer"; -+ #address-cells = <3>; -+ #size-cells = <2>; -+ device_type = "pci"; -+ dma-coherent; -+ fsl,lut_diff; -+ num-lanes = <8>; -+ bus-range = <0x0 0xff>; -+ ranges = <0x81000000 0x0 0x00000000 0x30 0x00010000 0x0 0x00010000 /* downstream I/O */ -+ 0x82000000 0x0 0x40000000 0x30 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */ -+ msi-parent = <&its>; -+ #interrupt-cells = <1>; -+ interrupt-map-mask = <0 0 0 7>; -+ interrupt-map = <0000 0 0 1 &gic 0 0 0 119 4>, -+ <0000 0 0 2 &gic 0 0 0 120 4>, -+ <0000 0 0 3 &gic 0 0 0 121 4>, -+ <0000 0 0 4 &gic 0 0 0 122 4>; -+ }; -+ -+ pcie4: pcie@3700000 { -+ compatible = "fsl,ls2080a-pcie", "fsl,ls2080a-pcie", -+ "fsl,ls2085a-pcie", "snps,dw-pcie"; -+ reg = <0x00 0x03700000 0x0 0x00100000 /* controller registers */ -+ 0x38 0x00000000 0x0 0x00001000>; /* configuration space */ -+ reg-names = "regs", "config"; -+ interrupts = <0 123 0x4>; /* Level high type */ -+ interrupt-names = "aer"; -+ #address-cells = <3>; -+ #size-cells = <2>; -+ device_type = "pci"; -+ dma-coherent; -+ fsl,lut_diff; -+ num-lanes = <4>; -+ bus-range = <0x0 0xff>; -+ ranges = <0x81000000 0x0 0x00000000 0x38 0x00010000 0x0 0x00010000 /* downstream I/O */ -+ 0x82000000 0x0 0x40000000 0x38 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */ -+ msi-parent = <&its>; -+ #interrupt-cells = <1>; -+ interrupt-map-mask = <0 0 0 7>; -+ interrupt-map = <0000 0 0 1 &gic 0 0 0 124 4>, -+ <0000 0 0 2 &gic 0 0 0 125 4>, -+ <0000 0 0 3 &gic 0 0 0 126 4>, -+ <0000 0 0 4 &gic 0 0 0 127 4>; -+ }; -+ -+ sata0: sata@3200000 { -+ status = "disabled"; -+ compatible = "fsl,ls2088a-ahci", "fsl,ls2080a-ahci"; -+ reg = <0x0 0x3200000 0x0 0x10000>; -+ interrupts = <0 133 0x4>; /* Level high type */ -+ clocks = <&clockgen 4 3>; -+ }; -+ -+ sata1: sata@3210000 { -+ status = "disabled"; -+ compatible = "fsl,ls2088a-ahci", "fsl,ls2080a-ahci"; -+ reg = <0x0 0x3210000 0x0 0x10000>; -+ interrupts = <0 136 0x4>; /* Level high type */ -+ clocks = <&clockgen 4 3>; -+ }; -+ -+ usb0: usb3@3100000 { -+ status = "disabled"; -+ compatible = "snps,dwc3"; -+ reg = <0x0 0x3100000 0x0 0x10000>; -+ interrupts = <0 80 0x4>; /* Level high type */ -+ dr_mode = "host"; -+ configure-gfladj; -+ snps,dis_rxdet_inp3_quirk; -+ }; -+ -+ usb1: usb3@3110000 { -+ status = "disabled"; -+ compatible = "snps,dwc3"; -+ reg = <0x0 0x3110000 0x0 0x10000>; -+ interrupts = <0 81 0x4>; /* Level high type */ -+ dr_mode = "host"; -+ configure-gfladj; -+ snps,dis_rxdet_inp3_quirk; -+ }; -+ -+ smmu: iommu@5000000 { -+ compatible = "arm,mmu-500"; -+ reg = <0 0x5000000 0 0x800000>; -+ #global-interrupts = <12>; -+ interrupts = <0 13 4>, /* global secure fault */ -+ <0 14 4>, /* combined secure interrupt */ -+ <0 15 4>, /* global non-secure fault */ -+ <0 16 4>, /* combined non-secure interrupt */ -+ /* performance counter interrupts 0-7 */ -+ <0 211 4>, -+ <0 212 4>, -+ <0 213 4>, -+ <0 214 4>, -+ <0 215 4>, -+ <0 216 4>, -+ <0 217 4>, -+ <0 218 4>, -+ /* per context interrupt, 64 interrupts */ -+ <0 146 4>, -+ <0 147 4>, -+ <0 148 4>, -+ <0 149 4>, -+ <0 150 4>, -+ <0 151 4>, -+ <0 152 4>, -+ <0 153 4>, -+ <0 154 4>, -+ <0 155 4>, -+ <0 156 4>, -+ <0 157 4>, -+ <0 158 4>, -+ <0 159 4>, -+ <0 160 4>, -+ <0 161 4>, -+ <0 162 4>, -+ <0 163 4>, -+ <0 164 4>, -+ <0 165 4>, -+ <0 166 4>, -+ <0 167 4>, -+ <0 168 4>, -+ <0 169 4>, -+ <0 170 4>, -+ <0 171 4>, -+ <0 172 4>, -+ <0 173 4>, -+ <0 174 4>, -+ <0 175 4>, -+ <0 176 4>, -+ <0 177 4>, -+ <0 178 4>, -+ <0 179 4>, -+ <0 180 4>, -+ <0 181 4>, -+ <0 182 4>, -+ <0 183 4>, -+ <0 184 4>, -+ <0 185 4>, -+ <0 186 4>, -+ <0 187 4>, -+ <0 188 4>, -+ <0 189 4>, -+ <0 190 4>, -+ <0 191 4>, -+ <0 192 4>, -+ <0 193 4>, -+ <0 194 4>, -+ <0 195 4>, -+ <0 196 4>, -+ <0 197 4>, -+ <0 198 4>, -+ <0 199 4>, -+ <0 200 4>, -+ <0 201 4>, -+ <0 202 4>, -+ <0 203 4>, -+ <0 204 4>, -+ <0 205 4>, -+ <0 206 4>, -+ <0 207 4>, -+ <0 208 4>, -+ <0 209 4>; -+ mmu-masters = <&fsl_mc 0x300 0>; -+ }; -+ -+ timer { -+ compatible = "arm,armv8-timer"; -+ interrupts = <1 13 0x1>, /* Physical Secure PPI, edge triggered */ -+ <1 14 0x1>, /* Physical Non-Secure PPI, edge triggered */ -+ <1 11 0x1>, /* Virtual PPI, edge triggered */ -+ <1 10 0x1>; /* Hypervisor PPI, edge triggered */ -+ arm,reread-timer; -+ fsl,erratum-a008585; -+ }; -+ -+ fsl_mc: fsl-mc@80c000000 { -+ compatible = "fsl,qoriq-mc"; -+ #stream-id-cells = <2>; -+ reg = <0x00000008 0x0c000000 0 0x40>, /* MC portal base */ -+ <0x00000000 0x08340000 0 0x40000>; /* MC control reg */ -+ msi-parent = <&its>; -+ #address-cells = <3>; -+ #size-cells = <1>; -+ -+ /* -+ * Region type 0x0 - MC portals -+ * Region type 0x1 - QBMAN portals -+ */ -+ ranges = <0x0 0x0 0x0 0x8 0x0c000000 0x4000000 -+ 0x1 0x0 0x0 0x8 0x18000000 0x8000000>; -+ -+ /* -+ * Define the maximum number of MACs present on the SoC. -+ * They won't necessarily be all probed, since the -+ * Data Path Layout file and the MC firmware can put fewer -+ * actual DPMAC objects on the MC bus. -+ */ -+ dpmacs { -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ dpmac1: dpmac@1 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <1>; -+ }; -+ dpmac2: dpmac@2 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <2>; -+ }; -+ dpmac3: dpmac@3 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <3>; -+ }; -+ dpmac4: dpmac@4 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <4>; -+ }; -+ dpmac5: dpmac@5 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <5>; -+ }; -+ dpmac6: dpmac@6 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <6>; -+ }; -+ dpmac7: dpmac@7 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <7>; -+ }; -+ dpmac8: dpmac@8 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <8>; -+ }; -+ dpmac9: dpmac@9 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <9>; -+ }; -+ dpmac10: dpmac@10 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <0xa>; -+ }; -+ dpmac11: dpmac@11 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <0xb>; -+ }; -+ dpmac12: dpmac@12 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <0xc>; -+ }; -+ dpmac13: dpmac@13 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <0xd>; -+ }; -+ dpmac14: dpmac@14 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <0xe>; -+ }; -+ dpmac15: dpmac@15 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <0xf>; -+ }; -+ dpmac16: dpmac@16 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <0x10>; -+ }; -+ }; -+ }; -+ -+ ccn@4000000 { -+ compatible = "arm,ccn-504"; -+ reg = <0x0 0x04000000 0x0 0x01000000>; -+ interrupts = <0 12 4>; -+ }; -+ -+ memory@80000000 { -+ device_type = "memory"; -+ reg = <0x00000000 0x80000000 0 0x80000000>; -+ /* DRAM space 1 - 2 GB DRAM */ -+ }; -+}; -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/thermal.h b/arch/arm64/boot/dts/thermal.h -new file mode 100644 -index 0000000..59822a9 ---- /dev/null -+++ b/arch/arm64/boot/dts/thermal.h -@@ -0,0 +1,17 @@ -+/* -+ * This header provides constants for most thermal bindings. -+ * -+ * Copyright (C) 2013 Texas Instruments -+ * Eduardo Valentin -+ * -+ * GPLv2 only -+ */ -+ -+#ifndef _DT_BINDINGS_THERMAL_THERMAL_H -+#define _DT_BINDINGS_THERMAL_THERMAL_H -+ -+/* On cooling devices upper and lower limits */ -+#define THERMAL_NO_LIMIT (-1UL) -+ -+#endif -+ -diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig -index dd301be..3852a77 100644 ---- a/arch/arm64/configs/defconfig -+++ b/arch/arm64/configs/defconfig -@@ -32,6 +32,7 @@ CONFIG_MODULES=y - CONFIG_MODULE_UNLOAD=y - # CONFIG_BLK_DEV_BSG is not set - # CONFIG_IOSCHED_DEADLINE is not set -+CONFIG_ARCH_LAYERSCAPE=y - CONFIG_ARCH_THUNDER=y - CONFIG_ARCH_VEXPRESS=y - CONFIG_ARCH_XGENE=y -diff --git a/arch/arm64/configs/nxp_ls2088rdb_config b/arch/arm64/configs/nxp_ls2088rdb_config -new file mode 100644 -index 0000000..f1127f9 ---- /dev/null -+++ b/arch/arm64/configs/nxp_ls2088rdb_config -@@ -0,0 +1,3034 @@ -+# -+# Automatically generated file; DO NOT EDIT. -+# Linux/arm64 3.18.25 Kernel Configuration -+# -+CONFIG_ARM64=y -+CONFIG_64BIT=y -+CONFIG_ARCH_PHYS_ADDR_T_64BIT=y -+CONFIG_MMU=y -+CONFIG_STACKTRACE_SUPPORT=y -+CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000 -+CONFIG_LOCKDEP_SUPPORT=y -+CONFIG_TRACE_IRQFLAGS_SUPPORT=y -+CONFIG_RWSEM_XCHGADD_ALGORITHM=y -+CONFIG_GENERIC_HWEIGHT=y -+CONFIG_GENERIC_CSUM=y -+CONFIG_GENERIC_CALIBRATE_DELAY=y -+CONFIG_ZONE_DMA=y -+CONFIG_HAVE_GENERIC_RCU_GUP=y -+CONFIG_ARCH_DMA_ADDR_T_64BIT=y -+CONFIG_NEED_DMA_MAP_STATE=y -+CONFIG_NEED_SG_DMA_LENGTH=y -+CONFIG_SWIOTLB=y -+CONFIG_IOMMU_HELPER=y -+CONFIG_KERNEL_MODE_NEON=y -+CONFIG_FIX_EARLYCON_MEM=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="aarch64-linux-gnu-" -+# CONFIG_COMPILE_TEST is not set -+CONFIG_LOCALVERSION="" -+CONFIG_LOCALVERSION_AUTO=y -+CONFIG_DEFAULT_HOSTNAME="(none)" -+CONFIG_SWAP=y -+CONFIG_SYSVIPC=y -+CONFIG_SYSVIPC_SYSCTL=y -+CONFIG_POSIX_MQUEUE=y -+CONFIG_POSIX_MQUEUE_SYSCTL=y -+CONFIG_CROSS_MEMORY_ATTACH=y -+# CONFIG_FHANDLE is not set -+CONFIG_USELIB=y -+CONFIG_AUDIT=y -+CONFIG_HAVE_ARCH_AUDITSYSCALL=y -+# CONFIG_AUDITSYSCALL 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_IRQ_DOMAIN_HIERARCHY=y -+CONFIG_GENERIC_MSI_IRQ=y -+CONFIG_GENERIC_MSI_IRQ_DOMAIN=y -+CONFIG_HANDLE_DOMAIN_IRQ=y -+# CONFIG_IRQ_DOMAIN_DEBUG is not set -+CONFIG_SPARSE_IRQ=y -+CONFIG_GENERIC_TIME_VSYSCALL=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 is not set -+CONFIG_HIGH_RES_TIMERS=y -+ -+# -+# CPU/Task time and stats accounting -+# -+CONFIG_TICK_CPU_ACCOUNTING=y -+# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set -+CONFIG_BSD_PROCESS_ACCT=y -+CONFIG_BSD_PROCESS_ACCT_V3=y -+CONFIG_TASKSTATS=y -+CONFIG_TASK_DELAY_ACCT=y -+CONFIG_TASK_XACCT=y -+CONFIG_TASK_IO_ACCOUNTING=y -+ -+# -+# RCU Subsystem -+# -+CONFIG_TREE_PREEMPT_RCU=y -+CONFIG_PREEMPT_RCU=y -+# CONFIG_TASKS_RCU is not set -+CONFIG_RCU_STALL_COMMON=y -+# CONFIG_RCU_USER_QS is not set -+CONFIG_RCU_FANOUT=64 -+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_BOOST is not set -+# CONFIG_RCU_NOCB_CPU is not set -+CONFIG_BUILD_BIN2C=y -+CONFIG_IKCONFIG=y -+CONFIG_IKCONFIG_PROC=y -+CONFIG_LOG_BUF_SHIFT=14 -+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=y -+CONFIG_MEMCG=y -+CONFIG_MEMCG_SWAP=y -+CONFIG_MEMCG_SWAP_ENABLED=y -+CONFIG_MEMCG_KMEM=y -+CONFIG_CGROUP_HUGETLB=y -+# CONFIG_CGROUP_PERF 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 is not set -+# CONFIG_IPC_NS is not set -+# CONFIG_USER_NS is not set -+# CONFIG_PID_NS is not set -+CONFIG_NET_NS=y -+CONFIG_SCHED_AUTOGROUP=y -+# 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_SYSCTL_EXCEPTION_TRACE=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=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_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=y -+# CONFIG_DEBUG_PERF_USE_VMALLOC is not set -+CONFIG_VM_EVENT_COUNTERS=y -+# CONFIG_COMPAT_BRK is not set -+CONFIG_SLAB=y -+# CONFIG_SLUB is not set -+# CONFIG_SYSTEM_TRUSTED_KEYRING is not set -+CONFIG_PROFILING=y -+CONFIG_JUMP_LABEL=y -+# CONFIG_UPROBES is not set -+# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set -+CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y -+CONFIG_HAVE_ARCH_TRACEHOOK=y -+CONFIG_HAVE_DMA_ATTRS=y -+CONFIG_HAVE_DMA_CONTIGUOUS=y -+CONFIG_GENERIC_SMP_IDLE_THREAD=y -+CONFIG_HAVE_CLK=y -+CONFIG_HAVE_DMA_API_DEBUG=y -+CONFIG_HAVE_HW_BREAKPOINT=y -+CONFIG_HAVE_PERF_REGS=y -+CONFIG_HAVE_PERF_USER_STACK_DUMP=y -+CONFIG_HAVE_ARCH_JUMP_LABEL=y -+CONFIG_HAVE_RCU_TABLE_FREE=y -+CONFIG_ARCH_WANT_COMPAT_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_ARCH_TRANSPARENT_HUGEPAGE=y -+CONFIG_MODULES_USE_ELF_RELA=y -+CONFIG_CLONE_BACKWARDS=y -+CONFIG_OLD_SIGSUSPEND3=y -+CONFIG_COMPAT_OLD_SIGACTION=y -+ -+# -+# GCOV-based kernel profiling -+# -+# CONFIG_GCOV_KERNEL is not set -+CONFIG_HAVE_GENERIC_DMA_COHERENT=y -+CONFIG_SLABINFO=y -+CONFIG_RT_MUTEXES=y -+CONFIG_BASE_SMALL=0 -+CONFIG_MODULES=y -+CONFIG_MODULE_FORCE_LOAD=y -+CONFIG_MODULE_UNLOAD=y -+# CONFIG_MODULE_FORCE_UNLOAD is not set -+CONFIG_MODVERSIONS=y -+# 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_BLK_DEV_BSG is not set -+# CONFIG_BLK_DEV_BSGLIB is not set -+# CONFIG_BLK_DEV_INTEGRITY is not set -+# CONFIG_BLK_CMDLINE_PARSER is not set -+ -+# -+# 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 is not set -+CONFIG_BLOCK_COMPAT=y -+ -+# -+# IO Schedulers -+# -+CONFIG_IOSCHED_NOOP=y -+# CONFIG_IOSCHED_DEADLINE is not set -+CONFIG_IOSCHED_CFQ=y -+CONFIG_DEFAULT_CFQ=y -+# CONFIG_DEFAULT_NOOP is not set -+CONFIG_DEFAULT_IOSCHED="cfq" -+CONFIG_PREEMPT_NOTIFIERS=y -+CONFIG_UNINLINE_SPIN_UNLOCK=y -+CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y -+CONFIG_MUTEX_SPIN_ON_OWNER=y -+CONFIG_RWSEM_SPIN_ON_OWNER=y -+CONFIG_FREEZER=y -+ -+# -+# Platform selection -+# -+CONFIG_ARCH_THUNDER=y -+CONFIG_ARCH_VEXPRESS=y -+CONFIG_ARCH_XGENE=y -+CONFIG_ARCH_LAYERSCAPE=y -+ -+# -+# Bus support -+# -+CONFIG_ARM_AMBA=y -+CONFIG_PCI=y -+CONFIG_PCI_DOMAINS=y -+CONFIG_PCI_DOMAINS_GENERIC=y -+CONFIG_PCI_SYSCALL=y -+CONFIG_PCI_MSI=y -+CONFIG_PCI_MSI_IRQ_DOMAIN=y -+# 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_PCIE_DW=y -+# CONFIG_PCI_HOST_GENERIC is not set -+CONFIG_PCI_XGENE=y -+CONFIG_PCI_XGENE_MSI=y -+CONFIG_PCI_LAYERSCAPE=y -+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_HOTPLUG_PCI is not set -+ -+# -+# Kernel Features -+# -+ -+# -+# ARM errata workarounds via the alternatives framework -+# -+CONFIG_ARM64_ERRATUM_826319=y -+CONFIG_ARM64_ERRATUM_827319=y -+CONFIG_ARM64_ERRATUM_824069=y -+CONFIG_ARM64_ERRATUM_819472=y -+CONFIG_ARM64_ERRATUM_832075=y -+CONFIG_ARM64_ERRATUM_845719=y -+CONFIG_ARM64_4K_PAGES=y -+# CONFIG_ARM64_64K_PAGES is not set -+# CONFIG_ARM64_VA_BITS_39 is not set -+CONFIG_ARM64_VA_BITS_48=y -+CONFIG_ARM64_VA_BITS=48 -+CONFIG_ARM64_PGTABLE_LEVELS=4 -+# CONFIG_CPU_BIG_ENDIAN is not set -+CONFIG_SMP=y -+# CONFIG_SCHED_MC is not set -+# CONFIG_SCHED_SMT is not set -+CONFIG_NR_CPUS=64 -+CONFIG_HOTPLUG_CPU=y -+# CONFIG_PREEMPT_NONE is not set -+# CONFIG_PREEMPT_VOLUNTARY is not set -+CONFIG_PREEMPT=y -+CONFIG_PREEMPT_COUNT=y -+CONFIG_HZ=100 -+CONFIG_ARCH_HAS_HOLES_MEMORYMODEL=y -+CONFIG_ARCH_SPARSEMEM_ENABLE=y -+CONFIG_ARCH_SPARSEMEM_DEFAULT=y -+CONFIG_ARCH_SELECT_MEMORY_MODEL=y -+CONFIG_HAVE_ARCH_PFN_VALID=y -+CONFIG_HW_PERF_EVENTS=y -+CONFIG_SYS_SUPPORTS_HUGETLBFS=y -+CONFIG_ARCH_WANT_GENERAL_HUGETLB=y -+CONFIG_ARCH_WANT_HUGE_PMD_SHARE=y -+CONFIG_ARCH_HAS_CACHE_LINE_SIZE=y -+CONFIG_SELECT_MEMORY_MODEL=y -+CONFIG_SPARSEMEM_MANUAL=y -+CONFIG_SPARSEMEM=y -+CONFIG_HAVE_MEMORY_PRESENT=y -+CONFIG_SPARSEMEM_EXTREME=y -+CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y -+CONFIG_SPARSEMEM_VMEMMAP=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_MEMORY_BALLOON=y -+CONFIG_BALLOON_COMPACTION=y -+CONFIG_COMPACTION=y -+CONFIG_MIGRATION=y -+CONFIG_PHYS_ADDR_T_64BIT=y -+CONFIG_ZONE_DMA_FLAG=1 -+CONFIG_BOUNCE=y -+CONFIG_MMU_NOTIFIER=y -+CONFIG_KSM=y -+CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 -+CONFIG_TRANSPARENT_HUGEPAGE=y -+CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y -+# CONFIG_TRANSPARENT_HUGEPAGE_MADVISE is not set -+# 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_GENERIC_EARLY_IOREMAP=y -+# CONFIG_XEN is not set -+CONFIG_FORCE_MAX_ZONEORDER=11 -+ -+# -+# Boot options -+# -+CONFIG_CMDLINE="console=ttyAMA0" -+# CONFIG_CMDLINE_FORCE is not set -+CONFIG_EFI_STUB=y -+CONFIG_EFI=y -+ -+# -+# Userspace binary formats -+# -+CONFIG_BINFMT_ELF=y -+CONFIG_COMPAT_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 -+CONFIG_COMPAT=y -+CONFIG_SYSVIPC_COMPAT=y -+ -+# -+# Power management options -+# -+CONFIG_SUSPEND=y -+CONFIG_SUSPEND_FREEZER=y -+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_PM_CLK=y -+# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set -+CONFIG_CPU_PM=y -+CONFIG_ARCH_SUSPEND_POSSIBLE=y -+CONFIG_ARM64_CPU_SUSPEND=y -+ -+# -+# CPU Power Management -+# -+ -+# -+# CPU Idle -+# -+# CONFIG_CPU_IDLE is not set -+# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set -+ -+# -+# 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=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_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_CONSERVATIVE=y -+# CONFIG_CPUFREQ_DT is not set -+ -+# -+# ARM CPU frequency scaling drivers -+# -+# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set -+CONFIG_ARM64_ERRATUM_843419=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_ALGO=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_XFRM_IPCOMP=y -+# CONFIG_NET_KEY is not set -+CONFIG_INET=y -+CONFIG_IP_MULTICAST=y -+CONFIG_IP_ADVANCED_ROUTER=y -+CONFIG_IP_FIB_TRIE_STATS=y -+CONFIG_IP_MULTIPLE_TABLES=y -+CONFIG_IP_ROUTE_MULTIPATH=y -+# CONFIG_IP_ROUTE_VERBOSE is not set -+CONFIG_IP_PNP=y -+CONFIG_IP_PNP_DHCP=y -+CONFIG_IP_PNP_BOOTP=y -+# 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=y -+# CONFIG_IP_MROUTE_MULTIPLE_TABLES is not set -+# CONFIG_IP_PIMSM_V1 is not set -+CONFIG_IP_PIMSM_V2=y -+# 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=y -+CONFIG_INET_XFRM_MODE_TRANSPORT=y -+CONFIG_INET_XFRM_MODE_TUNNEL=y -+CONFIG_INET_XFRM_MODE_BEET=y -+# CONFIG_INET_LRO is not set -+CONFIG_INET_DIAG=y -+CONFIG_INET_TCP_DIAG=y -+# CONFIG_INET_UDP_DIAG is not set -+CONFIG_TCP_CONG_ADVANCED=y -+CONFIG_TCP_CONG_BIC=y -+CONFIG_TCP_CONG_CUBIC=y -+CONFIG_TCP_CONG_WESTWOOD=y -+CONFIG_TCP_CONG_HTCP=y -+# CONFIG_TCP_CONG_HSTCP is not set -+# CONFIG_TCP_CONG_HYBLA is not set -+# CONFIG_TCP_CONG_VEGAS is not set -+# CONFIG_TCP_CONG_SCALABLE is not set -+# CONFIG_TCP_CONG_LP is not set -+# CONFIG_TCP_CONG_VENO is not set -+# CONFIG_TCP_CONG_YEAH is not set -+# CONFIG_TCP_CONG_ILLINOIS is not set -+# CONFIG_TCP_CONG_DCTCP is not set -+# CONFIG_DEFAULT_BIC is not set -+CONFIG_DEFAULT_CUBIC=y -+# CONFIG_DEFAULT_HTCP is not set -+# CONFIG_DEFAULT_WESTWOOD is not set -+# CONFIG_DEFAULT_RENO is not set -+CONFIG_DEFAULT_TCP_CONG="cubic" -+# CONFIG_TCP_MD5SIG is not set -+CONFIG_IPV6=y -+CONFIG_IPV6_ROUTER_PREF=y -+CONFIG_IPV6_ROUTE_INFO=y -+CONFIG_IPV6_OPTIMISTIC_DAD=y -+CONFIG_INET6_AH=y -+CONFIG_INET6_ESP=y -+CONFIG_INET6_IPCOMP=y -+CONFIG_IPV6_MIP6=y -+CONFIG_INET6_XFRM_TUNNEL=y -+CONFIG_INET6_TUNNEL=y -+CONFIG_INET6_XFRM_MODE_TRANSPORT=y -+CONFIG_INET6_XFRM_MODE_TUNNEL=y -+CONFIG_INET6_XFRM_MODE_BEET=y -+CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION=y -+# CONFIG_IPV6_VTI is not set -+CONFIG_IPV6_SIT=y -+# CONFIG_IPV6_SIT_6RD is not set -+CONFIG_IPV6_NDISC_NODETYPE=y -+CONFIG_IPV6_TUNNEL=y -+# CONFIG_IPV6_GRE is not set -+CONFIG_IPV6_MULTIPLE_TABLES=y -+CONFIG_IPV6_SUBTREES=y -+# CONFIG_IPV6_MROUTE is not set -+# CONFIG_NETLABEL is not set -+# CONFIG_NETWORK_SECMARK is not set -+CONFIG_NET_PTP_CLASSIFY=y -+# 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_STP=m -+CONFIG_BRIDGE=m -+CONFIG_BRIDGE_IGMP_SNOOPING=y -+# CONFIG_BRIDGE_VLAN_FILTERING is not set -+CONFIG_HAVE_NET_DSA=y -+CONFIG_VLAN_8021Q=y -+# CONFIG_VLAN_8021Q_GVRP is not set -+# CONFIG_VLAN_8021Q_MVRP is not set -+# CONFIG_DECNET is not set -+CONFIG_LLC=m -+# 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=y -+ -+# -+# Queueing/Scheduling -+# -+# CONFIG_NET_SCH_CBQ is not set -+# CONFIG_NET_SCH_HTB is not set -+# CONFIG_NET_SCH_HFSC is not set -+# CONFIG_NET_SCH_PRIO is not set -+# CONFIG_NET_SCH_MULTIQ is not set -+# CONFIG_NET_SCH_RED is not set -+# CONFIG_NET_SCH_SFB is not set -+# CONFIG_NET_SCH_SFQ is not set -+# CONFIG_NET_SCH_TEQL is not set -+# CONFIG_NET_SCH_TBF is not set -+# CONFIG_NET_SCH_GRED is not set -+# CONFIG_NET_SCH_DSMARK is not set -+# CONFIG_NET_SCH_NETEM is not set -+# CONFIG_NET_SCH_DRR is not set -+# CONFIG_NET_SCH_MQPRIO is not set -+# CONFIG_NET_SCH_CHOKE is not set -+# CONFIG_NET_SCH_QFQ is not set -+# CONFIG_NET_SCH_CODEL is not set -+# CONFIG_NET_SCH_FQ_CODEL is not set -+# CONFIG_NET_SCH_FQ is not set -+# CONFIG_NET_SCH_HHF is not set -+# CONFIG_NET_SCH_PIE is not set -+# CONFIG_NET_SCH_PLUG is not set -+ -+# -+# Classification -+# -+# CONFIG_NET_CLS_BASIC is not set -+# CONFIG_NET_CLS_TCINDEX is not set -+# CONFIG_NET_CLS_ROUTE4 is not set -+# CONFIG_NET_CLS_FW is not set -+# CONFIG_NET_CLS_U32 is not set -+# CONFIG_NET_CLS_RSVP is not set -+# CONFIG_NET_CLS_RSVP6 is not set -+# CONFIG_NET_CLS_FLOW is not set -+# CONFIG_NET_CLS_CGROUP is not set -+# CONFIG_NET_CLS_BPF is not set -+# CONFIG_NET_EMATCH is not set -+# CONFIG_NET_CLS_ACT is not set -+CONFIG_NET_SCH_FIFO=y -+CONFIG_DCB=y -+CONFIG_DNS_RESOLVER=y -+# 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=y -+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_FIB_RULES=y -+# CONFIG_WIRELESS is not set -+# CONFIG_WIMAX is not set -+# CONFIG_RFKILL is not set -+# CONFIG_RFKILL_REGULATOR is not set -+CONFIG_NET_9P=y -+CONFIG_NET_9P_VIRTIO=y -+# CONFIG_NET_9P_DEBUG 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=y -+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" -+CONFIG_DEVTMPFS=y -+CONFIG_DEVTMPFS_MOUNT=y -+CONFIG_STANDALONE=y -+CONFIG_PREVENT_FIRMWARE_BUILD=y -+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_GENERIC_CPU_AUTOPROBE=y -+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_ARM_CCN is not set -+CONFIG_VEXPRESS_CONFIG=y -+# 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_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=y -+# 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 -+ -+# -+# RAM/ROM/Flash chip drivers -+# -+CONFIG_MTD_CFI=y -+# CONFIG_MTD_JEDECPROBE is not set -+CONFIG_MTD_GEN_PROBE=y -+CONFIG_MTD_CFI_ADV_OPTIONS=y -+CONFIG_MTD_CFI_NOSWAP=y -+# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set -+# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set -+# CONFIG_MTD_CFI_GEOMETRY 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_OTP is not set -+CONFIG_MTD_CFI_INTELEXT=y -+CONFIG_MTD_CFI_AMDSTD=y -+CONFIG_MTD_CFI_STAA=y -+CONFIG_MTD_CFI_UTIL=y -+CONFIG_MTD_RAM=y -+# 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_PHYSMAP=y -+# CONFIG_MTD_PHYSMAP_COMPAT is not set -+CONFIG_MTD_PHYSMAP_OF=y -+# CONFIG_MTD_INTEL_VR_NOR is not set -+CONFIG_MTD_PLATRAM=y -+ -+# -+# Self-contained MTD device drivers -+# -+# CONFIG_MTD_PMC551 is not set -+# CONFIG_MTD_DATAFLASH is not set -+CONFIG_MTD_M25P80=y -+# 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_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=y -+# CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set -+CONFIG_MTD_NAND_IDS=y -+# 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_MTD_NAND_FSL_IFC=y -+# CONFIG_MTD_ONENAND is not set -+ -+# -+# LPDDR & LPDDR2 PCM memory drivers -+# -+# CONFIG_MTD_LPDDR is not set -+CONFIG_MTD_SPI_NOR=y -+CONFIG_MTD_SPI_NOR_USE_4K_SECTORS=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_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=y -+CONFIG_BLK_DEV_LOOP_MIN_COUNT=8 -+# CONFIG_BLK_DEV_CRYPTOLOOP 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_SKD 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=262144 -+# CONFIG_BLK_DEV_XIP is not set -+# CONFIG_CDROM_PKTCDVD is not set -+# CONFIG_ATA_OVER_ETH is not set -+CONFIG_VIRTIO_BLK=y -+# 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_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_VEXPRESS_SYSCFG=y -+# CONFIG_C2PORT is not set -+ -+# -+# EEPROM support -+# -+CONFIG_EEPROM_AT24=y -+CONFIG_EEPROM_AT25=y -+# 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_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_GENWQE is not set -+# CONFIG_ECHO is not set -+# CONFIG_CXL_BASE 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_LOWLEVEL_PCMCIA is not set -+# CONFIG_SCSI_DH is not set -+# CONFIG_SCSI_OSD_INITIATOR is not set -+CONFIG_HAVE_PATA_PLATFORM=y -+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=y -+CONFIG_SATA_AHCI_PLATFORM=y -+CONFIG_AHCI_XGENE=y -+# CONFIG_SATA_INIC162X is not set -+# CONFIG_SATA_ACARD_AHCI is not set -+# CONFIG_SATA_SIL24 is not set -+CONFIG_ATA_SFF=y -+ -+# -+# SFF controllers with custom DMA interface -+# -+# CONFIG_PDC_ADMA is not set -+# CONFIG_SATA_QSTOR is not set -+# CONFIG_SATA_SX4 is not set -+CONFIG_ATA_BMDMA=y -+ -+# -+# SATA SFF controllers with BMDMA -+# -+# CONFIG_ATA_PIIX is not set -+# CONFIG_SATA_MV is not set -+# CONFIG_SATA_NV is not set -+# CONFIG_SATA_PROMISE is not set -+# CONFIG_SATA_SIL is not set -+# CONFIG_SATA_SIS is not set -+# CONFIG_SATA_SVW is not set -+# CONFIG_SATA_ULI is not set -+# CONFIG_SATA_VIA is not set -+# CONFIG_SATA_VITESSE is not set -+ -+# -+# PATA SFF controllers with BMDMA -+# -+# CONFIG_PATA_ALI is not set -+# CONFIG_PATA_AMD is not set -+# CONFIG_PATA_ARTOP is not set -+# CONFIG_PATA_ATIIXP is not set -+# CONFIG_PATA_ATP867X is not set -+# CONFIG_PATA_CMD64X is not set -+# CONFIG_PATA_CYPRESS is not set -+# CONFIG_PATA_EFAR is not set -+# CONFIG_PATA_HPT366 is not set -+# CONFIG_PATA_HPT37X is not set -+# CONFIG_PATA_HPT3X2N is not set -+# CONFIG_PATA_HPT3X3 is not set -+# CONFIG_PATA_IT8213 is not set -+# CONFIG_PATA_IT821X is not set -+# CONFIG_PATA_JMICRON is not set -+# CONFIG_PATA_MARVELL is not set -+# CONFIG_PATA_NETCELL is not set -+# CONFIG_PATA_NINJA32 is not set -+# CONFIG_PATA_NS87415 is not set -+# CONFIG_PATA_OLDPIIX is not set -+# CONFIG_PATA_OPTIDMA is not set -+# CONFIG_PATA_PDC2027X is not set -+# CONFIG_PATA_PDC_OLD is not set -+# CONFIG_PATA_RADISYS is not set -+# CONFIG_PATA_RDC is not set -+# CONFIG_PATA_SCH is not set -+# CONFIG_PATA_SERVERWORKS is not set -+# CONFIG_PATA_SIL680 is not set -+# CONFIG_PATA_SIS is not set -+# CONFIG_PATA_TOSHIBA is not set -+# CONFIG_PATA_TRIFLEX is not set -+# CONFIG_PATA_VIA is not set -+# CONFIG_PATA_WINBOND is not set -+ -+# -+# PIO-only SFF controllers -+# -+# CONFIG_PATA_CMD640_PCI is not set -+# CONFIG_PATA_MPIIX is not set -+# CONFIG_PATA_NS87410 is not set -+# CONFIG_PATA_OPTI is not set -+# CONFIG_PATA_PLATFORM is not set -+# CONFIG_PATA_RZ1000 is not set -+ -+# -+# Generic fallback / legacy drivers -+# -+# CONFIG_ATA_GENERIC is not set -+# CONFIG_PATA_LEGACY 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_MII=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=y -+# CONFIG_MACVTAP 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=y -+# CONFIG_VETH is not set -+CONFIG_VIRTIO_NET=y -+# 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=y -+# CONFIG_VORTEX is not set -+# CONFIG_TYPHOON is not set -+CONFIG_NET_VENDOR_ADAPTEC=y -+# CONFIG_ADAPTEC_STARFIRE is not set -+CONFIG_NET_VENDOR_AGERE=y -+# CONFIG_ET131X is not set -+CONFIG_NET_VENDOR_ALTEON=y -+# CONFIG_ACENIC is not set -+# CONFIG_ALTERA_TSE is not set -+CONFIG_NET_VENDOR_AMD=y -+# CONFIG_AMD8111_ETH is not set -+# CONFIG_PCNET32 is not set -+# CONFIG_AMD_XGBE is not set -+CONFIG_NET_XGENE=y -+CONFIG_NET_VENDOR_ARC=y -+# CONFIG_ARC_EMAC is not set -+# CONFIG_EMAC_ROCKCHIP is not set -+CONFIG_NET_VENDOR_ATHEROS=y -+# CONFIG_ATL2 is not set -+# CONFIG_ATL1 is not set -+# CONFIG_ATL1E is not set -+# CONFIG_ATL1C is not set -+# CONFIG_ALX is not set -+CONFIG_NET_VENDOR_BROADCOM=y -+# CONFIG_B44 is not set -+# CONFIG_BCMGENET is not set -+# CONFIG_BNX2 is not set -+# CONFIG_CNIC is not set -+# CONFIG_TIGON3 is not set -+# CONFIG_BNX2X is not set -+# CONFIG_SYSTEMPORT is not set -+CONFIG_NET_VENDOR_BROCADE=y -+# CONFIG_BNA is not set -+CONFIG_NET_VENDOR_CHELSIO=y -+# CONFIG_CHELSIO_T1 is not set -+# CONFIG_CHELSIO_T3 is not set -+# CONFIG_CHELSIO_T4 is not set -+# CONFIG_CHELSIO_T4VF is not set -+CONFIG_NET_VENDOR_CISCO=y -+# CONFIG_ENIC is not set -+# CONFIG_DNET is not set -+CONFIG_NET_VENDOR_DEC=y -+# CONFIG_NET_TULIP is not set -+CONFIG_NET_VENDOR_DLINK=y -+# CONFIG_DL2K is not set -+# CONFIG_SUNDANCE is not set -+CONFIG_NET_VENDOR_EMULEX=y -+# CONFIG_BE2NET is not set -+CONFIG_NET_VENDOR_EXAR=y -+# CONFIG_S2IO is not set -+# CONFIG_VXGE is not set -+CONFIG_NET_VENDOR_FREESCALE=y -+# CONFIG_FSL_PQ_MDIO is not set -+CONFIG_FSL_XGMAC_MDIO=y -+CONFIG_NET_VENDOR_HP=y -+# CONFIG_HP100 is not set -+CONFIG_NET_VENDOR_INTEL=y -+# CONFIG_E100 is not set -+CONFIG_E1000=y -+CONFIG_E1000E=y -+# CONFIG_IGB is not set -+# CONFIG_IGBVF is not set -+# CONFIG_IXGB is not set -+# CONFIG_IXGBE is not set -+# CONFIG_IXGBEVF is not set -+# CONFIG_I40E is not set -+# CONFIG_I40EVF is not set -+# CONFIG_FM10K is not set -+CONFIG_NET_VENDOR_I825XX=y -+# CONFIG_IP1000 is not set -+# CONFIG_JME is not set -+CONFIG_NET_VENDOR_MARVELL=y -+# CONFIG_MVMDIO is not set -+# CONFIG_SKGE is not set -+# CONFIG_SKY2 is not set -+CONFIG_NET_VENDOR_MELLANOX=y -+# CONFIG_MLX4_EN is not set -+# CONFIG_MLX4_CORE is not set -+# CONFIG_MLX5_CORE is not set -+CONFIG_NET_VENDOR_MICREL=y -+# CONFIG_KS8842 is not set -+# CONFIG_KS8851 is not set -+# CONFIG_KS8851_MLL is not set -+# CONFIG_KSZ884X_PCI is not set -+CONFIG_NET_VENDOR_MICROCHIP=y -+# CONFIG_ENC28J60 is not set -+CONFIG_NET_VENDOR_MYRI=y -+# CONFIG_MYRI10GE is not set -+# CONFIG_FEALNX is not set -+CONFIG_NET_VENDOR_NATSEMI=y -+# CONFIG_NATSEMI is not set -+# CONFIG_NS83820 is not set -+CONFIG_NET_VENDOR_8390=y -+# CONFIG_NE2K_PCI is not set -+CONFIG_NET_VENDOR_NVIDIA=y -+# CONFIG_FORCEDETH is not set -+CONFIG_NET_VENDOR_OKI=y -+# CONFIG_ETHOC is not set -+CONFIG_NET_PACKET_ENGINE=y -+# CONFIG_HAMACHI is not set -+# CONFIG_YELLOWFIN is not set -+CONFIG_NET_VENDOR_QLOGIC=y -+# CONFIG_QLA3XXX is not set -+# CONFIG_QLCNIC is not set -+# CONFIG_QLGE is not set -+# CONFIG_NETXEN_NIC is not set -+CONFIG_NET_VENDOR_QUALCOMM=y -+# CONFIG_QCA7000 is not set -+CONFIG_NET_VENDOR_REALTEK=y -+# CONFIG_8139CP is not set -+# CONFIG_8139TOO is not set -+# CONFIG_R8169 is not set -+CONFIG_NET_VENDOR_RDC=y -+# CONFIG_R6040 is not set -+CONFIG_NET_VENDOR_SAMSUNG=y -+# CONFIG_SXGBE_ETH is not set -+CONFIG_NET_VENDOR_SEEQ=y -+CONFIG_NET_VENDOR_SILAN=y -+# CONFIG_SC92031 is not set -+CONFIG_NET_VENDOR_SIS=y -+# CONFIG_SIS900 is not set -+# CONFIG_SIS190 is not set -+# CONFIG_SFC is not set -+CONFIG_NET_VENDOR_SMSC=y -+CONFIG_SMC91X=y -+# CONFIG_EPIC100 is not set -+CONFIG_SMSC911X=y -+# CONFIG_SMSC911X_ARCH_HOOKS is not set -+# CONFIG_SMSC9420 is not set -+CONFIG_NET_VENDOR_STMICRO=y -+# CONFIG_STMMAC_ETH is not set -+CONFIG_NET_VENDOR_SUN=y -+# CONFIG_HAPPYMEAL is not set -+# CONFIG_SUNGEM is not set -+# CONFIG_CASSINI is not set -+# CONFIG_NIU is not set -+CONFIG_NET_VENDOR_TEHUTI=y -+# CONFIG_TEHUTI is not set -+CONFIG_NET_VENDOR_TI=y -+# CONFIG_TLAN is not set -+CONFIG_NET_VENDOR_VIA=y -+# CONFIG_VIA_RHINE is not set -+# CONFIG_VIA_VELOCITY is not set -+CONFIG_NET_VENDOR_WIZNET=y -+# CONFIG_WIZNET_W5100 is not set -+# CONFIG_WIZNET_W5300 is not set -+# CONFIG_FDDI is not set -+# CONFIG_HIPPI is not set -+CONFIG_PHYLIB=y -+ -+# -+# MII PHY device drivers -+# -+CONFIG_AQUANTIA_PHY=y -+# 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=y -+# CONFIG_TERANETICS_PHY is not set -+CONFIG_SMSC_PHY=y -+CONFIG_BROADCOM_PHY=y -+# CONFIG_BCM7XXX_PHY is not set -+# CONFIG_BCM87XX_PHY is not set -+# CONFIG_ICPLUS_PHY is not set -+CONFIG_REALTEK_PHY=y -+# 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=y -+# CONFIG_MDIO_BITBANG is not set -+CONFIG_MDIO_BUS_MUX=y -+# CONFIG_MDIO_BUS_MUX_GPIO is not set -+CONFIG_MDIO_BUS_MUX_MMIOREG=y -+# CONFIG_FSL_10GBASE_KR is not set -+# CONFIG_MDIO_BCM_UNIMAC 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_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 is not set -+CONFIG_INPUT_EVDEV=y -+# CONFIG_INPUT_EVBUG 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 is not set -+CONFIG_SERIO_AMBAKMI=y -+# 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=y -+CONFIG_UNIX98_PTYS=y -+# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set -+CONFIG_LEGACY_PTYS=y -+CONFIG_LEGACY_PTY_COUNT=16 -+# CONFIG_SERIAL_NONSTANDARD is not set -+# CONFIG_NOZOMI is not set -+# CONFIG_N_GSM is not set -+# CONFIG_TRACE_SINK is not set -+CONFIG_DEVKMEM=y -+ -+# -+# Serial drivers -+# -+CONFIG_SERIAL_EARLYCON=y -+CONFIG_SERIAL_8250=y -+CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y -+CONFIG_SERIAL_8250_CONSOLE=y -+CONFIG_SERIAL_8250_DMA=y -+CONFIG_SERIAL_8250_PCI=y -+CONFIG_SERIAL_8250_NR_UARTS=4 -+CONFIG_SERIAL_8250_RUNTIME_UARTS=4 -+# CONFIG_SERIAL_8250_EXTENDED is not set -+# CONFIG_SERIAL_8250_DW 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_OF_PLATFORM=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_RP2 is not set -+# CONFIG_SERIAL_FSL_LPUART is not set -+CONFIG_HVC_DRIVER=y -+CONFIG_VIRTIO_CONSOLE=y -+# CONFIG_IPMI_HANDLER is not set -+CONFIG_HW_RANDOM=y -+# CONFIG_HW_RANDOM_TIMERIOMEM is not set -+# CONFIG_HW_RANDOM_VIRTIO is not set -+CONFIG_HW_RANDOM_XGENE=y -+# CONFIG_R3964 is not set -+# CONFIG_APPLICOM is not set -+ -+# -+# PCMCIA character devices -+# -+# CONFIG_RAW_DRIVER is not set -+# CONFIG_TCG_TPM is not set -+CONFIG_DEVPORT=y -+# 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_ARB_GPIO_CHALLENGE is not set -+# CONFIG_I2C_MUX_GPIO is not set -+# CONFIG_I2C_MUX_PCA9541 is not set -+CONFIG_I2C_MUX_PCA954x=y -+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_CBUS_GPIO is not set -+# CONFIG_I2C_DESIGNWARE_PLATFORM is not set -+# CONFIG_I2C_DESIGNWARE_PCI is not set -+# CONFIG_I2C_GPIO is not set -+CONFIG_I2C_IMX=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_VERSATILE 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_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 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 is not set -+# CONFIG_SPI_TLE62X0 is not set -+# CONFIG_SPMI is not set -+# CONFIG_HSI is not set -+ -+# -+# PPS support -+# -+CONFIG_PPS=y -+# CONFIG_PPS_DEBUG is not set -+# CONFIG_NTP_PPS is not set -+ -+# -+# PPS clients support -+# -+# CONFIG_PPS_CLIENT_KTIMER is not set -+# CONFIG_PPS_CLIENT_LDISC is not set -+# CONFIG_PPS_CLIENT_GPIO is not set -+ -+# -+# PPS generators support -+# -+ -+# -+# PTP clock support -+# -+CONFIG_PTP_1588_CLOCK=y -+ -+# -+# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. -+# -+CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y -+CONFIG_ARCH_REQUIRE_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 is not set -+CONFIG_GPIO_GENERIC=y -+ -+# -+# Memory mapped GPIO drivers: -+# -+CONFIG_GPIO_GENERIC_PLATFORM=y -+# CONFIG_GPIO_DWAPB is not set -+CONFIG_GPIO_PL061=y -+# CONFIG_GPIO_SCH311X is not set -+# CONFIG_GPIO_SYSCON is not set -+CONFIG_GPIO_XGENE=y -+# CONFIG_GPIO_VX855 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: -+# -+# CONFIG_GPIO_BT8XX is not set -+# CONFIG_GPIO_AMD8111 is not set -+# CONFIG_GPIO_ML_IOH is not set -+# CONFIG_GPIO_RDC321X is not set -+ -+# -+# 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_MANAGER 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_GPIO is not set -+# CONFIG_POWER_RESET_GPIO_RESTART is not set -+# CONFIG_POWER_RESET_LTC2952 is not set -+CONFIG_POWER_RESET_VEXPRESS=y -+# CONFIG_POWER_RESET_XGENE is not set -+# CONFIG_POWER_RESET_SYSCON is not set -+CONFIG_POWER_RESET_LAYERSCAPE=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_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_HTC_PASIC3 is not set -+# CONFIG_HTC_I2CPLD 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_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_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_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_MFD_VEXPRESS_SYSREG=y -+CONFIG_REGULATOR=y -+# CONFIG_REGULATOR_DEBUG is not set -+CONFIG_REGULATOR_FIXED_VOLTAGE=y -+# 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_REGULATOR_VEXPRESS is not set -+# CONFIG_MEDIA_SUPPORT is not set -+ -+# -+# Graphics support -+# -+CONFIG_VGA_ARB=y -+CONFIG_VGA_ARB_MAX_GPUS=16 -+ -+# -+# 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=y -+CONFIG_FB_CFB_COPYAREA=y -+CONFIG_FB_CFB_IMAGEBLIT=y -+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set -+# CONFIG_FB_SYS_FILLRECT is not set -+# CONFIG_FB_SYS_COPYAREA is not set -+# CONFIG_FB_SYS_IMAGEBLIT is not set -+# CONFIG_FB_FOREIGN_ENDIAN is not set -+# CONFIG_FB_SYS_FOPS is not set -+# CONFIG_FB_SVGALIB is not set -+# CONFIG_FB_MACMODES is not set -+# CONFIG_FB_BACKLIGHT is not set -+CONFIG_FB_MODE_HELPERS=y -+# 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=y -+# 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_FB_SSD1307 is not set -+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set -+# CONFIG_VGASTATE is not set -+CONFIG_VIDEOMODE_HELPERS=y -+ -+# -+# Console display driver support -+# -+CONFIG_DUMMY_CONSOLE=y -+CONFIG_FRAMEBUFFER_CONSOLE=y -+# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set -+# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set -+CONFIG_LOGO=y -+# CONFIG_LOGO_LINUX_MONO is not set -+# CONFIG_LOGO_LINUX_VGA16 is not set -+CONFIG_LOGO_LINUX_CLUT224=y -+# 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_PCI=y -+CONFIG_USB_XHCI_PLATFORM=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=y -+# 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=y -+CONFIG_USB_DWC3_HOST=y -+ -+# -+# Platform Glue Driver Support -+# -+CONFIG_USB_DWC3_PCI=y -+ -+# -+# Debugging features -+# -+# CONFIG_USB_DWC3_DEBUG is not set -+# CONFIG_DWC3_HOST_USB3_LPM_ENABLE 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_NOP_USB_XCEIV is not set -+# CONFIG_USB_GPIO_VBUS is not set -+# CONFIG_USB_ISP1301 is not set -+CONFIG_USB_ULPI=y -+# CONFIG_USB_GADGET is not set -+# CONFIG_UWB is not set -+CONFIG_MMC=y -+# CONFIG_MMC_DEBUG is not set -+# CONFIG_MMC_CLKGATE is not set -+ -+# -+# MMC/SD/SDIO Card Drivers -+# -+CONFIG_MMC_BLOCK=y -+CONFIG_MMC_BLOCK_MINORS=8 -+CONFIG_MMC_BLOCK_BOUNCE=y -+# CONFIG_SDIO_UART is not set -+# CONFIG_MMC_TEST is not set -+ -+# -+# MMC/SD/SDIO Host Controller Drivers -+# -+CONFIG_MMC_ARMMMCI=y -+CONFIG_MMC_SDHCI=y -+CONFIG_MMC_SDHCI_IO_ACCESSORS=y -+# CONFIG_MMC_SDHCI_PCI is not set -+CONFIG_MMC_SDHCI_PLTFM=y -+# CONFIG_MMC_SDHCI_OF_ARASAN is not set -+CONFIG_MMC_SDHCI_OF_ESDHC=y -+# CONFIG_MMC_SDHCI_PXAV3 is not set -+# CONFIG_MMC_SDHCI_PXAV2 is not set -+# CONFIG_MMC_TIFM_SD is not set -+CONFIG_MMC_SPI=y -+# CONFIG_MMC_CB710 is not set -+# CONFIG_MMC_VIA_SDMMC is not set -+# CONFIG_MMC_VUB300 is not set -+# CONFIG_MMC_USHC is not set -+# CONFIG_MMC_USDHI6ROL0 is not set -+# CONFIG_MEMSTICK is not set -+# CONFIG_NEW_LEDS is not set -+# CONFIG_ACCESSIBILITY is not set -+# CONFIG_INFINIBAND 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=y -+# 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_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_EFI=y -+# 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=y -+ -+# -+# HID Sensor RTC drivers -+# -+# CONFIG_RTC_DRV_HID_SENSOR_TIME is not set -+CONFIG_DMADEVICES=y -+# CONFIG_DMADEVICES_DEBUG is not set -+ -+# -+# DMA Devices -+# -+# CONFIG_AMBA_PL08X is not set -+# CONFIG_DW_DMAC_CORE is not set -+# CONFIG_DW_DMAC is not set -+# CONFIG_DW_DMAC_PCI is not set -+# CONFIG_PL330_DMA is not set -+# CONFIG_FSL_EDMA is not set -+CONFIG_DMA_ENGINE=y -+CONFIG_DMA_OF=y -+ -+# -+# DMA Clients -+# -+# CONFIG_ASYNC_TX_DMA is not set -+# CONFIG_DMATEST is not set -+# CONFIG_AUXDISPLAY is not set -+# CONFIG_UIO is not set -+# CONFIG_VFIO_IOMMU_TYPE1 is not set -+CONFIG_VFIO=y -+CONFIG_VFIO_PCI=y -+CONFIG_VFIO_FSL_MC=y -+# CONFIG_VIRT_DRIVERS is not set -+CONFIG_VIRTIO=y -+ -+# -+# Virtio drivers -+# -+CONFIG_VIRTIO_PCI=y -+CONFIG_VIRTIO_BALLOON=y -+CONFIG_VIRTIO_MMIO=y -+# CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES is not set -+ -+# -+# Microsoft Hyper-V guest support -+# -+CONFIG_STAGING=y -+# CONFIG_COMEDI is not set -+# CONFIG_RTS5208 is not set -+# CONFIG_FB_XGI is not set -+# CONFIG_BCM_WIMAX is not set -+# CONFIG_FT1000 is not set -+ -+# -+# Speakup console speech -+# -+# CONFIG_SPEAKUP is not set -+# CONFIG_TOUCHSCREEN_CLEARPAD_TM1217 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_DGNC is not set -+# CONFIG_DGAP is not set -+# CONFIG_GS_FPGABOOT is not set -+CONFIG_FSL_MC_BUS=y -+CONFIG_FSL_MC_RESTOOL=y -+CONFIG_FSL_MC_DPIO=y -+# CONFIG_FSL_QBMAN_DEBUG is not set -+CONFIG_FSL_DPAA2=y -+CONFIG_FSL_DPAA2_ETH=y -+# CONFIG_FSL_DPAA2_ETH_USE_ERR_QUEUE is not set -+CONFIG_FSL_DPAA2_MAC=y -+# CONFIG_FSL_DPAA2_MAC_NETDEVS is not set -+ -+# -+# SOC (System On Chip) specific Drivers -+# -+# CONFIG_SOC_TI is not set -+CONFIG_FSL_SOC_DRIVERS=y -+CONFIG_FSL_GUTS=y -+CONFIG_LS_SOC_DRIVERS=y -+CONFIG_CLKDEV_LOOKUP=y -+CONFIG_HAVE_CLK_PREPARE=y -+CONFIG_COMMON_CLK=y -+ -+# -+# Common Clock Framework -+# -+CONFIG_COMMON_CLK_VERSATILE=y -+CONFIG_CLK_SP810=y -+CONFIG_CLK_VEXPRESS_OSC=y -+# CONFIG_COMMON_CLK_SI5351 is not set -+# CONFIG_COMMON_CLK_SI570 is not set -+CONFIG_CLK_QORIQ=y -+CONFIG_COMMON_CLK_XGENE=y -+# 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=y -+# CONFIG_MAILBOX is not set -+CONFIG_IOMMU_API=y -+CONFIG_IOMMU_SUPPORT=y -+ -+# -+# Generic IOMMU Pagetable Support -+# -+CONFIG_IOMMU_IO_PGTABLE=y -+CONFIG_IOMMU_IO_PGTABLE_LPAE=y -+# CONFIG_IOMMU_IO_PGTABLE_LPAE_SELFTEST is not set -+CONFIG_OF_IOMMU=y -+CONFIG_ARM_SMMU=y -+ -+# -+# 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=y -+CONFIG_FSL_IFC=y -+# CONFIG_IIO is not set -+# CONFIG_VME_BUS is not set -+# CONFIG_PWM is not set -+CONFIG_IRQCHIP=y -+CONFIG_ARM_GIC=y -+CONFIG_ARM_GIC_V2M=y -+CONFIG_ARM_GIC_V3=y -+CONFIG_ARM_GIC_V3_ITS=y -+# 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_XGENE=y -+# CONFIG_POWERCAP is not set -+# CONFIG_MCB is not set -+CONFIG_RAS=y -+# CONFIG_THUNDERBOLT is not set -+ -+# -+# Firmware Drivers -+# -+# CONFIG_FIRMWARE_MEMMAP is not set -+ -+# -+# EFI (Extensible Firmware Interface) Support -+# -+# CONFIG_EFI_VARS is not set -+CONFIG_EFI_PARAMS_FROM_FDT=y -+CONFIG_EFI_RUNTIME_WRAPPERS=y -+CONFIG_EFI_ARMSTUB=y -+ -+# -+# File systems -+# -+CONFIG_DCACHE_WORD_ACCESS=y -+CONFIG_EXT2_FS=y -+# CONFIG_EXT2_FS_XATTR is not set -+# CONFIG_EXT2_FS_XIP is not set -+CONFIG_EXT3_FS=y -+# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set -+# CONFIG_EXT3_FS_XATTR is not set -+CONFIG_EXT4_FS=y -+# CONFIG_EXT4_FS_POSIX_ACL is not set -+# CONFIG_EXT4_FS_SECURITY is not set -+# CONFIG_EXT4_DEBUG is not set -+CONFIG_JBD=y -+# CONFIG_JBD_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_BTRFS_FS is not set -+# CONFIG_NILFS2_FS is not set -+# CONFIG_FS_POSIX_ACL is not set -+CONFIG_FILE_LOCKING=y -+CONFIG_FSNOTIFY=y -+CONFIG_DNOTIFY=y -+CONFIG_INOTIFY_USER=y -+CONFIG_FANOTIFY=y -+CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y -+# CONFIG_QUOTA is not set -+# CONFIG_QUOTACTL is not set -+# CONFIG_AUTOFS4_FS is not set -+CONFIG_FUSE_FS=y -+CONFIG_CUSE=y -+CONFIG_OVERLAY_FS=y -+ -+# -+# 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_KCORE is not set -+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=y -+CONFIG_HUGETLBFS=y -+CONFIG_HUGETLB_PAGE=y -+# CONFIG_CONFIGFS_FS is not set -+CONFIG_MISC_FILESYSTEMS=y -+# CONFIG_ADFS_FS is not set -+# CONFIG_AFFS_FS is not set -+# CONFIG_ECRYPT_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_JFFS2_FS=y -+CONFIG_JFFS2_FS_DEBUG=0 -+CONFIG_JFFS2_FS_WRITEBUFFER=y -+# CONFIG_JFFS2_FS_WBUF_VERIFY is not set -+CONFIG_JFFS2_SUMMARY=y -+# 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 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=y -+CONFIG_SQUASHFS_ZLIB=y -+CONFIG_SQUASHFS_LZO=y -+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 is not set -+# CONFIG_EFIVAR_FS is not set -+# CONFIG_AUFS_FS is not set -+CONFIG_NETWORK_FILESYSTEMS=y -+CONFIG_NFS_FS=y -+CONFIG_NFS_V2=y -+CONFIG_NFS_V3=y -+# CONFIG_NFS_V3_ACL is not set -+CONFIG_NFS_V4=y -+# CONFIG_NFS_SWAP is not set -+# CONFIG_NFS_V4_1 is not set -+CONFIG_ROOT_NFS=y -+# CONFIG_NFS_USE_LEGACY_DNS is not set -+CONFIG_NFS_USE_KERNEL_DNS=y -+# CONFIG_NFSD is not set -+CONFIG_GRACE_PERIOD=y -+CONFIG_LOCKD=y -+CONFIG_LOCKD_V4=y -+CONFIG_NFS_COMMON=y -+CONFIG_SUNRPC=y -+CONFIG_SUNRPC_GSS=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_9P_FS=y -+# CONFIG_9P_FS_POSIX_ACL is not set -+# CONFIG_9P_FS_SECURITY 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=y -+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_HAVE_KVM_IRQCHIP=y -+CONFIG_KVM_MMIO=y -+CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT=y -+CONFIG_VIRTUALIZATION=y -+CONFIG_KVM=y -+CONFIG_KVM_ARM_HOST=y -+CONFIG_KVM_ARM_MAX_VCPUS=8 -+CONFIG_KVM_ARM_VGIC=y -+CONFIG_KVM_ARM_TIMER=y -+ -+# -+# Kernel hacking -+# -+ -+# -+# printk and dmesg options -+# -+CONFIG_PRINTK_TIME=y -+CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 -+# CONFIG_BOOT_PRINTK_DELAY is not set -+# CONFIG_DYNAMIC_DEBUG is not set -+ -+# -+# Compile-time checks and compiler options -+# -+CONFIG_DEBUG_INFO=y -+# CONFIG_DEBUG_INFO_REDUCED is not set -+# CONFIG_DEBUG_INFO_SPLIT is not set -+# CONFIG_DEBUG_INFO_DWARF4 is not set -+CONFIG_ENABLE_WARN_DEPRECATED=y -+CONFIG_ENABLE_MUST_CHECK=y -+CONFIG_FRAME_WARN=2048 -+# CONFIG_STRIP_ASM_SYMS is not set -+# CONFIG_READABLE_ASM is not set -+# CONFIG_UNUSED_SYMBOLS is not set -+CONFIG_DEBUG_FS=y -+# CONFIG_HEADERS_CHECK is not set -+# CONFIG_DEBUG_SECTION_MISMATCH is not set -+CONFIG_ARCH_WANT_FRAME_POINTERS=y -+CONFIG_FRAME_POINTER=y -+# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set -+CONFIG_MAGIC_SYSRQ=y -+CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x1 -+CONFIG_DEBUG_KERNEL=y -+ -+# -+# Memory Debugging -+# -+# CONFIG_DEBUG_PAGEALLOC is not set -+# CONFIG_DEBUG_OBJECTS is not set -+# CONFIG_DEBUG_SLAB 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_SHIRQ is not set -+ -+# -+# Debug Lockups and Hangs -+# -+CONFIG_LOCKUP_DETECTOR=y -+# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set -+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 -+CONFIG_DETECT_HUNG_TASK=y -+CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120 -+# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set -+CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 -+# 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 -+CONFIG_DEBUG_PREEMPT=y -+ -+# -+# 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 is not set -+# CONFIG_DEBUG_KOBJECT is not set -+CONFIG_HAVE_DEBUG_BUGVERBOSE=y -+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_VERBOSE=y -+# 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_LKDTM is not set -+# 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_STRICT_DEVMEM is not set -+CONFIG_PID_IN_CONTEXTIDR=y -+# CONFIG_ARM64_RANDOMIZE_TEXT_OFFSET is not set -+# CONFIG_DEBUG_SET_MODULE_RONX is not set -+ -+# -+# Security options -+# -+CONFIG_KEYS=y -+# CONFIG_PERSISTENT_KEYRINGS is not set -+# CONFIG_BIG_KEYS is not set -+# CONFIG_ENCRYPTED_KEYS is not set -+# CONFIG_KEYS_DEBUG_PROC_KEYS is not set -+# CONFIG_SECURITY_DMESG_RESTRICT is not set -+CONFIG_SECURITY=y -+# CONFIG_SECURITYFS is not set -+# CONFIG_SECURITY_NETWORK is not set -+# CONFIG_SECURITY_PATH is not set -+# CONFIG_SECURITY_SMACK is not set -+# CONFIG_SECURITY_TOMOYO is not set -+# CONFIG_SECURITY_APPARMOR is not set -+# CONFIG_SECURITY_YAMA is not set -+CONFIG_INTEGRITY=y -+# CONFIG_INTEGRITY_SIGNATURE is not set -+CONFIG_INTEGRITY_AUDIT=y -+# CONFIG_IMA is not set -+# CONFIG_EVM 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_PCRYPT is not set -+CONFIG_CRYPTO_WORKQUEUE=y -+CONFIG_CRYPTO_CRYPTD=y -+# CONFIG_CRYPTO_MCRYPTD is not set -+CONFIG_CRYPTO_AUTHENC=y -+# CONFIG_CRYPTO_TEST is not set -+CONFIG_CRYPTO_ABLK_HELPER=y -+ -+# -+# Authenticated Encryption with Associated Data -+# -+# CONFIG_CRYPTO_CCM is not set -+# CONFIG_CRYPTO_GCM is not set -+# CONFIG_CRYPTO_SEQIV is not set -+ -+# -+# Block modes -+# -+CONFIG_CRYPTO_CBC=y -+# CONFIG_CRYPTO_CTR is not set -+# CONFIG_CRYPTO_CTS is not set -+# CONFIG_CRYPTO_ECB is not set -+# CONFIG_CRYPTO_LRW is not set -+# CONFIG_CRYPTO_PCBC is not set -+# CONFIG_CRYPTO_XTS is not set -+ -+# -+# Hash modes -+# -+# CONFIG_CRYPTO_CMAC is not set -+CONFIG_CRYPTO_HMAC=y -+# 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=y -+# CONFIG_CRYPTO_MICHAEL_MIC is not set -+# CONFIG_CRYPTO_RMD128 is not set -+# CONFIG_CRYPTO_RMD160 is not set -+# CONFIG_CRYPTO_RMD256 is not set -+# CONFIG_CRYPTO_RMD320 is not set -+CONFIG_CRYPTO_SHA1=y -+# 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_ANUBIS is not set -+# CONFIG_CRYPTO_ARC4 is not set -+# CONFIG_CRYPTO_BLOWFISH is not set -+# CONFIG_CRYPTO_CAMELLIA is not set -+# CONFIG_CRYPTO_CAST5 is not set -+# CONFIG_CRYPTO_CAST6 is not set -+CONFIG_CRYPTO_DES=y -+# CONFIG_CRYPTO_FCRYPT is not set -+# CONFIG_CRYPTO_KHAZAD is not set -+# CONFIG_CRYPTO_SALSA20 is not set -+# CONFIG_CRYPTO_SEED is not set -+# CONFIG_CRYPTO_SERPENT is not set -+# CONFIG_CRYPTO_TEA is not set -+# CONFIG_CRYPTO_TWOFISH is not set -+ -+# -+# Compression -+# -+CONFIG_CRYPTO_DEFLATE=y -+# 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=y -+# 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_CCP is not set -+# CONFIG_ASYMMETRIC_KEY_TYPE is not set -+CONFIG_ARM64_CRYPTO=y -+CONFIG_CRYPTO_SHA1_ARM64_CE=y -+CONFIG_CRYPTO_SHA2_ARM64_CE=y -+CONFIG_CRYPTO_GHASH_ARM64_CE=y -+CONFIG_CRYPTO_AES_ARM64_CE=y -+CONFIG_CRYPTO_AES_ARM64_CE_CCM=y -+CONFIG_CRYPTO_AES_ARM64_CE_BLK=y -+CONFIG_CRYPTO_AES_ARM64_NEON_BLK=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_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=y -+# CONFIG_LIBCRC32C is not set -+# CONFIG_CRC8 is not set -+CONFIG_AUDIT_GENERIC=y -+CONFIG_AUDIT_ARCH_COMPAT_GENERIC=y -+CONFIG_AUDIT_COMPAT_GENERIC=y -+# 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_ASSOCIATIVE_ARRAY=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_OID_REGISTRY=y -+CONFIG_UCS2_STRING=y -+CONFIG_FONT_SUPPORT=y -+# CONFIG_FONTS is not set -+CONFIG_FONT_8x8=y -+CONFIG_FONT_8x16=y -+CONFIG_ARCH_HAS_SG_CHAIN=y -diff --git a/arch/arm64/include/asm/device.h b/arch/arm64/include/asm/device.h -index cf98b36..243ef25 100644 ---- a/arch/arm64/include/asm/device.h -+++ b/arch/arm64/include/asm/device.h -@@ -21,6 +21,7 @@ struct dev_archdata { - #ifdef CONFIG_IOMMU_API - void *iommu; /* private IOMMU data */ - #endif -+ bool dma_coherent; - }; - - struct pdev_archdata { -diff --git a/arch/arm64/include/asm/dma-mapping.h b/arch/arm64/include/asm/dma-mapping.h -index adeae3f..9ce3e68 100644 ---- a/arch/arm64/include/asm/dma-mapping.h -+++ b/arch/arm64/include/asm/dma-mapping.h -@@ -52,12 +52,20 @@ static inline void set_dma_ops(struct device *dev, struct dma_map_ops *ops) - dev->archdata.dma_ops = ops; - } - --static inline int set_arch_dma_coherent_ops(struct device *dev) -+static inline void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size, -+ struct iommu_ops *iommu, bool coherent) - { -- set_dma_ops(dev, &coherent_swiotlb_dma_ops); -- return 0; -+ dev->archdata.dma_coherent = coherent; -+ if (coherent) -+ set_dma_ops(dev, &coherent_swiotlb_dma_ops); -+} -+#define arch_setup_dma_ops arch_setup_dma_ops -+ -+/* do not use this function in a driver */ -+static inline bool is_device_dma_coherent(struct device *dev) -+{ -+ return dev->archdata.dma_coherent; - } --#define set_arch_dma_coherent_ops set_arch_dma_coherent_ops - - #include - -diff --git a/arch/arm64/include/asm/io.h b/arch/arm64/include/asm/io.h -index 75825b6..f58e31a 100644 ---- a/arch/arm64/include/asm/io.h -+++ b/arch/arm64/include/asm/io.h -@@ -249,6 +249,7 @@ extern void __iomem *ioremap_cache(phys_addr_t phys_addr, size_t size); - #define ioremap(addr, size) __ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE)) - #define ioremap_nocache(addr, size) __ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE)) - #define ioremap_wc(addr, size) __ioremap((addr), (size), __pgprot(PROT_NORMAL_NC)) -+#define ioremap_cache_ns(addr, size) __ioremap((addr), (size), __pgprot(PROT_NORMAL_NS)) - #define iounmap __iounmap - - #define ARCH_HAS_IOREMAP_WC -diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h -index 101a42b..8ec41e5 100644 ---- a/arch/arm64/include/asm/mmu_context.h -+++ b/arch/arm64/include/asm/mmu_context.h -@@ -64,6 +64,49 @@ static inline void cpu_set_reserved_ttbr0(void) - : "r" (ttbr)); - } - -+/* -+ * TCR.T0SZ value to use when the ID map is active. Usually equals -+ * TCR_T0SZ(VA_BITS), unless system RAM is positioned very high in -+ * physical memory, in which case it will be smaller. -+ */ -+extern u64 idmap_t0sz; -+ -+static inline bool __cpu_uses_extended_idmap(void) -+{ -+ return (!IS_ENABLED(CONFIG_ARM64_VA_BITS_48) && -+ unlikely(idmap_t0sz != TCR_T0SZ(VA_BITS))); -+} -+ -+static inline void __cpu_set_tcr_t0sz(u64 t0sz) -+{ -+ unsigned long tcr; -+ -+ if (__cpu_uses_extended_idmap()) -+ asm volatile ( -+ " mrs %0, tcr_el1 ;" -+ " bfi %0, %1, %2, %3 ;" -+ " msr tcr_el1, %0 ;" -+ " isb" -+ : "=&r" (tcr) -+ : "r"(t0sz), "I"(TCR_T0SZ_OFFSET), "I"(TCR_TxSZ_WIDTH)); -+} -+ -+/* -+ * Set TCR.T0SZ to the value appropriate for activating the identity map. -+ */ -+static inline void cpu_set_idmap_tcr_t0sz(void) -+{ -+ __cpu_set_tcr_t0sz(idmap_t0sz); -+} -+ -+/* -+ * Set TCR.T0SZ to its default value (based on VA_BITS) -+ */ -+static inline void cpu_set_default_tcr_t0sz(void) -+{ -+ __cpu_set_tcr_t0sz(TCR_T0SZ(VA_BITS)); -+} -+ - static inline void switch_new_context(struct mm_struct *mm) - { - unsigned long flags; -diff --git a/arch/arm64/include/asm/page.h b/arch/arm64/include/asm/page.h -index 22b1623..3d02b18 100644 ---- a/arch/arm64/include/asm/page.h -+++ b/arch/arm64/include/asm/page.h -@@ -33,7 +33,9 @@ - * image. Both require pgd, pud (4 levels only) and pmd tables to (section) - * map the kernel. With the 64K page configuration, swapper and idmap need to - * map to pte level. The swapper also maps the FDT (see __create_page_tables -- * for more information). -+ * for more information). Note that the number of ID map translation levels -+ * could be increased on the fly if system RAM is out of reach for the default -+ * VA range, so 3 pages are reserved in all cases. - */ - #ifdef CONFIG_ARM64_64K_PAGES - #define SWAPPER_PGTABLE_LEVELS (CONFIG_ARM64_PGTABLE_LEVELS) -@@ -42,7 +44,7 @@ - #endif - - #define SWAPPER_DIR_SIZE (SWAPPER_PGTABLE_LEVELS * PAGE_SIZE) --#define IDMAP_DIR_SIZE (SWAPPER_DIR_SIZE) -+#define IDMAP_DIR_SIZE (3 * PAGE_SIZE) - - #ifndef __ASSEMBLY__ - -diff --git a/arch/arm64/include/asm/pgtable-hwdef.h b/arch/arm64/include/asm/pgtable-hwdef.h -index 88174e0..500b74e 100644 ---- a/arch/arm64/include/asm/pgtable-hwdef.h -+++ b/arch/arm64/include/asm/pgtable-hwdef.h -@@ -142,7 +142,12 @@ - /* - * TCR flags. - */ --#define TCR_TxSZ(x) (((UL(64) - (x)) << 16) | ((UL(64) - (x)) << 0)) -+#define TCR_T0SZ_OFFSET 0 -+#define TCR_T1SZ_OFFSET 16 -+#define TCR_T0SZ(x) ((UL(64) - (x)) << TCR_T0SZ_OFFSET) -+#define TCR_T1SZ(x) ((UL(64) - (x)) << TCR_T1SZ_OFFSET) -+#define TCR_TxSZ(x) (TCR_T0SZ(x) | TCR_T1SZ(x)) -+#define TCR_TxSZ_WIDTH 6 - #define TCR_IRGN_NC ((UL(0) << 8) | (UL(0) << 24)) - #define TCR_IRGN_WBWA ((UL(1) << 8) | (UL(1) << 24)) - #define TCR_IRGN_WT ((UL(2) << 8) | (UL(2) << 24)) -diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h -index 41a43bf..9b417b8 100644 ---- a/arch/arm64/include/asm/pgtable.h -+++ b/arch/arm64/include/asm/pgtable.h -@@ -65,6 +65,7 @@ extern void __pgd_error(const char *file, int line, unsigned long val); - #define PROT_DEVICE_nGnRE (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_ATTRINDX(MT_DEVICE_nGnRE)) - #define PROT_NORMAL_NC (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_ATTRINDX(MT_NORMAL_NC)) - #define PROT_NORMAL (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_ATTRINDX(MT_NORMAL)) -+#define PROT_NORMAL_NS (PTE_TYPE_PAGE | PTE_AF | PTE_PXN | PTE_UXN | PTE_ATTRINDX(MT_NORMAL)) - - #define PROT_SECT_DEVICE_nGnRE (PROT_SECT_DEFAULT | PMD_SECT_PXN | PMD_SECT_UXN | PMD_ATTRINDX(MT_DEVICE_nGnRE)) - #define PROT_SECT_NORMAL (PROT_SECT_DEFAULT | PMD_SECT_PXN | PMD_SECT_UXN | PMD_ATTRINDX(MT_NORMAL)) -@@ -321,6 +322,13 @@ static inline int has_transparent_hugepage(void) - #define pgprot_device(prot) \ - __pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_DEVICE_nGnRE) | PTE_PXN | PTE_UXN) - #define __HAVE_PHYS_MEM_ACCESS_PROT -+#define pgprot_cached_ns(prot) \ -+ __pgprot(pgprot_val(pgprot_cached(prot)) & ~PTE_SHARED) -+#define pgprot_cached(prot) \ -+ __pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_NORMAL) | \ -+ PTE_PXN | PTE_UXN) -+ -+ - struct file; - extern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn, - unsigned long size, pgprot_t vma_prot); -diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S -index 2877dd8..ca02239 100644 ---- a/arch/arm64/kernel/head.S -+++ b/arch/arm64/kernel/head.S -@@ -592,6 +592,43 @@ __create_page_tables: - mov x0, x25 // idmap_pg_dir - ldr x3, =KERNEL_START - add x3, x3, x28 // __pa(KERNEL_START) -+ -+#ifndef CONFIG_ARM64_VA_BITS_48 -+#define EXTRA_SHIFT (PGDIR_SHIFT + PAGE_SHIFT - 3) -+#define EXTRA_PTRS (1 << (48 - EXTRA_SHIFT)) -+ -+ /* -+ * If VA_BITS < 48, it may be too small to allow for an ID mapping to be -+ * created that covers system RAM if that is located sufficiently high -+ * in the physical address space. So for the ID map, use an extended -+ * virtual range in that case, by configuring an additional translation -+ * level. -+ * First, we have to verify our assumption that the current value of -+ * VA_BITS was chosen such that all translation levels are fully -+ * utilised, and that lowering T0SZ will always result in an additional -+ * translation level to be configured. -+ */ -+#if VA_BITS != EXTRA_SHIFT -+#error "Mismatch between VA_BITS and page size/number of translation levels" -+#endif -+ -+ /* -+ * Calculate the maximum allowed value for TCR_EL1.T0SZ so that the -+ * entire kernel image can be ID mapped. As T0SZ == (64 - #bits used), -+ * this number conveniently equals the number of leading zeroes in -+ * the physical address of KERNEL_END. -+ */ -+ adrp x5, KERNEL_END -+ clz x5, x5 -+ cmp x5, TCR_T0SZ(VA_BITS) // default T0SZ small enough? -+ b.ge 1f // .. then skip additional level -+ -+ str_l x5, idmap_t0sz, x6 -+ -+ create_table_entry x0, x3, EXTRA_SHIFT, EXTRA_PTRS, x5, x6 -+1: -+#endif -+ - create_pgd_entry x0, x3, x5, x6 - ldr x6, =KERNEL_END - mov x5, x3 // __pa(KERNEL_START) -diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c -index 0ef8789..5657692 100644 ---- a/arch/arm64/kernel/smp.c -+++ b/arch/arm64/kernel/smp.c -@@ -152,6 +152,7 @@ asmlinkage void secondary_start_kernel(void) - */ - cpu_set_reserved_ttbr0(); - flush_tlb_all(); -+ cpu_set_default_tcr_t0sz(); - - preempt_disable(); - trace_hardirqs_off(); -diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c -index f4f8b50..53bbff9 100644 ---- a/arch/arm64/mm/mmu.c -+++ b/arch/arm64/mm/mmu.c -@@ -37,6 +37,8 @@ - - #include "mm.h" - -+u64 idmap_t0sz = TCR_T0SZ(VA_BITS); -+ - /* - * Empty_zero_page is a special page that is used for zero-initialized data - * and COW. -@@ -369,6 +371,7 @@ void __init paging_init(void) - */ - cpu_set_reserved_ttbr0(); - flush_tlb_all(); -+ cpu_set_default_tcr_t0sz(); - } - - /* -@@ -376,8 +379,10 @@ void __init paging_init(void) - */ - void setup_mm_for_reboot(void) - { -- cpu_switch_mm(idmap_pg_dir, &init_mm); -+ cpu_set_reserved_ttbr0(); - flush_tlb_all(); -+ cpu_set_idmap_tcr_t0sz(); -+ cpu_switch_mm(idmap_pg_dir, &init_mm); - } - - /* -diff --git a/arch/arm64/mm/proc-macros.S b/arch/arm64/mm/proc-macros.S -index 005d29e..4c4d93c 100644 ---- a/arch/arm64/mm/proc-macros.S -+++ b/arch/arm64/mm/proc-macros.S -@@ -52,3 +52,13 @@ - mov \reg, #4 // bytes per word - lsl \reg, \reg, \tmp // actual cache line size - .endm -+ -+/* -+ * tcr_set_idmap_t0sz - update TCR.T0SZ so that we can load the ID map -+ */ -+ .macro tcr_set_idmap_t0sz, valreg, tmpreg -+#ifndef CONFIG_ARM64_VA_BITS_48 -+ ldr_l \tmpreg, idmap_t0sz -+ bfi \valreg, \tmpreg, #TCR_T0SZ_OFFSET, #TCR_TxSZ_WIDTH -+#endif -+ .endm -diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S -index 4e778b1..cbea872 100644 ---- a/arch/arm64/mm/proc.S -+++ b/arch/arm64/mm/proc.S -@@ -156,6 +156,7 @@ ENTRY(cpu_do_resume) - msr cpacr_el1, x6 - msr ttbr0_el1, x1 - msr ttbr1_el1, x7 -+ tcr_set_idmap_t0sz x8, x7 - msr tcr_el1, x8 - msr vbar_el1, x9 - msr mdscr_el1, x10 -@@ -233,6 +234,8 @@ ENTRY(__cpu_setup) - */ - ldr x10, =TCR_TxSZ(VA_BITS) | TCR_CACHE_FLAGS | TCR_SMP_FLAGS | \ - TCR_TG_FLAGS | TCR_ASID16 | TCR_TBI0 -+ tcr_set_idmap_t0sz x10, x9 -+ - /* - * Read the PARange bits from ID_AA64MMFR0_EL1 and set the IPS bits in - * TCR_EL1. -diff --git a/arch/ia64/kernel/msi_ia64.c b/arch/ia64/kernel/msi_ia64.c -index 8c3730c..8ae36ea 100644 ---- a/arch/ia64/kernel/msi_ia64.c -+++ b/arch/ia64/kernel/msi_ia64.c -@@ -35,7 +35,7 @@ static int ia64_set_msi_irq_affinity(struct irq_data *idata, - data |= MSI_DATA_VECTOR(irq_to_vector(irq)); - msg.data = data; - -- write_msi_msg(irq, &msg); -+ pci_write_msi_msg(irq, &msg); - cpumask_copy(idata->affinity, cpumask_of(cpu)); - - return 0; -@@ -71,7 +71,7 @@ int ia64_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc) - MSI_DATA_DELIVERY_FIXED | - MSI_DATA_VECTOR(vector); - -- write_msi_msg(irq, &msg); -+ pci_write_msi_msg(irq, &msg); - irq_set_chip_and_handler(irq, &ia64_msi_chip, handle_edge_irq); - - return 0; -@@ -102,8 +102,8 @@ static int ia64_msi_retrigger_irq(struct irq_data *data) - */ - static struct irq_chip ia64_msi_chip = { - .name = "PCI-MSI", -- .irq_mask = mask_msi_irq, -- .irq_unmask = unmask_msi_irq, -+ .irq_mask = pci_msi_mask_irq, -+ .irq_unmask = pci_msi_unmask_irq, - .irq_ack = ia64_ack_msi_irq, - #ifdef CONFIG_SMP - .irq_set_affinity = ia64_set_msi_irq_affinity, -diff --git a/arch/ia64/sn/kernel/msi_sn.c b/arch/ia64/sn/kernel/msi_sn.c -index 446e779..a0eb27b 100644 ---- a/arch/ia64/sn/kernel/msi_sn.c -+++ b/arch/ia64/sn/kernel/msi_sn.c -@@ -145,7 +145,7 @@ int sn_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *entry) - msg.data = 0x100 + irq; - - irq_set_msi_desc(irq, entry); -- write_msi_msg(irq, &msg); -+ pci_write_msi_msg(irq, &msg); - irq_set_chip_and_handler(irq, &sn_msi_chip, handle_edge_irq); - - return 0; -@@ -205,7 +205,7 @@ static int sn_set_msi_irq_affinity(struct irq_data *data, - msg.address_hi = (u32)(bus_addr >> 32); - msg.address_lo = (u32)(bus_addr & 0x00000000ffffffff); - -- write_msi_msg(irq, &msg); -+ pci_write_msi_msg(irq, &msg); - cpumask_copy(data->affinity, cpu_mask); - - return 0; -@@ -228,8 +228,8 @@ static int sn_msi_retrigger_irq(struct irq_data *data) - - static struct irq_chip sn_msi_chip = { - .name = "PCI-MSI", -- .irq_mask = mask_msi_irq, -- .irq_unmask = unmask_msi_irq, -+ .irq_mask = pci_msi_mask_irq, -+ .irq_unmask = pci_msi_unmask_irq, - .irq_ack = sn_ack_msi_irq, - #ifdef CONFIG_SMP - .irq_set_affinity = sn_set_msi_irq_affinity, -diff --git a/arch/mips/pci/msi-octeon.c b/arch/mips/pci/msi-octeon.c -index 63bbe07..cffaaf4 100644 ---- a/arch/mips/pci/msi-octeon.c -+++ b/arch/mips/pci/msi-octeon.c -@@ -178,7 +178,7 @@ msi_irq_allocated: - pci_write_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, control); - - irq_set_msi_desc(irq, desc); -- write_msi_msg(irq, &msg); -+ pci_write_msi_msg(irq, &msg); - return 0; - } - -diff --git a/arch/mips/pci/msi-xlp.c b/arch/mips/pci/msi-xlp.c -index f7ac3ed..6a40f24 100644 ---- a/arch/mips/pci/msi-xlp.c -+++ b/arch/mips/pci/msi-xlp.c -@@ -217,7 +217,7 @@ static void xlp_msix_mask_ack(struct irq_data *d) - - msixvec = nlm_irq_msixvec(d->irq); - link = nlm_irq_msixlink(msixvec); -- mask_msi_irq(d); -+ pci_msi_mask_irq(d); - md = irq_data_get_irq_handler_data(d); - - /* Ack MSI on bridge */ -@@ -239,10 +239,10 @@ static void xlp_msix_mask_ack(struct irq_data *d) - - static struct irq_chip xlp_msix_chip = { - .name = "XLP-MSIX", -- .irq_enable = unmask_msi_irq, -- .irq_disable = mask_msi_irq, -+ .irq_enable = pci_msi_unmask_irq, -+ .irq_disable = pci_msi_mask_irq, - .irq_mask_ack = xlp_msix_mask_ack, -- .irq_unmask = unmask_msi_irq, -+ .irq_unmask = pci_msi_unmask_irq, - }; - - void arch_teardown_msi_irq(unsigned int irq) -@@ -345,7 +345,7 @@ static int xlp_setup_msi(uint64_t lnkbase, int node, int link, - if (ret < 0) - return ret; - -- write_msi_msg(xirq, &msg); -+ pci_write_msi_msg(xirq, &msg); - return 0; - } - -@@ -446,7 +446,7 @@ static int xlp_setup_msix(uint64_t lnkbase, int node, int link, - if (ret < 0) - return ret; - -- write_msi_msg(xirq, &msg); -+ pci_write_msi_msg(xirq, &msg); - return 0; - } - -diff --git a/arch/mips/pci/pci-xlr.c b/arch/mips/pci/pci-xlr.c -index 0dde803..26d2dab 100644 ---- a/arch/mips/pci/pci-xlr.c -+++ b/arch/mips/pci/pci-xlr.c -@@ -260,7 +260,7 @@ int arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc) - if (ret < 0) - return ret; - -- write_msi_msg(irq, &msg); -+ pci_write_msi_msg(irq, &msg); - return 0; - } - #endif -diff --git a/arch/powerpc/include/asm/mpc85xx.h b/arch/powerpc/include/asm/mpc85xx.h -deleted file mode 100644 -index 3bef74a..0000000 ---- a/arch/powerpc/include/asm/mpc85xx.h -+++ /dev/null -@@ -1,94 +0,0 @@ --/* -- * MPC85xx cpu type detection -- * -- * Copyright 2011-2012 Freescale Semiconductor, Inc. -- * -- * This 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. -- */ -- --#ifndef __ASM_PPC_MPC85XX_H --#define __ASM_PPC_MPC85XX_H -- --#define SVR_REV(svr) ((svr) & 0xFF) /* SOC design resision */ --#define SVR_MAJ(svr) (((svr) >> 4) & 0xF) /* Major revision field*/ --#define SVR_MIN(svr) (((svr) >> 0) & 0xF) /* Minor revision field*/ -- --/* Some parts define SVR[0:23] as the SOC version */ --#define SVR_SOC_VER(svr) (((svr) >> 8) & 0xFFF7FF) /* SOC Version fields */ -- --#define SVR_8533 0x803400 --#define SVR_8535 0x803701 --#define SVR_8536 0x803700 --#define SVR_8540 0x803000 --#define SVR_8541 0x807200 --#define SVR_8543 0x803200 --#define SVR_8544 0x803401 --#define SVR_8545 0x803102 --#define SVR_8547 0x803101 --#define SVR_8548 0x803100 --#define SVR_8555 0x807100 --#define SVR_8560 0x807000 --#define SVR_8567 0x807501 --#define SVR_8568 0x807500 --#define SVR_8569 0x808000 --#define SVR_8572 0x80E000 --#define SVR_P1010 0x80F100 --#define SVR_P1011 0x80E500 --#define SVR_P1012 0x80E501 --#define SVR_P1013 0x80E700 --#define SVR_P1014 0x80F101 --#define SVR_P1017 0x80F700 --#define SVR_P1020 0x80E400 --#define SVR_P1021 0x80E401 --#define SVR_P1022 0x80E600 --#define SVR_P1023 0x80F600 --#define SVR_P1024 0x80E402 --#define SVR_P1025 0x80E403 --#define SVR_P2010 0x80E300 --#define SVR_P2020 0x80E200 --#define SVR_P2040 0x821000 --#define SVR_P2041 0x821001 --#define SVR_P3041 0x821103 --#define SVR_P4040 0x820100 --#define SVR_P4080 0x820000 --#define SVR_P5010 0x822100 --#define SVR_P5020 0x822000 --#define SVR_P5021 0X820500 --#define SVR_P5040 0x820400 --#define SVR_T4240 0x824000 --#define SVR_T4120 0x824001 --#define SVR_T4160 0x824100 --#define SVR_C291 0x850000 --#define SVR_C292 0x850020 --#define SVR_C293 0x850030 --#define SVR_B4860 0X868000 --#define SVR_G4860 0x868001 --#define SVR_G4060 0x868003 --#define SVR_B4440 0x868100 --#define SVR_G4440 0x868101 --#define SVR_B4420 0x868102 --#define SVR_B4220 0x868103 --#define SVR_T1040 0x852000 --#define SVR_T1041 0x852001 --#define SVR_T1042 0x852002 --#define SVR_T1020 0x852100 --#define SVR_T1021 0x852101 --#define SVR_T1022 0x852102 --#define SVR_T2080 0x853000 --#define SVR_T2081 0x853100 -- --#define SVR_8610 0x80A000 --#define SVR_8641 0x809000 --#define SVR_8641D 0x809001 -- --#define SVR_9130 0x860001 --#define SVR_9131 0x860000 --#define SVR_9132 0x861000 --#define SVR_9232 0x861400 -- --#define SVR_Unknown 0xFFFFFF -- --#endif -diff --git a/arch/powerpc/platforms/512x/mpc5121_ads_cpld.c b/arch/powerpc/platforms/512x/mpc5121_ads_cpld.c -index ca3a062..11090ab 100644 ---- a/arch/powerpc/platforms/512x/mpc5121_ads_cpld.c -+++ b/arch/powerpc/platforms/512x/mpc5121_ads_cpld.c -@@ -123,7 +123,8 @@ cpld_pic_cascade(unsigned int irq, struct irq_desc *desc) - } - - static int --cpld_pic_host_match(struct irq_domain *h, struct device_node *node) -+cpld_pic_host_match(struct irq_domain *h, struct device_node *node, -+ enum irq_domain_bus_token bus_token) - { - return cpld_pic_node == node; - } -diff --git a/arch/powerpc/platforms/85xx/mpc85xx_mds.c b/arch/powerpc/platforms/85xx/mpc85xx_mds.c -index a392e94..f0be439 100644 ---- a/arch/powerpc/platforms/85xx/mpc85xx_mds.c -+++ b/arch/powerpc/platforms/85xx/mpc85xx_mds.c -@@ -34,6 +34,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -51,7 +52,6 @@ - #include - #include - #include --#include - #include "smp.h" - - #include "mpc85xx.h" -diff --git a/arch/powerpc/platforms/85xx/mpc85xx_rdb.c b/arch/powerpc/platforms/85xx/mpc85xx_rdb.c -index e358bed..50dcc00 100644 ---- a/arch/powerpc/platforms/85xx/mpc85xx_rdb.c -+++ b/arch/powerpc/platforms/85xx/mpc85xx_rdb.c -@@ -17,6 +17,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -27,7 +28,6 @@ - #include - #include - #include --#include - - #include - #include -diff --git a/arch/powerpc/platforms/85xx/p1022_ds.c b/arch/powerpc/platforms/85xx/p1022_ds.c -index 6ac986d..371df82 100644 ---- a/arch/powerpc/platforms/85xx/p1022_ds.c -+++ b/arch/powerpc/platforms/85xx/p1022_ds.c -@@ -16,6 +16,7 @@ - * kind, whether express or implied. - */ - -+#include - #include - #include - #include -@@ -25,7 +26,6 @@ - #include - #include - #include --#include - #include - #include "smp.h" - -diff --git a/arch/powerpc/platforms/85xx/p1022_rdk.c b/arch/powerpc/platforms/85xx/p1022_rdk.c -index 7a180f0..4f8fc5f 100644 ---- a/arch/powerpc/platforms/85xx/p1022_rdk.c -+++ b/arch/powerpc/platforms/85xx/p1022_rdk.c -@@ -12,6 +12,7 @@ - * kind, whether express or implied. - */ - -+#include - #include - #include - #include -@@ -21,7 +22,6 @@ - #include - #include - #include --#include - #include "smp.h" - - #include "mpc85xx.h" -diff --git a/arch/powerpc/platforms/85xx/smp.c b/arch/powerpc/platforms/85xx/smp.c -index d7c1e69..3956455 100644 ---- a/arch/powerpc/platforms/85xx/smp.c -+++ b/arch/powerpc/platforms/85xx/smp.c -@@ -19,6 +19,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -26,7 +27,6 @@ - #include - #include - #include --#include - #include - #include - -diff --git a/arch/powerpc/platforms/85xx/twr_p102x.c b/arch/powerpc/platforms/85xx/twr_p102x.c -index 1eadb6d..2799120 100644 ---- a/arch/powerpc/platforms/85xx/twr_p102x.c -+++ b/arch/powerpc/platforms/85xx/twr_p102x.c -@@ -15,6 +15,7 @@ - #include - #include - #include -+#include - #include - #include - -@@ -23,7 +24,6 @@ - #include - #include - #include --#include - - #include - #include -diff --git a/arch/powerpc/platforms/86xx/mpc8610_hpcd.c b/arch/powerpc/platforms/86xx/mpc8610_hpcd.c -index 55413a5..437a9c3 100644 ---- a/arch/powerpc/platforms/86xx/mpc8610_hpcd.c -+++ b/arch/powerpc/platforms/86xx/mpc8610_hpcd.c -@@ -24,6 +24,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -38,7 +39,6 @@ - #include - #include - #include --#include - - #include "mpc86xx.h" - -diff --git a/arch/powerpc/platforms/cell/axon_msi.c b/arch/powerpc/platforms/cell/axon_msi.c -index 862b327..0883994 100644 ---- a/arch/powerpc/platforms/cell/axon_msi.c -+++ b/arch/powerpc/platforms/cell/axon_msi.c -@@ -279,7 +279,7 @@ static int axon_msi_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) - - irq_set_msi_desc(virq, entry); - msg.data = virq; -- write_msi_msg(virq, &msg); -+ pci_write_msi_msg(virq, &msg); - } - - return 0; -@@ -301,9 +301,9 @@ static void axon_msi_teardown_msi_irqs(struct pci_dev *dev) - } - - static struct irq_chip msic_irq_chip = { -- .irq_mask = mask_msi_irq, -- .irq_unmask = unmask_msi_irq, -- .irq_shutdown = mask_msi_irq, -+ .irq_mask = pci_msi_mask_irq, -+ .irq_unmask = pci_msi_unmask_irq, -+ .irq_shutdown = pci_msi_mask_irq, - .name = "AXON-MSI", - }; - -diff --git a/arch/powerpc/platforms/cell/interrupt.c b/arch/powerpc/platforms/cell/interrupt.c -index 28e558d..109d236 100644 ---- a/arch/powerpc/platforms/cell/interrupt.c -+++ b/arch/powerpc/platforms/cell/interrupt.c -@@ -222,7 +222,8 @@ void iic_request_IPIs(void) - #endif /* CONFIG_SMP */ - - --static int iic_host_match(struct irq_domain *h, struct device_node *node) -+static int iic_host_match(struct irq_domain *h, struct device_node *node, -+ enum irq_domain_bus_token bus_token) - { - return of_device_is_compatible(node, - "IBM,CBEA-Internal-Interrupt-Controller"); -diff --git a/arch/powerpc/platforms/embedded6xx/flipper-pic.c b/arch/powerpc/platforms/embedded6xx/flipper-pic.c -index 4cde8e7..b7866e0 100644 ---- a/arch/powerpc/platforms/embedded6xx/flipper-pic.c -+++ b/arch/powerpc/platforms/embedded6xx/flipper-pic.c -@@ -108,7 +108,8 @@ static int flipper_pic_map(struct irq_domain *h, unsigned int virq, - return 0; - } - --static int flipper_pic_match(struct irq_domain *h, struct device_node *np) -+static int flipper_pic_match(struct irq_domain *h, struct device_node *np, -+ enum irq_domain_bus_token bus_token) - { - return 1; - } -diff --git a/arch/powerpc/platforms/powermac/pic.c b/arch/powerpc/platforms/powermac/pic.c -index 4c24bf6..246cab4 100644 ---- a/arch/powerpc/platforms/powermac/pic.c -+++ b/arch/powerpc/platforms/powermac/pic.c -@@ -268,7 +268,8 @@ static struct irqaction gatwick_cascade_action = { - .name = "cascade", - }; - --static int pmac_pic_host_match(struct irq_domain *h, struct device_node *node) -+static int pmac_pic_host_match(struct irq_domain *h, struct device_node *node, -+ enum irq_domain_bus_token bus_token) - { - /* We match all, we don't always have a node anyway */ - return 1; -diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c -index 9ff55d5..019991d 100644 ---- a/arch/powerpc/platforms/powernv/pci.c -+++ b/arch/powerpc/platforms/powernv/pci.c -@@ -90,7 +90,7 @@ static int pnv_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) - return rc; - } - irq_set_msi_desc(virq, entry); -- write_msi_msg(virq, &msg); -+ pci_write_msi_msg(virq, &msg); - } - return 0; - } -diff --git a/arch/powerpc/platforms/ps3/interrupt.c b/arch/powerpc/platforms/ps3/interrupt.c -index 5f3b232..df0c086 100644 ---- a/arch/powerpc/platforms/ps3/interrupt.c -+++ b/arch/powerpc/platforms/ps3/interrupt.c -@@ -678,7 +678,8 @@ static int ps3_host_map(struct irq_domain *h, unsigned int virq, - return 0; - } - --static int ps3_host_match(struct irq_domain *h, struct device_node *np) -+static int ps3_host_match(struct irq_domain *h, struct device_node *np, -+ enum irq_domain_bus_token bus_token) - { - /* Match all */ - return 1; -diff --git a/arch/powerpc/platforms/pseries/msi.c b/arch/powerpc/platforms/pseries/msi.c -index 8b909e9..691a154 100644 ---- a/arch/powerpc/platforms/pseries/msi.c -+++ b/arch/powerpc/platforms/pseries/msi.c -@@ -476,7 +476,7 @@ again: - irq_set_msi_desc(virq, entry); - - /* Read config space back so we can restore after reset */ -- __read_msi_msg(entry, &msg); -+ __pci_read_msi_msg(entry, &msg); - entry->msg = msg; - } - -diff --git a/arch/powerpc/sysdev/ehv_pic.c b/arch/powerpc/sysdev/ehv_pic.c -index 2d20f10..eca0b00 100644 ---- a/arch/powerpc/sysdev/ehv_pic.c -+++ b/arch/powerpc/sysdev/ehv_pic.c -@@ -177,7 +177,8 @@ unsigned int ehv_pic_get_irq(void) - return irq_linear_revmap(global_ehv_pic->irqhost, irq); - } - --static int ehv_pic_host_match(struct irq_domain *h, struct device_node *node) -+static int ehv_pic_host_match(struct irq_domain *h, struct device_node *node, -+ enum irq_domain_bus_token bus_token) - { - /* Exact match, unless ehv_pic node is NULL */ - return h->of_node == NULL || h->of_node == node; -diff --git a/arch/powerpc/sysdev/fsl_msi.c b/arch/powerpc/sysdev/fsl_msi.c -index ea6b3a1..f13282c 100644 ---- a/arch/powerpc/sysdev/fsl_msi.c -+++ b/arch/powerpc/sysdev/fsl_msi.c -@@ -82,8 +82,8 @@ static void fsl_msi_print_chip(struct irq_data *irqd, struct seq_file *p) - - - static struct irq_chip fsl_msi_chip = { -- .irq_mask = mask_msi_irq, -- .irq_unmask = unmask_msi_irq, -+ .irq_mask = pci_msi_mask_irq, -+ .irq_unmask = pci_msi_unmask_irq, - .irq_ack = fsl_msi_end_irq, - .irq_print_chip = fsl_msi_print_chip, - }; -@@ -243,7 +243,7 @@ static int fsl_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) - irq_set_msi_desc(virq, entry); - - fsl_compose_msi_msg(pdev, hwirq, &msg, msi_data); -- write_msi_msg(virq, &msg); -+ pci_write_msi_msg(virq, &msg); - } - return 0; - -diff --git a/arch/powerpc/sysdev/i8259.c b/arch/powerpc/sysdev/i8259.c -index 45598da..8c3756c 100644 ---- a/arch/powerpc/sysdev/i8259.c -+++ b/arch/powerpc/sysdev/i8259.c -@@ -162,7 +162,8 @@ static struct resource pic_edgectrl_iores = { - .flags = IORESOURCE_BUSY, - }; - --static int i8259_host_match(struct irq_domain *h, struct device_node *node) -+static int i8259_host_match(struct irq_domain *h, struct device_node *node, -+ enum irq_domain_bus_token bus_token) - { - return h->of_node == NULL || h->of_node == node; - } -diff --git a/arch/powerpc/sysdev/ipic.c b/arch/powerpc/sysdev/ipic.c -index b50f978..1b9b00f 100644 ---- a/arch/powerpc/sysdev/ipic.c -+++ b/arch/powerpc/sysdev/ipic.c -@@ -672,7 +672,8 @@ static struct irq_chip ipic_edge_irq_chip = { - .irq_set_type = ipic_set_irq_type, - }; - --static int ipic_host_match(struct irq_domain *h, struct device_node *node) -+static int ipic_host_match(struct irq_domain *h, struct device_node *node, -+ enum irq_domain_bus_token bus_token) - { - /* Exact match, unless ipic node is NULL */ - return h->of_node == NULL || h->of_node == node; -diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c -index 89cec0e..bf6f77e 100644 ---- a/arch/powerpc/sysdev/mpic.c -+++ b/arch/powerpc/sysdev/mpic.c -@@ -1009,7 +1009,8 @@ static struct irq_chip mpic_irq_ht_chip = { - #endif /* CONFIG_MPIC_U3_HT_IRQS */ - - --static int mpic_host_match(struct irq_domain *h, struct device_node *node) -+static int mpic_host_match(struct irq_domain *h, struct device_node *node, -+ enum irq_domain_bus_token bus_token) - { - /* Exact match, unless mpic node is NULL */ - return h->of_node == NULL || h->of_node == node; -diff --git a/arch/powerpc/sysdev/mpic_pasemi_msi.c b/arch/powerpc/sysdev/mpic_pasemi_msi.c -index a6add4a..5a4c474 100644 ---- a/arch/powerpc/sysdev/mpic_pasemi_msi.c -+++ b/arch/powerpc/sysdev/mpic_pasemi_msi.c -@@ -42,7 +42,7 @@ static struct mpic *msi_mpic; - static void mpic_pasemi_msi_mask_irq(struct irq_data *data) - { - pr_debug("mpic_pasemi_msi_mask_irq %d\n", data->irq); -- mask_msi_irq(data); -+ pci_msi_mask_irq(data); - mpic_mask_irq(data); - } - -@@ -50,7 +50,7 @@ static void mpic_pasemi_msi_unmask_irq(struct irq_data *data) - { - pr_debug("mpic_pasemi_msi_unmask_irq %d\n", data->irq); - mpic_unmask_irq(data); -- unmask_msi_irq(data); -+ pci_msi_unmask_irq(data); - } - - static struct irq_chip mpic_pasemi_msi_chip = { -@@ -138,7 +138,7 @@ static int pasemi_msi_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) - * register to generate MSI [512...1023] - */ - msg.data = hwirq-0x200; -- write_msi_msg(virq, &msg); -+ pci_write_msi_msg(virq, &msg); - } - - return 0; -diff --git a/arch/powerpc/sysdev/mpic_u3msi.c b/arch/powerpc/sysdev/mpic_u3msi.c -index db35a40..65880cc 100644 ---- a/arch/powerpc/sysdev/mpic_u3msi.c -+++ b/arch/powerpc/sysdev/mpic_u3msi.c -@@ -25,14 +25,14 @@ static struct mpic *msi_mpic; - - static void mpic_u3msi_mask_irq(struct irq_data *data) - { -- mask_msi_irq(data); -+ pci_msi_mask_irq(data); - mpic_mask_irq(data); - } - - static void mpic_u3msi_unmask_irq(struct irq_data *data) - { - mpic_unmask_irq(data); -- unmask_msi_irq(data); -+ pci_msi_unmask_irq(data); - } - - static struct irq_chip mpic_u3msi_chip = { -@@ -172,7 +172,7 @@ static int u3msi_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) - printk("u3msi: allocated virq 0x%x (hw 0x%x) addr 0x%lx\n", - virq, hwirq, (unsigned long)addr); - msg.data = hwirq; -- write_msi_msg(virq, &msg); -+ pci_write_msi_msg(virq, &msg); - - hwirq++; - } -diff --git a/arch/powerpc/sysdev/ppc4xx_hsta_msi.c b/arch/powerpc/sysdev/ppc4xx_hsta_msi.c -index a6a4dbd..908105f 100644 ---- a/arch/powerpc/sysdev/ppc4xx_hsta_msi.c -+++ b/arch/powerpc/sysdev/ppc4xx_hsta_msi.c -@@ -85,7 +85,7 @@ static int hsta_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) - msi_bitmap_free_hwirqs(&ppc4xx_hsta_msi.bmp, irq, 1); - return -EINVAL; - } -- write_msi_msg(hwirq, &msg); -+ pci_write_msi_msg(hwirq, &msg); - } - - return 0; -diff --git a/arch/powerpc/sysdev/ppc4xx_msi.c b/arch/powerpc/sysdev/ppc4xx_msi.c -index 85d9c18..c6df3e2 100644 ---- a/arch/powerpc/sysdev/ppc4xx_msi.c -+++ b/arch/powerpc/sysdev/ppc4xx_msi.c -@@ -116,7 +116,7 @@ static int ppc4xx_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) - - irq_set_msi_desc(virq, entry); - msg.data = int_no; -- write_msi_msg(virq, &msg); -+ pci_write_msi_msg(virq, &msg); - } - return 0; - } -diff --git a/arch/powerpc/sysdev/qe_lib/qe_ic.c b/arch/powerpc/sysdev/qe_lib/qe_ic.c -index b2b87c3..a433b3d 100644 ---- a/arch/powerpc/sysdev/qe_lib/qe_ic.c -+++ b/arch/powerpc/sysdev/qe_lib/qe_ic.c -@@ -245,7 +245,8 @@ static struct irq_chip qe_ic_irq_chip = { - .irq_mask_ack = qe_ic_mask_irq, - }; - --static int qe_ic_host_match(struct irq_domain *h, struct device_node *node) -+static int qe_ic_host_match(struct irq_domain *h, struct device_node *node, -+ enum irq_domain_bus_token bus_token) - { - /* Exact match, unless qe_ic node is NULL */ - return h->of_node == NULL || h->of_node == node; -diff --git a/arch/powerpc/sysdev/xics/ics-opal.c b/arch/powerpc/sysdev/xics/ics-opal.c -index 3c6ee1b..4ba554e 100644 ---- a/arch/powerpc/sysdev/xics/ics-opal.c -+++ b/arch/powerpc/sysdev/xics/ics-opal.c -@@ -73,7 +73,7 @@ static unsigned int ics_opal_startup(struct irq_data *d) - * at that level, so we do it here by hand. - */ - if (d->msi_desc) -- unmask_msi_irq(d); -+ pci_msi_unmask_irq(d); - #endif - - /* unmask it */ -diff --git a/arch/powerpc/sysdev/xics/ics-rtas.c b/arch/powerpc/sysdev/xics/ics-rtas.c -index 936575d..bc81335 100644 ---- a/arch/powerpc/sysdev/xics/ics-rtas.c -+++ b/arch/powerpc/sysdev/xics/ics-rtas.c -@@ -76,7 +76,7 @@ static unsigned int ics_rtas_startup(struct irq_data *d) - * at that level, so we do it here by hand. - */ - if (d->msi_desc) -- unmask_msi_irq(d); -+ pci_msi_unmask_irq(d); - #endif - /* unmask it */ - ics_rtas_unmask_irq(d); -diff --git a/arch/powerpc/sysdev/xics/xics-common.c b/arch/powerpc/sysdev/xics/xics-common.c -index fe0cca4..13ab716 100644 ---- a/arch/powerpc/sysdev/xics/xics-common.c -+++ b/arch/powerpc/sysdev/xics/xics-common.c -@@ -300,7 +300,8 @@ int xics_get_irq_server(unsigned int virq, const struct cpumask *cpumask, - } - #endif /* CONFIG_SMP */ - --static int xics_host_match(struct irq_domain *h, struct device_node *node) -+static int xics_host_match(struct irq_domain *h, struct device_node *node, -+ enum irq_domain_bus_token bus_token) - { - struct ics *ics; - -diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c -index 2fa7b14..d59c825 100644 ---- a/arch/s390/pci/pci.c -+++ b/arch/s390/pci/pci.c -@@ -50,8 +50,8 @@ static DEFINE_SPINLOCK(zpci_list_lock); - - static struct irq_chip zpci_irq_chip = { - .name = "zPCI", -- .irq_unmask = unmask_msi_irq, -- .irq_mask = mask_msi_irq, -+ .irq_unmask = pci_msi_unmask_irq, -+ .irq_mask = pci_msi_mask_irq, - }; - - static DECLARE_BITMAP(zpci_domain, ZPCI_NR_DEVICES); -@@ -403,7 +403,7 @@ int arch_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) - msg.data = hwirq; - msg.address_lo = zdev->msi_addr & 0xffffffff; - msg.address_hi = zdev->msi_addr >> 32; -- write_msi_msg(irq, &msg); -+ pci_write_msi_msg(irq, &msg); - airq_iv_set_data(zdev->aibv, hwirq, irq); - hwirq++; - } -@@ -448,9 +448,9 @@ void arch_teardown_msi_irqs(struct pci_dev *pdev) - /* Release MSI interrupts */ - list_for_each_entry(msi, &pdev->msi_list, list) { - if (msi->msi_attrib.is_msix) -- default_msix_mask_irq(msi, 1); -+ __pci_msix_desc_mask_irq(msi, 1); - else -- default_msi_mask_irq(msi, 1, 1); -+ __pci_msi_desc_mask_irq(msi, 1, 1); - irq_set_msi_desc(msi->irq, NULL); - irq_free_desc(msi->irq); - msi->msg.address_lo = 0; -diff --git a/arch/sparc/kernel/pci_msi.c b/arch/sparc/kernel/pci_msi.c -index 580651a..84e16d8 100644 ---- a/arch/sparc/kernel/pci_msi.c -+++ b/arch/sparc/kernel/pci_msi.c -@@ -111,10 +111,10 @@ static void free_msi(struct pci_pbm_info *pbm, int msi_num) - - static struct irq_chip msi_irq = { - .name = "PCI-MSI", -- .irq_mask = mask_msi_irq, -- .irq_unmask = unmask_msi_irq, -- .irq_enable = unmask_msi_irq, -- .irq_disable = mask_msi_irq, -+ .irq_mask = pci_msi_mask_irq, -+ .irq_unmask = pci_msi_unmask_irq, -+ .irq_enable = pci_msi_unmask_irq, -+ .irq_disable = pci_msi_mask_irq, - /* XXX affinity XXX */ - }; - -@@ -161,7 +161,7 @@ static int sparc64_setup_msi_irq(unsigned int *irq_p, - msg.data = msi; - - irq_set_msi_desc(*irq_p, entry); -- write_msi_msg(*irq_p, &msg); -+ pci_write_msi_msg(*irq_p, &msg); - - return 0; - -diff --git a/arch/tile/kernel/pci_gx.c b/arch/tile/kernel/pci_gx.c -index e39f9c5..e717af2 100644 ---- a/arch/tile/kernel/pci_gx.c -+++ b/arch/tile/kernel/pci_gx.c -@@ -1453,7 +1453,7 @@ static struct pci_ops tile_cfg_ops = { - static unsigned int tilegx_msi_startup(struct irq_data *d) - { - if (d->msi_desc) -- unmask_msi_irq(d); -+ pci_msi_unmask_irq(d); - - return 0; - } -@@ -1465,14 +1465,14 @@ static void tilegx_msi_ack(struct irq_data *d) - - static void tilegx_msi_mask(struct irq_data *d) - { -- mask_msi_irq(d); -+ pci_msi_mask_irq(d); - __insn_mtspr(SPR_IPI_MASK_SET_K, 1UL << d->irq); - } - - static void tilegx_msi_unmask(struct irq_data *d) - { - __insn_mtspr(SPR_IPI_MASK_RESET_K, 1UL << d->irq); -- unmask_msi_irq(d); -+ pci_msi_unmask_irq(d); - } - - static struct irq_chip tilegx_msi_chip = { -@@ -1590,7 +1590,7 @@ int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc) - msg.address_hi = msi_addr >> 32; - msg.address_lo = msi_addr & 0xffffffff; - -- write_msi_msg(irq, &msg); -+ pci_write_msi_msg(irq, &msg); - irq_set_chip_and_handler(irq, &tilegx_msi_chip, handle_level_irq); - irq_set_handler_data(irq, controller); - -diff --git a/arch/x86/include/asm/x86_init.h b/arch/x86/include/asm/x86_init.h -index e45e4da..f58a9c7 100644 ---- a/arch/x86/include/asm/x86_init.h -+++ b/arch/x86/include/asm/x86_init.h -@@ -172,7 +172,6 @@ struct x86_platform_ops { - - struct pci_dev; - struct msi_msg; --struct msi_desc; - - struct x86_msi_ops { - int (*setup_msi_irqs)(struct pci_dev *dev, int nvec, int type); -@@ -183,8 +182,6 @@ struct x86_msi_ops { - void (*teardown_msi_irqs)(struct pci_dev *dev); - void (*restore_msi_irqs)(struct pci_dev *dev); - int (*setup_hpet_msi)(unsigned int irq, unsigned int id); -- u32 (*msi_mask_irq)(struct msi_desc *desc, u32 mask, u32 flag); -- u32 (*msix_mask_irq)(struct msi_desc *desc, u32 flag); - }; - - struct IO_APIC_route_entry; -diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c -index 1183d54..7ffe0a2 100644 ---- a/arch/x86/kernel/apic/io_apic.c -+++ b/arch/x86/kernel/apic/io_apic.c -@@ -3158,7 +3158,7 @@ msi_set_affinity(struct irq_data *data, const struct cpumask *mask, bool force) - msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK; - msg.address_lo |= MSI_ADDR_DEST_ID(dest); - -- __write_msi_msg(data->msi_desc, &msg); -+ __pci_write_msi_msg(data->msi_desc, &msg); - - return IRQ_SET_MASK_OK_NOCOPY; - } -@@ -3169,8 +3169,8 @@ msi_set_affinity(struct irq_data *data, const struct cpumask *mask, bool force) - */ - static struct irq_chip msi_chip = { - .name = "PCI-MSI", -- .irq_unmask = unmask_msi_irq, -- .irq_mask = mask_msi_irq, -+ .irq_unmask = pci_msi_unmask_irq, -+ .irq_mask = pci_msi_mask_irq, - .irq_ack = ack_apic_edge, - .irq_set_affinity = msi_set_affinity, - .irq_retrigger = ioapic_retrigger_irq, -@@ -3196,7 +3196,7 @@ int setup_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc, - * MSI message denotes a contiguous group of IRQs, written for 0th IRQ. - */ - if (!irq_offset) -- write_msi_msg(irq, &msg); -+ pci_write_msi_msg(irq, &msg); - - setup_remapped_irq(irq, irq_cfg(irq), chip); - -diff --git a/arch/x86/kernel/x86_init.c b/arch/x86/kernel/x86_init.c -index e48b674..234b072 100644 ---- a/arch/x86/kernel/x86_init.c -+++ b/arch/x86/kernel/x86_init.c -@@ -116,8 +116,6 @@ struct x86_msi_ops x86_msi = { - .teardown_msi_irqs = default_teardown_msi_irqs, - .restore_msi_irqs = default_restore_msi_irqs, - .setup_hpet_msi = default_setup_hpet_msi, -- .msi_mask_irq = default_msi_mask_irq, -- .msix_mask_irq = default_msix_mask_irq, - }; - - /* MSI arch specific hooks */ -@@ -140,14 +138,6 @@ void arch_restore_msi_irqs(struct pci_dev *dev) - { - x86_msi.restore_msi_irqs(dev); - } --u32 arch_msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) --{ -- return x86_msi.msi_mask_irq(desc, mask, flag); --} --u32 arch_msix_mask_irq(struct msi_desc *desc, u32 flag) --{ -- return x86_msi.msix_mask_irq(desc, flag); --} - #endif - - struct x86_io_apic_ops x86_io_apic_ops = { -diff --git a/arch/x86/pci/bus_numa.c b/arch/x86/pci/bus_numa.c -index f3a2cfc..7bcf06a 100644 ---- a/arch/x86/pci/bus_numa.c -+++ b/arch/x86/pci/bus_numa.c -@@ -31,7 +31,7 @@ void x86_pci_root_bus_resources(int bus, struct list_head *resources) - { - struct pci_root_info *info = x86_find_pci_root_info(bus); - struct pci_root_res *root_res; -- struct pci_host_bridge_window *window; -+ struct resource_entry *window; - bool found = false; - - if (!info) -@@ -41,7 +41,7 @@ void x86_pci_root_bus_resources(int bus, struct list_head *resources) - bus); - - /* already added by acpi ? */ -- list_for_each_entry(window, resources, list) -+ resource_list_for_each_entry(window, resources) - if (window->res->flags & IORESOURCE_BUS) { - found = true; - break; -diff --git a/arch/x86/pci/xen.c b/arch/x86/pci/xen.c -index 6b3cf7c..878fb8e 100644 ---- a/arch/x86/pci/xen.c -+++ b/arch/x86/pci/xen.c -@@ -229,7 +229,7 @@ static int xen_hvm_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) - return 1; - - list_for_each_entry(msidesc, &dev->msi_list, list) { -- __read_msi_msg(msidesc, &msg); -+ __pci_read_msi_msg(msidesc, &msg); - pirq = MSI_ADDR_EXT_DEST_ID(msg.address_hi) | - ((msg.address_lo >> MSI_ADDR_DEST_ID_SHIFT) & 0xff); - if (msg.data != XEN_PIRQ_MSI_DATA || -@@ -240,7 +240,7 @@ static int xen_hvm_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) - goto error; - } - xen_msi_compose_msg(dev, pirq, &msg); -- __write_msi_msg(msidesc, &msg); -+ __pci_write_msi_msg(msidesc, &msg); - dev_dbg(&dev->dev, "xen: msi bound to pirq=%d\n", pirq); - } else { - dev_dbg(&dev->dev, -@@ -296,12 +296,16 @@ static int xen_initdom_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) - map_irq.entry_nr = nvec; - } else if (type == PCI_CAP_ID_MSIX) { - int pos; -+ unsigned long flags; - u32 table_offset, bir; - - pos = dev->msix_cap; - pci_read_config_dword(dev, pos + PCI_MSIX_TABLE, - &table_offset); - bir = (u8)(table_offset & PCI_MSIX_TABLE_BIR); -+ flags = pci_resource_flags(dev, bir); -+ if (!flags || (flags & IORESOURCE_UNSET)) -+ return -EINVAL; - - map_irq.table_base = pci_resource_start(dev, bir); - map_irq.entry_nr = msidesc->msi_attrib.entry_nr; -@@ -394,14 +398,7 @@ static void xen_teardown_msi_irq(unsigned int irq) - { - xen_destroy_irq(irq); - } --static u32 xen_nop_msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) --{ -- return 0; --} --static u32 xen_nop_msix_mask_irq(struct msi_desc *desc, u32 flag) --{ -- return 0; --} -+ - #endif - - int __init pci_xen_init(void) -@@ -425,8 +422,7 @@ int __init pci_xen_init(void) - x86_msi.setup_msi_irqs = xen_setup_msi_irqs; - x86_msi.teardown_msi_irq = xen_teardown_msi_irq; - x86_msi.teardown_msi_irqs = xen_teardown_msi_irqs; -- x86_msi.msi_mask_irq = xen_nop_msi_mask_irq; -- x86_msi.msix_mask_irq = xen_nop_msix_mask_irq; -+ pci_msi_ignore_mask = 1; - #endif - return 0; - } -@@ -460,8 +456,7 @@ int __init pci_xen_initial_domain(void) - x86_msi.setup_msi_irqs = xen_initdom_setup_msi_irqs; - x86_msi.teardown_msi_irq = xen_teardown_msi_irq; - x86_msi.restore_msi_irqs = xen_initdom_restore_msi_irqs; -- x86_msi.msi_mask_irq = xen_nop_msi_mask_irq; -- x86_msi.msix_mask_irq = xen_nop_msix_mask_irq; -+ pci_msi_ignore_mask = 1; - #endif - __acpi_register_gsi = acpi_register_gsi_xen; - /* Pre-allocate legacy irqs */ -diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c -index fdb5701..0ad0ce6 100644 ---- a/drivers/acpi/acpi_lpss.c -+++ b/drivers/acpi/acpi_lpss.c -@@ -325,7 +325,7 @@ static int acpi_lpss_create_device(struct acpi_device *adev, - { - struct lpss_device_desc *dev_desc; - struct lpss_private_data *pdata; -- struct resource_list_entry *rentry; -+ struct resource_entry *rentry; - struct list_head resource_list; - struct platform_device *pdev; - int ret; -@@ -345,12 +345,12 @@ static int acpi_lpss_create_device(struct acpi_device *adev, - goto err_out; - - list_for_each_entry(rentry, &resource_list, node) -- if (resource_type(&rentry->res) == IORESOURCE_MEM) { -+ if (resource_type(rentry->res) == IORESOURCE_MEM) { - if (dev_desc->prv_size_override) - pdata->mmio_size = dev_desc->prv_size_override; - else -- pdata->mmio_size = resource_size(&rentry->res); -- pdata->mmio_base = ioremap(rentry->res.start, -+ pdata->mmio_size = resource_size(rentry->res); -+ pdata->mmio_base = ioremap(rentry->res->start, - pdata->mmio_size); - break; - } -diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c -index 6ba8beb..1284138 100644 ---- a/drivers/acpi/acpi_platform.c -+++ b/drivers/acpi/acpi_platform.c -@@ -45,7 +45,7 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev) - struct platform_device *pdev = NULL; - struct acpi_device *acpi_parent; - struct platform_device_info pdevinfo; -- struct resource_list_entry *rentry; -+ struct resource_entry *rentry; - struct list_head resource_list; - struct resource *resources = NULL; - int count; -@@ -71,7 +71,7 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev) - } - count = 0; - list_for_each_entry(rentry, &resource_list, node) -- resources[count++] = rentry->res; -+ resources[count++] = *rentry->res; - - acpi_dev_free_resource_list(&resource_list); - } -diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c -index 2ba8f02..e7f4aa0 100644 ---- a/drivers/acpi/resource.c -+++ b/drivers/acpi/resource.c -@@ -415,12 +415,7 @@ EXPORT_SYMBOL_GPL(acpi_dev_resource_interrupt); - */ - void acpi_dev_free_resource_list(struct list_head *list) - { -- struct resource_list_entry *rentry, *re; -- -- list_for_each_entry_safe(rentry, re, list, node) { -- list_del(&rentry->node); -- kfree(rentry); -- } -+ resource_list_free(list); - } - EXPORT_SYMBOL_GPL(acpi_dev_free_resource_list); - -@@ -435,15 +430,15 @@ struct res_proc_context { - static acpi_status acpi_dev_new_resource_entry(struct resource *r, - struct res_proc_context *c) - { -- struct resource_list_entry *rentry; -+ struct resource_entry *rentry; - -- rentry = kmalloc(sizeof(*rentry), GFP_KERNEL); -+ rentry = resource_list_create_entry(NULL, 0); - if (!rentry) { - c->error = -ENOMEM; - return AE_NO_MEMORY; - } -- rentry->res = *r; -- list_add_tail(&rentry->node, c->list); -+ *rentry->res = *r; -+ resource_list_add_tail(rentry, c->list); - c->count++; - return AE_OK; - } -@@ -503,7 +498,7 @@ static acpi_status acpi_dev_process_resource(struct acpi_resource *ares, - * returned as the final error code. - * - * The resultant struct resource objects are put on the list pointed to by -- * @list, that must be empty initially, as members of struct resource_list_entry -+ * @list, that must be empty initially, as members of struct resource_entry - * objects. Callers of this routine should use %acpi_dev_free_resource_list() to - * free that list. - * -diff --git a/drivers/base/core.c b/drivers/base/core.c -index 842d047..4c7a18f 100644 ---- a/drivers/base/core.c -+++ b/drivers/base/core.c -@@ -661,6 +661,9 @@ void device_initialize(struct device *dev) - INIT_LIST_HEAD(&dev->devres_head); - device_pm_init(dev); - set_dev_node(dev, -1); -+#ifdef CONFIG_GENERIC_MSI_IRQ -+ INIT_LIST_HEAD(&dev->msi_list); -+#endif - } - EXPORT_SYMBOL_GPL(device_initialize); - -diff --git a/drivers/base/platform.c b/drivers/base/platform.c -index 317e0e4..b387fb9 100644 ---- a/drivers/base/platform.c -+++ b/drivers/base/platform.c -@@ -1011,6 +1011,7 @@ int __init platform_bus_init(void) - error = bus_register(&platform_bus_type); - if (error) - device_unregister(&platform_bus); -+ of_platform_register_reconfig_notifier(); - return error; - } - -diff --git a/drivers/block/loop.c b/drivers/block/loop.c -index 6cb1beb..12678be 100644 ---- a/drivers/block/loop.c -+++ b/drivers/block/loop.c -@@ -692,6 +692,24 @@ static inline int is_loop_device(struct file *file) - return i && S_ISBLK(i->i_mode) && MAJOR(i->i_rdev) == LOOP_MAJOR; - } - -+/* -+ * for AUFS -+ * no get/put for file. -+ */ -+struct file *loop_backing_file(struct super_block *sb) -+{ -+ struct file *ret; -+ struct loop_device *l; -+ -+ ret = NULL; -+ if (MAJOR(sb->s_dev) == LOOP_MAJOR) { -+ l = sb->s_bdev->bd_disk->private_data; -+ ret = l->lo_backing_file; -+ } -+ return ret; -+} -+EXPORT_SYMBOL_GPL(loop_backing_file); -+ - /* loop sysfs attributes */ - - static ssize_t loop_attr_show(struct device *dev, char *page, -diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig -index 455fd17..38c8814 100644 ---- a/drivers/clk/Kconfig -+++ b/drivers/clk/Kconfig -@@ -101,12 +101,12 @@ config COMMON_CLK_AXI_CLKGEN - Support for the Analog Devices axi-clkgen pcore clock generator for Xilinx - FPGAs. It is commonly used in Analog Devices' reference designs. - --config CLK_PPC_CORENET -- bool "Clock driver for PowerPC corenet platforms" -- depends on PPC_E500MC && OF -+config CLK_QORIQ -+ bool "Clock driver for Freescale QorIQ platforms" -+ depends on (PPC_E500MC || ARM || ARM64) && OF - ---help--- -- This adds the clock driver support for Freescale PowerPC corenet -- platforms using common clock framework. -+ This adds the clock driver support for Freescale QorIQ platforms -+ using common clock framework. - - config COMMON_CLK_XGENE - bool "Clock driver for APM XGene SoC" -diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile -index d5fba5b..4ff94cd 100644 ---- a/drivers/clk/Makefile -+++ b/drivers/clk/Makefile -@@ -30,7 +30,7 @@ obj-$(CONFIG_ARCH_MOXART) += clk-moxart.o - obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o - obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o - obj-$(CONFIG_COMMON_CLK_PALMAS) += clk-palmas.o --obj-$(CONFIG_CLK_PPC_CORENET) += clk-ppc-corenet.o -+obj-$(CONFIG_CLK_QORIQ) += clk-qoriq.o - obj-$(CONFIG_COMMON_CLK_RK808) += clk-rk808.o - obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o - obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o -diff --git a/drivers/clk/clk-qoriq.c b/drivers/clk/clk-qoriq.c -new file mode 100644 -index 0000000..74051c9 ---- /dev/null -+++ b/drivers/clk/clk-qoriq.c -@@ -0,0 +1,1256 @@ -+/* -+ * Copyright 2013 Freescale Semiconductor, 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. -+ * -+ * clock driver for Freescale QorIQ SoCs. -+ */ -+ -+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define PLL_DIV1 0 -+#define PLL_DIV2 1 -+#define PLL_DIV3 2 -+#define PLL_DIV4 3 -+ -+#define PLATFORM_PLL 0 -+#define CGA_PLL1 1 -+#define CGA_PLL2 2 -+#define CGA_PLL3 3 -+#define CGA_PLL4 4 /* only on clockgen-1.0, which lacks CGB */ -+#define CGB_PLL1 4 -+#define CGB_PLL2 5 -+ -+struct clockgen_pll_div { -+ struct clk *clk; -+ char name[32]; -+}; -+ -+struct clockgen_pll { -+ struct clockgen_pll_div div[4]; -+}; -+ -+#define CLKSEL_VALID 1 -+#define CLKSEL_80PCT 2 /* Only allowed if PLL <= 80% of max cpu freq */ -+ -+struct clockgen_sourceinfo { -+ u32 flags; /* CLKSEL_xxx */ -+ int pll; /* CGx_PLLn */ -+ int div; /* PLL_DIVn */ -+}; -+ -+#define NUM_MUX_PARENTS 16 -+ -+struct clockgen_muxinfo { -+ struct clockgen_sourceinfo clksel[NUM_MUX_PARENTS]; -+}; -+ -+#define NUM_HWACCEL 5 -+#define NUM_CMUX 8 -+ -+struct clockgen; -+ -+/* -+ * cmux freq must be >= platform pll. -+ * If not set, cmux freq must be >= platform pll/2 -+ */ -+#define CG_CMUX_GE_PLAT 1 -+ -+#define CG_PLL_8BIT 2 /* PLLCnGSR[CFG] is 8 bits, not 6 */ -+#define CG_VER3 4 /* version 3 cg: reg layout different */ -+#define CG_LITTLE_ENDIAN 8 -+ -+struct clockgen_chipinfo { -+ const char *compat, *guts_compat; -+ const struct clockgen_muxinfo *cmux_groups[2]; -+ const struct clockgen_muxinfo *hwaccel[NUM_HWACCEL]; -+ void (*init_periph)(struct clockgen *cg); -+ int cmux_to_group[NUM_CMUX]; /* -1 terminates if fewer than NUM_CMUX */ -+ u32 pll_mask; /* 1 << n bit set if PLL n is valid */ -+ u32 flags; /* CG_xxx */ -+}; -+ -+struct clockgen { -+ struct device_node *node; -+ void __iomem *regs; -+ struct clockgen_chipinfo info; /* mutable copy */ -+ struct clk *sysclk; -+ struct clockgen_pll pll[6]; -+ struct clk *cmux[NUM_CMUX]; -+ struct clk *hwaccel[NUM_HWACCEL]; -+ struct clk *fman[2]; -+ struct ccsr_guts __iomem *guts; -+}; -+ -+static struct clockgen clockgen; -+ -+static void cg_out(struct clockgen *cg, u32 val, u32 __iomem *reg) -+{ -+ if (cg->info.flags & CG_LITTLE_ENDIAN) -+ iowrite32(val, reg); -+ else -+ iowrite32be(val, reg); -+} -+ -+static u32 cg_in(struct clockgen *cg, u32 __iomem *reg) -+{ -+ u32 val; -+ -+ if (cg->info.flags & CG_LITTLE_ENDIAN) -+ val = ioread32(reg); -+ else -+ val = ioread32be(reg); -+ -+ return val; -+} -+ -+static const struct clockgen_muxinfo p2041_cmux_grp1 = { -+ { -+ [0] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 }, -+ [1] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 }, -+ [4] = { CLKSEL_VALID, CGA_PLL2, PLL_DIV1 }, -+ } -+}; -+ -+static const struct clockgen_muxinfo p2041_cmux_grp2 = { -+ { -+ [0] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 }, -+ [4] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 }, -+ [5] = { CLKSEL_VALID, CGA_PLL2, PLL_DIV1 }, -+ } -+}; -+ -+static const struct clockgen_muxinfo p5020_cmux_grp1 = { -+ { -+ [0] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 }, -+ [1] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 }, -+ [4] = { CLKSEL_VALID | CLKSEL_80PCT, CGA_PLL2, PLL_DIV1 }, -+ } -+}; -+ -+static const struct clockgen_muxinfo p5020_cmux_grp2 = { -+ { -+ [0] = { CLKSEL_VALID | CLKSEL_80PCT, CGA_PLL1, PLL_DIV1 }, -+ [4] = { CLKSEL_VALID, CGA_PLL2, PLL_DIV1 }, -+ [5] = { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 }, -+ } -+}; -+ -+static const struct clockgen_muxinfo p5040_cmux_grp1 = { -+ { -+ [0] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 }, -+ [1] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 }, -+ [4] = { CLKSEL_VALID | CLKSEL_80PCT, CGA_PLL2, PLL_DIV1 }, -+ [5] = { CLKSEL_VALID | CLKSEL_80PCT, CGA_PLL2, PLL_DIV2 }, -+ } -+}; -+ -+static const struct clockgen_muxinfo p5040_cmux_grp2 = { -+ { -+ [0] = { CLKSEL_VALID | CLKSEL_80PCT, CGA_PLL1, PLL_DIV1 }, -+ [1] = { CLKSEL_VALID | CLKSEL_80PCT, CGA_PLL1, PLL_DIV2 }, -+ [4] = { CLKSEL_VALID, CGA_PLL2, PLL_DIV1 }, -+ [5] = { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 }, -+ } -+}; -+ -+static const struct clockgen_muxinfo p4080_cmux_grp1 = { -+ { -+ [0] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 }, -+ [1] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 }, -+ [4] = { CLKSEL_VALID, CGA_PLL2, PLL_DIV1 }, -+ [5] = { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 }, -+ [8] = { CLKSEL_VALID | CLKSEL_80PCT, CGA_PLL3, PLL_DIV1 }, -+ } -+}; -+ -+static const struct clockgen_muxinfo p4080_cmux_grp2 = { -+ { -+ [0] = { CLKSEL_VALID | CLKSEL_80PCT, CGA_PLL1, PLL_DIV1 }, -+ [8] = { CLKSEL_VALID, CGA_PLL3, PLL_DIV1 }, -+ [9] = { CLKSEL_VALID, CGA_PLL3, PLL_DIV2 }, -+ [12] = { CLKSEL_VALID, CGA_PLL4, PLL_DIV1 }, -+ [13] = { CLKSEL_VALID, CGA_PLL4, PLL_DIV2 }, -+ } -+}; -+ -+static const struct clockgen_muxinfo t1023_cmux = { -+ { -+ [0] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 }, -+ [1] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 }, -+ } -+}; -+ -+static const struct clockgen_muxinfo t1040_cmux = { -+ { -+ [0] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 }, -+ [1] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 }, -+ [4] = { CLKSEL_VALID, CGA_PLL2, PLL_DIV1 }, -+ [5] = { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 }, -+ } -+}; -+ -+ -+static const struct clockgen_muxinfo clockgen2_cmux_cga = { -+ { -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 }, -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 }, -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV4 }, -+ {}, -+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV1 }, -+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 }, -+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV4 }, -+ {}, -+ { CLKSEL_VALID, CGA_PLL3, PLL_DIV1 }, -+ { CLKSEL_VALID, CGA_PLL3, PLL_DIV2 }, -+ { CLKSEL_VALID, CGA_PLL3, PLL_DIV4 }, -+ }, -+}; -+ -+static const struct clockgen_muxinfo clockgen2_cmux_cga12 = { -+ { -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 }, -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 }, -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV4 }, -+ {}, -+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV1 }, -+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 }, -+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV4 }, -+ }, -+}; -+ -+static const struct clockgen_muxinfo clockgen2_cmux_cgb = { -+ { -+ { CLKSEL_VALID, CGB_PLL1, PLL_DIV1 }, -+ { CLKSEL_VALID, CGB_PLL1, PLL_DIV2 }, -+ { CLKSEL_VALID, CGB_PLL1, PLL_DIV4 }, -+ {}, -+ { CLKSEL_VALID, CGB_PLL2, PLL_DIV1 }, -+ { CLKSEL_VALID, CGB_PLL2, PLL_DIV2 }, -+ { CLKSEL_VALID, CGB_PLL2, PLL_DIV4 }, -+ }, -+}; -+ -+static const struct clockgen_muxinfo t1023_hwa1 = { -+ { -+ {}, -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 }, -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 }, -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV3 }, -+ }, -+}; -+ -+static const struct clockgen_muxinfo t1023_hwa2 = { -+ { -+ [6] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 }, -+ }, -+}; -+ -+static const struct clockgen_muxinfo t2080_hwa1 = { -+ { -+ {}, -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 }, -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 }, -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV3 }, -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV4 }, -+ { CLKSEL_VALID, PLATFORM_PLL, PLL_DIV1 }, -+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 }, -+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV3 }, -+ }, -+}; -+ -+static const struct clockgen_muxinfo t2080_hwa2 = { -+ { -+ {}, -+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV1 }, -+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 }, -+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV3 }, -+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV4 }, -+ { CLKSEL_VALID, PLATFORM_PLL, PLL_DIV1 }, -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 }, -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV3 }, -+ }, -+}; -+ -+static const struct clockgen_muxinfo t4240_hwa1 = { -+ { -+ { CLKSEL_VALID, PLATFORM_PLL, PLL_DIV2 }, -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 }, -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 }, -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV3 }, -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV4 }, -+ {}, -+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 }, -+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV3 }, -+ }, -+}; -+ -+static const struct clockgen_muxinfo t4240_hwa4 = { -+ { -+ [2] = { CLKSEL_VALID, CGB_PLL1, PLL_DIV2 }, -+ [3] = { CLKSEL_VALID, CGB_PLL1, PLL_DIV3 }, -+ [4] = { CLKSEL_VALID, CGB_PLL1, PLL_DIV4 }, -+ [5] = { CLKSEL_VALID, PLATFORM_PLL, PLL_DIV1 }, -+ [6] = { CLKSEL_VALID, CGB_PLL2, PLL_DIV2 }, -+ }, -+}; -+ -+static const struct clockgen_muxinfo t4240_hwa5 = { -+ { -+ [2] = { CLKSEL_VALID, CGB_PLL2, PLL_DIV2 }, -+ [3] = { CLKSEL_VALID, CGB_PLL2, PLL_DIV3 }, -+ [4] = { CLKSEL_VALID, CGB_PLL2, PLL_DIV4 }, -+ [5] = { CLKSEL_VALID, PLATFORM_PLL, PLL_DIV1 }, -+ [6] = { CLKSEL_VALID, CGB_PLL1, PLL_DIV2 }, -+ [7] = { CLKSEL_VALID, CGB_PLL1, PLL_DIV3 }, -+ }, -+}; -+ -+#define RCWSR7_FM1_CLK_SEL 0x40000000 -+#define RCWSR7_FM2_CLK_SEL 0x20000000 -+#define RCWSR7_HWA_ASYNC_DIV 0x04000000 -+ -+static void __init p2041_init_periph(struct clockgen *cg) -+{ -+ u32 reg; -+ -+ reg = ioread32be(&cg->guts->rcwsr[7]); -+ -+ if (reg & RCWSR7_FM1_CLK_SEL) -+ cg->fman[0] = cg->pll[CGA_PLL2].div[PLL_DIV2].clk; -+ else -+ cg->fman[0] = cg->pll[PLATFORM_PLL].div[PLL_DIV2].clk; -+} -+ -+static void __init p4080_init_periph(struct clockgen *cg) -+{ -+ u32 reg; -+ -+ reg = ioread32be(&cg->guts->rcwsr[7]); -+ -+ if (reg & RCWSR7_FM1_CLK_SEL) -+ cg->fman[0] = cg->pll[CGA_PLL3].div[PLL_DIV2].clk; -+ else -+ cg->fman[0] = cg->pll[PLATFORM_PLL].div[PLL_DIV2].clk; -+ -+ if (reg & RCWSR7_FM2_CLK_SEL) -+ cg->fman[1] = cg->pll[CGA_PLL3].div[PLL_DIV2].clk; -+ else -+ cg->fman[1] = cg->pll[PLATFORM_PLL].div[PLL_DIV2].clk; -+} -+ -+static void __init p5020_init_periph(struct clockgen *cg) -+{ -+ u32 reg; -+ int div = PLL_DIV2; -+ -+ reg = ioread32be(&cg->guts->rcwsr[7]); -+ if (reg & RCWSR7_HWA_ASYNC_DIV) -+ div = PLL_DIV4; -+ -+ if (reg & RCWSR7_FM1_CLK_SEL) -+ cg->fman[0] = cg->pll[CGA_PLL2].div[div].clk; -+ else -+ cg->fman[0] = cg->pll[PLATFORM_PLL].div[PLL_DIV2].clk; -+} -+ -+static void __init p5040_init_periph(struct clockgen *cg) -+{ -+ u32 reg; -+ int div = PLL_DIV2; -+ -+ reg = ioread32be(&cg->guts->rcwsr[7]); -+ if (reg & RCWSR7_HWA_ASYNC_DIV) -+ div = PLL_DIV4; -+ -+ if (reg & RCWSR7_FM1_CLK_SEL) -+ cg->fman[0] = cg->pll[CGA_PLL3].div[div].clk; -+ else -+ cg->fman[0] = cg->pll[PLATFORM_PLL].div[PLL_DIV2].clk; -+ -+ if (reg & RCWSR7_FM2_CLK_SEL) -+ cg->fman[1] = cg->pll[CGA_PLL3].div[div].clk; -+ else -+ cg->fman[1] = cg->pll[PLATFORM_PLL].div[PLL_DIV2].clk; -+} -+ -+static void __init t1023_init_periph(struct clockgen *cg) -+{ -+ cg->fman[0] = cg->hwaccel[1]; -+} -+ -+static void __init t1040_init_periph(struct clockgen *cg) -+{ -+ cg->fman[0] = cg->pll[PLATFORM_PLL].div[PLL_DIV1].clk; -+} -+ -+static void __init t2080_init_periph(struct clockgen *cg) -+{ -+ cg->fman[0] = cg->hwaccel[0]; -+} -+ -+static void __init t4240_init_periph(struct clockgen *cg) -+{ -+ cg->fman[0] = cg->hwaccel[3]; -+ cg->fman[1] = cg->hwaccel[4]; -+} -+ -+static const struct clockgen_chipinfo chipinfo[] = { -+ { -+ .compat = "fsl,b4420-clockgen", -+ .guts_compat = "fsl,b4860-device-config", -+ .init_periph = t2080_init_periph, -+ .cmux_groups = { -+ &clockgen2_cmux_cga12, &clockgen2_cmux_cgb -+ }, -+ .hwaccel = { -+ &t2080_hwa1 -+ }, -+ .cmux_to_group = { -+ 0, 1, 1, 1, -1 -+ }, -+ .pll_mask = 0x3f, -+ .flags = CG_PLL_8BIT, -+ }, -+ { -+ .compat = "fsl,b4860-clockgen", -+ .guts_compat = "fsl,b4860-device-config", -+ .init_periph = t2080_init_periph, -+ .cmux_groups = { -+ &clockgen2_cmux_cga12, &clockgen2_cmux_cgb -+ }, -+ .hwaccel = { -+ &t2080_hwa1 -+ }, -+ .cmux_to_group = { -+ 0, 1, 1, 1, -1 -+ }, -+ .pll_mask = 0x3f, -+ .flags = CG_PLL_8BIT, -+ }, -+ { -+ .compat = "fsl,ls1021a-clockgen", -+ .cmux_groups = { -+ &t1023_cmux -+ }, -+ .cmux_to_group = { -+ 0, -1 -+ }, -+ .pll_mask = 0x03, -+ }, -+ { -+ .compat = "fsl,ls2080a-clockgen", -+ .cmux_groups = { -+ &clockgen2_cmux_cga12, &clockgen2_cmux_cgb -+ }, -+ .cmux_to_group = { -+ 0, 0, 1, 1, -1 -+ }, -+ .pll_mask = 0x37, -+ .flags = CG_VER3 | CG_LITTLE_ENDIAN, -+ }, -+ { -+ .compat = "fsl,ls2088a-clockgen", -+ .cmux_groups = { -+ &clockgen2_cmux_cga12, &clockgen2_cmux_cgb -+ }, -+ .cmux_to_group = { -+ 0, 0, 1, 1, -1 -+ }, -+ .pll_mask = 0x37, -+ .flags = CG_VER3 | CG_LITTLE_ENDIAN, -+ }, -+ { -+ .compat = "fsl,p2041-clockgen", -+ .guts_compat = "fsl,qoriq-device-config-1.0", -+ .init_periph = p2041_init_periph, -+ .cmux_groups = { -+ &p2041_cmux_grp1, &p2041_cmux_grp2 -+ }, -+ .cmux_to_group = { -+ 0, 0, 1, 1, -1 -+ }, -+ .pll_mask = 0x07, -+ }, -+ { -+ .compat = "fsl,p3041-clockgen", -+ .guts_compat = "fsl,qoriq-device-config-1.0", -+ .init_periph = p2041_init_periph, -+ .cmux_groups = { -+ &p2041_cmux_grp1, &p2041_cmux_grp2 -+ }, -+ .cmux_to_group = { -+ 0, 0, 1, 1, -1 -+ }, -+ .pll_mask = 0x07, -+ }, -+ { -+ .compat = "fsl,p4080-clockgen", -+ .guts_compat = "fsl,qoriq-device-config-1.0", -+ .init_periph = p4080_init_periph, -+ .cmux_groups = { -+ &p4080_cmux_grp1, &p4080_cmux_grp2 -+ }, -+ .cmux_to_group = { -+ 0, 0, 0, 0, 1, 1, 1, 1 -+ }, -+ .pll_mask = 0x1f, -+ }, -+ { -+ .compat = "fsl,p5020-clockgen", -+ .guts_compat = "fsl,qoriq-device-config-1.0", -+ .init_periph = p5020_init_periph, -+ .cmux_groups = { -+ &p2041_cmux_grp1, &p2041_cmux_grp2 -+ }, -+ .cmux_to_group = { -+ 0, 1, -1 -+ }, -+ .pll_mask = 0x07, -+ }, -+ { -+ .compat = "fsl,p5040-clockgen", -+ .guts_compat = "fsl,p5040-device-config", -+ .init_periph = p5040_init_periph, -+ .cmux_groups = { -+ &p5040_cmux_grp1, &p5040_cmux_grp2 -+ }, -+ .cmux_to_group = { -+ 0, 0, 1, 1, -1 -+ }, -+ .pll_mask = 0x0f, -+ }, -+ { -+ .compat = "fsl,t1023-clockgen", -+ .guts_compat = "fsl,t1023-device-config", -+ .init_periph = t1023_init_periph, -+ .cmux_groups = { -+ &t1023_cmux -+ }, -+ .hwaccel = { -+ &t1023_hwa1, &t1023_hwa2 -+ }, -+ .cmux_to_group = { -+ 0, 0, -1 -+ }, -+ .pll_mask = 0x03, -+ .flags = CG_PLL_8BIT, -+ }, -+ { -+ .compat = "fsl,t1040-clockgen", -+ .guts_compat = "fsl,t1040-device-config", -+ .init_periph = t1040_init_periph, -+ .cmux_groups = { -+ &t1040_cmux -+ }, -+ .cmux_to_group = { -+ 0, 0, 0, 0, -1 -+ }, -+ .pll_mask = 0x07, -+ .flags = CG_PLL_8BIT, -+ }, -+ { -+ .compat = "fsl,t2080-clockgen", -+ .guts_compat = "fsl,t2080-device-config", -+ .init_periph = t2080_init_periph, -+ .cmux_groups = { -+ &clockgen2_cmux_cga12 -+ }, -+ .hwaccel = { -+ &t2080_hwa1, &t2080_hwa2 -+ }, -+ .cmux_to_group = { -+ 0, -1 -+ }, -+ .pll_mask = 0x07, -+ .flags = CG_PLL_8BIT, -+ }, -+ { -+ .compat = "fsl,t4240-clockgen", -+ .guts_compat = "fsl,t4240-device-config", -+ .init_periph = t4240_init_periph, -+ .cmux_groups = { -+ &clockgen2_cmux_cga, &clockgen2_cmux_cgb -+ }, -+ .hwaccel = { -+ &t4240_hwa1, NULL, NULL, &t4240_hwa4, &t4240_hwa5 -+ }, -+ .cmux_to_group = { -+ 0, 0, 1, -1 -+ }, -+ .pll_mask = 0x3f, -+ .flags = CG_PLL_8BIT, -+ }, -+ {}, -+}; -+ -+struct mux_hwclock { -+ struct clk_hw hw; -+ struct clockgen *cg; -+ const struct clockgen_muxinfo *info; -+ u32 __iomem *reg; -+ u8 parent_to_clksel[NUM_MUX_PARENTS]; -+ s8 clksel_to_parent[NUM_MUX_PARENTS]; -+ int num_parents; -+}; -+ -+#define to_mux_hwclock(p) container_of(p, struct mux_hwclock, hw) -+#define CLKSEL_MASK 0x78000000 -+#define CLKSEL_SHIFT 27 -+ -+static int mux_set_parent(struct clk_hw *hw, u8 idx) -+{ -+ struct mux_hwclock *hwc = to_mux_hwclock(hw); -+ u32 clksel; -+ -+ if (idx >= hwc->num_parents) -+ return -EINVAL; -+ -+ clksel = hwc->parent_to_clksel[idx]; -+ cg_out(hwc->cg, (clksel << CLKSEL_SHIFT) & CLKSEL_MASK, hwc->reg); -+ -+ return 0; -+} -+ -+static u8 mux_get_parent(struct clk_hw *hw) -+{ -+ struct mux_hwclock *hwc = to_mux_hwclock(hw); -+ u32 clksel; -+ s8 ret; -+ -+ clksel = (cg_in(hwc->cg, hwc->reg) & CLKSEL_MASK) >> CLKSEL_SHIFT; -+ -+ ret = hwc->clksel_to_parent[clksel]; -+ if (ret < 0) { -+ pr_err("%s: mux at %p has bad clksel\n", __func__, hwc->reg); -+ return 0; -+ } -+ -+ return ret; -+} -+ -+static const struct clk_ops cmux_ops = { -+ .get_parent = mux_get_parent, -+ .set_parent = mux_set_parent, -+}; -+ -+/* -+ * Don't allow setting for now, as the clock options haven't been -+ * sanitized for additional restrictions. -+ */ -+static const struct clk_ops hwaccel_ops = { -+ .get_parent = mux_get_parent, -+}; -+ -+static const struct clockgen_pll_div *get_pll_div(struct clockgen *cg, -+ struct mux_hwclock *hwc, -+ int idx) -+{ -+ int pll, div; -+ -+ if (!(hwc->info->clksel[idx].flags & CLKSEL_VALID)) -+ return NULL; -+ -+ pll = hwc->info->clksel[idx].pll; -+ div = hwc->info->clksel[idx].div; -+ -+ return &cg->pll[pll].div[div]; -+} -+ -+static struct clk * __init create_mux_common(struct clockgen *cg, -+ struct mux_hwclock *hwc, -+ const struct clk_ops *ops, -+ unsigned long min_rate, -+ unsigned long pct80_rate, -+ const char *fmt, int idx) -+{ -+ struct clk_init_data init = {}; -+ struct clk *clk; -+ const struct clockgen_pll_div *div; -+ const char *parent_names[NUM_MUX_PARENTS]; -+ char name[32]; -+ int i, j; -+ -+ snprintf(name, sizeof(name), fmt, idx); -+ -+ for (i = 0, j = 0; i < NUM_MUX_PARENTS; i++) { -+ unsigned long rate; -+ -+ hwc->clksel_to_parent[i] = -1; -+ -+ div = get_pll_div(cg, hwc, i); -+ if (!div) -+ continue; -+ -+ rate = clk_get_rate(div->clk); -+ -+ if (hwc->info->clksel[i].flags & CLKSEL_80PCT && -+ rate > pct80_rate) -+ continue; -+ if (rate < min_rate) -+ continue; -+ -+ parent_names[j] = div->name; -+ hwc->parent_to_clksel[j] = i; -+ hwc->clksel_to_parent[i] = j; -+ j++; -+ } -+ -+ init.name = name; -+ init.ops = ops; -+ init.parent_names = parent_names; -+ init.num_parents = hwc->num_parents = j; -+ init.flags = 0; -+ hwc->hw.init = &init; -+ hwc->cg = cg; -+ -+ clk = clk_register(NULL, &hwc->hw); -+ if (IS_ERR(clk)) { -+ pr_err("%s: Couldn't register %s: %ld\n", __func__, name, -+ PTR_ERR(clk)); -+ kfree(hwc); -+ return NULL; -+ } -+ -+ return clk; -+} -+ -+static struct clk * __init create_one_cmux(struct clockgen *cg, int idx) -+{ -+ struct mux_hwclock *hwc; -+ const struct clockgen_pll_div *div; -+ unsigned long plat_rate, min_rate; -+ u64 pct80_rate; -+ u32 clksel; -+ -+ hwc = kzalloc(sizeof(*hwc), GFP_KERNEL); -+ if (!hwc) -+ return NULL; -+ -+ if (cg->info.flags & CG_VER3) -+ hwc->reg = cg->regs + 0x70000 + 0x20 * idx; -+ else -+ hwc->reg = cg->regs + 0x20 * idx; -+ -+ hwc->info = cg->info.cmux_groups[cg->info.cmux_to_group[idx]]; -+ -+ /* -+ * Find the rate for the default clksel, and treat it as the -+ * maximum rated core frequency. If this is an incorrect -+ * assumption, certain clock options (possibly including the -+ * default clksel) may be inappropriately excluded on certain -+ * chips. -+ */ -+ clksel = (cg_in(cg, hwc->reg) & CLKSEL_MASK) >> CLKSEL_SHIFT; -+ div = get_pll_div(cg, hwc, clksel); -+ if (!div) -+ return NULL; -+ -+ pct80_rate = clk_get_rate(div->clk); -+ pct80_rate *= 8; -+ do_div(pct80_rate, 10); -+ -+ plat_rate = clk_get_rate(cg->pll[PLATFORM_PLL].div[PLL_DIV1].clk); -+ -+ if (cg->info.flags & CG_CMUX_GE_PLAT) -+ min_rate = plat_rate; -+ else -+ min_rate = plat_rate / 2; -+ -+ return create_mux_common(cg, hwc, &cmux_ops, min_rate, -+ pct80_rate, "cg-cmux%d", idx); -+} -+ -+static struct clk * __init create_one_hwaccel(struct clockgen *cg, int idx) -+{ -+ struct mux_hwclock *hwc; -+ -+ hwc = kzalloc(sizeof(*hwc), GFP_KERNEL); -+ if (!hwc) -+ return NULL; -+ -+ hwc->reg = cg->regs + 0x20 * idx + 0x10; -+ hwc->info = cg->info.hwaccel[idx]; -+ -+ return create_mux_common(cg, hwc, &hwaccel_ops, 0, 0, -+ "cg-hwaccel%d", idx); -+} -+ -+static void __init create_muxes(struct clockgen *cg) -+{ -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(cg->cmux); i++) { -+ if (cg->info.cmux_to_group[i] < 0) -+ break; -+ if (cg->info.cmux_to_group[i] >= -+ ARRAY_SIZE(cg->info.cmux_groups)) { -+ WARN_ON_ONCE(1); -+ continue; -+ } -+ -+ cg->cmux[i] = create_one_cmux(cg, i); -+ } -+ -+ for (i = 0; i < ARRAY_SIZE(cg->hwaccel); i++) { -+ if (!cg->info.hwaccel[i]) -+ continue; -+ -+ cg->hwaccel[i] = create_one_hwaccel(cg, i); -+ } -+} -+ -+static void __init clockgen_init(struct device_node *np); -+ -+/* Legacy nodes may get probed before the parent clockgen node */ -+static void __init legacy_init_clockgen(struct device_node *np) -+{ -+ if (!clockgen.node) -+ clockgen_init(of_get_parent(np)); -+} -+ -+/* Legacy node */ -+static void __init core_mux_init(struct device_node *np) -+{ -+ struct clk *clk; -+ struct resource res; -+ int idx, rc; -+ -+ legacy_init_clockgen(np); -+ -+ if (of_address_to_resource(np, 0, &res)) -+ return; -+ -+ idx = (res.start & 0xf0) >> 5; -+ clk = clockgen.cmux[idx]; -+ -+ rc = of_clk_add_provider(np, of_clk_src_simple_get, clk); -+ if (rc) { -+ pr_err("%s: Couldn't register clk provider for node %s: %d\n", -+ __func__, np->name, rc); -+ return; -+ } -+} -+ -+static struct clk *sysclk_from_fixed(struct device_node *node, const char *name) -+{ -+ u32 rate; -+ -+ if (of_property_read_u32(node, "clock-frequency", &rate)) -+ return ERR_PTR(-ENODEV); -+ -+ return clk_register_fixed_rate(NULL, name, NULL, CLK_IS_ROOT, rate); -+} -+ -+static struct clk *sysclk_from_parent(const char *name) -+{ -+ struct clk *clk; -+ const char *parent_name; -+ -+ clk = of_clk_get(clockgen.node, 0); -+ if (IS_ERR(clk)) -+ return clk; -+ -+ /* Register the input clock under the desired name. */ -+ parent_name = __clk_get_name(clk); -+ clk = clk_register_fixed_factor(NULL, name, parent_name, -+ 0, 1, 1); -+ if (IS_ERR(clk)) -+ pr_err("%s: Couldn't register %s: %ld\n", __func__, name, -+ PTR_ERR(clk)); -+ -+ return clk; -+} -+ -+static struct clk * __init create_sysclk(const char *name) -+{ -+ struct device_node *sysclk; -+ struct clk *clk; -+ -+ clk = sysclk_from_fixed(clockgen.node, name); -+ if (!IS_ERR(clk)) -+ return clk; -+ -+ clk = sysclk_from_parent(name); -+ if (!IS_ERR(clk)) -+ return clk; -+ -+ sysclk = of_get_child_by_name(clockgen.node, "sysclk"); -+ if (sysclk) { -+ clk = sysclk_from_fixed(sysclk, name); -+ if (!IS_ERR(clk)) -+ return clk; -+ } -+ -+ pr_err("%s: No input clock\n", __func__); -+ return NULL; -+} -+ -+/* Legacy node */ -+static void __init sysclk_init(struct device_node *node) -+{ -+ struct clk *clk; -+ -+ legacy_init_clockgen(node); -+ -+ clk = clockgen.sysclk; -+ if (clk) -+ of_clk_add_provider(node, of_clk_src_simple_get, clk); -+} -+ -+#define PLL_KILL BIT(31) -+ -+static void __init create_one_pll(struct clockgen *cg, int idx) -+{ -+ u32 __iomem *reg; -+ u32 mult; -+ struct clockgen_pll *pll = &cg->pll[idx]; -+ int i; -+ -+ if (!(cg->info.pll_mask & (1 << idx))) -+ return; -+ -+ if (cg->info.flags & CG_VER3) { -+ switch (idx) { -+ case PLATFORM_PLL: -+ reg = cg->regs + 0x60080; -+ break; -+ case CGA_PLL1: -+ reg = cg->regs + 0x80; -+ break; -+ case CGA_PLL2: -+ reg = cg->regs + 0xa0; -+ break; -+ case CGB_PLL1: -+ reg = cg->regs + 0x10080; -+ break; -+ case CGB_PLL2: -+ reg = cg->regs + 0x100a0; -+ break; -+ default: -+ WARN_ONCE(1, "index %d\n", idx); -+ return; -+ } -+ } else { -+ if (idx == PLATFORM_PLL) -+ reg = cg->regs + 0xc00; -+ else -+ reg = cg->regs + 0x800 + 0x20 * (idx - 1); -+ } -+ -+ /* Get the multiple of PLL */ -+ mult = cg_in(cg, reg); -+ -+ /* Check if this PLL is disabled */ -+ if (mult & PLL_KILL) { -+ pr_debug("%s(): pll %p disabled\n", __func__, reg); -+ return; -+ } -+ -+ if ((cg->info.flags & CG_VER3) || -+ ((cg->info.flags & CG_PLL_8BIT) && idx != PLATFORM_PLL)) -+ mult = (mult & GENMASK(8, 1)) >> 1; -+ else -+ mult = (mult & GENMASK(6, 1)) >> 1; -+ -+ for (i = 0; i < ARRAY_SIZE(pll->div); i++) { -+ struct clk *clk; -+ -+ snprintf(pll->div[i].name, sizeof(pll->div[i].name), -+ "cg-pll%d-div%d", idx, i + 1); -+ -+ clk = clk_register_fixed_factor(NULL, -+ pll->div[i].name, "cg-sysclk", 0, mult, i + 1); -+ if (IS_ERR(clk)) { -+ pr_err("%s: %s: register failed %ld\n", -+ __func__, pll->div[i].name, PTR_ERR(clk)); -+ continue; -+ } -+ -+ pll->div[i].clk = clk; -+ } -+} -+ -+static void __init create_plls(struct clockgen *cg) -+{ -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(cg->pll); i++) -+ create_one_pll(cg, i); -+} -+ -+static void __init legacy_pll_init(struct device_node *np, int idx) -+{ -+ struct clockgen_pll *pll; -+ struct clk_onecell_data *onecell_data; -+ struct clk **subclks; -+ int count, rc; -+ -+ legacy_init_clockgen(np); -+ -+ pll = &clockgen.pll[idx]; -+ count = of_property_count_strings(np, "clock-output-names"); -+ -+ BUILD_BUG_ON(ARRAY_SIZE(pll->div) < 4); -+ subclks = kcalloc(4, sizeof(struct clk *), GFP_KERNEL); -+ if (!subclks) -+ return; -+ -+ onecell_data = kmalloc(sizeof(*onecell_data), GFP_KERNEL); -+ if (!onecell_data) -+ goto err_clks; -+ -+ if (count <= 3) { -+ subclks[0] = pll->div[0].clk; -+ subclks[1] = pll->div[1].clk; -+ subclks[2] = pll->div[3].clk; -+ } else { -+ subclks[0] = pll->div[0].clk; -+ subclks[1] = pll->div[1].clk; -+ subclks[2] = pll->div[2].clk; -+ subclks[3] = pll->div[3].clk; -+ } -+ -+ onecell_data->clks = subclks; -+ onecell_data->clk_num = count; -+ -+ rc = of_clk_add_provider(np, of_clk_src_onecell_get, onecell_data); -+ if (rc) { -+ pr_err("%s: Couldn't register clk provider for node %s: %d\n", -+ __func__, np->name, rc); -+ goto err_cell; -+ } -+ -+ return; -+err_cell: -+ kfree(onecell_data); -+err_clks: -+ kfree(subclks); -+} -+ -+/* Legacy node */ -+static void __init pltfrm_pll_init(struct device_node *np) -+{ -+ legacy_pll_init(np, PLATFORM_PLL); -+} -+ -+/* Legacy node */ -+static void __init core_pll_init(struct device_node *np) -+{ -+ struct resource res; -+ int idx; -+ -+ if (of_address_to_resource(np, 0, &res)) -+ return; -+ -+ if ((res.start & 0xfff) == 0xc00) { -+ /* -+ * ls1021a devtree labels the platform PLL -+ * with the core PLL compatible -+ */ -+ pltfrm_pll_init(np); -+ } else { -+ idx = (res.start & 0xf0) >> 5; -+ legacy_pll_init(np, CGA_PLL1 + idx); -+ } -+} -+ -+static struct clk *clockgen_clk_get(struct of_phandle_args *clkspec, void *data) -+{ -+ struct clockgen *cg = data; -+ struct clk *clk; -+ struct clockgen_pll *pll; -+ u32 type, idx; -+ -+ if (clkspec->args_count < 2) { -+ pr_err("%s: insufficient phandle args\n", __func__); -+ return ERR_PTR(-EINVAL); -+ } -+ -+ type = clkspec->args[0]; -+ idx = clkspec->args[1]; -+ -+ switch (type) { -+ case 0: -+ if (idx != 0) -+ goto bad_args; -+ clk = cg->sysclk; -+ break; -+ case 1: -+ if (idx >= ARRAY_SIZE(cg->cmux)) -+ goto bad_args; -+ clk = cg->cmux[idx]; -+ break; -+ case 2: -+ if (idx >= ARRAY_SIZE(cg->hwaccel)) -+ goto bad_args; -+ clk = cg->hwaccel[idx]; -+ break; -+ case 3: -+ if (idx >= ARRAY_SIZE(cg->fman)) -+ goto bad_args; -+ clk = cg->fman[idx]; -+ break; -+ case 4: -+ pll = &cg->pll[PLATFORM_PLL]; -+ if (idx >= ARRAY_SIZE(pll->div)) -+ goto bad_args; -+ clk = pll->div[idx].clk; -+ break; -+ default: -+ goto bad_args; -+ } -+ -+ if (!clk) -+ return ERR_PTR(-ENOENT); -+ return clk; -+ -+bad_args: -+ pr_err("%s: Bad phandle args %u %u\n", __func__, type, idx); -+ return ERR_PTR(-EINVAL); -+} -+ -+#ifdef CONFIG_PPC -+ -+static const u32 a4510_svrs[] __initconst = { -+ (SVR_P2040 << 8) | 0x10, /* P2040 1.0 */ -+ (SVR_P2040 << 8) | 0x11, /* P2040 1.1 */ -+ (SVR_P2041 << 8) | 0x10, /* P2041 1.0 */ -+ (SVR_P2041 << 8) | 0x11, /* P2041 1.1 */ -+ (SVR_P3041 << 8) | 0x10, /* P3041 1.0 */ -+ (SVR_P3041 << 8) | 0x11, /* P3041 1.1 */ -+ (SVR_P4040 << 8) | 0x20, /* P4040 2.0 */ -+ (SVR_P4080 << 8) | 0x20, /* P4080 2.0 */ -+ (SVR_P5010 << 8) | 0x10, /* P5010 1.0 */ -+ (SVR_P5010 << 8) | 0x20, /* P5010 2.0 */ -+ (SVR_P5020 << 8) | 0x10, /* P5020 1.0 */ -+ (SVR_P5021 << 8) | 0x10, /* P5021 1.0 */ -+ (SVR_P5040 << 8) | 0x10, /* P5040 1.0 */ -+}; -+ -+#define SVR_SECURITY 0x80000 /* The Security (E) bit */ -+ -+static bool __init has_erratum_a4510(void) -+{ -+ u32 svr = mfspr(SPRN_SVR); -+ int i; -+ -+ svr &= ~SVR_SECURITY; -+ -+ for (i = 0; i < ARRAY_SIZE(a4510_svrs); i++) { -+ if (svr == a4510_svrs[i]) -+ return true; -+ } -+ -+ return false; -+} -+#else -+static bool __init has_erratum_a4510(void) -+{ -+ return false; -+} -+#endif -+ -+static void __init clockgen_init(struct device_node *np) -+{ -+ int i, ret; -+ bool is_old_ls1021a = false; -+ -+ /* May have already been called by a legacy probe */ -+ if (clockgen.node) -+ return; -+ -+ clockgen.node = np; -+ clockgen.regs = of_iomap(np, 0); -+ if (!clockgen.regs && -+ of_device_is_compatible(of_root, "fsl,ls1021a")) { -+ /* Compatibility hack for old, broken device trees */ -+ clockgen.regs = ioremap(0x1ee1000, 0x1000); -+ is_old_ls1021a = true; -+ } -+ if (!clockgen.regs) { -+ pr_err("%s(): %s: of_iomap() failed\n", __func__, np->name); -+ return; -+ } -+ -+ for (i = 0; i < ARRAY_SIZE(chipinfo); i++) { -+ if (of_device_is_compatible(np, chipinfo[i].compat)) -+ break; -+ if (is_old_ls1021a && -+ !strcmp(chipinfo[i].compat, "fsl,ls1021a-clockgen")) -+ break; -+ } -+ -+ if (i == ARRAY_SIZE(chipinfo)) { -+ pr_err("%s: unknown clockgen node %s\n", __func__, -+ np->full_name); -+ goto err; -+ } -+ clockgen.info = chipinfo[i]; -+ -+ if (clockgen.info.guts_compat) { -+ struct device_node *guts; -+ -+ guts = of_find_compatible_node(NULL, NULL, -+ clockgen.info.guts_compat); -+ if (guts) { -+ clockgen.guts = of_iomap(guts, 0); -+ if (!clockgen.guts) { -+ pr_err("%s: Couldn't map %s regs\n", __func__, -+ guts->full_name); -+ } -+ } -+ -+ } -+ -+ if (has_erratum_a4510()) -+ clockgen.info.flags |= CG_CMUX_GE_PLAT; -+ -+ clockgen.sysclk = create_sysclk("cg-sysclk"); -+ create_plls(&clockgen); -+ create_muxes(&clockgen); -+ -+ if (clockgen.info.init_periph) -+ clockgen.info.init_periph(&clockgen); -+ -+ ret = of_clk_add_provider(np, clockgen_clk_get, &clockgen); -+ if (ret) { -+ pr_err("%s: Couldn't register clk provider for node %s: %d\n", -+ __func__, np->name, ret); -+ } -+ -+ return; -+err: -+ iounmap(clockgen.regs); -+ clockgen.regs = NULL; -+} -+ -+CLK_OF_DECLARE(qoriq_clockgen_1, "fsl,qoriq-clockgen-1.0", clockgen_init); -+CLK_OF_DECLARE(qoriq_clockgen_2, "fsl,qoriq-clockgen-2.0", clockgen_init); -+CLK_OF_DECLARE(qoriq_clockgen_ls1021a, "fsl,ls1021a-clockgen", clockgen_init); -+CLK_OF_DECLARE(qoriq_clockgen_ls2080a, "fsl,ls2080a-clockgen", clockgen_init); -+CLK_OF_DECLARE(qoriq_clockgen_ls2088a, "fsl,ls2088a-clockgen", clockgen_init); -+ -+/* Legacy nodes */ -+CLK_OF_DECLARE(qoriq_sysclk_1, "fsl,qoriq-sysclk-1.0", sysclk_init); -+CLK_OF_DECLARE(qoriq_sysclk_2, "fsl,qoriq-sysclk-2.0", sysclk_init); -+CLK_OF_DECLARE(qoriq_core_pll_1, "fsl,qoriq-core-pll-1.0", core_pll_init); -+CLK_OF_DECLARE(qoriq_core_pll_2, "fsl,qoriq-core-pll-2.0", core_pll_init); -+CLK_OF_DECLARE(qoriq_core_mux_1, "fsl,qoriq-core-mux-1.0", core_mux_init); -+CLK_OF_DECLARE(qoriq_core_mux_2, "fsl,qoriq-core-mux-2.0", core_mux_init); -+CLK_OF_DECLARE(qoriq_pltfrm_pll_1, "fsl,qoriq-platform-pll-1.0", pltfrm_pll_init); -+CLK_OF_DECLARE(qoriq_pltfrm_pll_2, "fsl,qoriq-platform-pll-2.0", pltfrm_pll_init); -diff --git a/drivers/cpufreq/Kconfig.powerpc b/drivers/cpufreq/Kconfig.powerpc -index 72564b7..7ea2441 100644 ---- a/drivers/cpufreq/Kconfig.powerpc -+++ b/drivers/cpufreq/Kconfig.powerpc -@@ -26,7 +26,7 @@ config CPU_FREQ_MAPLE - config PPC_CORENET_CPUFREQ - tristate "CPU frequency scaling driver for Freescale E500MC SoCs" - depends on PPC_E500MC && OF && COMMON_CLK -- select CLK_PPC_CORENET -+ select CLK_QORIQ - help - This adds the CPUFreq driver support for Freescale e500mc, - e5500 and e6500 series SoCs which are capable of changing -diff --git a/drivers/dma/acpi-dma.c b/drivers/dma/acpi-dma.c -index de361a1..5a63564 100644 ---- a/drivers/dma/acpi-dma.c -+++ b/drivers/dma/acpi-dma.c -@@ -43,7 +43,7 @@ static int acpi_dma_parse_resource_group(const struct acpi_csrt_group *grp, - { - const struct acpi_csrt_shared_info *si; - struct list_head resource_list; -- struct resource_list_entry *rentry; -+ struct resource_entry *rentry; - resource_size_t mem = 0, irq = 0; - int ret; - -@@ -56,10 +56,10 @@ static int acpi_dma_parse_resource_group(const struct acpi_csrt_group *grp, - return 0; - - list_for_each_entry(rentry, &resource_list, node) { -- if (resource_type(&rentry->res) == IORESOURCE_MEM) -- mem = rentry->res.start; -- else if (resource_type(&rentry->res) == IORESOURCE_IRQ) -- irq = rentry->res.start; -+ if (resource_type(rentry->res) == IORESOURCE_MEM) -+ mem = rentry->res->start; -+ else if (resource_type(rentry->res) == IORESOURCE_IRQ) -+ irq = rentry->res->start; - } - - acpi_dev_free_resource_list(&resource_list); -diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig -index 06e99eb..bbf8ae4 100644 ---- a/drivers/i2c/busses/Kconfig -+++ b/drivers/i2c/busses/Kconfig -@@ -526,10 +526,10 @@ config I2C_IBM_IIC - - config I2C_IMX - tristate "IMX I2C interface" -- depends on ARCH_MXC -+ depends on ARCH_MXC || ARCH_LAYERSCAPE - help - Say Y here if you want to use the IIC bus controller on -- the Freescale i.MX/MXC processors. -+ the Freescale i.MX/MXC and layerscape processors. - - This driver can also be built as a module. If so, the module - will be called i2c-imx. -diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c -index e9fb7cf..13f88f9 100644 ---- a/drivers/i2c/busses/i2c-imx.c -+++ b/drivers/i2c/busses/i2c-imx.c -@@ -33,6 +33,10 @@ - *******************************************************************************/ - - #include -+#include -+#include -+#include -+#include - #include - #include - #include -@@ -47,6 +51,7 @@ - #include - #include - #include -+#include - #include - - /** Defines ******************************************************************** -@@ -58,6 +63,15 @@ - /* Default value */ - #define IMX_I2C_BIT_RATE 100000 /* 100kHz */ - -+/* -+ * Enable DMA if transfer byte size is bigger than this threshold. -+ * As the hardware request, it must bigger than 4 bytes.\ -+ * I have set '16' here, maybe it's not the best but I think it's -+ * the appropriate. -+ */ -+#define DMA_THRESHOLD 16 -+#define DMA_TIMEOUT 1000 -+ - /* IMX I2C registers: - * the I2C register offset is different between SoCs, - * to provid support for all these chips, split the -@@ -83,6 +97,7 @@ - #define I2SR_IBB 0x20 - #define I2SR_IAAS 0x40 - #define I2SR_ICF 0x80 -+#define I2CR_DMAEN 0x02 - #define I2CR_RSTA 0x04 - #define I2CR_TXAK 0x08 - #define I2CR_MTX 0x10 -@@ -169,6 +184,17 @@ struct imx_i2c_hwdata { - unsigned i2cr_ien_opcode; - }; - -+struct imx_i2c_dma { -+ struct dma_chan *chan_tx; -+ struct dma_chan *chan_rx; -+ struct dma_chan *chan_using; -+ struct completion cmd_complete; -+ dma_addr_t dma_buf; -+ unsigned int dma_len; -+ enum dma_transfer_direction dma_transfer_dir; -+ enum dma_data_direction dma_data_dir; -+}; -+ - struct imx_i2c_struct { - struct i2c_adapter adapter; - struct clk *clk; -@@ -181,6 +207,8 @@ struct imx_i2c_struct { - unsigned int cur_clk; - unsigned int bitrate; - const struct imx_i2c_hwdata *hwdata; -+ -+ struct imx_i2c_dma *dma; - }; - - static const struct imx_i2c_hwdata imx1_i2c_hwdata = { -@@ -251,6 +279,162 @@ static inline unsigned char imx_i2c_read_reg(struct imx_i2c_struct *i2c_imx, - return readb(i2c_imx->base + (reg << i2c_imx->hwdata->regshift)); - } - -+/* Functions for DMA support */ -+static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx, -+ dma_addr_t phy_addr) -+{ -+ struct imx_i2c_dma *dma; -+ struct dma_slave_config dma_sconfig; -+ struct device *dev = &i2c_imx->adapter.dev; -+ int ret; -+ -+ dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); -+ if (!dma) -+ return; -+ -+ dma->chan_tx = dma_request_slave_channel(dev, "tx"); -+ if (!dma->chan_tx) { -+ dev_dbg(dev, "can't request DMA tx channel\n"); -+ goto fail_al; -+ } -+ -+ dma_sconfig.dst_addr = phy_addr + -+ (IMX_I2C_I2DR << i2c_imx->hwdata->regshift); -+ dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; -+ dma_sconfig.dst_maxburst = 1; -+ dma_sconfig.direction = DMA_MEM_TO_DEV; -+ ret = dmaengine_slave_config(dma->chan_tx, &dma_sconfig); -+ if (ret < 0) { -+ dev_dbg(dev, "can't configure tx channel\n"); -+ goto fail_tx; -+ } -+ -+ dma->chan_rx = dma_request_slave_channel(dev, "rx"); -+ if (!dma->chan_rx) { -+ dev_dbg(dev, "can't request DMA rx channel\n"); -+ goto fail_tx; -+ } -+ -+ dma_sconfig.src_addr = phy_addr + -+ (IMX_I2C_I2DR << i2c_imx->hwdata->regshift); -+ dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; -+ dma_sconfig.src_maxburst = 1; -+ dma_sconfig.direction = DMA_DEV_TO_MEM; -+ ret = dmaengine_slave_config(dma->chan_rx, &dma_sconfig); -+ if (ret < 0) { -+ dev_dbg(dev, "can't configure rx channel\n"); -+ goto fail_rx; -+ } -+ -+ i2c_imx->dma = dma; -+ init_completion(&dma->cmd_complete); -+ dev_info(dev, "using %s (tx) and %s (rx) for DMA transfers\n", -+ dma_chan_name(dma->chan_tx), dma_chan_name(dma->chan_rx)); -+ -+ return; -+ -+fail_rx: -+ dma_release_channel(dma->chan_rx); -+fail_tx: -+ dma_release_channel(dma->chan_tx); -+fail_al: -+ devm_kfree(dev, dma); -+ dev_info(dev, "can't use DMA\n"); -+} -+ -+static void i2c_imx_dma_callback(void *arg) -+{ -+ struct imx_i2c_struct *i2c_imx = (struct imx_i2c_struct *)arg; -+ struct imx_i2c_dma *dma = i2c_imx->dma; -+ -+ dma_unmap_single(dma->chan_using->device->dev, dma->dma_buf, -+ dma->dma_len, dma->dma_data_dir); -+ complete(&dma->cmd_complete); -+} -+ -+static int i2c_imx_dma_xfer(struct imx_i2c_struct *i2c_imx, -+ struct i2c_msg *msgs) -+{ -+ struct imx_i2c_dma *dma = i2c_imx->dma; -+ struct dma_async_tx_descriptor *txdesc; -+ struct device *dev = &i2c_imx->adapter.dev; -+ struct device *chan_dev = dma->chan_using->device->dev; -+ -+ dma->dma_buf = dma_map_single(chan_dev, msgs->buf, -+ dma->dma_len, dma->dma_data_dir); -+ if (dma_mapping_error(chan_dev, dma->dma_buf)) { -+ dev_err(dev, "DMA mapping failed\n"); -+ goto err_map; -+ } -+ -+ txdesc = dmaengine_prep_slave_single(dma->chan_using, dma->dma_buf, -+ dma->dma_len, dma->dma_transfer_dir, -+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK); -+ if (!txdesc) { -+ dev_err(dev, "Not able to get desc for DMA xfer\n"); -+ goto err_desc; -+ } -+ -+ txdesc->callback = i2c_imx_dma_callback; -+ txdesc->callback_param = i2c_imx; -+ if (dma_submit_error(dmaengine_submit(txdesc))) { -+ dev_err(dev, "DMA submit failed\n"); -+ goto err_submit; -+ } -+ -+ dma_async_issue_pending(dma->chan_using); -+ return 0; -+ -+err_submit: -+err_desc: -+ dma_unmap_single(chan_dev, dma->dma_buf, -+ dma->dma_len, dma->dma_data_dir); -+err_map: -+ return -EINVAL; -+} -+ -+static void i2c_imx_dma_free(struct imx_i2c_struct *i2c_imx) -+{ -+ struct imx_i2c_dma *dma = i2c_imx->dma; -+ -+ dma->dma_buf = 0; -+ dma->dma_len = 0; -+ -+ dma_release_channel(dma->chan_tx); -+ dma->chan_tx = NULL; -+ -+ dma_release_channel(dma->chan_rx); -+ dma->chan_rx = NULL; -+ -+ dma->chan_using = NULL; -+} -+ -+/* -+ * When a system reset does not cause all I2C devices to be reset, it is -+ * sometimes necessary to force the I2C module to become the I2C bus master -+ * out of reset and drive SCL A slave can hold bus low to cause bus hang. -+ * Thus, SDA can be driven low by another I2C device while this I2C module -+ * is coming out of reset and will stay low indefinitely. -+ * The I2C master has to generate 9 clock pulses to get the bus free or idle. -+ */ -+static void imx_i2c_fixup(struct imx_i2c_struct *i2c_imx) -+{ -+ int k; -+ u32 delay_val = 1000000 / i2c_imx->cur_clk + 1; -+ -+ if (delay_val < 2) -+ delay_val = 2; -+ -+ for (k = 9; k; k--) { -+ imx_i2c_write_reg(I2CR_IEN, i2c_imx, IMX_I2C_I2CR); -+ imx_i2c_write_reg((I2CR_MSTA | I2CR_MTX) & (~I2CR_IEN), -+ i2c_imx, IMX_I2C_I2CR); -+ imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); -+ imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2CR); -+ udelay(delay_val << 1); -+ } -+} -+ - /** Functions for IMX I2C adapter driver *************************************** - *******************************************************************************/ - -@@ -276,8 +460,15 @@ static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy) - if (!for_busy && !(temp & I2SR_IBB)) - break; - if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) { -+ u8 status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR); -+ - dev_dbg(&i2c_imx->adapter.dev, - "<%s> I2C bus is busy\n", __func__); -+ if ((status & (I2SR_ICF | I2SR_IBB | I2CR_TXAK)) != 0) { -+ imx_i2c_write_reg(status & ~I2SR_IAL, i2c_imx, -+ IMX_I2C_I2CR); -+ imx_i2c_fixup(i2c_imx); -+ } - return -ETIMEDOUT; - } - schedule(); -@@ -382,6 +573,7 @@ static int i2c_imx_start(struct imx_i2c_struct *i2c_imx) - i2c_imx->stopped = 0; - - temp |= I2CR_IIEN | I2CR_MTX | I2CR_TXAK; -+ temp &= ~I2CR_DMAEN; - imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); - return result; - } -@@ -395,6 +587,8 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx) - dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__); - temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); - temp &= ~(I2CR_MSTA | I2CR_MTX); -+ if (i2c_imx->dma) -+ temp &= ~I2CR_DMAEN; - imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); - } - if (is_imx1_i2c(i2c_imx)) { -@@ -435,6 +629,157 @@ static irqreturn_t i2c_imx_isr(int irq, void *dev_id) - return IRQ_NONE; - } - -+static int i2c_imx_dma_write(struct imx_i2c_struct *i2c_imx, -+ struct i2c_msg *msgs) -+{ -+ int result; -+ unsigned long time_left; -+ unsigned int temp = 0; -+ unsigned long orig_jiffies = jiffies; -+ struct imx_i2c_dma *dma = i2c_imx->dma; -+ struct device *dev = &i2c_imx->adapter.dev; -+ -+ dma->chan_using = dma->chan_tx; -+ dma->dma_transfer_dir = DMA_MEM_TO_DEV; -+ dma->dma_data_dir = DMA_TO_DEVICE; -+ dma->dma_len = msgs->len - 1; -+ result = i2c_imx_dma_xfer(i2c_imx, msgs); -+ if (result) -+ return result; -+ -+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); -+ temp |= I2CR_DMAEN; -+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); -+ -+ /* -+ * Write slave address. -+ * The first byte must be transmitted by the CPU. -+ */ -+ imx_i2c_write_reg(msgs->addr << 1, i2c_imx, IMX_I2C_I2DR); -+ reinit_completion(&i2c_imx->dma->cmd_complete); -+ time_left = wait_for_completion_timeout( -+ &i2c_imx->dma->cmd_complete, -+ msecs_to_jiffies(DMA_TIMEOUT)); -+ if (time_left == 0) { -+ dmaengine_terminate_all(dma->chan_using); -+ return -ETIMEDOUT; -+ } -+ -+ /* Waiting for transfer complete. */ -+ while (1) { -+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR); -+ if (temp & I2SR_ICF) -+ break; -+ if (time_after(jiffies, orig_jiffies + -+ msecs_to_jiffies(DMA_TIMEOUT))) { -+ dev_dbg(dev, "<%s> Timeout\n", __func__); -+ return -ETIMEDOUT; -+ } -+ schedule(); -+ } -+ -+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); -+ temp &= ~I2CR_DMAEN; -+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); -+ -+ /* The last data byte must be transferred by the CPU. */ -+ imx_i2c_write_reg(msgs->buf[msgs->len-1], -+ i2c_imx, IMX_I2C_I2DR); -+ result = i2c_imx_trx_complete(i2c_imx); -+ if (result) -+ return result; -+ -+ return i2c_imx_acked(i2c_imx); -+} -+ -+static int i2c_imx_dma_read(struct imx_i2c_struct *i2c_imx, -+ struct i2c_msg *msgs, bool is_lastmsg) -+{ -+ int result; -+ unsigned long time_left; -+ unsigned int temp; -+ unsigned long orig_jiffies = jiffies; -+ struct imx_i2c_dma *dma = i2c_imx->dma; -+ struct device *dev = &i2c_imx->adapter.dev; -+ -+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); -+ temp |= I2CR_DMAEN; -+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); -+ -+ dma->chan_using = dma->chan_rx; -+ dma->dma_transfer_dir = DMA_DEV_TO_MEM; -+ dma->dma_data_dir = DMA_FROM_DEVICE; -+ /* The last two data bytes must be transferred by the CPU. */ -+ dma->dma_len = msgs->len - 2; -+ result = i2c_imx_dma_xfer(i2c_imx, msgs); -+ if (result) -+ return result; -+ -+ reinit_completion(&i2c_imx->dma->cmd_complete); -+ time_left = wait_for_completion_timeout( -+ &i2c_imx->dma->cmd_complete, -+ msecs_to_jiffies(DMA_TIMEOUT)); -+ if (time_left == 0) { -+ dmaengine_terminate_all(dma->chan_using); -+ return -ETIMEDOUT; -+ } -+ -+ /* waiting for transfer complete. */ -+ while (1) { -+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR); -+ if (temp & I2SR_ICF) -+ break; -+ if (time_after(jiffies, orig_jiffies + -+ msecs_to_jiffies(DMA_TIMEOUT))) { -+ dev_dbg(dev, "<%s> Timeout\n", __func__); -+ return -ETIMEDOUT; -+ } -+ schedule(); -+ } -+ -+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); -+ temp &= ~I2CR_DMAEN; -+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); -+ -+ /* read n-1 byte data */ -+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); -+ temp |= I2CR_TXAK; -+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); -+ -+ msgs->buf[msgs->len-2] = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); -+ /* read n byte data */ -+ result = i2c_imx_trx_complete(i2c_imx); -+ if (result) -+ return result; -+ -+ if (is_lastmsg) { -+ /* -+ * It must generate STOP before read I2DR to prevent -+ * controller from generating another clock cycle -+ */ -+ dev_dbg(dev, "<%s> clear MSTA\n", __func__); -+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); -+ temp &= ~(I2CR_MSTA | I2CR_MTX); -+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); -+ i2c_imx_bus_busy(i2c_imx, 0); -+ i2c_imx->stopped = 1; -+ } else { -+ /* -+ * For i2c master receiver repeat restart operation like: -+ * read -> repeat MSTA -> read/write -+ * The controller must set MTX before read the last byte in -+ * the first read operation, otherwise the first read cost -+ * one extra clock cycle. -+ */ -+ temp = readb(i2c_imx->base + IMX_I2C_I2CR); -+ temp |= I2CR_MTX; -+ writeb(temp, i2c_imx->base + IMX_I2C_I2CR); -+ } -+ msgs->buf[msgs->len-1] = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); -+ -+ return 0; -+} -+ - static int i2c_imx_write(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs) - { - int i, result; -@@ -504,6 +849,9 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, bo - - dev_dbg(&i2c_imx->adapter.dev, "<%s> read data\n", __func__); - -+ if (i2c_imx->dma && msgs->len >= DMA_THRESHOLD && !block_data) -+ return i2c_imx_dma_read(i2c_imx, msgs, is_lastmsg); -+ - /* read data */ - for (i = 0; i < msgs->len; i++) { - u8 len = 0; -@@ -577,6 +925,13 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter, - - dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__); - -+ /* workround for ERR010027: ensure that the I2C BUS is idle -+ before switching to master mode and attempting a Start cycle -+ */ -+ result = i2c_imx_bus_busy(i2c_imx, 0); -+ if (result) -+ goto fail0; -+ - /* Start I2C transfer */ - result = i2c_imx_start(i2c_imx); - if (result) -@@ -618,8 +973,12 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter, - #endif - if (msgs[i].flags & I2C_M_RD) - result = i2c_imx_read(i2c_imx, &msgs[i], is_lastmsg); -- else -- result = i2c_imx_write(i2c_imx, &msgs[i]); -+ else { -+ if (i2c_imx->dma && msgs[i].len >= DMA_THRESHOLD) -+ result = i2c_imx_dma_write(i2c_imx, &msgs[i]); -+ else -+ result = i2c_imx_write(i2c_imx, &msgs[i]); -+ } - if (result) - goto fail0; - } -@@ -654,6 +1013,7 @@ static int i2c_imx_probe(struct platform_device *pdev) - struct imxi2c_platform_data *pdata = dev_get_platdata(&pdev->dev); - void __iomem *base; - int irq, ret; -+ dma_addr_t phy_addr; - - dev_dbg(&pdev->dev, "<%s>\n", __func__); - -@@ -668,6 +1028,7 @@ static int i2c_imx_probe(struct platform_device *pdev) - if (IS_ERR(base)) - return PTR_ERR(base); - -+ phy_addr = (dma_addr_t)res->start; - i2c_imx = devm_kzalloc(&pdev->dev, sizeof(struct imx_i2c_struct), - GFP_KERNEL); - if (!i2c_imx) -@@ -701,7 +1062,7 @@ static int i2c_imx_probe(struct platform_device *pdev) - return ret; - } - /* Request IRQ */ -- ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr, 0, -+ ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr, IRQF_SHARED, - pdev->name, i2c_imx); - if (ret) { - dev_err(&pdev->dev, "can't claim irq %d\n", irq); -@@ -743,6 +1104,9 @@ static int i2c_imx_probe(struct platform_device *pdev) - i2c_imx->adapter.name); - dev_info(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n"); - -+ /* Init DMA config if support*/ -+ i2c_imx_dma_request(i2c_imx, phy_addr); -+ - return 0; /* Return OK */ - - clk_disable: -@@ -758,6 +1122,9 @@ static int i2c_imx_remove(struct platform_device *pdev) - dev_dbg(&i2c_imx->adapter.dev, "adapter removed\n"); - i2c_del_adapter(&i2c_imx->adapter); - -+ if (i2c_imx->dma) -+ i2c_imx_dma_free(i2c_imx); -+ - /* setup chip registers to defaults */ - imx_i2c_write_reg(0, i2c_imx, IMX_I2C_IADR); - imx_i2c_write_reg(0, i2c_imx, IMX_I2C_IFDR); -diff --git a/drivers/i2c/muxes/i2c-mux-pca9541.c b/drivers/i2c/muxes/i2c-mux-pca9541.c -index cb77277..0c8d4d2 100644 ---- a/drivers/i2c/muxes/i2c-mux-pca9541.c -+++ b/drivers/i2c/muxes/i2c-mux-pca9541.c -@@ -104,7 +104,7 @@ static int pca9541_reg_write(struct i2c_client *client, u8 command, u8 val) - buf[0] = command; - buf[1] = val; - msg.buf = buf; -- ret = adap->algo->master_xfer(adap, &msg, 1); -+ ret = __i2c_transfer(adap, &msg, 1); - } else { - union i2c_smbus_data data; - -@@ -144,7 +144,7 @@ static int pca9541_reg_read(struct i2c_client *client, u8 command) - .buf = &val - } - }; -- ret = adap->algo->master_xfer(adap, msg, 2); -+ ret = __i2c_transfer(adap, msg, 2); - if (ret == 2) - ret = val; - else if (ret >= 0) -diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c -index ec11b40..28540a4 100644 ---- a/drivers/i2c/muxes/i2c-mux-pca954x.c -+++ b/drivers/i2c/muxes/i2c-mux-pca954x.c -@@ -41,6 +41,7 @@ - #include - #include - #include -+#include - #include - #include - -@@ -62,6 +63,7 @@ struct pca954x { - struct i2c_adapter *virt_adaps[PCA954X_MAX_NCHANS]; - - u8 last_chan; /* last register value */ -+ u8 disable_mux; /* do not disable mux if val not 0 */ - }; - - struct chip_desc { -@@ -133,7 +135,7 @@ static int pca954x_reg_write(struct i2c_adapter *adap, - msg.len = 1; - buf[0] = val; - msg.buf = buf; -- ret = adap->algo->master_xfer(adap, &msg, 1); -+ ret = __i2c_transfer(adap, &msg, 1); - } else { - union i2c_smbus_data data; - ret = adap->algo->smbus_xfer(adap, client->addr, -@@ -173,6 +175,13 @@ static int pca954x_deselect_mux(struct i2c_adapter *adap, - { - struct pca954x *data = i2c_get_clientdata(client); - -+#ifdef CONFIG_ARCH_LAYERSCAPE -+ if (data->disable_mux != 0) -+ data->last_chan = chips[data->type].nchans; -+ else -+ data->last_chan = 0; -+ return pca954x_reg_write(adap, client, data->disable_mux); -+#endif - /* Deselect active channel */ - data->last_chan = 0; - return pca954x_reg_write(adap, client, data->last_chan); -@@ -186,6 +195,8 @@ static int pca954x_probe(struct i2c_client *client, - { - struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent); - struct pca954x_platform_data *pdata = dev_get_platdata(&client->dev); -+ struct device_node *of_node = client->dev.of_node; -+ bool idle_disconnect_dt; - struct gpio_desc *gpio; - int num, force, class; - struct pca954x *data; -@@ -198,27 +209,55 @@ static int pca954x_probe(struct i2c_client *client, - if (!data) - return -ENOMEM; - -+#ifdef CONFIG_ARCH_LAYERSCAPE -+ /* The point here is that you must not disable a mux if there -+ * are no pullups on the input or you mess up the I2C. This -+ * needs to be put into the DTS really as the kernel cannot -+ * know this otherwise. -+ */ -+ data->type = id->driver_data; -+ data->disable_mux = of_node && -+ of_property_read_bool(of_node, "i2c-mux-never-disable") && -+ chips[data->type].muxtype == pca954x_ismux ? -+ chips[data->type].enable : 0; -+ /* force the first selection */ -+ if (data->disable_mux != 0) -+ data->last_chan = chips[data->type].nchans; -+ else -+ data->last_chan = 0; -+#endif - i2c_set_clientdata(client, data); - - /* Get the mux out of reset if a reset GPIO is specified. */ -- gpio = devm_gpiod_get(&client->dev, "reset"); -- if (!IS_ERR(gpio)) -- gpiod_direction_output(gpio, 0); -+ gpio = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_LOW); -+ if (IS_ERR(gpio)) -+ return PTR_ERR(gpio); - - /* Write the mux register at addr to verify - * that the mux is in fact present. This also - * initializes the mux to disconnected state. - */ -+#ifdef CONFIG_ARCH_LAYERSCAPE -+ if (i2c_smbus_write_byte(client, data->disable_mux) < 0) { -+#else - if (i2c_smbus_write_byte(client, 0) < 0) { -+#endif - dev_warn(&client->dev, "probe failed\n"); - return -ENODEV; - } - -+#ifndef CONFIG_ARCH_LAYERSCAPE - data->type = id->driver_data; - data->last_chan = 0; /* force the first selection */ -+#endif -+ -+ idle_disconnect_dt = of_node && -+ of_property_read_bool(of_node, "i2c-mux-idle-disconnect"); - - /* Now create an adapter for each channel */ - for (num = 0; num < chips[data->type].nchans; num++) { -+ bool idle_disconnect_pd = false; -+ - force = 0; /* dynamic adap number */ - class = 0; /* no class by default */ - if (pdata) { -@@ -229,12 +268,13 @@ static int pca954x_probe(struct i2c_client *client, - } else - /* discard unconfigured channels */ - break; -+ idle_disconnect_pd = pdata->modes[num].deselect_on_exit; - } - - data->virt_adaps[num] = - i2c_add_mux_adapter(adap, &client->dev, client, - force, num, class, pca954x_select_chan, -- (pdata && pdata->modes[num].deselect_on_exit) -+ (idle_disconnect_pd || idle_disconnect_dt) - ? pca954x_deselect_mux : NULL); - - if (data->virt_adaps[num] == NULL) { -@@ -280,6 +320,13 @@ static int pca954x_resume(struct device *dev) - struct i2c_client *client = to_i2c_client(dev); - struct pca954x *data = i2c_get_clientdata(client); - -+#ifdef CONFIG_ARCH_LAYERSCAPE -+ if (data->disable_mux != 0) -+ data->last_chan = chips[data->type].nchans; -+ else -+ data->last_chan = 0; -+ return i2c_smbus_write_byte(client, data->disable_mux); -+#endif - data->last_chan = 0; - return i2c_smbus_write_byte(client, 0); - } -diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig -index dd51122..2cdcc76 100644 ---- a/drivers/iommu/Kconfig -+++ b/drivers/iommu/Kconfig -@@ -13,9 +13,35 @@ menuconfig IOMMU_SUPPORT - - if IOMMU_SUPPORT - -+menu "Generic IOMMU Pagetable Support" -+ -+# Selected by the actual pagetable implementations -+config IOMMU_IO_PGTABLE -+ bool -+ -+config IOMMU_IO_PGTABLE_LPAE -+ bool "ARMv7/v8 Long Descriptor Format" -+ select IOMMU_IO_PGTABLE -+ help -+ Enable support for the ARM long descriptor pagetable format. -+ This allocator supports 4K/2M/1G, 16K/32M and 64K/512M page -+ sizes at both stage-1 and stage-2, as well as address spaces -+ up to 48-bits in size. -+ -+config IOMMU_IO_PGTABLE_LPAE_SELFTEST -+ bool "LPAE selftests" -+ depends on IOMMU_IO_PGTABLE_LPAE -+ help -+ Enable self-tests for LPAE page table allocator. This performs -+ a series of page-table consistency checks during boot. -+ -+ If unsure, say N here. -+ -+endmenu -+ - config OF_IOMMU - def_bool y -- depends on OF -+ depends on OF && IOMMU_API - - config FSL_PAMU - bool "Freescale IOMMU support" -@@ -291,13 +317,13 @@ config SPAPR_TCE_IOMMU - - config ARM_SMMU - bool "ARM Ltd. System MMU (SMMU) Support" -- depends on ARM64 || (ARM_LPAE && OF) -+ depends on ARM64 || ARM - select IOMMU_API -+ select IOMMU_IO_PGTABLE_LPAE - select ARM_DMA_USE_IOMMU if ARM - help - Support for implementations of the ARM System MMU architecture -- versions 1 and 2. The driver supports both v7l and v8l table -- formats with 4k and 64k page sizes. -+ versions 1 and 2. - - Say Y here if your SoC includes an IOMMU device implementing - the ARM SMMU architecture. -diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile -index 16edef7..269cdd8 100644 ---- a/drivers/iommu/Makefile -+++ b/drivers/iommu/Makefile -@@ -1,6 +1,8 @@ - obj-$(CONFIG_IOMMU_API) += iommu.o - obj-$(CONFIG_IOMMU_API) += iommu-traces.o - obj-$(CONFIG_IOMMU_API) += iommu-sysfs.o -+obj-$(CONFIG_IOMMU_IO_PGTABLE) += io-pgtable.o -+obj-$(CONFIG_IOMMU_IO_PGTABLE_LPAE) += io-pgtable-arm.o - obj-$(CONFIG_OF_IOMMU) += of_iommu.o - obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o - obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o -diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c -index af3daf8..f7131fa 100644 ---- a/drivers/iommu/amd_iommu.c -+++ b/drivers/iommu/amd_iommu.c -@@ -343,8 +343,9 @@ static u16 get_alias(struct device *dev) - */ - if (pci_alias == devid && - PCI_BUS_NUM(ivrs_alias) == pdev->bus->number) { -- pdev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVFN; -- pdev->dma_alias_devfn = ivrs_alias & 0xff; -+ pdev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVID; -+ pdev->dma_alias_devid = PCI_DEVID(pdev->bus->number, -+ ivrs_alias & 0xff); - pr_info("AMD-Vi: Added PCI DMA alias %02x.%d for %s\n", - PCI_SLOT(ivrs_alias), PCI_FUNC(ivrs_alias), - dev_name(dev)); -@@ -3432,6 +3433,7 @@ static const struct iommu_ops amd_iommu_ops = { - .detach_dev = amd_iommu_detach_device, - .map = amd_iommu_map, - .unmap = amd_iommu_unmap, -+ .map_sg = default_iommu_map_sg, - .iova_to_phys = amd_iommu_iova_to_phys, - .pgsize_bitmap = AMD_IOMMU_PGSIZES, - }; -diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c -index 60558f7..10e584b 100644 ---- a/drivers/iommu/arm-smmu.c -+++ b/drivers/iommu/arm-smmu.c -@@ -23,8 +23,6 @@ - * - Stream-matching and stream-indexing - * - v7/v8 long-descriptor format - * - Non-secure access to the SMMU -- * - 4k and 64k pages, with contiguous pte hints. -- * - Up to 48-bit addressing (dependent on VA_BITS) - * - Context fault reporting - */ - -@@ -36,7 +34,7 @@ - #include - #include - #include --#include -+#include - #include - #include - #include -@@ -46,6 +44,16 @@ - - #include - -+#include "io-pgtable.h" -+ -+#ifdef CONFIG_FSL_MC_BUS -+#include <../drivers/staging/fsl-mc/include/mc.h> -+#endif -+ -+#ifdef CONFIG_PCI_LAYERSCAPE -+#include <../drivers/pci/host/pci-layerscape.h> -+#endif -+ - #include - - /* Maximum number of stream IDs assigned to a single device */ -@@ -71,40 +79,6 @@ - ((smmu->options & ARM_SMMU_OPT_SECURE_CFG_ACCESS) \ - ? 0x400 : 0)) - --/* Page table bits */ --#define ARM_SMMU_PTE_XN (((pteval_t)3) << 53) --#define ARM_SMMU_PTE_CONT (((pteval_t)1) << 52) --#define ARM_SMMU_PTE_AF (((pteval_t)1) << 10) --#define ARM_SMMU_PTE_SH_NS (((pteval_t)0) << 8) --#define ARM_SMMU_PTE_SH_OS (((pteval_t)2) << 8) --#define ARM_SMMU_PTE_SH_IS (((pteval_t)3) << 8) --#define ARM_SMMU_PTE_PAGE (((pteval_t)3) << 0) -- --#if PAGE_SIZE == SZ_4K --#define ARM_SMMU_PTE_CONT_ENTRIES 16 --#elif PAGE_SIZE == SZ_64K --#define ARM_SMMU_PTE_CONT_ENTRIES 32 --#else --#define ARM_SMMU_PTE_CONT_ENTRIES 1 --#endif -- --#define ARM_SMMU_PTE_CONT_SIZE (PAGE_SIZE * ARM_SMMU_PTE_CONT_ENTRIES) --#define ARM_SMMU_PTE_CONT_MASK (~(ARM_SMMU_PTE_CONT_SIZE - 1)) -- --/* Stage-1 PTE */ --#define ARM_SMMU_PTE_AP_UNPRIV (((pteval_t)1) << 6) --#define ARM_SMMU_PTE_AP_RDONLY (((pteval_t)2) << 6) --#define ARM_SMMU_PTE_ATTRINDX_SHIFT 2 --#define ARM_SMMU_PTE_nG (((pteval_t)1) << 11) -- --/* Stage-2 PTE */ --#define ARM_SMMU_PTE_HAP_FAULT (((pteval_t)0) << 6) --#define ARM_SMMU_PTE_HAP_READ (((pteval_t)1) << 6) --#define ARM_SMMU_PTE_HAP_WRITE (((pteval_t)2) << 6) --#define ARM_SMMU_PTE_MEMATTR_OIWB (((pteval_t)0xf) << 2) --#define ARM_SMMU_PTE_MEMATTR_NC (((pteval_t)0x5) << 2) --#define ARM_SMMU_PTE_MEMATTR_DEV (((pteval_t)0x1) << 2) -- - /* Configuration registers */ - #define ARM_SMMU_GR0_sCR0 0x0 - #define sCR0_CLIENTPD (1 << 0) -@@ -132,17 +106,12 @@ - #define ARM_SMMU_GR0_sGFSYNR0 0x50 - #define ARM_SMMU_GR0_sGFSYNR1 0x54 - #define ARM_SMMU_GR0_sGFSYNR2 0x58 --#define ARM_SMMU_GR0_PIDR0 0xfe0 --#define ARM_SMMU_GR0_PIDR1 0xfe4 --#define ARM_SMMU_GR0_PIDR2 0xfe8 - - #define ID0_S1TS (1 << 30) - #define ID0_S2TS (1 << 29) - #define ID0_NTS (1 << 28) - #define ID0_SMS (1 << 27) --#define ID0_PTFS_SHIFT 24 --#define ID0_PTFS_MASK 0x2 --#define ID0_PTFS_V8_ONLY 0x2 -+#define ID0_ATOSNS (1 << 26) - #define ID0_CTTW (1 << 14) - #define ID0_NUMIRPT_SHIFT 16 - #define ID0_NUMIRPT_MASK 0xff -@@ -169,11 +138,7 @@ - #define ID2_PTFS_16K (1 << 13) - #define ID2_PTFS_64K (1 << 14) - --#define PIDR2_ARCH_SHIFT 4 --#define PIDR2_ARCH_MASK 0xf -- - /* Global TLB invalidation */ --#define ARM_SMMU_GR0_STLBIALL 0x60 - #define ARM_SMMU_GR0_TLBIVMID 0x64 - #define ARM_SMMU_GR0_TLBIALLNSNH 0x68 - #define ARM_SMMU_GR0_TLBIALLH 0x6c -@@ -231,13 +196,25 @@ - #define ARM_SMMU_CB_TTBCR2 0x10 - #define ARM_SMMU_CB_TTBR0_LO 0x20 - #define ARM_SMMU_CB_TTBR0_HI 0x24 -+#define ARM_SMMU_CB_TTBR1_LO 0x28 -+#define ARM_SMMU_CB_TTBR1_HI 0x2c - #define ARM_SMMU_CB_TTBCR 0x30 - #define ARM_SMMU_CB_S1_MAIR0 0x38 -+#define ARM_SMMU_CB_S1_MAIR1 0x3c -+#define ARM_SMMU_CB_PAR_LO 0x50 -+#define ARM_SMMU_CB_PAR_HI 0x54 - #define ARM_SMMU_CB_FSR 0x58 - #define ARM_SMMU_CB_FAR_LO 0x60 - #define ARM_SMMU_CB_FAR_HI 0x64 - #define ARM_SMMU_CB_FSYNR0 0x68 -+#define ARM_SMMU_CB_S1_TLBIVA 0x600 - #define ARM_SMMU_CB_S1_TLBIASID 0x610 -+#define ARM_SMMU_CB_S1_TLBIVAL 0x620 -+#define ARM_SMMU_CB_S2_TLBIIPAS2 0x630 -+#define ARM_SMMU_CB_S2_TLBIIPAS2L 0x638 -+#define ARM_SMMU_CB_ATS1PR_LO 0x800 -+#define ARM_SMMU_CB_ATS1PR_HI 0x804 -+#define ARM_SMMU_CB_ATSR 0x8f0 - - #define SCTLR_S1_ASIDPNE (1 << 12) - #define SCTLR_CFCFG (1 << 7) -@@ -249,64 +226,17 @@ - #define SCTLR_M (1 << 0) - #define SCTLR_EAE_SBOP (SCTLR_AFE | SCTLR_TRE) - --#define RESUME_RETRY (0 << 0) --#define RESUME_TERMINATE (1 << 0) -- --#define TTBCR_EAE (1 << 31) -+#define CB_PAR_F (1 << 0) - --#define TTBCR_PASIZE_SHIFT 16 --#define TTBCR_PASIZE_MASK 0x7 -+#define ATSR_ACTIVE (1 << 0) - --#define TTBCR_TG0_4K (0 << 14) --#define TTBCR_TG0_64K (1 << 14) -- --#define TTBCR_SH0_SHIFT 12 --#define TTBCR_SH0_MASK 0x3 --#define TTBCR_SH_NS 0 --#define TTBCR_SH_OS 2 --#define TTBCR_SH_IS 3 -- --#define TTBCR_ORGN0_SHIFT 10 --#define TTBCR_IRGN0_SHIFT 8 --#define TTBCR_RGN_MASK 0x3 --#define TTBCR_RGN_NC 0 --#define TTBCR_RGN_WBWA 1 --#define TTBCR_RGN_WT 2 --#define TTBCR_RGN_WB 3 -- --#define TTBCR_SL0_SHIFT 6 --#define TTBCR_SL0_MASK 0x3 --#define TTBCR_SL0_LVL_2 0 --#define TTBCR_SL0_LVL_1 1 -- --#define TTBCR_T1SZ_SHIFT 16 --#define TTBCR_T0SZ_SHIFT 0 --#define TTBCR_SZ_MASK 0xf -+#define RESUME_RETRY (0 << 0) -+#define RESUME_TERMINATE (1 << 0) - - #define TTBCR2_SEP_SHIFT 15 --#define TTBCR2_SEP_MASK 0x7 -- --#define TTBCR2_PASIZE_SHIFT 0 --#define TTBCR2_PASIZE_MASK 0x7 -- --/* Common definitions for PASize and SEP fields */ --#define TTBCR2_ADDR_32 0 --#define TTBCR2_ADDR_36 1 --#define TTBCR2_ADDR_40 2 --#define TTBCR2_ADDR_42 3 --#define TTBCR2_ADDR_44 4 --#define TTBCR2_ADDR_48 5 -- --#define TTBRn_HI_ASID_SHIFT 16 -- --#define MAIR_ATTR_SHIFT(n) ((n) << 3) --#define MAIR_ATTR_MASK 0xff --#define MAIR_ATTR_DEVICE 0x04 --#define MAIR_ATTR_NC 0x44 --#define MAIR_ATTR_WBRWA 0xff --#define MAIR_ATTR_IDX_NC 0 --#define MAIR_ATTR_IDX_CACHE 1 --#define MAIR_ATTR_IDX_DEV 2 -+#define TTBCR2_SEP_UPSTREAM (0x7 << TTBCR2_SEP_SHIFT) -+ -+#define TTBRn_HI_ASID_SHIFT 16 - - #define FSR_MULTI (1 << 31) - #define FSR_SS (1 << 30) -@@ -345,6 +275,7 @@ struct arm_smmu_smr { - struct arm_smmu_master_cfg { - int num_streamids; - u16 streamids[MAX_MASTER_STREAMIDS]; -+ u16 mask; - struct arm_smmu_smr *smrs; - }; - -@@ -366,6 +297,7 @@ struct arm_smmu_device { - #define ARM_SMMU_FEAT_TRANS_S1 (1 << 2) - #define ARM_SMMU_FEAT_TRANS_S2 (1 << 3) - #define ARM_SMMU_FEAT_TRANS_NESTED (1 << 4) -+#define ARM_SMMU_FEAT_TRANS_OPS (1 << 5) - u32 features; - - #define ARM_SMMU_OPT_SECURE_CFG_ACCESS (1 << 0) -@@ -380,10 +312,9 @@ struct arm_smmu_device { - u32 num_mapping_groups; - DECLARE_BITMAP(smr_map, ARM_SMMU_MAX_SMRS); - -- unsigned long s1_input_size; -- unsigned long s1_output_size; -- unsigned long s2_input_size; -- unsigned long s2_output_size; -+ unsigned long va_size; -+ unsigned long ipa_size; -+ unsigned long pa_size; - - u32 num_global_irqs; - u32 num_context_irqs; -@@ -397,19 +328,33 @@ struct arm_smmu_cfg { - u8 cbndx; - u8 irptndx; - u32 cbar; -- pgd_t *pgd; - }; - #define INVALID_IRPTNDX 0xff - - #define ARM_SMMU_CB_ASID(cfg) ((cfg)->cbndx) - #define ARM_SMMU_CB_VMID(cfg) ((cfg)->cbndx + 1) - -+enum arm_smmu_domain_stage { -+ ARM_SMMU_DOMAIN_S1 = 0, -+ ARM_SMMU_DOMAIN_S2, -+ ARM_SMMU_DOMAIN_NESTED, -+}; -+ - struct arm_smmu_domain { - struct arm_smmu_device *smmu; -+ struct io_pgtable_ops *pgtbl_ops; -+ spinlock_t pgtbl_lock; - struct arm_smmu_cfg cfg; -- spinlock_t lock; -+ enum arm_smmu_domain_stage stage; -+ struct mutex init_mutex; /* Protects smmu pointer */ -+ struct iommu_domain domain; - }; - -+static struct iommu_ops arm_smmu_ops; -+#ifdef CONFIG_FSL_MC_BUS -+static struct iommu_ops arm_fsl_mc_smmu_ops; -+#endif -+ - static DEFINE_SPINLOCK(arm_smmu_devices_lock); - static LIST_HEAD(arm_smmu_devices); - -@@ -422,6 +367,43 @@ static struct arm_smmu_option_prop arm_smmu_options[] = { - { ARM_SMMU_OPT_SECURE_CFG_ACCESS, "calxeda,smmu-secure-config-access" }, - { 0, NULL}, - }; -+#define CONFIG_AIOP_ERRATA -+#ifdef CONFIG_AIOP_ERRATA -+/* -+ * PL = 1, BMT = 1, VA = 1 -+ */ -+#define AIOP_SMR_VALUE 0x380 -+/* -+ * Following should be set: -+ * SHCFG: 0x3 -+ * MTCFG: 0x1 -+ * MemAttr: 0xf -+ * Type: 0x1 -+ * RACFG: 0x2 -+ * WACFG: 0x2 -+ */ -+#define AIOP_S2CR_VALUE 0xA1FB00 -+ -+static void arm_smmu_aiop_attr_trans(struct arm_smmu_device *smmu) -+{ -+ void __iomem *gr0_base = ARM_SMMU_GR0(smmu); -+ u16 mask = 0x7c7f; -+ int index; -+ u32 reg; -+ /* reserve one smr group for AIOP */ -+ index = --smmu->num_mapping_groups; -+ -+ reg = SMR_VALID | AIOP_SMR_VALUE << SMR_ID_SHIFT | -+ mask << SMR_MASK_SHIFT; -+ writel(reg, gr0_base + ARM_SMMU_GR0_SMR(index)); -+ writel(AIOP_S2CR_VALUE, gr0_base + ARM_SMMU_GR0_S2CR(index)); -+} -+#endif -+ -+static struct arm_smmu_domain *to_smmu_domain(struct iommu_domain *dom) -+{ -+ return container_of(dom, struct arm_smmu_domain, domain); -+} - - static void parse_driver_options(struct arm_smmu_device *smmu) - { -@@ -447,6 +429,16 @@ static struct device_node *dev_get_dev_node(struct device *dev) - return bus->bridge->parent->of_node; - } - -+#ifdef CONFIG_FSL_MC_BUS -+ if (dev->bus == &fsl_mc_bus_type) { -+ /* -+ * Get to the MC device tree node. -+ */ -+ while (dev->bus == &fsl_mc_bus_type) -+ dev = dev->parent; -+ } -+#endif -+ - return dev->of_node; - } - -@@ -590,7 +582,7 @@ static void __arm_smmu_free_bitmap(unsigned long *map, int idx) - } - - /* Wait for any pending TLB invalidations to complete */ --static void arm_smmu_tlb_sync(struct arm_smmu_device *smmu) -+static void __arm_smmu_tlb_sync(struct arm_smmu_device *smmu) - { - int count = 0; - void __iomem *gr0_base = ARM_SMMU_GR0(smmu); -@@ -608,12 +600,19 @@ static void arm_smmu_tlb_sync(struct arm_smmu_device *smmu) - } - } - --static void arm_smmu_tlb_inv_context(struct arm_smmu_domain *smmu_domain) -+static void arm_smmu_tlb_sync(void *cookie) - { -+ struct arm_smmu_domain *smmu_domain = cookie; -+ __arm_smmu_tlb_sync(smmu_domain->smmu); -+} -+ -+static void arm_smmu_tlb_inv_context(void *cookie) -+{ -+ struct arm_smmu_domain *smmu_domain = cookie; - struct arm_smmu_cfg *cfg = &smmu_domain->cfg; - struct arm_smmu_device *smmu = smmu_domain->smmu; -- void __iomem *base = ARM_SMMU_GR0(smmu); - bool stage1 = cfg->cbar != CBAR_TYPE_S2_TRANS; -+ void __iomem *base; - - if (stage1) { - base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx); -@@ -625,16 +624,83 @@ static void arm_smmu_tlb_inv_context(struct arm_smmu_domain *smmu_domain) - base + ARM_SMMU_GR0_TLBIVMID); - } - -- arm_smmu_tlb_sync(smmu); -+ __arm_smmu_tlb_sync(smmu); -+} -+ -+static void arm_smmu_tlb_inv_range_nosync(unsigned long iova, size_t size, -+ bool leaf, void *cookie) -+{ -+ struct arm_smmu_domain *smmu_domain = cookie; -+ struct arm_smmu_cfg *cfg = &smmu_domain->cfg; -+ struct arm_smmu_device *smmu = smmu_domain->smmu; -+ bool stage1 = cfg->cbar != CBAR_TYPE_S2_TRANS; -+ void __iomem *reg; -+ -+ if (stage1) { -+ reg = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx); -+ reg += leaf ? ARM_SMMU_CB_S1_TLBIVAL : ARM_SMMU_CB_S1_TLBIVA; -+ -+ if (!IS_ENABLED(CONFIG_64BIT) || smmu->version == ARM_SMMU_V1) { -+ iova &= ~12UL; -+ iova |= ARM_SMMU_CB_ASID(cfg); -+ writel_relaxed(iova, reg); -+#ifdef CONFIG_64BIT -+ } else { -+ iova >>= 12; -+ iova |= (u64)ARM_SMMU_CB_ASID(cfg) << 48; -+ writeq_relaxed(iova, reg); -+#endif -+ } -+#ifdef CONFIG_64BIT -+ } else if (smmu->version == ARM_SMMU_V2) { -+ reg = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx); -+ reg += leaf ? ARM_SMMU_CB_S2_TLBIIPAS2L : -+ ARM_SMMU_CB_S2_TLBIIPAS2; -+ writeq_relaxed(iova >> 12, reg); -+#endif -+ } else { -+ reg = ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_TLBIVMID; -+ writel_relaxed(ARM_SMMU_CB_VMID(cfg), reg); -+ } -+} -+ -+static void arm_smmu_flush_pgtable(void *addr, size_t size, void *cookie) -+{ -+ struct arm_smmu_domain *smmu_domain = cookie; -+ struct arm_smmu_device *smmu = smmu_domain->smmu; -+ unsigned long offset = (unsigned long)addr & ~PAGE_MASK; -+ -+ -+ /* Ensure new page tables are visible to the hardware walker */ -+ if (smmu->features & ARM_SMMU_FEAT_COHERENT_WALK) { -+ dsb(ishst); -+ } else { -+ /* -+ * If the SMMU can't walk tables in the CPU caches, treat them -+ * like non-coherent DMA since we need to flush the new entries -+ * all the way out to memory. There's no possibility of -+ * recursion here as the SMMU table walker will not be wired -+ * through another SMMU. -+ */ -+ dma_map_page(smmu->dev, virt_to_page(addr), offset, size, -+ DMA_TO_DEVICE); -+ } - } - -+static struct iommu_gather_ops arm_smmu_gather_ops = { -+ .tlb_flush_all = arm_smmu_tlb_inv_context, -+ .tlb_add_flush = arm_smmu_tlb_inv_range_nosync, -+ .tlb_sync = arm_smmu_tlb_sync, -+ .flush_pgtable = arm_smmu_flush_pgtable, -+}; -+ - static irqreturn_t arm_smmu_context_fault(int irq, void *dev) - { - int flags, ret; - u32 fsr, far, fsynr, resume; - unsigned long iova; - struct iommu_domain *domain = dev; -- struct arm_smmu_domain *smmu_domain = domain->priv; -+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); - struct arm_smmu_cfg *cfg = &smmu_domain->cfg; - struct arm_smmu_device *smmu = smmu_domain->smmu; - void __iomem *cb_base; -@@ -705,29 +771,8 @@ static irqreturn_t arm_smmu_global_fault(int irq, void *dev) - return IRQ_HANDLED; - } - --static void arm_smmu_flush_pgtable(struct arm_smmu_device *smmu, void *addr, -- size_t size) --{ -- unsigned long offset = (unsigned long)addr & ~PAGE_MASK; -- -- -- /* Ensure new page tables are visible to the hardware walker */ -- if (smmu->features & ARM_SMMU_FEAT_COHERENT_WALK) { -- dsb(ishst); -- } else { -- /* -- * If the SMMU can't walk tables in the CPU caches, treat them -- * like non-coherent DMA since we need to flush the new entries -- * all the way out to memory. There's no possibility of -- * recursion here as the SMMU table walker will not be wired -- * through another SMMU. -- */ -- dma_map_page(smmu->dev, virt_to_page(addr), offset, size, -- DMA_TO_DEVICE); -- } --} -- --static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain) -+static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain, -+ struct io_pgtable_cfg *pgtbl_cfg) - { - u32 reg; - bool stage1; -@@ -740,6 +785,20 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain) - stage1 = cfg->cbar != CBAR_TYPE_S2_TRANS; - cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx); - -+ if (smmu->version > ARM_SMMU_V1) { -+ /* -+ * CBA2R. -+ * *Must* be initialised before CBAR thanks to VMID16 -+ * architectural oversight affected some implementations. -+ */ -+#ifdef CONFIG_64BIT -+ reg = CBA2R_RW64_64BIT; -+#else -+ reg = CBA2R_RW64_32BIT; -+#endif -+ writel_relaxed(reg, gr1_base + ARM_SMMU_GR1_CBA2R(cfg->cbndx)); -+ } -+ - /* CBAR */ - reg = cfg->cbar; - if (smmu->version == ARM_SMMU_V1) -@@ -757,135 +816,51 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain) - } - writel_relaxed(reg, gr1_base + ARM_SMMU_GR1_CBAR(cfg->cbndx)); - -- if (smmu->version > ARM_SMMU_V1) { -- /* CBA2R */ --#ifdef CONFIG_64BIT -- reg = CBA2R_RW64_64BIT; --#else -- reg = CBA2R_RW64_32BIT; --#endif -- writel_relaxed(reg, -- gr1_base + ARM_SMMU_GR1_CBA2R(cfg->cbndx)); -- -- /* TTBCR2 */ -- switch (smmu->s1_input_size) { -- case 32: -- reg = (TTBCR2_ADDR_32 << TTBCR2_SEP_SHIFT); -- break; -- case 36: -- reg = (TTBCR2_ADDR_36 << TTBCR2_SEP_SHIFT); -- break; -- case 39: -- case 40: -- reg = (TTBCR2_ADDR_40 << TTBCR2_SEP_SHIFT); -- break; -- case 42: -- reg = (TTBCR2_ADDR_42 << TTBCR2_SEP_SHIFT); -- break; -- case 44: -- reg = (TTBCR2_ADDR_44 << TTBCR2_SEP_SHIFT); -- break; -- case 48: -- reg = (TTBCR2_ADDR_48 << TTBCR2_SEP_SHIFT); -- break; -- } -- -- switch (smmu->s1_output_size) { -- case 32: -- reg |= (TTBCR2_ADDR_32 << TTBCR2_PASIZE_SHIFT); -- break; -- case 36: -- reg |= (TTBCR2_ADDR_36 << TTBCR2_PASIZE_SHIFT); -- break; -- case 39: -- case 40: -- reg |= (TTBCR2_ADDR_40 << TTBCR2_PASIZE_SHIFT); -- break; -- case 42: -- reg |= (TTBCR2_ADDR_42 << TTBCR2_PASIZE_SHIFT); -- break; -- case 44: -- reg |= (TTBCR2_ADDR_44 << TTBCR2_PASIZE_SHIFT); -- break; -- case 48: -- reg |= (TTBCR2_ADDR_48 << TTBCR2_PASIZE_SHIFT); -- break; -- } -- -- if (stage1) -- writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBCR2); -- } -+ /* TTBRs */ -+ if (stage1) { -+ reg = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0]; -+ writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_LO); -+ reg = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0] >> 32; -+ reg |= ARM_SMMU_CB_ASID(cfg) << TTBRn_HI_ASID_SHIFT; -+ writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_HI); - -- /* TTBR0 */ -- arm_smmu_flush_pgtable(smmu, cfg->pgd, -- PTRS_PER_PGD * sizeof(pgd_t)); -- reg = __pa(cfg->pgd); -- writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_LO); -- reg = (phys_addr_t)__pa(cfg->pgd) >> 32; -- if (stage1) -+ reg = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[1]; -+ writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR1_LO); -+ reg = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[1] >> 32; - reg |= ARM_SMMU_CB_ASID(cfg) << TTBRn_HI_ASID_SHIFT; -- writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_HI); -+ writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR1_HI); -+ } else { -+ reg = pgtbl_cfg->arm_lpae_s2_cfg.vttbr; -+ writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_LO); -+ reg = pgtbl_cfg->arm_lpae_s2_cfg.vttbr >> 32; -+ writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_HI); -+ } - -- /* -- * TTBCR -- * We use long descriptor, with inner-shareable WBWA tables in TTBR0. -- */ -- if (smmu->version > ARM_SMMU_V1) { -- if (PAGE_SIZE == SZ_4K) -- reg = TTBCR_TG0_4K; -- else -- reg = TTBCR_TG0_64K; -- -- if (!stage1) { -- reg |= (64 - smmu->s2_input_size) << TTBCR_T0SZ_SHIFT; -- -- switch (smmu->s2_output_size) { -- case 32: -- reg |= (TTBCR2_ADDR_32 << TTBCR_PASIZE_SHIFT); -- break; -- case 36: -- reg |= (TTBCR2_ADDR_36 << TTBCR_PASIZE_SHIFT); -- break; -- case 40: -- reg |= (TTBCR2_ADDR_40 << TTBCR_PASIZE_SHIFT); -- break; -- case 42: -- reg |= (TTBCR2_ADDR_42 << TTBCR_PASIZE_SHIFT); -- break; -- case 44: -- reg |= (TTBCR2_ADDR_44 << TTBCR_PASIZE_SHIFT); -- break; -- case 48: -- reg |= (TTBCR2_ADDR_48 << TTBCR_PASIZE_SHIFT); -- break; -- } -- } else { -- reg |= (64 - smmu->s1_input_size) << TTBCR_T0SZ_SHIFT; -+ /* TTBCR */ -+ if (stage1) { -+ reg = pgtbl_cfg->arm_lpae_s1_cfg.tcr; -+ writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBCR); -+ if (smmu->version > ARM_SMMU_V1) { -+ reg = pgtbl_cfg->arm_lpae_s1_cfg.tcr >> 32; -+ reg |= TTBCR2_SEP_UPSTREAM; -+ writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBCR2); - } - } else { -- reg = 0; -+ reg = pgtbl_cfg->arm_lpae_s2_cfg.vtcr; -+ writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBCR); - } - -- reg |= TTBCR_EAE | -- (TTBCR_SH_IS << TTBCR_SH0_SHIFT) | -- (TTBCR_RGN_WBWA << TTBCR_ORGN0_SHIFT) | -- (TTBCR_RGN_WBWA << TTBCR_IRGN0_SHIFT); -- -- if (!stage1) -- reg |= (TTBCR_SL0_LVL_1 << TTBCR_SL0_SHIFT); -- -- writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBCR); -- -- /* MAIR0 (stage-1 only) */ -+ /* MAIRs (stage-1 only) */ - if (stage1) { -- reg = (MAIR_ATTR_NC << MAIR_ATTR_SHIFT(MAIR_ATTR_IDX_NC)) | -- (MAIR_ATTR_WBRWA << MAIR_ATTR_SHIFT(MAIR_ATTR_IDX_CACHE)) | -- (MAIR_ATTR_DEVICE << MAIR_ATTR_SHIFT(MAIR_ATTR_IDX_DEV)); -+ reg = pgtbl_cfg->arm_lpae_s1_cfg.mair[0]; - writel_relaxed(reg, cb_base + ARM_SMMU_CB_S1_MAIR0); -+ reg = pgtbl_cfg->arm_lpae_s1_cfg.mair[1]; -+ writel_relaxed(reg, cb_base + ARM_SMMU_CB_S1_MAIR1); - } - - /* SCTLR */ -- reg = SCTLR_CFCFG | SCTLR_CFIE | SCTLR_CFRE | SCTLR_M | SCTLR_EAE_SBOP; -+ /* Disable stall mode */ -+ reg = SCTLR_CFIE | SCTLR_CFRE | SCTLR_M | SCTLR_EAE_SBOP; - if (stage1) - reg |= SCTLR_S1_ASIDPNE; - #ifdef __BIG_ENDIAN -@@ -898,27 +873,69 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, - struct arm_smmu_device *smmu) - { - int irq, start, ret = 0; -- unsigned long flags; -- struct arm_smmu_domain *smmu_domain = domain->priv; -+ unsigned long ias, oas; -+ struct io_pgtable_ops *pgtbl_ops; -+ struct io_pgtable_cfg pgtbl_cfg; -+ enum io_pgtable_fmt fmt; -+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); - struct arm_smmu_cfg *cfg = &smmu_domain->cfg; - -- spin_lock_irqsave(&smmu_domain->lock, flags); -+ mutex_lock(&smmu_domain->init_mutex); - if (smmu_domain->smmu) - goto out_unlock; - -- if (smmu->features & ARM_SMMU_FEAT_TRANS_NESTED) { -+ /* -+ * Mapping the requested stage onto what we support is surprisingly -+ * complicated, mainly because the spec allows S1+S2 SMMUs without -+ * support for nested translation. That means we end up with the -+ * following table: -+ * -+ * Requested Supported Actual -+ * S1 N S1 -+ * S1 S1+S2 S1 -+ * S1 S2 S2 -+ * S1 S1 S1 -+ * N N N -+ * N S1+S2 S2 -+ * N S2 S2 -+ * N S1 S1 -+ * -+ * Note that you can't actually request stage-2 mappings. -+ */ -+ if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S1)) -+ smmu_domain->stage = ARM_SMMU_DOMAIN_S2; -+ if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S2)) -+ smmu_domain->stage = ARM_SMMU_DOMAIN_S1; -+ -+ switch (smmu_domain->stage) { -+ case ARM_SMMU_DOMAIN_S1: -+ cfg->cbar = CBAR_TYPE_S1_TRANS_S2_BYPASS; -+ start = smmu->num_s2_context_banks; -+ ias = smmu->va_size; -+ oas = smmu->ipa_size; -+ if (IS_ENABLED(CONFIG_64BIT)) -+ fmt = ARM_64_LPAE_S1; -+ else -+ fmt = ARM_32_LPAE_S1; -+ break; -+ case ARM_SMMU_DOMAIN_NESTED: - /* - * We will likely want to change this if/when KVM gets - * involved. - */ -- cfg->cbar = CBAR_TYPE_S1_TRANS_S2_BYPASS; -- start = smmu->num_s2_context_banks; -- } else if (smmu->features & ARM_SMMU_FEAT_TRANS_S1) { -- cfg->cbar = CBAR_TYPE_S1_TRANS_S2_BYPASS; -- start = smmu->num_s2_context_banks; -- } else { -+ case ARM_SMMU_DOMAIN_S2: - cfg->cbar = CBAR_TYPE_S2_TRANS; - start = 0; -+ ias = smmu->ipa_size; -+ oas = smmu->pa_size; -+ if (IS_ENABLED(CONFIG_64BIT)) -+ fmt = ARM_64_LPAE_S2; -+ else -+ fmt = ARM_32_LPAE_S2; -+ break; -+ default: -+ ret = -EINVAL; -+ goto out_unlock; - } - - ret = __arm_smmu_alloc_bitmap(smmu->context_map, start, -@@ -934,10 +951,33 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, - cfg->irptndx = cfg->cbndx; - } - -- ACCESS_ONCE(smmu_domain->smmu) = smmu; -- arm_smmu_init_context_bank(smmu_domain); -- spin_unlock_irqrestore(&smmu_domain->lock, flags); -+ pgtbl_cfg = (struct io_pgtable_cfg) { -+ .pgsize_bitmap = arm_smmu_ops.pgsize_bitmap, -+ .ias = ias, -+ .oas = oas, -+ .tlb = &arm_smmu_gather_ops, -+ }; -+ -+ smmu_domain->smmu = smmu; -+ pgtbl_ops = alloc_io_pgtable_ops(fmt, &pgtbl_cfg, smmu_domain); -+ if (!pgtbl_ops) { -+ ret = -ENOMEM; -+ goto out_clear_smmu; -+ } -+ -+ /* Update our support page sizes to reflect the page table format */ -+ arm_smmu_ops.pgsize_bitmap = pgtbl_cfg.pgsize_bitmap; -+#ifdef CONFIG_FSL_MC_BUS -+ arm_fsl_mc_smmu_ops.pgsize_bitmap = pgtbl_cfg.pgsize_bitmap; -+#endif -+ -+ /* Initialise the context bank with our page table cfg */ -+ arm_smmu_init_context_bank(smmu_domain, &pgtbl_cfg); - -+ /* -+ * Request context fault interrupt. Do this last to avoid the -+ * handler seeing a half-initialised domain state. -+ */ - irq = smmu->irqs[smmu->num_global_irqs + cfg->irptndx]; - ret = request_irq(irq, arm_smmu_context_fault, IRQF_SHARED, - "arm-smmu-context-fault", domain); -@@ -947,16 +987,22 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, - cfg->irptndx = INVALID_IRPTNDX; - } - -+ mutex_unlock(&smmu_domain->init_mutex); -+ -+ /* Publish page table ops for map/unmap */ -+ smmu_domain->pgtbl_ops = pgtbl_ops; - return 0; - -+out_clear_smmu: -+ smmu_domain->smmu = NULL; - out_unlock: -- spin_unlock_irqrestore(&smmu_domain->lock, flags); -+ mutex_unlock(&smmu_domain->init_mutex); - return ret; - } - - static void arm_smmu_destroy_domain_context(struct iommu_domain *domain) - { -- struct arm_smmu_domain *smmu_domain = domain->priv; -+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); - struct arm_smmu_device *smmu = smmu_domain->smmu; - struct arm_smmu_cfg *cfg = &smmu_domain->cfg; - void __iomem *cb_base; -@@ -965,24 +1011,30 @@ static void arm_smmu_destroy_domain_context(struct iommu_domain *domain) - if (!smmu) - return; - -- /* Disable the context bank and nuke the TLB before freeing it. */ -+ /* -+ * Disable the context bank and free the page tables before freeing -+ * it. -+ */ - cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx); - writel_relaxed(0, cb_base + ARM_SMMU_CB_SCTLR); -- arm_smmu_tlb_inv_context(smmu_domain); - - if (cfg->irptndx != INVALID_IRPTNDX) { - irq = smmu->irqs[smmu->num_global_irqs + cfg->irptndx]; - free_irq(irq, domain); - } - -+ if (smmu_domain->pgtbl_ops) -+ free_io_pgtable_ops(smmu_domain->pgtbl_ops); -+ - __arm_smmu_free_bitmap(smmu->context_map, cfg->cbndx); - } - --static int arm_smmu_domain_init(struct iommu_domain *domain) -+static struct iommu_domain *arm_smmu_domain_alloc(unsigned type) - { - struct arm_smmu_domain *smmu_domain; -- pgd_t *pgd; - -+ if (type != IOMMU_DOMAIN_UNMANAGED) -+ return NULL; - /* - * Allocate the domain and initialise some of its data structures. - * We can't really do anything meaningful until we've added a -@@ -990,95 +1042,23 @@ static int arm_smmu_domain_init(struct iommu_domain *domain) - */ - smmu_domain = kzalloc(sizeof(*smmu_domain), GFP_KERNEL); - if (!smmu_domain) -- return -ENOMEM; -+ return NULL; - -- pgd = kcalloc(PTRS_PER_PGD, sizeof(pgd_t), GFP_KERNEL); -- if (!pgd) -- goto out_free_domain; -- smmu_domain->cfg.pgd = pgd; -+ mutex_init(&smmu_domain->init_mutex); -+ spin_lock_init(&smmu_domain->pgtbl_lock); - -- spin_lock_init(&smmu_domain->lock); -- domain->priv = smmu_domain; -- return 0; -- --out_free_domain: -- kfree(smmu_domain); -- return -ENOMEM; -+ return &smmu_domain->domain; - } - --static void arm_smmu_free_ptes(pmd_t *pmd) -+static void arm_smmu_domain_free(struct iommu_domain *domain) - { -- pgtable_t table = pmd_pgtable(*pmd); -- -- __free_page(table); --} -- --static void arm_smmu_free_pmds(pud_t *pud) --{ -- int i; -- pmd_t *pmd, *pmd_base = pmd_offset(pud, 0); -- -- pmd = pmd_base; -- for (i = 0; i < PTRS_PER_PMD; ++i) { -- if (pmd_none(*pmd)) -- continue; -- -- arm_smmu_free_ptes(pmd); -- pmd++; -- } -- -- pmd_free(NULL, pmd_base); --} -- --static void arm_smmu_free_puds(pgd_t *pgd) --{ -- int i; -- pud_t *pud, *pud_base = pud_offset(pgd, 0); -- -- pud = pud_base; -- for (i = 0; i < PTRS_PER_PUD; ++i) { -- if (pud_none(*pud)) -- continue; -- -- arm_smmu_free_pmds(pud); -- pud++; -- } -- -- pud_free(NULL, pud_base); --} -- --static void arm_smmu_free_pgtables(struct arm_smmu_domain *smmu_domain) --{ -- int i; -- struct arm_smmu_cfg *cfg = &smmu_domain->cfg; -- pgd_t *pgd, *pgd_base = cfg->pgd; -- -- /* -- * Recursively free the page tables for this domain. We don't -- * care about speculative TLB filling because the tables should -- * not be active in any context bank at this point (SCTLR.M is 0). -- */ -- pgd = pgd_base; -- for (i = 0; i < PTRS_PER_PGD; ++i) { -- if (pgd_none(*pgd)) -- continue; -- arm_smmu_free_puds(pgd); -- pgd++; -- } -- -- kfree(pgd_base); --} -- --static void arm_smmu_domain_destroy(struct iommu_domain *domain) --{ -- struct arm_smmu_domain *smmu_domain = domain->priv; -+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); - - /* - * Free the domain resources. We assume that all devices have - * already been detached. - */ - arm_smmu_destroy_domain_context(domain); -- arm_smmu_free_pgtables(smmu_domain); - kfree(smmu_domain); - } - -@@ -1113,7 +1093,7 @@ static int arm_smmu_master_configure_smrs(struct arm_smmu_device *smmu, - - smrs[i] = (struct arm_smmu_smr) { - .idx = idx, -- .mask = 0, /* We don't currently share SMRs */ -+ .mask = cfg->mask, - .id = cfg->streamids[i], - }; - } -@@ -1209,8 +1189,8 @@ static void arm_smmu_domain_remove_master(struct arm_smmu_domain *smmu_domain, - static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) - { - int ret; -- struct arm_smmu_domain *smmu_domain = domain->priv; -- struct arm_smmu_device *smmu, *dom_smmu; -+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); -+ struct arm_smmu_device *smmu; - struct arm_smmu_master_cfg *cfg; - - smmu = find_smmu_for_device(dev); -@@ -1224,21 +1204,16 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) - return -EEXIST; - } - -+ /* Ensure that the domain is finalised */ -+ ret = arm_smmu_init_domain_context(domain, smmu); -+ if (IS_ERR_VALUE(ret)) -+ return ret; -+ - /* - * Sanity check the domain. We don't support domains across - * different SMMUs. - */ -- dom_smmu = ACCESS_ONCE(smmu_domain->smmu); -- if (!dom_smmu) { -- /* Now that we have a master, we can finalise the domain */ -- ret = arm_smmu_init_domain_context(domain, smmu); -- if (IS_ERR_VALUE(ret)) -- return ret; -- -- dom_smmu = smmu_domain->smmu; -- } -- -- if (dom_smmu != smmu) { -+ if (smmu_domain->smmu != smmu) { - dev_err(dev, - "cannot attach to SMMU %s whilst already attached to domain on SMMU %s\n", - dev_name(smmu_domain->smmu->dev), dev_name(smmu->dev)); -@@ -1258,7 +1233,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) - - static void arm_smmu_detach_dev(struct iommu_domain *domain, struct device *dev) - { -- struct arm_smmu_domain *smmu_domain = domain->priv; -+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); - struct arm_smmu_master_cfg *cfg; - - cfg = find_smmu_master_cfg(dev); -@@ -1269,292 +1244,106 @@ static void arm_smmu_detach_dev(struct iommu_domain *domain, struct device *dev) - arm_smmu_domain_remove_master(smmu_domain, cfg); - } - --static bool arm_smmu_pte_is_contiguous_range(unsigned long addr, -- unsigned long end) --{ -- return !(addr & ~ARM_SMMU_PTE_CONT_MASK) && -- (addr + ARM_SMMU_PTE_CONT_SIZE <= end); --} -- --static int arm_smmu_alloc_init_pte(struct arm_smmu_device *smmu, pmd_t *pmd, -- unsigned long addr, unsigned long end, -- unsigned long pfn, int prot, int stage) --{ -- pte_t *pte, *start; -- pteval_t pteval = ARM_SMMU_PTE_PAGE | ARM_SMMU_PTE_AF | ARM_SMMU_PTE_XN; -- -- if (pmd_none(*pmd)) { -- /* Allocate a new set of tables */ -- pgtable_t table = alloc_page(GFP_ATOMIC|__GFP_ZERO); -- -- if (!table) -- return -ENOMEM; -- -- arm_smmu_flush_pgtable(smmu, page_address(table), PAGE_SIZE); -- pmd_populate(NULL, pmd, table); -- arm_smmu_flush_pgtable(smmu, pmd, sizeof(*pmd)); -- } -- -- if (stage == 1) { -- pteval |= ARM_SMMU_PTE_AP_UNPRIV | ARM_SMMU_PTE_nG; -- if (!(prot & IOMMU_WRITE) && (prot & IOMMU_READ)) -- pteval |= ARM_SMMU_PTE_AP_RDONLY; -- -- if (prot & IOMMU_CACHE) -- pteval |= (MAIR_ATTR_IDX_CACHE << -- ARM_SMMU_PTE_ATTRINDX_SHIFT); -- } else { -- pteval |= ARM_SMMU_PTE_HAP_FAULT; -- if (prot & IOMMU_READ) -- pteval |= ARM_SMMU_PTE_HAP_READ; -- if (prot & IOMMU_WRITE) -- pteval |= ARM_SMMU_PTE_HAP_WRITE; -- if (prot & IOMMU_CACHE) -- pteval |= ARM_SMMU_PTE_MEMATTR_OIWB; -- else -- pteval |= ARM_SMMU_PTE_MEMATTR_NC; -- } -- -- /* If no access, create a faulting entry to avoid TLB fills */ -- if (prot & IOMMU_EXEC) -- pteval &= ~ARM_SMMU_PTE_XN; -- else if (!(prot & (IOMMU_READ | IOMMU_WRITE))) -- pteval &= ~ARM_SMMU_PTE_PAGE; -- -- pteval |= ARM_SMMU_PTE_SH_IS; -- start = pmd_page_vaddr(*pmd) + pte_index(addr); -- pte = start; -- -- /* -- * Install the page table entries. This is fairly complicated -- * since we attempt to make use of the contiguous hint in the -- * ptes where possible. The contiguous hint indicates a series -- * of ARM_SMMU_PTE_CONT_ENTRIES ptes mapping a physically -- * contiguous region with the following constraints: -- * -- * - The region start is aligned to ARM_SMMU_PTE_CONT_SIZE -- * - Each pte in the region has the contiguous hint bit set -- * -- * This complicates unmapping (also handled by this code, when -- * neither IOMMU_READ or IOMMU_WRITE are set) because it is -- * possible, yet highly unlikely, that a client may unmap only -- * part of a contiguous range. This requires clearing of the -- * contiguous hint bits in the range before installing the new -- * faulting entries. -- * -- * Note that re-mapping an address range without first unmapping -- * it is not supported, so TLB invalidation is not required here -- * and is instead performed at unmap and domain-init time. -- */ -- do { -- int i = 1; -- -- pteval &= ~ARM_SMMU_PTE_CONT; -- -- if (arm_smmu_pte_is_contiguous_range(addr, end)) { -- i = ARM_SMMU_PTE_CONT_ENTRIES; -- pteval |= ARM_SMMU_PTE_CONT; -- } else if (pte_val(*pte) & -- (ARM_SMMU_PTE_CONT | ARM_SMMU_PTE_PAGE)) { -- int j; -- pte_t *cont_start; -- unsigned long idx = pte_index(addr); -- -- idx &= ~(ARM_SMMU_PTE_CONT_ENTRIES - 1); -- cont_start = pmd_page_vaddr(*pmd) + idx; -- for (j = 0; j < ARM_SMMU_PTE_CONT_ENTRIES; ++j) -- pte_val(*(cont_start + j)) &= -- ~ARM_SMMU_PTE_CONT; -- -- arm_smmu_flush_pgtable(smmu, cont_start, -- sizeof(*pte) * -- ARM_SMMU_PTE_CONT_ENTRIES); -- } -- -- do { -- *pte = pfn_pte(pfn, __pgprot(pteval)); -- } while (pte++, pfn++, addr += PAGE_SIZE, --i); -- } while (addr != end); -- -- arm_smmu_flush_pgtable(smmu, start, sizeof(*pte) * (pte - start)); -- return 0; --} -- --static int arm_smmu_alloc_init_pmd(struct arm_smmu_device *smmu, pud_t *pud, -- unsigned long addr, unsigned long end, -- phys_addr_t phys, int prot, int stage) -+static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova, -+ phys_addr_t paddr, size_t size, int prot) - { - int ret; -- pmd_t *pmd; -- unsigned long next, pfn = __phys_to_pfn(phys); -- --#ifndef __PAGETABLE_PMD_FOLDED -- if (pud_none(*pud)) { -- pmd = (pmd_t *)get_zeroed_page(GFP_ATOMIC); -- if (!pmd) -- return -ENOMEM; -- -- arm_smmu_flush_pgtable(smmu, pmd, PAGE_SIZE); -- pud_populate(NULL, pud, pmd); -- arm_smmu_flush_pgtable(smmu, pud, sizeof(*pud)); -- -- pmd += pmd_index(addr); -- } else --#endif -- pmd = pmd_offset(pud, addr); -+ unsigned long flags; -+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); -+ struct io_pgtable_ops *ops= smmu_domain->pgtbl_ops; - -- do { -- next = pmd_addr_end(addr, end); -- ret = arm_smmu_alloc_init_pte(smmu, pmd, addr, next, pfn, -- prot, stage); -- phys += next - addr; -- pfn = __phys_to_pfn(phys); -- } while (pmd++, addr = next, addr < end); -+ if (!ops) -+ return -ENODEV; - -+ spin_lock_irqsave(&smmu_domain->pgtbl_lock, flags); -+ ret = ops->map(ops, iova, paddr, size, prot); -+ spin_unlock_irqrestore(&smmu_domain->pgtbl_lock, flags); - return ret; - } - --static int arm_smmu_alloc_init_pud(struct arm_smmu_device *smmu, pgd_t *pgd, -- unsigned long addr, unsigned long end, -- phys_addr_t phys, int prot, int stage) -+static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova, -+ size_t size) - { -- int ret = 0; -- pud_t *pud; -- unsigned long next; -- --#ifndef __PAGETABLE_PUD_FOLDED -- if (pgd_none(*pgd)) { -- pud = (pud_t *)get_zeroed_page(GFP_ATOMIC); -- if (!pud) -- return -ENOMEM; -- -- arm_smmu_flush_pgtable(smmu, pud, PAGE_SIZE); -- pgd_populate(NULL, pgd, pud); -- arm_smmu_flush_pgtable(smmu, pgd, sizeof(*pgd)); -- -- pud += pud_index(addr); -- } else --#endif -- pud = pud_offset(pgd, addr); -+ size_t ret; -+ unsigned long flags; -+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); -+ struct io_pgtable_ops *ops= smmu_domain->pgtbl_ops; - -- do { -- next = pud_addr_end(addr, end); -- ret = arm_smmu_alloc_init_pmd(smmu, pud, addr, next, phys, -- prot, stage); -- phys += next - addr; -- } while (pud++, addr = next, addr < end); -+ if (!ops) -+ return 0; - -+ spin_lock_irqsave(&smmu_domain->pgtbl_lock, flags); -+ ret = ops->unmap(ops, iova, size); -+ spin_unlock_irqrestore(&smmu_domain->pgtbl_lock, flags); - return ret; - } - --static int arm_smmu_handle_mapping(struct arm_smmu_domain *smmu_domain, -- unsigned long iova, phys_addr_t paddr, -- size_t size, int prot) -+static phys_addr_t arm_smmu_iova_to_phys_hard(struct iommu_domain *domain, -+ dma_addr_t iova) - { -- int ret, stage; -- unsigned long end; -- phys_addr_t input_mask, output_mask; -+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); - struct arm_smmu_device *smmu = smmu_domain->smmu; - struct arm_smmu_cfg *cfg = &smmu_domain->cfg; -- pgd_t *pgd = cfg->pgd; -- unsigned long flags; -+ struct io_pgtable_ops *ops= smmu_domain->pgtbl_ops; -+ struct device *dev = smmu->dev; -+ void __iomem *cb_base; -+ u32 tmp; -+ u64 phys; -+ -+ cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx); - -- if (cfg->cbar == CBAR_TYPE_S2_TRANS) { -- stage = 2; -- input_mask = (1ULL << smmu->s2_input_size) - 1; -- output_mask = (1ULL << smmu->s2_output_size) - 1; -+ if (smmu->version == 1) { -+ u32 reg = iova & ~0xfff; -+ writel_relaxed(reg, cb_base + ARM_SMMU_CB_ATS1PR_LO); - } else { -- stage = 1; -- input_mask = (1ULL << smmu->s1_input_size) - 1; -- output_mask = (1ULL << smmu->s1_output_size) - 1; -+ u32 reg = iova & ~0xfff; -+ writel_relaxed(reg, cb_base + ARM_SMMU_CB_ATS1PR_LO); -+ reg = ((u64)iova & ~0xfff) >> 32; -+ writel_relaxed(reg, cb_base + ARM_SMMU_CB_ATS1PR_HI); - } - -- if (!pgd) -- return -EINVAL; -- -- if (size & ~PAGE_MASK) -- return -EINVAL; -- -- if ((phys_addr_t)iova & ~input_mask) -- return -ERANGE; -- -- if (paddr & ~output_mask) -- return -ERANGE; -- -- spin_lock_irqsave(&smmu_domain->lock, flags); -- pgd += pgd_index(iova); -- end = iova + size; -- do { -- unsigned long next = pgd_addr_end(iova, end); -- -- ret = arm_smmu_alloc_init_pud(smmu, pgd, iova, next, paddr, -- prot, stage); -- if (ret) -- goto out_unlock; -- -- paddr += next - iova; -- iova = next; -- } while (pgd++, iova != end); -- --out_unlock: -- spin_unlock_irqrestore(&smmu_domain->lock, flags); -- -- return ret; --} -- --static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova, -- phys_addr_t paddr, size_t size, int prot) --{ -- struct arm_smmu_domain *smmu_domain = domain->priv; -- -- if (!smmu_domain) -- return -ENODEV; -+ if (readl_poll_timeout_atomic(cb_base + ARM_SMMU_CB_ATSR, tmp, -+ !(tmp & ATSR_ACTIVE), 5, 50)) { -+ dev_err(dev, -+ "iova to phys timed out on 0x%pad. Falling back to software table walk.\n", -+ &iova); -+ return ops->iova_to_phys(ops, iova); -+ } - -- return arm_smmu_handle_mapping(smmu_domain, iova, paddr, size, prot); --} -+ phys = readl_relaxed(cb_base + ARM_SMMU_CB_PAR_LO); -+ phys |= ((u64)readl_relaxed(cb_base + ARM_SMMU_CB_PAR_HI)) << 32; - --static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova, -- size_t size) --{ -- int ret; -- struct arm_smmu_domain *smmu_domain = domain->priv; -+ if (phys & CB_PAR_F) { -+ dev_err(dev, "translation fault!\n"); -+ dev_err(dev, "PAR = 0x%llx\n", phys); -+ return 0; -+ } - -- ret = arm_smmu_handle_mapping(smmu_domain, iova, 0, size, 0); -- arm_smmu_tlb_inv_context(smmu_domain); -- return ret ? 0 : size; -+ return (phys & GENMASK_ULL(39, 12)) | (iova & 0xfff); - } - - static phys_addr_t arm_smmu_iova_to_phys(struct iommu_domain *domain, -- dma_addr_t iova) -+ dma_addr_t iova) - { -- pgd_t *pgdp, pgd; -- pud_t pud; -- pmd_t pmd; -- pte_t pte; -- struct arm_smmu_domain *smmu_domain = domain->priv; -- struct arm_smmu_cfg *cfg = &smmu_domain->cfg; -- -- pgdp = cfg->pgd; -- if (!pgdp) -- return 0; -- -- pgd = *(pgdp + pgd_index(iova)); -- if (pgd_none(pgd)) -- return 0; -+ phys_addr_t ret; -+ unsigned long flags; -+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); -+ struct io_pgtable_ops *ops= smmu_domain->pgtbl_ops; - -- pud = *pud_offset(&pgd, iova); -- if (pud_none(pud)) -+ if (!ops) - return 0; - -- pmd = *pmd_offset(&pud, iova); -- if (pmd_none(pmd)) -- return 0; -+ spin_lock_irqsave(&smmu_domain->pgtbl_lock, flags); -+ if (smmu_domain->smmu->features & ARM_SMMU_FEAT_TRANS_OPS && -+ smmu_domain->stage == ARM_SMMU_DOMAIN_S1) { -+ ret = arm_smmu_iova_to_phys_hard(domain, iova); -+ } else { -+ ret = ops->iova_to_phys(ops, iova); -+ } - -- pte = *(pmd_page_vaddr(pmd) + pte_index(iova)); -- if (pte_none(pte)) -- return 0; -+ spin_unlock_irqrestore(&smmu_domain->pgtbl_lock, flags); - -- return __pfn_to_phys(pte_pfn(pte)) | (iova & ~PAGE_MASK); -+ return ret; - } - - static bool arm_smmu_capable(enum iommu_cap cap) -@@ -1568,6 +1357,8 @@ static bool arm_smmu_capable(enum iommu_cap cap) - return true; - case IOMMU_CAP_INTR_REMAP: - return true; /* MSIs are just memory writes */ -+ case IOMMU_CAP_NOEXEC: -+ return true; - default: - return false; - } -@@ -1584,81 +1375,248 @@ static void __arm_smmu_release_pci_iommudata(void *data) - kfree(data); - } - --static int arm_smmu_add_device(struct device *dev) -+static int arm_smmu_add_pci_device(struct pci_dev *pdev) - { -- struct arm_smmu_device *smmu; -+ int i, ret; -+ u16 sid; -+ struct iommu_group *group; - struct arm_smmu_master_cfg *cfg; -+#ifdef CONFIG_PCI_LAYERSCAPE -+ u32 streamid; -+#endif -+ -+ group = iommu_group_get_for_dev(&pdev->dev); -+ if (IS_ERR(group)) -+ return PTR_ERR(group); -+ -+ cfg = iommu_group_get_iommudata(group); -+ if (!cfg) { -+ cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); -+ if (!cfg) { -+ ret = -ENOMEM; -+ goto out_put_group; -+ } -+ -+ iommu_group_set_iommudata(group, cfg, -+ __arm_smmu_release_pci_iommudata); -+ } -+ -+ if (cfg->num_streamids >= MAX_MASTER_STREAMIDS) { -+ ret = -ENOSPC; -+ goto out_put_group; -+ } -+ -+ /* -+ * Assume Stream ID == Requester ID for now. -+ * We need a way to describe the ID mappings in FDT. -+ */ -+ pci_for_each_dma_alias(pdev, __arm_smmu_get_pci_sid, &sid); -+ for (i = 0; i < cfg->num_streamids; ++i) -+ if (cfg->streamids[i] == sid) -+ break; -+ -+ /* Avoid duplicate SIDs, as this can lead to SMR conflicts */ -+ if (i == cfg->num_streamids) -+ cfg->streamids[cfg->num_streamids++] = sid; -+ -+#ifdef CONFIG_PCI_LAYERSCAPE -+ streamid = set_pcie_streamid_translation(pdev, sid); -+ if (~streamid == 0) { -+ ret = -ENODEV; -+ goto out_put_group; -+ } -+ cfg->streamids[0] = streamid; -+ cfg->mask = 0x7c00; -+ -+ pdev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVID; -+ pdev->dma_alias_devid = streamid; -+#endif -+ -+ return 0; -+out_put_group: -+ iommu_group_put(group); -+ return ret; -+} -+ -+static int arm_smmu_add_platform_device(struct device *dev) -+{ - struct iommu_group *group; -- void (*releasefn)(void *) = NULL; -- int ret; -+ struct arm_smmu_master *master; -+ struct arm_smmu_device *smmu = find_smmu_for_device(dev); - -- smmu = find_smmu_for_device(dev); - if (!smmu) - return -ENODEV; - -+ master = find_smmu_master(smmu, dev->of_node); -+ if (!master) -+ return -ENODEV; -+ -+ /* No automatic group creation for platform devices */ - group = iommu_group_alloc(); -- if (IS_ERR(group)) { -- dev_err(dev, "Failed to allocate IOMMU group\n"); -+ if (IS_ERR(group)) - return PTR_ERR(group); -+ -+ iommu_group_set_iommudata(group, &master->cfg, NULL); -+ return iommu_group_add_device(group, dev); -+} -+ -+static int arm_smmu_add_device(struct device *dev) -+{ -+ if (dev_is_pci(dev)) -+ return arm_smmu_add_pci_device(to_pci_dev(dev)); -+ -+ return arm_smmu_add_platform_device(dev); -+} -+ -+static void arm_smmu_remove_device(struct device *dev) -+{ -+ iommu_group_remove_device(dev); -+} -+ -+static int arm_smmu_domain_get_attr(struct iommu_domain *domain, -+ enum iommu_attr attr, void *data) -+{ -+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); -+ -+ switch (attr) { -+ case DOMAIN_ATTR_NESTING: -+ *(int *)data = (smmu_domain->stage == ARM_SMMU_DOMAIN_NESTED); -+ return 0; -+ default: -+ return -ENODEV; - } -+} - -- if (dev_is_pci(dev)) { -- struct pci_dev *pdev = to_pci_dev(dev); -+static int arm_smmu_domain_set_attr(struct iommu_domain *domain, -+ enum iommu_attr attr, void *data) -+{ -+ int ret = 0; -+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); - -- cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); -- if (!cfg) { -- ret = -ENOMEM; -- goto out_put_group; -+ mutex_lock(&smmu_domain->init_mutex); -+ -+ switch (attr) { -+ case DOMAIN_ATTR_NESTING: -+ if (smmu_domain->smmu) { -+ ret = -EPERM; -+ goto out_unlock; - } - -- cfg->num_streamids = 1; -+ if (*(int *)data) -+ smmu_domain->stage = ARM_SMMU_DOMAIN_NESTED; -+ else -+ smmu_domain->stage = ARM_SMMU_DOMAIN_S1; -+ -+ break; -+ default: -+ ret = -ENODEV; -+ } -+ -+out_unlock: -+ mutex_unlock(&smmu_domain->init_mutex); -+ return ret; -+} -+ -+static struct iommu_ops arm_smmu_ops = { -+ .capable = arm_smmu_capable, -+ .domain_alloc = arm_smmu_domain_alloc, -+ .domain_free = arm_smmu_domain_free, -+ .attach_dev = arm_smmu_attach_dev, -+ .detach_dev = arm_smmu_detach_dev, -+ .map = arm_smmu_map, -+ .unmap = arm_smmu_unmap, -+ .iova_to_phys = arm_smmu_iova_to_phys, -+ .add_device = arm_smmu_add_device, -+ .remove_device = arm_smmu_remove_device, -+ .domain_get_attr = arm_smmu_domain_get_attr, -+ .domain_set_attr = arm_smmu_domain_set_attr, -+ .pgsize_bitmap = -1UL, /* Restricted during device attach */ -+}; -+ -+#ifdef CONFIG_FSL_MC_BUS -+ -+static void arm_smmu_release_fsl_mc_iommudata(void *data) -+{ -+ kfree(data); -+} -+ -+/* -+ * IOMMU group creation and stream ID programming for -+ * the LS devices -+ * -+ */ -+static int arm_fsl_mc_smmu_add_device(struct device *dev) -+{ -+ struct device *cont_dev; -+ struct fsl_mc_device *mc_dev; -+ struct iommu_group *group; -+ struct arm_smmu_master_cfg *cfg; -+ int ret = 0; -+ -+ mc_dev = to_fsl_mc_device(dev); -+ if (mc_dev->flags & FSL_MC_IS_DPRC) -+ cont_dev = dev; -+ else -+ cont_dev = mc_dev->dev.parent; -+ -+ get_device(cont_dev); -+ group = iommu_group_get(cont_dev); -+ put_device(cont_dev); -+ if (!group) { -+ void (*releasefn)(void *) = NULL; -+ -+ group = iommu_group_alloc(); -+ if (IS_ERR(group)) -+ return PTR_ERR(group); - /* -- * Assume Stream ID == Requester ID for now. -- * We need a way to describe the ID mappings in FDT. -+ * allocate the cfg for the container and associate it with -+ * the iommu group. In the find cfg function we get the cfg -+ * from the iommu group. - */ -- pci_for_each_dma_alias(pdev, __arm_smmu_get_pci_sid, -- &cfg->streamids[0]); -- releasefn = __arm_smmu_release_pci_iommudata; -- } else { -- struct arm_smmu_master *master; -- -- master = find_smmu_master(smmu, dev->of_node); -- if (!master) { -- ret = -ENODEV; -- goto out_put_group; -- } -+ cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); -+ if (!cfg) -+ return -ENOMEM; - -- cfg = &master->cfg; -+ mc_dev = to_fsl_mc_device(cont_dev); -+ cfg->num_streamids = 1; -+ cfg->streamids[0] = mc_dev->icid; -+ cfg->mask = 0x7c00; -+ releasefn = arm_smmu_release_fsl_mc_iommudata; -+ iommu_group_set_iommudata(group, cfg, releasefn); -+ ret = iommu_group_add_device(group, cont_dev); - } - -- iommu_group_set_iommudata(group, cfg, releasefn); -- ret = iommu_group_add_device(group, dev); -+ if (!ret && cont_dev != dev) -+ ret = iommu_group_add_device(group, dev); - --out_put_group: - iommu_group_put(group); -+ - return ret; - } - --static void arm_smmu_remove_device(struct device *dev) -+static void arm_fsl_mc_smmu_remove_device(struct device *dev) - { - iommu_group_remove_device(dev); -+ - } - --static const struct iommu_ops arm_smmu_ops = { -- .capable = arm_smmu_capable, -- .domain_init = arm_smmu_domain_init, -- .domain_destroy = arm_smmu_domain_destroy, -- .attach_dev = arm_smmu_attach_dev, -- .detach_dev = arm_smmu_detach_dev, -- .map = arm_smmu_map, -- .unmap = arm_smmu_unmap, -- .iova_to_phys = arm_smmu_iova_to_phys, -- .add_device = arm_smmu_add_device, -- .remove_device = arm_smmu_remove_device, -- .pgsize_bitmap = (SECTION_SIZE | -- ARM_SMMU_PTE_CONT_SIZE | -- PAGE_SIZE), -+static struct iommu_ops arm_fsl_mc_smmu_ops = { -+ .capable = arm_smmu_capable, -+ .domain_alloc = arm_smmu_domain_alloc, -+ .domain_free = arm_smmu_domain_free, -+ .attach_dev = arm_smmu_attach_dev, -+ .detach_dev = arm_smmu_detach_dev, -+ .map = arm_smmu_map, -+ .unmap = arm_smmu_unmap, -+ .map_sg = default_iommu_map_sg, -+ .iova_to_phys = arm_smmu_iova_to_phys, -+ .add_device = arm_fsl_mc_smmu_add_device, -+ .remove_device = arm_fsl_mc_smmu_remove_device, -+ .domain_get_attr = arm_smmu_domain_get_attr, -+ .domain_set_attr = arm_smmu_domain_set_attr, -+ .pgsize_bitmap = -1UL, /* Restricted during device attach */ - }; -+#endif - - static void arm_smmu_device_reset(struct arm_smmu_device *smmu) - { -@@ -1686,7 +1644,6 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu) - } - - /* Invalidate the TLB, just in case */ -- writel_relaxed(0, gr0_base + ARM_SMMU_GR0_STLBIALL); - writel_relaxed(0, gr0_base + ARM_SMMU_GR0_TLBIALLH); - writel_relaxed(0, gr0_base + ARM_SMMU_GR0_TLBIALLNSNH); - -@@ -1708,7 +1665,7 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu) - reg &= ~(sCR0_BSU_MASK << sCR0_BSU_SHIFT); - - /* Push the button */ -- arm_smmu_tlb_sync(smmu); -+ __arm_smmu_tlb_sync(smmu); - writel(reg, ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0); - } - -@@ -1742,12 +1699,6 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) - - /* ID0 */ - id = readl_relaxed(gr0_base + ARM_SMMU_GR0_ID0); --#ifndef CONFIG_64BIT -- if (((id >> ID0_PTFS_SHIFT) & ID0_PTFS_MASK) == ID0_PTFS_V8_ONLY) { -- dev_err(smmu->dev, "\tno v7 descriptor support!\n"); -- return -ENODEV; -- } --#endif - - /* Restrict available stages based on module parameter */ - if (force_stage == 1) -@@ -1776,6 +1727,11 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) - return -ENODEV; - } - -+ if ((id & ID0_S1TS) && ((smmu->version == 1) || !(id & ID0_ATOSNS))) { -+ smmu->features |= ARM_SMMU_FEAT_TRANS_OPS; -+ dev_notice(smmu->dev, "\taddress translation ops\n"); -+ } -+ - if (id & ID0_CTTW) { - smmu->features |= ARM_SMMU_FEAT_COHERENT_WALK; - dev_notice(smmu->dev, "\tcoherent table walk\n"); -@@ -1820,16 +1776,14 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) - smmu->pgshift = (id & ID1_PAGESIZE) ? 16 : 12; - - /* Check for size mismatch of SMMU address space from mapped region */ -- size = 1 << -- (((id >> ID1_NUMPAGENDXB_SHIFT) & ID1_NUMPAGENDXB_MASK) + 1); -+ size = 1 << (((id >> ID1_NUMPAGENDXB_SHIFT) & ID1_NUMPAGENDXB_MASK) + 1); - size *= 2 << smmu->pgshift; - if (smmu->size != size) - dev_warn(smmu->dev, - "SMMU address space size (0x%lx) differs from mapped region size (0x%lx)!\n", - size, smmu->size); - -- smmu->num_s2_context_banks = (id >> ID1_NUMS2CB_SHIFT) & -- ID1_NUMS2CB_MASK; -+ smmu->num_s2_context_banks = (id >> ID1_NUMS2CB_SHIFT) & ID1_NUMS2CB_MASK; - smmu->num_context_banks = (id >> ID1_NUMCB_SHIFT) & ID1_NUMCB_MASK; - if (smmu->num_s2_context_banks > smmu->num_context_banks) { - dev_err(smmu->dev, "impossible number of S2 context banks!\n"); -@@ -1841,46 +1795,49 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) - /* ID2 */ - id = readl_relaxed(gr0_base + ARM_SMMU_GR0_ID2); - size = arm_smmu_id_size_to_bits((id >> ID2_IAS_SHIFT) & ID2_IAS_MASK); -- smmu->s1_output_size = min_t(unsigned long, PHYS_MASK_SHIFT, size); -- -- /* Stage-2 input size limited due to pgd allocation (PTRS_PER_PGD) */ --#ifdef CONFIG_64BIT -- smmu->s2_input_size = min_t(unsigned long, VA_BITS, size); --#else -- smmu->s2_input_size = min(32UL, size); --#endif -+ smmu->ipa_size = size; - -- /* The stage-2 output mask is also applied for bypass */ -+ /* The output mask is also applied for bypass */ - size = arm_smmu_id_size_to_bits((id >> ID2_OAS_SHIFT) & ID2_OAS_MASK); -- smmu->s2_output_size = min_t(unsigned long, PHYS_MASK_SHIFT, size); -+ smmu->pa_size = size; -+ -+ /* -+ * What the page table walker can address actually depends on which -+ * descriptor format is in use, but since a) we don't know that yet, -+ * and b) it can vary per context bank, this will have to do... -+ */ -+ if (dma_set_mask_and_coherent(smmu->dev, DMA_BIT_MASK(size))) -+ dev_warn(smmu->dev, -+ "failed to set DMA mask for table walker\n"); - - if (smmu->version == ARM_SMMU_V1) { -- smmu->s1_input_size = 32; -+ smmu->va_size = smmu->ipa_size; -+ size = SZ_4K | SZ_2M | SZ_1G; - } else { --#ifdef CONFIG_64BIT - size = (id >> ID2_UBS_SHIFT) & ID2_UBS_MASK; -- size = min(VA_BITS, arm_smmu_id_size_to_bits(size)); --#else -- size = 32; -+ smmu->va_size = arm_smmu_id_size_to_bits(size); -+#ifndef CONFIG_64BIT -+ smmu->va_size = min(32UL, smmu->va_size); - #endif -- smmu->s1_input_size = size; -- -- if ((PAGE_SIZE == SZ_4K && !(id & ID2_PTFS_4K)) || -- (PAGE_SIZE == SZ_64K && !(id & ID2_PTFS_64K)) || -- (PAGE_SIZE != SZ_4K && PAGE_SIZE != SZ_64K)) { -- dev_err(smmu->dev, "CPU page size 0x%lx unsupported\n", -- PAGE_SIZE); -- return -ENODEV; -- } -+ size = 0; -+ if (id & ID2_PTFS_4K) -+ size |= SZ_4K | SZ_2M | SZ_1G; -+ if (id & ID2_PTFS_16K) -+ size |= SZ_16K | SZ_32M; -+ if (id & ID2_PTFS_64K) -+ size |= SZ_64K | SZ_512M; - } - -+ arm_smmu_ops.pgsize_bitmap &= size; -+ dev_notice(smmu->dev, "\tSupported page sizes: 0x%08lx\n", size); -+ - if (smmu->features & ARM_SMMU_FEAT_TRANS_S1) - dev_notice(smmu->dev, "\tStage-1: %lu-bit VA -> %lu-bit IPA\n", -- smmu->s1_input_size, smmu->s1_output_size); -+ smmu->va_size, smmu->ipa_size); - - if (smmu->features & ARM_SMMU_FEAT_TRANS_S2) - dev_notice(smmu->dev, "\tStage-2: %lu-bit IPA -> %lu-bit PA\n", -- smmu->s2_input_size, smmu->s2_output_size); -+ smmu->ipa_size, smmu->pa_size); - - return 0; - } -@@ -2007,6 +1964,10 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev) - spin_unlock(&arm_smmu_devices_lock); - - arm_smmu_device_reset(smmu); -+ /* AIOP Rev1 errata work around */ -+#ifdef CONFIG_AIOP_ERRATA -+ arm_smmu_aiop_attr_trans(smmu); -+#endif - return 0; - - out_free_irqs: -@@ -2062,7 +2023,6 @@ static int arm_smmu_device_remove(struct platform_device *pdev) - - static struct platform_driver arm_smmu_driver = { - .driver = { -- .owner = THIS_MODULE, - .name = "arm-smmu", - .of_match_table = of_match_ptr(arm_smmu_of_match), - }, -@@ -2072,8 +2032,20 @@ static struct platform_driver arm_smmu_driver = { - - static int __init arm_smmu_init(void) - { -+ struct device_node *np; - int ret; - -+ /* -+ * Play nice with systems that don't have an ARM SMMU by checking that -+ * an ARM SMMU exists in the system before proceeding with the driver -+ * and IOMMU bus operation registration. -+ */ -+ np = of_find_matching_node(NULL, arm_smmu_of_match); -+ if (!np) -+ return 0; -+ -+ of_node_put(np); -+ - ret = platform_driver_register(&arm_smmu_driver); - if (ret) - return ret; -@@ -2092,6 +2064,10 @@ static int __init arm_smmu_init(void) - bus_set_iommu(&pci_bus_type, &arm_smmu_ops); - #endif - -+#ifdef CONFIG_FSL_MC_BUS -+ if (!iommu_present(&fsl_mc_bus_type)) -+ bus_set_iommu(&fsl_mc_bus_type, &arm_fsl_mc_smmu_ops); -+#endif - return 0; - } - -diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c -index 7423318..7ce5273 100644 ---- a/drivers/iommu/exynos-iommu.c -+++ b/drivers/iommu/exynos-iommu.c -@@ -684,7 +684,6 @@ static const struct of_device_id sysmmu_of_match[] __initconst = { - static struct platform_driver exynos_sysmmu_driver __refdata = { - .probe = exynos_sysmmu_probe, - .driver = { -- .owner = THIS_MODULE, - .name = "exynos-sysmmu", - .of_match_table = sysmmu_of_match, - } -@@ -1178,6 +1177,7 @@ static const struct iommu_ops exynos_iommu_ops = { - .detach_dev = exynos_iommu_detach_device, - .map = exynos_iommu_map, - .unmap = exynos_iommu_unmap, -+ .map_sg = default_iommu_map_sg, - .iova_to_phys = exynos_iommu_iova_to_phys, - .add_device = exynos_iommu_add_device, - .remove_device = exynos_iommu_remove_device, -diff --git a/drivers/iommu/fsl_pamu.c b/drivers/iommu/fsl_pamu.c -index 2b6ce93..9396c85 100644 ---- a/drivers/iommu/fsl_pamu.c -+++ b/drivers/iommu/fsl_pamu.c -@@ -31,7 +31,7 @@ - #include - #include - #include --#include -+#include - - #include "fsl_pamu.h" - -@@ -1227,7 +1227,6 @@ static const struct of_device_id fsl_of_pamu_ids[] = { - static struct platform_driver fsl_of_pamu_driver = { - .driver = { - .name = "fsl-of-pamu", -- .owner = THIS_MODULE, - }, - .probe = fsl_pamu_probe, - }; -diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c -index 3d1fc73..9e97328 100644 ---- a/drivers/iommu/intel-iommu.c -+++ b/drivers/iommu/intel-iommu.c -@@ -4474,6 +4474,7 @@ static const struct iommu_ops intel_iommu_ops = { - .detach_dev = intel_iommu_detach_device, - .map = intel_iommu_map, - .unmap = intel_iommu_unmap, -+ .map_sg = default_iommu_map_sg, - .iova_to_phys = intel_iommu_iova_to_phys, - .add_device = intel_iommu_add_device, - .remove_device = intel_iommu_remove_device, -diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c -new file mode 100644 -index 0000000..fd6dd22 ---- /dev/null -+++ b/drivers/iommu/io-pgtable-arm.c -@@ -0,0 +1,997 @@ -+/* -+ * CPU-agnostic ARM page table allocator. -+ * -+ * 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. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ * -+ * Copyright (C) 2014 ARM Limited -+ * -+ * Author: Will Deacon -+ */ -+ -+#define pr_fmt(fmt) "arm-lpae io-pgtable: " fmt -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include "io-pgtable.h" -+ -+#define ARM_LPAE_MAX_ADDR_BITS 48 -+#define ARM_LPAE_S2_MAX_CONCAT_PAGES 16 -+#define ARM_LPAE_MAX_LEVELS 4 -+ -+/* Struct accessors */ -+#define io_pgtable_to_data(x) \ -+ container_of((x), struct arm_lpae_io_pgtable, iop) -+ -+#define io_pgtable_ops_to_pgtable(x) \ -+ container_of((x), struct io_pgtable, ops) -+ -+#define io_pgtable_ops_to_data(x) \ -+ io_pgtable_to_data(io_pgtable_ops_to_pgtable(x)) -+ -+/* -+ * For consistency with the architecture, we always consider -+ * ARM_LPAE_MAX_LEVELS levels, with the walk starting at level n >=0 -+ */ -+#define ARM_LPAE_START_LVL(d) (ARM_LPAE_MAX_LEVELS - (d)->levels) -+ -+/* -+ * Calculate the right shift amount to get to the portion describing level l -+ * in a virtual address mapped by the pagetable in d. -+ */ -+#define ARM_LPAE_LVL_SHIFT(l,d) \ -+ ((((d)->levels - ((l) - ARM_LPAE_START_LVL(d) + 1)) \ -+ * (d)->bits_per_level) + (d)->pg_shift) -+ -+#define ARM_LPAE_PAGES_PER_PGD(d) \ -+ DIV_ROUND_UP((d)->pgd_size, 1UL << (d)->pg_shift) -+ -+/* -+ * Calculate the index at level l used to map virtual address a using the -+ * pagetable in d. -+ */ -+#define ARM_LPAE_PGD_IDX(l,d) \ -+ ((l) == ARM_LPAE_START_LVL(d) ? ilog2(ARM_LPAE_PAGES_PER_PGD(d)) : 0) -+ -+#define ARM_LPAE_LVL_IDX(a,l,d) \ -+ (((u64)(a) >> ARM_LPAE_LVL_SHIFT(l,d)) & \ -+ ((1 << ((d)->bits_per_level + ARM_LPAE_PGD_IDX(l,d))) - 1)) -+ -+/* Calculate the block/page mapping size at level l for pagetable in d. */ -+#define ARM_LPAE_BLOCK_SIZE(l,d) \ -+ (1 << (ilog2(sizeof(arm_lpae_iopte)) + \ -+ ((ARM_LPAE_MAX_LEVELS - (l)) * (d)->bits_per_level))) -+ -+/* Page table bits */ -+#define ARM_LPAE_PTE_TYPE_SHIFT 0 -+#define ARM_LPAE_PTE_TYPE_MASK 0x3 -+ -+#define ARM_LPAE_PTE_TYPE_BLOCK 1 -+#define ARM_LPAE_PTE_TYPE_TABLE 3 -+#define ARM_LPAE_PTE_TYPE_PAGE 3 -+ -+#define ARM_LPAE_PTE_NSTABLE (((arm_lpae_iopte)1) << 63) -+#define ARM_LPAE_PTE_XN (((arm_lpae_iopte)3) << 53) -+#define ARM_LPAE_PTE_AF (((arm_lpae_iopte)1) << 10) -+#define ARM_LPAE_PTE_SH_NS (((arm_lpae_iopte)0) << 8) -+#define ARM_LPAE_PTE_SH_OS (((arm_lpae_iopte)2) << 8) -+#define ARM_LPAE_PTE_SH_IS (((arm_lpae_iopte)3) << 8) -+#define ARM_LPAE_PTE_NS (((arm_lpae_iopte)1) << 5) -+#define ARM_LPAE_PTE_VALID (((arm_lpae_iopte)1) << 0) -+ -+#define ARM_LPAE_PTE_ATTR_LO_MASK (((arm_lpae_iopte)0x3ff) << 2) -+/* Ignore the contiguous bit for block splitting */ -+#define ARM_LPAE_PTE_ATTR_HI_MASK (((arm_lpae_iopte)6) << 52) -+#define ARM_LPAE_PTE_ATTR_MASK (ARM_LPAE_PTE_ATTR_LO_MASK | \ -+ ARM_LPAE_PTE_ATTR_HI_MASK) -+ -+/* Stage-1 PTE */ -+#define ARM_LPAE_PTE_AP_UNPRIV (((arm_lpae_iopte)1) << 6) -+#define ARM_LPAE_PTE_AP_RDONLY (((arm_lpae_iopte)2) << 6) -+#define ARM_LPAE_PTE_ATTRINDX_SHIFT 2 -+#define ARM_LPAE_PTE_nG (((arm_lpae_iopte)1) << 11) -+ -+/* Stage-2 PTE */ -+#define ARM_LPAE_PTE_HAP_FAULT (((arm_lpae_iopte)0) << 6) -+#define ARM_LPAE_PTE_HAP_READ (((arm_lpae_iopte)1) << 6) -+#define ARM_LPAE_PTE_HAP_WRITE (((arm_lpae_iopte)2) << 6) -+#define ARM_LPAE_PTE_MEMATTR_OIWB (((arm_lpae_iopte)0xf) << 2) -+#define ARM_LPAE_PTE_MEMATTR_NC (((arm_lpae_iopte)0x5) << 2) -+#define ARM_LPAE_PTE_MEMATTR_DEV (((arm_lpae_iopte)0x1) << 2) -+ -+/* Register bits */ -+#define ARM_32_LPAE_TCR_EAE (1 << 31) -+#define ARM_64_LPAE_S2_TCR_RES1 (1 << 31) -+ -+#define ARM_LPAE_TCR_EPD1 (1 << 23) -+ -+#define ARM_LPAE_TCR_TG0_4K (0 << 14) -+#define ARM_LPAE_TCR_TG0_64K (1 << 14) -+#define ARM_LPAE_TCR_TG0_16K (2 << 14) -+ -+#define ARM_LPAE_TCR_SH0_SHIFT 12 -+#define ARM_LPAE_TCR_SH0_MASK 0x3 -+#define ARM_LPAE_TCR_SH_NS 0 -+#define ARM_LPAE_TCR_SH_OS 2 -+#define ARM_LPAE_TCR_SH_IS 3 -+ -+#define ARM_LPAE_TCR_ORGN0_SHIFT 10 -+#define ARM_LPAE_TCR_IRGN0_SHIFT 8 -+#define ARM_LPAE_TCR_RGN_MASK 0x3 -+#define ARM_LPAE_TCR_RGN_NC 0 -+#define ARM_LPAE_TCR_RGN_WBWA 1 -+#define ARM_LPAE_TCR_RGN_WT 2 -+#define ARM_LPAE_TCR_RGN_WB 3 -+ -+#define ARM_LPAE_TCR_SL0_SHIFT 6 -+#define ARM_LPAE_TCR_SL0_MASK 0x3 -+ -+#define ARM_LPAE_TCR_T0SZ_SHIFT 0 -+#define ARM_LPAE_TCR_SZ_MASK 0xf -+ -+#define ARM_LPAE_TCR_PS_SHIFT 16 -+#define ARM_LPAE_TCR_PS_MASK 0x7 -+ -+#define ARM_LPAE_TCR_IPS_SHIFT 32 -+#define ARM_LPAE_TCR_IPS_MASK 0x7 -+ -+#define ARM_LPAE_TCR_PS_32_BIT 0x0ULL -+#define ARM_LPAE_TCR_PS_36_BIT 0x1ULL -+#define ARM_LPAE_TCR_PS_40_BIT 0x2ULL -+#define ARM_LPAE_TCR_PS_42_BIT 0x3ULL -+#define ARM_LPAE_TCR_PS_44_BIT 0x4ULL -+#define ARM_LPAE_TCR_PS_48_BIT 0x5ULL -+ -+#define ARM_LPAE_MAIR_ATTR_SHIFT(n) ((n) << 3) -+#define ARM_LPAE_MAIR_ATTR_MASK 0xff -+#define ARM_LPAE_MAIR_ATTR_DEVICE 0x04 -+#define ARM_LPAE_MAIR_ATTR_NC 0x44 -+#define ARM_LPAE_MAIR_ATTR_WBRWA 0xff -+#define ARM_LPAE_MAIR_ATTR_IDX_NC 0 -+#define ARM_LPAE_MAIR_ATTR_IDX_CACHE 1 -+#define ARM_LPAE_MAIR_ATTR_IDX_DEV 2 -+ -+/* IOPTE accessors */ -+#define iopte_deref(pte,d) \ -+ (__va((pte) & ((1ULL << ARM_LPAE_MAX_ADDR_BITS) - 1) \ -+ & ~((1ULL << (d)->pg_shift) - 1))) -+ -+#define iopte_type(pte,l) \ -+ (((pte) >> ARM_LPAE_PTE_TYPE_SHIFT) & ARM_LPAE_PTE_TYPE_MASK) -+ -+#define iopte_prot(pte) ((pte) & ARM_LPAE_PTE_ATTR_MASK) -+ -+#define iopte_leaf(pte,l) \ -+ (l == (ARM_LPAE_MAX_LEVELS - 1) ? \ -+ (iopte_type(pte,l) == ARM_LPAE_PTE_TYPE_PAGE) : \ -+ (iopte_type(pte,l) == ARM_LPAE_PTE_TYPE_BLOCK)) -+ -+#define iopte_to_pfn(pte,d) \ -+ (((pte) & ((1ULL << ARM_LPAE_MAX_ADDR_BITS) - 1)) >> (d)->pg_shift) -+ -+#define pfn_to_iopte(pfn,d) \ -+ (((pfn) << (d)->pg_shift) & ((1ULL << ARM_LPAE_MAX_ADDR_BITS) - 1)) -+ -+struct arm_lpae_io_pgtable { -+ struct io_pgtable iop; -+ -+ int levels; -+ size_t pgd_size; -+ unsigned long pg_shift; -+ unsigned long bits_per_level; -+ -+ void *pgd; -+}; -+ -+typedef u64 arm_lpae_iopte; -+ -+static bool selftest_running = false; -+ -+static int arm_lpae_init_pte(struct arm_lpae_io_pgtable *data, -+ unsigned long iova, phys_addr_t paddr, -+ arm_lpae_iopte prot, int lvl, -+ arm_lpae_iopte *ptep) -+{ -+ arm_lpae_iopte pte = prot; -+ -+ /* We require an unmap first */ -+ if (iopte_leaf(*ptep, lvl)) { -+ WARN_ON(!selftest_running); -+ return -EEXIST; -+ } -+ -+ if (data->iop.cfg.quirks & IO_PGTABLE_QUIRK_ARM_NS) -+ pte |= ARM_LPAE_PTE_NS; -+ -+ if (lvl == ARM_LPAE_MAX_LEVELS - 1) -+ pte |= ARM_LPAE_PTE_TYPE_PAGE; -+ else -+ pte |= ARM_LPAE_PTE_TYPE_BLOCK; -+ -+ pte |= ARM_LPAE_PTE_AF | ARM_LPAE_PTE_SH_IS; -+ pte |= pfn_to_iopte(paddr >> data->pg_shift, data); -+ -+ *ptep = pte; -+ data->iop.cfg.tlb->flush_pgtable(ptep, sizeof(*ptep), data->iop.cookie); -+ return 0; -+} -+ -+static int __arm_lpae_map(struct arm_lpae_io_pgtable *data, unsigned long iova, -+ phys_addr_t paddr, size_t size, arm_lpae_iopte prot, -+ int lvl, arm_lpae_iopte *ptep) -+{ -+ arm_lpae_iopte *cptep, pte; -+ void *cookie = data->iop.cookie; -+ size_t block_size = ARM_LPAE_BLOCK_SIZE(lvl, data); -+ -+ /* Find our entry at the current level */ -+ ptep += ARM_LPAE_LVL_IDX(iova, lvl, data); -+ -+ /* If we can install a leaf entry at this level, then do so */ -+ if (size == block_size && (size & data->iop.cfg.pgsize_bitmap)) -+ return arm_lpae_init_pte(data, iova, paddr, prot, lvl, ptep); -+ -+ /* We can't allocate tables at the final level */ -+ if (WARN_ON(lvl >= ARM_LPAE_MAX_LEVELS - 1)) -+ return -EINVAL; -+ -+ /* Grab a pointer to the next level */ -+ pte = *ptep; -+ if (!pte) { -+ cptep = alloc_pages_exact(1UL << data->pg_shift, -+ GFP_ATOMIC | __GFP_ZERO); -+ if (!cptep) -+ return -ENOMEM; -+ -+ data->iop.cfg.tlb->flush_pgtable(cptep, 1UL << data->pg_shift, -+ cookie); -+ pte = __pa(cptep) | ARM_LPAE_PTE_TYPE_TABLE; -+ if (data->iop.cfg.quirks & IO_PGTABLE_QUIRK_ARM_NS) -+ pte |= ARM_LPAE_PTE_NSTABLE; -+ *ptep = pte; -+ data->iop.cfg.tlb->flush_pgtable(ptep, sizeof(*ptep), cookie); -+ } else { -+ cptep = iopte_deref(pte, data); -+ } -+ -+ /* Rinse, repeat */ -+ return __arm_lpae_map(data, iova, paddr, size, prot, lvl + 1, cptep); -+} -+ -+static arm_lpae_iopte arm_lpae_prot_to_pte(struct arm_lpae_io_pgtable *data, -+ int prot) -+{ -+ arm_lpae_iopte pte; -+ -+ if (data->iop.fmt == ARM_64_LPAE_S1 || -+ data->iop.fmt == ARM_32_LPAE_S1) { -+ pte = ARM_LPAE_PTE_AP_UNPRIV | ARM_LPAE_PTE_nG; -+ -+ if (!(prot & IOMMU_WRITE) && (prot & IOMMU_READ)) -+ pte |= ARM_LPAE_PTE_AP_RDONLY; -+ -+ if (prot & IOMMU_CACHE) -+ pte |= (ARM_LPAE_MAIR_ATTR_IDX_CACHE -+ << ARM_LPAE_PTE_ATTRINDX_SHIFT); -+ else if (prot & IOMMU_MMIO) -+ pte |= (ARM_LPAE_MAIR_ATTR_IDX_DEV -+ << ARM_LPAE_PTE_ATTRINDX_SHIFT); -+ } else { -+ pte = ARM_LPAE_PTE_HAP_FAULT; -+ if (prot & IOMMU_READ) -+ pte |= ARM_LPAE_PTE_HAP_READ; -+ if (prot & IOMMU_WRITE) -+ pte |= ARM_LPAE_PTE_HAP_WRITE; -+ if (prot & IOMMU_CACHE) -+ pte |= ARM_LPAE_PTE_MEMATTR_OIWB; -+ else if (prot & IOMMU_MMIO) -+ pte |= ARM_LPAE_PTE_MEMATTR_DEV; -+ else -+ pte |= ARM_LPAE_PTE_MEMATTR_NC; -+ } -+ -+ if (prot & IOMMU_NOEXEC) -+ pte |= ARM_LPAE_PTE_XN; -+ -+ return pte; -+} -+ -+static int arm_lpae_map(struct io_pgtable_ops *ops, unsigned long iova, -+ phys_addr_t paddr, size_t size, int iommu_prot) -+{ -+ struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops); -+ arm_lpae_iopte *ptep = data->pgd; -+ int lvl = ARM_LPAE_START_LVL(data); -+ arm_lpae_iopte prot; -+ -+ /* If no access, then nothing to do */ -+ if (!(iommu_prot & (IOMMU_READ | IOMMU_WRITE))) -+ return 0; -+ -+ prot = arm_lpae_prot_to_pte(data, iommu_prot); -+ return __arm_lpae_map(data, iova, paddr, size, prot, lvl, ptep); -+} -+ -+static void __arm_lpae_free_pgtable(struct arm_lpae_io_pgtable *data, int lvl, -+ arm_lpae_iopte *ptep) -+{ -+ arm_lpae_iopte *start, *end; -+ unsigned long table_size; -+ -+ /* Only leaf entries at the last level */ -+ if (lvl == ARM_LPAE_MAX_LEVELS - 1) -+ return; -+ -+ if (lvl == ARM_LPAE_START_LVL(data)) -+ table_size = data->pgd_size; -+ else -+ table_size = 1UL << data->pg_shift; -+ -+ start = ptep; -+ end = (void *)ptep + table_size; -+ -+ while (ptep != end) { -+ arm_lpae_iopte pte = *ptep++; -+ -+ if (!pte || iopte_leaf(pte, lvl)) -+ continue; -+ -+ __arm_lpae_free_pgtable(data, lvl + 1, iopte_deref(pte, data)); -+ } -+ -+ free_pages_exact(start, table_size); -+} -+ -+static void arm_lpae_free_pgtable(struct io_pgtable *iop) -+{ -+ struct arm_lpae_io_pgtable *data = io_pgtable_to_data(iop); -+ -+ __arm_lpae_free_pgtable(data, ARM_LPAE_START_LVL(data), data->pgd); -+ kfree(data); -+} -+ -+static int arm_lpae_split_blk_unmap(struct arm_lpae_io_pgtable *data, -+ unsigned long iova, size_t size, -+ arm_lpae_iopte prot, int lvl, -+ arm_lpae_iopte *ptep, size_t blk_size) -+{ -+ unsigned long blk_start, blk_end; -+ phys_addr_t blk_paddr; -+ arm_lpae_iopte table = 0; -+ void *cookie = data->iop.cookie; -+ const struct iommu_gather_ops *tlb = data->iop.cfg.tlb; -+ -+ blk_start = iova & ~(blk_size - 1); -+ blk_end = blk_start + blk_size; -+ blk_paddr = iopte_to_pfn(*ptep, data) << data->pg_shift; -+ -+ for (; blk_start < blk_end; blk_start += size, blk_paddr += size) { -+ arm_lpae_iopte *tablep; -+ -+ /* Unmap! */ -+ if (blk_start == iova) -+ continue; -+ -+ /* __arm_lpae_map expects a pointer to the start of the table */ -+ tablep = &table - ARM_LPAE_LVL_IDX(blk_start, lvl, data); -+ if (__arm_lpae_map(data, blk_start, blk_paddr, size, prot, lvl, -+ tablep) < 0) { -+ if (table) { -+ /* Free the table we allocated */ -+ tablep = iopte_deref(table, data); -+ __arm_lpae_free_pgtable(data, lvl + 1, tablep); -+ } -+ return 0; /* Bytes unmapped */ -+ } -+ } -+ -+ *ptep = table; -+ tlb->flush_pgtable(ptep, sizeof(*ptep), cookie); -+ iova &= ~(blk_size - 1); -+ tlb->tlb_add_flush(iova, blk_size, true, cookie); -+ return size; -+} -+ -+static int __arm_lpae_unmap(struct arm_lpae_io_pgtable *data, -+ unsigned long iova, size_t size, int lvl, -+ arm_lpae_iopte *ptep) -+{ -+ arm_lpae_iopte pte; -+ const struct iommu_gather_ops *tlb = data->iop.cfg.tlb; -+ void *cookie = data->iop.cookie; -+ size_t blk_size = ARM_LPAE_BLOCK_SIZE(lvl, data); -+ -+ ptep += ARM_LPAE_LVL_IDX(iova, lvl, data); -+ pte = *ptep; -+ -+ /* Something went horribly wrong and we ran out of page table */ -+ if (WARN_ON(!pte || (lvl == ARM_LPAE_MAX_LEVELS))) -+ return 0; -+ -+ /* If the size matches this level, we're in the right place */ -+ if (size == blk_size) { -+ *ptep = 0; -+ tlb->flush_pgtable(ptep, sizeof(*ptep), cookie); -+ -+ if (!iopte_leaf(pte, lvl)) { -+ /* Also flush any partial walks */ -+ tlb->tlb_add_flush(iova, size, false, cookie); -+ tlb->tlb_sync(data->iop.cookie); -+ ptep = iopte_deref(pte, data); -+ __arm_lpae_free_pgtable(data, lvl + 1, ptep); -+ } else { -+ tlb->tlb_add_flush(iova, size, true, cookie); -+ } -+ -+ return size; -+ } else if (iopte_leaf(pte, lvl)) { -+ /* -+ * Insert a table at the next level to map the old region, -+ * minus the part we want to unmap -+ */ -+ return arm_lpae_split_blk_unmap(data, iova, size, -+ iopte_prot(pte), lvl, ptep, -+ blk_size); -+ } -+ -+ /* Keep on walkin' */ -+ ptep = iopte_deref(pte, data); -+ return __arm_lpae_unmap(data, iova, size, lvl + 1, ptep); -+} -+ -+static int arm_lpae_unmap(struct io_pgtable_ops *ops, unsigned long iova, -+ size_t size) -+{ -+ size_t unmapped; -+ struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops); -+ struct io_pgtable *iop = &data->iop; -+ arm_lpae_iopte *ptep = data->pgd; -+ int lvl = ARM_LPAE_START_LVL(data); -+ -+ unmapped = __arm_lpae_unmap(data, iova, size, lvl, ptep); -+ if (unmapped) -+ iop->cfg.tlb->tlb_sync(iop->cookie); -+ -+ return unmapped; -+} -+ -+static phys_addr_t arm_lpae_iova_to_phys(struct io_pgtable_ops *ops, -+ unsigned long iova) -+{ -+ struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops); -+ arm_lpae_iopte pte, *ptep = data->pgd; -+ int lvl = ARM_LPAE_START_LVL(data); -+ -+ do { -+ /* Valid IOPTE pointer? */ -+ if (!ptep) -+ return 0; -+ -+ /* Grab the IOPTE we're interested in */ -+ pte = *(ptep + ARM_LPAE_LVL_IDX(iova, lvl, data)); -+ -+ /* Valid entry? */ -+ if (!pte) -+ return 0; -+ -+ /* Leaf entry? */ -+ if (iopte_leaf(pte,lvl)) -+ goto found_translation; -+ -+ /* Take it to the next level */ -+ ptep = iopte_deref(pte, data); -+ } while (++lvl < ARM_LPAE_MAX_LEVELS); -+ -+ /* Ran out of page tables to walk */ -+ return 0; -+ -+found_translation: -+ iova &= ((1 << data->pg_shift) - 1); -+ return ((phys_addr_t)iopte_to_pfn(pte,data) << data->pg_shift) | iova; -+} -+ -+static void arm_lpae_restrict_pgsizes(struct io_pgtable_cfg *cfg) -+{ -+ unsigned long granule; -+ -+ /* -+ * We need to restrict the supported page sizes to match the -+ * translation regime for a particular granule. Aim to match -+ * the CPU page size if possible, otherwise prefer smaller sizes. -+ * While we're at it, restrict the block sizes to match the -+ * chosen granule. -+ */ -+ if (cfg->pgsize_bitmap & PAGE_SIZE) -+ granule = PAGE_SIZE; -+ else if (cfg->pgsize_bitmap & ~PAGE_MASK) -+ granule = 1UL << __fls(cfg->pgsize_bitmap & ~PAGE_MASK); -+ else if (cfg->pgsize_bitmap & PAGE_MASK) -+ granule = 1UL << __ffs(cfg->pgsize_bitmap & PAGE_MASK); -+ else -+ granule = 0; -+ -+ switch (granule) { -+ case SZ_4K: -+ cfg->pgsize_bitmap &= (SZ_4K | SZ_2M | SZ_1G); -+ break; -+ case SZ_16K: -+ cfg->pgsize_bitmap &= (SZ_16K | SZ_32M); -+ break; -+ case SZ_64K: -+ cfg->pgsize_bitmap &= (SZ_64K | SZ_512M); -+ break; -+ default: -+ cfg->pgsize_bitmap = 0; -+ } -+} -+ -+static struct arm_lpae_io_pgtable * -+arm_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg) -+{ -+ unsigned long va_bits, pgd_bits; -+ struct arm_lpae_io_pgtable *data; -+ -+ arm_lpae_restrict_pgsizes(cfg); -+ -+ if (!(cfg->pgsize_bitmap & (SZ_4K | SZ_16K | SZ_64K))) -+ return NULL; -+ -+ if (cfg->ias > ARM_LPAE_MAX_ADDR_BITS) -+ return NULL; -+ -+ if (cfg->oas > ARM_LPAE_MAX_ADDR_BITS) -+ return NULL; -+ -+ data = kmalloc(sizeof(*data), GFP_KERNEL); -+ if (!data) -+ return NULL; -+ -+ data->pg_shift = __ffs(cfg->pgsize_bitmap); -+ data->bits_per_level = data->pg_shift - ilog2(sizeof(arm_lpae_iopte)); -+ -+ va_bits = cfg->ias - data->pg_shift; -+ data->levels = DIV_ROUND_UP(va_bits, data->bits_per_level); -+ -+ /* Calculate the actual size of our pgd (without concatenation) */ -+ pgd_bits = va_bits - (data->bits_per_level * (data->levels - 1)); -+ data->pgd_size = 1UL << (pgd_bits + ilog2(sizeof(arm_lpae_iopte))); -+ -+ data->iop.ops = (struct io_pgtable_ops) { -+ .map = arm_lpae_map, -+ .unmap = arm_lpae_unmap, -+ .iova_to_phys = arm_lpae_iova_to_phys, -+ }; -+ -+ return data; -+} -+ -+static struct io_pgtable * -+arm_64_lpae_alloc_pgtable_s1(struct io_pgtable_cfg *cfg, void *cookie) -+{ -+ u64 reg; -+ struct arm_lpae_io_pgtable *data = arm_lpae_alloc_pgtable(cfg); -+ -+ if (!data) -+ return NULL; -+ -+ /* TCR */ -+ reg = (ARM_LPAE_TCR_SH_IS << ARM_LPAE_TCR_SH0_SHIFT) | -+ (ARM_LPAE_TCR_RGN_WBWA << ARM_LPAE_TCR_IRGN0_SHIFT) | -+ (ARM_LPAE_TCR_RGN_WBWA << ARM_LPAE_TCR_ORGN0_SHIFT); -+ -+ switch (1 << data->pg_shift) { -+ case SZ_4K: -+ reg |= ARM_LPAE_TCR_TG0_4K; -+ break; -+ case SZ_16K: -+ reg |= ARM_LPAE_TCR_TG0_16K; -+ break; -+ case SZ_64K: -+ reg |= ARM_LPAE_TCR_TG0_64K; -+ break; -+ } -+ -+ switch (cfg->oas) { -+ case 32: -+ reg |= (ARM_LPAE_TCR_PS_32_BIT << ARM_LPAE_TCR_IPS_SHIFT); -+ break; -+ case 36: -+ reg |= (ARM_LPAE_TCR_PS_36_BIT << ARM_LPAE_TCR_IPS_SHIFT); -+ break; -+ case 40: -+ reg |= (ARM_LPAE_TCR_PS_40_BIT << ARM_LPAE_TCR_IPS_SHIFT); -+ break; -+ case 42: -+ reg |= (ARM_LPAE_TCR_PS_42_BIT << ARM_LPAE_TCR_IPS_SHIFT); -+ break; -+ case 44: -+ reg |= (ARM_LPAE_TCR_PS_44_BIT << ARM_LPAE_TCR_IPS_SHIFT); -+ break; -+ case 48: -+ reg |= (ARM_LPAE_TCR_PS_48_BIT << ARM_LPAE_TCR_IPS_SHIFT); -+ break; -+ default: -+ goto out_free_data; -+ } -+ -+ reg |= (64ULL - cfg->ias) << ARM_LPAE_TCR_T0SZ_SHIFT; -+ -+ /* Disable speculative walks through TTBR1 */ -+ reg |= ARM_LPAE_TCR_EPD1; -+ cfg->arm_lpae_s1_cfg.tcr = reg; -+ -+ /* MAIRs */ -+ reg = (ARM_LPAE_MAIR_ATTR_NC -+ << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_NC)) | -+ (ARM_LPAE_MAIR_ATTR_WBRWA -+ << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_CACHE)) | -+ (ARM_LPAE_MAIR_ATTR_DEVICE -+ << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_DEV)); -+ -+ cfg->arm_lpae_s1_cfg.mair[0] = reg; -+ cfg->arm_lpae_s1_cfg.mair[1] = 0; -+ -+ /* Looking good; allocate a pgd */ -+ data->pgd = alloc_pages_exact(data->pgd_size, GFP_KERNEL | __GFP_ZERO); -+ if (!data->pgd) -+ goto out_free_data; -+ -+ cfg->tlb->flush_pgtable(data->pgd, data->pgd_size, cookie); -+ -+ /* TTBRs */ -+ cfg->arm_lpae_s1_cfg.ttbr[0] = virt_to_phys(data->pgd); -+ cfg->arm_lpae_s1_cfg.ttbr[1] = 0; -+ return &data->iop; -+ -+out_free_data: -+ kfree(data); -+ return NULL; -+} -+ -+static struct io_pgtable * -+arm_64_lpae_alloc_pgtable_s2(struct io_pgtable_cfg *cfg, void *cookie) -+{ -+ u64 reg, sl; -+ struct arm_lpae_io_pgtable *data = arm_lpae_alloc_pgtable(cfg); -+ -+ if (!data) -+ return NULL; -+ -+ /* -+ * Concatenate PGDs at level 1 if possible in order to reduce -+ * the depth of the stage-2 walk. -+ */ -+ if (data->levels == ARM_LPAE_MAX_LEVELS) { -+ unsigned long pgd_pages; -+ -+ pgd_pages = data->pgd_size >> ilog2(sizeof(arm_lpae_iopte)); -+ if (pgd_pages <= ARM_LPAE_S2_MAX_CONCAT_PAGES) { -+ data->pgd_size = pgd_pages << data->pg_shift; -+ data->levels--; -+ } -+ } -+ -+ /* VTCR */ -+ reg = ARM_64_LPAE_S2_TCR_RES1 | -+ (ARM_LPAE_TCR_SH_IS << ARM_LPAE_TCR_SH0_SHIFT) | -+ (ARM_LPAE_TCR_RGN_WBWA << ARM_LPAE_TCR_IRGN0_SHIFT) | -+ (ARM_LPAE_TCR_RGN_WBWA << ARM_LPAE_TCR_ORGN0_SHIFT); -+ -+ sl = ARM_LPAE_START_LVL(data); -+ -+ switch (1 << data->pg_shift) { -+ case SZ_4K: -+ reg |= ARM_LPAE_TCR_TG0_4K; -+ sl++; /* SL0 format is different for 4K granule size */ -+ break; -+ case SZ_16K: -+ reg |= ARM_LPAE_TCR_TG0_16K; -+ break; -+ case SZ_64K: -+ reg |= ARM_LPAE_TCR_TG0_64K; -+ break; -+ } -+ -+ switch (cfg->oas) { -+ case 32: -+ reg |= (ARM_LPAE_TCR_PS_32_BIT << ARM_LPAE_TCR_PS_SHIFT); -+ break; -+ case 36: -+ reg |= (ARM_LPAE_TCR_PS_36_BIT << ARM_LPAE_TCR_PS_SHIFT); -+ break; -+ case 40: -+ reg |= (ARM_LPAE_TCR_PS_40_BIT << ARM_LPAE_TCR_PS_SHIFT); -+ break; -+ case 42: -+ reg |= (ARM_LPAE_TCR_PS_42_BIT << ARM_LPAE_TCR_PS_SHIFT); -+ break; -+ case 44: -+ reg |= (ARM_LPAE_TCR_PS_44_BIT << ARM_LPAE_TCR_PS_SHIFT); -+ break; -+ case 48: -+ reg |= (ARM_LPAE_TCR_PS_48_BIT << ARM_LPAE_TCR_PS_SHIFT); -+ break; -+ default: -+ goto out_free_data; -+ } -+ -+ reg |= (64ULL - cfg->ias) << ARM_LPAE_TCR_T0SZ_SHIFT; -+ reg |= (~sl & ARM_LPAE_TCR_SL0_MASK) << ARM_LPAE_TCR_SL0_SHIFT; -+ cfg->arm_lpae_s2_cfg.vtcr = reg; -+ -+ /* Allocate pgd pages */ -+ data->pgd = alloc_pages_exact(data->pgd_size, GFP_KERNEL | __GFP_ZERO); -+ if (!data->pgd) -+ goto out_free_data; -+ -+ cfg->tlb->flush_pgtable(data->pgd, data->pgd_size, cookie); -+ -+ /* VTTBR */ -+ cfg->arm_lpae_s2_cfg.vttbr = virt_to_phys(data->pgd); -+ return &data->iop; -+ -+out_free_data: -+ kfree(data); -+ return NULL; -+} -+ -+static struct io_pgtable * -+arm_32_lpae_alloc_pgtable_s1(struct io_pgtable_cfg *cfg, void *cookie) -+{ -+ struct io_pgtable *iop; -+ -+ if (cfg->ias > 32 || cfg->oas > 40) -+ return NULL; -+ -+ cfg->pgsize_bitmap &= (SZ_4K | SZ_2M | SZ_1G); -+ iop = arm_64_lpae_alloc_pgtable_s1(cfg, cookie); -+ if (iop) { -+ cfg->arm_lpae_s1_cfg.tcr |= ARM_32_LPAE_TCR_EAE; -+ cfg->arm_lpae_s1_cfg.tcr &= 0xffffffff; -+ } -+ -+ return iop; -+} -+ -+static struct io_pgtable * -+arm_32_lpae_alloc_pgtable_s2(struct io_pgtable_cfg *cfg, void *cookie) -+{ -+ struct io_pgtable *iop; -+ -+ if (cfg->ias > 40 || cfg->oas > 40) -+ return NULL; -+ -+ cfg->pgsize_bitmap &= (SZ_4K | SZ_2M | SZ_1G); -+ iop = arm_64_lpae_alloc_pgtable_s2(cfg, cookie); -+ if (iop) -+ cfg->arm_lpae_s2_cfg.vtcr &= 0xffffffff; -+ -+ return iop; -+} -+ -+struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns = { -+ .alloc = arm_64_lpae_alloc_pgtable_s1, -+ .free = arm_lpae_free_pgtable, -+}; -+ -+struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s2_init_fns = { -+ .alloc = arm_64_lpae_alloc_pgtable_s2, -+ .free = arm_lpae_free_pgtable, -+}; -+ -+struct io_pgtable_init_fns io_pgtable_arm_32_lpae_s1_init_fns = { -+ .alloc = arm_32_lpae_alloc_pgtable_s1, -+ .free = arm_lpae_free_pgtable, -+}; -+ -+struct io_pgtable_init_fns io_pgtable_arm_32_lpae_s2_init_fns = { -+ .alloc = arm_32_lpae_alloc_pgtable_s2, -+ .free = arm_lpae_free_pgtable, -+}; -+ -+#ifdef CONFIG_IOMMU_IO_PGTABLE_LPAE_SELFTEST -+ -+static struct io_pgtable_cfg *cfg_cookie; -+ -+static void dummy_tlb_flush_all(void *cookie) -+{ -+ WARN_ON(cookie != cfg_cookie); -+} -+ -+static void dummy_tlb_add_flush(unsigned long iova, size_t size, bool leaf, -+ void *cookie) -+{ -+ WARN_ON(cookie != cfg_cookie); -+ WARN_ON(!(size & cfg_cookie->pgsize_bitmap)); -+} -+ -+static void dummy_tlb_sync(void *cookie) -+{ -+ WARN_ON(cookie != cfg_cookie); -+} -+ -+static void dummy_flush_pgtable(void *ptr, size_t size, void *cookie) -+{ -+ WARN_ON(cookie != cfg_cookie); -+} -+ -+static struct iommu_gather_ops dummy_tlb_ops __initdata = { -+ .tlb_flush_all = dummy_tlb_flush_all, -+ .tlb_add_flush = dummy_tlb_add_flush, -+ .tlb_sync = dummy_tlb_sync, -+ .flush_pgtable = dummy_flush_pgtable, -+}; -+ -+static void __init arm_lpae_dump_ops(struct io_pgtable_ops *ops) -+{ -+ struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops); -+ struct io_pgtable_cfg *cfg = &data->iop.cfg; -+ -+ pr_err("cfg: pgsize_bitmap 0x%lx, ias %u-bit\n", -+ cfg->pgsize_bitmap, cfg->ias); -+ pr_err("data: %d levels, 0x%zx pgd_size, %lu pg_shift, %lu bits_per_level, pgd @ %p\n", -+ data->levels, data->pgd_size, data->pg_shift, -+ data->bits_per_level, data->pgd); -+} -+ -+#define __FAIL(ops, i) ({ \ -+ WARN(1, "selftest: test failed for fmt idx %d\n", (i)); \ -+ arm_lpae_dump_ops(ops); \ -+ selftest_running = false; \ -+ -EFAULT; \ -+}) -+ -+static int __init arm_lpae_run_tests(struct io_pgtable_cfg *cfg) -+{ -+ static const enum io_pgtable_fmt fmts[] = { -+ ARM_64_LPAE_S1, -+ ARM_64_LPAE_S2, -+ }; -+ -+ int i, j; -+ unsigned long iova; -+ size_t size; -+ struct io_pgtable_ops *ops; -+ -+ selftest_running = true; -+ -+ for (i = 0; i < ARRAY_SIZE(fmts); ++i) { -+ cfg_cookie = cfg; -+ ops = alloc_io_pgtable_ops(fmts[i], cfg, cfg); -+ if (!ops) { -+ pr_err("selftest: failed to allocate io pgtable ops\n"); -+ return -ENOMEM; -+ } -+ -+ /* -+ * Initial sanity checks. -+ * Empty page tables shouldn't provide any translations. -+ */ -+ if (ops->iova_to_phys(ops, 42)) -+ return __FAIL(ops, i); -+ -+ if (ops->iova_to_phys(ops, SZ_1G + 42)) -+ return __FAIL(ops, i); -+ -+ if (ops->iova_to_phys(ops, SZ_2G + 42)) -+ return __FAIL(ops, i); -+ -+ /* -+ * Distinct mappings of different granule sizes. -+ */ -+ iova = 0; -+ j = find_first_bit(&cfg->pgsize_bitmap, BITS_PER_LONG); -+ while (j != BITS_PER_LONG) { -+ size = 1UL << j; -+ -+ if (ops->map(ops, iova, iova, size, IOMMU_READ | -+ IOMMU_WRITE | -+ IOMMU_NOEXEC | -+ IOMMU_CACHE)) -+ return __FAIL(ops, i); -+ -+ /* Overlapping mappings */ -+ if (!ops->map(ops, iova, iova + size, size, -+ IOMMU_READ | IOMMU_NOEXEC)) -+ return __FAIL(ops, i); -+ -+ if (ops->iova_to_phys(ops, iova + 42) != (iova + 42)) -+ return __FAIL(ops, i); -+ -+ iova += SZ_1G; -+ j++; -+ j = find_next_bit(&cfg->pgsize_bitmap, BITS_PER_LONG, j); -+ } -+ -+ /* Partial unmap */ -+ size = 1UL << __ffs(cfg->pgsize_bitmap); -+ if (ops->unmap(ops, SZ_1G + size, size) != size) -+ return __FAIL(ops, i); -+ -+ /* Remap of partial unmap */ -+ if (ops->map(ops, SZ_1G + size, size, size, IOMMU_READ)) -+ return __FAIL(ops, i); -+ -+ if (ops->iova_to_phys(ops, SZ_1G + size + 42) != (size + 42)) -+ return __FAIL(ops, i); -+ -+ /* Full unmap */ -+ iova = 0; -+ j = find_first_bit(&cfg->pgsize_bitmap, BITS_PER_LONG); -+ while (j != BITS_PER_LONG) { -+ size = 1UL << j; -+ -+ if (ops->unmap(ops, iova, size) != size) -+ return __FAIL(ops, i); -+ -+ if (ops->iova_to_phys(ops, iova + 42)) -+ return __FAIL(ops, i); -+ -+ /* Remap full block */ -+ if (ops->map(ops, iova, iova, size, IOMMU_WRITE)) -+ return __FAIL(ops, i); -+ -+ if (ops->iova_to_phys(ops, iova + 42) != (iova + 42)) -+ return __FAIL(ops, i); -+ -+ iova += SZ_1G; -+ j++; -+ j = find_next_bit(&cfg->pgsize_bitmap, BITS_PER_LONG, j); -+ } -+ -+ free_io_pgtable_ops(ops); -+ } -+ -+ selftest_running = false; -+ return 0; -+} -+ -+static int __init arm_lpae_do_selftests(void) -+{ -+ static const unsigned long pgsize[] = { -+ SZ_4K | SZ_2M | SZ_1G, -+ SZ_16K | SZ_32M, -+ SZ_64K | SZ_512M, -+ }; -+ -+ static const unsigned int ias[] = { -+ 32, 36, 40, 42, 44, 48, -+ }; -+ -+ int i, j, pass = 0, fail = 0; -+ struct io_pgtable_cfg cfg = { -+ .tlb = &dummy_tlb_ops, -+ .oas = 48, -+ }; -+ -+ for (i = 0; i < ARRAY_SIZE(pgsize); ++i) { -+ for (j = 0; j < ARRAY_SIZE(ias); ++j) { -+ cfg.pgsize_bitmap = pgsize[i]; -+ cfg.ias = ias[j]; -+ pr_info("selftest: pgsize_bitmap 0x%08lx, IAS %u\n", -+ pgsize[i], ias[j]); -+ if (arm_lpae_run_tests(&cfg)) -+ fail++; -+ else -+ pass++; -+ } -+ } -+ -+ pr_info("selftest: completed with %d PASS %d FAIL\n", pass, fail); -+ return fail ? -EFAULT : 0; -+} -+subsys_initcall(arm_lpae_do_selftests); -+#endif -diff --git a/drivers/iommu/io-pgtable.c b/drivers/iommu/io-pgtable.c -new file mode 100644 -index 0000000..6436fe2 ---- /dev/null -+++ b/drivers/iommu/io-pgtable.c -@@ -0,0 +1,82 @@ -+/* -+ * Generic page table allocator for IOMMUs. -+ * -+ * 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. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ * -+ * Copyright (C) 2014 ARM Limited -+ * -+ * Author: Will Deacon -+ */ -+ -+#include -+#include -+#include -+ -+#include "io-pgtable.h" -+ -+extern struct io_pgtable_init_fns io_pgtable_arm_32_lpae_s1_init_fns; -+extern struct io_pgtable_init_fns io_pgtable_arm_32_lpae_s2_init_fns; -+extern struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns; -+extern struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s2_init_fns; -+ -+static const struct io_pgtable_init_fns * -+io_pgtable_init_table[IO_PGTABLE_NUM_FMTS] = -+{ -+#ifdef CONFIG_IOMMU_IO_PGTABLE_LPAE -+ [ARM_32_LPAE_S1] = &io_pgtable_arm_32_lpae_s1_init_fns, -+ [ARM_32_LPAE_S2] = &io_pgtable_arm_32_lpae_s2_init_fns, -+ [ARM_64_LPAE_S1] = &io_pgtable_arm_64_lpae_s1_init_fns, -+ [ARM_64_LPAE_S2] = &io_pgtable_arm_64_lpae_s2_init_fns, -+#endif -+}; -+ -+struct io_pgtable_ops *alloc_io_pgtable_ops(enum io_pgtable_fmt fmt, -+ struct io_pgtable_cfg *cfg, -+ void *cookie) -+{ -+ struct io_pgtable *iop; -+ const struct io_pgtable_init_fns *fns; -+ -+ if (fmt >= IO_PGTABLE_NUM_FMTS) -+ return NULL; -+ -+ fns = io_pgtable_init_table[fmt]; -+ if (!fns) -+ return NULL; -+ -+ iop = fns->alloc(cfg, cookie); -+ if (!iop) -+ return NULL; -+ -+ iop->fmt = fmt; -+ iop->cookie = cookie; -+ iop->cfg = *cfg; -+ -+ return &iop->ops; -+} -+ -+/* -+ * It is the IOMMU driver's responsibility to ensure that the page table -+ * is no longer accessible to the walker by this point. -+ */ -+void free_io_pgtable_ops(struct io_pgtable_ops *ops) -+{ -+ struct io_pgtable *iop; -+ -+ if (!ops) -+ return; -+ -+ iop = container_of(ops, struct io_pgtable, ops); -+ iop->cfg.tlb->tlb_flush_all(iop->cookie); -+ io_pgtable_init_table[iop->fmt]->free(iop); -+} -diff --git a/drivers/iommu/io-pgtable.h b/drivers/iommu/io-pgtable.h -new file mode 100644 -index 0000000..10e32f6 ---- /dev/null -+++ b/drivers/iommu/io-pgtable.h -@@ -0,0 +1,143 @@ -+#ifndef __IO_PGTABLE_H -+#define __IO_PGTABLE_H -+ -+/* -+ * Public API for use by IOMMU drivers -+ */ -+enum io_pgtable_fmt { -+ ARM_32_LPAE_S1, -+ ARM_32_LPAE_S2, -+ ARM_64_LPAE_S1, -+ ARM_64_LPAE_S2, -+ IO_PGTABLE_NUM_FMTS, -+}; -+ -+/** -+ * struct iommu_gather_ops - IOMMU callbacks for TLB and page table management. -+ * -+ * @tlb_flush_all: Synchronously invalidate the entire TLB context. -+ * @tlb_add_flush: Queue up a TLB invalidation for a virtual address range. -+ * @tlb_sync: Ensure any queue TLB invalidation has taken effect. -+ * @flush_pgtable: Ensure page table updates are visible to the IOMMU. -+ * -+ * Note that these can all be called in atomic context and must therefore -+ * not block. -+ */ -+struct iommu_gather_ops { -+ void (*tlb_flush_all)(void *cookie); -+ void (*tlb_add_flush)(unsigned long iova, size_t size, bool leaf, -+ void *cookie); -+ void (*tlb_sync)(void *cookie); -+ void (*flush_pgtable)(void *ptr, size_t size, void *cookie); -+}; -+ -+/** -+ * struct io_pgtable_cfg - Configuration data for a set of page tables. -+ * -+ * @quirks: A bitmap of hardware quirks that require some special -+ * action by the low-level page table allocator. -+ * @pgsize_bitmap: A bitmap of page sizes supported by this set of page -+ * tables. -+ * @ias: Input address (iova) size, in bits. -+ * @oas: Output address (paddr) size, in bits. -+ * @tlb: TLB management callbacks for this set of tables. -+ */ -+struct io_pgtable_cfg { -+ #define IO_PGTABLE_QUIRK_ARM_NS (1 << 0) /* Set NS bit in PTEs */ -+ int quirks; -+ unsigned long pgsize_bitmap; -+ unsigned int ias; -+ unsigned int oas; -+ const struct iommu_gather_ops *tlb; -+ -+ /* Low-level data specific to the table format */ -+ union { -+ struct { -+ u64 ttbr[2]; -+ u64 tcr; -+ u64 mair[2]; -+ } arm_lpae_s1_cfg; -+ -+ struct { -+ u64 vttbr; -+ u64 vtcr; -+ } arm_lpae_s2_cfg; -+ }; -+}; -+ -+/** -+ * struct io_pgtable_ops - Page table manipulation API for IOMMU drivers. -+ * -+ * @map: Map a physically contiguous memory region. -+ * @unmap: Unmap a physically contiguous memory region. -+ * @iova_to_phys: Translate iova to physical address. -+ * -+ * These functions map directly onto the iommu_ops member functions with -+ * the same names. -+ */ -+struct io_pgtable_ops { -+ int (*map)(struct io_pgtable_ops *ops, unsigned long iova, -+ phys_addr_t paddr, size_t size, int prot); -+ int (*unmap)(struct io_pgtable_ops *ops, unsigned long iova, -+ size_t size); -+ phys_addr_t (*iova_to_phys)(struct io_pgtable_ops *ops, -+ unsigned long iova); -+}; -+ -+/** -+ * alloc_io_pgtable_ops() - Allocate a page table allocator for use by an IOMMU. -+ * -+ * @fmt: The page table format. -+ * @cfg: The page table configuration. This will be modified to represent -+ * the configuration actually provided by the allocator (e.g. the -+ * pgsize_bitmap may be restricted). -+ * @cookie: An opaque token provided by the IOMMU driver and passed back to -+ * the callback routines in cfg->tlb. -+ */ -+struct io_pgtable_ops *alloc_io_pgtable_ops(enum io_pgtable_fmt fmt, -+ struct io_pgtable_cfg *cfg, -+ void *cookie); -+ -+/** -+ * free_io_pgtable_ops() - Free an io_pgtable_ops structure. The caller -+ * *must* ensure that the page table is no longer -+ * live, but the TLB can be dirty. -+ * -+ * @ops: The ops returned from alloc_io_pgtable_ops. -+ */ -+void free_io_pgtable_ops(struct io_pgtable_ops *ops); -+ -+ -+/* -+ * Internal structures for page table allocator implementations. -+ */ -+ -+/** -+ * struct io_pgtable - Internal structure describing a set of page tables. -+ * -+ * @fmt: The page table format. -+ * @cookie: An opaque token provided by the IOMMU driver and passed back to -+ * any callback routines. -+ * @cfg: A copy of the page table configuration. -+ * @ops: The page table operations in use for this set of page tables. -+ */ -+struct io_pgtable { -+ enum io_pgtable_fmt fmt; -+ void *cookie; -+ struct io_pgtable_cfg cfg; -+ struct io_pgtable_ops ops; -+}; -+ -+/** -+ * struct io_pgtable_init_fns - Alloc/free a set of page tables for a -+ * particular format. -+ * -+ * @alloc: Allocate a set of page tables described by cfg. -+ * @free: Free the page tables associated with iop. -+ */ -+struct io_pgtable_init_fns { -+ struct io_pgtable *(*alloc)(struct io_pgtable_cfg *cfg, void *cookie); -+ void (*free)(struct io_pgtable *iop); -+}; -+ -+#endif /* __IO_PGTABLE_H */ -diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c -index ed8b048..8d8e5a7 100644 ---- a/drivers/iommu/iommu.c -+++ b/drivers/iommu/iommu.c -@@ -591,10 +591,10 @@ static struct iommu_group *get_pci_alias_group(struct pci_dev *pdev, - continue; - - /* We alias them or they alias us */ -- if (((pdev->dev_flags & PCI_DEV_FLAGS_DMA_ALIAS_DEVFN) && -- pdev->dma_alias_devfn == tmp->devfn) || -- ((tmp->dev_flags & PCI_DEV_FLAGS_DMA_ALIAS_DEVFN) && -- tmp->dma_alias_devfn == pdev->devfn)) { -+ if (((pdev->dev_flags & PCI_DEV_FLAGS_DMA_ALIAS_DEVID) && -+ (pdev->dma_alias_devid & 0xff) == tmp->devfn) || -+ ((tmp->dev_flags & PCI_DEV_FLAGS_DMA_ALIAS_DEVID) && -+ (tmp->dma_alias_devid & 0xff) == pdev->devfn)) { - - group = get_pci_alias_group(tmp, devfns); - if (group) { -@@ -737,7 +737,7 @@ static int add_iommu_group(struct device *dev, void *data) - const struct iommu_ops *ops = cb->ops; - - if (!ops->add_device) -- return -ENODEV; -+ return 0; - - WARN_ON(dev->iommu_group); - -@@ -818,7 +818,15 @@ static int iommu_bus_init(struct bus_type *bus, const struct iommu_ops *ops) - kfree(nb); - return err; - } -- return bus_for_each_dev(bus, NULL, &cb, add_iommu_group); -+ -+ err = bus_for_each_dev(bus, NULL, &cb, add_iommu_group); -+ if (err) { -+ bus_unregister_notifier(bus, nb); -+ kfree(nb); -+ return err; -+ } -+ -+ return 0; - } - - /** -@@ -836,13 +844,19 @@ static int iommu_bus_init(struct bus_type *bus, const struct iommu_ops *ops) - */ - int bus_set_iommu(struct bus_type *bus, const struct iommu_ops *ops) - { -+ int err; -+ - if (bus->iommu_ops != NULL) - return -EBUSY; - - bus->iommu_ops = ops; - - /* Do IOMMU specific setup for this bus-type */ -- return iommu_bus_init(bus, ops); -+ err = iommu_bus_init(bus, ops); -+ if (err) -+ bus->iommu_ops = NULL; -+ -+ return err; - } - EXPORT_SYMBOL_GPL(bus_set_iommu); - -@@ -887,36 +901,24 @@ EXPORT_SYMBOL_GPL(iommu_set_fault_handler); - struct iommu_domain *iommu_domain_alloc(struct bus_type *bus) - { - struct iommu_domain *domain; -- int ret; - - if (bus == NULL || bus->iommu_ops == NULL) - return NULL; - -- domain = kzalloc(sizeof(*domain), GFP_KERNEL); -+ domain = bus->iommu_ops->domain_alloc(IOMMU_DOMAIN_UNMANAGED); - if (!domain) - return NULL; - -- domain->ops = bus->iommu_ops; -- -- ret = domain->ops->domain_init(domain); -- if (ret) -- goto out_free; -+ domain->ops = bus->iommu_ops; -+ domain->type = IOMMU_DOMAIN_UNMANAGED; - - return domain; -- --out_free: -- kfree(domain); -- -- return NULL; - } - EXPORT_SYMBOL_GPL(iommu_domain_alloc); - - void iommu_domain_free(struct iommu_domain *domain) - { -- if (likely(domain->ops->domain_destroy != NULL)) -- domain->ops->domain_destroy(domain); -- -- kfree(domain); -+ domain->ops->domain_free(domain); - } - EXPORT_SYMBOL_GPL(iommu_domain_free); - -@@ -943,6 +945,16 @@ void iommu_detach_device(struct iommu_domain *domain, struct device *dev) - } - EXPORT_SYMBOL_GPL(iommu_detach_device); - -+struct iommu_domain *iommu_get_dev_domain(struct device *dev) -+{ -+ const struct iommu_ops *ops = dev->bus->iommu_ops; -+ -+ if (unlikely(ops == NULL || ops->get_dev_iommu_domain == NULL)) -+ return NULL; -+ -+ return ops->get_dev_iommu_domain(dev); -+} -+EXPORT_SYMBOL_GPL(iommu_get_dev_domain); - /* - * IOMMU groups are really the natrual working unit of the IOMMU, but - * the IOMMU API works on domains and devices. Bridge that gap by -@@ -1035,6 +1047,9 @@ int iommu_map(struct iommu_domain *domain, unsigned long iova, - domain->ops->pgsize_bitmap == 0UL)) - return -ENODEV; - -+ if (unlikely(!(domain->type & __IOMMU_DOMAIN_PAGING))) -+ return -EINVAL; -+ - /* find out the minimum page size supported */ - min_pagesz = 1 << __ffs(domain->ops->pgsize_bitmap); - -@@ -1070,7 +1085,7 @@ int iommu_map(struct iommu_domain *domain, unsigned long iova, - if (ret) - iommu_unmap(domain, orig_iova, orig_size - size); - else -- trace_map(iova, paddr, size); -+ trace_map(orig_iova, paddr, orig_size); - - return ret; - } -@@ -1080,11 +1095,15 @@ size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size) - { - size_t unmapped_page, unmapped = 0; - unsigned int min_pagesz; -+ unsigned long orig_iova = iova; - - if (unlikely(domain->ops->unmap == NULL || - domain->ops->pgsize_bitmap == 0UL)) - return -ENODEV; - -+ if (unlikely(!(domain->type & __IOMMU_DOMAIN_PAGING))) -+ return -EINVAL; -+ - /* find out the minimum page size supported */ - min_pagesz = 1 << __ffs(domain->ops->pgsize_bitmap); - -@@ -1119,11 +1138,53 @@ size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size) - unmapped += unmapped_page; - } - -- trace_unmap(iova, 0, size); -+ trace_unmap(orig_iova, size, unmapped); - return unmapped; - } - EXPORT_SYMBOL_GPL(iommu_unmap); - -+size_t default_iommu_map_sg(struct iommu_domain *domain, unsigned long iova, -+ struct scatterlist *sg, unsigned int nents, int prot) -+{ -+ struct scatterlist *s; -+ size_t mapped = 0; -+ unsigned int i, min_pagesz; -+ int ret; -+ -+ if (unlikely(domain->ops->pgsize_bitmap == 0UL)) -+ return 0; -+ -+ min_pagesz = 1 << __ffs(domain->ops->pgsize_bitmap); -+ -+ for_each_sg(sg, s, nents, i) { -+ phys_addr_t phys = page_to_phys(sg_page(s)) + s->offset; -+ -+ /* -+ * We are mapping on IOMMU page boundaries, so offset within -+ * the page must be 0. However, the IOMMU may support pages -+ * smaller than PAGE_SIZE, so s->offset may still represent -+ * an offset of that boundary within the CPU page. -+ */ -+ if (!IS_ALIGNED(s->offset, min_pagesz)) -+ goto out_err; -+ -+ ret = iommu_map(domain, iova + mapped, phys, s->length, prot); -+ if (ret) -+ goto out_err; -+ -+ mapped += s->length; -+ } -+ -+ return mapped; -+ -+out_err: -+ /* undo mappings already done */ -+ iommu_unmap(domain, iova, mapped); -+ -+ return 0; -+ -+} -+EXPORT_SYMBOL_GPL(default_iommu_map_sg); - - int iommu_domain_window_enable(struct iommu_domain *domain, u32 wnd_nr, - phys_addr_t paddr, u64 size, int prot) -diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c -index 7dab5cb..f3c5ab6 100644 ---- a/drivers/iommu/ipmmu-vmsa.c -+++ b/drivers/iommu/ipmmu-vmsa.c -@@ -1127,6 +1127,7 @@ static const struct iommu_ops ipmmu_ops = { - .detach_dev = ipmmu_detach_device, - .map = ipmmu_map, - .unmap = ipmmu_unmap, -+ .map_sg = default_iommu_map_sg, - .iova_to_phys = ipmmu_iova_to_phys, - .add_device = ipmmu_add_device, - .remove_device = ipmmu_remove_device, -@@ -1221,7 +1222,6 @@ static int ipmmu_remove(struct platform_device *pdev) - - static struct platform_driver ipmmu_driver = { - .driver = { -- .owner = THIS_MODULE, - .name = "ipmmu-vmsa", - }, - .probe = ipmmu_probe, -diff --git a/drivers/iommu/irq_remapping.c b/drivers/iommu/irq_remapping.c -index 74a1767..2c3f5ad 100644 ---- a/drivers/iommu/irq_remapping.c -+++ b/drivers/iommu/irq_remapping.c -@@ -56,19 +56,13 @@ static int do_setup_msi_irqs(struct pci_dev *dev, int nvec) - unsigned int irq; - struct msi_desc *msidesc; - -- WARN_ON(!list_is_singular(&dev->msi_list)); - msidesc = list_entry(dev->msi_list.next, struct msi_desc, list); -- WARN_ON(msidesc->irq); -- WARN_ON(msidesc->msi_attrib.multiple); -- WARN_ON(msidesc->nvec_used); - - irq = irq_alloc_hwirqs(nvec, dev_to_node(&dev->dev)); - if (irq == 0) - return -ENOSPC; - - nvec_pow2 = __roundup_pow_of_two(nvec); -- msidesc->nvec_used = nvec; -- msidesc->msi_attrib.multiple = ilog2(nvec_pow2); - for (sub_handle = 0; sub_handle < nvec; sub_handle++) { - if (!sub_handle) { - index = msi_alloc_remapped_irq(dev, irq, nvec_pow2); -@@ -96,8 +90,6 @@ error: - * IRQs from tearing down again in default_teardown_msi_irqs() - */ - msidesc->irq = 0; -- msidesc->nvec_used = 0; -- msidesc->msi_attrib.multiple = 0; - - return ret; - } -diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c -index 6e3dcc2..1c7b78e 100644 ---- a/drivers/iommu/msm_iommu.c -+++ b/drivers/iommu/msm_iommu.c -@@ -681,6 +681,7 @@ static const struct iommu_ops msm_iommu_ops = { - .detach_dev = msm_iommu_detach_dev, - .map = msm_iommu_map, - .unmap = msm_iommu_unmap, -+ .map_sg = default_iommu_map_sg, - .iova_to_phys = msm_iommu_iova_to_phys, - .pgsize_bitmap = MSM_IOMMU_PGSIZES, - }; -diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c -index e550ccb..43429ab 100644 ---- a/drivers/iommu/of_iommu.c -+++ b/drivers/iommu/of_iommu.c -@@ -18,9 +18,14 @@ - */ - - #include -+#include - #include - #include - #include -+#include -+ -+static const struct of_device_id __iommu_of_table_sentinel -+ __used __section(__iommu_of_table_end); - - /** - * of_get_dma_window - Parse *dma-window property and returns 0 if found. -@@ -89,3 +94,93 @@ int of_get_dma_window(struct device_node *dn, const char *prefix, int index, - return 0; - } - EXPORT_SYMBOL_GPL(of_get_dma_window); -+ -+struct of_iommu_node { -+ struct list_head list; -+ struct device_node *np; -+ struct iommu_ops *ops; -+}; -+static LIST_HEAD(of_iommu_list); -+static DEFINE_SPINLOCK(of_iommu_lock); -+ -+void of_iommu_set_ops(struct device_node *np, struct iommu_ops *ops) -+{ -+ struct of_iommu_node *iommu = kzalloc(sizeof(*iommu), GFP_KERNEL); -+ -+ if (WARN_ON(!iommu)) -+ return; -+ -+ INIT_LIST_HEAD(&iommu->list); -+ iommu->np = np; -+ iommu->ops = ops; -+ spin_lock(&of_iommu_lock); -+ list_add_tail(&iommu->list, &of_iommu_list); -+ spin_unlock(&of_iommu_lock); -+} -+ -+struct iommu_ops *of_iommu_get_ops(struct device_node *np) -+{ -+ struct of_iommu_node *node; -+ struct iommu_ops *ops = NULL; -+ -+ spin_lock(&of_iommu_lock); -+ list_for_each_entry(node, &of_iommu_list, list) -+ if (node->np == np) { -+ ops = node->ops; -+ break; -+ } -+ spin_unlock(&of_iommu_lock); -+ return ops; -+} -+ -+struct iommu_ops *of_iommu_configure(struct device *dev, -+ struct device_node *master_np) -+{ -+ struct of_phandle_args iommu_spec; -+ struct device_node *np; -+ struct iommu_ops *ops = NULL; -+ int idx = 0; -+ -+ if (dev_is_pci(dev)) { -+ dev_err(dev, "IOMMU is currently not supported for PCI\n"); -+ return NULL; -+ } -+ -+ /* -+ * We don't currently walk up the tree looking for a parent IOMMU. -+ * See the `Notes:' section of -+ * Documentation/devicetree/bindings/iommu/iommu.txt -+ */ -+ while (!of_parse_phandle_with_args(master_np, "iommus", -+ "#iommu-cells", idx, -+ &iommu_spec)) { -+ np = iommu_spec.np; -+ ops = of_iommu_get_ops(np); -+ -+ if (!ops || !ops->of_xlate || ops->of_xlate(dev, &iommu_spec)) -+ goto err_put_node; -+ -+ of_node_put(np); -+ idx++; -+ } -+ -+ return ops; -+ -+err_put_node: -+ of_node_put(np); -+ return NULL; -+} -+ -+void __init of_iommu_init(void) -+{ -+ struct device_node *np; -+ const struct of_device_id *match, *matches = &__iommu_of_table; -+ -+ for_each_matching_node_and_match(np, matches, &match) { -+ const of_iommu_init_fn init_fn = match->data; -+ -+ if (init_fn(np)) -+ pr_err("Failed to initialise IOMMU %s\n", -+ of_node_full_name(np)); -+ } -+} -diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c -index 3627887..18003c0 100644 ---- a/drivers/iommu/omap-iommu.c -+++ b/drivers/iommu/omap-iommu.c -@@ -1288,6 +1288,7 @@ static const struct iommu_ops omap_iommu_ops = { - .detach_dev = omap_iommu_detach_dev, - .map = omap_iommu_map, - .unmap = omap_iommu_unmap, -+ .map_sg = default_iommu_map_sg, - .iova_to_phys = omap_iommu_iova_to_phys, - .add_device = omap_iommu_add_device, - .remove_device = omap_iommu_remove_device, -diff --git a/drivers/iommu/shmobile-iommu.c b/drivers/iommu/shmobile-iommu.c -index 1333e6f..f1b0077 100644 ---- a/drivers/iommu/shmobile-iommu.c -+++ b/drivers/iommu/shmobile-iommu.c -@@ -361,6 +361,7 @@ static const struct iommu_ops shmobile_iommu_ops = { - .detach_dev = shmobile_iommu_detach_device, - .map = shmobile_iommu_map, - .unmap = shmobile_iommu_unmap, -+ .map_sg = default_iommu_map_sg, - .iova_to_phys = shmobile_iommu_iova_to_phys, - .add_device = shmobile_iommu_add_device, - .pgsize_bitmap = SZ_1M | SZ_64K | SZ_4K, -diff --git a/drivers/iommu/shmobile-ipmmu.c b/drivers/iommu/shmobile-ipmmu.c -index bd97ade..951651a 100644 ---- a/drivers/iommu/shmobile-ipmmu.c -+++ b/drivers/iommu/shmobile-ipmmu.c -@@ -118,7 +118,6 @@ static int ipmmu_probe(struct platform_device *pdev) - static struct platform_driver ipmmu_driver = { - .probe = ipmmu_probe, - .driver = { -- .owner = THIS_MODULE, - .name = "ipmmu", - }, - }; -diff --git a/drivers/iommu/tegra-gart.c b/drivers/iommu/tegra-gart.c -index a6d76ab..f722a0c 100644 ---- a/drivers/iommu/tegra-gart.c -+++ b/drivers/iommu/tegra-gart.c -@@ -425,7 +425,6 @@ static struct platform_driver tegra_gart_driver = { - .probe = tegra_gart_probe, - .remove = tegra_gart_remove, - .driver = { -- .owner = THIS_MODULE, - .name = "tegra-gart", - .pm = &tegra_gart_pm_ops, - .of_match_table = tegra_gart_of_match, -diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c -index 3afdf43..cb0c9bf 100644 ---- a/drivers/iommu/tegra-smmu.c -+++ b/drivers/iommu/tegra-smmu.c -@@ -955,6 +955,7 @@ static const struct iommu_ops smmu_iommu_ops = { - .detach_dev = smmu_iommu_detach_dev, - .map = smmu_iommu_map, - .unmap = smmu_iommu_unmap, -+ .map_sg = default_iommu_map_sg, - .iova_to_phys = smmu_iommu_iova_to_phys, - .pgsize_bitmap = SMMU_IOMMU_PGSIZES, - }; -@@ -1269,7 +1270,6 @@ static struct platform_driver tegra_smmu_driver = { - .probe = tegra_smmu_probe, - .remove = tegra_smmu_remove, - .driver = { -- .owner = THIS_MODULE, - .name = "tegra-smmu", - .pm = &tegra_smmu_pm_ops, - .of_match_table = tegra_smmu_of_match, -diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig -index b21f12f..e72e239 100644 ---- a/drivers/irqchip/Kconfig -+++ b/drivers/irqchip/Kconfig -@@ -5,8 +5,15 @@ config IRQCHIP - config ARM_GIC - bool - select IRQ_DOMAIN -+ select IRQ_DOMAIN_HIERARCHY - select MULTI_IRQ_HANDLER - -+config ARM_GIC_V2M -+ bool -+ depends on ARM_GIC -+ depends on PCI && PCI_MSI -+ select PCI_MSI_IRQ_DOMAIN -+ - config GIC_NON_BANKED - bool - -@@ -14,6 +21,11 @@ config ARM_GIC_V3 - bool - select IRQ_DOMAIN - select MULTI_IRQ_HANDLER -+ select IRQ_DOMAIN_HIERARCHY -+ -+config ARM_GIC_V3_ITS -+ bool -+ select PCI_MSI_IRQ_DOMAIN - - config ARM_NVIC - bool -diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile -index 173bb5f..1c4f9a4 100644 ---- a/drivers/irqchip/Makefile -+++ b/drivers/irqchip/Makefile -@@ -19,7 +19,9 @@ obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o - obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o - obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o - obj-$(CONFIG_ARM_GIC) += irq-gic.o irq-gic-common.o -+obj-$(CONFIG_ARM_GIC_V2M) += irq-gic-v2m.o - obj-$(CONFIG_ARM_GIC_V3) += irq-gic-v3.o irq-gic-common.o -+obj-$(CONFIG_ARM_GIC_V3_ITS) += irq-gic-v3-its.o - obj-$(CONFIG_ARM_NVIC) += irq-nvic.o - obj-$(CONFIG_ARM_VIC) += irq-vic.o - obj-$(CONFIG_ATMEL_AIC_IRQ) += irq-atmel-aic-common.o irq-atmel-aic.o -diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c -index 41ac85a..615075d 100644 ---- a/drivers/irqchip/irq-armada-370-xp.c -+++ b/drivers/irqchip/irq-armada-370-xp.c -@@ -131,7 +131,7 @@ static void armada_370_xp_free_msi(int hwirq) - mutex_unlock(&msi_used_lock); - } - --static int armada_370_xp_setup_msi_irq(struct msi_chip *chip, -+static int armada_370_xp_setup_msi_irq(struct msi_controller *chip, - struct pci_dev *pdev, - struct msi_desc *desc) - { -@@ -158,11 +158,11 @@ static int armada_370_xp_setup_msi_irq(struct msi_chip *chip, - msg.address_hi = 0; - msg.data = 0xf00 | (hwirq + 16); - -- write_msi_msg(virq, &msg); -+ pci_write_msi_msg(virq, &msg); - return 0; - } - --static void armada_370_xp_teardown_msi_irq(struct msi_chip *chip, -+static void armada_370_xp_teardown_msi_irq(struct msi_controller *chip, - unsigned int irq) - { - struct irq_data *d = irq_get_irq_data(irq); -@@ -174,10 +174,10 @@ static void armada_370_xp_teardown_msi_irq(struct msi_chip *chip, - - static struct irq_chip armada_370_xp_msi_irq_chip = { - .name = "armada_370_xp_msi_irq", -- .irq_enable = unmask_msi_irq, -- .irq_disable = mask_msi_irq, -- .irq_mask = mask_msi_irq, -- .irq_unmask = unmask_msi_irq, -+ .irq_enable = pci_msi_unmask_irq, -+ .irq_disable = pci_msi_mask_irq, -+ .irq_mask = pci_msi_mask_irq, -+ .irq_unmask = pci_msi_unmask_irq, - }; - - static int armada_370_xp_msi_map(struct irq_domain *domain, unsigned int virq, -@@ -197,7 +197,7 @@ static const struct irq_domain_ops armada_370_xp_msi_irq_ops = { - static int armada_370_xp_msi_init(struct device_node *node, - phys_addr_t main_int_phys_base) - { -- struct msi_chip *msi_chip; -+ struct msi_controller *msi_chip; - u32 reg; - int ret; - -diff --git a/drivers/irqchip/irq-atmel-aic.c b/drivers/irqchip/irq-atmel-aic.c -index 9a2cf3c..27fdd8c 100644 ---- a/drivers/irqchip/irq-atmel-aic.c -+++ b/drivers/irqchip/irq-atmel-aic.c -@@ -65,11 +65,11 @@ aic_handle(struct pt_regs *regs) - u32 irqnr; - u32 irqstat; - -- irqnr = irq_reg_readl(gc->reg_base + AT91_AIC_IVR); -- irqstat = irq_reg_readl(gc->reg_base + AT91_AIC_ISR); -+ irqnr = irq_reg_readl(gc, AT91_AIC_IVR); -+ irqstat = irq_reg_readl(gc, AT91_AIC_ISR); - - if (!irqstat) -- irq_reg_writel(0, gc->reg_base + AT91_AIC_EOICR); -+ irq_reg_writel(gc, 0, AT91_AIC_EOICR); - else - handle_domain_irq(aic_domain, irqnr, regs); - } -@@ -80,7 +80,7 @@ static int aic_retrigger(struct irq_data *d) - - /* Enable interrupt on AIC5 */ - irq_gc_lock(gc); -- irq_reg_writel(d->mask, gc->reg_base + AT91_AIC_ISCR); -+ irq_reg_writel(gc, d->mask, AT91_AIC_ISCR); - irq_gc_unlock(gc); - - return 0; -@@ -92,12 +92,12 @@ static int aic_set_type(struct irq_data *d, unsigned type) - unsigned int smr; - int ret; - -- smr = irq_reg_readl(gc->reg_base + AT91_AIC_SMR(d->hwirq)); -+ smr = irq_reg_readl(gc, AT91_AIC_SMR(d->hwirq)); - ret = aic_common_set_type(d, type, &smr); - if (ret) - return ret; - -- irq_reg_writel(smr, gc->reg_base + AT91_AIC_SMR(d->hwirq)); -+ irq_reg_writel(gc, smr, AT91_AIC_SMR(d->hwirq)); - - return 0; - } -@@ -108,8 +108,8 @@ static void aic_suspend(struct irq_data *d) - struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); - - irq_gc_lock(gc); -- irq_reg_writel(gc->mask_cache, gc->reg_base + AT91_AIC_IDCR); -- irq_reg_writel(gc->wake_active, gc->reg_base + AT91_AIC_IECR); -+ irq_reg_writel(gc, gc->mask_cache, AT91_AIC_IDCR); -+ irq_reg_writel(gc, gc->wake_active, AT91_AIC_IECR); - irq_gc_unlock(gc); - } - -@@ -118,8 +118,8 @@ static void aic_resume(struct irq_data *d) - struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); - - irq_gc_lock(gc); -- irq_reg_writel(gc->wake_active, gc->reg_base + AT91_AIC_IDCR); -- irq_reg_writel(gc->mask_cache, gc->reg_base + AT91_AIC_IECR); -+ irq_reg_writel(gc, gc->wake_active, AT91_AIC_IDCR); -+ irq_reg_writel(gc, gc->mask_cache, AT91_AIC_IECR); - irq_gc_unlock(gc); - } - -@@ -128,8 +128,8 @@ static void aic_pm_shutdown(struct irq_data *d) - struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); - - irq_gc_lock(gc); -- irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_IDCR); -- irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_ICCR); -+ irq_reg_writel(gc, 0xffffffff, AT91_AIC_IDCR); -+ irq_reg_writel(gc, 0xffffffff, AT91_AIC_ICCR); - irq_gc_unlock(gc); - } - #else -@@ -148,24 +148,24 @@ static void __init aic_hw_init(struct irq_domain *domain) - * will not Lock out nIRQ - */ - for (i = 0; i < 8; i++) -- irq_reg_writel(0, gc->reg_base + AT91_AIC_EOICR); -+ irq_reg_writel(gc, 0, AT91_AIC_EOICR); - - /* - * Spurious Interrupt ID in Spurious Vector Register. - * When there is no current interrupt, the IRQ Vector Register - * reads the value stored in AIC_SPU - */ -- irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_SPU); -+ irq_reg_writel(gc, 0xffffffff, AT91_AIC_SPU); - - /* No debugging in AIC: Debug (Protect) Control Register */ -- irq_reg_writel(0, gc->reg_base + AT91_AIC_DCR); -+ irq_reg_writel(gc, 0, AT91_AIC_DCR); - - /* Disable and clear all interrupts initially */ -- irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_IDCR); -- irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_ICCR); -+ irq_reg_writel(gc, 0xffffffff, AT91_AIC_IDCR); -+ irq_reg_writel(gc, 0xffffffff, AT91_AIC_ICCR); - - for (i = 0; i < 32; i++) -- irq_reg_writel(i, gc->reg_base + AT91_AIC_SVR(i)); -+ irq_reg_writel(gc, i, AT91_AIC_SVR(i)); - } - - static int aic_irq_domain_xlate(struct irq_domain *d, -@@ -195,10 +195,10 @@ static int aic_irq_domain_xlate(struct irq_domain *d, - gc = dgc->gc[idx]; - - irq_gc_lock(gc); -- smr = irq_reg_readl(gc->reg_base + AT91_AIC_SMR(*out_hwirq)); -+ smr = irq_reg_readl(gc, AT91_AIC_SMR(*out_hwirq)); - ret = aic_common_set_priority(intspec[2], &smr); - if (!ret) -- irq_reg_writel(smr, gc->reg_base + AT91_AIC_SMR(*out_hwirq)); -+ irq_reg_writel(gc, smr, AT91_AIC_SMR(*out_hwirq)); - irq_gc_unlock(gc); - - return ret; -diff --git a/drivers/irqchip/irq-atmel-aic5.c b/drivers/irqchip/irq-atmel-aic5.c -index a11aae8..a2e8c3f 100644 ---- a/drivers/irqchip/irq-atmel-aic5.c -+++ b/drivers/irqchip/irq-atmel-aic5.c -@@ -75,11 +75,11 @@ aic5_handle(struct pt_regs *regs) - u32 irqnr; - u32 irqstat; - -- irqnr = irq_reg_readl(gc->reg_base + AT91_AIC5_IVR); -- irqstat = irq_reg_readl(gc->reg_base + AT91_AIC5_ISR); -+ irqnr = irq_reg_readl(gc, AT91_AIC5_IVR); -+ irqstat = irq_reg_readl(gc, AT91_AIC5_ISR); - - if (!irqstat) -- irq_reg_writel(0, gc->reg_base + AT91_AIC5_EOICR); -+ irq_reg_writel(gc, 0, AT91_AIC5_EOICR); - else - handle_domain_irq(aic5_domain, irqnr, regs); - } -@@ -92,8 +92,8 @@ static void aic5_mask(struct irq_data *d) - - /* Disable interrupt on AIC5 */ - irq_gc_lock(gc); -- irq_reg_writel(d->hwirq, gc->reg_base + AT91_AIC5_SSR); -- irq_reg_writel(1, gc->reg_base + AT91_AIC5_IDCR); -+ irq_reg_writel(gc, d->hwirq, AT91_AIC5_SSR); -+ irq_reg_writel(gc, 1, AT91_AIC5_IDCR); - gc->mask_cache &= ~d->mask; - irq_gc_unlock(gc); - } -@@ -106,8 +106,8 @@ static void aic5_unmask(struct irq_data *d) - - /* Enable interrupt on AIC5 */ - irq_gc_lock(gc); -- irq_reg_writel(d->hwirq, gc->reg_base + AT91_AIC5_SSR); -- irq_reg_writel(1, gc->reg_base + AT91_AIC5_IECR); -+ irq_reg_writel(gc, d->hwirq, AT91_AIC5_SSR); -+ irq_reg_writel(gc, 1, AT91_AIC5_IECR); - gc->mask_cache |= d->mask; - irq_gc_unlock(gc); - } -@@ -120,8 +120,8 @@ static int aic5_retrigger(struct irq_data *d) - - /* Enable interrupt on AIC5 */ - irq_gc_lock(gc); -- irq_reg_writel(d->hwirq, gc->reg_base + AT91_AIC5_SSR); -- irq_reg_writel(1, gc->reg_base + AT91_AIC5_ISCR); -+ irq_reg_writel(gc, d->hwirq, AT91_AIC5_SSR); -+ irq_reg_writel(gc, 1, AT91_AIC5_ISCR); - irq_gc_unlock(gc); - - return 0; -@@ -136,11 +136,11 @@ static int aic5_set_type(struct irq_data *d, unsigned type) - int ret; - - irq_gc_lock(gc); -- irq_reg_writel(d->hwirq, gc->reg_base + AT91_AIC5_SSR); -- smr = irq_reg_readl(gc->reg_base + AT91_AIC5_SMR); -+ irq_reg_writel(gc, d->hwirq, AT91_AIC5_SSR); -+ smr = irq_reg_readl(gc, AT91_AIC5_SMR); - ret = aic_common_set_type(d, type, &smr); - if (!ret) -- irq_reg_writel(smr, gc->reg_base + AT91_AIC5_SMR); -+ irq_reg_writel(gc, smr, AT91_AIC5_SMR); - irq_gc_unlock(gc); - - return ret; -@@ -162,12 +162,11 @@ static void aic5_suspend(struct irq_data *d) - if ((mask & gc->mask_cache) == (mask & gc->wake_active)) - continue; - -- irq_reg_writel(i + gc->irq_base, -- bgc->reg_base + AT91_AIC5_SSR); -+ irq_reg_writel(bgc, i + gc->irq_base, AT91_AIC5_SSR); - if (mask & gc->wake_active) -- irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IECR); -+ irq_reg_writel(bgc, 1, AT91_AIC5_IECR); - else -- irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IDCR); -+ irq_reg_writel(bgc, 1, AT91_AIC5_IDCR); - } - irq_gc_unlock(bgc); - } -@@ -187,12 +186,11 @@ static void aic5_resume(struct irq_data *d) - if ((mask & gc->mask_cache) == (mask & gc->wake_active)) - continue; - -- irq_reg_writel(i + gc->irq_base, -- bgc->reg_base + AT91_AIC5_SSR); -+ irq_reg_writel(bgc, i + gc->irq_base, AT91_AIC5_SSR); - if (mask & gc->mask_cache) -- irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IECR); -+ irq_reg_writel(bgc, 1, AT91_AIC5_IECR); - else -- irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IDCR); -+ irq_reg_writel(bgc, 1, AT91_AIC5_IDCR); - } - irq_gc_unlock(bgc); - } -@@ -207,10 +205,9 @@ static void aic5_pm_shutdown(struct irq_data *d) - - irq_gc_lock(bgc); - for (i = 0; i < dgc->irqs_per_chip; i++) { -- irq_reg_writel(i + gc->irq_base, -- bgc->reg_base + AT91_AIC5_SSR); -- irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IDCR); -- irq_reg_writel(1, bgc->reg_base + AT91_AIC5_ICCR); -+ irq_reg_writel(bgc, i + gc->irq_base, AT91_AIC5_SSR); -+ irq_reg_writel(bgc, 1, AT91_AIC5_IDCR); -+ irq_reg_writel(bgc, 1, AT91_AIC5_ICCR); - } - irq_gc_unlock(bgc); - } -@@ -230,24 +227,24 @@ static void __init aic5_hw_init(struct irq_domain *domain) - * will not Lock out nIRQ - */ - for (i = 0; i < 8; i++) -- irq_reg_writel(0, gc->reg_base + AT91_AIC5_EOICR); -+ irq_reg_writel(gc, 0, AT91_AIC5_EOICR); - - /* - * Spurious Interrupt ID in Spurious Vector Register. - * When there is no current interrupt, the IRQ Vector Register - * reads the value stored in AIC_SPU - */ -- irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC5_SPU); -+ irq_reg_writel(gc, 0xffffffff, AT91_AIC5_SPU); - - /* No debugging in AIC: Debug (Protect) Control Register */ -- irq_reg_writel(0, gc->reg_base + AT91_AIC5_DCR); -+ irq_reg_writel(gc, 0, AT91_AIC5_DCR); - - /* Disable and clear all interrupts initially */ - for (i = 0; i < domain->revmap_size; i++) { -- irq_reg_writel(i, gc->reg_base + AT91_AIC5_SSR); -- irq_reg_writel(i, gc->reg_base + AT91_AIC5_SVR); -- irq_reg_writel(1, gc->reg_base + AT91_AIC5_IDCR); -- irq_reg_writel(1, gc->reg_base + AT91_AIC5_ICCR); -+ irq_reg_writel(gc, i, AT91_AIC5_SSR); -+ irq_reg_writel(gc, i, AT91_AIC5_SVR); -+ irq_reg_writel(gc, 1, AT91_AIC5_IDCR); -+ irq_reg_writel(gc, 1, AT91_AIC5_ICCR); - } - } - -@@ -273,11 +270,11 @@ static int aic5_irq_domain_xlate(struct irq_domain *d, - gc = dgc->gc[0]; - - irq_gc_lock(gc); -- irq_reg_writel(*out_hwirq, gc->reg_base + AT91_AIC5_SSR); -- smr = irq_reg_readl(gc->reg_base + AT91_AIC5_SMR); -+ irq_reg_writel(gc, *out_hwirq, AT91_AIC5_SSR); -+ smr = irq_reg_readl(gc, AT91_AIC5_SMR); - ret = aic_common_set_priority(intspec[2], &smr); - if (!ret) -- irq_reg_writel(intspec[2] | smr, gc->reg_base + AT91_AIC5_SMR); -+ irq_reg_writel(gc, intspec[2] | smr, AT91_AIC5_SMR); - irq_gc_unlock(gc); - - return ret; -diff --git a/drivers/irqchip/irq-gic-common.c b/drivers/irqchip/irq-gic-common.c -index 61541ff..ad96ebb 100644 ---- a/drivers/irqchip/irq-gic-common.c -+++ b/drivers/irqchip/irq-gic-common.c -@@ -21,7 +21,7 @@ - - #include "irq-gic-common.h" - --void gic_configure_irq(unsigned int irq, unsigned int type, -+int gic_configure_irq(unsigned int irq, unsigned int type, - void __iomem *base, void (*sync_access)(void)) - { - u32 enablemask = 1 << (irq % 32); -@@ -29,16 +29,17 @@ void gic_configure_irq(unsigned int irq, unsigned int type, - u32 confmask = 0x2 << ((irq % 16) * 2); - u32 confoff = (irq / 16) * 4; - bool enabled = false; -- u32 val; -+ u32 val, oldval; -+ int ret = 0; - - /* - * Read current configuration register, and insert the config - * for "irq", depending on "type". - */ -- val = readl_relaxed(base + GIC_DIST_CONFIG + confoff); -- if (type == IRQ_TYPE_LEVEL_HIGH) -+ val = oldval = readl_relaxed(base + GIC_DIST_CONFIG + confoff); -+ if (type & IRQ_TYPE_LEVEL_MASK) - val &= ~confmask; -- else if (type == IRQ_TYPE_EDGE_RISING) -+ else if (type & IRQ_TYPE_EDGE_BOTH) - val |= confmask; - - /* -@@ -54,15 +55,20 @@ void gic_configure_irq(unsigned int irq, unsigned int type, - - /* - * Write back the new configuration, and possibly re-enable -- * the interrupt. -+ * the interrupt. If we tried to write a new configuration and failed, -+ * return an error. - */ - writel_relaxed(val, base + GIC_DIST_CONFIG + confoff); -+ if (readl_relaxed(base + GIC_DIST_CONFIG + confoff) != val && val != oldval) -+ ret = -EINVAL; - - if (enabled) - writel_relaxed(enablemask, base + GIC_DIST_ENABLE_SET + enableoff); - - if (sync_access) - sync_access(); -+ -+ return ret; - } - - void __init gic_dist_config(void __iomem *base, int gic_irqs, -diff --git a/drivers/irqchip/irq-gic-common.h b/drivers/irqchip/irq-gic-common.h -index b41f024..35a9884 100644 ---- a/drivers/irqchip/irq-gic-common.h -+++ b/drivers/irqchip/irq-gic-common.h -@@ -20,7 +20,7 @@ - #include - #include - --void gic_configure_irq(unsigned int irq, unsigned int type, -+int gic_configure_irq(unsigned int irq, unsigned int type, - void __iomem *base, void (*sync_access)(void)); - void gic_dist_config(void __iomem *base, int gic_irqs, - void (*sync_access)(void)); -diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c -new file mode 100644 -index 0000000..fdf7065 ---- /dev/null -+++ b/drivers/irqchip/irq-gic-v2m.c -@@ -0,0 +1,333 @@ -+/* -+ * ARM GIC v2m MSI(-X) support -+ * Support for Message Signaled Interrupts for systems that -+ * implement ARM Generic Interrupt Controller: GICv2m. -+ * -+ * Copyright (C) 2014 Advanced Micro Devices, Inc. -+ * Authors: Suravee Suthikulpanit -+ * Harish Kasiviswanathan -+ * Brandon Anderson -+ * -+ * 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) "GICv2m: " fmt -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* -+* MSI_TYPER: -+* [31:26] Reserved -+* [25:16] lowest SPI assigned to MSI -+* [15:10] Reserved -+* [9:0] Numer of SPIs assigned to MSI -+*/ -+#define V2M_MSI_TYPER 0x008 -+#define V2M_MSI_TYPER_BASE_SHIFT 16 -+#define V2M_MSI_TYPER_BASE_MASK 0x3FF -+#define V2M_MSI_TYPER_NUM_MASK 0x3FF -+#define V2M_MSI_SETSPI_NS 0x040 -+#define V2M_MIN_SPI 32 -+#define V2M_MAX_SPI 1019 -+ -+#define V2M_MSI_TYPER_BASE_SPI(x) \ -+ (((x) >> V2M_MSI_TYPER_BASE_SHIFT) & V2M_MSI_TYPER_BASE_MASK) -+ -+#define V2M_MSI_TYPER_NUM_SPI(x) ((x) & V2M_MSI_TYPER_NUM_MASK) -+ -+struct v2m_data { -+ spinlock_t msi_cnt_lock; -+ struct msi_controller mchip; -+ struct resource res; /* GICv2m resource */ -+ void __iomem *base; /* GICv2m virt address */ -+ u32 spi_start; /* The SPI number that MSIs start */ -+ u32 nr_spis; /* The number of SPIs for MSIs */ -+ unsigned long *bm; /* MSI vector bitmap */ -+ struct irq_domain *domain; -+}; -+ -+static void gicv2m_mask_msi_irq(struct irq_data *d) -+{ -+ pci_msi_mask_irq(d); -+ irq_chip_mask_parent(d); -+} -+ -+static void gicv2m_unmask_msi_irq(struct irq_data *d) -+{ -+ pci_msi_unmask_irq(d); -+ irq_chip_unmask_parent(d); -+} -+ -+static struct irq_chip gicv2m_msi_irq_chip = { -+ .name = "MSI", -+ .irq_mask = gicv2m_mask_msi_irq, -+ .irq_unmask = gicv2m_unmask_msi_irq, -+ .irq_eoi = irq_chip_eoi_parent, -+ .irq_write_msi_msg = pci_msi_domain_write_msg, -+}; -+ -+static struct msi_domain_info gicv2m_msi_domain_info = { -+ .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | -+ MSI_FLAG_PCI_MSIX), -+ .chip = &gicv2m_msi_irq_chip, -+}; -+ -+static int gicv2m_set_affinity(struct irq_data *irq_data, -+ const struct cpumask *mask, bool force) -+{ -+ int ret; -+ -+ ret = irq_chip_set_affinity_parent(irq_data, mask, force); -+ if (ret == IRQ_SET_MASK_OK) -+ ret = IRQ_SET_MASK_OK_DONE; -+ -+ return ret; -+} -+ -+static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) -+{ -+ struct v2m_data *v2m = irq_data_get_irq_chip_data(data); -+ phys_addr_t addr = v2m->res.start + V2M_MSI_SETSPI_NS; -+ -+ msg->address_hi = (u32) (addr >> 32); -+ msg->address_lo = (u32) (addr); -+ msg->data = data->hwirq; -+} -+ -+static struct irq_chip gicv2m_irq_chip = { -+ .name = "GICv2m", -+ .irq_mask = irq_chip_mask_parent, -+ .irq_unmask = irq_chip_unmask_parent, -+ .irq_eoi = irq_chip_eoi_parent, -+ .irq_set_affinity = gicv2m_set_affinity, -+ .irq_compose_msi_msg = gicv2m_compose_msi_msg, -+}; -+ -+static int gicv2m_irq_gic_domain_alloc(struct irq_domain *domain, -+ unsigned int virq, -+ irq_hw_number_t hwirq) -+{ -+ struct of_phandle_args args; -+ struct irq_data *d; -+ int err; -+ -+ args.np = domain->parent->of_node; -+ args.args_count = 3; -+ args.args[0] = 0; -+ args.args[1] = hwirq - 32; -+ args.args[2] = IRQ_TYPE_EDGE_RISING; -+ -+ err = irq_domain_alloc_irqs_parent(domain, virq, 1, &args); -+ if (err) -+ return err; -+ -+ /* Configure the interrupt line to be edge */ -+ d = irq_domain_get_irq_data(domain->parent, virq); -+ d->chip->irq_set_type(d, IRQ_TYPE_EDGE_RISING); -+ return 0; -+} -+ -+static void gicv2m_unalloc_msi(struct v2m_data *v2m, unsigned int hwirq) -+{ -+ int pos; -+ -+ pos = hwirq - v2m->spi_start; -+ if (pos < 0 || pos >= v2m->nr_spis) { -+ pr_err("Failed to teardown msi. Invalid hwirq %d\n", hwirq); -+ return; -+ } -+ -+ spin_lock(&v2m->msi_cnt_lock); -+ __clear_bit(pos, v2m->bm); -+ spin_unlock(&v2m->msi_cnt_lock); -+} -+ -+static int gicv2m_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, -+ unsigned int nr_irqs, void *args) -+{ -+ struct v2m_data *v2m = domain->host_data; -+ int hwirq, offset, err = 0; -+ -+ spin_lock(&v2m->msi_cnt_lock); -+ offset = find_first_zero_bit(v2m->bm, v2m->nr_spis); -+ if (offset < v2m->nr_spis) -+ __set_bit(offset, v2m->bm); -+ else -+ err = -ENOSPC; -+ spin_unlock(&v2m->msi_cnt_lock); -+ -+ if (err) -+ return err; -+ -+ hwirq = v2m->spi_start + offset; -+ -+ err = gicv2m_irq_gic_domain_alloc(domain, virq, hwirq); -+ if (err) { -+ gicv2m_unalloc_msi(v2m, hwirq); -+ return err; -+ } -+ -+ irq_domain_set_hwirq_and_chip(domain, virq, hwirq, -+ &gicv2m_irq_chip, v2m); -+ -+ return 0; -+} -+ -+static void gicv2m_irq_domain_free(struct irq_domain *domain, -+ unsigned int virq, unsigned int nr_irqs) -+{ -+ struct irq_data *d = irq_domain_get_irq_data(domain, virq); -+ struct v2m_data *v2m = irq_data_get_irq_chip_data(d); -+ -+ BUG_ON(nr_irqs != 1); -+ gicv2m_unalloc_msi(v2m, d->hwirq); -+ irq_domain_free_irqs_parent(domain, virq, nr_irqs); -+} -+ -+static const struct irq_domain_ops gicv2m_domain_ops = { -+ .alloc = gicv2m_irq_domain_alloc, -+ .free = gicv2m_irq_domain_free, -+}; -+ -+static bool is_msi_spi_valid(u32 base, u32 num) -+{ -+ if (base < V2M_MIN_SPI) { -+ pr_err("Invalid MSI base SPI (base:%u)\n", base); -+ return false; -+ } -+ -+ if ((num == 0) || (base + num > V2M_MAX_SPI)) { -+ pr_err("Number of SPIs (%u) exceed maximum (%u)\n", -+ num, V2M_MAX_SPI - V2M_MIN_SPI + 1); -+ return false; -+ } -+ -+ return true; -+} -+ -+static int __init gicv2m_init_one(struct device_node *node, -+ struct irq_domain *parent) -+{ -+ int ret; -+ struct v2m_data *v2m; -+ -+ v2m = kzalloc(sizeof(struct v2m_data), GFP_KERNEL); -+ if (!v2m) { -+ pr_err("Failed to allocate struct v2m_data.\n"); -+ return -ENOMEM; -+ } -+ -+ ret = of_address_to_resource(node, 0, &v2m->res); -+ if (ret) { -+ pr_err("Failed to allocate v2m resource.\n"); -+ goto err_free_v2m; -+ } -+ -+ v2m->base = ioremap(v2m->res.start, resource_size(&v2m->res)); -+ if (!v2m->base) { -+ pr_err("Failed to map GICv2m resource\n"); -+ ret = -ENOMEM; -+ goto err_free_v2m; -+ } -+ -+ if (!of_property_read_u32(node, "arm,msi-base-spi", &v2m->spi_start) && -+ !of_property_read_u32(node, "arm,msi-num-spis", &v2m->nr_spis)) { -+ pr_info("Overriding V2M MSI_TYPER (base:%u, num:%u)\n", -+ v2m->spi_start, v2m->nr_spis); -+ } else { -+ u32 typer = readl_relaxed(v2m->base + V2M_MSI_TYPER); -+ -+ v2m->spi_start = V2M_MSI_TYPER_BASE_SPI(typer); -+ v2m->nr_spis = V2M_MSI_TYPER_NUM_SPI(typer); -+ } -+ -+ if (!is_msi_spi_valid(v2m->spi_start, v2m->nr_spis)) { -+ ret = -EINVAL; -+ goto err_iounmap; -+ } -+ -+ v2m->bm = kzalloc(sizeof(long) * BITS_TO_LONGS(v2m->nr_spis), -+ GFP_KERNEL); -+ if (!v2m->bm) { -+ ret = -ENOMEM; -+ goto err_iounmap; -+ } -+ -+ v2m->domain = irq_domain_add_tree(NULL, &gicv2m_domain_ops, v2m); -+ if (!v2m->domain) { -+ pr_err("Failed to create GICv2m domain\n"); -+ ret = -ENOMEM; -+ goto err_free_bm; -+ } -+ -+ v2m->domain->parent = parent; -+ v2m->mchip.of_node = node; -+ v2m->mchip.domain = pci_msi_create_irq_domain(node, -+ &gicv2m_msi_domain_info, -+ v2m->domain); -+ if (!v2m->mchip.domain) { -+ pr_err("Failed to create MSI domain\n"); -+ ret = -ENOMEM; -+ goto err_free_domains; -+ } -+ -+ spin_lock_init(&v2m->msi_cnt_lock); -+ -+ ret = of_pci_msi_chip_add(&v2m->mchip); -+ if (ret) { -+ pr_err("Failed to add msi_chip.\n"); -+ goto err_free_domains; -+ } -+ -+ pr_info("Node %s: range[%#lx:%#lx], SPI[%d:%d]\n", node->name, -+ (unsigned long)v2m->res.start, (unsigned long)v2m->res.end, -+ v2m->spi_start, (v2m->spi_start + v2m->nr_spis)); -+ -+ return 0; -+ -+err_free_domains: -+ if (v2m->mchip.domain) -+ irq_domain_remove(v2m->mchip.domain); -+ if (v2m->domain) -+ irq_domain_remove(v2m->domain); -+err_free_bm: -+ kfree(v2m->bm); -+err_iounmap: -+ iounmap(v2m->base); -+err_free_v2m: -+ kfree(v2m); -+ return ret; -+} -+ -+static struct of_device_id gicv2m_device_id[] = { -+ { .compatible = "arm,gic-v2m-frame", }, -+ {}, -+}; -+ -+int __init gicv2m_of_init(struct device_node *node, struct irq_domain *parent) -+{ -+ int ret = 0; -+ struct device_node *child; -+ -+ for (child = of_find_matching_node(node, gicv2m_device_id); child; -+ child = of_find_matching_node(child, gicv2m_device_id)) { -+ if (!of_find_property(child, "msi-controller", NULL)) -+ continue; -+ -+ ret = gicv2m_init_one(child, parent); -+ if (ret) { -+ of_node_put(node); -+ break; -+ } -+ } -+ -+ return ret; -+} -diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c -new file mode 100644 -index 0000000..d689158 ---- /dev/null -+++ b/drivers/irqchip/irq-gic-v3-its.c -@@ -0,0 +1,1630 @@ -+/* -+ * Copyright (C) 2013, 2014 ARM Limited, All Rights Reserved. -+ * Author: Marc Zyngier -+ * -+ * 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. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#include -+#include -+#include -+ -+#include "irqchip.h" -+ -+#define ITS_FLAGS_CMDQ_NEEDS_FLUSHING (1 << 0) -+ -+#define RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING (1 << 0) -+ -+/* -+ * Collection structure - just an ID, and a redistributor address to -+ * ping. We use one per CPU as a bag of interrupts assigned to this -+ * CPU. -+ */ -+struct its_collection { -+ u64 target_address; -+ u16 col_id; -+}; -+ -+/* -+ * The ITS structure - contains most of the infrastructure, with the -+ * msi_controller, the command queue, the collections, and the list of -+ * devices writing to it. -+ */ -+struct its_node { -+ raw_spinlock_t lock; -+ struct list_head entry; -+ struct msi_controller msi_chip; -+ struct irq_domain *domain; -+ void __iomem *base; -+ unsigned long phys_base; -+ struct its_cmd_block *cmd_base; -+ struct its_cmd_block *cmd_write; -+ void *tables[GITS_BASER_NR_REGS]; -+ struct its_collection *collections; -+ struct list_head its_device_list; -+ u64 flags; -+ u32 ite_size; -+}; -+ -+#define ITS_ITT_ALIGN SZ_256 -+ -+struct event_lpi_map { -+ unsigned long *lpi_map; -+ u16 *col_map; -+ irq_hw_number_t lpi_base; -+ int nr_lpis; -+}; -+ -+/* -+ * The ITS view of a device - belongs to an ITS, a collection, owns an -+ * interrupt translation table, and a list of interrupts. -+ */ -+struct its_device { -+ struct list_head entry; -+ struct its_node *its; -+ struct event_lpi_map event_map; -+ void *itt; -+ u32 nr_ites; -+ u32 device_id; -+}; -+ -+static LIST_HEAD(its_nodes); -+static DEFINE_SPINLOCK(its_lock); -+static struct device_node *gic_root_node; -+static struct rdists *gic_rdists; -+ -+#define gic_data_rdist() (raw_cpu_ptr(gic_rdists->rdist)) -+#define gic_data_rdist_rd_base() (gic_data_rdist()->rd_base) -+ -+static struct its_collection *dev_event_to_col(struct its_device *its_dev, -+ u32 event) -+{ -+ struct its_node *its = its_dev->its; -+ -+ return its->collections + its_dev->event_map.col_map[event]; -+} -+ -+/* -+ * ITS command descriptors - parameters to be encoded in a command -+ * block. -+ */ -+struct its_cmd_desc { -+ union { -+ struct { -+ struct its_device *dev; -+ u32 event_id; -+ } its_inv_cmd; -+ -+ struct { -+ struct its_device *dev; -+ u32 event_id; -+ } its_int_cmd; -+ -+ struct { -+ struct its_device *dev; -+ int valid; -+ } its_mapd_cmd; -+ -+ struct { -+ struct its_collection *col; -+ int valid; -+ } its_mapc_cmd; -+ -+ struct { -+ struct its_device *dev; -+ u32 phys_id; -+ u32 event_id; -+ } its_mapvi_cmd; -+ -+ struct { -+ struct its_device *dev; -+ struct its_collection *col; -+ u32 event_id; -+ } its_movi_cmd; -+ -+ struct { -+ struct its_device *dev; -+ u32 event_id; -+ } its_discard_cmd; -+ -+ struct { -+ struct its_collection *col; -+ } its_invall_cmd; -+ }; -+}; -+ -+/* -+ * The ITS command block, which is what the ITS actually parses. -+ */ -+struct its_cmd_block { -+ u64 raw_cmd[4]; -+}; -+ -+#define ITS_CMD_QUEUE_SZ SZ_64K -+#define ITS_CMD_QUEUE_NR_ENTRIES (ITS_CMD_QUEUE_SZ / sizeof(struct its_cmd_block)) -+ -+typedef struct its_collection *(*its_cmd_builder_t)(struct its_cmd_block *, -+ struct its_cmd_desc *); -+ -+static void its_encode_cmd(struct its_cmd_block *cmd, u8 cmd_nr) -+{ -+ cmd->raw_cmd[0] &= ~0xffUL; -+ cmd->raw_cmd[0] |= cmd_nr; -+} -+ -+static void its_encode_devid(struct its_cmd_block *cmd, u32 devid) -+{ -+ cmd->raw_cmd[0] &= BIT_ULL(32) - 1; -+ cmd->raw_cmd[0] |= ((u64)devid) << 32; -+} -+ -+static void its_encode_event_id(struct its_cmd_block *cmd, u32 id) -+{ -+ cmd->raw_cmd[1] &= ~0xffffffffUL; -+ cmd->raw_cmd[1] |= id; -+} -+ -+static void its_encode_phys_id(struct its_cmd_block *cmd, u32 phys_id) -+{ -+ cmd->raw_cmd[1] &= 0xffffffffUL; -+ cmd->raw_cmd[1] |= ((u64)phys_id) << 32; -+} -+ -+static void its_encode_size(struct its_cmd_block *cmd, u8 size) -+{ -+ cmd->raw_cmd[1] &= ~0x1fUL; -+ cmd->raw_cmd[1] |= size & 0x1f; -+} -+ -+static void its_encode_itt(struct its_cmd_block *cmd, u64 itt_addr) -+{ -+ cmd->raw_cmd[2] &= ~0xffffffffffffUL; -+ cmd->raw_cmd[2] |= itt_addr & 0xffffffffff00UL; -+} -+ -+static void its_encode_valid(struct its_cmd_block *cmd, int valid) -+{ -+ cmd->raw_cmd[2] &= ~(1UL << 63); -+ cmd->raw_cmd[2] |= ((u64)!!valid) << 63; -+} -+ -+static void its_encode_target(struct its_cmd_block *cmd, u64 target_addr) -+{ -+ cmd->raw_cmd[2] &= ~(0xffffffffUL << 16); -+ cmd->raw_cmd[2] |= (target_addr & (0xffffffffUL << 16)); -+} -+ -+static void its_encode_collection(struct its_cmd_block *cmd, u16 col) -+{ -+ cmd->raw_cmd[2] &= ~0xffffUL; -+ cmd->raw_cmd[2] |= col; -+} -+ -+static inline void its_fixup_cmd(struct its_cmd_block *cmd) -+{ -+ /* Let's fixup BE commands */ -+ cmd->raw_cmd[0] = cpu_to_le64(cmd->raw_cmd[0]); -+ cmd->raw_cmd[1] = cpu_to_le64(cmd->raw_cmd[1]); -+ cmd->raw_cmd[2] = cpu_to_le64(cmd->raw_cmd[2]); -+ cmd->raw_cmd[3] = cpu_to_le64(cmd->raw_cmd[3]); -+} -+ -+static struct its_collection *its_build_mapd_cmd(struct its_cmd_block *cmd, -+ struct its_cmd_desc *desc) -+{ -+ unsigned long itt_addr; -+ u8 size = ilog2(desc->its_mapd_cmd.dev->nr_ites); -+ -+ itt_addr = virt_to_phys(desc->its_mapd_cmd.dev->itt); -+ itt_addr = ALIGN(itt_addr, ITS_ITT_ALIGN); -+ -+ its_encode_cmd(cmd, GITS_CMD_MAPD); -+ its_encode_devid(cmd, desc->its_mapd_cmd.dev->device_id); -+ its_encode_size(cmd, size - 1); -+ its_encode_itt(cmd, itt_addr); -+ its_encode_valid(cmd, desc->its_mapd_cmd.valid); -+ -+ its_fixup_cmd(cmd); -+ -+ return NULL; -+} -+ -+static struct its_collection *its_build_mapc_cmd(struct its_cmd_block *cmd, -+ struct its_cmd_desc *desc) -+{ -+ its_encode_cmd(cmd, GITS_CMD_MAPC); -+ its_encode_collection(cmd, desc->its_mapc_cmd.col->col_id); -+ its_encode_target(cmd, desc->its_mapc_cmd.col->target_address); -+ its_encode_valid(cmd, desc->its_mapc_cmd.valid); -+ -+ its_fixup_cmd(cmd); -+ -+ return desc->its_mapc_cmd.col; -+} -+ -+static struct its_collection *its_build_mapvi_cmd(struct its_cmd_block *cmd, -+ struct its_cmd_desc *desc) -+{ -+ struct its_collection *col; -+ -+ col = dev_event_to_col(desc->its_mapvi_cmd.dev, -+ desc->its_mapvi_cmd.event_id); -+ -+ its_encode_cmd(cmd, GITS_CMD_MAPVI); -+ its_encode_devid(cmd, desc->its_mapvi_cmd.dev->device_id); -+ its_encode_event_id(cmd, desc->its_mapvi_cmd.event_id); -+ its_encode_phys_id(cmd, desc->its_mapvi_cmd.phys_id); -+ its_encode_collection(cmd, col->col_id); -+ -+ its_fixup_cmd(cmd); -+ -+ return col; -+} -+ -+static struct its_collection *its_build_movi_cmd(struct its_cmd_block *cmd, -+ struct its_cmd_desc *desc) -+{ -+ struct its_collection *col; -+ -+ col = dev_event_to_col(desc->its_movi_cmd.dev, -+ desc->its_movi_cmd.event_id); -+ -+ its_encode_cmd(cmd, GITS_CMD_MOVI); -+ its_encode_devid(cmd, desc->its_movi_cmd.dev->device_id); -+ its_encode_event_id(cmd, desc->its_movi_cmd.event_id); -+ its_encode_collection(cmd, desc->its_movi_cmd.col->col_id); -+ -+ its_fixup_cmd(cmd); -+ -+ return col; -+} -+ -+static struct its_collection *its_build_discard_cmd(struct its_cmd_block *cmd, -+ struct its_cmd_desc *desc) -+{ -+ struct its_collection *col; -+ -+ col = dev_event_to_col(desc->its_discard_cmd.dev, -+ desc->its_discard_cmd.event_id); -+ -+ its_encode_cmd(cmd, GITS_CMD_DISCARD); -+ its_encode_devid(cmd, desc->its_discard_cmd.dev->device_id); -+ its_encode_event_id(cmd, desc->its_discard_cmd.event_id); -+ -+ its_fixup_cmd(cmd); -+ -+ return col; -+} -+ -+static struct its_collection *its_build_inv_cmd(struct its_cmd_block *cmd, -+ struct its_cmd_desc *desc) -+{ -+ struct its_collection *col; -+ -+ col = dev_event_to_col(desc->its_inv_cmd.dev, -+ desc->its_inv_cmd.event_id); -+ -+ its_encode_cmd(cmd, GITS_CMD_INV); -+ its_encode_devid(cmd, desc->its_inv_cmd.dev->device_id); -+ its_encode_event_id(cmd, desc->its_inv_cmd.event_id); -+ -+ its_fixup_cmd(cmd); -+ -+ return col; -+} -+ -+static struct its_collection *its_build_invall_cmd(struct its_cmd_block *cmd, -+ struct its_cmd_desc *desc) -+{ -+ its_encode_cmd(cmd, GITS_CMD_INVALL); -+ its_encode_collection(cmd, desc->its_mapc_cmd.col->col_id); -+ -+ its_fixup_cmd(cmd); -+ -+ return NULL; -+} -+ -+static u64 its_cmd_ptr_to_offset(struct its_node *its, -+ struct its_cmd_block *ptr) -+{ -+ return (ptr - its->cmd_base) * sizeof(*ptr); -+} -+ -+static int its_queue_full(struct its_node *its) -+{ -+ int widx; -+ int ridx; -+ -+ widx = its->cmd_write - its->cmd_base; -+ ridx = readl_relaxed(its->base + GITS_CREADR) / sizeof(struct its_cmd_block); -+ -+ /* This is incredibly unlikely to happen, unless the ITS locks up. */ -+ if (((widx + 1) % ITS_CMD_QUEUE_NR_ENTRIES) == ridx) -+ return 1; -+ -+ return 0; -+} -+ -+static struct its_cmd_block *its_allocate_entry(struct its_node *its) -+{ -+ struct its_cmd_block *cmd; -+ u32 count = 1000000; /* 1s! */ -+ -+ while (its_queue_full(its)) { -+ count--; -+ if (!count) { -+ pr_err_ratelimited("ITS queue not draining\n"); -+ return NULL; -+ } -+ cpu_relax(); -+ udelay(1); -+ } -+ -+ cmd = its->cmd_write++; -+ -+ /* Handle queue wrapping */ -+ if (its->cmd_write == (its->cmd_base + ITS_CMD_QUEUE_NR_ENTRIES)) -+ its->cmd_write = its->cmd_base; -+ -+ return cmd; -+} -+ -+static struct its_cmd_block *its_post_commands(struct its_node *its) -+{ -+ u64 wr = its_cmd_ptr_to_offset(its, its->cmd_write); -+ -+ writel_relaxed(wr, its->base + GITS_CWRITER); -+ -+ return its->cmd_write; -+} -+ -+static void its_flush_cmd(struct its_node *its, struct its_cmd_block *cmd) -+{ -+ /* -+ * Make sure the commands written to memory are observable by -+ * the ITS. -+ */ -+ if (its->flags & ITS_FLAGS_CMDQ_NEEDS_FLUSHING) -+ __flush_dcache_area(cmd, sizeof(*cmd)); -+ else -+ dsb(ishst); -+} -+ -+static void its_wait_for_range_completion(struct its_node *its, -+ struct its_cmd_block *from, -+ struct its_cmd_block *to) -+{ -+ u64 rd_idx, from_idx, to_idx; -+ u32 count = 1000000; /* 1s! */ -+ -+ from_idx = its_cmd_ptr_to_offset(its, from); -+ to_idx = its_cmd_ptr_to_offset(its, to); -+ -+ while (1) { -+ rd_idx = readl_relaxed(its->base + GITS_CREADR); -+ if (rd_idx >= to_idx || rd_idx < from_idx) -+ break; -+ -+ count--; -+ if (!count) { -+ pr_err_ratelimited("ITS queue timeout\n"); -+ return; -+ } -+ cpu_relax(); -+ udelay(1); -+ } -+} -+ -+static void its_send_single_command(struct its_node *its, -+ its_cmd_builder_t builder, -+ struct its_cmd_desc *desc) -+{ -+ struct its_cmd_block *cmd, *sync_cmd, *next_cmd; -+ struct its_collection *sync_col; -+ unsigned long flags; -+ -+ raw_spin_lock_irqsave(&its->lock, flags); -+ -+ cmd = its_allocate_entry(its); -+ if (!cmd) { /* We're soooooo screewed... */ -+ pr_err_ratelimited("ITS can't allocate, dropping command\n"); -+ raw_spin_unlock_irqrestore(&its->lock, flags); -+ return; -+ } -+ sync_col = builder(cmd, desc); -+ its_flush_cmd(its, cmd); -+ -+ if (sync_col) { -+ sync_cmd = its_allocate_entry(its); -+ if (!sync_cmd) { -+ pr_err_ratelimited("ITS can't SYNC, skipping\n"); -+ goto post; -+ } -+ its_encode_cmd(sync_cmd, GITS_CMD_SYNC); -+ its_encode_target(sync_cmd, sync_col->target_address); -+ its_fixup_cmd(sync_cmd); -+ its_flush_cmd(its, sync_cmd); -+ } -+ -+post: -+ next_cmd = its_post_commands(its); -+ raw_spin_unlock_irqrestore(&its->lock, flags); -+ -+ its_wait_for_range_completion(its, cmd, next_cmd); -+} -+ -+static void its_send_inv(struct its_device *dev, u32 event_id) -+{ -+ struct its_cmd_desc desc; -+ -+ desc.its_inv_cmd.dev = dev; -+ desc.its_inv_cmd.event_id = event_id; -+ -+ its_send_single_command(dev->its, its_build_inv_cmd, &desc); -+} -+ -+static void its_send_mapd(struct its_device *dev, int valid) -+{ -+ struct its_cmd_desc desc; -+ -+ desc.its_mapd_cmd.dev = dev; -+ desc.its_mapd_cmd.valid = !!valid; -+ -+ its_send_single_command(dev->its, its_build_mapd_cmd, &desc); -+} -+ -+static void its_send_mapc(struct its_node *its, struct its_collection *col, -+ int valid) -+{ -+ struct its_cmd_desc desc; -+ -+ desc.its_mapc_cmd.col = col; -+ desc.its_mapc_cmd.valid = !!valid; -+ -+ its_send_single_command(its, its_build_mapc_cmd, &desc); -+} -+ -+static void its_send_mapvi(struct its_device *dev, u32 irq_id, u32 id) -+{ -+ struct its_cmd_desc desc; -+ -+ desc.its_mapvi_cmd.dev = dev; -+ desc.its_mapvi_cmd.phys_id = irq_id; -+ desc.its_mapvi_cmd.event_id = id; -+ -+ its_send_single_command(dev->its, its_build_mapvi_cmd, &desc); -+} -+ -+static void its_send_movi(struct its_device *dev, -+ struct its_collection *col, u32 id) -+{ -+ struct its_cmd_desc desc; -+ -+ desc.its_movi_cmd.dev = dev; -+ desc.its_movi_cmd.col = col; -+ desc.its_movi_cmd.event_id = id; -+ -+ its_send_single_command(dev->its, its_build_movi_cmd, &desc); -+} -+ -+static void its_send_discard(struct its_device *dev, u32 id) -+{ -+ struct its_cmd_desc desc; -+ -+ desc.its_discard_cmd.dev = dev; -+ desc.its_discard_cmd.event_id = id; -+ -+ its_send_single_command(dev->its, its_build_discard_cmd, &desc); -+} -+ -+static void its_send_invall(struct its_node *its, struct its_collection *col) -+{ -+ struct its_cmd_desc desc; -+ -+ desc.its_invall_cmd.col = col; -+ -+ its_send_single_command(its, its_build_invall_cmd, &desc); -+} -+ -+/* -+ * irqchip functions - assumes MSI, mostly. -+ */ -+ -+static inline u32 its_get_event_id(struct irq_data *d) -+{ -+ struct its_device *its_dev = irq_data_get_irq_chip_data(d); -+ return d->hwirq - its_dev->event_map.lpi_base; -+} -+ -+static void lpi_set_config(struct irq_data *d, bool enable) -+{ -+ struct its_device *its_dev = irq_data_get_irq_chip_data(d); -+ irq_hw_number_t hwirq = d->hwirq; -+ u32 id = its_get_event_id(d); -+ u8 *cfg = page_address(gic_rdists->prop_page) + hwirq - 8192; -+ -+ if (enable) -+ *cfg |= LPI_PROP_ENABLED; -+ else -+ *cfg &= ~LPI_PROP_ENABLED; -+ -+ /* -+ * Make the above write visible to the redistributors. -+ * And yes, we're flushing exactly: One. Single. Byte. -+ * Humpf... -+ */ -+ if (gic_rdists->flags & RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING) -+ __flush_dcache_area(cfg, sizeof(*cfg)); -+ else -+ dsb(ishst); -+ its_send_inv(its_dev, id); -+} -+ -+static void its_mask_irq(struct irq_data *d) -+{ -+ lpi_set_config(d, false); -+} -+ -+static void its_unmask_irq(struct irq_data *d) -+{ -+ lpi_set_config(d, true); -+} -+ -+static void its_eoi_irq(struct irq_data *d) -+{ -+ gic_write_eoir(d->hwirq); -+} -+ -+static int its_set_affinity(struct irq_data *d, const struct cpumask *mask_val, -+ bool force) -+{ -+ unsigned int cpu = cpumask_any_and(mask_val, cpu_online_mask); -+ struct its_device *its_dev = irq_data_get_irq_chip_data(d); -+ struct its_collection *target_col; -+ u32 id = its_get_event_id(d); -+ -+ if (cpu >= nr_cpu_ids) -+ return -EINVAL; -+ -+ target_col = &its_dev->its->collections[cpu]; -+ its_send_movi(its_dev, target_col, id); -+ its_dev->event_map.col_map[id] = cpu; -+ -+ return IRQ_SET_MASK_OK_DONE; -+} -+ -+static void its_irq_compose_msi_msg(struct irq_data *d, struct msi_msg *msg) -+{ -+ struct its_device *its_dev = irq_data_get_irq_chip_data(d); -+ struct its_node *its; -+ u64 addr; -+ -+ its = its_dev->its; -+ addr = its->phys_base + GITS_TRANSLATER; -+ -+ msg->address_lo = addr & ((1UL << 32) - 1); -+ msg->address_hi = addr >> 32; -+ msg->data = its_get_event_id(d); -+} -+ -+static struct irq_chip its_irq_chip = { -+ .name = "ITS", -+ .irq_mask = its_mask_irq, -+ .irq_unmask = its_unmask_irq, -+ .irq_eoi = its_eoi_irq, -+ .irq_set_affinity = its_set_affinity, -+ .irq_compose_msi_msg = its_irq_compose_msi_msg, -+}; -+ -+static void its_mask_msi_irq(struct irq_data *d) -+{ -+ pci_msi_mask_irq(d); -+ irq_chip_mask_parent(d); -+} -+ -+static void its_unmask_msi_irq(struct irq_data *d) -+{ -+ pci_msi_unmask_irq(d); -+ irq_chip_unmask_parent(d); -+} -+ -+static struct irq_chip its_msi_irq_chip = { -+ .name = "ITS-MSI", -+ .irq_unmask = its_unmask_msi_irq, -+ .irq_mask = its_mask_msi_irq, -+ .irq_eoi = irq_chip_eoi_parent, -+ .irq_write_msi_msg = pci_msi_domain_write_msg, -+}; -+ -+/* -+ * How we allocate LPIs: -+ * -+ * The GIC has id_bits bits for interrupt identifiers. From there, we -+ * must subtract 8192 which are reserved for SGIs/PPIs/SPIs. Then, as -+ * we allocate LPIs by chunks of 32, we can shift the whole thing by 5 -+ * bits to the right. -+ * -+ * This gives us (((1UL << id_bits) - 8192) >> 5) possible allocations. -+ */ -+#define IRQS_PER_CHUNK_SHIFT 5 -+#define IRQS_PER_CHUNK (1 << IRQS_PER_CHUNK_SHIFT) -+ -+static unsigned long *lpi_bitmap; -+static u32 lpi_chunks; -+static DEFINE_SPINLOCK(lpi_lock); -+ -+static int its_lpi_to_chunk(int lpi) -+{ -+ return (lpi - 8192) >> IRQS_PER_CHUNK_SHIFT; -+} -+ -+static int its_chunk_to_lpi(int chunk) -+{ -+ return (chunk << IRQS_PER_CHUNK_SHIFT) + 8192; -+} -+ -+static int its_lpi_init(u32 id_bits) -+{ -+ lpi_chunks = its_lpi_to_chunk(1UL << id_bits); -+ -+ lpi_bitmap = kzalloc(BITS_TO_LONGS(lpi_chunks) * sizeof(long), -+ GFP_KERNEL); -+ if (!lpi_bitmap) { -+ lpi_chunks = 0; -+ return -ENOMEM; -+ } -+ -+ pr_info("ITS: Allocated %d chunks for LPIs\n", (int)lpi_chunks); -+ return 0; -+} -+ -+static unsigned long *its_lpi_alloc_chunks(int nr_irqs, int *base, int *nr_ids) -+{ -+ unsigned long *bitmap = NULL; -+ int chunk_id; -+ int nr_chunks; -+ int i; -+ -+ nr_chunks = DIV_ROUND_UP(nr_irqs, IRQS_PER_CHUNK); -+ -+ spin_lock(&lpi_lock); -+ -+ do { -+ chunk_id = bitmap_find_next_zero_area(lpi_bitmap, lpi_chunks, -+ 0, nr_chunks, 0); -+ if (chunk_id < lpi_chunks) -+ break; -+ -+ nr_chunks--; -+ } while (nr_chunks > 0); -+ -+ if (!nr_chunks) -+ goto out; -+ -+ bitmap = kzalloc(BITS_TO_LONGS(nr_chunks * IRQS_PER_CHUNK) * sizeof (long), -+ GFP_ATOMIC); -+ if (!bitmap) -+ goto out; -+ -+ for (i = 0; i < nr_chunks; i++) -+ set_bit(chunk_id + i, lpi_bitmap); -+ -+ *base = its_chunk_to_lpi(chunk_id); -+ *nr_ids = nr_chunks * IRQS_PER_CHUNK; -+ -+out: -+ spin_unlock(&lpi_lock); -+ -+ if (!bitmap) -+ *base = *nr_ids = 0; -+ -+ return bitmap; -+} -+ -+static void its_lpi_free(struct event_lpi_map *map) -+{ -+ int base = map->lpi_base; -+ int nr_ids = map->nr_lpis; -+ int lpi; -+ -+ spin_lock(&lpi_lock); -+ -+ for (lpi = base; lpi < (base + nr_ids); lpi += IRQS_PER_CHUNK) { -+ int chunk = its_lpi_to_chunk(lpi); -+ BUG_ON(chunk > lpi_chunks); -+ if (test_bit(chunk, lpi_bitmap)) { -+ clear_bit(chunk, lpi_bitmap); -+ } else { -+ pr_err("Bad LPI chunk %d\n", chunk); -+ } -+ } -+ -+ spin_unlock(&lpi_lock); -+ -+ kfree(map->lpi_map); -+ kfree(map->col_map); -+} -+ -+/* -+ * We allocate 64kB for PROPBASE. That gives us at most 64K LPIs to -+ * deal with (one configuration byte per interrupt). PENDBASE has to -+ * be 64kB aligned (one bit per LPI, plus 8192 bits for SPI/PPI/SGI). -+ */ -+#define LPI_PROPBASE_SZ SZ_64K -+#define LPI_PENDBASE_SZ (LPI_PROPBASE_SZ / 8 + SZ_1K) -+ -+/* -+ * This is how many bits of ID we need, including the useless ones. -+ */ -+#define LPI_NRBITS ilog2(LPI_PROPBASE_SZ + SZ_8K) -+ -+#define LPI_PROP_DEFAULT_PRIO 0xa0 -+ -+static int __init its_alloc_lpi_tables(void) -+{ -+ phys_addr_t paddr; -+ -+ gic_rdists->prop_page = alloc_pages(GFP_NOWAIT, -+ get_order(LPI_PROPBASE_SZ)); -+ if (!gic_rdists->prop_page) { -+ pr_err("Failed to allocate PROPBASE\n"); -+ return -ENOMEM; -+ } -+ -+ paddr = page_to_phys(gic_rdists->prop_page); -+ pr_info("GIC: using LPI property table @%pa\n", &paddr); -+ -+ /* Priority 0xa0, Group-1, disabled */ -+ memset(page_address(gic_rdists->prop_page), -+ LPI_PROP_DEFAULT_PRIO | LPI_PROP_GROUP1, -+ LPI_PROPBASE_SZ); -+ -+ /* Make sure the GIC will observe the written configuration */ -+ __flush_dcache_area(page_address(gic_rdists->prop_page), LPI_PROPBASE_SZ); -+ -+ return 0; -+} -+ -+static const char *its_base_type_string[] = { -+ [GITS_BASER_TYPE_DEVICE] = "Devices", -+ [GITS_BASER_TYPE_VCPU] = "Virtual CPUs", -+ [GITS_BASER_TYPE_CPU] = "Physical CPUs", -+ [GITS_BASER_TYPE_COLLECTION] = "Interrupt Collections", -+ [GITS_BASER_TYPE_RESERVED5] = "Reserved (5)", -+ [GITS_BASER_TYPE_RESERVED6] = "Reserved (6)", -+ [GITS_BASER_TYPE_RESERVED7] = "Reserved (7)", -+}; -+ -+static void its_free_tables(struct its_node *its) -+{ -+ int i; -+ -+ for (i = 0; i < GITS_BASER_NR_REGS; i++) { -+ if (its->tables[i]) { -+ free_page((unsigned long)its->tables[i]); -+ its->tables[i] = NULL; -+ } -+ } -+} -+ -+static int its_alloc_tables(struct its_node *its) -+{ -+ int err; -+ int i; -+ int psz = SZ_64K; -+ u64 shr = GITS_BASER_InnerShareable; -+ u64 cache = GITS_BASER_WaWb; -+ -+ for (i = 0; i < GITS_BASER_NR_REGS; i++) { -+ u64 val = readq_relaxed(its->base + GITS_BASER + i * 8); -+ u64 type = GITS_BASER_TYPE(val); -+ u64 entry_size = GITS_BASER_ENTRY_SIZE(val); -+ int order = get_order(psz); -+ int alloc_size; -+ u64 tmp; -+ void *base; -+ -+ if (type == GITS_BASER_TYPE_NONE) -+ continue; -+ -+ /* -+ * Allocate as many entries as required to fit the -+ * range of device IDs that the ITS can grok... The ID -+ * space being incredibly sparse, this results in a -+ * massive waste of memory. -+ * -+ * For other tables, only allocate a single page. -+ */ -+ if (type == GITS_BASER_TYPE_DEVICE) { -+ u64 typer = readq_relaxed(its->base + GITS_TYPER); -+ u32 ids = GITS_TYPER_DEVBITS(typer); -+ -+ /* -+ * 'order' was initialized earlier to the default page -+ * granule of the the ITS. We can't have an allocation -+ * smaller than that. If the requested allocation -+ * is smaller, round up to the default page granule. -+ */ -+ order = max(get_order((1UL << ids) * entry_size), -+ order); -+ if (order >= MAX_ORDER) { -+ order = MAX_ORDER - 1; -+ pr_warn("%s: Device Table too large, reduce its page order to %u\n", -+ its->msi_chip.of_node->full_name, order); -+ } -+ } -+ -+ alloc_size = (1 << order) * PAGE_SIZE; -+ base = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, order); -+ if (!base) { -+ err = -ENOMEM; -+ goto out_free; -+ } -+ -+ its->tables[i] = base; -+ -+retry_baser: -+ val = (virt_to_phys(base) | -+ (type << GITS_BASER_TYPE_SHIFT) | -+ ((entry_size - 1) << GITS_BASER_ENTRY_SIZE_SHIFT) | -+ cache | -+ shr | -+ GITS_BASER_VALID); -+ -+ switch (psz) { -+ case SZ_4K: -+ val |= GITS_BASER_PAGE_SIZE_4K; -+ break; -+ case SZ_16K: -+ val |= GITS_BASER_PAGE_SIZE_16K; -+ break; -+ case SZ_64K: -+ val |= GITS_BASER_PAGE_SIZE_64K; -+ break; -+ } -+ -+ val |= (alloc_size / psz) - 1; -+ -+ writeq_relaxed(val, its->base + GITS_BASER + i * 8); -+ tmp = readq_relaxed(its->base + GITS_BASER + i * 8); -+ -+ if ((val ^ tmp) & GITS_BASER_SHAREABILITY_MASK) { -+ /* -+ * Shareability didn't stick. Just use -+ * whatever the read reported, which is likely -+ * to be the only thing this redistributor -+ * supports. If that's zero, make it -+ * non-cacheable as well. -+ */ -+ shr = tmp & GITS_BASER_SHAREABILITY_MASK; -+ if (!shr) { -+ cache = GITS_BASER_nC; -+ __flush_dcache_area(base, alloc_size); -+ } -+ goto retry_baser; -+ } -+ -+ if ((val ^ tmp) & GITS_BASER_PAGE_SIZE_MASK) { -+ /* -+ * Page size didn't stick. Let's try a smaller -+ * size and retry. If we reach 4K, then -+ * something is horribly wrong... -+ */ -+ switch (psz) { -+ case SZ_16K: -+ psz = SZ_4K; -+ goto retry_baser; -+ case SZ_64K: -+ psz = SZ_16K; -+ goto retry_baser; -+ } -+ } -+ -+ if (val != tmp) { -+ pr_err("ITS: %s: GITS_BASER%d doesn't stick: %lx %lx\n", -+ its->msi_chip.of_node->full_name, i, -+ (unsigned long) val, (unsigned long) tmp); -+ err = -ENXIO; -+ goto out_free; -+ } -+ -+ pr_info("ITS: allocated %d %s @%lx (psz %dK, shr %d)\n", -+ (int)(alloc_size / entry_size), -+ its_base_type_string[type], -+ (unsigned long)virt_to_phys(base), -+ psz / SZ_1K, (int)shr >> GITS_BASER_SHAREABILITY_SHIFT); -+ } -+ -+ return 0; -+ -+out_free: -+ its_free_tables(its); -+ -+ return err; -+} -+ -+static int its_alloc_collections(struct its_node *its) -+{ -+ its->collections = kzalloc(nr_cpu_ids * sizeof(*its->collections), -+ GFP_KERNEL); -+ if (!its->collections) -+ return -ENOMEM; -+ -+ return 0; -+} -+ -+static void its_cpu_init_lpis(void) -+{ -+ void __iomem *rbase = gic_data_rdist_rd_base(); -+ struct page *pend_page; -+ u64 val, tmp; -+ -+ /* If we didn't allocate the pending table yet, do it now */ -+ pend_page = gic_data_rdist()->pend_page; -+ if (!pend_page) { -+ phys_addr_t paddr; -+ /* -+ * The pending pages have to be at least 64kB aligned, -+ * hence the 'max(LPI_PENDBASE_SZ, SZ_64K)' below. -+ */ -+ pend_page = alloc_pages(GFP_NOWAIT | __GFP_ZERO, -+ get_order(max(LPI_PENDBASE_SZ, SZ_64K))); -+ if (!pend_page) { -+ pr_err("Failed to allocate PENDBASE for CPU%d\n", -+ smp_processor_id()); -+ return; -+ } -+ -+ /* Make sure the GIC will observe the zero-ed page */ -+ __flush_dcache_area(page_address(pend_page), LPI_PENDBASE_SZ); -+ -+ paddr = page_to_phys(pend_page); -+ pr_info("CPU%d: using LPI pending table @%pa\n", -+ smp_processor_id(), &paddr); -+ gic_data_rdist()->pend_page = pend_page; -+ } -+ -+ /* Disable LPIs */ -+ val = readl_relaxed(rbase + GICR_CTLR); -+ val &= ~GICR_CTLR_ENABLE_LPIS; -+ writel_relaxed(val, rbase + GICR_CTLR); -+ -+ /* -+ * Make sure any change to the table is observable by the GIC. -+ */ -+ dsb(sy); -+ -+ /* set PROPBASE */ -+ val = (page_to_phys(gic_rdists->prop_page) | -+ GICR_PROPBASER_InnerShareable | -+ GICR_PROPBASER_WaWb | -+ ((LPI_NRBITS - 1) & GICR_PROPBASER_IDBITS_MASK)); -+ -+ writeq_relaxed(val, rbase + GICR_PROPBASER); -+ tmp = readq_relaxed(rbase + GICR_PROPBASER); -+ -+ if ((tmp ^ val) & GICR_PROPBASER_SHAREABILITY_MASK) { -+ if (!(tmp & GICR_PROPBASER_SHAREABILITY_MASK)) { -+ /* -+ * The HW reports non-shareable, we must -+ * remove the cacheability attributes as -+ * well. -+ */ -+ val &= ~(GICR_PROPBASER_SHAREABILITY_MASK | -+ GICR_PROPBASER_CACHEABILITY_MASK); -+ val |= GICR_PROPBASER_nC; -+ writeq_relaxed(val, rbase + GICR_PROPBASER); -+ } -+ pr_info_once("GIC: using cache flushing for LPI property table\n"); -+ gic_rdists->flags |= RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING; -+ } -+ -+ /* set PENDBASE */ -+ val = (page_to_phys(pend_page) | -+ GICR_PENDBASER_InnerShareable | -+ GICR_PENDBASER_WaWb); -+ -+ writeq_relaxed(val, rbase + GICR_PENDBASER); -+ tmp = readq_relaxed(rbase + GICR_PENDBASER); -+ -+ if (!(tmp & GICR_PENDBASER_SHAREABILITY_MASK)) { -+ /* -+ * The HW reports non-shareable, we must remove the -+ * cacheability attributes as well. -+ */ -+ val &= ~(GICR_PENDBASER_SHAREABILITY_MASK | -+ GICR_PENDBASER_CACHEABILITY_MASK); -+ val |= GICR_PENDBASER_nC; -+ writeq_relaxed(val, rbase + GICR_PENDBASER); -+ } -+ -+ /* Enable LPIs */ -+ val = readl_relaxed(rbase + GICR_CTLR); -+ val |= GICR_CTLR_ENABLE_LPIS; -+ writel_relaxed(val, rbase + GICR_CTLR); -+ -+ /* Make sure the GIC has seen the above */ -+ dsb(sy); -+} -+ -+static void its_cpu_init_collection(void) -+{ -+ struct its_node *its; -+ int cpu; -+ -+ spin_lock(&its_lock); -+ cpu = smp_processor_id(); -+ -+ list_for_each_entry(its, &its_nodes, entry) { -+ u64 target; -+ -+ /* -+ * We now have to bind each collection to its target -+ * redistributor. -+ */ -+ if (readq_relaxed(its->base + GITS_TYPER) & GITS_TYPER_PTA) { -+ /* -+ * This ITS wants the physical address of the -+ * redistributor. -+ */ -+ target = gic_data_rdist()->phys_base; -+ } else { -+ /* -+ * This ITS wants a linear CPU number. -+ */ -+ target = readq_relaxed(gic_data_rdist_rd_base() + GICR_TYPER); -+ target = GICR_TYPER_CPU_NUMBER(target) << 16; -+ } -+ -+ /* Perform collection mapping */ -+ its->collections[cpu].target_address = target; -+ its->collections[cpu].col_id = cpu; -+ -+ its_send_mapc(its, &its->collections[cpu], 1); -+ its_send_invall(its, &its->collections[cpu]); -+ } -+ -+ spin_unlock(&its_lock); -+} -+ -+static struct its_device *its_find_device(struct its_node *its, u32 dev_id) -+{ -+ struct its_device *its_dev = NULL, *tmp; -+ unsigned long flags; -+ -+ raw_spin_lock_irqsave(&its->lock, flags); -+ -+ list_for_each_entry(tmp, &its->its_device_list, entry) { -+ if (tmp->device_id == dev_id) { -+ its_dev = tmp; -+ break; -+ } -+ } -+ -+ raw_spin_unlock_irqrestore(&its->lock, flags); -+ -+ return its_dev; -+} -+ -+static struct its_device *its_create_device(struct its_node *its, u32 dev_id, -+ int nvecs) -+{ -+ struct its_device *dev; -+ unsigned long *lpi_map; -+ unsigned long flags; -+ u16 *col_map = NULL; -+ void *itt; -+ int lpi_base; -+ int nr_lpis; -+ int nr_ites; -+ int sz; -+ -+ dev = kzalloc(sizeof(*dev), GFP_KERNEL); -+ /* -+ * At least one bit of EventID is being used, hence a minimum -+ * of two entries. No, the architecture doesn't let you -+ * express an ITT with a single entry. -+ */ -+ nr_ites = max(2UL, roundup_pow_of_two(nvecs)); -+ sz = nr_ites * its->ite_size; -+ sz = max(sz, ITS_ITT_ALIGN) + ITS_ITT_ALIGN - 1; -+ itt = kzalloc(sz, GFP_KERNEL); -+ lpi_map = its_lpi_alloc_chunks(nvecs, &lpi_base, &nr_lpis); -+ if (lpi_map) -+ col_map = kzalloc(sizeof(*col_map) * nr_lpis, GFP_KERNEL); -+ -+ if (!dev || !itt || !lpi_map || !col_map) { -+ kfree(dev); -+ kfree(itt); -+ kfree(lpi_map); -+ kfree(col_map); -+ return NULL; -+ } -+ -+ __flush_dcache_area(itt, sz); -+ -+ dev->its = its; -+ dev->itt = itt; -+ dev->nr_ites = nr_ites; -+ dev->event_map.lpi_map = lpi_map; -+ dev->event_map.col_map = col_map; -+ dev->event_map.lpi_base = lpi_base; -+ dev->event_map.nr_lpis = nr_lpis; -+ dev->device_id = dev_id; -+ INIT_LIST_HEAD(&dev->entry); -+ -+ raw_spin_lock_irqsave(&its->lock, flags); -+ list_add(&dev->entry, &its->its_device_list); -+ raw_spin_unlock_irqrestore(&its->lock, flags); -+ -+ /* Map device to its ITT */ -+ its_send_mapd(dev, 1); -+ -+ return dev; -+} -+ -+static void its_free_device(struct its_device *its_dev) -+{ -+ unsigned long flags; -+ -+ raw_spin_lock_irqsave(&its_dev->its->lock, flags); -+ list_del(&its_dev->entry); -+ raw_spin_unlock_irqrestore(&its_dev->its->lock, flags); -+ kfree(its_dev->itt); -+ kfree(its_dev); -+} -+ -+static int its_alloc_device_irq(struct its_device *dev, irq_hw_number_t *hwirq) -+{ -+ int idx; -+ -+ idx = find_first_zero_bit(dev->event_map.lpi_map, -+ dev->event_map.nr_lpis); -+ if (idx == dev->event_map.nr_lpis) -+ return -ENOSPC; -+ -+ *hwirq = dev->event_map.lpi_base + idx; -+ set_bit(idx, dev->event_map.lpi_map); -+ -+ return 0; -+} -+ -+struct its_pci_alias { -+ struct pci_dev *pdev; -+ u32 dev_id; -+ u32 count; -+}; -+ -+static int its_pci_msi_vec_count(struct pci_dev *pdev) -+{ -+ int msi, msix; -+ -+ msi = max(pci_msi_vec_count(pdev), 0); -+ msix = max(pci_msix_vec_count(pdev), 0); -+ -+ return max(msi, msix); -+} -+ -+static int its_get_pci_alias(struct pci_dev *pdev, u16 alias, void *data) -+{ -+ struct its_pci_alias *dev_alias = data; -+ -+ dev_alias->dev_id = alias; -+ if (pdev != dev_alias->pdev) -+ dev_alias->count += its_pci_msi_vec_count(dev_alias->pdev); -+ -+ return 0; -+} -+ -+int __its_msi_prepare(struct irq_domain *domain, u32 dev_id, -+ struct device *dev, int nvec, msi_alloc_info_t *info) -+{ -+ struct its_node *its; -+ struct its_device *its_dev; -+ -+ its = domain->parent->host_data; -+ -+ its_dev = its_find_device(its, dev_id); -+ if (its_dev) { -+ /* -+ * We already have seen this ID, probably through -+ * another alias (PCI bridge of some sort). No need to -+ * create the device. -+ */ -+ dev_dbg(dev, "Reusing ITT for devID %x\n", dev_id); -+ goto out; -+ } -+ -+ its_dev = its_create_device(its, dev_id, nvec); -+ if (!its_dev) -+ return -ENOMEM; -+ -+ dev_dbg(dev, "ITT %d entries, %d bits\n", -+ nvec, ilog2(nvec)); -+out: -+ info->scratchpad[0].ptr = its_dev; -+ info->scratchpad[1].ptr = dev; -+ -+ return 0; -+} -+ -+static int its_msi_prepare(struct irq_domain *domain, struct device *dev, -+ int nvec, msi_alloc_info_t *info) -+{ -+ struct pci_dev *pdev; -+ struct its_pci_alias dev_alias; -+ u32 dev_id; -+ -+ if (!dev_is_pci(dev)) -+ return -EINVAL; -+ -+ pdev = to_pci_dev(dev); -+ dev_alias.pdev = pdev; -+ dev_alias.count = nvec; -+ -+ pci_for_each_dma_alias(pdev, its_get_pci_alias, &dev_alias); -+ -+ dev_dbg(dev, "ITT %d entries, %d bits\n", nvec, ilog2(nvec)); -+ dev_id = PCI_DEVID(pdev->bus->number, pdev->devfn); -+ return __its_msi_prepare(domain, dev_alias.dev_id, -+ dev, dev_alias.count, info); -+} -+ -+static struct msi_domain_ops its_pci_msi_ops = { -+ .msi_prepare = its_msi_prepare, -+}; -+ -+static struct msi_domain_info its_pci_msi_domain_info = { -+ .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | -+ MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX), -+ .ops = &its_pci_msi_ops, -+ .chip = &its_msi_irq_chip, -+}; -+ -+static int its_irq_gic_domain_alloc(struct irq_domain *domain, -+ unsigned int virq, -+ irq_hw_number_t hwirq) -+{ -+ struct of_phandle_args args; -+ -+ args.np = domain->parent->of_node; -+ args.args_count = 3; -+ args.args[0] = GIC_IRQ_TYPE_LPI; -+ args.args[1] = hwirq; -+ args.args[2] = IRQ_TYPE_EDGE_RISING; -+ -+ return irq_domain_alloc_irqs_parent(domain, virq, 1, &args); -+} -+ -+static int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, -+ unsigned int nr_irqs, void *args) -+{ -+ msi_alloc_info_t *info = args; -+ struct its_device *its_dev = info->scratchpad[0].ptr; -+ irq_hw_number_t hwirq; -+ int err; -+ int i; -+ -+ for (i = 0; i < nr_irqs; i++) { -+ err = its_alloc_device_irq(its_dev, &hwirq); -+ if (err) -+ return err; -+ -+ err = its_irq_gic_domain_alloc(domain, virq + i, hwirq); -+ if (err) -+ return err; -+ -+ irq_domain_set_hwirq_and_chip(domain, virq + i, -+ hwirq, &its_irq_chip, its_dev); -+ dev_dbg(info->scratchpad[1].ptr, "ID:%d pID:%d vID:%d\n", -+ (int)(hwirq - its_dev->event_map.lpi_base), -+ (int)hwirq, virq + i); -+ } -+ -+ return 0; -+} -+ -+static void its_irq_domain_activate(struct irq_domain *domain, -+ struct irq_data *d) -+{ -+ struct its_device *its_dev = irq_data_get_irq_chip_data(d); -+ u32 event = its_get_event_id(d); -+ -+ /* Bind the LPI to the first possible CPU */ -+ its_dev->event_map.col_map[event] = cpumask_first(cpu_online_mask); -+ -+ /* Map the GIC IRQ and event to the device */ -+ its_send_mapvi(its_dev, d->hwirq, event); -+} -+ -+static void its_irq_domain_deactivate(struct irq_domain *domain, -+ struct irq_data *d) -+{ -+ struct its_device *its_dev = irq_data_get_irq_chip_data(d); -+ u32 event = its_get_event_id(d); -+ -+ /* Stop the delivery of interrupts */ -+ its_send_discard(its_dev, event); -+} -+ -+static void its_irq_domain_free(struct irq_domain *domain, unsigned int virq, -+ unsigned int nr_irqs) -+{ -+ struct irq_data *d = irq_domain_get_irq_data(domain, virq); -+ struct its_device *its_dev = irq_data_get_irq_chip_data(d); -+ int i; -+ -+ for (i = 0; i < nr_irqs; i++) { -+ struct irq_data *data = irq_domain_get_irq_data(domain, -+ virq + i); -+ u32 event = its_get_event_id(data); -+ -+ /* Mark interrupt index as unused */ -+ clear_bit(event, its_dev->event_map.lpi_map); -+ -+ /* Nuke the entry in the domain */ -+ irq_domain_reset_irq_data(data); -+ } -+ -+ /* If all interrupts have been freed, start mopping the floor */ -+ if (bitmap_empty(its_dev->event_map.lpi_map, -+ its_dev->event_map.nr_lpis)) { -+ its_lpi_free(&its_dev->event_map); -+ -+ /* Unmap device/itt */ -+ its_send_mapd(its_dev, 0); -+ its_free_device(its_dev); -+ } -+ -+ irq_domain_free_irqs_parent(domain, virq, nr_irqs); -+} -+ -+static const struct irq_domain_ops its_domain_ops = { -+ .alloc = its_irq_domain_alloc, -+ .free = its_irq_domain_free, -+ .activate = its_irq_domain_activate, -+ .deactivate = its_irq_domain_deactivate, -+}; -+ -+static int its_force_quiescent(void __iomem *base) -+{ -+ u32 count = 1000000; /* 1s */ -+ u32 val; -+ -+ val = readl_relaxed(base + GITS_CTLR); -+ if (val & GITS_CTLR_QUIESCENT) -+ return 0; -+ -+ /* Disable the generation of all interrupts to this ITS */ -+ val &= ~GITS_CTLR_ENABLE; -+ writel_relaxed(val, base + GITS_CTLR); -+ -+ /* Poll GITS_CTLR and wait until ITS becomes quiescent */ -+ while (1) { -+ val = readl_relaxed(base + GITS_CTLR); -+ if (val & GITS_CTLR_QUIESCENT) -+ return 0; -+ -+ count--; -+ if (!count) -+ return -EBUSY; -+ -+ cpu_relax(); -+ udelay(1); -+ } -+} -+ -+static int its_probe(struct device_node *node, struct irq_domain *parent) -+{ -+ struct resource res; -+ struct its_node *its; -+ void __iomem *its_base; -+ u32 val; -+ u64 baser, tmp; -+ int err; -+ -+ err = of_address_to_resource(node, 0, &res); -+ if (err) { -+ pr_warn("%s: no regs?\n", node->full_name); -+ return -ENXIO; -+ } -+ -+ its_base = ioremap(res.start, resource_size(&res)); -+ if (!its_base) { -+ pr_warn("%s: unable to map registers\n", node->full_name); -+ return -ENOMEM; -+ } -+ -+ val = readl_relaxed(its_base + GITS_PIDR2) & GIC_PIDR2_ARCH_MASK; -+ if (val != 0x30 && val != 0x40) { -+ pr_warn("%s: no ITS detected, giving up\n", node->full_name); -+ err = -ENODEV; -+ goto out_unmap; -+ } -+ -+ err = its_force_quiescent(its_base); -+ if (err) { -+ pr_warn("%s: failed to quiesce, giving up\n", -+ node->full_name); -+ goto out_unmap; -+ } -+ -+ pr_info("ITS: %s\n", node->full_name); -+ -+ its = kzalloc(sizeof(*its), GFP_KERNEL); -+ if (!its) { -+ err = -ENOMEM; -+ goto out_unmap; -+ } -+ -+ raw_spin_lock_init(&its->lock); -+ INIT_LIST_HEAD(&its->entry); -+ INIT_LIST_HEAD(&its->its_device_list); -+ its->base = its_base; -+ its->phys_base = res.start; -+ its->msi_chip.of_node = node; -+ its->ite_size = ((readl_relaxed(its_base + GITS_TYPER) >> 4) & 0xf) + 1; -+ -+ its->cmd_base = kzalloc(ITS_CMD_QUEUE_SZ, GFP_KERNEL); -+ if (!its->cmd_base) { -+ err = -ENOMEM; -+ goto out_free_its; -+ } -+ its->cmd_write = its->cmd_base; -+ -+ err = its_alloc_tables(its); -+ if (err) -+ goto out_free_cmd; -+ -+ err = its_alloc_collections(its); -+ if (err) -+ goto out_free_tables; -+ -+ baser = (virt_to_phys(its->cmd_base) | -+ GITS_CBASER_WaWb | -+ GITS_CBASER_InnerShareable | -+ (ITS_CMD_QUEUE_SZ / SZ_4K - 1) | -+ GITS_CBASER_VALID); -+ -+ writeq_relaxed(baser, its->base + GITS_CBASER); -+ tmp = readq_relaxed(its->base + GITS_CBASER); -+ -+ if ((tmp ^ baser) & GITS_CBASER_SHAREABILITY_MASK) { -+ if (!(tmp & GITS_CBASER_SHAREABILITY_MASK)) { -+ /* -+ * The HW reports non-shareable, we must -+ * remove the cacheability attributes as -+ * well. -+ */ -+ baser &= ~(GITS_CBASER_SHAREABILITY_MASK | -+ GITS_CBASER_CACHEABILITY_MASK); -+ baser |= GITS_CBASER_nC; -+ writeq_relaxed(baser, its->base + GITS_CBASER); -+ } -+ pr_info("ITS: using cache flushing for cmd queue\n"); -+ its->flags |= ITS_FLAGS_CMDQ_NEEDS_FLUSHING; -+ } -+ -+ writeq_relaxed(0, its->base + GITS_CWRITER); -+ writel_relaxed(GITS_CTLR_ENABLE, its->base + GITS_CTLR); -+ -+ if (of_property_read_bool(its->msi_chip.of_node, "msi-controller")) { -+ its->domain = irq_domain_add_tree(node, &its_domain_ops, its); -+ if (!its->domain) { -+ err = -ENOMEM; -+ goto out_free_tables; -+ } -+ -+ its->domain->parent = parent; -+ its->domain->bus_token = DOMAIN_BUS_NEXUS; -+ -+ its->msi_chip.domain = pci_msi_create_irq_domain(node, -+ &its_pci_msi_domain_info, -+ its->domain); -+ if (!its->msi_chip.domain) { -+ err = -ENOMEM; -+ goto out_free_domains; -+ } -+ -+ err = of_pci_msi_chip_add(&its->msi_chip); -+ if (err) -+ goto out_free_domains; -+ } -+ -+ spin_lock(&its_lock); -+ list_add(&its->entry, &its_nodes); -+ spin_unlock(&its_lock); -+ -+ return 0; -+ -+out_free_domains: -+ if (its->msi_chip.domain) -+ irq_domain_remove(its->msi_chip.domain); -+ if (its->domain) -+ irq_domain_remove(its->domain); -+out_free_tables: -+ its_free_tables(its); -+out_free_cmd: -+ kfree(its->cmd_base); -+out_free_its: -+ kfree(its); -+out_unmap: -+ iounmap(its_base); -+ pr_err("ITS: failed probing %s (%d)\n", node->full_name, err); -+ return err; -+} -+ -+static bool gic_rdists_supports_plpis(void) -+{ -+ return !!(readl_relaxed(gic_data_rdist_rd_base() + GICR_TYPER) & GICR_TYPER_PLPIS); -+} -+ -+int its_cpu_init(void) -+{ -+ if (!list_empty(&its_nodes)) { -+ if (!gic_rdists_supports_plpis()) { -+ pr_info("CPU%d: LPIs not supported\n", smp_processor_id()); -+ return -ENXIO; -+ } -+ its_cpu_init_lpis(); -+ its_cpu_init_collection(); -+ } -+ -+ return 0; -+} -+ -+static struct of_device_id its_device_id[] = { -+ { .compatible = "arm,gic-v3-its", }, -+ {}, -+}; -+ -+int its_init(struct device_node *node, struct rdists *rdists, -+ struct irq_domain *parent_domain) -+{ -+ struct device_node *np; -+ -+ for (np = of_find_matching_node(node, its_device_id); np; -+ np = of_find_matching_node(np, its_device_id)) { -+ its_probe(np, parent_domain); -+ } -+ -+ if (list_empty(&its_nodes)) { -+ pr_warn("ITS: No ITS available, not enabling LPIs\n"); -+ return -ENXIO; -+ } -+ -+ gic_rdists = rdists; -+ gic_root_node = node; -+ -+ its_alloc_lpi_tables(); -+ its_lpi_init(rdists->id_bits); -+ -+ return 0; -+} -diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c -index aa17ae8..fd8850d 100644 ---- a/drivers/irqchip/irq-gic-v3.c -+++ b/drivers/irqchip/irq-gic-v3.c -@@ -34,20 +34,25 @@ - #include "irq-gic-common.h" - #include "irqchip.h" - -+struct redist_region { -+ void __iomem *redist_base; -+ phys_addr_t phys_base; -+}; -+ - struct gic_chip_data { - void __iomem *dist_base; -- void __iomem **redist_base; -- void __iomem * __percpu *rdist; -+ struct redist_region *redist_regions; -+ struct rdists rdists; - struct irq_domain *domain; - u64 redist_stride; -- u32 redist_regions; -+ u32 nr_redist_regions; - unsigned int irq_nr; - }; - - static struct gic_chip_data gic_data __read_mostly; - --#define gic_data_rdist() (this_cpu_ptr(gic_data.rdist)) --#define gic_data_rdist_rd_base() (*gic_data_rdist()) -+#define gic_data_rdist() (this_cpu_ptr(gic_data.rdists.rdist)) -+#define gic_data_rdist_rd_base() (gic_data_rdist()->rd_base) - #define gic_data_rdist_sgi_base() (gic_data_rdist_rd_base() + SZ_64K) - - /* Our default, arbitrary priority value. Linux only uses one anyway. */ -@@ -71,9 +76,6 @@ static inline void __iomem *gic_dist_base(struct irq_data *d) - if (d->hwirq <= 1023) /* SPI -> dist_base */ - return gic_data.dist_base; - -- if (d->hwirq >= 8192) -- BUG(); /* LPI Detected!!! */ -- - return NULL; - } - -@@ -236,7 +238,9 @@ static int gic_set_type(struct irq_data *d, unsigned int type) - if (irq < 16) - return -EINVAL; - -- if (type != IRQ_TYPE_LEVEL_HIGH && type != IRQ_TYPE_EDGE_RISING) -+ /* SPIs have restrictions on the supported types */ -+ if (irq >= 32 && type != IRQ_TYPE_LEVEL_HIGH && -+ type != IRQ_TYPE_EDGE_RISING) - return -EINVAL; - - if (gic_irq_in_rdist(d)) { -@@ -247,9 +251,7 @@ static int gic_set_type(struct irq_data *d, unsigned int type) - rwp_wait = gic_dist_wait_for_rwp; - } - -- gic_configure_irq(irq, type, base, rwp_wait); -- -- return 0; -+ return gic_configure_irq(irq, type, base, rwp_wait); - } - - static u64 gic_mpidr_to_affinity(u64 mpidr) -@@ -271,11 +273,11 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs - do { - irqnr = gic_read_iar(); - -- if (likely(irqnr > 15 && irqnr < 1020)) { -+ if (likely(irqnr > 15 && irqnr < 1020) || irqnr >= 8192) { - int err; - err = handle_domain_irq(gic_data.domain, irqnr, regs); - if (err) { -- WARN_ONCE(true, "Unexpected SPI received!\n"); -+ WARN_ONCE(true, "Unexpected interrupt received!\n"); - gic_write_eoir(irqnr); - } - continue; -@@ -333,8 +335,8 @@ static int gic_populate_rdist(void) - MPIDR_AFFINITY_LEVEL(mpidr, 1) << 8 | - MPIDR_AFFINITY_LEVEL(mpidr, 0)); - -- for (i = 0; i < gic_data.redist_regions; i++) { -- void __iomem *ptr = gic_data.redist_base[i]; -+ for (i = 0; i < gic_data.nr_redist_regions; i++) { -+ void __iomem *ptr = gic_data.redist_regions[i].redist_base; - u32 reg; - - reg = readl_relaxed(ptr + GICR_PIDR2) & GIC_PIDR2_ARCH_MASK; -@@ -347,10 +349,13 @@ static int gic_populate_rdist(void) - do { - typer = readq_relaxed(ptr + GICR_TYPER); - if ((typer >> 32) == aff) { -+ u64 offset = ptr - gic_data.redist_regions[i].redist_base; - gic_data_rdist_rd_base() = ptr; -- pr_info("CPU%d: found redistributor %llx @%p\n", -+ gic_data_rdist()->phys_base = gic_data.redist_regions[i].phys_base + offset; -+ pr_info("CPU%d: found redistributor %llx region %d:%pa\n", - smp_processor_id(), -- (unsigned long long)mpidr, ptr); -+ (unsigned long long)mpidr, -+ i, &gic_data_rdist()->phys_base); - return 0; - } - -@@ -385,6 +390,11 @@ static void gic_cpu_sys_reg_init(void) - gic_write_grpen1(1); - } - -+static int gic_dist_supports_lpis(void) -+{ -+ return !!(readl_relaxed(gic_data.dist_base + GICD_TYPER) & GICD_TYPER_LPIS); -+} -+ - static void gic_cpu_init(void) - { - void __iomem *rbase; -@@ -399,6 +409,10 @@ static void gic_cpu_init(void) - - gic_cpu_config(rbase, gic_redist_wait_for_rwp); - -+ /* Give LPIs a spin */ -+ if (IS_ENABLED(CONFIG_ARM_GIC_V3_ITS) && gic_dist_supports_lpis()) -+ its_cpu_init(); -+ - /* initialise system registers */ - gic_cpu_sys_reg_init(); - } -@@ -452,7 +466,7 @@ static u16 gic_compute_target_list(int *base_cpu, const struct cpumask *mask, - tlist |= 1 << (mpidr & 0xf); - - cpu = cpumask_next(cpu, mask); -- if (cpu == nr_cpu_ids) -+ if (cpu >= nr_cpu_ids) - goto out; - - mpidr = cpu_logical_map(cpu); -@@ -467,15 +481,19 @@ out: - return tlist; - } - -+#define MPIDR_TO_SGI_AFFINITY(cluster_id, level) \ -+ (MPIDR_AFFINITY_LEVEL(cluster_id, level) \ -+ << ICC_SGI1R_AFFINITY_## level ##_SHIFT) -+ - static void gic_send_sgi(u64 cluster_id, u16 tlist, unsigned int irq) - { - u64 val; - -- val = (MPIDR_AFFINITY_LEVEL(cluster_id, 3) << 48 | -- MPIDR_AFFINITY_LEVEL(cluster_id, 2) << 32 | -- irq << 24 | -- MPIDR_AFFINITY_LEVEL(cluster_id, 1) << 16 | -- tlist); -+ val = (MPIDR_TO_SGI_AFFINITY(cluster_id, 3) | -+ MPIDR_TO_SGI_AFFINITY(cluster_id, 2) | -+ irq << ICC_SGI1R_SGI_ID_SHIFT | -+ MPIDR_TO_SGI_AFFINITY(cluster_id, 1) | -+ tlist << ICC_SGI1R_TARGET_LIST_SHIFT); - - pr_debug("CPU%d: ICC_SGI1R_EL1 %llx\n", smp_processor_id(), val); - gic_write_sgi1r(val); -@@ -585,26 +603,43 @@ static struct irq_chip gic_chip = { - .irq_set_affinity = gic_set_affinity, - }; - -+#define GIC_ID_NR (1U << gic_data.rdists.id_bits) -+ - static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, - irq_hw_number_t hw) - { - /* SGIs are private to the core kernel */ - if (hw < 16) - return -EPERM; -+ /* Nothing here */ -+ if (hw >= gic_data.irq_nr && hw < 8192) -+ return -EPERM; -+ /* Off limits */ -+ if (hw >= GIC_ID_NR) -+ return -EPERM; -+ - /* PPIs */ - if (hw < 32) { - irq_set_percpu_devid(irq); -- irq_set_chip_and_handler(irq, &gic_chip, -- handle_percpu_devid_irq); -+ irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data, -+ handle_percpu_devid_irq, NULL, NULL); - set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN); - } - /* SPIs */ - if (hw >= 32 && hw < gic_data.irq_nr) { -- irq_set_chip_and_handler(irq, &gic_chip, -- handle_fasteoi_irq); -+ irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data, -+ handle_fasteoi_irq, NULL, NULL); - set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); - } -- irq_set_chip_data(irq, d->host_data); -+ /* LPIs */ -+ if (hw >= 8192 && hw < GIC_ID_NR) { -+ if (!gic_dist_supports_lpis()) -+ return -EPERM; -+ irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data, -+ handle_fasteoi_irq, NULL, NULL); -+ set_irq_flags(irq, IRQF_VALID); -+ } -+ - return 0; - } - -@@ -625,6 +660,9 @@ static int gic_irq_domain_xlate(struct irq_domain *d, - case 1: /* PPI */ - *out_hwirq = intspec[1] + 16; - break; -+ case GIC_IRQ_TYPE_LPI: /* LPI */ -+ *out_hwirq = intspec[1]; -+ break; - default: - return -EINVAL; - } -@@ -633,17 +671,50 @@ static int gic_irq_domain_xlate(struct irq_domain *d, - return 0; - } - -+static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, -+ unsigned int nr_irqs, void *arg) -+{ -+ int i, ret; -+ irq_hw_number_t hwirq; -+ unsigned int type = IRQ_TYPE_NONE; -+ struct of_phandle_args *irq_data = arg; -+ -+ ret = gic_irq_domain_xlate(domain, irq_data->np, irq_data->args, -+ irq_data->args_count, &hwirq, &type); -+ if (ret) -+ return ret; -+ -+ for (i = 0; i < nr_irqs; i++) -+ gic_irq_domain_map(domain, virq + i, hwirq + i); -+ -+ return 0; -+} -+ -+static void gic_irq_domain_free(struct irq_domain *domain, unsigned int virq, -+ unsigned int nr_irqs) -+{ -+ int i; -+ -+ for (i = 0; i < nr_irqs; i++) { -+ struct irq_data *d = irq_domain_get_irq_data(domain, virq + i); -+ irq_set_handler(virq + i, NULL); -+ irq_domain_reset_irq_data(d); -+ } -+} -+ - static const struct irq_domain_ops gic_irq_domain_ops = { -- .map = gic_irq_domain_map, - .xlate = gic_irq_domain_xlate, -+ .alloc = gic_irq_domain_alloc, -+ .free = gic_irq_domain_free, - }; - - static int __init gic_of_init(struct device_node *node, struct device_node *parent) - { - void __iomem *dist_base; -- void __iomem **redist_base; -+ struct redist_region *rdist_regs; - u64 redist_stride; -- u32 redist_regions; -+ u32 nr_redist_regions; -+ u32 typer; - u32 reg; - int gic_irqs; - int err; -@@ -664,54 +735,63 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare - goto out_unmap_dist; - } - -- if (of_property_read_u32(node, "#redistributor-regions", &redist_regions)) -- redist_regions = 1; -+ if (of_property_read_u32(node, "#redistributor-regions", &nr_redist_regions)) -+ nr_redist_regions = 1; - -- redist_base = kzalloc(sizeof(*redist_base) * redist_regions, GFP_KERNEL); -- if (!redist_base) { -+ rdist_regs = kzalloc(sizeof(*rdist_regs) * nr_redist_regions, GFP_KERNEL); -+ if (!rdist_regs) { - err = -ENOMEM; - goto out_unmap_dist; - } - -- for (i = 0; i < redist_regions; i++) { -- redist_base[i] = of_iomap(node, 1 + i); -- if (!redist_base[i]) { -+ for (i = 0; i < nr_redist_regions; i++) { -+ struct resource res; -+ int ret; -+ -+ ret = of_address_to_resource(node, 1 + i, &res); -+ rdist_regs[i].redist_base = of_iomap(node, 1 + i); -+ if (ret || !rdist_regs[i].redist_base) { - pr_err("%s: couldn't map region %d\n", - node->full_name, i); - err = -ENODEV; - goto out_unmap_rdist; - } -+ rdist_regs[i].phys_base = res.start; - } - - if (of_property_read_u64(node, "redistributor-stride", &redist_stride)) - redist_stride = 0; - - gic_data.dist_base = dist_base; -- gic_data.redist_base = redist_base; -- gic_data.redist_regions = redist_regions; -+ gic_data.redist_regions = rdist_regs; -+ gic_data.nr_redist_regions = nr_redist_regions; - gic_data.redist_stride = redist_stride; - - /* - * Find out how many interrupts are supported. - * The GIC only supports up to 1020 interrupt sources (SGI+PPI+SPI) - */ -- gic_irqs = readl_relaxed(gic_data.dist_base + GICD_TYPER) & 0x1f; -- gic_irqs = (gic_irqs + 1) * 32; -+ typer = readl_relaxed(gic_data.dist_base + GICD_TYPER); -+ gic_data.rdists.id_bits = GICD_TYPER_ID_BITS(typer); -+ gic_irqs = GICD_TYPER_IRQS(typer); - if (gic_irqs > 1020) - gic_irqs = 1020; - gic_data.irq_nr = gic_irqs; - - gic_data.domain = irq_domain_add_tree(node, &gic_irq_domain_ops, - &gic_data); -- gic_data.rdist = alloc_percpu(typeof(*gic_data.rdist)); -+ gic_data.rdists.rdist = alloc_percpu(typeof(*gic_data.rdists.rdist)); - -- if (WARN_ON(!gic_data.domain) || WARN_ON(!gic_data.rdist)) { -+ if (WARN_ON(!gic_data.domain) || WARN_ON(!gic_data.rdists.rdist)) { - err = -ENOMEM; - goto out_free; - } - - set_handle_irq(gic_handle_irq); - -+ if (IS_ENABLED(CONFIG_ARM_GIC_V3_ITS) && gic_dist_supports_lpis()) -+ its_init(node, &gic_data.rdists, gic_data.domain); -+ - gic_smp_init(); - gic_dist_init(); - gic_cpu_init(); -@@ -722,12 +802,12 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare - out_free: - if (gic_data.domain) - irq_domain_remove(gic_data.domain); -- free_percpu(gic_data.rdist); -+ free_percpu(gic_data.rdists.rdist); - out_unmap_rdist: -- for (i = 0; i < redist_regions; i++) -- if (redist_base[i]) -- iounmap(redist_base[i]); -- kfree(redist_base); -+ for (i = 0; i < nr_redist_regions; i++) -+ if (rdist_regs[i].redist_base) -+ iounmap(rdist_regs[i].redist_base); -+ kfree(rdist_regs); - out_unmap_dist: - iounmap(dist_base); - return err; -diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c -index 38493ff..ab0b1cb 100644 ---- a/drivers/irqchip/irq-gic.c -+++ b/drivers/irqchip/irq-gic.c -@@ -188,12 +188,15 @@ static int gic_set_type(struct irq_data *d, unsigned int type) - { - void __iomem *base = gic_dist_base(d); - unsigned int gicirq = gic_irq(d); -+ int ret; - - /* Interrupt configuration for SGIs can't be changed */ - if (gicirq < 16) - return -EINVAL; - -- if (type != IRQ_TYPE_LEVEL_HIGH && type != IRQ_TYPE_EDGE_RISING) -+ /* SPIs have restrictions on the supported types */ -+ if (gicirq >= 32 && type != IRQ_TYPE_LEVEL_HIGH && -+ type != IRQ_TYPE_EDGE_RISING) - return -EINVAL; - - raw_spin_lock(&irq_controller_lock); -@@ -201,11 +204,11 @@ static int gic_set_type(struct irq_data *d, unsigned int type) - if (gic_arch_extn.irq_set_type) - gic_arch_extn.irq_set_type(d, type); - -- gic_configure_irq(gicirq, type, base, NULL); -+ ret = gic_configure_irq(gicirq, type, base, NULL); - - raw_spin_unlock(&irq_controller_lock); - -- return 0; -+ return ret; - } - - static int gic_retrigger(struct irq_data *d) -@@ -788,17 +791,16 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, - { - if (hw < 32) { - irq_set_percpu_devid(irq); -- irq_set_chip_and_handler(irq, &gic_chip, -- handle_percpu_devid_irq); -+ irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data, -+ handle_percpu_devid_irq, NULL, NULL); - set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN); - } else { -- irq_set_chip_and_handler(irq, &gic_chip, -- handle_fasteoi_irq); -+ irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data, -+ handle_fasteoi_irq, NULL, NULL); - set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); - - gic_routable_irq_domain_ops->map(d, irq, hw); - } -- irq_set_chip_data(irq, d->host_data); - return 0; - } - -@@ -858,6 +860,31 @@ static struct notifier_block gic_cpu_notifier = { - }; - #endif - -+static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, -+ unsigned int nr_irqs, void *arg) -+{ -+ int i, ret; -+ irq_hw_number_t hwirq; -+ unsigned int type = IRQ_TYPE_NONE; -+ struct of_phandle_args *irq_data = arg; -+ -+ ret = gic_irq_domain_xlate(domain, irq_data->np, irq_data->args, -+ irq_data->args_count, &hwirq, &type); -+ if (ret) -+ return ret; -+ -+ for (i = 0; i < nr_irqs; i++) -+ gic_irq_domain_map(domain, virq + i, hwirq + i); -+ -+ return 0; -+} -+ -+static const struct irq_domain_ops gic_irq_domain_hierarchy_ops = { -+ .xlate = gic_irq_domain_xlate, -+ .alloc = gic_irq_domain_alloc, -+ .free = irq_domain_free_irqs_top, -+}; -+ - static const struct irq_domain_ops gic_irq_domain_ops = { - .map = gic_irq_domain_map, - .unmap = gic_irq_domain_unmap, -@@ -948,18 +975,6 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, - gic_cpu_map[i] = 0xff; - - /* -- * For primary GICs, skip over SGIs. -- * For secondary GICs, skip over PPIs, too. -- */ -- if (gic_nr == 0 && (irq_start & 31) > 0) { -- hwirq_base = 16; -- if (irq_start != -1) -- irq_start = (irq_start & ~31) + 16; -- } else { -- hwirq_base = 32; -- } -- -- /* - * Find out how many interrupts are supported. - * The GIC only supports up to 1020 interrupt sources. - */ -@@ -969,10 +984,31 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, - gic_irqs = 1020; - gic->gic_irqs = gic_irqs; - -- gic_irqs -= hwirq_base; /* calculate # of irqs to allocate */ -+ if (node) { /* DT case */ -+ const struct irq_domain_ops *ops = &gic_irq_domain_hierarchy_ops; -+ -+ if (!of_property_read_u32(node, "arm,routable-irqs", -+ &nr_routable_irqs)) { -+ ops = &gic_irq_domain_ops; -+ gic_irqs = nr_routable_irqs; -+ } -+ -+ gic->domain = irq_domain_add_linear(node, gic_irqs, ops, gic); -+ } else { /* Non-DT case */ -+ /* -+ * For primary GICs, skip over SGIs. -+ * For secondary GICs, skip over PPIs, too. -+ */ -+ if (gic_nr == 0 && (irq_start & 31) > 0) { -+ hwirq_base = 16; -+ if (irq_start != -1) -+ irq_start = (irq_start & ~31) + 16; -+ } else { -+ hwirq_base = 32; -+ } -+ -+ gic_irqs -= hwirq_base; /* calculate # of irqs to allocate */ - -- if (of_property_read_u32(node, "arm,routable-irqs", -- &nr_routable_irqs)) { - irq_base = irq_alloc_descs(irq_start, 16, gic_irqs, - numa_node_id()); - if (IS_ERR_VALUE(irq_base)) { -@@ -983,10 +1019,6 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, - - gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base, - hwirq_base, &gic_irq_domain_ops, gic); -- } else { -- gic->domain = irq_domain_add_linear(node, nr_routable_irqs, -- &gic_irq_domain_ops, -- gic); - } - - if (WARN_ON(!gic->domain)) -@@ -1037,6 +1069,10 @@ gic_of_init(struct device_node *node, struct device_node *parent) - irq = irq_of_parse_and_map(node, 0); - gic_cascade_irq(gic_cnt, irq); - } -+ -+ if (IS_ENABLED(CONFIG_ARM_GIC_V2M)) -+ gicv2m_of_init(node, gic_data[gic_cnt].domain); -+ - gic_cnt++; - return 0; - } -diff --git a/drivers/irqchip/irq-hip04.c b/drivers/irqchip/irq-hip04.c -index 9c8f833..5507a0c 100644 ---- a/drivers/irqchip/irq-hip04.c -+++ b/drivers/irqchip/irq-hip04.c -@@ -120,21 +120,24 @@ static int hip04_irq_set_type(struct irq_data *d, unsigned int type) - { - void __iomem *base = hip04_dist_base(d); - unsigned int irq = hip04_irq(d); -+ int ret; - - /* Interrupt configuration for SGIs can't be changed */ - if (irq < 16) - return -EINVAL; - -- if (type != IRQ_TYPE_LEVEL_HIGH && type != IRQ_TYPE_EDGE_RISING) -+ /* SPIs have restrictions on the supported types */ -+ if (irq >= 32 && type != IRQ_TYPE_LEVEL_HIGH && -+ type != IRQ_TYPE_EDGE_RISING) - return -EINVAL; - - raw_spin_lock(&irq_controller_lock); - -- gic_configure_irq(irq, type, base, NULL); -+ ret = gic_configure_irq(irq, type, base, NULL); - - raw_spin_unlock(&irq_controller_lock); - -- return 0; -+ return ret; - } - - #ifdef CONFIG_SMP -diff --git a/drivers/irqchip/irq-sunxi-nmi.c b/drivers/irqchip/irq-sunxi-nmi.c -index eb9b59e..6b2b582 100644 ---- a/drivers/irqchip/irq-sunxi-nmi.c -+++ b/drivers/irqchip/irq-sunxi-nmi.c -@@ -50,12 +50,12 @@ static struct sunxi_sc_nmi_reg_offs sun6i_reg_offs = { - static inline void sunxi_sc_nmi_write(struct irq_chip_generic *gc, u32 off, - u32 val) - { -- irq_reg_writel(val, gc->reg_base + off); -+ irq_reg_writel(gc, val, off); - } - - static inline u32 sunxi_sc_nmi_read(struct irq_chip_generic *gc, u32 off) - { -- return irq_reg_readl(gc->reg_base + off); -+ return irq_reg_readl(gc, off); - } - - static void sunxi_sc_nmi_handle_irq(unsigned int irq, struct irq_desc *desc) -diff --git a/drivers/irqchip/irq-tb10x.c b/drivers/irqchip/irq-tb10x.c -index 7c44c99..accc200 100644 ---- a/drivers/irqchip/irq-tb10x.c -+++ b/drivers/irqchip/irq-tb10x.c -@@ -43,12 +43,12 @@ - static inline void ab_irqctl_writereg(struct irq_chip_generic *gc, u32 reg, - u32 val) - { -- irq_reg_writel(val, gc->reg_base + reg); -+ irq_reg_writel(gc, val, reg); - } - - static inline u32 ab_irqctl_readreg(struct irq_chip_generic *gc, u32 reg) - { -- return irq_reg_readl(gc->reg_base + reg); -+ return irq_reg_readl(gc, reg); - } - - static int tb10x_irq_set_type(struct irq_data *data, unsigned int flow_type) -diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig -index 6d91c27..d6af99f 100644 ---- a/drivers/memory/Kconfig -+++ b/drivers/memory/Kconfig -@@ -83,6 +83,6 @@ config FSL_CORENET_CF - - config FSL_IFC - bool -- depends on FSL_SOC -+ depends on FSL_SOC || ARCH_LAYERSCAPE - - endif -diff --git a/drivers/memory/fsl_ifc.c b/drivers/memory/fsl_ifc.c -index 3d5d792..1b182b1 100644 ---- a/drivers/memory/fsl_ifc.c -+++ b/drivers/memory/fsl_ifc.c -@@ -22,6 +22,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -30,7 +31,9 @@ - #include - #include - #include --#include -+#include -+#include -+#include - - struct fsl_ifc_ctrl *fsl_ifc_ctrl_dev; - EXPORT_SYMBOL(fsl_ifc_ctrl_dev); -@@ -58,11 +61,11 @@ int fsl_ifc_find(phys_addr_t addr_base) - { - int i = 0; - -- if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->regs) -+ if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->gregs) - return -ENODEV; - -- for (i = 0; i < ARRAY_SIZE(fsl_ifc_ctrl_dev->regs->cspr_cs); i++) { -- u32 cspr = in_be32(&fsl_ifc_ctrl_dev->regs->cspr_cs[i].cspr); -+ for (i = 0; i < fsl_ifc_ctrl_dev->banks; i++) { -+ u32 cspr = ifc_in32(&fsl_ifc_ctrl_dev->gregs->cspr_cs[i].cspr); - if (cspr & CSPR_V && (cspr & CSPR_BA) == - convert_ifc_address(addr_base)) - return i; -@@ -74,21 +77,21 @@ EXPORT_SYMBOL(fsl_ifc_find); - - static int fsl_ifc_ctrl_init(struct fsl_ifc_ctrl *ctrl) - { -- struct fsl_ifc_regs __iomem *ifc = ctrl->regs; -+ struct fsl_ifc_global __iomem *ifc = ctrl->gregs; - - /* - * Clear all the common status and event registers - */ -- if (in_be32(&ifc->cm_evter_stat) & IFC_CM_EVTER_STAT_CSER) -- out_be32(&ifc->cm_evter_stat, IFC_CM_EVTER_STAT_CSER); -+ if (ifc_in32(&ifc->cm_evter_stat) & IFC_CM_EVTER_STAT_CSER) -+ ifc_out32(IFC_CM_EVTER_STAT_CSER, &ifc->cm_evter_stat); - - /* enable all error and events */ -- out_be32(&ifc->cm_evter_en, IFC_CM_EVTER_EN_CSEREN); -+ ifc_out32(IFC_CM_EVTER_EN_CSEREN, &ifc->cm_evter_en); - - /* enable all error and event interrupts */ -- out_be32(&ifc->cm_evter_intr_en, IFC_CM_EVTER_INTR_EN_CSERIREN); -- out_be32(&ifc->cm_erattr0, 0x0); -- out_be32(&ifc->cm_erattr1, 0x0); -+ ifc_out32(IFC_CM_EVTER_INTR_EN_CSERIREN, &ifc->cm_evter_intr_en); -+ ifc_out32(0x0, &ifc->cm_erattr0); -+ ifc_out32(0x0, &ifc->cm_erattr1); - - return 0; - } -@@ -103,7 +106,7 @@ static int fsl_ifc_ctrl_remove(struct platform_device *dev) - irq_dispose_mapping(ctrl->nand_irq); - irq_dispose_mapping(ctrl->irq); - -- iounmap(ctrl->regs); -+ iounmap(ctrl->gregs); - - dev_set_drvdata(&dev->dev, NULL); - kfree(ctrl); -@@ -121,15 +124,15 @@ static DEFINE_SPINLOCK(nand_irq_lock); - - static u32 check_nand_stat(struct fsl_ifc_ctrl *ctrl) - { -- struct fsl_ifc_regs __iomem *ifc = ctrl->regs; -+ struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; - unsigned long flags; - u32 stat; - - spin_lock_irqsave(&nand_irq_lock, flags); - -- stat = in_be32(&ifc->ifc_nand.nand_evter_stat); -+ stat = ifc_in32(&ifc->ifc_nand.nand_evter_stat); - if (stat) { -- out_be32(&ifc->ifc_nand.nand_evter_stat, stat); -+ ifc_out32(stat, &ifc->ifc_nand.nand_evter_stat); - ctrl->nand_stat = stat; - wake_up(&ctrl->nand_wait); - } -@@ -156,21 +159,21 @@ static irqreturn_t fsl_ifc_nand_irq(int irqno, void *data) - static irqreturn_t fsl_ifc_ctrl_irq(int irqno, void *data) - { - struct fsl_ifc_ctrl *ctrl = data; -- struct fsl_ifc_regs __iomem *ifc = ctrl->regs; -+ struct fsl_ifc_global __iomem *ifc = ctrl->gregs; - u32 err_axiid, err_srcid, status, cs_err, err_addr; - irqreturn_t ret = IRQ_NONE; - - /* read for chip select error */ -- cs_err = in_be32(&ifc->cm_evter_stat); -+ cs_err = ifc_in32(&ifc->cm_evter_stat); - if (cs_err) { - dev_err(ctrl->dev, "transaction sent to IFC is not mapped to" - "any memory bank 0x%08X\n", cs_err); - /* clear the chip select error */ -- out_be32(&ifc->cm_evter_stat, IFC_CM_EVTER_STAT_CSER); -+ ifc_out32(IFC_CM_EVTER_STAT_CSER, &ifc->cm_evter_stat); - - /* read error attribute registers print the error information */ -- status = in_be32(&ifc->cm_erattr0); -- err_addr = in_be32(&ifc->cm_erattr1); -+ status = ifc_in32(&ifc->cm_erattr0); -+ err_addr = ifc_in32(&ifc->cm_erattr1); - - if (status & IFC_CM_ERATTR0_ERTYP_READ) - dev_err(ctrl->dev, "Read transaction error" -@@ -213,7 +216,8 @@ static irqreturn_t fsl_ifc_ctrl_irq(int irqno, void *data) - static int fsl_ifc_ctrl_probe(struct platform_device *dev) - { - int ret = 0; -- -+ int version, banks; -+ void __iomem *addr; - - dev_info(&dev->dev, "Freescale Integrated Flash Controller\n"); - -@@ -224,16 +228,41 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev) - dev_set_drvdata(&dev->dev, fsl_ifc_ctrl_dev); - - /* IOMAP the entire IFC region */ -- fsl_ifc_ctrl_dev->regs = of_iomap(dev->dev.of_node, 0); -- if (!fsl_ifc_ctrl_dev->regs) { -+ fsl_ifc_ctrl_dev->gregs = of_iomap(dev->dev.of_node, 0); -+ if (!fsl_ifc_ctrl_dev->gregs) { - dev_err(&dev->dev, "failed to get memory region\n"); - ret = -ENODEV; - goto err; - } - -+ if (of_property_read_bool(dev->dev.of_node, "little-endian")) { -+ fsl_ifc_ctrl_dev->little_endian = true; -+ dev_dbg(&dev->dev, "IFC REGISTERS are LITTLE endian\n"); -+ } else { -+ fsl_ifc_ctrl_dev->little_endian = false; -+ dev_dbg(&dev->dev, "IFC REGISTERS are BIG endian\n"); -+ } -+ -+ version = ifc_in32(&fsl_ifc_ctrl_dev->gregs->ifc_rev) & -+ FSL_IFC_VERSION_MASK; -+ -+ banks = (version == FSL_IFC_VERSION_1_0_0) ? 4 : 8; -+ dev_info(&dev->dev, "IFC version %d.%d, %d banks\n", -+ version >> 24, (version >> 16) & 0xf, banks); -+ -+ fsl_ifc_ctrl_dev->version = version; -+ fsl_ifc_ctrl_dev->banks = banks; -+ -+ addr = fsl_ifc_ctrl_dev->gregs; -+ if (version >= FSL_IFC_VERSION_2_0_0) -+ addr += PGOFFSET_64K; -+ else -+ addr += PGOFFSET_4K; -+ fsl_ifc_ctrl_dev->rregs = addr; -+ - /* get the Controller level irq */ - fsl_ifc_ctrl_dev->irq = irq_of_parse_and_map(dev->dev.of_node, 0); -- if (fsl_ifc_ctrl_dev->irq == NO_IRQ) { -+ if (fsl_ifc_ctrl_dev->irq == 0) { - dev_err(&dev->dev, "failed to get irq resource " - "for IFC\n"); - ret = -ENODEV; -diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c -index 9e21e4f..8f43ab8 100644 ---- a/drivers/mfd/vexpress-sysreg.c -+++ b/drivers/mfd/vexpress-sysreg.c -@@ -223,7 +223,7 @@ static int vexpress_sysreg_probe(struct platform_device *pdev) - vexpress_config_set_master(vexpress_sysreg_get_master()); - - /* Confirm board type against DT property, if available */ -- if (of_property_read_u32(of_allnodes, "arm,hbi", &dt_hbi) == 0) { -+ if (of_property_read_u32(of_root, "arm,hbi", &dt_hbi) == 0) { - u32 id = vexpress_get_procid(VEXPRESS_SITE_MASTER); - u32 hbi = (id >> SYS_PROCIDx_HBI_SHIFT) & SYS_HBI_MASK; - -diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c -index 10ecc0a..d356dbc 100644 ---- a/drivers/mmc/card/block.c -+++ b/drivers/mmc/card/block.c -@@ -2402,6 +2402,10 @@ static const struct mmc_fixup blk_fixups[] = - * - * N.B. This doesn't affect SD cards. - */ -+ MMC_FIXUP("SDMB-32", CID_MANFID_SANDISK, CID_OEMID_ANY, add_quirk_mmc, -+ MMC_QUIRK_BLK_NO_CMD23), -+ MMC_FIXUP("SDM032", CID_MANFID_SANDISK, CID_OEMID_ANY, add_quirk_mmc, -+ MMC_QUIRK_BLK_NO_CMD23), - MMC_FIXUP("MMC08G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc, - MMC_QUIRK_BLK_NO_CMD23), - MMC_FIXUP("MMC16G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc, -diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig -index 1386065..b8c9b73 100644 ---- a/drivers/mmc/host/Kconfig -+++ b/drivers/mmc/host/Kconfig -@@ -66,7 +66,7 @@ config MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER - has the effect of scrambling the addresses and formats of data - accessed in sizes other than the datum size. - -- This is the case for the Freescale eSDHC and Nintendo Wii SDHCI. -+ This is the case for the Nintendo Wii SDHCI. - - config MMC_SDHCI_PCI - tristate "SDHCI support on PCI bus" -@@ -130,8 +130,10 @@ config MMC_SDHCI_OF_ARASAN - config MMC_SDHCI_OF_ESDHC - tristate "SDHCI OF support for the Freescale eSDHC controller" - depends on MMC_SDHCI_PLTFM -- depends on PPC_OF -- select MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER -+ depends on PPC || ARCH_MXC || ARCH_LAYERSCAPE -+ select MMC_SDHCI_IO_ACCESSORS -+ select FSL_SOC_DRIVERS -+ select FSL_GUTS - help - This selects the Freescale eSDHC controller support. - -@@ -142,7 +144,7 @@ config MMC_SDHCI_OF_ESDHC - config MMC_SDHCI_OF_HLWD - tristate "SDHCI OF support for the Nintendo Wii SDHCI controllers" - depends on MMC_SDHCI_PLTFM -- depends on PPC_OF -+ depends on PPC - select MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER - help - This selects the Secure Digital Host Controller Interface (SDHCI) -diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h -index a870c42..f2baede 100644 ---- a/drivers/mmc/host/sdhci-esdhc.h -+++ b/drivers/mmc/host/sdhci-esdhc.h -@@ -21,16 +21,23 @@ - #define ESDHC_DEFAULT_QUIRKS (SDHCI_QUIRK_FORCE_BLK_SZ_2048 | \ - SDHCI_QUIRK_NO_BUSY_IRQ | \ - SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | \ -- SDHCI_QUIRK_PIO_NEEDS_DELAY) -+ SDHCI_QUIRK_PIO_NEEDS_DELAY | \ -+ SDHCI_QUIRK_NO_HISPD_BIT) -+ -+#define ESDHC_PROCTL 0x28 - - #define ESDHC_SYSTEM_CONTROL 0x2c - #define ESDHC_CLOCK_MASK 0x0000fff0 - #define ESDHC_PREDIV_SHIFT 8 - #define ESDHC_DIVIDER_SHIFT 4 -+#define ESDHC_CLOCK_CRDEN 0x00000008 - #define ESDHC_CLOCK_PEREN 0x00000004 - #define ESDHC_CLOCK_HCKEN 0x00000002 - #define ESDHC_CLOCK_IPGEN 0x00000001 - -+#define ESDHC_PRESENT_STATE 0x24 -+#define ESDHC_CLOCK_STABLE 0x00000008 -+ - /* pltfm-specific */ - #define ESDHC_HOST_CONTROL_LE 0x20 - -diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c -index 8872c85..4a4a693 100644 ---- a/drivers/mmc/host/sdhci-of-esdhc.c -+++ b/drivers/mmc/host/sdhci-of-esdhc.c -@@ -18,128 +18,334 @@ - #include - #include - #include -+#include -+#include - #include - #include "sdhci-pltfm.h" - #include "sdhci-esdhc.h" - - #define VENDOR_V_22 0x12 - #define VENDOR_V_23 0x13 --static u32 esdhc_readl(struct sdhci_host *host, int reg) -+ -+struct sdhci_esdhc { -+ u8 vendor_ver; -+ u8 spec_ver; -+ u32 soc_ver; -+ u8 soc_rev; -+}; -+ -+/** -+ * esdhc_read*_fixup - Fixup the value read from incompatible eSDHC register -+ * to make it compatible with SD spec. -+ * -+ * @host: pointer to sdhci_host -+ * @spec_reg: SD spec register address -+ * @value: 32bit eSDHC register value on spec_reg address -+ * -+ * In SD spec, there are 8/16/32/64 bits registers, while all of eSDHC -+ * registers are 32 bits. There are differences in register size, register -+ * address, register function, bit position and function between eSDHC spec -+ * and SD spec. -+ * -+ * Return a fixed up register value -+ */ -+static u32 esdhc_readl_fixup(struct sdhci_host *host, -+ int spec_reg, u32 value) - { -+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); -+ struct sdhci_esdhc *esdhc = pltfm_host->priv; - u32 ret; - -- ret = in_be32(host->ioaddr + reg); - /* - * The bit of ADMA flag in eSDHC is not compatible with standard - * SDHC register, so set fake flag SDHCI_CAN_DO_ADMA2 when ADMA is - * supported by eSDHC. - * And for many FSL eSDHC controller, the reset value of field -- * SDHCI_CAN_DO_ADMA1 is one, but some of them can't support ADMA, -+ * SDHCI_CAN_DO_ADMA1 is 1, but some of them can't support ADMA, - * only these vendor version is greater than 2.2/0x12 support ADMA. -- * For FSL eSDHC, must aligned 4-byte, so use 0xFC to read the -- * the verdor version number, oxFE is SDHCI_HOST_VERSION. - */ -- if ((reg == SDHCI_CAPABILITIES) && (ret & SDHCI_CAN_DO_ADMA1)) { -- u32 tmp = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS); -- tmp = (tmp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; -- if (tmp > VENDOR_V_22) -- ret |= SDHCI_CAN_DO_ADMA2; -+ if ((spec_reg == SDHCI_CAPABILITIES) && (value & SDHCI_CAN_DO_ADMA1)) { -+ if (esdhc->vendor_ver > VENDOR_V_22) { -+ ret = value | SDHCI_CAN_DO_ADMA2; -+ return ret; -+ } - } -- -+ ret = value; - return ret; - } - --static u16 esdhc_readw(struct sdhci_host *host, int reg) -+static u16 esdhc_readw_fixup(struct sdhci_host *host, -+ int spec_reg, u32 value) - { - u16 ret; -- int base = reg & ~0x3; -- int shift = (reg & 0x2) * 8; -+ int shift = (spec_reg & 0x2) * 8; - -- if (unlikely(reg == SDHCI_HOST_VERSION)) -- ret = in_be32(host->ioaddr + base) & 0xffff; -+ if (spec_reg == SDHCI_HOST_VERSION) -+ ret = value & 0xffff; - else -- ret = (in_be32(host->ioaddr + base) >> shift) & 0xffff; -+ ret = (value >> shift) & 0xffff; - return ret; - } - --static u8 esdhc_readb(struct sdhci_host *host, int reg) -+static u8 esdhc_readb_fixup(struct sdhci_host *host, -+ int spec_reg, u32 value) - { -- int base = reg & ~0x3; -- int shift = (reg & 0x3) * 8; -- u8 ret = (in_be32(host->ioaddr + base) >> shift) & 0xff; -+ u8 ret; -+ u8 dma_bits; -+ int shift = (spec_reg & 0x3) * 8; -+ -+ ret = (value >> shift) & 0xff; - - /* - * "DMA select" locates at offset 0x28 in SD specification, but on - * P5020 or P3041, it locates at 0x29. - */ -- if (reg == SDHCI_HOST_CONTROL) { -- u32 dma_bits; -- -- dma_bits = in_be32(host->ioaddr + reg); -+ if (spec_reg == SDHCI_HOST_CONTROL) { - /* DMA select is 22,23 bits in Protocol Control Register */ -- dma_bits = (dma_bits >> 5) & SDHCI_CTRL_DMA_MASK; -- -+ dma_bits = (value >> 5) & SDHCI_CTRL_DMA_MASK; - /* fixup the result */ - ret &= ~SDHCI_CTRL_DMA_MASK; - ret |= dma_bits; - } -- - return ret; - } - --static void esdhc_writel(struct sdhci_host *host, u32 val, int reg) -+/** -+ * esdhc_write*_fixup - Fixup the SD spec register value so that it could be -+ * written into eSDHC register. -+ * -+ * @host: pointer to sdhci_host -+ * @spec_reg: SD spec register address -+ * @value: 8/16/32bit SD spec register value that would be written -+ * @old_value: 32bit eSDHC register value on spec_reg address -+ * -+ * In SD spec, there are 8/16/32/64 bits registers, while all of eSDHC -+ * registers are 32 bits. There are differences in register size, register -+ * address, register function, bit position and function between eSDHC spec -+ * and SD spec. -+ * -+ * Return a fixed up register value -+ */ -+static u32 esdhc_writel_fixup(struct sdhci_host *host, -+ int spec_reg, u32 value, u32 old_value) - { -+ u32 ret; -+ - /* -- * Enable IRQSTATEN[BGESEN] is just to set IRQSTAT[BGE] -- * when SYSCTL[RSTD]) is set for some special operations. -- * No any impact other operation. -+ * Enabling IRQSTATEN[BGESEN] is just to set IRQSTAT[BGE] -+ * when SYSCTL[RSTD] is set for some special operations. -+ * No any impact on other operation. - */ -- if (reg == SDHCI_INT_ENABLE) -- val |= SDHCI_INT_BLK_GAP; -- sdhci_be32bs_writel(host, val, reg); -+ if (spec_reg == SDHCI_INT_ENABLE) -+ ret = value | SDHCI_INT_BLK_GAP; -+ else -+ ret = value; -+ -+ return ret; - } - --static void esdhc_writew(struct sdhci_host *host, u16 val, int reg) -+static u32 esdhc_writew_fixup(struct sdhci_host *host, -+ int spec_reg, u16 value, u32 old_value) - { -- if (reg == SDHCI_BLOCK_SIZE) { -+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); -+ int shift = (spec_reg & 0x2) * 8; -+ u32 ret; -+ -+ switch (spec_reg) { -+ case SDHCI_TRANSFER_MODE: -+ /* -+ * Postpone this write, we must do it together with a -+ * command write that is down below. Return old value. -+ */ -+ pltfm_host->xfer_mode_shadow = value; -+ return old_value; -+ case SDHCI_COMMAND: -+ ret = (value << 16) | pltfm_host->xfer_mode_shadow; -+ return ret; -+ } -+ -+ ret = old_value & (~(0xffff << shift)); -+ ret |= (value << shift); -+ -+ if (spec_reg == SDHCI_BLOCK_SIZE) { - /* - * Two last DMA bits are reserved, and first one is used for - * non-standard blksz of 4096 bytes that we don't support - * yet. So clear the DMA boundary bits. - */ -- val &= ~SDHCI_MAKE_BLKSZ(0x7, 0); -+ ret &= (~SDHCI_MAKE_BLKSZ(0x7, 0)); - } -- sdhci_be32bs_writew(host, val, reg); -+ return ret; - } - --static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg) -+static u32 esdhc_writeb_fixup(struct sdhci_host *host, -+ int spec_reg, u8 value, u32 old_value) - { -+ u32 ret; -+ u32 dma_bits; -+ u8 tmp; -+ int shift = (spec_reg & 0x3) * 8; -+ -+ /* -+ * eSDHC doesn't have a standard power control register, so we do -+ * nothing here to avoid incorrect operation. -+ */ -+ if (spec_reg == SDHCI_POWER_CONTROL) -+ return old_value; - /* - * "DMA select" location is offset 0x28 in SD specification, but on - * P5020 or P3041, it's located at 0x29. - */ -- if (reg == SDHCI_HOST_CONTROL) { -- u32 dma_bits; -- -+ if (spec_reg == SDHCI_HOST_CONTROL) { - /* - * If host control register is not standard, exit - * this function - */ - if (host->quirks2 & SDHCI_QUIRK2_BROKEN_HOST_CONTROL) -- return; -+ return old_value; - - /* DMA select is 22,23 bits in Protocol Control Register */ -- dma_bits = (val & SDHCI_CTRL_DMA_MASK) << 5; -- clrsetbits_be32(host->ioaddr + reg , SDHCI_CTRL_DMA_MASK << 5, -- dma_bits); -- val &= ~SDHCI_CTRL_DMA_MASK; -- val |= in_be32(host->ioaddr + reg) & SDHCI_CTRL_DMA_MASK; -+ dma_bits = (value & SDHCI_CTRL_DMA_MASK) << 5; -+ ret = (old_value & (~(SDHCI_CTRL_DMA_MASK << 5))) | dma_bits; -+ tmp = (value & (~SDHCI_CTRL_DMA_MASK)) | -+ (old_value & SDHCI_CTRL_DMA_MASK); -+ ret = (ret & (~0xff)) | tmp; -+ -+ /* Prevent SDHCI core from writing reserved bits (e.g. HISPD) */ -+ ret &= ~ESDHC_HOST_CONTROL_RES; -+ return ret; - } - -- /* Prevent SDHCI core from writing reserved bits (e.g. HISPD). */ -- if (reg == SDHCI_HOST_CONTROL) -- val &= ~ESDHC_HOST_CONTROL_RES; -- sdhci_be32bs_writeb(host, val, reg); -+ ret = (old_value & (~(0xff << shift))) | (value << shift); -+ return ret; -+} -+ -+static u32 esdhc_be_readl(struct sdhci_host *host, int reg) -+{ -+ u32 ret; -+ u32 value; -+ -+ value = ioread32be(host->ioaddr + reg); -+ ret = esdhc_readl_fixup(host, reg, value); -+ -+ return ret; -+} -+ -+static u32 esdhc_le_readl(struct sdhci_host *host, int reg) -+{ -+ u32 ret; -+ u32 value; -+ -+ value = ioread32(host->ioaddr + reg); -+ ret = esdhc_readl_fixup(host, reg, value); -+ -+ return ret; -+} -+ -+static u16 esdhc_be_readw(struct sdhci_host *host, int reg) -+{ -+ u16 ret; -+ u32 value; -+ int base = reg & ~0x3; -+ -+ value = ioread32be(host->ioaddr + base); -+ ret = esdhc_readw_fixup(host, reg, value); -+ return ret; -+} -+ -+static u16 esdhc_le_readw(struct sdhci_host *host, int reg) -+{ -+ u16 ret; -+ u32 value; -+ int base = reg & ~0x3; -+ -+ value = ioread32(host->ioaddr + base); -+ ret = esdhc_readw_fixup(host, reg, value); -+ return ret; -+} -+ -+static u8 esdhc_be_readb(struct sdhci_host *host, int reg) -+{ -+ u8 ret; -+ u32 value; -+ int base = reg & ~0x3; -+ -+ value = ioread32be(host->ioaddr + base); -+ ret = esdhc_readb_fixup(host, reg, value); -+ return ret; -+} -+ -+static u8 esdhc_le_readb(struct sdhci_host *host, int reg) -+{ -+ u8 ret; -+ u32 value; -+ int base = reg & ~0x3; -+ -+ value = ioread32(host->ioaddr + base); -+ ret = esdhc_readb_fixup(host, reg, value); -+ return ret; -+} -+ -+static void esdhc_be_writel(struct sdhci_host *host, u32 val, int reg) -+{ -+ u32 value; -+ -+ value = esdhc_writel_fixup(host, reg, val, 0); -+ iowrite32be(value, host->ioaddr + reg); -+} -+ -+static void esdhc_le_writel(struct sdhci_host *host, u32 val, int reg) -+{ -+ u32 value; -+ -+ value = esdhc_writel_fixup(host, reg, val, 0); -+ iowrite32(value, host->ioaddr + reg); -+} -+ -+static void esdhc_be_writew(struct sdhci_host *host, u16 val, int reg) -+{ -+ int base = reg & ~0x3; -+ u32 value; -+ u32 ret; -+ -+ value = ioread32be(host->ioaddr + base); -+ ret = esdhc_writew_fixup(host, reg, val, value); -+ if (reg != SDHCI_TRANSFER_MODE) -+ iowrite32be(ret, host->ioaddr + base); -+} -+ -+static void esdhc_le_writew(struct sdhci_host *host, u16 val, int reg) -+{ -+ int base = reg & ~0x3; -+ u32 value; -+ u32 ret; -+ -+ value = ioread32(host->ioaddr + base); -+ ret = esdhc_writew_fixup(host, reg, val, value); -+ if (reg != SDHCI_TRANSFER_MODE) -+ iowrite32(ret, host->ioaddr + base); -+} -+ -+static void esdhc_be_writeb(struct sdhci_host *host, u8 val, int reg) -+{ -+ int base = reg & ~0x3; -+ u32 value; -+ u32 ret; -+ -+ value = ioread32be(host->ioaddr + base); -+ ret = esdhc_writeb_fixup(host, reg, val, value); -+ iowrite32be(ret, host->ioaddr + base); -+} -+ -+static void esdhc_le_writeb(struct sdhci_host *host, u8 val, int reg) -+{ -+ int base = reg & ~0x3; -+ u32 value; -+ u32 ret; -+ -+ value = ioread32(host->ioaddr + base); -+ ret = esdhc_writeb_fixup(host, reg, val, value); -+ iowrite32(ret, host->ioaddr + base); - } - - /* -@@ -149,37 +355,116 @@ static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg) - * For Continue, apply soft reset for data(SYSCTL[RSTD]); - * and re-issue the entire read transaction from beginning. - */ --static void esdhci_of_adma_workaround(struct sdhci_host *host, u32 intmask) -+static void esdhc_of_adma_workaround(struct sdhci_host *host, u32 intmask) - { -- u32 tmp; -+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); -+ struct sdhci_esdhc *esdhc = pltfm_host->priv; - bool applicable; - dma_addr_t dmastart; - dma_addr_t dmanow; - -- tmp = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS); -- tmp = (tmp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; -- - applicable = (intmask & SDHCI_INT_DATA_END) && -- (intmask & SDHCI_INT_BLK_GAP) && -- (tmp == VENDOR_V_23); -- if (!applicable) -+ (intmask & SDHCI_INT_BLK_GAP) && -+ (esdhc->vendor_ver == VENDOR_V_23); -+ if (applicable) { -+ -+ sdhci_reset(host, SDHCI_RESET_DATA); -+ host->data->error = 0; -+ dmastart = sg_dma_address(host->data->sg); -+ dmanow = dmastart + host->data->bytes_xfered; -+ /* -+ * Force update to the next DMA block boundary. -+ */ -+ dmanow = (dmanow & ~(SDHCI_DEFAULT_BOUNDARY_SIZE - 1)) + -+ SDHCI_DEFAULT_BOUNDARY_SIZE; -+ host->data->bytes_xfered = dmanow - dmastart; -+ sdhci_writel(host, dmanow, SDHCI_DMA_ADDRESS); -+ - return; -+ } - -- host->data->error = 0; -- dmastart = sg_dma_address(host->data->sg); -- dmanow = dmastart + host->data->bytes_xfered; - /* -- * Force update to the next DMA block boundary. -+ * Check for A-004388: eSDHC DMA might not stop if error -+ * occurs on system transaction -+ * Impact list: -+ * T4240-4160-R1.0 B4860-4420-R1.0-R2.0 P1010-1014-R1.0 -+ * P3041-R1.0-R2.0-R1.1 P2041-2040-R1.0-R1.1-R2.0 -+ * P5020-5010-R2.0-R1.0 P5040-5021-R2.0-R2.1 - */ -- dmanow = (dmanow & ~(SDHCI_DEFAULT_BOUNDARY_SIZE - 1)) + -- SDHCI_DEFAULT_BOUNDARY_SIZE; -- host->data->bytes_xfered = dmanow - dmastart; -- sdhci_writel(host, dmanow, SDHCI_DMA_ADDRESS); -+ if (!(((esdhc->soc_ver == SVR_T4240) && (esdhc->soc_rev == 0x10)) || -+ ((esdhc->soc_ver == SVR_T4160) && (esdhc->soc_rev == 0x10)) || -+ ((esdhc->soc_ver == SVR_B4860) && (esdhc->soc_rev == 0x10)) || -+ ((esdhc->soc_ver == SVR_B4860) && (esdhc->soc_rev == 0x20)) || -+ ((esdhc->soc_ver == SVR_B4420) && (esdhc->soc_rev == 0x10)) || -+ ((esdhc->soc_ver == SVR_B4420) && (esdhc->soc_rev == 0x20)) || -+ ((esdhc->soc_ver == SVR_P1010) && (esdhc->soc_rev == 0x10)) || -+ ((esdhc->soc_ver == SVR_P1014) && (esdhc->soc_rev == 0x10)) || -+ ((esdhc->soc_ver == SVR_P3041) && (esdhc->soc_rev <= 0x20)) || -+ ((esdhc->soc_ver == SVR_P2041) && (esdhc->soc_rev <= 0x20)) || -+ ((esdhc->soc_ver == SVR_P2040) && (esdhc->soc_rev <= 0x20)) || -+ ((esdhc->soc_ver == SVR_P5020) && (esdhc->soc_rev <= 0x20)) || -+ ((esdhc->soc_ver == SVR_P5010) && (esdhc->soc_rev <= 0x20)) || -+ ((esdhc->soc_ver == SVR_P5040) && (esdhc->soc_rev <= 0x21)) || -+ ((esdhc->soc_ver == SVR_P5021) && (esdhc->soc_rev <= 0x21)))) -+ return; -+ -+ sdhci_reset(host, SDHCI_RESET_DATA); -+ -+ if (host->flags & SDHCI_USE_ADMA) { -+ u32 mod, i, offset; -+ u8 *desc; -+ dma_addr_t addr; -+ struct scatterlist *sg; -+ __le32 *dataddr; -+ __le32 *cmdlen; -+ -+ /* -+ * If block count was enabled, in case read transfer there -+ * is no data was corrupted -+ */ -+ mod = sdhci_readl(host, SDHCI_TRANSFER_MODE); -+ if ((mod & SDHCI_TRNS_BLK_CNT_EN) && -+ (host->data->flags & MMC_DATA_READ)) -+ host->data->error = 0; -+ -+ BUG_ON(!host->data); -+ desc = host->adma_table; -+ for_each_sg(host->data->sg, sg, host->sg_count, i) { -+ addr = sg_dma_address(sg); -+ offset = (4 - (addr & 0x3)) & 0x3; -+ if (offset) -+ desc += 8; -+ desc += 8; -+ } -+ -+ /* -+ * Add an extra zero descriptor next to the -+ * terminating descriptor. -+ */ -+ desc += 8; -+ WARN_ON((desc - (u8 *)(host->adma_table)) > (128 * 2 + 1) * 4); -+ -+ dataddr = (__le32 __force *)(desc + 4); -+ cmdlen = (__le32 __force *)desc; -+ -+ cmdlen[0] = cpu_to_le32(0); -+ dataddr[0] = cpu_to_le32(0); -+ } -+ -+ if ((host->flags & SDHCI_USE_SDMA) && -+ (host->data->flags & MMC_DATA_READ)) -+ host->data->error = 0; -+ -+ return; - } - - static int esdhc_of_enable_dma(struct sdhci_host *host) - { -- setbits32(host->ioaddr + ESDHC_DMA_SYSCTL, ESDHC_DMA_SNOOP); -+ u32 value; -+ -+ value = sdhci_readl(host, ESDHC_DMA_SYSCTL); -+ value |= ESDHC_DMA_SNOOP; -+ sdhci_writel(host, value, ESDHC_DMA_SYSCTL); - return 0; - } - -@@ -199,15 +484,22 @@ static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host) - - static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) - { -- int pre_div = 2; -+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); -+ struct sdhci_esdhc *esdhc = pltfm_host->priv; -+ int pre_div = 1; - int div = 1; - u32 temp; -+ u32 timeout; - - host->mmc->actual_clock = 0; - - if (clock == 0) - return; - -+ /* Workaround to start pre_div at 2 for VNN < VENDOR_V_23 */ -+ if (esdhc->vendor_ver < VENDOR_V_23) -+ pre_div = 2; -+ - /* Workaround to reduce the clock frequency for p1010 esdhc */ - if (of_find_compatible_node(NULL, NULL, "fsl,p1010-esdhc")) { - if (clock > 20000000) -@@ -218,7 +510,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) - - temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); - temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN -- | ESDHC_CLOCK_MASK); -+ | ESDHC_CLOCK_CRDEN | ESDHC_CLOCK_MASK); - sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); - - while (host->max_clk / pre_div / 16 > clock && pre_div < 256) -@@ -229,7 +521,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) - - dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n", - clock, host->max_clk / pre_div / div); -- -+ host->mmc->actual_clock = host->max_clk / pre_div / div; - pre_div >>= 1; - div--; - -@@ -238,70 +530,117 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) - | (div << ESDHC_DIVIDER_SHIFT) - | (pre_div << ESDHC_PREDIV_SHIFT)); - sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); -- mdelay(1); --} - --static void esdhc_of_platform_init(struct sdhci_host *host) --{ -- u32 vvn; -- -- vvn = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS); -- vvn = (vvn & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; -- if (vvn == VENDOR_V_22) -- host->quirks2 |= SDHCI_QUIRK2_HOST_NO_CMD23; -+ /* Wait max 20 ms */ -+ timeout = 20; -+ while (!(sdhci_readl(host, ESDHC_PRESENT_STATE) & ESDHC_CLOCK_STABLE)) { -+ if (timeout == 0) { -+ pr_err("%s: Internal clock never stabilised.\n", -+ mmc_hostname(host->mmc)); -+ return; -+ } -+ timeout--; -+ mdelay(1); -+ } - -- if (vvn > VENDOR_V_22) -- host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ; -+ temp |= ESDHC_CLOCK_CRDEN; -+ sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); - } - - static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width) - { - u32 ctrl; - -+ ctrl = sdhci_readl(host, ESDHC_PROCTL); -+ ctrl &= (~ESDHC_CTRL_BUSWIDTH_MASK); - switch (width) { - case MMC_BUS_WIDTH_8: -- ctrl = ESDHC_CTRL_8BITBUS; -+ ctrl |= ESDHC_CTRL_8BITBUS; - break; - - case MMC_BUS_WIDTH_4: -- ctrl = ESDHC_CTRL_4BITBUS; -+ ctrl |= ESDHC_CTRL_4BITBUS; - break; - - default: -- ctrl = 0; - break; - } - -- clrsetbits_be32(host->ioaddr + SDHCI_HOST_CONTROL, -- ESDHC_CTRL_BUSWIDTH_MASK, ctrl); -+ sdhci_writel(host, ctrl, ESDHC_PROCTL); - } - --static const struct sdhci_ops sdhci_esdhc_ops = { -- .read_l = esdhc_readl, -- .read_w = esdhc_readw, -- .read_b = esdhc_readb, -- .write_l = esdhc_writel, -- .write_w = esdhc_writew, -- .write_b = esdhc_writeb, -- .set_clock = esdhc_of_set_clock, -- .enable_dma = esdhc_of_enable_dma, -- .get_max_clock = esdhc_of_get_max_clock, -- .get_min_clock = esdhc_of_get_min_clock, -- .platform_init = esdhc_of_platform_init, -- .adma_workaround = esdhci_of_adma_workaround, -- .set_bus_width = esdhc_pltfm_set_bus_width, -- .reset = sdhci_reset, -- .set_uhs_signaling = sdhci_set_uhs_signaling, --}; -+/* -+ * A-003980: SDHC: Glitch is generated on the card clock with software reset -+ * or clock divider change -+ * Workaround: -+ * A simple workaround is to disable the SD card clock before the software -+ * reset, and enable it when the module resumes normal operation. The Host -+ * and the SD card are in a master-slave relationship. The Host provides -+ * clock and control transfer across the interface. Therefore, any existing -+ * operation is discarded when the Host controller is reset. -+ */ -+static int esdhc_of_reset_workaround(struct sdhci_host *host, u8 mask) -+{ -+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); -+ struct sdhci_esdhc *esdhc = pltfm_host->priv; -+ bool disable_clk_before_reset = false; -+ u32 temp; - --#ifdef CONFIG_PM -+ /* -+ * Check for A-003980 -+ * Impact list: -+ * T4240-4160-R1.0-R2.0 B4860-4420-R1.0-R2.0 P5040-5021-R1.0-R2.0-R2.1 -+ * P5020-5010-R1.0-R2.0 P3041-R1.0-R1.1-R2.0 P2041-2040-R1.0-R1.1-R2.0 -+ * P1010-1014-R1.0 -+ */ -+ if (((esdhc->soc_ver == SVR_T4240) && (esdhc->soc_rev == 0x10)) || -+ ((esdhc->soc_ver == SVR_T4240) && (esdhc->soc_rev == 0x20)) || -+ ((esdhc->soc_ver == SVR_T4160) && (esdhc->soc_rev == 0x10)) || -+ ((esdhc->soc_ver == SVR_T4160) && (esdhc->soc_rev == 0x20)) || -+ ((esdhc->soc_ver == SVR_B4860) && (esdhc->soc_rev == 0x10)) || -+ ((esdhc->soc_ver == SVR_B4860) && (esdhc->soc_rev == 0x20)) || -+ ((esdhc->soc_ver == SVR_B4420) && (esdhc->soc_rev == 0x10)) || -+ ((esdhc->soc_ver == SVR_B4420) && (esdhc->soc_rev == 0x20)) || -+ ((esdhc->soc_ver == SVR_P5040) && (esdhc->soc_rev <= 0x21)) || -+ ((esdhc->soc_ver == SVR_P5021) && (esdhc->soc_rev <= 0x21)) || -+ ((esdhc->soc_ver == SVR_P5020) && (esdhc->soc_rev <= 0x20)) || -+ ((esdhc->soc_ver == SVR_P5010) && (esdhc->soc_rev <= 0x20)) || -+ ((esdhc->soc_ver == SVR_P3041) && (esdhc->soc_rev <= 0x20)) || -+ ((esdhc->soc_ver == SVR_P2041) && (esdhc->soc_rev <= 0x20)) || -+ ((esdhc->soc_ver == SVR_P2040) && (esdhc->soc_rev <= 0x20)) || -+ ((esdhc->soc_ver == SVR_P1014) && (esdhc->soc_rev == 0x10)) || -+ ((esdhc->soc_ver == SVR_P1010) && (esdhc->soc_rev == 0x10))) -+ disable_clk_before_reset = true; -+ -+ if (disable_clk_before_reset && (mask & SDHCI_RESET_ALL)) { -+ temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); -+ temp &= ~ESDHC_CLOCK_CRDEN; -+ sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); -+ sdhci_reset(host, mask); -+ temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); -+ temp |= ESDHC_CLOCK_CRDEN; -+ sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); -+ return 1; -+ } -+ return 0; -+} -+ -+static void esdhc_reset(struct sdhci_host *host, u8 mask) -+{ -+ if (!esdhc_of_reset_workaround(host, mask)) -+ sdhci_reset(host, mask); - -+ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); -+ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); -+} -+ -+#ifdef CONFIG_PM - static u32 esdhc_proctl; - static int esdhc_of_suspend(struct device *dev) - { - struct sdhci_host *host = dev_get_drvdata(dev); - -- esdhc_proctl = sdhci_be32bs_readl(host, SDHCI_HOST_CONTROL); -+ esdhc_proctl = sdhci_readl(host, SDHCI_HOST_CONTROL); - - return sdhci_suspend_host(host); - } -@@ -311,11 +650,8 @@ static int esdhc_of_resume(struct device *dev) - struct sdhci_host *host = dev_get_drvdata(dev); - int ret = sdhci_resume_host(host); - -- if (ret == 0) { -- /* Isn't this already done by sdhci_resume_host() ? --rmk */ -- esdhc_of_enable_dma(host); -- sdhci_be32bs_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL); -- } -+ if (ret == 0) -+ sdhci_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL); - - return ret; - } -@@ -329,30 +665,120 @@ static const struct dev_pm_ops esdhc_pmops = { - #define ESDHC_PMOPS NULL - #endif - --static const struct sdhci_pltfm_data sdhci_esdhc_pdata = { -- /* -- * card detection could be handled via GPIO -- * eSDHC cannot support End Attribute in NOP ADMA descriptor -- */ -+static const struct sdhci_ops sdhci_esdhc_be_ops = { -+ .read_l = esdhc_be_readl, -+ .read_w = esdhc_be_readw, -+ .read_b = esdhc_be_readb, -+ .write_l = esdhc_be_writel, -+ .write_w = esdhc_be_writew, -+ .write_b = esdhc_be_writeb, -+ .set_clock = esdhc_of_set_clock, -+ .enable_dma = esdhc_of_enable_dma, -+ .get_max_clock = esdhc_of_get_max_clock, -+ .get_min_clock = esdhc_of_get_min_clock, -+ .adma_workaround = esdhc_of_adma_workaround, -+ .set_bus_width = esdhc_pltfm_set_bus_width, -+ .reset = esdhc_reset, -+ .set_uhs_signaling = sdhci_set_uhs_signaling, -+}; -+ -+static const struct sdhci_ops sdhci_esdhc_le_ops = { -+ .read_l = esdhc_le_readl, -+ .read_w = esdhc_le_readw, -+ .read_b = esdhc_le_readb, -+ .write_l = esdhc_le_writel, -+ .write_w = esdhc_le_writew, -+ .write_b = esdhc_le_writeb, -+ .set_clock = esdhc_of_set_clock, -+ .enable_dma = esdhc_of_enable_dma, -+ .get_max_clock = esdhc_of_get_max_clock, -+ .get_min_clock = esdhc_of_get_min_clock, -+ .adma_workaround = esdhc_of_adma_workaround, -+ .set_bus_width = esdhc_pltfm_set_bus_width, -+ .reset = esdhc_reset, -+ .set_uhs_signaling = sdhci_set_uhs_signaling, -+}; -+ -+static const struct sdhci_pltfm_data sdhci_esdhc_be_pdata = { - .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_CARD_DETECTION - | SDHCI_QUIRK_NO_CARD_NO_RESET - | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, -- .ops = &sdhci_esdhc_ops, -+ .ops = &sdhci_esdhc_be_ops, - }; - -+static const struct sdhci_pltfm_data sdhci_esdhc_le_pdata = { -+ .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_CARD_DETECTION -+ | SDHCI_QUIRK_NO_CARD_NO_RESET -+ | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, -+ .ops = &sdhci_esdhc_le_ops, -+}; -+ -+static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host) -+{ -+ struct sdhci_pltfm_host *pltfm_host; -+ struct sdhci_esdhc *esdhc; -+ u16 host_ver; -+ u32 svr; -+ -+ pltfm_host = sdhci_priv(host); -+ esdhc = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_esdhc), -+ GFP_KERNEL); -+ pltfm_host->priv = esdhc; -+ -+ svr = guts_get_svr(); -+ esdhc->soc_ver = SVR_SOC_VER(svr); -+ esdhc->soc_rev = SVR_REV(svr); -+ -+ host_ver = sdhci_readw(host, SDHCI_HOST_VERSION); -+ esdhc->vendor_ver = (host_ver & SDHCI_VENDOR_VER_MASK) >> -+ SDHCI_VENDOR_VER_SHIFT; -+ esdhc->spec_ver = host_ver & SDHCI_SPEC_VER_MASK; -+} -+ - static int sdhci_esdhc_probe(struct platform_device *pdev) - { - struct sdhci_host *host; - struct device_node *np; -+ struct sdhci_pltfm_host *pltfm_host; -+ struct sdhci_esdhc *esdhc; - int ret; - -- host = sdhci_pltfm_init(pdev, &sdhci_esdhc_pdata, 0); -+ np = pdev->dev.of_node; -+ -+ if (of_get_property(np, "little-endian", NULL)) -+ host = sdhci_pltfm_init(pdev, &sdhci_esdhc_le_pdata, 0); -+ else -+ host = sdhci_pltfm_init(pdev, &sdhci_esdhc_be_pdata, 0); -+ - if (IS_ERR(host)) - return PTR_ERR(host); - -+ esdhc_init(pdev, host); -+ - sdhci_get_of_property(pdev); - -- np = pdev->dev.of_node; -+ pltfm_host = sdhci_priv(host); -+ esdhc = pltfm_host->priv; -+ if (esdhc->vendor_ver == VENDOR_V_22) -+ host->quirks2 |= SDHCI_QUIRK2_HOST_NO_CMD23; -+ -+ if (esdhc->vendor_ver > VENDOR_V_22) -+ host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ; -+ -+ if (of_device_is_compatible(np, "fsl,p5040-esdhc") || -+ of_device_is_compatible(np, "fsl,p5020-esdhc") || -+ of_device_is_compatible(np, "fsl,p4080-esdhc") || -+ of_device_is_compatible(np, "fsl,p1020-esdhc") || -+ of_device_is_compatible(np, "fsl,t1040-esdhc") || -+ of_device_is_compatible(np, "fsl,ls1021a-esdhc") || -+ of_device_is_compatible(np, "fsl,ls2080a-esdhc") || -+ of_device_is_compatible(np, "fsl,ls2085a-esdhc") || -+ of_device_is_compatible(np, "fsl,ls1043a-esdhc")) -+ host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; -+ -+ if (of_device_is_compatible(np, "fsl,ls1021a-esdhc")) -+ host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; -+ - if (of_device_is_compatible(np, "fsl,p2020-esdhc")) { - /* - * Freescale messed up with P2020 as it has a non-standard -@@ -362,13 +788,19 @@ static int sdhci_esdhc_probe(struct platform_device *pdev) - } - - /* call to generic mmc_of_parse to support additional capabilities */ -- mmc_of_parse(host->mmc); -+ ret = mmc_of_parse(host->mmc); -+ if (ret) -+ goto err; -+ - mmc_of_parse_voltage(np, &host->ocr_mask); - - ret = sdhci_add_host(host); - if (ret) -- sdhci_pltfm_free(pdev); -+ goto err; - -+ return 0; -+ err: -+ sdhci_pltfm_free(pdev); - return ret; - } - -diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c -index 023c201..8af38a6 100644 ---- a/drivers/mmc/host/sdhci.c -+++ b/drivers/mmc/host/sdhci.c -@@ -44,8 +44,6 @@ - - #define MAX_TUNING_LOOP 40 - --#define ADMA_SIZE ((128 * 2 + 1) * 4) -- - static unsigned int debug_quirks = 0; - static unsigned int debug_quirks2; - -@@ -119,10 +117,17 @@ static void sdhci_dumpregs(struct sdhci_host *host) - pr_debug(DRIVER_NAME ": Host ctl2: 0x%08x\n", - sdhci_readw(host, SDHCI_HOST_CONTROL2)); - -- if (host->flags & SDHCI_USE_ADMA) -- pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n", -- readl(host->ioaddr + SDHCI_ADMA_ERROR), -- readl(host->ioaddr + SDHCI_ADMA_ADDRESS)); -+ if (host->flags & SDHCI_USE_ADMA) { -+ if (host->flags & SDHCI_USE_64_BIT_DMA) -+ pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x%08x\n", -+ readl(host->ioaddr + SDHCI_ADMA_ERROR), -+ readl(host->ioaddr + SDHCI_ADMA_ADDRESS_HI), -+ readl(host->ioaddr + SDHCI_ADMA_ADDRESS)); -+ else -+ pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n", -+ readl(host->ioaddr + SDHCI_ADMA_ERROR), -+ readl(host->ioaddr + SDHCI_ADMA_ADDRESS)); -+ } - - pr_debug(DRIVER_NAME ": ===========================================\n"); - } -@@ -231,6 +236,9 @@ static void sdhci_init(struct sdhci_host *host, int soft) - SDHCI_INT_TIMEOUT | SDHCI_INT_DATA_END | - SDHCI_INT_RESPONSE; - -+ if (host->flags & SDHCI_AUTO_CMD12) -+ host->ier |= SDHCI_INT_ACMD12ERR; -+ - sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); - sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); - -@@ -448,18 +456,26 @@ static void sdhci_kunmap_atomic(void *buffer, unsigned long *flags) - local_irq_restore(*flags); - } - --static void sdhci_set_adma_desc(u8 *desc, u32 addr, int len, unsigned cmd) -+static void sdhci_adma_write_desc(struct sdhci_host *host, void *desc, -+ dma_addr_t addr, int len, unsigned cmd) - { -- __le32 *dataddr = (__le32 __force *)(desc + 4); -- __le16 *cmdlen = (__le16 __force *)desc; -+ struct sdhci_adma2_64_desc *dma_desc = desc; -+ -+ /* 32-bit and 64-bit descriptors have these members in same position */ -+ dma_desc->cmd = cpu_to_le16(cmd); -+ dma_desc->len = cpu_to_le16(len); -+ dma_desc->addr_lo = cpu_to_le32((u32)addr); - -- /* SDHCI specification says ADMA descriptors should be 4 byte -- * aligned, so using 16 or 32bit operations should be safe. */ -+ if (host->flags & SDHCI_USE_64_BIT_DMA) -+ dma_desc->addr_hi = cpu_to_le32((u64)addr >> 32); -+} - -- cmdlen[0] = cpu_to_le16(cmd); -- cmdlen[1] = cpu_to_le16(len); -+static void sdhci_adma_mark_end(void *desc) -+{ -+ struct sdhci_adma2_64_desc *dma_desc = desc; - -- dataddr[0] = cpu_to_le32(addr); -+ /* 32-bit and 64-bit descriptors have 'cmd' in same position */ -+ dma_desc->cmd |= cpu_to_le16(ADMA2_END); - } - - static int sdhci_adma_table_pre(struct sdhci_host *host, -@@ -467,8 +483,8 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, - { - int direction; - -- u8 *desc; -- u8 *align; -+ void *desc; -+ void *align; - dma_addr_t addr; - dma_addr_t align_addr; - int len, offset; -@@ -489,17 +505,17 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, - direction = DMA_TO_DEVICE; - - host->align_addr = dma_map_single(mmc_dev(host->mmc), -- host->align_buffer, 128 * 4, direction); -+ host->align_buffer, host->align_buffer_sz, direction); - if (dma_mapping_error(mmc_dev(host->mmc), host->align_addr)) - goto fail; -- BUG_ON(host->align_addr & 0x3); -+ BUG_ON(host->align_addr & host->align_mask); - - host->sg_count = dma_map_sg(mmc_dev(host->mmc), - data->sg, data->sg_len, direction); - if (host->sg_count == 0) - goto unmap_align; - -- desc = host->adma_desc; -+ desc = host->adma_table; - align = host->align_buffer; - - align_addr = host->align_addr; -@@ -515,24 +531,27 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, - * the (up to three) bytes that screw up the - * alignment. - */ -- offset = (4 - (addr & 0x3)) & 0x3; -+ offset = (host->align_sz - (addr & host->align_mask)) & -+ host->align_mask; - if (offset) { - if (data->flags & MMC_DATA_WRITE) { - buffer = sdhci_kmap_atomic(sg, &flags); -- WARN_ON(((long)buffer & PAGE_MASK) > (PAGE_SIZE - 3)); -+ WARN_ON(((long)buffer & (PAGE_SIZE - 1)) > -+ (PAGE_SIZE - offset)); - memcpy(align, buffer, offset); - sdhci_kunmap_atomic(buffer, &flags); - } - - /* tran, valid */ -- sdhci_set_adma_desc(desc, align_addr, offset, 0x21); -+ sdhci_adma_write_desc(host, desc, align_addr, offset, -+ ADMA2_TRAN_VALID); - - BUG_ON(offset > 65536); - -- align += 4; -- align_addr += 4; -+ align += host->align_sz; -+ align_addr += host->align_sz; - -- desc += 8; -+ desc += host->desc_sz; - - addr += offset; - len -= offset; -@@ -541,23 +560,23 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, - BUG_ON(len > 65536); - - /* tran, valid */ -- sdhci_set_adma_desc(desc, addr, len, 0x21); -- desc += 8; -+ sdhci_adma_write_desc(host, desc, addr, len, ADMA2_TRAN_VALID); -+ desc += host->desc_sz; - - /* - * If this triggers then we have a calculation bug - * somewhere. :/ - */ -- WARN_ON((desc - host->adma_desc) > ADMA_SIZE); -+ WARN_ON((desc - host->adma_table) >= host->adma_table_sz); - } - - if (host->quirks & SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC) { - /* - * Mark the last descriptor as the terminating descriptor - */ -- if (desc != host->adma_desc) { -- desc -= 8; -- desc[0] |= 0x2; /* end */ -+ if (desc != host->adma_table) { -+ desc -= host->desc_sz; -+ sdhci_adma_mark_end(desc); - } - } else { - /* -@@ -565,7 +584,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, - */ - - /* nop, end, valid */ -- sdhci_set_adma_desc(desc, 0, 0, 0x3); -+ sdhci_adma_write_desc(host, desc, 0, 0, ADMA2_NOP_END_VALID); - } - - /* -@@ -573,14 +592,14 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, - */ - if (data->flags & MMC_DATA_WRITE) { - dma_sync_single_for_device(mmc_dev(host->mmc), -- host->align_addr, 128 * 4, direction); -+ host->align_addr, host->align_buffer_sz, direction); - } - - return 0; - - unmap_align: - dma_unmap_single(mmc_dev(host->mmc), host->align_addr, -- 128 * 4, direction); -+ host->align_buffer_sz, direction); - fail: - return -EINVAL; - } -@@ -592,7 +611,7 @@ static void sdhci_adma_table_post(struct sdhci_host *host, - - struct scatterlist *sg; - int i, size; -- u8 *align; -+ void *align; - char *buffer; - unsigned long flags; - bool has_unaligned; -@@ -603,12 +622,12 @@ static void sdhci_adma_table_post(struct sdhci_host *host, - direction = DMA_TO_DEVICE; - - dma_unmap_single(mmc_dev(host->mmc), host->align_addr, -- 128 * 4, direction); -+ host->align_buffer_sz, direction); - - /* Do a quick scan of the SG list for any unaligned mappings */ - has_unaligned = false; - for_each_sg(data->sg, sg, host->sg_count, i) -- if (sg_dma_address(sg) & 3) { -+ if (sg_dma_address(sg) & host->align_mask) { - has_unaligned = true; - break; - } -@@ -620,15 +639,17 @@ static void sdhci_adma_table_post(struct sdhci_host *host, - align = host->align_buffer; - - for_each_sg(data->sg, sg, host->sg_count, i) { -- if (sg_dma_address(sg) & 0x3) { -- size = 4 - (sg_dma_address(sg) & 0x3); -+ if (sg_dma_address(sg) & host->align_mask) { -+ size = host->align_sz - -+ (sg_dma_address(sg) & host->align_mask); - - buffer = sdhci_kmap_atomic(sg, &flags); -- WARN_ON(((long)buffer & PAGE_MASK) > (PAGE_SIZE - 3)); -+ WARN_ON(((long)buffer & (PAGE_SIZE - 1)) > -+ (PAGE_SIZE - size)); - memcpy(buffer, align, size); - sdhci_kunmap_atomic(buffer, &flags); - -- align += 4; -+ align += host->align_sz; - } - } - } -@@ -822,6 +843,10 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) - } else { - sdhci_writel(host, host->adma_addr, - SDHCI_ADMA_ADDRESS); -+ if (host->flags & SDHCI_USE_64_BIT_DMA) -+ sdhci_writel(host, -+ (u64)host->adma_addr >> 32, -+ SDHCI_ADMA_ADDRESS_HI); - } - } else { - int sg_cnt; -@@ -855,10 +880,14 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) - ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); - ctrl &= ~SDHCI_CTRL_DMA_MASK; - if ((host->flags & SDHCI_REQ_USE_DMA) && -- (host->flags & SDHCI_USE_ADMA)) -- ctrl |= SDHCI_CTRL_ADMA32; -- else -+ (host->flags & SDHCI_USE_ADMA)) { -+ if (host->flags & SDHCI_USE_64_BIT_DMA) -+ ctrl |= SDHCI_CTRL_ADMA64; -+ else -+ ctrl |= SDHCI_CTRL_ADMA32; -+ } else { - ctrl |= SDHCI_CTRL_SDMA; -+ } - sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); - } - -@@ -1797,6 +1826,10 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, - ctrl |= SDHCI_CTRL_VDD_180; - sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); - -+ /* Some controller need to do more when switching */ -+ if (host->ops->voltage_switch) -+ host->ops->voltage_switch(host); -+ - /* 1.8V regulator output should be stable within 5 ms */ - ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); - if (ctrl & SDHCI_CTRL_VDD_180) -@@ -2250,7 +2283,7 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *mask) - if (intmask & SDHCI_INT_TIMEOUT) - host->cmd->error = -ETIMEDOUT; - else if (intmask & (SDHCI_INT_CRC | SDHCI_INT_END_BIT | -- SDHCI_INT_INDEX)) -+ SDHCI_INT_INDEX | SDHCI_INT_ACMD12ERR)) - host->cmd->error = -EILSEQ; - - if (host->cmd->error) { -@@ -2292,32 +2325,36 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *mask) - } - - #ifdef CONFIG_MMC_DEBUG --static void sdhci_show_adma_error(struct sdhci_host *host) -+static void sdhci_adma_show_error(struct sdhci_host *host) - { - const char *name = mmc_hostname(host->mmc); -- u8 *desc = host->adma_desc; -- __le32 *dma; -- __le16 *len; -- u8 attr; -+ void *desc = host->adma_table; - - sdhci_dumpregs(host); - - while (true) { -- dma = (__le32 *)(desc + 4); -- len = (__le16 *)(desc + 2); -- attr = *desc; -- -- DBG("%s: %p: DMA 0x%08x, LEN 0x%04x, Attr=0x%02x\n", -- name, desc, le32_to_cpu(*dma), le16_to_cpu(*len), attr); -+ struct sdhci_adma2_64_desc *dma_desc = desc; -+ -+ if (host->flags & SDHCI_USE_64_BIT_DMA) -+ DBG("%s: %p: DMA 0x%08x%08x, LEN 0x%04x, Attr=0x%02x\n", -+ name, desc, le32_to_cpu(dma_desc->addr_hi), -+ le32_to_cpu(dma_desc->addr_lo), -+ le16_to_cpu(dma_desc->len), -+ le16_to_cpu(dma_desc->cmd)); -+ else -+ DBG("%s: %p: DMA 0x%08x, LEN 0x%04x, Attr=0x%02x\n", -+ name, desc, le32_to_cpu(dma_desc->addr_lo), -+ le16_to_cpu(dma_desc->len), -+ le16_to_cpu(dma_desc->cmd)); - -- desc += 8; -+ desc += host->desc_sz; - -- if (attr & 2) -+ if (dma_desc->cmd & cpu_to_le16(ADMA2_END)) - break; - } - } - #else --static void sdhci_show_adma_error(struct sdhci_host *host) { } -+static void sdhci_adma_show_error(struct sdhci_host *host) { } - #endif - - static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) -@@ -2380,7 +2417,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) - host->data->error = -EILSEQ; - else if (intmask & SDHCI_INT_ADMA_ERROR) { - pr_err("%s: ADMA error\n", mmc_hostname(host->mmc)); -- sdhci_show_adma_error(host); -+ sdhci_adma_show_error(host); - host->data->error = -EIO; - if (host->ops->adma_workaround) - host->ops->adma_workaround(host, intmask); -@@ -2859,6 +2896,16 @@ int sdhci_add_host(struct sdhci_host *host) - host->flags &= ~SDHCI_USE_ADMA; - } - -+ /* -+ * It is assumed that a 64-bit capable device has set a 64-bit DMA mask -+ * and *must* do 64-bit DMA. A driver has the opportunity to change -+ * that during the first call to ->enable_dma(). Similarly -+ * SDHCI_QUIRK2_BROKEN_64_BIT_DMA must be left to the drivers to -+ * implement. -+ */ -+ if (sdhci_readl(host, SDHCI_CAPABILITIES) & SDHCI_CAN_64BIT) -+ host->flags |= SDHCI_USE_64_BIT_DMA; -+ - if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { - if (host->ops->enable_dma) { - if (host->ops->enable_dma(host)) { -@@ -2870,33 +2917,59 @@ int sdhci_add_host(struct sdhci_host *host) - } - } - -+ /* SDMA does not support 64-bit DMA */ -+ if (host->flags & SDHCI_USE_64_BIT_DMA) -+ host->flags &= ~SDHCI_USE_SDMA; -+ - if (host->flags & SDHCI_USE_ADMA) { - /* -- * We need to allocate descriptors for all sg entries -- * (128) and potentially one alignment transfer for -- * each of those entries. -+ * The DMA descriptor table size is calculated as the maximum -+ * number of segments times 2, to allow for an alignment -+ * descriptor for each segment, plus 1 for a nop end descriptor, -+ * all multipled by the descriptor size. - */ -- host->adma_desc = dma_alloc_coherent(mmc_dev(mmc), -- ADMA_SIZE, &host->adma_addr, -- GFP_KERNEL); -- host->align_buffer = kmalloc(128 * 4, GFP_KERNEL); -- if (!host->adma_desc || !host->align_buffer) { -- dma_free_coherent(mmc_dev(mmc), ADMA_SIZE, -- host->adma_desc, host->adma_addr); -+ if (host->flags & SDHCI_USE_64_BIT_DMA) { -+ host->adma_table_sz = (SDHCI_MAX_SEGS * 2 + 1) * -+ SDHCI_ADMA2_64_DESC_SZ; -+ host->align_buffer_sz = SDHCI_MAX_SEGS * -+ SDHCI_ADMA2_64_ALIGN; -+ host->desc_sz = SDHCI_ADMA2_64_DESC_SZ; -+ host->align_sz = SDHCI_ADMA2_64_ALIGN; -+ host->align_mask = SDHCI_ADMA2_64_ALIGN - 1; -+ } else { -+ host->adma_table_sz = (SDHCI_MAX_SEGS * 2 + 1) * -+ SDHCI_ADMA2_32_DESC_SZ; -+ host->align_buffer_sz = SDHCI_MAX_SEGS * -+ SDHCI_ADMA2_32_ALIGN; -+ host->desc_sz = SDHCI_ADMA2_32_DESC_SZ; -+ host->align_sz = SDHCI_ADMA2_32_ALIGN; -+ host->align_mask = SDHCI_ADMA2_32_ALIGN - 1; -+ } -+ host->adma_table = dma_alloc_coherent(mmc_dev(mmc), -+ host->adma_table_sz, -+ &host->adma_addr, -+ GFP_KERNEL); -+ host->align_buffer = kmalloc(host->align_buffer_sz, GFP_KERNEL); -+ if (!host->adma_table || !host->align_buffer) { -+ if (host->adma_table) -+ dma_free_coherent(mmc_dev(mmc), -+ host->adma_table_sz, -+ host->adma_table, -+ host->adma_addr); - kfree(host->align_buffer); - pr_warn("%s: Unable to allocate ADMA buffers - falling back to standard DMA\n", - mmc_hostname(mmc)); - host->flags &= ~SDHCI_USE_ADMA; -- host->adma_desc = NULL; -+ host->adma_table = NULL; - host->align_buffer = NULL; -- } else if (host->adma_addr & 3) { -+ } else if (host->adma_addr & host->align_mask) { - pr_warn("%s: unable to allocate aligned ADMA descriptor\n", - mmc_hostname(mmc)); - host->flags &= ~SDHCI_USE_ADMA; -- dma_free_coherent(mmc_dev(mmc), ADMA_SIZE, -- host->adma_desc, host->adma_addr); -+ dma_free_coherent(mmc_dev(mmc), host->adma_table_sz, -+ host->adma_table, host->adma_addr); - kfree(host->align_buffer); -- host->adma_desc = NULL; -+ host->adma_table = NULL; - host->align_buffer = NULL; - } - } -@@ -2995,7 +3068,8 @@ int sdhci_add_host(struct sdhci_host *host) - /* Auto-CMD23 stuff only works in ADMA or PIO. */ - if ((host->version >= SDHCI_SPEC_300) && - ((host->flags & SDHCI_USE_ADMA) || -- !(host->flags & SDHCI_USE_SDMA))) { -+ !(host->flags & SDHCI_USE_SDMA)) && -+ !(host->quirks2 & SDHCI_QUIRK2_ACMD23_BROKEN)) { - host->flags |= SDHCI_AUTO_CMD23; - DBG("%s: Auto-CMD23 available\n", mmc_hostname(mmc)); - } else { -@@ -3152,13 +3226,14 @@ int sdhci_add_host(struct sdhci_host *host) - SDHCI_MAX_CURRENT_MULTIPLIER; - } - -- /* If OCR set by external regulators, use it instead */ -+ /* If OCR set by host, use it instead. */ -+ if (host->ocr_mask) -+ ocr_avail = host->ocr_mask; -+ -+ /* If OCR set by external regulators, give it highest prio. */ - if (mmc->ocr_avail) - ocr_avail = mmc->ocr_avail; - -- if (host->ocr_mask) -- ocr_avail &= host->ocr_mask; -- - mmc->ocr_avail = ocr_avail; - mmc->ocr_avail_sdio = ocr_avail; - if (host->ocr_avail_sdio) -@@ -3185,11 +3260,11 @@ int sdhci_add_host(struct sdhci_host *host) - * can do scatter/gather or not. - */ - if (host->flags & SDHCI_USE_ADMA) -- mmc->max_segs = 128; -+ mmc->max_segs = SDHCI_MAX_SEGS; - else if (host->flags & SDHCI_USE_SDMA) - mmc->max_segs = 1; - else /* PIO */ -- mmc->max_segs = 128; -+ mmc->max_segs = SDHCI_MAX_SEGS; - - /* - * Maximum number of sectors in one transfer. Limited by DMA boundary -@@ -3287,7 +3362,8 @@ int sdhci_add_host(struct sdhci_host *host) - - pr_info("%s: SDHCI controller on %s [%s] using %s\n", - mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)), -- (host->flags & SDHCI_USE_ADMA) ? "ADMA" : -+ (host->flags & SDHCI_USE_ADMA) ? -+ (host->flags & SDHCI_USE_64_BIT_DMA) ? "ADMA 64-bit" : "ADMA" : - (host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO"); - - sdhci_enable_card_detection(host); -@@ -3355,12 +3431,12 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) - if (!IS_ERR(mmc->supply.vqmmc)) - regulator_disable(mmc->supply.vqmmc); - -- if (host->adma_desc) -- dma_free_coherent(mmc_dev(mmc), ADMA_SIZE, -- host->adma_desc, host->adma_addr); -+ if (host->adma_table) -+ dma_free_coherent(mmc_dev(mmc), host->adma_table_sz, -+ host->adma_table, host->adma_addr); - kfree(host->align_buffer); - -- host->adma_desc = NULL; -+ host->adma_table = NULL; - host->align_buffer = NULL; - } - -diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h -index 31896a7..5220f36 100644 ---- a/drivers/mmc/host/sdhci.h -+++ b/drivers/mmc/host/sdhci.h -@@ -227,6 +227,7 @@ - /* 55-57 reserved */ - - #define SDHCI_ADMA_ADDRESS 0x58 -+#define SDHCI_ADMA_ADDRESS_HI 0x5C - - /* 60-FB reserved */ - -@@ -266,6 +267,46 @@ - #define SDHCI_DEFAULT_BOUNDARY_SIZE (512 * 1024) - #define SDHCI_DEFAULT_BOUNDARY_ARG (ilog2(SDHCI_DEFAULT_BOUNDARY_SIZE) - 12) - -+/* ADMA2 32-bit DMA descriptor size */ -+#define SDHCI_ADMA2_32_DESC_SZ 8 -+ -+/* ADMA2 32-bit DMA alignment */ -+#define SDHCI_ADMA2_32_ALIGN 4 -+ -+/* ADMA2 32-bit descriptor */ -+struct sdhci_adma2_32_desc { -+ __le16 cmd; -+ __le16 len; -+ __le32 addr; -+} __packed __aligned(SDHCI_ADMA2_32_ALIGN); -+ -+/* ADMA2 64-bit DMA descriptor size */ -+#define SDHCI_ADMA2_64_DESC_SZ 12 -+ -+/* ADMA2 64-bit DMA alignment */ -+#define SDHCI_ADMA2_64_ALIGN 8 -+ -+/* -+ * ADMA2 64-bit descriptor. Note 12-byte descriptor can't always be 8-byte -+ * aligned. -+ */ -+struct sdhci_adma2_64_desc { -+ __le16 cmd; -+ __le16 len; -+ __le32 addr_lo; -+ __le32 addr_hi; -+} __packed __aligned(4); -+ -+#define ADMA2_TRAN_VALID 0x21 -+#define ADMA2_NOP_END_VALID 0x3 -+#define ADMA2_END 0x2 -+ -+/* -+ * Maximum segments assuming a 512KiB maximum requisition size and a minimum -+ * 4KiB page size. -+ */ -+#define SDHCI_MAX_SEGS 128 -+ - struct sdhci_ops { - #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS - u32 (*read_l)(struct sdhci_host *host, int reg); -@@ -296,6 +337,7 @@ struct sdhci_ops { - void (*adma_workaround)(struct sdhci_host *host, u32 intmask); - void (*platform_init)(struct sdhci_host *host); - void (*card_event)(struct sdhci_host *host); -+ void (*voltage_switch)(struct sdhci_host *host); - }; - - #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS -diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig -index dd10646..34ce759 100644 ---- a/drivers/mtd/nand/Kconfig -+++ b/drivers/mtd/nand/Kconfig -@@ -429,7 +429,7 @@ config MTD_NAND_FSL_ELBC - - config MTD_NAND_FSL_IFC - tristate "NAND support for Freescale IFC controller" -- depends on MTD_NAND && FSL_SOC -+ depends on MTD_NAND && (FSL_SOC || ARCH_LAYERSCAPE) - select FSL_IFC - select MEMORY - help -diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c -index 2338124..c8be272 100644 ---- a/drivers/mtd/nand/fsl_ifc_nand.c -+++ b/drivers/mtd/nand/fsl_ifc_nand.c -@@ -31,7 +31,6 @@ - #include - #include - --#define FSL_IFC_V1_1_0 0x01010000 - #define ERR_BYTE 0xFF /* Value returned for read - bytes when read failed */ - #define IFC_TIMEOUT_MSECS 500 /* Maximum number of mSecs to wait -@@ -234,13 +233,13 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob) - struct nand_chip *chip = mtd->priv; - struct fsl_ifc_mtd *priv = chip->priv; - struct fsl_ifc_ctrl *ctrl = priv->ctrl; -- struct fsl_ifc_regs __iomem *ifc = ctrl->regs; -+ struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; - int buf_num; - - ifc_nand_ctrl->page = page_addr; - /* Program ROW0/COL0 */ -- iowrite32be(page_addr, &ifc->ifc_nand.row0); -- iowrite32be((oob ? IFC_NAND_COL_MS : 0) | column, &ifc->ifc_nand.col0); -+ ifc_out32(page_addr, &ifc->ifc_nand.row0); -+ ifc_out32((oob ? IFC_NAND_COL_MS : 0) | column, &ifc->ifc_nand.col0); - - buf_num = page_addr & priv->bufnum_mask; - -@@ -297,28 +296,28 @@ static void fsl_ifc_run_command(struct mtd_info *mtd) - struct fsl_ifc_mtd *priv = chip->priv; - struct fsl_ifc_ctrl *ctrl = priv->ctrl; - struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl; -- struct fsl_ifc_regs __iomem *ifc = ctrl->regs; -+ struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; - u32 eccstat[4]; - int i; - - /* set the chip select for NAND Transaction */ -- iowrite32be(priv->bank << IFC_NAND_CSEL_SHIFT, -- &ifc->ifc_nand.nand_csel); -+ ifc_out32(priv->bank << IFC_NAND_CSEL_SHIFT, -+ &ifc->ifc_nand.nand_csel); - - dev_vdbg(priv->dev, - "%s: fir0=%08x fcr0=%08x\n", - __func__, -- ioread32be(&ifc->ifc_nand.nand_fir0), -- ioread32be(&ifc->ifc_nand.nand_fcr0)); -+ ifc_in32(&ifc->ifc_nand.nand_fir0), -+ ifc_in32(&ifc->ifc_nand.nand_fcr0)); - - ctrl->nand_stat = 0; - - /* start read/write seq */ -- iowrite32be(IFC_NAND_SEQ_STRT_FIR_STRT, &ifc->ifc_nand.nandseq_strt); -+ ifc_out32(IFC_NAND_SEQ_STRT_FIR_STRT, &ifc->ifc_nand.nandseq_strt); - - /* wait for command complete flag or timeout */ - wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat, -- IFC_TIMEOUT_MSECS * HZ/1000); -+ msecs_to_jiffies(IFC_TIMEOUT_MSECS)); - - /* ctrl->nand_stat will be updated from IRQ context */ - if (!ctrl->nand_stat) -@@ -337,7 +336,7 @@ static void fsl_ifc_run_command(struct mtd_info *mtd) - int sector_end = sector + chip->ecc.steps - 1; - - for (i = sector / 4; i <= sector_end / 4; i++) -- eccstat[i] = ioread32be(&ifc->ifc_nand.nand_eccstat[i]); -+ eccstat[i] = ifc_in32(&ifc->ifc_nand.nand_eccstat[i]); - - for (i = sector; i <= sector_end; i++) { - errors = check_read_ecc(mtd, ctrl, eccstat, i); -@@ -373,37 +372,37 @@ static void fsl_ifc_do_read(struct nand_chip *chip, - { - struct fsl_ifc_mtd *priv = chip->priv; - struct fsl_ifc_ctrl *ctrl = priv->ctrl; -- struct fsl_ifc_regs __iomem *ifc = ctrl->regs; -+ struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; - - /* Program FIR/IFC_NAND_FCR0 for Small/Large page */ - if (mtd->writesize > 512) { -- iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | -- (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | -- (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | -- (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) | -- (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP4_SHIFT), -- &ifc->ifc_nand.nand_fir0); -- iowrite32be(0x0, &ifc->ifc_nand.nand_fir1); -- -- iowrite32be((NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) | -- (NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT), -- &ifc->ifc_nand.nand_fcr0); -+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | -+ (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | -+ (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | -+ (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) | -+ (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP4_SHIFT), -+ &ifc->ifc_nand.nand_fir0); -+ ifc_out32(0x0, &ifc->ifc_nand.nand_fir1); -+ -+ ifc_out32((NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) | -+ (NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT), -+ &ifc->ifc_nand.nand_fcr0); - } else { -- iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | -- (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | -- (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | -- (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP3_SHIFT), -- &ifc->ifc_nand.nand_fir0); -- iowrite32be(0x0, &ifc->ifc_nand.nand_fir1); -+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | -+ (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | -+ (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | -+ (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP3_SHIFT), -+ &ifc->ifc_nand.nand_fir0); -+ ifc_out32(0x0, &ifc->ifc_nand.nand_fir1); - - if (oob) -- iowrite32be(NAND_CMD_READOOB << -- IFC_NAND_FCR0_CMD0_SHIFT, -- &ifc->ifc_nand.nand_fcr0); -+ ifc_out32(NAND_CMD_READOOB << -+ IFC_NAND_FCR0_CMD0_SHIFT, -+ &ifc->ifc_nand.nand_fcr0); - else -- iowrite32be(NAND_CMD_READ0 << -- IFC_NAND_FCR0_CMD0_SHIFT, -- &ifc->ifc_nand.nand_fcr0); -+ ifc_out32(NAND_CMD_READ0 << -+ IFC_NAND_FCR0_CMD0_SHIFT, -+ &ifc->ifc_nand.nand_fcr0); - } - } - -@@ -413,7 +412,7 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, - struct nand_chip *chip = mtd->priv; - struct fsl_ifc_mtd *priv = chip->priv; - struct fsl_ifc_ctrl *ctrl = priv->ctrl; -- struct fsl_ifc_regs __iomem *ifc = ctrl->regs; -+ struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; - - /* clear the read buffer */ - ifc_nand_ctrl->read_bytes = 0; -@@ -423,7 +422,7 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, - switch (command) { - /* READ0 read the entire buffer to use hardware ECC. */ - case NAND_CMD_READ0: -- iowrite32be(0, &ifc->ifc_nand.nand_fbcr); -+ ifc_out32(0, &ifc->ifc_nand.nand_fbcr); - set_addr(mtd, 0, page_addr, 0); - - ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize; -@@ -438,7 +437,7 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, - - /* READOOB reads only the OOB because no ECC is performed. */ - case NAND_CMD_READOOB: -- iowrite32be(mtd->oobsize - column, &ifc->ifc_nand.nand_fbcr); -+ ifc_out32(mtd->oobsize - column, &ifc->ifc_nand.nand_fbcr); - set_addr(mtd, column, page_addr, 1); - - ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize; -@@ -454,19 +453,19 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, - if (command == NAND_CMD_PARAM) - timing = IFC_FIR_OP_RBCD; - -- iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | -- (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | -- (timing << IFC_NAND_FIR0_OP2_SHIFT), -- &ifc->ifc_nand.nand_fir0); -- iowrite32be(command << IFC_NAND_FCR0_CMD0_SHIFT, -- &ifc->ifc_nand.nand_fcr0); -- iowrite32be(column, &ifc->ifc_nand.row3); -+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | -+ (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | -+ (timing << IFC_NAND_FIR0_OP2_SHIFT), -+ &ifc->ifc_nand.nand_fir0); -+ ifc_out32(command << IFC_NAND_FCR0_CMD0_SHIFT, -+ &ifc->ifc_nand.nand_fcr0); -+ ifc_out32(column, &ifc->ifc_nand.row3); - - /* - * although currently it's 8 bytes for READID, we always read - * the maximum 256 bytes(for PARAM) - */ -- iowrite32be(256, &ifc->ifc_nand.nand_fbcr); -+ ifc_out32(256, &ifc->ifc_nand.nand_fbcr); - ifc_nand_ctrl->read_bytes = 256; - - set_addr(mtd, 0, 0, 0); -@@ -481,16 +480,16 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, - - /* ERASE2 uses the block and page address from ERASE1 */ - case NAND_CMD_ERASE2: -- iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | -- (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP1_SHIFT) | -- (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP2_SHIFT), -- &ifc->ifc_nand.nand_fir0); -+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | -+ (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP1_SHIFT) | -+ (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP2_SHIFT), -+ &ifc->ifc_nand.nand_fir0); - -- iowrite32be((NAND_CMD_ERASE1 << IFC_NAND_FCR0_CMD0_SHIFT) | -- (NAND_CMD_ERASE2 << IFC_NAND_FCR0_CMD1_SHIFT), -- &ifc->ifc_nand.nand_fcr0); -+ ifc_out32((NAND_CMD_ERASE1 << IFC_NAND_FCR0_CMD0_SHIFT) | -+ (NAND_CMD_ERASE2 << IFC_NAND_FCR0_CMD1_SHIFT), -+ &ifc->ifc_nand.nand_fcr0); - -- iowrite32be(0, &ifc->ifc_nand.nand_fbcr); -+ ifc_out32(0, &ifc->ifc_nand.nand_fbcr); - ifc_nand_ctrl->read_bytes = 0; - fsl_ifc_run_command(mtd); - return; -@@ -507,19 +506,18 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, - (NAND_CMD_STATUS << IFC_NAND_FCR0_CMD1_SHIFT) | - (NAND_CMD_PAGEPROG << IFC_NAND_FCR0_CMD2_SHIFT); - -- iowrite32be( -- (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | -- (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | -- (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | -- (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP3_SHIFT) | -- (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP4_SHIFT), -- &ifc->ifc_nand.nand_fir0); -- iowrite32be( -- (IFC_FIR_OP_CW1 << IFC_NAND_FIR1_OP5_SHIFT) | -- (IFC_FIR_OP_RDSTAT << -- IFC_NAND_FIR1_OP6_SHIFT) | -- (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP7_SHIFT), -- &ifc->ifc_nand.nand_fir1); -+ ifc_out32( -+ (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | -+ (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | -+ (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | -+ (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP3_SHIFT) | -+ (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP4_SHIFT), -+ &ifc->ifc_nand.nand_fir0); -+ ifc_out32( -+ (IFC_FIR_OP_CW1 << IFC_NAND_FIR1_OP5_SHIFT) | -+ (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR1_OP6_SHIFT) | -+ (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP7_SHIFT), -+ &ifc->ifc_nand.nand_fir1); - } else { - nand_fcr0 = ((NAND_CMD_PAGEPROG << - IFC_NAND_FCR0_CMD1_SHIFT) | -@@ -528,20 +526,19 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, - (NAND_CMD_STATUS << - IFC_NAND_FCR0_CMD3_SHIFT)); - -- iowrite32be( -+ ifc_out32( - (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP1_SHIFT) | - (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP2_SHIFT) | - (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP3_SHIFT) | - (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP4_SHIFT), - &ifc->ifc_nand.nand_fir0); -- iowrite32be( -- (IFC_FIR_OP_CMD1 << IFC_NAND_FIR1_OP5_SHIFT) | -- (IFC_FIR_OP_CW3 << IFC_NAND_FIR1_OP6_SHIFT) | -- (IFC_FIR_OP_RDSTAT << -- IFC_NAND_FIR1_OP7_SHIFT) | -- (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP8_SHIFT), -- &ifc->ifc_nand.nand_fir1); -+ ifc_out32( -+ (IFC_FIR_OP_CMD1 << IFC_NAND_FIR1_OP5_SHIFT) | -+ (IFC_FIR_OP_CW3 << IFC_NAND_FIR1_OP6_SHIFT) | -+ (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR1_OP7_SHIFT) | -+ (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP8_SHIFT), -+ &ifc->ifc_nand.nand_fir1); - - if (column >= mtd->writesize) - nand_fcr0 |= -@@ -556,7 +553,7 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, - column -= mtd->writesize; - ifc_nand_ctrl->oob = 1; - } -- iowrite32be(nand_fcr0, &ifc->ifc_nand.nand_fcr0); -+ ifc_out32(nand_fcr0, &ifc->ifc_nand.nand_fcr0); - set_addr(mtd, column, page_addr, ifc_nand_ctrl->oob); - return; - } -@@ -564,24 +561,26 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, - /* PAGEPROG reuses all of the setup from SEQIN and adds the length */ - case NAND_CMD_PAGEPROG: { - if (ifc_nand_ctrl->oob) { -- iowrite32be(ifc_nand_ctrl->index - -- ifc_nand_ctrl->column, -- &ifc->ifc_nand.nand_fbcr); -+ ifc_out32(ifc_nand_ctrl->index - -+ ifc_nand_ctrl->column, -+ &ifc->ifc_nand.nand_fbcr); - } else { -- iowrite32be(0, &ifc->ifc_nand.nand_fbcr); -+ ifc_out32(0, &ifc->ifc_nand.nand_fbcr); - } - - fsl_ifc_run_command(mtd); - return; - } - -- case NAND_CMD_STATUS: -- iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | -- (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP1_SHIFT), -- &ifc->ifc_nand.nand_fir0); -- iowrite32be(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT, -- &ifc->ifc_nand.nand_fcr0); -- iowrite32be(1, &ifc->ifc_nand.nand_fbcr); -+ case NAND_CMD_STATUS: { -+ void __iomem *addr; -+ -+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | -+ (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP1_SHIFT), -+ &ifc->ifc_nand.nand_fir0); -+ ifc_out32(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT, -+ &ifc->ifc_nand.nand_fcr0); -+ ifc_out32(1, &ifc->ifc_nand.nand_fbcr); - set_addr(mtd, 0, 0, 0); - ifc_nand_ctrl->read_bytes = 1; - -@@ -591,17 +590,19 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, - * The chip always seems to report that it is - * write-protected, even when it is not. - */ -+ addr = ifc_nand_ctrl->addr; - if (chip->options & NAND_BUSWIDTH_16) -- setbits16(ifc_nand_ctrl->addr, NAND_STATUS_WP); -+ ifc_out16(ifc_in16(addr) | (NAND_STATUS_WP), addr); - else -- setbits8(ifc_nand_ctrl->addr, NAND_STATUS_WP); -+ ifc_out8(ifc_in8(addr) | (NAND_STATUS_WP), addr); - return; -+ } - - case NAND_CMD_RESET: -- iowrite32be(IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT, -- &ifc->ifc_nand.nand_fir0); -- iowrite32be(NAND_CMD_RESET << IFC_NAND_FCR0_CMD0_SHIFT, -- &ifc->ifc_nand.nand_fcr0); -+ ifc_out32(IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT, -+ &ifc->ifc_nand.nand_fir0); -+ ifc_out32(NAND_CMD_RESET << IFC_NAND_FCR0_CMD0_SHIFT, -+ &ifc->ifc_nand.nand_fcr0); - fsl_ifc_run_command(mtd); - return; - -@@ -659,7 +660,7 @@ static uint8_t fsl_ifc_read_byte(struct mtd_info *mtd) - */ - if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) { - offset = ifc_nand_ctrl->index++; -- return in_8(ifc_nand_ctrl->addr + offset); -+ return ifc_in8(ifc_nand_ctrl->addr + offset); - } - - dev_err(priv->dev, "%s: beyond end of buffer\n", __func__); -@@ -681,7 +682,7 @@ static uint8_t fsl_ifc_read_byte16(struct mtd_info *mtd) - * next byte. - */ - if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) { -- data = in_be16(ifc_nand_ctrl->addr + ifc_nand_ctrl->index); -+ data = ifc_in16(ifc_nand_ctrl->addr + ifc_nand_ctrl->index); - ifc_nand_ctrl->index += 2; - return (uint8_t) data; - } -@@ -723,22 +724,22 @@ static int fsl_ifc_wait(struct mtd_info *mtd, struct nand_chip *chip) - { - struct fsl_ifc_mtd *priv = chip->priv; - struct fsl_ifc_ctrl *ctrl = priv->ctrl; -- struct fsl_ifc_regs __iomem *ifc = ctrl->regs; -+ struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; - u32 nand_fsr; - - /* Use READ_STATUS command, but wait for the device to be ready */ -- iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | -- (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR0_OP1_SHIFT), -- &ifc->ifc_nand.nand_fir0); -- iowrite32be(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT, -- &ifc->ifc_nand.nand_fcr0); -- iowrite32be(1, &ifc->ifc_nand.nand_fbcr); -+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | -+ (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR0_OP1_SHIFT), -+ &ifc->ifc_nand.nand_fir0); -+ ifc_out32(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT, -+ &ifc->ifc_nand.nand_fcr0); -+ ifc_out32(1, &ifc->ifc_nand.nand_fbcr); - set_addr(mtd, 0, 0, 0); - ifc_nand_ctrl->read_bytes = 1; - - fsl_ifc_run_command(mtd); - -- nand_fsr = ioread32be(&ifc->ifc_nand.nand_fsr); -+ nand_fsr = ifc_in32(&ifc->ifc_nand.nand_fsr); - - /* - * The chip always seems to report that it is -@@ -825,67 +826,72 @@ static int fsl_ifc_chip_init_tail(struct mtd_info *mtd) - static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv) - { - struct fsl_ifc_ctrl *ctrl = priv->ctrl; -- struct fsl_ifc_regs __iomem *ifc = ctrl->regs; -+ struct fsl_ifc_runtime __iomem *ifc_runtime = ctrl->rregs; -+ struct fsl_ifc_global __iomem *ifc_global = ctrl->gregs; - uint32_t csor = 0, csor_8k = 0, csor_ext = 0; - uint32_t cs = priv->bank; - - /* Save CSOR and CSOR_ext */ -- csor = ioread32be(&ifc->csor_cs[cs].csor); -- csor_ext = ioread32be(&ifc->csor_cs[cs].csor_ext); -+ csor = ifc_in32(&ifc_global->csor_cs[cs].csor); -+ csor_ext = ifc_in32(&ifc_global->csor_cs[cs].csor_ext); - - /* chage PageSize 8K and SpareSize 1K*/ - csor_8k = (csor & ~(CSOR_NAND_PGS_MASK)) | 0x0018C000; -- iowrite32be(csor_8k, &ifc->csor_cs[cs].csor); -- iowrite32be(0x0000400, &ifc->csor_cs[cs].csor_ext); -+ ifc_out32(csor_8k, &ifc_global->csor_cs[cs].csor); -+ ifc_out32(0x0000400, &ifc_global->csor_cs[cs].csor_ext); - - /* READID */ -- iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | -+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | - (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT), -- &ifc->ifc_nand.nand_fir0); -- iowrite32be(NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT, -- &ifc->ifc_nand.nand_fcr0); -- iowrite32be(0x0, &ifc->ifc_nand.row3); -+ &ifc_runtime->ifc_nand.nand_fir0); -+ ifc_out32(NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT, -+ &ifc_runtime->ifc_nand.nand_fcr0); -+ ifc_out32(0x0, &ifc_runtime->ifc_nand.row3); - -- iowrite32be(0x0, &ifc->ifc_nand.nand_fbcr); -+ ifc_out32(0x0, &ifc_runtime->ifc_nand.nand_fbcr); - - /* Program ROW0/COL0 */ -- iowrite32be(0x0, &ifc->ifc_nand.row0); -- iowrite32be(0x0, &ifc->ifc_nand.col0); -+ ifc_out32(0x0, &ifc_runtime->ifc_nand.row0); -+ ifc_out32(0x0, &ifc_runtime->ifc_nand.col0); - - /* set the chip select for NAND Transaction */ -- iowrite32be(cs << IFC_NAND_CSEL_SHIFT, &ifc->ifc_nand.nand_csel); -+ ifc_out32(cs << IFC_NAND_CSEL_SHIFT, -+ &ifc_runtime->ifc_nand.nand_csel); - - /* start read seq */ -- iowrite32be(IFC_NAND_SEQ_STRT_FIR_STRT, &ifc->ifc_nand.nandseq_strt); -+ ifc_out32(IFC_NAND_SEQ_STRT_FIR_STRT, -+ &ifc_runtime->ifc_nand.nandseq_strt); - - /* wait for command complete flag or timeout */ - wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat, -- IFC_TIMEOUT_MSECS * HZ/1000); -+ msecs_to_jiffies(IFC_TIMEOUT_MSECS)); - - if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC) - printk(KERN_ERR "fsl-ifc: Failed to Initialise SRAM\n"); - - /* Restore CSOR and CSOR_ext */ -- iowrite32be(csor, &ifc->csor_cs[cs].csor); -- iowrite32be(csor_ext, &ifc->csor_cs[cs].csor_ext); -+ ifc_out32(csor, &ifc_global->csor_cs[cs].csor); -+ ifc_out32(csor_ext, &ifc_global->csor_cs[cs].csor_ext); - } - - static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) - { - struct fsl_ifc_ctrl *ctrl = priv->ctrl; -- struct fsl_ifc_regs __iomem *ifc = ctrl->regs; -+ struct fsl_ifc_global __iomem *ifc_global = ctrl->gregs; -+ struct fsl_ifc_runtime __iomem *ifc_runtime = ctrl->rregs; - struct nand_chip *chip = &priv->chip; - struct nand_ecclayout *layout; -- u32 csor, ver; -+ u32 csor; - - /* Fill in fsl_ifc_mtd structure */ - priv->mtd.priv = chip; -- priv->mtd.owner = THIS_MODULE; -+ priv->mtd.dev.parent = priv->dev; - - /* fill in nand_chip structure */ - /* set up function call table */ -- if ((ioread32be(&ifc->cspr_cs[priv->bank].cspr)) & CSPR_PORT_SIZE_16) -+ if ((ifc_in32(&ifc_global->cspr_cs[priv->bank].cspr)) -+ & CSPR_PORT_SIZE_16) - chip->read_byte = fsl_ifc_read_byte16; - else - chip->read_byte = fsl_ifc_read_byte; -@@ -899,13 +905,14 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) - chip->bbt_td = &bbt_main_descr; - chip->bbt_md = &bbt_mirror_descr; - -- iowrite32be(0x0, &ifc->ifc_nand.ncfgr); -+ ifc_out32(0x0, &ifc_runtime->ifc_nand.ncfgr); - - /* set up nand options */ - chip->bbt_options = NAND_BBT_USE_FLASH; - chip->options = NAND_NO_SUBPAGE_WRITE; - -- if (ioread32be(&ifc->cspr_cs[priv->bank].cspr) & CSPR_PORT_SIZE_16) { -+ if (ifc_in32(&ifc_global->cspr_cs[priv->bank].cspr) -+ & CSPR_PORT_SIZE_16) { - chip->read_byte = fsl_ifc_read_byte16; - chip->options |= NAND_BUSWIDTH_16; - } else { -@@ -918,7 +925,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) - chip->ecc.read_page = fsl_ifc_read_page; - chip->ecc.write_page = fsl_ifc_write_page; - -- csor = ioread32be(&ifc->csor_cs[priv->bank].csor); -+ csor = ifc_in32(&ifc_global->csor_cs[priv->bank].csor); - - /* Hardware generates ECC per 512 Bytes */ - chip->ecc.size = 512; -@@ -984,8 +991,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) - chip->ecc.mode = NAND_ECC_SOFT; - } - -- ver = ioread32be(&ifc->ifc_rev); -- if (ver == FSL_IFC_V1_1_0) -+ if (ctrl->version == FSL_IFC_VERSION_1_1_0) - fsl_ifc_sram_init(priv); - - return 0; -@@ -1005,10 +1011,10 @@ static int fsl_ifc_chip_remove(struct fsl_ifc_mtd *priv) - return 0; - } - --static int match_bank(struct fsl_ifc_regs __iomem *ifc, int bank, -+static int match_bank(struct fsl_ifc_global __iomem *ifc_global, int bank, - phys_addr_t addr) - { -- u32 cspr = ioread32be(&ifc->cspr_cs[bank].cspr); -+ u32 cspr = ifc_in32(&ifc_global->cspr_cs[bank].cspr); - - if (!(cspr & CSPR_V)) - return 0; -@@ -1022,7 +1028,7 @@ static DEFINE_MUTEX(fsl_ifc_nand_mutex); - - static int fsl_ifc_nand_probe(struct platform_device *dev) - { -- struct fsl_ifc_regs __iomem *ifc; -+ struct fsl_ifc_runtime __iomem *ifc; - struct fsl_ifc_mtd *priv; - struct resource res; - static const char *part_probe_types[] -@@ -1033,9 +1039,9 @@ static int fsl_ifc_nand_probe(struct platform_device *dev) - struct mtd_part_parser_data ppdata; - - ppdata.of_node = dev->dev.of_node; -- if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->regs) -+ if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->rregs) - return -ENODEV; -- ifc = fsl_ifc_ctrl_dev->regs; -+ ifc = fsl_ifc_ctrl_dev->rregs; - - /* get, allocate and map the memory resource */ - ret = of_address_to_resource(node, 0, &res); -@@ -1045,12 +1051,12 @@ static int fsl_ifc_nand_probe(struct platform_device *dev) - } - - /* find which chip select it is connected to */ -- for (bank = 0; bank < FSL_IFC_BANK_COUNT; bank++) { -- if (match_bank(ifc, bank, res.start)) -+ for (bank = 0; bank < fsl_ifc_ctrl_dev->banks; bank++) { -+ if (match_bank(fsl_ifc_ctrl_dev->gregs, bank, res.start)) - break; - } - -- if (bank >= FSL_IFC_BANK_COUNT) { -+ if (bank >= fsl_ifc_ctrl_dev->banks) { - dev_err(&dev->dev, "%s: address did not match any chip selects\n", - __func__); - return -ENODEV; -@@ -1094,16 +1100,16 @@ static int fsl_ifc_nand_probe(struct platform_device *dev) - - dev_set_drvdata(priv->dev, priv); - -- iowrite32be(IFC_NAND_EVTER_EN_OPC_EN | -- IFC_NAND_EVTER_EN_FTOER_EN | -- IFC_NAND_EVTER_EN_WPER_EN, -- &ifc->ifc_nand.nand_evter_en); -+ ifc_out32(IFC_NAND_EVTER_EN_OPC_EN | -+ IFC_NAND_EVTER_EN_FTOER_EN | -+ IFC_NAND_EVTER_EN_WPER_EN, -+ &ifc->ifc_nand.nand_evter_en); - - /* enable NAND Machine Interrupts */ -- iowrite32be(IFC_NAND_EVTER_INTR_OPCIR_EN | -- IFC_NAND_EVTER_INTR_FTOERIR_EN | -- IFC_NAND_EVTER_INTR_WPERIR_EN, -- &ifc->ifc_nand.nand_evter_intr_en); -+ ifc_out32(IFC_NAND_EVTER_INTR_OPCIR_EN | -+ IFC_NAND_EVTER_INTR_FTOERIR_EN | -+ IFC_NAND_EVTER_INTR_WPERIR_EN, -+ &ifc->ifc_nand.nand_evter_intr_en); - priv->mtd.name = kasprintf(GFP_KERNEL, "%llx.flash", (u64)res.start); - if (!priv->mtd.name) { - ret = -ENOMEM; -@@ -1163,6 +1169,7 @@ static const struct of_device_id fsl_ifc_nand_match[] = { - }, - {} - }; -+MODULE_DEVICE_TABLE(of, fsl_ifc_nand_match); - - static struct platform_driver fsl_ifc_nand_driver = { - .driver = { -diff --git a/drivers/net/ethernet/freescale/Kconfig b/drivers/net/ethernet/freescale/Kconfig -index 2703083..0c1c97d 100644 ---- a/drivers/net/ethernet/freescale/Kconfig -+++ b/drivers/net/ethernet/freescale/Kconfig -@@ -7,7 +7,8 @@ config NET_VENDOR_FREESCALE - default y - depends on FSL_SOC || QUICC_ENGINE || CPM1 || CPM2 || PPC_MPC512x || \ - M523x || M527x || M5272 || M528x || M520x || M532x || \ -- ARCH_MXC || ARCH_MXS || (PPC_MPC52xx && PPC_BESTCOMM) -+ ARCH_MXC || ARCH_MXS || (PPC_MPC52xx && PPC_BESTCOMM) || \ -+ ARCH_LAYERSCAPE - ---help--- - If you have a network (Ethernet) card belonging to this class, say Y - and read the Ethernet-HOWTO, available from -@@ -58,18 +59,17 @@ source "drivers/net/ethernet/freescale/fs_enet/Kconfig" - - config FSL_PQ_MDIO - tristate "Freescale PQ MDIO" -- depends on FSL_SOC - select PHYLIB - ---help--- - This driver supports the MDIO bus used by the gianfar and UCC drivers. - - config FSL_XGMAC_MDIO - tristate "Freescale XGMAC MDIO" -- depends on FSL_SOC - select PHYLIB - select OF_MDIO - ---help--- -- This driver supports the MDIO bus on the Fman 10G Ethernet MACs. -+ This driver supports the MDIO bus on the Fman 10G Ethernet MACs and -+ on mEMAC (which supports both Clauses 22 and 45) - - config UCC_GETH - tristate "Freescale QE Gigabit Ethernet" -diff --git a/drivers/net/ethernet/freescale/fec_mpc52xx.c b/drivers/net/ethernet/freescale/fec_mpc52xx.c -index ff55fbb..76ff046 100644 ---- a/drivers/net/ethernet/freescale/fec_mpc52xx.c -+++ b/drivers/net/ethernet/freescale/fec_mpc52xx.c -@@ -1057,7 +1057,7 @@ static int mpc52xx_fec_of_resume(struct platform_device *op) - } - #endif - --static struct of_device_id mpc52xx_fec_match[] = { -+static const struct of_device_id mpc52xx_fec_match[] = { - { .compatible = "fsl,mpc5200b-fec", }, - { .compatible = "fsl,mpc5200-fec", }, - { .compatible = "mpc5200-fec", }, -diff --git a/drivers/net/ethernet/freescale/fec_mpc52xx_phy.c b/drivers/net/ethernet/freescale/fec_mpc52xx_phy.c -index e052890..1e647be 100644 ---- a/drivers/net/ethernet/freescale/fec_mpc52xx_phy.c -+++ b/drivers/net/ethernet/freescale/fec_mpc52xx_phy.c -@@ -134,7 +134,7 @@ static int mpc52xx_fec_mdio_remove(struct platform_device *of) - return 0; - } - --static struct of_device_id mpc52xx_fec_mdio_match[] = { -+static const struct of_device_id mpc52xx_fec_mdio_match[] = { - { .compatible = "fsl,mpc5200b-mdio", }, - { .compatible = "fsl,mpc5200-mdio", }, - { .compatible = "mpc5200b-fec-phy", }, -diff --git a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c -index c92c3b7..dc0da6c 100644 ---- a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c -+++ b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c -@@ -886,7 +886,7 @@ static const struct net_device_ops fs_enet_netdev_ops = { - #endif - }; - --static struct of_device_id fs_enet_match[]; -+static const struct of_device_id fs_enet_match[]; - static int fs_enet_probe(struct platform_device *ofdev) - { - const struct of_device_id *match; -@@ -1047,7 +1047,7 @@ static int fs_enet_remove(struct platform_device *ofdev) - return 0; - } - --static struct of_device_id fs_enet_match[] = { -+static const struct of_device_id fs_enet_match[] = { - #ifdef CONFIG_FS_ENET_HAS_SCC - { - .compatible = "fsl,cpm1-scc-enet", -diff --git a/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c b/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c -index 3d3fde6..9ec396b 100644 ---- a/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c -+++ b/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c -@@ -213,7 +213,7 @@ static int fs_enet_mdio_remove(struct platform_device *ofdev) - return 0; - } - --static struct of_device_id fs_enet_mdio_bb_match[] = { -+static const struct of_device_id fs_enet_mdio_bb_match[] = { - { - .compatible = "fsl,cpm2-mdio-bitbang", - }, -diff --git a/drivers/net/ethernet/freescale/fs_enet/mii-fec.c b/drivers/net/ethernet/freescale/fs_enet/mii-fec.c -index ebf5d64..72205b0 100644 ---- a/drivers/net/ethernet/freescale/fs_enet/mii-fec.c -+++ b/drivers/net/ethernet/freescale/fs_enet/mii-fec.c -@@ -95,7 +95,7 @@ static int fs_enet_fec_mii_write(struct mii_bus *bus, int phy_id, int location, - - } - --static struct of_device_id fs_enet_mdio_fec_match[]; -+static const struct of_device_id fs_enet_mdio_fec_match[]; - static int fs_enet_mdio_probe(struct platform_device *ofdev) - { - const struct of_device_id *match; -@@ -208,7 +208,7 @@ static int fs_enet_mdio_remove(struct platform_device *ofdev) - return 0; - } - --static struct of_device_id fs_enet_mdio_fec_match[] = { -+static const struct of_device_id fs_enet_mdio_fec_match[] = { - { - .compatible = "fsl,pq1-fec-mdio", - }, -diff --git a/drivers/net/ethernet/freescale/fsl_pq_mdio.c b/drivers/net/ethernet/freescale/fsl_pq_mdio.c -index 964c6bf..f94fa63 100644 ---- a/drivers/net/ethernet/freescale/fsl_pq_mdio.c -+++ b/drivers/net/ethernet/freescale/fsl_pq_mdio.c -@@ -294,7 +294,7 @@ static void ucc_configure(phys_addr_t start, phys_addr_t end) - - #endif - --static struct of_device_id fsl_pq_mdio_match[] = { -+static const struct of_device_id fsl_pq_mdio_match[] = { - #if defined(CONFIG_GIANFAR) || defined(CONFIG_GIANFAR_MODULE) - { - .compatible = "fsl,gianfar-tbi", -diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c -index 4fdf0aa..0359cfd 100644 ---- a/drivers/net/ethernet/freescale/gianfar.c -+++ b/drivers/net/ethernet/freescale/gianfar.c -@@ -86,11 +86,11 @@ - #include - #include - #include -+#include - - #include - #ifdef CONFIG_PPC - #include --#include - #endif - #include - #include -@@ -1720,8 +1720,10 @@ static void gfar_configure_serdes(struct net_device *dev) - * everything for us? Resetting it takes the link down and requires - * several seconds for it to come back. - */ -- if (phy_read(tbiphy, MII_BMSR) & BMSR_LSTATUS) -+ if (phy_read(tbiphy, MII_BMSR) & BMSR_LSTATUS) { -+ put_device(&tbiphy->dev); - return; -+ } - - /* Single clk mode, mii mode off(for serdes communication) */ - phy_write(tbiphy, MII_TBICON, TBICON_CLK_SELECT); -@@ -3455,7 +3457,7 @@ static noinline void gfar_update_link_state(struct gfar_private *priv) - phy_print_status(phydev); - } - --static struct of_device_id gfar_match[] = -+static const struct of_device_id gfar_match[] = - { - { - .type = "network", -diff --git a/drivers/net/ethernet/freescale/gianfar_ptp.c b/drivers/net/ethernet/freescale/gianfar_ptp.c -index bb56800..c7c75de 100644 ---- a/drivers/net/ethernet/freescale/gianfar_ptp.c -+++ b/drivers/net/ethernet/freescale/gianfar_ptp.c -@@ -554,7 +554,7 @@ static int gianfar_ptp_remove(struct platform_device *dev) - return 0; - } - --static struct of_device_id match_table[] = { -+static const struct of_device_id match_table[] = { - { .compatible = "fsl,etsec-ptp" }, - {}, - }; -diff --git a/drivers/net/ethernet/freescale/ucc_geth.c b/drivers/net/ethernet/freescale/ucc_geth.c -index 3cf0478..741a7d4 100644 ---- a/drivers/net/ethernet/freescale/ucc_geth.c -+++ b/drivers/net/ethernet/freescale/ucc_geth.c -@@ -3930,7 +3930,7 @@ static int ucc_geth_remove(struct platform_device* ofdev) - return 0; - } - --static struct of_device_id ucc_geth_match[] = { -+static const struct of_device_id ucc_geth_match[] = { - { - .type = "network", - .compatible = "ucc_geth", -diff --git a/drivers/net/ethernet/freescale/xgmac_mdio.c b/drivers/net/ethernet/freescale/xgmac_mdio.c -index 6e7db66..7b8fe86 100644 ---- a/drivers/net/ethernet/freescale/xgmac_mdio.c -+++ b/drivers/net/ethernet/freescale/xgmac_mdio.c -@@ -32,31 +32,62 @@ struct tgec_mdio_controller { - __be32 mdio_addr; /* MDIO address */ - } __packed; - -+#define MDIO_STAT_ENC BIT(6) - #define MDIO_STAT_CLKDIV(x) (((x>>1) & 0xff) << 8) --#define MDIO_STAT_BSY (1 << 0) --#define MDIO_STAT_RD_ER (1 << 1) -+#define MDIO_STAT_BSY BIT(0) -+#define MDIO_STAT_RD_ER BIT(1) - #define MDIO_CTL_DEV_ADDR(x) (x & 0x1f) - #define MDIO_CTL_PORT_ADDR(x) ((x & 0x1f) << 5) --#define MDIO_CTL_PRE_DIS (1 << 10) --#define MDIO_CTL_SCAN_EN (1 << 11) --#define MDIO_CTL_POST_INC (1 << 14) --#define MDIO_CTL_READ (1 << 15) -+#define MDIO_CTL_PRE_DIS BIT(10) -+#define MDIO_CTL_SCAN_EN BIT(11) -+#define MDIO_CTL_POST_INC BIT(14) -+#define MDIO_CTL_READ BIT(15) - - #define MDIO_DATA(x) (x & 0xffff) --#define MDIO_DATA_BSY (1 << 31) -+#define MDIO_DATA_BSY BIT(31) -+ -+struct mdio_fsl_priv { -+ struct tgec_mdio_controller __iomem *mdio_base; -+ bool is_little_endian; -+}; -+ -+static u32 xgmac_read32(void __iomem *regs, -+ bool is_little_endian) -+{ -+ if (is_little_endian) -+ return ioread32(regs); -+ else -+ return ioread32be(regs); -+} -+ -+static void xgmac_write32(u32 value, -+ void __iomem *regs, -+ bool is_little_endian) -+{ -+ if (is_little_endian) -+ iowrite32(value, regs); -+ else -+ iowrite32be(value, regs); -+} - - /* - * Wait until the MDIO bus is free - */ - static int xgmac_wait_until_free(struct device *dev, -- struct tgec_mdio_controller __iomem *regs) -+ struct tgec_mdio_controller __iomem *regs, -+ bool is_little_endian) - { -- uint32_t status; -+ unsigned int timeout; - - /* Wait till the bus is free */ -- status = spin_event_timeout( -- !((in_be32(®s->mdio_stat)) & MDIO_STAT_BSY), TIMEOUT, 0); -- if (!status) { -+ timeout = TIMEOUT; -+ while ((xgmac_read32(®s->mdio_stat, is_little_endian) & -+ MDIO_STAT_BSY) && timeout) { -+ cpu_relax(); -+ timeout--; -+ } -+ -+ if (!timeout) { - dev_err(dev, "timeout waiting for bus to be free\n"); - return -ETIMEDOUT; - } -@@ -68,14 +99,20 @@ static int xgmac_wait_until_free(struct device *dev, - * Wait till the MDIO read or write operation is complete - */ - static int xgmac_wait_until_done(struct device *dev, -- struct tgec_mdio_controller __iomem *regs) -+ struct tgec_mdio_controller __iomem *regs, -+ bool is_little_endian) - { -- uint32_t status; -+ unsigned int timeout; - - /* Wait till the MDIO write is complete */ -- status = spin_event_timeout( -- !((in_be32(®s->mdio_data)) & MDIO_DATA_BSY), TIMEOUT, 0); -- if (!status) { -+ timeout = TIMEOUT; -+ while ((xgmac_read32(®s->mdio_stat, is_little_endian) & -+ MDIO_STAT_BSY) && timeout) { -+ cpu_relax(); -+ timeout--; -+ } -+ -+ if (!timeout) { - dev_err(dev, "timeout waiting for operation to complete\n"); - return -ETIMEDOUT; - } -@@ -90,32 +127,47 @@ static int xgmac_wait_until_done(struct device *dev, - */ - static int xgmac_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 value) - { -- struct tgec_mdio_controller __iomem *regs = bus->priv; -- uint16_t dev_addr = regnum >> 16; -+ struct mdio_fsl_priv *priv = (struct mdio_fsl_priv *)bus->priv; -+ struct tgec_mdio_controller __iomem *regs = priv->mdio_base; -+ uint16_t dev_addr; -+ u32 mdio_ctl, mdio_stat; - int ret; -+ bool endian = priv->is_little_endian; -+ -+ mdio_stat = xgmac_read32(®s->mdio_stat, endian); -+ if (regnum & MII_ADDR_C45) { -+ /* Clause 45 (ie 10G) */ -+ dev_addr = (regnum >> 16) & 0x1f; -+ mdio_stat |= MDIO_STAT_ENC; -+ } else { -+ /* Clause 22 (ie 1G) */ -+ dev_addr = regnum & 0x1f; -+ mdio_stat &= ~MDIO_STAT_ENC; -+ } - -- /* Setup the MII Mgmt clock speed */ -- out_be32(®s->mdio_stat, MDIO_STAT_CLKDIV(100)); -+ xgmac_write32(mdio_stat, ®s->mdio_stat, endian); - -- ret = xgmac_wait_until_free(&bus->dev, regs); -+ ret = xgmac_wait_until_free(&bus->dev, regs, endian); - if (ret) - return ret; - - /* Set the port and dev addr */ -- out_be32(®s->mdio_ctl, -- MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr)); -+ mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr); -+ xgmac_write32(mdio_ctl, ®s->mdio_ctl, endian); - - /* Set the register address */ -- out_be32(®s->mdio_addr, regnum & 0xffff); -+ if (regnum & MII_ADDR_C45) { -+ xgmac_write32(regnum & 0xffff, ®s->mdio_addr, endian); - -- ret = xgmac_wait_until_free(&bus->dev, regs); -- if (ret) -- return ret; -+ ret = xgmac_wait_until_free(&bus->dev, regs, endian); -+ if (ret) -+ return ret; -+ } - - /* Write the value to the register */ -- out_be32(®s->mdio_data, MDIO_DATA(value)); -+ xgmac_write32(MDIO_DATA(value), ®s->mdio_data, endian); - -- ret = xgmac_wait_until_done(&bus->dev, regs); -+ ret = xgmac_wait_until_done(&bus->dev, regs, endian); - if (ret) - return ret; - -@@ -129,74 +181,70 @@ static int xgmac_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 val - */ - static int xgmac_mdio_read(struct mii_bus *bus, int phy_id, int regnum) - { -- struct tgec_mdio_controller __iomem *regs = bus->priv; -- uint16_t dev_addr = regnum >> 16; -+ struct mdio_fsl_priv *priv = (struct mdio_fsl_priv *)bus->priv; -+ struct tgec_mdio_controller __iomem *regs = priv->mdio_base; -+ uint16_t dev_addr; -+ uint32_t mdio_stat; - uint32_t mdio_ctl; - uint16_t value; - int ret; -+ bool endian = priv->is_little_endian; -+ -+ mdio_stat = xgmac_read32(®s->mdio_stat, endian); -+ if (regnum & MII_ADDR_C45) { -+ dev_addr = (regnum >> 16) & 0x1f; -+ mdio_stat |= MDIO_STAT_ENC; -+ } else { -+ dev_addr = regnum & 0x1f; -+ mdio_stat &= ~MDIO_STAT_ENC; -+ } - -- /* Setup the MII Mgmt clock speed */ -- out_be32(®s->mdio_stat, MDIO_STAT_CLKDIV(100)); -+ xgmac_write32(mdio_stat, ®s->mdio_stat, endian); - -- ret = xgmac_wait_until_free(&bus->dev, regs); -+ ret = xgmac_wait_until_free(&bus->dev, regs, endian); - if (ret) - return ret; - - /* Set the Port and Device Addrs */ - mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr); -- out_be32(®s->mdio_ctl, mdio_ctl); -+ xgmac_write32(mdio_ctl, ®s->mdio_ctl, endian); - - /* Set the register address */ -- out_be32(®s->mdio_addr, regnum & 0xffff); -+ if (regnum & MII_ADDR_C45) { -+ xgmac_write32(regnum & 0xffff, ®s->mdio_addr, endian); - -- ret = xgmac_wait_until_free(&bus->dev, regs); -- if (ret) -- return ret; -+ ret = xgmac_wait_until_free(&bus->dev, regs, endian); -+ if (ret) -+ return ret; -+ } - - /* Initiate the read */ -- out_be32(®s->mdio_ctl, mdio_ctl | MDIO_CTL_READ); -+ xgmac_write32(mdio_ctl | MDIO_CTL_READ, ®s->mdio_ctl, endian); - -- ret = xgmac_wait_until_done(&bus->dev, regs); -+ ret = xgmac_wait_until_done(&bus->dev, regs, endian); - if (ret) - return ret; - - /* Return all Fs if nothing was there */ -- if (in_be32(®s->mdio_stat) & MDIO_STAT_RD_ER) { -+ if (xgmac_read32(®s->mdio_stat, endian) & MDIO_STAT_RD_ER) { - dev_err(&bus->dev, - "Error while reading PHY%d reg at %d.%hhu\n", - phy_id, dev_addr, regnum); - return 0xffff; - } - -- value = in_be32(®s->mdio_data) & 0xffff; -+ value = xgmac_read32(®s->mdio_data, endian) & 0xffff; - dev_dbg(&bus->dev, "read %04x\n", value); - - return value; - } - --/* Reset the MIIM registers, and wait for the bus to free */ --static int xgmac_mdio_reset(struct mii_bus *bus) --{ -- struct tgec_mdio_controller __iomem *regs = bus->priv; -- int ret; -- -- mutex_lock(&bus->mdio_lock); -- -- /* Setup the MII Mgmt clock speed */ -- out_be32(®s->mdio_stat, MDIO_STAT_CLKDIV(100)); -- -- ret = xgmac_wait_until_free(&bus->dev, regs); -- -- mutex_unlock(&bus->mdio_lock); -- -- return ret; --} -- - static int xgmac_mdio_probe(struct platform_device *pdev) - { - struct device_node *np = pdev->dev.of_node; - struct mii_bus *bus; - struct resource res; -+ struct mdio_fsl_priv *priv; - int ret; - - ret = of_address_to_resource(np, 0, &res); -@@ -205,25 +253,30 @@ static int xgmac_mdio_probe(struct platform_device *pdev) - return ret; - } - -- bus = mdiobus_alloc_size(PHY_MAX_ADDR * sizeof(int)); -+ bus = mdiobus_alloc_size(sizeof(struct mdio_fsl_priv)); - if (!bus) - return -ENOMEM; - - bus->name = "Freescale XGMAC MDIO Bus"; - bus->read = xgmac_mdio_read; - bus->write = xgmac_mdio_write; -- bus->reset = xgmac_mdio_reset; -- bus->irq = bus->priv; - bus->parent = &pdev->dev; - snprintf(bus->id, MII_BUS_ID_SIZE, "%llx", (unsigned long long)res.start); - - /* Set the PHY base address */ -- bus->priv = of_iomap(np, 0); -- if (!bus->priv) { -+ priv = bus->priv; -+ priv->mdio_base = of_iomap(np, 0); -+ if (!priv->mdio_base) { - ret = -ENOMEM; - goto err_ioremap; - } - -+ if (of_get_property(pdev->dev.of_node, -+ "little-endian", NULL)) -+ priv->is_little_endian = true; -+ else -+ priv->is_little_endian = false; -+ - ret = of_mdiobus_register(bus, np); - if (ret) { - dev_err(&pdev->dev, "cannot register MDIO bus\n"); -@@ -235,7 +288,7 @@ static int xgmac_mdio_probe(struct platform_device *pdev) - return 0; - - err_registration: -- iounmap(bus->priv); -+ iounmap(priv->mdio_base); - - err_ioremap: - mdiobus_free(bus); -@@ -254,10 +307,13 @@ static int xgmac_mdio_remove(struct platform_device *pdev) - return 0; - } - --static struct of_device_id xgmac_mdio_match[] = { -+static const struct of_device_id xgmac_mdio_match[] = { - { - .compatible = "fsl,fman-xmdio", - }, -+ { -+ .compatible = "fsl,fman-memac-mdio", -+ }, - {}, - }; - MODULE_DEVICE_TABLE(of, xgmac_mdio_match); -diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.c b/drivers/net/ethernet/intel/igb/e1000_82575.c -index 051ea94..2a04baa 100644 ---- a/drivers/net/ethernet/intel/igb/e1000_82575.c -+++ b/drivers/net/ethernet/intel/igb/e1000_82575.c -@@ -286,6 +286,9 @@ static s32 igb_init_phy_params_82575(struct e1000_hw *hw) - phy->ops.set_d3_lplu_state = igb_set_d3_lplu_state_82580; - phy->ops.force_speed_duplex = igb_phy_force_speed_duplex_m88; - break; -+ case BCM54616_E_PHY_ID: -+ phy->type = e1000_phy_bcm54616; -+ break; - default: - ret_val = -E1000_ERR_PHY; - goto out; -@@ -1550,6 +1553,7 @@ static s32 igb_setup_copper_link_82575(struct e1000_hw *hw) - case e1000_i350: - case e1000_i210: - case e1000_i211: -+ case e1000_i354: - phpm_reg = rd32(E1000_82580_PHY_POWER_MGMT); - phpm_reg &= ~E1000_82580_PM_GO_LINKD; - wr32(E1000_82580_PHY_POWER_MGMT, phpm_reg); -@@ -1593,6 +1597,8 @@ static s32 igb_setup_copper_link_82575(struct e1000_hw *hw) - case e1000_phy_82580: - ret_val = igb_copper_link_setup_82580(hw); - break; -+ case e1000_phy_bcm54616: -+ break; - default: - ret_val = -E1000_ERR_PHY; - break; -diff --git a/drivers/net/ethernet/intel/igb/e1000_defines.h b/drivers/net/ethernet/intel/igb/e1000_defines.h -index 217f813..5322fbf 100644 ---- a/drivers/net/ethernet/intel/igb/e1000_defines.h -+++ b/drivers/net/ethernet/intel/igb/e1000_defines.h -@@ -860,6 +860,7 @@ - #define M88_VENDOR 0x0141 - #define I210_I_PHY_ID 0x01410C00 - #define M88E1543_E_PHY_ID 0x01410EA0 -+#define BCM54616_E_PHY_ID 0x3625D10 - - /* M88E1000 Specific Registers */ - #define M88E1000_PHY_SPEC_CTRL 0x10 /* PHY Specific Control Register */ -diff --git a/drivers/net/ethernet/intel/igb/e1000_hw.h b/drivers/net/ethernet/intel/igb/e1000_hw.h -index 2003b37..d82c96b 100644 ---- a/drivers/net/ethernet/intel/igb/e1000_hw.h -+++ b/drivers/net/ethernet/intel/igb/e1000_hw.h -@@ -128,6 +128,7 @@ enum e1000_phy_type { - e1000_phy_ife, - e1000_phy_82580, - e1000_phy_i210, -+ e1000_phy_bcm54616, - }; - - enum e1000_bus_type { -diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c -index e0f3664..013c1f1 100644 ---- a/drivers/net/ethernet/intel/igb/igb_main.c -+++ b/drivers/net/ethernet/intel/igb/igb_main.c -@@ -108,6 +108,7 @@ static const struct pci_device_id igb_pci_tbl[] = { - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82575EB_COPPER), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82575EB_FIBER_SERDES), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82575GB_QUAD_COPPER), board_82575 }, -+ { PCI_VDEVICE(INTEL, E1000_DEV_ID_I354_SGMII), board_82575 }, - /* required last entry */ - {0, } - }; -diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig -index 75472cf..cdc9f8a 100644 ---- a/drivers/net/phy/Kconfig -+++ b/drivers/net/phy/Kconfig -@@ -14,6 +14,11 @@ if PHYLIB - - comment "MII PHY device drivers" - -+config AQUANTIA_PHY -+ tristate "Drivers for the Aquantia PHYs" -+ ---help--- -+ Currently supports the Aquantia AQ1202, AQ2104, AQR105, AQR405 -+ - config AT803X_PHY - tristate "Drivers for Atheros AT803X PHYs" - ---help--- -@@ -60,6 +65,11 @@ config VITESSE_PHY - ---help--- - Currently supports the vsc8244 - -+config TERANETICS_PHY -+ tristate "Drivers for the Teranetics PHYs" -+ ---help--- -+ Currently supports the Teranetics TN2020 -+ - config SMSC_PHY - tristate "Drivers for SMSC PHYs" - ---help--- -@@ -119,8 +129,8 @@ config MICREL_PHY - Supports the KSZ9021, VSC8201, KS8001 PHYs. - - config FIXED_PHY -- bool "Driver for MDIO Bus/PHY emulation with fixed speed/link PHYs" -- depends on PHYLIB=y -+ tristate "Driver for MDIO Bus/PHY emulation with fixed speed/link PHYs" -+ depends on PHYLIB - ---help--- - Adds the platform "fixed" MDIO Bus to cover the boards that use - PHYs that are not connected to the real MDIO bus. -@@ -202,6 +212,11 @@ config MDIO_BUS_MUX_MMIOREG - the FPGA's registers. - - Currently, only 8-bit registers are supported. -+config FSL_10GBASE_KR -+ tristate "Support for 10GBASE-KR on Freescale XFI interface" -+ depends on OF_MDIO -+ help -+ This module provides a driver for Freescale XFI's 10GBASE-KR. - - config MDIO_BCM_UNIMAC - tristate "Broadcom UniMAC MDIO bus controller" -diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile -index eb3b18b..8ad4ac6 100644 ---- a/drivers/net/phy/Makefile -+++ b/drivers/net/phy/Makefile -@@ -3,12 +3,14 @@ - libphy-objs := phy.o phy_device.o mdio_bus.o - - obj-$(CONFIG_PHYLIB) += libphy.o -+obj-$(CONFIG_AQUANTIA_PHY) += aquantia.o - obj-$(CONFIG_MARVELL_PHY) += marvell.o - obj-$(CONFIG_DAVICOM_PHY) += davicom.o - obj-$(CONFIG_CICADA_PHY) += cicada.o - obj-$(CONFIG_LXT_PHY) += lxt.o - obj-$(CONFIG_QSEMI_PHY) += qsemi.o - obj-$(CONFIG_SMSC_PHY) += smsc.o -+obj-$(CONFIG_TERANETICS_PHY) += teranetics.o - obj-$(CONFIG_VITESSE_PHY) += vitesse.o - obj-$(CONFIG_BROADCOM_PHY) += broadcom.o - obj-$(CONFIG_BCM63XX_PHY) += bcm63xx.o -@@ -17,7 +19,7 @@ obj-$(CONFIG_BCM87XX_PHY) += bcm87xx.o - obj-$(CONFIG_ICPLUS_PHY) += icplus.o - obj-$(CONFIG_REALTEK_PHY) += realtek.o - obj-$(CONFIG_LSI_ET1011C_PHY) += et1011c.o --obj-$(CONFIG_FIXED_PHY) += fixed.o -+obj-$(CONFIG_FIXED_PHY) += fixed_phy.o - obj-$(CONFIG_MDIO_BITBANG) += mdio-bitbang.o - obj-$(CONFIG_MDIO_GPIO) += mdio-gpio.o - obj-$(CONFIG_NATIONAL_PHY) += national.o -@@ -31,6 +33,7 @@ obj-$(CONFIG_AMD_PHY) += amd.o - obj-$(CONFIG_MDIO_BUS_MUX) += mdio-mux.o - obj-$(CONFIG_MDIO_BUS_MUX_GPIO) += mdio-mux-gpio.o - obj-$(CONFIG_MDIO_BUS_MUX_MMIOREG) += mdio-mux-mmioreg.o -+obj-$(CONFIG_FSL_10GBASE_KR) += fsl_10gkr.o - obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o - obj-$(CONFIG_MDIO_MOXART) += mdio-moxart.o - obj-$(CONFIG_AMD_XGBE_PHY) += amd-xgbe-phy.o -diff --git a/drivers/net/phy/aquantia.c b/drivers/net/phy/aquantia.c -new file mode 100644 -index 0000000..d6111af ---- /dev/null -+++ b/drivers/net/phy/aquantia.c -@@ -0,0 +1,201 @@ -+/* -+ * Driver for Aquantia PHY -+ * -+ * Author: Shaohui Xie -+ * -+ * Copyright 2015 Freescale Semiconductor, Inc. -+ * -+ * This file is licensed under the terms of the GNU General Public License -+ * version 2. This program is licensed "as is" without any warranty of any -+ * kind, whether express or implied. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define PHY_ID_AQ1202 0x03a1b445 -+#define PHY_ID_AQ2104 0x03a1b460 -+#define PHY_ID_AQR105 0x03a1b4a2 -+#define PHY_ID_AQR405 0x03a1b4b0 -+ -+#define PHY_AQUANTIA_FEATURES (SUPPORTED_10000baseT_Full | \ -+ SUPPORTED_1000baseT_Full | \ -+ SUPPORTED_100baseT_Full | \ -+ PHY_DEFAULT_FEATURES) -+ -+static int aquantia_config_aneg(struct phy_device *phydev) -+{ -+ phydev->supported = PHY_AQUANTIA_FEATURES; -+ phydev->advertising = phydev->supported; -+ -+ return 0; -+} -+ -+static int aquantia_aneg_done(struct phy_device *phydev) -+{ -+ int reg; -+ -+ reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); -+ return (reg < 0) ? reg : (reg & BMSR_ANEGCOMPLETE); -+} -+ -+static int aquantia_config_intr(struct phy_device *phydev) -+{ -+ int err; -+ -+ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { -+ err = phy_write_mmd(phydev, MDIO_MMD_AN, 0xd401, 1); -+ if (err < 0) -+ return err; -+ -+ err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff00, 1); -+ if (err < 0) -+ return err; -+ -+ err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff01, 0x1001); -+ } else { -+ err = phy_write_mmd(phydev, MDIO_MMD_AN, 0xd401, 0); -+ if (err < 0) -+ return err; -+ -+ err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff00, 0); -+ if (err < 0) -+ return err; -+ -+ err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff01, 0); -+ } -+ -+ return err; -+} -+ -+static int aquantia_ack_interrupt(struct phy_device *phydev) -+{ -+ int reg; -+ -+ reg = phy_read_mmd(phydev, MDIO_MMD_AN, 0xcc01); -+ return (reg < 0) ? reg : 0; -+} -+ -+static int aquantia_read_status(struct phy_device *phydev) -+{ -+ int reg; -+ -+ reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); -+ reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); -+ if (reg & MDIO_STAT1_LSTATUS) -+ phydev->link = 1; -+ else -+ phydev->link = 0; -+ -+ reg = phy_read_mmd(phydev, MDIO_MMD_AN, 0xc800); -+ mdelay(10); -+ reg = phy_read_mmd(phydev, MDIO_MMD_AN, 0xc800); -+ -+ switch (reg) { -+ case 0x9: -+ phydev->speed = SPEED_2500; -+ break; -+ case 0x5: -+ phydev->speed = SPEED_1000; -+ break; -+ case 0x3: -+ phydev->speed = SPEED_100; -+ break; -+ case 0x7: -+ default: -+ phydev->speed = SPEED_10000; -+ break; -+ } -+ phydev->duplex = DUPLEX_FULL; -+ -+ return 0; -+} -+ -+static struct phy_driver aquantia_driver[] = { -+{ -+ .phy_id = PHY_ID_AQ1202, -+ .phy_id_mask = 0xfffffff0, -+ .name = "Aquantia AQ1202", -+ .features = PHY_AQUANTIA_FEATURES, -+ .flags = PHY_HAS_INTERRUPT, -+ .aneg_done = aquantia_aneg_done, -+ .config_aneg = aquantia_config_aneg, -+ .config_intr = aquantia_config_intr, -+ .ack_interrupt = aquantia_ack_interrupt, -+ .read_status = aquantia_read_status, -+ .driver = { .owner = THIS_MODULE,}, -+}, -+{ -+ .phy_id = PHY_ID_AQ2104, -+ .phy_id_mask = 0xfffffff0, -+ .name = "Aquantia AQ2104", -+ .features = PHY_AQUANTIA_FEATURES, -+ .flags = PHY_HAS_INTERRUPT, -+ .aneg_done = aquantia_aneg_done, -+ .config_aneg = aquantia_config_aneg, -+ .config_intr = aquantia_config_intr, -+ .ack_interrupt = aquantia_ack_interrupt, -+ .read_status = aquantia_read_status, -+ .driver = { .owner = THIS_MODULE,}, -+}, -+{ -+ .phy_id = PHY_ID_AQR105, -+ .phy_id_mask = 0xfffffff0, -+ .name = "Aquantia AQR105", -+ .features = PHY_AQUANTIA_FEATURES, -+ .flags = PHY_HAS_INTERRUPT, -+ .aneg_done = aquantia_aneg_done, -+ .config_aneg = aquantia_config_aneg, -+ .config_intr = aquantia_config_intr, -+ .ack_interrupt = aquantia_ack_interrupt, -+ .read_status = aquantia_read_status, -+ .driver = { .owner = THIS_MODULE,}, -+}, -+{ -+ .phy_id = PHY_ID_AQR405, -+ .phy_id_mask = 0xfffffff0, -+ .name = "Aquantia AQR405", -+ .features = PHY_AQUANTIA_FEATURES, -+ .flags = PHY_HAS_INTERRUPT, -+ .aneg_done = aquantia_aneg_done, -+ .config_aneg = aquantia_config_aneg, -+ .config_intr = aquantia_config_intr, -+ .ack_interrupt = aquantia_ack_interrupt, -+ .read_status = aquantia_read_status, -+ .driver = { .owner = THIS_MODULE,}, -+}, -+}; -+ -+static int __init aquantia_init(void) -+{ -+ return phy_drivers_register(aquantia_driver, -+ ARRAY_SIZE(aquantia_driver)); -+} -+ -+static void __exit aquantia_exit(void) -+{ -+ return phy_drivers_unregister(aquantia_driver, -+ ARRAY_SIZE(aquantia_driver)); -+} -+ -+module_init(aquantia_init); -+module_exit(aquantia_exit); -+ -+static struct mdio_device_id __maybe_unused aquantia_tbl[] = { -+ { PHY_ID_AQ1202, 0xfffffff0 }, -+ { PHY_ID_AQ2104, 0xfffffff0 }, -+ { PHY_ID_AQR105, 0xfffffff0 }, -+ { PHY_ID_AQR405, 0xfffffff0 }, -+ { } -+}; -+ -+MODULE_DEVICE_TABLE(mdio, aquantia_tbl); -+ -+MODULE_DESCRIPTION("Aquantia PHY driver"); -+MODULE_AUTHOR("Shaohui Xie "); -+MODULE_LICENSE("GPL v2"); -diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c -index fdc1b41..a4f0886 100644 ---- a/drivers/net/phy/at803x.c -+++ b/drivers/net/phy/at803x.c -@@ -307,6 +307,8 @@ static struct phy_driver at803x_driver[] = { - .flags = PHY_HAS_INTERRUPT, - .config_aneg = genphy_config_aneg, - .read_status = genphy_read_status, -+ .ack_interrupt = at803x_ack_interrupt, -+ .config_intr = at803x_config_intr, - .driver = { - .owner = THIS_MODULE, - }, -@@ -326,6 +328,8 @@ static struct phy_driver at803x_driver[] = { - .flags = PHY_HAS_INTERRUPT, - .config_aneg = genphy_config_aneg, - .read_status = genphy_read_status, -+ .ack_interrupt = at803x_ack_interrupt, -+ .config_intr = at803x_config_intr, - .driver = { - .owner = THIS_MODULE, - }, -diff --git a/drivers/net/phy/fixed.c b/drivers/net/phy/fixed.c -deleted file mode 100644 -index 47872ca..0000000 ---- a/drivers/net/phy/fixed.c -+++ /dev/null -@@ -1,336 +0,0 @@ --/* -- * Fixed MDIO bus (MDIO bus emulation with fixed PHYs) -- * -- * Author: Vitaly Bordug -- * Anton Vorontsov -- * -- * Copyright (c) 2006-2007 MontaVista Software, 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 --#include --#include --#include --#include --#include --#include --#include --#include --#include -- --#define MII_REGS_NUM 29 -- --struct fixed_mdio_bus { -- int irqs[PHY_MAX_ADDR]; -- struct mii_bus *mii_bus; -- struct list_head phys; --}; -- --struct fixed_phy { -- int addr; -- u16 regs[MII_REGS_NUM]; -- struct phy_device *phydev; -- struct fixed_phy_status status; -- int (*link_update)(struct net_device *, struct fixed_phy_status *); -- struct list_head node; --}; -- --static struct platform_device *pdev; --static struct fixed_mdio_bus platform_fmb = { -- .phys = LIST_HEAD_INIT(platform_fmb.phys), --}; -- --static int fixed_phy_update_regs(struct fixed_phy *fp) --{ -- u16 bmsr = BMSR_ANEGCAPABLE; -- u16 bmcr = 0; -- u16 lpagb = 0; -- u16 lpa = 0; -- -- if (fp->status.duplex) { -- bmcr |= BMCR_FULLDPLX; -- -- switch (fp->status.speed) { -- case 1000: -- bmsr |= BMSR_ESTATEN; -- bmcr |= BMCR_SPEED1000; -- lpagb |= LPA_1000FULL; -- break; -- case 100: -- bmsr |= BMSR_100FULL; -- bmcr |= BMCR_SPEED100; -- lpa |= LPA_100FULL; -- break; -- case 10: -- bmsr |= BMSR_10FULL; -- lpa |= LPA_10FULL; -- break; -- default: -- pr_warn("fixed phy: unknown speed\n"); -- return -EINVAL; -- } -- } else { -- switch (fp->status.speed) { -- case 1000: -- bmsr |= BMSR_ESTATEN; -- bmcr |= BMCR_SPEED1000; -- lpagb |= LPA_1000HALF; -- break; -- case 100: -- bmsr |= BMSR_100HALF; -- bmcr |= BMCR_SPEED100; -- lpa |= LPA_100HALF; -- break; -- case 10: -- bmsr |= BMSR_10HALF; -- lpa |= LPA_10HALF; -- break; -- default: -- pr_warn("fixed phy: unknown speed\n"); -- return -EINVAL; -- } -- } -- -- if (fp->status.link) -- bmsr |= BMSR_LSTATUS | BMSR_ANEGCOMPLETE; -- -- if (fp->status.pause) -- lpa |= LPA_PAUSE_CAP; -- -- if (fp->status.asym_pause) -- lpa |= LPA_PAUSE_ASYM; -- -- fp->regs[MII_PHYSID1] = 0; -- fp->regs[MII_PHYSID2] = 0; -- -- fp->regs[MII_BMSR] = bmsr; -- fp->regs[MII_BMCR] = bmcr; -- fp->regs[MII_LPA] = lpa; -- fp->regs[MII_STAT1000] = lpagb; -- -- return 0; --} -- --static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num) --{ -- struct fixed_mdio_bus *fmb = bus->priv; -- struct fixed_phy *fp; -- -- if (reg_num >= MII_REGS_NUM) -- return -1; -- -- /* We do not support emulating Clause 45 over Clause 22 register reads -- * return an error instead of bogus data. -- */ -- switch (reg_num) { -- case MII_MMD_CTRL: -- case MII_MMD_DATA: -- return -1; -- default: -- break; -- } -- -- list_for_each_entry(fp, &fmb->phys, node) { -- if (fp->addr == phy_addr) { -- /* Issue callback if user registered it. */ -- if (fp->link_update) { -- fp->link_update(fp->phydev->attached_dev, -- &fp->status); -- fixed_phy_update_regs(fp); -- } -- return fp->regs[reg_num]; -- } -- } -- -- return 0xFFFF; --} -- --static int fixed_mdio_write(struct mii_bus *bus, int phy_addr, int reg_num, -- u16 val) --{ -- return 0; --} -- --/* -- * If something weird is required to be done with link/speed, -- * network driver is able to assign a function to implement this. -- * May be useful for PHY's that need to be software-driven. -- */ --int fixed_phy_set_link_update(struct phy_device *phydev, -- int (*link_update)(struct net_device *, -- struct fixed_phy_status *)) --{ -- struct fixed_mdio_bus *fmb = &platform_fmb; -- struct fixed_phy *fp; -- -- if (!link_update || !phydev || !phydev->bus) -- return -EINVAL; -- -- list_for_each_entry(fp, &fmb->phys, node) { -- if (fp->addr == phydev->addr) { -- fp->link_update = link_update; -- fp->phydev = phydev; -- return 0; -- } -- } -- -- return -ENOENT; --} --EXPORT_SYMBOL_GPL(fixed_phy_set_link_update); -- --int fixed_phy_add(unsigned int irq, int phy_addr, -- struct fixed_phy_status *status) --{ -- int ret; -- struct fixed_mdio_bus *fmb = &platform_fmb; -- struct fixed_phy *fp; -- -- fp = kzalloc(sizeof(*fp), GFP_KERNEL); -- if (!fp) -- return -ENOMEM; -- -- memset(fp->regs, 0xFF, sizeof(fp->regs[0]) * MII_REGS_NUM); -- -- fmb->irqs[phy_addr] = irq; -- -- fp->addr = phy_addr; -- fp->status = *status; -- -- ret = fixed_phy_update_regs(fp); -- if (ret) -- goto err_regs; -- -- list_add_tail(&fp->node, &fmb->phys); -- -- return 0; -- --err_regs: -- kfree(fp); -- return ret; --} --EXPORT_SYMBOL_GPL(fixed_phy_add); -- --void fixed_phy_del(int phy_addr) --{ -- struct fixed_mdio_bus *fmb = &platform_fmb; -- struct fixed_phy *fp, *tmp; -- -- list_for_each_entry_safe(fp, tmp, &fmb->phys, node) { -- if (fp->addr == phy_addr) { -- list_del(&fp->node); -- kfree(fp); -- return; -- } -- } --} --EXPORT_SYMBOL_GPL(fixed_phy_del); -- --static int phy_fixed_addr; --static DEFINE_SPINLOCK(phy_fixed_addr_lock); -- --struct phy_device *fixed_phy_register(unsigned int irq, -- struct fixed_phy_status *status, -- struct device_node *np) --{ -- struct fixed_mdio_bus *fmb = &platform_fmb; -- struct phy_device *phy; -- int phy_addr; -- int ret; -- -- /* Get the next available PHY address, up to PHY_MAX_ADDR */ -- spin_lock(&phy_fixed_addr_lock); -- if (phy_fixed_addr == PHY_MAX_ADDR) { -- spin_unlock(&phy_fixed_addr_lock); -- return ERR_PTR(-ENOSPC); -- } -- phy_addr = phy_fixed_addr++; -- spin_unlock(&phy_fixed_addr_lock); -- -- ret = fixed_phy_add(PHY_POLL, phy_addr, status); -- if (ret < 0) -- return ERR_PTR(ret); -- -- phy = get_phy_device(fmb->mii_bus, phy_addr, false); -- if (!phy || IS_ERR(phy)) { -- fixed_phy_del(phy_addr); -- return ERR_PTR(-EINVAL); -- } -- -- of_node_get(np); -- phy->dev.of_node = np; -- -- ret = phy_device_register(phy); -- if (ret) { -- phy_device_free(phy); -- of_node_put(np); -- fixed_phy_del(phy_addr); -- return ERR_PTR(ret); -- } -- -- return phy; --} -- --static int __init fixed_mdio_bus_init(void) --{ -- struct fixed_mdio_bus *fmb = &platform_fmb; -- int ret; -- -- pdev = platform_device_register_simple("Fixed MDIO bus", 0, NULL, 0); -- if (IS_ERR(pdev)) { -- ret = PTR_ERR(pdev); -- goto err_pdev; -- } -- -- fmb->mii_bus = mdiobus_alloc(); -- if (fmb->mii_bus == NULL) { -- ret = -ENOMEM; -- goto err_mdiobus_reg; -- } -- -- snprintf(fmb->mii_bus->id, MII_BUS_ID_SIZE, "fixed-0"); -- fmb->mii_bus->name = "Fixed MDIO Bus"; -- fmb->mii_bus->priv = fmb; -- fmb->mii_bus->parent = &pdev->dev; -- fmb->mii_bus->read = &fixed_mdio_read; -- fmb->mii_bus->write = &fixed_mdio_write; -- fmb->mii_bus->irq = fmb->irqs; -- -- ret = mdiobus_register(fmb->mii_bus); -- if (ret) -- goto err_mdiobus_alloc; -- -- return 0; -- --err_mdiobus_alloc: -- mdiobus_free(fmb->mii_bus); --err_mdiobus_reg: -- platform_device_unregister(pdev); --err_pdev: -- return ret; --} --module_init(fixed_mdio_bus_init); -- --static void __exit fixed_mdio_bus_exit(void) --{ -- struct fixed_mdio_bus *fmb = &platform_fmb; -- struct fixed_phy *fp, *tmp; -- -- mdiobus_unregister(fmb->mii_bus); -- mdiobus_free(fmb->mii_bus); -- platform_device_unregister(pdev); -- -- list_for_each_entry_safe(fp, tmp, &fmb->phys, node) { -- list_del(&fp->node); -- kfree(fp); -- } --} --module_exit(fixed_mdio_bus_exit); -- --MODULE_DESCRIPTION("Fixed MDIO bus (MDIO bus emulation with fixed PHYs)"); --MODULE_AUTHOR("Vitaly Bordug"); --MODULE_LICENSE("GPL"); -diff --git a/drivers/net/phy/fixed_phy.c b/drivers/net/phy/fixed_phy.c -new file mode 100644 -index 0000000..88b8194 ---- /dev/null -+++ b/drivers/net/phy/fixed_phy.c -@@ -0,0 +1,370 @@ -+/* -+ * Fixed MDIO bus (MDIO bus emulation with fixed PHYs) -+ * -+ * Author: Vitaly Bordug -+ * Anton Vorontsov -+ * -+ * Copyright (c) 2006-2007 MontaVista Software, 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define MII_REGS_NUM 29 -+ -+struct fixed_mdio_bus { -+ int irqs[PHY_MAX_ADDR]; -+ struct mii_bus *mii_bus; -+ struct list_head phys; -+}; -+ -+struct fixed_phy { -+ int addr; -+ u16 regs[MII_REGS_NUM]; -+ struct phy_device *phydev; -+ struct fixed_phy_status status; -+ int (*link_update)(struct net_device *, struct fixed_phy_status *); -+ struct list_head node; -+}; -+ -+static struct platform_device *pdev; -+static struct fixed_mdio_bus platform_fmb = { -+ .phys = LIST_HEAD_INIT(platform_fmb.phys), -+}; -+ -+static int fixed_phy_update_regs(struct fixed_phy *fp) -+{ -+ u16 bmsr = BMSR_ANEGCAPABLE; -+ u16 bmcr = 0; -+ u16 lpagb = 0; -+ u16 lpa = 0; -+ -+ if (fp->status.duplex) { -+ bmcr |= BMCR_FULLDPLX; -+ -+ switch (fp->status.speed) { -+ case 10000: -+ break; -+ case 1000: -+ bmsr |= BMSR_ESTATEN; -+ bmcr |= BMCR_SPEED1000; -+ lpagb |= LPA_1000FULL; -+ break; -+ case 100: -+ bmsr |= BMSR_100FULL; -+ bmcr |= BMCR_SPEED100; -+ lpa |= LPA_100FULL; -+ break; -+ case 10: -+ bmsr |= BMSR_10FULL; -+ lpa |= LPA_10FULL; -+ break; -+ default: -+ pr_warn("fixed phy: unknown speed\n"); -+ return -EINVAL; -+ } -+ } else { -+ switch (fp->status.speed) { -+ case 10000: -+ break; -+ case 1000: -+ bmsr |= BMSR_ESTATEN; -+ bmcr |= BMCR_SPEED1000; -+ lpagb |= LPA_1000HALF; -+ break; -+ case 100: -+ bmsr |= BMSR_100HALF; -+ bmcr |= BMCR_SPEED100; -+ lpa |= LPA_100HALF; -+ break; -+ case 10: -+ bmsr |= BMSR_10HALF; -+ lpa |= LPA_10HALF; -+ break; -+ default: -+ pr_warn("fixed phy: unknown speed\n"); -+ return -EINVAL; -+ } -+ } -+ -+ if (fp->status.link) -+ bmsr |= BMSR_LSTATUS | BMSR_ANEGCOMPLETE; -+ -+ if (fp->status.pause) -+ lpa |= LPA_PAUSE_CAP; -+ -+ if (fp->status.asym_pause) -+ lpa |= LPA_PAUSE_ASYM; -+ -+ fp->regs[MII_PHYSID1] = 0; -+ fp->regs[MII_PHYSID2] = 0; -+ -+ fp->regs[MII_BMSR] = bmsr; -+ fp->regs[MII_BMCR] = bmcr; -+ fp->regs[MII_LPA] = lpa; -+ fp->regs[MII_STAT1000] = lpagb; -+ -+ return 0; -+} -+ -+static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num) -+{ -+ struct fixed_mdio_bus *fmb = bus->priv; -+ struct fixed_phy *fp; -+ -+ if (reg_num >= MII_REGS_NUM) -+ return -1; -+ -+ /* We do not support emulating Clause 45 over Clause 22 register reads -+ * return an error instead of bogus data. -+ */ -+ switch (reg_num) { -+ case MII_MMD_CTRL: -+ case MII_MMD_DATA: -+ return -1; -+ default: -+ break; -+ } -+ -+ list_for_each_entry(fp, &fmb->phys, node) { -+ if (fp->addr == phy_addr) { -+ /* Issue callback if user registered it. */ -+ if (fp->link_update) { -+ fp->link_update(fp->phydev->attached_dev, -+ &fp->status); -+ fixed_phy_update_regs(fp); -+ } -+ return fp->regs[reg_num]; -+ } -+ } -+ -+ return 0xFFFF; -+} -+ -+static int fixed_mdio_write(struct mii_bus *bus, int phy_addr, int reg_num, -+ u16 val) -+{ -+ return 0; -+} -+ -+/* -+ * If something weird is required to be done with link/speed, -+ * network driver is able to assign a function to implement this. -+ * May be useful for PHY's that need to be software-driven. -+ */ -+int fixed_phy_set_link_update(struct phy_device *phydev, -+ int (*link_update)(struct net_device *, -+ struct fixed_phy_status *)) -+{ -+ struct fixed_mdio_bus *fmb = &platform_fmb; -+ struct fixed_phy *fp; -+ -+ if (!phydev || !phydev->bus) -+ return -EINVAL; -+ -+ list_for_each_entry(fp, &fmb->phys, node) { -+ if (fp->addr == phydev->addr) { -+ fp->link_update = link_update; -+ fp->phydev = phydev; -+ return 0; -+ } -+ } -+ -+ return -ENOENT; -+} -+EXPORT_SYMBOL_GPL(fixed_phy_set_link_update); -+ -+int fixed_phy_update_state(struct phy_device *phydev, -+ const struct fixed_phy_status *status, -+ const struct fixed_phy_status *changed) -+{ -+ struct fixed_mdio_bus *fmb = &platform_fmb; -+ struct fixed_phy *fp; -+ -+ if (!phydev || !phydev->bus) -+ return -EINVAL; -+ -+ list_for_each_entry(fp, &fmb->phys, node) { -+ if (fp->addr == phydev->addr) { -+#define _UPD(x) if (changed->x) \ -+ fp->status.x = status->x -+ _UPD(link); -+ _UPD(speed); -+ _UPD(duplex); -+ _UPD(pause); -+ _UPD(asym_pause); -+#undef _UPD -+ fixed_phy_update_regs(fp); -+ return 0; -+ } -+ } -+ -+ return -ENOENT; -+} -+EXPORT_SYMBOL(fixed_phy_update_state); -+ -+int fixed_phy_add(unsigned int irq, int phy_addr, -+ struct fixed_phy_status *status) -+{ -+ int ret; -+ struct fixed_mdio_bus *fmb = &platform_fmb; -+ struct fixed_phy *fp; -+ -+ fp = kzalloc(sizeof(*fp), GFP_KERNEL); -+ if (!fp) -+ return -ENOMEM; -+ -+ memset(fp->regs, 0xFF, sizeof(fp->regs[0]) * MII_REGS_NUM); -+ -+ fmb->irqs[phy_addr] = irq; -+ -+ fp->addr = phy_addr; -+ fp->status = *status; -+ -+ ret = fixed_phy_update_regs(fp); -+ if (ret) -+ goto err_regs; -+ -+ list_add_tail(&fp->node, &fmb->phys); -+ -+ return 0; -+ -+err_regs: -+ kfree(fp); -+ return ret; -+} -+EXPORT_SYMBOL_GPL(fixed_phy_add); -+ -+void fixed_phy_del(int phy_addr) -+{ -+ struct fixed_mdio_bus *fmb = &platform_fmb; -+ struct fixed_phy *fp, *tmp; -+ -+ list_for_each_entry_safe(fp, tmp, &fmb->phys, node) { -+ if (fp->addr == phy_addr) { -+ list_del(&fp->node); -+ kfree(fp); -+ return; -+ } -+ } -+} -+EXPORT_SYMBOL_GPL(fixed_phy_del); -+ -+static int phy_fixed_addr; -+static DEFINE_SPINLOCK(phy_fixed_addr_lock); -+ -+struct phy_device *fixed_phy_register(unsigned int irq, -+ struct fixed_phy_status *status, -+ struct device_node *np) -+{ -+ struct fixed_mdio_bus *fmb = &platform_fmb; -+ struct phy_device *phy; -+ int phy_addr; -+ int ret; -+ -+ /* Get the next available PHY address, up to PHY_MAX_ADDR */ -+ spin_lock(&phy_fixed_addr_lock); -+ if (phy_fixed_addr == PHY_MAX_ADDR) { -+ spin_unlock(&phy_fixed_addr_lock); -+ return ERR_PTR(-ENOSPC); -+ } -+ phy_addr = phy_fixed_addr++; -+ spin_unlock(&phy_fixed_addr_lock); -+ -+ ret = fixed_phy_add(PHY_POLL, phy_addr, status); -+ if (ret < 0) -+ return ERR_PTR(ret); -+ -+ phy = get_phy_device(fmb->mii_bus, phy_addr, false); -+ if (!phy || IS_ERR(phy)) { -+ fixed_phy_del(phy_addr); -+ return ERR_PTR(-EINVAL); -+ } -+ -+ of_node_get(np); -+ phy->dev.of_node = np; -+ -+ ret = phy_device_register(phy); -+ if (ret) { -+ phy_device_free(phy); -+ of_node_put(np); -+ fixed_phy_del(phy_addr); -+ return ERR_PTR(ret); -+ } -+ -+ return phy; -+} -+EXPORT_SYMBOL_GPL(fixed_phy_register); -+ -+static int __init fixed_mdio_bus_init(void) -+{ -+ struct fixed_mdio_bus *fmb = &platform_fmb; -+ int ret; -+ -+ pdev = platform_device_register_simple("Fixed MDIO bus", 0, NULL, 0); -+ if (IS_ERR(pdev)) { -+ ret = PTR_ERR(pdev); -+ goto err_pdev; -+ } -+ -+ fmb->mii_bus = mdiobus_alloc(); -+ if (fmb->mii_bus == NULL) { -+ ret = -ENOMEM; -+ goto err_mdiobus_reg; -+ } -+ -+ snprintf(fmb->mii_bus->id, MII_BUS_ID_SIZE, "fixed-0"); -+ fmb->mii_bus->name = "Fixed MDIO Bus"; -+ fmb->mii_bus->priv = fmb; -+ fmb->mii_bus->parent = &pdev->dev; -+ fmb->mii_bus->read = &fixed_mdio_read; -+ fmb->mii_bus->write = &fixed_mdio_write; -+ fmb->mii_bus->irq = fmb->irqs; -+ -+ ret = mdiobus_register(fmb->mii_bus); -+ if (ret) -+ goto err_mdiobus_alloc; -+ -+ return 0; -+ -+err_mdiobus_alloc: -+ mdiobus_free(fmb->mii_bus); -+err_mdiobus_reg: -+ platform_device_unregister(pdev); -+err_pdev: -+ return ret; -+} -+module_init(fixed_mdio_bus_init); -+ -+static void __exit fixed_mdio_bus_exit(void) -+{ -+ struct fixed_mdio_bus *fmb = &platform_fmb; -+ struct fixed_phy *fp, *tmp; -+ -+ mdiobus_unregister(fmb->mii_bus); -+ mdiobus_free(fmb->mii_bus); -+ platform_device_unregister(pdev); -+ -+ list_for_each_entry_safe(fp, tmp, &fmb->phys, node) { -+ list_del(&fp->node); -+ kfree(fp); -+ } -+} -+module_exit(fixed_mdio_bus_exit); -+ -+MODULE_DESCRIPTION("Fixed MDIO bus (MDIO bus emulation with fixed PHYs)"); -+MODULE_AUTHOR("Vitaly Bordug"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/net/phy/fsl_10gkr.c b/drivers/net/phy/fsl_10gkr.c -new file mode 100644 -index 0000000..3713726 ---- /dev/null -+++ b/drivers/net/phy/fsl_10gkr.c -@@ -0,0 +1,1467 @@ -+/* Freescale XFI 10GBASE-KR driver. -+ * Author: Shaohui Xie -+ * -+ * Copyright 2014 Freescale Semiconductor, Inc. -+ * -+ * Licensed under the GPL-2 or later. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define FSL_XFI_PCS_PHY_ID 0x7C000012 -+#define FSL_XFI_PCS_PHY_ID2 0x0083e400 -+ -+/* Freescale XFI PCS MMD */ -+#define FSL_XFI_PMD 0x1 -+#define FSL_XFI_PCS 0x3 -+#define FSL_XFI_AN 0x7 -+#define FSL_XFI_VS1 0x1e -+ -+/* Freescale XFI PMD registers */ -+#define FSL_XFI_PMD_CTRL 0x0 -+#define FSL_XFI_KR_PMD_CTRL 0x0096 -+#define FSL_XFI_KR_PMD_STATUS 0x0097 -+#define FSL_XFI_KR_LP_CU 0x0098 -+#define FSL_XFI_KR_LP_STATUS 0x0099 -+#define FSL_XFI_KR_LD_CU 0x009a -+#define FSL_XFI_KR_LD_STATUS 0x009b -+ -+/* PMD define */ -+#define PMD_RESET 0x1 -+#define PMD_STATUS_SUP_STAT 0x4 -+#define PMD_STATUS_FRAME_LOCK 0x2 -+#define TRAIN_EN 0x3 -+#define TRAIN_DISABLE 0x1 -+#define RX_STAT 0x1 -+ -+/* Freescale XFI PCS registers */ -+#define FSL_XFI_PCS_CTRL 0x0 -+#define FSL_XFI_PCS_STATUS 0x1 -+ -+/* Freescale XFI Auto-Negotiation Registers */ -+#define FSL_XFI_AN_CTRL 0x0000 -+#define FSL_XFI_LNK_STATUS 0x0001 -+#define FSL_XFI_AN_AD_1 0x0011 -+#define FSL_XFI_BP_STATUS 0x0030 -+ -+#define XFI_AN_AD1 0x85 -+#define XF_AN_RESTART 0x1200 -+#define XFI_AN_LNK_STAT_UP 0x4 -+ -+/* Freescale XFI Vendor-Specific 1 Registers */ -+#define FSL_XFI_PCS_INTR_EVENT 0x0002 -+#define FSL_XFI_PCS_INTR_MASK 0x0003 -+#define FSL_XFI_AN_INTR_EVENT 0x0004 -+#define FSL_XFI_AN_INTR_MASK 0x0005 -+#define FSL_XFI_LT_INTR_EVENT 0x0006 -+#define FSL_XFI_LT_INTR_MASK 0x0007 -+ -+/* C(-1) */ -+#define BIN_M1 0 -+/* C(1) */ -+#define BIN_LONG 1 -+#define BIN_M1_SEL 6 -+#define BIN_Long_SEL 7 -+#define CDR_SEL_MASK 0x00070000 -+#define BIN_SNAPSHOT_NUM 5 -+#define BIN_M1_THRESHOLD 3 -+#define BIN_LONG_THRESHOLD 2 -+ -+#define PRE_COE_MASK 0x03c00000 -+#define POST_COE_MASK 0x001f0000 -+#define ZERO_COE_MASK 0x00003f00 -+#define PRE_COE_SHIFT 22 -+#define POST_COE_SHIFT 16 -+#define ZERO_COE_SHIFT 8 -+ -+#define PRE_COE_MAX 0x0 -+#define PRE_COE_MIN 0x8 -+#define POST_COE_MAX 0x0 -+#define POST_COE_MIN 0x10 -+#define ZERO_COE_MAX 0x30 -+#define ZERO_COE_MIN 0x0 -+ -+#define TECR0_INIT 0x24200000 -+#define RATIO_PREQ 0x3 -+#define RATIO_PST1Q 0xd -+#define RATIO_EQ 0x20 -+ -+#define GCR1_CTL_SNP_START_MASK 0x00002000 -+#define GCR1_SNP_START_MASK 0x00000040 -+#define RECR1_SNP_DONE_MASK 0x00000004 -+#define RECR1_CTL_SNP_DONE_MASK 0x00000002 -+#define TCSR1_SNP_DATA_MASK 0x0000ffc0 -+#define TCSR1_SNP_DATA_SHIFT 6 -+#define TCSR1_EQ_SNPBIN_SIGN_MASK 0x100 -+ -+#define RECR1_GAINK2_MASK 0x0f000000 -+#define RECR1_GAINK2_SHIFT 24 -+#define RECR1_GAINK3_MASK 0x000f0000 -+#define RECR1_GAINK3_SHIFT 16 -+#define RECR1_OFFSET_MASK 0x00003f80 -+#define RECR1_OFFSET_SHIFT 7 -+#define RECR1_BLW_MASK 0x00000f80 -+#define RECR1_BLW_SHIFT 7 -+#define EYE_CTRL_SHIFT 12 -+#define BASE_WAND_SHIFT 10 -+ -+#define XGKR_TIMEOUT 1050 -+#define AN_ABILITY_MASK 0x9 -+#define AN_10GKR_MASK 0x8 -+#define LT_10GKR_MASK 0x4 -+#define TRAIN_FAIL 0x8 -+ -+#define INCREMENT 1 -+#define DECREMENT 2 -+#define TIMEOUT_LONG 3 -+#define TIMEOUT_M1 3 -+ -+#define RX_READY_MASK 0x8000 -+#define PRESET_MASK 0x2000 -+#define INIT_MASK 0x1000 -+#define COP1_MASK 0x30 -+#define COP1_SHIFT 4 -+#define COZ_MASK 0xc -+#define COZ_SHIFT 2 -+#define COM1_MASK 0x3 -+#define COM1_SHIFT 0 -+#define REQUEST_MASK 0x3f -+#define LD_ALL_MASK (PRESET_MASK | INIT_MASK | \ -+ COP1_MASK | COZ_MASK | COM1_MASK) -+ -+#define FSL_SERDES_INSTANCE1_BASE 0xffe0ea000 -+#define FSL_SERDES_INSTANCE2_BASE 0xffe0eb000 -+#define FSL_LANE_A_BASE 0x800 -+#define FSL_LANE_B_BASE 0x840 -+#define FSL_LANE_C_BASE 0x880 -+#define FSL_LANE_D_BASE 0x8C0 -+#define FSL_LANE_E_BASE 0x900 -+#define FSL_LANE_F_BASE 0x940 -+#define FSL_LANE_G_BASE 0x980 -+#define FSL_LANE_H_BASE 0x9C0 -+#define GCR0_RESET_MASK 0x600000 -+ -+#define NEW_ALGORITHM_TRAIN_TX -+#ifdef NEW_ALGORITHM_TRAIN_TX -+#define FORCE_INC_COP1_NUMBER 0 -+#define FORCE_INC_COM1_NUMBER 1 -+#endif -+ -+enum fsl_xgkr_driver { -+ FSL_XGKR_REV1, -+ FSL_XGKR_REV2, -+ FSL_XGKR_INV -+}; -+ -+static struct phy_driver fsl_xgkr_driver[FSL_XGKR_INV]; -+ -+enum coe_filed { -+ COE_COP1, -+ COE_COZ, -+ COE_COM -+}; -+ -+enum coe_update { -+ COE_NOTUPDATED, -+ COE_UPDATED, -+ COE_MIN, -+ COE_MAX, -+ COE_INV -+}; -+ -+enum serdes_inst { -+ SERDES_1, -+ SERDES_2, -+ SERDES_MAX -+}; -+ -+enum lane_inst { -+ LANE_A, -+ LANE_B, -+ LANE_C, -+ LANE_D, -+ LANE_E, -+ LANE_F, -+ LANE_G, -+ LANE_H, -+ LANE_MAX -+}; -+ -+struct serdes_map { -+ const char *serdes_name; -+ unsigned long serdes_base; -+}; -+ -+struct lane_map { -+ const char *lane_name; -+ unsigned long lane_base; -+}; -+ -+const struct serdes_map s_map[SERDES_MAX] = { -+ {"serdes-1", FSL_SERDES_INSTANCE1_BASE}, -+ {"serdes-2", FSL_SERDES_INSTANCE2_BASE} -+}; -+ -+const struct lane_map l_map[LANE_MAX] = { -+ {"lane-a", FSL_LANE_A_BASE}, -+ {"lane-b", FSL_LANE_B_BASE}, -+ {"lane-c", FSL_LANE_C_BASE}, -+ {"lane-d", FSL_LANE_D_BASE}, -+ {"lane-e", FSL_LANE_E_BASE}, -+ {"lane-f", FSL_LANE_F_BASE}, -+ {"lane-g", FSL_LANE_G_BASE}, -+ {"lane-h", FSL_LANE_H_BASE} -+}; -+ -+struct per_lane_ctrl_status { -+ __be32 gcr0; /* 0x.000 - General Control Register 0 */ -+ __be32 gcr1; /* 0x.004 - General Control Register 1 */ -+ __be32 gcr2; /* 0x.008 - General Control Register 2 */ -+ __be32 resv1; /* 0x.00C - Reserved */ -+ __be32 recr0; /* 0x.010 - Receive Equalization Control Register 0 */ -+ __be32 recr1; /* 0x.014 - Receive Equalization Control Register 1 */ -+ __be32 tecr0; /* 0x.018 - Transmit Equalization Control Register 0 */ -+ __be32 resv2; /* 0x.01C - Reserved */ -+ __be32 tlcr0; /* 0x.020 - TTL Control Register 0 */ -+ __be32 tlcr1; /* 0x.024 - TTL Control Register 1 */ -+ __be32 tlcr2; /* 0x.028 - TTL Control Register 2 */ -+ __be32 tlcr3; /* 0x.02C - TTL Control Register 3 */ -+ __be32 tcsr0; /* 0x.030 - Test Control/Status Register 0 */ -+ __be32 tcsr1; /* 0x.034 - Test Control/Status Register 1 */ -+ __be32 tcsr2; /* 0x.038 - Test Control/Status Register 2 */ -+ __be32 tcsr3; /* 0x.03C - Test Control/Status Register 3 */ -+}; -+ -+struct training_state_machine { -+ bool bin_m1_late_early; -+ bool bin_long_late_early; -+ bool bin_m1_stop; -+ bool bin_long_stop; -+ bool tx_complete; -+ bool an_ok; -+ bool link_up; -+ bool running; -+ bool sent_init; -+ int m1_min_max_cnt; -+ int long_min_max_cnt; -+#ifdef NEW_ALGORITHM_TRAIN_TX -+ int pre_inc; -+ int post_inc; -+#endif -+}; -+ -+struct fsl_xgkr_inst { -+ void *reg_base; -+ struct mii_bus *bus; -+ struct phy_device *phydev; -+ struct training_state_machine t_s_m; -+ u32 ld_update; -+ u32 ld_status; -+ u32 ratio_preq; -+ u32 ratio_pst1q; -+ u32 adpt_eq; -+}; -+ -+struct fsl_xgkr_wk { -+ struct work_struct xgkr_wk; -+ struct list_head xgkr_list; -+ struct fsl_xgkr_inst *xgkr_inst; -+}; -+ -+LIST_HEAD(fsl_xgkr_list); -+ -+static struct timer_list xgkr_timer; -+static int fire_timer; -+static struct workqueue_struct *xgkr_wq; -+ -+static void init_state_machine(struct training_state_machine *s_m) -+{ -+ s_m->bin_m1_late_early = true; -+ s_m->bin_long_late_early = false; -+ s_m->bin_m1_stop = false; -+ s_m->bin_long_stop = false; -+ s_m->tx_complete = false; -+ s_m->an_ok = false; -+ s_m->link_up = false; -+ s_m->running = false; -+ s_m->sent_init = false; -+ s_m->m1_min_max_cnt = 0; -+ s_m->long_min_max_cnt = 0; -+#ifdef NEW_ALGORITHM_TRAIN_TX -+ s_m->pre_inc = FORCE_INC_COM1_NUMBER; -+ s_m->post_inc = FORCE_INC_COP1_NUMBER; -+#endif -+} -+ -+void tune_tecr0(struct fsl_xgkr_inst *inst) -+{ -+ struct per_lane_ctrl_status *reg_base; -+ u32 val; -+ -+ reg_base = (struct per_lane_ctrl_status *)inst->reg_base; -+ -+ val = TECR0_INIT | -+ inst->adpt_eq << ZERO_COE_SHIFT | -+ inst->ratio_preq << PRE_COE_SHIFT | -+ inst->ratio_pst1q << POST_COE_SHIFT; -+ -+ /* reset the lane */ -+ iowrite32be(ioread32be(®_base->gcr0) & ~GCR0_RESET_MASK, -+ ®_base->gcr0); -+ udelay(1); -+ iowrite32be(val, ®_base->tecr0); -+ udelay(1); -+ /* unreset the lane */ -+ iowrite32be(ioread32be(®_base->gcr0) | GCR0_RESET_MASK, -+ ®_base->gcr0); -+ udelay(1); -+} -+ -+static void start_lt(struct phy_device *phydev) -+{ -+ phy_write_mmd(phydev, FSL_XFI_PMD, FSL_XFI_KR_PMD_CTRL, TRAIN_EN); -+} -+ -+static void stop_lt(struct phy_device *phydev) -+{ -+ phy_write_mmd(phydev, FSL_XFI_PMD, FSL_XFI_KR_PMD_CTRL, TRAIN_DISABLE); -+} -+ -+static void reset_gcr0(struct fsl_xgkr_inst *inst) -+{ -+ struct per_lane_ctrl_status *reg_base; -+ -+ reg_base = (struct per_lane_ctrl_status *)inst->reg_base; -+ -+ iowrite32be(ioread32be(®_base->gcr0) & ~GCR0_RESET_MASK, -+ ®_base->gcr0); -+ udelay(1); -+ iowrite32be(ioread32be(®_base->gcr0) | GCR0_RESET_MASK, -+ ®_base->gcr0); -+ udelay(1); -+} -+ -+static void reset_lt(struct phy_device *phydev) -+{ -+ phy_write_mmd(phydev, FSL_XFI_PMD, FSL_XFI_PMD_CTRL, PMD_RESET); -+ phy_write_mmd(phydev, FSL_XFI_PMD, FSL_XFI_KR_PMD_CTRL, TRAIN_DISABLE); -+ phy_write_mmd(phydev, FSL_XFI_PMD, FSL_XFI_KR_LD_CU, 0); -+ phy_write_mmd(phydev, FSL_XFI_PMD, FSL_XFI_KR_LD_STATUS, 0); -+ phy_write_mmd(phydev, FSL_XFI_PMD, FSL_XFI_KR_PMD_STATUS, 0); -+ phy_write_mmd(phydev, FSL_XFI_PMD, FSL_XFI_KR_LP_CU, 0); -+ phy_write_mmd(phydev, FSL_XFI_PMD, FSL_XFI_KR_LP_STATUS, 0); -+} -+ -+static void start_an(struct phy_device *phydev) -+{ -+ reset_lt(phydev); -+ phy_write_mmd(phydev, FSL_XFI_AN, FSL_XFI_AN_AD_1, XFI_AN_AD1); -+ phy_write_mmd(phydev, FSL_XFI_AN, FSL_XFI_AN_CTRL, XF_AN_RESTART); -+} -+ -+static void ld_coe_status(struct fsl_xgkr_inst *inst) -+{ -+ phy_write_mmd(inst->phydev, FSL_XFI_PMD, -+ FSL_XFI_KR_LD_STATUS, inst->ld_status); -+} -+ -+static void ld_coe_update(struct fsl_xgkr_inst *inst) -+{ -+ phy_write_mmd(inst->phydev, FSL_XFI_PMD, -+ FSL_XFI_KR_LD_CU, inst->ld_update); -+} -+ -+static void init_inst(struct fsl_xgkr_inst *inst, int reset) -+{ -+ if (reset) { -+ inst->ratio_preq = RATIO_PREQ; -+ inst->ratio_pst1q = RATIO_PST1Q; -+ inst->adpt_eq = RATIO_EQ; -+ tune_tecr0(inst); -+ } -+ -+ inst->ld_status &= RX_READY_MASK; -+ ld_coe_status(inst); -+ -+ /* init state machine */ -+ init_state_machine(&inst->t_s_m); -+ -+ inst->ld_update = 0; -+ ld_coe_update(inst); -+ -+ inst->ld_status &= ~RX_READY_MASK; -+ ld_coe_status(inst); -+} -+ -+#ifdef NEW_ALGORITHM_TRAIN_TX -+static int get_median_gaink2(u32 *reg) -+{ -+ int gaink2_snap_shot[BIN_SNAPSHOT_NUM]; -+ u32 rx_eq_snp; -+ struct per_lane_ctrl_status *reg_base; -+ int timeout; -+ int i, j, tmp, pos; -+ -+ reg_base = (struct per_lane_ctrl_status *)reg; -+ -+ for (i = 0; i < BIN_SNAPSHOT_NUM; i++) { -+ /* wait RECR1_CTL_SNP_DONE_MASK has cleared */ -+ timeout = 100; -+ while (ioread32be(®_base->recr1) & -+ RECR1_CTL_SNP_DONE_MASK) { -+ udelay(1); -+ timeout--; -+ if (timeout == 0) -+ break; -+ } -+ -+ /* start snap shot */ -+ iowrite32be((ioread32be(®_base->gcr1) | -+ GCR1_CTL_SNP_START_MASK), -+ ®_base->gcr1); -+ -+ /* wait for SNP done */ -+ timeout = 100; -+ while (!(ioread32be(®_base->recr1) & -+ RECR1_CTL_SNP_DONE_MASK)) { -+ udelay(1); -+ timeout--; -+ if (timeout == 0) -+ break; -+ } -+ -+ /* read and save the snap shot */ -+ rx_eq_snp = ioread32be(®_base->recr1); -+ gaink2_snap_shot[i] = (rx_eq_snp & RECR1_GAINK2_MASK) >> -+ RECR1_GAINK2_SHIFT; -+ -+ /* terminate the snap shot by setting GCR1[REQ_CTL_SNP] */ -+ iowrite32be((ioread32be(®_base->gcr1) & -+ ~GCR1_CTL_SNP_START_MASK), -+ ®_base->gcr1); -+ } -+ -+ /* get median of the 5 snap shot */ -+ for (i = 0; i < BIN_SNAPSHOT_NUM - 1; i++) { -+ tmp = gaink2_snap_shot[i]; -+ pos = i; -+ for (j = i + 1; j < BIN_SNAPSHOT_NUM; j++) { -+ if (gaink2_snap_shot[j] < tmp) { -+ tmp = gaink2_snap_shot[j]; -+ pos = j; -+ } -+ } -+ -+ gaink2_snap_shot[pos] = gaink2_snap_shot[i]; -+ gaink2_snap_shot[i] = tmp; -+ } -+ -+ return gaink2_snap_shot[2]; -+} -+#endif -+ -+static bool is_bin_early(int bin_sel, void __iomem *reg) -+{ -+ bool early = false; -+ int bin_snap_shot[BIN_SNAPSHOT_NUM]; -+ int i, negative_count = 0; -+ struct per_lane_ctrl_status *reg_base; -+ int timeout; -+ -+ reg_base = (struct per_lane_ctrl_status *)reg; -+ -+ for (i = 0; i < BIN_SNAPSHOT_NUM; i++) { -+ /* wait RECR1_SNP_DONE_MASK has cleared */ -+ timeout = 100; -+ while ((ioread32be(®_base->recr1) & RECR1_SNP_DONE_MASK)) { -+ udelay(1); -+ timeout--; -+ if (timeout == 0) -+ break; -+ } -+ -+ /* set TCSR1[CDR_SEL] to BinM1/BinLong */ -+ if (bin_sel == BIN_M1) { -+ iowrite32be((ioread32be(®_base->tcsr1) & -+ ~CDR_SEL_MASK) | BIN_M1_SEL, -+ ®_base->tcsr1); -+ } else { -+ iowrite32be((ioread32be(®_base->tcsr1) & -+ ~CDR_SEL_MASK) | BIN_Long_SEL, -+ ®_base->tcsr1); -+ } -+ -+ /* start snap shot */ -+ iowrite32be(ioread32be(®_base->gcr1) | GCR1_SNP_START_MASK, -+ ®_base->gcr1); -+ -+ /* wait for SNP done */ -+ timeout = 100; -+ while (!(ioread32be(®_base->recr1) & RECR1_SNP_DONE_MASK)) { -+ udelay(1); -+ timeout--; -+ if (timeout == 0) -+ break; -+ } -+ -+ /* read and save the snap shot */ -+ bin_snap_shot[i] = (ioread32be(®_base->tcsr1) & -+ TCSR1_SNP_DATA_MASK) >> TCSR1_SNP_DATA_SHIFT; -+ if (bin_snap_shot[i] & TCSR1_EQ_SNPBIN_SIGN_MASK) -+ negative_count++; -+ -+ /* terminate the snap shot by setting GCR1[REQ_CTL_SNP] */ -+ iowrite32be(ioread32be(®_base->gcr1) & ~GCR1_SNP_START_MASK, -+ ®_base->gcr1); -+ } -+ -+ if (((bin_sel == BIN_M1) && negative_count > BIN_M1_THRESHOLD) || -+ ((bin_sel == BIN_LONG && negative_count > BIN_LONG_THRESHOLD))) { -+ early = true; -+ } -+ -+ return early; -+} -+ -+static void train_tx(struct fsl_xgkr_inst *inst) -+{ -+ struct phy_device *phydev = inst->phydev; -+ struct training_state_machine *s_m = &inst->t_s_m; -+ bool bin_m1_early, bin_long_early; -+ u32 lp_status, old_ld_update; -+ u32 status_cop1, status_coz, status_com1; -+ u32 req_cop1, req_coz, req_com1, req_preset, req_init; -+ u32 temp; -+#ifdef NEW_ALGORITHM_TRAIN_TX -+ u32 median_gaink2; -+#endif -+ -+recheck: -+ if (s_m->bin_long_stop && s_m->bin_m1_stop) { -+ s_m->tx_complete = true; -+ inst->ld_status |= RX_READY_MASK; -+ ld_coe_status(inst); -+ /* tell LP we are ready */ -+ phy_write_mmd(phydev, FSL_XFI_PMD, -+ FSL_XFI_KR_PMD_STATUS, RX_STAT); -+ return; -+ } -+ -+ /* We start by checking the current LP status. If we got any responses, -+ * we can clear up the appropriate update request so that the -+ * subsequent code may easily issue new update requests if needed. -+ */ -+ lp_status = phy_read_mmd(phydev, FSL_XFI_PMD, FSL_XFI_KR_LP_STATUS) & -+ REQUEST_MASK; -+ status_cop1 = (lp_status & COP1_MASK) >> COP1_SHIFT; -+ status_coz = (lp_status & COZ_MASK) >> COZ_SHIFT; -+ status_com1 = (lp_status & COM1_MASK) >> COM1_SHIFT; -+ -+ old_ld_update = inst->ld_update; -+ req_cop1 = (old_ld_update & COP1_MASK) >> COP1_SHIFT; -+ req_coz = (old_ld_update & COZ_MASK) >> COZ_SHIFT; -+ req_com1 = (old_ld_update & COM1_MASK) >> COM1_SHIFT; -+ req_preset = old_ld_update & PRESET_MASK; -+ req_init = old_ld_update & INIT_MASK; -+ -+ /* IEEE802.3-2008, 72.6.10.2.3.1 -+ * We may clear PRESET when all coefficients show UPDATED or MAX. -+ */ -+ if (req_preset) { -+ if ((status_cop1 == COE_UPDATED || status_cop1 == COE_MAX) && -+ (status_coz == COE_UPDATED || status_coz == COE_MAX) && -+ (status_com1 == COE_UPDATED || status_com1 == COE_MAX)) { -+ inst->ld_update &= ~PRESET_MASK; -+ } -+ } -+ -+ /* IEEE802.3-2008, 72.6.10.2.3.2 -+ * We may clear INITIALIZE when no coefficients show NOT UPDATED. -+ */ -+ if (req_init) { -+ if (status_cop1 != COE_NOTUPDATED && -+ status_coz != COE_NOTUPDATED && -+ status_com1 != COE_NOTUPDATED) { -+ inst->ld_update &= ~INIT_MASK; -+ } -+ } -+ -+ /* IEEE802.3-2008, 72.6.10.2.3.2 -+ * we send initialize to the other side to ensure default settings -+ * for the LP. Naturally, we should do this only once. -+ */ -+ if (!s_m->sent_init) { -+ if (!lp_status && !(old_ld_update & (LD_ALL_MASK))) { -+ inst->ld_update |= INIT_MASK; -+ s_m->sent_init = true; -+ } -+ } -+ -+ /* IEEE802.3-2008, 72.6.10.2.3.3 -+ * We set coefficient requests to HOLD when we get the information -+ * about any updates On clearing our prior response, we also update -+ * our internal status. -+ */ -+ if (status_cop1 != COE_NOTUPDATED) { -+ if (req_cop1) { -+ inst->ld_update &= ~COP1_MASK; -+#ifdef NEW_ALGORITHM_TRAIN_TX -+ if (s_m->post_inc) { -+ if (req_cop1 == INCREMENT && -+ status_cop1 == COE_MAX) { -+ s_m->post_inc = 0; -+ s_m->bin_long_stop = true; -+ s_m->bin_m1_stop = true; -+ } else { -+ s_m->post_inc -= 1; -+ } -+ -+ ld_coe_update(inst); -+ goto recheck; -+ } -+#endif -+ if ((req_cop1 == DECREMENT && status_cop1 == COE_MIN) || -+ (req_cop1 == INCREMENT && status_cop1 == COE_MAX)) { -+ s_m->long_min_max_cnt++; -+ if (s_m->long_min_max_cnt >= TIMEOUT_LONG) { -+ s_m->bin_long_stop = true; -+ ld_coe_update(inst); -+ goto recheck; -+ } -+ } -+ } -+ } -+ -+ if (status_coz != COE_NOTUPDATED) { -+ if (req_coz) -+ inst->ld_update &= ~COZ_MASK; -+ } -+ -+ if (status_com1 != COE_NOTUPDATED) { -+ if (req_com1) { -+ inst->ld_update &= ~COM1_MASK; -+#ifdef NEW_ALGORITHM_TRAIN_TX -+ if (s_m->pre_inc) { -+ if (req_com1 == INCREMENT && -+ status_com1 == COE_MAX) -+ s_m->pre_inc = 0; -+ else -+ s_m->pre_inc -= 1; -+ -+ ld_coe_update(inst); -+ goto recheck; -+ } -+#endif -+ /* Stop If we have reached the limit for a parameter. */ -+ if ((req_com1 == DECREMENT && status_com1 == COE_MIN) || -+ (req_com1 == INCREMENT && status_com1 == COE_MAX)) { -+ s_m->m1_min_max_cnt++; -+ if (s_m->m1_min_max_cnt >= TIMEOUT_M1) { -+ s_m->bin_m1_stop = true; -+ ld_coe_update(inst); -+ goto recheck; -+ } -+ } -+ } -+ } -+ -+ if (old_ld_update != inst->ld_update) { -+ ld_coe_update(inst); -+ /* Redo these status checks and updates until we have no more -+ * changes, to speed up the overall process. -+ */ -+ goto recheck; -+ } -+ -+ /* Do nothing if we have pending request. */ -+ if ((req_coz || req_com1 || req_cop1)) -+ return; -+ else if (lp_status) -+ /* No pending request but LP status was not reverted to -+ * not updated. -+ */ -+ return; -+ -+#ifdef NEW_ALGORITHM_TRAIN_TX -+ if (!(inst->ld_update & (PRESET_MASK | INIT_MASK))) { -+ if (s_m->pre_inc) { -+ inst->ld_update = INCREMENT << COM1_SHIFT; -+ ld_coe_update(inst); -+ return; -+ } -+ -+ if (status_cop1 != COE_MAX) { -+ median_gaink2 = get_median_gaink2(inst->reg_base); -+ if (median_gaink2 == 0xf) { -+ s_m->post_inc = 1; -+ } else { -+ /* Gaink2 median lower than "F" */ -+ s_m->bin_m1_stop = true; -+ s_m->bin_long_stop = true; -+ goto recheck; -+ } -+ } else { -+ /* C1 MAX */ -+ s_m->bin_m1_stop = true; -+ s_m->bin_long_stop = true; -+ goto recheck; -+ } -+ -+ if (s_m->post_inc) { -+ inst->ld_update = INCREMENT << COP1_SHIFT; -+ ld_coe_update(inst); -+ return; -+ } -+ } -+#endif -+ -+ /* snapshot and select bin */ -+ bin_m1_early = is_bin_early(BIN_M1, inst->reg_base); -+ bin_long_early = is_bin_early(BIN_LONG, inst->reg_base); -+ -+ if (!s_m->bin_m1_stop && !s_m->bin_m1_late_early && bin_m1_early) { -+ s_m->bin_m1_stop = true; -+ goto recheck; -+ } -+ -+ if (!s_m->bin_long_stop && -+ s_m->bin_long_late_early && !bin_long_early) { -+ s_m->bin_long_stop = true; -+ goto recheck; -+ } -+ -+ /* IEEE802.3-2008, 72.6.10.2.3.3 -+ * We only request coefficient updates when no PRESET/INITIALIZE is -+ * pending! We also only request coefficient updates when the -+ * corresponding status is NOT UPDATED and nothing is pending. -+ */ -+ if (!(inst->ld_update & (PRESET_MASK | INIT_MASK))) { -+ if (!s_m->bin_long_stop) { -+ /* BinM1 correction means changing COM1 */ -+ if (!status_com1 && !(inst->ld_update & COM1_MASK)) { -+ /* Avoid BinM1Late by requesting an -+ * immediate decrement. -+ */ -+ if (!bin_m1_early) { -+ /* request decrement c(-1) */ -+ temp = DECREMENT << COM1_SHIFT; -+ inst->ld_update |= temp; -+ ld_coe_update(inst); -+ s_m->bin_m1_late_early = bin_m1_early; -+ return; -+ } -+ } -+ -+ /* BinLong correction means changing COP1 */ -+ if (!status_cop1 && !(inst->ld_update & COP1_MASK)) { -+ /* Locate BinLong transition point (if any) -+ * while avoiding BinM1Late. -+ */ -+ if (bin_long_early) { -+ /* request increment c(1) */ -+ temp = INCREMENT << COP1_SHIFT; -+ inst->ld_update |= temp; -+ } else { -+ /* request decrement c(1) */ -+ temp = DECREMENT << COP1_SHIFT; -+ inst->ld_update |= temp; -+ } -+ -+ ld_coe_update(inst); -+ s_m->bin_long_late_early = bin_long_early; -+ } -+ /* We try to finish BinLong before we do BinM1 */ -+ return; -+ } -+ -+ if (!s_m->bin_m1_stop) { -+ /* BinM1 correction means changing COM1 */ -+ if (!status_com1 && !(inst->ld_update & COM1_MASK)) { -+ /* Locate BinM1 transition point (if any) */ -+ if (bin_m1_early) { -+ /* request increment c(-1) */ -+ temp = INCREMENT << COM1_SHIFT; -+ inst->ld_update |= temp; -+ } else { -+ /* request decrement c(-1) */ -+ temp = DECREMENT << COM1_SHIFT; -+ inst->ld_update |= temp; -+ } -+ -+ ld_coe_update(inst); -+ s_m->bin_m1_late_early = bin_m1_early; -+ } -+ } -+ } -+} -+ -+static int check_an_link(struct phy_device *phydev) -+{ -+ int val; -+ int timeout = 100; -+ -+ while (timeout--) { -+ val = phy_read_mmd(phydev, FSL_XFI_AN, FSL_XFI_LNK_STATUS); -+ if (val & XFI_AN_LNK_STAT_UP) -+ return 1; -+ usleep_range(100, 500); -+ } -+ -+ return 0; -+} -+ -+static int is_link_training_fail(struct phy_device *phydev) -+{ -+ int val; -+ -+ val = phy_read_mmd(phydev, FSL_XFI_PMD, FSL_XFI_KR_PMD_STATUS); -+ if (!(val & TRAIN_FAIL) && (val & RX_STAT)) { -+ /* check LNK_STAT for sure */ -+ if (check_an_link(phydev)) -+ return 0; -+ return 1; -+ } -+ return 1; -+} -+ -+static int check_rx(struct phy_device *phydev) -+{ -+ return phy_read_mmd(phydev, FSL_XFI_PMD, FSL_XFI_KR_LP_STATUS) & -+ RX_READY_MASK; -+} -+ -+/* Coefficient values have hardware restrictions */ -+static int is_ld_valid(u32 *ld_coe) -+{ -+ u32 ratio_pst1q = *ld_coe; -+ u32 adpt_eq = *(ld_coe + 1); -+ u32 ratio_preq = *(ld_coe + 2); -+ -+ if ((ratio_pst1q + adpt_eq + ratio_preq) > 48) -+ return 0; -+ -+ if (((ratio_pst1q + adpt_eq + ratio_preq) * 4) >= -+ ((adpt_eq - ratio_pst1q - ratio_preq) * 17)) -+ return 0; -+ -+ if (ratio_preq > ratio_pst1q) -+ return 0; -+ -+ if (ratio_preq > 8) -+ return 0; -+ -+ if (adpt_eq < 26) -+ return 0; -+ -+ if (ratio_pst1q > 16) -+ return 0; -+ -+ return 1; -+} -+ -+#define VAL_INVALID 0xff -+ -+static const u32 preq_table[] = {0x0, 0x1, 0x3, 0x5, -+ 0x7, 0x9, 0xb, 0xc, VAL_INVALID}; -+static const u32 pst1q_table[] = {0x0, 0x1, 0x3, 0x5, -+ 0x7, 0x9, 0xb, 0xd, 0xf, 0x10, VAL_INVALID}; -+ -+static int is_value_allowed(const u32 *val_table, u32 val) -+{ -+ int i; -+ -+ for (i = 0;; i++) { -+ if (*(val_table + i) == VAL_INVALID) -+ return 0; -+ if (*(val_table + i) == val) -+ return 1; -+ } -+} -+ -+static int inc_dec(struct fsl_xgkr_inst *inst, int field, int request) -+{ -+ u32 ld_limit[3], ld_coe[3], step[3]; -+ -+ ld_coe[0] = inst->ratio_pst1q; -+ ld_coe[1] = inst->adpt_eq; -+ ld_coe[2] = inst->ratio_preq; -+ -+ /* Information specific to the Freescale SerDes for 10GBase-KR: -+ * Incrementing C(+1) means *decrementing* RATIO_PST1Q -+ * Incrementing C(0) means incrementing ADPT_EQ -+ * Incrementing C(-1) means *decrementing* RATIO_PREQ -+ */ -+ step[0] = -1; -+ step[1] = 1; -+ step[2] = -1; -+ -+ switch (request) { -+ case INCREMENT: -+ ld_limit[0] = POST_COE_MAX; -+ ld_limit[1] = ZERO_COE_MAX; -+ ld_limit[2] = PRE_COE_MAX; -+ if (ld_coe[field] != ld_limit[field]) -+ ld_coe[field] += step[field]; -+ else -+ /* MAX */ -+ return 2; -+ break; -+ case DECREMENT: -+ ld_limit[0] = POST_COE_MIN; -+ ld_limit[1] = ZERO_COE_MIN; -+ ld_limit[2] = PRE_COE_MIN; -+ if (ld_coe[field] != ld_limit[field]) -+ ld_coe[field] -= step[field]; -+ else -+ /* MIN */ -+ return 1; -+ break; -+ default: -+ break; -+ } -+ -+ if (is_ld_valid(ld_coe)) { -+ /* accept new ld */ -+ inst->ratio_pst1q = ld_coe[0]; -+ inst->adpt_eq = ld_coe[1]; -+ inst->ratio_preq = ld_coe[2]; -+ /* only some values for preq and pst1q can be used. -+ * for preq: 0x0, 0x1, 0x3, 0x5, 0x7, 0x9, 0xb, 0xc. -+ * for pst1q: 0x0, 0x1, 0x3, 0x5, 0x7, 0x9, 0xb, 0xd, 0xf, 0x10. -+ */ -+ if (!is_value_allowed((const u32 *)&preq_table, ld_coe[2])) { -+ dev_dbg(&inst->phydev->dev, -+ "preq skipped value: %d.\n", ld_coe[2]); -+ return 0; -+ } -+ -+ if (!is_value_allowed((const u32 *)&pst1q_table, ld_coe[0])) { -+ dev_dbg(&inst->phydev->dev, -+ "pst1q skipped value: %d.\n", ld_coe[0]); -+ return 0; -+ } -+ -+ tune_tecr0(inst); -+ } else { -+ if (request == DECREMENT) -+ /* MIN */ -+ return 1; -+ if (request == INCREMENT) -+ /* MAX */ -+ return 2; -+ } -+ -+ return 0; -+} -+ -+static void min_max_updated(struct fsl_xgkr_inst *inst, int field, int new_ld) -+{ -+ u32 ld_coe[] = {COE_UPDATED, COE_MIN, COE_MAX}; -+ u32 mask, val; -+ -+ switch (field) { -+ case COE_COP1: -+ mask = COP1_MASK; -+ val = ld_coe[new_ld] << COP1_SHIFT; -+ break; -+ case COE_COZ: -+ mask = COZ_MASK; -+ val = ld_coe[new_ld] << COZ_SHIFT; -+ break; -+ case COE_COM: -+ mask = COM1_MASK; -+ val = ld_coe[new_ld] << COM1_SHIFT; -+ break; -+ default: -+ return; -+ break; -+ } -+ -+ inst->ld_status &= ~mask; -+ inst->ld_status |= val; -+} -+ -+static void check_request(struct fsl_xgkr_inst *inst, int request) -+{ -+ int cop1_req, coz_req, com_req; -+ int old_status, new_ld_sta; -+ -+ cop1_req = (request & COP1_MASK) >> COP1_SHIFT; -+ coz_req = (request & COZ_MASK) >> COZ_SHIFT; -+ com_req = (request & COM1_MASK) >> COM1_SHIFT; -+ -+ /* IEEE802.3-2008, 72.6.10.2.5 -+ * Ensure we only act on INCREMENT/DECREMENT when we are in NOT UPDATED! -+ */ -+ old_status = inst->ld_status; -+ -+ if (cop1_req && !(inst->ld_status & COP1_MASK)) { -+ new_ld_sta = inc_dec(inst, COE_COP1, cop1_req); -+ min_max_updated(inst, COE_COP1, new_ld_sta); -+ } -+ -+ if (coz_req && !(inst->ld_status & COZ_MASK)) { -+ new_ld_sta = inc_dec(inst, COE_COZ, coz_req); -+ min_max_updated(inst, COE_COZ, new_ld_sta); -+ } -+ -+ if (com_req && !(inst->ld_status & COM1_MASK)) { -+ new_ld_sta = inc_dec(inst, COE_COM, com_req); -+ min_max_updated(inst, COE_COM, new_ld_sta); -+ } -+ -+ if (old_status != inst->ld_status) -+ ld_coe_status(inst); -+ -+} -+ -+static void preset(struct fsl_xgkr_inst *inst) -+{ -+ /* These are all MAX values from the IEEE802.3 perspective! */ -+ inst->ratio_pst1q = POST_COE_MAX; -+ inst->adpt_eq = ZERO_COE_MAX; -+ inst->ratio_preq = PRE_COE_MAX; -+ -+ tune_tecr0(inst); -+ inst->ld_status &= ~(COP1_MASK | COZ_MASK | COM1_MASK); -+ inst->ld_status |= COE_MAX << COP1_SHIFT | -+ COE_MAX << COZ_SHIFT | -+ COE_MAX << COM1_SHIFT; -+ ld_coe_status(inst); -+} -+ -+static void initialize(struct fsl_xgkr_inst *inst) -+{ -+ inst->ratio_preq = RATIO_PREQ; -+ inst->ratio_pst1q = RATIO_PST1Q; -+ inst->adpt_eq = RATIO_EQ; -+ -+ tune_tecr0(inst); -+ inst->ld_status &= ~(COP1_MASK | COZ_MASK | COM1_MASK); -+ inst->ld_status |= COE_UPDATED << COP1_SHIFT | -+ COE_UPDATED << COZ_SHIFT | -+ COE_UPDATED << COM1_SHIFT; -+ ld_coe_status(inst); -+} -+ -+static void train_rx(struct fsl_xgkr_inst *inst) -+{ -+ struct phy_device *phydev = inst->phydev; -+ int request, old_ld_status; -+ -+ /* get request from LP */ -+ request = phy_read_mmd(phydev, FSL_XFI_PMD, FSL_XFI_KR_LP_CU) & -+ (LD_ALL_MASK); -+ old_ld_status = inst->ld_status; -+ -+ /* IEEE802.3-2008, 72.6.10.2.5 -+ * Ensure we always go to NOT UDPATED for status reporting in -+ * response to HOLD requests. -+ * IEEE802.3-2008, 72.6.10.2.3.1/2 -+ * ... but only if PRESET/INITIALIZE are not active to ensure -+ * we keep status until they are released! -+ */ -+ if (!(request & (PRESET_MASK | INIT_MASK))) { -+ if (!(request & COP1_MASK)) -+ inst->ld_status &= ~COP1_MASK; -+ -+ if (!(request & COZ_MASK)) -+ inst->ld_status &= ~COZ_MASK; -+ -+ if (!(request & COM1_MASK)) -+ inst->ld_status &= ~COM1_MASK; -+ -+ if (old_ld_status != inst->ld_status) -+ ld_coe_status(inst); -+ -+ } -+ -+ /* As soon as the LP shows ready, no need to do any more updates. */ -+ if (check_rx(phydev)) { -+ /* LP receiver is ready */ -+ if (inst->ld_status & (COP1_MASK | COZ_MASK | COM1_MASK)) { -+ inst->ld_status &= ~(COP1_MASK | COZ_MASK | COM1_MASK); -+ ld_coe_status(inst); -+ } -+ } else { -+ /* IEEE802.3-2008, 72.6.10.2.3.1/2 -+ * only act on PRESET/INITIALIZE if all status is NOT UPDATED. -+ */ -+ if (request & (PRESET_MASK | INIT_MASK)) { -+ if (!(inst->ld_status & -+ (COP1_MASK | COZ_MASK | COM1_MASK))) { -+ if (request & PRESET_MASK) -+ preset(inst); -+ -+ if (request & INIT_MASK) -+ initialize(inst); -+ } -+ } -+ -+ /* LP Coefficient are not in HOLD */ -+ if (request & REQUEST_MASK) -+ check_request(inst, request & REQUEST_MASK); -+ } -+} -+ -+static void xgkr_wq_state_machine(struct work_struct *work) -+{ -+ struct fsl_xgkr_wk *wk = container_of(work, -+ struct fsl_xgkr_wk, xgkr_wk); -+ struct fsl_xgkr_inst *inst = wk->xgkr_inst; -+ struct training_state_machine *s_m = &inst->t_s_m; -+ struct phy_device *phydev = inst->phydev; -+ int val = 0, i; -+ int an_state, lt_state; -+ unsigned long dead_line; -+ int rx_ok, tx_ok; -+ -+ if (s_m->link_up) { -+ /* check abnormal link down events when link is up, for ex. -+ * the cable is pulled out or link partner is down. -+ */ -+ an_state = phy_read_mmd(phydev, FSL_XFI_AN, FSL_XFI_LNK_STATUS); -+ if (!(an_state & XFI_AN_LNK_STAT_UP)) { -+ dev_info(&phydev->dev, -+ "Detect hotplug, restart training!\n"); -+ init_inst(inst, 1); -+ start_an(phydev); -+ } -+ s_m->running = false; -+ return; -+ } -+ -+ if (!s_m->an_ok) { -+ an_state = phy_read_mmd(phydev, FSL_XFI_AN, FSL_XFI_BP_STATUS); -+ if (!(an_state & AN_10GKR_MASK)) { -+ s_m->running = false; -+ return; -+ } else -+ s_m->an_ok = true; -+ } -+ -+ dev_info(&phydev->dev, "is training.\n"); -+ -+ start_lt(phydev); -+ for (i = 0; i < 2;) { -+ /* i < 1 also works, but start one more try immediately when -+ * failed can adjust our training frequency to match other -+ * devices. This can help the link being established more -+ * quickly. -+ */ -+ dead_line = jiffies + msecs_to_jiffies(500); -+ while (time_before(jiffies, dead_line)) { -+ val = phy_read_mmd(phydev, FSL_XFI_PMD, -+ FSL_XFI_KR_PMD_STATUS); -+ if (val & TRAIN_FAIL) { -+ /* LT failed already, reset lane to avoid -+ * it run into hanging, then start LT again. -+ */ -+ reset_gcr0(inst); -+ start_lt(phydev); -+ } else if (val & PMD_STATUS_SUP_STAT && -+ val & PMD_STATUS_FRAME_LOCK) -+ break; -+ usleep_range(100, 500); -+ } -+ -+ if (!(val & PMD_STATUS_FRAME_LOCK && -+ val & PMD_STATUS_SUP_STAT)) { -+ i++; -+ continue; -+ } -+ -+ /* init process */ -+ rx_ok = tx_ok = false; -+ /* the LT should be finished in 500ms, failed or OK. */ -+ dead_line = jiffies + msecs_to_jiffies(500); -+ -+ while (time_before(jiffies, dead_line)) { -+ /* check if the LT is already failed */ -+ lt_state = phy_read_mmd(phydev, FSL_XFI_PMD, -+ FSL_XFI_KR_PMD_STATUS); -+ if (lt_state & TRAIN_FAIL) { -+ reset_gcr0(inst); -+ break; -+ } -+ -+ rx_ok = check_rx(phydev); -+ tx_ok = s_m->tx_complete; -+ -+ if (rx_ok && tx_ok) -+ break; -+ -+ if (!rx_ok) -+ train_rx(inst); -+ -+ if (!tx_ok) -+ train_tx(inst); -+ usleep_range(100, 500); -+ } -+ -+ i++; -+ /* check LT result */ -+ if (is_link_training_fail(phydev)) { -+ /* reset state machine */ -+ init_inst(inst, 0); -+ continue; -+ } else { -+ stop_lt(phydev); -+ s_m->running = false; -+ s_m->link_up = true; -+ dev_info(&phydev->dev, "LT training is SUCCEEDED!\n"); -+ break; -+ } -+ } -+ -+ if (!s_m->link_up) { -+ /* reset state machine */ -+ init_inst(inst, 0); -+ } -+} -+ -+static void xgkr_timer_handle(unsigned long arg) -+{ -+ struct list_head *pos; -+ struct fsl_xgkr_wk *wk; -+ struct fsl_xgkr_inst *xgkr_inst; -+ struct phy_device *phydev; -+ struct training_state_machine *s_m; -+ -+ list_for_each(pos, &fsl_xgkr_list) { -+ wk = list_entry(pos, struct fsl_xgkr_wk, xgkr_list); -+ xgkr_inst = wk->xgkr_inst; -+ phydev = xgkr_inst->phydev; -+ s_m = &xgkr_inst->t_s_m; -+ -+ if (!s_m->running && (!s_m->an_ok || s_m->link_up)) { -+ s_m->running = true; -+ queue_work(xgkr_wq, (struct work_struct *)wk); -+ } -+ } -+ -+ if (!list_empty(&fsl_xgkr_list)) -+ mod_timer(&xgkr_timer, -+ jiffies + msecs_to_jiffies(XGKR_TIMEOUT)); -+} -+ -+static int fsl_xgkr_bind_serdes(const char *lane_name, -+ struct phy_device *phydev) -+{ -+ unsigned long serdes_base; -+ unsigned long lane_base; -+ int i; -+ -+ for (i = 0; i < SERDES_MAX; i++) { -+ if (strstr(lane_name, s_map[i].serdes_name)) { -+ serdes_base = s_map[i].serdes_base; -+ break; -+ } -+ } -+ -+ if (i == SERDES_MAX) -+ goto serdes_err; -+ -+ for (i = 0; i < LANE_MAX; i++) { -+ if (strstr(lane_name, l_map[i].lane_name)) { -+ lane_base = l_map[i].lane_base; -+ break; -+ } -+ } -+ -+ if (i == LANE_MAX) -+ goto lane_err; -+ -+ phydev->priv = ioremap(serdes_base + lane_base, -+ sizeof(struct per_lane_ctrl_status)); -+ if (!phydev->priv) -+ return -ENOMEM; -+ -+ return 0; -+ -+serdes_err: -+ dev_err(&phydev->dev, "Unknown SerDes name"); -+ return -EINVAL; -+lane_err: -+ dev_err(&phydev->dev, "Unknown Lane name"); -+ return -EINVAL; -+} -+ -+static int fsl_xgkr_probe(struct phy_device *phydev) -+{ -+ struct fsl_xgkr_inst *xgkr_inst; -+ struct fsl_xgkr_wk *xgkr_wk; -+ struct device_node *child; -+ const char *lane_name; -+ int len; -+ -+ child = phydev->dev.of_node; -+ -+ /* if there is lane-instance property, 10G-KR need to run */ -+ lane_name = of_get_property(child, "lane-instance", &len); -+ if (!lane_name || (fsl_xgkr_bind_serdes(lane_name, phydev))) -+ return 0; -+ -+ xgkr_inst = kzalloc(sizeof(struct fsl_xgkr_inst), GFP_KERNEL); -+ if (!xgkr_inst) -+ goto mem_err1; -+ -+ xgkr_inst->reg_base = phydev->priv; -+ -+ xgkr_inst->bus = phydev->bus; -+ -+ xgkr_inst->phydev = phydev; -+ -+ init_inst(xgkr_inst, 1); -+ -+ xgkr_wk = kzalloc(sizeof(struct fsl_xgkr_wk), GFP_KERNEL); -+ if (!xgkr_wk) -+ goto mem_err2; -+ -+ xgkr_wk->xgkr_inst = xgkr_inst; -+ phydev->priv = xgkr_wk; -+ -+ list_add(&xgkr_wk->xgkr_list, &fsl_xgkr_list); -+ -+ if (!fire_timer) { -+ setup_timer(&xgkr_timer, xgkr_timer_handle, -+ (unsigned long)&fsl_xgkr_list); -+ mod_timer(&xgkr_timer, -+ jiffies + msecs_to_jiffies(XGKR_TIMEOUT)); -+ fire_timer = 1; -+ xgkr_wq = create_workqueue("fsl_xgkr"); -+ } -+ INIT_WORK((struct work_struct *)xgkr_wk, xgkr_wq_state_machine); -+ -+ /* start auto-negotiation to detect link partner */ -+ start_an(phydev); -+ -+ return 0; -+mem_err2: -+ kfree(xgkr_inst); -+mem_err1: -+ dev_err(&phydev->dev, "failed to allocate memory!\n"); -+ return -ENOMEM; -+} -+ -+static int fsl_xgkr_config_init(struct phy_device *phydev) -+{ -+ return 0; -+} -+ -+static int fsl_xgkr_config_aneg(struct phy_device *phydev) -+{ -+ return 0; -+} -+ -+static void fsl_xgkr_remove(struct phy_device *phydev) -+{ -+ struct fsl_xgkr_wk *wk = (struct fsl_xgkr_wk *)phydev->priv; -+ struct fsl_xgkr_inst *xgkr_inst = wk->xgkr_inst; -+ struct list_head *this, *next; -+ struct fsl_xgkr_wk *tmp; -+ -+ list_for_each_safe(this, next, &fsl_xgkr_list) { -+ tmp = list_entry(this, struct fsl_xgkr_wk, xgkr_list); -+ if (tmp == wk) { -+ cancel_work_sync((struct work_struct *)wk); -+ list_del(this); -+ } -+ } -+ -+ if (list_empty(&fsl_xgkr_list)) -+ del_timer(&xgkr_timer); -+ -+ if (xgkr_inst->reg_base) -+ iounmap(xgkr_inst->reg_base); -+ -+ kfree(xgkr_inst); -+ kfree(wk); -+} -+ -+static int fsl_xgkr_read_status(struct phy_device *phydev) -+{ -+ int val = phy_read_mmd(phydev, FSL_XFI_AN, FSL_XFI_LNK_STATUS); -+ -+ phydev->speed = SPEED_10000; -+ phydev->duplex = 1; -+ -+ if (val & XFI_AN_LNK_STAT_UP) -+ phydev->link = 1; -+ else -+ phydev->link = 0; -+ -+ return 0; -+} -+ -+static int fsl_xgkr_match_phy_device(struct phy_device *phydev) -+{ -+ return phydev->c45_ids.device_ids[3] == FSL_XFI_PCS_PHY_ID; -+} -+ -+static int fsl_xgkr_match_phy_device2(struct phy_device *phydev) -+{ -+ return phydev->c45_ids.device_ids[3] == FSL_XFI_PCS_PHY_ID2; -+} -+ -+static struct phy_driver fsl_xgkr_driver[] = { -+ { -+ .phy_id = FSL_XFI_PCS_PHY_ID, -+ .name = "Freescale 10G KR Rev1", -+ .phy_id_mask = 0xffffffff, -+ .features = PHY_GBIT_FEATURES, -+ .flags = PHY_HAS_INTERRUPT, -+ .probe = fsl_xgkr_probe, -+ .config_init = &fsl_xgkr_config_init, -+ .config_aneg = &fsl_xgkr_config_aneg, -+ .read_status = &fsl_xgkr_read_status, -+ .match_phy_device = fsl_xgkr_match_phy_device, -+ .remove = fsl_xgkr_remove, -+ .driver = { .owner = THIS_MODULE,}, -+ }, -+ { -+ .phy_id = FSL_XFI_PCS_PHY_ID2, -+ .name = "Freescale 10G KR Rev2", -+ .phy_id_mask = 0xffffffff, -+ .features = PHY_GBIT_FEATURES, -+ .flags = PHY_HAS_INTERRUPT, -+ .probe = fsl_xgkr_probe, -+ .config_init = &fsl_xgkr_config_init, -+ .config_aneg = &fsl_xgkr_config_aneg, -+ .read_status = &fsl_xgkr_read_status, -+ .match_phy_device = fsl_xgkr_match_phy_device2, -+ .remove = fsl_xgkr_remove, -+ .driver = { .owner = THIS_MODULE,}, -+ }, -+}; -+ -+static int __init fsl_xgkr_init(void) -+{ -+ return phy_drivers_register(fsl_xgkr_driver, -+ ARRAY_SIZE(fsl_xgkr_driver)); -+} -+ -+static void __exit fsl_xgkr_exit(void) -+{ -+ phy_drivers_unregister(fsl_xgkr_driver, -+ ARRAY_SIZE(fsl_xgkr_driver)); -+} -+ -+module_init(fsl_xgkr_init); -+module_exit(fsl_xgkr_exit); -+ -+static struct mdio_device_id __maybe_unused freescale_tbl[] = { -+ { FSL_XFI_PCS_PHY_ID, 0xffffffff }, -+ { FSL_XFI_PCS_PHY_ID2, 0xffffffff }, -+ { } -+}; -+ -+MODULE_DEVICE_TABLE(mdio, freescale_tbl); -diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c -index 225c033..969a198 100644 ---- a/drivers/net/phy/marvell.c -+++ b/drivers/net/phy/marvell.c -@@ -50,6 +50,7 @@ - #define MII_M1011_PHY_SCR 0x10 - #define MII_M1011_PHY_SCR_AUTO_CROSS 0x0060 - -+#define MII_M1145_PHY_EXT_ADDR_PAGE 0x16 - #define MII_M1145_PHY_EXT_SR 0x1b - #define MII_M1145_PHY_EXT_CR 0x14 - #define MII_M1145_RGMII_RX_DELAY 0x0080 -@@ -495,6 +496,16 @@ static int m88e1111_config_init(struct phy_device *phydev) - err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp); - if (err < 0) - return err; -+ -+ /* make sure copper is selected */ -+ err = phy_read(phydev, MII_M1145_PHY_EXT_ADDR_PAGE); -+ if (err < 0) -+ return err; -+ -+ err = phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE, -+ err & (~0xff)); -+ if (err < 0) -+ return err; - } - - if (phydev->interface == PHY_INTERFACE_MODE_RTBI) { -diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c -index 50051f2..accd605 100644 ---- a/drivers/net/phy/mdio_bus.c -+++ b/drivers/net/phy/mdio_bus.c -@@ -288,8 +288,11 @@ int mdiobus_register(struct mii_bus *bus) - - error: - while (--i >= 0) { -- if (bus->phy_map[i]) -- device_unregister(&bus->phy_map[i]->dev); -+ struct phy_device *phydev = bus->phy_map[i]; -+ if (phydev) { -+ phy_device_remove(phydev); -+ phy_device_free(phydev); -+ } - } - device_del(&bus->dev); - return err; -@@ -305,9 +308,11 @@ void mdiobus_unregister(struct mii_bus *bus) - - device_del(&bus->dev); - for (i = 0; i < PHY_MAX_ADDR; i++) { -- if (bus->phy_map[i]) -- device_unregister(&bus->phy_map[i]->dev); -- bus->phy_map[i] = NULL; -+ struct phy_device *phydev = bus->phy_map[i]; -+ if (phydev) { -+ phy_device_remove(phydev); -+ phy_device_free(phydev); -+ } - } - } - EXPORT_SYMBOL(mdiobus_unregister); -@@ -421,6 +426,8 @@ static int mdio_bus_match(struct device *dev, struct device_driver *drv) - { - struct phy_device *phydev = to_phy_device(dev); - struct phy_driver *phydrv = to_phy_driver(drv); -+ const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids); -+ int i; - - if (of_driver_match_device(dev, drv)) - return 1; -@@ -428,8 +435,21 @@ static int mdio_bus_match(struct device *dev, struct device_driver *drv) - if (phydrv->match_phy_device) - return phydrv->match_phy_device(phydev); - -- return (phydrv->phy_id & phydrv->phy_id_mask) == -- (phydev->phy_id & phydrv->phy_id_mask); -+ if (phydev->is_c45) { -+ for (i = 1; i < num_ids; i++) { -+ if (!(phydev->c45_ids.devices_in_package & (1 << i))) -+ continue; -+ -+ if ((phydrv->phy_id & phydrv->phy_id_mask) == -+ (phydev->c45_ids.device_ids[i] & -+ phydrv->phy_id_mask)) -+ return 1; -+ } -+ return 0; -+ } else { -+ return (phydrv->phy_id & phydrv->phy_id_mask) == -+ (phydev->phy_id & phydrv->phy_id_mask); -+ } - } - - #ifdef CONFIG_PM -diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c -index 91d6d03..840075e 100644 ---- a/drivers/net/phy/phy.c -+++ b/drivers/net/phy/phy.c -@@ -768,6 +768,7 @@ void phy_state_machine(struct work_struct *work) - container_of(dwork, struct phy_device, state_queue); - bool needs_aneg = false, do_suspend = false, do_resume = false; - int err = 0; -+ int old_link; - - mutex_lock(&phydev->lock); - -@@ -814,6 +815,9 @@ void phy_state_machine(struct work_struct *work) - needs_aneg = true; - break; - case PHY_NOLINK: -+ if (phy_interrupt_is_valid(phydev)) -+ break; -+ - err = phy_read_status(phydev); - if (err) - break; -@@ -851,11 +855,18 @@ void phy_state_machine(struct work_struct *work) - phydev->adjust_link(phydev->attached_dev); - break; - case PHY_RUNNING: -- /* Only register a CHANGE if we are -- * polling or ignoring interrupts -+ /* Only register a CHANGE if we are polling or ignoring -+ * interrupts and link changed since latest checking. - */ -- if (!phy_interrupt_is_valid(phydev)) -- phydev->state = PHY_CHANGELINK; -+ if (!phy_interrupt_is_valid(phydev)) { -+ old_link = phydev->link; -+ err = phy_read_status(phydev); -+ if (err) -+ break; -+ -+ if (old_link != phydev->link) -+ phydev->state = PHY_CHANGELINK; -+ } - break; - case PHY_CHANGELINK: - err = phy_read_status(phydev); -diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c -index 70a0d88..07b1aa9 100644 ---- a/drivers/net/phy/phy_device.c -+++ b/drivers/net/phy/phy_device.c -@@ -205,6 +205,37 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, - } - EXPORT_SYMBOL(phy_device_create); - -+/* get_phy_c45_devs_in_pkg - reads a MMD's devices in package registers. -+ * @bus: the target MII bus -+ * @addr: PHY address on the MII bus -+ * @dev_addr: MMD address in the PHY. -+ * @devices_in_package: where to store the devices in package information. -+ * -+ * Description: reads devices in package registers of a MMD at @dev_addr -+ * from PHY at @addr on @bus. -+ * -+ * Returns: 0 on success, -EIO on failure. -+ */ -+static int get_phy_c45_devs_in_pkg(struct mii_bus *bus, int addr, int dev_addr, -+ u32 *devices_in_package) -+{ -+ int phy_reg, reg_addr; -+ -+ reg_addr = MII_ADDR_C45 | dev_addr << 16 | MDIO_DEVS2; -+ phy_reg = mdiobus_read(bus, addr, reg_addr); -+ if (phy_reg < 0) -+ return -EIO; -+ *devices_in_package = (phy_reg & 0xffff) << 16; -+ -+ reg_addr = MII_ADDR_C45 | dev_addr << 16 | MDIO_DEVS1; -+ phy_reg = mdiobus_read(bus, addr, reg_addr); -+ if (phy_reg < 0) -+ return -EIO; -+ *devices_in_package |= (phy_reg & 0xffff); -+ -+ return 0; -+} -+ - /** - * get_phy_c45_ids - reads the specified addr for its 802.3-c45 IDs. - * @bus: the target MII bus -@@ -223,31 +254,32 @@ static int get_phy_c45_ids(struct mii_bus *bus, int addr, u32 *phy_id, - int phy_reg; - int i, reg_addr; - const int num_ids = ARRAY_SIZE(c45_ids->device_ids); -+ u32 *devs = &c45_ids->devices_in_package; - -- /* Find first non-zero Devices In package. Device -- * zero is reserved, so don't probe it. -+ /* Find first non-zero Devices In package. Device zero is reserved -+ * for 802.3 c45 complied PHYs, so don't probe it at first. - */ -- for (i = 1; -- i < num_ids && c45_ids->devices_in_package == 0; -- i++) { -- reg_addr = MII_ADDR_C45 | i << 16 | MDIO_DEVS2; -- phy_reg = mdiobus_read(bus, addr, reg_addr); -- if (phy_reg < 0) -- return -EIO; -- c45_ids->devices_in_package = (phy_reg & 0xffff) << 16; -- -- reg_addr = MII_ADDR_C45 | i << 16 | MDIO_DEVS1; -- phy_reg = mdiobus_read(bus, addr, reg_addr); -+ for (i = 1; i < num_ids && *devs == 0; i++) { -+ phy_reg = get_phy_c45_devs_in_pkg(bus, addr, i, devs); - if (phy_reg < 0) - return -EIO; -- c45_ids->devices_in_package |= (phy_reg & 0xffff); - -- /* If mostly Fs, there is no device there, -- * let's get out of here. -- */ -- if ((c45_ids->devices_in_package & 0x1fffffff) == 0x1fffffff) { -- *phy_id = 0xffffffff; -- return 0; -+ if ((*devs & 0x1fffffff) == 0x1fffffff) { -+ /* If mostly Fs, there is no device there, -+ * then let's continue to probe more, as some -+ * 10G PHYs have zero Devices In package, -+ * e.g. Cortina CS4315/CS4340 PHY. -+ */ -+ phy_reg = get_phy_c45_devs_in_pkg(bus, addr, 0, devs); -+ if (phy_reg < 0) -+ return -EIO; -+ /* no device there, let's get out of here */ -+ if ((*devs & 0x1fffffff) == 0x1fffffff) { -+ *phy_id = 0xffffffff; -+ return 0; -+ } else { -+ break; -+ } - } - } - -@@ -376,6 +408,24 @@ int phy_device_register(struct phy_device *phydev) - EXPORT_SYMBOL(phy_device_register); - - /** -+ * phy_device_remove - Remove a previously registered phy device from the MDIO bus -+ * @phydev: phy_device structure to remove -+ * -+ * This doesn't free the phy_device itself, it merely reverses the effects -+ * of phy_device_register(). Use phy_device_free() to free the device -+ * after calling this function. -+ */ -+void phy_device_remove(struct phy_device *phydev) -+{ -+ struct mii_bus *bus = phydev->bus; -+ int addr = phydev->addr; -+ -+ device_del(&phydev->dev); -+ bus->phy_map[addr] = NULL; -+} -+EXPORT_SYMBOL(phy_device_remove); -+ -+/** - * phy_find_first - finds the first PHY device on the bus - * @bus: the target MII bus - */ -diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c -index 45483fd..badcf24 100644 ---- a/drivers/net/phy/realtek.c -+++ b/drivers/net/phy/realtek.c -@@ -22,8 +22,12 @@ - #define RTL821x_INER 0x12 - #define RTL821x_INER_INIT 0x6400 - #define RTL821x_INSR 0x13 -+#define RTL8211E_INER_LINK_STATUS 0x400 - --#define RTL8211E_INER_LINK_STATUS 0x400 -+#define RTL8211F_INER_LINK_STATUS 0x0010 -+#define RTL8211F_INSR 0x1d -+#define RTL8211F_PAGE_SELECT 0x1f -+#define RTL8211F_TX_DELAY 0x100 - - MODULE_DESCRIPTION("Realtek PHY driver"); - MODULE_AUTHOR("Johnson Leung"); -@@ -38,6 +42,18 @@ static int rtl821x_ack_interrupt(struct phy_device *phydev) - return (err < 0) ? err : 0; - } - -+static int rtl8211f_ack_interrupt(struct phy_device *phydev) -+{ -+ int err; -+ -+ phy_write(phydev, RTL8211F_PAGE_SELECT, 0xa43); -+ err = phy_read(phydev, RTL8211F_INSR); -+ /* restore to default page 0 */ -+ phy_write(phydev, RTL8211F_PAGE_SELECT, 0x0); -+ -+ return (err < 0) ? err : 0; -+} -+ - static int rtl8211b_config_intr(struct phy_device *phydev) - { - int err; -@@ -64,6 +80,41 @@ static int rtl8211e_config_intr(struct phy_device *phydev) - return err; - } - -+static int rtl8211f_config_intr(struct phy_device *phydev) -+{ -+ int err; -+ -+ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) -+ err = phy_write(phydev, RTL821x_INER, -+ RTL8211F_INER_LINK_STATUS); -+ else -+ err = phy_write(phydev, RTL821x_INER, 0); -+ -+ return err; -+} -+ -+static int rtl8211f_config_init(struct phy_device *phydev) -+{ -+ int ret; -+ u16 reg; -+ -+ ret = genphy_config_init(phydev); -+ if (ret < 0) -+ return ret; -+ -+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII) { -+ /* enable TXDLY */ -+ phy_write(phydev, RTL8211F_PAGE_SELECT, 0xd08); -+ reg = phy_read(phydev, 0x11); -+ reg |= RTL8211F_TX_DELAY; -+ phy_write(phydev, 0x11, reg); -+ /* restore to default page 0 */ -+ phy_write(phydev, RTL8211F_PAGE_SELECT, 0x0); -+ } -+ -+ return 0; -+} -+ - static struct phy_driver realtek_drvs[] = { - { - .phy_id = 0x00008201, -@@ -86,6 +137,19 @@ static struct phy_driver realtek_drvs[] = { - .config_intr = &rtl8211b_config_intr, - .driver = { .owner = THIS_MODULE,}, - }, { -+ .phy_id = 0x001cc914, -+ .name = "RTL8211DN Gigabit Ethernet", -+ .phy_id_mask = 0x001fffff, -+ .features = PHY_GBIT_FEATURES, -+ .flags = PHY_HAS_INTERRUPT, -+ .config_aneg = genphy_config_aneg, -+ .read_status = genphy_read_status, -+ .ack_interrupt = rtl821x_ack_interrupt, -+ .config_intr = rtl8211e_config_intr, -+ .suspend = genphy_suspend, -+ .resume = genphy_resume, -+ .driver = { .owner = THIS_MODULE,}, -+ }, { - .phy_id = 0x001cc915, - .name = "RTL8211E Gigabit Ethernet", - .phy_id_mask = 0x001fffff, -@@ -98,6 +162,20 @@ static struct phy_driver realtek_drvs[] = { - .suspend = genphy_suspend, - .resume = genphy_resume, - .driver = { .owner = THIS_MODULE,}, -+ }, { -+ .phy_id = 0x001cc916, -+ .name = "RTL8211F Gigabit Ethernet", -+ .phy_id_mask = 0x001fffff, -+ .features = PHY_GBIT_FEATURES, -+ .flags = PHY_HAS_INTERRUPT, -+ .config_aneg = &genphy_config_aneg, -+ .config_init = &rtl8211f_config_init, -+ .read_status = &genphy_read_status, -+ .ack_interrupt = &rtl8211f_ack_interrupt, -+ .config_intr = &rtl8211f_config_intr, -+ .suspend = genphy_suspend, -+ .resume = genphy_resume, -+ .driver = { .owner = THIS_MODULE }, - }, - }; - -@@ -116,7 +194,9 @@ module_exit(realtek_exit); - - static struct mdio_device_id __maybe_unused realtek_tbl[] = { - { 0x001cc912, 0x001fffff }, -+ { 0x001cc914, 0x001fffff }, - { 0x001cc915, 0x001fffff }, -+ { 0x001cc916, 0x001fffff }, - { } - }; - -diff --git a/drivers/net/phy/teranetics.c b/drivers/net/phy/teranetics.c -new file mode 100644 -index 0000000..91e1bec ---- /dev/null -+++ b/drivers/net/phy/teranetics.c -@@ -0,0 +1,135 @@ -+/* -+ * Driver for Teranetics PHY -+ * -+ * Author: Shaohui Xie -+ * -+ * Copyright 2015 Freescale Semiconductor, Inc. -+ * -+ * This file is licensed under the terms of the GNU General Public License -+ * version 2. This program is licensed "as is" without any warranty of any -+ * kind, whether express or implied. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+MODULE_DESCRIPTION("Teranetics PHY driver"); -+MODULE_AUTHOR("Shaohui Xie "); -+MODULE_LICENSE("GPL v2"); -+ -+#define PHY_ID_TN2020 0x00a19410 -+#define MDIO_PHYXS_LNSTAT_SYNC0 0x0001 -+#define MDIO_PHYXS_LNSTAT_SYNC1 0x0002 -+#define MDIO_PHYXS_LNSTAT_SYNC2 0x0004 -+#define MDIO_PHYXS_LNSTAT_SYNC3 0x0008 -+#define MDIO_PHYXS_LNSTAT_ALIGN 0x1000 -+ -+#define MDIO_PHYXS_LANE_READY (MDIO_PHYXS_LNSTAT_SYNC0 | \ -+ MDIO_PHYXS_LNSTAT_SYNC1 | \ -+ MDIO_PHYXS_LNSTAT_SYNC2 | \ -+ MDIO_PHYXS_LNSTAT_SYNC3 | \ -+ MDIO_PHYXS_LNSTAT_ALIGN) -+ -+static int teranetics_config_init(struct phy_device *phydev) -+{ -+ phydev->supported = SUPPORTED_10000baseT_Full; -+ phydev->advertising = SUPPORTED_10000baseT_Full; -+ -+ return 0; -+} -+ -+static int teranetics_soft_reset(struct phy_device *phydev) -+{ -+ return 0; -+} -+ -+static int teranetics_aneg_done(struct phy_device *phydev) -+{ -+ int reg; -+ -+ /* auto negotiation state can only be checked when using copper -+ * port, if using fiber port, just lie it's done. -+ */ -+ if (!phy_read_mmd(phydev, MDIO_MMD_VEND1, 93)) { -+ reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); -+ return (reg < 0) ? reg : (reg & BMSR_ANEGCOMPLETE); -+ } -+ -+ return 1; -+} -+ -+static int teranetics_config_aneg(struct phy_device *phydev) -+{ -+ return 0; -+} -+ -+static int teranetics_read_status(struct phy_device *phydev) -+{ -+ int reg; -+ -+ phydev->link = 1; -+ -+ phydev->speed = SPEED_10000; -+ phydev->duplex = DUPLEX_FULL; -+ -+ if (!phy_read_mmd(phydev, MDIO_MMD_VEND1, 93)) { -+ reg = phy_read_mmd(phydev, MDIO_MMD_PHYXS, MDIO_PHYXS_LNSTAT); -+ if (reg < 0 || -+ !((reg & MDIO_PHYXS_LANE_READY) == MDIO_PHYXS_LANE_READY)) { -+ phydev->link = 0; -+ return 0; -+ } -+ -+ reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); -+ if (reg < 0 || !(reg & MDIO_STAT1_LSTATUS)) -+ phydev->link = 0; -+ } -+ -+ return 0; -+} -+ -+static int teranetics_match_phy_device(struct phy_device *phydev) -+{ -+ return phydev->c45_ids.device_ids[3] == PHY_ID_TN2020; -+} -+ -+static struct phy_driver teranetics_driver[] = { -+{ -+ .phy_id = PHY_ID_TN2020, -+ .phy_id_mask = 0xffffffff, -+ .name = "Teranetics TN2020", -+ .soft_reset = teranetics_soft_reset, -+ .aneg_done = teranetics_aneg_done, -+ .config_init = teranetics_config_init, -+ .config_aneg = teranetics_config_aneg, -+ .read_status = teranetics_read_status, -+ .match_phy_device = teranetics_match_phy_device, -+ .driver = { .owner = THIS_MODULE,}, -+}, -+}; -+ -+static int __init teranetics_init(void) -+{ -+ return phy_drivers_register(teranetics_driver, -+ ARRAY_SIZE(teranetics_driver)); -+} -+ -+static void __exit teranetics_exit(void) -+{ -+ return phy_drivers_unregister(teranetics_driver, -+ ARRAY_SIZE(teranetics_driver)); -+} -+ -+module_init(teranetics_init); -+module_exit(teranetics_exit); -+ -+static struct mdio_device_id __maybe_unused teranetics_tbl[] = { -+ { PHY_ID_TN2020, 0xffffffff }, -+ { } -+}; -+ -+MODULE_DEVICE_TABLE(mdio, teranetics_tbl); -diff --git a/drivers/of/base.c b/drivers/of/base.c -index 469d2b7..210c876 100644 ---- a/drivers/of/base.c -+++ b/drivers/of/base.c -@@ -32,8 +32,8 @@ - - LIST_HEAD(aliases_lookup); - --struct device_node *of_allnodes; --EXPORT_SYMBOL(of_allnodes); -+struct device_node *of_root; -+EXPORT_SYMBOL(of_root); - struct device_node *of_chosen; - struct device_node *of_aliases; - struct device_node *of_stdout; -@@ -48,7 +48,7 @@ struct kset *of_kset; - */ - DEFINE_MUTEX(of_mutex); - --/* use when traversing tree through the allnext, child, sibling, -+/* use when traversing tree through the child, sibling, - * or parent members of struct device_node. - */ - DEFINE_RAW_SPINLOCK(devtree_lock); -@@ -204,7 +204,7 @@ static int __init of_init(void) - mutex_unlock(&of_mutex); - - /* Symlink in /proc as required by userspace ABI */ -- if (of_allnodes) -+ if (of_root) - proc_symlink("device-tree", NULL, "/sys/firmware/devicetree/base"); - - return 0; -@@ -245,6 +245,23 @@ struct property *of_find_property(const struct device_node *np, - } - EXPORT_SYMBOL(of_find_property); - -+struct device_node *__of_find_all_nodes(struct device_node *prev) -+{ -+ struct device_node *np; -+ if (!prev) { -+ np = of_root; -+ } else if (prev->child) { -+ np = prev->child; -+ } else { -+ /* Walk back up looking for a sibling, or the end of the structure */ -+ np = prev; -+ while (np->parent && !np->sibling) -+ np = np->parent; -+ np = np->sibling; /* Might be null at the end of the tree */ -+ } -+ return np; -+} -+ - /** - * of_find_all_nodes - Get next node in global list - * @prev: Previous node or NULL to start iteration -@@ -259,10 +276,8 @@ struct device_node *of_find_all_nodes(struct device_node *prev) - unsigned long flags; - - raw_spin_lock_irqsave(&devtree_lock, flags); -- np = prev ? prev->allnext : of_allnodes; -- for (; np != NULL; np = np->allnext) -- if (of_node_get(np)) -- break; -+ np = __of_find_all_nodes(prev); -+ of_node_get(np); - of_node_put(prev); - raw_spin_unlock_irqrestore(&devtree_lock, flags); - return np; -@@ -736,7 +751,7 @@ struct device_node *of_find_node_by_path(const char *path) - unsigned long flags; - - if (strcmp(path, "/") == 0) -- return of_node_get(of_allnodes); -+ return of_node_get(of_root); - - /* The path could begin with an alias */ - if (*path != '/') { -@@ -761,7 +776,7 @@ struct device_node *of_find_node_by_path(const char *path) - /* Step down the tree matching path components */ - raw_spin_lock_irqsave(&devtree_lock, flags); - if (!np) -- np = of_node_get(of_allnodes); -+ np = of_node_get(of_root); - while (np && *path == '/') { - path++; /* Increment past '/' delimiter */ - np = __of_find_node_by_path(np, path); -@@ -790,8 +805,7 @@ struct device_node *of_find_node_by_name(struct device_node *from, - unsigned long flags; - - raw_spin_lock_irqsave(&devtree_lock, flags); -- np = from ? from->allnext : of_allnodes; -- for (; np; np = np->allnext) -+ for_each_of_allnodes_from(from, np) - if (np->name && (of_node_cmp(np->name, name) == 0) - && of_node_get(np)) - break; -@@ -820,8 +834,7 @@ struct device_node *of_find_node_by_type(struct device_node *from, - unsigned long flags; - - raw_spin_lock_irqsave(&devtree_lock, flags); -- np = from ? from->allnext : of_allnodes; -- for (; np; np = np->allnext) -+ for_each_of_allnodes_from(from, np) - if (np->type && (of_node_cmp(np->type, type) == 0) - && of_node_get(np)) - break; -@@ -852,12 +865,10 @@ struct device_node *of_find_compatible_node(struct device_node *from, - unsigned long flags; - - raw_spin_lock_irqsave(&devtree_lock, flags); -- np = from ? from->allnext : of_allnodes; -- for (; np; np = np->allnext) { -+ for_each_of_allnodes_from(from, np) - if (__of_device_is_compatible(np, compatible, type, NULL) && - of_node_get(np)) - break; -- } - of_node_put(from); - raw_spin_unlock_irqrestore(&devtree_lock, flags); - return np; -@@ -884,8 +895,7 @@ struct device_node *of_find_node_with_property(struct device_node *from, - unsigned long flags; - - raw_spin_lock_irqsave(&devtree_lock, flags); -- np = from ? from->allnext : of_allnodes; -- for (; np; np = np->allnext) { -+ for_each_of_allnodes_from(from, np) { - for (pp = np->properties; pp; pp = pp->next) { - if (of_prop_cmp(pp->name, prop_name) == 0) { - of_node_get(np); -@@ -967,8 +977,7 @@ struct device_node *of_find_matching_node_and_match(struct device_node *from, - *match = NULL; - - raw_spin_lock_irqsave(&devtree_lock, flags); -- np = from ? from->allnext : of_allnodes; -- for (; np; np = np->allnext) { -+ for_each_of_allnodes_from(from, np) { - m = __of_match_node(matches, np); - if (m && of_node_get(np)) { - if (match) -@@ -1025,7 +1034,7 @@ struct device_node *of_find_node_by_phandle(phandle handle) - return NULL; - - raw_spin_lock_irqsave(&devtree_lock, flags); -- for (np = of_allnodes; np; np = np->allnext) -+ for_each_of_allnodes(np) - if (np->phandle == handle) - break; - of_node_get(np); -diff --git a/drivers/of/device.c b/drivers/of/device.c -index 46d6c75..20c1332 100644 ---- a/drivers/of/device.c -+++ b/drivers/of/device.c -@@ -2,6 +2,9 @@ - #include - #include - #include -+#include -+#include -+#include - #include - #include - #include -@@ -66,6 +69,87 @@ int of_device_add(struct platform_device *ofdev) - return device_add(&ofdev->dev); - } - -+/** -+ * of_dma_configure - Setup DMA configuration -+ * @dev: Device to apply DMA configuration -+ * @np: Pointer to OF node having DMA configuration -+ * -+ * Try to get devices's DMA configuration from DT and update it -+ * accordingly. -+ * -+ * If platform code needs to use its own special DMA configuration, it -+ * can use a platform bus notifier and handle BUS_NOTIFY_ADD_DEVICE events -+ * to fix up DMA configuration. -+ */ -+void of_dma_configure(struct device *dev, struct device_node *np) -+{ -+ u64 dma_addr, paddr, size; -+ int ret; -+ bool coherent; -+ unsigned long offset; -+ struct iommu_ops *iommu; -+ -+ /* -+ * Set default coherent_dma_mask to 32 bit. Drivers are expected to -+ * setup the correct supported mask. -+ */ -+ if (!dev->coherent_dma_mask) -+ dev->coherent_dma_mask = DMA_BIT_MASK(32); -+ -+ /* -+ * Set it to coherent_dma_mask by default if the architecture -+ * code has not set it. -+ */ -+ if (!dev->dma_mask) -+ dev->dma_mask = &dev->coherent_dma_mask; -+ -+ ret = of_dma_get_range(np, &dma_addr, &paddr, &size); -+ if (ret < 0) { -+ dma_addr = offset = 0; -+ size = dev->coherent_dma_mask + 1; -+ } else { -+ offset = PFN_DOWN(paddr - dma_addr); -+ -+ /* -+ * Add a work around to treat the size as mask + 1 in case -+ * it is defined in DT as a mask. -+ */ -+ if (size & 1) { -+ dev_warn(dev, "Invalid size 0x%llx for dma-range\n", -+ size); -+ size = size + 1; -+ } -+ -+ if (!size) { -+ dev_err(dev, "Adjusted size 0x%llx invalid\n", size); -+ return; -+ } -+ dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", offset); -+ } -+ -+ dev->dma_pfn_offset = offset; -+ -+ /* -+ * Limit coherent and dma mask based on size and default mask -+ * set by the driver. -+ */ -+ dev->coherent_dma_mask = min(dev->coherent_dma_mask, -+ DMA_BIT_MASK(ilog2(dma_addr + size))); -+ *dev->dma_mask = min((*dev->dma_mask), -+ DMA_BIT_MASK(ilog2(dma_addr + size))); -+ -+ coherent = of_dma_is_coherent(np); -+ dev_dbg(dev, "device is%sdma coherent\n", -+ coherent ? " " : " not "); -+ -+ iommu = of_iommu_configure(dev, np); -+ dev_dbg(dev, "device is%sbehind an iommu\n", -+ iommu ? " " : " not "); -+ -+ arch_setup_dma_ops(dev, dma_addr, size, iommu, coherent); -+} -+EXPORT_SYMBOL_GPL(of_dma_configure); -+ - int of_device_register(struct platform_device *pdev) - { - device_initialize(&pdev->dev); -diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c -index d499417..d43f305 100644 ---- a/drivers/of/dynamic.c -+++ b/drivers/of/dynamic.c -@@ -117,8 +117,6 @@ void __of_attach_node(struct device_node *np) - - np->child = NULL; - np->sibling = np->parent->child; -- np->allnext = np->parent->allnext; -- np->parent->allnext = np; - np->parent->child = np; - of_node_clear_flag(np, OF_DETACHED); - } -@@ -154,17 +152,6 @@ void __of_detach_node(struct device_node *np) - if (WARN_ON(!parent)) - return; - -- if (of_allnodes == np) -- of_allnodes = np->allnext; -- else { -- struct device_node *prev; -- for (prev = of_allnodes; -- prev->allnext != np; -- prev = prev->allnext) -- ; -- prev->allnext = np->allnext; -- } -- - if (parent->child == np) - parent->child = np->sibling; - else { -diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c -index d134710..f6eda02 100644 ---- a/drivers/of/fdt.c -+++ b/drivers/of/fdt.c -@@ -145,15 +145,15 @@ static void *unflatten_dt_alloc(void **mem, unsigned long size, - * @mem: Memory chunk to use for allocating device nodes and properties - * @p: pointer to node in flat tree - * @dad: Parent struct device_node -- * @allnextpp: pointer to ->allnext from last allocated device_node - * @fpsize: Size of the node path up at the current depth. - */ - static void * unflatten_dt_node(void *blob, - void *mem, - int *poffset, - struct device_node *dad, -- struct device_node ***allnextpp, -- unsigned long fpsize) -+ struct device_node **nodepp, -+ unsigned long fpsize, -+ bool dryrun) - { - const __be32 *p; - struct device_node *np; -@@ -200,7 +200,7 @@ static void * unflatten_dt_node(void *blob, - - np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl, - __alignof__(struct device_node)); -- if (allnextpp) { -+ if (!dryrun) { - char *fn; - of_node_init(np); - np->full_name = fn = ((char *)np) + sizeof(*np); -@@ -222,8 +222,6 @@ static void * unflatten_dt_node(void *blob, - memcpy(fn, pathp, l); - - prev_pp = &np->properties; -- **allnextpp = np; -- *allnextpp = &np->allnext; - if (dad != NULL) { - np->parent = dad; - /* we temporarily use the next field as `last_child'*/ -@@ -254,7 +252,7 @@ static void * unflatten_dt_node(void *blob, - has_name = 1; - pp = unflatten_dt_alloc(&mem, sizeof(struct property), - __alignof__(struct property)); -- if (allnextpp) { -+ if (!dryrun) { - /* We accept flattened tree phandles either in - * ePAPR-style "phandle" properties, or the - * legacy "linux,phandle" properties. If both -@@ -296,7 +294,7 @@ static void * unflatten_dt_node(void *blob, - sz = (pa - ps) + 1; - pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz, - __alignof__(struct property)); -- if (allnextpp) { -+ if (!dryrun) { - pp->name = "name"; - pp->length = sz; - pp->value = pp + 1; -@@ -308,7 +306,7 @@ static void * unflatten_dt_node(void *blob, - (char *)pp->value); - } - } -- if (allnextpp) { -+ if (!dryrun) { - *prev_pp = NULL; - np->name = of_get_property(np, "name", NULL); - np->type = of_get_property(np, "device_type", NULL); -@@ -324,11 +322,13 @@ static void * unflatten_dt_node(void *blob, - if (depth < 0) - depth = 0; - while (*poffset > 0 && depth > old_depth) -- mem = unflatten_dt_node(blob, mem, poffset, np, allnextpp, -- fpsize); -+ mem = unflatten_dt_node(blob, mem, poffset, np, NULL, -+ fpsize, dryrun); - - if (*poffset < 0 && *poffset != -FDT_ERR_NOTFOUND) - pr_err("unflatten: error %d processing FDT\n", *poffset); -+ if (nodepp) -+ *nodepp = np; - - return mem; - } -@@ -352,7 +352,6 @@ static void __unflatten_device_tree(void *blob, - unsigned long size; - int start; - void *mem; -- struct device_node **allnextp = mynodes; - - pr_debug(" -> unflatten_device_tree()\n"); - -@@ -373,7 +372,7 @@ static void __unflatten_device_tree(void *blob, - - /* First pass, scan for size */ - start = 0; -- size = (unsigned long)unflatten_dt_node(blob, NULL, &start, NULL, NULL, 0); -+ size = (unsigned long)unflatten_dt_node(blob, NULL, &start, NULL, NULL, 0, true); - size = ALIGN(size, 4); - - pr_debug(" size is %lx, allocating...\n", size); -@@ -388,11 +387,10 @@ static void __unflatten_device_tree(void *blob, - - /* Second pass, do actual unflattening */ - start = 0; -- unflatten_dt_node(blob, mem, &start, NULL, &allnextp, 0); -+ unflatten_dt_node(blob, mem, &start, NULL, mynodes, 0, false); - if (be32_to_cpup(mem + size) != 0xdeadbeef) - pr_warning("End of tree marker overwritten: %08x\n", - be32_to_cpup(mem + size)); -- *allnextp = NULL; - - pr_debug(" <- unflatten_device_tree()\n"); - } -@@ -1039,7 +1037,7 @@ bool __init early_init_dt_scan(void *params) - */ - void __init unflatten_device_tree(void) - { -- __unflatten_device_tree(initial_boot_params, &of_allnodes, -+ __unflatten_device_tree(initial_boot_params, &of_root, - early_init_dt_alloc_memory_arch); - - /* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */ -diff --git a/drivers/of/irq.c b/drivers/of/irq.c -index b97363a..4419e62 100644 ---- a/drivers/of/irq.c -+++ b/drivers/of/irq.c -@@ -18,6 +18,7 @@ - * driver. - */ - -+#include - #include - #include - #include -@@ -576,3 +577,23 @@ err: - kfree(desc); - } - } -+ -+/** -+ * of_msi_configure - Set the msi_domain field of a device -+ * @dev: device structure to associate with an MSI irq domain -+ * @np: device node for that device -+ */ -+void of_msi_configure(struct device *dev, struct device_node *np) -+{ -+ struct device_node *msi_np; -+ struct irq_domain *d; -+ -+ msi_np = of_parse_phandle(np, "msi-parent", 0); -+ if (!msi_np) -+ return; -+ -+ d = irq_find_matching_host(msi_np, DOMAIN_BUS_PLATFORM_MSI); -+ if (!d) -+ d = irq_find_host(msi_np); -+ dev_set_msi_domain(dev, d); -+} -diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c -index ecc5fa5..5751dc5 100644 ---- a/drivers/of/of_pci.c -+++ b/drivers/of/of_pci.c -@@ -2,6 +2,7 @@ - #include - #include - #include -+#include - #include - #include - -@@ -116,6 +117,26 @@ int of_get_pci_domain_nr(struct device_node *node) - } - EXPORT_SYMBOL_GPL(of_get_pci_domain_nr); - -+/** -+ * of_pci_dma_configure - Setup DMA configuration -+ * @dev: ptr to pci_dev struct of the PCI device -+ * -+ * Function to update PCI devices's DMA configuration using the same -+ * info from the OF node of host bridge's parent (if any). -+ */ -+void of_pci_dma_configure(struct pci_dev *pci_dev) -+{ -+ struct device *dev = &pci_dev->dev; -+ struct device *bridge = pci_get_host_bridge_device(pci_dev); -+ -+ if (!bridge->parent) -+ return; -+ -+ of_dma_configure(dev, bridge->parent->of_node); -+ pci_put_host_bridge_device(bridge); -+} -+EXPORT_SYMBOL_GPL(of_pci_dma_configure); -+ - #if defined(CONFIG_OF_ADDRESS) - /** - * of_pci_get_host_bridge_resources - Parse PCI host bridge resources from DT -@@ -140,7 +161,7 @@ int of_pci_get_host_bridge_resources(struct device_node *dev, - unsigned char busno, unsigned char bus_max, - struct list_head *resources, resource_size_t *io_base) - { -- struct pci_host_bridge_window *window; -+ struct resource_entry *window; - struct resource *res; - struct resource *bus_range; - struct of_pci_range range; -@@ -226,10 +247,9 @@ int of_pci_get_host_bridge_resources(struct device_node *dev, - conversion_failed: - kfree(res); - parse_failed: -- list_for_each_entry(window, resources, list) -+ resource_list_for_each_entry(window, resources) - kfree(window->res); - pci_free_resource_list(resources); -- kfree(bus_range); - return err; - } - EXPORT_SYMBOL_GPL(of_pci_get_host_bridge_resources); -@@ -240,7 +260,7 @@ EXPORT_SYMBOL_GPL(of_pci_get_host_bridge_resources); - static LIST_HEAD(of_pci_msi_chip_list); - static DEFINE_MUTEX(of_pci_msi_chip_mutex); - --int of_pci_msi_chip_add(struct msi_chip *chip) -+int of_pci_msi_chip_add(struct msi_controller *chip) - { - if (!of_property_read_bool(chip->of_node, "msi-controller")) - return -EINVAL; -@@ -253,7 +273,7 @@ int of_pci_msi_chip_add(struct msi_chip *chip) - } - EXPORT_SYMBOL_GPL(of_pci_msi_chip_add); - --void of_pci_msi_chip_remove(struct msi_chip *chip) -+void of_pci_msi_chip_remove(struct msi_controller *chip) - { - mutex_lock(&of_pci_msi_chip_mutex); - list_del(&chip->list); -@@ -261,9 +281,9 @@ void of_pci_msi_chip_remove(struct msi_chip *chip) - } - EXPORT_SYMBOL_GPL(of_pci_msi_chip_remove); - --struct msi_chip *of_pci_find_msi_chip_by_node(struct device_node *of_node) -+struct msi_controller *of_pci_find_msi_chip_by_node(struct device_node *of_node) - { -- struct msi_chip *c; -+ struct msi_controller *c; - - mutex_lock(&of_pci_msi_chip_mutex); - list_for_each_entry(c, &of_pci_msi_chip_list, list) { -diff --git a/drivers/of/pdt.c b/drivers/of/pdt.c -index 36b4035..d2acae8 100644 ---- a/drivers/of/pdt.c -+++ b/drivers/of/pdt.c -@@ -25,8 +25,7 @@ - - static struct of_pdt_ops *of_pdt_prom_ops __initdata; - --void __initdata (*of_pdt_build_more)(struct device_node *dp, -- struct device_node ***nextp); -+void __initdata (*of_pdt_build_more)(struct device_node *dp); - - #if defined(CONFIG_SPARC) - unsigned int of_pdt_unique_id __initdata; -@@ -192,8 +191,7 @@ static struct device_node * __init of_pdt_create_node(phandle node, - } - - static struct device_node * __init of_pdt_build_tree(struct device_node *parent, -- phandle node, -- struct device_node ***nextp) -+ phandle node) - { - struct device_node *ret = NULL, *prev_sibling = NULL; - struct device_node *dp; -@@ -210,16 +208,12 @@ static struct device_node * __init of_pdt_build_tree(struct device_node *parent, - ret = dp; - prev_sibling = dp; - -- *(*nextp) = dp; -- *nextp = &dp->allnext; -- - dp->full_name = of_pdt_build_full_name(dp); - -- dp->child = of_pdt_build_tree(dp, -- of_pdt_prom_ops->getchild(node), nextp); -+ dp->child = of_pdt_build_tree(dp, of_pdt_prom_ops->getchild(node)); - - if (of_pdt_build_more) -- of_pdt_build_more(dp, nextp); -+ of_pdt_build_more(dp); - - node = of_pdt_prom_ops->getsibling(node); - } -@@ -234,20 +228,17 @@ static void * __init kernel_tree_alloc(u64 size, u64 align) - - void __init of_pdt_build_devicetree(phandle root_node, struct of_pdt_ops *ops) - { -- struct device_node **nextp; -- - BUG_ON(!ops); - of_pdt_prom_ops = ops; - -- of_allnodes = of_pdt_create_node(root_node, NULL); -+ of_root = of_pdt_create_node(root_node, NULL); - #if defined(CONFIG_SPARC) -- of_allnodes->path_component_name = ""; -+ of_root->path_component_name = ""; - #endif -- of_allnodes->full_name = "/"; -+ of_root->full_name = "/"; - -- nextp = &of_allnodes->allnext; -- of_allnodes->child = of_pdt_build_tree(of_allnodes, -- of_pdt_prom_ops->getchild(of_allnodes->phandle), &nextp); -+ of_root->child = of_pdt_build_tree(of_root, -+ of_pdt_prom_ops->getchild(of_root->phandle)); - - /* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */ - of_alias_scan(kernel_tree_alloc); -diff --git a/drivers/of/platform.c b/drivers/of/platform.c -index 3b64d0b..8a002d6 100644 ---- a/drivers/of/platform.c -+++ b/drivers/of/platform.c -@@ -25,6 +25,7 @@ - - const struct of_device_id of_default_bus_match_table[] = { - { .compatible = "simple-bus", }, -+ { .compatible = "simple-mfd", }, - #ifdef CONFIG_ARM_AMBA - { .compatible = "arm,amba-bus", }, - #endif /* CONFIG_ARM_AMBA */ -@@ -138,7 +139,7 @@ struct platform_device *of_device_alloc(struct device_node *np, - } - - dev->dev.of_node = of_node_get(np); -- dev->dev.parent = parent; -+ dev->dev.parent = parent ? : &platform_bus; - - if (bus_id) - dev_set_name(&dev->dev, "%s", bus_id); -@@ -149,57 +150,9 @@ struct platform_device *of_device_alloc(struct device_node *np, - } - EXPORT_SYMBOL(of_device_alloc); - --/** -- * of_dma_configure - Setup DMA configuration -- * @dev: Device to apply DMA configuration -- * -- * Try to get devices's DMA configuration from DT and update it -- * accordingly. -- * -- * In case if platform code need to use own special DMA configuration,it -- * can use Platform bus notifier and handle BUS_NOTIFY_ADD_DEVICE event -- * to fix up DMA configuration. -- */ --static void of_dma_configure(struct device *dev) -+static void of_dma_deconfigure(struct device *dev) - { -- u64 dma_addr, paddr, size; -- int ret; -- -- /* -- * Set default dma-mask to 32 bit. Drivers are expected to setup -- * the correct supported dma_mask. -- */ -- dev->coherent_dma_mask = DMA_BIT_MASK(32); -- -- /* -- * Set it to coherent_dma_mask by default if the architecture -- * code has not set it. -- */ -- if (!dev->dma_mask) -- dev->dma_mask = &dev->coherent_dma_mask; -- -- /* -- * if dma-coherent property exist, call arch hook to setup -- * dma coherent operations. -- */ -- if (of_dma_is_coherent(dev->of_node)) { -- set_arch_dma_coherent_ops(dev); -- dev_dbg(dev, "device is dma coherent\n"); -- } -- -- /* -- * if dma-ranges property doesn't exist - just return else -- * setup the dma offset -- */ -- ret = of_dma_get_range(dev->of_node, &dma_addr, &paddr, &size); -- if (ret < 0) { -- dev_dbg(dev, "no dma range information to setup\n"); -- return; -- } -- -- /* DMA ranges found. Calculate and set dma_pfn_offset */ -- dev->dma_pfn_offset = PFN_DOWN(paddr - dma_addr); -- dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", dev->dma_pfn_offset); -+ arch_teardown_dma_ops(dev); - } - - /** -@@ -228,16 +181,13 @@ static struct platform_device *of_platform_device_create_pdata( - if (!dev) - goto err_clear_flag; - -- of_dma_configure(&dev->dev); - dev->dev.bus = &platform_bus_type; - dev->dev.platform_data = platform_data; -- -- /* We do not fill the DMA ops for platform devices by default. -- * This is currently the responsibility of the platform code -- * to do such, possibly using a device notifier -- */ -+ of_dma_configure(&dev->dev, dev->dev.of_node); -+ of_msi_configure(&dev->dev, dev->dev.of_node); - - if (of_device_add(dev) != 0) { -+ of_dma_deconfigure(&dev->dev); - platform_device_put(dev); - goto err_clear_flag; - } -@@ -291,13 +241,13 @@ static struct amba_device *of_amba_device_create(struct device_node *node, - - /* setup generic device info */ - dev->dev.of_node = of_node_get(node); -- dev->dev.parent = parent; -+ dev->dev.parent = parent ? : &platform_bus; - dev->dev.platform_data = platform_data; - if (bus_id) - dev_set_name(&dev->dev, "%s", bus_id); - else - of_device_make_bus_id(&dev->dev); -- of_dma_configure(&dev->dev); -+ of_dma_configure(&dev->dev, dev->dev.of_node); - - /* Allow the HW Peripheral ID to be overridden */ - prop = of_get_property(node, "arm,primecell-periphid", NULL); -@@ -500,6 +450,7 @@ int of_platform_populate(struct device_node *root, - if (rc) - break; - } -+ of_node_set_flag(root, OF_POPULATED_BUS); - - of_node_put(root); - return rc; -@@ -523,6 +474,7 @@ static int of_platform_device_destroy(struct device *dev, void *data) - amba_device_unregister(to_amba_device(dev)); - #endif - -+ of_dma_deconfigure(dev); - of_node_clear_flag(dev->of_node, OF_POPULATED); - of_node_clear_flag(dev->of_node, OF_POPULATED_BUS); - return 0; -@@ -542,8 +494,75 @@ static int of_platform_device_destroy(struct device *dev, void *data) - */ - void of_platform_depopulate(struct device *parent) - { -- device_for_each_child(parent, NULL, of_platform_device_destroy); -+ if (parent->of_node && of_node_check_flag(parent->of_node, OF_POPULATED_BUS)) { -+ device_for_each_child(parent, NULL, of_platform_device_destroy); -+ of_node_clear_flag(parent->of_node, OF_POPULATED_BUS); -+ } - } - EXPORT_SYMBOL_GPL(of_platform_depopulate); - -+#ifdef CONFIG_OF_DYNAMIC -+static int of_platform_notify(struct notifier_block *nb, -+ unsigned long action, void *arg) -+{ -+ struct of_reconfig_data *rd = arg; -+ struct platform_device *pdev_parent, *pdev; -+ bool children_left; -+ -+ switch (of_reconfig_get_state_change(action, rd)) { -+ case OF_RECONFIG_CHANGE_ADD: -+ /* verify that the parent is a bus */ -+ if (!of_node_check_flag(rd->dn->parent, OF_POPULATED_BUS)) -+ return NOTIFY_OK; /* not for us */ -+ -+ /* already populated? (driver using of_populate manually) */ -+ if (of_node_check_flag(rd->dn, OF_POPULATED)) -+ return NOTIFY_OK; -+ -+ /* pdev_parent may be NULL when no bus platform device */ -+ pdev_parent = of_find_device_by_node(rd->dn->parent); -+ pdev = of_platform_device_create(rd->dn, NULL, -+ pdev_parent ? &pdev_parent->dev : NULL); -+ of_dev_put(pdev_parent); -+ -+ if (pdev == NULL) { -+ pr_err("%s: failed to create for '%s'\n", -+ __func__, rd->dn->full_name); -+ /* of_platform_device_create tosses the error code */ -+ return notifier_from_errno(-EINVAL); -+ } -+ break; -+ -+ case OF_RECONFIG_CHANGE_REMOVE: -+ -+ /* already depopulated? */ -+ if (!of_node_check_flag(rd->dn, OF_POPULATED)) -+ return NOTIFY_OK; -+ -+ /* find our device by node */ -+ pdev = of_find_device_by_node(rd->dn); -+ if (pdev == NULL) -+ return NOTIFY_OK; /* no? not meant for us */ -+ -+ /* unregister takes one ref away */ -+ of_platform_device_destroy(&pdev->dev, &children_left); -+ -+ /* and put the reference of the find */ -+ of_dev_put(pdev); -+ break; -+ } -+ -+ return NOTIFY_OK; -+} -+ -+static struct notifier_block platform_of_notifier = { -+ .notifier_call = of_platform_notify, -+}; -+ -+void of_platform_register_reconfig_notifier(void) -+{ -+ WARN_ON(of_reconfig_notifier_register(&platform_of_notifier)); -+} -+#endif /* CONFIG_OF_DYNAMIC */ -+ - #endif /* CONFIG_OF_ADDRESS */ -diff --git a/drivers/of/selftest.c b/drivers/of/selftest.c -index e2d79af..e40089e 100644 ---- a/drivers/of/selftest.c -+++ b/drivers/of/selftest.c -@@ -148,7 +148,7 @@ static void __init of_selftest_dynamic(void) - - static int __init of_selftest_check_node_linkage(struct device_node *np) - { -- struct device_node *child, *allnext_index = np; -+ struct device_node *child; - int count = 0, rc; - - for_each_child_of_node(np, child) { -@@ -158,14 +158,6 @@ static int __init of_selftest_check_node_linkage(struct device_node *np) - return -EINVAL; - } - -- while (allnext_index && allnext_index != child) -- allnext_index = allnext_index->allnext; -- if (allnext_index != child) { -- pr_err("Node %s is ordered differently in sibling and allnode lists\n", -- child->name); -- return -EINVAL; -- } -- - rc = of_selftest_check_node_linkage(child); - if (rc < 0) - return rc; -@@ -180,12 +172,12 @@ static void __init of_selftest_check_tree_linkage(void) - struct device_node *np; - int allnode_count = 0, child_count; - -- if (!of_allnodes) -+ if (!of_root) - return; - - for_each_of_allnodes(np) - allnode_count++; -- child_count = of_selftest_check_node_linkage(of_allnodes); -+ child_count = of_selftest_check_node_linkage(of_root); - - selftest(child_count > 0, "Device node data structure is corrupted\n"); - selftest(child_count == allnode_count, "allnodes list size (%i) doesn't match" -@@ -775,33 +767,29 @@ static void update_node_properties(struct device_node *np, - */ - static int attach_node_and_children(struct device_node *np) - { -- struct device_node *next, *root = np, *dup; -+ struct device_node *next, *dup, *child; - -- /* skip root node */ -- np = np->child; -- /* storing a copy in temporary node */ -- dup = np; -+ dup = of_find_node_by_path(np->full_name); -+ if (dup) { -+ update_node_properties(np, dup); -+ return 0; -+ } - -- while (dup) { -+ /* Children of the root need to be remembered for removal */ -+ if (np->parent == of_root) { - if (WARN_ON(last_node_index >= NO_OF_NODES)) - return -EINVAL; -- nodes[last_node_index++] = dup; -- dup = dup->sibling; -+ nodes[last_node_index++] = np; - } -- dup = NULL; - -- while (np) { -- next = np->allnext; -- dup = of_find_node_by_path(np->full_name); -- if (dup) -- update_node_properties(np, dup); -- else { -- np->child = NULL; -- if (np->parent == root) -- np->parent = of_allnodes; -- of_attach_node(np); -- } -- np = next; -+ child = np->child; -+ np->child = NULL; -+ np->sibling = NULL; -+ of_attach_node(np); -+ while (child) { -+ next = child->sibling; -+ attach_node_and_children(child); -+ child = next; - } - - return 0; -@@ -846,10 +834,10 @@ static int __init selftest_data_add(void) - return -EINVAL; - } - -- if (!of_allnodes) { -+ if (!of_root) { - /* enabling flag for removing nodes */ - selftest_live_tree = true; -- of_allnodes = selftest_data_node; -+ of_root = selftest_data_node; - - for_each_of_allnodes(np) - __of_attach_node_sysfs(np); -@@ -859,7 +847,14 @@ static int __init selftest_data_add(void) - } - - /* attach the sub-tree to live tree */ -- return attach_node_and_children(selftest_data_node); -+ np = selftest_data_node->child; -+ while (np) { -+ struct device_node *next = np->sibling; -+ np->parent = of_root; -+ attach_node_and_children(np); -+ np = next; -+ } -+ return 0; - } - - /** -@@ -889,10 +884,10 @@ static void selftest_data_remove(void) - of_node_put(of_chosen); - of_aliases = NULL; - of_chosen = NULL; -- for_each_child_of_node(of_allnodes, np) -+ for_each_child_of_node(of_root, np) - detach_node_and_children(np); -- __of_detach_node_sysfs(of_allnodes); -- of_allnodes = NULL; -+ __of_detach_node_sysfs(of_root); -+ of_root = NULL; - return; - } - -diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig -index 893503f..cced842 100644 ---- a/drivers/pci/Kconfig -+++ b/drivers/pci/Kconfig -@@ -4,6 +4,7 @@ - config PCI_MSI - bool "Message Signaled Interrupts (MSI and MSI-X)" - depends on PCI -+ select GENERIC_MSI_IRQ - help - This allows device drivers to enable MSI (Message Signaled - Interrupts). Message Signaled Interrupts enable a device to -@@ -16,6 +17,11 @@ config PCI_MSI - - If you don't know what to do here, say Y. - -+config PCI_MSI_IRQ_DOMAIN -+ bool -+ depends on PCI_MSI -+ select GENERIC_MSI_IRQ_DOMAIN -+ - config PCI_DEBUG - bool "PCI Debugging" - depends on PCI && DEBUG_KERNEL -diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile -index e04fe2d..e9815ac 100644 ---- a/drivers/pci/Makefile -+++ b/drivers/pci/Makefile -@@ -35,6 +35,7 @@ obj-$(CONFIG_PCI_IOV) += iov.o - # - obj-$(CONFIG_ALPHA) += setup-irq.o - obj-$(CONFIG_ARM) += setup-irq.o -+obj-$(CONFIG_ARM64) += setup-irq.o - obj-$(CONFIG_UNICORE32) += setup-irq.o - obj-$(CONFIG_SUPERH) += setup-irq.o - obj-$(CONFIG_MIPS) += setup-irq.o -diff --git a/drivers/pci/access.c b/drivers/pci/access.c -index 7f249b9..b965c12 100644 ---- a/drivers/pci/access.c -+++ b/drivers/pci/access.c -@@ -67,6 +67,93 @@ EXPORT_SYMBOL(pci_bus_write_config_byte); - EXPORT_SYMBOL(pci_bus_write_config_word); - EXPORT_SYMBOL(pci_bus_write_config_dword); - -+int pci_generic_config_read(struct pci_bus *bus, unsigned int devfn, -+ int where, int size, u32 *val) -+{ -+ void __iomem *addr; -+ -+ addr = bus->ops->map_bus(bus, devfn, where); -+ if (!addr) { -+ *val = ~0; -+ return PCIBIOS_DEVICE_NOT_FOUND; -+ } -+ -+ if (size == 1) -+ *val = readb(addr); -+ else if (size == 2) -+ *val = readw(addr); -+ else -+ *val = readl(addr); -+ -+ return PCIBIOS_SUCCESSFUL; -+} -+EXPORT_SYMBOL_GPL(pci_generic_config_read); -+ -+int pci_generic_config_write(struct pci_bus *bus, unsigned int devfn, -+ int where, int size, u32 val) -+{ -+ void __iomem *addr; -+ -+ addr = bus->ops->map_bus(bus, devfn, where); -+ if (!addr) -+ return PCIBIOS_DEVICE_NOT_FOUND; -+ -+ if (size == 1) -+ writeb(val, addr); -+ else if (size == 2) -+ writew(val, addr); -+ else -+ writel(val, addr); -+ -+ return PCIBIOS_SUCCESSFUL; -+} -+EXPORT_SYMBOL_GPL(pci_generic_config_write); -+ -+int pci_generic_config_read32(struct pci_bus *bus, unsigned int devfn, -+ int where, int size, u32 *val) -+{ -+ void __iomem *addr; -+ -+ addr = bus->ops->map_bus(bus, devfn, where & ~0x3); -+ if (!addr) { -+ *val = ~0; -+ return PCIBIOS_DEVICE_NOT_FOUND; -+ } -+ -+ *val = readl(addr); -+ -+ if (size <= 2) -+ *val = (*val >> (8 * (where & 3))) & ((1 << (size * 8)) - 1); -+ -+ return PCIBIOS_SUCCESSFUL; -+} -+EXPORT_SYMBOL_GPL(pci_generic_config_read32); -+ -+int pci_generic_config_write32(struct pci_bus *bus, unsigned int devfn, -+ int where, int size, u32 val) -+{ -+ void __iomem *addr; -+ u32 mask, tmp; -+ -+ addr = bus->ops->map_bus(bus, devfn, where & ~0x3); -+ if (!addr) -+ return PCIBIOS_DEVICE_NOT_FOUND; -+ -+ if (size == 4) { -+ writel(val, addr); -+ return PCIBIOS_SUCCESSFUL; -+ } else { -+ mask = ~(((1 << (size * 8)) - 1) << ((where & 0x3) * 8)); -+ } -+ -+ tmp = readl(addr) & mask; -+ tmp |= val << ((where & 0x3) * 8); -+ writel(tmp, addr); -+ -+ return PCIBIOS_SUCCESSFUL; -+} -+EXPORT_SYMBOL_GPL(pci_generic_config_write32); -+ - /** - * pci_bus_set_ops - Set raw operations of pci bus - * @bus: pci bus struct -diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c -index 8fb1618..90fa3a7 100644 ---- a/drivers/pci/bus.c -+++ b/drivers/pci/bus.c -@@ -20,17 +20,16 @@ - void pci_add_resource_offset(struct list_head *resources, struct resource *res, - resource_size_t offset) - { -- struct pci_host_bridge_window *window; -+ struct resource_entry *entry; - -- window = kzalloc(sizeof(struct pci_host_bridge_window), GFP_KERNEL); -- if (!window) { -+ entry = resource_list_create_entry(res, 0); -+ if (!entry) { - printk(KERN_ERR "PCI: can't add host bridge window %pR\n", res); - return; - } - -- window->res = res; -- window->offset = offset; -- list_add_tail(&window->list, resources); -+ entry->offset = offset; -+ resource_list_add_tail(entry, resources); - } - EXPORT_SYMBOL(pci_add_resource_offset); - -@@ -42,12 +41,7 @@ EXPORT_SYMBOL(pci_add_resource); - - void pci_free_resource_list(struct list_head *resources) - { -- struct pci_host_bridge_window *window, *tmp; -- -- list_for_each_entry_safe(window, tmp, resources, list) { -- list_del(&window->list); -- kfree(window); -- } -+ resource_list_free(resources); - } - EXPORT_SYMBOL(pci_free_resource_list); - -diff --git a/drivers/pci/host-bridge.c b/drivers/pci/host-bridge.c -index 0e5f3c9..3e5bbf9 100644 ---- a/drivers/pci/host-bridge.c -+++ b/drivers/pci/host-bridge.c -@@ -23,6 +23,20 @@ static struct pci_host_bridge *find_pci_host_bridge(struct pci_bus *bus) - return to_pci_host_bridge(root_bus->bridge); - } - -+struct device *pci_get_host_bridge_device(struct pci_dev *dev) -+{ -+ struct pci_bus *root_bus = find_pci_root_bus(dev->bus); -+ struct device *bridge = root_bus->bridge; -+ -+ kobject_get(&bridge->kobj); -+ return bridge; -+} -+ -+void pci_put_host_bridge_device(struct device *dev) -+{ -+ kobject_put(&dev->kobj); -+} -+ - void pci_set_host_bridge_release(struct pci_host_bridge *bridge, - void (*release_fn)(struct pci_host_bridge *), - void *release_data) -@@ -35,10 +49,10 @@ void pcibios_resource_to_bus(struct pci_bus *bus, struct pci_bus_region *region, - struct resource *res) - { - struct pci_host_bridge *bridge = find_pci_host_bridge(bus); -- struct pci_host_bridge_window *window; -+ struct resource_entry *window; - resource_size_t offset = 0; - -- list_for_each_entry(window, &bridge->windows, list) { -+ resource_list_for_each_entry(window, &bridge->windows) { - if (resource_contains(window->res, res)) { - offset = window->offset; - break; -@@ -60,10 +74,10 @@ void pcibios_bus_to_resource(struct pci_bus *bus, struct resource *res, - struct pci_bus_region *region) - { - struct pci_host_bridge *bridge = find_pci_host_bridge(bus); -- struct pci_host_bridge_window *window; -+ struct resource_entry *window; - resource_size_t offset = 0; - -- list_for_each_entry(window, &bridge->windows, list) { -+ resource_list_for_each_entry(window, &bridge->windows) { - struct pci_bus_region bus_region; - - if (resource_type(res) != resource_type(window->res)) -diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig -index 3dc25fa..dafa3dc 100644 ---- a/drivers/pci/host/Kconfig -+++ b/drivers/pci/host/Kconfig -@@ -50,7 +50,7 @@ config PCI_RCAR_GEN2_PCIE - - config PCI_HOST_GENERIC - bool "Generic PCI host controller" -- depends on ARM && OF -+ depends on (ARM || ARM64) && OF - help - Say Y here if you want to support a simple generic PCI host - controller, such as the one emulated by kvmtool. -@@ -86,9 +86,26 @@ config PCI_XGENE - depends on ARCH_XGENE - depends on OF - select PCIEPORTBUS -+ select PCI_MSI_IRQ_DOMAIN if PCI_MSI - help - Say Y here if you want internal PCI support on APM X-Gene SoC. - There are 5 internal PCIe ports available. Each port is GEN3 capable - and have varied lanes from x1 to x8. - -+config PCI_XGENE_MSI -+ bool "X-Gene v1 PCIe MSI feature" -+ depends on PCI_XGENE && PCI_MSI -+ default y -+ help -+ Say Y here if you want PCIe MSI support for the APM X-Gene v1 SoC. -+ This MSI driver supports 5 PCIe ports on the APM X-Gene v1 SoC. -+ -+config PCI_LAYERSCAPE -+ bool "Freescale Layerscape PCIe controller" -+ depends on OF && (ARM || ARCH_LAYERSCAPE) -+ select PCIE_DW -+ select MFD_SYSCON -+ help -+ Say Y here if you want PCIe controller support on Layerscape SoCs. -+ - endmenu -diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile -index 26b3461..029685e 100644 ---- a/drivers/pci/host/Makefile -+++ b/drivers/pci/host/Makefile -@@ -1,3 +1,4 @@ -+obj-$(CONFIG_PCIE_DW_BASE) += pcie-designware-base.o - obj-$(CONFIG_PCIE_DW) += pcie-designware.o - obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o - obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o -@@ -11,3 +12,5 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o - obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o - obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o - obj-$(CONFIG_PCI_XGENE) += pci-xgene.o -+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o -+obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o -diff --git a/drivers/pci/host/pci-dra7xx.c b/drivers/pci/host/pci-dra7xx.c -index 52b34fe..84a45cf 100644 ---- a/drivers/pci/host/pci-dra7xx.c -+++ b/drivers/pci/host/pci-dra7xx.c -@@ -61,6 +61,7 @@ - - #define PCIECTRL_DRA7XX_CONF_PHY_CS 0x010C - #define LINK_UP BIT(16) -+#define DRA7XX_CPU_TO_BUS_ADDR 0x0FFFFFFF - - struct dra7xx_pcie { - void __iomem *base; -@@ -144,6 +145,12 @@ static void dra7xx_pcie_enable_interrupts(struct pcie_port *pp) - static void dra7xx_pcie_host_init(struct pcie_port *pp) - { - dw_pcie_setup_rc(pp); -+ -+ pp->io_base &= DRA7XX_CPU_TO_BUS_ADDR; -+ pp->mem_base &= DRA7XX_CPU_TO_BUS_ADDR; -+ pp->cfg0_base &= DRA7XX_CPU_TO_BUS_ADDR; -+ pp->cfg1_base &= DRA7XX_CPU_TO_BUS_ADDR; -+ - dra7xx_pcie_establish_link(pp); - if (IS_ENABLED(CONFIG_PCI_MSI)) - dw_pcie_msi_init(pp); -@@ -160,7 +167,6 @@ static int dra7xx_pcie_intx_map(struct irq_domain *domain, unsigned int irq, - { - irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq); - irq_set_chip_data(irq, domain->host_data); -- set_irq_flags(irq, IRQF_VALID); - - return 0; - } -diff --git a/drivers/pci/host/pci-exynos.c b/drivers/pci/host/pci-exynos.c -index c5d0ca3..2fd6b4e 100644 ---- a/drivers/pci/host/pci-exynos.c -+++ b/drivers/pci/host/pci-exynos.c -@@ -466,7 +466,7 @@ static int exynos_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, - int ret; - - exynos_pcie_sideband_dbi_r_mode(pp, true); -- ret = dw_pcie_cfg_read(pp->dbi_base + (where & ~0x3), where, size, val); -+ ret = dw_pcie_cfg_read(pp->dbi_base + where, size, val); - exynos_pcie_sideband_dbi_r_mode(pp, false); - return ret; - } -@@ -477,8 +477,7 @@ static int exynos_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, - int ret; - - exynos_pcie_sideband_dbi_w_mode(pp, true); -- ret = dw_pcie_cfg_write(pp->dbi_base + (where & ~0x3), -- where, size, val); -+ ret = dw_pcie_cfg_write(pp->dbi_base + where, size, val); - exynos_pcie_sideband_dbi_w_mode(pp, false); - return ret; - } -diff --git a/drivers/pci/host/pci-host-generic.c b/drivers/pci/host/pci-host-generic.c -index 3d2076f..83fb705 100644 ---- a/drivers/pci/host/pci-host-generic.c -+++ b/drivers/pci/host/pci-host-generic.c -@@ -32,13 +32,22 @@ struct gen_pci_cfg_bus_ops { - - struct gen_pci_cfg_windows { - struct resource res; -- struct resource bus_range; -+ struct resource *bus_range; - void __iomem **win; - - const struct gen_pci_cfg_bus_ops *ops; - }; - -+/* -+ * ARM pcibios functions expect the ARM struct pci_sys_data as the PCI -+ * sysdata. Add pci_sys_data as the first element in struct gen_pci so -+ * that when we use a gen_pci pointer as sysdata, it is also a pointer to -+ * a struct pci_sys_data. -+ */ - struct gen_pci { -+#ifdef CONFIG_ARM -+ struct pci_sys_data sys; -+#endif - struct pci_host_bridge host; - struct gen_pci_cfg_windows cfg; - struct list_head resources; -@@ -48,9 +57,8 @@ static void __iomem *gen_pci_map_cfg_bus_cam(struct pci_bus *bus, - unsigned int devfn, - int where) - { -- struct pci_sys_data *sys = bus->sysdata; -- struct gen_pci *pci = sys->private_data; -- resource_size_t idx = bus->number - pci->cfg.bus_range.start; -+ struct gen_pci *pci = bus->sysdata; -+ resource_size_t idx = bus->number - pci->cfg.bus_range->start; - - return pci->cfg.win[idx] + ((devfn << 8) | where); - } -@@ -64,9 +72,8 @@ static void __iomem *gen_pci_map_cfg_bus_ecam(struct pci_bus *bus, - unsigned int devfn, - int where) - { -- struct pci_sys_data *sys = bus->sysdata; -- struct gen_pci *pci = sys->private_data; -- resource_size_t idx = bus->number - pci->cfg.bus_range.start; -+ struct gen_pci *pci = bus->sysdata; -+ resource_size_t idx = bus->number - pci->cfg.bus_range->start; - - return pci->cfg.win[idx] + ((devfn << 12) | where); - } -@@ -76,55 +83,9 @@ static struct gen_pci_cfg_bus_ops gen_pci_cfg_ecam_bus_ops = { - .map_bus = gen_pci_map_cfg_bus_ecam, - }; - --static int gen_pci_config_read(struct pci_bus *bus, unsigned int devfn, -- int where, int size, u32 *val) --{ -- void __iomem *addr; -- struct pci_sys_data *sys = bus->sysdata; -- struct gen_pci *pci = sys->private_data; -- -- addr = pci->cfg.ops->map_bus(bus, devfn, where); -- -- switch (size) { -- case 1: -- *val = readb(addr); -- break; -- case 2: -- *val = readw(addr); -- break; -- default: -- *val = readl(addr); -- } -- -- return PCIBIOS_SUCCESSFUL; --} -- --static int gen_pci_config_write(struct pci_bus *bus, unsigned int devfn, -- int where, int size, u32 val) --{ -- void __iomem *addr; -- struct pci_sys_data *sys = bus->sysdata; -- struct gen_pci *pci = sys->private_data; -- -- addr = pci->cfg.ops->map_bus(bus, devfn, where); -- -- switch (size) { -- case 1: -- writeb(val, addr); -- break; -- case 2: -- writew(val, addr); -- break; -- default: -- writel(val, addr); -- } -- -- return PCIBIOS_SUCCESSFUL; --} -- - static struct pci_ops gen_pci_ops = { -- .read = gen_pci_config_read, -- .write = gen_pci_config_write, -+ .read = pci_generic_config_read, -+ .write = pci_generic_config_write, - }; - - static const struct of_device_id gen_pci_of_match[] = { -@@ -138,106 +99,50 @@ static const struct of_device_id gen_pci_of_match[] = { - }; - MODULE_DEVICE_TABLE(of, gen_pci_of_match); - --static int gen_pci_calc_io_offset(struct device *dev, -- struct of_pci_range *range, -- struct resource *res, -- resource_size_t *offset) --{ -- static atomic_t wins = ATOMIC_INIT(0); -- int err, idx, max_win; -- unsigned int window; -- -- if (!PAGE_ALIGNED(range->cpu_addr)) -- return -EINVAL; -- -- max_win = (IO_SPACE_LIMIT + 1) / SZ_64K; -- idx = atomic_inc_return(&wins); -- if (idx > max_win) -- return -ENOSPC; -- -- window = (idx - 1) * SZ_64K; -- err = pci_ioremap_io(window, range->cpu_addr); -- if (err) -- return err; -- -- of_pci_range_to_resource(range, dev->of_node, res); -- res->start = window; -- res->end = res->start + range->size - 1; -- *offset = window - range->pci_addr; -- return 0; --} -- --static int gen_pci_calc_mem_offset(struct device *dev, -- struct of_pci_range *range, -- struct resource *res, -- resource_size_t *offset) --{ -- of_pci_range_to_resource(range, dev->of_node, res); -- *offset = range->cpu_addr - range->pci_addr; -- return 0; --} -- - static void gen_pci_release_of_pci_ranges(struct gen_pci *pci) - { -- struct pci_host_bridge_window *win; -- -- list_for_each_entry(win, &pci->resources, list) -- release_resource(win->res); -- - pci_free_resource_list(&pci->resources); - } - - static int gen_pci_parse_request_of_pci_ranges(struct gen_pci *pci) - { -- struct of_pci_range range; -- struct of_pci_range_parser parser; - int err, res_valid = 0; - struct device *dev = pci->host.dev.parent; - struct device_node *np = dev->of_node; -+ resource_size_t iobase; -+ struct resource_entry *win; - -- if (of_pci_range_parser_init(&parser, np)) { -- dev_err(dev, "missing \"ranges\" property\n"); -- return -EINVAL; -- } -- -- for_each_of_pci_range(&parser, &range) { -- struct resource *parent, *res; -- resource_size_t offset; -- u32 restype = range.flags & IORESOURCE_TYPE_BITS; -+ err = of_pci_get_host_bridge_resources(np, 0, 0xff, &pci->resources, -+ &iobase); -+ if (err) -+ return err; - -- res = devm_kmalloc(dev, sizeof(*res), GFP_KERNEL); -- if (!res) { -- err = -ENOMEM; -- goto out_release_res; -- } -+ resource_list_for_each_entry(win, &pci->resources) { -+ struct resource *parent, *res = win->res; - -- switch (restype) { -+ switch (resource_type(res)) { - case IORESOURCE_IO: - parent = &ioport_resource; -- err = gen_pci_calc_io_offset(dev, &range, res, &offset); -+ err = pci_remap_iospace(res, iobase); -+ if (err) { -+ dev_warn(dev, "error %d: failed to map resource %pR\n", -+ err, res); -+ continue; -+ } - break; - case IORESOURCE_MEM: - parent = &iomem_resource; -- err = gen_pci_calc_mem_offset(dev, &range, res, &offset); -- res_valid |= !(res->flags & IORESOURCE_PREFETCH || err); -+ res_valid |= !(res->flags & IORESOURCE_PREFETCH); - break; -+ case IORESOURCE_BUS: -+ pci->cfg.bus_range = res; - default: -- err = -EINVAL; - continue; - } - -- if (err) { -- dev_warn(dev, -- "error %d: failed to add resource [type 0x%x, %lld bytes]\n", -- err, restype, range.size); -- continue; -- } -- -- err = request_resource(parent, res); -+ err = devm_request_resource(dev, parent, res); - if (err) - goto out_release_res; -- -- pci_add_resource_offset(&pci->resources, res, offset); - } - - if (!res_valid) { -@@ -262,38 +167,30 @@ static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci) - struct device *dev = pci->host.dev.parent; - struct device_node *np = dev->of_node; - -- if (of_pci_parse_bus_range(np, &pci->cfg.bus_range)) -- pci->cfg.bus_range = (struct resource) { -- .name = np->name, -- .start = 0, -- .end = 0xff, -- .flags = IORESOURCE_BUS, -- }; -- - err = of_address_to_resource(np, 0, &pci->cfg.res); - if (err) { - dev_err(dev, "missing \"reg\" property\n"); - return err; - } - -- pci->cfg.win = devm_kcalloc(dev, resource_size(&pci->cfg.bus_range), -+ /* Limit the bus-range to fit within reg */ -+ bus_max = pci->cfg.bus_range->start + -+ (resource_size(&pci->cfg.res) >> pci->cfg.ops->bus_shift) - 1; -+ pci->cfg.bus_range->end = min_t(resource_size_t, -+ pci->cfg.bus_range->end, bus_max); -+ -+ pci->cfg.win = devm_kcalloc(dev, resource_size(pci->cfg.bus_range), - sizeof(*pci->cfg.win), GFP_KERNEL); - if (!pci->cfg.win) - return -ENOMEM; - -- /* Limit the bus-range to fit within reg */ -- bus_max = pci->cfg.bus_range.start + -- (resource_size(&pci->cfg.res) >> pci->cfg.ops->bus_shift) - 1; -- pci->cfg.bus_range.end = min_t(resource_size_t, pci->cfg.bus_range.end, -- bus_max); -- - /* Map our Configuration Space windows */ - if (!devm_request_mem_region(dev, pci->cfg.res.start, - resource_size(&pci->cfg.res), - "Configuration Space")) - return -ENOMEM; - -- bus_range = &pci->cfg.bus_range; -+ bus_range = pci->cfg.bus_range; - for (busn = bus_range->start; busn <= bus_range->end; ++busn) { - u32 idx = busn - bus_range->start; - u32 sz = 1 << pci->cfg.ops->bus_shift; -@@ -305,18 +202,9 @@ static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci) - return -ENOMEM; - } - -- /* Register bus resource */ -- pci_add_resource(&pci->resources, bus_range); - return 0; - } - --static int gen_pci_setup(int nr, struct pci_sys_data *sys) --{ -- struct gen_pci *pci = sys->private_data; -- list_splice_init(&pci->resources, &sys->resources); -- return 1; --} -- - static int gen_pci_probe(struct platform_device *pdev) - { - int err; -@@ -326,13 +214,7 @@ static int gen_pci_probe(struct platform_device *pdev) - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - struct gen_pci *pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); -- struct hw_pci hw = { -- .nr_controllers = 1, -- .private_data = (void **)&pci, -- .setup = gen_pci_setup, -- .map_irq = of_irq_parse_and_map_pci, -- .ops = &gen_pci_ops, -- }; -+ struct pci_bus *bus, *child; - - if (!pci) - return -ENOMEM; -@@ -353,6 +235,7 @@ static int gen_pci_probe(struct platform_device *pdev) - - of_id = of_match_node(gen_pci_of_match, np); - pci->cfg.ops = of_id->data; -+ gen_pci_ops.map_bus = pci->cfg.ops->map_bus; - pci->host.dev.parent = dev; - INIT_LIST_HEAD(&pci->host.windows); - INIT_LIST_HEAD(&pci->resources); -@@ -369,7 +252,27 @@ static int gen_pci_probe(struct platform_device *pdev) - return err; - } - -- pci_common_init_dev(dev, &hw); -+ /* Do not reassign resources if probe only */ -+ if (!pci_has_flag(PCI_PROBE_ONLY)) -+ pci_add_flags(PCI_REASSIGN_ALL_RSRC | PCI_REASSIGN_ALL_BUS); -+ -+ bus = pci_scan_root_bus(dev, 0, &gen_pci_ops, pci, &pci->resources); -+ if (!bus) { -+ dev_err(dev, "Scanning rootbus failed"); -+ return -ENODEV; -+ } -+ -+ pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci); -+ -+ if (!pci_has_flag(PCI_PROBE_ONLY)) { -+ pci_bus_size_bridges(bus); -+ pci_bus_assign_resources(bus); -+ -+ list_for_each_entry(child, &bus->children, node) -+ pcie_bus_configure_settings(child); -+ } -+ -+ pci_bus_add_devices(bus); - return 0; - } - -diff --git a/drivers/pci/host/pci-keystone-dw.c b/drivers/pci/host/pci-keystone-dw.c -index 34086ce..c1b5980 100644 ---- a/drivers/pci/host/pci-keystone-dw.c -+++ b/drivers/pci/host/pci-keystone-dw.c -@@ -70,7 +70,7 @@ static inline void update_reg_offset_bit_pos(u32 offset, u32 *reg_offset, - *bit_pos = offset >> 3; - } - --u32 ks_dw_pcie_get_msi_addr(struct pcie_port *pp) -+phys_addr_t ks_dw_pcie_get_msi_addr(struct pcie_port *pp) - { - struct keystone_pcie *ks_pcie = to_keystone_pcie(pp); - -@@ -104,14 +104,13 @@ static void ks_dw_pcie_msi_irq_ack(struct irq_data *d) - { - u32 offset, reg_offset, bit_pos; - struct keystone_pcie *ks_pcie; -- unsigned int irq = d->irq; - struct msi_desc *msi; - struct pcie_port *pp; - -- msi = irq_get_msi_desc(irq); -- pp = sys_to_pcie(msi->dev->bus->sysdata); -+ msi = irq_data_get_msi_desc(d); -+ pp = sys_to_pcie(msi_desc_to_pci_sysdata(msi)); - ks_pcie = to_keystone_pcie(pp); -- offset = irq - irq_linear_revmap(pp->irq_domain, 0); -+ offset = d->irq - irq_linear_revmap(pp->irq_domain, 0); - update_reg_offset_bit_pos(offset, ®_offset, &bit_pos); - - writel(BIT(bit_pos), -@@ -142,20 +141,19 @@ void ks_dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq) - static void ks_dw_pcie_msi_irq_mask(struct irq_data *d) - { - struct keystone_pcie *ks_pcie; -- unsigned int irq = d->irq; - struct msi_desc *msi; - struct pcie_port *pp; - u32 offset; - -- msi = irq_get_msi_desc(irq); -- pp = sys_to_pcie(msi->dev->bus->sysdata); -+ msi = irq_data_get_msi_desc(d); -+ pp = sys_to_pcie(msi_desc_to_pci_sysdata(msi)); - ks_pcie = to_keystone_pcie(pp); -- offset = irq - irq_linear_revmap(pp->irq_domain, 0); -+ offset = d->irq - irq_linear_revmap(pp->irq_domain, 0); - - /* Mask the end point if PVM implemented */ - if (IS_ENABLED(CONFIG_PCI_MSI)) { - if (msi->msi_attrib.maskbit) -- mask_msi_irq(d); -+ pci_msi_mask_irq(d); - } - - ks_dw_pcie_msi_clear_irq(pp, offset); -@@ -164,20 +162,19 @@ static void ks_dw_pcie_msi_irq_mask(struct irq_data *d) - static void ks_dw_pcie_msi_irq_unmask(struct irq_data *d) - { - struct keystone_pcie *ks_pcie; -- unsigned int irq = d->irq; - struct msi_desc *msi; - struct pcie_port *pp; - u32 offset; - -- msi = irq_get_msi_desc(irq); -- pp = sys_to_pcie(msi->dev->bus->sysdata); -+ msi = irq_data_get_msi_desc(d); -+ pp = sys_to_pcie(msi_desc_to_pci_sysdata(msi)); - ks_pcie = to_keystone_pcie(pp); -- offset = irq - irq_linear_revmap(pp->irq_domain, 0); -+ offset = d->irq - irq_linear_revmap(pp->irq_domain, 0); - - /* Mask the end point if PVM implemented */ - if (IS_ENABLED(CONFIG_PCI_MSI)) { - if (msi->msi_attrib.maskbit) -- unmask_msi_irq(d); -+ pci_msi_unmask_irq(d); - } - - ks_dw_pcie_msi_set_irq(pp, offset); -@@ -196,7 +193,6 @@ static int ks_dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq, - irq_set_chip_and_handler(irq, &ks_dw_pcie_msi_irq_chip, - handle_level_irq); - irq_set_chip_data(irq, domain->host_data); -- set_irq_flags(irq, IRQF_VALID); - - return 0; - } -@@ -205,7 +201,7 @@ const struct irq_domain_ops ks_dw_pcie_msi_domain_ops = { - .map = ks_dw_pcie_msi_map, - }; - --int ks_dw_pcie_msi_host_init(struct pcie_port *pp, struct msi_chip *chip) -+int ks_dw_pcie_msi_host_init(struct pcie_port *pp, struct msi_controller *chip) - { - struct keystone_pcie *ks_pcie = to_keystone_pcie(pp); - int i; -@@ -277,7 +273,6 @@ static int ks_dw_pcie_init_legacy_irq_map(struct irq_domain *d, - irq_set_chip_and_handler(irq, &ks_dw_pcie_legacy_irq_chip, - handle_level_irq); - irq_set_chip_data(irq, d->host_data); -- set_irq_flags(irq, IRQF_VALID); - - return 0; - } -@@ -327,7 +322,7 @@ static void ks_dw_pcie_clear_dbi_mode(void __iomem *reg_virt) - void ks_dw_pcie_setup_rc_app_regs(struct keystone_pcie *ks_pcie) - { - struct pcie_port *pp = &ks_pcie->pp; -- u32 start = pp->mem.start, end = pp->mem.end; -+ u32 start = pp->mem->start, end = pp->mem->end; - int i, tr_size; - - /* Disable BARs for inbound access */ -@@ -403,7 +398,7 @@ int ks_dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, - - addr = ks_pcie_cfg_setup(ks_pcie, bus_num, devfn); - -- return dw_pcie_cfg_read(addr + (where & ~0x3), where, size, val); -+ return dw_pcie_cfg_read(addr + where, size, val); - } - - int ks_dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, -@@ -415,7 +410,7 @@ int ks_dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, - - addr = ks_pcie_cfg_setup(ks_pcie, bus_num, devfn); - -- return dw_pcie_cfg_write(addr + (where & ~0x3), where, size, val); -+ return dw_pcie_cfg_write(addr + where, size, val); - } - - /** -diff --git a/drivers/pci/host/pci-keystone.h b/drivers/pci/host/pci-keystone.h -index 1fc1fce..f0944e8 100644 ---- a/drivers/pci/host/pci-keystone.h -+++ b/drivers/pci/host/pci-keystone.h -@@ -37,7 +37,7 @@ struct keystone_pcie { - - /* Keystone DW specific MSI controller APIs/definitions */ - void ks_dw_pcie_handle_msi_irq(struct keystone_pcie *ks_pcie, int offset); --u32 ks_dw_pcie_get_msi_addr(struct pcie_port *pp); -+phys_addr_t ks_dw_pcie_get_msi_addr(struct pcie_port *pp); - - /* Keystone specific PCI controller APIs */ - void ks_dw_pcie_enable_legacy_irqs(struct keystone_pcie *ks_pcie); -@@ -55,4 +55,4 @@ void ks_dw_pcie_msi_set_irq(struct pcie_port *pp, int irq); - void ks_dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq); - void ks_dw_pcie_v3_65_scan_bus(struct pcie_port *pp); - int ks_dw_pcie_msi_host_init(struct pcie_port *pp, -- struct msi_chip *chip); -+ struct msi_controller *chip); -diff --git a/drivers/pci/host/pci-layerscape.c b/drivers/pci/host/pci-layerscape.c -new file mode 100644 -index 0000000..baa1232 ---- /dev/null -+++ b/drivers/pci/host/pci-layerscape.c -@@ -0,0 +1,729 @@ -+/* -+ * PCIe host controller driver for Freescale Layerscape SoCs -+ * -+ * Copyright (C) 2014 Freescale Semiconductor. -+ * -+ * Author: Minghuan Lian -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "pcie-designware.h" -+ -+/* PEX1/2 Misc Ports Status Register */ -+#define SCFG_PEXMSCPORTSR(pex_idx) (0x94 + (pex_idx) * 4) -+#define SCFG_PEXPMWRCR(pex_idx) (0x5c + (pex_idx) * 0x64) -+#define LTSSM_STATE_SHIFT 20 -+#define LTSSM_STATE_MASK 0x3f -+#define LTSSM_PCIE_L0 0x11 /* L0 state */ -+#define LTSSM_PCIE_L2_IDLE 0x15 /* L2 idle state */ -+ -+#define PCIE_SRIOV_OFFSET 0x178 -+ -+/* CS2 */ -+#define PCIE_CS2_OFFSET 0x1000 /* For PCIe without SR-IOV */ -+#define PCIE_ENABLE_CS2 0x80000000 /* For PCIe with SR-IOV */ -+ -+/* PEX Internal Configuration Registers */ -+#define PCIE_STRFMR1 0x71c /* Symbol Timer & Filter Mask Register1 */ -+#define PCIE_DBI_RO_WR_EN 0x8bc /* DBI Read-Only Write Enable Register */ -+#define PCIE_ABSERR 0x8d0 /* Bridge Slave Error Response Register */ -+#define PCIE_ABSERR_SETTING 0x9401 /* Forward error of non-posted request */ -+ -+/* PEX LUT registers */ -+#define PCIE_LUT_DBG 0x7FC /* PEX LUT Debug Register */ -+#define PCIE_LUT_CTRL0 0x7f8 -+#define PCIE_LUT_UDR(n) (0x800 + (n) * 8) -+#define PCIE_LUT_LDR(n) (0x804 + (n) * 8) -+#define PCIE_LUT_MASK_ALL 0xffff -+#define PCIE_LUT_DR_NUM 32 -+#define PCIE_LUT_ENABLE (1 << 31) -+ -+#define PCIE_PM_SCR 0x44 -+#define PCIE_PM_SCR_PMEEN 0x10 -+#define PCIE_PM_SCR_PMEPS_D0 0xfffc -+#define PCIE_PM_SCR_PMEPS_D3 0x3 -+#define PCIE_PM_SCR_PME_STATE 0x8000 -+ -+#define PCIE_PEX_DCR 0x78 -+#define PCIE_PEX_DCR_AUXPOWEREN 0x0400 -+ -+#define PCIE_PEX_SSR 0x8a -+#define PCIE_PEX_SSR_PDS 0x40 -+ -+#define PCIE_PEX_RCR 0x8c -+#define PCIE_PEX_RCR_PMEIE 0x0008 -+ -+#define PCIE_PEX_RSR 0x90 -+#define PCIE_PEX_PMES 0x00010000 -+ -+#define QIXIS_RST_FORCE_3 0x45 -+#define QIXIS_RST_FORCE_3_PCIESLOT 0xe0 -+ -+#define CPLD_RST_PCIE_SLOT 0x14 -+#define CPLD_RST_PCIESLOT 0x3 -+ -+#define PCIE_IATU_NUM 6 -+ -+struct ls_pcie; -+ -+struct ls_pcie_pm_data { -+ void __iomem *fpga; -+ void __iomem *cpld; -+}; -+ -+struct ls_pcie_pm_ops { -+ u32 (*get_link_state)(struct ls_pcie *pcie); -+ int (*send_turn_off_message)(struct ls_pcie *pcie); -+ void (*clear_turn_off_message)(struct ls_pcie *pcie); -+ void (*reset_slot)(struct ls_pcie *pcie, -+ struct ls_pcie_pm_data *pm_data); -+}; -+ -+struct ls_pcie_drvdata { -+ u32 lut_offset; -+ u32 ltssm_shift; -+ struct pcie_host_ops *ops; -+ struct ls_pcie_pm_ops *pm; -+}; -+ -+struct ls_pcie { -+ struct list_head list_node; -+ void __iomem *dbi; -+ void __iomem *lut; -+ struct regmap *scfg; -+ struct pcie_port pp; -+ const struct ls_pcie_drvdata *drvdata; -+ struct ls_pcie_pm_data pm_data; -+ int index; -+ const u32 *avail_streamids; -+ int streamid_index; -+ int pme_irq; -+ bool in_slot; -+}; -+ -+#define to_ls_pcie(x) container_of(x, struct ls_pcie, pp) -+ -+static void ls_pcie_host_init(struct pcie_port *pp); -+ -+u32 set_pcie_streamid_translation(struct pci_dev *pdev, u32 devid) -+{ -+ u32 index, streamid; -+ struct pcie_port *pp = pdev->bus->sysdata; -+ struct ls_pcie *pcie = to_ls_pcie(pp); -+ -+ if (!pcie->avail_streamids || !pcie->streamid_index) -+ return ~(u32)0; -+ -+ index = --pcie->streamid_index; -+ /* mask is set as all zeroes, want to match all bits */ -+ iowrite32((devid << 16), pcie->lut + PCIE_LUT_UDR(index)); -+ streamid = be32_to_cpup(&pcie->avail_streamids[index]); -+ iowrite32(streamid | PCIE_LUT_ENABLE, pcie->lut + PCIE_LUT_LDR(index)); -+ -+ return streamid; -+} -+ -+LIST_HEAD(hose_list); -+ -+static bool ls_pcie_is_bridge(struct ls_pcie *pcie) -+{ -+ u32 header_type; -+ -+ header_type = ioread8(pcie->dbi + PCI_HEADER_TYPE); -+ header_type &= 0x7f; -+ -+ return header_type == PCI_HEADER_TYPE_BRIDGE; -+} -+ -+/* Clear multi-function bit */ -+static void ls_pcie_clear_multifunction(struct ls_pcie *pcie) -+{ -+ iowrite8(PCI_HEADER_TYPE_BRIDGE, pcie->dbi + PCI_HEADER_TYPE); -+} -+ -+/* Fix class value */ -+static void ls_pcie_fix_class(struct ls_pcie *pcie) -+{ -+ iowrite16(PCI_CLASS_BRIDGE_PCI, pcie->dbi + PCI_CLASS_DEVICE); -+} -+ -+/* Drop MSG TLP except for Vendor MSG */ -+static void ls_pcie_drop_msg_tlp(struct ls_pcie *pcie) -+{ -+ u32 val; -+ -+ val = ioread32(pcie->dbi + PCIE_STRFMR1); -+ val &= 0xDFFFFFFF; -+ iowrite32(val, pcie->dbi + PCIE_STRFMR1); -+} -+ -+/* Disable all bars in RC mode */ -+static void ls_pcie_disable_bars(struct ls_pcie *pcie) -+{ -+ u32 header; -+ -+ header = ioread32(pcie->dbi + PCIE_SRIOV_OFFSET); -+ if (PCI_EXT_CAP_ID(header) == PCI_EXT_CAP_ID_SRIOV) { -+ iowrite32(PCIE_ENABLE_CS2, pcie->lut + PCIE_LUT_CTRL0); -+ iowrite32(0, pcie->dbi + PCI_BASE_ADDRESS_0); -+ iowrite32(0, pcie->dbi + PCI_BASE_ADDRESS_1); -+ iowrite32(0, pcie->dbi + PCI_ROM_ADDRESS1); -+ iowrite32(0, pcie->lut + PCIE_LUT_CTRL0); -+ } else { -+ iowrite32(0, -+ pcie->dbi + PCIE_CS2_OFFSET + PCI_BASE_ADDRESS_0); -+ iowrite32(0, -+ pcie->dbi + PCIE_CS2_OFFSET + PCI_BASE_ADDRESS_1); -+ iowrite32(0, -+ pcie->dbi + PCIE_CS2_OFFSET + PCI_ROM_ADDRESS1); -+ } -+} -+ -+static void ls_pcie_disable_outbound_atus(struct ls_pcie *pcie) -+{ -+ int i; -+ -+ for (i = 0; i < PCIE_IATU_NUM; i++) -+ dw_pcie_disable_outbound_atu(&pcie->pp, i); -+} -+ -+/* Forward error response of outbound non-posted requests */ -+static void ls_pcie_fix_error_response(struct ls_pcie *pcie) -+{ -+ iowrite32(PCIE_ABSERR_SETTING, pcie->dbi + PCIE_ABSERR); -+} -+ -+static int ls1021_pcie_link_up(struct pcie_port *pp) -+{ -+ u32 state; -+ struct ls_pcie *pcie = to_ls_pcie(pp); -+ -+ if (!pcie->scfg) -+ return 0; -+ -+ regmap_read(pcie->scfg, SCFG_PEXMSCPORTSR(pcie->index), &state); -+ state = (state >> LTSSM_STATE_SHIFT) & LTSSM_STATE_MASK; -+ -+ if (state < LTSSM_PCIE_L0) -+ return 0; -+ -+ return 1; -+} -+ -+static u32 ls1021_pcie_get_link_state(struct ls_pcie *pcie) -+{ -+ u32 state; -+ -+ if (!pcie->scfg) -+ return 0; -+ -+ regmap_read(pcie->scfg, SCFG_PEXMSCPORTSR(pcie->index), &state); -+ state = (state >> LTSSM_STATE_SHIFT) & LTSSM_STATE_MASK; -+ -+ return state; -+} -+ -+static int ls1021_pcie_send_turn_off_message(struct ls_pcie *pcie) -+{ -+ u32 val; -+ -+ if (!pcie->scfg) -+ return -EINVAL; -+ -+ /* Send Turn_off message */ -+ regmap_read(pcie->scfg, SCFG_PEXPMWRCR(pcie->index), &val); -+ val |= 0x80000000; -+ regmap_write(pcie->scfg, SCFG_PEXPMWRCR(pcie->index), val); -+ -+ return 0; -+} -+ -+static void ls1021_pcie_clear_turn_off_message(struct ls_pcie *pcie) -+{ -+ u32 val; -+ -+ if (!pcie->scfg) -+ return; -+ -+ /* Clear Turn_off message */ -+ regmap_read(pcie->scfg, SCFG_PEXPMWRCR(pcie->index), &val); -+ val &= 0x00000000; -+ regmap_write(pcie->scfg, SCFG_PEXPMWRCR(pcie->index), val); -+} -+ -+static void ls1021_pcie_reset_slot(struct ls_pcie *pcie, -+ struct ls_pcie_pm_data *pm_data) -+{ -+ u8 val; -+ -+ /* Try to reset PCIe slot to relink EP */ -+ if (pm_data->fpga) { -+ /* PULL DOWN PCIe RST# */ -+ val = ioread8(pm_data->fpga + QIXIS_RST_FORCE_3); -+ val |= QIXIS_RST_FORCE_3_PCIESLOT; -+ iowrite8(val, pm_data->fpga + QIXIS_RST_FORCE_3); -+ -+ /* PULL ON PCIe RST# */ -+ val = ioread8(pm_data->fpga + QIXIS_RST_FORCE_3); -+ val &= 0x0; -+ iowrite8(val, pm_data->fpga + QIXIS_RST_FORCE_3); -+ } -+ -+ if (pm_data->cpld) { -+ /* PULL DOWN PCIe RST# */ -+ val = ioread8(pm_data->cpld + CPLD_RST_PCIE_SLOT); -+ val &= 0x0; -+ iowrite8(val, pm_data->cpld + CPLD_RST_PCIE_SLOT); -+ -+ /* PULL ON PCIe RST# */ -+ val = ioread8(pm_data->cpld + CPLD_RST_PCIE_SLOT); -+ val |= CPLD_RST_PCIESLOT; -+ iowrite8(val, pm_data->cpld + CPLD_RST_PCIE_SLOT); -+ } -+} -+ -+static void ls1021_pcie_host_init(struct pcie_port *pp) -+{ -+ struct ls_pcie *pcie = to_ls_pcie(pp); -+ u32 index[2]; -+ -+ pcie->scfg = syscon_regmap_lookup_by_phandle(pp->dev->of_node, -+ "fsl,pcie-scfg"); -+ if (IS_ERR(pcie->scfg)) { -+ dev_err(pp->dev, "No syscfg phandle specified\n"); -+ pcie->scfg = NULL; -+ return; -+ } -+ -+ if (of_property_read_u32_array(pp->dev->of_node, -+ "fsl,pcie-scfg", index, 2)) { -+ pcie->scfg = NULL; -+ return; -+ } -+ pcie->index = index[1]; -+ -+ ls_pcie_host_init(pp); -+ -+ dw_pcie_setup_rc(pp); -+} -+ -+static int ls_pcie_link_up(struct pcie_port *pp) -+{ -+ struct ls_pcie *pcie = to_ls_pcie(pp); -+ u32 state, offset; -+ -+ if (of_get_property(pp->dev->of_node, "fsl,lut_diff", NULL)) -+ offset = 0x407fc; -+ else -+ offset = PCIE_LUT_DBG; -+ -+ state = (ioread32(pcie->lut + offset) >> -+ pcie->drvdata->ltssm_shift) & -+ LTSSM_STATE_MASK; -+ -+ if (state < LTSSM_PCIE_L0) -+ return 0; -+ -+ return 1; -+} -+ -+static u32 ls_pcie_get_link_state(struct ls_pcie *pcie) -+{ -+ return (ioread32(pcie->lut + PCIE_LUT_DBG) >> -+ pcie->drvdata->ltssm_shift) & -+ LTSSM_STATE_MASK; -+} -+ -+static void ls_pcie_host_init(struct pcie_port *pp) -+{ -+ struct ls_pcie *pcie = to_ls_pcie(pp); -+ -+ iowrite32(1, pcie->dbi + PCIE_DBI_RO_WR_EN); -+ ls_pcie_fix_class(pcie); -+ ls_pcie_clear_multifunction(pcie); -+ ls_pcie_drop_msg_tlp(pcie); -+ iowrite32(0, pcie->dbi + PCIE_DBI_RO_WR_EN); -+ -+ ls_pcie_disable_bars(pcie); -+ ls_pcie_disable_outbound_atus(pcie); -+ ls_pcie_fix_error_response(pcie); -+} -+ -+static int ls_pcie_msi_host_init(struct pcie_port *pp, -+ struct msi_controller *chip) -+{ -+ struct device_node *msi_node; -+ struct device_node *np = pp->dev->of_node; -+ -+ /* -+ * The MSI domain is set by the generic of_msi_configure(). This -+ * .msi_host_init() function keeps us from doing the default MSI -+ * domain setup in dw_pcie_host_init() and also enforces the -+ * requirement that "msi-parent" exists. -+ */ -+ msi_node = of_parse_phandle(np, "msi-parent", 0); -+ if (!msi_node) { -+ dev_err(pp->dev, "failed to find msi-parent\n"); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static struct pcie_host_ops ls1021_pcie_host_ops = { -+ .link_up = ls1021_pcie_link_up, -+ .host_init = ls1021_pcie_host_init, -+ .msi_host_init = ls_pcie_msi_host_init, -+}; -+ -+static struct ls_pcie_pm_ops ls1021_pcie_host_pm_ops = { -+ .get_link_state = &ls1021_pcie_get_link_state, -+ .send_turn_off_message = &ls1021_pcie_send_turn_off_message, -+ .clear_turn_off_message = &ls1021_pcie_clear_turn_off_message, -+ .reset_slot = &ls1021_pcie_reset_slot, -+}; -+ -+static struct pcie_host_ops ls_pcie_host_ops = { -+ .link_up = ls_pcie_link_up, -+ .host_init = ls_pcie_host_init, -+ .msi_host_init = ls_pcie_msi_host_init, -+}; -+ -+static struct ls_pcie_pm_ops ls_pcie_host_pm_ops = { -+ .get_link_state = &ls_pcie_get_link_state, -+}; -+ -+static struct ls_pcie_drvdata ls1021_drvdata = { -+ .ops = &ls1021_pcie_host_ops, -+ .pm = &ls1021_pcie_host_pm_ops, -+}; -+ -+static struct ls_pcie_drvdata ls1043_drvdata = { -+ .lut_offset = 0x10000, -+ .ltssm_shift = 24, -+ .ops = &ls_pcie_host_ops, -+ .pm = &ls_pcie_host_pm_ops, -+}; -+ -+static struct ls_pcie_drvdata ls2080_drvdata = { -+ .lut_offset = 0x80000, -+ .ltssm_shift = 0, -+ .ops = &ls_pcie_host_ops, -+ .pm = &ls_pcie_host_pm_ops, -+}; -+ -+static const struct of_device_id ls_pcie_of_match[] = { -+ { .compatible = "fsl,ls1021a-pcie", .data = &ls1021_drvdata }, -+ { .compatible = "fsl,ls1043a-pcie", .data = &ls1043_drvdata }, -+ { .compatible = "fsl,ls2080a-pcie", .data = &ls2080_drvdata }, -+ { .compatible = "fsl,ls2085a-pcie", .data = &ls2080_drvdata }, -+ { }, -+}; -+MODULE_DEVICE_TABLE(of, ls_pcie_of_match); -+ -+static void ls_pcie_host_hack_pm_init(struct ls_pcie *pcie) -+{ -+ struct device_node *np; -+ struct ls_pcie_pm_data *pm_data = &pcie->pm_data; -+ -+ np = of_find_compatible_node(NULL, NULL, "fsl,ls1021aqds-fpga"); -+ if (np) -+ pm_data->fpga = of_iomap(np, 0); -+ -+ of_node_put(np); -+ -+ np = of_find_compatible_node(NULL, NULL, "fsl,ls1021atwr-cpld"); -+ if (np) -+ pm_data->cpld = of_iomap(np, 0); -+ -+ of_node_put(np); -+} -+ -+static irqreturn_t ls_pcie_pme_irq_handler(int irq, void *data) -+{ -+ struct pcie_port *pp = data; -+ struct ls_pcie *pcie = to_ls_pcie(pp); -+ u32 val; -+ -+ if (pcie->drvdata->pm->clear_turn_off_message) -+ pcie->drvdata->pm->clear_turn_off_message(pcie); -+ -+ /* Clear Host root PME_STATE bit */ -+ val = ioread32(pcie->dbi + PCIE_PEX_RSR); -+ val |= PCIE_PEX_PMES; -+ iowrite32(val, pcie->dbi + PCIE_PEX_RSR); -+ -+ return IRQ_HANDLED; -+} -+ -+static int ls_pcie_host_pme_init(struct ls_pcie *pcie, -+ struct platform_device *pdev) -+{ -+ struct pcie_port *pp; -+ int ret; -+ u16 val; -+ -+ pp = &pcie->pp; -+ -+ if (dw_pcie_link_up(&pcie->pp)) -+ pcie->in_slot = true; -+ else -+ pcie->in_slot = false; -+ -+ pcie->pme_irq = platform_get_irq_byname(pdev, "pme"); -+ if (pcie->pme_irq < 0) { -+ dev_err(&pdev->dev, -+ "failed to get PME IRQ: %d\n", pcie->pme_irq); -+ return pcie->pme_irq; -+ } -+ -+ ret = devm_request_irq(pp->dev, pcie->pme_irq, ls_pcie_pme_irq_handler, -+ IRQF_SHARED, "ls-pcie-pme", pp); -+ if (ret) { -+ dev_err(pp->dev, "Failed to request pme irq\n"); -+ return ret; -+ } -+ -+ ls_pcie_host_hack_pm_init(pcie); -+ -+ /* AUX Power PM Enable */ -+ val = ioread16(pcie->dbi + PCIE_PEX_DCR); -+ val |= PCIE_PEX_DCR_AUXPOWEREN; -+ iowrite16(val, pcie->dbi + PCIE_PEX_DCR); -+ -+ /* Enable PME message */ -+ val = ioread16(pcie->dbi + PCIE_PM_SCR); -+ val |= PCIE_PM_SCR_PMEEN; -+ iowrite16(val, pcie->dbi + PCIE_PM_SCR); -+ -+ /* Clear Host PME_STATE bit */ -+ val = ioread16(pcie->dbi + PCIE_PM_SCR); -+ val |= PCIE_PM_SCR_PME_STATE; -+ iowrite16(val, pcie->dbi + PCIE_PM_SCR); -+ -+ /* Enable Host %d interrupt */ -+ val = ioread16(pcie->dbi + PCIE_PEX_RCR); -+ val |= PCIE_PEX_RCR_PMEIE; -+ iowrite16(val, pcie->dbi + PCIE_PEX_RCR); -+ -+ return 0; -+} -+ -+static int __init ls_add_pcie_port(struct pcie_port *pp, -+ struct platform_device *pdev) -+{ -+ int ret; -+ struct ls_pcie *pcie = to_ls_pcie(pp); -+ -+ pp->dev = &pdev->dev; -+ pp->dbi_base = pcie->dbi; -+ pp->ops = pcie->drvdata->ops; -+ -+ ret = dw_pcie_host_init(pp); -+ if (ret) { -+ dev_err(pp->dev, "failed to initialize host\n"); -+ return ret; -+ } -+ -+ ret = ls_pcie_host_pme_init(pcie, pdev); -+ if (ret) -+ dev_warn(pp->dev, "failed to initialize PME\n"); -+ -+ return 0; -+} -+ -+static int ls_pcie_probe(struct platform_device *pdev) -+{ -+ const struct of_device_id *match; -+ struct ls_pcie *pcie; -+ struct resource *dbi_base; -+ int ret; -+ -+ match = of_match_device(ls_pcie_of_match, &pdev->dev); -+ if (!match) -+ return -ENODEV; -+ -+ pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL); -+ if (!pcie) -+ return -ENOMEM; -+ -+ dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); -+ pcie->dbi = devm_ioremap_resource(&pdev->dev, dbi_base); -+ if (IS_ERR(pcie->dbi)) { -+ dev_err(&pdev->dev, "missing *regs* space\n"); -+ return PTR_ERR(pcie->dbi); -+ } -+ -+ pcie->drvdata = match->data; -+ pcie->lut = pcie->dbi + pcie->drvdata->lut_offset; -+ /* Disable LDR zero */ -+ iowrite32(0, pcie->lut + PCIE_LUT_LDR(0)); -+ -+ if (!ls_pcie_is_bridge(pcie)) -+ return -ENODEV; -+ -+ if (of_device_is_compatible(pdev->dev.of_node, "fsl,ls2085a-pcie") || -+ of_device_is_compatible(pdev->dev.of_node, "fsl,ls2080a-pcie")) { -+ int len; -+ const u32 *prop; -+ struct device_node *np; -+ -+ np = pdev->dev.of_node; -+ prop = (u32 *)of_get_property(np, "available-stream-ids", &len); -+ if (prop) { -+ pcie->avail_streamids = prop; -+ pcie->streamid_index = len/sizeof(u32); -+ } else -+ dev_err(&pdev->dev, "PCIe endpoint partitioning not possible\n"); -+ } -+ -+ ret = ls_add_pcie_port(&pcie->pp, pdev); -+ if (ret < 0) -+ return ret; -+ -+ list_add_tail(&pcie->list_node, &hose_list); -+ -+ platform_set_drvdata(pdev, pcie); -+ -+ return 0; -+} -+ -+#ifdef CONFIG_PM_SLEEP -+static int ls_pcie_pm_do_suspend(struct ls_pcie *pcie) -+{ -+ u32 state; -+ int i = 0; -+ int ret; -+ u16 val; -+ -+ if (!pcie->in_slot) -+ return 0; -+ -+ if (!pcie->drvdata->pm->send_turn_off_message) -+ return 0; -+ -+ ret = pcie->drvdata->pm->send_turn_off_message(pcie); -+ if (ret) -+ return -EINVAL; -+ -+ while (i < 100) { -+ state = pcie->drvdata->pm->get_link_state(pcie); -+ if (state == LTSSM_PCIE_L2_IDLE) -+ break; -+ i++; -+ mdelay(1); -+ } -+ -+ /* Put RC in D3 */ -+ val = ioread16(pcie->dbi + PCIE_PM_SCR); -+ val |= PCIE_PM_SCR_PMEPS_D3; -+ iowrite16(val, pcie->dbi + PCIE_PM_SCR); -+ -+ mdelay(10); -+ -+ return 0; -+} -+ -+static int ls_pcie_pm_do_resume(struct ls_pcie *pcie) -+{ -+ u32 state; -+ int i = 0; -+ u16 val; -+ struct pcie_port *pp = &pcie->pp; -+ -+ if (!pcie->in_slot) -+ return 0; -+ -+ dw_pcie_setup_rc(pp); -+ ls_pcie_host_init(pp); -+ -+ /* Put RC in D0 */ -+ val = ioread16(pcie->dbi + PCIE_PM_SCR); -+ val &= PCIE_PM_SCR_PMEPS_D0; -+ iowrite16(val, pcie->dbi + PCIE_PM_SCR); -+ -+ mdelay(10); -+ -+ state = pcie->drvdata->pm->get_link_state(pcie); -+ if (state == LTSSM_PCIE_L0) -+ return 0; -+ -+ if (!pcie->drvdata->pm->reset_slot) -+ return -EINVAL; -+ -+ pcie->drvdata->pm->reset_slot(pcie, &pcie->pm_data); -+ -+ while (i < 100) { -+ state = pcie->drvdata->pm->get_link_state(pcie); -+ if (state == LTSSM_PCIE_L0) -+ return 0; -+ i++; -+ mdelay(1); -+ } -+ -+ return -EINVAL; -+} -+ -+static int ls_pcie_pm_suspend(void) -+{ -+ struct ls_pcie *hose, *tmp; -+ -+ list_for_each_entry_safe(hose, tmp, &hose_list, list_node) -+ ls_pcie_pm_do_suspend(hose); -+ -+ return 0; -+} -+ -+static void ls_pcie_pm_resume(void) -+{ -+ struct ls_pcie *hose, *tmp; -+ -+ list_for_each_entry_safe(hose, tmp, &hose_list, list_node) -+ ls_pcie_pm_do_resume(hose); -+} -+ -+static struct syscore_ops ls_pcie_syscore_pm_ops = { -+ .suspend = ls_pcie_pm_suspend, -+ .resume = ls_pcie_pm_resume, -+}; -+#endif /* CONFIG_PM_SLEEP */ -+ -+static struct platform_driver ls_pcie_driver = { -+ .probe = ls_pcie_probe, -+ .driver = { -+ .name = "layerscape-pcie", -+ .of_match_table = ls_pcie_of_match, -+ }, -+}; -+ -+static int __init fsl_pci_init(void) -+{ -+#ifdef CONFIG_PM_SLEEP -+ register_syscore_ops(&ls_pcie_syscore_pm_ops); -+#endif -+ return platform_driver_register(&ls_pcie_driver); -+} -+module_init(fsl_pci_init); -+ -+MODULE_AUTHOR("Minghuan Lian "); -+MODULE_DESCRIPTION("Freescale Layerscape PCIe host controller driver"); -+MODULE_LICENSE("GPL v2"); -diff --git a/drivers/pci/host/pci-layerscape.h b/drivers/pci/host/pci-layerscape.h -new file mode 100644 -index 0000000..e90e114 ---- /dev/null -+++ b/drivers/pci/host/pci-layerscape.h -@@ -0,0 +1,13 @@ -+/* -+ * 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 _PCI_LAYERSCAPE_H -+#define _PCI_LAYERSCAPE_H -+ -+/* function for setting up stream id to device id translation */ -+u32 set_pcie_streamid_translation(struct pci_dev *pdev, u32 devid); -+ -+#endif /* _PCI_LAYERSCAPE_H */ -diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c -index b1315e1..94b42d1 100644 ---- a/drivers/pci/host/pci-mvebu.c -+++ b/drivers/pci/host/pci-mvebu.c -@@ -99,11 +99,9 @@ struct mvebu_pcie_port; - struct mvebu_pcie { - struct platform_device *pdev; - struct mvebu_pcie_port *ports; -- struct msi_chip *msi; -+ struct msi_controller *msi; - struct resource io; -- char io_name[30]; - struct resource realio; -- char mem_name[30]; - struct resource mem; - struct resource busn; - int nports; -@@ -722,18 +720,9 @@ static int mvebu_pcie_setup(int nr, struct pci_sys_data *sys) - { - struct mvebu_pcie *pcie = sys_to_pcie(sys); - int i; -- int domain = 0; - --#ifdef CONFIG_PCI_DOMAINS -- domain = sys->domain; --#endif -- -- snprintf(pcie->mem_name, sizeof(pcie->mem_name), "PCI MEM %04x", -- domain); -- pcie->mem.name = pcie->mem_name; -- -- snprintf(pcie->io_name, sizeof(pcie->io_name), "PCI I/O %04x", domain); -- pcie->realio.name = pcie->io_name; -+ pcie->mem.name = "PCI MEM"; -+ pcie->realio.name = "PCI I/O"; - - if (request_resource(&iomem_resource, &pcie->mem)) - return 0; -diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c -index 19bb19c..971d8d7 100644 ---- a/drivers/pci/host/pci-tegra.c -+++ b/drivers/pci/host/pci-tegra.c -@@ -238,7 +238,7 @@ - ) - - struct tegra_msi { -- struct msi_chip chip; -+ struct msi_controller chip; - DECLARE_BITMAP(used, INT_PCI_MSI_NR); - struct irq_domain *domain; - unsigned long pages; -@@ -259,7 +259,7 @@ struct tegra_pcie_soc_data { - bool has_gen2; - }; - --static inline struct tegra_msi *to_tegra_msi(struct msi_chip *chip) -+static inline struct tegra_msi *to_tegra_msi(struct msi_controller *chip) - { - return container_of(chip, struct tegra_msi, chip); - } -@@ -1280,8 +1280,8 @@ static irqreturn_t tegra_pcie_msi_irq(int irq, void *data) - return processed > 0 ? IRQ_HANDLED : IRQ_NONE; - } - --static int tegra_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, -- struct msi_desc *desc) -+static int tegra_msi_setup_irq(struct msi_controller *chip, -+ struct pci_dev *pdev, struct msi_desc *desc) - { - struct tegra_msi *msi = to_tegra_msi(chip); - struct msi_msg msg; -@@ -1305,12 +1305,13 @@ static int tegra_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, - msg.address_hi = 0; - msg.data = hwirq; - -- write_msi_msg(irq, &msg); -+ pci_write_msi_msg(irq, &msg); - - return 0; - } - --static void tegra_msi_teardown_irq(struct msi_chip *chip, unsigned int irq) -+static void tegra_msi_teardown_irq(struct msi_controller *chip, -+ unsigned int irq) - { - struct tegra_msi *msi = to_tegra_msi(chip); - struct irq_data *d = irq_get_irq_data(irq); -@@ -1322,10 +1323,10 @@ static void tegra_msi_teardown_irq(struct msi_chip *chip, unsigned int irq) - - static struct irq_chip tegra_msi_irq_chip = { - .name = "Tegra PCIe MSI", -- .irq_enable = unmask_msi_irq, -- .irq_disable = mask_msi_irq, -- .irq_mask = mask_msi_irq, -- .irq_unmask = unmask_msi_irq, -+ .irq_enable = pci_msi_unmask_irq, -+ .irq_disable = pci_msi_mask_irq, -+ .irq_mask = pci_msi_mask_irq, -+ .irq_unmask = pci_msi_unmask_irq, - }; - - static int tegra_msi_map(struct irq_domain *domain, unsigned int irq, -@@ -1333,7 +1334,6 @@ static int tegra_msi_map(struct irq_domain *domain, unsigned int irq, - { - irq_set_chip_and_handler(irq, &tegra_msi_irq_chip, handle_simple_irq); - irq_set_chip_data(irq, domain->host_data); -- set_irq_flags(irq, IRQF_VALID); - - tegra_cpuidle_pcie_irqs_in_use(); - -diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c -new file mode 100644 -index 0000000..8e559d1 ---- /dev/null -+++ b/drivers/pci/host/pci-xgene-msi.c -@@ -0,0 +1,595 @@ -+/* -+ * APM X-Gene MSI Driver -+ * -+ * Copyright (c) 2014, Applied Micro Circuits Corporation -+ * Author: Tanmay Inamdar -+ * Duc Dang -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define MSI_IR0 0x000000 -+#define MSI_INT0 0x800000 -+#define IDX_PER_GROUP 8 -+#define IRQS_PER_IDX 16 -+#define NR_HW_IRQS 16 -+#define NR_MSI_VEC (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS) -+ -+struct xgene_msi_group { -+ struct xgene_msi *msi; -+ int gic_irq; -+ u32 msi_grp; -+}; -+ -+struct xgene_msi { -+ struct device_node *node; -+ struct msi_controller mchip; -+ struct irq_domain *domain; -+ u64 msi_addr; -+ void __iomem *msi_regs; -+ unsigned long *bitmap; -+ struct mutex bitmap_lock; -+ struct xgene_msi_group *msi_groups; -+ int num_cpus; -+}; -+ -+/* Global data */ -+static struct xgene_msi xgene_msi_ctrl; -+ -+static struct irq_chip xgene_msi_top_irq_chip = { -+ .name = "X-Gene1 MSI", -+ .irq_enable = pci_msi_unmask_irq, -+ .irq_disable = pci_msi_mask_irq, -+ .irq_mask = pci_msi_mask_irq, -+ .irq_unmask = pci_msi_unmask_irq, -+}; -+ -+static struct msi_domain_info xgene_msi_domain_info = { -+ .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | -+ MSI_FLAG_PCI_MSIX), -+ .chip = &xgene_msi_top_irq_chip, -+}; -+ -+/* -+ * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where -+ * n is group number (0..F), x is index of registers in each group (0..7) -+ * The register layout is as follows: -+ * MSI0IR0 base_addr -+ * MSI0IR1 base_addr + 0x10000 -+ * ... ... -+ * MSI0IR6 base_addr + 0x60000 -+ * MSI0IR7 base_addr + 0x70000 -+ * MSI1IR0 base_addr + 0x80000 -+ * MSI1IR1 base_addr + 0x90000 -+ * ... ... -+ * MSI1IR7 base_addr + 0xF0000 -+ * MSI2IR0 base_addr + 0x100000 -+ * ... ... -+ * MSIFIR0 base_addr + 0x780000 -+ * MSIFIR1 base_addr + 0x790000 -+ * ... ... -+ * MSIFIR7 base_addr + 0x7F0000 -+ * MSIINT0 base_addr + 0x800000 -+ * MSIINT1 base_addr + 0x810000 -+ * ... ... -+ * MSIINTF base_addr + 0x8F0000 -+ * -+ * Each index register supports 16 MSI vectors (0..15) to generate interrupt. -+ * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination -+ * registers. -+ * -+ * Each MSI termination group has 1 MSIINTn register (n is 0..15) to indicate -+ * the MSI pending status caused by 1 of its 8 index registers. -+ */ -+ -+/* MSInIRx read helper */ -+static u32 xgene_msi_ir_read(struct xgene_msi *msi, -+ u32 msi_grp, u32 msir_idx) -+{ -+ return readl_relaxed(msi->msi_regs + MSI_IR0 + -+ (msi_grp << 19) + (msir_idx << 16)); -+} -+ -+/* MSIINTn read helper */ -+static u32 xgene_msi_int_read(struct xgene_msi *msi, u32 msi_grp) -+{ -+ return readl_relaxed(msi->msi_regs + MSI_INT0 + (msi_grp << 16)); -+} -+ -+/* -+ * With 2048 MSI vectors supported, the MSI message can be constructed using -+ * following scheme: -+ * - Divide into 8 256-vector groups -+ * Group 0: 0-255 -+ * Group 1: 256-511 -+ * Group 2: 512-767 -+ * ... -+ * Group 7: 1792-2047 -+ * - Each 256-vector group is divided into 16 16-vector groups -+ * As an example: 16 16-vector groups for 256-vector group 0-255 is -+ * Group 0: 0-15 -+ * Group 1: 16-32 -+ * ... -+ * Group 15: 240-255 -+ * - The termination address of MSI vector in 256-vector group n and 16-vector -+ * group x is the address of MSIxIRn -+ * - The data for MSI vector in 16-vector group x is x -+ */ -+static u32 hwirq_to_reg_set(unsigned long hwirq) -+{ -+ return (hwirq / (NR_HW_IRQS * IRQS_PER_IDX)); -+} -+ -+static u32 hwirq_to_group(unsigned long hwirq) -+{ -+ return (hwirq % NR_HW_IRQS); -+} -+ -+static u32 hwirq_to_msi_data(unsigned long hwirq) -+{ -+ return ((hwirq / NR_HW_IRQS) % IRQS_PER_IDX); -+} -+ -+static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) -+{ -+ struct xgene_msi *msi = irq_data_get_irq_chip_data(data); -+ u32 reg_set = hwirq_to_reg_set(data->hwirq); -+ u32 group = hwirq_to_group(data->hwirq); -+ u64 target_addr = msi->msi_addr + (((8 * group) + reg_set) << 16); -+ -+ msg->address_hi = upper_32_bits(target_addr); -+ msg->address_lo = lower_32_bits(target_addr); -+ msg->data = hwirq_to_msi_data(data->hwirq); -+} -+ -+/* -+ * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors. To maintain -+ * the expected behaviour of .set_affinity for each MSI interrupt, the 16 -+ * MSI GIC IRQs are statically allocated to 8 X-Gene v1 cores (2 GIC IRQs -+ * for each core). The MSI vector is moved fom 1 MSI GIC IRQ to another -+ * MSI GIC IRQ to steer its MSI interrupt to correct X-Gene v1 core. As a -+ * consequence, the total MSI vectors that X-Gene v1 supports will be -+ * reduced to 256 (2048/8) vectors. -+ */ -+static int hwirq_to_cpu(unsigned long hwirq) -+{ -+ return (hwirq % xgene_msi_ctrl.num_cpus); -+} -+ -+static unsigned long hwirq_to_canonical_hwirq(unsigned long hwirq) -+{ -+ return (hwirq - hwirq_to_cpu(hwirq)); -+} -+ -+static int xgene_msi_set_affinity(struct irq_data *irqdata, -+ const struct cpumask *mask, bool force) -+{ -+ int target_cpu = cpumask_first(mask); -+ int curr_cpu; -+ -+ curr_cpu = hwirq_to_cpu(irqdata->hwirq); -+ if (curr_cpu == target_cpu) -+ return IRQ_SET_MASK_OK_DONE; -+ -+ /* Update MSI number to target the new CPU */ -+ irqdata->hwirq = hwirq_to_canonical_hwirq(irqdata->hwirq) + target_cpu; -+ -+ return IRQ_SET_MASK_OK; -+} -+ -+static struct irq_chip xgene_msi_bottom_irq_chip = { -+ .name = "MSI", -+ .irq_set_affinity = xgene_msi_set_affinity, -+ .irq_compose_msi_msg = xgene_compose_msi_msg, -+}; -+ -+static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, -+ unsigned int nr_irqs, void *args) -+{ -+ struct xgene_msi *msi = domain->host_data; -+ int msi_irq; -+ -+ mutex_lock(&msi->bitmap_lock); -+ -+ msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0, -+ msi->num_cpus, 0); -+ if (msi_irq < NR_MSI_VEC) -+ bitmap_set(msi->bitmap, msi_irq, msi->num_cpus); -+ else -+ msi_irq = -ENOSPC; -+ -+ mutex_unlock(&msi->bitmap_lock); -+ -+ if (msi_irq < 0) -+ return msi_irq; -+ -+ irq_domain_set_info(domain, virq, msi_irq, -+ &xgene_msi_bottom_irq_chip, domain->host_data, -+ handle_simple_irq, NULL, NULL); -+ -+ return 0; -+} -+ -+static void xgene_irq_domain_free(struct irq_domain *domain, -+ unsigned int virq, unsigned int nr_irqs) -+{ -+ struct irq_data *d = irq_domain_get_irq_data(domain, virq); -+ struct xgene_msi *msi = irq_data_get_irq_chip_data(d); -+ u32 hwirq; -+ -+ mutex_lock(&msi->bitmap_lock); -+ -+ hwirq = hwirq_to_canonical_hwirq(d->hwirq); -+ bitmap_clear(msi->bitmap, hwirq, msi->num_cpus); -+ -+ mutex_unlock(&msi->bitmap_lock); -+ -+ irq_domain_free_irqs_parent(domain, virq, nr_irqs); -+} -+ -+static const struct irq_domain_ops msi_domain_ops = { -+ .alloc = xgene_irq_domain_alloc, -+ .free = xgene_irq_domain_free, -+}; -+ -+static int xgene_allocate_domains(struct xgene_msi *msi) -+{ -+ msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC, -+ &msi_domain_ops, msi); -+ if (!msi->domain) -+ return -ENOMEM; -+ -+ msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node, -+ &xgene_msi_domain_info, -+ msi->domain); -+ -+ if (!msi->mchip.domain) { -+ irq_domain_remove(msi->domain); -+ return -ENOMEM; -+ } -+ -+ return 0; -+} -+ -+static void xgene_free_domains(struct xgene_msi *msi) -+{ -+ if (msi->mchip.domain) -+ irq_domain_remove(msi->mchip.domain); -+ if (msi->domain) -+ irq_domain_remove(msi->domain); -+} -+ -+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi) -+{ -+ int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long); -+ -+ xgene_msi->bitmap = kzalloc(size, GFP_KERNEL); -+ if (!xgene_msi->bitmap) -+ return -ENOMEM; -+ -+ mutex_init(&xgene_msi->bitmap_lock); -+ -+ xgene_msi->msi_groups = kcalloc(NR_HW_IRQS, -+ sizeof(struct xgene_msi_group), -+ GFP_KERNEL); -+ if (!xgene_msi->msi_groups) -+ return -ENOMEM; -+ -+ return 0; -+} -+ -+static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc) -+{ -+ struct irq_chip *chip = irq_desc_get_chip(desc); -+ struct xgene_msi_group *msi_groups; -+ struct xgene_msi *xgene_msi; -+ unsigned int virq; -+ int msir_index, msir_val, hw_irq; -+ u32 intr_index, grp_select, msi_grp; -+ -+ chained_irq_enter(chip, desc); -+ -+ msi_groups = irq_desc_get_handler_data(desc); -+ xgene_msi = msi_groups->msi; -+ msi_grp = msi_groups->msi_grp; -+ -+ /* -+ * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt -+ * If bit x of this register is set (x is 0..7), one or more interupts -+ * corresponding to MSInIRx is set. -+ */ -+ grp_select = xgene_msi_int_read(xgene_msi, msi_grp); -+ while (grp_select) { -+ msir_index = ffs(grp_select) - 1; -+ /* -+ * Calculate MSInIRx address to read to check for interrupts -+ * (refer to termination address and data assignment -+ * described in xgene_compose_msi_msg() ) -+ */ -+ msir_val = xgene_msi_ir_read(xgene_msi, msi_grp, msir_index); -+ while (msir_val) { -+ intr_index = ffs(msir_val) - 1; -+ /* -+ * Calculate MSI vector number (refer to the termination -+ * address and data assignment described in -+ * xgene_compose_msi_msg function) -+ */ -+ hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) * -+ NR_HW_IRQS) + msi_grp; -+ /* -+ * As we have multiple hw_irq that maps to single MSI, -+ * always look up the virq using the hw_irq as seen from -+ * CPU0 -+ */ -+ hw_irq = hwirq_to_canonical_hwirq(hw_irq); -+ virq = irq_find_mapping(xgene_msi->domain, hw_irq); -+ WARN_ON(!virq); -+ if (virq != 0) -+ generic_handle_irq(virq); -+ msir_val &= ~(1 << intr_index); -+ } -+ grp_select &= ~(1 << msir_index); -+ -+ if (!grp_select) { -+ /* -+ * We handled all interrupts happened in this group, -+ * resample this group MSI_INTx register in case -+ * something else has been made pending in the meantime -+ */ -+ grp_select = xgene_msi_int_read(xgene_msi, msi_grp); -+ } -+ } -+ -+ chained_irq_exit(chip, desc); -+} -+ -+static int xgene_msi_remove(struct platform_device *pdev) -+{ -+ int virq, i; -+ struct xgene_msi *msi = platform_get_drvdata(pdev); -+ -+ for (i = 0; i < NR_HW_IRQS; i++) { -+ virq = msi->msi_groups[i].gic_irq; -+ if (virq != 0) { -+ irq_set_chained_handler(virq, NULL); -+ irq_set_handler_data(virq, NULL); -+ } -+ } -+ kfree(msi->msi_groups); -+ -+ kfree(msi->bitmap); -+ msi->bitmap = NULL; -+ -+ xgene_free_domains(msi); -+ -+ return 0; -+} -+ -+static int xgene_msi_hwirq_alloc(unsigned int cpu) -+{ -+ struct xgene_msi *msi = &xgene_msi_ctrl; -+ struct xgene_msi_group *msi_group; -+ cpumask_var_t mask; -+ int i; -+ int err; -+ -+ for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) { -+ msi_group = &msi->msi_groups[i]; -+ if (!msi_group->gic_irq) -+ continue; -+ -+ irq_set_chained_handler(msi_group->gic_irq, -+ xgene_msi_isr); -+ err = irq_set_handler_data(msi_group->gic_irq, msi_group); -+ if (err) { -+ pr_err("failed to register GIC IRQ handler\n"); -+ return -EINVAL; -+ } -+ /* -+ * Statically allocate MSI GIC IRQs to each CPU core. -+ * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated -+ * to each core. -+ */ -+ if (alloc_cpumask_var(&mask, GFP_KERNEL)) { -+ cpumask_clear(mask); -+ cpumask_set_cpu(cpu, mask); -+ err = irq_set_affinity(msi_group->gic_irq, mask); -+ if (err) -+ pr_err("failed to set affinity for GIC IRQ"); -+ free_cpumask_var(mask); -+ } else { -+ pr_err("failed to alloc CPU mask for affinity\n"); -+ err = -EINVAL; -+ } -+ -+ if (err) { -+ irq_set_chained_handler(msi_group->gic_irq, NULL); -+ irq_set_handler_data(msi_group->gic_irq, NULL); -+ return err; -+ } -+ } -+ -+ return 0; -+} -+ -+static void xgene_msi_hwirq_free(unsigned int cpu) -+{ -+ struct xgene_msi *msi = &xgene_msi_ctrl; -+ struct xgene_msi_group *msi_group; -+ int i; -+ -+ for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) { -+ msi_group = &msi->msi_groups[i]; -+ if (!msi_group->gic_irq) -+ continue; -+ -+ irq_set_chained_handler(msi_group->gic_irq, NULL); -+ irq_set_handler_data(msi_group->gic_irq, NULL); -+ } -+} -+ -+static int xgene_msi_cpu_callback(struct notifier_block *nfb, -+ unsigned long action, void *hcpu) -+{ -+ unsigned cpu = (unsigned long)hcpu; -+ -+ switch (action) { -+ case CPU_ONLINE: -+ case CPU_ONLINE_FROZEN: -+ xgene_msi_hwirq_alloc(cpu); -+ break; -+ case CPU_DEAD: -+ case CPU_DEAD_FROZEN: -+ xgene_msi_hwirq_free(cpu); -+ break; -+ default: -+ break; -+ } -+ -+ return NOTIFY_OK; -+} -+ -+static struct notifier_block xgene_msi_cpu_notifier = { -+ .notifier_call = xgene_msi_cpu_callback, -+}; -+ -+static const struct of_device_id xgene_msi_match_table[] = { -+ {.compatible = "apm,xgene1-msi"}, -+ {}, -+}; -+ -+static int xgene_msi_probe(struct platform_device *pdev) -+{ -+ struct resource *res; -+ int rc, irq_index; -+ struct xgene_msi *xgene_msi; -+ unsigned int cpu; -+ int virt_msir; -+ u32 msi_val, msi_idx; -+ -+ xgene_msi = &xgene_msi_ctrl; -+ -+ platform_set_drvdata(pdev, xgene_msi); -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res); -+ if (IS_ERR(xgene_msi->msi_regs)) { -+ dev_err(&pdev->dev, "no reg space\n"); -+ rc = -EINVAL; -+ goto error; -+ } -+ xgene_msi->msi_addr = res->start; -+ -+ xgene_msi->num_cpus = num_possible_cpus(); -+ -+ rc = xgene_msi_init_allocator(xgene_msi); -+ if (rc) { -+ dev_err(&pdev->dev, "Error allocating MSI bitmap\n"); -+ goto error; -+ } -+ -+ rc = xgene_allocate_domains(xgene_msi); -+ if (rc) { -+ dev_err(&pdev->dev, "Failed to allocate MSI domain\n"); -+ goto error; -+ } -+ -+ for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) { -+ virt_msir = platform_get_irq(pdev, irq_index); -+ if (virt_msir < 0) { -+ dev_err(&pdev->dev, "Cannot translate IRQ index %d\n", -+ irq_index); -+ rc = -EINVAL; -+ goto error; -+ } -+ xgene_msi->msi_groups[irq_index].gic_irq = virt_msir; -+ xgene_msi->msi_groups[irq_index].msi_grp = irq_index; -+ xgene_msi->msi_groups[irq_index].msi = xgene_msi; -+ } -+ -+ /* -+ * MSInIRx registers are read-to-clear; before registering -+ * interrupt handlers, read all of them to clear spurious -+ * interrupts that may occur before the driver is probed. -+ */ -+ for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) { -+ for (msi_idx = 0; msi_idx < IDX_PER_GROUP; msi_idx++) -+ msi_val = xgene_msi_ir_read(xgene_msi, irq_index, -+ msi_idx); -+ /* Read MSIINTn to confirm */ -+ msi_val = xgene_msi_int_read(xgene_msi, irq_index); -+ if (msi_val) { -+ dev_err(&pdev->dev, "Failed to clear spurious IRQ\n"); -+ rc = -EINVAL; -+ goto error; -+ } -+ } -+ -+ cpu_notifier_register_begin(); -+ -+ for_each_online_cpu(cpu) -+ if (xgene_msi_hwirq_alloc(cpu)) { -+ dev_err(&pdev->dev, "failed to register MSI handlers\n"); -+ cpu_notifier_register_done(); -+ goto error; -+ } -+ -+ rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier); -+ if (rc) { -+ dev_err(&pdev->dev, "failed to add CPU MSI notifier\n"); -+ cpu_notifier_register_done(); -+ goto error; -+ } -+ -+ cpu_notifier_register_done(); -+ -+ xgene_msi->mchip.of_node = pdev->dev.of_node; -+ rc = of_pci_msi_chip_add(&xgene_msi->mchip); -+ if (rc) { -+ dev_err(&pdev->dev, "failed to add MSI controller chip\n"); -+ goto error_notifier; -+ } -+ -+ dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n"); -+ -+ return 0; -+ -+error_notifier: -+ unregister_hotcpu_notifier(&xgene_msi_cpu_notifier); -+error: -+ xgene_msi_remove(pdev); -+ return rc; -+} -+ -+static struct platform_driver xgene_msi_driver = { -+ .driver = { -+ .name = "xgene-msi", -+ .owner = THIS_MODULE, -+ .of_match_table = xgene_msi_match_table, -+ }, -+ .probe = xgene_msi_probe, -+ .remove = xgene_msi_remove, -+}; -+ -+static int __init xgene_pcie_msi_init(void) -+{ -+ return platform_driver_register(&xgene_msi_driver); -+} -+subsys_initcall(xgene_pcie_msi_init); -diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c -index 2988fe1..0dac1fb 100644 ---- a/drivers/pci/host/pci-xgene.c -+++ b/drivers/pci/host/pci-xgene.c -@@ -401,11 +401,11 @@ static int xgene_pcie_map_ranges(struct xgene_pcie_port *port, - struct list_head *res, - resource_size_t io_base) - { -- struct pci_host_bridge_window *window; -+ struct resource_entry *window; - struct device *dev = port->dev; - int ret; - -- list_for_each_entry(window, res, list) { -+ resource_list_for_each_entry(window, res) { - struct resource *res = window->res; - u64 restype = resource_type(res); - -@@ -600,6 +600,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port, - return 0; - } - -+static int xgene_pcie_msi_enable(struct pci_bus *bus) -+{ -+ struct device_node *msi_node; -+ -+ msi_node = of_parse_phandle(bus->dev.of_node, -+ "msi-parent", 0); -+ if (!msi_node) -+ return -ENODEV; -+ -+ bus->msi = of_pci_find_msi_chip_by_node(msi_node); -+ if (!bus->msi) -+ return -ENODEV; -+ -+ bus->msi->dev = &bus->dev; -+ return 0; -+} -+ - static int xgene_pcie_probe_bridge(struct platform_device *pdev) - { - struct device_node *dn = pdev->dev.of_node; -@@ -636,6 +653,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev) - if (!bus) - return -ENOMEM; - -+ if (IS_ENABLED(CONFIG_PCI_MSI)) -+ if (xgene_pcie_msi_enable(bus)) -+ dev_info(port->dev, "failed to enable MSI\n"); -+ - pci_scan_child_bus(bus); - pci_assign_unassigned_bus_resources(bus); - pci_bus_add_devices(bus); -diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c -index f69b0d0..0961ffc 100644 ---- a/drivers/pci/host/pcie-designware.c -+++ b/drivers/pci/host/pcie-designware.c -@@ -15,7 +15,6 @@ - #include - #include - #include --#include - #include - #include - #include -@@ -31,6 +30,7 @@ - #define PORT_LINK_MODE_1_LANES (0x1 << 16) - #define PORT_LINK_MODE_2_LANES (0x3 << 16) - #define PORT_LINK_MODE_4_LANES (0x7 << 16) -+#define PORT_LINK_MODE_8_LANES (0xf << 16) - - #define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C - #define PORT_LOGIC_SPEED_CHANGE (0x1 << 17) -@@ -38,12 +38,7 @@ - #define PORT_LOGIC_LINK_WIDTH_1_LANES (0x1 << 8) - #define PORT_LOGIC_LINK_WIDTH_2_LANES (0x2 << 8) - #define PORT_LOGIC_LINK_WIDTH_4_LANES (0x4 << 8) -- --#define PCIE_MSI_ADDR_LO 0x820 --#define PCIE_MSI_ADDR_HI 0x824 --#define PCIE_MSI_INTR0_ENABLE 0x828 --#define PCIE_MSI_INTR0_MASK 0x82C --#define PCIE_MSI_INTR0_STATUS 0x830 -+#define PORT_LOGIC_LINK_WIDTH_8_LANES (0x8 << 8) - - #define PCIE_ATU_VIEWPORT 0x900 - #define PCIE_ATU_REGION_INBOUND (0x1 << 31) -@@ -67,39 +62,40 @@ - #define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16) - #define PCIE_ATU_UPPER_TARGET 0x91C - --static struct hw_pci dw_pci; -- --static unsigned long global_io_offset; -- --static inline struct pcie_port *sys_to_pcie(struct pci_sys_data *sys) --{ -- BUG_ON(!sys->private_data); -- -- return sys->private_data; --} -+static struct pci_ops dw_pcie_ops; - --int dw_pcie_cfg_read(void __iomem *addr, int where, int size, u32 *val) -+int dw_pcie_cfg_read(void __iomem *addr, int size, u32 *val) - { -- *val = readl(addr); -+ if ((uintptr_t)addr & (size - 1)) { -+ *val = 0; -+ return PCIBIOS_BAD_REGISTER_NUMBER; -+ } - -- if (size == 1) -- *val = (*val >> (8 * (where & 3))) & 0xff; -+ if (size == 4) -+ *val = readl(addr); - else if (size == 2) -- *val = (*val >> (8 * (where & 3))) & 0xffff; -- else if (size != 4) -+ *val = readw(addr); -+ else if (size == 1) -+ *val = readb(addr); -+ else { -+ *val = 0; - return PCIBIOS_BAD_REGISTER_NUMBER; -+ } - - return PCIBIOS_SUCCESSFUL; - } - --int dw_pcie_cfg_write(void __iomem *addr, int where, int size, u32 val) -+int dw_pcie_cfg_write(void __iomem *addr, int size, u32 val) - { -+ if ((uintptr_t)addr & (size - 1)) -+ return PCIBIOS_BAD_REGISTER_NUMBER; -+ - if (size == 4) - writel(val, addr); - else if (size == 2) -- writew(val, addr + (where & 2)); -+ writew(val, addr); - else if (size == 1) -- writeb(val, addr + (where & 3)); -+ writeb(val, addr); - else - return PCIBIOS_BAD_REGISTER_NUMBER; - -@@ -130,8 +126,7 @@ static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, - if (pp->ops->rd_own_conf) - ret = pp->ops->rd_own_conf(pp, where, size, val); - else -- ret = dw_pcie_cfg_read(pp->dbi_base + (where & ~0x3), where, -- size, val); -+ ret = dw_pcie_cfg_read(pp->dbi_base + where, size, val); - - return ret; - } -@@ -144,182 +139,33 @@ static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, - if (pp->ops->wr_own_conf) - ret = pp->ops->wr_own_conf(pp, where, size, val); - else -- ret = dw_pcie_cfg_write(pp->dbi_base + (where & ~0x3), where, -- size, val); -- -- return ret; --} -- --static struct irq_chip dw_msi_irq_chip = { -- .name = "PCI-MSI", -- .irq_enable = unmask_msi_irq, -- .irq_disable = mask_msi_irq, -- .irq_mask = mask_msi_irq, -- .irq_unmask = unmask_msi_irq, --}; -- --/* MSI int handler */ --irqreturn_t dw_handle_msi_irq(struct pcie_port *pp) --{ -- unsigned long val; -- int i, pos, irq; -- irqreturn_t ret = IRQ_NONE; -- -- for (i = 0; i < MAX_MSI_CTRLS; i++) { -- dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4, -- (u32 *)&val); -- if (val) { -- ret = IRQ_HANDLED; -- pos = 0; -- while ((pos = find_next_bit(&val, 32, pos)) != 32) { -- irq = irq_find_mapping(pp->irq_domain, -- i * 32 + pos); -- dw_pcie_wr_own_conf(pp, -- PCIE_MSI_INTR0_STATUS + i * 12, -- 4, 1 << pos); -- generic_handle_irq(irq); -- pos++; -- } -- } -- } -+ ret = dw_pcie_cfg_write(pp->dbi_base + where, size, val); - - return ret; - } - --void dw_pcie_msi_init(struct pcie_port *pp) --{ -- pp->msi_data = __get_free_pages(GFP_KERNEL, 0); -- -- /* program the msi_data */ -- dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_LO, 4, -- virt_to_phys((void *)pp->msi_data)); -- dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4, 0); --} -- --static void dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq) --{ -- unsigned int res, bit, val; -- -- res = (irq / 32) * 12; -- bit = irq % 32; -- dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); -- val &= ~(1 << bit); -- dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); --} -- --static void clear_irq_range(struct pcie_port *pp, unsigned int irq_base, -- unsigned int nvec, unsigned int pos) --{ -- unsigned int i; -- -- for (i = 0; i < nvec; i++) { -- irq_set_msi_desc_off(irq_base, i, NULL); -- /* Disable corresponding interrupt on MSI controller */ -- if (pp->ops->msi_clear_irq) -- pp->ops->msi_clear_irq(pp, pos + i); -- else -- dw_pcie_msi_clear_irq(pp, pos + i); -- } -- -- bitmap_release_region(pp->msi_irq_in_use, pos, order_base_2(nvec)); --} -- --static void dw_pcie_msi_set_irq(struct pcie_port *pp, int irq) --{ -- unsigned int res, bit, val; -- -- res = (irq / 32) * 12; -- bit = irq % 32; -- dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); -- val |= 1 << bit; -- dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); --} -- --static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) -+static void dw_pcie_prog_outbound_atu(struct pcie_port *pp, int index, -+ int type, u64 cpu_addr, u64 pci_addr, u32 size) - { -- int irq, pos0, i; -- struct pcie_port *pp = sys_to_pcie(desc->dev->bus->sysdata); -- -- pos0 = bitmap_find_free_region(pp->msi_irq_in_use, MAX_MSI_IRQS, -- order_base_2(no_irqs)); -- if (pos0 < 0) -- goto no_valid_irq; -- -- irq = irq_find_mapping(pp->irq_domain, pos0); -- if (!irq) -- goto no_valid_irq; -- -- /* -- * irq_create_mapping (called from dw_pcie_host_init) pre-allocates -- * descs so there is no need to allocate descs here. We can therefore -- * assume that if irq_find_mapping above returns non-zero, then the -- * descs are also successfully allocated. -- */ -- -- for (i = 0; i < no_irqs; i++) { -- if (irq_set_msi_desc_off(irq, i, desc) != 0) { -- clear_irq_range(pp, irq, i, pos0); -- goto no_valid_irq; -- } -- /*Enable corresponding interrupt in MSI interrupt controller */ -- if (pp->ops->msi_set_irq) -- pp->ops->msi_set_irq(pp, pos0 + i); -- else -- dw_pcie_msi_set_irq(pp, pos0 + i); -- } -- -- *pos = pos0; -- return irq; -- --no_valid_irq: -- *pos = pos0; -- return -ENOSPC; --} -- --static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, -- struct msi_desc *desc) --{ -- int irq, pos; -- struct msi_msg msg; -- struct pcie_port *pp = sys_to_pcie(pdev->bus->sysdata); -- -- if (desc->msi_attrib.is_msix) -- return -EINVAL; -- -- irq = assign_irq(1, desc, &pos); -- if (irq < 0) -- return irq; -- -- if (pp->ops->get_msi_addr) -- msg.address_lo = pp->ops->get_msi_addr(pp); -- else -- msg.address_lo = virt_to_phys((void *)pp->msi_data); -- msg.address_hi = 0x0; -- -- if (pp->ops->get_msi_data) -- msg.data = pp->ops->get_msi_data(pp, pos); -- else -- msg.data = pos; -- -- write_msi_msg(irq, &msg); -- -- return 0; -+ dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | index, -+ PCIE_ATU_VIEWPORT); -+ dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr), PCIE_ATU_LOWER_BASE); -+ dw_pcie_writel_rc(pp, upper_32_bits(cpu_addr), PCIE_ATU_UPPER_BASE); -+ dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr + size - 1), -+ PCIE_ATU_LIMIT); -+ dw_pcie_writel_rc(pp, lower_32_bits(pci_addr), PCIE_ATU_LOWER_TARGET); -+ dw_pcie_writel_rc(pp, upper_32_bits(pci_addr), PCIE_ATU_UPPER_TARGET); -+ dw_pcie_writel_rc(pp, type, PCIE_ATU_CR1); -+ dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2); - } - --static void dw_msi_teardown_irq(struct msi_chip *chip, unsigned int irq) -+void dw_pcie_disable_outbound_atu(struct pcie_port *pp, int index) - { -- struct irq_data *data = irq_get_irq_data(irq); -- struct msi_desc *msi = irq_data_get_msi(data); -- struct pcie_port *pp = sys_to_pcie(msi->dev->bus->sysdata); -- -- clear_irq_range(pp, irq, 1, data->hwirq); -+ dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | index, -+ PCIE_ATU_VIEWPORT); -+ dw_pcie_writel_rc(pp, 0, PCIE_ATU_CR2); - } - --static struct msi_chip dw_pcie_msi_chip = { -- .setup_irq = dw_msi_setup_irq, -- .teardown_irq = dw_msi_teardown_irq, --}; -- - int dw_pcie_link_up(struct pcie_port *pp) - { - if (pp->ops->link_up) -@@ -328,36 +174,42 @@ int dw_pcie_link_up(struct pcie_port *pp) - return 0; - } - --static int dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq, -- irq_hw_number_t hwirq) -+static int dw_pcie_msi_ctrl_init(struct pcie_port *pp) - { -- irq_set_chip_and_handler(irq, &dw_msi_irq_chip, handle_simple_irq); -- irq_set_chip_data(irq, domain->host_data); -- set_irq_flags(irq, IRQF_VALID); -+ struct device_node *msi_node; -+ -+ if (!IS_ENABLED(CONFIG_PCI_MSI)) { -+ pp->msi = NULL; -+ return 0; -+ } -+ -+ if (pp->msi) -+ return 0; -+ -+ msi_node = of_parse_phandle(pp->dev->of_node, "msi-parent", 0); -+ if (msi_node) { -+ pp->msi = of_pci_find_msi_chip_by_node(msi_node); -+ if (!pp->msi) { -+ dev_err(pp->dev, "Cannot find msi chip of %s\n", -+ msi_node->full_name); -+ return -ENODEV; -+ } else -+ return 0; -+ } - - return 0; - } - --static const struct irq_domain_ops msi_domain_ops = { -- .map = dw_pcie_msi_map, --}; -- - int dw_pcie_host_init(struct pcie_port *pp) - { - struct device_node *np = pp->dev->of_node; - struct platform_device *pdev = to_platform_device(pp->dev); -- struct of_pci_range range; -- struct of_pci_range_parser parser; -+ struct pci_bus *bus, *child; - struct resource *cfg_res; -- u32 val, na, ns; -- const __be32 *addrp; -- int i, index, ret; -- -- /* Find the address cell size and the number of cells in order to get -- * the untranslated address. -- */ -- of_property_read_u32(np, "#address-cells", &na); -- ns = of_n_size_cells(np); -+ u32 val; -+ int ret; -+ LIST_HEAD(res); -+ struct resource_entry *win; - - cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config"); - if (cfg_res) { -@@ -365,87 +217,61 @@ int dw_pcie_host_init(struct pcie_port *pp) - pp->cfg1_size = resource_size(cfg_res)/2; - pp->cfg0_base = cfg_res->start; - pp->cfg1_base = cfg_res->start + pp->cfg0_size; -- -- /* Find the untranslated configuration space address */ -- index = of_property_match_string(np, "reg-names", "config"); -- addrp = of_get_address(np, index, NULL, NULL); -- pp->cfg0_mod_base = of_read_number(addrp, ns); -- pp->cfg1_mod_base = pp->cfg0_mod_base + pp->cfg0_size; -- } else { -+ } else if (!pp->va_cfg0_base) { - dev_err(pp->dev, "missing *config* reg space\n"); - } - -- if (of_pci_range_parser_init(&parser, np)) { -- dev_err(pp->dev, "missing ranges property\n"); -- return -EINVAL; -- } -+ ret = of_pci_get_host_bridge_resources(np, 0, 0xff, &res, &pp->io_base); -+ if (ret) -+ return ret; - - /* Get the I/O and memory ranges from DT */ -- for_each_of_pci_range(&parser, &range) { -- unsigned long restype = range.flags & IORESOURCE_TYPE_BITS; -- if (restype == IORESOURCE_IO) { -- of_pci_range_to_resource(&range, np, &pp->io); -- pp->io.name = "I/O"; -- pp->io.start = max_t(resource_size_t, -- PCIBIOS_MIN_IO, -- range.pci_addr + global_io_offset); -- pp->io.end = min_t(resource_size_t, -- IO_SPACE_LIMIT, -- range.pci_addr + range.size -- + global_io_offset - 1); -- pp->io_size = resource_size(&pp->io); -- pp->io_bus_addr = range.pci_addr; -- pp->io_base = range.cpu_addr; -- -- /* Find the untranslated IO space address */ -- pp->io_mod_base = of_read_number(parser.range - -- parser.np + na, ns); -- } -- if (restype == IORESOURCE_MEM) { -- of_pci_range_to_resource(&range, np, &pp->mem); -- pp->mem.name = "MEM"; -- pp->mem_size = resource_size(&pp->mem); -- pp->mem_bus_addr = range.pci_addr; -- -- /* Find the untranslated MEM space address */ -- pp->mem_mod_base = of_read_number(parser.range - -- parser.np + na, ns); -- } -- if (restype == 0) { -- of_pci_range_to_resource(&range, np, &pp->cfg); -- pp->cfg0_size = resource_size(&pp->cfg)/2; -- pp->cfg1_size = resource_size(&pp->cfg)/2; -- pp->cfg0_base = pp->cfg.start; -- pp->cfg1_base = pp->cfg.start + pp->cfg0_size; -- -- /* Find the untranslated configuration space address */ -- pp->cfg0_mod_base = of_read_number(parser.range - -- parser.np + na, ns); -- pp->cfg1_mod_base = pp->cfg0_mod_base + -- pp->cfg0_size; -+ resource_list_for_each_entry(win, &res) { -+ switch (resource_type(win->res)) { -+ case IORESOURCE_IO: -+ pp->io = win->res; -+ pp->io->name = "I/O"; -+ pp->io_size = resource_size(pp->io); -+ pp->io_bus_addr = pp->io->start - win->offset; -+ ret = pci_remap_iospace(pp->io, pp->io_base); -+ if (ret) { -+ dev_warn(pp->dev, "error %d: failed to map resource %pR\n", -+ ret, pp->io); -+ continue; -+ } -+ pp->io_base = pp->io->start; -+ break; -+ case IORESOURCE_MEM: -+ pp->mem = win->res; -+ pp->mem->name = "MEM"; -+ pp->mem_size = resource_size(pp->mem); -+ pp->mem_bus_addr = pp->mem->start - win->offset; -+ break; -+ case 0: -+ pp->cfg = win->res; -+ pp->cfg0_size = resource_size(pp->cfg)/2; -+ pp->cfg1_size = resource_size(pp->cfg)/2; -+ pp->cfg0_base = pp->cfg->start; -+ pp->cfg1_base = pp->cfg->start + pp->cfg0_size; -+ break; -+ case IORESOURCE_BUS: -+ pp->busn = win->res; -+ break; -+ default: -+ continue; - } - } - -- ret = of_pci_parse_bus_range(np, &pp->busn); -- if (ret < 0) { -- pp->busn.name = np->name; -- pp->busn.start = 0; -- pp->busn.end = 0xff; -- pp->busn.flags = IORESOURCE_BUS; -- dev_dbg(pp->dev, "failed to parse bus-range property: %d, using default %pR\n", -- ret, &pp->busn); -- } -- - if (!pp->dbi_base) { -- pp->dbi_base = devm_ioremap(pp->dev, pp->cfg.start, -- resource_size(&pp->cfg)); -+ pp->dbi_base = devm_ioremap(pp->dev, pp->cfg->start, -+ resource_size(pp->cfg)); - if (!pp->dbi_base) { - dev_err(pp->dev, "error with ioremap\n"); - return -ENOMEM; - } - } - -- pp->mem_base = pp->mem.start; -+ pp->mem_base = pp->mem->start; - - if (!pp->va_cfg0_base) { - pp->va_cfg0_base = devm_ioremap(pp->dev, pp->cfg0_base, -@@ -465,33 +291,18 @@ int dw_pcie_host_init(struct pcie_port *pp) - } - } - -- if (of_property_read_u32(np, "num-lanes", &pp->lanes)) { -- dev_err(pp->dev, "Failed to parse the number of lanes\n"); -- return -EINVAL; -- } -- -- if (IS_ENABLED(CONFIG_PCI_MSI)) { -- if (!pp->ops->msi_host_init) { -- pp->irq_domain = irq_domain_add_linear(pp->dev->of_node, -- MAX_MSI_IRQS, &msi_domain_ops, -- &dw_pcie_msi_chip); -- if (!pp->irq_domain) { -- dev_err(pp->dev, "irq domain init failed\n"); -- return -ENXIO; -- } -- -- for (i = 0; i < MAX_MSI_IRQS; i++) -- irq_create_mapping(pp->irq_domain, i); -- } else { -- ret = pp->ops->msi_host_init(pp, &dw_pcie_msi_chip); -- if (ret < 0) -- return ret; -- } -- } -+ ret = of_property_read_u32(np, "num-lanes", &pp->lanes); -+ if (ret) -+ pp->lanes = 0; - - if (pp->ops->host_init) - pp->ops->host_init(pp); - -+ if (!pp->ops->rd_other_conf) -+ dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1, -+ PCIE_ATU_TYPE_MEM, pp->mem_base, -+ pp->mem_bus_addr, pp->mem_size); -+ - dw_pcie_wr_own_conf(pp, PCI_BASE_ADDRESS_0, 4, 0); - - /* program correct class for RC */ -@@ -501,126 +312,113 @@ int dw_pcie_host_init(struct pcie_port *pp) - val |= PORT_LOGIC_SPEED_CHANGE; - dw_pcie_wr_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, val); - -- dw_pci.nr_controllers = 1; -- dw_pci.private_data = (void **)&pp; -+ pp->root_bus_nr = pp->busn->start; -+#if 0 -+ bus = pci_scan_root_bus(pp->dev, pp->root_bus_nr, &dw_pcie_ops, -+ pp, &res); -+ if (!bus) -+ return -ENOMEM; -+#else -+ bus = pci_create_root_bus(pp->dev, pp->root_bus_nr, &dw_pcie_ops, -+ pp, &res); -+ if (!bus) -+ return -ENODEV; -+ -+ ret = dw_pcie_msi_ctrl_init(pp); -+ if (ret) -+ return ret; -+ -+ bus->msi = pp->msi; - -- pci_common_init_dev(pp->dev, &dw_pci); --#ifdef CONFIG_PCI_DOMAINS -- dw_pci.domain++; -+ pci_scan_child_bus(bus); - #endif - -- return 0; --} -+ if (pp->ops->scan_bus) -+ pp->ops->scan_bus(pp); - --static void dw_pcie_prog_viewport_cfg0(struct pcie_port *pp, u32 busdev) --{ -- /* Program viewport 0 : OUTBOUND : CFG0 */ -- dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX0, -- PCIE_ATU_VIEWPORT); -- dw_pcie_writel_rc(pp, pp->cfg0_mod_base, PCIE_ATU_LOWER_BASE); -- dw_pcie_writel_rc(pp, (pp->cfg0_mod_base >> 32), PCIE_ATU_UPPER_BASE); -- dw_pcie_writel_rc(pp, pp->cfg0_mod_base + pp->cfg0_size - 1, -- PCIE_ATU_LIMIT); -- dw_pcie_writel_rc(pp, busdev, PCIE_ATU_LOWER_TARGET); -- dw_pcie_writel_rc(pp, 0, PCIE_ATU_UPPER_TARGET); -- dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_CFG0, PCIE_ATU_CR1); -- dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2); --} -+#ifdef CONFIG_ARM -+ /* support old dtbs that incorrectly describe IRQs */ -+ pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci); -+#endif - --static void dw_pcie_prog_viewport_cfg1(struct pcie_port *pp, u32 busdev) --{ -- /* Program viewport 1 : OUTBOUND : CFG1 */ -- dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX1, -- PCIE_ATU_VIEWPORT); -- dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_CFG1, PCIE_ATU_CR1); -- dw_pcie_writel_rc(pp, pp->cfg1_mod_base, PCIE_ATU_LOWER_BASE); -- dw_pcie_writel_rc(pp, (pp->cfg1_mod_base >> 32), PCIE_ATU_UPPER_BASE); -- dw_pcie_writel_rc(pp, pp->cfg1_mod_base + pp->cfg1_size - 1, -- PCIE_ATU_LIMIT); -- dw_pcie_writel_rc(pp, busdev, PCIE_ATU_LOWER_TARGET); -- dw_pcie_writel_rc(pp, 0, PCIE_ATU_UPPER_TARGET); -- dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2); --} -+ if (!pci_has_flag(PCI_PROBE_ONLY)) { -+ pci_bus_size_bridges(bus); -+ pci_bus_assign_resources(bus); - --static void dw_pcie_prog_viewport_mem_outbound(struct pcie_port *pp) --{ -- /* Program viewport 0 : OUTBOUND : MEM */ -- dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX0, -- PCIE_ATU_VIEWPORT); -- dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_MEM, PCIE_ATU_CR1); -- dw_pcie_writel_rc(pp, pp->mem_mod_base, PCIE_ATU_LOWER_BASE); -- dw_pcie_writel_rc(pp, (pp->mem_mod_base >> 32), PCIE_ATU_UPPER_BASE); -- dw_pcie_writel_rc(pp, pp->mem_mod_base + pp->mem_size - 1, -- PCIE_ATU_LIMIT); -- dw_pcie_writel_rc(pp, pp->mem_bus_addr, PCIE_ATU_LOWER_TARGET); -- dw_pcie_writel_rc(pp, upper_32_bits(pp->mem_bus_addr), -- PCIE_ATU_UPPER_TARGET); -- dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2); --} -+ list_for_each_entry(child, &bus->children, node) -+ pcie_bus_configure_settings(child); -+ } - --static void dw_pcie_prog_viewport_io_outbound(struct pcie_port *pp) --{ -- /* Program viewport 1 : OUTBOUND : IO */ -- dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX1, -- PCIE_ATU_VIEWPORT); -- dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_IO, PCIE_ATU_CR1); -- dw_pcie_writel_rc(pp, pp->io_mod_base, PCIE_ATU_LOWER_BASE); -- dw_pcie_writel_rc(pp, (pp->io_mod_base >> 32), PCIE_ATU_UPPER_BASE); -- dw_pcie_writel_rc(pp, pp->io_mod_base + pp->io_size - 1, -- PCIE_ATU_LIMIT); -- dw_pcie_writel_rc(pp, pp->io_bus_addr, PCIE_ATU_LOWER_TARGET); -- dw_pcie_writel_rc(pp, upper_32_bits(pp->io_bus_addr), -- PCIE_ATU_UPPER_TARGET); -- dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2); -+ pci_bus_add_devices(bus); -+ -+ return 0; - } - - static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, - u32 devfn, int where, int size, u32 *val) - { -- int ret = PCIBIOS_SUCCESSFUL; -- u32 address, busdev; -+ int ret, type; -+ u32 busdev, cfg_size; -+ u64 cpu_addr; -+ void __iomem *va_cfg_base; - - busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) | - PCIE_ATU_FUNC(PCI_FUNC(devfn)); -- address = where & ~0x3; - - if (bus->parent->number == pp->root_bus_nr) { -- dw_pcie_prog_viewport_cfg0(pp, busdev); -- ret = dw_pcie_cfg_read(pp->va_cfg0_base + address, where, size, -- val); -- dw_pcie_prog_viewport_mem_outbound(pp); -+ type = PCIE_ATU_TYPE_CFG0; -+ cpu_addr = pp->cfg0_base; -+ cfg_size = pp->cfg0_size; -+ va_cfg_base = pp->va_cfg0_base; - } else { -- dw_pcie_prog_viewport_cfg1(pp, busdev); -- ret = dw_pcie_cfg_read(pp->va_cfg1_base + address, where, size, -- val); -- dw_pcie_prog_viewport_io_outbound(pp); -+ type = PCIE_ATU_TYPE_CFG1; -+ cpu_addr = pp->cfg1_base; -+ cfg_size = pp->cfg1_size; -+ va_cfg_base = pp->va_cfg1_base; - } - -+ dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0, -+ type, cpu_addr, -+ busdev, cfg_size); -+ ret = dw_pcie_cfg_read(va_cfg_base + where, size, val); -+ dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0, -+ PCIE_ATU_TYPE_IO, pp->io_base, -+ pp->io_bus_addr, pp->io_size); -+ - return ret; - } - - static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, - u32 devfn, int where, int size, u32 val) - { -- int ret = PCIBIOS_SUCCESSFUL; -- u32 address, busdev; -+ int ret, type; -+ u32 busdev, cfg_size; -+ u64 cpu_addr; -+ void __iomem *va_cfg_base; - - busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) | - PCIE_ATU_FUNC(PCI_FUNC(devfn)); -- address = where & ~0x3; - - if (bus->parent->number == pp->root_bus_nr) { -- dw_pcie_prog_viewport_cfg0(pp, busdev); -- ret = dw_pcie_cfg_write(pp->va_cfg0_base + address, where, size, -- val); -- dw_pcie_prog_viewport_mem_outbound(pp); -+ type = PCIE_ATU_TYPE_CFG0; -+ cpu_addr = pp->cfg0_base; -+ cfg_size = pp->cfg0_size; -+ va_cfg_base = pp->va_cfg0_base; - } else { -- dw_pcie_prog_viewport_cfg1(pp, busdev); -- ret = dw_pcie_cfg_write(pp->va_cfg1_base + address, where, size, -- val); -- dw_pcie_prog_viewport_io_outbound(pp); -+ type = PCIE_ATU_TYPE_CFG1; -+ cpu_addr = pp->cfg1_base; -+ cfg_size = pp->cfg1_size; -+ va_cfg_base = pp->va_cfg1_base; - } - -+ dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0, -+ type, cpu_addr, -+ busdev, cfg_size); -+ ret = dw_pcie_cfg_write(va_cfg_base + where, size, val); -+ dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0, -+ PCIE_ATU_TYPE_IO, pp->io_base, -+ pp->io_bus_addr, pp->io_size); -+ - return ret; - } - -@@ -650,7 +448,7 @@ static int dw_pcie_valid_config(struct pcie_port *pp, - static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, - int size, u32 *val) - { -- struct pcie_port *pp = sys_to_pcie(bus->sysdata); -+ struct pcie_port *pp = bus->sysdata; - int ret; - - if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) { -@@ -674,7 +472,7 @@ static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, - static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn, - int where, int size, u32 val) - { -- struct pcie_port *pp = sys_to_pcie(bus->sysdata); -+ struct pcie_port *pp = bus->sysdata; - int ret; - - if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) -@@ -698,81 +496,19 @@ static struct pci_ops dw_pcie_ops = { - .write = dw_pcie_wr_conf, - }; - --static int dw_pcie_setup(int nr, struct pci_sys_data *sys) --{ -- struct pcie_port *pp; -- -- pp = sys_to_pcie(sys); -- -- if (global_io_offset < SZ_1M && pp->io_size > 0) { -- sys->io_offset = global_io_offset - pp->io_bus_addr; -- pci_ioremap_io(global_io_offset, pp->io_base); -- global_io_offset += SZ_64K; -- pci_add_resource_offset(&sys->resources, &pp->io, -- sys->io_offset); -- } -- -- sys->mem_offset = pp->mem.start - pp->mem_bus_addr; -- pci_add_resource_offset(&sys->resources, &pp->mem, sys->mem_offset); -- pci_add_resource(&sys->resources, &pp->busn); -- -- return 1; --} -- --static struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys) --{ -- struct pci_bus *bus; -- struct pcie_port *pp = sys_to_pcie(sys); -- -- pp->root_bus_nr = sys->busnr; -- bus = pci_create_root_bus(pp->dev, sys->busnr, -- &dw_pcie_ops, sys, &sys->resources); -- if (!bus) -- return NULL; -- -- pci_scan_child_bus(bus); -- -- if (bus && pp->ops->scan_bus) -- pp->ops->scan_bus(pp); -- -- return bus; --} -- --static int dw_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) --{ -- struct pcie_port *pp = sys_to_pcie(dev->bus->sysdata); -- int irq; -- -- irq = of_irq_parse_and_map_pci(dev, slot, pin); -- if (!irq) -- irq = pp->irq; -- -- return irq; --} -- --static void dw_pcie_add_bus(struct pci_bus *bus) --{ -- if (IS_ENABLED(CONFIG_PCI_MSI)) { -- struct pcie_port *pp = sys_to_pcie(bus->sysdata); -- -- dw_pcie_msi_chip.dev = pp->dev; -- bus->msi = &dw_pcie_msi_chip; -- } --} -- --static struct hw_pci dw_pci = { -- .setup = dw_pcie_setup, -- .scan = dw_pcie_scan_bus, -- .map_irq = dw_pcie_map_irq, -- .add_bus = dw_pcie_add_bus, --}; -- - void dw_pcie_setup_rc(struct pcie_port *pp) - { - u32 val; - u32 membase; - u32 memlimit; - -+ dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0, -+ PCIE_ATU_TYPE_IO, pp->io_base, -+ pp->io_bus_addr, pp->io_size); -+ dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1, -+ PCIE_ATU_TYPE_MEM, pp->mem_base, -+ pp->mem_bus_addr, pp->mem_size); -+ - /* set the number of lanes */ - dw_pcie_readl_rc(pp, PCIE_PORT_LINK_CONTROL, &val); - val &= ~PORT_LINK_MODE_MASK; -@@ -786,6 +522,12 @@ void dw_pcie_setup_rc(struct pcie_port *pp) - case 4: - val |= PORT_LINK_MODE_4_LANES; - break; -+ case 8: -+ val |= PORT_LINK_MODE_8_LANES; -+ break; -+ default: -+ dev_err(pp->dev, "num-lanes %u: invalid value\n", pp->lanes); -+ return; - } - dw_pcie_writel_rc(pp, val, PCIE_PORT_LINK_CONTROL); - -@@ -802,6 +544,9 @@ void dw_pcie_setup_rc(struct pcie_port *pp) - case 4: - val |= PORT_LOGIC_LINK_WIDTH_4_LANES; - break; -+ case 8: -+ val |= PORT_LOGIC_LINK_WIDTH_8_LANES; -+ break; - } - dw_pcie_writel_rc(pp, val, PCIE_LINK_WIDTH_SPEED_CONTROL); - -diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h -index c625675..fcd6431 100644 ---- a/drivers/pci/host/pcie-designware.h -+++ b/drivers/pci/host/pcie-designware.h -@@ -27,28 +27,25 @@ struct pcie_port { - u8 root_bus_nr; - void __iomem *dbi_base; - u64 cfg0_base; -- u64 cfg0_mod_base; - void __iomem *va_cfg0_base; - u32 cfg0_size; - u64 cfg1_base; -- u64 cfg1_mod_base; - void __iomem *va_cfg1_base; - u32 cfg1_size; -- u64 io_base; -- u64 io_mod_base; -+ resource_size_t io_base; - phys_addr_t io_bus_addr; - u32 io_size; - u64 mem_base; -- u64 mem_mod_base; - phys_addr_t mem_bus_addr; - u32 mem_size; -- struct resource cfg; -- struct resource io; -- struct resource mem; -- struct resource busn; -+ struct resource *cfg; -+ struct resource *io; -+ struct resource *mem; -+ struct resource *busn; - int irq; - u32 lanes; - struct pcie_host_ops *ops; -+ struct msi_controller *msi; - int msi_irq; - struct irq_domain *irq_domain; - unsigned long msi_data; -@@ -70,18 +67,19 @@ struct pcie_host_ops { - void (*host_init)(struct pcie_port *pp); - void (*msi_set_irq)(struct pcie_port *pp, int irq); - void (*msi_clear_irq)(struct pcie_port *pp, int irq); -- u32 (*get_msi_addr)(struct pcie_port *pp); -+ phys_addr_t (*get_msi_addr)(struct pcie_port *pp); - u32 (*get_msi_data)(struct pcie_port *pp, int pos); - void (*scan_bus)(struct pcie_port *pp); -- int (*msi_host_init)(struct pcie_port *pp, struct msi_chip *chip); -+ int (*msi_host_init)(struct pcie_port *pp, struct msi_controller *chip); - }; - --int dw_pcie_cfg_read(void __iomem *addr, int where, int size, u32 *val); --int dw_pcie_cfg_write(void __iomem *addr, int where, int size, u32 val); -+int dw_pcie_cfg_read(void __iomem *addr, int size, u32 *val); -+int dw_pcie_cfg_write(void __iomem *addr, int size, u32 val); - irqreturn_t dw_handle_msi_irq(struct pcie_port *pp); - void dw_pcie_msi_init(struct pcie_port *pp); - int dw_pcie_link_up(struct pcie_port *pp); - void dw_pcie_setup_rc(struct pcie_port *pp); - int dw_pcie_host_init(struct pcie_port *pp); -+void dw_pcie_disable_outbound_atu(struct pcie_port *pp, int index); - - #endif /* _PCIE_DESIGNWARE_H */ -diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c -index 61158e0..f8ec96d 100644 ---- a/drivers/pci/host/pcie-rcar.c -+++ b/drivers/pci/host/pcie-rcar.c -@@ -111,14 +111,14 @@ - struct rcar_msi { - DECLARE_BITMAP(used, INT_PCI_MSI_NR); - struct irq_domain *domain; -- struct msi_chip chip; -+ struct msi_controller chip; - unsigned long pages; - struct mutex lock; - int irq1; - int irq2; - }; - --static inline struct rcar_msi *to_rcar_msi(struct msi_chip *chip) -+static inline struct rcar_msi *to_rcar_msi(struct msi_controller *chip) - { - return container_of(chip, struct rcar_msi, chip); - } -@@ -404,9 +404,6 @@ static void rcar_pcie_enable(struct rcar_pcie *pcie) - rcar_pci.private_data = (void **)&pcie; - - pci_common_init_dev(&pdev->dev, &rcar_pci); --#ifdef CONFIG_PCI_DOMAINS -- rcar_pci.domain++; --#endif - } - - static int phy_wait_for_ack(struct rcar_pcie *pcie) -@@ -622,7 +619,7 @@ static irqreturn_t rcar_pcie_msi_irq(int irq, void *data) - return IRQ_HANDLED; - } - --static int rcar_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, -+static int rcar_msi_setup_irq(struct msi_controller *chip, struct pci_dev *pdev, - struct msi_desc *desc) - { - struct rcar_msi *msi = to_rcar_msi(chip); -@@ -647,12 +644,12 @@ static int rcar_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, - msg.address_hi = rcar_pci_read_reg(pcie, PCIEMSIAUR); - msg.data = hwirq; - -- write_msi_msg(irq, &msg); -+ pci_write_msi_msg(irq, &msg); - - return 0; - } - --static void rcar_msi_teardown_irq(struct msi_chip *chip, unsigned int irq) -+static void rcar_msi_teardown_irq(struct msi_controller *chip, unsigned int irq) - { - struct rcar_msi *msi = to_rcar_msi(chip); - struct irq_data *d = irq_get_irq_data(irq); -@@ -662,10 +659,10 @@ static void rcar_msi_teardown_irq(struct msi_chip *chip, unsigned int irq) - - static struct irq_chip rcar_msi_irq_chip = { - .name = "R-Car PCIe MSI", -- .irq_enable = unmask_msi_irq, -- .irq_disable = mask_msi_irq, -- .irq_mask = mask_msi_irq, -- .irq_unmask = unmask_msi_irq, -+ .irq_enable = pci_msi_unmask_irq, -+ .irq_disable = pci_msi_mask_irq, -+ .irq_mask = pci_msi_mask_irq, -+ .irq_unmask = pci_msi_unmask_irq, - }; - - static int rcar_msi_map(struct irq_domain *domain, unsigned int irq, -@@ -673,7 +670,6 @@ static int rcar_msi_map(struct irq_domain *domain, unsigned int irq, - { - irq_set_chip_and_handler(irq, &rcar_msi_irq_chip, handle_simple_irq); - irq_set_chip_data(irq, domain->host_data); -- set_irq_flags(irq, IRQF_VALID); - - return 0; - } -diff --git a/drivers/pci/host/pcie-xilinx.c b/drivers/pci/host/pcie-xilinx.c -index ccc496b..eef849c 100644 ---- a/drivers/pci/host/pcie-xilinx.c -+++ b/drivers/pci/host/pcie-xilinx.c -@@ -297,18 +297,16 @@ static struct pci_ops xilinx_pcie_ops = { - */ - static void xilinx_pcie_destroy_msi(unsigned int irq) - { -- struct irq_desc *desc; - struct msi_desc *msi; - struct xilinx_pcie_port *port; - -- desc = irq_to_desc(irq); -- msi = irq_desc_get_msi_desc(desc); -- port = sys_to_pcie(msi->dev->bus->sysdata); -- -- if (!test_bit(irq, msi_irq_in_use)) -+ if (!test_bit(irq, msi_irq_in_use)) { -+ msi = irq_get_msi_desc(irq); -+ port = sys_to_pcie(msi_desc_to_pci_sys_data(msi)); - dev_err(port->dev, "Trying to free unused MSI#%d\n", irq); -- else -+ } else { - clear_bit(irq, msi_irq_in_use); -+ } - } - - /** -@@ -335,7 +333,8 @@ static int xilinx_pcie_assign_msi(struct xilinx_pcie_port *port) - * @chip: MSI Chip descriptor - * @irq: MSI IRQ to destroy - */ --static void xilinx_msi_teardown_irq(struct msi_chip *chip, unsigned int irq) -+static void xilinx_msi_teardown_irq(struct msi_controller *chip, -+ unsigned int irq) - { - xilinx_pcie_destroy_msi(irq); - } -@@ -348,7 +347,7 @@ static void xilinx_msi_teardown_irq(struct msi_chip *chip, unsigned int irq) - * - * Return: '0' on success and error value on failure - */ --static int xilinx_pcie_msi_setup_irq(struct msi_chip *chip, -+static int xilinx_pcie_msi_setup_irq(struct msi_controller *chip, - struct pci_dev *pdev, - struct msi_desc *desc) - { -@@ -374,13 +373,13 @@ static int xilinx_pcie_msi_setup_irq(struct msi_chip *chip, - msg.address_lo = msg_addr; - msg.data = irq; - -- write_msi_msg(irq, &msg); -+ pci_write_msi_msg(irq, &msg); - - return 0; - } - - /* MSI Chip Descriptor */ --static struct msi_chip xilinx_pcie_msi_chip = { -+static struct msi_controller xilinx_pcie_msi_chip = { - .setup_irq = xilinx_pcie_msi_setup_irq, - .teardown_irq = xilinx_msi_teardown_irq, - }; -@@ -388,10 +387,10 @@ static struct msi_chip xilinx_pcie_msi_chip = { - /* HW Interrupt Chip Descriptor */ - static struct irq_chip xilinx_msi_irq_chip = { - .name = "Xilinx PCIe MSI", -- .irq_enable = unmask_msi_irq, -- .irq_disable = mask_msi_irq, -- .irq_mask = mask_msi_irq, -- .irq_unmask = unmask_msi_irq, -+ .irq_enable = pci_msi_unmask_irq, -+ .irq_disable = pci_msi_mask_irq, -+ .irq_mask = pci_msi_mask_irq, -+ .irq_unmask = pci_msi_unmask_irq, - }; - - /** -@@ -407,7 +406,6 @@ static int xilinx_pcie_msi_map(struct irq_domain *domain, unsigned int irq, - { - irq_set_chip_and_handler(irq, &xilinx_msi_irq_chip, handle_simple_irq); - irq_set_chip_data(irq, domain->host_data); -- set_irq_flags(irq, IRQF_VALID); - - return 0; - } -@@ -431,20 +429,6 @@ static void xilinx_pcie_enable_msi(struct xilinx_pcie_port *port) - pcie_write(port, msg_addr, XILINX_PCIE_REG_MSIBASE2); - } - --/** -- * xilinx_pcie_add_bus - Add MSI chip info to PCIe bus -- * @bus: PCIe bus -- */ --static void xilinx_pcie_add_bus(struct pci_bus *bus) --{ -- if (IS_ENABLED(CONFIG_PCI_MSI)) { -- struct xilinx_pcie_port *port = sys_to_pcie(bus->sysdata); -- -- xilinx_pcie_msi_chip.dev = port->dev; -- bus->msi = &xilinx_pcie_msi_chip; -- } --} -- - /* INTx Functions */ - - /** -@@ -460,7 +444,6 @@ static int xilinx_pcie_intx_map(struct irq_domain *domain, unsigned int irq, - { - irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq); - irq_set_chip_data(irq, domain->host_data); -- set_irq_flags(irq, IRQF_VALID); - - return 0; - } -@@ -730,9 +713,15 @@ static struct pci_bus *xilinx_pcie_scan_bus(int nr, struct pci_sys_data *sys) - struct pci_bus *bus; - - port->root_busno = sys->busnr; -- bus = pci_scan_root_bus(port->dev, sys->busnr, &xilinx_pcie_ops, -- sys, &sys->resources); - -+ if (IS_ENABLED(CONFIG_PCI_MSI)) -+ bus = pci_scan_root_bus_msi(port->dev, sys->busnr, -+ &xilinx_pcie_ops, sys, -+ &sys->resources, -+ &xilinx_pcie_msi_chip); -+ else -+ bus = pci_scan_root_bus(port->dev, sys->busnr, -+ &xilinx_pcie_ops, sys, &sys->resources); - return bus; - } - -@@ -750,7 +739,7 @@ static int xilinx_pcie_parse_and_add_res(struct xilinx_pcie_port *port) - resource_size_t offset; - struct of_pci_range_parser parser; - struct of_pci_range range; -- struct pci_host_bridge_window *win; -+ struct resource_entry *win; - int err = 0, mem_resno = 0; - - /* Get the ranges */ -@@ -820,7 +809,7 @@ static int xilinx_pcie_parse_and_add_res(struct xilinx_pcie_port *port) - - free_resources: - release_child_resources(&iomem_resource); -- list_for_each_entry(win, &port->resources, list) -+ resource_list_for_each_entry(win, &port->resources) - devm_kfree(dev, win->res); - pci_free_resource_list(&port->resources); - -@@ -924,10 +913,13 @@ static int xilinx_pcie_probe(struct platform_device *pdev) - .private_data = (void **)&port, - .setup = xilinx_pcie_setup, - .map_irq = of_irq_parse_and_map_pci, -- .add_bus = xilinx_pcie_add_bus, - .scan = xilinx_pcie_scan_bus, - .ops = &xilinx_pcie_ops, - }; -+ -+#ifdef CONFIG_PCI_MSI -+ xilinx_pcie_msi_chip.dev = port->dev; -+#endif - pci_common_init_dev(dev, &hw); - - return 0; -diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c -index 084587d..5e64d37 100644 ---- a/drivers/pci/msi.c -+++ b/drivers/pci/msi.c -@@ -19,19 +19,81 @@ - #include - #include - #include -+#include - - #include "pci.h" - - static int pci_msi_enable = 1; -+int pci_msi_ignore_mask; - - #define msix_table_size(flags) ((flags & PCI_MSIX_FLAGS_QSIZE) + 1) - -+#ifdef CONFIG_PCI_MSI_IRQ_DOMAIN -+static struct irq_domain *pci_msi_default_domain; -+static DEFINE_MUTEX(pci_msi_domain_lock); -+ -+struct irq_domain * __weak arch_get_pci_msi_domain(struct pci_dev *dev) -+{ -+ return pci_msi_default_domain; -+} -+ -+static struct irq_domain *pci_msi_get_domain(struct pci_dev *dev) -+{ -+ struct irq_domain *domain; -+ -+ domain = dev_get_msi_domain(&dev->dev); -+ if (domain) -+ return domain; -+ -+ return arch_get_pci_msi_domain(dev); -+} -+ -+static int pci_msi_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) -+{ -+ struct irq_domain *domain; -+ -+ domain = pci_msi_get_domain(dev); -+ if (domain) -+ return pci_msi_domain_alloc_irqs(domain, dev, nvec, type); -+ -+ return arch_setup_msi_irqs(dev, nvec, type); -+} -+ -+static void pci_msi_teardown_msi_irqs(struct pci_dev *dev) -+{ -+ struct irq_domain *domain; -+ -+ domain = pci_msi_get_domain(dev); -+ if (domain) -+ pci_msi_domain_free_irqs(domain, dev); -+ else -+ arch_teardown_msi_irqs(dev); -+} -+#else -+#define pci_msi_setup_msi_irqs arch_setup_msi_irqs -+#define pci_msi_teardown_msi_irqs arch_teardown_msi_irqs -+#endif - - /* Arch hooks */ - -+struct msi_controller * __weak pcibios_msi_controller(struct pci_dev *dev) -+{ -+ return NULL; -+} -+ -+static struct msi_controller *pci_msi_controller(struct pci_dev *dev) -+{ -+ struct msi_controller *msi_ctrl = dev->bus->msi; -+ -+ if (msi_ctrl) -+ return msi_ctrl; -+ -+ return pcibios_msi_controller(dev); -+} -+ - int __weak arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc) - { -- struct msi_chip *chip = dev->bus->msi; -+ struct msi_controller *chip = pci_msi_controller(dev); - int err; - - if (!chip || !chip->setup_irq) -@@ -48,7 +110,7 @@ int __weak arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc) - - void __weak arch_teardown_msi_irq(unsigned int irq) - { -- struct msi_chip *chip = irq_get_chip_data(irq); -+ struct msi_controller *chip = irq_get_chip_data(irq); - - if (!chip || !chip->teardown_irq) - return; -@@ -58,9 +120,12 @@ void __weak arch_teardown_msi_irq(unsigned int irq) - - int __weak arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) - { -+ struct msi_controller *chip = dev->bus->msi; - struct msi_desc *entry; - int ret; - -+ if (chip && chip->setup_irqs) -+ return chip->setup_irqs(chip, dev, nvec, type); - /* - * If an architecture wants to support multiple MSI, it needs to - * override arch_setup_msi_irqs() -@@ -68,7 +133,7 @@ int __weak arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) - if (type == PCI_CAP_ID_MSI && nvec > 1) - return 1; - -- list_for_each_entry(entry, &dev->msi_list, list) { -+ for_each_pci_msi_entry(entry, dev) { - ret = arch_setup_msi_irq(dev, entry); - if (ret < 0) - return ret; -@@ -85,19 +150,13 @@ int __weak arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) - */ - void default_teardown_msi_irqs(struct pci_dev *dev) - { -+ int i; - struct msi_desc *entry; - -- list_for_each_entry(entry, &dev->msi_list, list) { -- int i, nvec; -- if (entry->irq == 0) -- continue; -- if (entry->nvec_used) -- nvec = entry->nvec_used; -- else -- nvec = 1 << entry->msi_attrib.multiple; -- for (i = 0; i < nvec; i++) -- arch_teardown_msi_irq(entry->irq + i); -- } -+ for_each_pci_msi_entry(entry, dev) -+ if (entry->irq) -+ for (i = 0; i < entry->nvec_used; i++) -+ arch_teardown_msi_irq(entry->irq + i); - } - - void __weak arch_teardown_msi_irqs(struct pci_dev *dev) -@@ -111,7 +170,7 @@ static void default_restore_msi_irq(struct pci_dev *dev, int irq) - - entry = NULL; - if (dev->msix_enabled) { -- list_for_each_entry(entry, &dev->msi_list, list) { -+ for_each_pci_msi_entry(entry, dev) { - if (irq == entry->irq) - break; - } -@@ -120,7 +179,7 @@ static void default_restore_msi_irq(struct pci_dev *dev, int irq) - } - - if (entry) -- __write_msi_msg(entry, &entry->msg); -+ __pci_write_msi_msg(entry, &entry->msg); - } - - void __weak arch_restore_msi_irqs(struct pci_dev *dev) -@@ -128,27 +187,6 @@ void __weak arch_restore_msi_irqs(struct pci_dev *dev) - return default_restore_msi_irqs(dev); - } - --static void msi_set_enable(struct pci_dev *dev, int enable) --{ -- u16 control; -- -- pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control); -- control &= ~PCI_MSI_FLAGS_ENABLE; -- if (enable) -- control |= PCI_MSI_FLAGS_ENABLE; -- pci_write_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, control); --} -- --static void msix_clear_and_set_ctrl(struct pci_dev *dev, u16 clear, u16 set) --{ -- u16 ctrl; -- -- pci_read_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, &ctrl); -- ctrl &= ~clear; -- ctrl |= set; -- pci_write_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, ctrl); --} -- - static inline __attribute_const__ u32 msi_mask(unsigned x) - { - /* Don't shift by >= width of type */ -@@ -163,28 +201,24 @@ static inline __attribute_const__ u32 msi_mask(unsigned x) - * reliably as devices without an INTx disable bit will then generate a - * level IRQ which will never be cleared. - */ --u32 default_msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) -+u32 __pci_msi_desc_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) - { - u32 mask_bits = desc->masked; - -- if (!desc->msi_attrib.maskbit) -+ if (pci_msi_ignore_mask || !desc->msi_attrib.maskbit) - return 0; - - mask_bits &= ~mask; - mask_bits |= flag; -- pci_write_config_dword(desc->dev, desc->mask_pos, mask_bits); -+ pci_write_config_dword(msi_desc_to_pci_dev(desc), desc->mask_pos, -+ mask_bits); - - return mask_bits; - } - --__weak u32 arch_msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) --{ -- return default_msi_mask_irq(desc, mask, flag); --} -- - static void msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) - { -- desc->masked = arch_msi_mask_irq(desc, mask, flag); -+ desc->masked = __pci_msi_desc_mask_irq(desc, mask, flag); - } - - /* -@@ -194,11 +228,15 @@ static void msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) - * file. This saves a few milliseconds when initialising devices with lots - * of MSI-X interrupts. - */ --u32 default_msix_mask_irq(struct msi_desc *desc, u32 flag) -+u32 __pci_msix_desc_mask_irq(struct msi_desc *desc, u32 flag) - { - u32 mask_bits = desc->masked; - unsigned offset = desc->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + - PCI_MSIX_ENTRY_VECTOR_CTRL; -+ -+ if (pci_msi_ignore_mask) -+ return 0; -+ - mask_bits &= ~PCI_MSIX_ENTRY_CTRL_MASKBIT; - if (flag) - mask_bits |= PCI_MSIX_ENTRY_CTRL_MASKBIT; -@@ -207,19 +245,14 @@ u32 default_msix_mask_irq(struct msi_desc *desc, u32 flag) - return mask_bits; - } - --__weak u32 arch_msix_mask_irq(struct msi_desc *desc, u32 flag) --{ -- return default_msix_mask_irq(desc, flag); --} -- - static void msix_mask_irq(struct msi_desc *desc, u32 flag) - { -- desc->masked = arch_msix_mask_irq(desc, flag); -+ desc->masked = __pci_msix_desc_mask_irq(desc, flag); - } - - static void msi_set_mask_bit(struct irq_data *data, u32 flag) - { -- struct msi_desc *desc = irq_data_get_msi(data); -+ struct msi_desc *desc = irq_data_get_msi_desc(data); - - if (desc->msi_attrib.is_msix) { - msix_mask_irq(desc, flag); -@@ -230,12 +263,20 @@ static void msi_set_mask_bit(struct irq_data *data, u32 flag) - } - } - --void mask_msi_irq(struct irq_data *data) -+/** -+ * pci_msi_mask_irq - Generic irq chip callback to mask PCI/MSI interrupts -+ * @data: pointer to irqdata associated to that interrupt -+ */ -+void pci_msi_mask_irq(struct irq_data *data) - { - msi_set_mask_bit(data, 1); - } - --void unmask_msi_irq(struct irq_data *data) -+/** -+ * pci_msi_unmask_irq - Generic irq chip callback to unmask PCI/MSI interrupts -+ * @data: pointer to irqdata associated to that interrupt -+ */ -+void pci_msi_unmask_irq(struct irq_data *data) - { - msi_set_mask_bit(data, 0); - } -@@ -244,14 +285,15 @@ void default_restore_msi_irqs(struct pci_dev *dev) - { - struct msi_desc *entry; - -- list_for_each_entry(entry, &dev->msi_list, list) { -+ for_each_pci_msi_entry(entry, dev) - default_restore_msi_irq(dev, entry->irq); -- } - } - --void __read_msi_msg(struct msi_desc *entry, struct msi_msg *msg) -+void __pci_read_msi_msg(struct msi_desc *entry, struct msi_msg *msg) - { -- BUG_ON(entry->dev->current_state != PCI_D0); -+ struct pci_dev *dev = msi_desc_to_pci_dev(entry); -+ -+ BUG_ON(dev->current_state != PCI_D0); - - if (entry->msi_attrib.is_msix) { - void __iomem *base = entry->mask_base + -@@ -261,7 +303,6 @@ void __read_msi_msg(struct msi_desc *entry, struct msi_msg *msg) - msg->address_hi = readl(base + PCI_MSIX_ENTRY_UPPER_ADDR); - msg->data = readl(base + PCI_MSIX_ENTRY_DATA); - } else { -- struct pci_dev *dev = entry->dev; - int pos = dev->msi_cap; - u16 data; - -@@ -279,34 +320,11 @@ void __read_msi_msg(struct msi_desc *entry, struct msi_msg *msg) - } - } - --void read_msi_msg(unsigned int irq, struct msi_msg *msg) --{ -- struct msi_desc *entry = irq_get_msi_desc(irq); -- -- __read_msi_msg(entry, msg); --} -- --void __get_cached_msi_msg(struct msi_desc *entry, struct msi_msg *msg) --{ -- /* Assert that the cache is valid, assuming that -- * valid messages are not all-zeroes. */ -- BUG_ON(!(entry->msg.address_hi | entry->msg.address_lo | -- entry->msg.data)); -- -- *msg = entry->msg; --} -- --void get_cached_msi_msg(unsigned int irq, struct msi_msg *msg) -+void __pci_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) - { -- struct msi_desc *entry = irq_get_msi_desc(irq); -- -- __get_cached_msi_msg(entry, msg); --} --EXPORT_SYMBOL_GPL(get_cached_msi_msg); -+ struct pci_dev *dev = msi_desc_to_pci_dev(entry); - --void __write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) --{ -- if (entry->dev->current_state != PCI_D0) { -+ if (dev->current_state != PCI_D0) { - /* Don't touch the hardware now */ - } else if (entry->msi_attrib.is_msix) { - void __iomem *base; -@@ -317,7 +335,6 @@ void __write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) - writel(msg->address_hi, base + PCI_MSIX_ENTRY_UPPER_ADDR); - writel(msg->data, base + PCI_MSIX_ENTRY_DATA); - } else { -- struct pci_dev *dev = entry->dev; - int pos = dev->msi_cap; - u16 msgctl; - -@@ -341,38 +358,32 @@ void __write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) - entry->msg = *msg; - } - --void write_msi_msg(unsigned int irq, struct msi_msg *msg) -+void pci_write_msi_msg(unsigned int irq, struct msi_msg *msg) - { - struct msi_desc *entry = irq_get_msi_desc(irq); - -- __write_msi_msg(entry, msg); -+ __pci_write_msi_msg(entry, msg); - } --EXPORT_SYMBOL_GPL(write_msi_msg); -+EXPORT_SYMBOL_GPL(pci_write_msi_msg); - - static void free_msi_irqs(struct pci_dev *dev) - { -+ struct list_head *msi_list = dev_to_msi_list(&dev->dev); - struct msi_desc *entry, *tmp; - struct attribute **msi_attrs; - struct device_attribute *dev_attr; -- int count = 0; -+ int i, count = 0; - -- list_for_each_entry(entry, &dev->msi_list, list) { -- int i, nvec; -- if (!entry->irq) -- continue; -- if (entry->nvec_used) -- nvec = entry->nvec_used; -- else -- nvec = 1 << entry->msi_attrib.multiple; -- for (i = 0; i < nvec; i++) -- BUG_ON(irq_has_action(entry->irq + i)); -- } -+ for_each_pci_msi_entry(entry, dev) -+ if (entry->irq) -+ for (i = 0; i < entry->nvec_used; i++) -+ BUG_ON(irq_has_action(entry->irq + i)); - -- arch_teardown_msi_irqs(dev); -+ pci_msi_teardown_msi_irqs(dev); - -- list_for_each_entry_safe(entry, tmp, &dev->msi_list, list) { -+ list_for_each_entry_safe(entry, tmp, msi_list, list) { - if (entry->msi_attrib.is_msix) { -- if (list_is_last(&entry->list, &dev->msi_list)) -+ if (list_is_last(&entry->list, msi_list)) - iounmap(entry->mask_base); - } - -@@ -397,18 +408,6 @@ static void free_msi_irqs(struct pci_dev *dev) - } - } - --static struct msi_desc *alloc_msi_entry(struct pci_dev *dev) --{ -- struct msi_desc *desc = kzalloc(sizeof(*desc), GFP_KERNEL); -- if (!desc) -- return NULL; -- -- INIT_LIST_HEAD(&desc->list); -- desc->dev = dev; -- -- return desc; --} -- - static void pci_intx_for_msi(struct pci_dev *dev, int enable) - { - if (!(dev->dev_flags & PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG)) -@@ -426,7 +425,7 @@ static void __pci_restore_msi_state(struct pci_dev *dev) - entry = irq_get_msi_desc(dev->irq); - - pci_intx_for_msi(dev, 0); -- msi_set_enable(dev, 0); -+ pci_msi_set_enable(dev, 0); - arch_restore_msi_irqs(dev); - - pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control); -@@ -443,19 +442,18 @@ static void __pci_restore_msix_state(struct pci_dev *dev) - - if (!dev->msix_enabled) - return; -- BUG_ON(list_empty(&dev->msi_list)); -+ BUG_ON(list_empty(dev_to_msi_list(&dev->dev))); - - /* route the table */ - pci_intx_for_msi(dev, 0); -- msix_clear_and_set_ctrl(dev, 0, -+ pci_msix_clear_and_set_ctrl(dev, 0, - PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL); - - arch_restore_msi_irqs(dev); -- list_for_each_entry(entry, &dev->msi_list, list) { -+ for_each_pci_msi_entry(entry, dev) - msix_mask_irq(entry, entry->masked); -- } - -- msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL, 0); -+ pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL, 0); - } - - void pci_restore_msi_state(struct pci_dev *dev) -@@ -497,9 +495,8 @@ static int populate_msi_sysfs(struct pci_dev *pdev) - int count = 0; - - /* Determine how many msi entries we have */ -- list_for_each_entry(entry, &pdev->msi_list, list) { -+ for_each_pci_msi_entry(entry, pdev) - ++num_msi; -- } - if (!num_msi) - return 0; - -@@ -507,7 +504,7 @@ static int populate_msi_sysfs(struct pci_dev *pdev) - msi_attrs = kzalloc(sizeof(void *) * (num_msi + 1), GFP_KERNEL); - if (!msi_attrs) - return -ENOMEM; -- list_for_each_entry(entry, &pdev->msi_list, list) { -+ for_each_pci_msi_entry(entry, pdev) { - msi_dev_attr = kzalloc(sizeof(*msi_dev_attr), GFP_KERNEL); - if (!msi_dev_attr) - goto error_attrs; -@@ -559,13 +556,13 @@ error_attrs: - return ret; - } - --static struct msi_desc *msi_setup_entry(struct pci_dev *dev) -+static struct msi_desc *msi_setup_entry(struct pci_dev *dev, int nvec) - { - u16 control; - struct msi_desc *entry; - - /* MSI Entry Initialization */ -- entry = alloc_msi_entry(dev); -+ entry = alloc_msi_entry(&dev->dev); - if (!entry) - return NULL; - -@@ -577,6 +574,8 @@ static struct msi_desc *msi_setup_entry(struct pci_dev *dev) - entry->msi_attrib.maskbit = !!(control & PCI_MSI_FLAGS_MASKBIT); - entry->msi_attrib.default_irq = dev->irq; /* Save IOAPIC IRQ */ - entry->msi_attrib.multi_cap = (control & PCI_MSI_FLAGS_QMASK) >> 1; -+ entry->msi_attrib.multiple = ilog2(__roundup_pow_of_two(nvec)); -+ entry->nvec_used = nvec; - - if (control & PCI_MSI_FLAGS_64BIT) - entry->mask_pos = dev->msi_cap + PCI_MSI_MASK_64; -@@ -594,7 +593,7 @@ static int msi_verify_entries(struct pci_dev *dev) - { - struct msi_desc *entry; - -- list_for_each_entry(entry, &dev->msi_list, list) { -+ for_each_pci_msi_entry(entry, dev) { - if (!dev->no_64bit_msi || !entry->msg.address_hi) - continue; - dev_err(&dev->dev, "Device has broken 64-bit MSI but arch" -@@ -621,9 +620,9 @@ static int msi_capability_init(struct pci_dev *dev, int nvec) - int ret; - unsigned mask; - -- msi_set_enable(dev, 0); /* Disable MSI during set up */ -+ pci_msi_set_enable(dev, 0); /* Disable MSI during set up */ - -- entry = msi_setup_entry(dev); -+ entry = msi_setup_entry(dev, nvec); - if (!entry) - return -ENOMEM; - -@@ -631,10 +630,10 @@ static int msi_capability_init(struct pci_dev *dev, int nvec) - mask = msi_mask(entry->msi_attrib.multi_cap); - msi_mask_irq(entry, mask, mask); - -- list_add_tail(&entry->list, &dev->msi_list); -+ list_add_tail(&entry->list, dev_to_msi_list(&dev->dev)); - - /* Configure MSI capability structure */ -- ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSI); -+ ret = pci_msi_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSI); - if (ret) { - msi_mask_irq(entry, mask, ~mask); - free_msi_irqs(dev); -@@ -657,7 +656,7 @@ static int msi_capability_init(struct pci_dev *dev, int nvec) - - /* Set MSI enabled bits */ - pci_intx_for_msi(dev, 0); -- msi_set_enable(dev, 1); -+ pci_msi_set_enable(dev, 1); - dev->msi_enabled = 1; - - dev->irq = entry->irq; -@@ -668,11 +667,16 @@ static void __iomem *msix_map_region(struct pci_dev *dev, unsigned nr_entries) - { - resource_size_t phys_addr; - u32 table_offset; -+ unsigned long flags; - u8 bir; - - pci_read_config_dword(dev, dev->msix_cap + PCI_MSIX_TABLE, - &table_offset); - bir = (u8)(table_offset & PCI_MSIX_TABLE_BIR); -+ flags = pci_resource_flags(dev, bir); -+ if (!flags || (flags & IORESOURCE_UNSET)) -+ return NULL; -+ - table_offset &= PCI_MSIX_TABLE_OFFSET; - phys_addr = pci_resource_start(dev, bir) + table_offset; - -@@ -686,7 +690,7 @@ static int msix_setup_entries(struct pci_dev *dev, void __iomem *base, - int i; - - for (i = 0; i < nvec; i++) { -- entry = alloc_msi_entry(dev); -+ entry = alloc_msi_entry(&dev->dev); - if (!entry) { - if (!i) - iounmap(base); -@@ -701,8 +705,9 @@ static int msix_setup_entries(struct pci_dev *dev, void __iomem *base, - entry->msi_attrib.entry_nr = entries[i].entry; - entry->msi_attrib.default_irq = dev->irq; - entry->mask_base = base; -+ entry->nvec_used = 1; - -- list_add_tail(&entry->list, &dev->msi_list); -+ list_add_tail(&entry->list, dev_to_msi_list(&dev->dev)); - } - - return 0; -@@ -714,12 +719,11 @@ static void msix_program_entries(struct pci_dev *dev, - struct msi_desc *entry; - int i = 0; - -- list_for_each_entry(entry, &dev->msi_list, list) { -+ for_each_pci_msi_entry(entry, dev) { - int offset = entries[i].entry * PCI_MSIX_ENTRY_SIZE + - PCI_MSIX_ENTRY_VECTOR_CTRL; - - entries[i].vector = entry->irq; -- irq_set_msi_desc(entry->irq, entry); - entry->masked = readl(entry->mask_base + offset); - msix_mask_irq(entry, 1); - i++; -@@ -744,7 +748,7 @@ static int msix_capability_init(struct pci_dev *dev, - void __iomem *base; - - /* Ensure MSI-X is disabled while it is set up */ -- msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_ENABLE, 0); -+ pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_ENABLE, 0); - - pci_read_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, &control); - /* Request & Map MSI-X table region */ -@@ -756,7 +760,7 @@ static int msix_capability_init(struct pci_dev *dev, - if (ret) - return ret; - -- ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSIX); -+ ret = pci_msi_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSIX); - if (ret) - goto out_avail; - -@@ -770,7 +774,7 @@ static int msix_capability_init(struct pci_dev *dev, - * MSI-X registers. We need to mask all the vectors to prevent - * interrupts coming in before they're fully set up. - */ -- msix_clear_and_set_ctrl(dev, 0, -+ pci_msix_clear_and_set_ctrl(dev, 0, - PCI_MSIX_FLAGS_MASKALL | PCI_MSIX_FLAGS_ENABLE); - - msix_program_entries(dev, entries); -@@ -783,7 +787,7 @@ static int msix_capability_init(struct pci_dev *dev, - pci_intx_for_msi(dev, 0); - dev->msix_enabled = 1; - -- msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL, 0); -+ pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL, 0); - - return 0; - -@@ -796,7 +800,7 @@ out_avail: - struct msi_desc *entry; - int avail = 0; - -- list_for_each_entry(entry, &dev->msi_list, list) { -+ for_each_pci_msi_entry(entry, dev) { - if (entry->irq != 0) - avail++; - } -@@ -885,17 +889,17 @@ void pci_msi_shutdown(struct pci_dev *dev) - if (!pci_msi_enable || !dev || !dev->msi_enabled) - return; - -- BUG_ON(list_empty(&dev->msi_list)); -- desc = list_first_entry(&dev->msi_list, struct msi_desc, list); -+ BUG_ON(list_empty(dev_to_msi_list(&dev->dev))); -+ desc = first_pci_msi_entry(dev); - -- msi_set_enable(dev, 0); -+ pci_msi_set_enable(dev, 0); - pci_intx_for_msi(dev, 1); - dev->msi_enabled = 0; - - /* Return the device with MSI unmasked as initial states */ - mask = msi_mask(desc->msi_attrib.multi_cap); - /* Keep cached state to be restored */ -- arch_msi_mask_irq(desc, mask, ~mask); -+ __pci_msi_desc_mask_irq(desc, mask, ~mask); - - /* Restore dev->irq to its default pin-assertion irq */ - dev->irq = desc->msi_attrib.default_irq; -@@ -991,12 +995,12 @@ void pci_msix_shutdown(struct pci_dev *dev) - return; - - /* Return the device with MSI-X masked as initial states */ -- list_for_each_entry(entry, &dev->msi_list, list) { -+ for_each_pci_msi_entry(entry, dev) { - /* Keep cached states to be restored */ -- arch_msix_mask_irq(entry, 1); -+ __pci_msix_desc_mask_irq(entry, 1); - } - -- msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_ENABLE, 0); -+ pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_ENABLE, 0); - pci_intx_for_msi(dev, 1); - dev->msix_enabled = 0; - } -@@ -1030,19 +1034,6 @@ EXPORT_SYMBOL(pci_msi_enabled); - - void pci_msi_init_pci_dev(struct pci_dev *dev) - { -- INIT_LIST_HEAD(&dev->msi_list); -- -- /* Disable the msi hardware to avoid screaming interrupts -- * during boot. This is the power on reset default so -- * usually this should be a noop. -- */ -- dev->msi_cap = pci_find_capability(dev, PCI_CAP_ID_MSI); -- if (dev->msi_cap) -- msi_set_enable(dev, 0); -- -- dev->msix_cap = pci_find_capability(dev, PCI_CAP_ID_MSIX); -- if (dev->msix_cap) -- msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_ENABLE, 0); - } - - /** -@@ -1138,3 +1129,217 @@ int pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries, - return nvec; - } - EXPORT_SYMBOL(pci_enable_msix_range); -+ -+struct pci_dev *msi_desc_to_pci_dev(struct msi_desc *desc) -+{ -+ return to_pci_dev(desc->dev); -+} -+ -+void *msi_desc_to_pci_sysdata(struct msi_desc *desc) -+{ -+ struct pci_dev *dev = msi_desc_to_pci_dev(desc); -+ -+ return dev->bus->sysdata; -+} -+EXPORT_SYMBOL_GPL(msi_desc_to_pci_sysdata); -+ -+#ifdef CONFIG_PCI_MSI_IRQ_DOMAIN -+/** -+ * pci_msi_domain_write_msg - Helper to write MSI message to PCI config space -+ * @irq_data: Pointer to interrupt data of the MSI interrupt -+ * @msg: Pointer to the message -+ */ -+void pci_msi_domain_write_msg(struct irq_data *irq_data, struct msi_msg *msg) -+{ -+ struct msi_desc *desc = irq_data->msi_desc; -+ -+ /* -+ * For MSI-X desc->irq is always equal to irq_data->irq. For -+ * MSI only the first interrupt of MULTI MSI passes the test. -+ */ -+ if (desc->irq == irq_data->irq) -+ __pci_write_msi_msg(desc, msg); -+} -+ -+/** -+ * pci_msi_domain_calc_hwirq - Generate a unique ID for an MSI source -+ * @dev: Pointer to the PCI device -+ * @desc: Pointer to the msi descriptor -+ * -+ * The ID number is only used within the irqdomain. -+ */ -+irq_hw_number_t pci_msi_domain_calc_hwirq(struct pci_dev *dev, -+ struct msi_desc *desc) -+{ -+ return (irq_hw_number_t)desc->msi_attrib.entry_nr | -+ PCI_DEVID(dev->bus->number, dev->devfn) << 11 | -+ (pci_domain_nr(dev->bus) & 0xFFFFFFFF) << 27; -+} -+ -+static inline bool pci_msi_desc_is_multi_msi(struct msi_desc *desc) -+{ -+ return !desc->msi_attrib.is_msix && desc->nvec_used > 1; -+} -+ -+/** -+ * pci_msi_domain_check_cap - Verify that @domain supports the capabilities for @dev -+ * @domain: The interrupt domain to check -+ * @info: The domain info for verification -+ * @dev: The device to check -+ * -+ * Returns: -+ * 0 if the functionality is supported -+ * 1 if Multi MSI is requested, but the domain does not support it -+ * -ENOTSUPP otherwise -+ */ -+int pci_msi_domain_check_cap(struct irq_domain *domain, -+ struct msi_domain_info *info, struct device *dev) -+{ -+ struct msi_desc *desc = first_pci_msi_entry(to_pci_dev(dev)); -+ -+ /* Special handling to support pci_enable_msi_range() */ -+ if (pci_msi_desc_is_multi_msi(desc) && -+ !(info->flags & MSI_FLAG_MULTI_PCI_MSI)) -+ return 1; -+ else if (desc->msi_attrib.is_msix && !(info->flags & MSI_FLAG_PCI_MSIX)) -+ return -ENOTSUPP; -+ -+ return 0; -+} -+ -+static int pci_msi_domain_handle_error(struct irq_domain *domain, -+ struct msi_desc *desc, int error) -+{ -+ /* Special handling to support pci_enable_msi_range() */ -+ if (pci_msi_desc_is_multi_msi(desc) && error == -ENOSPC) -+ return 1; -+ -+ return error; -+} -+ -+#ifdef GENERIC_MSI_DOMAIN_OPS -+static void pci_msi_domain_set_desc(msi_alloc_info_t *arg, -+ struct msi_desc *desc) -+{ -+ arg->desc = desc; -+ arg->hwirq = pci_msi_domain_calc_hwirq(msi_desc_to_pci_dev(desc), -+ desc); -+} -+#else -+#define pci_msi_domain_set_desc NULL -+#endif -+ -+static struct msi_domain_ops pci_msi_domain_ops_default = { -+ .set_desc = pci_msi_domain_set_desc, -+ .msi_check = pci_msi_domain_check_cap, -+ .handle_error = pci_msi_domain_handle_error, -+}; -+ -+static void pci_msi_domain_update_dom_ops(struct msi_domain_info *info) -+{ -+ struct msi_domain_ops *ops = info->ops; -+ -+ if (ops == NULL) { -+ info->ops = &pci_msi_domain_ops_default; -+ } else { -+ if (ops->set_desc == NULL) -+ ops->set_desc = pci_msi_domain_set_desc; -+ if (ops->msi_check == NULL) -+ ops->msi_check = pci_msi_domain_check_cap; -+ if (ops->handle_error == NULL) -+ ops->handle_error = pci_msi_domain_handle_error; -+ } -+} -+ -+static void pci_msi_domain_update_chip_ops(struct msi_domain_info *info) -+{ -+ struct irq_chip *chip = info->chip; -+ -+ BUG_ON(!chip); -+ if (!chip->irq_write_msi_msg) -+ chip->irq_write_msi_msg = pci_msi_domain_write_msg; -+} -+ -+/** -+ * pci_msi_create_irq_domain - Creat a MSI interrupt domain -+ * @node: Optional device-tree node of the interrupt controller -+ * @info: MSI domain info -+ * @parent: Parent irq domain -+ * -+ * Updates the domain and chip ops and creates a MSI interrupt domain. -+ * -+ * Returns: -+ * A domain pointer or NULL in case of failure. -+ */ -+struct irq_domain *pci_msi_create_irq_domain(struct device_node *node, -+ struct msi_domain_info *info, -+ struct irq_domain *parent) -+{ -+ struct irq_domain *domain; -+ -+ if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS) -+ pci_msi_domain_update_dom_ops(info); -+ if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS) -+ pci_msi_domain_update_chip_ops(info); -+ -+ domain = msi_create_irq_domain(node, info, parent); -+ if (!domain) -+ return NULL; -+ -+ domain->bus_token = DOMAIN_BUS_PCI_MSI; -+ return domain; -+} -+ -+/** -+ * pci_msi_domain_alloc_irqs - Allocate interrupts for @dev in @domain -+ * @domain: The interrupt domain to allocate from -+ * @dev: The device for which to allocate -+ * @nvec: The number of interrupts to allocate -+ * @type: Unused to allow simpler migration from the arch_XXX interfaces -+ * -+ * Returns: -+ * A virtual interrupt number or an error code in case of failure -+ */ -+int pci_msi_domain_alloc_irqs(struct irq_domain *domain, struct pci_dev *dev, -+ int nvec, int type) -+{ -+ return msi_domain_alloc_irqs(domain, &dev->dev, nvec); -+} -+ -+/** -+ * pci_msi_domain_free_irqs - Free interrupts for @dev in @domain -+ * @domain: The interrupt domain -+ * @dev: The device for which to free interrupts -+ */ -+void pci_msi_domain_free_irqs(struct irq_domain *domain, struct pci_dev *dev) -+{ -+ msi_domain_free_irqs(domain, &dev->dev); -+} -+ -+/** -+ * pci_msi_create_default_irq_domain - Create a default MSI interrupt domain -+ * @node: Optional device-tree node of the interrupt controller -+ * @info: MSI domain info -+ * @parent: Parent irq domain -+ * -+ * Returns: A domain pointer or NULL in case of failure. If successful -+ * the default PCI/MSI irqdomain pointer is updated. -+ */ -+struct irq_domain *pci_msi_create_default_irq_domain(struct device_node *node, -+ struct msi_domain_info *info, struct irq_domain *parent) -+{ -+ struct irq_domain *domain; -+ -+ mutex_lock(&pci_msi_domain_lock); -+ if (pci_msi_default_domain) { -+ pr_err("PCI: default irq domain for PCI MSI has already been created.\n"); -+ domain = NULL; -+ } else { -+ domain = pci_msi_create_irq_domain(node, info, parent); -+ pci_msi_default_domain = domain; -+ } -+ mutex_unlock(&pci_msi_domain_lock); -+ -+ return domain; -+} -+#endif /* CONFIG_PCI_MSI_IRQ_DOMAIN */ -diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c -index ce0aa47..a6783a5 100644 ---- a/drivers/pci/pci.c -+++ b/drivers/pci/pci.c -@@ -2467,6 +2467,7 @@ u8 pci_common_swizzle(struct pci_dev *dev, u8 *pinp) - *pinp = pin; - return PCI_SLOT(dev->devfn); - } -+EXPORT_SYMBOL_GPL(pci_common_swizzle); - - /** - * pci_release_region - Release a PCI bar -diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h -index b5defca..df2169e 100644 ---- a/drivers/pci/pci.h -+++ b/drivers/pci/pci.h -@@ -140,6 +140,27 @@ static inline void pci_no_msi(void) { } - static inline void pci_msi_init_pci_dev(struct pci_dev *dev) { } - #endif - -+static inline void pci_msi_set_enable(struct pci_dev *dev, int enable) -+{ -+ u16 control; -+ -+ pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control); -+ control &= ~PCI_MSI_FLAGS_ENABLE; -+ if (enable) -+ control |= PCI_MSI_FLAGS_ENABLE; -+ pci_write_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, control); -+} -+ -+static inline void pci_msix_clear_and_set_ctrl(struct pci_dev *dev, u16 clear, u16 set) -+{ -+ u16 ctrl; -+ -+ pci_read_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, &ctrl); -+ ctrl &= ~clear; -+ ctrl |= set; -+ pci_write_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, ctrl); -+} -+ - void pci_realloc_get_opt(char *); - - static inline int pci_no_d1d2(struct pci_dev *dev) -diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c -index 2f0ce66..95ef171 100644 ---- a/drivers/pci/pcie/portdrv_core.c -+++ b/drivers/pci/pcie/portdrv_core.c -@@ -15,6 +15,7 @@ - #include - #include - #include -+#include - - #include "../pci.h" - #include "portdrv.h" -@@ -199,6 +200,28 @@ static int pcie_port_enable_msix(struct pci_dev *dev, int *vectors, int mask) - static int init_service_irqs(struct pci_dev *dev, int *irqs, int mask) - { - int i, irq = -1; -+ int ret; -+ struct device_node *np = NULL; -+ -+ for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) -+ irqs[i] = 0; -+ -+ if (dev->bus->dev.of_node) -+ np = dev->bus->dev.of_node; -+ -+ /* If root port doesn't support MSI/MSI-X/INTx in RC mode, -+ * request irq for aer -+ */ -+ if (IS_ENABLED(CONFIG_OF_IRQ) && np && -+ (mask & PCIE_PORT_SERVICE_PME)) { -+ ret = of_irq_get_byname(np, "aer"); -+ if (ret > 0) { -+ irqs[PCIE_PORT_SERVICE_AER_SHIFT] = ret; -+ if (dev->irq) -+ irq = dev->irq; -+ goto no_msi; -+ } -+ } - - /* - * If MSI cannot be used for PCIe PME or hotplug, we have to use -@@ -224,11 +247,13 @@ static int init_service_irqs(struct pci_dev *dev, int *irqs, int mask) - irq = dev->irq; - - no_msi: -- for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) -- irqs[i] = irq; -+ for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) { -+ if (!irqs[i]) -+ irqs[i] = irq; -+ } - irqs[PCIE_PORT_SERVICE_VC_SHIFT] = -1; - -- if (irq < 0) -+ if (irq < 0 && irqs[PCIE_PORT_SERVICE_AER_SHIFT] < 0) - return -ENODEV; - return 0; - } -diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c -index 3010ffc..0b16384 100644 ---- a/drivers/pci/probe.c -+++ b/drivers/pci/probe.c -@@ -1097,6 +1097,22 @@ int pci_cfg_space_size(struct pci_dev *dev) - - #define LEGACY_IO_RESOURCE (IORESOURCE_IO | IORESOURCE_PCI_FIXED) - -+static void pci_msi_setup_pci_dev(struct pci_dev *dev) -+{ -+ /* -+ * Disable the MSI hardware to avoid screaming interrupts -+ * during boot. This is the power on reset default so -+ * usually this should be a noop. -+ */ -+ dev->msi_cap = pci_find_capability(dev, PCI_CAP_ID_MSI); -+ if (dev->msi_cap) -+ pci_msi_set_enable(dev, 0); -+ -+ dev->msix_cap = pci_find_capability(dev, PCI_CAP_ID_MSIX); -+ if (dev->msix_cap) -+ pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_ENABLE, 0); -+} -+ - /** - * pci_setup_device - fill in class and map information of a device - * @dev: the device structure to fill -@@ -1152,6 +1168,8 @@ int pci_setup_device(struct pci_dev *dev) - /* "Unknown power state" */ - dev->current_state = PCI_UNKNOWN; - -+ pci_msi_setup_pci_dev(dev); -+ - /* Early fixups, before probing the BARs */ - pci_fixup_device(pci_fixup_early, dev); - /* device class may be changed after fixup */ -@@ -1908,7 +1926,7 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, - int error; - struct pci_host_bridge *bridge; - struct pci_bus *b, *b2; -- struct pci_host_bridge_window *window, *n; -+ struct resource_entry *window, *n; - struct resource *res; - resource_size_t offset; - char bus_addr[64]; -@@ -1972,8 +1990,8 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, - printk(KERN_INFO "PCI host bridge to bus %s\n", dev_name(&b->dev)); - - /* Add initial resources to the bus */ -- list_for_each_entry_safe(window, n, resources, list) { -- list_move_tail(&window->list, &bridge->windows); -+ resource_list_for_each_entry_safe(window, n, resources) { -+ list_move_tail(&window->node, &bridge->windows); - res = window->res; - offset = window->offset; - if (res->flags & IORESOURCE_BUS) -@@ -2006,6 +2024,7 @@ err_out: - kfree(b); - return NULL; - } -+EXPORT_SYMBOL_GPL(pci_create_root_bus); - - int pci_bus_insert_busn_res(struct pci_bus *b, int bus, int bus_max) - { -@@ -2073,12 +2092,12 @@ void pci_bus_release_busn_res(struct pci_bus *b) - struct pci_bus *pci_scan_root_bus(struct device *parent, int bus, - struct pci_ops *ops, void *sysdata, struct list_head *resources) - { -- struct pci_host_bridge_window *window; -+ struct resource_entry *window; - bool found = false; - struct pci_bus *b; - int max; - -- list_for_each_entry(window, resources, list) -+ resource_list_for_each_entry(window, resources) - if (window->res->flags & IORESOURCE_BUS) { - found = true; - break; -diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c -index b6d646a..f3681e2 100644 ---- a/drivers/pci/quirks.c -+++ b/drivers/pci/quirks.c -@@ -3516,8 +3516,9 @@ int pci_dev_specific_reset(struct pci_dev *dev, int probe) - static void quirk_dma_func0_alias(struct pci_dev *dev) - { - if (PCI_FUNC(dev->devfn) != 0) { -- dev->dma_alias_devfn = PCI_DEVFN(PCI_SLOT(dev->devfn), 0); -- dev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVFN; -+ dev->dma_alias_devid = PCI_DEVID(dev->bus->number, -+ PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); -+ dev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVID; - } - } - -@@ -3532,8 +3533,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_RICOH, 0xe476, quirk_dma_func0_alias); - static void quirk_dma_func1_alias(struct pci_dev *dev) - { - if (PCI_FUNC(dev->devfn) != 1) { -- dev->dma_alias_devfn = PCI_DEVFN(PCI_SLOT(dev->devfn), 1); -- dev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVFN; -+ dev->dma_alias_devid = PCI_DEVID(dev->bus->number, -+ PCI_DEVFN(PCI_SLOT(dev->devfn), 1)); -+ dev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVID; - } - } - -diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c -index 8bd76c9..8a280e9 100644 ---- a/drivers/pci/remove.c -+++ b/drivers/pci/remove.c -@@ -139,6 +139,7 @@ void pci_stop_root_bus(struct pci_bus *bus) - /* stop the host bridge */ - device_release_driver(&host_bridge->dev); - } -+EXPORT_SYMBOL_GPL(pci_stop_root_bus); - - void pci_remove_root_bus(struct pci_bus *bus) - { -@@ -158,3 +159,4 @@ void pci_remove_root_bus(struct pci_bus *bus) - /* remove the host bridge */ - device_unregister(&host_bridge->dev); - } -+EXPORT_SYMBOL_GPL(pci_remove_root_bus); -diff --git a/drivers/pci/search.c b/drivers/pci/search.c -index a81f413..a00924f 100644 ---- a/drivers/pci/search.c -+++ b/drivers/pci/search.c -@@ -40,9 +40,8 @@ int pci_for_each_dma_alias(struct pci_dev *pdev, - * If the device is broken and uses an alias requester ID for - * DMA, iterate over that too. - */ -- if (unlikely(pdev->dev_flags & PCI_DEV_FLAGS_DMA_ALIAS_DEVFN)) { -- ret = fn(pdev, PCI_DEVID(pdev->bus->number, -- pdev->dma_alias_devfn), data); -+ if (unlikely(pdev->dev_flags & PCI_DEV_FLAGS_DMA_ALIAS_DEVID)) { -+ ret = fn(pdev, pdev->dma_alias_devid, data); - if (ret) - return ret; - } -diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c -index e3e17f3..8169597 100644 ---- a/drivers/pci/setup-bus.c -+++ b/drivers/pci/setup-bus.c -@@ -1750,3 +1750,4 @@ void pci_assign_unassigned_bus_resources(struct pci_bus *bus) - __pci_bus_assign_resources(bus, &add_list, NULL); - BUG_ON(!list_empty(&add_list)); - } -+EXPORT_SYMBOL_GPL(pci_assign_unassigned_bus_resources); -diff --git a/drivers/pci/setup-irq.c b/drivers/pci/setup-irq.c -index 4e2d595..95c225b 100644 ---- a/drivers/pci/setup-irq.c -+++ b/drivers/pci/setup-irq.c -@@ -65,3 +65,4 @@ void pci_fixup_irqs(u8 (*swizzle)(struct pci_dev *, u8 *), - for_each_pci_dev(dev) - pdev_fixup_irq(dev, swizzle, map_irq); - } -+EXPORT_SYMBOL_GPL(pci_fixup_irqs); -diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c -index 116ca37..37d4218 100644 ---- a/drivers/pci/xen-pcifront.c -+++ b/drivers/pci/xen-pcifront.c -@@ -267,7 +267,7 @@ static int pci_frontend_enable_msix(struct pci_dev *dev, - } - - i = 0; -- list_for_each_entry(entry, &dev->msi_list, list) { -+ for_each_pci_msi_entry(entry, dev) { - op.msix_entries[i].entry = entry->msi_attrib.entry_nr; - /* Vector is useless at this point. */ - op.msix_entries[i].vector = -1; -diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig -index f65ff49..b56b084 100644 ---- a/drivers/power/reset/Kconfig -+++ b/drivers/power/reset/Kconfig -@@ -150,5 +150,11 @@ config POWER_RESET_SYSCON - help - Reboot support for generic SYSCON mapped register reset. - -+config POWER_RESET_LAYERSCAPE -+ bool "Freescale LayerScape reset driver" -+ depends on ARCH_LAYERSCAPE -+ help -+ Reboot support for the Freescale LayerScape SoCs. -+ - endif - -diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile -index 76ce1c5..d924bdb 100644 ---- a/drivers/power/reset/Makefile -+++ b/drivers/power/reset/Makefile -@@ -17,3 +17,4 @@ obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o - obj-$(CONFIG_POWER_RESET_XGENE) += xgene-reboot.o - obj-$(CONFIG_POWER_RESET_KEYSTONE) += keystone-reset.o - obj-$(CONFIG_POWER_RESET_SYSCON) += syscon-reboot.o -+obj-$(CONFIG_POWER_RESET_LAYERSCAPE) += ls-reboot.o -diff --git a/drivers/power/reset/ls-reboot.c b/drivers/power/reset/ls-reboot.c -new file mode 100644 -index 0000000..fa1152c ---- /dev/null -+++ b/drivers/power/reset/ls-reboot.c -@@ -0,0 +1,93 @@ -+/* -+ * Freescale LayerScape reboot driver -+ * -+ * Copyright (c) 2015, Freescale Semiconductor. -+ * Author: Pankaj Chauhan -+ * -+ * 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. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+struct ls_reboot_priv { -+ struct device *dev; -+ u32 *rstcr; -+}; -+ -+static struct ls_reboot_priv *ls_reboot_priv; -+ -+static void ls_reboot(enum reboot_mode reboot_mode, const char *cmd) -+{ -+ struct ls_reboot_priv *priv = ls_reboot_priv; -+ u32 val; -+ unsigned long timeout; -+ -+ if (ls_reboot_priv) { -+ val = readl(priv->rstcr); -+ val |= 0x02; -+ writel(val, priv->rstcr); -+ } -+ -+ timeout = jiffies + HZ; -+ while (time_before(jiffies, timeout)) -+ cpu_relax(); -+ -+} -+ -+static int ls_reboot_probe(struct platform_device *pdev) -+{ -+ ls_reboot_priv = devm_kzalloc(&pdev->dev, -+ sizeof(*ls_reboot_priv), GFP_KERNEL); -+ if (!ls_reboot_priv) { -+ dev_err(&pdev->dev, "out of memory for context\n"); -+ return -ENODEV; -+ } -+ -+ ls_reboot_priv->rstcr = of_iomap(pdev->dev.of_node, 0); -+ if (!ls_reboot_priv->rstcr) { -+ devm_kfree(&pdev->dev, ls_reboot_priv); -+ dev_err(&pdev->dev, "can not map resource\n"); -+ return -ENODEV; -+ } -+ -+ ls_reboot_priv->dev = &pdev->dev; -+ -+ arm_pm_restart = ls_reboot; -+ -+ return 0; -+} -+ -+static struct of_device_id ls_reboot_of_match[] = { -+ { .compatible = "fsl,ls-reset" }, -+ {} -+}; -+ -+static struct platform_driver ls_reboot_driver = { -+ .probe = ls_reboot_probe, -+ .driver = { -+ .name = "ls-reset", -+ .of_match_table = ls_reboot_of_match, -+ }, -+}; -+ -+static int __init ls_reboot_init(void) -+{ -+ return platform_driver_register(&ls_reboot_driver); -+} -+device_initcall(ls_reboot_init); -diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig -index 76d6bd4..d4bcacf 100644 ---- a/drivers/soc/Kconfig -+++ b/drivers/soc/Kconfig -@@ -4,4 +4,17 @@ source "drivers/soc/qcom/Kconfig" - source "drivers/soc/ti/Kconfig" - source "drivers/soc/versatile/Kconfig" - -+config FSL_SOC_DRIVERS -+ bool "Freescale Soc Drivers" -+ depends on FSL_SOC || ARCH_MXC || ARCH_LAYERSCAPE -+ default n -+ help -+ Say y here to enable Freescale Soc Device Drivers support. -+ The Soc Drivers provides the device driver that is a specific block -+ or feature on Freescale platform. -+ -+if FSL_SOC_DRIVERS -+ source "drivers/soc/fsl/Kconfig" -+endif -+ - endmenu -diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile -index 063113d..ef82e45 100644 ---- a/drivers/soc/Makefile -+++ b/drivers/soc/Makefile -@@ -6,3 +6,4 @@ obj-$(CONFIG_ARCH_QCOM) += qcom/ - obj-$(CONFIG_ARCH_TEGRA) += tegra/ - obj-$(CONFIG_SOC_TI) += ti/ - obj-$(CONFIG_PLAT_VERSATILE) += versatile/ -+obj-$(CONFIG_FSL_SOC_DRIVERS) += fsl/ -diff --git a/drivers/soc/fsl/Kconfig b/drivers/soc/fsl/Kconfig -new file mode 100644 -index 0000000..92a085e ---- /dev/null -+++ b/drivers/soc/fsl/Kconfig -@@ -0,0 +1,6 @@ -+config FSL_GUTS -+ bool -+ -+if ARM || ARM64 -+source "drivers/soc/fsl/Kconfig.arm" -+endif -diff --git a/drivers/soc/fsl/Kconfig.arm b/drivers/soc/fsl/Kconfig.arm -new file mode 100644 -index 0000000..5f2d214 ---- /dev/null -+++ b/drivers/soc/fsl/Kconfig.arm -@@ -0,0 +1,25 @@ -+# -+# Freescale ARM SOC Drivers -+# -+ -+config LS1_SOC_DRIVERS -+ bool "LS1021A Soc Drivers" -+ depends on SOC_LS1021A -+ default n -+ help -+ Say y here to enable Freescale LS1021A Soc Device Drivers support. -+ The Soc Drivers provides the device driver that is a specific block -+ or feature on LS1021A platform. -+ -+config LS_SOC_DRIVERS -+ bool "Layerscape Soc Drivers" -+ depends on ARCH_LAYERSCAPE -+ default n -+ help -+ Say y here to enable Freescale Layerscape Soc Device Drivers support. -+ The Soc Drivers provides the device driver that is a specific block -+ or feature on Layerscape platform. -+ -+if LS1_SOC_DRIVERS -+ source "drivers/soc/fsl/ls1/Kconfig" -+endif -diff --git a/drivers/soc/fsl/Makefile b/drivers/soc/fsl/Makefile -new file mode 100644 -index 0000000..9fc17b3 ---- /dev/null -+++ b/drivers/soc/fsl/Makefile -@@ -0,0 +1,6 @@ -+# -+# Makefile for Freescale Soc specific device drivers. -+# -+ -+obj-$(CONFIG_LS1_SOC_DRIVERS) += ls1/ -+obj-$(CONFIG_FSL_GUTS) += guts.o -diff --git a/drivers/soc/fsl/guts.c b/drivers/soc/fsl/guts.c -new file mode 100644 -index 0000000..11065c2 ---- /dev/null -+++ b/drivers/soc/fsl/guts.c -@@ -0,0 +1,123 @@ -+/* -+ * Freescale QorIQ Platforms GUTS Driver -+ * -+ * Copyright (C) 2016 Freescale Semiconductor, 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 -+#include -+#include -+#include -+#include -+#include -+ -+struct guts { -+ struct ccsr_guts __iomem *regs; -+ bool little_endian; -+}; -+ -+static struct guts *guts; -+ -+u32 guts_get_svr(void) -+{ -+ u32 svr = 0; -+ -+ if ((!guts) || (!(guts->regs))) { -+#ifdef CONFIG_PPC -+ svr = mfspr(SPRN_SVR); -+#endif -+ return svr; -+ } -+ -+ if (guts->little_endian) -+ svr = ioread32(&guts->regs->svr); -+ else -+ svr = ioread32be(&guts->regs->svr); -+ -+ return svr; -+} -+EXPORT_SYMBOL_GPL(guts_get_svr); -+ -+static int guts_probe(struct platform_device *pdev) -+{ -+ struct device_node *np = pdev->dev.of_node; -+ -+ guts = kzalloc(sizeof(*guts), GFP_KERNEL); -+ if (!guts) -+ return -ENOMEM; -+ -+ if (of_property_read_bool(np, "little-endian")) -+ guts->little_endian = true; -+ else -+ guts->little_endian = false; -+ -+ guts->regs = of_iomap(np, 0); -+ if (!(guts->regs)) -+ return -ENOMEM; -+ -+ of_node_put(np); -+ return 0; -+} -+ -+static int guts_remove(struct platform_device *pdev) -+{ -+ iounmap(guts->regs); -+ kfree(guts); -+ return 0; -+} -+ -+/* -+ * Table for matching compatible strings, for device tree -+ * guts node, for Freescale QorIQ SOCs. -+ */ -+static const struct of_device_id guts_of_match[] = { -+ /* For T4 & B4 SOCs */ -+ { .compatible = "fsl,qoriq-device-config-1.0", }, -+ /* For P Series SOCs */ -+ { .compatible = "fsl,qoriq-device-config-2.0", }, -+ { .compatible = "fsl,p1010-guts", }, -+ { .compatible = "fsl,p1020-guts", }, -+ { .compatible = "fsl,p1021-guts", }, -+ { .compatible = "fsl,p1022-guts", }, -+ { .compatible = "fsl,p1023-guts", }, -+ { .compatible = "fsl,p2020-guts", }, -+ /* For BSC Series SOCs */ -+ { .compatible = "fsl,bsc9131-guts", }, -+ { .compatible = "fsl,bsc9132-guts", }, -+ /* For Layerscape Series SOCs */ -+ { .compatible = "fsl,ls1021a-dcfg", }, -+ { .compatible = "fsl,ls1043a-dcfg", }, -+ { .compatible = "fsl,ls2080a-dcfg", }, -+ {} -+}; -+MODULE_DEVICE_TABLE(of, guts_of_match); -+ -+static struct platform_driver guts_driver = { -+ .driver = { -+ .name = "fsl-guts", -+ .of_match_table = guts_of_match, -+ }, -+ .probe = guts_probe, -+ .remove = guts_remove, -+}; -+ -+static int __init guts_drv_init(void) -+{ -+ return platform_driver_register(&guts_driver); -+} -+subsys_initcall(guts_drv_init); -+ -+static void __exit guts_drv_exit(void) -+{ -+ platform_driver_unregister(&guts_driver); -+} -+module_exit(guts_drv_exit); -+ -+MODULE_AUTHOR("Freescale Semiconductor, Inc."); -+MODULE_DESCRIPTION("Freescale QorIQ Platforms GUTS Driver"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/soc/fsl/ls1/Kconfig b/drivers/soc/fsl/ls1/Kconfig -new file mode 100644 -index 0000000..c9b04c4 ---- /dev/null -+++ b/drivers/soc/fsl/ls1/Kconfig -@@ -0,0 +1,11 @@ -+# -+# LS-1 Soc drivers -+# -+config FTM_ALARM -+ bool "FTM alarm driver" -+ depends on SOC_LS1021A -+ default n -+ help -+ Say y here to enable FTM alarm support. The FTM alarm provides -+ alarm functions for wakeup system from deep sleep. There is only -+ one FTM can be used in ALARM(FTM 0). -diff --git a/drivers/soc/fsl/ls1/Makefile b/drivers/soc/fsl/ls1/Makefile -new file mode 100644 -index 0000000..6299aa1 ---- /dev/null -+++ b/drivers/soc/fsl/ls1/Makefile -@@ -0,0 +1 @@ -+obj-$(CONFIG_FTM_ALARM) += ftm_alarm.o -diff --git a/drivers/soc/fsl/ls1/ftm_alarm.c b/drivers/soc/fsl/ls1/ftm_alarm.c -new file mode 100644 -index 0000000..c42b26b ---- /dev/null -+++ b/drivers/soc/fsl/ls1/ftm_alarm.c -@@ -0,0 +1,274 @@ -+/* -+ * Freescale FlexTimer Module (FTM) Alarm driver. -+ * -+ * Copyright 2014 Freescale Semiconductor, 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 -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define FTM_SC 0x00 -+#define FTM_SC_CLK_SHIFT 3 -+#define FTM_SC_CLK_MASK (0x3 << FTM_SC_CLK_SHIFT) -+#define FTM_SC_CLK(c) ((c) << FTM_SC_CLK_SHIFT) -+#define FTM_SC_PS_MASK 0x7 -+#define FTM_SC_TOIE BIT(6) -+#define FTM_SC_TOF BIT(7) -+ -+#define FTM_SC_CLKS_FIXED_FREQ 0x02 -+ -+#define FTM_CNT 0x04 -+#define FTM_MOD 0x08 -+#define FTM_CNTIN 0x4C -+ -+#define FIXED_FREQ_CLK 32000 -+#define MAX_FREQ_DIV (1 << FTM_SC_PS_MASK) -+#define MAX_COUNT_VAL 0xffff -+ -+static void __iomem *ftm1_base; -+static u32 alarm_freq; -+static bool big_endian; -+ -+static inline u32 ftm_readl(void __iomem *addr) -+{ -+ if (big_endian) -+ return ioread32be(addr); -+ -+ return ioread32(addr); -+} -+ -+static inline void ftm_writel(u32 val, void __iomem *addr) -+{ -+ if (big_endian) -+ iowrite32be(val, addr); -+ else -+ iowrite32(val, addr); -+} -+ -+static inline void ftm_counter_enable(void __iomem *base) -+{ -+ u32 val; -+ -+ /* select and enable counter clock source */ -+ val = ftm_readl(base + FTM_SC); -+ val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK); -+ val |= (FTM_SC_PS_MASK | FTM_SC_CLK(FTM_SC_CLKS_FIXED_FREQ)); -+ ftm_writel(val, base + FTM_SC); -+} -+ -+static inline void ftm_counter_disable(void __iomem *base) -+{ -+ u32 val; -+ -+ /* disable counter clock source */ -+ val = ftm_readl(base + FTM_SC); -+ val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK); -+ ftm_writel(val, base + FTM_SC); -+} -+ -+static inline void ftm_irq_acknowledge(void __iomem *base) -+{ -+ u32 val; -+ -+ val = ftm_readl(base + FTM_SC); -+ val &= ~FTM_SC_TOF; -+ ftm_writel(val, base + FTM_SC); -+} -+ -+static inline void ftm_irq_enable(void __iomem *base) -+{ -+ u32 val; -+ -+ val = ftm_readl(base + FTM_SC); -+ val |= FTM_SC_TOIE; -+ ftm_writel(val, base + FTM_SC); -+} -+ -+static inline void ftm_irq_disable(void __iomem *base) -+{ -+ u32 val; -+ -+ val = ftm_readl(base + FTM_SC); -+ val &= ~FTM_SC_TOIE; -+ ftm_writel(val, base + FTM_SC); -+} -+ -+static inline void ftm_reset_counter(void __iomem *base) -+{ -+ /* -+ * The CNT register contains the FTM counter value. -+ * Reset clears the CNT register. Writing any value to COUNT -+ * updates the counter with its initial value, CNTIN. -+ */ -+ ftm_writel(0x00, base + FTM_CNT); -+} -+ -+static u32 time_to_cycle(unsigned long time) -+{ -+ u32 cycle; -+ -+ cycle = time * alarm_freq; -+ if (cycle > MAX_COUNT_VAL) { -+ pr_err("Out of alarm range.\n"); -+ cycle = 0; -+ } -+ -+ return cycle; -+} -+ -+static u32 cycle_to_time(u32 cycle) -+{ -+ return cycle / alarm_freq + 1; -+} -+ -+static void ftm_clean_alarm(void) -+{ -+ ftm_counter_disable(ftm1_base); -+ -+ ftm_writel(0x00, ftm1_base + FTM_CNTIN); -+ ftm_writel(~0UL, ftm1_base + FTM_MOD); -+ -+ ftm_reset_counter(ftm1_base); -+} -+ -+static int ftm_set_alarm(u64 cycle) -+{ -+ ftm_irq_disable(ftm1_base); -+ -+ /* -+ * The counter increments until the value of MOD is reached, -+ * at which point the counter is reloaded with the value of CNTIN. -+ * The TOF (the overflow flag) bit is set when the FTM counter -+ * changes from MOD to CNTIN. So we should using the cycle - 1. -+ */ -+ ftm_writel(cycle - 1, ftm1_base + FTM_MOD); -+ -+ ftm_counter_enable(ftm1_base); -+ -+ ftm_irq_enable(ftm1_base); -+ -+ return 0; -+} -+ -+static irqreturn_t ftm_alarm_interrupt(int irq, void *dev_id) -+{ -+ ftm_irq_acknowledge(ftm1_base); -+ ftm_irq_disable(ftm1_base); -+ ftm_clean_alarm(); -+ -+ return IRQ_HANDLED; -+} -+ -+static ssize_t ftm_alarm_show(struct device *dev, -+ struct device_attribute *attr, -+ char *buf) -+{ -+ u32 count, val; -+ -+ count = ftm_readl(ftm1_base + FTM_MOD); -+ val = ftm_readl(ftm1_base + FTM_CNT); -+ val = (count & MAX_COUNT_VAL) - val; -+ val = cycle_to_time(val); -+ -+ return sprintf(buf, "%u\n", val); -+} -+ -+static ssize_t ftm_alarm_store(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ u32 cycle; -+ unsigned long time; -+ -+ if (kstrtoul(buf, 0, &time)) -+ return -EINVAL; -+ -+ ftm_clean_alarm(); -+ -+ cycle = time_to_cycle(time); -+ if (!cycle) -+ return -EINVAL; -+ -+ ftm_set_alarm(cycle); -+ -+ return count; -+} -+ -+static struct device_attribute ftm_alarm_attributes = __ATTR(ftm_alarm, 0644, -+ ftm_alarm_show, ftm_alarm_store); -+ -+static int ftm_alarm_probe(struct platform_device *pdev) -+{ -+ struct device_node *np = pdev->dev.of_node; -+ struct resource *r; -+ int irq; -+ int ret; -+ -+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (!r) -+ return -ENODEV; -+ -+ ftm1_base = devm_ioremap_resource(&pdev->dev, r); -+ if (IS_ERR(ftm1_base)) -+ return PTR_ERR(ftm1_base); -+ -+ irq = irq_of_parse_and_map(np, 0); -+ if (irq <= 0) { -+ pr_err("ftm: unable to get IRQ from DT, %d\n", irq); -+ return -EINVAL; -+ } -+ -+ big_endian = of_property_read_bool(np, "big-endian"); -+ -+ ret = devm_request_irq(&pdev->dev, irq, ftm_alarm_interrupt, -+ IRQF_NO_SUSPEND, dev_name(&pdev->dev), NULL); -+ if (ret < 0) { -+ dev_err(&pdev->dev, "failed to request irq\n"); -+ return ret; -+ } -+ -+ ret = device_create_file(&pdev->dev, &ftm_alarm_attributes); -+ if (ret) { -+ dev_err(&pdev->dev, "create sysfs fail.\n"); -+ return ret; -+ } -+ -+ alarm_freq = (u32)FIXED_FREQ_CLK / (u32)MAX_FREQ_DIV; -+ -+ ftm_clean_alarm(); -+ -+ device_init_wakeup(&pdev->dev, true); -+ -+ return ret; -+} -+ -+static const struct of_device_id ftm_alarm_match[] = { -+ { .compatible = "fsl,ftm-alarm", }, -+ { .compatible = "fsl,ftm-timer", }, -+ { }, -+}; -+ -+static struct platform_driver ftm_alarm_driver = { -+ .probe = ftm_alarm_probe, -+ .driver = { -+ .name = "ftm-alarm", -+ .owner = THIS_MODULE, -+ .of_match_table = ftm_alarm_match, -+ }, -+}; -+ -+static int __init ftm_alarm_init(void) -+{ -+ return platform_driver_register(&ftm_alarm_driver); -+} -+device_initcall(ftm_alarm_init); -diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig -index 4690ae9..43ff2b5 100644 ---- a/drivers/staging/Kconfig -+++ b/drivers/staging/Kconfig -@@ -108,4 +108,8 @@ source "drivers/staging/skein/Kconfig" - - source "drivers/staging/unisys/Kconfig" - -+source "drivers/staging/fsl-mc/Kconfig" -+ -+source "drivers/staging/fsl-dpaa2/Kconfig" -+ - endif # STAGING -diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile -index c780a0e..a9bd303 100644 ---- a/drivers/staging/Makefile -+++ b/drivers/staging/Makefile -@@ -46,3 +46,5 @@ obj-$(CONFIG_MTD_SPINAND_MT29F) += mt29f_spinand/ - obj-$(CONFIG_GS_FPGABOOT) += gs_fpgaboot/ - obj-$(CONFIG_CRYPTO_SKEIN) += skein/ - obj-$(CONFIG_UNISYSSPAR) += unisys/ -+obj-$(CONFIG_FSL_MC_BUS) += fsl-mc/ -+obj-$(CONFIG_FSL_DPAA2) += fsl-dpaa2/ -diff --git a/drivers/staging/fsl-dpaa2/Kconfig b/drivers/staging/fsl-dpaa2/Kconfig -new file mode 100644 -index 0000000..3fe47bc ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/Kconfig -@@ -0,0 +1,12 @@ -+# -+# Freescale device configuration -+# -+ -+config FSL_DPAA2 -+ bool "Freescale DPAA2 devices" -+ depends on FSL_MC_BUS -+ ---help--- -+ Build drivers for Freescale DataPath Acceleration Architecture (DPAA2) family of SoCs. -+# TODO move DPIO driver in-here? -+source "drivers/staging/fsl-dpaa2/ethernet/Kconfig" -+source "drivers/staging/fsl-dpaa2/mac/Kconfig" -diff --git a/drivers/staging/fsl-dpaa2/Makefile b/drivers/staging/fsl-dpaa2/Makefile -new file mode 100644 -index 0000000..bc687a1 ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/Makefile -@@ -0,0 +1,6 @@ -+# -+# Makefile for the Freescale network device drivers. -+# -+ -+obj-$(CONFIG_FSL_DPAA2_ETH) += ethernet/ -+obj-$(CONFIG_FSL_DPAA2_MAC) += mac/ -diff --git a/drivers/staging/fsl-dpaa2/ethernet/Kconfig b/drivers/staging/fsl-dpaa2/ethernet/Kconfig -new file mode 100644 -index 0000000..df91da2 ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/ethernet/Kconfig -@@ -0,0 +1,36 @@ -+# -+# Freescale DPAA Ethernet driver configuration -+# -+# Copyright (C) 2014-2015 Freescale Semiconductor, Inc. -+# -+# This file is released under the GPLv2 -+# -+ -+menuconfig FSL_DPAA2_ETH -+ tristate "Freescale DPAA2 Ethernet" -+ depends on FSL_DPAA2 && FSL_MC_BUS && FSL_MC_DPIO -+ select FSL_DPAA2_MAC -+ default y -+ ---help--- -+ Freescale Data Path Acceleration Architecture Ethernet -+ driver, using the Freescale MC bus driver. -+ -+if FSL_DPAA2_ETH -+ -+config FSL_DPAA2_ETH_USE_ERR_QUEUE -+ bool "Enable Rx error queue" -+ default n -+ ---help--- -+ Allow Rx error frames to be enqueued on an error queue -+ and processed by the driver (by default they are dropped -+ in hardware). -+ This may impact performance, recommended for debugging -+ purposes only. -+ -+config FSL_DPAA2_ETH_DEBUGFS -+ depends on DEBUG_FS && FSL_QBMAN_DEBUG -+ bool "Enable debugfs support" -+ default n -+ ---help--- -+ Enable advanced statistics through debugfs interface. -+endif -diff --git a/drivers/staging/fsl-dpaa2/ethernet/Makefile b/drivers/staging/fsl-dpaa2/ethernet/Makefile -new file mode 100644 -index 0000000..74bff15 ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/ethernet/Makefile -@@ -0,0 +1,21 @@ -+# -+# Makefile for the Freescale DPAA Ethernet controllers -+# -+# Copyright (C) 2014-2015 Freescale Semiconductor, Inc. -+# -+# This file is released under the GPLv2 -+# -+ -+ccflags-y += -DVERSION=\"\" -+ -+obj-$(CONFIG_FSL_DPAA2_ETH) += fsl-dpaa2-eth.o -+ -+fsl-dpaa2-eth-objs := dpaa2-eth.o dpaa2-ethtool.o dpni.o -+fsl-dpaa2-eth-${CONFIG_FSL_DPAA2_ETH_DEBUGFS} += dpaa2-eth-debugfs.o -+ -+#Needed by the tracing framework -+CFLAGS_dpaa2-eth.o := -I$(src) -+ -+ifeq ($(CONFIG_FSL_DPAA2_ETH_GCOV),y) -+ GCOV_PROFILE := y -+endif -diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-debugfs.c b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-debugfs.c -new file mode 100644 -index 0000000..c397983 ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-debugfs.c -@@ -0,0 +1,317 @@ -+ -+/* Copyright 2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 -+#include -+#include "dpaa2-eth.h" -+#include "dpaa2-eth-debugfs.h" -+ -+#define DPAA2_ETH_DBG_ROOT "dpaa2-eth" -+ -+static struct dentry *dpaa2_dbg_root; -+ -+static int dpaa2_dbg_cpu_show(struct seq_file *file, void *offset) -+{ -+ struct dpaa2_eth_priv *priv = (struct dpaa2_eth_priv *)file->private; -+ struct rtnl_link_stats64 *stats; -+ struct dpaa2_eth_drv_stats *extras; -+ int i; -+ -+ seq_printf(file, "Per-CPU stats for %s\n", priv->net_dev->name); -+ seq_printf(file, "%s%16s%16s%16s%16s%16s%16s%16s%16s\n", -+ "CPU", "Rx", "Rx Err", "Rx SG", "Tx", "Tx Err", "Tx conf", -+ "Tx SG", "Enq busy"); -+ -+ for_each_online_cpu(i) { -+ stats = per_cpu_ptr(priv->percpu_stats, i); -+ extras = per_cpu_ptr(priv->percpu_extras, i); -+ seq_printf(file, "%3d%16llu%16llu%16llu%16llu%16llu%16llu%16llu%16llu\n", -+ i, -+ stats->rx_packets, -+ stats->rx_errors, -+ extras->rx_sg_frames, -+ stats->tx_packets, -+ stats->tx_errors, -+ extras->tx_conf_frames, -+ extras->tx_sg_frames, -+ extras->tx_portal_busy); -+ } -+ -+ return 0; -+} -+ -+static int dpaa2_dbg_cpu_open(struct inode *inode, struct file *file) -+{ -+ int err; -+ struct dpaa2_eth_priv *priv = (struct dpaa2_eth_priv *)inode->i_private; -+ -+ err = single_open(file, dpaa2_dbg_cpu_show, priv); -+ if (err < 0) -+ netdev_err(priv->net_dev, "single_open() failed\n"); -+ -+ return err; -+} -+ -+static const struct file_operations dpaa2_dbg_cpu_ops = { -+ .open = dpaa2_dbg_cpu_open, -+ .read = seq_read, -+ .llseek = seq_lseek, -+ .release = single_release, -+}; -+ -+static char *fq_type_to_str(struct dpaa2_eth_fq *fq) -+{ -+ switch (fq->type) { -+ case DPAA2_RX_FQ: -+ return "Rx"; -+ case DPAA2_TX_CONF_FQ: -+ return "Tx conf"; -+ case DPAA2_RX_ERR_FQ: -+ return "Rx err"; -+ default: -+ return "N/A"; -+ } -+} -+ -+static int dpaa2_dbg_fqs_show(struct seq_file *file, void *offset) -+{ -+ struct dpaa2_eth_priv *priv = (struct dpaa2_eth_priv *)file->private; -+ struct dpaa2_eth_fq *fq; -+ u32 fcnt, bcnt; -+ int i, err; -+ -+ seq_printf(file, "FQ stats for %s:\n", priv->net_dev->name); -+ seq_printf(file, "%s%16s%16s%16s%16s\n", -+ "VFQID", "CPU", "Type", "Frames", "Pending frames"); -+ -+ for (i = 0; i < priv->num_fqs; i++) { -+ fq = &priv->fq[i]; -+ err = dpaa2_io_query_fq_count(NULL, fq->fqid, &fcnt, &bcnt); -+ if (err) -+ fcnt = 0; -+ -+ seq_printf(file, "%5d%16d%16s%16llu%16u\n", -+ fq->fqid, -+ fq->target_cpu, -+ fq_type_to_str(fq), -+ fq->stats.frames, -+ fcnt); -+ } -+ -+ return 0; -+} -+ -+static int dpaa2_dbg_fqs_open(struct inode *inode, struct file *file) -+{ -+ int err; -+ struct dpaa2_eth_priv *priv = (struct dpaa2_eth_priv *)inode->i_private; -+ -+ err = single_open(file, dpaa2_dbg_fqs_show, priv); -+ if (err < 0) -+ netdev_err(priv->net_dev, "single_open() failed\n"); -+ -+ return err; -+} -+ -+static const struct file_operations dpaa2_dbg_fq_ops = { -+ .open = dpaa2_dbg_fqs_open, -+ .read = seq_read, -+ .llseek = seq_lseek, -+ .release = single_release, -+}; -+ -+static int dpaa2_dbg_ch_show(struct seq_file *file, void *offset) -+{ -+ struct dpaa2_eth_priv *priv = (struct dpaa2_eth_priv *)file->private; -+ struct dpaa2_eth_channel *ch; -+ int i; -+ -+ seq_printf(file, "Channel stats for %s:\n", priv->net_dev->name); -+ seq_printf(file, "%s%16s%16s%16s%16s%16s\n", -+ "CHID", "CPU", "Deq busy", "Frames", "CDANs", -+ "Avg frm/CDAN"); -+ -+ for (i = 0; i < priv->num_channels; i++) { -+ ch = priv->channel[i]; -+ seq_printf(file, "%4d%16d%16llu%16llu%16llu%16llu\n", -+ ch->ch_id, -+ ch->nctx.desired_cpu, -+ ch->stats.dequeue_portal_busy, -+ ch->stats.frames, -+ ch->stats.cdan, -+ ch->stats.frames / ch->stats.cdan); -+ } -+ -+ return 0; -+} -+ -+static int dpaa2_dbg_ch_open(struct inode *inode, struct file *file) -+{ -+ int err; -+ struct dpaa2_eth_priv *priv = (struct dpaa2_eth_priv *)inode->i_private; -+ -+ err = single_open(file, dpaa2_dbg_ch_show, priv); -+ if (err < 0) -+ netdev_err(priv->net_dev, "single_open() failed\n"); -+ -+ return err; -+} -+ -+static const struct file_operations dpaa2_dbg_ch_ops = { -+ .open = dpaa2_dbg_ch_open, -+ .read = seq_read, -+ .llseek = seq_lseek, -+ .release = single_release, -+}; -+ -+static ssize_t dpaa2_dbg_reset_write(struct file *file, const char __user *buf, -+ size_t count, loff_t *offset) -+{ -+ struct dpaa2_eth_priv *priv = file->private_data; -+ struct rtnl_link_stats64 *percpu_stats; -+ struct dpaa2_eth_drv_stats *percpu_extras; -+ struct dpaa2_eth_fq *fq; -+ struct dpaa2_eth_channel *ch; -+ int i; -+ -+ for_each_online_cpu(i) { -+ percpu_stats = per_cpu_ptr(priv->percpu_stats, i); -+ memset(percpu_stats, 0, sizeof(*percpu_stats)); -+ -+ percpu_extras = per_cpu_ptr(priv->percpu_extras, i); -+ memset(percpu_extras, 0, sizeof(*percpu_extras)); -+ } -+ -+ for (i = 0; i < priv->num_fqs; i++) { -+ fq = &priv->fq[i]; -+ memset(&fq->stats, 0, sizeof(fq->stats)); -+ } -+ -+ for_each_cpu(i, &priv->dpio_cpumask) { -+ ch = priv->channel[i]; -+ memset(&ch->stats, 0, sizeof(ch->stats)); -+ } -+ -+ return count; -+} -+ -+static const struct file_operations dpaa2_dbg_reset_ops = { -+ .open = simple_open, -+ .write = dpaa2_dbg_reset_write, -+}; -+ -+void dpaa2_dbg_add(struct dpaa2_eth_priv *priv) -+{ -+ if (!dpaa2_dbg_root) -+ return; -+ -+ /* Create a directory for the interface */ -+ priv->dbg.dir = debugfs_create_dir(priv->net_dev->name, -+ dpaa2_dbg_root); -+ if (!priv->dbg.dir) { -+ netdev_err(priv->net_dev, "debugfs_create_dir() failed\n"); -+ return; -+ } -+ -+ /* per-cpu stats file */ -+ priv->dbg.cpu_stats = debugfs_create_file("cpu_stats", S_IRUGO, -+ priv->dbg.dir, priv, -+ &dpaa2_dbg_cpu_ops); -+ if (!priv->dbg.cpu_stats) { -+ netdev_err(priv->net_dev, "debugfs_create_file() failed\n"); -+ goto err_cpu_stats; -+ } -+ -+ /* per-fq stats file */ -+ priv->dbg.fq_stats = debugfs_create_file("fq_stats", S_IRUGO, -+ priv->dbg.dir, priv, -+ &dpaa2_dbg_fq_ops); -+ if (!priv->dbg.fq_stats) { -+ netdev_err(priv->net_dev, "debugfs_create_file() failed\n"); -+ goto err_fq_stats; -+ } -+ -+ /* per-fq stats file */ -+ priv->dbg.ch_stats = debugfs_create_file("ch_stats", S_IRUGO, -+ priv->dbg.dir, priv, -+ &dpaa2_dbg_ch_ops); -+ if (!priv->dbg.fq_stats) { -+ netdev_err(priv->net_dev, "debugfs_create_file() failed\n"); -+ goto err_ch_stats; -+ } -+ -+ /* reset stats */ -+ priv->dbg.reset_stats = debugfs_create_file("reset_stats", S_IWUSR, -+ priv->dbg.dir, priv, -+ &dpaa2_dbg_reset_ops); -+ if (!priv->dbg.reset_stats) { -+ netdev_err(priv->net_dev, "debugfs_create_file() failed\n"); -+ goto err_reset_stats; -+ } -+ -+ return; -+ -+err_reset_stats: -+ debugfs_remove(priv->dbg.ch_stats); -+err_ch_stats: -+ debugfs_remove(priv->dbg.fq_stats); -+err_fq_stats: -+ debugfs_remove(priv->dbg.cpu_stats); -+err_cpu_stats: -+ debugfs_remove(priv->dbg.dir); -+} -+ -+void dpaa2_dbg_remove(struct dpaa2_eth_priv *priv) -+{ -+ debugfs_remove(priv->dbg.reset_stats); -+ debugfs_remove(priv->dbg.fq_stats); -+ debugfs_remove(priv->dbg.ch_stats); -+ debugfs_remove(priv->dbg.cpu_stats); -+ debugfs_remove(priv->dbg.dir); -+} -+ -+void dpaa2_eth_dbg_init(void) -+{ -+ dpaa2_dbg_root = debugfs_create_dir(DPAA2_ETH_DBG_ROOT, NULL); -+ if (!dpaa2_dbg_root) { -+ pr_err("DPAA2-ETH: debugfs create failed\n"); -+ return; -+ } -+ -+ pr_info("DPAA2-ETH: debugfs created\n"); -+} -+ -+void __exit dpaa2_eth_dbg_exit(void) -+{ -+ debugfs_remove(dpaa2_dbg_root); -+} -+ -diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-debugfs.h b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-debugfs.h -new file mode 100644 -index 0000000..7ba706c ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-debugfs.h -@@ -0,0 +1,61 @@ -+/* Copyright 2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 DPAA2_ETH_DEBUGFS_H -+#define DPAA2_ETH_DEBUGFS_H -+ -+#include -+#include "dpaa2-eth.h" -+ -+extern struct dpaa2_eth_priv *priv; -+ -+struct dpaa2_debugfs { -+ struct dentry *dir; -+ struct dentry *fq_stats; -+ struct dentry *ch_stats; -+ struct dentry *cpu_stats; -+ struct dentry *reset_stats; -+}; -+ -+#ifdef CONFIG_FSL_DPAA2_ETH_DEBUGFS -+void dpaa2_eth_dbg_init(void); -+void dpaa2_eth_dbg_exit(void); -+void dpaa2_dbg_add(struct dpaa2_eth_priv *priv); -+void dpaa2_dbg_remove(struct dpaa2_eth_priv *priv); -+#else -+static inline void dpaa2_eth_dbg_init(void) {} -+static inline void dpaa2_eth_dbg_exit(void) {} -+static inline void dpaa2_dbg_add(struct dpaa2_eth_priv *priv) {} -+static inline void dpaa2_dbg_remove(struct dpaa2_eth_priv *priv) {} -+#endif /* CONFIG_FSL_DPAA2_ETH_DEBUGFS */ -+ -+#endif /* DPAA2_ETH_DEBUGFS_H */ -+ -diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-trace.h b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-trace.h -new file mode 100644 -index 0000000..3b040e8 ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-trace.h -@@ -0,0 +1,185 @@ -+/* Copyright 2014-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. -+ */ -+ -+#undef TRACE_SYSTEM -+#define TRACE_SYSTEM dpaa2_eth -+ -+#if !defined(_DPAA2_ETH_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) -+#define _DPAA2_ETH_TRACE_H -+ -+#include -+#include -+#include "dpaa2-eth.h" -+#include -+ -+#define TR_FMT "[%s] fd: addr=0x%llx, len=%u, off=%u" -+/* trace_printk format for raw buffer event class */ -+#define TR_BUF_FMT "[%s] vaddr=%p size=%zu dma_addr=%pad map_size=%zu bpid=%d" -+ -+/* This is used to declare a class of events. -+ * individual events of this type will be defined below. -+ */ -+ -+/* Store details about a frame descriptor */ -+DECLARE_EVENT_CLASS(dpaa2_eth_fd, -+ /* Trace function prototype */ -+ TP_PROTO(struct net_device *netdev, -+ const struct dpaa2_fd *fd), -+ -+ /* Repeat argument list here */ -+ TP_ARGS(netdev, fd), -+ -+ /* A structure containing the relevant information we want -+ * to record. Declare name and type for each normal element, -+ * name, type and size for arrays. Use __string for variable -+ * length strings. -+ */ -+ TP_STRUCT__entry( -+ __field(u64, fd_addr) -+ __field(u32, fd_len) -+ __field(u16, fd_offset) -+ __string(name, netdev->name) -+ ), -+ -+ /* The function that assigns values to the above declared -+ * fields -+ */ -+ TP_fast_assign( -+ __entry->fd_addr = dpaa2_fd_get_addr(fd); -+ __entry->fd_len = dpaa2_fd_get_len(fd); -+ __entry->fd_offset = dpaa2_fd_get_offset(fd); -+ __assign_str(name, netdev->name); -+ ), -+ -+ /* This is what gets printed when the trace event is -+ * triggered. -+ */ -+ TP_printk(TR_FMT, -+ __get_str(name), -+ __entry->fd_addr, -+ __entry->fd_len, -+ __entry->fd_offset) -+); -+ -+/* Now declare events of the above type. Format is: -+ * DEFINE_EVENT(class, name, proto, args), with proto and args same as for class -+ */ -+ -+/* Tx (egress) fd */ -+DEFINE_EVENT(dpaa2_eth_fd, dpaa2_tx_fd, -+ TP_PROTO(struct net_device *netdev, -+ const struct dpaa2_fd *fd), -+ -+ TP_ARGS(netdev, fd) -+); -+ -+/* Rx fd */ -+DEFINE_EVENT(dpaa2_eth_fd, dpaa2_rx_fd, -+ TP_PROTO(struct net_device *netdev, -+ const struct dpaa2_fd *fd), -+ -+ TP_ARGS(netdev, fd) -+); -+ -+/* Tx confirmation fd */ -+DEFINE_EVENT(dpaa2_eth_fd, dpaa2_tx_conf_fd, -+ TP_PROTO(struct net_device *netdev, -+ const struct dpaa2_fd *fd), -+ -+ TP_ARGS(netdev, fd) -+); -+ -+/* Log data about raw buffers. Useful for tracing DPBP content. */ -+TRACE_EVENT(dpaa2_eth_buf_seed, -+ /* Trace function prototype */ -+ TP_PROTO(struct net_device *netdev, -+ /* virtual address and size */ -+ void *vaddr, -+ size_t size, -+ /* dma map address and size */ -+ dma_addr_t dma_addr, -+ size_t map_size, -+ /* buffer pool id, if relevant */ -+ u16 bpid), -+ -+ /* Repeat argument list here */ -+ TP_ARGS(netdev, vaddr, size, dma_addr, map_size, bpid), -+ -+ /* A structure containing the relevant information we want -+ * to record. Declare name and type for each normal element, -+ * name, type and size for arrays. Use __string for variable -+ * length strings. -+ */ -+ TP_STRUCT__entry( -+ __field(void *, vaddr) -+ __field(size_t, size) -+ __field(dma_addr_t, dma_addr) -+ __field(size_t, map_size) -+ __field(u16, bpid) -+ __string(name, netdev->name) -+ ), -+ -+ /* The function that assigns values to the above declared -+ * fields -+ */ -+ TP_fast_assign( -+ __entry->vaddr = vaddr; -+ __entry->size = size; -+ __entry->dma_addr = dma_addr; -+ __entry->map_size = map_size; -+ __entry->bpid = bpid; -+ __assign_str(name, netdev->name); -+ ), -+ -+ /* This is what gets printed when the trace event is -+ * triggered. -+ */ -+ TP_printk(TR_BUF_FMT, -+ __get_str(name), -+ __entry->vaddr, -+ __entry->size, -+ &__entry->dma_addr, -+ __entry->map_size, -+ __entry->bpid) -+); -+ -+/* If only one event of a certain type needs to be declared, use TRACE_EVENT(). -+ * The syntax is the same as for DECLARE_EVENT_CLASS(). -+ */ -+ -+#endif /* _DPAA2_ETH_TRACE_H */ -+ -+/* This must be outside ifdef _DPAA2_ETH_TRACE_H */ -+#undef TRACE_INCLUDE_PATH -+#define TRACE_INCLUDE_PATH . -+#undef TRACE_INCLUDE_FILE -+#define TRACE_INCLUDE_FILE dpaa2-eth-trace -+#include -diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c -new file mode 100644 -index 0000000..cb52ede ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c -@@ -0,0 +1,2957 @@ -+/* Copyright 2014-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "../../fsl-mc/include/mc.h" -+#include "../../fsl-mc/include/mc-sys.h" -+#include "dpaa2-eth.h" -+ -+/* CREATE_TRACE_POINTS only needs to be defined once. Other dpa files -+ * using trace events only need to #include -+ */ -+#define CREATE_TRACE_POINTS -+#include "dpaa2-eth-trace.h" -+ -+MODULE_LICENSE("Dual BSD/GPL"); -+MODULE_AUTHOR("Freescale Semiconductor, Inc"); -+MODULE_DESCRIPTION("Freescale DPAA2 Ethernet Driver"); -+ -+static void validate_rx_csum(struct dpaa2_eth_priv *priv, -+ u32 fd_status, -+ struct sk_buff *skb) -+{ -+ skb_checksum_none_assert(skb); -+ -+ /* HW checksum validation is disabled, nothing to do here */ -+ if (!(priv->net_dev->features & NETIF_F_RXCSUM)) -+ return; -+ -+ /* Read checksum validation bits */ -+ if (!((fd_status & DPAA2_FAS_L3CV) && -+ (fd_status & DPAA2_FAS_L4CV))) -+ return; -+ -+ /* Inform the stack there's no need to compute L3/L4 csum anymore */ -+ skb->ip_summed = CHECKSUM_UNNECESSARY; -+} -+ -+/* Free a received FD. -+ * Not to be used for Tx conf FDs or on any other paths. -+ */ -+static void free_rx_fd(struct dpaa2_eth_priv *priv, -+ const struct dpaa2_fd *fd, -+ void *vaddr) -+{ -+ struct device *dev = priv->net_dev->dev.parent; -+ dma_addr_t addr = dpaa2_fd_get_addr(fd); -+ u8 fd_format = dpaa2_fd_get_format(fd); -+ struct dpaa2_sg_entry *sgt; -+ void *sg_vaddr; -+ int i; -+ -+ /* If single buffer frame, just free the data buffer */ -+ if (fd_format == dpaa2_fd_single) -+ goto free_buf; -+ -+ /* For S/G frames, we first need to free all SG entries */ -+ sgt = vaddr + dpaa2_fd_get_offset(fd); -+ for (i = 0; i < DPAA2_ETH_MAX_SG_ENTRIES; i++) { -+ dpaa2_sg_le_to_cpu(&sgt[i]); -+ -+ addr = dpaa2_sg_get_addr(&sgt[i]); -+ dma_unmap_single(dev, addr, DPAA2_ETH_RX_BUF_SIZE, -+ DMA_FROM_DEVICE); -+ -+ sg_vaddr = phys_to_virt(addr); -+ put_page(virt_to_head_page(sg_vaddr)); -+ -+ if (dpaa2_sg_is_final(&sgt[i])) -+ break; -+ } -+ -+free_buf: -+ put_page(virt_to_head_page(vaddr)); -+} -+ -+/* Build a linear skb based on a single-buffer frame descriptor */ -+static struct sk_buff *build_linear_skb(struct dpaa2_eth_priv *priv, -+ struct dpaa2_eth_channel *ch, -+ const struct dpaa2_fd *fd, -+ void *fd_vaddr) -+{ -+ struct sk_buff *skb = NULL; -+ u16 fd_offset = dpaa2_fd_get_offset(fd); -+ u32 fd_length = dpaa2_fd_get_len(fd); -+ -+ skb = build_skb(fd_vaddr, DPAA2_ETH_RX_BUF_SIZE + -+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info))); -+ if (unlikely(!skb)) -+ return NULL; -+ -+ skb_reserve(skb, fd_offset); -+ skb_put(skb, fd_length); -+ -+ ch->buf_count--; -+ -+ return skb; -+} -+ -+/* Build a non linear (fragmented) skb based on a S/G table */ -+static struct sk_buff *build_frag_skb(struct dpaa2_eth_priv *priv, -+ struct dpaa2_eth_channel *ch, -+ struct dpaa2_sg_entry *sgt) -+{ -+ struct sk_buff *skb = NULL; -+ struct device *dev = priv->net_dev->dev.parent; -+ void *sg_vaddr; -+ dma_addr_t sg_addr; -+ u16 sg_offset; -+ u32 sg_length; -+ struct page *page, *head_page; -+ int page_offset; -+ int i; -+ -+ for (i = 0; i < DPAA2_ETH_MAX_SG_ENTRIES; i++) { -+ struct dpaa2_sg_entry *sge = &sgt[i]; -+ -+ dpaa2_sg_le_to_cpu(sge); -+ -+ /* NOTE: We only support SG entries in dpaa2_sg_single format, -+ * but this is the only format we may receive from HW anyway -+ */ -+ -+ /* Get the address and length from the S/G entry */ -+ sg_addr = dpaa2_sg_get_addr(sge); -+ dma_unmap_single(dev, sg_addr, DPAA2_ETH_RX_BUF_SIZE, -+ DMA_FROM_DEVICE); -+ -+ sg_vaddr = phys_to_virt(sg_addr); -+ sg_length = dpaa2_sg_get_len(sge); -+ -+ if (i == 0) { -+ /* We build the skb around the first data buffer */ -+ skb = build_skb(sg_vaddr, DPAA2_ETH_RX_BUF_SIZE + -+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info))); -+ if (unlikely(!skb)) -+ return NULL; -+ -+ sg_offset = dpaa2_sg_get_offset(sge); -+ skb_reserve(skb, sg_offset); -+ skb_put(skb, sg_length); -+ } else { -+ /* Rest of the data buffers are stored as skb frags */ -+ page = virt_to_page(sg_vaddr); -+ head_page = virt_to_head_page(sg_vaddr); -+ -+ /* Offset in page (which may be compound). -+ * Data in subsequent SG entries is stored from the -+ * beginning of the buffer, so we don't need to add the -+ * sg_offset. -+ */ -+ page_offset = ((unsigned long)sg_vaddr & -+ (PAGE_SIZE - 1)) + -+ (page_address(page) - page_address(head_page)); -+ -+ skb_add_rx_frag(skb, i - 1, head_page, page_offset, -+ sg_length, DPAA2_ETH_RX_BUF_SIZE); -+ } -+ -+ if (dpaa2_sg_is_final(sge)) -+ break; -+ } -+ -+ /* Count all data buffers + SG table buffer */ -+ ch->buf_count -= i + 2; -+ -+ return skb; -+} -+ -+/* Main Rx frame processing routine */ -+static void dpaa2_eth_rx(struct dpaa2_eth_priv *priv, -+ struct dpaa2_eth_channel *ch, -+ const struct dpaa2_fd *fd, -+ struct napi_struct *napi) -+{ -+ dma_addr_t addr = dpaa2_fd_get_addr(fd); -+ u8 fd_format = dpaa2_fd_get_format(fd); -+ void *vaddr; -+ struct sk_buff *skb; -+ struct rtnl_link_stats64 *percpu_stats; -+ struct dpaa2_eth_drv_stats *percpu_extras; -+ struct device *dev = priv->net_dev->dev.parent; -+ struct dpaa2_fas *fas; -+ u32 status = 0; -+ -+ /* Tracing point */ -+ trace_dpaa2_rx_fd(priv->net_dev, fd); -+ -+ dma_unmap_single(dev, addr, DPAA2_ETH_RX_BUF_SIZE, DMA_FROM_DEVICE); -+ vaddr = phys_to_virt(addr); -+ -+ prefetch(vaddr + priv->buf_layout.private_data_size); -+ prefetch(vaddr + dpaa2_fd_get_offset(fd)); -+ -+ percpu_stats = this_cpu_ptr(priv->percpu_stats); -+ percpu_extras = this_cpu_ptr(priv->percpu_extras); -+ -+ if (fd_format == dpaa2_fd_single) { -+ skb = build_linear_skb(priv, ch, fd, vaddr); -+ } else if (fd_format == dpaa2_fd_sg) { -+ struct dpaa2_sg_entry *sgt = -+ vaddr + dpaa2_fd_get_offset(fd); -+ skb = build_frag_skb(priv, ch, sgt); -+ put_page(virt_to_head_page(vaddr)); -+ percpu_extras->rx_sg_frames++; -+ percpu_extras->rx_sg_bytes += dpaa2_fd_get_len(fd); -+ } else { -+ /* We don't support any other format */ -+ goto err_frame_format; -+ } -+ -+ if (unlikely(!skb)) -+ goto err_build_skb; -+ -+ prefetch(skb->data); -+ -+ /* Get the timestamp value */ -+ if (priv->ts_rx_en) { -+ struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb); -+ u64 *ns = (u64 *)(vaddr + -+ priv->buf_layout.private_data_size + -+ sizeof(struct dpaa2_fas)); -+ -+ *ns = DPAA2_PTP_NOMINAL_FREQ_PERIOD_NS * le64_to_cpup(ns); -+ memset(shhwtstamps, 0, sizeof(*shhwtstamps)); -+ shhwtstamps->hwtstamp = ns_to_ktime(*ns); -+ } -+ -+ /* Check if we need to validate the L4 csum */ -+ if (likely(fd->simple.frc & DPAA2_FD_FRC_FASV)) { -+ fas = (struct dpaa2_fas *) -+ (vaddr + priv->buf_layout.private_data_size); -+ status = le32_to_cpu(fas->status); -+ validate_rx_csum(priv, status, skb); -+ } -+ -+ skb->protocol = eth_type_trans(skb, priv->net_dev); -+ -+ percpu_stats->rx_packets++; -+ percpu_stats->rx_bytes += skb->len; -+ -+ if (priv->net_dev->features & NETIF_F_GRO) -+ napi_gro_receive(napi, skb); -+ else -+ netif_receive_skb(skb); -+ -+ return; -+err_frame_format: -+err_build_skb: -+ free_rx_fd(priv, fd, vaddr); -+ percpu_stats->rx_dropped++; -+} -+ -+#ifdef CONFIG_FSL_DPAA2_ETH_USE_ERR_QUEUE -+/* Processing of Rx frames received on the error FQ -+ * We check and print the error bits and then free the frame -+ */ -+static void dpaa2_eth_rx_err(struct dpaa2_eth_priv *priv, -+ struct dpaa2_eth_channel *ch, -+ const struct dpaa2_fd *fd, -+ struct napi_struct *napi __always_unused) -+{ -+ struct device *dev = priv->net_dev->dev.parent; -+ dma_addr_t addr = dpaa2_fd_get_addr(fd); -+ void *vaddr; -+ struct rtnl_link_stats64 *percpu_stats; -+ struct dpaa2_fas *fas; -+ u32 status = 0; -+ -+ dma_unmap_single(dev, addr, DPAA2_ETH_RX_BUF_SIZE, DMA_FROM_DEVICE); -+ vaddr = phys_to_virt(addr); -+ -+ if (fd->simple.frc & DPAA2_FD_FRC_FASV) { -+ fas = (struct dpaa2_fas *) -+ (vaddr + priv->buf_layout.private_data_size); -+ status = le32_to_cpu(fas->status); -+ if (net_ratelimit()) -+ netdev_warn(priv->net_dev, "Rx frame error: 0x%08x\n", -+ status & DPAA2_ETH_RX_ERR_MASK); -+ } -+ free_rx_fd(priv, fd, vaddr); -+ -+ percpu_stats = this_cpu_ptr(priv->percpu_stats); -+ percpu_stats->rx_errors++; -+} -+#endif -+ -+/* Consume all frames pull-dequeued into the store. This is the simplest way to -+ * make sure we don't accidentally issue another volatile dequeue which would -+ * overwrite (leak) frames already in the store. -+ * -+ * Observance of NAPI budget is not our concern, leaving that to the caller. -+ */ -+static int consume_frames(struct dpaa2_eth_channel *ch) -+{ -+ struct dpaa2_eth_priv *priv = ch->priv; -+ struct dpaa2_eth_fq *fq; -+ struct dpaa2_dq *dq; -+ const struct dpaa2_fd *fd; -+ int cleaned = 0; -+ int is_last; -+ -+ do { -+ dq = dpaa2_io_store_next(ch->store, &is_last); -+ if (unlikely(!dq)) { -+ /* If we're here, we *must* have placed a -+ * volatile dequeue comnmand, so keep reading through -+ * the store until we get some sort of valid response -+ * token (either a valid frame or an "empty dequeue") -+ */ -+ continue; -+ } -+ -+ fd = dpaa2_dq_fd(dq); -+ fq = (struct dpaa2_eth_fq *)dpaa2_dq_fqd_ctx(dq); -+ fq->stats.frames++; -+ -+ fq->consume(priv, ch, fd, &ch->napi); -+ cleaned++; -+ } while (!is_last); -+ -+ return cleaned; -+} -+ -+/* Configure the egress frame annotation for timestamp update */ -+static void enable_tx_tstamp(struct dpaa2_fd *fd, void *hwa_start) -+{ -+ struct dpaa2_faead *faead; -+ u32 ctrl; -+ u32 frc; -+ -+ /* Mark the egress frame annotation area as valid */ -+ frc = dpaa2_fd_get_frc(fd); -+ dpaa2_fd_set_frc(fd, frc | DPAA2_FD_FRC_FAEADV); -+ -+ /* enable UPD (update prepanded data) bit in FAEAD field of -+ * hardware frame annotation area -+ */ -+ ctrl = DPAA2_FAEAD_A2V | DPAA2_FAEAD_UPDV | DPAA2_FAEAD_UPD; -+ faead = hwa_start + DPAA2_FAEAD_OFFSET; -+ faead->ctrl = cpu_to_le32(ctrl); -+} -+ -+/* Create a frame descriptor based on a fragmented skb */ -+static int build_sg_fd(struct dpaa2_eth_priv *priv, -+ struct sk_buff *skb, -+ struct dpaa2_fd *fd) -+{ -+ struct device *dev = priv->net_dev->dev.parent; -+ void *sgt_buf = NULL; -+ void *hwa; -+ dma_addr_t addr; -+ int nr_frags = skb_shinfo(skb)->nr_frags; -+ struct dpaa2_sg_entry *sgt; -+ int i, j, err; -+ int sgt_buf_size; -+ struct scatterlist *scl, *crt_scl; -+ int num_sg; -+ int num_dma_bufs; -+ struct dpaa2_eth_swa *swa; -+ -+ /* Create and map scatterlist. -+ * We don't advertise NETIF_F_FRAGLIST, so skb_to_sgvec() will not have -+ * to go beyond nr_frags+1. -+ * Note: We don't support chained scatterlists -+ */ -+ if (unlikely(PAGE_SIZE / sizeof(struct scatterlist) < nr_frags + 1)) -+ return -EINVAL; -+ -+ scl = kcalloc(nr_frags + 1, sizeof(struct scatterlist), GFP_ATOMIC); -+ if (unlikely(!scl)) -+ return -ENOMEM; -+ -+ sg_init_table(scl, nr_frags + 1); -+ num_sg = skb_to_sgvec(skb, scl, 0, skb->len); -+ num_dma_bufs = dma_map_sg(dev, scl, num_sg, DMA_TO_DEVICE); -+ if (unlikely(!num_dma_bufs)) { -+ err = -ENOMEM; -+ goto dma_map_sg_failed; -+ } -+ -+ /* Prepare the HW SGT structure */ -+ sgt_buf_size = priv->tx_data_offset + -+ sizeof(struct dpaa2_sg_entry) * (1 + num_dma_bufs); -+ sgt_buf = kzalloc(sgt_buf_size + DPAA2_ETH_TX_BUF_ALIGN, GFP_ATOMIC); -+ if (unlikely(!sgt_buf)) { -+ err = -ENOMEM; -+ goto sgt_buf_alloc_failed; -+ } -+ sgt_buf = PTR_ALIGN(sgt_buf, DPAA2_ETH_TX_BUF_ALIGN); -+ -+ /* PTA from egress side is passed as is to the confirmation side so -+ * we need to clear some fields here in order to find consistent values -+ * on TX confirmation. We are clearing FAS (Frame Annotation Status) -+ * field here. -+ */ -+ hwa = sgt_buf + priv->buf_layout.private_data_size; -+ memset(hwa, 0, 8); -+ -+ sgt = (struct dpaa2_sg_entry *)(sgt_buf + priv->tx_data_offset); -+ -+ /* Fill in the HW SGT structure. -+ * -+ * sgt_buf is zeroed out, so the following fields are implicit -+ * in all sgt entries: -+ * - offset is 0 -+ * - format is 'dpaa2_sg_single' -+ */ -+ for_each_sg(scl, crt_scl, num_dma_bufs, i) { -+ dpaa2_sg_set_addr(&sgt[i], sg_dma_address(crt_scl)); -+ dpaa2_sg_set_len(&sgt[i], sg_dma_len(crt_scl)); -+ } -+ dpaa2_sg_set_final(&sgt[i - 1], true); -+ -+ /* Store the skb backpointer in the SGT buffer. -+ * Fit the scatterlist and the number of buffers alongside the -+ * skb backpointer in the SWA. We'll need all of them on Tx Conf. -+ */ -+ swa = (struct dpaa2_eth_swa *)sgt_buf; -+ swa->skb = skb; -+ swa->scl = scl; -+ swa->num_sg = num_sg; -+ swa->num_dma_bufs = num_dma_bufs; -+ -+ /* Hardware expects the SG table to be in little endian format */ -+ for (j = 0; j < i; j++) -+ dpaa2_sg_cpu_to_le(&sgt[j]); -+ -+ /* Separately map the SGT buffer */ -+ addr = dma_map_single(dev, sgt_buf, sgt_buf_size, DMA_TO_DEVICE); -+ if (unlikely(dma_mapping_error(dev, addr))) { -+ err = -ENOMEM; -+ goto dma_map_single_failed; -+ } -+ dpaa2_fd_set_offset(fd, priv->tx_data_offset); -+ dpaa2_fd_set_format(fd, dpaa2_fd_sg); -+ dpaa2_fd_set_addr(fd, addr); -+ dpaa2_fd_set_len(fd, skb->len); -+ -+ fd->simple.ctrl = DPAA2_FD_CTRL_ASAL | DPAA2_FD_CTRL_PTA | -+ DPAA2_FD_CTRL_PTV1; -+ -+ if (priv->ts_tx_en && skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) -+ enable_tx_tstamp(fd, hwa); -+ -+ return 0; -+ -+dma_map_single_failed: -+ kfree(sgt_buf); -+sgt_buf_alloc_failed: -+ dma_unmap_sg(dev, scl, num_sg, DMA_TO_DEVICE); -+dma_map_sg_failed: -+ kfree(scl); -+ return err; -+} -+ -+/* Create a frame descriptor based on a linear skb */ -+static int build_single_fd(struct dpaa2_eth_priv *priv, -+ struct sk_buff *skb, -+ struct dpaa2_fd *fd) -+{ -+ struct device *dev = priv->net_dev->dev.parent; -+ u8 *buffer_start; -+ struct sk_buff **skbh; -+ dma_addr_t addr; -+ void *hwa; -+ -+ buffer_start = PTR_ALIGN(skb->data - priv->tx_data_offset - -+ DPAA2_ETH_TX_BUF_ALIGN, -+ DPAA2_ETH_TX_BUF_ALIGN); -+ -+ /* PTA from egress side is passed as is to the confirmation side so -+ * we need to clear some fields here in order to find consistent values -+ * on TX confirmation. We are clearing FAS (Frame Annotation Status) -+ * field here -+ */ -+ hwa = buffer_start + priv->buf_layout.private_data_size; -+ memset(hwa, 0, 8); -+ -+ /* Store a backpointer to the skb at the beginning of the buffer -+ * (in the private data area) such that we can release it -+ * on Tx confirm -+ */ -+ skbh = (struct sk_buff **)buffer_start; -+ *skbh = skb; -+ -+ addr = dma_map_single(dev, buffer_start, -+ skb_tail_pointer(skb) - buffer_start, -+ DMA_TO_DEVICE); -+ if (unlikely(dma_mapping_error(dev, addr))) -+ return -ENOMEM; -+ -+ dpaa2_fd_set_addr(fd, addr); -+ dpaa2_fd_set_offset(fd, (u16)(skb->data - buffer_start)); -+ dpaa2_fd_set_len(fd, skb->len); -+ dpaa2_fd_set_format(fd, dpaa2_fd_single); -+ -+ fd->simple.ctrl = DPAA2_FD_CTRL_ASAL | DPAA2_FD_CTRL_PTA | -+ DPAA2_FD_CTRL_PTV1; -+ -+ if (priv->ts_tx_en && skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) -+ enable_tx_tstamp(fd, hwa); -+ -+ return 0; -+} -+ -+/* FD freeing routine on the Tx path -+ * -+ * DMA-unmap and free FD and possibly SGT buffer allocated on Tx. The skb -+ * back-pointed to is also freed. -+ * This can be called either from dpaa2_eth_tx_conf() or on the error path of -+ * dpaa2_eth_tx(). -+ * Optionally, return the frame annotation status word (FAS), which needs -+ * to be checked if we're on the confirmation path. -+ */ -+static void free_tx_fd(const struct dpaa2_eth_priv *priv, -+ const struct dpaa2_fd *fd, -+ u32 *status) -+{ -+ struct device *dev = priv->net_dev->dev.parent; -+ dma_addr_t fd_addr; -+ struct sk_buff **skbh, *skb; -+ unsigned char *buffer_start; -+ int unmap_size; -+ struct scatterlist *scl; -+ int num_sg, num_dma_bufs; -+ struct dpaa2_eth_swa *swa; -+ bool fd_single; -+ struct dpaa2_fas *fas; -+ -+ fd_addr = dpaa2_fd_get_addr(fd); -+ skbh = phys_to_virt(fd_addr); -+ fd_single = (dpaa2_fd_get_format(fd) == dpaa2_fd_single); -+ -+ if (fd_single) { -+ skb = *skbh; -+ buffer_start = (unsigned char *)skbh; -+ /* Accessing the skb buffer is safe before dma unmap, because -+ * we didn't map the actual skb shell. -+ */ -+ dma_unmap_single(dev, fd_addr, -+ skb_tail_pointer(skb) - buffer_start, -+ DMA_TO_DEVICE); -+ } else { -+ swa = (struct dpaa2_eth_swa *)skbh; -+ skb = swa->skb; -+ scl = swa->scl; -+ num_sg = swa->num_sg; -+ num_dma_bufs = swa->num_dma_bufs; -+ -+ /* Unmap the scatterlist */ -+ dma_unmap_sg(dev, scl, num_sg, DMA_TO_DEVICE); -+ kfree(scl); -+ -+ /* Unmap the SGT buffer */ -+ unmap_size = priv->tx_data_offset + -+ sizeof(struct dpaa2_sg_entry) * (1 + num_dma_bufs); -+ dma_unmap_single(dev, fd_addr, unmap_size, DMA_TO_DEVICE); -+ } -+ -+ /* Get the timestamp value */ -+ if (priv->ts_tx_en && skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) { -+ struct skb_shared_hwtstamps shhwtstamps; -+ u64 *ns; -+ -+ memset(&shhwtstamps, 0, sizeof(shhwtstamps)); -+ -+ ns = (u64 *)((void *)skbh + -+ priv->buf_layout.private_data_size + -+ sizeof(struct dpaa2_fas)); -+ *ns = DPAA2_PTP_NOMINAL_FREQ_PERIOD_NS * le64_to_cpup(ns); -+ shhwtstamps.hwtstamp = ns_to_ktime(*ns); -+ skb_tstamp_tx(skb, &shhwtstamps); -+ } -+ -+ /* Read the status from the Frame Annotation after we unmap the first -+ * buffer but before we free it. The caller function is responsible -+ * for checking the status value. -+ */ -+ if (status && (fd->simple.frc & DPAA2_FD_FRC_FASV)) { -+ fas = (struct dpaa2_fas *) -+ ((void *)skbh + priv->buf_layout.private_data_size); -+ *status = le32_to_cpu(fas->status); -+ } -+ -+ /* Free SGT buffer kmalloc'ed on tx */ -+ if (!fd_single) -+ kfree(skbh); -+ -+ /* Move on with skb release */ -+ dev_kfree_skb(skb); -+} -+ -+static int dpaa2_eth_tx(struct sk_buff *skb, struct net_device *net_dev) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ struct dpaa2_fd fd; -+ struct rtnl_link_stats64 *percpu_stats; -+ struct dpaa2_eth_drv_stats *percpu_extras; -+ u16 queue_mapping, flow_id; -+ int err, i; -+ -+ percpu_stats = this_cpu_ptr(priv->percpu_stats); -+ percpu_extras = this_cpu_ptr(priv->percpu_extras); -+ -+ if (unlikely(skb_headroom(skb) < DPAA2_ETH_NEEDED_HEADROOM(priv))) { -+ struct sk_buff *ns; -+ -+ ns = skb_realloc_headroom(skb, DPAA2_ETH_NEEDED_HEADROOM(priv)); -+ if (unlikely(!ns)) { -+ percpu_stats->tx_dropped++; -+ goto err_alloc_headroom; -+ } -+ dev_kfree_skb(skb); -+ skb = ns; -+ } -+ -+ /* We'll be holding a back-reference to the skb until Tx Confirmation; -+ * we don't want that overwritten by a concurrent Tx with a cloned skb. -+ */ -+ skb = skb_unshare(skb, GFP_ATOMIC); -+ if (unlikely(!skb)) { -+ /* skb_unshare() has already freed the skb */ -+ percpu_stats->tx_dropped++; -+ return NETDEV_TX_OK; -+ } -+ -+ /* Setup the FD fields */ -+ memset(&fd, 0, sizeof(fd)); -+ -+ if (skb_is_nonlinear(skb)) { -+ err = build_sg_fd(priv, skb, &fd); -+ percpu_extras->tx_sg_frames++; -+ percpu_extras->tx_sg_bytes += skb->len; -+ } else { -+ err = build_single_fd(priv, skb, &fd); -+ } -+ -+ if (unlikely(err)) { -+ percpu_stats->tx_dropped++; -+ goto err_build_fd; -+ } -+ -+ /* Tracing point */ -+ trace_dpaa2_tx_fd(net_dev, &fd); -+ -+ /* TxConf FQ selection primarily based on cpu affinity; this is -+ * non-migratable context, so it's safe to call smp_processor_id(). -+ */ -+ queue_mapping = smp_processor_id() % priv->dpni_attrs.max_senders; -+ flow_id = priv->fq[queue_mapping].flowid; -+ for (i = 0; i < (DPAA2_ETH_MAX_TX_QUEUES << 1); i++) { -+ err = dpaa2_io_service_enqueue_qd(NULL, priv->tx_qdid, 0, -+ flow_id, &fd); -+ if (err != -EBUSY) -+ break; -+ } -+ percpu_extras->tx_portal_busy += i; -+ if (unlikely(err < 0)) { -+ percpu_stats->tx_errors++; -+ /* Clean up everything, including freeing the skb */ -+ free_tx_fd(priv, &fd, NULL); -+ } else { -+ percpu_stats->tx_packets++; -+ percpu_stats->tx_bytes += skb->len; -+ } -+ -+ return NETDEV_TX_OK; -+ -+err_build_fd: -+err_alloc_headroom: -+ dev_kfree_skb(skb); -+ -+ return NETDEV_TX_OK; -+} -+ -+/* Tx confirmation frame processing routine */ -+static void dpaa2_eth_tx_conf(struct dpaa2_eth_priv *priv, -+ struct dpaa2_eth_channel *ch, -+ const struct dpaa2_fd *fd, -+ struct napi_struct *napi __always_unused) -+{ -+ struct rtnl_link_stats64 *percpu_stats; -+ struct dpaa2_eth_drv_stats *percpu_extras; -+ u32 status = 0; -+ -+ /* Tracing point */ -+ trace_dpaa2_tx_conf_fd(priv->net_dev, fd); -+ -+ percpu_extras = this_cpu_ptr(priv->percpu_extras); -+ percpu_extras->tx_conf_frames++; -+ percpu_extras->tx_conf_bytes += dpaa2_fd_get_len(fd); -+ -+ free_tx_fd(priv, fd, &status); -+ -+ if (unlikely(status & DPAA2_ETH_TXCONF_ERR_MASK)) { -+ percpu_stats = this_cpu_ptr(priv->percpu_stats); -+ /* Tx-conf logically pertains to the egress path. */ -+ percpu_stats->tx_errors++; -+ } -+} -+ -+static int set_rx_csum(struct dpaa2_eth_priv *priv, bool enable) -+{ -+ int err; -+ -+ err = dpni_set_l3_chksum_validation(priv->mc_io, 0, priv->mc_token, -+ enable); -+ if (err) { -+ netdev_err(priv->net_dev, -+ "dpni_set_l3_chksum_validation() failed\n"); -+ return err; -+ } -+ -+ err = dpni_set_l4_chksum_validation(priv->mc_io, 0, priv->mc_token, -+ enable); -+ if (err) { -+ netdev_err(priv->net_dev, -+ "dpni_set_l4_chksum_validation failed\n"); -+ return err; -+ } -+ -+ return 0; -+} -+ -+static int set_tx_csum(struct dpaa2_eth_priv *priv, bool enable) -+{ -+ struct dpaa2_eth_fq *fq; -+ struct dpni_tx_flow_cfg tx_flow_cfg; -+ int err; -+ int i; -+ -+ memset(&tx_flow_cfg, 0, sizeof(tx_flow_cfg)); -+ tx_flow_cfg.options = DPNI_TX_FLOW_OPT_L3_CHKSUM_GEN | -+ DPNI_TX_FLOW_OPT_L4_CHKSUM_GEN; -+ tx_flow_cfg.l3_chksum_gen = enable; -+ tx_flow_cfg.l4_chksum_gen = enable; -+ -+ for (i = 0; i < priv->num_fqs; i++) { -+ fq = &priv->fq[i]; -+ if (fq->type != DPAA2_TX_CONF_FQ) -+ continue; -+ -+ /* The Tx flowid is kept in the corresponding TxConf FQ. */ -+ err = dpni_set_tx_flow(priv->mc_io, 0, priv->mc_token, -+ &fq->flowid, &tx_flow_cfg); -+ if (err) { -+ netdev_err(priv->net_dev, "dpni_set_tx_flow failed\n"); -+ return err; -+ } -+ } -+ -+ return 0; -+} -+ -+/* Perform a single release command to add buffers -+ * to the specified buffer pool -+ */ -+static int add_bufs(struct dpaa2_eth_priv *priv, u16 bpid) -+{ -+ struct device *dev = priv->net_dev->dev.parent; -+ u64 buf_array[DPAA2_ETH_BUFS_PER_CMD]; -+ void *buf; -+ dma_addr_t addr; -+ int i; -+ -+ for (i = 0; i < DPAA2_ETH_BUFS_PER_CMD; i++) { -+ /* Allocate buffer visible to WRIOP + skb shared info + -+ * alignment padding -+ */ -+ buf = netdev_alloc_frag(DPAA2_ETH_BUF_RAW_SIZE); -+ if (unlikely(!buf)) -+ goto err_alloc; -+ -+ buf = PTR_ALIGN(buf, DPAA2_ETH_RX_BUF_ALIGN); -+ -+ addr = dma_map_single(dev, buf, DPAA2_ETH_RX_BUF_SIZE, -+ DMA_FROM_DEVICE); -+ if (unlikely(dma_mapping_error(dev, addr))) -+ goto err_map; -+ -+ buf_array[i] = addr; -+ -+ /* tracing point */ -+ trace_dpaa2_eth_buf_seed(priv->net_dev, -+ buf, DPAA2_ETH_BUF_RAW_SIZE, -+ addr, DPAA2_ETH_RX_BUF_SIZE, -+ bpid); -+ } -+ -+release_bufs: -+ /* In case the portal is busy, retry until successful. -+ * The buffer release function would only fail if the QBMan portal -+ * was busy, which implies portal contention (i.e. more CPUs than -+ * portals, i.e. GPPs w/o affine DPIOs). For all practical purposes, -+ * there is little we can realistically do, short of giving up - -+ * in which case we'd risk depleting the buffer pool and never again -+ * receiving the Rx interrupt which would kick-start the refill logic. -+ * So just keep retrying, at the risk of being moved to ksoftirqd. -+ */ -+ while (dpaa2_io_service_release(NULL, bpid, buf_array, i)) -+ cpu_relax(); -+ return i; -+ -+err_map: -+ put_page(virt_to_head_page(buf)); -+err_alloc: -+ if (i) -+ goto release_bufs; -+ -+ return 0; -+} -+ -+static int seed_pool(struct dpaa2_eth_priv *priv, u16 bpid) -+{ -+ int i, j; -+ int new_count; -+ -+ /* This is the lazy seeding of Rx buffer pools. -+ * dpaa2_add_bufs() is also used on the Rx hotpath and calls -+ * napi_alloc_frag(). The trouble with that is that it in turn ends up -+ * calling this_cpu_ptr(), which mandates execution in atomic context. -+ * Rather than splitting up the code, do a one-off preempt disable. -+ */ -+ preempt_disable(); -+ for (j = 0; j < priv->num_channels; j++) { -+ for (i = 0; i < DPAA2_ETH_NUM_BUFS; -+ i += DPAA2_ETH_BUFS_PER_CMD) { -+ new_count = add_bufs(priv, bpid); -+ priv->channel[j]->buf_count += new_count; -+ -+ if (new_count < DPAA2_ETH_BUFS_PER_CMD) { -+ preempt_enable(); -+ return -ENOMEM; -+ } -+ } -+ } -+ preempt_enable(); -+ -+ return 0; -+} -+ -+/** -+ * Drain the specified number of buffers from the DPNI's private buffer pool. -+ * @count must not exceeed DPAA2_ETH_BUFS_PER_CMD -+ */ -+static void drain_bufs(struct dpaa2_eth_priv *priv, int count) -+{ -+ struct device *dev = priv->net_dev->dev.parent; -+ u64 buf_array[DPAA2_ETH_BUFS_PER_CMD]; -+ void *vaddr; -+ int ret, i; -+ -+ do { -+ ret = dpaa2_io_service_acquire(NULL, priv->dpbp_attrs.bpid, -+ buf_array, count); -+ if (ret < 0) { -+ netdev_err(priv->net_dev, "dpaa2_io_service_acquire() failed\n"); -+ return; -+ } -+ for (i = 0; i < ret; i++) { -+ /* Same logic as on regular Rx path */ -+ dma_unmap_single(dev, buf_array[i], -+ DPAA2_ETH_RX_BUF_SIZE, -+ DMA_FROM_DEVICE); -+ vaddr = phys_to_virt(buf_array[i]); -+ put_page(virt_to_head_page(vaddr)); -+ } -+ } while (ret); -+} -+ -+static void drain_pool(struct dpaa2_eth_priv *priv) -+{ -+ int i; -+ -+ drain_bufs(priv, DPAA2_ETH_BUFS_PER_CMD); -+ drain_bufs(priv, 1); -+ -+ for (i = 0; i < priv->num_channels; i++) -+ priv->channel[i]->buf_count = 0; -+} -+ -+/* Function is called from softirq context only, so we don't need to guard -+ * the access to percpu count -+ */ -+static int refill_pool(struct dpaa2_eth_priv *priv, -+ struct dpaa2_eth_channel *ch, -+ u16 bpid) -+{ -+ int new_count; -+ -+ if (likely(ch->buf_count >= DPAA2_ETH_REFILL_THRESH)) -+ return 0; -+ -+ do { -+ new_count = add_bufs(priv, bpid); -+ if (unlikely(!new_count)) { -+ /* Out of memory; abort for now, we'll try later on */ -+ break; -+ } -+ ch->buf_count += new_count; -+ } while (ch->buf_count < DPAA2_ETH_NUM_BUFS); -+ -+ if (unlikely(ch->buf_count < DPAA2_ETH_NUM_BUFS)) -+ return -ENOMEM; -+ -+ return 0; -+} -+ -+static int pull_channel(struct dpaa2_eth_channel *ch) -+{ -+ int err; -+ int dequeues = -1; -+ -+ /* Retry while portal is busy */ -+ do { -+ err = dpaa2_io_service_pull_channel(NULL, ch->ch_id, ch->store); -+ dequeues++; -+ cpu_relax(); -+ } while (err == -EBUSY); -+ -+ ch->stats.dequeue_portal_busy += dequeues; -+ if (unlikely(err)) -+ ch->stats.pull_err++; -+ -+ return err; -+} -+ -+/* NAPI poll routine -+ * -+ * Frames are dequeued from the QMan channel associated with this NAPI context. -+ * Rx, Tx confirmation and (if configured) Rx error frames all count -+ * towards the NAPI budget. -+ */ -+static int dpaa2_eth_poll(struct napi_struct *napi, int budget) -+{ -+ struct dpaa2_eth_channel *ch; -+ int cleaned = 0, store_cleaned; -+ struct dpaa2_eth_priv *priv; -+ int err; -+ -+ ch = container_of(napi, struct dpaa2_eth_channel, napi); -+ priv = ch->priv; -+ -+ while (cleaned < budget) { -+ err = pull_channel(ch); -+ if (unlikely(err)) -+ break; -+ -+ /* Refill pool if appropriate */ -+ refill_pool(priv, ch, priv->dpbp_attrs.bpid); -+ -+ store_cleaned = consume_frames(ch); -+ cleaned += store_cleaned; -+ -+ /* If we have enough budget left for a full store, -+ * try a new pull dequeue, otherwise we're done here -+ */ -+ if (store_cleaned == 0 || -+ cleaned > budget - DPAA2_ETH_STORE_SIZE) -+ break; -+ } -+ -+ if (cleaned < budget) { -+ napi_complete(napi); -+ /* Re-enable data available notifications */ -+ do { -+ err = dpaa2_io_service_rearm(NULL, &ch->nctx); -+ cpu_relax(); -+ } while (err == -EBUSY); -+ } -+ -+ ch->stats.frames += cleaned; -+ -+ return cleaned; -+} -+ -+static void enable_ch_napi(struct dpaa2_eth_priv *priv) -+{ -+ struct dpaa2_eth_channel *ch; -+ int i; -+ -+ for (i = 0; i < priv->num_channels; i++) { -+ ch = priv->channel[i]; -+ napi_enable(&ch->napi); -+ } -+} -+ -+static void disable_ch_napi(struct dpaa2_eth_priv *priv) -+{ -+ struct dpaa2_eth_channel *ch; -+ int i; -+ -+ for (i = 0; i < priv->num_channels; i++) { -+ ch = priv->channel[i]; -+ napi_disable(&ch->napi); -+ } -+} -+ -+static int link_state_update(struct dpaa2_eth_priv *priv) -+{ -+ struct dpni_link_state state; -+ int err; -+ -+ err = dpni_get_link_state(priv->mc_io, 0, priv->mc_token, &state); -+ if (unlikely(err)) { -+ netdev_err(priv->net_dev, -+ "dpni_get_link_state() failed\n"); -+ return err; -+ } -+ -+ /* Chech link state; speed / duplex changes are not treated yet */ -+ if (priv->link_state.up == state.up) -+ return 0; -+ -+ priv->link_state = state; -+ if (state.up) { -+ netif_carrier_on(priv->net_dev); -+ netif_tx_start_all_queues(priv->net_dev); -+ } else { -+ netif_tx_stop_all_queues(priv->net_dev); -+ netif_carrier_off(priv->net_dev); -+ } -+ -+ netdev_info(priv->net_dev, "Link Event: state %s", -+ state.up ? "up" : "down"); -+ -+ return 0; -+} -+ -+static int dpaa2_eth_open(struct net_device *net_dev) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ int err; -+ -+ err = seed_pool(priv, priv->dpbp_attrs.bpid); -+ if (err) { -+ /* Not much to do; the buffer pool, though not filled up, -+ * may still contain some buffers which would enable us -+ * to limp on. -+ */ -+ netdev_err(net_dev, "Buffer seeding failed for DPBP %d (bpid=%d)\n", -+ priv->dpbp_dev->obj_desc.id, priv->dpbp_attrs.bpid); -+ } -+ -+ /* We'll only start the txqs when the link is actually ready; make sure -+ * we don't race against the link up notification, which may come -+ * immediately after dpni_enable(); -+ */ -+ netif_tx_stop_all_queues(net_dev); -+ enable_ch_napi(priv); -+ /* Also, explicitly set carrier off, otherwise netif_carrier_ok() will -+ * return true and cause 'ip link show' to report the LOWER_UP flag, -+ * even though the link notification wasn't even received. -+ */ -+ netif_carrier_off(net_dev); -+ -+ err = dpni_enable(priv->mc_io, 0, priv->mc_token); -+ if (err < 0) { -+ netdev_err(net_dev, "dpni_enable() failed\n"); -+ goto enable_err; -+ } -+ -+ /* If the DPMAC object has already processed the link up interrupt, -+ * we have to learn the link state ourselves. -+ */ -+ err = link_state_update(priv); -+ if (err < 0) { -+ netdev_err(net_dev, "Can't update link state\n"); -+ goto link_state_err; -+ } -+ -+ return 0; -+ -+link_state_err: -+enable_err: -+ disable_ch_napi(priv); -+ drain_pool(priv); -+ return err; -+} -+ -+/* The DPIO store must be empty when we call this, -+ * at the end of every NAPI cycle. -+ */ -+static u32 drain_channel(struct dpaa2_eth_priv *priv, -+ struct dpaa2_eth_channel *ch) -+{ -+ u32 drained = 0, total = 0; -+ -+ do { -+ pull_channel(ch); -+ drained = consume_frames(ch); -+ total += drained; -+ } while (drained); -+ -+ return total; -+} -+ -+static u32 drain_ingress_frames(struct dpaa2_eth_priv *priv) -+{ -+ struct dpaa2_eth_channel *ch; -+ int i; -+ u32 drained = 0; -+ -+ for (i = 0; i < priv->num_channels; i++) { -+ ch = priv->channel[i]; -+ drained += drain_channel(priv, ch); -+ } -+ -+ return drained; -+} -+ -+static int dpaa2_eth_stop(struct net_device *net_dev) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ int dpni_enabled; -+ int retries = 10; -+ u32 drained; -+ -+ netif_tx_stop_all_queues(net_dev); -+ netif_carrier_off(net_dev); -+ -+ /* Loop while dpni_disable() attempts to drain the egress FQs -+ * and confirm them back to us. -+ */ -+ do { -+ dpni_disable(priv->mc_io, 0, priv->mc_token); -+ dpni_is_enabled(priv->mc_io, 0, priv->mc_token, &dpni_enabled); -+ if (dpni_enabled) -+ /* Allow the MC some slack */ -+ msleep(100); -+ } while (dpni_enabled && --retries); -+ if (!retries) { -+ netdev_warn(net_dev, "Retry count exceeded disabling DPNI\n"); -+ /* Must go on and disable NAPI nonetheless, so we don't crash at -+ * the next "ifconfig up" -+ */ -+ } -+ -+ /* Wait for NAPI to complete on every core and disable it. -+ * In particular, this will also prevent NAPI from being rescheduled if -+ * a new CDAN is serviced, effectively discarding the CDAN. We therefore -+ * don't even need to disarm the channels, except perhaps for the case -+ * of a huge coalescing value. -+ */ -+ disable_ch_napi(priv); -+ -+ /* Manually drain the Rx and TxConf queues */ -+ drained = drain_ingress_frames(priv); -+ if (drained) -+ netdev_dbg(net_dev, "Drained %d frames.\n", drained); -+ -+ /* Empty the buffer pool */ -+ drain_pool(priv); -+ -+ return 0; -+} -+ -+static int dpaa2_eth_init(struct net_device *net_dev) -+{ -+ u64 supported = 0; -+ u64 not_supported = 0; -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ u32 options = priv->dpni_attrs.options; -+ -+ /* Capabilities listing */ -+ supported |= IFF_LIVE_ADDR_CHANGE | IFF_PROMISC | IFF_ALLMULTI; -+ -+ if (options & DPNI_OPT_UNICAST_FILTER) -+ supported |= IFF_UNICAST_FLT; -+ else -+ not_supported |= IFF_UNICAST_FLT; -+ -+ if (options & DPNI_OPT_MULTICAST_FILTER) -+ supported |= IFF_MULTICAST; -+ else -+ not_supported |= IFF_MULTICAST; -+ -+ net_dev->priv_flags |= supported; -+ net_dev->priv_flags &= ~not_supported; -+ -+ /* Features */ -+ net_dev->features = NETIF_F_RXCSUM | -+ NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | -+ NETIF_F_SG | NETIF_F_HIGHDMA | -+ NETIF_F_LLTX; -+ net_dev->hw_features = net_dev->features; -+ -+ return 0; -+} -+ -+static int dpaa2_eth_set_addr(struct net_device *net_dev, void *addr) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ struct device *dev = net_dev->dev.parent; -+ int err; -+ -+ err = eth_mac_addr(net_dev, addr); -+ if (err < 0) { -+ dev_err(dev, "eth_mac_addr() failed with error %d\n", err); -+ return err; -+ } -+ -+ err = dpni_set_primary_mac_addr(priv->mc_io, 0, priv->mc_token, -+ net_dev->dev_addr); -+ if (err) { -+ dev_err(dev, "dpni_set_primary_mac_addr() failed (%d)\n", err); -+ return err; -+ } -+ -+ return 0; -+} -+ -+/** Fill in counters maintained by the GPP driver. These may be different from -+ * the hardware counters obtained by ethtool. -+ */ -+static struct rtnl_link_stats64 -+*dpaa2_eth_get_stats(struct net_device *net_dev, -+ struct rtnl_link_stats64 *stats) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ struct rtnl_link_stats64 *percpu_stats; -+ u64 *cpustats; -+ u64 *netstats = (u64 *)stats; -+ int i, j; -+ int num = sizeof(struct rtnl_link_stats64) / sizeof(u64); -+ -+ for_each_possible_cpu(i) { -+ percpu_stats = per_cpu_ptr(priv->percpu_stats, i); -+ cpustats = (u64 *)percpu_stats; -+ for (j = 0; j < num; j++) -+ netstats[j] += cpustats[j]; -+ } -+ -+ return stats; -+} -+ -+static int dpaa2_eth_change_mtu(struct net_device *net_dev, int mtu) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ int err; -+ -+ if (mtu < 68 || mtu > DPAA2_ETH_MAX_MTU) { -+ netdev_err(net_dev, "Invalid MTU %d. Valid range is: 68..%d\n", -+ mtu, DPAA2_ETH_MAX_MTU); -+ return -EINVAL; -+ } -+ -+ /* Set the maximum Rx frame length to match the transmit side; -+ * account for L2 headers when computing the MFL -+ */ -+ err = dpni_set_max_frame_length(priv->mc_io, 0, priv->mc_token, -+ (u16)DPAA2_ETH_L2_MAX_FRM(mtu)); -+ if (err) { -+ netdev_err(net_dev, "dpni_set_max_frame_length() failed\n"); -+ return err; -+ } -+ -+ net_dev->mtu = mtu; -+ return 0; -+} -+ -+/* Copy mac unicast addresses from @net_dev to @priv. -+ * Its sole purpose is to make dpaa2_eth_set_rx_mode() more readable. -+ */ -+static void add_uc_hw_addr(const struct net_device *net_dev, -+ struct dpaa2_eth_priv *priv) -+{ -+ struct netdev_hw_addr *ha; -+ int err; -+ -+ netdev_for_each_uc_addr(ha, net_dev) { -+ err = dpni_add_mac_addr(priv->mc_io, 0, priv->mc_token, -+ ha->addr); -+ if (err) -+ netdev_warn(priv->net_dev, -+ "Could not add ucast MAC %pM to the filtering table (err %d)\n", -+ ha->addr, err); -+ } -+} -+ -+/* Copy mac multicast addresses from @net_dev to @priv -+ * Its sole purpose is to make dpaa2_eth_set_rx_mode() more readable. -+ */ -+static void add_mc_hw_addr(const struct net_device *net_dev, -+ struct dpaa2_eth_priv *priv) -+{ -+ struct netdev_hw_addr *ha; -+ int err; -+ -+ netdev_for_each_mc_addr(ha, net_dev) { -+ err = dpni_add_mac_addr(priv->mc_io, 0, priv->mc_token, -+ ha->addr); -+ if (err) -+ netdev_warn(priv->net_dev, -+ "Could not add mcast MAC %pM to the filtering table (err %d)\n", -+ ha->addr, err); -+ } -+} -+ -+static void dpaa2_eth_set_rx_mode(struct net_device *net_dev) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ int uc_count = netdev_uc_count(net_dev); -+ int mc_count = netdev_mc_count(net_dev); -+ u8 max_uc = priv->dpni_attrs.max_unicast_filters; -+ u8 max_mc = priv->dpni_attrs.max_multicast_filters; -+ u32 options = priv->dpni_attrs.options; -+ u16 mc_token = priv->mc_token; -+ struct fsl_mc_io *mc_io = priv->mc_io; -+ int err; -+ -+ /* Basic sanity checks; these probably indicate a misconfiguration */ -+ if (!(options & DPNI_OPT_UNICAST_FILTER) && max_uc != 0) -+ netdev_info(net_dev, -+ "max_unicast_filters=%d, DPNI_OPT_UNICAST_FILTER option must be enabled\n", -+ max_uc); -+ if (!(options & DPNI_OPT_MULTICAST_FILTER) && max_mc != 0) -+ netdev_info(net_dev, -+ "max_multicast_filters=%d, DPNI_OPT_MULTICAST_FILTER option must be enabled\n", -+ max_mc); -+ -+ /* Force promiscuous if the uc or mc counts exceed our capabilities. */ -+ if (uc_count > max_uc) { -+ netdev_info(net_dev, -+ "Unicast addr count reached %d, max allowed is %d; forcing promisc\n", -+ uc_count, max_uc); -+ goto force_promisc; -+ } -+ if (mc_count > max_mc) { -+ netdev_info(net_dev, -+ "Multicast addr count reached %d, max allowed is %d; forcing promisc\n", -+ mc_count, max_mc); -+ goto force_mc_promisc; -+ } -+ -+ /* Adjust promisc settings due to flag combinations */ -+ if (net_dev->flags & IFF_PROMISC) -+ goto force_promisc; -+ if (net_dev->flags & IFF_ALLMULTI) { -+ /* First, rebuild unicast filtering table. This should be done -+ * in promisc mode, in order to avoid frame loss while we -+ * progressively add entries to the table. -+ * We don't know whether we had been in promisc already, and -+ * making an MC call to find out is expensive; so set uc promisc -+ * nonetheless. -+ */ -+ err = dpni_set_unicast_promisc(mc_io, 0, mc_token, 1); -+ if (err) -+ netdev_warn(net_dev, "Can't set uc promisc\n"); -+ -+ /* Actual uc table reconstruction. */ -+ err = dpni_clear_mac_filters(mc_io, 0, mc_token, 1, 0); -+ if (err) -+ netdev_warn(net_dev, "Can't clear uc filters\n"); -+ add_uc_hw_addr(net_dev, priv); -+ -+ /* Finally, clear uc promisc and set mc promisc as requested. */ -+ err = dpni_set_unicast_promisc(mc_io, 0, mc_token, 0); -+ if (err) -+ netdev_warn(net_dev, "Can't clear uc promisc\n"); -+ goto force_mc_promisc; -+ } -+ -+ /* Neither unicast, nor multicast promisc will be on... eventually. -+ * For now, rebuild mac filtering tables while forcing both of them on. -+ */ -+ err = dpni_set_unicast_promisc(mc_io, 0, mc_token, 1); -+ if (err) -+ netdev_warn(net_dev, "Can't set uc promisc (%d)\n", err); -+ err = dpni_set_multicast_promisc(mc_io, 0, mc_token, 1); -+ if (err) -+ netdev_warn(net_dev, "Can't set mc promisc (%d)\n", err); -+ -+ /* Actual mac filtering tables reconstruction */ -+ err = dpni_clear_mac_filters(mc_io, 0, mc_token, 1, 1); -+ if (err) -+ netdev_warn(net_dev, "Can't clear mac filters\n"); -+ add_mc_hw_addr(net_dev, priv); -+ add_uc_hw_addr(net_dev, priv); -+ -+ /* Now we can clear both ucast and mcast promisc, without risking -+ * to drop legitimate frames anymore. -+ */ -+ err = dpni_set_unicast_promisc(mc_io, 0, mc_token, 0); -+ if (err) -+ netdev_warn(net_dev, "Can't clear ucast promisc\n"); -+ err = dpni_set_multicast_promisc(mc_io, 0, mc_token, 0); -+ if (err) -+ netdev_warn(net_dev, "Can't clear mcast promisc\n"); -+ -+ return; -+ -+force_promisc: -+ err = dpni_set_unicast_promisc(mc_io, 0, mc_token, 1); -+ if (err) -+ netdev_warn(net_dev, "Can't set ucast promisc\n"); -+force_mc_promisc: -+ err = dpni_set_multicast_promisc(mc_io, 0, mc_token, 1); -+ if (err) -+ netdev_warn(net_dev, "Can't set mcast promisc\n"); -+} -+ -+static int dpaa2_eth_set_features(struct net_device *net_dev, -+ netdev_features_t features) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ netdev_features_t changed = features ^ net_dev->features; -+ bool enable; -+ int err; -+ -+ if (changed & NETIF_F_RXCSUM) { -+ enable = !!(features & NETIF_F_RXCSUM); -+ err = set_rx_csum(priv, enable); -+ if (err) -+ return err; -+ } -+ -+ if (changed & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) { -+ enable = !!(features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)); -+ err = set_tx_csum(priv, enable); -+ if (err) -+ return err; -+ } -+ -+ return 0; -+} -+ -+static int dpaa2_eth_ts_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(dev); -+ struct hwtstamp_config config; -+ -+ if (copy_from_user(&config, rq->ifr_data, sizeof(config))) -+ return -EFAULT; -+ -+ switch (config.tx_type) { -+ case HWTSTAMP_TX_OFF: -+ priv->ts_tx_en = false; -+ break; -+ case HWTSTAMP_TX_ON: -+ priv->ts_tx_en = true; -+ break; -+ default: -+ return -ERANGE; -+ } -+ -+ if (config.rx_filter == HWTSTAMP_FILTER_NONE) { -+ priv->ts_rx_en = false; -+ } else { -+ priv->ts_rx_en = true; -+ /* TS is set for all frame types, not only those requested */ -+ config.rx_filter = HWTSTAMP_FILTER_ALL; -+ } -+ -+ return copy_to_user(rq->ifr_data, &config, sizeof(config)) ? -+ -EFAULT : 0; -+} -+ -+static int dpaa2_eth_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -+{ -+ if (cmd == SIOCSHWTSTAMP) -+ return dpaa2_eth_ts_ioctl(dev, rq, cmd); -+ -+ return -EINVAL; -+} -+ -+static const struct net_device_ops dpaa2_eth_ops = { -+ .ndo_open = dpaa2_eth_open, -+ .ndo_start_xmit = dpaa2_eth_tx, -+ .ndo_stop = dpaa2_eth_stop, -+ .ndo_init = dpaa2_eth_init, -+ .ndo_set_mac_address = dpaa2_eth_set_addr, -+ .ndo_get_stats64 = dpaa2_eth_get_stats, -+ .ndo_change_mtu = dpaa2_eth_change_mtu, -+ .ndo_set_rx_mode = dpaa2_eth_set_rx_mode, -+ .ndo_set_features = dpaa2_eth_set_features, -+ .ndo_do_ioctl = dpaa2_eth_ioctl, -+}; -+ -+static void cdan_cb(struct dpaa2_io_notification_ctx *ctx) -+{ -+ struct dpaa2_eth_channel *ch; -+ -+ ch = container_of(ctx, struct dpaa2_eth_channel, nctx); -+ -+ /* Update NAPI statistics */ -+ ch->stats.cdan++; -+ -+ napi_schedule(&ch->napi); -+} -+ -+/* Allocate and configure a DPCON object */ -+static struct fsl_mc_device *setup_dpcon(struct dpaa2_eth_priv *priv) -+{ -+ struct fsl_mc_device *dpcon; -+ struct device *dev = priv->net_dev->dev.parent; -+ struct dpcon_attr attrs; -+ int err; -+ -+ err = fsl_mc_object_allocate(to_fsl_mc_device(dev), -+ FSL_MC_POOL_DPCON, &dpcon); -+ if (err) { -+ dev_info(dev, "Not enough DPCONs, will go on as-is\n"); -+ return NULL; -+ } -+ -+ err = dpcon_open(priv->mc_io, 0, dpcon->obj_desc.id, &dpcon->mc_handle); -+ if (err) { -+ dev_err(dev, "dpcon_open() failed\n"); -+ goto err_open; -+ } -+ -+ err = dpcon_reset(priv->mc_io, 0, dpcon->mc_handle); -+ if (err) { -+ dev_err(dev, "dpcon_reset() failed\n"); -+ goto err_reset; -+ } -+ -+ err = dpcon_get_attributes(priv->mc_io, 0, dpcon->mc_handle, &attrs); -+ if (err) { -+ dev_err(dev, "dpcon_get_attributes() failed\n"); -+ goto err_get_attr; -+ } -+ -+ err = dpcon_enable(priv->mc_io, 0, dpcon->mc_handle); -+ if (err) { -+ dev_err(dev, "dpcon_enable() failed\n"); -+ goto err_enable; -+ } -+ -+ return dpcon; -+ -+err_enable: -+err_get_attr: -+err_reset: -+ dpcon_close(priv->mc_io, 0, dpcon->mc_handle); -+err_open: -+ fsl_mc_object_free(dpcon); -+ -+ return NULL; -+} -+ -+static void free_dpcon(struct dpaa2_eth_priv *priv, -+ struct fsl_mc_device *dpcon) -+{ -+ dpcon_disable(priv->mc_io, 0, dpcon->mc_handle); -+ dpcon_close(priv->mc_io, 0, dpcon->mc_handle); -+ fsl_mc_object_free(dpcon); -+} -+ -+static struct dpaa2_eth_channel * -+alloc_channel(struct dpaa2_eth_priv *priv) -+{ -+ struct dpaa2_eth_channel *channel; -+ struct dpcon_attr attr; -+ struct device *dev = priv->net_dev->dev.parent; -+ int err; -+ -+ channel = kzalloc(sizeof(*channel), GFP_ATOMIC); -+ if (!channel) -+ return NULL; -+ -+ channel->dpcon = setup_dpcon(priv); -+ if (!channel->dpcon) -+ goto err_setup; -+ -+ err = dpcon_get_attributes(priv->mc_io, 0, channel->dpcon->mc_handle, -+ &attr); -+ if (err) { -+ dev_err(dev, "dpcon_get_attributes() failed\n"); -+ goto err_get_attr; -+ } -+ -+ channel->dpcon_id = attr.id; -+ channel->ch_id = attr.qbman_ch_id; -+ channel->priv = priv; -+ -+ return channel; -+ -+err_get_attr: -+ free_dpcon(priv, channel->dpcon); -+err_setup: -+ kfree(channel); -+ return NULL; -+} -+ -+static void free_channel(struct dpaa2_eth_priv *priv, -+ struct dpaa2_eth_channel *channel) -+{ -+ free_dpcon(priv, channel->dpcon); -+ kfree(channel); -+} -+ -+/* DPIO setup: allocate and configure QBMan channels, setup core affinity -+ * and register data availability notifications -+ */ -+static int setup_dpio(struct dpaa2_eth_priv *priv) -+{ -+ struct dpaa2_io_notification_ctx *nctx; -+ struct dpaa2_eth_channel *channel; -+ struct dpcon_notification_cfg dpcon_notif_cfg; -+ struct device *dev = priv->net_dev->dev.parent; -+ int i, err; -+ -+ /* Don't allocate more channels than strictly necessary and assign -+ * them to cores starting from the first one available in -+ * cpu_online_mask. -+ * If the number of channels is lower than the number of cores, -+ * there will be no rx/tx conf processing on the last cores in the mask. -+ */ -+ cpumask_clear(&priv->dpio_cpumask); -+ for_each_online_cpu(i) { -+ /* Try to allocate a channel */ -+ channel = alloc_channel(priv); -+ if (!channel) -+ goto err_alloc_ch; -+ -+ priv->channel[priv->num_channels] = channel; -+ -+ nctx = &channel->nctx; -+ nctx->is_cdan = 1; -+ nctx->cb = cdan_cb; -+ nctx->id = channel->ch_id; -+ nctx->desired_cpu = i; -+ -+ /* Register the new context */ -+ err = dpaa2_io_service_register(NULL, nctx); -+ if (err) { -+ dev_info(dev, "No affine DPIO for core %d\n", i); -+ /* This core doesn't have an affine DPIO, but there's -+ * a chance another one does, so keep trying -+ */ -+ free_channel(priv, channel); -+ continue; -+ } -+ -+ /* Register DPCON notification with MC */ -+ dpcon_notif_cfg.dpio_id = nctx->dpio_id; -+ dpcon_notif_cfg.priority = 0; -+ dpcon_notif_cfg.user_ctx = nctx->qman64; -+ err = dpcon_set_notification(priv->mc_io, 0, -+ channel->dpcon->mc_handle, -+ &dpcon_notif_cfg); -+ if (err) { -+ dev_err(dev, "dpcon_set_notification failed()\n"); -+ goto err_set_cdan; -+ } -+ -+ /* If we managed to allocate a channel and also found an affine -+ * DPIO for this core, add it to the final mask -+ */ -+ cpumask_set_cpu(i, &priv->dpio_cpumask); -+ priv->num_channels++; -+ -+ if (priv->num_channels == dpaa2_eth_max_channels(priv)) -+ break; -+ } -+ -+ /* Tx confirmation queues can only be serviced by cpus -+ * with an affine DPIO/channel -+ */ -+ cpumask_copy(&priv->txconf_cpumask, &priv->dpio_cpumask); -+ -+ return 0; -+ -+err_set_cdan: -+ dpaa2_io_service_deregister(NULL, nctx); -+ free_channel(priv, channel); -+err_alloc_ch: -+ if (cpumask_empty(&priv->dpio_cpumask)) { -+ dev_err(dev, "No cpu with an affine DPIO/DPCON\n"); -+ return -ENODEV; -+ } -+ cpumask_copy(&priv->txconf_cpumask, &priv->dpio_cpumask); -+ -+ return 0; -+} -+ -+static void free_dpio(struct dpaa2_eth_priv *priv) -+{ -+ int i; -+ struct dpaa2_eth_channel *ch; -+ -+ /* deregister CDAN notifications and free channels */ -+ for (i = 0; i < priv->num_channels; i++) { -+ ch = priv->channel[i]; -+ dpaa2_io_service_deregister(NULL, &ch->nctx); -+ free_channel(priv, ch); -+ } -+} -+ -+static struct dpaa2_eth_channel *get_affine_channel(struct dpaa2_eth_priv *priv, -+ int cpu) -+{ -+ struct device *dev = priv->net_dev->dev.parent; -+ int i; -+ -+ for (i = 0; i < priv->num_channels; i++) -+ if (priv->channel[i]->nctx.desired_cpu == cpu) -+ return priv->channel[i]; -+ -+ /* We should never get here. Issue a warning and return -+ * the first channel, because it's still better than nothing -+ */ -+ dev_warn(dev, "No affine channel found for cpu %d\n", cpu); -+ -+ return priv->channel[0]; -+} -+ -+static void set_fq_affinity(struct dpaa2_eth_priv *priv) -+{ -+ struct device *dev = priv->net_dev->dev.parent; -+ struct dpaa2_eth_fq *fq; -+ int rx_cpu, txc_cpu; -+ int i; -+ -+ /* For each FQ, pick one channel/CPU to deliver frames to. -+ * This may well change at runtime, either through irqbalance or -+ * through direct user intervention. -+ */ -+ rx_cpu = cpumask_first(&priv->dpio_cpumask); -+ txc_cpu = cpumask_first(&priv->txconf_cpumask); -+ -+ for (i = 0; i < priv->num_fqs; i++) { -+ fq = &priv->fq[i]; -+ switch (fq->type) { -+ case DPAA2_RX_FQ: -+ case DPAA2_RX_ERR_FQ: -+ fq->target_cpu = rx_cpu; -+ rx_cpu = cpumask_next(rx_cpu, &priv->dpio_cpumask); -+ if (rx_cpu >= nr_cpu_ids) -+ rx_cpu = cpumask_first(&priv->dpio_cpumask); -+ break; -+ case DPAA2_TX_CONF_FQ: -+ fq->target_cpu = txc_cpu; -+ txc_cpu = cpumask_next(txc_cpu, &priv->txconf_cpumask); -+ if (txc_cpu >= nr_cpu_ids) -+ txc_cpu = cpumask_first(&priv->txconf_cpumask); -+ break; -+ default: -+ dev_err(dev, "Unknown FQ type: %d\n", fq->type); -+ } -+ fq->channel = get_affine_channel(priv, fq->target_cpu); -+ } -+} -+ -+static void setup_fqs(struct dpaa2_eth_priv *priv) -+{ -+ int i; -+ -+ /* We have one TxConf FQ per Tx flow */ -+ for (i = 0; i < priv->dpni_attrs.max_senders; i++) { -+ priv->fq[priv->num_fqs].type = DPAA2_TX_CONF_FQ; -+ priv->fq[priv->num_fqs].consume = dpaa2_eth_tx_conf; -+ priv->fq[priv->num_fqs++].flowid = DPNI_NEW_FLOW_ID; -+ } -+ -+ /* The number of Rx queues (Rx distribution width) may be different from -+ * the number of cores. -+ * We only support one traffic class for now. -+ */ -+ for (i = 0; i < dpaa2_eth_queue_count(priv); i++) { -+ priv->fq[priv->num_fqs].type = DPAA2_RX_FQ; -+ priv->fq[priv->num_fqs].consume = dpaa2_eth_rx; -+ priv->fq[priv->num_fqs++].flowid = (u16)i; -+ } -+ -+#ifdef CONFIG_FSL_DPAA2_ETH_USE_ERR_QUEUE -+ /* We have exactly one Rx error queue per DPNI */ -+ priv->fq[priv->num_fqs].type = DPAA2_RX_ERR_FQ; -+ priv->fq[priv->num_fqs++].consume = dpaa2_eth_rx_err; -+#endif -+ -+ /* For each FQ, decide on which core to process incoming frames */ -+ set_fq_affinity(priv); -+} -+ -+/* Allocate and configure one buffer pool for each interface */ -+static int setup_dpbp(struct dpaa2_eth_priv *priv) -+{ -+ int err; -+ struct fsl_mc_device *dpbp_dev; -+ struct device *dev = priv->net_dev->dev.parent; -+ -+ err = fsl_mc_object_allocate(to_fsl_mc_device(dev), FSL_MC_POOL_DPBP, -+ &dpbp_dev); -+ if (err) { -+ dev_err(dev, "DPBP device allocation failed\n"); -+ return err; -+ } -+ -+ priv->dpbp_dev = dpbp_dev; -+ -+ err = dpbp_open(priv->mc_io, 0, priv->dpbp_dev->obj_desc.id, -+ &dpbp_dev->mc_handle); -+ if (err) { -+ dev_err(dev, "dpbp_open() failed\n"); -+ goto err_open; -+ } -+ -+ err = dpbp_reset(priv->mc_io, 0, dpbp_dev->mc_handle); -+ if (err) { -+ dev_err(dev, "dpbp_reset() failed\n"); -+ goto err_reset; -+ } -+ -+ err = dpbp_enable(priv->mc_io, 0, dpbp_dev->mc_handle); -+ if (err) { -+ dev_err(dev, "dpbp_enable() failed\n"); -+ goto err_enable; -+ } -+ -+ err = dpbp_get_attributes(priv->mc_io, 0, dpbp_dev->mc_handle, -+ &priv->dpbp_attrs); -+ if (err) { -+ dev_err(dev, "dpbp_get_attributes() failed\n"); -+ goto err_get_attr; -+ } -+ -+ return 0; -+ -+err_get_attr: -+ dpbp_disable(priv->mc_io, 0, dpbp_dev->mc_handle); -+err_enable: -+err_reset: -+ dpbp_close(priv->mc_io, 0, dpbp_dev->mc_handle); -+err_open: -+ fsl_mc_object_free(dpbp_dev); -+ -+ return err; -+} -+ -+static void free_dpbp(struct dpaa2_eth_priv *priv) -+{ -+ drain_pool(priv); -+ dpbp_disable(priv->mc_io, 0, priv->dpbp_dev->mc_handle); -+ dpbp_close(priv->mc_io, 0, priv->dpbp_dev->mc_handle); -+ fsl_mc_object_free(priv->dpbp_dev); -+} -+ -+/* Configure the DPNI object this interface is associated with */ -+static int setup_dpni(struct fsl_mc_device *ls_dev) -+{ -+ struct device *dev = &ls_dev->dev; -+ struct dpaa2_eth_priv *priv; -+ struct net_device *net_dev; -+ void *dma_mem; -+ int err; -+ -+ net_dev = dev_get_drvdata(dev); -+ priv = netdev_priv(net_dev); -+ -+ priv->dpni_id = ls_dev->obj_desc.id; -+ -+ /* get a handle for the DPNI object */ -+ err = dpni_open(priv->mc_io, 0, priv->dpni_id, &priv->mc_token); -+ if (err) { -+ dev_err(dev, "dpni_open() failed\n"); -+ goto err_open; -+ } -+ -+ ls_dev->mc_io = priv->mc_io; -+ ls_dev->mc_handle = priv->mc_token; -+ -+ err = dpni_reset(priv->mc_io, 0, priv->mc_token); -+ if (err) { -+ dev_err(dev, "dpni_reset() failed\n"); -+ goto err_reset; -+ } -+ -+ /* Map a memory region which will be used by MC to pass us an -+ * attribute structure -+ */ -+ dma_mem = kzalloc(DPAA2_EXT_CFG_SIZE, GFP_DMA | GFP_KERNEL); -+ if (!dma_mem) -+ goto err_alloc; -+ -+ priv->dpni_attrs.ext_cfg_iova = dma_map_single(dev, dma_mem, -+ DPAA2_EXT_CFG_SIZE, -+ DMA_FROM_DEVICE); -+ if (dma_mapping_error(dev, priv->dpni_attrs.ext_cfg_iova)) { -+ dev_err(dev, "dma mapping for dpni_ext_cfg failed\n"); -+ goto err_dma_map; -+ } -+ -+ err = dpni_get_attributes(priv->mc_io, 0, priv->mc_token, -+ &priv->dpni_attrs); -+ -+ /* We'll check the return code after unmapping, as we need to -+ * do this anyway -+ */ -+ dma_unmap_single(dev, priv->dpni_attrs.ext_cfg_iova, -+ DPAA2_EXT_CFG_SIZE, DMA_FROM_DEVICE); -+ -+ if (err) { -+ dev_err(dev, "dpni_get_attributes() failed (err=%d)\n", err); -+ goto err_get_attr; -+ } -+ -+ memset(&priv->dpni_ext_cfg, 0, sizeof(priv->dpni_ext_cfg)); -+ err = dpni_extract_extended_cfg(&priv->dpni_ext_cfg, dma_mem); -+ if (err) { -+ dev_err(dev, "dpni_extract_extended_cfg() failed\n"); -+ goto err_extract; -+ } -+ -+ /* Configure our buffers' layout */ -+ priv->buf_layout.options = DPNI_BUF_LAYOUT_OPT_PARSER_RESULT | -+ DPNI_BUF_LAYOUT_OPT_FRAME_STATUS | -+ DPNI_BUF_LAYOUT_OPT_PRIVATE_DATA_SIZE | -+ DPNI_BUF_LAYOUT_OPT_DATA_ALIGN; -+ priv->buf_layout.pass_parser_result = true; -+ priv->buf_layout.pass_frame_status = true; -+ priv->buf_layout.private_data_size = DPAA2_ETH_SWA_SIZE; -+ /* HW erratum mandates data alignment in multiples of 256 */ -+ priv->buf_layout.data_align = DPAA2_ETH_RX_BUF_ALIGN; -+ -+ /* rx buffer */ -+ err = dpni_set_rx_buffer_layout(priv->mc_io, 0, priv->mc_token, -+ &priv->buf_layout); -+ if (err) { -+ dev_err(dev, "dpni_set_rx_buffer_layout() failed"); -+ goto err_buf_layout; -+ } -+ /* tx buffer: remove Rx-only options */ -+ priv->buf_layout.options &= ~(DPNI_BUF_LAYOUT_OPT_DATA_ALIGN | -+ DPNI_BUF_LAYOUT_OPT_PARSER_RESULT); -+ err = dpni_set_tx_buffer_layout(priv->mc_io, 0, priv->mc_token, -+ &priv->buf_layout); -+ if (err) { -+ dev_err(dev, "dpni_set_tx_buffer_layout() failed"); -+ goto err_buf_layout; -+ } -+ /* tx-confirm: same options as tx */ -+ priv->buf_layout.options &= ~DPNI_BUF_LAYOUT_OPT_PRIVATE_DATA_SIZE; -+ priv->buf_layout.options |= DPNI_BUF_LAYOUT_OPT_TIMESTAMP; -+ priv->buf_layout.pass_timestamp = 1; -+ err = dpni_set_tx_conf_buffer_layout(priv->mc_io, 0, priv->mc_token, -+ &priv->buf_layout); -+ if (err) { -+ dev_err(dev, "dpni_set_tx_conf_buffer_layout() failed"); -+ goto err_buf_layout; -+ } -+ /* Now that we've set our tx buffer layout, retrieve the minimum -+ * required tx data offset. -+ */ -+ err = dpni_get_tx_data_offset(priv->mc_io, 0, priv->mc_token, -+ &priv->tx_data_offset); -+ if (err) { -+ dev_err(dev, "dpni_get_tx_data_offset() failed\n"); -+ goto err_data_offset; -+ } -+ -+ if ((priv->tx_data_offset % 64) != 0) -+ dev_warn(dev, "Tx data offset (%d) not a multiple of 64B", -+ priv->tx_data_offset); -+ -+ /* Accommodate SWA space. */ -+ priv->tx_data_offset += DPAA2_ETH_SWA_SIZE; -+ -+ /* allocate classification rule space */ -+ priv->cls_rule = kzalloc(sizeof(*priv->cls_rule) * -+ DPAA2_CLASSIFIER_ENTRY_COUNT, GFP_KERNEL); -+ if (!priv->cls_rule) -+ goto err_cls_rule; -+ -+ kfree(dma_mem); -+ -+ return 0; -+ -+err_cls_rule: -+err_data_offset: -+err_buf_layout: -+err_extract: -+err_get_attr: -+err_dma_map: -+ kfree(dma_mem); -+err_alloc: -+err_reset: -+ dpni_close(priv->mc_io, 0, priv->mc_token); -+err_open: -+ return err; -+} -+ -+static void free_dpni(struct dpaa2_eth_priv *priv) -+{ -+ int err; -+ -+ err = dpni_reset(priv->mc_io, 0, priv->mc_token); -+ if (err) -+ netdev_warn(priv->net_dev, "dpni_reset() failed (err %d)\n", -+ err); -+ -+ dpni_close(priv->mc_io, 0, priv->mc_token); -+} -+ -+static int setup_rx_flow(struct dpaa2_eth_priv *priv, -+ struct dpaa2_eth_fq *fq) -+{ -+ struct device *dev = priv->net_dev->dev.parent; -+ struct dpni_queue_attr rx_queue_attr; -+ struct dpni_queue_cfg queue_cfg; -+ int err; -+ -+ memset(&queue_cfg, 0, sizeof(queue_cfg)); -+ queue_cfg.options = DPNI_QUEUE_OPT_USER_CTX | DPNI_QUEUE_OPT_DEST | -+ DPNI_QUEUE_OPT_TAILDROP_THRESHOLD; -+ queue_cfg.dest_cfg.dest_type = DPNI_DEST_DPCON; -+ queue_cfg.dest_cfg.priority = 1; -+ queue_cfg.user_ctx = (u64)fq; -+ queue_cfg.dest_cfg.dest_id = fq->channel->dpcon_id; -+ queue_cfg.tail_drop_threshold = DPAA2_ETH_TAILDROP_THRESH; -+ err = dpni_set_rx_flow(priv->mc_io, 0, priv->mc_token, 0, fq->flowid, -+ &queue_cfg); -+ if (err) { -+ dev_err(dev, "dpni_set_rx_flow() failed\n"); -+ return err; -+ } -+ -+ /* Get the actual FQID that was assigned by MC */ -+ err = dpni_get_rx_flow(priv->mc_io, 0, priv->mc_token, 0, fq->flowid, -+ &rx_queue_attr); -+ if (err) { -+ dev_err(dev, "dpni_get_rx_flow() failed\n"); -+ return err; -+ } -+ fq->fqid = rx_queue_attr.fqid; -+ -+ return 0; -+} -+ -+static int setup_tx_flow(struct dpaa2_eth_priv *priv, -+ struct dpaa2_eth_fq *fq) -+{ -+ struct device *dev = priv->net_dev->dev.parent; -+ struct dpni_tx_flow_cfg tx_flow_cfg; -+ struct dpni_tx_conf_cfg tx_conf_cfg; -+ struct dpni_tx_conf_attr tx_conf_attr; -+ int err; -+ -+ memset(&tx_flow_cfg, 0, sizeof(tx_flow_cfg)); -+ tx_flow_cfg.options = DPNI_TX_FLOW_OPT_TX_CONF_ERROR; -+ tx_flow_cfg.use_common_tx_conf_queue = 0; -+ err = dpni_set_tx_flow(priv->mc_io, 0, priv->mc_token, -+ &fq->flowid, &tx_flow_cfg); -+ if (err) { -+ dev_err(dev, "dpni_set_tx_flow() failed\n"); -+ return err; -+ } -+ -+ tx_conf_cfg.errors_only = 0; -+ tx_conf_cfg.queue_cfg.options = DPNI_QUEUE_OPT_USER_CTX | -+ DPNI_QUEUE_OPT_DEST; -+ tx_conf_cfg.queue_cfg.user_ctx = (u64)fq; -+ tx_conf_cfg.queue_cfg.dest_cfg.dest_type = DPNI_DEST_DPCON; -+ tx_conf_cfg.queue_cfg.dest_cfg.dest_id = fq->channel->dpcon_id; -+ tx_conf_cfg.queue_cfg.dest_cfg.priority = 0; -+ -+ err = dpni_set_tx_conf(priv->mc_io, 0, priv->mc_token, fq->flowid, -+ &tx_conf_cfg); -+ if (err) { -+ dev_err(dev, "dpni_set_tx_conf() failed\n"); -+ return err; -+ } -+ -+ err = dpni_get_tx_conf(priv->mc_io, 0, priv->mc_token, fq->flowid, -+ &tx_conf_attr); -+ if (err) { -+ dev_err(dev, "dpni_get_tx_conf() failed\n"); -+ return err; -+ } -+ -+ fq->fqid = tx_conf_attr.queue_attr.fqid; -+ -+ return 0; -+} -+ -+#ifdef CONFIG_FSL_DPAA2_ETH_USE_ERR_QUEUE -+static int setup_rx_err_flow(struct dpaa2_eth_priv *priv, -+ struct dpaa2_eth_fq *fq) -+{ -+ struct dpni_queue_attr queue_attr; -+ struct dpni_queue_cfg queue_cfg; -+ int err; -+ -+ /* Configure the Rx error queue to generate CDANs, -+ * just like the Rx queues -+ */ -+ queue_cfg.options = DPNI_QUEUE_OPT_USER_CTX | DPNI_QUEUE_OPT_DEST; -+ queue_cfg.dest_cfg.dest_type = DPNI_DEST_DPCON; -+ queue_cfg.dest_cfg.priority = 1; -+ queue_cfg.user_ctx = (u64)fq; -+ queue_cfg.dest_cfg.dest_id = fq->channel->dpcon_id; -+ err = dpni_set_rx_err_queue(priv->mc_io, 0, priv->mc_token, &queue_cfg); -+ if (err) { -+ netdev_err(priv->net_dev, "dpni_set_rx_err_queue() failed\n"); -+ return err; -+ } -+ -+ /* Get the FQID */ -+ err = dpni_get_rx_err_queue(priv->mc_io, 0, priv->mc_token, -+ &queue_attr); -+ if (err) { -+ netdev_err(priv->net_dev, "dpni_get_rx_err_queue() failed\n"); -+ return err; -+ } -+ fq->fqid = queue_attr.fqid; -+ -+ return 0; -+} -+#endif -+ -+/* default hash key fields */ -+static struct dpaa2_eth_hash_fields default_hash_fields[] = { -+ { -+ /* L2 header */ -+ .rxnfc_field = RXH_L2DA, -+ .cls_prot = NET_PROT_ETH, -+ .cls_field = NH_FLD_ETH_DA, -+ .size = 6, -+ }, { -+ .cls_prot = NET_PROT_ETH, -+ .cls_field = NH_FLD_ETH_SA, -+ .size = 6, -+ }, { -+ /* This is the last ethertype field parsed: -+ * depending on frame format, it can be the MAC ethertype -+ * or the VLAN etype. -+ */ -+ .cls_prot = NET_PROT_ETH, -+ .cls_field = NH_FLD_ETH_TYPE, -+ .size = 2, -+ }, { -+ /* VLAN header */ -+ .rxnfc_field = RXH_VLAN, -+ .cls_prot = NET_PROT_VLAN, -+ .cls_field = NH_FLD_VLAN_TCI, -+ .size = 2, -+ }, { -+ /* IP header */ -+ .rxnfc_field = RXH_IP_SRC, -+ .cls_prot = NET_PROT_IP, -+ .cls_field = NH_FLD_IP_SRC, -+ .size = 4, -+ }, { -+ .rxnfc_field = RXH_IP_DST, -+ .cls_prot = NET_PROT_IP, -+ .cls_field = NH_FLD_IP_DST, -+ .size = 4, -+ }, { -+ .rxnfc_field = RXH_L3_PROTO, -+ .cls_prot = NET_PROT_IP, -+ .cls_field = NH_FLD_IP_PROTO, -+ .size = 1, -+ }, { -+ /* Using UDP ports, this is functionally equivalent to raw -+ * byte pairs from L4 header. -+ */ -+ .rxnfc_field = RXH_L4_B_0_1, -+ .cls_prot = NET_PROT_UDP, -+ .cls_field = NH_FLD_UDP_PORT_SRC, -+ .size = 2, -+ }, { -+ .rxnfc_field = RXH_L4_B_2_3, -+ .cls_prot = NET_PROT_UDP, -+ .cls_field = NH_FLD_UDP_PORT_DST, -+ .size = 2, -+ }, -+}; -+ -+/* Set RX hash options */ -+int set_hash(struct dpaa2_eth_priv *priv) -+{ -+ struct device *dev = priv->net_dev->dev.parent; -+ struct dpkg_profile_cfg cls_cfg; -+ struct dpni_rx_tc_dist_cfg dist_cfg; -+ u8 *dma_mem; -+ int i; -+ int err = 0; -+ -+ memset(&cls_cfg, 0, sizeof(cls_cfg)); -+ -+ for (i = 0; i < priv->num_hash_fields; i++) { -+ struct dpkg_extract *key = -+ &cls_cfg.extracts[cls_cfg.num_extracts]; -+ -+ key->type = DPKG_EXTRACT_FROM_HDR; -+ key->extract.from_hdr.prot = priv->hash_fields[i].cls_prot; -+ key->extract.from_hdr.type = DPKG_FULL_FIELD; -+ key->extract.from_hdr.field = priv->hash_fields[i].cls_field; -+ cls_cfg.num_extracts++; -+ -+ priv->rx_flow_hash |= priv->hash_fields[i].rxnfc_field; -+ } -+ -+ dma_mem = kzalloc(DPAA2_CLASSIFIER_DMA_SIZE, GFP_DMA | GFP_KERNEL); -+ if (!dma_mem) -+ return -ENOMEM; -+ -+ err = dpni_prepare_key_cfg(&cls_cfg, dma_mem); -+ if (err) { -+ dev_err(dev, "dpni_prepare_key_cfg error %d", err); -+ return err; -+ } -+ -+ memset(&dist_cfg, 0, sizeof(dist_cfg)); -+ -+ /* Prepare for setting the rx dist */ -+ dist_cfg.key_cfg_iova = dma_map_single(dev, dma_mem, -+ DPAA2_CLASSIFIER_DMA_SIZE, -+ DMA_TO_DEVICE); -+ if (dma_mapping_error(dev, dist_cfg.key_cfg_iova)) { -+ dev_err(dev, "DMA mapping failed\n"); -+ kfree(dma_mem); -+ return -ENOMEM; -+ } -+ -+ dist_cfg.dist_size = dpaa2_eth_queue_count(priv); -+ if (dpaa2_eth_fs_enabled(priv)) { -+ dist_cfg.dist_mode = DPNI_DIST_MODE_FS; -+ dist_cfg.fs_cfg.miss_action = DPNI_FS_MISS_HASH; -+ } else { -+ dist_cfg.dist_mode = DPNI_DIST_MODE_HASH; -+ } -+ -+ err = dpni_set_rx_tc_dist(priv->mc_io, 0, priv->mc_token, 0, &dist_cfg); -+ dma_unmap_single(dev, dist_cfg.key_cfg_iova, -+ DPAA2_CLASSIFIER_DMA_SIZE, DMA_TO_DEVICE); -+ kfree(dma_mem); -+ if (err) { -+ dev_err(dev, "dpni_set_rx_tc_dist() error %d\n", err); -+ return err; -+ } -+ -+ return 0; -+} -+ -+/* Bind the DPNI to its needed objects and resources: buffer pool, DPIOs, -+ * frame queues and channels -+ */ -+static int bind_dpni(struct dpaa2_eth_priv *priv) -+{ -+ struct net_device *net_dev = priv->net_dev; -+ struct device *dev = net_dev->dev.parent; -+ struct dpni_pools_cfg pools_params; -+ struct dpni_error_cfg err_cfg; -+ int err = 0; -+ int i; -+ -+ pools_params.num_dpbp = 1; -+ pools_params.pools[0].dpbp_id = priv->dpbp_dev->obj_desc.id; -+ pools_params.pools[0].backup_pool = 0; -+ pools_params.pools[0].buffer_size = DPAA2_ETH_RX_BUF_SIZE; -+ err = dpni_set_pools(priv->mc_io, 0, priv->mc_token, &pools_params); -+ if (err) { -+ dev_err(dev, "dpni_set_pools() failed\n"); -+ return err; -+ } -+ -+ /* Verify classification options and disable hashing and/or -+ * flow steering support in case of invalid configuration values -+ */ -+ check_cls_support(priv); -+ -+ /* have the interface implicitly distribute traffic based on -+ * a static hash key -+ */ -+ if (dpaa2_eth_hash_enabled(priv)) { -+ priv->hash_fields = default_hash_fields; -+ priv->num_hash_fields = ARRAY_SIZE(default_hash_fields); -+ err = set_hash(priv); -+ if (err) { -+ dev_err(dev, "Hashing configuration failed\n"); -+ return err; -+ } -+ } -+ -+ /* Configure handling of error frames */ -+ err_cfg.errors = DPAA2_ETH_RX_ERR_MASK; -+ err_cfg.set_frame_annotation = 1; -+#ifdef CONFIG_FSL_DPAA2_ETH_USE_ERR_QUEUE -+ err_cfg.error_action = DPNI_ERROR_ACTION_SEND_TO_ERROR_QUEUE; -+#else -+ err_cfg.error_action = DPNI_ERROR_ACTION_DISCARD; -+#endif -+ err = dpni_set_errors_behavior(priv->mc_io, 0, priv->mc_token, -+ &err_cfg); -+ if (err) { -+ dev_err(dev, "dpni_set_errors_behavior failed\n"); -+ return err; -+ } -+ -+ /* Configure Rx and Tx conf queues to generate CDANs */ -+ for (i = 0; i < priv->num_fqs; i++) { -+ switch (priv->fq[i].type) { -+ case DPAA2_RX_FQ: -+ err = setup_rx_flow(priv, &priv->fq[i]); -+ break; -+ case DPAA2_TX_CONF_FQ: -+ err = setup_tx_flow(priv, &priv->fq[i]); -+ break; -+#ifdef CONFIG_FSL_DPAA2_ETH_USE_ERR_QUEUE -+ case DPAA2_RX_ERR_FQ: -+ err = setup_rx_err_flow(priv, &priv->fq[i]); -+ break; -+#endif -+ default: -+ dev_err(dev, "Invalid FQ type %d\n", priv->fq[i].type); -+ return -EINVAL; -+ } -+ if (err) -+ return err; -+ } -+ -+ err = dpni_get_qdid(priv->mc_io, 0, priv->mc_token, &priv->tx_qdid); -+ if (err) { -+ dev_err(dev, "dpni_get_qdid() failed\n"); -+ return err; -+ } -+ -+ return 0; -+} -+ -+/* Allocate rings for storing incoming frame descriptors */ -+static int alloc_rings(struct dpaa2_eth_priv *priv) -+{ -+ struct net_device *net_dev = priv->net_dev; -+ struct device *dev = net_dev->dev.parent; -+ int i; -+ -+ for (i = 0; i < priv->num_channels; i++) { -+ priv->channel[i]->store = -+ dpaa2_io_store_create(DPAA2_ETH_STORE_SIZE, dev); -+ if (!priv->channel[i]->store) { -+ netdev_err(net_dev, "dpaa2_io_store_create() failed\n"); -+ goto err_ring; -+ } -+ } -+ -+ return 0; -+ -+err_ring: -+ for (i = 0; i < priv->num_channels; i++) { -+ if (!priv->channel[i]->store) -+ break; -+ dpaa2_io_store_destroy(priv->channel[i]->store); -+ } -+ -+ return -ENOMEM; -+} -+ -+static void free_rings(struct dpaa2_eth_priv *priv) -+{ -+ int i; -+ -+ for (i = 0; i < priv->num_channels; i++) -+ dpaa2_io_store_destroy(priv->channel[i]->store); -+} -+ -+static int netdev_init(struct net_device *net_dev) -+{ -+ int err; -+ struct device *dev = net_dev->dev.parent; -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ u8 mac_addr[ETH_ALEN]; -+ u8 bcast_addr[ETH_ALEN]; -+ -+ net_dev->netdev_ops = &dpaa2_eth_ops; -+ -+ /* If the DPNI attributes contain an all-0 mac_addr, -+ * set a random hardware address -+ */ -+ err = dpni_get_primary_mac_addr(priv->mc_io, 0, priv->mc_token, -+ mac_addr); -+ if (err) { -+ dev_err(dev, "dpni_get_primary_mac_addr() failed (%d)", err); -+ return err; -+ } -+ if (is_zero_ether_addr(mac_addr)) { -+ /* Fills in net_dev->dev_addr, as required by -+ * register_netdevice() -+ */ -+ eth_hw_addr_random(net_dev); -+ /* Make the user aware, without cluttering the boot log */ -+ pr_info_once(KBUILD_MODNAME " device(s) have all-zero hwaddr, replaced with random"); -+ err = dpni_set_primary_mac_addr(priv->mc_io, 0, priv->mc_token, -+ net_dev->dev_addr); -+ if (err) { -+ dev_err(dev, "dpni_set_primary_mac_addr(): %d\n", err); -+ return err; -+ } -+ /* Override NET_ADDR_RANDOM set by eth_hw_addr_random(); for all -+ * practical purposes, this will be our "permanent" mac address, -+ * at least until the next reboot. This move will also permit -+ * register_netdevice() to properly fill up net_dev->perm_addr. -+ */ -+ net_dev->addr_assign_type = NET_ADDR_PERM; -+ } else { -+ /* NET_ADDR_PERM is default, all we have to do is -+ * fill in the device addr. -+ */ -+ memcpy(net_dev->dev_addr, mac_addr, net_dev->addr_len); -+ } -+ -+ /* Explicitly add the broadcast address to the MAC filtering table; -+ * the MC won't do that for us. -+ */ -+ eth_broadcast_addr(bcast_addr); -+ err = dpni_add_mac_addr(priv->mc_io, 0, priv->mc_token, bcast_addr); -+ if (err) { -+ dev_warn(dev, "dpni_add_mac_addr() failed (%d)\n", err); -+ /* Won't return an error; at least, we'd have egress traffic */ -+ } -+ -+ /* Reserve enough space to align buffer as per hardware requirement; -+ * NOTE: priv->tx_data_offset MUST be initialized at this point. -+ */ -+ net_dev->needed_headroom = DPAA2_ETH_NEEDED_HEADROOM(priv); -+ -+ /* Our .ndo_init will be called herein */ -+ err = register_netdev(net_dev); -+ if (err < 0) { -+ dev_err(dev, "register_netdev() = %d\n", err); -+ return err; -+ } -+ -+ return 0; -+} -+ -+static int poll_link_state(void *arg) -+{ -+ struct dpaa2_eth_priv *priv = (struct dpaa2_eth_priv *)arg; -+ int err; -+ -+ while (!kthread_should_stop()) { -+ err = link_state_update(priv); -+ if (unlikely(err)) -+ return err; -+ -+ msleep(DPAA2_ETH_LINK_STATE_REFRESH); -+ } -+ -+ return 0; -+} -+ -+static irqreturn_t dpni_irq0_handler(int irq_num, void *arg) -+{ -+ return IRQ_WAKE_THREAD; -+} -+ -+static irqreturn_t dpni_irq0_handler_thread(int irq_num, void *arg) -+{ -+ u8 irq_index = DPNI_IRQ_INDEX; -+ u32 status, clear = 0; -+ struct device *dev = (struct device *)arg; -+ struct fsl_mc_device *dpni_dev = to_fsl_mc_device(dev); -+ struct net_device *net_dev = dev_get_drvdata(dev); -+ int err; -+ -+ err = dpni_get_irq_status(dpni_dev->mc_io, 0, dpni_dev->mc_handle, -+ irq_index, &status); -+ if (unlikely(err)) { -+ netdev_err(net_dev, "Can't get irq status (err %d)", err); -+ clear = 0xffffffff; -+ goto out; -+ } -+ -+ if (status & DPNI_IRQ_EVENT_LINK_CHANGED) { -+ clear |= DPNI_IRQ_EVENT_LINK_CHANGED; -+ link_state_update(netdev_priv(net_dev)); -+ } -+ -+out: -+ dpni_clear_irq_status(dpni_dev->mc_io, 0, dpni_dev->mc_handle, -+ irq_index, clear); -+ return IRQ_HANDLED; -+} -+ -+static int setup_irqs(struct fsl_mc_device *ls_dev) -+{ -+ int err = 0; -+ struct fsl_mc_device_irq *irq; -+ u8 irq_index = DPNI_IRQ_INDEX; -+ u32 mask = DPNI_IRQ_EVENT_LINK_CHANGED; -+ -+ err = fsl_mc_allocate_irqs(ls_dev); -+ if (err) { -+ dev_err(&ls_dev->dev, "MC irqs allocation failed\n"); -+ return err; -+ } -+ -+ irq = ls_dev->irqs[0]; -+ err = devm_request_threaded_irq(&ls_dev->dev, irq->irq_number, -+ dpni_irq0_handler, -+ dpni_irq0_handler_thread, -+ IRQF_NO_SUSPEND | IRQF_ONESHOT, -+ dev_name(&ls_dev->dev), &ls_dev->dev); -+ if (err < 0) { -+ dev_err(&ls_dev->dev, "devm_request_threaded_irq(): %d", err); -+ goto free_mc_irq; -+ } -+ -+ err = dpni_set_irq_mask(ls_dev->mc_io, 0, ls_dev->mc_handle, -+ irq_index, mask); -+ if (err < 0) { -+ dev_err(&ls_dev->dev, "dpni_set_irq_mask(): %d", err); -+ goto free_irq; -+ } -+ -+ err = dpni_set_irq_enable(ls_dev->mc_io, 0, ls_dev->mc_handle, -+ irq_index, 1); -+ if (err < 0) { -+ dev_err(&ls_dev->dev, "dpni_set_irq_enable(): %d", err); -+ goto free_irq; -+ } -+ -+ return 0; -+ -+free_irq: -+ devm_free_irq(&ls_dev->dev, irq->irq_number, &ls_dev->dev); -+free_mc_irq: -+ fsl_mc_free_irqs(ls_dev); -+ -+ return err; -+} -+ -+static void add_ch_napi(struct dpaa2_eth_priv *priv) -+{ -+ int i; -+ struct dpaa2_eth_channel *ch; -+ -+ for (i = 0; i < priv->num_channels; i++) { -+ ch = priv->channel[i]; -+ /* NAPI weight *MUST* be a multiple of DPAA2_ETH_STORE_SIZE */ -+ netif_napi_add(priv->net_dev, &ch->napi, dpaa2_eth_poll, -+ NAPI_POLL_WEIGHT); -+ } -+} -+ -+static void del_ch_napi(struct dpaa2_eth_priv *priv) -+{ -+ int i; -+ struct dpaa2_eth_channel *ch; -+ -+ for (i = 0; i < priv->num_channels; i++) { -+ ch = priv->channel[i]; -+ netif_napi_del(&ch->napi); -+ } -+} -+ -+/* SysFS support */ -+static ssize_t dpaa2_eth_show_tx_shaping(struct device *dev, -+ struct device_attribute *attr, -+ char *buf) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(to_net_dev(dev)); -+ /* No MC API for getting the shaping config. We're stateful. */ -+ struct dpni_tx_shaping_cfg *scfg = &priv->shaping_cfg; -+ -+ return sprintf(buf, "%u %hu\n", scfg->rate_limit, scfg->max_burst_size); -+} -+ -+static ssize_t dpaa2_eth_write_tx_shaping(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, -+ size_t count) -+{ -+ int err, items; -+ struct dpaa2_eth_priv *priv = netdev_priv(to_net_dev(dev)); -+ struct dpni_tx_shaping_cfg scfg; -+ -+ items = sscanf(buf, "%u %hu", &scfg.rate_limit, &scfg.max_burst_size); -+ if (items != 2) { -+ pr_err("Expected format: \"rate_limit(Mbps) max_burst_size(bytes)\"\n"); -+ return -EINVAL; -+ } -+ /* Size restriction as per MC API documentation */ -+ if (scfg.max_burst_size > 64000) { -+ pr_err("max_burst_size must be <= 64000, thanks.\n"); -+ return -EINVAL; -+ } -+ -+ err = dpni_set_tx_shaping(priv->mc_io, 0, priv->mc_token, &scfg); -+ if (err) { -+ dev_err(dev, "dpni_set_tx_shaping() failed\n"); -+ return -EPERM; -+ } -+ /* If successful, save the current configuration for future inquiries */ -+ priv->shaping_cfg = scfg; -+ -+ return count; -+} -+ -+static ssize_t dpaa2_eth_show_txconf_cpumask(struct device *dev, -+ struct device_attribute *attr, -+ char *buf) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(to_net_dev(dev)); -+ -+ return cpumask_scnprintf(buf, PAGE_SIZE, &priv->txconf_cpumask); -+} -+ -+static ssize_t dpaa2_eth_write_txconf_cpumask(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, -+ size_t count) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(to_net_dev(dev)); -+ struct dpaa2_eth_fq *fq; -+ bool running = netif_running(priv->net_dev); -+ int i, err; -+ -+ err = cpulist_parse(buf, &priv->txconf_cpumask); -+ if (err) -+ return err; -+ -+ /* Only accept CPUs that have an affine DPIO */ -+ if (!cpumask_subset(&priv->txconf_cpumask, &priv->dpio_cpumask)) { -+ netdev_info(priv->net_dev, -+ "cpumask must be a subset of 0x%lx\n", -+ *cpumask_bits(&priv->dpio_cpumask)); -+ cpumask_and(&priv->txconf_cpumask, &priv->dpio_cpumask, -+ &priv->txconf_cpumask); -+ } -+ -+ /* Rewiring the TxConf FQs requires interface shutdown. -+ */ -+ if (running) { -+ err = dpaa2_eth_stop(priv->net_dev); -+ if (err) -+ return -ENODEV; -+ } -+ -+ /* Set the new TxConf FQ affinities */ -+ set_fq_affinity(priv); -+ -+ /* dpaa2_eth_open() below will *stop* the Tx queues until an explicit -+ * link up notification is received. Give the polling thread enough time -+ * to detect the link state change, or else we'll end up with the -+ * transmission side forever shut down. -+ */ -+ if (priv->do_link_poll) -+ msleep(2 * DPAA2_ETH_LINK_STATE_REFRESH); -+ -+ for (i = 0; i < priv->num_fqs; i++) { -+ fq = &priv->fq[i]; -+ if (fq->type != DPAA2_TX_CONF_FQ) -+ continue; -+ setup_tx_flow(priv, fq); -+ } -+ -+ if (running) { -+ err = dpaa2_eth_open(priv->net_dev); -+ if (err) -+ return -ENODEV; -+ } -+ -+ return count; -+} -+ -+static struct device_attribute dpaa2_eth_attrs[] = { -+ __ATTR(txconf_cpumask, -+ S_IRUSR | S_IWUSR, -+ dpaa2_eth_show_txconf_cpumask, -+ dpaa2_eth_write_txconf_cpumask), -+ -+ __ATTR(tx_shaping, -+ S_IRUSR | S_IWUSR, -+ dpaa2_eth_show_tx_shaping, -+ dpaa2_eth_write_tx_shaping), -+}; -+ -+void dpaa2_eth_sysfs_init(struct device *dev) -+{ -+ int i, err; -+ -+ for (i = 0; i < ARRAY_SIZE(dpaa2_eth_attrs); i++) { -+ err = device_create_file(dev, &dpaa2_eth_attrs[i]); -+ if (err) { -+ dev_err(dev, "ERROR creating sysfs file\n"); -+ goto undo; -+ } -+ } -+ return; -+ -+undo: -+ while (i > 0) -+ device_remove_file(dev, &dpaa2_eth_attrs[--i]); -+} -+ -+void dpaa2_eth_sysfs_remove(struct device *dev) -+{ -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(dpaa2_eth_attrs); i++) -+ device_remove_file(dev, &dpaa2_eth_attrs[i]); -+} -+ -+static int dpaa2_eth_probe(struct fsl_mc_device *dpni_dev) -+{ -+ struct device *dev; -+ struct net_device *net_dev = NULL; -+ struct dpaa2_eth_priv *priv = NULL; -+ int err = 0; -+ -+ dev = &dpni_dev->dev; -+ -+ /* Net device */ -+ net_dev = alloc_etherdev_mq(sizeof(*priv), DPAA2_ETH_MAX_TX_QUEUES); -+ if (!net_dev) { -+ dev_err(dev, "alloc_etherdev_mq() failed\n"); -+ return -ENOMEM; -+ } -+ -+ SET_NETDEV_DEV(net_dev, dev); -+ dev_set_drvdata(dev, net_dev); -+ -+ priv = netdev_priv(net_dev); -+ priv->net_dev = net_dev; -+ -+ /* Obtain a MC portal */ -+ err = fsl_mc_portal_allocate(dpni_dev, FSL_MC_IO_ATOMIC_CONTEXT_PORTAL, -+ &priv->mc_io); -+ if (err) { -+ dev_err(dev, "MC portal allocation failed\n"); -+ goto err_portal_alloc; -+ } -+ -+ /* MC objects initialization and configuration */ -+ err = setup_dpni(dpni_dev); -+ if (err) -+ goto err_dpni_setup; -+ -+ err = setup_dpio(priv); -+ if (err) -+ goto err_dpio_setup; -+ -+ setup_fqs(priv); -+ -+ err = setup_dpbp(priv); -+ if (err) -+ goto err_dpbp_setup; -+ -+ err = bind_dpni(priv); -+ if (err) -+ goto err_bind; -+ -+ /* Add a NAPI context for each channel */ -+ add_ch_napi(priv); -+ -+ /* Percpu statistics */ -+ priv->percpu_stats = alloc_percpu(*priv->percpu_stats); -+ if (!priv->percpu_stats) { -+ dev_err(dev, "alloc_percpu(percpu_stats) failed\n"); -+ err = -ENOMEM; -+ goto err_alloc_percpu_stats; -+ } -+ priv->percpu_extras = alloc_percpu(*priv->percpu_extras); -+ if (!priv->percpu_extras) { -+ dev_err(dev, "alloc_percpu(percpu_extras) failed\n"); -+ err = -ENOMEM; -+ goto err_alloc_percpu_extras; -+ } -+ -+ snprintf(net_dev->name, IFNAMSIZ, "ni%d", dpni_dev->obj_desc.id); -+ if (!dev_valid_name(net_dev->name)) { -+ dev_warn(&net_dev->dev, -+ "netdevice name \"%s\" cannot be used, reverting to default..\n", -+ net_dev->name); -+ dev_alloc_name(net_dev, "eth%d"); -+ dev_warn(&net_dev->dev, "using name \"%s\"\n", net_dev->name); -+ } -+ -+ err = netdev_init(net_dev); -+ if (err) -+ goto err_netdev_init; -+ -+ /* Configure checksum offload based on current interface flags */ -+ err = set_rx_csum(priv, !!(net_dev->features & NETIF_F_RXCSUM)); -+ if (err) -+ goto err_csum; -+ -+ err = set_tx_csum(priv, !!(net_dev->features & -+ (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM))); -+ if (err) -+ goto err_csum; -+ -+ err = alloc_rings(priv); -+ if (err) -+ goto err_alloc_rings; -+ -+ net_dev->ethtool_ops = &dpaa2_ethtool_ops; -+ -+ err = setup_irqs(dpni_dev); -+ if (err) { -+ netdev_warn(net_dev, "Failed to set link interrupt, fall back to polling\n"); -+ priv->poll_thread = kthread_run(poll_link_state, priv, -+ "%s_poll_link", net_dev->name); -+ if (IS_ERR(priv->poll_thread)) { -+ netdev_err(net_dev, "Error starting polling thread\n"); -+ goto err_poll_thread; -+ } -+ priv->do_link_poll = true; -+ } -+ -+ dpaa2_eth_sysfs_init(&net_dev->dev); -+ dpaa2_dbg_add(priv); -+ -+ dev_info(dev, "Probed interface %s\n", net_dev->name); -+ return 0; -+ -+err_poll_thread: -+ free_rings(priv); -+err_alloc_rings: -+err_csum: -+ unregister_netdev(net_dev); -+err_netdev_init: -+ free_percpu(priv->percpu_extras); -+err_alloc_percpu_extras: -+ free_percpu(priv->percpu_stats); -+err_alloc_percpu_stats: -+ del_ch_napi(priv); -+err_bind: -+ free_dpbp(priv); -+err_dpbp_setup: -+ free_dpio(priv); -+err_dpio_setup: -+ kfree(priv->cls_rule); -+ dpni_close(priv->mc_io, 0, priv->mc_token); -+err_dpni_setup: -+ fsl_mc_portal_free(priv->mc_io); -+err_portal_alloc: -+ dev_set_drvdata(dev, NULL); -+ free_netdev(net_dev); -+ -+ return err; -+} -+ -+static int dpaa2_eth_remove(struct fsl_mc_device *ls_dev) -+{ -+ struct device *dev; -+ struct net_device *net_dev; -+ struct dpaa2_eth_priv *priv; -+ -+ dev = &ls_dev->dev; -+ net_dev = dev_get_drvdata(dev); -+ priv = netdev_priv(net_dev); -+ -+ dpaa2_dbg_remove(priv); -+ dpaa2_eth_sysfs_remove(&net_dev->dev); -+ -+ unregister_netdev(net_dev); -+ dev_info(net_dev->dev.parent, "Removed interface %s\n", net_dev->name); -+ -+ free_dpio(priv); -+ free_rings(priv); -+ del_ch_napi(priv); -+ free_dpbp(priv); -+ free_dpni(priv); -+ -+ fsl_mc_portal_free(priv->mc_io); -+ -+ free_percpu(priv->percpu_stats); -+ free_percpu(priv->percpu_extras); -+ -+ if (priv->do_link_poll) -+ kthread_stop(priv->poll_thread); -+ else -+ fsl_mc_free_irqs(ls_dev); -+ -+ kfree(priv->cls_rule); -+ -+ dev_set_drvdata(dev, NULL); -+ free_netdev(net_dev); -+ -+ return 0; -+} -+ -+static const struct fsl_mc_device_match_id dpaa2_eth_match_id_table[] = { -+ { -+ .vendor = FSL_MC_VENDOR_FREESCALE, -+ .obj_type = "dpni", -+ .ver_major = DPNI_VER_MAJOR, -+ .ver_minor = DPNI_VER_MINOR -+ }, -+ { .vendor = 0x0 } -+}; -+ -+static struct fsl_mc_driver dpaa2_eth_driver = { -+ .driver = { -+ .name = KBUILD_MODNAME, -+ .owner = THIS_MODULE, -+ }, -+ .probe = dpaa2_eth_probe, -+ .remove = dpaa2_eth_remove, -+ .match_id_table = dpaa2_eth_match_id_table -+}; -+ -+static int __init dpaa2_eth_driver_init(void) -+{ -+ int err; -+ -+ dpaa2_eth_dbg_init(); -+ -+ err = fsl_mc_driver_register(&dpaa2_eth_driver); -+ if (err) { -+ dpaa2_eth_dbg_exit(); -+ return err; -+ } -+ -+ return 0; -+} -+ -+static void __exit dpaa2_eth_driver_exit(void) -+{ -+ fsl_mc_driver_unregister(&dpaa2_eth_driver); -+ dpaa2_eth_dbg_exit(); -+} -+ -+module_init(dpaa2_eth_driver_init); -+module_exit(dpaa2_eth_driver_exit); -diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.h b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.h -new file mode 100644 -index 0000000..bdcdbd6 ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.h -@@ -0,0 +1,397 @@ -+/* Copyright 2014-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 __DPAA2_ETH_H -+#define __DPAA2_ETH_H -+ -+#include -+#include -+#include "../../fsl-mc/include/fsl_dpaa2_io.h" -+#include "../../fsl-mc/include/fsl_dpaa2_fd.h" -+#include "../../fsl-mc/include/dpbp.h" -+#include "../../fsl-mc/include/dpbp-cmd.h" -+#include "../../fsl-mc/include/dpcon.h" -+#include "../../fsl-mc/include/dpcon-cmd.h" -+#include "dpni.h" -+#include "dpni-cmd.h" -+ -+#include "dpaa2-eth-trace.h" -+#include "dpaa2-eth-debugfs.h" -+ -+#define DPAA2_ETH_STORE_SIZE 16 -+ -+/* Maximum number of scatter-gather entries in an ingress frame, -+ * considering the maximum receive frame size is 64K -+ */ -+#define DPAA2_ETH_MAX_SG_ENTRIES ((64 * 1024) / DPAA2_ETH_RX_BUF_SIZE) -+ -+/* Maximum acceptable MTU value. It is in direct relation with the hardware -+ * enforced Max Frame Length (currently 10k). -+ */ -+#define DPAA2_ETH_MFL (10 * 1024) -+#define DPAA2_ETH_MAX_MTU (DPAA2_ETH_MFL - VLAN_ETH_HLEN) -+/* Convert L3 MTU to L2 MFL */ -+#define DPAA2_ETH_L2_MAX_FRM(mtu) (mtu + VLAN_ETH_HLEN) -+ -+/* Set the taildrop threshold (in bytes) to allow the enqueue of several jumbo -+ * frames in the Rx queues (length of the current frame is not -+ * taken into account when making the taildrop decision) -+ */ -+#define DPAA2_ETH_TAILDROP_THRESH (64 * 1024) -+ -+/* Buffer quota per queue. Must be large enough such that for minimum sized -+ * frames taildrop kicks in before the bpool gets depleted, so we compute -+ * how many 64B frames fit inside the taildrop threshold and add a margin -+ * to accommodate the buffer refill delay. -+ */ -+#define DPAA2_ETH_MAX_FRAMES_PER_QUEUE (DPAA2_ETH_TAILDROP_THRESH / 64) -+#define DPAA2_ETH_NUM_BUFS (DPAA2_ETH_MAX_FRAMES_PER_QUEUE + 256) -+#define DPAA2_ETH_REFILL_THRESH DPAA2_ETH_MAX_FRAMES_PER_QUEUE -+ -+/* Maximum number of buffers that can be acquired/released through a single -+ * QBMan command -+ */ -+#define DPAA2_ETH_BUFS_PER_CMD 7 -+ -+/* Hardware requires alignment for ingress/egress buffer addresses -+ * and ingress buffer lengths. -+ */ -+#define DPAA2_ETH_RX_BUF_SIZE 2048 -+#define DPAA2_ETH_TX_BUF_ALIGN 64 -+#define DPAA2_ETH_RX_BUF_ALIGN 256 -+#define DPAA2_ETH_NEEDED_HEADROOM(p_priv) \ -+ ((p_priv)->tx_data_offset + DPAA2_ETH_TX_BUF_ALIGN) -+ -+/* Hardware only sees DPAA2_ETH_RX_BUF_SIZE, but we need to allocate ingress -+ * buffers large enough to allow building an skb around them and also account -+ * for alignment restrictions -+ */ -+#define DPAA2_ETH_BUF_RAW_SIZE \ -+ (DPAA2_ETH_RX_BUF_SIZE + \ -+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + \ -+ DPAA2_ETH_RX_BUF_ALIGN) -+ -+/* PTP nominal frequency 1GHz */ -+#define DPAA2_PTP_NOMINAL_FREQ_PERIOD_NS 1 -+ -+/* We are accommodating a skb backpointer and some S/G info -+ * in the frame's software annotation. The hardware -+ * options are either 0 or 64, so we choose the latter. -+ */ -+#define DPAA2_ETH_SWA_SIZE 64 -+ -+/* Must keep this struct smaller than DPAA2_ETH_SWA_SIZE */ -+struct dpaa2_eth_swa { -+ struct sk_buff *skb; -+ struct scatterlist *scl; -+ int num_sg; -+ int num_dma_bufs; -+}; -+ -+/* Annotation valid bits in FD FRC */ -+#define DPAA2_FD_FRC_FASV 0x8000 -+#define DPAA2_FD_FRC_FAEADV 0x4000 -+#define DPAA2_FD_FRC_FAPRV 0x2000 -+#define DPAA2_FD_FRC_FAIADV 0x1000 -+#define DPAA2_FD_FRC_FASWOV 0x0800 -+#define DPAA2_FD_FRC_FAICFDV 0x0400 -+ -+/* Annotation bits in FD CTRL */ -+#define DPAA2_FD_CTRL_ASAL 0x00020000 /* ASAL = 128 */ -+#define DPAA2_FD_CTRL_PTA 0x00800000 -+#define DPAA2_FD_CTRL_PTV1 0x00400000 -+ -+/* Frame annotation status */ -+struct dpaa2_fas { -+ u8 reserved; -+ u8 ppid; -+ __le16 ifpid; -+ __le32 status; -+} __packed; -+ -+/* Frame annotation egress action descriptor */ -+#define DPAA2_FAEAD_OFFSET 0x58 -+ -+struct dpaa2_faead { -+ __le32 conf_fqid; -+ __le32 ctrl; -+}; -+ -+#define DPAA2_FAEAD_A2V 0x20000000 -+#define DPAA2_FAEAD_UPDV 0x00001000 -+#define DPAA2_FAEAD_UPD 0x00000010 -+ -+/* Error and status bits in the frame annotation status word */ -+/* Debug frame, otherwise supposed to be discarded */ -+#define DPAA2_FAS_DISC 0x80000000 -+/* MACSEC frame */ -+#define DPAA2_FAS_MS 0x40000000 -+#define DPAA2_FAS_PTP 0x08000000 -+/* Ethernet multicast frame */ -+#define DPAA2_FAS_MC 0x04000000 -+/* Ethernet broadcast frame */ -+#define DPAA2_FAS_BC 0x02000000 -+#define DPAA2_FAS_KSE 0x00040000 -+#define DPAA2_FAS_EOFHE 0x00020000 -+#define DPAA2_FAS_MNLE 0x00010000 -+#define DPAA2_FAS_TIDE 0x00008000 -+#define DPAA2_FAS_PIEE 0x00004000 -+/* Frame length error */ -+#define DPAA2_FAS_FLE 0x00002000 -+/* Frame physical error */ -+#define DPAA2_FAS_FPE 0x00001000 -+#define DPAA2_FAS_PTE 0x00000080 -+#define DPAA2_FAS_ISP 0x00000040 -+#define DPAA2_FAS_PHE 0x00000020 -+#define DPAA2_FAS_BLE 0x00000010 -+/* L3 csum validation performed */ -+#define DPAA2_FAS_L3CV 0x00000008 -+/* L3 csum error */ -+#define DPAA2_FAS_L3CE 0x00000004 -+/* L4 csum validation performed */ -+#define DPAA2_FAS_L4CV 0x00000002 -+/* L4 csum error */ -+#define DPAA2_FAS_L4CE 0x00000001 -+/* Possible errors on the ingress path */ -+#define DPAA2_ETH_RX_ERR_MASK (DPAA2_FAS_KSE | \ -+ DPAA2_FAS_EOFHE | \ -+ DPAA2_FAS_MNLE | \ -+ DPAA2_FAS_TIDE | \ -+ DPAA2_FAS_PIEE | \ -+ DPAA2_FAS_FLE | \ -+ DPAA2_FAS_FPE | \ -+ DPAA2_FAS_PTE | \ -+ DPAA2_FAS_ISP | \ -+ DPAA2_FAS_PHE | \ -+ DPAA2_FAS_BLE | \ -+ DPAA2_FAS_L3CE | \ -+ DPAA2_FAS_L4CE) -+/* Tx errors */ -+#define DPAA2_ETH_TXCONF_ERR_MASK (DPAA2_FAS_KSE | \ -+ DPAA2_FAS_EOFHE | \ -+ DPAA2_FAS_MNLE | \ -+ DPAA2_FAS_TIDE) -+ -+/* Time in milliseconds between link state updates */ -+#define DPAA2_ETH_LINK_STATE_REFRESH 1000 -+ -+/* Driver statistics, other than those in struct rtnl_link_stats64. -+ * These are usually collected per-CPU and aggregated by ethtool. -+ */ -+struct dpaa2_eth_drv_stats { -+ __u64 tx_conf_frames; -+ __u64 tx_conf_bytes; -+ __u64 tx_sg_frames; -+ __u64 tx_sg_bytes; -+ __u64 rx_sg_frames; -+ __u64 rx_sg_bytes; -+ /* Enqueues retried due to portal busy */ -+ __u64 tx_portal_busy; -+}; -+ -+/* Per-FQ statistics */ -+struct dpaa2_eth_fq_stats { -+ /* Number of frames received on this queue */ -+ __u64 frames; -+}; -+ -+/* Per-channel statistics */ -+struct dpaa2_eth_ch_stats { -+ /* Volatile dequeues retried due to portal busy */ -+ __u64 dequeue_portal_busy; -+ /* Number of CDANs; useful to estimate avg NAPI len */ -+ __u64 cdan; -+ /* Number of frames received on queues from this channel */ -+ __u64 frames; -+ /* Pull errors */ -+ __u64 pull_err; -+}; -+ -+/* Maximum number of queues associated with a DPNI */ -+#define DPAA2_ETH_MAX_RX_QUEUES 16 -+#define DPAA2_ETH_MAX_TX_QUEUES NR_CPUS -+#define DPAA2_ETH_MAX_RX_ERR_QUEUES 1 -+#define DPAA2_ETH_MAX_QUEUES (DPAA2_ETH_MAX_RX_QUEUES + \ -+ DPAA2_ETH_MAX_TX_QUEUES + \ -+ DPAA2_ETH_MAX_RX_ERR_QUEUES) -+ -+#define DPAA2_ETH_MAX_DPCONS NR_CPUS -+ -+enum dpaa2_eth_fq_type { -+ DPAA2_RX_FQ = 0, -+ DPAA2_TX_CONF_FQ, -+ DPAA2_RX_ERR_FQ -+}; -+ -+struct dpaa2_eth_priv; -+ -+struct dpaa2_eth_fq { -+ u32 fqid; -+ u16 flowid; -+ int target_cpu; -+ struct dpaa2_eth_channel *channel; -+ enum dpaa2_eth_fq_type type; -+ -+ void (*consume)(struct dpaa2_eth_priv *, -+ struct dpaa2_eth_channel *, -+ const struct dpaa2_fd *, -+ struct napi_struct *); -+ struct dpaa2_eth_fq_stats stats; -+}; -+ -+struct dpaa2_eth_channel { -+ struct dpaa2_io_notification_ctx nctx; -+ struct fsl_mc_device *dpcon; -+ int dpcon_id; -+ int ch_id; -+ int dpio_id; -+ struct napi_struct napi; -+ struct dpaa2_io_store *store; -+ struct dpaa2_eth_priv *priv; -+ int buf_count; -+ struct dpaa2_eth_ch_stats stats; -+}; -+ -+struct dpaa2_eth_cls_rule { -+ struct ethtool_rx_flow_spec fs; -+ bool in_use; -+}; -+ -+struct dpaa2_eth_hash_fields { -+ u64 rxnfc_field; -+ enum net_prot cls_prot; -+ int cls_field; -+ int offset; -+ int size; -+}; -+ -+/* Driver private data */ -+struct dpaa2_eth_priv { -+ struct net_device *net_dev; -+ -+ u8 num_fqs; -+ struct dpaa2_eth_fq fq[DPAA2_ETH_MAX_QUEUES]; -+ -+ u8 num_channels; -+ struct dpaa2_eth_channel *channel[DPAA2_ETH_MAX_DPCONS]; -+ -+ int dpni_id; -+ struct dpni_attr dpni_attrs; -+ struct dpni_extended_cfg dpni_ext_cfg; -+ /* Insofar as the MC is concerned, we're using one layout on all 3 types -+ * of buffers (Rx, Tx, Tx-Conf). -+ */ -+ struct dpni_buffer_layout buf_layout; -+ u16 tx_data_offset; -+ -+ struct fsl_mc_device *dpbp_dev; -+ struct dpbp_attr dpbp_attrs; -+ -+ u16 tx_qdid; -+ struct fsl_mc_io *mc_io; -+ /* SysFS-controlled affinity mask for TxConf FQs */ -+ struct cpumask txconf_cpumask; -+ /* Cores which have an affine DPIO/DPCON. -+ * This is the cpu set on which Rx frames are processed; -+ * Tx confirmation frames are processed on a subset of this, -+ * depending on user settings. -+ */ -+ struct cpumask dpio_cpumask; -+ -+ /* Standard statistics */ -+ struct rtnl_link_stats64 __percpu *percpu_stats; -+ /* Extra stats, in addition to the ones known by the kernel */ -+ struct dpaa2_eth_drv_stats __percpu *percpu_extras; -+ -+ u16 mc_token; -+ -+ struct dpni_link_state link_state; -+ bool do_link_poll; -+ struct task_struct *poll_thread; -+ -+ struct dpaa2_eth_hash_fields *hash_fields; -+ u8 num_hash_fields; -+ /* enabled ethtool hashing bits */ -+ u64 rx_flow_hash; -+ -+#ifdef CONFIG_FSL_DPAA2_ETH_DEBUGFS -+ struct dpaa2_debugfs dbg; -+#endif -+ -+ /* array of classification rules */ -+ struct dpaa2_eth_cls_rule *cls_rule; -+ -+ struct dpni_tx_shaping_cfg shaping_cfg; -+ -+ bool ts_tx_en; /* Tx timestamping enabled */ -+ bool ts_rx_en; /* Rx timestamping enabled */ -+}; -+ -+#define dpaa2_eth_hash_enabled(priv) \ -+ ((priv)->dpni_attrs.options & DPNI_OPT_DIST_HASH) -+ -+#define dpaa2_eth_fs_enabled(priv) \ -+ ((priv)->dpni_attrs.options & DPNI_OPT_DIST_FS) -+ -+#define dpaa2_eth_fs_mask_enabled(priv) \ -+ ((priv)->dpni_attrs.options & DPNI_OPT_FS_MASK_SUPPORT) -+ -+#define DPAA2_CLASSIFIER_ENTRY_COUNT 16 -+ -+/* Required by struct dpni_attr::ext_cfg_iova */ -+#define DPAA2_EXT_CFG_SIZE 256 -+ -+/* size of DMA memory used to pass configuration to classifier, in bytes */ -+#define DPAA2_CLASSIFIER_DMA_SIZE 256 -+ -+extern const struct ethtool_ops dpaa2_ethtool_ops; -+ -+static int dpaa2_eth_queue_count(struct dpaa2_eth_priv *priv) -+{ -+ if (!dpaa2_eth_hash_enabled(priv)) -+ return 1; -+ -+ return priv->dpni_ext_cfg.tc_cfg[0].max_dist; -+} -+ -+static inline int dpaa2_eth_max_channels(struct dpaa2_eth_priv *priv) -+{ -+ /* Ideally, we want a number of channels large enough -+ * to accommodate both the Rx distribution size -+ * and the max number of Tx confirmation queues -+ */ -+ return max_t(int, dpaa2_eth_queue_count(priv), -+ priv->dpni_attrs.max_senders); -+} -+ -+void check_cls_support(struct dpaa2_eth_priv *priv); -+ -+#endif /* __DPAA2_H */ -diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-ethtool.c b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-ethtool.c -new file mode 100644 -index 0000000..1d792cd ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-ethtool.c -@@ -0,0 +1,732 @@ -+/* Copyright 2014-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 "dpni.h" /* DPNI_LINK_OPT_* */ -+#include "dpaa2-eth.h" -+ -+/* To be kept in sync with 'enum dpni_counter' */ -+char dpaa2_ethtool_stats[][ETH_GSTRING_LEN] = { -+ "rx frames", -+ "rx bytes", -+ /* rx frames filtered/policed */ -+ "rx filtered frames", -+ /* rx frames dropped with errors */ -+ "rx discarded frames", -+ "rx mcast frames", -+ "rx mcast bytes", -+ "rx bcast frames", -+ "rx bcast bytes", -+ "tx frames", -+ "tx bytes", -+ /* tx frames dropped with errors */ -+ "tx discarded frames", -+}; -+ -+#define DPAA2_ETH_NUM_STATS ARRAY_SIZE(dpaa2_ethtool_stats) -+ -+/* To be kept in sync with 'struct dpaa2_eth_drv_stats' */ -+char dpaa2_ethtool_extras[][ETH_GSTRING_LEN] = { -+ /* per-cpu stats */ -+ -+ "tx conf frames", -+ "tx conf bytes", -+ "tx sg frames", -+ "tx sg bytes", -+ "rx sg frames", -+ "rx sg bytes", -+ /* how many times we had to retry the enqueue command */ -+ "enqueue portal busy", -+ -+ /* Channel stats */ -+ /* How many times we had to retry the volatile dequeue command */ -+ "dequeue portal busy", -+ "channel pull errors", -+ /* Number of notifications received */ -+ "cdan", -+#ifdef CONFIG_FSL_QBMAN_DEBUG -+ /* FQ stats */ -+ "rx pending frames", -+ "rx pending bytes", -+ "tx conf pending frames", -+ "tx conf pending bytes", -+ "buffer count" -+#endif -+}; -+ -+#define DPAA2_ETH_NUM_EXTRA_STATS ARRAY_SIZE(dpaa2_ethtool_extras) -+ -+static void dpaa2_eth_get_drvinfo(struct net_device *net_dev, -+ struct ethtool_drvinfo *drvinfo) -+{ -+ strlcpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver)); -+ strlcpy(drvinfo->version, VERSION, sizeof(drvinfo->version)); -+ strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); -+ strlcpy(drvinfo->bus_info, dev_name(net_dev->dev.parent->parent), -+ sizeof(drvinfo->bus_info)); -+} -+ -+static int dpaa2_eth_get_settings(struct net_device *net_dev, -+ struct ethtool_cmd *cmd) -+{ -+ struct dpni_link_state state = {0}; -+ int err = 0; -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ -+ err = dpni_get_link_state(priv->mc_io, 0, priv->mc_token, &state); -+ if (err) { -+ netdev_err(net_dev, "ERROR %d getting link state", err); -+ goto out; -+ } -+ -+ /* At the moment, we have no way of interrogating the DPMAC -+ * from the DPNI side - and for that matter there may exist -+ * no DPMAC at all. So for now we just don't report anything -+ * beyond the DPNI attributes. -+ */ -+ if (state.options & DPNI_LINK_OPT_AUTONEG) -+ cmd->autoneg = AUTONEG_ENABLE; -+ if (!(state.options & DPNI_LINK_OPT_HALF_DUPLEX)) -+ cmd->duplex = DUPLEX_FULL; -+ ethtool_cmd_speed_set(cmd, state.rate); -+ -+out: -+ return err; -+} -+ -+static int dpaa2_eth_set_settings(struct net_device *net_dev, -+ struct ethtool_cmd *cmd) -+{ -+ struct dpni_link_cfg cfg = {0}; -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ int err = 0; -+ -+ netdev_dbg(net_dev, "Setting link parameters..."); -+ -+ /* Due to a temporary MC limitation, the DPNI must be down -+ * in order to be able to change link settings. Taking steps to let -+ * the user know that. -+ */ -+ if (netif_running(net_dev)) { -+ netdev_info(net_dev, "Sorry, interface must be brought down first.\n"); -+ return -EACCES; -+ } -+ -+ cfg.rate = ethtool_cmd_speed(cmd); -+ if (cmd->autoneg == AUTONEG_ENABLE) -+ cfg.options |= DPNI_LINK_OPT_AUTONEG; -+ else -+ cfg.options &= ~DPNI_LINK_OPT_AUTONEG; -+ if (cmd->duplex == DUPLEX_HALF) -+ cfg.options |= DPNI_LINK_OPT_HALF_DUPLEX; -+ else -+ cfg.options &= ~DPNI_LINK_OPT_HALF_DUPLEX; -+ -+ err = dpni_set_link_cfg(priv->mc_io, 0, priv->mc_token, &cfg); -+ if (err) -+ /* ethtool will be loud enough if we return an error; no point -+ * in putting our own error message on the console by default -+ */ -+ netdev_dbg(net_dev, "ERROR %d setting link cfg", err); -+ -+ return err; -+} -+ -+static void dpaa2_eth_get_strings(struct net_device *netdev, u32 stringset, -+ u8 *data) -+{ -+ u8 *p = data; -+ int i; -+ -+ switch (stringset) { -+ case ETH_SS_STATS: -+ for (i = 0; i < DPAA2_ETH_NUM_STATS; i++) { -+ strlcpy(p, dpaa2_ethtool_stats[i], ETH_GSTRING_LEN); -+ p += ETH_GSTRING_LEN; -+ } -+ for (i = 0; i < DPAA2_ETH_NUM_EXTRA_STATS; i++) { -+ strlcpy(p, dpaa2_ethtool_extras[i], ETH_GSTRING_LEN); -+ p += ETH_GSTRING_LEN; -+ } -+ break; -+ } -+} -+ -+static int dpaa2_eth_get_sset_count(struct net_device *net_dev, int sset) -+{ -+ switch (sset) { -+ case ETH_SS_STATS: /* ethtool_get_stats(), ethtool_get_drvinfo() */ -+ return DPAA2_ETH_NUM_STATS + DPAA2_ETH_NUM_EXTRA_STATS; -+ default: -+ return -EOPNOTSUPP; -+ } -+} -+ -+/** Fill in hardware counters, as returned by MC. -+ */ -+static void dpaa2_eth_get_ethtool_stats(struct net_device *net_dev, -+ struct ethtool_stats *stats, -+ u64 *data) -+{ -+ int i; /* Current index in the data array */ -+ int j, k, err; -+ -+#ifdef CONFIG_FSL_QBMAN_DEBUG -+ u32 fcnt, bcnt; -+ u32 fcnt_rx_total = 0, fcnt_tx_total = 0; -+ u32 bcnt_rx_total = 0, bcnt_tx_total = 0; -+ u32 buf_cnt; -+#endif -+ u64 cdan = 0; -+ u64 portal_busy = 0, pull_err = 0; -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ struct dpaa2_eth_drv_stats *extras; -+ struct dpaa2_eth_ch_stats *ch_stats; -+ -+ memset(data, 0, -+ sizeof(u64) * (DPAA2_ETH_NUM_STATS + DPAA2_ETH_NUM_EXTRA_STATS)); -+ -+ /* Print standard counters, from DPNI statistics */ -+ for (i = 0; i < DPAA2_ETH_NUM_STATS; i++) { -+ err = dpni_get_counter(priv->mc_io, 0, priv->mc_token, i, -+ data + i); -+ if (err != 0) -+ netdev_warn(net_dev, "Err %d getting DPNI counter %d", -+ err, i); -+ } -+ -+ /* Print per-cpu extra stats */ -+ for_each_online_cpu(k) { -+ extras = per_cpu_ptr(priv->percpu_extras, k); -+ for (j = 0; j < sizeof(*extras) / sizeof(__u64); j++) -+ *((__u64 *)data + i + j) += *((__u64 *)extras + j); -+ } -+ i += j; -+ -+ /* We may be using fewer DPIOs than actual CPUs */ -+ for_each_cpu(j, &priv->dpio_cpumask) { -+ ch_stats = &priv->channel[j]->stats; -+ cdan += ch_stats->cdan; -+ portal_busy += ch_stats->dequeue_portal_busy; -+ pull_err += ch_stats->pull_err; -+ } -+ -+ *(data + i++) = portal_busy; -+ *(data + i++) = pull_err; -+ *(data + i++) = cdan; -+ -+#ifdef CONFIG_FSL_QBMAN_DEBUG -+ for (j = 0; j < priv->num_fqs; j++) { -+ /* Print FQ instantaneous counts */ -+ err = dpaa2_io_query_fq_count(NULL, priv->fq[j].fqid, -+ &fcnt, &bcnt); -+ if (err) { -+ netdev_warn(net_dev, "FQ query error %d", err); -+ return; -+ } -+ -+ if (priv->fq[j].type == DPAA2_TX_CONF_FQ) { -+ fcnt_tx_total += fcnt; -+ bcnt_tx_total += bcnt; -+ } else { -+ fcnt_rx_total += fcnt; -+ bcnt_rx_total += bcnt; -+ } -+ } -+ *(data + i++) = fcnt_rx_total; -+ *(data + i++) = bcnt_rx_total; -+ *(data + i++) = fcnt_tx_total; -+ *(data + i++) = bcnt_tx_total; -+ -+ err = dpaa2_io_query_bp_count(NULL, priv->dpbp_attrs.bpid, &buf_cnt); -+ if (err) { -+ netdev_warn(net_dev, "Buffer count query error %d\n", err); -+ return; -+ } -+ *(data + i++) = buf_cnt; -+#endif -+} -+ -+static int cls_key_off(struct dpaa2_eth_priv *priv, int prot, int field) -+{ -+ int i, off = 0; -+ -+ for (i = 0; i < priv->num_hash_fields; i++) { -+ if (priv->hash_fields[i].cls_prot == prot && -+ priv->hash_fields[i].cls_field == field) -+ return off; -+ off += priv->hash_fields[i].size; -+ } -+ -+ return -1; -+} -+ -+static u8 cls_key_size(struct dpaa2_eth_priv *priv) -+{ -+ u8 i, size = 0; -+ -+ for (i = 0; i < priv->num_hash_fields; i++) -+ size += priv->hash_fields[i].size; -+ -+ return size; -+} -+ -+void check_cls_support(struct dpaa2_eth_priv *priv) -+{ -+ u8 key_size = cls_key_size(priv); -+ struct device *dev = priv->net_dev->dev.parent; -+ -+ if (dpaa2_eth_hash_enabled(priv)) { -+ if (priv->dpni_attrs.max_dist_key_size < key_size) { -+ dev_dbg(dev, "max_dist_key_size = %d, expected %d. Hashing and steering are disabled\n", -+ priv->dpni_attrs.max_dist_key_size, -+ key_size); -+ goto disable_cls; -+ } -+ if (priv->num_hash_fields > DPKG_MAX_NUM_OF_EXTRACTS) { -+ dev_dbg(dev, "Too many key fields (max = %d). Hashing and steering are disabled\n", -+ DPKG_MAX_NUM_OF_EXTRACTS); -+ goto disable_cls; -+ } -+ } -+ -+ if (dpaa2_eth_fs_enabled(priv)) { -+ if (!dpaa2_eth_hash_enabled(priv)) { -+ dev_dbg(dev, "DPNI_OPT_DIST_HASH option missing. Steering is disabled\n"); -+ goto disable_cls; -+ } -+ if (!dpaa2_eth_fs_mask_enabled(priv)) { -+ dev_dbg(dev, "Key masks not supported. Steering is disabled\n"); -+ goto disable_fs; -+ } -+ } -+ -+ return; -+ -+disable_cls: -+ priv->dpni_attrs.options &= ~DPNI_OPT_DIST_HASH; -+disable_fs: -+ priv->dpni_attrs.options &= ~(DPNI_OPT_DIST_FS | -+ DPNI_OPT_FS_MASK_SUPPORT); -+} -+ -+static int prep_l4_rule(struct dpaa2_eth_priv *priv, -+ struct ethtool_tcpip4_spec *l4_value, -+ struct ethtool_tcpip4_spec *l4_mask, -+ void *key, void *mask, u8 l4_proto) -+{ -+ int offset; -+ -+ if (l4_mask->tos) { -+ netdev_err(priv->net_dev, "ToS is not supported for IPv4 L4\n"); -+ return -EOPNOTSUPP; -+ } -+ -+ if (l4_mask->ip4src) { -+ offset = cls_key_off(priv, NET_PROT_IP, NH_FLD_IP_SRC); -+ *(u32 *)(key + offset) = l4_value->ip4src; -+ *(u32 *)(mask + offset) = l4_mask->ip4src; -+ } -+ -+ if (l4_mask->ip4dst) { -+ offset = cls_key_off(priv, NET_PROT_IP, NH_FLD_IP_DST); -+ *(u32 *)(key + offset) = l4_value->ip4dst; -+ *(u32 *)(mask + offset) = l4_mask->ip4dst; -+ } -+ -+ if (l4_mask->psrc) { -+ offset = cls_key_off(priv, NET_PROT_UDP, NH_FLD_UDP_PORT_SRC); -+ *(u32 *)(key + offset) = l4_value->psrc; -+ *(u32 *)(mask + offset) = l4_mask->psrc; -+ } -+ -+ if (l4_mask->pdst) { -+ offset = cls_key_off(priv, NET_PROT_UDP, NH_FLD_UDP_PORT_DST); -+ *(u32 *)(key + offset) = l4_value->pdst; -+ *(u32 *)(mask + offset) = l4_mask->pdst; -+ } -+ -+ /* Only apply the rule for the user-specified L4 protocol -+ * and if ethertype matches IPv4 -+ */ -+ offset = cls_key_off(priv, NET_PROT_ETH, NH_FLD_ETH_TYPE); -+ *(u16 *)(key + offset) = htons(ETH_P_IP); -+ *(u16 *)(mask + offset) = 0xFFFF; -+ -+ offset = cls_key_off(priv, NET_PROT_IP, NH_FLD_IP_PROTO); -+ *(u8 *)(key + offset) = l4_proto; -+ *(u8 *)(mask + offset) = 0xFF; -+ -+ /* TODO: check IP version */ -+ -+ return 0; -+} -+ -+static int prep_eth_rule(struct dpaa2_eth_priv *priv, -+ struct ethhdr *eth_value, struct ethhdr *eth_mask, -+ void *key, void *mask) -+{ -+ int offset; -+ -+ if (eth_mask->h_proto) { -+ netdev_err(priv->net_dev, "Ethertype is not supported!\n"); -+ return -EOPNOTSUPP; -+ } -+ -+ if (!is_zero_ether_addr(eth_mask->h_source)) { -+ offset = cls_key_off(priv, NET_PROT_ETH, NH_FLD_ETH_SA); -+ ether_addr_copy(key + offset, eth_value->h_source); -+ ether_addr_copy(mask + offset, eth_mask->h_source); -+ } -+ -+ if (!is_zero_ether_addr(eth_mask->h_dest)) { -+ offset = cls_key_off(priv, NET_PROT_ETH, NH_FLD_ETH_DA); -+ ether_addr_copy(key + offset, eth_value->h_dest); -+ ether_addr_copy(mask + offset, eth_mask->h_dest); -+ } -+ -+ return 0; -+} -+ -+static int prep_user_ip_rule(struct dpaa2_eth_priv *priv, -+ struct ethtool_usrip4_spec *uip_value, -+ struct ethtool_usrip4_spec *uip_mask, -+ void *key, void *mask) -+{ -+ int offset; -+ -+ if (uip_mask->tos) -+ return -EOPNOTSUPP; -+ -+ if (uip_mask->ip4src) { -+ offset = cls_key_off(priv, NET_PROT_IP, NH_FLD_IP_SRC); -+ *(u32 *)(key + offset) = uip_value->ip4src; -+ *(u32 *)(mask + offset) = uip_mask->ip4src; -+ } -+ -+ if (uip_mask->ip4dst) { -+ offset = cls_key_off(priv, NET_PROT_IP, NH_FLD_IP_DST); -+ *(u32 *)(key + offset) = uip_value->ip4dst; -+ *(u32 *)(mask + offset) = uip_mask->ip4dst; -+ } -+ -+ if (uip_mask->proto) { -+ offset = cls_key_off(priv, NET_PROT_IP, NH_FLD_IP_PROTO); -+ *(u32 *)(key + offset) = uip_value->proto; -+ *(u32 *)(mask + offset) = uip_mask->proto; -+ } -+ if (uip_mask->l4_4_bytes) { -+ offset = cls_key_off(priv, NET_PROT_UDP, NH_FLD_UDP_PORT_SRC); -+ *(u16 *)(key + offset) = uip_value->l4_4_bytes << 16; -+ *(u16 *)(mask + offset) = uip_mask->l4_4_bytes << 16; -+ -+ offset = cls_key_off(priv, NET_PROT_UDP, NH_FLD_UDP_PORT_DST); -+ *(u16 *)(key + offset) = uip_value->l4_4_bytes & 0xFFFF; -+ *(u16 *)(mask + offset) = uip_mask->l4_4_bytes & 0xFFFF; -+ } -+ -+ /* Ethertype must be IP */ -+ offset = cls_key_off(priv, NET_PROT_ETH, NH_FLD_ETH_TYPE); -+ *(u16 *)(key + offset) = htons(ETH_P_IP); -+ *(u16 *)(mask + offset) = 0xFFFF; -+ -+ return 0; -+} -+ -+static int prep_ext_rule(struct dpaa2_eth_priv *priv, -+ struct ethtool_flow_ext *ext_value, -+ struct ethtool_flow_ext *ext_mask, -+ void *key, void *mask) -+{ -+ int offset; -+ -+ if (ext_mask->vlan_etype) -+ return -EOPNOTSUPP; -+ -+ if (ext_mask->vlan_tci) { -+ offset = cls_key_off(priv, NET_PROT_VLAN, NH_FLD_VLAN_TCI); -+ *(u16 *)(key + offset) = ext_value->vlan_tci; -+ *(u16 *)(mask + offset) = ext_mask->vlan_tci; -+ } -+ -+ return 0; -+} -+ -+static int prep_mac_ext_rule(struct dpaa2_eth_priv *priv, -+ struct ethtool_flow_ext *ext_value, -+ struct ethtool_flow_ext *ext_mask, -+ void *key, void *mask) -+{ -+ int offset; -+ -+ if (!is_zero_ether_addr(ext_mask->h_dest)) { -+ offset = cls_key_off(priv, NET_PROT_ETH, NH_FLD_ETH_DA); -+ ether_addr_copy(key + offset, ext_value->h_dest); -+ ether_addr_copy(mask + offset, ext_mask->h_dest); -+ } -+ -+ return 0; -+} -+ -+static int prep_cls_rule(struct net_device *net_dev, -+ struct ethtool_rx_flow_spec *fs, -+ void *key) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ const u8 key_size = cls_key_size(priv); -+ void *msk = key + key_size; -+ int err; -+ -+ memset(key, 0, key_size * 2); -+ -+ switch (fs->flow_type & 0xff) { -+ case TCP_V4_FLOW: -+ err = prep_l4_rule(priv, &fs->h_u.tcp_ip4_spec, -+ &fs->m_u.tcp_ip4_spec, key, msk, -+ IPPROTO_TCP); -+ break; -+ case UDP_V4_FLOW: -+ err = prep_l4_rule(priv, &fs->h_u.udp_ip4_spec, -+ &fs->m_u.udp_ip4_spec, key, msk, -+ IPPROTO_UDP); -+ break; -+ case SCTP_V4_FLOW: -+ err = prep_l4_rule(priv, &fs->h_u.sctp_ip4_spec, -+ &fs->m_u.sctp_ip4_spec, key, msk, -+ IPPROTO_SCTP); -+ break; -+ case ETHER_FLOW: -+ err = prep_eth_rule(priv, &fs->h_u.ether_spec, -+ &fs->m_u.ether_spec, key, msk); -+ break; -+ case IP_USER_FLOW: -+ err = prep_user_ip_rule(priv, &fs->h_u.usr_ip4_spec, -+ &fs->m_u.usr_ip4_spec, key, msk); -+ break; -+ default: -+ /* TODO: AH, ESP */ -+ return -EOPNOTSUPP; -+ } -+ if (err) -+ return err; -+ -+ if (fs->flow_type & FLOW_EXT) { -+ err = prep_ext_rule(priv, &fs->h_ext, &fs->m_ext, key, msk); -+ if (err) -+ return err; -+ } -+ -+ if (fs->flow_type & FLOW_MAC_EXT) { -+ err = prep_mac_ext_rule(priv, &fs->h_ext, &fs->m_ext, key, msk); -+ if (err) -+ return err; -+ } -+ -+ return 0; -+} -+ -+static int do_cls(struct net_device *net_dev, -+ struct ethtool_rx_flow_spec *fs, -+ bool add) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ struct device *dev = net_dev->dev.parent; -+ const int rule_cnt = DPAA2_CLASSIFIER_ENTRY_COUNT; -+ struct dpni_rule_cfg rule_cfg; -+ void *dma_mem; -+ int err = 0; -+ -+ if (!dpaa2_eth_fs_enabled(priv)) { -+ netdev_err(net_dev, "dev does not support steering!\n"); -+ /* dev doesn't support steering */ -+ return -EOPNOTSUPP; -+ } -+ -+ if ((fs->ring_cookie != RX_CLS_FLOW_DISC && -+ fs->ring_cookie >= dpaa2_eth_queue_count(priv)) || -+ fs->location >= rule_cnt) -+ return -EINVAL; -+ -+ memset(&rule_cfg, 0, sizeof(rule_cfg)); -+ rule_cfg.key_size = cls_key_size(priv); -+ -+ /* allocate twice the key size, for the actual key and for mask */ -+ dma_mem = kzalloc(rule_cfg.key_size * 2, GFP_DMA | GFP_KERNEL); -+ if (!dma_mem) -+ return -ENOMEM; -+ -+ err = prep_cls_rule(net_dev, fs, dma_mem); -+ if (err) -+ goto err_free_mem; -+ -+ rule_cfg.key_iova = dma_map_single(dev, dma_mem, -+ rule_cfg.key_size * 2, -+ DMA_TO_DEVICE); -+ -+ rule_cfg.mask_iova = rule_cfg.key_iova + rule_cfg.key_size; -+ -+ /* No way to control rule order in firmware */ -+ if (add) -+ err = dpni_add_fs_entry(priv->mc_io, 0, priv->mc_token, 0, -+ &rule_cfg, (u16)fs->ring_cookie); -+ else -+ err = dpni_remove_fs_entry(priv->mc_io, 0, priv->mc_token, 0, -+ &rule_cfg); -+ -+ dma_unmap_single(dev, rule_cfg.key_iova, -+ rule_cfg.key_size * 2, DMA_TO_DEVICE); -+ if (err) { -+ netdev_err(net_dev, "dpaa2_add/remove_cls() error %d\n", err); -+ goto err_free_mem; -+ } -+ -+ priv->cls_rule[fs->location].fs = *fs; -+ priv->cls_rule[fs->location].in_use = true; -+ -+err_free_mem: -+ kfree(dma_mem); -+ -+ return err; -+} -+ -+static int add_cls(struct net_device *net_dev, -+ struct ethtool_rx_flow_spec *fs) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ int err; -+ -+ err = do_cls(net_dev, fs, true); -+ if (err) -+ return err; -+ -+ priv->cls_rule[fs->location].in_use = true; -+ priv->cls_rule[fs->location].fs = *fs; -+ -+ return 0; -+} -+ -+static int del_cls(struct net_device *net_dev, int location) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ int err; -+ -+ err = do_cls(net_dev, &priv->cls_rule[location].fs, false); -+ if (err) -+ return err; -+ -+ priv->cls_rule[location].in_use = false; -+ -+ return 0; -+} -+ -+static int dpaa2_eth_set_rxnfc(struct net_device *net_dev, -+ struct ethtool_rxnfc *rxnfc) -+{ -+ int err = 0; -+ -+ switch (rxnfc->cmd) { -+ case ETHTOOL_SRXCLSRLINS: -+ err = add_cls(net_dev, &rxnfc->fs); -+ break; -+ -+ case ETHTOOL_SRXCLSRLDEL: -+ err = del_cls(net_dev, rxnfc->fs.location); -+ break; -+ -+ default: -+ err = -EOPNOTSUPP; -+ } -+ -+ return err; -+} -+ -+static int dpaa2_eth_get_rxnfc(struct net_device *net_dev, -+ struct ethtool_rxnfc *rxnfc, u32 *rule_locs) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ const int rule_cnt = DPAA2_CLASSIFIER_ENTRY_COUNT; -+ int i, j; -+ -+ switch (rxnfc->cmd) { -+ case ETHTOOL_GRXFH: -+ /* we purposely ignore cmd->flow_type, because the hashing key -+ * is the same (and fixed) for all protocols -+ */ -+ rxnfc->data = priv->rx_flow_hash; -+ break; -+ -+ case ETHTOOL_GRXRINGS: -+ rxnfc->data = dpaa2_eth_queue_count(priv); -+ break; -+ -+ case ETHTOOL_GRXCLSRLCNT: -+ for (i = 0, rxnfc->rule_cnt = 0; i < rule_cnt; i++) -+ if (priv->cls_rule[i].in_use) -+ rxnfc->rule_cnt++; -+ rxnfc->data = rule_cnt; -+ break; -+ -+ case ETHTOOL_GRXCLSRULE: -+ if (!priv->cls_rule[rxnfc->fs.location].in_use) -+ return -EINVAL; -+ -+ rxnfc->fs = priv->cls_rule[rxnfc->fs.location].fs; -+ break; -+ -+ case ETHTOOL_GRXCLSRLALL: -+ for (i = 0, j = 0; i < rule_cnt; i++) { -+ if (!priv->cls_rule[i].in_use) -+ continue; -+ if (j == rxnfc->rule_cnt) -+ return -EMSGSIZE; -+ rule_locs[j++] = i; -+ } -+ rxnfc->rule_cnt = j; -+ rxnfc->data = rule_cnt; -+ break; -+ -+ default: -+ return -EOPNOTSUPP; -+ } -+ -+ return 0; -+} -+ -+const struct ethtool_ops dpaa2_ethtool_ops = { -+ .get_drvinfo = dpaa2_eth_get_drvinfo, -+ .get_link = ethtool_op_get_link, -+ .get_settings = dpaa2_eth_get_settings, -+ .set_settings = dpaa2_eth_set_settings, -+ .get_sset_count = dpaa2_eth_get_sset_count, -+ .get_ethtool_stats = dpaa2_eth_get_ethtool_stats, -+ .get_strings = dpaa2_eth_get_strings, -+ .get_rxnfc = dpaa2_eth_get_rxnfc, -+ .set_rxnfc = dpaa2_eth_set_rxnfc, -+}; -diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpkg.h b/drivers/staging/fsl-dpaa2/ethernet/dpkg.h -new file mode 100644 -index 0000000..92ec12b ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/ethernet/dpkg.h -@@ -0,0 +1,175 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef __FSL_DPKG_H_ -+#define __FSL_DPKG_H_ -+ -+#include -+#include "../../fsl-mc/include/net.h" -+ -+/* Data Path Key Generator API -+ * Contains initialization APIs and runtime APIs for the Key Generator -+ */ -+ -+/** Key Generator properties */ -+ -+/** -+ * Number of masks per key extraction -+ */ -+#define DPKG_NUM_OF_MASKS 4 -+/** -+ * Number of extractions per key profile -+ */ -+#define DPKG_MAX_NUM_OF_EXTRACTS 10 -+ -+/** -+ * enum dpkg_extract_from_hdr_type - Selecting extraction by header types -+ * @DPKG_FROM_HDR: Extract selected bytes from header, by offset -+ * @DPKG_FROM_FIELD: Extract selected bytes from header, by offset from field -+ * @DPKG_FULL_FIELD: Extract a full field -+ */ -+enum dpkg_extract_from_hdr_type { -+ DPKG_FROM_HDR = 0, -+ DPKG_FROM_FIELD = 1, -+ DPKG_FULL_FIELD = 2 -+}; -+ -+/** -+ * enum dpkg_extract_type - Enumeration for selecting extraction type -+ * @DPKG_EXTRACT_FROM_HDR: Extract from the header -+ * @DPKG_EXTRACT_FROM_DATA: Extract from data not in specific header -+ * @DPKG_EXTRACT_FROM_PARSE: Extract from parser-result; -+ * e.g. can be used to extract header existence; -+ * please refer to 'Parse Result definition' section in the parser BG -+ */ -+enum dpkg_extract_type { -+ DPKG_EXTRACT_FROM_HDR = 0, -+ DPKG_EXTRACT_FROM_DATA = 1, -+ DPKG_EXTRACT_FROM_PARSE = 3 -+}; -+ -+/** -+ * struct dpkg_mask - A structure for defining a single extraction mask -+ * @mask: Byte mask for the extracted content -+ * @offset: Offset within the extracted content -+ */ -+struct dpkg_mask { -+ uint8_t mask; -+ uint8_t offset; -+}; -+ -+/** -+ * struct dpkg_extract - A structure for defining a single extraction -+ * @type: Determines how the union below is interpreted: -+ * DPKG_EXTRACT_FROM_HDR: selects 'from_hdr'; -+ * DPKG_EXTRACT_FROM_DATA: selects 'from_data'; -+ * DPKG_EXTRACT_FROM_PARSE: selects 'from_parse' -+ * @extract: Selects extraction method -+ * @num_of_byte_masks: Defines the number of valid entries in the array below; -+ * This is also the number of bytes to be used as masks -+ * @masks: Masks parameters -+ */ -+struct dpkg_extract { -+ enum dpkg_extract_type type; -+ /** -+ * union extract - Selects extraction method -+ * @from_hdr - Used when 'type = DPKG_EXTRACT_FROM_HDR' -+ * @from_data - Used when 'type = DPKG_EXTRACT_FROM_DATA' -+ * @from_parse - Used when 'type = DPKG_EXTRACT_FROM_PARSE' -+ */ -+ union { -+ /** -+ * struct from_hdr - Used when 'type = DPKG_EXTRACT_FROM_HDR' -+ * @prot: Any of the supported headers -+ * @type: Defines the type of header extraction: -+ * DPKG_FROM_HDR: use size & offset below; -+ * DPKG_FROM_FIELD: use field, size and offset below; -+ * DPKG_FULL_FIELD: use field below -+ * @field: One of the supported fields (NH_FLD_) -+ * -+ * @size: Size in bytes -+ * @offset: Byte offset -+ * @hdr_index: Clear for cases not listed below; -+ * Used for protocols that may have more than a single -+ * header, 0 indicates an outer header; -+ * Supported protocols (possible values): -+ * NET_PROT_VLAN (0, HDR_INDEX_LAST); -+ * NET_PROT_MPLS (0, 1, HDR_INDEX_LAST); -+ * NET_PROT_IP(0, HDR_INDEX_LAST); -+ * NET_PROT_IPv4(0, HDR_INDEX_LAST); -+ * NET_PROT_IPv6(0, HDR_INDEX_LAST); -+ */ -+ -+ struct { -+ enum net_prot prot; -+ enum dpkg_extract_from_hdr_type type; -+ uint32_t field; -+ uint8_t size; -+ uint8_t offset; -+ uint8_t hdr_index; -+ } from_hdr; -+ /** -+ * struct from_data - Used when 'type = DPKG_EXTRACT_FROM_DATA' -+ * @size: Size in bytes -+ * @offset: Byte offset -+ */ -+ struct { -+ uint8_t size; -+ uint8_t offset; -+ } from_data; -+ -+ /** -+ * struct from_parse - Used when 'type = DPKG_EXTRACT_FROM_PARSE' -+ * @size: Size in bytes -+ * @offset: Byte offset -+ */ -+ struct { -+ uint8_t size; -+ uint8_t offset; -+ } from_parse; -+ } extract; -+ -+ uint8_t num_of_byte_masks; -+ struct dpkg_mask masks[DPKG_NUM_OF_MASKS]; -+}; -+ -+/** -+ * struct dpkg_profile_cfg - A structure for defining a full Key Generation -+ * profile (rule) -+ * @num_extracts: Defines the number of valid entries in the array below -+ * @extracts: Array of required extractions -+ */ -+struct dpkg_profile_cfg { -+ uint8_t num_extracts; -+ struct dpkg_extract extracts[DPKG_MAX_NUM_OF_EXTRACTS]; -+}; -+ -+#endif /* __FSL_DPKG_H_ */ -diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpni-cmd.h b/drivers/staging/fsl-dpaa2/ethernet/dpni-cmd.h -new file mode 100644 -index 0000000..c0f8af0 ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/ethernet/dpni-cmd.h -@@ -0,0 +1,1058 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef _FSL_DPNI_CMD_H -+#define _FSL_DPNI_CMD_H -+ -+/* DPNI Version */ -+#define DPNI_VER_MAJOR 6 -+#define DPNI_VER_MINOR 0 -+ -+/* Command IDs */ -+#define DPNI_CMDID_OPEN 0x801 -+#define DPNI_CMDID_CLOSE 0x800 -+#define DPNI_CMDID_CREATE 0x901 -+#define DPNI_CMDID_DESTROY 0x900 -+ -+#define DPNI_CMDID_ENABLE 0x002 -+#define DPNI_CMDID_DISABLE 0x003 -+#define DPNI_CMDID_GET_ATTR 0x004 -+#define DPNI_CMDID_RESET 0x005 -+#define DPNI_CMDID_IS_ENABLED 0x006 -+ -+#define DPNI_CMDID_SET_IRQ 0x010 -+#define DPNI_CMDID_GET_IRQ 0x011 -+#define DPNI_CMDID_SET_IRQ_ENABLE 0x012 -+#define DPNI_CMDID_GET_IRQ_ENABLE 0x013 -+#define DPNI_CMDID_SET_IRQ_MASK 0x014 -+#define DPNI_CMDID_GET_IRQ_MASK 0x015 -+#define DPNI_CMDID_GET_IRQ_STATUS 0x016 -+#define DPNI_CMDID_CLEAR_IRQ_STATUS 0x017 -+ -+#define DPNI_CMDID_SET_POOLS 0x200 -+#define DPNI_CMDID_GET_RX_BUFFER_LAYOUT 0x201 -+#define DPNI_CMDID_SET_RX_BUFFER_LAYOUT 0x202 -+#define DPNI_CMDID_GET_TX_BUFFER_LAYOUT 0x203 -+#define DPNI_CMDID_SET_TX_BUFFER_LAYOUT 0x204 -+#define DPNI_CMDID_SET_TX_CONF_BUFFER_LAYOUT 0x205 -+#define DPNI_CMDID_GET_TX_CONF_BUFFER_LAYOUT 0x206 -+#define DPNI_CMDID_SET_L3_CHKSUM_VALIDATION 0x207 -+#define DPNI_CMDID_GET_L3_CHKSUM_VALIDATION 0x208 -+#define DPNI_CMDID_SET_L4_CHKSUM_VALIDATION 0x209 -+#define DPNI_CMDID_GET_L4_CHKSUM_VALIDATION 0x20A -+#define DPNI_CMDID_SET_ERRORS_BEHAVIOR 0x20B -+#define DPNI_CMDID_SET_TX_CONF_REVOKE 0x20C -+ -+#define DPNI_CMDID_GET_QDID 0x210 -+#define DPNI_CMDID_GET_SP_INFO 0x211 -+#define DPNI_CMDID_GET_TX_DATA_OFFSET 0x212 -+#define DPNI_CMDID_GET_COUNTER 0x213 -+#define DPNI_CMDID_SET_COUNTER 0x214 -+#define DPNI_CMDID_GET_LINK_STATE 0x215 -+#define DPNI_CMDID_SET_MAX_FRAME_LENGTH 0x216 -+#define DPNI_CMDID_GET_MAX_FRAME_LENGTH 0x217 -+#define DPNI_CMDID_SET_MTU 0x218 -+#define DPNI_CMDID_GET_MTU 0x219 -+#define DPNI_CMDID_SET_LINK_CFG 0x21A -+#define DPNI_CMDID_SET_TX_SHAPING 0x21B -+ -+#define DPNI_CMDID_SET_MCAST_PROMISC 0x220 -+#define DPNI_CMDID_GET_MCAST_PROMISC 0x221 -+#define DPNI_CMDID_SET_UNICAST_PROMISC 0x222 -+#define DPNI_CMDID_GET_UNICAST_PROMISC 0x223 -+#define DPNI_CMDID_SET_PRIM_MAC 0x224 -+#define DPNI_CMDID_GET_PRIM_MAC 0x225 -+#define DPNI_CMDID_ADD_MAC_ADDR 0x226 -+#define DPNI_CMDID_REMOVE_MAC_ADDR 0x227 -+#define DPNI_CMDID_CLR_MAC_FILTERS 0x228 -+ -+#define DPNI_CMDID_SET_VLAN_FILTERS 0x230 -+#define DPNI_CMDID_ADD_VLAN_ID 0x231 -+#define DPNI_CMDID_REMOVE_VLAN_ID 0x232 -+#define DPNI_CMDID_CLR_VLAN_FILTERS 0x233 -+ -+#define DPNI_CMDID_SET_RX_TC_DIST 0x235 -+#define DPNI_CMDID_SET_TX_FLOW 0x236 -+#define DPNI_CMDID_GET_TX_FLOW 0x237 -+#define DPNI_CMDID_SET_RX_FLOW 0x238 -+#define DPNI_CMDID_GET_RX_FLOW 0x239 -+#define DPNI_CMDID_SET_RX_ERR_QUEUE 0x23A -+#define DPNI_CMDID_GET_RX_ERR_QUEUE 0x23B -+ -+#define DPNI_CMDID_SET_RX_TC_POLICING 0x23E -+#define DPNI_CMDID_SET_RX_TC_EARLY_DROP 0x23F -+ -+#define DPNI_CMDID_SET_QOS_TBL 0x240 -+#define DPNI_CMDID_ADD_QOS_ENT 0x241 -+#define DPNI_CMDID_REMOVE_QOS_ENT 0x242 -+#define DPNI_CMDID_CLR_QOS_TBL 0x243 -+#define DPNI_CMDID_ADD_FS_ENT 0x244 -+#define DPNI_CMDID_REMOVE_FS_ENT 0x245 -+#define DPNI_CMDID_CLR_FS_ENT 0x246 -+#define DPNI_CMDID_SET_VLAN_INSERTION 0x247 -+#define DPNI_CMDID_SET_VLAN_REMOVAL 0x248 -+#define DPNI_CMDID_SET_IPR 0x249 -+#define DPNI_CMDID_SET_IPF 0x24A -+ -+#define DPNI_CMDID_SET_TX_SELECTION 0x250 -+#define DPNI_CMDID_GET_RX_TC_POLICING 0x251 -+#define DPNI_CMDID_GET_RX_TC_EARLY_DROP 0x252 -+#define DPNI_CMDID_SET_RX_TC_CONGESTION_NOTIFICATION 0x253 -+#define DPNI_CMDID_GET_RX_TC_CONGESTION_NOTIFICATION 0x254 -+#define DPNI_CMDID_SET_TX_TC_CONGESTION_NOTIFICATION 0x255 -+#define DPNI_CMDID_GET_TX_TC_CONGESTION_NOTIFICATION 0x256 -+#define DPNI_CMDID_SET_TX_CONF 0x257 -+#define DPNI_CMDID_GET_TX_CONF 0x258 -+#define DPNI_CMDID_SET_TX_CONF_CONGESTION_NOTIFICATION 0x259 -+#define DPNI_CMDID_GET_TX_CONF_CONGESTION_NOTIFICATION 0x25A -+#define DPNI_CMDID_SET_TX_TC_EARLY_DROP 0x25B -+#define DPNI_CMDID_GET_TX_TC_EARLY_DROP 0x25C -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_OPEN(cmd, dpni_id) \ -+ MC_CMD_OP(cmd, 0, 0, 32, int, dpni_id) -+ -+#define DPNI_PREP_EXTENDED_CFG(ext, cfg) \ -+do { \ -+ MC_PREP_OP(ext, 0, 0, 16, uint16_t, cfg->tc_cfg[0].max_dist); \ -+ MC_PREP_OP(ext, 0, 16, 16, uint16_t, cfg->tc_cfg[0].max_fs_entries); \ -+ MC_PREP_OP(ext, 0, 32, 16, uint16_t, cfg->tc_cfg[1].max_dist); \ -+ MC_PREP_OP(ext, 0, 48, 16, uint16_t, cfg->tc_cfg[1].max_fs_entries); \ -+ MC_PREP_OP(ext, 1, 0, 16, uint16_t, cfg->tc_cfg[2].max_dist); \ -+ MC_PREP_OP(ext, 1, 16, 16, uint16_t, cfg->tc_cfg[2].max_fs_entries); \ -+ MC_PREP_OP(ext, 1, 32, 16, uint16_t, cfg->tc_cfg[3].max_dist); \ -+ MC_PREP_OP(ext, 1, 48, 16, uint16_t, cfg->tc_cfg[3].max_fs_entries); \ -+ MC_PREP_OP(ext, 2, 0, 16, uint16_t, cfg->tc_cfg[4].max_dist); \ -+ MC_PREP_OP(ext, 2, 16, 16, uint16_t, cfg->tc_cfg[4].max_fs_entries); \ -+ MC_PREP_OP(ext, 2, 32, 16, uint16_t, cfg->tc_cfg[5].max_dist); \ -+ MC_PREP_OP(ext, 2, 48, 16, uint16_t, cfg->tc_cfg[5].max_fs_entries); \ -+ MC_PREP_OP(ext, 3, 0, 16, uint16_t, cfg->tc_cfg[6].max_dist); \ -+ MC_PREP_OP(ext, 3, 16, 16, uint16_t, cfg->tc_cfg[6].max_fs_entries); \ -+ MC_PREP_OP(ext, 3, 32, 16, uint16_t, cfg->tc_cfg[7].max_dist); \ -+ MC_PREP_OP(ext, 3, 48, 16, uint16_t, cfg->tc_cfg[7].max_fs_entries); \ -+ MC_PREP_OP(ext, 4, 0, 16, uint16_t, \ -+ cfg->ipr_cfg.max_open_frames_ipv4); \ -+ MC_PREP_OP(ext, 4, 16, 16, uint16_t, \ -+ cfg->ipr_cfg.max_open_frames_ipv6); \ -+ MC_PREP_OP(ext, 4, 32, 16, uint16_t, \ -+ cfg->ipr_cfg.max_reass_frm_size); \ -+ MC_PREP_OP(ext, 5, 0, 16, uint16_t, \ -+ cfg->ipr_cfg.min_frag_size_ipv4); \ -+ MC_PREP_OP(ext, 5, 16, 16, uint16_t, \ -+ cfg->ipr_cfg.min_frag_size_ipv6); \ -+} while (0) -+ -+#define DPNI_EXT_EXTENDED_CFG(ext, cfg) \ -+do { \ -+ MC_EXT_OP(ext, 0, 0, 16, uint16_t, cfg->tc_cfg[0].max_dist); \ -+ MC_EXT_OP(ext, 0, 16, 16, uint16_t, cfg->tc_cfg[0].max_fs_entries); \ -+ MC_EXT_OP(ext, 0, 32, 16, uint16_t, cfg->tc_cfg[1].max_dist); \ -+ MC_EXT_OP(ext, 0, 48, 16, uint16_t, cfg->tc_cfg[1].max_fs_entries); \ -+ MC_EXT_OP(ext, 1, 0, 16, uint16_t, cfg->tc_cfg[2].max_dist); \ -+ MC_EXT_OP(ext, 1, 16, 16, uint16_t, cfg->tc_cfg[2].max_fs_entries); \ -+ MC_EXT_OP(ext, 1, 32, 16, uint16_t, cfg->tc_cfg[3].max_dist); \ -+ MC_EXT_OP(ext, 1, 48, 16, uint16_t, cfg->tc_cfg[3].max_fs_entries); \ -+ MC_EXT_OP(ext, 2, 0, 16, uint16_t, cfg->tc_cfg[4].max_dist); \ -+ MC_EXT_OP(ext, 2, 16, 16, uint16_t, cfg->tc_cfg[4].max_fs_entries); \ -+ MC_EXT_OP(ext, 2, 32, 16, uint16_t, cfg->tc_cfg[5].max_dist); \ -+ MC_EXT_OP(ext, 2, 48, 16, uint16_t, cfg->tc_cfg[5].max_fs_entries); \ -+ MC_EXT_OP(ext, 3, 0, 16, uint16_t, cfg->tc_cfg[6].max_dist); \ -+ MC_EXT_OP(ext, 3, 16, 16, uint16_t, cfg->tc_cfg[6].max_fs_entries); \ -+ MC_EXT_OP(ext, 3, 32, 16, uint16_t, cfg->tc_cfg[7].max_dist); \ -+ MC_EXT_OP(ext, 3, 48, 16, uint16_t, cfg->tc_cfg[7].max_fs_entries); \ -+ MC_EXT_OP(ext, 4, 0, 16, uint16_t, \ -+ cfg->ipr_cfg.max_open_frames_ipv4); \ -+ MC_EXT_OP(ext, 4, 16, 16, uint16_t, \ -+ cfg->ipr_cfg.max_open_frames_ipv6); \ -+ MC_EXT_OP(ext, 4, 32, 16, uint16_t, \ -+ cfg->ipr_cfg.max_reass_frm_size); \ -+ MC_EXT_OP(ext, 5, 0, 16, uint16_t, \ -+ cfg->ipr_cfg.min_frag_size_ipv4); \ -+ MC_EXT_OP(ext, 5, 16, 16, uint16_t, \ -+ cfg->ipr_cfg.min_frag_size_ipv6); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_CREATE(cmd, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, cfg->adv.max_tcs); \ -+ MC_CMD_OP(cmd, 0, 8, 8, uint8_t, cfg->adv.max_senders); \ -+ MC_CMD_OP(cmd, 0, 16, 8, uint8_t, cfg->mac_addr[5]); \ -+ MC_CMD_OP(cmd, 0, 24, 8, uint8_t, cfg->mac_addr[4]); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, cfg->mac_addr[3]); \ -+ MC_CMD_OP(cmd, 0, 40, 8, uint8_t, cfg->mac_addr[2]); \ -+ MC_CMD_OP(cmd, 0, 48, 8, uint8_t, cfg->mac_addr[1]); \ -+ MC_CMD_OP(cmd, 0, 56, 8, uint8_t, cfg->mac_addr[0]); \ -+ MC_CMD_OP(cmd, 1, 0, 32, uint32_t, cfg->adv.options); \ -+ MC_CMD_OP(cmd, 2, 0, 8, uint8_t, cfg->adv.max_unicast_filters); \ -+ MC_CMD_OP(cmd, 2, 8, 8, uint8_t, cfg->adv.max_multicast_filters); \ -+ MC_CMD_OP(cmd, 2, 16, 8, uint8_t, cfg->adv.max_vlan_filters); \ -+ MC_CMD_OP(cmd, 2, 24, 8, uint8_t, cfg->adv.max_qos_entries); \ -+ MC_CMD_OP(cmd, 2, 32, 8, uint8_t, cfg->adv.max_qos_key_size); \ -+ MC_CMD_OP(cmd, 2, 48, 8, uint8_t, cfg->adv.max_dist_key_size); \ -+ MC_CMD_OP(cmd, 2, 56, 8, enum net_prot, cfg->adv.start_hdr); \ -+ MC_CMD_OP(cmd, 4, 48, 8, uint8_t, cfg->adv.max_policers); \ -+ MC_CMD_OP(cmd, 4, 56, 8, uint8_t, cfg->adv.max_congestion_ctrl); \ -+ MC_CMD_OP(cmd, 5, 0, 64, uint64_t, cfg->adv.ext_cfg_iova); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_POOLS(cmd, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, cfg->num_dpbp); \ -+ MC_CMD_OP(cmd, 0, 8, 1, int, cfg->pools[0].backup_pool); \ -+ MC_CMD_OP(cmd, 0, 9, 1, int, cfg->pools[1].backup_pool); \ -+ MC_CMD_OP(cmd, 0, 10, 1, int, cfg->pools[2].backup_pool); \ -+ MC_CMD_OP(cmd, 0, 11, 1, int, cfg->pools[3].backup_pool); \ -+ MC_CMD_OP(cmd, 0, 12, 1, int, cfg->pools[4].backup_pool); \ -+ MC_CMD_OP(cmd, 0, 13, 1, int, cfg->pools[5].backup_pool); \ -+ MC_CMD_OP(cmd, 0, 14, 1, int, cfg->pools[6].backup_pool); \ -+ MC_CMD_OP(cmd, 0, 15, 1, int, cfg->pools[7].backup_pool); \ -+ MC_CMD_OP(cmd, 0, 32, 32, int, cfg->pools[0].dpbp_id); \ -+ MC_CMD_OP(cmd, 4, 32, 16, uint16_t, cfg->pools[0].buffer_size);\ -+ MC_CMD_OP(cmd, 1, 0, 32, int, cfg->pools[1].dpbp_id); \ -+ MC_CMD_OP(cmd, 4, 48, 16, uint16_t, cfg->pools[1].buffer_size);\ -+ MC_CMD_OP(cmd, 1, 32, 32, int, cfg->pools[2].dpbp_id); \ -+ MC_CMD_OP(cmd, 5, 0, 16, uint16_t, cfg->pools[2].buffer_size);\ -+ MC_CMD_OP(cmd, 2, 0, 32, int, cfg->pools[3].dpbp_id); \ -+ MC_CMD_OP(cmd, 5, 16, 16, uint16_t, cfg->pools[3].buffer_size);\ -+ MC_CMD_OP(cmd, 2, 32, 32, int, cfg->pools[4].dpbp_id); \ -+ MC_CMD_OP(cmd, 5, 32, 16, uint16_t, cfg->pools[4].buffer_size);\ -+ MC_CMD_OP(cmd, 3, 0, 32, int, cfg->pools[5].dpbp_id); \ -+ MC_CMD_OP(cmd, 5, 48, 16, uint16_t, cfg->pools[5].buffer_size);\ -+ MC_CMD_OP(cmd, 3, 32, 32, int, cfg->pools[6].dpbp_id); \ -+ MC_CMD_OP(cmd, 6, 0, 16, uint16_t, cfg->pools[6].buffer_size);\ -+ MC_CMD_OP(cmd, 4, 0, 32, int, cfg->pools[7].dpbp_id); \ -+ MC_CMD_OP(cmd, 6, 16, 16, uint16_t, cfg->pools[7].buffer_size);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_IS_ENABLED(cmd, en) \ -+ MC_RSP_OP(cmd, 0, 0, 1, int, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_IRQ(cmd, irq_index, irq_cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, irq_cfg->val); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index); \ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, irq_cfg->addr); \ -+ MC_CMD_OP(cmd, 2, 0, 32, int, irq_cfg->irq_num); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_GET_IRQ(cmd, irq_index) \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_IRQ(cmd, type, irq_cfg) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, irq_cfg->val); \ -+ MC_RSP_OP(cmd, 1, 0, 64, uint64_t, irq_cfg->addr); \ -+ MC_RSP_OP(cmd, 2, 0, 32, int, irq_cfg->irq_num); \ -+ MC_RSP_OP(cmd, 2, 32, 32, int, type); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_IRQ_ENABLE(cmd, irq_index, en) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, en); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_GET_IRQ_ENABLE(cmd, irq_index) \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_IRQ_ENABLE(cmd, en) \ -+ MC_RSP_OP(cmd, 0, 0, 8, uint8_t, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_IRQ_MASK(cmd, irq_index, mask) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, mask); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_GET_IRQ_MASK(cmd, irq_index) \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_IRQ_MASK(cmd, mask) \ -+ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, mask) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_GET_IRQ_STATUS(cmd, irq_index, status) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, status);\ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_IRQ_STATUS(cmd, status) \ -+ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, status) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_CLEAR_IRQ_STATUS(cmd, irq_index, status) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, status); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_GET_ATTR(cmd, attr) \ -+ MC_CMD_OP(cmd, 6, 0, 64, uint64_t, attr->ext_cfg_iova) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_ATTR(cmd, attr) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 32, int, attr->id);\ -+ MC_RSP_OP(cmd, 0, 32, 8, uint8_t, attr->max_tcs); \ -+ MC_RSP_OP(cmd, 0, 40, 8, uint8_t, attr->max_senders); \ -+ MC_RSP_OP(cmd, 0, 48, 8, enum net_prot, attr->start_hdr); \ -+ MC_RSP_OP(cmd, 1, 0, 32, uint32_t, attr->options); \ -+ MC_RSP_OP(cmd, 2, 0, 8, uint8_t, attr->max_unicast_filters); \ -+ MC_RSP_OP(cmd, 2, 8, 8, uint8_t, attr->max_multicast_filters);\ -+ MC_RSP_OP(cmd, 2, 16, 8, uint8_t, attr->max_vlan_filters); \ -+ MC_RSP_OP(cmd, 2, 24, 8, uint8_t, attr->max_qos_entries); \ -+ MC_RSP_OP(cmd, 2, 32, 8, uint8_t, attr->max_qos_key_size); \ -+ MC_RSP_OP(cmd, 2, 40, 8, uint8_t, attr->max_dist_key_size); \ -+ MC_RSP_OP(cmd, 4, 48, 8, uint8_t, attr->max_policers); \ -+ MC_RSP_OP(cmd, 4, 56, 8, uint8_t, attr->max_congestion_ctrl); \ -+ MC_RSP_OP(cmd, 5, 32, 16, uint16_t, attr->version.major);\ -+ MC_RSP_OP(cmd, 5, 48, 16, uint16_t, attr->version.minor);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_ERRORS_BEHAVIOR(cmd, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, cfg->errors); \ -+ MC_CMD_OP(cmd, 0, 32, 4, enum dpni_error_action, cfg->error_action); \ -+ MC_CMD_OP(cmd, 0, 36, 1, int, cfg->set_frame_annotation); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_RX_BUFFER_LAYOUT(cmd, layout) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 16, uint16_t, layout->private_data_size); \ -+ MC_RSP_OP(cmd, 0, 16, 16, uint16_t, layout->data_align); \ -+ MC_RSP_OP(cmd, 1, 0, 1, int, layout->pass_timestamp); \ -+ MC_RSP_OP(cmd, 1, 1, 1, int, layout->pass_parser_result); \ -+ MC_RSP_OP(cmd, 1, 2, 1, int, layout->pass_frame_status); \ -+ MC_RSP_OP(cmd, 1, 16, 16, uint16_t, layout->data_head_room); \ -+ MC_RSP_OP(cmd, 1, 32, 16, uint16_t, layout->data_tail_room); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_RX_BUFFER_LAYOUT(cmd, layout) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 16, uint16_t, layout->private_data_size); \ -+ MC_CMD_OP(cmd, 0, 16, 16, uint16_t, layout->data_align); \ -+ MC_CMD_OP(cmd, 0, 32, 32, uint32_t, layout->options); \ -+ MC_CMD_OP(cmd, 1, 0, 1, int, layout->pass_timestamp); \ -+ MC_CMD_OP(cmd, 1, 1, 1, int, layout->pass_parser_result); \ -+ MC_CMD_OP(cmd, 1, 2, 1, int, layout->pass_frame_status); \ -+ MC_CMD_OP(cmd, 1, 16, 16, uint16_t, layout->data_head_room); \ -+ MC_CMD_OP(cmd, 1, 32, 16, uint16_t, layout->data_tail_room); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_TX_BUFFER_LAYOUT(cmd, layout) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 16, uint16_t, layout->private_data_size); \ -+ MC_RSP_OP(cmd, 0, 16, 16, uint16_t, layout->data_align); \ -+ MC_RSP_OP(cmd, 1, 0, 1, int, layout->pass_timestamp); \ -+ MC_RSP_OP(cmd, 1, 1, 1, int, layout->pass_parser_result); \ -+ MC_RSP_OP(cmd, 1, 2, 1, int, layout->pass_frame_status); \ -+ MC_RSP_OP(cmd, 1, 16, 16, uint16_t, layout->data_head_room); \ -+ MC_RSP_OP(cmd, 1, 32, 16, uint16_t, layout->data_tail_room); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_TX_BUFFER_LAYOUT(cmd, layout) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 16, uint16_t, layout->private_data_size); \ -+ MC_CMD_OP(cmd, 0, 16, 16, uint16_t, layout->data_align); \ -+ MC_CMD_OP(cmd, 0, 32, 32, uint32_t, layout->options); \ -+ MC_CMD_OP(cmd, 1, 0, 1, int, layout->pass_timestamp); \ -+ MC_CMD_OP(cmd, 1, 1, 1, int, layout->pass_parser_result); \ -+ MC_CMD_OP(cmd, 1, 2, 1, int, layout->pass_frame_status); \ -+ MC_CMD_OP(cmd, 1, 16, 16, uint16_t, layout->data_head_room); \ -+ MC_CMD_OP(cmd, 1, 32, 16, uint16_t, layout->data_tail_room); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_TX_CONF_BUFFER_LAYOUT(cmd, layout) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 16, uint16_t, layout->private_data_size); \ -+ MC_RSP_OP(cmd, 0, 16, 16, uint16_t, layout->data_align); \ -+ MC_RSP_OP(cmd, 1, 0, 1, int, layout->pass_timestamp); \ -+ MC_RSP_OP(cmd, 1, 1, 1, int, layout->pass_parser_result); \ -+ MC_RSP_OP(cmd, 1, 2, 1, int, layout->pass_frame_status); \ -+ MC_RSP_OP(cmd, 1, 16, 16, uint16_t, layout->data_head_room); \ -+ MC_RSP_OP(cmd, 1, 32, 16, uint16_t, layout->data_tail_room); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_TX_CONF_BUFFER_LAYOUT(cmd, layout) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 16, uint16_t, layout->private_data_size); \ -+ MC_CMD_OP(cmd, 0, 16, 16, uint16_t, layout->data_align); \ -+ MC_CMD_OP(cmd, 0, 32, 32, uint32_t, layout->options); \ -+ MC_CMD_OP(cmd, 1, 0, 1, int, layout->pass_timestamp); \ -+ MC_CMD_OP(cmd, 1, 1, 1, int, layout->pass_parser_result); \ -+ MC_CMD_OP(cmd, 1, 2, 1, int, layout->pass_frame_status); \ -+ MC_CMD_OP(cmd, 1, 16, 16, uint16_t, layout->data_head_room); \ -+ MC_CMD_OP(cmd, 1, 32, 16, uint16_t, layout->data_tail_room); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_L3_CHKSUM_VALIDATION(cmd, en) \ -+ MC_CMD_OP(cmd, 0, 0, 1, int, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_L3_CHKSUM_VALIDATION(cmd, en) \ -+ MC_RSP_OP(cmd, 0, 0, 1, int, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_L4_CHKSUM_VALIDATION(cmd, en) \ -+ MC_CMD_OP(cmd, 0, 0, 1, int, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_L4_CHKSUM_VALIDATION(cmd, en) \ -+ MC_RSP_OP(cmd, 0, 0, 1, int, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_QDID(cmd, qdid) \ -+ MC_RSP_OP(cmd, 0, 0, 16, uint16_t, qdid) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_SP_INFO(cmd, sp_info) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 16, uint16_t, sp_info->spids[0]); \ -+ MC_RSP_OP(cmd, 0, 16, 16, uint16_t, sp_info->spids[1]); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_TX_DATA_OFFSET(cmd, data_offset) \ -+ MC_RSP_OP(cmd, 0, 0, 16, uint16_t, data_offset) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_GET_COUNTER(cmd, counter) \ -+ MC_CMD_OP(cmd, 0, 0, 16, enum dpni_counter, counter) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_COUNTER(cmd, value) \ -+ MC_RSP_OP(cmd, 1, 0, 64, uint64_t, value) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_COUNTER(cmd, counter, value) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 16, enum dpni_counter, counter); \ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, value); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_LINK_CFG(cmd, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 1, 0, 32, uint32_t, cfg->rate);\ -+ MC_CMD_OP(cmd, 2, 0, 64, uint64_t, cfg->options);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_LINK_STATE(cmd, state) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 32, 1, int, state->up);\ -+ MC_RSP_OP(cmd, 1, 0, 32, uint32_t, state->rate);\ -+ MC_RSP_OP(cmd, 2, 0, 64, uint64_t, state->options);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_TX_SHAPING(cmd, tx_shaper) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 16, uint16_t, tx_shaper->max_burst_size);\ -+ MC_CMD_OP(cmd, 1, 0, 32, uint32_t, tx_shaper->rate_limit);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_MAX_FRAME_LENGTH(cmd, max_frame_length) \ -+ MC_CMD_OP(cmd, 0, 0, 16, uint16_t, max_frame_length) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_MAX_FRAME_LENGTH(cmd, max_frame_length) \ -+ MC_RSP_OP(cmd, 0, 0, 16, uint16_t, max_frame_length) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_MTU(cmd, mtu) \ -+ MC_CMD_OP(cmd, 0, 0, 16, uint16_t, mtu) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_MTU(cmd, mtu) \ -+ MC_RSP_OP(cmd, 0, 0, 16, uint16_t, mtu) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_MULTICAST_PROMISC(cmd, en) \ -+ MC_CMD_OP(cmd, 0, 0, 1, int, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_MULTICAST_PROMISC(cmd, en) \ -+ MC_RSP_OP(cmd, 0, 0, 1, int, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_UNICAST_PROMISC(cmd, en) \ -+ MC_CMD_OP(cmd, 0, 0, 1, int, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_UNICAST_PROMISC(cmd, en) \ -+ MC_RSP_OP(cmd, 0, 0, 1, int, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_PRIMARY_MAC_ADDR(cmd, mac_addr) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 16, 8, uint8_t, mac_addr[5]); \ -+ MC_CMD_OP(cmd, 0, 24, 8, uint8_t, mac_addr[4]); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, mac_addr[3]); \ -+ MC_CMD_OP(cmd, 0, 40, 8, uint8_t, mac_addr[2]); \ -+ MC_CMD_OP(cmd, 0, 48, 8, uint8_t, mac_addr[1]); \ -+ MC_CMD_OP(cmd, 0, 56, 8, uint8_t, mac_addr[0]); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_PRIMARY_MAC_ADDR(cmd, mac_addr) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 16, 8, uint8_t, mac_addr[5]); \ -+ MC_RSP_OP(cmd, 0, 24, 8, uint8_t, mac_addr[4]); \ -+ MC_RSP_OP(cmd, 0, 32, 8, uint8_t, mac_addr[3]); \ -+ MC_RSP_OP(cmd, 0, 40, 8, uint8_t, mac_addr[2]); \ -+ MC_RSP_OP(cmd, 0, 48, 8, uint8_t, mac_addr[1]); \ -+ MC_RSP_OP(cmd, 0, 56, 8, uint8_t, mac_addr[0]); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_ADD_MAC_ADDR(cmd, mac_addr) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 16, 8, uint8_t, mac_addr[5]); \ -+ MC_CMD_OP(cmd, 0, 24, 8, uint8_t, mac_addr[4]); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, mac_addr[3]); \ -+ MC_CMD_OP(cmd, 0, 40, 8, uint8_t, mac_addr[2]); \ -+ MC_CMD_OP(cmd, 0, 48, 8, uint8_t, mac_addr[1]); \ -+ MC_CMD_OP(cmd, 0, 56, 8, uint8_t, mac_addr[0]); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_REMOVE_MAC_ADDR(cmd, mac_addr) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 16, 8, uint8_t, mac_addr[5]); \ -+ MC_CMD_OP(cmd, 0, 24, 8, uint8_t, mac_addr[4]); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, mac_addr[3]); \ -+ MC_CMD_OP(cmd, 0, 40, 8, uint8_t, mac_addr[2]); \ -+ MC_CMD_OP(cmd, 0, 48, 8, uint8_t, mac_addr[1]); \ -+ MC_CMD_OP(cmd, 0, 56, 8, uint8_t, mac_addr[0]); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_CLEAR_MAC_FILTERS(cmd, unicast, multicast) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 1, int, unicast); \ -+ MC_CMD_OP(cmd, 0, 1, 1, int, multicast); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_VLAN_FILTERS(cmd, en) \ -+ MC_CMD_OP(cmd, 0, 0, 1, int, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_ADD_VLAN_ID(cmd, vlan_id) \ -+ MC_CMD_OP(cmd, 0, 32, 16, uint16_t, vlan_id) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_REMOVE_VLAN_ID(cmd, vlan_id) \ -+ MC_CMD_OP(cmd, 0, 32, 16, uint16_t, vlan_id) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_TX_SELECTION(cmd, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 16, uint16_t, cfg->tc_sched[0].delta_bandwidth);\ -+ MC_CMD_OP(cmd, 0, 16, 4, enum dpni_tx_schedule_mode, \ -+ cfg->tc_sched[0].mode); \ -+ MC_CMD_OP(cmd, 0, 32, 16, uint16_t, cfg->tc_sched[1].delta_bandwidth);\ -+ MC_CMD_OP(cmd, 0, 48, 4, enum dpni_tx_schedule_mode, \ -+ cfg->tc_sched[1].mode); \ -+ MC_CMD_OP(cmd, 1, 0, 16, uint16_t, cfg->tc_sched[2].delta_bandwidth);\ -+ MC_CMD_OP(cmd, 1, 16, 4, enum dpni_tx_schedule_mode, \ -+ cfg->tc_sched[2].mode); \ -+ MC_CMD_OP(cmd, 1, 32, 16, uint16_t, cfg->tc_sched[3].delta_bandwidth);\ -+ MC_CMD_OP(cmd, 1, 48, 4, enum dpni_tx_schedule_mode, \ -+ cfg->tc_sched[3].mode); \ -+ MC_CMD_OP(cmd, 2, 0, 16, uint16_t, cfg->tc_sched[4].delta_bandwidth);\ -+ MC_CMD_OP(cmd, 2, 16, 4, enum dpni_tx_schedule_mode, \ -+ cfg->tc_sched[4].mode); \ -+ MC_CMD_OP(cmd, 2, 32, 16, uint16_t, cfg->tc_sched[5].delta_bandwidth);\ -+ MC_CMD_OP(cmd, 2, 48, 4, enum dpni_tx_schedule_mode, \ -+ cfg->tc_sched[5].mode); \ -+ MC_CMD_OP(cmd, 3, 0, 16, uint16_t, cfg->tc_sched[6].delta_bandwidth);\ -+ MC_CMD_OP(cmd, 3, 16, 4, enum dpni_tx_schedule_mode, \ -+ cfg->tc_sched[6].mode); \ -+ MC_CMD_OP(cmd, 3, 32, 16, uint16_t, cfg->tc_sched[7].delta_bandwidth);\ -+ MC_CMD_OP(cmd, 3, 48, 4, enum dpni_tx_schedule_mode, \ -+ cfg->tc_sched[7].mode); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_RX_TC_DIST(cmd, tc_id, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 16, uint16_t, cfg->dist_size); \ -+ MC_CMD_OP(cmd, 0, 16, 8, uint8_t, tc_id); \ -+ MC_CMD_OP(cmd, 0, 24, 4, enum dpni_dist_mode, cfg->dist_mode); \ -+ MC_CMD_OP(cmd, 0, 28, 4, enum dpni_fs_miss_action, \ -+ cfg->fs_cfg.miss_action); \ -+ MC_CMD_OP(cmd, 0, 48, 16, uint16_t, cfg->fs_cfg.default_flow_id); \ -+ MC_CMD_OP(cmd, 6, 0, 64, uint64_t, cfg->key_cfg_iova); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_TX_FLOW(cmd, flow_id, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 43, 1, int, cfg->l3_chksum_gen);\ -+ MC_CMD_OP(cmd, 0, 44, 1, int, cfg->l4_chksum_gen);\ -+ MC_CMD_OP(cmd, 0, 45, 1, int, cfg->use_common_tx_conf_queue);\ -+ MC_CMD_OP(cmd, 0, 48, 16, uint16_t, flow_id);\ -+ MC_CMD_OP(cmd, 2, 0, 32, uint32_t, cfg->options);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_SET_TX_FLOW(cmd, flow_id) \ -+ MC_RSP_OP(cmd, 0, 48, 16, uint16_t, flow_id) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_GET_TX_FLOW(cmd, flow_id) \ -+ MC_CMD_OP(cmd, 0, 48, 16, uint16_t, flow_id) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_TX_FLOW(cmd, attr) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 43, 1, int, attr->l3_chksum_gen);\ -+ MC_RSP_OP(cmd, 0, 44, 1, int, attr->l4_chksum_gen);\ -+ MC_RSP_OP(cmd, 0, 45, 1, int, attr->use_common_tx_conf_queue);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_RX_FLOW(cmd, tc_id, flow_id, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, int, cfg->dest_cfg.dest_id); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, cfg->dest_cfg.priority);\ -+ MC_CMD_OP(cmd, 0, 40, 2, enum dpni_dest, cfg->dest_cfg.dest_type);\ -+ MC_CMD_OP(cmd, 0, 42, 1, int, cfg->order_preservation_en);\ -+ MC_CMD_OP(cmd, 0, 48, 16, uint16_t, flow_id); \ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, cfg->user_ctx); \ -+ MC_CMD_OP(cmd, 2, 16, 8, uint8_t, tc_id); \ -+ MC_CMD_OP(cmd, 2, 32, 32, uint32_t, cfg->options); \ -+ MC_CMD_OP(cmd, 3, 0, 4, enum dpni_flc_type, cfg->flc_cfg.flc_type); \ -+ MC_CMD_OP(cmd, 3, 4, 4, enum dpni_stash_size, \ -+ cfg->flc_cfg.frame_data_size);\ -+ MC_CMD_OP(cmd, 3, 8, 4, enum dpni_stash_size, \ -+ cfg->flc_cfg.flow_context_size);\ -+ MC_CMD_OP(cmd, 3, 32, 32, uint32_t, cfg->flc_cfg.options);\ -+ MC_CMD_OP(cmd, 4, 0, 64, uint64_t, cfg->flc_cfg.flow_context);\ -+ MC_CMD_OP(cmd, 5, 0, 32, uint32_t, cfg->tail_drop_threshold); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_GET_RX_FLOW(cmd, tc_id, flow_id) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 16, 8, uint8_t, tc_id); \ -+ MC_CMD_OP(cmd, 0, 48, 16, uint16_t, flow_id); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_RX_FLOW(cmd, attr) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 32, int, attr->dest_cfg.dest_id); \ -+ MC_RSP_OP(cmd, 0, 32, 8, uint8_t, attr->dest_cfg.priority);\ -+ MC_RSP_OP(cmd, 0, 40, 2, enum dpni_dest, attr->dest_cfg.dest_type); \ -+ MC_RSP_OP(cmd, 0, 42, 1, int, attr->order_preservation_en);\ -+ MC_RSP_OP(cmd, 1, 0, 64, uint64_t, attr->user_ctx); \ -+ MC_RSP_OP(cmd, 2, 0, 32, uint32_t, attr->tail_drop_threshold); \ -+ MC_RSP_OP(cmd, 2, 32, 32, uint32_t, attr->fqid); \ -+ MC_RSP_OP(cmd, 3, 0, 4, enum dpni_flc_type, attr->flc_cfg.flc_type); \ -+ MC_RSP_OP(cmd, 3, 4, 4, enum dpni_stash_size, \ -+ attr->flc_cfg.frame_data_size);\ -+ MC_RSP_OP(cmd, 3, 8, 4, enum dpni_stash_size, \ -+ attr->flc_cfg.flow_context_size);\ -+ MC_RSP_OP(cmd, 3, 32, 32, uint32_t, attr->flc_cfg.options);\ -+ MC_RSP_OP(cmd, 4, 0, 64, uint64_t, attr->flc_cfg.flow_context);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_RX_ERR_QUEUE(cmd, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, int, cfg->dest_cfg.dest_id); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, cfg->dest_cfg.priority);\ -+ MC_CMD_OP(cmd, 0, 40, 2, enum dpni_dest, cfg->dest_cfg.dest_type);\ -+ MC_CMD_OP(cmd, 0, 42, 1, int, cfg->order_preservation_en);\ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, cfg->user_ctx); \ -+ MC_CMD_OP(cmd, 2, 0, 32, uint32_t, cfg->options); \ -+ MC_CMD_OP(cmd, 2, 32, 32, uint32_t, cfg->tail_drop_threshold); \ -+ MC_CMD_OP(cmd, 3, 0, 4, enum dpni_flc_type, cfg->flc_cfg.flc_type); \ -+ MC_CMD_OP(cmd, 3, 4, 4, enum dpni_stash_size, \ -+ cfg->flc_cfg.frame_data_size);\ -+ MC_CMD_OP(cmd, 3, 8, 4, enum dpni_stash_size, \ -+ cfg->flc_cfg.flow_context_size);\ -+ MC_CMD_OP(cmd, 3, 32, 32, uint32_t, cfg->flc_cfg.options);\ -+ MC_CMD_OP(cmd, 4, 0, 64, uint64_t, cfg->flc_cfg.flow_context);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_RX_ERR_QUEUE(cmd, attr) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 32, int, attr->dest_cfg.dest_id); \ -+ MC_RSP_OP(cmd, 0, 32, 8, uint8_t, attr->dest_cfg.priority);\ -+ MC_RSP_OP(cmd, 0, 40, 2, enum dpni_dest, attr->dest_cfg.dest_type);\ -+ MC_RSP_OP(cmd, 0, 42, 1, int, attr->order_preservation_en);\ -+ MC_RSP_OP(cmd, 1, 0, 64, uint64_t, attr->user_ctx); \ -+ MC_RSP_OP(cmd, 2, 0, 32, uint32_t, attr->tail_drop_threshold); \ -+ MC_RSP_OP(cmd, 2, 32, 32, uint32_t, attr->fqid); \ -+ MC_RSP_OP(cmd, 3, 0, 4, enum dpni_flc_type, attr->flc_cfg.flc_type); \ -+ MC_RSP_OP(cmd, 3, 4, 4, enum dpni_stash_size, \ -+ attr->flc_cfg.frame_data_size);\ -+ MC_RSP_OP(cmd, 3, 8, 4, enum dpni_stash_size, \ -+ attr->flc_cfg.flow_context_size);\ -+ MC_RSP_OP(cmd, 3, 32, 32, uint32_t, attr->flc_cfg.options);\ -+ MC_RSP_OP(cmd, 4, 0, 64, uint64_t, attr->flc_cfg.flow_context);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_TX_CONF_REVOKE(cmd, revoke) \ -+ MC_CMD_OP(cmd, 0, 0, 1, int, revoke) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_QOS_TABLE(cmd, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, cfg->default_tc); \ -+ MC_CMD_OP(cmd, 0, 40, 1, int, cfg->discard_on_miss); \ -+ MC_CMD_OP(cmd, 6, 0, 64, uint64_t, cfg->key_cfg_iova); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_ADD_QOS_ENTRY(cmd, cfg, tc_id) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 16, 8, uint8_t, tc_id); \ -+ MC_CMD_OP(cmd, 0, 24, 8, uint8_t, cfg->key_size); \ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, cfg->key_iova); \ -+ MC_CMD_OP(cmd, 2, 0, 64, uint64_t, cfg->mask_iova); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_REMOVE_QOS_ENTRY(cmd, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 24, 8, uint8_t, cfg->key_size); \ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, cfg->key_iova); \ -+ MC_CMD_OP(cmd, 2, 0, 64, uint64_t, cfg->mask_iova); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_ADD_FS_ENTRY(cmd, tc_id, cfg, flow_id) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 16, 8, uint8_t, tc_id); \ -+ MC_CMD_OP(cmd, 0, 48, 16, uint16_t, flow_id); \ -+ MC_CMD_OP(cmd, 0, 24, 8, uint8_t, cfg->key_size); \ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, cfg->key_iova); \ -+ MC_CMD_OP(cmd, 2, 0, 64, uint64_t, cfg->mask_iova); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_REMOVE_FS_ENTRY(cmd, tc_id, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 16, 8, uint8_t, tc_id); \ -+ MC_CMD_OP(cmd, 0, 24, 8, uint8_t, cfg->key_size); \ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, cfg->key_iova); \ -+ MC_CMD_OP(cmd, 2, 0, 64, uint64_t, cfg->mask_iova); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_CLEAR_FS_ENTRIES(cmd, tc_id) \ -+ MC_CMD_OP(cmd, 0, 16, 8, uint8_t, tc_id) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_VLAN_INSERTION(cmd, en) \ -+ MC_CMD_OP(cmd, 0, 0, 1, int, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_VLAN_REMOVAL(cmd, en) \ -+ MC_CMD_OP(cmd, 0, 0, 1, int, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_IPR(cmd, en) \ -+ MC_CMD_OP(cmd, 0, 0, 1, int, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_IPF(cmd, en) \ -+ MC_CMD_OP(cmd, 0, 0, 1, int, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_RX_TC_POLICING(cmd, tc_id, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 4, enum dpni_policer_mode, cfg->mode); \ -+ MC_CMD_OP(cmd, 0, 4, 4, enum dpni_policer_color, cfg->default_color); \ -+ MC_CMD_OP(cmd, 0, 8, 4, enum dpni_policer_unit, cfg->units); \ -+ MC_CMD_OP(cmd, 0, 16, 8, uint8_t, tc_id); \ -+ MC_CMD_OP(cmd, 0, 32, 32, uint32_t, cfg->options); \ -+ MC_CMD_OP(cmd, 1, 0, 32, uint32_t, cfg->cir); \ -+ MC_CMD_OP(cmd, 1, 32, 32, uint32_t, cfg->cbs); \ -+ MC_CMD_OP(cmd, 2, 0, 32, uint32_t, cfg->eir); \ -+ MC_CMD_OP(cmd, 2, 32, 32, uint32_t, cfg->ebs);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_GET_RX_TC_POLICING(cmd, tc_id) \ -+ MC_CMD_OP(cmd, 0, 16, 8, uint8_t, tc_id) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_RX_TC_POLICING(cmd, cfg) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 4, enum dpni_policer_mode, cfg->mode); \ -+ MC_RSP_OP(cmd, 0, 4, 4, enum dpni_policer_color, cfg->default_color); \ -+ MC_RSP_OP(cmd, 0, 8, 4, enum dpni_policer_unit, cfg->units); \ -+ MC_RSP_OP(cmd, 0, 32, 32, uint32_t, cfg->options); \ -+ MC_RSP_OP(cmd, 1, 0, 32, uint32_t, cfg->cir); \ -+ MC_RSP_OP(cmd, 1, 32, 32, uint32_t, cfg->cbs); \ -+ MC_RSP_OP(cmd, 2, 0, 32, uint32_t, cfg->eir); \ -+ MC_RSP_OP(cmd, 2, 32, 32, uint32_t, cfg->ebs);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_PREP_EARLY_DROP(ext, cfg) \ -+do { \ -+ MC_PREP_OP(ext, 0, 0, 2, enum dpni_early_drop_mode, cfg->mode); \ -+ MC_PREP_OP(ext, 0, 2, 2, \ -+ enum dpni_congestion_unit, cfg->units); \ -+ MC_PREP_OP(ext, 0, 32, 32, uint32_t, cfg->tail_drop_threshold); \ -+ MC_PREP_OP(ext, 1, 0, 8, uint8_t, cfg->green.drop_probability); \ -+ MC_PREP_OP(ext, 2, 0, 64, uint64_t, cfg->green.max_threshold); \ -+ MC_PREP_OP(ext, 3, 0, 64, uint64_t, cfg->green.min_threshold); \ -+ MC_PREP_OP(ext, 5, 0, 8, uint8_t, cfg->yellow.drop_probability);\ -+ MC_PREP_OP(ext, 6, 0, 64, uint64_t, cfg->yellow.max_threshold); \ -+ MC_PREP_OP(ext, 7, 0, 64, uint64_t, cfg->yellow.min_threshold); \ -+ MC_PREP_OP(ext, 9, 0, 8, uint8_t, cfg->red.drop_probability); \ -+ MC_PREP_OP(ext, 10, 0, 64, uint64_t, cfg->red.max_threshold); \ -+ MC_PREP_OP(ext, 11, 0, 64, uint64_t, cfg->red.min_threshold); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_EXT_EARLY_DROP(ext, cfg) \ -+do { \ -+ MC_EXT_OP(ext, 0, 0, 2, enum dpni_early_drop_mode, cfg->mode); \ -+ MC_EXT_OP(ext, 0, 2, 2, \ -+ enum dpni_congestion_unit, cfg->units); \ -+ MC_EXT_OP(ext, 0, 32, 32, uint32_t, cfg->tail_drop_threshold); \ -+ MC_EXT_OP(ext, 1, 0, 8, uint8_t, cfg->green.drop_probability); \ -+ MC_EXT_OP(ext, 2, 0, 64, uint64_t, cfg->green.max_threshold); \ -+ MC_EXT_OP(ext, 3, 0, 64, uint64_t, cfg->green.min_threshold); \ -+ MC_EXT_OP(ext, 5, 0, 8, uint8_t, cfg->yellow.drop_probability);\ -+ MC_EXT_OP(ext, 6, 0, 64, uint64_t, cfg->yellow.max_threshold); \ -+ MC_EXT_OP(ext, 7, 0, 64, uint64_t, cfg->yellow.min_threshold); \ -+ MC_EXT_OP(ext, 9, 0, 8, uint8_t, cfg->red.drop_probability); \ -+ MC_EXT_OP(ext, 10, 0, 64, uint64_t, cfg->red.max_threshold); \ -+ MC_EXT_OP(ext, 11, 0, 64, uint64_t, cfg->red.min_threshold); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_RX_TC_EARLY_DROP(cmd, tc_id, early_drop_iova) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 8, 8, uint8_t, tc_id); \ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, early_drop_iova); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_GET_RX_TC_EARLY_DROP(cmd, tc_id, early_drop_iova) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 8, 8, uint8_t, tc_id); \ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, early_drop_iova); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_TX_TC_EARLY_DROP(cmd, tc_id, early_drop_iova) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 8, 8, uint8_t, tc_id); \ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, early_drop_iova); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_GET_TX_TC_EARLY_DROP(cmd, tc_id, early_drop_iova) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 8, 8, uint8_t, tc_id); \ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, early_drop_iova); \ -+} while (0) -+ -+#define DPNI_CMD_SET_RX_TC_CONGESTION_NOTIFICATION(cmd, tc_id, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 2, enum dpni_congestion_unit, cfg->units); \ -+ MC_CMD_OP(cmd, 0, 4, 4, enum dpni_dest, cfg->dest_cfg.dest_type); \ -+ MC_CMD_OP(cmd, 0, 8, 8, uint8_t, tc_id); \ -+ MC_CMD_OP(cmd, 0, 16, 8, uint8_t, cfg->dest_cfg.priority); \ -+ MC_CMD_OP(cmd, 1, 0, 32, uint32_t, cfg->threshold_entry); \ -+ MC_CMD_OP(cmd, 1, 32, 32, uint32_t, cfg->threshold_exit); \ -+ MC_CMD_OP(cmd, 2, 0, 16, uint16_t, cfg->options); \ -+ MC_CMD_OP(cmd, 2, 32, 32, int, cfg->dest_cfg.dest_id); \ -+ MC_CMD_OP(cmd, 3, 0, 64, uint64_t, cfg->message_ctx); \ -+ MC_CMD_OP(cmd, 4, 0, 64, uint64_t, cfg->message_iova); \ -+} while (0) -+ -+#define DPNI_CMD_GET_RX_TC_CONGESTION_NOTIFICATION(cmd, tc_id) \ -+ MC_CMD_OP(cmd, 0, 8, 8, uint8_t, tc_id) -+ -+#define DPNI_RSP_GET_RX_TC_CONGESTION_NOTIFICATION(cmd, cfg) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 2, enum dpni_congestion_unit, cfg->units); \ -+ MC_RSP_OP(cmd, 0, 4, 4, enum dpni_dest, cfg->dest_cfg.dest_type); \ -+ MC_RSP_OP(cmd, 0, 16, 8, uint8_t, cfg->dest_cfg.priority); \ -+ MC_RSP_OP(cmd, 1, 0, 32, uint32_t, cfg->threshold_entry); \ -+ MC_RSP_OP(cmd, 1, 32, 32, uint32_t, cfg->threshold_exit); \ -+ MC_RSP_OP(cmd, 2, 0, 16, uint16_t, cfg->options); \ -+ MC_RSP_OP(cmd, 2, 32, 32, int, cfg->dest_cfg.dest_id); \ -+ MC_RSP_OP(cmd, 3, 0, 64, uint64_t, cfg->message_ctx); \ -+ MC_RSP_OP(cmd, 4, 0, 64, uint64_t, cfg->message_iova); \ -+} while (0) -+ -+#define DPNI_CMD_SET_TX_TC_CONGESTION_NOTIFICATION(cmd, tc_id, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 2, enum dpni_congestion_unit, cfg->units); \ -+ MC_CMD_OP(cmd, 0, 4, 4, enum dpni_dest, cfg->dest_cfg.dest_type); \ -+ MC_CMD_OP(cmd, 0, 8, 8, uint8_t, tc_id); \ -+ MC_CMD_OP(cmd, 0, 16, 8, uint8_t, cfg->dest_cfg.priority); \ -+ MC_CMD_OP(cmd, 1, 0, 32, uint32_t, cfg->threshold_entry); \ -+ MC_CMD_OP(cmd, 1, 32, 32, uint32_t, cfg->threshold_exit); \ -+ MC_CMD_OP(cmd, 2, 0, 16, uint16_t, cfg->options); \ -+ MC_CMD_OP(cmd, 2, 32, 32, int, cfg->dest_cfg.dest_id); \ -+ MC_CMD_OP(cmd, 3, 0, 64, uint64_t, cfg->message_ctx); \ -+ MC_CMD_OP(cmd, 4, 0, 64, uint64_t, cfg->message_iova); \ -+} while (0) -+ -+#define DPNI_CMD_GET_TX_TC_CONGESTION_NOTIFICATION(cmd, tc_id) \ -+ MC_CMD_OP(cmd, 0, 8, 8, uint8_t, tc_id) -+ -+#define DPNI_RSP_GET_TX_TC_CONGESTION_NOTIFICATION(cmd, cfg) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 2, enum dpni_congestion_unit, cfg->units); \ -+ MC_RSP_OP(cmd, 0, 4, 4, enum dpni_dest, cfg->dest_cfg.dest_type); \ -+ MC_RSP_OP(cmd, 0, 16, 8, uint8_t, cfg->dest_cfg.priority); \ -+ MC_RSP_OP(cmd, 1, 0, 32, uint32_t, cfg->threshold_entry); \ -+ MC_RSP_OP(cmd, 1, 32, 32, uint32_t, cfg->threshold_exit); \ -+ MC_RSP_OP(cmd, 2, 0, 16, uint16_t, cfg->options); \ -+ MC_RSP_OP(cmd, 2, 32, 32, int, cfg->dest_cfg.dest_id); \ -+ MC_RSP_OP(cmd, 3, 0, 64, uint64_t, cfg->message_ctx); \ -+ MC_RSP_OP(cmd, 4, 0, 64, uint64_t, cfg->message_iova); \ -+} while (0) -+ -+#define DPNI_CMD_SET_TX_CONF(cmd, flow_id, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, cfg->queue_cfg.dest_cfg.priority); \ -+ MC_CMD_OP(cmd, 0, 40, 2, enum dpni_dest, \ -+ cfg->queue_cfg.dest_cfg.dest_type); \ -+ MC_CMD_OP(cmd, 0, 42, 1, int, cfg->errors_only); \ -+ MC_CMD_OP(cmd, 0, 46, 1, int, cfg->queue_cfg.order_preservation_en); \ -+ MC_CMD_OP(cmd, 0, 48, 16, uint16_t, flow_id); \ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, cfg->queue_cfg.user_ctx); \ -+ MC_CMD_OP(cmd, 2, 0, 32, uint32_t, cfg->queue_cfg.options); \ -+ MC_CMD_OP(cmd, 2, 32, 32, int, cfg->queue_cfg.dest_cfg.dest_id); \ -+ MC_CMD_OP(cmd, 3, 0, 32, uint32_t, \ -+ cfg->queue_cfg.tail_drop_threshold); \ -+ MC_CMD_OP(cmd, 4, 0, 4, enum dpni_flc_type, \ -+ cfg->queue_cfg.flc_cfg.flc_type); \ -+ MC_CMD_OP(cmd, 4, 4, 4, enum dpni_stash_size, \ -+ cfg->queue_cfg.flc_cfg.frame_data_size); \ -+ MC_CMD_OP(cmd, 4, 8, 4, enum dpni_stash_size, \ -+ cfg->queue_cfg.flc_cfg.flow_context_size); \ -+ MC_CMD_OP(cmd, 4, 32, 32, uint32_t, cfg->queue_cfg.flc_cfg.options); \ -+ MC_CMD_OP(cmd, 5, 0, 64, uint64_t, \ -+ cfg->queue_cfg.flc_cfg.flow_context); \ -+} while (0) -+ -+#define DPNI_CMD_GET_TX_CONF(cmd, flow_id) \ -+ MC_CMD_OP(cmd, 0, 48, 16, uint16_t, flow_id) -+ -+#define DPNI_RSP_GET_TX_CONF(cmd, attr) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 32, 8, uint8_t, \ -+ attr->queue_attr.dest_cfg.priority); \ -+ MC_RSP_OP(cmd, 0, 40, 2, enum dpni_dest, \ -+ attr->queue_attr.dest_cfg.dest_type); \ -+ MC_RSP_OP(cmd, 0, 42, 1, int, attr->errors_only); \ -+ MC_RSP_OP(cmd, 0, 46, 1, int, \ -+ attr->queue_attr.order_preservation_en); \ -+ MC_RSP_OP(cmd, 1, 0, 64, uint64_t, attr->queue_attr.user_ctx); \ -+ MC_RSP_OP(cmd, 2, 32, 32, int, attr->queue_attr.dest_cfg.dest_id); \ -+ MC_RSP_OP(cmd, 3, 0, 32, uint32_t, \ -+ attr->queue_attr.tail_drop_threshold); \ -+ MC_RSP_OP(cmd, 3, 32, 32, uint32_t, attr->queue_attr.fqid); \ -+ MC_RSP_OP(cmd, 4, 0, 4, enum dpni_flc_type, \ -+ attr->queue_attr.flc_cfg.flc_type); \ -+ MC_RSP_OP(cmd, 4, 4, 4, enum dpni_stash_size, \ -+ attr->queue_attr.flc_cfg.frame_data_size); \ -+ MC_RSP_OP(cmd, 4, 8, 4, enum dpni_stash_size, \ -+ attr->queue_attr.flc_cfg.flow_context_size); \ -+ MC_RSP_OP(cmd, 4, 32, 32, uint32_t, attr->queue_attr.flc_cfg.options); \ -+ MC_RSP_OP(cmd, 5, 0, 64, uint64_t, \ -+ attr->queue_attr.flc_cfg.flow_context); \ -+} while (0) -+ -+#define DPNI_CMD_SET_TX_CONF_CONGESTION_NOTIFICATION(cmd, flow_id, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 2, enum dpni_congestion_unit, cfg->units); \ -+ MC_CMD_OP(cmd, 0, 4, 4, enum dpni_dest, cfg->dest_cfg.dest_type); \ -+ MC_CMD_OP(cmd, 0, 16, 8, uint8_t, cfg->dest_cfg.priority); \ -+ MC_CMD_OP(cmd, 0, 48, 16, uint16_t, flow_id); \ -+ MC_CMD_OP(cmd, 1, 0, 32, uint32_t, cfg->threshold_entry); \ -+ MC_CMD_OP(cmd, 1, 32, 32, uint32_t, cfg->threshold_exit); \ -+ MC_CMD_OP(cmd, 2, 0, 16, uint16_t, cfg->options); \ -+ MC_CMD_OP(cmd, 2, 32, 32, int, cfg->dest_cfg.dest_id); \ -+ MC_CMD_OP(cmd, 3, 0, 64, uint64_t, cfg->message_ctx); \ -+ MC_CMD_OP(cmd, 4, 0, 64, uint64_t, cfg->message_iova); \ -+} while (0) -+ -+#define DPNI_CMD_GET_TX_CONF_CONGESTION_NOTIFICATION(cmd, flow_id) \ -+ MC_CMD_OP(cmd, 0, 48, 16, uint16_t, flow_id) -+ -+#define DPNI_RSP_GET_TX_CONF_CONGESTION_NOTIFICATION(cmd, cfg) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 2, enum dpni_congestion_unit, cfg->units); \ -+ MC_RSP_OP(cmd, 0, 4, 4, enum dpni_dest, cfg->dest_cfg.dest_type); \ -+ MC_RSP_OP(cmd, 0, 16, 8, uint8_t, cfg->dest_cfg.priority); \ -+ MC_RSP_OP(cmd, 1, 0, 32, uint32_t, cfg->threshold_entry); \ -+ MC_RSP_OP(cmd, 1, 32, 32, uint32_t, cfg->threshold_exit); \ -+ MC_RSP_OP(cmd, 2, 0, 16, uint16_t, cfg->options); \ -+ MC_RSP_OP(cmd, 2, 32, 32, int, cfg->dest_cfg.dest_id); \ -+ MC_RSP_OP(cmd, 3, 0, 64, uint64_t, cfg->message_ctx); \ -+ MC_RSP_OP(cmd, 4, 0, 64, uint64_t, cfg->message_iova); \ -+} while (0) -+ -+#endif /* _FSL_DPNI_CMD_H */ -diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpni.c b/drivers/staging/fsl-dpaa2/ethernet/dpni.c -new file mode 100644 -index 0000000..c228ce5 ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/ethernet/dpni.c -@@ -0,0 +1,1907 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#include "../../fsl-mc/include/mc-sys.h" -+#include "../../fsl-mc/include/mc-cmd.h" -+#include "dpni.h" -+#include "dpni-cmd.h" -+ -+int dpni_prepare_key_cfg(const struct dpkg_profile_cfg *cfg, -+ uint8_t *key_cfg_buf) -+{ -+ int i, j; -+ int offset = 0; -+ int param = 1; -+ uint64_t *params = (uint64_t *)key_cfg_buf; -+ -+ if (!key_cfg_buf || !cfg) -+ return -EINVAL; -+ -+ params[0] |= mc_enc(0, 8, cfg->num_extracts); -+ params[0] = cpu_to_le64(params[0]); -+ -+ if (cfg->num_extracts >= DPKG_MAX_NUM_OF_EXTRACTS) -+ return -EINVAL; -+ -+ for (i = 0; i < cfg->num_extracts; i++) { -+ switch (cfg->extracts[i].type) { -+ case DPKG_EXTRACT_FROM_HDR: -+ params[param] |= mc_enc(0, 8, -+ cfg->extracts[i].extract.from_hdr.prot); -+ params[param] |= mc_enc(8, 4, -+ cfg->extracts[i].extract.from_hdr.type); -+ params[param] |= mc_enc(16, 8, -+ cfg->extracts[i].extract.from_hdr.size); -+ params[param] |= mc_enc(24, 8, -+ cfg->extracts[i].extract. -+ from_hdr.offset); -+ params[param] |= mc_enc(32, 32, -+ cfg->extracts[i].extract. -+ from_hdr.field); -+ params[param] = cpu_to_le64(params[param]); -+ param++; -+ params[param] |= mc_enc(0, 8, -+ cfg->extracts[i].extract. -+ from_hdr.hdr_index); -+ break; -+ case DPKG_EXTRACT_FROM_DATA: -+ params[param] |= mc_enc(16, 8, -+ cfg->extracts[i].extract. -+ from_data.size); -+ params[param] |= mc_enc(24, 8, -+ cfg->extracts[i].extract. -+ from_data.offset); -+ params[param] = cpu_to_le64(params[param]); -+ param++; -+ break; -+ case DPKG_EXTRACT_FROM_PARSE: -+ params[param] |= mc_enc(16, 8, -+ cfg->extracts[i].extract. -+ from_parse.size); -+ params[param] |= mc_enc(24, 8, -+ cfg->extracts[i].extract. -+ from_parse.offset); -+ params[param] = cpu_to_le64(params[param]); -+ param++; -+ break; -+ default: -+ return -EINVAL; -+ } -+ params[param] |= mc_enc( -+ 24, 8, cfg->extracts[i].num_of_byte_masks); -+ params[param] |= mc_enc(32, 4, cfg->extracts[i].type); -+ params[param] = cpu_to_le64(params[param]); -+ param++; -+ for (offset = 0, j = 0; -+ j < DPKG_NUM_OF_MASKS; -+ offset += 16, j++) { -+ params[param] |= mc_enc( -+ (offset), 8, cfg->extracts[i].masks[j].mask); -+ params[param] |= mc_enc( -+ (offset + 8), 8, -+ cfg->extracts[i].masks[j].offset); -+ } -+ params[param] = cpu_to_le64(params[param]); -+ param++; -+ } -+ return 0; -+} -+ -+int dpni_prepare_extended_cfg(const struct dpni_extended_cfg *cfg, -+ uint8_t *ext_cfg_buf) -+{ -+ uint64_t *ext_params = (uint64_t *)ext_cfg_buf; -+ -+ DPNI_PREP_EXTENDED_CFG(ext_params, cfg); -+ -+ return 0; -+} -+ -+int dpni_extract_extended_cfg(struct dpni_extended_cfg *cfg, -+ const uint8_t *ext_cfg_buf) -+{ -+ const uint64_t *ext_params = (const uint64_t *)ext_cfg_buf; -+ -+ DPNI_EXT_EXTENDED_CFG(ext_params, cfg); -+ -+ return 0; -+} -+ -+int dpni_open(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ int dpni_id, -+ uint16_t *token) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_OPEN, -+ cmd_flags, -+ 0); -+ DPNI_CMD_OPEN(cmd, dpni_id); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *token = MC_CMD_HDR_READ_TOKEN(cmd.header); -+ -+ return 0; -+} -+ -+int dpni_close(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_CLOSE, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_create(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ const struct dpni_cfg *cfg, -+ uint16_t *token) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_CREATE, -+ cmd_flags, -+ 0); -+ DPNI_CMD_CREATE(cmd, cfg); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *token = MC_CMD_HDR_READ_TOKEN(cmd.header); -+ -+ return 0; -+} -+ -+int dpni_destroy(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_DESTROY, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_set_pools(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_pools_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_POOLS, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_POOLS(cmd, cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_ENABLE, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_disable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_DISABLE, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_is_enabled(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *en) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_IS_ENABLED, cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_IS_ENABLED(cmd, *en); -+ -+ return 0; -+} -+ -+int dpni_reset(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_RESET, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_set_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ struct dpni_irq_cfg *irq_cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_IRQ, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_IRQ(cmd, irq_index, irq_cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ int *type, -+ struct dpni_irq_cfg *irq_cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_IRQ, -+ cmd_flags, -+ token); -+ DPNI_CMD_GET_IRQ(cmd, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_IRQ(cmd, *type, irq_cfg); -+ -+ return 0; -+} -+ -+int dpni_set_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t en) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_IRQ_ENABLE, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_IRQ_ENABLE(cmd, irq_index, en); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t *en) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_IRQ_ENABLE, -+ cmd_flags, -+ token); -+ DPNI_CMD_GET_IRQ_ENABLE(cmd, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_IRQ_ENABLE(cmd, *en); -+ -+ return 0; -+} -+ -+int dpni_set_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t mask) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_IRQ_MASK, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_IRQ_MASK(cmd, irq_index, mask); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *mask) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_IRQ_MASK, -+ cmd_flags, -+ token); -+ DPNI_CMD_GET_IRQ_MASK(cmd, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_IRQ_MASK(cmd, *mask); -+ -+ return 0; -+} -+ -+int dpni_get_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *status) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_IRQ_STATUS, -+ cmd_flags, -+ token); -+ DPNI_CMD_GET_IRQ_STATUS(cmd, irq_index, *status); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_IRQ_STATUS(cmd, *status); -+ -+ return 0; -+} -+ -+int dpni_clear_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t status) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_CLEAR_IRQ_STATUS, -+ cmd_flags, -+ token); -+ DPNI_CMD_CLEAR_IRQ_STATUS(cmd, irq_index, status); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_attributes(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpni_attr *attr) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_ATTR, -+ cmd_flags, -+ token); -+ DPNI_CMD_GET_ATTR(cmd, attr); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_ATTR(cmd, attr); -+ -+ return 0; -+} -+ -+int dpni_set_errors_behavior(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpni_error_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_ERRORS_BEHAVIOR, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_ERRORS_BEHAVIOR(cmd, cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_rx_buffer_layout(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpni_buffer_layout *layout) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_RX_BUFFER_LAYOUT, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_RX_BUFFER_LAYOUT(cmd, layout); -+ -+ return 0; -+} -+ -+int dpni_set_rx_buffer_layout(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_buffer_layout *layout) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_RX_BUFFER_LAYOUT, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_RX_BUFFER_LAYOUT(cmd, layout); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_tx_buffer_layout(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpni_buffer_layout *layout) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_TX_BUFFER_LAYOUT, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_TX_BUFFER_LAYOUT(cmd, layout); -+ -+ return 0; -+} -+ -+int dpni_set_tx_buffer_layout(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_buffer_layout *layout) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_TX_BUFFER_LAYOUT, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_TX_BUFFER_LAYOUT(cmd, layout); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_tx_conf_buffer_layout(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpni_buffer_layout *layout) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_TX_CONF_BUFFER_LAYOUT, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_TX_CONF_BUFFER_LAYOUT(cmd, layout); -+ -+ return 0; -+} -+ -+int dpni_set_tx_conf_buffer_layout(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_buffer_layout *layout) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_TX_CONF_BUFFER_LAYOUT, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_TX_CONF_BUFFER_LAYOUT(cmd, layout); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_l3_chksum_validation(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *en) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_L3_CHKSUM_VALIDATION, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_L3_CHKSUM_VALIDATION(cmd, *en); -+ -+ return 0; -+} -+ -+int dpni_set_l3_chksum_validation(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_L3_CHKSUM_VALIDATION, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_L3_CHKSUM_VALIDATION(cmd, en); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_l4_chksum_validation(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *en) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_L4_CHKSUM_VALIDATION, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_L4_CHKSUM_VALIDATION(cmd, *en); -+ -+ return 0; -+} -+ -+int dpni_set_l4_chksum_validation(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_L4_CHKSUM_VALIDATION, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_L4_CHKSUM_VALIDATION(cmd, en); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_qdid(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t *qdid) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_QDID, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_QDID(cmd, *qdid); -+ -+ return 0; -+} -+ -+int dpni_get_sp_info(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpni_sp_info *sp_info) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_SP_INFO, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_SP_INFO(cmd, sp_info); -+ -+ return 0; -+} -+ -+int dpni_get_tx_data_offset(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t *data_offset) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_TX_DATA_OFFSET, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_TX_DATA_OFFSET(cmd, *data_offset); -+ -+ return 0; -+} -+ -+int dpni_get_counter(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ enum dpni_counter counter, -+ uint64_t *value) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_COUNTER, -+ cmd_flags, -+ token); -+ DPNI_CMD_GET_COUNTER(cmd, counter); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_COUNTER(cmd, *value); -+ -+ return 0; -+} -+ -+int dpni_set_counter(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ enum dpni_counter counter, -+ uint64_t value) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_COUNTER, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_COUNTER(cmd, counter, value); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_set_link_cfg(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_link_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_LINK_CFG, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_LINK_CFG(cmd, cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_link_state(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpni_link_state *state) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_LINK_STATE, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_LINK_STATE(cmd, state); -+ -+ return 0; -+} -+ -+int dpni_set_tx_shaping(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_tx_shaping_cfg *tx_shaper) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_TX_SHAPING, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_TX_SHAPING(cmd, tx_shaper); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_set_max_frame_length(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t max_frame_length) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_MAX_FRAME_LENGTH, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_MAX_FRAME_LENGTH(cmd, max_frame_length); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_max_frame_length(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t *max_frame_length) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_MAX_FRAME_LENGTH, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_MAX_FRAME_LENGTH(cmd, *max_frame_length); -+ -+ return 0; -+} -+ -+int dpni_set_mtu(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t mtu) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_MTU, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_MTU(cmd, mtu); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_mtu(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t *mtu) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_MTU, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_MTU(cmd, *mtu); -+ -+ return 0; -+} -+ -+int dpni_set_multicast_promisc(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_MCAST_PROMISC, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_MULTICAST_PROMISC(cmd, en); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_multicast_promisc(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *en) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_MCAST_PROMISC, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_MULTICAST_PROMISC(cmd, *en); -+ -+ return 0; -+} -+ -+int dpni_set_unicast_promisc(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_UNICAST_PROMISC, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_UNICAST_PROMISC(cmd, en); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_unicast_promisc(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *en) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_UNICAST_PROMISC, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_UNICAST_PROMISC(cmd, *en); -+ -+ return 0; -+} -+ -+int dpni_set_primary_mac_addr(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const uint8_t mac_addr[6]) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_PRIM_MAC, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_PRIMARY_MAC_ADDR(cmd, mac_addr); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_primary_mac_addr(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t mac_addr[6]) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_PRIM_MAC, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_PRIMARY_MAC_ADDR(cmd, mac_addr); -+ -+ return 0; -+} -+ -+int dpni_add_mac_addr(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const uint8_t mac_addr[6]) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_ADD_MAC_ADDR, -+ cmd_flags, -+ token); -+ DPNI_CMD_ADD_MAC_ADDR(cmd, mac_addr); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_remove_mac_addr(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const uint8_t mac_addr[6]) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_REMOVE_MAC_ADDR, -+ cmd_flags, -+ token); -+ DPNI_CMD_REMOVE_MAC_ADDR(cmd, mac_addr); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_clear_mac_filters(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int unicast, -+ int multicast) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_CLR_MAC_FILTERS, -+ cmd_flags, -+ token); -+ DPNI_CMD_CLEAR_MAC_FILTERS(cmd, unicast, multicast); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_set_vlan_filters(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_VLAN_FILTERS, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_VLAN_FILTERS(cmd, en); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_add_vlan_id(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t vlan_id) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_ADD_VLAN_ID, -+ cmd_flags, -+ token); -+ DPNI_CMD_ADD_VLAN_ID(cmd, vlan_id); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_remove_vlan_id(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t vlan_id) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_REMOVE_VLAN_ID, -+ cmd_flags, -+ token); -+ DPNI_CMD_REMOVE_VLAN_ID(cmd, vlan_id); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_clear_vlan_filters(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_CLR_VLAN_FILTERS, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_set_tx_selection(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_tx_selection_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_TX_SELECTION, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_TX_SELECTION(cmd, cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_set_rx_tc_dist(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ const struct dpni_rx_tc_dist_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_RX_TC_DIST, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_RX_TC_DIST(cmd, tc_id, cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_set_tx_flow(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t *flow_id, -+ const struct dpni_tx_flow_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_TX_FLOW, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_TX_FLOW(cmd, *flow_id, cfg); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_SET_TX_FLOW(cmd, *flow_id); -+ -+ return 0; -+} -+ -+int dpni_get_tx_flow(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t flow_id, -+ struct dpni_tx_flow_attr *attr) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_TX_FLOW, -+ cmd_flags, -+ token); -+ DPNI_CMD_GET_TX_FLOW(cmd, flow_id); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_TX_FLOW(cmd, attr); -+ -+ return 0; -+} -+ -+int dpni_set_rx_flow(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ uint16_t flow_id, -+ const struct dpni_queue_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_RX_FLOW, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_RX_FLOW(cmd, tc_id, flow_id, cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_rx_flow(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ uint16_t flow_id, -+ struct dpni_queue_attr *attr) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_RX_FLOW, -+ cmd_flags, -+ token); -+ DPNI_CMD_GET_RX_FLOW(cmd, tc_id, flow_id); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_RX_FLOW(cmd, attr); -+ -+ return 0; -+} -+ -+int dpni_set_rx_err_queue(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_queue_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_RX_ERR_QUEUE, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_RX_ERR_QUEUE(cmd, cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_rx_err_queue(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpni_queue_attr *attr) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_RX_ERR_QUEUE, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_RX_ERR_QUEUE(cmd, attr); -+ -+ return 0; -+} -+ -+int dpni_set_tx_conf_revoke(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int revoke) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_TX_CONF_REVOKE, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_TX_CONF_REVOKE(cmd, revoke); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_set_qos_table(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_qos_tbl_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_QOS_TBL, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_QOS_TABLE(cmd, cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_add_qos_entry(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_rule_cfg *cfg, -+ uint8_t tc_id) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_ADD_QOS_ENT, -+ cmd_flags, -+ token); -+ DPNI_CMD_ADD_QOS_ENTRY(cmd, cfg, tc_id); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_remove_qos_entry(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_rule_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_REMOVE_QOS_ENT, -+ cmd_flags, -+ token); -+ DPNI_CMD_REMOVE_QOS_ENTRY(cmd, cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_clear_qos_table(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_CLR_QOS_TBL, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_add_fs_entry(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ const struct dpni_rule_cfg *cfg, -+ uint16_t flow_id) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_ADD_FS_ENT, -+ cmd_flags, -+ token); -+ DPNI_CMD_ADD_FS_ENTRY(cmd, tc_id, cfg, flow_id); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_remove_fs_entry(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ const struct dpni_rule_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_REMOVE_FS_ENT, -+ cmd_flags, -+ token); -+ DPNI_CMD_REMOVE_FS_ENTRY(cmd, tc_id, cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_clear_fs_entries(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_CLR_FS_ENT, -+ cmd_flags, -+ token); -+ DPNI_CMD_CLEAR_FS_ENTRIES(cmd, tc_id); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_set_vlan_insertion(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_VLAN_INSERTION, -+ cmd_flags, token); -+ DPNI_CMD_SET_VLAN_INSERTION(cmd, en); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_set_vlan_removal(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_VLAN_REMOVAL, -+ cmd_flags, token); -+ DPNI_CMD_SET_VLAN_REMOVAL(cmd, en); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_set_ipr(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_IPR, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_IPR(cmd, en); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_set_ipf(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_IPF, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_IPF(cmd, en); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_set_rx_tc_policing(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ const struct dpni_rx_tc_policing_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_RX_TC_POLICING, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_RX_TC_POLICING(cmd, tc_id, cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_rx_tc_policing(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ struct dpni_rx_tc_policing_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_RX_TC_POLICING, -+ cmd_flags, -+ token); -+ DPNI_CMD_GET_RX_TC_POLICING(cmd, tc_id); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ DPNI_RSP_GET_RX_TC_POLICING(cmd, cfg); -+ -+ return 0; -+} -+ -+void dpni_prepare_early_drop(const struct dpni_early_drop_cfg *cfg, -+ uint8_t *early_drop_buf) -+{ -+ uint64_t *ext_params = (uint64_t *)early_drop_buf; -+ -+ DPNI_PREP_EARLY_DROP(ext_params, cfg); -+} -+ -+void dpni_extract_early_drop(struct dpni_early_drop_cfg *cfg, -+ const uint8_t *early_drop_buf) -+{ -+ const uint64_t *ext_params = (const uint64_t *)early_drop_buf; -+ -+ DPNI_EXT_EARLY_DROP(ext_params, cfg); -+} -+ -+int dpni_set_rx_tc_early_drop(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ uint64_t early_drop_iova) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_RX_TC_EARLY_DROP, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_RX_TC_EARLY_DROP(cmd, tc_id, early_drop_iova); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_rx_tc_early_drop(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ uint64_t early_drop_iova) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_RX_TC_EARLY_DROP, -+ cmd_flags, -+ token); -+ DPNI_CMD_GET_RX_TC_EARLY_DROP(cmd, tc_id, early_drop_iova); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_set_tx_tc_early_drop(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ uint64_t early_drop_iova) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_TX_TC_EARLY_DROP, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_TX_TC_EARLY_DROP(cmd, tc_id, early_drop_iova); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_tx_tc_early_drop(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ uint64_t early_drop_iova) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_TX_TC_EARLY_DROP, -+ cmd_flags, -+ token); -+ DPNI_CMD_GET_TX_TC_EARLY_DROP(cmd, tc_id, early_drop_iova); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_set_rx_tc_congestion_notification(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ const struct dpni_congestion_notification_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header( -+ DPNI_CMDID_SET_RX_TC_CONGESTION_NOTIFICATION, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_RX_TC_CONGESTION_NOTIFICATION(cmd, tc_id, cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_rx_tc_congestion_notification(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ struct dpni_congestion_notification_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header( -+ DPNI_CMDID_GET_RX_TC_CONGESTION_NOTIFICATION, -+ cmd_flags, -+ token); -+ DPNI_CMD_GET_RX_TC_CONGESTION_NOTIFICATION(cmd, tc_id); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ DPNI_RSP_GET_RX_TC_CONGESTION_NOTIFICATION(cmd, cfg); -+ -+ return 0; -+} -+ -+int dpni_set_tx_tc_congestion_notification(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ const struct dpni_congestion_notification_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header( -+ DPNI_CMDID_SET_TX_TC_CONGESTION_NOTIFICATION, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_TX_TC_CONGESTION_NOTIFICATION(cmd, tc_id, cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_tx_tc_congestion_notification(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ struct dpni_congestion_notification_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header( -+ DPNI_CMDID_GET_TX_TC_CONGESTION_NOTIFICATION, -+ cmd_flags, -+ token); -+ DPNI_CMD_GET_TX_TC_CONGESTION_NOTIFICATION(cmd, tc_id); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ DPNI_RSP_GET_TX_TC_CONGESTION_NOTIFICATION(cmd, cfg); -+ -+ return 0; -+} -+ -+int dpni_set_tx_conf(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t flow_id, -+ const struct dpni_tx_conf_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_TX_CONF, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_TX_CONF(cmd, flow_id, cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_tx_conf(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t flow_id, -+ struct dpni_tx_conf_attr *attr) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_TX_CONF, -+ cmd_flags, -+ token); -+ DPNI_CMD_GET_TX_CONF(cmd, flow_id); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ DPNI_RSP_GET_TX_CONF(cmd, attr); -+ -+ return 0; -+} -+ -+int dpni_set_tx_conf_congestion_notification(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t flow_id, -+ const struct dpni_congestion_notification_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header( -+ DPNI_CMDID_SET_TX_CONF_CONGESTION_NOTIFICATION, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_TX_CONF_CONGESTION_NOTIFICATION(cmd, flow_id, cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_tx_conf_congestion_notification(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t flow_id, -+ struct dpni_congestion_notification_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header( -+ DPNI_CMDID_GET_TX_CONF_CONGESTION_NOTIFICATION, -+ cmd_flags, -+ token); -+ DPNI_CMD_GET_TX_CONF_CONGESTION_NOTIFICATION(cmd, flow_id); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ DPNI_RSP_GET_TX_CONF_CONGESTION_NOTIFICATION(cmd, cfg); -+ -+ return 0; -+} -diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpni.h b/drivers/staging/fsl-dpaa2/ethernet/dpni.h -new file mode 100644 -index 0000000..fca426d ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/ethernet/dpni.h -@@ -0,0 +1,2581 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef __FSL_DPNI_H -+#define __FSL_DPNI_H -+ -+#include "dpkg.h" -+ -+struct fsl_mc_io; -+ -+/** -+ * Data Path Network Interface API -+ * Contains initialization APIs and runtime control APIs for DPNI -+ */ -+ -+/** General DPNI macros */ -+ -+/** -+ * Maximum number of traffic classes -+ */ -+#define DPNI_MAX_TC 8 -+/** -+ * Maximum number of buffer pools per DPNI -+ */ -+#define DPNI_MAX_DPBP 8 -+/** -+ * Maximum number of storage-profiles per DPNI -+ */ -+#define DPNI_MAX_SP 2 -+ -+/** -+ * All traffic classes considered; see dpni_set_rx_flow() -+ */ -+#define DPNI_ALL_TCS (uint8_t)(-1) -+/** -+ * All flows within traffic class considered; see dpni_set_rx_flow() -+ */ -+#define DPNI_ALL_TC_FLOWS (uint16_t)(-1) -+/** -+ * Generate new flow ID; see dpni_set_tx_flow() -+ */ -+#define DPNI_NEW_FLOW_ID (uint16_t)(-1) -+/* use for common tx-conf queue; see dpni_set_tx_conf_() */ -+#define DPNI_COMMON_TX_CONF (uint16_t)(-1) -+ -+/** -+ * dpni_open() - Open a control session for the specified object -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @dpni_id: DPNI unique ID -+ * @token: Returned token; use in subsequent API calls -+ * -+ * This function can be used to open a control session for an -+ * already created object; an object may have been declared in -+ * the DPL or by calling the dpni_create() function. -+ * This function returns a unique authentication token, -+ * associated with the specific object ID and the specific MC -+ * portal; this token must be used in all subsequent commands for -+ * this specific object. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_open(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ int dpni_id, -+ uint16_t *token); -+ -+/** -+ * dpni_close() - Close the control session of the object -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * -+ * After this function is called, no further operations are -+ * allowed on the object without opening a new control session. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_close(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/* DPNI configuration options */ -+ -+/** -+ * Allow different distribution key profiles for different traffic classes; -+ * if not set, a single key profile is assumed -+ */ -+#define DPNI_OPT_ALLOW_DIST_KEY_PER_TC 0x00000001 -+ -+/** -+ * Disable all non-error transmit confirmation; error frames are reported -+ * back to a common Tx error queue -+ */ -+#define DPNI_OPT_TX_CONF_DISABLED 0x00000002 -+ -+/** -+ * Disable per-sender private Tx confirmation/error queue -+ */ -+#define DPNI_OPT_PRIVATE_TX_CONF_ERROR_DISABLED 0x00000004 -+ -+/** -+ * Support distribution based on hashed key; -+ * allows statistical distribution over receive queues in a traffic class -+ */ -+#define DPNI_OPT_DIST_HASH 0x00000010 -+ -+/** -+ * DEPRECATED - if this flag is selected and and all new 'max_fs_entries' are -+ * '0' then backward compatibility is preserved; -+ * Support distribution based on flow steering; -+ * allows explicit control of distribution over receive queues in a traffic -+ * class -+ */ -+#define DPNI_OPT_DIST_FS 0x00000020 -+ -+/** -+ * Unicast filtering support -+ */ -+#define DPNI_OPT_UNICAST_FILTER 0x00000080 -+/** -+ * Multicast filtering support -+ */ -+#define DPNI_OPT_MULTICAST_FILTER 0x00000100 -+/** -+ * VLAN filtering support -+ */ -+#define DPNI_OPT_VLAN_FILTER 0x00000200 -+/** -+ * Support IP reassembly on received packets -+ */ -+#define DPNI_OPT_IPR 0x00000800 -+/** -+ * Support IP fragmentation on transmitted packets -+ */ -+#define DPNI_OPT_IPF 0x00001000 -+/** -+ * VLAN manipulation support -+ */ -+#define DPNI_OPT_VLAN_MANIPULATION 0x00010000 -+/** -+ * Support masking of QoS lookup keys -+ */ -+#define DPNI_OPT_QOS_MASK_SUPPORT 0x00020000 -+/** -+ * Support masking of Flow Steering lookup keys -+ */ -+#define DPNI_OPT_FS_MASK_SUPPORT 0x00040000 -+ -+/** -+ * struct dpni_extended_cfg - Structure representing extended DPNI configuration -+ * @tc_cfg: TCs configuration -+ * @ipr_cfg: IP reassembly configuration -+ */ -+struct dpni_extended_cfg { -+ /** -+ * struct tc_cfg - TC configuration -+ * @max_dist: Maximum distribution size for Rx traffic class; -+ * supported values: 1,2,3,4,6,7,8,12,14,16,24,28,32,48,56,64,96, -+ * 112,128,192,224,256,384,448,512,768,896,1024; -+ * value '0' will be treated as '1'. -+ * other unsupported values will be round down to the nearest -+ * supported value. -+ * @max_fs_entries: Maximum FS entries for Rx traffic class; -+ * '0' means no support for this TC; -+ */ -+ struct { -+ uint16_t max_dist; -+ uint16_t max_fs_entries; -+ } tc_cfg[DPNI_MAX_TC]; -+ /** -+ * struct ipr_cfg - Structure representing IP reassembly configuration -+ * @max_reass_frm_size: Maximum size of the reassembled frame -+ * @min_frag_size_ipv4: Minimum fragment size of IPv4 fragments -+ * @min_frag_size_ipv6: Minimum fragment size of IPv6 fragments -+ * @max_open_frames_ipv4: Maximum concurrent IPv4 packets in reassembly -+ * process -+ * @max_open_frames_ipv6: Maximum concurrent IPv6 packets in reassembly -+ * process -+ */ -+ struct { -+ uint16_t max_reass_frm_size; -+ uint16_t min_frag_size_ipv4; -+ uint16_t min_frag_size_ipv6; -+ uint16_t max_open_frames_ipv4; -+ uint16_t max_open_frames_ipv6; -+ } ipr_cfg; -+}; -+ -+/** -+ * dpni_prepare_extended_cfg() - function prepare extended parameters -+ * @cfg: extended structure -+ * @ext_cfg_buf: Zeroed 256 bytes of memory before mapping it to DMA -+ * -+ * This function has to be called before dpni_create() -+ */ -+int dpni_prepare_extended_cfg(const struct dpni_extended_cfg *cfg, -+ uint8_t *ext_cfg_buf); -+ -+/** -+ * struct dpni_cfg - Structure representing DPNI configuration -+ * @mac_addr: Primary MAC address -+ * @adv: Advanced parameters; default is all zeros; -+ * use this structure to change default settings -+ */ -+struct dpni_cfg { -+ uint8_t mac_addr[6]; -+ /** -+ * struct adv - Advanced parameters -+ * @options: Mask of available options; use 'DPNI_OPT_' values -+ * @start_hdr: Selects the packet starting header for parsing; -+ * 'NET_PROT_NONE' is treated as default: 'NET_PROT_ETH' -+ * @max_senders: Maximum number of different senders; used as the number -+ * of dedicated Tx flows; Non-power-of-2 values are rounded -+ * up to the next power-of-2 value as hardware demands it; -+ * '0' will be treated as '1' -+ * @max_tcs: Maximum number of traffic classes (for both Tx and Rx); -+ * '0' will e treated as '1' -+ * @max_unicast_filters: Maximum number of unicast filters; -+ * '0' is treated as '16' -+ * @max_multicast_filters: Maximum number of multicast filters; -+ * '0' is treated as '64' -+ * @max_qos_entries: if 'max_tcs > 1', declares the maximum entries in -+ * the QoS table; '0' is treated as '64' -+ * @max_qos_key_size: Maximum key size for the QoS look-up; -+ * '0' is treated as '24' which is enough for IPv4 -+ * 5-tuple -+ * @max_dist_key_size: Maximum key size for the distribution; -+ * '0' is treated as '24' which is enough for IPv4 5-tuple -+ * @max_policers: Maximum number of policers; -+ * should be between '0' and max_tcs -+ * @max_congestion_ctrl: Maximum number of congestion control groups -+ * (CGs); covers early drop and congestion notification -+ * requirements; -+ * should be between '0' and ('max_tcs' + 'max_senders') -+ * @ext_cfg_iova: I/O virtual address of 256 bytes DMA-able memory -+ * filled with the extended configuration by calling -+ * dpni_prepare_extended_cfg() -+ */ -+ struct { -+ uint32_t options; -+ enum net_prot start_hdr; -+ uint8_t max_senders; -+ uint8_t max_tcs; -+ uint8_t max_unicast_filters; -+ uint8_t max_multicast_filters; -+ uint8_t max_vlan_filters; -+ uint8_t max_qos_entries; -+ uint8_t max_qos_key_size; -+ uint8_t max_dist_key_size; -+ uint8_t max_policers; -+ uint8_t max_congestion_ctrl; -+ uint64_t ext_cfg_iova; -+ } adv; -+}; -+ -+/** -+ * dpni_create() - Create the DPNI object -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @cfg: Configuration structure -+ * @token: Returned token; use in subsequent API calls -+ * -+ * Create the DPNI object, allocate required resources and -+ * perform required initialization. -+ * -+ * The object can be created either by declaring it in the -+ * DPL file, or by calling this function. -+ * -+ * This function returns a unique authentication token, -+ * associated with the specific object ID and the specific MC -+ * portal; this token must be used in all subsequent calls to -+ * this specific object. For objects that are created using the -+ * DPL file, call dpni_open() function to get an authentication -+ * token first. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_create(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ const struct dpni_cfg *cfg, -+ uint16_t *token); -+ -+/** -+ * dpni_destroy() - Destroy the DPNI object and release all its resources. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpni_destroy(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * struct dpni_pools_cfg - Structure representing buffer pools configuration -+ * @num_dpbp: Number of DPBPs -+ * @pools: Array of buffer pools parameters; The number of valid entries -+ * must match 'num_dpbp' value -+ */ -+struct dpni_pools_cfg { -+ uint8_t num_dpbp; -+ /** -+ * struct pools - Buffer pools parameters -+ * @dpbp_id: DPBP object ID -+ * @buffer_size: Buffer size -+ * @backup_pool: Backup pool -+ */ -+ struct { -+ int dpbp_id; -+ uint16_t buffer_size; -+ int backup_pool; -+ } pools[DPNI_MAX_DPBP]; -+}; -+ -+/** -+ * dpni_set_pools() - Set buffer pools configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @cfg: Buffer pools configuration -+ * -+ * mandatory for DPNI operation -+ * warning:Allowed only when DPNI is disabled -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_pools(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_pools_cfg *cfg); -+ -+/** -+ * dpni_enable() - Enable the DPNI, allow sending and receiving frames. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * dpni_disable() - Disable the DPNI, stop sending and receiving frames. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_disable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * dpni_is_enabled() - Check if the DPNI is enabled. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @en: Returns '1' if object is enabled; '0' otherwise -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_is_enabled(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *en); -+ -+/** -+ * dpni_reset() - Reset the DPNI, returns the object to initial state. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_reset(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * DPNI IRQ Index and Events -+ */ -+ -+/** -+ * IRQ index -+ */ -+#define DPNI_IRQ_INDEX 0 -+/** -+ * IRQ event - indicates a change in link state -+ */ -+#define DPNI_IRQ_EVENT_LINK_CHANGED 0x00000001 -+ -+/** -+ * struct dpni_irq_cfg - IRQ configuration -+ * @addr: Address that must be written to signal a message-based interrupt -+ * @val: Value to write into irq_addr address -+ * @irq_num: A user defined number associated with this IRQ -+ */ -+struct dpni_irq_cfg { -+ uint64_t addr; -+ uint32_t val; -+ int irq_num; -+}; -+ -+/** -+ * dpni_set_irq() - Set IRQ information for the DPNI to trigger an interrupt. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @irq_index: Identifies the interrupt index to configure -+ * @irq_cfg: IRQ configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ struct dpni_irq_cfg *irq_cfg); -+ -+/** -+ * dpni_get_irq() - Get IRQ information from the DPNI. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @irq_index: The interrupt index to configure -+ * @type: Interrupt type: 0 represents message interrupt -+ * type (both irq_addr and irq_val are valid) -+ * @irq_cfg: IRQ attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ int *type, -+ struct dpni_irq_cfg *irq_cfg); -+ -+/** -+ * dpni_set_irq_enable() - Set overall interrupt state. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @irq_index: The interrupt index to configure -+ * @en: Interrupt state: - enable = 1, disable = 0 -+ * -+ * Allows GPP software to control when interrupts are generated. -+ * Each interrupt can have up to 32 causes. The enable/disable control's the -+ * overall interrupt state. if the interrupt is disabled no causes will cause -+ * an interrupt. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t en); -+ -+/** -+ * dpni_get_irq_enable() - Get overall interrupt state -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @irq_index: The interrupt index to configure -+ * @en: Returned interrupt state - enable = 1, disable = 0 -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t *en); -+ -+/** -+ * dpni_set_irq_mask() - Set interrupt mask. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @irq_index: The interrupt index to configure -+ * @mask: event mask to trigger interrupt; -+ * each bit: -+ * 0 = ignore event -+ * 1 = consider event for asserting IRQ -+ * -+ * Every interrupt can have up to 32 causes and the interrupt model supports -+ * masking/unmasking each cause independently -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t mask); -+ -+/** -+ * dpni_get_irq_mask() - Get interrupt mask. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @irq_index: The interrupt index to configure -+ * @mask: Returned event mask to trigger interrupt -+ * -+ * Every interrupt can have up to 32 causes and the interrupt model supports -+ * masking/unmasking each cause independently -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *mask); -+ -+/** -+ * dpni_get_irq_status() - Get the current status of any pending interrupts. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @irq_index: The interrupt index to configure -+ * @status: Returned interrupts status - one bit per cause: -+ * 0 = no interrupt pending -+ * 1 = interrupt pending -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *status); -+ -+/** -+ * dpni_clear_irq_status() - Clear a pending interrupt's status -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @irq_index: The interrupt index to configure -+ * @status: bits to clear (W1C) - one bit per cause: -+ * 0 = don't change -+ * 1 = clear status bit -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_clear_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t status); -+ -+/** -+ * struct dpni_attr - Structure representing DPNI attributes -+ * @id: DPNI object ID -+ * @version: DPNI version -+ * @start_hdr: Indicates the packet starting header for parsing -+ * @options: Mask of available options; reflects the value as was given in -+ * object's creation -+ * @max_senders: Maximum number of different senders; used as the number -+ * of dedicated Tx flows; -+ * @max_tcs: Maximum number of traffic classes (for both Tx and Rx) -+ * @max_unicast_filters: Maximum number of unicast filters -+ * @max_multicast_filters: Maximum number of multicast filters -+ * @max_vlan_filters: Maximum number of VLAN filters -+ * @max_qos_entries: if 'max_tcs > 1', declares the maximum entries in QoS table -+ * @max_qos_key_size: Maximum key size for the QoS look-up -+ * @max_dist_key_size: Maximum key size for the distribution look-up -+ * @max_policers: Maximum number of policers; -+ * @max_congestion_ctrl: Maximum number of congestion control groups (CGs); -+ * @ext_cfg_iova: I/O virtual address of 256 bytes DMA-able memory; -+ * call dpni_extract_extended_cfg() to extract the extended configuration -+ */ -+struct dpni_attr { -+ int id; -+ /** -+ * struct version - DPNI version -+ * @major: DPNI major version -+ * @minor: DPNI minor version -+ */ -+ struct { -+ uint16_t major; -+ uint16_t minor; -+ } version; -+ enum net_prot start_hdr; -+ uint32_t options; -+ uint8_t max_senders; -+ uint8_t max_tcs; -+ uint8_t max_unicast_filters; -+ uint8_t max_multicast_filters; -+ uint8_t max_vlan_filters; -+ uint8_t max_qos_entries; -+ uint8_t max_qos_key_size; -+ uint8_t max_dist_key_size; -+ uint8_t max_policers; -+ uint8_t max_congestion_ctrl; -+ uint64_t ext_cfg_iova; -+}; -+ -+/** -+ * dpni_get_attributes() - Retrieve DPNI attributes. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @attr: Object's attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_attributes(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpni_attr *attr); -+ -+/** -+ * dpni_extract_extended_cfg() - extract the extended parameters -+ * @cfg: extended structure -+ * @ext_cfg_buf: 256 bytes of DMA-able memory -+ * -+ * This function has to be called after dpni_get_attributes() -+ */ -+int dpni_extract_extended_cfg(struct dpni_extended_cfg *cfg, -+ const uint8_t *ext_cfg_buf); -+ -+/** -+ * DPNI errors -+ */ -+ -+/** -+ * Extract out of frame header error -+ */ -+#define DPNI_ERROR_EOFHE 0x00020000 -+/** -+ * Frame length error -+ */ -+#define DPNI_ERROR_FLE 0x00002000 -+/** -+ * Frame physical error -+ */ -+#define DPNI_ERROR_FPE 0x00001000 -+/** -+ * Parsing header error -+ */ -+#define DPNI_ERROR_PHE 0x00000020 -+/** -+ * Parser L3 checksum error -+ */ -+#define DPNI_ERROR_L3CE 0x00000004 -+/** -+ * Parser L3 checksum error -+ */ -+#define DPNI_ERROR_L4CE 0x00000001 -+ -+/** -+ * enum dpni_error_action - Defines DPNI behavior for errors -+ * @DPNI_ERROR_ACTION_DISCARD: Discard the frame -+ * @DPNI_ERROR_ACTION_CONTINUE: Continue with the normal flow -+ * @DPNI_ERROR_ACTION_SEND_TO_ERROR_QUEUE: Send the frame to the error queue -+ */ -+enum dpni_error_action { -+ DPNI_ERROR_ACTION_DISCARD = 0, -+ DPNI_ERROR_ACTION_CONTINUE = 1, -+ DPNI_ERROR_ACTION_SEND_TO_ERROR_QUEUE = 2 -+}; -+ -+/** -+ * struct dpni_error_cfg - Structure representing DPNI errors treatment -+ * @errors: Errors mask; use 'DPNI_ERROR__ -+ * @error_action: The desired action for the errors mask -+ * @set_frame_annotation: Set to '1' to mark the errors in frame annotation -+ * status (FAS); relevant only for the non-discard action -+ */ -+struct dpni_error_cfg { -+ uint32_t errors; -+ enum dpni_error_action error_action; -+ int set_frame_annotation; -+}; -+ -+/** -+ * dpni_set_errors_behavior() - Set errors behavior -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @cfg: Errors configuration -+ * -+ * this function may be called numerous times with different -+ * error masks -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_errors_behavior(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpni_error_cfg *cfg); -+ -+/** -+ * DPNI buffer layout modification options -+ */ -+ -+/** -+ * Select to modify the time-stamp setting -+ */ -+#define DPNI_BUF_LAYOUT_OPT_TIMESTAMP 0x00000001 -+/** -+ * Select to modify the parser-result setting; not applicable for Tx -+ */ -+#define DPNI_BUF_LAYOUT_OPT_PARSER_RESULT 0x00000002 -+/** -+ * Select to modify the frame-status setting -+ */ -+#define DPNI_BUF_LAYOUT_OPT_FRAME_STATUS 0x00000004 -+/** -+ * Select to modify the private-data-size setting -+ */ -+#define DPNI_BUF_LAYOUT_OPT_PRIVATE_DATA_SIZE 0x00000008 -+/** -+ * Select to modify the data-alignment setting -+ */ -+#define DPNI_BUF_LAYOUT_OPT_DATA_ALIGN 0x00000010 -+/** -+ * Select to modify the data-head-room setting -+ */ -+#define DPNI_BUF_LAYOUT_OPT_DATA_HEAD_ROOM 0x00000020 -+/** -+ * Select to modify the data-tail-room setting -+ */ -+#define DPNI_BUF_LAYOUT_OPT_DATA_TAIL_ROOM 0x00000040 -+ -+/** -+ * struct dpni_buffer_layout - Structure representing DPNI buffer layout -+ * @options: Flags representing the suggested modifications to the buffer -+ * layout; Use any combination of 'DPNI_BUF_LAYOUT_OPT_' flags -+ * @pass_timestamp: Pass timestamp value -+ * @pass_parser_result: Pass parser results -+ * @pass_frame_status: Pass frame status -+ * @private_data_size: Size kept for private data (in bytes) -+ * @data_align: Data alignment -+ * @data_head_room: Data head room -+ * @data_tail_room: Data tail room -+ */ -+struct dpni_buffer_layout { -+ uint32_t options; -+ int pass_timestamp; -+ int pass_parser_result; -+ int pass_frame_status; -+ uint16_t private_data_size; -+ uint16_t data_align; -+ uint16_t data_head_room; -+ uint16_t data_tail_room; -+}; -+ -+/** -+ * dpni_get_rx_buffer_layout() - Retrieve Rx buffer layout attributes. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @layout: Returns buffer layout attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_rx_buffer_layout(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpni_buffer_layout *layout); -+ -+/** -+ * dpni_set_rx_buffer_layout() - Set Rx buffer layout configuration. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @layout: Buffer layout configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ * -+ * @warning Allowed only when DPNI is disabled -+ */ -+int dpni_set_rx_buffer_layout(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_buffer_layout *layout); -+ -+/** -+ * dpni_get_tx_buffer_layout() - Retrieve Tx buffer layout attributes. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @layout: Returns buffer layout attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_tx_buffer_layout(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpni_buffer_layout *layout); -+ -+/** -+ * dpni_set_tx_buffer_layout() - Set Tx buffer layout configuration. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @layout: Buffer layout configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ * -+ * @warning Allowed only when DPNI is disabled -+ */ -+int dpni_set_tx_buffer_layout(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_buffer_layout *layout); -+ -+/** -+ * dpni_get_tx_conf_buffer_layout() - Retrieve Tx confirmation buffer layout -+ * attributes. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @layout: Returns buffer layout attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_tx_conf_buffer_layout(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpni_buffer_layout *layout); -+ -+/** -+ * dpni_set_tx_conf_buffer_layout() - Set Tx confirmation buffer layout -+ * configuration. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @layout: Buffer layout configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ * -+ * @warning Allowed only when DPNI is disabled -+ */ -+int dpni_set_tx_conf_buffer_layout(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_buffer_layout *layout); -+ -+/** -+ * dpni_set_l3_chksum_validation() - Enable/disable L3 checksum validation -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @en: Set to '1' to enable; '0' to disable -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_l3_chksum_validation(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en); -+ -+/** -+ * dpni_get_l3_chksum_validation() - Get L3 checksum validation mode -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @en: Returns '1' if enabled; '0' otherwise -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_l3_chksum_validation(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *en); -+ -+/** -+ * dpni_set_l4_chksum_validation() - Enable/disable L4 checksum validation -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @en: Set to '1' to enable; '0' to disable -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_l4_chksum_validation(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en); -+ -+/** -+ * dpni_get_l4_chksum_validation() - Get L4 checksum validation mode -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @en: Returns '1' if enabled; '0' otherwise -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_l4_chksum_validation(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *en); -+ -+/** -+ * dpni_get_qdid() - Get the Queuing Destination ID (QDID) that should be used -+ * for enqueue operations -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @qdid: Returned virtual QDID value that should be used as an argument -+ * in all enqueue operations -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_qdid(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t *qdid); -+ -+/** -+ * struct dpni_sp_info - Structure representing DPNI storage-profile information -+ * (relevant only for DPNI owned by AIOP) -+ * @spids: array of storage-profiles -+ */ -+struct dpni_sp_info { -+ uint16_t spids[DPNI_MAX_SP]; -+}; -+ -+/** -+ * dpni_get_spids() - Get the AIOP storage profile IDs associated with the DPNI -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @sp_info: Returned AIOP storage-profile information -+ * -+ * Return: '0' on Success; Error code otherwise. -+ * -+ * @warning Only relevant for DPNI that belongs to AIOP container. -+ */ -+int dpni_get_sp_info(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpni_sp_info *sp_info); -+ -+/** -+ * dpni_get_tx_data_offset() - Get the Tx data offset (from start of buffer) -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @data_offset: Tx data offset (from start of buffer) -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_tx_data_offset(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t *data_offset); -+ -+/** -+ * enum dpni_counter - DPNI counter types -+ * @DPNI_CNT_ING_FRAME: Counts ingress frames -+ * @DPNI_CNT_ING_BYTE: Counts ingress bytes -+ * @DPNI_CNT_ING_FRAME_DROP: Counts ingress frames dropped due to explicit -+ * 'drop' setting -+ * @DPNI_CNT_ING_FRAME_DISCARD: Counts ingress frames discarded due to errors -+ * @DPNI_CNT_ING_MCAST_FRAME: Counts ingress multicast frames -+ * @DPNI_CNT_ING_MCAST_BYTE: Counts ingress multicast bytes -+ * @DPNI_CNT_ING_BCAST_FRAME: Counts ingress broadcast frames -+ * @DPNI_CNT_ING_BCAST_BYTES: Counts ingress broadcast bytes -+ * @DPNI_CNT_EGR_FRAME: Counts egress frames -+ * @DPNI_CNT_EGR_BYTE: Counts egress bytes -+ * @DPNI_CNT_EGR_FRAME_DISCARD: Counts egress frames discarded due to errors -+ */ -+enum dpni_counter { -+ DPNI_CNT_ING_FRAME = 0x0, -+ DPNI_CNT_ING_BYTE = 0x1, -+ DPNI_CNT_ING_FRAME_DROP = 0x2, -+ DPNI_CNT_ING_FRAME_DISCARD = 0x3, -+ DPNI_CNT_ING_MCAST_FRAME = 0x4, -+ DPNI_CNT_ING_MCAST_BYTE = 0x5, -+ DPNI_CNT_ING_BCAST_FRAME = 0x6, -+ DPNI_CNT_ING_BCAST_BYTES = 0x7, -+ DPNI_CNT_EGR_FRAME = 0x8, -+ DPNI_CNT_EGR_BYTE = 0x9, -+ DPNI_CNT_EGR_FRAME_DISCARD = 0xa -+}; -+ -+/** -+ * dpni_get_counter() - Read a specific DPNI counter -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @counter: The requested counter -+ * @value: Returned counter's current value -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_counter(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ enum dpni_counter counter, -+ uint64_t *value); -+ -+/** -+ * dpni_set_counter() - Set (or clear) a specific DPNI counter -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @counter: The requested counter -+ * @value: New counter value; typically pass '0' for resetting -+ * the counter. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_counter(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ enum dpni_counter counter, -+ uint64_t value); -+ -+/** -+ * Enable auto-negotiation -+ */ -+#define DPNI_LINK_OPT_AUTONEG 0x0000000000000001ULL -+/** -+ * Enable half-duplex mode -+ */ -+#define DPNI_LINK_OPT_HALF_DUPLEX 0x0000000000000002ULL -+/** -+ * Enable pause frames -+ */ -+#define DPNI_LINK_OPT_PAUSE 0x0000000000000004ULL -+/** -+ * Enable a-symmetric pause frames -+ */ -+#define DPNI_LINK_OPT_ASYM_PAUSE 0x0000000000000008ULL -+ -+/** -+ * struct - Structure representing DPNI link configuration -+ * @rate: Rate -+ * @options: Mask of available options; use 'DPNI_LINK_OPT_' values -+ */ -+struct dpni_link_cfg { -+ uint32_t rate; -+ uint64_t options; -+}; -+ -+/** -+ * dpni_set_link_cfg() - set the link configuration. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @cfg: Link configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_link_cfg(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_link_cfg *cfg); -+ -+/** -+ * struct dpni_link_state - Structure representing DPNI link state -+ * @rate: Rate -+ * @options: Mask of available options; use 'DPNI_LINK_OPT_' values -+ * @up: Link state; '0' for down, '1' for up -+ */ -+struct dpni_link_state { -+ uint32_t rate; -+ uint64_t options; -+ int up; -+}; -+ -+/** -+ * dpni_get_link_state() - Return the link state (either up or down) -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @state: Returned link state; -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_link_state(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpni_link_state *state); -+ -+/** -+ * struct dpni_tx_shaping - Structure representing DPNI tx shaping configuration -+ * @rate_limit: rate in Mbps -+ * @max_burst_size: burst size in bytes (up to 64KB) -+ */ -+struct dpni_tx_shaping_cfg { -+ uint32_t rate_limit; -+ uint16_t max_burst_size; -+}; -+ -+/** -+ * dpni_set_tx_shaping() - Set the transmit shaping -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @tx_shaper: tx shaping configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_tx_shaping(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_tx_shaping_cfg *tx_shaper); -+ -+/** -+ * dpni_set_max_frame_length() - Set the maximum received frame length. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @max_frame_length: Maximum received frame length (in -+ * bytes); frame is discarded if its -+ * length exceeds this value -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_max_frame_length(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t max_frame_length); -+ -+/** -+ * dpni_get_max_frame_length() - Get the maximum received frame length. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @max_frame_length: Maximum received frame length (in -+ * bytes); frame is discarded if its -+ * length exceeds this value -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_max_frame_length(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t *max_frame_length); -+ -+/** -+ * dpni_set_mtu() - Set the MTU for the interface. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @mtu: MTU length (in bytes) -+ * -+ * MTU determines the maximum fragment size for performing IP -+ * fragmentation on egress packets. -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_mtu(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t mtu); -+ -+/** -+ * dpni_get_mtu() - Get the MTU. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @mtu: Returned MTU length (in bytes) -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_mtu(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t *mtu); -+ -+/** -+ * dpni_set_multicast_promisc() - Enable/disable multicast promiscuous mode -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @en: Set to '1' to enable; '0' to disable -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_multicast_promisc(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en); -+ -+/** -+ * dpni_get_multicast_promisc() - Get multicast promiscuous mode -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @en: Returns '1' if enabled; '0' otherwise -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_multicast_promisc(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *en); -+ -+/** -+ * dpni_set_unicast_promisc() - Enable/disable unicast promiscuous mode -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @en: Set to '1' to enable; '0' to disable -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_unicast_promisc(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en); -+ -+/** -+ * dpni_get_unicast_promisc() - Get unicast promiscuous mode -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @en: Returns '1' if enabled; '0' otherwise -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_unicast_promisc(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *en); -+ -+/** -+ * dpni_set_primary_mac_addr() - Set the primary MAC address -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @mac_addr: MAC address to set as primary address -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_primary_mac_addr(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const uint8_t mac_addr[6]); -+ -+/** -+ * dpni_get_primary_mac_addr() - Get the primary MAC address -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @mac_addr: Returned MAC address -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_primary_mac_addr(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t mac_addr[6]); -+ -+/** -+ * dpni_add_mac_addr() - Add MAC address filter -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @mac_addr: MAC address to add -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_add_mac_addr(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const uint8_t mac_addr[6]); -+ -+/** -+ * dpni_remove_mac_addr() - Remove MAC address filter -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @mac_addr: MAC address to remove -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_remove_mac_addr(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const uint8_t mac_addr[6]); -+ -+/** -+ * dpni_clear_mac_filters() - Clear all unicast and/or multicast MAC filters -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @unicast: Set to '1' to clear unicast addresses -+ * @multicast: Set to '1' to clear multicast addresses -+ * -+ * The primary MAC address is not cleared by this operation. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_clear_mac_filters(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int unicast, -+ int multicast); -+ -+/** -+ * dpni_set_vlan_filters() - Enable/disable VLAN filtering mode -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @en: Set to '1' to enable; '0' to disable -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_vlan_filters(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en); -+ -+/** -+ * dpni_add_vlan_id() - Add VLAN ID filter -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @vlan_id: VLAN ID to add -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_add_vlan_id(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t vlan_id); -+ -+/** -+ * dpni_remove_vlan_id() - Remove VLAN ID filter -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @vlan_id: VLAN ID to remove -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_remove_vlan_id(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t vlan_id); -+ -+/** -+ * dpni_clear_vlan_filters() - Clear all VLAN filters -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_clear_vlan_filters(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * enum dpni_tx_schedule_mode - DPNI Tx scheduling mode -+ * @DPNI_TX_SCHED_STRICT_PRIORITY: strict priority -+ * @DPNI_TX_SCHED_WEIGHTED: weighted based scheduling -+ */ -+enum dpni_tx_schedule_mode { -+ DPNI_TX_SCHED_STRICT_PRIORITY, -+ DPNI_TX_SCHED_WEIGHTED, -+}; -+ -+/** -+ * struct dpni_tx_schedule_cfg - Structure representing Tx -+ * scheduling configuration -+ * @mode: scheduling mode -+ * @delta_bandwidth: Bandwidth represented in weights from 100 to 10000; -+ * not applicable for 'strict-priority' mode; -+ */ -+struct dpni_tx_schedule_cfg { -+ enum dpni_tx_schedule_mode mode; -+ uint16_t delta_bandwidth; -+}; -+ -+/** -+ * struct dpni_tx_selection_cfg - Structure representing transmission -+ * selection configuration -+ * @tc_sched: an array of traffic-classes -+ */ -+struct dpni_tx_selection_cfg { -+ struct dpni_tx_schedule_cfg tc_sched[DPNI_MAX_TC]; -+}; -+ -+/** -+ * dpni_set_tx_selection() - Set transmission selection configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @cfg: transmission selection configuration -+ * -+ * warning: Allowed only when DPNI is disabled -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_tx_selection(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_tx_selection_cfg *cfg); -+ -+/** -+ * enum dpni_dist_mode - DPNI distribution mode -+ * @DPNI_DIST_MODE_NONE: No distribution -+ * @DPNI_DIST_MODE_HASH: Use hash distribution; only relevant if -+ * the 'DPNI_OPT_DIST_HASH' option was set at DPNI creation -+ * @DPNI_DIST_MODE_FS: Use explicit flow steering; only relevant if -+ * the 'DPNI_OPT_DIST_FS' option was set at DPNI creation -+ */ -+enum dpni_dist_mode { -+ DPNI_DIST_MODE_NONE = 0, -+ DPNI_DIST_MODE_HASH = 1, -+ DPNI_DIST_MODE_FS = 2 -+}; -+ -+/** -+ * enum dpni_fs_miss_action - DPNI Flow Steering miss action -+ * @DPNI_FS_MISS_DROP: In case of no-match, drop the frame -+ * @DPNI_FS_MISS_EXPLICIT_FLOWID: In case of no-match, use explicit flow-id -+ * @DPNI_FS_MISS_HASH: In case of no-match, distribute using hash -+ */ -+enum dpni_fs_miss_action { -+ DPNI_FS_MISS_DROP = 0, -+ DPNI_FS_MISS_EXPLICIT_FLOWID = 1, -+ DPNI_FS_MISS_HASH = 2 -+}; -+ -+/** -+ * struct dpni_fs_tbl_cfg - Flow Steering table configuration -+ * @miss_action: Miss action selection -+ * @default_flow_id: Used when 'miss_action = DPNI_FS_MISS_EXPLICIT_FLOWID' -+ */ -+struct dpni_fs_tbl_cfg { -+ enum dpni_fs_miss_action miss_action; -+ uint16_t default_flow_id; -+}; -+ -+/** -+ * dpni_prepare_key_cfg() - function prepare extract parameters -+ * @cfg: defining a full Key Generation profile (rule) -+ * @key_cfg_buf: Zeroed 256 bytes of memory before mapping it to DMA -+ * -+ * This function has to be called before the following functions: -+ * - dpni_set_rx_tc_dist() -+ * - dpni_set_qos_table() -+ */ -+int dpni_prepare_key_cfg(const struct dpkg_profile_cfg *cfg, -+ uint8_t *key_cfg_buf); -+ -+/** -+ * struct dpni_rx_tc_dist_cfg - Rx traffic class distribution configuration -+ * @dist_size: Set the distribution size; -+ * supported values: 1,2,3,4,6,7,8,12,14,16,24,28,32,48,56,64,96, -+ * 112,128,192,224,256,384,448,512,768,896,1024 -+ * @dist_mode: Distribution mode -+ * @key_cfg_iova: I/O virtual address of 256 bytes DMA-able memory filled with -+ * the extractions to be used for the distribution key by calling -+ * dpni_prepare_key_cfg() relevant only when -+ * 'dist_mode != DPNI_DIST_MODE_NONE', otherwise it can be '0' -+ * @fs_cfg: Flow Steering table configuration; only relevant if -+ * 'dist_mode = DPNI_DIST_MODE_FS' -+ */ -+struct dpni_rx_tc_dist_cfg { -+ uint16_t dist_size; -+ enum dpni_dist_mode dist_mode; -+ uint64_t key_cfg_iova; -+ struct dpni_fs_tbl_cfg fs_cfg; -+}; -+ -+/** -+ * dpni_set_rx_tc_dist() - Set Rx traffic class distribution configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @tc_id: Traffic class selection (0-7) -+ * @cfg: Traffic class distribution configuration -+ * -+ * warning: if 'dist_mode != DPNI_DIST_MODE_NONE', call dpni_prepare_key_cfg() -+ * first to prepare the key_cfg_iova parameter -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpni_set_rx_tc_dist(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ const struct dpni_rx_tc_dist_cfg *cfg); -+ -+/** -+ * Set to select color aware mode (otherwise - color blind) -+ */ -+#define DPNI_POLICER_OPT_COLOR_AWARE 0x00000001 -+/** -+ * Set to discard frame with RED color -+ */ -+#define DPNI_POLICER_OPT_DISCARD_RED 0x00000002 -+ -+/** -+ * enum dpni_policer_mode - selecting the policer mode -+ * @DPNI_POLICER_MODE_NONE: Policer is disabled -+ * @DPNI_POLICER_MODE_PASS_THROUGH: Policer pass through -+ * @DPNI_POLICER_MODE_RFC_2698: Policer algorithm RFC 2698 -+ * @DPNI_POLICER_MODE_RFC_4115: Policer algorithm RFC 4115 -+ */ -+enum dpni_policer_mode { -+ DPNI_POLICER_MODE_NONE = 0, -+ DPNI_POLICER_MODE_PASS_THROUGH, -+ DPNI_POLICER_MODE_RFC_2698, -+ DPNI_POLICER_MODE_RFC_4115 -+}; -+ -+/** -+ * enum dpni_policer_unit - DPNI policer units -+ * @DPNI_POLICER_UNIT_BYTES: bytes units -+ * @DPNI_POLICER_UNIT_FRAMES: frames units -+ */ -+enum dpni_policer_unit { -+ DPNI_POLICER_UNIT_BYTES = 0, -+ DPNI_POLICER_UNIT_FRAMES -+}; -+ -+/** -+ * enum dpni_policer_color - selecting the policer color -+ * @DPNI_POLICER_COLOR_GREEN: Green color -+ * @DPNI_POLICER_COLOR_YELLOW: Yellow color -+ * @DPNI_POLICER_COLOR_RED: Red color -+ */ -+enum dpni_policer_color { -+ DPNI_POLICER_COLOR_GREEN = 0, -+ DPNI_POLICER_COLOR_YELLOW, -+ DPNI_POLICER_COLOR_RED -+}; -+ -+/** -+ * struct dpni_rx_tc_policing_cfg - Policer configuration -+ * @options: Mask of available options; use 'DPNI_POLICER_OPT_' values -+ * @mode: policer mode -+ * @default_color: For pass-through mode the policer re-colors with this -+ * color any incoming packets. For Color aware non-pass-through mode: -+ * policer re-colors with this color all packets with FD[DROPP]>2. -+ * @units: Bytes or Packets -+ * @cir: Committed information rate (CIR) in Kbps or packets/second -+ * @cbs: Committed burst size (CBS) in bytes or packets -+ * @eir: Peak information rate (PIR, rfc2698) in Kbps or packets/second -+ * Excess information rate (EIR, rfc4115) in Kbps or packets/second -+ * @ebs: Peak burst size (PBS, rfc2698) in bytes or packets -+ * Excess burst size (EBS, rfc4115) in bytes or packets -+ */ -+struct dpni_rx_tc_policing_cfg { -+ uint32_t options; -+ enum dpni_policer_mode mode; -+ enum dpni_policer_unit units; -+ enum dpni_policer_color default_color; -+ uint32_t cir; -+ uint32_t cbs; -+ uint32_t eir; -+ uint32_t ebs; -+}; -+ -+/** -+ * dpni_set_rx_tc_policing() - Set Rx traffic class policing configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @tc_id: Traffic class selection (0-7) -+ * @cfg: Traffic class policing configuration -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpni_set_rx_tc_policing(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ const struct dpni_rx_tc_policing_cfg *cfg); -+ -+/** -+ * dpni_get_rx_tc_policing() - Get Rx traffic class policing configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @tc_id: Traffic class selection (0-7) -+ * @cfg: Traffic class policing configuration -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpni_get_rx_tc_policing(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ struct dpni_rx_tc_policing_cfg *cfg); -+ -+/** -+ * enum dpni_congestion_unit - DPNI congestion units -+ * @DPNI_CONGESTION_UNIT_BYTES: bytes units -+ * @DPNI_CONGESTION_UNIT_FRAMES: frames units -+ */ -+enum dpni_congestion_unit { -+ DPNI_CONGESTION_UNIT_BYTES = 0, -+ DPNI_CONGESTION_UNIT_FRAMES -+}; -+ -+/** -+ * enum dpni_early_drop_mode - DPNI early drop mode -+ * @DPNI_EARLY_DROP_MODE_NONE: early drop is disabled -+ * @DPNI_EARLY_DROP_MODE_TAIL: early drop in taildrop mode -+ * @DPNI_EARLY_DROP_MODE_WRED: early drop in WRED mode -+ */ -+enum dpni_early_drop_mode { -+ DPNI_EARLY_DROP_MODE_NONE = 0, -+ DPNI_EARLY_DROP_MODE_TAIL, -+ DPNI_EARLY_DROP_MODE_WRED -+}; -+ -+/** -+ * struct dpni_wred_cfg - WRED configuration -+ * @max_threshold: maximum threshold that packets may be discarded. Above this -+ * threshold all packets are discarded; must be less than 2^39; -+ * approximated to be expressed as (x+256)*2^(y-1) due to HW -+ * implementation. -+ * @min_threshold: minimum threshold that packets may be discarded at -+ * @drop_probability: probability that a packet will be discarded (1-100, -+ * associated with the max_threshold). -+ */ -+struct dpni_wred_cfg { -+ uint64_t max_threshold; -+ uint64_t min_threshold; -+ uint8_t drop_probability; -+}; -+ -+/** -+ * struct dpni_early_drop_cfg - early-drop configuration -+ * @mode: drop mode -+ * @units: units type -+ * @green: WRED - 'green' configuration -+ * @yellow: WRED - 'yellow' configuration -+ * @red: WRED - 'red' configuration -+ * @tail_drop_threshold: tail drop threshold -+ */ -+struct dpni_early_drop_cfg { -+ enum dpni_early_drop_mode mode; -+ enum dpni_congestion_unit units; -+ -+ struct dpni_wred_cfg green; -+ struct dpni_wred_cfg yellow; -+ struct dpni_wred_cfg red; -+ -+ uint32_t tail_drop_threshold; -+}; -+ -+/** -+ * dpni_prepare_early_drop() - prepare an early drop. -+ * @cfg: Early-drop configuration -+ * @early_drop_buf: Zeroed 256 bytes of memory before mapping it to DMA -+ * -+ * This function has to be called before dpni_set_rx_tc_early_drop or -+ * dpni_set_tx_tc_early_drop -+ * -+ */ -+void dpni_prepare_early_drop(const struct dpni_early_drop_cfg *cfg, -+ uint8_t *early_drop_buf); -+ -+/** -+ * dpni_extract_early_drop() - extract the early drop configuration. -+ * @cfg: Early-drop configuration -+ * @early_drop_buf: Zeroed 256 bytes of memory before mapping it to DMA -+ * -+ * This function has to be called after dpni_get_rx_tc_early_drop or -+ * dpni_get_tx_tc_early_drop -+ * -+ */ -+void dpni_extract_early_drop(struct dpni_early_drop_cfg *cfg, -+ const uint8_t *early_drop_buf); -+ -+/** -+ * dpni_set_rx_tc_early_drop() - Set Rx traffic class early-drop configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @tc_id: Traffic class selection (0-7) -+ * @early_drop_iova: I/O virtual address of 256 bytes DMA-able memory filled -+ * with the early-drop configuration by calling dpni_prepare_early_drop() -+ * -+ * warning: Before calling this function, call dpni_prepare_early_drop() to -+ * prepare the early_drop_iova parameter -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpni_set_rx_tc_early_drop(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ uint64_t early_drop_iova); -+ -+/** -+ * dpni_get_rx_tc_early_drop() - Get Rx traffic class early-drop configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @tc_id: Traffic class selection (0-7) -+ * @early_drop_iova: I/O virtual address of 256 bytes DMA-able memory -+ * -+ * warning: After calling this function, call dpni_extract_early_drop() to -+ * get the early drop configuration -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpni_get_rx_tc_early_drop(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ uint64_t early_drop_iova); -+ -+/** -+ * dpni_set_tx_tc_early_drop() - Set Tx traffic class early-drop configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @tc_id: Traffic class selection (0-7) -+ * @early_drop_iova: I/O virtual address of 256 bytes DMA-able memory filled -+ * with the early-drop configuration by calling dpni_prepare_early_drop() -+ * -+ * warning: Before calling this function, call dpni_prepare_early_drop() to -+ * prepare the early_drop_iova parameter -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpni_set_tx_tc_early_drop(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ uint64_t early_drop_iova); -+ -+/** -+ * dpni_get_tx_tc_early_drop() - Get Tx traffic class early-drop configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @tc_id: Traffic class selection (0-7) -+ * @early_drop_iova: I/O virtual address of 256 bytes DMA-able memory -+ * -+ * warning: After calling this function, call dpni_extract_early_drop() to -+ * get the early drop configuration -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpni_get_tx_tc_early_drop(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ uint64_t early_drop_iova); -+ -+/** -+ * enum dpni_dest - DPNI destination types -+ * @DPNI_DEST_NONE: Unassigned destination; The queue is set in parked mode and -+ * does not generate FQDAN notifications; user is expected to -+ * dequeue from the queue based on polling or other user-defined -+ * method -+ * @DPNI_DEST_DPIO: The queue is set in schedule mode and generates FQDAN -+ * notifications to the specified DPIO; user is expected to dequeue -+ * from the queue only after notification is received -+ * @DPNI_DEST_DPCON: The queue is set in schedule mode and does not generate -+ * FQDAN notifications, but is connected to the specified DPCON -+ * object; user is expected to dequeue from the DPCON channel -+ */ -+enum dpni_dest { -+ DPNI_DEST_NONE = 0, -+ DPNI_DEST_DPIO = 1, -+ DPNI_DEST_DPCON = 2 -+}; -+ -+/** -+ * struct dpni_dest_cfg - Structure representing DPNI destination parameters -+ * @dest_type: Destination type -+ * @dest_id: Either DPIO ID or DPCON ID, depending on the destination type -+ * @priority: Priority selection within the DPIO or DPCON channel; valid values -+ * are 0-1 or 0-7, depending on the number of priorities in that -+ * channel; not relevant for 'DPNI_DEST_NONE' option -+ */ -+struct dpni_dest_cfg { -+ enum dpni_dest dest_type; -+ int dest_id; -+ uint8_t priority; -+}; -+ -+/* DPNI congestion options */ -+ -+/** -+ * CSCN message is written to message_iova once entering a -+ * congestion state (see 'threshold_entry') -+ */ -+#define DPNI_CONG_OPT_WRITE_MEM_ON_ENTER 0x00000001 -+/** -+ * CSCN message is written to message_iova once exiting a -+ * congestion state (see 'threshold_exit') -+ */ -+#define DPNI_CONG_OPT_WRITE_MEM_ON_EXIT 0x00000002 -+/** -+ * CSCN write will attempt to allocate into a cache (coherent write); -+ * valid only if 'DPNI_CONG_OPT_WRITE_MEM_' is selected -+ */ -+#define DPNI_CONG_OPT_COHERENT_WRITE 0x00000004 -+/** -+ * if 'dest_cfg.dest_type != DPNI_DEST_NONE' CSCN message is sent to -+ * DPIO/DPCON's WQ channel once entering a congestion state -+ * (see 'threshold_entry') -+ */ -+#define DPNI_CONG_OPT_NOTIFY_DEST_ON_ENTER 0x00000008 -+/** -+ * if 'dest_cfg.dest_type != DPNI_DEST_NONE' CSCN message is sent to -+ * DPIO/DPCON's WQ channel once exiting a congestion state -+ * (see 'threshold_exit') -+ */ -+#define DPNI_CONG_OPT_NOTIFY_DEST_ON_EXIT 0x00000010 -+/** -+ * if 'dest_cfg.dest_type != DPNI_DEST_NONE' when the CSCN is written to the -+ * sw-portal's DQRR, the DQRI interrupt is asserted immediately (if enabled) -+ */ -+#define DPNI_CONG_OPT_INTR_COALESCING_DISABLED 0x00000020 -+ -+/** -+ * struct dpni_congestion_notification_cfg - congestion notification -+ * configuration -+ * @units: units type -+ * @threshold_entry: above this threshold we enter a congestion state. -+ * set it to '0' to disable it -+ * @threshold_exit: below this threshold we exit the congestion state. -+ * @message_ctx: The context that will be part of the CSCN message -+ * @message_iova: I/O virtual address (must be in DMA-able memory), -+ * must be 16B aligned; valid only if 'DPNI_CONG_OPT_WRITE_MEM_' is -+ * contained in 'options' -+ * @dest_cfg: CSCN can be send to either DPIO or DPCON WQ channel -+ * @options: Mask of available options; use 'DPNI_CONG_OPT_' values -+ */ -+ -+struct dpni_congestion_notification_cfg { -+ enum dpni_congestion_unit units; -+ uint32_t threshold_entry; -+ uint32_t threshold_exit; -+ uint64_t message_ctx; -+ uint64_t message_iova; -+ struct dpni_dest_cfg dest_cfg; -+ uint16_t options; -+}; -+ -+/** -+ * dpni_set_rx_tc_congestion_notification() - Set Rx traffic class congestion -+ * notification configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @tc_id: Traffic class selection (0-7) -+ * @cfg: congestion notification configuration -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpni_set_rx_tc_congestion_notification(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ const struct dpni_congestion_notification_cfg *cfg); -+ -+/** -+ * dpni_get_rx_tc_congestion_notification() - Get Rx traffic class congestion -+ * notification configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @tc_id: Traffic class selection (0-7) -+ * @cfg: congestion notification configuration -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpni_get_rx_tc_congestion_notification(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ struct dpni_congestion_notification_cfg *cfg); -+ -+/** -+ * dpni_set_tx_tc_congestion_notification() - Set Tx traffic class congestion -+ * notification configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @tc_id: Traffic class selection (0-7) -+ * @cfg: congestion notification configuration -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpni_set_tx_tc_congestion_notification(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ const struct dpni_congestion_notification_cfg *cfg); -+ -+/** -+ * dpni_get_tx_tc_congestion_notification() - Get Tx traffic class congestion -+ * notification configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @tc_id: Traffic class selection (0-7) -+ * @cfg: congestion notification configuration -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpni_get_tx_tc_congestion_notification(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ struct dpni_congestion_notification_cfg *cfg); -+ -+/** -+ * enum dpni_flc_type - DPNI FLC types -+ * @DPNI_FLC_USER_DEFINED: select the FLC to be used for user defined value -+ * @DPNI_FLC_STASH: select the FLC to be used for stash control -+ */ -+enum dpni_flc_type { -+ DPNI_FLC_USER_DEFINED = 0, -+ DPNI_FLC_STASH = 1, -+}; -+ -+/** -+ * enum dpni_stash_size - DPNI FLC stashing size -+ * @DPNI_STASH_SIZE_0B: no stash -+ * @DPNI_STASH_SIZE_64B: stashes 64 bytes -+ * @DPNI_STASH_SIZE_128B: stashes 128 bytes -+ * @DPNI_STASH_SIZE_192B: stashes 192 bytes -+ */ -+enum dpni_stash_size { -+ DPNI_STASH_SIZE_0B = 0, -+ DPNI_STASH_SIZE_64B = 1, -+ DPNI_STASH_SIZE_128B = 2, -+ DPNI_STASH_SIZE_192B = 3, -+}; -+ -+/* DPNI FLC stash options */ -+ -+/** -+ * stashes the whole annotation area (up to 192 bytes) -+ */ -+#define DPNI_FLC_STASH_FRAME_ANNOTATION 0x00000001 -+ -+/** -+ * struct dpni_flc_cfg - Structure representing DPNI FLC configuration -+ * @flc_type: FLC type -+ * @options: Mask of available options; -+ * use 'DPNI_FLC_STASH_' values -+ * @frame_data_size: Size of frame data to be stashed -+ * @flow_context_size: Size of flow context to be stashed -+ * @flow_context: 1. In case flc_type is 'DPNI_FLC_USER_DEFINED': -+ * this value will be provided in the frame descriptor -+ * (FD[FLC]) -+ * 2. In case flc_type is 'DPNI_FLC_STASH': -+ * this value will be I/O virtual address of the -+ * flow-context; -+ * Must be cacheline-aligned and DMA-able memory -+ */ -+struct dpni_flc_cfg { -+ enum dpni_flc_type flc_type; -+ uint32_t options; -+ enum dpni_stash_size frame_data_size; -+ enum dpni_stash_size flow_context_size; -+ uint64_t flow_context; -+}; -+ -+/** -+ * DPNI queue modification options -+ */ -+ -+/** -+ * Select to modify the user's context associated with the queue -+ */ -+#define DPNI_QUEUE_OPT_USER_CTX 0x00000001 -+/** -+ * Select to modify the queue's destination -+ */ -+#define DPNI_QUEUE_OPT_DEST 0x00000002 -+/** Select to modify the flow-context parameters; -+ * not applicable for Tx-conf/Err queues as the FD comes from the user -+ */ -+#define DPNI_QUEUE_OPT_FLC 0x00000004 -+/** -+ * Select to modify the queue's order preservation -+ */ -+#define DPNI_QUEUE_OPT_ORDER_PRESERVATION 0x00000008 -+/* Select to modify the queue's tail-drop threshold */ -+#define DPNI_QUEUE_OPT_TAILDROP_THRESHOLD 0x00000010 -+ -+/** -+ * struct dpni_queue_cfg - Structure representing queue configuration -+ * @options: Flags representing the suggested modifications to the queue; -+ * Use any combination of 'DPNI_QUEUE_OPT_' flags -+ * @user_ctx: User context value provided in the frame descriptor of each -+ * dequeued frame; valid only if 'DPNI_QUEUE_OPT_USER_CTX' -+ * is contained in 'options' -+ * @dest_cfg: Queue destination parameters; -+ * valid only if 'DPNI_QUEUE_OPT_DEST' is contained in 'options' -+ * @flc_cfg: Flow context configuration; in case the TC's distribution -+ * is either NONE or HASH the FLC's settings of flow#0 are used. -+ * in the case of FS (flow-steering) the flow's FLC settings -+ * are used. -+ * valid only if 'DPNI_QUEUE_OPT_FLC' is contained in 'options' -+ * @order_preservation_en: enable/disable order preservation; -+ * valid only if 'DPNI_QUEUE_OPT_ORDER_PRESERVATION' is contained -+ * in 'options' -+ * @tail_drop_threshold: set the queue's tail drop threshold in bytes; -+ * '0' value disable the threshold; maximum value is 0xE000000; -+ * valid only if 'DPNI_QUEUE_OPT_TAILDROP_THRESHOLD' is contained -+ * in 'options' -+ */ -+struct dpni_queue_cfg { -+ uint32_t options; -+ uint64_t user_ctx; -+ struct dpni_dest_cfg dest_cfg; -+ struct dpni_flc_cfg flc_cfg; -+ int order_preservation_en; -+ uint32_t tail_drop_threshold; -+}; -+ -+/** -+ * struct dpni_queue_attr - Structure representing queue attributes -+ * @user_ctx: User context value provided in the frame descriptor of each -+ * dequeued frame -+ * @dest_cfg: Queue destination configuration -+ * @flc_cfg: Flow context configuration -+ * @order_preservation_en: enable/disable order preservation -+ * @tail_drop_threshold: queue's tail drop threshold in bytes; -+ * @fqid: Virtual fqid value to be used for dequeue operations -+ */ -+struct dpni_queue_attr { -+ uint64_t user_ctx; -+ struct dpni_dest_cfg dest_cfg; -+ struct dpni_flc_cfg flc_cfg; -+ int order_preservation_en; -+ uint32_t tail_drop_threshold; -+ -+ uint32_t fqid; -+}; -+ -+/** -+ * DPNI Tx flow modification options -+ */ -+ -+/** -+ * Select to modify the settings for dedicate Tx confirmation/error -+ */ -+#define DPNI_TX_FLOW_OPT_TX_CONF_ERROR 0x00000001 -+/** -+ * Select to modify the L3 checksum generation setting -+ */ -+#define DPNI_TX_FLOW_OPT_L3_CHKSUM_GEN 0x00000010 -+/** -+ * Select to modify the L4 checksum generation setting -+ */ -+#define DPNI_TX_FLOW_OPT_L4_CHKSUM_GEN 0x00000020 -+ -+/** -+ * struct dpni_tx_flow_cfg - Structure representing Tx flow configuration -+ * @options: Flags representing the suggested modifications to the Tx flow; -+ * Use any combination 'DPNI_TX_FLOW_OPT_' flags -+ * @use_common_tx_conf_queue: Set to '1' to use the common (default) Tx -+ * confirmation and error queue; Set to '0' to use the private -+ * Tx confirmation and error queue; valid only if -+ * 'DPNI_OPT_PRIVATE_TX_CONF_ERROR_DISABLED' wasn't set at DPNI creation -+ * and 'DPNI_TX_FLOW_OPT_TX_CONF_ERROR' is contained in 'options' -+ * @l3_chksum_gen: Set to '1' to enable L3 checksum generation; '0' to disable; -+ * valid only if 'DPNI_TX_FLOW_OPT_L3_CHKSUM_GEN' is contained in 'options' -+ * @l4_chksum_gen: Set to '1' to enable L4 checksum generation; '0' to disable; -+ * valid only if 'DPNI_TX_FLOW_OPT_L4_CHKSUM_GEN' is contained in 'options' -+ */ -+struct dpni_tx_flow_cfg { -+ uint32_t options; -+ int use_common_tx_conf_queue; -+ int l3_chksum_gen; -+ int l4_chksum_gen; -+}; -+ -+/** -+ * dpni_set_tx_flow() - Set Tx flow configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @flow_id: Provides (or returns) the sender's flow ID; -+ * for each new sender set (*flow_id) to 'DPNI_NEW_FLOW_ID' to generate -+ * a new flow_id; this ID should be used as the QDBIN argument -+ * in enqueue operations -+ * @cfg: Tx flow configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_tx_flow(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t *flow_id, -+ const struct dpni_tx_flow_cfg *cfg); -+ -+/** -+ * struct dpni_tx_flow_attr - Structure representing Tx flow attributes -+ * @use_common_tx_conf_queue: '1' if using common (default) Tx confirmation and -+ * error queue; '0' if using private Tx confirmation and error queue -+ * @l3_chksum_gen: '1' if L3 checksum generation is enabled; '0' if disabled -+ * @l4_chksum_gen: '1' if L4 checksum generation is enabled; '0' if disabled -+ */ -+struct dpni_tx_flow_attr { -+ int use_common_tx_conf_queue; -+ int l3_chksum_gen; -+ int l4_chksum_gen; -+}; -+ -+/** -+ * dpni_get_tx_flow() - Get Tx flow attributes -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @flow_id: The sender's flow ID, as returned by the -+ * dpni_set_tx_flow() function -+ * @attr: Returned Tx flow attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_tx_flow(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t flow_id, -+ struct dpni_tx_flow_attr *attr); -+ -+/** -+ * struct dpni_tx_conf_cfg - Structure representing Tx conf configuration -+ * @errors_only: Set to '1' to report back only error frames; -+ * Set to '0' to confirm transmission/error for all transmitted frames; -+ * @queue_cfg: Queue configuration -+ */ -+struct dpni_tx_conf_cfg { -+ int errors_only; -+ struct dpni_queue_cfg queue_cfg; -+}; -+ -+/** -+ * dpni_set_tx_conf() - Set Tx confirmation and error queue configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @flow_id: The sender's flow ID, as returned by the -+ * dpni_set_tx_flow() function; -+ * use 'DPNI_COMMON_TX_CONF' for common tx-conf -+ * @cfg: Queue configuration -+ * -+ * If either 'DPNI_OPT_TX_CONF_DISABLED' or -+ * 'DPNI_OPT_PRIVATE_TX_CONF_ERROR_DISABLED' were selected at DPNI creation, -+ * this function can ONLY be used with 'flow_id == DPNI_COMMON_TX_CONF'; -+ * i.e. only serve the common tx-conf-err queue; -+ * if 'DPNI_OPT_TX_CONF_DISABLED' was selected, only error frames are reported -+ * back - successfully transmitted frames are not confirmed. Otherwise, all -+ * transmitted frames are sent for confirmation. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_tx_conf(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t flow_id, -+ const struct dpni_tx_conf_cfg *cfg); -+ -+/** -+ * struct dpni_tx_conf_attr - Structure representing Tx conf attributes -+ * @errors_only: '1' if only error frames are reported back; '0' if all -+ * transmitted frames are confirmed -+ * @queue_attr: Queue attributes -+ */ -+struct dpni_tx_conf_attr { -+ int errors_only; -+ struct dpni_queue_attr queue_attr; -+}; -+ -+/** -+ * dpni_get_tx_conf() - Get Tx confirmation and error queue attributes -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @flow_id: The sender's flow ID, as returned by the -+ * dpni_set_tx_flow() function; -+ * use 'DPNI_COMMON_TX_CONF' for common tx-conf -+ * @attr: Returned tx-conf attributes -+ * -+ * If either 'DPNI_OPT_TX_CONF_DISABLED' or -+ * 'DPNI_OPT_PRIVATE_TX_CONF_ERROR_DISABLED' were selected at DPNI creation, -+ * this function can ONLY be used with 'flow_id == DPNI_COMMON_TX_CONF'; -+ * i.e. only serve the common tx-conf-err queue; -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_tx_conf(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t flow_id, -+ struct dpni_tx_conf_attr *attr); -+ -+/** -+ * dpni_set_tx_conf_congestion_notification() - Set Tx conf congestion -+ * notification configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @flow_id: The sender's flow ID, as returned by the -+ * dpni_set_tx_flow() function; -+ * use 'DPNI_COMMON_TX_CONF' for common tx-conf -+ * @cfg: congestion notification configuration -+ * -+ * If either 'DPNI_OPT_TX_CONF_DISABLED' or -+ * 'DPNI_OPT_PRIVATE_TX_CONF_ERROR_DISABLED' were selected at DPNI creation, -+ * this function can ONLY be used with 'flow_id == DPNI_COMMON_TX_CONF'; -+ * i.e. only serve the common tx-conf-err queue; -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpni_set_tx_conf_congestion_notification(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t flow_id, -+ const struct dpni_congestion_notification_cfg *cfg); -+ -+/** -+ * dpni_get_tx_conf_congestion_notification() - Get Tx conf congestion -+ * notification configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @flow_id: The sender's flow ID, as returned by the -+ * dpni_set_tx_flow() function; -+ * use 'DPNI_COMMON_TX_CONF' for common tx-conf -+ * @cfg: congestion notification -+ * -+ * If either 'DPNI_OPT_TX_CONF_DISABLED' or -+ * 'DPNI_OPT_PRIVATE_TX_CONF_ERROR_DISABLED' were selected at DPNI creation, -+ * this function can ONLY be used with 'flow_id == DPNI_COMMON_TX_CONF'; -+ * i.e. only serve the common tx-conf-err queue; -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpni_get_tx_conf_congestion_notification(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t flow_id, -+ struct dpni_congestion_notification_cfg *cfg); -+ -+/** -+ * dpni_set_tx_conf_revoke() - Tx confirmation revocation -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @revoke: revoke or not -+ * -+ * This function is useful only when 'DPNI_OPT_TX_CONF_DISABLED' is not -+ * selected at DPNI creation. -+ * Calling this function with 'revoke' set to '1' disables all transmit -+ * confirmation (including the private confirmation queues), regardless of -+ * previous settings; Note that in this case, Tx error frames are still -+ * enqueued to the general transmit errors queue. -+ * Calling this function with 'revoke' set to '0' restores the previous -+ * settings for both general and private transmit confirmation. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_tx_conf_revoke(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int revoke); -+ -+/** -+ * dpni_set_rx_flow() - Set Rx flow configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @tc_id: Traffic class selection (0-7); -+ * use 'DPNI_ALL_TCS' to set all TCs and all flows -+ * @flow_id: Rx flow id within the traffic class; use -+ * 'DPNI_ALL_TC_FLOWS' to set all flows within -+ * this tc_id; ignored if tc_id is set to -+ * 'DPNI_ALL_TCS'; -+ * @cfg: Rx flow configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_rx_flow(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ uint16_t flow_id, -+ const struct dpni_queue_cfg *cfg); -+ -+/** -+ * dpni_get_rx_flow() - Get Rx flow attributes -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @tc_id: Traffic class selection (0-7) -+ * @flow_id: Rx flow id within the traffic class -+ * @attr: Returned Rx flow attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_rx_flow(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ uint16_t flow_id, -+ struct dpni_queue_attr *attr); -+ -+/** -+ * dpni_set_rx_err_queue() - Set Rx error queue configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @cfg: Queue configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_rx_err_queue(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_queue_cfg *cfg); -+ -+/** -+ * dpni_get_rx_err_queue() - Get Rx error queue attributes -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @attr: Returned Queue attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_rx_err_queue(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpni_queue_attr *attr); -+ -+/** -+ * struct dpni_qos_tbl_cfg - Structure representing QOS table configuration -+ * @key_cfg_iova: I/O virtual address of 256 bytes DMA-able memory filled with -+ * key extractions to be used as the QoS criteria by calling -+ * dpni_prepare_key_cfg() -+ * @discard_on_miss: Set to '1' to discard frames in case of no match (miss); -+ * '0' to use the 'default_tc' in such cases -+ * @default_tc: Used in case of no-match and 'discard_on_miss'= 0 -+ */ -+struct dpni_qos_tbl_cfg { -+ uint64_t key_cfg_iova; -+ int discard_on_miss; -+ uint8_t default_tc; -+}; -+ -+/** -+ * dpni_set_qos_table() - Set QoS mapping table -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @cfg: QoS table configuration -+ * -+ * This function and all QoS-related functions require that -+ *'max_tcs > 1' was set at DPNI creation. -+ * -+ * warning: Before calling this function, call dpni_prepare_key_cfg() to -+ * prepare the key_cfg_iova parameter -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_qos_table(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_qos_tbl_cfg *cfg); -+ -+/** -+ * struct dpni_rule_cfg - Rule configuration for table lookup -+ * @key_iova: I/O virtual address of the key (must be in DMA-able memory) -+ * @mask_iova: I/O virtual address of the mask (must be in DMA-able memory) -+ * @key_size: key and mask size (in bytes) -+ */ -+struct dpni_rule_cfg { -+ uint64_t key_iova; -+ uint64_t mask_iova; -+ uint8_t key_size; -+}; -+ -+/** -+ * dpni_add_qos_entry() - Add QoS mapping entry (to select a traffic class) -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @cfg: QoS rule to add -+ * @tc_id: Traffic class selection (0-7) -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_add_qos_entry(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_rule_cfg *cfg, -+ uint8_t tc_id); -+ -+/** -+ * dpni_remove_qos_entry() - Remove QoS mapping entry -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @cfg: QoS rule to remove -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_remove_qos_entry(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_rule_cfg *cfg); -+ -+/** -+ * dpni_clear_qos_table() - Clear all QoS mapping entries -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * -+ * Following this function call, all frames are directed to -+ * the default traffic class (0) -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_clear_qos_table(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * dpni_add_fs_entry() - Add Flow Steering entry for a specific traffic class -+ * (to select a flow ID) -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @tc_id: Traffic class selection (0-7) -+ * @cfg: Flow steering rule to add -+ * @flow_id: Flow id selection (must be smaller than the -+ * distribution size of the traffic class) -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_add_fs_entry(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ const struct dpni_rule_cfg *cfg, -+ uint16_t flow_id); -+ -+/** -+ * dpni_remove_fs_entry() - Remove Flow Steering entry from a specific -+ * traffic class -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @tc_id: Traffic class selection (0-7) -+ * @cfg: Flow steering rule to remove -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_remove_fs_entry(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ const struct dpni_rule_cfg *cfg); -+ -+/** -+ * dpni_clear_fs_entries() - Clear all Flow Steering entries of a specific -+ * traffic class -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @tc_id: Traffic class selection (0-7) -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_clear_fs_entries(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id); -+ -+/** -+ * dpni_set_vlan_insertion() - Enable/disable VLAN insertion for egress frames -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @en: Set to '1' to enable; '0' to disable -+ * -+ * Requires that the 'DPNI_OPT_VLAN_MANIPULATION' option is set -+ * at DPNI creation. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_vlan_insertion(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en); -+ -+/** -+ * dpni_set_vlan_removal() - Enable/disable VLAN removal for ingress frames -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @en: Set to '1' to enable; '0' to disable -+ * -+ * Requires that the 'DPNI_OPT_VLAN_MANIPULATION' option is set -+ * at DPNI creation. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_vlan_removal(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en); -+ -+/** -+ * dpni_set_ipr() - Enable/disable IP reassembly of ingress frames -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @en: Set to '1' to enable; '0' to disable -+ * -+ * Requires that the 'DPNI_OPT_IPR' option is set at DPNI creation. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_ipr(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en); -+ -+/** -+ * dpni_set_ipf() - Enable/disable IP fragmentation of egress frames -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @en: Set to '1' to enable; '0' to disable -+ * -+ * Requires that the 'DPNI_OPT_IPF' option is set at DPNI -+ * creation. Fragmentation is performed according to MTU value -+ * set by dpni_set_mtu() function -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_ipf(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en); -+ -+#endif /* __FSL_DPNI_H */ -diff --git a/drivers/staging/fsl-dpaa2/mac/Kconfig b/drivers/staging/fsl-dpaa2/mac/Kconfig -new file mode 100644 -index 0000000..174a9cd ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/mac/Kconfig -@@ -0,0 +1,24 @@ -+config FSL_DPAA2_MAC -+ tristate "DPAA2 MAC / PHY interface" -+ depends on FSL_MC_BUS && FSL_DPAA2 -+ select MDIO_BUS_MUX_MMIOREG -+ select FSL_XGMAC_MDIO -+ select FIXED_PHY -+ ---help--- -+ Prototype driver for DPAA2 MAC / PHY interface object. -+ This driver works as a proxy between phylib including phy drivers and -+ the MC firmware. It receives updates on link state changes from PHY -+ lib and forwards them to MC and receives interrupt from MC whenever -+ a request is made to change the link state. -+ -+ -+config FSL_DPAA2_MAC_NETDEVS -+ bool "Expose net interfaces for PHYs" -+ default n -+ depends on FSL_DPAA2_MAC -+ ---help--- -+ Exposes macX net interfaces which allow direct control over MACs and -+ PHYs. -+ . -+ Leave disabled if unsure. -+ -diff --git a/drivers/staging/fsl-dpaa2/mac/Makefile b/drivers/staging/fsl-dpaa2/mac/Makefile -new file mode 100644 -index 0000000..bda9410 ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/mac/Makefile -@@ -0,0 +1,10 @@ -+ -+obj-$(CONFIG_FSL_DPAA2_MAC) += dpaa2-mac.o -+ -+dpaa2-mac-objs := mac.o dpmac.o -+ -+all: -+ make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules -+ -+clean: -+ make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean -diff --git a/drivers/staging/fsl-dpaa2/mac/dpmac-cmd.h b/drivers/staging/fsl-dpaa2/mac/dpmac-cmd.h -new file mode 100644 -index 0000000..dc00590 ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/mac/dpmac-cmd.h -@@ -0,0 +1,195 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef _FSL_DPMAC_CMD_H -+#define _FSL_DPMAC_CMD_H -+ -+/* DPMAC Version */ -+#define DPMAC_VER_MAJOR 3 -+#define DPMAC_VER_MINOR 2 -+ -+/* Command IDs */ -+#define DPMAC_CMDID_CLOSE 0x800 -+#define DPMAC_CMDID_OPEN 0x80c -+#define DPMAC_CMDID_CREATE 0x90c -+#define DPMAC_CMDID_DESTROY 0x900 -+ -+#define DPMAC_CMDID_GET_ATTR 0x004 -+#define DPMAC_CMDID_RESET 0x005 -+ -+#define DPMAC_CMDID_SET_IRQ 0x010 -+#define DPMAC_CMDID_GET_IRQ 0x011 -+#define DPMAC_CMDID_SET_IRQ_ENABLE 0x012 -+#define DPMAC_CMDID_GET_IRQ_ENABLE 0x013 -+#define DPMAC_CMDID_SET_IRQ_MASK 0x014 -+#define DPMAC_CMDID_GET_IRQ_MASK 0x015 -+#define DPMAC_CMDID_GET_IRQ_STATUS 0x016 -+#define DPMAC_CMDID_CLEAR_IRQ_STATUS 0x017 -+ -+#define DPMAC_CMDID_MDIO_READ 0x0c0 -+#define DPMAC_CMDID_MDIO_WRITE 0x0c1 -+#define DPMAC_CMDID_GET_LINK_CFG 0x0c2 -+#define DPMAC_CMDID_SET_LINK_STATE 0x0c3 -+#define DPMAC_CMDID_GET_COUNTER 0x0c4 -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_CREATE(cmd, cfg) \ -+ MC_CMD_OP(cmd, 0, 0, 32, int, cfg->mac_id) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_OPEN(cmd, dpmac_id) \ -+ MC_CMD_OP(cmd, 0, 0, 32, int, dpmac_id) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_SET_IRQ(cmd, irq_index, irq_cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, irq_index);\ -+ MC_CMD_OP(cmd, 0, 32, 32, uint32_t, irq_cfg->val);\ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, irq_cfg->addr); \ -+ MC_CMD_OP(cmd, 2, 0, 32, int, irq_cfg->irq_num); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_GET_IRQ(cmd, irq_index) \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_RSP_GET_IRQ(cmd, type, irq_cfg) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, irq_cfg->val); \ -+ MC_RSP_OP(cmd, 1, 0, 64, uint64_t, irq_cfg->addr); \ -+ MC_RSP_OP(cmd, 2, 0, 32, int, irq_cfg->irq_num); \ -+ MC_RSP_OP(cmd, 2, 32, 32, int, type); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_SET_IRQ_ENABLE(cmd, irq_index, en) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, en); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_GET_IRQ_ENABLE(cmd, irq_index) \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_RSP_GET_IRQ_ENABLE(cmd, en) \ -+ MC_RSP_OP(cmd, 0, 0, 8, uint8_t, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_SET_IRQ_MASK(cmd, irq_index, mask) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, mask);\ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_GET_IRQ_MASK(cmd, irq_index) \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_RSP_GET_IRQ_MASK(cmd, mask) \ -+ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, mask) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_GET_IRQ_STATUS(cmd, irq_index, status) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, status);\ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_RSP_GET_IRQ_STATUS(cmd, status) \ -+ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, status) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_CLEAR_IRQ_STATUS(cmd, irq_index, status) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, status); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_RSP_GET_ATTRIBUTES(cmd, attr) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 32, int, attr->phy_id);\ -+ MC_RSP_OP(cmd, 0, 32, 32, int, attr->id);\ -+ MC_RSP_OP(cmd, 1, 0, 16, uint16_t, attr->version.major);\ -+ MC_RSP_OP(cmd, 1, 16, 16, uint16_t, attr->version.minor);\ -+ MC_RSP_OP(cmd, 1, 32, 8, enum dpmac_link_type, attr->link_type);\ -+ MC_RSP_OP(cmd, 1, 40, 8, enum dpmac_eth_if, attr->eth_if);\ -+ MC_RSP_OP(cmd, 2, 0, 32, uint32_t, attr->max_rate);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_MDIO_READ(cmd, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, cfg->phy_addr); \ -+ MC_CMD_OP(cmd, 0, 8, 8, uint8_t, cfg->reg); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_RSP_MDIO_READ(cmd, data) \ -+ MC_RSP_OP(cmd, 0, 16, 16, uint16_t, data) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_MDIO_WRITE(cmd, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, cfg->phy_addr); \ -+ MC_CMD_OP(cmd, 0, 8, 8, uint8_t, cfg->reg); \ -+ MC_CMD_OP(cmd, 0, 16, 16, uint16_t, cfg->data); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_RSP_GET_LINK_CFG(cmd, cfg) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 64, uint64_t, cfg->options); \ -+ MC_RSP_OP(cmd, 1, 0, 32, uint32_t, cfg->rate); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_SET_LINK_STATE(cmd, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 64, uint64_t, cfg->options); \ -+ MC_CMD_OP(cmd, 1, 0, 32, uint32_t, cfg->rate); \ -+ MC_CMD_OP(cmd, 2, 0, 1, int, cfg->up); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_GET_COUNTER(cmd, type) \ -+ MC_CMD_OP(cmd, 0, 0, 8, enum dpmac_counter, type) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_RSP_GET_COUNTER(cmd, counter) \ -+ MC_RSP_OP(cmd, 1, 0, 64, uint64_t, counter) -+ -+#endif /* _FSL_DPMAC_CMD_H */ -diff --git a/drivers/staging/fsl-dpaa2/mac/dpmac.c b/drivers/staging/fsl-dpaa2/mac/dpmac.c -new file mode 100644 -index 0000000..fc23b40 ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/mac/dpmac.c -@@ -0,0 +1,422 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#include "../../fsl-mc/include/mc-sys.h" -+#include "../../fsl-mc/include/mc-cmd.h" -+#include "dpmac.h" -+#include "dpmac-cmd.h" -+ -+int dpmac_open(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ int dpmac_id, -+ uint16_t *token) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_OPEN, -+ cmd_flags, -+ 0); -+ DPMAC_CMD_OPEN(cmd, dpmac_id); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *token = MC_CMD_HDR_READ_TOKEN(cmd.header); -+ -+ return err; -+} -+ -+int dpmac_close(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_CLOSE, cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpmac_create(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ const struct dpmac_cfg *cfg, -+ uint16_t *token) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_CREATE, -+ cmd_flags, -+ 0); -+ DPMAC_CMD_CREATE(cmd, cfg); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *token = MC_CMD_HDR_READ_TOKEN(cmd.header); -+ -+ return 0; -+} -+ -+int dpmac_destroy(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_DESTROY, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpmac_set_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ struct dpmac_irq_cfg *irq_cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_SET_IRQ, -+ cmd_flags, -+ token); -+ DPMAC_CMD_SET_IRQ(cmd, irq_index, irq_cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpmac_get_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ int *type, -+ struct dpmac_irq_cfg *irq_cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_GET_IRQ, -+ cmd_flags, -+ token); -+ DPMAC_CMD_GET_IRQ(cmd, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPMAC_RSP_GET_IRQ(cmd, *type, irq_cfg); -+ -+ return 0; -+} -+ -+int dpmac_set_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t en) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_SET_IRQ_ENABLE, -+ cmd_flags, -+ token); -+ DPMAC_CMD_SET_IRQ_ENABLE(cmd, irq_index, en); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpmac_get_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t *en) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_GET_IRQ_ENABLE, -+ cmd_flags, -+ token); -+ DPMAC_CMD_GET_IRQ_ENABLE(cmd, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPMAC_RSP_GET_IRQ_ENABLE(cmd, *en); -+ -+ return 0; -+} -+ -+int dpmac_set_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t mask) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_SET_IRQ_MASK, -+ cmd_flags, -+ token); -+ DPMAC_CMD_SET_IRQ_MASK(cmd, irq_index, mask); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpmac_get_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *mask) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_GET_IRQ_MASK, -+ cmd_flags, -+ token); -+ DPMAC_CMD_GET_IRQ_MASK(cmd, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPMAC_RSP_GET_IRQ_MASK(cmd, *mask); -+ -+ return 0; -+} -+ -+int dpmac_get_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *status) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_GET_IRQ_STATUS, -+ cmd_flags, -+ token); -+ DPMAC_CMD_GET_IRQ_STATUS(cmd, irq_index, *status); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPMAC_RSP_GET_IRQ_STATUS(cmd, *status); -+ -+ return 0; -+} -+ -+int dpmac_clear_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t status) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_CLEAR_IRQ_STATUS, -+ cmd_flags, -+ token); -+ DPMAC_CMD_CLEAR_IRQ_STATUS(cmd, irq_index, status); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpmac_get_attributes(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpmac_attr *attr) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_GET_ATTR, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPMAC_RSP_GET_ATTRIBUTES(cmd, attr); -+ -+ return 0; -+} -+ -+int dpmac_mdio_read(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpmac_mdio_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_MDIO_READ, -+ cmd_flags, -+ token); -+ DPMAC_CMD_MDIO_READ(cmd, cfg); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPMAC_RSP_MDIO_READ(cmd, cfg->data); -+ -+ return 0; -+} -+ -+int dpmac_mdio_write(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpmac_mdio_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_MDIO_WRITE, -+ cmd_flags, -+ token); -+ DPMAC_CMD_MDIO_WRITE(cmd, cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpmac_get_link_cfg(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpmac_link_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ int err = 0; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_GET_LINK_CFG, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ DPMAC_RSP_GET_LINK_CFG(cmd, cfg); -+ -+ return 0; -+} -+ -+int dpmac_set_link_state(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpmac_link_state *link_state) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_SET_LINK_STATE, -+ cmd_flags, -+ token); -+ DPMAC_CMD_SET_LINK_STATE(cmd, link_state); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpmac_get_counter(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ enum dpmac_counter type, -+ uint64_t *counter) -+{ -+ struct mc_command cmd = { 0 }; -+ int err = 0; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_GET_COUNTER, -+ cmd_flags, -+ token); -+ DPMAC_CMD_GET_COUNTER(cmd, type); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ DPMAC_RSP_GET_COUNTER(cmd, *counter); -+ -+ return 0; -+} -diff --git a/drivers/staging/fsl-dpaa2/mac/dpmac.h b/drivers/staging/fsl-dpaa2/mac/dpmac.h -new file mode 100644 -index 0000000..ad27772 ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/mac/dpmac.h -@@ -0,0 +1,593 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef __FSL_DPMAC_H -+#define __FSL_DPMAC_H -+ -+/* Data Path MAC API -+ * Contains initialization APIs and runtime control APIs for DPMAC -+ */ -+ -+struct fsl_mc_io; -+ -+/** -+ * dpmac_open() - Open a control session for the specified object. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @dpmac_id: DPMAC unique ID -+ * @token: Returned token; use in subsequent API calls -+ * -+ * This function can be used to open a control session for an -+ * already created object; an object may have been declared in -+ * the DPL or by calling the dpmac_create function. -+ * This function returns a unique authentication token, -+ * associated with the specific object ID and the specific MC -+ * portal; this token must be used in all subsequent commands for -+ * this specific object -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_open(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ int dpmac_id, -+ uint16_t *token); -+ -+/** -+ * dpmac_close() - Close the control session of the object -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMAC object -+ * -+ * After this function is called, no further operations are -+ * allowed on the object without opening a new control session. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_close(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * enum dpmac_link_type - DPMAC link type -+ * @DPMAC_LINK_TYPE_NONE: No link -+ * @DPMAC_LINK_TYPE_FIXED: Link is fixed type -+ * @DPMAC_LINK_TYPE_PHY: Link by PHY ID -+ * @DPMAC_LINK_TYPE_BACKPLANE: Backplane link type -+ */ -+enum dpmac_link_type { -+ DPMAC_LINK_TYPE_NONE, -+ DPMAC_LINK_TYPE_FIXED, -+ DPMAC_LINK_TYPE_PHY, -+ DPMAC_LINK_TYPE_BACKPLANE -+}; -+ -+/** -+ * enum dpmac_eth_if - DPMAC Ethrnet interface -+ * @DPMAC_ETH_IF_MII: MII interface -+ * @DPMAC_ETH_IF_RMII: RMII interface -+ * @DPMAC_ETH_IF_SMII: SMII interface -+ * @DPMAC_ETH_IF_GMII: GMII interface -+ * @DPMAC_ETH_IF_RGMII: RGMII interface -+ * @DPMAC_ETH_IF_SGMII: SGMII interface -+ * @DPMAC_ETH_IF_QSGMII: QSGMII interface -+ * @DPMAC_ETH_IF_XAUI: XAUI interface -+ * @DPMAC_ETH_IF_XFI: XFI interface -+ */ -+enum dpmac_eth_if { -+ DPMAC_ETH_IF_MII, -+ DPMAC_ETH_IF_RMII, -+ DPMAC_ETH_IF_SMII, -+ DPMAC_ETH_IF_GMII, -+ DPMAC_ETH_IF_RGMII, -+ DPMAC_ETH_IF_SGMII, -+ DPMAC_ETH_IF_QSGMII, -+ DPMAC_ETH_IF_XAUI, -+ DPMAC_ETH_IF_XFI -+}; -+ -+/** -+ * struct dpmac_cfg - Structure representing DPMAC configuration -+ * @mac_id: Represents the Hardware MAC ID; in case of multiple WRIOP, -+ * the MAC IDs are continuous. -+ * For example: 2 WRIOPs, 16 MACs in each: -+ * MAC IDs for the 1st WRIOP: 1-16, -+ * MAC IDs for the 2nd WRIOP: 17-32. -+ */ -+struct dpmac_cfg { -+ int mac_id; -+}; -+ -+/** -+ * dpmac_create() - Create the DPMAC object. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @cfg: Configuration structure -+ * @token: Returned token; use in subsequent API calls -+ * -+ * Create the DPMAC object, allocate required resources and -+ * perform required initialization. -+ * -+ * The object can be created either by declaring it in the -+ * DPL file, or by calling this function. -+ * This function returns a unique authentication token, -+ * associated with the specific object ID and the specific MC -+ * portal; this token must be used in all subsequent calls to -+ * this specific object. For objects that are created using the -+ * DPL file, call dpmac_open function to get an authentication -+ * token first. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_create(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ const struct dpmac_cfg *cfg, -+ uint16_t *token); -+ -+/** -+ * dpmac_destroy() - Destroy the DPMAC object and release all its resources. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMAC object -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpmac_destroy(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * DPMAC IRQ Index and Events -+ */ -+ -+/** -+ * IRQ index -+ */ -+#define DPMAC_IRQ_INDEX 0 -+/** -+ * IRQ event - indicates a change in link state -+ */ -+#define DPMAC_IRQ_EVENT_LINK_CFG_REQ 0x00000001 -+/** -+ * IRQ event - Indicates that the link state changed -+ */ -+#define DPMAC_IRQ_EVENT_LINK_CHANGED 0x00000002 -+ -+/** -+ * struct dpmac_irq_cfg - IRQ configuration -+ * @addr: Address that must be written to signal a message-based interrupt -+ * @val: Value to write into irq_addr address -+ * @irq_num: A user defined number associated with this IRQ -+ */ -+struct dpmac_irq_cfg { -+ uint64_t addr; -+ uint32_t val; -+ int irq_num; -+}; -+ -+/** -+ * dpmac_set_irq() - Set IRQ information for the DPMAC to trigger an interrupt. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMAC object -+ * @irq_index: Identifies the interrupt index to configure -+ * @irq_cfg: IRQ configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_set_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ struct dpmac_irq_cfg *irq_cfg); -+ -+/** -+ * dpmac_get_irq() - Get IRQ information from the DPMAC. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMAC object -+ * @irq_index: The interrupt index to configure -+ * @type: Interrupt type: 0 represents message interrupt -+ * type (both irq_addr and irq_val are valid) -+ * @irq_cfg: IRQ attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_get_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ int *type, -+ struct dpmac_irq_cfg *irq_cfg); -+ -+/** -+ * dpmac_set_irq_enable() - Set overall interrupt state. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMAC object -+ * @irq_index: The interrupt index to configure -+ * @en: Interrupt state - enable = 1, disable = 0 -+ * -+ * Allows GPP software to control when interrupts are generated. -+ * Each interrupt can have up to 32 causes. The enable/disable control's the -+ * overall interrupt state. if the interrupt is disabled no causes will cause -+ * an interrupt. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_set_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t en); -+ -+/** -+ * dpmac_get_irq_enable() - Get overall interrupt state -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMAC object -+ * @irq_index: The interrupt index to configure -+ * @en: Returned interrupt state - enable = 1, disable = 0 -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_get_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t *en); -+ -+/** -+ * dpmac_set_irq_mask() - Set interrupt mask. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMAC object -+ * @irq_index: The interrupt index to configure -+ * @mask: Event mask to trigger interrupt; -+ * each bit: -+ * 0 = ignore event -+ * 1 = consider event for asserting IRQ -+ * -+ * Every interrupt can have up to 32 causes and the interrupt model supports -+ * masking/unmasking each cause independently -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_set_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t mask); -+ -+/** -+ * dpmac_get_irq_mask() - Get interrupt mask. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMAC object -+ * @irq_index: The interrupt index to configure -+ * @mask: Returned event mask to trigger interrupt -+ * -+ * Every interrupt can have up to 32 causes and the interrupt model supports -+ * masking/unmasking each cause independently -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_get_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *mask); -+ -+/** -+ * dpmac_get_irq_status() - Get the current status of any pending interrupts. -+ * -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMAC object -+ * @irq_index: The interrupt index to configure -+ * @status: Returned interrupts status - one bit per cause: -+ * 0 = no interrupt pending -+ * 1 = interrupt pending -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_get_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *status); -+ -+/** -+ * dpmac_clear_irq_status() - Clear a pending interrupt's status -+ * -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMAC object -+ * @irq_index: The interrupt index to configure -+ * @status: Bits to clear (W1C) - one bit per cause: -+ * 0 = don't change -+ * 1 = clear status bit -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_clear_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t status); -+ -+/** -+ * struct dpmac_attr - Structure representing DPMAC attributes -+ * @id: DPMAC object ID -+ * @phy_id: PHY ID -+ * @link_type: link type -+ * @eth_if: Ethernet interface -+ * @max_rate: Maximum supported rate - in Mbps -+ * @version: DPMAC version -+ */ -+struct dpmac_attr { -+ int id; -+ int phy_id; -+ enum dpmac_link_type link_type; -+ enum dpmac_eth_if eth_if; -+ uint32_t max_rate; -+ /** -+ * struct version - Structure representing DPMAC version -+ * @major: DPMAC major version -+ * @minor: DPMAC minor version -+ */ -+ struct { -+ uint16_t major; -+ uint16_t minor; -+ } version; -+}; -+ -+/** -+ * dpmac_get_attributes - Retrieve DPMAC attributes. -+ * -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMAC object -+ * @attr: Returned object's attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_get_attributes(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpmac_attr *attr); -+ -+/** -+ * struct dpmac_mdio_cfg - DPMAC MDIO read/write parameters -+ * @phy_addr: MDIO device address -+ * @reg: Address of the register within the Clause 45 PHY device from which data -+ * is to be read -+ * @data: Data read/write from/to MDIO -+ */ -+struct dpmac_mdio_cfg { -+ uint8_t phy_addr; -+ uint8_t reg; -+ uint16_t data; -+}; -+ -+/** -+ * dpmac_mdio_read() - Perform MDIO read transaction -+ * @mc_io: Pointer to opaque I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMAC object -+ * @cfg: Structure with MDIO transaction parameters -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_mdio_read(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpmac_mdio_cfg *cfg); -+ -+/** -+ * dpmac_mdio_write() - Perform MDIO write transaction -+ * @mc_io: Pointer to opaque I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMAC object -+ * @cfg: Structure with MDIO transaction parameters -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_mdio_write(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpmac_mdio_cfg *cfg); -+ -+/** -+ * DPMAC link configuration/state options -+ */ -+ -+/** -+ * Enable auto-negotiation -+ */ -+#define DPMAC_LINK_OPT_AUTONEG 0x0000000000000001ULL -+/** -+ * Enable half-duplex mode -+ */ -+#define DPMAC_LINK_OPT_HALF_DUPLEX 0x0000000000000002ULL -+/** -+ * Enable pause frames -+ */ -+#define DPMAC_LINK_OPT_PAUSE 0x0000000000000004ULL -+/** -+ * Enable a-symmetric pause frames -+ */ -+#define DPMAC_LINK_OPT_ASYM_PAUSE 0x0000000000000008ULL -+ -+/** -+ * struct dpmac_link_cfg - Structure representing DPMAC link configuration -+ * @rate: Link's rate - in Mbps -+ * @options: Enable/Disable DPMAC link cfg features (bitmap) -+ */ -+struct dpmac_link_cfg { -+ uint32_t rate; -+ uint64_t options; -+}; -+ -+/** -+ * dpmac_get_link_cfg() - Get Ethernet link configuration -+ * @mc_io: Pointer to opaque I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMAC object -+ * @cfg: Returned structure with the link configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_get_link_cfg(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpmac_link_cfg *cfg); -+ -+/** -+ * struct dpmac_link_state - DPMAC link configuration request -+ * @rate: Rate in Mbps -+ * @options: Enable/Disable DPMAC link cfg features (bitmap) -+ * @up: Link state -+ */ -+struct dpmac_link_state { -+ uint32_t rate; -+ uint64_t options; -+ int up; -+}; -+ -+/** -+ * dpmac_set_link_state() - Set the Ethernet link status -+ * @mc_io: Pointer to opaque I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMAC object -+ * @link_state: Link state configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_set_link_state(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpmac_link_state *link_state); -+ -+/** -+ * enum dpmac_counter - DPMAC counter types -+ * @DPMAC_CNT_ING_FRAME_64: counts 64-bytes frames, good or bad. -+ * @DPMAC_CNT_ING_FRAME_127: counts 65- to 127-bytes frames, good or bad. -+ * @DPMAC_CNT_ING_FRAME_255: counts 128- to 255-bytes frames, good or bad. -+ * @DPMAC_CNT_ING_FRAME_511: counts 256- to 511-bytes frames, good or bad. -+ * @DPMAC_CNT_ING_FRAME_1023: counts 512- to 1023-bytes frames, good or bad. -+ * @DPMAC_CNT_ING_FRAME_1518: counts 1024- to 1518-bytes frames, good or bad. -+ * @DPMAC_CNT_ING_FRAME_1519_MAX: counts 1519-bytes frames and larger -+ * (up to max frame length specified), -+ * good or bad. -+ * @DPMAC_CNT_ING_FRAG: counts frames which are shorter than 64 bytes received -+ * with a wrong CRC -+ * @DPMAC_CNT_ING_JABBER: counts frames longer than the maximum frame length -+ * specified, with a bad frame check sequence. -+ * @DPMAC_CNT_ING_FRAME_DISCARD: counts dropped frames due to internal errors. -+ * Occurs when a receive FIFO overflows. -+ * Includes also frames truncated as a result of -+ * the receive FIFO overflow. -+ * @DPMAC_CNT_ING_ALIGN_ERR: counts frames with an alignment error -+ * (optional used for wrong SFD). -+ * @DPMAC_CNT_EGR_UNDERSIZED: counts frames transmitted that was less than 64 -+ * bytes long with a good CRC. -+ * @DPMAC_CNT_ING_OVERSIZED: counts frames longer than the maximum frame length -+ * specified, with a good frame check sequence. -+ * @DPMAC_CNT_ING_VALID_PAUSE_FRAME: counts valid pause frames (regular and PFC) -+ * @DPMAC_CNT_EGR_VALID_PAUSE_FRAME: counts valid pause frames transmitted -+ * (regular and PFC). -+ * @DPMAC_CNT_ING_BYTE: counts bytes received except preamble for all valid -+ * frames and valid pause frames. -+ * @DPMAC_CNT_ING_MCAST_FRAME: counts received multicast frames. -+ * @DPMAC_CNT_ING_BCAST_FRAME: counts received broadcast frames. -+ * @DPMAC_CNT_ING_ALL_FRAME: counts each good or bad frames received. -+ * @DPMAC_CNT_ING_UCAST_FRAME: counts received unicast frames. -+ * @DPMAC_CNT_ING_ERR_FRAME: counts frames received with an error -+ * (except for undersized/fragment frame). -+ * @DPMAC_CNT_EGR_BYTE: counts bytes transmitted except preamble for all valid -+ * frames and valid pause frames transmitted. -+ * @DPMAC_CNT_EGR_MCAST_FRAME: counts transmitted multicast frames. -+ * @DPMAC_CNT_EGR_BCAST_FRAME: counts transmitted broadcast frames. -+ * @DPMAC_CNT_EGR_UCAST_FRAME: counts transmitted unicast frames. -+ * @DPMAC_CNT_EGR_ERR_FRAME: counts frames transmitted with an error. -+ * @DPMAC_CNT_ING_GOOD_FRAME: counts frames received without error, including -+ * pause frames. -+ * @DPMAC_CNT_ENG_GOOD_FRAME: counts frames transmitted without error, including -+ * pause frames. -+ */ -+enum dpmac_counter { -+ DPMAC_CNT_ING_FRAME_64, -+ DPMAC_CNT_ING_FRAME_127, -+ DPMAC_CNT_ING_FRAME_255, -+ DPMAC_CNT_ING_FRAME_511, -+ DPMAC_CNT_ING_FRAME_1023, -+ DPMAC_CNT_ING_FRAME_1518, -+ DPMAC_CNT_ING_FRAME_1519_MAX, -+ DPMAC_CNT_ING_FRAG, -+ DPMAC_CNT_ING_JABBER, -+ DPMAC_CNT_ING_FRAME_DISCARD, -+ DPMAC_CNT_ING_ALIGN_ERR, -+ DPMAC_CNT_EGR_UNDERSIZED, -+ DPMAC_CNT_ING_OVERSIZED, -+ DPMAC_CNT_ING_VALID_PAUSE_FRAME, -+ DPMAC_CNT_EGR_VALID_PAUSE_FRAME, -+ DPMAC_CNT_ING_BYTE, -+ DPMAC_CNT_ING_MCAST_FRAME, -+ DPMAC_CNT_ING_BCAST_FRAME, -+ DPMAC_CNT_ING_ALL_FRAME, -+ DPMAC_CNT_ING_UCAST_FRAME, -+ DPMAC_CNT_ING_ERR_FRAME, -+ DPMAC_CNT_EGR_BYTE, -+ DPMAC_CNT_EGR_MCAST_FRAME, -+ DPMAC_CNT_EGR_BCAST_FRAME, -+ DPMAC_CNT_EGR_UCAST_FRAME, -+ DPMAC_CNT_EGR_ERR_FRAME, -+ DPMAC_CNT_ING_GOOD_FRAME, -+ DPMAC_CNT_ENG_GOOD_FRAME -+}; -+ -+/** -+ * dpmac_get_counter() - Read a specific DPMAC counter -+ * @mc_io: Pointer to opaque I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMAC object -+ * @type: The requested counter -+ * @counter: Returned counter value -+ * -+ * Return: The requested counter; '0' otherwise. -+ */ -+int dpmac_get_counter(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ enum dpmac_counter type, -+ uint64_t *counter); -+ -+#endif /* __FSL_DPMAC_H */ -diff --git a/drivers/staging/fsl-dpaa2/mac/mac.c b/drivers/staging/fsl-dpaa2/mac/mac.c -new file mode 100644 -index 0000000..fe16b8b ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/mac/mac.c -@@ -0,0 +1,694 @@ -+/* Copyright 2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 -+ -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include "../../fsl-mc/include/mc.h" -+#include "../../fsl-mc/include/mc-sys.h" -+ -+#include "dpmac.h" -+#include "dpmac-cmd.h" -+ -+#define DPAA2_SUPPORTED_DPMAC_VERSION 3 -+ -+struct dpaa2_mac_priv { -+ struct net_device *netdev; -+ struct fsl_mc_device *mc_dev; -+ struct dpmac_attr attr; -+ struct dpmac_link_state old_state; -+}; -+ -+/* TODO: fix the 10G modes, mapping can't be right: -+ * XGMII is paralel -+ * XAUI is serial, using 8b/10b encoding -+ * XFI is also serial but using 64b/66b encoding -+ * they can't all map to XGMII... -+ * -+ * This must be kept in sync with enum dpmac_eth_if. -+ */ -+static phy_interface_t dpaa2_mac_iface_mode[] = { -+ /* DPMAC_ETH_IF_MII */ -+ PHY_INTERFACE_MODE_MII, -+ /* DPMAC_ETH_IF_RMII */ -+ PHY_INTERFACE_MODE_RMII, -+ /* DPMAC_ETH_IF_SMII */ -+ PHY_INTERFACE_MODE_SMII, -+ /* DPMAC_ETH_IF_GMII */ -+ PHY_INTERFACE_MODE_GMII, -+ /* DPMAC_ETH_IF_RGMII */ -+ PHY_INTERFACE_MODE_RGMII, -+ /* DPMAC_ETH_IF_SGMII */ -+ PHY_INTERFACE_MODE_SGMII, -+ /* DPMAC_ETH_IF_QSGMII */ -+ PHY_INTERFACE_MODE_QSGMII, -+ /* DPMAC_ETH_IF_XAUI */ -+ PHY_INTERFACE_MODE_XGMII, -+ /* DPMAC_ETH_IF_XFI */ -+ PHY_INTERFACE_MODE_XGMII, -+}; -+ -+static void dpaa2_mac_link_changed(struct net_device *netdev) -+{ -+ struct phy_device *phydev; -+ struct dpmac_link_state state = { 0 }; -+ struct dpaa2_mac_priv *priv = netdev_priv(netdev); -+ int err; -+ -+ /* the PHY just notified us of link state change */ -+ phydev = netdev->phydev; -+ -+ state.up = !!phydev->link; -+ if (phydev->link) { -+ state.rate = phydev->speed; -+ -+ if (!phydev->duplex) -+ state.options |= DPMAC_LINK_OPT_HALF_DUPLEX; -+ if (phydev->autoneg) -+ state.options |= DPMAC_LINK_OPT_AUTONEG; -+ -+ netif_carrier_on(netdev); -+ } else { -+ netif_carrier_off(netdev); -+ } -+ -+ if (priv->old_state.up != state.up || -+ priv->old_state.rate != state.rate || -+ priv->old_state.options != state.options) { -+ priv->old_state = state; -+ phy_print_status(phydev); -+ } -+ -+ /* We must interrogate MC at all times, because we don't know -+ * when and whether a potential DPNI may have read the link state. -+ */ -+ err = dpmac_set_link_state(priv->mc_dev->mc_io, 0, -+ priv->mc_dev->mc_handle, &state); -+ if (unlikely(err)) -+ dev_err(&priv->mc_dev->dev, "dpmac_set_link_state: %d\n", err); -+} -+ -+/* IRQ bits that we handle */ -+static const u32 dpmac_irq_mask = DPMAC_IRQ_EVENT_LINK_CFG_REQ; -+ -+#ifdef CONFIG_FSL_DPAA2_MAC_NETDEVS -+static netdev_tx_t dpaa2_mac_drop_frame(struct sk_buff *skb, -+ struct net_device *dev) -+{ -+ /* we don't support I/O for now, drop the frame */ -+ dev_kfree_skb_any(skb); -+ return NETDEV_TX_OK; -+} -+ -+static int dpaa2_mac_open(struct net_device *netdev) -+{ -+ /* start PHY state machine */ -+ phy_start(netdev->phydev); -+ -+ return 0; -+} -+ -+static int dpaa2_mac_stop(struct net_device *netdev) -+{ -+ if (!netdev->phydev) -+ goto done; -+ -+ /* stop PHY state machine */ -+ phy_stop(netdev->phydev); -+ -+ /* signal link down to firmware */ -+ netdev->phydev->link = 0; -+ dpaa2_mac_link_changed(netdev); -+ -+done: -+ return 0; -+} -+ -+static int dpaa2_mac_get_settings(struct net_device *netdev, -+ struct ethtool_cmd *cmd) -+{ -+ return phy_ethtool_gset(netdev->phydev, cmd); -+} -+ -+static int dpaa2_mac_set_settings(struct net_device *netdev, -+ struct ethtool_cmd *cmd) -+{ -+ return phy_ethtool_sset(netdev->phydev, cmd); -+} -+ -+static struct rtnl_link_stats64 -+*dpaa2_mac_get_stats(struct net_device *netdev, -+ struct rtnl_link_stats64 *storage) -+{ -+ struct dpaa2_mac_priv *priv = netdev_priv(netdev); -+ u64 tmp; -+ int err; -+ -+ err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, -+ DPMAC_CNT_EGR_MCAST_FRAME, -+ &storage->tx_packets); -+ if (err) -+ goto error; -+ err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, -+ DPMAC_CNT_EGR_BCAST_FRAME, &tmp); -+ if (err) -+ goto error; -+ storage->tx_packets += tmp; -+ err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, -+ DPMAC_CNT_EGR_UCAST_FRAME, &tmp); -+ if (err) -+ goto error; -+ storage->tx_packets += tmp; -+ -+ err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, -+ DPMAC_CNT_EGR_UNDERSIZED, &storage->tx_dropped); -+ if (err) -+ goto error; -+ err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, -+ DPMAC_CNT_EGR_BYTE, &storage->tx_bytes); -+ if (err) -+ goto error; -+ err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, -+ DPMAC_CNT_EGR_ERR_FRAME, &storage->tx_errors); -+ if (err) -+ goto error; -+ -+ err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, -+ DPMAC_CNT_ING_ALL_FRAME, &storage->rx_packets); -+ if (err) -+ goto error; -+ err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, -+ DPMAC_CNT_ING_MCAST_FRAME, &storage->multicast); -+ if (err) -+ goto error; -+ err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, -+ DPMAC_CNT_ING_FRAME_DISCARD, -+ &storage->rx_dropped); -+ if (err) -+ goto error; -+ err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, -+ DPMAC_CNT_ING_ALIGN_ERR, &storage->rx_errors); -+ if (err) -+ goto error; -+ err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, -+ DPMAC_CNT_ING_OVERSIZED, &tmp); -+ if (err) -+ goto error; -+ storage->rx_errors += tmp; -+ err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, -+ DPMAC_CNT_ING_BYTE, &storage->rx_bytes); -+ if (err) -+ goto error; -+ -+ return storage; -+ -+error: -+ netdev_err(netdev, "dpmac_get_counter err %d\n", err); -+ return storage; -+} -+ -+static struct { -+ enum dpmac_counter id; -+ char name[ETH_GSTRING_LEN]; -+} dpaa2_mac_counters[] = { -+ {DPMAC_CNT_ING_ALL_FRAME, "rx all frames"}, -+ {DPMAC_CNT_ING_GOOD_FRAME, "rx frames ok"}, -+ {DPMAC_CNT_ING_ERR_FRAME, "rx frame errors"}, -+ {DPMAC_CNT_ING_FRAME_DISCARD, "rx frame discards"}, -+ {DPMAC_CNT_ING_UCAST_FRAME, "rx u-cast"}, -+ {DPMAC_CNT_ING_BCAST_FRAME, "rx b-cast"}, -+ {DPMAC_CNT_ING_MCAST_FRAME, "rx m-cast"}, -+ {DPMAC_CNT_ING_FRAME_64, "rx 64 bytes"}, -+ {DPMAC_CNT_ING_FRAME_127, "rx 65-127 bytes"}, -+ {DPMAC_CNT_ING_FRAME_255, "rx 128-255 bytes"}, -+ {DPMAC_CNT_ING_FRAME_511, "rx 256-511 bytes"}, -+ {DPMAC_CNT_ING_FRAME_1023, "rx 512-1023 bytes"}, -+ {DPMAC_CNT_ING_FRAME_1518, "rx 1024-1518 bytes"}, -+ {DPMAC_CNT_ING_FRAME_1519_MAX, "rx 1519-max bytes"}, -+ {DPMAC_CNT_ING_FRAG, "rx frags"}, -+ {DPMAC_CNT_ING_JABBER, "rx jabber"}, -+ {DPMAC_CNT_ING_ALIGN_ERR, "rx align errors"}, -+ {DPMAC_CNT_ING_OVERSIZED, "rx oversized"}, -+ {DPMAC_CNT_ING_VALID_PAUSE_FRAME, "rx pause"}, -+ {DPMAC_CNT_ING_BYTE, "rx bytes"}, -+ {DPMAC_CNT_ENG_GOOD_FRAME, "tx frames ok"}, -+ {DPMAC_CNT_EGR_UCAST_FRAME, "tx u-cast"}, -+ {DPMAC_CNT_EGR_MCAST_FRAME, "tx m-cast"}, -+ {DPMAC_CNT_EGR_BCAST_FRAME, "tx b-cast"}, -+ {DPMAC_CNT_EGR_ERR_FRAME, "tx frame errors"}, -+ {DPMAC_CNT_EGR_UNDERSIZED, "tx undersized"}, -+ {DPMAC_CNT_EGR_VALID_PAUSE_FRAME, "tx b-pause"}, -+ {DPMAC_CNT_EGR_BYTE, "tx bytes"}, -+ -+}; -+ -+static void dpaa2_mac_get_strings(struct net_device *netdev, -+ u32 stringset, u8 *data) -+{ -+ int i; -+ -+ switch (stringset) { -+ case ETH_SS_STATS: -+ for (i = 0; i < ARRAY_SIZE(dpaa2_mac_counters); i++) -+ memcpy(data + i * ETH_GSTRING_LEN, -+ dpaa2_mac_counters[i].name, -+ ETH_GSTRING_LEN); -+ break; -+ } -+} -+ -+static void dpaa2_mac_get_ethtool_stats(struct net_device *netdev, -+ struct ethtool_stats *stats, -+ u64 *data) -+{ -+ struct dpaa2_mac_priv *priv = netdev_priv(netdev); -+ int i; -+ int err; -+ -+ for (i = 0; i < ARRAY_SIZE(dpaa2_mac_counters); i++) { -+ err = dpmac_get_counter(priv->mc_dev->mc_io, -+ 0, -+ priv->mc_dev->mc_handle, -+ dpaa2_mac_counters[i].id, &data[i]); -+ if (err) -+ netdev_err(netdev, "dpmac_get_counter[%s] err %d\n", -+ dpaa2_mac_counters[i].name, err); -+ } -+} -+ -+static int dpaa2_mac_get_sset_count(struct net_device *dev, int sset) -+{ -+ switch (sset) { -+ case ETH_SS_STATS: -+ return ARRAY_SIZE(dpaa2_mac_counters); -+ default: -+ return -EOPNOTSUPP; -+ } -+} -+ -+static const struct net_device_ops dpaa2_mac_ndo_ops = { -+ .ndo_start_xmit = &dpaa2_mac_drop_frame, -+ .ndo_open = &dpaa2_mac_open, -+ .ndo_stop = &dpaa2_mac_stop, -+ .ndo_get_stats64 = &dpaa2_mac_get_stats, -+}; -+ -+static const struct ethtool_ops dpaa2_mac_ethtool_ops = { -+ .get_settings = &dpaa2_mac_get_settings, -+ .set_settings = &dpaa2_mac_set_settings, -+ .get_strings = &dpaa2_mac_get_strings, -+ .get_ethtool_stats = &dpaa2_mac_get_ethtool_stats, -+ .get_sset_count = &dpaa2_mac_get_sset_count, -+}; -+#endif /* CONFIG_FSL_DPAA2_MAC_NETDEVS */ -+ -+static void configure_link(struct dpaa2_mac_priv *priv, -+ struct dpmac_link_cfg *cfg) -+{ -+ struct phy_device *phydev = priv->netdev->phydev; -+ -+ if (unlikely(!phydev)) -+ return; -+ -+ phydev->speed = cfg->rate; -+ phydev->duplex = !!(cfg->options & DPMAC_LINK_OPT_HALF_DUPLEX); -+ -+ if (cfg->options & DPMAC_LINK_OPT_AUTONEG) { -+ phydev->autoneg = 1; -+ phydev->advertising |= ADVERTISED_Autoneg; -+ } else { -+ phydev->autoneg = 0; -+ phydev->advertising &= ~ADVERTISED_Autoneg; -+ } -+ -+ phy_start_aneg(phydev); -+} -+ -+static irqreturn_t dpaa2_mac_irq_handler(int irq_num, void *arg) -+{ -+ struct device *dev = (struct device *)arg; -+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); -+ struct dpaa2_mac_priv *priv = dev_get_drvdata(dev); -+ struct dpmac_link_cfg link_cfg; -+ u32 status; -+ int err; -+ -+ err = dpmac_get_irq_status(mc_dev->mc_io, 0, mc_dev->mc_handle, -+ DPMAC_IRQ_INDEX, &status); -+ if (unlikely(err || !status)) -+ return IRQ_NONE; -+ -+ /* DPNI-initiated link configuration; 'ifconfig up' also calls this */ -+ if (status & DPMAC_IRQ_EVENT_LINK_CFG_REQ) { -+ err = dpmac_get_link_cfg(mc_dev->mc_io, 0, mc_dev->mc_handle, -+ &link_cfg); -+ if (unlikely(err)) -+ goto out; -+ -+ configure_link(priv, &link_cfg); -+ } -+ -+out: -+ dpmac_clear_irq_status(mc_dev->mc_io, 0, mc_dev->mc_handle, -+ DPMAC_IRQ_INDEX, status); -+ -+ return IRQ_HANDLED; -+} -+ -+static int setup_irqs(struct fsl_mc_device *mc_dev) -+{ -+ int err; -+ -+ err = fsl_mc_allocate_irqs(mc_dev); -+ if (err) { -+ dev_err(&mc_dev->dev, "fsl_mc_allocate_irqs err %d\n", err); -+ return err; -+ } -+ -+ err = devm_request_threaded_irq(&mc_dev->dev, -+ mc_dev->irqs[0]->irq_number, -+ NULL, &dpaa2_mac_irq_handler, -+ IRQF_NO_SUSPEND | IRQF_ONESHOT, -+ dev_name(&mc_dev->dev), &mc_dev->dev); -+ if (err) { -+ dev_err(&mc_dev->dev, "devm_request_threaded_irq err %d\n", -+ err); -+ goto free_irq; -+ } -+ -+ err = dpmac_set_irq_mask(mc_dev->mc_io, 0, mc_dev->mc_handle, -+ DPMAC_IRQ_INDEX, dpmac_irq_mask); -+ if (err) { -+ dev_err(&mc_dev->dev, "dpmac_set_irq_mask err %d\n", err); -+ goto free_irq; -+ } -+ err = dpmac_set_irq_enable(mc_dev->mc_io, 0, mc_dev->mc_handle, -+ DPMAC_IRQ_INDEX, 1); -+ if (err) { -+ dev_err(&mc_dev->dev, "dpmac_set_irq_enable err %d\n", err); -+ goto free_irq; -+ } -+ -+ return 0; -+ -+free_irq: -+ fsl_mc_free_irqs(mc_dev); -+ -+ return err; -+} -+ -+static void teardown_irqs(struct fsl_mc_device *mc_dev) -+{ -+ int err; -+ -+ err = dpmac_set_irq_mask(mc_dev->mc_io, 0, mc_dev->mc_handle, -+ DPMAC_IRQ_INDEX, dpmac_irq_mask); -+ if (err) -+ dev_err(&mc_dev->dev, "dpmac_set_irq_mask err %d\n", err); -+ -+ err = dpmac_set_irq_enable(mc_dev->mc_io, 0, mc_dev->mc_handle, -+ DPMAC_IRQ_INDEX, 0); -+ if (err) -+ dev_err(&mc_dev->dev, "dpmac_set_irq_enable err %d\n", err); -+ -+ fsl_mc_free_irqs(mc_dev); -+} -+ -+static struct device_node *lookup_node(struct device *dev, int dpmac_id) -+{ -+ struct device_node *dpmacs, *dpmac = NULL; -+ struct device_node *mc_node = dev->of_node; -+ u32 id; -+ int err; -+ -+ dpmacs = of_find_node_by_name(mc_node, "dpmacs"); -+ if (!dpmacs) { -+ dev_err(dev, "No dpmacs subnode in device-tree\n"); -+ return NULL; -+ } -+ -+ while ((dpmac = of_get_next_child(dpmacs, dpmac))) { -+ err = of_property_read_u32(dpmac, "reg", &id); -+ if (err) -+ continue; -+ if (id == dpmac_id) -+ return dpmac; -+ } -+ -+ return NULL; -+} -+ -+static int dpaa2_mac_probe(struct fsl_mc_device *mc_dev) -+{ -+ struct device *dev; -+ struct dpaa2_mac_priv *priv = NULL; -+ struct device_node *phy_node, *dpmac_node; -+ struct net_device *netdev; -+ phy_interface_t if_mode; -+ int err = 0; -+ -+ dev = &mc_dev->dev; -+ -+ /* prepare a net_dev structure to make the phy lib API happy */ -+ netdev = alloc_etherdev(sizeof(*priv)); -+ if (!netdev) { -+ dev_err(dev, "alloc_etherdev error\n"); -+ err = -ENOMEM; -+ goto err_exit; -+ } -+ priv = netdev_priv(netdev); -+ priv->mc_dev = mc_dev; -+ priv->netdev = netdev; -+ -+ SET_NETDEV_DEV(netdev, dev); -+ -+#ifdef CONFIG_FSL_DPAA2_MAC_NETDEVS -+ snprintf(netdev->name, IFNAMSIZ, "mac%d", mc_dev->obj_desc.id); -+#endif -+ -+ dev_set_drvdata(dev, priv); -+ -+ err = fsl_mc_portal_allocate(mc_dev, 0, &mc_dev->mc_io); -+ if (err || !mc_dev->mc_io) { -+ dev_err(dev, "fsl_mc_portal_allocate error: %d\n", err); -+ err = -ENODEV; -+ goto err_free_netdev; -+ } -+ -+ err = dpmac_open(mc_dev->mc_io, 0, mc_dev->obj_desc.id, -+ &mc_dev->mc_handle); -+ if (err || !mc_dev->mc_handle) { -+ dev_err(dev, "dpmac_open error: %d\n", err); -+ err = -ENODEV; -+ goto err_free_mcp; -+ } -+ -+ err = dpmac_get_attributes(mc_dev->mc_io, 0, -+ mc_dev->mc_handle, &priv->attr); -+ if (err) { -+ dev_err(dev, "dpmac_get_attributes err %d\n", err); -+ err = -EINVAL; -+ goto err_close; -+ } -+ -+ dev_warn(dev, "Using DPMAC API %d.%d\n", -+ priv->attr.version.major, priv->attr.version.minor); -+ -+ /* Look up the DPMAC node in the device-tree. */ -+ dpmac_node = lookup_node(dev, priv->attr.id); -+ if (!dpmac_node) { -+ dev_err(dev, "No dpmac@%d subnode found.\n", priv->attr.id); -+ err = -ENODEV; -+ goto err_close; -+ } -+ -+ err = setup_irqs(mc_dev); -+ if (err) { -+ err = -EFAULT; -+ goto err_close; -+ } -+ -+#ifdef CONFIG_FSL_DPAA2_MAC_NETDEVS -+ /* OPTIONAL, register netdev just to make it visible to the user */ -+ netdev->netdev_ops = &dpaa2_mac_ndo_ops; -+ netdev->ethtool_ops = &dpaa2_mac_ethtool_ops; -+ -+ /* phy starts up enabled so netdev should be up too */ -+ netdev->flags |= IFF_UP; -+ -+ err = register_netdev(priv->netdev); -+ if (err < 0) { -+ dev_err(dev, "register_netdev error %d\n", err); -+ err = -ENODEV; -+ goto err_free_irq; -+ } -+#endif /* CONFIG_FSL_DPAA2_MAC_NETDEVS */ -+ -+ /* probe the PHY as a fixed-link if the link type declared in DPC -+ * explicitly mandates this -+ */ -+ if (priv->attr.link_type == DPMAC_LINK_TYPE_FIXED) -+ goto probe_fixed_link; -+ -+ if (priv->attr.eth_if < ARRAY_SIZE(dpaa2_mac_iface_mode)) { -+ if_mode = dpaa2_mac_iface_mode[priv->attr.eth_if]; -+ dev_dbg(dev, "\tusing if mode %s for eth_if %d\n", -+ phy_modes(if_mode), priv->attr.eth_if); -+ } else { -+ dev_warn(dev, "Unexpected interface mode %d, will probe as fixed link\n", -+ priv->attr.eth_if); -+ goto probe_fixed_link; -+ } -+ -+ /* try to connect to the PHY */ -+ phy_node = of_parse_phandle(dpmac_node, "phy-handle", 0); -+ if (!phy_node) { -+ if (!phy_node) { -+ dev_err(dev, "dpmac node has no phy-handle property\n"); -+ err = -ENODEV; -+ goto err_no_phy; -+ } -+ } -+ netdev->phydev = of_phy_connect(netdev, phy_node, -+ &dpaa2_mac_link_changed, 0, if_mode); -+ if (!netdev->phydev) { -+ /* No need for dev_err(); the kernel's loud enough as it is. */ -+ dev_dbg(dev, "Can't of_phy_connect() now.\n"); -+ /* We might be waiting for the MDIO MUX to probe, so defer -+ * our own probing. -+ */ -+ err = -EPROBE_DEFER; -+ goto err_defer; -+ } -+ dev_info(dev, "Connected to %s PHY.\n", phy_modes(if_mode)); -+ -+probe_fixed_link: -+ if (!netdev->phydev) { -+ struct fixed_phy_status status = { -+ .link = 1, -+ /* fixed-phys don't support 10Gbps speed for now */ -+ .speed = 1000, -+ .duplex = 1, -+ }; -+ -+ /* try to register a fixed link phy */ -+ netdev->phydev = fixed_phy_register(PHY_POLL, &status, NULL); -+ if (!netdev->phydev || IS_ERR(netdev->phydev)) { -+ dev_err(dev, "error trying to register fixed PHY\n"); -+ /* So we don't crash unregister_netdev() later on */ -+ netdev->phydev = NULL; -+ err = -EFAULT; -+ goto err_no_phy; -+ } -+ dev_info(dev, "Registered fixed PHY.\n"); -+ } -+ -+ /* start PHY state machine */ -+#ifdef CONFIG_FSL_DPAA2_MAC_NETDEVS -+ dpaa2_mac_open(netdev); -+#else /* CONFIG_FSL_DPAA2_MAC_NETDEVS */ -+ phy_start(netdev->phydev); -+#endif /* CONFIG_FSL_DPAA2_MAC_NETDEVS */ -+ return 0; -+ -+err_defer: -+err_no_phy: -+#ifdef CONFIG_FSL_DPAA2_MAC_NETDEVS -+ unregister_netdev(netdev); -+err_free_irq: -+#endif -+ teardown_irqs(mc_dev); -+err_close: -+ dpmac_close(mc_dev->mc_io, 0, mc_dev->mc_handle); -+err_free_mcp: -+ fsl_mc_portal_free(mc_dev->mc_io); -+err_free_netdev: -+ free_netdev(netdev); -+err_exit: -+ return err; -+} -+ -+static int dpaa2_mac_remove(struct fsl_mc_device *mc_dev) -+{ -+ struct device *dev = &mc_dev->dev; -+ struct dpaa2_mac_priv *priv = dev_get_drvdata(dev); -+ -+#ifdef CONFIG_FSL_DPAA2_MAC_NETDEVS -+ unregister_netdev(priv->netdev); -+#endif -+ teardown_irqs(priv->mc_dev); -+ dpmac_close(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle); -+ fsl_mc_portal_free(priv->mc_dev->mc_io); -+ free_netdev(priv->netdev); -+ -+ dev_set_drvdata(dev, NULL); -+ kfree(priv); -+ -+ return 0; -+} -+ -+static const struct fsl_mc_device_match_id dpaa2_mac_match_id_table[] = { -+ { -+ .vendor = FSL_MC_VENDOR_FREESCALE, -+ .obj_type = "dpmac", -+ .ver_major = DPMAC_VER_MAJOR, -+ .ver_minor = DPMAC_VER_MINOR, -+ }, -+ {} -+}; -+ -+static struct fsl_mc_driver dpaa2_mac_drv = { -+ .driver = { -+ .name = KBUILD_MODNAME, -+ .owner = THIS_MODULE, -+ }, -+ .probe = dpaa2_mac_probe, -+ .remove = dpaa2_mac_remove, -+ .match_id_table = dpaa2_mac_match_id_table, -+}; -+ -+module_fsl_mc_driver(dpaa2_mac_drv); -+ -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("DPAA2 PHY proxy interface driver"); -diff --git a/drivers/staging/fsl-mc/Kconfig b/drivers/staging/fsl-mc/Kconfig -new file mode 100644 -index 0000000..32df07b ---- /dev/null -+++ b/drivers/staging/fsl-mc/Kconfig -@@ -0,0 +1 @@ -+source "drivers/staging/fsl-mc/bus/Kconfig" -diff --git a/drivers/staging/fsl-mc/Makefile b/drivers/staging/fsl-mc/Makefile -new file mode 100644 -index 0000000..9c6a001 ---- /dev/null -+++ b/drivers/staging/fsl-mc/Makefile -@@ -0,0 +1,2 @@ -+# Freescale Management Complex (MC) bus drivers -+obj-$(CONFIG_FSL_MC_BUS) += bus/ -diff --git a/drivers/staging/fsl-mc/TODO b/drivers/staging/fsl-mc/TODO -new file mode 100644 -index 0000000..d78288b ---- /dev/null -+++ b/drivers/staging/fsl-mc/TODO -@@ -0,0 +1,13 @@ -+* Add README file (with ASCII art) describing relationships between -+ DPAA2 objects and how combine them to make a NIC, an LS2 switch, etc. -+ Also, define all acronyms used. -+ -+* Decide if multiple root fsl-mc buses will be supported per Linux instance, -+ and if so add support for this. -+ -+* Add at least one device driver for a DPAA2 object (child device of the -+ fsl-mc bus). -+ -+Please send any patches to Greg Kroah-Hartman , -+german.rivera@freescale.com, devel@driverdev.osuosl.org, -+linux-kernel@vger.kernel.org -diff --git a/drivers/staging/fsl-mc/bus/Kconfig b/drivers/staging/fsl-mc/bus/Kconfig -new file mode 100644 -index 0000000..8bef5b8 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/Kconfig -@@ -0,0 +1,45 @@ -+# -+# Freescale Management Complex (MC) bus drivers -+# -+# Copyright (C) 2014 Freescale Semiconductor, Inc. -+# -+# This file is released under the GPLv2 -+# -+ -+config FSL_MC_BUS -+ tristate "Freescale Management Complex (MC) bus driver" -+ depends on OF && ARM64 -+ help -+ Driver to enable the bus infrastructure for the Freescale -+ QorIQ Management Complex (fsl-mc). The fsl-mc is a hardware -+ module of the QorIQ LS2 SoCs, that does resource management -+ for hardware building-blocks in the SoC that can be used -+ to dynamically create networking hardware objects such as -+ network interfaces (NICs), crypto accelerator instances, -+ or L2 switches. -+ -+ Only enable this option when building the kernel for -+ Freescale QorQIQ LS2xxxx SoCs. -+ -+config FSL_MC_RESTOOL -+ tristate "Freescale Management Complex (MC) restool driver" -+ depends on FSL_MC_BUS -+ help -+ Driver that provides kernel support for the Freescale Management -+ Complex resource manager user-space tool. -+ -+config FSL_MC_DPIO -+ tristate "Freescale Data Path I/O (DPIO) driver" -+ depends on FSL_MC_BUS -+ help -+ Driver for Freescale Data Path I/O (DPIO) devices. -+ A DPIO device provides queue and buffer management facilities -+ for software to interact with other Data Path devices. This -+ driver does not expose the DPIO device individually, but -+ groups them under a service layer API. -+ -+config FSL_QBMAN_DEBUG -+ tristate "Freescale QBMAN Debug APIs" -+ depends on FSL_MC_DPIO -+ help -+ QBMan debug assistant APIs. -diff --git a/drivers/staging/fsl-mc/bus/Makefile b/drivers/staging/fsl-mc/bus/Makefile -new file mode 100644 -index 0000000..f29399c ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/Makefile -@@ -0,0 +1,24 @@ -+# -+# Freescale Management Complex (MC) bus drivers -+# -+# Copyright (C) 2014 Freescale Semiconductor, Inc. -+# -+# This file is released under the GPLv2 -+# -+obj-$(CONFIG_FSL_MC_BUS) += mc-bus-driver.o -+ -+mc-bus-driver-objs := mc-bus.o \ -+ mc-sys.o \ -+ dprc.o \ -+ dpmng.o \ -+ dprc-driver.o \ -+ mc-allocator.o \ -+ dpmcp.o \ -+ dpbp.o \ -+ dpcon.o -+ -+# MC restool kernel support -+obj-$(CONFIG_FSL_MC_RESTOOL) += mc-restool.o -+ -+# MC DPIO driver -+obj-$(CONFIG_FSL_MC_DPIO) += dpio/ -diff --git a/drivers/staging/fsl-mc/bus/dpbp.c b/drivers/staging/fsl-mc/bus/dpbp.c -new file mode 100644 -index 0000000..f183121 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpbp.c -@@ -0,0 +1,459 @@ -+/* Copyright 2013-2014 Freescale Semiconductor Inc. -+* -+* Redistribution and use in source and binary forms, with or without -+* modification, are permitted provided that the following conditions are met: -+* * Redistributions of source code must retain the above copyright -+* notice, this list of conditions and the following disclaimer. -+* * 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. -+* * Neither the name of the above-listed copyright holders nor the -+* names of any contributors may be used to endorse or promote products -+* derived from this software without specific prior written permission. -+* -+* -+* ALTERNATIVELY, this software may be distributed under the terms of the -+* GNU General Public License ("GPL") as published by the Free Software -+* Foundation, either version 2 of that License or (at your option) any -+* later version. -+* -+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+*/ -+#include "../include/mc-sys.h" -+#include "../include/mc-cmd.h" -+#include "../include/dpbp.h" -+#include "../include/dpbp-cmd.h" -+ -+int dpbp_open(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ int dpbp_id, -+ uint16_t *token) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_OPEN, -+ cmd_flags, -+ 0); -+ -+ cmd.params[0] |= mc_enc(0, 32, dpbp_id); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *token = MC_CMD_HDR_READ_TOKEN(cmd.header); -+ -+ return err; -+} -+EXPORT_SYMBOL(dpbp_open); -+ -+int dpbp_close(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_CLOSE, cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dpbp_close); -+ -+int dpbp_create(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ const struct dpbp_cfg *cfg, -+ uint16_t *token) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ (void)(cfg); /* unused */ -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_CREATE, -+ cmd_flags, -+ 0); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *token = MC_CMD_HDR_READ_TOKEN(cmd.header); -+ -+ return 0; -+} -+ -+int dpbp_destroy(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_DESTROY, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpbp_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_ENABLE, cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dpbp_enable); -+ -+int dpbp_disable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_DISABLE, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dpbp_disable); -+ -+int dpbp_is_enabled(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *en) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_IS_ENABLED, cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *en = (int)mc_dec(cmd.params[0], 0, 1); -+ -+ return 0; -+} -+ -+int dpbp_reset(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_RESET, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpbp_set_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ struct dpbp_irq_cfg *irq_cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_SET_IRQ, -+ cmd_flags, -+ token); -+ -+ cmd.params[0] |= mc_enc(0, 8, irq_index); -+ cmd.params[0] |= mc_enc(32, 32, irq_cfg->val); -+ cmd.params[1] |= mc_enc(0, 64, irq_cfg->addr); -+ cmd.params[2] |= mc_enc(0, 32, irq_cfg->irq_num); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpbp_get_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ int *type, -+ struct dpbp_irq_cfg *irq_cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_GET_IRQ, -+ cmd_flags, -+ token); -+ -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ irq_cfg->val = (uint32_t)mc_dec(cmd.params[0], 0, 32); -+ irq_cfg->addr = (uint64_t)mc_dec(cmd.params[1], 0, 64); -+ irq_cfg->irq_num = (int)mc_dec(cmd.params[2], 0, 32); -+ *type = (int)mc_dec(cmd.params[2], 32, 32); -+ -+ return 0; -+} -+ -+int dpbp_set_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t en) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_SET_IRQ_ENABLE, -+ cmd_flags, -+ token); -+ -+ cmd.params[0] |= mc_enc(0, 8, en); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpbp_get_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t *en) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_GET_IRQ_ENABLE, -+ cmd_flags, -+ token); -+ -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *en = (uint8_t)mc_dec(cmd.params[0], 0, 8); -+ return 0; -+} -+ -+int dpbp_set_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t mask) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_SET_IRQ_MASK, -+ cmd_flags, -+ token); -+ -+ cmd.params[0] |= mc_enc(0, 32, mask); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpbp_get_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *mask) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_GET_IRQ_MASK, -+ cmd_flags, -+ token); -+ -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *mask = (uint32_t)mc_dec(cmd.params[0], 0, 32); -+ return 0; -+} -+ -+int dpbp_get_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *status) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_GET_IRQ_STATUS, -+ cmd_flags, -+ token); -+ -+ cmd.params[0] |= mc_enc(0, 32, *status); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *status = (uint32_t)mc_dec(cmd.params[0], 0, 32); -+ return 0; -+} -+ -+int dpbp_clear_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t status) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_CLEAR_IRQ_STATUS, -+ cmd_flags, -+ token); -+ -+ cmd.params[0] |= mc_enc(0, 32, status); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpbp_get_attributes(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpbp_attr *attr) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_GET_ATTR, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ attr->bpid = (uint16_t)mc_dec(cmd.params[0], 16, 16); -+ attr->id = (int)mc_dec(cmd.params[0], 32, 32); -+ attr->version.major = (uint16_t)mc_dec(cmd.params[1], 0, 16); -+ attr->version.minor = (uint16_t)mc_dec(cmd.params[1], 16, 16); -+ return 0; -+} -+EXPORT_SYMBOL(dpbp_get_attributes); -+ -+int dpbp_set_notifications(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpbp_notification_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_SET_NOTIFICATIONS, -+ cmd_flags, -+ token); -+ -+ cmd.params[0] |= mc_enc(0, 32, cfg->depletion_entry); -+ cmd.params[0] |= mc_enc(32, 32, cfg->depletion_exit); -+ cmd.params[1] |= mc_enc(0, 32, cfg->surplus_entry); -+ cmd.params[1] |= mc_enc(32, 32, cfg->surplus_exit); -+ cmd.params[2] |= mc_enc(0, 16, cfg->options); -+ cmd.params[3] |= mc_enc(0, 64, cfg->message_ctx); -+ cmd.params[4] |= mc_enc(0, 64, cfg->message_iova); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpbp_get_notifications(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpbp_notification_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_GET_NOTIFICATIONS, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ cfg->depletion_entry = (uint32_t)mc_dec(cmd.params[0], 0, 32); -+ cfg->depletion_exit = (uint32_t)mc_dec(cmd.params[0], 32, 32); -+ cfg->surplus_entry = (uint32_t)mc_dec(cmd.params[1], 0, 32); -+ cfg->surplus_exit = (uint32_t)mc_dec(cmd.params[1], 32, 32); -+ cfg->options = (uint16_t)mc_dec(cmd.params[2], 0, 16); -+ cfg->message_ctx = (uint64_t)mc_dec(cmd.params[3], 0, 64); -+ cfg->message_iova = (uint64_t)mc_dec(cmd.params[4], 0, 64); -+ -+ return 0; -+} -diff --git a/drivers/staging/fsl-mc/bus/dpcon.c b/drivers/staging/fsl-mc/bus/dpcon.c -new file mode 100644 -index 0000000..7965284 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpcon.c -@@ -0,0 +1,407 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#include "../include/mc-sys.h" -+#include "../include/mc-cmd.h" -+#include "../include/dpcon.h" -+#include "../include/dpcon-cmd.h" -+ -+int dpcon_open(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ int dpcon_id, -+ uint16_t *token) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_OPEN, -+ cmd_flags, -+ 0); -+ DPCON_CMD_OPEN(cmd, dpcon_id); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *token = MC_CMD_HDR_READ_TOKEN(cmd.header); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dpcon_open); -+ -+int dpcon_close(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_CLOSE, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dpcon_close); -+ -+int dpcon_create(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ const struct dpcon_cfg *cfg, -+ uint16_t *token) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_CREATE, -+ cmd_flags, -+ 0); -+ DPCON_CMD_CREATE(cmd, cfg); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *token = MC_CMD_HDR_READ_TOKEN(cmd.header); -+ -+ return 0; -+} -+ -+int dpcon_destroy(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_DESTROY, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpcon_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_ENABLE, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dpcon_enable); -+ -+int dpcon_disable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_DISABLE, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dpcon_disable); -+ -+int dpcon_is_enabled(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *en) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_IS_ENABLED, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPCON_RSP_IS_ENABLED(cmd, *en); -+ -+ return 0; -+} -+ -+int dpcon_reset(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_RESET, -+ cmd_flags, token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpcon_set_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ struct dpcon_irq_cfg *irq_cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_SET_IRQ, -+ cmd_flags, -+ token); -+ DPCON_CMD_SET_IRQ(cmd, irq_index, irq_cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpcon_get_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ int *type, -+ struct dpcon_irq_cfg *irq_cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_GET_IRQ, -+ cmd_flags, -+ token); -+ DPCON_CMD_GET_IRQ(cmd, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPCON_RSP_GET_IRQ(cmd, *type, irq_cfg); -+ -+ return 0; -+} -+ -+int dpcon_set_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t en) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_SET_IRQ_ENABLE, -+ cmd_flags, -+ token); -+ DPCON_CMD_SET_IRQ_ENABLE(cmd, irq_index, en); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpcon_get_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t *en) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_GET_IRQ_ENABLE, -+ cmd_flags, -+ token); -+ DPCON_CMD_GET_IRQ_ENABLE(cmd, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPCON_RSP_GET_IRQ_ENABLE(cmd, *en); -+ -+ return 0; -+} -+ -+int dpcon_set_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t mask) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_SET_IRQ_MASK, -+ cmd_flags, -+ token); -+ DPCON_CMD_SET_IRQ_MASK(cmd, irq_index, mask); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpcon_get_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *mask) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_GET_IRQ_MASK, -+ cmd_flags, -+ token); -+ DPCON_CMD_GET_IRQ_MASK(cmd, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPCON_RSP_GET_IRQ_MASK(cmd, *mask); -+ -+ return 0; -+} -+ -+int dpcon_get_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *status) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_GET_IRQ_STATUS, -+ cmd_flags, -+ token); -+ DPCON_CMD_GET_IRQ_STATUS(cmd, irq_index, *status); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPCON_RSP_GET_IRQ_STATUS(cmd, *status); -+ -+ return 0; -+} -+ -+int dpcon_clear_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t status) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_CLEAR_IRQ_STATUS, -+ cmd_flags, -+ token); -+ DPCON_CMD_CLEAR_IRQ_STATUS(cmd, irq_index, status); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpcon_get_attributes(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpcon_attr *attr) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_GET_ATTR, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPCON_RSP_GET_ATTR(cmd, attr); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dpcon_get_attributes); -+ -+int dpcon_set_notification(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpcon_notification_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_SET_NOTIFICATION, -+ cmd_flags, -+ token); -+ DPCON_CMD_SET_NOTIFICATION(cmd, cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dpcon_set_notification); -+ -diff --git a/drivers/staging/fsl-mc/bus/dpio/Makefile b/drivers/staging/fsl-mc/bus/dpio/Makefile -new file mode 100644 -index 0000000..c20356b ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpio/Makefile -@@ -0,0 +1,9 @@ -+# -+# Freescale DPIO driver -+# -+ -+obj-$(CONFIG_FSL_MC_BUS) += fsl-dpio-drv.o -+ -+fsl-dpio-drv-objs := dpio-drv.o dpio_service.o dpio.o qbman_portal.o -+ -+obj-$(CONFIG_FSL_QBMAN_DEBUG) += qbman_debug.o -diff --git a/drivers/staging/fsl-mc/bus/dpio/dpio-drv.c b/drivers/staging/fsl-mc/bus/dpio/dpio-drv.c -new file mode 100644 -index 0000000..80add27 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpio/dpio-drv.c -@@ -0,0 +1,401 @@ -+/* Copyright 2014 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "../../include/mc.h" -+#include "../../include/fsl_dpaa2_io.h" -+ -+#include "fsl_qbman_portal.h" -+#include "fsl_dpio.h" -+#include "fsl_dpio_cmd.h" -+ -+#include "dpio-drv.h" -+ -+#define DPIO_DESCRIPTION "DPIO Driver" -+ -+MODULE_LICENSE("Dual BSD/GPL"); -+MODULE_AUTHOR("Freescale Semiconductor, Inc"); -+MODULE_DESCRIPTION(DPIO_DESCRIPTION); -+ -+#define MAX_DPIO_IRQ_NAME 16 /* Big enough for "FSL DPIO %d" */ -+ -+struct dpio_priv { -+ struct dpaa2_io *io; -+ char irq_name[MAX_DPIO_IRQ_NAME]; -+ struct task_struct *thread; -+}; -+ -+static int dpio_thread(void *data) -+{ -+ struct dpaa2_io *io = data; -+ -+ while (!kthread_should_stop()) { -+ int err = dpaa2_io_poll(io); -+ -+ if (err) { -+ pr_err("dpaa2_io_poll() failed\n"); -+ return err; -+ } -+ msleep(50); -+ } -+ return 0; -+} -+ -+static irqreturn_t dpio_irq_handler(int irq_num, void *arg) -+{ -+ struct device *dev = (struct device *)arg; -+ struct dpio_priv *priv = dev_get_drvdata(dev); -+ -+ return dpaa2_io_irq(priv->io); -+} -+ -+static void unregister_dpio_irq_handlers(struct fsl_mc_device *ls_dev) -+{ -+ int i; -+ struct fsl_mc_device_irq *irq; -+ int irq_count = ls_dev->obj_desc.irq_count; -+ -+ for (i = 0; i < irq_count; i++) { -+ irq = ls_dev->irqs[i]; -+ devm_free_irq(&ls_dev->dev, irq->irq_number, &ls_dev->dev); -+ } -+} -+ -+static int register_dpio_irq_handlers(struct fsl_mc_device *ls_dev, int cpu) -+{ -+ struct dpio_priv *priv; -+ unsigned int i; -+ int error; -+ struct fsl_mc_device_irq *irq; -+ unsigned int num_irq_handlers_registered = 0; -+ int irq_count = ls_dev->obj_desc.irq_count; -+ cpumask_t mask; -+ -+ priv = dev_get_drvdata(&ls_dev->dev); -+ -+ if (WARN_ON(irq_count != 1)) -+ return -EINVAL; -+ -+ for (i = 0; i < irq_count; i++) { -+ irq = ls_dev->irqs[i]; -+ error = devm_request_irq(&ls_dev->dev, -+ irq->irq_number, -+ dpio_irq_handler, -+ 0, -+ priv->irq_name, -+ &ls_dev->dev); -+ if (error < 0) { -+ dev_err(&ls_dev->dev, -+ "devm_request_irq() failed: %d\n", -+ error); -+ goto error_unregister_irq_handlers; -+ } -+ -+ /* Set the IRQ affinity */ -+ cpumask_clear(&mask); -+ cpumask_set_cpu(cpu, &mask); -+ if (irq_set_affinity(irq->irq_number, &mask)) -+ pr_err("irq_set_affinity failed irq %d cpu %d\n", -+ irq->irq_number, cpu); -+ -+ num_irq_handlers_registered++; -+ } -+ -+ return 0; -+ -+error_unregister_irq_handlers: -+ for (i = 0; i < num_irq_handlers_registered; i++) { -+ irq = ls_dev->irqs[i]; -+ devm_free_irq(&ls_dev->dev, irq->irq_number, -+ &ls_dev->dev); -+ } -+ -+ return error; -+} -+ -+static int __cold -+dpaa2_dpio_probe(struct fsl_mc_device *ls_dev) -+{ -+ struct dpio_attr dpio_attrs; -+ struct dpaa2_io_desc desc; -+ struct dpio_priv *priv; -+ int err = -ENOMEM; -+ struct device *dev = &ls_dev->dev; -+ struct dpaa2_io *defservice; -+ bool irq_allocated = false; -+ static int next_cpu; -+ -+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ goto err_priv_alloc; -+ -+ dev_set_drvdata(dev, priv); -+ -+ err = fsl_mc_portal_allocate(ls_dev, 0, &ls_dev->mc_io); -+ if (err) { -+ dev_err(dev, "MC portal allocation failed\n"); -+ err = -EPROBE_DEFER; -+ goto err_mcportal; -+ } -+ -+ err = dpio_open(ls_dev->mc_io, 0, ls_dev->obj_desc.id, -+ &ls_dev->mc_handle); -+ if (err) { -+ dev_err(dev, "dpio_open() failed\n"); -+ goto err_open; -+ } -+ -+ err = dpio_get_attributes(ls_dev->mc_io, 0, ls_dev->mc_handle, -+ &dpio_attrs); -+ if (err) { -+ dev_err(dev, "dpio_get_attributes() failed %d\n", err); -+ goto err_get_attr; -+ } -+ err = dpio_enable(ls_dev->mc_io, 0, ls_dev->mc_handle); -+ if (err) { -+ dev_err(dev, "dpio_enable() failed %d\n", err); -+ goto err_get_attr; -+ } -+ pr_info("ce_paddr=0x%llx, ci_paddr=0x%llx, portalid=%d, prios=%d\n", -+ ls_dev->regions[0].start, -+ ls_dev->regions[1].start, -+ dpio_attrs.qbman_portal_id, -+ dpio_attrs.num_priorities); -+ -+ pr_info("ce_size=0x%llx, ci_size=0x%llx\n", -+ resource_size(&ls_dev->regions[0]), -+ resource_size(&ls_dev->regions[1])); -+ -+ desc.qman_version = dpio_attrs.qbman_version; -+ /* Build DPIO driver object out of raw MC object */ -+ desc.receives_notifications = dpio_attrs.num_priorities ? 1 : 0; -+ desc.has_irq = 1; -+ desc.will_poll = 1; -+ desc.has_8prio = dpio_attrs.num_priorities == 8 ? 1 : 0; -+ desc.cpu = next_cpu; -+ desc.stash_affinity = next_cpu; -+ next_cpu = (next_cpu + 1) % num_active_cpus(); -+ desc.dpio_id = ls_dev->obj_desc.id; -+ desc.regs_cena = ioremap_cache_ns(ls_dev->regions[0].start, -+ resource_size(&ls_dev->regions[0])); -+ desc.regs_cinh = ioremap(ls_dev->regions[1].start, -+ resource_size(&ls_dev->regions[1])); -+ -+ err = fsl_mc_allocate_irqs(ls_dev); -+ if (err) { -+ dev_err(dev, "DPIO fsl_mc_allocate_irqs failed\n"); -+ desc.has_irq = 0; -+ } else { -+ irq_allocated = true; -+ -+ snprintf(priv->irq_name, MAX_DPIO_IRQ_NAME, "FSL DPIO %d", -+ desc.dpio_id); -+ -+ err = register_dpio_irq_handlers(ls_dev, desc.cpu); -+ if (err) -+ desc.has_irq = 0; -+ } -+ -+ priv->io = dpaa2_io_create(&desc); -+ if (!priv->io) { -+ dev_err(dev, "DPIO setup failed\n"); -+ goto err_dpaa2_io_create; -+ } -+ -+ /* If no irq then go to poll mode */ -+ if (desc.has_irq == 0) { -+ dev_info(dev, "Using polling mode for DPIO %d\n", -+ desc.dpio_id); -+ /* goto err_register_dpio_irq; */ -+ /* TEMP: Start polling if IRQ could not -+ be registered. This will go away once -+ KVM support for MSI is present */ -+ if (irq_allocated == true) -+ fsl_mc_free_irqs(ls_dev); -+ -+ if (desc.stash_affinity) -+ priv->thread = kthread_create_on_cpu(dpio_thread, -+ priv->io, -+ desc.cpu, -+ "dpio_aff%u"); -+ else -+ priv->thread = -+ kthread_create(dpio_thread, -+ priv->io, -+ "dpio_non%u", -+ dpio_attrs.qbman_portal_id); -+ if (IS_ERR(priv->thread)) { -+ dev_err(dev, "DPIO thread failure\n"); -+ err = PTR_ERR(priv->thread); -+ goto err_dpaa_thread; -+ } -+ wake_up_process(priv->thread); -+ } -+ -+ defservice = dpaa2_io_default_service(); -+ err = dpaa2_io_service_add(defservice, priv->io); -+ dpaa2_io_down(defservice); -+ if (err) { -+ dev_err(dev, "DPIO add-to-service failed\n"); -+ goto err_dpaa2_io_add; -+ } -+ -+ dev_info(dev, "dpio: probed object %d\n", ls_dev->obj_desc.id); -+ dev_info(dev, " receives_notifications = %d\n", -+ desc.receives_notifications); -+ dev_info(dev, " has_irq = %d\n", desc.has_irq); -+ dpio_close(ls_dev->mc_io, 0, ls_dev->mc_handle); -+ fsl_mc_portal_free(ls_dev->mc_io); -+ return 0; -+ -+err_dpaa2_io_add: -+ unregister_dpio_irq_handlers(ls_dev); -+/* TEMP: To be restored once polling is removed -+ err_register_dpio_irq: -+ fsl_mc_free_irqs(ls_dev); -+*/ -+err_dpaa_thread: -+err_dpaa2_io_create: -+ dpio_disable(ls_dev->mc_io, 0, ls_dev->mc_handle); -+err_get_attr: -+ dpio_close(ls_dev->mc_io, 0, ls_dev->mc_handle); -+err_open: -+ fsl_mc_portal_free(ls_dev->mc_io); -+err_mcportal: -+ dev_set_drvdata(dev, NULL); -+ devm_kfree(dev, priv); -+err_priv_alloc: -+ return err; -+} -+ -+/* -+ * Tear down interrupts for a given DPIO object -+ */ -+static void dpio_teardown_irqs(struct fsl_mc_device *ls_dev) -+{ -+ /* (void)disable_dpio_irqs(ls_dev); */ -+ unregister_dpio_irq_handlers(ls_dev); -+ fsl_mc_free_irqs(ls_dev); -+} -+ -+static int __cold -+dpaa2_dpio_remove(struct fsl_mc_device *ls_dev) -+{ -+ struct device *dev; -+ struct dpio_priv *priv; -+ int err; -+ -+ dev = &ls_dev->dev; -+ priv = dev_get_drvdata(dev); -+ -+ /* there is no implementation yet for pulling a DPIO object out of a -+ * running service (and they're currently always running). -+ */ -+ dev_crit(dev, "DPIO unplugging is broken, the service holds onto it\n"); -+ -+ if (priv->thread) -+ kthread_stop(priv->thread); -+ else -+ dpio_teardown_irqs(ls_dev); -+ -+ err = fsl_mc_portal_allocate(ls_dev, 0, &ls_dev->mc_io); -+ if (err) { -+ dev_err(dev, "MC portal allocation failed\n"); -+ goto err_mcportal; -+ } -+ -+ err = dpio_open(ls_dev->mc_io, 0, ls_dev->obj_desc.id, -+ &ls_dev->mc_handle); -+ if (err) { -+ dev_err(dev, "dpio_open() failed\n"); -+ goto err_open; -+ } -+ -+ dev_set_drvdata(dev, NULL); -+ dpaa2_io_down(priv->io); -+ -+ err = 0; -+ -+ dpio_disable(ls_dev->mc_io, 0, ls_dev->mc_handle); -+ dpio_close(ls_dev->mc_io, 0, ls_dev->mc_handle); -+err_open: -+ fsl_mc_portal_free(ls_dev->mc_io); -+err_mcportal: -+ return err; -+} -+ -+static const struct fsl_mc_device_match_id dpaa2_dpio_match_id_table[] = { -+ { -+ .vendor = FSL_MC_VENDOR_FREESCALE, -+ .obj_type = "dpio", -+ .ver_major = DPIO_VER_MAJOR, -+ .ver_minor = DPIO_VER_MINOR -+ }, -+ { .vendor = 0x0 } -+}; -+ -+static struct fsl_mc_driver dpaa2_dpio_driver = { -+ .driver = { -+ .name = KBUILD_MODNAME, -+ .owner = THIS_MODULE, -+ }, -+ .probe = dpaa2_dpio_probe, -+ .remove = dpaa2_dpio_remove, -+ .match_id_table = dpaa2_dpio_match_id_table -+}; -+ -+static int dpio_driver_init(void) -+{ -+ int err; -+ -+ err = dpaa2_io_service_driver_init(); -+ if (!err) { -+ err = fsl_mc_driver_register(&dpaa2_dpio_driver); -+ if (err) -+ dpaa2_io_service_driver_exit(); -+ } -+ return err; -+} -+static void dpio_driver_exit(void) -+{ -+ fsl_mc_driver_unregister(&dpaa2_dpio_driver); -+ dpaa2_io_service_driver_exit(); -+} -+module_init(dpio_driver_init); -+module_exit(dpio_driver_exit); -diff --git a/drivers/staging/fsl-mc/bus/dpio/dpio-drv.h b/drivers/staging/fsl-mc/bus/dpio/dpio-drv.h -new file mode 100644 -index 0000000..fe8d40b ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpio/dpio-drv.h -@@ -0,0 +1,33 @@ -+/* Copyright 2014 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. -+ */ -+ -+int dpaa2_io_service_driver_init(void); -+void dpaa2_io_service_driver_exit(void); -diff --git a/drivers/staging/fsl-mc/bus/dpio/dpio.c b/drivers/staging/fsl-mc/bus/dpio/dpio.c -new file mode 100644 -index 0000000..b63edd6 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpio/dpio.c -@@ -0,0 +1,468 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#include "../../include/mc-sys.h" -+#include "../../include/mc-cmd.h" -+#include "fsl_dpio.h" -+#include "fsl_dpio_cmd.h" -+ -+int dpio_open(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ int dpio_id, -+ uint16_t *token) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_OPEN, -+ cmd_flags, -+ 0); -+ DPIO_CMD_OPEN(cmd, dpio_id); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *token = MC_CMD_HDR_READ_TOKEN(cmd.header); -+ -+ return 0; -+} -+ -+int dpio_close(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_CLOSE, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpio_create(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ const struct dpio_cfg *cfg, -+ uint16_t *token) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_CREATE, -+ cmd_flags, -+ 0); -+ DPIO_CMD_CREATE(cmd, cfg); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *token = MC_CMD_HDR_READ_TOKEN(cmd.header); -+ -+ return 0; -+} -+ -+int dpio_destroy(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_DESTROY, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpio_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_ENABLE, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpio_disable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_DISABLE, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpio_is_enabled(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *en) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_IS_ENABLED, cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPIO_RSP_IS_ENABLED(cmd, *en); -+ -+ return 0; -+} -+ -+int dpio_reset(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_RESET, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpio_set_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ struct dpio_irq_cfg *irq_cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_SET_IRQ, -+ cmd_flags, -+ token); -+ DPIO_CMD_SET_IRQ(cmd, irq_index, irq_cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpio_get_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ int *type, -+ struct dpio_irq_cfg *irq_cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_GET_IRQ, -+ cmd_flags, -+ token); -+ DPIO_CMD_GET_IRQ(cmd, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPIO_RSP_GET_IRQ(cmd, *type, irq_cfg); -+ -+ return 0; -+} -+ -+int dpio_set_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t en) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_SET_IRQ_ENABLE, -+ cmd_flags, -+ token); -+ DPIO_CMD_SET_IRQ_ENABLE(cmd, irq_index, en); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpio_get_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t *en) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_GET_IRQ_ENABLE, -+ cmd_flags, -+ token); -+ DPIO_CMD_GET_IRQ_ENABLE(cmd, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPIO_RSP_GET_IRQ_ENABLE(cmd, *en); -+ -+ return 0; -+} -+ -+int dpio_set_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t mask) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_SET_IRQ_MASK, -+ cmd_flags, -+ token); -+ DPIO_CMD_SET_IRQ_MASK(cmd, irq_index, mask); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpio_get_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *mask) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_GET_IRQ_MASK, -+ cmd_flags, -+ token); -+ DPIO_CMD_GET_IRQ_MASK(cmd, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPIO_RSP_GET_IRQ_MASK(cmd, *mask); -+ -+ return 0; -+} -+ -+int dpio_get_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *status) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_GET_IRQ_STATUS, -+ cmd_flags, -+ token); -+ DPIO_CMD_GET_IRQ_STATUS(cmd, irq_index, *status); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPIO_RSP_GET_IRQ_STATUS(cmd, *status); -+ -+ return 0; -+} -+ -+int dpio_clear_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t status) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_CLEAR_IRQ_STATUS, -+ cmd_flags, -+ token); -+ DPIO_CMD_CLEAR_IRQ_STATUS(cmd, irq_index, status); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpio_get_attributes(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpio_attr *attr) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_GET_ATTR, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPIO_RSP_GET_ATTR(cmd, attr); -+ -+ return 0; -+} -+ -+int dpio_set_stashing_destination(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t sdest) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_SET_STASHING_DEST, -+ cmd_flags, -+ token); -+ DPIO_CMD_SET_STASHING_DEST(cmd, sdest); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpio_get_stashing_destination(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t *sdest) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_GET_STASHING_DEST, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPIO_RSP_GET_STASHING_DEST(cmd, *sdest); -+ -+ return 0; -+} -+ -+int dpio_add_static_dequeue_channel(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int dpcon_id, -+ uint8_t *channel_index) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_ADD_STATIC_DEQUEUE_CHANNEL, -+ cmd_flags, -+ token); -+ DPIO_CMD_ADD_STATIC_DEQUEUE_CHANNEL(cmd, dpcon_id); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPIO_RSP_ADD_STATIC_DEQUEUE_CHANNEL(cmd, *channel_index); -+ -+ return 0; -+} -+ -+int dpio_remove_static_dequeue_channel(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int dpcon_id) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header( -+ DPIO_CMDID_REMOVE_STATIC_DEQUEUE_CHANNEL, -+ cmd_flags, -+ token); -+ DPIO_CMD_REMOVE_STATIC_DEQUEUE_CHANNEL(cmd, dpcon_id); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -diff --git a/drivers/staging/fsl-mc/bus/dpio/dpio_service.c b/drivers/staging/fsl-mc/bus/dpio/dpio_service.c -new file mode 100644 -index 0000000..ebcfd59 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpio/dpio_service.c -@@ -0,0 +1,801 @@ -+/* Copyright 2014 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 -+#include "fsl_qbman_portal.h" -+#include "../../include/mc.h" -+#include "../../include/fsl_dpaa2_io.h" -+#include "fsl_dpio.h" -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "dpio-drv.h" -+#include "qbman_debug.h" -+ -+#define UNIMPLEMENTED() pr_err("FOO: %s unimplemented!\n", __func__) -+ -+#define MAGIC_SERVICE 0xabcd9876 -+#define MAGIC_OBJECT 0x1234fedc -+ -+struct dpaa2_io { -+ /* If MAGIC_SERVICE, this is a group of objects, use the 'service' part -+ * of the union. If MAGIC_OBJECT, use the 'object' part of the union. If -+ * it's neither, something got corrupted. This is mainly to satisfy -+ * dpaa2_io_from_registration(), which dereferences a caller- -+ * instantiated struct and so warrants a bug-checking step - hence the -+ * magic rather than a boolean. -+ */ -+ unsigned int magic; -+ atomic_t refs; -+ union { -+ struct dpaa2_io_service { -+ spinlock_t lock; -+ struct list_head list; -+ /* for targeted dpaa2_io selection */ -+ struct dpaa2_io *objects_by_cpu[NR_CPUS]; -+ cpumask_t cpus_notifications; -+ cpumask_t cpus_stashing; -+ int has_nonaffine; -+ /* slight hack. record the special case of the -+ * "default service", because that's the case where we -+ * need to avoid a kfree() ... */ -+ int is_defservice; -+ } service; -+ struct dpaa2_io_object { -+ struct dpaa2_io_desc dpio_desc; -+ struct qbman_swp_desc swp_desc; -+ struct qbman_swp *swp; -+ /* If the object is part of a service, this is it (and -+ * 'node' is linked into the service's list) */ -+ struct dpaa2_io *service; -+ struct list_head node; -+ /* Interrupt mask, as used with -+ * qbman_swp_interrupt_[gs]et_vanish(). This isn't -+ * locked, because the higher layer is driving all -+ * "ingress" processing. */ -+ uint32_t irq_mask; -+ /* As part of simplifying assumptions, we provide an -+ * irq-safe lock for each type of DPIO operation that -+ * isn't innately lockless. The selection algorithms -+ * (which are simplified) require this, whereas -+ * eventually adherence to cpu-affinity will presumably -+ * relax the locking requirements. */ -+ spinlock_t lock_mgmt_cmd; -+ spinlock_t lock_notifications; -+ struct list_head notifications; -+ } object; -+ }; -+}; -+ -+struct dpaa2_io_store { -+ unsigned int max; -+ dma_addr_t paddr; -+ struct dpaa2_dq *vaddr; -+ void *alloced_addr; /* the actual return from kmalloc as it may -+ be adjusted for alignment purposes */ -+ unsigned int idx; /* position of the next-to-be-returned entry */ -+ struct qbman_swp *swp; /* portal used to issue VDQCR */ -+ struct device *dev; /* device used for DMA mapping */ -+}; -+ -+static struct dpaa2_io def_serv; -+ -+/**********************/ -+/* Internal functions */ -+/**********************/ -+ -+static void service_init(struct dpaa2_io *d, int is_defservice) -+{ -+ struct dpaa2_io_service *s = &d->service; -+ -+ d->magic = MAGIC_SERVICE; -+ atomic_set(&d->refs, 1); -+ spin_lock_init(&s->lock); -+ INIT_LIST_HEAD(&s->list); -+ cpumask_clear(&s->cpus_notifications); -+ cpumask_clear(&s->cpus_stashing); -+ s->has_nonaffine = 0; -+ s->is_defservice = is_defservice; -+} -+ -+/* Selection algorithms, stupid ones at that. These are to handle the case where -+ * the given dpaa2_io is a service, by choosing the non-service dpaa2_io within -+ * it to use. -+ */ -+static struct dpaa2_io *_service_select_by_cpu_slow(struct dpaa2_io_service *ss, -+ int cpu) -+{ -+ struct dpaa2_io *o; -+ unsigned long irqflags; -+ -+ spin_lock_irqsave(&ss->lock, irqflags); -+ /* TODO: this is about the dumbest and slowest selection algorithm you -+ * could imagine. (We're looking for something working first, and -+ * something efficient second...) -+ */ -+ list_for_each_entry(o, &ss->list, object.node) -+ if (o->object.dpio_desc.cpu == cpu) -+ goto found; -+ -+ /* No joy. Try the first nonaffine portal (bleurgh) */ -+ if (ss->has_nonaffine) -+ list_for_each_entry(o, &ss->list, object.node) -+ if (!o->object.dpio_desc.stash_affinity) -+ goto found; -+ -+ /* No joy. Try the first object. Told you it was horrible. */ -+ if (!list_empty(&ss->list)) -+ o = list_entry(ss->list.next, struct dpaa2_io, object.node); -+ else -+ o = NULL; -+ -+found: -+ spin_unlock_irqrestore(&ss->lock, irqflags); -+ return o; -+} -+ -+static struct dpaa2_io *service_select_by_cpu(struct dpaa2_io *d, int cpu) -+{ -+ struct dpaa2_io_service *ss; -+ unsigned long irqflags; -+ -+ if (!d) -+ d = &def_serv; -+ else if (d->magic == MAGIC_OBJECT) -+ return d; -+ BUG_ON(d->magic != MAGIC_SERVICE); -+ -+ ss = &d->service; -+ -+ /* If cpu==-1, choose the current cpu, with no guarantees about -+ * potentially being migrated away. -+ */ -+ if (unlikely(cpu < 0)) { -+ spin_lock_irqsave(&ss->lock, irqflags); -+ cpu = smp_processor_id(); -+ spin_unlock_irqrestore(&ss->lock, irqflags); -+ -+ return _service_select_by_cpu_slow(ss, cpu); -+ } -+ -+ /* If a specific cpu was requested, pick it up immediately */ -+ return ss->objects_by_cpu[cpu]; -+} -+ -+static inline struct dpaa2_io *service_select_any(struct dpaa2_io *d) -+{ -+ struct dpaa2_io_service *ss; -+ struct dpaa2_io *o; -+ unsigned long irqflags; -+ -+ if (!d) -+ d = &def_serv; -+ else if (d->magic == MAGIC_OBJECT) -+ return d; -+ BUG_ON(d->magic != MAGIC_SERVICE); -+ -+ /* -+ * Lock the service, looking for the first DPIO object in the list, -+ * ignore everything else about that DPIO, and choose it to do the -+ * operation! As a post-selection step, move the DPIO to the end of -+ * the list. It should improve load-balancing a little, although it -+ * might also incur a performance hit, given that the lock is *global* -+ * and this may be called on the fast-path... -+ */ -+ ss = &d->service; -+ spin_lock_irqsave(&ss->lock, irqflags); -+ if (!list_empty(&ss->list)) { -+ o = list_entry(ss->list.next, struct dpaa2_io, object.node); -+ list_del(&o->object.node); -+ list_add_tail(&o->object.node, &ss->list); -+ } else -+ o = NULL; -+ spin_unlock_irqrestore(&ss->lock, irqflags); -+ return o; -+} -+ -+/* If the context is not preemptible, select the service affine to the -+ * current cpu. Otherwise, "select any". -+ */ -+static inline struct dpaa2_io *_service_select(struct dpaa2_io *d) -+{ -+ struct dpaa2_io *temp = d; -+ -+ if (likely(!preemptible())) { -+ d = service_select_by_cpu(d, smp_processor_id()); -+ if (likely(d)) -+ return d; -+ } -+ return service_select_any(temp); -+} -+ -+/**********************/ -+/* Exported functions */ -+/**********************/ -+ -+struct dpaa2_io *dpaa2_io_create(const struct dpaa2_io_desc *desc) -+{ -+ struct dpaa2_io *ret = kmalloc(sizeof(*ret), GFP_KERNEL); -+ struct dpaa2_io_object *o = &ret->object; -+ -+ if (!ret) -+ return NULL; -+ ret->magic = MAGIC_OBJECT; -+ atomic_set(&ret->refs, 1); -+ o->dpio_desc = *desc; -+ o->swp_desc.cena_bar = o->dpio_desc.regs_cena; -+ o->swp_desc.cinh_bar = o->dpio_desc.regs_cinh; -+ o->swp_desc.qman_version = o->dpio_desc.qman_version; -+ o->swp = qbman_swp_init(&o->swp_desc); -+ o->service = NULL; -+ if (!o->swp) { -+ kfree(ret); -+ return NULL; -+ } -+ INIT_LIST_HEAD(&o->node); -+ spin_lock_init(&o->lock_mgmt_cmd); -+ spin_lock_init(&o->lock_notifications); -+ INIT_LIST_HEAD(&o->notifications); -+ if (!o->dpio_desc.has_irq) -+ qbman_swp_interrupt_set_vanish(o->swp, 0xffffffff); -+ else { -+ /* For now only enable DQRR interrupts */ -+ qbman_swp_interrupt_set_trigger(o->swp, -+ QBMAN_SWP_INTERRUPT_DQRI); -+ } -+ qbman_swp_interrupt_clear_status(o->swp, 0xffffffff); -+ if (o->dpio_desc.receives_notifications) -+ qbman_swp_push_set(o->swp, 0, 1); -+ return ret; -+} -+EXPORT_SYMBOL(dpaa2_io_create); -+ -+struct dpaa2_io *dpaa2_io_create_service(void) -+{ -+ struct dpaa2_io *ret = kmalloc(sizeof(*ret), GFP_KERNEL); -+ -+ if (ret) -+ service_init(ret, 0); -+ return ret; -+} -+EXPORT_SYMBOL(dpaa2_io_create_service); -+ -+struct dpaa2_io *dpaa2_io_default_service(void) -+{ -+ atomic_inc(&def_serv.refs); -+ return &def_serv; -+} -+EXPORT_SYMBOL(dpaa2_io_default_service); -+ -+void dpaa2_io_down(struct dpaa2_io *d) -+{ -+ if (!atomic_dec_and_test(&d->refs)) -+ return; -+ if (d->magic == MAGIC_SERVICE) { -+ BUG_ON(!list_empty(&d->service.list)); -+ if (d->service.is_defservice) -+ /* avoid the kfree()! */ -+ return; -+ } else { -+ BUG_ON(d->magic != MAGIC_OBJECT); -+ BUG_ON(d->object.service); -+ BUG_ON(!list_empty(&d->object.notifications)); -+ } -+ kfree(d); -+} -+EXPORT_SYMBOL(dpaa2_io_down); -+ -+int dpaa2_io_service_add(struct dpaa2_io *s, struct dpaa2_io *o) -+{ -+ struct dpaa2_io_service *ss = &s->service; -+ struct dpaa2_io_object *oo = &o->object; -+ int res = -EINVAL; -+ -+ if ((s->magic != MAGIC_SERVICE) || (o->magic != MAGIC_OBJECT)) -+ return res; -+ atomic_inc(&o->refs); -+ atomic_inc(&s->refs); -+ spin_lock(&ss->lock); -+ /* 'obj' must not already be associated with a service */ -+ if (!oo->service) { -+ oo->service = s; -+ list_add(&oo->node, &ss->list); -+ if (oo->dpio_desc.receives_notifications) { -+ cpumask_set_cpu(oo->dpio_desc.cpu, -+ &ss->cpus_notifications); -+ /* Update the fast-access array */ -+ ss->objects_by_cpu[oo->dpio_desc.cpu] = -+ container_of(oo, struct dpaa2_io, object); -+ } -+ if (oo->dpio_desc.stash_affinity) -+ cpumask_set_cpu(oo->dpio_desc.cpu, -+ &ss->cpus_stashing); -+ if (!oo->dpio_desc.stash_affinity) -+ ss->has_nonaffine = 1; -+ /* success */ -+ res = 0; -+ } -+ spin_unlock(&ss->lock); -+ if (res) { -+ dpaa2_io_down(s); -+ dpaa2_io_down(o); -+ } -+ return res; -+} -+EXPORT_SYMBOL(dpaa2_io_service_add); -+ -+int dpaa2_io_get_descriptor(struct dpaa2_io *obj, struct dpaa2_io_desc *desc) -+{ -+ if (obj->magic == MAGIC_SERVICE) -+ return -EINVAL; -+ BUG_ON(obj->magic != MAGIC_OBJECT); -+ *desc = obj->object.dpio_desc; -+ return 0; -+} -+EXPORT_SYMBOL(dpaa2_io_get_descriptor); -+ -+#define DPAA_POLL_MAX 32 -+ -+int dpaa2_io_poll(struct dpaa2_io *obj) -+{ -+ const struct dpaa2_dq *dq; -+ struct qbman_swp *swp; -+ int max = 0; -+ -+ if (obj->magic != MAGIC_OBJECT) -+ return -EINVAL; -+ swp = obj->object.swp; -+ dq = qbman_swp_dqrr_next(swp); -+ while (dq) { -+ if (qbman_result_is_SCN(dq)) { -+ struct dpaa2_io_notification_ctx *ctx; -+ uint64_t q64; -+ -+ q64 = qbman_result_SCN_ctx(dq); -+ ctx = (void *)q64; -+ ctx->cb(ctx); -+ } else -+ pr_crit("Unrecognised/ignored DQRR entry\n"); -+ qbman_swp_dqrr_consume(swp, dq); -+ ++max; -+ if (max > DPAA_POLL_MAX) -+ return 0; -+ dq = qbman_swp_dqrr_next(swp); -+ } -+ return 0; -+} -+EXPORT_SYMBOL(dpaa2_io_poll); -+ -+int dpaa2_io_irq(struct dpaa2_io *obj) -+{ -+ struct qbman_swp *swp; -+ uint32_t status; -+ -+ if (obj->magic != MAGIC_OBJECT) -+ return -EINVAL; -+ swp = obj->object.swp; -+ status = qbman_swp_interrupt_read_status(swp); -+ if (!status) -+ return IRQ_NONE; -+ dpaa2_io_poll(obj); -+ qbman_swp_interrupt_clear_status(swp, status); -+ qbman_swp_interrupt_set_inhibit(swp, 0); -+ return IRQ_HANDLED; -+} -+EXPORT_SYMBOL(dpaa2_io_irq); -+ -+int dpaa2_io_pause_poll(struct dpaa2_io *obj) -+{ -+ UNIMPLEMENTED(); -+ return -EINVAL; -+} -+EXPORT_SYMBOL(dpaa2_io_pause_poll); -+ -+int dpaa2_io_resume_poll(struct dpaa2_io *obj) -+{ -+ UNIMPLEMENTED(); -+ return -EINVAL; -+} -+EXPORT_SYMBOL(dpaa2_io_resume_poll); -+ -+void dpaa2_io_service_notifications(struct dpaa2_io *s, cpumask_t *mask) -+{ -+ struct dpaa2_io_service *ss = &s->service; -+ -+ BUG_ON(s->magic != MAGIC_SERVICE); -+ cpumask_copy(mask, &ss->cpus_notifications); -+} -+EXPORT_SYMBOL(dpaa2_io_service_notifications); -+ -+void dpaa2_io_service_stashing(struct dpaa2_io *s, cpumask_t *mask) -+{ -+ struct dpaa2_io_service *ss = &s->service; -+ -+ BUG_ON(s->magic != MAGIC_SERVICE); -+ cpumask_copy(mask, &ss->cpus_stashing); -+} -+EXPORT_SYMBOL(dpaa2_io_service_stashing); -+ -+int dpaa2_io_service_has_nonaffine(struct dpaa2_io *s) -+{ -+ struct dpaa2_io_service *ss = &s->service; -+ -+ BUG_ON(s->magic != MAGIC_SERVICE); -+ return ss->has_nonaffine; -+} -+EXPORT_SYMBOL(dpaa2_io_service_has_nonaffine); -+ -+int dpaa2_io_service_register(struct dpaa2_io *d, -+ struct dpaa2_io_notification_ctx *ctx) -+{ -+ unsigned long irqflags; -+ -+ d = service_select_by_cpu(d, ctx->desired_cpu); -+ if (!d) -+ return -ENODEV; -+ ctx->dpio_id = d->object.dpio_desc.dpio_id; -+ ctx->qman64 = (uint64_t)ctx; -+ ctx->dpio_private = d; -+ spin_lock_irqsave(&d->object.lock_notifications, irqflags); -+ list_add(&ctx->node, &d->object.notifications); -+ spin_unlock_irqrestore(&d->object.lock_notifications, irqflags); -+ if (ctx->is_cdan) -+ /* Enable the generation of CDAN notifications */ -+ qbman_swp_CDAN_set_context_enable(d->object.swp, -+ (uint16_t)ctx->id, -+ ctx->qman64); -+ return 0; -+} -+EXPORT_SYMBOL(dpaa2_io_service_register); -+ -+int dpaa2_io_service_deregister(struct dpaa2_io *service, -+ struct dpaa2_io_notification_ctx *ctx) -+{ -+ struct dpaa2_io *d = ctx->dpio_private; -+ unsigned long irqflags; -+ -+ if (!service) -+ service = &def_serv; -+ BUG_ON((service != d) && (service != d->object.service)); -+ if (ctx->is_cdan) -+ qbman_swp_CDAN_disable(d->object.swp, -+ (uint16_t)ctx->id); -+ spin_lock_irqsave(&d->object.lock_notifications, irqflags); -+ list_del(&ctx->node); -+ spin_unlock_irqrestore(&d->object.lock_notifications, irqflags); -+ return 0; -+} -+EXPORT_SYMBOL(dpaa2_io_service_deregister); -+ -+int dpaa2_io_service_rearm(struct dpaa2_io *d, -+ struct dpaa2_io_notification_ctx *ctx) -+{ -+ unsigned long irqflags; -+ int err; -+ -+ d = _service_select(d); -+ if (!d) -+ return -ENODEV; -+ spin_lock_irqsave(&d->object.lock_mgmt_cmd, irqflags); -+ if (ctx->is_cdan) -+ err = qbman_swp_CDAN_enable(d->object.swp, (uint16_t)ctx->id); -+ else -+ err = qbman_swp_fq_schedule(d->object.swp, ctx->id); -+ spin_unlock_irqrestore(&d->object.lock_mgmt_cmd, irqflags); -+ return err; -+} -+EXPORT_SYMBOL(dpaa2_io_service_rearm); -+ -+int dpaa2_io_from_registration(struct dpaa2_io_notification_ctx *ctx, -+ struct dpaa2_io **io) -+{ -+ struct dpaa2_io_notification_ctx *tmp; -+ struct dpaa2_io *d = ctx->dpio_private; -+ unsigned long irqflags; -+ int ret = 0; -+ -+ BUG_ON(d->magic != MAGIC_OBJECT); -+ /* Iterate the notifications associated with 'd' looking for a match. If -+ * not, we've been passed an unregistered ctx! */ -+ spin_lock_irqsave(&d->object.lock_notifications, irqflags); -+ list_for_each_entry(tmp, &d->object.notifications, node) -+ if (tmp == ctx) -+ goto found; -+ ret = -EINVAL; -+found: -+ spin_unlock_irqrestore(&d->object.lock_notifications, irqflags); -+ if (!ret) { -+ atomic_inc(&d->refs); -+ *io = d; -+ } -+ return ret; -+} -+EXPORT_SYMBOL(dpaa2_io_from_registration); -+ -+int dpaa2_io_service_get_persistent(struct dpaa2_io *service, int cpu, -+ struct dpaa2_io **ret) -+{ -+ if (cpu == -1) -+ *ret = service_select_any(service); -+ else -+ *ret = service_select_by_cpu(service, cpu); -+ if (*ret) { -+ atomic_inc(&(*ret)->refs); -+ return 0; -+ } -+ return -ENODEV; -+} -+EXPORT_SYMBOL(dpaa2_io_service_get_persistent); -+ -+int dpaa2_io_service_pull_fq(struct dpaa2_io *d, uint32_t fqid, -+ struct dpaa2_io_store *s) -+{ -+ struct qbman_pull_desc pd; -+ int err; -+ -+ qbman_pull_desc_clear(&pd); -+ qbman_pull_desc_set_storage(&pd, s->vaddr, s->paddr, 1); -+ qbman_pull_desc_set_numframes(&pd, (uint8_t)s->max); -+ qbman_pull_desc_set_fq(&pd, fqid); -+ d = _service_select(d); -+ if (!d) -+ return -ENODEV; -+ s->swp = d->object.swp; -+ err = qbman_swp_pull(d->object.swp, &pd); -+ if (err) -+ s->swp = NULL; -+ return err; -+} -+EXPORT_SYMBOL(dpaa2_io_service_pull_fq); -+ -+int dpaa2_io_service_pull_channel(struct dpaa2_io *d, uint32_t channelid, -+ struct dpaa2_io_store *s) -+{ -+ struct qbman_pull_desc pd; -+ int err; -+ -+ qbman_pull_desc_clear(&pd); -+ qbman_pull_desc_set_storage(&pd, s->vaddr, s->paddr, 1); -+ qbman_pull_desc_set_numframes(&pd, (uint8_t)s->max); -+ qbman_pull_desc_set_channel(&pd, channelid, qbman_pull_type_prio); -+ d = _service_select(d); -+ if (!d) -+ return -ENODEV; -+ s->swp = d->object.swp; -+ err = qbman_swp_pull(d->object.swp, &pd); -+ if (err) -+ s->swp = NULL; -+ return err; -+} -+EXPORT_SYMBOL(dpaa2_io_service_pull_channel); -+ -+int dpaa2_io_service_enqueue_fq(struct dpaa2_io *d, -+ uint32_t fqid, -+ const struct dpaa2_fd *fd) -+{ -+ struct qbman_eq_desc ed; -+ -+ d = _service_select(d); -+ if (!d) -+ return -ENODEV; -+ qbman_eq_desc_clear(&ed); -+ qbman_eq_desc_set_no_orp(&ed, 0); -+ qbman_eq_desc_set_fq(&ed, fqid); -+ return qbman_swp_enqueue(d->object.swp, &ed, -+ (const struct qbman_fd *)fd); -+} -+EXPORT_SYMBOL(dpaa2_io_service_enqueue_fq); -+ -+int dpaa2_io_service_enqueue_qd(struct dpaa2_io *d, -+ uint32_t qdid, uint8_t prio, uint16_t qdbin, -+ const struct dpaa2_fd *fd) -+{ -+ struct qbman_eq_desc ed; -+ -+ d = _service_select(d); -+ if (!d) -+ return -ENODEV; -+ qbman_eq_desc_clear(&ed); -+ qbman_eq_desc_set_no_orp(&ed, 0); -+ qbman_eq_desc_set_qd(&ed, qdid, qdbin, prio); -+ return qbman_swp_enqueue(d->object.swp, &ed, -+ (const struct qbman_fd *)fd); -+} -+EXPORT_SYMBOL(dpaa2_io_service_enqueue_qd); -+ -+int dpaa2_io_service_release(struct dpaa2_io *d, -+ uint32_t bpid, -+ const uint64_t *buffers, -+ unsigned int num_buffers) -+{ -+ struct qbman_release_desc rd; -+ -+ d = _service_select(d); -+ if (!d) -+ return -ENODEV; -+ qbman_release_desc_clear(&rd); -+ qbman_release_desc_set_bpid(&rd, bpid); -+ return qbman_swp_release(d->object.swp, &rd, buffers, num_buffers); -+} -+EXPORT_SYMBOL(dpaa2_io_service_release); -+ -+int dpaa2_io_service_acquire(struct dpaa2_io *d, -+ uint32_t bpid, -+ uint64_t *buffers, -+ unsigned int num_buffers) -+{ -+ unsigned long irqflags; -+ int err; -+ -+ d = _service_select(d); -+ if (!d) -+ return -ENODEV; -+ spin_lock_irqsave(&d->object.lock_mgmt_cmd, irqflags); -+ err = qbman_swp_acquire(d->object.swp, bpid, buffers, num_buffers); -+ spin_unlock_irqrestore(&d->object.lock_mgmt_cmd, irqflags); -+ return err; -+} -+EXPORT_SYMBOL(dpaa2_io_service_acquire); -+ -+struct dpaa2_io_store *dpaa2_io_store_create(unsigned int max_frames, -+ struct device *dev) -+{ -+ struct dpaa2_io_store *ret = kmalloc(sizeof(*ret), GFP_KERNEL); -+ size_t size; -+ -+ BUG_ON(!max_frames || (max_frames > 16)); -+ if (!ret) -+ return NULL; -+ ret->max = max_frames; -+ size = max_frames * sizeof(struct dpaa2_dq) + 64; -+ ret->alloced_addr = kmalloc(size, GFP_KERNEL); -+ if (!ret->alloced_addr) { -+ kfree(ret); -+ return NULL; -+ } -+ ret->vaddr = PTR_ALIGN(ret->alloced_addr, 64); -+ ret->paddr = dma_map_single(dev, ret->vaddr, -+ sizeof(struct dpaa2_dq) * max_frames, -+ DMA_FROM_DEVICE); -+ if (dma_mapping_error(dev, ret->paddr)) { -+ kfree(ret->alloced_addr); -+ kfree(ret); -+ return NULL; -+ } -+ ret->idx = 0; -+ ret->dev = dev; -+ return ret; -+} -+EXPORT_SYMBOL(dpaa2_io_store_create); -+ -+void dpaa2_io_store_destroy(struct dpaa2_io_store *s) -+{ -+ dma_unmap_single(s->dev, s->paddr, sizeof(struct dpaa2_dq) * s->max, -+ DMA_FROM_DEVICE); -+ kfree(s->alloced_addr); -+ kfree(s); -+} -+EXPORT_SYMBOL(dpaa2_io_store_destroy); -+ -+struct dpaa2_dq *dpaa2_io_store_next(struct dpaa2_io_store *s, int *is_last) -+{ -+ int match; -+ struct dpaa2_dq *ret = &s->vaddr[s->idx]; -+ -+ match = qbman_result_has_new_result(s->swp, ret); -+ if (!match) { -+ *is_last = 0; -+ return NULL; -+ } -+ BUG_ON(!qbman_result_is_DQ(ret)); -+ s->idx++; -+ if (dpaa2_dq_is_pull_complete(ret)) { -+ *is_last = 1; -+ s->idx = 0; -+ /* If we get an empty dequeue result to terminate a zero-results -+ * vdqcr, return NULL to the caller rather than expecting him to -+ * check non-NULL results every time. */ -+ if (!(dpaa2_dq_flags(ret) & DPAA2_DQ_STAT_VALIDFRAME)) -+ ret = NULL; -+ } else -+ *is_last = 0; -+ return ret; -+} -+EXPORT_SYMBOL(dpaa2_io_store_next); -+ -+#ifdef CONFIG_FSL_QBMAN_DEBUG -+int dpaa2_io_query_fq_count(struct dpaa2_io *d, uint32_t fqid, -+ uint32_t *fcnt, uint32_t *bcnt) -+{ -+ struct qbman_attr state; -+ struct qbman_swp *swp; -+ unsigned long irqflags; -+ int ret; -+ -+ d = service_select_any(d); -+ if (!d) -+ return -ENODEV; -+ -+ swp = d->object.swp; -+ spin_lock_irqsave(&d->object.lock_mgmt_cmd, irqflags); -+ ret = qbman_fq_query_state(swp, fqid, &state); -+ spin_unlock_irqrestore(&d->object.lock_mgmt_cmd, irqflags); -+ if (ret) -+ return ret; -+ *fcnt = qbman_fq_state_frame_count(&state); -+ *bcnt = qbman_fq_state_byte_count(&state); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dpaa2_io_query_fq_count); -+ -+int dpaa2_io_query_bp_count(struct dpaa2_io *d, uint32_t bpid, -+ uint32_t *num) -+{ -+ struct qbman_attr state; -+ struct qbman_swp *swp; -+ unsigned long irqflags; -+ int ret; -+ -+ d = service_select_any(d); -+ if (!d) -+ return -ENODEV; -+ -+ swp = d->object.swp; -+ spin_lock_irqsave(&d->object.lock_mgmt_cmd, irqflags); -+ ret = qbman_bp_query(swp, bpid, &state); -+ spin_unlock_irqrestore(&d->object.lock_mgmt_cmd, irqflags); -+ if (ret) -+ return ret; -+ *num = qbman_bp_info_num_free_bufs(&state); -+ return 0; -+} -+EXPORT_SYMBOL(dpaa2_io_query_bp_count); -+ -+#endif -+ -+/* module init/exit hooks called from dpio-drv.c. These are declared in -+ * dpio-drv.h. -+ */ -+int dpaa2_io_service_driver_init(void) -+{ -+ service_init(&def_serv, 1); -+ return 0; -+} -+ -+void dpaa2_io_service_driver_exit(void) -+{ -+ if (atomic_read(&def_serv.refs) != 1) -+ pr_err("default DPIO service leaves dangling DPIO objects!\n"); -+} -diff --git a/drivers/staging/fsl-mc/bus/dpio/fsl_dpio.h b/drivers/staging/fsl-mc/bus/dpio/fsl_dpio.h -new file mode 100644 -index 0000000..88a492f ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpio/fsl_dpio.h -@@ -0,0 +1,460 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef __FSL_DPIO_H -+#define __FSL_DPIO_H -+ -+/* Data Path I/O Portal API -+ * Contains initialization APIs and runtime control APIs for DPIO -+ */ -+ -+struct fsl_mc_io; -+ -+/** -+ * dpio_open() - Open a control session for the specified object -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @dpio_id: DPIO unique ID -+ * @token: Returned token; use in subsequent API calls -+ * -+ * This function can be used to open a control session for an -+ * already created object; an object may have been declared in -+ * the DPL or by calling the dpio_create() function. -+ * This function returns a unique authentication token, -+ * associated with the specific object ID and the specific MC -+ * portal; this token must be used in all subsequent commands for -+ * this specific object. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpio_open(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ int dpio_id, -+ uint16_t *token); -+ -+/** -+ * dpio_close() - Close the control session of the object -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpio_close(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * enum dpio_channel_mode - DPIO notification channel mode -+ * @DPIO_NO_CHANNEL: No support for notification channel -+ * @DPIO_LOCAL_CHANNEL: Notifications on data availability can be received by a -+ * dedicated channel in the DPIO; user should point the queue's -+ * destination in the relevant interface to this DPIO -+ */ -+enum dpio_channel_mode { -+ DPIO_NO_CHANNEL = 0, -+ DPIO_LOCAL_CHANNEL = 1, -+}; -+ -+/** -+ * struct dpio_cfg - Structure representing DPIO configuration -+ * @channel_mode: Notification channel mode -+ * @num_priorities: Number of priorities for the notification channel (1-8); -+ * relevant only if 'channel_mode = DPIO_LOCAL_CHANNEL' -+ */ -+struct dpio_cfg { -+ enum dpio_channel_mode channel_mode; -+ uint8_t num_priorities; -+}; -+ -+/** -+ * dpio_create() - Create the DPIO object. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @cfg: Configuration structure -+ * @token: Returned token; use in subsequent API calls -+ * -+ * Create the DPIO object, allocate required resources and -+ * perform required initialization. -+ * -+ * The object can be created either by declaring it in the -+ * DPL file, or by calling this function. -+ * -+ * This function returns a unique authentication token, -+ * associated with the specific object ID and the specific MC -+ * portal; this token must be used in all subsequent calls to -+ * this specific object. For objects that are created using the -+ * DPL file, call dpio_open() function to get an authentication -+ * token first. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpio_create(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ const struct dpio_cfg *cfg, -+ uint16_t *token); -+ -+/** -+ * dpio_destroy() - Destroy the DPIO object and release all its resources. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * -+ * Return: '0' on Success; Error code otherwise -+ */ -+int dpio_destroy(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * dpio_enable() - Enable the DPIO, allow I/O portal operations. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * -+ * Return: '0' on Success; Error code otherwise -+ */ -+int dpio_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * dpio_disable() - Disable the DPIO, stop any I/O portal operation. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * -+ * Return: '0' on Success; Error code otherwise -+ */ -+int dpio_disable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * dpio_is_enabled() - Check if the DPIO is enabled. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * @en: Returns '1' if object is enabled; '0' otherwise -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpio_is_enabled(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *en); -+ -+/** -+ * dpio_reset() - Reset the DPIO, returns the object to initial state. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpio_reset(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * dpio_set_stashing_destination() - Set the stashing destination. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * @sdest: stashing destination value -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpio_set_stashing_destination(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t sdest); -+ -+/** -+ * dpio_get_stashing_destination() - Get the stashing destination.. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * @sdest: Returns the stashing destination value -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpio_get_stashing_destination(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t *sdest); -+ -+/** -+ * dpio_add_static_dequeue_channel() - Add a static dequeue channel. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * @dpcon_id: DPCON object ID -+ * @channel_index: Returned channel index to be used in qbman API -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpio_add_static_dequeue_channel(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int dpcon_id, -+ uint8_t *channel_index); -+ -+/** -+ * dpio_remove_static_dequeue_channel() - Remove a static dequeue channel. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * @dpcon_id: DPCON object ID -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpio_remove_static_dequeue_channel(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int dpcon_id); -+ -+/** -+ * DPIO IRQ Index and Events -+ */ -+ -+/** -+ * Irq software-portal index -+ */ -+#define DPIO_IRQ_SWP_INDEX 0 -+ -+/** -+ * struct dpio_irq_cfg - IRQ configuration -+ * @addr: Address that must be written to signal a message-based interrupt -+ * @val: Value to write into irq_addr address -+ * @irq_num: A user defined number associated with this IRQ -+ */ -+struct dpio_irq_cfg { -+ uint64_t addr; -+ uint32_t val; -+ int irq_num; -+}; -+ -+/** -+ * dpio_set_irq() - Set IRQ information for the DPIO to trigger an interrupt. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * @irq_index: Identifies the interrupt index to configure -+ * @irq_cfg: IRQ configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpio_set_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ struct dpio_irq_cfg *irq_cfg); -+ -+/** -+ * dpio_get_irq() - Get IRQ information from the DPIO. -+ * -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * @irq_index: The interrupt index to configure -+ * @type: Interrupt type: 0 represents message interrupt -+ * type (both irq_addr and irq_val are valid) -+ * @irq_cfg: IRQ attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpio_get_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ int *type, -+ struct dpio_irq_cfg *irq_cfg); -+ -+/** -+ * dpio_set_irq_enable() - Set overall interrupt state. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * @irq_index: The interrupt index to configure -+ * @en: Interrupt state - enable = 1, disable = 0 -+ * -+ * Allows GPP software to control when interrupts are generated. -+ * Each interrupt can have up to 32 causes. The enable/disable control's the -+ * overall interrupt state. if the interrupt is disabled no causes will cause -+ * an interrupt. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpio_set_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t en); -+ -+/** -+ * dpio_get_irq_enable() - Get overall interrupt state -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * @irq_index: The interrupt index to configure -+ * @en: Returned interrupt state - enable = 1, disable = 0 -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpio_get_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t *en); -+ -+/** -+ * dpio_set_irq_mask() - Set interrupt mask. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * @irq_index: The interrupt index to configure -+ * @mask: event mask to trigger interrupt; -+ * each bit: -+ * 0 = ignore event -+ * 1 = consider event for asserting IRQ -+ * -+ * Every interrupt can have up to 32 causes and the interrupt model supports -+ * masking/unmasking each cause independently -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpio_set_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t mask); -+ -+/** -+ * dpio_get_irq_mask() - Get interrupt mask. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * @irq_index: The interrupt index to configure -+ * @mask: Returned event mask to trigger interrupt -+ * -+ * Every interrupt can have up to 32 causes and the interrupt model supports -+ * masking/unmasking each cause independently -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpio_get_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *mask); -+ -+/** -+ * dpio_get_irq_status() - Get the current status of any pending interrupts. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * @irq_index: The interrupt index to configure -+ * @status: Returned interrupts status - one bit per cause: -+ * 0 = no interrupt pending -+ * 1 = interrupt pending -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpio_get_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *status); -+ -+/** -+ * dpio_clear_irq_status() - Clear a pending interrupt's status -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * @irq_index: The interrupt index to configure -+ * @status: bits to clear (W1C) - one bit per cause: -+ * 0 = don't change -+ * 1 = clear status bit -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpio_clear_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t status); -+ -+/** -+ * struct dpio_attr - Structure representing DPIO attributes -+ * @id: DPIO object ID -+ * @version: DPIO version -+ * @qbman_portal_ce_offset: offset of the software portal cache-enabled area -+ * @qbman_portal_ci_offset: offset of the software portal cache-inhibited area -+ * @qbman_portal_id: Software portal ID -+ * @channel_mode: Notification channel mode -+ * @num_priorities: Number of priorities for the notification channel (1-8); -+ * relevant only if 'channel_mode = DPIO_LOCAL_CHANNEL' -+ * @qbman_version: QBMAN version -+ */ -+struct dpio_attr { -+ int id; -+ /** -+ * struct version - DPIO version -+ * @major: DPIO major version -+ * @minor: DPIO minor version -+ */ -+ struct { -+ uint16_t major; -+ uint16_t minor; -+ } version; -+ uint64_t qbman_portal_ce_offset; -+ uint64_t qbman_portal_ci_offset; -+ uint16_t qbman_portal_id; -+ enum dpio_channel_mode channel_mode; -+ uint8_t num_priorities; -+ uint32_t qbman_version; -+}; -+ -+/** -+ * dpio_get_attributes() - Retrieve DPIO attributes -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * @attr: Returned object's attributes -+ * -+ * Return: '0' on Success; Error code otherwise -+ */ -+int dpio_get_attributes(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpio_attr *attr); -+#endif /* __FSL_DPIO_H */ -diff --git a/drivers/staging/fsl-mc/bus/dpio/fsl_dpio_cmd.h b/drivers/staging/fsl-mc/bus/dpio/fsl_dpio_cmd.h -new file mode 100644 -index 0000000..f339cd6 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpio/fsl_dpio_cmd.h -@@ -0,0 +1,184 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef _FSL_DPIO_CMD_H -+#define _FSL_DPIO_CMD_H -+ -+/* DPIO Version */ -+#define DPIO_VER_MAJOR 3 -+#define DPIO_VER_MINOR 2 -+ -+/* Command IDs */ -+#define DPIO_CMDID_CLOSE 0x800 -+#define DPIO_CMDID_OPEN 0x803 -+#define DPIO_CMDID_CREATE 0x903 -+#define DPIO_CMDID_DESTROY 0x900 -+ -+#define DPIO_CMDID_ENABLE 0x002 -+#define DPIO_CMDID_DISABLE 0x003 -+#define DPIO_CMDID_GET_ATTR 0x004 -+#define DPIO_CMDID_RESET 0x005 -+#define DPIO_CMDID_IS_ENABLED 0x006 -+ -+#define DPIO_CMDID_SET_IRQ 0x010 -+#define DPIO_CMDID_GET_IRQ 0x011 -+#define DPIO_CMDID_SET_IRQ_ENABLE 0x012 -+#define DPIO_CMDID_GET_IRQ_ENABLE 0x013 -+#define DPIO_CMDID_SET_IRQ_MASK 0x014 -+#define DPIO_CMDID_GET_IRQ_MASK 0x015 -+#define DPIO_CMDID_GET_IRQ_STATUS 0x016 -+#define DPIO_CMDID_CLEAR_IRQ_STATUS 0x017 -+ -+#define DPIO_CMDID_SET_STASHING_DEST 0x120 -+#define DPIO_CMDID_GET_STASHING_DEST 0x121 -+#define DPIO_CMDID_ADD_STATIC_DEQUEUE_CHANNEL 0x122 -+#define DPIO_CMDID_REMOVE_STATIC_DEQUEUE_CHANNEL 0x123 -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_CMD_OPEN(cmd, dpio_id) \ -+ MC_CMD_OP(cmd, 0, 0, 32, int, dpio_id) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_CMD_CREATE(cmd, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 16, 2, enum dpio_channel_mode, \ -+ cfg->channel_mode);\ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, cfg->num_priorities);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_RSP_IS_ENABLED(cmd, en) \ -+ MC_RSP_OP(cmd, 0, 0, 1, int, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_CMD_SET_IRQ(cmd, irq_index, irq_cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, irq_index);\ -+ MC_CMD_OP(cmd, 0, 32, 32, uint32_t, irq_cfg->val);\ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, irq_cfg->addr);\ -+ MC_CMD_OP(cmd, 2, 0, 32, int, irq_cfg->irq_num); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_CMD_GET_IRQ(cmd, irq_index) \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_RSP_GET_IRQ(cmd, type, irq_cfg) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, irq_cfg->val); \ -+ MC_RSP_OP(cmd, 1, 0, 64, uint64_t, irq_cfg->addr); \ -+ MC_RSP_OP(cmd, 2, 0, 32, int, irq_cfg->irq_num); \ -+ MC_RSP_OP(cmd, 2, 32, 32, int, type); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_CMD_SET_IRQ_ENABLE(cmd, irq_index, en) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, en); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_CMD_GET_IRQ_ENABLE(cmd, irq_index) \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_RSP_GET_IRQ_ENABLE(cmd, en) \ -+ MC_RSP_OP(cmd, 0, 0, 8, uint8_t, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_CMD_SET_IRQ_MASK(cmd, irq_index, mask) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, mask); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_CMD_GET_IRQ_MASK(cmd, irq_index) \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_RSP_GET_IRQ_MASK(cmd, mask) \ -+ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, mask) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_CMD_GET_IRQ_STATUS(cmd, irq_index, status) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, status);\ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_RSP_GET_IRQ_STATUS(cmd, status) \ -+ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, status) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_CMD_CLEAR_IRQ_STATUS(cmd, irq_index, status) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, status); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_RSP_GET_ATTR(cmd, attr) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 32, int, attr->id);\ -+ MC_RSP_OP(cmd, 0, 32, 16, uint16_t, attr->qbman_portal_id);\ -+ MC_RSP_OP(cmd, 0, 48, 8, uint8_t, attr->num_priorities);\ -+ MC_RSP_OP(cmd, 0, 56, 4, enum dpio_channel_mode, attr->channel_mode);\ -+ MC_RSP_OP(cmd, 1, 0, 64, uint64_t, attr->qbman_portal_ce_offset);\ -+ MC_RSP_OP(cmd, 2, 0, 64, uint64_t, attr->qbman_portal_ci_offset);\ -+ MC_RSP_OP(cmd, 3, 0, 16, uint16_t, attr->version.major);\ -+ MC_RSP_OP(cmd, 3, 16, 16, uint16_t, attr->version.minor);\ -+ MC_RSP_OP(cmd, 3, 32, 32, uint32_t, attr->qbman_version);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_CMD_SET_STASHING_DEST(cmd, sdest) \ -+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, sdest) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_RSP_GET_STASHING_DEST(cmd, sdest) \ -+ MC_RSP_OP(cmd, 0, 0, 8, uint8_t, sdest) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_CMD_ADD_STATIC_DEQUEUE_CHANNEL(cmd, dpcon_id) \ -+ MC_CMD_OP(cmd, 0, 0, 32, int, dpcon_id) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_RSP_ADD_STATIC_DEQUEUE_CHANNEL(cmd, channel_index) \ -+ MC_RSP_OP(cmd, 0, 0, 8, uint8_t, channel_index) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_CMD_REMOVE_STATIC_DEQUEUE_CHANNEL(cmd, dpcon_id) \ -+ MC_CMD_OP(cmd, 0, 0, 32, int, dpcon_id) -+#endif /* _FSL_DPIO_CMD_H */ -diff --git a/drivers/staging/fsl-mc/bus/dpio/fsl_qbman_base.h b/drivers/staging/fsl-mc/bus/dpio/fsl_qbman_base.h -new file mode 100644 -index 0000000..2874ff8 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpio/fsl_qbman_base.h -@@ -0,0 +1,123 @@ -+/* Copyright (C) 2014 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 _FSL_QBMAN_BASE_H -+#define _FSL_QBMAN_BASE_H -+ -+/** -+ * struct qbman_block_desc - qbman block descriptor structure -+ * -+ * Descriptor for a QBMan instance on the SoC. On partitions/targets that do not -+ * control this QBMan instance, these values may simply be place-holders. The -+ * idea is simply that we be able to distinguish between them, eg. so that SWP -+ * descriptors can identify which QBMan instance they belong to. -+ */ -+struct qbman_block_desc { -+ void *ccsr_reg_bar; /* CCSR register map */ -+ int irq_rerr; /* Recoverable error interrupt line */ -+ int irq_nrerr; /* Non-recoverable error interrupt line */ -+}; -+ -+/** -+ * struct qbman_swp_desc - qbman software portal descriptor structure -+ * -+ * Descriptor for a QBMan software portal, expressed in terms that make sense to -+ * the user context. Ie. on MC, this information is likely to be true-physical, -+ * and instantiated statically at compile-time. On GPP, this information is -+ * likely to be obtained via "discovery" over a partition's "layerscape bus" -+ * (ie. in response to a MC portal command), and would take into account any -+ * virtualisation of the GPP user's address space and/or interrupt numbering. -+ */ -+struct qbman_swp_desc { -+ const struct qbman_block_desc *block; /* The QBMan instance */ -+ void *cena_bar; /* Cache-enabled portal register map */ -+ void *cinh_bar; /* Cache-inhibited portal register map */ -+ uint32_t qman_version; -+}; -+ -+/* Driver object for managing a QBMan portal */ -+struct qbman_swp; -+ -+/** -+ * struct qbman_fd - basci structure for qbman frame descriptor -+ * -+ * Place-holder for FDs, we represent it via the simplest form that we need for -+ * now. Different overlays may be needed to support different options, etc. (It -+ * is impractical to define One True Struct, because the resulting encoding -+ * routines (lots of read-modify-writes) would be worst-case performance whether -+ * or not circumstances required them.) -+ * -+ * Note, as with all data-structures exchanged between software and hardware (be -+ * they located in the portal register map or DMA'd to and from main-memory), -+ * the driver ensures that the caller of the driver API sees the data-structures -+ * in host-endianness. "struct qbman_fd" is no exception. The 32-bit words -+ * contained within this structure are represented in host-endianness, even if -+ * hardware always treats them as little-endian. As such, if any of these fields -+ * are interpreted in a binary (rather than numerical) fashion by hardware -+ * blocks (eg. accelerators), then the user should be careful. We illustrate -+ * with an example; -+ * -+ * Suppose the desired behaviour of an accelerator is controlled by the "frc" -+ * field of the FDs that are sent to it. Suppose also that the behaviour desired -+ * by the user corresponds to an "frc" value which is expressed as the literal -+ * sequence of bytes 0xfe, 0xed, 0xab, and 0xba. So "frc" should be the 32-bit -+ * value in which 0xfe is the first byte and 0xba is the last byte, and as -+ * hardware is little-endian, this amounts to a 32-bit "value" of 0xbaabedfe. If -+ * the software is little-endian also, this can simply be achieved by setting -+ * frc=0xbaabedfe. On the other hand, if software is big-endian, it should set -+ * frc=0xfeedabba! The best away of avoiding trouble with this sort of thing is -+ * to treat the 32-bit words as numerical values, in which the offset of a field -+ * from the beginning of the first byte (as required or generated by hardware) -+ * is numerically encoded by a left-shift (ie. by raising the field to a -+ * corresponding power of 2). Ie. in the current example, software could set -+ * "frc" in the following way, and it would work correctly on both little-endian -+ * and big-endian operation; -+ * fd.frc = (0xfe << 0) | (0xed << 8) | (0xab << 16) | (0xba << 24); -+ */ -+struct qbman_fd { -+ union { -+ uint32_t words[8]; -+ struct qbman_fd_simple { -+ uint32_t addr_lo; -+ uint32_t addr_hi; -+ uint32_t len; -+ /* offset in the MS 16 bits, BPID in the LS 16 bits */ -+ uint32_t bpid_offset; -+ uint32_t frc; /* frame context */ -+ /* "err", "va", "cbmt", "asal", [...] */ -+ uint32_t ctrl; -+ /* flow context */ -+ uint32_t flc_lo; -+ uint32_t flc_hi; -+ } simple; -+ }; -+}; -+ -+#endif /* !_FSL_QBMAN_BASE_H */ -diff --git a/drivers/staging/fsl-mc/bus/dpio/fsl_qbman_portal.h b/drivers/staging/fsl-mc/bus/dpio/fsl_qbman_portal.h -new file mode 100644 -index 0000000..c9e543e ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpio/fsl_qbman_portal.h -@@ -0,0 +1,753 @@ -+/* Copyright (C) 2014 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 _FSL_QBMAN_PORTAL_H -+#define _FSL_QBMAN_PORTAL_H -+ -+#include "fsl_qbman_base.h" -+ -+/** -+ * qbman_swp_init() - Create a functional object representing the given -+ * QBMan portal descriptor. -+ * @d: the given qbman swp descriptor -+ * -+ * Return qbman_swp portal object for success, NULL if the object cannot -+ * be created. -+ */ -+struct qbman_swp *qbman_swp_init(const struct qbman_swp_desc *d); -+/** -+ * qbman_swp_finish() - Create and destroy a functional object representing -+ * the given QBMan portal descriptor. -+ * @p: the qbman_swp object to be destroyed. -+ * -+ */ -+void qbman_swp_finish(struct qbman_swp *p); -+ -+/** -+ * qbman_swp_get_desc() - Get the descriptor of the given portal object. -+ * @p: the given portal object. -+ * -+ * Return the descriptor for this portal. -+ */ -+const struct qbman_swp_desc *qbman_swp_get_desc(struct qbman_swp *p); -+ -+ /**************/ -+ /* Interrupts */ -+ /**************/ -+ -+/* See the QBMan driver API documentation for details on the interrupt -+ * mechanisms. */ -+#define QBMAN_SWP_INTERRUPT_EQRI ((uint32_t)0x00000001) -+#define QBMAN_SWP_INTERRUPT_EQDI ((uint32_t)0x00000002) -+#define QBMAN_SWP_INTERRUPT_DQRI ((uint32_t)0x00000004) -+#define QBMAN_SWP_INTERRUPT_RCRI ((uint32_t)0x00000008) -+#define QBMAN_SWP_INTERRUPT_RCDI ((uint32_t)0x00000010) -+#define QBMAN_SWP_INTERRUPT_VDCI ((uint32_t)0x00000020) -+ -+/** -+ * qbman_swp_interrupt_get_vanish() -+ * qbman_swp_interrupt_set_vanish() - Get/Set the data in software portal -+ * interrupt status disable register. -+ * @p: the given software portal object. -+ * @mask: The mask to set in SWP_IDSR register. -+ * -+ * Return the settings in SWP_ISDR register for Get function. -+ */ -+uint32_t qbman_swp_interrupt_get_vanish(struct qbman_swp *p); -+void qbman_swp_interrupt_set_vanish(struct qbman_swp *p, uint32_t mask); -+ -+/** -+ * qbman_swp_interrupt_read_status() -+ * qbman_swp_interrupt_clear_status() - Get/Set the data in software portal -+ * interrupt status register. -+ * @p: the given software portal object. -+ * @mask: The mask to set in SWP_ISR register. -+ * -+ * Return the settings in SWP_ISR register for Get function. -+ * -+ */ -+uint32_t qbman_swp_interrupt_read_status(struct qbman_swp *p); -+void qbman_swp_interrupt_clear_status(struct qbman_swp *p, uint32_t mask); -+ -+/** -+ * qbman_swp_interrupt_get_trigger() -+ * qbman_swp_interrupt_set_trigger() - Get/Set the data in software portal -+ * interrupt enable register. -+ * @p: the given software portal object. -+ * @mask: The mask to set in SWP_IER register. -+ * -+ * Return the settings in SWP_IER register for Get function. -+ */ -+uint32_t qbman_swp_interrupt_get_trigger(struct qbman_swp *p); -+void qbman_swp_interrupt_set_trigger(struct qbman_swp *p, uint32_t mask); -+ -+/** -+ * qbman_swp_interrupt_get_inhibit() -+ * qbman_swp_interrupt_set_inhibit() - Set/Set the data in software portal -+ * interrupt inhibit register. -+ * @p: the given software portal object. -+ * @mask: The mask to set in SWP_IIR register. -+ * -+ * Return the settings in SWP_IIR register for Get function. -+ */ -+int qbman_swp_interrupt_get_inhibit(struct qbman_swp *p); -+void qbman_swp_interrupt_set_inhibit(struct qbman_swp *p, int inhibit); -+ -+ /************/ -+ /* Dequeues */ -+ /************/ -+ -+/* See the QBMan driver API documentation for details on the enqueue -+ * mechanisms. NB: the use of a 'dpaa2_' prefix for this type is because it is -+ * primarily used by the "DPIO" layer that sits above (and hides) the QBMan -+ * driver. The structure is defined in the DPIO interface, but to avoid circular -+ * dependencies we just pre/re-declare it here opaquely. */ -+struct dpaa2_dq; -+ -+/* ------------------- */ -+/* Push-mode dequeuing */ -+/* ------------------- */ -+ -+/** -+ * qbman_swp_push_get() - Get the push dequeue setup. -+ * @p: the software portal object. -+ * @channel_idx: the channel index to query. -+ * @enabled: returned boolean to show whether the push dequeue is enabled for -+ * the given channel. -+ */ -+void qbman_swp_push_get(struct qbman_swp *, uint8_t channel_idx, int *enabled); -+/** -+ * qbman_swp_push_set() - Enable or disable push dequeue. -+ * @p: the software portal object. -+ * @channel_idx: the channel index.. -+ * @enable: enable or disable push dequeue. -+ * -+ * The user of a portal can enable and disable push-mode dequeuing of up to 16 -+ * channels independently. It does not specify this toggling by channel IDs, but -+ * rather by specifying the index (from 0 to 15) that has been mapped to the -+ * desired channel. -+ */ -+void qbman_swp_push_set(struct qbman_swp *, uint8_t channel_idx, int enable); -+ -+/* ------------------- */ -+/* Pull-mode dequeuing */ -+/* ------------------- */ -+ -+/** -+ * struct qbman_pull_desc - the structure for pull dequeue descriptor -+ */ -+struct qbman_pull_desc { -+ uint32_t dont_manipulate_directly[6]; -+}; -+ -+enum qbman_pull_type_e { -+ /* dequeue with priority precedence, respect intra-class scheduling */ -+ qbman_pull_type_prio = 1, -+ /* dequeue with active FQ precedence, respect ICS */ -+ qbman_pull_type_active, -+ /* dequeue with active FQ precedence, no ICS */ -+ qbman_pull_type_active_noics -+}; -+ -+/** -+ * qbman_pull_desc_clear() - Clear the contents of a descriptor to -+ * default/starting state. -+ * @d: the pull dequeue descriptor to be cleared. -+ */ -+void qbman_pull_desc_clear(struct qbman_pull_desc *d); -+ -+/** -+ * qbman_pull_desc_set_storage()- Set the pull dequeue storage -+ * @d: the pull dequeue descriptor to be set. -+ * @storage: the pointer of the memory to store the dequeue result. -+ * @storage_phys: the physical address of the storage memory. -+ * @stash: to indicate whether write allocate is enabled. -+ * -+ * If not called, or if called with 'storage' as NULL, the result pull dequeues -+ * will produce results to DQRR. If 'storage' is non-NULL, then results are -+ * produced to the given memory location (using the physical/DMA address which -+ * the caller provides in 'storage_phys'), and 'stash' controls whether or not -+ * those writes to main-memory express a cache-warming attribute. -+ */ -+void qbman_pull_desc_set_storage(struct qbman_pull_desc *d, -+ struct dpaa2_dq *storage, -+ dma_addr_t storage_phys, -+ int stash); -+/** -+ * qbman_pull_desc_set_numframes() - Set the number of frames to be dequeued. -+ * @d: the pull dequeue descriptor to be set. -+ * @numframes: number of frames to be set, must be between 1 and 16, inclusive. -+ */ -+void qbman_pull_desc_set_numframes(struct qbman_pull_desc *, uint8_t numframes); -+ -+/** -+ * qbman_pull_desc_set_fq() - Set fqid from which the dequeue command dequeues. -+ * @fqid: the frame queue index of the given FQ. -+ * -+ * qbman_pull_desc_set_wq() - Set wqid from which the dequeue command dequeues. -+ * @wqid: composed of channel id and wqid within the channel. -+ * @dct: the dequeue command type. -+ * -+ * qbman_pull_desc_set_channel() - Set channelid from which the dequeue command -+ * dequeues. -+ * @chid: the channel id to be dequeued. -+ * @dct: the dequeue command type. -+ * -+ * Exactly one of the following descriptor "actions" should be set. (Calling any -+ * one of these will replace the effect of any prior call to one of these.) -+ * - pull dequeue from the given frame queue (FQ) -+ * - pull dequeue from any FQ in the given work queue (WQ) -+ * - pull dequeue from any FQ in any WQ in the given channel -+ */ -+void qbman_pull_desc_set_fq(struct qbman_pull_desc *, uint32_t fqid); -+void qbman_pull_desc_set_wq(struct qbman_pull_desc *, uint32_t wqid, -+ enum qbman_pull_type_e dct); -+void qbman_pull_desc_set_channel(struct qbman_pull_desc *, uint32_t chid, -+ enum qbman_pull_type_e dct); -+ -+/** -+ * qbman_swp_pull() - Issue the pull dequeue command -+ * @s: the software portal object. -+ * @d: the software portal descriptor which has been configured with -+ * the set of qbman_pull_desc_set_*() calls. -+ * -+ * Return 0 for success, and -EBUSY if the software portal is not ready -+ * to do pull dequeue. -+ */ -+int qbman_swp_pull(struct qbman_swp *, struct qbman_pull_desc *d); -+ -+/* -------------------------------- */ -+/* Polling DQRR for dequeue results */ -+/* -------------------------------- */ -+ -+/** -+ * qbman_swp_dqrr_next() - Get an valid DQRR entry. -+ * @s: the software portal object. -+ * -+ * Return NULL if there are no unconsumed DQRR entries. Return a DQRR entry -+ * only once, so repeated calls can return a sequence of DQRR entries, without -+ * requiring they be consumed immediately or in any particular order. -+ */ -+const struct dpaa2_dq *qbman_swp_dqrr_next(struct qbman_swp *s); -+ -+/** -+ * qbman_swp_dqrr_consume() - Consume DQRR entries previously returned from -+ * qbman_swp_dqrr_next(). -+ * @s: the software portal object. -+ * @dq: the DQRR entry to be consumed. -+ */ -+void qbman_swp_dqrr_consume(struct qbman_swp *s, const struct dpaa2_dq *dq); -+ -+/* ------------------------------------------------- */ -+/* Polling user-provided storage for dequeue results */ -+/* ------------------------------------------------- */ -+/** -+ * qbman_result_has_new_result() - Check and get the dequeue response from the -+ * dq storage memory set in pull dequeue command -+ * @s: the software portal object. -+ * @dq: the dequeue result read from the memory. -+ * -+ * Only used for user-provided storage of dequeue results, not DQRR. For -+ * efficiency purposes, the driver will perform any required endianness -+ * conversion to ensure that the user's dequeue result storage is in host-endian -+ * format (whether or not that is the same as the little-endian format that -+ * hardware DMA'd to the user's storage). As such, once the user has called -+ * qbman_result_has_new_result() and been returned a valid dequeue result, -+ * they should not call it again on the same memory location (except of course -+ * if another dequeue command has been executed to produce a new result to that -+ * location). -+ * -+ * Return 1 for getting a valid dequeue result, or 0 for not getting a valid -+ * dequeue result. -+ */ -+int qbman_result_has_new_result(struct qbman_swp *, -+ const struct dpaa2_dq *); -+ -+/* -------------------------------------------------------- */ -+/* Parsing dequeue entries (DQRR and user-provided storage) */ -+/* -------------------------------------------------------- */ -+ -+/** -+ * qbman_result_is_DQ() - check the dequeue result is a dequeue response or not -+ * @dq: the dequeue result to be checked. -+ * -+ * DQRR entries may contain non-dequeue results, ie. notifications -+ */ -+int qbman_result_is_DQ(const struct dpaa2_dq *); -+ -+/** -+ * qbman_result_is_SCN() - Check the dequeue result is notification or not -+ * @dq: the dequeue result to be checked. -+ * -+ * All the non-dequeue results (FQDAN/CDAN/CSCN/...) are "state change -+ * notifications" of one type or another. Some APIs apply to all of them, of the -+ * form qbman_result_SCN_***(). -+ */ -+static inline int qbman_result_is_SCN(const struct dpaa2_dq *dq) -+{ -+ return !qbman_result_is_DQ(dq); -+} -+ -+/** -+ * Recognise different notification types, only required if the user allows for -+ * these to occur, and cares about them when they do. -+ */ -+int qbman_result_is_FQDAN(const struct dpaa2_dq *); -+ /* FQ Data Availability */ -+int qbman_result_is_CDAN(const struct dpaa2_dq *); -+ /* Channel Data Availability */ -+int qbman_result_is_CSCN(const struct dpaa2_dq *); -+ /* Congestion State Change */ -+int qbman_result_is_BPSCN(const struct dpaa2_dq *); -+ /* Buffer Pool State Change */ -+int qbman_result_is_CGCU(const struct dpaa2_dq *); -+ /* Congestion Group Count Update */ -+/* Frame queue state change notifications; (FQDAN in theory counts too as it -+ * leaves a FQ parked, but it is primarily a data availability notification) */ -+int qbman_result_is_FQRN(const struct dpaa2_dq *); /* Retirement */ -+int qbman_result_is_FQRNI(const struct dpaa2_dq *); -+ /* Retirement Immediate */ -+int qbman_result_is_FQPN(const struct dpaa2_dq *); /* Park */ -+ -+/* NB: for parsing dequeue results (when "is_DQ" is TRUE), use the higher-layer -+ * dpaa2_dq_*() functions. */ -+ -+/* State-change notifications (FQDAN/CDAN/CSCN/...). */ -+/** -+ * qbman_result_SCN_state() - Get the state field in State-change notification -+ */ -+uint8_t qbman_result_SCN_state(const struct dpaa2_dq *); -+/** -+ * qbman_result_SCN_rid() - Get the resource id in State-change notification -+ */ -+uint32_t qbman_result_SCN_rid(const struct dpaa2_dq *); -+/** -+ * qbman_result_SCN_ctx() - Get the context data in State-change notification -+ */ -+uint64_t qbman_result_SCN_ctx(const struct dpaa2_dq *); -+/** -+ * qbman_result_SCN_state_in_mem() - Get the state field in State-change -+ * notification which is written to memory instead of DQRR. -+ */ -+uint8_t qbman_result_SCN_state_in_mem(const struct dpaa2_dq *); -+/** -+ * qbman_result_SCN_rid_in_mem() - Get the resource id in State-change -+ * notification which is written to memory instead of DQRR. -+ */ -+uint32_t qbman_result_SCN_rid_in_mem(const struct dpaa2_dq *); -+ -+/* Type-specific "resource IDs". Mainly for illustration purposes, though it -+ * also gives the appropriate type widths. */ -+#define qbman_result_FQDAN_fqid(dq) qbman_result_SCN_rid(dq) -+#define qbman_result_FQRN_fqid(dq) qbman_result_SCN_rid(dq) -+#define qbman_result_FQRNI_fqid(dq) qbman_result_SCN_rid(dq) -+#define qbman_result_FQPN_fqid(dq) qbman_result_SCN_rid(dq) -+#define qbman_result_CDAN_cid(dq) ((uint16_t)qbman_result_SCN_rid(dq)) -+#define qbman_result_CSCN_cgid(dq) ((uint16_t)qbman_result_SCN_rid(dq)) -+ -+/** -+ * qbman_result_bpscn_bpid() - Get the bpid from BPSCN -+ * -+ * Return the buffer pool id. -+ */ -+uint16_t qbman_result_bpscn_bpid(const struct dpaa2_dq *); -+/** -+ * qbman_result_bpscn_has_free_bufs() - Check whether there are free -+ * buffers in the pool from BPSCN. -+ * -+ * Return the number of free buffers. -+ */ -+int qbman_result_bpscn_has_free_bufs(const struct dpaa2_dq *); -+/** -+ * qbman_result_bpscn_is_depleted() - Check BPSCN to see whether the -+ * buffer pool is depleted. -+ * -+ * Return the status of buffer pool depletion. -+ */ -+int qbman_result_bpscn_is_depleted(const struct dpaa2_dq *); -+/** -+ * qbman_result_bpscn_is_surplus() - Check BPSCN to see whether the buffer -+ * pool is surplus or not. -+ * -+ * Return the status of buffer pool surplus. -+ */ -+int qbman_result_bpscn_is_surplus(const struct dpaa2_dq *); -+/** -+ * qbman_result_bpscn_ctx() - Get the BPSCN CTX from BPSCN message -+ * -+ * Return the BPSCN context. -+ */ -+uint64_t qbman_result_bpscn_ctx(const struct dpaa2_dq *); -+ -+/* Parsing CGCU */ -+/** -+ * qbman_result_cgcu_cgid() - Check CGCU resouce id, i.e. cgid -+ * -+ * Return the CGCU resource id. -+ */ -+uint16_t qbman_result_cgcu_cgid(const struct dpaa2_dq *); -+/** -+ * qbman_result_cgcu_icnt() - Get the I_CNT from CGCU -+ * -+ * Return instantaneous count in the CGCU notification. -+ */ -+uint64_t qbman_result_cgcu_icnt(const struct dpaa2_dq *); -+ -+ /************/ -+ /* Enqueues */ -+ /************/ -+/** -+ * struct qbman_eq_desc - structure of enqueue descriptor -+ */ -+struct qbman_eq_desc { -+ uint32_t dont_manipulate_directly[8]; -+}; -+ -+/** -+ * struct qbman_eq_response - structure of enqueue response -+ */ -+struct qbman_eq_response { -+ uint32_t dont_manipulate_directly[16]; -+}; -+ -+/** -+ * qbman_eq_desc_clear() - Clear the contents of a descriptor to -+ * default/starting state. -+ */ -+void qbman_eq_desc_clear(struct qbman_eq_desc *); -+ -+/* Exactly one of the following descriptor "actions" should be set. (Calling -+ * any one of these will replace the effect of any prior call to one of these.) -+ * - enqueue without order-restoration -+ * - enqueue with order-restoration -+ * - fill a hole in the order-restoration sequence, without any enqueue -+ * - advance NESN (Next Expected Sequence Number), without any enqueue -+ * 'respond_success' indicates whether an enqueue response should be DMA'd -+ * after success (otherwise a response is DMA'd only after failure). -+ * 'incomplete' indicates that other fragments of the same 'seqnum' are yet to -+ * be enqueued. -+ */ -+/** -+ * qbman_eq_desc_set_no_orp() - Set enqueue descriptor without orp -+ * @d: the enqueue descriptor. -+ * @response_success: 1 = enqueue with response always; 0 = enqueue with -+ * rejections returned on a FQ. -+ */ -+void qbman_eq_desc_set_no_orp(struct qbman_eq_desc *d, int respond_success); -+ -+/** -+ * qbman_eq_desc_set_orp() - Set order-resotration in the enqueue descriptor -+ * @d: the enqueue descriptor. -+ * @response_success: 1 = enqueue with response always; 0 = enqueue with -+ * rejections returned on a FQ. -+ * @opr_id: the order point record id. -+ * @seqnum: the order restoration sequence number. -+ * @incomplete: indiates whether this is the last fragments using the same -+ * sequeue number. -+ */ -+void qbman_eq_desc_set_orp(struct qbman_eq_desc *d, int respond_success, -+ uint32_t opr_id, uint32_t seqnum, int incomplete); -+ -+/** -+ * qbman_eq_desc_set_orp_hole() - fill a hole in the order-restoration sequence -+ * without any enqueue -+ * @d: the enqueue descriptor. -+ * @opr_id: the order point record id. -+ * @seqnum: the order restoration sequence number. -+ */ -+void qbman_eq_desc_set_orp_hole(struct qbman_eq_desc *d, uint32_t opr_id, -+ uint32_t seqnum); -+ -+/** -+ * qbman_eq_desc_set_orp_nesn() - advance NESN (Next Expected Sequence Number) -+ * without any enqueue -+ * @d: the enqueue descriptor. -+ * @opr_id: the order point record id. -+ * @seqnum: the order restoration sequence number. -+ */ -+void qbman_eq_desc_set_orp_nesn(struct qbman_eq_desc *d, uint32_t opr_id, -+ uint32_t seqnum); -+ -+/** -+ * qbman_eq_desc_set_response() - Set the enqueue response info. -+ * @d: the enqueue descriptor -+ * @storage_phys: the physical address of the enqueue response in memory. -+ * @stash: indicate that the write allocation enabled or not. -+ * -+ * In the case where an enqueue response is DMA'd, this determines where that -+ * response should go. (The physical/DMA address is given for hardware's -+ * benefit, but software should interpret it as a "struct qbman_eq_response" -+ * data structure.) 'stash' controls whether or not the write to main-memory -+ * expresses a cache-warming attribute. -+ */ -+void qbman_eq_desc_set_response(struct qbman_eq_desc *d, -+ dma_addr_t storage_phys, -+ int stash); -+/** -+ * qbman_eq_desc_set_token() - Set token for the enqueue command -+ * @d: the enqueue descriptor -+ * @token: the token to be set. -+ * -+ * token is the value that shows up in an enqueue response that can be used to -+ * detect when the results have been published. The easiest technique is to zero -+ * result "storage" before issuing an enqueue, and use any non-zero 'token' -+ * value. -+ */ -+void qbman_eq_desc_set_token(struct qbman_eq_desc *d, uint8_t token); -+ -+/** -+ * qbman_eq_desc_set_fq() -+ * qbman_eq_desc_set_qd() - Set eithe FQ or Queuing Destination for the enqueue -+ * command. -+ * @d: the enqueue descriptor -+ * @fqid: the id of the frame queue to be enqueued. -+ * @qdid: the id of the queuing destination to be enqueued. -+ * @qd_bin: the queuing destination bin -+ * @qd_prio: the queuing destination priority. -+ * -+ * Exactly one of the following descriptor "targets" should be set. (Calling any -+ * one of these will replace the effect of any prior call to one of these.) -+ * - enqueue to a frame queue -+ * - enqueue to a queuing destination -+ * Note, that none of these will have any affect if the "action" type has been -+ * set to "orp_hole" or "orp_nesn". -+ */ -+void qbman_eq_desc_set_fq(struct qbman_eq_desc *, uint32_t fqid); -+void qbman_eq_desc_set_qd(struct qbman_eq_desc *, uint32_t qdid, -+ uint32_t qd_bin, uint32_t qd_prio); -+ -+/** -+ * qbman_eq_desc_set_eqdi() - enable/disable EQDI interrupt -+ * @d: the enqueue descriptor -+ * @enable: boolean to enable/disable EQDI -+ * -+ * Determines whether or not the portal's EQDI interrupt source should be -+ * asserted after the enqueue command is completed. -+ */ -+void qbman_eq_desc_set_eqdi(struct qbman_eq_desc *, int enable); -+ -+/** -+ * qbman_eq_desc_set_dca() - Set DCA mode in the enqueue command. -+ * @d: the enqueue descriptor. -+ * @enable: enabled/disable DCA mode. -+ * @dqrr_idx: DCAP_CI, the DCAP consumer index. -+ * @park: determine the whether park the FQ or not -+ * -+ * Determines whether or not a portal DQRR entry should be consumed once the -+ * enqueue command is completed. (And if so, and the DQRR entry corresponds -+ * to a held-active (order-preserving) FQ, whether the FQ should be parked -+ * instead of being rescheduled.) -+ */ -+void qbman_eq_desc_set_dca(struct qbman_eq_desc *, int enable, -+ uint32_t dqrr_idx, int park); -+ -+/** -+ * qbman_swp_enqueue() - Issue an enqueue command. -+ * @s: the software portal used for enqueue. -+ * @d: the enqueue descriptor. -+ * @fd: the frame descriptor to be enqueued. -+ * -+ * Please note that 'fd' should only be NULL if the "action" of the -+ * descriptor is "orp_hole" or "orp_nesn". -+ * -+ * Return 0 for successful enqueue, -EBUSY if the EQCR is not ready. -+ */ -+int qbman_swp_enqueue(struct qbman_swp *, const struct qbman_eq_desc *, -+ const struct qbman_fd *fd); -+ -+/** -+ * qbman_swp_enqueue_thresh() - Set the threshold for EQRI interrupt. -+ * -+ * An EQRI interrupt can be generated when the fill-level of EQCR falls below -+ * the 'thresh' value set here. Setting thresh==0 (the default) disables. -+ */ -+int qbman_swp_enqueue_thresh(struct qbman_swp *, unsigned int thresh); -+ -+ /*******************/ -+ /* Buffer releases */ -+ /*******************/ -+/** -+ * struct qbman_release_desc - The structure for buffer release descriptor -+ */ -+struct qbman_release_desc { -+ uint32_t dont_manipulate_directly[1]; -+}; -+ -+/** -+ * qbman_release_desc_clear() - Clear the contents of a descriptor to -+ * default/starting state. -+ */ -+void qbman_release_desc_clear(struct qbman_release_desc *); -+ -+/** -+ * qbman_release_desc_set_bpid() - Set the ID of the buffer pool to release to -+ */ -+void qbman_release_desc_set_bpid(struct qbman_release_desc *, uint32_t bpid); -+ -+/** -+ * qbman_release_desc_set_rcdi() - Determines whether or not the portal's RCDI -+ * interrupt source should be asserted after the release command is completed. -+ */ -+void qbman_release_desc_set_rcdi(struct qbman_release_desc *, int enable); -+ -+/** -+ * qbman_swp_release() - Issue a buffer release command. -+ * @s: the software portal object. -+ * @d: the release descriptor. -+ * @buffers: a pointer pointing to the buffer address to be released. -+ * @num_buffers: number of buffers to be released, must be less than 8. -+ * -+ * Return 0 for success, -EBUSY if the release command ring is not ready. -+ */ -+int qbman_swp_release(struct qbman_swp *s, const struct qbman_release_desc *d, -+ const uint64_t *buffers, unsigned int num_buffers); -+ -+ /*******************/ -+ /* Buffer acquires */ -+ /*******************/ -+ -+/** -+ * qbman_swp_acquire() - Issue a buffer acquire command. -+ * @s: the software portal object. -+ * @bpid: the buffer pool index. -+ * @buffers: a pointer pointing to the acquired buffer address|es. -+ * @num_buffers: number of buffers to be acquired, must be less than 8. -+ * -+ * Return 0 for success, or negative error code if the acquire command -+ * fails. -+ */ -+int qbman_swp_acquire(struct qbman_swp *, uint32_t bpid, uint64_t *buffers, -+ unsigned int num_buffers); -+ -+ /*****************/ -+ /* FQ management */ -+ /*****************/ -+ -+/** -+ * qbman_swp_fq_schedule() - Move the fq to the scheduled state. -+ * @s: the software portal object. -+ * @fqid: the index of frame queue to be scheduled. -+ * -+ * There are a couple of different ways that a FQ can end up parked state, -+ * This schedules it. -+ * -+ * Return 0 for success, or negative error code for failure. -+ */ -+int qbman_swp_fq_schedule(struct qbman_swp *s, uint32_t fqid); -+ -+/** -+ * qbman_swp_fq_force() - Force the FQ to fully scheduled state. -+ * @s: the software portal object. -+ * @fqid: the index of frame queue to be forced. -+ * -+ * Force eligible will force a tentatively-scheduled FQ to be fully-scheduled -+ * and thus be available for selection by any channel-dequeuing behaviour (push -+ * or pull). If the FQ is subsequently "dequeued" from the channel and is still -+ * empty at the time this happens, the resulting dq_entry will have no FD. -+ * (qbman_result_DQ_fd() will return NULL.) -+ * -+ * Return 0 for success, or negative error code for failure. -+ */ -+int qbman_swp_fq_force(struct qbman_swp *s, uint32_t fqid); -+ -+/** -+ * qbman_swp_fq_xon() -+ * qbman_swp_fq_xoff() - XON/XOFF the frame queue. -+ * @s: the software portal object. -+ * @fqid: the index of frame queue. -+ * -+ * These functions change the FQ flow-control stuff between XON/XOFF. (The -+ * default is XON.) This setting doesn't affect enqueues to the FQ, just -+ * dequeues. XOFF FQs will remain in the tenatively-scheduled state, even when -+ * non-empty, meaning they won't be selected for scheduled dequeuing. If a FQ is -+ * changed to XOFF after it had already become truly-scheduled to a channel, and -+ * a pull dequeue of that channel occurs that selects that FQ for dequeuing, -+ * then the resulting dq_entry will have no FD. (qbman_result_DQ_fd() will -+ * return NULL.) -+ * -+ * Return 0 for success, or negative error code for failure. -+ */ -+int qbman_swp_fq_xon(struct qbman_swp *s, uint32_t fqid); -+int qbman_swp_fq_xoff(struct qbman_swp *s, uint32_t fqid); -+ -+ /**********************/ -+ /* Channel management */ -+ /**********************/ -+ -+/* If the user has been allocated a channel object that is going to generate -+ * CDANs to another channel, then these functions will be necessary. -+ * CDAN-enabled channels only generate a single CDAN notification, after which -+ * it they need to be reenabled before they'll generate another. (The idea is -+ * that pull dequeuing will occur in reaction to the CDAN, followed by a -+ * reenable step.) Each function generates a distinct command to hardware, so a -+ * combination function is provided if the user wishes to modify the "context" -+ * (which shows up in each CDAN message) each time they reenable, as a single -+ * command to hardware. */ -+/** -+ * qbman_swp_CDAN_set_context() - Set CDAN context -+ * @s: the software portal object. -+ * @channelid: the channel index. -+ * @ctx: the context to be set in CDAN. -+ * -+ * Return 0 for success, or negative error code for failure. -+ */ -+int qbman_swp_CDAN_set_context(struct qbman_swp *, uint16_t channelid, -+ uint64_t ctx); -+ -+/** -+ * qbman_swp_CDAN_enable() - Enable CDAN for the channel. -+ * @s: the software portal object. -+ * @channelid: the index of the channel to generate CDAN. -+ * -+ * Return 0 for success, or negative error code for failure. -+ */ -+int qbman_swp_CDAN_enable(struct qbman_swp *, uint16_t channelid); -+ -+/** -+ * qbman_swp_CDAN_disable() - disable CDAN for the channel. -+ * @s: the software portal object. -+ * @channelid: the index of the channel to generate CDAN. -+ * -+ * Return 0 for success, or negative error code for failure. -+ */ -+int qbman_swp_CDAN_disable(struct qbman_swp *, uint16_t channelid); -+ -+/** -+ * qbman_swp_CDAN_set_context_enable() - Set CDAN contest and enable CDAN -+ * @s: the software portal object. -+ * @channelid: the index of the channel to generate CDAN. -+ * @ctx: the context set in CDAN. -+ * -+ * Return 0 for success, or negative error code for failure. -+ */ -+int qbman_swp_CDAN_set_context_enable(struct qbman_swp *, uint16_t channelid, -+ uint64_t ctx); -+ -+#endif /* !_FSL_QBMAN_PORTAL_H */ -diff --git a/drivers/staging/fsl-mc/bus/dpio/qbman_debug.c b/drivers/staging/fsl-mc/bus/dpio/qbman_debug.c -new file mode 100644 -index 0000000..12e33d3 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpio/qbman_debug.c -@@ -0,0 +1,846 @@ -+/* Copyright (C) 2015 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 "qbman_portal.h" -+#include "qbman_debug.h" -+#include "fsl_qbman_portal.h" -+ -+/* QBMan portal management command code */ -+#define QBMAN_BP_QUERY 0x32 -+#define QBMAN_FQ_QUERY 0x44 -+#define QBMAN_FQ_QUERY_NP 0x45 -+#define QBMAN_CGR_QUERY 0x51 -+#define QBMAN_WRED_QUERY 0x54 -+#define QBMAN_CGR_STAT_QUERY 0x55 -+#define QBMAN_CGR_STAT_QUERY_CLR 0x56 -+ -+enum qbman_attr_usage_e { -+ qbman_attr_usage_fq, -+ qbman_attr_usage_bpool, -+ qbman_attr_usage_cgr, -+}; -+ -+struct int_qbman_attr { -+ uint32_t words[32]; -+ enum qbman_attr_usage_e usage; -+}; -+ -+#define attr_type_set(a, e) \ -+{ \ -+ struct qbman_attr *__attr = a; \ -+ enum qbman_attr_usage_e __usage = e; \ -+ ((struct int_qbman_attr *)__attr)->usage = __usage; \ -+} -+ -+#define ATTR32(d) (&(d)->dont_manipulate_directly[0]) -+#define ATTR32_1(d) (&(d)->dont_manipulate_directly[16]) -+ -+static struct qb_attr_code code_bp_bpid = QB_CODE(0, 16, 16); -+static struct qb_attr_code code_bp_bdi = QB_CODE(1, 16, 1); -+static struct qb_attr_code code_bp_va = QB_CODE(1, 17, 1); -+static struct qb_attr_code code_bp_wae = QB_CODE(1, 18, 1); -+static struct qb_attr_code code_bp_swdet = QB_CODE(4, 0, 16); -+static struct qb_attr_code code_bp_swdxt = QB_CODE(4, 16, 16); -+static struct qb_attr_code code_bp_hwdet = QB_CODE(5, 0, 16); -+static struct qb_attr_code code_bp_hwdxt = QB_CODE(5, 16, 16); -+static struct qb_attr_code code_bp_swset = QB_CODE(6, 0, 16); -+static struct qb_attr_code code_bp_swsxt = QB_CODE(6, 16, 16); -+static struct qb_attr_code code_bp_vbpid = QB_CODE(7, 0, 14); -+static struct qb_attr_code code_bp_icid = QB_CODE(7, 16, 15); -+static struct qb_attr_code code_bp_pl = QB_CODE(7, 31, 1); -+static struct qb_attr_code code_bp_bpscn_addr_lo = QB_CODE(8, 0, 32); -+static struct qb_attr_code code_bp_bpscn_addr_hi = QB_CODE(9, 0, 32); -+static struct qb_attr_code code_bp_bpscn_ctx_lo = QB_CODE(10, 0, 32); -+static struct qb_attr_code code_bp_bpscn_ctx_hi = QB_CODE(11, 0, 32); -+static struct qb_attr_code code_bp_hw_targ = QB_CODE(12, 0, 16); -+static struct qb_attr_code code_bp_state = QB_CODE(1, 24, 3); -+static struct qb_attr_code code_bp_fill = QB_CODE(2, 0, 32); -+static struct qb_attr_code code_bp_hdptr = QB_CODE(3, 0, 32); -+static struct qb_attr_code code_bp_sdcnt = QB_CODE(13, 0, 8); -+static struct qb_attr_code code_bp_hdcnt = QB_CODE(13, 1, 8); -+static struct qb_attr_code code_bp_sscnt = QB_CODE(13, 2, 8); -+ -+void qbman_bp_attr_clear(struct qbman_attr *a) -+{ -+ memset(a, 0, sizeof(*a)); -+ attr_type_set(a, qbman_attr_usage_bpool); -+} -+ -+int qbman_bp_query(struct qbman_swp *s, uint32_t bpid, -+ struct qbman_attr *a) -+{ -+ uint32_t *p; -+ uint32_t verb, rslt; -+ uint32_t *attr = ATTR32(a); -+ -+ qbman_bp_attr_clear(a); -+ -+ /* Start the management command */ -+ p = qbman_swp_mc_start(s); -+ if (!p) -+ return -EBUSY; -+ -+ /* Encode the caller-provided attributes */ -+ qb_attr_code_encode(&code_bp_bpid, p, bpid); -+ -+ /* Complete the management command */ -+ p = qbman_swp_mc_complete(s, p, p[0] | QBMAN_BP_QUERY); -+ -+ /* Decode the outcome */ -+ verb = qb_attr_code_decode(&code_generic_verb, p); -+ rslt = qb_attr_code_decode(&code_generic_rslt, p); -+ BUG_ON(verb != QBMAN_BP_QUERY); -+ -+ /* Determine success or failure */ -+ if (unlikely(rslt != QBMAN_MC_RSLT_OK)) { -+ pr_err("Query of BPID 0x%x failed, code=0x%02x\n", bpid, rslt); -+ return -EIO; -+ } -+ -+ /* For the query, word[0] of the result contains only the -+ * verb/rslt fields, so skip word[0]. -+ */ -+ word_copy(&attr[1], &p[1], 15); -+ return 0; -+} -+ -+void qbman_bp_attr_get_bdi(struct qbman_attr *a, int *bdi, int *va, int *wae) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ *bdi = !!qb_attr_code_decode(&code_bp_bdi, p); -+ *va = !!qb_attr_code_decode(&code_bp_va, p); -+ *wae = !!qb_attr_code_decode(&code_bp_wae, p); -+} -+ -+static uint32_t qbman_bp_thresh_to_value(uint32_t val) -+{ -+ return (val & 0xff) << ((val & 0xf00) >> 8); -+} -+ -+void qbman_bp_attr_get_swdet(struct qbman_attr *a, uint32_t *swdet) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ *swdet = qbman_bp_thresh_to_value(qb_attr_code_decode(&code_bp_swdet, -+ p)); -+} -+void qbman_bp_attr_get_swdxt(struct qbman_attr *a, uint32_t *swdxt) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ *swdxt = qbman_bp_thresh_to_value(qb_attr_code_decode(&code_bp_swdxt, -+ p)); -+} -+void qbman_bp_attr_get_hwdet(struct qbman_attr *a, uint32_t *hwdet) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ *hwdet = qbman_bp_thresh_to_value(qb_attr_code_decode(&code_bp_hwdet, -+ p)); -+} -+void qbman_bp_attr_get_hwdxt(struct qbman_attr *a, uint32_t *hwdxt) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ *hwdxt = qbman_bp_thresh_to_value(qb_attr_code_decode(&code_bp_hwdxt, -+ p)); -+} -+ -+void qbman_bp_attr_get_swset(struct qbman_attr *a, uint32_t *swset) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ *swset = qbman_bp_thresh_to_value(qb_attr_code_decode(&code_bp_swset, -+ p)); -+} -+ -+void qbman_bp_attr_get_swsxt(struct qbman_attr *a, uint32_t *swsxt) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ *swsxt = qbman_bp_thresh_to_value(qb_attr_code_decode(&code_bp_swsxt, -+ p)); -+} -+ -+void qbman_bp_attr_get_vbpid(struct qbman_attr *a, uint32_t *vbpid) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ *vbpid = qb_attr_code_decode(&code_bp_vbpid, p); -+} -+ -+void qbman_bp_attr_get_icid(struct qbman_attr *a, uint32_t *icid, int *pl) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ *icid = qb_attr_code_decode(&code_bp_icid, p); -+ *pl = !!qb_attr_code_decode(&code_bp_pl, p); -+} -+ -+void qbman_bp_attr_get_bpscn_addr(struct qbman_attr *a, uint64_t *bpscn_addr) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ *bpscn_addr = ((uint64_t)qb_attr_code_decode(&code_bp_bpscn_addr_hi, -+ p) << 32) | -+ (uint64_t)qb_attr_code_decode(&code_bp_bpscn_addr_lo, -+ p); -+} -+ -+void qbman_bp_attr_get_bpscn_ctx(struct qbman_attr *a, uint64_t *bpscn_ctx) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ *bpscn_ctx = ((uint64_t)qb_attr_code_decode(&code_bp_bpscn_ctx_hi, p) -+ << 32) | -+ (uint64_t)qb_attr_code_decode(&code_bp_bpscn_ctx_lo, -+ p); -+} -+ -+void qbman_bp_attr_get_hw_targ(struct qbman_attr *a, uint32_t *hw_targ) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ *hw_targ = qb_attr_code_decode(&code_bp_hw_targ, p); -+} -+ -+int qbman_bp_info_has_free_bufs(struct qbman_attr *a) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ return !(int)(qb_attr_code_decode(&code_bp_state, p) & 0x1); -+} -+ -+int qbman_bp_info_is_depleted(struct qbman_attr *a) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ return (int)(qb_attr_code_decode(&code_bp_state, p) & 0x2); -+} -+ -+int qbman_bp_info_is_surplus(struct qbman_attr *a) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ return (int)(qb_attr_code_decode(&code_bp_state, p) & 0x4); -+} -+ -+uint32_t qbman_bp_info_num_free_bufs(struct qbman_attr *a) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ return qb_attr_code_decode(&code_bp_fill, p); -+} -+ -+uint32_t qbman_bp_info_hdptr(struct qbman_attr *a) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ return qb_attr_code_decode(&code_bp_hdptr, p); -+} -+ -+uint32_t qbman_bp_info_sdcnt(struct qbman_attr *a) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ return qb_attr_code_decode(&code_bp_sdcnt, p); -+} -+ -+uint32_t qbman_bp_info_hdcnt(struct qbman_attr *a) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ return qb_attr_code_decode(&code_bp_hdcnt, p); -+} -+ -+uint32_t qbman_bp_info_sscnt(struct qbman_attr *a) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ return qb_attr_code_decode(&code_bp_sscnt, p); -+} -+ -+static struct qb_attr_code code_fq_fqid = QB_CODE(1, 0, 24); -+static struct qb_attr_code code_fq_cgrid = QB_CODE(2, 16, 16); -+static struct qb_attr_code code_fq_destwq = QB_CODE(3, 0, 15); -+static struct qb_attr_code code_fq_fqctrl = QB_CODE(3, 24, 8); -+static struct qb_attr_code code_fq_icscred = QB_CODE(4, 0, 15); -+static struct qb_attr_code code_fq_tdthresh = QB_CODE(4, 16, 13); -+static struct qb_attr_code code_fq_oa_len = QB_CODE(5, 0, 12); -+static struct qb_attr_code code_fq_oa_ics = QB_CODE(5, 14, 1); -+static struct qb_attr_code code_fq_oa_cgr = QB_CODE(5, 15, 1); -+static struct qb_attr_code code_fq_mctl_bdi = QB_CODE(5, 24, 1); -+static struct qb_attr_code code_fq_mctl_ff = QB_CODE(5, 25, 1); -+static struct qb_attr_code code_fq_mctl_va = QB_CODE(5, 26, 1); -+static struct qb_attr_code code_fq_mctl_ps = QB_CODE(5, 27, 1); -+static struct qb_attr_code code_fq_ctx_lower32 = QB_CODE(6, 0, 32); -+static struct qb_attr_code code_fq_ctx_upper32 = QB_CODE(7, 0, 32); -+static struct qb_attr_code code_fq_icid = QB_CODE(8, 0, 15); -+static struct qb_attr_code code_fq_pl = QB_CODE(8, 15, 1); -+static struct qb_attr_code code_fq_vfqid = QB_CODE(9, 0, 24); -+static struct qb_attr_code code_fq_erfqid = QB_CODE(10, 0, 24); -+ -+void qbman_fq_attr_clear(struct qbman_attr *a) -+{ -+ memset(a, 0, sizeof(*a)); -+ attr_type_set(a, qbman_attr_usage_fq); -+} -+ -+/* FQ query function for programmable fields */ -+int qbman_fq_query(struct qbman_swp *s, uint32_t fqid, struct qbman_attr *desc) -+{ -+ uint32_t *p; -+ uint32_t verb, rslt; -+ uint32_t *d = ATTR32(desc); -+ -+ qbman_fq_attr_clear(desc); -+ -+ p = qbman_swp_mc_start(s); -+ if (!p) -+ return -EBUSY; -+ qb_attr_code_encode(&code_fq_fqid, p, fqid); -+ p = qbman_swp_mc_complete(s, p, QBMAN_FQ_QUERY); -+ -+ /* Decode the outcome */ -+ verb = qb_attr_code_decode(&code_generic_verb, p); -+ rslt = qb_attr_code_decode(&code_generic_rslt, p); -+ BUG_ON(verb != QBMAN_FQ_QUERY); -+ -+ /* Determine success or failure */ -+ if (unlikely(rslt != QBMAN_MC_RSLT_OK)) { -+ pr_err("Query of FQID 0x%x failed, code=0x%02x\n", -+ fqid, rslt); -+ return -EIO; -+ } -+ /* For the configure, word[0] of the command contains only the WE-mask. -+ * For the query, word[0] of the result contains only the verb/rslt -+ * fields. Skip word[0] in the latter case. */ -+ word_copy(&d[1], &p[1], 15); -+ return 0; -+} -+ -+void qbman_fq_attr_get_fqctrl(struct qbman_attr *d, uint32_t *fqctrl) -+{ -+ uint32_t *p = ATTR32(d); -+ -+ *fqctrl = qb_attr_code_decode(&code_fq_fqctrl, p); -+} -+ -+void qbman_fq_attr_get_cgrid(struct qbman_attr *d, uint32_t *cgrid) -+{ -+ uint32_t *p = ATTR32(d); -+ -+ *cgrid = qb_attr_code_decode(&code_fq_cgrid, p); -+} -+ -+void qbman_fq_attr_get_destwq(struct qbman_attr *d, uint32_t *destwq) -+{ -+ uint32_t *p = ATTR32(d); -+ -+ *destwq = qb_attr_code_decode(&code_fq_destwq, p); -+} -+ -+void qbman_fq_attr_get_icscred(struct qbman_attr *d, uint32_t *icscred) -+{ -+ uint32_t *p = ATTR32(d); -+ -+ *icscred = qb_attr_code_decode(&code_fq_icscred, p); -+} -+ -+static struct qb_attr_code code_tdthresh_exp = QB_CODE(0, 0, 5); -+static struct qb_attr_code code_tdthresh_mant = QB_CODE(0, 5, 8); -+static uint32_t qbman_thresh_to_value(uint32_t val) -+{ -+ uint32_t m, e; -+ -+ m = qb_attr_code_decode(&code_tdthresh_mant, &val); -+ e = qb_attr_code_decode(&code_tdthresh_exp, &val); -+ return m << e; -+} -+ -+void qbman_fq_attr_get_tdthresh(struct qbman_attr *d, uint32_t *tdthresh) -+{ -+ uint32_t *p = ATTR32(d); -+ -+ *tdthresh = qbman_thresh_to_value(qb_attr_code_decode(&code_fq_tdthresh, -+ p)); -+} -+ -+void qbman_fq_attr_get_oa(struct qbman_attr *d, -+ int *oa_ics, int *oa_cgr, int32_t *oa_len) -+{ -+ uint32_t *p = ATTR32(d); -+ -+ *oa_ics = !!qb_attr_code_decode(&code_fq_oa_ics, p); -+ *oa_cgr = !!qb_attr_code_decode(&code_fq_oa_cgr, p); -+ *oa_len = qb_attr_code_makesigned(&code_fq_oa_len, -+ qb_attr_code_decode(&code_fq_oa_len, p)); -+} -+ -+void qbman_fq_attr_get_mctl(struct qbman_attr *d, -+ int *bdi, int *ff, int *va, int *ps) -+{ -+ uint32_t *p = ATTR32(d); -+ -+ *bdi = !!qb_attr_code_decode(&code_fq_mctl_bdi, p); -+ *ff = !!qb_attr_code_decode(&code_fq_mctl_ff, p); -+ *va = !!qb_attr_code_decode(&code_fq_mctl_va, p); -+ *ps = !!qb_attr_code_decode(&code_fq_mctl_ps, p); -+} -+ -+void qbman_fq_attr_get_ctx(struct qbman_attr *d, uint32_t *hi, uint32_t *lo) -+{ -+ uint32_t *p = ATTR32(d); -+ -+ *hi = qb_attr_code_decode(&code_fq_ctx_upper32, p); -+ *lo = qb_attr_code_decode(&code_fq_ctx_lower32, p); -+} -+ -+void qbman_fq_attr_get_icid(struct qbman_attr *d, uint32_t *icid, int *pl) -+{ -+ uint32_t *p = ATTR32(d); -+ -+ *icid = qb_attr_code_decode(&code_fq_icid, p); -+ *pl = !!qb_attr_code_decode(&code_fq_pl, p); -+} -+ -+void qbman_fq_attr_get_vfqid(struct qbman_attr *d, uint32_t *vfqid) -+{ -+ uint32_t *p = ATTR32(d); -+ -+ *vfqid = qb_attr_code_decode(&code_fq_vfqid, p); -+} -+ -+void qbman_fq_attr_get_erfqid(struct qbman_attr *d, uint32_t *erfqid) -+{ -+ uint32_t *p = ATTR32(d); -+ -+ *erfqid = qb_attr_code_decode(&code_fq_erfqid, p); -+} -+ -+/* Query FQ Non-Programmalbe Fields */ -+static struct qb_attr_code code_fq_np_state = QB_CODE(0, 16, 3); -+static struct qb_attr_code code_fq_np_fe = QB_CODE(0, 19, 1); -+static struct qb_attr_code code_fq_np_x = QB_CODE(0, 20, 1); -+static struct qb_attr_code code_fq_np_r = QB_CODE(0, 21, 1); -+static struct qb_attr_code code_fq_np_oe = QB_CODE(0, 22, 1); -+static struct qb_attr_code code_fq_np_frm_cnt = QB_CODE(6, 0, 24); -+static struct qb_attr_code code_fq_np_byte_cnt = QB_CODE(7, 0, 32); -+ -+int qbman_fq_query_state(struct qbman_swp *s, uint32_t fqid, -+ struct qbman_attr *state) -+{ -+ uint32_t *p; -+ uint32_t verb, rslt; -+ uint32_t *d = ATTR32(state); -+ -+ qbman_fq_attr_clear(state); -+ -+ p = qbman_swp_mc_start(s); -+ if (!p) -+ return -EBUSY; -+ qb_attr_code_encode(&code_fq_fqid, p, fqid); -+ p = qbman_swp_mc_complete(s, p, QBMAN_FQ_QUERY_NP); -+ -+ /* Decode the outcome */ -+ verb = qb_attr_code_decode(&code_generic_verb, p); -+ rslt = qb_attr_code_decode(&code_generic_rslt, p); -+ BUG_ON(verb != QBMAN_FQ_QUERY_NP); -+ -+ /* Determine success or failure */ -+ if (unlikely(rslt != QBMAN_MC_RSLT_OK)) { -+ pr_err("Query NP fields of FQID 0x%x failed, code=0x%02x\n", -+ fqid, rslt); -+ return -EIO; -+ } -+ word_copy(&d[0], &p[0], 16); -+ return 0; -+} -+ -+uint32_t qbman_fq_state_schedstate(const struct qbman_attr *state) -+{ -+ const uint32_t *p = ATTR32(state); -+ -+ return qb_attr_code_decode(&code_fq_np_state, p); -+} -+ -+int qbman_fq_state_force_eligible(const struct qbman_attr *state) -+{ -+ const uint32_t *p = ATTR32(state); -+ -+ return !!qb_attr_code_decode(&code_fq_np_fe, p); -+} -+ -+int qbman_fq_state_xoff(const struct qbman_attr *state) -+{ -+ const uint32_t *p = ATTR32(state); -+ -+ return !!qb_attr_code_decode(&code_fq_np_x, p); -+} -+ -+int qbman_fq_state_retirement_pending(const struct qbman_attr *state) -+{ -+ const uint32_t *p = ATTR32(state); -+ -+ return !!qb_attr_code_decode(&code_fq_np_r, p); -+} -+ -+int qbman_fq_state_overflow_error(const struct qbman_attr *state) -+{ -+ const uint32_t *p = ATTR32(state); -+ -+ return !!qb_attr_code_decode(&code_fq_np_oe, p); -+} -+ -+uint32_t qbman_fq_state_frame_count(const struct qbman_attr *state) -+{ -+ const uint32_t *p = ATTR32(state); -+ -+ return qb_attr_code_decode(&code_fq_np_frm_cnt, p); -+} -+ -+uint32_t qbman_fq_state_byte_count(const struct qbman_attr *state) -+{ -+ const uint32_t *p = ATTR32(state); -+ -+ return qb_attr_code_decode(&code_fq_np_byte_cnt, p); -+} -+ -+/* Query CGR */ -+static struct qb_attr_code code_cgr_cgid = QB_CODE(0, 16, 16); -+static struct qb_attr_code code_cgr_cscn_wq_en_enter = QB_CODE(2, 0, 1); -+static struct qb_attr_code code_cgr_cscn_wq_en_exit = QB_CODE(2, 1, 1); -+static struct qb_attr_code code_cgr_cscn_wq_icd = QB_CODE(2, 2, 1); -+static struct qb_attr_code code_cgr_mode = QB_CODE(3, 16, 2); -+static struct qb_attr_code code_cgr_rej_cnt_mode = QB_CODE(3, 18, 1); -+static struct qb_attr_code code_cgr_cscn_bdi = QB_CODE(3, 19, 1); -+static struct qb_attr_code code_cgr_cscn_wr_en_enter = QB_CODE(3, 24, 1); -+static struct qb_attr_code code_cgr_cscn_wr_en_exit = QB_CODE(3, 25, 1); -+static struct qb_attr_code code_cgr_cg_wr_ae = QB_CODE(3, 26, 1); -+static struct qb_attr_code code_cgr_cscn_dcp_en = QB_CODE(3, 27, 1); -+static struct qb_attr_code code_cgr_cg_wr_va = QB_CODE(3, 28, 1); -+static struct qb_attr_code code_cgr_i_cnt_wr_en = QB_CODE(4, 0, 1); -+static struct qb_attr_code code_cgr_i_cnt_wr_bnd = QB_CODE(4, 1, 5); -+static struct qb_attr_code code_cgr_td_en = QB_CODE(4, 8, 1); -+static struct qb_attr_code code_cgr_cs_thres = QB_CODE(4, 16, 13); -+static struct qb_attr_code code_cgr_cs_thres_x = QB_CODE(5, 0, 13); -+static struct qb_attr_code code_cgr_td_thres = QB_CODE(5, 16, 13); -+static struct qb_attr_code code_cgr_cscn_tdcp = QB_CODE(6, 0, 16); -+static struct qb_attr_code code_cgr_cscn_wqid = QB_CODE(6, 16, 16); -+static struct qb_attr_code code_cgr_cscn_vcgid = QB_CODE(7, 0, 16); -+static struct qb_attr_code code_cgr_cg_icid = QB_CODE(7, 16, 15); -+static struct qb_attr_code code_cgr_cg_pl = QB_CODE(7, 31, 1); -+static struct qb_attr_code code_cgr_cg_wr_addr_lo = QB_CODE(8, 0, 32); -+static struct qb_attr_code code_cgr_cg_wr_addr_hi = QB_CODE(9, 0, 32); -+static struct qb_attr_code code_cgr_cscn_ctx_lo = QB_CODE(10, 0, 32); -+static struct qb_attr_code code_cgr_cscn_ctx_hi = QB_CODE(11, 0, 32); -+ -+void qbman_cgr_attr_clear(struct qbman_attr *a) -+{ -+ memset(a, 0, sizeof(*a)); -+ attr_type_set(a, qbman_attr_usage_cgr); -+} -+ -+int qbman_cgr_query(struct qbman_swp *s, uint32_t cgid, struct qbman_attr *attr) -+{ -+ uint32_t *p; -+ uint32_t verb, rslt; -+ uint32_t *d[2]; -+ int i; -+ uint32_t query_verb; -+ -+ d[0] = ATTR32(attr); -+ d[1] = ATTR32_1(attr); -+ -+ qbman_cgr_attr_clear(attr); -+ -+ for (i = 0; i < 2; i++) { -+ p = qbman_swp_mc_start(s); -+ if (!p) -+ return -EBUSY; -+ query_verb = i ? QBMAN_WRED_QUERY : QBMAN_CGR_QUERY; -+ -+ qb_attr_code_encode(&code_cgr_cgid, p, cgid); -+ p = qbman_swp_mc_complete(s, p, p[0] | query_verb); -+ -+ /* Decode the outcome */ -+ verb = qb_attr_code_decode(&code_generic_verb, p); -+ rslt = qb_attr_code_decode(&code_generic_rslt, p); -+ BUG_ON(verb != query_verb); -+ -+ /* Determine success or failure */ -+ if (unlikely(rslt != QBMAN_MC_RSLT_OK)) { -+ pr_err("Query CGID 0x%x failed,", cgid); -+ pr_err(" verb=0x%02x, code=0x%02x\n", verb, rslt); -+ return -EIO; -+ } -+ /* For the configure, word[0] of the command contains only the -+ * verb/cgid. For the query, word[0] of the result contains -+ * only the verb/rslt fields. Skip word[0] in the latter case. -+ */ -+ word_copy(&d[i][1], &p[1], 15); -+ } -+ return 0; -+} -+ -+void qbman_cgr_attr_get_ctl1(struct qbman_attr *d, int *cscn_wq_en_enter, -+ int *cscn_wq_en_exit, int *cscn_wq_icd) -+ { -+ uint32_t *p = ATTR32(d); -+ *cscn_wq_en_enter = !!qb_attr_code_decode(&code_cgr_cscn_wq_en_enter, -+ p); -+ *cscn_wq_en_exit = !!qb_attr_code_decode(&code_cgr_cscn_wq_en_exit, p); -+ *cscn_wq_icd = !!qb_attr_code_decode(&code_cgr_cscn_wq_icd, p); -+} -+ -+void qbman_cgr_attr_get_mode(struct qbman_attr *d, uint32_t *mode, -+ int *rej_cnt_mode, int *cscn_bdi) -+{ -+ uint32_t *p = ATTR32(d); -+ *mode = qb_attr_code_decode(&code_cgr_mode, p); -+ *rej_cnt_mode = !!qb_attr_code_decode(&code_cgr_rej_cnt_mode, p); -+ *cscn_bdi = !!qb_attr_code_decode(&code_cgr_cscn_bdi, p); -+} -+ -+void qbman_cgr_attr_get_ctl2(struct qbman_attr *d, int *cscn_wr_en_enter, -+ int *cscn_wr_en_exit, int *cg_wr_ae, -+ int *cscn_dcp_en, int *cg_wr_va) -+{ -+ uint32_t *p = ATTR32(d); -+ *cscn_wr_en_enter = !!qb_attr_code_decode(&code_cgr_cscn_wr_en_enter, -+ p); -+ *cscn_wr_en_exit = !!qb_attr_code_decode(&code_cgr_cscn_wr_en_exit, p); -+ *cg_wr_ae = !!qb_attr_code_decode(&code_cgr_cg_wr_ae, p); -+ *cscn_dcp_en = !!qb_attr_code_decode(&code_cgr_cscn_dcp_en, p); -+ *cg_wr_va = !!qb_attr_code_decode(&code_cgr_cg_wr_va, p); -+} -+ -+void qbman_cgr_attr_get_iwc(struct qbman_attr *d, int *i_cnt_wr_en, -+ uint32_t *i_cnt_wr_bnd) -+{ -+ uint32_t *p = ATTR32(d); -+ *i_cnt_wr_en = !!qb_attr_code_decode(&code_cgr_i_cnt_wr_en, p); -+ *i_cnt_wr_bnd = qb_attr_code_decode(&code_cgr_i_cnt_wr_bnd, p); -+} -+ -+void qbman_cgr_attr_get_tdc(struct qbman_attr *d, int *td_en) -+{ -+ uint32_t *p = ATTR32(d); -+ *td_en = !!qb_attr_code_decode(&code_cgr_td_en, p); -+} -+ -+void qbman_cgr_attr_get_cs_thres(struct qbman_attr *d, uint32_t *cs_thres) -+{ -+ uint32_t *p = ATTR32(d); -+ *cs_thres = qbman_thresh_to_value(qb_attr_code_decode( -+ &code_cgr_cs_thres, p)); -+} -+ -+void qbman_cgr_attr_get_cs_thres_x(struct qbman_attr *d, -+ uint32_t *cs_thres_x) -+{ -+ uint32_t *p = ATTR32(d); -+ *cs_thres_x = qbman_thresh_to_value(qb_attr_code_decode( -+ &code_cgr_cs_thres_x, p)); -+} -+ -+void qbman_cgr_attr_get_td_thres(struct qbman_attr *d, uint32_t *td_thres) -+{ -+ uint32_t *p = ATTR32(d); -+ *td_thres = qbman_thresh_to_value(qb_attr_code_decode( -+ &code_cgr_td_thres, p)); -+} -+ -+void qbman_cgr_attr_get_cscn_tdcp(struct qbman_attr *d, uint32_t *cscn_tdcp) -+{ -+ uint32_t *p = ATTR32(d); -+ *cscn_tdcp = qb_attr_code_decode(&code_cgr_cscn_tdcp, p); -+} -+ -+void qbman_cgr_attr_get_cscn_wqid(struct qbman_attr *d, uint32_t *cscn_wqid) -+{ -+ uint32_t *p = ATTR32(d); -+ *cscn_wqid = qb_attr_code_decode(&code_cgr_cscn_wqid, p); -+} -+ -+void qbman_cgr_attr_get_cscn_vcgid(struct qbman_attr *d, -+ uint32_t *cscn_vcgid) -+{ -+ uint32_t *p = ATTR32(d); -+ *cscn_vcgid = qb_attr_code_decode(&code_cgr_cscn_vcgid, p); -+} -+ -+void qbman_cgr_attr_get_cg_icid(struct qbman_attr *d, uint32_t *icid, -+ int *pl) -+{ -+ uint32_t *p = ATTR32(d); -+ *icid = qb_attr_code_decode(&code_cgr_cg_icid, p); -+ *pl = !!qb_attr_code_decode(&code_cgr_cg_pl, p); -+} -+ -+void qbman_cgr_attr_get_cg_wr_addr(struct qbman_attr *d, -+ uint64_t *cg_wr_addr) -+{ -+ uint32_t *p = ATTR32(d); -+ *cg_wr_addr = ((uint64_t)qb_attr_code_decode(&code_cgr_cg_wr_addr_hi, -+ p) << 32) | -+ (uint64_t)qb_attr_code_decode(&code_cgr_cg_wr_addr_lo, -+ p); -+} -+ -+void qbman_cgr_attr_get_cscn_ctx(struct qbman_attr *d, uint64_t *cscn_ctx) -+{ -+ uint32_t *p = ATTR32(d); -+ *cscn_ctx = ((uint64_t)qb_attr_code_decode(&code_cgr_cscn_ctx_hi, p) -+ << 32) | -+ (uint64_t)qb_attr_code_decode(&code_cgr_cscn_ctx_lo, p); -+} -+ -+#define WRED_EDP_WORD(n) (18 + n/4) -+#define WRED_EDP_OFFSET(n) (8 * (n % 4)) -+#define WRED_PARM_DP_WORD(n) (n + 20) -+#define WRED_WE_EDP(n) (16 + n * 2) -+#define WRED_WE_PARM_DP(n) (17 + n * 2) -+void qbman_cgr_attr_wred_get_edp(struct qbman_attr *d, uint32_t idx, -+ int *edp) -+{ -+ uint32_t *p = ATTR32(d); -+ struct qb_attr_code code_wred_edp = QB_CODE(WRED_EDP_WORD(idx), -+ WRED_EDP_OFFSET(idx), 8); -+ *edp = (int)qb_attr_code_decode(&code_wred_edp, p); -+} -+ -+void qbman_cgr_attr_wred_dp_decompose(uint32_t dp, uint64_t *minth, -+ uint64_t *maxth, uint8_t *maxp) -+{ -+ uint8_t ma, mn, step_i, step_s, pn; -+ -+ ma = (uint8_t)(dp >> 24); -+ mn = (uint8_t)(dp >> 19) & 0x1f; -+ step_i = (uint8_t)(dp >> 11); -+ step_s = (uint8_t)(dp >> 6) & 0x1f; -+ pn = (uint8_t)dp & 0x3f; -+ -+ *maxp = ((pn<<2) * 100)/256; -+ -+ if (mn == 0) -+ *maxth = ma; -+ else -+ *maxth = ((ma+256) * (1<<(mn-1))); -+ -+ if (step_s == 0) -+ *minth = *maxth - step_i; -+ else -+ *minth = *maxth - (256 + step_i) * (1<<(step_s - 1)); -+} -+ -+void qbman_cgr_attr_wred_get_parm_dp(struct qbman_attr *d, uint32_t idx, -+ uint32_t *dp) -+{ -+ uint32_t *p = ATTR32(d); -+ struct qb_attr_code code_wred_parm_dp = QB_CODE(WRED_PARM_DP_WORD(idx), -+ 0, 8); -+ *dp = qb_attr_code_decode(&code_wred_parm_dp, p); -+} -+ -+/* Query CGR/CCGR/CQ statistics */ -+static struct qb_attr_code code_cgr_stat_ct = QB_CODE(4, 0, 32); -+static struct qb_attr_code code_cgr_stat_frame_cnt_lo = QB_CODE(4, 0, 32); -+static struct qb_attr_code code_cgr_stat_frame_cnt_hi = QB_CODE(5, 0, 8); -+static struct qb_attr_code code_cgr_stat_byte_cnt_lo = QB_CODE(6, 0, 32); -+static struct qb_attr_code code_cgr_stat_byte_cnt_hi = QB_CODE(7, 0, 16); -+static int qbman_cgr_statistics_query(struct qbman_swp *s, uint32_t cgid, -+ int clear, uint32_t command_type, -+ uint64_t *frame_cnt, uint64_t *byte_cnt) -+{ -+ uint32_t *p; -+ uint32_t verb, rslt; -+ uint32_t query_verb; -+ uint32_t hi, lo; -+ -+ p = qbman_swp_mc_start(s); -+ if (!p) -+ return -EBUSY; -+ -+ qb_attr_code_encode(&code_cgr_cgid, p, cgid); -+ if (command_type < 2) -+ qb_attr_code_encode(&code_cgr_stat_ct, p, command_type); -+ query_verb = clear ? -+ QBMAN_CGR_STAT_QUERY_CLR : QBMAN_CGR_STAT_QUERY; -+ p = qbman_swp_mc_complete(s, p, p[0] | query_verb); -+ -+ /* Decode the outcome */ -+ verb = qb_attr_code_decode(&code_generic_verb, p); -+ rslt = qb_attr_code_decode(&code_generic_rslt, p); -+ BUG_ON(verb != query_verb); -+ -+ /* Determine success or failure */ -+ if (unlikely(rslt != QBMAN_MC_RSLT_OK)) { -+ pr_err("Query statistics of CGID 0x%x failed,", cgid); -+ pr_err(" verb=0x%02x code=0x%02x\n", verb, rslt); -+ return -EIO; -+ } -+ -+ if (*frame_cnt) { -+ hi = qb_attr_code_decode(&code_cgr_stat_frame_cnt_hi, p); -+ lo = qb_attr_code_decode(&code_cgr_stat_frame_cnt_lo, p); -+ *frame_cnt = ((uint64_t)hi << 32) | (uint64_t)lo; -+ } -+ if (*byte_cnt) { -+ hi = qb_attr_code_decode(&code_cgr_stat_byte_cnt_hi, p); -+ lo = qb_attr_code_decode(&code_cgr_stat_byte_cnt_lo, p); -+ *byte_cnt = ((uint64_t)hi << 32) | (uint64_t)lo; -+ } -+ -+ return 0; -+} -+ -+int qbman_cgr_reject_statistics(struct qbman_swp *s, uint32_t cgid, int clear, -+ uint64_t *frame_cnt, uint64_t *byte_cnt) -+{ -+ return qbman_cgr_statistics_query(s, cgid, clear, 0xff, -+ frame_cnt, byte_cnt); -+} -+ -+int qbman_ccgr_reject_statistics(struct qbman_swp *s, uint32_t cgid, int clear, -+ uint64_t *frame_cnt, uint64_t *byte_cnt) -+{ -+ return qbman_cgr_statistics_query(s, cgid, clear, 1, -+ frame_cnt, byte_cnt); -+} -+ -+int qbman_cq_dequeue_statistics(struct qbman_swp *s, uint32_t cgid, int clear, -+ uint64_t *frame_cnt, uint64_t *byte_cnt) -+{ -+ return qbman_cgr_statistics_query(s, cgid, clear, 0, -+ frame_cnt, byte_cnt); -+} -diff --git a/drivers/staging/fsl-mc/bus/dpio/qbman_debug.h b/drivers/staging/fsl-mc/bus/dpio/qbman_debug.h -new file mode 100644 -index 0000000..1e6b002 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpio/qbman_debug.h -@@ -0,0 +1,136 @@ -+/* Copyright (C) 2015 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. -+ */ -+ -+struct qbman_attr { -+ uint32_t dont_manipulate_directly[40]; -+}; -+ -+/* Buffer pool query commands */ -+int qbman_bp_query(struct qbman_swp *s, uint32_t bpid, -+ struct qbman_attr *a); -+void qbman_bp_attr_get_bdi(struct qbman_attr *a, int *bdi, int *va, int *wae); -+void qbman_bp_attr_get_swdet(struct qbman_attr *a, uint32_t *swdet); -+void qbman_bp_attr_get_swdxt(struct qbman_attr *a, uint32_t *swdxt); -+void qbman_bp_attr_get_hwdet(struct qbman_attr *a, uint32_t *hwdet); -+void qbman_bp_attr_get_hwdxt(struct qbman_attr *a, uint32_t *hwdxt); -+void qbman_bp_attr_get_swset(struct qbman_attr *a, uint32_t *swset); -+void qbman_bp_attr_get_swsxt(struct qbman_attr *a, uint32_t *swsxt); -+void qbman_bp_attr_get_vbpid(struct qbman_attr *a, uint32_t *vbpid); -+void qbman_bp_attr_get_icid(struct qbman_attr *a, uint32_t *icid, int *pl); -+void qbman_bp_attr_get_bpscn_addr(struct qbman_attr *a, uint64_t *bpscn_addr); -+void qbman_bp_attr_get_bpscn_ctx(struct qbman_attr *a, uint64_t *bpscn_ctx); -+void qbman_bp_attr_get_hw_targ(struct qbman_attr *a, uint32_t *hw_targ); -+int qbman_bp_info_has_free_bufs(struct qbman_attr *a); -+int qbman_bp_info_is_depleted(struct qbman_attr *a); -+int qbman_bp_info_is_surplus(struct qbman_attr *a); -+uint32_t qbman_bp_info_num_free_bufs(struct qbman_attr *a); -+uint32_t qbman_bp_info_hdptr(struct qbman_attr *a); -+uint32_t qbman_bp_info_sdcnt(struct qbman_attr *a); -+uint32_t qbman_bp_info_hdcnt(struct qbman_attr *a); -+uint32_t qbman_bp_info_sscnt(struct qbman_attr *a); -+ -+/* FQ query function for programmable fields */ -+int qbman_fq_query(struct qbman_swp *s, uint32_t fqid, -+ struct qbman_attr *desc); -+void qbman_fq_attr_get_fqctrl(struct qbman_attr *d, uint32_t *fqctrl); -+void qbman_fq_attr_get_cgrid(struct qbman_attr *d, uint32_t *cgrid); -+void qbman_fq_attr_get_destwq(struct qbman_attr *d, uint32_t *destwq); -+void qbman_fq_attr_get_icscred(struct qbman_attr *d, uint32_t *icscred); -+void qbman_fq_attr_get_tdthresh(struct qbman_attr *d, uint32_t *tdthresh); -+void qbman_fq_attr_get_oa(struct qbman_attr *d, -+ int *oa_ics, int *oa_cgr, int32_t *oa_len); -+void qbman_fq_attr_get_mctl(struct qbman_attr *d, -+ int *bdi, int *ff, int *va, int *ps); -+void qbman_fq_attr_get_ctx(struct qbman_attr *d, uint32_t *hi, uint32_t *lo); -+void qbman_fq_attr_get_icid(struct qbman_attr *d, uint32_t *icid, int *pl); -+void qbman_fq_attr_get_vfqid(struct qbman_attr *d, uint32_t *vfqid); -+void qbman_fq_attr_get_erfqid(struct qbman_attr *d, uint32_t *erfqid); -+ -+/* FQ query command for non-programmable fields*/ -+enum qbman_fq_schedstate_e { -+ qbman_fq_schedstate_oos = 0, -+ qbman_fq_schedstate_retired, -+ qbman_fq_schedstate_tentatively_scheduled, -+ qbman_fq_schedstate_truly_scheduled, -+ qbman_fq_schedstate_parked, -+ qbman_fq_schedstate_held_active, -+}; -+ -+int qbman_fq_query_state(struct qbman_swp *s, uint32_t fqid, -+ struct qbman_attr *state); -+uint32_t qbman_fq_state_schedstate(const struct qbman_attr *state); -+int qbman_fq_state_force_eligible(const struct qbman_attr *state); -+int qbman_fq_state_xoff(const struct qbman_attr *state); -+int qbman_fq_state_retirement_pending(const struct qbman_attr *state); -+int qbman_fq_state_overflow_error(const struct qbman_attr *state); -+uint32_t qbman_fq_state_frame_count(const struct qbman_attr *state); -+uint32_t qbman_fq_state_byte_count(const struct qbman_attr *state); -+ -+/* CGR query */ -+int qbman_cgr_query(struct qbman_swp *s, uint32_t cgid, -+ struct qbman_attr *attr); -+void qbman_cgr_attr_get_ctl1(struct qbman_attr *d, int *cscn_wq_en_enter, -+ int *cscn_wq_en_exit, int *cscn_wq_icd); -+void qbman_cgr_attr_get_mode(struct qbman_attr *d, uint32_t *mode, -+ int *rej_cnt_mode, int *cscn_bdi); -+void qbman_cgr_attr_get_ctl2(struct qbman_attr *d, int *cscn_wr_en_enter, -+ int *cscn_wr_en_exit, int *cg_wr_ae, -+ int *cscn_dcp_en, int *cg_wr_va); -+void qbman_cgr_attr_get_iwc(struct qbman_attr *d, int *i_cnt_wr_en, -+ uint32_t *i_cnt_wr_bnd); -+void qbman_cgr_attr_get_tdc(struct qbman_attr *d, int *td_en); -+void qbman_cgr_attr_get_cs_thres(struct qbman_attr *d, uint32_t *cs_thres); -+void qbman_cgr_attr_get_cs_thres_x(struct qbman_attr *d, -+ uint32_t *cs_thres_x); -+void qbman_cgr_attr_get_td_thres(struct qbman_attr *d, uint32_t *td_thres); -+void qbman_cgr_attr_get_cscn_tdcp(struct qbman_attr *d, uint32_t *cscn_tdcp); -+void qbman_cgr_attr_get_cscn_wqid(struct qbman_attr *d, uint32_t *cscn_wqid); -+void qbman_cgr_attr_get_cscn_vcgid(struct qbman_attr *d, -+ uint32_t *cscn_vcgid); -+void qbman_cgr_attr_get_cg_icid(struct qbman_attr *d, uint32_t *icid, -+ int *pl); -+void qbman_cgr_attr_get_cg_wr_addr(struct qbman_attr *d, -+ uint64_t *cg_wr_addr); -+void qbman_cgr_attr_get_cscn_ctx(struct qbman_attr *d, uint64_t *cscn_ctx); -+void qbman_cgr_attr_wred_get_edp(struct qbman_attr *d, uint32_t idx, -+ int *edp); -+void qbman_cgr_attr_wred_dp_decompose(uint32_t dp, uint64_t *minth, -+ uint64_t *maxth, uint8_t *maxp); -+void qbman_cgr_attr_wred_get_parm_dp(struct qbman_attr *d, uint32_t idx, -+ uint32_t *dp); -+ -+/* CGR/CCGR/CQ statistics query */ -+int qbman_cgr_reject_statistics(struct qbman_swp *s, uint32_t cgid, int clear, -+ uint64_t *frame_cnt, uint64_t *byte_cnt); -+int qbman_ccgr_reject_statistics(struct qbman_swp *s, uint32_t cgid, int clear, -+ uint64_t *frame_cnt, uint64_t *byte_cnt); -+int qbman_cq_dequeue_statistics(struct qbman_swp *s, uint32_t cgid, int clear, -+ uint64_t *frame_cnt, uint64_t *byte_cnt); -diff --git a/drivers/staging/fsl-mc/bus/dpio/qbman_portal.c b/drivers/staging/fsl-mc/bus/dpio/qbman_portal.c -new file mode 100644 -index 0000000..6c5638b ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpio/qbman_portal.c -@@ -0,0 +1,1212 @@ -+/* Copyright (C) 2014 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 "qbman_portal.h" -+ -+/* QBMan portal management command codes */ -+#define QBMAN_MC_ACQUIRE 0x30 -+#define QBMAN_WQCHAN_CONFIGURE 0x46 -+ -+/* CINH register offsets */ -+#define QBMAN_CINH_SWP_EQAR 0x8c0 -+#define QBMAN_CINH_SWP_DQPI 0xa00 -+#define QBMAN_CINH_SWP_DCAP 0xac0 -+#define QBMAN_CINH_SWP_SDQCR 0xb00 -+#define QBMAN_CINH_SWP_RAR 0xcc0 -+#define QBMAN_CINH_SWP_ISR 0xe00 -+#define QBMAN_CINH_SWP_IER 0xe40 -+#define QBMAN_CINH_SWP_ISDR 0xe80 -+#define QBMAN_CINH_SWP_IIR 0xec0 -+ -+/* CENA register offsets */ -+#define QBMAN_CENA_SWP_EQCR(n) (0x000 + ((uint32_t)(n) << 6)) -+#define QBMAN_CENA_SWP_DQRR(n) (0x200 + ((uint32_t)(n) << 6)) -+#define QBMAN_CENA_SWP_RCR(n) (0x400 + ((uint32_t)(n) << 6)) -+#define QBMAN_CENA_SWP_CR 0x600 -+#define QBMAN_CENA_SWP_RR(vb) (0x700 + ((uint32_t)(vb) >> 1)) -+#define QBMAN_CENA_SWP_VDQCR 0x780 -+ -+/* Reverse mapping of QBMAN_CENA_SWP_DQRR() */ -+#define QBMAN_IDX_FROM_DQRR(p) (((unsigned long)p & 0x1ff) >> 6) -+ -+/* QBMan FQ management command codes */ -+#define QBMAN_FQ_SCHEDULE 0x48 -+#define QBMAN_FQ_FORCE 0x49 -+#define QBMAN_FQ_XON 0x4d -+#define QBMAN_FQ_XOFF 0x4e -+ -+/*******************************/ -+/* Pre-defined attribute codes */ -+/*******************************/ -+ -+struct qb_attr_code code_generic_verb = QB_CODE(0, 0, 7); -+struct qb_attr_code code_generic_rslt = QB_CODE(0, 8, 8); -+ -+/*************************/ -+/* SDQCR attribute codes */ -+/*************************/ -+ -+/* we put these here because at least some of them are required by -+ * qbman_swp_init() */ -+struct qb_attr_code code_sdqcr_dct = QB_CODE(0, 24, 2); -+struct qb_attr_code code_sdqcr_fc = QB_CODE(0, 29, 1); -+struct qb_attr_code code_sdqcr_tok = QB_CODE(0, 16, 8); -+#define CODE_SDQCR_DQSRC(n) QB_CODE(0, n, 1) -+enum qbman_sdqcr_dct { -+ qbman_sdqcr_dct_null = 0, -+ qbman_sdqcr_dct_prio_ics, -+ qbman_sdqcr_dct_active_ics, -+ qbman_sdqcr_dct_active -+}; -+enum qbman_sdqcr_fc { -+ qbman_sdqcr_fc_one = 0, -+ qbman_sdqcr_fc_up_to_3 = 1 -+}; -+struct qb_attr_code code_sdqcr_dqsrc = QB_CODE(0, 0, 16); -+ -+/*********************************/ -+/* Portal constructor/destructor */ -+/*********************************/ -+ -+/* Software portals should always be in the power-on state when we initialise, -+ * due to the CCSR-based portal reset functionality that MC has. -+ * -+ * Erk! Turns out that QMan versions prior to 4.1 do not correctly reset DQRR -+ * valid-bits, so we need to support a workaround where we don't trust -+ * valid-bits when detecting new entries until any stale ring entries have been -+ * overwritten at least once. The idea is that we read PI for the first few -+ * entries, then switch to valid-bit after that. The trick is to clear the -+ * bug-work-around boolean once the PI wraps around the ring for the first time. -+ * -+ * Note: this still carries a slight additional cost once the decrementer hits -+ * zero, so ideally the workaround should only be compiled in if the compiled -+ * image needs to support affected chips. We use WORKAROUND_DQRR_RESET_BUG for -+ * this. -+ */ -+struct qbman_swp *qbman_swp_init(const struct qbman_swp_desc *d) -+{ -+ int ret; -+ struct qbman_swp *p = kmalloc(sizeof(*p), GFP_KERNEL); -+ -+ if (!p) -+ return NULL; -+ p->desc = d; -+#ifdef QBMAN_CHECKING -+ p->mc.check = swp_mc_can_start; -+#endif -+ p->mc.valid_bit = QB_VALID_BIT; -+ p->sdq = 0; -+ qb_attr_code_encode(&code_sdqcr_dct, &p->sdq, qbman_sdqcr_dct_prio_ics); -+ qb_attr_code_encode(&code_sdqcr_fc, &p->sdq, qbman_sdqcr_fc_up_to_3); -+ qb_attr_code_encode(&code_sdqcr_tok, &p->sdq, 0xbb); -+ atomic_set(&p->vdq.busy, 1); -+ p->vdq.valid_bit = QB_VALID_BIT; -+ p->dqrr.next_idx = 0; -+ p->dqrr.valid_bit = QB_VALID_BIT; -+ /* TODO: should also read PI/CI type registers and check that they're on -+ * PoR values. If we're asked to initialise portals that aren't in reset -+ * state, bad things will follow. */ -+#ifdef WORKAROUND_DQRR_RESET_BUG -+ p->dqrr.reset_bug = 1; -+#endif -+ if ((p->desc->qman_version & 0xFFFF0000) < QMAN_REV_4100) -+ p->dqrr.dqrr_size = 4; -+ else -+ p->dqrr.dqrr_size = 8; -+ ret = qbman_swp_sys_init(&p->sys, d, p->dqrr.dqrr_size); -+ if (ret) { -+ kfree(p); -+ pr_err("qbman_swp_sys_init() failed %d\n", ret); -+ return NULL; -+ } -+ /* SDQCR needs to be initialized to 0 when no channels are -+ being dequeued from or else the QMan HW will indicate an -+ error. The values that were calculated above will be -+ applied when dequeues from a specific channel are enabled */ -+ qbman_cinh_write(&p->sys, QBMAN_CINH_SWP_SDQCR, 0); -+ return p; -+} -+ -+void qbman_swp_finish(struct qbman_swp *p) -+{ -+#ifdef QBMAN_CHECKING -+ BUG_ON(p->mc.check != swp_mc_can_start); -+#endif -+ qbman_swp_sys_finish(&p->sys); -+ kfree(p); -+} -+ -+const struct qbman_swp_desc *qbman_swp_get_desc(struct qbman_swp *p) -+{ -+ return p->desc; -+} -+ -+/**************/ -+/* Interrupts */ -+/**************/ -+ -+uint32_t qbman_swp_interrupt_get_vanish(struct qbman_swp *p) -+{ -+ return qbman_cinh_read(&p->sys, QBMAN_CINH_SWP_ISDR); -+} -+ -+void qbman_swp_interrupt_set_vanish(struct qbman_swp *p, uint32_t mask) -+{ -+ qbman_cinh_write(&p->sys, QBMAN_CINH_SWP_ISDR, mask); -+} -+ -+uint32_t qbman_swp_interrupt_read_status(struct qbman_swp *p) -+{ -+ return qbman_cinh_read(&p->sys, QBMAN_CINH_SWP_ISR); -+} -+ -+void qbman_swp_interrupt_clear_status(struct qbman_swp *p, uint32_t mask) -+{ -+ qbman_cinh_write(&p->sys, QBMAN_CINH_SWP_ISR, mask); -+} -+ -+uint32_t qbman_swp_interrupt_get_trigger(struct qbman_swp *p) -+{ -+ return qbman_cinh_read(&p->sys, QBMAN_CINH_SWP_IER); -+} -+ -+void qbman_swp_interrupt_set_trigger(struct qbman_swp *p, uint32_t mask) -+{ -+ qbman_cinh_write(&p->sys, QBMAN_CINH_SWP_IER, mask); -+} -+ -+int qbman_swp_interrupt_get_inhibit(struct qbman_swp *p) -+{ -+ return qbman_cinh_read(&p->sys, QBMAN_CINH_SWP_IIR); -+} -+ -+void qbman_swp_interrupt_set_inhibit(struct qbman_swp *p, int inhibit) -+{ -+ qbman_cinh_write(&p->sys, QBMAN_CINH_SWP_IIR, inhibit ? 0xffffffff : 0); -+} -+ -+/***********************/ -+/* Management commands */ -+/***********************/ -+ -+/* -+ * Internal code common to all types of management commands. -+ */ -+ -+void *qbman_swp_mc_start(struct qbman_swp *p) -+{ -+ void *ret; -+#ifdef QBMAN_CHECKING -+ BUG_ON(p->mc.check != swp_mc_can_start); -+#endif -+ ret = qbman_cena_write_start(&p->sys, QBMAN_CENA_SWP_CR); -+#ifdef QBMAN_CHECKING -+ if (!ret) -+ p->mc.check = swp_mc_can_submit; -+#endif -+ return ret; -+} -+ -+void qbman_swp_mc_submit(struct qbman_swp *p, void *cmd, uint32_t cmd_verb) -+{ -+ uint32_t *v = cmd; -+#ifdef QBMAN_CHECKING -+ BUG_ON(!p->mc.check != swp_mc_can_submit); -+#endif -+ /* TBD: "|=" is going to hurt performance. Need to move as many fields -+ * out of word zero, and for those that remain, the "OR" needs to occur -+ * at the caller side. This debug check helps to catch cases where the -+ * caller wants to OR but has forgotten to do so. */ -+ BUG_ON((*v & cmd_verb) != *v); -+ *v = cmd_verb | p->mc.valid_bit; -+ qbman_cena_write_complete(&p->sys, QBMAN_CENA_SWP_CR, cmd); -+#ifdef QBMAN_CHECKING -+ p->mc.check = swp_mc_can_poll; -+#endif -+} -+ -+void *qbman_swp_mc_result(struct qbman_swp *p) -+{ -+ uint32_t *ret, verb; -+#ifdef QBMAN_CHECKING -+ BUG_ON(p->mc.check != swp_mc_can_poll); -+#endif -+ qbman_cena_invalidate_prefetch(&p->sys, -+ QBMAN_CENA_SWP_RR(p->mc.valid_bit)); -+ ret = qbman_cena_read(&p->sys, QBMAN_CENA_SWP_RR(p->mc.valid_bit)); -+ /* Remove the valid-bit - command completed iff the rest is non-zero */ -+ verb = ret[0] & ~QB_VALID_BIT; -+ if (!verb) -+ return NULL; -+#ifdef QBMAN_CHECKING -+ p->mc.check = swp_mc_can_start; -+#endif -+ p->mc.valid_bit ^= QB_VALID_BIT; -+ return ret; -+} -+ -+/***********/ -+/* Enqueue */ -+/***********/ -+ -+/* These should be const, eventually */ -+static struct qb_attr_code code_eq_cmd = QB_CODE(0, 0, 2); -+static struct qb_attr_code code_eq_eqdi = QB_CODE(0, 3, 1); -+static struct qb_attr_code code_eq_dca_en = QB_CODE(0, 15, 1); -+static struct qb_attr_code code_eq_dca_pk = QB_CODE(0, 14, 1); -+static struct qb_attr_code code_eq_dca_idx = QB_CODE(0, 8, 2); -+static struct qb_attr_code code_eq_orp_en = QB_CODE(0, 2, 1); -+static struct qb_attr_code code_eq_orp_is_nesn = QB_CODE(0, 31, 1); -+static struct qb_attr_code code_eq_orp_nlis = QB_CODE(0, 30, 1); -+static struct qb_attr_code code_eq_orp_seqnum = QB_CODE(0, 16, 14); -+static struct qb_attr_code code_eq_opr_id = QB_CODE(1, 0, 16); -+static struct qb_attr_code code_eq_tgt_id = QB_CODE(2, 0, 24); -+/* static struct qb_attr_code code_eq_tag = QB_CODE(3, 0, 32); */ -+static struct qb_attr_code code_eq_qd_en = QB_CODE(0, 4, 1); -+static struct qb_attr_code code_eq_qd_bin = QB_CODE(4, 0, 16); -+static struct qb_attr_code code_eq_qd_pri = QB_CODE(4, 16, 4); -+static struct qb_attr_code code_eq_rsp_stash = QB_CODE(5, 16, 1); -+static struct qb_attr_code code_eq_rsp_id = QB_CODE(5, 24, 8); -+static struct qb_attr_code code_eq_rsp_lo = QB_CODE(6, 0, 32); -+ -+enum qbman_eq_cmd_e { -+ /* No enqueue, primarily for plugging ORP gaps for dropped frames */ -+ qbman_eq_cmd_empty, -+ /* DMA an enqueue response once complete */ -+ qbman_eq_cmd_respond, -+ /* DMA an enqueue response only if the enqueue fails */ -+ qbman_eq_cmd_respond_reject -+}; -+ -+void qbman_eq_desc_clear(struct qbman_eq_desc *d) -+{ -+ memset(d, 0, sizeof(*d)); -+} -+ -+void qbman_eq_desc_set_no_orp(struct qbman_eq_desc *d, int respond_success) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ qb_attr_code_encode(&code_eq_orp_en, cl, 0); -+ qb_attr_code_encode(&code_eq_cmd, cl, -+ respond_success ? qbman_eq_cmd_respond : -+ qbman_eq_cmd_respond_reject); -+} -+ -+void qbman_eq_desc_set_orp(struct qbman_eq_desc *d, int respond_success, -+ uint32_t opr_id, uint32_t seqnum, int incomplete) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ qb_attr_code_encode(&code_eq_orp_en, cl, 1); -+ qb_attr_code_encode(&code_eq_cmd, cl, -+ respond_success ? qbman_eq_cmd_respond : -+ qbman_eq_cmd_respond_reject); -+ qb_attr_code_encode(&code_eq_opr_id, cl, opr_id); -+ qb_attr_code_encode(&code_eq_orp_seqnum, cl, seqnum); -+ qb_attr_code_encode(&code_eq_orp_nlis, cl, !!incomplete); -+} -+ -+void qbman_eq_desc_set_orp_hole(struct qbman_eq_desc *d, uint32_t opr_id, -+ uint32_t seqnum) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ qb_attr_code_encode(&code_eq_orp_en, cl, 1); -+ qb_attr_code_encode(&code_eq_cmd, cl, qbman_eq_cmd_empty); -+ qb_attr_code_encode(&code_eq_opr_id, cl, opr_id); -+ qb_attr_code_encode(&code_eq_orp_seqnum, cl, seqnum); -+ qb_attr_code_encode(&code_eq_orp_nlis, cl, 0); -+ qb_attr_code_encode(&code_eq_orp_is_nesn, cl, 0); -+} -+ -+void qbman_eq_desc_set_orp_nesn(struct qbman_eq_desc *d, uint32_t opr_id, -+ uint32_t seqnum) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ qb_attr_code_encode(&code_eq_orp_en, cl, 1); -+ qb_attr_code_encode(&code_eq_cmd, cl, qbman_eq_cmd_empty); -+ qb_attr_code_encode(&code_eq_opr_id, cl, opr_id); -+ qb_attr_code_encode(&code_eq_orp_seqnum, cl, seqnum); -+ qb_attr_code_encode(&code_eq_orp_nlis, cl, 0); -+ qb_attr_code_encode(&code_eq_orp_is_nesn, cl, 1); -+} -+ -+void qbman_eq_desc_set_response(struct qbman_eq_desc *d, -+ dma_addr_t storage_phys, -+ int stash) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ qb_attr_code_encode_64(&code_eq_rsp_lo, (uint64_t *)cl, storage_phys); -+ qb_attr_code_encode(&code_eq_rsp_stash, cl, !!stash); -+} -+ -+void qbman_eq_desc_set_token(struct qbman_eq_desc *d, uint8_t token) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ qb_attr_code_encode(&code_eq_rsp_id, cl, (uint32_t)token); -+} -+ -+void qbman_eq_desc_set_fq(struct qbman_eq_desc *d, uint32_t fqid) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ qb_attr_code_encode(&code_eq_qd_en, cl, 0); -+ qb_attr_code_encode(&code_eq_tgt_id, cl, fqid); -+} -+ -+void qbman_eq_desc_set_qd(struct qbman_eq_desc *d, uint32_t qdid, -+ uint32_t qd_bin, uint32_t qd_prio) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ qb_attr_code_encode(&code_eq_qd_en, cl, 1); -+ qb_attr_code_encode(&code_eq_tgt_id, cl, qdid); -+ qb_attr_code_encode(&code_eq_qd_bin, cl, qd_bin); -+ qb_attr_code_encode(&code_eq_qd_pri, cl, qd_prio); -+} -+ -+void qbman_eq_desc_set_eqdi(struct qbman_eq_desc *d, int enable) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ qb_attr_code_encode(&code_eq_eqdi, cl, !!enable); -+} -+ -+void qbman_eq_desc_set_dca(struct qbman_eq_desc *d, int enable, -+ uint32_t dqrr_idx, int park) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ qb_attr_code_encode(&code_eq_dca_en, cl, !!enable); -+ if (enable) { -+ qb_attr_code_encode(&code_eq_dca_pk, cl, !!park); -+ qb_attr_code_encode(&code_eq_dca_idx, cl, dqrr_idx); -+ } -+} -+ -+#define EQAR_IDX(eqar) ((eqar) & 0x7) -+#define EQAR_VB(eqar) ((eqar) & 0x80) -+#define EQAR_SUCCESS(eqar) ((eqar) & 0x100) -+ -+int qbman_swp_enqueue(struct qbman_swp *s, const struct qbman_eq_desc *d, -+ const struct qbman_fd *fd) -+{ -+ uint32_t *p; -+ const uint32_t *cl = qb_cl(d); -+ uint32_t eqar = qbman_cinh_read(&s->sys, QBMAN_CINH_SWP_EQAR); -+ -+ pr_debug("EQAR=%08x\n", eqar); -+ if (!EQAR_SUCCESS(eqar)) -+ return -EBUSY; -+ p = qbman_cena_write_start(&s->sys, -+ QBMAN_CENA_SWP_EQCR(EQAR_IDX(eqar))); -+ word_copy(&p[1], &cl[1], 7); -+ word_copy(&p[8], fd, sizeof(*fd) >> 2); -+ /* Set the verb byte, have to substitute in the valid-bit */ -+ p[0] = cl[0] | EQAR_VB(eqar); -+ qbman_cena_write_complete(&s->sys, -+ QBMAN_CENA_SWP_EQCR(EQAR_IDX(eqar)), -+ p); -+ return 0; -+} -+ -+/*************************/ -+/* Static (push) dequeue */ -+/*************************/ -+ -+void qbman_swp_push_get(struct qbman_swp *s, uint8_t channel_idx, int *enabled) -+{ -+ struct qb_attr_code code = CODE_SDQCR_DQSRC(channel_idx); -+ -+ BUG_ON(channel_idx > 15); -+ *enabled = (int)qb_attr_code_decode(&code, &s->sdq); -+} -+ -+void qbman_swp_push_set(struct qbman_swp *s, uint8_t channel_idx, int enable) -+{ -+ uint16_t dqsrc; -+ struct qb_attr_code code = CODE_SDQCR_DQSRC(channel_idx); -+ -+ BUG_ON(channel_idx > 15); -+ qb_attr_code_encode(&code, &s->sdq, !!enable); -+ /* Read make the complete src map. If no channels are enabled -+ the SDQCR must be 0 or else QMan will assert errors */ -+ dqsrc = (uint16_t)qb_attr_code_decode(&code_sdqcr_dqsrc, &s->sdq); -+ if (dqsrc != 0) -+ qbman_cinh_write(&s->sys, QBMAN_CINH_SWP_SDQCR, s->sdq); -+ else -+ qbman_cinh_write(&s->sys, QBMAN_CINH_SWP_SDQCR, 0); -+} -+ -+/***************************/ -+/* Volatile (pull) dequeue */ -+/***************************/ -+ -+/* These should be const, eventually */ -+static struct qb_attr_code code_pull_dct = QB_CODE(0, 0, 2); -+static struct qb_attr_code code_pull_dt = QB_CODE(0, 2, 2); -+static struct qb_attr_code code_pull_rls = QB_CODE(0, 4, 1); -+static struct qb_attr_code code_pull_stash = QB_CODE(0, 5, 1); -+static struct qb_attr_code code_pull_numframes = QB_CODE(0, 8, 4); -+static struct qb_attr_code code_pull_token = QB_CODE(0, 16, 8); -+static struct qb_attr_code code_pull_dqsource = QB_CODE(1, 0, 24); -+static struct qb_attr_code code_pull_rsp_lo = QB_CODE(2, 0, 32); -+ -+enum qb_pull_dt_e { -+ qb_pull_dt_channel, -+ qb_pull_dt_workqueue, -+ qb_pull_dt_framequeue -+}; -+ -+void qbman_pull_desc_clear(struct qbman_pull_desc *d) -+{ -+ memset(d, 0, sizeof(*d)); -+} -+ -+void qbman_pull_desc_set_storage(struct qbman_pull_desc *d, -+ struct dpaa2_dq *storage, -+ dma_addr_t storage_phys, -+ int stash) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ /* Squiggle the pointer 'storage' into the extra 2 words of the -+ * descriptor (which aren't copied to the hw command) */ -+ *(void **)&cl[4] = storage; -+ if (!storage) { -+ qb_attr_code_encode(&code_pull_rls, cl, 0); -+ return; -+ } -+ qb_attr_code_encode(&code_pull_rls, cl, 1); -+ qb_attr_code_encode(&code_pull_stash, cl, !!stash); -+ qb_attr_code_encode_64(&code_pull_rsp_lo, (uint64_t *)cl, storage_phys); -+} -+ -+void qbman_pull_desc_set_numframes(struct qbman_pull_desc *d, uint8_t numframes) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ BUG_ON(!numframes || (numframes > 16)); -+ qb_attr_code_encode(&code_pull_numframes, cl, -+ (uint32_t)(numframes - 1)); -+} -+ -+void qbman_pull_desc_set_token(struct qbman_pull_desc *d, uint8_t token) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ qb_attr_code_encode(&code_pull_token, cl, token); -+} -+ -+void qbman_pull_desc_set_fq(struct qbman_pull_desc *d, uint32_t fqid) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ qb_attr_code_encode(&code_pull_dct, cl, 1); -+ qb_attr_code_encode(&code_pull_dt, cl, qb_pull_dt_framequeue); -+ qb_attr_code_encode(&code_pull_dqsource, cl, fqid); -+} -+ -+void qbman_pull_desc_set_wq(struct qbman_pull_desc *d, uint32_t wqid, -+ enum qbman_pull_type_e dct) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ qb_attr_code_encode(&code_pull_dct, cl, dct); -+ qb_attr_code_encode(&code_pull_dt, cl, qb_pull_dt_workqueue); -+ qb_attr_code_encode(&code_pull_dqsource, cl, wqid); -+} -+ -+void qbman_pull_desc_set_channel(struct qbman_pull_desc *d, uint32_t chid, -+ enum qbman_pull_type_e dct) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ qb_attr_code_encode(&code_pull_dct, cl, dct); -+ qb_attr_code_encode(&code_pull_dt, cl, qb_pull_dt_channel); -+ qb_attr_code_encode(&code_pull_dqsource, cl, chid); -+} -+ -+int qbman_swp_pull(struct qbman_swp *s, struct qbman_pull_desc *d) -+{ -+ uint32_t *p; -+ uint32_t *cl = qb_cl(d); -+ -+ if (!atomic_dec_and_test(&s->vdq.busy)) { -+ atomic_inc(&s->vdq.busy); -+ return -EBUSY; -+ } -+ s->vdq.storage = *(void **)&cl[4]; -+ qb_attr_code_encode(&code_pull_token, cl, 1); -+ p = qbman_cena_write_start(&s->sys, QBMAN_CENA_SWP_VDQCR); -+ word_copy(&p[1], &cl[1], 3); -+ /* Set the verb byte, have to substitute in the valid-bit */ -+ p[0] = cl[0] | s->vdq.valid_bit; -+ s->vdq.valid_bit ^= QB_VALID_BIT; -+ qbman_cena_write_complete(&s->sys, QBMAN_CENA_SWP_VDQCR, p); -+ return 0; -+} -+ -+/****************/ -+/* Polling DQRR */ -+/****************/ -+ -+static struct qb_attr_code code_dqrr_verb = QB_CODE(0, 0, 8); -+static struct qb_attr_code code_dqrr_response = QB_CODE(0, 0, 7); -+static struct qb_attr_code code_dqrr_stat = QB_CODE(0, 8, 8); -+static struct qb_attr_code code_dqrr_seqnum = QB_CODE(0, 16, 14); -+static struct qb_attr_code code_dqrr_odpid = QB_CODE(1, 0, 16); -+/* static struct qb_attr_code code_dqrr_tok = QB_CODE(1, 24, 8); */ -+static struct qb_attr_code code_dqrr_fqid = QB_CODE(2, 0, 24); -+static struct qb_attr_code code_dqrr_byte_count = QB_CODE(4, 0, 32); -+static struct qb_attr_code code_dqrr_frame_count = QB_CODE(5, 0, 24); -+static struct qb_attr_code code_dqrr_ctx_lo = QB_CODE(6, 0, 32); -+ -+#define QBMAN_RESULT_DQ 0x60 -+#define QBMAN_RESULT_FQRN 0x21 -+#define QBMAN_RESULT_FQRNI 0x22 -+#define QBMAN_RESULT_FQPN 0x24 -+#define QBMAN_RESULT_FQDAN 0x25 -+#define QBMAN_RESULT_CDAN 0x26 -+#define QBMAN_RESULT_CSCN_MEM 0x27 -+#define QBMAN_RESULT_CGCU 0x28 -+#define QBMAN_RESULT_BPSCN 0x29 -+#define QBMAN_RESULT_CSCN_WQ 0x2a -+ -+static struct qb_attr_code code_dqpi_pi = QB_CODE(0, 0, 4); -+ -+/* NULL return if there are no unconsumed DQRR entries. Returns a DQRR entry -+ * only once, so repeated calls can return a sequence of DQRR entries, without -+ * requiring they be consumed immediately or in any particular order. */ -+const struct dpaa2_dq *qbman_swp_dqrr_next(struct qbman_swp *s) -+{ -+ uint32_t verb; -+ uint32_t response_verb; -+ uint32_t flags; -+ const struct dpaa2_dq *dq; -+ const uint32_t *p; -+ -+ /* Before using valid-bit to detect if something is there, we have to -+ * handle the case of the DQRR reset bug... */ -+#ifdef WORKAROUND_DQRR_RESET_BUG -+ if (unlikely(s->dqrr.reset_bug)) { -+ /* We pick up new entries by cache-inhibited producer index, -+ * which means that a non-coherent mapping would require us to -+ * invalidate and read *only* once that PI has indicated that -+ * there's an entry here. The first trip around the DQRR ring -+ * will be much less efficient than all subsequent trips around -+ * it... -+ */ -+ uint32_t dqpi = qbman_cinh_read(&s->sys, QBMAN_CINH_SWP_DQPI); -+ uint32_t pi = qb_attr_code_decode(&code_dqpi_pi, &dqpi); -+ /* there are new entries iff pi != next_idx */ -+ if (pi == s->dqrr.next_idx) -+ return NULL; -+ /* if next_idx is/was the last ring index, and 'pi' is -+ * different, we can disable the workaround as all the ring -+ * entries have now been DMA'd to so valid-bit checking is -+ * repaired. Note: this logic needs to be based on next_idx -+ * (which increments one at a time), rather than on pi (which -+ * can burst and wrap-around between our snapshots of it). -+ */ -+ if (s->dqrr.next_idx == (s->dqrr.dqrr_size - 1)) { -+ pr_debug("DEBUG: next_idx=%d, pi=%d, clear reset bug\n", -+ s->dqrr.next_idx, pi); -+ s->dqrr.reset_bug = 0; -+ } -+ qbman_cena_invalidate_prefetch(&s->sys, -+ QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx)); -+ } -+#endif -+ -+ dq = qbman_cena_read(&s->sys, QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx)); -+ p = qb_cl(dq); -+ verb = qb_attr_code_decode(&code_dqrr_verb, p); -+ -+ /* If the valid-bit isn't of the expected polarity, nothing there. Note, -+ * in the DQRR reset bug workaround, we shouldn't need to skip these -+ * check, because we've already determined that a new entry is available -+ * and we've invalidated the cacheline before reading it, so the -+ * valid-bit behaviour is repaired and should tell us what we already -+ * knew from reading PI. -+ */ -+ if ((verb & QB_VALID_BIT) != s->dqrr.valid_bit) { -+ qbman_cena_invalidate_prefetch(&s->sys, -+ QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx)); -+ return NULL; -+ } -+ /* There's something there. Move "next_idx" attention to the next ring -+ * entry (and prefetch it) before returning what we found. */ -+ s->dqrr.next_idx++; -+ s->dqrr.next_idx &= s->dqrr.dqrr_size - 1; /* Wrap around */ -+ /* TODO: it's possible to do all this without conditionals, optimise it -+ * later. */ -+ if (!s->dqrr.next_idx) -+ s->dqrr.valid_bit ^= QB_VALID_BIT; -+ -+ /* If this is the final response to a volatile dequeue command -+ indicate that the vdq is no longer busy */ -+ flags = dpaa2_dq_flags(dq); -+ response_verb = qb_attr_code_decode(&code_dqrr_response, &verb); -+ if ((response_verb == QBMAN_RESULT_DQ) && -+ (flags & DPAA2_DQ_STAT_VOLATILE) && -+ (flags & DPAA2_DQ_STAT_EXPIRED)) -+ atomic_inc(&s->vdq.busy); -+ -+ qbman_cena_invalidate_prefetch(&s->sys, -+ QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx)); -+ return dq; -+} -+ -+/* Consume DQRR entries previously returned from qbman_swp_dqrr_next(). */ -+void qbman_swp_dqrr_consume(struct qbman_swp *s, const struct dpaa2_dq *dq) -+{ -+ qbman_cinh_write(&s->sys, QBMAN_CINH_SWP_DCAP, QBMAN_IDX_FROM_DQRR(dq)); -+} -+ -+/*********************************/ -+/* Polling user-provided storage */ -+/*********************************/ -+ -+int qbman_result_has_new_result(struct qbman_swp *s, -+ const struct dpaa2_dq *dq) -+{ -+ /* To avoid converting the little-endian DQ entry to host-endian prior -+ * to us knowing whether there is a valid entry or not (and run the -+ * risk of corrupting the incoming hardware LE write), we detect in -+ * hardware endianness rather than host. This means we need a different -+ * "code" depending on whether we are BE or LE in software, which is -+ * where DQRR_TOK_OFFSET comes in... */ -+ static struct qb_attr_code code_dqrr_tok_detect = -+ QB_CODE(0, DQRR_TOK_OFFSET, 8); -+ /* The user trying to poll for a result treats "dq" as const. It is -+ * however the same address that was provided to us non-const in the -+ * first place, for directing hardware DMA to. So we can cast away the -+ * const because it is mutable from our perspective. */ -+ uint32_t *p = qb_cl((struct dpaa2_dq *)dq); -+ uint32_t token; -+ -+ token = qb_attr_code_decode(&code_dqrr_tok_detect, &p[1]); -+ if (token != 1) -+ return 0; -+ qb_attr_code_encode(&code_dqrr_tok_detect, &p[1], 0); -+ -+ /* Only now do we convert from hardware to host endianness. Also, as we -+ * are returning success, the user has promised not to call us again, so -+ * there's no risk of us converting the endianness twice... */ -+ make_le32_n(p, 16); -+ -+ /* VDQCR "no longer busy" hook - not quite the same as DQRR, because the -+ * fact "VDQCR" shows busy doesn't mean that the result we're looking at -+ * is from the same command. Eg. we may be looking at our 10th dequeue -+ * result from our first VDQCR command, yet the second dequeue command -+ * could have been kicked off already, after seeing the 1st result. Ie. -+ * the result we're looking at is not necessarily proof that we can -+ * reset "busy". We instead base the decision on whether the current -+ * result is sitting at the first 'storage' location of the busy -+ * command. */ -+ if (s->vdq.storage == dq) { -+ s->vdq.storage = NULL; -+ atomic_inc(&s->vdq.busy); -+ } -+ return 1; -+} -+ -+/********************************/ -+/* Categorising qbman_result */ -+/********************************/ -+ -+static struct qb_attr_code code_result_in_mem = -+ QB_CODE(0, QBMAN_RESULT_VERB_OFFSET_IN_MEM, 7); -+ -+static inline int __qbman_result_is_x(const struct dpaa2_dq *dq, uint32_t x) -+{ -+ const uint32_t *p = qb_cl(dq); -+ uint32_t response_verb = qb_attr_code_decode(&code_dqrr_response, p); -+ -+ return response_verb == x; -+} -+ -+static inline int __qbman_result_is_x_in_mem(const struct dpaa2_dq *dq, -+ uint32_t x) -+{ -+ const uint32_t *p = qb_cl(dq); -+ uint32_t response_verb = qb_attr_code_decode(&code_result_in_mem, p); -+ -+ return (response_verb == x); -+} -+ -+int qbman_result_is_DQ(const struct dpaa2_dq *dq) -+{ -+ return __qbman_result_is_x(dq, QBMAN_RESULT_DQ); -+} -+ -+int qbman_result_is_FQDAN(const struct dpaa2_dq *dq) -+{ -+ return __qbman_result_is_x(dq, QBMAN_RESULT_FQDAN); -+} -+ -+int qbman_result_is_CDAN(const struct dpaa2_dq *dq) -+{ -+ return __qbman_result_is_x(dq, QBMAN_RESULT_CDAN); -+} -+ -+int qbman_result_is_CSCN(const struct dpaa2_dq *dq) -+{ -+ return __qbman_result_is_x_in_mem(dq, QBMAN_RESULT_CSCN_MEM) || -+ __qbman_result_is_x(dq, QBMAN_RESULT_CSCN_WQ); -+} -+ -+int qbman_result_is_BPSCN(const struct dpaa2_dq *dq) -+{ -+ return __qbman_result_is_x_in_mem(dq, QBMAN_RESULT_BPSCN); -+} -+ -+int qbman_result_is_CGCU(const struct dpaa2_dq *dq) -+{ -+ return __qbman_result_is_x_in_mem(dq, QBMAN_RESULT_CGCU); -+} -+ -+int qbman_result_is_FQRN(const struct dpaa2_dq *dq) -+{ -+ return __qbman_result_is_x_in_mem(dq, QBMAN_RESULT_FQRN); -+} -+ -+int qbman_result_is_FQRNI(const struct dpaa2_dq *dq) -+{ -+ return __qbman_result_is_x_in_mem(dq, QBMAN_RESULT_FQRNI); -+} -+ -+int qbman_result_is_FQPN(const struct dpaa2_dq *dq) -+{ -+ return __qbman_result_is_x(dq, QBMAN_RESULT_FQPN); -+} -+ -+/*********************************/ -+/* Parsing frame dequeue results */ -+/*********************************/ -+ -+/* These APIs assume qbman_result_is_DQ() is TRUE */ -+ -+uint32_t dpaa2_dq_flags(const struct dpaa2_dq *dq) -+{ -+ const uint32_t *p = qb_cl(dq); -+ -+ return qb_attr_code_decode(&code_dqrr_stat, p); -+} -+ -+uint16_t dpaa2_dq_seqnum(const struct dpaa2_dq *dq) -+{ -+ const uint32_t *p = qb_cl(dq); -+ -+ return (uint16_t)qb_attr_code_decode(&code_dqrr_seqnum, p); -+} -+ -+uint16_t dpaa2_dq_odpid(const struct dpaa2_dq *dq) -+{ -+ const uint32_t *p = qb_cl(dq); -+ -+ return (uint16_t)qb_attr_code_decode(&code_dqrr_odpid, p); -+} -+ -+uint32_t dpaa2_dq_fqid(const struct dpaa2_dq *dq) -+{ -+ const uint32_t *p = qb_cl(dq); -+ -+ return qb_attr_code_decode(&code_dqrr_fqid, p); -+} -+ -+uint32_t dpaa2_dq_byte_count(const struct dpaa2_dq *dq) -+{ -+ const uint32_t *p = qb_cl(dq); -+ -+ return qb_attr_code_decode(&code_dqrr_byte_count, p); -+} -+ -+uint32_t dpaa2_dq_frame_count(const struct dpaa2_dq *dq) -+{ -+ const uint32_t *p = qb_cl(dq); -+ -+ return qb_attr_code_decode(&code_dqrr_frame_count, p); -+} -+ -+uint64_t dpaa2_dq_fqd_ctx(const struct dpaa2_dq *dq) -+{ -+ const uint64_t *p = (uint64_t *)qb_cl(dq); -+ -+ return qb_attr_code_decode_64(&code_dqrr_ctx_lo, p); -+} -+EXPORT_SYMBOL(dpaa2_dq_fqd_ctx); -+ -+const struct dpaa2_fd *dpaa2_dq_fd(const struct dpaa2_dq *dq) -+{ -+ const uint32_t *p = qb_cl(dq); -+ -+ return (const struct dpaa2_fd *)&p[8]; -+} -+EXPORT_SYMBOL(dpaa2_dq_fd); -+ -+/**************************************/ -+/* Parsing state-change notifications */ -+/**************************************/ -+ -+static struct qb_attr_code code_scn_state = QB_CODE(0, 16, 8); -+static struct qb_attr_code code_scn_rid = QB_CODE(1, 0, 24); -+static struct qb_attr_code code_scn_state_in_mem = -+ QB_CODE(0, SCN_STATE_OFFSET_IN_MEM, 8); -+static struct qb_attr_code code_scn_rid_in_mem = -+ QB_CODE(1, SCN_RID_OFFSET_IN_MEM, 24); -+static struct qb_attr_code code_scn_ctx_lo = QB_CODE(2, 0, 32); -+ -+uint8_t qbman_result_SCN_state(const struct dpaa2_dq *scn) -+{ -+ const uint32_t *p = qb_cl(scn); -+ -+ return (uint8_t)qb_attr_code_decode(&code_scn_state, p); -+} -+ -+uint32_t qbman_result_SCN_rid(const struct dpaa2_dq *scn) -+{ -+ const uint32_t *p = qb_cl(scn); -+ -+ return qb_attr_code_decode(&code_scn_rid, p); -+} -+ -+uint64_t qbman_result_SCN_ctx(const struct dpaa2_dq *scn) -+{ -+ const uint64_t *p = (uint64_t *)qb_cl(scn); -+ -+ return qb_attr_code_decode_64(&code_scn_ctx_lo, p); -+} -+ -+uint8_t qbman_result_SCN_state_in_mem(const struct dpaa2_dq *scn) -+{ -+ const uint32_t *p = qb_cl(scn); -+ -+ return (uint8_t)qb_attr_code_decode(&code_scn_state_in_mem, p); -+} -+ -+uint32_t qbman_result_SCN_rid_in_mem(const struct dpaa2_dq *scn) -+{ -+ const uint32_t *p = qb_cl(scn); -+ uint32_t result_rid; -+ -+ result_rid = qb_attr_code_decode(&code_scn_rid_in_mem, p); -+ return make_le24(result_rid); -+} -+ -+/*****************/ -+/* Parsing BPSCN */ -+/*****************/ -+uint16_t qbman_result_bpscn_bpid(const struct dpaa2_dq *scn) -+{ -+ return (uint16_t)qbman_result_SCN_rid_in_mem(scn) & 0x3FFF; -+} -+ -+int qbman_result_bpscn_has_free_bufs(const struct dpaa2_dq *scn) -+{ -+ return !(int)(qbman_result_SCN_state_in_mem(scn) & 0x1); -+} -+ -+int qbman_result_bpscn_is_depleted(const struct dpaa2_dq *scn) -+{ -+ return (int)(qbman_result_SCN_state_in_mem(scn) & 0x2); -+} -+ -+int qbman_result_bpscn_is_surplus(const struct dpaa2_dq *scn) -+{ -+ return (int)(qbman_result_SCN_state_in_mem(scn) & 0x4); -+} -+ -+uint64_t qbman_result_bpscn_ctx(const struct dpaa2_dq *scn) -+{ -+ return qbman_result_SCN_ctx(scn); -+} -+ -+/*****************/ -+/* Parsing CGCU */ -+/*****************/ -+uint16_t qbman_result_cgcu_cgid(const struct dpaa2_dq *scn) -+{ -+ return (uint16_t)qbman_result_SCN_rid_in_mem(scn) & 0xFFFF; -+} -+ -+uint64_t qbman_result_cgcu_icnt(const struct dpaa2_dq *scn) -+{ -+ return qbman_result_SCN_ctx(scn) & 0xFFFFFFFFFF; -+} -+ -+/******************/ -+/* Buffer release */ -+/******************/ -+ -+/* These should be const, eventually */ -+/* static struct qb_attr_code code_release_num = QB_CODE(0, 0, 3); */ -+static struct qb_attr_code code_release_set_me = QB_CODE(0, 5, 1); -+static struct qb_attr_code code_release_rcdi = QB_CODE(0, 6, 1); -+static struct qb_attr_code code_release_bpid = QB_CODE(0, 16, 16); -+ -+void qbman_release_desc_clear(struct qbman_release_desc *d) -+{ -+ uint32_t *cl; -+ -+ memset(d, 0, sizeof(*d)); -+ cl = qb_cl(d); -+ qb_attr_code_encode(&code_release_set_me, cl, 1); -+} -+ -+void qbman_release_desc_set_bpid(struct qbman_release_desc *d, uint32_t bpid) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ qb_attr_code_encode(&code_release_bpid, cl, bpid); -+} -+ -+void qbman_release_desc_set_rcdi(struct qbman_release_desc *d, int enable) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ qb_attr_code_encode(&code_release_rcdi, cl, !!enable); -+} -+ -+#define RAR_IDX(rar) ((rar) & 0x7) -+#define RAR_VB(rar) ((rar) & 0x80) -+#define RAR_SUCCESS(rar) ((rar) & 0x100) -+ -+int qbman_swp_release(struct qbman_swp *s, const struct qbman_release_desc *d, -+ const uint64_t *buffers, unsigned int num_buffers) -+{ -+ uint32_t *p; -+ const uint32_t *cl = qb_cl(d); -+ uint32_t rar = qbman_cinh_read(&s->sys, QBMAN_CINH_SWP_RAR); -+ -+ pr_debug("RAR=%08x\n", rar); -+ if (!RAR_SUCCESS(rar)) -+ return -EBUSY; -+ BUG_ON(!num_buffers || (num_buffers > 7)); -+ /* Start the release command */ -+ p = qbman_cena_write_start(&s->sys, -+ QBMAN_CENA_SWP_RCR(RAR_IDX(rar))); -+ /* Copy the caller's buffer pointers to the command */ -+ u64_to_le32_copy(&p[2], buffers, num_buffers); -+ /* Set the verb byte, have to substitute in the valid-bit and the number -+ * of buffers. */ -+ p[0] = cl[0] | RAR_VB(rar) | num_buffers; -+ qbman_cena_write_complete(&s->sys, -+ QBMAN_CENA_SWP_RCR(RAR_IDX(rar)), -+ p); -+ return 0; -+} -+ -+/*******************/ -+/* Buffer acquires */ -+/*******************/ -+ -+/* These should be const, eventually */ -+static struct qb_attr_code code_acquire_bpid = QB_CODE(0, 16, 16); -+static struct qb_attr_code code_acquire_num = QB_CODE(1, 0, 3); -+static struct qb_attr_code code_acquire_r_num = QB_CODE(1, 0, 3); -+ -+int qbman_swp_acquire(struct qbman_swp *s, uint32_t bpid, uint64_t *buffers, -+ unsigned int num_buffers) -+{ -+ uint32_t *p; -+ uint32_t verb, rslt, num; -+ -+ BUG_ON(!num_buffers || (num_buffers > 7)); -+ -+ /* Start the management command */ -+ p = qbman_swp_mc_start(s); -+ -+ if (!p) -+ return -EBUSY; -+ -+ /* Encode the caller-provided attributes */ -+ qb_attr_code_encode(&code_acquire_bpid, p, bpid); -+ qb_attr_code_encode(&code_acquire_num, p, num_buffers); -+ -+ /* Complete the management command */ -+ p = qbman_swp_mc_complete(s, p, p[0] | QBMAN_MC_ACQUIRE); -+ -+ /* Decode the outcome */ -+ verb = qb_attr_code_decode(&code_generic_verb, p); -+ rslt = qb_attr_code_decode(&code_generic_rslt, p); -+ num = qb_attr_code_decode(&code_acquire_r_num, p); -+ BUG_ON(verb != QBMAN_MC_ACQUIRE); -+ -+ /* Determine success or failure */ -+ if (unlikely(rslt != QBMAN_MC_RSLT_OK)) { -+ pr_err("Acquire buffers from BPID 0x%x failed, code=0x%02x\n", -+ bpid, rslt); -+ return -EIO; -+ } -+ BUG_ON(num > num_buffers); -+ /* Copy the acquired buffers to the caller's array */ -+ u64_from_le32_copy(buffers, &p[2], num); -+ return (int)num; -+} -+ -+/*****************/ -+/* FQ management */ -+/*****************/ -+ -+static struct qb_attr_code code_fqalt_fqid = QB_CODE(1, 0, 32); -+ -+static int qbman_swp_alt_fq_state(struct qbman_swp *s, uint32_t fqid, -+ uint8_t alt_fq_verb) -+{ -+ uint32_t *p; -+ uint32_t verb, rslt; -+ -+ /* Start the management command */ -+ p = qbman_swp_mc_start(s); -+ if (!p) -+ return -EBUSY; -+ -+ qb_attr_code_encode(&code_fqalt_fqid, p, fqid); -+ /* Complete the management command */ -+ p = qbman_swp_mc_complete(s, p, p[0] | alt_fq_verb); -+ -+ /* Decode the outcome */ -+ verb = qb_attr_code_decode(&code_generic_verb, p); -+ rslt = qb_attr_code_decode(&code_generic_rslt, p); -+ BUG_ON(verb != alt_fq_verb); -+ -+ /* Determine success or failure */ -+ if (unlikely(rslt != QBMAN_MC_RSLT_OK)) { -+ pr_err("ALT FQID %d failed: verb = 0x%08x, code = 0x%02x\n", -+ fqid, alt_fq_verb, rslt); -+ return -EIO; -+ } -+ -+ return 0; -+} -+ -+int qbman_swp_fq_schedule(struct qbman_swp *s, uint32_t fqid) -+{ -+ return qbman_swp_alt_fq_state(s, fqid, QBMAN_FQ_SCHEDULE); -+} -+ -+int qbman_swp_fq_force(struct qbman_swp *s, uint32_t fqid) -+{ -+ return qbman_swp_alt_fq_state(s, fqid, QBMAN_FQ_FORCE); -+} -+ -+int qbman_swp_fq_xon(struct qbman_swp *s, uint32_t fqid) -+{ -+ return qbman_swp_alt_fq_state(s, fqid, QBMAN_FQ_XON); -+} -+ -+int qbman_swp_fq_xoff(struct qbman_swp *s, uint32_t fqid) -+{ -+ return qbman_swp_alt_fq_state(s, fqid, QBMAN_FQ_XOFF); -+} -+ -+/**********************/ -+/* Channel management */ -+/**********************/ -+ -+static struct qb_attr_code code_cdan_cid = QB_CODE(0, 16, 12); -+static struct qb_attr_code code_cdan_we = QB_CODE(1, 0, 8); -+static struct qb_attr_code code_cdan_en = QB_CODE(1, 8, 1); -+static struct qb_attr_code code_cdan_ctx_lo = QB_CODE(2, 0, 32); -+ -+/* Hide "ICD" for now as we don't use it, don't set it, and don't test it, so it -+ * would be irresponsible to expose it. */ -+#define CODE_CDAN_WE_EN 0x1 -+#define CODE_CDAN_WE_CTX 0x4 -+ -+static int qbman_swp_CDAN_set(struct qbman_swp *s, uint16_t channelid, -+ uint8_t we_mask, uint8_t cdan_en, -+ uint64_t ctx) -+{ -+ uint32_t *p; -+ uint32_t verb, rslt; -+ -+ /* Start the management command */ -+ p = qbman_swp_mc_start(s); -+ if (!p) -+ return -EBUSY; -+ -+ /* Encode the caller-provided attributes */ -+ qb_attr_code_encode(&code_cdan_cid, p, channelid); -+ qb_attr_code_encode(&code_cdan_we, p, we_mask); -+ qb_attr_code_encode(&code_cdan_en, p, cdan_en); -+ qb_attr_code_encode_64(&code_cdan_ctx_lo, (uint64_t *)p, ctx); -+ /* Complete the management command */ -+ p = qbman_swp_mc_complete(s, p, p[0] | QBMAN_WQCHAN_CONFIGURE); -+ -+ /* Decode the outcome */ -+ verb = qb_attr_code_decode(&code_generic_verb, p); -+ rslt = qb_attr_code_decode(&code_generic_rslt, p); -+ BUG_ON(verb != QBMAN_WQCHAN_CONFIGURE); -+ -+ /* Determine success or failure */ -+ if (unlikely(rslt != QBMAN_MC_RSLT_OK)) { -+ pr_err("CDAN cQID %d failed: code = 0x%02x\n", -+ channelid, rslt); -+ return -EIO; -+ } -+ -+ return 0; -+} -+ -+int qbman_swp_CDAN_set_context(struct qbman_swp *s, uint16_t channelid, -+ uint64_t ctx) -+{ -+ return qbman_swp_CDAN_set(s, channelid, -+ CODE_CDAN_WE_CTX, -+ 0, ctx); -+} -+ -+int qbman_swp_CDAN_enable(struct qbman_swp *s, uint16_t channelid) -+{ -+ return qbman_swp_CDAN_set(s, channelid, -+ CODE_CDAN_WE_EN, -+ 1, 0); -+} -+int qbman_swp_CDAN_disable(struct qbman_swp *s, uint16_t channelid) -+{ -+ return qbman_swp_CDAN_set(s, channelid, -+ CODE_CDAN_WE_EN, -+ 0, 0); -+} -+int qbman_swp_CDAN_set_context_enable(struct qbman_swp *s, uint16_t channelid, -+ uint64_t ctx) -+{ -+ return qbman_swp_CDAN_set(s, channelid, -+ CODE_CDAN_WE_EN | CODE_CDAN_WE_CTX, -+ 1, ctx); -+} -diff --git a/drivers/staging/fsl-mc/bus/dpio/qbman_portal.h b/drivers/staging/fsl-mc/bus/dpio/qbman_portal.h -new file mode 100644 -index 0000000..65ebf3f ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpio/qbman_portal.h -@@ -0,0 +1,261 @@ -+/* Copyright (C) 2014 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 "qbman_private.h" -+#include "fsl_qbman_portal.h" -+#include "../../include/fsl_dpaa2_fd.h" -+ -+/* All QBMan command and result structures use this "valid bit" encoding */ -+#define QB_VALID_BIT ((uint32_t)0x80) -+ -+/* Management command result codes */ -+#define QBMAN_MC_RSLT_OK 0xf0 -+ -+/* TBD: as of QBMan 4.1, DQRR will be 8 rather than 4! */ -+#define QBMAN_DQRR_SIZE 4 -+ -+/* DQRR valid-bit reset bug. See qbman_portal.c::qbman_swp_init(). */ -+#define WORKAROUND_DQRR_RESET_BUG -+ -+/* --------------------- */ -+/* portal data structure */ -+/* --------------------- */ -+ -+struct qbman_swp { -+ const struct qbman_swp_desc *desc; -+ /* The qbman_sys (ie. arch/OS-specific) support code can put anything it -+ * needs in here. */ -+ struct qbman_swp_sys sys; -+ /* Management commands */ -+ struct { -+#ifdef QBMAN_CHECKING -+ enum swp_mc_check { -+ swp_mc_can_start, /* call __qbman_swp_mc_start() */ -+ swp_mc_can_submit, /* call __qbman_swp_mc_submit() */ -+ swp_mc_can_poll, /* call __qbman_swp_mc_result() */ -+ } check; -+#endif -+ uint32_t valid_bit; /* 0x00 or 0x80 */ -+ } mc; -+ /* Push dequeues */ -+ uint32_t sdq; -+ /* Volatile dequeues */ -+ struct { -+ /* VDQCR supports a "1 deep pipeline", meaning that if you know -+ * the last-submitted command is already executing in the -+ * hardware (as evidenced by at least 1 valid dequeue result), -+ * you can write another dequeue command to the register, the -+ * hardware will start executing it as soon as the -+ * already-executing command terminates. (This minimises latency -+ * and stalls.) With that in mind, this "busy" variable refers -+ * to whether or not a command can be submitted, not whether or -+ * not a previously-submitted command is still executing. In -+ * other words, once proof is seen that the previously-submitted -+ * command is executing, "vdq" is no longer "busy". -+ */ -+ atomic_t busy; -+ uint32_t valid_bit; /* 0x00 or 0x80 */ -+ /* We need to determine when vdq is no longer busy. This depends -+ * on whether the "busy" (last-submitted) dequeue command is -+ * targeting DQRR or main-memory, and detected is based on the -+ * presence of the dequeue command's "token" showing up in -+ * dequeue entries in DQRR or main-memory (respectively). */ -+ struct dpaa2_dq *storage; /* NULL if DQRR */ -+ } vdq; -+ /* DQRR */ -+ struct { -+ uint32_t next_idx; -+ uint32_t valid_bit; -+ uint8_t dqrr_size; -+#ifdef WORKAROUND_DQRR_RESET_BUG -+ int reset_bug; -+#endif -+ } dqrr; -+}; -+ -+/* -------------------------- */ -+/* portal management commands */ -+/* -------------------------- */ -+ -+/* Different management commands all use this common base layer of code to issue -+ * commands and poll for results. The first function returns a pointer to where -+ * the caller should fill in their MC command (though they should ignore the -+ * verb byte), the second function commits merges in the caller-supplied command -+ * verb (which should not include the valid-bit) and submits the command to -+ * hardware, and the third function checks for a completed response (returns -+ * non-NULL if only if the response is complete). */ -+void *qbman_swp_mc_start(struct qbman_swp *p); -+void qbman_swp_mc_submit(struct qbman_swp *p, void *cmd, uint32_t cmd_verb); -+void *qbman_swp_mc_result(struct qbman_swp *p); -+ -+/* Wraps up submit + poll-for-result */ -+static inline void *qbman_swp_mc_complete(struct qbman_swp *swp, void *cmd, -+ uint32_t cmd_verb) -+{ -+ int loopvar; -+ -+ qbman_swp_mc_submit(swp, cmd, cmd_verb); -+ DBG_POLL_START(loopvar); -+ do { -+ DBG_POLL_CHECK(loopvar); -+ cmd = qbman_swp_mc_result(swp); -+ } while (!cmd); -+ return cmd; -+} -+ -+/* ------------ */ -+/* qb_attr_code */ -+/* ------------ */ -+ -+/* This struct locates a sub-field within a QBMan portal (CENA) cacheline which -+ * is either serving as a configuration command or a query result. The -+ * representation is inherently little-endian, as the indexing of the words is -+ * itself little-endian in nature and layerscape is little endian for anything -+ * that crosses a word boundary too (64-bit fields are the obvious examples). -+ */ -+struct qb_attr_code { -+ unsigned int word; /* which uint32_t[] array member encodes the field */ -+ unsigned int lsoffset; /* encoding offset from ls-bit */ -+ unsigned int width; /* encoding width. (bool must be 1.) */ -+}; -+ -+/* Some pre-defined codes */ -+extern struct qb_attr_code code_generic_verb; -+extern struct qb_attr_code code_generic_rslt; -+ -+/* Macros to define codes */ -+#define QB_CODE(a, b, c) { a, b, c} -+#define QB_CODE_NULL \ -+ QB_CODE((unsigned int)-1, (unsigned int)-1, (unsigned int)-1) -+ -+/* Rotate a code "ms", meaning that it moves from less-significant bytes to -+ * more-significant, from less-significant words to more-significant, etc. The -+ * "ls" version does the inverse, from more-significant towards -+ * less-significant. -+ */ -+static inline void qb_attr_code_rotate_ms(struct qb_attr_code *code, -+ unsigned int bits) -+{ -+ code->lsoffset += bits; -+ while (code->lsoffset > 31) { -+ code->word++; -+ code->lsoffset -= 32; -+ } -+} -+static inline void qb_attr_code_rotate_ls(struct qb_attr_code *code, -+ unsigned int bits) -+{ -+ /* Don't be fooled, this trick should work because the types are -+ * unsigned. So the case that interests the while loop (the rotate has -+ * gone too far and the word count needs to compensate for it), is -+ * manifested when lsoffset is negative. But that equates to a really -+ * large unsigned value, starting with lots of "F"s. As such, we can -+ * continue adding 32 back to it until it wraps back round above zero, -+ * to a value of 31 or less... -+ */ -+ code->lsoffset -= bits; -+ while (code->lsoffset > 31) { -+ code->word--; -+ code->lsoffset += 32; -+ } -+} -+/* Implement a loop of code rotations until 'expr' evaluates to FALSE (0). */ -+#define qb_attr_code_for_ms(code, bits, expr) \ -+ for (; expr; qb_attr_code_rotate_ms(code, bits)) -+#define qb_attr_code_for_ls(code, bits, expr) \ -+ for (; expr; qb_attr_code_rotate_ls(code, bits)) -+ -+/* decode a field from a cacheline */ -+static inline uint32_t qb_attr_code_decode(const struct qb_attr_code *code, -+ const uint32_t *cacheline) -+{ -+ return d32_uint32_t(code->lsoffset, code->width, cacheline[code->word]); -+} -+static inline uint64_t qb_attr_code_decode_64(const struct qb_attr_code *code, -+ const uint64_t *cacheline) -+{ -+ uint64_t res; -+ u64_from_le32_copy(&res, &cacheline[code->word/2], 1); -+ return res; -+} -+ -+/* encode a field to a cacheline */ -+static inline void qb_attr_code_encode(const struct qb_attr_code *code, -+ uint32_t *cacheline, uint32_t val) -+{ -+ cacheline[code->word] = -+ r32_uint32_t(code->lsoffset, code->width, cacheline[code->word]) -+ | e32_uint32_t(code->lsoffset, code->width, val); -+} -+static inline void qb_attr_code_encode_64(const struct qb_attr_code *code, -+ uint64_t *cacheline, uint64_t val) -+{ -+ u64_to_le32_copy(&cacheline[code->word/2], &val, 1); -+} -+ -+/* Small-width signed values (two's-complement) will decode into medium-width -+ * positives. (Eg. for an 8-bit signed field, which stores values from -128 to -+ * +127, a setting of -7 would appear to decode to the 32-bit unsigned value -+ * 249. Likewise -120 would decode as 136.) This function allows the caller to -+ * "re-sign" such fields to 32-bit signed. (Eg. -7, which was 249 with an 8-bit -+ * encoding, will become 0xfffffff9 if you cast the return value to uint32_t). -+ */ -+static inline int32_t qb_attr_code_makesigned(const struct qb_attr_code *code, -+ uint32_t val) -+{ -+ BUG_ON(val >= (1 << code->width)); -+ /* If the high bit was set, it was encoding a negative */ -+ if (val >= (1 << (code->width - 1))) -+ return (int32_t)0 - (int32_t)(((uint32_t)1 << code->width) - -+ val); -+ /* Otherwise, it was encoding a positive */ -+ return (int32_t)val; -+} -+ -+/* ---------------------- */ -+/* Descriptors/cachelines */ -+/* ---------------------- */ -+ -+/* To avoid needless dynamic allocation, the driver API often gives the caller -+ * a "descriptor" type that the caller can instantiate however they like. -+ * Ultimately though, it is just a cacheline of binary storage (or something -+ * smaller when it is known that the descriptor doesn't need all 64 bytes) for -+ * holding pre-formatted pieces of hardware commands. The performance-critical -+ * code can then copy these descriptors directly into hardware command -+ * registers more efficiently than trying to construct/format commands -+ * on-the-fly. The API user sees the descriptor as an array of 32-bit words in -+ * order for the compiler to know its size, but the internal details are not -+ * exposed. The following macro is used within the driver for converting *any* -+ * descriptor pointer to a usable array pointer. The use of a macro (instead of -+ * an inline) is necessary to work with different descriptor types and to work -+ * correctly with const and non-const inputs (and similarly-qualified outputs). -+ */ -+#define qb_cl(d) (&(d)->dont_manipulate_directly[0]) -diff --git a/drivers/staging/fsl-mc/bus/dpio/qbman_private.h b/drivers/staging/fsl-mc/bus/dpio/qbman_private.h -new file mode 100644 -index 0000000..e376b80 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpio/qbman_private.h -@@ -0,0 +1,173 @@ -+/* Copyright (C) 2014 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. -+*/ -+ -+/* Perform extra checking */ -+#define QBMAN_CHECKING -+ -+/* To maximise the amount of logic that is common between the Linux driver and -+ * other targets (such as the embedded MC firmware), we pivot here between the -+ * inclusion of two platform-specific headers. -+ * -+ * The first, qbman_sys_decl.h, includes any and all required system headers as -+ * well as providing any definitions for the purposes of compatibility. The -+ * second, qbman_sys.h, is where platform-specific routines go. -+ * -+ * The point of the split is that the platform-independent code (including this -+ * header) may depend on platform-specific declarations, yet other -+ * platform-specific routines may depend on platform-independent definitions. -+ */ -+ -+#include "qbman_sys_decl.h" -+ -+#define QMAN_REV_4000 0x04000000 -+#define QMAN_REV_4100 0x04010000 -+#define QMAN_REV_4101 0x04010001 -+ -+/* When things go wrong, it is a convenient trick to insert a few FOO() -+ * statements in the code to trace progress. TODO: remove this once we are -+ * hacking the code less actively. -+ */ -+#define FOO() fsl_os_print("FOO: %s:%d\n", __FILE__, __LINE__) -+ -+/* Any time there is a register interface which we poll on, this provides a -+ * "break after x iterations" scheme for it. It's handy for debugging, eg. -+ * where you don't want millions of lines of log output from a polling loop -+ * that won't, because such things tend to drown out the earlier log output -+ * that might explain what caused the problem. (NB: put ";" after each macro!) -+ * TODO: we should probably remove this once we're done sanitising the -+ * simulator... -+ */ -+#define DBG_POLL_START(loopvar) (loopvar = 10) -+#define DBG_POLL_CHECK(loopvar) \ -+ do {if (!(loopvar--)) BUG_ON(1); } while (0) -+ -+/* For CCSR or portal-CINH registers that contain fields at arbitrary offsets -+ * and widths, these macro-generated encode/decode/isolate/remove inlines can -+ * be used. -+ * -+ * Eg. to "d"ecode a 14-bit field out of a register (into a "uint16_t" type), -+ * where the field is located 3 bits "up" from the least-significant bit of the -+ * register (ie. the field location within the 32-bit register corresponds to a -+ * mask of 0x0001fff8), you would do; -+ * uint16_t field = d32_uint16_t(3, 14, reg_value); -+ * -+ * Or to "e"ncode a 1-bit boolean value (input type is "int", zero is FALSE, -+ * non-zero is TRUE, so must convert all non-zero inputs to 1, hence the "!!" -+ * operator) into a register at bit location 0x00080000 (19 bits "in" from the -+ * LS bit), do; -+ * reg_value |= e32_int(19, 1, !!field); -+ * -+ * If you wish to read-modify-write a register, such that you leave the 14-bit -+ * field as-is but have all other fields set to zero, then "i"solate the 14-bit -+ * value using; -+ * reg_value = i32_uint16_t(3, 14, reg_value); -+ * -+ * Alternatively, you could "r"emove the 1-bit boolean field (setting it to -+ * zero) but leaving all other fields as-is; -+ * reg_val = r32_int(19, 1, reg_value); -+ * -+ */ -+#define MAKE_MASK32(width) (width == 32 ? 0xffffffff : \ -+ (uint32_t)((1 << width) - 1)) -+#define DECLARE_CODEC32(t) \ -+static inline uint32_t e32_##t(uint32_t lsoffset, uint32_t width, t val) \ -+{ \ -+ BUG_ON(width > (sizeof(t) * 8)); \ -+ return ((uint32_t)val & MAKE_MASK32(width)) << lsoffset; \ -+} \ -+static inline t d32_##t(uint32_t lsoffset, uint32_t width, uint32_t val) \ -+{ \ -+ BUG_ON(width > (sizeof(t) * 8)); \ -+ return (t)((val >> lsoffset) & MAKE_MASK32(width)); \ -+} \ -+static inline uint32_t i32_##t(uint32_t lsoffset, uint32_t width, \ -+ uint32_t val) \ -+{ \ -+ BUG_ON(width > (sizeof(t) * 8)); \ -+ return e32_##t(lsoffset, width, d32_##t(lsoffset, width, val)); \ -+} \ -+static inline uint32_t r32_##t(uint32_t lsoffset, uint32_t width, \ -+ uint32_t val) \ -+{ \ -+ BUG_ON(width > (sizeof(t) * 8)); \ -+ return ~(MAKE_MASK32(width) << lsoffset) & val; \ -+} -+DECLARE_CODEC32(uint32_t) -+DECLARE_CODEC32(uint16_t) -+DECLARE_CODEC32(uint8_t) -+DECLARE_CODEC32(int) -+ -+ /*********************/ -+ /* Debugging assists */ -+ /*********************/ -+ -+static inline void __hexdump(unsigned long start, unsigned long end, -+ unsigned long p, size_t sz, const unsigned char *c) -+{ -+ while (start < end) { -+ unsigned int pos = 0; -+ char buf[64]; -+ int nl = 0; -+ -+ pos += sprintf(buf + pos, "%08lx: ", start); -+ do { -+ if ((start < p) || (start >= (p + sz))) -+ pos += sprintf(buf + pos, ".."); -+ else -+ pos += sprintf(buf + pos, "%02x", *(c++)); -+ if (!(++start & 15)) { -+ buf[pos++] = '\n'; -+ nl = 1; -+ } else { -+ nl = 0; -+ if (!(start & 1)) -+ buf[pos++] = ' '; -+ if (!(start & 3)) -+ buf[pos++] = ' '; -+ } -+ } while (start & 15); -+ if (!nl) -+ buf[pos++] = '\n'; -+ buf[pos] = '\0'; -+ pr_info("%s", buf); -+ } -+} -+static inline void hexdump(const void *ptr, size_t sz) -+{ -+ unsigned long p = (unsigned long)ptr; -+ unsigned long start = p & ~(unsigned long)15; -+ unsigned long end = (p + sz + 15) & ~(unsigned long)15; -+ const unsigned char *c = ptr; -+ -+ __hexdump(start, end, p, sz, c); -+} -+ -+#include "qbman_sys.h" -diff --git a/drivers/staging/fsl-mc/bus/dpio/qbman_sys.h b/drivers/staging/fsl-mc/bus/dpio/qbman_sys.h -new file mode 100644 -index 0000000..4849212 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpio/qbman_sys.h -@@ -0,0 +1,307 @@ -+/* Copyright (C) 2014 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. -+ */ -+/* qbman_sys_decl.h and qbman_sys.h are the two platform-specific files in the -+ * driver. They are only included via qbman_private.h, which is itself a -+ * platform-independent file and is included by all the other driver source. -+ * -+ * qbman_sys_decl.h is included prior to all other declarations and logic, and -+ * it exists to provide compatibility with any linux interfaces our -+ * single-source driver code is dependent on (eg. kmalloc). Ie. this file -+ * provides linux compatibility. -+ * -+ * This qbman_sys.h header, on the other hand, is included *after* any common -+ * and platform-neutral declarations and logic in qbman_private.h, and exists to -+ * implement any platform-specific logic of the qbman driver itself. Ie. it is -+ * *not* to provide linux compatibility. -+ */ -+ -+/* Trace the 3 different classes of read/write access to QBMan. #undef as -+ * required. */ -+#undef QBMAN_CCSR_TRACE -+#undef QBMAN_CINH_TRACE -+#undef QBMAN_CENA_TRACE -+ -+static inline void word_copy(void *d, const void *s, unsigned int cnt) -+{ -+ uint32_t *dd = d; -+ const uint32_t *ss = s; -+ -+ while (cnt--) -+ *(dd++) = *(ss++); -+} -+ -+/* Currently, the CENA support code expects each 32-bit word to be written in -+ * host order, and these are converted to hardware (little-endian) order on -+ * command submission. However, 64-bit quantities are must be written (and read) -+ * as two 32-bit words with the least-significant word first, irrespective of -+ * host endianness. */ -+static inline void u64_to_le32_copy(void *d, const uint64_t *s, -+ unsigned int cnt) -+{ -+ uint32_t *dd = d; -+ const uint32_t *ss = (const uint32_t *)s; -+ -+ while (cnt--) { -+ /* TBD: the toolchain was choking on the use of 64-bit types up -+ * until recently so this works entirely with 32-bit variables. -+ * When 64-bit types become usable again, investigate better -+ * ways of doing this. */ -+#if defined(__BIG_ENDIAN) -+ *(dd++) = ss[1]; -+ *(dd++) = ss[0]; -+ ss += 2; -+#else -+ *(dd++) = *(ss++); -+ *(dd++) = *(ss++); -+#endif -+ } -+} -+static inline void u64_from_le32_copy(uint64_t *d, const void *s, -+ unsigned int cnt) -+{ -+ const uint32_t *ss = s; -+ uint32_t *dd = (uint32_t *)d; -+ -+ while (cnt--) { -+#if defined(__BIG_ENDIAN) -+ dd[1] = *(ss++); -+ dd[0] = *(ss++); -+ dd += 2; -+#else -+ *(dd++) = *(ss++); -+ *(dd++) = *(ss++); -+#endif -+ } -+} -+ -+/* Convert a host-native 32bit value into little endian */ -+#if defined(__BIG_ENDIAN) -+static inline uint32_t make_le32(uint32_t val) -+{ -+ return ((val & 0xff) << 24) | ((val & 0xff00) << 8) | -+ ((val & 0xff0000) >> 8) | ((val & 0xff000000) >> 24); -+} -+static inline uint32_t make_le24(uint32_t val) -+{ -+ return (((val & 0xff) << 16) | (val & 0xff00) | -+ ((val & 0xff0000) >> 16)); -+} -+#else -+#define make_le32(val) (val) -+#define make_le24(val) (val) -+#endif -+static inline void make_le32_n(uint32_t *val, unsigned int num) -+{ -+ while (num--) { -+ *val = make_le32(*val); -+ val++; -+ } -+} -+ -+ /******************/ -+ /* Portal access */ -+ /******************/ -+struct qbman_swp_sys { -+ /* On GPP, the sys support for qbman_swp is here. The CENA region isi -+ * not an mmap() of the real portal registers, but an allocated -+ * place-holder, because the actual writes/reads to/from the portal are -+ * marshalled from these allocated areas using QBMan's "MC access -+ * registers". CINH accesses are atomic so there's no need for a -+ * place-holder. */ -+ void *cena; -+ void __iomem *addr_cena; -+ void __iomem *addr_cinh; -+}; -+ -+/* P_OFFSET is (ACCESS_CMD,0,12) - offset within the portal -+ * C is (ACCESS_CMD,12,1) - is inhibited? (0==CENA, 1==CINH) -+ * SWP_IDX is (ACCESS_CMD,16,10) - Software portal index -+ * P is (ACCESS_CMD,28,1) - (0==special portal, 1==any portal) -+ * T is (ACCESS_CMD,29,1) - Command type (0==READ, 1==WRITE) -+ * E is (ACCESS_CMD,31,1) - Command execute (1 to issue, poll for 0==complete) -+ */ -+ -+static inline void qbman_cinh_write(struct qbman_swp_sys *s, uint32_t offset, -+ uint32_t val) -+{ -+ -+ writel_relaxed(val, s->addr_cinh + offset); -+#ifdef QBMAN_CINH_TRACE -+ pr_info("qbman_cinh_write(%p:0x%03x) 0x%08x\n", -+ s->addr_cinh, offset, val); -+#endif -+} -+ -+static inline uint32_t qbman_cinh_read(struct qbman_swp_sys *s, uint32_t offset) -+{ -+ uint32_t reg = readl_relaxed(s->addr_cinh + offset); -+ -+#ifdef QBMAN_CINH_TRACE -+ pr_info("qbman_cinh_read(%p:0x%03x) 0x%08x\n", -+ s->addr_cinh, offset, reg); -+#endif -+ return reg; -+} -+ -+static inline void *qbman_cena_write_start(struct qbman_swp_sys *s, -+ uint32_t offset) -+{ -+ void *shadow = s->cena + offset; -+ -+#ifdef QBMAN_CENA_TRACE -+ pr_info("qbman_cena_write_start(%p:0x%03x) %p\n", -+ s->addr_cena, offset, shadow); -+#endif -+ BUG_ON(offset & 63); -+ dcbz(shadow); -+ return shadow; -+} -+ -+static inline void qbman_cena_write_complete(struct qbman_swp_sys *s, -+ uint32_t offset, void *cmd) -+{ -+ const uint32_t *shadow = cmd; -+ int loop; -+ -+#ifdef QBMAN_CENA_TRACE -+ pr_info("qbman_cena_write_complete(%p:0x%03x) %p\n", -+ s->addr_cena, offset, shadow); -+ hexdump(cmd, 64); -+#endif -+ for (loop = 15; loop >= 1; loop--) -+ writel_relaxed(shadow[loop], s->addr_cena + -+ offset + loop * 4); -+ lwsync(); -+ writel_relaxed(shadow[0], s->addr_cena + offset); -+ dcbf(s->addr_cena + offset); -+} -+ -+static inline void *qbman_cena_read(struct qbman_swp_sys *s, uint32_t offset) -+{ -+ uint32_t *shadow = s->cena + offset; -+ unsigned int loop; -+ -+#ifdef QBMAN_CENA_TRACE -+ pr_info("qbman_cena_read(%p:0x%03x) %p\n", -+ s->addr_cena, offset, shadow); -+#endif -+ -+ for (loop = 0; loop < 16; loop++) -+ shadow[loop] = readl_relaxed(s->addr_cena + offset -+ + loop * 4); -+#ifdef QBMAN_CENA_TRACE -+ hexdump(shadow, 64); -+#endif -+ return shadow; -+} -+ -+static inline void qbman_cena_invalidate_prefetch(struct qbman_swp_sys *s, -+ uint32_t offset) -+{ -+ dcivac(s->addr_cena + offset); -+ prefetch_for_load(s->addr_cena + offset); -+} -+ -+ /******************/ -+ /* Portal support */ -+ /******************/ -+ -+/* The SWP_CFG portal register is special, in that it is used by the -+ * platform-specific code rather than the platform-independent code in -+ * qbman_portal.c. So use of it is declared locally here. */ -+#define QBMAN_CINH_SWP_CFG 0xd00 -+ -+/* For MC portal use, we always configure with -+ * DQRR_MF is (SWP_CFG,20,3) - DQRR max fill (<- 0x4) -+ * EST is (SWP_CFG,16,3) - EQCR_CI stashing threshold (<- 0x0) -+ * RPM is (SWP_CFG,12,2) - RCR production notification mode (<- 0x3) -+ * DCM is (SWP_CFG,10,2) - DQRR consumption notification mode (<- 0x2) -+ * EPM is (SWP_CFG,8,2) - EQCR production notification mode (<- 0x3) -+ * SD is (SWP_CFG,5,1) - memory stashing drop enable (<- FALSE) -+ * SP is (SWP_CFG,4,1) - memory stashing priority (<- TRUE) -+ * SE is (SWP_CFG,3,1) - memory stashing enable (<- 0x0) -+ * DP is (SWP_CFG,2,1) - dequeue stashing priority (<- TRUE) -+ * DE is (SWP_CFG,1,1) - dequeue stashing enable (<- 0x0) -+ * EP is (SWP_CFG,0,1) - EQCR_CI stashing priority (<- FALSE) -+ */ -+static inline uint32_t qbman_set_swp_cfg(uint8_t max_fill, uint8_t wn, -+ uint8_t est, uint8_t rpm, uint8_t dcm, -+ uint8_t epm, int sd, int sp, int se, -+ int dp, int de, int ep) -+{ -+ uint32_t reg; -+ -+ reg = e32_uint8_t(20, (uint32_t)(3 + (max_fill >> 3)), max_fill) | -+ e32_uint8_t(16, 3, est) | e32_uint8_t(12, 2, rpm) | -+ e32_uint8_t(10, 2, dcm) | e32_uint8_t(8, 2, epm) | -+ e32_int(5, 1, sd) | e32_int(4, 1, sp) | e32_int(3, 1, se) | -+ e32_int(2, 1, dp) | e32_int(1, 1, de) | e32_int(0, 1, ep) | -+ e32_uint8_t(14, 1, wn); -+ return reg; -+} -+ -+static inline int qbman_swp_sys_init(struct qbman_swp_sys *s, -+ const struct qbman_swp_desc *d, -+ uint8_t dqrr_size) -+{ -+ uint32_t reg; -+ -+ s->addr_cena = d->cena_bar; -+ s->addr_cinh = d->cinh_bar; -+ s->cena = (void *)get_zeroed_page(GFP_KERNEL); -+ if (!s->cena) { -+ pr_err("Could not allocate page for cena shadow\n"); -+ return -1; -+ } -+ -+#ifdef QBMAN_CHECKING -+ /* We should never be asked to initialise for a portal that isn't in -+ * the power-on state. (Ie. don't forget to reset portals when they are -+ * decommissioned!) -+ */ -+ reg = qbman_cinh_read(s, QBMAN_CINH_SWP_CFG); -+ BUG_ON(reg); -+#endif -+ reg = qbman_set_swp_cfg(dqrr_size, 0, 0, 3, 2, 3, 0, 1, 0, 1, 0, 0); -+ qbman_cinh_write(s, QBMAN_CINH_SWP_CFG, reg); -+ reg = qbman_cinh_read(s, QBMAN_CINH_SWP_CFG); -+ if (!reg) { -+ pr_err("The portal is not enabled!\n"); -+ kfree(s->cena); -+ return -1; -+ } -+ return 0; -+} -+ -+static inline void qbman_swp_sys_finish(struct qbman_swp_sys *s) -+{ -+ free_page((unsigned long)s->cena); -+} -diff --git a/drivers/staging/fsl-mc/bus/dpio/qbman_sys_decl.h b/drivers/staging/fsl-mc/bus/dpio/qbman_sys_decl.h -new file mode 100644 -index 0000000..5b3a224 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpio/qbman_sys_decl.h -@@ -0,0 +1,86 @@ -+/* Copyright (C) 2014 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "fsl_qbman_base.h" -+ -+/* The platform-independent code shouldn't need endianness, except for -+ * weird/fast-path cases like qbman_result_has_token(), which needs to -+ * perform a passive and endianness-specific test on a read-only data structure -+ * very quickly. It's an exception, and this symbol is used for that case. */ -+#if defined(__BIG_ENDIAN) -+#define DQRR_TOK_OFFSET 0 -+#define QBMAN_RESULT_VERB_OFFSET_IN_MEM 24 -+#define SCN_STATE_OFFSET_IN_MEM 8 -+#define SCN_RID_OFFSET_IN_MEM 8 -+#else -+#define DQRR_TOK_OFFSET 24 -+#define QBMAN_RESULT_VERB_OFFSET_IN_MEM 0 -+#define SCN_STATE_OFFSET_IN_MEM 16 -+#define SCN_RID_OFFSET_IN_MEM 0 -+#endif -+ -+/* Similarly-named functions */ -+#define upper32(a) upper_32_bits(a) -+#define lower32(a) lower_32_bits(a) -+ -+ /****************/ -+ /* arch assists */ -+ /****************/ -+ -+#define dcbz(p) { asm volatile("dc zva, %0" : : "r" (p) : "memory"); } -+#define lwsync() { asm volatile("dmb st" : : : "memory"); } -+#define dcbf(p) { asm volatile("dc cvac, %0;" : : "r" (p) : "memory"); } -+#define dcivac(p) { asm volatile("dc ivac, %0" : : "r"(p) : "memory"); } -+static inline void prefetch_for_load(void *p) -+{ -+ asm volatile("prfm pldl1keep, [%0, #64]" : : "r" (p)); -+} -+static inline void prefetch_for_store(void *p) -+{ -+ asm volatile("prfm pstl1keep, [%0, #64]" : : "r" (p)); -+} -diff --git a/drivers/staging/fsl-mc/bus/dpio/qbman_test.c b/drivers/staging/fsl-mc/bus/dpio/qbman_test.c -new file mode 100644 -index 0000000..28396e7 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpio/qbman_test.c -@@ -0,0 +1,664 @@ -+/* Copyright (C) 2014 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 -+#include -+#include -+ -+#include "qbman_private.h" -+#include "fsl_qbman_portal.h" -+#include "qbman_debug.h" -+#include "../../include/fsl_dpaa2_fd.h" -+ -+#define QBMAN_SWP_CENA_BASE 0x818000000 -+#define QBMAN_SWP_CINH_BASE 0x81c000000 -+ -+#define QBMAN_PORTAL_IDX 2 -+#define QBMAN_TEST_FQID 19 -+#define QBMAN_TEST_BPID 23 -+#define QBMAN_USE_QD -+#ifdef QBMAN_USE_QD -+#define QBMAN_TEST_QDID 1 -+#endif -+#define QBMAN_TEST_LFQID 0xf00010 -+ -+#define NUM_EQ_FRAME 10 -+#define NUM_DQ_FRAME 10 -+#define NUM_DQ_IN_DQRR 5 -+#define NUM_DQ_IN_MEM (NUM_DQ_FRAME - NUM_DQ_IN_DQRR) -+ -+static struct qbman_swp *swp; -+static struct qbman_eq_desc eqdesc; -+static struct qbman_pull_desc pulldesc; -+static struct qbman_release_desc releasedesc; -+static struct qbman_eq_response eq_storage[1]; -+static struct dpaa2_dq dq_storage[NUM_DQ_IN_MEM] __aligned(64); -+static dma_addr_t eq_storage_phys; -+static dma_addr_t dq_storage_phys; -+ -+/* FQ ctx attribute values for the test code. */ -+#define FQCTX_HI 0xabbaf00d -+#define FQCTX_LO 0x98765432 -+#define FQ_VFQID 0x123456 -+ -+/* Sample frame descriptor */ -+static struct qbman_fd_simple fd = { -+ .addr_lo = 0xbabaf33d, -+ .addr_hi = 0x01234567, -+ .len = 0x7777, -+ .frc = 0xdeadbeef, -+ .flc_lo = 0xcafecafe, -+ .flc_hi = 0xbeadabba -+}; -+ -+static void fd_inc(struct qbman_fd_simple *_fd) -+{ -+ _fd->addr_lo += _fd->len; -+ _fd->flc_lo += 0x100; -+ _fd->frc += 0x10; -+} -+ -+static int fd_cmp(struct qbman_fd *fda, struct qbman_fd *fdb) -+{ -+ int i; -+ -+ for (i = 0; i < 8; i++) -+ if (fda->words[i] - fdb->words[i]) -+ return 1; -+ return 0; -+} -+ -+struct qbman_fd fd_eq[NUM_EQ_FRAME]; -+struct qbman_fd fd_dq[NUM_DQ_FRAME]; -+ -+/* "Buffers" to be released (and storage for buffers to be acquired) */ -+static uint64_t rbufs[320]; -+static uint64_t abufs[320]; -+ -+static void do_enqueue(struct qbman_swp *swp) -+{ -+ int i, j, ret; -+ -+#ifdef QBMAN_USE_QD -+ pr_info("*****QBMan_test: Enqueue %d frames to QD %d\n", -+ NUM_EQ_FRAME, QBMAN_TEST_QDID); -+#else -+ pr_info("*****QBMan_test: Enqueue %d frames to FQ %d\n", -+ NUM_EQ_FRAME, QBMAN_TEST_FQID); -+#endif -+ for (i = 0; i < NUM_EQ_FRAME; i++) { -+ /*********************************/ -+ /* Prepare a enqueue descriptor */ -+ /*********************************/ -+ memset(eq_storage, 0, sizeof(eq_storage)); -+ eq_storage_phys = virt_to_phys(eq_storage); -+ qbman_eq_desc_clear(&eqdesc); -+ qbman_eq_desc_set_no_orp(&eqdesc, 0); -+ qbman_eq_desc_set_response(&eqdesc, eq_storage_phys, 0); -+ qbman_eq_desc_set_token(&eqdesc, 0x99); -+#ifdef QBMAN_USE_QD -+ /**********************************/ -+ /* Prepare a Queueing Destination */ -+ /**********************************/ -+ qbman_eq_desc_set_qd(&eqdesc, QBMAN_TEST_QDID, 0, 3); -+#else -+ qbman_eq_desc_set_fq(&eqdesc, QBMAN_TEST_FQID); -+#endif -+ -+ /******************/ -+ /* Try an enqueue */ -+ /******************/ -+ ret = qbman_swp_enqueue(swp, &eqdesc, -+ (const struct qbman_fd *)&fd); -+ BUG_ON(ret); -+ for (j = 0; j < 8; j++) -+ fd_eq[i].words[j] = *((uint32_t *)&fd + j); -+ fd_inc(&fd); -+ } -+} -+ -+static void do_push_dequeue(struct qbman_swp *swp) -+{ -+ int i, j; -+ const struct dpaa2_dq *dq_storage1; -+ const struct qbman_fd *__fd; -+ int loopvar; -+ -+ pr_info("*****QBMan_test: Start push dequeue\n"); -+ for (i = 0; i < NUM_DQ_FRAME; i++) { -+ DBG_POLL_START(loopvar); -+ do { -+ DBG_POLL_CHECK(loopvar); -+ dq_storage1 = qbman_swp_dqrr_next(swp); -+ } while (!dq_storage1); -+ if (dq_storage1) { -+ __fd = (const struct qbman_fd *) -+ dpaa2_dq_fd(dq_storage1); -+ for (j = 0; j < 8; j++) -+ fd_dq[i].words[j] = __fd->words[j]; -+ if (fd_cmp(&fd_eq[i], &fd_dq[i])) { -+ pr_info("enqueue FD is\n"); -+ hexdump(&fd_eq[i], 32); -+ pr_info("dequeue FD is\n"); -+ hexdump(&fd_dq[i], 32); -+ } -+ qbman_swp_dqrr_consume(swp, dq_storage1); -+ } else { -+ pr_info("The push dequeue fails\n"); -+ } -+ } -+} -+ -+static void do_pull_dequeue(struct qbman_swp *swp) -+{ -+ int i, j, ret; -+ const struct dpaa2_dq *dq_storage1; -+ const struct qbman_fd *__fd; -+ int loopvar; -+ -+ pr_info("*****QBMan_test: Dequeue %d frames with dq entry in DQRR\n", -+ NUM_DQ_IN_DQRR); -+ for (i = 0; i < NUM_DQ_IN_DQRR; i++) { -+ qbman_pull_desc_clear(&pulldesc); -+ qbman_pull_desc_set_storage(&pulldesc, NULL, 0, 0); -+ qbman_pull_desc_set_numframes(&pulldesc, 1); -+ qbman_pull_desc_set_fq(&pulldesc, QBMAN_TEST_FQID); -+ -+ ret = qbman_swp_pull(swp, &pulldesc); -+ BUG_ON(ret); -+ DBG_POLL_START(loopvar); -+ do { -+ DBG_POLL_CHECK(loopvar); -+ dq_storage1 = qbman_swp_dqrr_next(swp); -+ } while (!dq_storage1); -+ -+ if (dq_storage1) { -+ __fd = (const struct qbman_fd *) -+ dpaa2_dq_fd(dq_storage1); -+ for (j = 0; j < 8; j++) -+ fd_dq[i].words[j] = __fd->words[j]; -+ if (fd_cmp(&fd_eq[i], &fd_dq[i])) { -+ pr_info("enqueue FD is\n"); -+ hexdump(&fd_eq[i], 32); -+ pr_info("dequeue FD is\n"); -+ hexdump(&fd_dq[i], 32); -+ } -+ qbman_swp_dqrr_consume(swp, dq_storage1); -+ } else { -+ pr_info("Dequeue with dq entry in DQRR fails\n"); -+ } -+ } -+ -+ pr_info("*****QBMan_test: Dequeue %d frames with dq entry in memory\n", -+ NUM_DQ_IN_MEM); -+ for (i = 0; i < NUM_DQ_IN_MEM; i++) { -+ dq_storage_phys = virt_to_phys(&dq_storage[i]); -+ qbman_pull_desc_clear(&pulldesc); -+ qbman_pull_desc_set_storage(&pulldesc, &dq_storage[i], -+ dq_storage_phys, 1); -+ qbman_pull_desc_set_numframes(&pulldesc, 1); -+ qbman_pull_desc_set_fq(&pulldesc, QBMAN_TEST_FQID); -+ ret = qbman_swp_pull(swp, &pulldesc); -+ BUG_ON(ret); -+ -+ DBG_POLL_START(loopvar); -+ do { -+ DBG_POLL_CHECK(loopvar); -+ ret = qbman_result_has_new_result(swp, -+ &dq_storage[i]); -+ } while (!ret); -+ -+ if (ret) { -+ for (j = 0; j < 8; j++) -+ fd_dq[i + NUM_DQ_IN_DQRR].words[j] = -+ dq_storage[i].dont_manipulate_directly[j + 8]; -+ j = i + NUM_DQ_IN_DQRR; -+ if (fd_cmp(&fd_eq[j], &fd_dq[j])) { -+ pr_info("enqueue FD is\n"); -+ hexdump(&fd_eq[i + NUM_DQ_IN_DQRR], 32); -+ pr_info("dequeue FD is\n"); -+ hexdump(&fd_dq[i + NUM_DQ_IN_DQRR], 32); -+ hexdump(&dq_storage[i], 64); -+ } -+ } else { -+ pr_info("Dequeue with dq entry in memory fails\n"); -+ } -+ } -+} -+ -+static void release_buffer(struct qbman_swp *swp, unsigned int num) -+{ -+ int ret; -+ unsigned int i, j; -+ -+ qbman_release_desc_clear(&releasedesc); -+ qbman_release_desc_set_bpid(&releasedesc, QBMAN_TEST_BPID); -+ pr_info("*****QBMan_test: Release %d buffers to BP %d\n", -+ num, QBMAN_TEST_BPID); -+ for (i = 0; i < (num / 7 + 1); i++) { -+ j = ((num - i * 7) > 7) ? 7 : (num - i * 7); -+ ret = qbman_swp_release(swp, &releasedesc, &rbufs[i * 7], j); -+ BUG_ON(ret); -+ } -+} -+ -+static void acquire_buffer(struct qbman_swp *swp, unsigned int num) -+{ -+ int ret; -+ unsigned int i, j; -+ -+ pr_info("*****QBMan_test: Acquire %d buffers from BP %d\n", -+ num, QBMAN_TEST_BPID); -+ -+ for (i = 0; i < (num / 7 + 1); i++) { -+ j = ((num - i * 7) > 7) ? 7 : (num - i * 7); -+ ret = qbman_swp_acquire(swp, QBMAN_TEST_BPID, &abufs[i * 7], j); -+ BUG_ON(ret != j); -+ } -+} -+ -+static void buffer_pool_test(struct qbman_swp *swp) -+{ -+ struct qbman_attr info; -+ struct dpaa2_dq *bpscn_message; -+ dma_addr_t bpscn_phys; -+ uint64_t bpscn_ctx; -+ uint64_t ctx = 0xbbccddaadeadbeefull; -+ int i, ret; -+ uint32_t hw_targ; -+ -+ pr_info("*****QBMan_test: test buffer pool management\n"); -+ ret = qbman_bp_query(swp, QBMAN_TEST_BPID, &info); -+ qbman_bp_attr_get_bpscn_addr(&info, &bpscn_phys); -+ pr_info("The bpscn is %llx, info_phys is %llx\n", bpscn_phys, -+ virt_to_phys(&info)); -+ bpscn_message = phys_to_virt(bpscn_phys); -+ -+ for (i = 0; i < 320; i++) -+ rbufs[i] = 0xf00dabba01234567ull + i * 0x40; -+ -+ release_buffer(swp, 320); -+ -+ pr_info("QBMan_test: query the buffer pool\n"); -+ qbman_bp_query(swp, QBMAN_TEST_BPID, &info); -+ hexdump(&info, 64); -+ qbman_bp_attr_get_hw_targ(&info, &hw_targ); -+ pr_info("hw_targ is %d\n", hw_targ); -+ -+ /* Acquire buffers to trigger BPSCN */ -+ acquire_buffer(swp, 300); -+ /* BPSCN should be written to the memory */ -+ qbman_bp_query(swp, QBMAN_TEST_BPID, &info); -+ hexdump(&info, 64); -+ hexdump(bpscn_message, 64); -+ BUG_ON(!qbman_result_is_BPSCN(bpscn_message)); -+ /* There should be free buffers in the pool */ -+ BUG_ON(!(qbman_result_bpscn_has_free_bufs(bpscn_message))); -+ /* Buffer pool is depleted */ -+ BUG_ON(!qbman_result_bpscn_is_depleted(bpscn_message)); -+ /* The ctx should match */ -+ bpscn_ctx = qbman_result_bpscn_ctx(bpscn_message); -+ pr_info("BPSCN test: ctx %llx, bpscn_ctx %llx\n", ctx, bpscn_ctx); -+ BUG_ON(ctx != bpscn_ctx); -+ memset(bpscn_message, 0, sizeof(struct dpaa2_dq)); -+ -+ /* Re-seed the buffer pool to trigger BPSCN */ -+ release_buffer(swp, 240); -+ /* BPSCN should be written to the memory */ -+ BUG_ON(!qbman_result_is_BPSCN(bpscn_message)); -+ /* There should be free buffers in the pool */ -+ BUG_ON(!(qbman_result_bpscn_has_free_bufs(bpscn_message))); -+ /* Buffer pool is not depleted */ -+ BUG_ON(qbman_result_bpscn_is_depleted(bpscn_message)); -+ memset(bpscn_message, 0, sizeof(struct dpaa2_dq)); -+ -+ acquire_buffer(swp, 260); -+ /* BPSCN should be written to the memory */ -+ BUG_ON(!qbman_result_is_BPSCN(bpscn_message)); -+ /* There should be free buffers in the pool while BPSCN generated */ -+ BUG_ON(!(qbman_result_bpscn_has_free_bufs(bpscn_message))); -+ /* Buffer pool is depletion */ -+ BUG_ON(!qbman_result_bpscn_is_depleted(bpscn_message)); -+} -+ -+static void ceetm_test(struct qbman_swp *swp) -+{ -+ int i, j, ret; -+ -+ qbman_eq_desc_clear(&eqdesc); -+ qbman_eq_desc_set_no_orp(&eqdesc, 0); -+ qbman_eq_desc_set_fq(&eqdesc, QBMAN_TEST_LFQID); -+ pr_info("*****QBMan_test: Enqueue to LFQID %x\n", -+ QBMAN_TEST_LFQID); -+ for (i = 0; i < NUM_EQ_FRAME; i++) { -+ ret = qbman_swp_enqueue(swp, &eqdesc, -+ (const struct qbman_fd *)&fd); -+ BUG_ON(ret); -+ for (j = 0; j < 8; j++) -+ fd_eq[i].words[j] = *((uint32_t *)&fd + j); -+ fd_inc(&fd); -+ } -+} -+ -+int qbman_test(void) -+{ -+ struct qbman_swp_desc pd; -+ uint32_t reg; -+ -+ pd.cena_bar = ioremap_cache_ns(QBMAN_SWP_CENA_BASE + -+ QBMAN_PORTAL_IDX * 0x10000, 0x10000); -+ pd.cinh_bar = ioremap(QBMAN_SWP_CINH_BASE + -+ QBMAN_PORTAL_IDX * 0x10000, 0x10000); -+ -+ /* Detect whether the mc image is the test image with GPP setup */ -+ reg = readl_relaxed(pd.cena_bar + 0x4); -+ if (reg != 0xdeadbeef) { -+ pr_err("The MC image doesn't have GPP test setup, stop!\n"); -+ iounmap(pd.cena_bar); -+ iounmap(pd.cinh_bar); -+ return -1; -+ } -+ -+ pr_info("*****QBMan_test: Init QBMan SWP %d\n", QBMAN_PORTAL_IDX); -+ swp = qbman_swp_init(&pd); -+ if (!swp) { -+ iounmap(pd.cena_bar); -+ iounmap(pd.cinh_bar); -+ return -1; -+ } -+ -+ /*******************/ -+ /* Enqueue frames */ -+ /*******************/ -+ do_enqueue(swp); -+ -+ /*******************/ -+ /* Do pull dequeue */ -+ /*******************/ -+ do_pull_dequeue(swp); -+ -+ /*******************/ -+ /* Enqueue frames */ -+ /*******************/ -+ qbman_swp_push_set(swp, 0, 1); -+ qbman_swp_fq_schedule(swp, QBMAN_TEST_FQID); -+ do_enqueue(swp); -+ -+ /*******************/ -+ /* Do push dequeue */ -+ /*******************/ -+ do_push_dequeue(swp); -+ -+ /**************************/ -+ /* Test buffer pool funcs */ -+ /**************************/ -+ buffer_pool_test(swp); -+ -+ /******************/ -+ /* CEETM test */ -+ /******************/ -+ ceetm_test(swp); -+ -+ qbman_swp_finish(swp); -+ pr_info("*****QBMan_test: Kernel test Passed\n"); -+ return 0; -+} -+ -+/* user-space test-case, definitions: -+ * -+ * 1 portal only, using portal index 3. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define QBMAN_TEST_US_SWP 3 /* portal index for user space */ -+ -+#define QBMAN_TEST_MAGIC 'q' -+struct qbman_test_swp_ioctl { -+ unsigned long portal1_cinh; -+ unsigned long portal1_cena; -+}; -+struct qbman_test_dma_ioctl { -+ unsigned long ptr; -+ uint64_t phys_addr; -+}; -+ -+struct qbman_test_priv { -+ int has_swp_map; -+ int has_dma_map; -+ unsigned long pgoff; -+}; -+ -+#define QBMAN_TEST_SWP_MAP \ -+ _IOR(QBMAN_TEST_MAGIC, 0x01, struct qbman_test_swp_ioctl) -+#define QBMAN_TEST_SWP_UNMAP \ -+ _IOR(QBMAN_TEST_MAGIC, 0x02, struct qbman_test_swp_ioctl) -+#define QBMAN_TEST_DMA_MAP \ -+ _IOR(QBMAN_TEST_MAGIC, 0x03, struct qbman_test_dma_ioctl) -+#define QBMAN_TEST_DMA_UNMAP \ -+ _IOR(QBMAN_TEST_MAGIC, 0x04, struct qbman_test_dma_ioctl) -+ -+#define TEST_PORTAL1_CENA_PGOFF ((QBMAN_SWP_CENA_BASE + QBMAN_TEST_US_SWP * \ -+ 0x10000) >> PAGE_SHIFT) -+#define TEST_PORTAL1_CINH_PGOFF ((QBMAN_SWP_CINH_BASE + QBMAN_TEST_US_SWP * \ -+ 0x10000) >> PAGE_SHIFT) -+ -+static int qbman_test_open(struct inode *inode, struct file *filp) -+{ -+ struct qbman_test_priv *priv; -+ -+ priv = kmalloc(sizeof(struct qbman_test_priv), GFP_KERNEL); -+ if (!priv) -+ return -EIO; -+ filp->private_data = priv; -+ priv->has_swp_map = 0; -+ priv->has_dma_map = 0; -+ priv->pgoff = 0; -+ return 0; -+} -+ -+static int qbman_test_mmap(struct file *filp, struct vm_area_struct *vma) -+{ -+ int ret; -+ struct qbman_test_priv *priv = filp->private_data; -+ -+ BUG_ON(!priv); -+ -+ if (vma->vm_pgoff == TEST_PORTAL1_CINH_PGOFF) -+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); -+ else if (vma->vm_pgoff == TEST_PORTAL1_CENA_PGOFF) -+ vma->vm_page_prot = pgprot_cached_ns(vma->vm_page_prot); -+ else if (vma->vm_pgoff == priv->pgoff) -+ vma->vm_page_prot = pgprot_cached(vma->vm_page_prot); -+ else { -+ pr_err("Damn, unrecognised pg_off!!\n"); -+ return -EINVAL; -+ } -+ ret = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, -+ vma->vm_end - vma->vm_start, -+ vma->vm_page_prot); -+ return ret; -+} -+ -+static long qbman_test_ioctl(struct file *fp, unsigned int cmd, -+ unsigned long arg) -+{ -+ void __user *a = (void __user *)arg; -+ unsigned long longret, populate; -+ int ret = 0; -+ struct qbman_test_priv *priv = fp->private_data; -+ -+ BUG_ON(!priv); -+ -+ switch (cmd) { -+ case QBMAN_TEST_SWP_MAP: -+ { -+ struct qbman_test_swp_ioctl params; -+ -+ if (priv->has_swp_map) -+ return -EINVAL; -+ down_write(¤t->mm->mmap_sem); -+ /* Map portal1 CINH */ -+ longret = do_mmap_pgoff(fp, PAGE_SIZE, 0x10000, -+ PROT_READ | PROT_WRITE, MAP_SHARED, -+ TEST_PORTAL1_CINH_PGOFF, &populate); -+ if (longret & ~PAGE_MASK) { -+ ret = (int)longret; -+ goto out; -+ } -+ params.portal1_cinh = longret; -+ /* Map portal1 CENA */ -+ longret = do_mmap_pgoff(fp, PAGE_SIZE, 0x10000, -+ PROT_READ | PROT_WRITE, MAP_SHARED, -+ TEST_PORTAL1_CENA_PGOFF, &populate); -+ if (longret & ~PAGE_MASK) { -+ ret = (int)longret; -+ goto out; -+ } -+ params.portal1_cena = longret; -+ priv->has_swp_map = 1; -+out: -+ up_write(¤t->mm->mmap_sem); -+ if (!ret && copy_to_user(a, ¶ms, sizeof(params))) -+ return -EFAULT; -+ return ret; -+ } -+ case QBMAN_TEST_SWP_UNMAP: -+ { -+ struct qbman_test_swp_ioctl params; -+ -+ if (!priv->has_swp_map) -+ return -EINVAL; -+ -+ if (copy_from_user(¶ms, a, sizeof(params))) -+ return -EFAULT; -+ down_write(¤t->mm->mmap_sem); -+ do_munmap(current->mm, params.portal1_cena, 0x10000); -+ do_munmap(current->mm, params.portal1_cinh, 0x10000); -+ up_write(¤t->mm->mmap_sem); -+ priv->has_swp_map = 0; -+ return 0; -+ } -+ case QBMAN_TEST_DMA_MAP: -+ { -+ struct qbman_test_dma_ioctl params; -+ void *vaddr; -+ -+ if (priv->has_dma_map) -+ return -EINVAL; -+ vaddr = (void *)get_zeroed_page(GFP_KERNEL); -+ params.phys_addr = virt_to_phys(vaddr); -+ priv->pgoff = (unsigned long)params.phys_addr >> PAGE_SHIFT; -+ down_write(¤t->mm->mmap_sem); -+ longret = do_mmap_pgoff(fp, PAGE_SIZE, PAGE_SIZE, -+ PROT_READ | PROT_WRITE, MAP_SHARED, -+ priv->pgoff, &populate); -+ if (longret & ~PAGE_MASK) { -+ ret = (int)longret; -+ return ret; -+ } -+ params.ptr = longret; -+ priv->has_dma_map = 1; -+ up_write(¤t->mm->mmap_sem); -+ if (copy_to_user(a, ¶ms, sizeof(params))) -+ return -EFAULT; -+ return 0; -+ } -+ case QBMAN_TEST_DMA_UNMAP: -+ { -+ struct qbman_test_dma_ioctl params; -+ -+ if (!priv->has_dma_map) -+ return -EINVAL; -+ if (copy_from_user(¶ms, a, sizeof(params))) -+ return -EFAULT; -+ down_write(¤t->mm->mmap_sem); -+ do_munmap(current->mm, params.ptr, PAGE_SIZE); -+ up_write(¤t->mm->mmap_sem); -+ free_page((unsigned long)phys_to_virt(params.phys_addr)); -+ priv->has_dma_map = 0; -+ return 0; -+ } -+ default: -+ pr_err("Bad ioctl cmd!\n"); -+ } -+ return -EINVAL; -+} -+ -+static const struct file_operations qbman_fops = { -+ .open = qbman_test_open, -+ .mmap = qbman_test_mmap, -+ .unlocked_ioctl = qbman_test_ioctl -+}; -+ -+static struct miscdevice qbman_miscdev = { -+ .name = "qbman-test", -+ .fops = &qbman_fops, -+ .minor = MISC_DYNAMIC_MINOR, -+}; -+ -+static int qbman_miscdev_init; -+ -+static int test_init(void) -+{ -+ int ret = qbman_test(); -+ -+ if (!ret) { -+ /* MC image supports the test cases, so instantiate the -+ * character devic that the user-space test case will use to do -+ * its memory mappings. */ -+ ret = misc_register(&qbman_miscdev); -+ if (ret) { -+ pr_err("qbman-test: failed to register misc device\n"); -+ return ret; -+ } -+ pr_info("qbman-test: misc device registered!\n"); -+ qbman_miscdev_init = 1; -+ } -+ return 0; -+} -+ -+static void test_exit(void) -+{ -+ if (qbman_miscdev_init) { -+ misc_deregister(&qbman_miscdev); -+ qbman_miscdev_init = 0; -+ } -+} -+ -+module_init(test_init); -+module_exit(test_exit); -diff --git a/drivers/staging/fsl-mc/bus/dpmcp-cmd.h b/drivers/staging/fsl-mc/bus/dpmcp-cmd.h -new file mode 100644 -index 0000000..c9b52dd ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpmcp-cmd.h -@@ -0,0 +1,56 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef _FSL_DPMCP_CMD_H -+#define _FSL_DPMCP_CMD_H -+ -+/* Minimal supported DPMCP Version */ -+#define DPMCP_MIN_VER_MAJOR 3 -+#define DPMCP_MIN_VER_MINOR 0 -+ -+/* Command IDs */ -+#define DPMCP_CMDID_CLOSE 0x800 -+#define DPMCP_CMDID_OPEN 0x80b -+#define DPMCP_CMDID_CREATE 0x90b -+#define DPMCP_CMDID_DESTROY 0x900 -+ -+#define DPMCP_CMDID_GET_ATTR 0x004 -+#define DPMCP_CMDID_RESET 0x005 -+ -+#define DPMCP_CMDID_SET_IRQ 0x010 -+#define DPMCP_CMDID_GET_IRQ 0x011 -+#define DPMCP_CMDID_SET_IRQ_ENABLE 0x012 -+#define DPMCP_CMDID_GET_IRQ_ENABLE 0x013 -+#define DPMCP_CMDID_SET_IRQ_MASK 0x014 -+#define DPMCP_CMDID_GET_IRQ_MASK 0x015 -+#define DPMCP_CMDID_GET_IRQ_STATUS 0x016 -+ -+#endif /* _FSL_DPMCP_CMD_H */ -diff --git a/drivers/staging/fsl-mc/bus/dpmcp.c b/drivers/staging/fsl-mc/bus/dpmcp.c -new file mode 100644 -index 0000000..e23592a ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpmcp.c -@@ -0,0 +1,318 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#include "../include/mc-sys.h" -+#include "../include/mc-cmd.h" -+#include "dpmcp.h" -+#include "dpmcp-cmd.h" -+ -+int dpmcp_open(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ int dpmcp_id, -+ uint16_t *token) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_OPEN, -+ cmd_flags, -+ 0); -+ cmd.params[0] |= mc_enc(0, 32, dpmcp_id); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *token = MC_CMD_HDR_READ_TOKEN(cmd.header); -+ -+ return err; -+} -+ -+int dpmcp_close(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_CLOSE, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpmcp_create(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ const struct dpmcp_cfg *cfg, -+ uint16_t *token) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_CREATE, -+ cmd_flags, -+ 0); -+ cmd.params[0] |= mc_enc(0, 32, cfg->portal_id); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *token = MC_CMD_HDR_READ_TOKEN(cmd.header); -+ -+ return 0; -+} -+ -+int dpmcp_destroy(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_DESTROY, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpmcp_reset(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_RESET, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpmcp_set_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ struct dpmcp_irq_cfg *irq_cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_SET_IRQ, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 8, irq_index); -+ cmd.params[0] |= mc_enc(32, 32, irq_cfg->val); -+ cmd.params[1] |= mc_enc(0, 64, irq_cfg->paddr); -+ cmd.params[2] |= mc_enc(0, 32, irq_cfg->irq_num); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpmcp_get_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ int *type, -+ struct dpmcp_irq_cfg *irq_cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_GET_IRQ, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ irq_cfg->val = (uint32_t)mc_dec(cmd.params[0], 0, 32); -+ irq_cfg->paddr = (uint64_t)mc_dec(cmd.params[1], 0, 64); -+ irq_cfg->irq_num = (int)mc_dec(cmd.params[2], 0, 32); -+ *type = (int)mc_dec(cmd.params[2], 32, 32); -+ return 0; -+} -+ -+int dpmcp_set_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t en) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_SET_IRQ_ENABLE, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 8, en); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpmcp_get_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t *en) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_GET_IRQ_ENABLE, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *en = (uint8_t)mc_dec(cmd.params[0], 0, 8); -+ return 0; -+} -+ -+int dpmcp_set_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t mask) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_SET_IRQ_MASK, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, mask); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpmcp_get_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *mask) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_GET_IRQ_MASK, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *mask = (uint32_t)mc_dec(cmd.params[0], 0, 32); -+ return 0; -+} -+ -+int dpmcp_get_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *status) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_GET_IRQ_STATUS, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *status = (uint32_t)mc_dec(cmd.params[0], 0, 32); -+ return 0; -+} -+ -+int dpmcp_get_attributes(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpmcp_attr *attr) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_GET_ATTR, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ attr->id = (int)mc_dec(cmd.params[0], 32, 32); -+ attr->version.major = (uint16_t)mc_dec(cmd.params[1], 0, 16); -+ attr->version.minor = (uint16_t)mc_dec(cmd.params[1], 16, 16); -+ return 0; -+} -diff --git a/drivers/staging/fsl-mc/bus/dpmcp.h b/drivers/staging/fsl-mc/bus/dpmcp.h -new file mode 100644 -index 0000000..e434a24 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpmcp.h -@@ -0,0 +1,323 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef __FSL_DPMCP_H -+#define __FSL_DPMCP_H -+ -+/* Data Path Management Command Portal API -+ * Contains initialization APIs and runtime control APIs for DPMCP -+ */ -+ -+struct fsl_mc_io; -+ -+/** -+ * dpmcp_open() - Open a control session for the specified object. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @dpmcp_id: DPMCP unique ID -+ * @token: Returned token; use in subsequent API calls -+ * -+ * This function can be used to open a control session for an -+ * already created object; an object may have been declared in -+ * the DPL or by calling the dpmcp_create function. -+ * This function returns a unique authentication token, -+ * associated with the specific object ID and the specific MC -+ * portal; this token must be used in all subsequent commands for -+ * this specific object -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmcp_open(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ int dpmcp_id, -+ uint16_t *token); -+ -+/* Get portal ID from pool */ -+#define DPMCP_GET_PORTAL_ID_FROM_POOL (-1) -+ -+/** -+ * dpmcp_close() - Close the control session of the object -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMCP object -+ * -+ * After this function is called, no further operations are -+ * allowed on the object without opening a new control session. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmcp_close(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * struct dpmcp_cfg - Structure representing DPMCP configuration -+ * @portal_id: Portal ID; 'DPMCP_GET_PORTAL_ID_FROM_POOL' to get the portal ID -+ * from pool -+ */ -+struct dpmcp_cfg { -+ int portal_id; -+}; -+ -+/** -+ * dpmcp_create() - Create the DPMCP object. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @cfg: Configuration structure -+ * @token: Returned token; use in subsequent API calls -+ * -+ * Create the DPMCP object, allocate required resources and -+ * perform required initialization. -+ * -+ * The object can be created either by declaring it in the -+ * DPL file, or by calling this function. -+ * This function returns a unique authentication token, -+ * associated with the specific object ID and the specific MC -+ * portal; this token must be used in all subsequent calls to -+ * this specific object. For objects that are created using the -+ * DPL file, call dpmcp_open function to get an authentication -+ * token first. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmcp_create(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ const struct dpmcp_cfg *cfg, -+ uint16_t *token); -+ -+/** -+ * dpmcp_destroy() - Destroy the DPMCP object and release all its resources. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMCP object -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpmcp_destroy(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * dpmcp_reset() - Reset the DPMCP, returns the object to initial state. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMCP object -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmcp_reset(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/* IRQ */ -+/* IRQ Index */ -+#define DPMCP_IRQ_INDEX 0 -+/* irq event - Indicates that the link state changed */ -+#define DPMCP_IRQ_EVENT_CMD_DONE 0x00000001 -+ -+/** -+ * struct dpmcp_irq_cfg - IRQ configuration -+ * @paddr: Address that must be written to signal a message-based interrupt -+ * @val: Value to write into irq_addr address -+ * @irq_num: A user defined number associated with this IRQ -+ */ -+struct dpmcp_irq_cfg { -+ uint64_t paddr; -+ uint32_t val; -+ int irq_num; -+}; -+ -+/** -+ * dpmcp_set_irq() - Set IRQ information for the DPMCP to trigger an interrupt. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMCP object -+ * @irq_index: Identifies the interrupt index to configure -+ * @irq_cfg: IRQ configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmcp_set_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ struct dpmcp_irq_cfg *irq_cfg); -+ -+/** -+ * dpmcp_get_irq() - Get IRQ information from the DPMCP. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMCP object -+ * @irq_index: The interrupt index to configure -+ * @type: Interrupt type: 0 represents message interrupt -+ * type (both irq_addr and irq_val are valid) -+ * @irq_cfg: IRQ attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmcp_get_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ int *type, -+ struct dpmcp_irq_cfg *irq_cfg); -+ -+/** -+ * dpmcp_set_irq_enable() - Set overall interrupt state. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMCP object -+ * @irq_index: The interrupt index to configure -+ * @en: Interrupt state - enable = 1, disable = 0 -+ * -+ * Allows GPP software to control when interrupts are generated. -+ * Each interrupt can have up to 32 causes. The enable/disable control's the -+ * overall interrupt state. if the interrupt is disabled no causes will cause -+ * an interrupt. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmcp_set_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t en); -+ -+/** -+ * dpmcp_get_irq_enable() - Get overall interrupt state -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMCP object -+ * @irq_index: The interrupt index to configure -+ * @en: Returned interrupt state - enable = 1, disable = 0 -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmcp_get_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t *en); -+ -+/** -+ * dpmcp_set_irq_mask() - Set interrupt mask. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMCP object -+ * @irq_index: The interrupt index to configure -+ * @mask: Event mask to trigger interrupt; -+ * each bit: -+ * 0 = ignore event -+ * 1 = consider event for asserting IRQ -+ * -+ * Every interrupt can have up to 32 causes and the interrupt model supports -+ * masking/unmasking each cause independently -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmcp_set_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t mask); -+ -+/** -+ * dpmcp_get_irq_mask() - Get interrupt mask. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMCP object -+ * @irq_index: The interrupt index to configure -+ * @mask: Returned event mask to trigger interrupt -+ * -+ * Every interrupt can have up to 32 causes and the interrupt model supports -+ * masking/unmasking each cause independently -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmcp_get_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *mask); -+ -+/** -+ * dpmcp_get_irq_status() - Get the current status of any pending interrupts. -+ * -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMCP object -+ * @irq_index: The interrupt index to configure -+ * @status: Returned interrupts status - one bit per cause: -+ * 0 = no interrupt pending -+ * 1 = interrupt pending -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmcp_get_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *status); -+ -+/** -+ * struct dpmcp_attr - Structure representing DPMCP attributes -+ * @id: DPMCP object ID -+ * @version: DPMCP version -+ */ -+struct dpmcp_attr { -+ int id; -+ /** -+ * struct version - Structure representing DPMCP version -+ * @major: DPMCP major version -+ * @minor: DPMCP minor version -+ */ -+ struct { -+ uint16_t major; -+ uint16_t minor; -+ } version; -+}; -+ -+/** -+ * dpmcp_get_attributes - Retrieve DPMCP attributes. -+ * -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMCP object -+ * @attr: Returned object's attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmcp_get_attributes(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpmcp_attr *attr); -+ -+#endif /* __FSL_DPMCP_H */ -diff --git a/drivers/staging/fsl-mc/bus/dpmng-cmd.h b/drivers/staging/fsl-mc/bus/dpmng-cmd.h -new file mode 100644 -index 0000000..ba8cfa9 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpmng-cmd.h -@@ -0,0 +1,47 @@ -+/* Copyright 2013-2014 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+ -+/*************************************************************************//* -+ dpmng-cmd.h -+ -+ defines portal commands -+ -+ *//**************************************************************************/ -+ -+#ifndef __FSL_DPMNG_CMD_H -+#define __FSL_DPMNG_CMD_H -+ -+/* Command IDs */ -+#define DPMNG_CMDID_GET_CONT_ID 0x830 -+#define DPMNG_CMDID_GET_VERSION 0x831 -+ -+#endif /* __FSL_DPMNG_CMD_H */ -diff --git a/drivers/staging/fsl-mc/bus/dpmng.c b/drivers/staging/fsl-mc/bus/dpmng.c -new file mode 100644 -index 0000000..387390b ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpmng.c -@@ -0,0 +1,85 @@ -+/* Copyright 2013-2014 Freescale Semiconductor Inc. -+* -+* Redistribution and use in source and binary forms, with or without -+* modification, are permitted provided that the following conditions are met: -+* * Redistributions of source code must retain the above copyright -+* notice, this list of conditions and the following disclaimer. -+* * 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. -+* * Neither the name of the above-listed copyright holders nor the -+* names of any contributors may be used to endorse or promote products -+* derived from this software without specific prior written permission. -+* -+* -+* ALTERNATIVELY, this software may be distributed under the terms of the -+* GNU General Public License ("GPL") as published by the Free Software -+* Foundation, either version 2 of that License or (at your option) any -+* later version. -+* -+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+*/ -+#include "../include/mc-sys.h" -+#include "../include/mc-cmd.h" -+#include "../include/dpmng.h" -+#include "dpmng-cmd.h" -+ -+int mc_get_version(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ struct mc_version *mc_ver_info) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMNG_CMDID_GET_VERSION, -+ cmd_flags, -+ 0); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ mc_ver_info->revision = mc_dec(cmd.params[0], 0, 32); -+ mc_ver_info->major = mc_dec(cmd.params[0], 32, 32); -+ mc_ver_info->minor = mc_dec(cmd.params[1], 0, 32); -+ -+ return 0; -+} -+EXPORT_SYMBOL(mc_get_version); -+ -+int dpmng_get_container_id(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ int *container_id) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMNG_CMDID_GET_CONT_ID, -+ cmd_flags, -+ 0); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *container_id = mc_dec(cmd.params[0], 0, 32); -+ -+ return 0; -+} -+ -diff --git a/drivers/staging/fsl-mc/bus/dprc-cmd.h b/drivers/staging/fsl-mc/bus/dprc-cmd.h -new file mode 100644 -index 0000000..9b854fa ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dprc-cmd.h -@@ -0,0 +1,87 @@ -+/* Copyright 2013-2014 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+ -+/*************************************************************************//* -+ dprc-cmd.h -+ -+ defines dprc portal commands -+ -+ *//**************************************************************************/ -+ -+#ifndef _FSL_DPRC_CMD_H -+#define _FSL_DPRC_CMD_H -+ -+/* Minimal supported DPRC Version */ -+#define DPRC_MIN_VER_MAJOR 5 -+#define DPRC_MIN_VER_MINOR 0 -+ -+/* Command IDs */ -+#define DPRC_CMDID_CLOSE 0x800 -+#define DPRC_CMDID_OPEN 0x805 -+#define DPRC_CMDID_CREATE 0x905 -+ -+#define DPRC_CMDID_GET_ATTR 0x004 -+#define DPRC_CMDID_RESET_CONT 0x005 -+ -+#define DPRC_CMDID_SET_IRQ 0x010 -+#define DPRC_CMDID_GET_IRQ 0x011 -+#define DPRC_CMDID_SET_IRQ_ENABLE 0x012 -+#define DPRC_CMDID_GET_IRQ_ENABLE 0x013 -+#define DPRC_CMDID_SET_IRQ_MASK 0x014 -+#define DPRC_CMDID_GET_IRQ_MASK 0x015 -+#define DPRC_CMDID_GET_IRQ_STATUS 0x016 -+#define DPRC_CMDID_CLEAR_IRQ_STATUS 0x017 -+ -+#define DPRC_CMDID_CREATE_CONT 0x151 -+#define DPRC_CMDID_DESTROY_CONT 0x152 -+#define DPRC_CMDID_SET_RES_QUOTA 0x155 -+#define DPRC_CMDID_GET_RES_QUOTA 0x156 -+#define DPRC_CMDID_ASSIGN 0x157 -+#define DPRC_CMDID_UNASSIGN 0x158 -+#define DPRC_CMDID_GET_OBJ_COUNT 0x159 -+#define DPRC_CMDID_GET_OBJ 0x15A -+#define DPRC_CMDID_GET_RES_COUNT 0x15B -+#define DPRC_CMDID_GET_RES_IDS 0x15C -+#define DPRC_CMDID_GET_OBJ_REG 0x15E -+#define DPRC_CMDID_SET_OBJ_IRQ 0x15F -+#define DPRC_CMDID_GET_OBJ_IRQ 0x160 -+#define DPRC_CMDID_SET_OBJ_LABEL 0x161 -+#define DPRC_CMDID_GET_OBJ_DESC 0x162 -+ -+#define DPRC_CMDID_CONNECT 0x167 -+#define DPRC_CMDID_DISCONNECT 0x168 -+#define DPRC_CMDID_GET_POOL 0x169 -+#define DPRC_CMDID_GET_POOL_COUNT 0x16A -+ -+#define DPRC_CMDID_GET_CONNECTION 0x16C -+ -+#endif /* _FSL_DPRC_CMD_H */ -diff --git a/drivers/staging/fsl-mc/bus/dprc-driver.c b/drivers/staging/fsl-mc/bus/dprc-driver.c -new file mode 100644 -index 0000000..5b6fa1c ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dprc-driver.c -@@ -0,0 +1,1084 @@ -+/* -+ * Freescale data path resource container (DPRC) driver -+ * -+ * Copyright (C) 2014 Freescale Semiconductor, Inc. -+ * Author: German Rivera -+ * -+ * This file is licensed under the terms of the GNU General Public -+ * License version 2. This program is licensed "as is" without any -+ * warranty of any kind, whether express or implied. -+ */ -+ -+#include "../include/mc-private.h" -+#include "../include/mc-sys.h" -+#include -+#include -+#include -+#include "dprc-cmd.h" -+#include "dpmcp.h" -+ -+struct dprc_child_objs { -+ int child_count; -+ struct dprc_obj_desc *child_array; -+}; -+ -+static int __fsl_mc_device_remove_if_not_in_mc(struct device *dev, void *data) -+{ -+ int i; -+ struct dprc_child_objs *objs; -+ struct fsl_mc_device *mc_dev; -+ -+ WARN_ON(!dev); -+ WARN_ON(!data); -+ mc_dev = to_fsl_mc_device(dev); -+ objs = data; -+ -+ for (i = 0; i < objs->child_count; i++) { -+ struct dprc_obj_desc *obj_desc = &objs->child_array[i]; -+ -+ if (strlen(obj_desc->type) != 0 && -+ FSL_MC_DEVICE_MATCH(mc_dev, obj_desc)) -+ break; -+ } -+ -+ if (i == objs->child_count) -+ fsl_mc_device_remove(mc_dev); -+ -+ return 0; -+} -+ -+static int __fsl_mc_device_remove(struct device *dev, void *data) -+{ -+ WARN_ON(!dev); -+ WARN_ON(data); -+ fsl_mc_device_remove(to_fsl_mc_device(dev)); -+ return 0; -+} -+ -+/** -+ * dprc_remove_devices - Removes devices for objects removed from a DPRC -+ * -+ * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object -+ * @obj_desc_array: array of object descriptors for child objects currently -+ * present in the DPRC in the MC. -+ * @num_child_objects_in_mc: number of entries in obj_desc_array -+ * -+ * Synchronizes the state of the Linux bus driver with the actual state of -+ * the MC by removing devices that represent MC objects that have -+ * been dynamically removed in the physical DPRC. -+ */ -+static void dprc_remove_devices(struct fsl_mc_device *mc_bus_dev, -+ struct dprc_obj_desc *obj_desc_array, -+ int num_child_objects_in_mc) -+{ -+ if (num_child_objects_in_mc != 0) { -+ /* -+ * Remove child objects that are in the DPRC in Linux, -+ * but not in the MC: -+ */ -+ struct dprc_child_objs objs; -+ -+ objs.child_count = num_child_objects_in_mc; -+ objs.child_array = obj_desc_array; -+ device_for_each_child(&mc_bus_dev->dev, &objs, -+ __fsl_mc_device_remove_if_not_in_mc); -+ } else { -+ /* -+ * There are no child objects for this DPRC in the MC. -+ * So, remove all the child devices from Linux: -+ */ -+ device_for_each_child(&mc_bus_dev->dev, NULL, -+ __fsl_mc_device_remove); -+ } -+} -+ -+static int __fsl_mc_device_match(struct device *dev, void *data) -+{ -+ struct dprc_obj_desc *obj_desc = data; -+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); -+ -+ return FSL_MC_DEVICE_MATCH(mc_dev, obj_desc); -+} -+ -+static struct fsl_mc_device *fsl_mc_device_lookup(struct dprc_obj_desc -+ *obj_desc, -+ struct fsl_mc_device -+ *mc_bus_dev) -+{ -+ struct device *dev; -+ -+ dev = device_find_child(&mc_bus_dev->dev, obj_desc, -+ __fsl_mc_device_match); -+ -+ return dev ? to_fsl_mc_device(dev) : NULL; -+} -+ -+/** -+ * check_plugged_state_change - Check change in an MC object's plugged state -+ * -+ * @mc_dev: pointer to the fsl-mc device for a given MC object -+ * @obj_desc: pointer to the MC object's descriptor in the MC -+ * -+ * If the plugged state has changed from unplugged to plugged, the fsl-mc -+ * device is bound to the corresponding device driver. -+ * If the plugged state has changed from plugged to unplugged, the fsl-mc -+ * device is unbound from the corresponding device driver. -+ */ -+static void check_plugged_state_change(struct fsl_mc_device *mc_dev, -+ struct dprc_obj_desc *obj_desc) -+{ -+ int error; -+ uint32_t plugged_flag_at_mc = -+ (obj_desc->state & DPRC_OBJ_STATE_PLUGGED); -+ -+ if (plugged_flag_at_mc != -+ (mc_dev->obj_desc.state & DPRC_OBJ_STATE_PLUGGED)) { -+ if (plugged_flag_at_mc) { -+ mc_dev->obj_desc.state |= DPRC_OBJ_STATE_PLUGGED; -+ error = device_attach(&mc_dev->dev); -+ if (error < 0) { -+ dev_err(&mc_dev->dev, -+ "device_attach() failed: %d\n", -+ error); -+ } -+ } else { -+ mc_dev->obj_desc.state &= ~DPRC_OBJ_STATE_PLUGGED; -+ device_release_driver(&mc_dev->dev); -+ } -+ } -+} -+ -+/** -+ * dprc_add_new_devices - Adds devices to the logical bus for a DPRC -+ * -+ * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object -+ * @driver_override: driver override to apply to new objects found in the DPRC, -+ * or NULL, if none. -+ * @obj_desc_array: array of device descriptors for child devices currently -+ * present in the physical DPRC. -+ * @num_child_objects_in_mc: number of entries in obj_desc_array -+ * -+ * Synchronizes the state of the Linux bus driver with the actual -+ * state of the MC by adding objects that have been newly discovered -+ * in the physical DPRC. -+ */ -+static void dprc_add_new_devices(struct fsl_mc_device *mc_bus_dev, -+ const char *driver_override, -+ struct dprc_obj_desc *obj_desc_array, -+ int num_child_objects_in_mc) -+{ -+ int error; -+ int i; -+ -+ for (i = 0; i < num_child_objects_in_mc; i++) { -+ struct fsl_mc_device *child_dev; -+ struct dprc_obj_desc *obj_desc = &obj_desc_array[i]; -+ -+ if (strlen(obj_desc->type) == 0) -+ continue; -+ -+ /* -+ * Check if device is already known to Linux: -+ */ -+ child_dev = fsl_mc_device_lookup(obj_desc, mc_bus_dev); -+ if (child_dev) { -+ check_plugged_state_change(child_dev, obj_desc); -+ continue; -+ } -+ -+ error = fsl_mc_device_add(obj_desc, NULL, &mc_bus_dev->dev, -+ driver_override, &child_dev); -+ if (error < 0) -+ continue; -+ } -+} -+ -+void dprc_init_all_resource_pools(struct fsl_mc_device *mc_bus_dev) -+{ -+ int pool_type; -+ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); -+ -+ for (pool_type = 0; pool_type < FSL_MC_NUM_POOL_TYPES; pool_type++) { -+ struct fsl_mc_resource_pool *res_pool = -+ &mc_bus->resource_pools[pool_type]; -+ -+ res_pool->type = pool_type; -+ res_pool->max_count = 0; -+ res_pool->free_count = 0; -+ res_pool->mc_bus = mc_bus; -+ INIT_LIST_HEAD(&res_pool->free_list); -+ mutex_init(&res_pool->mutex); -+ } -+} -+ -+static void dprc_cleanup_resource_pool(struct fsl_mc_device *mc_bus_dev, -+ enum fsl_mc_pool_type pool_type) -+{ -+ struct fsl_mc_resource *resource; -+ struct fsl_mc_resource *next; -+ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); -+ struct fsl_mc_resource_pool *res_pool = -+ &mc_bus->resource_pools[pool_type]; -+ int free_count = 0; -+ -+ WARN_ON(res_pool->type != pool_type); -+ WARN_ON(res_pool->free_count != res_pool->max_count); -+ -+ list_for_each_entry_safe(resource, next, &res_pool->free_list, node) { -+ free_count++; -+ WARN_ON(resource->type != res_pool->type); -+ WARN_ON(resource->parent_pool != res_pool); -+ devm_kfree(&mc_bus_dev->dev, resource); -+ } -+ -+ WARN_ON(free_count != res_pool->free_count); -+} -+ -+/* -+ * Clean up all resource pools other than the IRQ pool -+ */ -+void dprc_cleanup_all_resource_pools(struct fsl_mc_device *mc_bus_dev) -+{ -+ int pool_type; -+ -+ for (pool_type = 0; pool_type < FSL_MC_NUM_POOL_TYPES; pool_type++) { -+ if (pool_type != FSL_MC_POOL_IRQ) -+ dprc_cleanup_resource_pool(mc_bus_dev, pool_type); -+ } -+} -+ -+/** -+ * dprc_scan_objects - Discover objects in a DPRC -+ * -+ * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object -+ * @driver_override: driver override to apply to new objects found in the DPRC, -+ * or NULL, if none. -+ * @total_irq_count: total number of IRQs needed by objects in the DPRC. -+ * -+ * Detects objects added and removed from a DPRC and synchronizes the -+ * state of the Linux bus driver, MC by adding and removing -+ * devices accordingly. -+ * Two types of devices can be found in a DPRC: allocatable objects (e.g., -+ * dpbp, dpmcp) and non-allocatable devices (e.g., dprc, dpni). -+ * All allocatable devices needed to be probed before all non-allocatable -+ * devices, to ensure that device drivers for non-allocatable -+ * devices can allocate any type of allocatable devices. -+ * That is, we need to ensure that the corresponding resource pools are -+ * populated before they can get allocation requests from probe callbacks -+ * of the device drivers for the non-allocatable devices. -+ */ -+int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev, -+ const char *driver_override, -+ unsigned int *total_irq_count) -+{ -+ int num_child_objects; -+ int dprc_get_obj_failures; -+ int error; -+ unsigned int irq_count = mc_bus_dev->obj_desc.irq_count; -+ struct dprc_obj_desc *child_obj_desc_array = NULL; -+ -+ error = dprc_get_obj_count(mc_bus_dev->mc_io, -+ 0, -+ mc_bus_dev->mc_handle, -+ &num_child_objects); -+ if (error < 0) { -+ dev_err(&mc_bus_dev->dev, "dprc_get_obj_count() failed: %d\n", -+ error); -+ return error; -+ } -+ -+ if (num_child_objects != 0) { -+ int i; -+ -+ child_obj_desc_array = -+ devm_kmalloc_array(&mc_bus_dev->dev, num_child_objects, -+ sizeof(*child_obj_desc_array), -+ GFP_KERNEL); -+ if (!child_obj_desc_array) -+ return -ENOMEM; -+ -+ /* -+ * Discover objects currently present in the physical DPRC: -+ */ -+ dprc_get_obj_failures = 0; -+ for (i = 0; i < num_child_objects; i++) { -+ struct dprc_obj_desc *obj_desc = -+ &child_obj_desc_array[i]; -+ -+ error = dprc_get_obj(mc_bus_dev->mc_io, -+ 0, -+ mc_bus_dev->mc_handle, -+ i, obj_desc); -+ -+ /* -+ * -ENXIO means object index was invalid. -+ * This is caused when the DPRC was changed at -+ * the MC during the scan. In this case, -+ * abort the current scan. -+ */ -+ if (error == -ENXIO) -+ return error; -+ -+ if (error < 0) { -+ dev_err(&mc_bus_dev->dev, -+ "dprc_get_obj(i=%d) failed: %d\n", -+ i, error); -+ /* -+ * Mark the obj entry as "invalid", by using the -+ * empty string as obj type: -+ */ -+ obj_desc->type[0] = '\0'; -+ obj_desc->id = error; -+ dprc_get_obj_failures++; -+ continue; -+ } -+ -+ /* -+ * for DPRC versions that do not support the -+ * shareability attribute, make simplifying assumption -+ * that only SEC is not shareable. -+ */ -+ if ((strcmp(obj_desc->type, "dpseci") == 0) && -+ (obj_desc->ver_major < 4)) -+ obj_desc->flags |= -+ DPRC_OBJ_FLAG_NO_MEM_SHAREABILITY; -+ -+ irq_count += obj_desc->irq_count; -+ dev_dbg(&mc_bus_dev->dev, -+ "Discovered object: type %s, id %d\n", -+ obj_desc->type, obj_desc->id); -+ } -+ -+ if (dprc_get_obj_failures != 0) { -+ dev_err(&mc_bus_dev->dev, -+ "%d out of %d devices could not be retrieved\n", -+ dprc_get_obj_failures, num_child_objects); -+ } -+ } -+ -+ *total_irq_count = irq_count; -+ dprc_remove_devices(mc_bus_dev, child_obj_desc_array, -+ num_child_objects); -+ -+ dprc_add_new_devices(mc_bus_dev, driver_override, child_obj_desc_array, -+ num_child_objects); -+ -+ if (child_obj_desc_array) -+ devm_kfree(&mc_bus_dev->dev, child_obj_desc_array); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(dprc_scan_objects); -+ -+/** -+ * dprc_scan_container - Scans a physical DPRC and synchronizes Linux bus state -+ * -+ * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object -+ * -+ * Scans the physical DPRC and synchronizes the state of the Linux -+ * bus driver with the actual state of the MC by adding and removing -+ * devices as appropriate. -+ */ -+static int dprc_scan_container(struct fsl_mc_device *mc_bus_dev) -+{ -+ int error; -+ unsigned int irq_count; -+ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); -+ -+ dprc_init_all_resource_pools(mc_bus_dev); -+ -+ /* -+ * Discover objects in the DPRC: -+ */ -+ mutex_lock(&mc_bus->scan_mutex); -+ error = dprc_scan_objects(mc_bus_dev, NULL, &irq_count); -+ mutex_unlock(&mc_bus->scan_mutex); -+ if (error < 0) -+ goto error; -+ -+ if (fsl_mc_interrupts_supported() && !mc_bus->irq_resources) { -+ irq_count += FSL_MC_IRQ_POOL_MAX_EXTRA_IRQS; -+ error = fsl_mc_populate_irq_pool(mc_bus, irq_count); -+ if (error < 0) -+ goto error; -+ } -+ -+ return 0; -+error: -+ device_for_each_child(&mc_bus_dev->dev, NULL, __fsl_mc_device_remove); -+ dprc_cleanup_all_resource_pools(mc_bus_dev); -+ return error; -+} -+ -+/** -+ * dprc_irq0_handler - Regular ISR for DPRC interrupt 0 -+ * -+ * @irq: IRQ number of the interrupt being handled -+ * @arg: Pointer to device structure -+ */ -+static irqreturn_t dprc_irq0_handler(int irq_num, void *arg) -+{ -+ return IRQ_WAKE_THREAD; -+} -+ -+/** -+ * dprc_irq0_handler_thread - Handler thread function for DPRC interrupt 0 -+ * -+ * @irq: IRQ number of the interrupt being handled -+ * @arg: Pointer to device structure -+ */ -+static irqreturn_t dprc_irq0_handler_thread(int irq_num, void *arg) -+{ -+ int error; -+ uint32_t status; -+ struct device *dev = (struct device *)arg; -+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); -+ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev); -+ struct fsl_mc_io *mc_io = mc_dev->mc_io; -+ int irq_index = 0; -+ -+ dev_dbg(dev, "DPRC IRQ %d triggered on CPU %u\n", -+ irq_num, smp_processor_id()); -+ if (WARN_ON(!(mc_dev->flags & FSL_MC_IS_DPRC))) -+ return IRQ_HANDLED; -+ -+ mutex_lock(&mc_bus->scan_mutex); -+ if (WARN_ON(mc_dev->irqs[irq_index]->irq_number != (uint32_t)irq_num)) -+ goto out; -+ -+ status = 0; -+ error = dprc_get_irq_status(mc_io, 0, mc_dev->mc_handle, irq_index, -+ &status); -+ if (error < 0) { -+ dev_err(dev, -+ "dprc_get_irq_status() failed: %d\n", error); -+ goto out; -+ } -+ -+ error = dprc_clear_irq_status(mc_io, 0, mc_dev->mc_handle, irq_index, -+ status); -+ if (error < 0) { -+ dev_err(dev, -+ "dprc_clear_irq_status() failed: %d\n", error); -+ goto out; -+ } -+ -+ if (status & (DPRC_IRQ_EVENT_OBJ_ADDED | -+ DPRC_IRQ_EVENT_OBJ_REMOVED | -+ DPRC_IRQ_EVENT_CONTAINER_DESTROYED | -+ DPRC_IRQ_EVENT_OBJ_DESTROYED | -+ DPRC_IRQ_EVENT_OBJ_CREATED)) { -+ unsigned int irq_count; -+ -+ error = dprc_scan_objects(mc_dev, NULL, &irq_count); -+ if (error < 0) { -+ if (error != -ENXIO) /* don't need to report aborted scan */ -+ dev_err(dev, "dprc_scan_objects() failed: %d\n", error); -+ goto out; -+ } -+ -+ WARN_ON((int16_t)irq_count < 0); -+ -+ if ((int16_t)irq_count > -+ mc_bus->resource_pools[FSL_MC_POOL_IRQ].max_count) { -+ dev_warn(dev, -+ "IRQs needed (%u) exceed IRQs preallocated (%u)\n", -+ irq_count, -+ mc_bus->resource_pools[FSL_MC_POOL_IRQ]. -+ max_count); -+ } -+ } -+ -+out: -+ mutex_unlock(&mc_bus->scan_mutex); -+ return IRQ_HANDLED; -+} -+ -+/* -+ * Disable and clear interrupts for a given DPRC object -+ */ -+static int disable_dprc_irqs(struct fsl_mc_device *mc_dev) -+{ -+ int i; -+ int error; -+ struct fsl_mc_io *mc_io = mc_dev->mc_io; -+ int irq_count = mc_dev->obj_desc.irq_count; -+ -+ if (WARN_ON(irq_count == 0)) -+ return -EINVAL; -+ -+ for (i = 0; i < irq_count; i++) { -+ /* -+ * Disable generation of interrupt i, while we configure it: -+ */ -+ error = dprc_set_irq_enable(mc_io, 0, mc_dev->mc_handle, i, 0); -+ if (error < 0) { -+ dev_err(&mc_dev->dev, -+ "Disabling DPRC IRQ %d failed: dprc_set_irq_enable() failed: %d\n", -+ i, error); -+ -+ return error; -+ } -+ -+ /* -+ * Disable all interrupt causes for interrupt i: -+ */ -+ error = dprc_set_irq_mask(mc_io, 0, mc_dev->mc_handle, i, 0x0); -+ if (error < 0) { -+ dev_err(&mc_dev->dev, -+ "Disabling DPRC IRQ %d failed: dprc_set_irq_mask() failed: %d\n", -+ i, error); -+ -+ return error; -+ } -+ -+ /* -+ * Clear any leftover interrupt i: -+ */ -+ error = dprc_clear_irq_status(mc_io, 0, mc_dev->mc_handle, i, -+ ~0x0U); -+ if (error < 0) { -+ dev_err(&mc_dev->dev, -+ "Disabling DPRC IRQ %d failed: dprc_clear_irq_status() failed: %d\n", -+ i, error); -+ -+ return error; -+ } -+ } -+ -+ return 0; -+} -+ -+static void unregister_dprc_irq_handlers(struct fsl_mc_device *mc_dev) -+{ -+ int i; -+ struct fsl_mc_device_irq *irq; -+ int irq_count = mc_dev->obj_desc.irq_count; -+ -+ for (i = 0; i < irq_count; i++) { -+ irq = mc_dev->irqs[i]; -+ devm_free_irq(&mc_dev->dev, irq->irq_number, -+ &mc_dev->dev); -+ } -+} -+ -+static int register_dprc_irq_handlers(struct fsl_mc_device *mc_dev) -+{ -+ static const struct irq_handler { -+ irq_handler_t irq_handler; -+ irq_handler_t irq_handler_thread; -+ const char *irq_name; -+ } irq_handlers[] = { -+ [0] = { -+ .irq_handler = dprc_irq0_handler, -+ .irq_handler_thread = dprc_irq0_handler_thread, -+ .irq_name = "FSL MC DPRC irq0", -+ }, -+ }; -+ -+ unsigned int i; -+ int error; -+ struct fsl_mc_device_irq *irq; -+ unsigned int num_irq_handlers_registered = 0; -+ int irq_count = mc_dev->obj_desc.irq_count; -+ -+ if (WARN_ON(irq_count != ARRAY_SIZE(irq_handlers))) -+ return -EINVAL; -+ -+ for (i = 0; i < ARRAY_SIZE(irq_handlers); i++) { -+ irq = mc_dev->irqs[i]; -+ -+ /* -+ * NOTE: devm_request_threaded_irq() invokes the device-specific -+ * function that programs the MSI physically in the device -+ */ -+ error = devm_request_threaded_irq(&mc_dev->dev, -+ irq->irq_number, -+ irq_handlers[i].irq_handler, -+ irq_handlers[i]. -+ irq_handler_thread, -+ IRQF_NO_SUSPEND | -+ IRQF_ONESHOT, -+ irq_handlers[i].irq_name, -+ &mc_dev->dev); -+ if (error < 0) { -+ dev_err(&mc_dev->dev, -+ "devm_request_threaded_irq() failed: %d\n", -+ error); -+ goto error_unregister_irq_handlers; -+ } -+ -+ num_irq_handlers_registered++; -+ } -+ -+ return 0; -+ -+error_unregister_irq_handlers: -+ for (i = 0; i < num_irq_handlers_registered; i++) { -+ irq = mc_dev->irqs[i]; -+ devm_free_irq(&mc_dev->dev, irq->irq_number, -+ &mc_dev->dev); -+ } -+ -+ return error; -+} -+ -+static int enable_dprc_irqs(struct fsl_mc_device *mc_dev) -+{ -+ int i; -+ int error; -+ int irq_count = mc_dev->obj_desc.irq_count; -+ -+ for (i = 0; i < irq_count; i++) { -+ /* -+ * Enable all interrupt causes for the interrupt: -+ */ -+ error = dprc_set_irq_mask(mc_dev->mc_io, -+ 0, -+ mc_dev->mc_handle, -+ i, -+ ~0x0u); -+ if (error < 0) { -+ dev_err(&mc_dev->dev, -+ "Enabling DPRC IRQ %d failed: dprc_set_irq_mask() failed: %d\n", -+ i, error); -+ -+ return error; -+ } -+ -+ /* -+ * Enable generation of the interrupt: -+ */ -+ error = dprc_set_irq_enable(mc_dev->mc_io, -+ 0, -+ mc_dev->mc_handle, -+ i, 1); -+ if (error < 0) { -+ dev_err(&mc_dev->dev, -+ "Enabling DPRC IRQ %d failed: dprc_set_irq_enable() failed: %d\n", -+ i, error); -+ -+ return error; -+ } -+ } -+ -+ return 0; -+} -+ -+/* -+ * Setup interrupts for a given DPRC device -+ */ -+static int dprc_setup_irqs(struct fsl_mc_device *mc_dev) -+{ -+ int error; -+ -+ error = fsl_mc_allocate_irqs(mc_dev); -+ if (error < 0) -+ return error; -+ -+ error = disable_dprc_irqs(mc_dev); -+ if (error < 0) -+ goto error_free_irqs; -+ -+ error = register_dprc_irq_handlers(mc_dev); -+ if (error < 0) -+ goto error_free_irqs; -+ -+ error = enable_dprc_irqs(mc_dev); -+ if (error < 0) -+ goto error_unregister_irq_handlers; -+ -+ return 0; -+ -+error_unregister_irq_handlers: -+ unregister_dprc_irq_handlers(mc_dev); -+ -+error_free_irqs: -+ fsl_mc_free_irqs(mc_dev); -+ return error; -+} -+ -+/* -+ * Creates a DPMCP for a DPRC's built-in MC portal -+ */ -+static int dprc_create_dpmcp(struct fsl_mc_device *dprc_dev) -+{ -+ int error; -+ struct dpmcp_cfg dpmcp_cfg; -+ uint16_t dpmcp_handle; -+ struct dprc_res_req res_req; -+ struct dpmcp_attr dpmcp_attr; -+ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(dprc_dev); -+ -+ dpmcp_cfg.portal_id = mc_bus->dprc_attr.portal_id; -+ error = dpmcp_create(dprc_dev->mc_io, -+ MC_CMD_FLAG_INTR_DIS, -+ &dpmcp_cfg, -+ &dpmcp_handle); -+ if (error < 0) { -+ dev_err(&dprc_dev->dev, "dpmcp_create() failed: %d\n", -+ error); -+ return error; -+ } -+ -+ /* -+ * Set the state of the newly created DPMCP object to be "plugged": -+ */ -+ -+ error = dpmcp_get_attributes(dprc_dev->mc_io, -+ MC_CMD_FLAG_INTR_DIS, -+ dpmcp_handle, -+ &dpmcp_attr); -+ if (error < 0) { -+ dev_err(&dprc_dev->dev, "dpmcp_get_attributes() failed: %d\n", -+ error); -+ goto error_destroy_dpmcp; -+ } -+ -+ if (WARN_ON(dpmcp_attr.id != mc_bus->dprc_attr.portal_id)) { -+ error = -EINVAL; -+ goto error_destroy_dpmcp; -+ } -+ -+ strcpy(res_req.type, "dpmcp"); -+ res_req.num = 1; -+ res_req.options = -+ (DPRC_RES_REQ_OPT_EXPLICIT | DPRC_RES_REQ_OPT_PLUGGED); -+ res_req.id_base_align = dpmcp_attr.id; -+ -+ error = dprc_assign(dprc_dev->mc_io, -+ MC_CMD_FLAG_INTR_DIS, -+ dprc_dev->mc_handle, -+ dprc_dev->obj_desc.id, -+ &res_req); -+ -+ if (error < 0) { -+ dev_err(&dprc_dev->dev, "dprc_assign() failed: %d\n", error); -+ goto error_destroy_dpmcp; -+ } -+ -+ (void)dpmcp_close(dprc_dev->mc_io, -+ MC_CMD_FLAG_INTR_DIS, -+ dpmcp_handle); -+ return 0; -+ -+error_destroy_dpmcp: -+ (void)dpmcp_destroy(dprc_dev->mc_io, -+ MC_CMD_FLAG_INTR_DIS, -+ dpmcp_handle); -+ return error; -+} -+ -+/* -+ * Destroys the DPMCP for a DPRC's built-in MC portal -+ */ -+static void dprc_destroy_dpmcp(struct fsl_mc_device *dprc_dev) -+{ -+ int error; -+ uint16_t dpmcp_handle; -+ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(dprc_dev); -+ -+ if (WARN_ON(!dprc_dev->mc_io || dprc_dev->mc_io->dpmcp_dev)) -+ return; -+ -+ error = dpmcp_open(dprc_dev->mc_io, -+ MC_CMD_FLAG_INTR_DIS, -+ mc_bus->dprc_attr.portal_id, -+ &dpmcp_handle); -+ if (error < 0) { -+ dev_err(&dprc_dev->dev, "dpmcp_open() failed: %d\n", -+ error); -+ return; -+ } -+ -+ error = dpmcp_destroy(dprc_dev->mc_io, -+ MC_CMD_FLAG_INTR_DIS, -+ dpmcp_handle); -+ if (error < 0) { -+ dev_err(&dprc_dev->dev, "dpmcp_destroy() failed: %d\n", -+ error); -+ return; -+ } -+} -+ -+/** -+ * dprc_probe - callback invoked when a DPRC is being bound to this driver -+ * -+ * @mc_dev: Pointer to fsl-mc device representing a DPRC -+ * -+ * It opens the physical DPRC in the MC. -+ * It scans the DPRC to discover the MC objects contained in it. -+ * It creates the interrupt pool for the MC bus associated with the DPRC. -+ * It configures the interrupts for the DPRC device itself. -+ */ -+static int dprc_probe(struct fsl_mc_device *mc_dev) -+{ -+ int error; -+ size_t region_size; -+ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev); -+ bool mc_io_created = false; -+ bool dev_root_set = false; -+ -+ if (WARN_ON(strcmp(mc_dev->obj_desc.type, "dprc") != 0)) -+ return -EINVAL; -+ -+ if (mc_dev->mc_io) { -+ /* -+ * This is the root DPRC -+ */ -+ if (WARN_ON(fsl_mc_bus_type.dev_root)) -+ return -EINVAL; -+ -+ fsl_mc_bus_type.dev_root = &mc_dev->dev; -+ dev_root_set = true; -+ } else { -+ /* -+ * This is a child DPRC -+ */ -+ if (WARN_ON(!fsl_mc_bus_type.dev_root)) -+ return -EINVAL; -+ -+ if (WARN_ON(mc_dev->obj_desc.region_count == 0)) -+ return -EINVAL; -+ -+ region_size = mc_dev->regions[0].end - -+ mc_dev->regions[0].start + 1; -+ -+ error = fsl_create_mc_io(&mc_dev->dev, -+ mc_dev->regions[0].start, -+ region_size, -+ NULL, 0, &mc_dev->mc_io); -+ if (error < 0) -+ return error; -+ -+ mc_io_created = true; -+ } -+ -+ error = dprc_open(mc_dev->mc_io, 0, mc_dev->obj_desc.id, -+ &mc_dev->mc_handle); -+ if (error < 0) { -+ dev_err(&mc_dev->dev, "dprc_open() failed: %d\n", error); -+ goto error_cleanup_mc_io; -+ } -+ -+ error = dprc_get_attributes(mc_dev->mc_io, 0, mc_dev->mc_handle, -+ &mc_bus->dprc_attr); -+ if (error < 0) { -+ dev_err(&mc_dev->dev, "dprc_get_attributes() failed: %d\n", -+ error); -+ goto error_cleanup_open; -+ } -+ -+ if (mc_bus->dprc_attr.version.major < DPRC_MIN_VER_MAJOR || -+ (mc_bus->dprc_attr.version.major == DPRC_MIN_VER_MAJOR && -+ mc_bus->dprc_attr.version.minor < DPRC_MIN_VER_MINOR)) { -+ dev_err(&mc_dev->dev, -+ "ERROR: DPRC version %d.%d not supported\n", -+ mc_bus->dprc_attr.version.major, -+ mc_bus->dprc_attr.version.minor); -+ error = -ENOTSUPP; -+ goto error_cleanup_open; -+ } -+ -+ if (fsl_mc_interrupts_supported()) { -+ /* -+ * Create DPMCP for the DPRC's built-in portal: -+ */ -+ error = dprc_create_dpmcp(mc_dev); -+ if (error < 0) -+ goto error_cleanup_open; -+ } -+ -+ mutex_init(&mc_bus->scan_mutex); -+ -+ /* -+ * Discover MC objects in the DPRC object: -+ */ -+ error = dprc_scan_container(mc_dev); -+ if (error < 0) -+ goto error_destroy_dpmcp; -+ -+ if (fsl_mc_interrupts_supported()) { -+ /* -+ * The fsl_mc_device object associated with the DPMCP object -+ * created above was created as part of the -+ * dprc_scan_container() call above: -+ */ -+ if (WARN_ON(!mc_dev->mc_io->dpmcp_dev)) { -+ error = -EINVAL; -+ goto error_cleanup_dprc_scan; -+ } -+ -+ /* -+ * Allocate MC portal to be used in atomic context -+ * (e.g., to program MSIs from program_msi_at_mc()) -+ */ -+ error = fsl_mc_portal_allocate(NULL, -+ FSL_MC_IO_ATOMIC_CONTEXT_PORTAL, -+ &mc_bus->atomic_mc_io); -+ if (error < 0) -+ goto error_cleanup_dprc_scan; -+ -+ pr_info("fsl-mc: Allocated dpmcp.%d to dprc.%d for atomic MC I/O\n", -+ mc_bus->atomic_mc_io->dpmcp_dev->obj_desc.id, -+ mc_dev->obj_desc.id); -+ -+ /* -+ * Open DPRC handle to be used with mc_bus->atomic_mc_io: -+ */ -+ error = dprc_open(mc_bus->atomic_mc_io, 0, mc_dev->obj_desc.id, -+ &mc_bus->atomic_dprc_handle); -+ if (error < 0) { -+ dev_err(&mc_dev->dev, "dprc_open() failed: %d\n", -+ error); -+ goto error_cleanup_atomic_mc_io; -+ } -+ -+ /* -+ * Configure interrupt for the DPMCP object associated with the -+ * DPRC object's built-in portal: -+ * -+ * NOTE: We have to do this after calling dprc_scan_container(), -+ * since dprc_scan_container() populates the IRQ pool for -+ * this DPRC. -+ */ -+ error = fsl_mc_io_setup_dpmcp_irq(mc_dev->mc_io); -+ if (error < 0) -+ goto error_cleanup_atomic_dprc_handle; -+ -+ /* -+ * Configure interrupts for the DPRC object associated with -+ * this MC bus: -+ */ -+ error = dprc_setup_irqs(mc_dev); -+ if (error < 0) -+ goto error_cleanup_atomic_dprc_handle; -+ } -+ -+ dev_info(&mc_dev->dev, "DPRC device bound to driver"); -+ return 0; -+ -+error_cleanup_atomic_dprc_handle: -+ (void)dprc_close(mc_bus->atomic_mc_io, 0, mc_bus->atomic_dprc_handle); -+ -+error_cleanup_atomic_mc_io: -+ fsl_mc_portal_free(mc_bus->atomic_mc_io); -+ -+error_cleanup_dprc_scan: -+ fsl_mc_io_unset_dpmcp(mc_dev->mc_io); -+ device_for_each_child(&mc_dev->dev, NULL, __fsl_mc_device_remove); -+ dprc_cleanup_all_resource_pools(mc_dev); -+ if (fsl_mc_interrupts_supported()) -+ fsl_mc_cleanup_irq_pool(mc_bus); -+ -+error_destroy_dpmcp: -+ dprc_destroy_dpmcp(mc_dev); -+ -+error_cleanup_open: -+ (void)dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle); -+ -+error_cleanup_mc_io: -+ if (mc_io_created) { -+ fsl_destroy_mc_io(mc_dev->mc_io); -+ mc_dev->mc_io = NULL; -+ } -+ -+ if (dev_root_set) -+ fsl_mc_bus_type.dev_root = NULL; -+ -+ return error; -+} -+ -+/* -+ * Tear down interrupts for a given DPRC object -+ */ -+static void dprc_teardown_irqs(struct fsl_mc_device *mc_dev) -+{ -+ (void)disable_dprc_irqs(mc_dev); -+ unregister_dprc_irq_handlers(mc_dev); -+ fsl_mc_free_irqs(mc_dev); -+} -+ -+/** -+ * dprc_remove - callback invoked when a DPRC is being unbound from this driver -+ * -+ * @mc_dev: Pointer to fsl-mc device representing the DPRC -+ * -+ * It removes the DPRC's child objects from Linux (not from the MC) and -+ * closes the DPRC device in the MC. -+ * It tears down the interrupts that were configured for the DPRC device. -+ * It destroys the interrupt pool associated with this MC bus. -+ */ -+static int dprc_remove(struct fsl_mc_device *mc_dev) -+{ -+ int error; -+ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev); -+ -+ if (WARN_ON(strcmp(mc_dev->obj_desc.type, "dprc") != 0)) -+ return -EINVAL; -+ if (WARN_ON(!mc_dev->mc_io)) -+ return -EINVAL; -+ -+ if (WARN_ON(!mc_bus->irq_resources)) -+ return -EINVAL; -+ -+ if (fsl_mc_interrupts_supported()) { -+ dprc_teardown_irqs(mc_dev); -+ error = dprc_close(mc_bus->atomic_mc_io, 0, -+ mc_bus->atomic_dprc_handle); -+ if (error < 0) { -+ dev_err(&mc_dev->dev, "dprc_close() failed: %d\n", -+ error); -+ } -+ -+ fsl_mc_portal_free(mc_bus->atomic_mc_io); -+ } -+ -+ fsl_mc_io_unset_dpmcp(mc_dev->mc_io); -+ device_for_each_child(&mc_dev->dev, NULL, __fsl_mc_device_remove); -+ dprc_cleanup_all_resource_pools(mc_dev); -+ dprc_destroy_dpmcp(mc_dev); -+ error = dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle); -+ if (error < 0) -+ dev_err(&mc_dev->dev, "dprc_close() failed: %d\n", error); -+ -+ if (fsl_mc_interrupts_supported()) -+ fsl_mc_cleanup_irq_pool(mc_bus); -+ -+ fsl_destroy_mc_io(mc_dev->mc_io); -+ mc_dev->mc_io = NULL; -+ -+ if (&mc_dev->dev == fsl_mc_bus_type.dev_root) -+ fsl_mc_bus_type.dev_root = NULL; -+ -+ dev_info(&mc_dev->dev, "DPRC device unbound from driver"); -+ return 0; -+} -+ -+static const struct fsl_mc_device_match_id match_id_table[] = { -+ { -+ .vendor = FSL_MC_VENDOR_FREESCALE, -+ .obj_type = "dprc"}, -+ {.vendor = 0x0}, -+}; -+ -+static struct fsl_mc_driver dprc_driver = { -+ .driver = { -+ .name = FSL_MC_DPRC_DRIVER_NAME, -+ .owner = THIS_MODULE, -+ .pm = NULL, -+ }, -+ .match_id_table = match_id_table, -+ .probe = dprc_probe, -+ .remove = dprc_remove, -+}; -+ -+int __init dprc_driver_init(void) -+{ -+ return fsl_mc_driver_register(&dprc_driver); -+} -+ -+void dprc_driver_exit(void) -+{ -+ fsl_mc_driver_unregister(&dprc_driver); -+} -diff --git a/drivers/staging/fsl-mc/bus/dprc.c b/drivers/staging/fsl-mc/bus/dprc.c -new file mode 100644 -index 0000000..4d86438 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dprc.c -@@ -0,0 +1,1218 @@ -+/* Copyright 2013-2014 Freescale Semiconductor Inc. -+* -+* Redistribution and use in source and binary forms, with or without -+* modification, are permitted provided that the following conditions are met: -+* * Redistributions of source code must retain the above copyright -+* notice, this list of conditions and the following disclaimer. -+* * 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. -+* * Neither the name of the above-listed copyright holders nor the -+* names of any contributors may be used to endorse or promote products -+* derived from this software without specific prior written permission. -+* -+* -+* ALTERNATIVELY, this software may be distributed under the terms of the -+* GNU General Public License ("GPL") as published by the Free Software -+* Foundation, either version 2 of that License or (at your option) any -+* later version. -+* -+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+*/ -+#include "../include/mc-sys.h" -+#include "../include/mc-cmd.h" -+#include "../include/dprc.h" -+#include "dprc-cmd.h" -+ -+int dprc_open(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ int container_id, -+ uint16_t *token) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_OPEN, cmd_flags, -+ 0); -+ cmd.params[0] |= mc_enc(0, 32, container_id); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *token = MC_CMD_HDR_READ_TOKEN(cmd.header); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dprc_open); -+ -+int dprc_close(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_CLOSE, cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dprc_close); -+ -+int dprc_create_container(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dprc_cfg *cfg, -+ int *child_container_id, -+ uint64_t *child_portal_offset) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.params[0] |= mc_enc(32, 16, cfg->icid); -+ cmd.params[0] |= mc_enc(0, 32, cfg->options); -+ cmd.params[1] |= mc_enc(32, 32, cfg->portal_id); -+ cmd.params[2] |= mc_enc(0, 8, cfg->label[0]); -+ cmd.params[2] |= mc_enc(8, 8, cfg->label[1]); -+ cmd.params[2] |= mc_enc(16, 8, cfg->label[2]); -+ cmd.params[2] |= mc_enc(24, 8, cfg->label[3]); -+ cmd.params[2] |= mc_enc(32, 8, cfg->label[4]); -+ cmd.params[2] |= mc_enc(40, 8, cfg->label[5]); -+ cmd.params[2] |= mc_enc(48, 8, cfg->label[6]); -+ cmd.params[2] |= mc_enc(56, 8, cfg->label[7]); -+ cmd.params[3] |= mc_enc(0, 8, cfg->label[8]); -+ cmd.params[3] |= mc_enc(8, 8, cfg->label[9]); -+ cmd.params[3] |= mc_enc(16, 8, cfg->label[10]); -+ cmd.params[3] |= mc_enc(24, 8, cfg->label[11]); -+ cmd.params[3] |= mc_enc(32, 8, cfg->label[12]); -+ cmd.params[3] |= mc_enc(40, 8, cfg->label[13]); -+ cmd.params[3] |= mc_enc(48, 8, cfg->label[14]); -+ cmd.params[3] |= mc_enc(56, 8, cfg->label[15]); -+ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_CREATE_CONT, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *child_container_id = mc_dec(cmd.params[1], 0, 32); -+ *child_portal_offset = mc_dec(cmd.params[2], 0, 64); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dprc_create_container); -+ -+int dprc_destroy_container(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int child_container_id) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_DESTROY_CONT, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, child_container_id); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dprc_destroy_container); -+ -+int dprc_reset_container(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int child_container_id) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_RESET_CONT, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, child_container_id); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dprc_reset_container); -+ -+int dprc_get_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ int *type, -+ struct dprc_irq_cfg *irq_cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_IRQ, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ irq_cfg->val = mc_dec(cmd.params[0], 0, 32); -+ irq_cfg->paddr = mc_dec(cmd.params[1], 0, 64); -+ irq_cfg->irq_num = mc_dec(cmd.params[2], 0, 32); -+ *type = mc_dec(cmd.params[2], 32, 32); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dprc_get_irq); -+ -+int dprc_set_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ struct dprc_irq_cfg *irq_cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_SET_IRQ, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ cmd.params[0] |= mc_enc(0, 32, irq_cfg->val); -+ cmd.params[1] |= mc_enc(0, 64, irq_cfg->paddr); -+ cmd.params[2] |= mc_enc(0, 32, irq_cfg->irq_num); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dprc_set_irq); -+ -+int dprc_get_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t *en) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_IRQ_ENABLE, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *en = mc_dec(cmd.params[0], 0, 8); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dprc_get_irq_enable); -+ -+int dprc_set_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t en) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_SET_IRQ_ENABLE, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 8, en); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dprc_set_irq_enable); -+ -+int dprc_get_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *mask) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_IRQ_MASK, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *mask = mc_dec(cmd.params[0], 0, 32); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dprc_get_irq_mask); -+ -+int dprc_set_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t mask) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_SET_IRQ_MASK, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, mask); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dprc_set_irq_mask); -+ -+int dprc_get_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *status) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_IRQ_STATUS, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, *status); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *status = mc_dec(cmd.params[0], 0, 32); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dprc_get_irq_status); -+ -+int dprc_clear_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t status) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_CLEAR_IRQ_STATUS, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, status); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dprc_clear_irq_status); -+ -+int dprc_get_attributes(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dprc_attributes *attr) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_ATTR, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ attr->container_id = mc_dec(cmd.params[0], 0, 32); -+ attr->icid = mc_dec(cmd.params[0], 32, 16); -+ attr->options = mc_dec(cmd.params[1], 0, 32); -+ attr->portal_id = mc_dec(cmd.params[1], 32, 32); -+ attr->version.major = mc_dec(cmd.params[2], 0, 16); -+ attr->version.minor = mc_dec(cmd.params[2], 16, 16); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dprc_get_attributes); -+ -+int dprc_set_res_quota(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int child_container_id, -+ char *type, -+ uint16_t quota) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_SET_RES_QUOTA, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, child_container_id); -+ cmd.params[0] |= mc_enc(32, 16, quota); -+ cmd.params[1] |= mc_enc(0, 8, type[0]); -+ cmd.params[1] |= mc_enc(8, 8, type[1]); -+ cmd.params[1] |= mc_enc(16, 8, type[2]); -+ cmd.params[1] |= mc_enc(24, 8, type[3]); -+ cmd.params[1] |= mc_enc(32, 8, type[4]); -+ cmd.params[1] |= mc_enc(40, 8, type[5]); -+ cmd.params[1] |= mc_enc(48, 8, type[6]); -+ cmd.params[1] |= mc_enc(56, 8, type[7]); -+ cmd.params[2] |= mc_enc(0, 8, type[8]); -+ cmd.params[2] |= mc_enc(8, 8, type[9]); -+ cmd.params[2] |= mc_enc(16, 8, type[10]); -+ cmd.params[2] |= mc_enc(24, 8, type[11]); -+ cmd.params[2] |= mc_enc(32, 8, type[12]); -+ cmd.params[2] |= mc_enc(40, 8, type[13]); -+ cmd.params[2] |= mc_enc(48, 8, type[14]); -+ cmd.params[2] |= mc_enc(56, 8, '\0'); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dprc_set_res_quota); -+ -+int dprc_get_res_quota(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int child_container_id, -+ char *type, -+ uint16_t *quota) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_RES_QUOTA, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, child_container_id); -+ cmd.params[1] |= mc_enc(0, 8, type[0]); -+ cmd.params[1] |= mc_enc(8, 8, type[1]); -+ cmd.params[1] |= mc_enc(16, 8, type[2]); -+ cmd.params[1] |= mc_enc(24, 8, type[3]); -+ cmd.params[1] |= mc_enc(32, 8, type[4]); -+ cmd.params[1] |= mc_enc(40, 8, type[5]); -+ cmd.params[1] |= mc_enc(48, 8, type[6]); -+ cmd.params[1] |= mc_enc(56, 8, type[7]); -+ cmd.params[2] |= mc_enc(0, 8, type[8]); -+ cmd.params[2] |= mc_enc(8, 8, type[9]); -+ cmd.params[2] |= mc_enc(16, 8, type[10]); -+ cmd.params[2] |= mc_enc(24, 8, type[11]); -+ cmd.params[2] |= mc_enc(32, 8, type[12]); -+ cmd.params[2] |= mc_enc(40, 8, type[13]); -+ cmd.params[2] |= mc_enc(48, 8, type[14]); -+ cmd.params[2] |= mc_enc(56, 8, '\0'); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *quota = mc_dec(cmd.params[0], 32, 16); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dprc_get_res_quota); -+ -+int dprc_assign(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int container_id, -+ struct dprc_res_req *res_req) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_ASSIGN, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, container_id); -+ cmd.params[0] |= mc_enc(32, 32, res_req->options); -+ cmd.params[1] |= mc_enc(0, 32, res_req->num); -+ cmd.params[1] |= mc_enc(32, 32, res_req->id_base_align); -+ cmd.params[2] |= mc_enc(0, 8, res_req->type[0]); -+ cmd.params[2] |= mc_enc(8, 8, res_req->type[1]); -+ cmd.params[2] |= mc_enc(16, 8, res_req->type[2]); -+ cmd.params[2] |= mc_enc(24, 8, res_req->type[3]); -+ cmd.params[2] |= mc_enc(32, 8, res_req->type[4]); -+ cmd.params[2] |= mc_enc(40, 8, res_req->type[5]); -+ cmd.params[2] |= mc_enc(48, 8, res_req->type[6]); -+ cmd.params[2] |= mc_enc(56, 8, res_req->type[7]); -+ cmd.params[3] |= mc_enc(0, 8, res_req->type[8]); -+ cmd.params[3] |= mc_enc(8, 8, res_req->type[9]); -+ cmd.params[3] |= mc_enc(16, 8, res_req->type[10]); -+ cmd.params[3] |= mc_enc(24, 8, res_req->type[11]); -+ cmd.params[3] |= mc_enc(32, 8, res_req->type[12]); -+ cmd.params[3] |= mc_enc(40, 8, res_req->type[13]); -+ cmd.params[3] |= mc_enc(48, 8, res_req->type[14]); -+ cmd.params[3] |= mc_enc(56, 8, res_req->type[15]); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dprc_assign); -+ -+int dprc_unassign(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int child_container_id, -+ struct dprc_res_req *res_req) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_UNASSIGN, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, child_container_id); -+ cmd.params[0] |= mc_enc(32, 32, res_req->options); -+ cmd.params[1] |= mc_enc(0, 32, res_req->num); -+ cmd.params[1] |= mc_enc(32, 32, res_req->id_base_align); -+ cmd.params[2] |= mc_enc(0, 8, res_req->type[0]); -+ cmd.params[2] |= mc_enc(8, 8, res_req->type[1]); -+ cmd.params[2] |= mc_enc(16, 8, res_req->type[2]); -+ cmd.params[2] |= mc_enc(24, 8, res_req->type[3]); -+ cmd.params[2] |= mc_enc(32, 8, res_req->type[4]); -+ cmd.params[2] |= mc_enc(40, 8, res_req->type[5]); -+ cmd.params[2] |= mc_enc(48, 8, res_req->type[6]); -+ cmd.params[2] |= mc_enc(56, 8, res_req->type[7]); -+ cmd.params[3] |= mc_enc(0, 8, res_req->type[8]); -+ cmd.params[3] |= mc_enc(8, 8, res_req->type[9]); -+ cmd.params[3] |= mc_enc(16, 8, res_req->type[10]); -+ cmd.params[3] |= mc_enc(24, 8, res_req->type[11]); -+ cmd.params[3] |= mc_enc(32, 8, res_req->type[12]); -+ cmd.params[3] |= mc_enc(40, 8, res_req->type[13]); -+ cmd.params[3] |= mc_enc(48, 8, res_req->type[14]); -+ cmd.params[3] |= mc_enc(56, 8, res_req->type[15]); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dprc_unassign); -+ -+int dprc_get_pool_count(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *pool_count) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_POOL_COUNT, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *pool_count = mc_dec(cmd.params[0], 0, 32); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dprc_get_pool_count); -+ -+int dprc_get_pool(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int pool_index, -+ char *type) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_POOL, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, pool_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ type[0] = mc_dec(cmd.params[1], 0, 8); -+ type[1] = mc_dec(cmd.params[1], 8, 8); -+ type[2] = mc_dec(cmd.params[1], 16, 8); -+ type[3] = mc_dec(cmd.params[1], 24, 8); -+ type[4] = mc_dec(cmd.params[1], 32, 8); -+ type[5] = mc_dec(cmd.params[1], 40, 8); -+ type[6] = mc_dec(cmd.params[1], 48, 8); -+ type[7] = mc_dec(cmd.params[1], 56, 8); -+ type[8] = mc_dec(cmd.params[2], 0, 8); -+ type[9] = mc_dec(cmd.params[2], 8, 8); -+ type[10] = mc_dec(cmd.params[2], 16, 8); -+ type[11] = mc_dec(cmd.params[2], 24, 8); -+ type[12] = mc_dec(cmd.params[2], 32, 8); -+ type[13] = mc_dec(cmd.params[2], 40, 8); -+ type[14] = mc_dec(cmd.params[2], 48, 8); -+ type[15] = '\0'; -+ -+ return 0; -+} -+EXPORT_SYMBOL(dprc_get_pool); -+ -+int dprc_get_obj_count(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *obj_count) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_OBJ_COUNT, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *obj_count = mc_dec(cmd.params[0], 32, 32); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dprc_get_obj_count); -+ -+int dprc_get_obj(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int obj_index, -+ struct dprc_obj_desc *obj_desc) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_OBJ, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, obj_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ obj_desc->id = mc_dec(cmd.params[0], 32, 32); -+ obj_desc->vendor = mc_dec(cmd.params[1], 0, 16); -+ obj_desc->irq_count = mc_dec(cmd.params[1], 16, 8); -+ obj_desc->region_count = mc_dec(cmd.params[1], 24, 8); -+ obj_desc->state = mc_dec(cmd.params[1], 32, 32); -+ obj_desc->ver_major = mc_dec(cmd.params[2], 0, 16); -+ obj_desc->ver_minor = mc_dec(cmd.params[2], 16, 16); -+ obj_desc->flags = mc_dec(cmd.params[2], 32, 16); -+ obj_desc->type[0] = mc_dec(cmd.params[3], 0, 8); -+ obj_desc->type[1] = mc_dec(cmd.params[3], 8, 8); -+ obj_desc->type[2] = mc_dec(cmd.params[3], 16, 8); -+ obj_desc->type[3] = mc_dec(cmd.params[3], 24, 8); -+ obj_desc->type[4] = mc_dec(cmd.params[3], 32, 8); -+ obj_desc->type[5] = mc_dec(cmd.params[3], 40, 8); -+ obj_desc->type[6] = mc_dec(cmd.params[3], 48, 8); -+ obj_desc->type[7] = mc_dec(cmd.params[3], 56, 8); -+ obj_desc->type[8] = mc_dec(cmd.params[4], 0, 8); -+ obj_desc->type[9] = mc_dec(cmd.params[4], 8, 8); -+ obj_desc->type[10] = mc_dec(cmd.params[4], 16, 8); -+ obj_desc->type[11] = mc_dec(cmd.params[4], 24, 8); -+ obj_desc->type[12] = mc_dec(cmd.params[4], 32, 8); -+ obj_desc->type[13] = mc_dec(cmd.params[4], 40, 8); -+ obj_desc->type[14] = mc_dec(cmd.params[4], 48, 8); -+ obj_desc->type[15] = '\0'; -+ obj_desc->label[0] = mc_dec(cmd.params[5], 0, 8); -+ obj_desc->label[1] = mc_dec(cmd.params[5], 8, 8); -+ obj_desc->label[2] = mc_dec(cmd.params[5], 16, 8); -+ obj_desc->label[3] = mc_dec(cmd.params[5], 24, 8); -+ obj_desc->label[4] = mc_dec(cmd.params[5], 32, 8); -+ obj_desc->label[5] = mc_dec(cmd.params[5], 40, 8); -+ obj_desc->label[6] = mc_dec(cmd.params[5], 48, 8); -+ obj_desc->label[7] = mc_dec(cmd.params[5], 56, 8); -+ obj_desc->label[8] = mc_dec(cmd.params[6], 0, 8); -+ obj_desc->label[9] = mc_dec(cmd.params[6], 8, 8); -+ obj_desc->label[10] = mc_dec(cmd.params[6], 16, 8); -+ obj_desc->label[11] = mc_dec(cmd.params[6], 24, 8); -+ obj_desc->label[12] = mc_dec(cmd.params[6], 32, 8); -+ obj_desc->label[13] = mc_dec(cmd.params[6], 40, 8); -+ obj_desc->label[14] = mc_dec(cmd.params[6], 48, 8); -+ obj_desc->label[15] = '\0'; -+ return 0; -+} -+EXPORT_SYMBOL(dprc_get_obj); -+ -+int dprc_get_obj_desc(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ char *obj_type, -+ int obj_id, -+ struct dprc_obj_desc *obj_desc) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_OBJ_DESC, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, obj_id); -+ cmd.params[1] |= mc_enc(0, 8, obj_type[0]); -+ cmd.params[1] |= mc_enc(8, 8, obj_type[1]); -+ cmd.params[1] |= mc_enc(16, 8, obj_type[2]); -+ cmd.params[1] |= mc_enc(24, 8, obj_type[3]); -+ cmd.params[1] |= mc_enc(32, 8, obj_type[4]); -+ cmd.params[1] |= mc_enc(40, 8, obj_type[5]); -+ cmd.params[1] |= mc_enc(48, 8, obj_type[6]); -+ cmd.params[1] |= mc_enc(56, 8, obj_type[7]); -+ cmd.params[2] |= mc_enc(0, 8, obj_type[8]); -+ cmd.params[2] |= mc_enc(8, 8, obj_type[9]); -+ cmd.params[2] |= mc_enc(16, 8, obj_type[10]); -+ cmd.params[2] |= mc_enc(24, 8, obj_type[11]); -+ cmd.params[2] |= mc_enc(32, 8, obj_type[12]); -+ cmd.params[2] |= mc_enc(40, 8, obj_type[13]); -+ cmd.params[2] |= mc_enc(48, 8, obj_type[14]); -+ cmd.params[2] |= mc_enc(56, 8, obj_type[15]); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ obj_desc->id = (int)mc_dec(cmd.params[0], 32, 32); -+ obj_desc->vendor = (uint16_t)mc_dec(cmd.params[1], 0, 16); -+ obj_desc->vendor = (uint8_t)mc_dec(cmd.params[1], 16, 8); -+ obj_desc->region_count = (uint8_t)mc_dec(cmd.params[1], 24, 8); -+ obj_desc->state = (uint32_t)mc_dec(cmd.params[1], 32, 32); -+ obj_desc->ver_major = (uint16_t)mc_dec(cmd.params[2], 0, 16); -+ obj_desc->ver_minor = (uint16_t)mc_dec(cmd.params[2], 16, 16); -+ obj_desc->flags = mc_dec(cmd.params[2], 32, 16); -+ obj_desc->type[0] = (char)mc_dec(cmd.params[3], 0, 8); -+ obj_desc->type[1] = (char)mc_dec(cmd.params[3], 8, 8); -+ obj_desc->type[2] = (char)mc_dec(cmd.params[3], 16, 8); -+ obj_desc->type[3] = (char)mc_dec(cmd.params[3], 24, 8); -+ obj_desc->type[4] = (char)mc_dec(cmd.params[3], 32, 8); -+ obj_desc->type[5] = (char)mc_dec(cmd.params[3], 40, 8); -+ obj_desc->type[6] = (char)mc_dec(cmd.params[3], 48, 8); -+ obj_desc->type[7] = (char)mc_dec(cmd.params[3], 56, 8); -+ obj_desc->type[8] = (char)mc_dec(cmd.params[4], 0, 8); -+ obj_desc->type[9] = (char)mc_dec(cmd.params[4], 8, 8); -+ obj_desc->type[10] = (char)mc_dec(cmd.params[4], 16, 8); -+ obj_desc->type[11] = (char)mc_dec(cmd.params[4], 24, 8); -+ obj_desc->type[12] = (char)mc_dec(cmd.params[4], 32, 8); -+ obj_desc->type[13] = (char)mc_dec(cmd.params[4], 40, 8); -+ obj_desc->type[14] = (char)mc_dec(cmd.params[4], 48, 8); -+ obj_desc->type[15] = (char)mc_dec(cmd.params[4], 56, 8); -+ obj_desc->label[0] = (char)mc_dec(cmd.params[5], 0, 8); -+ obj_desc->label[1] = (char)mc_dec(cmd.params[5], 8, 8); -+ obj_desc->label[2] = (char)mc_dec(cmd.params[5], 16, 8); -+ obj_desc->label[3] = (char)mc_dec(cmd.params[5], 24, 8); -+ obj_desc->label[4] = (char)mc_dec(cmd.params[5], 32, 8); -+ obj_desc->label[5] = (char)mc_dec(cmd.params[5], 40, 8); -+ obj_desc->label[6] = (char)mc_dec(cmd.params[5], 48, 8); -+ obj_desc->label[7] = (char)mc_dec(cmd.params[5], 56, 8); -+ obj_desc->label[8] = (char)mc_dec(cmd.params[6], 0, 8); -+ obj_desc->label[9] = (char)mc_dec(cmd.params[6], 8, 8); -+ obj_desc->label[10] = (char)mc_dec(cmd.params[6], 16, 8); -+ obj_desc->label[11] = (char)mc_dec(cmd.params[6], 24, 8); -+ obj_desc->label[12] = (char)mc_dec(cmd.params[6], 32, 8); -+ obj_desc->label[13] = (char)mc_dec(cmd.params[6], 40, 8); -+ obj_desc->label[14] = (char)mc_dec(cmd.params[6], 48, 8); -+ obj_desc->label[15] = (char)mc_dec(cmd.params[6], 56, 8); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dprc_get_obj_desc); -+ -+int dprc_set_obj_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ char *obj_type, -+ int obj_id, -+ uint8_t irq_index, -+ struct dprc_irq_cfg *irq_cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_SET_OBJ_IRQ, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ cmd.params[0] |= mc_enc(0, 32, irq_cfg->val); -+ cmd.params[1] |= mc_enc(0, 64, irq_cfg->paddr); -+ cmd.params[2] |= mc_enc(0, 32, irq_cfg->irq_num); -+ cmd.params[2] |= mc_enc(32, 32, obj_id); -+ cmd.params[3] |= mc_enc(0, 8, obj_type[0]); -+ cmd.params[3] |= mc_enc(8, 8, obj_type[1]); -+ cmd.params[3] |= mc_enc(16, 8, obj_type[2]); -+ cmd.params[3] |= mc_enc(24, 8, obj_type[3]); -+ cmd.params[3] |= mc_enc(32, 8, obj_type[4]); -+ cmd.params[3] |= mc_enc(40, 8, obj_type[5]); -+ cmd.params[3] |= mc_enc(48, 8, obj_type[6]); -+ cmd.params[3] |= mc_enc(56, 8, obj_type[7]); -+ cmd.params[4] |= mc_enc(0, 8, obj_type[8]); -+ cmd.params[4] |= mc_enc(8, 8, obj_type[9]); -+ cmd.params[4] |= mc_enc(16, 8, obj_type[10]); -+ cmd.params[4] |= mc_enc(24, 8, obj_type[11]); -+ cmd.params[4] |= mc_enc(32, 8, obj_type[12]); -+ cmd.params[4] |= mc_enc(40, 8, obj_type[13]); -+ cmd.params[4] |= mc_enc(48, 8, obj_type[14]); -+ cmd.params[4] |= mc_enc(56, 8, obj_type[15]); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dprc_set_obj_irq); -+ -+int dprc_get_obj_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ char *obj_type, -+ int obj_id, -+ uint8_t irq_index, -+ int *type, -+ struct dprc_irq_cfg *irq_cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_OBJ_IRQ, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, obj_id); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ cmd.params[1] |= mc_enc(0, 8, obj_type[0]); -+ cmd.params[1] |= mc_enc(8, 8, obj_type[1]); -+ cmd.params[1] |= mc_enc(16, 8, obj_type[2]); -+ cmd.params[1] |= mc_enc(24, 8, obj_type[3]); -+ cmd.params[1] |= mc_enc(32, 8, obj_type[4]); -+ cmd.params[1] |= mc_enc(40, 8, obj_type[5]); -+ cmd.params[1] |= mc_enc(48, 8, obj_type[6]); -+ cmd.params[1] |= mc_enc(56, 8, obj_type[7]); -+ cmd.params[2] |= mc_enc(0, 8, obj_type[8]); -+ cmd.params[2] |= mc_enc(8, 8, obj_type[9]); -+ cmd.params[2] |= mc_enc(16, 8, obj_type[10]); -+ cmd.params[2] |= mc_enc(24, 8, obj_type[11]); -+ cmd.params[2] |= mc_enc(32, 8, obj_type[12]); -+ cmd.params[2] |= mc_enc(40, 8, obj_type[13]); -+ cmd.params[2] |= mc_enc(48, 8, obj_type[14]); -+ cmd.params[2] |= mc_enc(56, 8, obj_type[15]); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ irq_cfg->val = (uint32_t)mc_dec(cmd.params[0], 0, 32); -+ irq_cfg->paddr = (uint64_t)mc_dec(cmd.params[1], 0, 64); -+ irq_cfg->irq_num = (int)mc_dec(cmd.params[2], 0, 32); -+ *type = (int)mc_dec(cmd.params[2], 32, 32); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dprc_get_obj_irq); -+ -+int dprc_get_res_count(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ char *type, -+ int *res_count) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ *res_count = 0; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_RES_COUNT, -+ cmd_flags, -+ token); -+ cmd.params[1] |= mc_enc(0, 8, type[0]); -+ cmd.params[1] |= mc_enc(8, 8, type[1]); -+ cmd.params[1] |= mc_enc(16, 8, type[2]); -+ cmd.params[1] |= mc_enc(24, 8, type[3]); -+ cmd.params[1] |= mc_enc(32, 8, type[4]); -+ cmd.params[1] |= mc_enc(40, 8, type[5]); -+ cmd.params[1] |= mc_enc(48, 8, type[6]); -+ cmd.params[1] |= mc_enc(56, 8, type[7]); -+ cmd.params[2] |= mc_enc(0, 8, type[8]); -+ cmd.params[2] |= mc_enc(8, 8, type[9]); -+ cmd.params[2] |= mc_enc(16, 8, type[10]); -+ cmd.params[2] |= mc_enc(24, 8, type[11]); -+ cmd.params[2] |= mc_enc(32, 8, type[12]); -+ cmd.params[2] |= mc_enc(40, 8, type[13]); -+ cmd.params[2] |= mc_enc(48, 8, type[14]); -+ cmd.params[2] |= mc_enc(56, 8, '\0'); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *res_count = mc_dec(cmd.params[0], 0, 32); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dprc_get_res_count); -+ -+int dprc_get_res_ids(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ char *type, -+ struct dprc_res_ids_range_desc *range_desc) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_RES_IDS, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(42, 7, range_desc->iter_status); -+ cmd.params[1] |= mc_enc(0, 32, range_desc->base_id); -+ cmd.params[1] |= mc_enc(32, 32, range_desc->last_id); -+ cmd.params[2] |= mc_enc(0, 8, type[0]); -+ cmd.params[2] |= mc_enc(8, 8, type[1]); -+ cmd.params[2] |= mc_enc(16, 8, type[2]); -+ cmd.params[2] |= mc_enc(24, 8, type[3]); -+ cmd.params[2] |= mc_enc(32, 8, type[4]); -+ cmd.params[2] |= mc_enc(40, 8, type[5]); -+ cmd.params[2] |= mc_enc(48, 8, type[6]); -+ cmd.params[2] |= mc_enc(56, 8, type[7]); -+ cmd.params[3] |= mc_enc(0, 8, type[8]); -+ cmd.params[3] |= mc_enc(8, 8, type[9]); -+ cmd.params[3] |= mc_enc(16, 8, type[10]); -+ cmd.params[3] |= mc_enc(24, 8, type[11]); -+ cmd.params[3] |= mc_enc(32, 8, type[12]); -+ cmd.params[3] |= mc_enc(40, 8, type[13]); -+ cmd.params[3] |= mc_enc(48, 8, type[14]); -+ cmd.params[3] |= mc_enc(56, 8, '\0'); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ range_desc->iter_status = mc_dec(cmd.params[0], 42, 7); -+ range_desc->base_id = mc_dec(cmd.params[1], 0, 32); -+ range_desc->last_id = mc_dec(cmd.params[1], 32, 32); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dprc_get_res_ids); -+ -+int dprc_get_obj_region(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ char *obj_type, -+ int obj_id, -+ uint8_t region_index, -+ struct dprc_region_desc *region_desc) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_OBJ_REG, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, obj_id); -+ cmd.params[0] |= mc_enc(48, 8, region_index); -+ cmd.params[3] |= mc_enc(0, 8, obj_type[0]); -+ cmd.params[3] |= mc_enc(8, 8, obj_type[1]); -+ cmd.params[3] |= mc_enc(16, 8, obj_type[2]); -+ cmd.params[3] |= mc_enc(24, 8, obj_type[3]); -+ cmd.params[3] |= mc_enc(32, 8, obj_type[4]); -+ cmd.params[3] |= mc_enc(40, 8, obj_type[5]); -+ cmd.params[3] |= mc_enc(48, 8, obj_type[6]); -+ cmd.params[3] |= mc_enc(56, 8, obj_type[7]); -+ cmd.params[4] |= mc_enc(0, 8, obj_type[8]); -+ cmd.params[4] |= mc_enc(8, 8, obj_type[9]); -+ cmd.params[4] |= mc_enc(16, 8, obj_type[10]); -+ cmd.params[4] |= mc_enc(24, 8, obj_type[11]); -+ cmd.params[4] |= mc_enc(32, 8, obj_type[12]); -+ cmd.params[4] |= mc_enc(40, 8, obj_type[13]); -+ cmd.params[4] |= mc_enc(48, 8, obj_type[14]); -+ cmd.params[4] |= mc_enc(56, 8, '\0'); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ region_desc->base_offset = mc_dec(cmd.params[1], 0, 64); -+ region_desc->size = mc_dec(cmd.params[2], 0, 32); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dprc_get_obj_region); -+ -+int dprc_set_obj_label(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ char *obj_type, -+ int obj_id, -+ char *label) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_SET_OBJ_LABEL, -+ cmd_flags, -+ token); -+ -+ cmd.params[0] |= mc_enc(0, 32, obj_id); -+ cmd.params[1] |= mc_enc(0, 8, label[0]); -+ cmd.params[1] |= mc_enc(8, 8, label[1]); -+ cmd.params[1] |= mc_enc(16, 8, label[2]); -+ cmd.params[1] |= mc_enc(24, 8, label[3]); -+ cmd.params[1] |= mc_enc(32, 8, label[4]); -+ cmd.params[1] |= mc_enc(40, 8, label[5]); -+ cmd.params[1] |= mc_enc(48, 8, label[6]); -+ cmd.params[1] |= mc_enc(56, 8, label[7]); -+ cmd.params[2] |= mc_enc(0, 8, label[8]); -+ cmd.params[2] |= mc_enc(8, 8, label[9]); -+ cmd.params[2] |= mc_enc(16, 8, label[10]); -+ cmd.params[2] |= mc_enc(24, 8, label[11]); -+ cmd.params[2] |= mc_enc(32, 8, label[12]); -+ cmd.params[2] |= mc_enc(40, 8, label[13]); -+ cmd.params[2] |= mc_enc(48, 8, label[14]); -+ cmd.params[2] |= mc_enc(56, 8, label[15]); -+ cmd.params[3] |= mc_enc(0, 8, obj_type[0]); -+ cmd.params[3] |= mc_enc(8, 8, obj_type[1]); -+ cmd.params[3] |= mc_enc(16, 8, obj_type[2]); -+ cmd.params[3] |= mc_enc(24, 8, obj_type[3]); -+ cmd.params[3] |= mc_enc(32, 8, obj_type[4]); -+ cmd.params[3] |= mc_enc(40, 8, obj_type[5]); -+ cmd.params[3] |= mc_enc(48, 8, obj_type[6]); -+ cmd.params[3] |= mc_enc(56, 8, obj_type[7]); -+ cmd.params[4] |= mc_enc(0, 8, obj_type[8]); -+ cmd.params[4] |= mc_enc(8, 8, obj_type[9]); -+ cmd.params[4] |= mc_enc(16, 8, obj_type[10]); -+ cmd.params[4] |= mc_enc(24, 8, obj_type[11]); -+ cmd.params[4] |= mc_enc(32, 8, obj_type[12]); -+ cmd.params[4] |= mc_enc(40, 8, obj_type[13]); -+ cmd.params[4] |= mc_enc(48, 8, obj_type[14]); -+ cmd.params[4] |= mc_enc(56, 8, obj_type[15]); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dprc_set_obj_label); -+ -+int dprc_connect(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dprc_endpoint *endpoint1, -+ const struct dprc_endpoint *endpoint2, -+ const struct dprc_connection_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_CONNECT, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, endpoint1->id); -+ cmd.params[0] |= mc_enc(32, 32, endpoint1->if_id); -+ cmd.params[1] |= mc_enc(0, 32, endpoint2->id); -+ cmd.params[1] |= mc_enc(32, 32, endpoint2->if_id); -+ cmd.params[2] |= mc_enc(0, 8, endpoint1->type[0]); -+ cmd.params[2] |= mc_enc(8, 8, endpoint1->type[1]); -+ cmd.params[2] |= mc_enc(16, 8, endpoint1->type[2]); -+ cmd.params[2] |= mc_enc(24, 8, endpoint1->type[3]); -+ cmd.params[2] |= mc_enc(32, 8, endpoint1->type[4]); -+ cmd.params[2] |= mc_enc(40, 8, endpoint1->type[5]); -+ cmd.params[2] |= mc_enc(48, 8, endpoint1->type[6]); -+ cmd.params[2] |= mc_enc(56, 8, endpoint1->type[7]); -+ cmd.params[3] |= mc_enc(0, 8, endpoint1->type[8]); -+ cmd.params[3] |= mc_enc(8, 8, endpoint1->type[9]); -+ cmd.params[3] |= mc_enc(16, 8, endpoint1->type[10]); -+ cmd.params[3] |= mc_enc(24, 8, endpoint1->type[11]); -+ cmd.params[3] |= mc_enc(32, 8, endpoint1->type[12]); -+ cmd.params[3] |= mc_enc(40, 8, endpoint1->type[13]); -+ cmd.params[3] |= mc_enc(48, 8, endpoint1->type[14]); -+ cmd.params[3] |= mc_enc(56, 8, endpoint1->type[15]); -+ cmd.params[4] |= mc_enc(0, 32, cfg->max_rate); -+ cmd.params[4] |= mc_enc(32, 32, cfg->committed_rate); -+ cmd.params[5] |= mc_enc(0, 8, endpoint2->type[0]); -+ cmd.params[5] |= mc_enc(8, 8, endpoint2->type[1]); -+ cmd.params[5] |= mc_enc(16, 8, endpoint2->type[2]); -+ cmd.params[5] |= mc_enc(24, 8, endpoint2->type[3]); -+ cmd.params[5] |= mc_enc(32, 8, endpoint2->type[4]); -+ cmd.params[5] |= mc_enc(40, 8, endpoint2->type[5]); -+ cmd.params[5] |= mc_enc(48, 8, endpoint2->type[6]); -+ cmd.params[5] |= mc_enc(56, 8, endpoint2->type[7]); -+ cmd.params[6] |= mc_enc(0, 8, endpoint2->type[8]); -+ cmd.params[6] |= mc_enc(8, 8, endpoint2->type[9]); -+ cmd.params[6] |= mc_enc(16, 8, endpoint2->type[10]); -+ cmd.params[6] |= mc_enc(24, 8, endpoint2->type[11]); -+ cmd.params[6] |= mc_enc(32, 8, endpoint2->type[12]); -+ cmd.params[6] |= mc_enc(40, 8, endpoint2->type[13]); -+ cmd.params[6] |= mc_enc(48, 8, endpoint2->type[14]); -+ cmd.params[6] |= mc_enc(56, 8, endpoint2->type[15]); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dprc_connect); -+ -+int dprc_disconnect(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dprc_endpoint *endpoint) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_DISCONNECT, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, endpoint->id); -+ cmd.params[0] |= mc_enc(32, 32, endpoint->if_id); -+ cmd.params[1] |= mc_enc(0, 8, endpoint->type[0]); -+ cmd.params[1] |= mc_enc(8, 8, endpoint->type[1]); -+ cmd.params[1] |= mc_enc(16, 8, endpoint->type[2]); -+ cmd.params[1] |= mc_enc(24, 8, endpoint->type[3]); -+ cmd.params[1] |= mc_enc(32, 8, endpoint->type[4]); -+ cmd.params[1] |= mc_enc(40, 8, endpoint->type[5]); -+ cmd.params[1] |= mc_enc(48, 8, endpoint->type[6]); -+ cmd.params[1] |= mc_enc(56, 8, endpoint->type[7]); -+ cmd.params[2] |= mc_enc(0, 8, endpoint->type[8]); -+ cmd.params[2] |= mc_enc(8, 8, endpoint->type[9]); -+ cmd.params[2] |= mc_enc(16, 8, endpoint->type[10]); -+ cmd.params[2] |= mc_enc(24, 8, endpoint->type[11]); -+ cmd.params[2] |= mc_enc(32, 8, endpoint->type[12]); -+ cmd.params[2] |= mc_enc(40, 8, endpoint->type[13]); -+ cmd.params[2] |= mc_enc(48, 8, endpoint->type[14]); -+ cmd.params[2] |= mc_enc(56, 8, endpoint->type[15]); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dprc_disconnect); -+ -+int dprc_get_connection(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dprc_endpoint *endpoint1, -+ struct dprc_endpoint *endpoint2, -+ int *state) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_CONNECTION, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, endpoint1->id); -+ cmd.params[0] |= mc_enc(32, 32, endpoint1->if_id); -+ cmd.params[1] |= mc_enc(0, 8, endpoint1->type[0]); -+ cmd.params[1] |= mc_enc(8, 8, endpoint1->type[1]); -+ cmd.params[1] |= mc_enc(16, 8, endpoint1->type[2]); -+ cmd.params[1] |= mc_enc(24, 8, endpoint1->type[3]); -+ cmd.params[1] |= mc_enc(32, 8, endpoint1->type[4]); -+ cmd.params[1] |= mc_enc(40, 8, endpoint1->type[5]); -+ cmd.params[1] |= mc_enc(48, 8, endpoint1->type[6]); -+ cmd.params[1] |= mc_enc(56, 8, endpoint1->type[7]); -+ cmd.params[2] |= mc_enc(0, 8, endpoint1->type[8]); -+ cmd.params[2] |= mc_enc(8, 8, endpoint1->type[9]); -+ cmd.params[2] |= mc_enc(16, 8, endpoint1->type[10]); -+ cmd.params[2] |= mc_enc(24, 8, endpoint1->type[11]); -+ cmd.params[2] |= mc_enc(32, 8, endpoint1->type[12]); -+ cmd.params[2] |= mc_enc(40, 8, endpoint1->type[13]); -+ cmd.params[2] |= mc_enc(48, 8, endpoint1->type[14]); -+ cmd.params[2] |= mc_enc(56, 8, endpoint1->type[15]); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ endpoint2->id = mc_dec(cmd.params[3], 0, 32); -+ endpoint2->if_id = mc_dec(cmd.params[3], 32, 32); -+ endpoint2->type[0] = mc_dec(cmd.params[4], 0, 8); -+ endpoint2->type[1] = mc_dec(cmd.params[4], 8, 8); -+ endpoint2->type[2] = mc_dec(cmd.params[4], 16, 8); -+ endpoint2->type[3] = mc_dec(cmd.params[4], 24, 8); -+ endpoint2->type[4] = mc_dec(cmd.params[4], 32, 8); -+ endpoint2->type[5] = mc_dec(cmd.params[4], 40, 8); -+ endpoint2->type[6] = mc_dec(cmd.params[4], 48, 8); -+ endpoint2->type[7] = mc_dec(cmd.params[4], 56, 8); -+ endpoint2->type[8] = mc_dec(cmd.params[5], 0, 8); -+ endpoint2->type[9] = mc_dec(cmd.params[5], 8, 8); -+ endpoint2->type[10] = mc_dec(cmd.params[5], 16, 8); -+ endpoint2->type[11] = mc_dec(cmd.params[5], 24, 8); -+ endpoint2->type[12] = mc_dec(cmd.params[5], 32, 8); -+ endpoint2->type[13] = mc_dec(cmd.params[5], 40, 8); -+ endpoint2->type[14] = mc_dec(cmd.params[5], 48, 8); -+ endpoint2->type[15] = mc_dec(cmd.params[5], 56, 8); -+ *state = mc_dec(cmd.params[6], 0, 32); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dprc_get_connection); -diff --git a/drivers/staging/fsl-mc/bus/mc-allocator.c b/drivers/staging/fsl-mc/bus/mc-allocator.c -new file mode 100644 -index 0000000..a3940a0 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/mc-allocator.c -@@ -0,0 +1,716 @@ -+/* -+ * Freescale MC object device allocator driver -+ * -+ * Copyright (C) 2013 Freescale Semiconductor, Inc. -+ * -+ * This file is licensed under the terms of the GNU General Public -+ * License version 2. This program is licensed "as is" without any -+ * warranty of any kind, whether express or implied. -+ */ -+ -+#include "../include/mc-private.h" -+#include "../include/mc-sys.h" -+#include -+#include "../include/dpbp-cmd.h" -+#include "../include/dpcon-cmd.h" -+#include "dpmcp-cmd.h" -+#include "dpmcp.h" -+ -+/** -+ * fsl_mc_resource_pool_add_device - add allocatable device to a resource -+ * pool of a given MC bus -+ * -+ * @mc_bus: pointer to the MC bus -+ * @pool_type: MC bus pool type -+ * @mc_dev: Pointer to allocatable MC object device -+ * -+ * It adds an allocatable MC object device to a container's resource pool of -+ * the given resource type -+ */ -+static int __must_check fsl_mc_resource_pool_add_device(struct fsl_mc_bus -+ *mc_bus, -+ enum fsl_mc_pool_type -+ pool_type, -+ struct fsl_mc_device -+ *mc_dev) -+{ -+ struct fsl_mc_resource_pool *res_pool; -+ struct fsl_mc_resource *resource; -+ struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev; -+ int error = -EINVAL; -+ bool mutex_locked = false; -+ -+ if (WARN_ON(pool_type < 0 || pool_type >= FSL_MC_NUM_POOL_TYPES)) -+ goto out; -+ if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type))) -+ goto out; -+ if (WARN_ON(mc_dev->resource)) -+ goto out; -+ -+ res_pool = &mc_bus->resource_pools[pool_type]; -+ if (WARN_ON(res_pool->type != pool_type)) -+ goto out; -+ if (WARN_ON(res_pool->mc_bus != mc_bus)) -+ goto out; -+ -+ mutex_lock(&res_pool->mutex); -+ mutex_locked = true; -+ -+ if (WARN_ON(res_pool->max_count < 0)) -+ goto out; -+ if (WARN_ON(res_pool->free_count < 0 || -+ res_pool->free_count > res_pool->max_count)) -+ goto out; -+ -+ resource = devm_kzalloc(&mc_bus_dev->dev, sizeof(*resource), -+ GFP_KERNEL); -+ if (!resource) { -+ error = -ENOMEM; -+ dev_err(&mc_bus_dev->dev, -+ "Failed to allocate memory for fsl_mc_resource\n"); -+ goto out; -+ } -+ -+ resource->type = pool_type; -+ resource->id = mc_dev->obj_desc.id; -+ resource->data = mc_dev; -+ resource->parent_pool = res_pool; -+ INIT_LIST_HEAD(&resource->node); -+ list_add_tail(&resource->node, &res_pool->free_list); -+ mc_dev->resource = resource; -+ res_pool->free_count++; -+ res_pool->max_count++; -+ error = 0; -+out: -+ if (mutex_locked) -+ mutex_unlock(&res_pool->mutex); -+ -+ return error; -+} -+ -+/** -+ * fsl_mc_resource_pool_remove_device - remove an allocatable device from a -+ * resource pool -+ * -+ * @mc_dev: Pointer to allocatable MC object device -+ * -+ * It permanently removes an allocatable MC object device from the resource -+ * pool, the device is currently in, as long as it is in the pool's free list. -+ */ -+static int __must_check fsl_mc_resource_pool_remove_device(struct fsl_mc_device -+ *mc_dev) -+{ -+ struct fsl_mc_device *mc_bus_dev; -+ struct fsl_mc_bus *mc_bus; -+ struct fsl_mc_resource_pool *res_pool; -+ struct fsl_mc_resource *resource; -+ int error = -EINVAL; -+ bool mutex_locked = false; -+ -+ if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type))) -+ goto out; -+ -+ resource = mc_dev->resource; -+ if (WARN_ON(!resource || resource->data != mc_dev)) -+ goto out; -+ -+ mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); -+ mc_bus = to_fsl_mc_bus(mc_bus_dev); -+ res_pool = resource->parent_pool; -+ if (WARN_ON(res_pool != &mc_bus->resource_pools[resource->type])) -+ goto out; -+ -+ mutex_lock(&res_pool->mutex); -+ mutex_locked = true; -+ -+ if (WARN_ON(res_pool->max_count <= 0)) -+ goto out; -+ if (WARN_ON(res_pool->free_count <= 0 || -+ res_pool->free_count > res_pool->max_count)) -+ goto out; -+ -+ /* -+ * If the device is currently allocated, its resource is not -+ * in the free list and thus, the device cannot be removed. -+ */ -+ if (list_empty(&resource->node)) { -+ error = -EBUSY; -+ dev_err(&mc_bus_dev->dev, -+ "Device %s cannot be removed from resource pool\n", -+ dev_name(&mc_dev->dev)); -+ goto out; -+ } -+ -+ list_del(&resource->node); -+ INIT_LIST_HEAD(&resource->node); -+ res_pool->free_count--; -+ res_pool->max_count--; -+ -+ devm_kfree(&mc_bus_dev->dev, resource); -+ mc_dev->resource = NULL; -+ error = 0; -+out: -+ if (mutex_locked) -+ mutex_unlock(&res_pool->mutex); -+ -+ return error; -+} -+ -+static const char *const fsl_mc_pool_type_strings[] = { -+ [FSL_MC_POOL_DPMCP] = "dpmcp", -+ [FSL_MC_POOL_DPBP] = "dpbp", -+ [FSL_MC_POOL_DPCON] = "dpcon", -+ [FSL_MC_POOL_IRQ] = "irq", -+}; -+ -+static int __must_check object_type_to_pool_type(const char *object_type, -+ enum fsl_mc_pool_type -+ *pool_type) -+{ -+ unsigned int i; -+ -+ for (i = 0; i < ARRAY_SIZE(fsl_mc_pool_type_strings); i++) { -+ if (strcmp(object_type, fsl_mc_pool_type_strings[i]) == 0) { -+ *pool_type = i; -+ return 0; -+ } -+ } -+ -+ return -EINVAL; -+} -+ -+int __must_check fsl_mc_resource_allocate(struct fsl_mc_bus *mc_bus, -+ enum fsl_mc_pool_type pool_type, -+ struct fsl_mc_resource **new_resource) -+{ -+ struct fsl_mc_resource_pool *res_pool; -+ struct fsl_mc_resource *resource; -+ struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev; -+ int error = -EINVAL; -+ bool mutex_locked = false; -+ -+ BUILD_BUG_ON(ARRAY_SIZE(fsl_mc_pool_type_strings) != -+ FSL_MC_NUM_POOL_TYPES); -+ -+ *new_resource = NULL; -+ if (WARN_ON(pool_type < 0 || pool_type >= FSL_MC_NUM_POOL_TYPES)) -+ goto error; -+ -+ res_pool = &mc_bus->resource_pools[pool_type]; -+ if (WARN_ON(res_pool->mc_bus != mc_bus)) -+ goto error; -+ -+ mutex_lock(&res_pool->mutex); -+ mutex_locked = true; -+ resource = list_first_entry_or_null(&res_pool->free_list, -+ struct fsl_mc_resource, node); -+ -+ if (!resource) { -+ WARN_ON(res_pool->free_count != 0); -+ error = -ENXIO; -+ dev_err(&mc_bus_dev->dev, -+ "No more resources of type %s left\n", -+ fsl_mc_pool_type_strings[pool_type]); -+ goto error; -+ } -+ -+ if (WARN_ON(resource->type != pool_type)) -+ goto error; -+ if (WARN_ON(resource->parent_pool != res_pool)) -+ goto error; -+ if (WARN_ON(res_pool->free_count <= 0 || -+ res_pool->free_count > res_pool->max_count)) -+ goto error; -+ -+ list_del(&resource->node); -+ INIT_LIST_HEAD(&resource->node); -+ -+ res_pool->free_count--; -+ mutex_unlock(&res_pool->mutex); -+ *new_resource = resource; -+ return 0; -+error: -+ if (mutex_locked) -+ mutex_unlock(&res_pool->mutex); -+ -+ return error; -+} -+EXPORT_SYMBOL_GPL(fsl_mc_resource_allocate); -+ -+void fsl_mc_resource_free(struct fsl_mc_resource *resource) -+{ -+ struct fsl_mc_resource_pool *res_pool; -+ bool mutex_locked = false; -+ -+ res_pool = resource->parent_pool; -+ if (WARN_ON(resource->type != res_pool->type)) -+ goto out; -+ -+ mutex_lock(&res_pool->mutex); -+ mutex_locked = true; -+ if (WARN_ON(res_pool->free_count < 0 || -+ res_pool->free_count >= res_pool->max_count)) -+ goto out; -+ -+ if (WARN_ON(!list_empty(&resource->node))) -+ goto out; -+ -+ list_add_tail(&resource->node, &res_pool->free_list); -+ res_pool->free_count++; -+out: -+ if (mutex_locked) -+ mutex_unlock(&res_pool->mutex); -+} -+EXPORT_SYMBOL_GPL(fsl_mc_resource_free); -+ -+/** -+ * fsl_mc_portal_allocate - Allocates an MC portal -+ * -+ * @mc_dev: MC device for which the MC portal is to be allocated -+ * @mc_io_flags: Flags for the fsl_mc_io object that wraps the allocated -+ * MC portal. -+ * @new_mc_io: Pointer to area where the pointer to the fsl_mc_io object -+ * that wraps the allocated MC portal is to be returned -+ * -+ * This function allocates an MC portal from the device's parent DPRC, -+ * from the corresponding MC bus' pool of MC portals and wraps -+ * it in a new fsl_mc_io object. If 'mc_dev' is a DPRC itself, the -+ * portal is allocated from its own MC bus. -+ */ -+int __must_check fsl_mc_portal_allocate(struct fsl_mc_device *mc_dev, -+ uint16_t mc_io_flags, -+ struct fsl_mc_io **new_mc_io) -+{ -+ struct fsl_mc_device *mc_bus_dev; -+ struct fsl_mc_bus *mc_bus; -+ phys_addr_t mc_portal_phys_addr; -+ size_t mc_portal_size; -+ struct fsl_mc_device *dpmcp_dev; -+ int error = -EINVAL; -+ struct fsl_mc_resource *resource = NULL; -+ struct fsl_mc_io *mc_io = NULL; -+ -+ if (!mc_dev) { -+ if (WARN_ON(!fsl_mc_bus_type.dev_root)) -+ return error; -+ -+ mc_bus_dev = to_fsl_mc_device(fsl_mc_bus_type.dev_root); -+ } else if (mc_dev->flags & FSL_MC_IS_DPRC) { -+ mc_bus_dev = mc_dev; -+ } else { -+ if (WARN_ON(mc_dev->dev.parent->bus != &fsl_mc_bus_type)) -+ return error; -+ -+ mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); -+ } -+ -+ mc_bus = to_fsl_mc_bus(mc_bus_dev); -+ *new_mc_io = NULL; -+ error = fsl_mc_resource_allocate(mc_bus, FSL_MC_POOL_DPMCP, &resource); -+ if (error < 0) -+ return error; -+ -+ error = -EINVAL; -+ dpmcp_dev = resource->data; -+ if (WARN_ON(!dpmcp_dev || -+ strcmp(dpmcp_dev->obj_desc.type, "dpmcp") != 0)) -+ goto error_cleanup_resource; -+ -+ if (dpmcp_dev->obj_desc.ver_major < DPMCP_MIN_VER_MAJOR || -+ (dpmcp_dev->obj_desc.ver_major == DPMCP_MIN_VER_MAJOR && -+ dpmcp_dev->obj_desc.ver_minor < DPMCP_MIN_VER_MINOR)) { -+ dev_err(&dpmcp_dev->dev, -+ "ERROR: Version %d.%d of DPMCP not supported.\n", -+ dpmcp_dev->obj_desc.ver_major, -+ dpmcp_dev->obj_desc.ver_minor); -+ error = -ENOTSUPP; -+ goto error_cleanup_resource; -+ } -+ -+ if (WARN_ON(dpmcp_dev->obj_desc.region_count == 0)) -+ goto error_cleanup_resource; -+ -+ mc_portal_phys_addr = dpmcp_dev->regions[0].start; -+ mc_portal_size = dpmcp_dev->regions[0].end - -+ dpmcp_dev->regions[0].start + 1; -+ -+ if (WARN_ON(mc_portal_size != mc_bus_dev->mc_io->portal_size)) -+ goto error_cleanup_resource; -+ -+ error = fsl_create_mc_io(&mc_bus_dev->dev, -+ mc_portal_phys_addr, -+ mc_portal_size, dpmcp_dev, -+ mc_io_flags, &mc_io); -+ if (error < 0) -+ goto error_cleanup_resource; -+ -+ *new_mc_io = mc_io; -+ return 0; -+ -+error_cleanup_resource: -+ fsl_mc_resource_free(resource); -+ return error; -+} -+EXPORT_SYMBOL_GPL(fsl_mc_portal_allocate); -+ -+/** -+ * fsl_mc_portal_free - Returns an MC portal to the pool of free MC portals -+ * of a given MC bus -+ * -+ * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free -+ */ -+void fsl_mc_portal_free(struct fsl_mc_io *mc_io) -+{ -+ struct fsl_mc_device *dpmcp_dev; -+ struct fsl_mc_resource *resource; -+ -+ /* -+ * Every mc_io obtained by calling fsl_mc_portal_allocate() is supposed -+ * to have a DPMCP object associated with. -+ */ -+ dpmcp_dev = mc_io->dpmcp_dev; -+ if (WARN_ON(!dpmcp_dev)) -+ return; -+ if (WARN_ON(strcmp(dpmcp_dev->obj_desc.type, "dpmcp") != 0)) -+ return; -+ if (WARN_ON(dpmcp_dev->mc_io != mc_io)) -+ return; -+ -+ resource = dpmcp_dev->resource; -+ if (WARN_ON(!resource || resource->type != FSL_MC_POOL_DPMCP)) -+ return; -+ -+ if (WARN_ON(resource->data != dpmcp_dev)) -+ return; -+ -+ fsl_destroy_mc_io(mc_io); -+ fsl_mc_resource_free(resource); -+} -+EXPORT_SYMBOL_GPL(fsl_mc_portal_free); -+ -+/** -+ * fsl_mc_portal_reset - Resets the dpmcp object for a given fsl_mc_io object -+ * -+ * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free -+ */ -+int fsl_mc_portal_reset(struct fsl_mc_io *mc_io) -+{ -+ int error; -+ struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev; -+ -+ if (WARN_ON(!dpmcp_dev)) -+ return -EINVAL; -+ -+ error = dpmcp_reset(mc_io, 0, dpmcp_dev->mc_handle); -+ if (error < 0) { -+ dev_err(&dpmcp_dev->dev, "dpmcp_reset() failed: %d\n", error); -+ return error; -+ } -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(fsl_mc_portal_reset); -+ -+/** -+ * fsl_mc_object_allocate - Allocates a MC object device of the given -+ * pool type from a given MC bus -+ * -+ * @mc_dev: MC device for which the MC object device is to be allocated -+ * @pool_type: MC bus resource pool type -+ * @new_mc_dev: Pointer to area where the pointer to the allocated -+ * MC object device is to be returned -+ * -+ * This function allocates a MC object device from the device's parent DPRC, -+ * from the corresponding MC bus' pool of allocatable MC object devices of -+ * the given resource type. mc_dev cannot be a DPRC itself. -+ * -+ * NOTE: pool_type must be different from FSL_MC_POOL_MCP, since MC -+ * portals are allocated using fsl_mc_portal_allocate(), instead of -+ * this function. -+ */ -+int __must_check fsl_mc_object_allocate(struct fsl_mc_device *mc_dev, -+ enum fsl_mc_pool_type pool_type, -+ struct fsl_mc_device **new_mc_adev) -+{ -+ struct fsl_mc_device *mc_bus_dev; -+ struct fsl_mc_bus *mc_bus; -+ struct fsl_mc_device *mc_adev; -+ int error = -EINVAL; -+ struct fsl_mc_resource *resource = NULL; -+ -+ *new_mc_adev = NULL; -+ if (WARN_ON(mc_dev->flags & FSL_MC_IS_DPRC)) -+ goto error; -+ -+ if (WARN_ON(mc_dev->dev.parent->bus != &fsl_mc_bus_type)) -+ goto error; -+ -+ if (WARN_ON(pool_type == FSL_MC_POOL_DPMCP)) -+ goto error; -+ -+ mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); -+ mc_bus = to_fsl_mc_bus(mc_bus_dev); -+ error = fsl_mc_resource_allocate(mc_bus, pool_type, &resource); -+ if (error < 0) -+ goto error; -+ -+ mc_adev = resource->data; -+ if (WARN_ON(!mc_adev)) -+ goto error; -+ -+ *new_mc_adev = mc_adev; -+ return 0; -+error: -+ if (resource) -+ fsl_mc_resource_free(resource); -+ -+ return error; -+} -+EXPORT_SYMBOL_GPL(fsl_mc_object_allocate); -+ -+/** -+ * fsl_mc_object_free - Returns an allocatable MC object device to the -+ * corresponding resource pool of a given MC bus. -+ * -+ * @mc_adev: Pointer to the MC object device -+ */ -+void fsl_mc_object_free(struct fsl_mc_device *mc_adev) -+{ -+ struct fsl_mc_resource *resource; -+ -+ resource = mc_adev->resource; -+ if (WARN_ON(resource->type == FSL_MC_POOL_DPMCP)) -+ return; -+ if (WARN_ON(resource->data != mc_adev)) -+ return; -+ -+ fsl_mc_resource_free(resource); -+} -+EXPORT_SYMBOL_GPL(fsl_mc_object_free); -+ -+/** -+ * It allocates the IRQs required by a given MC object device. The -+ * IRQs are allocated from the interrupt pool associated with the -+ * MC bus that contains the device, if the device is not a DPRC device. -+ * Otherwise, the IRQs are allocated from the interrupt pool associated -+ * with the MC bus that represents the DPRC device itself. -+ */ -+int __must_check fsl_mc_allocate_irqs(struct fsl_mc_device *mc_dev) -+{ -+ int i; -+ int irq_count; -+ int res_allocated_count = 0; -+ int error = -EINVAL; -+ struct fsl_mc_device_irq **irqs = NULL; -+ struct fsl_mc_bus *mc_bus; -+ struct fsl_mc_resource_pool *res_pool; -+ struct fsl_mc *mc = dev_get_drvdata(fsl_mc_bus_type.dev_root->parent); -+ -+ if (!mc->gic_supported) -+ return -ENOTSUPP; -+ -+ if (WARN_ON(mc_dev->irqs)) -+ goto error; -+ -+ irq_count = mc_dev->obj_desc.irq_count; -+ if (WARN_ON(irq_count == 0)) -+ goto error; -+ -+ if (strcmp(mc_dev->obj_desc.type, "dprc") == 0) -+ mc_bus = to_fsl_mc_bus(mc_dev); -+ else -+ mc_bus = to_fsl_mc_bus(to_fsl_mc_device(mc_dev->dev.parent)); -+ -+ if (WARN_ON(!mc_bus->irq_resources)) -+ goto error; -+ -+ res_pool = &mc_bus->resource_pools[FSL_MC_POOL_IRQ]; -+ if (res_pool->free_count < irq_count) { -+ dev_err(&mc_dev->dev, -+ "Not able to allocate %u irqs for device\n", irq_count); -+ error = -ENOSPC; -+ goto error; -+ } -+ -+ irqs = devm_kzalloc(&mc_dev->dev, irq_count * sizeof(irqs[0]), -+ GFP_KERNEL); -+ if (!irqs) { -+ error = -ENOMEM; -+ dev_err(&mc_dev->dev, "No memory to allocate irqs[]\n"); -+ goto error; -+ } -+ -+ for (i = 0; i < irq_count; i++) { -+ struct fsl_mc_resource *resource; -+ -+ error = fsl_mc_resource_allocate(mc_bus, FSL_MC_POOL_IRQ, -+ &resource); -+ if (error < 0) -+ goto error; -+ -+ irqs[i] = to_fsl_mc_irq(resource); -+ res_allocated_count++; -+ -+ WARN_ON(irqs[i]->mc_dev); -+ irqs[i]->mc_dev = mc_dev; -+ irqs[i]->dev_irq_index = i; -+ } -+ -+ mc_dev->irqs = irqs; -+ return 0; -+error: -+ for (i = 0; i < res_allocated_count; i++) { -+ irqs[i]->mc_dev = NULL; -+ fsl_mc_resource_free(&irqs[i]->resource); -+ } -+ -+ if (irqs) -+ devm_kfree(&mc_dev->dev, irqs); -+ -+ return error; -+} -+EXPORT_SYMBOL_GPL(fsl_mc_allocate_irqs); -+ -+/* -+ * It frees the IRQs that were allocated for a MC object device, by -+ * returning them to the corresponding interrupt pool. -+ */ -+void fsl_mc_free_irqs(struct fsl_mc_device *mc_dev) -+{ -+ int i; -+ int irq_count; -+ struct fsl_mc_bus *mc_bus; -+ struct fsl_mc_device_irq **irqs = mc_dev->irqs; -+ -+ if (WARN_ON(!irqs)) -+ return; -+ -+ irq_count = mc_dev->obj_desc.irq_count; -+ -+ if (strcmp(mc_dev->obj_desc.type, "dprc") == 0) -+ mc_bus = to_fsl_mc_bus(mc_dev); -+ else -+ mc_bus = to_fsl_mc_bus(to_fsl_mc_device(mc_dev->dev.parent)); -+ -+ if (WARN_ON(!mc_bus->irq_resources)) -+ return; -+ -+ for (i = 0; i < irq_count; i++) { -+ WARN_ON(!irqs[i]->mc_dev); -+ irqs[i]->mc_dev = NULL; -+ fsl_mc_resource_free(&irqs[i]->resource); -+ } -+ -+ devm_kfree(&mc_dev->dev, mc_dev->irqs); -+ mc_dev->irqs = NULL; -+} -+EXPORT_SYMBOL_GPL(fsl_mc_free_irqs); -+ -+/** -+ * fsl_mc_allocator_probe - callback invoked when an allocatable device is -+ * being added to the system -+ */ -+static int fsl_mc_allocator_probe(struct fsl_mc_device *mc_dev) -+{ -+ enum fsl_mc_pool_type pool_type; -+ struct fsl_mc_device *mc_bus_dev; -+ struct fsl_mc_bus *mc_bus; -+ int error = -EINVAL; -+ -+ if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type))) -+ goto error; -+ -+ mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); -+ if (WARN_ON(mc_bus_dev->dev.bus != &fsl_mc_bus_type)) -+ goto error; -+ -+ mc_bus = to_fsl_mc_bus(mc_bus_dev); -+ -+ /* -+ * If mc_dev is the DPMCP object for the parent DPRC's built-in -+ * portal, we don't add this DPMCP to the DPMCP object pool, -+ * but instead allocate it directly to the parent DPRC (mc_bus_dev): -+ */ -+ if (strcmp(mc_dev->obj_desc.type, "dpmcp") == 0 && -+ mc_dev->obj_desc.id == mc_bus->dprc_attr.portal_id) { -+ error = fsl_mc_io_set_dpmcp(mc_bus_dev->mc_io, mc_dev); -+ if (error < 0) -+ goto error; -+ } else { -+ error = object_type_to_pool_type(mc_dev->obj_desc.type, -+ &pool_type); -+ if (error < 0) -+ goto error; -+ -+ error = fsl_mc_resource_pool_add_device(mc_bus, pool_type, -+ mc_dev); -+ if (error < 0) -+ goto error; -+ } -+ -+ dev_dbg(&mc_dev->dev, -+ "Allocatable MC object device bound to fsl_mc_allocator driver"); -+ return 0; -+error: -+ -+ return error; -+} -+ -+/** -+ * fsl_mc_allocator_remove - callback invoked when an allocatable device is -+ * being removed from the system -+ */ -+static int fsl_mc_allocator_remove(struct fsl_mc_device *mc_dev) -+{ -+ int error; -+ -+ if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type))) -+ return -EINVAL; -+ -+ if (mc_dev->resource) { -+ error = fsl_mc_resource_pool_remove_device(mc_dev); -+ if (error < 0) -+ return error; -+ } -+ -+ dev_dbg(&mc_dev->dev, -+ "Allocatable MC object device unbound from fsl_mc_allocator driver"); -+ return 0; -+} -+ -+static const struct fsl_mc_device_match_id match_id_table[] = { -+ { -+ .vendor = FSL_MC_VENDOR_FREESCALE, -+ .obj_type = "dpbp", -+ }, -+ { -+ .vendor = FSL_MC_VENDOR_FREESCALE, -+ .obj_type = "dpmcp", -+ }, -+ { -+ .vendor = FSL_MC_VENDOR_FREESCALE, -+ .obj_type = "dpcon", -+ }, -+ {.vendor = 0x0}, -+}; -+ -+static struct fsl_mc_driver fsl_mc_allocator_driver = { -+ .driver = { -+ .name = "fsl_mc_allocator", -+ .owner = THIS_MODULE, -+ .pm = NULL, -+ }, -+ .match_id_table = match_id_table, -+ .probe = fsl_mc_allocator_probe, -+ .remove = fsl_mc_allocator_remove, -+}; -+ -+int __init fsl_mc_allocator_driver_init(void) -+{ -+ return fsl_mc_driver_register(&fsl_mc_allocator_driver); -+} -+ -+void __exit fsl_mc_allocator_driver_exit(void) -+{ -+ fsl_mc_driver_unregister(&fsl_mc_allocator_driver); -+} -diff --git a/drivers/staging/fsl-mc/bus/mc-bus.c b/drivers/staging/fsl-mc/bus/mc-bus.c -new file mode 100644 -index 0000000..f173b35 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/mc-bus.c -@@ -0,0 +1,1347 @@ -+/* -+ * Freescale Management Complex (MC) bus driver -+ * -+ * Copyright (C) 2014 Freescale Semiconductor, Inc. -+ * Author: German Rivera -+ * -+ * This file is licensed under the terms of the GNU General Public -+ * License version 2. This program is licensed "as is" without any -+ * warranty of any kind, whether express or implied. -+ */ -+ -+#include "../include/mc-private.h" -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "../include/dpmng.h" -+#include "../include/mc-sys.h" -+#include "dprc-cmd.h" -+ -+/* -+ * IOMMU stream ID flags -+ */ -+#define STREAM_ID_PL_MASK BIT(9) /* privilege level */ -+#define STREAM_ID_BMT_MASK BIT(8) /* bypass memory translation */ -+#define STREAM_ID_VA_MASK BIT(7) /* virtual address translation -+ * (two-stage translation) */ -+#define STREAM_ID_ICID_MASK (BIT(7) - 1) /* isolation context ID -+ * (translation context) */ -+ -+#define MAX_STREAM_ID_ICID STREAM_ID_ICID_MASK -+ -+static struct kmem_cache *mc_dev_cache; -+ -+/** -+ * fsl_mc_bus_match - device to driver matching callback -+ * @dev: the MC object device structure to match against -+ * @drv: the device driver to search for matching MC object device id -+ * structures -+ * -+ * Returns 1 on success, 0 otherwise. -+ */ -+static int fsl_mc_bus_match(struct device *dev, struct device_driver *drv) -+{ -+ const struct fsl_mc_device_match_id *id; -+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); -+ struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(drv); -+ bool found = false; -+ -+ /* When driver_override is set, only bind to the matching driver */ -+ if (mc_dev->driver_override) { -+ found = !strcmp(mc_dev->driver_override, mc_drv->driver.name); -+ goto out; -+ } -+ -+ if (!mc_drv->match_id_table) -+ goto out; -+ -+ /* -+ * If the object is not 'plugged' don't match. -+ * Only exception is the root DPRC, which is a special case. -+ * -+ * NOTE: Only when this function is invoked for the root DPRC, -+ * mc_dev->mc_io is not NULL -+ */ -+ if ((mc_dev->obj_desc.state & DPRC_OBJ_STATE_PLUGGED) == 0 && -+ !mc_dev->mc_io) -+ goto out; -+ -+ /* -+ * Traverse the match_id table of the given driver, trying to find -+ * a matching for the given MC object device. -+ */ -+ for (id = mc_drv->match_id_table; id->vendor != 0x0; id++) { -+ if (id->vendor == mc_dev->obj_desc.vendor && -+ strcmp(id->obj_type, mc_dev->obj_desc.type) == 0) { -+ found = true; -+ -+ break; -+ } -+ } -+ -+out: -+ dev_dbg(dev, "%smatched\n", found ? "" : "not "); -+ return found; -+} -+ -+/** -+ * fsl_mc_bus_uevent - callback invoked when a device is added -+ */ -+static int fsl_mc_bus_uevent(struct device *dev, struct kobj_uevent_env *env) -+{ -+ pr_debug("%s invoked\n", __func__); -+ return 0; -+} -+ -+static ssize_t driver_override_store(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); -+ const char *driver_override, *old = mc_dev->driver_override; -+ char *cp; -+ -+ if (WARN_ON(dev->bus != &fsl_mc_bus_type)) -+ return -EINVAL; -+ -+ if (count > PATH_MAX) -+ return -EINVAL; -+ -+ driver_override = kstrndup(buf, count, GFP_KERNEL); -+ if (!driver_override) -+ return -ENOMEM; -+ -+ cp = strchr(driver_override, '\n'); -+ if (cp) -+ *cp = '\0'; -+ -+ if (strlen(driver_override)) { -+ mc_dev->driver_override = driver_override; -+ } else { -+ kfree(driver_override); -+ mc_dev->driver_override = NULL; -+ } -+ -+ kfree(old); -+ -+ return count; -+} -+ -+static ssize_t driver_override_show(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); -+ -+ return sprintf(buf, "%s\n", mc_dev->driver_override); -+} -+ -+static DEVICE_ATTR_RW(driver_override); -+ -+static ssize_t rescan_store(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ unsigned long val; -+ unsigned int irq_count; -+ struct fsl_mc_device *root_mc_dev; -+ struct fsl_mc_bus *root_mc_bus; -+ -+ if (!is_root_dprc(dev)) -+ return -EINVAL; -+ -+ root_mc_dev = to_fsl_mc_device(dev); -+ root_mc_bus = to_fsl_mc_bus(root_mc_dev); -+ -+ if (kstrtoul(buf, 0, &val) < 0) -+ return -EINVAL; -+ -+ if (val) { -+ mutex_lock(&root_mc_bus->scan_mutex); -+ dprc_scan_objects(root_mc_dev, NULL, &irq_count); -+ mutex_unlock(&root_mc_bus->scan_mutex); -+ } -+ -+ return count; -+} -+ -+static DEVICE_ATTR_WO(rescan); -+ -+static struct attribute *fsl_mc_dev_attrs[] = { -+ &dev_attr_driver_override.attr, -+ &dev_attr_rescan.attr, -+ NULL, -+}; -+ -+static const struct attribute_group fsl_mc_dev_group = { -+ .attrs = fsl_mc_dev_attrs, -+}; -+ -+static const struct attribute_group *fsl_mc_dev_groups[] = { -+ &fsl_mc_dev_group, -+ NULL, -+}; -+ -+static int scan_fsl_mc_bus(struct device *dev, void *data) -+{ -+ unsigned int irq_count; -+ struct fsl_mc_device *root_mc_dev; -+ struct fsl_mc_bus *root_mc_bus; -+ -+ if (is_root_dprc(dev)) { -+ root_mc_dev = to_fsl_mc_device(dev); -+ root_mc_bus = to_fsl_mc_bus(root_mc_dev); -+ mutex_lock(&root_mc_bus->scan_mutex); -+ dprc_scan_objects(root_mc_dev, NULL, &irq_count); -+ mutex_unlock(&root_mc_bus->scan_mutex); -+ } -+ -+ return 0; -+} -+ -+static ssize_t bus_rescan_store(struct bus_type *bus, -+ const char *buf, size_t count) -+{ -+ unsigned long val; -+ -+ if (kstrtoul(buf, 0, &val) < 0) -+ return -EINVAL; -+ -+ if (val) -+ bus_for_each_dev(bus, NULL, NULL, scan_fsl_mc_bus); -+ -+ return count; -+} -+static BUS_ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, bus_rescan_store); -+ -+static struct attribute *fsl_mc_bus_attrs[] = { -+ &bus_attr_rescan.attr, -+ NULL, -+}; -+ -+static const struct attribute_group fsl_mc_bus_group = { -+ .attrs = fsl_mc_bus_attrs, -+}; -+ -+static const struct attribute_group *fsl_mc_bus_groups[] = { -+ &fsl_mc_bus_group, -+ NULL, -+}; -+ -+struct bus_type fsl_mc_bus_type = { -+ .name = "fsl-mc", -+ .match = fsl_mc_bus_match, -+ .uevent = fsl_mc_bus_uevent, -+ .dev_groups = fsl_mc_dev_groups, -+ .bus_groups = fsl_mc_bus_groups, -+}; -+EXPORT_SYMBOL_GPL(fsl_mc_bus_type); -+ -+static int fsl_mc_driver_probe(struct device *dev) -+{ -+ struct fsl_mc_driver *mc_drv; -+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); -+ int error; -+ -+ if (WARN_ON(!dev->driver)) -+ return -EINVAL; -+ -+ mc_drv = to_fsl_mc_driver(dev->driver); -+ if (WARN_ON(!mc_drv->probe)) -+ return -EINVAL; -+ -+ error = mc_drv->probe(mc_dev); -+ if (error < 0) { -+ dev_err(dev, "MC object device probe callback failed: %d\n", -+ error); -+ return error; -+ } -+ -+ return 0; -+} -+ -+static int fsl_mc_driver_remove(struct device *dev) -+{ -+ struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver); -+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); -+ int error; -+ -+ if (WARN_ON(!dev->driver)) -+ return -EINVAL; -+ -+ error = mc_drv->remove(mc_dev); -+ if (error < 0) { -+ dev_err(dev, -+ "MC object device remove callback failed: %d\n", -+ error); -+ return error; -+ } -+ -+ return 0; -+} -+ -+static void fsl_mc_driver_shutdown(struct device *dev) -+{ -+ struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver); -+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); -+ -+ mc_drv->shutdown(mc_dev); -+} -+ -+/** -+ * __fsl_mc_driver_register - registers a child device driver with the -+ * MC bus -+ * -+ * This function is implicitly invoked from the registration function of -+ * fsl_mc device drivers, which is generated by the -+ * module_fsl_mc_driver() macro. -+ */ -+int __fsl_mc_driver_register(struct fsl_mc_driver *mc_driver, -+ struct module *owner) -+{ -+ int error; -+ -+ mc_driver->driver.owner = owner; -+ mc_driver->driver.bus = &fsl_mc_bus_type; -+ -+ if (mc_driver->probe) -+ mc_driver->driver.probe = fsl_mc_driver_probe; -+ -+ if (mc_driver->remove) -+ mc_driver->driver.remove = fsl_mc_driver_remove; -+ -+ if (mc_driver->shutdown) -+ mc_driver->driver.shutdown = fsl_mc_driver_shutdown; -+ -+ error = driver_register(&mc_driver->driver); -+ if (error < 0) { -+ pr_err("driver_register() failed for %s: %d\n", -+ mc_driver->driver.name, error); -+ return error; -+ } -+ -+ pr_info("MC object device driver %s registered\n", -+ mc_driver->driver.name); -+ return 0; -+} -+EXPORT_SYMBOL_GPL(__fsl_mc_driver_register); -+ -+/** -+ * fsl_mc_driver_unregister - unregisters a device driver from the -+ * MC bus -+ */ -+void fsl_mc_driver_unregister(struct fsl_mc_driver *mc_driver) -+{ -+ driver_unregister(&mc_driver->driver); -+} -+EXPORT_SYMBOL_GPL(fsl_mc_driver_unregister); -+ -+bool fsl_mc_interrupts_supported(void) -+{ -+ struct fsl_mc *mc = dev_get_drvdata(fsl_mc_bus_type.dev_root->parent); -+ -+ return mc->gic_supported; -+} -+EXPORT_SYMBOL_GPL(fsl_mc_interrupts_supported); -+ -+static int get_dprc_attr(struct fsl_mc_io *mc_io, -+ int container_id, struct dprc_attributes *attr) -+{ -+ uint16_t dprc_handle; -+ int error; -+ -+ error = dprc_open(mc_io, 0, container_id, &dprc_handle); -+ if (error < 0) { -+ pr_err("dprc_open() failed: %d\n", error); -+ return error; -+ } -+ -+ memset(attr, 0, sizeof(struct dprc_attributes)); -+ error = dprc_get_attributes(mc_io, 0, dprc_handle, attr); -+ if (error < 0) { -+ pr_err("dprc_get_attributes() failed: %d\n", error); -+ goto common_cleanup; -+ } -+ -+ error = 0; -+ -+common_cleanup: -+ (void)dprc_close(mc_io, 0, dprc_handle); -+ return error; -+} -+ -+static int get_dprc_icid(struct fsl_mc_io *mc_io, -+ int container_id, uint16_t *icid) -+{ -+ struct dprc_attributes attr; -+ int error; -+ -+ error = get_dprc_attr(mc_io, container_id, &attr); -+ if (error == 0) -+ *icid = attr.icid; -+ -+ return error; -+} -+ -+static int get_dprc_version(struct fsl_mc_io *mc_io, -+ int container_id, uint16_t *major, uint16_t *minor) -+{ -+ struct dprc_attributes attr; -+ int error; -+ -+ error = get_dprc_attr(mc_io, container_id, &attr); -+ if (error == 0) { -+ *major = attr.version.major; -+ *minor = attr.version.minor; -+ } -+ -+ return error; -+} -+ -+static int translate_mc_addr(enum fsl_mc_region_types mc_region_type, -+ uint64_t mc_offset, phys_addr_t *phys_addr) -+{ -+ int i; -+ struct fsl_mc *mc = dev_get_drvdata(fsl_mc_bus_type.dev_root->parent); -+ -+ if (mc->num_translation_ranges == 0) { -+ /* -+ * Do identity mapping: -+ */ -+ *phys_addr = mc_offset; -+ return 0; -+ } -+ -+ for (i = 0; i < mc->num_translation_ranges; i++) { -+ struct fsl_mc_addr_translation_range *range = -+ &mc->translation_ranges[i]; -+ -+ if (mc_region_type == range->mc_region_type && -+ mc_offset >= range->start_mc_offset && -+ mc_offset < range->end_mc_offset) { -+ *phys_addr = range->start_phys_addr + -+ (mc_offset - range->start_mc_offset); -+ return 0; -+ } -+ } -+ -+ return -EFAULT; -+} -+ -+static int fsl_mc_device_get_mmio_regions(struct fsl_mc_device *mc_dev, -+ struct fsl_mc_device *mc_bus_dev) -+{ -+ int i; -+ int error; -+ struct resource *regions; -+ struct dprc_obj_desc *obj_desc = &mc_dev->obj_desc; -+ struct device *parent_dev = mc_dev->dev.parent; -+ enum fsl_mc_region_types mc_region_type; -+ -+ if (strcmp(obj_desc->type, "dprc") == 0 || -+ strcmp(obj_desc->type, "dpmcp") == 0) { -+ mc_region_type = FSL_MC_PORTAL; -+ } else if (strcmp(obj_desc->type, "dpio") == 0) { -+ mc_region_type = FSL_QBMAN_PORTAL; -+ } else { -+ /* -+ * This function should not have been called for this MC object -+ * type, as this object type is not supposed to have MMIO -+ * regions -+ */ -+ WARN_ON(true); -+ return -EINVAL; -+ } -+ -+ regions = kmalloc_array(obj_desc->region_count, -+ sizeof(regions[0]), GFP_KERNEL); -+ if (!regions) -+ return -ENOMEM; -+ -+ for (i = 0; i < obj_desc->region_count; i++) { -+ struct dprc_region_desc region_desc; -+ -+ error = dprc_get_obj_region(mc_bus_dev->mc_io, -+ 0, -+ mc_bus_dev->mc_handle, -+ obj_desc->type, -+ obj_desc->id, i, ®ion_desc); -+ if (error < 0) { -+ dev_err(parent_dev, -+ "dprc_get_obj_region() failed: %d\n", error); -+ goto error_cleanup_regions; -+ } -+ -+ WARN_ON(region_desc.size == 0); -+ error = translate_mc_addr(mc_region_type, -+ region_desc.base_offset, -+ ®ions[i].start); -+ if (error < 0) { -+ dev_err(parent_dev, -+ "Invalid MC offset: %#x (for %s.%d\'s region %d)\n", -+ region_desc.base_offset, -+ obj_desc->type, obj_desc->id, i); -+ goto error_cleanup_regions; -+ } -+ -+ regions[i].end = regions[i].start + region_desc.size - 1; -+ regions[i].name = "fsl-mc object MMIO region"; -+ regions[i].flags = IORESOURCE_IO; -+ if (region_desc.flags & DPRC_REGION_CACHEABLE) -+ regions[i].flags |= IORESOURCE_CACHEABLE; -+ } -+ -+ mc_dev->regions = regions; -+ return 0; -+ -+error_cleanup_regions: -+ kfree(regions); -+ return error; -+} -+ -+/** -+ * Add a newly discovered MC object device to be visible in Linux -+ */ -+int fsl_mc_device_add(struct dprc_obj_desc *obj_desc, -+ struct fsl_mc_io *mc_io, -+ struct device *parent_dev, -+ const char *driver_override, -+ struct fsl_mc_device **new_mc_dev) -+{ -+ int error; -+ struct fsl_mc_device *mc_dev = NULL; -+ struct fsl_mc_bus *mc_bus = NULL; -+ struct fsl_mc_device *parent_mc_dev; -+ -+ if (parent_dev->bus == &fsl_mc_bus_type) -+ parent_mc_dev = to_fsl_mc_device(parent_dev); -+ else -+ parent_mc_dev = NULL; -+ -+ if (strcmp(obj_desc->type, "dprc") == 0) { -+ /* -+ * Allocate an MC bus device object: -+ */ -+ mc_bus = devm_kzalloc(parent_dev, sizeof(*mc_bus), GFP_KERNEL); -+ if (!mc_bus) -+ return -ENOMEM; -+ -+ mc_dev = &mc_bus->mc_dev; -+ } else { -+ /* -+ * Allocate a regular fsl_mc_device object: -+ */ -+ mc_dev = kmem_cache_zalloc(mc_dev_cache, GFP_KERNEL); -+ if (!mc_dev) -+ return -ENOMEM; -+ } -+ -+ mc_dev->obj_desc = *obj_desc; -+ mc_dev->mc_io = mc_io; -+ if (driver_override) { -+ /* -+ * We trust driver_override, so we don't need to use -+ * kstrndup() here -+ */ -+ mc_dev->driver_override = kstrdup(driver_override, GFP_KERNEL); -+ if (!mc_dev->driver_override) { -+ error = -ENOMEM; -+ goto error_cleanup_dev; -+ } -+ } -+ -+ device_initialize(&mc_dev->dev); -+ INIT_LIST_HEAD(&mc_dev->dev.msi_list); -+ mc_dev->dev.parent = parent_dev; -+ mc_dev->dev.bus = &fsl_mc_bus_type; -+ dev_set_name(&mc_dev->dev, "%s.%d", obj_desc->type, obj_desc->id); -+ -+ if (strcmp(obj_desc->type, "dprc") == 0) { -+ struct fsl_mc_io *mc_io2; -+ -+ mc_dev->flags |= FSL_MC_IS_DPRC; -+ -+ /* -+ * To get the DPRC's ICID, we need to open the DPRC -+ * in get_dprc_icid(). For child DPRCs, we do so using the -+ * parent DPRC's MC portal instead of the child DPRC's MC -+ * portal, in case the child DPRC is already opened with -+ * its own portal (e.g., the DPRC used by AIOP). -+ * -+ * NOTE: There cannot be more than one active open for a -+ * given MC object, using the same MC portal. -+ */ -+ if (parent_mc_dev) { -+ /* -+ * device being added is a child DPRC device -+ */ -+ mc_io2 = parent_mc_dev->mc_io; -+ } else { -+ /* -+ * device being added is the root DPRC device -+ */ -+ if (WARN_ON(!mc_io)) { -+ error = -EINVAL; -+ goto error_cleanup_dev; -+ } -+ -+ mc_io2 = mc_io; -+ } -+ -+ error = get_dprc_icid(mc_io2, obj_desc->id, &mc_dev->icid); -+ if (error < 0) -+ goto error_cleanup_dev; -+ } else { -+ /* -+ * A non-DPRC MC object device has to be a child of another -+ * MC object (specifically a DPRC object) -+ */ -+ mc_dev->icid = parent_mc_dev->icid; -+ mc_dev->dma_mask = FSL_MC_DEFAULT_DMA_MASK; -+ mc_dev->dev.dma_mask = &mc_dev->dma_mask; -+ } -+ -+ /* -+ * Get MMIO regions for the device from the MC: -+ * -+ * NOTE: the root DPRC is a special case as its MMIO region is -+ * obtained from the device tree -+ */ -+ if (parent_mc_dev && obj_desc->region_count != 0) { -+ error = fsl_mc_device_get_mmio_regions(mc_dev, -+ parent_mc_dev); -+ if (error < 0) -+ goto error_cleanup_dev; -+ } -+ -+ /* -+ * Objects are coherent, unless 'no shareability' flag set. -+ * FIXME: fill up @dma_base, @size, @iommu -+ */ -+ if (!(obj_desc->flags & DPRC_OBJ_FLAG_NO_MEM_SHAREABILITY)) -+ arch_setup_dma_ops(&mc_dev->dev, 0, 0, NULL, true); -+ -+ /* -+ * The device-specific probe callback will get invoked by device_add() -+ */ -+ error = device_add(&mc_dev->dev); -+ if (error < 0) { -+ dev_err(parent_dev, -+ "device_add() failed for device %s: %d\n", -+ dev_name(&mc_dev->dev), error); -+ goto error_cleanup_dev; -+ } -+ -+ (void)get_device(&mc_dev->dev); -+ dev_dbg(parent_dev, "Added MC object device %s\n", -+ dev_name(&mc_dev->dev)); -+ -+ *new_mc_dev = mc_dev; -+ return 0; -+ -+error_cleanup_dev: -+ kfree(mc_dev->regions); -+ if (mc_bus) -+ devm_kfree(parent_dev, mc_bus); -+ else -+ kmem_cache_free(mc_dev_cache, mc_dev); -+ -+ return error; -+} -+EXPORT_SYMBOL_GPL(fsl_mc_device_add); -+ -+/** -+ * fsl_mc_device_remove - Remove a MC object device from being visible to -+ * Linux -+ * -+ * @mc_dev: Pointer to a MC object device object -+ */ -+void fsl_mc_device_remove(struct fsl_mc_device *mc_dev) -+{ -+ struct fsl_mc_bus *mc_bus = NULL; -+ -+ kfree(mc_dev->regions); -+ -+ /* -+ * The device-specific remove callback will get invoked by device_del() -+ */ -+ device_del(&mc_dev->dev); -+ put_device(&mc_dev->dev); -+ -+ if (strcmp(mc_dev->obj_desc.type, "dprc") == 0) { -+ mc_bus = to_fsl_mc_bus(mc_dev); -+ -+ if (&mc_dev->dev == fsl_mc_bus_type.dev_root) -+ fsl_mc_bus_type.dev_root = NULL; -+ } else -+ WARN_ON(mc_dev->mc_io != NULL); -+ -+ kfree(mc_dev->driver_override); -+ mc_dev->driver_override = NULL; -+ if (mc_bus) -+ devm_kfree(mc_dev->dev.parent, mc_bus); -+ else -+ kmem_cache_free(mc_dev_cache, mc_dev); -+} -+EXPORT_SYMBOL_GPL(fsl_mc_device_remove); -+ -+static int mc_bus_msi_prepare(struct irq_domain *domain, struct device *dev, -+ int nvec, msi_alloc_info_t *info) -+{ -+ int error; -+ u32 its_dev_id; -+ struct dprc_attributes dprc_attr; -+ struct fsl_mc_device *mc_bus_dev = to_fsl_mc_device(dev); -+ -+ if (WARN_ON(!(mc_bus_dev->flags & FSL_MC_IS_DPRC))) -+ return -EINVAL; -+ -+ error = dprc_get_attributes(mc_bus_dev->mc_io, -+ 0, -+ mc_bus_dev->mc_handle, &dprc_attr); -+ if (error < 0) { -+ dev_err(&mc_bus_dev->dev, -+ "dprc_get_attributes() failed: %d\n", -+ error); -+ return error; -+ } -+ -+ /* -+ * Build the device Id to be passed to the GIC-ITS: -+ * -+ * NOTE: This device id corresponds to the IOMMU stream ID -+ * associated with the DPRC object. -+ */ -+ its_dev_id = mc_bus_dev->icid; -+ if (its_dev_id > STREAM_ID_ICID_MASK) { -+ dev_err(&mc_bus_dev->dev, -+ "Invalid ICID: %#x\n", its_dev_id); -+ return -ERANGE; -+ } -+ -+ if (dprc_attr.options & DPRC_CFG_OPT_AIOP) -+ its_dev_id |= STREAM_ID_PL_MASK | STREAM_ID_BMT_MASK; -+ -+ return __its_msi_prepare(domain, its_dev_id, dev, nvec, info); -+} -+ -+static void mc_bus_mask_msi_irq(struct irq_data *d) -+{ -+ /* Bus specefic Mask */ -+ irq_chip_mask_parent(d); -+} -+ -+static void mc_bus_unmask_msi_irq(struct irq_data *d) -+{ -+ /* Bus specefic unmask */ -+ irq_chip_unmask_parent(d); -+} -+ -+static void program_msi_at_mc(struct fsl_mc_device *mc_bus_dev, -+ struct fsl_mc_device_irq *irq) -+{ -+ int error; -+ struct fsl_mc_device *owner_mc_dev = irq->mc_dev; -+ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); -+ struct dprc_irq_cfg irq_cfg; -+ -+ /* -+ * irq->msi_paddr is 0x0 when this function is invoked in the -+ * free_irq() code path. In this case, for the MC, we don't -+ * really need to "unprogram" the MSI, so we just return. -+ * This helps avoid subtle ordering problems in the MC -+ * bus IRQ teardown logic. -+ * FIXME: evaluate whether there is a better way to address -+ * the underlying issue (upstreamability concern) -+ */ -+ if (irq->msi_paddr == 0x0) -+ return; -+ -+ if (WARN_ON(!owner_mc_dev)) -+ return; -+ -+ irq_cfg.paddr = irq->msi_paddr; -+ irq_cfg.val = irq->msi_value; -+ irq_cfg.irq_num = irq->irq_number; -+ -+ if (owner_mc_dev == mc_bus_dev) { -+ /* -+ * IRQ is for the mc_bus_dev's DPRC itself -+ */ -+ error = dprc_set_irq(mc_bus->atomic_mc_io, -+ MC_CMD_FLAG_INTR_DIS | MC_CMD_FLAG_PRI, -+ mc_bus->atomic_dprc_handle, -+ irq->dev_irq_index, -+ &irq_cfg); -+ if (error < 0) { -+ dev_err(&owner_mc_dev->dev, -+ "dprc_set_irq() failed: %d\n", error); -+ } -+ } else { -+ error = dprc_set_obj_irq(mc_bus->atomic_mc_io, -+ MC_CMD_FLAG_INTR_DIS | MC_CMD_FLAG_PRI, -+ mc_bus->atomic_dprc_handle, -+ owner_mc_dev->obj_desc.type, -+ owner_mc_dev->obj_desc.id, -+ irq->dev_irq_index, -+ &irq_cfg); -+ if (error < 0) { -+ dev_err(&owner_mc_dev->dev, -+ "dprc_obj_set_irq() failed: %d\n", error); -+ } -+ } -+} -+ -+/* -+ * This function is invoked from devm_request_irq(), -+ * devm_request_threaded_irq(), dev_free_irq() -+ */ -+static void mc_bus_msi_domain_write_msg(struct irq_data *irq_data, -+ struct msi_msg *msg) -+{ -+ struct msi_desc *msi_entry = irq_data->msi_desc; -+ struct fsl_mc_device *mc_bus_dev = to_fsl_mc_device(msi_entry->dev); -+ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); -+ struct fsl_mc_device_irq *irq_res = -+ &mc_bus->irq_resources[msi_entry->msi_attrib.entry_nr]; -+ -+ /* -+ * NOTE: This function is invoked with interrupts disabled -+ */ -+ -+ if (irq_res->irq_number == irq_data->irq) { -+ irq_res->msi_paddr = -+ ((u64)msg->address_hi << 32) | msg->address_lo; -+ -+ irq_res->msi_value = msg->data; -+ -+ /* -+ * Program the MSI (paddr, value) pair in the device: -+ */ -+ program_msi_at_mc(mc_bus_dev, irq_res); -+ } -+} -+ -+static struct irq_chip mc_bus_msi_irq_chip = { -+ .name = "fsl-mc-bus-msi", -+ .irq_unmask = mc_bus_unmask_msi_irq, -+ .irq_mask = mc_bus_mask_msi_irq, -+ .irq_eoi = irq_chip_eoi_parent, -+ .irq_write_msi_msg = mc_bus_msi_domain_write_msg, -+}; -+ -+static struct msi_domain_ops mc_bus_msi_ops = { -+ .msi_prepare = mc_bus_msi_prepare, -+}; -+ -+static struct msi_domain_info mc_bus_msi_domain_info = { -+ .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | -+ MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX), -+ .ops = &mc_bus_msi_ops, -+ .chip = &mc_bus_msi_irq_chip, -+}; -+ -+static int create_mc_irq_domain(struct platform_device *mc_pdev, -+ struct irq_domain **new_irq_domain) -+{ -+ int error; -+ struct device_node *its_of_node; -+ struct irq_domain *its_domain; -+ struct irq_domain *irq_domain; -+ struct device_node *mc_of_node = mc_pdev->dev.of_node; -+ -+ its_of_node = of_parse_phandle(mc_of_node, "msi-parent", 0); -+ if (!its_of_node) { -+ dev_err(&mc_pdev->dev, -+ "msi-parent phandle missing for %s\n", -+ mc_of_node->full_name); -+ return -ENOENT; -+ } -+ -+ /* -+ * Extract MSI parent node: -+ */ -+ its_domain = irq_find_host(its_of_node); -+ if (!its_domain) { -+ dev_err(&mc_pdev->dev, "Unable to find parent domain\n"); -+ error = -ENOENT; -+ goto cleanup_its_of_node; -+ } -+ -+ irq_domain = msi_create_irq_domain(mc_of_node, &mc_bus_msi_domain_info, -+ its_domain->parent); -+ if (!irq_domain) { -+ dev_err(&mc_pdev->dev, "Failed to allocate msi_domain\n"); -+ error = -ENOMEM; -+ goto cleanup_its_of_node; -+ } -+ -+ dev_dbg(&mc_pdev->dev, "Allocated MSI domain\n"); -+ *new_irq_domain = irq_domain; -+ return 0; -+ -+cleanup_its_of_node: -+ of_node_put(its_of_node); -+ return error; -+} -+ -+/* -+ * Initialize the interrupt pool associated with a MC bus. -+ * It allocates a block of IRQs from the GIC-ITS -+ */ -+int __must_check fsl_mc_populate_irq_pool(struct fsl_mc_bus *mc_bus, -+ unsigned int irq_count) -+{ -+ unsigned int i; -+ struct msi_desc *msi_entry; -+ struct msi_desc *next_msi_entry; -+ struct fsl_mc_device_irq *irq_resources; -+ struct fsl_mc_device_irq *irq_res; -+ int error; -+ struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev; -+ struct fsl_mc *mc = dev_get_drvdata(fsl_mc_bus_type.dev_root->parent); -+ struct fsl_mc_resource_pool *res_pool = -+ &mc_bus->resource_pools[FSL_MC_POOL_IRQ]; -+ -+ /* -+ * Detect duplicate invocations of this function: -+ */ -+ if (WARN_ON(!list_empty(&mc_bus_dev->dev.msi_list))) -+ return -EINVAL; -+ -+ if (WARN_ON(irq_count == 0 || -+ irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS)) -+ return -EINVAL; -+ -+ irq_resources = -+ devm_kzalloc(&mc_bus_dev->dev, -+ sizeof(*irq_resources) * irq_count, -+ GFP_KERNEL); -+ if (!irq_resources) -+ return -ENOMEM; -+ -+ for (i = 0; i < irq_count; i++) { -+ irq_res = &irq_resources[i]; -+ msi_entry = alloc_msi_entry(&mc_bus_dev->dev); -+ if (!msi_entry) { -+ dev_err(&mc_bus_dev->dev, "Failed to allocate msi entry\n"); -+ error = -ENOMEM; -+ goto cleanup_msi_entries; -+ } -+ -+ msi_entry->msi_attrib.is_msix = 1; -+ msi_entry->msi_attrib.is_64 = 1; -+ msi_entry->msi_attrib.entry_nr = i; -+ msi_entry->nvec_used = 1; -+ list_add_tail(&msi_entry->list, &mc_bus_dev->dev.msi_list); -+ -+ /* -+ * NOTE: irq_res->msi_paddr will be set by the -+ * mc_bus_msi_domain_write_msg() callback -+ */ -+ irq_res->resource.type = res_pool->type; -+ irq_res->resource.data = irq_res; -+ irq_res->resource.parent_pool = res_pool; -+ INIT_LIST_HEAD(&irq_res->resource.node); -+ list_add_tail(&irq_res->resource.node, &res_pool->free_list); -+ } -+ -+ /* -+ * NOTE: Calling this function will trigger the invocation of the -+ * mc_bus_msi_prepare() callback -+ */ -+ error = msi_domain_alloc_irqs(mc->irq_domain, -+ &mc_bus_dev->dev, irq_count); -+ -+ if (error) { -+ dev_err(&mc_bus_dev->dev, "Failed to allocate IRQs\n"); -+ goto cleanup_msi_entries; -+ } -+ -+ for_each_msi_entry(msi_entry, &mc_bus_dev->dev) { -+ u32 irq_num = msi_entry->irq; -+ -+ irq_res = &irq_resources[msi_entry->msi_attrib.entry_nr]; -+ irq_res->irq_number = irq_num; -+ irq_res->resource.id = irq_num; -+ } -+ -+ res_pool->max_count = irq_count; -+ res_pool->free_count = irq_count; -+ mc_bus->irq_resources = irq_resources; -+ return 0; -+ -+cleanup_msi_entries: -+ list_for_each_entry_safe(msi_entry, next_msi_entry, -+ &mc_bus_dev->dev.msi_list, list) { -+ list_del(&msi_entry->list); -+ kfree(msi_entry); -+ } -+ -+ devm_kfree(&mc_bus_dev->dev, irq_resources); -+ return error; -+} -+EXPORT_SYMBOL_GPL(fsl_mc_populate_irq_pool); -+ -+/** -+ * Teardown the interrupt pool associated with an MC bus. -+ * It frees the IRQs that were allocated to the pool, back to the GIC-ITS. -+ */ -+void fsl_mc_cleanup_irq_pool(struct fsl_mc_bus *mc_bus) -+{ -+ struct msi_desc *msi_entry; -+ struct msi_desc *next_msi_entry; -+ struct fsl_mc *mc = dev_get_drvdata(fsl_mc_bus_type.dev_root->parent); -+ struct fsl_mc_resource_pool *res_pool = -+ &mc_bus->resource_pools[FSL_MC_POOL_IRQ]; -+ -+ if (WARN_ON(!mc_bus->irq_resources)) -+ return; -+ -+ if (WARN_ON(res_pool->max_count == 0)) -+ return; -+ -+ if (WARN_ON(res_pool->free_count != res_pool->max_count)) -+ return; -+ -+ msi_domain_free_irqs(mc->irq_domain, &mc_bus->mc_dev.dev); -+ list_for_each_entry_safe(msi_entry, next_msi_entry, -+ &mc_bus->mc_dev.dev.msi_list, list) { -+ list_del(&msi_entry->list); -+ kfree(msi_entry); -+ } -+ -+ devm_kfree(&mc_bus->mc_dev.dev, mc_bus->irq_resources); -+ res_pool->max_count = 0; -+ res_pool->free_count = 0; -+ mc_bus->irq_resources = NULL; -+} -+EXPORT_SYMBOL_GPL(fsl_mc_cleanup_irq_pool); -+ -+static int parse_mc_ranges(struct device *dev, -+ int *paddr_cells, -+ int *mc_addr_cells, -+ int *mc_size_cells, -+ const __be32 **ranges_start, -+ uint8_t *num_ranges) -+{ -+ const __be32 *prop; -+ int range_tuple_cell_count; -+ int ranges_len; -+ int tuple_len; -+ struct device_node *mc_node = dev->of_node; -+ -+ *ranges_start = of_get_property(mc_node, "ranges", &ranges_len); -+ if (!(*ranges_start) || !ranges_len) { -+ dev_warn(dev, -+ "missing or empty ranges property for device tree node '%s'\n", -+ mc_node->name); -+ -+ *num_ranges = 0; -+ return 0; -+ } -+ -+ *paddr_cells = of_n_addr_cells(mc_node); -+ -+ prop = of_get_property(mc_node, "#address-cells", NULL); -+ if (prop) -+ *mc_addr_cells = be32_to_cpup(prop); -+ else -+ *mc_addr_cells = *paddr_cells; -+ -+ prop = of_get_property(mc_node, "#size-cells", NULL); -+ if (prop) -+ *mc_size_cells = be32_to_cpup(prop); -+ else -+ *mc_size_cells = of_n_size_cells(mc_node); -+ -+ range_tuple_cell_count = *paddr_cells + *mc_addr_cells + -+ *mc_size_cells; -+ -+ tuple_len = range_tuple_cell_count * sizeof(__be32); -+ if (ranges_len % tuple_len != 0) { -+ dev_err(dev, "malformed ranges property '%s'\n", mc_node->name); -+ return -EINVAL; -+ } -+ -+ *num_ranges = ranges_len / tuple_len; -+ return 0; -+} -+ -+static int get_mc_addr_translation_ranges(struct device *dev, -+ struct fsl_mc_addr_translation_range -+ **ranges, -+ uint8_t *num_ranges) -+{ -+ int error; -+ int paddr_cells; -+ int mc_addr_cells; -+ int mc_size_cells; -+ int i; -+ const __be32 *ranges_start; -+ const __be32 *cell; -+ -+ error = parse_mc_ranges(dev, -+ &paddr_cells, -+ &mc_addr_cells, -+ &mc_size_cells, -+ &ranges_start, -+ num_ranges); -+ if (error < 0) -+ return error; -+ -+ if (!(*num_ranges)) { -+ /* -+ * Missing or empty ranges property ("ranges;") for the -+ * 'fsl,qoriq-mc' node. In this case, identity mapping -+ * will be used. -+ */ -+ *ranges = NULL; -+ return 0; -+ } -+ -+ *ranges = devm_kcalloc(dev, *num_ranges, -+ sizeof(struct fsl_mc_addr_translation_range), -+ GFP_KERNEL); -+ if (!(*ranges)) -+ return -ENOMEM; -+ -+ cell = ranges_start; -+ for (i = 0; i < *num_ranges; ++i) { -+ struct fsl_mc_addr_translation_range *range = &(*ranges)[i]; -+ -+ range->mc_region_type = of_read_number(cell, 1); -+ range->start_mc_offset = of_read_number(cell + 1, -+ mc_addr_cells - 1); -+ cell += mc_addr_cells; -+ range->start_phys_addr = of_read_number(cell, paddr_cells); -+ cell += paddr_cells; -+ range->end_mc_offset = range->start_mc_offset + -+ of_read_number(cell, mc_size_cells); -+ -+ cell += mc_size_cells; -+ } -+ -+ return 0; -+} -+ -+/** -+ * fsl_mc_bus_probe - callback invoked when the root MC bus is being -+ * added -+ */ -+static int fsl_mc_bus_probe(struct platform_device *pdev) -+{ -+ struct dprc_obj_desc obj_desc; -+ int error; -+ struct fsl_mc *mc; -+ struct fsl_mc_device *mc_bus_dev = NULL; -+ struct fsl_mc_io *mc_io = NULL; -+ int container_id; -+ phys_addr_t mc_portal_phys_addr; -+ uint32_t mc_portal_size; -+ struct mc_version mc_version; -+ struct resource res; -+ -+ dev_info(&pdev->dev, "Root MC bus device probed"); -+ -+ mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL); -+ if (!mc) -+ return -ENOMEM; -+ -+ platform_set_drvdata(pdev, mc); -+ error = create_mc_irq_domain(pdev, &mc->irq_domain); -+ if (error < 0) { -+ dev_warn(&pdev->dev, -+ "WARNING: MC bus driver will run without interrupt support\n"); -+ } else { -+ mc->gic_supported = true; -+ } -+ -+ /* -+ * Get physical address of MC portal for the root DPRC: -+ */ -+ error = of_address_to_resource(pdev->dev.of_node, 0, &res); -+ if (error < 0) { -+ dev_err(&pdev->dev, -+ "of_address_to_resource() failed for %s\n", -+ pdev->dev.of_node->full_name); -+ goto error_cleanup_irq_domain; -+ } -+ -+ mc_portal_phys_addr = res.start; -+ mc_portal_size = resource_size(&res); -+ error = fsl_create_mc_io(&pdev->dev, mc_portal_phys_addr, -+ mc_portal_size, NULL, 0, &mc_io); -+ if (error < 0) -+ goto error_cleanup_irq_domain; -+ -+ error = mc_get_version(mc_io, 0, &mc_version); -+ if (error != 0) { -+ dev_err(&pdev->dev, -+ "mc_get_version() failed with error %d\n", error); -+ goto error_cleanup_mc_io; -+ } -+ -+ dev_info(&pdev->dev, -+ "Freescale Management Complex Firmware version: %u.%u.%u\n", -+ mc_version.major, mc_version.minor, mc_version.revision); -+ -+ error = get_mc_addr_translation_ranges(&pdev->dev, -+ &mc->translation_ranges, -+ &mc->num_translation_ranges); -+ if (error < 0) -+ goto error_cleanup_mc_io; -+ -+ error = dpmng_get_container_id(mc_io, 0, &container_id); -+ if (error < 0) { -+ dev_err(&pdev->dev, -+ "dpmng_get_container_id() failed: %d\n", error); -+ goto error_cleanup_mc_io; -+ } -+ -+ memset(&obj_desc, 0, sizeof(struct dprc_obj_desc)); -+ error = get_dprc_version(mc_io, container_id, -+ &obj_desc.ver_major, &obj_desc.ver_minor); -+ if (error < 0) -+ goto error_cleanup_mc_io; -+ -+ obj_desc.vendor = FSL_MC_VENDOR_FREESCALE; -+ strcpy(obj_desc.type, "dprc"); -+ obj_desc.id = container_id; -+ obj_desc.irq_count = 1; -+ obj_desc.region_count = 0; -+ -+ error = fsl_mc_device_add(&obj_desc, mc_io, &pdev->dev, NULL, -+ &mc_bus_dev); -+ if (error < 0) -+ goto error_cleanup_mc_io; -+ -+ mc->root_mc_bus_dev = mc_bus_dev; -+ return 0; -+ -+error_cleanup_mc_io: -+ fsl_destroy_mc_io(mc_io); -+ -+error_cleanup_irq_domain: -+ if (mc->gic_supported) -+ irq_domain_remove(mc->irq_domain); -+ -+ return error; -+} -+ -+/** -+ * fsl_mc_bus_remove - callback invoked when the root MC bus is being -+ * removed -+ */ -+static int fsl_mc_bus_remove(struct platform_device *pdev) -+{ -+ struct fsl_mc *mc = platform_get_drvdata(pdev); -+ -+ if (WARN_ON(&mc->root_mc_bus_dev->dev != fsl_mc_bus_type.dev_root)) -+ return -EINVAL; -+ -+ if (mc->gic_supported) -+ irq_domain_remove(mc->irq_domain); -+ -+ fsl_mc_device_remove(mc->root_mc_bus_dev); -+ fsl_destroy_mc_io(mc->root_mc_bus_dev->mc_io); -+ mc->root_mc_bus_dev->mc_io = NULL; -+ -+ dev_info(&pdev->dev, "Root MC bus device removed"); -+ return 0; -+} -+ -+static const struct of_device_id fsl_mc_bus_match_table[] = { -+ {.compatible = "fsl,qoriq-mc",}, -+ {}, -+}; -+ -+MODULE_DEVICE_TABLE(of, fsl_mc_bus_match_table); -+ -+static struct platform_driver fsl_mc_bus_driver = { -+ .driver = { -+ .name = "fsl_mc_bus", -+ .owner = THIS_MODULE, -+ .pm = NULL, -+ .of_match_table = fsl_mc_bus_match_table, -+ }, -+ .probe = fsl_mc_bus_probe, -+ .remove = fsl_mc_bus_remove, -+}; -+ -+static int __init fsl_mc_bus_driver_init(void) -+{ -+ int error; -+ -+ mc_dev_cache = kmem_cache_create("fsl_mc_device", -+ sizeof(struct fsl_mc_device), 0, 0, -+ NULL); -+ if (!mc_dev_cache) { -+ pr_err("Could not create fsl_mc_device cache\n"); -+ return -ENOMEM; -+ } -+ -+ error = bus_register(&fsl_mc_bus_type); -+ if (error < 0) { -+ pr_err("fsl-mc bus type registration failed: %d\n", error); -+ goto error_cleanup_cache; -+ } -+ -+ pr_info("fsl-mc bus type registered\n"); -+ -+ error = platform_driver_register(&fsl_mc_bus_driver); -+ if (error < 0) { -+ pr_err("platform_driver_register() failed: %d\n", error); -+ goto error_cleanup_bus; -+ } -+ -+ error = dprc_driver_init(); -+ if (error < 0) -+ goto error_cleanup_driver; -+ -+ error = fsl_mc_allocator_driver_init(); -+ if (error < 0) -+ goto error_cleanup_dprc_driver; -+ -+ return 0; -+ -+error_cleanup_dprc_driver: -+ dprc_driver_exit(); -+ -+error_cleanup_driver: -+ platform_driver_unregister(&fsl_mc_bus_driver); -+ -+error_cleanup_bus: -+ bus_unregister(&fsl_mc_bus_type); -+ -+error_cleanup_cache: -+ kmem_cache_destroy(mc_dev_cache); -+ return error; -+} -+ -+postcore_initcall(fsl_mc_bus_driver_init); -+ -+static void __exit fsl_mc_bus_driver_exit(void) -+{ -+ if (WARN_ON(!mc_dev_cache)) -+ return; -+ -+ fsl_mc_allocator_driver_exit(); -+ dprc_driver_exit(); -+ platform_driver_unregister(&fsl_mc_bus_driver); -+ bus_unregister(&fsl_mc_bus_type); -+ kmem_cache_destroy(mc_dev_cache); -+ pr_info("MC bus unregistered\n"); -+} -+ -+module_exit(fsl_mc_bus_driver_exit); -+ -+MODULE_AUTHOR("Freescale Semiconductor Inc."); -+MODULE_DESCRIPTION("Freescale Management Complex (MC) bus driver"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/staging/fsl-mc/bus/mc-ioctl.h b/drivers/staging/fsl-mc/bus/mc-ioctl.h -new file mode 100644 -index 0000000..d5c1bc3 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/mc-ioctl.h -@@ -0,0 +1,25 @@ -+/* -+ * Freescale Management Complex (MC) ioclt interface -+ * -+ * Copyright (C) 2014 Freescale Semiconductor, Inc. -+ * Author: German Rivera -+ * Lijun Pan -+ * -+ * This file is licensed under the terms of the GNU General Public -+ * License version 2. This program is licensed "as is" without any -+ * warranty of any kind, whether express or implied. -+ */ -+#ifndef _FSL_MC_IOCTL_H_ -+#define _FSL_MC_IOCTL_H_ -+ -+#include -+ -+#define RESTOOL_IOCTL_TYPE 'R' -+ -+#define RESTOOL_GET_ROOT_DPRC_INFO \ -+ _IOR(RESTOOL_IOCTL_TYPE, 0x1, uint32_t) -+ -+#define RESTOOL_SEND_MC_COMMAND \ -+ _IOWR(RESTOOL_IOCTL_TYPE, 0x4, struct mc_command) -+ -+#endif /* _FSL_MC_IOCTL_H_ */ -diff --git a/drivers/staging/fsl-mc/bus/mc-restool.c b/drivers/staging/fsl-mc/bus/mc-restool.c -new file mode 100644 -index 0000000..d261c1a ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/mc-restool.c -@@ -0,0 +1,312 @@ -+/* -+ * Freescale Management Complex (MC) restool driver -+ * -+ * Copyright (C) 2014 Freescale Semiconductor, Inc. -+ * Author: German Rivera -+ * Lijun Pan -+ * This file is licensed under the terms of the GNU General Public -+ * License version 2. This program is licensed "as is" without any -+ * warranty of any kind, whether express or implied. -+ */ -+ -+#include "../include/mc-private.h" -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "mc-ioctl.h" -+#include "../include/mc-sys.h" -+#include "../include/mc-cmd.h" -+#include "../include/dpmng.h" -+ -+/** -+ * Maximum number of DPRCs that can be opened at the same time -+ */ -+#define MAX_DPRC_HANDLES 64 -+ -+/** -+ * struct fsl_mc_restool - Management Complex (MC) resource manager object -+ * @tool_mc_io: pointer to the MC I/O object used by the restool -+ */ -+struct fsl_mc_restool { -+ struct fsl_mc_io *tool_mc_io; -+}; -+ -+/** -+ * struct global_state - indicating the number of static and dynamic instance -+ * @dynamic_instance_count - number of dynamically created instances -+ * @static_instance_in_use - static instance is in use or not -+ * @mutex - mutex lock to serialze the operations -+ */ -+struct global_state { -+ uint32_t dynamic_instance_count; -+ bool static_instance_in_use; -+ struct mutex mutex; -+}; -+ -+static struct fsl_mc_restool fsl_mc_restool = { 0 }; -+static struct global_state global_state = { 0 }; -+ -+static int fsl_mc_restool_dev_open(struct inode *inode, struct file *filep) -+{ -+ struct fsl_mc_device *root_mc_dev; -+ int error = 0; -+ struct fsl_mc_restool *fsl_mc_restool_new = NULL; -+ -+ mutex_lock(&global_state.mutex); -+ -+ if (WARN_ON(fsl_mc_bus_type.dev_root == NULL)) { -+ error = -EINVAL; -+ goto error; -+ } -+ -+ if (!global_state.static_instance_in_use) { -+ global_state.static_instance_in_use = true; -+ filep->private_data = &fsl_mc_restool; -+ } else { -+ fsl_mc_restool_new = kmalloc(sizeof(struct fsl_mc_restool), -+ GFP_KERNEL); -+ if (fsl_mc_restool_new == NULL) { -+ error = -ENOMEM; -+ goto error; -+ } -+ memset(fsl_mc_restool_new, 0, sizeof(*fsl_mc_restool_new)); -+ -+ root_mc_dev = to_fsl_mc_device(fsl_mc_bus_type.dev_root); -+ error = fsl_mc_portal_allocate(root_mc_dev, 0, -+ &fsl_mc_restool_new->tool_mc_io); -+ if (error < 0) { -+ pr_err("Not able to allocate MC portal\n"); -+ goto error; -+ } -+ ++global_state.dynamic_instance_count; -+ filep->private_data = fsl_mc_restool_new; -+ } -+ -+ mutex_unlock(&global_state.mutex); -+ return 0; -+error: -+ if (fsl_mc_restool_new != NULL && -+ fsl_mc_restool_new->tool_mc_io != NULL) { -+ fsl_mc_portal_free(fsl_mc_restool_new->tool_mc_io); -+ fsl_mc_restool_new->tool_mc_io = NULL; -+ } -+ -+ kfree(fsl_mc_restool_new); -+ mutex_unlock(&global_state.mutex); -+ return error; -+} -+ -+static int fsl_mc_restool_dev_release(struct inode *inode, struct file *filep) -+{ -+ struct fsl_mc_restool *fsl_mc_restool_local = filep->private_data; -+ -+ if (WARN_ON(filep->private_data == NULL)) -+ return -EINVAL; -+ -+ mutex_lock(&global_state.mutex); -+ -+ if (WARN_ON(global_state.dynamic_instance_count == 0 && -+ !global_state.static_instance_in_use)) { -+ mutex_unlock(&global_state.mutex); -+ return -EINVAL; -+ } -+ -+ /* Globally clean up opened/untracked handles */ -+ fsl_mc_portal_reset(fsl_mc_restool_local->tool_mc_io); -+ -+ pr_debug("dynamic instance count: %d\n", -+ global_state.dynamic_instance_count); -+ pr_debug("static instance count: %d\n", -+ global_state.static_instance_in_use); -+ -+ /* -+ * must check -+ * whether fsl_mc_restool_local is dynamic or global instance -+ * Otherwise it will free up the reserved portal by accident -+ * or even not free up the dynamic allocated portal -+ * if 2 or more instances running concurrently -+ */ -+ if (fsl_mc_restool_local == &fsl_mc_restool) { -+ pr_debug("this is reserved portal"); -+ pr_debug("reserved portal not in use\n"); -+ global_state.static_instance_in_use = false; -+ } else { -+ pr_debug("this is dynamically allocated portal"); -+ pr_debug("free one dynamically allocated portal\n"); -+ fsl_mc_portal_free(fsl_mc_restool_local->tool_mc_io); -+ kfree(filep->private_data); -+ --global_state.dynamic_instance_count; -+ } -+ -+ filep->private_data = NULL; -+ mutex_unlock(&global_state.mutex); -+ return 0; -+} -+ -+static int restool_get_root_dprc_info(unsigned long arg) -+{ -+ int error = -EINVAL; -+ uint32_t root_dprc_id; -+ struct fsl_mc_device *root_mc_dev; -+ -+ root_mc_dev = to_fsl_mc_device(fsl_mc_bus_type.dev_root); -+ root_dprc_id = root_mc_dev->obj_desc.id; -+ error = copy_to_user((void __user *)arg, &root_dprc_id, -+ sizeof(root_dprc_id)); -+ if (error < 0) { -+ pr_err("copy_to_user() failed with error %d\n", error); -+ goto error; -+ } -+ -+ return 0; -+error: -+ return error; -+} -+ -+static int restool_send_mc_command(unsigned long arg, -+ struct fsl_mc_restool *fsl_mc_restool) -+{ -+ int error = -EINVAL; -+ struct mc_command mc_cmd; -+ -+ error = copy_from_user(&mc_cmd, (void __user *)arg, sizeof(mc_cmd)); -+ if (error < 0) { -+ pr_err("copy_to_user() failed with error %d\n", error); -+ goto error; -+ } -+ -+ /* -+ * Send MC command to the MC: -+ */ -+ error = mc_send_command(fsl_mc_restool->tool_mc_io, &mc_cmd); -+ if (error < 0) -+ goto error; -+ -+ error = copy_to_user((void __user *)arg, &mc_cmd, sizeof(mc_cmd)); -+ if (error < 0) { -+ pr_err("copy_to_user() failed with error %d\n", error); -+ goto error; -+ } -+ -+ return 0; -+error: -+ return error; -+} -+ -+static long -+fsl_mc_restool_dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -+{ -+ int error = -EINVAL; -+ -+ if (WARN_ON(fsl_mc_bus_type.dev_root == NULL)) -+ goto out; -+ -+ switch (cmd) { -+ case RESTOOL_GET_ROOT_DPRC_INFO: -+ error = restool_get_root_dprc_info(arg); -+ break; -+ -+ case RESTOOL_SEND_MC_COMMAND: -+ error = restool_send_mc_command(arg, file->private_data); -+ break; -+ default: -+ error = -EINVAL; -+ } -+out: -+ return error; -+} -+ -+static const struct file_operations fsl_mc_restool_dev_fops = { -+ .owner = THIS_MODULE, -+ .open = fsl_mc_restool_dev_open, -+ .release = fsl_mc_restool_dev_release, -+ .unlocked_ioctl = fsl_mc_restool_dev_ioctl, -+ .compat_ioctl = fsl_mc_restool_dev_ioctl, -+}; -+ -+static struct miscdevice fsl_mc_restool_dev = { -+ .minor = MISC_DYNAMIC_MINOR, -+ .name = "mc_restool", -+ .fops = &fsl_mc_restool_dev_fops -+}; -+ -+static int __init fsl_mc_restool_driver_init(void) -+{ -+ struct fsl_mc_device *root_mc_dev; -+ int error = -EINVAL; -+ bool restool_dev_registered = false; -+ -+ mutex_init(&global_state.mutex); -+ -+ if (WARN_ON(fsl_mc_restool.tool_mc_io != NULL)) -+ goto error; -+ -+ if (WARN_ON(global_state.dynamic_instance_count != 0)) -+ goto error; -+ -+ if (WARN_ON(global_state.static_instance_in_use)) -+ goto error; -+ -+ if (fsl_mc_bus_type.dev_root == NULL) { -+ pr_err("fsl-mc bus not found, restool driver registration failed\n"); -+ goto error; -+ } -+ -+ root_mc_dev = to_fsl_mc_device(fsl_mc_bus_type.dev_root); -+ error = fsl_mc_portal_allocate(root_mc_dev, 0, -+ &fsl_mc_restool.tool_mc_io); -+ if (error < 0) { -+ pr_err("Not able to allocate MC portal\n"); -+ goto error; -+ } -+ -+ error = misc_register(&fsl_mc_restool_dev); -+ if (error < 0) { -+ pr_err("misc_register() failed: %d\n", error); -+ goto error; -+ } -+ -+ restool_dev_registered = true; -+ pr_info("%s driver registered\n", fsl_mc_restool_dev.name); -+ return 0; -+error: -+ if (restool_dev_registered) -+ misc_deregister(&fsl_mc_restool_dev); -+ -+ if (fsl_mc_restool.tool_mc_io != NULL) { -+ fsl_mc_portal_free(fsl_mc_restool.tool_mc_io); -+ fsl_mc_restool.tool_mc_io = NULL; -+ } -+ -+ return error; -+} -+ -+module_init(fsl_mc_restool_driver_init); -+ -+static void __exit fsl_mc_restool_driver_exit(void) -+{ -+ if (WARN_ON(fsl_mc_restool.tool_mc_io == NULL)) -+ return; -+ -+ if (WARN_ON(global_state.dynamic_instance_count != 0)) -+ return; -+ -+ if (WARN_ON(global_state.static_instance_in_use)) -+ return; -+ -+ misc_deregister(&fsl_mc_restool_dev); -+ fsl_mc_portal_free(fsl_mc_restool.tool_mc_io); -+ fsl_mc_restool.tool_mc_io = NULL; -+ pr_info("%s driver unregistered\n", fsl_mc_restool_dev.name); -+} -+ -+module_exit(fsl_mc_restool_driver_exit); -+ -+MODULE_AUTHOR("Freescale Semiconductor Inc."); -+MODULE_DESCRIPTION("Freescale's MC restool driver"); -+MODULE_LICENSE("GPL"); -+ -diff --git a/drivers/staging/fsl-mc/bus/mc-sys.c b/drivers/staging/fsl-mc/bus/mc-sys.c -new file mode 100644 -index 0000000..d3b6940 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/mc-sys.c -@@ -0,0 +1,677 @@ -+/* Copyright 2013-2014 Freescale Semiconductor Inc. -+ * -+ * I/O services to send MC commands to the MC hardware -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+ -+#include "../include/mc-sys.h" -+#include "../include/mc-cmd.h" -+#include "../include/mc.h" -+#include -+#include -+#include -+#include -+#include -+#include "dpmcp.h" -+ -+/** -+ * Timeout in milliseconds to wait for the completion of an MC command -+ * 5000 ms is barely enough for dpsw/dpdmux creation -+ * TODO: if MC firmware could response faster, we should decrease this value -+ */ -+#define MC_CMD_COMPLETION_TIMEOUT_MS 5000 -+ -+/* -+ * usleep_range() min and max values used to throttle down polling -+ * iterations while waiting for MC command completion -+ */ -+#define MC_CMD_COMPLETION_POLLING_MIN_SLEEP_USECS 10 -+#define MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS 500 -+ -+#define MC_CMD_HDR_READ_CMDID(_hdr) \ -+ ((uint16_t)mc_dec((_hdr), MC_CMD_HDR_CMDID_O, MC_CMD_HDR_CMDID_S)) -+ -+/** -+ * dpmcp_irq0_handler - Regular ISR for DPMCP interrupt 0 -+ * -+ * @irq: IRQ number of the interrupt being handled -+ * @arg: Pointer to device structure -+ */ -+static irqreturn_t dpmcp_irq0_handler(int irq_num, void *arg) -+{ -+ struct device *dev = (struct device *)arg; -+ struct fsl_mc_device *dpmcp_dev = to_fsl_mc_device(dev); -+ struct fsl_mc_io *mc_io = dpmcp_dev->mc_io; -+ -+ dev_dbg(dev, "DPMCP IRQ %d triggered on CPU %u\n", irq_num, -+ smp_processor_id()); -+ -+ if (WARN_ON(dpmcp_dev->irqs[0]->irq_number != (uint32_t)irq_num)) -+ goto out; -+ -+ if (WARN_ON(!mc_io)) -+ goto out; -+ -+ complete(&mc_io->mc_command_done_completion); -+out: -+ return IRQ_HANDLED; -+} -+ -+/* -+ * Disable and clear interrupts for a given DPMCP object -+ */ -+static int disable_dpmcp_irq(struct fsl_mc_device *dpmcp_dev) -+{ -+ int error; -+ -+ /* -+ * Disable generation of the DPMCP interrupt: -+ */ -+ error = dpmcp_set_irq_enable(dpmcp_dev->mc_io, -+ MC_CMD_FLAG_INTR_DIS, -+ dpmcp_dev->mc_handle, -+ DPMCP_IRQ_INDEX, 0); -+ if (error < 0) { -+ dev_err(&dpmcp_dev->dev, -+ "dpmcp_set_irq_enable() failed: %d\n", error); -+ -+ return error; -+ } -+ -+ /* -+ * Disable all DPMCP interrupt causes: -+ */ -+ error = dpmcp_set_irq_mask(dpmcp_dev->mc_io, -+ MC_CMD_FLAG_INTR_DIS, -+ dpmcp_dev->mc_handle, -+ DPMCP_IRQ_INDEX, 0x0); -+ if (error < 0) { -+ dev_err(&dpmcp_dev->dev, -+ "dpmcp_set_irq_mask() failed: %d\n", error); -+ -+ return error; -+ } -+ -+ return 0; -+} -+ -+static void unregister_dpmcp_irq_handler(struct fsl_mc_device *dpmcp_dev) -+{ -+ struct fsl_mc_device_irq *irq = dpmcp_dev->irqs[DPMCP_IRQ_INDEX]; -+ -+ devm_free_irq(&dpmcp_dev->dev, irq->irq_number, &dpmcp_dev->dev); -+} -+ -+static int register_dpmcp_irq_handler(struct fsl_mc_device *dpmcp_dev) -+{ -+ int error; -+ struct fsl_mc_device_irq *irq = dpmcp_dev->irqs[DPMCP_IRQ_INDEX]; -+ -+ error = devm_request_irq(&dpmcp_dev->dev, -+ irq->irq_number, -+ dpmcp_irq0_handler, -+ IRQF_NO_SUSPEND | IRQF_ONESHOT, -+ "FSL MC DPMCP irq0", -+ &dpmcp_dev->dev); -+ if (error < 0) { -+ dev_err(&dpmcp_dev->dev, -+ "devm_request_irq() failed: %d\n", -+ error); -+ return error; -+ } -+ -+ return 0; -+} -+ -+static int enable_dpmcp_irq(struct fsl_mc_device *dpmcp_dev) -+{ -+ int error; -+ -+ /* -+ * Enable MC command completion event to trigger DPMCP interrupt: -+ */ -+ error = dpmcp_set_irq_mask(dpmcp_dev->mc_io, -+ MC_CMD_FLAG_INTR_DIS, -+ dpmcp_dev->mc_handle, -+ DPMCP_IRQ_INDEX, -+ DPMCP_IRQ_EVENT_CMD_DONE); -+ if (error < 0) { -+ dev_err(&dpmcp_dev->dev, -+ "dpmcp_set_irq_mask() failed: %d\n", error); -+ -+ return error; -+ } -+ -+ /* -+ * Enable generation of the interrupt: -+ */ -+ error = dpmcp_set_irq_enable(dpmcp_dev->mc_io, -+ MC_CMD_FLAG_INTR_DIS, -+ dpmcp_dev->mc_handle, -+ DPMCP_IRQ_INDEX, 1); -+ if (error < 0) { -+ dev_err(&dpmcp_dev->dev, -+ "dpmcp_set_irq_enable() failed: %d\n", error); -+ -+ return error; -+ } -+ -+ return 0; -+} -+ -+/* -+ * Setup MC command completion interrupt for the DPMCP device associated with a -+ * given fsl_mc_io object -+ */ -+int fsl_mc_io_setup_dpmcp_irq(struct fsl_mc_io *mc_io) -+{ -+ int error; -+ struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev; -+ -+ if (WARN_ON(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)) -+ return -EINVAL; -+ -+ if (WARN_ON(!dpmcp_dev)) -+ return -EINVAL; -+ -+ if (WARN_ON(!fsl_mc_interrupts_supported())) -+ return -EINVAL; -+ -+ if (WARN_ON(dpmcp_dev->obj_desc.irq_count != 1)) -+ return -EINVAL; -+ -+ if (WARN_ON(dpmcp_dev->mc_io != mc_io)) -+ return -EINVAL; -+ -+ error = fsl_mc_allocate_irqs(dpmcp_dev); -+ if (error < 0) -+ return error; -+ -+ error = disable_dpmcp_irq(dpmcp_dev); -+ if (error < 0) -+ goto error_free_irqs; -+ -+ error = register_dpmcp_irq_handler(dpmcp_dev); -+ if (error < 0) -+ goto error_free_irqs; -+ -+ error = enable_dpmcp_irq(dpmcp_dev); -+ if (error < 0) -+ goto error_unregister_irq_handler; -+ -+ mc_io->mc_command_done_irq_armed = true; -+ return 0; -+ -+error_unregister_irq_handler: -+ unregister_dpmcp_irq_handler(dpmcp_dev); -+ -+error_free_irqs: -+ fsl_mc_free_irqs(dpmcp_dev); -+ -+ return error; -+} -+EXPORT_SYMBOL_GPL(fsl_mc_io_setup_dpmcp_irq); -+ -+/* -+ * Tear down interrupts for the DPMCP device associated with a given fsl_mc_io -+ * object -+ */ -+static void teardown_dpmcp_irq(struct fsl_mc_io *mc_io) -+{ -+ struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev; -+ -+ if (WARN_ON(!dpmcp_dev)) -+ return; -+ if (WARN_ON(!fsl_mc_interrupts_supported())) -+ return; -+ if (WARN_ON(!dpmcp_dev->irqs)) -+ return; -+ -+ mc_io->mc_command_done_irq_armed = false; -+ (void)disable_dpmcp_irq(dpmcp_dev); -+ unregister_dpmcp_irq_handler(dpmcp_dev); -+ fsl_mc_free_irqs(dpmcp_dev); -+} -+ -+/** -+ * Creates an MC I/O object -+ * -+ * @dev: device to be associated with the MC I/O object -+ * @mc_portal_phys_addr: physical address of the MC portal to use -+ * @mc_portal_size: size in bytes of the MC portal -+ * @resource: Pointer to MC bus object allocator resource associated -+ * with this MC I/O object or NULL if none. -+ * @flags: flags for the new MC I/O object -+ * @new_mc_io: Area to return pointer to newly created MC I/O object -+ * -+ * Returns '0' on Success; Error code otherwise. -+ */ -+int __must_check fsl_create_mc_io(struct device *dev, -+ phys_addr_t mc_portal_phys_addr, -+ uint32_t mc_portal_size, -+ struct fsl_mc_device *dpmcp_dev, -+ uint32_t flags, struct fsl_mc_io **new_mc_io) -+{ -+ int error; -+ struct fsl_mc_io *mc_io; -+ void __iomem *mc_portal_virt_addr; -+ struct resource *res; -+ -+ mc_io = devm_kzalloc(dev, sizeof(*mc_io), GFP_KERNEL); -+ if (!mc_io) -+ return -ENOMEM; -+ -+ mc_io->dev = dev; -+ mc_io->flags = flags; -+ mc_io->portal_phys_addr = mc_portal_phys_addr; -+ mc_io->portal_size = mc_portal_size; -+ mc_io->mc_command_done_irq_armed = false; -+ if (flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL) { -+ spin_lock_init(&mc_io->spinlock); -+ } else { -+ mutex_init(&mc_io->mutex); -+ init_completion(&mc_io->mc_command_done_completion); -+ } -+ -+ res = devm_request_mem_region(dev, -+ mc_portal_phys_addr, -+ mc_portal_size, -+ "mc_portal"); -+ if (!res) { -+ dev_err(dev, -+ "devm_request_mem_region failed for MC portal %#llx\n", -+ mc_portal_phys_addr); -+ return -EBUSY; -+ } -+ -+ mc_portal_virt_addr = devm_ioremap_nocache(dev, -+ mc_portal_phys_addr, -+ mc_portal_size); -+ if (!mc_portal_virt_addr) { -+ dev_err(dev, -+ "devm_ioremap_nocache failed for MC portal %#llx\n", -+ mc_portal_phys_addr); -+ return -ENXIO; -+ } -+ -+ mc_io->portal_virt_addr = mc_portal_virt_addr; -+ if (dpmcp_dev) { -+ error = fsl_mc_io_set_dpmcp(mc_io, dpmcp_dev); -+ if (error < 0) -+ goto error_destroy_mc_io; -+ -+ if (!(flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL) && -+ fsl_mc_interrupts_supported()) { -+ error = fsl_mc_io_setup_dpmcp_irq(mc_io); -+ if (error < 0) -+ goto error_destroy_mc_io; -+ } -+ } -+ -+ *new_mc_io = mc_io; -+ return 0; -+ -+error_destroy_mc_io: -+ fsl_destroy_mc_io(mc_io); -+ return error; -+ -+} -+EXPORT_SYMBOL_GPL(fsl_create_mc_io); -+ -+/** -+ * Destroys an MC I/O object -+ * -+ * @mc_io: MC I/O object to destroy -+ */ -+void fsl_destroy_mc_io(struct fsl_mc_io *mc_io) -+{ -+ struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev; -+ -+ if (dpmcp_dev) -+ fsl_mc_io_unset_dpmcp(mc_io); -+ -+ devm_iounmap(mc_io->dev, mc_io->portal_virt_addr); -+ devm_release_mem_region(mc_io->dev, -+ mc_io->portal_phys_addr, -+ mc_io->portal_size); -+ -+ mc_io->portal_virt_addr = NULL; -+ devm_kfree(mc_io->dev, mc_io); -+} -+EXPORT_SYMBOL_GPL(fsl_destroy_mc_io); -+ -+int fsl_mc_io_set_dpmcp(struct fsl_mc_io *mc_io, -+ struct fsl_mc_device *dpmcp_dev) -+{ -+ int error; -+ -+ if (WARN_ON(!dpmcp_dev)) -+ return -EINVAL; -+ -+ if (WARN_ON(mc_io->dpmcp_dev)) -+ return -EINVAL; -+ -+ if (WARN_ON(dpmcp_dev->mc_io)) -+ return -EINVAL; -+ -+ if (!(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)) { -+ error = dpmcp_open(mc_io, -+ 0, -+ dpmcp_dev->obj_desc.id, -+ &dpmcp_dev->mc_handle); -+ if (error < 0) -+ return error; -+ } -+ -+ mc_io->dpmcp_dev = dpmcp_dev; -+ dpmcp_dev->mc_io = mc_io; -+ return 0; -+} -+EXPORT_SYMBOL_GPL(fsl_mc_io_set_dpmcp); -+ -+void fsl_mc_io_unset_dpmcp(struct fsl_mc_io *mc_io) -+{ -+ int error; -+ struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev; -+ -+ if (WARN_ON(!dpmcp_dev)) -+ return; -+ -+ if (WARN_ON(dpmcp_dev->mc_io != mc_io)) -+ return; -+ -+ if (!(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)) { -+ if (dpmcp_dev->irqs) -+ teardown_dpmcp_irq(mc_io); -+ -+ error = dpmcp_close(mc_io, -+ 0, -+ dpmcp_dev->mc_handle); -+ if (error < 0) { -+ dev_err(&dpmcp_dev->dev, "dpmcp_close() failed: %d\n", -+ error); -+ } -+ } -+ -+ mc_io->dpmcp_dev = NULL; -+ dpmcp_dev->mc_io = NULL; -+} -+EXPORT_SYMBOL_GPL(fsl_mc_io_unset_dpmcp); -+ -+static int mc_status_to_error(enum mc_cmd_status status) -+{ -+ static const int mc_status_to_error_map[] = { -+ [MC_CMD_STATUS_OK] = 0, -+ [MC_CMD_STATUS_AUTH_ERR] = -EACCES, -+ [MC_CMD_STATUS_NO_PRIVILEGE] = -EPERM, -+ [MC_CMD_STATUS_DMA_ERR] = -EIO, -+ [MC_CMD_STATUS_CONFIG_ERR] = -ENXIO, -+ [MC_CMD_STATUS_TIMEOUT] = -ETIMEDOUT, -+ [MC_CMD_STATUS_NO_RESOURCE] = -ENAVAIL, -+ [MC_CMD_STATUS_NO_MEMORY] = -ENOMEM, -+ [MC_CMD_STATUS_BUSY] = -EBUSY, -+ [MC_CMD_STATUS_UNSUPPORTED_OP] = -ENOTSUPP, -+ [MC_CMD_STATUS_INVALID_STATE] = -ENODEV, -+ }; -+ -+ if (WARN_ON((u32)status >= ARRAY_SIZE(mc_status_to_error_map))) -+ return -EINVAL; -+ -+ return mc_status_to_error_map[status]; -+} -+ -+static const char *mc_status_to_string(enum mc_cmd_status status) -+{ -+ static const char *const status_strings[] = { -+ [MC_CMD_STATUS_OK] = "Command completed successfully", -+ [MC_CMD_STATUS_READY] = "Command ready to be processed", -+ [MC_CMD_STATUS_AUTH_ERR] = "Authentication error", -+ [MC_CMD_STATUS_NO_PRIVILEGE] = "No privilege", -+ [MC_CMD_STATUS_DMA_ERR] = "DMA or I/O error", -+ [MC_CMD_STATUS_CONFIG_ERR] = "Configuration error", -+ [MC_CMD_STATUS_TIMEOUT] = "Operation timed out", -+ [MC_CMD_STATUS_NO_RESOURCE] = "No resources", -+ [MC_CMD_STATUS_NO_MEMORY] = "No memory available", -+ [MC_CMD_STATUS_BUSY] = "Device is busy", -+ [MC_CMD_STATUS_UNSUPPORTED_OP] = "Unsupported operation", -+ [MC_CMD_STATUS_INVALID_STATE] = "Invalid state" -+ }; -+ -+ if ((unsigned int)status >= ARRAY_SIZE(status_strings)) -+ return "Unknown MC error"; -+ -+ return status_strings[status]; -+} -+ -+/** -+ * mc_write_command - writes a command to a Management Complex (MC) portal -+ * -+ * @portal: pointer to an MC portal -+ * @cmd: pointer to a filled command -+ */ -+static inline void mc_write_command(struct mc_command __iomem *portal, -+ struct mc_command *cmd) -+{ -+ int i; -+ -+ /* copy command parameters into the portal */ -+ for (i = 0; i < MC_CMD_NUM_OF_PARAMS; i++) -+ writeq(cmd->params[i], &portal->params[i]); -+ -+ /* submit the command by writing the header */ -+ writeq(cmd->header, &portal->header); -+} -+ -+/** -+ * mc_read_response - reads the response for the last MC command from a -+ * Management Complex (MC) portal -+ * -+ * @portal: pointer to an MC portal -+ * @resp: pointer to command response buffer -+ * -+ * Returns MC_CMD_STATUS_OK on Success; Error code otherwise. -+ */ -+static inline enum mc_cmd_status mc_read_response(struct mc_command __iomem * -+ portal, -+ struct mc_command *resp) -+{ -+ int i; -+ enum mc_cmd_status status; -+ -+ /* Copy command response header from MC portal: */ -+ resp->header = readq(&portal->header); -+ status = MC_CMD_HDR_READ_STATUS(resp->header); -+ if (status != MC_CMD_STATUS_OK) -+ return status; -+ -+ /* Copy command response data from MC portal: */ -+ for (i = 0; i < MC_CMD_NUM_OF_PARAMS; i++) -+ resp->params[i] = readq(&portal->params[i]); -+ -+ return status; -+} -+ -+static int mc_completion_wait(struct fsl_mc_io *mc_io, struct mc_command *cmd, -+ enum mc_cmd_status *mc_status) -+{ -+ enum mc_cmd_status status; -+ unsigned long jiffies_left; -+ unsigned long timeout_jiffies = -+ msecs_to_jiffies(MC_CMD_COMPLETION_TIMEOUT_MS); -+ -+ if (WARN_ON(!mc_io->dpmcp_dev)) -+ return -EINVAL; -+ -+ if (WARN_ON(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)) -+ return -EINVAL; -+ -+ for (;;) { -+ status = mc_read_response(mc_io->portal_virt_addr, cmd); -+ if (status != MC_CMD_STATUS_READY) -+ break; -+ -+ jiffies_left = wait_for_completion_timeout( -+ &mc_io->mc_command_done_completion, -+ timeout_jiffies); -+ if (jiffies_left == 0) -+ return -ETIMEDOUT; -+ } -+ -+ *mc_status = status; -+ return 0; -+} -+ -+static int mc_polling_wait_preemptible(struct fsl_mc_io *mc_io, -+ struct mc_command *cmd, -+ enum mc_cmd_status *mc_status) -+{ -+ enum mc_cmd_status status; -+ unsigned long jiffies_until_timeout = -+ jiffies + msecs_to_jiffies(MC_CMD_COMPLETION_TIMEOUT_MS); -+ -+ for (;;) { -+ status = mc_read_response(mc_io->portal_virt_addr, cmd); -+ if (status != MC_CMD_STATUS_READY) -+ break; -+ -+ usleep_range(MC_CMD_COMPLETION_POLLING_MIN_SLEEP_USECS, -+ MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS); -+ -+ if (time_after_eq(jiffies, jiffies_until_timeout)) -+ return -ETIMEDOUT; -+ } -+ -+ *mc_status = status; -+ return 0; -+} -+ -+static int mc_polling_wait_atomic(struct fsl_mc_io *mc_io, -+ struct mc_command *cmd, -+ enum mc_cmd_status *mc_status) -+{ -+ enum mc_cmd_status status; -+ unsigned long timeout_usecs = MC_CMD_COMPLETION_TIMEOUT_MS * 1000; -+ -+ BUILD_BUG_ON((MC_CMD_COMPLETION_TIMEOUT_MS * 1000) % -+ MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS != 0); -+ -+ for (;;) { -+ status = mc_read_response(mc_io->portal_virt_addr, cmd); -+ if (status != MC_CMD_STATUS_READY) -+ break; -+ -+ udelay(MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS); -+ timeout_usecs -= MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS; -+ if (timeout_usecs == 0) -+ return -ETIMEDOUT; -+ } -+ -+ *mc_status = status; -+ return 0; -+} -+ -+/** -+ * Sends a command to the MC device using the given MC I/O object -+ * -+ * @mc_io: MC I/O object to be used -+ * @cmd: command to be sent -+ * -+ * Returns '0' on Success; Error code otherwise. -+ */ -+int mc_send_command(struct fsl_mc_io *mc_io, struct mc_command *cmd) -+{ -+ int error; -+ enum mc_cmd_status status; -+ unsigned long irq_flags = 0; -+ bool dpmcp_completion_intr_disabled = -+ (MC_CMD_HDR_READ_FLAGS(cmd->header) & MC_CMD_FLAG_INTR_DIS); -+ -+ if (WARN_ON(in_irq() && -+ (!dpmcp_completion_intr_disabled || -+ !(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)))) -+ return -EINVAL; -+ -+ if (mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL) -+ spin_lock_irqsave(&mc_io->spinlock, irq_flags); -+ else -+ mutex_lock(&mc_io->mutex); -+ -+ /* -+ * Send command to the MC hardware: -+ */ -+ mc_write_command(mc_io->portal_virt_addr, cmd); -+ -+ /* -+ * Wait for response from the MC hardware: -+ */ -+ if (mc_io->mc_command_done_irq_armed && !dpmcp_completion_intr_disabled) -+ error = mc_completion_wait(mc_io, cmd, &status); -+ else if (!(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)) -+ error = mc_polling_wait_preemptible(mc_io, cmd, &status); -+ else -+ error = mc_polling_wait_atomic(mc_io, cmd, &status); -+ -+ if (error < 0) { -+ if (error == -ETIMEDOUT) { -+ pr_debug("MC command timed out (portal: %#llx, obj handle: %#x, command: %#x)\n", -+ mc_io->portal_phys_addr, -+ (unsigned int) -+ MC_CMD_HDR_READ_TOKEN(cmd->header), -+ (unsigned int) -+ MC_CMD_HDR_READ_CMDID(cmd->header)); -+ } -+ goto common_exit; -+ -+ } -+ -+ if (status != MC_CMD_STATUS_OK) { -+ pr_debug("MC command failed: portal: %#llx, obj handle: %#x, command: %#x, status: %s (%#x)\n", -+ mc_io->portal_phys_addr, -+ (unsigned int)MC_CMD_HDR_READ_TOKEN(cmd->header), -+ (unsigned int)MC_CMD_HDR_READ_CMDID(cmd->header), -+ mc_status_to_string(status), -+ (unsigned int)status); -+ -+ error = mc_status_to_error(status); -+ goto common_exit; -+ } -+ -+ error = 0; -+ -+common_exit: -+ if (mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL) -+ spin_unlock_irqrestore(&mc_io->spinlock, irq_flags); -+ else -+ mutex_unlock(&mc_io->mutex); -+ -+ return error; -+} -+EXPORT_SYMBOL(mc_send_command); -diff --git a/drivers/staging/fsl-mc/include/dpbp-cmd.h b/drivers/staging/fsl-mc/include/dpbp-cmd.h -new file mode 100644 -index 0000000..1ec04e4 ---- /dev/null -+++ b/drivers/staging/fsl-mc/include/dpbp-cmd.h -@@ -0,0 +1,62 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef _FSL_DPBP_CMD_H -+#define _FSL_DPBP_CMD_H -+ -+/* DPBP Version */ -+#define DPBP_VER_MAJOR 2 -+#define DPBP_VER_MINOR 2 -+ -+/* Command IDs */ -+#define DPBP_CMDID_CLOSE 0x800 -+#define DPBP_CMDID_OPEN 0x804 -+#define DPBP_CMDID_CREATE 0x904 -+#define DPBP_CMDID_DESTROY 0x900 -+ -+#define DPBP_CMDID_ENABLE 0x002 -+#define DPBP_CMDID_DISABLE 0x003 -+#define DPBP_CMDID_GET_ATTR 0x004 -+#define DPBP_CMDID_RESET 0x005 -+#define DPBP_CMDID_IS_ENABLED 0x006 -+ -+#define DPBP_CMDID_SET_IRQ 0x010 -+#define DPBP_CMDID_GET_IRQ 0x011 -+#define DPBP_CMDID_SET_IRQ_ENABLE 0x012 -+#define DPBP_CMDID_GET_IRQ_ENABLE 0x013 -+#define DPBP_CMDID_SET_IRQ_MASK 0x014 -+#define DPBP_CMDID_GET_IRQ_MASK 0x015 -+#define DPBP_CMDID_GET_IRQ_STATUS 0x016 -+#define DPBP_CMDID_CLEAR_IRQ_STATUS 0x017 -+ -+#define DPBP_CMDID_SET_NOTIFICATIONS 0x01b0 -+#define DPBP_CMDID_GET_NOTIFICATIONS 0x01b1 -+#endif /* _FSL_DPBP_CMD_H */ -diff --git a/drivers/staging/fsl-mc/include/dpbp.h b/drivers/staging/fsl-mc/include/dpbp.h -new file mode 100644 -index 0000000..9856bb8 ---- /dev/null -+++ b/drivers/staging/fsl-mc/include/dpbp.h -@@ -0,0 +1,438 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef __FSL_DPBP_H -+#define __FSL_DPBP_H -+ -+/* Data Path Buffer Pool API -+ * Contains initialization APIs and runtime control APIs for DPBP -+ */ -+ -+struct fsl_mc_io; -+ -+/** -+ * dpbp_open() - Open a control session for the specified object. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @dpbp_id: DPBP unique ID -+ * @token: Returned token; use in subsequent API calls -+ * -+ * This function can be used to open a control session for an -+ * already created object; an object may have been declared in -+ * the DPL or by calling the dpbp_create function. -+ * This function returns a unique authentication token, -+ * associated with the specific object ID and the specific MC -+ * portal; this token must be used in all subsequent commands for -+ * this specific object -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_open(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ int dpbp_id, -+ uint16_t *token); -+ -+/** -+ * dpbp_close() - Close the control session of the object -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPBP object -+ * -+ * After this function is called, no further operations are -+ * allowed on the object without opening a new control session. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_close(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * struct dpbp_cfg - Structure representing DPBP configuration -+ * @options: place holder -+ */ -+struct dpbp_cfg { -+ uint32_t options; -+}; -+ -+/** -+ * dpbp_create() - Create the DPBP object. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @cfg: Configuration structure -+ * @token: Returned token; use in subsequent API calls -+ * -+ * Create the DPBP object, allocate required resources and -+ * perform required initialization. -+ * -+ * The object can be created either by declaring it in the -+ * DPL file, or by calling this function. -+ * This function returns a unique authentication token, -+ * associated with the specific object ID and the specific MC -+ * portal; this token must be used in all subsequent calls to -+ * this specific object. For objects that are created using the -+ * DPL file, call dpbp_open function to get an authentication -+ * token first. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_create(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ const struct dpbp_cfg *cfg, -+ uint16_t *token); -+ -+/** -+ * dpbp_destroy() - Destroy the DPBP object and release all its resources. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPBP object -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpbp_destroy(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * dpbp_enable() - Enable the DPBP. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPBP object -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * dpbp_disable() - Disable the DPBP. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPBP object -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_disable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * dpbp_is_enabled() - Check if the DPBP is enabled. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPBP object -+ * @en: Returns '1' if object is enabled; '0' otherwise -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_is_enabled(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *en); -+ -+/** -+ * dpbp_reset() - Reset the DPBP, returns the object to initial state. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPBP object -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_reset(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * struct dpbp_irq_cfg - IRQ configuration -+ * @addr: Address that must be written to signal a message-based interrupt -+ * @val: Value to write into irq_addr address -+ * @irq_num: A user defined number associated with this IRQ -+ */ -+struct dpbp_irq_cfg { -+ uint64_t addr; -+ uint32_t val; -+ int irq_num; -+}; -+ -+/** -+ * dpbp_set_irq() - Set IRQ information for the DPBP to trigger an interrupt. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPBP object -+ * @irq_index: Identifies the interrupt index to configure -+ * @irq_cfg: IRQ configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_set_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ struct dpbp_irq_cfg *irq_cfg); -+ -+/** -+ * dpbp_get_irq() - Get IRQ information from the DPBP. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPBP object -+ * @irq_index: The interrupt index to configure -+ * @type: Interrupt type: 0 represents message interrupt -+ * type (both irq_addr and irq_val are valid) -+ * @irq_cfg: IRQ attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_get_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ int *type, -+ struct dpbp_irq_cfg *irq_cfg); -+ -+/** -+ * dpbp_set_irq_enable() - Set overall interrupt state. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPBP object -+ * @irq_index: The interrupt index to configure -+ * @en: Interrupt state - enable = 1, disable = 0 -+ * -+ * Allows GPP software to control when interrupts are generated. -+ * Each interrupt can have up to 32 causes. The enable/disable control's the -+ * overall interrupt state. if the interrupt is disabled no causes will cause -+ * an interrupt. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_set_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t en); -+ -+/** -+ * dpbp_get_irq_enable() - Get overall interrupt state -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPBP object -+ * @irq_index: The interrupt index to configure -+ * @en: Returned interrupt state - enable = 1, disable = 0 -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_get_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t *en); -+ -+/** -+ * dpbp_set_irq_mask() - Set interrupt mask. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPBP object -+ * @irq_index: The interrupt index to configure -+ * @mask: Event mask to trigger interrupt; -+ * each bit: -+ * 0 = ignore event -+ * 1 = consider event for asserting IRQ -+ * -+ * Every interrupt can have up to 32 causes and the interrupt model supports -+ * masking/unmasking each cause independently -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_set_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t mask); -+ -+/** -+ * dpbp_get_irq_mask() - Get interrupt mask. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPBP object -+ * @irq_index: The interrupt index to configure -+ * @mask: Returned event mask to trigger interrupt -+ * -+ * Every interrupt can have up to 32 causes and the interrupt model supports -+ * masking/unmasking each cause independently -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_get_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *mask); -+ -+/** -+ * dpbp_get_irq_status() - Get the current status of any pending interrupts. -+ * -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPBP object -+ * @irq_index: The interrupt index to configure -+ * @status: Returned interrupts status - one bit per cause: -+ * 0 = no interrupt pending -+ * 1 = interrupt pending -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_get_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *status); -+ -+/** -+ * dpbp_clear_irq_status() - Clear a pending interrupt's status -+ * -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPBP object -+ * @irq_index: The interrupt index to configure -+ * @status: Bits to clear (W1C) - one bit per cause: -+ * 0 = don't change -+ * 1 = clear status bit -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_clear_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t status); -+ -+/** -+ * struct dpbp_attr - Structure representing DPBP attributes -+ * @id: DPBP object ID -+ * @version: DPBP version -+ * @bpid: Hardware buffer pool ID; should be used as an argument in -+ * acquire/release operations on buffers -+ */ -+struct dpbp_attr { -+ int id; -+ /** -+ * struct version - Structure representing DPBP version -+ * @major: DPBP major version -+ * @minor: DPBP minor version -+ */ -+ struct { -+ uint16_t major; -+ uint16_t minor; -+ } version; -+ uint16_t bpid; -+}; -+ -+/** -+ * dpbp_get_attributes - Retrieve DPBP attributes. -+ * -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPBP object -+ * @attr: Returned object's attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_get_attributes(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpbp_attr *attr); -+ -+/** -+ * DPBP notifications options -+ */ -+ -+/** -+ * BPSCN write will attempt to allocate into a cache (coherent write) -+ */ -+#define DPBP_NOTIF_OPT_COHERENT_WRITE 0x00000001 -+ -+/** -+ * struct dpbp_notification_cfg - Structure representing DPBP notifications -+ * towards software -+ * @depletion_entry: below this threshold the pool is "depleted"; -+ * set it to '0' to disable it -+ * @depletion_exit: greater than or equal to this threshold the pool exit its -+ * "depleted" state -+ * @surplus_entry: above this threshold the pool is in "surplus" state; -+ * set it to '0' to disable it -+ * @surplus_exit: less than or equal to this threshold the pool exit its -+ * "surplus" state -+ * @message_iova: MUST be given if either 'depletion_entry' or 'surplus_entry' -+ * is not '0' (enable); I/O virtual address (must be in DMA-able memory), -+ * must be 16B aligned. -+ * @message_ctx: The context that will be part of the BPSCN message and will -+ * be written to 'message_iova' -+ * @options: Mask of available options; use 'DPBP_NOTIF_OPT_' values -+ */ -+struct dpbp_notification_cfg { -+ uint32_t depletion_entry; -+ uint32_t depletion_exit; -+ uint32_t surplus_entry; -+ uint32_t surplus_exit; -+ uint64_t message_iova; -+ uint64_t message_ctx; -+ uint16_t options; -+}; -+ -+/** -+ * dpbp_set_notifications() - Set notifications towards software -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPBP object -+ * @cfg: notifications configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_set_notifications(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpbp_notification_cfg *cfg); -+ -+/** -+ * dpbp_get_notifications() - Get the notifications configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPBP object -+ * @cfg: notifications configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_get_notifications(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpbp_notification_cfg *cfg); -+ -+#endif /* __FSL_DPBP_H */ -diff --git a/drivers/staging/fsl-mc/include/dpcon-cmd.h b/drivers/staging/fsl-mc/include/dpcon-cmd.h -new file mode 100644 -index 0000000..ecb40d0 ---- /dev/null -+++ b/drivers/staging/fsl-mc/include/dpcon-cmd.h -@@ -0,0 +1,162 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef _FSL_DPCON_CMD_H -+#define _FSL_DPCON_CMD_H -+ -+/* DPCON Version */ -+#define DPCON_VER_MAJOR 2 -+#define DPCON_VER_MINOR 2 -+ -+/* Command IDs */ -+#define DPCON_CMDID_CLOSE 0x800 -+#define DPCON_CMDID_OPEN 0x808 -+#define DPCON_CMDID_CREATE 0x908 -+#define DPCON_CMDID_DESTROY 0x900 -+ -+#define DPCON_CMDID_ENABLE 0x002 -+#define DPCON_CMDID_DISABLE 0x003 -+#define DPCON_CMDID_GET_ATTR 0x004 -+#define DPCON_CMDID_RESET 0x005 -+#define DPCON_CMDID_IS_ENABLED 0x006 -+ -+#define DPCON_CMDID_SET_IRQ 0x010 -+#define DPCON_CMDID_GET_IRQ 0x011 -+#define DPCON_CMDID_SET_IRQ_ENABLE 0x012 -+#define DPCON_CMDID_GET_IRQ_ENABLE 0x013 -+#define DPCON_CMDID_SET_IRQ_MASK 0x014 -+#define DPCON_CMDID_GET_IRQ_MASK 0x015 -+#define DPCON_CMDID_GET_IRQ_STATUS 0x016 -+#define DPCON_CMDID_CLEAR_IRQ_STATUS 0x017 -+ -+#define DPCON_CMDID_SET_NOTIFICATION 0x100 -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPCON_CMD_OPEN(cmd, dpcon_id) \ -+ MC_CMD_OP(cmd, 0, 0, 32, int, dpcon_id) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPCON_CMD_CREATE(cmd, cfg) \ -+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, cfg->num_priorities) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPCON_RSP_IS_ENABLED(cmd, en) \ -+ MC_RSP_OP(cmd, 0, 0, 1, int, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPCON_CMD_SET_IRQ(cmd, irq_index, irq_cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, irq_index);\ -+ MC_CMD_OP(cmd, 0, 32, 32, uint32_t, irq_cfg->val);\ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, irq_cfg->addr);\ -+ MC_CMD_OP(cmd, 2, 0, 32, int, irq_cfg->irq_num); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPCON_CMD_GET_IRQ(cmd, irq_index) \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPCON_RSP_GET_IRQ(cmd, type, irq_cfg) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, irq_cfg->val);\ -+ MC_RSP_OP(cmd, 1, 0, 64, uint64_t, irq_cfg->addr);\ -+ MC_RSP_OP(cmd, 2, 0, 32, int, irq_cfg->irq_num); \ -+ MC_RSP_OP(cmd, 2, 32, 32, int, type);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPCON_CMD_SET_IRQ_ENABLE(cmd, irq_index, en) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, en); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPCON_CMD_GET_IRQ_ENABLE(cmd, irq_index) \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPCON_RSP_GET_IRQ_ENABLE(cmd, en) \ -+ MC_RSP_OP(cmd, 0, 0, 8, uint8_t, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPCON_CMD_SET_IRQ_MASK(cmd, irq_index, mask) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, mask); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPCON_CMD_GET_IRQ_MASK(cmd, irq_index) \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPCON_RSP_GET_IRQ_MASK(cmd, mask) \ -+ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, mask) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPCON_CMD_GET_IRQ_STATUS(cmd, irq_index, status) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, status);\ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPCON_RSP_GET_IRQ_STATUS(cmd, status) \ -+ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, status) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPCON_CMD_CLEAR_IRQ_STATUS(cmd, irq_index, status) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, status); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPCON_RSP_GET_ATTR(cmd, attr) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 32, int, attr->id);\ -+ MC_RSP_OP(cmd, 0, 32, 16, uint16_t, attr->qbman_ch_id);\ -+ MC_RSP_OP(cmd, 0, 48, 8, uint8_t, attr->num_priorities);\ -+ MC_RSP_OP(cmd, 1, 0, 16, uint16_t, attr->version.major);\ -+ MC_RSP_OP(cmd, 1, 16, 16, uint16_t, attr->version.minor);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPCON_CMD_SET_NOTIFICATION(cmd, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, int, cfg->dpio_id);\ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, cfg->priority);\ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, cfg->user_ctx);\ -+} while (0) -+ -+#endif /* _FSL_DPCON_CMD_H */ -diff --git a/drivers/staging/fsl-mc/include/dpcon.h b/drivers/staging/fsl-mc/include/dpcon.h -new file mode 100644 -index 0000000..2555be5 ---- /dev/null -+++ b/drivers/staging/fsl-mc/include/dpcon.h -@@ -0,0 +1,407 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef __FSL_DPCON_H -+#define __FSL_DPCON_H -+ -+/* Data Path Concentrator API -+ * Contains initialization APIs and runtime control APIs for DPCON -+ */ -+ -+struct fsl_mc_io; -+ -+/** General DPCON macros */ -+ -+/** -+ * Use it to disable notifications; see dpcon_set_notification() -+ */ -+#define DPCON_INVALID_DPIO_ID (int)(-1) -+ -+/** -+ * dpcon_open() - Open a control session for the specified object -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @dpcon_id: DPCON unique ID -+ * @token: Returned token; use in subsequent API calls -+ * -+ * This function can be used to open a control session for an -+ * already created object; an object may have been declared in -+ * the DPL or by calling the dpcon_create() function. -+ * This function returns a unique authentication token, -+ * associated with the specific object ID and the specific MC -+ * portal; this token must be used in all subsequent commands for -+ * this specific object. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpcon_open(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ int dpcon_id, -+ uint16_t *token); -+ -+/** -+ * dpcon_close() - Close the control session of the object -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPCON object -+ * -+ * After this function is called, no further operations are -+ * allowed on the object without opening a new control session. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpcon_close(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * struct dpcon_cfg - Structure representing DPCON configuration -+ * @num_priorities: Number of priorities for the DPCON channel (1-8) -+ */ -+struct dpcon_cfg { -+ uint8_t num_priorities; -+}; -+ -+/** -+ * dpcon_create() - Create the DPCON object. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @cfg: Configuration structure -+ * @token: Returned token; use in subsequent API calls -+ * -+ * Create the DPCON object, allocate required resources and -+ * perform required initialization. -+ * -+ * The object can be created either by declaring it in the -+ * DPL file, or by calling this function. -+ * -+ * This function returns a unique authentication token, -+ * associated with the specific object ID and the specific MC -+ * portal; this token must be used in all subsequent calls to -+ * this specific object. For objects that are created using the -+ * DPL file, call dpcon_open() function to get an authentication -+ * token first. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpcon_create(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ const struct dpcon_cfg *cfg, -+ uint16_t *token); -+ -+/** -+ * dpcon_destroy() - Destroy the DPCON object and release all its resources. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPCON object -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpcon_destroy(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * dpcon_enable() - Enable the DPCON -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPCON object -+ * -+ * Return: '0' on Success; Error code otherwise -+ */ -+int dpcon_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * dpcon_disable() - Disable the DPCON -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPCON object -+ * -+ * Return: '0' on Success; Error code otherwise -+ */ -+int dpcon_disable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * dpcon_is_enabled() - Check if the DPCON is enabled. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPCON object -+ * @en: Returns '1' if object is enabled; '0' otherwise -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpcon_is_enabled(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *en); -+ -+/** -+ * dpcon_reset() - Reset the DPCON, returns the object to initial state. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPCON object -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpcon_reset(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * struct dpcon_irq_cfg - IRQ configuration -+ * @addr: Address that must be written to signal a message-based interrupt -+ * @val: Value to write into irq_addr address -+ * @irq_num: A user defined number associated with this IRQ -+ */ -+struct dpcon_irq_cfg { -+ uint64_t addr; -+ uint32_t val; -+ int irq_num; -+}; -+ -+/** -+ * dpcon_set_irq() - Set IRQ information for the DPCON to trigger an interrupt. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPCON object -+ * @irq_index: Identifies the interrupt index to configure -+ * @irq_cfg: IRQ configuration -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpcon_set_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ struct dpcon_irq_cfg *irq_cfg); -+ -+/** -+ * dpcon_get_irq() - Get IRQ information from the DPCON. -+ * -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPCON object -+ * @irq_index: The interrupt index to configure -+ * @type: Interrupt type: 0 represents message interrupt -+ * type (both irq_addr and irq_val are valid) -+ * @irq_cfg: IRQ attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpcon_get_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ int *type, -+ struct dpcon_irq_cfg *irq_cfg); -+ -+/** -+ * dpcon_set_irq_enable() - Set overall interrupt state. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPCON object -+ * @irq_index: The interrupt index to configure -+ * @en: Interrupt state - enable = 1, disable = 0 -+ * -+ * Allows GPP software to control when interrupts are generated. -+ * Each interrupt can have up to 32 causes. The enable/disable control's the -+ * overall interrupt state. if the interrupt is disabled no causes will cause -+ * an interrupt. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpcon_set_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t en); -+ -+/** -+ * dpcon_get_irq_enable() - Get overall interrupt state. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPCON object -+ * @irq_index: The interrupt index to configure -+ * @en: Returned interrupt state - enable = 1, disable = 0 -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpcon_get_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t *en); -+ -+/** -+ * dpcon_set_irq_mask() - Set interrupt mask. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPCON object -+ * @irq_index: The interrupt index to configure -+ * @mask: Event mask to trigger interrupt; -+ * each bit: -+ * 0 = ignore event -+ * 1 = consider event for asserting IRQ -+ * -+ * Every interrupt can have up to 32 causes and the interrupt model supports -+ * masking/unmasking each cause independently -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpcon_set_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t mask); -+ -+/** -+ * dpcon_get_irq_mask() - Get interrupt mask. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPCON object -+ * @irq_index: The interrupt index to configure -+ * @mask: Returned event mask to trigger interrupt -+ * -+ * Every interrupt can have up to 32 causes and the interrupt model supports -+ * masking/unmasking each cause independently -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpcon_get_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *mask); -+ -+/** -+ * dpcon_get_irq_status() - Get the current status of any pending interrupts. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPCON object -+ * @irq_index: The interrupt index to configure -+ * @status: interrupts status - one bit per cause: -+ * 0 = no interrupt pending -+ * 1 = interrupt pending -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpcon_get_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *status); -+ -+/** -+ * dpcon_clear_irq_status() - Clear a pending interrupt's status -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPCON object -+ * @irq_index: The interrupt index to configure -+ * @status: bits to clear (W1C) - one bit per cause: -+ * 0 = don't change -+ * 1 = clear status bit -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpcon_clear_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t status); -+ -+/** -+ * struct dpcon_attr - Structure representing DPCON attributes -+ * @id: DPCON object ID -+ * @version: DPCON version -+ * @qbman_ch_id: Channel ID to be used by dequeue operation -+ * @num_priorities: Number of priorities for the DPCON channel (1-8) -+ */ -+struct dpcon_attr { -+ int id; -+ /** -+ * struct version - DPCON version -+ * @major: DPCON major version -+ * @minor: DPCON minor version -+ */ -+ struct { -+ uint16_t major; -+ uint16_t minor; -+ } version; -+ uint16_t qbman_ch_id; -+ uint8_t num_priorities; -+}; -+ -+/** -+ * dpcon_get_attributes() - Retrieve DPCON attributes. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPCON object -+ * @attr: Object's attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpcon_get_attributes(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpcon_attr *attr); -+ -+/** -+ * struct dpcon_notification_cfg - Structure representing notification parameters -+ * @dpio_id: DPIO object ID; must be configured with a notification channel; -+ * to disable notifications set it to 'DPCON_INVALID_DPIO_ID'; -+ * @priority: Priority selection within the DPIO channel; valid values -+ * are 0-7, depending on the number of priorities in that channel -+ * @user_ctx: User context value provided with each CDAN message -+ */ -+struct dpcon_notification_cfg { -+ int dpio_id; -+ uint8_t priority; -+ uint64_t user_ctx; -+}; -+ -+/** -+ * dpcon_set_notification() - Set DPCON notification destination -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPCON object -+ * @cfg: Notification parameters -+ * -+ * Return: '0' on Success; Error code otherwise -+ */ -+int dpcon_set_notification(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpcon_notification_cfg *cfg); -+ -+#endif /* __FSL_DPCON_H */ -diff --git a/drivers/staging/fsl-mc/include/dpmac-cmd.h b/drivers/staging/fsl-mc/include/dpmac-cmd.h -new file mode 100644 -index 0000000..c123aab ---- /dev/null -+++ b/drivers/staging/fsl-mc/include/dpmac-cmd.h -@@ -0,0 +1,192 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef _FSL_DPMAC_CMD_H -+#define _FSL_DPMAC_CMD_H -+ -+/* DPMAC Version */ -+#define DPMAC_VER_MAJOR 3 -+#define DPMAC_VER_MINOR 0 -+ -+/* Command IDs */ -+#define DPMAC_CMDID_CLOSE 0x800 -+#define DPMAC_CMDID_OPEN 0x80c -+#define DPMAC_CMDID_CREATE 0x90c -+#define DPMAC_CMDID_DESTROY 0x900 -+ -+#define DPMAC_CMDID_GET_ATTR 0x004 -+#define DPMAC_CMDID_RESET 0x005 -+ -+#define DPMAC_CMDID_SET_IRQ 0x010 -+#define DPMAC_CMDID_GET_IRQ 0x011 -+#define DPMAC_CMDID_SET_IRQ_ENABLE 0x012 -+#define DPMAC_CMDID_GET_IRQ_ENABLE 0x013 -+#define DPMAC_CMDID_SET_IRQ_MASK 0x014 -+#define DPMAC_CMDID_GET_IRQ_MASK 0x015 -+#define DPMAC_CMDID_GET_IRQ_STATUS 0x016 -+#define DPMAC_CMDID_CLEAR_IRQ_STATUS 0x017 -+ -+#define DPMAC_CMDID_MDIO_READ 0x0c0 -+#define DPMAC_CMDID_MDIO_WRITE 0x0c1 -+#define DPMAC_CMDID_GET_LINK_CFG 0x0c2 -+#define DPMAC_CMDID_SET_LINK_STATE 0x0c3 -+#define DPMAC_CMDID_GET_COUNTER 0x0c4 -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_CREATE(cmd, cfg) \ -+ MC_CMD_OP(cmd, 0, 0, 32, int, cfg->mac_id) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_OPEN(cmd, dpmac_id) \ -+ MC_CMD_OP(cmd, 0, 0, 32, int, dpmac_id) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_SET_IRQ(cmd, irq_index, irq_addr, irq_val, user_irq_id) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, irq_index);\ -+ MC_CMD_OP(cmd, 0, 32, 32, uint32_t, irq_val);\ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, irq_addr); \ -+ MC_CMD_OP(cmd, 2, 0, 32, int, user_irq_id); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_GET_IRQ(cmd, irq_index) \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_RSP_GET_IRQ(cmd, type, irq_addr, irq_val, user_irq_id) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, irq_val); \ -+ MC_RSP_OP(cmd, 1, 0, 64, uint64_t, irq_addr); \ -+ MC_RSP_OP(cmd, 2, 0, 32, int, user_irq_id); \ -+ MC_RSP_OP(cmd, 2, 32, 32, int, type); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_SET_IRQ_ENABLE(cmd, irq_index, en) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, en); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_GET_IRQ_ENABLE(cmd, irq_index) \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_RSP_GET_IRQ_ENABLE(cmd, en) \ -+ MC_RSP_OP(cmd, 0, 0, 8, uint8_t, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_SET_IRQ_MASK(cmd, irq_index, mask) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, mask);\ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_GET_IRQ_MASK(cmd, irq_index) \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_RSP_GET_IRQ_MASK(cmd, mask) \ -+ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, mask) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_GET_IRQ_STATUS(cmd, irq_index) \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_RSP_GET_IRQ_STATUS(cmd, status) \ -+ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, status) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_CLEAR_IRQ_STATUS(cmd, irq_index, status) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, status); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_RSP_GET_ATTRIBUTES(cmd, attr) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 32, int, attr->phy_id);\ -+ MC_RSP_OP(cmd, 0, 32, 32, int, attr->id);\ -+ MC_RSP_OP(cmd, 1, 0, 16, uint16_t, attr->version.major);\ -+ MC_RSP_OP(cmd, 1, 16, 16, uint16_t, attr->version.minor);\ -+ MC_RSP_OP(cmd, 1, 32, 8, enum dpmac_link_type, attr->link_type);\ -+ MC_RSP_OP(cmd, 1, 40, 8, enum dpmac_eth_if, attr->eth_if);\ -+ MC_RSP_OP(cmd, 2, 0, 32, uint32_t, attr->max_rate);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_MDIO_READ(cmd, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, cfg->phy_addr); \ -+ MC_CMD_OP(cmd, 0, 8, 8, uint8_t, cfg->reg); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_RSP_MDIO_READ(cmd, data) \ -+ MC_RSP_OP(cmd, 0, 16, 16, uint16_t, data) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_MDIO_WRITE(cmd, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, cfg->phy_addr); \ -+ MC_CMD_OP(cmd, 0, 8, 8, uint8_t, cfg->reg); \ -+ MC_CMD_OP(cmd, 0, 16, 16, uint16_t, cfg->data); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_RSP_GET_LINK_CFG(cmd, cfg) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 64, uint64_t, cfg->options); \ -+ MC_RSP_OP(cmd, 1, 0, 32, uint32_t, cfg->rate); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_SET_LINK_STATE(cmd, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 64, uint64_t, cfg->options); \ -+ MC_CMD_OP(cmd, 1, 0, 32, uint32_t, cfg->rate); \ -+ MC_CMD_OP(cmd, 2, 0, 1, int, cfg->up); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_GET_COUNTER(cmd, type) \ -+ MC_CMD_OP(cmd, 0, 0, 8, enum dpmac_counter, type) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_RSP_GET_COUNTER(cmd, counter) \ -+ MC_RSP_OP(cmd, 1, 0, 64, uint64_t, counter) -+ -+#endif /* _FSL_DPMAC_CMD_H */ -diff --git a/drivers/staging/fsl-mc/include/dpmac.h b/drivers/staging/fsl-mc/include/dpmac.h -new file mode 100644 -index 0000000..88091b5 ---- /dev/null -+++ b/drivers/staging/fsl-mc/include/dpmac.h -@@ -0,0 +1,528 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef __FSL_DPMAC_H -+#define __FSL_DPMAC_H -+ -+/* Data Path MAC API -+ * Contains initialization APIs and runtime control APIs for DPMAC -+ */ -+ -+struct fsl_mc_io; -+ -+/** -+ * dpmac_open() - Open a control session for the specified object. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @dpmac_id: DPMAC unique ID -+ * @token: Returned token; use in subsequent API calls -+ * -+ * This function can be used to open a control session for an -+ * already created object; an object may have been declared in -+ * the DPL or by calling the dpmac_create function. -+ * This function returns a unique authentication token, -+ * associated with the specific object ID and the specific MC -+ * portal; this token must be used in all subsequent commands for -+ * this specific object -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_open(struct fsl_mc_io *mc_io, int dpmac_id, uint16_t *token); -+ -+/** -+ * dpmac_close() - Close the control session of the object -+ * @mc_io: Pointer to MC portal's I/O object -+ * @token: Token of DPMAC object -+ * -+ * After this function is called, no further operations are -+ * allowed on the object without opening a new control session. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_close(struct fsl_mc_io *mc_io, uint16_t token); -+ -+/** -+ * enum dpmac_link_type - DPMAC link type -+ * @DPMAC_LINK_TYPE_NONE: No link -+ * @DPMAC_LINK_TYPE_FIXED: Link is fixed type -+ * @DPMAC_LINK_TYPE_PHY: Link by PHY ID -+ * @DPMAC_LINK_TYPE_BACKPLANE: Backplane link type -+ */ -+enum dpmac_link_type { -+ DPMAC_LINK_TYPE_NONE, -+ DPMAC_LINK_TYPE_FIXED, -+ DPMAC_LINK_TYPE_PHY, -+ DPMAC_LINK_TYPE_BACKPLANE -+}; -+ -+/** -+ * enum dpmac_eth_if - DPMAC Ethrnet interface -+ * @DPMAC_ETH_IF_MII: MII interface -+ * @DPMAC_ETH_IF_RMII: RMII interface -+ * @DPMAC_ETH_IF_SMII: SMII interface -+ * @DPMAC_ETH_IF_GMII: GMII interface -+ * @DPMAC_ETH_IF_RGMII: RGMII interface -+ * @DPMAC_ETH_IF_SGMII: SGMII interface -+ * @DPMAC_ETH_IF_XGMII: XGMII interface -+ * @DPMAC_ETH_IF_QSGMII: QSGMII interface -+ * @DPMAC_ETH_IF_XAUI: XAUI interface -+ * @DPMAC_ETH_IF_XFI: XFI interface -+ */ -+enum dpmac_eth_if { -+ DPMAC_ETH_IF_MII, -+ DPMAC_ETH_IF_RMII, -+ DPMAC_ETH_IF_SMII, -+ DPMAC_ETH_IF_GMII, -+ DPMAC_ETH_IF_RGMII, -+ DPMAC_ETH_IF_SGMII, -+ DPMAC_ETH_IF_XGMII, -+ DPMAC_ETH_IF_QSGMII, -+ DPMAC_ETH_IF_XAUI, -+ DPMAC_ETH_IF_XFI -+}; -+ -+/** -+ * struct dpmac_cfg() - Structure representing DPMAC configuration -+ * @mac_id: Represents the Hardware MAC ID; in case of multiple WRIOP, -+ * the MAC IDs are continuous. -+ * For example: 2 WRIOPs, 16 MACs in each: -+ * MAC IDs for the 1st WRIOP: 1-16, -+ * MAC IDs for the 2nd WRIOP: 17-32. -+ */ -+struct dpmac_cfg { -+ int mac_id; -+}; -+ -+/** -+ * dpmac_create() - Create the DPMAC object. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cfg: Configuration structure -+ * @token: Returned token; use in subsequent API calls -+ * -+ * Create the DPMAC object, allocate required resources and -+ * perform required initialization. -+ * -+ * The object can be created either by declaring it in the -+ * DPL file, or by calling this function. -+ * This function returns a unique authentication token, -+ * associated with the specific object ID and the specific MC -+ * portal; this token must be used in all subsequent calls to -+ * this specific object. For objects that are created using the -+ * DPL file, call dpmac_open function to get an authentication -+ * token first. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_create(struct fsl_mc_io *mc_io, -+ const struct dpmac_cfg *cfg, -+ uint16_t *token); -+ -+/** -+ * dpmac_destroy() - Destroy the DPMAC object and release all its resources. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @token: Token of DPMAC object -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpmac_destroy(struct fsl_mc_io *mc_io, uint16_t token); -+ -+/* DPMAC IRQ Index and Events */ -+ -+/* IRQ index */ -+#define DPMAC_IRQ_INDEX 0 -+/* IRQ event - indicates a change in link state */ -+#define DPMAC_IRQ_EVENT_LINK_CFG_REQ 0x00000001 -+/* irq event - Indicates that the link state changed */ -+#define DPMAC_IRQ_EVENT_LINK_CHANGED 0x00000002 -+ -+/** -+ * dpmac_set_irq() - Set IRQ information for the DPMAC to trigger an interrupt. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @token: Token of DPMAC object -+ * @irq_index: Identifies the interrupt index to configure -+ * @irq_addr: Address that must be written to -+ * signal a message-based interrupt -+ * @irq_val: Value to write into irq_addr address -+ * @user_irq_id: A user defined number associated with this IRQ -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_set_irq(struct fsl_mc_io *mc_io, -+ uint16_t token, -+ uint8_t irq_index, -+ uint64_t irq_addr, -+ uint32_t irq_val, -+ int user_irq_id); -+ -+/** -+ * dpmac_get_irq() - Get IRQ information from the DPMAC. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @token: Token of DPMAC object -+ * @irq_index: The interrupt index to configure -+ * @type: Interrupt type: 0 represents message interrupt -+ * type (both irq_addr and irq_val are valid) -+ * @irq_addr: Returned address that must be written to -+ * signal the message-based interrupt -+ * @irq_val: Value to write into irq_addr address -+ * @user_irq_id: A user defined number associated with this IRQ -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_get_irq(struct fsl_mc_io *mc_io, -+ uint16_t token, -+ uint8_t irq_index, -+ int *type, -+ uint64_t *irq_addr, -+ uint32_t *irq_val, -+ int *user_irq_id); -+ -+/** -+ * dpmac_set_irq_enable() - Set overall interrupt state. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @token: Token of DPMAC object -+ * @irq_index: The interrupt index to configure -+ * @en: Interrupt state - enable = 1, disable = 0 -+ * -+ * Allows GPP software to control when interrupts are generated. -+ * Each interrupt can have up to 32 causes. The enable/disable control's the -+ * overall interrupt state. if the interrupt is disabled no causes will cause -+ * an interrupt. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_set_irq_enable(struct fsl_mc_io *mc_io, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t en); -+ -+/** -+ * dpmac_get_irq_enable() - Get overall interrupt state -+ * @mc_io: Pointer to MC portal's I/O object -+ * @token: Token of DPMAC object -+ * @irq_index: The interrupt index to configure -+ * @en: Returned interrupt state - enable = 1, disable = 0 -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_get_irq_enable(struct fsl_mc_io *mc_io, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t *en); -+ -+/** -+ * dpmac_set_irq_mask() - Set interrupt mask. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @token: Token of DPMAC object -+ * @irq_index: The interrupt index to configure -+ * @mask: Event mask to trigger interrupt; -+ * each bit: -+ * 0 = ignore event -+ * 1 = consider event for asserting IRQ -+ * -+ * Every interrupt can have up to 32 causes and the interrupt model supports -+ * masking/unmasking each cause independently -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_set_irq_mask(struct fsl_mc_io *mc_io, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t mask); -+ -+/** -+ * dpmac_get_irq_mask() - Get interrupt mask. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @token: Token of DPMAC object -+ * @irq_index: The interrupt index to configure -+ * @mask: Returned event mask to trigger interrupt -+ * -+ * Every interrupt can have up to 32 causes and the interrupt model supports -+ * masking/unmasking each cause independently -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_get_irq_mask(struct fsl_mc_io *mc_io, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *mask); -+ -+/** -+ * dpmac_get_irq_status() - Get the current status of any pending interrupts. -+ * -+ * @mc_io: Pointer to MC portal's I/O object -+ * @token: Token of DPMAC object -+ * @irq_index: The interrupt index to configure -+ * @status: Returned interrupts status - one bit per cause: -+ * 0 = no interrupt pending -+ * 1 = interrupt pending -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_get_irq_status(struct fsl_mc_io *mc_io, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *status); -+ -+/** -+ * dpmac_clear_irq_status() - Clear a pending interrupt's status -+ * -+ * @mc_io: Pointer to MC portal's I/O object -+ * @token: Token of DPMAC object -+ * @irq_index: The interrupt index to configure -+ * @status: Bits to clear (W1C) - one bit per cause: -+ * 0 = don't change -+ * 1 = clear status bit -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_clear_irq_status(struct fsl_mc_io *mc_io, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t status); -+ -+/** -+ * struct dpmac_attr - Structure representing DPMAC attributes -+ * @id: DPMAC object ID -+ * @phy_id: PHY ID -+ * @link_type: link type -+ * @eth_if: Ethernet interface -+ * @max_rate: Maximum supported rate - in Mbps -+ * @version: DPMAC version -+ */ -+struct dpmac_attr { -+ int id; -+ int phy_id; -+ enum dpmac_link_type link_type; -+ enum dpmac_eth_if eth_if; -+ uint32_t max_rate; -+ /** -+ * struct version - Structure representing DPMAC version -+ * @major: DPMAC major version -+ * @minor: DPMAC minor version -+ */ -+ struct { -+ uint16_t major; -+ uint16_t minor; -+ } version; -+}; -+ -+/** -+ * dpmac_get_attributes - Retrieve DPMAC attributes. -+ * -+ * @mc_io: Pointer to MC portal's I/O object -+ * @token: Token of DPMAC object -+ * @attr: Returned object's attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_get_attributes(struct fsl_mc_io *mc_io, -+ uint16_t token, -+ struct dpmac_attr *attr); -+ -+/** -+ * struct dpmac_mdio_cfg - DPMAC MDIO read/write parameters -+ * @phy_addr: MDIO device address -+ * @reg: Address of the register within the Clause 45 PHY device from which data -+ * is to be read -+ * @data: Data read/write from/to MDIO -+ */ -+struct dpmac_mdio_cfg { -+ uint8_t phy_addr; -+ uint8_t reg; -+ uint16_t data; -+}; -+ -+/** -+ * dpmac_mdio_read() - Perform MDIO read transaction -+ * @mc_io: Pointer to opaque I/O object -+ * @token: Token of DPMAC object -+ * @cfg: Structure with MDIO transaction parameters -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_mdio_read(struct fsl_mc_io *mc_io, uint16_t token, -+ struct dpmac_mdio_cfg *cfg); -+ -+ -+/** -+ * dpmac_mdio_write() - Perform MDIO write transaction -+ * @mc_io: Pointer to opaque I/O object -+ * @token: Token of DPMAC object -+ * @cfg: Structure with MDIO transaction parameters -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_mdio_write(struct fsl_mc_io *mc_io, uint16_t token, -+ struct dpmac_mdio_cfg *cfg); -+ -+/* DPMAC link configuration/state options */ -+ -+/* Enable auto-negotiation */ -+#define DPMAC_LINK_OPT_AUTONEG 0x0000000000000001ULL -+/* Enable half-duplex mode */ -+#define DPMAC_LINK_OPT_HALF_DUPLEX 0x0000000000000002ULL -+/* Enable pause frames */ -+#define DPMAC_LINK_OPT_PAUSE 0x0000000000000004ULL -+/* Enable a-symmetric pause frames */ -+#define DPMAC_LINK_OPT_ASYM_PAUSE 0x0000000000000008ULL -+ -+/** -+ * struct dpmac_link_cfg - Structure representing DPMAC link configuration -+ * @rate: Link's rate - in Mbps -+ * @options: Enable/Disable DPMAC link cfg features (bitmap) -+ */ -+struct dpmac_link_cfg { -+ uint32_t rate; -+ uint64_t options; -+}; -+ -+/** -+ * dpmac_get_link_cfg() - Get Ethernet link configuration -+ * @mc_io: Pointer to opaque I/O object -+ * @token: Token of DPMAC object -+ * @cfg: Returned structure with the link configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_get_link_cfg(struct fsl_mc_io *mc_io, uint16_t token, -+ struct dpmac_link_cfg *cfg); -+ -+/** -+ * struct dpmac_link_state - DPMAC link configuration request -+ * @rate: Rate in Mbps -+ * @options: Enable/Disable DPMAC link cfg features (bitmap) -+ * @up: Link state -+ */ -+struct dpmac_link_state { -+ uint32_t rate; -+ uint64_t options; -+ int up; -+}; -+ -+/** -+ * dpmac_set_link_state() - Set the Ethernet link status -+ * @mc_io: Pointer to opaque I/O object -+ * @token: Token of DPMAC object -+ * @link_state: Link state configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_set_link_state(struct fsl_mc_io *mc_io, uint16_t token, -+ struct dpmac_link_state *link_state); -+ -+/** -+ * enum dpni_counter - DPNI counter types -+ * @DPMAC_CNT_ING_FRAME_64: counts 64-octet frame, good or bad. -+ * @DPMAC_CNT_ING_FRAME_127: counts 65- to 127-octet frame, good or bad. -+ * @DPMAC_CNT_ING_FRAME_255: counts 128- to 255-octet frame, good or bad. -+ * @DPMAC_CNT_ING_FRAME_511: counts 256- to 511-octet frame, good or bad. -+ * @DPMAC_CNT_ING_FRAME_1023: counts 512- to 1023-octet frame, good or bad. -+ * @DPMAC_CNT_ING_FRAME_1518: counts 1024- to 1518-octet frame, good or bad. -+ * @DPMAC_CNT_ING_FRAME_1519_MAX: counts 1519-octet frame and larger -+ * (up to max frame length specified), -+ * good or bad. -+ * @DPMAC_CNT_ING_FRAG: counts packet which is shorter than 64 octets received -+ * with a wrong CRC -+ * @DPMAC_CNT_ING_JABBER: counts packet longer than the maximum frame length -+ * specified, with a bad frame check sequence. -+ * @DPMAC_CNT_ING_FRAME_DISCARD: counts dropped packet due to internal errors. -+ * Occurs when a receive FIFO overflows. -+ * Includes also packets truncated as a result of -+ * the receive FIFO overflow. -+ * @DPMAC_CNT_ING_ALIGN_ERR: counts frame with an alignment error -+ * (optional used for wrong SFD) -+ * @DPMAC_CNT_EGR_UNDERSIZED: counts packet transmitted that was less than 64 -+ * octets long with a good CRC. -+ * @DPMAC_CNT_ING_OVERSIZED: counts packet longer than the maximum frame length -+ * specified, with a good frame check sequence. -+ * @DPMAC_CNT_ING_VALID_PAUSE_FRAME: counts valid pause frame (regular and PFC). -+ * @DPMAC_CNT_EGR_VALID_PAUSE_FRAME: counts valid pause frame transmitted -+ * (regular and PFC). -+ * @DPMAC_CNT_ING_BYTE: counts octet received except preamble for all valid -+ frames and valid pause frames. -+ * @DPMAC_CNT_ING_MCAST_FRAME: counts received multicast frame -+ * @DPMAC_CNT_ING_BCAST_FRAME: counts received broadcast frame -+ * @DPMAC_CNT_ING_ALL_FRAME: counts each good or bad packet received. -+ * @DPMAC_CNT_ING_UCAST_FRAME: counts received unicast frame -+ * @DPMAC_CNT_ING_ERR_FRAME: counts frame received with an error -+ * (except for undersized/fragment frame) -+ * @DPMAC_CNT_EGR_BYTE: counts octet transmitted except preamble for all valid -+ * frames and valid pause frames transmitted. -+ * @DPMAC_CNT_EGR_MCAST_FRAME: counts transmitted multicast frame -+ * @DPMAC_CNT_EGR_BCAST_FRAME: counts transmitted broadcast frame -+ * @DPMAC_CNT_EGR_UCAST_FRAME: counts transmitted unicast frame -+ * @DPMAC_CNT_EGR_ERR_FRAME: counts frame transmitted with an error -+ * @DPMAC_CNT_ING_GOOD_FRAME: counts frame received without error, including -+ * pause frames. -+ */ -+enum dpmac_counter { -+ DPMAC_CNT_ING_FRAME_64, -+ DPMAC_CNT_ING_FRAME_127, -+ DPMAC_CNT_ING_FRAME_255, -+ DPMAC_CNT_ING_FRAME_511, -+ DPMAC_CNT_ING_FRAME_1023, -+ DPMAC_CNT_ING_FRAME_1518, -+ DPMAC_CNT_ING_FRAME_1519_MAX, -+ DPMAC_CNT_ING_FRAG, -+ DPMAC_CNT_ING_JABBER, -+ DPMAC_CNT_ING_FRAME_DISCARD, -+ DPMAC_CNT_ING_ALIGN_ERR, -+ DPMAC_CNT_EGR_UNDERSIZED, -+ DPMAC_CNT_ING_OVERSIZED, -+ DPMAC_CNT_ING_VALID_PAUSE_FRAME, -+ DPMAC_CNT_EGR_VALID_PAUSE_FRAME, -+ DPMAC_CNT_ING_BYTE, -+ DPMAC_CNT_ING_MCAST_FRAME, -+ DPMAC_CNT_ING_BCAST_FRAME, -+ DPMAC_CNT_ING_ALL_FRAME, -+ DPMAC_CNT_ING_UCAST_FRAME, -+ DPMAC_CNT_ING_ERR_FRAME, -+ DPMAC_CNT_EGR_BYTE, -+ DPMAC_CNT_EGR_MCAST_FRAME, -+ DPMAC_CNT_EGR_BCAST_FRAME, -+ DPMAC_CNT_EGR_UCAST_FRAME, -+ DPMAC_CNT_EGR_ERR_FRAME, -+ DPMAC_CNT_ING_GOOD_FRAME -+}; -+ -+/** -+ * dpmac_get_counter() - Read a specific DPMAC counter -+ * @mc_io: Pointer to opaque I/O object -+ * @token: Token of DPMAC object -+ * @type: The requested counter -+ * @counter: Returned counter value -+ * -+ * Return: The requested counter; '0' otherwise. -+ */ -+int dpmac_get_counter(struct fsl_mc_io *mc_io, uint16_t token, -+ enum dpmac_counter type, -+ uint64_t *counter); -+ -+#endif /* __FSL_DPMAC_H */ -diff --git a/drivers/staging/fsl-mc/include/dpmng.h b/drivers/staging/fsl-mc/include/dpmng.h -new file mode 100644 -index 0000000..d1c4588 ---- /dev/null -+++ b/drivers/staging/fsl-mc/include/dpmng.h -@@ -0,0 +1,80 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef __FSL_DPMNG_H -+#define __FSL_DPMNG_H -+ -+/* Management Complex General API -+ * Contains general API for the Management Complex firmware -+ */ -+ -+struct fsl_mc_io; -+ -+/** -+ * struct mc_version -+ * @major: Major version number: incremented on API compatibility changes -+ * @minor: Minor version number: incremented on API additions (that are -+ * backward compatible); reset when major version is incremented -+ * @revision: Internal revision number: incremented on implementation changes -+ * and/or bug fixes that have no impact on API -+ */ -+struct mc_version { -+ uint32_t major; -+ uint32_t minor; -+ uint32_t revision; -+}; -+ -+/** -+ * mc_get_version() - Retrieves the Management Complex firmware -+ * version information -+ * @mc_io: Pointer to opaque I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @mc_ver_info: Returned version information structure -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int mc_get_version(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ struct mc_version *mc_ver_info); -+ -+/** -+ * dpmng_get_container_id() - Get container ID associated with a given portal. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @container_id: Requested container ID -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmng_get_container_id(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ int *container_id); -+ -+#endif /* __FSL_DPMNG_H */ -diff --git a/drivers/staging/fsl-mc/include/dprc.h b/drivers/staging/fsl-mc/include/dprc.h -new file mode 100644 -index 0000000..810ded0 ---- /dev/null -+++ b/drivers/staging/fsl-mc/include/dprc.h -@@ -0,0 +1,990 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef _FSL_DPRC_H -+#define _FSL_DPRC_H -+ -+#include "mc-cmd.h" -+ -+/* Data Path Resource Container API -+ * Contains DPRC API for managing and querying DPAA resources -+ */ -+ -+struct fsl_mc_io; -+ -+/** -+ * Set this value as the icid value in dprc_cfg structure when creating a -+ * container, in case the ICID is not selected by the user and should be -+ * allocated by the DPRC from the pool of ICIDs. -+ */ -+#define DPRC_GET_ICID_FROM_POOL (uint16_t)(~(0)) -+ -+/** -+ * Set this value as the portal_id value in dprc_cfg structure when creating a -+ * container, in case the portal ID is not specifically selected by the -+ * user and should be allocated by the DPRC from the pool of portal ids. -+ */ -+#define DPRC_GET_PORTAL_ID_FROM_POOL (int)(~(0)) -+ -+/** -+ * dprc_open() - Open DPRC object for use -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @container_id: Container ID to open -+ * @token: Returned token of DPRC object -+ * -+ * Return: '0' on Success; Error code otherwise. -+ * -+ * @warning Required before any operation on the object. -+ */ -+int dprc_open(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ int container_id, -+ uint16_t *token); -+ -+/** -+ * dprc_close() - Close the control session of the object -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * -+ * After this function is called, no further operations are -+ * allowed on the object without opening a new control session. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_close(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * Container general options -+ * -+ * These options may be selected at container creation by the container creator -+ * and can be retrieved using dprc_get_attributes() -+ */ -+ -+/* Spawn Policy Option allowed - Indicates that the new container is allowed -+ * to spawn and have its own child containers. -+ */ -+#define DPRC_CFG_OPT_SPAWN_ALLOWED 0x00000001 -+ -+/* General Container allocation policy - Indicates that the new container is -+ * allowed to allocate requested resources from its parent container; if not -+ * set, the container is only allowed to use resources in its own pools; Note -+ * that this is a container's global policy, but the parent container may -+ * override it and set specific quota per resource type. -+ */ -+#define DPRC_CFG_OPT_ALLOC_ALLOWED 0x00000002 -+ -+/* Object initialization allowed - software context associated with this -+ * container is allowed to invoke object initialization operations. -+ */ -+#define DPRC_CFG_OPT_OBJ_CREATE_ALLOWED 0x00000004 -+ -+/* Topology change allowed - software context associated with this -+ * container is allowed to invoke topology operations, such as attach/detach -+ * of network objects. -+ */ -+#define DPRC_CFG_OPT_TOPOLOGY_CHANGES_ALLOWED 0x00000008 -+ -+/* AIOP - Indicates that container belongs to AIOP. */ -+#define DPRC_CFG_OPT_AIOP 0x00000020 -+ -+/* IRQ Config - Indicates that the container allowed to configure its IRQs. */ -+#define DPRC_CFG_OPT_IRQ_CFG_ALLOWED 0x00000040 -+ -+/** -+ * struct dprc_cfg - Container configuration options -+ * @icid: Container's ICID; if set to 'DPRC_GET_ICID_FROM_POOL', a free -+ * ICID value is allocated by the DPRC -+ * @portal_id: Portal ID; if set to 'DPRC_GET_PORTAL_ID_FROM_POOL', a free -+ * portal ID is allocated by the DPRC -+ * @options: Combination of 'DPRC_CFG_OPT_' options -+ * @label: Object's label -+ */ -+struct dprc_cfg { -+ uint16_t icid; -+ int portal_id; -+ uint64_t options; -+ char label[16]; -+}; -+ -+/** -+ * dprc_create_container() - Create child container -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @cfg: Child container configuration -+ * @child_container_id: Returned child container ID -+ * @child_portal_offset: Returned child portal offset from MC portal base -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_create_container(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dprc_cfg *cfg, -+ int *child_container_id, -+ uint64_t *child_portal_offset); -+ -+/** -+ * dprc_destroy_container() - Destroy child container. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @child_container_id: ID of the container to destroy -+ * -+ * This function terminates the child container, so following this call the -+ * child container ID becomes invalid. -+ * -+ * Notes: -+ * - All resources and objects of the destroyed container are returned to the -+ * parent container or destroyed if were created be the destroyed container. -+ * - This function destroy all the child containers of the specified -+ * container prior to destroying the container itself. -+ * -+ * warning: Only the parent container is allowed to destroy a child policy -+ * Container 0 can't be destroyed -+ * -+ * Return: '0' on Success; Error code otherwise. -+ * -+ */ -+int dprc_destroy_container(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int child_container_id); -+ -+/** -+ * dprc_reset_container - Reset child container. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @child_container_id: ID of the container to reset -+ * -+ * In case a software context crashes or becomes non-responsive, the parent -+ * may wish to reset its resources container before the software context is -+ * restarted. -+ * -+ * This routine informs all objects assigned to the child container that the -+ * container is being reset, so they may perform any cleanup operations that are -+ * needed. All objects handles that were owned by the child container shall be -+ * closed. -+ * -+ * Note that such request may be submitted even if the child software context -+ * has not crashed, but the resulting object cleanup operations will not be -+ * aware of that. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_reset_container(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int child_container_id); -+ -+/* IRQ */ -+ -+/* IRQ index */ -+#define DPRC_IRQ_INDEX 0 -+ -+/* Number of dprc's IRQs */ -+#define DPRC_NUM_OF_IRQS 1 -+ -+/* DPRC IRQ events */ -+ -+/* IRQ event - Indicates that a new object added to the container */ -+#define DPRC_IRQ_EVENT_OBJ_ADDED 0x00000001 -+ -+/* IRQ event - Indicates that an object was removed from the container */ -+#define DPRC_IRQ_EVENT_OBJ_REMOVED 0x00000002 -+ -+/* IRQ event - Indicates that resources added to the container */ -+#define DPRC_IRQ_EVENT_RES_ADDED 0x00000004 -+ -+/* IRQ event - Indicates that resources removed from the container */ -+#define DPRC_IRQ_EVENT_RES_REMOVED 0x00000008 -+ -+/* IRQ event - Indicates that one of the descendant containers that opened by -+ * this container is destroyed -+ */ -+#define DPRC_IRQ_EVENT_CONTAINER_DESTROYED 0x00000010 -+ -+/* IRQ event - Indicates that on one of the container's opened object is -+ * destroyed -+ */ -+#define DPRC_IRQ_EVENT_OBJ_DESTROYED 0x00000020 -+ -+/* Irq event - Indicates that object is created at the container */ -+#define DPRC_IRQ_EVENT_OBJ_CREATED 0x00000040 -+ -+/** -+ * struct dprc_irq_cfg - IRQ configuration -+ * @paddr: Address that must be written to signal a message-based interrupt -+ * @val: Value to write into irq_addr address -+ * @irq_num: A user defined number associated with this IRQ -+ */ -+struct dprc_irq_cfg { -+ uint64_t paddr; -+ uint32_t val; -+ int irq_num; -+}; -+ -+/** -+ * dprc_set_irq() - Set IRQ information for the DPRC to trigger an interrupt. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @irq_index: Identifies the interrupt index to configure -+ * @irq_cfg: IRQ configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_set_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ struct dprc_irq_cfg *irq_cfg); -+ -+/** -+ * dprc_get_irq() - Get IRQ information from the DPRC. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @irq_index: The interrupt index to configure -+ * @type: Interrupt type: 0 represents message interrupt -+ * type (both irq_addr and irq_val are valid) -+ * @irq_cfg: IRQ attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_get_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ int *type, -+ struct dprc_irq_cfg *irq_cfg); -+ -+/** -+ * dprc_set_irq_enable() - Set overall interrupt state. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @irq_index: The interrupt index to configure -+ * @en: Interrupt state - enable = 1, disable = 0 -+ * -+ * Allows GPP software to control when interrupts are generated. -+ * Each interrupt can have up to 32 causes. The enable/disable control's the -+ * overall interrupt state. if the interrupt is disabled no causes will cause -+ * an interrupt. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_set_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t en); -+ -+/** -+ * dprc_get_irq_enable() - Get overall interrupt state. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @irq_index: The interrupt index to configure -+ * @en: Returned interrupt state - enable = 1, disable = 0 -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_get_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t *en); -+ -+/** -+ * dprc_set_irq_mask() - Set interrupt mask. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @irq_index: The interrupt index to configure -+ * @mask: event mask to trigger interrupt; -+ * each bit: -+ * 0 = ignore event -+ * 1 = consider event for asserting irq -+ * -+ * Every interrupt can have up to 32 causes and the interrupt model supports -+ * masking/unmasking each cause independently -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_set_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t mask); -+ -+/** -+ * dprc_get_irq_mask() - Get interrupt mask. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @irq_index: The interrupt index to configure -+ * @mask: Returned event mask to trigger interrupt -+ * -+ * Every interrupt can have up to 32 causes and the interrupt model supports -+ * masking/unmasking each cause independently -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_get_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *mask); -+ -+/** -+ * dprc_get_irq_status() - Get the current status of any pending interrupts. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @irq_index: The interrupt index to configure -+ * @status: Returned interrupts status - one bit per cause: -+ * 0 = no interrupt pending -+ * 1 = interrupt pending -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_get_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *status); -+ -+/** -+ * dprc_clear_irq_status() - Clear a pending interrupt's status -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @irq_index: The interrupt index to configure -+ * @status: bits to clear (W1C) - one bit per cause: -+ * 0 = don't change -+ * 1 = clear status bit -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_clear_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t status); -+ -+/** -+ * struct dprc_attributes - Container attributes -+ * @container_id: Container's ID -+ * @icid: Container's ICID -+ * @portal_id: Container's portal ID -+ * @options: Container's options as set at container's creation -+ * @version: DPRC version -+ */ -+struct dprc_attributes { -+ int container_id; -+ uint16_t icid; -+ int portal_id; -+ uint64_t options; -+ /** -+ * struct version - DPRC version -+ * @major: DPRC major version -+ * @minor: DPRC minor version -+ */ -+ struct { -+ uint16_t major; -+ uint16_t minor; -+ } version; -+}; -+ -+/** -+ * dprc_get_attributes() - Obtains container attributes -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @attributes: Returned container attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_get_attributes(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dprc_attributes *attributes); -+ -+/** -+ * dprc_set_res_quota() - Set allocation policy for a specific resource/object -+ * type in a child container -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @child_container_id: ID of the child container -+ * @type: Resource/object type -+ * @quota: Sets the maximum number of resources of the selected type -+ * that the child container is allowed to allocate from its parent; -+ * when quota is set to -1, the policy is the same as container's -+ * general policy. -+ * -+ * Allocation policy determines whether or not a container may allocate -+ * resources from its parent. Each container has a 'global' allocation policy -+ * that is set when the container is created. -+ * -+ * This function sets allocation policy for a specific resource type. -+ * The default policy for all resource types matches the container's 'global' -+ * allocation policy. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ * -+ * @warning Only the parent container is allowed to change a child policy. -+ */ -+int dprc_set_res_quota(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int child_container_id, -+ char *type, -+ uint16_t quota); -+ -+/** -+ * dprc_get_res_quota() - Gets the allocation policy of a specific -+ * resource/object type in a child container -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @child_container_id: ID of the child container -+ * @type: resource/object type -+ * @quota: Returnes the maximum number of resources of the selected type -+ * that the child container is allowed to allocate from the parent; -+ * when quota is set to -1, the policy is the same as container's -+ * general policy. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_get_res_quota(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int child_container_id, -+ char *type, -+ uint16_t *quota); -+ -+/* Resource request options */ -+ -+/* Explicit resource ID request - The requested objects/resources -+ * are explicit and sequential (in case of resources). -+ * The base ID is given at res_req at base_align field -+ */ -+#define DPRC_RES_REQ_OPT_EXPLICIT 0x00000001 -+ -+/* Aligned resources request - Relevant only for resources -+ * request (and not objects). Indicates that resources base ID should be -+ * sequential and aligned to the value given at dprc_res_req base_align field -+ */ -+#define DPRC_RES_REQ_OPT_ALIGNED 0x00000002 -+ -+/* Plugged Flag - Relevant only for object assignment request. -+ * Indicates that after all objects assigned. An interrupt will be invoked at -+ * the relevant GPP. The assigned object will be marked as plugged. -+ * plugged objects can't be assigned from their container -+ */ -+#define DPRC_RES_REQ_OPT_PLUGGED 0x00000004 -+ -+/** -+ * struct dprc_res_req - Resource request descriptor, to be used in assignment -+ * or un-assignment of resources and objects. -+ * @type: Resource/object type: Represent as a NULL terminated string. -+ * This string may received by using dprc_get_pool() to get resource -+ * type and dprc_get_obj() to get object type; -+ * Note: it is not possible to assign/un-assign DPRC objects -+ * @num: Number of resources -+ * @options: Request options: combination of DPRC_RES_REQ_OPT_ options -+ * @id_base_align: In case of explicit assignment (DPRC_RES_REQ_OPT_EXPLICIT -+ * is set at option), this field represents the required base ID -+ * for resource allocation; In case of aligned assignment -+ * (DPRC_RES_REQ_OPT_ALIGNED is set at option), this field -+ * indicates the required alignment for the resource ID(s) - -+ * use 0 if there is no alignment or explicit ID requirements -+ */ -+struct dprc_res_req { -+ char type[16]; -+ uint32_t num; -+ uint32_t options; -+ int id_base_align; -+}; -+ -+/** -+ * dprc_assign() - Assigns objects or resource to a child container. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @container_id: ID of the child container -+ * @res_req: Describes the type and amount of resources to -+ * assign to the given container -+ * -+ * Assignment is usually done by a parent (this DPRC) to one of its child -+ * containers. -+ * -+ * According to the DPRC allocation policy, the assigned resources may be taken -+ * (allocated) from the container's ancestors, if not enough resources are -+ * available in the container itself. -+ * -+ * The type of assignment depends on the dprc_res_req options, as follows: -+ * - DPRC_RES_REQ_OPT_EXPLICIT: indicates that assigned resources should have -+ * the explicit base ID specified at the id_base_align field of res_req. -+ * - DPRC_RES_REQ_OPT_ALIGNED: indicates that the assigned resources should be -+ * aligned to the value given at id_base_align field of res_req. -+ * - DPRC_RES_REQ_OPT_PLUGGED: Relevant only for object assignment, -+ * and indicates that the object must be set to the plugged state. -+ * -+ * A container may use this function with its own ID in order to change a -+ * object state to plugged or unplugged. -+ * -+ * If IRQ information has been set in the child DPRC, it will signal an -+ * interrupt following every change in its object assignment. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_assign(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int container_id, -+ struct dprc_res_req *res_req); -+ -+/** -+ * dprc_unassign() - Un-assigns objects or resources from a child container -+ * and moves them into this (parent) DPRC. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @child_container_id: ID of the child container -+ * @res_req: Describes the type and amount of resources to un-assign from -+ * the child container -+ * -+ * Un-assignment of objects can succeed only if the object is not in the -+ * plugged or opened state. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_unassign(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int child_container_id, -+ struct dprc_res_req *res_req); -+ -+/** -+ * dprc_get_pool_count() - Get the number of dprc's pools -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @mc_io: Pointer to MC portal's I/O object -+ * @token: Token of DPRC object -+ * @pool_count: Returned number of resource pools in the dprc -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_get_pool_count(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *pool_count); -+ -+/** -+ * dprc_get_pool() - Get the type (string) of a certain dprc's pool -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @pool_index: Index of the pool to be queried (< pool_count) -+ * @type: The type of the pool -+ * -+ * The pool types retrieved one by one by incrementing -+ * pool_index up to (not including) the value of pool_count returned -+ * from dprc_get_pool_count(). dprc_get_pool_count() must -+ * be called prior to dprc_get_pool(). -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_get_pool(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int pool_index, -+ char *type); -+ -+/** -+ * dprc_get_obj_count() - Obtains the number of objects in the DPRC -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @obj_count: Number of objects assigned to the DPRC -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_get_obj_count(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *obj_count); -+ -+/* Objects Attributes Flags */ -+ -+/* Opened state - Indicates that an object is open by at least one owner */ -+#define DPRC_OBJ_STATE_OPEN 0x00000001 -+/* Plugged state - Indicates that the object is plugged */ -+#define DPRC_OBJ_STATE_PLUGGED 0x00000002 -+ -+/** -+ * Shareability flag - Object flag indicating no memory shareability. -+ * the object generates memory accesses that are non coherent with other -+ * masters; -+ * user is responsible for proper memory handling through IOMMU configuration. -+ */ -+#define DPRC_OBJ_FLAG_NO_MEM_SHAREABILITY 0x0001 -+ -+/** -+ * struct dprc_obj_desc - Object descriptor, returned from dprc_get_obj() -+ * @type: Type of object: NULL terminated string -+ * @id: ID of logical object resource -+ * @vendor: Object vendor identifier -+ * @ver_major: Major version number -+ * @ver_minor: Minor version number -+ * @irq_count: Number of interrupts supported by the object -+ * @region_count: Number of mappable regions supported by the object -+ * @state: Object state: combination of DPRC_OBJ_STATE_ states -+ * @label: Object label -+ * @flags: Object's flags -+ */ -+struct dprc_obj_desc { -+ char type[16]; -+ int id; -+ uint16_t vendor; -+ uint16_t ver_major; -+ uint16_t ver_minor; -+ uint8_t irq_count; -+ uint8_t region_count; -+ uint32_t state; -+ char label[16]; -+ uint16_t flags; -+}; -+ -+/** -+ * dprc_get_obj() - Get general information on an object -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @obj_index: Index of the object to be queried (< obj_count) -+ * @obj_desc: Returns the requested object descriptor -+ * -+ * The object descriptors are retrieved one by one by incrementing -+ * obj_index up to (not including) the value of obj_count returned -+ * from dprc_get_obj_count(). dprc_get_obj_count() must -+ * be called prior to dprc_get_obj(). -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_get_obj(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int obj_index, -+ struct dprc_obj_desc *obj_desc); -+ -+/** -+ * dprc_get_obj_desc() - Get object descriptor. -+ * -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @obj_type: The type of the object to get its descriptor. -+ * @obj_id: The id of the object to get its descriptor -+ * @obj_desc: The returned descriptor to fill and return to the user -+ * -+ * Return: '0' on Success; Error code otherwise. -+ * -+ */ -+int dprc_get_obj_desc(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ char *obj_type, -+ int obj_id, -+ struct dprc_obj_desc *obj_desc); -+ -+/** -+ * dprc_set_obj_irq() - Set IRQ information for object to trigger an interrupt. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @obj_type: Type of the object to set its IRQ -+ * @obj_id: ID of the object to set its IRQ -+ * @irq_index: The interrupt index to configure -+ * @irq_cfg: IRQ configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_set_obj_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ char *obj_type, -+ int obj_id, -+ uint8_t irq_index, -+ struct dprc_irq_cfg *irq_cfg); -+ -+/** -+ * dprc_get_obj_irq() - Get IRQ information from object. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @obj_type: Type od the object to get its IRQ -+ * @obj_id: ID of the object to get its IRQ -+ * @irq_index: The interrupt index to configure -+ * @type: Interrupt type: 0 represents message interrupt -+ * type (both irq_addr and irq_val are valid) -+ * @irq_cfg: The returned IRQ attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_get_obj_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ char *obj_type, -+ int obj_id, -+ uint8_t irq_index, -+ int *type, -+ struct dprc_irq_cfg *irq_cfg); -+ -+/** -+ * dprc_get_res_count() - Obtains the number of free resources that are assigned -+ * to this container, by pool type -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @type: pool type -+ * @res_count: Returned number of free resources of the given -+ * resource type that are assigned to this DPRC -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_get_res_count(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ char *type, -+ int *res_count); -+ -+/** -+ * enum dprc_iter_status - Iteration status -+ * @DPRC_ITER_STATUS_FIRST: Perform first iteration -+ * @DPRC_ITER_STATUS_MORE: Indicates more/next iteration is needed -+ * @DPRC_ITER_STATUS_LAST: Indicates last iteration -+ */ -+enum dprc_iter_status { -+ DPRC_ITER_STATUS_FIRST = 0, -+ DPRC_ITER_STATUS_MORE = 1, -+ DPRC_ITER_STATUS_LAST = 2 -+}; -+ -+/** -+ * struct dprc_res_ids_range_desc - Resource ID range descriptor -+ * @base_id: Base resource ID of this range -+ * @last_id: Last resource ID of this range -+ * @iter_status: Iteration status - should be set to DPRC_ITER_STATUS_FIRST at -+ * first iteration; while the returned marker is DPRC_ITER_STATUS_MORE, -+ * additional iterations are needed, until the returned marker is -+ * DPRC_ITER_STATUS_LAST -+ */ -+struct dprc_res_ids_range_desc { -+ int base_id; -+ int last_id; -+ enum dprc_iter_status iter_status; -+}; -+ -+/** -+ * dprc_get_res_ids() - Obtains IDs of free resources in the container -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @type: pool type -+ * @range_desc: range descriptor -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_get_res_ids(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ char *type, -+ struct dprc_res_ids_range_desc *range_desc); -+ -+/* Region flags */ -+/* Cacheable - Indicates that region should be mapped as cacheable */ -+#define DPRC_REGION_CACHEABLE 0x00000001 -+ -+/** -+ * enum dprc_region_type - Region type -+ * @DPRC_REGION_TYPE_MC_PORTAL: MC portal region -+ * @DPRC_REGION_TYPE_QBMAN_PORTAL: Qbman portal region -+ */ -+enum dprc_region_type { -+ DPRC_REGION_TYPE_MC_PORTAL, -+ DPRC_REGION_TYPE_QBMAN_PORTAL -+}; -+ -+/** -+ * struct dprc_region_desc - Mappable region descriptor -+ * @base_offset: Region offset from region's base address. -+ * For DPMCP and DPRC objects, region base is offset from SoC MC portals -+ * base address; For DPIO, region base is offset from SoC QMan portals -+ * base address -+ * @size: Region size (in bytes) -+ * @flags: Region attributes -+ * @type: Portal region type -+ */ -+struct dprc_region_desc { -+ uint32_t base_offset; -+ uint32_t size; -+ uint32_t flags; -+ enum dprc_region_type type; -+}; -+ -+/** -+ * dprc_get_obj_region() - Get region information for a specified object. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @obj_type: Object type as returned in dprc_get_obj() -+ * @obj_id: Unique object instance as returned in dprc_get_obj() -+ * @region_index: The specific region to query -+ * @region_desc: Returns the requested region descriptor -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_get_obj_region(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ char *obj_type, -+ int obj_id, -+ uint8_t region_index, -+ struct dprc_region_desc *region_desc); -+ -+/** -+ * dprc_set_obj_label() - Set object label. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @obj_type: Object's type -+ * @obj_id: Object's ID -+ * @label: The required label. The maximum length is 16 chars. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_set_obj_label(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ char *obj_type, -+ int obj_id, -+ char *label); -+ -+/** -+ * struct dprc_endpoint - Endpoint description for link connect/disconnect -+ * operations -+ * @type: Endpoint object type: NULL terminated string -+ * @id: Endpoint object ID -+ * @if_id: Interface ID; should be set for endpoints with multiple -+ * interfaces ("dpsw", "dpdmux"); for others, always set to 0 -+ */ -+struct dprc_endpoint { -+ char type[16]; -+ int id; -+ int if_id; -+}; -+ -+/** -+ * struct dprc_connection_cfg - Connection configuration. -+ * Used for virtual connections only -+ * @committed_rate: Committed rate (Mbits/s) -+ * @max_rate: Maximum rate (Mbits/s) -+ */ -+struct dprc_connection_cfg { -+ uint32_t committed_rate; -+ uint32_t max_rate; -+}; -+ -+/** -+ * dprc_connect() - Connect two endpoints to create a network link between them -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @endpoint1: Endpoint 1 configuration parameters -+ * @endpoint2: Endpoint 2 configuration parameters -+ * @cfg: Connection configuration. The connection configuration is ignored for -+ * connections made to DPMAC objects, where rate is retrieved from the -+ * MAC configuration. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_connect(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dprc_endpoint *endpoint1, -+ const struct dprc_endpoint *endpoint2, -+ const struct dprc_connection_cfg *cfg); -+ -+/** -+ * dprc_disconnect() - Disconnect one endpoint to remove its network connection -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @endpoint: Endpoint configuration parameters -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_disconnect(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dprc_endpoint *endpoint); -+ -+/** -+* dprc_get_connection() - Get connected endpoint and link status if connection -+* exists. -+* @mc_io: Pointer to MC portal's I/O object -+* @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+* @token: Token of DPRC object -+* @endpoint1: Endpoint 1 configuration parameters -+* @endpoint2: Returned endpoint 2 configuration parameters -+* @state: Returned link state: -+* 1 - link is up; -+* 0 - link is down; -+* -1 - no connection (endpoint2 information is irrelevant) -+* -+* Return: '0' on Success; -ENAVAIL if connection does not exist. -+*/ -+int dprc_get_connection(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dprc_endpoint *endpoint1, -+ struct dprc_endpoint *endpoint2, -+ int *state); -+ -+#endif /* _FSL_DPRC_H */ -+ -diff --git a/drivers/staging/fsl-mc/include/fsl_dpaa2_fd.h b/drivers/staging/fsl-mc/include/fsl_dpaa2_fd.h -new file mode 100644 -index 0000000..3e9af59 ---- /dev/null -+++ b/drivers/staging/fsl-mc/include/fsl_dpaa2_fd.h -@@ -0,0 +1,774 @@ -+/* Copyright 2014 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 __FSL_DPAA2_FD_H -+#define __FSL_DPAA2_FD_H -+ -+/** -+ * DOC: DPAA2 FD - Frame Descriptor APIs for DPAA2 -+ * -+ * Frame Descriptors (FDs) are used to describe frame data in the DPAA2. -+ * Frames can be enqueued and dequeued to Frame Queues which are consumed -+ * by the various DPAA accelerators (WRIOP, SEC, PME, DCE) -+ * -+ * There are three types of frames: Single, Scatter Gather and Frame Lists. -+ * -+ * The set of APIs in this file must be used to create, manipulate and -+ * query Frame Descriptor. -+ * -+ */ -+ -+/** -+ * struct dpaa2_fd - Place-holder for FDs. -+ * @words: for easier/faster copying the whole FD structure. -+ * @addr_lo: the lower 32 bits of the address in FD. -+ * @addr_hi: the upper 32 bits of the address in FD. -+ * @len: the length field in FD. -+ * @bpid_offset: represent the bpid and offset fields in FD -+ * @frc: frame context -+ * @ctrl: the 32bit control bits including dd, sc,... va, err. -+ * @flc_lo: the lower 32bit of flow context. -+ * @flc_hi: the upper 32bits of flow context. -+ * -+ * This structure represents the basic Frame Descriptor used in the system. -+ * We represent it via the simplest form that we need for now. Different -+ * overlays may be needed to support different options, etc. (It is impractical -+ * to define One True Struct, because the resulting encoding routines (lots of -+ * read-modify-writes) would be worst-case performance whether or not -+ * circumstances required them.) -+ */ -+struct dpaa2_fd { -+ union { -+ u32 words[8]; -+ struct dpaa2_fd_simple { -+ u32 addr_lo; -+ u32 addr_hi; -+ u32 len; -+ /* offset in the MS 16 bits, BPID in the LS 16 bits */ -+ u32 bpid_offset; -+ u32 frc; /* frame context */ -+ /* "err", "va", "cbmt", "asal", [...] */ -+ u32 ctrl; -+ /* flow context */ -+ u32 flc_lo; -+ u32 flc_hi; -+ } simple; -+ }; -+}; -+ -+enum dpaa2_fd_format { -+ dpaa2_fd_single = 0, -+ dpaa2_fd_list, -+ dpaa2_fd_sg -+}; -+ -+/* Accessors for SG entry fields -+ * -+ * These setters and getters assume little endian format. For converting -+ * between LE and cpu endianness, the specific conversion functions must be -+ * called before the SGE contents are accessed by the core (on Rx), -+ * respectively before the SG table is sent to hardware (on Tx) -+ */ -+ -+/** -+ * dpaa2_fd_get_addr() - get the addr field of frame descriptor -+ * @fd: the given frame descriptor. -+ * -+ * Return the address in the frame descriptor. -+ */ -+static inline dma_addr_t dpaa2_fd_get_addr(const struct dpaa2_fd *fd) -+{ -+ return (dma_addr_t)((((uint64_t)fd->simple.addr_hi) << 32) -+ + fd->simple.addr_lo); -+} -+ -+/** -+ * dpaa2_fd_set_addr() - Set the addr field of frame descriptor -+ * @fd: the given frame descriptor. -+ * @addr: the address needs to be set in frame descriptor. -+ */ -+static inline void dpaa2_fd_set_addr(struct dpaa2_fd *fd, dma_addr_t addr) -+{ -+ fd->simple.addr_hi = upper_32_bits(addr); -+ fd->simple.addr_lo = lower_32_bits(addr); -+} -+ -+/** -+ * dpaa2_fd_get_frc() - Get the frame context in the frame descriptor -+ * @fd: the given frame descriptor. -+ * -+ * Return the frame context field in the frame descriptor. -+ */ -+static inline u32 dpaa2_fd_get_frc(const struct dpaa2_fd *fd) -+{ -+ return fd->simple.frc; -+} -+ -+/** -+ * dpaa2_fd_set_frc() - Set the frame context in the frame descriptor -+ * @fd: the given frame descriptor. -+ * @frc: the frame context needs to be set in frame descriptor. -+ */ -+static inline void dpaa2_fd_set_frc(struct dpaa2_fd *fd, u32 frc) -+{ -+ fd->simple.frc = frc; -+} -+ -+/** -+ * dpaa2_fd_get_flc() - Get the flow context in the frame descriptor -+ * @fd: the given frame descriptor. -+ * -+ * Return the flow context in the frame descriptor. -+ */ -+static inline dma_addr_t dpaa2_fd_get_flc(const struct dpaa2_fd *fd) -+{ -+ return (dma_addr_t)((((uint64_t)fd->simple.flc_hi) << 32) + -+ fd->simple.flc_lo); -+} -+ -+/** -+ * dpaa2_fd_set_flc() - Set the flow context field of frame descriptor -+ * @fd: the given frame descriptor. -+ * @flc_addr: the flow context needs to be set in frame descriptor. -+ */ -+static inline void dpaa2_fd_set_flc(struct dpaa2_fd *fd, dma_addr_t flc_addr) -+{ -+ fd->simple.flc_hi = upper_32_bits(flc_addr); -+ fd->simple.flc_lo = lower_32_bits(flc_addr); -+} -+ -+/** -+ * dpaa2_fd_get_len() - Get the length in the frame descriptor -+ * @fd: the given frame descriptor. -+ * -+ * Return the length field in the frame descriptor. -+ */ -+static inline u32 dpaa2_fd_get_len(const struct dpaa2_fd *fd) -+{ -+ return fd->simple.len; -+} -+ -+/** -+ * dpaa2_fd_set_len() - Set the length field of frame descriptor -+ * @fd: the given frame descriptor. -+ * @len: the length needs to be set in frame descriptor. -+ */ -+static inline void dpaa2_fd_set_len(struct dpaa2_fd *fd, u32 len) -+{ -+ fd->simple.len = len; -+} -+ -+/** -+ * dpaa2_fd_get_offset() - Get the offset field in the frame descriptor -+ * @fd: the given frame descriptor. -+ * -+ * Return the offset. -+ */ -+static inline uint16_t dpaa2_fd_get_offset(const struct dpaa2_fd *fd) -+{ -+ return (uint16_t)(fd->simple.bpid_offset >> 16) & 0x0FFF; -+} -+ -+/** -+ * dpaa2_fd_set_offset() - Set the offset field of frame descriptor -+ * -+ * @fd: the given frame descriptor. -+ * @offset: the offset needs to be set in frame descriptor. -+ */ -+static inline void dpaa2_fd_set_offset(struct dpaa2_fd *fd, uint16_t offset) -+{ -+ fd->simple.bpid_offset &= 0xF000FFFF; -+ fd->simple.bpid_offset |= (u32)offset << 16; -+} -+ -+/** -+ * dpaa2_fd_get_format() - Get the format field in the frame descriptor -+ * @fd: the given frame descriptor. -+ * -+ * Return the format. -+ */ -+static inline enum dpaa2_fd_format dpaa2_fd_get_format( -+ const struct dpaa2_fd *fd) -+{ -+ return (enum dpaa2_fd_format)((fd->simple.bpid_offset >> 28) & 0x3); -+} -+ -+/** -+ * dpaa2_fd_set_format() - Set the format field of frame descriptor -+ * -+ * @fd: the given frame descriptor. -+ * @format: the format needs to be set in frame descriptor. -+ */ -+static inline void dpaa2_fd_set_format(struct dpaa2_fd *fd, -+ enum dpaa2_fd_format format) -+{ -+ fd->simple.bpid_offset &= 0xCFFFFFFF; -+ fd->simple.bpid_offset |= (u32)format << 28; -+} -+ -+/** -+ * dpaa2_fd_get_bpid() - Get the bpid field in the frame descriptor -+ * @fd: the given frame descriptor. -+ * -+ * Return the bpid. -+ */ -+static inline uint16_t dpaa2_fd_get_bpid(const struct dpaa2_fd *fd) -+{ -+ return (uint16_t)(fd->simple.bpid_offset & 0xFFFF); -+} -+ -+/** -+ * dpaa2_fd_set_bpid() - Set the bpid field of frame descriptor -+ * -+ * @fd: the given frame descriptor. -+ * @bpid: the bpid needs to be set in frame descriptor. -+ */ -+static inline void dpaa2_fd_set_bpid(struct dpaa2_fd *fd, uint16_t bpid) -+{ -+ fd->simple.bpid_offset &= 0xFFFF0000; -+ fd->simple.bpid_offset |= (u32)bpid; -+} -+ -+/** -+ * struct dpaa2_sg_entry - the scatter-gathering structure -+ * @addr_lo: the lower 32bit of address -+ * @addr_hi: the upper 32bit of address -+ * @len: the length in this sg entry. -+ * @bpid_offset: offset in the MS 16 bits, BPID in the LS 16 bits. -+ */ -+struct dpaa2_sg_entry { -+ u32 addr_lo; -+ u32 addr_hi; -+ u32 len; -+ u32 bpid_offset; -+}; -+ -+enum dpaa2_sg_format { -+ dpaa2_sg_single = 0, -+ dpaa2_sg_frame_data, -+ dpaa2_sg_sgt_ext -+}; -+ -+/** -+ * dpaa2_sg_get_addr() - Get the address from SG entry -+ * @sg: the given scatter-gathering object. -+ * -+ * Return the address. -+ */ -+static inline dma_addr_t dpaa2_sg_get_addr(const struct dpaa2_sg_entry *sg) -+{ -+ return (dma_addr_t)((((u64)sg->addr_hi) << 32) + sg->addr_lo); -+} -+ -+/** -+ * dpaa2_sg_set_addr() - Set the address in SG entry -+ * @sg: the given scatter-gathering object. -+ * @addr: the address to be set. -+ */ -+static inline void dpaa2_sg_set_addr(struct dpaa2_sg_entry *sg, dma_addr_t addr) -+{ -+ sg->addr_hi = upper_32_bits(addr); -+ sg->addr_lo = lower_32_bits(addr); -+} -+ -+ -+static inline bool dpaa2_sg_short_len(const struct dpaa2_sg_entry *sg) -+{ -+ return (sg->bpid_offset >> 30) & 0x1; -+} -+ -+/** -+ * dpaa2_sg_get_len() - Get the length in SG entry -+ * @sg: the given scatter-gathering object. -+ * -+ * Return the length. -+ */ -+static inline u32 dpaa2_sg_get_len(const struct dpaa2_sg_entry *sg) -+{ -+ if (dpaa2_sg_short_len(sg)) -+ return sg->len & 0x1FFFF; -+ return sg->len; -+} -+ -+/** -+ * dpaa2_sg_set_len() - Set the length in SG entry -+ * @sg: the given scatter-gathering object. -+ * @len: the length to be set. -+ */ -+static inline void dpaa2_sg_set_len(struct dpaa2_sg_entry *sg, u32 len) -+{ -+ sg->len = len; -+} -+ -+/** -+ * dpaa2_sg_get_offset() - Get the offset in SG entry -+ * @sg: the given scatter-gathering object. -+ * -+ * Return the offset. -+ */ -+static inline u16 dpaa2_sg_get_offset(const struct dpaa2_sg_entry *sg) -+{ -+ return (u16)(sg->bpid_offset >> 16) & 0x0FFF; -+} -+ -+/** -+ * dpaa2_sg_set_offset() - Set the offset in SG entry -+ * @sg: the given scatter-gathering object. -+ * @offset: the offset to be set. -+ */ -+static inline void dpaa2_sg_set_offset(struct dpaa2_sg_entry *sg, -+ u16 offset) -+{ -+ sg->bpid_offset &= 0xF000FFFF; -+ sg->bpid_offset |= (u32)offset << 16; -+} -+ -+/** -+ * dpaa2_sg_get_format() - Get the SG format in SG entry -+ * @sg: the given scatter-gathering object. -+ * -+ * Return the format. -+ */ -+static inline enum dpaa2_sg_format -+ dpaa2_sg_get_format(const struct dpaa2_sg_entry *sg) -+{ -+ return (enum dpaa2_sg_format)((sg->bpid_offset >> 28) & 0x3); -+} -+ -+/** -+ * dpaa2_sg_set_format() - Set the SG format in SG entry -+ * @sg: the given scatter-gathering object. -+ * @format: the format to be set. -+ */ -+static inline void dpaa2_sg_set_format(struct dpaa2_sg_entry *sg, -+ enum dpaa2_sg_format format) -+{ -+ sg->bpid_offset &= 0xCFFFFFFF; -+ sg->bpid_offset |= (u32)format << 28; -+} -+ -+/** -+ * dpaa2_sg_get_bpid() - Get the buffer pool id in SG entry -+ * @sg: the given scatter-gathering object. -+ * -+ * Return the bpid. -+ */ -+static inline u16 dpaa2_sg_get_bpid(const struct dpaa2_sg_entry *sg) -+{ -+ return (u16)(sg->bpid_offset & 0x3FFF); -+} -+ -+/** -+ * dpaa2_sg_set_bpid() - Set the buffer pool id in SG entry -+ * @sg: the given scatter-gathering object. -+ * @bpid: the bpid to be set. -+ */ -+static inline void dpaa2_sg_set_bpid(struct dpaa2_sg_entry *sg, u16 bpid) -+{ -+ sg->bpid_offset &= 0xFFFFC000; -+ sg->bpid_offset |= (u32)bpid; -+} -+ -+/** -+ * dpaa2_sg_is_final() - Check final bit in SG entry -+ * @sg: the given scatter-gathering object. -+ * -+ * Return bool. -+ */ -+static inline bool dpaa2_sg_is_final(const struct dpaa2_sg_entry *sg) -+{ -+ return !!(sg->bpid_offset >> 31); -+} -+ -+/** -+ * dpaa2_sg_set_final() - Set the final bit in SG entry -+ * @sg: the given scatter-gathering object. -+ * @final: the final boolean to be set. -+ */ -+static inline void dpaa2_sg_set_final(struct dpaa2_sg_entry *sg, bool final) -+{ -+ sg->bpid_offset &= 0x7FFFFFFF; -+ sg->bpid_offset |= (u32)final << 31; -+} -+ -+/* Endianness conversion helper functions -+ * The accelerator drivers which construct / read scatter gather entries -+ * need to call these in order to account for endianness mismatches between -+ * hardware and cpu -+ */ -+#ifdef __BIG_ENDIAN -+/** -+ * dpaa2_sg_cpu_to_le() - convert scatter gather entry from native cpu -+ * format little endian format. -+ * @sg: the given scatter gather entry. -+ */ -+static inline void dpaa2_sg_cpu_to_le(struct dpaa2_sg_entry *sg) -+{ -+ uint32_t *p = (uint32_t *)sg; -+ int i; -+ -+ for (i = 0; i < sizeof(*sg) / sizeof(u32); i++) -+ cpu_to_le32s(p++); -+} -+ -+/** -+ * dpaa2_sg_le_to_cpu() - convert scatter gather entry from little endian -+ * format to native cpu format. -+ * @sg: the given scatter gather entry. -+ */ -+static inline void dpaa2_sg_le_to_cpu(struct dpaa2_sg_entry *sg) -+{ -+ uint32_t *p = (uint32_t *)sg; -+ int i; -+ -+ for (i = 0; i < sizeof(*sg) / sizeof(u32); i++) -+ le32_to_cpus(p++); -+} -+#else -+#define dpaa2_sg_cpu_to_le(sg) -+#define dpaa2_sg_le_to_cpu(sg) -+#endif /* __BIG_ENDIAN */ -+ -+ -+/** -+ * struct dpaa2_fl_entry - structure for frame list entry. -+ * @addr_lo: the lower 32bit of address -+ * @addr_hi: the upper 32bit of address -+ * @len: the length in this sg entry. -+ * @bpid_offset: offset in the MS 16 bits, BPID in the LS 16 bits. -+ * @frc: frame context -+ * @ctrl: the 32bit control bits including dd, sc,... va, err. -+ * @flc_lo: the lower 32bit of flow context. -+ * @flc_hi: the upper 32bits of flow context. -+ * -+ * Frame List Entry (FLE) -+ * Identical to dpaa2_fd.simple layout, but some bits are different -+ */ -+struct dpaa2_fl_entry { -+ u32 addr_lo; -+ u32 addr_hi; -+ u32 len; -+ u32 bpid_offset; -+ u32 frc; -+ u32 ctrl; -+ u32 flc_lo; -+ u32 flc_hi; -+}; -+ -+enum dpaa2_fl_format { -+ dpaa2_fl_single = 0, -+ dpaa2_fl_res, -+ dpaa2_fl_sg -+}; -+ -+/** -+ * dpaa2_fl_get_addr() - Get address in the frame list entry -+ * @fle: the given frame list entry. -+ * -+ * Return address for the get function. -+ */ -+static inline dma_addr_t dpaa2_fl_get_addr(const struct dpaa2_fl_entry *fle) -+{ -+ return (dma_addr_t)((((uint64_t)fle->addr_hi) << 32) + fle->addr_lo); -+} -+ -+/** -+ * dpaa2_fl_set_addr() - Set the address in the frame list entry -+ * @fle: the given frame list entry. -+ * @addr: the address needs to be set. -+ * -+ */ -+static inline void dpaa2_fl_set_addr(struct dpaa2_fl_entry *fle, -+ dma_addr_t addr) -+{ -+ fle->addr_hi = upper_32_bits(addr); -+ fle->addr_lo = lower_32_bits(addr); -+} -+ -+/** -+ * dpaa2_fl_get_flc() - Get the flow context in the frame list entry -+ * @fle: the given frame list entry. -+ * -+ * Return flow context for the get function. -+ */ -+static inline dma_addr_t dpaa2_fl_get_flc(const struct dpaa2_fl_entry *fle) -+{ -+ return (dma_addr_t)((((uint64_t)fle->flc_hi) << 32) + fle->flc_lo); -+} -+ -+/** -+ * dpaa2_fl_set_flc() - Set the flow context in the frame list entry -+ * @fle: the given frame list entry. -+ * @flc_addr: the flow context address needs to be set. -+ * -+ */ -+static inline void dpaa2_fl_set_flc(struct dpaa2_fl_entry *fle, -+ dma_addr_t flc_addr) -+{ -+ fle->flc_hi = upper_32_bits(flc_addr); -+ fle->flc_lo = lower_32_bits(flc_addr); -+} -+ -+/** -+ * dpaa2_fl_get_len() - Get the length in the frame list entry -+ * @fle: the given frame list entry. -+ * -+ * Return length for the get function. -+ */ -+static inline u32 dpaa2_fl_get_len(const struct dpaa2_fl_entry *fle) -+{ -+ return fle->len; -+} -+ -+/** -+ * dpaa2_fl_set_len() - Set the length in the frame list entry -+ * @fle: the given frame list entry. -+ * @len: the length needs to be set. -+ * -+ */ -+static inline void dpaa2_fl_set_len(struct dpaa2_fl_entry *fle, u32 len) -+{ -+ fle->len = len; -+} -+ -+/** -+ * dpaa2_fl_get_offset() - Get/Set the offset in the frame list entry -+ * @fle: the given frame list entry. -+ * -+ * Return offset for the get function. -+ */ -+static inline uint16_t dpaa2_fl_get_offset(const struct dpaa2_fl_entry *fle) -+{ -+ return (uint16_t)(fle->bpid_offset >> 16) & 0x0FFF; -+} -+ -+/** -+ * dpaa2_fl_set_offset() - Set the offset in the frame list entry -+ * @fle: the given frame list entry. -+ * @offset: the offset needs to be set. -+ * -+ */ -+static inline void dpaa2_fl_set_offset(struct dpaa2_fl_entry *fle, -+ uint16_t offset) -+{ -+ fle->bpid_offset &= 0xF000FFFF; -+ fle->bpid_offset |= (u32)(offset & 0x0FFF) << 16; -+} -+ -+/** -+ * dpaa2_fl_get_format() - Get the format in the frame list entry -+ * @fle: the given frame list entry. -+ * -+ * Return frame list format for the get function. -+ */ -+static inline enum dpaa2_fl_format dpaa2_fl_get_format( -+ const struct dpaa2_fl_entry *fle) -+{ -+ return (enum dpaa2_fl_format)((fle->bpid_offset >> 28) & 0x3); -+} -+ -+/** -+ * dpaa2_fl_set_format() - Set the format in the frame list entry -+ * @fle: the given frame list entry. -+ * @format: the frame list format needs to be set. -+ * -+ */ -+static inline void dpaa2_fl_set_format(struct dpaa2_fl_entry *fle, -+ enum dpaa2_fl_format format) -+{ -+ fle->bpid_offset &= 0xCFFFFFFF; -+ fle->bpid_offset |= (u32)(format & 0x3) << 28; -+} -+ -+/** -+ * dpaa2_fl_get_bpid() - Get the buffer pool id in the frame list entry -+ * @fle: the given frame list entry. -+ * -+ * Return bpid for the get function. -+ */ -+static inline uint16_t dpaa2_fl_get_bpid(const struct dpaa2_fl_entry *fle) -+{ -+ return (uint16_t)(fle->bpid_offset & 0x3FFF); -+} -+ -+/** -+ * dpaa2_fl_set_bpid() - Set the buffer pool id in the frame list entry -+ * @fle: the given frame list entry. -+ * @bpid: the buffer pool id needs to be set. -+ * -+ */ -+static inline void dpaa2_fl_set_bpid(struct dpaa2_fl_entry *fle, uint16_t bpid) -+{ -+ fle->bpid_offset &= 0xFFFFC000; -+ fle->bpid_offset |= (u32)bpid; -+} -+ -+/** dpaa2_fl_is_final() - check the final bit is set or not in the frame list. -+ * @fle: the given frame list entry. -+ * -+ * Return final bit settting. -+ */ -+static inline bool dpaa2_fl_is_final(const struct dpaa2_fl_entry *fle) -+{ -+ return !!(fle->bpid_offset >> 31); -+} -+ -+/** -+ * dpaa2_fl_set_final() - Set the final bit in the frame list entry -+ * @fle: the given frame list entry. -+ * @final: the final bit needs to be set. -+ * -+ */ -+static inline void dpaa2_fl_set_final(struct dpaa2_fl_entry *fle, bool final) -+{ -+ fle->bpid_offset &= 0x7FFFFFFF; -+ fle->bpid_offset |= (u32)final << 31; -+} -+ -+/** -+ * struct dpaa2_dq - the qman result structure -+ * @dont_manipulate_directly: the 16 32bit data to represent the whole -+ * possible qman dequeue result. -+ * -+ * When frames are dequeued, the FDs show up inside "dequeue" result structures -+ * (if at all, not all dequeue results contain valid FDs). This structure type -+ * is intentionally defined without internal detail, and the only reason it -+ * isn't declared opaquely (without size) is to allow the user to provide -+ * suitably-sized (and aligned) memory for these entries. -+ */ -+struct dpaa2_dq { -+ uint32_t dont_manipulate_directly[16]; -+}; -+ -+/* Parsing frame dequeue results */ -+/* FQ empty */ -+#define DPAA2_DQ_STAT_FQEMPTY 0x80 -+/* FQ held active */ -+#define DPAA2_DQ_STAT_HELDACTIVE 0x40 -+/* FQ force eligible */ -+#define DPAA2_DQ_STAT_FORCEELIGIBLE 0x20 -+/* Valid frame */ -+#define DPAA2_DQ_STAT_VALIDFRAME 0x10 -+/* FQ ODP enable */ -+#define DPAA2_DQ_STAT_ODPVALID 0x04 -+/* Volatile dequeue */ -+#define DPAA2_DQ_STAT_VOLATILE 0x02 -+/* volatile dequeue command is expired */ -+#define DPAA2_DQ_STAT_EXPIRED 0x01 -+ -+/** -+ * dpaa2_dq_flags() - Get the stat field of dequeue response -+ * @dq: the dequeue result. -+ */ -+uint32_t dpaa2_dq_flags(const struct dpaa2_dq *dq); -+ -+/** -+ * dpaa2_dq_is_pull() - Check whether the dq response is from a pull -+ * command. -+ * @dq: the dequeue result. -+ * -+ * Return 1 for volatile(pull) dequeue, 0 for static dequeue. -+ */ -+static inline int dpaa2_dq_is_pull(const struct dpaa2_dq *dq) -+{ -+ return (int)(dpaa2_dq_flags(dq) & DPAA2_DQ_STAT_VOLATILE); -+} -+ -+/** -+ * dpaa2_dq_is_pull_complete() - Check whether the pull command is completed. -+ * @dq: the dequeue result. -+ * -+ * Return boolean. -+ */ -+static inline int dpaa2_dq_is_pull_complete( -+ const struct dpaa2_dq *dq) -+{ -+ return (int)(dpaa2_dq_flags(dq) & DPAA2_DQ_STAT_EXPIRED); -+} -+ -+/** -+ * dpaa2_dq_seqnum() - Get the seqnum field in dequeue response -+ * seqnum is valid only if VALIDFRAME flag is TRUE -+ * @dq: the dequeue result. -+ * -+ * Return seqnum. -+ */ -+uint16_t dpaa2_dq_seqnum(const struct dpaa2_dq *dq); -+ -+/** -+ * dpaa2_dq_odpid() - Get the seqnum field in dequeue response -+ * odpid is valid only if ODPVAILD flag is TRUE. -+ * @dq: the dequeue result. -+ * -+ * Return odpid. -+ */ -+uint16_t dpaa2_dq_odpid(const struct dpaa2_dq *dq); -+ -+/** -+ * dpaa2_dq_fqid() - Get the fqid in dequeue response -+ * @dq: the dequeue result. -+ * -+ * Return fqid. -+ */ -+uint32_t dpaa2_dq_fqid(const struct dpaa2_dq *dq); -+ -+/** -+ * dpaa2_dq_byte_count() - Get the byte count in dequeue response -+ * @dq: the dequeue result. -+ * -+ * Return the byte count remaining in the FQ. -+ */ -+uint32_t dpaa2_dq_byte_count(const struct dpaa2_dq *dq); -+ -+/** -+ * dpaa2_dq_frame_count() - Get the frame count in dequeue response -+ * @dq: the dequeue result. -+ * -+ * Return the frame count remaining in the FQ. -+ */ -+uint32_t dpaa2_dq_frame_count(const struct dpaa2_dq *dq); -+ -+/** -+ * dpaa2_dq_fd_ctx() - Get the frame queue context in dequeue response -+ * @dq: the dequeue result. -+ * -+ * Return the frame queue context. -+ */ -+uint64_t dpaa2_dq_fqd_ctx(const struct dpaa2_dq *dq); -+ -+/** -+ * dpaa2_dq_fd() - Get the frame descriptor in dequeue response -+ * @dq: the dequeue result. -+ * -+ * Return the frame descriptor. -+ */ -+const struct dpaa2_fd *dpaa2_dq_fd(const struct dpaa2_dq *dq); -+ -+#endif /* __FSL_DPAA2_FD_H */ -diff --git a/drivers/staging/fsl-mc/include/fsl_dpaa2_io.h b/drivers/staging/fsl-mc/include/fsl_dpaa2_io.h -new file mode 100644 -index 0000000..6ea2ff9 ---- /dev/null -+++ b/drivers/staging/fsl-mc/include/fsl_dpaa2_io.h -@@ -0,0 +1,619 @@ -+/* Copyright 2014 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 __FSL_DPAA2_IO_H -+#define __FSL_DPAA2_IO_H -+ -+#include "fsl_dpaa2_fd.h" -+ -+struct dpaa2_io; -+struct dpaa2_io_store; -+ -+/** -+ * DOC: DPIO Service Management -+ * -+ * The DPIO service provides APIs for users to interact with the datapath -+ * by enqueueing and dequeing frame descriptors. -+ * -+ * The following set of APIs can be used to enqueue and dequeue frames -+ * as well as producing notification callbacks when data is available -+ * for dequeue. -+ */ -+ -+/** -+ * struct dpaa2_io_desc - The DPIO descriptor. -+ * @receives_notifications: Use notificaton mode. -+ * @has_irq: use irq-based proessing. -+ * @will_poll: use poll processing. -+ * @has_8prio: set for channel with 8 priority WQs. -+ * @cpu: the cpu index that at least interrupt handlers will execute on. -+ * @stash_affinity: the stash affinity for this portal favour 'cpu' -+ * @regs_cena: the cache enabled regs. -+ * @regs_cinh: the cache inhibited regs. -+ * @dpio_id: The dpio index. -+ * @qman_version: the qman version -+ * -+ * Describe the attributes and features of the DPIO object. -+ */ -+struct dpaa2_io_desc { -+ /* non-zero iff the DPIO has a channel */ -+ int receives_notifications; -+ /* non-zero if the DPIO portal interrupt is handled. If so, the -+ * caller/OS handles the interrupt and calls dpaa2_io_service_irq(). */ -+ int has_irq; -+ /* non-zero if the caller/OS is prepared to called the -+ * dpaa2_io_service_poll() routine as part of its run-to-completion (or -+ * scheduling) loop. If so, the DPIO service may dynamically switch some -+ * of its processing between polling-based and irq-based. It is illegal -+ * combination to have (!has_irq && !will_poll). */ -+ int will_poll; -+ /* ignored unless 'receives_notifications'. Non-zero iff the channel has -+ * 8 priority WQs, otherwise the channel has 2. */ -+ int has_8prio; -+ /* the cpu index that at least interrupt handlers will execute on. And -+ * if 'stash_affinity' is non-zero, the cache targeted by stash -+ * transactions is affine to this cpu. */ -+ int cpu; -+ /* non-zero if stash transactions for this portal favour 'cpu' over -+ * other CPUs. (Eg. zero if there's no stashing, or stashing is to -+ * shared cache.) */ -+ int stash_affinity; -+ /* Caller-provided flags, determined by bus-scanning and/or creation of -+ * DPIO objects via MC commands. */ -+ void *regs_cena; -+ void *regs_cinh; -+ int dpio_id; -+ uint32_t qman_version; -+}; -+ -+/** -+ * dpaa2_io_create() - create a dpaa2_io object. -+ * @desc: the dpaa2_io descriptor -+ * -+ * Activates a "struct dpaa2_io" corresponding to the given config of an actual -+ * DPIO object. This handle can be used on it's own (like a one-portal "DPIO -+ * service") or later be added to a service-type "struct dpaa2_io" object. Note, -+ * the information required on 'cfg' is copied so the caller is free to do as -+ * they wish with the input parameter upon return. -+ * -+ * Return a valid dpaa2_io object for success, or NULL for failure. -+ */ -+struct dpaa2_io *dpaa2_io_create(const struct dpaa2_io_desc *desc); -+ -+/** -+ * dpaa2_io_create_service() - Create an (initially empty) DPIO service. -+ * -+ * Return a valid dpaa2_io object for success, or NULL for failure. -+ */ -+struct dpaa2_io *dpaa2_io_create_service(void); -+ -+/** -+ * dpaa2_io_default_service() - Use the driver's own global (and initially -+ * empty) DPIO service. -+ * -+ * This increments the reference count, so don't forget to use dpaa2_io_down() -+ * for each time this function is called. -+ * -+ * Return a valid dpaa2_io object for success, or NULL for failure. -+ */ -+struct dpaa2_io *dpaa2_io_default_service(void); -+ -+/** -+ * dpaa2_io_down() - release the dpaa2_io object. -+ * @d: the dpaa2_io object to be released. -+ * -+ * The "struct dpaa2_io" type can represent an individual DPIO object (as -+ * described by "struct dpaa2_io_desc") or an instance of a "DPIO service", -+ * which can be used to group/encapsulate multiple DPIO objects. In all cases, -+ * each handle obtained should be released using this function. -+ */ -+void dpaa2_io_down(struct dpaa2_io *d); -+ -+/** -+ * dpaa2_io_service_add() - Add the given DPIO object to the given DPIO service. -+ * @service: the given DPIO service. -+ * @obj: the given DPIO object. -+ * -+ * 'service' must have been created by dpaa2_io_create_service() and 'obj' -+ * must have been created by dpaa2_io_create(). This increments the reference -+ * count on the object that 'obj' refers to, so the user could call -+ * dpaa2_io_down(obj) after this and the object will persist within the service -+ * (and will be destroyed when the service is destroyed). -+ * -+ * Return 0 for success, or -EINVAL for failure. -+ */ -+int dpaa2_io_service_add(struct dpaa2_io *service, struct dpaa2_io *obj); -+ -+/** -+ * dpaa2_io_get_descriptor() - Get the DPIO descriptor of the given DPIO object. -+ * @obj: the given DPIO object. -+ * @desc: the returned DPIO descriptor. -+ * -+ * This function will return failure if the given dpaa2_io struct represents a -+ * service rather than an individual DPIO object, otherwise it returns zero and -+ * the given 'cfg' structure is filled in. -+ * -+ * Return 0 for success, or -EINVAL for failure. -+ */ -+int dpaa2_io_get_descriptor(struct dpaa2_io *obj, struct dpaa2_io_desc *desc); -+ -+/** -+ * dpaa2_io_poll() - Process any notifications and h/w-initiated events that -+ * are polling-driven. -+ * @obj: the given DPIO object. -+ * -+ * Obligatory for DPIO objects that have dpaa2_io_desc::will_poll non-zero. -+ * -+ * Return 0 for success, or -EINVAL for failure. -+ */ -+int dpaa2_io_poll(struct dpaa2_io *obj); -+ -+/** -+ * dpaa2_io_irq() - Process any notifications and h/w-initiated events that are -+ * irq-driven. -+ * @obj: the given DPIO object. -+ * -+ * Obligatory for DPIO objects that have dpaa2_io_desc::has_irq non-zero. -+ * -+ * Return IRQ_HANDLED for success, or -EINVAL for failure. -+ */ -+int dpaa2_io_irq(struct dpaa2_io *obj); -+ -+/** -+ * dpaa2_io_pause_poll() - Used to stop polling. -+ * @obj: the given DPIO object. -+ * -+ * If a polling application is going to stop polling for a period of time and -+ * supports interrupt processing, it can call this function to convert all -+ * processing to IRQ. (Eg. when sleeping.) -+ * -+ * Return -EINVAL. -+ */ -+int dpaa2_io_pause_poll(struct dpaa2_io *obj); -+ -+/** -+ * dpaa2_io_resume_poll() - Resume polling -+ * @obj: the given DPIO object. -+ * -+ * Return -EINVAL. -+ */ -+int dpaa2_io_resume_poll(struct dpaa2_io *obj); -+ -+/** -+ * dpaa2_io_service_notifications() - Get a mask of cpus that the DPIO service -+ * can receive notifications on. -+ * @s: the given DPIO object. -+ * @mask: the mask of cpus. -+ * -+ * Note that this is a run-time snapshot. If things like cpu-hotplug are -+ * supported in the target system, then an attempt to register notifications -+ * for a cpu that appears present in the given mask might fail if that cpu has -+ * gone offline in the mean time. -+ */ -+void dpaa2_io_service_notifications(struct dpaa2_io *s, cpumask_t *mask); -+ -+/** -+ * dpaa2_io_service_stashing - Get a mask of cpus that the DPIO service has stash -+ * affinity to. -+ * @s: the given DPIO object. -+ * @mask: the mask of cpus. -+ */ -+void dpaa2_io_service_stashing(struct dpaa2_io *s, cpumask_t *mask); -+ -+/** -+ * dpaa2_io_service_nonaffine() - Check the DPIO service's cpu affinity -+ * for stashing. -+ * @s: the given DPIO object. -+ * -+ * Return a boolean, whether or not the DPIO service has resources that have no -+ * particular cpu affinity for stashing. (Useful to know if you wish to operate -+ * on CPUs that the service has no affinity to, you would choose to use -+ * resources that are neutral, rather than affine to a different CPU.) Unlike -+ * other service-specific APIs, this one doesn't return an error if it is passed -+ * a non-service object. So don't do it. -+ */ -+int dpaa2_io_service_has_nonaffine(struct dpaa2_io *s); -+ -+/*************************/ -+/* Notification handling */ -+/*************************/ -+ -+/** -+ * struct dpaa2_io_notification_ctx - The DPIO notification context structure. -+ * @cb: the callback to be invoked when the notification arrives. -+ * @is_cdan: Zero/FALSE for FQDAN, non-zero/TRUE for CDAN. -+ * @id: FQID or channel ID, needed for rearm. -+ * @desired_cpu: the cpu on which the notifications will show up. -+ * @actual_cpu: the cpu the notification actually shows up. -+ * @migration_cb: callback function used for migration. -+ * @dpio_id: the dpio index. -+ * @qman64: the 64-bit context value shows up in the FQDAN/CDAN. -+ * @node: the list node. -+ * @dpio_private: the dpio object internal to dpio_service. -+ * -+ * When a FQDAN/CDAN registration is made (eg. by DPNI/DPCON/DPAI code), a -+ * context of the following type is used. The caller can embed it within a -+ * larger structure in order to add state that is tracked along with the -+ * notification (this may be useful when callbacks are invoked that pass this -+ * notification context as a parameter). -+ */ -+struct dpaa2_io_notification_ctx { -+ void (*cb)(struct dpaa2_io_notification_ctx *); -+ int is_cdan; -+ uint32_t id; -+ /* This specifies which cpu the user wants notifications to show up on -+ * (ie. to execute 'cb'). If notification-handling on that cpu is not -+ * available at the time of notification registration, the registration -+ * will fail. */ -+ int desired_cpu; -+ /* If the target platform supports cpu-hotplug or other features -+ * (related to power-management, one would expect) that can migrate IRQ -+ * handling of a given DPIO object, then this value will potentially be -+ * different to 'desired_cpu' at run-time. */ -+ int actual_cpu; -+ /* And if migration does occur and this callback is non-NULL, it will -+ * be invoked prior to any futher notification callbacks executing on -+ * 'newcpu'. Note that 'oldcpu' is what 'actual_cpu' was prior to the -+ * migration, and 'newcpu' is what it is now. Both could conceivably be -+ * different to 'desired_cpu'. */ -+ void (*migration_cb)(struct dpaa2_io_notification_ctx *, -+ int oldcpu, int newcpu); -+ /* These are returned from dpaa2_io_service_register(). -+ * 'dpio_id' is the dpaa2_io_desc::dpio_id value of the DPIO object that -+ * has been selected by the service for receiving the notifications. The -+ * caller can use this value in the MC command that attaches the FQ (or -+ * channel) of their DPNI (or DPCON, respectively) to this DPIO for -+ * notification-generation. -+ * 'qman64' is the 64-bit context value that needs to be sent in the -+ * same MC command in order to be programmed into the FQ or channel - -+ * this is the 64-bit value that shows up in the FQDAN/CDAN messages to -+ * the DPIO object, and the DPIO service specifies this value back to -+ * the caller so that the notifications that show up will be -+ * comprensible/demux-able to the DPIO service. */ -+ int dpio_id; -+ uint64_t qman64; -+ /* These fields are internal to the DPIO service once the context is -+ * registered. TBD: may require more internal state fields. */ -+ struct list_head node; -+ void *dpio_private; -+}; -+ -+/** -+ * dpaa2_io_service_register() - Prepare for servicing of FQDAN or CDAN -+ * notifications on the given DPIO service. -+ * @service: the given DPIO service. -+ * @ctx: the notification context. -+ * -+ * The MC command to attach the caller's DPNI/DPCON/DPAI device to a -+ * DPIO object is performed after this function is called. In that way, (a) the -+ * DPIO service is "ready" to handle a notification arrival (which might happen -+ * before the "attach" command to MC has returned control of execution back to -+ * the caller), and (b) the DPIO service can provide back to the caller the -+ * 'dpio_id' and 'qman64' parameters that it should pass along in the MC command -+ * in order for the DPNI/DPCON/DPAI resources to be configured to produce the -+ * right notification fields to the DPIO service. -+ * -+ * Return 0 for success, or -ENODEV for failure. -+ */ -+int dpaa2_io_service_register(struct dpaa2_io *service, -+ struct dpaa2_io_notification_ctx *ctx); -+ -+/** -+ * dpaa2_io_service_deregister - The opposite of 'register'. -+ * @service: the given DPIO service. -+ * @ctx: the notification context. -+ * -+ * Note that 'register' should be called *before* -+ * making the MC call to attach the notification-producing device to the -+ * notification-handling DPIO service, the 'unregister' function should be -+ * called *after* making the MC call to detach the notification-producing -+ * device. -+ * -+ * Return 0 for success. -+ */ -+int dpaa2_io_service_deregister(struct dpaa2_io *service, -+ struct dpaa2_io_notification_ctx *ctx); -+ -+/** -+ * dpaa2_io_service_rearm() - Rearm the notification for the given DPIO service. -+ * @service: the given DPIO service. -+ * @ctx: the notification context. -+ * -+ * Once a FQDAN/CDAN has been produced, the corresponding FQ/channel is -+ * considered "disarmed". Ie. the user can issue pull dequeue operations on that -+ * traffic source for as long as it likes. Eventually it may wish to "rearm" -+ * that source to allow it to produce another FQDAN/CDAN, that's what this -+ * function achieves. -+ * -+ * Return 0 for success, or -ENODEV if no service available, -EBUSY/-EIO for not -+ * being able to implement the rearm the notifiaton due to setting CDAN or -+ * scheduling fq. -+ */ -+int dpaa2_io_service_rearm(struct dpaa2_io *service, -+ struct dpaa2_io_notification_ctx *ctx); -+ -+/** -+ * dpaa2_io_from_registration() - Get the DPIO object from the given notification -+ * context. -+ * @ctx: the given notifiation context. -+ * @ret: the returned DPIO object. -+ * -+ * Like 'dpaa2_io_service_get_persistent()' (see below), except that the -+ * returned handle is not selected based on a 'cpu' argument, but is the same -+ * DPIO object that the given notification context is registered against. The -+ * returned handle carries a reference count, so a corresponding dpaa2_io_down() -+ * would be required when the reference is no longer needed. -+ * -+ * Return 0 for success, or -EINVAL for failure. -+ */ -+int dpaa2_io_from_registration(struct dpaa2_io_notification_ctx *ctx, -+ struct dpaa2_io **ret); -+ -+/**********************************/ -+/* General usage of DPIO services */ -+/**********************************/ -+ -+/** -+ * dpaa2_io_service_get_persistent() - Get the DPIO resource from the given -+ * notification context and cpu. -+ * @service: the DPIO service. -+ * @cpu: the cpu that the DPIO resource has stashing affinity to. -+ * @ret: the returned DPIO resource. -+ * -+ * The various DPIO interfaces can accept a "struct dpaa2_io" handle that refers -+ * to an individual DPIO object or to a whole service. In the latter case, an -+ * internal choice is made for each operation. This function supports the former -+ * case, by selecting an individual DPIO object *from* the service in order for -+ * it to be used multiple times to provide "persistence". The returned handle -+ * also carries a reference count, so a corresponding dpaa2_io_down() would be -+ * required when the reference is no longer needed. Note, a parameter of -1 for -+ * 'cpu' will select a DPIO resource that has no particular stashing affinity to -+ * any cpu (eg. one that stashes to platform cache). -+ * -+ * Return 0 for success, or -ENODEV for failure. -+ */ -+int dpaa2_io_service_get_persistent(struct dpaa2_io *service, int cpu, -+ struct dpaa2_io **ret); -+ -+/*****************/ -+/* Pull dequeues */ -+/*****************/ -+ -+/** -+ * dpaa2_io_service_pull_fq() - pull dequeue functions from a fq. -+ * @d: the given DPIO service. -+ * @fqid: the given frame queue id. -+ * @s: the dpaa2_io_store object for the result. -+ * -+ * To support DCA/order-preservation, it will be necessary to support an -+ * alternative form, because they must ultimately dequeue to DQRR rather than a -+ * user-supplied dpaa2_io_store. Furthermore, those dequeue results will -+ * "complete" using a caller-provided callback (from DQRR processing) rather -+ * than the caller explicitly looking at their dpaa2_io_store for results. Eg. -+ * the alternative form will likely take a callback parameter rather than a -+ * store parameter. Ignoring it for now to keep the picture clearer. -+ * -+ * Return 0 for success, or error code for failure. -+ */ -+int dpaa2_io_service_pull_fq(struct dpaa2_io *d, uint32_t fqid, -+ struct dpaa2_io_store *s); -+ -+/** -+ * dpaa2_io_service_pull_channel() - pull dequeue functions from a channel. -+ * @d: the given DPIO service. -+ * @channelid: the given channel id. -+ * @s: the dpaa2_io_store object for the result. -+ * -+ * To support DCA/order-preservation, it will be necessary to support an -+ * alternative form, because they must ultimately dequeue to DQRR rather than a -+ * user-supplied dpaa2_io_store. Furthermore, those dequeue results will -+ * "complete" using a caller-provided callback (from DQRR processing) rather -+ * than the caller explicitly looking at their dpaa2_io_store for results. Eg. -+ * the alternative form will likely take a callback parameter rather than a -+ * store parameter. Ignoring it for now to keep the picture clearer. -+ * -+ * Return 0 for success, or error code for failure. -+ */ -+int dpaa2_io_service_pull_channel(struct dpaa2_io *d, uint32_t channelid, -+ struct dpaa2_io_store *s); -+ -+/************/ -+/* Enqueues */ -+/************/ -+ -+/** -+ * dpaa2_io_service_enqueue_fq() - Enqueue a frame to a frame queue. -+ * @d: the given DPIO service. -+ * @fqid: the given frame queue id. -+ * @fd: the frame descriptor which is enqueued. -+ * -+ * This definition bypasses some features that are not expected to be priority-1 -+ * features, and may not be needed at all via current assumptions (QBMan's -+ * feature set is wider than the MC object model is intendeding to support, -+ * initially at least). Plus, keeping them out (for now) keeps the API view -+ * simpler. Missing features are; -+ * - enqueue confirmation (results DMA'd back to the user) -+ * - ORP -+ * - DCA/order-preservation (see note in "pull dequeues") -+ * - enqueue consumption interrupts -+ * -+ * Return 0 for successful enqueue, or -EBUSY if the enqueue ring is not ready, -+ * or -ENODEV if there is no dpio service. -+ */ -+int dpaa2_io_service_enqueue_fq(struct dpaa2_io *d, -+ uint32_t fqid, -+ const struct dpaa2_fd *fd); -+ -+/** -+ * dpaa2_io_service_enqueue_qd() - Enqueue a frame to a QD. -+ * @d: the given DPIO service. -+ * @qdid: the given queuing destination id. -+ * @prio: the given queuing priority. -+ * @qdbin: the given queuing destination bin. -+ * @fd: the frame descriptor which is enqueued. -+ * -+ * This definition bypasses some features that are not expected to be priority-1 -+ * features, and may not be needed at all via current assumptions (QBMan's -+ * feature set is wider than the MC object model is intendeding to support, -+ * initially at least). Plus, keeping them out (for now) keeps the API view -+ * simpler. Missing features are; -+ * - enqueue confirmation (results DMA'd back to the user) -+ * - ORP -+ * - DCA/order-preservation (see note in "pull dequeues") -+ * - enqueue consumption interrupts -+ * -+ * Return 0 for successful enqueue, or -EBUSY if the enqueue ring is not ready, -+ * or -ENODEV if there is no dpio service. -+ */ -+int dpaa2_io_service_enqueue_qd(struct dpaa2_io *d, -+ uint32_t qdid, uint8_t prio, uint16_t qdbin, -+ const struct dpaa2_fd *fd); -+ -+/*******************/ -+/* Buffer handling */ -+/*******************/ -+ -+/** -+ * dpaa2_io_service_release() - Release buffers to a buffer pool. -+ * @d: the given DPIO object. -+ * @bpid: the buffer pool id. -+ * @buffers: the buffers to be released. -+ * @num_buffers: the number of the buffers to be released. -+ * -+ * Return 0 for success, and negative error code for failure. -+ */ -+int dpaa2_io_service_release(struct dpaa2_io *d, -+ uint32_t bpid, -+ const uint64_t *buffers, -+ unsigned int num_buffers); -+ -+/** -+ * dpaa2_io_service_acquire() - Acquire buffers from a buffer pool. -+ * @d: the given DPIO object. -+ * @bpid: the buffer pool id. -+ * @buffers: the buffer addresses for acquired buffers. -+ * @num_buffers: the expected number of the buffers to acquire. -+ * -+ * Return a negative error code if the command failed, otherwise it returns -+ * the number of buffers acquired, which may be less than the number requested. -+ * Eg. if the buffer pool is empty, this will return zero. -+ */ -+int dpaa2_io_service_acquire(struct dpaa2_io *d, -+ uint32_t bpid, -+ uint64_t *buffers, -+ unsigned int num_buffers); -+ -+/***************/ -+/* DPIO stores */ -+/***************/ -+ -+/* These are reusable memory blocks for retrieving dequeue results into, and to -+ * assist with parsing those results once they show up. They also hide the -+ * details of how to use "tokens" to make detection of DMA results possible (ie. -+ * comparing memory before the DMA and after it) while minimising the needless -+ * clearing/rewriting of those memory locations between uses. -+ */ -+ -+/** -+ * dpaa2_io_store_create() - Create the dma memory storage for dequeue -+ * result. -+ * @max_frames: the maximum number of dequeued result for frames, must be <= 16. -+ * @dev: the device to allow mapping/unmapping the DMAable region. -+ * -+ * Constructor - max_frames must be <= 16. The user provides the -+ * device struct to allow mapping/unmapping of the DMAable region. Area for -+ * storage will be allocated during create. The size of this storage is -+ * "max_frames*sizeof(struct dpaa2_dq)". The 'dpaa2_io_store' returned is a -+ * wrapper structure allocated within the DPIO code, which owns and manages -+ * allocated store. -+ * -+ * Return dpaa2_io_store struct for successfuly created storage memory, or NULL -+ * if not getting the stroage for dequeue result in create API. -+ */ -+struct dpaa2_io_store *dpaa2_io_store_create(unsigned int max_frames, -+ struct device *dev); -+ -+/** -+ * dpaa2_io_store_destroy() - Destroy the dma memory storage for dequeue -+ * result. -+ * @s: the storage memory to be destroyed. -+ * -+ * Frees to specified storage memory. -+ */ -+void dpaa2_io_store_destroy(struct dpaa2_io_store *s); -+ -+/** -+ * dpaa2_io_store_next() - Determine when the next dequeue result is available. -+ * @s: the dpaa2_io_store object. -+ * @is_last: indicate whether this is the last frame in the pull command. -+ * -+ * Once dpaa2_io_store has been passed to a function that performs dequeues to -+ * it, like dpaa2_ni_rx(), this function can be used to determine when the next -+ * frame result is available. Once this function returns non-NULL, a subsequent -+ * call to it will try to find the *next* dequeue result. -+ * -+ * Note that if a pull-dequeue has a null result because the target FQ/channel -+ * was empty, then this function will return NULL rather than expect the caller -+ * to always check for this on his own side. As such, "is_last" can be used to -+ * differentiate between "end-of-empty-dequeue" and "still-waiting". -+ * -+ * Return dequeue result for a valid dequeue result, or NULL for empty dequeue. -+ */ -+struct dpaa2_dq *dpaa2_io_store_next(struct dpaa2_io_store *s, int *is_last); -+ -+#ifdef CONFIG_FSL_QBMAN_DEBUG -+/** -+ * dpaa2_io_query_fq_count() - Get the frame and byte count for a given fq. -+ * @d: the given DPIO object. -+ * @fqid: the id of frame queue to be queried. -+ * @fcnt: the queried frame count. -+ * @bcnt: the queried byte count. -+ * -+ * Knowing the FQ count at run-time can be useful in debugging situations. -+ * The instantaneous frame- and byte-count are hereby returned. -+ * -+ * Return 0 for a successful query, and negative error code if query fails. -+ */ -+int dpaa2_io_query_fq_count(struct dpaa2_io *d, uint32_t fqid, -+ uint32_t *fcnt, uint32_t *bcnt); -+ -+/** -+ * dpaa2_io_query_bp_count() - Query the number of buffers currenty in a -+ * buffer pool. -+ * @d: the given DPIO object. -+ * @bpid: the index of buffer pool to be queried. -+ * @num: the queried number of buffers in the buffer pool. -+ * -+ * Return 0 for a sucessful query, and negative error code if query fails. -+ */ -+int dpaa2_io_query_bp_count(struct dpaa2_io *d, uint32_t bpid, -+ uint32_t *num); -+#endif -+#endif /* __FSL_DPAA2_IO_H */ -diff --git a/drivers/staging/fsl-mc/include/mc-cmd.h b/drivers/staging/fsl-mc/include/mc-cmd.h -new file mode 100644 -index 0000000..00f0b74 ---- /dev/null -+++ b/drivers/staging/fsl-mc/include/mc-cmd.h -@@ -0,0 +1,133 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef __FSL_MC_CMD_H -+#define __FSL_MC_CMD_H -+ -+#define MC_CMD_NUM_OF_PARAMS 7 -+ -+#define MAKE_UMASK64(_width) \ -+ ((uint64_t)((_width) < 64 ? ((uint64_t)1 << (_width)) - 1 : \ -+ (uint64_t)-1)) -+ -+static inline uint64_t mc_enc(int lsoffset, int width, uint64_t val) -+{ -+ return (uint64_t)(((uint64_t)val & MAKE_UMASK64(width)) << lsoffset); -+} -+ -+static inline uint64_t mc_dec(uint64_t val, int lsoffset, int width) -+{ -+ return (uint64_t)((val >> lsoffset) & MAKE_UMASK64(width)); -+} -+ -+struct mc_command { -+ uint64_t header; -+ uint64_t params[MC_CMD_NUM_OF_PARAMS]; -+}; -+ -+enum mc_cmd_status { -+ MC_CMD_STATUS_OK = 0x0, /* Completed successfully */ -+ MC_CMD_STATUS_READY = 0x1, /* Ready to be processed */ -+ MC_CMD_STATUS_AUTH_ERR = 0x3, /* Authentication error */ -+ MC_CMD_STATUS_NO_PRIVILEGE = 0x4, /* No privilege */ -+ MC_CMD_STATUS_DMA_ERR = 0x5, /* DMA or I/O error */ -+ MC_CMD_STATUS_CONFIG_ERR = 0x6, /* Configuration error */ -+ MC_CMD_STATUS_TIMEOUT = 0x7, /* Operation timed out */ -+ MC_CMD_STATUS_NO_RESOURCE = 0x8, /* No resources */ -+ MC_CMD_STATUS_NO_MEMORY = 0x9, /* No memory available */ -+ MC_CMD_STATUS_BUSY = 0xA, /* Device is busy */ -+ MC_CMD_STATUS_UNSUPPORTED_OP = 0xB, /* Unsupported operation */ -+ MC_CMD_STATUS_INVALID_STATE = 0xC /* Invalid state */ -+}; -+ -+/* -+ * MC command flags -+ */ -+ -+/* High priority flag */ -+#define MC_CMD_FLAG_PRI 0x00008000 -+/* Command completion flag */ -+#define MC_CMD_FLAG_INTR_DIS 0x01000000 -+ -+/* TODO Remove following two defines after completion of flib 8.0.0 -+integration */ -+#define MC_CMD_PRI_LOW 0 /*!< Low Priority command indication */ -+#define MC_CMD_PRI_HIGH 1 /*!< High Priority command indication */ -+ -+#define MC_CMD_HDR_CMDID_O 52 /* Command ID field offset */ -+#define MC_CMD_HDR_CMDID_S 12 /* Command ID field size */ -+#define MC_CMD_HDR_TOKEN_O 38 /* Token field offset */ -+#define MC_CMD_HDR_TOKEN_S 10 /* Token field size */ -+#define MC_CMD_HDR_STATUS_O 16 /* Status field offset */ -+#define MC_CMD_HDR_STATUS_S 8 /* Status field size*/ -+#define MC_CMD_HDR_FLAGS_O 0 /* Flags field offset */ -+#define MC_CMD_HDR_FLAGS_S 32 /* Flags field size*/ -+#define MC_CMD_HDR_FLAGS_MASK 0xFF00FF00 /* Command flags mask */ -+ -+#define MC_CMD_HDR_READ_STATUS(_hdr) \ -+ ((enum mc_cmd_status)mc_dec((_hdr), \ -+ MC_CMD_HDR_STATUS_O, MC_CMD_HDR_STATUS_S)) -+ -+#define MC_CMD_HDR_READ_TOKEN(_hdr) \ -+ ((uint16_t)mc_dec((_hdr), MC_CMD_HDR_TOKEN_O, MC_CMD_HDR_TOKEN_S)) -+ -+#define MC_CMD_HDR_READ_FLAGS(_hdr) \ -+ ((uint32_t)mc_dec((_hdr), MC_CMD_HDR_FLAGS_O, MC_CMD_HDR_FLAGS_S)) -+ -+#define MC_PREP_OP(_ext, _param, _offset, _width, _type, _arg) \ -+ ((_ext)[_param] |= cpu_to_le64(mc_enc((_offset), (_width), _arg))) -+ -+#define MC_EXT_OP(_ext, _param, _offset, _width, _type, _arg) \ -+ (_arg = (_type)mc_dec(cpu_to_le64(_ext[_param]), (_offset), (_width))) -+ -+#define MC_CMD_OP(_cmd, _param, _offset, _width, _type, _arg) \ -+ ((_cmd).params[_param] |= mc_enc((_offset), (_width), _arg)) -+ -+#define MC_RSP_OP(_cmd, _param, _offset, _width, _type, _arg) \ -+ (_arg = (_type)mc_dec(_cmd.params[_param], (_offset), (_width))) -+ -+static inline uint64_t mc_encode_cmd_header(uint16_t cmd_id, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ uint64_t hdr; -+ -+ hdr = mc_enc(MC_CMD_HDR_CMDID_O, MC_CMD_HDR_CMDID_S, cmd_id); -+ hdr |= mc_enc(MC_CMD_HDR_FLAGS_O, MC_CMD_HDR_FLAGS_S, -+ (cmd_flags & MC_CMD_HDR_FLAGS_MASK)); -+ hdr |= mc_enc(MC_CMD_HDR_TOKEN_O, MC_CMD_HDR_TOKEN_S, token); -+ hdr |= mc_enc(MC_CMD_HDR_STATUS_O, MC_CMD_HDR_STATUS_S, -+ MC_CMD_STATUS_READY); -+ -+ return hdr; -+} -+ -+#endif /* __FSL_MC_CMD_H */ -diff --git a/drivers/staging/fsl-mc/include/mc-private.h b/drivers/staging/fsl-mc/include/mc-private.h -new file mode 100644 -index 0000000..58ed441 ---- /dev/null -+++ b/drivers/staging/fsl-mc/include/mc-private.h -@@ -0,0 +1,168 @@ -+/* -+ * Freescale Management Complex (MC) bus private declarations -+ * -+ * Copyright (C) 2014 Freescale Semiconductor, Inc. -+ * Author: German Rivera -+ * -+ * This file is licensed under the terms of the GNU General Public -+ * License version 2. This program is licensed "as is" without any -+ * warranty of any kind, whether express or implied. -+ */ -+#ifndef _FSL_MC_PRIVATE_H_ -+#define _FSL_MC_PRIVATE_H_ -+ -+#include "../include/mc.h" -+#include -+#include -+ -+#define FSL_MC_DPRC_DRIVER_NAME "fsl_mc_dprc" -+ -+#define FSL_MC_DEVICE_MATCH(_mc_dev, _obj_desc) \ -+ (strcmp((_mc_dev)->obj_desc.type, (_obj_desc)->type) == 0 && \ -+ (_mc_dev)->obj_desc.id == (_obj_desc)->id) -+ -+#define FSL_MC_IS_ALLOCATABLE(_obj_type) \ -+ (strcmp(_obj_type, "dpbp") == 0 || \ -+ strcmp(_obj_type, "dpmcp") == 0 || \ -+ strcmp(_obj_type, "dpcon") == 0) -+ -+/** -+ * Maximum number of total IRQs that can be pre-allocated for an MC bus' -+ * IRQ pool -+ */ -+#define FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS 256 -+ -+/** -+ * Maximum number of extra IRQs pre-reallocated for an MC bus' IRQ pool, -+ * to be used by dynamically created MC objects -+ */ -+#define FSL_MC_IRQ_POOL_MAX_EXTRA_IRQS 64 -+ -+/** -+ * struct fsl_mc - Private data of a "fsl,qoriq-mc" platform device -+ * @root_mc_bus_dev: MC object device representing the root DPRC -+ * @irq_domain: IRQ domain for the fsl-mc bus type -+ * @gic_supported: boolean flag that indicates if the GIC interrupt controller -+ * is supported. -+ * @num_translation_ranges: number of entries in addr_translation_ranges -+ * @addr_translation_ranges: array of bus to system address translation ranges -+ */ -+struct fsl_mc { -+ struct fsl_mc_device *root_mc_bus_dev; -+ struct irq_domain *irq_domain; -+ bool gic_supported; -+ uint8_t num_translation_ranges; -+ struct fsl_mc_addr_translation_range *translation_ranges; -+}; -+ -+/** -+ * enum mc_region_types - Types of MC MMIO regions -+ */ -+enum fsl_mc_region_types { -+ FSL_MC_PORTAL = 0x0, -+ FSL_QBMAN_PORTAL, -+ -+ /* -+ * New offset types must be added above this entry -+ */ -+ FSL_NUM_MC_OFFSET_TYPES -+}; -+ -+/** -+ * struct fsl_mc_addr_translation_range - bus to system address translation -+ * range -+ * @mc_region_type: Type of MC region for the range being translated -+ * @start_mc_offset: Start MC offset of the range being translated -+ * @end_mc_offset: MC offset of the first byte after the range (last MC -+ * offset of the range is end_mc_offset - 1) -+ * @start_phys_addr: system physical address corresponding to start_mc_addr -+ */ -+struct fsl_mc_addr_translation_range { -+ enum fsl_mc_region_types mc_region_type; -+ uint64_t start_mc_offset; -+ uint64_t end_mc_offset; -+ phys_addr_t start_phys_addr; -+}; -+ -+/** -+ * struct fsl_mc_resource_pool - Pool of MC resources of a given -+ * type -+ * @type: type of resources in the pool -+ * @max_count: maximum number of resources in the pool -+ * @free_count: number of free resources in the pool -+ * @mutex: mutex to serialize access to the pool's free list -+ * @free_list: anchor node of list of free resources in the pool -+ * @mc_bus: pointer to the MC bus that owns this resource pool -+ */ -+struct fsl_mc_resource_pool { -+ enum fsl_mc_pool_type type; -+ int16_t max_count; -+ int16_t free_count; -+ struct mutex mutex; /* serializes access to free_list */ -+ struct list_head free_list; -+ struct fsl_mc_bus *mc_bus; -+}; -+ -+/** -+ * struct fsl_mc_bus - logical bus that corresponds to a physical DPRC -+ * @mc_dev: fsl-mc device for the bus device itself. -+ * @resource_pools: array of resource pools (one pool per resource type) -+ * for this MC bus. These resources represent allocatable entities -+ * from the physical DPRC. -+ * @atomic_mc_io: mc_io object to be used to send DPRC commands to the MC -+ * in atomic context (e.g., when programming MSIs in program_msi_at_mc()). -+ * @atomic_dprc_handle: DPRC handle opened using the atomic_mc_io's portal. -+ * @irq_resources: Pointer to array of IRQ objects for the IRQ pool. -+ * @scan_mutex: Serializes bus scanning -+ * @dprc_attr: DPRC attributes -+ */ -+struct fsl_mc_bus { -+ struct fsl_mc_device mc_dev; -+ struct fsl_mc_resource_pool resource_pools[FSL_MC_NUM_POOL_TYPES]; -+ struct fsl_mc_device_irq *irq_resources; -+ struct fsl_mc_io *atomic_mc_io; -+ uint16_t atomic_dprc_handle; -+ struct mutex scan_mutex; /* serializes bus scanning */ -+ struct dprc_attributes dprc_attr; -+}; -+ -+#define to_fsl_mc_bus(_mc_dev) \ -+ container_of(_mc_dev, struct fsl_mc_bus, mc_dev) -+ -+int __must_check fsl_mc_device_add(struct dprc_obj_desc *obj_desc, -+ struct fsl_mc_io *mc_io, -+ struct device *parent_dev, -+ const char *driver_override, -+ struct fsl_mc_device **new_mc_dev); -+ -+void fsl_mc_device_remove(struct fsl_mc_device *mc_dev); -+ -+int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev, -+ const char *driver_override, -+ unsigned int *total_irq_count); -+ -+int __init dprc_driver_init(void); -+ -+void dprc_driver_exit(void); -+ -+int __init fsl_mc_allocator_driver_init(void); -+ -+void __exit fsl_mc_allocator_driver_exit(void); -+ -+int __must_check fsl_mc_resource_allocate(struct fsl_mc_bus *mc_bus, -+ enum fsl_mc_pool_type pool_type, -+ struct fsl_mc_resource -+ **new_resource); -+ -+void fsl_mc_resource_free(struct fsl_mc_resource *resource); -+ -+int __must_check fsl_mc_populate_irq_pool(struct fsl_mc_bus *mc_bus, -+ unsigned int irq_count); -+ -+void fsl_mc_cleanup_irq_pool(struct fsl_mc_bus *mc_bus); -+ -+void dprc_init_all_resource_pools(struct fsl_mc_device *mc_bus_dev); -+ -+void dprc_cleanup_all_resource_pools(struct fsl_mc_device *mc_bus_dev); -+ -+#endif /* _FSL_MC_PRIVATE_H_ */ -diff --git a/drivers/staging/fsl-mc/include/mc-sys.h b/drivers/staging/fsl-mc/include/mc-sys.h -new file mode 100644 -index 0000000..b08df85 ---- /dev/null -+++ b/drivers/staging/fsl-mc/include/mc-sys.h -@@ -0,0 +1,128 @@ -+/* Copyright 2013-2014 Freescale Semiconductor Inc. -+ * -+ * Interface of the I/O services to send MC commands to the MC hardware -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+ -+#ifndef _FSL_MC_SYS_H -+#define _FSL_MC_SYS_H -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/** -+ * Bit masks for a MC I/O object (struct fsl_mc_io) flags -+ */ -+#define FSL_MC_IO_ATOMIC_CONTEXT_PORTAL 0x0001 -+ -+struct fsl_mc_resource; -+struct mc_command; -+ -+/** -+ * struct fsl_mc_io - MC I/O object to be passed-in to mc_send_command() -+ * @dev: device associated with this Mc I/O object -+ * @flags: flags for mc_send_command() -+ * @portal_size: MC command portal size in bytes -+ * @portal_phys_addr: MC command portal physical address -+ * @portal_virt_addr: MC command portal virtual address -+ * @dpmcp_dev: pointer to the DPMCP device associated with the MC portal. -+ * @mc_command_done_irq_armed: Flag indicating that the MC command done IRQ -+ * is currently armed. -+ * @mc_command_done_completion: Completion variable to be signaled when an MC -+ * command sent to the MC fw is completed. -+ * -+ * Fields are only meaningful if the FSL_MC_IO_ATOMIC_CONTEXT_PORTAL flag is not -+ * set: -+ * @mutex: Mutex to serialize mc_send_command() calls that use the same MC -+ * portal, if the fsl_mc_io object was created with the -+ * FSL_MC_IO_ATOMIC_CONTEXT_PORTAL flag off. mc_send_command() calls for this -+ * fsl_mc_io object must be made only from non-atomic context. -+ * @mc_command_done_completion: Linux completion variable to be signaled -+ * when a DPMCP command completion interrupts is received. -+ * @mc_command_done_irq_armed: Boolean flag that indicates if interrupts have -+ * been successfully configured for the corresponding DPMCP object. -+ * -+ * Fields are only meaningful if the FSL_MC_IO_ATOMIC_CONTEXT_PORTAL flag is -+ * set: -+ * @spinlock: Spinlock to serialize mc_send_command() calls that use the same MC -+ * portal, if the fsl_mc_io object was created with the -+ * FSL_MC_IO_ATOMIC_CONTEXT_PORTAL flag on. mc_send_command() calls for this -+ * fsl_mc_io object can be made from atomic or non-atomic context. -+ */ -+struct fsl_mc_io { -+ struct device *dev; -+ uint16_t flags; -+ uint16_t portal_size; -+ phys_addr_t portal_phys_addr; -+ void __iomem *portal_virt_addr; -+ struct fsl_mc_device *dpmcp_dev; -+ union { -+ /* -+ * These fields are only meaningful if the -+ * FSL_MC_IO_ATOMIC_CONTEXT_PORTAL flag is not set -+ */ -+ struct { -+ struct mutex mutex; /* serializes mc_send_command() */ -+ struct completion mc_command_done_completion; -+ bool mc_command_done_irq_armed; -+ }; -+ -+ /* -+ * This field is only meaningful if the -+ * FSL_MC_IO_ATOMIC_CONTEXT_PORTAL flag is set -+ */ -+ spinlock_t spinlock; /* serializes mc_send_command() */ -+ }; -+}; -+ -+int __must_check fsl_create_mc_io(struct device *dev, -+ phys_addr_t mc_portal_phys_addr, -+ uint32_t mc_portal_size, -+ struct fsl_mc_device *dpmcp_dev, -+ uint32_t flags, struct fsl_mc_io **new_mc_io); -+ -+void fsl_destroy_mc_io(struct fsl_mc_io *mc_io); -+ -+int fsl_mc_io_set_dpmcp(struct fsl_mc_io *mc_io, -+ struct fsl_mc_device *dpmcp_dev); -+ -+void fsl_mc_io_unset_dpmcp(struct fsl_mc_io *mc_io); -+ -+int fsl_mc_io_setup_dpmcp_irq(struct fsl_mc_io *mc_io); -+ -+int mc_send_command(struct fsl_mc_io *mc_io, struct mc_command *cmd); -+ -+#endif /* _FSL_MC_SYS_H */ -diff --git a/drivers/staging/fsl-mc/include/mc.h b/drivers/staging/fsl-mc/include/mc.h -new file mode 100644 -index 0000000..bbeb121 ---- /dev/null -+++ b/drivers/staging/fsl-mc/include/mc.h -@@ -0,0 +1,244 @@ -+/* -+ * Freescale Management Complex (MC) bus public interface -+ * -+ * Copyright (C) 2014 Freescale Semiconductor, Inc. -+ * Author: German Rivera -+ * -+ * This file is licensed under the terms of the GNU General Public -+ * License version 2. This program is licensed "as is" without any -+ * warranty of any kind, whether express or implied. -+ */ -+#ifndef _FSL_MC_H_ -+#define _FSL_MC_H_ -+ -+#include -+#include -+#include -+#include -+#include -+#include "../include/dprc.h" -+ -+#define FSL_MC_VENDOR_FREESCALE 0x1957 -+ -+struct fsl_mc_device; -+struct fsl_mc_io; -+ -+/** -+ * struct fsl_mc_driver - MC object device driver object -+ * @driver: Generic device driver -+ * @match_id_table: table of supported device matching Ids -+ * @probe: Function called when a device is added -+ * @remove: Function called when a device is removed -+ * @shutdown: Function called at shutdown time to quiesce the device -+ * @suspend: Function called when a device is stopped -+ * @resume: Function called when a device is resumed -+ * -+ * Generic DPAA device driver object for device drivers that are registered -+ * with a DPRC bus. This structure is to be embedded in each device-specific -+ * driver structure. -+ */ -+struct fsl_mc_driver { -+ struct device_driver driver; -+ const struct fsl_mc_device_match_id *match_id_table; -+ int (*probe)(struct fsl_mc_device *dev); -+ int (*remove)(struct fsl_mc_device *dev); -+ void (*shutdown)(struct fsl_mc_device *dev); -+ int (*suspend)(struct fsl_mc_device *dev, pm_message_t state); -+ int (*resume)(struct fsl_mc_device *dev); -+}; -+ -+#define to_fsl_mc_driver(_drv) \ -+ container_of(_drv, struct fsl_mc_driver, driver) -+ -+/** -+ * struct fsl_mc_device_match_id - MC object device Id entry for driver matching -+ * @vendor: vendor ID -+ * @obj_type: MC object type -+ * @ver_major: MC object version major number -+ * @ver_minor: MC object version minor number -+ * -+ * Type of entries in the "device Id" table for MC object devices supported by -+ * a MC object device driver. The last entry of the table has vendor set to 0x0 -+ */ -+struct fsl_mc_device_match_id { -+ uint16_t vendor; -+ const char obj_type[16]; -+ uint32_t ver_major; -+ uint32_t ver_minor; -+}; -+ -+/** -+ * enum fsl_mc_pool_type - Types of allocatable MC bus resources -+ * -+ * Entries in these enum are used as indices in the array of resource -+ * pools of an fsl_mc_bus object. -+ */ -+enum fsl_mc_pool_type { -+ FSL_MC_POOL_DPMCP = 0x0, /* corresponds to "dpmcp" in the MC */ -+ FSL_MC_POOL_DPBP, /* corresponds to "dpbp" in the MC */ -+ FSL_MC_POOL_DPCON, /* corresponds to "dpcon" in the MC */ -+ FSL_MC_POOL_IRQ, -+ -+ /* -+ * NOTE: New resource pool types must be added before this entry -+ */ -+ FSL_MC_NUM_POOL_TYPES -+}; -+ -+/** -+ * struct fsl_mc_resource - MC generic resource -+ * @type: type of resource -+ * @id: unique MC resource Id within the resources of the same type -+ * @data: pointer to resource-specific data if the resource is currently -+ * allocated, or NULL if the resource is not currently allocated. -+ * @parent_pool: pointer to the parent resource pool from which this -+ * resource is allocated from. -+ * @node: Node in the free list of the corresponding resource pool -+ * -+ * NOTE: This structure is to be embedded as a field of specific -+ * MC resource structures. -+ */ -+struct fsl_mc_resource { -+ enum fsl_mc_pool_type type; -+ int32_t id; -+ void *data; -+ struct fsl_mc_resource_pool *parent_pool; -+ struct list_head node; -+}; -+ -+/** -+ * struct fsl_mc_device_irq - MC object device message-based interrupt -+ * @msi_paddr: message-based interrupt physical address -+ * @msi_value: message-based interrupt data value -+ * @irq_number: Linux IRQ number assigned to the interrupt -+ * @mc_dev: MC object device that owns this interrupt -+ * @dev_irq_index: device-relative IRQ index -+ * @resource: MC generic resource associated with the interrupt -+ */ -+struct fsl_mc_device_irq { -+ phys_addr_t msi_paddr; -+ uint32_t msi_value; -+ uint32_t irq_number; -+ struct fsl_mc_device *mc_dev; -+ uint8_t dev_irq_index; -+ struct fsl_mc_resource resource; -+}; -+ -+#define to_fsl_mc_irq(_mc_resource) \ -+ container_of(_mc_resource, struct fsl_mc_device_irq, resource) -+ -+/** -+ * Bit masks for a MC object device (struct fsl_mc_device) flags -+ */ -+#define FSL_MC_IS_DPRC 0x0001 -+ -+/** -+ * root dprc's parent is a platform device -+ * that platform device's bus type is platform_bus_type. -+ */ -+#define is_root_dprc(dev) \ -+ ((to_fsl_mc_device(dev)->flags & FSL_MC_IS_DPRC) && \ -+ ((dev)->bus == &fsl_mc_bus_type) && \ -+ ((dev)->parent->bus == &platform_bus_type)) -+ -+/** -+ * Default DMA mask for devices on a fsl-mc bus -+ */ -+#define FSL_MC_DEFAULT_DMA_MASK (~0ULL) -+ -+/** -+ * struct fsl_mc_device - MC object device object -+ * @dev: Linux driver model device object -+ * @dma_mask: Default DMA mask -+ * @flags: MC object device flags -+ * @icid: Isolation context ID for the device -+ * @mc_handle: MC handle for the corresponding MC object opened -+ * @mc_io: Pointer to MC IO object assigned to this device or -+ * NULL if none. -+ * @obj_desc: MC description of the DPAA device -+ * @regions: pointer to array of MMIO region entries -+ * @irqs: pointer to array of pointers to interrupts allocated to this device -+ * @resource: generic resource associated with this MC object device, if any. -+ * @driver_override: Driver name to force a match -+ * -+ * Generic device object for MC object devices that are "attached" to a -+ * MC bus. -+ * -+ * NOTES: -+ * - For a non-DPRC object its icid is the same as its parent DPRC's icid. -+ * - The SMMU notifier callback gets invoked after device_add() has been -+ * called for an MC object device, but before the device-specific probe -+ * callback gets called. -+ * - DP_OBJ_DPRC objects are the only MC objects that have built-in MC -+ * portals. For all other MC objects, their device drivers are responsible for -+ * allocating MC portals for them by calling fsl_mc_portal_allocate(). -+ * - Some types of MC objects (e.g., DP_OBJ_DPBP, DP_OBJ_DPCON) are -+ * treated as resources that can be allocated/deallocated from the -+ * corresponding resource pool in the object's parent DPRC, using the -+ * fsl_mc_object_allocate()/fsl_mc_object_free() functions. These MC objects -+ * are known as "allocatable" objects. For them, the corresponding -+ * fsl_mc_device's 'resource' points to the associated resource object. -+ * For MC objects that are not allocatable (e.g., DP_OBJ_DPRC, DP_OBJ_DPNI), -+ * 'resource' is NULL. -+ */ -+struct fsl_mc_device { -+ struct device dev; -+ uint64_t dma_mask; -+ uint16_t flags; -+ uint16_t icid; -+ uint16_t mc_handle; -+ struct fsl_mc_io *mc_io; -+ struct dprc_obj_desc obj_desc; -+ struct resource *regions; -+ struct fsl_mc_device_irq **irqs; -+ struct fsl_mc_resource *resource; -+ const char *driver_override; -+}; -+ -+#define to_fsl_mc_device(_dev) \ -+ container_of(_dev, struct fsl_mc_device, dev) -+ -+/* -+ * module_fsl_mc_driver() - Helper macro for drivers that don't do -+ * anything special in module init/exit. This eliminates a lot of -+ * boilerplate. Each module may only use this macro once, and -+ * calling it replaces module_init() and module_exit() -+ */ -+#define module_fsl_mc_driver(__fsl_mc_driver) \ -+ module_driver(__fsl_mc_driver, fsl_mc_driver_register, \ -+ fsl_mc_driver_unregister) -+ -+/* -+ * Macro to avoid include chaining to get THIS_MODULE -+ */ -+#define fsl_mc_driver_register(drv) \ -+ __fsl_mc_driver_register(drv, THIS_MODULE) -+ -+int __must_check __fsl_mc_driver_register(struct fsl_mc_driver *fsl_mc_driver, -+ struct module *owner); -+ -+void fsl_mc_driver_unregister(struct fsl_mc_driver *driver); -+ -+bool fsl_mc_interrupts_supported(void); -+ -+int __must_check fsl_mc_portal_allocate(struct fsl_mc_device *mc_dev, -+ uint16_t mc_io_flags, -+ struct fsl_mc_io **new_mc_io); -+ -+void fsl_mc_portal_free(struct fsl_mc_io *mc_io); -+ -+int fsl_mc_portal_reset(struct fsl_mc_io *mc_io); -+ -+int __must_check fsl_mc_object_allocate(struct fsl_mc_device *mc_dev, -+ enum fsl_mc_pool_type pool_type, -+ struct fsl_mc_device **new_mc_adev); -+ -+void fsl_mc_object_free(struct fsl_mc_device *mc_adev); -+ -+int __must_check fsl_mc_allocate_irqs(struct fsl_mc_device *mc_dev); -+ -+void fsl_mc_free_irqs(struct fsl_mc_device *mc_dev); -+ -+extern struct bus_type fsl_mc_bus_type; -+ -+#endif /* _FSL_MC_H_ */ -diff --git a/drivers/staging/fsl-mc/include/net.h b/drivers/staging/fsl-mc/include/net.h -new file mode 100644 -index 0000000..7480f6a ---- /dev/null -+++ b/drivers/staging/fsl-mc/include/net.h -@@ -0,0 +1,481 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef __FSL_NET_H -+#define __FSL_NET_H -+ -+#define LAST_HDR_INDEX 0xFFFFFFFF -+ -+/*****************************************************************************/ -+/* Protocol fields */ -+/*****************************************************************************/ -+ -+/************************* Ethernet fields *********************************/ -+#define NH_FLD_ETH_DA (1) -+#define NH_FLD_ETH_SA (NH_FLD_ETH_DA << 1) -+#define NH_FLD_ETH_LENGTH (NH_FLD_ETH_DA << 2) -+#define NH_FLD_ETH_TYPE (NH_FLD_ETH_DA << 3) -+#define NH_FLD_ETH_FINAL_CKSUM (NH_FLD_ETH_DA << 4) -+#define NH_FLD_ETH_PADDING (NH_FLD_ETH_DA << 5) -+#define NH_FLD_ETH_ALL_FIELDS ((NH_FLD_ETH_DA << 6) - 1) -+ -+#define NH_FLD_ETH_ADDR_SIZE 6 -+ -+/*************************** VLAN fields ***********************************/ -+#define NH_FLD_VLAN_VPRI (1) -+#define NH_FLD_VLAN_CFI (NH_FLD_VLAN_VPRI << 1) -+#define NH_FLD_VLAN_VID (NH_FLD_VLAN_VPRI << 2) -+#define NH_FLD_VLAN_LENGTH (NH_FLD_VLAN_VPRI << 3) -+#define NH_FLD_VLAN_TYPE (NH_FLD_VLAN_VPRI << 4) -+#define NH_FLD_VLAN_ALL_FIELDS ((NH_FLD_VLAN_VPRI << 5) - 1) -+ -+#define NH_FLD_VLAN_TCI (NH_FLD_VLAN_VPRI | \ -+ NH_FLD_VLAN_CFI | \ -+ NH_FLD_VLAN_VID) -+ -+/************************ IP (generic) fields ******************************/ -+#define NH_FLD_IP_VER (1) -+#define NH_FLD_IP_DSCP (NH_FLD_IP_VER << 2) -+#define NH_FLD_IP_ECN (NH_FLD_IP_VER << 3) -+#define NH_FLD_IP_PROTO (NH_FLD_IP_VER << 4) -+#define NH_FLD_IP_SRC (NH_FLD_IP_VER << 5) -+#define NH_FLD_IP_DST (NH_FLD_IP_VER << 6) -+#define NH_FLD_IP_TOS_TC (NH_FLD_IP_VER << 7) -+#define NH_FLD_IP_ID (NH_FLD_IP_VER << 8) -+#define NH_FLD_IP_ALL_FIELDS ((NH_FLD_IP_VER << 9) - 1) -+ -+#define NH_FLD_IP_PROTO_SIZE 1 -+ -+/***************************** IPV4 fields *********************************/ -+#define NH_FLD_IPV4_VER (1) -+#define NH_FLD_IPV4_HDR_LEN (NH_FLD_IPV4_VER << 1) -+#define NH_FLD_IPV4_TOS (NH_FLD_IPV4_VER << 2) -+#define NH_FLD_IPV4_TOTAL_LEN (NH_FLD_IPV4_VER << 3) -+#define NH_FLD_IPV4_ID (NH_FLD_IPV4_VER << 4) -+#define NH_FLD_IPV4_FLAG_D (NH_FLD_IPV4_VER << 5) -+#define NH_FLD_IPV4_FLAG_M (NH_FLD_IPV4_VER << 6) -+#define NH_FLD_IPV4_OFFSET (NH_FLD_IPV4_VER << 7) -+#define NH_FLD_IPV4_TTL (NH_FLD_IPV4_VER << 8) -+#define NH_FLD_IPV4_PROTO (NH_FLD_IPV4_VER << 9) -+#define NH_FLD_IPV4_CKSUM (NH_FLD_IPV4_VER << 10) -+#define NH_FLD_IPV4_SRC_IP (NH_FLD_IPV4_VER << 11) -+#define NH_FLD_IPV4_DST_IP (NH_FLD_IPV4_VER << 12) -+#define NH_FLD_IPV4_OPTS (NH_FLD_IPV4_VER << 13) -+#define NH_FLD_IPV4_OPTS_COUNT (NH_FLD_IPV4_VER << 14) -+#define NH_FLD_IPV4_ALL_FIELDS ((NH_FLD_IPV4_VER << 15) - 1) -+ -+#define NH_FLD_IPV4_ADDR_SIZE 4 -+#define NH_FLD_IPV4_PROTO_SIZE 1 -+ -+/***************************** IPV6 fields *********************************/ -+#define NH_FLD_IPV6_VER (1) -+#define NH_FLD_IPV6_TC (NH_FLD_IPV6_VER << 1) -+#define NH_FLD_IPV6_SRC_IP (NH_FLD_IPV6_VER << 2) -+#define NH_FLD_IPV6_DST_IP (NH_FLD_IPV6_VER << 3) -+#define NH_FLD_IPV6_NEXT_HDR (NH_FLD_IPV6_VER << 4) -+#define NH_FLD_IPV6_FL (NH_FLD_IPV6_VER << 5) -+#define NH_FLD_IPV6_HOP_LIMIT (NH_FLD_IPV6_VER << 6) -+#define NH_FLD_IPV6_ID (NH_FLD_IPV6_VER << 7) -+#define NH_FLD_IPV6_ALL_FIELDS ((NH_FLD_IPV6_VER << 8) - 1) -+ -+#define NH_FLD_IPV6_ADDR_SIZE 16 -+#define NH_FLD_IPV6_NEXT_HDR_SIZE 1 -+ -+/***************************** ICMP fields *********************************/ -+#define NH_FLD_ICMP_TYPE (1) -+#define NH_FLD_ICMP_CODE (NH_FLD_ICMP_TYPE << 1) -+#define NH_FLD_ICMP_CKSUM (NH_FLD_ICMP_TYPE << 2) -+#define NH_FLD_ICMP_ID (NH_FLD_ICMP_TYPE << 3) -+#define NH_FLD_ICMP_SQ_NUM (NH_FLD_ICMP_TYPE << 4) -+#define NH_FLD_ICMP_ALL_FIELDS ((NH_FLD_ICMP_TYPE << 5) - 1) -+ -+#define NH_FLD_ICMP_CODE_SIZE 1 -+#define NH_FLD_ICMP_TYPE_SIZE 1 -+ -+/***************************** IGMP fields *********************************/ -+#define NH_FLD_IGMP_VERSION (1) -+#define NH_FLD_IGMP_TYPE (NH_FLD_IGMP_VERSION << 1) -+#define NH_FLD_IGMP_CKSUM (NH_FLD_IGMP_VERSION << 2) -+#define NH_FLD_IGMP_DATA (NH_FLD_IGMP_VERSION << 3) -+#define NH_FLD_IGMP_ALL_FIELDS ((NH_FLD_IGMP_VERSION << 4) - 1) -+ -+/***************************** TCP fields **********************************/ -+#define NH_FLD_TCP_PORT_SRC (1) -+#define NH_FLD_TCP_PORT_DST (NH_FLD_TCP_PORT_SRC << 1) -+#define NH_FLD_TCP_SEQ (NH_FLD_TCP_PORT_SRC << 2) -+#define NH_FLD_TCP_ACK (NH_FLD_TCP_PORT_SRC << 3) -+#define NH_FLD_TCP_OFFSET (NH_FLD_TCP_PORT_SRC << 4) -+#define NH_FLD_TCP_FLAGS (NH_FLD_TCP_PORT_SRC << 5) -+#define NH_FLD_TCP_WINDOW (NH_FLD_TCP_PORT_SRC << 6) -+#define NH_FLD_TCP_CKSUM (NH_FLD_TCP_PORT_SRC << 7) -+#define NH_FLD_TCP_URGPTR (NH_FLD_TCP_PORT_SRC << 8) -+#define NH_FLD_TCP_OPTS (NH_FLD_TCP_PORT_SRC << 9) -+#define NH_FLD_TCP_OPTS_COUNT (NH_FLD_TCP_PORT_SRC << 10) -+#define NH_FLD_TCP_ALL_FIELDS ((NH_FLD_TCP_PORT_SRC << 11) - 1) -+ -+#define NH_FLD_TCP_PORT_SIZE 2 -+ -+/***************************** UDP fields **********************************/ -+#define NH_FLD_UDP_PORT_SRC (1) -+#define NH_FLD_UDP_PORT_DST (NH_FLD_UDP_PORT_SRC << 1) -+#define NH_FLD_UDP_LEN (NH_FLD_UDP_PORT_SRC << 2) -+#define NH_FLD_UDP_CKSUM (NH_FLD_UDP_PORT_SRC << 3) -+#define NH_FLD_UDP_ALL_FIELDS ((NH_FLD_UDP_PORT_SRC << 4) - 1) -+ -+#define NH_FLD_UDP_PORT_SIZE 2 -+ -+/*************************** UDP-lite fields *******************************/ -+#define NH_FLD_UDP_LITE_PORT_SRC (1) -+#define NH_FLD_UDP_LITE_PORT_DST (NH_FLD_UDP_LITE_PORT_SRC << 1) -+#define NH_FLD_UDP_LITE_ALL_FIELDS \ -+ ((NH_FLD_UDP_LITE_PORT_SRC << 2) - 1) -+ -+#define NH_FLD_UDP_LITE_PORT_SIZE 2 -+ -+/*************************** UDP-encap-ESP fields **************************/ -+#define NH_FLD_UDP_ENC_ESP_PORT_SRC (1) -+#define NH_FLD_UDP_ENC_ESP_PORT_DST (NH_FLD_UDP_ENC_ESP_PORT_SRC << 1) -+#define NH_FLD_UDP_ENC_ESP_LEN (NH_FLD_UDP_ENC_ESP_PORT_SRC << 2) -+#define NH_FLD_UDP_ENC_ESP_CKSUM (NH_FLD_UDP_ENC_ESP_PORT_SRC << 3) -+#define NH_FLD_UDP_ENC_ESP_SPI (NH_FLD_UDP_ENC_ESP_PORT_SRC << 4) -+#define NH_FLD_UDP_ENC_ESP_SEQUENCE_NUM (NH_FLD_UDP_ENC_ESP_PORT_SRC << 5) -+#define NH_FLD_UDP_ENC_ESP_ALL_FIELDS \ -+ ((NH_FLD_UDP_ENC_ESP_PORT_SRC << 6) - 1) -+ -+#define NH_FLD_UDP_ENC_ESP_PORT_SIZE 2 -+#define NH_FLD_UDP_ENC_ESP_SPI_SIZE 4 -+ -+/***************************** SCTP fields *********************************/ -+#define NH_FLD_SCTP_PORT_SRC (1) -+#define NH_FLD_SCTP_PORT_DST (NH_FLD_SCTP_PORT_SRC << 1) -+#define NH_FLD_SCTP_VER_TAG (NH_FLD_SCTP_PORT_SRC << 2) -+#define NH_FLD_SCTP_CKSUM (NH_FLD_SCTP_PORT_SRC << 3) -+#define NH_FLD_SCTP_ALL_FIELDS ((NH_FLD_SCTP_PORT_SRC << 4) - 1) -+ -+#define NH_FLD_SCTP_PORT_SIZE 2 -+ -+/***************************** DCCP fields *********************************/ -+#define NH_FLD_DCCP_PORT_SRC (1) -+#define NH_FLD_DCCP_PORT_DST (NH_FLD_DCCP_PORT_SRC << 1) -+#define NH_FLD_DCCP_ALL_FIELDS ((NH_FLD_DCCP_PORT_SRC << 2) - 1) -+ -+#define NH_FLD_DCCP_PORT_SIZE 2 -+ -+/***************************** IPHC fields *********************************/ -+#define NH_FLD_IPHC_CID (1) -+#define NH_FLD_IPHC_CID_TYPE (NH_FLD_IPHC_CID << 1) -+#define NH_FLD_IPHC_HCINDEX (NH_FLD_IPHC_CID << 2) -+#define NH_FLD_IPHC_GEN (NH_FLD_IPHC_CID << 3) -+#define NH_FLD_IPHC_D_BIT (NH_FLD_IPHC_CID << 4) -+#define NH_FLD_IPHC_ALL_FIELDS ((NH_FLD_IPHC_CID << 5) - 1) -+ -+/***************************** SCTP fields *********************************/ -+#define NH_FLD_SCTP_CHUNK_DATA_TYPE (1) -+#define NH_FLD_SCTP_CHUNK_DATA_FLAGS (NH_FLD_SCTP_CHUNK_DATA_TYPE << 1) -+#define NH_FLD_SCTP_CHUNK_DATA_LENGTH (NH_FLD_SCTP_CHUNK_DATA_TYPE << 2) -+#define NH_FLD_SCTP_CHUNK_DATA_TSN (NH_FLD_SCTP_CHUNK_DATA_TYPE << 3) -+#define NH_FLD_SCTP_CHUNK_DATA_STREAM_ID (NH_FLD_SCTP_CHUNK_DATA_TYPE << 4) -+#define NH_FLD_SCTP_CHUNK_DATA_STREAM_SQN (NH_FLD_SCTP_CHUNK_DATA_TYPE << 5) -+#define NH_FLD_SCTP_CHUNK_DATA_PAYLOAD_PID (NH_FLD_SCTP_CHUNK_DATA_TYPE << 6) -+#define NH_FLD_SCTP_CHUNK_DATA_UNORDERED (NH_FLD_SCTP_CHUNK_DATA_TYPE << 7) -+#define NH_FLD_SCTP_CHUNK_DATA_BEGGINING (NH_FLD_SCTP_CHUNK_DATA_TYPE << 8) -+#define NH_FLD_SCTP_CHUNK_DATA_END (NH_FLD_SCTP_CHUNK_DATA_TYPE << 9) -+#define NH_FLD_SCTP_CHUNK_DATA_ALL_FIELDS \ -+ ((NH_FLD_SCTP_CHUNK_DATA_TYPE << 10) - 1) -+ -+/*************************** L2TPV2 fields *********************************/ -+#define NH_FLD_L2TPV2_TYPE_BIT (1) -+#define NH_FLD_L2TPV2_LENGTH_BIT (NH_FLD_L2TPV2_TYPE_BIT << 1) -+#define NH_FLD_L2TPV2_SEQUENCE_BIT (NH_FLD_L2TPV2_TYPE_BIT << 2) -+#define NH_FLD_L2TPV2_OFFSET_BIT (NH_FLD_L2TPV2_TYPE_BIT << 3) -+#define NH_FLD_L2TPV2_PRIORITY_BIT (NH_FLD_L2TPV2_TYPE_BIT << 4) -+#define NH_FLD_L2TPV2_VERSION (NH_FLD_L2TPV2_TYPE_BIT << 5) -+#define NH_FLD_L2TPV2_LEN (NH_FLD_L2TPV2_TYPE_BIT << 6) -+#define NH_FLD_L2TPV2_TUNNEL_ID (NH_FLD_L2TPV2_TYPE_BIT << 7) -+#define NH_FLD_L2TPV2_SESSION_ID (NH_FLD_L2TPV2_TYPE_BIT << 8) -+#define NH_FLD_L2TPV2_NS (NH_FLD_L2TPV2_TYPE_BIT << 9) -+#define NH_FLD_L2TPV2_NR (NH_FLD_L2TPV2_TYPE_BIT << 10) -+#define NH_FLD_L2TPV2_OFFSET_SIZE (NH_FLD_L2TPV2_TYPE_BIT << 11) -+#define NH_FLD_L2TPV2_FIRST_BYTE (NH_FLD_L2TPV2_TYPE_BIT << 12) -+#define NH_FLD_L2TPV2_ALL_FIELDS \ -+ ((NH_FLD_L2TPV2_TYPE_BIT << 13) - 1) -+ -+/*************************** L2TPV3 fields *********************************/ -+#define NH_FLD_L2TPV3_CTRL_TYPE_BIT (1) -+#define NH_FLD_L2TPV3_CTRL_LENGTH_BIT (NH_FLD_L2TPV3_CTRL_TYPE_BIT << 1) -+#define NH_FLD_L2TPV3_CTRL_SEQUENCE_BIT (NH_FLD_L2TPV3_CTRL_TYPE_BIT << 2) -+#define NH_FLD_L2TPV3_CTRL_VERSION (NH_FLD_L2TPV3_CTRL_TYPE_BIT << 3) -+#define NH_FLD_L2TPV3_CTRL_LENGTH (NH_FLD_L2TPV3_CTRL_TYPE_BIT << 4) -+#define NH_FLD_L2TPV3_CTRL_CONTROL (NH_FLD_L2TPV3_CTRL_TYPE_BIT << 5) -+#define NH_FLD_L2TPV3_CTRL_SENT (NH_FLD_L2TPV3_CTRL_TYPE_BIT << 6) -+#define NH_FLD_L2TPV3_CTRL_RECV (NH_FLD_L2TPV3_CTRL_TYPE_BIT << 7) -+#define NH_FLD_L2TPV3_CTRL_FIRST_BYTE (NH_FLD_L2TPV3_CTRL_TYPE_BIT << 8) -+#define NH_FLD_L2TPV3_CTRL_ALL_FIELDS \ -+ ((NH_FLD_L2TPV3_CTRL_TYPE_BIT << 9) - 1) -+ -+#define NH_FLD_L2TPV3_SESS_TYPE_BIT (1) -+#define NH_FLD_L2TPV3_SESS_VERSION (NH_FLD_L2TPV3_SESS_TYPE_BIT << 1) -+#define NH_FLD_L2TPV3_SESS_ID (NH_FLD_L2TPV3_SESS_TYPE_BIT << 2) -+#define NH_FLD_L2TPV3_SESS_COOKIE (NH_FLD_L2TPV3_SESS_TYPE_BIT << 3) -+#define NH_FLD_L2TPV3_SESS_ALL_FIELDS \ -+ ((NH_FLD_L2TPV3_SESS_TYPE_BIT << 4) - 1) -+ -+/**************************** PPP fields ***********************************/ -+#define NH_FLD_PPP_PID (1) -+#define NH_FLD_PPP_COMPRESSED (NH_FLD_PPP_PID << 1) -+#define NH_FLD_PPP_ALL_FIELDS ((NH_FLD_PPP_PID << 2) - 1) -+ -+/************************** PPPoE fields ***********************************/ -+#define NH_FLD_PPPOE_VER (1) -+#define NH_FLD_PPPOE_TYPE (NH_FLD_PPPOE_VER << 1) -+#define NH_FLD_PPPOE_CODE (NH_FLD_PPPOE_VER << 2) -+#define NH_FLD_PPPOE_SID (NH_FLD_PPPOE_VER << 3) -+#define NH_FLD_PPPOE_LEN (NH_FLD_PPPOE_VER << 4) -+#define NH_FLD_PPPOE_SESSION (NH_FLD_PPPOE_VER << 5) -+#define NH_FLD_PPPOE_PID (NH_FLD_PPPOE_VER << 6) -+#define NH_FLD_PPPOE_ALL_FIELDS ((NH_FLD_PPPOE_VER << 7) - 1) -+ -+/************************* PPP-Mux fields **********************************/ -+#define NH_FLD_PPPMUX_PID (1) -+#define NH_FLD_PPPMUX_CKSUM (NH_FLD_PPPMUX_PID << 1) -+#define NH_FLD_PPPMUX_COMPRESSED (NH_FLD_PPPMUX_PID << 2) -+#define NH_FLD_PPPMUX_ALL_FIELDS ((NH_FLD_PPPMUX_PID << 3) - 1) -+ -+/*********************** PPP-Mux sub-frame fields **************************/ -+#define NH_FLD_PPPMUX_SUBFRM_PFF (1) -+#define NH_FLD_PPPMUX_SUBFRM_LXT (NH_FLD_PPPMUX_SUBFRM_PFF << 1) -+#define NH_FLD_PPPMUX_SUBFRM_LEN (NH_FLD_PPPMUX_SUBFRM_PFF << 2) -+#define NH_FLD_PPPMUX_SUBFRM_PID (NH_FLD_PPPMUX_SUBFRM_PFF << 3) -+#define NH_FLD_PPPMUX_SUBFRM_USE_PID (NH_FLD_PPPMUX_SUBFRM_PFF << 4) -+#define NH_FLD_PPPMUX_SUBFRM_ALL_FIELDS \ -+ ((NH_FLD_PPPMUX_SUBFRM_PFF << 5) - 1) -+ -+/*************************** LLC fields ************************************/ -+#define NH_FLD_LLC_DSAP (1) -+#define NH_FLD_LLC_SSAP (NH_FLD_LLC_DSAP << 1) -+#define NH_FLD_LLC_CTRL (NH_FLD_LLC_DSAP << 2) -+#define NH_FLD_LLC_ALL_FIELDS ((NH_FLD_LLC_DSAP << 3) - 1) -+ -+/*************************** NLPID fields **********************************/ -+#define NH_FLD_NLPID_NLPID (1) -+#define NH_FLD_NLPID_ALL_FIELDS ((NH_FLD_NLPID_NLPID << 1) - 1) -+ -+/*************************** SNAP fields ***********************************/ -+#define NH_FLD_SNAP_OUI (1) -+#define NH_FLD_SNAP_PID (NH_FLD_SNAP_OUI << 1) -+#define NH_FLD_SNAP_ALL_FIELDS ((NH_FLD_SNAP_OUI << 2) - 1) -+ -+/*************************** LLC SNAP fields *******************************/ -+#define NH_FLD_LLC_SNAP_TYPE (1) -+#define NH_FLD_LLC_SNAP_ALL_FIELDS ((NH_FLD_LLC_SNAP_TYPE << 1) - 1) -+ -+#define NH_FLD_ARP_HTYPE (1) -+#define NH_FLD_ARP_PTYPE (NH_FLD_ARP_HTYPE << 1) -+#define NH_FLD_ARP_HLEN (NH_FLD_ARP_HTYPE << 2) -+#define NH_FLD_ARP_PLEN (NH_FLD_ARP_HTYPE << 3) -+#define NH_FLD_ARP_OPER (NH_FLD_ARP_HTYPE << 4) -+#define NH_FLD_ARP_SHA (NH_FLD_ARP_HTYPE << 5) -+#define NH_FLD_ARP_SPA (NH_FLD_ARP_HTYPE << 6) -+#define NH_FLD_ARP_THA (NH_FLD_ARP_HTYPE << 7) -+#define NH_FLD_ARP_TPA (NH_FLD_ARP_HTYPE << 8) -+#define NH_FLD_ARP_ALL_FIELDS ((NH_FLD_ARP_HTYPE << 9) - 1) -+ -+/*************************** RFC2684 fields ********************************/ -+#define NH_FLD_RFC2684_LLC (1) -+#define NH_FLD_RFC2684_NLPID (NH_FLD_RFC2684_LLC << 1) -+#define NH_FLD_RFC2684_OUI (NH_FLD_RFC2684_LLC << 2) -+#define NH_FLD_RFC2684_PID (NH_FLD_RFC2684_LLC << 3) -+#define NH_FLD_RFC2684_VPN_OUI (NH_FLD_RFC2684_LLC << 4) -+#define NH_FLD_RFC2684_VPN_IDX (NH_FLD_RFC2684_LLC << 5) -+#define NH_FLD_RFC2684_ALL_FIELDS ((NH_FLD_RFC2684_LLC << 6) - 1) -+ -+/*************************** User defined fields ***************************/ -+#define NH_FLD_USER_DEFINED_SRCPORT (1) -+#define NH_FLD_USER_DEFINED_PCDID (NH_FLD_USER_DEFINED_SRCPORT << 1) -+#define NH_FLD_USER_DEFINED_ALL_FIELDS \ -+ ((NH_FLD_USER_DEFINED_SRCPORT << 2) - 1) -+ -+/*************************** Payload fields ********************************/ -+#define NH_FLD_PAYLOAD_BUFFER (1) -+#define NH_FLD_PAYLOAD_SIZE (NH_FLD_PAYLOAD_BUFFER << 1) -+#define NH_FLD_MAX_FRM_SIZE (NH_FLD_PAYLOAD_BUFFER << 2) -+#define NH_FLD_MIN_FRM_SIZE (NH_FLD_PAYLOAD_BUFFER << 3) -+#define NH_FLD_PAYLOAD_TYPE (NH_FLD_PAYLOAD_BUFFER << 4) -+#define NH_FLD_FRAME_SIZE (NH_FLD_PAYLOAD_BUFFER << 5) -+#define NH_FLD_PAYLOAD_ALL_FIELDS ((NH_FLD_PAYLOAD_BUFFER << 6) - 1) -+ -+/*************************** GRE fields ************************************/ -+#define NH_FLD_GRE_TYPE (1) -+#define NH_FLD_GRE_ALL_FIELDS ((NH_FLD_GRE_TYPE << 1) - 1) -+ -+/*************************** MINENCAP fields *******************************/ -+#define NH_FLD_MINENCAP_SRC_IP (1) -+#define NH_FLD_MINENCAP_DST_IP (NH_FLD_MINENCAP_SRC_IP << 1) -+#define NH_FLD_MINENCAP_TYPE (NH_FLD_MINENCAP_SRC_IP << 2) -+#define NH_FLD_MINENCAP_ALL_FIELDS \ -+ ((NH_FLD_MINENCAP_SRC_IP << 3) - 1) -+ -+/*************************** IPSEC AH fields *******************************/ -+#define NH_FLD_IPSEC_AH_SPI (1) -+#define NH_FLD_IPSEC_AH_NH (NH_FLD_IPSEC_AH_SPI << 1) -+#define NH_FLD_IPSEC_AH_ALL_FIELDS ((NH_FLD_IPSEC_AH_SPI << 2) - 1) -+ -+/*************************** IPSEC ESP fields ******************************/ -+#define NH_FLD_IPSEC_ESP_SPI (1) -+#define NH_FLD_IPSEC_ESP_SEQUENCE_NUM (NH_FLD_IPSEC_ESP_SPI << 1) -+#define NH_FLD_IPSEC_ESP_ALL_FIELDS ((NH_FLD_IPSEC_ESP_SPI << 2) - 1) -+ -+#define NH_FLD_IPSEC_ESP_SPI_SIZE 4 -+ -+/*************************** MPLS fields ***********************************/ -+#define NH_FLD_MPLS_LABEL_STACK (1) -+#define NH_FLD_MPLS_LABEL_STACK_ALL_FIELDS \ -+ ((NH_FLD_MPLS_LABEL_STACK << 1) - 1) -+ -+/*************************** MACSEC fields *********************************/ -+#define NH_FLD_MACSEC_SECTAG (1) -+#define NH_FLD_MACSEC_ALL_FIELDS ((NH_FLD_MACSEC_SECTAG << 1) - 1) -+ -+/*************************** GTP fields ************************************/ -+#define NH_FLD_GTP_TEID (1) -+ -+ -+/* Protocol options */ -+ -+/* Ethernet options */ -+#define NH_OPT_ETH_BROADCAST 1 -+#define NH_OPT_ETH_MULTICAST 2 -+#define NH_OPT_ETH_UNICAST 3 -+#define NH_OPT_ETH_BPDU 4 -+ -+#define NH_ETH_IS_MULTICAST_ADDR(addr) (addr[0] & 0x01) -+/* also applicable for broadcast */ -+ -+/* VLAN options */ -+#define NH_OPT_VLAN_CFI 1 -+ -+/* IPV4 options */ -+#define NH_OPT_IPV4_UNICAST 1 -+#define NH_OPT_IPV4_MULTICAST 2 -+#define NH_OPT_IPV4_BROADCAST 3 -+#define NH_OPT_IPV4_OPTION 4 -+#define NH_OPT_IPV4_FRAG 5 -+#define NH_OPT_IPV4_INITIAL_FRAG 6 -+ -+/* IPV6 options */ -+#define NH_OPT_IPV6_UNICAST 1 -+#define NH_OPT_IPV6_MULTICAST 2 -+#define NH_OPT_IPV6_OPTION 3 -+#define NH_OPT_IPV6_FRAG 4 -+#define NH_OPT_IPV6_INITIAL_FRAG 5 -+ -+/* General IP options (may be used for any version) */ -+#define NH_OPT_IP_FRAG 1 -+#define NH_OPT_IP_INITIAL_FRAG 2 -+#define NH_OPT_IP_OPTION 3 -+ -+/* Minenc. options */ -+#define NH_OPT_MINENCAP_SRC_ADDR_PRESENT 1 -+ -+/* GRE. options */ -+#define NH_OPT_GRE_ROUTING_PRESENT 1 -+ -+/* TCP options */ -+#define NH_OPT_TCP_OPTIONS 1 -+#define NH_OPT_TCP_CONTROL_HIGH_BITS 2 -+#define NH_OPT_TCP_CONTROL_LOW_BITS 3 -+ -+/* CAPWAP options */ -+#define NH_OPT_CAPWAP_DTLS 1 -+ -+enum net_prot { -+ NET_PROT_NONE = 0, -+ NET_PROT_PAYLOAD, -+ NET_PROT_ETH, -+ NET_PROT_VLAN, -+ NET_PROT_IPV4, -+ NET_PROT_IPV6, -+ NET_PROT_IP, -+ NET_PROT_TCP, -+ NET_PROT_UDP, -+ NET_PROT_UDP_LITE, -+ NET_PROT_IPHC, -+ NET_PROT_SCTP, -+ NET_PROT_SCTP_CHUNK_DATA, -+ NET_PROT_PPPOE, -+ NET_PROT_PPP, -+ NET_PROT_PPPMUX, -+ NET_PROT_PPPMUX_SUBFRM, -+ NET_PROT_L2TPV2, -+ NET_PROT_L2TPV3_CTRL, -+ NET_PROT_L2TPV3_SESS, -+ NET_PROT_LLC, -+ NET_PROT_LLC_SNAP, -+ NET_PROT_NLPID, -+ NET_PROT_SNAP, -+ NET_PROT_MPLS, -+ NET_PROT_IPSEC_AH, -+ NET_PROT_IPSEC_ESP, -+ NET_PROT_UDP_ENC_ESP, /* RFC 3948 */ -+ NET_PROT_MACSEC, -+ NET_PROT_GRE, -+ NET_PROT_MINENCAP, -+ NET_PROT_DCCP, -+ NET_PROT_ICMP, -+ NET_PROT_IGMP, -+ NET_PROT_ARP, -+ NET_PROT_CAPWAP_DATA, -+ NET_PROT_CAPWAP_CTRL, -+ NET_PROT_RFC2684, -+ NET_PROT_ICMPV6, -+ NET_PROT_FCOE, -+ NET_PROT_FIP, -+ NET_PROT_ISCSI, -+ NET_PROT_GTP, -+ NET_PROT_USER_DEFINED_L2, -+ NET_PROT_USER_DEFINED_L3, -+ NET_PROT_USER_DEFINED_L4, -+ NET_PROT_USER_DEFINED_L5, -+ NET_PROT_USER_DEFINED_SHIM1, -+ NET_PROT_USER_DEFINED_SHIM2, -+ -+ NET_PROT_DUMMY_LAST -+}; -+ -+/*! IEEE8021.Q */ -+#define NH_IEEE8021Q_ETYPE 0x8100 -+#define NH_IEEE8021Q_HDR(etype, pcp, dei, vlan_id) \ -+ ((((uint32_t)(etype & 0xFFFF)) << 16) | \ -+ (((uint32_t)(pcp & 0x07)) << 13) | \ -+ (((uint32_t)(dei & 0x01)) << 12) | \ -+ (((uint32_t)(vlan_id & 0xFFF)))) -+ -+#endif /* __FSL_NET_H */ -diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c -index b9ddf0c..894894f 100644 ---- a/drivers/usb/core/config.c -+++ b/drivers/usb/core/config.c -@@ -115,7 +115,8 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, - USB_SS_MULT(desc->bmAttributes) > 3) { - dev_warn(ddev, "Isoc endpoint has Mult of %d in " - "config %d interface %d altsetting %d ep %d: " -- "setting to 3\n", desc->bmAttributes + 1, -+ "setting to 3\n", -+ USB_SS_MULT(desc->bmAttributes), - cfgno, inum, asnum, ep->desc.bEndpointAddress); - ep->ss_ep_comp.bmAttributes = 2; - } -diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c -index d7a6d8b..66be3b4 100644 ---- a/drivers/usb/core/driver.c -+++ b/drivers/usb/core/driver.c -@@ -499,11 +499,15 @@ static int usb_unbind_interface(struct device *dev) - int usb_driver_claim_interface(struct usb_driver *driver, - struct usb_interface *iface, void *priv) - { -- struct device *dev = &iface->dev; -+ struct device *dev; - struct usb_device *udev; - int retval = 0; - int lpm_disable_error; - -+ if (!iface) -+ return -ENODEV; -+ -+ dev = &iface->dev; - if (dev->driver) - return -EBUSY; - -diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c -index efc9531..a4c0b85 100644 ---- a/drivers/usb/core/hcd-pci.c -+++ b/drivers/usb/core/hcd-pci.c -@@ -74,6 +74,15 @@ static void for_each_companion(struct pci_dev *pdev, struct usb_hcd *hcd, - if (companion->bus != pdev->bus || - PCI_SLOT(companion->devfn) != slot) - continue; -+ -+ /* -+ * Companion device should be either UHCI,OHCI or EHCI host -+ * controller, otherwise skip. -+ */ -+ if (companion->class != CL_UHCI && companion->class != CL_OHCI && -+ companion->class != CL_EHCI) -+ continue; -+ - companion_hcd = pci_get_drvdata(companion); - if (!companion_hcd || !companion_hcd->self.root_hub) - continue; -diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c -index 2222899..d8e1d5c 100644 ---- a/drivers/usb/core/hub.c -+++ b/drivers/usb/core/hub.c -@@ -124,6 +124,10 @@ struct usb_hub *usb_hub_to_struct_hub(struct usb_device *hdev) - - static int usb_device_supports_lpm(struct usb_device *udev) - { -+ /* Some devices have trouble with LPM */ -+ if (udev->quirks & USB_QUIRK_NO_LPM) -+ return 0; -+ - /* USB 2.1 (and greater) devices indicate LPM support through - * their USB 2.0 Extended Capabilities BOS descriptor. - */ -@@ -1030,10 +1034,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 +1245,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 +1267,11 @@ 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 */ -@@ -4222,7 +4242,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, - { - struct usb_device *hdev = hub->hdev; - struct usb_hcd *hcd = bus_to_hcd(hdev->bus); -- int i, j, retval; -+ int retries, operations, retval, i; - unsigned delay = HUB_SHORT_RESET_TIME; - enum usb_device_speed oldspeed = udev->speed; - const char *speed; -@@ -4324,7 +4344,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, - * first 8 bytes of the device descriptor to get the ep0 maxpacket - * value. - */ -- for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) { -+ for (retries = 0; retries < GET_DESCRIPTOR_TRIES; (++retries, msleep(100))) { - bool did_new_scheme = false; - - if (use_new_scheme(udev, retry_counter)) { -@@ -4351,7 +4371,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, - * 255 is for WUSB devices, we actually need to use - * 512 (WUSB1.0[4.8.1]). - */ -- for (j = 0; j < 3; ++j) { -+ for (operations = 0; operations < 3; ++operations) { - buf->bMaxPacketSize0 = 0; - r = usb_control_msg(udev, usb_rcvaddr0pipe(), - USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, -@@ -4371,7 +4391,13 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, - r = -EPROTO; - break; - } -- if (r == 0) -+ /* -+ * Some devices time out if they are powered on -+ * when already connected. They need a second -+ * reset. But only on the first attempt, -+ * lest we get into a time out/reset loop -+ */ -+ if (r == 0 || (r == -ETIMEDOUT && retries == 0)) - break; - } - udev->descriptor.bMaxPacketSize0 = -@@ -4403,7 +4429,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, - * authorization will assign the final address. - */ - if (udev->wusb == 0) { -- for (j = 0; j < SET_ADDRESS_TRIES; ++j) { -+ for (operations = 0; operations < SET_ADDRESS_TRIES; ++operations) { - retval = hub_set_address(udev, devnum); - if (retval >= 0) - break; -@@ -4498,6 +4524,8 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, - goto fail; - } - -+ usb_detect_quirks(udev); -+ - if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) { - retval = usb_get_bos_descriptor(udev); - if (!retval) { -@@ -4692,7 +4720,6 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus, - if (status < 0) - goto loop; - -- usb_detect_quirks(udev); - if (udev->quirks & USB_QUIRK_DELAY_INIT) - msleep(1000); - -@@ -5324,9 +5351,6 @@ static int usb_reset_and_verify_device(struct usb_device *udev) - if (udev->usb2_hw_lpm_enabled == 1) - usb_set_usb2_hardware_lpm(udev, 0); - -- bos = udev->bos; -- udev->bos = NULL; -- - /* Disable LPM and LTM while we reset the device and reinstall the alt - * settings. Device-initiated LPM settings, and system exit latency - * settings are cleared when the device is reset, so we have to set -@@ -5335,15 +5359,17 @@ static int usb_reset_and_verify_device(struct usb_device *udev) - ret = usb_unlocked_disable_lpm(udev); - if (ret) { - dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__); -- goto re_enumerate; -+ goto re_enumerate_no_bos; - } - ret = usb_disable_ltm(udev); - if (ret) { - dev_err(&udev->dev, "%s Failed to disable LTM\n.", - __func__); -- goto re_enumerate; -+ goto re_enumerate_no_bos; - } - -+ bos = udev->bos; -+ - for (i = 0; i < SET_CONFIG_TRIES; ++i) { - - /* ep0 maxpacket size may change; let the HCD know about it. -@@ -5435,15 +5461,19 @@ done: - usb_set_usb2_hardware_lpm(udev, 1); - usb_unlocked_enable_lpm(udev); - usb_enable_ltm(udev); -- usb_release_bos_descriptor(udev); -- udev->bos = bos; -+ /* release the new BOS descriptor allocated by hub_port_init() */ -+ if (udev->bos != bos) { -+ usb_release_bos_descriptor(udev); -+ udev->bos = bos; -+ } - return 0; - - re_enumerate: -- /* LPM state doesn't matter when we're about to destroy the device. */ -- hub_port_logical_disconnect(parent_hub, port1); - usb_release_bos_descriptor(udev); - udev->bos = bos; -+re_enumerate_no_bos: -+ /* LPM state doesn't matter when we're about to destroy the device. */ -+ hub_port_logical_disconnect(parent_hub, port1); - return -ENODEV; - } - -diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c -index 8a77a41..6b53fc3 100644 ---- a/drivers/usb/core/quirks.c -+++ b/drivers/usb/core/quirks.c -@@ -196,6 +196,12 @@ static const struct usb_device_id usb_quirk_list[] = { - { USB_DEVICE(0x1a0a, 0x0200), .driver_info = - USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL }, - -+ /* Blackmagic Design Intensity Shuttle */ -+ { USB_DEVICE(0x1edb, 0xbd3b), .driver_info = USB_QUIRK_NO_LPM }, -+ -+ /* Blackmagic Design UltraStudio SDI */ -+ { USB_DEVICE(0x1edb, 0xbd4f), .driver_info = USB_QUIRK_NO_LPM }, -+ - { } /* terminating entry must be last */ - }; - -diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c -index b0f4d52..17eeab8 100644 ---- a/drivers/usb/dwc3/core.c -+++ b/drivers/usb/dwc3/core.c -@@ -673,22 +673,20 @@ static int dwc3_probe(struct platform_device *pdev) - * since it will be requested by the xhci-plat driver. - */ - regs = devm_ioremap_resource(dev, res); -- if (IS_ERR(regs)) -- return PTR_ERR(regs); -+ if (IS_ERR(regs)) { -+ ret = PTR_ERR(regs); -+ goto err0; -+ } - - dwc->regs = regs; - dwc->regs_size = resource_size(res); -- /* -- * restore res->start back to its original value so that, -- * in case the probe is deferred, we don't end up getting error in -- * request the memory region the next time probe is called. -- */ -- res->start -= DWC3_GLOBALS_REGS_START; - - if (node) { - dwc->maximum_speed = of_usb_get_maximum_speed(node); - - dwc->needs_fifo_resize = of_property_read_bool(node, "tx-fifo-resize"); -+ dwc->configure_gfladj = -+ of_property_read_bool(node, "configure-gfladj"); - dwc->dr_mode = of_usb_get_dr_mode(node); - } else if (pdata) { - dwc->maximum_speed = pdata->maximum_speed; -@@ -703,7 +701,7 @@ static int dwc3_probe(struct platform_device *pdev) - - ret = dwc3_core_get_phy(dwc); - if (ret) -- return ret; -+ goto err0; - - spin_lock_init(&dwc->lock); - platform_set_drvdata(pdev, dwc); -@@ -722,7 +720,25 @@ static int dwc3_probe(struct platform_device *pdev) - if (ret) { - dev_err(dwc->dev, "failed to allocate event buffers\n"); - ret = -ENOMEM; -- goto err0; -+ goto err1; -+ } -+ -+ /* Adjust Frame Length */ -+ if (dwc->configure_gfladj) -+ dwc3_writel(dwc->regs, DWC3_GFLADJ, GFLADJ_30MHZ_REG_SEL | -+ GFLADJ_30MHZ(GFLADJ_30MHZ_DEFAULT)); -+ -+ /* Change burst beat and outstanding pipelined transfers requests */ -+ dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, -+ (dwc3_readl(dwc->regs, DWC3_GSBUSCFG0) & ~0xff) | 0xf); -+ dwc3_writel(dwc->regs, DWC3_GSBUSCFG1, -+ dwc3_readl(dwc->regs, DWC3_GSBUSCFG1) | 0xf00); -+ -+ /* Enable Snooping */ -+ if (node && of_dma_is_coherent(node)) { -+ dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, -+ dwc3_readl(dwc->regs, DWC3_GSBUSCFG0) | 0x22220000); -+ dev_dbg(dev, "enabled snooping for usb\n"); - } - - if (IS_ENABLED(CONFIG_USB_DWC3_HOST)) -@@ -736,65 +752,81 @@ static int dwc3_probe(struct platform_device *pdev) - ret = dwc3_core_init(dwc); - if (ret) { - dev_err(dev, "failed to initialize core\n"); -- goto err0; -+ goto err1; - } - - usb_phy_set_suspend(dwc->usb2_phy, 0); - usb_phy_set_suspend(dwc->usb3_phy, 0); - ret = phy_power_on(dwc->usb2_generic_phy); - if (ret < 0) -- goto err1; -+ goto err2; - - ret = phy_power_on(dwc->usb3_generic_phy); - if (ret < 0) -- goto err_usb2phy_power; -+ goto err3; - - ret = dwc3_event_buffers_setup(dwc); - if (ret) { - dev_err(dwc->dev, "failed to setup event buffers\n"); -- goto err_usb3phy_power; -+ goto err4; - } - - ret = dwc3_core_init_mode(dwc); - if (ret) -- goto err2; -+ goto err5; - - ret = dwc3_debugfs_init(dwc); - if (ret) { - dev_err(dev, "failed to initialize debugfs\n"); -- goto err3; -+ goto err6; - } - - pm_runtime_allow(dev); - - return 0; - --err3: -+err6: - dwc3_core_exit_mode(dwc); - --err2: -+err5: - dwc3_event_buffers_cleanup(dwc); - --err_usb3phy_power: -+err4: - phy_power_off(dwc->usb3_generic_phy); - --err_usb2phy_power: -+err3: - phy_power_off(dwc->usb2_generic_phy); - --err1: -+err2: - usb_phy_set_suspend(dwc->usb2_phy, 1); - usb_phy_set_suspend(dwc->usb3_phy, 1); - dwc3_core_exit(dwc); - --err0: -+err1: - dwc3_free_event_buffers(dwc); - -+err0: -+ /* -+ * restore res->start back to its original value so that, in case the -+ * probe is deferred, we don't end up getting error in request the -+ * memory region the next time probe is called. -+ */ -+ res->start -= DWC3_GLOBALS_REGS_START; -+ - return ret; - } - - static int dwc3_remove(struct platform_device *pdev) - { - struct dwc3 *dwc = platform_get_drvdata(pdev); -+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ -+ /* -+ * restore res->start back to its original value so that, in case the -+ * probe is deferred, we don't end up getting error in request the -+ * memory region the next time probe is called. -+ */ -+ res->start -= DWC3_GLOBALS_REGS_START; - - dwc3_debugfs_exit(dwc); - dwc3_core_exit_mode(dwc); -diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h -index 66f6256..aec8953 100644 ---- a/drivers/usb/dwc3/core.h -+++ b/drivers/usb/dwc3/core.h -@@ -26,6 +26,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -123,6 +124,7 @@ - #define DWC3_GEVNTCOUNT(n) (0xc40c + (n * 0x10)) - - #define DWC3_GHWPARAMS8 0xc600 -+#define DWC3_GFLADJ 0xc630 - - /* Device Registers */ - #define DWC3_DCFG 0xc700 -@@ -210,6 +212,11 @@ - #define DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(n) (((n) & (0x0f << 13)) >> 13) - #define DWC3_MAX_HIBER_SCRATCHBUFS 15 - -+/* Global Frame Length Adjustment Register */ -+#define GFLADJ_30MHZ_REG_SEL (1 << 7) -+#define GFLADJ_30MHZ(n) ((n) & 0x3f) -+#define GFLADJ_30MHZ_DEFAULT 0x20 -+ - /* Device Configuration Register */ - #define DWC3_DCFG_DEVADDR(addr) ((addr) << 3) - #define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f) -@@ -766,6 +773,7 @@ struct dwc3 { - unsigned has_hibernation:1; - unsigned is_selfpowered:1; - unsigned needs_fifo_resize:1; -+ unsigned configure_gfladj:1; - unsigned pullups_connected:1; - unsigned resize_fifos:1; - unsigned setup_packet_pending:1; -diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c -index dcb8ca0..c41d46c 100644 ---- a/drivers/usb/dwc3/host.c -+++ b/drivers/usb/dwc3/host.c -@@ -39,6 +39,12 @@ int dwc3_host_init(struct dwc3 *dwc) - xhci->dev.dma_mask = dwc->dev->dma_mask; - xhci->dev.dma_parms = dwc->dev->dma_parms; - -+ /* set DMA operations */ -+ if (dwc->dev->of_node && of_dma_is_coherent(dwc->dev->of_node)) { -+ xhci->dev.archdata.dma_ops = dwc->dev->archdata.dma_ops; -+ dev_dbg(dwc->dev, "set dma_ops for usb\n"); -+ } -+ - dwc->xhci = xhci; - - ret = platform_device_add_resources(xhci, dwc->xhci_resources, -diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c -index 7e5c90e..c6027ac 100644 ---- a/drivers/usb/host/xhci-pci.c -+++ b/drivers/usb/host/xhci-pci.c -@@ -23,10 +23,17 @@ - #include - #include - #include -+#include - - #include "xhci.h" - #include "xhci-trace.h" - -+#define SSIC_PORT_NUM 2 -+#define SSIC_PORT_CFG2 0x880c -+#define SSIC_PORT_CFG2_OFFSET 0x30 -+#define PROG_DONE (1 << 30) -+#define SSIC_PORT_UNUSED (1 << 31) -+ - /* Device for a quirk */ - #define PCI_VENDOR_ID_FRESCO_LOGIC 0x1b73 - #define PCI_DEVICE_ID_FRESCO_LOGIC_PDK 0x1000 -@@ -40,6 +47,8 @@ - #define PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI 0x22b5 - #define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_XHCI 0xa12f - #define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI 0x9d2f -+#define PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI 0x0aa8 -+#define PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI 0x1aa8 - - static const char hcd_name[] = "xhci_hcd"; - -@@ -140,9 +149,15 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) - if (pdev->vendor == PCI_VENDOR_ID_INTEL && - (pdev->device == PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI || - pdev->device == PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_XHCI || -- pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI)) { -+ pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI || -+ pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI || -+ pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI)) { - xhci->quirks |= XHCI_PME_STUCK_QUIRK; - } -+ if (pdev->vendor == PCI_VENDOR_ID_INTEL && -+ pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI) { -+ xhci->quirks |= XHCI_SSIC_PORT_UNUSED; -+ } - if (pdev->vendor == PCI_VENDOR_ID_ETRON && - pdev->device == PCI_DEVICE_ID_EJ168) { - xhci->quirks |= XHCI_RESET_ON_RESUME; -@@ -169,20 +184,18 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) - "QUIRK: Resetting on resume"); - } - --/* -- * Make sure PME works on some Intel xHCI controllers by writing 1 to clear -- * the Internal PME flag bit in vendor specific PMCTRL register at offset 0x80a4 -- */ --static void xhci_pme_quirk(struct xhci_hcd *xhci) -+#ifdef CONFIG_ACPI -+static void xhci_pme_acpi_rtd3_enable(struct pci_dev *dev) - { -- u32 val; -- void __iomem *reg; -- -- reg = (void __iomem *) xhci->cap_regs + 0x80a4; -- val = readl(reg); -- writel(val | BIT(28), reg); -- readl(reg); -+ static const u8 intel_dsm_uuid[] = { -+ 0xb7, 0x0c, 0x34, 0xac, 0x01, 0xe9, 0xbf, 0x45, -+ 0xb7, 0xe6, 0x2b, 0x34, 0xec, 0x93, 0x1e, 0x23, -+ }; -+ acpi_evaluate_dsm(ACPI_HANDLE(&dev->dev), intel_dsm_uuid, 3, 1, NULL); - } -+#else -+ static void xhci_pme_acpi_rtd3_enable(struct pci_dev *dev) { } -+#endif /* CONFIG_ACPI */ - - /* called during probe() after chip reset completes */ - static int xhci_pci_setup(struct usb_hcd *hcd) -@@ -263,6 +276,9 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) - HCC_MAX_PSA(xhci->hcc_params) >= 4) - xhci->shared_hcd->can_do_streams = 1; - -+ if (xhci->quirks & XHCI_PME_STUCK_QUIRK) -+ xhci_pme_acpi_rtd3_enable(dev); -+ - /* USB-2 and USB-3 roothubs initialized, allow runtime pm suspend */ - pm_runtime_put_noidle(&dev->dev); - -@@ -282,6 +298,7 @@ static void xhci_pci_remove(struct pci_dev *dev) - struct xhci_hcd *xhci; - - xhci = hcd_to_xhci(pci_get_drvdata(dev)); -+ xhci->xhc_state |= XHCI_STATE_REMOVING; - if (xhci->shared_hcd) { - usb_remove_hcd(xhci->shared_hcd); - usb_put_hcd(xhci->shared_hcd); -@@ -296,10 +313,65 @@ static void xhci_pci_remove(struct pci_dev *dev) - } - - #ifdef CONFIG_PM -+/* -+ * In some Intel xHCI controllers, in order to get D3 working, -+ * through a vendor specific SSIC CONFIG register at offset 0x883c, -+ * SSIC PORT need to be marked as "unused" before putting xHCI -+ * into D3. After D3 exit, the SSIC port need to be marked as "used". -+ * Without this change, xHCI might not enter D3 state. -+ */ -+static void xhci_ssic_port_unused_quirk(struct usb_hcd *hcd, bool suspend) -+{ -+ struct xhci_hcd *xhci = hcd_to_xhci(hcd); -+ u32 val; -+ void __iomem *reg; -+ int i; -+ -+ for (i = 0; i < SSIC_PORT_NUM; i++) { -+ reg = (void __iomem *) xhci->cap_regs + -+ SSIC_PORT_CFG2 + -+ i * SSIC_PORT_CFG2_OFFSET; -+ -+ /* Notify SSIC that SSIC profile programming is not done. */ -+ val = readl(reg) & ~PROG_DONE; -+ writel(val, reg); -+ -+ /* Mark SSIC port as unused(suspend) or used(resume) */ -+ val = readl(reg); -+ if (suspend) -+ val |= SSIC_PORT_UNUSED; -+ else -+ val &= ~SSIC_PORT_UNUSED; -+ writel(val, reg); -+ -+ /* Notify SSIC that SSIC profile programming is done */ -+ val = readl(reg) | PROG_DONE; -+ writel(val, reg); -+ readl(reg); -+ } -+} -+ -+/* -+ * Make sure PME works on some Intel xHCI controllers by writing 1 to clear -+ * the Internal PME flag bit in vendor specific PMCTRL register at offset 0x80a4 -+ */ -+static void xhci_pme_quirk(struct usb_hcd *hcd) -+{ -+ struct xhci_hcd *xhci = hcd_to_xhci(hcd); -+ void __iomem *reg; -+ u32 val; -+ -+ reg = (void __iomem *) xhci->cap_regs + 0x80a4; -+ val = readl(reg); -+ writel(val | BIT(28), reg); -+ readl(reg); -+} -+ - static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) - { - struct xhci_hcd *xhci = hcd_to_xhci(hcd); - struct pci_dev *pdev = to_pci_dev(hcd->self.controller); -+ int ret; - - /* - * Systems with the TI redriver that loses port status change events -@@ -309,9 +381,16 @@ static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) - pdev->no_d3cold = true; - - if (xhci->quirks & XHCI_PME_STUCK_QUIRK) -- xhci_pme_quirk(xhci); -+ xhci_pme_quirk(hcd); -+ -+ if (xhci->quirks & XHCI_SSIC_PORT_UNUSED) -+ xhci_ssic_port_unused_quirk(hcd, true); - -- return xhci_suspend(xhci, do_wakeup); -+ ret = xhci_suspend(xhci, do_wakeup); -+ if (ret && (xhci->quirks & XHCI_SSIC_PORT_UNUSED)) -+ xhci_ssic_port_unused_quirk(hcd, false); -+ -+ return ret; - } - - static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated) -@@ -341,8 +420,11 @@ static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated) - if (pdev->vendor == PCI_VENDOR_ID_INTEL) - usb_enable_intel_xhci_ports(pdev); - -+ if (xhci->quirks & XHCI_SSIC_PORT_UNUSED) -+ xhci_ssic_port_unused_quirk(hcd, false); -+ - if (xhci->quirks & XHCI_PME_STUCK_QUIRK) -- xhci_pme_quirk(xhci); -+ xhci_pme_quirk(hcd); - - retval = xhci_resume(xhci, hibernated); - return retval; -diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c -index 1e5fb8c..04e7525 100644 ---- a/drivers/usb/host/xhci-ring.c -+++ b/drivers/usb/host/xhci-ring.c -@@ -3840,8 +3840,12 @@ static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd, - { - int reserved_trbs = xhci->cmd_ring_reserved_trbs; - int ret; -- if (xhci->xhc_state & XHCI_STATE_DYING) -+ -+ if ((xhci->xhc_state & XHCI_STATE_DYING) || -+ (xhci->xhc_state & XHCI_STATE_HALTED)) { -+ xhci_dbg(xhci, "xHCI dying or halted, can't queue_command\n"); - return -ESHUTDOWN; -+ } - - if (!command_must_succeed) - reserved_trbs++; -diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c -index 98380fa..600a137 100644 ---- a/drivers/usb/host/xhci.c -+++ b/drivers/usb/host/xhci.c -@@ -147,7 +147,8 @@ static int xhci_start(struct xhci_hcd *xhci) - "waited %u microseconds.\n", - XHCI_MAX_HALT_USEC); - if (!ret) -- xhci->xhc_state &= ~(XHCI_STATE_HALTED | XHCI_STATE_DYING); -+ /* clear state flags. Including dying, halted or removing */ -+ xhci->xhc_state = 0; - - return ret; - } -@@ -1102,8 +1103,8 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) - /* Resume root hubs only when have pending events. */ - status = readl(&xhci->op_regs->status); - if (status & STS_EINT) { -- usb_hcd_resume_root_hub(hcd); - usb_hcd_resume_root_hub(xhci->shared_hcd); -+ usb_hcd_resume_root_hub(hcd); - } - } - -@@ -1118,10 +1119,10 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) - - /* Re-enable port polling. */ - xhci_dbg(xhci, "%s: starting port polling.\n", __func__); -- set_bit(HCD_FLAG_POLL_RH, &hcd->flags); -- usb_hcd_poll_rh_status(hcd); - set_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags); - usb_hcd_poll_rh_status(xhci->shared_hcd); -+ set_bit(HCD_FLAG_POLL_RH, &hcd->flags); -+ usb_hcd_poll_rh_status(hcd); - - return retval; - } -@@ -1548,7 +1549,9 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) - xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, - "HW died, freeing TD."); - urb_priv = urb->hcpriv; -- for (i = urb_priv->td_cnt; i < urb_priv->length; i++) { -+ for (i = urb_priv->td_cnt; -+ i < urb_priv->length && xhci->devs[urb->dev->slot_id]; -+ i++) { - td = urb_priv->td[i]; - if (!list_empty(&td->td_list)) - list_del_init(&td->td_list); -@@ -1682,8 +1685,10 @@ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev, - cpu_to_le32(EP_STATE_DISABLED)) || - le32_to_cpu(ctrl_ctx->drop_flags) & - xhci_get_endpoint_flag(&ep->desc)) { -- xhci_warn(xhci, "xHCI %s called with disabled ep %p\n", -- __func__, ep); -+ /* Do not warn when called after a usb_device_reset */ -+ if (xhci->devs[udev->slot_id]->eps[ep_index].ring != NULL) -+ xhci_warn(xhci, "xHCI %s called with disabled ep %p\n", -+ __func__, ep); - return 0; - } - -@@ -2751,7 +2756,8 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) - if (ret <= 0) - return ret; - xhci = hcd_to_xhci(hcd); -- if (xhci->xhc_state & XHCI_STATE_DYING) -+ if ((xhci->xhc_state & XHCI_STATE_DYING) || -+ (xhci->xhc_state & XHCI_STATE_REMOVING)) - return -ENODEV; - - xhci_dbg(xhci, "%s called for udev %p\n", __func__, udev); -@@ -3793,7 +3799,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, - u64 temp_64; - struct xhci_command *command; - -- if (xhci->xhc_state) /* dying or halted */ -+ if (xhci->xhc_state) /* dying, removing or halted */ - return -EINVAL; - - if (!udev->slot_id) { -@@ -4912,6 +4918,16 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) - goto error; - xhci_dbg(xhci, "Reset complete\n"); - -+ /* -+ * On some xHCI controllers (e.g. R-Car SoCs), the AC64 bit (bit 0) -+ * of HCCPARAMS1 is set to 1. However, the xHCs don't support 64-bit -+ * address memory pointers actually. So, this driver clears the AC64 -+ * bit of xhci->hcc_params to call dma_set_coherent_mask(dev, -+ * DMA_BIT_MASK(32)) in this xhci_gen_setup(). -+ */ -+ if (xhci->quirks & XHCI_NO_64BIT_SUPPORT) -+ xhci->hcc_params &= ~BIT(0); -+ - /* Set dma_mask and coherent_dma_mask to 64-bits, - * if xHC supports 64-bit addressing */ - if (HCC_64BIT_ADDR(xhci->hcc_params) && -diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h -index 54f386f..3850cb2 100644 ---- a/drivers/usb/host/xhci.h -+++ b/drivers/usb/host/xhci.h -@@ -1531,6 +1531,7 @@ struct xhci_hcd { - */ - #define XHCI_STATE_DYING (1 << 0) - #define XHCI_STATE_HALTED (1 << 1) -+#define XHCI_STATE_REMOVING (1 << 2) - /* Statistics */ - int error_bitmask; - unsigned int quirks; -@@ -1565,6 +1566,8 @@ struct xhci_hcd { - /* For controllers with a broken beyond repair streams implementation */ - #define XHCI_BROKEN_STREAMS (1 << 19) - #define XHCI_PME_STUCK_QUIRK (1 << 20) -+#define XHCI_SSIC_PORT_UNUSED (1 << 22) -+#define XHCI_NO_64BIT_SUPPORT (1 << 23) - unsigned int num_active_eps; - unsigned int limit_active_eps; - /* There are two roothubs to keep track of bus suspend info for */ -diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig -index d8c5763..db494c0 100644 ---- a/drivers/vfio/Kconfig -+++ b/drivers/vfio/Kconfig -@@ -16,7 +16,7 @@ config VFIO_SPAPR_EEH - menuconfig VFIO - tristate "VFIO Non-Privileged userspace driver framework" - depends on IOMMU_API -- select VFIO_IOMMU_TYPE1 if X86 -+ select VFIO_IOMMU_TYPE1 if (X86 || ARM_SMMU) - select VFIO_IOMMU_SPAPR_TCE if (PPC_POWERNV || PPC_PSERIES) - select VFIO_SPAPR_EEH if (PPC_POWERNV || PPC_PSERIES) - select ANON_INODES -@@ -27,3 +27,6 @@ menuconfig VFIO - If you don't know what to do here, say N. - - source "drivers/vfio/pci/Kconfig" -+#source "drivers/vfio/platform/Kconfig" -+source "drivers/vfio/fsl-mc/Kconfig" -+ -diff --git a/drivers/vfio/Makefile b/drivers/vfio/Makefile -index 0b035b1..69bcd84 100644 ---- a/drivers/vfio/Makefile -+++ b/drivers/vfio/Makefile -@@ -3,3 +3,4 @@ obj-$(CONFIG_VFIO_IOMMU_TYPE1) += vfio_iommu_type1.o - obj-$(CONFIG_VFIO_IOMMU_SPAPR_TCE) += vfio_iommu_spapr_tce.o - obj-$(CONFIG_VFIO_SPAPR_EEH) += vfio_spapr_eeh.o - obj-$(CONFIG_VFIO_PCI) += pci/ -+obj-$(CONFIG_VFIO_FSL_MC) += fsl-mc/ -diff --git a/drivers/vfio/fsl-mc/Kconfig b/drivers/vfio/fsl-mc/Kconfig -new file mode 100644 -index 0000000..eb6ba2b ---- /dev/null -+++ b/drivers/vfio/fsl-mc/Kconfig -@@ -0,0 +1,9 @@ -+config VFIO_FSL_MC -+ tristate "VFIO support for Freescale Management Complex devices" -+ depends on VFIO && FSL_MC_BUS && EVENTFD -+ help -+ Support for the Freescale Management Complex(MC) VFIO driver. -+ This is required to passthrough Freescale MC devices using the -+ VFIO framework. -+ -+ If you don't know what to do here, say N. -diff --git a/drivers/vfio/fsl-mc/Makefile b/drivers/vfio/fsl-mc/Makefile -new file mode 100644 -index 0000000..2aca75a ---- /dev/null -+++ b/drivers/vfio/fsl-mc/Makefile -@@ -0,0 +1,2 @@ -+vfio-fsl_mc-y := vfio_fsl_mc.o -+obj-$(CONFIG_VFIO_FSL_MC) += vfio_fsl_mc.o vfio_fsl_mc_intr.o -diff --git a/drivers/vfio/fsl-mc/vfio_fsl_mc.c b/drivers/vfio/fsl-mc/vfio_fsl_mc.c -new file mode 100644 -index 0000000..ffbe845 ---- /dev/null -+++ b/drivers/vfio/fsl-mc/vfio_fsl_mc.c -@@ -0,0 +1,603 @@ -+/* -+ * Freescale Management Complex (MC) device passthrough using VFIO -+ * -+ * Copyright (C) 2014 Freescale Semiconductor, Inc. -+ * Author: Bharat Bhushan -+ * -+ * This file is licensed under the terms of the GNU General Public -+ * License version 2. This program is licensed "as is" without any -+ * warranty of any kind, whether express or implied. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "../../staging/fsl-mc/include/mc.h" -+#include "../../staging/fsl-mc/include/mc-sys.h" -+#include "../../staging/fsl-mc/include/mc-private.h" -+ -+#include "vfio_fsl_mc_private.h" -+struct fsl_mc_io *vfio_mc_io = NULL; -+struct fsl_mc_io *vfio_atomic_mc_io = NULL; -+ -+static DEFINE_MUTEX(driver_lock); -+ -+/* Validate that requested address range falls in one of container's -+ * device region. -+ */ -+static bool vfio_validate_mmap_addr(struct vfio_fsl_mc_device *vdev, -+ unsigned long addr, unsigned long size) -+{ -+ struct fsl_mc_device *mc_dev = vdev->mc_dev; -+ phys_addr_t region_addr; -+ size_t region_size; -+ int idx; -+ -+ /* Do not try to validate if address range wraps */ -+ if ((addr + size) < addr) -+ return false; -+ -+ /* Hack to allow mmap GITS_TRANSLATOR Register Page */ -+ if (addr == 0x6030000) -+ return true; -+ -+ for (idx = 0; idx < mc_dev->obj_desc.region_count; idx++) { -+ region_addr = mc_dev->regions[idx].start; -+ region_size = mc_dev->regions[idx].end - -+ mc_dev->regions[idx].start + 1; -+ -+ /* -+ * Align search to minimum mappable size of PAGE_SIZE. -+ * Thanks to our hardware that even though the -+ * region_size is less then PAGE_SIZE but there -+ * is no other device maps in this address range. -+ * So no security threat/issue in mapping PAGE_SIZE. -+ */ -+ if (region_size < PAGE_SIZE) -+ region_size = PAGE_SIZE; -+ -+ if (addr >= region_addr && -+ ((addr + size) <= (region_addr + region_size))) -+ return true; -+ } -+ -+ return false; -+} -+ -+static long vfio_fsl_mc_ioctl(void *device_data, -+ unsigned int cmd, unsigned long arg) -+{ -+ struct vfio_fsl_mc_device *vdev = device_data; -+ struct fsl_mc_device *mc_dev = vdev->mc_dev; -+ struct device *dev = &mc_dev->dev; -+ unsigned long minsz; -+ int ret; -+ -+ if (WARN_ON(!mc_dev)) -+ return -ENODEV; -+ -+ switch (cmd) { -+ case VFIO_DEVICE_GET_INFO: -+ { -+ struct vfio_device_info info; -+ -+ minsz = offsetofend(struct vfio_device_info, num_irqs); -+ -+ if (copy_from_user(&info, (void __user *)arg, minsz)) -+ return -EFAULT; -+ -+ if (info.argsz < minsz) -+ return -EINVAL; -+ -+ info.flags = VFIO_DEVICE_FLAGS_FSL_MC; -+ if (strcmp(mc_dev->obj_desc.type, "dprc") == 0) -+ info.flags |= VFIO_DEVICE_FLAGS_RESET; -+ -+ info.num_regions = mc_dev->obj_desc.region_count; -+ info.num_irqs = mc_dev->obj_desc.irq_count; -+ -+ return copy_to_user((void __user *)arg, &info, minsz); -+ } -+ case VFIO_DEVICE_GET_REGION_INFO: -+ { -+ struct vfio_region_info info; -+ -+ minsz = offsetofend(struct vfio_region_info, offset); -+ -+ if (copy_from_user(&info, (void __user *)arg, minsz)) -+ return -EFAULT; -+ -+ if (info.argsz < minsz) -+ return -EINVAL; -+ -+ info.offset = mc_dev->regions[info.index].start; -+ info.size = mc_dev->regions[info.index].end - -+ mc_dev->regions[info.index].start + 1; -+ info.flags = VFIO_REGION_INFO_FLAG_READ | -+ VFIO_REGION_INFO_FLAG_WRITE | -+ VFIO_REGION_INFO_FLAG_MMAP; -+ -+ return copy_to_user((void __user *)arg, &info, minsz); -+ } -+ case VFIO_DEVICE_GET_IRQ_INFO: -+ { -+ struct vfio_irq_info info; -+ -+ minsz = offsetofend(struct vfio_irq_info, count); -+ if (copy_from_user(&info, (void __user *)arg, minsz)) -+ return -EFAULT; -+ -+ if (info.argsz < minsz) -+ return -EINVAL; -+ -+ if (info.index >= mc_dev->obj_desc.irq_count) -+ return -EINVAL; -+ -+ if (vdev->mc_irqs[info.index].irq_initialized) { -+ info.flags = vdev->mc_irqs[info.index].flags; -+ info.count = vdev->mc_irqs[info.index].count; -+ } else { -+ /* -+ * If IRQs are not initialized then these can not -+ * be configuted and used by user-space/ -+ */ -+ info.flags = 0; -+ info.count = 0; -+ } -+ -+ return copy_to_user((void __user *)arg, &info, minsz); -+ } -+ case VFIO_DEVICE_SET_IRQS: -+ { -+ struct vfio_irq_set hdr; -+ u8 *data = NULL; -+ int ret = 0; -+ -+ minsz = offsetofend(struct vfio_irq_set, count); -+ -+ if (copy_from_user(&hdr, (void __user *)arg, minsz)) -+ return -EFAULT; -+ -+ if (hdr.argsz < minsz) -+ return -EINVAL; -+ -+ if (hdr.index >= mc_dev->obj_desc.irq_count) -+ return -EINVAL; -+ -+ if (hdr.start != 0 || hdr.count > 1) -+ return -EINVAL; -+ -+ if (hdr.count == 0 && -+ (!(hdr.flags & VFIO_IRQ_SET_DATA_NONE) || -+ !(hdr.flags & VFIO_IRQ_SET_ACTION_TRIGGER))) -+ return -EINVAL; -+ -+ if (hdr.flags & ~(VFIO_IRQ_SET_DATA_TYPE_MASK | -+ VFIO_IRQ_SET_ACTION_TYPE_MASK)) -+ return -EINVAL; -+ -+ if (!(hdr.flags & VFIO_IRQ_SET_DATA_NONE)) { -+ size_t size; -+ -+ if (hdr.flags & VFIO_IRQ_SET_DATA_BOOL) -+ size = sizeof(uint8_t); -+ else if (hdr.flags & VFIO_IRQ_SET_DATA_EVENTFD) -+ size = sizeof(int32_t); -+ else -+ return -EINVAL; -+ -+ if (hdr.argsz - minsz < hdr.count * size) -+ return -EINVAL; -+ -+ data = memdup_user((void __user *)(arg + minsz), -+ hdr.count * size); -+ if (IS_ERR(data)) -+ return PTR_ERR(data); -+ } -+ -+ ret = vfio_fsl_mc_set_irqs_ioctl(vdev, hdr.flags, -+ hdr.index, hdr.start, -+ hdr.count, data); -+ return ret; -+ } -+ case VFIO_DEVICE_RESET: -+ { -+ if (strcmp(mc_dev->obj_desc.type, "dprc") != 0) -+ return -EINVAL; -+ -+ ret = dprc_reset_container(mc_dev->mc_io, 0, -+ mc_dev->mc_handle, -+ mc_dev->obj_desc.id); -+ if (ret) { -+ dev_err(dev, "Error in resetting container %d\n", ret); -+ return ret; -+ } -+ -+ ret = 0; -+ break; -+ } -+ default: -+ ret = -EINVAL; -+ } -+ -+ return ret; -+} -+ -+static ssize_t vfio_fsl_mc_read(void *device_data, char __user *buf, -+ size_t count, loff_t *ppos) -+{ -+ struct vfio_fsl_mc_device *vdev = device_data; -+ struct fsl_mc_device *mc_dev = vdev->mc_dev; -+ struct device *dev = &mc_dev->dev; -+ -+ dev_err(dev, "%s: Unimplemented\n", __func__); -+ return -EFAULT; -+} -+ -+static ssize_t vfio_fsl_mc_write(void *device_data, const char __user *buf, -+ size_t count, loff_t *ppos) -+{ -+ struct vfio_fsl_mc_device *vdev = device_data; -+ struct fsl_mc_device *mc_dev = vdev->mc_dev; -+ struct device *dev = &mc_dev->dev; -+ -+ dev_err(dev, "%s: Unimplemented\n", __func__); -+ return -EFAULT; -+} -+ -+/* Allows mmaping fsl_mc device regions in assigned DPRC */ -+static int vfio_fsl_mc_mmap(void *device_data, struct vm_area_struct *vma) -+{ -+ struct vfio_fsl_mc_device *vdev = device_data; -+ struct fsl_mc_device *mc_dev = vdev->mc_dev; -+ unsigned long size = vma->vm_end - vma->vm_start; -+ unsigned long addr = vma->vm_pgoff << PAGE_SHIFT; -+ int ret; -+ -+ if (vma->vm_end < vma->vm_start) -+ return -EINVAL; -+ if (vma->vm_start & ~PAGE_MASK) -+ return -EINVAL; -+ if (vma->vm_end & ~PAGE_MASK) -+ return -EINVAL; -+ if ((vma->vm_flags & VM_SHARED) == 0) -+ return -EINVAL; -+ -+ if (!vfio_validate_mmap_addr(vdev, addr, size)) -+ return -EINVAL; -+ -+ vma->vm_private_data = mc_dev; -+ -+#define QBMAN_SWP_CENA_BASE 0x818000000ULL -+ if ((addr & 0xFFF000000) == QBMAN_SWP_CENA_BASE) -+ vma->vm_page_prot = pgprot_cached_ns(vma->vm_page_prot); -+ else -+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); -+ -+ ret = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, -+ size, vma->vm_page_prot); -+ return ret; -+} -+ -+static void vfio_fsl_mc_release(void *device_data) -+{ -+ struct vfio_fsl_mc_device *vdev = device_data; -+ struct fsl_mc_device *mc_dev = vdev->mc_dev; -+ -+ if (WARN_ON(mc_dev == NULL)) -+ return; -+ -+ mutex_lock(&driver_lock); -+ vdev->refcnt--; -+ -+ if (strcmp(mc_dev->obj_desc.type, "dprc") == 0) -+ dprc_reset_container(mc_dev->mc_io, 0, mc_dev->mc_handle, -+ mc_dev->obj_desc.id); -+ else -+ vfio_fsl_mc_unconfigure_irqs(vdev); -+ -+ mutex_unlock(&driver_lock); -+ -+ module_put(THIS_MODULE); -+} -+ -+static int vfio_fsl_mc_open(void *device_data) -+{ -+ struct vfio_fsl_mc_device *vdev = device_data; -+ -+ if (!try_module_get(THIS_MODULE)) -+ return -ENODEV; -+ -+ mutex_lock(&driver_lock); -+ vdev->refcnt++; -+ mutex_unlock(&driver_lock); -+ -+ return 0; -+} -+ -+static const struct vfio_device_ops vfio_fsl_mc_ops = { -+ .name = "vfio-fsl-mc", -+ .open = vfio_fsl_mc_open, -+ .release = vfio_fsl_mc_release, -+ .ioctl = vfio_fsl_mc_ioctl, -+ .read = vfio_fsl_mc_read, -+ .write = vfio_fsl_mc_write, -+ .mmap = vfio_fsl_mc_mmap, -+}; -+ -+static int vfio_fsl_mc_device_remove(struct device *dev, void *data) -+{ -+ struct fsl_mc_device *mc_dev; -+ WARN_ON(dev == NULL); -+ -+ mc_dev = to_fsl_mc_device(dev); -+ if (WARN_ON(mc_dev == NULL)) -+ return -ENODEV; -+ -+ fsl_mc_device_remove(mc_dev); -+ return 0; -+} -+ -+static int vfio_fsl_mc_probe(struct fsl_mc_device *mc_dev) -+{ -+ struct vfio_fsl_mc_device *vdev; -+ struct iommu_group *group; -+ struct device *dev = &mc_dev->dev; -+ struct fsl_mc_bus *mc_bus; -+ unsigned int irq_count; -+ int ret; -+ -+ dev_info(dev, "Binding with vfio-fsl_mc driver\n"); -+ -+ group = iommu_group_get(dev); -+ if (!group) { -+ dev_err(dev, "%s: VFIO: No IOMMU group\n", __func__); -+ return -EINVAL; -+ } -+ -+ vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); -+ if (!vdev) { -+ iommu_group_put(group); -+ return -ENOMEM; -+ } -+ -+ if (strcmp(mc_dev->obj_desc.type, "dprc") == 0) { -+ vdev->mc_dev = mc_dev; -+ -+ /* Free inbuilt dprc MC portal if exists */ -+ if (mc_dev->mc_io && (mc_dev->mc_io != vfio_mc_io)) -+ fsl_destroy_mc_io(mc_dev->mc_io); -+ -+ /* Use New Allocated MC Portal (DPMCP object) */ -+ mc_dev->mc_io = vfio_mc_io; -+ -+ ret = dprc_open(mc_dev->mc_io, -+ 0, -+ mc_dev->obj_desc.id, -+ &mc_dev->mc_handle); -+ if (ret) { -+ dev_err(dev, "dprc_open() failed: error = %d\n", ret); -+ goto free_vfio_device; -+ } -+ -+ /* Initialize resource pool */ -+ dprc_init_all_resource_pools(mc_dev); -+ -+ mc_bus = to_fsl_mc_bus(mc_dev); -+ mutex_init(&mc_bus->scan_mutex); -+ -+ mc_bus->atomic_mc_io = vfio_atomic_mc_io; -+ ret = dprc_open(mc_bus->atomic_mc_io, 0, mc_dev->obj_desc.id, -+ &mc_bus->atomic_dprc_handle); -+ if (ret < 0) { -+ dev_err(dev, "fail to open dprc with atomic io (%d)\n", ret); -+ goto clean_resource_pool; -+ } -+ -+ if (fsl_mc_interrupts_supported() && !mc_bus->irq_resources) { -+ irq_count = FSL_MC_IRQ_POOL_MAX_EXTRA_IRQS; -+ ret = fsl_mc_populate_irq_pool(mc_bus, irq_count); -+ if (ret < 0) { -+ dev_err(dev, "%s: Failed to init irq-pool\n", -+ __func__); -+ goto free_open_dprc; -+ } -+ } -+ -+ mutex_lock(&mc_bus->scan_mutex); -+ ret = dprc_scan_objects(mc_dev, mc_dev->driver_override, -+ &irq_count); -+ mutex_unlock(&mc_bus->scan_mutex); -+ if (ret) { -+ dev_err(dev, "dprc_scan_objects() fails (%d)\n", ret); -+ goto clean_irq_pool; -+ } -+ -+ ret = vfio_add_group_dev(dev, &vfio_fsl_mc_ops, vdev); -+ if (ret) { -+ dev_err(dev, "%s: Failed to add to vfio group\n", -+ __func__); -+ goto dprc_clean_scan_objects; -+ } -+ -+ ret = vfio_fsl_mc_init_irqs(vdev); -+ if (ret) { -+ dev_err(dev, "%s: Failed to setup irqs\n", -+ __func__); -+ vfio_del_group_dev(dev); -+ goto dprc_clean_scan_objects; -+ } -+ } else { -+ vdev->mc_dev = mc_dev; -+ -+ /* Use New Allocated MC Portal (DPMCP object) */ -+ mc_dev->mc_io = vfio_mc_io; -+ -+ ret = vfio_add_group_dev(dev, &vfio_fsl_mc_ops, vdev); -+ if (ret) { -+ dev_err(dev, "%s: Failed to add to vfio group\n", -+ __func__); -+ goto free_vfio_device; -+ } -+ -+ if (mc_dev->obj_desc.irq_count) { -+ ret = vfio_fsl_mc_init_irqs(vdev); -+ if (ret) { -+ dev_err(dev, "%s: Failed to setup irqs\n", -+ __func__); -+ vfio_del_group_dev(dev); -+ goto free_vfio_device; -+ } -+ } -+ } -+ -+ return 0; -+ -+dprc_clean_scan_objects: -+ fsl_mc_cleanup_irq_pool(mc_bus); -+ device_for_each_child(&mc_dev->dev, NULL, vfio_fsl_mc_device_remove); -+ -+clean_irq_pool: -+ fsl_mc_cleanup_irq_pool(mc_bus); -+ -+free_open_dprc: -+ dprc_close(vfio_atomic_mc_io, 0, mc_dev->mc_handle); -+ -+clean_resource_pool: -+ dprc_cleanup_all_resource_pools(mc_dev); -+ dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle); -+ -+free_vfio_device: -+ kfree(vdev); -+ iommu_group_put(group); -+ return ret; -+} -+ -+static int vfio_fsl_mc_remove(struct fsl_mc_device *mc_dev) -+{ -+ struct vfio_fsl_mc_device *vdev; -+ struct fsl_mc_bus *mc_bus; -+ struct device *dev = &mc_dev->dev; -+ int ret; -+ -+ dev_info(dev, "Un-binding with vfio-fsl-mc driver\n"); -+ -+ vdev = vfio_del_group_dev(dev); -+ if (!vdev) -+ return -EINVAL; -+ -+ /* Only FSL-MC DPRC device can be unbound */ -+ if (strcmp(mc_dev->obj_desc.type, "dprc") == 0) { -+ device_for_each_child(dev, NULL, vfio_fsl_mc_device_remove); -+ -+ vfio_fsl_mc_free_irqs(vdev); -+ dprc_cleanup_all_resource_pools(mc_dev); -+ mc_bus = to_fsl_mc_bus(mc_dev); -+ -+ if (fsl_mc_interrupts_supported()) -+ fsl_mc_cleanup_irq_pool(mc_bus); -+ -+ ret = dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle); -+ if (ret < 0) -+ dev_err(dev, "dprc_close() fails %d\n", ret); -+ -+ ret = dprc_close(mc_bus->atomic_mc_io, 0, -+ mc_bus->atomic_dprc_handle); -+ if (ret < 0) -+ dev_err(dev, "dprc_close(atomic-io) fails %d\n", ret); -+ } else { -+ if (mc_dev->obj_desc.irq_count) -+ vfio_fsl_mc_free_irqs(vdev); -+ -+ mc_dev->mc_io = NULL; -+ } -+ -+ iommu_group_put(mc_dev->dev.iommu_group); -+ kfree(vdev); -+ -+ return 0; -+} -+ -+/* -+ * vfio-fsl_mc is a meta-driver, so use driver_override interface to -+ * bind a fsl_mc container with this driver and match_id_table is NULL. -+ */ -+static struct fsl_mc_driver vfio_fsl_mc_driver = { -+ .probe = vfio_fsl_mc_probe, -+ .remove = vfio_fsl_mc_remove, -+ .match_id_table = NULL, -+ .driver = { -+ .name = "vfio-fsl-mc", -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+static int __init vfio_fsl_mc_driver_init(void) -+{ -+ int err; -+ struct fsl_mc_device *root_mc_dev; -+ -+ if (fsl_mc_bus_type.dev_root == NULL) { -+ pr_err("%s: Driver registration fails as no fsl_mc_bus found\n", -+ __func__); -+ return -ENODEV; -+ } -+ -+ root_mc_dev = to_fsl_mc_device(fsl_mc_bus_type.dev_root); -+ -+ /* Allocate a new MC portal (DPMCP object) */ -+ err = fsl_mc_portal_allocate(root_mc_dev, 0, &vfio_mc_io); -+ if (err < 0) -+ goto err; -+ -+ /* Reset MCP before move on */ -+ err = fsl_mc_portal_reset(vfio_mc_io); -+ if (err < 0) -+ return err; -+ -+ /* Allocate a new MC portal (DPMCP object) */ -+ err = fsl_mc_portal_allocate(root_mc_dev, -+ FSL_MC_IO_ATOMIC_CONTEXT_PORTAL, -+ &vfio_atomic_mc_io); -+ if (err < 0) -+ goto err; -+ -+ err = fsl_mc_driver_register(&vfio_fsl_mc_driver); -+ if (err < 0) -+ goto err; -+ -+ return 0; -+err: -+ if (vfio_mc_io) -+ fsl_mc_portal_free(vfio_mc_io); -+ -+ if (vfio_atomic_mc_io) -+ fsl_mc_portal_free(vfio_atomic_mc_io); -+ -+ vfio_atomic_mc_io = NULL; -+ vfio_mc_io = NULL; -+ return err; -+} -+ -+static void __exit vfio_fsl_mc_driver_exit(void) -+{ -+ fsl_mc_portal_free(vfio_mc_io); -+ fsl_mc_portal_free(vfio_atomic_mc_io); -+ fsl_mc_driver_unregister(&vfio_fsl_mc_driver); -+} -+ -+module_init(vfio_fsl_mc_driver_init); -+module_exit(vfio_fsl_mc_driver_exit); -+ -+MODULE_AUTHOR("Bharat Bhushan "); -+MODULE_LICENSE("GPL v2"); -+MODULE_DESCRIPTION("VFIO for FSL MC devices - User Level meta-driver"); -diff --git a/drivers/vfio/fsl-mc/vfio_fsl_mc_intr.c b/drivers/vfio/fsl-mc/vfio_fsl_mc_intr.c -new file mode 100644 -index 0000000..a4db758 ---- /dev/null -+++ b/drivers/vfio/fsl-mc/vfio_fsl_mc_intr.c -@@ -0,0 +1,273 @@ -+/* -+ * Freescale Management Complex (MC) device passthrough using VFIO -+ * -+ * Copyright (C) 2014 Freescale Semiconductor, Inc. -+ * Author: Bharat Bhushan -+ * -+ * This file is licensed under the terms of the GNU General Public -+ * License version 2. This program is licensed "as is" without any -+ * warranty of any kind, whether express or implied. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "../../staging/fsl-mc/include/mc.h" -+#include "../../staging/fsl-mc/include/mc-sys.h" -+#include "../../staging/fsl-mc/include/mc-private.h" -+#include -+ -+#include "vfio_fsl_mc_private.h" -+ -+static irqreturn_t vfio_fsl_mc_irq_handler(int irq_num, void *arg) -+{ -+ struct vfio_fsl_mc_irq *mc_irq = (struct vfio_fsl_mc_irq *)arg; -+ -+ eventfd_signal(mc_irq->trigger, 1); -+ return IRQ_HANDLED; -+} -+ -+int vfio_fsl_mc_configure_irq(struct vfio_fsl_mc_device *vdev, -+ int irq_index) -+{ -+ int error; -+ struct fsl_mc_device *mc_dev = vdev->mc_dev; -+ struct fsl_mc_device_irq *irq = mc_dev->irqs[irq_index]; -+ struct vfio_fsl_mc_irq *mc_irq = &vdev->mc_irqs[irq_index]; -+ struct device *dev = &mc_dev->dev; -+ -+ if (WARN_ON(!mc_irq->irq_initialized)) -+ return -EOPNOTSUPP; -+ -+ if (WARN_ON(mc_irq->irq_configured)) -+ return -EINVAL; -+ -+ mc_irq->name = kasprintf(GFP_KERNEL, "%s-%s-%d", "vfio-fsl-mc", -+ dev_name(dev), irq->irq_number); -+ -+ error = request_irq(irq->irq_number, vfio_fsl_mc_irq_handler, -+ 0, mc_irq->name, mc_irq); -+ if (error < 0) { -+ dev_err(&mc_dev->dev, -+ "IRQ registration fails with error: %d\n", error); -+ kfree(mc_irq->name); -+ return error; -+ } -+ -+ mc_irq->irq_configured = true; -+ return 0; -+} -+ -+static void vfio_fsl_mc_unconfigure_irq(struct vfio_fsl_mc_device *vdev, -+ int irq_index) -+{ -+ struct fsl_mc_device_irq *irq = vdev->mc_dev->irqs[irq_index]; -+ struct vfio_fsl_mc_irq *mc_irq = &vdev->mc_irqs[irq_index]; -+ -+ if (!vdev->mc_irqs[irq_index].irq_configured) -+ return; -+ -+ free_irq(irq->irq_number, mc_irq); -+ kfree(vdev->mc_irqs[irq_index].name); -+ vdev->mc_irqs[irq_index].irq_configured = false; -+} -+ -+static int vfio_fsl_mc_setup_irqs(struct fsl_mc_device *mc_dev) -+{ -+ int ret; -+ int irq_count = mc_dev->obj_desc.irq_count; -+ int hwirq; -+ int i; -+ -+ /* Allocate IRQs */ -+ ret = fsl_mc_allocate_irqs(mc_dev); -+ if (ret) -+ return ret; -+ -+ /* Disable IRQs */ -+ for (i = 0; i < irq_count; i++) { -+ hwirq = mc_dev->irqs[i]->irq_number; -+ disable_irq_nosync(hwirq); -+ } -+ -+ return 0; -+} -+ -+int vfio_fsl_mc_init_irqs(struct vfio_fsl_mc_device *vdev) -+{ -+ struct fsl_mc_device *mc_dev = vdev->mc_dev; -+ struct device *dev = &mc_dev->dev; -+ int irq_count = mc_dev->obj_desc.irq_count; -+ struct vfio_fsl_mc_irq *mc_irq; -+ int ret, i; -+ -+ mc_irq = kcalloc(irq_count, sizeof(*mc_irq), GFP_KERNEL); -+ if (mc_irq == NULL) -+ return -ENOMEM; -+ -+ ret = vfio_fsl_mc_setup_irqs(mc_dev); -+ if (ret) { -+ kfree(mc_irq); -+ dev_err(dev, "vfio_fsl_mc_setup_irqs Fails %d\n", ret); -+ return ret; -+ } -+ -+ for (i = 0; i < irq_count; i++) { -+ mc_irq[i].count = 1; -+ mc_irq[i].flags = VFIO_IRQ_INFO_EVENTFD | -+ VFIO_IRQ_INFO_MASKABLE; -+ mc_irq[i].irq_initialized = true; -+ } -+ -+ vdev->mc_irqs = mc_irq; -+ -+ return 0; -+} -+ -+int vfio_fsl_mc_unconfigure_irqs(struct vfio_fsl_mc_device *vdev) -+{ -+ struct fsl_mc_device *mc_dev = vdev->mc_dev; -+ int i; -+ -+ for (i = 0; i < mc_dev->obj_desc.irq_count; i++) { -+ if (!vdev->mc_irqs[i].irq_initialized) -+ continue; -+ -+ vfio_fsl_mc_unconfigure_irq(vdev, i); -+ } -+ return 0; -+} -+ -+/* Free All IRQs for the given MC object */ -+void vfio_fsl_mc_free_irqs(struct vfio_fsl_mc_device *vdev) -+{ -+ struct fsl_mc_device *mc_dev = vdev->mc_dev; -+ -+ vfio_fsl_mc_unconfigure_irqs(vdev); -+ fsl_mc_free_irqs(mc_dev); -+ -+ kfree(vdev->mc_irqs); -+} -+ -+static int vfio_fsl_mc_irq_mask(struct vfio_fsl_mc_device *vdev, -+ unsigned index, unsigned start, -+ unsigned count, uint32_t flags, void *data, -+ uint32_t mask) -+{ -+ uint8_t arr; -+ -+ if (start != 0 || count != 1) -+ return -EINVAL; -+ -+ switch (flags & VFIO_IRQ_SET_DATA_TYPE_MASK) { -+ case VFIO_IRQ_SET_DATA_BOOL: -+ arr = *(uint8_t *) data; -+ if (arr != 0x1) -+ return -EINVAL; -+ -+ case VFIO_IRQ_SET_DATA_NONE: -+ return -ENOTTY; /* To be Implemented */ -+ case VFIO_IRQ_SET_DATA_EVENTFD: -+ return -ENOTTY; /* To be Implemented */ -+ -+ default: -+ return -ENOTTY; -+ } -+ -+ return 0; -+} -+ -+static int vfio_fsl_mc_config_irq_signal(struct vfio_fsl_mc_device *vdev, -+ int irq_index, int32_t fd) -+{ -+ struct eventfd_ctx *trigger; -+ struct vfio_fsl_mc_irq *mc_irq = &vdev->mc_irqs[irq_index]; -+ int ret; -+ -+ if (vdev->mc_irqs[irq_index].trigger) { -+ eventfd_ctx_put(vdev->mc_irqs[irq_index].trigger); -+ vdev->mc_irqs[irq_index].trigger = NULL; -+ } -+ -+ if (fd < 0) -+ return 0; -+ -+ trigger = eventfd_ctx_fdget(fd); -+ if (IS_ERR(trigger)) -+ return PTR_ERR(trigger); -+ -+ /* If IRQ not configured the configure */ -+ if (!mc_irq->irq_configured) { -+ ret = vfio_fsl_mc_configure_irq(vdev, irq_index); -+ if (ret) { -+ eventfd_ctx_put(trigger); -+ return ret; -+ } -+ } -+ -+ vdev->mc_irqs[irq_index].trigger = trigger; -+ return 0; -+} -+ -+static int vfio_fsl_mc_set_irq_trigger(struct vfio_fsl_mc_device *vdev, -+ unsigned index, unsigned start, -+ unsigned count, uint32_t flags, void *data) -+{ -+ struct fsl_mc_device *mc_dev = vdev->mc_dev; -+ int32_t fd; -+ int hwirq; -+ -+ /* If count = 0 and DATA_NONE, disable interrupt */ -+ if (!count && (flags & VFIO_IRQ_SET_DATA_NONE)) { -+ hwirq = mc_dev->irqs[index]->irq_number; -+ disable_irq_nosync(hwirq); -+ return 0; -+ } -+ -+ if (flags & VFIO_IRQ_SET_DATA_BOOL) -+ fd = *(int8_t *)data; -+ else if (flags & VFIO_IRQ_SET_DATA_EVENTFD) -+ fd = *(int32_t *)data; -+ else -+ return -EINVAL; -+ -+ if (start != 0 || count != 1) -+ return -EINVAL; -+ -+ return vfio_fsl_mc_config_irq_signal(vdev, index, fd); -+} -+ -+int vfio_fsl_mc_set_irqs_ioctl(struct vfio_fsl_mc_device *vdev, -+ uint32_t flags, unsigned index, unsigned start, -+ unsigned count, void *data) -+{ -+ int ret = -ENOTTY; -+ -+ switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) { -+ case VFIO_IRQ_SET_ACTION_MASK: -+ /* mask all sources */ -+ ret = vfio_fsl_mc_irq_mask(vdev, index, start, -+ count, flags, data, 0); -+ break; -+ case VFIO_IRQ_SET_ACTION_UNMASK: -+ /* unmask all sources */ -+ ret = vfio_fsl_mc_irq_mask(vdev, index, start, -+ count, flags, data, ~0); -+ break; -+ case VFIO_IRQ_SET_ACTION_TRIGGER: -+ ret = vfio_fsl_mc_set_irq_trigger(vdev, index, start, -+ count, flags, data); -+ break; -+ } -+ -+ return ret; -+} -diff --git a/drivers/vfio/fsl-mc/vfio_fsl_mc_private.h b/drivers/vfio/fsl-mc/vfio_fsl_mc_private.h -new file mode 100644 -index 0000000..8980536 ---- /dev/null -+++ b/drivers/vfio/fsl-mc/vfio_fsl_mc_private.h -@@ -0,0 +1,43 @@ -+/* -+ * Freescale Management Complex VFIO private declarations -+ * -+ * Copyright (C) 2014 Freescale Semiconductor, Inc. -+ * Author: Bharat Bhushan -+ * -+ * This file is licensed under the terms of the GNU General Public -+ * License version 2. This program is licensed "as is" without any -+ * warranty of any kind, whether express or implied. -+ */ -+ -+#include "../../staging/fsl-mc/include/mc.h" -+ -+#ifndef VFIO_FSL_MC_PRIVATE_H -+#define VFIO_FSL_MC_PRIVATE_H -+ -+struct vfio_fsl_mc_irq { -+ struct eventfd_ctx *trigger; -+ u32 flags; -+ u32 count; -+ char *name; -+ bool irq_initialized; -+ bool irq_configured; -+}; -+ -+struct vfio_fsl_mc_device { -+ struct fsl_mc_device *mc_dev; -+ int refcnt; -+ struct vfio_fsl_mc_irq *mc_irqs; -+}; -+ -+int vfio_fsl_mc_init_irqs(struct vfio_fsl_mc_device *vdev); -+ -+void vfio_fsl_mc_free_irqs(struct vfio_fsl_mc_device *vdev); -+ -+int vfio_fsl_mc_configure_irq(struct vfio_fsl_mc_device *vdev, int irq_idx); -+ -+int vfio_fsl_mc_unconfigure_irqs(struct vfio_fsl_mc_device *vdev); -+ -+int vfio_fsl_mc_set_irqs_ioctl(struct vfio_fsl_mc_device *vdev, -+ uint32_t flags, unsigned index, unsigned start, -+ unsigned count, void *data); -+#endif /* VFIO_PCI_PRIVATE_H */ -diff --git a/drivers/vfio/pci/vfio_pci_intrs.c b/drivers/vfio/pci/vfio_pci_intrs.c -index 553212f..e8d695b 100644 ---- a/drivers/vfio/pci/vfio_pci_intrs.c -+++ b/drivers/vfio/pci/vfio_pci_intrs.c -@@ -560,7 +560,7 @@ static int vfio_msi_set_vector_signal(struct vfio_pci_device *vdev, - struct msi_msg msg; - - get_cached_msi_msg(irq, &msg); -- write_msi_msg(irq, &msg); -+ pci_write_msi_msg(irq, &msg); - } - - ret = request_irq(irq, vfio_msihandler, 0, -diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c -index 4a9d666..d795e07 100644 ---- a/drivers/vfio/vfio_iommu_type1.c -+++ b/drivers/vfio/vfio_iommu_type1.c -@@ -547,6 +547,8 @@ static int vfio_dma_do_map(struct vfio_iommu *iommu, - prot |= IOMMU_WRITE; - if (map->flags & VFIO_DMA_MAP_FLAG_READ) - prot |= IOMMU_READ; -+ if (map->flags & VFIO_DMA_MAP_FLAG_MMIO) -+ prot |= IOMMU_MMIO; - - if (!prot || !size || (size | iova | vaddr) & mask) - return -EINVAL; -@@ -933,7 +935,8 @@ static long vfio_iommu_type1_ioctl(void *iommu_data, - } else if (cmd == VFIO_IOMMU_MAP_DMA) { - struct vfio_iommu_type1_dma_map map; - uint32_t mask = VFIO_DMA_MAP_FLAG_READ | -- VFIO_DMA_MAP_FLAG_WRITE; -+ VFIO_DMA_MAP_FLAG_WRITE | -+ VFIO_DMA_MAP_FLAG_MMIO; - - minsz = offsetofend(struct vfio_iommu_type1_dma_map, size); - -diff --git a/fs/Kconfig b/fs/Kconfig -index 664991a..1481093 100644 ---- a/fs/Kconfig -+++ b/fs/Kconfig -@@ -210,6 +210,7 @@ source "fs/ufs/Kconfig" - source "fs/exofs/Kconfig" - source "fs/f2fs/Kconfig" - source "fs/efivarfs/Kconfig" -+source "fs/aufs/Kconfig" - - endif # MISC_FILESYSTEMS - -diff --git a/fs/Makefile b/fs/Makefile -index da0bbb4..c8bc724 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_AUFS_FS) += aufs/ -diff --git a/fs/aufs/Kconfig b/fs/aufs/Kconfig -new file mode 100644 -index 0000000..63560ce ---- /dev/null -+++ b/fs/aufs/Kconfig -@@ -0,0 +1,185 @@ -+config AUFS_FS -+ tristate "Aufs (Advanced multi layered unification filesystem) support" -+ help -+ Aufs is a stackable unification filesystem such as Unionfs, -+ which unifies several directories and provides a merged single -+ directory. -+ In the early days, aufs was entirely re-designed and -+ re-implemented Unionfs Version 1.x series. Introducing many -+ original ideas, approaches and improvements, it becomes totally -+ different from Unionfs while keeping the basic features. -+ -+if AUFS_FS -+choice -+ prompt "Maximum number of branches" -+ default AUFS_BRANCH_MAX_127 -+ help -+ Specifies the maximum number of branches (or member directories) -+ in a single aufs. The larger value consumes more system -+ resources and has a minor impact to performance. -+config AUFS_BRANCH_MAX_127 -+ bool "127" -+ help -+ Specifies the maximum number of branches (or member directories) -+ in a single aufs. The larger value consumes more system -+ resources and has a minor impact to performance. -+config AUFS_BRANCH_MAX_511 -+ bool "511" -+ help -+ Specifies the maximum number of branches (or member directories) -+ in a single aufs. The larger value consumes more system -+ resources and has a minor impact to performance. -+config AUFS_BRANCH_MAX_1023 -+ bool "1023" -+ help -+ Specifies the maximum number of branches (or member directories) -+ in a single aufs. The larger value consumes more system -+ resources and has a minor impact to performance. -+config AUFS_BRANCH_MAX_32767 -+ bool "32767" -+ help -+ Specifies the maximum number of branches (or member directories) -+ in a single aufs. The larger value consumes more system -+ resources and has a minor impact to performance. -+endchoice -+ -+config AUFS_SBILIST -+ bool -+ depends on AUFS_MAGIC_SYSRQ || PROC_FS -+ default y -+ help -+ Automatic configuration for internal use. -+ When aufs supports Magic SysRq or /proc, enabled automatically. -+ -+config AUFS_HNOTIFY -+ bool "Detect direct branch access (bypassing aufs)" -+ help -+ If you want to modify files on branches directly, eg. bypassing aufs, -+ and want aufs to detect the changes of them fully, then enable this -+ option and use 'udba=notify' mount option. -+ Currently there is only one available configuration, "fsnotify". -+ It will have a negative impact to the performance. -+ See detail in aufs.5. -+ -+choice -+ prompt "method" if AUFS_HNOTIFY -+ default AUFS_HFSNOTIFY -+config AUFS_HFSNOTIFY -+ bool "fsnotify" -+ select FSNOTIFY -+endchoice -+ -+config AUFS_EXPORT -+ bool "NFS-exportable aufs" -+ depends on EXPORTFS -+ help -+ If you want to export your mounted aufs via NFS, then enable this -+ option. There are several requirements for this configuration. -+ See detail in aufs.5. -+ -+config AUFS_INO_T_64 -+ bool -+ depends on AUFS_EXPORT -+ depends on 64BIT && !(ALPHA || S390) -+ default y -+ help -+ Automatic configuration for internal use. -+ /* typedef unsigned long/int __kernel_ino_t */ -+ /* alpha and s390x are int */ -+ -+config AUFS_XATTR -+ bool "support for XATTR/EA (including Security Labels)" -+ help -+ If your branch fs supports XATTR/EA and you want to make them -+ available in aufs too, then enable this opsion and specify the -+ branch attributes for EA. -+ See detail in aufs.5. -+ -+config AUFS_FHSM -+ bool "File-based Hierarchical Storage Management" -+ help -+ Hierarchical Storage Management (or HSM) is a well-known feature -+ in the storage world. Aufs provides this feature as file-based. -+ with multiple branches. -+ These multiple branches are prioritized, ie. the topmost one -+ should be the fastest drive and be used heavily. -+ -+config AUFS_RDU -+ bool "Readdir in userspace" -+ help -+ Aufs has two methods to provide a merged view for a directory, -+ by a user-space library and by kernel-space natively. The latter -+ is always enabled but sometimes large and slow. -+ If you enable this option, install the library in aufs2-util -+ package, and set some environment variables for your readdir(3), -+ then the work will be handled in user-space which generally -+ shows better performance in most cases. -+ See detail in aufs.5. -+ -+config AUFS_SHWH -+ bool "Show whiteouts" -+ help -+ If you want to make the whiteouts in aufs visible, then enable -+ this option and specify 'shwh' mount option. Although it may -+ sounds like philosophy or something, but in technically it -+ simply shows the name of whiteout with keeping its behaviour. -+ -+config AUFS_BR_RAMFS -+ bool "Ramfs (initramfs/rootfs) as an aufs branch" -+ help -+ If you want to use ramfs as an aufs branch fs, then enable this -+ option. Generally tmpfs is recommended. -+ Aufs prohibited them to be a branch fs by default, because -+ initramfs becomes unusable after switch_root or something -+ generally. If you sets initramfs as an aufs branch and boot your -+ system by switch_root, you will meet a problem easily since the -+ files in initramfs may be inaccessible. -+ Unless you are going to use ramfs as an aufs branch fs without -+ switch_root or something, leave it N. -+ -+config AUFS_BR_FUSE -+ bool "Fuse fs as an aufs branch" -+ depends on FUSE_FS -+ select AUFS_POLL -+ help -+ If you want to use fuse-based userspace filesystem as an aufs -+ branch fs, then enable this option. -+ It implements the internal poll(2) operation which is -+ implemented by fuse only (curretnly). -+ -+config AUFS_POLL -+ bool -+ help -+ Automatic configuration for internal use. -+ -+config AUFS_BR_HFSPLUS -+ bool "Hfsplus as an aufs branch" -+ depends on HFSPLUS_FS -+ default y -+ help -+ If you want to use hfsplus fs as an aufs branch fs, then enable -+ this option. This option introduces a small overhead at -+ copying-up a file on hfsplus. -+ -+config AUFS_BDEV_LOOP -+ bool -+ depends on BLK_DEV_LOOP -+ default y -+ help -+ Automatic configuration for internal use. -+ Convert =[ym] into =y. -+ -+config AUFS_DEBUG -+ bool "Debug aufs" -+ help -+ Enable this to compile aufs internal debug code. -+ It will have a negative impact to the performance. -+ -+config AUFS_MAGIC_SYSRQ -+ bool -+ depends on AUFS_DEBUG && MAGIC_SYSRQ -+ default y -+ help -+ Automatic configuration for internal use. -+ When aufs supports Magic SysRq, enabled automatically. -+endif -diff --git a/fs/aufs/Makefile b/fs/aufs/Makefile -new file mode 100644 -index 0000000..c7a501e ---- /dev/null -+++ b/fs/aufs/Makefile -@@ -0,0 +1,44 @@ -+ -+include ${src}/magic.mk -+ifeq (${CONFIG_AUFS_FS},m) -+include ${src}/conf.mk -+endif -+-include ${src}/priv_def.mk -+ -+# cf. include/linux/kernel.h -+# enable pr_debug -+ccflags-y += -DDEBUG -+# sparse requires the full pathname -+ifdef M -+ccflags-y += -include ${M}/../../include/uapi/linux/aufs_type.h -+else -+ccflags-y += -include ${srctree}/include/uapi/linux/aufs_type.h -+endif -+ -+obj-$(CONFIG_AUFS_FS) += aufs.o -+aufs-y := module.o sbinfo.o super.o branch.o xino.o sysaufs.o opts.o \ -+ wkq.o vfsub.o dcsub.o \ -+ cpup.o whout.o wbr_policy.o \ -+ dinfo.o dentry.o \ -+ dynop.o \ -+ finfo.o file.o f_op.o \ -+ dir.o vdir.o \ -+ iinfo.o inode.o i_op.o i_op_add.o i_op_del.o i_op_ren.o \ -+ mvdown.o ioctl.o -+ -+# all are boolean -+aufs-$(CONFIG_PROC_FS) += procfs.o plink.o -+aufs-$(CONFIG_SYSFS) += sysfs.o -+aufs-$(CONFIG_DEBUG_FS) += dbgaufs.o -+aufs-$(CONFIG_AUFS_BDEV_LOOP) += loop.o -+aufs-$(CONFIG_AUFS_HNOTIFY) += hnotify.o -+aufs-$(CONFIG_AUFS_HFSNOTIFY) += hfsnotify.o -+aufs-$(CONFIG_AUFS_EXPORT) += export.o -+aufs-$(CONFIG_AUFS_XATTR) += xattr.o -+aufs-$(CONFIG_FS_POSIX_ACL) += posix_acl.o -+aufs-$(CONFIG_AUFS_FHSM) += fhsm.o -+aufs-$(CONFIG_AUFS_POLL) += poll.o -+aufs-$(CONFIG_AUFS_RDU) += rdu.o -+aufs-$(CONFIG_AUFS_BR_HFSPLUS) += hfsplus.o -+aufs-$(CONFIG_AUFS_DEBUG) += debug.o -+aufs-$(CONFIG_AUFS_MAGIC_SYSRQ) += sysrq.o -diff --git a/fs/aufs/aufs.h b/fs/aufs/aufs.h -new file mode 100644 -index 0000000..e48d268 ---- /dev/null -+++ b/fs/aufs/aufs.h -@@ -0,0 +1,59 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * all header files -+ */ -+ -+#ifndef __AUFS_H__ -+#define __AUFS_H__ -+ -+#ifdef __KERNEL__ -+ -+#define AuStub(type, name, body, ...) \ -+ static inline type name(__VA_ARGS__) { body; } -+ -+#define AuStubVoid(name, ...) \ -+ AuStub(void, name, , __VA_ARGS__) -+#define AuStubInt0(name, ...) \ -+ AuStub(int, name, return 0, __VA_ARGS__) -+ -+#include "debug.h" -+ -+#include "branch.h" -+#include "cpup.h" -+#include "dcsub.h" -+#include "dbgaufs.h" -+#include "dentry.h" -+#include "dir.h" -+#include "dynop.h" -+#include "file.h" -+#include "fstype.h" -+#include "inode.h" -+#include "loop.h" -+#include "module.h" -+#include "opts.h" -+#include "rwsem.h" -+#include "spl.h" -+#include "super.h" -+#include "sysaufs.h" -+#include "vfsub.h" -+#include "whout.h" -+#include "wkq.h" -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_H__ */ -diff --git a/fs/aufs/branch.c b/fs/aufs/branch.c -new file mode 100644 -index 0000000..17210b2 ---- /dev/null -+++ b/fs/aufs/branch.c -@@ -0,0 +1,1402 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * branch management -+ */ -+ -+#include -+#include -+#include "aufs.h" -+ -+/* -+ * free a single branch -+ */ -+static void au_br_do_free(struct au_branch *br) -+{ -+ int i; -+ struct au_wbr *wbr; -+ struct au_dykey **key; -+ -+ au_hnotify_fin_br(br); -+ -+ if (br->br_xino.xi_file) -+ fput(br->br_xino.xi_file); -+ mutex_destroy(&br->br_xino.xi_nondir_mtx); -+ -+ AuDebugOn(atomic_read(&br->br_count)); -+ -+ wbr = br->br_wbr; -+ if (wbr) { -+ for (i = 0; i < AuBrWh_Last; i++) -+ dput(wbr->wbr_wh[i]); -+ AuDebugOn(atomic_read(&wbr->wbr_wh_running)); -+ AuRwDestroy(&wbr->wbr_wh_rwsem); -+ } -+ -+ if (br->br_fhsm) { -+ au_br_fhsm_fin(br->br_fhsm); -+ kfree(br->br_fhsm); -+ } -+ -+ key = br->br_dykey; -+ for (i = 0; i < AuBrDynOp; i++, key++) -+ if (*key) -+ au_dy_put(*key); -+ else -+ break; -+ -+ /* recursive lock, s_umount of branch's */ -+ lockdep_off(); -+ path_put(&br->br_path); -+ lockdep_on(); -+ kfree(wbr); -+ kfree(br); -+} -+ -+/* -+ * frees all branches -+ */ -+void au_br_free(struct au_sbinfo *sbinfo) -+{ -+ aufs_bindex_t bmax; -+ struct au_branch **br; -+ -+ AuRwMustWriteLock(&sbinfo->si_rwsem); -+ -+ bmax = sbinfo->si_bend + 1; -+ br = sbinfo->si_branch; -+ while (bmax--) -+ au_br_do_free(*br++); -+} -+ -+/* -+ * find the index of a branch which is specified by @br_id. -+ */ -+int au_br_index(struct super_block *sb, aufs_bindex_t br_id) -+{ -+ aufs_bindex_t bindex, bend; -+ -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) -+ if (au_sbr_id(sb, bindex) == br_id) -+ return bindex; -+ return -1; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * add a branch -+ */ -+ -+static int test_overlap(struct super_block *sb, struct dentry *h_adding, -+ struct dentry *h_root) -+{ -+ if (unlikely(h_adding == h_root -+ || au_test_loopback_overlap(sb, h_adding))) -+ return 1; -+ if (h_adding->d_sb != h_root->d_sb) -+ return 0; -+ return au_test_subdir(h_adding, h_root) -+ || au_test_subdir(h_root, h_adding); -+} -+ -+/* -+ * returns a newly allocated branch. @new_nbranch is a number of branches -+ * after adding a branch. -+ */ -+static struct au_branch *au_br_alloc(struct super_block *sb, int new_nbranch, -+ int perm) -+{ -+ struct au_branch *add_branch; -+ struct dentry *root; -+ int err; -+ -+ err = -ENOMEM; -+ root = sb->s_root; -+ add_branch = kzalloc(sizeof(*add_branch), GFP_NOFS); -+ if (unlikely(!add_branch)) -+ goto out; -+ -+ err = au_hnotify_init_br(add_branch, perm); -+ if (unlikely(err)) -+ goto out_br; -+ -+ if (au_br_writable(perm)) { -+ /* may be freed separately at changing the branch permission */ -+ add_branch->br_wbr = kzalloc(sizeof(*add_branch->br_wbr), -+ GFP_NOFS); -+ if (unlikely(!add_branch->br_wbr)) -+ goto out_hnotify; -+ } -+ -+ if (au_br_fhsm(perm)) { -+ err = au_fhsm_br_alloc(add_branch); -+ if (unlikely(err)) -+ goto out_wbr; -+ } -+ -+ err = au_sbr_realloc(au_sbi(sb), new_nbranch); -+ if (!err) -+ err = au_di_realloc(au_di(root), new_nbranch); -+ if (!err) -+ err = au_ii_realloc(au_ii(root->d_inode), new_nbranch); -+ if (!err) -+ return add_branch; /* success */ -+ -+out_wbr: -+ kfree(add_branch->br_wbr); -+out_hnotify: -+ au_hnotify_fin_br(add_branch); -+out_br: -+ kfree(add_branch); -+out: -+ return ERR_PTR(err); -+} -+ -+/* -+ * test if the branch permission is legal or not. -+ */ -+static int test_br(struct inode *inode, int brperm, char *path) -+{ -+ int err; -+ -+ err = (au_br_writable(brperm) && IS_RDONLY(inode)); -+ if (!err) -+ goto out; -+ -+ err = -EINVAL; -+ pr_err("write permission for readonly mount or inode, %s\n", path); -+ -+out: -+ return err; -+} -+ -+/* -+ * returns: -+ * 0: success, the caller will add it -+ * plus: success, it is already unified, the caller should ignore it -+ * minus: error -+ */ -+static int test_add(struct super_block *sb, struct au_opt_add *add, int remount) -+{ -+ int err; -+ aufs_bindex_t bend, bindex; -+ struct dentry *root; -+ struct inode *inode, *h_inode; -+ -+ root = sb->s_root; -+ bend = au_sbend(sb); -+ if (unlikely(bend >= 0 -+ && au_find_dbindex(root, add->path.dentry) >= 0)) { -+ err = 1; -+ if (!remount) { -+ err = -EINVAL; -+ pr_err("%s duplicated\n", add->pathname); -+ } -+ goto out; -+ } -+ -+ err = -ENOSPC; /* -E2BIG; */ -+ if (unlikely(AUFS_BRANCH_MAX <= add->bindex -+ || AUFS_BRANCH_MAX - 1 <= bend)) { -+ pr_err("number of branches exceeded %s\n", add->pathname); -+ goto out; -+ } -+ -+ err = -EDOM; -+ if (unlikely(add->bindex < 0 || bend + 1 < add->bindex)) { -+ pr_err("bad index %d\n", add->bindex); -+ goto out; -+ } -+ -+ inode = add->path.dentry->d_inode; -+ err = -ENOENT; -+ if (unlikely(!inode->i_nlink)) { -+ pr_err("no existence %s\n", add->pathname); -+ goto out; -+ } -+ -+ err = -EINVAL; -+ if (unlikely(inode->i_sb == sb)) { -+ pr_err("%s must be outside\n", add->pathname); -+ goto out; -+ } -+ -+ if (unlikely(au_test_fs_unsuppoted(inode->i_sb))) { -+ pr_err("unsupported filesystem, %s (%s)\n", -+ add->pathname, au_sbtype(inode->i_sb)); -+ goto out; -+ } -+ -+ if (unlikely(inode->i_sb->s_stack_depth)) { -+ pr_err("already stacked, %s (%s)\n", -+ add->pathname, au_sbtype(inode->i_sb)); -+ goto out; -+ } -+ -+ err = test_br(add->path.dentry->d_inode, add->perm, add->pathname); -+ if (unlikely(err)) -+ goto out; -+ -+ if (bend < 0) -+ return 0; /* success */ -+ -+ err = -EINVAL; -+ for (bindex = 0; bindex <= bend; bindex++) -+ if (unlikely(test_overlap(sb, add->path.dentry, -+ au_h_dptr(root, bindex)))) { -+ pr_err("%s is overlapped\n", add->pathname); -+ goto out; -+ } -+ -+ err = 0; -+ if (au_opt_test(au_mntflags(sb), WARN_PERM)) { -+ h_inode = au_h_dptr(root, 0)->d_inode; -+ if ((h_inode->i_mode & S_IALLUGO) != (inode->i_mode & S_IALLUGO) -+ || !uid_eq(h_inode->i_uid, inode->i_uid) -+ || !gid_eq(h_inode->i_gid, inode->i_gid)) -+ pr_warn("uid/gid/perm %s %u/%u/0%o, %u/%u/0%o\n", -+ add->pathname, -+ i_uid_read(inode), i_gid_read(inode), -+ (inode->i_mode & S_IALLUGO), -+ i_uid_read(h_inode), i_gid_read(h_inode), -+ (h_inode->i_mode & S_IALLUGO)); -+ } -+ -+out: -+ return err; -+} -+ -+/* -+ * initialize or clean the whiteouts for an adding branch -+ */ -+static int au_br_init_wh(struct super_block *sb, struct au_branch *br, -+ int new_perm) -+{ -+ int err, old_perm; -+ aufs_bindex_t bindex; -+ struct mutex *h_mtx; -+ struct au_wbr *wbr; -+ struct au_hinode *hdir; -+ -+ err = vfsub_mnt_want_write(au_br_mnt(br)); -+ if (unlikely(err)) -+ goto out; -+ -+ wbr = br->br_wbr; -+ old_perm = br->br_perm; -+ br->br_perm = new_perm; -+ hdir = NULL; -+ h_mtx = NULL; -+ bindex = au_br_index(sb, br->br_id); -+ if (0 <= bindex) { -+ hdir = au_hi(sb->s_root->d_inode, bindex); -+ au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); -+ } else { -+ h_mtx = &au_br_dentry(br)->d_inode->i_mutex; -+ mutex_lock_nested(h_mtx, AuLsc_I_PARENT); -+ } -+ if (!wbr) -+ err = au_wh_init(br, sb); -+ else { -+ wbr_wh_write_lock(wbr); -+ err = au_wh_init(br, sb); -+ wbr_wh_write_unlock(wbr); -+ } -+ if (hdir) -+ au_hn_imtx_unlock(hdir); -+ else -+ mutex_unlock(h_mtx); -+ vfsub_mnt_drop_write(au_br_mnt(br)); -+ br->br_perm = old_perm; -+ -+ if (!err && wbr && !au_br_writable(new_perm)) { -+ kfree(wbr); -+ br->br_wbr = NULL; -+ } -+ -+out: -+ return err; -+} -+ -+static int au_wbr_init(struct au_branch *br, struct super_block *sb, -+ int perm) -+{ -+ int err; -+ struct kstatfs kst; -+ struct au_wbr *wbr; -+ -+ wbr = br->br_wbr; -+ au_rw_init(&wbr->wbr_wh_rwsem); -+ atomic_set(&wbr->wbr_wh_running, 0); -+ -+ /* -+ * a limit for rmdir/rename a dir -+ * cf. AUFS_MAX_NAMELEN in include/uapi/linux/aufs_type.h -+ */ -+ err = vfs_statfs(&br->br_path, &kst); -+ if (unlikely(err)) -+ goto out; -+ err = -EINVAL; -+ if (kst.f_namelen >= NAME_MAX) -+ err = au_br_init_wh(sb, br, perm); -+ else -+ pr_err("%pd(%s), unsupported namelen %ld\n", -+ au_br_dentry(br), -+ au_sbtype(au_br_dentry(br)->d_sb), kst.f_namelen); -+ -+out: -+ return err; -+} -+ -+/* initialize a new branch */ -+static int au_br_init(struct au_branch *br, struct super_block *sb, -+ struct au_opt_add *add) -+{ -+ int err; -+ -+ err = 0; -+ mutex_init(&br->br_xino.xi_nondir_mtx); -+ br->br_perm = add->perm; -+ br->br_path = add->path; /* set first, path_get() later */ -+ spin_lock_init(&br->br_dykey_lock); -+ atomic_set(&br->br_count, 0); -+ atomic_set(&br->br_xino_running, 0); -+ br->br_id = au_new_br_id(sb); -+ AuDebugOn(br->br_id < 0); -+ -+ if (au_br_writable(add->perm)) { -+ err = au_wbr_init(br, sb, add->perm); -+ if (unlikely(err)) -+ goto out_err; -+ } -+ -+ if (au_opt_test(au_mntflags(sb), XINO)) { -+ err = au_xino_br(sb, br, add->path.dentry->d_inode->i_ino, -+ au_sbr(sb, 0)->br_xino.xi_file, /*do_test*/1); -+ if (unlikely(err)) { -+ AuDebugOn(br->br_xino.xi_file); -+ goto out_err; -+ } -+ } -+ -+ sysaufs_br_init(br); -+ path_get(&br->br_path); -+ goto out; /* success */ -+ -+out_err: -+ memset(&br->br_path, 0, sizeof(br->br_path)); -+out: -+ return err; -+} -+ -+static void au_br_do_add_brp(struct au_sbinfo *sbinfo, aufs_bindex_t bindex, -+ struct au_branch *br, aufs_bindex_t bend, -+ aufs_bindex_t amount) -+{ -+ struct au_branch **brp; -+ -+ AuRwMustWriteLock(&sbinfo->si_rwsem); -+ -+ brp = sbinfo->si_branch + bindex; -+ memmove(brp + 1, brp, sizeof(*brp) * amount); -+ *brp = br; -+ sbinfo->si_bend++; -+ if (unlikely(bend < 0)) -+ sbinfo->si_bend = 0; -+} -+ -+static void au_br_do_add_hdp(struct au_dinfo *dinfo, aufs_bindex_t bindex, -+ aufs_bindex_t bend, aufs_bindex_t amount) -+{ -+ struct au_hdentry *hdp; -+ -+ AuRwMustWriteLock(&dinfo->di_rwsem); -+ -+ hdp = dinfo->di_hdentry + bindex; -+ memmove(hdp + 1, hdp, sizeof(*hdp) * amount); -+ au_h_dentry_init(hdp); -+ dinfo->di_bend++; -+ if (unlikely(bend < 0)) -+ dinfo->di_bstart = 0; -+} -+ -+static void au_br_do_add_hip(struct au_iinfo *iinfo, aufs_bindex_t bindex, -+ aufs_bindex_t bend, aufs_bindex_t amount) -+{ -+ struct au_hinode *hip; -+ -+ AuRwMustWriteLock(&iinfo->ii_rwsem); -+ -+ hip = iinfo->ii_hinode + bindex; -+ memmove(hip + 1, hip, sizeof(*hip) * amount); -+ hip->hi_inode = NULL; -+ au_hn_init(hip); -+ iinfo->ii_bend++; -+ if (unlikely(bend < 0)) -+ iinfo->ii_bstart = 0; -+} -+ -+static void au_br_do_add(struct super_block *sb, struct au_branch *br, -+ aufs_bindex_t bindex) -+{ -+ struct dentry *root, *h_dentry; -+ struct inode *root_inode; -+ aufs_bindex_t bend, amount; -+ -+ root = sb->s_root; -+ root_inode = root->d_inode; -+ bend = au_sbend(sb); -+ amount = bend + 1 - bindex; -+ h_dentry = au_br_dentry(br); -+ au_sbilist_lock(); -+ au_br_do_add_brp(au_sbi(sb), bindex, br, bend, amount); -+ au_br_do_add_hdp(au_di(root), bindex, bend, amount); -+ au_br_do_add_hip(au_ii(root_inode), bindex, bend, amount); -+ au_set_h_dptr(root, bindex, dget(h_dentry)); -+ au_set_h_iptr(root_inode, bindex, au_igrab(h_dentry->d_inode), -+ /*flags*/0); -+ au_sbilist_unlock(); -+} -+ -+int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount) -+{ -+ int err; -+ aufs_bindex_t bend, add_bindex; -+ struct dentry *root, *h_dentry; -+ struct inode *root_inode; -+ struct au_branch *add_branch; -+ -+ root = sb->s_root; -+ root_inode = root->d_inode; -+ IMustLock(root_inode); -+ err = test_add(sb, add, remount); -+ if (unlikely(err < 0)) -+ goto out; -+ if (err) { -+ err = 0; -+ goto out; /* success */ -+ } -+ -+ bend = au_sbend(sb); -+ add_branch = au_br_alloc(sb, bend + 2, add->perm); -+ err = PTR_ERR(add_branch); -+ if (IS_ERR(add_branch)) -+ goto out; -+ -+ err = au_br_init(add_branch, sb, add); -+ if (unlikely(err)) { -+ au_br_do_free(add_branch); -+ goto out; -+ } -+ -+ add_bindex = add->bindex; -+ if (!remount) -+ au_br_do_add(sb, add_branch, add_bindex); -+ else { -+ sysaufs_brs_del(sb, add_bindex); -+ au_br_do_add(sb, add_branch, add_bindex); -+ sysaufs_brs_add(sb, add_bindex); -+ } -+ -+ h_dentry = add->path.dentry; -+ if (!add_bindex) { -+ au_cpup_attr_all(root_inode, /*force*/1); -+ sb->s_maxbytes = h_dentry->d_sb->s_maxbytes; -+ } else -+ au_add_nlink(root_inode, h_dentry->d_inode); -+ -+ /* -+ * this test/set prevents aufs from handling unnecesary notify events -+ * of xino files, in case of re-adding a writable branch which was -+ * once detached from aufs. -+ */ -+ if (au_xino_brid(sb) < 0 -+ && au_br_writable(add_branch->br_perm) -+ && !au_test_fs_bad_xino(h_dentry->d_sb) -+ && add_branch->br_xino.xi_file -+ && add_branch->br_xino.xi_file->f_dentry->d_parent == h_dentry) -+ au_xino_brid_set(sb, add_branch->br_id); -+ -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static unsigned long long au_farray_cb(void *a, -+ unsigned long long max __maybe_unused, -+ void *arg) -+{ -+ unsigned long long n; -+ struct file **p, *f; -+ struct au_sphlhead *files; -+ struct au_finfo *finfo; -+ struct super_block *sb = arg; -+ -+ n = 0; -+ p = a; -+ files = &au_sbi(sb)->si_files; -+ spin_lock(&files->spin); -+ hlist_for_each_entry(finfo, &files->head, fi_hlist) { -+ f = finfo->fi_file; -+ if (file_count(f) -+ && !special_file(file_inode(f)->i_mode)) { -+ get_file(f); -+ *p++ = f; -+ n++; -+ AuDebugOn(n > max); -+ } -+ } -+ spin_unlock(&files->spin); -+ -+ return n; -+} -+ -+static struct file **au_farray_alloc(struct super_block *sb, -+ unsigned long long *max) -+{ -+ *max = atomic_long_read(&au_sbi(sb)->si_nfiles); -+ return au_array_alloc(max, au_farray_cb, sb); -+} -+ -+static void au_farray_free(struct file **a, unsigned long long max) -+{ -+ unsigned long long ull; -+ -+ for (ull = 0; ull < max; ull++) -+ if (a[ull]) -+ fput(a[ull]); -+ kvfree(a); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * delete a branch -+ */ -+ -+/* to show the line number, do not make it inlined function */ -+#define AuVerbose(do_info, fmt, ...) do { \ -+ if (do_info) \ -+ pr_info(fmt, ##__VA_ARGS__); \ -+} while (0) -+ -+static int au_test_ibusy(struct inode *inode, aufs_bindex_t bstart, -+ aufs_bindex_t bend) -+{ -+ return (inode && !S_ISDIR(inode->i_mode)) || bstart == bend; -+} -+ -+static int au_test_dbusy(struct dentry *dentry, aufs_bindex_t bstart, -+ aufs_bindex_t bend) -+{ -+ return au_test_ibusy(dentry->d_inode, bstart, bend); -+} -+ -+/* -+ * test if the branch is deletable or not. -+ */ -+static int test_dentry_busy(struct dentry *root, aufs_bindex_t bindex, -+ unsigned int sigen, const unsigned int verbose) -+{ -+ int err, i, j, ndentry; -+ aufs_bindex_t bstart, bend; -+ struct au_dcsub_pages dpages; -+ struct au_dpage *dpage; -+ struct dentry *d; -+ -+ err = au_dpages_init(&dpages, GFP_NOFS); -+ if (unlikely(err)) -+ goto out; -+ err = au_dcsub_pages(&dpages, root, NULL, NULL); -+ if (unlikely(err)) -+ goto out_dpages; -+ -+ for (i = 0; !err && i < dpages.ndpage; i++) { -+ dpage = dpages.dpages + i; -+ ndentry = dpage->ndentry; -+ for (j = 0; !err && j < ndentry; j++) { -+ d = dpage->dentries[j]; -+ AuDebugOn(au_dcount(d) <= 0); -+ if (!au_digen_test(d, sigen)) { -+ di_read_lock_child(d, AuLock_IR); -+ if (unlikely(au_dbrange_test(d))) { -+ di_read_unlock(d, AuLock_IR); -+ continue; -+ } -+ } else { -+ di_write_lock_child(d); -+ if (unlikely(au_dbrange_test(d))) { -+ di_write_unlock(d); -+ continue; -+ } -+ err = au_reval_dpath(d, sigen); -+ if (!err) -+ di_downgrade_lock(d, AuLock_IR); -+ else { -+ di_write_unlock(d); -+ break; -+ } -+ } -+ -+ /* AuDbgDentry(d); */ -+ bstart = au_dbstart(d); -+ bend = au_dbend(d); -+ if (bstart <= bindex -+ && bindex <= bend -+ && au_h_dptr(d, bindex) -+ && au_test_dbusy(d, bstart, bend)) { -+ err = -EBUSY; -+ AuVerbose(verbose, "busy %pd\n", d); -+ AuDbgDentry(d); -+ } -+ di_read_unlock(d, AuLock_IR); -+ } -+ } -+ -+out_dpages: -+ au_dpages_free(&dpages); -+out: -+ return err; -+} -+ -+static int test_inode_busy(struct super_block *sb, aufs_bindex_t bindex, -+ unsigned int sigen, const unsigned int verbose) -+{ -+ int err; -+ unsigned long long max, ull; -+ struct inode *i, **array; -+ aufs_bindex_t bstart, bend; -+ -+ array = au_iarray_alloc(sb, &max); -+ err = PTR_ERR(array); -+ if (IS_ERR(array)) -+ goto out; -+ -+ err = 0; -+ AuDbg("b%d\n", bindex); -+ for (ull = 0; !err && ull < max; ull++) { -+ i = array[ull]; -+ if (unlikely(!i)) -+ break; -+ if (i->i_ino == AUFS_ROOT_INO) -+ continue; -+ -+ /* AuDbgInode(i); */ -+ if (au_iigen(i, NULL) == sigen) -+ ii_read_lock_child(i); -+ else { -+ ii_write_lock_child(i); -+ err = au_refresh_hinode_self(i); -+ au_iigen_dec(i); -+ if (!err) -+ ii_downgrade_lock(i); -+ else { -+ ii_write_unlock(i); -+ break; -+ } -+ } -+ -+ bstart = au_ibstart(i); -+ bend = au_ibend(i); -+ if (bstart <= bindex -+ && bindex <= bend -+ && au_h_iptr(i, bindex) -+ && au_test_ibusy(i, bstart, bend)) { -+ err = -EBUSY; -+ AuVerbose(verbose, "busy i%lu\n", i->i_ino); -+ AuDbgInode(i); -+ } -+ ii_read_unlock(i); -+ } -+ au_iarray_free(array, max); -+ -+out: -+ return err; -+} -+ -+static int test_children_busy(struct dentry *root, aufs_bindex_t bindex, -+ const unsigned int verbose) -+{ -+ int err; -+ unsigned int sigen; -+ -+ sigen = au_sigen(root->d_sb); -+ DiMustNoWaiters(root); -+ IiMustNoWaiters(root->d_inode); -+ di_write_unlock(root); -+ err = test_dentry_busy(root, bindex, sigen, verbose); -+ if (!err) -+ err = test_inode_busy(root->d_sb, bindex, sigen, verbose); -+ di_write_lock_child(root); /* aufs_write_lock() calls ..._child() */ -+ -+ return err; -+} -+ -+static int test_dir_busy(struct file *file, aufs_bindex_t br_id, -+ struct file **to_free, int *idx) -+{ -+ int err; -+ unsigned char matched, root; -+ aufs_bindex_t bindex, bend; -+ struct au_fidir *fidir; -+ struct au_hfile *hfile; -+ -+ err = 0; -+ root = IS_ROOT(file->f_dentry); -+ if (root) { -+ get_file(file); -+ to_free[*idx] = file; -+ (*idx)++; -+ goto out; -+ } -+ -+ matched = 0; -+ fidir = au_fi(file)->fi_hdir; -+ AuDebugOn(!fidir); -+ bend = au_fbend_dir(file); -+ for (bindex = au_fbstart(file); bindex <= bend; bindex++) { -+ hfile = fidir->fd_hfile + bindex; -+ if (!hfile->hf_file) -+ continue; -+ -+ if (hfile->hf_br->br_id == br_id) { -+ matched = 1; -+ break; -+ } -+ } -+ if (matched) -+ err = -EBUSY; -+ -+out: -+ return err; -+} -+ -+static int test_file_busy(struct super_block *sb, aufs_bindex_t br_id, -+ struct file **to_free, int opened) -+{ -+ int err, idx; -+ unsigned long long ull, max; -+ aufs_bindex_t bstart; -+ struct file *file, **array; -+ struct dentry *root; -+ struct au_hfile *hfile; -+ -+ array = au_farray_alloc(sb, &max); -+ err = PTR_ERR(array); -+ if (IS_ERR(array)) -+ goto out; -+ -+ err = 0; -+ idx = 0; -+ root = sb->s_root; -+ di_write_unlock(root); -+ for (ull = 0; ull < max; ull++) { -+ file = array[ull]; -+ if (unlikely(!file)) -+ break; -+ -+ /* AuDbg("%pD\n", file); */ -+ fi_read_lock(file); -+ bstart = au_fbstart(file); -+ if (!d_is_dir(file->f_path.dentry)) { -+ hfile = &au_fi(file)->fi_htop; -+ if (hfile->hf_br->br_id == br_id) -+ err = -EBUSY; -+ } else -+ err = test_dir_busy(file, br_id, to_free, &idx); -+ fi_read_unlock(file); -+ if (unlikely(err)) -+ break; -+ } -+ di_write_lock_child(root); -+ au_farray_free(array, max); -+ AuDebugOn(idx > opened); -+ -+out: -+ return err; -+} -+ -+static void br_del_file(struct file **to_free, unsigned long long opened, -+ aufs_bindex_t br_id) -+{ -+ unsigned long long ull; -+ aufs_bindex_t bindex, bstart, bend, bfound; -+ struct file *file; -+ struct au_fidir *fidir; -+ struct au_hfile *hfile; -+ -+ for (ull = 0; ull < opened; ull++) { -+ file = to_free[ull]; -+ if (unlikely(!file)) -+ break; -+ -+ /* AuDbg("%pD\n", file); */ -+ AuDebugOn(!d_is_dir(file->f_path.dentry)); -+ bfound = -1; -+ fidir = au_fi(file)->fi_hdir; -+ AuDebugOn(!fidir); -+ fi_write_lock(file); -+ bstart = au_fbstart(file); -+ bend = au_fbend_dir(file); -+ for (bindex = bstart; bindex <= bend; bindex++) { -+ hfile = fidir->fd_hfile + bindex; -+ if (!hfile->hf_file) -+ continue; -+ -+ if (hfile->hf_br->br_id == br_id) { -+ bfound = bindex; -+ break; -+ } -+ } -+ AuDebugOn(bfound < 0); -+ au_set_h_fptr(file, bfound, NULL); -+ if (bfound == bstart) { -+ for (bstart++; bstart <= bend; bstart++) -+ if (au_hf_dir(file, bstart)) { -+ au_set_fbstart(file, bstart); -+ break; -+ } -+ } -+ fi_write_unlock(file); -+ } -+} -+ -+static void au_br_do_del_brp(struct au_sbinfo *sbinfo, -+ const aufs_bindex_t bindex, -+ const aufs_bindex_t bend) -+{ -+ struct au_branch **brp, **p; -+ -+ AuRwMustWriteLock(&sbinfo->si_rwsem); -+ -+ brp = sbinfo->si_branch + bindex; -+ if (bindex < bend) -+ memmove(brp, brp + 1, sizeof(*brp) * (bend - bindex)); -+ sbinfo->si_branch[0 + bend] = NULL; -+ sbinfo->si_bend--; -+ -+ p = krealloc(sbinfo->si_branch, sizeof(*p) * bend, AuGFP_SBILIST); -+ if (p) -+ sbinfo->si_branch = p; -+ /* harmless error */ -+} -+ -+static void au_br_do_del_hdp(struct au_dinfo *dinfo, const aufs_bindex_t bindex, -+ const aufs_bindex_t bend) -+{ -+ struct au_hdentry *hdp, *p; -+ -+ AuRwMustWriteLock(&dinfo->di_rwsem); -+ -+ hdp = dinfo->di_hdentry; -+ if (bindex < bend) -+ memmove(hdp + bindex, hdp + bindex + 1, -+ sizeof(*hdp) * (bend - bindex)); -+ hdp[0 + bend].hd_dentry = NULL; -+ dinfo->di_bend--; -+ -+ p = krealloc(hdp, sizeof(*p) * bend, AuGFP_SBILIST); -+ if (p) -+ dinfo->di_hdentry = p; -+ /* harmless error */ -+} -+ -+static void au_br_do_del_hip(struct au_iinfo *iinfo, const aufs_bindex_t bindex, -+ const aufs_bindex_t bend) -+{ -+ struct au_hinode *hip, *p; -+ -+ AuRwMustWriteLock(&iinfo->ii_rwsem); -+ -+ hip = iinfo->ii_hinode + bindex; -+ if (bindex < bend) -+ memmove(hip, hip + 1, sizeof(*hip) * (bend - bindex)); -+ iinfo->ii_hinode[0 + bend].hi_inode = NULL; -+ au_hn_init(iinfo->ii_hinode + bend); -+ iinfo->ii_bend--; -+ -+ p = krealloc(iinfo->ii_hinode, sizeof(*p) * bend, AuGFP_SBILIST); -+ if (p) -+ iinfo->ii_hinode = p; -+ /* harmless error */ -+} -+ -+static void au_br_do_del(struct super_block *sb, aufs_bindex_t bindex, -+ struct au_branch *br) -+{ -+ aufs_bindex_t bend; -+ struct au_sbinfo *sbinfo; -+ struct dentry *root, *h_root; -+ struct inode *inode, *h_inode; -+ struct au_hinode *hinode; -+ -+ SiMustWriteLock(sb); -+ -+ root = sb->s_root; -+ inode = root->d_inode; -+ sbinfo = au_sbi(sb); -+ bend = sbinfo->si_bend; -+ -+ h_root = au_h_dptr(root, bindex); -+ hinode = au_hi(inode, bindex); -+ h_inode = au_igrab(hinode->hi_inode); -+ au_hiput(hinode); -+ -+ au_sbilist_lock(); -+ au_br_do_del_brp(sbinfo, bindex, bend); -+ au_br_do_del_hdp(au_di(root), bindex, bend); -+ au_br_do_del_hip(au_ii(inode), bindex, bend); -+ au_sbilist_unlock(); -+ -+ dput(h_root); -+ iput(h_inode); -+ au_br_do_free(br); -+} -+ -+static unsigned long long empty_cb(void *array, unsigned long long max, -+ void *arg) -+{ -+ return max; -+} -+ -+int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount) -+{ -+ int err, rerr, i; -+ unsigned long long opened; -+ unsigned int mnt_flags; -+ aufs_bindex_t bindex, bend, br_id; -+ unsigned char do_wh, verbose; -+ struct au_branch *br; -+ struct au_wbr *wbr; -+ struct dentry *root; -+ struct file **to_free; -+ -+ err = 0; -+ opened = 0; -+ to_free = NULL; -+ root = sb->s_root; -+ bindex = au_find_dbindex(root, del->h_path.dentry); -+ if (bindex < 0) { -+ if (remount) -+ goto out; /* success */ -+ err = -ENOENT; -+ pr_err("%s no such branch\n", del->pathname); -+ goto out; -+ } -+ AuDbg("bindex b%d\n", bindex); -+ -+ err = -EBUSY; -+ mnt_flags = au_mntflags(sb); -+ verbose = !!au_opt_test(mnt_flags, VERBOSE); -+ bend = au_sbend(sb); -+ if (unlikely(!bend)) { -+ AuVerbose(verbose, "no more branches left\n"); -+ goto out; -+ } -+ br = au_sbr(sb, bindex); -+ AuDebugOn(!path_equal(&br->br_path, &del->h_path)); -+ -+ br_id = br->br_id; -+ opened = atomic_read(&br->br_count); -+ if (unlikely(opened)) { -+ to_free = au_array_alloc(&opened, empty_cb, NULL); -+ err = PTR_ERR(to_free); -+ if (IS_ERR(to_free)) -+ goto out; -+ -+ err = test_file_busy(sb, br_id, to_free, opened); -+ if (unlikely(err)) { -+ AuVerbose(verbose, "%llu file(s) opened\n", opened); -+ goto out; -+ } -+ } -+ -+ wbr = br->br_wbr; -+ do_wh = wbr && (wbr->wbr_whbase || wbr->wbr_plink || wbr->wbr_orph); -+ if (do_wh) { -+ /* instead of WbrWhMustWriteLock(wbr) */ -+ SiMustWriteLock(sb); -+ for (i = 0; i < AuBrWh_Last; i++) { -+ dput(wbr->wbr_wh[i]); -+ wbr->wbr_wh[i] = NULL; -+ } -+ } -+ -+ err = test_children_busy(root, bindex, verbose); -+ if (unlikely(err)) { -+ if (do_wh) -+ goto out_wh; -+ goto out; -+ } -+ -+ err = 0; -+ if (to_free) { -+ /* -+ * now we confirmed the branch is deletable. -+ * let's free the remaining opened dirs on the branch. -+ */ -+ di_write_unlock(root); -+ br_del_file(to_free, opened, br_id); -+ di_write_lock_child(root); -+ } -+ -+ if (!remount) -+ au_br_do_del(sb, bindex, br); -+ else { -+ sysaufs_brs_del(sb, bindex); -+ au_br_do_del(sb, bindex, br); -+ sysaufs_brs_add(sb, bindex); -+ } -+ -+ if (!bindex) { -+ au_cpup_attr_all(root->d_inode, /*force*/1); -+ sb->s_maxbytes = au_sbr_sb(sb, 0)->s_maxbytes; -+ } else -+ au_sub_nlink(root->d_inode, del->h_path.dentry->d_inode); -+ if (au_opt_test(mnt_flags, PLINK)) -+ au_plink_half_refresh(sb, br_id); -+ -+ if (au_xino_brid(sb) == br_id) -+ au_xino_brid_set(sb, -1); -+ goto out; /* success */ -+ -+out_wh: -+ /* revert */ -+ rerr = au_br_init_wh(sb, br, br->br_perm); -+ if (rerr) -+ pr_warn("failed re-creating base whiteout, %s. (%d)\n", -+ del->pathname, rerr); -+out: -+ if (to_free) -+ au_farray_free(to_free, opened); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_ibusy(struct super_block *sb, struct aufs_ibusy __user *arg) -+{ -+ int err; -+ aufs_bindex_t bstart, bend; -+ struct aufs_ibusy ibusy; -+ struct inode *inode, *h_inode; -+ -+ err = -EPERM; -+ if (unlikely(!capable(CAP_SYS_ADMIN))) -+ goto out; -+ -+ err = copy_from_user(&ibusy, arg, sizeof(ibusy)); -+ if (!err) -+ err = !access_ok(VERIFY_WRITE, &arg->h_ino, sizeof(arg->h_ino)); -+ if (unlikely(err)) { -+ err = -EFAULT; -+ AuTraceErr(err); -+ goto out; -+ } -+ -+ err = -EINVAL; -+ si_read_lock(sb, AuLock_FLUSH); -+ if (unlikely(ibusy.bindex < 0 || ibusy.bindex > au_sbend(sb))) -+ goto out_unlock; -+ -+ err = 0; -+ ibusy.h_ino = 0; /* invalid */ -+ inode = ilookup(sb, ibusy.ino); -+ if (!inode -+ || inode->i_ino == AUFS_ROOT_INO -+ || is_bad_inode(inode)) -+ goto out_unlock; -+ -+ ii_read_lock_child(inode); -+ bstart = au_ibstart(inode); -+ bend = au_ibend(inode); -+ if (bstart <= ibusy.bindex && ibusy.bindex <= bend) { -+ h_inode = au_h_iptr(inode, ibusy.bindex); -+ if (h_inode && au_test_ibusy(inode, bstart, bend)) -+ ibusy.h_ino = h_inode->i_ino; -+ } -+ ii_read_unlock(inode); -+ iput(inode); -+ -+out_unlock: -+ si_read_unlock(sb); -+ if (!err) { -+ err = __put_user(ibusy.h_ino, &arg->h_ino); -+ if (unlikely(err)) { -+ err = -EFAULT; -+ AuTraceErr(err); -+ } -+ } -+out: -+ return err; -+} -+ -+long au_ibusy_ioctl(struct file *file, unsigned long arg) -+{ -+ return au_ibusy(file->f_dentry->d_sb, (void __user *)arg); -+} -+ -+#ifdef CONFIG_COMPAT -+long au_ibusy_compat_ioctl(struct file *file, unsigned long arg) -+{ -+ return au_ibusy(file->f_dentry->d_sb, compat_ptr(arg)); -+} -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * change a branch permission -+ */ -+ -+static void au_warn_ima(void) -+{ -+#ifdef CONFIG_IMA -+ /* since it doesn't support mark_files_ro() */ -+ AuWarn1("RW -> RO makes IMA to produce wrong message\n"); -+#endif -+} -+ -+static int do_need_sigen_inc(int a, int b) -+{ -+ return au_br_whable(a) && !au_br_whable(b); -+} -+ -+static int need_sigen_inc(int old, int new) -+{ -+ return do_need_sigen_inc(old, new) -+ || do_need_sigen_inc(new, old); -+} -+ -+static int au_br_mod_files_ro(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ int err, do_warn; -+ unsigned int mnt_flags; -+ unsigned long long ull, max; -+ aufs_bindex_t br_id; -+ unsigned char verbose, writer; -+ struct file *file, *hf, **array; -+ struct inode *inode; -+ struct au_hfile *hfile; -+ -+ mnt_flags = au_mntflags(sb); -+ verbose = !!au_opt_test(mnt_flags, VERBOSE); -+ -+ array = au_farray_alloc(sb, &max); -+ err = PTR_ERR(array); -+ if (IS_ERR(array)) -+ goto out; -+ -+ do_warn = 0; -+ br_id = au_sbr_id(sb, bindex); -+ for (ull = 0; ull < max; ull++) { -+ file = array[ull]; -+ if (unlikely(!file)) -+ break; -+ -+ /* AuDbg("%pD\n", file); */ -+ fi_read_lock(file); -+ if (unlikely(au_test_mmapped(file))) { -+ err = -EBUSY; -+ AuVerbose(verbose, "mmapped %pD\n", file); -+ AuDbgFile(file); -+ FiMustNoWaiters(file); -+ fi_read_unlock(file); -+ goto out_array; -+ } -+ -+ inode = file_inode(file); -+ hfile = &au_fi(file)->fi_htop; -+ hf = hfile->hf_file; -+ if (!S_ISREG(inode->i_mode) -+ || !(file->f_mode & FMODE_WRITE) -+ || hfile->hf_br->br_id != br_id -+ || !(hf->f_mode & FMODE_WRITE)) -+ array[ull] = NULL; -+ else { -+ do_warn = 1; -+ get_file(file); -+ } -+ -+ FiMustNoWaiters(file); -+ fi_read_unlock(file); -+ fput(file); -+ } -+ -+ err = 0; -+ if (do_warn) -+ au_warn_ima(); -+ -+ for (ull = 0; ull < max; ull++) { -+ file = array[ull]; -+ if (!file) -+ continue; -+ -+ /* todo: already flushed? */ -+ /* -+ * fs/super.c:mark_files_ro() is gone, but aufs keeps its -+ * approach which resets f_mode and calls mnt_drop_write() and -+ * file_release_write() for each file, because the branch -+ * attribute in aufs world is totally different from the native -+ * fs rw/ro mode. -+ */ -+ /* fi_read_lock(file); */ -+ hfile = &au_fi(file)->fi_htop; -+ hf = hfile->hf_file; -+ /* fi_read_unlock(file); */ -+ spin_lock(&hf->f_lock); -+ writer = !!(hf->f_mode & FMODE_WRITER); -+ hf->f_mode &= ~(FMODE_WRITE | FMODE_WRITER); -+ spin_unlock(&hf->f_lock); -+ if (writer) { -+ put_write_access(file_inode(hf)); -+ __mnt_drop_write(hf->f_path.mnt); -+ } -+ } -+ -+out_array: -+ au_farray_free(array, max); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount, -+ int *do_refresh) -+{ -+ int err, rerr; -+ aufs_bindex_t bindex; -+ struct dentry *root; -+ struct au_branch *br; -+ struct au_br_fhsm *bf; -+ -+ root = sb->s_root; -+ bindex = au_find_dbindex(root, mod->h_root); -+ if (bindex < 0) { -+ if (remount) -+ return 0; /* success */ -+ err = -ENOENT; -+ pr_err("%s no such branch\n", mod->path); -+ goto out; -+ } -+ AuDbg("bindex b%d\n", bindex); -+ -+ err = test_br(mod->h_root->d_inode, mod->perm, mod->path); -+ if (unlikely(err)) -+ goto out; -+ -+ br = au_sbr(sb, bindex); -+ AuDebugOn(mod->h_root != au_br_dentry(br)); -+ if (br->br_perm == mod->perm) -+ return 0; /* success */ -+ -+ /* pre-allocate for non-fhsm --> fhsm */ -+ bf = NULL; -+ if (!au_br_fhsm(br->br_perm) && au_br_fhsm(mod->perm)) { -+ err = au_fhsm_br_alloc(br); -+ if (unlikely(err)) -+ goto out; -+ bf = br->br_fhsm; -+ br->br_fhsm = NULL; -+ } -+ -+ if (au_br_writable(br->br_perm)) { -+ /* remove whiteout base */ -+ err = au_br_init_wh(sb, br, mod->perm); -+ if (unlikely(err)) -+ goto out_bf; -+ -+ if (!au_br_writable(mod->perm)) { -+ /* rw --> ro, file might be mmapped */ -+ DiMustNoWaiters(root); -+ IiMustNoWaiters(root->d_inode); -+ di_write_unlock(root); -+ err = au_br_mod_files_ro(sb, bindex); -+ /* aufs_write_lock() calls ..._child() */ -+ di_write_lock_child(root); -+ -+ if (unlikely(err)) { -+ rerr = -ENOMEM; -+ br->br_wbr = kzalloc(sizeof(*br->br_wbr), -+ GFP_NOFS); -+ if (br->br_wbr) -+ rerr = au_wbr_init(br, sb, br->br_perm); -+ if (unlikely(rerr)) { -+ AuIOErr("nested error %d (%d)\n", -+ rerr, err); -+ br->br_perm = mod->perm; -+ } -+ } -+ } -+ } else if (au_br_writable(mod->perm)) { -+ /* ro --> rw */ -+ err = -ENOMEM; -+ br->br_wbr = kzalloc(sizeof(*br->br_wbr), GFP_NOFS); -+ if (br->br_wbr) { -+ err = au_wbr_init(br, sb, mod->perm); -+ if (unlikely(err)) { -+ kfree(br->br_wbr); -+ br->br_wbr = NULL; -+ } -+ } -+ } -+ if (unlikely(err)) -+ goto out_bf; -+ -+ if (au_br_fhsm(br->br_perm)) { -+ if (!au_br_fhsm(mod->perm)) { -+ /* fhsm --> non-fhsm */ -+ au_br_fhsm_fin(br->br_fhsm); -+ kfree(br->br_fhsm); -+ br->br_fhsm = NULL; -+ } -+ } else if (au_br_fhsm(mod->perm)) -+ /* non-fhsm --> fhsm */ -+ br->br_fhsm = bf; -+ -+ *do_refresh |= need_sigen_inc(br->br_perm, mod->perm); -+ br->br_perm = mod->perm; -+ goto out; /* success */ -+ -+out_bf: -+ kfree(bf); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int au_br_stfs(struct au_branch *br, struct aufs_stfs *stfs) -+{ -+ int err; -+ struct kstatfs kstfs; -+ -+ err = vfs_statfs(&br->br_path, &kstfs); -+ if (!err) { -+ stfs->f_blocks = kstfs.f_blocks; -+ stfs->f_bavail = kstfs.f_bavail; -+ stfs->f_files = kstfs.f_files; -+ stfs->f_ffree = kstfs.f_ffree; -+ } -+ -+ return err; -+} -diff --git a/fs/aufs/branch.h b/fs/aufs/branch.h -new file mode 100644 -index 0000000..6ae006e ---- /dev/null -+++ b/fs/aufs/branch.h -@@ -0,0 +1,279 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * branch filesystems and xino for them -+ */ -+ -+#ifndef __AUFS_BRANCH_H__ -+#define __AUFS_BRANCH_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+#include "dynop.h" -+#include "rwsem.h" -+#include "super.h" -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* a xino file */ -+struct au_xino_file { -+ struct file *xi_file; -+ struct mutex xi_nondir_mtx; -+ -+ /* todo: make xino files an array to support huge inode number */ -+ -+#ifdef CONFIG_DEBUG_FS -+ struct dentry *xi_dbgaufs; -+#endif -+}; -+ -+/* File-based Hierarchical Storage Management */ -+struct au_br_fhsm { -+#ifdef CONFIG_AUFS_FHSM -+ struct mutex bf_lock; -+ unsigned long bf_jiffy; -+ struct aufs_stfs bf_stfs; -+ int bf_readable; -+#endif -+}; -+ -+/* members for writable branch only */ -+enum {AuBrWh_BASE, AuBrWh_PLINK, AuBrWh_ORPH, AuBrWh_Last}; -+struct au_wbr { -+ struct au_rwsem wbr_wh_rwsem; -+ struct dentry *wbr_wh[AuBrWh_Last]; -+ atomic_t wbr_wh_running; -+#define wbr_whbase wbr_wh[AuBrWh_BASE] /* whiteout base */ -+#define wbr_plink wbr_wh[AuBrWh_PLINK] /* pseudo-link dir */ -+#define wbr_orph wbr_wh[AuBrWh_ORPH] /* dir for orphans */ -+ -+ /* mfs mode */ -+ unsigned long long wbr_bytes; -+}; -+ -+/* ext2 has 3 types of operations at least, ext3 has 4 */ -+#define AuBrDynOp (AuDyLast * 4) -+ -+#ifdef CONFIG_AUFS_HFSNOTIFY -+/* support for asynchronous destruction */ -+struct au_br_hfsnotify { -+ struct fsnotify_group *hfsn_group; -+}; -+#endif -+ -+/* sysfs entries */ -+struct au_brsysfs { -+ char name[16]; -+ struct attribute attr; -+}; -+ -+enum { -+ AuBrSysfs_BR, -+ AuBrSysfs_BRID, -+ AuBrSysfs_Last -+}; -+ -+/* protected by superblock rwsem */ -+struct au_branch { -+ struct au_xino_file br_xino; -+ -+ aufs_bindex_t br_id; -+ -+ int br_perm; -+ struct path br_path; -+ spinlock_t br_dykey_lock; -+ struct au_dykey *br_dykey[AuBrDynOp]; -+ atomic_t br_count; -+ -+ struct au_wbr *br_wbr; -+ struct au_br_fhsm *br_fhsm; -+ -+ /* xino truncation */ -+ atomic_t br_xino_running; -+ -+#ifdef CONFIG_AUFS_HFSNOTIFY -+ struct au_br_hfsnotify *br_hfsn; -+#endif -+ -+#ifdef CONFIG_SYSFS -+ /* entries under sysfs per mount-point */ -+ struct au_brsysfs br_sysfs[AuBrSysfs_Last]; -+#endif -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline struct vfsmount *au_br_mnt(struct au_branch *br) -+{ -+ return br->br_path.mnt; -+} -+ -+static inline struct dentry *au_br_dentry(struct au_branch *br) -+{ -+ return br->br_path.dentry; -+} -+ -+static inline struct super_block *au_br_sb(struct au_branch *br) -+{ -+ return au_br_mnt(br)->mnt_sb; -+} -+ -+static inline int au_br_rdonly(struct au_branch *br) -+{ -+ return ((au_br_sb(br)->s_flags & MS_RDONLY) -+ || !au_br_writable(br->br_perm)) -+ ? -EROFS : 0; -+} -+ -+static inline int au_br_hnotifyable(int brperm __maybe_unused) -+{ -+#ifdef CONFIG_AUFS_HNOTIFY -+ return !(brperm & AuBrPerm_RR); -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_br_test_oflag(int oflag, struct au_branch *br) -+{ -+ int err, exec_flag; -+ -+ err = 0; -+ exec_flag = oflag & __FMODE_EXEC; -+ if (unlikely(exec_flag && (au_br_mnt(br)->mnt_flags & MNT_NOEXEC))) -+ err = -EACCES; -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* branch.c */ -+struct au_sbinfo; -+void au_br_free(struct au_sbinfo *sinfo); -+int au_br_index(struct super_block *sb, aufs_bindex_t br_id); -+struct au_opt_add; -+int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount); -+struct au_opt_del; -+int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount); -+long au_ibusy_ioctl(struct file *file, unsigned long arg); -+#ifdef CONFIG_COMPAT -+long au_ibusy_compat_ioctl(struct file *file, unsigned long arg); -+#endif -+struct au_opt_mod; -+int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount, -+ int *do_refresh); -+struct aufs_stfs; -+int au_br_stfs(struct au_branch *br, struct aufs_stfs *stfs); -+ -+/* xino.c */ -+static const loff_t au_loff_max = LLONG_MAX; -+ -+int au_xib_trunc(struct super_block *sb); -+ssize_t xino_fread(au_readf_t func, struct file *file, void *buf, size_t size, -+ loff_t *pos); -+ssize_t xino_fwrite(au_writef_t func, struct file *file, void *buf, size_t size, -+ loff_t *pos); -+struct file *au_xino_create2(struct file *base_file, struct file *copy_src); -+struct file *au_xino_create(struct super_block *sb, char *fname, int silent); -+ino_t au_xino_new_ino(struct super_block *sb); -+void au_xino_delete_inode(struct inode *inode, const int unlinked); -+int au_xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, -+ ino_t ino); -+int au_xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, -+ ino_t *ino); -+int au_xino_br(struct super_block *sb, struct au_branch *br, ino_t hino, -+ struct file *base_file, int do_test); -+int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex); -+ -+struct au_opt_xino; -+int au_xino_set(struct super_block *sb, struct au_opt_xino *xino, int remount); -+void au_xino_clr(struct super_block *sb); -+struct file *au_xino_def(struct super_block *sb); -+int au_xino_path(struct seq_file *seq, struct file *file); -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* Superblock to branch */ -+static inline -+aufs_bindex_t au_sbr_id(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ return au_sbr(sb, bindex)->br_id; -+} -+ -+static inline -+struct vfsmount *au_sbr_mnt(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ return au_br_mnt(au_sbr(sb, bindex)); -+} -+ -+static inline -+struct super_block *au_sbr_sb(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ return au_br_sb(au_sbr(sb, bindex)); -+} -+ -+static inline void au_sbr_put(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ atomic_dec(&au_sbr(sb, bindex)->br_count); -+} -+ -+static inline int au_sbr_perm(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ return au_sbr(sb, bindex)->br_perm; -+} -+ -+static inline int au_sbr_whable(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ return au_br_whable(au_sbr_perm(sb, bindex)); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * wbr_wh_read_lock, wbr_wh_write_lock -+ * wbr_wh_read_unlock, wbr_wh_write_unlock, wbr_wh_downgrade_lock -+ */ -+AuSimpleRwsemFuncs(wbr_wh, struct au_wbr *wbr, &wbr->wbr_wh_rwsem); -+ -+#define WbrWhMustNoWaiters(wbr) AuRwMustNoWaiters(&wbr->wbr_wh_rwsem) -+#define WbrWhMustAnyLock(wbr) AuRwMustAnyLock(&wbr->wbr_wh_rwsem) -+#define WbrWhMustWriteLock(wbr) AuRwMustWriteLock(&wbr->wbr_wh_rwsem) -+ -+/* ---------------------------------------------------------------------- */ -+ -+#ifdef CONFIG_AUFS_FHSM -+static inline void au_br_fhsm_init(struct au_br_fhsm *brfhsm) -+{ -+ mutex_init(&brfhsm->bf_lock); -+ brfhsm->bf_jiffy = 0; -+ brfhsm->bf_readable = 0; -+} -+ -+static inline void au_br_fhsm_fin(struct au_br_fhsm *brfhsm) -+{ -+ mutex_destroy(&brfhsm->bf_lock); -+} -+#else -+AuStubVoid(au_br_fhsm_init, struct au_br_fhsm *brfhsm) -+AuStubVoid(au_br_fhsm_fin, struct au_br_fhsm *brfhsm) -+#endif -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_BRANCH_H__ */ -diff --git a/fs/aufs/conf.mk b/fs/aufs/conf.mk -new file mode 100644 -index 0000000..0bbb2d3 ---- /dev/null -+++ b/fs/aufs/conf.mk -@@ -0,0 +1,38 @@ -+ -+AuConfStr = CONFIG_AUFS_FS=${CONFIG_AUFS_FS} -+ -+define AuConf -+ifdef ${1} -+AuConfStr += ${1}=${${1}} -+endif -+endef -+ -+AuConfAll = BRANCH_MAX_127 BRANCH_MAX_511 BRANCH_MAX_1023 BRANCH_MAX_32767 \ -+ SBILIST \ -+ HNOTIFY HFSNOTIFY \ -+ EXPORT INO_T_64 \ -+ XATTR \ -+ FHSM \ -+ RDU \ -+ SHWH \ -+ BR_RAMFS \ -+ BR_FUSE POLL \ -+ BR_HFSPLUS \ -+ BDEV_LOOP \ -+ DEBUG MAGIC_SYSRQ -+$(foreach i, ${AuConfAll}, \ -+ $(eval $(call AuConf,CONFIG_AUFS_${i}))) -+ -+AuConfName = ${obj}/conf.str -+${AuConfName}.tmp: FORCE -+ @echo ${AuConfStr} | tr ' ' '\n' | sed -e 's/^/"/' -e 's/$$/\\n"/' > $@ -+${AuConfName}: ${AuConfName}.tmp -+ @diff -q $< $@ > /dev/null 2>&1 || { \ -+ echo ' GEN ' $@; \ -+ cp -p $< $@; \ -+ } -+FORCE: -+clean-files += ${AuConfName} ${AuConfName}.tmp -+${obj}/sysfs.o: ${AuConfName} -+ -+-include ${srctree}/${src}/conf_priv.mk -diff --git a/fs/aufs/cpup.c b/fs/aufs/cpup.c -new file mode 100644 -index 0000000..9d8b767 ---- /dev/null -+++ b/fs/aufs/cpup.c -@@ -0,0 +1,1368 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * copy-up functions, see wbr_policy.c for copy-down -+ */ -+ -+#include -+#include -+#include -+#include "aufs.h" -+ -+void au_cpup_attr_flags(struct inode *dst, unsigned int iflags) -+{ -+ const unsigned int mask = S_DEAD | S_SWAPFILE | S_PRIVATE -+ | S_NOATIME | S_NOCMTIME | S_AUTOMOUNT; -+ -+ BUILD_BUG_ON(sizeof(iflags) != sizeof(dst->i_flags)); -+ -+ dst->i_flags |= iflags & ~mask; -+ if (au_test_fs_notime(dst->i_sb)) -+ dst->i_flags |= S_NOATIME | S_NOCMTIME; -+} -+ -+void au_cpup_attr_timesizes(struct inode *inode) -+{ -+ struct inode *h_inode; -+ -+ h_inode = au_h_iptr(inode, au_ibstart(inode)); -+ fsstack_copy_attr_times(inode, h_inode); -+ fsstack_copy_inode_size(inode, h_inode); -+} -+ -+void au_cpup_attr_nlink(struct inode *inode, int force) -+{ -+ struct inode *h_inode; -+ struct super_block *sb; -+ aufs_bindex_t bindex, bend; -+ -+ sb = inode->i_sb; -+ bindex = au_ibstart(inode); -+ h_inode = au_h_iptr(inode, bindex); -+ if (!force -+ && !S_ISDIR(h_inode->i_mode) -+ && au_opt_test(au_mntflags(sb), PLINK) -+ && au_plink_test(inode)) -+ return; -+ -+ /* -+ * 0 can happen in revalidating. -+ * h_inode->i_mutex may not be held here, but it is harmless since once -+ * i_nlink reaches 0, it will never become positive except O_TMPFILE -+ * case. -+ * todo: O_TMPFILE+linkat(AT_SYMLINK_FOLLOW) bypassing aufs may cause -+ * the incorrect link count. -+ */ -+ set_nlink(inode, h_inode->i_nlink); -+ -+ /* -+ * fewer nlink makes find(1) noisy, but larger nlink doesn't. -+ * it may includes whplink directory. -+ */ -+ if (S_ISDIR(h_inode->i_mode)) { -+ bend = au_ibend(inode); -+ for (bindex++; bindex <= bend; bindex++) { -+ h_inode = au_h_iptr(inode, bindex); -+ if (h_inode) -+ au_add_nlink(inode, h_inode); -+ } -+ } -+} -+ -+void au_cpup_attr_changeable(struct inode *inode) -+{ -+ struct inode *h_inode; -+ -+ h_inode = au_h_iptr(inode, au_ibstart(inode)); -+ inode->i_mode = h_inode->i_mode; -+ inode->i_uid = h_inode->i_uid; -+ inode->i_gid = h_inode->i_gid; -+ au_cpup_attr_timesizes(inode); -+ au_cpup_attr_flags(inode, h_inode->i_flags); -+} -+ -+void au_cpup_igen(struct inode *inode, struct inode *h_inode) -+{ -+ struct au_iinfo *iinfo = au_ii(inode); -+ -+ IiMustWriteLock(inode); -+ -+ iinfo->ii_higen = h_inode->i_generation; -+ iinfo->ii_hsb1 = h_inode->i_sb; -+} -+ -+void au_cpup_attr_all(struct inode *inode, int force) -+{ -+ struct inode *h_inode; -+ -+ h_inode = au_h_iptr(inode, au_ibstart(inode)); -+ au_cpup_attr_changeable(inode); -+ if (inode->i_nlink > 0) -+ au_cpup_attr_nlink(inode, force); -+ inode->i_rdev = h_inode->i_rdev; -+ inode->i_blkbits = h_inode->i_blkbits; -+ au_cpup_igen(inode, h_inode); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* Note: dt_dentry and dt_h_dentry are not dget/dput-ed */ -+ -+/* keep the timestamps of the parent dir when cpup */ -+void au_dtime_store(struct au_dtime *dt, struct dentry *dentry, -+ struct path *h_path) -+{ -+ struct inode *h_inode; -+ -+ dt->dt_dentry = dentry; -+ dt->dt_h_path = *h_path; -+ h_inode = h_path->dentry->d_inode; -+ dt->dt_atime = h_inode->i_atime; -+ dt->dt_mtime = h_inode->i_mtime; -+ /* smp_mb(); */ -+} -+ -+void au_dtime_revert(struct au_dtime *dt) -+{ -+ struct iattr attr; -+ int err; -+ -+ attr.ia_atime = dt->dt_atime; -+ attr.ia_mtime = dt->dt_mtime; -+ attr.ia_valid = ATTR_FORCE | ATTR_MTIME | ATTR_MTIME_SET -+ | ATTR_ATIME | ATTR_ATIME_SET; -+ -+ /* no delegation since this is a directory */ -+ err = vfsub_notify_change(&dt->dt_h_path, &attr, /*delegated*/NULL); -+ if (unlikely(err)) -+ pr_warn("restoring timestamps failed(%d). ignored\n", err); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* internal use only */ -+struct au_cpup_reg_attr { -+ int valid; -+ struct kstat st; -+ unsigned int iflags; /* inode->i_flags */ -+}; -+ -+static noinline_for_stack -+int cpup_iattr(struct dentry *dst, aufs_bindex_t bindex, struct dentry *h_src, -+ struct au_cpup_reg_attr *h_src_attr) -+{ -+ int err, sbits, icex; -+ unsigned int mnt_flags; -+ unsigned char verbose; -+ struct iattr ia; -+ struct path h_path; -+ struct inode *h_isrc, *h_idst; -+ struct kstat *h_st; -+ struct au_branch *br; -+ -+ h_path.dentry = au_h_dptr(dst, bindex); -+ h_idst = h_path.dentry->d_inode; -+ br = au_sbr(dst->d_sb, bindex); -+ h_path.mnt = au_br_mnt(br); -+ h_isrc = h_src->d_inode; -+ ia.ia_valid = ATTR_FORCE | ATTR_UID | ATTR_GID -+ | ATTR_ATIME | ATTR_MTIME -+ | ATTR_ATIME_SET | ATTR_MTIME_SET; -+ if (h_src_attr && h_src_attr->valid) { -+ h_st = &h_src_attr->st; -+ ia.ia_uid = h_st->uid; -+ ia.ia_gid = h_st->gid; -+ ia.ia_atime = h_st->atime; -+ ia.ia_mtime = h_st->mtime; -+ if (h_idst->i_mode != h_st->mode -+ && !S_ISLNK(h_idst->i_mode)) { -+ ia.ia_valid |= ATTR_MODE; -+ ia.ia_mode = h_st->mode; -+ } -+ sbits = !!(h_st->mode & (S_ISUID | S_ISGID)); -+ au_cpup_attr_flags(h_idst, h_src_attr->iflags); -+ } else { -+ ia.ia_uid = h_isrc->i_uid; -+ ia.ia_gid = h_isrc->i_gid; -+ ia.ia_atime = h_isrc->i_atime; -+ ia.ia_mtime = h_isrc->i_mtime; -+ if (h_idst->i_mode != h_isrc->i_mode -+ && !S_ISLNK(h_idst->i_mode)) { -+ ia.ia_valid |= ATTR_MODE; -+ ia.ia_mode = h_isrc->i_mode; -+ } -+ sbits = !!(h_isrc->i_mode & (S_ISUID | S_ISGID)); -+ au_cpup_attr_flags(h_idst, h_isrc->i_flags); -+ } -+ /* no delegation since it is just created */ -+ err = vfsub_notify_change(&h_path, &ia, /*delegated*/NULL); -+ -+ /* is this nfs only? */ -+ if (!err && sbits && au_test_nfs(h_path.dentry->d_sb)) { -+ ia.ia_valid = ATTR_FORCE | ATTR_MODE; -+ ia.ia_mode = h_isrc->i_mode; -+ err = vfsub_notify_change(&h_path, &ia, /*delegated*/NULL); -+ } -+ -+ icex = br->br_perm & AuBrAttr_ICEX; -+ if (!err) { -+ mnt_flags = au_mntflags(dst->d_sb); -+ verbose = !!au_opt_test(mnt_flags, VERBOSE); -+ err = au_cpup_xattr(h_path.dentry, h_src, icex, verbose); -+ } -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_do_copy_file(struct file *dst, struct file *src, loff_t len, -+ char *buf, unsigned long blksize) -+{ -+ int err; -+ size_t sz, rbytes, wbytes; -+ unsigned char all_zero; -+ char *p, *zp; -+ struct mutex *h_mtx; -+ /* reduce stack usage */ -+ struct iattr *ia; -+ -+ zp = page_address(ZERO_PAGE(0)); -+ if (unlikely(!zp)) -+ return -ENOMEM; /* possible? */ -+ -+ err = 0; -+ all_zero = 0; -+ while (len) { -+ AuDbg("len %lld\n", len); -+ sz = blksize; -+ if (len < blksize) -+ sz = len; -+ -+ rbytes = 0; -+ /* todo: signal_pending? */ -+ while (!rbytes || err == -EAGAIN || err == -EINTR) { -+ rbytes = vfsub_read_k(src, buf, sz, &src->f_pos); -+ err = rbytes; -+ } -+ if (unlikely(err < 0)) -+ break; -+ -+ all_zero = 0; -+ if (len >= rbytes && rbytes == blksize) -+ all_zero = !memcmp(buf, zp, rbytes); -+ if (!all_zero) { -+ wbytes = rbytes; -+ p = buf; -+ while (wbytes) { -+ size_t b; -+ -+ b = vfsub_write_k(dst, p, wbytes, &dst->f_pos); -+ err = b; -+ /* todo: signal_pending? */ -+ if (unlikely(err == -EAGAIN || err == -EINTR)) -+ continue; -+ if (unlikely(err < 0)) -+ break; -+ wbytes -= b; -+ p += b; -+ } -+ if (unlikely(err < 0)) -+ break; -+ } else { -+ loff_t res; -+ -+ AuLabel(hole); -+ res = vfsub_llseek(dst, rbytes, SEEK_CUR); -+ err = res; -+ if (unlikely(res < 0)) -+ break; -+ } -+ len -= rbytes; -+ err = 0; -+ } -+ -+ /* the last block may be a hole */ -+ if (!err && all_zero) { -+ AuLabel(last hole); -+ -+ err = 1; -+ if (au_test_nfs(dst->f_dentry->d_sb)) { -+ /* nfs requires this step to make last hole */ -+ /* is this only nfs? */ -+ do { -+ /* todo: signal_pending? */ -+ err = vfsub_write_k(dst, "\0", 1, &dst->f_pos); -+ } while (err == -EAGAIN || err == -EINTR); -+ if (err == 1) -+ dst->f_pos--; -+ } -+ -+ if (err == 1) { -+ ia = (void *)buf; -+ ia->ia_size = dst->f_pos; -+ ia->ia_valid = ATTR_SIZE | ATTR_FILE; -+ ia->ia_file = dst; -+ h_mtx = &file_inode(dst)->i_mutex; -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD2); -+ /* no delegation since it is just created */ -+ err = vfsub_notify_change(&dst->f_path, ia, -+ /*delegated*/NULL); -+ mutex_unlock(h_mtx); -+ } -+ } -+ -+ return err; -+} -+ -+int au_copy_file(struct file *dst, struct file *src, loff_t len) -+{ -+ int err; -+ unsigned long blksize; -+ unsigned char do_kfree; -+ char *buf; -+ -+ err = -ENOMEM; -+ blksize = dst->f_dentry->d_sb->s_blocksize; -+ if (!blksize || PAGE_SIZE < blksize) -+ blksize = PAGE_SIZE; -+ AuDbg("blksize %lu\n", blksize); -+ do_kfree = (blksize != PAGE_SIZE && blksize >= sizeof(struct iattr *)); -+ if (do_kfree) -+ buf = kmalloc(blksize, GFP_NOFS); -+ else -+ buf = (void *)__get_free_page(GFP_NOFS); -+ if (unlikely(!buf)) -+ goto out; -+ -+ if (len > (1 << 22)) -+ AuDbg("copying a large file %lld\n", (long long)len); -+ -+ src->f_pos = 0; -+ dst->f_pos = 0; -+ err = au_do_copy_file(dst, src, len, buf, blksize); -+ if (do_kfree) -+ kfree(buf); -+ else -+ free_page((unsigned long)buf); -+ -+out: -+ return err; -+} -+ -+/* -+ * to support a sparse file which is opened with O_APPEND, -+ * we need to close the file. -+ */ -+static int au_cp_regular(struct au_cp_generic *cpg) -+{ -+ int err, i; -+ enum { SRC, DST }; -+ struct { -+ aufs_bindex_t bindex; -+ unsigned int flags; -+ struct dentry *dentry; -+ int force_wr; -+ struct file *file; -+ void *label; -+ } *f, file[] = { -+ { -+ .bindex = cpg->bsrc, -+ .flags = O_RDONLY | O_NOATIME | O_LARGEFILE, -+ .label = &&out -+ }, -+ { -+ .bindex = cpg->bdst, -+ .flags = O_WRONLY | O_NOATIME | O_LARGEFILE, -+ .force_wr = !!au_ftest_cpup(cpg->flags, RWDST), -+ .label = &&out_src -+ } -+ }; -+ struct super_block *sb; -+ struct task_struct *tsk = current; -+ -+ /* bsrc branch can be ro/rw. */ -+ sb = cpg->dentry->d_sb; -+ f = file; -+ for (i = 0; i < 2; i++, f++) { -+ f->dentry = au_h_dptr(cpg->dentry, f->bindex); -+ f->file = au_h_open(cpg->dentry, f->bindex, f->flags, -+ /*file*/NULL, f->force_wr); -+ err = PTR_ERR(f->file); -+ if (IS_ERR(f->file)) -+ goto *f->label; -+ } -+ -+ /* try stopping to update while we copyup */ -+ IMustLock(file[SRC].dentry->d_inode); -+ err = au_copy_file(file[DST].file, file[SRC].file, cpg->len); -+ -+ /* i wonder if we had O_NO_DELAY_FPUT flag */ -+ if (tsk->flags & PF_KTHREAD) -+ __fput_sync(file[DST].file); -+ else { -+ WARN(1, "%pD\nPlease report this warning to aufs-users ML", -+ file[DST].file); -+ fput(file[DST].file); -+ /* -+ * too bad. -+ * we have to call both since we don't know which place the file -+ * was added to. -+ */ -+ task_work_run(); -+ flush_delayed_fput(); -+ } -+ au_sbr_put(sb, file[DST].bindex); -+ -+out_src: -+ fput(file[SRC].file); -+ au_sbr_put(sb, file[SRC].bindex); -+out: -+ return err; -+} -+ -+static int au_do_cpup_regular(struct au_cp_generic *cpg, -+ struct au_cpup_reg_attr *h_src_attr) -+{ -+ int err, rerr; -+ loff_t l; -+ struct path h_path; -+ struct inode *h_src_inode, *h_dst_inode; -+ -+ err = 0; -+ h_src_inode = au_h_iptr(cpg->dentry->d_inode, cpg->bsrc); -+ l = i_size_read(h_src_inode); -+ if (cpg->len == -1 || l < cpg->len) -+ cpg->len = l; -+ if (cpg->len) { -+ /* try stopping to update while we are referencing */ -+ mutex_lock_nested(&h_src_inode->i_mutex, AuLsc_I_CHILD); -+ au_pin_hdir_unlock(cpg->pin); -+ -+ h_path.dentry = au_h_dptr(cpg->dentry, cpg->bsrc); -+ h_path.mnt = au_sbr_mnt(cpg->dentry->d_sb, cpg->bsrc); -+ h_src_attr->iflags = h_src_inode->i_flags; -+ err = vfs_getattr(&h_path, &h_src_attr->st); -+ if (unlikely(err)) { -+ mutex_unlock(&h_src_inode->i_mutex); -+ goto out; -+ } -+ h_src_attr->valid = 1; -+ err = au_cp_regular(cpg); -+ mutex_unlock(&h_src_inode->i_mutex); -+ rerr = au_pin_hdir_relock(cpg->pin); -+ if (!err && rerr) -+ err = rerr; -+ } -+ if (!err && (h_src_inode->i_state & I_LINKABLE)) { -+ h_path.dentry = au_h_dptr(cpg->dentry, cpg->bdst); -+ h_dst_inode = h_path.dentry->d_inode; -+ spin_lock(&h_dst_inode->i_lock); -+ h_dst_inode->i_state |= I_LINKABLE; -+ spin_unlock(&h_dst_inode->i_lock); -+ } -+ -+out: -+ return err; -+} -+ -+static int au_do_cpup_symlink(struct path *h_path, struct dentry *h_src, -+ struct inode *h_dir) -+{ -+ int err, symlen; -+ mm_segment_t old_fs; -+ union { -+ char *k; -+ char __user *u; -+ } sym; -+ -+ err = -ENOSYS; -+ if (unlikely(!h_src->d_inode->i_op->readlink)) -+ goto out; -+ -+ err = -ENOMEM; -+ sym.k = (void *)__get_free_page(GFP_NOFS); -+ if (unlikely(!sym.k)) -+ goto out; -+ -+ /* unnecessary to support mmap_sem since symlink is not mmap-able */ -+ old_fs = get_fs(); -+ set_fs(KERNEL_DS); -+ symlen = h_src->d_inode->i_op->readlink(h_src, sym.u, PATH_MAX); -+ err = symlen; -+ set_fs(old_fs); -+ -+ if (symlen > 0) { -+ sym.k[symlen] = 0; -+ err = vfsub_symlink(h_dir, h_path, sym.k); -+ } -+ free_page((unsigned long)sym.k); -+ -+out: -+ return err; -+} -+ -+/* -+ * regardless 'acl' option, reset all ACL. -+ * All ACL will be copied up later from the original entry on the lower branch. -+ */ -+static int au_reset_acl(struct inode *h_dir, struct path *h_path, umode_t mode) -+{ -+ int err; -+ struct dentry *h_dentry; -+ struct inode *h_inode; -+ -+ h_dentry = h_path->dentry; -+ h_inode = h_dentry->d_inode; -+ /* forget_all_cached_acls(h_inode)); */ -+ err = vfsub_removexattr(h_dentry, XATTR_NAME_POSIX_ACL_ACCESS); -+ AuTraceErr(err); -+ if (err == -EOPNOTSUPP) -+ err = 0; -+ if (!err) -+ err = vfsub_acl_chmod(h_inode, mode); -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+static int au_do_cpup_dir(struct au_cp_generic *cpg, struct dentry *dst_parent, -+ struct inode *h_dir, struct path *h_path) -+{ -+ int err; -+ struct inode *dir; -+ -+ err = vfsub_removexattr(h_path->dentry, XATTR_NAME_POSIX_ACL_DEFAULT); -+ AuTraceErr(err); -+ if (err == -EOPNOTSUPP) -+ err = 0; -+ if (unlikely(err)) -+ goto out; -+ -+ /* -+ * strange behaviour from the users view, -+ * particularry setattr case -+ */ -+ dir = dst_parent->d_inode; -+ if (au_ibstart(dir) == cpg->bdst) -+ au_cpup_attr_nlink(dir, /*force*/1); -+ au_cpup_attr_nlink(cpg->dentry->d_inode, /*force*/1); -+ -+out: -+ return err; -+} -+ -+static noinline_for_stack -+int cpup_entry(struct au_cp_generic *cpg, struct dentry *dst_parent, -+ struct au_cpup_reg_attr *h_src_attr) -+{ -+ int err; -+ umode_t mode; -+ unsigned int mnt_flags; -+ unsigned char isdir, isreg, force; -+ const unsigned char do_dt = !!au_ftest_cpup(cpg->flags, DTIME); -+ struct au_dtime dt; -+ struct path h_path; -+ struct dentry *h_src, *h_dst, *h_parent; -+ struct inode *h_inode, *h_dir; -+ struct super_block *sb; -+ -+ /* bsrc branch can be ro/rw. */ -+ h_src = au_h_dptr(cpg->dentry, cpg->bsrc); -+ h_inode = h_src->d_inode; -+ AuDebugOn(h_inode != au_h_iptr(cpg->dentry->d_inode, cpg->bsrc)); -+ -+ /* try stopping to be referenced while we are creating */ -+ h_dst = au_h_dptr(cpg->dentry, cpg->bdst); -+ if (au_ftest_cpup(cpg->flags, RENAME)) -+ AuDebugOn(strncmp(h_dst->d_name.name, AUFS_WH_PFX, -+ AUFS_WH_PFX_LEN)); -+ h_parent = h_dst->d_parent; /* dir inode is locked */ -+ h_dir = h_parent->d_inode; -+ IMustLock(h_dir); -+ AuDebugOn(h_parent != h_dst->d_parent); -+ -+ sb = cpg->dentry->d_sb; -+ h_path.mnt = au_sbr_mnt(sb, cpg->bdst); -+ if (do_dt) { -+ h_path.dentry = h_parent; -+ au_dtime_store(&dt, dst_parent, &h_path); -+ } -+ h_path.dentry = h_dst; -+ -+ isreg = 0; -+ isdir = 0; -+ mode = h_inode->i_mode; -+ switch (mode & S_IFMT) { -+ case S_IFREG: -+ isreg = 1; -+ err = vfsub_create(h_dir, &h_path, S_IRUSR | S_IWUSR, -+ /*want_excl*/true); -+ if (!err) -+ err = au_do_cpup_regular(cpg, h_src_attr); -+ break; -+ case S_IFDIR: -+ isdir = 1; -+ err = vfsub_mkdir(h_dir, &h_path, mode); -+ if (!err) -+ err = au_do_cpup_dir(cpg, dst_parent, h_dir, &h_path); -+ break; -+ case S_IFLNK: -+ err = au_do_cpup_symlink(&h_path, h_src, h_dir); -+ break; -+ case S_IFCHR: -+ case S_IFBLK: -+ AuDebugOn(!capable(CAP_MKNOD)); -+ /*FALLTHROUGH*/ -+ case S_IFIFO: -+ case S_IFSOCK: -+ err = vfsub_mknod(h_dir, &h_path, mode, h_inode->i_rdev); -+ break; -+ default: -+ AuIOErr("Unknown inode type 0%o\n", mode); -+ err = -EIO; -+ } -+ if (!err) -+ err = au_reset_acl(h_dir, &h_path, mode); -+ -+ mnt_flags = au_mntflags(sb); -+ if (!au_opt_test(mnt_flags, UDBA_NONE) -+ && !isdir -+ && au_opt_test(mnt_flags, XINO) -+ && (h_inode->i_nlink == 1 -+ || (h_inode->i_state & I_LINKABLE)) -+ /* todo: unnecessary? */ -+ /* && cpg->dentry->d_inode->i_nlink == 1 */ -+ && cpg->bdst < cpg->bsrc -+ && !au_ftest_cpup(cpg->flags, KEEPLINO)) -+ au_xino_write(sb, cpg->bsrc, h_inode->i_ino, /*ino*/0); -+ /* ignore this error */ -+ -+ if (!err) { -+ force = 0; -+ if (isreg) { -+ force = !!cpg->len; -+ if (cpg->len == -1) -+ force = !!i_size_read(h_inode); -+ } -+ au_fhsm_wrote(sb, cpg->bdst, force); -+ } -+ -+ if (do_dt) -+ au_dtime_revert(&dt); -+ return err; -+} -+ -+static int au_do_ren_after_cpup(struct au_cp_generic *cpg, struct path *h_path) -+{ -+ int err; -+ struct dentry *dentry, *h_dentry, *h_parent, *parent; -+ struct inode *h_dir; -+ aufs_bindex_t bdst; -+ -+ dentry = cpg->dentry; -+ bdst = cpg->bdst; -+ h_dentry = au_h_dptr(dentry, bdst); -+ if (!au_ftest_cpup(cpg->flags, OVERWRITE)) { -+ dget(h_dentry); -+ au_set_h_dptr(dentry, bdst, NULL); -+ err = au_lkup_neg(dentry, bdst, /*wh*/0); -+ if (!err) -+ h_path->dentry = dget(au_h_dptr(dentry, bdst)); -+ au_set_h_dptr(dentry, bdst, h_dentry); -+ } else { -+ err = 0; -+ parent = dget_parent(dentry); -+ h_parent = au_h_dptr(parent, bdst); -+ dput(parent); -+ h_path->dentry = vfsub_lkup_one(&dentry->d_name, h_parent); -+ if (IS_ERR(h_path->dentry)) -+ err = PTR_ERR(h_path->dentry); -+ } -+ if (unlikely(err)) -+ goto out; -+ -+ h_parent = h_dentry->d_parent; /* dir inode is locked */ -+ h_dir = h_parent->d_inode; -+ IMustLock(h_dir); -+ AuDbg("%pd %pd\n", h_dentry, h_path->dentry); -+ /* no delegation since it is just created */ -+ err = vfsub_rename(h_dir, h_dentry, h_dir, h_path, /*delegated*/NULL); -+ dput(h_path->dentry); -+ -+out: -+ return err; -+} -+ -+/* -+ * copyup the @dentry from @bsrc to @bdst. -+ * the caller must set the both of lower dentries. -+ * @len is for truncating when it is -1 copyup the entire file. -+ * in link/rename cases, @dst_parent may be different from the real one. -+ * basic->bsrc can be larger than basic->bdst. -+ */ -+static int au_cpup_single(struct au_cp_generic *cpg, struct dentry *dst_parent) -+{ -+ int err, rerr; -+ aufs_bindex_t old_ibstart; -+ unsigned char isdir, plink; -+ struct dentry *h_src, *h_dst, *h_parent; -+ struct inode *dst_inode, *h_dir, *inode, *delegated; -+ struct super_block *sb; -+ struct au_branch *br; -+ /* to reuduce stack size */ -+ struct { -+ struct au_dtime dt; -+ struct path h_path; -+ struct au_cpup_reg_attr h_src_attr; -+ } *a; -+ -+ err = -ENOMEM; -+ a = kmalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ a->h_src_attr.valid = 0; -+ -+ sb = cpg->dentry->d_sb; -+ br = au_sbr(sb, cpg->bdst); -+ a->h_path.mnt = au_br_mnt(br); -+ h_dst = au_h_dptr(cpg->dentry, cpg->bdst); -+ h_parent = h_dst->d_parent; /* dir inode is locked */ -+ h_dir = h_parent->d_inode; -+ IMustLock(h_dir); -+ -+ h_src = au_h_dptr(cpg->dentry, cpg->bsrc); -+ inode = cpg->dentry->d_inode; -+ -+ if (!dst_parent) -+ dst_parent = dget_parent(cpg->dentry); -+ else -+ dget(dst_parent); -+ -+ plink = !!au_opt_test(au_mntflags(sb), PLINK); -+ dst_inode = au_h_iptr(inode, cpg->bdst); -+ if (dst_inode) { -+ if (unlikely(!plink)) { -+ err = -EIO; -+ AuIOErr("hi%lu(i%lu) exists on b%d " -+ "but plink is disabled\n", -+ dst_inode->i_ino, inode->i_ino, cpg->bdst); -+ goto out_parent; -+ } -+ -+ if (dst_inode->i_nlink) { -+ const int do_dt = au_ftest_cpup(cpg->flags, DTIME); -+ -+ h_src = au_plink_lkup(inode, cpg->bdst); -+ err = PTR_ERR(h_src); -+ if (IS_ERR(h_src)) -+ goto out_parent; -+ if (unlikely(!h_src->d_inode)) { -+ err = -EIO; -+ AuIOErr("i%lu exists on b%d " -+ "but not pseudo-linked\n", -+ inode->i_ino, cpg->bdst); -+ dput(h_src); -+ goto out_parent; -+ } -+ -+ if (do_dt) { -+ a->h_path.dentry = h_parent; -+ au_dtime_store(&a->dt, dst_parent, &a->h_path); -+ } -+ -+ a->h_path.dentry = h_dst; -+ delegated = NULL; -+ err = vfsub_link(h_src, h_dir, &a->h_path, &delegated); -+ if (!err && au_ftest_cpup(cpg->flags, RENAME)) -+ err = au_do_ren_after_cpup(cpg, &a->h_path); -+ if (do_dt) -+ au_dtime_revert(&a->dt); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal link\n"); -+ iput(delegated); -+ } -+ dput(h_src); -+ goto out_parent; -+ } else -+ /* todo: cpup_wh_file? */ -+ /* udba work */ -+ au_update_ibrange(inode, /*do_put_zero*/1); -+ } -+ -+ isdir = S_ISDIR(inode->i_mode); -+ old_ibstart = au_ibstart(inode); -+ err = cpup_entry(cpg, dst_parent, &a->h_src_attr); -+ if (unlikely(err)) -+ goto out_rev; -+ dst_inode = h_dst->d_inode; -+ mutex_lock_nested(&dst_inode->i_mutex, AuLsc_I_CHILD2); -+ /* todo: necessary? */ -+ /* au_pin_hdir_unlock(cpg->pin); */ -+ -+ err = cpup_iattr(cpg->dentry, cpg->bdst, h_src, &a->h_src_attr); -+ if (unlikely(err)) { -+ /* todo: necessary? */ -+ /* au_pin_hdir_relock(cpg->pin); */ /* ignore an error */ -+ mutex_unlock(&dst_inode->i_mutex); -+ goto out_rev; -+ } -+ -+ if (cpg->bdst < old_ibstart) { -+ if (S_ISREG(inode->i_mode)) { -+ err = au_dy_iaop(inode, cpg->bdst, dst_inode); -+ if (unlikely(err)) { -+ /* ignore an error */ -+ /* au_pin_hdir_relock(cpg->pin); */ -+ mutex_unlock(&dst_inode->i_mutex); -+ goto out_rev; -+ } -+ } -+ au_set_ibstart(inode, cpg->bdst); -+ } else -+ au_set_ibend(inode, cpg->bdst); -+ au_set_h_iptr(inode, cpg->bdst, au_igrab(dst_inode), -+ au_hi_flags(inode, isdir)); -+ -+ /* todo: necessary? */ -+ /* err = au_pin_hdir_relock(cpg->pin); */ -+ mutex_unlock(&dst_inode->i_mutex); -+ if (unlikely(err)) -+ goto out_rev; -+ -+ if (!isdir -+ && (h_src->d_inode->i_nlink > 1 -+ || h_src->d_inode->i_state & I_LINKABLE) -+ && plink) -+ au_plink_append(inode, cpg->bdst, h_dst); -+ -+ if (au_ftest_cpup(cpg->flags, RENAME)) { -+ a->h_path.dentry = h_dst; -+ err = au_do_ren_after_cpup(cpg, &a->h_path); -+ } -+ if (!err) -+ goto out_parent; /* success */ -+ -+ /* revert */ -+out_rev: -+ a->h_path.dentry = h_parent; -+ au_dtime_store(&a->dt, dst_parent, &a->h_path); -+ a->h_path.dentry = h_dst; -+ rerr = 0; -+ if (h_dst->d_inode) { -+ if (!isdir) { -+ /* no delegation since it is just created */ -+ rerr = vfsub_unlink(h_dir, &a->h_path, -+ /*delegated*/NULL, /*force*/0); -+ } else -+ rerr = vfsub_rmdir(h_dir, &a->h_path); -+ } -+ au_dtime_revert(&a->dt); -+ if (rerr) { -+ AuIOErr("failed removing broken entry(%d, %d)\n", err, rerr); -+ err = -EIO; -+ } -+out_parent: -+ dput(dst_parent); -+ kfree(a); -+out: -+ return err; -+} -+ -+#if 0 /* reserved */ -+struct au_cpup_single_args { -+ int *errp; -+ struct au_cp_generic *cpg; -+ struct dentry *dst_parent; -+}; -+ -+static void au_call_cpup_single(void *args) -+{ -+ struct au_cpup_single_args *a = args; -+ -+ au_pin_hdir_acquire_nest(a->cpg->pin); -+ *a->errp = au_cpup_single(a->cpg, a->dst_parent); -+ au_pin_hdir_release(a->cpg->pin); -+} -+#endif -+ -+/* -+ * prevent SIGXFSZ in copy-up. -+ * testing CAP_MKNOD is for generic fs, -+ * but CAP_FSETID is for xfs only, currently. -+ */ -+static int au_cpup_sio_test(struct au_pin *pin, umode_t mode) -+{ -+ int do_sio; -+ struct super_block *sb; -+ struct inode *h_dir; -+ -+ do_sio = 0; -+ sb = au_pinned_parent(pin)->d_sb; -+ if (!au_wkq_test() -+ && (!au_sbi(sb)->si_plink_maint_pid -+ || au_plink_maint(sb, AuLock_NOPLM))) { -+ switch (mode & S_IFMT) { -+ case S_IFREG: -+ /* no condition about RLIMIT_FSIZE and the file size */ -+ do_sio = 1; -+ break; -+ case S_IFCHR: -+ case S_IFBLK: -+ do_sio = !capable(CAP_MKNOD); -+ break; -+ } -+ if (!do_sio) -+ do_sio = ((mode & (S_ISUID | S_ISGID)) -+ && !capable(CAP_FSETID)); -+ /* this workaround may be removed in the future */ -+ if (!do_sio) { -+ h_dir = au_pinned_h_dir(pin); -+ do_sio = h_dir->i_mode & S_ISVTX; -+ } -+ } -+ -+ return do_sio; -+} -+ -+#if 0 /* reserved */ -+int au_sio_cpup_single(struct au_cp_generic *cpg, struct dentry *dst_parent) -+{ -+ int err, wkq_err; -+ struct dentry *h_dentry; -+ -+ h_dentry = au_h_dptr(cpg->dentry, cpg->bsrc); -+ if (!au_cpup_sio_test(pin, h_dentry->d_inode->i_mode)) -+ err = au_cpup_single(cpg, dst_parent); -+ else { -+ struct au_cpup_single_args args = { -+ .errp = &err, -+ .cpg = cpg, -+ .dst_parent = dst_parent -+ }; -+ wkq_err = au_wkq_wait(au_call_cpup_single, &args); -+ if (unlikely(wkq_err)) -+ err = wkq_err; -+ } -+ -+ return err; -+} -+#endif -+ -+/* -+ * copyup the @dentry from the first active lower branch to @bdst, -+ * using au_cpup_single(). -+ */ -+static int au_cpup_simple(struct au_cp_generic *cpg) -+{ -+ int err; -+ unsigned int flags_orig; -+ struct dentry *dentry; -+ -+ AuDebugOn(cpg->bsrc < 0); -+ -+ dentry = cpg->dentry; -+ DiMustWriteLock(dentry); -+ -+ err = au_lkup_neg(dentry, cpg->bdst, /*wh*/1); -+ if (!err) { -+ flags_orig = cpg->flags; -+ au_fset_cpup(cpg->flags, RENAME); -+ err = au_cpup_single(cpg, NULL); -+ cpg->flags = flags_orig; -+ if (!err) -+ return 0; /* success */ -+ -+ /* revert */ -+ au_set_h_dptr(dentry, cpg->bdst, NULL); -+ au_set_dbstart(dentry, cpg->bsrc); -+ } -+ -+ return err; -+} -+ -+struct au_cpup_simple_args { -+ int *errp; -+ struct au_cp_generic *cpg; -+}; -+ -+static void au_call_cpup_simple(void *args) -+{ -+ struct au_cpup_simple_args *a = args; -+ -+ au_pin_hdir_acquire_nest(a->cpg->pin); -+ *a->errp = au_cpup_simple(a->cpg); -+ au_pin_hdir_release(a->cpg->pin); -+} -+ -+static int au_do_sio_cpup_simple(struct au_cp_generic *cpg) -+{ -+ int err, wkq_err; -+ struct dentry *dentry, *parent; -+ struct file *h_file; -+ struct inode *h_dir; -+ -+ dentry = cpg->dentry; -+ h_file = NULL; -+ if (au_ftest_cpup(cpg->flags, HOPEN)) { -+ AuDebugOn(cpg->bsrc < 0); -+ h_file = au_h_open_pre(dentry, cpg->bsrc, /*force_wr*/0); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ } -+ -+ parent = dget_parent(dentry); -+ h_dir = au_h_iptr(parent->d_inode, cpg->bdst); -+ if (!au_test_h_perm_sio(h_dir, MAY_EXEC | MAY_WRITE) -+ && !au_cpup_sio_test(cpg->pin, dentry->d_inode->i_mode)) -+ err = au_cpup_simple(cpg); -+ else { -+ struct au_cpup_simple_args args = { -+ .errp = &err, -+ .cpg = cpg -+ }; -+ wkq_err = au_wkq_wait(au_call_cpup_simple, &args); -+ if (unlikely(wkq_err)) -+ err = wkq_err; -+ } -+ -+ dput(parent); -+ if (h_file) -+ au_h_open_post(dentry, cpg->bsrc, h_file); -+ -+out: -+ return err; -+} -+ -+int au_sio_cpup_simple(struct au_cp_generic *cpg) -+{ -+ aufs_bindex_t bsrc, bend; -+ struct dentry *dentry, *h_dentry; -+ -+ if (cpg->bsrc < 0) { -+ dentry = cpg->dentry; -+ bend = au_dbend(dentry); -+ for (bsrc = cpg->bdst + 1; bsrc <= bend; bsrc++) { -+ h_dentry = au_h_dptr(dentry, bsrc); -+ if (h_dentry) { -+ AuDebugOn(!h_dentry->d_inode); -+ break; -+ } -+ } -+ AuDebugOn(bsrc > bend); -+ cpg->bsrc = bsrc; -+ } -+ AuDebugOn(cpg->bsrc <= cpg->bdst); -+ return au_do_sio_cpup_simple(cpg); -+} -+ -+int au_sio_cpdown_simple(struct au_cp_generic *cpg) -+{ -+ AuDebugOn(cpg->bdst <= cpg->bsrc); -+ return au_do_sio_cpup_simple(cpg); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * copyup the deleted file for writing. -+ */ -+static int au_do_cpup_wh(struct au_cp_generic *cpg, struct dentry *wh_dentry, -+ struct file *file) -+{ -+ int err; -+ unsigned int flags_orig; -+ aufs_bindex_t bsrc_orig; -+ struct dentry *h_d_dst, *h_d_start; -+ struct au_dinfo *dinfo; -+ struct au_hdentry *hdp; -+ -+ dinfo = au_di(cpg->dentry); -+ AuRwMustWriteLock(&dinfo->di_rwsem); -+ -+ bsrc_orig = cpg->bsrc; -+ cpg->bsrc = dinfo->di_bstart; -+ hdp = dinfo->di_hdentry; -+ h_d_dst = hdp[0 + cpg->bdst].hd_dentry; -+ dinfo->di_bstart = cpg->bdst; -+ hdp[0 + cpg->bdst].hd_dentry = wh_dentry; -+ h_d_start = NULL; -+ if (file) { -+ h_d_start = hdp[0 + cpg->bsrc].hd_dentry; -+ hdp[0 + cpg->bsrc].hd_dentry = au_hf_top(file)->f_dentry; -+ } -+ flags_orig = cpg->flags; -+ cpg->flags = !AuCpup_DTIME; -+ err = au_cpup_single(cpg, /*h_parent*/NULL); -+ cpg->flags = flags_orig; -+ if (file) { -+ if (!err) -+ err = au_reopen_nondir(file); -+ hdp[0 + cpg->bsrc].hd_dentry = h_d_start; -+ } -+ hdp[0 + cpg->bdst].hd_dentry = h_d_dst; -+ dinfo->di_bstart = cpg->bsrc; -+ cpg->bsrc = bsrc_orig; -+ -+ return err; -+} -+ -+static int au_cpup_wh(struct au_cp_generic *cpg, struct file *file) -+{ -+ int err; -+ aufs_bindex_t bdst; -+ struct au_dtime dt; -+ struct dentry *dentry, *parent, *h_parent, *wh_dentry; -+ struct au_branch *br; -+ struct path h_path; -+ -+ dentry = cpg->dentry; -+ bdst = cpg->bdst; -+ br = au_sbr(dentry->d_sb, bdst); -+ parent = dget_parent(dentry); -+ h_parent = au_h_dptr(parent, bdst); -+ wh_dentry = au_whtmp_lkup(h_parent, br, &dentry->d_name); -+ err = PTR_ERR(wh_dentry); -+ if (IS_ERR(wh_dentry)) -+ goto out; -+ -+ h_path.dentry = h_parent; -+ h_path.mnt = au_br_mnt(br); -+ au_dtime_store(&dt, parent, &h_path); -+ err = au_do_cpup_wh(cpg, wh_dentry, file); -+ if (unlikely(err)) -+ goto out_wh; -+ -+ dget(wh_dentry); -+ h_path.dentry = wh_dentry; -+ if (!d_is_dir(wh_dentry)) { -+ /* no delegation since it is just created */ -+ err = vfsub_unlink(h_parent->d_inode, &h_path, -+ /*delegated*/NULL, /*force*/0); -+ } else -+ err = vfsub_rmdir(h_parent->d_inode, &h_path); -+ if (unlikely(err)) { -+ AuIOErr("failed remove copied-up tmp file %pd(%d)\n", -+ wh_dentry, err); -+ err = -EIO; -+ } -+ au_dtime_revert(&dt); -+ au_set_hi_wh(dentry->d_inode, bdst, wh_dentry); -+ -+out_wh: -+ dput(wh_dentry); -+out: -+ dput(parent); -+ return err; -+} -+ -+struct au_cpup_wh_args { -+ int *errp; -+ struct au_cp_generic *cpg; -+ struct file *file; -+}; -+ -+static void au_call_cpup_wh(void *args) -+{ -+ struct au_cpup_wh_args *a = args; -+ -+ au_pin_hdir_acquire_nest(a->cpg->pin); -+ *a->errp = au_cpup_wh(a->cpg, a->file); -+ au_pin_hdir_release(a->cpg->pin); -+} -+ -+int au_sio_cpup_wh(struct au_cp_generic *cpg, struct file *file) -+{ -+ int err, wkq_err; -+ aufs_bindex_t bdst; -+ struct dentry *dentry, *parent, *h_orph, *h_parent; -+ struct inode *dir, *h_dir, *h_tmpdir; -+ struct au_wbr *wbr; -+ struct au_pin wh_pin, *pin_orig; -+ -+ dentry = cpg->dentry; -+ bdst = cpg->bdst; -+ parent = dget_parent(dentry); -+ dir = parent->d_inode; -+ h_orph = NULL; -+ h_parent = NULL; -+ h_dir = au_igrab(au_h_iptr(dir, bdst)); -+ h_tmpdir = h_dir; -+ pin_orig = NULL; -+ if (!h_dir->i_nlink) { -+ wbr = au_sbr(dentry->d_sb, bdst)->br_wbr; -+ h_orph = wbr->wbr_orph; -+ -+ h_parent = dget(au_h_dptr(parent, bdst)); -+ au_set_h_dptr(parent, bdst, dget(h_orph)); -+ h_tmpdir = h_orph->d_inode; -+ au_set_h_iptr(dir, bdst, au_igrab(h_tmpdir), /*flags*/0); -+ -+ mutex_lock_nested(&h_tmpdir->i_mutex, AuLsc_I_PARENT3); -+ /* todo: au_h_open_pre()? */ -+ -+ pin_orig = cpg->pin; -+ au_pin_init(&wh_pin, dentry, bdst, AuLsc_DI_PARENT, -+ AuLsc_I_PARENT3, cpg->pin->udba, AuPin_DI_LOCKED); -+ cpg->pin = &wh_pin; -+ } -+ -+ if (!au_test_h_perm_sio(h_tmpdir, MAY_EXEC | MAY_WRITE) -+ && !au_cpup_sio_test(cpg->pin, dentry->d_inode->i_mode)) -+ err = au_cpup_wh(cpg, file); -+ else { -+ struct au_cpup_wh_args args = { -+ .errp = &err, -+ .cpg = cpg, -+ .file = file -+ }; -+ wkq_err = au_wkq_wait(au_call_cpup_wh, &args); -+ if (unlikely(wkq_err)) -+ err = wkq_err; -+ } -+ -+ if (h_orph) { -+ mutex_unlock(&h_tmpdir->i_mutex); -+ /* todo: au_h_open_post()? */ -+ au_set_h_iptr(dir, bdst, au_igrab(h_dir), /*flags*/0); -+ au_set_h_dptr(parent, bdst, h_parent); -+ AuDebugOn(!pin_orig); -+ cpg->pin = pin_orig; -+ } -+ iput(h_dir); -+ dput(parent); -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * generic routine for both of copy-up and copy-down. -+ */ -+/* cf. revalidate function in file.c */ -+int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst, -+ int (*cp)(struct dentry *dentry, aufs_bindex_t bdst, -+ struct au_pin *pin, -+ struct dentry *h_parent, void *arg), -+ void *arg) -+{ -+ int err; -+ struct au_pin pin; -+ struct dentry *d, *parent, *h_parent, *real_parent; -+ -+ err = 0; -+ parent = dget_parent(dentry); -+ if (IS_ROOT(parent)) -+ goto out; -+ -+ au_pin_init(&pin, dentry, bdst, AuLsc_DI_PARENT2, AuLsc_I_PARENT2, -+ au_opt_udba(dentry->d_sb), AuPin_MNT_WRITE); -+ -+ /* do not use au_dpage */ -+ real_parent = parent; -+ while (1) { -+ dput(parent); -+ parent = dget_parent(dentry); -+ h_parent = au_h_dptr(parent, bdst); -+ if (h_parent) -+ goto out; /* success */ -+ -+ /* find top dir which is necessary to cpup */ -+ do { -+ d = parent; -+ dput(parent); -+ parent = dget_parent(d); -+ di_read_lock_parent3(parent, !AuLock_IR); -+ h_parent = au_h_dptr(parent, bdst); -+ di_read_unlock(parent, !AuLock_IR); -+ } while (!h_parent); -+ -+ if (d != real_parent) -+ di_write_lock_child3(d); -+ -+ /* somebody else might create while we were sleeping */ -+ if (!au_h_dptr(d, bdst) || !au_h_dptr(d, bdst)->d_inode) { -+ if (au_h_dptr(d, bdst)) -+ au_update_dbstart(d); -+ -+ au_pin_set_dentry(&pin, d); -+ err = au_do_pin(&pin); -+ if (!err) { -+ err = cp(d, bdst, &pin, h_parent, arg); -+ au_unpin(&pin); -+ } -+ } -+ -+ if (d != real_parent) -+ di_write_unlock(d); -+ if (unlikely(err)) -+ break; -+ } -+ -+out: -+ dput(parent); -+ return err; -+} -+ -+static int au_cpup_dir(struct dentry *dentry, aufs_bindex_t bdst, -+ struct au_pin *pin, -+ struct dentry *h_parent __maybe_unused, -+ void *arg __maybe_unused) -+{ -+ struct au_cp_generic cpg = { -+ .dentry = dentry, -+ .bdst = bdst, -+ .bsrc = -1, -+ .len = 0, -+ .pin = pin, -+ .flags = AuCpup_DTIME -+ }; -+ return au_sio_cpup_simple(&cpg); -+} -+ -+int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst) -+{ -+ return au_cp_dirs(dentry, bdst, au_cpup_dir, NULL); -+} -+ -+int au_test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst) -+{ -+ int err; -+ struct dentry *parent; -+ struct inode *dir; -+ -+ parent = dget_parent(dentry); -+ dir = parent->d_inode; -+ err = 0; -+ if (au_h_iptr(dir, bdst)) -+ goto out; -+ -+ di_read_unlock(parent, AuLock_IR); -+ di_write_lock_parent(parent); -+ /* someone else might change our inode while we were sleeping */ -+ if (!au_h_iptr(dir, bdst)) -+ err = au_cpup_dirs(dentry, bdst); -+ di_downgrade_lock(parent, AuLock_IR); -+ -+out: -+ dput(parent); -+ return err; -+} -diff --git a/fs/aufs/cpup.h b/fs/aufs/cpup.h -new file mode 100644 -index 0000000..7721429 ---- /dev/null -+++ b/fs/aufs/cpup.h -@@ -0,0 +1,94 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * copy-up/down functions -+ */ -+ -+#ifndef __AUFS_CPUP_H__ -+#define __AUFS_CPUP_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+ -+struct inode; -+struct file; -+struct au_pin; -+ -+void au_cpup_attr_flags(struct inode *dst, unsigned int iflags); -+void au_cpup_attr_timesizes(struct inode *inode); -+void au_cpup_attr_nlink(struct inode *inode, int force); -+void au_cpup_attr_changeable(struct inode *inode); -+void au_cpup_igen(struct inode *inode, struct inode *h_inode); -+void au_cpup_attr_all(struct inode *inode, int force); -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct au_cp_generic { -+ struct dentry *dentry; -+ aufs_bindex_t bdst, bsrc; -+ loff_t len; -+ struct au_pin *pin; -+ unsigned int flags; -+}; -+ -+/* cpup flags */ -+#define AuCpup_DTIME 1 /* do dtime_store/revert */ -+#define AuCpup_KEEPLINO (1 << 1) /* do not clear the lower xino, -+ for link(2) */ -+#define AuCpup_RENAME (1 << 2) /* rename after cpup */ -+#define AuCpup_HOPEN (1 << 3) /* call h_open_pre/post() in -+ cpup */ -+#define AuCpup_OVERWRITE (1 << 4) /* allow overwriting the -+ existing entry */ -+#define AuCpup_RWDST (1 << 5) /* force write target even if -+ the branch is marked as RO */ -+ -+#define au_ftest_cpup(flags, name) ((flags) & AuCpup_##name) -+#define au_fset_cpup(flags, name) \ -+ do { (flags) |= AuCpup_##name; } while (0) -+#define au_fclr_cpup(flags, name) \ -+ do { (flags) &= ~AuCpup_##name; } while (0) -+ -+int au_copy_file(struct file *dst, struct file *src, loff_t len); -+int au_sio_cpup_simple(struct au_cp_generic *cpg); -+int au_sio_cpdown_simple(struct au_cp_generic *cpg); -+int au_sio_cpup_wh(struct au_cp_generic *cpg, struct file *file); -+ -+int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst, -+ int (*cp)(struct dentry *dentry, aufs_bindex_t bdst, -+ struct au_pin *pin, -+ struct dentry *h_parent, void *arg), -+ void *arg); -+int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst); -+int au_test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst); -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* keep timestamps when copyup */ -+struct au_dtime { -+ struct dentry *dt_dentry; -+ struct path dt_h_path; -+ struct timespec dt_atime, dt_mtime; -+}; -+void au_dtime_store(struct au_dtime *dt, struct dentry *dentry, -+ struct path *h_path); -+void au_dtime_revert(struct au_dtime *dt); -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_CPUP_H__ */ -diff --git a/fs/aufs/dbgaufs.c b/fs/aufs/dbgaufs.c -new file mode 100644 -index 0000000..b4fdc25 ---- /dev/null -+++ b/fs/aufs/dbgaufs.c -@@ -0,0 +1,432 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * debugfs interface -+ */ -+ -+#include -+#include "aufs.h" -+ -+#ifndef CONFIG_SYSFS -+#error DEBUG_FS depends upon SYSFS -+#endif -+ -+static struct dentry *dbgaufs; -+static const mode_t dbgaufs_mode = S_IRUSR | S_IRGRP | S_IROTH; -+ -+/* 20 is max digits length of ulong 64 */ -+struct dbgaufs_arg { -+ int n; -+ char a[20 * 4]; -+}; -+ -+/* -+ * common function for all XINO files -+ */ -+static int dbgaufs_xi_release(struct inode *inode __maybe_unused, -+ struct file *file) -+{ -+ kfree(file->private_data); -+ return 0; -+} -+ -+static int dbgaufs_xi_open(struct file *xf, struct file *file, int do_fcnt) -+{ -+ int err; -+ struct kstat st; -+ struct dbgaufs_arg *p; -+ -+ err = -ENOMEM; -+ p = kmalloc(sizeof(*p), GFP_NOFS); -+ if (unlikely(!p)) -+ goto out; -+ -+ err = 0; -+ p->n = 0; -+ file->private_data = p; -+ if (!xf) -+ goto out; -+ -+ err = vfs_getattr(&xf->f_path, &st); -+ if (!err) { -+ if (do_fcnt) -+ p->n = snprintf -+ (p->a, sizeof(p->a), "%ld, %llux%lu %lld\n", -+ (long)file_count(xf), st.blocks, st.blksize, -+ (long long)st.size); -+ else -+ p->n = snprintf(p->a, sizeof(p->a), "%llux%lu %lld\n", -+ st.blocks, st.blksize, -+ (long long)st.size); -+ AuDebugOn(p->n >= sizeof(p->a)); -+ } else { -+ p->n = snprintf(p->a, sizeof(p->a), "err %d\n", err); -+ err = 0; -+ } -+ -+out: -+ return err; -+ -+} -+ -+static ssize_t dbgaufs_xi_read(struct file *file, char __user *buf, -+ size_t count, loff_t *ppos) -+{ -+ struct dbgaufs_arg *p; -+ -+ p = file->private_data; -+ return simple_read_from_buffer(buf, count, ppos, p->a, p->n); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct dbgaufs_plink_arg { -+ int n; -+ char a[]; -+}; -+ -+static int dbgaufs_plink_release(struct inode *inode __maybe_unused, -+ struct file *file) -+{ -+ free_page((unsigned long)file->private_data); -+ return 0; -+} -+ -+static int dbgaufs_plink_open(struct inode *inode, struct file *file) -+{ -+ int err, i, limit; -+ unsigned long n, sum; -+ struct dbgaufs_plink_arg *p; -+ struct au_sbinfo *sbinfo; -+ struct super_block *sb; -+ struct au_sphlhead *sphl; -+ -+ err = -ENOMEM; -+ p = (void *)get_zeroed_page(GFP_NOFS); -+ if (unlikely(!p)) -+ goto out; -+ -+ err = -EFBIG; -+ sbinfo = inode->i_private; -+ sb = sbinfo->si_sb; -+ si_noflush_read_lock(sb); -+ if (au_opt_test(au_mntflags(sb), PLINK)) { -+ limit = PAGE_SIZE - sizeof(p->n); -+ -+ /* the number of buckets */ -+ n = snprintf(p->a + p->n, limit, "%d\n", AuPlink_NHASH); -+ p->n += n; -+ limit -= n; -+ -+ sum = 0; -+ for (i = 0, sphl = sbinfo->si_plink; -+ i < AuPlink_NHASH; -+ i++, sphl++) { -+ n = au_sphl_count(sphl); -+ sum += n; -+ -+ n = snprintf(p->a + p->n, limit, "%lu ", n); -+ p->n += n; -+ limit -= n; -+ if (unlikely(limit <= 0)) -+ goto out_free; -+ } -+ p->a[p->n - 1] = '\n'; -+ -+ /* the sum of plinks */ -+ n = snprintf(p->a + p->n, limit, "%lu\n", sum); -+ p->n += n; -+ limit -= n; -+ if (unlikely(limit <= 0)) -+ goto out_free; -+ } else { -+#define str "1\n0\n0\n" -+ p->n = sizeof(str) - 1; -+ strcpy(p->a, str); -+#undef str -+ } -+ si_read_unlock(sb); -+ -+ err = 0; -+ file->private_data = p; -+ goto out; /* success */ -+ -+out_free: -+ free_page((unsigned long)p); -+out: -+ return err; -+} -+ -+static ssize_t dbgaufs_plink_read(struct file *file, char __user *buf, -+ size_t count, loff_t *ppos) -+{ -+ struct dbgaufs_plink_arg *p; -+ -+ p = file->private_data; -+ return simple_read_from_buffer(buf, count, ppos, p->a, p->n); -+} -+ -+static const struct file_operations dbgaufs_plink_fop = { -+ .owner = THIS_MODULE, -+ .open = dbgaufs_plink_open, -+ .release = dbgaufs_plink_release, -+ .read = dbgaufs_plink_read -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int dbgaufs_xib_open(struct inode *inode, struct file *file) -+{ -+ int err; -+ struct au_sbinfo *sbinfo; -+ struct super_block *sb; -+ -+ sbinfo = inode->i_private; -+ sb = sbinfo->si_sb; -+ si_noflush_read_lock(sb); -+ err = dbgaufs_xi_open(sbinfo->si_xib, file, /*do_fcnt*/0); -+ si_read_unlock(sb); -+ return err; -+} -+ -+static const struct file_operations dbgaufs_xib_fop = { -+ .owner = THIS_MODULE, -+ .open = dbgaufs_xib_open, -+ .release = dbgaufs_xi_release, -+ .read = dbgaufs_xi_read -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+#define DbgaufsXi_PREFIX "xi" -+ -+static int dbgaufs_xino_open(struct inode *inode, struct file *file) -+{ -+ int err; -+ long l; -+ struct au_sbinfo *sbinfo; -+ struct super_block *sb; -+ struct file *xf; -+ struct qstr *name; -+ -+ err = -ENOENT; -+ xf = NULL; -+ name = &file->f_dentry->d_name; -+ if (unlikely(name->len < sizeof(DbgaufsXi_PREFIX) -+ || memcmp(name->name, DbgaufsXi_PREFIX, -+ sizeof(DbgaufsXi_PREFIX) - 1))) -+ goto out; -+ err = kstrtol(name->name + sizeof(DbgaufsXi_PREFIX) - 1, 10, &l); -+ if (unlikely(err)) -+ goto out; -+ -+ sbinfo = inode->i_private; -+ sb = sbinfo->si_sb; -+ si_noflush_read_lock(sb); -+ if (l <= au_sbend(sb)) { -+ xf = au_sbr(sb, (aufs_bindex_t)l)->br_xino.xi_file; -+ err = dbgaufs_xi_open(xf, file, /*do_fcnt*/1); -+ } else -+ err = -ENOENT; -+ si_read_unlock(sb); -+ -+out: -+ return err; -+} -+ -+static const struct file_operations dbgaufs_xino_fop = { -+ .owner = THIS_MODULE, -+ .open = dbgaufs_xino_open, -+ .release = dbgaufs_xi_release, -+ .read = dbgaufs_xi_read -+}; -+ -+void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ aufs_bindex_t bend; -+ struct au_branch *br; -+ struct au_xino_file *xi; -+ -+ if (!au_sbi(sb)->si_dbgaufs) -+ return; -+ -+ bend = au_sbend(sb); -+ for (; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ xi = &br->br_xino; -+ debugfs_remove(xi->xi_dbgaufs); -+ xi->xi_dbgaufs = NULL; -+ } -+} -+ -+void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ struct au_sbinfo *sbinfo; -+ struct dentry *parent; -+ struct au_branch *br; -+ struct au_xino_file *xi; -+ aufs_bindex_t bend; -+ char name[sizeof(DbgaufsXi_PREFIX) + 5]; /* "xi" bindex NULL */ -+ -+ sbinfo = au_sbi(sb); -+ parent = sbinfo->si_dbgaufs; -+ if (!parent) -+ return; -+ -+ bend = au_sbend(sb); -+ for (; bindex <= bend; bindex++) { -+ snprintf(name, sizeof(name), DbgaufsXi_PREFIX "%d", bindex); -+ br = au_sbr(sb, bindex); -+ xi = &br->br_xino; -+ AuDebugOn(xi->xi_dbgaufs); -+ xi->xi_dbgaufs = debugfs_create_file(name, dbgaufs_mode, parent, -+ sbinfo, &dbgaufs_xino_fop); -+ /* ignore an error */ -+ if (unlikely(!xi->xi_dbgaufs)) -+ AuWarn1("failed %s under debugfs\n", name); -+ } -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+#ifdef CONFIG_AUFS_EXPORT -+static int dbgaufs_xigen_open(struct inode *inode, struct file *file) -+{ -+ int err; -+ struct au_sbinfo *sbinfo; -+ struct super_block *sb; -+ -+ sbinfo = inode->i_private; -+ sb = sbinfo->si_sb; -+ si_noflush_read_lock(sb); -+ err = dbgaufs_xi_open(sbinfo->si_xigen, file, /*do_fcnt*/0); -+ si_read_unlock(sb); -+ return err; -+} -+ -+static const struct file_operations dbgaufs_xigen_fop = { -+ .owner = THIS_MODULE, -+ .open = dbgaufs_xigen_open, -+ .release = dbgaufs_xi_release, -+ .read = dbgaufs_xi_read -+}; -+ -+static int dbgaufs_xigen_init(struct au_sbinfo *sbinfo) -+{ -+ int err; -+ -+ /* -+ * This function is a dynamic '__init' function actually, -+ * so the tiny check for si_rwsem is unnecessary. -+ */ -+ /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ -+ -+ err = -EIO; -+ sbinfo->si_dbgaufs_xigen = debugfs_create_file -+ ("xigen", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, -+ &dbgaufs_xigen_fop); -+ if (sbinfo->si_dbgaufs_xigen) -+ err = 0; -+ -+ return err; -+} -+#else -+static int dbgaufs_xigen_init(struct au_sbinfo *sbinfo) -+{ -+ return 0; -+} -+#endif /* CONFIG_AUFS_EXPORT */ -+ -+/* ---------------------------------------------------------------------- */ -+ -+void dbgaufs_si_fin(struct au_sbinfo *sbinfo) -+{ -+ /* -+ * This function is a dynamic '__fin' function actually, -+ * so the tiny check for si_rwsem is unnecessary. -+ */ -+ /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ -+ -+ debugfs_remove_recursive(sbinfo->si_dbgaufs); -+ sbinfo->si_dbgaufs = NULL; -+ kobject_put(&sbinfo->si_kobj); -+} -+ -+int dbgaufs_si_init(struct au_sbinfo *sbinfo) -+{ -+ int err; -+ char name[SysaufsSiNameLen]; -+ -+ /* -+ * This function is a dynamic '__init' function actually, -+ * so the tiny check for si_rwsem is unnecessary. -+ */ -+ /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ -+ -+ err = -ENOENT; -+ if (!dbgaufs) { -+ AuErr1("/debug/aufs is uninitialized\n"); -+ goto out; -+ } -+ -+ err = -EIO; -+ sysaufs_name(sbinfo, name); -+ sbinfo->si_dbgaufs = debugfs_create_dir(name, dbgaufs); -+ if (unlikely(!sbinfo->si_dbgaufs)) -+ goto out; -+ kobject_get(&sbinfo->si_kobj); -+ -+ sbinfo->si_dbgaufs_xib = debugfs_create_file -+ ("xib", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, -+ &dbgaufs_xib_fop); -+ if (unlikely(!sbinfo->si_dbgaufs_xib)) -+ goto out_dir; -+ -+ sbinfo->si_dbgaufs_plink = debugfs_create_file -+ ("plink", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, -+ &dbgaufs_plink_fop); -+ if (unlikely(!sbinfo->si_dbgaufs_plink)) -+ goto out_dir; -+ -+ err = dbgaufs_xigen_init(sbinfo); -+ if (!err) -+ goto out; /* success */ -+ -+out_dir: -+ dbgaufs_si_fin(sbinfo); -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void dbgaufs_fin(void) -+{ -+ debugfs_remove(dbgaufs); -+} -+ -+int __init dbgaufs_init(void) -+{ -+ int err; -+ -+ err = -EIO; -+ dbgaufs = debugfs_create_dir(AUFS_NAME, NULL); -+ if (dbgaufs) -+ err = 0; -+ return err; -+} -diff --git a/fs/aufs/dbgaufs.h b/fs/aufs/dbgaufs.h -new file mode 100644 -index 0000000..d1e09bd ---- /dev/null -+++ b/fs/aufs/dbgaufs.h -@@ -0,0 +1,48 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * debugfs interface -+ */ -+ -+#ifndef __DBGAUFS_H__ -+#define __DBGAUFS_H__ -+ -+#ifdef __KERNEL__ -+ -+struct super_block; -+struct au_sbinfo; -+ -+#ifdef CONFIG_DEBUG_FS -+/* dbgaufs.c */ -+void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex); -+void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex); -+void dbgaufs_si_fin(struct au_sbinfo *sbinfo); -+int dbgaufs_si_init(struct au_sbinfo *sbinfo); -+void dbgaufs_fin(void); -+int __init dbgaufs_init(void); -+#else -+AuStubVoid(dbgaufs_brs_del, struct super_block *sb, aufs_bindex_t bindex) -+AuStubVoid(dbgaufs_brs_add, struct super_block *sb, aufs_bindex_t bindex) -+AuStubVoid(dbgaufs_si_fin, struct au_sbinfo *sbinfo) -+AuStubInt0(dbgaufs_si_init, struct au_sbinfo *sbinfo) -+AuStubVoid(dbgaufs_fin, void) -+AuStubInt0(__init dbgaufs_init, void) -+#endif /* CONFIG_DEBUG_FS */ -+ -+#endif /* __KERNEL__ */ -+#endif /* __DBGAUFS_H__ */ -diff --git a/fs/aufs/dcsub.c b/fs/aufs/dcsub.c -new file mode 100644 -index 0000000..832baa4 ---- /dev/null -+++ b/fs/aufs/dcsub.c -@@ -0,0 +1,224 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * sub-routines for dentry cache -+ */ -+ -+#include "aufs.h" -+ -+static void au_dpage_free(struct au_dpage *dpage) -+{ -+ int i; -+ struct dentry **p; -+ -+ p = dpage->dentries; -+ for (i = 0; i < dpage->ndentry; i++) -+ dput(*p++); -+ free_page((unsigned long)dpage->dentries); -+} -+ -+int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp) -+{ -+ int err; -+ void *p; -+ -+ err = -ENOMEM; -+ dpages->dpages = kmalloc(sizeof(*dpages->dpages), gfp); -+ if (unlikely(!dpages->dpages)) -+ goto out; -+ -+ p = (void *)__get_free_page(gfp); -+ if (unlikely(!p)) -+ goto out_dpages; -+ -+ dpages->dpages[0].ndentry = 0; -+ dpages->dpages[0].dentries = p; -+ dpages->ndpage = 1; -+ return 0; /* success */ -+ -+out_dpages: -+ kfree(dpages->dpages); -+out: -+ return err; -+} -+ -+void au_dpages_free(struct au_dcsub_pages *dpages) -+{ -+ int i; -+ struct au_dpage *p; -+ -+ p = dpages->dpages; -+ for (i = 0; i < dpages->ndpage; i++) -+ au_dpage_free(p++); -+ kfree(dpages->dpages); -+} -+ -+static int au_dpages_append(struct au_dcsub_pages *dpages, -+ struct dentry *dentry, gfp_t gfp) -+{ -+ int err, sz; -+ struct au_dpage *dpage; -+ void *p; -+ -+ dpage = dpages->dpages + dpages->ndpage - 1; -+ sz = PAGE_SIZE / sizeof(dentry); -+ if (unlikely(dpage->ndentry >= sz)) { -+ AuLabel(new dpage); -+ err = -ENOMEM; -+ sz = dpages->ndpage * sizeof(*dpages->dpages); -+ p = au_kzrealloc(dpages->dpages, sz, -+ sz + sizeof(*dpages->dpages), gfp); -+ if (unlikely(!p)) -+ goto out; -+ -+ dpages->dpages = p; -+ dpage = dpages->dpages + dpages->ndpage; -+ p = (void *)__get_free_page(gfp); -+ if (unlikely(!p)) -+ goto out; -+ -+ dpage->ndentry = 0; -+ dpage->dentries = p; -+ dpages->ndpage++; -+ } -+ -+ AuDebugOn(au_dcount(dentry) <= 0); -+ dpage->dentries[dpage->ndentry++] = dget_dlock(dentry); -+ return 0; /* success */ -+ -+out: -+ return err; -+} -+ -+/* todo: BAD approach */ -+/* copied from linux/fs/dcache.c */ -+enum d_walk_ret { -+ D_WALK_CONTINUE, -+ D_WALK_QUIT, -+ D_WALK_NORETRY, -+ D_WALK_SKIP, -+}; -+ -+extern void d_walk(struct dentry *parent, void *data, -+ enum d_walk_ret (*enter)(void *, struct dentry *), -+ void (*finish)(void *)); -+ -+struct ac_dpages_arg { -+ int err; -+ struct au_dcsub_pages *dpages; -+ struct super_block *sb; -+ au_dpages_test test; -+ void *arg; -+}; -+ -+static enum d_walk_ret au_call_dpages_append(void *_arg, struct dentry *dentry) -+{ -+ enum d_walk_ret ret; -+ struct ac_dpages_arg *arg = _arg; -+ -+ ret = D_WALK_CONTINUE; -+ if (dentry->d_sb == arg->sb -+ && !IS_ROOT(dentry) -+ && au_dcount(dentry) > 0 -+ && au_di(dentry) -+ && (!arg->test || arg->test(dentry, arg->arg))) { -+ arg->err = au_dpages_append(arg->dpages, dentry, GFP_ATOMIC); -+ if (unlikely(arg->err)) -+ ret = D_WALK_QUIT; -+ } -+ -+ return ret; -+} -+ -+int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root, -+ au_dpages_test test, void *arg) -+{ -+ struct ac_dpages_arg args = { -+ .err = 0, -+ .dpages = dpages, -+ .sb = root->d_sb, -+ .test = test, -+ .arg = arg -+ }; -+ -+ d_walk(root, &args, au_call_dpages_append, NULL); -+ -+ return args.err; -+} -+ -+int au_dcsub_pages_rev(struct au_dcsub_pages *dpages, struct dentry *dentry, -+ int do_include, au_dpages_test test, void *arg) -+{ -+ int err; -+ -+ err = 0; -+ write_seqlock(&rename_lock); -+ spin_lock(&dentry->d_lock); -+ if (do_include -+ && au_dcount(dentry) > 0 -+ && (!test || test(dentry, arg))) -+ err = au_dpages_append(dpages, dentry, GFP_ATOMIC); -+ spin_unlock(&dentry->d_lock); -+ if (unlikely(err)) -+ goto out; -+ -+ /* -+ * RCU for vfsmount is unnecessary since this is a traverse in a single -+ * mount -+ */ -+ while (!IS_ROOT(dentry)) { -+ dentry = dentry->d_parent; /* rename_lock is locked */ -+ spin_lock(&dentry->d_lock); -+ if (au_dcount(dentry) > 0 -+ && (!test || test(dentry, arg))) -+ err = au_dpages_append(dpages, dentry, GFP_ATOMIC); -+ spin_unlock(&dentry->d_lock); -+ if (unlikely(err)) -+ break; -+ } -+ -+out: -+ write_sequnlock(&rename_lock); -+ return err; -+} -+ -+static inline int au_dcsub_dpages_aufs(struct dentry *dentry, void *arg) -+{ -+ return au_di(dentry) && dentry->d_sb == arg; -+} -+ -+int au_dcsub_pages_rev_aufs(struct au_dcsub_pages *dpages, -+ struct dentry *dentry, int do_include) -+{ -+ return au_dcsub_pages_rev(dpages, dentry, do_include, -+ au_dcsub_dpages_aufs, dentry->d_sb); -+} -+ -+int au_test_subdir(struct dentry *d1, struct dentry *d2) -+{ -+ struct path path[2] = { -+ { -+ .dentry = d1 -+ }, -+ { -+ .dentry = d2 -+ } -+ }; -+ -+ return path_is_under(path + 0, path + 1); -+} -diff --git a/fs/aufs/dcsub.h b/fs/aufs/dcsub.h -new file mode 100644 -index 0000000..7997944 ---- /dev/null -+++ b/fs/aufs/dcsub.h -@@ -0,0 +1,123 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * sub-routines for dentry cache -+ */ -+ -+#ifndef __AUFS_DCSUB_H__ -+#define __AUFS_DCSUB_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+#include -+ -+struct au_dpage { -+ int ndentry; -+ struct dentry **dentries; -+}; -+ -+struct au_dcsub_pages { -+ int ndpage; -+ struct au_dpage *dpages; -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* dcsub.c */ -+int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp); -+void au_dpages_free(struct au_dcsub_pages *dpages); -+typedef int (*au_dpages_test)(struct dentry *dentry, void *arg); -+int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root, -+ au_dpages_test test, void *arg); -+int au_dcsub_pages_rev(struct au_dcsub_pages *dpages, struct dentry *dentry, -+ int do_include, au_dpages_test test, void *arg); -+int au_dcsub_pages_rev_aufs(struct au_dcsub_pages *dpages, -+ struct dentry *dentry, int do_include); -+int au_test_subdir(struct dentry *d1, struct dentry *d2); -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * todo: in linux-3.13, several similar (but faster) helpers are added to -+ * include/linux/dcache.h. Try them (in the future). -+ */ -+ -+static inline int au_d_hashed_positive(struct dentry *d) -+{ -+ int err; -+ struct inode *inode = d->d_inode; -+ -+ err = 0; -+ if (unlikely(d_unhashed(d) || !inode || !inode->i_nlink)) -+ err = -ENOENT; -+ return err; -+} -+ -+static inline int au_d_linkable(struct dentry *d) -+{ -+ int err; -+ struct inode *inode = d->d_inode; -+ -+ err = au_d_hashed_positive(d); -+ if (err -+ && inode -+ && (inode->i_state & I_LINKABLE)) -+ err = 0; -+ return err; -+} -+ -+static inline int au_d_alive(struct dentry *d) -+{ -+ int err; -+ struct inode *inode; -+ -+ err = 0; -+ if (!IS_ROOT(d)) -+ err = au_d_hashed_positive(d); -+ else { -+ inode = d->d_inode; -+ if (unlikely(d_unlinked(d) || !inode || !inode->i_nlink)) -+ err = -ENOENT; -+ } -+ return err; -+} -+ -+static inline int au_alive_dir(struct dentry *d) -+{ -+ int err; -+ -+ err = au_d_alive(d); -+ if (unlikely(err || IS_DEADDIR(d->d_inode))) -+ err = -ENOENT; -+ return err; -+} -+ -+static inline int au_qstreq(struct qstr *a, struct qstr *b) -+{ -+ return a->len == b->len -+ && !memcmp(a->name, b->name, a->len); -+} -+ -+static inline int au_dcount(struct dentry *d) -+{ -+ return (int)d_count(d); -+} -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_DCSUB_H__ */ -diff --git a/fs/aufs/debug.c b/fs/aufs/debug.c -new file mode 100644 -index 0000000..2747d13 ---- /dev/null -+++ b/fs/aufs/debug.c -@@ -0,0 +1,436 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * debug print functions -+ */ -+ -+#include "aufs.h" -+ -+/* Returns 0, or -errno. arg is in kp->arg. */ -+static int param_atomic_t_set(const char *val, const struct kernel_param *kp) -+{ -+ int err, n; -+ -+ err = kstrtoint(val, 0, &n); -+ if (!err) { -+ if (n > 0) -+ au_debug_on(); -+ else -+ au_debug_off(); -+ } -+ return err; -+} -+ -+/* Returns length written or -errno. Buffer is 4k (ie. be short!) */ -+static int param_atomic_t_get(char *buffer, const struct kernel_param *kp) -+{ -+ atomic_t *a; -+ -+ a = kp->arg; -+ return sprintf(buffer, "%d", atomic_read(a)); -+} -+ -+static struct kernel_param_ops param_ops_atomic_t = { -+ .set = param_atomic_t_set, -+ .get = param_atomic_t_get -+ /* void (*free)(void *arg) */ -+}; -+ -+atomic_t aufs_debug = ATOMIC_INIT(0); -+MODULE_PARM_DESC(debug, "debug print"); -+module_param_named(debug, aufs_debug, atomic_t, S_IRUGO | S_IWUSR | S_IWGRP); -+ -+DEFINE_MUTEX(au_dbg_mtx); /* just to serialize the dbg msgs */ -+char *au_plevel = KERN_DEBUG; -+#define dpri(fmt, ...) do { \ -+ if ((au_plevel \ -+ && strcmp(au_plevel, KERN_DEBUG)) \ -+ || au_debug_test()) \ -+ printk("%s" fmt, au_plevel, ##__VA_ARGS__); \ -+} while (0) -+ -+/* ---------------------------------------------------------------------- */ -+ -+void au_dpri_whlist(struct au_nhash *whlist) -+{ -+ unsigned long ul, n; -+ struct hlist_head *head; -+ struct au_vdir_wh *pos; -+ -+ n = whlist->nh_num; -+ head = whlist->nh_head; -+ for (ul = 0; ul < n; ul++) { -+ hlist_for_each_entry(pos, head, wh_hash) -+ dpri("b%d, %.*s, %d\n", -+ pos->wh_bindex, -+ pos->wh_str.len, pos->wh_str.name, -+ pos->wh_str.len); -+ head++; -+ } -+} -+ -+void au_dpri_vdir(struct au_vdir *vdir) -+{ -+ unsigned long ul; -+ union au_vdir_deblk_p p; -+ unsigned char *o; -+ -+ if (!vdir || IS_ERR(vdir)) { -+ dpri("err %ld\n", PTR_ERR(vdir)); -+ return; -+ } -+ -+ dpri("deblk %u, nblk %lu, deblk %p, last{%lu, %p}, ver %lu\n", -+ vdir->vd_deblk_sz, vdir->vd_nblk, vdir->vd_deblk, -+ vdir->vd_last.ul, vdir->vd_last.p.deblk, vdir->vd_version); -+ for (ul = 0; ul < vdir->vd_nblk; ul++) { -+ p.deblk = vdir->vd_deblk[ul]; -+ o = p.deblk; -+ dpri("[%lu]: %p\n", ul, o); -+ } -+} -+ -+static int do_pri_inode(aufs_bindex_t bindex, struct inode *inode, int hn, -+ struct dentry *wh) -+{ -+ char *n = NULL; -+ int l = 0; -+ -+ if (!inode || IS_ERR(inode)) { -+ dpri("i%d: err %ld\n", bindex, PTR_ERR(inode)); -+ return -1; -+ } -+ -+ /* the type of i_blocks depends upon CONFIG_LBDAF */ -+ BUILD_BUG_ON(sizeof(inode->i_blocks) != sizeof(unsigned long) -+ && sizeof(inode->i_blocks) != sizeof(u64)); -+ if (wh) { -+ n = (void *)wh->d_name.name; -+ l = wh->d_name.len; -+ } -+ -+ dpri("i%d: %p, i%lu, %s, cnt %d, nl %u, 0%o, sz %llu, blk %llu," -+ " hn %d, ct %lld, np %lu, st 0x%lx, f 0x%x, v %llu, g %x%s%.*s\n", -+ bindex, inode, -+ inode->i_ino, inode->i_sb ? au_sbtype(inode->i_sb) : "??", -+ atomic_read(&inode->i_count), inode->i_nlink, inode->i_mode, -+ i_size_read(inode), (unsigned long long)inode->i_blocks, -+ hn, (long long)timespec_to_ns(&inode->i_ctime) & 0x0ffff, -+ inode->i_mapping ? inode->i_mapping->nrpages : 0, -+ inode->i_state, inode->i_flags, inode->i_version, -+ inode->i_generation, -+ l ? ", wh " : "", l, n); -+ return 0; -+} -+ -+void au_dpri_inode(struct inode *inode) -+{ -+ struct au_iinfo *iinfo; -+ aufs_bindex_t bindex; -+ int err, hn; -+ -+ err = do_pri_inode(-1, inode, -1, NULL); -+ if (err || !au_test_aufs(inode->i_sb)) -+ return; -+ -+ iinfo = au_ii(inode); -+ if (!iinfo) -+ return; -+ dpri("i-1: bstart %d, bend %d, gen %d\n", -+ iinfo->ii_bstart, iinfo->ii_bend, au_iigen(inode, NULL)); -+ if (iinfo->ii_bstart < 0) -+ return; -+ hn = 0; -+ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; bindex++) { -+ hn = !!au_hn(iinfo->ii_hinode + bindex); -+ do_pri_inode(bindex, iinfo->ii_hinode[0 + bindex].hi_inode, hn, -+ iinfo->ii_hinode[0 + bindex].hi_whdentry); -+ } -+} -+ -+void au_dpri_dalias(struct inode *inode) -+{ -+ struct dentry *d; -+ -+ spin_lock(&inode->i_lock); -+ hlist_for_each_entry(d, &inode->i_dentry, d_u.d_alias) -+ au_dpri_dentry(d); -+ spin_unlock(&inode->i_lock); -+} -+ -+static int do_pri_dentry(aufs_bindex_t bindex, struct dentry *dentry) -+{ -+ struct dentry *wh = NULL; -+ int hn; -+ struct au_iinfo *iinfo; -+ -+ if (!dentry || IS_ERR(dentry)) { -+ dpri("d%d: err %ld\n", bindex, PTR_ERR(dentry)); -+ return -1; -+ } -+ /* do not call dget_parent() here */ -+ /* note: access d_xxx without d_lock */ -+ dpri("d%d: %p, %pd2?, %s, cnt %d, flags 0x%x, %shashed\n", -+ bindex, dentry, dentry, -+ dentry->d_sb ? au_sbtype(dentry->d_sb) : "??", -+ au_dcount(dentry), dentry->d_flags, -+ d_unhashed(dentry) ? "un" : ""); -+ hn = -1; -+ if (bindex >= 0 && dentry->d_inode && au_test_aufs(dentry->d_sb)) { -+ iinfo = au_ii(dentry->d_inode); -+ if (iinfo) { -+ hn = !!au_hn(iinfo->ii_hinode + bindex); -+ wh = iinfo->ii_hinode[0 + bindex].hi_whdentry; -+ } -+ } -+ do_pri_inode(bindex, dentry->d_inode, hn, wh); -+ return 0; -+} -+ -+void au_dpri_dentry(struct dentry *dentry) -+{ -+ struct au_dinfo *dinfo; -+ aufs_bindex_t bindex; -+ int err; -+ struct au_hdentry *hdp; -+ -+ err = do_pri_dentry(-1, dentry); -+ if (err || !au_test_aufs(dentry->d_sb)) -+ return; -+ -+ dinfo = au_di(dentry); -+ if (!dinfo) -+ return; -+ dpri("d-1: bstart %d, bend %d, bwh %d, bdiropq %d, gen %d, tmp %d\n", -+ dinfo->di_bstart, dinfo->di_bend, -+ dinfo->di_bwh, dinfo->di_bdiropq, au_digen(dentry), -+ dinfo->di_tmpfile); -+ if (dinfo->di_bstart < 0) -+ return; -+ hdp = dinfo->di_hdentry; -+ for (bindex = dinfo->di_bstart; bindex <= dinfo->di_bend; bindex++) -+ do_pri_dentry(bindex, hdp[0 + bindex].hd_dentry); -+} -+ -+static int do_pri_file(aufs_bindex_t bindex, struct file *file) -+{ -+ char a[32]; -+ -+ if (!file || IS_ERR(file)) { -+ dpri("f%d: err %ld\n", bindex, PTR_ERR(file)); -+ return -1; -+ } -+ a[0] = 0; -+ if (bindex < 0 -+ && !IS_ERR_OR_NULL(file->f_dentry) -+ && au_test_aufs(file->f_dentry->d_sb) -+ && au_fi(file)) -+ snprintf(a, sizeof(a), ", gen %d, mmapped %d", -+ au_figen(file), atomic_read(&au_fi(file)->fi_mmapped)); -+ dpri("f%d: mode 0x%x, flags 0%o, cnt %ld, v %llu, pos %llu%s\n", -+ bindex, file->f_mode, file->f_flags, (long)file_count(file), -+ file->f_version, file->f_pos, a); -+ if (!IS_ERR_OR_NULL(file->f_dentry)) -+ do_pri_dentry(bindex, file->f_dentry); -+ return 0; -+} -+ -+void au_dpri_file(struct file *file) -+{ -+ struct au_finfo *finfo; -+ struct au_fidir *fidir; -+ struct au_hfile *hfile; -+ aufs_bindex_t bindex; -+ int err; -+ -+ err = do_pri_file(-1, file); -+ if (err -+ || IS_ERR_OR_NULL(file->f_dentry) -+ || !au_test_aufs(file->f_dentry->d_sb)) -+ return; -+ -+ finfo = au_fi(file); -+ if (!finfo) -+ return; -+ if (finfo->fi_btop < 0) -+ return; -+ fidir = finfo->fi_hdir; -+ if (!fidir) -+ do_pri_file(finfo->fi_btop, finfo->fi_htop.hf_file); -+ else -+ for (bindex = finfo->fi_btop; -+ bindex >= 0 && bindex <= fidir->fd_bbot; -+ bindex++) { -+ hfile = fidir->fd_hfile + bindex; -+ do_pri_file(bindex, hfile ? hfile->hf_file : NULL); -+ } -+} -+ -+static int do_pri_br(aufs_bindex_t bindex, struct au_branch *br) -+{ -+ struct vfsmount *mnt; -+ struct super_block *sb; -+ -+ if (!br || IS_ERR(br)) -+ goto out; -+ mnt = au_br_mnt(br); -+ if (!mnt || IS_ERR(mnt)) -+ goto out; -+ sb = mnt->mnt_sb; -+ if (!sb || IS_ERR(sb)) -+ goto out; -+ -+ dpri("s%d: {perm 0x%x, id %d, cnt %d, wbr %p}, " -+ "%s, dev 0x%02x%02x, flags 0x%lx, cnt %d, active %d, " -+ "xino %d\n", -+ bindex, br->br_perm, br->br_id, atomic_read(&br->br_count), -+ br->br_wbr, au_sbtype(sb), MAJOR(sb->s_dev), MINOR(sb->s_dev), -+ sb->s_flags, sb->s_count, -+ atomic_read(&sb->s_active), !!br->br_xino.xi_file); -+ return 0; -+ -+out: -+ dpri("s%d: err %ld\n", bindex, PTR_ERR(br)); -+ return -1; -+} -+ -+void au_dpri_sb(struct super_block *sb) -+{ -+ struct au_sbinfo *sbinfo; -+ aufs_bindex_t bindex; -+ int err; -+ /* to reuduce stack size */ -+ struct { -+ struct vfsmount mnt; -+ struct au_branch fake; -+ } *a; -+ -+ /* this function can be called from magic sysrq */ -+ a = kzalloc(sizeof(*a), GFP_ATOMIC); -+ if (unlikely(!a)) { -+ dpri("no memory\n"); -+ return; -+ } -+ -+ a->mnt.mnt_sb = sb; -+ a->fake.br_path.mnt = &a->mnt; -+ atomic_set(&a->fake.br_count, 0); -+ smp_mb(); /* atomic_set */ -+ err = do_pri_br(-1, &a->fake); -+ kfree(a); -+ dpri("dev 0x%x\n", sb->s_dev); -+ if (err || !au_test_aufs(sb)) -+ return; -+ -+ sbinfo = au_sbi(sb); -+ if (!sbinfo) -+ return; -+ dpri("nw %d, gen %u, kobj %d\n", -+ atomic_read(&sbinfo->si_nowait.nw_len), sbinfo->si_generation, -+ atomic_read(&sbinfo->si_kobj.kref.refcount)); -+ for (bindex = 0; bindex <= sbinfo->si_bend; bindex++) -+ do_pri_br(bindex, sbinfo->si_branch[0 + bindex]); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void __au_dbg_verify_dinode(struct dentry *dentry, const char *func, int line) -+{ -+ struct inode *h_inode, *inode = dentry->d_inode; -+ struct dentry *h_dentry; -+ aufs_bindex_t bindex, bend, bi; -+ -+ if (!inode /* || au_di(dentry)->di_lsc == AuLsc_DI_TMP */) -+ return; -+ -+ bend = au_dbend(dentry); -+ bi = au_ibend(inode); -+ if (bi < bend) -+ bend = bi; -+ bindex = au_dbstart(dentry); -+ bi = au_ibstart(inode); -+ if (bi > bindex) -+ bindex = bi; -+ -+ for (; bindex <= bend; bindex++) { -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (!h_dentry) -+ continue; -+ h_inode = au_h_iptr(inode, bindex); -+ if (unlikely(h_inode != h_dentry->d_inode)) { -+ au_debug_on(); -+ AuDbg("b%d, %s:%d\n", bindex, func, line); -+ AuDbgDentry(dentry); -+ AuDbgInode(inode); -+ au_debug_off(); -+ BUG(); -+ } -+ } -+} -+ -+void au_dbg_verify_gen(struct dentry *parent, unsigned int sigen) -+{ -+ int err, i, j; -+ struct au_dcsub_pages dpages; -+ struct au_dpage *dpage; -+ struct dentry **dentries; -+ -+ err = au_dpages_init(&dpages, GFP_NOFS); -+ AuDebugOn(err); -+ err = au_dcsub_pages_rev_aufs(&dpages, parent, /*do_include*/1); -+ AuDebugOn(err); -+ for (i = dpages.ndpage - 1; !err && i >= 0; i--) { -+ dpage = dpages.dpages + i; -+ dentries = dpage->dentries; -+ for (j = dpage->ndentry - 1; !err && j >= 0; j--) -+ AuDebugOn(au_digen_test(dentries[j], sigen)); -+ } -+ au_dpages_free(&dpages); -+} -+ -+void au_dbg_verify_kthread(void) -+{ -+ if (au_wkq_test()) { -+ au_dbg_blocked(); -+ /* -+ * It may be recursive, but udba=notify between two aufs mounts, -+ * where a single ro branch is shared, is not a problem. -+ */ -+ /* WARN_ON(1); */ -+ } -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int __init au_debug_init(void) -+{ -+ aufs_bindex_t bindex; -+ struct au_vdir_destr destr; -+ -+ bindex = -1; -+ AuDebugOn(bindex >= 0); -+ -+ destr.len = -1; -+ AuDebugOn(destr.len < NAME_MAX); -+ -+#ifdef CONFIG_4KSTACKS -+ pr_warn("CONFIG_4KSTACKS is defined.\n"); -+#endif -+ -+ return 0; -+} -diff --git a/fs/aufs/debug.h b/fs/aufs/debug.h -new file mode 100644 -index 0000000..039e6f8 ---- /dev/null -+++ b/fs/aufs/debug.h -@@ -0,0 +1,228 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * debug print functions -+ */ -+ -+#ifndef __AUFS_DEBUG_H__ -+#define __AUFS_DEBUG_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+#include -+#include -+#include -+ -+#ifdef CONFIG_AUFS_DEBUG -+#define AuDebugOn(a) BUG_ON(a) -+ -+/* module parameter */ -+extern atomic_t aufs_debug; -+static inline void au_debug_on(void) -+{ -+ atomic_inc(&aufs_debug); -+} -+static inline void au_debug_off(void) -+{ -+ atomic_dec_if_positive(&aufs_debug); -+} -+ -+static inline int au_debug_test(void) -+{ -+ return atomic_read(&aufs_debug) > 0; -+} -+#else -+#define AuDebugOn(a) do {} while (0) -+AuStubVoid(au_debug_on, void) -+AuStubVoid(au_debug_off, void) -+AuStubInt0(au_debug_test, void) -+#endif /* CONFIG_AUFS_DEBUG */ -+ -+#define param_check_atomic_t(name, p) __param_check(name, p, atomic_t) -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* debug print */ -+ -+#define AuDbg(fmt, ...) do { \ -+ if (au_debug_test()) \ -+ pr_debug("DEBUG: " fmt, ##__VA_ARGS__); \ -+} while (0) -+#define AuLabel(l) AuDbg(#l "\n") -+#define AuIOErr(fmt, ...) pr_err("I/O Error, " fmt, ##__VA_ARGS__) -+#define AuWarn1(fmt, ...) do { \ -+ static unsigned char _c; \ -+ if (!_c++) \ -+ pr_warn(fmt, ##__VA_ARGS__); \ -+} while (0) -+ -+#define AuErr1(fmt, ...) do { \ -+ static unsigned char _c; \ -+ if (!_c++) \ -+ pr_err(fmt, ##__VA_ARGS__); \ -+} while (0) -+ -+#define AuIOErr1(fmt, ...) do { \ -+ static unsigned char _c; \ -+ if (!_c++) \ -+ AuIOErr(fmt, ##__VA_ARGS__); \ -+} while (0) -+ -+#define AuUnsupportMsg "This operation is not supported." \ -+ " Please report this application to aufs-users ML." -+#define AuUnsupport(fmt, ...) do { \ -+ pr_err(AuUnsupportMsg "\n" fmt, ##__VA_ARGS__); \ -+ dump_stack(); \ -+} while (0) -+ -+#define AuTraceErr(e) do { \ -+ if (unlikely((e) < 0)) \ -+ AuDbg("err %d\n", (int)(e)); \ -+} while (0) -+ -+#define AuTraceErrPtr(p) do { \ -+ if (IS_ERR(p)) \ -+ AuDbg("err %ld\n", PTR_ERR(p)); \ -+} while (0) -+ -+/* dirty macros for debug print, use with "%.*s" and caution */ -+#define AuLNPair(qstr) (qstr)->len, (qstr)->name -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct dentry; -+#ifdef CONFIG_AUFS_DEBUG -+extern struct mutex au_dbg_mtx; -+extern char *au_plevel; -+struct au_nhash; -+void au_dpri_whlist(struct au_nhash *whlist); -+struct au_vdir; -+void au_dpri_vdir(struct au_vdir *vdir); -+struct inode; -+void au_dpri_inode(struct inode *inode); -+void au_dpri_dalias(struct inode *inode); -+void au_dpri_dentry(struct dentry *dentry); -+struct file; -+void au_dpri_file(struct file *filp); -+struct super_block; -+void au_dpri_sb(struct super_block *sb); -+ -+#define au_dbg_verify_dinode(d) __au_dbg_verify_dinode(d, __func__, __LINE__) -+void __au_dbg_verify_dinode(struct dentry *dentry, const char *func, int line); -+void au_dbg_verify_gen(struct dentry *parent, unsigned int sigen); -+void au_dbg_verify_kthread(void); -+ -+int __init au_debug_init(void); -+ -+#define AuDbgWhlist(w) do { \ -+ mutex_lock(&au_dbg_mtx); \ -+ AuDbg(#w "\n"); \ -+ au_dpri_whlist(w); \ -+ mutex_unlock(&au_dbg_mtx); \ -+} while (0) -+ -+#define AuDbgVdir(v) do { \ -+ mutex_lock(&au_dbg_mtx); \ -+ AuDbg(#v "\n"); \ -+ au_dpri_vdir(v); \ -+ mutex_unlock(&au_dbg_mtx); \ -+} while (0) -+ -+#define AuDbgInode(i) do { \ -+ mutex_lock(&au_dbg_mtx); \ -+ AuDbg(#i "\n"); \ -+ au_dpri_inode(i); \ -+ mutex_unlock(&au_dbg_mtx); \ -+} while (0) -+ -+#define AuDbgDAlias(i) do { \ -+ mutex_lock(&au_dbg_mtx); \ -+ AuDbg(#i "\n"); \ -+ au_dpri_dalias(i); \ -+ mutex_unlock(&au_dbg_mtx); \ -+} while (0) -+ -+#define AuDbgDentry(d) do { \ -+ mutex_lock(&au_dbg_mtx); \ -+ AuDbg(#d "\n"); \ -+ au_dpri_dentry(d); \ -+ mutex_unlock(&au_dbg_mtx); \ -+} while (0) -+ -+#define AuDbgFile(f) do { \ -+ mutex_lock(&au_dbg_mtx); \ -+ AuDbg(#f "\n"); \ -+ au_dpri_file(f); \ -+ mutex_unlock(&au_dbg_mtx); \ -+} while (0) -+ -+#define AuDbgSb(sb) do { \ -+ mutex_lock(&au_dbg_mtx); \ -+ AuDbg(#sb "\n"); \ -+ au_dpri_sb(sb); \ -+ mutex_unlock(&au_dbg_mtx); \ -+} while (0) -+ -+#define AuDbgSym(addr) do { \ -+ char sym[KSYM_SYMBOL_LEN]; \ -+ sprint_symbol(sym, (unsigned long)addr); \ -+ AuDbg("%s\n", sym); \ -+} while (0) -+#else -+AuStubVoid(au_dbg_verify_dinode, struct dentry *dentry) -+AuStubVoid(au_dbg_verify_dir_parent, struct dentry *dentry, unsigned int sigen) -+AuStubVoid(au_dbg_verify_nondir_parent, struct dentry *dentry, -+ unsigned int sigen) -+AuStubVoid(au_dbg_verify_gen, struct dentry *parent, unsigned int sigen) -+AuStubVoid(au_dbg_verify_kthread, void) -+AuStubInt0(__init au_debug_init, void) -+ -+#define AuDbgWhlist(w) do {} while (0) -+#define AuDbgVdir(v) do {} while (0) -+#define AuDbgInode(i) do {} while (0) -+#define AuDbgDAlias(i) do {} while (0) -+#define AuDbgDentry(d) do {} while (0) -+#define AuDbgFile(f) do {} while (0) -+#define AuDbgSb(sb) do {} while (0) -+#define AuDbgSym(addr) do {} while (0) -+#endif /* CONFIG_AUFS_DEBUG */ -+ -+/* ---------------------------------------------------------------------- */ -+ -+#ifdef CONFIG_AUFS_MAGIC_SYSRQ -+int __init au_sysrq_init(void); -+void au_sysrq_fin(void); -+ -+#ifdef CONFIG_HW_CONSOLE -+#define au_dbg_blocked() do { \ -+ WARN_ON(1); \ -+ handle_sysrq('w'); \ -+} while (0) -+#else -+AuStubVoid(au_dbg_blocked, void) -+#endif -+ -+#else -+AuStubInt0(__init au_sysrq_init, void) -+AuStubVoid(au_sysrq_fin, void) -+AuStubVoid(au_dbg_blocked, void) -+#endif /* CONFIG_AUFS_MAGIC_SYSRQ */ -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_DEBUG_H__ */ -diff --git a/fs/aufs/dentry.c b/fs/aufs/dentry.c -new file mode 100644 -index 0000000..ed56947 ---- /dev/null -+++ b/fs/aufs/dentry.c -@@ -0,0 +1,1129 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * lookup and dentry operations -+ */ -+ -+#include -+#include "aufs.h" -+ -+#define AuLkup_ALLOW_NEG 1 -+#define AuLkup_IGNORE_PERM (1 << 1) -+#define au_ftest_lkup(flags, name) ((flags) & AuLkup_##name) -+#define au_fset_lkup(flags, name) \ -+ do { (flags) |= AuLkup_##name; } while (0) -+#define au_fclr_lkup(flags, name) \ -+ do { (flags) &= ~AuLkup_##name; } while (0) -+ -+struct au_do_lookup_args { -+ unsigned int flags; -+ mode_t type; -+}; -+ -+/* -+ * returns positive/negative dentry, NULL or an error. -+ * NULL means whiteout-ed or not-found. -+ */ -+static struct dentry* -+au_do_lookup(struct dentry *h_parent, struct dentry *dentry, -+ aufs_bindex_t bindex, struct qstr *wh_name, -+ struct au_do_lookup_args *args) -+{ -+ struct dentry *h_dentry; -+ struct inode *h_inode; -+ struct au_branch *br; -+ int wh_found, opq; -+ unsigned char wh_able; -+ const unsigned char allow_neg = !!au_ftest_lkup(args->flags, ALLOW_NEG); -+ const unsigned char ignore_perm = !!au_ftest_lkup(args->flags, -+ IGNORE_PERM); -+ -+ wh_found = 0; -+ br = au_sbr(dentry->d_sb, bindex); -+ wh_able = !!au_br_whable(br->br_perm); -+ if (wh_able) -+ wh_found = au_wh_test(h_parent, wh_name, /*try_sio*/0); -+ h_dentry = ERR_PTR(wh_found); -+ if (!wh_found) -+ goto real_lookup; -+ if (unlikely(wh_found < 0)) -+ goto out; -+ -+ /* We found a whiteout */ -+ /* au_set_dbend(dentry, bindex); */ -+ au_set_dbwh(dentry, bindex); -+ if (!allow_neg) -+ return NULL; /* success */ -+ -+real_lookup: -+ if (!ignore_perm) -+ h_dentry = vfsub_lkup_one(&dentry->d_name, h_parent); -+ else -+ h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent); -+ if (IS_ERR(h_dentry)) { -+ if (PTR_ERR(h_dentry) == -ENAMETOOLONG -+ && !allow_neg) -+ h_dentry = NULL; -+ goto out; -+ } -+ -+ h_inode = h_dentry->d_inode; -+ if (!h_inode) { -+ if (!allow_neg) -+ goto out_neg; -+ } else if (wh_found -+ || (args->type && args->type != (h_inode->i_mode & S_IFMT))) -+ goto out_neg; -+ -+ if (au_dbend(dentry) <= bindex) -+ au_set_dbend(dentry, bindex); -+ if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry)) -+ au_set_dbstart(dentry, bindex); -+ au_set_h_dptr(dentry, bindex, h_dentry); -+ -+ if (!d_is_dir(h_dentry) -+ || !wh_able -+ || (d_is_positive(dentry) && !d_is_dir(dentry))) -+ goto out; /* success */ -+ -+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); -+ opq = au_diropq_test(h_dentry); -+ mutex_unlock(&h_inode->i_mutex); -+ if (opq > 0) -+ au_set_dbdiropq(dentry, bindex); -+ else if (unlikely(opq < 0)) { -+ au_set_h_dptr(dentry, bindex, NULL); -+ h_dentry = ERR_PTR(opq); -+ } -+ goto out; -+ -+out_neg: -+ dput(h_dentry); -+ h_dentry = NULL; -+out: -+ return h_dentry; -+} -+ -+static int au_test_shwh(struct super_block *sb, const struct qstr *name) -+{ -+ if (unlikely(!au_opt_test(au_mntflags(sb), SHWH) -+ && !strncmp(name->name, AUFS_WH_PFX, AUFS_WH_PFX_LEN))) -+ return -EPERM; -+ return 0; -+} -+ -+/* -+ * returns the number of lower positive dentries, -+ * otherwise an error. -+ * can be called at unlinking with @type is zero. -+ */ -+int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type) -+{ -+ int npositive, err; -+ aufs_bindex_t bindex, btail, bdiropq; -+ unsigned char isdir, dirperm1; -+ struct qstr whname; -+ struct au_do_lookup_args args = { -+ .flags = 0, -+ .type = type -+ }; -+ const struct qstr *name = &dentry->d_name; -+ struct dentry *parent; -+ struct inode *inode; -+ struct super_block *sb; -+ -+ sb = dentry->d_sb; -+ err = au_test_shwh(sb, name); -+ if (unlikely(err)) -+ goto out; -+ -+ err = au_wh_name_alloc(&whname, name); -+ if (unlikely(err)) -+ goto out; -+ -+ inode = dentry->d_inode; -+ isdir = !!d_is_dir(dentry); -+ if (!type) -+ au_fset_lkup(args.flags, ALLOW_NEG); -+ dirperm1 = !!au_opt_test(au_mntflags(sb), DIRPERM1); -+ -+ npositive = 0; -+ parent = dget_parent(dentry); -+ btail = au_dbtaildir(parent); -+ for (bindex = bstart; bindex <= btail; bindex++) { -+ struct dentry *h_parent, *h_dentry; -+ struct inode *h_inode, *h_dir; -+ -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (h_dentry) { -+ if (h_dentry->d_inode) -+ npositive++; -+ if (type != S_IFDIR) -+ break; -+ continue; -+ } -+ h_parent = au_h_dptr(parent, bindex); -+ if (!h_parent || !d_is_dir(h_parent)) -+ continue; -+ -+ h_dir = h_parent->d_inode; -+ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); -+ h_dentry = au_do_lookup(h_parent, dentry, bindex, &whname, -+ &args); -+ mutex_unlock(&h_dir->i_mutex); -+ err = PTR_ERR(h_dentry); -+ if (IS_ERR(h_dentry)) -+ goto out_parent; -+ if (h_dentry) -+ au_fclr_lkup(args.flags, ALLOW_NEG); -+ if (dirperm1) -+ au_fset_lkup(args.flags, IGNORE_PERM); -+ -+ if (au_dbwh(dentry) == bindex) -+ break; -+ if (!h_dentry) -+ continue; -+ h_inode = h_dentry->d_inode; -+ if (!h_inode) -+ continue; -+ npositive++; -+ if (!args.type) -+ args.type = h_inode->i_mode & S_IFMT; -+ if (args.type != S_IFDIR) -+ break; -+ else if (isdir) { -+ /* the type of lower may be different */ -+ bdiropq = au_dbdiropq(dentry); -+ if (bdiropq >= 0 && bdiropq <= bindex) -+ break; -+ } -+ } -+ -+ if (npositive) { -+ AuLabel(positive); -+ au_update_dbstart(dentry); -+ } -+ err = npositive; -+ if (unlikely(!au_opt_test(au_mntflags(sb), UDBA_NONE) -+ && au_dbstart(dentry) < 0)) { -+ err = -EIO; -+ AuIOErr("both of real entry and whiteout found, %pd, err %d\n", -+ dentry, err); -+ } -+ -+out_parent: -+ dput(parent); -+ kfree(whname.name); -+out: -+ return err; -+} -+ -+struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent) -+{ -+ struct dentry *dentry; -+ int wkq_err; -+ -+ if (!au_test_h_perm_sio(parent->d_inode, MAY_EXEC)) -+ dentry = vfsub_lkup_one(name, parent); -+ else { -+ struct vfsub_lkup_one_args args = { -+ .errp = &dentry, -+ .name = name, -+ .parent = parent -+ }; -+ -+ wkq_err = au_wkq_wait(vfsub_call_lkup_one, &args); -+ if (unlikely(wkq_err)) -+ dentry = ERR_PTR(wkq_err); -+ } -+ -+ return dentry; -+} -+ -+/* -+ * lookup @dentry on @bindex which should be negative. -+ */ -+int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex, int wh) -+{ -+ int err; -+ struct dentry *parent, *h_parent, *h_dentry; -+ struct au_branch *br; -+ -+ parent = dget_parent(dentry); -+ h_parent = au_h_dptr(parent, bindex); -+ br = au_sbr(dentry->d_sb, bindex); -+ if (wh) -+ h_dentry = au_whtmp_lkup(h_parent, br, &dentry->d_name); -+ else -+ h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent); -+ err = PTR_ERR(h_dentry); -+ if (IS_ERR(h_dentry)) -+ goto out; -+ if (unlikely(h_dentry->d_inode)) { -+ err = -EIO; -+ AuIOErr("%pd should be negative on b%d.\n", h_dentry, bindex); -+ dput(h_dentry); -+ goto out; -+ } -+ -+ err = 0; -+ if (bindex < au_dbstart(dentry)) -+ au_set_dbstart(dentry, bindex); -+ if (au_dbend(dentry) < bindex) -+ au_set_dbend(dentry, bindex); -+ au_set_h_dptr(dentry, bindex, h_dentry); -+ -+out: -+ dput(parent); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* subset of struct inode */ -+struct au_iattr { -+ unsigned long i_ino; -+ /* unsigned int i_nlink; */ -+ kuid_t i_uid; -+ kgid_t i_gid; -+ u64 i_version; -+/* -+ loff_t i_size; -+ blkcnt_t i_blocks; -+*/ -+ umode_t i_mode; -+}; -+ -+static void au_iattr_save(struct au_iattr *ia, struct inode *h_inode) -+{ -+ ia->i_ino = h_inode->i_ino; -+ /* ia->i_nlink = h_inode->i_nlink; */ -+ ia->i_uid = h_inode->i_uid; -+ ia->i_gid = h_inode->i_gid; -+ ia->i_version = h_inode->i_version; -+/* -+ ia->i_size = h_inode->i_size; -+ ia->i_blocks = h_inode->i_blocks; -+*/ -+ ia->i_mode = (h_inode->i_mode & S_IFMT); -+} -+ -+static int au_iattr_test(struct au_iattr *ia, struct inode *h_inode) -+{ -+ return ia->i_ino != h_inode->i_ino -+ /* || ia->i_nlink != h_inode->i_nlink */ -+ || !uid_eq(ia->i_uid, h_inode->i_uid) -+ || !gid_eq(ia->i_gid, h_inode->i_gid) -+ || ia->i_version != h_inode->i_version -+/* -+ || ia->i_size != h_inode->i_size -+ || ia->i_blocks != h_inode->i_blocks -+*/ -+ || ia->i_mode != (h_inode->i_mode & S_IFMT); -+} -+ -+static int au_h_verify_dentry(struct dentry *h_dentry, struct dentry *h_parent, -+ struct au_branch *br) -+{ -+ int err; -+ struct au_iattr ia; -+ struct inode *h_inode; -+ struct dentry *h_d; -+ struct super_block *h_sb; -+ -+ err = 0; -+ memset(&ia, -1, sizeof(ia)); -+ h_sb = h_dentry->d_sb; -+ h_inode = h_dentry->d_inode; -+ if (h_inode) -+ au_iattr_save(&ia, h_inode); -+ else if (au_test_nfs(h_sb) || au_test_fuse(h_sb)) -+ /* nfs d_revalidate may return 0 for negative dentry */ -+ /* fuse d_revalidate always return 0 for negative dentry */ -+ goto out; -+ -+ /* main purpose is namei.c:cached_lookup() and d_revalidate */ -+ h_d = vfsub_lkup_one(&h_dentry->d_name, h_parent); -+ err = PTR_ERR(h_d); -+ if (IS_ERR(h_d)) -+ goto out; -+ -+ err = 0; -+ if (unlikely(h_d != h_dentry -+ || h_d->d_inode != h_inode -+ || (h_inode && au_iattr_test(&ia, h_inode)))) -+ err = au_busy_or_stale(); -+ dput(h_d); -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir, -+ struct dentry *h_parent, struct au_branch *br) -+{ -+ int err; -+ -+ err = 0; -+ if (udba == AuOpt_UDBA_REVAL -+ && !au_test_fs_remote(h_dentry->d_sb)) { -+ IMustLock(h_dir); -+ err = (h_dentry->d_parent->d_inode != h_dir); -+ } else if (udba != AuOpt_UDBA_NONE) -+ err = au_h_verify_dentry(h_dentry, h_parent, br); -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_do_refresh_hdentry(struct dentry *dentry, struct dentry *parent) -+{ -+ int err; -+ aufs_bindex_t new_bindex, bindex, bend, bwh, bdiropq; -+ struct au_hdentry tmp, *p, *q; -+ struct au_dinfo *dinfo; -+ struct super_block *sb; -+ -+ DiMustWriteLock(dentry); -+ -+ sb = dentry->d_sb; -+ dinfo = au_di(dentry); -+ bend = dinfo->di_bend; -+ bwh = dinfo->di_bwh; -+ bdiropq = dinfo->di_bdiropq; -+ p = dinfo->di_hdentry + dinfo->di_bstart; -+ for (bindex = dinfo->di_bstart; bindex <= bend; bindex++, p++) { -+ if (!p->hd_dentry) -+ continue; -+ -+ new_bindex = au_br_index(sb, p->hd_id); -+ if (new_bindex == bindex) -+ continue; -+ -+ if (dinfo->di_bwh == bindex) -+ bwh = new_bindex; -+ if (dinfo->di_bdiropq == bindex) -+ bdiropq = new_bindex; -+ if (new_bindex < 0) { -+ au_hdput(p); -+ p->hd_dentry = NULL; -+ continue; -+ } -+ -+ /* swap two lower dentries, and loop again */ -+ q = dinfo->di_hdentry + new_bindex; -+ tmp = *q; -+ *q = *p; -+ *p = tmp; -+ if (tmp.hd_dentry) { -+ bindex--; -+ p--; -+ } -+ } -+ -+ dinfo->di_bwh = -1; -+ if (bwh >= 0 && bwh <= au_sbend(sb) && au_sbr_whable(sb, bwh)) -+ dinfo->di_bwh = bwh; -+ -+ dinfo->di_bdiropq = -1; -+ if (bdiropq >= 0 -+ && bdiropq <= au_sbend(sb) -+ && au_sbr_whable(sb, bdiropq)) -+ dinfo->di_bdiropq = bdiropq; -+ -+ err = -EIO; -+ dinfo->di_bstart = -1; -+ dinfo->di_bend = -1; -+ bend = au_dbend(parent); -+ p = dinfo->di_hdentry; -+ for (bindex = 0; bindex <= bend; bindex++, p++) -+ if (p->hd_dentry) { -+ dinfo->di_bstart = bindex; -+ break; -+ } -+ -+ if (dinfo->di_bstart >= 0) { -+ p = dinfo->di_hdentry + bend; -+ for (bindex = bend; bindex >= 0; bindex--, p--) -+ if (p->hd_dentry) { -+ dinfo->di_bend = bindex; -+ err = 0; -+ break; -+ } -+ } -+ -+ return err; -+} -+ -+static void au_do_hide(struct dentry *dentry) -+{ -+ struct inode *inode; -+ -+ inode = dentry->d_inode; -+ if (inode) { -+ if (!S_ISDIR(inode->i_mode)) { -+ if (inode->i_nlink && !d_unhashed(dentry)) -+ drop_nlink(inode); -+ } else { -+ clear_nlink(inode); -+ /* stop next lookup */ -+ inode->i_flags |= S_DEAD; -+ } -+ smp_mb(); /* necessary? */ -+ } -+ d_drop(dentry); -+} -+ -+static int au_hide_children(struct dentry *parent) -+{ -+ int err, i, j, ndentry; -+ struct au_dcsub_pages dpages; -+ struct au_dpage *dpage; -+ struct dentry *dentry; -+ -+ err = au_dpages_init(&dpages, GFP_NOFS); -+ if (unlikely(err)) -+ goto out; -+ err = au_dcsub_pages(&dpages, parent, NULL, NULL); -+ if (unlikely(err)) -+ goto out_dpages; -+ -+ /* in reverse order */ -+ for (i = dpages.ndpage - 1; i >= 0; i--) { -+ dpage = dpages.dpages + i; -+ ndentry = dpage->ndentry; -+ for (j = ndentry - 1; j >= 0; j--) { -+ dentry = dpage->dentries[j]; -+ if (dentry != parent) -+ au_do_hide(dentry); -+ } -+ } -+ -+out_dpages: -+ au_dpages_free(&dpages); -+out: -+ return err; -+} -+ -+static void au_hide(struct dentry *dentry) -+{ -+ int err; -+ -+ AuDbgDentry(dentry); -+ if (d_is_dir(dentry)) { -+ /* shrink_dcache_parent(dentry); */ -+ err = au_hide_children(dentry); -+ if (unlikely(err)) -+ AuIOErr("%pd, failed hiding children, ignored %d\n", -+ dentry, err); -+ } -+ au_do_hide(dentry); -+} -+ -+/* -+ * By adding a dirty branch, a cached dentry may be affected in various ways. -+ * -+ * a dirty branch is added -+ * - on the top of layers -+ * - in the middle of layers -+ * - to the bottom of layers -+ * -+ * on the added branch there exists -+ * - a whiteout -+ * - a diropq -+ * - a same named entry -+ * + exist -+ * * negative --> positive -+ * * positive --> positive -+ * - type is unchanged -+ * - type is changed -+ * + doesn't exist -+ * * negative --> negative -+ * * positive --> negative (rejected by au_br_del() for non-dir case) -+ * - none -+ */ -+static int au_refresh_by_dinfo(struct dentry *dentry, struct au_dinfo *dinfo, -+ struct au_dinfo *tmp) -+{ -+ int err; -+ aufs_bindex_t bindex, bend; -+ struct { -+ struct dentry *dentry; -+ struct inode *inode; -+ mode_t mode; -+ } orig_h, tmp_h = { -+ .dentry = NULL -+ }; -+ struct au_hdentry *hd; -+ struct inode *inode, *h_inode; -+ struct dentry *h_dentry; -+ -+ err = 0; -+ AuDebugOn(dinfo->di_bstart < 0); -+ orig_h.dentry = dinfo->di_hdentry[dinfo->di_bstart].hd_dentry; -+ orig_h.inode = orig_h.dentry->d_inode; -+ orig_h.mode = 0; -+ if (orig_h.inode) -+ orig_h.mode = orig_h.inode->i_mode & S_IFMT; -+ if (tmp->di_bstart >= 0) { -+ tmp_h.dentry = tmp->di_hdentry[tmp->di_bstart].hd_dentry; -+ tmp_h.inode = tmp_h.dentry->d_inode; -+ if (tmp_h.inode) -+ tmp_h.mode = tmp_h.inode->i_mode & S_IFMT; -+ } -+ -+ inode = dentry->d_inode; -+ if (!orig_h.inode) { -+ AuDbg("nagative originally\n"); -+ if (inode) { -+ au_hide(dentry); -+ goto out; -+ } -+ AuDebugOn(inode); -+ AuDebugOn(dinfo->di_bstart != dinfo->di_bend); -+ AuDebugOn(dinfo->di_bdiropq != -1); -+ -+ if (!tmp_h.inode) { -+ AuDbg("negative --> negative\n"); -+ /* should have only one negative lower */ -+ if (tmp->di_bstart >= 0 -+ && tmp->di_bstart < dinfo->di_bstart) { -+ AuDebugOn(tmp->di_bstart != tmp->di_bend); -+ AuDebugOn(dinfo->di_bstart != dinfo->di_bend); -+ au_set_h_dptr(dentry, dinfo->di_bstart, NULL); -+ au_di_cp(dinfo, tmp); -+ hd = tmp->di_hdentry + tmp->di_bstart; -+ au_set_h_dptr(dentry, tmp->di_bstart, -+ dget(hd->hd_dentry)); -+ } -+ au_dbg_verify_dinode(dentry); -+ } else { -+ AuDbg("negative --> positive\n"); -+ /* -+ * similar to the behaviour of creating with bypassing -+ * aufs. -+ * unhash it in order to force an error in the -+ * succeeding create operation. -+ * we should not set S_DEAD here. -+ */ -+ d_drop(dentry); -+ /* au_di_swap(tmp, dinfo); */ -+ au_dbg_verify_dinode(dentry); -+ } -+ } else { -+ AuDbg("positive originally\n"); -+ /* inode may be NULL */ -+ AuDebugOn(inode && (inode->i_mode & S_IFMT) != orig_h.mode); -+ if (!tmp_h.inode) { -+ AuDbg("positive --> negative\n"); -+ /* or bypassing aufs */ -+ au_hide(dentry); -+ if (tmp->di_bwh >= 0 && tmp->di_bwh <= dinfo->di_bstart) -+ dinfo->di_bwh = tmp->di_bwh; -+ if (inode) -+ err = au_refresh_hinode_self(inode); -+ au_dbg_verify_dinode(dentry); -+ } else if (orig_h.mode == tmp_h.mode) { -+ AuDbg("positive --> positive, same type\n"); -+ if (!S_ISDIR(orig_h.mode) -+ && dinfo->di_bstart > tmp->di_bstart) { -+ /* -+ * similar to the behaviour of removing and -+ * creating. -+ */ -+ au_hide(dentry); -+ if (inode) -+ err = au_refresh_hinode_self(inode); -+ au_dbg_verify_dinode(dentry); -+ } else { -+ /* fill empty slots */ -+ if (dinfo->di_bstart > tmp->di_bstart) -+ dinfo->di_bstart = tmp->di_bstart; -+ if (dinfo->di_bend < tmp->di_bend) -+ dinfo->di_bend = tmp->di_bend; -+ dinfo->di_bwh = tmp->di_bwh; -+ dinfo->di_bdiropq = tmp->di_bdiropq; -+ hd = tmp->di_hdentry; -+ bend = dinfo->di_bend; -+ for (bindex = tmp->di_bstart; bindex <= bend; -+ bindex++) { -+ if (au_h_dptr(dentry, bindex)) -+ continue; -+ h_dentry = hd[bindex].hd_dentry; -+ if (!h_dentry) -+ continue; -+ h_inode = h_dentry->d_inode; -+ AuDebugOn(!h_inode); -+ AuDebugOn(orig_h.mode -+ != (h_inode->i_mode -+ & S_IFMT)); -+ au_set_h_dptr(dentry, bindex, -+ dget(h_dentry)); -+ } -+ err = au_refresh_hinode(inode, dentry); -+ au_dbg_verify_dinode(dentry); -+ } -+ } else { -+ AuDbg("positive --> positive, different type\n"); -+ /* similar to the behaviour of removing and creating */ -+ au_hide(dentry); -+ if (inode) -+ err = au_refresh_hinode_self(inode); -+ au_dbg_verify_dinode(dentry); -+ } -+ } -+ -+out: -+ return err; -+} -+ -+void au_refresh_dop(struct dentry *dentry, int force_reval) -+{ -+ const struct dentry_operations *dop -+ = force_reval ? &aufs_dop : dentry->d_sb->s_d_op; -+ static const unsigned int mask -+ = DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE; -+ -+ BUILD_BUG_ON(sizeof(mask) != sizeof(dentry->d_flags)); -+ -+ if (dentry->d_op == dop) -+ return; -+ -+ AuDbg("%pd\n", dentry); -+ spin_lock(&dentry->d_lock); -+ if (dop == &aufs_dop) -+ dentry->d_flags |= mask; -+ else -+ dentry->d_flags &= ~mask; -+ dentry->d_op = dop; -+ spin_unlock(&dentry->d_lock); -+} -+ -+int au_refresh_dentry(struct dentry *dentry, struct dentry *parent) -+{ -+ int err, ebrange; -+ unsigned int sigen; -+ struct au_dinfo *dinfo, *tmp; -+ struct super_block *sb; -+ struct inode *inode; -+ -+ DiMustWriteLock(dentry); -+ AuDebugOn(IS_ROOT(dentry)); -+ AuDebugOn(!parent->d_inode); -+ -+ sb = dentry->d_sb; -+ inode = dentry->d_inode; -+ sigen = au_sigen(sb); -+ err = au_digen_test(parent, sigen); -+ if (unlikely(err)) -+ goto out; -+ -+ dinfo = au_di(dentry); -+ err = au_di_realloc(dinfo, au_sbend(sb) + 1); -+ if (unlikely(err)) -+ goto out; -+ ebrange = au_dbrange_test(dentry); -+ if (!ebrange) -+ ebrange = au_do_refresh_hdentry(dentry, parent); -+ -+ if (d_unhashed(dentry) || ebrange /* || dinfo->di_tmpfile */) { -+ AuDebugOn(au_dbstart(dentry) < 0 && au_dbend(dentry) >= 0); -+ if (inode) -+ err = au_refresh_hinode_self(inode); -+ au_dbg_verify_dinode(dentry); -+ if (!err) -+ goto out_dgen; /* success */ -+ goto out; -+ } -+ -+ /* temporary dinfo */ -+ AuDbgDentry(dentry); -+ err = -ENOMEM; -+ tmp = au_di_alloc(sb, AuLsc_DI_TMP); -+ if (unlikely(!tmp)) -+ goto out; -+ au_di_swap(tmp, dinfo); -+ /* returns the number of positive dentries */ -+ /* -+ * if current working dir is removed, it returns an error. -+ * but the dentry is legal. -+ */ -+ err = au_lkup_dentry(dentry, /*bstart*/0, /*type*/0); -+ AuDbgDentry(dentry); -+ au_di_swap(tmp, dinfo); -+ if (err == -ENOENT) -+ err = 0; -+ if (err >= 0) { -+ /* compare/refresh by dinfo */ -+ AuDbgDentry(dentry); -+ err = au_refresh_by_dinfo(dentry, dinfo, tmp); -+ au_dbg_verify_dinode(dentry); -+ AuTraceErr(err); -+ } -+ au_rw_write_unlock(&tmp->di_rwsem); -+ au_di_free(tmp); -+ if (unlikely(err)) -+ goto out; -+ -+out_dgen: -+ au_update_digen(dentry); -+out: -+ if (unlikely(err && !(dentry->d_flags & DCACHE_NFSFS_RENAMED))) { -+ AuIOErr("failed refreshing %pd, %d\n", dentry, err); -+ AuDbgDentry(dentry); -+ } -+ AuTraceErr(err); -+ return err; -+} -+ -+static int au_do_h_d_reval(struct dentry *h_dentry, unsigned int flags, -+ struct dentry *dentry, aufs_bindex_t bindex) -+{ -+ int err, valid; -+ -+ err = 0; -+ if (!(h_dentry->d_flags & DCACHE_OP_REVALIDATE)) -+ goto out; -+ -+ AuDbg("b%d\n", bindex); -+ /* -+ * gave up supporting LOOKUP_CREATE/OPEN for lower fs, -+ * due to whiteout and branch permission. -+ */ -+ flags &= ~(/*LOOKUP_PARENT |*/ LOOKUP_OPEN | LOOKUP_CREATE -+ | LOOKUP_FOLLOW | LOOKUP_EXCL); -+ /* it may return tri-state */ -+ valid = h_dentry->d_op->d_revalidate(h_dentry, flags); -+ -+ if (unlikely(valid < 0)) -+ err = valid; -+ else if (!valid) -+ err = -EINVAL; -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* todo: remove this */ -+static int h_d_revalidate(struct dentry *dentry, struct inode *inode, -+ unsigned int flags, int do_udba) -+{ -+ int err; -+ umode_t mode, h_mode; -+ aufs_bindex_t bindex, btail, bstart, ibs, ibe; -+ unsigned char plus, unhashed, is_root, h_plus, h_nfs, tmpfile; -+ struct inode *h_inode, *h_cached_inode; -+ struct dentry *h_dentry; -+ struct qstr *name, *h_name; -+ -+ err = 0; -+ plus = 0; -+ mode = 0; -+ ibs = -1; -+ ibe = -1; -+ unhashed = !!d_unhashed(dentry); -+ is_root = !!IS_ROOT(dentry); -+ name = &dentry->d_name; -+ tmpfile = au_di(dentry)->di_tmpfile; -+ -+ /* -+ * Theoretically, REVAL test should be unnecessary in case of -+ * {FS,I}NOTIFY. -+ * But {fs,i}notify doesn't fire some necessary events, -+ * IN_ATTRIB for atime/nlink/pageio -+ * Let's do REVAL test too. -+ */ -+ if (do_udba && inode) { -+ mode = (inode->i_mode & S_IFMT); -+ plus = (inode->i_nlink > 0); -+ ibs = au_ibstart(inode); -+ ibe = au_ibend(inode); -+ } -+ -+ bstart = au_dbstart(dentry); -+ btail = bstart; -+ if (inode && S_ISDIR(inode->i_mode)) -+ btail = au_dbtaildir(dentry); -+ for (bindex = bstart; bindex <= btail; bindex++) { -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (!h_dentry) -+ continue; -+ -+ AuDbg("b%d, %pd\n", bindex, h_dentry); -+ h_nfs = !!au_test_nfs(h_dentry->d_sb); -+ spin_lock(&h_dentry->d_lock); -+ h_name = &h_dentry->d_name; -+ if (unlikely(do_udba -+ && !is_root -+ && ((!h_nfs -+ && (unhashed != !!d_unhashed(h_dentry) -+ || (!tmpfile -+ && !au_qstreq(name, h_name)) -+ )) -+ || (h_nfs -+ && !(flags & LOOKUP_OPEN) -+ && (h_dentry->d_flags -+ & DCACHE_NFSFS_RENAMED))) -+ )) { -+ int h_unhashed; -+ -+ h_unhashed = d_unhashed(h_dentry); -+ spin_unlock(&h_dentry->d_lock); -+ AuDbg("unhash 0x%x 0x%x, %pd %pd\n", -+ unhashed, h_unhashed, dentry, h_dentry); -+ goto err; -+ } -+ spin_unlock(&h_dentry->d_lock); -+ -+ err = au_do_h_d_reval(h_dentry, flags, dentry, bindex); -+ if (unlikely(err)) -+ /* do not goto err, to keep the errno */ -+ break; -+ -+ /* todo: plink too? */ -+ if (!do_udba) -+ continue; -+ -+ /* UDBA tests */ -+ h_inode = h_dentry->d_inode; -+ if (unlikely(!!inode != !!h_inode)) -+ goto err; -+ -+ h_plus = plus; -+ h_mode = mode; -+ h_cached_inode = h_inode; -+ if (h_inode) { -+ h_mode = (h_inode->i_mode & S_IFMT); -+ h_plus = (h_inode->i_nlink > 0); -+ } -+ if (inode && ibs <= bindex && bindex <= ibe) -+ h_cached_inode = au_h_iptr(inode, bindex); -+ -+ if (!h_nfs) { -+ if (unlikely(plus != h_plus && !tmpfile)) -+ goto err; -+ } else { -+ if (unlikely(!(h_dentry->d_flags & DCACHE_NFSFS_RENAMED) -+ && !is_root -+ && !IS_ROOT(h_dentry) -+ && unhashed != d_unhashed(h_dentry))) -+ goto err; -+ } -+ if (unlikely(mode != h_mode -+ || h_cached_inode != h_inode)) -+ goto err; -+ continue; -+ -+err: -+ err = -EINVAL; -+ break; -+ } -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+/* todo: consolidate with do_refresh() and au_reval_for_attr() */ -+static int simple_reval_dpath(struct dentry *dentry, unsigned int sigen) -+{ -+ int err; -+ struct dentry *parent; -+ -+ if (!au_digen_test(dentry, sigen)) -+ return 0; -+ -+ parent = dget_parent(dentry); -+ di_read_lock_parent(parent, AuLock_IR); -+ AuDebugOn(au_digen_test(parent, sigen)); -+ au_dbg_verify_gen(parent, sigen); -+ err = au_refresh_dentry(dentry, parent); -+ di_read_unlock(parent, AuLock_IR); -+ dput(parent); -+ AuTraceErr(err); -+ return err; -+} -+ -+int au_reval_dpath(struct dentry *dentry, unsigned int sigen) -+{ -+ int err; -+ struct dentry *d, *parent; -+ struct inode *inode; -+ -+ if (!au_ftest_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIR)) -+ return simple_reval_dpath(dentry, sigen); -+ -+ /* slow loop, keep it simple and stupid */ -+ /* cf: au_cpup_dirs() */ -+ err = 0; -+ parent = NULL; -+ while (au_digen_test(dentry, sigen)) { -+ d = dentry; -+ while (1) { -+ dput(parent); -+ parent = dget_parent(d); -+ if (!au_digen_test(parent, sigen)) -+ break; -+ d = parent; -+ } -+ -+ inode = d->d_inode; -+ if (d != dentry) -+ di_write_lock_child2(d); -+ -+ /* someone might update our dentry while we were sleeping */ -+ if (au_digen_test(d, sigen)) { -+ /* -+ * todo: consolidate with simple_reval_dpath(), -+ * do_refresh() and au_reval_for_attr(). -+ */ -+ di_read_lock_parent(parent, AuLock_IR); -+ err = au_refresh_dentry(d, parent); -+ di_read_unlock(parent, AuLock_IR); -+ } -+ -+ if (d != dentry) -+ di_write_unlock(d); -+ dput(parent); -+ if (unlikely(err)) -+ break; -+ } -+ -+ return err; -+} -+ -+/* -+ * if valid returns 1, otherwise 0. -+ */ -+static int aufs_d_revalidate(struct dentry *dentry, unsigned int flags) -+{ -+ int valid, err; -+ unsigned int sigen; -+ unsigned char do_udba; -+ struct super_block *sb; -+ struct inode *inode; -+ -+ /* todo: support rcu-walk? */ -+ if (flags & LOOKUP_RCU) -+ return -ECHILD; -+ -+ valid = 0; -+ if (unlikely(!au_di(dentry))) -+ goto out; -+ -+ valid = 1; -+ sb = dentry->d_sb; -+ /* -+ * todo: very ugly -+ * i_mutex of parent dir may be held, -+ * but we should not return 'invalid' due to busy. -+ */ -+ err = aufs_read_lock(dentry, AuLock_FLUSH | AuLock_DW | AuLock_NOPLM); -+ if (unlikely(err)) { -+ valid = err; -+ AuTraceErr(err); -+ goto out; -+ } -+ inode = dentry->d_inode; -+ if (unlikely(inode && is_bad_inode(inode))) { -+ err = -EINVAL; -+ AuTraceErr(err); -+ goto out_dgrade; -+ } -+ if (unlikely(au_dbrange_test(dentry))) { -+ err = -EINVAL; -+ AuTraceErr(err); -+ goto out_dgrade; -+ } -+ -+ sigen = au_sigen(sb); -+ if (au_digen_test(dentry, sigen)) { -+ AuDebugOn(IS_ROOT(dentry)); -+ err = au_reval_dpath(dentry, sigen); -+ if (unlikely(err)) { -+ AuTraceErr(err); -+ goto out_dgrade; -+ } -+ } -+ di_downgrade_lock(dentry, AuLock_IR); -+ -+ err = -EINVAL; -+ if (!(flags & (LOOKUP_OPEN | LOOKUP_EMPTY)) -+ && inode -+ && !(inode->i_state && I_LINKABLE) -+ && (IS_DEADDIR(inode) || !inode->i_nlink)) { -+ AuTraceErr(err); -+ goto out_inval; -+ } -+ -+ do_udba = !au_opt_test(au_mntflags(sb), UDBA_NONE); -+ if (do_udba && inode) { -+ aufs_bindex_t bstart = au_ibstart(inode); -+ struct inode *h_inode; -+ -+ if (bstart >= 0) { -+ h_inode = au_h_iptr(inode, bstart); -+ if (h_inode && au_test_higen(inode, h_inode)) { -+ AuTraceErr(err); -+ goto out_inval; -+ } -+ } -+ } -+ -+ err = h_d_revalidate(dentry, inode, flags, do_udba); -+ if (unlikely(!err && do_udba && au_dbstart(dentry) < 0)) { -+ err = -EIO; -+ AuDbg("both of real entry and whiteout found, %p, err %d\n", -+ dentry, err); -+ } -+ goto out_inval; -+ -+out_dgrade: -+ di_downgrade_lock(dentry, AuLock_IR); -+out_inval: -+ aufs_read_unlock(dentry, AuLock_IR); -+ AuTraceErr(err); -+ valid = !err; -+out: -+ if (!valid) { -+ AuDbg("%pd invalid, %d\n", dentry, valid); -+ d_drop(dentry); -+ } -+ return valid; -+} -+ -+static void aufs_d_release(struct dentry *dentry) -+{ -+ if (au_di(dentry)) { -+ au_di_fin(dentry); -+ au_hn_di_reinit(dentry); -+ } -+} -+ -+const struct dentry_operations aufs_dop = { -+ .d_revalidate = aufs_d_revalidate, -+ .d_weak_revalidate = aufs_d_revalidate, -+ .d_release = aufs_d_release -+}; -+ -+/* aufs_dop without d_revalidate */ -+const struct dentry_operations aufs_dop_noreval = { -+ .d_release = aufs_d_release -+}; -diff --git a/fs/aufs/dentry.h b/fs/aufs/dentry.h -new file mode 100644 -index 0000000..4006484 ---- /dev/null -+++ b/fs/aufs/dentry.h -@@ -0,0 +1,234 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * lookup and dentry operations -+ */ -+ -+#ifndef __AUFS_DENTRY_H__ -+#define __AUFS_DENTRY_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+#include "rwsem.h" -+ -+struct au_hdentry { -+ struct dentry *hd_dentry; -+ aufs_bindex_t hd_id; -+}; -+ -+struct au_dinfo { -+ atomic_t di_generation; -+ -+ struct au_rwsem di_rwsem; -+ aufs_bindex_t di_bstart, di_bend, di_bwh, di_bdiropq; -+ unsigned char di_tmpfile; /* to allow the different name */ -+ struct au_hdentry *di_hdentry; -+} ____cacheline_aligned_in_smp; -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* dentry.c */ -+extern const struct dentry_operations aufs_dop, aufs_dop_noreval; -+struct au_branch; -+struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent); -+int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir, -+ struct dentry *h_parent, struct au_branch *br); -+ -+int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type); -+int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex, int wh); -+int au_refresh_dentry(struct dentry *dentry, struct dentry *parent); -+int au_reval_dpath(struct dentry *dentry, unsigned int sigen); -+void au_refresh_dop(struct dentry *dentry, int force_reval); -+ -+/* dinfo.c */ -+void au_di_init_once(void *_di); -+struct au_dinfo *au_di_alloc(struct super_block *sb, unsigned int lsc); -+void au_di_free(struct au_dinfo *dinfo); -+void au_di_swap(struct au_dinfo *a, struct au_dinfo *b); -+void au_di_cp(struct au_dinfo *dst, struct au_dinfo *src); -+int au_di_init(struct dentry *dentry); -+void au_di_fin(struct dentry *dentry); -+int au_di_realloc(struct au_dinfo *dinfo, int nbr); -+ -+void di_read_lock(struct dentry *d, int flags, unsigned int lsc); -+void di_read_unlock(struct dentry *d, int flags); -+void di_downgrade_lock(struct dentry *d, int flags); -+void di_write_lock(struct dentry *d, unsigned int lsc); -+void di_write_unlock(struct dentry *d); -+void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir); -+void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir); -+void di_write_unlock2(struct dentry *d1, struct dentry *d2); -+ -+struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex); -+struct dentry *au_h_d_alias(struct dentry *dentry, aufs_bindex_t bindex); -+aufs_bindex_t au_dbtail(struct dentry *dentry); -+aufs_bindex_t au_dbtaildir(struct dentry *dentry); -+ -+void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex, -+ struct dentry *h_dentry); -+int au_digen_test(struct dentry *dentry, unsigned int sigen); -+int au_dbrange_test(struct dentry *dentry); -+void au_update_digen(struct dentry *dentry); -+void au_update_dbrange(struct dentry *dentry, int do_put_zero); -+void au_update_dbstart(struct dentry *dentry); -+void au_update_dbend(struct dentry *dentry); -+int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry); -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline struct au_dinfo *au_di(struct dentry *dentry) -+{ -+ return dentry->d_fsdata; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* lock subclass for dinfo */ -+enum { -+ AuLsc_DI_CHILD, /* child first */ -+ AuLsc_DI_CHILD2, /* rename(2), link(2), and cpup at hnotify */ -+ AuLsc_DI_CHILD3, /* copyup dirs */ -+ AuLsc_DI_PARENT, -+ AuLsc_DI_PARENT2, -+ AuLsc_DI_PARENT3, -+ AuLsc_DI_TMP /* temp for replacing dinfo */ -+}; -+ -+/* -+ * di_read_lock_child, di_write_lock_child, -+ * di_read_lock_child2, di_write_lock_child2, -+ * di_read_lock_child3, di_write_lock_child3, -+ * di_read_lock_parent, di_write_lock_parent, -+ * di_read_lock_parent2, di_write_lock_parent2, -+ * di_read_lock_parent3, di_write_lock_parent3, -+ */ -+#define AuReadLockFunc(name, lsc) \ -+static inline void di_read_lock_##name(struct dentry *d, int flags) \ -+{ di_read_lock(d, flags, AuLsc_DI_##lsc); } -+ -+#define AuWriteLockFunc(name, lsc) \ -+static inline void di_write_lock_##name(struct dentry *d) \ -+{ di_write_lock(d, AuLsc_DI_##lsc); } -+ -+#define AuRWLockFuncs(name, lsc) \ -+ AuReadLockFunc(name, lsc) \ -+ AuWriteLockFunc(name, lsc) -+ -+AuRWLockFuncs(child, CHILD); -+AuRWLockFuncs(child2, CHILD2); -+AuRWLockFuncs(child3, CHILD3); -+AuRWLockFuncs(parent, PARENT); -+AuRWLockFuncs(parent2, PARENT2); -+AuRWLockFuncs(parent3, PARENT3); -+ -+#undef AuReadLockFunc -+#undef AuWriteLockFunc -+#undef AuRWLockFuncs -+ -+#define DiMustNoWaiters(d) AuRwMustNoWaiters(&au_di(d)->di_rwsem) -+#define DiMustAnyLock(d) AuRwMustAnyLock(&au_di(d)->di_rwsem) -+#define DiMustWriteLock(d) AuRwMustWriteLock(&au_di(d)->di_rwsem) -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* todo: memory barrier? */ -+static inline unsigned int au_digen(struct dentry *d) -+{ -+ return atomic_read(&au_di(d)->di_generation); -+} -+ -+static inline void au_h_dentry_init(struct au_hdentry *hdentry) -+{ -+ hdentry->hd_dentry = NULL; -+} -+ -+static inline void au_hdput(struct au_hdentry *hd) -+{ -+ if (hd) -+ dput(hd->hd_dentry); -+} -+ -+static inline aufs_bindex_t au_dbstart(struct dentry *dentry) -+{ -+ DiMustAnyLock(dentry); -+ return au_di(dentry)->di_bstart; -+} -+ -+static inline aufs_bindex_t au_dbend(struct dentry *dentry) -+{ -+ DiMustAnyLock(dentry); -+ return au_di(dentry)->di_bend; -+} -+ -+static inline aufs_bindex_t au_dbwh(struct dentry *dentry) -+{ -+ DiMustAnyLock(dentry); -+ return au_di(dentry)->di_bwh; -+} -+ -+static inline aufs_bindex_t au_dbdiropq(struct dentry *dentry) -+{ -+ DiMustAnyLock(dentry); -+ return au_di(dentry)->di_bdiropq; -+} -+ -+/* todo: hard/soft set? */ -+static inline void au_set_dbstart(struct dentry *dentry, aufs_bindex_t bindex) -+{ -+ DiMustWriteLock(dentry); -+ au_di(dentry)->di_bstart = bindex; -+} -+ -+static inline void au_set_dbend(struct dentry *dentry, aufs_bindex_t bindex) -+{ -+ DiMustWriteLock(dentry); -+ au_di(dentry)->di_bend = bindex; -+} -+ -+static inline void au_set_dbwh(struct dentry *dentry, aufs_bindex_t bindex) -+{ -+ DiMustWriteLock(dentry); -+ /* dbwh can be outside of bstart - bend range */ -+ au_di(dentry)->di_bwh = bindex; -+} -+ -+static inline void au_set_dbdiropq(struct dentry *dentry, aufs_bindex_t bindex) -+{ -+ DiMustWriteLock(dentry); -+ au_di(dentry)->di_bdiropq = bindex; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+#ifdef CONFIG_AUFS_HNOTIFY -+static inline void au_digen_dec(struct dentry *d) -+{ -+ atomic_dec(&au_di(d)->di_generation); -+} -+ -+static inline void au_hn_di_reinit(struct dentry *dentry) -+{ -+ dentry->d_fsdata = NULL; -+} -+#else -+AuStubVoid(au_hn_di_reinit, struct dentry *dentry __maybe_unused) -+#endif /* CONFIG_AUFS_HNOTIFY */ -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_DENTRY_H__ */ -diff --git a/fs/aufs/dinfo.c b/fs/aufs/dinfo.c -new file mode 100644 -index 0000000..28c02b3 ---- /dev/null -+++ b/fs/aufs/dinfo.c -@@ -0,0 +1,544 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * dentry private data -+ */ -+ -+#include "aufs.h" -+ -+void au_di_init_once(void *_dinfo) -+{ -+ struct au_dinfo *dinfo = _dinfo; -+ static struct lock_class_key aufs_di; -+ -+ au_rw_init(&dinfo->di_rwsem); -+ au_rw_class(&dinfo->di_rwsem, &aufs_di); -+} -+ -+struct au_dinfo *au_di_alloc(struct super_block *sb, unsigned int lsc) -+{ -+ struct au_dinfo *dinfo; -+ int nbr, i; -+ -+ dinfo = au_cache_alloc_dinfo(); -+ if (unlikely(!dinfo)) -+ goto out; -+ -+ nbr = au_sbend(sb) + 1; -+ if (nbr <= 0) -+ nbr = 1; -+ dinfo->di_hdentry = kcalloc(nbr, sizeof(*dinfo->di_hdentry), GFP_NOFS); -+ if (dinfo->di_hdentry) { -+ au_rw_write_lock_nested(&dinfo->di_rwsem, lsc); -+ dinfo->di_bstart = -1; -+ dinfo->di_bend = -1; -+ dinfo->di_bwh = -1; -+ dinfo->di_bdiropq = -1; -+ dinfo->di_tmpfile = 0; -+ for (i = 0; i < nbr; i++) -+ dinfo->di_hdentry[i].hd_id = -1; -+ goto out; -+ } -+ -+ au_cache_free_dinfo(dinfo); -+ dinfo = NULL; -+ -+out: -+ return dinfo; -+} -+ -+void au_di_free(struct au_dinfo *dinfo) -+{ -+ struct au_hdentry *p; -+ aufs_bindex_t bend, bindex; -+ -+ /* dentry may not be revalidated */ -+ bindex = dinfo->di_bstart; -+ if (bindex >= 0) { -+ bend = dinfo->di_bend; -+ p = dinfo->di_hdentry + bindex; -+ while (bindex++ <= bend) -+ au_hdput(p++); -+ } -+ kfree(dinfo->di_hdentry); -+ au_cache_free_dinfo(dinfo); -+} -+ -+void au_di_swap(struct au_dinfo *a, struct au_dinfo *b) -+{ -+ struct au_hdentry *p; -+ aufs_bindex_t bi; -+ -+ AuRwMustWriteLock(&a->di_rwsem); -+ AuRwMustWriteLock(&b->di_rwsem); -+ -+#define DiSwap(v, name) \ -+ do { \ -+ v = a->di_##name; \ -+ a->di_##name = b->di_##name; \ -+ b->di_##name = v; \ -+ } while (0) -+ -+ DiSwap(p, hdentry); -+ DiSwap(bi, bstart); -+ DiSwap(bi, bend); -+ DiSwap(bi, bwh); -+ DiSwap(bi, bdiropq); -+ /* smp_mb(); */ -+ -+#undef DiSwap -+} -+ -+void au_di_cp(struct au_dinfo *dst, struct au_dinfo *src) -+{ -+ AuRwMustWriteLock(&dst->di_rwsem); -+ AuRwMustWriteLock(&src->di_rwsem); -+ -+ dst->di_bstart = src->di_bstart; -+ dst->di_bend = src->di_bend; -+ dst->di_bwh = src->di_bwh; -+ dst->di_bdiropq = src->di_bdiropq; -+ /* smp_mb(); */ -+} -+ -+int au_di_init(struct dentry *dentry) -+{ -+ int err; -+ struct super_block *sb; -+ struct au_dinfo *dinfo; -+ -+ err = 0; -+ sb = dentry->d_sb; -+ dinfo = au_di_alloc(sb, AuLsc_DI_CHILD); -+ if (dinfo) { -+ atomic_set(&dinfo->di_generation, au_sigen(sb)); -+ /* smp_mb(); */ /* atomic_set */ -+ dentry->d_fsdata = dinfo; -+ } else -+ err = -ENOMEM; -+ -+ return err; -+} -+ -+void au_di_fin(struct dentry *dentry) -+{ -+ struct au_dinfo *dinfo; -+ -+ dinfo = au_di(dentry); -+ AuRwDestroy(&dinfo->di_rwsem); -+ au_di_free(dinfo); -+} -+ -+int au_di_realloc(struct au_dinfo *dinfo, int nbr) -+{ -+ int err, sz; -+ struct au_hdentry *hdp; -+ -+ AuRwMustWriteLock(&dinfo->di_rwsem); -+ -+ err = -ENOMEM; -+ sz = sizeof(*hdp) * (dinfo->di_bend + 1); -+ if (!sz) -+ sz = sizeof(*hdp); -+ hdp = au_kzrealloc(dinfo->di_hdentry, sz, sizeof(*hdp) * nbr, GFP_NOFS); -+ if (hdp) { -+ dinfo->di_hdentry = hdp; -+ err = 0; -+ } -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void do_ii_write_lock(struct inode *inode, unsigned int lsc) -+{ -+ switch (lsc) { -+ case AuLsc_DI_CHILD: -+ ii_write_lock_child(inode); -+ break; -+ case AuLsc_DI_CHILD2: -+ ii_write_lock_child2(inode); -+ break; -+ case AuLsc_DI_CHILD3: -+ ii_write_lock_child3(inode); -+ break; -+ case AuLsc_DI_PARENT: -+ ii_write_lock_parent(inode); -+ break; -+ case AuLsc_DI_PARENT2: -+ ii_write_lock_parent2(inode); -+ break; -+ case AuLsc_DI_PARENT3: -+ ii_write_lock_parent3(inode); -+ break; -+ default: -+ BUG(); -+ } -+} -+ -+static void do_ii_read_lock(struct inode *inode, unsigned int lsc) -+{ -+ switch (lsc) { -+ case AuLsc_DI_CHILD: -+ ii_read_lock_child(inode); -+ break; -+ case AuLsc_DI_CHILD2: -+ ii_read_lock_child2(inode); -+ break; -+ case AuLsc_DI_CHILD3: -+ ii_read_lock_child3(inode); -+ break; -+ case AuLsc_DI_PARENT: -+ ii_read_lock_parent(inode); -+ break; -+ case AuLsc_DI_PARENT2: -+ ii_read_lock_parent2(inode); -+ break; -+ case AuLsc_DI_PARENT3: -+ ii_read_lock_parent3(inode); -+ break; -+ default: -+ BUG(); -+ } -+} -+ -+void di_read_lock(struct dentry *d, int flags, unsigned int lsc) -+{ -+ au_rw_read_lock_nested(&au_di(d)->di_rwsem, lsc); -+ if (d->d_inode) { -+ if (au_ftest_lock(flags, IW)) -+ do_ii_write_lock(d->d_inode, lsc); -+ else if (au_ftest_lock(flags, IR)) -+ do_ii_read_lock(d->d_inode, lsc); -+ } -+} -+ -+void di_read_unlock(struct dentry *d, int flags) -+{ -+ if (d->d_inode) { -+ if (au_ftest_lock(flags, IW)) { -+ au_dbg_verify_dinode(d); -+ ii_write_unlock(d->d_inode); -+ } else if (au_ftest_lock(flags, IR)) { -+ au_dbg_verify_dinode(d); -+ ii_read_unlock(d->d_inode); -+ } -+ } -+ au_rw_read_unlock(&au_di(d)->di_rwsem); -+} -+ -+void di_downgrade_lock(struct dentry *d, int flags) -+{ -+ if (d->d_inode && au_ftest_lock(flags, IR)) -+ ii_downgrade_lock(d->d_inode); -+ au_rw_dgrade_lock(&au_di(d)->di_rwsem); -+} -+ -+void di_write_lock(struct dentry *d, unsigned int lsc) -+{ -+ au_rw_write_lock_nested(&au_di(d)->di_rwsem, lsc); -+ if (d->d_inode) -+ do_ii_write_lock(d->d_inode, lsc); -+} -+ -+void di_write_unlock(struct dentry *d) -+{ -+ au_dbg_verify_dinode(d); -+ if (d->d_inode) -+ ii_write_unlock(d->d_inode); -+ au_rw_write_unlock(&au_di(d)->di_rwsem); -+} -+ -+void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir) -+{ -+ AuDebugOn(d1 == d2 -+ || d1->d_inode == d2->d_inode -+ || d1->d_sb != d2->d_sb); -+ -+ if (isdir && au_test_subdir(d1, d2)) { -+ di_write_lock_child(d1); -+ di_write_lock_child2(d2); -+ } else { -+ /* there should be no races */ -+ di_write_lock_child(d2); -+ di_write_lock_child2(d1); -+ } -+} -+ -+void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir) -+{ -+ AuDebugOn(d1 == d2 -+ || d1->d_inode == d2->d_inode -+ || d1->d_sb != d2->d_sb); -+ -+ if (isdir && au_test_subdir(d1, d2)) { -+ di_write_lock_parent(d1); -+ di_write_lock_parent2(d2); -+ } else { -+ /* there should be no races */ -+ di_write_lock_parent(d2); -+ di_write_lock_parent2(d1); -+ } -+} -+ -+void di_write_unlock2(struct dentry *d1, struct dentry *d2) -+{ -+ di_write_unlock(d1); -+ if (d1->d_inode == d2->d_inode) -+ au_rw_write_unlock(&au_di(d2)->di_rwsem); -+ else -+ di_write_unlock(d2); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex) -+{ -+ struct dentry *d; -+ -+ DiMustAnyLock(dentry); -+ -+ if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry)) -+ return NULL; -+ AuDebugOn(bindex < 0); -+ d = au_di(dentry)->di_hdentry[0 + bindex].hd_dentry; -+ AuDebugOn(d && au_dcount(d) <= 0); -+ return d; -+} -+ -+/* -+ * extended version of au_h_dptr(). -+ * returns a hashed and positive (or linkable) h_dentry in bindex, NULL, or -+ * error. -+ */ -+struct dentry *au_h_d_alias(struct dentry *dentry, aufs_bindex_t bindex) -+{ -+ struct dentry *h_dentry; -+ struct inode *inode, *h_inode; -+ -+ inode = dentry->d_inode; -+ AuDebugOn(!inode); -+ -+ h_dentry = NULL; -+ if (au_dbstart(dentry) <= bindex -+ && bindex <= au_dbend(dentry)) -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (h_dentry && !au_d_linkable(h_dentry)) { -+ dget(h_dentry); -+ goto out; /* success */ -+ } -+ -+ AuDebugOn(bindex < au_ibstart(inode)); -+ AuDebugOn(au_ibend(inode) < bindex); -+ h_inode = au_h_iptr(inode, bindex); -+ h_dentry = d_find_alias(h_inode); -+ if (h_dentry) { -+ if (!IS_ERR(h_dentry)) { -+ if (!au_d_linkable(h_dentry)) -+ goto out; /* success */ -+ dput(h_dentry); -+ } else -+ goto out; -+ } -+ -+ if (au_opt_test(au_mntflags(dentry->d_sb), PLINK)) { -+ h_dentry = au_plink_lkup(inode, bindex); -+ AuDebugOn(!h_dentry); -+ if (!IS_ERR(h_dentry)) { -+ if (!au_d_hashed_positive(h_dentry)) -+ goto out; /* success */ -+ dput(h_dentry); -+ h_dentry = NULL; -+ } -+ } -+ -+out: -+ AuDbgDentry(h_dentry); -+ return h_dentry; -+} -+ -+aufs_bindex_t au_dbtail(struct dentry *dentry) -+{ -+ aufs_bindex_t bend, bwh; -+ -+ bend = au_dbend(dentry); -+ if (0 <= bend) { -+ bwh = au_dbwh(dentry); -+ if (!bwh) -+ return bwh; -+ if (0 < bwh && bwh < bend) -+ return bwh - 1; -+ } -+ return bend; -+} -+ -+aufs_bindex_t au_dbtaildir(struct dentry *dentry) -+{ -+ aufs_bindex_t bend, bopq; -+ -+ bend = au_dbtail(dentry); -+ if (0 <= bend) { -+ bopq = au_dbdiropq(dentry); -+ if (0 <= bopq && bopq < bend) -+ bend = bopq; -+ } -+ return bend; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex, -+ struct dentry *h_dentry) -+{ -+ struct au_hdentry *hd = au_di(dentry)->di_hdentry + bindex; -+ struct au_branch *br; -+ -+ DiMustWriteLock(dentry); -+ -+ au_hdput(hd); -+ hd->hd_dentry = h_dentry; -+ if (h_dentry) { -+ br = au_sbr(dentry->d_sb, bindex); -+ hd->hd_id = br->br_id; -+ } -+} -+ -+int au_dbrange_test(struct dentry *dentry) -+{ -+ int err; -+ aufs_bindex_t bstart, bend; -+ -+ err = 0; -+ bstart = au_dbstart(dentry); -+ bend = au_dbend(dentry); -+ if (bstart >= 0) -+ AuDebugOn(bend < 0 && bstart > bend); -+ else { -+ err = -EIO; -+ AuDebugOn(bend >= 0); -+ } -+ -+ return err; -+} -+ -+int au_digen_test(struct dentry *dentry, unsigned int sigen) -+{ -+ int err; -+ -+ err = 0; -+ if (unlikely(au_digen(dentry) != sigen -+ || au_iigen_test(dentry->d_inode, sigen))) -+ err = -EIO; -+ -+ return err; -+} -+ -+void au_update_digen(struct dentry *dentry) -+{ -+ atomic_set(&au_di(dentry)->di_generation, au_sigen(dentry->d_sb)); -+ /* smp_mb(); */ /* atomic_set */ -+} -+ -+void au_update_dbrange(struct dentry *dentry, int do_put_zero) -+{ -+ struct au_dinfo *dinfo; -+ struct dentry *h_d; -+ struct au_hdentry *hdp; -+ -+ DiMustWriteLock(dentry); -+ -+ dinfo = au_di(dentry); -+ if (!dinfo || dinfo->di_bstart < 0) -+ return; -+ -+ hdp = dinfo->di_hdentry; -+ if (do_put_zero) { -+ aufs_bindex_t bindex, bend; -+ -+ bend = dinfo->di_bend; -+ for (bindex = dinfo->di_bstart; bindex <= bend; bindex++) { -+ h_d = hdp[0 + bindex].hd_dentry; -+ if (h_d && !h_d->d_inode) -+ au_set_h_dptr(dentry, bindex, NULL); -+ } -+ } -+ -+ dinfo->di_bstart = -1; -+ while (++dinfo->di_bstart <= dinfo->di_bend) -+ if (hdp[0 + dinfo->di_bstart].hd_dentry) -+ break; -+ if (dinfo->di_bstart > dinfo->di_bend) { -+ dinfo->di_bstart = -1; -+ dinfo->di_bend = -1; -+ return; -+ } -+ -+ dinfo->di_bend++; -+ while (0 <= --dinfo->di_bend) -+ if (hdp[0 + dinfo->di_bend].hd_dentry) -+ break; -+ AuDebugOn(dinfo->di_bstart > dinfo->di_bend || dinfo->di_bend < 0); -+} -+ -+void au_update_dbstart(struct dentry *dentry) -+{ -+ aufs_bindex_t bindex, bend; -+ struct dentry *h_dentry; -+ -+ bend = au_dbend(dentry); -+ for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) { -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (!h_dentry) -+ continue; -+ if (h_dentry->d_inode) { -+ au_set_dbstart(dentry, bindex); -+ return; -+ } -+ au_set_h_dptr(dentry, bindex, NULL); -+ } -+} -+ -+void au_update_dbend(struct dentry *dentry) -+{ -+ aufs_bindex_t bindex, bstart; -+ struct dentry *h_dentry; -+ -+ bstart = au_dbstart(dentry); -+ for (bindex = au_dbend(dentry); bindex >= bstart; bindex--) { -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (!h_dentry) -+ continue; -+ if (h_dentry->d_inode) { -+ au_set_dbend(dentry, bindex); -+ return; -+ } -+ au_set_h_dptr(dentry, bindex, NULL); -+ } -+} -+ -+int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry) -+{ -+ aufs_bindex_t bindex, bend; -+ -+ bend = au_dbend(dentry); -+ for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) -+ if (au_h_dptr(dentry, bindex) == h_dentry) -+ return bindex; -+ return -1; -+} -diff --git a/fs/aufs/dir.c b/fs/aufs/dir.c -new file mode 100644 -index 0000000..3d61b05 ---- /dev/null -+++ b/fs/aufs/dir.c -@@ -0,0 +1,756 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * directory operations -+ */ -+ -+#include -+#include "aufs.h" -+ -+void au_add_nlink(struct inode *dir, struct inode *h_dir) -+{ -+ unsigned int nlink; -+ -+ AuDebugOn(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode)); -+ -+ nlink = dir->i_nlink; -+ nlink += h_dir->i_nlink - 2; -+ if (h_dir->i_nlink < 2) -+ nlink += 2; -+ smp_mb(); /* for i_nlink */ -+ /* 0 can happen in revaliding */ -+ set_nlink(dir, nlink); -+} -+ -+void au_sub_nlink(struct inode *dir, struct inode *h_dir) -+{ -+ unsigned int nlink; -+ -+ AuDebugOn(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode)); -+ -+ nlink = dir->i_nlink; -+ nlink -= h_dir->i_nlink - 2; -+ if (h_dir->i_nlink < 2) -+ nlink -= 2; -+ smp_mb(); /* for i_nlink */ -+ /* nlink == 0 means the branch-fs is broken */ -+ set_nlink(dir, nlink); -+} -+ -+loff_t au_dir_size(struct file *file, struct dentry *dentry) -+{ -+ loff_t sz; -+ aufs_bindex_t bindex, bend; -+ struct file *h_file; -+ struct dentry *h_dentry; -+ -+ sz = 0; -+ if (file) { -+ AuDebugOn(!d_is_dir(file->f_path.dentry)); -+ -+ bend = au_fbend_dir(file); -+ for (bindex = au_fbstart(file); -+ bindex <= bend && sz < KMALLOC_MAX_SIZE; -+ bindex++) { -+ h_file = au_hf_dir(file, bindex); -+ if (h_file && file_inode(h_file)) -+ sz += vfsub_f_size_read(h_file); -+ } -+ } else { -+ AuDebugOn(!dentry); -+ AuDebugOn(!d_is_dir(dentry)); -+ -+ bend = au_dbtaildir(dentry); -+ for (bindex = au_dbstart(dentry); -+ bindex <= bend && sz < KMALLOC_MAX_SIZE; -+ bindex++) { -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (h_dentry && h_dentry->d_inode) -+ sz += i_size_read(h_dentry->d_inode); -+ } -+ } -+ if (sz < KMALLOC_MAX_SIZE) -+ sz = roundup_pow_of_two(sz); -+ if (sz > KMALLOC_MAX_SIZE) -+ sz = KMALLOC_MAX_SIZE; -+ else if (sz < NAME_MAX) { -+ BUILD_BUG_ON(AUFS_RDBLK_DEF < NAME_MAX); -+ sz = AUFS_RDBLK_DEF; -+ } -+ return sz; -+} -+ -+struct au_dir_ts_arg { -+ struct dentry *dentry; -+ aufs_bindex_t brid; -+}; -+ -+static void au_do_dir_ts(void *arg) -+{ -+ struct au_dir_ts_arg *a = arg; -+ struct au_dtime dt; -+ struct path h_path; -+ struct inode *dir, *h_dir; -+ struct super_block *sb; -+ struct au_branch *br; -+ struct au_hinode *hdir; -+ int err; -+ aufs_bindex_t bstart, bindex; -+ -+ sb = a->dentry->d_sb; -+ dir = a->dentry->d_inode; -+ if (!dir) -+ goto out; -+ /* no dir->i_mutex lock */ -+ aufs_read_lock(a->dentry, AuLock_DW); /* noflush */ -+ -+ bstart = au_ibstart(dir); -+ bindex = au_br_index(sb, a->brid); -+ if (bindex < bstart) -+ goto out_unlock; -+ -+ br = au_sbr(sb, bindex); -+ h_path.dentry = au_h_dptr(a->dentry, bindex); -+ if (!h_path.dentry) -+ goto out_unlock; -+ h_path.mnt = au_br_mnt(br); -+ au_dtime_store(&dt, a->dentry, &h_path); -+ -+ br = au_sbr(sb, bstart); -+ if (!au_br_writable(br->br_perm)) -+ goto out_unlock; -+ h_path.dentry = au_h_dptr(a->dentry, bstart); -+ h_path.mnt = au_br_mnt(br); -+ err = vfsub_mnt_want_write(h_path.mnt); -+ if (err) -+ goto out_unlock; -+ hdir = au_hi(dir, bstart); -+ au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); -+ h_dir = au_h_iptr(dir, bstart); -+ if (h_dir->i_nlink -+ && timespec_compare(&h_dir->i_mtime, &dt.dt_mtime) < 0) { -+ dt.dt_h_path = h_path; -+ au_dtime_revert(&dt); -+ } -+ au_hn_imtx_unlock(hdir); -+ vfsub_mnt_drop_write(h_path.mnt); -+ au_cpup_attr_timesizes(dir); -+ -+out_unlock: -+ aufs_read_unlock(a->dentry, AuLock_DW); -+out: -+ dput(a->dentry); -+ au_nwt_done(&au_sbi(sb)->si_nowait); -+ kfree(arg); -+} -+ -+void au_dir_ts(struct inode *dir, aufs_bindex_t bindex) -+{ -+ int perm, wkq_err; -+ aufs_bindex_t bstart; -+ struct au_dir_ts_arg *arg; -+ struct dentry *dentry; -+ struct super_block *sb; -+ -+ IMustLock(dir); -+ -+ dentry = d_find_any_alias(dir); -+ AuDebugOn(!dentry); -+ sb = dentry->d_sb; -+ bstart = au_ibstart(dir); -+ if (bstart == bindex) { -+ au_cpup_attr_timesizes(dir); -+ goto out; -+ } -+ -+ perm = au_sbr_perm(sb, bstart); -+ if (!au_br_writable(perm)) -+ goto out; -+ -+ arg = kmalloc(sizeof(*arg), GFP_NOFS); -+ if (!arg) -+ goto out; -+ -+ arg->dentry = dget(dentry); /* will be dput-ted by au_do_dir_ts() */ -+ arg->brid = au_sbr_id(sb, bindex); -+ wkq_err = au_wkq_nowait(au_do_dir_ts, arg, sb, /*flags*/0); -+ if (unlikely(wkq_err)) { -+ pr_err("wkq %d\n", wkq_err); -+ dput(dentry); -+ kfree(arg); -+ } -+ -+out: -+ dput(dentry); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int reopen_dir(struct file *file) -+{ -+ int err; -+ unsigned int flags; -+ aufs_bindex_t bindex, btail, bstart; -+ struct dentry *dentry, *h_dentry; -+ struct file *h_file; -+ -+ /* open all lower dirs */ -+ dentry = file->f_dentry; -+ bstart = au_dbstart(dentry); -+ for (bindex = au_fbstart(file); bindex < bstart; bindex++) -+ au_set_h_fptr(file, bindex, NULL); -+ au_set_fbstart(file, bstart); -+ -+ btail = au_dbtaildir(dentry); -+ for (bindex = au_fbend_dir(file); btail < bindex; bindex--) -+ au_set_h_fptr(file, bindex, NULL); -+ au_set_fbend_dir(file, btail); -+ -+ flags = vfsub_file_flags(file); -+ for (bindex = bstart; bindex <= btail; bindex++) { -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (!h_dentry) -+ continue; -+ h_file = au_hf_dir(file, bindex); -+ if (h_file) -+ continue; -+ -+ h_file = au_h_open(dentry, bindex, flags, file, /*force_wr*/0); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; /* close all? */ -+ au_set_h_fptr(file, bindex, h_file); -+ } -+ au_update_figen(file); -+ /* todo: necessary? */ -+ /* file->f_ra = h_file->f_ra; */ -+ err = 0; -+ -+out: -+ return err; -+} -+ -+static int do_open_dir(struct file *file, int flags, struct file *h_file) -+{ -+ int err; -+ aufs_bindex_t bindex, btail; -+ struct dentry *dentry, *h_dentry; -+ struct vfsmount *mnt; -+ -+ FiMustWriteLock(file); -+ AuDebugOn(h_file); -+ -+ err = 0; -+ mnt = file->f_path.mnt; -+ dentry = file->f_dentry; -+ file->f_version = dentry->d_inode->i_version; -+ bindex = au_dbstart(dentry); -+ au_set_fbstart(file, bindex); -+ btail = au_dbtaildir(dentry); -+ au_set_fbend_dir(file, btail); -+ for (; !err && bindex <= btail; bindex++) { -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (!h_dentry) -+ continue; -+ -+ err = vfsub_test_mntns(mnt, h_dentry->d_sb); -+ if (unlikely(err)) -+ break; -+ h_file = au_h_open(dentry, bindex, flags, file, /*force_wr*/0); -+ if (IS_ERR(h_file)) { -+ err = PTR_ERR(h_file); -+ break; -+ } -+ au_set_h_fptr(file, bindex, h_file); -+ } -+ au_update_figen(file); -+ /* todo: necessary? */ -+ /* file->f_ra = h_file->f_ra; */ -+ if (!err) -+ return 0; /* success */ -+ -+ /* close all */ -+ for (bindex = au_fbstart(file); bindex <= btail; bindex++) -+ au_set_h_fptr(file, bindex, NULL); -+ au_set_fbstart(file, -1); -+ au_set_fbend_dir(file, -1); -+ -+ return err; -+} -+ -+static int aufs_open_dir(struct inode *inode __maybe_unused, -+ struct file *file) -+{ -+ int err; -+ struct super_block *sb; -+ struct au_fidir *fidir; -+ -+ err = -ENOMEM; -+ sb = file->f_dentry->d_sb; -+ si_read_lock(sb, AuLock_FLUSH); -+ fidir = au_fidir_alloc(sb); -+ if (fidir) { -+ struct au_do_open_args args = { -+ .open = do_open_dir, -+ .fidir = fidir -+ }; -+ err = au_do_open(file, &args); -+ if (unlikely(err)) -+ kfree(fidir); -+ } -+ si_read_unlock(sb); -+ return err; -+} -+ -+static int aufs_release_dir(struct inode *inode __maybe_unused, -+ struct file *file) -+{ -+ struct au_vdir *vdir_cache; -+ struct au_finfo *finfo; -+ struct au_fidir *fidir; -+ aufs_bindex_t bindex, bend; -+ -+ finfo = au_fi(file); -+ fidir = finfo->fi_hdir; -+ if (fidir) { -+ au_sphl_del(&finfo->fi_hlist, -+ &au_sbi(file->f_dentry->d_sb)->si_files); -+ vdir_cache = fidir->fd_vdir_cache; /* lock-free */ -+ if (vdir_cache) -+ au_vdir_free(vdir_cache); -+ -+ bindex = finfo->fi_btop; -+ if (bindex >= 0) { -+ /* -+ * calls fput() instead of filp_close(), -+ * since no dnotify or lock for the lower file. -+ */ -+ bend = fidir->fd_bbot; -+ for (; bindex <= bend; bindex++) -+ au_set_h_fptr(file, bindex, NULL); -+ } -+ kfree(fidir); -+ finfo->fi_hdir = NULL; -+ } -+ au_finfo_fin(file); -+ return 0; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_do_flush_dir(struct file *file, fl_owner_t id) -+{ -+ int err; -+ aufs_bindex_t bindex, bend; -+ struct file *h_file; -+ -+ err = 0; -+ bend = au_fbend_dir(file); -+ for (bindex = au_fbstart(file); !err && bindex <= bend; bindex++) { -+ h_file = au_hf_dir(file, bindex); -+ if (h_file) -+ err = vfsub_flush(h_file, id); -+ } -+ return err; -+} -+ -+static int aufs_flush_dir(struct file *file, fl_owner_t id) -+{ -+ return au_do_flush(file, id, au_do_flush_dir); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_do_fsync_dir_no_file(struct dentry *dentry, int datasync) -+{ -+ int err; -+ aufs_bindex_t bend, bindex; -+ struct inode *inode; -+ struct super_block *sb; -+ -+ err = 0; -+ sb = dentry->d_sb; -+ inode = dentry->d_inode; -+ IMustLock(inode); -+ bend = au_dbend(dentry); -+ for (bindex = au_dbstart(dentry); !err && bindex <= bend; bindex++) { -+ struct path h_path; -+ -+ if (au_test_ro(sb, bindex, inode)) -+ continue; -+ h_path.dentry = au_h_dptr(dentry, bindex); -+ if (!h_path.dentry) -+ continue; -+ -+ h_path.mnt = au_sbr_mnt(sb, bindex); -+ err = vfsub_fsync(NULL, &h_path, datasync); -+ } -+ -+ return err; -+} -+ -+static int au_do_fsync_dir(struct file *file, int datasync) -+{ -+ int err; -+ aufs_bindex_t bend, bindex; -+ struct file *h_file; -+ struct super_block *sb; -+ struct inode *inode; -+ -+ err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1); -+ if (unlikely(err)) -+ goto out; -+ -+ inode = file_inode(file); -+ sb = inode->i_sb; -+ bend = au_fbend_dir(file); -+ for (bindex = au_fbstart(file); !err && bindex <= bend; bindex++) { -+ h_file = au_hf_dir(file, bindex); -+ if (!h_file || au_test_ro(sb, bindex, inode)) -+ continue; -+ -+ err = vfsub_fsync(h_file, &h_file->f_path, datasync); -+ } -+ -+out: -+ return err; -+} -+ -+/* -+ * @file may be NULL -+ */ -+static int aufs_fsync_dir(struct file *file, loff_t start, loff_t end, -+ int datasync) -+{ -+ int err; -+ struct dentry *dentry; -+ struct super_block *sb; -+ struct mutex *mtx; -+ -+ err = 0; -+ dentry = file->f_dentry; -+ mtx = &dentry->d_inode->i_mutex; -+ mutex_lock(mtx); -+ sb = dentry->d_sb; -+ si_noflush_read_lock(sb); -+ if (file) -+ err = au_do_fsync_dir(file, datasync); -+ else { -+ di_write_lock_child(dentry); -+ err = au_do_fsync_dir_no_file(dentry, datasync); -+ } -+ au_cpup_attr_timesizes(dentry->d_inode); -+ di_write_unlock(dentry); -+ if (file) -+ fi_write_unlock(file); -+ -+ si_read_unlock(sb); -+ mutex_unlock(mtx); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int aufs_iterate(struct file *file, struct dir_context *ctx) -+{ -+ int err; -+ struct dentry *dentry; -+ struct inode *inode, *h_inode; -+ struct super_block *sb; -+ -+ AuDbg("%pD, ctx{%pf, %llu}\n", file, ctx->actor, ctx->pos); -+ -+ dentry = file->f_dentry; -+ inode = dentry->d_inode; -+ IMustLock(inode); -+ -+ sb = dentry->d_sb; -+ si_read_lock(sb, AuLock_FLUSH); -+ err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1); -+ if (unlikely(err)) -+ goto out; -+ err = au_alive_dir(dentry); -+ if (!err) -+ err = au_vdir_init(file); -+ di_downgrade_lock(dentry, AuLock_IR); -+ if (unlikely(err)) -+ goto out_unlock; -+ -+ h_inode = au_h_iptr(inode, au_ibstart(inode)); -+ if (!au_test_nfsd()) { -+ err = au_vdir_fill_de(file, ctx); -+ fsstack_copy_attr_atime(inode, h_inode); -+ } else { -+ /* -+ * nfsd filldir may call lookup_one_len(), vfs_getattr(), -+ * encode_fh() and others. -+ */ -+ atomic_inc(&h_inode->i_count); -+ di_read_unlock(dentry, AuLock_IR); -+ si_read_unlock(sb); -+ err = au_vdir_fill_de(file, ctx); -+ fsstack_copy_attr_atime(inode, h_inode); -+ fi_write_unlock(file); -+ iput(h_inode); -+ -+ AuTraceErr(err); -+ return err; -+ } -+ -+out_unlock: -+ di_read_unlock(dentry, AuLock_IR); -+ fi_write_unlock(file); -+out: -+ si_read_unlock(sb); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+#define AuTestEmpty_WHONLY 1 -+#define AuTestEmpty_CALLED (1 << 1) -+#define AuTestEmpty_SHWH (1 << 2) -+#define au_ftest_testempty(flags, name) ((flags) & AuTestEmpty_##name) -+#define au_fset_testempty(flags, name) \ -+ do { (flags) |= AuTestEmpty_##name; } while (0) -+#define au_fclr_testempty(flags, name) \ -+ do { (flags) &= ~AuTestEmpty_##name; } while (0) -+ -+#ifndef CONFIG_AUFS_SHWH -+#undef AuTestEmpty_SHWH -+#define AuTestEmpty_SHWH 0 -+#endif -+ -+struct test_empty_arg { -+ struct dir_context ctx; -+ struct au_nhash *whlist; -+ unsigned int flags; -+ int err; -+ aufs_bindex_t bindex; -+}; -+ -+static int test_empty_cb(struct dir_context *ctx, const char *__name, -+ int namelen, loff_t offset __maybe_unused, u64 ino, -+ unsigned int d_type) -+{ -+ struct test_empty_arg *arg = container_of(ctx, struct test_empty_arg, -+ ctx); -+ char *name = (void *)__name; -+ -+ arg->err = 0; -+ au_fset_testempty(arg->flags, CALLED); -+ /* smp_mb(); */ -+ if (name[0] == '.' -+ && (namelen == 1 || (name[1] == '.' && namelen == 2))) -+ goto out; /* success */ -+ -+ if (namelen <= AUFS_WH_PFX_LEN -+ || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { -+ if (au_ftest_testempty(arg->flags, WHONLY) -+ && !au_nhash_test_known_wh(arg->whlist, name, namelen)) -+ arg->err = -ENOTEMPTY; -+ goto out; -+ } -+ -+ name += AUFS_WH_PFX_LEN; -+ namelen -= AUFS_WH_PFX_LEN; -+ if (!au_nhash_test_known_wh(arg->whlist, name, namelen)) -+ arg->err = au_nhash_append_wh -+ (arg->whlist, name, namelen, ino, d_type, arg->bindex, -+ au_ftest_testempty(arg->flags, SHWH)); -+ -+out: -+ /* smp_mb(); */ -+ AuTraceErr(arg->err); -+ return arg->err; -+} -+ -+static int do_test_empty(struct dentry *dentry, struct test_empty_arg *arg) -+{ -+ int err; -+ struct file *h_file; -+ -+ h_file = au_h_open(dentry, arg->bindex, -+ O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_LARGEFILE, -+ /*file*/NULL, /*force_wr*/0); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ err = 0; -+ if (!au_opt_test(au_mntflags(dentry->d_sb), UDBA_NONE) -+ && !file_inode(h_file)->i_nlink) -+ goto out_put; -+ -+ do { -+ arg->err = 0; -+ au_fclr_testempty(arg->flags, CALLED); -+ /* smp_mb(); */ -+ err = vfsub_iterate_dir(h_file, &arg->ctx); -+ if (err >= 0) -+ err = arg->err; -+ } while (!err && au_ftest_testempty(arg->flags, CALLED)); -+ -+out_put: -+ fput(h_file); -+ au_sbr_put(dentry->d_sb, arg->bindex); -+out: -+ return err; -+} -+ -+struct do_test_empty_args { -+ int *errp; -+ struct dentry *dentry; -+ struct test_empty_arg *arg; -+}; -+ -+static void call_do_test_empty(void *args) -+{ -+ struct do_test_empty_args *a = args; -+ *a->errp = do_test_empty(a->dentry, a->arg); -+} -+ -+static int sio_test_empty(struct dentry *dentry, struct test_empty_arg *arg) -+{ -+ int err, wkq_err; -+ struct dentry *h_dentry; -+ struct inode *h_inode; -+ -+ h_dentry = au_h_dptr(dentry, arg->bindex); -+ h_inode = h_dentry->d_inode; -+ /* todo: i_mode changes anytime? */ -+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); -+ err = au_test_h_perm_sio(h_inode, MAY_EXEC | MAY_READ); -+ mutex_unlock(&h_inode->i_mutex); -+ if (!err) -+ err = do_test_empty(dentry, arg); -+ else { -+ struct do_test_empty_args args = { -+ .errp = &err, -+ .dentry = dentry, -+ .arg = arg -+ }; -+ unsigned int flags = arg->flags; -+ -+ wkq_err = au_wkq_wait(call_do_test_empty, &args); -+ if (unlikely(wkq_err)) -+ err = wkq_err; -+ arg->flags = flags; -+ } -+ -+ return err; -+} -+ -+int au_test_empty_lower(struct dentry *dentry) -+{ -+ int err; -+ unsigned int rdhash; -+ aufs_bindex_t bindex, bstart, btail; -+ struct au_nhash whlist; -+ struct test_empty_arg arg = { -+ .ctx = { -+ .actor = au_diractor(test_empty_cb) -+ } -+ }; -+ int (*test_empty)(struct dentry *dentry, struct test_empty_arg *arg); -+ -+ SiMustAnyLock(dentry->d_sb); -+ -+ rdhash = au_sbi(dentry->d_sb)->si_rdhash; -+ if (!rdhash) -+ rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, dentry)); -+ err = au_nhash_alloc(&whlist, rdhash, GFP_NOFS); -+ if (unlikely(err)) -+ goto out; -+ -+ arg.flags = 0; -+ arg.whlist = &whlist; -+ bstart = au_dbstart(dentry); -+ if (au_opt_test(au_mntflags(dentry->d_sb), SHWH)) -+ au_fset_testempty(arg.flags, SHWH); -+ test_empty = do_test_empty; -+ if (au_opt_test(au_mntflags(dentry->d_sb), DIRPERM1)) -+ test_empty = sio_test_empty; -+ arg.bindex = bstart; -+ err = test_empty(dentry, &arg); -+ if (unlikely(err)) -+ goto out_whlist; -+ -+ au_fset_testempty(arg.flags, WHONLY); -+ btail = au_dbtaildir(dentry); -+ for (bindex = bstart + 1; !err && bindex <= btail; bindex++) { -+ struct dentry *h_dentry; -+ -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (h_dentry && h_dentry->d_inode) { -+ arg.bindex = bindex; -+ err = test_empty(dentry, &arg); -+ } -+ } -+ -+out_whlist: -+ au_nhash_wh_free(&whlist); -+out: -+ return err; -+} -+ -+int au_test_empty(struct dentry *dentry, struct au_nhash *whlist) -+{ -+ int err; -+ struct test_empty_arg arg = { -+ .ctx = { -+ .actor = au_diractor(test_empty_cb) -+ } -+ }; -+ aufs_bindex_t bindex, btail; -+ -+ err = 0; -+ arg.whlist = whlist; -+ arg.flags = AuTestEmpty_WHONLY; -+ if (au_opt_test(au_mntflags(dentry->d_sb), SHWH)) -+ au_fset_testempty(arg.flags, SHWH); -+ btail = au_dbtaildir(dentry); -+ for (bindex = au_dbstart(dentry); !err && bindex <= btail; bindex++) { -+ struct dentry *h_dentry; -+ -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (h_dentry && h_dentry->d_inode) { -+ arg.bindex = bindex; -+ err = sio_test_empty(dentry, &arg); -+ } -+ } -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+const struct file_operations aufs_dir_fop = { -+ .owner = THIS_MODULE, -+ .llseek = default_llseek, -+ .read = generic_read_dir, -+ .iterate = aufs_iterate, -+ .unlocked_ioctl = aufs_ioctl_dir, -+#ifdef CONFIG_COMPAT -+ .compat_ioctl = aufs_compat_ioctl_dir, -+#endif -+ .open = aufs_open_dir, -+ .release = aufs_release_dir, -+ .flush = aufs_flush_dir, -+ .fsync = aufs_fsync_dir -+}; -diff --git a/fs/aufs/dir.h b/fs/aufs/dir.h -new file mode 100644 -index 0000000..16821f9 ---- /dev/null -+++ b/fs/aufs/dir.h -@@ -0,0 +1,131 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * directory operations -+ */ -+ -+#ifndef __AUFS_DIR_H__ -+#define __AUFS_DIR_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* need to be faster and smaller */ -+ -+struct au_nhash { -+ unsigned int nh_num; -+ struct hlist_head *nh_head; -+}; -+ -+struct au_vdir_destr { -+ unsigned char len; -+ unsigned char name[0]; -+} __packed; -+ -+struct au_vdir_dehstr { -+ struct hlist_node hash; -+ struct au_vdir_destr *str; -+} ____cacheline_aligned_in_smp; -+ -+struct au_vdir_de { -+ ino_t de_ino; -+ unsigned char de_type; -+ /* caution: packed */ -+ struct au_vdir_destr de_str; -+} __packed; -+ -+struct au_vdir_wh { -+ struct hlist_node wh_hash; -+#ifdef CONFIG_AUFS_SHWH -+ ino_t wh_ino; -+ aufs_bindex_t wh_bindex; -+ unsigned char wh_type; -+#else -+ aufs_bindex_t wh_bindex; -+#endif -+ /* caution: packed */ -+ struct au_vdir_destr wh_str; -+} __packed; -+ -+union au_vdir_deblk_p { -+ unsigned char *deblk; -+ struct au_vdir_de *de; -+}; -+ -+struct au_vdir { -+ unsigned char **vd_deblk; -+ unsigned long vd_nblk; -+ struct { -+ unsigned long ul; -+ union au_vdir_deblk_p p; -+ } vd_last; -+ -+ unsigned long vd_version; -+ unsigned int vd_deblk_sz; -+ unsigned long vd_jiffy; -+} ____cacheline_aligned_in_smp; -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* dir.c */ -+extern const struct file_operations aufs_dir_fop; -+void au_add_nlink(struct inode *dir, struct inode *h_dir); -+void au_sub_nlink(struct inode *dir, struct inode *h_dir); -+loff_t au_dir_size(struct file *file, struct dentry *dentry); -+void au_dir_ts(struct inode *dir, aufs_bindex_t bsrc); -+int au_test_empty_lower(struct dentry *dentry); -+int au_test_empty(struct dentry *dentry, struct au_nhash *whlist); -+ -+/* vdir.c */ -+unsigned int au_rdhash_est(loff_t sz); -+int au_nhash_alloc(struct au_nhash *nhash, unsigned int num_hash, gfp_t gfp); -+void au_nhash_wh_free(struct au_nhash *whlist); -+int au_nhash_test_longer_wh(struct au_nhash *whlist, aufs_bindex_t btgt, -+ int limit); -+int au_nhash_test_known_wh(struct au_nhash *whlist, char *name, int nlen); -+int au_nhash_append_wh(struct au_nhash *whlist, char *name, int nlen, ino_t ino, -+ unsigned int d_type, aufs_bindex_t bindex, -+ unsigned char shwh); -+void au_vdir_free(struct au_vdir *vdir); -+int au_vdir_init(struct file *file); -+int au_vdir_fill_de(struct file *file, struct dir_context *ctx); -+ -+/* ioctl.c */ -+long aufs_ioctl_dir(struct file *file, unsigned int cmd, unsigned long arg); -+ -+#ifdef CONFIG_AUFS_RDU -+/* rdu.c */ -+long au_rdu_ioctl(struct file *file, unsigned int cmd, unsigned long arg); -+#ifdef CONFIG_COMPAT -+long au_rdu_compat_ioctl(struct file *file, unsigned int cmd, -+ unsigned long arg); -+#endif -+#else -+AuStub(long, au_rdu_ioctl, return -EINVAL, struct file *file, -+ unsigned int cmd, unsigned long arg) -+#ifdef CONFIG_COMPAT -+AuStub(long, au_rdu_compat_ioctl, return -EINVAL, struct file *file, -+ unsigned int cmd, unsigned long arg) -+#endif -+#endif -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_DIR_H__ */ -diff --git a/fs/aufs/dynop.c b/fs/aufs/dynop.c -new file mode 100644 -index 0000000..d758805 ---- /dev/null -+++ b/fs/aufs/dynop.c -@@ -0,0 +1,379 @@ -+/* -+ * Copyright (C) 2010-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * dynamically customizable operations for regular files -+ */ -+ -+#include "aufs.h" -+ -+#define DyPrSym(key) AuDbgSym(key->dk_op.dy_hop) -+ -+/* -+ * How large will these lists be? -+ * Usually just a few elements, 20-30 at most for each, I guess. -+ */ -+static struct au_splhead dynop[AuDyLast]; -+ -+static struct au_dykey *dy_gfind_get(struct au_splhead *spl, const void *h_op) -+{ -+ struct au_dykey *key, *tmp; -+ struct list_head *head; -+ -+ key = NULL; -+ head = &spl->head; -+ rcu_read_lock(); -+ list_for_each_entry_rcu(tmp, head, dk_list) -+ if (tmp->dk_op.dy_hop == h_op) { -+ key = tmp; -+ kref_get(&key->dk_kref); -+ break; -+ } -+ rcu_read_unlock(); -+ -+ return key; -+} -+ -+static struct au_dykey *dy_bradd(struct au_branch *br, struct au_dykey *key) -+{ -+ struct au_dykey **k, *found; -+ const void *h_op = key->dk_op.dy_hop; -+ int i; -+ -+ found = NULL; -+ k = br->br_dykey; -+ for (i = 0; i < AuBrDynOp; i++) -+ if (k[i]) { -+ if (k[i]->dk_op.dy_hop == h_op) { -+ found = k[i]; -+ break; -+ } -+ } else -+ break; -+ if (!found) { -+ spin_lock(&br->br_dykey_lock); -+ for (; i < AuBrDynOp; i++) -+ if (k[i]) { -+ if (k[i]->dk_op.dy_hop == h_op) { -+ found = k[i]; -+ break; -+ } -+ } else { -+ k[i] = key; -+ break; -+ } -+ spin_unlock(&br->br_dykey_lock); -+ BUG_ON(i == AuBrDynOp); /* expand the array */ -+ } -+ -+ return found; -+} -+ -+/* kref_get() if @key is already added */ -+static struct au_dykey *dy_gadd(struct au_splhead *spl, struct au_dykey *key) -+{ -+ struct au_dykey *tmp, *found; -+ struct list_head *head; -+ const void *h_op = key->dk_op.dy_hop; -+ -+ found = NULL; -+ head = &spl->head; -+ spin_lock(&spl->spin); -+ list_for_each_entry(tmp, head, dk_list) -+ if (tmp->dk_op.dy_hop == h_op) { -+ kref_get(&tmp->dk_kref); -+ found = tmp; -+ break; -+ } -+ if (!found) -+ list_add_rcu(&key->dk_list, head); -+ spin_unlock(&spl->spin); -+ -+ if (!found) -+ DyPrSym(key); -+ return found; -+} -+ -+static void dy_free_rcu(struct rcu_head *rcu) -+{ -+ struct au_dykey *key; -+ -+ key = container_of(rcu, struct au_dykey, dk_rcu); -+ DyPrSym(key); -+ kfree(key); -+} -+ -+static void dy_free(struct kref *kref) -+{ -+ struct au_dykey *key; -+ struct au_splhead *spl; -+ -+ key = container_of(kref, struct au_dykey, dk_kref); -+ spl = dynop + key->dk_op.dy_type; -+ au_spl_del_rcu(&key->dk_list, spl); -+ call_rcu(&key->dk_rcu, dy_free_rcu); -+} -+ -+void au_dy_put(struct au_dykey *key) -+{ -+ kref_put(&key->dk_kref, dy_free); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+#define DyDbgSize(cnt, op) AuDebugOn(cnt != sizeof(op)/sizeof(void *)) -+ -+#ifdef CONFIG_AUFS_DEBUG -+#define DyDbgDeclare(cnt) unsigned int cnt = 0 -+#define DyDbgInc(cnt) do { cnt++; } while (0) -+#else -+#define DyDbgDeclare(cnt) do {} while (0) -+#define DyDbgInc(cnt) do {} while (0) -+#endif -+ -+#define DySet(func, dst, src, h_op, h_sb) do { \ -+ DyDbgInc(cnt); \ -+ if (h_op->func) { \ -+ if (src.func) \ -+ dst.func = src.func; \ -+ else \ -+ AuDbg("%s %s\n", au_sbtype(h_sb), #func); \ -+ } \ -+} while (0) -+ -+#define DySetForce(func, dst, src) do { \ -+ AuDebugOn(!src.func); \ -+ DyDbgInc(cnt); \ -+ dst.func = src.func; \ -+} while (0) -+ -+#define DySetAop(func) \ -+ DySet(func, dyaop->da_op, aufs_aop, h_aop, h_sb) -+#define DySetAopForce(func) \ -+ DySetForce(func, dyaop->da_op, aufs_aop) -+ -+static void dy_aop(struct au_dykey *key, const void *h_op, -+ struct super_block *h_sb __maybe_unused) -+{ -+ struct au_dyaop *dyaop = (void *)key; -+ const struct address_space_operations *h_aop = h_op; -+ DyDbgDeclare(cnt); -+ -+ AuDbg("%s\n", au_sbtype(h_sb)); -+ -+ DySetAop(writepage); -+ DySetAopForce(readpage); /* force */ -+ DySetAop(writepages); -+ DySetAop(set_page_dirty); -+ DySetAop(readpages); -+ DySetAop(write_begin); -+ DySetAop(write_end); -+ DySetAop(bmap); -+ DySetAop(invalidatepage); -+ DySetAop(releasepage); -+ DySetAop(freepage); -+ /* these two will be changed according to an aufs mount option */ -+ DySetAop(direct_IO); -+ DySetAop(get_xip_mem); -+ DySetAop(migratepage); -+ DySetAop(launder_page); -+ DySetAop(is_partially_uptodate); -+ DySetAop(is_dirty_writeback); -+ DySetAop(error_remove_page); -+ DySetAop(swap_activate); -+ DySetAop(swap_deactivate); -+ -+ DyDbgSize(cnt, *h_aop); -+ dyaop->da_get_xip_mem = h_aop->get_xip_mem; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void dy_bug(struct kref *kref) -+{ -+ BUG(); -+} -+ -+static struct au_dykey *dy_get(struct au_dynop *op, struct au_branch *br) -+{ -+ struct au_dykey *key, *old; -+ struct au_splhead *spl; -+ struct op { -+ unsigned int sz; -+ void (*set)(struct au_dykey *key, const void *h_op, -+ struct super_block *h_sb __maybe_unused); -+ }; -+ static const struct op a[] = { -+ [AuDy_AOP] = { -+ .sz = sizeof(struct au_dyaop), -+ .set = dy_aop -+ } -+ }; -+ const struct op *p; -+ -+ spl = dynop + op->dy_type; -+ key = dy_gfind_get(spl, op->dy_hop); -+ if (key) -+ goto out_add; /* success */ -+ -+ p = a + op->dy_type; -+ key = kzalloc(p->sz, GFP_NOFS); -+ if (unlikely(!key)) { -+ key = ERR_PTR(-ENOMEM); -+ goto out; -+ } -+ -+ key->dk_op.dy_hop = op->dy_hop; -+ kref_init(&key->dk_kref); -+ p->set(key, op->dy_hop, au_br_sb(br)); -+ old = dy_gadd(spl, key); -+ if (old) { -+ kfree(key); -+ key = old; -+ } -+ -+out_add: -+ old = dy_bradd(br, key); -+ if (old) -+ /* its ref-count should never be zero here */ -+ kref_put(&key->dk_kref, dy_bug); -+out: -+ return key; -+} -+ -+/* ---------------------------------------------------------------------- */ -+/* -+ * Aufs prohibits O_DIRECT by defaut even if the branch supports it. -+ * This behaviour is necessary to return an error from open(O_DIRECT) instead -+ * of the succeeding I/O. The dio mount option enables O_DIRECT and makes -+ * open(O_DIRECT) always succeed, but the succeeding I/O may return an error. -+ * See the aufs manual in detail. -+ * -+ * To keep this behaviour, aufs has to set NULL to ->get_xip_mem too, and the -+ * performance of fadvise() and madvise() may be affected. -+ */ -+static void dy_adx(struct au_dyaop *dyaop, int do_dx) -+{ -+ if (!do_dx) { -+ dyaop->da_op.direct_IO = NULL; -+ dyaop->da_op.get_xip_mem = NULL; -+ } else { -+ dyaop->da_op.direct_IO = aufs_aop.direct_IO; -+ dyaop->da_op.get_xip_mem = aufs_aop.get_xip_mem; -+ if (!dyaop->da_get_xip_mem) -+ dyaop->da_op.get_xip_mem = NULL; -+ } -+} -+ -+static struct au_dyaop *dy_aget(struct au_branch *br, -+ const struct address_space_operations *h_aop, -+ int do_dx) -+{ -+ struct au_dyaop *dyaop; -+ struct au_dynop op; -+ -+ op.dy_type = AuDy_AOP; -+ op.dy_haop = h_aop; -+ dyaop = (void *)dy_get(&op, br); -+ if (IS_ERR(dyaop)) -+ goto out; -+ dy_adx(dyaop, do_dx); -+ -+out: -+ return dyaop; -+} -+ -+int au_dy_iaop(struct inode *inode, aufs_bindex_t bindex, -+ struct inode *h_inode) -+{ -+ int err, do_dx; -+ struct super_block *sb; -+ struct au_branch *br; -+ struct au_dyaop *dyaop; -+ -+ AuDebugOn(!S_ISREG(h_inode->i_mode)); -+ IiMustWriteLock(inode); -+ -+ sb = inode->i_sb; -+ br = au_sbr(sb, bindex); -+ do_dx = !!au_opt_test(au_mntflags(sb), DIO); -+ dyaop = dy_aget(br, h_inode->i_mapping->a_ops, do_dx); -+ err = PTR_ERR(dyaop); -+ if (IS_ERR(dyaop)) -+ /* unnecessary to call dy_fput() */ -+ goto out; -+ -+ err = 0; -+ inode->i_mapping->a_ops = &dyaop->da_op; -+ -+out: -+ return err; -+} -+ -+/* -+ * Is it safe to replace a_ops during the inode/file is in operation? -+ * Yes, I hope so. -+ */ -+int au_dy_irefresh(struct inode *inode) -+{ -+ int err; -+ aufs_bindex_t bstart; -+ struct inode *h_inode; -+ -+ err = 0; -+ if (S_ISREG(inode->i_mode)) { -+ bstart = au_ibstart(inode); -+ h_inode = au_h_iptr(inode, bstart); -+ err = au_dy_iaop(inode, bstart, h_inode); -+ } -+ return err; -+} -+ -+void au_dy_arefresh(int do_dx) -+{ -+ struct au_splhead *spl; -+ struct list_head *head; -+ struct au_dykey *key; -+ -+ spl = dynop + AuDy_AOP; -+ head = &spl->head; -+ spin_lock(&spl->spin); -+ list_for_each_entry(key, head, dk_list) -+ dy_adx((void *)key, do_dx); -+ spin_unlock(&spl->spin); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void __init au_dy_init(void) -+{ -+ int i; -+ -+ /* make sure that 'struct au_dykey *' can be any type */ -+ BUILD_BUG_ON(offsetof(struct au_dyaop, da_key)); -+ -+ for (i = 0; i < AuDyLast; i++) -+ au_spl_init(dynop + i); -+} -+ -+void au_dy_fin(void) -+{ -+ int i; -+ -+ for (i = 0; i < AuDyLast; i++) -+ WARN_ON(!list_empty(&dynop[i].head)); -+} -diff --git a/fs/aufs/dynop.h b/fs/aufs/dynop.h -new file mode 100644 -index 0000000..cdf1499 ---- /dev/null -+++ b/fs/aufs/dynop.h -@@ -0,0 +1,76 @@ -+/* -+ * Copyright (C) 2010-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * dynamically customizable operations (for regular files only) -+ */ -+ -+#ifndef __AUFS_DYNOP_H__ -+#define __AUFS_DYNOP_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+#include -+ -+enum {AuDy_AOP, AuDyLast}; -+ -+struct au_dynop { -+ int dy_type; -+ union { -+ const void *dy_hop; -+ const struct address_space_operations *dy_haop; -+ }; -+}; -+ -+struct au_dykey { -+ union { -+ struct list_head dk_list; -+ struct rcu_head dk_rcu; -+ }; -+ struct au_dynop dk_op; -+ -+ /* -+ * during I am in the branch local array, kref is gotten. when the -+ * branch is removed, kref is put. -+ */ -+ struct kref dk_kref; -+}; -+ -+/* stop unioning since their sizes are very different from each other */ -+struct au_dyaop { -+ struct au_dykey da_key; -+ struct address_space_operations da_op; /* not const */ -+ int (*da_get_xip_mem)(struct address_space *, pgoff_t, int, -+ void **, unsigned long *); -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* dynop.c */ -+struct au_branch; -+void au_dy_put(struct au_dykey *key); -+int au_dy_iaop(struct inode *inode, aufs_bindex_t bindex, -+ struct inode *h_inode); -+int au_dy_irefresh(struct inode *inode); -+void au_dy_arefresh(int do_dio); -+ -+void __init au_dy_init(void); -+void au_dy_fin(void); -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_DYNOP_H__ */ -diff --git a/fs/aufs/export.c b/fs/aufs/export.c -new file mode 100644 -index 0000000..c5bfa76 ---- /dev/null -+++ b/fs/aufs/export.c -@@ -0,0 +1,831 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * export via nfs -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include "../fs/mount.h" -+#include "aufs.h" -+ -+union conv { -+#ifdef CONFIG_AUFS_INO_T_64 -+ __u32 a[2]; -+#else -+ __u32 a[1]; -+#endif -+ ino_t ino; -+}; -+ -+static ino_t decode_ino(__u32 *a) -+{ -+ union conv u; -+ -+ BUILD_BUG_ON(sizeof(u.ino) != sizeof(u.a)); -+ u.a[0] = a[0]; -+#ifdef CONFIG_AUFS_INO_T_64 -+ u.a[1] = a[1]; -+#endif -+ return u.ino; -+} -+ -+static void encode_ino(__u32 *a, ino_t ino) -+{ -+ union conv u; -+ -+ u.ino = ino; -+ a[0] = u.a[0]; -+#ifdef CONFIG_AUFS_INO_T_64 -+ a[1] = u.a[1]; -+#endif -+} -+ -+/* NFS file handle */ -+enum { -+ Fh_br_id, -+ Fh_sigen, -+#ifdef CONFIG_AUFS_INO_T_64 -+ /* support 64bit inode number */ -+ Fh_ino1, -+ Fh_ino2, -+ Fh_dir_ino1, -+ Fh_dir_ino2, -+#else -+ Fh_ino1, -+ Fh_dir_ino1, -+#endif -+ Fh_igen, -+ Fh_h_type, -+ Fh_tail, -+ -+ Fh_ino = Fh_ino1, -+ Fh_dir_ino = Fh_dir_ino1 -+}; -+ -+static int au_test_anon(struct dentry *dentry) -+{ -+ /* note: read d_flags without d_lock */ -+ return !!(dentry->d_flags & DCACHE_DISCONNECTED); -+} -+ -+int au_test_nfsd(void) -+{ -+ int ret; -+ struct task_struct *tsk = current; -+ char comm[sizeof(tsk->comm)]; -+ -+ ret = 0; -+ if (tsk->flags & PF_KTHREAD) { -+ get_task_comm(comm, tsk); -+ ret = !strcmp(comm, "nfsd"); -+ } -+ -+ return ret; -+} -+ -+/* ---------------------------------------------------------------------- */ -+/* inode generation external table */ -+ -+void au_xigen_inc(struct inode *inode) -+{ -+ loff_t pos; -+ ssize_t sz; -+ __u32 igen; -+ struct super_block *sb; -+ struct au_sbinfo *sbinfo; -+ -+ sb = inode->i_sb; -+ AuDebugOn(!au_opt_test(au_mntflags(sb), XINO)); -+ -+ sbinfo = au_sbi(sb); -+ pos = inode->i_ino; -+ pos *= sizeof(igen); -+ igen = inode->i_generation + 1; -+ sz = xino_fwrite(sbinfo->si_xwrite, sbinfo->si_xigen, &igen, -+ sizeof(igen), &pos); -+ if (sz == sizeof(igen)) -+ return; /* success */ -+ -+ if (unlikely(sz >= 0)) -+ AuIOErr("xigen error (%zd)\n", sz); -+} -+ -+int au_xigen_new(struct inode *inode) -+{ -+ int err; -+ loff_t pos; -+ ssize_t sz; -+ struct super_block *sb; -+ struct au_sbinfo *sbinfo; -+ struct file *file; -+ -+ err = 0; -+ /* todo: dirty, at mount time */ -+ if (inode->i_ino == AUFS_ROOT_INO) -+ goto out; -+ sb = inode->i_sb; -+ SiMustAnyLock(sb); -+ if (unlikely(!au_opt_test(au_mntflags(sb), XINO))) -+ goto out; -+ -+ err = -EFBIG; -+ pos = inode->i_ino; -+ if (unlikely(au_loff_max / sizeof(inode->i_generation) - 1 < pos)) { -+ AuIOErr1("too large i%lld\n", pos); -+ goto out; -+ } -+ pos *= sizeof(inode->i_generation); -+ -+ err = 0; -+ sbinfo = au_sbi(sb); -+ file = sbinfo->si_xigen; -+ BUG_ON(!file); -+ -+ if (vfsub_f_size_read(file) -+ < pos + sizeof(inode->i_generation)) { -+ inode->i_generation = atomic_inc_return(&sbinfo->si_xigen_next); -+ sz = xino_fwrite(sbinfo->si_xwrite, file, &inode->i_generation, -+ sizeof(inode->i_generation), &pos); -+ } else -+ sz = xino_fread(sbinfo->si_xread, file, &inode->i_generation, -+ sizeof(inode->i_generation), &pos); -+ if (sz == sizeof(inode->i_generation)) -+ goto out; /* success */ -+ -+ err = sz; -+ if (unlikely(sz >= 0)) { -+ err = -EIO; -+ AuIOErr("xigen error (%zd)\n", sz); -+ } -+ -+out: -+ return err; -+} -+ -+int au_xigen_set(struct super_block *sb, struct file *base) -+{ -+ int err; -+ struct au_sbinfo *sbinfo; -+ struct file *file; -+ -+ SiMustWriteLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ file = au_xino_create2(base, sbinfo->si_xigen); -+ err = PTR_ERR(file); -+ if (IS_ERR(file)) -+ goto out; -+ err = 0; -+ if (sbinfo->si_xigen) -+ fput(sbinfo->si_xigen); -+ sbinfo->si_xigen = file; -+ -+out: -+ return err; -+} -+ -+void au_xigen_clr(struct super_block *sb) -+{ -+ struct au_sbinfo *sbinfo; -+ -+ SiMustWriteLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ if (sbinfo->si_xigen) { -+ fput(sbinfo->si_xigen); -+ sbinfo->si_xigen = NULL; -+ } -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static struct dentry *decode_by_ino(struct super_block *sb, ino_t ino, -+ ino_t dir_ino) -+{ -+ struct dentry *dentry, *d; -+ struct inode *inode; -+ unsigned int sigen; -+ -+ dentry = NULL; -+ inode = ilookup(sb, ino); -+ if (!inode) -+ goto out; -+ -+ dentry = ERR_PTR(-ESTALE); -+ sigen = au_sigen(sb); -+ if (unlikely(is_bad_inode(inode) -+ || IS_DEADDIR(inode) -+ || sigen != au_iigen(inode, NULL))) -+ goto out_iput; -+ -+ dentry = NULL; -+ if (!dir_ino || S_ISDIR(inode->i_mode)) -+ dentry = d_find_alias(inode); -+ else { -+ spin_lock(&inode->i_lock); -+ hlist_for_each_entry(d, &inode->i_dentry, d_u.d_alias) { -+ spin_lock(&d->d_lock); -+ if (!au_test_anon(d) -+ && d->d_parent->d_inode->i_ino == dir_ino) { -+ dentry = dget_dlock(d); -+ spin_unlock(&d->d_lock); -+ break; -+ } -+ spin_unlock(&d->d_lock); -+ } -+ spin_unlock(&inode->i_lock); -+ } -+ if (unlikely(dentry && au_digen_test(dentry, sigen))) { -+ /* need to refresh */ -+ dput(dentry); -+ dentry = NULL; -+ } -+ -+out_iput: -+ iput(inode); -+out: -+ AuTraceErrPtr(dentry); -+ return dentry; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* todo: dirty? */ -+/* if exportfs_decode_fh() passed vfsmount*, we could be happy */ -+ -+struct au_compare_mnt_args { -+ /* input */ -+ struct super_block *sb; -+ -+ /* output */ -+ struct vfsmount *mnt; -+}; -+ -+static int au_compare_mnt(struct vfsmount *mnt, void *arg) -+{ -+ struct au_compare_mnt_args *a = arg; -+ -+ if (mnt->mnt_sb != a->sb) -+ return 0; -+ a->mnt = mntget(mnt); -+ return 1; -+} -+ -+static struct vfsmount *au_mnt_get(struct super_block *sb) -+{ -+ int err; -+ struct path root; -+ struct au_compare_mnt_args args = { -+ .sb = sb -+ }; -+ -+ get_fs_root(current->fs, &root); -+ rcu_read_lock(); -+ err = iterate_mounts(au_compare_mnt, &args, root.mnt); -+ rcu_read_unlock(); -+ path_put(&root); -+ AuDebugOn(!err); -+ AuDebugOn(!args.mnt); -+ return args.mnt; -+} -+ -+struct au_nfsd_si_lock { -+ unsigned int sigen; -+ aufs_bindex_t bindex, br_id; -+ unsigned char force_lock; -+}; -+ -+static int si_nfsd_read_lock(struct super_block *sb, -+ struct au_nfsd_si_lock *nsi_lock) -+{ -+ int err; -+ aufs_bindex_t bindex; -+ -+ si_read_lock(sb, AuLock_FLUSH); -+ -+ /* branch id may be wrapped around */ -+ err = 0; -+ bindex = au_br_index(sb, nsi_lock->br_id); -+ if (bindex >= 0 && nsi_lock->sigen + AUFS_BRANCH_MAX > au_sigen(sb)) -+ goto out; /* success */ -+ -+ err = -ESTALE; -+ bindex = -1; -+ if (!nsi_lock->force_lock) -+ si_read_unlock(sb); -+ -+out: -+ nsi_lock->bindex = bindex; -+ return err; -+} -+ -+struct find_name_by_ino { -+ struct dir_context ctx; -+ int called, found; -+ ino_t ino; -+ char *name; -+ int namelen; -+}; -+ -+static int -+find_name_by_ino(struct dir_context *ctx, const char *name, int namelen, -+ loff_t offset, u64 ino, unsigned int d_type) -+{ -+ struct find_name_by_ino *a = container_of(ctx, struct find_name_by_ino, -+ ctx); -+ -+ a->called++; -+ if (a->ino != ino) -+ return 0; -+ -+ memcpy(a->name, name, namelen); -+ a->namelen = namelen; -+ a->found = 1; -+ return 1; -+} -+ -+static struct dentry *au_lkup_by_ino(struct path *path, ino_t ino, -+ struct au_nfsd_si_lock *nsi_lock) -+{ -+ struct dentry *dentry, *parent; -+ struct file *file; -+ struct inode *dir; -+ struct find_name_by_ino arg = { -+ .ctx = { -+ .actor = au_diractor(find_name_by_ino) -+ } -+ }; -+ int err; -+ -+ parent = path->dentry; -+ if (nsi_lock) -+ si_read_unlock(parent->d_sb); -+ file = vfsub_dentry_open(path, au_dir_roflags); -+ dentry = (void *)file; -+ if (IS_ERR(file)) -+ goto out; -+ -+ dentry = ERR_PTR(-ENOMEM); -+ arg.name = (void *)__get_free_page(GFP_NOFS); -+ if (unlikely(!arg.name)) -+ goto out_file; -+ arg.ino = ino; -+ arg.found = 0; -+ do { -+ arg.called = 0; -+ /* smp_mb(); */ -+ err = vfsub_iterate_dir(file, &arg.ctx); -+ } while (!err && !arg.found && arg.called); -+ dentry = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out_name; -+ /* instead of ENOENT */ -+ dentry = ERR_PTR(-ESTALE); -+ if (!arg.found) -+ goto out_name; -+ -+ /* do not call vfsub_lkup_one() */ -+ dir = parent->d_inode; -+ mutex_lock(&dir->i_mutex); -+ dentry = vfsub_lookup_one_len(arg.name, parent, arg.namelen); -+ mutex_unlock(&dir->i_mutex); -+ AuTraceErrPtr(dentry); -+ if (IS_ERR(dentry)) -+ goto out_name; -+ AuDebugOn(au_test_anon(dentry)); -+ if (unlikely(!dentry->d_inode)) { -+ dput(dentry); -+ dentry = ERR_PTR(-ENOENT); -+ } -+ -+out_name: -+ free_page((unsigned long)arg.name); -+out_file: -+ fput(file); -+out: -+ if (unlikely(nsi_lock -+ && si_nfsd_read_lock(parent->d_sb, nsi_lock) < 0)) -+ if (!IS_ERR(dentry)) { -+ dput(dentry); -+ dentry = ERR_PTR(-ESTALE); -+ } -+ AuTraceErrPtr(dentry); -+ return dentry; -+} -+ -+static struct dentry *decode_by_dir_ino(struct super_block *sb, ino_t ino, -+ ino_t dir_ino, -+ struct au_nfsd_si_lock *nsi_lock) -+{ -+ struct dentry *dentry; -+ struct path path; -+ -+ if (dir_ino != AUFS_ROOT_INO) { -+ path.dentry = decode_by_ino(sb, dir_ino, 0); -+ dentry = path.dentry; -+ if (!path.dentry || IS_ERR(path.dentry)) -+ goto out; -+ AuDebugOn(au_test_anon(path.dentry)); -+ } else -+ path.dentry = dget(sb->s_root); -+ -+ path.mnt = au_mnt_get(sb); -+ dentry = au_lkup_by_ino(&path, ino, nsi_lock); -+ path_put(&path); -+ -+out: -+ AuTraceErrPtr(dentry); -+ return dentry; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int h_acceptable(void *expv, struct dentry *dentry) -+{ -+ return 1; -+} -+ -+static char *au_build_path(struct dentry *h_parent, struct path *h_rootpath, -+ char *buf, int len, struct super_block *sb) -+{ -+ char *p; -+ int n; -+ struct path path; -+ -+ p = d_path(h_rootpath, buf, len); -+ if (IS_ERR(p)) -+ goto out; -+ n = strlen(p); -+ -+ path.mnt = h_rootpath->mnt; -+ path.dentry = h_parent; -+ p = d_path(&path, buf, len); -+ if (IS_ERR(p)) -+ goto out; -+ if (n != 1) -+ p += n; -+ -+ path.mnt = au_mnt_get(sb); -+ path.dentry = sb->s_root; -+ p = d_path(&path, buf, len - strlen(p)); -+ mntput(path.mnt); -+ if (IS_ERR(p)) -+ goto out; -+ if (n != 1) -+ p[strlen(p)] = '/'; -+ -+out: -+ AuTraceErrPtr(p); -+ return p; -+} -+ -+static -+struct dentry *decode_by_path(struct super_block *sb, ino_t ino, __u32 *fh, -+ int fh_len, struct au_nfsd_si_lock *nsi_lock) -+{ -+ struct dentry *dentry, *h_parent, *root; -+ struct super_block *h_sb; -+ char *pathname, *p; -+ struct vfsmount *h_mnt; -+ struct au_branch *br; -+ int err; -+ struct path path; -+ -+ br = au_sbr(sb, nsi_lock->bindex); -+ h_mnt = au_br_mnt(br); -+ h_sb = h_mnt->mnt_sb; -+ /* todo: call lower fh_to_dentry()? fh_to_parent()? */ -+ h_parent = exportfs_decode_fh(h_mnt, (void *)(fh + Fh_tail), -+ fh_len - Fh_tail, fh[Fh_h_type], -+ h_acceptable, /*context*/NULL); -+ dentry = h_parent; -+ if (unlikely(!h_parent || IS_ERR(h_parent))) { -+ AuWarn1("%s decode_fh failed, %ld\n", -+ au_sbtype(h_sb), PTR_ERR(h_parent)); -+ goto out; -+ } -+ dentry = NULL; -+ if (unlikely(au_test_anon(h_parent))) { -+ AuWarn1("%s decode_fh returned a disconnected dentry\n", -+ au_sbtype(h_sb)); -+ goto out_h_parent; -+ } -+ -+ dentry = ERR_PTR(-ENOMEM); -+ pathname = (void *)__get_free_page(GFP_NOFS); -+ if (unlikely(!pathname)) -+ goto out_h_parent; -+ -+ root = sb->s_root; -+ path.mnt = h_mnt; -+ di_read_lock_parent(root, !AuLock_IR); -+ path.dentry = au_h_dptr(root, nsi_lock->bindex); -+ di_read_unlock(root, !AuLock_IR); -+ p = au_build_path(h_parent, &path, pathname, PAGE_SIZE, sb); -+ dentry = (void *)p; -+ if (IS_ERR(p)) -+ goto out_pathname; -+ -+ si_read_unlock(sb); -+ err = vfsub_kern_path(p, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path); -+ dentry = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out_relock; -+ -+ dentry = ERR_PTR(-ENOENT); -+ AuDebugOn(au_test_anon(path.dentry)); -+ if (unlikely(!path.dentry->d_inode)) -+ goto out_path; -+ -+ if (ino != path.dentry->d_inode->i_ino) -+ dentry = au_lkup_by_ino(&path, ino, /*nsi_lock*/NULL); -+ else -+ dentry = dget(path.dentry); -+ -+out_path: -+ path_put(&path); -+out_relock: -+ if (unlikely(si_nfsd_read_lock(sb, nsi_lock) < 0)) -+ if (!IS_ERR(dentry)) { -+ dput(dentry); -+ dentry = ERR_PTR(-ESTALE); -+ } -+out_pathname: -+ free_page((unsigned long)pathname); -+out_h_parent: -+ dput(h_parent); -+out: -+ AuTraceErrPtr(dentry); -+ return dentry; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static struct dentry * -+aufs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len, -+ int fh_type) -+{ -+ struct dentry *dentry; -+ __u32 *fh = fid->raw; -+ struct au_branch *br; -+ ino_t ino, dir_ino; -+ struct au_nfsd_si_lock nsi_lock = { -+ .force_lock = 0 -+ }; -+ -+ dentry = ERR_PTR(-ESTALE); -+ /* it should never happen, but the file handle is unreliable */ -+ if (unlikely(fh_len < Fh_tail)) -+ goto out; -+ nsi_lock.sigen = fh[Fh_sigen]; -+ nsi_lock.br_id = fh[Fh_br_id]; -+ -+ /* branch id may be wrapped around */ -+ br = NULL; -+ if (unlikely(si_nfsd_read_lock(sb, &nsi_lock))) -+ goto out; -+ nsi_lock.force_lock = 1; -+ -+ /* is this inode still cached? */ -+ ino = decode_ino(fh + Fh_ino); -+ /* it should never happen */ -+ if (unlikely(ino == AUFS_ROOT_INO)) -+ goto out_unlock; -+ -+ dir_ino = decode_ino(fh + Fh_dir_ino); -+ dentry = decode_by_ino(sb, ino, dir_ino); -+ if (IS_ERR(dentry)) -+ goto out_unlock; -+ if (dentry) -+ goto accept; -+ -+ /* is the parent dir cached? */ -+ br = au_sbr(sb, nsi_lock.bindex); -+ atomic_inc(&br->br_count); -+ dentry = decode_by_dir_ino(sb, ino, dir_ino, &nsi_lock); -+ if (IS_ERR(dentry)) -+ goto out_unlock; -+ if (dentry) -+ goto accept; -+ -+ /* lookup path */ -+ dentry = decode_by_path(sb, ino, fh, fh_len, &nsi_lock); -+ if (IS_ERR(dentry)) -+ goto out_unlock; -+ if (unlikely(!dentry)) -+ /* todo?: make it ESTALE */ -+ goto out_unlock; -+ -+accept: -+ if (!au_digen_test(dentry, au_sigen(sb)) -+ && dentry->d_inode->i_generation == fh[Fh_igen]) -+ goto out_unlock; /* success */ -+ -+ dput(dentry); -+ dentry = ERR_PTR(-ESTALE); -+out_unlock: -+ if (br) -+ atomic_dec(&br->br_count); -+ si_read_unlock(sb); -+out: -+ AuTraceErrPtr(dentry); -+ return dentry; -+} -+ -+#if 0 /* reserved for future use */ -+/* support subtreecheck option */ -+static struct dentry *aufs_fh_to_parent(struct super_block *sb, struct fid *fid, -+ int fh_len, int fh_type) -+{ -+ struct dentry *parent; -+ __u32 *fh = fid->raw; -+ ino_t dir_ino; -+ -+ dir_ino = decode_ino(fh + Fh_dir_ino); -+ parent = decode_by_ino(sb, dir_ino, 0); -+ if (IS_ERR(parent)) -+ goto out; -+ if (!parent) -+ parent = decode_by_path(sb, au_br_index(sb, fh[Fh_br_id]), -+ dir_ino, fh, fh_len); -+ -+out: -+ AuTraceErrPtr(parent); -+ return parent; -+} -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int aufs_encode_fh(struct inode *inode, __u32 *fh, int *max_len, -+ struct inode *dir) -+{ -+ int err; -+ aufs_bindex_t bindex; -+ struct super_block *sb, *h_sb; -+ struct dentry *dentry, *parent, *h_parent; -+ struct inode *h_dir; -+ struct au_branch *br; -+ -+ err = -ENOSPC; -+ if (unlikely(*max_len <= Fh_tail)) { -+ AuWarn1("NFSv2 client (max_len %d)?\n", *max_len); -+ goto out; -+ } -+ -+ err = FILEID_ROOT; -+ if (inode->i_ino == AUFS_ROOT_INO) { -+ AuDebugOn(inode->i_ino != AUFS_ROOT_INO); -+ goto out; -+ } -+ -+ h_parent = NULL; -+ sb = inode->i_sb; -+ err = si_read_lock(sb, AuLock_FLUSH); -+ if (unlikely(err)) -+ goto out; -+ -+#ifdef CONFIG_AUFS_DEBUG -+ if (unlikely(!au_opt_test(au_mntflags(sb), XINO))) -+ AuWarn1("NFS-exporting requires xino\n"); -+#endif -+ err = -EIO; -+ parent = NULL; -+ ii_read_lock_child(inode); -+ bindex = au_ibstart(inode); -+ if (!dir) { -+ dentry = d_find_any_alias(inode); -+ if (unlikely(!dentry)) -+ goto out_unlock; -+ AuDebugOn(au_test_anon(dentry)); -+ parent = dget_parent(dentry); -+ dput(dentry); -+ if (unlikely(!parent)) -+ goto out_unlock; -+ dir = parent->d_inode; -+ } -+ -+ ii_read_lock_parent(dir); -+ h_dir = au_h_iptr(dir, bindex); -+ ii_read_unlock(dir); -+ if (unlikely(!h_dir)) -+ goto out_parent; -+ h_parent = d_find_any_alias(h_dir); -+ if (unlikely(!h_parent)) -+ goto out_hparent; -+ -+ err = -EPERM; -+ br = au_sbr(sb, bindex); -+ h_sb = au_br_sb(br); -+ if (unlikely(!h_sb->s_export_op)) { -+ AuErr1("%s branch is not exportable\n", au_sbtype(h_sb)); -+ goto out_hparent; -+ } -+ -+ fh[Fh_br_id] = br->br_id; -+ fh[Fh_sigen] = au_sigen(sb); -+ encode_ino(fh + Fh_ino, inode->i_ino); -+ encode_ino(fh + Fh_dir_ino, dir->i_ino); -+ fh[Fh_igen] = inode->i_generation; -+ -+ *max_len -= Fh_tail; -+ fh[Fh_h_type] = exportfs_encode_fh(h_parent, (void *)(fh + Fh_tail), -+ max_len, -+ /*connectable or subtreecheck*/0); -+ err = fh[Fh_h_type]; -+ *max_len += Fh_tail; -+ /* todo: macros? */ -+ if (err != FILEID_INVALID) -+ err = 99; -+ else -+ AuWarn1("%s encode_fh failed\n", au_sbtype(h_sb)); -+ -+out_hparent: -+ dput(h_parent); -+out_parent: -+ dput(parent); -+out_unlock: -+ ii_read_unlock(inode); -+ si_read_unlock(sb); -+out: -+ if (unlikely(err < 0)) -+ err = FILEID_INVALID; -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int aufs_commit_metadata(struct inode *inode) -+{ -+ int err; -+ aufs_bindex_t bindex; -+ struct super_block *sb; -+ struct inode *h_inode; -+ int (*f)(struct inode *inode); -+ -+ sb = inode->i_sb; -+ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); -+ ii_write_lock_child(inode); -+ bindex = au_ibstart(inode); -+ AuDebugOn(bindex < 0); -+ h_inode = au_h_iptr(inode, bindex); -+ -+ f = h_inode->i_sb->s_export_op->commit_metadata; -+ if (f) -+ err = f(h_inode); -+ else { -+ struct writeback_control wbc = { -+ .sync_mode = WB_SYNC_ALL, -+ .nr_to_write = 0 /* metadata only */ -+ }; -+ -+ err = sync_inode(h_inode, &wbc); -+ } -+ -+ au_cpup_attr_timesizes(inode); -+ ii_write_unlock(inode); -+ si_read_unlock(sb); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static struct export_operations aufs_export_op = { -+ .fh_to_dentry = aufs_fh_to_dentry, -+ /* .fh_to_parent = aufs_fh_to_parent, */ -+ .encode_fh = aufs_encode_fh, -+ .commit_metadata = aufs_commit_metadata -+}; -+ -+void au_export_init(struct super_block *sb) -+{ -+ struct au_sbinfo *sbinfo; -+ __u32 u; -+ -+ sb->s_export_op = &aufs_export_op; -+ sbinfo = au_sbi(sb); -+ sbinfo->si_xigen = NULL; -+ get_random_bytes(&u, sizeof(u)); -+ BUILD_BUG_ON(sizeof(u) != sizeof(int)); -+ atomic_set(&sbinfo->si_xigen_next, u); -+} -diff --git a/fs/aufs/f_op.c b/fs/aufs/f_op.c -new file mode 100644 -index 0000000..b08981a ---- /dev/null -+++ b/fs/aufs/f_op.c -@@ -0,0 +1,781 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * file and vm operations -+ */ -+ -+#include -+#include -+#include -+#include -+#include "aufs.h" -+ -+int au_do_open_nondir(struct file *file, int flags, struct file *h_file) -+{ -+ int err; -+ aufs_bindex_t bindex; -+ struct dentry *dentry, *h_dentry; -+ struct au_finfo *finfo; -+ struct inode *h_inode; -+ -+ FiMustWriteLock(file); -+ -+ err = 0; -+ dentry = file->f_dentry; -+ AuDebugOn(IS_ERR_OR_NULL(dentry)); -+ finfo = au_fi(file); -+ memset(&finfo->fi_htop, 0, sizeof(finfo->fi_htop)); -+ atomic_set(&finfo->fi_mmapped, 0); -+ bindex = au_dbstart(dentry); -+ if (!h_file) { -+ h_dentry = au_h_dptr(dentry, bindex); -+ err = vfsub_test_mntns(file->f_path.mnt, h_dentry->d_sb); -+ if (unlikely(err)) -+ goto out; -+ h_file = au_h_open(dentry, bindex, flags, file, /*force_wr*/0); -+ } else { -+ h_dentry = h_file->f_dentry; -+ err = vfsub_test_mntns(file->f_path.mnt, h_dentry->d_sb); -+ if (unlikely(err)) -+ goto out; -+ get_file(h_file); -+ } -+ if (IS_ERR(h_file)) -+ err = PTR_ERR(h_file); -+ else { -+ if ((flags & __O_TMPFILE) -+ && !(flags & O_EXCL)) { -+ h_inode = file_inode(h_file); -+ spin_lock(&h_inode->i_lock); -+ h_inode->i_state |= I_LINKABLE; -+ spin_unlock(&h_inode->i_lock); -+ } -+ au_set_fbstart(file, bindex); -+ au_set_h_fptr(file, bindex, h_file); -+ au_update_figen(file); -+ /* todo: necessary? */ -+ /* file->f_ra = h_file->f_ra; */ -+ } -+ -+out: -+ return err; -+} -+ -+static int aufs_open_nondir(struct inode *inode __maybe_unused, -+ struct file *file) -+{ -+ int err; -+ struct super_block *sb; -+ struct au_do_open_args args = { -+ .open = au_do_open_nondir -+ }; -+ -+ AuDbg("%pD, f_flags 0x%x, f_mode 0x%x\n", -+ file, vfsub_file_flags(file), file->f_mode); -+ -+ sb = file->f_dentry->d_sb; -+ si_read_lock(sb, AuLock_FLUSH); -+ err = au_do_open(file, &args); -+ si_read_unlock(sb); -+ return err; -+} -+ -+int aufs_release_nondir(struct inode *inode __maybe_unused, struct file *file) -+{ -+ struct au_finfo *finfo; -+ aufs_bindex_t bindex; -+ -+ finfo = au_fi(file); -+ au_sphl_del(&finfo->fi_hlist, &au_sbi(file->f_dentry->d_sb)->si_files); -+ bindex = finfo->fi_btop; -+ if (bindex >= 0) -+ au_set_h_fptr(file, bindex, NULL); -+ -+ au_finfo_fin(file); -+ return 0; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_do_flush_nondir(struct file *file, fl_owner_t id) -+{ -+ int err; -+ struct file *h_file; -+ -+ err = 0; -+ h_file = au_hf_top(file); -+ if (h_file) -+ err = vfsub_flush(h_file, id); -+ return err; -+} -+ -+static int aufs_flush_nondir(struct file *file, fl_owner_t id) -+{ -+ return au_do_flush(file, id, au_do_flush_nondir); -+} -+ -+/* ---------------------------------------------------------------------- */ -+/* -+ * read and write functions acquire [fdi]_rwsem once, but release before -+ * mmap_sem. This is because to stop a race condition between mmap(2). -+ * Releasing these aufs-rwsem should be safe, no branch-mamagement (by keeping -+ * si_rwsem), no harmful copy-up should happen. Actually copy-up may happen in -+ * read functions after [fdi]_rwsem are released, but it should be harmless. -+ */ -+ -+/* Callers should call au_read_post() or fput() in the end */ -+struct file *au_read_pre(struct file *file, int keep_fi) -+{ -+ struct file *h_file; -+ int err; -+ -+ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0); -+ if (!err) { -+ di_read_unlock(file->f_dentry, AuLock_IR); -+ h_file = au_hf_top(file); -+ get_file(h_file); -+ if (!keep_fi) -+ fi_read_unlock(file); -+ } else -+ h_file = ERR_PTR(err); -+ -+ return h_file; -+} -+ -+static void au_read_post(struct inode *inode, struct file *h_file) -+{ -+ /* update without lock, I don't think it a problem */ -+ fsstack_copy_attr_atime(inode, file_inode(h_file)); -+ fput(h_file); -+} -+ -+struct au_write_pre { -+ blkcnt_t blks; -+ aufs_bindex_t bstart; -+}; -+ -+/* -+ * return with iinfo is write-locked -+ * callers should call au_write_post() or iinfo_write_unlock() + fput() in the -+ * end -+ */ -+static struct file *au_write_pre(struct file *file, int do_ready, -+ struct au_write_pre *wpre) -+{ -+ struct file *h_file; -+ struct dentry *dentry; -+ int err; -+ struct au_pin pin; -+ -+ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); -+ h_file = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out; -+ -+ dentry = file->f_dentry; -+ if (do_ready) { -+ err = au_ready_to_write(file, -1, &pin); -+ if (unlikely(err)) { -+ h_file = ERR_PTR(err); -+ di_write_unlock(dentry); -+ goto out_fi; -+ } -+ } -+ -+ di_downgrade_lock(dentry, /*flags*/0); -+ if (wpre) -+ wpre->bstart = au_fbstart(file); -+ h_file = au_hf_top(file); -+ get_file(h_file); -+ if (wpre) -+ wpre->blks = file_inode(h_file)->i_blocks; -+ if (do_ready) -+ au_unpin(&pin); -+ di_read_unlock(dentry, /*flags*/0); -+ -+out_fi: -+ fi_write_unlock(file); -+out: -+ return h_file; -+} -+ -+static void au_write_post(struct inode *inode, struct file *h_file, -+ struct au_write_pre *wpre, ssize_t written) -+{ -+ struct inode *h_inode; -+ -+ au_cpup_attr_timesizes(inode); -+ AuDebugOn(au_ibstart(inode) != wpre->bstart); -+ h_inode = file_inode(h_file); -+ inode->i_mode = h_inode->i_mode; -+ ii_write_unlock(inode); -+ fput(h_file); -+ -+ /* AuDbg("blks %llu, %llu\n", (u64)blks, (u64)h_inode->i_blocks); */ -+ if (written > 0) -+ au_fhsm_wrote(inode->i_sb, wpre->bstart, -+ /*force*/h_inode->i_blocks > wpre->blks); -+} -+ -+static ssize_t aufs_read(struct file *file, char __user *buf, size_t count, -+ loff_t *ppos) -+{ -+ ssize_t err; -+ struct inode *inode; -+ struct file *h_file; -+ struct super_block *sb; -+ -+ inode = file_inode(file); -+ sb = inode->i_sb; -+ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); -+ -+ h_file = au_read_pre(file, /*keep_fi*/0); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ /* filedata may be obsoleted by concurrent copyup, but no problem */ -+ err = vfsub_read_u(h_file, buf, count, ppos); -+ /* todo: necessary? */ -+ /* file->f_ra = h_file->f_ra; */ -+ au_read_post(inode, h_file); -+ -+out: -+ si_read_unlock(sb); -+ return err; -+} -+ -+/* -+ * todo: very ugly -+ * it locks both of i_mutex and si_rwsem for read in safe. -+ * if the plink maintenance mode continues forever (that is the problem), -+ * may loop forever. -+ */ -+static void au_mtx_and_read_lock(struct inode *inode) -+{ -+ int err; -+ struct super_block *sb = inode->i_sb; -+ -+ while (1) { -+ mutex_lock(&inode->i_mutex); -+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ if (!err) -+ break; -+ mutex_unlock(&inode->i_mutex); -+ si_read_lock(sb, AuLock_NOPLMW); -+ si_read_unlock(sb); -+ } -+} -+ -+static ssize_t aufs_write(struct file *file, const char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ ssize_t err; -+ struct au_write_pre wpre; -+ struct inode *inode; -+ struct file *h_file; -+ char __user *buf = (char __user *)ubuf; -+ -+ inode = file_inode(file); -+ au_mtx_and_read_lock(inode); -+ -+ h_file = au_write_pre(file, /*do_ready*/1, &wpre); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ err = vfsub_write_u(h_file, buf, count, ppos); -+ au_write_post(inode, h_file, &wpre, err); -+ -+out: -+ si_read_unlock(inode->i_sb); -+ mutex_unlock(&inode->i_mutex); -+ return err; -+} -+ -+static ssize_t au_do_iter(struct file *h_file, int rw, struct kiocb *kio, -+ struct iov_iter *iov_iter) -+{ -+ ssize_t err; -+ struct file *file; -+ ssize_t (*iter)(struct kiocb *, struct iov_iter *); -+ ssize_t (*aio)(struct kiocb *, const struct iovec *, unsigned long, -+ loff_t); -+ -+ err = security_file_permission(h_file, rw); -+ if (unlikely(err)) -+ goto out; -+ -+ err = -ENOSYS; -+ iter = NULL; -+ aio = NULL; -+ if (rw == MAY_READ) { -+ iter = h_file->f_op->read_iter; -+ aio = h_file->f_op->aio_read; -+ } else if (rw == MAY_WRITE) { -+ iter = h_file->f_op->write_iter; -+ aio = h_file->f_op->aio_write; -+ } -+ -+ file = kio->ki_filp; -+ kio->ki_filp = h_file; -+ if (iter) { -+ lockdep_off(); -+ err = iter(kio, iov_iter); -+ lockdep_on(); -+ } else if (aio) { -+ lockdep_off(); -+ err = aio(kio, iov_iter->iov, iov_iter->nr_segs, kio->ki_pos); -+ lockdep_on(); -+ } else -+ /* currently there is no such fs */ -+ WARN_ON_ONCE(1); -+ kio->ki_filp = file; -+ -+out: -+ return err; -+} -+ -+static ssize_t aufs_read_iter(struct kiocb *kio, struct iov_iter *iov_iter) -+{ -+ ssize_t err; -+ struct file *file, *h_file; -+ struct inode *inode; -+ struct super_block *sb; -+ -+ file = kio->ki_filp; -+ inode = file_inode(file); -+ sb = inode->i_sb; -+ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); -+ -+ h_file = au_read_pre(file, /*keep_fi*/0); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ err = au_do_iter(h_file, MAY_READ, kio, iov_iter); -+ /* todo: necessary? */ -+ /* file->f_ra = h_file->f_ra; */ -+ au_read_post(inode, h_file); -+ -+out: -+ si_read_unlock(sb); -+ return err; -+} -+ -+static ssize_t aufs_write_iter(struct kiocb *kio, struct iov_iter *iov_iter) -+{ -+ ssize_t err; -+ struct au_write_pre wpre; -+ struct inode *inode; -+ struct file *file, *h_file; -+ -+ file = kio->ki_filp; -+ inode = file_inode(file); -+ au_mtx_and_read_lock(inode); -+ -+ h_file = au_write_pre(file, /*do_ready*/1, &wpre); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ err = au_do_iter(h_file, MAY_WRITE, kio, iov_iter); -+ au_write_post(inode, h_file, &wpre, err); -+ -+out: -+ si_read_unlock(inode->i_sb); -+ mutex_unlock(&inode->i_mutex); -+ return err; -+} -+ -+static ssize_t aufs_splice_read(struct file *file, loff_t *ppos, -+ struct pipe_inode_info *pipe, size_t len, -+ unsigned int flags) -+{ -+ ssize_t err; -+ struct file *h_file; -+ struct inode *inode; -+ struct super_block *sb; -+ -+ inode = file_inode(file); -+ sb = inode->i_sb; -+ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); -+ -+ h_file = au_read_pre(file, /*keep_fi*/1); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ if (au_test_loopback_kthread()) { -+ au_warn_loopback(h_file->f_dentry->d_sb); -+ if (file->f_mapping != h_file->f_mapping) { -+ file->f_mapping = h_file->f_mapping; -+ smp_mb(); /* unnecessary? */ -+ } -+ } -+ fi_read_unlock(file); -+ -+ err = vfsub_splice_to(h_file, ppos, pipe, len, flags); -+ /* todo: necessasry? */ -+ /* file->f_ra = h_file->f_ra; */ -+ au_read_post(inode, h_file); -+ -+out: -+ si_read_unlock(sb); -+ return err; -+} -+ -+static ssize_t -+aufs_splice_write(struct pipe_inode_info *pipe, struct file *file, loff_t *ppos, -+ size_t len, unsigned int flags) -+{ -+ ssize_t err; -+ struct au_write_pre wpre; -+ struct inode *inode; -+ struct file *h_file; -+ -+ inode = file_inode(file); -+ au_mtx_and_read_lock(inode); -+ -+ h_file = au_write_pre(file, /*do_ready*/1, &wpre); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ err = vfsub_splice_from(pipe, h_file, ppos, len, flags); -+ au_write_post(inode, h_file, &wpre, err); -+ -+out: -+ si_read_unlock(inode->i_sb); -+ mutex_unlock(&inode->i_mutex); -+ return err; -+} -+ -+static long aufs_fallocate(struct file *file, int mode, loff_t offset, -+ loff_t len) -+{ -+ long err; -+ struct au_write_pre wpre; -+ struct inode *inode; -+ struct file *h_file; -+ -+ inode = file_inode(file); -+ au_mtx_and_read_lock(inode); -+ -+ h_file = au_write_pre(file, /*do_ready*/1, &wpre); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ lockdep_off(); -+ err = do_fallocate(h_file, mode, offset, len); -+ lockdep_on(); -+ au_write_post(inode, h_file, &wpre, /*written*/1); -+ -+out: -+ si_read_unlock(inode->i_sb); -+ mutex_unlock(&inode->i_mutex); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * The locking order around current->mmap_sem. -+ * - in most and regular cases -+ * file I/O syscall -- aufs_read() or something -+ * -- si_rwsem for read -- mmap_sem -+ * (Note that [fdi]i_rwsem are released before mmap_sem). -+ * - in mmap case -+ * mmap(2) -- mmap_sem -- aufs_mmap() -- si_rwsem for read -- [fdi]i_rwsem -+ * This AB-BA order is definitly bad, but is not a problem since "si_rwsem for -+ * read" allows muliple processes to acquire it and [fdi]i_rwsem are not held in -+ * file I/O. Aufs needs to stop lockdep in aufs_mmap() though. -+ * It means that when aufs acquires si_rwsem for write, the process should never -+ * acquire mmap_sem. -+ * -+ * Actually aufs_iterate() holds [fdi]i_rwsem before mmap_sem, but this is not a -+ * problem either since any directory is not able to be mmap-ed. -+ * The similar scenario is applied to aufs_readlink() too. -+ */ -+ -+#if 0 /* stop calling security_file_mmap() */ -+/* cf. linux/include/linux/mman.h: calc_vm_prot_bits() */ -+#define AuConv_VM_PROT(f, b) _calc_vm_trans(f, VM_##b, PROT_##b) -+ -+static unsigned long au_arch_prot_conv(unsigned long flags) -+{ -+ /* currently ppc64 only */ -+#ifdef CONFIG_PPC64 -+ /* cf. linux/arch/powerpc/include/asm/mman.h */ -+ AuDebugOn(arch_calc_vm_prot_bits(-1) != VM_SAO); -+ return AuConv_VM_PROT(flags, SAO); -+#else -+ AuDebugOn(arch_calc_vm_prot_bits(-1)); -+ return 0; -+#endif -+} -+ -+static unsigned long au_prot_conv(unsigned long flags) -+{ -+ return AuConv_VM_PROT(flags, READ) -+ | AuConv_VM_PROT(flags, WRITE) -+ | AuConv_VM_PROT(flags, EXEC) -+ | au_arch_prot_conv(flags); -+} -+ -+/* cf. linux/include/linux/mman.h: calc_vm_flag_bits() */ -+#define AuConv_VM_MAP(f, b) _calc_vm_trans(f, VM_##b, MAP_##b) -+ -+static unsigned long au_flag_conv(unsigned long flags) -+{ -+ return AuConv_VM_MAP(flags, GROWSDOWN) -+ | AuConv_VM_MAP(flags, DENYWRITE) -+ | AuConv_VM_MAP(flags, LOCKED); -+} -+#endif -+ -+static int aufs_mmap(struct file *file, struct vm_area_struct *vma) -+{ -+ int err; -+ const unsigned char wlock -+ = (file->f_mode & FMODE_WRITE) && (vma->vm_flags & VM_SHARED); -+ struct super_block *sb; -+ struct file *h_file; -+ struct inode *inode; -+ -+ AuDbgVmRegion(file, vma); -+ -+ inode = file_inode(file); -+ sb = inode->i_sb; -+ lockdep_off(); -+ si_read_lock(sb, AuLock_NOPLMW); -+ -+ h_file = au_write_pre(file, wlock, /*wpre*/NULL); -+ lockdep_on(); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ err = 0; -+ au_set_mmapped(file); -+ au_vm_file_reset(vma, h_file); -+ /* -+ * we cannot call security_mmap_file() here since it may acquire -+ * mmap_sem or i_mutex. -+ * -+ * err = security_mmap_file(h_file, au_prot_conv(vma->vm_flags), -+ * au_flag_conv(vma->vm_flags)); -+ */ -+ if (!err) -+ err = h_file->f_op->mmap(h_file, vma); -+ if (!err) { -+ au_vm_prfile_set(vma, file); -+ fsstack_copy_attr_atime(inode, file_inode(h_file)); -+ goto out_fput; /* success */ -+ } -+ au_unset_mmapped(file); -+ au_vm_file_reset(vma, file); -+ -+out_fput: -+ lockdep_off(); -+ ii_write_unlock(inode); -+ lockdep_on(); -+ fput(h_file); -+out: -+ lockdep_off(); -+ si_read_unlock(sb); -+ lockdep_on(); -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int aufs_fsync_nondir(struct file *file, loff_t start, loff_t end, -+ int datasync) -+{ -+ int err; -+ struct au_write_pre wpre; -+ struct inode *inode; -+ struct file *h_file; -+ -+ err = 0; /* -EBADF; */ /* posix? */ -+ if (unlikely(!(file->f_mode & FMODE_WRITE))) -+ goto out; -+ -+ inode = file_inode(file); -+ au_mtx_and_read_lock(inode); -+ -+ h_file = au_write_pre(file, /*do_ready*/1, &wpre); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out_unlock; -+ -+ err = vfsub_fsync(h_file, &h_file->f_path, datasync); -+ au_write_post(inode, h_file, &wpre, /*written*/0); -+ -+out_unlock: -+ si_read_unlock(inode->i_sb); -+ mutex_unlock(&inode->i_mutex); -+out: -+ return err; -+} -+ -+/* no one supports this operation, currently */ -+#if 0 -+static int aufs_aio_fsync_nondir(struct kiocb *kio, int datasync) -+{ -+ int err; -+ struct au_write_pre wpre; -+ struct inode *inode; -+ struct file *file, *h_file; -+ -+ err = 0; /* -EBADF; */ /* posix? */ -+ if (unlikely(!(file->f_mode & FMODE_WRITE))) -+ goto out; -+ -+ file = kio->ki_filp; -+ inode = file_inode(file); -+ au_mtx_and_read_lock(inode); -+ -+ h_file = au_write_pre(file, /*do_ready*/1, &wpre); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out_unlock; -+ -+ err = -ENOSYS; -+ h_file = au_hf_top(file); -+ if (h_file->f_op->aio_fsync) { -+ struct mutex *h_mtx; -+ -+ h_mtx = &file_inode(h_file)->i_mutex; -+ if (!is_sync_kiocb(kio)) { -+ get_file(h_file); -+ fput(file); -+ } -+ kio->ki_filp = h_file; -+ err = h_file->f_op->aio_fsync(kio, datasync); -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); -+ if (!err) -+ vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL); -+ /*ignore*/ -+ mutex_unlock(h_mtx); -+ } -+ au_write_post(inode, h_file, &wpre, /*written*/0); -+ -+out_unlock: -+ si_read_unlock(inode->sb); -+ mutex_unlock(&inode->i_mutex); -+out: -+ return err; -+} -+#endif -+ -+static int aufs_fasync(int fd, struct file *file, int flag) -+{ -+ int err; -+ struct file *h_file; -+ struct super_block *sb; -+ -+ sb = file->f_dentry->d_sb; -+ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); -+ -+ h_file = au_read_pre(file, /*keep_fi*/0); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ if (h_file->f_op->fasync) -+ err = h_file->f_op->fasync(fd, h_file, flag); -+ fput(h_file); /* instead of au_read_post() */ -+ -+out: -+ si_read_unlock(sb); -+ return err; -+} -+ -+static int aufs_setfl(struct file *file, unsigned long arg) -+{ -+ int err; -+ struct file *h_file; -+ struct super_block *sb; -+ -+ sb = file->f_dentry->d_sb; -+ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); -+ -+ h_file = au_read_pre(file, /*keep_fi*/0); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ arg |= vfsub_file_flags(file) & FASYNC; /* stop calling h_file->fasync */ -+ err = setfl(/*unused fd*/-1, h_file, arg); -+ fput(h_file); /* instead of au_read_post() */ -+ -+out: -+ si_read_unlock(sb); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* no one supports this operation, currently */ -+#if 0 -+static ssize_t aufs_sendpage(struct file *file, struct page *page, int offset, -+ size_t len, loff_t *pos, int more) -+{ -+} -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+const struct file_operations aufs_file_fop = { -+ .owner = THIS_MODULE, -+ -+ .llseek = default_llseek, -+ -+ .read = aufs_read, -+ .write = aufs_write, -+ .read_iter = aufs_read_iter, -+ .write_iter = aufs_write_iter, -+ -+#ifdef CONFIG_AUFS_POLL -+ .poll = aufs_poll, -+#endif -+ .unlocked_ioctl = aufs_ioctl_nondir, -+#ifdef CONFIG_COMPAT -+ .compat_ioctl = aufs_compat_ioctl_nondir, -+#endif -+ .mmap = aufs_mmap, -+ .open = aufs_open_nondir, -+ .flush = aufs_flush_nondir, -+ .release = aufs_release_nondir, -+ .fsync = aufs_fsync_nondir, -+ /* .aio_fsync = aufs_aio_fsync_nondir, */ -+ .fasync = aufs_fasync, -+ /* .sendpage = aufs_sendpage, */ -+ .setfl = aufs_setfl, -+ .splice_write = aufs_splice_write, -+ .splice_read = aufs_splice_read, -+#if 0 -+ .aio_splice_write = aufs_aio_splice_write, -+ .aio_splice_read = aufs_aio_splice_read, -+#endif -+ .fallocate = aufs_fallocate -+}; -diff --git a/fs/aufs/fhsm.c b/fs/aufs/fhsm.c -new file mode 100644 -index 0000000..5b3ad74 ---- /dev/null -+++ b/fs/aufs/fhsm.c -@@ -0,0 +1,426 @@ -+/* -+ * Copyright (C) 2011-2016 Junjiro R. Okajima -+ * -+ * This program, aufs is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -+ */ -+ -+/* -+ * File-based Hierarchy Storage Management -+ */ -+ -+#include -+#include -+#include -+#include -+#include "aufs.h" -+ -+static aufs_bindex_t au_fhsm_bottom(struct super_block *sb) -+{ -+ struct au_sbinfo *sbinfo; -+ struct au_fhsm *fhsm; -+ -+ SiMustAnyLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ fhsm = &sbinfo->si_fhsm; -+ AuDebugOn(!fhsm); -+ return fhsm->fhsm_bottom; -+} -+ -+void au_fhsm_set_bottom(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ struct au_sbinfo *sbinfo; -+ struct au_fhsm *fhsm; -+ -+ SiMustWriteLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ fhsm = &sbinfo->si_fhsm; -+ AuDebugOn(!fhsm); -+ fhsm->fhsm_bottom = bindex; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_fhsm_test_jiffy(struct au_sbinfo *sbinfo, struct au_branch *br) -+{ -+ struct au_br_fhsm *bf; -+ -+ bf = br->br_fhsm; -+ MtxMustLock(&bf->bf_lock); -+ -+ return !bf->bf_readable -+ || time_after(jiffies, -+ bf->bf_jiffy + sbinfo->si_fhsm.fhsm_expire); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void au_fhsm_notify(struct super_block *sb, int val) -+{ -+ struct au_sbinfo *sbinfo; -+ struct au_fhsm *fhsm; -+ -+ SiMustAnyLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ fhsm = &sbinfo->si_fhsm; -+ if (au_fhsm_pid(fhsm) -+ && atomic_read(&fhsm->fhsm_readable) != -1) { -+ atomic_set(&fhsm->fhsm_readable, val); -+ if (val) -+ wake_up(&fhsm->fhsm_wqh); -+ } -+} -+ -+static int au_fhsm_stfs(struct super_block *sb, aufs_bindex_t bindex, -+ struct aufs_stfs *rstfs, int do_lock, int do_notify) -+{ -+ int err; -+ struct au_branch *br; -+ struct au_br_fhsm *bf; -+ -+ br = au_sbr(sb, bindex); -+ AuDebugOn(au_br_rdonly(br)); -+ bf = br->br_fhsm; -+ AuDebugOn(!bf); -+ -+ if (do_lock) -+ mutex_lock(&bf->bf_lock); -+ else -+ MtxMustLock(&bf->bf_lock); -+ -+ /* sb->s_root for NFS is unreliable */ -+ err = au_br_stfs(br, &bf->bf_stfs); -+ if (unlikely(err)) { -+ AuErr1("FHSM failed (%d), b%d, ignored.\n", bindex, err); -+ goto out; -+ } -+ -+ bf->bf_jiffy = jiffies; -+ bf->bf_readable = 1; -+ if (do_notify) -+ au_fhsm_notify(sb, /*val*/1); -+ if (rstfs) -+ *rstfs = bf->bf_stfs; -+ -+out: -+ if (do_lock) -+ mutex_unlock(&bf->bf_lock); -+ au_fhsm_notify(sb, /*val*/1); -+ -+ return err; -+} -+ -+void au_fhsm_wrote(struct super_block *sb, aufs_bindex_t bindex, int force) -+{ -+ int err; -+ struct au_sbinfo *sbinfo; -+ struct au_fhsm *fhsm; -+ struct au_branch *br; -+ struct au_br_fhsm *bf; -+ -+ AuDbg("b%d, force %d\n", bindex, force); -+ SiMustAnyLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ fhsm = &sbinfo->si_fhsm; -+ if (!au_ftest_si(sbinfo, FHSM) -+ || fhsm->fhsm_bottom == bindex) -+ return; -+ -+ br = au_sbr(sb, bindex); -+ bf = br->br_fhsm; -+ AuDebugOn(!bf); -+ mutex_lock(&bf->bf_lock); -+ if (force -+ || au_fhsm_pid(fhsm) -+ || au_fhsm_test_jiffy(sbinfo, br)) -+ err = au_fhsm_stfs(sb, bindex, /*rstfs*/NULL, /*do_lock*/0, -+ /*do_notify*/1); -+ mutex_unlock(&bf->bf_lock); -+} -+ -+void au_fhsm_wrote_all(struct super_block *sb, int force) -+{ -+ aufs_bindex_t bindex, bend; -+ struct au_branch *br; -+ -+ /* exclude the bottom */ -+ bend = au_fhsm_bottom(sb); -+ for (bindex = 0; bindex < bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ if (au_br_fhsm(br->br_perm)) -+ au_fhsm_wrote(sb, bindex, force); -+ } -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static unsigned int au_fhsm_poll(struct file *file, -+ struct poll_table_struct *wait) -+{ -+ unsigned int mask; -+ struct au_sbinfo *sbinfo; -+ struct au_fhsm *fhsm; -+ -+ mask = 0; -+ sbinfo = file->private_data; -+ fhsm = &sbinfo->si_fhsm; -+ poll_wait(file, &fhsm->fhsm_wqh, wait); -+ if (atomic_read(&fhsm->fhsm_readable)) -+ mask = POLLIN /* | POLLRDNORM */; -+ -+ AuTraceErr((int)mask); -+ return mask; -+} -+ -+static int au_fhsm_do_read_one(struct aufs_stbr __user *stbr, -+ struct aufs_stfs *stfs, __s16 brid) -+{ -+ int err; -+ -+ err = copy_to_user(&stbr->stfs, stfs, sizeof(*stfs)); -+ if (!err) -+ err = __put_user(brid, &stbr->brid); -+ if (unlikely(err)) -+ err = -EFAULT; -+ -+ return err; -+} -+ -+static ssize_t au_fhsm_do_read(struct super_block *sb, -+ struct aufs_stbr __user *stbr, size_t count) -+{ -+ ssize_t err; -+ int nstbr; -+ aufs_bindex_t bindex, bend; -+ struct au_branch *br; -+ struct au_br_fhsm *bf; -+ -+ /* except the bottom branch */ -+ err = 0; -+ nstbr = 0; -+ bend = au_fhsm_bottom(sb); -+ for (bindex = 0; !err && bindex < bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ if (!au_br_fhsm(br->br_perm)) -+ continue; -+ -+ bf = br->br_fhsm; -+ mutex_lock(&bf->bf_lock); -+ if (bf->bf_readable) { -+ err = -EFAULT; -+ if (count >= sizeof(*stbr)) -+ err = au_fhsm_do_read_one(stbr++, &bf->bf_stfs, -+ br->br_id); -+ if (!err) { -+ bf->bf_readable = 0; -+ count -= sizeof(*stbr); -+ nstbr++; -+ } -+ } -+ mutex_unlock(&bf->bf_lock); -+ } -+ if (!err) -+ err = sizeof(*stbr) * nstbr; -+ -+ return err; -+} -+ -+static ssize_t au_fhsm_read(struct file *file, char __user *buf, size_t count, -+ loff_t *pos) -+{ -+ ssize_t err; -+ int readable; -+ aufs_bindex_t nfhsm, bindex, bend; -+ struct au_sbinfo *sbinfo; -+ struct au_fhsm *fhsm; -+ struct au_branch *br; -+ struct super_block *sb; -+ -+ err = 0; -+ sbinfo = file->private_data; -+ fhsm = &sbinfo->si_fhsm; -+need_data: -+ spin_lock_irq(&fhsm->fhsm_wqh.lock); -+ if (!atomic_read(&fhsm->fhsm_readable)) { -+ if (vfsub_file_flags(file) & O_NONBLOCK) -+ err = -EAGAIN; -+ else -+ err = wait_event_interruptible_locked_irq -+ (fhsm->fhsm_wqh, -+ atomic_read(&fhsm->fhsm_readable)); -+ } -+ spin_unlock_irq(&fhsm->fhsm_wqh.lock); -+ if (unlikely(err)) -+ goto out; -+ -+ /* sb may already be dead */ -+ au_rw_read_lock(&sbinfo->si_rwsem); -+ readable = atomic_read(&fhsm->fhsm_readable); -+ if (readable > 0) { -+ sb = sbinfo->si_sb; -+ AuDebugOn(!sb); -+ /* exclude the bottom branch */ -+ nfhsm = 0; -+ bend = au_fhsm_bottom(sb); -+ for (bindex = 0; bindex < bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ if (au_br_fhsm(br->br_perm)) -+ nfhsm++; -+ } -+ err = -EMSGSIZE; -+ if (nfhsm * sizeof(struct aufs_stbr) <= count) { -+ atomic_set(&fhsm->fhsm_readable, 0); -+ err = au_fhsm_do_read(sbinfo->si_sb, (void __user *)buf, -+ count); -+ } -+ } -+ au_rw_read_unlock(&sbinfo->si_rwsem); -+ if (!readable) -+ goto need_data; -+ -+out: -+ return err; -+} -+ -+static int au_fhsm_release(struct inode *inode, struct file *file) -+{ -+ struct au_sbinfo *sbinfo; -+ struct au_fhsm *fhsm; -+ -+ /* sb may already be dead */ -+ sbinfo = file->private_data; -+ fhsm = &sbinfo->si_fhsm; -+ spin_lock(&fhsm->fhsm_spin); -+ fhsm->fhsm_pid = 0; -+ spin_unlock(&fhsm->fhsm_spin); -+ kobject_put(&sbinfo->si_kobj); -+ -+ return 0; -+} -+ -+static const struct file_operations au_fhsm_fops = { -+ .owner = THIS_MODULE, -+ .llseek = noop_llseek, -+ .read = au_fhsm_read, -+ .poll = au_fhsm_poll, -+ .release = au_fhsm_release -+}; -+ -+int au_fhsm_fd(struct super_block *sb, int oflags) -+{ -+ int err, fd; -+ struct au_sbinfo *sbinfo; -+ struct au_fhsm *fhsm; -+ -+ err = -EPERM; -+ if (unlikely(!capable(CAP_SYS_ADMIN))) -+ goto out; -+ -+ err = -EINVAL; -+ if (unlikely(oflags & ~(O_CLOEXEC | O_NONBLOCK))) -+ goto out; -+ -+ err = 0; -+ sbinfo = au_sbi(sb); -+ fhsm = &sbinfo->si_fhsm; -+ spin_lock(&fhsm->fhsm_spin); -+ if (!fhsm->fhsm_pid) -+ fhsm->fhsm_pid = current->pid; -+ else -+ err = -EBUSY; -+ spin_unlock(&fhsm->fhsm_spin); -+ if (unlikely(err)) -+ goto out; -+ -+ oflags |= O_RDONLY; -+ /* oflags |= FMODE_NONOTIFY; */ -+ fd = anon_inode_getfd("[aufs_fhsm]", &au_fhsm_fops, sbinfo, oflags); -+ err = fd; -+ if (unlikely(fd < 0)) -+ goto out_pid; -+ -+ /* succeed reglardless 'fhsm' status */ -+ kobject_get(&sbinfo->si_kobj); -+ si_noflush_read_lock(sb); -+ if (au_ftest_si(sbinfo, FHSM)) -+ au_fhsm_wrote_all(sb, /*force*/0); -+ si_read_unlock(sb); -+ goto out; /* success */ -+ -+out_pid: -+ spin_lock(&fhsm->fhsm_spin); -+ fhsm->fhsm_pid = 0; -+ spin_unlock(&fhsm->fhsm_spin); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int au_fhsm_br_alloc(struct au_branch *br) -+{ -+ int err; -+ -+ err = 0; -+ br->br_fhsm = kmalloc(sizeof(*br->br_fhsm), GFP_NOFS); -+ if (br->br_fhsm) -+ au_br_fhsm_init(br->br_fhsm); -+ else -+ err = -ENOMEM; -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void au_fhsm_fin(struct super_block *sb) -+{ -+ au_fhsm_notify(sb, /*val*/-1); -+} -+ -+void au_fhsm_init(struct au_sbinfo *sbinfo) -+{ -+ struct au_fhsm *fhsm; -+ -+ fhsm = &sbinfo->si_fhsm; -+ spin_lock_init(&fhsm->fhsm_spin); -+ init_waitqueue_head(&fhsm->fhsm_wqh); -+ atomic_set(&fhsm->fhsm_readable, 0); -+ fhsm->fhsm_expire -+ = msecs_to_jiffies(AUFS_FHSM_CACHE_DEF_SEC * MSEC_PER_SEC); -+ fhsm->fhsm_bottom = -1; -+} -+ -+void au_fhsm_set(struct au_sbinfo *sbinfo, unsigned int sec) -+{ -+ sbinfo->si_fhsm.fhsm_expire -+ = msecs_to_jiffies(sec * MSEC_PER_SEC); -+} -+ -+void au_fhsm_show(struct seq_file *seq, struct au_sbinfo *sbinfo) -+{ -+ unsigned int u; -+ -+ if (!au_ftest_si(sbinfo, FHSM)) -+ return; -+ -+ u = jiffies_to_msecs(sbinfo->si_fhsm.fhsm_expire) / MSEC_PER_SEC; -+ if (u != AUFS_FHSM_CACHE_DEF_SEC) -+ seq_printf(seq, ",fhsm_sec=%u", u); -+} -diff --git a/fs/aufs/file.c b/fs/aufs/file.c -new file mode 100644 -index 0000000..12c7620 ---- /dev/null -+++ b/fs/aufs/file.c -@@ -0,0 +1,857 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * handling file/dir, and address_space operation -+ */ -+ -+#ifdef CONFIG_AUFS_DEBUG -+#include -+#endif -+#include -+#include "aufs.h" -+ -+/* drop flags for writing */ -+unsigned int au_file_roflags(unsigned int flags) -+{ -+ flags &= ~(O_WRONLY | O_RDWR | O_APPEND | O_CREAT | O_TRUNC); -+ flags |= O_RDONLY | O_NOATIME; -+ return flags; -+} -+ -+/* common functions to regular file and dir */ -+struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags, -+ struct file *file, int force_wr) -+{ -+ struct file *h_file; -+ struct dentry *h_dentry; -+ struct inode *h_inode; -+ struct super_block *sb; -+ struct au_branch *br; -+ struct path h_path; -+ int err; -+ -+ /* a race condition can happen between open and unlink/rmdir */ -+ h_file = ERR_PTR(-ENOENT); -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (au_test_nfsd() && !h_dentry) -+ goto out; -+ h_inode = h_dentry->d_inode; -+ if (au_test_nfsd() && !h_inode) -+ goto out; -+ spin_lock(&h_dentry->d_lock); -+ err = (!d_unhashed(dentry) && d_unlinked(h_dentry)) -+ || !h_inode -+ /* || !dentry->d_inode->i_nlink */ -+ ; -+ spin_unlock(&h_dentry->d_lock); -+ if (unlikely(err)) -+ goto out; -+ -+ sb = dentry->d_sb; -+ br = au_sbr(sb, bindex); -+ err = au_br_test_oflag(flags, br); -+ h_file = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out; -+ -+ /* drop flags for writing */ -+ if (au_test_ro(sb, bindex, dentry->d_inode)) { -+ if (force_wr && !(flags & O_WRONLY)) -+ force_wr = 0; -+ flags = au_file_roflags(flags); -+ if (force_wr) { -+ h_file = ERR_PTR(-EROFS); -+ flags = au_file_roflags(flags); -+ if (unlikely(vfsub_native_ro(h_inode) -+ || IS_APPEND(h_inode))) -+ goto out; -+ flags &= ~O_ACCMODE; -+ flags |= O_WRONLY; -+ } -+ } -+ flags &= ~O_CREAT; -+ atomic_inc(&br->br_count); -+ h_path.dentry = h_dentry; -+ h_path.mnt = au_br_mnt(br); -+ h_file = vfsub_dentry_open(&h_path, flags); -+ if (IS_ERR(h_file)) -+ goto out_br; -+ -+ if (flags & __FMODE_EXEC) { -+ err = deny_write_access(h_file); -+ if (unlikely(err)) { -+ fput(h_file); -+ h_file = ERR_PTR(err); -+ goto out_br; -+ } -+ } -+ fsnotify_open(h_file); -+ goto out; /* success */ -+ -+out_br: -+ atomic_dec(&br->br_count); -+out: -+ return h_file; -+} -+ -+static int au_cmoo(struct dentry *dentry) -+{ -+ int err, cmoo; -+ unsigned int udba; -+ struct path h_path; -+ struct au_pin pin; -+ struct au_cp_generic cpg = { -+ .dentry = dentry, -+ .bdst = -1, -+ .bsrc = -1, -+ .len = -1, -+ .pin = &pin, -+ .flags = AuCpup_DTIME | AuCpup_HOPEN -+ }; -+ struct inode *inode, *delegated; -+ struct super_block *sb; -+ struct au_sbinfo *sbinfo; -+ struct au_fhsm *fhsm; -+ pid_t pid; -+ struct au_branch *br; -+ struct dentry *parent; -+ struct au_hinode *hdir; -+ -+ DiMustWriteLock(dentry); -+ inode = dentry->d_inode; -+ IiMustWriteLock(inode); -+ -+ err = 0; -+ if (IS_ROOT(dentry)) -+ goto out; -+ cpg.bsrc = au_dbstart(dentry); -+ if (!cpg.bsrc) -+ goto out; -+ -+ sb = dentry->d_sb; -+ sbinfo = au_sbi(sb); -+ fhsm = &sbinfo->si_fhsm; -+ pid = au_fhsm_pid(fhsm); -+ if (pid -+ && (current->pid == pid -+ || current->real_parent->pid == pid)) -+ goto out; -+ -+ br = au_sbr(sb, cpg.bsrc); -+ cmoo = au_br_cmoo(br->br_perm); -+ if (!cmoo) -+ goto out; -+ if (!S_ISREG(inode->i_mode)) -+ cmoo &= AuBrAttr_COO_ALL; -+ if (!cmoo) -+ goto out; -+ -+ parent = dget_parent(dentry); -+ di_write_lock_parent(parent); -+ err = au_wbr_do_copyup_bu(dentry, cpg.bsrc - 1); -+ cpg.bdst = err; -+ if (unlikely(err < 0)) { -+ err = 0; /* there is no upper writable branch */ -+ goto out_dgrade; -+ } -+ AuDbg("bsrc %d, bdst %d\n", cpg.bsrc, cpg.bdst); -+ -+ /* do not respect the coo attrib for the target branch */ -+ err = au_cpup_dirs(dentry, cpg.bdst); -+ if (unlikely(err)) -+ goto out_dgrade; -+ -+ di_downgrade_lock(parent, AuLock_IR); -+ udba = au_opt_udba(sb); -+ err = au_pin(&pin, dentry, cpg.bdst, udba, -+ AuPin_DI_LOCKED | AuPin_MNT_WRITE); -+ if (unlikely(err)) -+ goto out_parent; -+ -+ err = au_sio_cpup_simple(&cpg); -+ au_unpin(&pin); -+ if (unlikely(err)) -+ goto out_parent; -+ if (!(cmoo & AuBrWAttr_MOO)) -+ goto out_parent; /* success */ -+ -+ err = au_pin(&pin, dentry, cpg.bsrc, udba, -+ AuPin_DI_LOCKED | AuPin_MNT_WRITE); -+ if (unlikely(err)) -+ goto out_parent; -+ -+ h_path.mnt = au_br_mnt(br); -+ h_path.dentry = au_h_dptr(dentry, cpg.bsrc); -+ hdir = au_hi(parent->d_inode, cpg.bsrc); -+ delegated = NULL; -+ err = vfsub_unlink(hdir->hi_inode, &h_path, &delegated, /*force*/1); -+ au_unpin(&pin); -+ /* todo: keep h_dentry or not? */ -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal unlink\n"); -+ iput(delegated); -+ } -+ if (unlikely(err)) { -+ pr_err("unlink %pd after coo failed (%d), ignored\n", -+ dentry, err); -+ err = 0; -+ } -+ goto out_parent; /* success */ -+ -+out_dgrade: -+ di_downgrade_lock(parent, AuLock_IR); -+out_parent: -+ di_read_unlock(parent, AuLock_IR); -+ dput(parent); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+int au_do_open(struct file *file, struct au_do_open_args *args) -+{ -+ int err, no_lock = args->no_lock; -+ struct dentry *dentry; -+ struct au_finfo *finfo; -+ -+ if (!no_lock) -+ err = au_finfo_init(file, args->fidir); -+ else { -+ lockdep_off(); -+ err = au_finfo_init(file, args->fidir); -+ lockdep_on(); -+ } -+ if (unlikely(err)) -+ goto out; -+ -+ dentry = file->f_dentry; -+ AuDebugOn(IS_ERR_OR_NULL(dentry)); -+ if (!no_lock) { -+ di_write_lock_child(dentry); -+ err = au_cmoo(dentry); -+ di_downgrade_lock(dentry, AuLock_IR); -+ if (!err) -+ err = args->open(file, vfsub_file_flags(file), NULL); -+ di_read_unlock(dentry, AuLock_IR); -+ } else { -+ err = au_cmoo(dentry); -+ if (!err) -+ err = args->open(file, vfsub_file_flags(file), -+ args->h_file); -+ if (!err && au_fbstart(file) != au_dbstart(dentry)) -+ /* -+ * cmoo happens after h_file was opened. -+ * need to refresh file later. -+ */ -+ atomic_dec(&au_fi(file)->fi_generation); -+ } -+ -+ finfo = au_fi(file); -+ if (!err) { -+ finfo->fi_file = file; -+ au_sphl_add(&finfo->fi_hlist, -+ &au_sbi(file->f_dentry->d_sb)->si_files); -+ } -+ if (!no_lock) -+ fi_write_unlock(file); -+ else { -+ lockdep_off(); -+ fi_write_unlock(file); -+ lockdep_on(); -+ } -+ if (unlikely(err)) { -+ finfo->fi_hdir = NULL; -+ au_finfo_fin(file); -+ } -+ -+out: -+ return err; -+} -+ -+int au_reopen_nondir(struct file *file) -+{ -+ int err; -+ aufs_bindex_t bstart; -+ struct dentry *dentry; -+ struct file *h_file, *h_file_tmp; -+ -+ dentry = file->f_dentry; -+ bstart = au_dbstart(dentry); -+ h_file_tmp = NULL; -+ if (au_fbstart(file) == bstart) { -+ h_file = au_hf_top(file); -+ if (file->f_mode == h_file->f_mode) -+ return 0; /* success */ -+ h_file_tmp = h_file; -+ get_file(h_file_tmp); -+ au_set_h_fptr(file, bstart, NULL); -+ } -+ AuDebugOn(au_fi(file)->fi_hdir); -+ /* -+ * it can happen -+ * file exists on both of rw and ro -+ * open --> dbstart and fbstart are both 0 -+ * prepend a branch as rw, "rw" become ro -+ * remove rw/file -+ * delete the top branch, "rw" becomes rw again -+ * --> dbstart is 1, fbstart is still 0 -+ * write --> fbstart is 0 but dbstart is 1 -+ */ -+ /* AuDebugOn(au_fbstart(file) < bstart); */ -+ -+ h_file = au_h_open(dentry, bstart, vfsub_file_flags(file) & ~O_TRUNC, -+ file, /*force_wr*/0); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) { -+ if (h_file_tmp) { -+ atomic_inc(&au_sbr(dentry->d_sb, bstart)->br_count); -+ au_set_h_fptr(file, bstart, h_file_tmp); -+ h_file_tmp = NULL; -+ } -+ goto out; /* todo: close all? */ -+ } -+ -+ err = 0; -+ au_set_fbstart(file, bstart); -+ au_set_h_fptr(file, bstart, h_file); -+ au_update_figen(file); -+ /* todo: necessary? */ -+ /* file->f_ra = h_file->f_ra; */ -+ -+out: -+ if (h_file_tmp) -+ fput(h_file_tmp); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_reopen_wh(struct file *file, aufs_bindex_t btgt, -+ struct dentry *hi_wh) -+{ -+ int err; -+ aufs_bindex_t bstart; -+ struct au_dinfo *dinfo; -+ struct dentry *h_dentry; -+ struct au_hdentry *hdp; -+ -+ dinfo = au_di(file->f_dentry); -+ AuRwMustWriteLock(&dinfo->di_rwsem); -+ -+ bstart = dinfo->di_bstart; -+ dinfo->di_bstart = btgt; -+ hdp = dinfo->di_hdentry; -+ h_dentry = hdp[0 + btgt].hd_dentry; -+ hdp[0 + btgt].hd_dentry = hi_wh; -+ err = au_reopen_nondir(file); -+ hdp[0 + btgt].hd_dentry = h_dentry; -+ dinfo->di_bstart = bstart; -+ -+ return err; -+} -+ -+static int au_ready_to_write_wh(struct file *file, loff_t len, -+ aufs_bindex_t bcpup, struct au_pin *pin) -+{ -+ int err; -+ struct inode *inode, *h_inode; -+ struct dentry *h_dentry, *hi_wh; -+ struct au_cp_generic cpg = { -+ .dentry = file->f_dentry, -+ .bdst = bcpup, -+ .bsrc = -1, -+ .len = len, -+ .pin = pin -+ }; -+ -+ au_update_dbstart(cpg.dentry); -+ inode = cpg.dentry->d_inode; -+ h_inode = NULL; -+ if (au_dbstart(cpg.dentry) <= bcpup -+ && au_dbend(cpg.dentry) >= bcpup) { -+ h_dentry = au_h_dptr(cpg.dentry, bcpup); -+ if (h_dentry) -+ h_inode = h_dentry->d_inode; -+ } -+ hi_wh = au_hi_wh(inode, bcpup); -+ if (!hi_wh && !h_inode) -+ err = au_sio_cpup_wh(&cpg, file); -+ else -+ /* already copied-up after unlink */ -+ err = au_reopen_wh(file, bcpup, hi_wh); -+ -+ if (!err -+ && (inode->i_nlink > 1 -+ || (inode->i_state & I_LINKABLE)) -+ && au_opt_test(au_mntflags(cpg.dentry->d_sb), PLINK)) -+ au_plink_append(inode, bcpup, au_h_dptr(cpg.dentry, bcpup)); -+ -+ return err; -+} -+ -+/* -+ * prepare the @file for writing. -+ */ -+int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin) -+{ -+ int err; -+ aufs_bindex_t dbstart; -+ struct dentry *parent; -+ struct inode *inode; -+ struct super_block *sb; -+ struct file *h_file; -+ struct au_cp_generic cpg = { -+ .dentry = file->f_dentry, -+ .bdst = -1, -+ .bsrc = -1, -+ .len = len, -+ .pin = pin, -+ .flags = AuCpup_DTIME -+ }; -+ -+ sb = cpg.dentry->d_sb; -+ inode = cpg.dentry->d_inode; -+ cpg.bsrc = au_fbstart(file); -+ err = au_test_ro(sb, cpg.bsrc, inode); -+ if (!err && (au_hf_top(file)->f_mode & FMODE_WRITE)) { -+ err = au_pin(pin, cpg.dentry, cpg.bsrc, AuOpt_UDBA_NONE, -+ /*flags*/0); -+ goto out; -+ } -+ -+ /* need to cpup or reopen */ -+ parent = dget_parent(cpg.dentry); -+ di_write_lock_parent(parent); -+ err = AuWbrCopyup(au_sbi(sb), cpg.dentry); -+ cpg.bdst = err; -+ if (unlikely(err < 0)) -+ goto out_dgrade; -+ err = 0; -+ -+ if (!d_unhashed(cpg.dentry) && !au_h_dptr(parent, cpg.bdst)) { -+ err = au_cpup_dirs(cpg.dentry, cpg.bdst); -+ if (unlikely(err)) -+ goto out_dgrade; -+ } -+ -+ err = au_pin(pin, cpg.dentry, cpg.bdst, AuOpt_UDBA_NONE, -+ AuPin_DI_LOCKED | AuPin_MNT_WRITE); -+ if (unlikely(err)) -+ goto out_dgrade; -+ -+ dbstart = au_dbstart(cpg.dentry); -+ if (dbstart <= cpg.bdst) -+ cpg.bsrc = cpg.bdst; -+ -+ if (dbstart <= cpg.bdst /* just reopen */ -+ || !d_unhashed(cpg.dentry) /* copyup and reopen */ -+ ) { -+ h_file = au_h_open_pre(cpg.dentry, cpg.bsrc, /*force_wr*/0); -+ if (IS_ERR(h_file)) -+ err = PTR_ERR(h_file); -+ else { -+ di_downgrade_lock(parent, AuLock_IR); -+ if (dbstart > cpg.bdst) -+ err = au_sio_cpup_simple(&cpg); -+ if (!err) -+ err = au_reopen_nondir(file); -+ au_h_open_post(cpg.dentry, cpg.bsrc, h_file); -+ } -+ } else { /* copyup as wh and reopen */ -+ /* -+ * since writable hfsplus branch is not supported, -+ * h_open_pre/post() are unnecessary. -+ */ -+ err = au_ready_to_write_wh(file, len, cpg.bdst, pin); -+ di_downgrade_lock(parent, AuLock_IR); -+ } -+ -+ if (!err) { -+ au_pin_set_parent_lflag(pin, /*lflag*/0); -+ goto out_dput; /* success */ -+ } -+ au_unpin(pin); -+ goto out_unlock; -+ -+out_dgrade: -+ di_downgrade_lock(parent, AuLock_IR); -+out_unlock: -+ di_read_unlock(parent, AuLock_IR); -+out_dput: -+ dput(parent); -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int au_do_flush(struct file *file, fl_owner_t id, -+ int (*flush)(struct file *file, fl_owner_t id)) -+{ -+ int err; -+ struct super_block *sb; -+ struct inode *inode; -+ -+ inode = file_inode(file); -+ sb = inode->i_sb; -+ si_noflush_read_lock(sb); -+ fi_read_lock(file); -+ ii_read_lock_child(inode); -+ -+ err = flush(file, id); -+ au_cpup_attr_timesizes(inode); -+ -+ ii_read_unlock(inode); -+ fi_read_unlock(file); -+ si_read_unlock(sb); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_file_refresh_by_inode(struct file *file, int *need_reopen) -+{ -+ int err; -+ struct au_pin pin; -+ struct au_finfo *finfo; -+ struct dentry *parent, *hi_wh; -+ struct inode *inode; -+ struct super_block *sb; -+ struct au_cp_generic cpg = { -+ .dentry = file->f_dentry, -+ .bdst = -1, -+ .bsrc = -1, -+ .len = -1, -+ .pin = &pin, -+ .flags = AuCpup_DTIME -+ }; -+ -+ FiMustWriteLock(file); -+ -+ err = 0; -+ finfo = au_fi(file); -+ sb = cpg.dentry->d_sb; -+ inode = cpg.dentry->d_inode; -+ cpg.bdst = au_ibstart(inode); -+ if (cpg.bdst == finfo->fi_btop || IS_ROOT(cpg.dentry)) -+ goto out; -+ -+ parent = dget_parent(cpg.dentry); -+ if (au_test_ro(sb, cpg.bdst, inode)) { -+ di_read_lock_parent(parent, !AuLock_IR); -+ err = AuWbrCopyup(au_sbi(sb), cpg.dentry); -+ cpg.bdst = err; -+ di_read_unlock(parent, !AuLock_IR); -+ if (unlikely(err < 0)) -+ goto out_parent; -+ err = 0; -+ } -+ -+ di_read_lock_parent(parent, AuLock_IR); -+ hi_wh = au_hi_wh(inode, cpg.bdst); -+ if (!S_ISDIR(inode->i_mode) -+ && au_opt_test(au_mntflags(sb), PLINK) -+ && au_plink_test(inode) -+ && !d_unhashed(cpg.dentry) -+ && cpg.bdst < au_dbstart(cpg.dentry)) { -+ err = au_test_and_cpup_dirs(cpg.dentry, cpg.bdst); -+ if (unlikely(err)) -+ goto out_unlock; -+ -+ /* always superio. */ -+ err = au_pin(&pin, cpg.dentry, cpg.bdst, AuOpt_UDBA_NONE, -+ AuPin_DI_LOCKED | AuPin_MNT_WRITE); -+ if (!err) { -+ err = au_sio_cpup_simple(&cpg); -+ au_unpin(&pin); -+ } -+ } else if (hi_wh) { -+ /* already copied-up after unlink */ -+ err = au_reopen_wh(file, cpg.bdst, hi_wh); -+ *need_reopen = 0; -+ } -+ -+out_unlock: -+ di_read_unlock(parent, AuLock_IR); -+out_parent: -+ dput(parent); -+out: -+ return err; -+} -+ -+static void au_do_refresh_dir(struct file *file) -+{ -+ aufs_bindex_t bindex, bend, new_bindex, brid; -+ struct au_hfile *p, tmp, *q; -+ struct au_finfo *finfo; -+ struct super_block *sb; -+ struct au_fidir *fidir; -+ -+ FiMustWriteLock(file); -+ -+ sb = file->f_dentry->d_sb; -+ finfo = au_fi(file); -+ fidir = finfo->fi_hdir; -+ AuDebugOn(!fidir); -+ p = fidir->fd_hfile + finfo->fi_btop; -+ brid = p->hf_br->br_id; -+ bend = fidir->fd_bbot; -+ for (bindex = finfo->fi_btop; bindex <= bend; bindex++, p++) { -+ if (!p->hf_file) -+ continue; -+ -+ new_bindex = au_br_index(sb, p->hf_br->br_id); -+ if (new_bindex == bindex) -+ continue; -+ if (new_bindex < 0) { -+ au_set_h_fptr(file, bindex, NULL); -+ continue; -+ } -+ -+ /* swap two lower inode, and loop again */ -+ q = fidir->fd_hfile + new_bindex; -+ tmp = *q; -+ *q = *p; -+ *p = tmp; -+ if (tmp.hf_file) { -+ bindex--; -+ p--; -+ } -+ } -+ -+ p = fidir->fd_hfile; -+ if (!au_test_mmapped(file) && !d_unlinked(file->f_dentry)) { -+ bend = au_sbend(sb); -+ for (finfo->fi_btop = 0; finfo->fi_btop <= bend; -+ finfo->fi_btop++, p++) -+ if (p->hf_file) { -+ if (file_inode(p->hf_file)) -+ break; -+ au_hfput(p, file); -+ } -+ } else { -+ bend = au_br_index(sb, brid); -+ for (finfo->fi_btop = 0; finfo->fi_btop < bend; -+ finfo->fi_btop++, p++) -+ if (p->hf_file) -+ au_hfput(p, file); -+ bend = au_sbend(sb); -+ } -+ -+ p = fidir->fd_hfile + bend; -+ for (fidir->fd_bbot = bend; fidir->fd_bbot >= finfo->fi_btop; -+ fidir->fd_bbot--, p--) -+ if (p->hf_file) { -+ if (file_inode(p->hf_file)) -+ break; -+ au_hfput(p, file); -+ } -+ AuDebugOn(fidir->fd_bbot < finfo->fi_btop); -+} -+ -+/* -+ * after branch manipulating, refresh the file. -+ */ -+static int refresh_file(struct file *file, int (*reopen)(struct file *file)) -+{ -+ int err, need_reopen; -+ aufs_bindex_t bend, bindex; -+ struct dentry *dentry; -+ struct au_finfo *finfo; -+ struct au_hfile *hfile; -+ -+ dentry = file->f_dentry; -+ finfo = au_fi(file); -+ if (!finfo->fi_hdir) { -+ hfile = &finfo->fi_htop; -+ AuDebugOn(!hfile->hf_file); -+ bindex = au_br_index(dentry->d_sb, hfile->hf_br->br_id); -+ AuDebugOn(bindex < 0); -+ if (bindex != finfo->fi_btop) -+ au_set_fbstart(file, bindex); -+ } else { -+ err = au_fidir_realloc(finfo, au_sbend(dentry->d_sb) + 1); -+ if (unlikely(err)) -+ goto out; -+ au_do_refresh_dir(file); -+ } -+ -+ err = 0; -+ need_reopen = 1; -+ if (!au_test_mmapped(file)) -+ err = au_file_refresh_by_inode(file, &need_reopen); -+ if (!err && need_reopen && !d_unlinked(dentry)) -+ err = reopen(file); -+ if (!err) { -+ au_update_figen(file); -+ goto out; /* success */ -+ } -+ -+ /* error, close all lower files */ -+ if (finfo->fi_hdir) { -+ bend = au_fbend_dir(file); -+ for (bindex = au_fbstart(file); bindex <= bend; bindex++) -+ au_set_h_fptr(file, bindex, NULL); -+ } -+ -+out: -+ return err; -+} -+ -+/* common function to regular file and dir */ -+int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file), -+ int wlock) -+{ -+ int err; -+ unsigned int sigen, figen; -+ aufs_bindex_t bstart; -+ unsigned char pseudo_link; -+ struct dentry *dentry; -+ struct inode *inode; -+ -+ err = 0; -+ dentry = file->f_dentry; -+ inode = dentry->d_inode; -+ sigen = au_sigen(dentry->d_sb); -+ fi_write_lock(file); -+ figen = au_figen(file); -+ di_write_lock_child(dentry); -+ bstart = au_dbstart(dentry); -+ pseudo_link = (bstart != au_ibstart(inode)); -+ if (sigen == figen && !pseudo_link && au_fbstart(file) == bstart) { -+ if (!wlock) { -+ di_downgrade_lock(dentry, AuLock_IR); -+ fi_downgrade_lock(file); -+ } -+ goto out; /* success */ -+ } -+ -+ AuDbg("sigen %d, figen %d\n", sigen, figen); -+ if (au_digen_test(dentry, sigen)) { -+ err = au_reval_dpath(dentry, sigen); -+ AuDebugOn(!err && au_digen_test(dentry, sigen)); -+ } -+ -+ if (!err) -+ err = refresh_file(file, reopen); -+ if (!err) { -+ if (!wlock) { -+ di_downgrade_lock(dentry, AuLock_IR); -+ fi_downgrade_lock(file); -+ } -+ } else { -+ di_write_unlock(dentry); -+ fi_write_unlock(file); -+ } -+ -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* cf. aufs_nopage() */ -+/* for madvise(2) */ -+static int aufs_readpage(struct file *file __maybe_unused, struct page *page) -+{ -+ unlock_page(page); -+ return 0; -+} -+ -+/* it will never be called, but necessary to support O_DIRECT */ -+static ssize_t aufs_direct_IO(int rw, struct kiocb *iocb, -+ struct iov_iter *iter, loff_t offset) -+{ BUG(); return 0; } -+ -+/* -+ * it will never be called, but madvise and fadvise behaves differently -+ * when get_xip_mem is defined -+ */ -+static int aufs_get_xip_mem(struct address_space *mapping, pgoff_t pgoff, -+ int create, void **kmem, unsigned long *pfn) -+{ BUG(); return 0; } -+ -+/* they will never be called. */ -+#ifdef CONFIG_AUFS_DEBUG -+static int aufs_write_begin(struct file *file, struct address_space *mapping, -+ loff_t pos, unsigned len, unsigned flags, -+ struct page **pagep, void **fsdata) -+{ AuUnsupport(); return 0; } -+static int aufs_write_end(struct file *file, struct address_space *mapping, -+ loff_t pos, unsigned len, unsigned copied, -+ struct page *page, void *fsdata) -+{ AuUnsupport(); return 0; } -+static int aufs_writepage(struct page *page, struct writeback_control *wbc) -+{ AuUnsupport(); return 0; } -+ -+static int aufs_set_page_dirty(struct page *page) -+{ AuUnsupport(); return 0; } -+static void aufs_invalidatepage(struct page *page, unsigned int offset, -+ unsigned int length) -+{ AuUnsupport(); } -+static int aufs_releasepage(struct page *page, gfp_t gfp) -+{ AuUnsupport(); return 0; } -+#if 0 /* called by memory compaction regardless file */ -+static int aufs_migratepage(struct address_space *mapping, struct page *newpage, -+ struct page *page, enum migrate_mode mode) -+{ AuUnsupport(); return 0; } -+#endif -+static int aufs_launder_page(struct page *page) -+{ AuUnsupport(); return 0; } -+static int aufs_is_partially_uptodate(struct page *page, -+ unsigned long from, -+ unsigned long count) -+{ AuUnsupport(); return 0; } -+static void aufs_is_dirty_writeback(struct page *page, bool *dirty, -+ bool *writeback) -+{ AuUnsupport(); } -+static int aufs_error_remove_page(struct address_space *mapping, -+ struct page *page) -+{ AuUnsupport(); return 0; } -+static int aufs_swap_activate(struct swap_info_struct *sis, struct file *file, -+ sector_t *span) -+{ AuUnsupport(); return 0; } -+static void aufs_swap_deactivate(struct file *file) -+{ AuUnsupport(); } -+#endif /* CONFIG_AUFS_DEBUG */ -+ -+const struct address_space_operations aufs_aop = { -+ .readpage = aufs_readpage, -+ .direct_IO = aufs_direct_IO, -+ .get_xip_mem = aufs_get_xip_mem, -+#ifdef CONFIG_AUFS_DEBUG -+ .writepage = aufs_writepage, -+ /* no writepages, because of writepage */ -+ .set_page_dirty = aufs_set_page_dirty, -+ /* no readpages, because of readpage */ -+ .write_begin = aufs_write_begin, -+ .write_end = aufs_write_end, -+ /* no bmap, no block device */ -+ .invalidatepage = aufs_invalidatepage, -+ .releasepage = aufs_releasepage, -+ /* is fallback_migrate_page ok? */ -+ /* .migratepage = aufs_migratepage, */ -+ .launder_page = aufs_launder_page, -+ .is_partially_uptodate = aufs_is_partially_uptodate, -+ .is_dirty_writeback = aufs_is_dirty_writeback, -+ .error_remove_page = aufs_error_remove_page, -+ .swap_activate = aufs_swap_activate, -+ .swap_deactivate = aufs_swap_deactivate -+#endif /* CONFIG_AUFS_DEBUG */ -+}; -diff --git a/fs/aufs/file.h b/fs/aufs/file.h -new file mode 100644 -index 0000000..564be91 ---- /dev/null -+++ b/fs/aufs/file.h -@@ -0,0 +1,291 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * file operations -+ */ -+ -+#ifndef __AUFS_FILE_H__ -+#define __AUFS_FILE_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+#include -+#include -+#include "rwsem.h" -+ -+struct au_branch; -+struct au_hfile { -+ struct file *hf_file; -+ struct au_branch *hf_br; -+}; -+ -+struct au_vdir; -+struct au_fidir { -+ aufs_bindex_t fd_bbot; -+ aufs_bindex_t fd_nent; -+ struct au_vdir *fd_vdir_cache; -+ struct au_hfile fd_hfile[]; -+}; -+ -+static inline int au_fidir_sz(int nent) -+{ -+ AuDebugOn(nent < 0); -+ return sizeof(struct au_fidir) + sizeof(struct au_hfile) * nent; -+} -+ -+struct au_finfo { -+ atomic_t fi_generation; -+ -+ struct au_rwsem fi_rwsem; -+ aufs_bindex_t fi_btop; -+ -+ /* do not union them */ -+ struct { /* for non-dir */ -+ struct au_hfile fi_htop; -+ atomic_t fi_mmapped; -+ }; -+ struct au_fidir *fi_hdir; /* for dir only */ -+ -+ struct hlist_node fi_hlist; -+ struct file *fi_file; /* very ugly */ -+} ____cacheline_aligned_in_smp; -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* file.c */ -+extern const struct address_space_operations aufs_aop; -+unsigned int au_file_roflags(unsigned int flags); -+struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags, -+ struct file *file, int force_wr); -+struct au_do_open_args { -+ int no_lock; -+ int (*open)(struct file *file, int flags, -+ struct file *h_file); -+ struct au_fidir *fidir; -+ struct file *h_file; -+}; -+int au_do_open(struct file *file, struct au_do_open_args *args); -+int au_reopen_nondir(struct file *file); -+struct au_pin; -+int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin); -+int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file), -+ int wlock); -+int au_do_flush(struct file *file, fl_owner_t id, -+ int (*flush)(struct file *file, fl_owner_t id)); -+ -+/* poll.c */ -+#ifdef CONFIG_AUFS_POLL -+unsigned int aufs_poll(struct file *file, poll_table *wait); -+#endif -+ -+#ifdef CONFIG_AUFS_BR_HFSPLUS -+/* hfsplus.c */ -+struct file *au_h_open_pre(struct dentry *dentry, aufs_bindex_t bindex, -+ int force_wr); -+void au_h_open_post(struct dentry *dentry, aufs_bindex_t bindex, -+ struct file *h_file); -+#else -+AuStub(struct file *, au_h_open_pre, return NULL, struct dentry *dentry, -+ aufs_bindex_t bindex, int force_wr) -+AuStubVoid(au_h_open_post, struct dentry *dentry, aufs_bindex_t bindex, -+ struct file *h_file); -+#endif -+ -+/* f_op.c */ -+extern const struct file_operations aufs_file_fop; -+int au_do_open_nondir(struct file *file, int flags, struct file *h_file); -+int aufs_release_nondir(struct inode *inode __maybe_unused, struct file *file); -+struct file *au_read_pre(struct file *file, int keep_fi); -+ -+/* finfo.c */ -+void au_hfput(struct au_hfile *hf, struct file *file); -+void au_set_h_fptr(struct file *file, aufs_bindex_t bindex, -+ struct file *h_file); -+ -+void au_update_figen(struct file *file); -+struct au_fidir *au_fidir_alloc(struct super_block *sb); -+int au_fidir_realloc(struct au_finfo *finfo, int nbr); -+ -+void au_fi_init_once(void *_fi); -+void au_finfo_fin(struct file *file); -+int au_finfo_init(struct file *file, struct au_fidir *fidir); -+ -+/* ioctl.c */ -+long aufs_ioctl_nondir(struct file *file, unsigned int cmd, unsigned long arg); -+#ifdef CONFIG_COMPAT -+long aufs_compat_ioctl_dir(struct file *file, unsigned int cmd, -+ unsigned long arg); -+long aufs_compat_ioctl_nondir(struct file *file, unsigned int cmd, -+ unsigned long arg); -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline struct au_finfo *au_fi(struct file *file) -+{ -+ return file->private_data; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * fi_read_lock, fi_write_lock, -+ * fi_read_unlock, fi_write_unlock, fi_downgrade_lock -+ */ -+AuSimpleRwsemFuncs(fi, struct file *f, &au_fi(f)->fi_rwsem); -+ -+#define FiMustNoWaiters(f) AuRwMustNoWaiters(&au_fi(f)->fi_rwsem) -+#define FiMustAnyLock(f) AuRwMustAnyLock(&au_fi(f)->fi_rwsem) -+#define FiMustWriteLock(f) AuRwMustWriteLock(&au_fi(f)->fi_rwsem) -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* todo: hard/soft set? */ -+static inline aufs_bindex_t au_fbstart(struct file *file) -+{ -+ FiMustAnyLock(file); -+ return au_fi(file)->fi_btop; -+} -+ -+static inline aufs_bindex_t au_fbend_dir(struct file *file) -+{ -+ FiMustAnyLock(file); -+ AuDebugOn(!au_fi(file)->fi_hdir); -+ return au_fi(file)->fi_hdir->fd_bbot; -+} -+ -+static inline struct au_vdir *au_fvdir_cache(struct file *file) -+{ -+ FiMustAnyLock(file); -+ AuDebugOn(!au_fi(file)->fi_hdir); -+ return au_fi(file)->fi_hdir->fd_vdir_cache; -+} -+ -+static inline void au_set_fbstart(struct file *file, aufs_bindex_t bindex) -+{ -+ FiMustWriteLock(file); -+ au_fi(file)->fi_btop = bindex; -+} -+ -+static inline void au_set_fbend_dir(struct file *file, aufs_bindex_t bindex) -+{ -+ FiMustWriteLock(file); -+ AuDebugOn(!au_fi(file)->fi_hdir); -+ au_fi(file)->fi_hdir->fd_bbot = bindex; -+} -+ -+static inline void au_set_fvdir_cache(struct file *file, -+ struct au_vdir *vdir_cache) -+{ -+ FiMustWriteLock(file); -+ AuDebugOn(!au_fi(file)->fi_hdir); -+ au_fi(file)->fi_hdir->fd_vdir_cache = vdir_cache; -+} -+ -+static inline struct file *au_hf_top(struct file *file) -+{ -+ FiMustAnyLock(file); -+ AuDebugOn(au_fi(file)->fi_hdir); -+ return au_fi(file)->fi_htop.hf_file; -+} -+ -+static inline struct file *au_hf_dir(struct file *file, aufs_bindex_t bindex) -+{ -+ FiMustAnyLock(file); -+ AuDebugOn(!au_fi(file)->fi_hdir); -+ return au_fi(file)->fi_hdir->fd_hfile[0 + bindex].hf_file; -+} -+ -+/* todo: memory barrier? */ -+static inline unsigned int au_figen(struct file *f) -+{ -+ return atomic_read(&au_fi(f)->fi_generation); -+} -+ -+static inline void au_set_mmapped(struct file *f) -+{ -+ if (atomic_inc_return(&au_fi(f)->fi_mmapped)) -+ return; -+ pr_warn("fi_mmapped wrapped around\n"); -+ while (!atomic_inc_return(&au_fi(f)->fi_mmapped)) -+ ; -+} -+ -+static inline void au_unset_mmapped(struct file *f) -+{ -+ atomic_dec(&au_fi(f)->fi_mmapped); -+} -+ -+static inline int au_test_mmapped(struct file *f) -+{ -+ return atomic_read(&au_fi(f)->fi_mmapped); -+} -+ -+/* customize vma->vm_file */ -+ -+static inline void au_do_vm_file_reset(struct vm_area_struct *vma, -+ struct file *file) -+{ -+ struct file *f; -+ -+ f = vma->vm_file; -+ get_file(file); -+ vma->vm_file = file; -+ fput(f); -+} -+ -+#ifdef CONFIG_MMU -+#define AuDbgVmRegion(file, vma) do {} while (0) -+ -+static inline void au_vm_file_reset(struct vm_area_struct *vma, -+ struct file *file) -+{ -+ au_do_vm_file_reset(vma, file); -+} -+#else -+#define AuDbgVmRegion(file, vma) \ -+ AuDebugOn((vma)->vm_region && (vma)->vm_region->vm_file != (file)) -+ -+static inline void au_vm_file_reset(struct vm_area_struct *vma, -+ struct file *file) -+{ -+ struct file *f; -+ -+ au_do_vm_file_reset(vma, file); -+ f = vma->vm_region->vm_file; -+ get_file(file); -+ vma->vm_region->vm_file = file; -+ fput(f); -+} -+#endif /* CONFIG_MMU */ -+ -+/* handle vma->vm_prfile */ -+static inline void au_vm_prfile_set(struct vm_area_struct *vma, -+ struct file *file) -+{ -+ get_file(file); -+ vma->vm_prfile = file; -+#ifndef CONFIG_MMU -+ get_file(file); -+ vma->vm_region->vm_prfile = file; -+#endif -+} -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_FILE_H__ */ -diff --git a/fs/aufs/finfo.c b/fs/aufs/finfo.c -new file mode 100644 -index 0000000..7e25db3 ---- /dev/null -+++ b/fs/aufs/finfo.c -@@ -0,0 +1,156 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * file private data -+ */ -+ -+#include "aufs.h" -+ -+void au_hfput(struct au_hfile *hf, struct file *file) -+{ -+ /* todo: direct access f_flags */ -+ if (vfsub_file_flags(file) & __FMODE_EXEC) -+ allow_write_access(hf->hf_file); -+ fput(hf->hf_file); -+ hf->hf_file = NULL; -+ atomic_dec(&hf->hf_br->br_count); -+ hf->hf_br = NULL; -+} -+ -+void au_set_h_fptr(struct file *file, aufs_bindex_t bindex, struct file *val) -+{ -+ struct au_finfo *finfo = au_fi(file); -+ struct au_hfile *hf; -+ struct au_fidir *fidir; -+ -+ fidir = finfo->fi_hdir; -+ if (!fidir) { -+ AuDebugOn(finfo->fi_btop != bindex); -+ hf = &finfo->fi_htop; -+ } else -+ hf = fidir->fd_hfile + bindex; -+ -+ if (hf && hf->hf_file) -+ au_hfput(hf, file); -+ if (val) { -+ FiMustWriteLock(file); -+ AuDebugOn(IS_ERR_OR_NULL(file->f_dentry)); -+ hf->hf_file = val; -+ hf->hf_br = au_sbr(file->f_dentry->d_sb, bindex); -+ } -+} -+ -+void au_update_figen(struct file *file) -+{ -+ atomic_set(&au_fi(file)->fi_generation, au_digen(file->f_dentry)); -+ /* smp_mb(); */ /* atomic_set */ -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct au_fidir *au_fidir_alloc(struct super_block *sb) -+{ -+ struct au_fidir *fidir; -+ int nbr; -+ -+ nbr = au_sbend(sb) + 1; -+ if (nbr < 2) -+ nbr = 2; /* initial allocate for 2 branches */ -+ fidir = kzalloc(au_fidir_sz(nbr), GFP_NOFS); -+ if (fidir) { -+ fidir->fd_bbot = -1; -+ fidir->fd_nent = nbr; -+ } -+ -+ return fidir; -+} -+ -+int au_fidir_realloc(struct au_finfo *finfo, int nbr) -+{ -+ int err; -+ struct au_fidir *fidir, *p; -+ -+ AuRwMustWriteLock(&finfo->fi_rwsem); -+ fidir = finfo->fi_hdir; -+ AuDebugOn(!fidir); -+ -+ err = -ENOMEM; -+ p = au_kzrealloc(fidir, au_fidir_sz(fidir->fd_nent), au_fidir_sz(nbr), -+ GFP_NOFS); -+ if (p) { -+ p->fd_nent = nbr; -+ finfo->fi_hdir = p; -+ err = 0; -+ } -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void au_finfo_fin(struct file *file) -+{ -+ struct au_finfo *finfo; -+ -+ au_nfiles_dec(file->f_dentry->d_sb); -+ -+ finfo = au_fi(file); -+ AuDebugOn(finfo->fi_hdir); -+ AuRwDestroy(&finfo->fi_rwsem); -+ au_cache_free_finfo(finfo); -+} -+ -+void au_fi_init_once(void *_finfo) -+{ -+ struct au_finfo *finfo = _finfo; -+ static struct lock_class_key aufs_fi; -+ -+ au_rw_init(&finfo->fi_rwsem); -+ au_rw_class(&finfo->fi_rwsem, &aufs_fi); -+} -+ -+int au_finfo_init(struct file *file, struct au_fidir *fidir) -+{ -+ int err; -+ struct au_finfo *finfo; -+ struct dentry *dentry; -+ -+ err = -ENOMEM; -+ dentry = file->f_dentry; -+ finfo = au_cache_alloc_finfo(); -+ if (unlikely(!finfo)) -+ goto out; -+ -+ err = 0; -+ au_nfiles_inc(dentry->d_sb); -+ /* verbose coding for lock class name */ -+ if (!fidir) -+ au_rw_class(&finfo->fi_rwsem, au_lc_key + AuLcNonDir_FIINFO); -+ else -+ au_rw_class(&finfo->fi_rwsem, au_lc_key + AuLcDir_FIINFO); -+ au_rw_write_lock(&finfo->fi_rwsem); -+ finfo->fi_btop = -1; -+ finfo->fi_hdir = fidir; -+ atomic_set(&finfo->fi_generation, au_digen(dentry)); -+ /* smp_mb(); */ /* atomic_set */ -+ -+ file->private_data = finfo; -+ -+out: -+ return err; -+} -diff --git a/fs/aufs/fstype.h b/fs/aufs/fstype.h -new file mode 100644 -index 0000000..2842400 ---- /dev/null -+++ b/fs/aufs/fstype.h -@@ -0,0 +1,400 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * judging filesystem type -+ */ -+ -+#ifndef __AUFS_FSTYPE_H__ -+#define __AUFS_FSTYPE_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+#include -+#include -+#include -+ -+static inline int au_test_aufs(struct super_block *sb) -+{ -+ return sb->s_magic == AUFS_SUPER_MAGIC; -+} -+ -+static inline const char *au_sbtype(struct super_block *sb) -+{ -+ return sb->s_type->name; -+} -+ -+static inline int au_test_iso9660(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_ISO9660_FS) || defined(CONFIG_ISO9660_FS_MODULE) -+ return sb->s_magic == ISOFS_SUPER_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_romfs(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_ROMFS_FS) || defined(CONFIG_ROMFS_FS_MODULE) -+ return sb->s_magic == ROMFS_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_cramfs(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_CRAMFS) || defined(CONFIG_CRAMFS_MODULE) -+ return sb->s_magic == CRAMFS_MAGIC; -+#endif -+ return 0; -+} -+ -+static inline int au_test_nfs(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_NFS_FS) || defined(CONFIG_NFS_FS_MODULE) -+ return sb->s_magic == NFS_SUPER_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_fuse(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_FUSE_FS) || defined(CONFIG_FUSE_FS_MODULE) -+ return sb->s_magic == FUSE_SUPER_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_xfs(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_XFS_FS) || defined(CONFIG_XFS_FS_MODULE) -+ return sb->s_magic == XFS_SB_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_tmpfs(struct super_block *sb __maybe_unused) -+{ -+#ifdef CONFIG_TMPFS -+ return sb->s_magic == TMPFS_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_ecryptfs(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_ECRYPT_FS) || defined(CONFIG_ECRYPT_FS_MODULE) -+ return !strcmp(au_sbtype(sb), "ecryptfs"); -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_ramfs(struct super_block *sb) -+{ -+ return sb->s_magic == RAMFS_MAGIC; -+} -+ -+static inline int au_test_ubifs(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_UBIFS_FS) || defined(CONFIG_UBIFS_FS_MODULE) -+ return sb->s_magic == UBIFS_SUPER_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_procfs(struct super_block *sb __maybe_unused) -+{ -+#ifdef CONFIG_PROC_FS -+ return sb->s_magic == PROC_SUPER_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_sysfs(struct super_block *sb __maybe_unused) -+{ -+#ifdef CONFIG_SYSFS -+ return sb->s_magic == SYSFS_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_configfs(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_CONFIGFS_FS) || defined(CONFIG_CONFIGFS_FS_MODULE) -+ return sb->s_magic == CONFIGFS_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_minix(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_MINIX_FS) || defined(CONFIG_MINIX_FS_MODULE) -+ return sb->s_magic == MINIX3_SUPER_MAGIC -+ || sb->s_magic == MINIX2_SUPER_MAGIC -+ || sb->s_magic == MINIX2_SUPER_MAGIC2 -+ || sb->s_magic == MINIX_SUPER_MAGIC -+ || sb->s_magic == MINIX_SUPER_MAGIC2; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_fat(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_FAT_FS) || defined(CONFIG_FAT_FS_MODULE) -+ return sb->s_magic == MSDOS_SUPER_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_msdos(struct super_block *sb) -+{ -+ return au_test_fat(sb); -+} -+ -+static inline int au_test_vfat(struct super_block *sb) -+{ -+ return au_test_fat(sb); -+} -+ -+static inline int au_test_securityfs(struct super_block *sb __maybe_unused) -+{ -+#ifdef CONFIG_SECURITYFS -+ return sb->s_magic == SECURITYFS_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_squashfs(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_SQUASHFS) || defined(CONFIG_SQUASHFS_MODULE) -+ return sb->s_magic == SQUASHFS_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_btrfs(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_BTRFS_FS) || defined(CONFIG_BTRFS_FS_MODULE) -+ return sb->s_magic == BTRFS_SUPER_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_xenfs(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_XENFS) || defined(CONFIG_XENFS_MODULE) -+ return sb->s_magic == XENFS_SUPER_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_debugfs(struct super_block *sb __maybe_unused) -+{ -+#ifdef CONFIG_DEBUG_FS -+ return sb->s_magic == DEBUGFS_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_nilfs(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_NILFS) || defined(CONFIG_NILFS_MODULE) -+ return sb->s_magic == NILFS_SUPER_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_hfsplus(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_HFSPLUS_FS) || defined(CONFIG_HFSPLUS_FS_MODULE) -+ return sb->s_magic == HFSPLUS_SUPER_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+/* ---------------------------------------------------------------------- */ -+/* -+ * they can't be an aufs branch. -+ */ -+static inline int au_test_fs_unsuppoted(struct super_block *sb) -+{ -+ return -+#ifndef CONFIG_AUFS_BR_RAMFS -+ au_test_ramfs(sb) || -+#endif -+ au_test_procfs(sb) -+ || au_test_sysfs(sb) -+ || au_test_configfs(sb) -+ || au_test_debugfs(sb) -+ || au_test_securityfs(sb) -+ || au_test_xenfs(sb) -+ || au_test_ecryptfs(sb) -+ /* || !strcmp(au_sbtype(sb), "unionfs") */ -+ || au_test_aufs(sb); /* will be supported in next version */ -+} -+ -+static inline int au_test_fs_remote(struct super_block *sb) -+{ -+ return !au_test_tmpfs(sb) -+#ifdef CONFIG_AUFS_BR_RAMFS -+ && !au_test_ramfs(sb) -+#endif -+ && !(sb->s_type->fs_flags & FS_REQUIRES_DEV); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * Note: these functions (below) are created after reading ->getattr() in all -+ * filesystems under linux/fs. it means we have to do so in every update... -+ */ -+ -+/* -+ * some filesystems require getattr to refresh the inode attributes before -+ * referencing. -+ * in most cases, we can rely on the inode attribute in NFS (or every remote fs) -+ * and leave the work for d_revalidate() -+ */ -+static inline int au_test_fs_refresh_iattr(struct super_block *sb) -+{ -+ return au_test_nfs(sb) -+ || au_test_fuse(sb) -+ /* || au_test_btrfs(sb) */ /* untested */ -+ ; -+} -+ -+/* -+ * filesystems which don't maintain i_size or i_blocks. -+ */ -+static inline int au_test_fs_bad_iattr_size(struct super_block *sb) -+{ -+ return au_test_xfs(sb) -+ || au_test_btrfs(sb) -+ || au_test_ubifs(sb) -+ || au_test_hfsplus(sb) /* maintained, but incorrect */ -+ /* || au_test_minix(sb) */ /* untested */ -+ ; -+} -+ -+/* -+ * filesystems which don't store the correct value in some of their inode -+ * attributes. -+ */ -+static inline int au_test_fs_bad_iattr(struct super_block *sb) -+{ -+ return au_test_fs_bad_iattr_size(sb) -+ || au_test_fat(sb) -+ || au_test_msdos(sb) -+ || au_test_vfat(sb); -+} -+ -+/* they don't check i_nlink in link(2) */ -+static inline int au_test_fs_no_limit_nlink(struct super_block *sb) -+{ -+ return au_test_tmpfs(sb) -+#ifdef CONFIG_AUFS_BR_RAMFS -+ || au_test_ramfs(sb) -+#endif -+ || au_test_ubifs(sb) -+ || au_test_hfsplus(sb); -+} -+ -+/* -+ * filesystems which sets S_NOATIME and S_NOCMTIME. -+ */ -+static inline int au_test_fs_notime(struct super_block *sb) -+{ -+ return au_test_nfs(sb) -+ || au_test_fuse(sb) -+ || au_test_ubifs(sb) -+ ; -+} -+ -+/* temporary support for i#1 in cramfs */ -+static inline int au_test_fs_unique_ino(struct inode *inode) -+{ -+ if (au_test_cramfs(inode->i_sb)) -+ return inode->i_ino != 1; -+ return 1; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * the filesystem where the xino files placed must support i/o after unlink and -+ * maintain i_size and i_blocks. -+ */ -+static inline int au_test_fs_bad_xino(struct super_block *sb) -+{ -+ return au_test_fs_remote(sb) -+ || au_test_fs_bad_iattr_size(sb) -+ /* don't want unnecessary work for xino */ -+ || au_test_aufs(sb) -+ || au_test_ecryptfs(sb) -+ || au_test_nilfs(sb); -+} -+ -+static inline int au_test_fs_trunc_xino(struct super_block *sb) -+{ -+ return au_test_tmpfs(sb) -+ || au_test_ramfs(sb); -+} -+ -+/* -+ * test if the @sb is real-readonly. -+ */ -+static inline int au_test_fs_rr(struct super_block *sb) -+{ -+ return au_test_squashfs(sb) -+ || au_test_iso9660(sb) -+ || au_test_cramfs(sb) -+ || au_test_romfs(sb); -+} -+ -+/* -+ * test if the @inode is nfs with 'noacl' option -+ * NFS always sets MS_POSIXACL regardless its mount option 'noacl.' -+ */ -+static inline int au_test_nfs_noacl(struct inode *inode) -+{ -+ return au_test_nfs(inode->i_sb) -+ /* && IS_POSIXACL(inode) */ -+ && !nfs_server_capable(inode, NFS_CAP_ACLS); -+} -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_FSTYPE_H__ */ -diff --git a/fs/aufs/hfsnotify.c b/fs/aufs/hfsnotify.c -new file mode 100644 -index 0000000..6fa79b0 ---- /dev/null -+++ b/fs/aufs/hfsnotify.c -@@ -0,0 +1,288 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * fsnotify for the lower directories -+ */ -+ -+#include "aufs.h" -+ -+/* FS_IN_IGNORED is unnecessary */ -+static const __u32 AuHfsnMask = (FS_MOVED_TO | FS_MOVED_FROM | FS_DELETE -+ | FS_CREATE | FS_EVENT_ON_CHILD); -+static DECLARE_WAIT_QUEUE_HEAD(au_hfsn_wq); -+static __cacheline_aligned_in_smp atomic64_t au_hfsn_ifree = ATOMIC64_INIT(0); -+ -+static void au_hfsn_free_mark(struct fsnotify_mark *mark) -+{ -+ struct au_hnotify *hn = container_of(mark, struct au_hnotify, -+ hn_mark); -+ AuDbg("here\n"); -+ au_cache_free_hnotify(hn); -+ smp_mb__before_atomic(); -+ if (atomic64_dec_and_test(&au_hfsn_ifree)) -+ wake_up(&au_hfsn_wq); -+} -+ -+static int au_hfsn_alloc(struct au_hinode *hinode) -+{ -+ int err; -+ struct au_hnotify *hn; -+ struct super_block *sb; -+ struct au_branch *br; -+ struct fsnotify_mark *mark; -+ aufs_bindex_t bindex; -+ -+ hn = hinode->hi_notify; -+ sb = hn->hn_aufs_inode->i_sb; -+ bindex = au_br_index(sb, hinode->hi_id); -+ br = au_sbr(sb, bindex); -+ AuDebugOn(!br->br_hfsn); -+ -+ mark = &hn->hn_mark; -+ fsnotify_init_mark(mark, au_hfsn_free_mark); -+ mark->mask = AuHfsnMask; -+ /* -+ * by udba rename or rmdir, aufs assign a new inode to the known -+ * h_inode, so specify 1 to allow dups. -+ */ -+ lockdep_off(); -+ err = fsnotify_add_mark(mark, br->br_hfsn->hfsn_group, hinode->hi_inode, -+ /*mnt*/NULL, /*allow_dups*/1); -+ /* even if err */ -+ fsnotify_put_mark(mark); -+ lockdep_on(); -+ -+ return err; -+} -+ -+static int au_hfsn_free(struct au_hinode *hinode, struct au_hnotify *hn) -+{ -+ struct fsnotify_mark *mark; -+ unsigned long long ull; -+ struct fsnotify_group *group; -+ -+ ull = atomic64_inc_return(&au_hfsn_ifree); -+ BUG_ON(!ull); -+ -+ mark = &hn->hn_mark; -+ spin_lock(&mark->lock); -+ group = mark->group; -+ fsnotify_get_group(group); -+ spin_unlock(&mark->lock); -+ lockdep_off(); -+ fsnotify_destroy_mark(mark, group); -+ fsnotify_put_group(group); -+ lockdep_on(); -+ -+ /* free hn by myself */ -+ return 0; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void au_hfsn_ctl(struct au_hinode *hinode, int do_set) -+{ -+ struct fsnotify_mark *mark; -+ -+ mark = &hinode->hi_notify->hn_mark; -+ spin_lock(&mark->lock); -+ if (do_set) { -+ AuDebugOn(mark->mask & AuHfsnMask); -+ mark->mask |= AuHfsnMask; -+ } else { -+ AuDebugOn(!(mark->mask & AuHfsnMask)); -+ mark->mask &= ~AuHfsnMask; -+ } -+ spin_unlock(&mark->lock); -+ /* fsnotify_recalc_inode_mask(hinode->hi_inode); */ -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* #define AuDbgHnotify */ -+#ifdef AuDbgHnotify -+static char *au_hfsn_name(u32 mask) -+{ -+#ifdef CONFIG_AUFS_DEBUG -+#define test_ret(flag) \ -+ do { \ -+ if (mask & flag) \ -+ return #flag; \ -+ } while (0) -+ test_ret(FS_ACCESS); -+ test_ret(FS_MODIFY); -+ test_ret(FS_ATTRIB); -+ test_ret(FS_CLOSE_WRITE); -+ test_ret(FS_CLOSE_NOWRITE); -+ test_ret(FS_OPEN); -+ test_ret(FS_MOVED_FROM); -+ test_ret(FS_MOVED_TO); -+ test_ret(FS_CREATE); -+ test_ret(FS_DELETE); -+ test_ret(FS_DELETE_SELF); -+ test_ret(FS_MOVE_SELF); -+ test_ret(FS_UNMOUNT); -+ test_ret(FS_Q_OVERFLOW); -+ test_ret(FS_IN_IGNORED); -+ test_ret(FS_ISDIR); -+ test_ret(FS_IN_ONESHOT); -+ test_ret(FS_EVENT_ON_CHILD); -+ return ""; -+#undef test_ret -+#else -+ return "??"; -+#endif -+} -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void au_hfsn_free_group(struct fsnotify_group *group) -+{ -+ struct au_br_hfsnotify *hfsn = group->private; -+ -+ AuDbg("here\n"); -+ kfree(hfsn); -+} -+ -+static int au_hfsn_handle_event(struct fsnotify_group *group, -+ struct inode *inode, -+ struct fsnotify_mark *inode_mark, -+ struct fsnotify_mark *vfsmount_mark, -+ u32 mask, void *data, int data_type, -+ const unsigned char *file_name, u32 cookie) -+{ -+ int err; -+ struct au_hnotify *hnotify; -+ struct inode *h_dir, *h_inode; -+ struct qstr h_child_qstr = QSTR_INIT(file_name, strlen(file_name)); -+ -+ AuDebugOn(data_type != FSNOTIFY_EVENT_INODE); -+ -+ err = 0; -+ /* if FS_UNMOUNT happens, there must be another bug */ -+ AuDebugOn(mask & FS_UNMOUNT); -+ if (mask & (FS_IN_IGNORED | FS_UNMOUNT)) -+ goto out; -+ -+ h_dir = inode; -+ h_inode = NULL; -+#ifdef AuDbgHnotify -+ au_debug_on(); -+ if (1 || h_child_qstr.len != sizeof(AUFS_XINO_FNAME) - 1 -+ || strncmp(h_child_qstr.name, AUFS_XINO_FNAME, h_child_qstr.len)) { -+ AuDbg("i%lu, mask 0x%x %s, hcname %.*s, hi%lu\n", -+ h_dir->i_ino, mask, au_hfsn_name(mask), -+ AuLNPair(&h_child_qstr), h_inode ? h_inode->i_ino : 0); -+ /* WARN_ON(1); */ -+ } -+ au_debug_off(); -+#endif -+ -+ AuDebugOn(!inode_mark); -+ hnotify = container_of(inode_mark, struct au_hnotify, hn_mark); -+ err = au_hnotify(h_dir, hnotify, mask, &h_child_qstr, h_inode); -+ -+out: -+ return err; -+} -+ -+static struct fsnotify_ops au_hfsn_ops = { -+ .handle_event = au_hfsn_handle_event, -+ .free_group_priv = au_hfsn_free_group -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void au_hfsn_fin_br(struct au_branch *br) -+{ -+ struct au_br_hfsnotify *hfsn; -+ -+ hfsn = br->br_hfsn; -+ if (hfsn) { -+ lockdep_off(); -+ fsnotify_put_group(hfsn->hfsn_group); -+ lockdep_on(); -+ } -+} -+ -+static int au_hfsn_init_br(struct au_branch *br, int perm) -+{ -+ int err; -+ struct fsnotify_group *group; -+ struct au_br_hfsnotify *hfsn; -+ -+ err = 0; -+ br->br_hfsn = NULL; -+ if (!au_br_hnotifyable(perm)) -+ goto out; -+ -+ err = -ENOMEM; -+ hfsn = kmalloc(sizeof(*hfsn), GFP_NOFS); -+ if (unlikely(!hfsn)) -+ goto out; -+ -+ err = 0; -+ group = fsnotify_alloc_group(&au_hfsn_ops); -+ if (IS_ERR(group)) { -+ err = PTR_ERR(group); -+ pr_err("fsnotify_alloc_group() failed, %d\n", err); -+ goto out_hfsn; -+ } -+ -+ group->private = hfsn; -+ hfsn->hfsn_group = group; -+ br->br_hfsn = hfsn; -+ goto out; /* success */ -+ -+out_hfsn: -+ kfree(hfsn); -+out: -+ return err; -+} -+ -+static int au_hfsn_reset_br(unsigned int udba, struct au_branch *br, int perm) -+{ -+ int err; -+ -+ err = 0; -+ if (!br->br_hfsn) -+ err = au_hfsn_init_br(br, perm); -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void au_hfsn_fin(void) -+{ -+ AuDbg("au_hfsn_ifree %lld\n", (long long)atomic64_read(&au_hfsn_ifree)); -+ wait_event(au_hfsn_wq, !atomic64_read(&au_hfsn_ifree)); -+} -+ -+const struct au_hnotify_op au_hnotify_op = { -+ .ctl = au_hfsn_ctl, -+ .alloc = au_hfsn_alloc, -+ .free = au_hfsn_free, -+ -+ .fin = au_hfsn_fin, -+ -+ .reset_br = au_hfsn_reset_br, -+ .fin_br = au_hfsn_fin_br, -+ .init_br = au_hfsn_init_br -+}; -diff --git a/fs/aufs/hfsplus.c b/fs/aufs/hfsplus.c -new file mode 100644 -index 0000000..8a54c82 ---- /dev/null -+++ b/fs/aufs/hfsplus.c -@@ -0,0 +1,56 @@ -+/* -+ * Copyright (C) 2010-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * special support for filesystems which aqucires an inode mutex -+ * at final closing a file, eg, hfsplus. -+ * -+ * This trick is very simple and stupid, just to open the file before really -+ * neceeary open to tell hfsplus that this is not the final closing. -+ * The caller should call au_h_open_pre() after acquiring the inode mutex, -+ * and au_h_open_post() after releasing it. -+ */ -+ -+#include "aufs.h" -+ -+struct file *au_h_open_pre(struct dentry *dentry, aufs_bindex_t bindex, -+ int force_wr) -+{ -+ struct file *h_file; -+ struct dentry *h_dentry; -+ -+ h_dentry = au_h_dptr(dentry, bindex); -+ AuDebugOn(!h_dentry); -+ AuDebugOn(!h_dentry->d_inode); -+ -+ h_file = NULL; -+ if (au_test_hfsplus(h_dentry->d_sb) -+ && S_ISREG(h_dentry->d_inode->i_mode)) -+ h_file = au_h_open(dentry, bindex, -+ O_RDONLY | O_NOATIME | O_LARGEFILE, -+ /*file*/NULL, force_wr); -+ return h_file; -+} -+ -+void au_h_open_post(struct dentry *dentry, aufs_bindex_t bindex, -+ struct file *h_file) -+{ -+ if (h_file) { -+ fput(h_file); -+ au_sbr_put(dentry->d_sb, bindex); -+ } -+} -diff --git a/fs/aufs/hnotify.c b/fs/aufs/hnotify.c -new file mode 100644 -index 0000000..1801420 ---- /dev/null -+++ b/fs/aufs/hnotify.c -@@ -0,0 +1,714 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * abstraction to notify the direct changes on lower directories -+ */ -+ -+#include "aufs.h" -+ -+int au_hn_alloc(struct au_hinode *hinode, struct inode *inode) -+{ -+ int err; -+ struct au_hnotify *hn; -+ -+ err = -ENOMEM; -+ hn = au_cache_alloc_hnotify(); -+ if (hn) { -+ hn->hn_aufs_inode = inode; -+ hinode->hi_notify = hn; -+ err = au_hnotify_op.alloc(hinode); -+ AuTraceErr(err); -+ if (unlikely(err)) { -+ hinode->hi_notify = NULL; -+ au_cache_free_hnotify(hn); -+ /* -+ * The upper dir was removed by udba, but the same named -+ * dir left. In this case, aufs assignes a new inode -+ * number and set the monitor again. -+ * For the lower dir, the old monitnor is still left. -+ */ -+ if (err == -EEXIST) -+ err = 0; -+ } -+ } -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+void au_hn_free(struct au_hinode *hinode) -+{ -+ struct au_hnotify *hn; -+ -+ hn = hinode->hi_notify; -+ if (hn) { -+ hinode->hi_notify = NULL; -+ if (au_hnotify_op.free(hinode, hn)) -+ au_cache_free_hnotify(hn); -+ } -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void au_hn_ctl(struct au_hinode *hinode, int do_set) -+{ -+ if (hinode->hi_notify) -+ au_hnotify_op.ctl(hinode, do_set); -+} -+ -+void au_hn_reset(struct inode *inode, unsigned int flags) -+{ -+ aufs_bindex_t bindex, bend; -+ struct inode *hi; -+ struct dentry *iwhdentry; -+ -+ bend = au_ibend(inode); -+ for (bindex = au_ibstart(inode); bindex <= bend; bindex++) { -+ hi = au_h_iptr(inode, bindex); -+ if (!hi) -+ continue; -+ -+ /* mutex_lock_nested(&hi->i_mutex, AuLsc_I_CHILD); */ -+ iwhdentry = au_hi_wh(inode, bindex); -+ if (iwhdentry) -+ dget(iwhdentry); -+ au_igrab(hi); -+ au_set_h_iptr(inode, bindex, NULL, 0); -+ au_set_h_iptr(inode, bindex, au_igrab(hi), -+ flags & ~AuHi_XINO); -+ iput(hi); -+ dput(iwhdentry); -+ /* mutex_unlock(&hi->i_mutex); */ -+ } -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int hn_xino(struct inode *inode, struct inode *h_inode) -+{ -+ int err; -+ aufs_bindex_t bindex, bend, bfound, bstart; -+ struct inode *h_i; -+ -+ err = 0; -+ if (unlikely(inode->i_ino == AUFS_ROOT_INO)) { -+ pr_warn("branch root dir was changed\n"); -+ goto out; -+ } -+ -+ bfound = -1; -+ bend = au_ibend(inode); -+ bstart = au_ibstart(inode); -+#if 0 /* reserved for future use */ -+ if (bindex == bend) { -+ /* keep this ino in rename case */ -+ goto out; -+ } -+#endif -+ for (bindex = bstart; bindex <= bend; bindex++) -+ if (au_h_iptr(inode, bindex) == h_inode) { -+ bfound = bindex; -+ break; -+ } -+ if (bfound < 0) -+ goto out; -+ -+ for (bindex = bstart; bindex <= bend; bindex++) { -+ h_i = au_h_iptr(inode, bindex); -+ if (!h_i) -+ continue; -+ -+ err = au_xino_write(inode->i_sb, bindex, h_i->i_ino, /*ino*/0); -+ /* ignore this error */ -+ /* bad action? */ -+ } -+ -+ /* children inode number will be broken */ -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+static int hn_gen_tree(struct dentry *dentry) -+{ -+ int err, i, j, ndentry; -+ struct au_dcsub_pages dpages; -+ struct au_dpage *dpage; -+ struct dentry **dentries; -+ -+ err = au_dpages_init(&dpages, GFP_NOFS); -+ if (unlikely(err)) -+ goto out; -+ err = au_dcsub_pages(&dpages, dentry, NULL, NULL); -+ if (unlikely(err)) -+ goto out_dpages; -+ -+ for (i = 0; i < dpages.ndpage; i++) { -+ dpage = dpages.dpages + i; -+ dentries = dpage->dentries; -+ ndentry = dpage->ndentry; -+ for (j = 0; j < ndentry; j++) { -+ struct dentry *d; -+ -+ d = dentries[j]; -+ if (IS_ROOT(d)) -+ continue; -+ -+ au_digen_dec(d); -+ if (d->d_inode) -+ /* todo: reset children xino? -+ cached children only? */ -+ au_iigen_dec(d->d_inode); -+ } -+ } -+ -+out_dpages: -+ au_dpages_free(&dpages); -+ -+#if 0 -+ /* discard children */ -+ dentry_unhash(dentry); -+ dput(dentry); -+#endif -+out: -+ return err; -+} -+ -+/* -+ * return 0 if processed. -+ */ -+static int hn_gen_by_inode(char *name, unsigned int nlen, struct inode *inode, -+ const unsigned int isdir) -+{ -+ int err; -+ struct dentry *d; -+ struct qstr *dname; -+ -+ err = 1; -+ if (unlikely(inode->i_ino == AUFS_ROOT_INO)) { -+ pr_warn("branch root dir was changed\n"); -+ err = 0; -+ goto out; -+ } -+ -+ if (!isdir) { -+ AuDebugOn(!name); -+ au_iigen_dec(inode); -+ spin_lock(&inode->i_lock); -+ hlist_for_each_entry(d, &inode->i_dentry, d_u.d_alias) { -+ spin_lock(&d->d_lock); -+ dname = &d->d_name; -+ if (dname->len != nlen -+ && memcmp(dname->name, name, nlen)) { -+ spin_unlock(&d->d_lock); -+ continue; -+ } -+ err = 0; -+ au_digen_dec(d); -+ spin_unlock(&d->d_lock); -+ break; -+ } -+ spin_unlock(&inode->i_lock); -+ } else { -+ au_fset_si(au_sbi(inode->i_sb), FAILED_REFRESH_DIR); -+ d = d_find_any_alias(inode); -+ if (!d) { -+ au_iigen_dec(inode); -+ goto out; -+ } -+ -+ spin_lock(&d->d_lock); -+ dname = &d->d_name; -+ if (dname->len == nlen && !memcmp(dname->name, name, nlen)) { -+ spin_unlock(&d->d_lock); -+ err = hn_gen_tree(d); -+ spin_lock(&d->d_lock); -+ } -+ spin_unlock(&d->d_lock); -+ dput(d); -+ } -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+static int hn_gen_by_name(struct dentry *dentry, const unsigned int isdir) -+{ -+ int err; -+ struct inode *inode; -+ -+ inode = dentry->d_inode; -+ if (IS_ROOT(dentry) -+ /* || (inode && inode->i_ino == AUFS_ROOT_INO) */ -+ ) { -+ pr_warn("branch root dir was changed\n"); -+ return 0; -+ } -+ -+ err = 0; -+ if (!isdir) { -+ au_digen_dec(dentry); -+ if (inode) -+ au_iigen_dec(inode); -+ } else { -+ au_fset_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIR); -+ if (inode) -+ err = hn_gen_tree(dentry); -+ } -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* hnotify job flags */ -+#define AuHnJob_XINO0 1 -+#define AuHnJob_GEN (1 << 1) -+#define AuHnJob_DIRENT (1 << 2) -+#define AuHnJob_ISDIR (1 << 3) -+#define AuHnJob_TRYXINO0 (1 << 4) -+#define AuHnJob_MNTPNT (1 << 5) -+#define au_ftest_hnjob(flags, name) ((flags) & AuHnJob_##name) -+#define au_fset_hnjob(flags, name) \ -+ do { (flags) |= AuHnJob_##name; } while (0) -+#define au_fclr_hnjob(flags, name) \ -+ do { (flags) &= ~AuHnJob_##name; } while (0) -+ -+enum { -+ AuHn_CHILD, -+ AuHn_PARENT, -+ AuHnLast -+}; -+ -+struct au_hnotify_args { -+ struct inode *h_dir, *dir, *h_child_inode; -+ u32 mask; -+ unsigned int flags[AuHnLast]; -+ unsigned int h_child_nlen; -+ char h_child_name[]; -+}; -+ -+struct hn_job_args { -+ unsigned int flags; -+ struct inode *inode, *h_inode, *dir, *h_dir; -+ struct dentry *dentry; -+ char *h_name; -+ int h_nlen; -+}; -+ -+static int hn_job(struct hn_job_args *a) -+{ -+ const unsigned int isdir = au_ftest_hnjob(a->flags, ISDIR); -+ int e; -+ -+ /* reset xino */ -+ if (au_ftest_hnjob(a->flags, XINO0) && a->inode) -+ hn_xino(a->inode, a->h_inode); /* ignore this error */ -+ -+ if (au_ftest_hnjob(a->flags, TRYXINO0) -+ && a->inode -+ && a->h_inode) { -+ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); -+ if (!a->h_inode->i_nlink -+ && !(a->h_inode->i_state & I_LINKABLE)) -+ hn_xino(a->inode, a->h_inode); /* ignore this error */ -+ mutex_unlock(&a->h_inode->i_mutex); -+ } -+ -+ /* make the generation obsolete */ -+ if (au_ftest_hnjob(a->flags, GEN)) { -+ e = -1; -+ if (a->inode) -+ e = hn_gen_by_inode(a->h_name, a->h_nlen, a->inode, -+ isdir); -+ if (e && a->dentry) -+ hn_gen_by_name(a->dentry, isdir); -+ /* ignore this error */ -+ } -+ -+ /* make dir entries obsolete */ -+ if (au_ftest_hnjob(a->flags, DIRENT) && a->inode) { -+ struct au_vdir *vdir; -+ -+ vdir = au_ivdir(a->inode); -+ if (vdir) -+ vdir->vd_jiffy = 0; -+ /* IMustLock(a->inode); */ -+ /* a->inode->i_version++; */ -+ } -+ -+ /* can do nothing but warn */ -+ if (au_ftest_hnjob(a->flags, MNTPNT) -+ && a->dentry -+ && d_mountpoint(a->dentry)) -+ pr_warn("mount-point %pd is removed or renamed\n", a->dentry); -+ -+ return 0; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static struct dentry *lookup_wlock_by_name(char *name, unsigned int nlen, -+ struct inode *dir) -+{ -+ struct dentry *dentry, *d, *parent; -+ struct qstr *dname; -+ -+ parent = d_find_any_alias(dir); -+ if (!parent) -+ return NULL; -+ -+ dentry = NULL; -+ spin_lock(&parent->d_lock); -+ list_for_each_entry(d, &parent->d_subdirs, d_child) { -+ /* AuDbg("%pd\n", d); */ -+ spin_lock_nested(&d->d_lock, DENTRY_D_LOCK_NESTED); -+ dname = &d->d_name; -+ if (dname->len != nlen || memcmp(dname->name, name, nlen)) -+ goto cont_unlock; -+ if (au_di(d)) -+ au_digen_dec(d); -+ else -+ goto cont_unlock; -+ if (au_dcount(d) > 0) { -+ dentry = dget_dlock(d); -+ spin_unlock(&d->d_lock); -+ break; -+ } -+ -+cont_unlock: -+ spin_unlock(&d->d_lock); -+ } -+ spin_unlock(&parent->d_lock); -+ dput(parent); -+ -+ if (dentry) -+ di_write_lock_child(dentry); -+ -+ return dentry; -+} -+ -+static struct inode *lookup_wlock_by_ino(struct super_block *sb, -+ aufs_bindex_t bindex, ino_t h_ino) -+{ -+ struct inode *inode; -+ ino_t ino; -+ int err; -+ -+ inode = NULL; -+ err = au_xino_read(sb, bindex, h_ino, &ino); -+ if (!err && ino) -+ inode = ilookup(sb, ino); -+ if (!inode) -+ goto out; -+ -+ if (unlikely(inode->i_ino == AUFS_ROOT_INO)) { -+ pr_warn("wrong root branch\n"); -+ iput(inode); -+ inode = NULL; -+ goto out; -+ } -+ -+ ii_write_lock_child(inode); -+ -+out: -+ return inode; -+} -+ -+static void au_hn_bh(void *_args) -+{ -+ struct au_hnotify_args *a = _args; -+ struct super_block *sb; -+ aufs_bindex_t bindex, bend, bfound; -+ unsigned char xino, try_iput; -+ int err; -+ struct inode *inode; -+ ino_t h_ino; -+ struct hn_job_args args; -+ struct dentry *dentry; -+ struct au_sbinfo *sbinfo; -+ -+ AuDebugOn(!_args); -+ AuDebugOn(!a->h_dir); -+ AuDebugOn(!a->dir); -+ AuDebugOn(!a->mask); -+ AuDbg("mask 0x%x, i%lu, hi%lu, hci%lu\n", -+ a->mask, a->dir->i_ino, a->h_dir->i_ino, -+ a->h_child_inode ? a->h_child_inode->i_ino : 0); -+ -+ inode = NULL; -+ dentry = NULL; -+ /* -+ * do not lock a->dir->i_mutex here -+ * because of d_revalidate() may cause a deadlock. -+ */ -+ sb = a->dir->i_sb; -+ AuDebugOn(!sb); -+ sbinfo = au_sbi(sb); -+ AuDebugOn(!sbinfo); -+ si_write_lock(sb, AuLock_NOPLMW); -+ -+ ii_read_lock_parent(a->dir); -+ bfound = -1; -+ bend = au_ibend(a->dir); -+ for (bindex = au_ibstart(a->dir); bindex <= bend; bindex++) -+ if (au_h_iptr(a->dir, bindex) == a->h_dir) { -+ bfound = bindex; -+ break; -+ } -+ ii_read_unlock(a->dir); -+ if (unlikely(bfound < 0)) -+ goto out; -+ -+ xino = !!au_opt_test(au_mntflags(sb), XINO); -+ h_ino = 0; -+ if (a->h_child_inode) -+ h_ino = a->h_child_inode->i_ino; -+ -+ if (a->h_child_nlen -+ && (au_ftest_hnjob(a->flags[AuHn_CHILD], GEN) -+ || au_ftest_hnjob(a->flags[AuHn_CHILD], MNTPNT))) -+ dentry = lookup_wlock_by_name(a->h_child_name, a->h_child_nlen, -+ a->dir); -+ try_iput = 0; -+ if (dentry) -+ inode = dentry->d_inode; -+ if (xino && !inode && h_ino -+ && (au_ftest_hnjob(a->flags[AuHn_CHILD], XINO0) -+ || au_ftest_hnjob(a->flags[AuHn_CHILD], TRYXINO0) -+ || au_ftest_hnjob(a->flags[AuHn_CHILD], GEN))) { -+ inode = lookup_wlock_by_ino(sb, bfound, h_ino); -+ try_iput = 1; -+ } -+ -+ args.flags = a->flags[AuHn_CHILD]; -+ args.dentry = dentry; -+ args.inode = inode; -+ args.h_inode = a->h_child_inode; -+ args.dir = a->dir; -+ args.h_dir = a->h_dir; -+ args.h_name = a->h_child_name; -+ args.h_nlen = a->h_child_nlen; -+ err = hn_job(&args); -+ if (dentry) { -+ if (au_di(dentry)) -+ di_write_unlock(dentry); -+ dput(dentry); -+ } -+ if (inode && try_iput) { -+ ii_write_unlock(inode); -+ iput(inode); -+ } -+ -+ ii_write_lock_parent(a->dir); -+ args.flags = a->flags[AuHn_PARENT]; -+ args.dentry = NULL; -+ args.inode = a->dir; -+ args.h_inode = a->h_dir; -+ args.dir = NULL; -+ args.h_dir = NULL; -+ args.h_name = NULL; -+ args.h_nlen = 0; -+ err = hn_job(&args); -+ ii_write_unlock(a->dir); -+ -+out: -+ iput(a->h_child_inode); -+ iput(a->h_dir); -+ iput(a->dir); -+ si_write_unlock(sb); -+ au_nwt_done(&sbinfo->si_nowait); -+ kfree(a); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int au_hnotify(struct inode *h_dir, struct au_hnotify *hnotify, u32 mask, -+ struct qstr *h_child_qstr, struct inode *h_child_inode) -+{ -+ int err, len; -+ unsigned int flags[AuHnLast], f; -+ unsigned char isdir, isroot, wh; -+ struct inode *dir; -+ struct au_hnotify_args *args; -+ char *p, *h_child_name; -+ -+ err = 0; -+ AuDebugOn(!hnotify || !hnotify->hn_aufs_inode); -+ dir = igrab(hnotify->hn_aufs_inode); -+ if (!dir) -+ goto out; -+ -+ isroot = (dir->i_ino == AUFS_ROOT_INO); -+ wh = 0; -+ h_child_name = (void *)h_child_qstr->name; -+ len = h_child_qstr->len; -+ if (h_child_name) { -+ if (len > AUFS_WH_PFX_LEN -+ && !memcmp(h_child_name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { -+ h_child_name += AUFS_WH_PFX_LEN; -+ len -= AUFS_WH_PFX_LEN; -+ wh = 1; -+ } -+ } -+ -+ isdir = 0; -+ if (h_child_inode) -+ isdir = !!S_ISDIR(h_child_inode->i_mode); -+ flags[AuHn_PARENT] = AuHnJob_ISDIR; -+ flags[AuHn_CHILD] = 0; -+ if (isdir) -+ flags[AuHn_CHILD] = AuHnJob_ISDIR; -+ au_fset_hnjob(flags[AuHn_PARENT], DIRENT); -+ au_fset_hnjob(flags[AuHn_CHILD], GEN); -+ switch (mask & FS_EVENTS_POSS_ON_CHILD) { -+ case FS_MOVED_FROM: -+ case FS_MOVED_TO: -+ au_fset_hnjob(flags[AuHn_CHILD], XINO0); -+ au_fset_hnjob(flags[AuHn_CHILD], MNTPNT); -+ /*FALLTHROUGH*/ -+ case FS_CREATE: -+ AuDebugOn(!h_child_name); -+ break; -+ -+ case FS_DELETE: -+ /* -+ * aufs never be able to get this child inode. -+ * revalidation should be in d_revalidate() -+ * by checking i_nlink, i_generation or d_unhashed(). -+ */ -+ AuDebugOn(!h_child_name); -+ au_fset_hnjob(flags[AuHn_CHILD], TRYXINO0); -+ au_fset_hnjob(flags[AuHn_CHILD], MNTPNT); -+ break; -+ -+ default: -+ AuDebugOn(1); -+ } -+ -+ if (wh) -+ h_child_inode = NULL; -+ -+ err = -ENOMEM; -+ /* iput() and kfree() will be called in au_hnotify() */ -+ args = kmalloc(sizeof(*args) + len + 1, GFP_NOFS); -+ if (unlikely(!args)) { -+ AuErr1("no memory\n"); -+ iput(dir); -+ goto out; -+ } -+ args->flags[AuHn_PARENT] = flags[AuHn_PARENT]; -+ args->flags[AuHn_CHILD] = flags[AuHn_CHILD]; -+ args->mask = mask; -+ args->dir = dir; -+ args->h_dir = igrab(h_dir); -+ if (h_child_inode) -+ h_child_inode = igrab(h_child_inode); /* can be NULL */ -+ args->h_child_inode = h_child_inode; -+ args->h_child_nlen = len; -+ if (len) { -+ p = (void *)args; -+ p += sizeof(*args); -+ memcpy(p, h_child_name, len); -+ p[len] = 0; -+ } -+ -+ /* NFS fires the event for silly-renamed one from kworker */ -+ f = 0; -+ if (!dir->i_nlink -+ || (au_test_nfs(h_dir->i_sb) && (mask & FS_DELETE))) -+ f = AuWkq_NEST; -+ err = au_wkq_nowait(au_hn_bh, args, dir->i_sb, f); -+ if (unlikely(err)) { -+ pr_err("wkq %d\n", err); -+ iput(args->h_child_inode); -+ iput(args->h_dir); -+ iput(args->dir); -+ kfree(args); -+ } -+ -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int au_hnotify_reset_br(unsigned int udba, struct au_branch *br, int perm) -+{ -+ int err; -+ -+ AuDebugOn(!(udba & AuOptMask_UDBA)); -+ -+ err = 0; -+ if (au_hnotify_op.reset_br) -+ err = au_hnotify_op.reset_br(udba, br, perm); -+ -+ return err; -+} -+ -+int au_hnotify_init_br(struct au_branch *br, int perm) -+{ -+ int err; -+ -+ err = 0; -+ if (au_hnotify_op.init_br) -+ err = au_hnotify_op.init_br(br, perm); -+ -+ return err; -+} -+ -+void au_hnotify_fin_br(struct au_branch *br) -+{ -+ if (au_hnotify_op.fin_br) -+ au_hnotify_op.fin_br(br); -+} -+ -+static void au_hn_destroy_cache(void) -+{ -+ kmem_cache_destroy(au_cachep[AuCache_HNOTIFY]); -+ au_cachep[AuCache_HNOTIFY] = NULL; -+} -+ -+int __init au_hnotify_init(void) -+{ -+ int err; -+ -+ err = -ENOMEM; -+ au_cachep[AuCache_HNOTIFY] = AuCache(au_hnotify); -+ if (au_cachep[AuCache_HNOTIFY]) { -+ err = 0; -+ if (au_hnotify_op.init) -+ err = au_hnotify_op.init(); -+ if (unlikely(err)) -+ au_hn_destroy_cache(); -+ } -+ AuTraceErr(err); -+ return err; -+} -+ -+void au_hnotify_fin(void) -+{ -+ if (au_hnotify_op.fin) -+ au_hnotify_op.fin(); -+ /* cf. au_cache_fin() */ -+ if (au_cachep[AuCache_HNOTIFY]) -+ au_hn_destroy_cache(); -+} -diff --git a/fs/aufs/i_op.c b/fs/aufs/i_op.c -new file mode 100644 -index 0000000..02dc95a ---- /dev/null -+++ b/fs/aufs/i_op.c -@@ -0,0 +1,1460 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * inode operations (except add/del/rename) -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include "aufs.h" -+ -+static int h_permission(struct inode *h_inode, int mask, -+ struct vfsmount *h_mnt, int brperm) -+{ -+ int err; -+ const unsigned char write_mask = !!(mask & (MAY_WRITE | MAY_APPEND)); -+ -+ err = -EACCES; -+ if ((write_mask && IS_IMMUTABLE(h_inode)) -+ || ((mask & MAY_EXEC) -+ && S_ISREG(h_inode->i_mode) -+ && ((h_mnt->mnt_flags & MNT_NOEXEC) -+ || !(h_inode->i_mode & S_IXUGO)))) -+ goto out; -+ -+ /* -+ * - skip the lower fs test in the case of write to ro branch. -+ * - nfs dir permission write check is optimized, but a policy for -+ * link/rename requires a real check. -+ * - nfs always sets MS_POSIXACL regardless its mount option 'noacl.' -+ * in this case, generic_permission() returns -EOPNOTSUPP. -+ */ -+ if ((write_mask && !au_br_writable(brperm)) -+ || (au_test_nfs(h_inode->i_sb) && S_ISDIR(h_inode->i_mode) -+ && write_mask && !(mask & MAY_READ)) -+ || !h_inode->i_op->permission) { -+ /* AuLabel(generic_permission); */ -+ /* AuDbg("get_acl %pf\n", h_inode->i_op->get_acl); */ -+ err = generic_permission(h_inode, mask); -+ if (err == -EOPNOTSUPP && au_test_nfs_noacl(h_inode)) -+ err = h_inode->i_op->permission(h_inode, mask); -+ AuTraceErr(err); -+ } else { -+ /* AuLabel(h_inode->permission); */ -+ err = h_inode->i_op->permission(h_inode, mask); -+ AuTraceErr(err); -+ } -+ -+ if (!err) -+ err = devcgroup_inode_permission(h_inode, mask); -+ if (!err) -+ err = security_inode_permission(h_inode, mask); -+ -+#if 0 -+ if (!err) { -+ /* todo: do we need to call ima_path_check()? */ -+ struct path h_path = { -+ .dentry = -+ .mnt = h_mnt -+ }; -+ err = ima_path_check(&h_path, -+ mask & (MAY_READ | MAY_WRITE | MAY_EXEC), -+ IMA_COUNT_LEAVE); -+ } -+#endif -+ -+out: -+ return err; -+} -+ -+static int aufs_permission(struct inode *inode, int mask) -+{ -+ int err; -+ aufs_bindex_t bindex, bend; -+ const unsigned char isdir = !!S_ISDIR(inode->i_mode), -+ write_mask = !!(mask & (MAY_WRITE | MAY_APPEND)); -+ struct inode *h_inode; -+ struct super_block *sb; -+ struct au_branch *br; -+ -+ /* todo: support rcu-walk? */ -+ if (mask & MAY_NOT_BLOCK) -+ return -ECHILD; -+ -+ sb = inode->i_sb; -+ si_read_lock(sb, AuLock_FLUSH); -+ ii_read_lock_child(inode); -+#if 0 -+ err = au_iigen_test(inode, au_sigen(sb)); -+ if (unlikely(err)) -+ goto out; -+#endif -+ -+ if (!isdir -+ || write_mask -+ || au_opt_test(au_mntflags(sb), DIRPERM1)) { -+ err = au_busy_or_stale(); -+ h_inode = au_h_iptr(inode, au_ibstart(inode)); -+ if (unlikely(!h_inode -+ || (h_inode->i_mode & S_IFMT) -+ != (inode->i_mode & S_IFMT))) -+ goto out; -+ -+ err = 0; -+ bindex = au_ibstart(inode); -+ br = au_sbr(sb, bindex); -+ err = h_permission(h_inode, mask, au_br_mnt(br), br->br_perm); -+ if (write_mask -+ && !err -+ && !special_file(h_inode->i_mode)) { -+ /* test whether the upper writable branch exists */ -+ err = -EROFS; -+ for (; bindex >= 0; bindex--) -+ if (!au_br_rdonly(au_sbr(sb, bindex))) { -+ err = 0; -+ break; -+ } -+ } -+ goto out; -+ } -+ -+ /* non-write to dir */ -+ err = 0; -+ bend = au_ibend(inode); -+ for (bindex = au_ibstart(inode); !err && bindex <= bend; bindex++) { -+ h_inode = au_h_iptr(inode, bindex); -+ if (h_inode) { -+ err = au_busy_or_stale(); -+ if (unlikely(!S_ISDIR(h_inode->i_mode))) -+ break; -+ -+ br = au_sbr(sb, bindex); -+ err = h_permission(h_inode, mask, au_br_mnt(br), -+ br->br_perm); -+ } -+ } -+ -+out: -+ ii_read_unlock(inode); -+ si_read_unlock(sb); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry, -+ unsigned int flags) -+{ -+ struct dentry *ret, *parent; -+ struct inode *inode; -+ struct super_block *sb; -+ int err, npositive; -+ -+ IMustLock(dir); -+ -+ /* todo: support rcu-walk? */ -+ ret = ERR_PTR(-ECHILD); -+ if (flags & LOOKUP_RCU) -+ goto out; -+ -+ ret = ERR_PTR(-ENAMETOOLONG); -+ if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) -+ goto out; -+ -+ sb = dir->i_sb; -+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ ret = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out; -+ -+ err = au_di_init(dentry); -+ ret = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out_si; -+ -+ inode = NULL; -+ npositive = 0; /* suppress a warning */ -+ parent = dentry->d_parent; /* dir inode is locked */ -+ di_read_lock_parent(parent, AuLock_IR); -+ err = au_alive_dir(parent); -+ if (!err) -+ err = au_digen_test(parent, au_sigen(sb)); -+ if (!err) { -+ npositive = au_lkup_dentry(dentry, au_dbstart(parent), -+ /*type*/0); -+ err = npositive; -+ } -+ di_read_unlock(parent, AuLock_IR); -+ ret = ERR_PTR(err); -+ if (unlikely(err < 0)) -+ goto out_unlock; -+ -+ if (npositive) { -+ inode = au_new_inode(dentry, /*must_new*/0); -+ if (IS_ERR(inode)) { -+ ret = (void *)inode; -+ inode = NULL; -+ goto out_unlock; -+ } -+ } -+ -+ if (inode) -+ atomic_inc(&inode->i_count); -+ ret = d_splice_alias(inode, dentry); -+ if (IS_ERR(ret) -+ && PTR_ERR(ret) == -EIO -+ && inode -+ && S_ISDIR(inode->i_mode)) { -+ atomic_inc(&inode->i_count); -+ ret = d_materialise_unique(dentry, inode); -+ if (!IS_ERR(ret)) -+ ii_write_unlock(inode); -+ } -+#if 0 -+ if (unlikely(d_need_lookup(dentry))) { -+ spin_lock(&dentry->d_lock); -+ dentry->d_flags &= ~DCACHE_NEED_LOOKUP; -+ spin_unlock(&dentry->d_lock); -+ } else -+#endif -+ if (inode) { -+ if (!IS_ERR(ret)) -+ iput(inode); -+ else { -+ ii_write_unlock(inode); -+ iput(inode); -+ inode = NULL; -+ } -+ } -+ -+out_unlock: -+ di_write_unlock(dentry); -+ if (inode) { -+ /* verbose coding for lock class name */ -+ if (unlikely(S_ISLNK(inode->i_mode))) -+ au_rw_class(&au_di(dentry)->di_rwsem, -+ au_lc_key + AuLcSymlink_DIINFO); -+ else if (unlikely(S_ISDIR(inode->i_mode))) -+ au_rw_class(&au_di(dentry)->di_rwsem, -+ au_lc_key + AuLcDir_DIINFO); -+ else /* likely */ -+ au_rw_class(&au_di(dentry)->di_rwsem, -+ au_lc_key + AuLcNonDir_DIINFO); -+ } -+out_si: -+ si_read_unlock(sb); -+out: -+ return ret; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct aopen_node { -+ struct hlist_node hlist; -+ struct file *file, *h_file; -+}; -+ -+static int au_do_aopen(struct inode *inode, struct file *file) -+{ -+ struct au_sphlhead *aopen; -+ struct aopen_node *node; -+ struct au_do_open_args args = { -+ .no_lock = 1, -+ .open = au_do_open_nondir -+ }; -+ -+ aopen = &au_sbi(inode->i_sb)->si_aopen; -+ spin_lock(&aopen->spin); -+ hlist_for_each_entry(node, &aopen->head, hlist) -+ if (node->file == file) { -+ args.h_file = node->h_file; -+ break; -+ } -+ spin_unlock(&aopen->spin); -+ /* AuDebugOn(!args.h_file); */ -+ -+ return au_do_open(file, &args); -+} -+ -+static int aufs_atomic_open(struct inode *dir, struct dentry *dentry, -+ struct file *file, unsigned int open_flag, -+ umode_t create_mode, int *opened) -+{ -+ int err, h_opened = *opened; -+ struct dentry *parent; -+ struct dentry *d; -+ struct au_sphlhead *aopen; -+ struct vfsub_aopen_args args = { -+ .open_flag = open_flag, -+ .create_mode = create_mode, -+ .opened = &h_opened -+ }; -+ struct aopen_node aopen_node = { -+ .file = file -+ }; -+ -+ IMustLock(dir); -+ AuDbg("open_flag 0x%x\n", open_flag); -+ AuDbgDentry(dentry); -+ -+ err = 0; -+ if (!au_di(dentry)) { -+ d = aufs_lookup(dir, dentry, /*flags*/0); -+ if (IS_ERR(d)) { -+ err = PTR_ERR(d); -+ goto out; -+ } else if (d) { -+ /* -+ * obsoleted dentry found. -+ * another error will be returned later. -+ */ -+ d_drop(d); -+ dput(d); -+ AuDbgDentry(d); -+ } -+ AuDbgDentry(dentry); -+ } -+ -+ if (d_is_positive(dentry) -+ || d_unhashed(dentry) -+ || d_unlinked(dentry) -+ || !(open_flag & O_CREAT)) -+ goto out_no_open; -+ -+ err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN); -+ if (unlikely(err)) -+ goto out; -+ -+ parent = dentry->d_parent; /* dir is locked */ -+ di_write_lock_parent(parent); -+ err = au_lkup_dentry(dentry, /*bstart*/0, /*type*/0); -+ if (unlikely(err)) -+ goto out_unlock; -+ -+ AuDbgDentry(dentry); -+ if (d_is_positive(dentry)) -+ goto out_unlock; -+ -+ args.file = get_empty_filp(); -+ err = PTR_ERR(args.file); -+ if (IS_ERR(args.file)) -+ goto out_unlock; -+ -+ args.file->f_flags = file->f_flags; -+ err = au_aopen_or_create(dir, dentry, &args); -+ AuTraceErr(err); -+ AuDbgFile(args.file); -+ if (unlikely(err < 0)) { -+ if (h_opened & FILE_OPENED) -+ fput(args.file); -+ else -+ put_filp(args.file); -+ goto out_unlock; -+ } -+ -+ /* some filesystems don't set FILE_CREATED while succeeded? */ -+ *opened |= FILE_CREATED; -+ if (h_opened & FILE_OPENED) -+ aopen_node.h_file = args.file; -+ else { -+ put_filp(args.file); -+ args.file = NULL; -+ } -+ aopen = &au_sbi(dir->i_sb)->si_aopen; -+ au_sphl_add(&aopen_node.hlist, aopen); -+ err = finish_open(file, dentry, au_do_aopen, opened); -+ au_sphl_del(&aopen_node.hlist, aopen); -+ AuTraceErr(err); -+ AuDbgFile(file); -+ if (aopen_node.h_file) -+ fput(aopen_node.h_file); -+ -+out_unlock: -+ di_write_unlock(parent); -+ aufs_read_unlock(dentry, AuLock_DW); -+ AuDbgDentry(dentry); -+ if (unlikely(err)) -+ goto out; -+out_no_open: -+ if (!err && !(*opened & FILE_CREATED)) { -+ AuLabel(out_no_open); -+ dget(dentry); -+ err = finish_no_open(file, dentry); -+ } -+out: -+ AuDbg("%pd%s%s\n", dentry, -+ (*opened & FILE_CREATED) ? " created" : "", -+ (*opened & FILE_OPENED) ? " opened" : ""); -+ AuTraceErr(err); -+ return err; -+} -+ -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_wr_dir_cpup(struct dentry *dentry, struct dentry *parent, -+ const unsigned char add_entry, aufs_bindex_t bcpup, -+ aufs_bindex_t bstart) -+{ -+ int err; -+ struct dentry *h_parent; -+ struct inode *h_dir; -+ -+ if (add_entry) -+ IMustLock(parent->d_inode); -+ else -+ di_write_lock_parent(parent); -+ -+ err = 0; -+ if (!au_h_dptr(parent, bcpup)) { -+ if (bstart > bcpup) -+ err = au_cpup_dirs(dentry, bcpup); -+ else if (bstart < bcpup) -+ err = au_cpdown_dirs(dentry, bcpup); -+ else -+ BUG(); -+ } -+ if (!err && add_entry && !au_ftest_wrdir(add_entry, TMPFILE)) { -+ h_parent = au_h_dptr(parent, bcpup); -+ h_dir = h_parent->d_inode; -+ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); -+ err = au_lkup_neg(dentry, bcpup, /*wh*/0); -+ /* todo: no unlock here */ -+ mutex_unlock(&h_dir->i_mutex); -+ -+ AuDbg("bcpup %d\n", bcpup); -+ if (!err) { -+ if (!dentry->d_inode) -+ au_set_h_dptr(dentry, bstart, NULL); -+ au_update_dbrange(dentry, /*do_put_zero*/0); -+ } -+ } -+ -+ if (!add_entry) -+ di_write_unlock(parent); -+ if (!err) -+ err = bcpup; /* success */ -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+/* -+ * decide the branch and the parent dir where we will create a new entry. -+ * returns new bindex or an error. -+ * copyup the parent dir if needed. -+ */ -+int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry, -+ struct au_wr_dir_args *args) -+{ -+ int err; -+ unsigned int flags; -+ aufs_bindex_t bcpup, bstart, src_bstart; -+ const unsigned char add_entry -+ = au_ftest_wrdir(args->flags, ADD_ENTRY) -+ | au_ftest_wrdir(args->flags, TMPFILE); -+ struct super_block *sb; -+ struct dentry *parent; -+ struct au_sbinfo *sbinfo; -+ -+ sb = dentry->d_sb; -+ sbinfo = au_sbi(sb); -+ parent = dget_parent(dentry); -+ bstart = au_dbstart(dentry); -+ bcpup = bstart; -+ if (args->force_btgt < 0) { -+ if (src_dentry) { -+ src_bstart = au_dbstart(src_dentry); -+ if (src_bstart < bstart) -+ bcpup = src_bstart; -+ } else if (add_entry) { -+ flags = 0; -+ if (au_ftest_wrdir(args->flags, ISDIR)) -+ au_fset_wbr(flags, DIR); -+ err = AuWbrCreate(sbinfo, dentry, flags); -+ bcpup = err; -+ } -+ -+ if (bcpup < 0 || au_test_ro(sb, bcpup, dentry->d_inode)) { -+ if (add_entry) -+ err = AuWbrCopyup(sbinfo, dentry); -+ else { -+ if (!IS_ROOT(dentry)) { -+ di_read_lock_parent(parent, !AuLock_IR); -+ err = AuWbrCopyup(sbinfo, dentry); -+ di_read_unlock(parent, !AuLock_IR); -+ } else -+ err = AuWbrCopyup(sbinfo, dentry); -+ } -+ bcpup = err; -+ if (unlikely(err < 0)) -+ goto out; -+ } -+ } else { -+ bcpup = args->force_btgt; -+ AuDebugOn(au_test_ro(sb, bcpup, dentry->d_inode)); -+ } -+ -+ AuDbg("bstart %d, bcpup %d\n", bstart, bcpup); -+ err = bcpup; -+ if (bcpup == bstart) -+ goto out; /* success */ -+ -+ /* copyup the new parent into the branch we process */ -+ err = au_wr_dir_cpup(dentry, parent, add_entry, bcpup, bstart); -+ if (err >= 0) { -+ if (!dentry->d_inode) { -+ au_set_h_dptr(dentry, bstart, NULL); -+ au_set_dbstart(dentry, bcpup); -+ au_set_dbend(dentry, bcpup); -+ } -+ AuDebugOn(add_entry -+ && !au_ftest_wrdir(args->flags, TMPFILE) -+ && !au_h_dptr(dentry, bcpup)); -+ } -+ -+out: -+ dput(parent); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void au_pin_hdir_unlock(struct au_pin *p) -+{ -+ if (p->hdir) -+ au_hn_imtx_unlock(p->hdir); -+} -+ -+int au_pin_hdir_lock(struct au_pin *p) -+{ -+ int err; -+ -+ err = 0; -+ if (!p->hdir) -+ goto out; -+ -+ /* even if an error happens later, keep this lock */ -+ au_hn_imtx_lock_nested(p->hdir, p->lsc_hi); -+ -+ err = -EBUSY; -+ if (unlikely(p->hdir->hi_inode != p->h_parent->d_inode)) -+ goto out; -+ -+ err = 0; -+ if (p->h_dentry) -+ err = au_h_verify(p->h_dentry, p->udba, p->hdir->hi_inode, -+ p->h_parent, p->br); -+ -+out: -+ return err; -+} -+ -+int au_pin_hdir_relock(struct au_pin *p) -+{ -+ int err, i; -+ struct inode *h_i; -+ struct dentry *h_d[] = { -+ p->h_dentry, -+ p->h_parent -+ }; -+ -+ err = au_pin_hdir_lock(p); -+ if (unlikely(err)) -+ goto out; -+ -+ for (i = 0; !err && i < sizeof(h_d)/sizeof(*h_d); i++) { -+ if (!h_d[i]) -+ continue; -+ h_i = h_d[i]->d_inode; -+ if (h_i) -+ err = !h_i->i_nlink; -+ } -+ -+out: -+ return err; -+} -+ -+void au_pin_hdir_set_owner(struct au_pin *p, struct task_struct *task) -+{ -+#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_SMP) -+ p->hdir->hi_inode->i_mutex.owner = task; -+#endif -+} -+ -+void au_pin_hdir_acquire_nest(struct au_pin *p) -+{ -+ if (p->hdir) { -+ mutex_acquire_nest(&p->hdir->hi_inode->i_mutex.dep_map, -+ p->lsc_hi, 0, NULL, _RET_IP_); -+ au_pin_hdir_set_owner(p, current); -+ } -+} -+ -+void au_pin_hdir_release(struct au_pin *p) -+{ -+ if (p->hdir) { -+ au_pin_hdir_set_owner(p, p->task); -+ mutex_release(&p->hdir->hi_inode->i_mutex.dep_map, 1, _RET_IP_); -+ } -+} -+ -+struct dentry *au_pinned_h_parent(struct au_pin *pin) -+{ -+ if (pin && pin->parent) -+ return au_h_dptr(pin->parent, pin->bindex); -+ return NULL; -+} -+ -+void au_unpin(struct au_pin *p) -+{ -+ if (p->hdir) -+ au_pin_hdir_unlock(p); -+ if (p->h_mnt && au_ftest_pin(p->flags, MNT_WRITE)) -+ vfsub_mnt_drop_write(p->h_mnt); -+ if (!p->hdir) -+ return; -+ -+ if (!au_ftest_pin(p->flags, DI_LOCKED)) -+ di_read_unlock(p->parent, AuLock_IR); -+ iput(p->hdir->hi_inode); -+ dput(p->parent); -+ p->parent = NULL; -+ p->hdir = NULL; -+ p->h_mnt = NULL; -+ /* do not clear p->task */ -+} -+ -+int au_do_pin(struct au_pin *p) -+{ -+ int err; -+ struct super_block *sb; -+ struct inode *h_dir; -+ -+ err = 0; -+ sb = p->dentry->d_sb; -+ p->br = au_sbr(sb, p->bindex); -+ if (IS_ROOT(p->dentry)) { -+ if (au_ftest_pin(p->flags, MNT_WRITE)) { -+ p->h_mnt = au_br_mnt(p->br); -+ err = vfsub_mnt_want_write(p->h_mnt); -+ if (unlikely(err)) { -+ au_fclr_pin(p->flags, MNT_WRITE); -+ goto out_err; -+ } -+ } -+ goto out; -+ } -+ -+ p->h_dentry = NULL; -+ if (p->bindex <= au_dbend(p->dentry)) -+ p->h_dentry = au_h_dptr(p->dentry, p->bindex); -+ -+ p->parent = dget_parent(p->dentry); -+ if (!au_ftest_pin(p->flags, DI_LOCKED)) -+ di_read_lock(p->parent, AuLock_IR, p->lsc_di); -+ -+ h_dir = NULL; -+ p->h_parent = au_h_dptr(p->parent, p->bindex); -+ p->hdir = au_hi(p->parent->d_inode, p->bindex); -+ if (p->hdir) -+ h_dir = p->hdir->hi_inode; -+ -+ /* -+ * udba case, or -+ * if DI_LOCKED is not set, then p->parent may be different -+ * and h_parent can be NULL. -+ */ -+ if (unlikely(!p->hdir || !h_dir || !p->h_parent)) { -+ err = -EBUSY; -+ if (!au_ftest_pin(p->flags, DI_LOCKED)) -+ di_read_unlock(p->parent, AuLock_IR); -+ dput(p->parent); -+ p->parent = NULL; -+ goto out_err; -+ } -+ -+ if (au_ftest_pin(p->flags, MNT_WRITE)) { -+ p->h_mnt = au_br_mnt(p->br); -+ err = vfsub_mnt_want_write(p->h_mnt); -+ if (unlikely(err)) { -+ au_fclr_pin(p->flags, MNT_WRITE); -+ if (!au_ftest_pin(p->flags, DI_LOCKED)) -+ di_read_unlock(p->parent, AuLock_IR); -+ dput(p->parent); -+ p->parent = NULL; -+ goto out_err; -+ } -+ } -+ -+ au_igrab(h_dir); -+ err = au_pin_hdir_lock(p); -+ if (!err) -+ goto out; /* success */ -+ -+ au_unpin(p); -+ -+out_err: -+ pr_err("err %d\n", err); -+ err = au_busy_or_stale(); -+out: -+ return err; -+} -+ -+void au_pin_init(struct au_pin *p, struct dentry *dentry, -+ aufs_bindex_t bindex, int lsc_di, int lsc_hi, -+ unsigned int udba, unsigned char flags) -+{ -+ p->dentry = dentry; -+ p->udba = udba; -+ p->lsc_di = lsc_di; -+ p->lsc_hi = lsc_hi; -+ p->flags = flags; -+ p->bindex = bindex; -+ -+ p->parent = NULL; -+ p->hdir = NULL; -+ p->h_mnt = NULL; -+ -+ p->h_dentry = NULL; -+ p->h_parent = NULL; -+ p->br = NULL; -+ p->task = current; -+} -+ -+int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex, -+ unsigned int udba, unsigned char flags) -+{ -+ au_pin_init(pin, dentry, bindex, AuLsc_DI_PARENT, AuLsc_I_PARENT2, -+ udba, flags); -+ return au_do_pin(pin); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * ->setattr() and ->getattr() are called in various cases. -+ * chmod, stat: dentry is revalidated. -+ * fchmod, fstat: file and dentry are not revalidated, additionally they may be -+ * unhashed. -+ * for ->setattr(), ia->ia_file is passed from ftruncate only. -+ */ -+/* todo: consolidate with do_refresh() and simple_reval_dpath() */ -+int au_reval_for_attr(struct dentry *dentry, unsigned int sigen) -+{ -+ int err; -+ struct inode *inode; -+ struct dentry *parent; -+ -+ err = 0; -+ inode = dentry->d_inode; -+ if (au_digen_test(dentry, sigen)) { -+ parent = dget_parent(dentry); -+ di_read_lock_parent(parent, AuLock_IR); -+ err = au_refresh_dentry(dentry, parent); -+ di_read_unlock(parent, AuLock_IR); -+ dput(parent); -+ } -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia, -+ struct au_icpup_args *a) -+{ -+ int err; -+ loff_t sz; -+ aufs_bindex_t bstart, ibstart; -+ struct dentry *hi_wh, *parent; -+ struct inode *inode; -+ struct au_wr_dir_args wr_dir_args = { -+ .force_btgt = -1, -+ .flags = 0 -+ }; -+ -+ if (d_is_dir(dentry)) -+ au_fset_wrdir(wr_dir_args.flags, ISDIR); -+ /* plink or hi_wh() case */ -+ bstart = au_dbstart(dentry); -+ inode = dentry->d_inode; -+ ibstart = au_ibstart(inode); -+ if (bstart != ibstart && !au_test_ro(inode->i_sb, ibstart, inode)) -+ wr_dir_args.force_btgt = ibstart; -+ err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args); -+ if (unlikely(err < 0)) -+ goto out; -+ a->btgt = err; -+ if (err != bstart) -+ au_fset_icpup(a->flags, DID_CPUP); -+ -+ err = 0; -+ a->pin_flags = AuPin_MNT_WRITE; -+ parent = NULL; -+ if (!IS_ROOT(dentry)) { -+ au_fset_pin(a->pin_flags, DI_LOCKED); -+ parent = dget_parent(dentry); -+ di_write_lock_parent(parent); -+ } -+ -+ err = au_pin(&a->pin, dentry, a->btgt, a->udba, a->pin_flags); -+ if (unlikely(err)) -+ goto out_parent; -+ -+ a->h_path.dentry = au_h_dptr(dentry, bstart); -+ a->h_inode = a->h_path.dentry->d_inode; -+ sz = -1; -+ if (ia && (ia->ia_valid & ATTR_SIZE)) { -+ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); -+ if (ia->ia_size < i_size_read(a->h_inode)) -+ sz = ia->ia_size; -+ mutex_unlock(&a->h_inode->i_mutex); -+ } -+ -+ hi_wh = NULL; -+ if (au_ftest_icpup(a->flags, DID_CPUP) && d_unlinked(dentry)) { -+ hi_wh = au_hi_wh(inode, a->btgt); -+ if (!hi_wh) { -+ struct au_cp_generic cpg = { -+ .dentry = dentry, -+ .bdst = a->btgt, -+ .bsrc = -1, -+ .len = sz, -+ .pin = &a->pin -+ }; -+ err = au_sio_cpup_wh(&cpg, /*file*/NULL); -+ if (unlikely(err)) -+ goto out_unlock; -+ hi_wh = au_hi_wh(inode, a->btgt); -+ /* todo: revalidate hi_wh? */ -+ } -+ } -+ -+ if (parent) { -+ au_pin_set_parent_lflag(&a->pin, /*lflag*/0); -+ di_downgrade_lock(parent, AuLock_IR); -+ dput(parent); -+ parent = NULL; -+ } -+ if (!au_ftest_icpup(a->flags, DID_CPUP)) -+ goto out; /* success */ -+ -+ if (!d_unhashed(dentry)) { -+ struct au_cp_generic cpg = { -+ .dentry = dentry, -+ .bdst = a->btgt, -+ .bsrc = bstart, -+ .len = sz, -+ .pin = &a->pin, -+ .flags = AuCpup_DTIME | AuCpup_HOPEN -+ }; -+ err = au_sio_cpup_simple(&cpg); -+ if (!err) -+ a->h_path.dentry = au_h_dptr(dentry, a->btgt); -+ } else if (!hi_wh) -+ a->h_path.dentry = au_h_dptr(dentry, a->btgt); -+ else -+ a->h_path.dentry = hi_wh; /* do not dget here */ -+ -+out_unlock: -+ a->h_inode = a->h_path.dentry->d_inode; -+ if (!err) -+ goto out; /* success */ -+ au_unpin(&a->pin); -+out_parent: -+ if (parent) { -+ di_write_unlock(parent); -+ dput(parent); -+ } -+out: -+ if (!err) -+ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); -+ return err; -+} -+ -+static int aufs_setattr(struct dentry *dentry, struct iattr *ia) -+{ -+ int err; -+ struct inode *inode, *delegated; -+ struct super_block *sb; -+ struct file *file; -+ struct au_icpup_args *a; -+ -+ inode = dentry->d_inode; -+ IMustLock(inode); -+ -+ err = -ENOMEM; -+ a = kzalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ -+ if (ia->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) -+ ia->ia_valid &= ~ATTR_MODE; -+ -+ file = NULL; -+ sb = dentry->d_sb; -+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ if (unlikely(err)) -+ goto out_kfree; -+ -+ if (ia->ia_valid & ATTR_FILE) { -+ /* currently ftruncate(2) only */ -+ AuDebugOn(!S_ISREG(inode->i_mode)); -+ file = ia->ia_file; -+ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); -+ if (unlikely(err)) -+ goto out_si; -+ ia->ia_file = au_hf_top(file); -+ a->udba = AuOpt_UDBA_NONE; -+ } else { -+ /* fchmod() doesn't pass ia_file */ -+ a->udba = au_opt_udba(sb); -+ di_write_lock_child(dentry); -+ /* no d_unlinked(), to set UDBA_NONE for root */ -+ if (d_unhashed(dentry)) -+ a->udba = AuOpt_UDBA_NONE; -+ if (a->udba != AuOpt_UDBA_NONE) { -+ AuDebugOn(IS_ROOT(dentry)); -+ err = au_reval_for_attr(dentry, au_sigen(sb)); -+ if (unlikely(err)) -+ goto out_dentry; -+ } -+ } -+ -+ err = au_pin_and_icpup(dentry, ia, a); -+ if (unlikely(err < 0)) -+ goto out_dentry; -+ if (au_ftest_icpup(a->flags, DID_CPUP)) { -+ ia->ia_file = NULL; -+ ia->ia_valid &= ~ATTR_FILE; -+ } -+ -+ a->h_path.mnt = au_sbr_mnt(sb, a->btgt); -+ if ((ia->ia_valid & (ATTR_MODE | ATTR_CTIME)) -+ == (ATTR_MODE | ATTR_CTIME)) { -+ err = security_path_chmod(&a->h_path, ia->ia_mode); -+ if (unlikely(err)) -+ goto out_unlock; -+ } else if ((ia->ia_valid & (ATTR_UID | ATTR_GID)) -+ && (ia->ia_valid & ATTR_CTIME)) { -+ err = security_path_chown(&a->h_path, ia->ia_uid, ia->ia_gid); -+ if (unlikely(err)) -+ goto out_unlock; -+ } -+ -+ if (ia->ia_valid & ATTR_SIZE) { -+ struct file *f; -+ -+ if (ia->ia_size < i_size_read(inode)) -+ /* unmap only */ -+ truncate_setsize(inode, ia->ia_size); -+ -+ f = NULL; -+ if (ia->ia_valid & ATTR_FILE) -+ f = ia->ia_file; -+ mutex_unlock(&a->h_inode->i_mutex); -+ err = vfsub_trunc(&a->h_path, ia->ia_size, ia->ia_valid, f); -+ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); -+ } else { -+ delegated = NULL; -+ while (1) { -+ err = vfsub_notify_change(&a->h_path, ia, &delegated); -+ if (delegated) { -+ err = break_deleg_wait(&delegated); -+ if (!err) -+ continue; -+ } -+ break; -+ } -+ } -+ /* -+ * regardless aufs 'acl' option setting. -+ * why don't all acl-aware fs call this func from their ->setattr()? -+ */ -+ if (!err && (ia->ia_valid & ATTR_MODE)) -+ err = vfsub_acl_chmod(a->h_inode, ia->ia_mode); -+ if (!err) -+ au_cpup_attr_changeable(inode); -+ -+out_unlock: -+ mutex_unlock(&a->h_inode->i_mutex); -+ au_unpin(&a->pin); -+ if (unlikely(err)) -+ au_update_dbstart(dentry); -+out_dentry: -+ di_write_unlock(dentry); -+ if (file) { -+ fi_write_unlock(file); -+ ia->ia_file = file; -+ ia->ia_valid |= ATTR_FILE; -+ } -+out_si: -+ si_read_unlock(sb); -+out_kfree: -+ kfree(a); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+#if IS_ENABLED(CONFIG_AUFS_XATTR) || IS_ENABLED(CONFIG_FS_POSIX_ACL) -+static int au_h_path_to_set_attr(struct dentry *dentry, -+ struct au_icpup_args *a, struct path *h_path) -+{ -+ int err; -+ struct super_block *sb; -+ -+ sb = dentry->d_sb; -+ a->udba = au_opt_udba(sb); -+ /* no d_unlinked(), to set UDBA_NONE for root */ -+ if (d_unhashed(dentry)) -+ a->udba = AuOpt_UDBA_NONE; -+ if (a->udba != AuOpt_UDBA_NONE) { -+ AuDebugOn(IS_ROOT(dentry)); -+ err = au_reval_for_attr(dentry, au_sigen(sb)); -+ if (unlikely(err)) -+ goto out; -+ } -+ err = au_pin_and_icpup(dentry, /*ia*/NULL, a); -+ if (unlikely(err < 0)) -+ goto out; -+ -+ h_path->dentry = a->h_path.dentry; -+ h_path->mnt = au_sbr_mnt(sb, a->btgt); -+ -+out: -+ return err; -+} -+ -+ssize_t au_srxattr(struct dentry *dentry, struct au_srxattr *arg) -+{ -+ int err; -+ struct path h_path; -+ struct super_block *sb; -+ struct au_icpup_args *a; -+ struct inode *inode, *h_inode; -+ -+ inode = dentry->d_inode; -+ IMustLock(inode); -+ -+ err = -ENOMEM; -+ a = kzalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ -+ sb = dentry->d_sb; -+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ if (unlikely(err)) -+ goto out_kfree; -+ -+ h_path.dentry = NULL; /* silence gcc */ -+ di_write_lock_child(dentry); -+ err = au_h_path_to_set_attr(dentry, a, &h_path); -+ if (unlikely(err)) -+ goto out_di; -+ -+ mutex_unlock(&a->h_inode->i_mutex); -+ switch (arg->type) { -+ case AU_XATTR_SET: -+ err = vfsub_setxattr(h_path.dentry, -+ arg->u.set.name, arg->u.set.value, -+ arg->u.set.size, arg->u.set.flags); -+ break; -+ case AU_XATTR_REMOVE: -+ err = vfsub_removexattr(h_path.dentry, arg->u.remove.name); -+ break; -+ case AU_ACL_SET: -+ err = -EOPNOTSUPP; -+ h_inode = h_path.dentry->d_inode; -+ if (h_inode->i_op->set_acl) -+ err = h_inode->i_op->set_acl(h_inode, -+ arg->u.acl_set.acl, -+ arg->u.acl_set.type); -+ break; -+ } -+ if (!err) -+ au_cpup_attr_timesizes(inode); -+ -+ au_unpin(&a->pin); -+ if (unlikely(err)) -+ au_update_dbstart(dentry); -+ -+out_di: -+ di_write_unlock(dentry); -+ si_read_unlock(sb); -+out_kfree: -+ kfree(a); -+out: -+ AuTraceErr(err); -+ return err; -+} -+#endif -+ -+static void au_refresh_iattr(struct inode *inode, struct kstat *st, -+ unsigned int nlink) -+{ -+ unsigned int n; -+ -+ inode->i_mode = st->mode; -+ /* don't i_[ug]id_write() here */ -+ inode->i_uid = st->uid; -+ inode->i_gid = st->gid; -+ inode->i_atime = st->atime; -+ inode->i_mtime = st->mtime; -+ inode->i_ctime = st->ctime; -+ -+ au_cpup_attr_nlink(inode, /*force*/0); -+ if (S_ISDIR(inode->i_mode)) { -+ n = inode->i_nlink; -+ n -= nlink; -+ n += st->nlink; -+ smp_mb(); /* for i_nlink */ -+ /* 0 can happen */ -+ set_nlink(inode, n); -+ } -+ -+ spin_lock(&inode->i_lock); -+ inode->i_blocks = st->blocks; -+ i_size_write(inode, st->size); -+ spin_unlock(&inode->i_lock); -+} -+ -+/* -+ * common routine for aufs_getattr() and aufs_getxattr(). -+ * returns zero or negative (an error). -+ * @dentry will be read-locked in success. -+ */ -+int au_h_path_getattr(struct dentry *dentry, int force, struct path *h_path) -+{ -+ int err; -+ unsigned int mnt_flags, sigen; -+ unsigned char udba_none; -+ aufs_bindex_t bindex; -+ struct super_block *sb, *h_sb; -+ struct inode *inode; -+ -+ h_path->mnt = NULL; -+ h_path->dentry = NULL; -+ -+ err = 0; -+ sb = dentry->d_sb; -+ mnt_flags = au_mntflags(sb); -+ udba_none = !!au_opt_test(mnt_flags, UDBA_NONE); -+ -+ /* support fstat(2) */ -+ if (!d_unlinked(dentry) && !udba_none) { -+ sigen = au_sigen(sb); -+ err = au_digen_test(dentry, sigen); -+ if (!err) { -+ di_read_lock_child(dentry, AuLock_IR); -+ err = au_dbrange_test(dentry); -+ if (unlikely(err)) { -+ di_read_unlock(dentry, AuLock_IR); -+ goto out; -+ } -+ } else { -+ AuDebugOn(IS_ROOT(dentry)); -+ di_write_lock_child(dentry); -+ err = au_dbrange_test(dentry); -+ if (!err) -+ err = au_reval_for_attr(dentry, sigen); -+ if (!err) -+ di_downgrade_lock(dentry, AuLock_IR); -+ else { -+ di_write_unlock(dentry); -+ goto out; -+ } -+ } -+ } else -+ di_read_lock_child(dentry, AuLock_IR); -+ -+ inode = dentry->d_inode; -+ bindex = au_ibstart(inode); -+ h_path->mnt = au_sbr_mnt(sb, bindex); -+ h_sb = h_path->mnt->mnt_sb; -+ if (!force -+ && !au_test_fs_bad_iattr(h_sb) -+ && udba_none) -+ goto out; /* success */ -+ -+ if (au_dbstart(dentry) == bindex) -+ h_path->dentry = au_h_dptr(dentry, bindex); -+ else if (au_opt_test(mnt_flags, PLINK) && au_plink_test(inode)) { -+ h_path->dentry = au_plink_lkup(inode, bindex); -+ if (IS_ERR(h_path->dentry)) -+ /* pretending success */ -+ h_path->dentry = NULL; -+ else -+ dput(h_path->dentry); -+ } -+ -+out: -+ return err; -+} -+ -+static int aufs_getattr(struct vfsmount *mnt __maybe_unused, -+ struct dentry *dentry, struct kstat *st) -+{ -+ int err; -+ unsigned char positive; -+ struct path h_path; -+ struct inode *inode; -+ struct super_block *sb; -+ -+ inode = dentry->d_inode; -+ sb = dentry->d_sb; -+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ if (unlikely(err)) -+ goto out; -+ err = au_h_path_getattr(dentry, /*force*/0, &h_path); -+ if (unlikely(err)) -+ goto out_si; -+ if (unlikely(!h_path.dentry)) -+ /* illegally overlapped or something */ -+ goto out_fill; /* pretending success */ -+ -+ positive = !!h_path.dentry->d_inode; -+ if (positive) -+ err = vfs_getattr(&h_path, st); -+ if (!err) { -+ if (positive) -+ au_refresh_iattr(inode, st, -+ h_path.dentry->d_inode->i_nlink); -+ goto out_fill; /* success */ -+ } -+ AuTraceErr(err); -+ goto out_di; -+ -+out_fill: -+ generic_fillattr(inode, st); -+out_di: -+ di_read_unlock(dentry, AuLock_IR); -+out_si: -+ si_read_unlock(sb); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int h_readlink(struct dentry *dentry, int bindex, char __user *buf, -+ int bufsiz) -+{ -+ int err; -+ struct super_block *sb; -+ struct dentry *h_dentry; -+ -+ err = -EINVAL; -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (unlikely(!h_dentry->d_inode->i_op->readlink)) -+ goto out; -+ -+ err = security_inode_readlink(h_dentry); -+ if (unlikely(err)) -+ goto out; -+ -+ sb = dentry->d_sb; -+ if (!au_test_ro(sb, bindex, dentry->d_inode)) { -+ vfsub_touch_atime(au_sbr_mnt(sb, bindex), h_dentry); -+ fsstack_copy_attr_atime(dentry->d_inode, h_dentry->d_inode); -+ } -+ err = h_dentry->d_inode->i_op->readlink(h_dentry, buf, bufsiz); -+ -+out: -+ return err; -+} -+ -+static int aufs_readlink(struct dentry *dentry, char __user *buf, int bufsiz) -+{ -+ int err; -+ -+ err = aufs_read_lock(dentry, AuLock_IR | AuLock_GEN); -+ if (unlikely(err)) -+ goto out; -+ err = au_d_hashed_positive(dentry); -+ if (!err) -+ err = h_readlink(dentry, au_dbstart(dentry), buf, bufsiz); -+ aufs_read_unlock(dentry, AuLock_IR); -+ -+out: -+ return err; -+} -+ -+static void *aufs_follow_link(struct dentry *dentry, struct nameidata *nd) -+{ -+ int err; -+ mm_segment_t old_fs; -+ union { -+ char *k; -+ char __user *u; -+ } buf; -+ -+ err = -ENOMEM; -+ buf.k = (void *)__get_free_page(GFP_NOFS); -+ if (unlikely(!buf.k)) -+ goto out; -+ -+ err = aufs_read_lock(dentry, AuLock_IR | AuLock_GEN); -+ if (unlikely(err)) -+ goto out_name; -+ -+ err = au_d_hashed_positive(dentry); -+ if (!err) { -+ old_fs = get_fs(); -+ set_fs(KERNEL_DS); -+ err = h_readlink(dentry, au_dbstart(dentry), buf.u, PATH_MAX); -+ set_fs(old_fs); -+ } -+ aufs_read_unlock(dentry, AuLock_IR); -+ -+ if (err >= 0) { -+ buf.k[err] = 0; -+ /* will be freed by put_link */ -+ nd_set_link(nd, buf.k); -+ return NULL; /* success */ -+ } -+ -+out_name: -+ free_page((unsigned long)buf.k); -+out: -+ AuTraceErr(err); -+ return ERR_PTR(err); -+} -+ -+static void aufs_put_link(struct dentry *dentry __maybe_unused, -+ struct nameidata *nd, void *cookie __maybe_unused) -+{ -+ char *p; -+ -+ p = nd_get_link(nd); -+ if (!IS_ERR_OR_NULL(p)) -+ free_page((unsigned long)p); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int aufs_update_time(struct inode *inode, struct timespec *ts, int flags) -+{ -+ int err; -+ struct super_block *sb; -+ struct inode *h_inode; -+ -+ sb = inode->i_sb; -+ /* mmap_sem might be acquired already, cf. aufs_mmap() */ -+ lockdep_off(); -+ si_read_lock(sb, AuLock_FLUSH); -+ ii_write_lock_child(inode); -+ lockdep_on(); -+ h_inode = au_h_iptr(inode, au_ibstart(inode)); -+ err = vfsub_update_time(h_inode, ts, flags); -+ lockdep_off(); -+ if (!err) -+ au_cpup_attr_timesizes(inode); -+ ii_write_unlock(inode); -+ si_read_unlock(sb); -+ lockdep_on(); -+ -+ if (!err && (flags & S_VERSION)) -+ inode_inc_iversion(inode); -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* no getattr version will be set by module.c:aufs_init() */ -+struct inode_operations aufs_iop_nogetattr[AuIop_Last], -+ aufs_iop[] = { -+ [AuIop_SYMLINK] = { -+ .permission = aufs_permission, -+#ifdef CONFIG_FS_POSIX_ACL -+ .get_acl = aufs_get_acl, -+ .set_acl = aufs_set_acl, /* unsupport for symlink? */ -+#endif -+ -+ .setattr = aufs_setattr, -+ .getattr = aufs_getattr, -+ -+#ifdef CONFIG_AUFS_XATTR -+ .setxattr = aufs_setxattr, -+ .getxattr = aufs_getxattr, -+ .listxattr = aufs_listxattr, -+ .removexattr = aufs_removexattr, -+#endif -+ -+ .readlink = aufs_readlink, -+ .follow_link = aufs_follow_link, -+ .put_link = aufs_put_link, -+ -+ /* .update_time = aufs_update_time */ -+ }, -+ [AuIop_DIR] = { -+ .create = aufs_create, -+ .lookup = aufs_lookup, -+ .link = aufs_link, -+ .unlink = aufs_unlink, -+ .symlink = aufs_symlink, -+ .mkdir = aufs_mkdir, -+ .rmdir = aufs_rmdir, -+ .mknod = aufs_mknod, -+ .rename = aufs_rename, -+ -+ .permission = aufs_permission, -+#ifdef CONFIG_FS_POSIX_ACL -+ .get_acl = aufs_get_acl, -+ .set_acl = aufs_set_acl, -+#endif -+ -+ .setattr = aufs_setattr, -+ .getattr = aufs_getattr, -+ -+#ifdef CONFIG_AUFS_XATTR -+ .setxattr = aufs_setxattr, -+ .getxattr = aufs_getxattr, -+ .listxattr = aufs_listxattr, -+ .removexattr = aufs_removexattr, -+#endif -+ -+ .update_time = aufs_update_time, -+ .atomic_open = aufs_atomic_open, -+ .tmpfile = aufs_tmpfile -+ }, -+ [AuIop_OTHER] = { -+ .permission = aufs_permission, -+#ifdef CONFIG_FS_POSIX_ACL -+ .get_acl = aufs_get_acl, -+ .set_acl = aufs_set_acl, -+#endif -+ -+ .setattr = aufs_setattr, -+ .getattr = aufs_getattr, -+ -+#ifdef CONFIG_AUFS_XATTR -+ .setxattr = aufs_setxattr, -+ .getxattr = aufs_getxattr, -+ .listxattr = aufs_listxattr, -+ .removexattr = aufs_removexattr, -+#endif -+ -+ .update_time = aufs_update_time -+ } -+}; -diff --git a/fs/aufs/i_op_add.c b/fs/aufs/i_op_add.c -new file mode 100644 -index 0000000..9e4f65c ---- /dev/null -+++ b/fs/aufs/i_op_add.c -@@ -0,0 +1,930 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * inode operations (add entry) -+ */ -+ -+#include "aufs.h" -+ -+/* -+ * final procedure of adding a new entry, except link(2). -+ * remove whiteout, instantiate, copyup the parent dir's times and size -+ * and update version. -+ * if it failed, re-create the removed whiteout. -+ */ -+static int epilog(struct inode *dir, aufs_bindex_t bindex, -+ struct dentry *wh_dentry, struct dentry *dentry) -+{ -+ int err, rerr; -+ aufs_bindex_t bwh; -+ struct path h_path; -+ struct super_block *sb; -+ struct inode *inode, *h_dir; -+ struct dentry *wh; -+ -+ bwh = -1; -+ sb = dir->i_sb; -+ if (wh_dentry) { -+ h_dir = wh_dentry->d_parent->d_inode; /* dir inode is locked */ -+ IMustLock(h_dir); -+ AuDebugOn(au_h_iptr(dir, bindex) != h_dir); -+ bwh = au_dbwh(dentry); -+ h_path.dentry = wh_dentry; -+ h_path.mnt = au_sbr_mnt(sb, bindex); -+ err = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path, -+ dentry); -+ if (unlikely(err)) -+ goto out; -+ } -+ -+ inode = au_new_inode(dentry, /*must_new*/1); -+ if (!IS_ERR(inode)) { -+ d_instantiate(dentry, inode); -+ dir = dentry->d_parent->d_inode; /* dir inode is locked */ -+ IMustLock(dir); -+ au_dir_ts(dir, bindex); -+ dir->i_version++; -+ au_fhsm_wrote(sb, bindex, /*force*/0); -+ return 0; /* success */ -+ } -+ -+ err = PTR_ERR(inode); -+ if (!wh_dentry) -+ goto out; -+ -+ /* revert */ -+ /* dir inode is locked */ -+ wh = au_wh_create(dentry, bwh, wh_dentry->d_parent); -+ rerr = PTR_ERR(wh); -+ if (IS_ERR(wh)) { -+ AuIOErr("%pd reverting whiteout failed(%d, %d)\n", -+ dentry, err, rerr); -+ err = -EIO; -+ } else -+ dput(wh); -+ -+out: -+ return err; -+} -+ -+static int au_d_may_add(struct dentry *dentry) -+{ -+ int err; -+ -+ err = 0; -+ if (unlikely(d_unhashed(dentry))) -+ err = -ENOENT; -+ if (unlikely(dentry->d_inode)) -+ err = -EEXIST; -+ return err; -+} -+ -+/* -+ * simple tests for the adding inode operations. -+ * following the checks in vfs, plus the parent-child relationship. -+ */ -+int au_may_add(struct dentry *dentry, aufs_bindex_t bindex, -+ struct dentry *h_parent, int isdir) -+{ -+ int err; -+ umode_t h_mode; -+ struct dentry *h_dentry; -+ struct inode *h_inode; -+ -+ err = -ENAMETOOLONG; -+ if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) -+ goto out; -+ -+ h_dentry = au_h_dptr(dentry, bindex); -+ h_inode = h_dentry->d_inode; -+ if (!dentry->d_inode) { -+ err = -EEXIST; -+ if (unlikely(h_inode)) -+ goto out; -+ } else { -+ /* rename(2) case */ -+ err = -EIO; -+ if (unlikely(!h_inode || !h_inode->i_nlink)) -+ goto out; -+ -+ h_mode = h_inode->i_mode; -+ if (!isdir) { -+ err = -EISDIR; -+ if (unlikely(S_ISDIR(h_mode))) -+ goto out; -+ } else if (unlikely(!S_ISDIR(h_mode))) { -+ err = -ENOTDIR; -+ goto out; -+ } -+ } -+ -+ err = 0; -+ /* expected parent dir is locked */ -+ if (unlikely(h_parent != h_dentry->d_parent)) -+ err = -EIO; -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* -+ * initial procedure of adding a new entry. -+ * prepare writable branch and the parent dir, lock it, -+ * and lookup whiteout for the new entry. -+ */ -+static struct dentry* -+lock_hdir_lkup_wh(struct dentry *dentry, struct au_dtime *dt, -+ struct dentry *src_dentry, struct au_pin *pin, -+ struct au_wr_dir_args *wr_dir_args) -+{ -+ struct dentry *wh_dentry, *h_parent; -+ struct super_block *sb; -+ struct au_branch *br; -+ int err; -+ unsigned int udba; -+ aufs_bindex_t bcpup; -+ -+ AuDbg("%pd\n", dentry); -+ -+ err = au_wr_dir(dentry, src_dentry, wr_dir_args); -+ bcpup = err; -+ wh_dentry = ERR_PTR(err); -+ if (unlikely(err < 0)) -+ goto out; -+ -+ sb = dentry->d_sb; -+ udba = au_opt_udba(sb); -+ err = au_pin(pin, dentry, bcpup, udba, -+ AuPin_DI_LOCKED | AuPin_MNT_WRITE); -+ wh_dentry = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out; -+ -+ h_parent = au_pinned_h_parent(pin); -+ if (udba != AuOpt_UDBA_NONE -+ && au_dbstart(dentry) == bcpup) -+ err = au_may_add(dentry, bcpup, h_parent, -+ au_ftest_wrdir(wr_dir_args->flags, ISDIR)); -+ else if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) -+ err = -ENAMETOOLONG; -+ wh_dentry = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out_unpin; -+ -+ br = au_sbr(sb, bcpup); -+ if (dt) { -+ struct path tmp = { -+ .dentry = h_parent, -+ .mnt = au_br_mnt(br) -+ }; -+ au_dtime_store(dt, au_pinned_parent(pin), &tmp); -+ } -+ -+ wh_dentry = NULL; -+ if (bcpup != au_dbwh(dentry)) -+ goto out; /* success */ -+ -+ /* -+ * ENAMETOOLONG here means that if we allowed create such name, then it -+ * would not be able to removed in the future. So we don't allow such -+ * name here and we don't handle ENAMETOOLONG differently here. -+ */ -+ wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, br); -+ -+out_unpin: -+ if (IS_ERR(wh_dentry)) -+ au_unpin(pin); -+out: -+ return wh_dentry; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+enum { Mknod, Symlink, Creat }; -+struct simple_arg { -+ int type; -+ union { -+ struct { -+ umode_t mode; -+ bool want_excl; -+ bool try_aopen; -+ struct vfsub_aopen_args *aopen; -+ } c; -+ struct { -+ const char *symname; -+ } s; -+ struct { -+ umode_t mode; -+ dev_t dev; -+ } m; -+ } u; -+}; -+ -+static int add_simple(struct inode *dir, struct dentry *dentry, -+ struct simple_arg *arg) -+{ -+ int err, rerr; -+ aufs_bindex_t bstart; -+ unsigned char created; -+ const unsigned char try_aopen -+ = (arg->type == Creat && arg->u.c.try_aopen); -+ struct dentry *wh_dentry, *parent; -+ struct inode *h_dir; -+ struct super_block *sb; -+ struct au_branch *br; -+ /* to reuduce stack size */ -+ struct { -+ struct au_dtime dt; -+ struct au_pin pin; -+ struct path h_path; -+ struct au_wr_dir_args wr_dir_args; -+ } *a; -+ -+ AuDbg("%pd\n", dentry); -+ IMustLock(dir); -+ -+ err = -ENOMEM; -+ a = kmalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ a->wr_dir_args.force_btgt = -1; -+ a->wr_dir_args.flags = AuWrDir_ADD_ENTRY; -+ -+ parent = dentry->d_parent; /* dir inode is locked */ -+ if (!try_aopen) { -+ err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); -+ if (unlikely(err)) -+ goto out_free; -+ } -+ err = au_d_may_add(dentry); -+ if (unlikely(err)) -+ goto out_unlock; -+ if (!try_aopen) -+ di_write_lock_parent(parent); -+ wh_dentry = lock_hdir_lkup_wh(dentry, &a->dt, /*src_dentry*/NULL, -+ &a->pin, &a->wr_dir_args); -+ err = PTR_ERR(wh_dentry); -+ if (IS_ERR(wh_dentry)) -+ goto out_parent; -+ -+ bstart = au_dbstart(dentry); -+ sb = dentry->d_sb; -+ br = au_sbr(sb, bstart); -+ a->h_path.dentry = au_h_dptr(dentry, bstart); -+ a->h_path.mnt = au_br_mnt(br); -+ h_dir = au_pinned_h_dir(&a->pin); -+ switch (arg->type) { -+ case Creat: -+ err = 0; -+ if (!try_aopen || !h_dir->i_op->atomic_open) -+ err = vfsub_create(h_dir, &a->h_path, arg->u.c.mode, -+ arg->u.c.want_excl); -+ else -+ err = vfsub_atomic_open(h_dir, a->h_path.dentry, -+ arg->u.c.aopen, br); -+ break; -+ case Symlink: -+ err = vfsub_symlink(h_dir, &a->h_path, arg->u.s.symname); -+ break; -+ case Mknod: -+ err = vfsub_mknod(h_dir, &a->h_path, arg->u.m.mode, -+ arg->u.m.dev); -+ break; -+ default: -+ BUG(); -+ } -+ created = !err; -+ if (!err) -+ err = epilog(dir, bstart, wh_dentry, dentry); -+ -+ /* revert */ -+ if (unlikely(created && err && a->h_path.dentry->d_inode)) { -+ /* no delegation since it is just created */ -+ rerr = vfsub_unlink(h_dir, &a->h_path, /*delegated*/NULL, -+ /*force*/0); -+ if (rerr) { -+ AuIOErr("%pd revert failure(%d, %d)\n", -+ dentry, err, rerr); -+ err = -EIO; -+ } -+ au_dtime_revert(&a->dt); -+ } -+ -+ if (!err && try_aopen && !h_dir->i_op->atomic_open) -+ *arg->u.c.aopen->opened |= FILE_CREATED; -+ -+ au_unpin(&a->pin); -+ dput(wh_dentry); -+ -+out_parent: -+ if (!try_aopen) -+ di_write_unlock(parent); -+out_unlock: -+ if (unlikely(err)) { -+ au_update_dbstart(dentry); -+ d_drop(dentry); -+ } -+ if (!try_aopen) -+ aufs_read_unlock(dentry, AuLock_DW); -+out_free: -+ kfree(a); -+out: -+ return err; -+} -+ -+int aufs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, -+ dev_t dev) -+{ -+ struct simple_arg arg = { -+ .type = Mknod, -+ .u.m = { -+ .mode = mode, -+ .dev = dev -+ } -+ }; -+ return add_simple(dir, dentry, &arg); -+} -+ -+int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) -+{ -+ struct simple_arg arg = { -+ .type = Symlink, -+ .u.s.symname = symname -+ }; -+ return add_simple(dir, dentry, &arg); -+} -+ -+int aufs_create(struct inode *dir, struct dentry *dentry, umode_t mode, -+ bool want_excl) -+{ -+ struct simple_arg arg = { -+ .type = Creat, -+ .u.c = { -+ .mode = mode, -+ .want_excl = want_excl -+ } -+ }; -+ return add_simple(dir, dentry, &arg); -+} -+ -+int au_aopen_or_create(struct inode *dir, struct dentry *dentry, -+ struct vfsub_aopen_args *aopen_args) -+{ -+ struct simple_arg arg = { -+ .type = Creat, -+ .u.c = { -+ .mode = aopen_args->create_mode, -+ .want_excl = aopen_args->open_flag & O_EXCL, -+ .try_aopen = true, -+ .aopen = aopen_args -+ } -+ }; -+ return add_simple(dir, dentry, &arg); -+} -+ -+int aufs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) -+{ -+ int err; -+ aufs_bindex_t bindex; -+ struct super_block *sb; -+ struct dentry *parent, *h_parent, *h_dentry; -+ struct inode *h_dir, *inode; -+ struct vfsmount *h_mnt; -+ struct au_wr_dir_args wr_dir_args = { -+ .force_btgt = -1, -+ .flags = AuWrDir_TMPFILE -+ }; -+ -+ /* copy-up may happen */ -+ mutex_lock(&dir->i_mutex); -+ -+ sb = dir->i_sb; -+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ if (unlikely(err)) -+ goto out; -+ -+ err = au_di_init(dentry); -+ if (unlikely(err)) -+ goto out_si; -+ -+ err = -EBUSY; -+ parent = d_find_any_alias(dir); -+ AuDebugOn(!parent); -+ di_write_lock_parent(parent); -+ if (unlikely(parent->d_inode != dir)) -+ goto out_parent; -+ -+ err = au_digen_test(parent, au_sigen(sb)); -+ if (unlikely(err)) -+ goto out_parent; -+ -+ bindex = au_dbstart(parent); -+ au_set_dbstart(dentry, bindex); -+ au_set_dbend(dentry, bindex); -+ err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args); -+ bindex = err; -+ if (unlikely(err < 0)) -+ goto out_parent; -+ -+ err = -EOPNOTSUPP; -+ h_dir = au_h_iptr(dir, bindex); -+ if (unlikely(!h_dir->i_op->tmpfile)) -+ goto out_parent; -+ -+ h_mnt = au_sbr_mnt(sb, bindex); -+ err = vfsub_mnt_want_write(h_mnt); -+ if (unlikely(err)) -+ goto out_parent; -+ -+ h_parent = au_h_dptr(parent, bindex); -+ err = inode_permission(h_parent->d_inode, MAY_WRITE | MAY_EXEC); -+ if (unlikely(err)) -+ goto out_mnt; -+ -+ err = -ENOMEM; -+ h_dentry = d_alloc(h_parent, &dentry->d_name); -+ if (unlikely(!h_dentry)) -+ goto out_mnt; -+ -+ err = h_dir->i_op->tmpfile(h_dir, h_dentry, mode); -+ if (unlikely(err)) -+ goto out_dentry; -+ -+ au_set_dbstart(dentry, bindex); -+ au_set_dbend(dentry, bindex); -+ au_set_h_dptr(dentry, bindex, dget(h_dentry)); -+ inode = au_new_inode(dentry, /*must_new*/1); -+ if (IS_ERR(inode)) { -+ err = PTR_ERR(inode); -+ au_set_h_dptr(dentry, bindex, NULL); -+ au_set_dbstart(dentry, -1); -+ au_set_dbend(dentry, -1); -+ } else { -+ if (!inode->i_nlink) -+ set_nlink(inode, 1); -+ d_tmpfile(dentry, inode); -+ au_di(dentry)->di_tmpfile = 1; -+ -+ /* update without i_mutex */ -+ if (au_ibstart(dir) == au_dbstart(dentry)) -+ au_cpup_attr_timesizes(dir); -+ } -+ -+out_dentry: -+ dput(h_dentry); -+out_mnt: -+ vfsub_mnt_drop_write(h_mnt); -+out_parent: -+ di_write_unlock(parent); -+ dput(parent); -+ di_write_unlock(dentry); -+ if (!err) -+#if 0 -+ /* verbose coding for lock class name */ -+ au_rw_class(&au_di(dentry)->di_rwsem, -+ au_lc_key + AuLcNonDir_DIINFO); -+#else -+ ; -+#endif -+ else { -+ au_di_fin(dentry); -+ dentry->d_fsdata = NULL; -+ } -+out_si: -+ si_read_unlock(sb); -+out: -+ mutex_unlock(&dir->i_mutex); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct au_link_args { -+ aufs_bindex_t bdst, bsrc; -+ struct au_pin pin; -+ struct path h_path; -+ struct dentry *src_parent, *parent; -+}; -+ -+static int au_cpup_before_link(struct dentry *src_dentry, -+ struct au_link_args *a) -+{ -+ int err; -+ struct dentry *h_src_dentry; -+ struct au_cp_generic cpg = { -+ .dentry = src_dentry, -+ .bdst = a->bdst, -+ .bsrc = a->bsrc, -+ .len = -1, -+ .pin = &a->pin, -+ .flags = AuCpup_DTIME | AuCpup_HOPEN /* | AuCpup_KEEPLINO */ -+ }; -+ -+ di_read_lock_parent(a->src_parent, AuLock_IR); -+ err = au_test_and_cpup_dirs(src_dentry, a->bdst); -+ if (unlikely(err)) -+ goto out; -+ -+ h_src_dentry = au_h_dptr(src_dentry, a->bsrc); -+ err = au_pin(&a->pin, src_dentry, a->bdst, -+ au_opt_udba(src_dentry->d_sb), -+ AuPin_DI_LOCKED | AuPin_MNT_WRITE); -+ if (unlikely(err)) -+ goto out; -+ -+ err = au_sio_cpup_simple(&cpg); -+ au_unpin(&a->pin); -+ -+out: -+ di_read_unlock(a->src_parent, AuLock_IR); -+ return err; -+} -+ -+static int au_cpup_or_link(struct dentry *src_dentry, struct dentry *dentry, -+ struct au_link_args *a) -+{ -+ int err; -+ unsigned char plink; -+ aufs_bindex_t bend; -+ struct dentry *h_src_dentry; -+ struct inode *h_inode, *inode, *delegated; -+ struct super_block *sb; -+ struct file *h_file; -+ -+ plink = 0; -+ h_inode = NULL; -+ sb = src_dentry->d_sb; -+ inode = src_dentry->d_inode; -+ if (au_ibstart(inode) <= a->bdst) -+ h_inode = au_h_iptr(inode, a->bdst); -+ if (!h_inode || !h_inode->i_nlink) { -+ /* copyup src_dentry as the name of dentry. */ -+ bend = au_dbend(dentry); -+ if (bend < a->bsrc) -+ au_set_dbend(dentry, a->bsrc); -+ au_set_h_dptr(dentry, a->bsrc, -+ dget(au_h_dptr(src_dentry, a->bsrc))); -+ dget(a->h_path.dentry); -+ au_set_h_dptr(dentry, a->bdst, NULL); -+ AuDbg("temporary d_inode...\n"); -+ spin_lock(&dentry->d_lock); -+ dentry->d_inode = src_dentry->d_inode; /* tmp */ -+ spin_unlock(&dentry->d_lock); -+ h_file = au_h_open_pre(dentry, a->bsrc, /*force_wr*/0); -+ if (IS_ERR(h_file)) -+ err = PTR_ERR(h_file); -+ else { -+ struct au_cp_generic cpg = { -+ .dentry = dentry, -+ .bdst = a->bdst, -+ .bsrc = -1, -+ .len = -1, -+ .pin = &a->pin, -+ .flags = AuCpup_KEEPLINO -+ }; -+ err = au_sio_cpup_simple(&cpg); -+ au_h_open_post(dentry, a->bsrc, h_file); -+ if (!err) { -+ dput(a->h_path.dentry); -+ a->h_path.dentry = au_h_dptr(dentry, a->bdst); -+ } else -+ au_set_h_dptr(dentry, a->bdst, -+ a->h_path.dentry); -+ } -+ spin_lock(&dentry->d_lock); -+ dentry->d_inode = NULL; /* restore */ -+ spin_unlock(&dentry->d_lock); -+ AuDbg("temporary d_inode...done\n"); -+ au_set_h_dptr(dentry, a->bsrc, NULL); -+ au_set_dbend(dentry, bend); -+ } else { -+ /* the inode of src_dentry already exists on a.bdst branch */ -+ h_src_dentry = d_find_alias(h_inode); -+ if (!h_src_dentry && au_plink_test(inode)) { -+ plink = 1; -+ h_src_dentry = au_plink_lkup(inode, a->bdst); -+ err = PTR_ERR(h_src_dentry); -+ if (IS_ERR(h_src_dentry)) -+ goto out; -+ -+ if (unlikely(!h_src_dentry->d_inode)) { -+ dput(h_src_dentry); -+ h_src_dentry = NULL; -+ } -+ -+ } -+ if (h_src_dentry) { -+ delegated = NULL; -+ err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin), -+ &a->h_path, &delegated); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal link\n"); -+ iput(delegated); -+ } -+ dput(h_src_dentry); -+ } else { -+ AuIOErr("no dentry found for hi%lu on b%d\n", -+ h_inode->i_ino, a->bdst); -+ err = -EIO; -+ } -+ } -+ -+ if (!err && !plink) -+ au_plink_append(inode, a->bdst, a->h_path.dentry); -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+int aufs_link(struct dentry *src_dentry, struct inode *dir, -+ struct dentry *dentry) -+{ -+ int err, rerr; -+ struct au_dtime dt; -+ struct au_link_args *a; -+ struct dentry *wh_dentry, *h_src_dentry; -+ struct inode *inode, *delegated; -+ struct super_block *sb; -+ struct au_wr_dir_args wr_dir_args = { -+ /* .force_btgt = -1, */ -+ .flags = AuWrDir_ADD_ENTRY -+ }; -+ -+ IMustLock(dir); -+ inode = src_dentry->d_inode; -+ IMustLock(inode); -+ -+ err = -ENOMEM; -+ a = kzalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ -+ a->parent = dentry->d_parent; /* dir inode is locked */ -+ err = aufs_read_and_write_lock2(dentry, src_dentry, -+ AuLock_NOPLM | AuLock_GEN); -+ if (unlikely(err)) -+ goto out_kfree; -+ err = au_d_linkable(src_dentry); -+ if (unlikely(err)) -+ goto out_unlock; -+ err = au_d_may_add(dentry); -+ if (unlikely(err)) -+ goto out_unlock; -+ -+ a->src_parent = dget_parent(src_dentry); -+ wr_dir_args.force_btgt = au_ibstart(inode); -+ -+ di_write_lock_parent(a->parent); -+ wr_dir_args.force_btgt = au_wbr(dentry, wr_dir_args.force_btgt); -+ wh_dentry = lock_hdir_lkup_wh(dentry, &dt, src_dentry, &a->pin, -+ &wr_dir_args); -+ err = PTR_ERR(wh_dentry); -+ if (IS_ERR(wh_dentry)) -+ goto out_parent; -+ -+ err = 0; -+ sb = dentry->d_sb; -+ a->bdst = au_dbstart(dentry); -+ a->h_path.dentry = au_h_dptr(dentry, a->bdst); -+ a->h_path.mnt = au_sbr_mnt(sb, a->bdst); -+ a->bsrc = au_ibstart(inode); -+ h_src_dentry = au_h_d_alias(src_dentry, a->bsrc); -+ if (!h_src_dentry && au_di(src_dentry)->di_tmpfile) -+ h_src_dentry = dget(au_hi_wh(inode, a->bsrc)); -+ if (!h_src_dentry) { -+ a->bsrc = au_dbstart(src_dentry); -+ h_src_dentry = au_h_d_alias(src_dentry, a->bsrc); -+ AuDebugOn(!h_src_dentry); -+ } else if (IS_ERR(h_src_dentry)) { -+ err = PTR_ERR(h_src_dentry); -+ goto out_parent; -+ } -+ -+ if (au_opt_test(au_mntflags(sb), PLINK)) { -+ if (a->bdst < a->bsrc -+ /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */) -+ err = au_cpup_or_link(src_dentry, dentry, a); -+ else { -+ delegated = NULL; -+ err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin), -+ &a->h_path, &delegated); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal link\n"); -+ iput(delegated); -+ } -+ } -+ dput(h_src_dentry); -+ } else { -+ /* -+ * copyup src_dentry to the branch we process, -+ * and then link(2) to it. -+ */ -+ dput(h_src_dentry); -+ if (a->bdst < a->bsrc -+ /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */) { -+ au_unpin(&a->pin); -+ di_write_unlock(a->parent); -+ err = au_cpup_before_link(src_dentry, a); -+ di_write_lock_parent(a->parent); -+ if (!err) -+ err = au_pin(&a->pin, dentry, a->bdst, -+ au_opt_udba(sb), -+ AuPin_DI_LOCKED | AuPin_MNT_WRITE); -+ if (unlikely(err)) -+ goto out_wh; -+ } -+ if (!err) { -+ h_src_dentry = au_h_dptr(src_dentry, a->bdst); -+ err = -ENOENT; -+ if (h_src_dentry && h_src_dentry->d_inode) { -+ delegated = NULL; -+ err = vfsub_link(h_src_dentry, -+ au_pinned_h_dir(&a->pin), -+ &a->h_path, &delegated); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry" -+ " for NFSv4 delegation" -+ " for an internal link\n"); -+ iput(delegated); -+ } -+ } -+ } -+ } -+ if (unlikely(err)) -+ goto out_unpin; -+ -+ if (wh_dentry) { -+ a->h_path.dentry = wh_dentry; -+ err = au_wh_unlink_dentry(au_pinned_h_dir(&a->pin), &a->h_path, -+ dentry); -+ if (unlikely(err)) -+ goto out_revert; -+ } -+ -+ au_dir_ts(dir, a->bdst); -+ dir->i_version++; -+ inc_nlink(inode); -+ inode->i_ctime = dir->i_ctime; -+ d_instantiate(dentry, au_igrab(inode)); -+ if (d_unhashed(a->h_path.dentry)) -+ /* some filesystem calls d_drop() */ -+ d_drop(dentry); -+ /* some filesystems consume an inode even hardlink */ -+ au_fhsm_wrote(sb, a->bdst, /*force*/0); -+ goto out_unpin; /* success */ -+ -+out_revert: -+ /* no delegation since it is just created */ -+ rerr = vfsub_unlink(au_pinned_h_dir(&a->pin), &a->h_path, -+ /*delegated*/NULL, /*force*/0); -+ if (unlikely(rerr)) { -+ AuIOErr("%pd reverting failed(%d, %d)\n", dentry, err, rerr); -+ err = -EIO; -+ } -+ au_dtime_revert(&dt); -+out_unpin: -+ au_unpin(&a->pin); -+out_wh: -+ dput(wh_dentry); -+out_parent: -+ di_write_unlock(a->parent); -+ dput(a->src_parent); -+out_unlock: -+ if (unlikely(err)) { -+ au_update_dbstart(dentry); -+ d_drop(dentry); -+ } -+ aufs_read_and_write_unlock2(dentry, src_dentry); -+out_kfree: -+ kfree(a); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+int aufs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) -+{ -+ int err, rerr; -+ aufs_bindex_t bindex; -+ unsigned char diropq; -+ struct path h_path; -+ struct dentry *wh_dentry, *parent, *opq_dentry; -+ struct mutex *h_mtx; -+ struct super_block *sb; -+ struct { -+ struct au_pin pin; -+ struct au_dtime dt; -+ } *a; /* reduce the stack usage */ -+ struct au_wr_dir_args wr_dir_args = { -+ .force_btgt = -1, -+ .flags = AuWrDir_ADD_ENTRY | AuWrDir_ISDIR -+ }; -+ -+ IMustLock(dir); -+ -+ err = -ENOMEM; -+ a = kmalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ -+ err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); -+ if (unlikely(err)) -+ goto out_free; -+ err = au_d_may_add(dentry); -+ if (unlikely(err)) -+ goto out_unlock; -+ -+ parent = dentry->d_parent; /* dir inode is locked */ -+ di_write_lock_parent(parent); -+ wh_dentry = lock_hdir_lkup_wh(dentry, &a->dt, /*src_dentry*/NULL, -+ &a->pin, &wr_dir_args); -+ err = PTR_ERR(wh_dentry); -+ if (IS_ERR(wh_dentry)) -+ goto out_parent; -+ -+ sb = dentry->d_sb; -+ bindex = au_dbstart(dentry); -+ h_path.dentry = au_h_dptr(dentry, bindex); -+ h_path.mnt = au_sbr_mnt(sb, bindex); -+ err = vfsub_mkdir(au_pinned_h_dir(&a->pin), &h_path, mode); -+ if (unlikely(err)) -+ goto out_unpin; -+ -+ /* make the dir opaque */ -+ diropq = 0; -+ h_mtx = &h_path.dentry->d_inode->i_mutex; -+ if (wh_dentry -+ || au_opt_test(au_mntflags(sb), ALWAYS_DIROPQ)) { -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); -+ opq_dentry = au_diropq_create(dentry, bindex); -+ mutex_unlock(h_mtx); -+ err = PTR_ERR(opq_dentry); -+ if (IS_ERR(opq_dentry)) -+ goto out_dir; -+ dput(opq_dentry); -+ diropq = 1; -+ } -+ -+ err = epilog(dir, bindex, wh_dentry, dentry); -+ if (!err) { -+ inc_nlink(dir); -+ goto out_unpin; /* success */ -+ } -+ -+ /* revert */ -+ if (diropq) { -+ AuLabel(revert opq); -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); -+ rerr = au_diropq_remove(dentry, bindex); -+ mutex_unlock(h_mtx); -+ if (rerr) { -+ AuIOErr("%pd reverting diropq failed(%d, %d)\n", -+ dentry, err, rerr); -+ err = -EIO; -+ } -+ } -+ -+out_dir: -+ AuLabel(revert dir); -+ rerr = vfsub_rmdir(au_pinned_h_dir(&a->pin), &h_path); -+ if (rerr) { -+ AuIOErr("%pd reverting dir failed(%d, %d)\n", -+ dentry, err, rerr); -+ err = -EIO; -+ } -+ au_dtime_revert(&a->dt); -+out_unpin: -+ au_unpin(&a->pin); -+ dput(wh_dentry); -+out_parent: -+ di_write_unlock(parent); -+out_unlock: -+ if (unlikely(err)) { -+ au_update_dbstart(dentry); -+ d_drop(dentry); -+ } -+ aufs_read_unlock(dentry, AuLock_DW); -+out_free: -+ kfree(a); -+out: -+ return err; -+} -diff --git a/fs/aufs/i_op_del.c b/fs/aufs/i_op_del.c -new file mode 100644 -index 0000000..b4dd686 ---- /dev/null -+++ b/fs/aufs/i_op_del.c -@@ -0,0 +1,506 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * inode operations (del entry) -+ */ -+ -+#include "aufs.h" -+ -+/* -+ * decide if a new whiteout for @dentry is necessary or not. -+ * when it is necessary, prepare the parent dir for the upper branch whose -+ * branch index is @bcpup for creation. the actual creation of the whiteout will -+ * be done by caller. -+ * return value: -+ * 0: wh is unnecessary -+ * plus: wh is necessary -+ * minus: error -+ */ -+int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup) -+{ -+ int need_wh, err; -+ aufs_bindex_t bstart; -+ struct super_block *sb; -+ -+ sb = dentry->d_sb; -+ bstart = au_dbstart(dentry); -+ if (*bcpup < 0) { -+ *bcpup = bstart; -+ if (au_test_ro(sb, bstart, dentry->d_inode)) { -+ err = AuWbrCopyup(au_sbi(sb), dentry); -+ *bcpup = err; -+ if (unlikely(err < 0)) -+ goto out; -+ } -+ } else -+ AuDebugOn(bstart < *bcpup -+ || au_test_ro(sb, *bcpup, dentry->d_inode)); -+ AuDbg("bcpup %d, bstart %d\n", *bcpup, bstart); -+ -+ if (*bcpup != bstart) { -+ err = au_cpup_dirs(dentry, *bcpup); -+ if (unlikely(err)) -+ goto out; -+ need_wh = 1; -+ } else { -+ struct au_dinfo *dinfo, *tmp; -+ -+ need_wh = -ENOMEM; -+ dinfo = au_di(dentry); -+ tmp = au_di_alloc(sb, AuLsc_DI_TMP); -+ if (tmp) { -+ au_di_cp(tmp, dinfo); -+ au_di_swap(tmp, dinfo); -+ /* returns the number of positive dentries */ -+ need_wh = au_lkup_dentry(dentry, bstart + 1, /*type*/0); -+ au_di_swap(tmp, dinfo); -+ au_rw_write_unlock(&tmp->di_rwsem); -+ au_di_free(tmp); -+ } -+ } -+ AuDbg("need_wh %d\n", need_wh); -+ err = need_wh; -+ -+out: -+ return err; -+} -+ -+/* -+ * simple tests for the del-entry operations. -+ * following the checks in vfs, plus the parent-child relationship. -+ */ -+int au_may_del(struct dentry *dentry, aufs_bindex_t bindex, -+ struct dentry *h_parent, int isdir) -+{ -+ int err; -+ umode_t h_mode; -+ struct dentry *h_dentry, *h_latest; -+ struct inode *h_inode; -+ -+ h_dentry = au_h_dptr(dentry, bindex); -+ h_inode = h_dentry->d_inode; -+ if (dentry->d_inode) { -+ err = -ENOENT; -+ if (unlikely(!h_inode || !h_inode->i_nlink)) -+ goto out; -+ -+ h_mode = h_inode->i_mode; -+ if (!isdir) { -+ err = -EISDIR; -+ if (unlikely(S_ISDIR(h_mode))) -+ goto out; -+ } else if (unlikely(!S_ISDIR(h_mode))) { -+ err = -ENOTDIR; -+ goto out; -+ } -+ } else { -+ /* rename(2) case */ -+ err = -EIO; -+ if (unlikely(h_inode)) -+ goto out; -+ } -+ -+ err = -ENOENT; -+ /* expected parent dir is locked */ -+ if (unlikely(h_parent != h_dentry->d_parent)) -+ goto out; -+ err = 0; -+ -+ /* -+ * rmdir a dir may break the consistency on some filesystem. -+ * let's try heavy test. -+ */ -+ err = -EACCES; -+ if (unlikely(!au_opt_test(au_mntflags(dentry->d_sb), DIRPERM1) -+ && au_test_h_perm(h_parent->d_inode, -+ MAY_EXEC | MAY_WRITE))) -+ goto out; -+ -+ h_latest = au_sio_lkup_one(&dentry->d_name, h_parent); -+ err = -EIO; -+ if (IS_ERR(h_latest)) -+ goto out; -+ if (h_latest == h_dentry) -+ err = 0; -+ dput(h_latest); -+ -+out: -+ return err; -+} -+ -+/* -+ * decide the branch where we operate for @dentry. the branch index will be set -+ * @rbcpup. after diciding it, 'pin' it and store the timestamps of the parent -+ * dir for reverting. -+ * when a new whiteout is necessary, create it. -+ */ -+static struct dentry* -+lock_hdir_create_wh(struct dentry *dentry, int isdir, aufs_bindex_t *rbcpup, -+ struct au_dtime *dt, struct au_pin *pin) -+{ -+ struct dentry *wh_dentry; -+ struct super_block *sb; -+ struct path h_path; -+ int err, need_wh; -+ unsigned int udba; -+ aufs_bindex_t bcpup; -+ -+ need_wh = au_wr_dir_need_wh(dentry, isdir, rbcpup); -+ wh_dentry = ERR_PTR(need_wh); -+ if (unlikely(need_wh < 0)) -+ goto out; -+ -+ sb = dentry->d_sb; -+ udba = au_opt_udba(sb); -+ bcpup = *rbcpup; -+ err = au_pin(pin, dentry, bcpup, udba, -+ AuPin_DI_LOCKED | AuPin_MNT_WRITE); -+ wh_dentry = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out; -+ -+ h_path.dentry = au_pinned_h_parent(pin); -+ if (udba != AuOpt_UDBA_NONE -+ && au_dbstart(dentry) == bcpup) { -+ err = au_may_del(dentry, bcpup, h_path.dentry, isdir); -+ wh_dentry = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out_unpin; -+ } -+ -+ h_path.mnt = au_sbr_mnt(sb, bcpup); -+ au_dtime_store(dt, au_pinned_parent(pin), &h_path); -+ wh_dentry = NULL; -+ if (!need_wh) -+ goto out; /* success, no need to create whiteout */ -+ -+ wh_dentry = au_wh_create(dentry, bcpup, h_path.dentry); -+ if (IS_ERR(wh_dentry)) -+ goto out_unpin; -+ -+ /* returns with the parent is locked and wh_dentry is dget-ed */ -+ goto out; /* success */ -+ -+out_unpin: -+ au_unpin(pin); -+out: -+ return wh_dentry; -+} -+ -+/* -+ * when removing a dir, rename it to a unique temporary whiteout-ed name first -+ * in order to be revertible and save time for removing many child whiteouts -+ * under the dir. -+ * returns 1 when there are too many child whiteout and caller should remove -+ * them asynchronously. returns 0 when the number of children is enough small to -+ * remove now or the branch fs is a remote fs. -+ * otherwise return an error. -+ */ -+static int renwh_and_rmdir(struct dentry *dentry, aufs_bindex_t bindex, -+ struct au_nhash *whlist, struct inode *dir) -+{ -+ int rmdir_later, err, dirwh; -+ struct dentry *h_dentry; -+ struct super_block *sb; -+ -+ sb = dentry->d_sb; -+ SiMustAnyLock(sb); -+ h_dentry = au_h_dptr(dentry, bindex); -+ err = au_whtmp_ren(h_dentry, au_sbr(sb, bindex)); -+ if (unlikely(err)) -+ goto out; -+ -+ /* stop monitoring */ -+ au_hn_free(au_hi(dentry->d_inode, bindex)); -+ -+ if (!au_test_fs_remote(h_dentry->d_sb)) { -+ dirwh = au_sbi(sb)->si_dirwh; -+ rmdir_later = (dirwh <= 1); -+ if (!rmdir_later) -+ rmdir_later = au_nhash_test_longer_wh(whlist, bindex, -+ dirwh); -+ if (rmdir_later) -+ return rmdir_later; -+ } -+ -+ err = au_whtmp_rmdir(dir, bindex, h_dentry, whlist); -+ if (unlikely(err)) { -+ AuIOErr("rmdir %pd, b%d failed, %d. ignored\n", -+ h_dentry, bindex, err); -+ err = 0; -+ } -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* -+ * final procedure for deleting a entry. -+ * maintain dentry and iattr. -+ */ -+static void epilog(struct inode *dir, struct dentry *dentry, -+ aufs_bindex_t bindex) -+{ -+ struct inode *inode; -+ -+ inode = dentry->d_inode; -+ d_drop(dentry); -+ inode->i_ctime = dir->i_ctime; -+ -+ au_dir_ts(dir, bindex); -+ dir->i_version++; -+} -+ -+/* -+ * when an error happened, remove the created whiteout and revert everything. -+ */ -+static int do_revert(int err, struct inode *dir, aufs_bindex_t bindex, -+ aufs_bindex_t bwh, struct dentry *wh_dentry, -+ struct dentry *dentry, struct au_dtime *dt) -+{ -+ int rerr; -+ struct path h_path = { -+ .dentry = wh_dentry, -+ .mnt = au_sbr_mnt(dir->i_sb, bindex) -+ }; -+ -+ rerr = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path, dentry); -+ if (!rerr) { -+ au_set_dbwh(dentry, bwh); -+ au_dtime_revert(dt); -+ return 0; -+ } -+ -+ AuIOErr("%pd reverting whiteout failed(%d, %d)\n", dentry, err, rerr); -+ return -EIO; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int aufs_unlink(struct inode *dir, struct dentry *dentry) -+{ -+ int err; -+ aufs_bindex_t bwh, bindex, bstart; -+ struct inode *inode, *h_dir, *delegated; -+ struct dentry *parent, *wh_dentry; -+ /* to reuduce stack size */ -+ struct { -+ struct au_dtime dt; -+ struct au_pin pin; -+ struct path h_path; -+ } *a; -+ -+ IMustLock(dir); -+ -+ err = -ENOMEM; -+ a = kmalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ -+ err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); -+ if (unlikely(err)) -+ goto out_free; -+ err = au_d_hashed_positive(dentry); -+ if (unlikely(err)) -+ goto out_unlock; -+ inode = dentry->d_inode; -+ IMustLock(inode); -+ err = -EISDIR; -+ if (unlikely(d_is_dir(dentry))) -+ goto out_unlock; /* possible? */ -+ -+ bstart = au_dbstart(dentry); -+ bwh = au_dbwh(dentry); -+ bindex = -1; -+ parent = dentry->d_parent; /* dir inode is locked */ -+ di_write_lock_parent(parent); -+ wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/0, &bindex, &a->dt, -+ &a->pin); -+ err = PTR_ERR(wh_dentry); -+ if (IS_ERR(wh_dentry)) -+ goto out_parent; -+ -+ a->h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart); -+ a->h_path.dentry = au_h_dptr(dentry, bstart); -+ dget(a->h_path.dentry); -+ if (bindex == bstart) { -+ h_dir = au_pinned_h_dir(&a->pin); -+ delegated = NULL; -+ err = vfsub_unlink(h_dir, &a->h_path, &delegated, /*force*/0); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal unlink\n"); -+ iput(delegated); -+ } -+ } else { -+ /* dir inode is locked */ -+ h_dir = wh_dentry->d_parent->d_inode; -+ IMustLock(h_dir); -+ err = 0; -+ } -+ -+ if (!err) { -+ vfsub_drop_nlink(inode); -+ epilog(dir, dentry, bindex); -+ -+ /* update target timestamps */ -+ if (bindex == bstart) { -+ vfsub_update_h_iattr(&a->h_path, /*did*/NULL); -+ /*ignore*/ -+ inode->i_ctime = a->h_path.dentry->d_inode->i_ctime; -+ } else -+ /* todo: this timestamp may be reverted later */ -+ inode->i_ctime = h_dir->i_ctime; -+ goto out_unpin; /* success */ -+ } -+ -+ /* revert */ -+ if (wh_dentry) { -+ int rerr; -+ -+ rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, -+ &a->dt); -+ if (rerr) -+ err = rerr; -+ } -+ -+out_unpin: -+ au_unpin(&a->pin); -+ dput(wh_dentry); -+ dput(a->h_path.dentry); -+out_parent: -+ di_write_unlock(parent); -+out_unlock: -+ aufs_read_unlock(dentry, AuLock_DW); -+out_free: -+ kfree(a); -+out: -+ return err; -+} -+ -+int aufs_rmdir(struct inode *dir, struct dentry *dentry) -+{ -+ int err, rmdir_later; -+ aufs_bindex_t bwh, bindex, bstart; -+ struct inode *inode; -+ struct dentry *parent, *wh_dentry, *h_dentry; -+ struct au_whtmp_rmdir *args; -+ /* to reuduce stack size */ -+ struct { -+ struct au_dtime dt; -+ struct au_pin pin; -+ } *a; -+ -+ IMustLock(dir); -+ -+ err = -ENOMEM; -+ a = kmalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ -+ err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN); -+ if (unlikely(err)) -+ goto out_free; -+ err = au_alive_dir(dentry); -+ if (unlikely(err)) -+ goto out_unlock; -+ inode = dentry->d_inode; -+ IMustLock(inode); -+ err = -ENOTDIR; -+ if (unlikely(!d_is_dir(dentry))) -+ goto out_unlock; /* possible? */ -+ -+ err = -ENOMEM; -+ args = au_whtmp_rmdir_alloc(dir->i_sb, GFP_NOFS); -+ if (unlikely(!args)) -+ goto out_unlock; -+ -+ parent = dentry->d_parent; /* dir inode is locked */ -+ di_write_lock_parent(parent); -+ err = au_test_empty(dentry, &args->whlist); -+ if (unlikely(err)) -+ goto out_parent; -+ -+ bstart = au_dbstart(dentry); -+ bwh = au_dbwh(dentry); -+ bindex = -1; -+ wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/1, &bindex, &a->dt, -+ &a->pin); -+ err = PTR_ERR(wh_dentry); -+ if (IS_ERR(wh_dentry)) -+ goto out_parent; -+ -+ h_dentry = au_h_dptr(dentry, bstart); -+ dget(h_dentry); -+ rmdir_later = 0; -+ if (bindex == bstart) { -+ err = renwh_and_rmdir(dentry, bstart, &args->whlist, dir); -+ if (err > 0) { -+ rmdir_later = err; -+ err = 0; -+ } -+ } else { -+ /* stop monitoring */ -+ au_hn_free(au_hi(inode, bstart)); -+ -+ /* dir inode is locked */ -+ IMustLock(wh_dentry->d_parent->d_inode); -+ err = 0; -+ } -+ -+ if (!err) { -+ vfsub_dead_dir(inode); -+ au_set_dbdiropq(dentry, -1); -+ epilog(dir, dentry, bindex); -+ -+ if (rmdir_later) { -+ au_whtmp_kick_rmdir(dir, bstart, h_dentry, args); -+ args = NULL; -+ } -+ -+ goto out_unpin; /* success */ -+ } -+ -+ /* revert */ -+ AuLabel(revert); -+ if (wh_dentry) { -+ int rerr; -+ -+ rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, -+ &a->dt); -+ if (rerr) -+ err = rerr; -+ } -+ -+out_unpin: -+ au_unpin(&a->pin); -+ dput(wh_dentry); -+ dput(h_dentry); -+out_parent: -+ di_write_unlock(parent); -+ if (args) -+ au_whtmp_rmdir_free(args); -+out_unlock: -+ aufs_read_unlock(dentry, AuLock_DW); -+out_free: -+ kfree(a); -+out: -+ AuTraceErr(err); -+ return err; -+} -diff --git a/fs/aufs/i_op_ren.c b/fs/aufs/i_op_ren.c -new file mode 100644 -index 0000000..6ce2ed6 ---- /dev/null -+++ b/fs/aufs/i_op_ren.c -@@ -0,0 +1,1013 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * inode operation (rename entry) -+ * todo: this is crazy monster -+ */ -+ -+#include "aufs.h" -+ -+enum { AuSRC, AuDST, AuSrcDst }; -+enum { AuPARENT, AuCHILD, AuParentChild }; -+ -+#define AuRen_ISDIR 1 -+#define AuRen_ISSAMEDIR (1 << 1) -+#define AuRen_WHSRC (1 << 2) -+#define AuRen_WHDST (1 << 3) -+#define AuRen_MNT_WRITE (1 << 4) -+#define AuRen_DT_DSTDIR (1 << 5) -+#define AuRen_DIROPQ (1 << 6) -+#define au_ftest_ren(flags, name) ((flags) & AuRen_##name) -+#define au_fset_ren(flags, name) \ -+ do { (flags) |= AuRen_##name; } while (0) -+#define au_fclr_ren(flags, name) \ -+ do { (flags) &= ~AuRen_##name; } while (0) -+ -+struct au_ren_args { -+ struct { -+ struct dentry *dentry, *h_dentry, *parent, *h_parent, -+ *wh_dentry; -+ struct inode *dir, *inode; -+ struct au_hinode *hdir; -+ struct au_dtime dt[AuParentChild]; -+ aufs_bindex_t bstart; -+ } sd[AuSrcDst]; -+ -+#define src_dentry sd[AuSRC].dentry -+#define src_dir sd[AuSRC].dir -+#define src_inode sd[AuSRC].inode -+#define src_h_dentry sd[AuSRC].h_dentry -+#define src_parent sd[AuSRC].parent -+#define src_h_parent sd[AuSRC].h_parent -+#define src_wh_dentry sd[AuSRC].wh_dentry -+#define src_hdir sd[AuSRC].hdir -+#define src_h_dir sd[AuSRC].hdir->hi_inode -+#define src_dt sd[AuSRC].dt -+#define src_bstart sd[AuSRC].bstart -+ -+#define dst_dentry sd[AuDST].dentry -+#define dst_dir sd[AuDST].dir -+#define dst_inode sd[AuDST].inode -+#define dst_h_dentry sd[AuDST].h_dentry -+#define dst_parent sd[AuDST].parent -+#define dst_h_parent sd[AuDST].h_parent -+#define dst_wh_dentry sd[AuDST].wh_dentry -+#define dst_hdir sd[AuDST].hdir -+#define dst_h_dir sd[AuDST].hdir->hi_inode -+#define dst_dt sd[AuDST].dt -+#define dst_bstart sd[AuDST].bstart -+ -+ struct dentry *h_trap; -+ struct au_branch *br; -+ struct au_hinode *src_hinode; -+ struct path h_path; -+ struct au_nhash whlist; -+ aufs_bindex_t btgt, src_bwh, src_bdiropq; -+ -+ unsigned int flags; -+ -+ struct au_whtmp_rmdir *thargs; -+ struct dentry *h_dst; -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * functions for reverting. -+ * when an error happened in a single rename systemcall, we should revert -+ * everything as if nothing happened. -+ * we don't need to revert the copied-up/down the parent dir since they are -+ * harmless. -+ */ -+ -+#define RevertFailure(fmt, ...) do { \ -+ AuIOErr("revert failure: " fmt " (%d, %d)\n", \ -+ ##__VA_ARGS__, err, rerr); \ -+ err = -EIO; \ -+} while (0) -+ -+static void au_ren_rev_diropq(int err, struct au_ren_args *a) -+{ -+ int rerr; -+ -+ au_hn_imtx_lock_nested(a->src_hinode, AuLsc_I_CHILD); -+ rerr = au_diropq_remove(a->src_dentry, a->btgt); -+ au_hn_imtx_unlock(a->src_hinode); -+ au_set_dbdiropq(a->src_dentry, a->src_bdiropq); -+ if (rerr) -+ RevertFailure("remove diropq %pd", a->src_dentry); -+} -+ -+static void au_ren_rev_rename(int err, struct au_ren_args *a) -+{ -+ int rerr; -+ struct inode *delegated; -+ -+ a->h_path.dentry = vfsub_lkup_one(&a->src_dentry->d_name, -+ a->src_h_parent); -+ rerr = PTR_ERR(a->h_path.dentry); -+ if (IS_ERR(a->h_path.dentry)) { -+ RevertFailure("lkup one %pd", a->src_dentry); -+ return; -+ } -+ -+ delegated = NULL; -+ rerr = vfsub_rename(a->dst_h_dir, -+ au_h_dptr(a->src_dentry, a->btgt), -+ a->src_h_dir, &a->h_path, &delegated); -+ if (unlikely(rerr == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal rename\n"); -+ iput(delegated); -+ } -+ d_drop(a->h_path.dentry); -+ dput(a->h_path.dentry); -+ /* au_set_h_dptr(a->src_dentry, a->btgt, NULL); */ -+ if (rerr) -+ RevertFailure("rename %pd", a->src_dentry); -+} -+ -+static void au_ren_rev_whtmp(int err, struct au_ren_args *a) -+{ -+ int rerr; -+ struct inode *delegated; -+ -+ a->h_path.dentry = vfsub_lkup_one(&a->dst_dentry->d_name, -+ a->dst_h_parent); -+ rerr = PTR_ERR(a->h_path.dentry); -+ if (IS_ERR(a->h_path.dentry)) { -+ RevertFailure("lkup one %pd", a->dst_dentry); -+ return; -+ } -+ if (a->h_path.dentry->d_inode) { -+ d_drop(a->h_path.dentry); -+ dput(a->h_path.dentry); -+ return; -+ } -+ -+ delegated = NULL; -+ rerr = vfsub_rename(a->dst_h_dir, a->h_dst, a->dst_h_dir, &a->h_path, -+ &delegated); -+ if (unlikely(rerr == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal rename\n"); -+ iput(delegated); -+ } -+ d_drop(a->h_path.dentry); -+ dput(a->h_path.dentry); -+ if (!rerr) -+ au_set_h_dptr(a->dst_dentry, a->btgt, dget(a->h_dst)); -+ else -+ RevertFailure("rename %pd", a->h_dst); -+} -+ -+static void au_ren_rev_whsrc(int err, struct au_ren_args *a) -+{ -+ int rerr; -+ -+ a->h_path.dentry = a->src_wh_dentry; -+ rerr = au_wh_unlink_dentry(a->src_h_dir, &a->h_path, a->src_dentry); -+ au_set_dbwh(a->src_dentry, a->src_bwh); -+ if (rerr) -+ RevertFailure("unlink %pd", a->src_wh_dentry); -+} -+#undef RevertFailure -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * when we have to copyup the renaming entry, do it with the rename-target name -+ * in order to minimize the cost (the later actual rename is unnecessary). -+ * otherwise rename it on the target branch. -+ */ -+static int au_ren_or_cpup(struct au_ren_args *a) -+{ -+ int err; -+ struct dentry *d; -+ struct inode *delegated; -+ -+ d = a->src_dentry; -+ if (au_dbstart(d) == a->btgt) { -+ a->h_path.dentry = a->dst_h_dentry; -+ if (au_ftest_ren(a->flags, DIROPQ) -+ && au_dbdiropq(d) == a->btgt) -+ au_fclr_ren(a->flags, DIROPQ); -+ AuDebugOn(au_dbstart(d) != a->btgt); -+ delegated = NULL; -+ err = vfsub_rename(a->src_h_dir, au_h_dptr(d, a->btgt), -+ a->dst_h_dir, &a->h_path, &delegated); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal rename\n"); -+ iput(delegated); -+ } -+ } else -+ BUG(); -+ -+ if (!err && a->h_dst) -+ /* it will be set to dinfo later */ -+ dget(a->h_dst); -+ -+ return err; -+} -+ -+/* cf. aufs_rmdir() */ -+static int au_ren_del_whtmp(struct au_ren_args *a) -+{ -+ int err; -+ struct inode *dir; -+ -+ dir = a->dst_dir; -+ SiMustAnyLock(dir->i_sb); -+ if (!au_nhash_test_longer_wh(&a->whlist, a->btgt, -+ au_sbi(dir->i_sb)->si_dirwh) -+ || au_test_fs_remote(a->h_dst->d_sb)) { -+ err = au_whtmp_rmdir(dir, a->btgt, a->h_dst, &a->whlist); -+ if (unlikely(err)) -+ pr_warn("failed removing whtmp dir %pd (%d), " -+ "ignored.\n", a->h_dst, err); -+ } else { -+ au_nhash_wh_free(&a->thargs->whlist); -+ a->thargs->whlist = a->whlist; -+ a->whlist.nh_num = 0; -+ au_whtmp_kick_rmdir(dir, a->btgt, a->h_dst, a->thargs); -+ dput(a->h_dst); -+ a->thargs = NULL; -+ } -+ -+ return 0; -+} -+ -+/* make it 'opaque' dir. */ -+static int au_ren_diropq(struct au_ren_args *a) -+{ -+ int err; -+ struct dentry *diropq; -+ -+ err = 0; -+ a->src_bdiropq = au_dbdiropq(a->src_dentry); -+ a->src_hinode = au_hi(a->src_inode, a->btgt); -+ au_hn_imtx_lock_nested(a->src_hinode, AuLsc_I_CHILD); -+ diropq = au_diropq_create(a->src_dentry, a->btgt); -+ au_hn_imtx_unlock(a->src_hinode); -+ if (IS_ERR(diropq)) -+ err = PTR_ERR(diropq); -+ else -+ dput(diropq); -+ -+ return err; -+} -+ -+static int do_rename(struct au_ren_args *a) -+{ -+ int err; -+ struct dentry *d, *h_d; -+ -+ /* prepare workqueue args for asynchronous rmdir */ -+ h_d = a->dst_h_dentry; -+ if (au_ftest_ren(a->flags, ISDIR) && h_d->d_inode) { -+ err = -ENOMEM; -+ a->thargs = au_whtmp_rmdir_alloc(a->src_dentry->d_sb, GFP_NOFS); -+ if (unlikely(!a->thargs)) -+ goto out; -+ a->h_dst = dget(h_d); -+ } -+ -+ /* create whiteout for src_dentry */ -+ if (au_ftest_ren(a->flags, WHSRC)) { -+ a->src_bwh = au_dbwh(a->src_dentry); -+ AuDebugOn(a->src_bwh >= 0); -+ a->src_wh_dentry -+ = au_wh_create(a->src_dentry, a->btgt, a->src_h_parent); -+ err = PTR_ERR(a->src_wh_dentry); -+ if (IS_ERR(a->src_wh_dentry)) -+ goto out_thargs; -+ } -+ -+ /* lookup whiteout for dentry */ -+ if (au_ftest_ren(a->flags, WHDST)) { -+ h_d = au_wh_lkup(a->dst_h_parent, &a->dst_dentry->d_name, -+ a->br); -+ err = PTR_ERR(h_d); -+ if (IS_ERR(h_d)) -+ goto out_whsrc; -+ if (!h_d->d_inode) -+ dput(h_d); -+ else -+ a->dst_wh_dentry = h_d; -+ } -+ -+ /* rename dentry to tmpwh */ -+ if (a->thargs) { -+ err = au_whtmp_ren(a->dst_h_dentry, a->br); -+ if (unlikely(err)) -+ goto out_whdst; -+ -+ d = a->dst_dentry; -+ au_set_h_dptr(d, a->btgt, NULL); -+ err = au_lkup_neg(d, a->btgt, /*wh*/0); -+ if (unlikely(err)) -+ goto out_whtmp; -+ a->dst_h_dentry = au_h_dptr(d, a->btgt); -+ } -+ -+ BUG_ON(a->dst_h_dentry->d_inode && a->src_bstart != a->btgt); -+ -+ /* rename by vfs_rename or cpup */ -+ d = a->dst_dentry; -+ if (au_ftest_ren(a->flags, ISDIR) -+ && (a->dst_wh_dentry -+ || au_dbdiropq(d) == a->btgt -+ /* hide the lower to keep xino */ -+ || a->btgt < au_dbend(d) -+ || au_opt_test(au_mntflags(d->d_sb), ALWAYS_DIROPQ))) -+ au_fset_ren(a->flags, DIROPQ); -+ err = au_ren_or_cpup(a); -+ if (unlikely(err)) -+ /* leave the copied-up one */ -+ goto out_whtmp; -+ -+ /* make dir opaque */ -+ if (au_ftest_ren(a->flags, DIROPQ)) { -+ err = au_ren_diropq(a); -+ if (unlikely(err)) -+ goto out_rename; -+ } -+ -+ /* update target timestamps */ -+ AuDebugOn(au_dbstart(a->src_dentry) != a->btgt); -+ a->h_path.dentry = au_h_dptr(a->src_dentry, a->btgt); -+ vfsub_update_h_iattr(&a->h_path, /*did*/NULL); /*ignore*/ -+ a->src_inode->i_ctime = a->h_path.dentry->d_inode->i_ctime; -+ -+ /* remove whiteout for dentry */ -+ if (a->dst_wh_dentry) { -+ a->h_path.dentry = a->dst_wh_dentry; -+ err = au_wh_unlink_dentry(a->dst_h_dir, &a->h_path, -+ a->dst_dentry); -+ if (unlikely(err)) -+ goto out_diropq; -+ } -+ -+ /* remove whtmp */ -+ if (a->thargs) -+ au_ren_del_whtmp(a); /* ignore this error */ -+ -+ au_fhsm_wrote(a->src_dentry->d_sb, a->btgt, /*force*/0); -+ err = 0; -+ goto out_success; -+ -+out_diropq: -+ if (au_ftest_ren(a->flags, DIROPQ)) -+ au_ren_rev_diropq(err, a); -+out_rename: -+ au_ren_rev_rename(err, a); -+ dput(a->h_dst); -+out_whtmp: -+ if (a->thargs) -+ au_ren_rev_whtmp(err, a); -+out_whdst: -+ dput(a->dst_wh_dentry); -+ a->dst_wh_dentry = NULL; -+out_whsrc: -+ if (a->src_wh_dentry) -+ au_ren_rev_whsrc(err, a); -+out_success: -+ dput(a->src_wh_dentry); -+ dput(a->dst_wh_dentry); -+out_thargs: -+ if (a->thargs) { -+ dput(a->h_dst); -+ au_whtmp_rmdir_free(a->thargs); -+ a->thargs = NULL; -+ } -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * test if @dentry dir can be rename destination or not. -+ * success means, it is a logically empty dir. -+ */ -+static int may_rename_dstdir(struct dentry *dentry, struct au_nhash *whlist) -+{ -+ return au_test_empty(dentry, whlist); -+} -+ -+/* -+ * test if @dentry dir can be rename source or not. -+ * if it can, return 0 and @children is filled. -+ * success means, -+ * - it is a logically empty dir. -+ * - or, it exists on writable branch and has no children including whiteouts -+ * on the lower branch. -+ */ -+static int may_rename_srcdir(struct dentry *dentry, aufs_bindex_t btgt) -+{ -+ int err; -+ unsigned int rdhash; -+ aufs_bindex_t bstart; -+ -+ bstart = au_dbstart(dentry); -+ if (bstart != btgt) { -+ struct au_nhash whlist; -+ -+ SiMustAnyLock(dentry->d_sb); -+ rdhash = au_sbi(dentry->d_sb)->si_rdhash; -+ if (!rdhash) -+ rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, -+ dentry)); -+ err = au_nhash_alloc(&whlist, rdhash, GFP_NOFS); -+ if (unlikely(err)) -+ goto out; -+ err = au_test_empty(dentry, &whlist); -+ au_nhash_wh_free(&whlist); -+ goto out; -+ } -+ -+ if (bstart == au_dbtaildir(dentry)) -+ return 0; /* success */ -+ -+ err = au_test_empty_lower(dentry); -+ -+out: -+ if (err == -ENOTEMPTY) { -+ AuWarn1("renaming dir who has child(ren) on multiple branches," -+ " is not supported\n"); -+ err = -EXDEV; -+ } -+ return err; -+} -+ -+/* side effect: sets whlist and h_dentry */ -+static int au_ren_may_dir(struct au_ren_args *a) -+{ -+ int err; -+ unsigned int rdhash; -+ struct dentry *d; -+ -+ d = a->dst_dentry; -+ SiMustAnyLock(d->d_sb); -+ -+ err = 0; -+ if (au_ftest_ren(a->flags, ISDIR) && a->dst_inode) { -+ rdhash = au_sbi(d->d_sb)->si_rdhash; -+ if (!rdhash) -+ rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, d)); -+ err = au_nhash_alloc(&a->whlist, rdhash, GFP_NOFS); -+ if (unlikely(err)) -+ goto out; -+ -+ au_set_dbstart(d, a->dst_bstart); -+ err = may_rename_dstdir(d, &a->whlist); -+ au_set_dbstart(d, a->btgt); -+ } -+ a->dst_h_dentry = au_h_dptr(d, au_dbstart(d)); -+ if (unlikely(err)) -+ goto out; -+ -+ d = a->src_dentry; -+ a->src_h_dentry = au_h_dptr(d, au_dbstart(d)); -+ if (au_ftest_ren(a->flags, ISDIR)) { -+ err = may_rename_srcdir(d, a->btgt); -+ if (unlikely(err)) { -+ au_nhash_wh_free(&a->whlist); -+ a->whlist.nh_num = 0; -+ } -+ } -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * simple tests for rename. -+ * following the checks in vfs, plus the parent-child relationship. -+ */ -+static int au_may_ren(struct au_ren_args *a) -+{ -+ int err, isdir; -+ struct inode *h_inode; -+ -+ if (a->src_bstart == a->btgt) { -+ err = au_may_del(a->src_dentry, a->btgt, a->src_h_parent, -+ au_ftest_ren(a->flags, ISDIR)); -+ if (unlikely(err)) -+ goto out; -+ err = -EINVAL; -+ if (unlikely(a->src_h_dentry == a->h_trap)) -+ goto out; -+ } -+ -+ err = 0; -+ if (a->dst_bstart != a->btgt) -+ goto out; -+ -+ err = -ENOTEMPTY; -+ if (unlikely(a->dst_h_dentry == a->h_trap)) -+ goto out; -+ -+ err = -EIO; -+ h_inode = a->dst_h_dentry->d_inode; -+ isdir = !!au_ftest_ren(a->flags, ISDIR); -+ if (!a->dst_dentry->d_inode) { -+ if (unlikely(h_inode)) -+ goto out; -+ err = au_may_add(a->dst_dentry, a->btgt, a->dst_h_parent, -+ isdir); -+ } else { -+ if (unlikely(!h_inode || !h_inode->i_nlink)) -+ goto out; -+ err = au_may_del(a->dst_dentry, a->btgt, a->dst_h_parent, -+ isdir); -+ if (unlikely(err)) -+ goto out; -+ } -+ -+out: -+ if (unlikely(err == -ENOENT || err == -EEXIST)) -+ err = -EIO; -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * locking order -+ * (VFS) -+ * - src_dir and dir by lock_rename() -+ * - inode if exitsts -+ * (aufs) -+ * - lock all -+ * + src_dentry and dentry by aufs_read_and_write_lock2() which calls, -+ * + si_read_lock -+ * + di_write_lock2_child() -+ * + di_write_lock_child() -+ * + ii_write_lock_child() -+ * + di_write_lock_child2() -+ * + ii_write_lock_child2() -+ * + src_parent and parent -+ * + di_write_lock_parent() -+ * + ii_write_lock_parent() -+ * + di_write_lock_parent2() -+ * + ii_write_lock_parent2() -+ * + lower src_dir and dir by vfsub_lock_rename() -+ * + verify the every relationships between child and parent. if any -+ * of them failed, unlock all and return -EBUSY. -+ */ -+static void au_ren_unlock(struct au_ren_args *a) -+{ -+ vfsub_unlock_rename(a->src_h_parent, a->src_hdir, -+ a->dst_h_parent, a->dst_hdir); -+ if (au_ftest_ren(a->flags, MNT_WRITE)) -+ vfsub_mnt_drop_write(au_br_mnt(a->br)); -+} -+ -+static int au_ren_lock(struct au_ren_args *a) -+{ -+ int err; -+ unsigned int udba; -+ -+ err = 0; -+ a->src_h_parent = au_h_dptr(a->src_parent, a->btgt); -+ a->src_hdir = au_hi(a->src_dir, a->btgt); -+ a->dst_h_parent = au_h_dptr(a->dst_parent, a->btgt); -+ a->dst_hdir = au_hi(a->dst_dir, a->btgt); -+ -+ err = vfsub_mnt_want_write(au_br_mnt(a->br)); -+ if (unlikely(err)) -+ goto out; -+ au_fset_ren(a->flags, MNT_WRITE); -+ a->h_trap = vfsub_lock_rename(a->src_h_parent, a->src_hdir, -+ a->dst_h_parent, a->dst_hdir); -+ udba = au_opt_udba(a->src_dentry->d_sb); -+ if (unlikely(a->src_hdir->hi_inode != a->src_h_parent->d_inode -+ || a->dst_hdir->hi_inode != a->dst_h_parent->d_inode)) -+ err = au_busy_or_stale(); -+ if (!err && au_dbstart(a->src_dentry) == a->btgt) -+ err = au_h_verify(a->src_h_dentry, udba, -+ a->src_h_parent->d_inode, a->src_h_parent, -+ a->br); -+ if (!err && au_dbstart(a->dst_dentry) == a->btgt) -+ err = au_h_verify(a->dst_h_dentry, udba, -+ a->dst_h_parent->d_inode, a->dst_h_parent, -+ a->br); -+ if (!err) -+ goto out; /* success */ -+ -+ err = au_busy_or_stale(); -+ au_ren_unlock(a); -+ -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void au_ren_refresh_dir(struct au_ren_args *a) -+{ -+ struct inode *dir; -+ -+ dir = a->dst_dir; -+ dir->i_version++; -+ if (au_ftest_ren(a->flags, ISDIR)) { -+ /* is this updating defined in POSIX? */ -+ au_cpup_attr_timesizes(a->src_inode); -+ au_cpup_attr_nlink(dir, /*force*/1); -+ } -+ -+ au_dir_ts(dir, a->btgt); -+ -+ if (au_ftest_ren(a->flags, ISSAMEDIR)) -+ return; -+ -+ dir = a->src_dir; -+ dir->i_version++; -+ if (au_ftest_ren(a->flags, ISDIR)) -+ au_cpup_attr_nlink(dir, /*force*/1); -+ au_dir_ts(dir, a->btgt); -+} -+ -+static void au_ren_refresh(struct au_ren_args *a) -+{ -+ aufs_bindex_t bend, bindex; -+ struct dentry *d, *h_d; -+ struct inode *i, *h_i; -+ struct super_block *sb; -+ -+ d = a->dst_dentry; -+ d_drop(d); -+ if (a->h_dst) -+ /* already dget-ed by au_ren_or_cpup() */ -+ au_set_h_dptr(d, a->btgt, a->h_dst); -+ -+ i = a->dst_inode; -+ if (i) { -+ if (!au_ftest_ren(a->flags, ISDIR)) -+ vfsub_drop_nlink(i); -+ else { -+ vfsub_dead_dir(i); -+ au_cpup_attr_timesizes(i); -+ } -+ au_update_dbrange(d, /*do_put_zero*/1); -+ } else { -+ bend = a->btgt; -+ for (bindex = au_dbstart(d); bindex < bend; bindex++) -+ au_set_h_dptr(d, bindex, NULL); -+ bend = au_dbend(d); -+ for (bindex = a->btgt + 1; bindex <= bend; bindex++) -+ au_set_h_dptr(d, bindex, NULL); -+ au_update_dbrange(d, /*do_put_zero*/0); -+ } -+ -+ d = a->src_dentry; -+ au_set_dbwh(d, -1); -+ bend = au_dbend(d); -+ for (bindex = a->btgt + 1; bindex <= bend; bindex++) { -+ h_d = au_h_dptr(d, bindex); -+ if (h_d) -+ au_set_h_dptr(d, bindex, NULL); -+ } -+ au_set_dbend(d, a->btgt); -+ -+ sb = d->d_sb; -+ i = a->src_inode; -+ if (au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(i)) -+ return; /* success */ -+ -+ bend = au_ibend(i); -+ for (bindex = a->btgt + 1; bindex <= bend; bindex++) { -+ h_i = au_h_iptr(i, bindex); -+ if (h_i) { -+ au_xino_write(sb, bindex, h_i->i_ino, /*ino*/0); -+ /* ignore this error */ -+ au_set_h_iptr(i, bindex, NULL, 0); -+ } -+ } -+ au_set_ibend(i, a->btgt); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* mainly for link(2) and rename(2) */ -+int au_wbr(struct dentry *dentry, aufs_bindex_t btgt) -+{ -+ aufs_bindex_t bdiropq, bwh; -+ struct dentry *parent; -+ struct au_branch *br; -+ -+ parent = dentry->d_parent; -+ IMustLock(parent->d_inode); /* dir is locked */ -+ -+ bdiropq = au_dbdiropq(parent); -+ bwh = au_dbwh(dentry); -+ br = au_sbr(dentry->d_sb, btgt); -+ if (au_br_rdonly(br) -+ || (0 <= bdiropq && bdiropq < btgt) -+ || (0 <= bwh && bwh < btgt)) -+ btgt = -1; -+ -+ AuDbg("btgt %d\n", btgt); -+ return btgt; -+} -+ -+/* sets src_bstart, dst_bstart and btgt */ -+static int au_ren_wbr(struct au_ren_args *a) -+{ -+ int err; -+ struct au_wr_dir_args wr_dir_args = { -+ /* .force_btgt = -1, */ -+ .flags = AuWrDir_ADD_ENTRY -+ }; -+ -+ a->src_bstart = au_dbstart(a->src_dentry); -+ a->dst_bstart = au_dbstart(a->dst_dentry); -+ if (au_ftest_ren(a->flags, ISDIR)) -+ au_fset_wrdir(wr_dir_args.flags, ISDIR); -+ wr_dir_args.force_btgt = a->src_bstart; -+ if (a->dst_inode && a->dst_bstart < a->src_bstart) -+ wr_dir_args.force_btgt = a->dst_bstart; -+ wr_dir_args.force_btgt = au_wbr(a->dst_dentry, wr_dir_args.force_btgt); -+ err = au_wr_dir(a->dst_dentry, a->src_dentry, &wr_dir_args); -+ a->btgt = err; -+ -+ return err; -+} -+ -+static void au_ren_dt(struct au_ren_args *a) -+{ -+ a->h_path.dentry = a->src_h_parent; -+ au_dtime_store(a->src_dt + AuPARENT, a->src_parent, &a->h_path); -+ if (!au_ftest_ren(a->flags, ISSAMEDIR)) { -+ a->h_path.dentry = a->dst_h_parent; -+ au_dtime_store(a->dst_dt + AuPARENT, a->dst_parent, &a->h_path); -+ } -+ -+ au_fclr_ren(a->flags, DT_DSTDIR); -+ if (!au_ftest_ren(a->flags, ISDIR)) -+ return; -+ -+ a->h_path.dentry = a->src_h_dentry; -+ au_dtime_store(a->src_dt + AuCHILD, a->src_dentry, &a->h_path); -+ if (a->dst_h_dentry->d_inode) { -+ au_fset_ren(a->flags, DT_DSTDIR); -+ a->h_path.dentry = a->dst_h_dentry; -+ au_dtime_store(a->dst_dt + AuCHILD, a->dst_dentry, &a->h_path); -+ } -+} -+ -+static void au_ren_rev_dt(int err, struct au_ren_args *a) -+{ -+ struct dentry *h_d; -+ struct mutex *h_mtx; -+ -+ au_dtime_revert(a->src_dt + AuPARENT); -+ if (!au_ftest_ren(a->flags, ISSAMEDIR)) -+ au_dtime_revert(a->dst_dt + AuPARENT); -+ -+ if (au_ftest_ren(a->flags, ISDIR) && err != -EIO) { -+ h_d = a->src_dt[AuCHILD].dt_h_path.dentry; -+ h_mtx = &h_d->d_inode->i_mutex; -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); -+ au_dtime_revert(a->src_dt + AuCHILD); -+ mutex_unlock(h_mtx); -+ -+ if (au_ftest_ren(a->flags, DT_DSTDIR)) { -+ h_d = a->dst_dt[AuCHILD].dt_h_path.dentry; -+ h_mtx = &h_d->d_inode->i_mutex; -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); -+ au_dtime_revert(a->dst_dt + AuCHILD); -+ mutex_unlock(h_mtx); -+ } -+ } -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry, -+ struct inode *_dst_dir, struct dentry *_dst_dentry) -+{ -+ int err, flags; -+ /* reduce stack space */ -+ struct au_ren_args *a; -+ -+ AuDbg("%pd, %pd\n", _src_dentry, _dst_dentry); -+ IMustLock(_src_dir); -+ IMustLock(_dst_dir); -+ -+ err = -ENOMEM; -+ BUILD_BUG_ON(sizeof(*a) > PAGE_SIZE); -+ a = kzalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ -+ a->src_dir = _src_dir; -+ a->src_dentry = _src_dentry; -+ a->src_inode = a->src_dentry->d_inode; -+ a->src_parent = a->src_dentry->d_parent; /* dir inode is locked */ -+ a->dst_dir = _dst_dir; -+ a->dst_dentry = _dst_dentry; -+ a->dst_inode = a->dst_dentry->d_inode; -+ a->dst_parent = a->dst_dentry->d_parent; /* dir inode is locked */ -+ if (a->dst_inode) { -+ IMustLock(a->dst_inode); -+ au_igrab(a->dst_inode); -+ } -+ -+ err = -ENOTDIR; -+ flags = AuLock_FLUSH | AuLock_NOPLM | AuLock_GEN; -+ if (d_is_dir(a->src_dentry)) { -+ au_fset_ren(a->flags, ISDIR); -+ if (unlikely(d_is_positive(a->dst_dentry) -+ && !d_is_dir(a->dst_dentry))) -+ goto out_free; -+ flags |= AuLock_DIRS; -+ } -+ err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry, flags); -+ if (unlikely(err)) -+ goto out_free; -+ -+ err = au_d_hashed_positive(a->src_dentry); -+ if (unlikely(err)) -+ goto out_unlock; -+ err = -ENOENT; -+ if (a->dst_inode) { -+ /* -+ * If it is a dir, VFS unhash dst_dentry before this -+ * function. It means we cannot rely upon d_unhashed(). -+ */ -+ if (unlikely(!a->dst_inode->i_nlink)) -+ goto out_unlock; -+ if (!S_ISDIR(a->dst_inode->i_mode)) { -+ err = au_d_hashed_positive(a->dst_dentry); -+ if (unlikely(err)) -+ goto out_unlock; -+ } else if (unlikely(IS_DEADDIR(a->dst_inode))) -+ goto out_unlock; -+ } else if (unlikely(d_unhashed(a->dst_dentry))) -+ goto out_unlock; -+ -+ /* -+ * is it possible? -+ * yes, it happened (in linux-3.3-rcN) but I don't know why. -+ * there may exist a problem somewhere else. -+ */ -+ err = -EINVAL; -+ if (unlikely(a->dst_parent->d_inode == a->src_dentry->d_inode)) -+ goto out_unlock; -+ -+ au_fset_ren(a->flags, ISSAMEDIR); /* temporary */ -+ di_write_lock_parent(a->dst_parent); -+ -+ /* which branch we process */ -+ err = au_ren_wbr(a); -+ if (unlikely(err < 0)) -+ goto out_parent; -+ a->br = au_sbr(a->dst_dentry->d_sb, a->btgt); -+ a->h_path.mnt = au_br_mnt(a->br); -+ -+ /* are they available to be renamed */ -+ err = au_ren_may_dir(a); -+ if (unlikely(err)) -+ goto out_children; -+ -+ /* prepare the writable parent dir on the same branch */ -+ if (a->dst_bstart == a->btgt) { -+ au_fset_ren(a->flags, WHDST); -+ } else { -+ err = au_cpup_dirs(a->dst_dentry, a->btgt); -+ if (unlikely(err)) -+ goto out_children; -+ } -+ -+ if (a->src_dir != a->dst_dir) { -+ /* -+ * this temporary unlock is safe, -+ * because both dir->i_mutex are locked. -+ */ -+ di_write_unlock(a->dst_parent); -+ di_write_lock_parent(a->src_parent); -+ err = au_wr_dir_need_wh(a->src_dentry, -+ au_ftest_ren(a->flags, ISDIR), -+ &a->btgt); -+ di_write_unlock(a->src_parent); -+ di_write_lock2_parent(a->src_parent, a->dst_parent, /*isdir*/1); -+ au_fclr_ren(a->flags, ISSAMEDIR); -+ } else -+ err = au_wr_dir_need_wh(a->src_dentry, -+ au_ftest_ren(a->flags, ISDIR), -+ &a->btgt); -+ if (unlikely(err < 0)) -+ goto out_children; -+ if (err) -+ au_fset_ren(a->flags, WHSRC); -+ -+ /* cpup src */ -+ if (a->src_bstart != a->btgt) { -+ struct au_pin pin; -+ -+ err = au_pin(&pin, a->src_dentry, a->btgt, -+ au_opt_udba(a->src_dentry->d_sb), -+ AuPin_DI_LOCKED | AuPin_MNT_WRITE); -+ if (!err) { -+ struct au_cp_generic cpg = { -+ .dentry = a->src_dentry, -+ .bdst = a->btgt, -+ .bsrc = a->src_bstart, -+ .len = -1, -+ .pin = &pin, -+ .flags = AuCpup_DTIME | AuCpup_HOPEN -+ }; -+ AuDebugOn(au_dbstart(a->src_dentry) != a->src_bstart); -+ err = au_sio_cpup_simple(&cpg); -+ au_unpin(&pin); -+ } -+ if (unlikely(err)) -+ goto out_children; -+ a->src_bstart = a->btgt; -+ a->src_h_dentry = au_h_dptr(a->src_dentry, a->btgt); -+ au_fset_ren(a->flags, WHSRC); -+ } -+ -+ /* lock them all */ -+ err = au_ren_lock(a); -+ if (unlikely(err)) -+ /* leave the copied-up one */ -+ goto out_children; -+ -+ if (!au_opt_test(au_mntflags(a->dst_dir->i_sb), UDBA_NONE)) -+ err = au_may_ren(a); -+ else if (unlikely(a->dst_dentry->d_name.len > AUFS_MAX_NAMELEN)) -+ err = -ENAMETOOLONG; -+ if (unlikely(err)) -+ goto out_hdir; -+ -+ /* store timestamps to be revertible */ -+ au_ren_dt(a); -+ -+ /* here we go */ -+ err = do_rename(a); -+ if (unlikely(err)) -+ goto out_dt; -+ -+ /* update dir attributes */ -+ au_ren_refresh_dir(a); -+ -+ /* dput/iput all lower dentries */ -+ au_ren_refresh(a); -+ -+ goto out_hdir; /* success */ -+ -+out_dt: -+ au_ren_rev_dt(err, a); -+out_hdir: -+ au_ren_unlock(a); -+out_children: -+ au_nhash_wh_free(&a->whlist); -+ if (err && a->dst_inode && a->dst_bstart != a->btgt) { -+ AuDbg("bstart %d, btgt %d\n", a->dst_bstart, a->btgt); -+ au_set_h_dptr(a->dst_dentry, a->btgt, NULL); -+ au_set_dbstart(a->dst_dentry, a->dst_bstart); -+ } -+out_parent: -+ if (!err) -+ d_move(a->src_dentry, a->dst_dentry); -+ else { -+ au_update_dbstart(a->dst_dentry); -+ if (!a->dst_inode) -+ d_drop(a->dst_dentry); -+ } -+ if (au_ftest_ren(a->flags, ISSAMEDIR)) -+ di_write_unlock(a->dst_parent); -+ else -+ di_write_unlock2(a->src_parent, a->dst_parent); -+out_unlock: -+ aufs_read_and_write_unlock2(a->dst_dentry, a->src_dentry); -+out_free: -+ iput(a->dst_inode); -+ if (a->thargs) -+ au_whtmp_rmdir_free(a->thargs); -+ kfree(a); -+out: -+ AuTraceErr(err); -+ return err; -+} -diff --git a/fs/aufs/iinfo.c b/fs/aufs/iinfo.c -new file mode 100644 -index 0000000..f889aba ---- /dev/null -+++ b/fs/aufs/iinfo.c -@@ -0,0 +1,277 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * inode private data -+ */ -+ -+#include "aufs.h" -+ -+struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex) -+{ -+ struct inode *h_inode; -+ -+ IiMustAnyLock(inode); -+ -+ h_inode = au_ii(inode)->ii_hinode[0 + bindex].hi_inode; -+ AuDebugOn(h_inode && atomic_read(&h_inode->i_count) <= 0); -+ return h_inode; -+} -+ -+/* todo: hard/soft set? */ -+void au_hiput(struct au_hinode *hinode) -+{ -+ au_hn_free(hinode); -+ dput(hinode->hi_whdentry); -+ iput(hinode->hi_inode); -+} -+ -+unsigned int au_hi_flags(struct inode *inode, int isdir) -+{ -+ unsigned int flags; -+ const unsigned int mnt_flags = au_mntflags(inode->i_sb); -+ -+ flags = 0; -+ if (au_opt_test(mnt_flags, XINO)) -+ au_fset_hi(flags, XINO); -+ if (isdir && au_opt_test(mnt_flags, UDBA_HNOTIFY)) -+ au_fset_hi(flags, HNOTIFY); -+ return flags; -+} -+ -+void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex, -+ struct inode *h_inode, unsigned int flags) -+{ -+ struct au_hinode *hinode; -+ struct inode *hi; -+ struct au_iinfo *iinfo = au_ii(inode); -+ -+ IiMustWriteLock(inode); -+ -+ hinode = iinfo->ii_hinode + bindex; -+ hi = hinode->hi_inode; -+ AuDebugOn(h_inode && atomic_read(&h_inode->i_count) <= 0); -+ -+ if (hi) -+ au_hiput(hinode); -+ hinode->hi_inode = h_inode; -+ if (h_inode) { -+ int err; -+ struct super_block *sb = inode->i_sb; -+ struct au_branch *br; -+ -+ AuDebugOn(inode->i_mode -+ && (h_inode->i_mode & S_IFMT) -+ != (inode->i_mode & S_IFMT)); -+ if (bindex == iinfo->ii_bstart) -+ au_cpup_igen(inode, h_inode); -+ br = au_sbr(sb, bindex); -+ hinode->hi_id = br->br_id; -+ if (au_ftest_hi(flags, XINO)) { -+ err = au_xino_write(sb, bindex, h_inode->i_ino, -+ inode->i_ino); -+ if (unlikely(err)) -+ AuIOErr1("failed au_xino_write() %d\n", err); -+ } -+ -+ if (au_ftest_hi(flags, HNOTIFY) -+ && au_br_hnotifyable(br->br_perm)) { -+ err = au_hn_alloc(hinode, inode); -+ if (unlikely(err)) -+ AuIOErr1("au_hn_alloc() %d\n", err); -+ } -+ } -+} -+ -+void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex, -+ struct dentry *h_wh) -+{ -+ struct au_hinode *hinode; -+ -+ IiMustWriteLock(inode); -+ -+ hinode = au_ii(inode)->ii_hinode + bindex; -+ AuDebugOn(hinode->hi_whdentry); -+ hinode->hi_whdentry = h_wh; -+} -+ -+void au_update_iigen(struct inode *inode, int half) -+{ -+ struct au_iinfo *iinfo; -+ struct au_iigen *iigen; -+ unsigned int sigen; -+ -+ sigen = au_sigen(inode->i_sb); -+ iinfo = au_ii(inode); -+ iigen = &iinfo->ii_generation; -+ spin_lock(&iigen->ig_spin); -+ iigen->ig_generation = sigen; -+ if (half) -+ au_ig_fset(iigen->ig_flags, HALF_REFRESHED); -+ else -+ au_ig_fclr(iigen->ig_flags, HALF_REFRESHED); -+ spin_unlock(&iigen->ig_spin); -+} -+ -+/* it may be called at remount time, too */ -+void au_update_ibrange(struct inode *inode, int do_put_zero) -+{ -+ struct au_iinfo *iinfo; -+ aufs_bindex_t bindex, bend; -+ -+ iinfo = au_ii(inode); -+ if (!iinfo) -+ return; -+ -+ IiMustWriteLock(inode); -+ -+ if (do_put_zero && iinfo->ii_bstart >= 0) { -+ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; -+ bindex++) { -+ struct inode *h_i; -+ -+ h_i = iinfo->ii_hinode[0 + bindex].hi_inode; -+ if (h_i -+ && !h_i->i_nlink -+ && !(h_i->i_state & I_LINKABLE)) -+ au_set_h_iptr(inode, bindex, NULL, 0); -+ } -+ } -+ -+ iinfo->ii_bstart = -1; -+ iinfo->ii_bend = -1; -+ bend = au_sbend(inode->i_sb); -+ for (bindex = 0; bindex <= bend; bindex++) -+ if (iinfo->ii_hinode[0 + bindex].hi_inode) { -+ iinfo->ii_bstart = bindex; -+ break; -+ } -+ if (iinfo->ii_bstart >= 0) -+ for (bindex = bend; bindex >= iinfo->ii_bstart; bindex--) -+ if (iinfo->ii_hinode[0 + bindex].hi_inode) { -+ iinfo->ii_bend = bindex; -+ break; -+ } -+ AuDebugOn(iinfo->ii_bstart > iinfo->ii_bend); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void au_icntnr_init_once(void *_c) -+{ -+ struct au_icntnr *c = _c; -+ struct au_iinfo *iinfo = &c->iinfo; -+ static struct lock_class_key aufs_ii; -+ -+ spin_lock_init(&iinfo->ii_generation.ig_spin); -+ au_rw_init(&iinfo->ii_rwsem); -+ au_rw_class(&iinfo->ii_rwsem, &aufs_ii); -+ inode_init_once(&c->vfs_inode); -+} -+ -+int au_iinfo_init(struct inode *inode) -+{ -+ struct au_iinfo *iinfo; -+ struct super_block *sb; -+ int nbr, i; -+ -+ sb = inode->i_sb; -+ iinfo = &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo); -+ nbr = au_sbend(sb) + 1; -+ if (unlikely(nbr <= 0)) -+ nbr = 1; -+ iinfo->ii_hinode = kcalloc(nbr, sizeof(*iinfo->ii_hinode), GFP_NOFS); -+ if (iinfo->ii_hinode) { -+ au_ninodes_inc(sb); -+ for (i = 0; i < nbr; i++) -+ iinfo->ii_hinode[i].hi_id = -1; -+ -+ iinfo->ii_generation.ig_generation = au_sigen(sb); -+ iinfo->ii_bstart = -1; -+ iinfo->ii_bend = -1; -+ iinfo->ii_vdir = NULL; -+ return 0; -+ } -+ return -ENOMEM; -+} -+ -+int au_ii_realloc(struct au_iinfo *iinfo, int nbr) -+{ -+ int err, sz; -+ struct au_hinode *hip; -+ -+ AuRwMustWriteLock(&iinfo->ii_rwsem); -+ -+ err = -ENOMEM; -+ sz = sizeof(*hip) * (iinfo->ii_bend + 1); -+ if (!sz) -+ sz = sizeof(*hip); -+ hip = au_kzrealloc(iinfo->ii_hinode, sz, sizeof(*hip) * nbr, GFP_NOFS); -+ if (hip) { -+ iinfo->ii_hinode = hip; -+ err = 0; -+ } -+ -+ return err; -+} -+ -+void au_iinfo_fin(struct inode *inode) -+{ -+ struct au_iinfo *iinfo; -+ struct au_hinode *hi; -+ struct super_block *sb; -+ aufs_bindex_t bindex, bend; -+ const unsigned char unlinked = !inode->i_nlink; -+ -+ iinfo = au_ii(inode); -+ /* bad_inode case */ -+ if (!iinfo) -+ return; -+ -+ sb = inode->i_sb; -+ au_ninodes_dec(sb); -+ if (si_pid_test(sb)) -+ au_xino_delete_inode(inode, unlinked); -+ else { -+ /* -+ * it is safe to hide the dependency between sbinfo and -+ * sb->s_umount. -+ */ -+ lockdep_off(); -+ si_noflush_read_lock(sb); -+ au_xino_delete_inode(inode, unlinked); -+ si_read_unlock(sb); -+ lockdep_on(); -+ } -+ -+ if (iinfo->ii_vdir) -+ au_vdir_free(iinfo->ii_vdir); -+ -+ bindex = iinfo->ii_bstart; -+ if (bindex >= 0) { -+ hi = iinfo->ii_hinode + bindex; -+ bend = iinfo->ii_bend; -+ while (bindex++ <= bend) { -+ if (hi->hi_inode) -+ au_hiput(hi); -+ hi++; -+ } -+ } -+ kfree(iinfo->ii_hinode); -+ iinfo->ii_hinode = NULL; -+ AuRwDestroy(&iinfo->ii_rwsem); -+} -diff --git a/fs/aufs/inode.c b/fs/aufs/inode.c -new file mode 100644 -index 0000000..75ec2e5 ---- /dev/null -+++ b/fs/aufs/inode.c -@@ -0,0 +1,522 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * inode functions -+ */ -+ -+#include "aufs.h" -+ -+struct inode *au_igrab(struct inode *inode) -+{ -+ if (inode) { -+ AuDebugOn(!atomic_read(&inode->i_count)); -+ ihold(inode); -+ } -+ return inode; -+} -+ -+static void au_refresh_hinode_attr(struct inode *inode, int do_version) -+{ -+ au_cpup_attr_all(inode, /*force*/0); -+ au_update_iigen(inode, /*half*/1); -+ if (do_version) -+ inode->i_version++; -+} -+ -+static int au_ii_refresh(struct inode *inode, int *update) -+{ -+ int err, e; -+ umode_t type; -+ aufs_bindex_t bindex, new_bindex; -+ struct super_block *sb; -+ struct au_iinfo *iinfo; -+ struct au_hinode *p, *q, tmp; -+ -+ IiMustWriteLock(inode); -+ -+ *update = 0; -+ sb = inode->i_sb; -+ type = inode->i_mode & S_IFMT; -+ iinfo = au_ii(inode); -+ err = au_ii_realloc(iinfo, au_sbend(sb) + 1); -+ if (unlikely(err)) -+ goto out; -+ -+ AuDebugOn(iinfo->ii_bstart < 0); -+ p = iinfo->ii_hinode + iinfo->ii_bstart; -+ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; -+ bindex++, p++) { -+ if (!p->hi_inode) -+ continue; -+ -+ AuDebugOn(type != (p->hi_inode->i_mode & S_IFMT)); -+ new_bindex = au_br_index(sb, p->hi_id); -+ if (new_bindex == bindex) -+ continue; -+ -+ if (new_bindex < 0) { -+ *update = 1; -+ au_hiput(p); -+ p->hi_inode = NULL; -+ continue; -+ } -+ -+ if (new_bindex < iinfo->ii_bstart) -+ iinfo->ii_bstart = new_bindex; -+ if (iinfo->ii_bend < new_bindex) -+ iinfo->ii_bend = new_bindex; -+ /* swap two lower inode, and loop again */ -+ q = iinfo->ii_hinode + new_bindex; -+ tmp = *q; -+ *q = *p; -+ *p = tmp; -+ if (tmp.hi_inode) { -+ bindex--; -+ p--; -+ } -+ } -+ au_update_ibrange(inode, /*do_put_zero*/0); -+ e = au_dy_irefresh(inode); -+ if (unlikely(e && !err)) -+ err = e; -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+void au_refresh_iop(struct inode *inode, int force_getattr) -+{ -+ int type; -+ struct au_sbinfo *sbi = au_sbi(inode->i_sb); -+ const struct inode_operations *iop -+ = force_getattr ? aufs_iop : sbi->si_iop_array; -+ -+ if (inode->i_op == iop) -+ return; -+ -+ switch (inode->i_mode & S_IFMT) { -+ case S_IFDIR: -+ type = AuIop_DIR; -+ break; -+ case S_IFLNK: -+ type = AuIop_SYMLINK; -+ break; -+ default: -+ type = AuIop_OTHER; -+ break; -+ } -+ -+ inode->i_op = iop + type; -+ /* unnecessary smp_wmb() */ -+} -+ -+int au_refresh_hinode_self(struct inode *inode) -+{ -+ int err, update; -+ -+ err = au_ii_refresh(inode, &update); -+ if (!err) -+ au_refresh_hinode_attr(inode, update && S_ISDIR(inode->i_mode)); -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+int au_refresh_hinode(struct inode *inode, struct dentry *dentry) -+{ -+ int err, e, update; -+ unsigned int flags; -+ umode_t mode; -+ aufs_bindex_t bindex, bend; -+ unsigned char isdir; -+ struct au_hinode *p; -+ struct au_iinfo *iinfo; -+ -+ err = au_ii_refresh(inode, &update); -+ if (unlikely(err)) -+ goto out; -+ -+ update = 0; -+ iinfo = au_ii(inode); -+ p = iinfo->ii_hinode + iinfo->ii_bstart; -+ mode = (inode->i_mode & S_IFMT); -+ isdir = S_ISDIR(mode); -+ flags = au_hi_flags(inode, isdir); -+ bend = au_dbend(dentry); -+ for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) { -+ struct inode *h_i; -+ struct dentry *h_d; -+ -+ h_d = au_h_dptr(dentry, bindex); -+ if (!h_d || !h_d->d_inode) -+ continue; -+ -+ AuDebugOn(mode != (h_d->d_inode->i_mode & S_IFMT)); -+ if (iinfo->ii_bstart <= bindex && bindex <= iinfo->ii_bend) { -+ h_i = au_h_iptr(inode, bindex); -+ if (h_i) { -+ if (h_i == h_d->d_inode) -+ continue; -+ err = -EIO; -+ break; -+ } -+ } -+ if (bindex < iinfo->ii_bstart) -+ iinfo->ii_bstart = bindex; -+ if (iinfo->ii_bend < bindex) -+ iinfo->ii_bend = bindex; -+ au_set_h_iptr(inode, bindex, au_igrab(h_d->d_inode), flags); -+ update = 1; -+ } -+ au_update_ibrange(inode, /*do_put_zero*/0); -+ e = au_dy_irefresh(inode); -+ if (unlikely(e && !err)) -+ err = e; -+ if (!err) -+ au_refresh_hinode_attr(inode, update && isdir); -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+static int set_inode(struct inode *inode, struct dentry *dentry) -+{ -+ int err; -+ unsigned int flags; -+ umode_t mode; -+ aufs_bindex_t bindex, bstart, btail; -+ unsigned char isdir; -+ struct dentry *h_dentry; -+ struct inode *h_inode; -+ struct au_iinfo *iinfo; -+ struct inode_operations *iop; -+ -+ IiMustWriteLock(inode); -+ -+ err = 0; -+ isdir = 0; -+ iop = au_sbi(inode->i_sb)->si_iop_array; -+ bstart = au_dbstart(dentry); -+ h_inode = au_h_dptr(dentry, bstart)->d_inode; -+ mode = h_inode->i_mode; -+ switch (mode & S_IFMT) { -+ case S_IFREG: -+ btail = au_dbtail(dentry); -+ inode->i_op = iop + AuIop_OTHER; -+ inode->i_fop = &aufs_file_fop; -+ err = au_dy_iaop(inode, bstart, h_inode); -+ if (unlikely(err)) -+ goto out; -+ break; -+ case S_IFDIR: -+ isdir = 1; -+ btail = au_dbtaildir(dentry); -+ inode->i_op = iop + AuIop_DIR; -+ inode->i_fop = &aufs_dir_fop; -+ break; -+ case S_IFLNK: -+ btail = au_dbtail(dentry); -+ inode->i_op = iop + AuIop_SYMLINK; -+ break; -+ case S_IFBLK: -+ case S_IFCHR: -+ case S_IFIFO: -+ case S_IFSOCK: -+ btail = au_dbtail(dentry); -+ inode->i_op = iop + AuIop_OTHER; -+ init_special_inode(inode, mode, h_inode->i_rdev); -+ break; -+ default: -+ AuIOErr("Unknown file type 0%o\n", mode); -+ err = -EIO; -+ goto out; -+ } -+ -+ /* do not set hnotify for whiteouted dirs (SHWH mode) */ -+ flags = au_hi_flags(inode, isdir); -+ if (au_opt_test(au_mntflags(dentry->d_sb), SHWH) -+ && au_ftest_hi(flags, HNOTIFY) -+ && dentry->d_name.len > AUFS_WH_PFX_LEN -+ && !memcmp(dentry->d_name.name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) -+ au_fclr_hi(flags, HNOTIFY); -+ iinfo = au_ii(inode); -+ iinfo->ii_bstart = bstart; -+ iinfo->ii_bend = btail; -+ for (bindex = bstart; bindex <= btail; bindex++) { -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (h_dentry) -+ au_set_h_iptr(inode, bindex, -+ au_igrab(h_dentry->d_inode), flags); -+ } -+ au_cpup_attr_all(inode, /*force*/1); -+ /* -+ * to force calling aufs_get_acl() every time, -+ * do not call cache_no_acl() for aufs inode. -+ */ -+ -+out: -+ return err; -+} -+ -+/* -+ * successful returns with iinfo write_locked -+ * minus: errno -+ * zero: success, matched -+ * plus: no error, but unmatched -+ */ -+static int reval_inode(struct inode *inode, struct dentry *dentry) -+{ -+ int err; -+ unsigned int gen, igflags; -+ aufs_bindex_t bindex, bend; -+ struct inode *h_inode, *h_dinode; -+ -+ /* -+ * before this function, if aufs got any iinfo lock, it must be only -+ * one, the parent dir. -+ * it can happen by UDBA and the obsoleted inode number. -+ */ -+ err = -EIO; -+ if (unlikely(inode->i_ino == parent_ino(dentry))) -+ goto out; -+ -+ err = 1; -+ ii_write_lock_new_child(inode); -+ h_dinode = au_h_dptr(dentry, au_dbstart(dentry))->d_inode; -+ bend = au_ibend(inode); -+ for (bindex = au_ibstart(inode); bindex <= bend; bindex++) { -+ h_inode = au_h_iptr(inode, bindex); -+ if (!h_inode || h_inode != h_dinode) -+ continue; -+ -+ err = 0; -+ gen = au_iigen(inode, &igflags); -+ if (gen == au_digen(dentry) -+ && !au_ig_ftest(igflags, HALF_REFRESHED)) -+ break; -+ -+ /* fully refresh inode using dentry */ -+ err = au_refresh_hinode(inode, dentry); -+ if (!err) -+ au_update_iigen(inode, /*half*/0); -+ break; -+ } -+ -+ if (unlikely(err)) -+ ii_write_unlock(inode); -+out: -+ return err; -+} -+ -+int au_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, -+ unsigned int d_type, ino_t *ino) -+{ -+ int err; -+ struct mutex *mtx; -+ -+ /* prevent hardlinked inode number from race condition */ -+ mtx = NULL; -+ if (d_type != DT_DIR) { -+ mtx = &au_sbr(sb, bindex)->br_xino.xi_nondir_mtx; -+ mutex_lock(mtx); -+ } -+ err = au_xino_read(sb, bindex, h_ino, ino); -+ if (unlikely(err)) -+ goto out; -+ -+ if (!*ino) { -+ err = -EIO; -+ *ino = au_xino_new_ino(sb); -+ if (unlikely(!*ino)) -+ goto out; -+ err = au_xino_write(sb, bindex, h_ino, *ino); -+ if (unlikely(err)) -+ goto out; -+ } -+ -+out: -+ if (mtx) -+ mutex_unlock(mtx); -+ return err; -+} -+ -+/* successful returns with iinfo write_locked */ -+/* todo: return with unlocked? */ -+struct inode *au_new_inode(struct dentry *dentry, int must_new) -+{ -+ struct inode *inode; -+ struct dentry *h_dentry; -+ struct super_block *sb; -+ struct mutex *mtx; -+ ino_t h_ino, ino; -+ int err; -+ aufs_bindex_t bstart; -+ -+ sb = dentry->d_sb; -+ bstart = au_dbstart(dentry); -+ h_dentry = au_h_dptr(dentry, bstart); -+ h_ino = h_dentry->d_inode->i_ino; -+ -+ /* -+ * stop 'race'-ing between hardlinks under different -+ * parents. -+ */ -+ mtx = NULL; -+ if (!d_is_dir(h_dentry)) -+ mtx = &au_sbr(sb, bstart)->br_xino.xi_nondir_mtx; -+ -+new_ino: -+ if (mtx) -+ mutex_lock(mtx); -+ err = au_xino_read(sb, bstart, h_ino, &ino); -+ inode = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out; -+ -+ if (!ino) { -+ ino = au_xino_new_ino(sb); -+ if (unlikely(!ino)) { -+ inode = ERR_PTR(-EIO); -+ goto out; -+ } -+ } -+ -+ AuDbg("i%lu\n", (unsigned long)ino); -+ inode = au_iget_locked(sb, ino); -+ err = PTR_ERR(inode); -+ if (IS_ERR(inode)) -+ goto out; -+ -+ AuDbg("%lx, new %d\n", inode->i_state, !!(inode->i_state & I_NEW)); -+ if (inode->i_state & I_NEW) { -+ /* verbose coding for lock class name */ -+ if (unlikely(d_is_symlink(h_dentry))) -+ au_rw_class(&au_ii(inode)->ii_rwsem, -+ au_lc_key + AuLcSymlink_IIINFO); -+ else if (unlikely(d_is_dir(h_dentry))) -+ au_rw_class(&au_ii(inode)->ii_rwsem, -+ au_lc_key + AuLcDir_IIINFO); -+ else /* likely */ -+ au_rw_class(&au_ii(inode)->ii_rwsem, -+ au_lc_key + AuLcNonDir_IIINFO); -+ -+ ii_write_lock_new_child(inode); -+ err = set_inode(inode, dentry); -+ if (!err) { -+ unlock_new_inode(inode); -+ goto out; /* success */ -+ } -+ -+ /* -+ * iget_failed() calls iput(), but we need to call -+ * ii_write_unlock() after iget_failed(). so dirty hack for -+ * i_count. -+ */ -+ atomic_inc(&inode->i_count); -+ iget_failed(inode); -+ ii_write_unlock(inode); -+ au_xino_write(sb, bstart, h_ino, /*ino*/0); -+ /* ignore this error */ -+ goto out_iput; -+ } else if (!must_new && !IS_DEADDIR(inode) && inode->i_nlink) { -+ /* -+ * horrible race condition between lookup, readdir and copyup -+ * (or something). -+ */ -+ if (mtx) -+ mutex_unlock(mtx); -+ err = reval_inode(inode, dentry); -+ if (unlikely(err < 0)) { -+ mtx = NULL; -+ goto out_iput; -+ } -+ -+ if (!err) { -+ mtx = NULL; -+ goto out; /* success */ -+ } else if (mtx) -+ mutex_lock(mtx); -+ } -+ -+ if (unlikely(au_test_fs_unique_ino(h_dentry->d_inode))) -+ AuWarn1("Warning: Un-notified UDBA or repeatedly renamed dir," -+ " b%d, %s, %pd, hi%lu, i%lu.\n", -+ bstart, au_sbtype(h_dentry->d_sb), dentry, -+ (unsigned long)h_ino, (unsigned long)ino); -+ ino = 0; -+ err = au_xino_write(sb, bstart, h_ino, /*ino*/0); -+ if (!err) { -+ iput(inode); -+ if (mtx) -+ mutex_unlock(mtx); -+ goto new_ino; -+ } -+ -+out_iput: -+ iput(inode); -+ inode = ERR_PTR(err); -+out: -+ if (mtx) -+ mutex_unlock(mtx); -+ return inode; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int au_test_ro(struct super_block *sb, aufs_bindex_t bindex, -+ struct inode *inode) -+{ -+ int err; -+ struct inode *hi; -+ -+ err = au_br_rdonly(au_sbr(sb, bindex)); -+ -+ /* pseudo-link after flushed may happen out of bounds */ -+ if (!err -+ && inode -+ && au_ibstart(inode) <= bindex -+ && bindex <= au_ibend(inode)) { -+ /* -+ * permission check is unnecessary since vfsub routine -+ * will be called later -+ */ -+ hi = au_h_iptr(inode, bindex); -+ if (hi) -+ err = IS_IMMUTABLE(hi) ? -EROFS : 0; -+ } -+ -+ return err; -+} -+ -+int au_test_h_perm(struct inode *h_inode, int mask) -+{ -+ if (uid_eq(current_fsuid(), GLOBAL_ROOT_UID)) -+ return 0; -+ return inode_permission(h_inode, mask); -+} -+ -+int au_test_h_perm_sio(struct inode *h_inode, int mask) -+{ -+ if (au_test_nfs(h_inode->i_sb) -+ && (mask & MAY_WRITE) -+ && S_ISDIR(h_inode->i_mode)) -+ mask |= MAY_READ; /* force permission check */ -+ return au_test_h_perm(h_inode, mask); -+} -diff --git a/fs/aufs/inode.h b/fs/aufs/inode.h -new file mode 100644 -index 0000000..49d53a2 ---- /dev/null -+++ b/fs/aufs/inode.h -@@ -0,0 +1,686 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * inode operations -+ */ -+ -+#ifndef __AUFS_INODE_H__ -+#define __AUFS_INODE_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+#include "rwsem.h" -+ -+struct vfsmount; -+ -+struct au_hnotify { -+#ifdef CONFIG_AUFS_HNOTIFY -+#ifdef CONFIG_AUFS_HFSNOTIFY -+ /* never use fsnotify_add_vfsmount_mark() */ -+ struct fsnotify_mark hn_mark; -+#endif -+ struct inode *hn_aufs_inode; /* no get/put */ -+#endif -+} ____cacheline_aligned_in_smp; -+ -+struct au_hinode { -+ struct inode *hi_inode; -+ aufs_bindex_t hi_id; -+#ifdef CONFIG_AUFS_HNOTIFY -+ struct au_hnotify *hi_notify; -+#endif -+ -+ /* reference to the copied-up whiteout with get/put */ -+ struct dentry *hi_whdentry; -+}; -+ -+/* ig_flags */ -+#define AuIG_HALF_REFRESHED 1 -+#define au_ig_ftest(flags, name) ((flags) & AuIG_##name) -+#define au_ig_fset(flags, name) \ -+ do { (flags) |= AuIG_##name; } while (0) -+#define au_ig_fclr(flags, name) \ -+ do { (flags) &= ~AuIG_##name; } while (0) -+ -+struct au_iigen { -+ spinlock_t ig_spin; -+ __u32 ig_generation, ig_flags; -+}; -+ -+struct au_vdir; -+struct au_iinfo { -+ struct au_iigen ii_generation; -+ struct super_block *ii_hsb1; /* no get/put */ -+ -+ struct au_rwsem ii_rwsem; -+ aufs_bindex_t ii_bstart, ii_bend; -+ __u32 ii_higen; -+ struct au_hinode *ii_hinode; -+ struct au_vdir *ii_vdir; -+}; -+ -+struct au_icntnr { -+ struct au_iinfo iinfo; -+ struct inode vfs_inode; -+ struct hlist_node plink; -+} ____cacheline_aligned_in_smp; -+ -+/* au_pin flags */ -+#define AuPin_DI_LOCKED 1 -+#define AuPin_MNT_WRITE (1 << 1) -+#define au_ftest_pin(flags, name) ((flags) & AuPin_##name) -+#define au_fset_pin(flags, name) \ -+ do { (flags) |= AuPin_##name; } while (0) -+#define au_fclr_pin(flags, name) \ -+ do { (flags) &= ~AuPin_##name; } while (0) -+ -+struct au_pin { -+ /* input */ -+ struct dentry *dentry; -+ unsigned int udba; -+ unsigned char lsc_di, lsc_hi, flags; -+ aufs_bindex_t bindex; -+ -+ /* output */ -+ struct dentry *parent; -+ struct au_hinode *hdir; -+ struct vfsmount *h_mnt; -+ -+ /* temporary unlock/relock for copyup */ -+ struct dentry *h_dentry, *h_parent; -+ struct au_branch *br; -+ struct task_struct *task; -+}; -+ -+void au_pin_hdir_unlock(struct au_pin *p); -+int au_pin_hdir_lock(struct au_pin *p); -+int au_pin_hdir_relock(struct au_pin *p); -+void au_pin_hdir_set_owner(struct au_pin *p, struct task_struct *task); -+void au_pin_hdir_acquire_nest(struct au_pin *p); -+void au_pin_hdir_release(struct au_pin *p); -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline struct au_iinfo *au_ii(struct inode *inode) -+{ -+ struct au_iinfo *iinfo; -+ -+ iinfo = &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo); -+ if (iinfo->ii_hinode) -+ return iinfo; -+ return NULL; /* debugging bad_inode case */ -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* inode.c */ -+struct inode *au_igrab(struct inode *inode); -+void au_refresh_iop(struct inode *inode, int force_getattr); -+int au_refresh_hinode_self(struct inode *inode); -+int au_refresh_hinode(struct inode *inode, struct dentry *dentry); -+int au_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, -+ unsigned int d_type, ino_t *ino); -+struct inode *au_new_inode(struct dentry *dentry, int must_new); -+int au_test_ro(struct super_block *sb, aufs_bindex_t bindex, -+ struct inode *inode); -+int au_test_h_perm(struct inode *h_inode, int mask); -+int au_test_h_perm_sio(struct inode *h_inode, int mask); -+ -+static inline int au_wh_ino(struct super_block *sb, aufs_bindex_t bindex, -+ ino_t h_ino, unsigned int d_type, ino_t *ino) -+{ -+#ifdef CONFIG_AUFS_SHWH -+ return au_ino(sb, bindex, h_ino, d_type, ino); -+#else -+ return 0; -+#endif -+} -+ -+/* i_op.c */ -+enum { -+ AuIop_SYMLINK, -+ AuIop_DIR, -+ AuIop_OTHER, -+ AuIop_Last -+}; -+extern struct inode_operations aufs_iop[AuIop_Last], -+ aufs_iop_nogetattr[AuIop_Last]; -+ -+/* au_wr_dir flags */ -+#define AuWrDir_ADD_ENTRY 1 -+#define AuWrDir_ISDIR (1 << 1) -+#define AuWrDir_TMPFILE (1 << 2) -+#define au_ftest_wrdir(flags, name) ((flags) & AuWrDir_##name) -+#define au_fset_wrdir(flags, name) \ -+ do { (flags) |= AuWrDir_##name; } while (0) -+#define au_fclr_wrdir(flags, name) \ -+ do { (flags) &= ~AuWrDir_##name; } while (0) -+ -+struct au_wr_dir_args { -+ aufs_bindex_t force_btgt; -+ unsigned char flags; -+}; -+int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry, -+ struct au_wr_dir_args *args); -+ -+struct dentry *au_pinned_h_parent(struct au_pin *pin); -+void au_pin_init(struct au_pin *pin, struct dentry *dentry, -+ aufs_bindex_t bindex, int lsc_di, int lsc_hi, -+ unsigned int udba, unsigned char flags); -+int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex, -+ unsigned int udba, unsigned char flags) __must_check; -+int au_do_pin(struct au_pin *pin) __must_check; -+void au_unpin(struct au_pin *pin); -+int au_reval_for_attr(struct dentry *dentry, unsigned int sigen); -+ -+#define AuIcpup_DID_CPUP 1 -+#define au_ftest_icpup(flags, name) ((flags) & AuIcpup_##name) -+#define au_fset_icpup(flags, name) \ -+ do { (flags) |= AuIcpup_##name; } while (0) -+#define au_fclr_icpup(flags, name) \ -+ do { (flags) &= ~AuIcpup_##name; } while (0) -+ -+struct au_icpup_args { -+ unsigned char flags; -+ unsigned char pin_flags; -+ aufs_bindex_t btgt; -+ unsigned int udba; -+ struct au_pin pin; -+ struct path h_path; -+ struct inode *h_inode; -+}; -+ -+int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia, -+ struct au_icpup_args *a); -+ -+int au_h_path_getattr(struct dentry *dentry, int force, struct path *h_path); -+ -+/* i_op_add.c */ -+int au_may_add(struct dentry *dentry, aufs_bindex_t bindex, -+ struct dentry *h_parent, int isdir); -+int aufs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, -+ dev_t dev); -+int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname); -+int aufs_create(struct inode *dir, struct dentry *dentry, umode_t mode, -+ bool want_excl); -+struct vfsub_aopen_args; -+int au_aopen_or_create(struct inode *dir, struct dentry *dentry, -+ struct vfsub_aopen_args *args); -+int aufs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode); -+int aufs_link(struct dentry *src_dentry, struct inode *dir, -+ struct dentry *dentry); -+int aufs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode); -+ -+/* i_op_del.c */ -+int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup); -+int au_may_del(struct dentry *dentry, aufs_bindex_t bindex, -+ struct dentry *h_parent, int isdir); -+int aufs_unlink(struct inode *dir, struct dentry *dentry); -+int aufs_rmdir(struct inode *dir, struct dentry *dentry); -+ -+/* i_op_ren.c */ -+int au_wbr(struct dentry *dentry, aufs_bindex_t btgt); -+int aufs_rename(struct inode *src_dir, struct dentry *src_dentry, -+ struct inode *dir, struct dentry *dentry); -+ -+/* iinfo.c */ -+struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex); -+void au_hiput(struct au_hinode *hinode); -+void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex, -+ struct dentry *h_wh); -+unsigned int au_hi_flags(struct inode *inode, int isdir); -+ -+/* hinode flags */ -+#define AuHi_XINO 1 -+#define AuHi_HNOTIFY (1 << 1) -+#define au_ftest_hi(flags, name) ((flags) & AuHi_##name) -+#define au_fset_hi(flags, name) \ -+ do { (flags) |= AuHi_##name; } while (0) -+#define au_fclr_hi(flags, name) \ -+ do { (flags) &= ~AuHi_##name; } while (0) -+ -+#ifndef CONFIG_AUFS_HNOTIFY -+#undef AuHi_HNOTIFY -+#define AuHi_HNOTIFY 0 -+#endif -+ -+void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex, -+ struct inode *h_inode, unsigned int flags); -+ -+void au_update_iigen(struct inode *inode, int half); -+void au_update_ibrange(struct inode *inode, int do_put_zero); -+ -+void au_icntnr_init_once(void *_c); -+int au_iinfo_init(struct inode *inode); -+void au_iinfo_fin(struct inode *inode); -+int au_ii_realloc(struct au_iinfo *iinfo, int nbr); -+ -+#ifdef CONFIG_PROC_FS -+/* plink.c */ -+int au_plink_maint(struct super_block *sb, int flags); -+struct au_sbinfo; -+void au_plink_maint_leave(struct au_sbinfo *sbinfo); -+int au_plink_maint_enter(struct super_block *sb); -+#ifdef CONFIG_AUFS_DEBUG -+void au_plink_list(struct super_block *sb); -+#else -+AuStubVoid(au_plink_list, struct super_block *sb) -+#endif -+int au_plink_test(struct inode *inode); -+struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex); -+void au_plink_append(struct inode *inode, aufs_bindex_t bindex, -+ struct dentry *h_dentry); -+void au_plink_put(struct super_block *sb, int verbose); -+void au_plink_clean(struct super_block *sb, int verbose); -+void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id); -+#else -+AuStubInt0(au_plink_maint, struct super_block *sb, int flags); -+AuStubVoid(au_plink_maint_leave, struct au_sbinfo *sbinfo); -+AuStubInt0(au_plink_maint_enter, struct super_block *sb); -+AuStubVoid(au_plink_list, struct super_block *sb); -+AuStubInt0(au_plink_test, struct inode *inode); -+AuStub(struct dentry *, au_plink_lkup, return NULL, -+ struct inode *inode, aufs_bindex_t bindex); -+AuStubVoid(au_plink_append, struct inode *inode, aufs_bindex_t bindex, -+ struct dentry *h_dentry); -+AuStubVoid(au_plink_put, struct super_block *sb, int verbose); -+AuStubVoid(au_plink_clean, struct super_block *sb, int verbose); -+AuStubVoid(au_plink_half_refresh, struct super_block *sb, aufs_bindex_t br_id); -+#endif /* CONFIG_PROC_FS */ -+ -+#ifdef CONFIG_AUFS_XATTR -+/* xattr.c */ -+int au_cpup_xattr(struct dentry *h_dst, struct dentry *h_src, int ignore_flags, -+ unsigned int verbose); -+ssize_t aufs_listxattr(struct dentry *dentry, char *list, size_t size); -+ssize_t aufs_getxattr(struct dentry *dentry, const char *name, void *value, -+ size_t size); -+int aufs_setxattr(struct dentry *dentry, const char *name, const void *value, -+ size_t size, int flags); -+int aufs_removexattr(struct dentry *dentry, const char *name); -+ -+/* void au_xattr_init(struct super_block *sb); */ -+#else -+AuStubInt0(au_cpup_xattr, struct dentry *h_dst, struct dentry *h_src, -+ int ignore_flags, unsigned int verbose); -+/* AuStubVoid(au_xattr_init, struct super_block *sb); */ -+#endif -+ -+#ifdef CONFIG_FS_POSIX_ACL -+struct posix_acl *aufs_get_acl(struct inode *inode, int type); -+int aufs_set_acl(struct inode *inode, struct posix_acl *acl, int type); -+#endif -+ -+#if IS_ENABLED(CONFIG_AUFS_XATTR) || IS_ENABLED(CONFIG_FS_POSIX_ACL) -+enum { -+ AU_XATTR_SET, -+ AU_XATTR_REMOVE, -+ AU_ACL_SET -+}; -+ -+struct au_srxattr { -+ int type; -+ union { -+ struct { -+ const char *name; -+ const void *value; -+ size_t size; -+ int flags; -+ } set; -+ struct { -+ const char *name; -+ } remove; -+ struct { -+ struct posix_acl *acl; -+ int type; -+ } acl_set; -+ } u; -+}; -+ssize_t au_srxattr(struct dentry *dentry, struct au_srxattr *arg); -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* lock subclass for iinfo */ -+enum { -+ AuLsc_II_CHILD, /* child first */ -+ AuLsc_II_CHILD2, /* rename(2), link(2), and cpup at hnotify */ -+ AuLsc_II_CHILD3, /* copyup dirs */ -+ AuLsc_II_PARENT, /* see AuLsc_I_PARENT in vfsub.h */ -+ AuLsc_II_PARENT2, -+ AuLsc_II_PARENT3, /* copyup dirs */ -+ AuLsc_II_NEW_CHILD -+}; -+ -+/* -+ * ii_read_lock_child, ii_write_lock_child, -+ * ii_read_lock_child2, ii_write_lock_child2, -+ * ii_read_lock_child3, ii_write_lock_child3, -+ * ii_read_lock_parent, ii_write_lock_parent, -+ * ii_read_lock_parent2, ii_write_lock_parent2, -+ * ii_read_lock_parent3, ii_write_lock_parent3, -+ * ii_read_lock_new_child, ii_write_lock_new_child, -+ */ -+#define AuReadLockFunc(name, lsc) \ -+static inline void ii_read_lock_##name(struct inode *i) \ -+{ \ -+ au_rw_read_lock_nested(&au_ii(i)->ii_rwsem, AuLsc_II_##lsc); \ -+} -+ -+#define AuWriteLockFunc(name, lsc) \ -+static inline void ii_write_lock_##name(struct inode *i) \ -+{ \ -+ au_rw_write_lock_nested(&au_ii(i)->ii_rwsem, AuLsc_II_##lsc); \ -+} -+ -+#define AuRWLockFuncs(name, lsc) \ -+ AuReadLockFunc(name, lsc) \ -+ AuWriteLockFunc(name, lsc) -+ -+AuRWLockFuncs(child, CHILD); -+AuRWLockFuncs(child2, CHILD2); -+AuRWLockFuncs(child3, CHILD3); -+AuRWLockFuncs(parent, PARENT); -+AuRWLockFuncs(parent2, PARENT2); -+AuRWLockFuncs(parent3, PARENT3); -+AuRWLockFuncs(new_child, NEW_CHILD); -+ -+#undef AuReadLockFunc -+#undef AuWriteLockFunc -+#undef AuRWLockFuncs -+ -+/* -+ * ii_read_unlock, ii_write_unlock, ii_downgrade_lock -+ */ -+AuSimpleUnlockRwsemFuncs(ii, struct inode *i, &au_ii(i)->ii_rwsem); -+ -+#define IiMustNoWaiters(i) AuRwMustNoWaiters(&au_ii(i)->ii_rwsem) -+#define IiMustAnyLock(i) AuRwMustAnyLock(&au_ii(i)->ii_rwsem) -+#define IiMustWriteLock(i) AuRwMustWriteLock(&au_ii(i)->ii_rwsem) -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline void au_icntnr_init(struct au_icntnr *c) -+{ -+#ifdef CONFIG_AUFS_DEBUG -+ c->vfs_inode.i_mode = 0; -+#endif -+} -+ -+static inline unsigned int au_iigen(struct inode *inode, unsigned int *igflags) -+{ -+ unsigned int gen; -+ struct au_iinfo *iinfo; -+ struct au_iigen *iigen; -+ -+ iinfo = au_ii(inode); -+ iigen = &iinfo->ii_generation; -+ spin_lock(&iigen->ig_spin); -+ if (igflags) -+ *igflags = iigen->ig_flags; -+ gen = iigen->ig_generation; -+ spin_unlock(&iigen->ig_spin); -+ -+ return gen; -+} -+ -+/* tiny test for inode number */ -+/* tmpfs generation is too rough */ -+static inline int au_test_higen(struct inode *inode, struct inode *h_inode) -+{ -+ struct au_iinfo *iinfo; -+ -+ iinfo = au_ii(inode); -+ AuRwMustAnyLock(&iinfo->ii_rwsem); -+ return !(iinfo->ii_hsb1 == h_inode->i_sb -+ && iinfo->ii_higen == h_inode->i_generation); -+} -+ -+static inline void au_iigen_dec(struct inode *inode) -+{ -+ struct au_iinfo *iinfo; -+ struct au_iigen *iigen; -+ -+ iinfo = au_ii(inode); -+ iigen = &iinfo->ii_generation; -+ spin_lock(&iigen->ig_spin); -+ iigen->ig_generation--; -+ spin_unlock(&iigen->ig_spin); -+} -+ -+static inline int au_iigen_test(struct inode *inode, unsigned int sigen) -+{ -+ int err; -+ -+ err = 0; -+ if (unlikely(inode && au_iigen(inode, NULL) != sigen)) -+ err = -EIO; -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline aufs_bindex_t au_ii_br_id(struct inode *inode, -+ aufs_bindex_t bindex) -+{ -+ IiMustAnyLock(inode); -+ return au_ii(inode)->ii_hinode[0 + bindex].hi_id; -+} -+ -+static inline aufs_bindex_t au_ibstart(struct inode *inode) -+{ -+ IiMustAnyLock(inode); -+ return au_ii(inode)->ii_bstart; -+} -+ -+static inline aufs_bindex_t au_ibend(struct inode *inode) -+{ -+ IiMustAnyLock(inode); -+ return au_ii(inode)->ii_bend; -+} -+ -+static inline struct au_vdir *au_ivdir(struct inode *inode) -+{ -+ IiMustAnyLock(inode); -+ return au_ii(inode)->ii_vdir; -+} -+ -+static inline struct dentry *au_hi_wh(struct inode *inode, aufs_bindex_t bindex) -+{ -+ IiMustAnyLock(inode); -+ return au_ii(inode)->ii_hinode[0 + bindex].hi_whdentry; -+} -+ -+static inline void au_set_ibstart(struct inode *inode, aufs_bindex_t bindex) -+{ -+ IiMustWriteLock(inode); -+ au_ii(inode)->ii_bstart = bindex; -+} -+ -+static inline void au_set_ibend(struct inode *inode, aufs_bindex_t bindex) -+{ -+ IiMustWriteLock(inode); -+ au_ii(inode)->ii_bend = bindex; -+} -+ -+static inline void au_set_ivdir(struct inode *inode, struct au_vdir *vdir) -+{ -+ IiMustWriteLock(inode); -+ au_ii(inode)->ii_vdir = vdir; -+} -+ -+static inline struct au_hinode *au_hi(struct inode *inode, aufs_bindex_t bindex) -+{ -+ IiMustAnyLock(inode); -+ return au_ii(inode)->ii_hinode + bindex; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline struct dentry *au_pinned_parent(struct au_pin *pin) -+{ -+ if (pin) -+ return pin->parent; -+ return NULL; -+} -+ -+static inline struct inode *au_pinned_h_dir(struct au_pin *pin) -+{ -+ if (pin && pin->hdir) -+ return pin->hdir->hi_inode; -+ return NULL; -+} -+ -+static inline struct au_hinode *au_pinned_hdir(struct au_pin *pin) -+{ -+ if (pin) -+ return pin->hdir; -+ return NULL; -+} -+ -+static inline void au_pin_set_dentry(struct au_pin *pin, struct dentry *dentry) -+{ -+ if (pin) -+ pin->dentry = dentry; -+} -+ -+static inline void au_pin_set_parent_lflag(struct au_pin *pin, -+ unsigned char lflag) -+{ -+ if (pin) { -+ if (lflag) -+ au_fset_pin(pin->flags, DI_LOCKED); -+ else -+ au_fclr_pin(pin->flags, DI_LOCKED); -+ } -+} -+ -+#if 0 /* reserved */ -+static inline void au_pin_set_parent(struct au_pin *pin, struct dentry *parent) -+{ -+ if (pin) { -+ dput(pin->parent); -+ pin->parent = dget(parent); -+ } -+} -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct au_branch; -+#ifdef CONFIG_AUFS_HNOTIFY -+struct au_hnotify_op { -+ void (*ctl)(struct au_hinode *hinode, int do_set); -+ int (*alloc)(struct au_hinode *hinode); -+ -+ /* -+ * if it returns true, the the caller should free hinode->hi_notify, -+ * otherwise ->free() frees it. -+ */ -+ int (*free)(struct au_hinode *hinode, -+ struct au_hnotify *hn) __must_check; -+ -+ void (*fin)(void); -+ int (*init)(void); -+ -+ int (*reset_br)(unsigned int udba, struct au_branch *br, int perm); -+ void (*fin_br)(struct au_branch *br); -+ int (*init_br)(struct au_branch *br, int perm); -+}; -+ -+/* hnotify.c */ -+int au_hn_alloc(struct au_hinode *hinode, struct inode *inode); -+void au_hn_free(struct au_hinode *hinode); -+void au_hn_ctl(struct au_hinode *hinode, int do_set); -+void au_hn_reset(struct inode *inode, unsigned int flags); -+int au_hnotify(struct inode *h_dir, struct au_hnotify *hnotify, u32 mask, -+ struct qstr *h_child_qstr, struct inode *h_child_inode); -+int au_hnotify_reset_br(unsigned int udba, struct au_branch *br, int perm); -+int au_hnotify_init_br(struct au_branch *br, int perm); -+void au_hnotify_fin_br(struct au_branch *br); -+int __init au_hnotify_init(void); -+void au_hnotify_fin(void); -+ -+/* hfsnotify.c */ -+extern const struct au_hnotify_op au_hnotify_op; -+ -+static inline -+void au_hn_init(struct au_hinode *hinode) -+{ -+ hinode->hi_notify = NULL; -+} -+ -+static inline struct au_hnotify *au_hn(struct au_hinode *hinode) -+{ -+ return hinode->hi_notify; -+} -+ -+#else -+AuStub(int, au_hn_alloc, return -EOPNOTSUPP, -+ struct au_hinode *hinode __maybe_unused, -+ struct inode *inode __maybe_unused) -+AuStub(struct au_hnotify *, au_hn, return NULL, struct au_hinode *hinode) -+AuStubVoid(au_hn_free, struct au_hinode *hinode __maybe_unused) -+AuStubVoid(au_hn_ctl, struct au_hinode *hinode __maybe_unused, -+ int do_set __maybe_unused) -+AuStubVoid(au_hn_reset, struct inode *inode __maybe_unused, -+ unsigned int flags __maybe_unused) -+AuStubInt0(au_hnotify_reset_br, unsigned int udba __maybe_unused, -+ struct au_branch *br __maybe_unused, -+ int perm __maybe_unused) -+AuStubInt0(au_hnotify_init_br, struct au_branch *br __maybe_unused, -+ int perm __maybe_unused) -+AuStubVoid(au_hnotify_fin_br, struct au_branch *br __maybe_unused) -+AuStubInt0(__init au_hnotify_init, void) -+AuStubVoid(au_hnotify_fin, void) -+AuStubVoid(au_hn_init, struct au_hinode *hinode __maybe_unused) -+#endif /* CONFIG_AUFS_HNOTIFY */ -+ -+static inline void au_hn_suspend(struct au_hinode *hdir) -+{ -+ au_hn_ctl(hdir, /*do_set*/0); -+} -+ -+static inline void au_hn_resume(struct au_hinode *hdir) -+{ -+ au_hn_ctl(hdir, /*do_set*/1); -+} -+ -+static inline void au_hn_imtx_lock(struct au_hinode *hdir) -+{ -+ mutex_lock(&hdir->hi_inode->i_mutex); -+ au_hn_suspend(hdir); -+} -+ -+static inline void au_hn_imtx_lock_nested(struct au_hinode *hdir, -+ unsigned int sc __maybe_unused) -+{ -+ mutex_lock_nested(&hdir->hi_inode->i_mutex, sc); -+ au_hn_suspend(hdir); -+} -+ -+static inline void au_hn_imtx_unlock(struct au_hinode *hdir) -+{ -+ au_hn_resume(hdir); -+ mutex_unlock(&hdir->hi_inode->i_mutex); -+} -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_INODE_H__ */ -diff --git a/fs/aufs/ioctl.c b/fs/aufs/ioctl.c -new file mode 100644 -index 0000000..10e2315 ---- /dev/null -+++ b/fs/aufs/ioctl.c -@@ -0,0 +1,219 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * ioctl -+ * plink-management and readdir in userspace. -+ * assist the pathconf(3) wrapper library. -+ * move-down -+ * File-based Hierarchical Storage Management. -+ */ -+ -+#include -+#include -+#include "aufs.h" -+ -+static int au_wbr_fd(struct path *path, struct aufs_wbr_fd __user *arg) -+{ -+ int err, fd; -+ aufs_bindex_t wbi, bindex, bend; -+ struct file *h_file; -+ struct super_block *sb; -+ struct dentry *root; -+ struct au_branch *br; -+ struct aufs_wbr_fd wbrfd = { -+ .oflags = au_dir_roflags, -+ .brid = -1 -+ }; -+ const int valid = O_RDONLY | O_NONBLOCK | O_LARGEFILE | O_DIRECTORY -+ | O_NOATIME | O_CLOEXEC; -+ -+ AuDebugOn(wbrfd.oflags & ~valid); -+ -+ if (arg) { -+ err = copy_from_user(&wbrfd, arg, sizeof(wbrfd)); -+ if (unlikely(err)) { -+ err = -EFAULT; -+ goto out; -+ } -+ -+ err = -EINVAL; -+ AuDbg("wbrfd{0%o, %d}\n", wbrfd.oflags, wbrfd.brid); -+ wbrfd.oflags |= au_dir_roflags; -+ AuDbg("0%o\n", wbrfd.oflags); -+ if (unlikely(wbrfd.oflags & ~valid)) -+ goto out; -+ } -+ -+ fd = get_unused_fd(); -+ err = fd; -+ if (unlikely(fd < 0)) -+ goto out; -+ -+ h_file = ERR_PTR(-EINVAL); -+ wbi = 0; -+ br = NULL; -+ sb = path->dentry->d_sb; -+ root = sb->s_root; -+ aufs_read_lock(root, AuLock_IR); -+ bend = au_sbend(sb); -+ if (wbrfd.brid >= 0) { -+ wbi = au_br_index(sb, wbrfd.brid); -+ if (unlikely(wbi < 0 || wbi > bend)) -+ goto out_unlock; -+ } -+ -+ h_file = ERR_PTR(-ENOENT); -+ br = au_sbr(sb, wbi); -+ if (!au_br_writable(br->br_perm)) { -+ if (arg) -+ goto out_unlock; -+ -+ bindex = wbi + 1; -+ wbi = -1; -+ for (; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ if (au_br_writable(br->br_perm)) { -+ wbi = bindex; -+ br = au_sbr(sb, wbi); -+ break; -+ } -+ } -+ } -+ AuDbg("wbi %d\n", wbi); -+ if (wbi >= 0) -+ h_file = au_h_open(root, wbi, wbrfd.oflags, NULL, -+ /*force_wr*/0); -+ -+out_unlock: -+ aufs_read_unlock(root, AuLock_IR); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out_fd; -+ -+ atomic_dec(&br->br_count); /* cf. au_h_open() */ -+ fd_install(fd, h_file); -+ err = fd; -+ goto out; /* success */ -+ -+out_fd: -+ put_unused_fd(fd); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+long aufs_ioctl_dir(struct file *file, unsigned int cmd, unsigned long arg) -+{ -+ long err; -+ struct dentry *dentry; -+ -+ switch (cmd) { -+ case AUFS_CTL_RDU: -+ case AUFS_CTL_RDU_INO: -+ err = au_rdu_ioctl(file, cmd, arg); -+ break; -+ -+ case AUFS_CTL_WBR_FD: -+ err = au_wbr_fd(&file->f_path, (void __user *)arg); -+ break; -+ -+ case AUFS_CTL_IBUSY: -+ err = au_ibusy_ioctl(file, arg); -+ break; -+ -+ case AUFS_CTL_BRINFO: -+ err = au_brinfo_ioctl(file, arg); -+ break; -+ -+ case AUFS_CTL_FHSM_FD: -+ dentry = file->f_dentry; -+ if (IS_ROOT(dentry)) -+ err = au_fhsm_fd(dentry->d_sb, arg); -+ else -+ err = -ENOTTY; -+ break; -+ -+ default: -+ /* do not call the lower */ -+ AuDbg("0x%x\n", cmd); -+ err = -ENOTTY; -+ } -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+long aufs_ioctl_nondir(struct file *file, unsigned int cmd, unsigned long arg) -+{ -+ long err; -+ -+ switch (cmd) { -+ case AUFS_CTL_MVDOWN: -+ err = au_mvdown(file->f_dentry, (void __user *)arg); -+ break; -+ -+ case AUFS_CTL_WBR_FD: -+ err = au_wbr_fd(&file->f_path, (void __user *)arg); -+ break; -+ -+ default: -+ /* do not call the lower */ -+ AuDbg("0x%x\n", cmd); -+ err = -ENOTTY; -+ } -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+#ifdef CONFIG_COMPAT -+long aufs_compat_ioctl_dir(struct file *file, unsigned int cmd, -+ unsigned long arg) -+{ -+ long err; -+ -+ switch (cmd) { -+ case AUFS_CTL_RDU: -+ case AUFS_CTL_RDU_INO: -+ err = au_rdu_compat_ioctl(file, cmd, arg); -+ break; -+ -+ case AUFS_CTL_IBUSY: -+ err = au_ibusy_compat_ioctl(file, arg); -+ break; -+ -+ case AUFS_CTL_BRINFO: -+ err = au_brinfo_compat_ioctl(file, arg); -+ break; -+ -+ default: -+ err = aufs_ioctl_dir(file, cmd, arg); -+ } -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+long aufs_compat_ioctl_nondir(struct file *file, unsigned int cmd, -+ unsigned long arg) -+{ -+ return aufs_ioctl_nondir(file, cmd, (unsigned long)compat_ptr(arg)); -+} -+#endif -diff --git a/fs/aufs/loop.c b/fs/aufs/loop.c -new file mode 100644 -index 0000000..1eaf59f ---- /dev/null -+++ b/fs/aufs/loop.c -@@ -0,0 +1,146 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * support for loopback block device as a branch -+ */ -+ -+#include "aufs.h" -+ -+/* added into drivers/block/loop.c */ -+static struct file *(*backing_file_func)(struct super_block *sb); -+ -+/* -+ * test if two lower dentries have overlapping branches. -+ */ -+int au_test_loopback_overlap(struct super_block *sb, struct dentry *h_adding) -+{ -+ struct super_block *h_sb; -+ struct file *backing_file; -+ -+ if (unlikely(!backing_file_func)) { -+ /* don't load "loop" module here */ -+ backing_file_func = symbol_get(loop_backing_file); -+ if (unlikely(!backing_file_func)) -+ /* "loop" module is not loaded */ -+ return 0; -+ } -+ -+ h_sb = h_adding->d_sb; -+ backing_file = backing_file_func(h_sb); -+ if (!backing_file) -+ return 0; -+ -+ h_adding = backing_file->f_dentry; -+ /* -+ * h_adding can be local NFS. -+ * in this case aufs cannot detect the loop. -+ */ -+ if (unlikely(h_adding->d_sb == sb)) -+ return 1; -+ return !!au_test_subdir(h_adding, sb->s_root); -+} -+ -+/* true if a kernel thread named 'loop[0-9].*' accesses a file */ -+int au_test_loopback_kthread(void) -+{ -+ int ret; -+ struct task_struct *tsk = current; -+ char c, comm[sizeof(tsk->comm)]; -+ -+ ret = 0; -+ if (tsk->flags & PF_KTHREAD) { -+ get_task_comm(comm, tsk); -+ c = comm[4]; -+ ret = ('0' <= c && c <= '9' -+ && !strncmp(comm, "loop", 4)); -+ } -+ -+ return ret; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+#define au_warn_loopback_step 16 -+static int au_warn_loopback_nelem = au_warn_loopback_step; -+static unsigned long *au_warn_loopback_array; -+ -+void au_warn_loopback(struct super_block *h_sb) -+{ -+ int i, new_nelem; -+ unsigned long *a, magic; -+ static DEFINE_SPINLOCK(spin); -+ -+ magic = h_sb->s_magic; -+ spin_lock(&spin); -+ a = au_warn_loopback_array; -+ for (i = 0; i < au_warn_loopback_nelem && *a; i++) -+ if (a[i] == magic) { -+ spin_unlock(&spin); -+ return; -+ } -+ -+ /* h_sb is new to us, print it */ -+ if (i < au_warn_loopback_nelem) { -+ a[i] = magic; -+ goto pr; -+ } -+ -+ /* expand the array */ -+ new_nelem = au_warn_loopback_nelem + au_warn_loopback_step; -+ a = au_kzrealloc(au_warn_loopback_array, -+ au_warn_loopback_nelem * sizeof(unsigned long), -+ new_nelem * sizeof(unsigned long), GFP_ATOMIC); -+ if (a) { -+ au_warn_loopback_nelem = new_nelem; -+ au_warn_loopback_array = a; -+ a[i] = magic; -+ goto pr; -+ } -+ -+ spin_unlock(&spin); -+ AuWarn1("realloc failed, ignored\n"); -+ return; -+ -+pr: -+ spin_unlock(&spin); -+ pr_warn("you may want to try another patch for loopback file " -+ "on %s(0x%lx) branch\n", au_sbtype(h_sb), magic); -+} -+ -+int au_loopback_init(void) -+{ -+ int err; -+ struct super_block *sb __maybe_unused; -+ -+ BUILD_BUG_ON(sizeof(sb->s_magic) != sizeof(unsigned long)); -+ -+ err = 0; -+ au_warn_loopback_array = kcalloc(au_warn_loopback_step, -+ sizeof(unsigned long), GFP_NOFS); -+ if (unlikely(!au_warn_loopback_array)) -+ err = -ENOMEM; -+ -+ return err; -+} -+ -+void au_loopback_fin(void) -+{ -+ if (backing_file_func) -+ symbol_put(loop_backing_file); -+ kfree(au_warn_loopback_array); -+} -diff --git a/fs/aufs/loop.h b/fs/aufs/loop.h -new file mode 100644 -index 0000000..35f7446 ---- /dev/null -+++ b/fs/aufs/loop.h -@@ -0,0 +1,52 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * support for loopback mount as a branch -+ */ -+ -+#ifndef __AUFS_LOOP_H__ -+#define __AUFS_LOOP_H__ -+ -+#ifdef __KERNEL__ -+ -+struct dentry; -+struct super_block; -+ -+#ifdef CONFIG_AUFS_BDEV_LOOP -+/* drivers/block/loop.c */ -+struct file *loop_backing_file(struct super_block *sb); -+ -+/* loop.c */ -+int au_test_loopback_overlap(struct super_block *sb, struct dentry *h_adding); -+int au_test_loopback_kthread(void); -+void au_warn_loopback(struct super_block *h_sb); -+ -+int au_loopback_init(void); -+void au_loopback_fin(void); -+#else -+AuStubInt0(au_test_loopback_overlap, struct super_block *sb, -+ struct dentry *h_adding) -+AuStubInt0(au_test_loopback_kthread, void) -+AuStubVoid(au_warn_loopback, struct super_block *h_sb) -+ -+AuStubInt0(au_loopback_init, void) -+AuStubVoid(au_loopback_fin, void) -+#endif /* BLK_DEV_LOOP */ -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_LOOP_H__ */ -diff --git a/fs/aufs/magic.mk b/fs/aufs/magic.mk -new file mode 100644 -index 0000000..4f83bdf ---- /dev/null -+++ b/fs/aufs/magic.mk -@@ -0,0 +1,30 @@ -+ -+# defined in ${srctree}/fs/fuse/inode.c -+# tristate -+ifdef CONFIG_FUSE_FS -+ccflags-y += -DFUSE_SUPER_MAGIC=0x65735546 -+endif -+ -+# defined in ${srctree}/fs/xfs/xfs_sb.h -+# tristate -+ifdef CONFIG_XFS_FS -+ccflags-y += -DXFS_SB_MAGIC=0x58465342 -+endif -+ -+# defined in ${srctree}/fs/configfs/mount.c -+# tristate -+ifdef CONFIG_CONFIGFS_FS -+ccflags-y += -DCONFIGFS_MAGIC=0x62656570 -+endif -+ -+# defined in ${srctree}/fs/ubifs/ubifs.h -+# tristate -+ifdef CONFIG_UBIFS_FS -+ccflags-y += -DUBIFS_SUPER_MAGIC=0x24051905 -+endif -+ -+# defined in ${srctree}/fs/hfsplus/hfsplus_raw.h -+# tristate -+ifdef CONFIG_HFSPLUS_FS -+ccflags-y += -DHFSPLUS_SUPER_MAGIC=0x482b -+endif -diff --git a/fs/aufs/module.c b/fs/aufs/module.c -new file mode 100644 -index 0000000..e4e04aa ---- /dev/null -+++ b/fs/aufs/module.c -@@ -0,0 +1,222 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * module global variables and operations -+ */ -+ -+#include -+#include -+#include "aufs.h" -+ -+void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp) -+{ -+ if (new_sz <= nused) -+ return p; -+ -+ p = krealloc(p, new_sz, gfp); -+ if (p) -+ memset(p + nused, 0, new_sz - nused); -+ return p; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * aufs caches -+ */ -+struct kmem_cache *au_cachep[AuCache_Last]; -+static int __init au_cache_init(void) -+{ -+ au_cachep[AuCache_DINFO] = AuCacheCtor(au_dinfo, au_di_init_once); -+ if (au_cachep[AuCache_DINFO]) -+ /* SLAB_DESTROY_BY_RCU */ -+ au_cachep[AuCache_ICNTNR] = AuCacheCtor(au_icntnr, -+ au_icntnr_init_once); -+ if (au_cachep[AuCache_ICNTNR]) -+ au_cachep[AuCache_FINFO] = AuCacheCtor(au_finfo, -+ au_fi_init_once); -+ if (au_cachep[AuCache_FINFO]) -+ au_cachep[AuCache_VDIR] = AuCache(au_vdir); -+ if (au_cachep[AuCache_VDIR]) -+ au_cachep[AuCache_DEHSTR] = AuCache(au_vdir_dehstr); -+ if (au_cachep[AuCache_DEHSTR]) -+ return 0; -+ -+ return -ENOMEM; -+} -+ -+static void au_cache_fin(void) -+{ -+ int i; -+ -+ /* -+ * Make sure all delayed rcu free inodes are flushed before we -+ * destroy cache. -+ */ -+ rcu_barrier(); -+ -+ /* excluding AuCache_HNOTIFY */ -+ BUILD_BUG_ON(AuCache_HNOTIFY + 1 != AuCache_Last); -+ for (i = 0; i < AuCache_HNOTIFY; i++) -+ if (au_cachep[i]) { -+ kmem_cache_destroy(au_cachep[i]); -+ au_cachep[i] = NULL; -+ } -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int au_dir_roflags; -+ -+#ifdef CONFIG_AUFS_SBILIST -+/* -+ * iterate_supers_type() doesn't protect us from -+ * remounting (branch management) -+ */ -+struct au_sphlhead au_sbilist; -+#endif -+ -+struct lock_class_key au_lc_key[AuLcKey_Last]; -+ -+/* -+ * functions for module interface. -+ */ -+MODULE_LICENSE("GPL"); -+/* MODULE_LICENSE("GPL v2"); */ -+MODULE_AUTHOR("Junjiro R. Okajima "); -+MODULE_DESCRIPTION(AUFS_NAME -+ " -- Advanced multi layered unification filesystem"); -+MODULE_VERSION(AUFS_VERSION); -+MODULE_ALIAS_FS(AUFS_NAME); -+ -+/* this module parameter has no meaning when SYSFS is disabled */ -+int sysaufs_brs = 1; -+MODULE_PARM_DESC(brs, "use /fs/aufs/si_*/brN"); -+module_param_named(brs, sysaufs_brs, int, S_IRUGO); -+ -+/* this module parameter has no meaning when USER_NS is disabled */ -+bool au_userns; -+MODULE_PARM_DESC(allow_userns, "allow unprivileged to mount under userns"); -+module_param_named(allow_userns, au_userns, bool, S_IRUGO); -+ -+/* ---------------------------------------------------------------------- */ -+ -+static char au_esc_chars[0x20 + 3]; /* 0x01-0x20, backslash, del, and NULL */ -+ -+int au_seq_path(struct seq_file *seq, struct path *path) -+{ -+ int err; -+ -+ err = seq_path(seq, path, au_esc_chars); -+ if (err > 0) -+ err = 0; -+ else if (err < 0) -+ err = -ENOMEM; -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int __init aufs_init(void) -+{ -+ int err, i; -+ char *p; -+ -+ p = au_esc_chars; -+ for (i = 1; i <= ' '; i++) -+ *p++ = i; -+ *p++ = '\\'; -+ *p++ = '\x7f'; -+ *p = 0; -+ -+ au_dir_roflags = au_file_roflags(O_DIRECTORY | O_LARGEFILE); -+ -+ memcpy(aufs_iop_nogetattr, aufs_iop, sizeof(aufs_iop)); -+ for (i = 0; i < AuIop_Last; i++) -+ aufs_iop_nogetattr[i].getattr = NULL; -+ -+ au_sbilist_init(); -+ sysaufs_brs_init(); -+ au_debug_init(); -+ au_dy_init(); -+ err = sysaufs_init(); -+ if (unlikely(err)) -+ goto out; -+ err = au_procfs_init(); -+ if (unlikely(err)) -+ goto out_sysaufs; -+ err = au_wkq_init(); -+ if (unlikely(err)) -+ goto out_procfs; -+ err = au_loopback_init(); -+ if (unlikely(err)) -+ goto out_wkq; -+ err = au_hnotify_init(); -+ if (unlikely(err)) -+ goto out_loopback; -+ err = au_sysrq_init(); -+ if (unlikely(err)) -+ goto out_hin; -+ err = au_cache_init(); -+ if (unlikely(err)) -+ goto out_sysrq; -+ -+ aufs_fs_type.fs_flags |= au_userns ? FS_USERNS_MOUNT : 0; -+ err = register_filesystem(&aufs_fs_type); -+ if (unlikely(err)) -+ goto out_cache; -+ -+ /* since we define pr_fmt, call printk directly */ -+ printk(KERN_INFO AUFS_NAME " " AUFS_VERSION "\n"); -+ goto out; /* success */ -+ -+out_cache: -+ au_cache_fin(); -+out_sysrq: -+ au_sysrq_fin(); -+out_hin: -+ au_hnotify_fin(); -+out_loopback: -+ au_loopback_fin(); -+out_wkq: -+ au_wkq_fin(); -+out_procfs: -+ au_procfs_fin(); -+out_sysaufs: -+ sysaufs_fin(); -+ au_dy_fin(); -+out: -+ return err; -+} -+ -+static void __exit aufs_exit(void) -+{ -+ unregister_filesystem(&aufs_fs_type); -+ au_cache_fin(); -+ au_sysrq_fin(); -+ au_hnotify_fin(); -+ au_loopback_fin(); -+ au_wkq_fin(); -+ au_procfs_fin(); -+ sysaufs_fin(); -+ au_dy_fin(); -+} -+ -+module_init(aufs_init); -+module_exit(aufs_exit); -diff --git a/fs/aufs/module.h b/fs/aufs/module.h -new file mode 100644 -index 0000000..90c3c8f ---- /dev/null -+++ b/fs/aufs/module.h -@@ -0,0 +1,105 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * module initialization and module-global -+ */ -+ -+#ifndef __AUFS_MODULE_H__ -+#define __AUFS_MODULE_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+ -+struct path; -+struct seq_file; -+ -+/* module parameters */ -+extern int sysaufs_brs; -+extern bool au_userns; -+ -+/* ---------------------------------------------------------------------- */ -+ -+extern int au_dir_roflags; -+ -+enum { -+ AuLcNonDir_FIINFO, -+ AuLcNonDir_DIINFO, -+ AuLcNonDir_IIINFO, -+ -+ AuLcDir_FIINFO, -+ AuLcDir_DIINFO, -+ AuLcDir_IIINFO, -+ -+ AuLcSymlink_DIINFO, -+ AuLcSymlink_IIINFO, -+ -+ AuLcKey_Last -+}; -+extern struct lock_class_key au_lc_key[AuLcKey_Last]; -+ -+void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp); -+int au_seq_path(struct seq_file *seq, struct path *path); -+ -+#ifdef CONFIG_PROC_FS -+/* procfs.c */ -+int __init au_procfs_init(void); -+void au_procfs_fin(void); -+#else -+AuStubInt0(au_procfs_init, void); -+AuStubVoid(au_procfs_fin, void); -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* kmem cache */ -+enum { -+ AuCache_DINFO, -+ AuCache_ICNTNR, -+ AuCache_FINFO, -+ AuCache_VDIR, -+ AuCache_DEHSTR, -+ AuCache_HNOTIFY, /* must be last */ -+ AuCache_Last -+}; -+ -+#define AuCacheFlags (SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD) -+#define AuCache(type) KMEM_CACHE(type, AuCacheFlags) -+#define AuCacheCtor(type, ctor) \ -+ kmem_cache_create(#type, sizeof(struct type), \ -+ __alignof__(struct type), AuCacheFlags, ctor) -+ -+extern struct kmem_cache *au_cachep[]; -+ -+#define AuCacheFuncs(name, index) \ -+static inline struct au_##name *au_cache_alloc_##name(void) \ -+{ return kmem_cache_alloc(au_cachep[AuCache_##index], GFP_NOFS); } \ -+static inline void au_cache_free_##name(struct au_##name *p) \ -+{ kmem_cache_free(au_cachep[AuCache_##index], p); } -+ -+AuCacheFuncs(dinfo, DINFO); -+AuCacheFuncs(icntnr, ICNTNR); -+AuCacheFuncs(finfo, FINFO); -+AuCacheFuncs(vdir, VDIR); -+AuCacheFuncs(vdir_dehstr, DEHSTR); -+#ifdef CONFIG_AUFS_HNOTIFY -+AuCacheFuncs(hnotify, HNOTIFY); -+#endif -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_MODULE_H__ */ -diff --git a/fs/aufs/mvdown.c b/fs/aufs/mvdown.c -new file mode 100644 -index 0000000..e660c8f ---- /dev/null -+++ b/fs/aufs/mvdown.c -@@ -0,0 +1,703 @@ -+/* -+ * Copyright (C) 2011-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * move-down, opposite of copy-up -+ */ -+ -+#include "aufs.h" -+ -+struct au_mvd_args { -+ struct { -+ struct super_block *h_sb; -+ struct dentry *h_parent; -+ struct au_hinode *hdir; -+ struct inode *h_dir, *h_inode; -+ struct au_pin pin; -+ } info[AUFS_MVDOWN_NARRAY]; -+ -+ struct aufs_mvdown mvdown; -+ struct dentry *dentry, *parent; -+ struct inode *inode, *dir; -+ struct super_block *sb; -+ aufs_bindex_t bopq, bwh, bfound; -+ unsigned char rename_lock; -+}; -+ -+#define mvd_errno mvdown.au_errno -+#define mvd_bsrc mvdown.stbr[AUFS_MVDOWN_UPPER].bindex -+#define mvd_src_brid mvdown.stbr[AUFS_MVDOWN_UPPER].brid -+#define mvd_bdst mvdown.stbr[AUFS_MVDOWN_LOWER].bindex -+#define mvd_dst_brid mvdown.stbr[AUFS_MVDOWN_LOWER].brid -+ -+#define mvd_h_src_sb info[AUFS_MVDOWN_UPPER].h_sb -+#define mvd_h_src_parent info[AUFS_MVDOWN_UPPER].h_parent -+#define mvd_hdir_src info[AUFS_MVDOWN_UPPER].hdir -+#define mvd_h_src_dir info[AUFS_MVDOWN_UPPER].h_dir -+#define mvd_h_src_inode info[AUFS_MVDOWN_UPPER].h_inode -+#define mvd_pin_src info[AUFS_MVDOWN_UPPER].pin -+ -+#define mvd_h_dst_sb info[AUFS_MVDOWN_LOWER].h_sb -+#define mvd_h_dst_parent info[AUFS_MVDOWN_LOWER].h_parent -+#define mvd_hdir_dst info[AUFS_MVDOWN_LOWER].hdir -+#define mvd_h_dst_dir info[AUFS_MVDOWN_LOWER].h_dir -+#define mvd_h_dst_inode info[AUFS_MVDOWN_LOWER].h_inode -+#define mvd_pin_dst info[AUFS_MVDOWN_LOWER].pin -+ -+#define AU_MVD_PR(flag, ...) do { \ -+ if (flag) \ -+ pr_err(__VA_ARGS__); \ -+ } while (0) -+ -+static int find_lower_writable(struct au_mvd_args *a) -+{ -+ struct super_block *sb; -+ aufs_bindex_t bindex, bend; -+ struct au_branch *br; -+ -+ sb = a->sb; -+ bindex = a->mvd_bsrc; -+ bend = au_sbend(sb); -+ if (a->mvdown.flags & AUFS_MVDOWN_FHSM_LOWER) -+ for (bindex++; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ if (au_br_fhsm(br->br_perm) -+ && (!(au_br_sb(br)->s_flags & MS_RDONLY))) -+ return bindex; -+ } -+ else if (!(a->mvdown.flags & AUFS_MVDOWN_ROLOWER)) -+ for (bindex++; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ if (!au_br_rdonly(br)) -+ return bindex; -+ } -+ else -+ for (bindex++; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ if (!(au_br_sb(br)->s_flags & MS_RDONLY)) { -+ if (au_br_rdonly(br)) -+ a->mvdown.flags -+ |= AUFS_MVDOWN_ROLOWER_R; -+ return bindex; -+ } -+ } -+ -+ return -1; -+} -+ -+/* make the parent dir on bdst */ -+static int au_do_mkdir(const unsigned char dmsg, struct au_mvd_args *a) -+{ -+ int err; -+ -+ err = 0; -+ a->mvd_hdir_src = au_hi(a->dir, a->mvd_bsrc); -+ a->mvd_hdir_dst = au_hi(a->dir, a->mvd_bdst); -+ a->mvd_h_src_parent = au_h_dptr(a->parent, a->mvd_bsrc); -+ a->mvd_h_dst_parent = NULL; -+ if (au_dbend(a->parent) >= a->mvd_bdst) -+ a->mvd_h_dst_parent = au_h_dptr(a->parent, a->mvd_bdst); -+ if (!a->mvd_h_dst_parent) { -+ err = au_cpdown_dirs(a->dentry, a->mvd_bdst); -+ if (unlikely(err)) { -+ AU_MVD_PR(dmsg, "cpdown_dirs failed\n"); -+ goto out; -+ } -+ a->mvd_h_dst_parent = au_h_dptr(a->parent, a->mvd_bdst); -+ } -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* lock them all */ -+static int au_do_lock(const unsigned char dmsg, struct au_mvd_args *a) -+{ -+ int err; -+ struct dentry *h_trap; -+ -+ a->mvd_h_src_sb = au_sbr_sb(a->sb, a->mvd_bsrc); -+ a->mvd_h_dst_sb = au_sbr_sb(a->sb, a->mvd_bdst); -+ err = au_pin(&a->mvd_pin_dst, a->dentry, a->mvd_bdst, -+ au_opt_udba(a->sb), -+ AuPin_MNT_WRITE | AuPin_DI_LOCKED); -+ AuTraceErr(err); -+ if (unlikely(err)) { -+ AU_MVD_PR(dmsg, "pin_dst failed\n"); -+ goto out; -+ } -+ -+ if (a->mvd_h_src_sb != a->mvd_h_dst_sb) { -+ a->rename_lock = 0; -+ au_pin_init(&a->mvd_pin_src, a->dentry, a->mvd_bsrc, -+ AuLsc_DI_PARENT, AuLsc_I_PARENT3, -+ au_opt_udba(a->sb), -+ AuPin_MNT_WRITE | AuPin_DI_LOCKED); -+ err = au_do_pin(&a->mvd_pin_src); -+ AuTraceErr(err); -+ a->mvd_h_src_dir = a->mvd_h_src_parent->d_inode; -+ if (unlikely(err)) { -+ AU_MVD_PR(dmsg, "pin_src failed\n"); -+ goto out_dst; -+ } -+ goto out; /* success */ -+ } -+ -+ a->rename_lock = 1; -+ au_pin_hdir_unlock(&a->mvd_pin_dst); -+ err = au_pin(&a->mvd_pin_src, a->dentry, a->mvd_bsrc, -+ au_opt_udba(a->sb), -+ AuPin_MNT_WRITE | AuPin_DI_LOCKED); -+ AuTraceErr(err); -+ a->mvd_h_src_dir = a->mvd_h_src_parent->d_inode; -+ if (unlikely(err)) { -+ AU_MVD_PR(dmsg, "pin_src failed\n"); -+ au_pin_hdir_lock(&a->mvd_pin_dst); -+ goto out_dst; -+ } -+ au_pin_hdir_unlock(&a->mvd_pin_src); -+ h_trap = vfsub_lock_rename(a->mvd_h_src_parent, a->mvd_hdir_src, -+ a->mvd_h_dst_parent, a->mvd_hdir_dst); -+ if (h_trap) { -+ err = (h_trap != a->mvd_h_src_parent); -+ if (err) -+ err = (h_trap != a->mvd_h_dst_parent); -+ } -+ BUG_ON(err); /* it should never happen */ -+ if (unlikely(a->mvd_h_src_dir != au_pinned_h_dir(&a->mvd_pin_src))) { -+ err = -EBUSY; -+ AuTraceErr(err); -+ vfsub_unlock_rename(a->mvd_h_src_parent, a->mvd_hdir_src, -+ a->mvd_h_dst_parent, a->mvd_hdir_dst); -+ au_pin_hdir_lock(&a->mvd_pin_src); -+ au_unpin(&a->mvd_pin_src); -+ au_pin_hdir_lock(&a->mvd_pin_dst); -+ goto out_dst; -+ } -+ goto out; /* success */ -+ -+out_dst: -+ au_unpin(&a->mvd_pin_dst); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+static void au_do_unlock(const unsigned char dmsg, struct au_mvd_args *a) -+{ -+ if (!a->rename_lock) -+ au_unpin(&a->mvd_pin_src); -+ else { -+ vfsub_unlock_rename(a->mvd_h_src_parent, a->mvd_hdir_src, -+ a->mvd_h_dst_parent, a->mvd_hdir_dst); -+ au_pin_hdir_lock(&a->mvd_pin_src); -+ au_unpin(&a->mvd_pin_src); -+ au_pin_hdir_lock(&a->mvd_pin_dst); -+ } -+ au_unpin(&a->mvd_pin_dst); -+} -+ -+/* copy-down the file */ -+static int au_do_cpdown(const unsigned char dmsg, struct au_mvd_args *a) -+{ -+ int err; -+ struct au_cp_generic cpg = { -+ .dentry = a->dentry, -+ .bdst = a->mvd_bdst, -+ .bsrc = a->mvd_bsrc, -+ .len = -1, -+ .pin = &a->mvd_pin_dst, -+ .flags = AuCpup_DTIME | AuCpup_HOPEN -+ }; -+ -+ AuDbg("b%d, b%d\n", cpg.bsrc, cpg.bdst); -+ if (a->mvdown.flags & AUFS_MVDOWN_OWLOWER) -+ au_fset_cpup(cpg.flags, OVERWRITE); -+ if (a->mvdown.flags & AUFS_MVDOWN_ROLOWER) -+ au_fset_cpup(cpg.flags, RWDST); -+ err = au_sio_cpdown_simple(&cpg); -+ if (unlikely(err)) -+ AU_MVD_PR(dmsg, "cpdown failed\n"); -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+/* -+ * unlink the whiteout on bdst if exist which may be created by UDBA while we -+ * were sleeping -+ */ -+static int au_do_unlink_wh(const unsigned char dmsg, struct au_mvd_args *a) -+{ -+ int err; -+ struct path h_path; -+ struct au_branch *br; -+ struct inode *delegated; -+ -+ br = au_sbr(a->sb, a->mvd_bdst); -+ h_path.dentry = au_wh_lkup(a->mvd_h_dst_parent, &a->dentry->d_name, br); -+ err = PTR_ERR(h_path.dentry); -+ if (IS_ERR(h_path.dentry)) { -+ AU_MVD_PR(dmsg, "wh_lkup failed\n"); -+ goto out; -+ } -+ -+ err = 0; -+ if (h_path.dentry->d_inode) { -+ h_path.mnt = au_br_mnt(br); -+ delegated = NULL; -+ err = vfsub_unlink(a->mvd_h_dst_parent->d_inode, &h_path, -+ &delegated, /*force*/0); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal unlink\n"); -+ iput(delegated); -+ } -+ if (unlikely(err)) -+ AU_MVD_PR(dmsg, "wh_unlink failed\n"); -+ } -+ dput(h_path.dentry); -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* -+ * unlink the topmost h_dentry -+ */ -+static int au_do_unlink(const unsigned char dmsg, struct au_mvd_args *a) -+{ -+ int err; -+ struct path h_path; -+ struct inode *delegated; -+ -+ h_path.mnt = au_sbr_mnt(a->sb, a->mvd_bsrc); -+ h_path.dentry = au_h_dptr(a->dentry, a->mvd_bsrc); -+ delegated = NULL; -+ err = vfsub_unlink(a->mvd_h_src_dir, &h_path, &delegated, /*force*/0); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal unlink\n"); -+ iput(delegated); -+ } -+ if (unlikely(err)) -+ AU_MVD_PR(dmsg, "unlink failed\n"); -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+/* Since mvdown succeeded, we ignore an error of this function */ -+static void au_do_stfs(const unsigned char dmsg, struct au_mvd_args *a) -+{ -+ int err; -+ struct au_branch *br; -+ -+ a->mvdown.flags |= AUFS_MVDOWN_STFS_FAILED; -+ br = au_sbr(a->sb, a->mvd_bsrc); -+ err = au_br_stfs(br, &a->mvdown.stbr[AUFS_MVDOWN_UPPER].stfs); -+ if (!err) { -+ br = au_sbr(a->sb, a->mvd_bdst); -+ a->mvdown.stbr[AUFS_MVDOWN_LOWER].brid = br->br_id; -+ err = au_br_stfs(br, &a->mvdown.stbr[AUFS_MVDOWN_LOWER].stfs); -+ } -+ if (!err) -+ a->mvdown.flags &= ~AUFS_MVDOWN_STFS_FAILED; -+ else -+ AU_MVD_PR(dmsg, "statfs failed (%d), ignored\n", err); -+} -+ -+/* -+ * copy-down the file and unlink the bsrc file. -+ * - unlink the bdst whout if exist -+ * - copy-down the file (with whtmp name and rename) -+ * - unlink the bsrc file -+ */ -+static int au_do_mvdown(const unsigned char dmsg, struct au_mvd_args *a) -+{ -+ int err; -+ -+ err = au_do_mkdir(dmsg, a); -+ if (!err) -+ err = au_do_lock(dmsg, a); -+ if (unlikely(err)) -+ goto out; -+ -+ /* -+ * do not revert the activities we made on bdst since they should be -+ * harmless in aufs. -+ */ -+ -+ err = au_do_cpdown(dmsg, a); -+ if (!err) -+ err = au_do_unlink_wh(dmsg, a); -+ if (!err && !(a->mvdown.flags & AUFS_MVDOWN_KUPPER)) -+ err = au_do_unlink(dmsg, a); -+ if (unlikely(err)) -+ goto out_unlock; -+ -+ AuDbg("%pd2, 0x%x, %d --> %d\n", -+ a->dentry, a->mvdown.flags, a->mvd_bsrc, a->mvd_bdst); -+ if (find_lower_writable(a) < 0) -+ a->mvdown.flags |= AUFS_MVDOWN_BOTTOM; -+ -+ if (a->mvdown.flags & AUFS_MVDOWN_STFS) -+ au_do_stfs(dmsg, a); -+ -+ /* maintain internal array */ -+ if (!(a->mvdown.flags & AUFS_MVDOWN_KUPPER)) { -+ au_set_h_dptr(a->dentry, a->mvd_bsrc, NULL); -+ au_set_dbstart(a->dentry, a->mvd_bdst); -+ au_set_h_iptr(a->inode, a->mvd_bsrc, NULL, /*flags*/0); -+ au_set_ibstart(a->inode, a->mvd_bdst); -+ } else { -+ /* hide the lower */ -+ au_set_h_dptr(a->dentry, a->mvd_bdst, NULL); -+ au_set_dbend(a->dentry, a->mvd_bsrc); -+ au_set_h_iptr(a->inode, a->mvd_bdst, NULL, /*flags*/0); -+ au_set_ibend(a->inode, a->mvd_bsrc); -+ } -+ if (au_dbend(a->dentry) < a->mvd_bdst) -+ au_set_dbend(a->dentry, a->mvd_bdst); -+ if (au_ibend(a->inode) < a->mvd_bdst) -+ au_set_ibend(a->inode, a->mvd_bdst); -+ -+out_unlock: -+ au_do_unlock(dmsg, a); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* make sure the file is idle */ -+static int au_mvd_args_busy(const unsigned char dmsg, struct au_mvd_args *a) -+{ -+ int err, plinked; -+ -+ err = 0; -+ plinked = !!au_opt_test(au_mntflags(a->sb), PLINK); -+ if (au_dbstart(a->dentry) == a->mvd_bsrc -+ && au_dcount(a->dentry) == 1 -+ && atomic_read(&a->inode->i_count) == 1 -+ /* && a->mvd_h_src_inode->i_nlink == 1 */ -+ && (!plinked || !au_plink_test(a->inode)) -+ && a->inode->i_nlink == 1) -+ goto out; -+ -+ err = -EBUSY; -+ AU_MVD_PR(dmsg, -+ "b%d, d{b%d, c%d?}, i{c%d?, l%u}, hi{l%u}, p{%d, %d}\n", -+ a->mvd_bsrc, au_dbstart(a->dentry), au_dcount(a->dentry), -+ atomic_read(&a->inode->i_count), a->inode->i_nlink, -+ a->mvd_h_src_inode->i_nlink, -+ plinked, plinked ? au_plink_test(a->inode) : 0); -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* make sure the parent dir is fine */ -+static int au_mvd_args_parent(const unsigned char dmsg, -+ struct au_mvd_args *a) -+{ -+ int err; -+ aufs_bindex_t bindex; -+ -+ err = 0; -+ if (unlikely(au_alive_dir(a->parent))) { -+ err = -ENOENT; -+ AU_MVD_PR(dmsg, "parent dir is dead\n"); -+ goto out; -+ } -+ -+ a->bopq = au_dbdiropq(a->parent); -+ bindex = au_wbr_nonopq(a->dentry, a->mvd_bdst); -+ AuDbg("b%d\n", bindex); -+ if (unlikely((bindex >= 0 && bindex < a->mvd_bdst) -+ || (a->bopq != -1 && a->bopq < a->mvd_bdst))) { -+ err = -EINVAL; -+ a->mvd_errno = EAU_MVDOWN_OPAQUE; -+ AU_MVD_PR(dmsg, "ancestor is opaque b%d, b%d\n", -+ a->bopq, a->mvd_bdst); -+ } -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+static int au_mvd_args_intermediate(const unsigned char dmsg, -+ struct au_mvd_args *a) -+{ -+ int err; -+ struct au_dinfo *dinfo, *tmp; -+ -+ /* lookup the next lower positive entry */ -+ err = -ENOMEM; -+ tmp = au_di_alloc(a->sb, AuLsc_DI_TMP); -+ if (unlikely(!tmp)) -+ goto out; -+ -+ a->bfound = -1; -+ a->bwh = -1; -+ dinfo = au_di(a->dentry); -+ au_di_cp(tmp, dinfo); -+ au_di_swap(tmp, dinfo); -+ -+ /* returns the number of positive dentries */ -+ err = au_lkup_dentry(a->dentry, a->mvd_bsrc + 1, /*type*/0); -+ if (!err) -+ a->bwh = au_dbwh(a->dentry); -+ else if (err > 0) -+ a->bfound = au_dbstart(a->dentry); -+ -+ au_di_swap(tmp, dinfo); -+ au_rw_write_unlock(&tmp->di_rwsem); -+ au_di_free(tmp); -+ if (unlikely(err < 0)) -+ AU_MVD_PR(dmsg, "failed look-up lower\n"); -+ -+ /* -+ * here, we have these cases. -+ * bfound == -1 -+ * no positive dentry under bsrc. there are more sub-cases. -+ * bwh < 0 -+ * there no whiteout, we can safely move-down. -+ * bwh <= bsrc -+ * impossible -+ * bsrc < bwh && bwh < bdst -+ * there is a whiteout on RO branch. cannot proceed. -+ * bwh == bdst -+ * there is a whiteout on the RW target branch. it should -+ * be removed. -+ * bdst < bwh -+ * there is a whiteout somewhere unrelated branch. -+ * -1 < bfound && bfound <= bsrc -+ * impossible. -+ * bfound < bdst -+ * found, but it is on RO branch between bsrc and bdst. cannot -+ * proceed. -+ * bfound == bdst -+ * found, replace it if AUFS_MVDOWN_FORCE is set. otherwise return -+ * error. -+ * bdst < bfound -+ * found, after we create the file on bdst, it will be hidden. -+ */ -+ -+ AuDebugOn(a->bfound == -1 -+ && a->bwh != -1 -+ && a->bwh <= a->mvd_bsrc); -+ AuDebugOn(-1 < a->bfound -+ && a->bfound <= a->mvd_bsrc); -+ -+ err = -EINVAL; -+ if (a->bfound == -1 -+ && a->mvd_bsrc < a->bwh -+ && a->bwh != -1 -+ && a->bwh < a->mvd_bdst) { -+ a->mvd_errno = EAU_MVDOWN_WHITEOUT; -+ AU_MVD_PR(dmsg, "bsrc %d, bdst %d, bfound %d, bwh %d\n", -+ a->mvd_bsrc, a->mvd_bdst, a->bfound, a->bwh); -+ goto out; -+ } else if (a->bfound != -1 && a->bfound < a->mvd_bdst) { -+ a->mvd_errno = EAU_MVDOWN_UPPER; -+ AU_MVD_PR(dmsg, "bdst %d, bfound %d\n", -+ a->mvd_bdst, a->bfound); -+ goto out; -+ } -+ -+ err = 0; /* success */ -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+static int au_mvd_args_exist(const unsigned char dmsg, struct au_mvd_args *a) -+{ -+ int err; -+ -+ err = 0; -+ if (!(a->mvdown.flags & AUFS_MVDOWN_OWLOWER) -+ && a->bfound == a->mvd_bdst) -+ err = -EEXIST; -+ AuTraceErr(err); -+ return err; -+} -+ -+static int au_mvd_args(const unsigned char dmsg, struct au_mvd_args *a) -+{ -+ int err; -+ struct au_branch *br; -+ -+ err = -EISDIR; -+ if (unlikely(S_ISDIR(a->inode->i_mode))) -+ goto out; -+ -+ err = -EINVAL; -+ if (!(a->mvdown.flags & AUFS_MVDOWN_BRID_UPPER)) -+ a->mvd_bsrc = au_ibstart(a->inode); -+ else { -+ a->mvd_bsrc = au_br_index(a->sb, a->mvd_src_brid); -+ if (unlikely(a->mvd_bsrc < 0 -+ || (a->mvd_bsrc < au_dbstart(a->dentry) -+ || au_dbend(a->dentry) < a->mvd_bsrc -+ || !au_h_dptr(a->dentry, a->mvd_bsrc)) -+ || (a->mvd_bsrc < au_ibstart(a->inode) -+ || au_ibend(a->inode) < a->mvd_bsrc -+ || !au_h_iptr(a->inode, a->mvd_bsrc)))) { -+ a->mvd_errno = EAU_MVDOWN_NOUPPER; -+ AU_MVD_PR(dmsg, "no upper\n"); -+ goto out; -+ } -+ } -+ if (unlikely(a->mvd_bsrc == au_sbend(a->sb))) { -+ a->mvd_errno = EAU_MVDOWN_BOTTOM; -+ AU_MVD_PR(dmsg, "on the bottom\n"); -+ goto out; -+ } -+ a->mvd_h_src_inode = au_h_iptr(a->inode, a->mvd_bsrc); -+ br = au_sbr(a->sb, a->mvd_bsrc); -+ err = au_br_rdonly(br); -+ if (!(a->mvdown.flags & AUFS_MVDOWN_ROUPPER)) { -+ if (unlikely(err)) -+ goto out; -+ } else if (!(vfsub_native_ro(a->mvd_h_src_inode) -+ || IS_APPEND(a->mvd_h_src_inode))) { -+ if (err) -+ a->mvdown.flags |= AUFS_MVDOWN_ROUPPER_R; -+ /* go on */ -+ } else -+ goto out; -+ -+ err = -EINVAL; -+ if (!(a->mvdown.flags & AUFS_MVDOWN_BRID_LOWER)) { -+ a->mvd_bdst = find_lower_writable(a); -+ if (unlikely(a->mvd_bdst < 0)) { -+ a->mvd_errno = EAU_MVDOWN_BOTTOM; -+ AU_MVD_PR(dmsg, "no writable lower branch\n"); -+ goto out; -+ } -+ } else { -+ a->mvd_bdst = au_br_index(a->sb, a->mvd_dst_brid); -+ if (unlikely(a->mvd_bdst < 0 -+ || au_sbend(a->sb) < a->mvd_bdst)) { -+ a->mvd_errno = EAU_MVDOWN_NOLOWERBR; -+ AU_MVD_PR(dmsg, "no lower brid\n"); -+ goto out; -+ } -+ } -+ -+ err = au_mvd_args_busy(dmsg, a); -+ if (!err) -+ err = au_mvd_args_parent(dmsg, a); -+ if (!err) -+ err = au_mvd_args_intermediate(dmsg, a); -+ if (!err) -+ err = au_mvd_args_exist(dmsg, a); -+ if (!err) -+ AuDbg("b%d, b%d\n", a->mvd_bsrc, a->mvd_bdst); -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+int au_mvdown(struct dentry *dentry, struct aufs_mvdown __user *uarg) -+{ -+ int err, e; -+ unsigned char dmsg; -+ struct au_mvd_args *args; -+ struct inode *inode; -+ -+ inode = dentry->d_inode; -+ err = -EPERM; -+ if (unlikely(!capable(CAP_SYS_ADMIN))) -+ goto out; -+ -+ err = -ENOMEM; -+ args = kmalloc(sizeof(*args), GFP_NOFS); -+ if (unlikely(!args)) -+ goto out; -+ -+ err = copy_from_user(&args->mvdown, uarg, sizeof(args->mvdown)); -+ if (!err) -+ err = !access_ok(VERIFY_WRITE, uarg, sizeof(*uarg)); -+ if (unlikely(err)) { -+ err = -EFAULT; -+ AuTraceErr(err); -+ goto out_free; -+ } -+ AuDbg("flags 0x%x\n", args->mvdown.flags); -+ args->mvdown.flags &= ~(AUFS_MVDOWN_ROLOWER_R | AUFS_MVDOWN_ROUPPER_R); -+ args->mvdown.au_errno = 0; -+ args->dentry = dentry; -+ args->inode = inode; -+ args->sb = dentry->d_sb; -+ -+ err = -ENOENT; -+ dmsg = !!(args->mvdown.flags & AUFS_MVDOWN_DMSG); -+ args->parent = dget_parent(dentry); -+ args->dir = args->parent->d_inode; -+ mutex_lock_nested(&args->dir->i_mutex, I_MUTEX_PARENT); -+ dput(args->parent); -+ if (unlikely(args->parent != dentry->d_parent)) { -+ AU_MVD_PR(dmsg, "parent dir is moved\n"); -+ goto out_dir; -+ } -+ -+ mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD); -+ err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_NOPLMW); -+ if (unlikely(err)) -+ goto out_inode; -+ -+ di_write_lock_parent(args->parent); -+ err = au_mvd_args(dmsg, args); -+ if (unlikely(err)) -+ goto out_parent; -+ -+ err = au_do_mvdown(dmsg, args); -+ if (unlikely(err)) -+ goto out_parent; -+ -+ au_cpup_attr_timesizes(args->dir); -+ au_cpup_attr_timesizes(inode); -+ if (!(args->mvdown.flags & AUFS_MVDOWN_KUPPER)) -+ au_cpup_igen(inode, au_h_iptr(inode, args->mvd_bdst)); -+ /* au_digen_dec(dentry); */ -+ -+out_parent: -+ di_write_unlock(args->parent); -+ aufs_read_unlock(dentry, AuLock_DW); -+out_inode: -+ mutex_unlock(&inode->i_mutex); -+out_dir: -+ mutex_unlock(&args->dir->i_mutex); -+out_free: -+ e = copy_to_user(uarg, &args->mvdown, sizeof(args->mvdown)); -+ if (unlikely(e)) -+ err = -EFAULT; -+ kfree(args); -+out: -+ AuTraceErr(err); -+ return err; -+} -diff --git a/fs/aufs/opts.c b/fs/aufs/opts.c -new file mode 100644 -index 0000000..0363f67 ---- /dev/null -+++ b/fs/aufs/opts.c -@@ -0,0 +1,1878 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * mount options/flags -+ */ -+ -+#include -+#include /* a distribution requires */ -+#include -+#include "aufs.h" -+ -+/* ---------------------------------------------------------------------- */ -+ -+enum { -+ Opt_br, -+ Opt_add, Opt_del, Opt_mod, Opt_append, Opt_prepend, -+ Opt_idel, Opt_imod, -+ Opt_dirwh, Opt_rdcache, Opt_rdblk, Opt_rdhash, -+ Opt_rdblk_def, Opt_rdhash_def, -+ Opt_xino, Opt_noxino, -+ Opt_trunc_xino, Opt_trunc_xino_v, Opt_notrunc_xino, -+ Opt_trunc_xino_path, Opt_itrunc_xino, -+ Opt_trunc_xib, Opt_notrunc_xib, -+ Opt_shwh, Opt_noshwh, -+ Opt_plink, Opt_noplink, Opt_list_plink, -+ Opt_udba, -+ Opt_dio, Opt_nodio, -+ Opt_diropq_a, Opt_diropq_w, -+ Opt_warn_perm, Opt_nowarn_perm, -+ Opt_wbr_copyup, Opt_wbr_create, -+ Opt_fhsm_sec, -+ Opt_refrof, Opt_norefrof, -+ Opt_verbose, Opt_noverbose, -+ Opt_sum, Opt_nosum, Opt_wsum, -+ Opt_dirperm1, Opt_nodirperm1, -+ Opt_acl, Opt_noacl, -+ Opt_tail, Opt_ignore, Opt_ignore_silent, Opt_err -+}; -+ -+static match_table_t options = { -+ {Opt_br, "br=%s"}, -+ {Opt_br, "br:%s"}, -+ -+ {Opt_add, "add=%d:%s"}, -+ {Opt_add, "add:%d:%s"}, -+ {Opt_add, "ins=%d:%s"}, -+ {Opt_add, "ins:%d:%s"}, -+ {Opt_append, "append=%s"}, -+ {Opt_append, "append:%s"}, -+ {Opt_prepend, "prepend=%s"}, -+ {Opt_prepend, "prepend:%s"}, -+ -+ {Opt_del, "del=%s"}, -+ {Opt_del, "del:%s"}, -+ /* {Opt_idel, "idel:%d"}, */ -+ {Opt_mod, "mod=%s"}, -+ {Opt_mod, "mod:%s"}, -+ /* {Opt_imod, "imod:%d:%s"}, */ -+ -+ {Opt_dirwh, "dirwh=%d"}, -+ -+ {Opt_xino, "xino=%s"}, -+ {Opt_noxino, "noxino"}, -+ {Opt_trunc_xino, "trunc_xino"}, -+ {Opt_trunc_xino_v, "trunc_xino_v=%d:%d"}, -+ {Opt_notrunc_xino, "notrunc_xino"}, -+ {Opt_trunc_xino_path, "trunc_xino=%s"}, -+ {Opt_itrunc_xino, "itrunc_xino=%d"}, -+ /* {Opt_zxino, "zxino=%s"}, */ -+ {Opt_trunc_xib, "trunc_xib"}, -+ {Opt_notrunc_xib, "notrunc_xib"}, -+ -+#ifdef CONFIG_PROC_FS -+ {Opt_plink, "plink"}, -+#else -+ {Opt_ignore_silent, "plink"}, -+#endif -+ -+ {Opt_noplink, "noplink"}, -+ -+#ifdef CONFIG_AUFS_DEBUG -+ {Opt_list_plink, "list_plink"}, -+#endif -+ -+ {Opt_udba, "udba=%s"}, -+ -+ {Opt_dio, "dio"}, -+ {Opt_nodio, "nodio"}, -+ -+#ifdef CONFIG_AUFS_FHSM -+ {Opt_fhsm_sec, "fhsm_sec=%d"}, -+#else -+ {Opt_ignore_silent, "fhsm_sec=%d"}, -+#endif -+ -+ {Opt_diropq_a, "diropq=always"}, -+ {Opt_diropq_a, "diropq=a"}, -+ {Opt_diropq_w, "diropq=whiteouted"}, -+ {Opt_diropq_w, "diropq=w"}, -+ -+ {Opt_warn_perm, "warn_perm"}, -+ {Opt_nowarn_perm, "nowarn_perm"}, -+ -+ /* keep them temporary */ -+ {Opt_ignore_silent, "nodlgt"}, -+ {Opt_ignore_silent, "clean_plink"}, -+ -+#ifdef CONFIG_AUFS_SHWH -+ {Opt_shwh, "shwh"}, -+#endif -+ {Opt_noshwh, "noshwh"}, -+ -+ {Opt_dirperm1, "dirperm1"}, -+ {Opt_nodirperm1, "nodirperm1"}, -+ -+ {Opt_refrof, "refrof"}, -+ {Opt_norefrof, "norefrof"}, -+ -+ {Opt_verbose, "verbose"}, -+ {Opt_verbose, "v"}, -+ {Opt_noverbose, "noverbose"}, -+ {Opt_noverbose, "quiet"}, -+ {Opt_noverbose, "q"}, -+ {Opt_noverbose, "silent"}, -+ -+ {Opt_sum, "sum"}, -+ {Opt_nosum, "nosum"}, -+ {Opt_wsum, "wsum"}, -+ -+ {Opt_rdcache, "rdcache=%d"}, -+ {Opt_rdblk, "rdblk=%d"}, -+ {Opt_rdblk_def, "rdblk=def"}, -+ {Opt_rdhash, "rdhash=%d"}, -+ {Opt_rdhash_def, "rdhash=def"}, -+ -+ {Opt_wbr_create, "create=%s"}, -+ {Opt_wbr_create, "create_policy=%s"}, -+ {Opt_wbr_copyup, "cpup=%s"}, -+ {Opt_wbr_copyup, "copyup=%s"}, -+ {Opt_wbr_copyup, "copyup_policy=%s"}, -+ -+ /* generic VFS flag */ -+#ifdef CONFIG_FS_POSIX_ACL -+ {Opt_acl, "acl"}, -+ {Opt_noacl, "noacl"}, -+#else -+ {Opt_ignore_silent, "acl"}, -+ {Opt_ignore_silent, "noacl"}, -+#endif -+ -+ /* internal use for the scripts */ -+ {Opt_ignore_silent, "si=%s"}, -+ -+ {Opt_br, "dirs=%s"}, -+ {Opt_ignore, "debug=%d"}, -+ {Opt_ignore, "delete=whiteout"}, -+ {Opt_ignore, "delete=all"}, -+ {Opt_ignore, "imap=%s"}, -+ -+ /* temporary workaround, due to old mount(8)? */ -+ {Opt_ignore_silent, "relatime"}, -+ -+ {Opt_err, NULL} -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+static const char *au_parser_pattern(int val, match_table_t tbl) -+{ -+ struct match_token *p; -+ -+ p = tbl; -+ while (p->pattern) { -+ if (p->token == val) -+ return p->pattern; -+ p++; -+ } -+ BUG(); -+ return "??"; -+} -+ -+static const char *au_optstr(int *val, match_table_t tbl) -+{ -+ struct match_token *p; -+ int v; -+ -+ v = *val; -+ if (!v) -+ goto out; -+ p = tbl; -+ while (p->pattern) { -+ if (p->token -+ && (v & p->token) == p->token) { -+ *val &= ~p->token; -+ return p->pattern; -+ } -+ p++; -+ } -+ -+out: -+ return NULL; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static match_table_t brperm = { -+ {AuBrPerm_RO, AUFS_BRPERM_RO}, -+ {AuBrPerm_RR, AUFS_BRPERM_RR}, -+ {AuBrPerm_RW, AUFS_BRPERM_RW}, -+ {0, NULL} -+}; -+ -+static match_table_t brattr = { -+ /* general */ -+ {AuBrAttr_COO_REG, AUFS_BRATTR_COO_REG}, -+ {AuBrAttr_COO_ALL, AUFS_BRATTR_COO_ALL}, -+ /* 'unpin' attrib is meaningless since linux-3.18-rc1 */ -+ {AuBrAttr_UNPIN, AUFS_BRATTR_UNPIN}, -+#ifdef CONFIG_AUFS_FHSM -+ {AuBrAttr_FHSM, AUFS_BRATTR_FHSM}, -+#endif -+#ifdef CONFIG_AUFS_XATTR -+ {AuBrAttr_ICEX, AUFS_BRATTR_ICEX}, -+ {AuBrAttr_ICEX_SEC, AUFS_BRATTR_ICEX_SEC}, -+ {AuBrAttr_ICEX_SYS, AUFS_BRATTR_ICEX_SYS}, -+ {AuBrAttr_ICEX_TR, AUFS_BRATTR_ICEX_TR}, -+ {AuBrAttr_ICEX_USR, AUFS_BRATTR_ICEX_USR}, -+ {AuBrAttr_ICEX_OTH, AUFS_BRATTR_ICEX_OTH}, -+#endif -+ -+ /* ro/rr branch */ -+ {AuBrRAttr_WH, AUFS_BRRATTR_WH}, -+ -+ /* rw branch */ -+ {AuBrWAttr_MOO, AUFS_BRWATTR_MOO}, -+ {AuBrWAttr_NoLinkWH, AUFS_BRWATTR_NLWH}, -+ -+ {0, NULL} -+}; -+ -+static int br_attr_val(char *str, match_table_t table, substring_t args[]) -+{ -+ int attr, v; -+ char *p; -+ -+ attr = 0; -+ do { -+ p = strchr(str, '+'); -+ if (p) -+ *p = 0; -+ v = match_token(str, table, args); -+ if (v) { -+ if (v & AuBrAttr_CMOO_Mask) -+ attr &= ~AuBrAttr_CMOO_Mask; -+ attr |= v; -+ } else { -+ if (p) -+ *p = '+'; -+ pr_warn("ignored branch attribute %s\n", str); -+ break; -+ } -+ if (p) -+ str = p + 1; -+ } while (p); -+ -+ return attr; -+} -+ -+static int au_do_optstr_br_attr(au_br_perm_str_t *str, int perm) -+{ -+ int sz; -+ const char *p; -+ char *q; -+ -+ q = str->a; -+ *q = 0; -+ p = au_optstr(&perm, brattr); -+ if (p) { -+ sz = strlen(p); -+ memcpy(q, p, sz + 1); -+ q += sz; -+ } else -+ goto out; -+ -+ do { -+ p = au_optstr(&perm, brattr); -+ if (p) { -+ *q++ = '+'; -+ sz = strlen(p); -+ memcpy(q, p, sz + 1); -+ q += sz; -+ } -+ } while (p); -+ -+out: -+ return q - str->a; -+} -+ -+static int noinline_for_stack br_perm_val(char *perm) -+{ -+ int val, bad, sz; -+ char *p; -+ substring_t args[MAX_OPT_ARGS]; -+ au_br_perm_str_t attr; -+ -+ p = strchr(perm, '+'); -+ if (p) -+ *p = 0; -+ val = match_token(perm, brperm, args); -+ if (!val) { -+ if (p) -+ *p = '+'; -+ pr_warn("ignored branch permission %s\n", perm); -+ val = AuBrPerm_RO; -+ goto out; -+ } -+ if (!p) -+ goto out; -+ -+ val |= br_attr_val(p + 1, brattr, args); -+ -+ bad = 0; -+ switch (val & AuBrPerm_Mask) { -+ case AuBrPerm_RO: -+ case AuBrPerm_RR: -+ bad = val & AuBrWAttr_Mask; -+ val &= ~AuBrWAttr_Mask; -+ break; -+ case AuBrPerm_RW: -+ bad = val & AuBrRAttr_Mask; -+ val &= ~AuBrRAttr_Mask; -+ break; -+ } -+ -+ /* -+ * 'unpin' attrib becomes meaningless since linux-3.18-rc1, but aufs -+ * does not treat it as an error, just warning. -+ * this is a tiny guard for the user operation. -+ */ -+ if (val & AuBrAttr_UNPIN) { -+ bad |= AuBrAttr_UNPIN; -+ val &= ~AuBrAttr_UNPIN; -+ } -+ -+ if (unlikely(bad)) { -+ sz = au_do_optstr_br_attr(&attr, bad); -+ AuDebugOn(!sz); -+ pr_warn("ignored branch attribute %s\n", attr.a); -+ } -+ -+out: -+ return val; -+} -+ -+void au_optstr_br_perm(au_br_perm_str_t *str, int perm) -+{ -+ au_br_perm_str_t attr; -+ const char *p; -+ char *q; -+ int sz; -+ -+ q = str->a; -+ p = au_optstr(&perm, brperm); -+ AuDebugOn(!p || !*p); -+ sz = strlen(p); -+ memcpy(q, p, sz + 1); -+ q += sz; -+ -+ sz = au_do_optstr_br_attr(&attr, perm); -+ if (sz) { -+ *q++ = '+'; -+ memcpy(q, attr.a, sz + 1); -+ } -+ -+ AuDebugOn(strlen(str->a) >= sizeof(str->a)); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static match_table_t udbalevel = { -+ {AuOpt_UDBA_REVAL, "reval"}, -+ {AuOpt_UDBA_NONE, "none"}, -+#ifdef CONFIG_AUFS_HNOTIFY -+ {AuOpt_UDBA_HNOTIFY, "notify"}, /* abstraction */ -+#ifdef CONFIG_AUFS_HFSNOTIFY -+ {AuOpt_UDBA_HNOTIFY, "fsnotify"}, -+#endif -+#endif -+ {-1, NULL} -+}; -+ -+static int noinline_for_stack udba_val(char *str) -+{ -+ substring_t args[MAX_OPT_ARGS]; -+ -+ return match_token(str, udbalevel, args); -+} -+ -+const char *au_optstr_udba(int udba) -+{ -+ return au_parser_pattern(udba, udbalevel); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static match_table_t au_wbr_create_policy = { -+ {AuWbrCreate_TDP, "tdp"}, -+ {AuWbrCreate_TDP, "top-down-parent"}, -+ {AuWbrCreate_RR, "rr"}, -+ {AuWbrCreate_RR, "round-robin"}, -+ {AuWbrCreate_MFS, "mfs"}, -+ {AuWbrCreate_MFS, "most-free-space"}, -+ {AuWbrCreate_MFSV, "mfs:%d"}, -+ {AuWbrCreate_MFSV, "most-free-space:%d"}, -+ -+ {AuWbrCreate_MFSRR, "mfsrr:%d"}, -+ {AuWbrCreate_MFSRRV, "mfsrr:%d:%d"}, -+ {AuWbrCreate_PMFS, "pmfs"}, -+ {AuWbrCreate_PMFSV, "pmfs:%d"}, -+ {AuWbrCreate_PMFSRR, "pmfsrr:%d"}, -+ {AuWbrCreate_PMFSRRV, "pmfsrr:%d:%d"}, -+ -+ {-1, NULL} -+}; -+ -+/* -+ * cf. linux/lib/parser.c and cmdline.c -+ * gave up calling memparse() since it uses simple_strtoull() instead of -+ * kstrto...(). -+ */ -+static int noinline_for_stack -+au_match_ull(substring_t *s, unsigned long long *result) -+{ -+ int err; -+ unsigned int len; -+ char a[32]; -+ -+ err = -ERANGE; -+ len = s->to - s->from; -+ if (len + 1 <= sizeof(a)) { -+ memcpy(a, s->from, len); -+ a[len] = '\0'; -+ err = kstrtoull(a, 0, result); -+ } -+ return err; -+} -+ -+static int au_wbr_mfs_wmark(substring_t *arg, char *str, -+ struct au_opt_wbr_create *create) -+{ -+ int err; -+ unsigned long long ull; -+ -+ err = 0; -+ if (!au_match_ull(arg, &ull)) -+ create->mfsrr_watermark = ull; -+ else { -+ pr_err("bad integer in %s\n", str); -+ err = -EINVAL; -+ } -+ -+ return err; -+} -+ -+static int au_wbr_mfs_sec(substring_t *arg, char *str, -+ struct au_opt_wbr_create *create) -+{ -+ int n, err; -+ -+ err = 0; -+ if (!match_int(arg, &n) && 0 <= n && n <= AUFS_MFS_MAX_SEC) -+ create->mfs_second = n; -+ else { -+ pr_err("bad integer in %s\n", str); -+ err = -EINVAL; -+ } -+ -+ return err; -+} -+ -+static int noinline_for_stack -+au_wbr_create_val(char *str, struct au_opt_wbr_create *create) -+{ -+ int err, e; -+ substring_t args[MAX_OPT_ARGS]; -+ -+ err = match_token(str, au_wbr_create_policy, args); -+ create->wbr_create = err; -+ switch (err) { -+ case AuWbrCreate_MFSRRV: -+ case AuWbrCreate_PMFSRRV: -+ e = au_wbr_mfs_wmark(&args[0], str, create); -+ if (!e) -+ e = au_wbr_mfs_sec(&args[1], str, create); -+ if (unlikely(e)) -+ err = e; -+ break; -+ case AuWbrCreate_MFSRR: -+ case AuWbrCreate_PMFSRR: -+ e = au_wbr_mfs_wmark(&args[0], str, create); -+ if (unlikely(e)) { -+ err = e; -+ break; -+ } -+ /*FALLTHROUGH*/ -+ case AuWbrCreate_MFS: -+ case AuWbrCreate_PMFS: -+ create->mfs_second = AUFS_MFS_DEF_SEC; -+ break; -+ case AuWbrCreate_MFSV: -+ case AuWbrCreate_PMFSV: -+ e = au_wbr_mfs_sec(&args[0], str, create); -+ if (unlikely(e)) -+ err = e; -+ break; -+ } -+ -+ return err; -+} -+ -+const char *au_optstr_wbr_create(int wbr_create) -+{ -+ return au_parser_pattern(wbr_create, au_wbr_create_policy); -+} -+ -+static match_table_t au_wbr_copyup_policy = { -+ {AuWbrCopyup_TDP, "tdp"}, -+ {AuWbrCopyup_TDP, "top-down-parent"}, -+ {AuWbrCopyup_BUP, "bup"}, -+ {AuWbrCopyup_BUP, "bottom-up-parent"}, -+ {AuWbrCopyup_BU, "bu"}, -+ {AuWbrCopyup_BU, "bottom-up"}, -+ {-1, NULL} -+}; -+ -+static int noinline_for_stack au_wbr_copyup_val(char *str) -+{ -+ substring_t args[MAX_OPT_ARGS]; -+ -+ return match_token(str, au_wbr_copyup_policy, args); -+} -+ -+const char *au_optstr_wbr_copyup(int wbr_copyup) -+{ -+ return au_parser_pattern(wbr_copyup, au_wbr_copyup_policy); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static const int lkup_dirflags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY; -+ -+static void dump_opts(struct au_opts *opts) -+{ -+#ifdef CONFIG_AUFS_DEBUG -+ /* reduce stack space */ -+ union { -+ struct au_opt_add *add; -+ struct au_opt_del *del; -+ struct au_opt_mod *mod; -+ struct au_opt_xino *xino; -+ struct au_opt_xino_itrunc *xino_itrunc; -+ struct au_opt_wbr_create *create; -+ } u; -+ struct au_opt *opt; -+ -+ opt = opts->opt; -+ while (opt->type != Opt_tail) { -+ switch (opt->type) { -+ case Opt_add: -+ u.add = &opt->add; -+ AuDbg("add {b%d, %s, 0x%x, %p}\n", -+ u.add->bindex, u.add->pathname, u.add->perm, -+ u.add->path.dentry); -+ break; -+ case Opt_del: -+ case Opt_idel: -+ u.del = &opt->del; -+ AuDbg("del {%s, %p}\n", -+ u.del->pathname, u.del->h_path.dentry); -+ break; -+ case Opt_mod: -+ case Opt_imod: -+ u.mod = &opt->mod; -+ AuDbg("mod {%s, 0x%x, %p}\n", -+ u.mod->path, u.mod->perm, u.mod->h_root); -+ break; -+ case Opt_append: -+ u.add = &opt->add; -+ AuDbg("append {b%d, %s, 0x%x, %p}\n", -+ u.add->bindex, u.add->pathname, u.add->perm, -+ u.add->path.dentry); -+ break; -+ case Opt_prepend: -+ u.add = &opt->add; -+ AuDbg("prepend {b%d, %s, 0x%x, %p}\n", -+ u.add->bindex, u.add->pathname, u.add->perm, -+ u.add->path.dentry); -+ break; -+ case Opt_dirwh: -+ AuDbg("dirwh %d\n", opt->dirwh); -+ break; -+ case Opt_rdcache: -+ AuDbg("rdcache %d\n", opt->rdcache); -+ break; -+ case Opt_rdblk: -+ AuDbg("rdblk %u\n", opt->rdblk); -+ break; -+ case Opt_rdblk_def: -+ AuDbg("rdblk_def\n"); -+ break; -+ case Opt_rdhash: -+ AuDbg("rdhash %u\n", opt->rdhash); -+ break; -+ case Opt_rdhash_def: -+ AuDbg("rdhash_def\n"); -+ break; -+ case Opt_xino: -+ u.xino = &opt->xino; -+ AuDbg("xino {%s %pD}\n", u.xino->path, u.xino->file); -+ break; -+ case Opt_trunc_xino: -+ AuLabel(trunc_xino); -+ break; -+ case Opt_notrunc_xino: -+ AuLabel(notrunc_xino); -+ break; -+ case Opt_trunc_xino_path: -+ case Opt_itrunc_xino: -+ u.xino_itrunc = &opt->xino_itrunc; -+ AuDbg("trunc_xino %d\n", u.xino_itrunc->bindex); -+ break; -+ case Opt_noxino: -+ AuLabel(noxino); -+ break; -+ case Opt_trunc_xib: -+ AuLabel(trunc_xib); -+ break; -+ case Opt_notrunc_xib: -+ AuLabel(notrunc_xib); -+ break; -+ case Opt_shwh: -+ AuLabel(shwh); -+ break; -+ case Opt_noshwh: -+ AuLabel(noshwh); -+ break; -+ case Opt_dirperm1: -+ AuLabel(dirperm1); -+ break; -+ case Opt_nodirperm1: -+ AuLabel(nodirperm1); -+ break; -+ case Opt_plink: -+ AuLabel(plink); -+ break; -+ case Opt_noplink: -+ AuLabel(noplink); -+ break; -+ case Opt_list_plink: -+ AuLabel(list_plink); -+ break; -+ case Opt_udba: -+ AuDbg("udba %d, %s\n", -+ opt->udba, au_optstr_udba(opt->udba)); -+ break; -+ case Opt_dio: -+ AuLabel(dio); -+ break; -+ case Opt_nodio: -+ AuLabel(nodio); -+ break; -+ case Opt_diropq_a: -+ AuLabel(diropq_a); -+ break; -+ case Opt_diropq_w: -+ AuLabel(diropq_w); -+ break; -+ case Opt_warn_perm: -+ AuLabel(warn_perm); -+ break; -+ case Opt_nowarn_perm: -+ AuLabel(nowarn_perm); -+ break; -+ case Opt_refrof: -+ AuLabel(refrof); -+ break; -+ case Opt_norefrof: -+ AuLabel(norefrof); -+ break; -+ case Opt_verbose: -+ AuLabel(verbose); -+ break; -+ case Opt_noverbose: -+ AuLabel(noverbose); -+ break; -+ case Opt_sum: -+ AuLabel(sum); -+ break; -+ case Opt_nosum: -+ AuLabel(nosum); -+ break; -+ case Opt_wsum: -+ AuLabel(wsum); -+ break; -+ case Opt_wbr_create: -+ u.create = &opt->wbr_create; -+ AuDbg("create %d, %s\n", u.create->wbr_create, -+ au_optstr_wbr_create(u.create->wbr_create)); -+ switch (u.create->wbr_create) { -+ case AuWbrCreate_MFSV: -+ case AuWbrCreate_PMFSV: -+ AuDbg("%d sec\n", u.create->mfs_second); -+ break; -+ case AuWbrCreate_MFSRR: -+ AuDbg("%llu watermark\n", -+ u.create->mfsrr_watermark); -+ break; -+ case AuWbrCreate_MFSRRV: -+ case AuWbrCreate_PMFSRRV: -+ AuDbg("%llu watermark, %d sec\n", -+ u.create->mfsrr_watermark, -+ u.create->mfs_second); -+ break; -+ } -+ break; -+ case Opt_wbr_copyup: -+ AuDbg("copyup %d, %s\n", opt->wbr_copyup, -+ au_optstr_wbr_copyup(opt->wbr_copyup)); -+ break; -+ case Opt_fhsm_sec: -+ AuDbg("fhsm_sec %u\n", opt->fhsm_second); -+ break; -+ case Opt_acl: -+ AuLabel(acl); -+ break; -+ case Opt_noacl: -+ AuLabel(noacl); -+ break; -+ default: -+ BUG(); -+ } -+ opt++; -+ } -+#endif -+} -+ -+void au_opts_free(struct au_opts *opts) -+{ -+ struct au_opt *opt; -+ -+ opt = opts->opt; -+ while (opt->type != Opt_tail) { -+ switch (opt->type) { -+ case Opt_add: -+ case Opt_append: -+ case Opt_prepend: -+ path_put(&opt->add.path); -+ break; -+ case Opt_del: -+ case Opt_idel: -+ path_put(&opt->del.h_path); -+ break; -+ case Opt_mod: -+ case Opt_imod: -+ dput(opt->mod.h_root); -+ break; -+ case Opt_xino: -+ fput(opt->xino.file); -+ break; -+ } -+ opt++; -+ } -+} -+ -+static int opt_add(struct au_opt *opt, char *opt_str, unsigned long sb_flags, -+ aufs_bindex_t bindex) -+{ -+ int err; -+ struct au_opt_add *add = &opt->add; -+ char *p; -+ -+ add->bindex = bindex; -+ add->perm = AuBrPerm_RO; -+ add->pathname = opt_str; -+ p = strchr(opt_str, '='); -+ if (p) { -+ *p++ = 0; -+ if (*p) -+ add->perm = br_perm_val(p); -+ } -+ -+ err = vfsub_kern_path(add->pathname, lkup_dirflags, &add->path); -+ if (!err) { -+ if (!p) { -+ add->perm = AuBrPerm_RO; -+ if (au_test_fs_rr(add->path.dentry->d_sb)) -+ add->perm = AuBrPerm_RR; -+ else if (!bindex && !(sb_flags & MS_RDONLY)) -+ add->perm = AuBrPerm_RW; -+ } -+ opt->type = Opt_add; -+ goto out; -+ } -+ pr_err("lookup failed %s (%d)\n", add->pathname, err); -+ err = -EINVAL; -+ -+out: -+ return err; -+} -+ -+static int au_opts_parse_del(struct au_opt_del *del, substring_t args[]) -+{ -+ int err; -+ -+ del->pathname = args[0].from; -+ AuDbg("del path %s\n", del->pathname); -+ -+ err = vfsub_kern_path(del->pathname, lkup_dirflags, &del->h_path); -+ if (unlikely(err)) -+ pr_err("lookup failed %s (%d)\n", del->pathname, err); -+ -+ return err; -+} -+ -+#if 0 /* reserved for future use */ -+static int au_opts_parse_idel(struct super_block *sb, aufs_bindex_t bindex, -+ struct au_opt_del *del, substring_t args[]) -+{ -+ int err; -+ struct dentry *root; -+ -+ err = -EINVAL; -+ root = sb->s_root; -+ aufs_read_lock(root, AuLock_FLUSH); -+ if (bindex < 0 || au_sbend(sb) < bindex) { -+ pr_err("out of bounds, %d\n", bindex); -+ goto out; -+ } -+ -+ err = 0; -+ del->h_path.dentry = dget(au_h_dptr(root, bindex)); -+ del->h_path.mnt = mntget(au_sbr_mnt(sb, bindex)); -+ -+out: -+ aufs_read_unlock(root, !AuLock_IR); -+ return err; -+} -+#endif -+ -+static int noinline_for_stack -+au_opts_parse_mod(struct au_opt_mod *mod, substring_t args[]) -+{ -+ int err; -+ struct path path; -+ char *p; -+ -+ err = -EINVAL; -+ mod->path = args[0].from; -+ p = strchr(mod->path, '='); -+ if (unlikely(!p)) { -+ pr_err("no permssion %s\n", args[0].from); -+ goto out; -+ } -+ -+ *p++ = 0; -+ err = vfsub_kern_path(mod->path, lkup_dirflags, &path); -+ if (unlikely(err)) { -+ pr_err("lookup failed %s (%d)\n", mod->path, err); -+ goto out; -+ } -+ -+ mod->perm = br_perm_val(p); -+ AuDbg("mod path %s, perm 0x%x, %s\n", mod->path, mod->perm, p); -+ mod->h_root = dget(path.dentry); -+ path_put(&path); -+ -+out: -+ return err; -+} -+ -+#if 0 /* reserved for future use */ -+static int au_opts_parse_imod(struct super_block *sb, aufs_bindex_t bindex, -+ struct au_opt_mod *mod, substring_t args[]) -+{ -+ int err; -+ struct dentry *root; -+ -+ err = -EINVAL; -+ root = sb->s_root; -+ aufs_read_lock(root, AuLock_FLUSH); -+ if (bindex < 0 || au_sbend(sb) < bindex) { -+ pr_err("out of bounds, %d\n", bindex); -+ goto out; -+ } -+ -+ err = 0; -+ mod->perm = br_perm_val(args[1].from); -+ AuDbg("mod path %s, perm 0x%x, %s\n", -+ mod->path, mod->perm, args[1].from); -+ mod->h_root = dget(au_h_dptr(root, bindex)); -+ -+out: -+ aufs_read_unlock(root, !AuLock_IR); -+ return err; -+} -+#endif -+ -+static int au_opts_parse_xino(struct super_block *sb, struct au_opt_xino *xino, -+ substring_t args[]) -+{ -+ int err; -+ struct file *file; -+ -+ file = au_xino_create(sb, args[0].from, /*silent*/0); -+ err = PTR_ERR(file); -+ if (IS_ERR(file)) -+ goto out; -+ -+ err = -EINVAL; -+ if (unlikely(file->f_dentry->d_sb == sb)) { -+ fput(file); -+ pr_err("%s must be outside\n", args[0].from); -+ goto out; -+ } -+ -+ err = 0; -+ xino->file = file; -+ xino->path = args[0].from; -+ -+out: -+ return err; -+} -+ -+static int noinline_for_stack -+au_opts_parse_xino_itrunc_path(struct super_block *sb, -+ struct au_opt_xino_itrunc *xino_itrunc, -+ substring_t args[]) -+{ -+ int err; -+ aufs_bindex_t bend, bindex; -+ struct path path; -+ struct dentry *root; -+ -+ err = vfsub_kern_path(args[0].from, lkup_dirflags, &path); -+ if (unlikely(err)) { -+ pr_err("lookup failed %s (%d)\n", args[0].from, err); -+ goto out; -+ } -+ -+ xino_itrunc->bindex = -1; -+ root = sb->s_root; -+ aufs_read_lock(root, AuLock_FLUSH); -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { -+ if (au_h_dptr(root, bindex) == path.dentry) { -+ xino_itrunc->bindex = bindex; -+ break; -+ } -+ } -+ aufs_read_unlock(root, !AuLock_IR); -+ path_put(&path); -+ -+ if (unlikely(xino_itrunc->bindex < 0)) { -+ pr_err("no such branch %s\n", args[0].from); -+ err = -EINVAL; -+ } -+ -+out: -+ return err; -+} -+ -+/* called without aufs lock */ -+int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts) -+{ -+ int err, n, token; -+ aufs_bindex_t bindex; -+ unsigned char skipped; -+ struct dentry *root; -+ struct au_opt *opt, *opt_tail; -+ char *opt_str; -+ /* reduce the stack space */ -+ union { -+ struct au_opt_xino_itrunc *xino_itrunc; -+ struct au_opt_wbr_create *create; -+ } u; -+ struct { -+ substring_t args[MAX_OPT_ARGS]; -+ } *a; -+ -+ err = -ENOMEM; -+ a = kmalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ -+ root = sb->s_root; -+ err = 0; -+ bindex = 0; -+ opt = opts->opt; -+ opt_tail = opt + opts->max_opt - 1; -+ opt->type = Opt_tail; -+ while (!err && (opt_str = strsep(&str, ",")) && *opt_str) { -+ err = -EINVAL; -+ skipped = 0; -+ token = match_token(opt_str, options, a->args); -+ switch (token) { -+ case Opt_br: -+ err = 0; -+ while (!err && (opt_str = strsep(&a->args[0].from, ":")) -+ && *opt_str) { -+ err = opt_add(opt, opt_str, opts->sb_flags, -+ bindex++); -+ if (unlikely(!err && ++opt > opt_tail)) { -+ err = -E2BIG; -+ break; -+ } -+ opt->type = Opt_tail; -+ skipped = 1; -+ } -+ break; -+ case Opt_add: -+ if (unlikely(match_int(&a->args[0], &n))) { -+ pr_err("bad integer in %s\n", opt_str); -+ break; -+ } -+ bindex = n; -+ err = opt_add(opt, a->args[1].from, opts->sb_flags, -+ bindex); -+ if (!err) -+ opt->type = token; -+ break; -+ case Opt_append: -+ err = opt_add(opt, a->args[0].from, opts->sb_flags, -+ /*dummy bindex*/1); -+ if (!err) -+ opt->type = token; -+ break; -+ case Opt_prepend: -+ err = opt_add(opt, a->args[0].from, opts->sb_flags, -+ /*bindex*/0); -+ if (!err) -+ opt->type = token; -+ break; -+ case Opt_del: -+ err = au_opts_parse_del(&opt->del, a->args); -+ if (!err) -+ opt->type = token; -+ break; -+#if 0 /* reserved for future use */ -+ case Opt_idel: -+ del->pathname = "(indexed)"; -+ if (unlikely(match_int(&args[0], &n))) { -+ pr_err("bad integer in %s\n", opt_str); -+ break; -+ } -+ err = au_opts_parse_idel(sb, n, &opt->del, a->args); -+ if (!err) -+ opt->type = token; -+ break; -+#endif -+ case Opt_mod: -+ err = au_opts_parse_mod(&opt->mod, a->args); -+ if (!err) -+ opt->type = token; -+ break; -+#ifdef IMOD /* reserved for future use */ -+ case Opt_imod: -+ u.mod->path = "(indexed)"; -+ if (unlikely(match_int(&a->args[0], &n))) { -+ pr_err("bad integer in %s\n", opt_str); -+ break; -+ } -+ err = au_opts_parse_imod(sb, n, &opt->mod, a->args); -+ if (!err) -+ opt->type = token; -+ break; -+#endif -+ case Opt_xino: -+ err = au_opts_parse_xino(sb, &opt->xino, a->args); -+ if (!err) -+ opt->type = token; -+ break; -+ -+ case Opt_trunc_xino_path: -+ err = au_opts_parse_xino_itrunc_path -+ (sb, &opt->xino_itrunc, a->args); -+ if (!err) -+ opt->type = token; -+ break; -+ -+ case Opt_itrunc_xino: -+ u.xino_itrunc = &opt->xino_itrunc; -+ if (unlikely(match_int(&a->args[0], &n))) { -+ pr_err("bad integer in %s\n", opt_str); -+ break; -+ } -+ u.xino_itrunc->bindex = n; -+ aufs_read_lock(root, AuLock_FLUSH); -+ if (n < 0 || au_sbend(sb) < n) { -+ pr_err("out of bounds, %d\n", n); -+ aufs_read_unlock(root, !AuLock_IR); -+ break; -+ } -+ aufs_read_unlock(root, !AuLock_IR); -+ err = 0; -+ opt->type = token; -+ break; -+ -+ case Opt_dirwh: -+ if (unlikely(match_int(&a->args[0], &opt->dirwh))) -+ break; -+ err = 0; -+ opt->type = token; -+ break; -+ -+ case Opt_rdcache: -+ if (unlikely(match_int(&a->args[0], &n))) { -+ pr_err("bad integer in %s\n", opt_str); -+ break; -+ } -+ if (unlikely(n > AUFS_RDCACHE_MAX)) { -+ pr_err("rdcache must be smaller than %d\n", -+ AUFS_RDCACHE_MAX); -+ break; -+ } -+ opt->rdcache = n; -+ err = 0; -+ opt->type = token; -+ break; -+ case Opt_rdblk: -+ if (unlikely(match_int(&a->args[0], &n) -+ || n < 0 -+ || n > KMALLOC_MAX_SIZE)) { -+ pr_err("bad integer in %s\n", opt_str); -+ break; -+ } -+ if (unlikely(n && n < NAME_MAX)) { -+ pr_err("rdblk must be larger than %d\n", -+ NAME_MAX); -+ break; -+ } -+ opt->rdblk = n; -+ err = 0; -+ opt->type = token; -+ break; -+ case Opt_rdhash: -+ if (unlikely(match_int(&a->args[0], &n) -+ || n < 0 -+ || n * sizeof(struct hlist_head) -+ > KMALLOC_MAX_SIZE)) { -+ pr_err("bad integer in %s\n", opt_str); -+ break; -+ } -+ opt->rdhash = n; -+ err = 0; -+ opt->type = token; -+ break; -+ -+ case Opt_trunc_xino: -+ case Opt_notrunc_xino: -+ case Opt_noxino: -+ case Opt_trunc_xib: -+ case Opt_notrunc_xib: -+ case Opt_shwh: -+ case Opt_noshwh: -+ case Opt_dirperm1: -+ case Opt_nodirperm1: -+ case Opt_plink: -+ case Opt_noplink: -+ case Opt_list_plink: -+ case Opt_dio: -+ case Opt_nodio: -+ case Opt_diropq_a: -+ case Opt_diropq_w: -+ case Opt_warn_perm: -+ case Opt_nowarn_perm: -+ case Opt_refrof: -+ case Opt_norefrof: -+ case Opt_verbose: -+ case Opt_noverbose: -+ case Opt_sum: -+ case Opt_nosum: -+ case Opt_wsum: -+ case Opt_rdblk_def: -+ case Opt_rdhash_def: -+ case Opt_acl: -+ case Opt_noacl: -+ err = 0; -+ opt->type = token; -+ break; -+ -+ case Opt_udba: -+ opt->udba = udba_val(a->args[0].from); -+ if (opt->udba >= 0) { -+ err = 0; -+ opt->type = token; -+ } else -+ pr_err("wrong value, %s\n", opt_str); -+ break; -+ -+ case Opt_wbr_create: -+ u.create = &opt->wbr_create; -+ u.create->wbr_create -+ = au_wbr_create_val(a->args[0].from, u.create); -+ if (u.create->wbr_create >= 0) { -+ err = 0; -+ opt->type = token; -+ } else -+ pr_err("wrong value, %s\n", opt_str); -+ break; -+ case Opt_wbr_copyup: -+ opt->wbr_copyup = au_wbr_copyup_val(a->args[0].from); -+ if (opt->wbr_copyup >= 0) { -+ err = 0; -+ opt->type = token; -+ } else -+ pr_err("wrong value, %s\n", opt_str); -+ break; -+ -+ case Opt_fhsm_sec: -+ if (unlikely(match_int(&a->args[0], &n) -+ || n < 0)) { -+ pr_err("bad integer in %s\n", opt_str); -+ break; -+ } -+ if (sysaufs_brs) { -+ opt->fhsm_second = n; -+ opt->type = token; -+ } else -+ pr_warn("ignored %s\n", opt_str); -+ err = 0; -+ break; -+ -+ case Opt_ignore: -+ pr_warn("ignored %s\n", opt_str); -+ /*FALLTHROUGH*/ -+ case Opt_ignore_silent: -+ skipped = 1; -+ err = 0; -+ break; -+ case Opt_err: -+ pr_err("unknown option %s\n", opt_str); -+ break; -+ } -+ -+ if (!err && !skipped) { -+ if (unlikely(++opt > opt_tail)) { -+ err = -E2BIG; -+ opt--; -+ opt->type = Opt_tail; -+ break; -+ } -+ opt->type = Opt_tail; -+ } -+ } -+ -+ kfree(a); -+ dump_opts(opts); -+ if (unlikely(err)) -+ au_opts_free(opts); -+ -+out: -+ return err; -+} -+ -+static int au_opt_wbr_create(struct super_block *sb, -+ struct au_opt_wbr_create *create) -+{ -+ int err; -+ struct au_sbinfo *sbinfo; -+ -+ SiMustWriteLock(sb); -+ -+ err = 1; /* handled */ -+ sbinfo = au_sbi(sb); -+ if (sbinfo->si_wbr_create_ops->fin) { -+ err = sbinfo->si_wbr_create_ops->fin(sb); -+ if (!err) -+ err = 1; -+ } -+ -+ sbinfo->si_wbr_create = create->wbr_create; -+ sbinfo->si_wbr_create_ops = au_wbr_create_ops + create->wbr_create; -+ switch (create->wbr_create) { -+ case AuWbrCreate_MFSRRV: -+ case AuWbrCreate_MFSRR: -+ case AuWbrCreate_PMFSRR: -+ case AuWbrCreate_PMFSRRV: -+ sbinfo->si_wbr_mfs.mfsrr_watermark = create->mfsrr_watermark; -+ /*FALLTHROUGH*/ -+ case AuWbrCreate_MFS: -+ case AuWbrCreate_MFSV: -+ case AuWbrCreate_PMFS: -+ case AuWbrCreate_PMFSV: -+ sbinfo->si_wbr_mfs.mfs_expire -+ = msecs_to_jiffies(create->mfs_second * MSEC_PER_SEC); -+ break; -+ } -+ -+ if (sbinfo->si_wbr_create_ops->init) -+ sbinfo->si_wbr_create_ops->init(sb); /* ignore */ -+ -+ return err; -+} -+ -+/* -+ * returns, -+ * plus: processed without an error -+ * zero: unprocessed -+ */ -+static int au_opt_simple(struct super_block *sb, struct au_opt *opt, -+ struct au_opts *opts) -+{ -+ int err; -+ struct au_sbinfo *sbinfo; -+ -+ SiMustWriteLock(sb); -+ -+ err = 1; /* handled */ -+ sbinfo = au_sbi(sb); -+ switch (opt->type) { -+ case Opt_udba: -+ sbinfo->si_mntflags &= ~AuOptMask_UDBA; -+ sbinfo->si_mntflags |= opt->udba; -+ opts->given_udba |= opt->udba; -+ break; -+ -+ case Opt_plink: -+ au_opt_set(sbinfo->si_mntflags, PLINK); -+ break; -+ case Opt_noplink: -+ if (au_opt_test(sbinfo->si_mntflags, PLINK)) -+ au_plink_put(sb, /*verbose*/1); -+ au_opt_clr(sbinfo->si_mntflags, PLINK); -+ break; -+ case Opt_list_plink: -+ if (au_opt_test(sbinfo->si_mntflags, PLINK)) -+ au_plink_list(sb); -+ break; -+ -+ case Opt_dio: -+ au_opt_set(sbinfo->si_mntflags, DIO); -+ au_fset_opts(opts->flags, REFRESH_DYAOP); -+ break; -+ case Opt_nodio: -+ au_opt_clr(sbinfo->si_mntflags, DIO); -+ au_fset_opts(opts->flags, REFRESH_DYAOP); -+ break; -+ -+ case Opt_fhsm_sec: -+ au_fhsm_set(sbinfo, opt->fhsm_second); -+ break; -+ -+ case Opt_diropq_a: -+ au_opt_set(sbinfo->si_mntflags, ALWAYS_DIROPQ); -+ break; -+ case Opt_diropq_w: -+ au_opt_clr(sbinfo->si_mntflags, ALWAYS_DIROPQ); -+ break; -+ -+ case Opt_warn_perm: -+ au_opt_set(sbinfo->si_mntflags, WARN_PERM); -+ break; -+ case Opt_nowarn_perm: -+ au_opt_clr(sbinfo->si_mntflags, WARN_PERM); -+ break; -+ -+ case Opt_refrof: -+ au_opt_set(sbinfo->si_mntflags, REFROF); -+ break; -+ case Opt_norefrof: -+ au_opt_clr(sbinfo->si_mntflags, REFROF); -+ break; -+ -+ case Opt_verbose: -+ au_opt_set(sbinfo->si_mntflags, VERBOSE); -+ break; -+ case Opt_noverbose: -+ au_opt_clr(sbinfo->si_mntflags, VERBOSE); -+ break; -+ -+ case Opt_sum: -+ au_opt_set(sbinfo->si_mntflags, SUM); -+ break; -+ case Opt_wsum: -+ au_opt_clr(sbinfo->si_mntflags, SUM); -+ au_opt_set(sbinfo->si_mntflags, SUM_W); -+ case Opt_nosum: -+ au_opt_clr(sbinfo->si_mntflags, SUM); -+ au_opt_clr(sbinfo->si_mntflags, SUM_W); -+ break; -+ -+ case Opt_wbr_create: -+ err = au_opt_wbr_create(sb, &opt->wbr_create); -+ break; -+ case Opt_wbr_copyup: -+ sbinfo->si_wbr_copyup = opt->wbr_copyup; -+ sbinfo->si_wbr_copyup_ops = au_wbr_copyup_ops + opt->wbr_copyup; -+ break; -+ -+ case Opt_dirwh: -+ sbinfo->si_dirwh = opt->dirwh; -+ break; -+ -+ case Opt_rdcache: -+ sbinfo->si_rdcache -+ = msecs_to_jiffies(opt->rdcache * MSEC_PER_SEC); -+ break; -+ case Opt_rdblk: -+ sbinfo->si_rdblk = opt->rdblk; -+ break; -+ case Opt_rdblk_def: -+ sbinfo->si_rdblk = AUFS_RDBLK_DEF; -+ break; -+ case Opt_rdhash: -+ sbinfo->si_rdhash = opt->rdhash; -+ break; -+ case Opt_rdhash_def: -+ sbinfo->si_rdhash = AUFS_RDHASH_DEF; -+ break; -+ -+ case Opt_shwh: -+ au_opt_set(sbinfo->si_mntflags, SHWH); -+ break; -+ case Opt_noshwh: -+ au_opt_clr(sbinfo->si_mntflags, SHWH); -+ break; -+ -+ case Opt_dirperm1: -+ au_opt_set(sbinfo->si_mntflags, DIRPERM1); -+ break; -+ case Opt_nodirperm1: -+ au_opt_clr(sbinfo->si_mntflags, DIRPERM1); -+ break; -+ -+ case Opt_trunc_xino: -+ au_opt_set(sbinfo->si_mntflags, TRUNC_XINO); -+ break; -+ case Opt_notrunc_xino: -+ au_opt_clr(sbinfo->si_mntflags, TRUNC_XINO); -+ break; -+ -+ case Opt_trunc_xino_path: -+ case Opt_itrunc_xino: -+ err = au_xino_trunc(sb, opt->xino_itrunc.bindex); -+ if (!err) -+ err = 1; -+ break; -+ -+ case Opt_trunc_xib: -+ au_fset_opts(opts->flags, TRUNC_XIB); -+ break; -+ case Opt_notrunc_xib: -+ au_fclr_opts(opts->flags, TRUNC_XIB); -+ break; -+ -+ case Opt_acl: -+ sb->s_flags |= MS_POSIXACL; -+ break; -+ case Opt_noacl: -+ sb->s_flags &= ~MS_POSIXACL; -+ break; -+ -+ default: -+ err = 0; -+ break; -+ } -+ -+ return err; -+} -+ -+/* -+ * returns tri-state. -+ * plus: processed without an error -+ * zero: unprocessed -+ * minus: error -+ */ -+static int au_opt_br(struct super_block *sb, struct au_opt *opt, -+ struct au_opts *opts) -+{ -+ int err, do_refresh; -+ -+ err = 0; -+ switch (opt->type) { -+ case Opt_append: -+ opt->add.bindex = au_sbend(sb) + 1; -+ if (opt->add.bindex < 0) -+ opt->add.bindex = 0; -+ goto add; -+ case Opt_prepend: -+ opt->add.bindex = 0; -+ add: /* indented label */ -+ case Opt_add: -+ err = au_br_add(sb, &opt->add, -+ au_ftest_opts(opts->flags, REMOUNT)); -+ if (!err) { -+ err = 1; -+ au_fset_opts(opts->flags, REFRESH); -+ } -+ break; -+ -+ case Opt_del: -+ case Opt_idel: -+ err = au_br_del(sb, &opt->del, -+ au_ftest_opts(opts->flags, REMOUNT)); -+ if (!err) { -+ err = 1; -+ au_fset_opts(opts->flags, TRUNC_XIB); -+ au_fset_opts(opts->flags, REFRESH); -+ } -+ break; -+ -+ case Opt_mod: -+ case Opt_imod: -+ err = au_br_mod(sb, &opt->mod, -+ au_ftest_opts(opts->flags, REMOUNT), -+ &do_refresh); -+ if (!err) { -+ err = 1; -+ if (do_refresh) -+ au_fset_opts(opts->flags, REFRESH); -+ } -+ break; -+ } -+ -+ return err; -+} -+ -+static int au_opt_xino(struct super_block *sb, struct au_opt *opt, -+ struct au_opt_xino **opt_xino, -+ struct au_opts *opts) -+{ -+ int err; -+ aufs_bindex_t bend, bindex; -+ struct dentry *root, *parent, *h_root; -+ -+ err = 0; -+ switch (opt->type) { -+ case Opt_xino: -+ err = au_xino_set(sb, &opt->xino, -+ !!au_ftest_opts(opts->flags, REMOUNT)); -+ if (unlikely(err)) -+ break; -+ -+ *opt_xino = &opt->xino; -+ au_xino_brid_set(sb, -1); -+ -+ /* safe d_parent access */ -+ parent = opt->xino.file->f_dentry->d_parent; -+ root = sb->s_root; -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { -+ h_root = au_h_dptr(root, bindex); -+ if (h_root == parent) { -+ au_xino_brid_set(sb, au_sbr_id(sb, bindex)); -+ break; -+ } -+ } -+ break; -+ -+ case Opt_noxino: -+ au_xino_clr(sb); -+ au_xino_brid_set(sb, -1); -+ *opt_xino = (void *)-1; -+ break; -+ } -+ -+ return err; -+} -+ -+int au_opts_verify(struct super_block *sb, unsigned long sb_flags, -+ unsigned int pending) -+{ -+ int err, fhsm; -+ aufs_bindex_t bindex, bend; -+ unsigned char do_plink, skip, do_free, can_no_dreval; -+ struct au_branch *br; -+ struct au_wbr *wbr; -+ struct dentry *root, *dentry; -+ struct inode *dir, *h_dir; -+ struct au_sbinfo *sbinfo; -+ struct au_hinode *hdir; -+ -+ SiMustAnyLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ AuDebugOn(!(sbinfo->si_mntflags & AuOptMask_UDBA)); -+ -+ if (!(sb_flags & MS_RDONLY)) { -+ if (unlikely(!au_br_writable(au_sbr_perm(sb, 0)))) -+ pr_warn("first branch should be rw\n"); -+ if (unlikely(au_opt_test(sbinfo->si_mntflags, SHWH))) -+ pr_warn_once("shwh should be used with ro\n"); -+ } -+ -+ if (au_opt_test((sbinfo->si_mntflags | pending), UDBA_HNOTIFY) -+ && !au_opt_test(sbinfo->si_mntflags, XINO)) -+ pr_warn_once("udba=*notify requires xino\n"); -+ -+ if (au_opt_test(sbinfo->si_mntflags, DIRPERM1)) -+ pr_warn_once("dirperm1 breaks the protection" -+ " by the permission bits on the lower branch\n"); -+ -+ err = 0; -+ fhsm = 0; -+ root = sb->s_root; -+ dir = root->d_inode; -+ do_plink = !!au_opt_test(sbinfo->si_mntflags, PLINK); -+ can_no_dreval = !!au_opt_test((sbinfo->si_mntflags | pending), -+ UDBA_NONE); -+ bend = au_sbend(sb); -+ for (bindex = 0; !err && bindex <= bend; bindex++) { -+ skip = 0; -+ h_dir = au_h_iptr(dir, bindex); -+ br = au_sbr(sb, bindex); -+ -+ if ((br->br_perm & AuBrAttr_ICEX) -+ && !h_dir->i_op->listxattr) -+ br->br_perm &= ~AuBrAttr_ICEX; -+#if 0 -+ if ((br->br_perm & AuBrAttr_ICEX_SEC) -+ && (au_br_sb(br)->s_flags & MS_NOSEC)) -+ br->br_perm &= ~AuBrAttr_ICEX_SEC; -+#endif -+ -+ do_free = 0; -+ wbr = br->br_wbr; -+ if (wbr) -+ wbr_wh_read_lock(wbr); -+ -+ if (!au_br_writable(br->br_perm)) { -+ do_free = !!wbr; -+ skip = (!wbr -+ || (!wbr->wbr_whbase -+ && !wbr->wbr_plink -+ && !wbr->wbr_orph)); -+ } else if (!au_br_wh_linkable(br->br_perm)) { -+ /* skip = (!br->br_whbase && !br->br_orph); */ -+ skip = (!wbr || !wbr->wbr_whbase); -+ if (skip && wbr) { -+ if (do_plink) -+ skip = !!wbr->wbr_plink; -+ else -+ skip = !wbr->wbr_plink; -+ } -+ } else { -+ /* skip = (br->br_whbase && br->br_ohph); */ -+ skip = (wbr && wbr->wbr_whbase); -+ if (skip) { -+ if (do_plink) -+ skip = !!wbr->wbr_plink; -+ else -+ skip = !wbr->wbr_plink; -+ } -+ } -+ if (wbr) -+ wbr_wh_read_unlock(wbr); -+ -+ if (can_no_dreval) { -+ dentry = br->br_path.dentry; -+ spin_lock(&dentry->d_lock); -+ if (dentry->d_flags & -+ (DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE)) -+ can_no_dreval = 0; -+ spin_unlock(&dentry->d_lock); -+ } -+ -+ if (au_br_fhsm(br->br_perm)) { -+ fhsm++; -+ AuDebugOn(!br->br_fhsm); -+ } -+ -+ if (skip) -+ continue; -+ -+ hdir = au_hi(dir, bindex); -+ au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); -+ if (wbr) -+ wbr_wh_write_lock(wbr); -+ err = au_wh_init(br, sb); -+ if (wbr) -+ wbr_wh_write_unlock(wbr); -+ au_hn_imtx_unlock(hdir); -+ -+ if (!err && do_free) { -+ kfree(wbr); -+ br->br_wbr = NULL; -+ } -+ } -+ -+ if (can_no_dreval) -+ au_fset_si(sbinfo, NO_DREVAL); -+ else -+ au_fclr_si(sbinfo, NO_DREVAL); -+ -+ if (fhsm >= 2) { -+ au_fset_si(sbinfo, FHSM); -+ for (bindex = bend; bindex >= 0; bindex--) { -+ br = au_sbr(sb, bindex); -+ if (au_br_fhsm(br->br_perm)) { -+ au_fhsm_set_bottom(sb, bindex); -+ break; -+ } -+ } -+ } else { -+ au_fclr_si(sbinfo, FHSM); -+ au_fhsm_set_bottom(sb, -1); -+ } -+ -+ return err; -+} -+ -+int au_opts_mount(struct super_block *sb, struct au_opts *opts) -+{ -+ int err; -+ unsigned int tmp; -+ aufs_bindex_t bindex, bend; -+ struct au_opt *opt; -+ struct au_opt_xino *opt_xino, xino; -+ struct au_sbinfo *sbinfo; -+ struct au_branch *br; -+ struct inode *dir; -+ -+ SiMustWriteLock(sb); -+ -+ err = 0; -+ opt_xino = NULL; -+ opt = opts->opt; -+ while (err >= 0 && opt->type != Opt_tail) -+ err = au_opt_simple(sb, opt++, opts); -+ if (err > 0) -+ err = 0; -+ else if (unlikely(err < 0)) -+ goto out; -+ -+ /* disable xino and udba temporary */ -+ sbinfo = au_sbi(sb); -+ tmp = sbinfo->si_mntflags; -+ au_opt_clr(sbinfo->si_mntflags, XINO); -+ au_opt_set_udba(sbinfo->si_mntflags, UDBA_REVAL); -+ -+ opt = opts->opt; -+ while (err >= 0 && opt->type != Opt_tail) -+ err = au_opt_br(sb, opt++, opts); -+ if (err > 0) -+ err = 0; -+ else if (unlikely(err < 0)) -+ goto out; -+ -+ bend = au_sbend(sb); -+ if (unlikely(bend < 0)) { -+ err = -EINVAL; -+ pr_err("no branches\n"); -+ goto out; -+ } -+ -+ if (au_opt_test(tmp, XINO)) -+ au_opt_set(sbinfo->si_mntflags, XINO); -+ opt = opts->opt; -+ while (!err && opt->type != Opt_tail) -+ err = au_opt_xino(sb, opt++, &opt_xino, opts); -+ if (unlikely(err)) -+ goto out; -+ -+ err = au_opts_verify(sb, sb->s_flags, tmp); -+ if (unlikely(err)) -+ goto out; -+ -+ /* restore xino */ -+ if (au_opt_test(tmp, XINO) && !opt_xino) { -+ xino.file = au_xino_def(sb); -+ err = PTR_ERR(xino.file); -+ if (IS_ERR(xino.file)) -+ goto out; -+ -+ err = au_xino_set(sb, &xino, /*remount*/0); -+ fput(xino.file); -+ if (unlikely(err)) -+ goto out; -+ } -+ -+ /* restore udba */ -+ tmp &= AuOptMask_UDBA; -+ sbinfo->si_mntflags &= ~AuOptMask_UDBA; -+ sbinfo->si_mntflags |= tmp; -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ err = au_hnotify_reset_br(tmp, br, br->br_perm); -+ if (unlikely(err)) -+ AuIOErr("hnotify failed on br %d, %d, ignored\n", -+ bindex, err); -+ /* go on even if err */ -+ } -+ if (au_opt_test(tmp, UDBA_HNOTIFY)) { -+ dir = sb->s_root->d_inode; -+ au_hn_reset(dir, au_hi_flags(dir, /*isdir*/1) & ~AuHi_XINO); -+ } -+ -+out: -+ return err; -+} -+ -+int au_opts_remount(struct super_block *sb, struct au_opts *opts) -+{ -+ int err, rerr; -+ unsigned char no_dreval; -+ struct inode *dir; -+ struct au_opt_xino *opt_xino; -+ struct au_opt *opt; -+ struct au_sbinfo *sbinfo; -+ -+ SiMustWriteLock(sb); -+ -+ err = 0; -+ dir = sb->s_root->d_inode; -+ sbinfo = au_sbi(sb); -+ opt_xino = NULL; -+ opt = opts->opt; -+ while (err >= 0 && opt->type != Opt_tail) { -+ err = au_opt_simple(sb, opt, opts); -+ if (!err) -+ err = au_opt_br(sb, opt, opts); -+ if (!err) -+ err = au_opt_xino(sb, opt, &opt_xino, opts); -+ opt++; -+ } -+ if (err > 0) -+ err = 0; -+ AuTraceErr(err); -+ /* go on even err */ -+ -+ no_dreval = !!au_ftest_si(sbinfo, NO_DREVAL); -+ rerr = au_opts_verify(sb, opts->sb_flags, /*pending*/0); -+ if (unlikely(rerr && !err)) -+ err = rerr; -+ -+ if (no_dreval != !!au_ftest_si(sbinfo, NO_DREVAL)) -+ au_fset_opts(opts->flags, REFRESH_IDOP); -+ -+ if (au_ftest_opts(opts->flags, TRUNC_XIB)) { -+ rerr = au_xib_trunc(sb); -+ if (unlikely(rerr && !err)) -+ err = rerr; -+ } -+ -+ /* will be handled by the caller */ -+ if (!au_ftest_opts(opts->flags, REFRESH) -+ && (opts->given_udba -+ || au_opt_test(sbinfo->si_mntflags, XINO) -+ || au_ftest_opts(opts->flags, REFRESH_IDOP) -+ )) -+ au_fset_opts(opts->flags, REFRESH); -+ -+ AuDbg("status 0x%x\n", opts->flags); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+unsigned int au_opt_udba(struct super_block *sb) -+{ -+ return au_mntflags(sb) & AuOptMask_UDBA; -+} -diff --git a/fs/aufs/opts.h b/fs/aufs/opts.h -new file mode 100644 -index 0000000..50949a0 ---- /dev/null -+++ b/fs/aufs/opts.h -@@ -0,0 +1,212 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * mount options/flags -+ */ -+ -+#ifndef __AUFS_OPTS_H__ -+#define __AUFS_OPTS_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+ -+struct file; -+struct super_block; -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* mount flags */ -+#define AuOpt_XINO 1 /* external inode number bitmap -+ and translation table */ -+#define AuOpt_TRUNC_XINO (1 << 1) /* truncate xino files */ -+#define AuOpt_UDBA_NONE (1 << 2) /* users direct branch access */ -+#define AuOpt_UDBA_REVAL (1 << 3) -+#define AuOpt_UDBA_HNOTIFY (1 << 4) -+#define AuOpt_SHWH (1 << 5) /* show whiteout */ -+#define AuOpt_PLINK (1 << 6) /* pseudo-link */ -+#define AuOpt_DIRPERM1 (1 << 7) /* ignore the lower dir's perm -+ bits */ -+#define AuOpt_REFROF (1 << 8) /* unimplemented */ -+#define AuOpt_ALWAYS_DIROPQ (1 << 9) /* policy to creating diropq */ -+#define AuOpt_SUM (1 << 10) /* summation for statfs(2) */ -+#define AuOpt_SUM_W (1 << 11) /* unimplemented */ -+#define AuOpt_WARN_PERM (1 << 12) /* warn when add-branch */ -+#define AuOpt_VERBOSE (1 << 13) /* busy inode when del-branch */ -+#define AuOpt_DIO (1 << 14) /* direct io */ -+ -+#ifndef CONFIG_AUFS_HNOTIFY -+#undef AuOpt_UDBA_HNOTIFY -+#define AuOpt_UDBA_HNOTIFY 0 -+#endif -+#ifndef CONFIG_AUFS_SHWH -+#undef AuOpt_SHWH -+#define AuOpt_SHWH 0 -+#endif -+ -+#define AuOpt_Def (AuOpt_XINO \ -+ | AuOpt_UDBA_REVAL \ -+ | AuOpt_PLINK \ -+ /* | AuOpt_DIRPERM1 */ \ -+ | AuOpt_WARN_PERM) -+#define AuOptMask_UDBA (AuOpt_UDBA_NONE \ -+ | AuOpt_UDBA_REVAL \ -+ | AuOpt_UDBA_HNOTIFY) -+ -+#define au_opt_test(flags, name) (flags & AuOpt_##name) -+#define au_opt_set(flags, name) do { \ -+ BUILD_BUG_ON(AuOpt_##name & AuOptMask_UDBA); \ -+ ((flags) |= AuOpt_##name); \ -+} while (0) -+#define au_opt_set_udba(flags, name) do { \ -+ (flags) &= ~AuOptMask_UDBA; \ -+ ((flags) |= AuOpt_##name); \ -+} while (0) -+#define au_opt_clr(flags, name) do { \ -+ ((flags) &= ~AuOpt_##name); \ -+} while (0) -+ -+static inline unsigned int au_opts_plink(unsigned int mntflags) -+{ -+#ifdef CONFIG_PROC_FS -+ return mntflags; -+#else -+ return mntflags & ~AuOpt_PLINK; -+#endif -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* policies to select one among multiple writable branches */ -+enum { -+ AuWbrCreate_TDP, /* top down parent */ -+ AuWbrCreate_RR, /* round robin */ -+ AuWbrCreate_MFS, /* most free space */ -+ AuWbrCreate_MFSV, /* mfs with seconds */ -+ AuWbrCreate_MFSRR, /* mfs then rr */ -+ AuWbrCreate_MFSRRV, /* mfs then rr with seconds */ -+ AuWbrCreate_PMFS, /* parent and mfs */ -+ AuWbrCreate_PMFSV, /* parent and mfs with seconds */ -+ AuWbrCreate_PMFSRR, /* parent, mfs and round-robin */ -+ AuWbrCreate_PMFSRRV, /* plus seconds */ -+ -+ AuWbrCreate_Def = AuWbrCreate_TDP -+}; -+ -+enum { -+ AuWbrCopyup_TDP, /* top down parent */ -+ AuWbrCopyup_BUP, /* bottom up parent */ -+ AuWbrCopyup_BU, /* bottom up */ -+ -+ AuWbrCopyup_Def = AuWbrCopyup_TDP -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct au_opt_add { -+ aufs_bindex_t bindex; -+ char *pathname; -+ int perm; -+ struct path path; -+}; -+ -+struct au_opt_del { -+ char *pathname; -+ struct path h_path; -+}; -+ -+struct au_opt_mod { -+ char *path; -+ int perm; -+ struct dentry *h_root; -+}; -+ -+struct au_opt_xino { -+ char *path; -+ struct file *file; -+}; -+ -+struct au_opt_xino_itrunc { -+ aufs_bindex_t bindex; -+}; -+ -+struct au_opt_wbr_create { -+ int wbr_create; -+ int mfs_second; -+ unsigned long long mfsrr_watermark; -+}; -+ -+struct au_opt { -+ int type; -+ union { -+ struct au_opt_xino xino; -+ struct au_opt_xino_itrunc xino_itrunc; -+ struct au_opt_add add; -+ struct au_opt_del del; -+ struct au_opt_mod mod; -+ int dirwh; -+ int rdcache; -+ unsigned int rdblk; -+ unsigned int rdhash; -+ int udba; -+ struct au_opt_wbr_create wbr_create; -+ int wbr_copyup; -+ unsigned int fhsm_second; -+ }; -+}; -+ -+/* opts flags */ -+#define AuOpts_REMOUNT 1 -+#define AuOpts_REFRESH (1 << 1) -+#define AuOpts_TRUNC_XIB (1 << 2) -+#define AuOpts_REFRESH_DYAOP (1 << 3) -+#define AuOpts_REFRESH_IDOP (1 << 4) -+#define au_ftest_opts(flags, name) ((flags) & AuOpts_##name) -+#define au_fset_opts(flags, name) \ -+ do { (flags) |= AuOpts_##name; } while (0) -+#define au_fclr_opts(flags, name) \ -+ do { (flags) &= ~AuOpts_##name; } while (0) -+ -+struct au_opts { -+ struct au_opt *opt; -+ int max_opt; -+ -+ unsigned int given_udba; -+ unsigned int flags; -+ unsigned long sb_flags; -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* opts.c */ -+void au_optstr_br_perm(au_br_perm_str_t *str, int perm); -+const char *au_optstr_udba(int udba); -+const char *au_optstr_wbr_copyup(int wbr_copyup); -+const char *au_optstr_wbr_create(int wbr_create); -+ -+void au_opts_free(struct au_opts *opts); -+int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts); -+int au_opts_verify(struct super_block *sb, unsigned long sb_flags, -+ unsigned int pending); -+int au_opts_mount(struct super_block *sb, struct au_opts *opts); -+int au_opts_remount(struct super_block *sb, struct au_opts *opts); -+ -+unsigned int au_opt_udba(struct super_block *sb); -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_OPTS_H__ */ -diff --git a/fs/aufs/plink.c b/fs/aufs/plink.c -new file mode 100644 -index 0000000..4f372ec ---- /dev/null -+++ b/fs/aufs/plink.c -@@ -0,0 +1,506 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * pseudo-link -+ */ -+ -+#include "aufs.h" -+ -+/* -+ * the pseudo-link maintenance mode. -+ * during a user process maintains the pseudo-links, -+ * prohibit adding a new plink and branch manipulation. -+ * -+ * Flags -+ * NOPLM: -+ * For entry functions which will handle plink, and i_mutex is already held -+ * in VFS. -+ * They cannot wait and should return an error at once. -+ * Callers has to check the error. -+ * NOPLMW: -+ * For entry functions which will handle plink, but i_mutex is not held -+ * in VFS. -+ * They can wait the plink maintenance mode to finish. -+ * -+ * They behave like F_SETLK and F_SETLKW. -+ * If the caller never handle plink, then both flags are unnecessary. -+ */ -+ -+int au_plink_maint(struct super_block *sb, int flags) -+{ -+ int err; -+ pid_t pid, ppid; -+ struct au_sbinfo *sbi; -+ -+ SiMustAnyLock(sb); -+ -+ err = 0; -+ if (!au_opt_test(au_mntflags(sb), PLINK)) -+ goto out; -+ -+ sbi = au_sbi(sb); -+ pid = sbi->si_plink_maint_pid; -+ if (!pid || pid == current->pid) -+ goto out; -+ -+ /* todo: it highly depends upon /sbin/mount.aufs */ -+ rcu_read_lock(); -+ ppid = task_pid_vnr(rcu_dereference(current->real_parent)); -+ rcu_read_unlock(); -+ if (pid == ppid) -+ goto out; -+ -+ if (au_ftest_lock(flags, NOPLMW)) { -+ /* if there is no i_mutex lock in VFS, we don't need to wait */ -+ /* AuDebugOn(!lockdep_depth(current)); */ -+ while (sbi->si_plink_maint_pid) { -+ si_read_unlock(sb); -+ /* gave up wake_up_bit() */ -+ wait_event(sbi->si_plink_wq, !sbi->si_plink_maint_pid); -+ -+ if (au_ftest_lock(flags, FLUSH)) -+ au_nwt_flush(&sbi->si_nowait); -+ si_noflush_read_lock(sb); -+ } -+ } else if (au_ftest_lock(flags, NOPLM)) { -+ AuDbg("ppid %d, pid %d\n", ppid, pid); -+ err = -EAGAIN; -+ } -+ -+out: -+ return err; -+} -+ -+void au_plink_maint_leave(struct au_sbinfo *sbinfo) -+{ -+ spin_lock(&sbinfo->si_plink_maint_lock); -+ sbinfo->si_plink_maint_pid = 0; -+ spin_unlock(&sbinfo->si_plink_maint_lock); -+ wake_up_all(&sbinfo->si_plink_wq); -+} -+ -+int au_plink_maint_enter(struct super_block *sb) -+{ -+ int err; -+ struct au_sbinfo *sbinfo; -+ -+ err = 0; -+ sbinfo = au_sbi(sb); -+ /* make sure i am the only one in this fs */ -+ si_write_lock(sb, AuLock_FLUSH); -+ if (au_opt_test(au_mntflags(sb), PLINK)) { -+ spin_lock(&sbinfo->si_plink_maint_lock); -+ if (!sbinfo->si_plink_maint_pid) -+ sbinfo->si_plink_maint_pid = current->pid; -+ else -+ err = -EBUSY; -+ spin_unlock(&sbinfo->si_plink_maint_lock); -+ } -+ si_write_unlock(sb); -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+#ifdef CONFIG_AUFS_DEBUG -+void au_plink_list(struct super_block *sb) -+{ -+ int i; -+ struct au_sbinfo *sbinfo; -+ struct hlist_head *plink_hlist; -+ struct au_icntnr *icntnr; -+ -+ SiMustAnyLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); -+ AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); -+ -+ for (i = 0; i < AuPlink_NHASH; i++) { -+ plink_hlist = &sbinfo->si_plink[i].head; -+ rcu_read_lock(); -+ hlist_for_each_entry_rcu(icntnr, plink_hlist, plink) -+ AuDbg("%lu\n", icntnr->vfs_inode.i_ino); -+ rcu_read_unlock(); -+ } -+} -+#endif -+ -+/* is the inode pseudo-linked? */ -+int au_plink_test(struct inode *inode) -+{ -+ int found, i; -+ struct au_sbinfo *sbinfo; -+ struct hlist_head *plink_hlist; -+ struct au_icntnr *icntnr; -+ -+ sbinfo = au_sbi(inode->i_sb); -+ AuRwMustAnyLock(&sbinfo->si_rwsem); -+ AuDebugOn(!au_opt_test(au_mntflags(inode->i_sb), PLINK)); -+ AuDebugOn(au_plink_maint(inode->i_sb, AuLock_NOPLM)); -+ -+ found = 0; -+ i = au_plink_hash(inode->i_ino); -+ plink_hlist = &sbinfo->si_plink[i].head; -+ rcu_read_lock(); -+ hlist_for_each_entry_rcu(icntnr, plink_hlist, plink) -+ if (&icntnr->vfs_inode == inode) { -+ found = 1; -+ break; -+ } -+ rcu_read_unlock(); -+ return found; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * generate a name for plink. -+ * the file will be stored under AUFS_WH_PLINKDIR. -+ */ -+/* 20 is max digits length of ulong 64 */ -+#define PLINK_NAME_LEN ((20 + 1) * 2) -+ -+static int plink_name(char *name, int len, struct inode *inode, -+ aufs_bindex_t bindex) -+{ -+ int rlen; -+ struct inode *h_inode; -+ -+ h_inode = au_h_iptr(inode, bindex); -+ rlen = snprintf(name, len, "%lu.%lu", inode->i_ino, h_inode->i_ino); -+ return rlen; -+} -+ -+struct au_do_plink_lkup_args { -+ struct dentry **errp; -+ struct qstr *tgtname; -+ struct dentry *h_parent; -+ struct au_branch *br; -+}; -+ -+static struct dentry *au_do_plink_lkup(struct qstr *tgtname, -+ struct dentry *h_parent, -+ struct au_branch *br) -+{ -+ struct dentry *h_dentry; -+ struct mutex *h_mtx; -+ -+ h_mtx = &h_parent->d_inode->i_mutex; -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD2); -+ h_dentry = vfsub_lkup_one(tgtname, h_parent); -+ mutex_unlock(h_mtx); -+ return h_dentry; -+} -+ -+static void au_call_do_plink_lkup(void *args) -+{ -+ struct au_do_plink_lkup_args *a = args; -+ *a->errp = au_do_plink_lkup(a->tgtname, a->h_parent, a->br); -+} -+ -+/* lookup the plink-ed @inode under the branch at @bindex */ -+struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex) -+{ -+ struct dentry *h_dentry, *h_parent; -+ struct au_branch *br; -+ struct inode *h_dir; -+ int wkq_err; -+ char a[PLINK_NAME_LEN]; -+ struct qstr tgtname = QSTR_INIT(a, 0); -+ -+ AuDebugOn(au_plink_maint(inode->i_sb, AuLock_NOPLM)); -+ -+ br = au_sbr(inode->i_sb, bindex); -+ h_parent = br->br_wbr->wbr_plink; -+ h_dir = h_parent->d_inode; -+ tgtname.len = plink_name(a, sizeof(a), inode, bindex); -+ -+ if (!uid_eq(current_fsuid(), GLOBAL_ROOT_UID)) { -+ struct au_do_plink_lkup_args args = { -+ .errp = &h_dentry, -+ .tgtname = &tgtname, -+ .h_parent = h_parent, -+ .br = br -+ }; -+ -+ wkq_err = au_wkq_wait(au_call_do_plink_lkup, &args); -+ if (unlikely(wkq_err)) -+ h_dentry = ERR_PTR(wkq_err); -+ } else -+ h_dentry = au_do_plink_lkup(&tgtname, h_parent, br); -+ -+ return h_dentry; -+} -+ -+/* create a pseudo-link */ -+static int do_whplink(struct qstr *tgt, struct dentry *h_parent, -+ struct dentry *h_dentry, struct au_branch *br) -+{ -+ int err; -+ struct path h_path = { -+ .mnt = au_br_mnt(br) -+ }; -+ struct inode *h_dir, *delegated; -+ -+ h_dir = h_parent->d_inode; -+ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_CHILD2); -+again: -+ h_path.dentry = vfsub_lkup_one(tgt, h_parent); -+ err = PTR_ERR(h_path.dentry); -+ if (IS_ERR(h_path.dentry)) -+ goto out; -+ -+ err = 0; -+ /* wh.plink dir is not monitored */ -+ /* todo: is it really safe? */ -+ if (h_path.dentry->d_inode -+ && h_path.dentry->d_inode != h_dentry->d_inode) { -+ delegated = NULL; -+ err = vfsub_unlink(h_dir, &h_path, &delegated, /*force*/0); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal unlink\n"); -+ iput(delegated); -+ } -+ dput(h_path.dentry); -+ h_path.dentry = NULL; -+ if (!err) -+ goto again; -+ } -+ if (!err && !h_path.dentry->d_inode) { -+ delegated = NULL; -+ err = vfsub_link(h_dentry, h_dir, &h_path, &delegated); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal link\n"); -+ iput(delegated); -+ } -+ } -+ dput(h_path.dentry); -+ -+out: -+ mutex_unlock(&h_dir->i_mutex); -+ return err; -+} -+ -+struct do_whplink_args { -+ int *errp; -+ struct qstr *tgt; -+ struct dentry *h_parent; -+ struct dentry *h_dentry; -+ struct au_branch *br; -+}; -+ -+static void call_do_whplink(void *args) -+{ -+ struct do_whplink_args *a = args; -+ *a->errp = do_whplink(a->tgt, a->h_parent, a->h_dentry, a->br); -+} -+ -+static int whplink(struct dentry *h_dentry, struct inode *inode, -+ aufs_bindex_t bindex, struct au_branch *br) -+{ -+ int err, wkq_err; -+ struct au_wbr *wbr; -+ struct dentry *h_parent; -+ struct inode *h_dir; -+ char a[PLINK_NAME_LEN]; -+ struct qstr tgtname = QSTR_INIT(a, 0); -+ -+ wbr = au_sbr(inode->i_sb, bindex)->br_wbr; -+ h_parent = wbr->wbr_plink; -+ h_dir = h_parent->d_inode; -+ tgtname.len = plink_name(a, sizeof(a), inode, bindex); -+ -+ /* always superio. */ -+ if (!uid_eq(current_fsuid(), GLOBAL_ROOT_UID)) { -+ struct do_whplink_args args = { -+ .errp = &err, -+ .tgt = &tgtname, -+ .h_parent = h_parent, -+ .h_dentry = h_dentry, -+ .br = br -+ }; -+ wkq_err = au_wkq_wait(call_do_whplink, &args); -+ if (unlikely(wkq_err)) -+ err = wkq_err; -+ } else -+ err = do_whplink(&tgtname, h_parent, h_dentry, br); -+ -+ return err; -+} -+ -+/* -+ * create a new pseudo-link for @h_dentry on @bindex. -+ * the linked inode is held in aufs @inode. -+ */ -+void au_plink_append(struct inode *inode, aufs_bindex_t bindex, -+ struct dentry *h_dentry) -+{ -+ struct super_block *sb; -+ struct au_sbinfo *sbinfo; -+ struct hlist_head *plink_hlist; -+ struct au_icntnr *icntnr; -+ struct au_sphlhead *sphl; -+ int found, err, cnt, i; -+ -+ sb = inode->i_sb; -+ sbinfo = au_sbi(sb); -+ AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); -+ AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); -+ -+ found = au_plink_test(inode); -+ if (found) -+ return; -+ -+ i = au_plink_hash(inode->i_ino); -+ sphl = sbinfo->si_plink + i; -+ plink_hlist = &sphl->head; -+ au_igrab(inode); -+ -+ spin_lock(&sphl->spin); -+ hlist_for_each_entry(icntnr, plink_hlist, plink) { -+ if (&icntnr->vfs_inode == inode) { -+ found = 1; -+ break; -+ } -+ } -+ if (!found) { -+ icntnr = container_of(inode, struct au_icntnr, vfs_inode); -+ hlist_add_head_rcu(&icntnr->plink, plink_hlist); -+ } -+ spin_unlock(&sphl->spin); -+ if (!found) { -+ cnt = au_sphl_count(sphl); -+#define msg "unexpectedly unblanced or too many pseudo-links" -+ if (cnt > AUFS_PLINK_WARN) -+ AuWarn1(msg ", %d\n", cnt); -+#undef msg -+ err = whplink(h_dentry, inode, bindex, au_sbr(sb, bindex)); -+ if (unlikely(err)) { -+ pr_warn("err %d, damaged pseudo link.\n", err); -+ au_sphl_del_rcu(&icntnr->plink, sphl); -+ iput(&icntnr->vfs_inode); -+ } -+ } else -+ iput(&icntnr->vfs_inode); -+} -+ -+/* free all plinks */ -+void au_plink_put(struct super_block *sb, int verbose) -+{ -+ int i, warned; -+ struct au_sbinfo *sbinfo; -+ struct hlist_head *plink_hlist; -+ struct hlist_node *tmp; -+ struct au_icntnr *icntnr; -+ -+ SiMustWriteLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); -+ AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); -+ -+ /* no spin_lock since sbinfo is write-locked */ -+ warned = 0; -+ for (i = 0; i < AuPlink_NHASH; i++) { -+ plink_hlist = &sbinfo->si_plink[i].head; -+ if (!warned && verbose && !hlist_empty(plink_hlist)) { -+ pr_warn("pseudo-link is not flushed"); -+ warned = 1; -+ } -+ hlist_for_each_entry_safe(icntnr, tmp, plink_hlist, plink) -+ iput(&icntnr->vfs_inode); -+ INIT_HLIST_HEAD(plink_hlist); -+ } -+} -+ -+void au_plink_clean(struct super_block *sb, int verbose) -+{ -+ struct dentry *root; -+ -+ root = sb->s_root; -+ aufs_write_lock(root); -+ if (au_opt_test(au_mntflags(sb), PLINK)) -+ au_plink_put(sb, verbose); -+ aufs_write_unlock(root); -+} -+ -+static int au_plink_do_half_refresh(struct inode *inode, aufs_bindex_t br_id) -+{ -+ int do_put; -+ aufs_bindex_t bstart, bend, bindex; -+ -+ do_put = 0; -+ bstart = au_ibstart(inode); -+ bend = au_ibend(inode); -+ if (bstart >= 0) { -+ for (bindex = bstart; bindex <= bend; bindex++) { -+ if (!au_h_iptr(inode, bindex) -+ || au_ii_br_id(inode, bindex) != br_id) -+ continue; -+ au_set_h_iptr(inode, bindex, NULL, 0); -+ do_put = 1; -+ break; -+ } -+ if (do_put) -+ for (bindex = bstart; bindex <= bend; bindex++) -+ if (au_h_iptr(inode, bindex)) { -+ do_put = 0; -+ break; -+ } -+ } else -+ do_put = 1; -+ -+ return do_put; -+} -+ -+/* free the plinks on a branch specified by @br_id */ -+void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id) -+{ -+ struct au_sbinfo *sbinfo; -+ struct hlist_head *plink_hlist; -+ struct hlist_node *tmp; -+ struct au_icntnr *icntnr; -+ struct inode *inode; -+ int i, do_put; -+ -+ SiMustWriteLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); -+ AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); -+ -+ /* no spin_lock since sbinfo is write-locked */ -+ for (i = 0; i < AuPlink_NHASH; i++) { -+ plink_hlist = &sbinfo->si_plink[i].head; -+ hlist_for_each_entry_safe(icntnr, tmp, plink_hlist, plink) { -+ inode = au_igrab(&icntnr->vfs_inode); -+ ii_write_lock_child(inode); -+ do_put = au_plink_do_half_refresh(inode, br_id); -+ if (do_put) { -+ hlist_del(&icntnr->plink); -+ iput(inode); -+ } -+ ii_write_unlock(inode); -+ iput(inode); -+ } -+ } -+} -diff --git a/fs/aufs/poll.c b/fs/aufs/poll.c -new file mode 100644 -index 0000000..eea19e7 ---- /dev/null -+++ b/fs/aufs/poll.c -@@ -0,0 +1,52 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * poll operation -+ * There is only one filesystem which implements ->poll operation, currently. -+ */ -+ -+#include "aufs.h" -+ -+unsigned int aufs_poll(struct file *file, poll_table *wait) -+{ -+ unsigned int mask; -+ int err; -+ struct file *h_file; -+ struct super_block *sb; -+ -+ /* We should pretend an error happened. */ -+ mask = POLLERR /* | POLLIN | POLLOUT */; -+ sb = file->f_dentry->d_sb; -+ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); -+ -+ h_file = au_read_pre(file, /*keep_fi*/0); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ /* it is not an error if h_file has no operation */ -+ mask = DEFAULT_POLLMASK; -+ if (h_file->f_op->poll) -+ mask = h_file->f_op->poll(h_file, wait); -+ fput(h_file); /* instead of au_read_post() */ -+ -+out: -+ si_read_unlock(sb); -+ AuTraceErr((int)mask); -+ return mask; -+} -diff --git a/fs/aufs/posix_acl.c b/fs/aufs/posix_acl.c -new file mode 100644 -index 0000000..89b4127 ---- /dev/null -+++ b/fs/aufs/posix_acl.c -@@ -0,0 +1,98 @@ -+/* -+ * Copyright (C) 2014-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * posix acl operations -+ */ -+ -+#include -+#include "aufs.h" -+ -+struct posix_acl *aufs_get_acl(struct inode *inode, int type) -+{ -+ struct posix_acl *acl; -+ int err; -+ aufs_bindex_t bindex; -+ struct inode *h_inode; -+ struct super_block *sb; -+ -+ acl = NULL; -+ sb = inode->i_sb; -+ si_read_lock(sb, AuLock_FLUSH); -+ ii_read_lock_child(inode); -+ if (!(sb->s_flags & MS_POSIXACL)) -+ goto out; -+ -+ bindex = au_ibstart(inode); -+ h_inode = au_h_iptr(inode, bindex); -+ if (unlikely(!h_inode -+ || ((h_inode->i_mode & S_IFMT) -+ != (inode->i_mode & S_IFMT)))) { -+ err = au_busy_or_stale(); -+ acl = ERR_PTR(err); -+ goto out; -+ } -+ -+ /* always topmost only */ -+ acl = get_acl(h_inode, type); -+ -+out: -+ ii_read_unlock(inode); -+ si_read_unlock(sb); -+ -+ AuTraceErrPtr(acl); -+ return acl; -+} -+ -+int aufs_set_acl(struct inode *inode, struct posix_acl *acl, int type) -+{ -+ int err; -+ ssize_t ssz; -+ struct dentry *dentry; -+ struct au_srxattr arg = { -+ .type = AU_ACL_SET, -+ .u.acl_set = { -+ .acl = acl, -+ .type = type -+ }, -+ }; -+ -+ mutex_lock(&inode->i_mutex); -+ if (inode->i_ino == AUFS_ROOT_INO) -+ dentry = dget(inode->i_sb->s_root); -+ else { -+ dentry = d_find_alias(inode); -+ if (!dentry) -+ dentry = d_find_any_alias(inode); -+ if (!dentry) { -+ pr_warn("cannot handle this inode, " -+ "please report to aufs-users ML\n"); -+ err = -ENOENT; -+ goto out; -+ } -+ } -+ -+ ssz = au_srxattr(dentry, &arg); -+ dput(dentry); -+ err = ssz; -+ if (ssz >= 0) -+ err = 0; -+ -+out: -+ mutex_unlock(&inode->i_mutex); -+ return err; -+} -diff --git a/fs/aufs/procfs.c b/fs/aufs/procfs.c -new file mode 100644 -index 0000000..a334330 ---- /dev/null -+++ b/fs/aufs/procfs.c -@@ -0,0 +1,169 @@ -+/* -+ * Copyright (C) 2010-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * procfs interfaces -+ */ -+ -+#include -+#include "aufs.h" -+ -+static int au_procfs_plm_release(struct inode *inode, struct file *file) -+{ -+ struct au_sbinfo *sbinfo; -+ -+ sbinfo = file->private_data; -+ if (sbinfo) { -+ au_plink_maint_leave(sbinfo); -+ kobject_put(&sbinfo->si_kobj); -+ } -+ -+ return 0; -+} -+ -+static void au_procfs_plm_write_clean(struct file *file) -+{ -+ struct au_sbinfo *sbinfo; -+ -+ sbinfo = file->private_data; -+ if (sbinfo) -+ au_plink_clean(sbinfo->si_sb, /*verbose*/0); -+} -+ -+static int au_procfs_plm_write_si(struct file *file, unsigned long id) -+{ -+ int err; -+ struct super_block *sb; -+ struct au_sbinfo *sbinfo; -+ -+ err = -EBUSY; -+ if (unlikely(file->private_data)) -+ goto out; -+ -+ sb = NULL; -+ /* don't use au_sbilist_lock() here */ -+ spin_lock(&au_sbilist.spin); -+ hlist_for_each_entry(sbinfo, &au_sbilist.head, si_list) -+ if (id == sysaufs_si_id(sbinfo)) { -+ kobject_get(&sbinfo->si_kobj); -+ sb = sbinfo->si_sb; -+ break; -+ } -+ spin_unlock(&au_sbilist.spin); -+ -+ err = -EINVAL; -+ if (unlikely(!sb)) -+ goto out; -+ -+ err = au_plink_maint_enter(sb); -+ if (!err) -+ /* keep kobject_get() */ -+ file->private_data = sbinfo; -+ else -+ kobject_put(&sbinfo->si_kobj); -+out: -+ return err; -+} -+ -+/* -+ * Accept a valid "si=xxxx" only. -+ * Once it is accepted successfully, accept "clean" too. -+ */ -+static ssize_t au_procfs_plm_write(struct file *file, const char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ ssize_t err; -+ unsigned long id; -+ /* last newline is allowed */ -+ char buf[3 + sizeof(unsigned long) * 2 + 1]; -+ -+ err = -EACCES; -+ if (unlikely(!capable(CAP_SYS_ADMIN))) -+ goto out; -+ -+ err = -EINVAL; -+ if (unlikely(count > sizeof(buf))) -+ goto out; -+ -+ err = copy_from_user(buf, ubuf, count); -+ if (unlikely(err)) { -+ err = -EFAULT; -+ goto out; -+ } -+ buf[count] = 0; -+ -+ err = -EINVAL; -+ if (!strcmp("clean", buf)) { -+ au_procfs_plm_write_clean(file); -+ goto out_success; -+ } else if (unlikely(strncmp("si=", buf, 3))) -+ goto out; -+ -+ err = kstrtoul(buf + 3, 16, &id); -+ if (unlikely(err)) -+ goto out; -+ -+ err = au_procfs_plm_write_si(file, id); -+ if (unlikely(err)) -+ goto out; -+ -+out_success: -+ err = count; /* success */ -+out: -+ return err; -+} -+ -+static const struct file_operations au_procfs_plm_fop = { -+ .write = au_procfs_plm_write, -+ .release = au_procfs_plm_release, -+ .owner = THIS_MODULE -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+static struct proc_dir_entry *au_procfs_dir; -+ -+void au_procfs_fin(void) -+{ -+ remove_proc_entry(AUFS_PLINK_MAINT_NAME, au_procfs_dir); -+ remove_proc_entry(AUFS_PLINK_MAINT_DIR, NULL); -+} -+ -+int __init au_procfs_init(void) -+{ -+ int err; -+ struct proc_dir_entry *entry; -+ -+ err = -ENOMEM; -+ au_procfs_dir = proc_mkdir(AUFS_PLINK_MAINT_DIR, NULL); -+ if (unlikely(!au_procfs_dir)) -+ goto out; -+ -+ entry = proc_create(AUFS_PLINK_MAINT_NAME, S_IFREG | S_IWUSR, -+ au_procfs_dir, &au_procfs_plm_fop); -+ if (unlikely(!entry)) -+ goto out_dir; -+ -+ err = 0; -+ goto out; /* success */ -+ -+ -+out_dir: -+ remove_proc_entry(AUFS_PLINK_MAINT_DIR, NULL); -+out: -+ return err; -+} -diff --git a/fs/aufs/rdu.c b/fs/aufs/rdu.c -new file mode 100644 -index 0000000..d22b2f8 ---- /dev/null -+++ b/fs/aufs/rdu.c -@@ -0,0 +1,388 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * readdir in userspace. -+ */ -+ -+#include -+#include -+#include -+#include "aufs.h" -+ -+/* bits for struct aufs_rdu.flags */ -+#define AuRdu_CALLED 1 -+#define AuRdu_CONT (1 << 1) -+#define AuRdu_FULL (1 << 2) -+#define au_ftest_rdu(flags, name) ((flags) & AuRdu_##name) -+#define au_fset_rdu(flags, name) \ -+ do { (flags) |= AuRdu_##name; } while (0) -+#define au_fclr_rdu(flags, name) \ -+ do { (flags) &= ~AuRdu_##name; } while (0) -+ -+struct au_rdu_arg { -+ struct dir_context ctx; -+ struct aufs_rdu *rdu; -+ union au_rdu_ent_ul ent; -+ unsigned long end; -+ -+ struct super_block *sb; -+ int err; -+}; -+ -+static int au_rdu_fill(struct dir_context *ctx, const char *name, int nlen, -+ loff_t offset, u64 h_ino, unsigned int d_type) -+{ -+ int err, len; -+ struct au_rdu_arg *arg = container_of(ctx, struct au_rdu_arg, ctx); -+ struct aufs_rdu *rdu = arg->rdu; -+ struct au_rdu_ent ent; -+ -+ err = 0; -+ arg->err = 0; -+ au_fset_rdu(rdu->cookie.flags, CALLED); -+ len = au_rdu_len(nlen); -+ if (arg->ent.ul + len < arg->end) { -+ ent.ino = h_ino; -+ ent.bindex = rdu->cookie.bindex; -+ ent.type = d_type; -+ ent.nlen = nlen; -+ if (unlikely(nlen > AUFS_MAX_NAMELEN)) -+ ent.type = DT_UNKNOWN; -+ -+ /* unnecessary to support mmap_sem since this is a dir */ -+ err = -EFAULT; -+ if (copy_to_user(arg->ent.e, &ent, sizeof(ent))) -+ goto out; -+ if (copy_to_user(arg->ent.e->name, name, nlen)) -+ goto out; -+ /* the terminating NULL */ -+ if (__put_user(0, arg->ent.e->name + nlen)) -+ goto out; -+ err = 0; -+ /* AuDbg("%p, %.*s\n", arg->ent.p, nlen, name); */ -+ arg->ent.ul += len; -+ rdu->rent++; -+ } else { -+ err = -EFAULT; -+ au_fset_rdu(rdu->cookie.flags, FULL); -+ rdu->full = 1; -+ rdu->tail = arg->ent; -+ } -+ -+out: -+ /* AuTraceErr(err); */ -+ return err; -+} -+ -+static int au_rdu_do(struct file *h_file, struct au_rdu_arg *arg) -+{ -+ int err; -+ loff_t offset; -+ struct au_rdu_cookie *cookie = &arg->rdu->cookie; -+ -+ /* we don't have to care (FMODE_32BITHASH | FMODE_64BITHASH) for ext4 */ -+ offset = vfsub_llseek(h_file, cookie->h_pos, SEEK_SET); -+ err = offset; -+ if (unlikely(offset != cookie->h_pos)) -+ goto out; -+ -+ err = 0; -+ do { -+ arg->err = 0; -+ au_fclr_rdu(cookie->flags, CALLED); -+ /* smp_mb(); */ -+ err = vfsub_iterate_dir(h_file, &arg->ctx); -+ if (err >= 0) -+ err = arg->err; -+ } while (!err -+ && au_ftest_rdu(cookie->flags, CALLED) -+ && !au_ftest_rdu(cookie->flags, FULL)); -+ cookie->h_pos = h_file->f_pos; -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+static int au_rdu(struct file *file, struct aufs_rdu *rdu) -+{ -+ int err; -+ aufs_bindex_t bend; -+ struct au_rdu_arg arg = { -+ .ctx = { -+ .actor = au_diractor(au_rdu_fill) -+ } -+ }; -+ struct dentry *dentry; -+ struct inode *inode; -+ struct file *h_file; -+ struct au_rdu_cookie *cookie = &rdu->cookie; -+ -+ err = !access_ok(VERIFY_WRITE, rdu->ent.e, rdu->sz); -+ if (unlikely(err)) { -+ err = -EFAULT; -+ AuTraceErr(err); -+ goto out; -+ } -+ rdu->rent = 0; -+ rdu->tail = rdu->ent; -+ rdu->full = 0; -+ arg.rdu = rdu; -+ arg.ent = rdu->ent; -+ arg.end = arg.ent.ul; -+ arg.end += rdu->sz; -+ -+ err = -ENOTDIR; -+ if (unlikely(!file->f_op->iterate)) -+ goto out; -+ -+ err = security_file_permission(file, MAY_READ); -+ AuTraceErr(err); -+ if (unlikely(err)) -+ goto out; -+ -+ dentry = file->f_dentry; -+ inode = dentry->d_inode; -+#if 1 -+ mutex_lock(&inode->i_mutex); -+#else -+ err = mutex_lock_killable(&inode->i_mutex); -+ AuTraceErr(err); -+ if (unlikely(err)) -+ goto out; -+#endif -+ -+ arg.sb = inode->i_sb; -+ err = si_read_lock(arg.sb, AuLock_FLUSH | AuLock_NOPLM); -+ if (unlikely(err)) -+ goto out_mtx; -+ err = au_alive_dir(dentry); -+ if (unlikely(err)) -+ goto out_si; -+ /* todo: reval? */ -+ fi_read_lock(file); -+ -+ err = -EAGAIN; -+ if (unlikely(au_ftest_rdu(cookie->flags, CONT) -+ && cookie->generation != au_figen(file))) -+ goto out_unlock; -+ -+ err = 0; -+ if (!rdu->blk) { -+ rdu->blk = au_sbi(arg.sb)->si_rdblk; -+ if (!rdu->blk) -+ rdu->blk = au_dir_size(file, /*dentry*/NULL); -+ } -+ bend = au_fbstart(file); -+ if (cookie->bindex < bend) -+ cookie->bindex = bend; -+ bend = au_fbend_dir(file); -+ /* AuDbg("b%d, b%d\n", cookie->bindex, bend); */ -+ for (; !err && cookie->bindex <= bend; -+ cookie->bindex++, cookie->h_pos = 0) { -+ h_file = au_hf_dir(file, cookie->bindex); -+ if (!h_file) -+ continue; -+ -+ au_fclr_rdu(cookie->flags, FULL); -+ err = au_rdu_do(h_file, &arg); -+ AuTraceErr(err); -+ if (unlikely(au_ftest_rdu(cookie->flags, FULL) || err)) -+ break; -+ } -+ AuDbg("rent %llu\n", rdu->rent); -+ -+ if (!err && !au_ftest_rdu(cookie->flags, CONT)) { -+ rdu->shwh = !!au_opt_test(au_sbi(arg.sb)->si_mntflags, SHWH); -+ au_fset_rdu(cookie->flags, CONT); -+ cookie->generation = au_figen(file); -+ } -+ -+ ii_read_lock_child(inode); -+ fsstack_copy_attr_atime(inode, au_h_iptr(inode, au_ibstart(inode))); -+ ii_read_unlock(inode); -+ -+out_unlock: -+ fi_read_unlock(file); -+out_si: -+ si_read_unlock(arg.sb); -+out_mtx: -+ mutex_unlock(&inode->i_mutex); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+static int au_rdu_ino(struct file *file, struct aufs_rdu *rdu) -+{ -+ int err; -+ ino_t ino; -+ unsigned long long nent; -+ union au_rdu_ent_ul *u; -+ struct au_rdu_ent ent; -+ struct super_block *sb; -+ -+ err = 0; -+ nent = rdu->nent; -+ u = &rdu->ent; -+ sb = file->f_dentry->d_sb; -+ si_read_lock(sb, AuLock_FLUSH); -+ while (nent-- > 0) { -+ /* unnecessary to support mmap_sem since this is a dir */ -+ err = copy_from_user(&ent, u->e, sizeof(ent)); -+ if (!err) -+ err = !access_ok(VERIFY_WRITE, &u->e->ino, sizeof(ino)); -+ if (unlikely(err)) { -+ err = -EFAULT; -+ AuTraceErr(err); -+ break; -+ } -+ -+ /* AuDbg("b%d, i%llu\n", ent.bindex, ent.ino); */ -+ if (!ent.wh) -+ err = au_ino(sb, ent.bindex, ent.ino, ent.type, &ino); -+ else -+ err = au_wh_ino(sb, ent.bindex, ent.ino, ent.type, -+ &ino); -+ if (unlikely(err)) { -+ AuTraceErr(err); -+ break; -+ } -+ -+ err = __put_user(ino, &u->e->ino); -+ if (unlikely(err)) { -+ err = -EFAULT; -+ AuTraceErr(err); -+ break; -+ } -+ u->ul += au_rdu_len(ent.nlen); -+ } -+ si_read_unlock(sb); -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_rdu_verify(struct aufs_rdu *rdu) -+{ -+ AuDbg("rdu{%llu, %p, %u | %u | %llu, %u, %u | " -+ "%llu, b%d, 0x%x, g%u}\n", -+ rdu->sz, rdu->ent.e, rdu->verify[AufsCtlRduV_SZ], -+ rdu->blk, -+ rdu->rent, rdu->shwh, rdu->full, -+ rdu->cookie.h_pos, rdu->cookie.bindex, rdu->cookie.flags, -+ rdu->cookie.generation); -+ -+ if (rdu->verify[AufsCtlRduV_SZ] == sizeof(*rdu)) -+ return 0; -+ -+ AuDbg("%u:%u\n", -+ rdu->verify[AufsCtlRduV_SZ], (unsigned int)sizeof(*rdu)); -+ return -EINVAL; -+} -+ -+long au_rdu_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -+{ -+ long err, e; -+ struct aufs_rdu rdu; -+ void __user *p = (void __user *)arg; -+ -+ err = copy_from_user(&rdu, p, sizeof(rdu)); -+ if (unlikely(err)) { -+ err = -EFAULT; -+ AuTraceErr(err); -+ goto out; -+ } -+ err = au_rdu_verify(&rdu); -+ if (unlikely(err)) -+ goto out; -+ -+ switch (cmd) { -+ case AUFS_CTL_RDU: -+ err = au_rdu(file, &rdu); -+ if (unlikely(err)) -+ break; -+ -+ e = copy_to_user(p, &rdu, sizeof(rdu)); -+ if (unlikely(e)) { -+ err = -EFAULT; -+ AuTraceErr(err); -+ } -+ break; -+ case AUFS_CTL_RDU_INO: -+ err = au_rdu_ino(file, &rdu); -+ break; -+ -+ default: -+ /* err = -ENOTTY; */ -+ err = -EINVAL; -+ } -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+#ifdef CONFIG_COMPAT -+long au_rdu_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -+{ -+ long err, e; -+ struct aufs_rdu rdu; -+ void __user *p = compat_ptr(arg); -+ -+ /* todo: get_user()? */ -+ err = copy_from_user(&rdu, p, sizeof(rdu)); -+ if (unlikely(err)) { -+ err = -EFAULT; -+ AuTraceErr(err); -+ goto out; -+ } -+ rdu.ent.e = compat_ptr(rdu.ent.ul); -+ err = au_rdu_verify(&rdu); -+ if (unlikely(err)) -+ goto out; -+ -+ switch (cmd) { -+ case AUFS_CTL_RDU: -+ err = au_rdu(file, &rdu); -+ if (unlikely(err)) -+ break; -+ -+ rdu.ent.ul = ptr_to_compat(rdu.ent.e); -+ rdu.tail.ul = ptr_to_compat(rdu.tail.e); -+ e = copy_to_user(p, &rdu, sizeof(rdu)); -+ if (unlikely(e)) { -+ err = -EFAULT; -+ AuTraceErr(err); -+ } -+ break; -+ case AUFS_CTL_RDU_INO: -+ err = au_rdu_ino(file, &rdu); -+ break; -+ -+ default: -+ /* err = -ENOTTY; */ -+ err = -EINVAL; -+ } -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+#endif -diff --git a/fs/aufs/rwsem.h b/fs/aufs/rwsem.h -new file mode 100644 -index 0000000..09ed5a0 ---- /dev/null -+++ b/fs/aufs/rwsem.h -@@ -0,0 +1,191 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * simple read-write semaphore wrappers -+ */ -+ -+#ifndef __AUFS_RWSEM_H__ -+#define __AUFS_RWSEM_H__ -+ -+#ifdef __KERNEL__ -+ -+#include "debug.h" -+ -+struct au_rwsem { -+ struct rw_semaphore rwsem; -+#ifdef CONFIG_AUFS_DEBUG -+ /* just for debugging, not almighty counter */ -+ atomic_t rcnt, wcnt; -+#endif -+}; -+ -+#ifdef CONFIG_AUFS_DEBUG -+#define AuDbgCntInit(rw) do { \ -+ atomic_set(&(rw)->rcnt, 0); \ -+ atomic_set(&(rw)->wcnt, 0); \ -+ smp_mb(); /* atomic set */ \ -+} while (0) -+ -+#define AuDbgRcntInc(rw) atomic_inc(&(rw)->rcnt) -+#define AuDbgRcntDec(rw) WARN_ON(atomic_dec_return(&(rw)->rcnt) < 0) -+#define AuDbgWcntInc(rw) atomic_inc(&(rw)->wcnt) -+#define AuDbgWcntDec(rw) WARN_ON(atomic_dec_return(&(rw)->wcnt) < 0) -+#else -+#define AuDbgCntInit(rw) do {} while (0) -+#define AuDbgRcntInc(rw) do {} while (0) -+#define AuDbgRcntDec(rw) do {} while (0) -+#define AuDbgWcntInc(rw) do {} while (0) -+#define AuDbgWcntDec(rw) do {} while (0) -+#endif /* CONFIG_AUFS_DEBUG */ -+ -+/* to debug easier, do not make them inlined functions */ -+#define AuRwMustNoWaiters(rw) AuDebugOn(!list_empty(&(rw)->rwsem.wait_list)) -+/* rwsem_is_locked() is unusable */ -+#define AuRwMustReadLock(rw) AuDebugOn(atomic_read(&(rw)->rcnt) <= 0) -+#define AuRwMustWriteLock(rw) AuDebugOn(atomic_read(&(rw)->wcnt) <= 0) -+#define AuRwMustAnyLock(rw) AuDebugOn(atomic_read(&(rw)->rcnt) <= 0 \ -+ && atomic_read(&(rw)->wcnt) <= 0) -+#define AuRwDestroy(rw) AuDebugOn(atomic_read(&(rw)->rcnt) \ -+ || atomic_read(&(rw)->wcnt)) -+ -+#define au_rw_class(rw, key) lockdep_set_class(&(rw)->rwsem, key) -+ -+static inline void au_rw_init(struct au_rwsem *rw) -+{ -+ AuDbgCntInit(rw); -+ init_rwsem(&rw->rwsem); -+} -+ -+static inline void au_rw_init_wlock(struct au_rwsem *rw) -+{ -+ au_rw_init(rw); -+ down_write(&rw->rwsem); -+ AuDbgWcntInc(rw); -+} -+ -+static inline void au_rw_init_wlock_nested(struct au_rwsem *rw, -+ unsigned int lsc) -+{ -+ au_rw_init(rw); -+ down_write_nested(&rw->rwsem, lsc); -+ AuDbgWcntInc(rw); -+} -+ -+static inline void au_rw_read_lock(struct au_rwsem *rw) -+{ -+ down_read(&rw->rwsem); -+ AuDbgRcntInc(rw); -+} -+ -+static inline void au_rw_read_lock_nested(struct au_rwsem *rw, unsigned int lsc) -+{ -+ down_read_nested(&rw->rwsem, lsc); -+ AuDbgRcntInc(rw); -+} -+ -+static inline void au_rw_read_unlock(struct au_rwsem *rw) -+{ -+ AuRwMustReadLock(rw); -+ AuDbgRcntDec(rw); -+ up_read(&rw->rwsem); -+} -+ -+static inline void au_rw_dgrade_lock(struct au_rwsem *rw) -+{ -+ AuRwMustWriteLock(rw); -+ AuDbgRcntInc(rw); -+ AuDbgWcntDec(rw); -+ downgrade_write(&rw->rwsem); -+} -+ -+static inline void au_rw_write_lock(struct au_rwsem *rw) -+{ -+ down_write(&rw->rwsem); -+ AuDbgWcntInc(rw); -+} -+ -+static inline void au_rw_write_lock_nested(struct au_rwsem *rw, -+ unsigned int lsc) -+{ -+ down_write_nested(&rw->rwsem, lsc); -+ AuDbgWcntInc(rw); -+} -+ -+static inline void au_rw_write_unlock(struct au_rwsem *rw) -+{ -+ AuRwMustWriteLock(rw); -+ AuDbgWcntDec(rw); -+ up_write(&rw->rwsem); -+} -+ -+/* why is not _nested version defined */ -+static inline int au_rw_read_trylock(struct au_rwsem *rw) -+{ -+ int ret; -+ -+ ret = down_read_trylock(&rw->rwsem); -+ if (ret) -+ AuDbgRcntInc(rw); -+ return ret; -+} -+ -+static inline int au_rw_write_trylock(struct au_rwsem *rw) -+{ -+ int ret; -+ -+ ret = down_write_trylock(&rw->rwsem); -+ if (ret) -+ AuDbgWcntInc(rw); -+ return ret; -+} -+ -+#undef AuDbgCntInit -+#undef AuDbgRcntInc -+#undef AuDbgRcntDec -+#undef AuDbgWcntInc -+#undef AuDbgWcntDec -+ -+#define AuSimpleLockRwsemFuncs(prefix, param, rwsem) \ -+static inline void prefix##_read_lock(param) \ -+{ au_rw_read_lock(rwsem); } \ -+static inline void prefix##_write_lock(param) \ -+{ au_rw_write_lock(rwsem); } \ -+static inline int prefix##_read_trylock(param) \ -+{ return au_rw_read_trylock(rwsem); } \ -+static inline int prefix##_write_trylock(param) \ -+{ return au_rw_write_trylock(rwsem); } -+/* why is not _nested version defined */ -+/* static inline void prefix##_read_trylock_nested(param, lsc) -+{ au_rw_read_trylock_nested(rwsem, lsc)); } -+static inline void prefix##_write_trylock_nestd(param, lsc) -+{ au_rw_write_trylock_nested(rwsem, lsc); } */ -+ -+#define AuSimpleUnlockRwsemFuncs(prefix, param, rwsem) \ -+static inline void prefix##_read_unlock(param) \ -+{ au_rw_read_unlock(rwsem); } \ -+static inline void prefix##_write_unlock(param) \ -+{ au_rw_write_unlock(rwsem); } \ -+static inline void prefix##_downgrade_lock(param) \ -+{ au_rw_dgrade_lock(rwsem); } -+ -+#define AuSimpleRwsemFuncs(prefix, param, rwsem) \ -+ AuSimpleLockRwsemFuncs(prefix, param, rwsem) \ -+ AuSimpleUnlockRwsemFuncs(prefix, param, rwsem) -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_RWSEM_H__ */ -diff --git a/fs/aufs/sbinfo.c b/fs/aufs/sbinfo.c -new file mode 100644 -index 0000000..ff13c9f ---- /dev/null -+++ b/fs/aufs/sbinfo.c -@@ -0,0 +1,348 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * superblock private data -+ */ -+ -+#include "aufs.h" -+ -+/* -+ * they are necessary regardless sysfs is disabled. -+ */ -+void au_si_free(struct kobject *kobj) -+{ -+ int i; -+ struct au_sbinfo *sbinfo; -+ char *locked __maybe_unused; /* debug only */ -+ -+ sbinfo = container_of(kobj, struct au_sbinfo, si_kobj); -+ for (i = 0; i < AuPlink_NHASH; i++) -+ AuDebugOn(!hlist_empty(&sbinfo->si_plink[i].head)); -+ AuDebugOn(atomic_read(&sbinfo->si_nowait.nw_len)); -+ -+ au_rw_write_lock(&sbinfo->si_rwsem); -+ au_br_free(sbinfo); -+ au_rw_write_unlock(&sbinfo->si_rwsem); -+ -+ kfree(sbinfo->si_branch); -+ for (i = 0; i < AU_NPIDMAP; i++) -+ kfree(sbinfo->au_si_pid.pid_bitmap[i]); -+ mutex_destroy(&sbinfo->au_si_pid.pid_mtx); -+ mutex_destroy(&sbinfo->si_xib_mtx); -+ AuRwDestroy(&sbinfo->si_rwsem); -+ -+ kfree(sbinfo); -+} -+ -+int au_si_alloc(struct super_block *sb) -+{ -+ int err, i; -+ struct au_sbinfo *sbinfo; -+ static struct lock_class_key aufs_si; -+ -+ err = -ENOMEM; -+ sbinfo = kzalloc(sizeof(*sbinfo), GFP_NOFS); -+ if (unlikely(!sbinfo)) -+ goto out; -+ -+ /* will be reallocated separately */ -+ sbinfo->si_branch = kzalloc(sizeof(*sbinfo->si_branch), GFP_NOFS); -+ if (unlikely(!sbinfo->si_branch)) -+ goto out_sbinfo; -+ -+ err = sysaufs_si_init(sbinfo); -+ if (unlikely(err)) -+ goto out_br; -+ -+ au_nwt_init(&sbinfo->si_nowait); -+ au_rw_init_wlock(&sbinfo->si_rwsem); -+ au_rw_class(&sbinfo->si_rwsem, &aufs_si); -+ mutex_init(&sbinfo->au_si_pid.pid_mtx); -+ -+ atomic_long_set(&sbinfo->si_ninodes, 0); -+ atomic_long_set(&sbinfo->si_nfiles, 0); -+ -+ sbinfo->si_bend = -1; -+ sbinfo->si_last_br_id = AUFS_BRANCH_MAX / 2; -+ -+ sbinfo->si_wbr_copyup = AuWbrCopyup_Def; -+ sbinfo->si_wbr_create = AuWbrCreate_Def; -+ sbinfo->si_wbr_copyup_ops = au_wbr_copyup_ops + sbinfo->si_wbr_copyup; -+ sbinfo->si_wbr_create_ops = au_wbr_create_ops + sbinfo->si_wbr_create; -+ -+ au_fhsm_init(sbinfo); -+ -+ sbinfo->si_mntflags = au_opts_plink(AuOpt_Def); -+ -+ sbinfo->si_xino_jiffy = jiffies; -+ sbinfo->si_xino_expire -+ = msecs_to_jiffies(AUFS_XINO_DEF_SEC * MSEC_PER_SEC); -+ mutex_init(&sbinfo->si_xib_mtx); -+ sbinfo->si_xino_brid = -1; -+ /* leave si_xib_last_pindex and si_xib_next_bit */ -+ -+ au_sphl_init(&sbinfo->si_aopen); -+ -+ sbinfo->si_rdcache = msecs_to_jiffies(AUFS_RDCACHE_DEF * MSEC_PER_SEC); -+ sbinfo->si_rdblk = AUFS_RDBLK_DEF; -+ sbinfo->si_rdhash = AUFS_RDHASH_DEF; -+ sbinfo->si_dirwh = AUFS_DIRWH_DEF; -+ -+ for (i = 0; i < AuPlink_NHASH; i++) -+ au_sphl_init(sbinfo->si_plink + i); -+ init_waitqueue_head(&sbinfo->si_plink_wq); -+ spin_lock_init(&sbinfo->si_plink_maint_lock); -+ -+ au_sphl_init(&sbinfo->si_files); -+ -+ /* with getattr by default */ -+ sbinfo->si_iop_array = aufs_iop; -+ -+ /* leave other members for sysaufs and si_mnt. */ -+ sbinfo->si_sb = sb; -+ sb->s_fs_info = sbinfo; -+ si_pid_set(sb); -+ return 0; /* success */ -+ -+out_br: -+ kfree(sbinfo->si_branch); -+out_sbinfo: -+ kfree(sbinfo); -+out: -+ return err; -+} -+ -+int au_sbr_realloc(struct au_sbinfo *sbinfo, int nbr) -+{ -+ int err, sz; -+ struct au_branch **brp; -+ -+ AuRwMustWriteLock(&sbinfo->si_rwsem); -+ -+ err = -ENOMEM; -+ sz = sizeof(*brp) * (sbinfo->si_bend + 1); -+ if (unlikely(!sz)) -+ sz = sizeof(*brp); -+ brp = au_kzrealloc(sbinfo->si_branch, sz, sizeof(*brp) * nbr, GFP_NOFS); -+ if (brp) { -+ sbinfo->si_branch = brp; -+ err = 0; -+ } -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+unsigned int au_sigen_inc(struct super_block *sb) -+{ -+ unsigned int gen; -+ -+ SiMustWriteLock(sb); -+ -+ gen = ++au_sbi(sb)->si_generation; -+ au_update_digen(sb->s_root); -+ au_update_iigen(sb->s_root->d_inode, /*half*/0); -+ sb->s_root->d_inode->i_version++; -+ return gen; -+} -+ -+aufs_bindex_t au_new_br_id(struct super_block *sb) -+{ -+ aufs_bindex_t br_id; -+ int i; -+ struct au_sbinfo *sbinfo; -+ -+ SiMustWriteLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ for (i = 0; i <= AUFS_BRANCH_MAX; i++) { -+ br_id = ++sbinfo->si_last_br_id; -+ AuDebugOn(br_id < 0); -+ if (br_id && au_br_index(sb, br_id) < 0) -+ return br_id; -+ } -+ -+ return -1; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* it is ok that new 'nwt' tasks are appended while we are sleeping */ -+int si_read_lock(struct super_block *sb, int flags) -+{ -+ int err; -+ -+ err = 0; -+ if (au_ftest_lock(flags, FLUSH)) -+ au_nwt_flush(&au_sbi(sb)->si_nowait); -+ -+ si_noflush_read_lock(sb); -+ err = au_plink_maint(sb, flags); -+ if (unlikely(err)) -+ si_read_unlock(sb); -+ -+ return err; -+} -+ -+int si_write_lock(struct super_block *sb, int flags) -+{ -+ int err; -+ -+ if (au_ftest_lock(flags, FLUSH)) -+ au_nwt_flush(&au_sbi(sb)->si_nowait); -+ -+ si_noflush_write_lock(sb); -+ err = au_plink_maint(sb, flags); -+ if (unlikely(err)) -+ si_write_unlock(sb); -+ -+ return err; -+} -+ -+/* dentry and super_block lock. call at entry point */ -+int aufs_read_lock(struct dentry *dentry, int flags) -+{ -+ int err; -+ struct super_block *sb; -+ -+ sb = dentry->d_sb; -+ err = si_read_lock(sb, flags); -+ if (unlikely(err)) -+ goto out; -+ -+ if (au_ftest_lock(flags, DW)) -+ di_write_lock_child(dentry); -+ else -+ di_read_lock_child(dentry, flags); -+ -+ if (au_ftest_lock(flags, GEN)) { -+ err = au_digen_test(dentry, au_sigen(sb)); -+ if (!au_opt_test(au_mntflags(sb), UDBA_NONE)) -+ AuDebugOn(!err && au_dbrange_test(dentry)); -+ else if (!err) -+ err = au_dbrange_test(dentry); -+ if (unlikely(err)) -+ aufs_read_unlock(dentry, flags); -+ } -+ -+out: -+ return err; -+} -+ -+void aufs_read_unlock(struct dentry *dentry, int flags) -+{ -+ if (au_ftest_lock(flags, DW)) -+ di_write_unlock(dentry); -+ else -+ di_read_unlock(dentry, flags); -+ si_read_unlock(dentry->d_sb); -+} -+ -+void aufs_write_lock(struct dentry *dentry) -+{ -+ si_write_lock(dentry->d_sb, AuLock_FLUSH | AuLock_NOPLMW); -+ di_write_lock_child(dentry); -+} -+ -+void aufs_write_unlock(struct dentry *dentry) -+{ -+ di_write_unlock(dentry); -+ si_write_unlock(dentry->d_sb); -+} -+ -+int aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int flags) -+{ -+ int err; -+ unsigned int sigen; -+ struct super_block *sb; -+ -+ sb = d1->d_sb; -+ err = si_read_lock(sb, flags); -+ if (unlikely(err)) -+ goto out; -+ -+ di_write_lock2_child(d1, d2, au_ftest_lock(flags, DIRS)); -+ -+ if (au_ftest_lock(flags, GEN)) { -+ sigen = au_sigen(sb); -+ err = au_digen_test(d1, sigen); -+ AuDebugOn(!err && au_dbrange_test(d1)); -+ if (!err) { -+ err = au_digen_test(d2, sigen); -+ AuDebugOn(!err && au_dbrange_test(d2)); -+ } -+ if (unlikely(err)) -+ aufs_read_and_write_unlock2(d1, d2); -+ } -+ -+out: -+ return err; -+} -+ -+void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2) -+{ -+ di_write_unlock2(d1, d2); -+ si_read_unlock(d1->d_sb); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void si_pid_alloc(struct au_si_pid *au_si_pid, int idx) -+{ -+ unsigned long *p; -+ -+ BUILD_BUG_ON(sizeof(unsigned long) != -+ sizeof(*au_si_pid->pid_bitmap)); -+ -+ mutex_lock(&au_si_pid->pid_mtx); -+ p = au_si_pid->pid_bitmap[idx]; -+ while (!p) { -+ /* -+ * bad approach. -+ * but keeping 'si_pid_set()' void is more important. -+ */ -+ p = kcalloc(BITS_TO_LONGS(AU_PIDSTEP), -+ sizeof(*au_si_pid->pid_bitmap), -+ GFP_NOFS); -+ if (p) -+ break; -+ cond_resched(); -+ } -+ au_si_pid->pid_bitmap[idx] = p; -+ mutex_unlock(&au_si_pid->pid_mtx); -+} -+ -+void si_pid_set(struct super_block *sb) -+{ -+ pid_t bit; -+ int idx; -+ unsigned long *bitmap; -+ struct au_si_pid *au_si_pid; -+ -+ si_pid_idx_bit(&idx, &bit); -+ au_si_pid = &au_sbi(sb)->au_si_pid; -+ bitmap = au_si_pid->pid_bitmap[idx]; -+ if (!bitmap) { -+ si_pid_alloc(au_si_pid, idx); -+ bitmap = au_si_pid->pid_bitmap[idx]; -+ } -+ AuDebugOn(test_bit(bit, bitmap)); -+ set_bit(bit, bitmap); -+ /* smp_mb(); */ -+} -diff --git a/fs/aufs/spl.h b/fs/aufs/spl.h -new file mode 100644 -index 0000000..945343a ---- /dev/null -+++ b/fs/aufs/spl.h -@@ -0,0 +1,111 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * simple list protected by a spinlock -+ */ -+ -+#ifndef __AUFS_SPL_H__ -+#define __AUFS_SPL_H__ -+ -+#ifdef __KERNEL__ -+ -+struct au_splhead { -+ spinlock_t spin; -+ struct list_head head; -+}; -+ -+static inline void au_spl_init(struct au_splhead *spl) -+{ -+ spin_lock_init(&spl->spin); -+ INIT_LIST_HEAD(&spl->head); -+} -+ -+static inline void au_spl_add(struct list_head *list, struct au_splhead *spl) -+{ -+ spin_lock(&spl->spin); -+ list_add(list, &spl->head); -+ spin_unlock(&spl->spin); -+} -+ -+static inline void au_spl_del(struct list_head *list, struct au_splhead *spl) -+{ -+ spin_lock(&spl->spin); -+ list_del(list); -+ spin_unlock(&spl->spin); -+} -+ -+static inline void au_spl_del_rcu(struct list_head *list, -+ struct au_splhead *spl) -+{ -+ spin_lock(&spl->spin); -+ list_del_rcu(list); -+ spin_unlock(&spl->spin); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct au_sphlhead { -+ spinlock_t spin; -+ struct hlist_head head; -+}; -+ -+static inline void au_sphl_init(struct au_sphlhead *sphl) -+{ -+ spin_lock_init(&sphl->spin); -+ INIT_HLIST_HEAD(&sphl->head); -+} -+ -+static inline void au_sphl_add(struct hlist_node *hlist, -+ struct au_sphlhead *sphl) -+{ -+ spin_lock(&sphl->spin); -+ hlist_add_head(hlist, &sphl->head); -+ spin_unlock(&sphl->spin); -+} -+ -+static inline void au_sphl_del(struct hlist_node *hlist, -+ struct au_sphlhead *sphl) -+{ -+ spin_lock(&sphl->spin); -+ hlist_del(hlist); -+ spin_unlock(&sphl->spin); -+} -+ -+static inline void au_sphl_del_rcu(struct hlist_node *hlist, -+ struct au_sphlhead *sphl) -+{ -+ spin_lock(&sphl->spin); -+ hlist_del_rcu(hlist); -+ spin_unlock(&sphl->spin); -+} -+ -+static inline unsigned long au_sphl_count(struct au_sphlhead *sphl) -+{ -+ unsigned long cnt; -+ struct hlist_node *pos; -+ -+ cnt = 0; -+ spin_lock(&sphl->spin); -+ hlist_for_each(pos, &sphl->head) -+ cnt++; -+ spin_unlock(&sphl->spin); -+ return cnt; -+} -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_SPL_H__ */ -diff --git a/fs/aufs/super.c b/fs/aufs/super.c -new file mode 100644 -index 0000000..64a6bb4 ---- /dev/null -+++ b/fs/aufs/super.c -@@ -0,0 +1,1041 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * mount and super_block operations -+ */ -+ -+#include -+#include -+#include -+#include -+#include "aufs.h" -+ -+/* -+ * super_operations -+ */ -+static struct inode *aufs_alloc_inode(struct super_block *sb __maybe_unused) -+{ -+ struct au_icntnr *c; -+ -+ c = au_cache_alloc_icntnr(); -+ if (c) { -+ au_icntnr_init(c); -+ c->vfs_inode.i_version = 1; /* sigen(sb); */ -+ c->iinfo.ii_hinode = NULL; -+ return &c->vfs_inode; -+ } -+ return NULL; -+} -+ -+static void aufs_destroy_inode_cb(struct rcu_head *head) -+{ -+ struct inode *inode = container_of(head, struct inode, i_rcu); -+ -+ INIT_HLIST_HEAD(&inode->i_dentry); -+ au_cache_free_icntnr(container_of(inode, struct au_icntnr, vfs_inode)); -+} -+ -+static void aufs_destroy_inode(struct inode *inode) -+{ -+ au_iinfo_fin(inode); -+ call_rcu(&inode->i_rcu, aufs_destroy_inode_cb); -+} -+ -+struct inode *au_iget_locked(struct super_block *sb, ino_t ino) -+{ -+ struct inode *inode; -+ int err; -+ -+ inode = iget_locked(sb, ino); -+ if (unlikely(!inode)) { -+ inode = ERR_PTR(-ENOMEM); -+ goto out; -+ } -+ if (!(inode->i_state & I_NEW)) -+ goto out; -+ -+ err = au_xigen_new(inode); -+ if (!err) -+ err = au_iinfo_init(inode); -+ if (!err) -+ inode->i_version++; -+ else { -+ iget_failed(inode); -+ inode = ERR_PTR(err); -+ } -+ -+out: -+ /* never return NULL */ -+ AuDebugOn(!inode); -+ AuTraceErrPtr(inode); -+ return inode; -+} -+ -+/* lock free root dinfo */ -+static int au_show_brs(struct seq_file *seq, struct super_block *sb) -+{ -+ int err; -+ aufs_bindex_t bindex, bend; -+ struct path path; -+ struct au_hdentry *hdp; -+ struct au_branch *br; -+ au_br_perm_str_t perm; -+ -+ err = 0; -+ bend = au_sbend(sb); -+ hdp = au_di(sb->s_root)->di_hdentry; -+ for (bindex = 0; !err && bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ path.mnt = au_br_mnt(br); -+ path.dentry = hdp[bindex].hd_dentry; -+ err = au_seq_path(seq, &path); -+ if (!err) { -+ au_optstr_br_perm(&perm, br->br_perm); -+ err = seq_printf(seq, "=%s", perm.a); -+ if (err == -1) -+ err = -E2BIG; -+ } -+ if (!err && bindex != bend) -+ err = seq_putc(seq, ':'); -+ } -+ -+ return err; -+} -+ -+static void au_show_wbr_create(struct seq_file *m, int v, -+ struct au_sbinfo *sbinfo) -+{ -+ const char *pat; -+ -+ AuRwMustAnyLock(&sbinfo->si_rwsem); -+ -+ seq_puts(m, ",create="); -+ pat = au_optstr_wbr_create(v); -+ switch (v) { -+ case AuWbrCreate_TDP: -+ case AuWbrCreate_RR: -+ case AuWbrCreate_MFS: -+ case AuWbrCreate_PMFS: -+ seq_puts(m, pat); -+ break; -+ case AuWbrCreate_MFSV: -+ seq_printf(m, /*pat*/"mfs:%lu", -+ jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire) -+ / MSEC_PER_SEC); -+ break; -+ case AuWbrCreate_PMFSV: -+ seq_printf(m, /*pat*/"pmfs:%lu", -+ jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire) -+ / MSEC_PER_SEC); -+ break; -+ case AuWbrCreate_MFSRR: -+ seq_printf(m, /*pat*/"mfsrr:%llu", -+ sbinfo->si_wbr_mfs.mfsrr_watermark); -+ break; -+ case AuWbrCreate_MFSRRV: -+ seq_printf(m, /*pat*/"mfsrr:%llu:%lu", -+ sbinfo->si_wbr_mfs.mfsrr_watermark, -+ jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire) -+ / MSEC_PER_SEC); -+ break; -+ case AuWbrCreate_PMFSRR: -+ seq_printf(m, /*pat*/"pmfsrr:%llu", -+ sbinfo->si_wbr_mfs.mfsrr_watermark); -+ break; -+ case AuWbrCreate_PMFSRRV: -+ seq_printf(m, /*pat*/"pmfsrr:%llu:%lu", -+ sbinfo->si_wbr_mfs.mfsrr_watermark, -+ jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire) -+ / MSEC_PER_SEC); -+ break; -+ } -+} -+ -+static int au_show_xino(struct seq_file *seq, struct super_block *sb) -+{ -+#ifdef CONFIG_SYSFS -+ return 0; -+#else -+ int err; -+ const int len = sizeof(AUFS_XINO_FNAME) - 1; -+ aufs_bindex_t bindex, brid; -+ struct qstr *name; -+ struct file *f; -+ struct dentry *d, *h_root; -+ struct au_hdentry *hdp; -+ -+ AuRwMustAnyLock(&sbinfo->si_rwsem); -+ -+ err = 0; -+ f = au_sbi(sb)->si_xib; -+ if (!f) -+ goto out; -+ -+ /* stop printing the default xino path on the first writable branch */ -+ h_root = NULL; -+ brid = au_xino_brid(sb); -+ if (brid >= 0) { -+ bindex = au_br_index(sb, brid); -+ hdp = au_di(sb->s_root)->di_hdentry; -+ h_root = hdp[0 + bindex].hd_dentry; -+ } -+ d = f->f_dentry; -+ name = &d->d_name; -+ /* safe ->d_parent because the file is unlinked */ -+ if (d->d_parent == h_root -+ && name->len == len -+ && !memcmp(name->name, AUFS_XINO_FNAME, len)) -+ goto out; -+ -+ seq_puts(seq, ",xino="); -+ err = au_xino_path(seq, f); -+ -+out: -+ return err; -+#endif -+} -+ -+/* seq_file will re-call me in case of too long string */ -+static int aufs_show_options(struct seq_file *m, struct dentry *dentry) -+{ -+ int err; -+ unsigned int mnt_flags, v; -+ struct super_block *sb; -+ struct au_sbinfo *sbinfo; -+ -+#define AuBool(name, str) do { \ -+ v = au_opt_test(mnt_flags, name); \ -+ if (v != au_opt_test(AuOpt_Def, name)) \ -+ seq_printf(m, ",%s" #str, v ? "" : "no"); \ -+} while (0) -+ -+#define AuStr(name, str) do { \ -+ v = mnt_flags & AuOptMask_##name; \ -+ if (v != (AuOpt_Def & AuOptMask_##name)) \ -+ seq_printf(m, "," #str "=%s", au_optstr_##str(v)); \ -+} while (0) -+ -+#define AuUInt(name, str, val) do { \ -+ if (val != AUFS_##name##_DEF) \ -+ seq_printf(m, "," #str "=%u", val); \ -+} while (0) -+ -+ sb = dentry->d_sb; -+ if (sb->s_flags & MS_POSIXACL) -+ seq_puts(m, ",acl"); -+ -+ /* lock free root dinfo */ -+ si_noflush_read_lock(sb); -+ sbinfo = au_sbi(sb); -+ seq_printf(m, ",si=%lx", sysaufs_si_id(sbinfo)); -+ -+ mnt_flags = au_mntflags(sb); -+ if (au_opt_test(mnt_flags, XINO)) { -+ err = au_show_xino(m, sb); -+ if (unlikely(err)) -+ goto out; -+ } else -+ seq_puts(m, ",noxino"); -+ -+ AuBool(TRUNC_XINO, trunc_xino); -+ AuStr(UDBA, udba); -+ AuBool(SHWH, shwh); -+ AuBool(PLINK, plink); -+ AuBool(DIO, dio); -+ AuBool(DIRPERM1, dirperm1); -+ /* AuBool(REFROF, refrof); */ -+ -+ v = sbinfo->si_wbr_create; -+ if (v != AuWbrCreate_Def) -+ au_show_wbr_create(m, v, sbinfo); -+ -+ v = sbinfo->si_wbr_copyup; -+ if (v != AuWbrCopyup_Def) -+ seq_printf(m, ",cpup=%s", au_optstr_wbr_copyup(v)); -+ -+ v = au_opt_test(mnt_flags, ALWAYS_DIROPQ); -+ if (v != au_opt_test(AuOpt_Def, ALWAYS_DIROPQ)) -+ seq_printf(m, ",diropq=%c", v ? 'a' : 'w'); -+ -+ AuUInt(DIRWH, dirwh, sbinfo->si_dirwh); -+ -+ v = jiffies_to_msecs(sbinfo->si_rdcache) / MSEC_PER_SEC; -+ AuUInt(RDCACHE, rdcache, v); -+ -+ AuUInt(RDBLK, rdblk, sbinfo->si_rdblk); -+ AuUInt(RDHASH, rdhash, sbinfo->si_rdhash); -+ -+ au_fhsm_show(m, sbinfo); -+ -+ AuBool(SUM, sum); -+ /* AuBool(SUM_W, wsum); */ -+ AuBool(WARN_PERM, warn_perm); -+ AuBool(VERBOSE, verbose); -+ -+out: -+ /* be sure to print "br:" last */ -+ if (!sysaufs_brs) { -+ seq_puts(m, ",br:"); -+ au_show_brs(m, sb); -+ } -+ si_read_unlock(sb); -+ return 0; -+ -+#undef AuBool -+#undef AuStr -+#undef AuUInt -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* sum mode which returns the summation for statfs(2) */ -+ -+static u64 au_add_till_max(u64 a, u64 b) -+{ -+ u64 old; -+ -+ old = a; -+ a += b; -+ if (old <= a) -+ return a; -+ return ULLONG_MAX; -+} -+ -+static u64 au_mul_till_max(u64 a, long mul) -+{ -+ u64 old; -+ -+ old = a; -+ a *= mul; -+ if (old <= a) -+ return a; -+ return ULLONG_MAX; -+} -+ -+static int au_statfs_sum(struct super_block *sb, struct kstatfs *buf) -+{ -+ int err; -+ long bsize, factor; -+ u64 blocks, bfree, bavail, files, ffree; -+ aufs_bindex_t bend, bindex, i; -+ unsigned char shared; -+ struct path h_path; -+ struct super_block *h_sb; -+ -+ err = 0; -+ bsize = LONG_MAX; -+ files = 0; -+ ffree = 0; -+ blocks = 0; -+ bfree = 0; -+ bavail = 0; -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { -+ h_path.mnt = au_sbr_mnt(sb, bindex); -+ h_sb = h_path.mnt->mnt_sb; -+ shared = 0; -+ for (i = 0; !shared && i < bindex; i++) -+ shared = (au_sbr_sb(sb, i) == h_sb); -+ if (shared) -+ continue; -+ -+ /* sb->s_root for NFS is unreliable */ -+ h_path.dentry = h_path.mnt->mnt_root; -+ err = vfs_statfs(&h_path, buf); -+ if (unlikely(err)) -+ goto out; -+ -+ if (bsize > buf->f_bsize) { -+ /* -+ * we will reduce bsize, so we have to expand blocks -+ * etc. to match them again -+ */ -+ factor = (bsize / buf->f_bsize); -+ blocks = au_mul_till_max(blocks, factor); -+ bfree = au_mul_till_max(bfree, factor); -+ bavail = au_mul_till_max(bavail, factor); -+ bsize = buf->f_bsize; -+ } -+ -+ factor = (buf->f_bsize / bsize); -+ blocks = au_add_till_max(blocks, -+ au_mul_till_max(buf->f_blocks, factor)); -+ bfree = au_add_till_max(bfree, -+ au_mul_till_max(buf->f_bfree, factor)); -+ bavail = au_add_till_max(bavail, -+ au_mul_till_max(buf->f_bavail, factor)); -+ files = au_add_till_max(files, buf->f_files); -+ ffree = au_add_till_max(ffree, buf->f_ffree); -+ } -+ -+ buf->f_bsize = bsize; -+ buf->f_blocks = blocks; -+ buf->f_bfree = bfree; -+ buf->f_bavail = bavail; -+ buf->f_files = files; -+ buf->f_ffree = ffree; -+ buf->f_frsize = 0; -+ -+out: -+ return err; -+} -+ -+static int aufs_statfs(struct dentry *dentry, struct kstatfs *buf) -+{ -+ int err; -+ struct path h_path; -+ struct super_block *sb; -+ -+ /* lock free root dinfo */ -+ sb = dentry->d_sb; -+ si_noflush_read_lock(sb); -+ if (!au_opt_test(au_mntflags(sb), SUM)) { -+ /* sb->s_root for NFS is unreliable */ -+ h_path.mnt = au_sbr_mnt(sb, 0); -+ h_path.dentry = h_path.mnt->mnt_root; -+ err = vfs_statfs(&h_path, buf); -+ } else -+ err = au_statfs_sum(sb, buf); -+ si_read_unlock(sb); -+ -+ if (!err) { -+ buf->f_type = AUFS_SUPER_MAGIC; -+ buf->f_namelen = AUFS_MAX_NAMELEN; -+ memset(&buf->f_fsid, 0, sizeof(buf->f_fsid)); -+ } -+ /* buf->f_bsize = buf->f_blocks = buf->f_bfree = buf->f_bavail = -1; */ -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int aufs_sync_fs(struct super_block *sb, int wait) -+{ -+ int err, e; -+ aufs_bindex_t bend, bindex; -+ struct au_branch *br; -+ struct super_block *h_sb; -+ -+ err = 0; -+ si_noflush_read_lock(sb); -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ if (!au_br_writable(br->br_perm)) -+ continue; -+ -+ h_sb = au_sbr_sb(sb, bindex); -+ if (h_sb->s_op->sync_fs) { -+ e = h_sb->s_op->sync_fs(h_sb, wait); -+ if (unlikely(e && !err)) -+ err = e; -+ /* go on even if an error happens */ -+ } -+ } -+ si_read_unlock(sb); -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* final actions when unmounting a file system */ -+static void aufs_put_super(struct super_block *sb) -+{ -+ struct au_sbinfo *sbinfo; -+ -+ sbinfo = au_sbi(sb); -+ if (!sbinfo) -+ return; -+ -+ dbgaufs_si_fin(sbinfo); -+ kobject_put(&sbinfo->si_kobj); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void *au_array_alloc(unsigned long long *hint, au_arraycb_t cb, void *arg) -+{ -+ void *array; -+ unsigned long long n, sz; -+ -+ array = NULL; -+ n = 0; -+ if (!*hint) -+ goto out; -+ -+ if (*hint > ULLONG_MAX / sizeof(array)) { -+ array = ERR_PTR(-EMFILE); -+ pr_err("hint %llu\n", *hint); -+ goto out; -+ } -+ -+ sz = sizeof(array) * *hint; -+ array = kzalloc(sz, GFP_NOFS); -+ if (unlikely(!array)) -+ array = vzalloc(sz); -+ if (unlikely(!array)) { -+ array = ERR_PTR(-ENOMEM); -+ goto out; -+ } -+ -+ n = cb(array, *hint, arg); -+ AuDebugOn(n > *hint); -+ -+out: -+ *hint = n; -+ return array; -+} -+ -+static unsigned long long au_iarray_cb(void *a, -+ unsigned long long max __maybe_unused, -+ void *arg) -+{ -+ unsigned long long n; -+ struct inode **p, *inode; -+ struct list_head *head; -+ -+ n = 0; -+ p = a; -+ head = arg; -+ spin_lock(&inode_sb_list_lock); -+ list_for_each_entry(inode, head, i_sb_list) { -+ if (!is_bad_inode(inode) -+ && au_ii(inode)->ii_bstart >= 0) { -+ spin_lock(&inode->i_lock); -+ if (atomic_read(&inode->i_count)) { -+ au_igrab(inode); -+ *p++ = inode; -+ n++; -+ AuDebugOn(n > max); -+ } -+ spin_unlock(&inode->i_lock); -+ } -+ } -+ spin_unlock(&inode_sb_list_lock); -+ -+ return n; -+} -+ -+struct inode **au_iarray_alloc(struct super_block *sb, unsigned long long *max) -+{ -+ *max = atomic_long_read(&au_sbi(sb)->si_ninodes); -+ return au_array_alloc(max, au_iarray_cb, &sb->s_inodes); -+} -+ -+void au_iarray_free(struct inode **a, unsigned long long max) -+{ -+ unsigned long long ull; -+ -+ for (ull = 0; ull < max; ull++) -+ iput(a[ull]); -+ kvfree(a); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * refresh dentry and inode at remount time. -+ */ -+/* todo: consolidate with simple_reval_dpath() and au_reval_for_attr() */ -+static int au_do_refresh(struct dentry *dentry, unsigned int dir_flags, -+ struct dentry *parent) -+{ -+ int err; -+ -+ di_write_lock_child(dentry); -+ di_read_lock_parent(parent, AuLock_IR); -+ err = au_refresh_dentry(dentry, parent); -+ if (!err && dir_flags) -+ au_hn_reset(dentry->d_inode, dir_flags); -+ di_read_unlock(parent, AuLock_IR); -+ di_write_unlock(dentry); -+ -+ return err; -+} -+ -+static int au_do_refresh_d(struct dentry *dentry, unsigned int sigen, -+ struct au_sbinfo *sbinfo, -+ const unsigned int dir_flags, unsigned int do_idop) -+{ -+ int err; -+ struct dentry *parent; -+ struct inode *inode; -+ -+ err = 0; -+ parent = dget_parent(dentry); -+ if (!au_digen_test(parent, sigen) && au_digen_test(dentry, sigen)) { -+ inode = dentry->d_inode; -+ if (inode) { -+ if (!S_ISDIR(inode->i_mode)) -+ err = au_do_refresh(dentry, /*dir_flags*/0, -+ parent); -+ else { -+ err = au_do_refresh(dentry, dir_flags, parent); -+ if (unlikely(err)) -+ au_fset_si(sbinfo, FAILED_REFRESH_DIR); -+ } -+ } else -+ err = au_do_refresh(dentry, /*dir_flags*/0, parent); -+ AuDbgDentry(dentry); -+ } -+ dput(parent); -+ -+ if (!err) { -+ if (do_idop) -+ au_refresh_dop(dentry, /*force_reval*/0); -+ } else -+ au_refresh_dop(dentry, /*force_reval*/1); -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+static int au_refresh_d(struct super_block *sb, unsigned int do_idop) -+{ -+ int err, i, j, ndentry, e; -+ unsigned int sigen; -+ struct au_dcsub_pages dpages; -+ struct au_dpage *dpage; -+ struct dentry **dentries, *d; -+ struct au_sbinfo *sbinfo; -+ struct dentry *root = sb->s_root; -+ const unsigned int dir_flags = au_hi_flags(root->d_inode, /*isdir*/1); -+ -+ if (do_idop) -+ au_refresh_dop(root, /*force_reval*/0); -+ -+ err = au_dpages_init(&dpages, GFP_NOFS); -+ if (unlikely(err)) -+ goto out; -+ err = au_dcsub_pages(&dpages, root, NULL, NULL); -+ if (unlikely(err)) -+ goto out_dpages; -+ -+ sigen = au_sigen(sb); -+ sbinfo = au_sbi(sb); -+ for (i = 0; i < dpages.ndpage; i++) { -+ dpage = dpages.dpages + i; -+ dentries = dpage->dentries; -+ ndentry = dpage->ndentry; -+ for (j = 0; j < ndentry; j++) { -+ d = dentries[j]; -+ e = au_do_refresh_d(d, sigen, sbinfo, dir_flags, -+ do_idop); -+ if (unlikely(e && !err)) -+ err = e; -+ /* go on even err */ -+ } -+ } -+ -+out_dpages: -+ au_dpages_free(&dpages); -+out: -+ return err; -+} -+ -+static int au_refresh_i(struct super_block *sb, unsigned int do_idop) -+{ -+ int err, e; -+ unsigned int sigen; -+ unsigned long long max, ull; -+ struct inode *inode, **array; -+ -+ array = au_iarray_alloc(sb, &max); -+ err = PTR_ERR(array); -+ if (IS_ERR(array)) -+ goto out; -+ -+ err = 0; -+ sigen = au_sigen(sb); -+ for (ull = 0; ull < max; ull++) { -+ inode = array[ull]; -+ if (unlikely(!inode)) -+ break; -+ -+ e = 0; -+ ii_write_lock_child(inode); -+ if (au_iigen(inode, NULL) != sigen) { -+ e = au_refresh_hinode_self(inode); -+ if (unlikely(e)) { -+ au_refresh_iop(inode, /*force_getattr*/1); -+ pr_err("error %d, i%lu\n", e, inode->i_ino); -+ if (!err) -+ err = e; -+ /* go on even if err */ -+ } -+ } -+ if (!e && do_idop) -+ au_refresh_iop(inode, /*force_getattr*/0); -+ ii_write_unlock(inode); -+ } -+ -+ au_iarray_free(array, max); -+ -+out: -+ return err; -+} -+ -+static void au_remount_refresh(struct super_block *sb, unsigned int do_idop) -+{ -+ int err, e; -+ unsigned int udba; -+ aufs_bindex_t bindex, bend; -+ struct dentry *root; -+ struct inode *inode; -+ struct au_branch *br; -+ struct au_sbinfo *sbi; -+ -+ au_sigen_inc(sb); -+ sbi = au_sbi(sb); -+ au_fclr_si(sbi, FAILED_REFRESH_DIR); -+ -+ root = sb->s_root; -+ DiMustNoWaiters(root); -+ inode = root->d_inode; -+ IiMustNoWaiters(inode); -+ -+ udba = au_opt_udba(sb); -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ err = au_hnotify_reset_br(udba, br, br->br_perm); -+ if (unlikely(err)) -+ AuIOErr("hnotify failed on br %d, %d, ignored\n", -+ bindex, err); -+ /* go on even if err */ -+ } -+ au_hn_reset(inode, au_hi_flags(inode, /*isdir*/1)); -+ -+ if (do_idop) { -+ if (au_ftest_si(sbi, NO_DREVAL)) { -+ AuDebugOn(sb->s_d_op == &aufs_dop_noreval); -+ sb->s_d_op = &aufs_dop_noreval; -+ AuDebugOn(sbi->si_iop_array == aufs_iop_nogetattr); -+ sbi->si_iop_array = aufs_iop_nogetattr; -+ } else { -+ AuDebugOn(sb->s_d_op == &aufs_dop); -+ sb->s_d_op = &aufs_dop; -+ AuDebugOn(sbi->si_iop_array == aufs_iop); -+ sbi->si_iop_array = aufs_iop; -+ } -+ pr_info("reset to %pf and %pf\n", -+ sb->s_d_op, sbi->si_iop_array); -+ } -+ -+ di_write_unlock(root); -+ err = au_refresh_d(sb, do_idop); -+ e = au_refresh_i(sb, do_idop); -+ if (unlikely(e && !err)) -+ err = e; -+ /* aufs_write_lock() calls ..._child() */ -+ di_write_lock_child(root); -+ -+ au_cpup_attr_all(inode, /*force*/1); -+ -+ if (unlikely(err)) -+ AuIOErr("refresh failed, ignored, %d\n", err); -+} -+ -+/* stop extra interpretation of errno in mount(8), and strange error messages */ -+static int cvt_err(int err) -+{ -+ AuTraceErr(err); -+ -+ switch (err) { -+ case -ENOENT: -+ case -ENOTDIR: -+ case -EEXIST: -+ case -EIO: -+ err = -EINVAL; -+ } -+ return err; -+} -+ -+static int aufs_remount_fs(struct super_block *sb, int *flags, char *data) -+{ -+ int err, do_dx; -+ unsigned int mntflags; -+ struct au_opts opts = { -+ .opt = NULL -+ }; -+ struct dentry *root; -+ struct inode *inode; -+ struct au_sbinfo *sbinfo; -+ -+ err = 0; -+ root = sb->s_root; -+ if (!data || !*data) { -+ err = si_write_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ if (!err) { -+ di_write_lock_child(root); -+ err = au_opts_verify(sb, *flags, /*pending*/0); -+ aufs_write_unlock(root); -+ } -+ goto out; -+ } -+ -+ err = -ENOMEM; -+ opts.opt = (void *)__get_free_page(GFP_NOFS); -+ if (unlikely(!opts.opt)) -+ goto out; -+ opts.max_opt = PAGE_SIZE / sizeof(*opts.opt); -+ opts.flags = AuOpts_REMOUNT; -+ opts.sb_flags = *flags; -+ -+ /* parse it before aufs lock */ -+ err = au_opts_parse(sb, data, &opts); -+ if (unlikely(err)) -+ goto out_opts; -+ -+ sbinfo = au_sbi(sb); -+ inode = root->d_inode; -+ mutex_lock(&inode->i_mutex); -+ err = si_write_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ if (unlikely(err)) -+ goto out_mtx; -+ di_write_lock_child(root); -+ -+ /* au_opts_remount() may return an error */ -+ err = au_opts_remount(sb, &opts); -+ au_opts_free(&opts); -+ -+ if (au_ftest_opts(opts.flags, REFRESH)) -+ au_remount_refresh(sb, au_ftest_opts(opts.flags, REFRESH_IDOP)); -+ -+ if (au_ftest_opts(opts.flags, REFRESH_DYAOP)) { -+ mntflags = au_mntflags(sb); -+ do_dx = !!au_opt_test(mntflags, DIO); -+ au_dy_arefresh(do_dx); -+ } -+ -+ au_fhsm_wrote_all(sb, /*force*/1); /* ?? */ -+ aufs_write_unlock(root); -+ -+out_mtx: -+ mutex_unlock(&inode->i_mutex); -+out_opts: -+ free_page((unsigned long)opts.opt); -+out: -+ err = cvt_err(err); -+ AuTraceErr(err); -+ return err; -+} -+ -+static const struct super_operations aufs_sop = { -+ .alloc_inode = aufs_alloc_inode, -+ .destroy_inode = aufs_destroy_inode, -+ /* always deleting, no clearing */ -+ .drop_inode = generic_delete_inode, -+ .show_options = aufs_show_options, -+ .statfs = aufs_statfs, -+ .put_super = aufs_put_super, -+ .sync_fs = aufs_sync_fs, -+ .remount_fs = aufs_remount_fs -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int alloc_root(struct super_block *sb) -+{ -+ int err; -+ struct inode *inode; -+ struct dentry *root; -+ -+ err = -ENOMEM; -+ inode = au_iget_locked(sb, AUFS_ROOT_INO); -+ err = PTR_ERR(inode); -+ if (IS_ERR(inode)) -+ goto out; -+ -+ inode->i_op = aufs_iop + AuIop_DIR; /* with getattr by default */ -+ inode->i_fop = &aufs_dir_fop; -+ inode->i_mode = S_IFDIR; -+ set_nlink(inode, 2); -+ unlock_new_inode(inode); -+ -+ root = d_make_root(inode); -+ if (unlikely(!root)) -+ goto out; -+ err = PTR_ERR(root); -+ if (IS_ERR(root)) -+ goto out; -+ -+ err = au_di_init(root); -+ if (!err) { -+ sb->s_root = root; -+ return 0; /* success */ -+ } -+ dput(root); -+ -+out: -+ return err; -+} -+ -+static int aufs_fill_super(struct super_block *sb, void *raw_data, -+ int silent __maybe_unused) -+{ -+ int err; -+ struct au_opts opts = { -+ .opt = NULL -+ }; -+ struct au_sbinfo *sbinfo; -+ struct dentry *root; -+ struct inode *inode; -+ char *arg = raw_data; -+ -+ if (unlikely(!arg || !*arg)) { -+ err = -EINVAL; -+ pr_err("no arg\n"); -+ goto out; -+ } -+ -+ err = -ENOMEM; -+ opts.opt = (void *)__get_free_page(GFP_NOFS); -+ if (unlikely(!opts.opt)) -+ goto out; -+ opts.max_opt = PAGE_SIZE / sizeof(*opts.opt); -+ opts.sb_flags = sb->s_flags; -+ -+ err = au_si_alloc(sb); -+ if (unlikely(err)) -+ goto out_opts; -+ sbinfo = au_sbi(sb); -+ -+ /* all timestamps always follow the ones on the branch */ -+ sb->s_flags |= MS_NOATIME | MS_NODIRATIME; -+ sb->s_op = &aufs_sop; -+ sb->s_d_op = &aufs_dop; -+ sb->s_magic = AUFS_SUPER_MAGIC; -+ sb->s_maxbytes = 0; -+ sb->s_stack_depth = 1; -+ au_export_init(sb); -+ /* au_xattr_init(sb); */ -+ -+ err = alloc_root(sb); -+ if (unlikely(err)) { -+ si_write_unlock(sb); -+ goto out_info; -+ } -+ root = sb->s_root; -+ inode = root->d_inode; -+ -+ /* -+ * actually we can parse options regardless aufs lock here. -+ * but at remount time, parsing must be done before aufs lock. -+ * so we follow the same rule. -+ */ -+ ii_write_lock_parent(inode); -+ aufs_write_unlock(root); -+ err = au_opts_parse(sb, arg, &opts); -+ if (unlikely(err)) -+ goto out_root; -+ -+ /* lock vfs_inode first, then aufs. */ -+ mutex_lock(&inode->i_mutex); -+ aufs_write_lock(root); -+ err = au_opts_mount(sb, &opts); -+ au_opts_free(&opts); -+ if (!err && au_ftest_si(sbinfo, NO_DREVAL)) { -+ sb->s_d_op = &aufs_dop_noreval; -+ pr_info("%pf\n", sb->s_d_op); -+ au_refresh_dop(root, /*force_reval*/0); -+ sbinfo->si_iop_array = aufs_iop_nogetattr; -+ au_refresh_iop(inode, /*force_getattr*/0); -+ } -+ aufs_write_unlock(root); -+ mutex_unlock(&inode->i_mutex); -+ if (!err) -+ goto out_opts; /* success */ -+ -+out_root: -+ dput(root); -+ sb->s_root = NULL; -+out_info: -+ dbgaufs_si_fin(sbinfo); -+ kobject_put(&sbinfo->si_kobj); -+ sb->s_fs_info = NULL; -+out_opts: -+ free_page((unsigned long)opts.opt); -+out: -+ AuTraceErr(err); -+ err = cvt_err(err); -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static struct dentry *aufs_mount(struct file_system_type *fs_type, int flags, -+ const char *dev_name __maybe_unused, -+ void *raw_data) -+{ -+ struct dentry *root; -+ struct super_block *sb; -+ -+ /* all timestamps always follow the ones on the branch */ -+ /* mnt->mnt_flags |= MNT_NOATIME | MNT_NODIRATIME; */ -+ root = mount_nodev(fs_type, flags, raw_data, aufs_fill_super); -+ if (IS_ERR(root)) -+ goto out; -+ -+ sb = root->d_sb; -+ si_write_lock(sb, !AuLock_FLUSH); -+ sysaufs_brs_add(sb, 0); -+ si_write_unlock(sb); -+ au_sbilist_add(sb); -+ -+out: -+ return root; -+} -+ -+static void aufs_kill_sb(struct super_block *sb) -+{ -+ struct au_sbinfo *sbinfo; -+ -+ sbinfo = au_sbi(sb); -+ if (sbinfo) { -+ au_sbilist_del(sb); -+ aufs_write_lock(sb->s_root); -+ au_fhsm_fin(sb); -+ if (sbinfo->si_wbr_create_ops->fin) -+ sbinfo->si_wbr_create_ops->fin(sb); -+ if (au_opt_test(sbinfo->si_mntflags, UDBA_HNOTIFY)) { -+ au_opt_set_udba(sbinfo->si_mntflags, UDBA_NONE); -+ au_remount_refresh(sb, /*do_idop*/0); -+ } -+ if (au_opt_test(sbinfo->si_mntflags, PLINK)) -+ au_plink_put(sb, /*verbose*/1); -+ au_xino_clr(sb); -+ sbinfo->si_sb = NULL; -+ aufs_write_unlock(sb->s_root); -+ au_nwt_flush(&sbinfo->si_nowait); -+ } -+ kill_anon_super(sb); -+} -+ -+struct file_system_type aufs_fs_type = { -+ .name = AUFS_FSTYPE, -+ /* a race between rename and others */ -+ .fs_flags = FS_RENAME_DOES_D_MOVE, -+ .mount = aufs_mount, -+ .kill_sb = aufs_kill_sb, -+ /* no need to __module_get() and module_put(). */ -+ .owner = THIS_MODULE, -+}; -diff --git a/fs/aufs/super.h b/fs/aufs/super.h -new file mode 100644 -index 0000000..ecd364b ---- /dev/null -+++ b/fs/aufs/super.h -@@ -0,0 +1,626 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * super_block operations -+ */ -+ -+#ifndef __AUFS_SUPER_H__ -+#define __AUFS_SUPER_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+#include "rwsem.h" -+#include "spl.h" -+#include "wkq.h" -+ -+typedef ssize_t (*au_readf_t)(struct file *, char __user *, size_t, loff_t *); -+typedef ssize_t (*au_writef_t)(struct file *, const char __user *, size_t, -+ loff_t *); -+ -+/* policies to select one among multiple writable branches */ -+struct au_wbr_copyup_operations { -+ int (*copyup)(struct dentry *dentry); -+}; -+ -+#define AuWbr_DIR 1 /* target is a dir */ -+#define AuWbr_PARENT (1 << 1) /* always require a parent */ -+ -+#define au_ftest_wbr(flags, name) ((flags) & AuWbr_##name) -+#define au_fset_wbr(flags, name) { (flags) |= AuWbr_##name; } -+#define au_fclr_wbr(flags, name) { (flags) &= ~AuWbr_##name; } -+ -+struct au_wbr_create_operations { -+ int (*create)(struct dentry *dentry, unsigned int flags); -+ int (*init)(struct super_block *sb); -+ int (*fin)(struct super_block *sb); -+}; -+ -+struct au_wbr_mfs { -+ struct mutex mfs_lock; /* protect this structure */ -+ unsigned long mfs_jiffy; -+ unsigned long mfs_expire; -+ aufs_bindex_t mfs_bindex; -+ -+ unsigned long long mfsrr_bytes; -+ unsigned long long mfsrr_watermark; -+}; -+ -+#define AuPlink_NHASH 100 -+static inline int au_plink_hash(ino_t ino) -+{ -+ return ino % AuPlink_NHASH; -+} -+ -+/* File-based Hierarchical Storage Management */ -+struct au_fhsm { -+#ifdef CONFIG_AUFS_FHSM -+ /* allow only one process who can receive the notification */ -+ spinlock_t fhsm_spin; -+ pid_t fhsm_pid; -+ wait_queue_head_t fhsm_wqh; -+ atomic_t fhsm_readable; -+ -+ /* these are protected by si_rwsem */ -+ unsigned long fhsm_expire; -+ aufs_bindex_t fhsm_bottom; -+#endif -+}; -+ -+#define AU_PIDSTEP (int)(BITS_TO_LONGS(PID_MAX_DEFAULT) * BITS_PER_LONG) -+#define AU_NPIDMAP (int)DIV_ROUND_UP(PID_MAX_LIMIT, AU_PIDSTEP) -+struct au_si_pid { -+ unsigned long *pid_bitmap[AU_NPIDMAP]; -+ struct mutex pid_mtx; -+}; -+ -+struct au_branch; -+struct au_sbinfo { -+ /* nowait tasks in the system-wide workqueue */ -+ struct au_nowait_tasks si_nowait; -+ -+ /* -+ * tried sb->s_umount, but failed due to the dependecy between i_mutex. -+ * rwsem for au_sbinfo is necessary. -+ */ -+ struct au_rwsem si_rwsem; -+ -+ /* prevent recursive locking in deleting inode */ -+ struct au_si_pid au_si_pid; -+ -+ /* -+ * dirty approach to protect sb->sb_inodes and ->s_files (gone) from -+ * remount. -+ */ -+ atomic_long_t si_ninodes, si_nfiles; -+ -+ /* branch management */ -+ unsigned int si_generation; -+ -+ /* see AuSi_ flags */ -+ unsigned char au_si_status; -+ -+ aufs_bindex_t si_bend; -+ -+ /* dirty trick to keep br_id plus */ -+ unsigned int si_last_br_id : -+ sizeof(aufs_bindex_t) * BITS_PER_BYTE - 1; -+ struct au_branch **si_branch; -+ -+ /* policy to select a writable branch */ -+ unsigned char si_wbr_copyup; -+ unsigned char si_wbr_create; -+ struct au_wbr_copyup_operations *si_wbr_copyup_ops; -+ struct au_wbr_create_operations *si_wbr_create_ops; -+ -+ /* round robin */ -+ atomic_t si_wbr_rr_next; -+ -+ /* most free space */ -+ struct au_wbr_mfs si_wbr_mfs; -+ -+ /* File-based Hierarchical Storage Management */ -+ struct au_fhsm si_fhsm; -+ -+ /* mount flags */ -+ /* include/asm-ia64/siginfo.h defines a macro named si_flags */ -+ unsigned int si_mntflags; -+ -+ /* external inode number (bitmap and translation table) */ -+ au_readf_t si_xread; -+ au_writef_t si_xwrite; -+ struct file *si_xib; -+ struct mutex si_xib_mtx; /* protect xib members */ -+ unsigned long *si_xib_buf; -+ unsigned long si_xib_last_pindex; -+ int si_xib_next_bit; -+ aufs_bindex_t si_xino_brid; -+ unsigned long si_xino_jiffy; -+ unsigned long si_xino_expire; -+ /* reserved for future use */ -+ /* unsigned long long si_xib_limit; */ /* Max xib file size */ -+ -+#ifdef CONFIG_AUFS_EXPORT -+ /* i_generation */ -+ struct file *si_xigen; -+ atomic_t si_xigen_next; -+#endif -+ -+ /* dirty trick to suppoer atomic_open */ -+ struct au_sphlhead si_aopen; -+ -+ /* vdir parameters */ -+ unsigned long si_rdcache; /* max cache time in jiffies */ -+ unsigned int si_rdblk; /* deblk size */ -+ unsigned int si_rdhash; /* hash size */ -+ -+ /* -+ * If the number of whiteouts are larger than si_dirwh, leave all of -+ * them after au_whtmp_ren to reduce the cost of rmdir(2). -+ * future fsck.aufs or kernel thread will remove them later. -+ * Otherwise, remove all whiteouts and the dir in rmdir(2). -+ */ -+ unsigned int si_dirwh; -+ -+ /* pseudo_link list */ -+ struct au_sphlhead si_plink[AuPlink_NHASH]; -+ wait_queue_head_t si_plink_wq; -+ spinlock_t si_plink_maint_lock; -+ pid_t si_plink_maint_pid; -+ -+ /* file list */ -+ struct au_sphlhead si_files; -+ -+ /* with/without getattr, brother of sb->s_d_op */ -+ struct inode_operations *si_iop_array; -+ -+ /* -+ * sysfs and lifetime management. -+ * this is not a small structure and it may be a waste of memory in case -+ * of sysfs is disabled, particulary when many aufs-es are mounted. -+ * but using sysfs is majority. -+ */ -+ struct kobject si_kobj; -+#ifdef CONFIG_DEBUG_FS -+ struct dentry *si_dbgaufs; -+ struct dentry *si_dbgaufs_plink; -+ struct dentry *si_dbgaufs_xib; -+#ifdef CONFIG_AUFS_EXPORT -+ struct dentry *si_dbgaufs_xigen; -+#endif -+#endif -+ -+#ifdef CONFIG_AUFS_SBILIST -+ struct hlist_node si_list; -+#endif -+ -+ /* dirty, necessary for unmounting, sysfs and sysrq */ -+ struct super_block *si_sb; -+}; -+ -+/* sbinfo status flags */ -+/* -+ * set true when refresh_dirs() failed at remount time. -+ * then try refreshing dirs at access time again. -+ * if it is false, refreshing dirs at access time is unnecesary -+ */ -+#define AuSi_FAILED_REFRESH_DIR 1 -+#define AuSi_FHSM (1 << 1) /* fhsm is active now */ -+#define AuSi_NO_DREVAL (1 << 2) /* disable all d_revalidate */ -+ -+#ifndef CONFIG_AUFS_FHSM -+#undef AuSi_FHSM -+#define AuSi_FHSM 0 -+#endif -+ -+static inline unsigned char au_do_ftest_si(struct au_sbinfo *sbi, -+ unsigned int flag) -+{ -+ AuRwMustAnyLock(&sbi->si_rwsem); -+ return sbi->au_si_status & flag; -+} -+#define au_ftest_si(sbinfo, name) au_do_ftest_si(sbinfo, AuSi_##name) -+#define au_fset_si(sbinfo, name) do { \ -+ AuRwMustWriteLock(&(sbinfo)->si_rwsem); \ -+ (sbinfo)->au_si_status |= AuSi_##name; \ -+} while (0) -+#define au_fclr_si(sbinfo, name) do { \ -+ AuRwMustWriteLock(&(sbinfo)->si_rwsem); \ -+ (sbinfo)->au_si_status &= ~AuSi_##name; \ -+} while (0) -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* policy to select one among writable branches */ -+#define AuWbrCopyup(sbinfo, ...) \ -+ ((sbinfo)->si_wbr_copyup_ops->copyup(__VA_ARGS__)) -+#define AuWbrCreate(sbinfo, ...) \ -+ ((sbinfo)->si_wbr_create_ops->create(__VA_ARGS__)) -+ -+/* flags for si_read_lock()/aufs_read_lock()/di_read_lock() */ -+#define AuLock_DW 1 /* write-lock dentry */ -+#define AuLock_IR (1 << 1) /* read-lock inode */ -+#define AuLock_IW (1 << 2) /* write-lock inode */ -+#define AuLock_FLUSH (1 << 3) /* wait for 'nowait' tasks */ -+#define AuLock_DIRS (1 << 4) /* target is a pair of dirs */ -+#define AuLock_NOPLM (1 << 5) /* return err in plm mode */ -+#define AuLock_NOPLMW (1 << 6) /* wait for plm mode ends */ -+#define AuLock_GEN (1 << 7) /* test digen/iigen */ -+#define au_ftest_lock(flags, name) ((flags) & AuLock_##name) -+#define au_fset_lock(flags, name) \ -+ do { (flags) |= AuLock_##name; } while (0) -+#define au_fclr_lock(flags, name) \ -+ do { (flags) &= ~AuLock_##name; } while (0) -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* super.c */ -+extern struct file_system_type aufs_fs_type; -+struct inode *au_iget_locked(struct super_block *sb, ino_t ino); -+typedef unsigned long long (*au_arraycb_t)(void *array, unsigned long long max, -+ void *arg); -+void *au_array_alloc(unsigned long long *hint, au_arraycb_t cb, void *arg); -+struct inode **au_iarray_alloc(struct super_block *sb, unsigned long long *max); -+void au_iarray_free(struct inode **a, unsigned long long max); -+ -+/* sbinfo.c */ -+void au_si_free(struct kobject *kobj); -+int au_si_alloc(struct super_block *sb); -+int au_sbr_realloc(struct au_sbinfo *sbinfo, int nbr); -+ -+unsigned int au_sigen_inc(struct super_block *sb); -+aufs_bindex_t au_new_br_id(struct super_block *sb); -+ -+int si_read_lock(struct super_block *sb, int flags); -+int si_write_lock(struct super_block *sb, int flags); -+int aufs_read_lock(struct dentry *dentry, int flags); -+void aufs_read_unlock(struct dentry *dentry, int flags); -+void aufs_write_lock(struct dentry *dentry); -+void aufs_write_unlock(struct dentry *dentry); -+int aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int flags); -+void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2); -+ -+/* wbr_policy.c */ -+extern struct au_wbr_copyup_operations au_wbr_copyup_ops[]; -+extern struct au_wbr_create_operations au_wbr_create_ops[]; -+int au_cpdown_dirs(struct dentry *dentry, aufs_bindex_t bdst); -+int au_wbr_nonopq(struct dentry *dentry, aufs_bindex_t bindex); -+int au_wbr_do_copyup_bu(struct dentry *dentry, aufs_bindex_t bstart); -+ -+/* mvdown.c */ -+int au_mvdown(struct dentry *dentry, struct aufs_mvdown __user *arg); -+ -+#ifdef CONFIG_AUFS_FHSM -+/* fhsm.c */ -+ -+static inline pid_t au_fhsm_pid(struct au_fhsm *fhsm) -+{ -+ pid_t pid; -+ -+ spin_lock(&fhsm->fhsm_spin); -+ pid = fhsm->fhsm_pid; -+ spin_unlock(&fhsm->fhsm_spin); -+ -+ return pid; -+} -+ -+void au_fhsm_wrote(struct super_block *sb, aufs_bindex_t bindex, int force); -+void au_fhsm_wrote_all(struct super_block *sb, int force); -+int au_fhsm_fd(struct super_block *sb, int oflags); -+int au_fhsm_br_alloc(struct au_branch *br); -+void au_fhsm_set_bottom(struct super_block *sb, aufs_bindex_t bindex); -+void au_fhsm_fin(struct super_block *sb); -+void au_fhsm_init(struct au_sbinfo *sbinfo); -+void au_fhsm_set(struct au_sbinfo *sbinfo, unsigned int sec); -+void au_fhsm_show(struct seq_file *seq, struct au_sbinfo *sbinfo); -+#else -+AuStubVoid(au_fhsm_wrote, struct super_block *sb, aufs_bindex_t bindex, -+ int force) -+AuStubVoid(au_fhsm_wrote_all, struct super_block *sb, int force) -+AuStub(int, au_fhsm_fd, return -EOPNOTSUPP, struct super_block *sb, int oflags) -+AuStub(pid_t, au_fhsm_pid, return 0, struct au_fhsm *fhsm) -+AuStubInt0(au_fhsm_br_alloc, struct au_branch *br) -+AuStubVoid(au_fhsm_set_bottom, struct super_block *sb, aufs_bindex_t bindex) -+AuStubVoid(au_fhsm_fin, struct super_block *sb) -+AuStubVoid(au_fhsm_init, struct au_sbinfo *sbinfo) -+AuStubVoid(au_fhsm_set, struct au_sbinfo *sbinfo, unsigned int sec) -+AuStubVoid(au_fhsm_show, struct seq_file *seq, struct au_sbinfo *sbinfo) -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline struct au_sbinfo *au_sbi(struct super_block *sb) -+{ -+ return sb->s_fs_info; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+#ifdef CONFIG_AUFS_EXPORT -+int au_test_nfsd(void); -+void au_export_init(struct super_block *sb); -+void au_xigen_inc(struct inode *inode); -+int au_xigen_new(struct inode *inode); -+int au_xigen_set(struct super_block *sb, struct file *base); -+void au_xigen_clr(struct super_block *sb); -+ -+static inline int au_busy_or_stale(void) -+{ -+ if (!au_test_nfsd()) -+ return -EBUSY; -+ return -ESTALE; -+} -+#else -+AuStubInt0(au_test_nfsd, void) -+AuStubVoid(au_export_init, struct super_block *sb) -+AuStubVoid(au_xigen_inc, struct inode *inode) -+AuStubInt0(au_xigen_new, struct inode *inode) -+AuStubInt0(au_xigen_set, struct super_block *sb, struct file *base) -+AuStubVoid(au_xigen_clr, struct super_block *sb) -+AuStub(int, au_busy_or_stale, return -EBUSY, void) -+#endif /* CONFIG_AUFS_EXPORT */ -+ -+/* ---------------------------------------------------------------------- */ -+ -+#ifdef CONFIG_AUFS_SBILIST -+/* module.c */ -+extern struct au_sphlhead au_sbilist; -+ -+static inline void au_sbilist_init(void) -+{ -+ au_sphl_init(&au_sbilist); -+} -+ -+static inline void au_sbilist_add(struct super_block *sb) -+{ -+ au_sphl_add(&au_sbi(sb)->si_list, &au_sbilist); -+} -+ -+static inline void au_sbilist_del(struct super_block *sb) -+{ -+ au_sphl_del(&au_sbi(sb)->si_list, &au_sbilist); -+} -+ -+#ifdef CONFIG_AUFS_MAGIC_SYSRQ -+static inline void au_sbilist_lock(void) -+{ -+ spin_lock(&au_sbilist.spin); -+} -+ -+static inline void au_sbilist_unlock(void) -+{ -+ spin_unlock(&au_sbilist.spin); -+} -+#define AuGFP_SBILIST GFP_ATOMIC -+#else -+AuStubVoid(au_sbilist_lock, void) -+AuStubVoid(au_sbilist_unlock, void) -+#define AuGFP_SBILIST GFP_NOFS -+#endif /* CONFIG_AUFS_MAGIC_SYSRQ */ -+#else -+AuStubVoid(au_sbilist_init, void) -+AuStubVoid(au_sbilist_add, struct super_block *sb) -+AuStubVoid(au_sbilist_del, struct super_block *sb) -+AuStubVoid(au_sbilist_lock, void) -+AuStubVoid(au_sbilist_unlock, void) -+#define AuGFP_SBILIST GFP_NOFS -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline void dbgaufs_si_null(struct au_sbinfo *sbinfo) -+{ -+ /* -+ * This function is a dynamic '__init' function actually, -+ * so the tiny check for si_rwsem is unnecessary. -+ */ -+ /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ -+#ifdef CONFIG_DEBUG_FS -+ sbinfo->si_dbgaufs = NULL; -+ sbinfo->si_dbgaufs_plink = NULL; -+ sbinfo->si_dbgaufs_xib = NULL; -+#ifdef CONFIG_AUFS_EXPORT -+ sbinfo->si_dbgaufs_xigen = NULL; -+#endif -+#endif -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline void si_pid_idx_bit(int *idx, pid_t *bit) -+{ -+ /* the origin of pid is 1, but the bitmap's is 0 */ -+ *bit = current->pid - 1; -+ *idx = *bit / AU_PIDSTEP; -+ *bit %= AU_PIDSTEP; -+} -+ -+static inline int si_pid_test(struct super_block *sb) -+{ -+ pid_t bit; -+ int idx; -+ unsigned long *bitmap; -+ -+ si_pid_idx_bit(&idx, &bit); -+ bitmap = au_sbi(sb)->au_si_pid.pid_bitmap[idx]; -+ if (bitmap) -+ return test_bit(bit, bitmap); -+ return 0; -+} -+ -+static inline void si_pid_clr(struct super_block *sb) -+{ -+ pid_t bit; -+ int idx; -+ unsigned long *bitmap; -+ -+ si_pid_idx_bit(&idx, &bit); -+ bitmap = au_sbi(sb)->au_si_pid.pid_bitmap[idx]; -+ BUG_ON(!bitmap); -+ AuDebugOn(!test_bit(bit, bitmap)); -+ clear_bit(bit, bitmap); -+ /* smp_mb(); */ -+} -+ -+void si_pid_set(struct super_block *sb); -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* lock superblock. mainly for entry point functions */ -+/* -+ * __si_read_lock, __si_write_lock, -+ * __si_read_unlock, __si_write_unlock, __si_downgrade_lock -+ */ -+AuSimpleRwsemFuncs(__si, struct super_block *sb, &au_sbi(sb)->si_rwsem); -+ -+#define SiMustNoWaiters(sb) AuRwMustNoWaiters(&au_sbi(sb)->si_rwsem) -+#define SiMustAnyLock(sb) AuRwMustAnyLock(&au_sbi(sb)->si_rwsem) -+#define SiMustWriteLock(sb) AuRwMustWriteLock(&au_sbi(sb)->si_rwsem) -+ -+static inline void si_noflush_read_lock(struct super_block *sb) -+{ -+ __si_read_lock(sb); -+ si_pid_set(sb); -+} -+ -+static inline int si_noflush_read_trylock(struct super_block *sb) -+{ -+ int locked; -+ -+ locked = __si_read_trylock(sb); -+ if (locked) -+ si_pid_set(sb); -+ return locked; -+} -+ -+static inline void si_noflush_write_lock(struct super_block *sb) -+{ -+ __si_write_lock(sb); -+ si_pid_set(sb); -+} -+ -+static inline int si_noflush_write_trylock(struct super_block *sb) -+{ -+ int locked; -+ -+ locked = __si_write_trylock(sb); -+ if (locked) -+ si_pid_set(sb); -+ return locked; -+} -+ -+#if 0 /* reserved */ -+static inline int si_read_trylock(struct super_block *sb, int flags) -+{ -+ if (au_ftest_lock(flags, FLUSH)) -+ au_nwt_flush(&au_sbi(sb)->si_nowait); -+ return si_noflush_read_trylock(sb); -+} -+#endif -+ -+static inline void si_read_unlock(struct super_block *sb) -+{ -+ si_pid_clr(sb); -+ __si_read_unlock(sb); -+} -+ -+#if 0 /* reserved */ -+static inline int si_write_trylock(struct super_block *sb, int flags) -+{ -+ if (au_ftest_lock(flags, FLUSH)) -+ au_nwt_flush(&au_sbi(sb)->si_nowait); -+ return si_noflush_write_trylock(sb); -+} -+#endif -+ -+static inline void si_write_unlock(struct super_block *sb) -+{ -+ si_pid_clr(sb); -+ __si_write_unlock(sb); -+} -+ -+#if 0 /* reserved */ -+static inline void si_downgrade_lock(struct super_block *sb) -+{ -+ __si_downgrade_lock(sb); -+} -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline aufs_bindex_t au_sbend(struct super_block *sb) -+{ -+ SiMustAnyLock(sb); -+ return au_sbi(sb)->si_bend; -+} -+ -+static inline unsigned int au_mntflags(struct super_block *sb) -+{ -+ SiMustAnyLock(sb); -+ return au_sbi(sb)->si_mntflags; -+} -+ -+static inline unsigned int au_sigen(struct super_block *sb) -+{ -+ SiMustAnyLock(sb); -+ return au_sbi(sb)->si_generation; -+} -+ -+static inline void au_ninodes_inc(struct super_block *sb) -+{ -+ atomic_long_inc(&au_sbi(sb)->si_ninodes); -+} -+ -+static inline void au_ninodes_dec(struct super_block *sb) -+{ -+ AuDebugOn(!atomic_long_read(&au_sbi(sb)->si_ninodes)); -+ atomic_long_dec(&au_sbi(sb)->si_ninodes); -+} -+ -+static inline void au_nfiles_inc(struct super_block *sb) -+{ -+ atomic_long_inc(&au_sbi(sb)->si_nfiles); -+} -+ -+static inline void au_nfiles_dec(struct super_block *sb) -+{ -+ AuDebugOn(!atomic_long_read(&au_sbi(sb)->si_nfiles)); -+ atomic_long_dec(&au_sbi(sb)->si_nfiles); -+} -+ -+static inline struct au_branch *au_sbr(struct super_block *sb, -+ aufs_bindex_t bindex) -+{ -+ SiMustAnyLock(sb); -+ return au_sbi(sb)->si_branch[0 + bindex]; -+} -+ -+static inline void au_xino_brid_set(struct super_block *sb, aufs_bindex_t brid) -+{ -+ SiMustWriteLock(sb); -+ au_sbi(sb)->si_xino_brid = brid; -+} -+ -+static inline aufs_bindex_t au_xino_brid(struct super_block *sb) -+{ -+ SiMustAnyLock(sb); -+ return au_sbi(sb)->si_xino_brid; -+} -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_SUPER_H__ */ -diff --git a/fs/aufs/sysaufs.c b/fs/aufs/sysaufs.c -new file mode 100644 -index 0000000..75c9c24 ---- /dev/null -+++ b/fs/aufs/sysaufs.c -@@ -0,0 +1,104 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * sysfs interface and lifetime management -+ * they are necessary regardless sysfs is disabled. -+ */ -+ -+#include -+#include "aufs.h" -+ -+unsigned long sysaufs_si_mask; -+struct kset *sysaufs_kset; -+ -+#define AuSiAttr(_name) { \ -+ .attr = { .name = __stringify(_name), .mode = 0444 }, \ -+ .show = sysaufs_si_##_name, \ -+} -+ -+static struct sysaufs_si_attr sysaufs_si_attr_xi_path = AuSiAttr(xi_path); -+struct attribute *sysaufs_si_attrs[] = { -+ &sysaufs_si_attr_xi_path.attr, -+ NULL, -+}; -+ -+static const struct sysfs_ops au_sbi_ops = { -+ .show = sysaufs_si_show -+}; -+ -+static struct kobj_type au_sbi_ktype = { -+ .release = au_si_free, -+ .sysfs_ops = &au_sbi_ops, -+ .default_attrs = sysaufs_si_attrs -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+int sysaufs_si_init(struct au_sbinfo *sbinfo) -+{ -+ int err; -+ -+ sbinfo->si_kobj.kset = sysaufs_kset; -+ /* cf. sysaufs_name() */ -+ err = kobject_init_and_add -+ (&sbinfo->si_kobj, &au_sbi_ktype, /*&sysaufs_kset->kobj*/NULL, -+ SysaufsSiNamePrefix "%lx", sysaufs_si_id(sbinfo)); -+ -+ dbgaufs_si_null(sbinfo); -+ if (!err) { -+ err = dbgaufs_si_init(sbinfo); -+ if (unlikely(err)) -+ kobject_put(&sbinfo->si_kobj); -+ } -+ return err; -+} -+ -+void sysaufs_fin(void) -+{ -+ dbgaufs_fin(); -+ sysfs_remove_group(&sysaufs_kset->kobj, sysaufs_attr_group); -+ kset_unregister(sysaufs_kset); -+} -+ -+int __init sysaufs_init(void) -+{ -+ int err; -+ -+ do { -+ get_random_bytes(&sysaufs_si_mask, sizeof(sysaufs_si_mask)); -+ } while (!sysaufs_si_mask); -+ -+ err = -EINVAL; -+ sysaufs_kset = kset_create_and_add(AUFS_NAME, NULL, fs_kobj); -+ if (unlikely(!sysaufs_kset)) -+ goto out; -+ err = PTR_ERR(sysaufs_kset); -+ if (IS_ERR(sysaufs_kset)) -+ goto out; -+ err = sysfs_create_group(&sysaufs_kset->kobj, sysaufs_attr_group); -+ if (unlikely(err)) { -+ kset_unregister(sysaufs_kset); -+ goto out; -+ } -+ -+ err = dbgaufs_init(); -+ if (unlikely(err)) -+ sysaufs_fin(); -+out: -+ return err; -+} -diff --git a/fs/aufs/sysaufs.h b/fs/aufs/sysaufs.h -new file mode 100644 -index 0000000..14975c9 ---- /dev/null -+++ b/fs/aufs/sysaufs.h -@@ -0,0 +1,101 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * sysfs interface and mount lifetime management -+ */ -+ -+#ifndef __SYSAUFS_H__ -+#define __SYSAUFS_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+#include "module.h" -+ -+struct super_block; -+struct au_sbinfo; -+ -+struct sysaufs_si_attr { -+ struct attribute attr; -+ int (*show)(struct seq_file *seq, struct super_block *sb); -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* sysaufs.c */ -+extern unsigned long sysaufs_si_mask; -+extern struct kset *sysaufs_kset; -+extern struct attribute *sysaufs_si_attrs[]; -+int sysaufs_si_init(struct au_sbinfo *sbinfo); -+int __init sysaufs_init(void); -+void sysaufs_fin(void); -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* some people doesn't like to show a pointer in kernel */ -+static inline unsigned long sysaufs_si_id(struct au_sbinfo *sbinfo) -+{ -+ return sysaufs_si_mask ^ (unsigned long)sbinfo; -+} -+ -+#define SysaufsSiNamePrefix "si_" -+#define SysaufsSiNameLen (sizeof(SysaufsSiNamePrefix) + 16) -+static inline void sysaufs_name(struct au_sbinfo *sbinfo, char *name) -+{ -+ snprintf(name, SysaufsSiNameLen, SysaufsSiNamePrefix "%lx", -+ sysaufs_si_id(sbinfo)); -+} -+ -+struct au_branch; -+#ifdef CONFIG_SYSFS -+/* sysfs.c */ -+extern struct attribute_group *sysaufs_attr_group; -+ -+int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb); -+ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr, -+ char *buf); -+long au_brinfo_ioctl(struct file *file, unsigned long arg); -+#ifdef CONFIG_COMPAT -+long au_brinfo_compat_ioctl(struct file *file, unsigned long arg); -+#endif -+ -+void sysaufs_br_init(struct au_branch *br); -+void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex); -+void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex); -+ -+#define sysaufs_brs_init() do {} while (0) -+ -+#else -+#define sysaufs_attr_group NULL -+ -+AuStubInt0(sysaufs_si_xi_path, struct seq_file *seq, struct super_block *sb) -+AuStub(ssize_t, sysaufs_si_show, return 0, struct kobject *kobj, -+ struct attribute *attr, char *buf) -+AuStubVoid(sysaufs_br_init, struct au_branch *br) -+AuStubVoid(sysaufs_brs_add, struct super_block *sb, aufs_bindex_t bindex) -+AuStubVoid(sysaufs_brs_del, struct super_block *sb, aufs_bindex_t bindex) -+ -+static inline void sysaufs_brs_init(void) -+{ -+ sysaufs_brs = 0; -+} -+ -+#endif /* CONFIG_SYSFS */ -+ -+#endif /* __KERNEL__ */ -+#endif /* __SYSAUFS_H__ */ -diff --git a/fs/aufs/sysfs.c b/fs/aufs/sysfs.c -new file mode 100644 -index 0000000..b2d1888 ---- /dev/null -+++ b/fs/aufs/sysfs.c -@@ -0,0 +1,376 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * sysfs interface -+ */ -+ -+#include -+#include -+#include "aufs.h" -+ -+#ifdef CONFIG_AUFS_FS_MODULE -+/* this entry violates the "one line per file" policy of sysfs */ -+static ssize_t config_show(struct kobject *kobj, struct kobj_attribute *attr, -+ char *buf) -+{ -+ ssize_t err; -+ static char *conf = -+/* this file is generated at compiling */ -+#include "conf.str" -+ ; -+ -+ err = snprintf(buf, PAGE_SIZE, conf); -+ if (unlikely(err >= PAGE_SIZE)) -+ err = -EFBIG; -+ return err; -+} -+ -+static struct kobj_attribute au_config_attr = __ATTR_RO(config); -+#endif -+ -+static struct attribute *au_attr[] = { -+#ifdef CONFIG_AUFS_FS_MODULE -+ &au_config_attr.attr, -+#endif -+ NULL, /* need to NULL terminate the list of attributes */ -+}; -+ -+static struct attribute_group sysaufs_attr_group_body = { -+ .attrs = au_attr -+}; -+ -+struct attribute_group *sysaufs_attr_group = &sysaufs_attr_group_body; -+ -+/* ---------------------------------------------------------------------- */ -+ -+int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb) -+{ -+ int err; -+ -+ SiMustAnyLock(sb); -+ -+ err = 0; -+ if (au_opt_test(au_mntflags(sb), XINO)) { -+ err = au_xino_path(seq, au_sbi(sb)->si_xib); -+ seq_putc(seq, '\n'); -+ } -+ return err; -+} -+ -+/* -+ * the lifetime of branch is independent from the entry under sysfs. -+ * sysfs handles the lifetime of the entry, and never call ->show() after it is -+ * unlinked. -+ */ -+static int sysaufs_si_br(struct seq_file *seq, struct super_block *sb, -+ aufs_bindex_t bindex, int idx) -+{ -+ int err; -+ struct path path; -+ struct dentry *root; -+ struct au_branch *br; -+ au_br_perm_str_t perm; -+ -+ AuDbg("b%d\n", bindex); -+ -+ err = 0; -+ root = sb->s_root; -+ di_read_lock_parent(root, !AuLock_IR); -+ br = au_sbr(sb, bindex); -+ -+ switch (idx) { -+ case AuBrSysfs_BR: -+ path.mnt = au_br_mnt(br); -+ path.dentry = au_h_dptr(root, bindex); -+ err = au_seq_path(seq, &path); -+ if (!err) { -+ au_optstr_br_perm(&perm, br->br_perm); -+ err = seq_printf(seq, "=%s\n", perm.a); -+ } -+ break; -+ case AuBrSysfs_BRID: -+ err = seq_printf(seq, "%d\n", br->br_id); -+ break; -+ } -+ di_read_unlock(root, !AuLock_IR); -+ if (err == -1) -+ err = -E2BIG; -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static struct seq_file *au_seq(char *p, ssize_t len) -+{ -+ struct seq_file *seq; -+ -+ seq = kzalloc(sizeof(*seq), GFP_NOFS); -+ if (seq) { -+ /* mutex_init(&seq.lock); */ -+ seq->buf = p; -+ seq->size = len; -+ return seq; /* success */ -+ } -+ -+ seq = ERR_PTR(-ENOMEM); -+ return seq; -+} -+ -+#define SysaufsBr_PREFIX "br" -+#define SysaufsBrid_PREFIX "brid" -+ -+/* todo: file size may exceed PAGE_SIZE */ -+ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr, -+ char *buf) -+{ -+ ssize_t err; -+ int idx; -+ long l; -+ aufs_bindex_t bend; -+ struct au_sbinfo *sbinfo; -+ struct super_block *sb; -+ struct seq_file *seq; -+ char *name; -+ struct attribute **cattr; -+ -+ sbinfo = container_of(kobj, struct au_sbinfo, si_kobj); -+ sb = sbinfo->si_sb; -+ -+ /* -+ * prevent a race condition between sysfs and aufs. -+ * for instance, sysfs_file_read() calls sysfs_get_active_two() which -+ * prohibits maintaining the sysfs entries. -+ * hew we acquire read lock after sysfs_get_active_two(). -+ * on the other hand, the remount process may maintain the sysfs/aufs -+ * entries after acquiring write lock. -+ * it can cause a deadlock. -+ * simply we gave up processing read here. -+ */ -+ err = -EBUSY; -+ if (unlikely(!si_noflush_read_trylock(sb))) -+ goto out; -+ -+ seq = au_seq(buf, PAGE_SIZE); -+ err = PTR_ERR(seq); -+ if (IS_ERR(seq)) -+ goto out_unlock; -+ -+ name = (void *)attr->name; -+ cattr = sysaufs_si_attrs; -+ while (*cattr) { -+ if (!strcmp(name, (*cattr)->name)) { -+ err = container_of(*cattr, struct sysaufs_si_attr, attr) -+ ->show(seq, sb); -+ goto out_seq; -+ } -+ cattr++; -+ } -+ -+ if (!strncmp(name, SysaufsBrid_PREFIX, -+ sizeof(SysaufsBrid_PREFIX) - 1)) { -+ idx = AuBrSysfs_BRID; -+ name += sizeof(SysaufsBrid_PREFIX) - 1; -+ } else if (!strncmp(name, SysaufsBr_PREFIX, -+ sizeof(SysaufsBr_PREFIX) - 1)) { -+ idx = AuBrSysfs_BR; -+ name += sizeof(SysaufsBr_PREFIX) - 1; -+ } else -+ BUG(); -+ -+ err = kstrtol(name, 10, &l); -+ if (!err) { -+ bend = au_sbend(sb); -+ if (l <= bend) -+ err = sysaufs_si_br(seq, sb, (aufs_bindex_t)l, idx); -+ else -+ err = -ENOENT; -+ } -+ -+out_seq: -+ if (!err) { -+ err = seq->count; -+ /* sysfs limit */ -+ if (unlikely(err == PAGE_SIZE)) -+ err = -EFBIG; -+ } -+ kfree(seq); -+out_unlock: -+ si_read_unlock(sb); -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_brinfo(struct super_block *sb, union aufs_brinfo __user *arg) -+{ -+ int err; -+ int16_t brid; -+ aufs_bindex_t bindex, bend; -+ size_t sz; -+ char *buf; -+ struct seq_file *seq; -+ struct au_branch *br; -+ -+ si_read_lock(sb, AuLock_FLUSH); -+ bend = au_sbend(sb); -+ err = bend + 1; -+ if (!arg) -+ goto out; -+ -+ err = -ENOMEM; -+ buf = (void *)__get_free_page(GFP_NOFS); -+ if (unlikely(!buf)) -+ goto out; -+ -+ seq = au_seq(buf, PAGE_SIZE); -+ err = PTR_ERR(seq); -+ if (IS_ERR(seq)) -+ goto out_buf; -+ -+ sz = sizeof(*arg) - offsetof(union aufs_brinfo, path); -+ for (bindex = 0; bindex <= bend; bindex++, arg++) { -+ err = !access_ok(VERIFY_WRITE, arg, sizeof(*arg)); -+ if (unlikely(err)) -+ break; -+ -+ br = au_sbr(sb, bindex); -+ brid = br->br_id; -+ BUILD_BUG_ON(sizeof(brid) != sizeof(arg->id)); -+ err = __put_user(brid, &arg->id); -+ if (unlikely(err)) -+ break; -+ -+ BUILD_BUG_ON(sizeof(br->br_perm) != sizeof(arg->perm)); -+ err = __put_user(br->br_perm, &arg->perm); -+ if (unlikely(err)) -+ break; -+ -+ err = au_seq_path(seq, &br->br_path); -+ if (unlikely(err)) -+ break; -+ err = seq_putc(seq, '\0'); -+ if (!err && seq->count <= sz) { -+ err = copy_to_user(arg->path, seq->buf, seq->count); -+ seq->count = 0; -+ if (unlikely(err)) -+ break; -+ } else { -+ err = -E2BIG; -+ goto out_seq; -+ } -+ } -+ if (unlikely(err)) -+ err = -EFAULT; -+ -+out_seq: -+ kfree(seq); -+out_buf: -+ free_page((unsigned long)buf); -+out: -+ si_read_unlock(sb); -+ return err; -+} -+ -+long au_brinfo_ioctl(struct file *file, unsigned long arg) -+{ -+ return au_brinfo(file->f_dentry->d_sb, (void __user *)arg); -+} -+ -+#ifdef CONFIG_COMPAT -+long au_brinfo_compat_ioctl(struct file *file, unsigned long arg) -+{ -+ return au_brinfo(file->f_dentry->d_sb, compat_ptr(arg)); -+} -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+void sysaufs_br_init(struct au_branch *br) -+{ -+ int i; -+ struct au_brsysfs *br_sysfs; -+ struct attribute *attr; -+ -+ br_sysfs = br->br_sysfs; -+ for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) { -+ attr = &br_sysfs->attr; -+ sysfs_attr_init(attr); -+ attr->name = br_sysfs->name; -+ attr->mode = S_IRUGO; -+ br_sysfs++; -+ } -+} -+ -+void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ struct au_branch *br; -+ struct kobject *kobj; -+ struct au_brsysfs *br_sysfs; -+ int i; -+ aufs_bindex_t bend; -+ -+ dbgaufs_brs_del(sb, bindex); -+ -+ if (!sysaufs_brs) -+ return; -+ -+ kobj = &au_sbi(sb)->si_kobj; -+ bend = au_sbend(sb); -+ for (; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ br_sysfs = br->br_sysfs; -+ for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) { -+ sysfs_remove_file(kobj, &br_sysfs->attr); -+ br_sysfs++; -+ } -+ } -+} -+ -+void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ int err, i; -+ aufs_bindex_t bend; -+ struct kobject *kobj; -+ struct au_branch *br; -+ struct au_brsysfs *br_sysfs; -+ -+ dbgaufs_brs_add(sb, bindex); -+ -+ if (!sysaufs_brs) -+ return; -+ -+ kobj = &au_sbi(sb)->si_kobj; -+ bend = au_sbend(sb); -+ for (; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ br_sysfs = br->br_sysfs; -+ snprintf(br_sysfs[AuBrSysfs_BR].name, sizeof(br_sysfs->name), -+ SysaufsBr_PREFIX "%d", bindex); -+ snprintf(br_sysfs[AuBrSysfs_BRID].name, sizeof(br_sysfs->name), -+ SysaufsBrid_PREFIX "%d", bindex); -+ for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) { -+ err = sysfs_create_file(kobj, &br_sysfs->attr); -+ if (unlikely(err)) -+ pr_warn("failed %s under sysfs(%d)\n", -+ br_sysfs->name, err); -+ br_sysfs++; -+ } -+ } -+} -diff --git a/fs/aufs/sysrq.c b/fs/aufs/sysrq.c -new file mode 100644 -index 0000000..057c23e ---- /dev/null -+++ b/fs/aufs/sysrq.c -@@ -0,0 +1,157 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * magic sysrq hanlder -+ */ -+ -+/* #include */ -+#include -+#include "aufs.h" -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void sysrq_sb(struct super_block *sb) -+{ -+ char *plevel; -+ struct au_sbinfo *sbinfo; -+ struct file *file; -+ struct au_sphlhead *files; -+ struct au_finfo *finfo; -+ -+ plevel = au_plevel; -+ au_plevel = KERN_WARNING; -+ -+ /* since we define pr_fmt, call printk directly */ -+#define pr(str) printk(KERN_WARNING AUFS_NAME ": " str) -+ -+ sbinfo = au_sbi(sb); -+ printk(KERN_WARNING "si=%lx\n", sysaufs_si_id(sbinfo)); -+ pr("superblock\n"); -+ au_dpri_sb(sb); -+ -+#if 0 -+ pr("root dentry\n"); -+ au_dpri_dentry(sb->s_root); -+ pr("root inode\n"); -+ au_dpri_inode(sb->s_root->d_inode); -+#endif -+ -+#if 0 -+ do { -+ int err, i, j, ndentry; -+ struct au_dcsub_pages dpages; -+ struct au_dpage *dpage; -+ -+ err = au_dpages_init(&dpages, GFP_ATOMIC); -+ if (unlikely(err)) -+ break; -+ err = au_dcsub_pages(&dpages, sb->s_root, NULL, NULL); -+ if (!err) -+ for (i = 0; i < dpages.ndpage; i++) { -+ dpage = dpages.dpages + i; -+ ndentry = dpage->ndentry; -+ for (j = 0; j < ndentry; j++) -+ au_dpri_dentry(dpage->dentries[j]); -+ } -+ au_dpages_free(&dpages); -+ } while (0); -+#endif -+ -+#if 1 -+ { -+ struct inode *i; -+ -+ pr("isolated inode\n"); -+ spin_lock(&inode_sb_list_lock); -+ list_for_each_entry(i, &sb->s_inodes, i_sb_list) { -+ spin_lock(&i->i_lock); -+ if (1 || hlist_empty(&i->i_dentry)) -+ au_dpri_inode(i); -+ spin_unlock(&i->i_lock); -+ } -+ spin_unlock(&inode_sb_list_lock); -+ } -+#endif -+ pr("files\n"); -+ files = &au_sbi(sb)->si_files; -+ spin_lock(&files->spin); -+ hlist_for_each_entry(finfo, &files->head, fi_hlist) { -+ umode_t mode; -+ -+ file = finfo->fi_file; -+ mode = file_inode(file)->i_mode; -+ if (!special_file(mode)) -+ au_dpri_file(file); -+ } -+ spin_unlock(&files->spin); -+ pr("done\n"); -+ -+#undef pr -+ au_plevel = plevel; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* module parameter */ -+static char *aufs_sysrq_key = "a"; -+module_param_named(sysrq, aufs_sysrq_key, charp, S_IRUGO); -+MODULE_PARM_DESC(sysrq, "MagicSysRq key for " AUFS_NAME); -+ -+static void au_sysrq(int key __maybe_unused) -+{ -+ struct au_sbinfo *sbinfo; -+ -+ lockdep_off(); -+ au_sbilist_lock(); -+ hlist_for_each_entry(sbinfo, &au_sbilist.head, si_list) -+ sysrq_sb(sbinfo->si_sb); -+ au_sbilist_unlock(); -+ lockdep_on(); -+} -+ -+static struct sysrq_key_op au_sysrq_op = { -+ .handler = au_sysrq, -+ .help_msg = "Aufs", -+ .action_msg = "Aufs", -+ .enable_mask = SYSRQ_ENABLE_DUMP -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+int __init au_sysrq_init(void) -+{ -+ int err; -+ char key; -+ -+ err = -1; -+ key = *aufs_sysrq_key; -+ if ('a' <= key && key <= 'z') -+ err = register_sysrq_key(key, &au_sysrq_op); -+ if (unlikely(err)) -+ pr_err("err %d, sysrq=%c\n", err, key); -+ return err; -+} -+ -+void au_sysrq_fin(void) -+{ -+ int err; -+ -+ err = unregister_sysrq_key(*aufs_sysrq_key, &au_sysrq_op); -+ if (unlikely(err)) -+ pr_err("err %d (ignored)\n", err); -+} -diff --git a/fs/aufs/vdir.c b/fs/aufs/vdir.c -new file mode 100644 -index 0000000..f942d16 ---- /dev/null -+++ b/fs/aufs/vdir.c -@@ -0,0 +1,888 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * virtual or vertical directory -+ */ -+ -+#include "aufs.h" -+ -+static unsigned int calc_size(int nlen) -+{ -+ return ALIGN(sizeof(struct au_vdir_de) + nlen, sizeof(ino_t)); -+} -+ -+static int set_deblk_end(union au_vdir_deblk_p *p, -+ union au_vdir_deblk_p *deblk_end) -+{ -+ if (calc_size(0) <= deblk_end->deblk - p->deblk) { -+ p->de->de_str.len = 0; -+ /* smp_mb(); */ -+ return 0; -+ } -+ return -1; /* error */ -+} -+ -+/* returns true or false */ -+static int is_deblk_end(union au_vdir_deblk_p *p, -+ union au_vdir_deblk_p *deblk_end) -+{ -+ if (calc_size(0) <= deblk_end->deblk - p->deblk) -+ return !p->de->de_str.len; -+ return 1; -+} -+ -+static unsigned char *last_deblk(struct au_vdir *vdir) -+{ -+ return vdir->vd_deblk[vdir->vd_nblk - 1]; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* estimate the appropriate size for name hash table */ -+unsigned int au_rdhash_est(loff_t sz) -+{ -+ unsigned int n; -+ -+ n = UINT_MAX; -+ sz >>= 10; -+ if (sz < n) -+ n = sz; -+ if (sz < AUFS_RDHASH_DEF) -+ n = AUFS_RDHASH_DEF; -+ /* pr_info("n %u\n", n); */ -+ return n; -+} -+ -+/* -+ * the allocated memory has to be freed by -+ * au_nhash_wh_free() or au_nhash_de_free(). -+ */ -+int au_nhash_alloc(struct au_nhash *nhash, unsigned int num_hash, gfp_t gfp) -+{ -+ struct hlist_head *head; -+ unsigned int u; -+ size_t sz; -+ -+ sz = sizeof(*nhash->nh_head) * num_hash; -+ head = kmalloc(sz, gfp); -+ if (head) { -+ nhash->nh_num = num_hash; -+ nhash->nh_head = head; -+ for (u = 0; u < num_hash; u++) -+ INIT_HLIST_HEAD(head++); -+ return 0; /* success */ -+ } -+ -+ return -ENOMEM; -+} -+ -+static void nhash_count(struct hlist_head *head) -+{ -+#if 0 -+ unsigned long n; -+ struct hlist_node *pos; -+ -+ n = 0; -+ hlist_for_each(pos, head) -+ n++; -+ pr_info("%lu\n", n); -+#endif -+} -+ -+static void au_nhash_wh_do_free(struct hlist_head *head) -+{ -+ struct au_vdir_wh *pos; -+ struct hlist_node *node; -+ -+ hlist_for_each_entry_safe(pos, node, head, wh_hash) -+ kfree(pos); -+} -+ -+static void au_nhash_de_do_free(struct hlist_head *head) -+{ -+ struct au_vdir_dehstr *pos; -+ struct hlist_node *node; -+ -+ hlist_for_each_entry_safe(pos, node, head, hash) -+ au_cache_free_vdir_dehstr(pos); -+} -+ -+static void au_nhash_do_free(struct au_nhash *nhash, -+ void (*free)(struct hlist_head *head)) -+{ -+ unsigned int n; -+ struct hlist_head *head; -+ -+ n = nhash->nh_num; -+ if (!n) -+ return; -+ -+ head = nhash->nh_head; -+ while (n-- > 0) { -+ nhash_count(head); -+ free(head++); -+ } -+ kfree(nhash->nh_head); -+} -+ -+void au_nhash_wh_free(struct au_nhash *whlist) -+{ -+ au_nhash_do_free(whlist, au_nhash_wh_do_free); -+} -+ -+static void au_nhash_de_free(struct au_nhash *delist) -+{ -+ au_nhash_do_free(delist, au_nhash_de_do_free); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int au_nhash_test_longer_wh(struct au_nhash *whlist, aufs_bindex_t btgt, -+ int limit) -+{ -+ int num; -+ unsigned int u, n; -+ struct hlist_head *head; -+ struct au_vdir_wh *pos; -+ -+ num = 0; -+ n = whlist->nh_num; -+ head = whlist->nh_head; -+ for (u = 0; u < n; u++, head++) -+ hlist_for_each_entry(pos, head, wh_hash) -+ if (pos->wh_bindex == btgt && ++num > limit) -+ return 1; -+ return 0; -+} -+ -+static struct hlist_head *au_name_hash(struct au_nhash *nhash, -+ unsigned char *name, -+ unsigned int len) -+{ -+ unsigned int v; -+ /* const unsigned int magic_bit = 12; */ -+ -+ AuDebugOn(!nhash->nh_num || !nhash->nh_head); -+ -+ v = 0; -+ while (len--) -+ v += *name++; -+ /* v = hash_long(v, magic_bit); */ -+ v %= nhash->nh_num; -+ return nhash->nh_head + v; -+} -+ -+static int au_nhash_test_name(struct au_vdir_destr *str, const char *name, -+ int nlen) -+{ -+ return str->len == nlen && !memcmp(str->name, name, nlen); -+} -+ -+/* returns found or not */ -+int au_nhash_test_known_wh(struct au_nhash *whlist, char *name, int nlen) -+{ -+ struct hlist_head *head; -+ struct au_vdir_wh *pos; -+ struct au_vdir_destr *str; -+ -+ head = au_name_hash(whlist, name, nlen); -+ hlist_for_each_entry(pos, head, wh_hash) { -+ str = &pos->wh_str; -+ AuDbg("%.*s\n", str->len, str->name); -+ if (au_nhash_test_name(str, name, nlen)) -+ return 1; -+ } -+ return 0; -+} -+ -+/* returns found(true) or not */ -+static int test_known(struct au_nhash *delist, char *name, int nlen) -+{ -+ struct hlist_head *head; -+ struct au_vdir_dehstr *pos; -+ struct au_vdir_destr *str; -+ -+ head = au_name_hash(delist, name, nlen); -+ hlist_for_each_entry(pos, head, hash) { -+ str = pos->str; -+ AuDbg("%.*s\n", str->len, str->name); -+ if (au_nhash_test_name(str, name, nlen)) -+ return 1; -+ } -+ return 0; -+} -+ -+static void au_shwh_init_wh(struct au_vdir_wh *wh, ino_t ino, -+ unsigned char d_type) -+{ -+#ifdef CONFIG_AUFS_SHWH -+ wh->wh_ino = ino; -+ wh->wh_type = d_type; -+#endif -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int au_nhash_append_wh(struct au_nhash *whlist, char *name, int nlen, ino_t ino, -+ unsigned int d_type, aufs_bindex_t bindex, -+ unsigned char shwh) -+{ -+ int err; -+ struct au_vdir_destr *str; -+ struct au_vdir_wh *wh; -+ -+ AuDbg("%.*s\n", nlen, name); -+ AuDebugOn(!whlist->nh_num || !whlist->nh_head); -+ -+ err = -ENOMEM; -+ wh = kmalloc(sizeof(*wh) + nlen, GFP_NOFS); -+ if (unlikely(!wh)) -+ goto out; -+ -+ err = 0; -+ wh->wh_bindex = bindex; -+ if (shwh) -+ au_shwh_init_wh(wh, ino, d_type); -+ str = &wh->wh_str; -+ str->len = nlen; -+ memcpy(str->name, name, nlen); -+ hlist_add_head(&wh->wh_hash, au_name_hash(whlist, name, nlen)); -+ /* smp_mb(); */ -+ -+out: -+ return err; -+} -+ -+static int append_deblk(struct au_vdir *vdir) -+{ -+ int err; -+ unsigned long ul; -+ const unsigned int deblk_sz = vdir->vd_deblk_sz; -+ union au_vdir_deblk_p p, deblk_end; -+ unsigned char **o; -+ -+ err = -ENOMEM; -+ o = krealloc(vdir->vd_deblk, sizeof(*o) * (vdir->vd_nblk + 1), -+ GFP_NOFS); -+ if (unlikely(!o)) -+ goto out; -+ -+ vdir->vd_deblk = o; -+ p.deblk = kmalloc(deblk_sz, GFP_NOFS); -+ if (p.deblk) { -+ ul = vdir->vd_nblk++; -+ vdir->vd_deblk[ul] = p.deblk; -+ vdir->vd_last.ul = ul; -+ vdir->vd_last.p.deblk = p.deblk; -+ deblk_end.deblk = p.deblk + deblk_sz; -+ err = set_deblk_end(&p, &deblk_end); -+ } -+ -+out: -+ return err; -+} -+ -+static int append_de(struct au_vdir *vdir, char *name, int nlen, ino_t ino, -+ unsigned int d_type, struct au_nhash *delist) -+{ -+ int err; -+ unsigned int sz; -+ const unsigned int deblk_sz = vdir->vd_deblk_sz; -+ union au_vdir_deblk_p p, *room, deblk_end; -+ struct au_vdir_dehstr *dehstr; -+ -+ p.deblk = last_deblk(vdir); -+ deblk_end.deblk = p.deblk + deblk_sz; -+ room = &vdir->vd_last.p; -+ AuDebugOn(room->deblk < p.deblk || deblk_end.deblk <= room->deblk -+ || !is_deblk_end(room, &deblk_end)); -+ -+ sz = calc_size(nlen); -+ if (unlikely(sz > deblk_end.deblk - room->deblk)) { -+ err = append_deblk(vdir); -+ if (unlikely(err)) -+ goto out; -+ -+ p.deblk = last_deblk(vdir); -+ deblk_end.deblk = p.deblk + deblk_sz; -+ /* smp_mb(); */ -+ AuDebugOn(room->deblk != p.deblk); -+ } -+ -+ err = -ENOMEM; -+ dehstr = au_cache_alloc_vdir_dehstr(); -+ if (unlikely(!dehstr)) -+ goto out; -+ -+ dehstr->str = &room->de->de_str; -+ hlist_add_head(&dehstr->hash, au_name_hash(delist, name, nlen)); -+ room->de->de_ino = ino; -+ room->de->de_type = d_type; -+ room->de->de_str.len = nlen; -+ memcpy(room->de->de_str.name, name, nlen); -+ -+ err = 0; -+ room->deblk += sz; -+ if (unlikely(set_deblk_end(room, &deblk_end))) -+ err = append_deblk(vdir); -+ /* smp_mb(); */ -+ -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void au_vdir_free(struct au_vdir *vdir) -+{ -+ unsigned char **deblk; -+ -+ deblk = vdir->vd_deblk; -+ while (vdir->vd_nblk--) -+ kfree(*deblk++); -+ kfree(vdir->vd_deblk); -+ au_cache_free_vdir(vdir); -+} -+ -+static struct au_vdir *alloc_vdir(struct file *file) -+{ -+ struct au_vdir *vdir; -+ struct super_block *sb; -+ int err; -+ -+ sb = file->f_dentry->d_sb; -+ SiMustAnyLock(sb); -+ -+ err = -ENOMEM; -+ vdir = au_cache_alloc_vdir(); -+ if (unlikely(!vdir)) -+ goto out; -+ -+ vdir->vd_deblk = kzalloc(sizeof(*vdir->vd_deblk), GFP_NOFS); -+ if (unlikely(!vdir->vd_deblk)) -+ goto out_free; -+ -+ vdir->vd_deblk_sz = au_sbi(sb)->si_rdblk; -+ if (!vdir->vd_deblk_sz) { -+ /* estimate the appropriate size for deblk */ -+ vdir->vd_deblk_sz = au_dir_size(file, /*dentry*/NULL); -+ /* pr_info("vd_deblk_sz %u\n", vdir->vd_deblk_sz); */ -+ } -+ vdir->vd_nblk = 0; -+ vdir->vd_version = 0; -+ vdir->vd_jiffy = 0; -+ err = append_deblk(vdir); -+ if (!err) -+ return vdir; /* success */ -+ -+ kfree(vdir->vd_deblk); -+ -+out_free: -+ au_cache_free_vdir(vdir); -+out: -+ vdir = ERR_PTR(err); -+ return vdir; -+} -+ -+static int reinit_vdir(struct au_vdir *vdir) -+{ -+ int err; -+ union au_vdir_deblk_p p, deblk_end; -+ -+ while (vdir->vd_nblk > 1) { -+ kfree(vdir->vd_deblk[vdir->vd_nblk - 1]); -+ /* vdir->vd_deblk[vdir->vd_nblk - 1] = NULL; */ -+ vdir->vd_nblk--; -+ } -+ p.deblk = vdir->vd_deblk[0]; -+ deblk_end.deblk = p.deblk + vdir->vd_deblk_sz; -+ err = set_deblk_end(&p, &deblk_end); -+ /* keep vd_dblk_sz */ -+ vdir->vd_last.ul = 0; -+ vdir->vd_last.p.deblk = vdir->vd_deblk[0]; -+ vdir->vd_version = 0; -+ vdir->vd_jiffy = 0; -+ /* smp_mb(); */ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+#define AuFillVdir_CALLED 1 -+#define AuFillVdir_WHABLE (1 << 1) -+#define AuFillVdir_SHWH (1 << 2) -+#define au_ftest_fillvdir(flags, name) ((flags) & AuFillVdir_##name) -+#define au_fset_fillvdir(flags, name) \ -+ do { (flags) |= AuFillVdir_##name; } while (0) -+#define au_fclr_fillvdir(flags, name) \ -+ do { (flags) &= ~AuFillVdir_##name; } while (0) -+ -+#ifndef CONFIG_AUFS_SHWH -+#undef AuFillVdir_SHWH -+#define AuFillVdir_SHWH 0 -+#endif -+ -+struct fillvdir_arg { -+ struct dir_context ctx; -+ struct file *file; -+ struct au_vdir *vdir; -+ struct au_nhash delist; -+ struct au_nhash whlist; -+ aufs_bindex_t bindex; -+ unsigned int flags; -+ int err; -+}; -+ -+static int fillvdir(struct dir_context *ctx, const char *__name, int nlen, -+ loff_t offset __maybe_unused, u64 h_ino, -+ unsigned int d_type) -+{ -+ struct fillvdir_arg *arg = container_of(ctx, struct fillvdir_arg, ctx); -+ char *name = (void *)__name; -+ struct super_block *sb; -+ ino_t ino; -+ const unsigned char shwh = !!au_ftest_fillvdir(arg->flags, SHWH); -+ -+ arg->err = 0; -+ sb = arg->file->f_dentry->d_sb; -+ au_fset_fillvdir(arg->flags, CALLED); -+ /* smp_mb(); */ -+ if (nlen <= AUFS_WH_PFX_LEN -+ || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { -+ if (test_known(&arg->delist, name, nlen) -+ || au_nhash_test_known_wh(&arg->whlist, name, nlen)) -+ goto out; /* already exists or whiteouted */ -+ -+ arg->err = au_ino(sb, arg->bindex, h_ino, d_type, &ino); -+ if (!arg->err) { -+ if (unlikely(nlen > AUFS_MAX_NAMELEN)) -+ d_type = DT_UNKNOWN; -+ arg->err = append_de(arg->vdir, name, nlen, ino, -+ d_type, &arg->delist); -+ } -+ } else if (au_ftest_fillvdir(arg->flags, WHABLE)) { -+ name += AUFS_WH_PFX_LEN; -+ nlen -= AUFS_WH_PFX_LEN; -+ if (au_nhash_test_known_wh(&arg->whlist, name, nlen)) -+ goto out; /* already whiteouted */ -+ -+ if (shwh) -+ arg->err = au_wh_ino(sb, arg->bindex, h_ino, d_type, -+ &ino); -+ if (!arg->err) { -+ if (nlen <= AUFS_MAX_NAMELEN + AUFS_WH_PFX_LEN) -+ d_type = DT_UNKNOWN; -+ arg->err = au_nhash_append_wh -+ (&arg->whlist, name, nlen, ino, d_type, -+ arg->bindex, shwh); -+ } -+ } -+ -+out: -+ if (!arg->err) -+ arg->vdir->vd_jiffy = jiffies; -+ /* smp_mb(); */ -+ AuTraceErr(arg->err); -+ return arg->err; -+} -+ -+static int au_handle_shwh(struct super_block *sb, struct au_vdir *vdir, -+ struct au_nhash *whlist, struct au_nhash *delist) -+{ -+#ifdef CONFIG_AUFS_SHWH -+ int err; -+ unsigned int nh, u; -+ struct hlist_head *head; -+ struct au_vdir_wh *pos; -+ struct hlist_node *n; -+ char *p, *o; -+ struct au_vdir_destr *destr; -+ -+ AuDebugOn(!au_opt_test(au_mntflags(sb), SHWH)); -+ -+ err = -ENOMEM; -+ o = p = (void *)__get_free_page(GFP_NOFS); -+ if (unlikely(!p)) -+ goto out; -+ -+ err = 0; -+ nh = whlist->nh_num; -+ memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); -+ p += AUFS_WH_PFX_LEN; -+ for (u = 0; u < nh; u++) { -+ head = whlist->nh_head + u; -+ hlist_for_each_entry_safe(pos, n, head, wh_hash) { -+ destr = &pos->wh_str; -+ memcpy(p, destr->name, destr->len); -+ err = append_de(vdir, o, destr->len + AUFS_WH_PFX_LEN, -+ pos->wh_ino, pos->wh_type, delist); -+ if (unlikely(err)) -+ break; -+ } -+ } -+ -+ free_page((unsigned long)o); -+ -+out: -+ AuTraceErr(err); -+ return err; -+#else -+ return 0; -+#endif -+} -+ -+static int au_do_read_vdir(struct fillvdir_arg *arg) -+{ -+ int err; -+ unsigned int rdhash; -+ loff_t offset; -+ aufs_bindex_t bend, bindex, bstart; -+ unsigned char shwh; -+ struct file *hf, *file; -+ struct super_block *sb; -+ -+ file = arg->file; -+ sb = file->f_dentry->d_sb; -+ SiMustAnyLock(sb); -+ -+ rdhash = au_sbi(sb)->si_rdhash; -+ if (!rdhash) -+ rdhash = au_rdhash_est(au_dir_size(file, /*dentry*/NULL)); -+ err = au_nhash_alloc(&arg->delist, rdhash, GFP_NOFS); -+ if (unlikely(err)) -+ goto out; -+ err = au_nhash_alloc(&arg->whlist, rdhash, GFP_NOFS); -+ if (unlikely(err)) -+ goto out_delist; -+ -+ err = 0; -+ arg->flags = 0; -+ shwh = 0; -+ if (au_opt_test(au_mntflags(sb), SHWH)) { -+ shwh = 1; -+ au_fset_fillvdir(arg->flags, SHWH); -+ } -+ bstart = au_fbstart(file); -+ bend = au_fbend_dir(file); -+ for (bindex = bstart; !err && bindex <= bend; bindex++) { -+ hf = au_hf_dir(file, bindex); -+ if (!hf) -+ continue; -+ -+ offset = vfsub_llseek(hf, 0, SEEK_SET); -+ err = offset; -+ if (unlikely(offset)) -+ break; -+ -+ arg->bindex = bindex; -+ au_fclr_fillvdir(arg->flags, WHABLE); -+ if (shwh -+ || (bindex != bend -+ && au_br_whable(au_sbr_perm(sb, bindex)))) -+ au_fset_fillvdir(arg->flags, WHABLE); -+ do { -+ arg->err = 0; -+ au_fclr_fillvdir(arg->flags, CALLED); -+ /* smp_mb(); */ -+ err = vfsub_iterate_dir(hf, &arg->ctx); -+ if (err >= 0) -+ err = arg->err; -+ } while (!err && au_ftest_fillvdir(arg->flags, CALLED)); -+ -+ /* -+ * dir_relax() may be good for concurrency, but aufs should not -+ * use it since it will cause a lockdep problem. -+ */ -+ } -+ -+ if (!err && shwh) -+ err = au_handle_shwh(sb, arg->vdir, &arg->whlist, &arg->delist); -+ -+ au_nhash_wh_free(&arg->whlist); -+ -+out_delist: -+ au_nhash_de_free(&arg->delist); -+out: -+ return err; -+} -+ -+static int read_vdir(struct file *file, int may_read) -+{ -+ int err; -+ unsigned long expire; -+ unsigned char do_read; -+ struct fillvdir_arg arg = { -+ .ctx = { -+ .actor = au_diractor(fillvdir) -+ } -+ }; -+ struct inode *inode; -+ struct au_vdir *vdir, *allocated; -+ -+ err = 0; -+ inode = file_inode(file); -+ IMustLock(inode); -+ SiMustAnyLock(inode->i_sb); -+ -+ allocated = NULL; -+ do_read = 0; -+ expire = au_sbi(inode->i_sb)->si_rdcache; -+ vdir = au_ivdir(inode); -+ if (!vdir) { -+ do_read = 1; -+ vdir = alloc_vdir(file); -+ err = PTR_ERR(vdir); -+ if (IS_ERR(vdir)) -+ goto out; -+ err = 0; -+ allocated = vdir; -+ } else if (may_read -+ && (inode->i_version != vdir->vd_version -+ || time_after(jiffies, vdir->vd_jiffy + expire))) { -+ do_read = 1; -+ err = reinit_vdir(vdir); -+ if (unlikely(err)) -+ goto out; -+ } -+ -+ if (!do_read) -+ return 0; /* success */ -+ -+ arg.file = file; -+ arg.vdir = vdir; -+ err = au_do_read_vdir(&arg); -+ if (!err) { -+ /* file->f_pos = 0; */ /* todo: ctx->pos? */ -+ vdir->vd_version = inode->i_version; -+ vdir->vd_last.ul = 0; -+ vdir->vd_last.p.deblk = vdir->vd_deblk[0]; -+ if (allocated) -+ au_set_ivdir(inode, allocated); -+ } else if (allocated) -+ au_vdir_free(allocated); -+ -+out: -+ return err; -+} -+ -+static int copy_vdir(struct au_vdir *tgt, struct au_vdir *src) -+{ -+ int err, rerr; -+ unsigned long ul, n; -+ const unsigned int deblk_sz = src->vd_deblk_sz; -+ -+ AuDebugOn(tgt->vd_nblk != 1); -+ -+ err = -ENOMEM; -+ if (tgt->vd_nblk < src->vd_nblk) { -+ unsigned char **p; -+ -+ p = krealloc(tgt->vd_deblk, sizeof(*p) * src->vd_nblk, -+ GFP_NOFS); -+ if (unlikely(!p)) -+ goto out; -+ tgt->vd_deblk = p; -+ } -+ -+ if (tgt->vd_deblk_sz != deblk_sz) { -+ unsigned char *p; -+ -+ tgt->vd_deblk_sz = deblk_sz; -+ p = krealloc(tgt->vd_deblk[0], deblk_sz, GFP_NOFS); -+ if (unlikely(!p)) -+ goto out; -+ tgt->vd_deblk[0] = p; -+ } -+ memcpy(tgt->vd_deblk[0], src->vd_deblk[0], deblk_sz); -+ tgt->vd_version = src->vd_version; -+ tgt->vd_jiffy = src->vd_jiffy; -+ -+ n = src->vd_nblk; -+ for (ul = 1; ul < n; ul++) { -+ tgt->vd_deblk[ul] = kmemdup(src->vd_deblk[ul], deblk_sz, -+ GFP_NOFS); -+ if (unlikely(!tgt->vd_deblk[ul])) -+ goto out; -+ tgt->vd_nblk++; -+ } -+ tgt->vd_nblk = n; -+ tgt->vd_last.ul = tgt->vd_last.ul; -+ tgt->vd_last.p.deblk = tgt->vd_deblk[tgt->vd_last.ul]; -+ tgt->vd_last.p.deblk += src->vd_last.p.deblk -+ - src->vd_deblk[src->vd_last.ul]; -+ /* smp_mb(); */ -+ return 0; /* success */ -+ -+out: -+ rerr = reinit_vdir(tgt); -+ BUG_ON(rerr); -+ return err; -+} -+ -+int au_vdir_init(struct file *file) -+{ -+ int err; -+ struct inode *inode; -+ struct au_vdir *vdir_cache, *allocated; -+ -+ /* test file->f_pos here instead of ctx->pos */ -+ err = read_vdir(file, !file->f_pos); -+ if (unlikely(err)) -+ goto out; -+ -+ allocated = NULL; -+ vdir_cache = au_fvdir_cache(file); -+ if (!vdir_cache) { -+ vdir_cache = alloc_vdir(file); -+ err = PTR_ERR(vdir_cache); -+ if (IS_ERR(vdir_cache)) -+ goto out; -+ allocated = vdir_cache; -+ } else if (!file->f_pos && vdir_cache->vd_version != file->f_version) { -+ /* test file->f_pos here instead of ctx->pos */ -+ err = reinit_vdir(vdir_cache); -+ if (unlikely(err)) -+ goto out; -+ } else -+ return 0; /* success */ -+ -+ inode = file_inode(file); -+ err = copy_vdir(vdir_cache, au_ivdir(inode)); -+ if (!err) { -+ file->f_version = inode->i_version; -+ if (allocated) -+ au_set_fvdir_cache(file, allocated); -+ } else if (allocated) -+ au_vdir_free(allocated); -+ -+out: -+ return err; -+} -+ -+static loff_t calc_offset(struct au_vdir *vdir) -+{ -+ loff_t offset; -+ union au_vdir_deblk_p p; -+ -+ p.deblk = vdir->vd_deblk[vdir->vd_last.ul]; -+ offset = vdir->vd_last.p.deblk - p.deblk; -+ offset += vdir->vd_deblk_sz * vdir->vd_last.ul; -+ return offset; -+} -+ -+/* returns true or false */ -+static int seek_vdir(struct file *file, struct dir_context *ctx) -+{ -+ int valid; -+ unsigned int deblk_sz; -+ unsigned long ul, n; -+ loff_t offset; -+ union au_vdir_deblk_p p, deblk_end; -+ struct au_vdir *vdir_cache; -+ -+ valid = 1; -+ vdir_cache = au_fvdir_cache(file); -+ offset = calc_offset(vdir_cache); -+ AuDbg("offset %lld\n", offset); -+ if (ctx->pos == offset) -+ goto out; -+ -+ vdir_cache->vd_last.ul = 0; -+ vdir_cache->vd_last.p.deblk = vdir_cache->vd_deblk[0]; -+ if (!ctx->pos) -+ goto out; -+ -+ valid = 0; -+ deblk_sz = vdir_cache->vd_deblk_sz; -+ ul = div64_u64(ctx->pos, deblk_sz); -+ AuDbg("ul %lu\n", ul); -+ if (ul >= vdir_cache->vd_nblk) -+ goto out; -+ -+ n = vdir_cache->vd_nblk; -+ for (; ul < n; ul++) { -+ p.deblk = vdir_cache->vd_deblk[ul]; -+ deblk_end.deblk = p.deblk + deblk_sz; -+ offset = ul; -+ offset *= deblk_sz; -+ while (!is_deblk_end(&p, &deblk_end) && offset < ctx->pos) { -+ unsigned int l; -+ -+ l = calc_size(p.de->de_str.len); -+ offset += l; -+ p.deblk += l; -+ } -+ if (!is_deblk_end(&p, &deblk_end)) { -+ valid = 1; -+ vdir_cache->vd_last.ul = ul; -+ vdir_cache->vd_last.p = p; -+ break; -+ } -+ } -+ -+out: -+ /* smp_mb(); */ -+ AuTraceErr(!valid); -+ return valid; -+} -+ -+int au_vdir_fill_de(struct file *file, struct dir_context *ctx) -+{ -+ unsigned int l, deblk_sz; -+ union au_vdir_deblk_p deblk_end; -+ struct au_vdir *vdir_cache; -+ struct au_vdir_de *de; -+ -+ vdir_cache = au_fvdir_cache(file); -+ if (!seek_vdir(file, ctx)) -+ return 0; -+ -+ deblk_sz = vdir_cache->vd_deblk_sz; -+ while (1) { -+ deblk_end.deblk = vdir_cache->vd_deblk[vdir_cache->vd_last.ul]; -+ deblk_end.deblk += deblk_sz; -+ while (!is_deblk_end(&vdir_cache->vd_last.p, &deblk_end)) { -+ de = vdir_cache->vd_last.p.de; -+ AuDbg("%.*s, off%lld, i%lu, dt%d\n", -+ de->de_str.len, de->de_str.name, ctx->pos, -+ (unsigned long)de->de_ino, de->de_type); -+ if (unlikely(!dir_emit(ctx, de->de_str.name, -+ de->de_str.len, de->de_ino, -+ de->de_type))) { -+ /* todo: ignore the error caused by udba? */ -+ /* return err; */ -+ return 0; -+ } -+ -+ l = calc_size(de->de_str.len); -+ vdir_cache->vd_last.p.deblk += l; -+ ctx->pos += l; -+ } -+ if (vdir_cache->vd_last.ul < vdir_cache->vd_nblk - 1) { -+ vdir_cache->vd_last.ul++; -+ vdir_cache->vd_last.p.deblk -+ = vdir_cache->vd_deblk[vdir_cache->vd_last.ul]; -+ ctx->pos = deblk_sz * vdir_cache->vd_last.ul; -+ continue; -+ } -+ break; -+ } -+ -+ /* smp_mb(); */ -+ return 0; -+} -diff --git a/fs/aufs/vfsub.c b/fs/aufs/vfsub.c -new file mode 100644 -index 0000000..5fd008c ---- /dev/null -+++ b/fs/aufs/vfsub.c -@@ -0,0 +1,864 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * sub-routines for VFS -+ */ -+ -+#include -+#include -+#include -+#include -+#include "../fs/mount.h" -+#include "aufs.h" -+ -+#ifdef CONFIG_AUFS_BR_FUSE -+int vfsub_test_mntns(struct vfsmount *mnt, struct super_block *h_sb) -+{ -+ struct nsproxy *ns; -+ -+ if (!au_test_fuse(h_sb) || !au_userns) -+ return 0; -+ -+ ns = current->nsproxy; -+ /* no {get,put}_nsproxy(ns) */ -+ return real_mount(mnt)->mnt_ns == ns->mnt_ns ? 0 : -EACCES; -+} -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+int vfsub_update_h_iattr(struct path *h_path, int *did) -+{ -+ int err; -+ struct kstat st; -+ struct super_block *h_sb; -+ -+ /* for remote fs, leave work for its getattr or d_revalidate */ -+ /* for bad i_attr fs, handle them in aufs_getattr() */ -+ /* still some fs may acquire i_mutex. we need to skip them */ -+ err = 0; -+ if (!did) -+ did = &err; -+ h_sb = h_path->dentry->d_sb; -+ *did = (!au_test_fs_remote(h_sb) && au_test_fs_refresh_iattr(h_sb)); -+ if (*did) -+ err = vfs_getattr(h_path, &st); -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct file *vfsub_dentry_open(struct path *path, int flags) -+{ -+ struct file *file; -+ -+ file = dentry_open(path, flags /* | __FMODE_NONOTIFY */, -+ current_cred()); -+ if (!IS_ERR_OR_NULL(file) -+ && (file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) -+ i_readcount_inc(path->dentry->d_inode); -+ -+ return file; -+} -+ -+struct file *vfsub_filp_open(const char *path, int oflags, int mode) -+{ -+ struct file *file; -+ -+ lockdep_off(); -+ file = filp_open(path, -+ oflags /* | __FMODE_NONOTIFY */, -+ mode); -+ lockdep_on(); -+ if (IS_ERR(file)) -+ goto out; -+ vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ -+ -+out: -+ return file; -+} -+ -+/* -+ * Ideally this function should call VFS:do_last() in order to keep all its -+ * checkings. But it is very hard for aufs to regenerate several VFS internal -+ * structure such as nameidata. This is a second (or third) best approach. -+ * cf. linux/fs/namei.c:do_last(), lookup_open() and atomic_open(). -+ */ -+int vfsub_atomic_open(struct inode *dir, struct dentry *dentry, -+ struct vfsub_aopen_args *args, struct au_branch *br) -+{ -+ int err; -+ struct file *file = args->file; -+ /* copied from linux/fs/namei.c:atomic_open() */ -+ struct dentry *const DENTRY_NOT_SET = (void *)-1UL; -+ -+ IMustLock(dir); -+ AuDebugOn(!dir->i_op->atomic_open); -+ -+ err = au_br_test_oflag(args->open_flag, br); -+ if (unlikely(err)) -+ goto out; -+ -+ args->file->f_path.dentry = DENTRY_NOT_SET; -+ args->file->f_path.mnt = au_br_mnt(br); -+ err = dir->i_op->atomic_open(dir, dentry, file, args->open_flag, -+ args->create_mode, args->opened); -+ if (err >= 0) { -+ /* some filesystems don't set FILE_CREATED while succeeded? */ -+ if (*args->opened & FILE_CREATED) -+ fsnotify_create(dir, dentry); -+ } else -+ goto out; -+ -+ -+ if (!err) { -+ /* todo: call VFS:may_open() here */ -+ err = open_check_o_direct(file); -+ /* todo: ima_file_check() too? */ -+ if (!err && (args->open_flag & __FMODE_EXEC)) -+ err = deny_write_access(file); -+ if (unlikely(err)) -+ /* note that the file is created and still opened */ -+ goto out; -+ } -+ -+ atomic_inc(&br->br_count); -+ fsnotify_open(file); -+ -+out: -+ return err; -+} -+ -+int vfsub_kern_path(const char *name, unsigned int flags, struct path *path) -+{ -+ int err; -+ -+ err = kern_path(name, flags, path); -+ if (!err && path->dentry->d_inode) -+ vfsub_update_h_iattr(path, /*did*/NULL); /*ignore*/ -+ return err; -+} -+ -+struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent, -+ int len) -+{ -+ struct path path = { -+ .mnt = NULL -+ }; -+ -+ /* VFS checks it too, but by WARN_ON_ONCE() */ -+ IMustLock(parent->d_inode); -+ -+ path.dentry = lookup_one_len(name, parent, len); -+ if (IS_ERR(path.dentry)) -+ goto out; -+ if (path.dentry->d_inode) -+ vfsub_update_h_iattr(&path, /*did*/NULL); /*ignore*/ -+ -+out: -+ AuTraceErrPtr(path.dentry); -+ return path.dentry; -+} -+ -+void vfsub_call_lkup_one(void *args) -+{ -+ struct vfsub_lkup_one_args *a = args; -+ *a->errp = vfsub_lkup_one(a->name, a->parent); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct dentry *vfsub_lock_rename(struct dentry *d1, struct au_hinode *hdir1, -+ struct dentry *d2, struct au_hinode *hdir2) -+{ -+ struct dentry *d; -+ -+ lockdep_off(); -+ d = lock_rename(d1, d2); -+ lockdep_on(); -+ au_hn_suspend(hdir1); -+ if (hdir1 != hdir2) -+ au_hn_suspend(hdir2); -+ -+ return d; -+} -+ -+void vfsub_unlock_rename(struct dentry *d1, struct au_hinode *hdir1, -+ struct dentry *d2, struct au_hinode *hdir2) -+{ -+ au_hn_resume(hdir1); -+ if (hdir1 != hdir2) -+ au_hn_resume(hdir2); -+ lockdep_off(); -+ unlock_rename(d1, d2); -+ lockdep_on(); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int vfsub_create(struct inode *dir, struct path *path, int mode, bool want_excl) -+{ -+ int err; -+ struct dentry *d; -+ -+ IMustLock(dir); -+ -+ d = path->dentry; -+ path->dentry = d->d_parent; -+ err = security_path_mknod(path, d, mode, 0); -+ path->dentry = d; -+ if (unlikely(err)) -+ goto out; -+ -+ lockdep_off(); -+ err = vfs_create(dir, path->dentry, mode, want_excl); -+ lockdep_on(); -+ if (!err) { -+ struct path tmp = *path; -+ int did; -+ -+ vfsub_update_h_iattr(&tmp, &did); -+ if (did) { -+ tmp.dentry = path->dentry->d_parent; -+ vfsub_update_h_iattr(&tmp, /*did*/NULL); -+ } -+ /*ignore*/ -+ } -+ -+out: -+ return err; -+} -+ -+int vfsub_symlink(struct inode *dir, struct path *path, const char *symname) -+{ -+ int err; -+ struct dentry *d; -+ -+ IMustLock(dir); -+ -+ d = path->dentry; -+ path->dentry = d->d_parent; -+ err = security_path_symlink(path, d, symname); -+ path->dentry = d; -+ if (unlikely(err)) -+ goto out; -+ -+ lockdep_off(); -+ err = vfs_symlink(dir, path->dentry, symname); -+ lockdep_on(); -+ if (!err) { -+ struct path tmp = *path; -+ int did; -+ -+ vfsub_update_h_iattr(&tmp, &did); -+ if (did) { -+ tmp.dentry = path->dentry->d_parent; -+ vfsub_update_h_iattr(&tmp, /*did*/NULL); -+ } -+ /*ignore*/ -+ } -+ -+out: -+ return err; -+} -+ -+int vfsub_mknod(struct inode *dir, struct path *path, int mode, dev_t dev) -+{ -+ int err; -+ struct dentry *d; -+ -+ IMustLock(dir); -+ -+ d = path->dentry; -+ path->dentry = d->d_parent; -+ err = security_path_mknod(path, d, mode, new_encode_dev(dev)); -+ path->dentry = d; -+ if (unlikely(err)) -+ goto out; -+ -+ lockdep_off(); -+ err = vfs_mknod(dir, path->dentry, mode, dev); -+ lockdep_on(); -+ if (!err) { -+ struct path tmp = *path; -+ int did; -+ -+ vfsub_update_h_iattr(&tmp, &did); -+ if (did) { -+ tmp.dentry = path->dentry->d_parent; -+ vfsub_update_h_iattr(&tmp, /*did*/NULL); -+ } -+ /*ignore*/ -+ } -+ -+out: -+ return err; -+} -+ -+static int au_test_nlink(struct inode *inode) -+{ -+ const unsigned int link_max = UINT_MAX >> 1; /* rough margin */ -+ -+ if (!au_test_fs_no_limit_nlink(inode->i_sb) -+ || inode->i_nlink < link_max) -+ return 0; -+ return -EMLINK; -+} -+ -+int vfsub_link(struct dentry *src_dentry, struct inode *dir, struct path *path, -+ struct inode **delegated_inode) -+{ -+ int err; -+ struct dentry *d; -+ -+ IMustLock(dir); -+ -+ err = au_test_nlink(src_dentry->d_inode); -+ if (unlikely(err)) -+ return err; -+ -+ /* we don't call may_linkat() */ -+ d = path->dentry; -+ path->dentry = d->d_parent; -+ err = security_path_link(src_dentry, path, d); -+ path->dentry = d; -+ if (unlikely(err)) -+ goto out; -+ -+ lockdep_off(); -+ err = vfs_link(src_dentry, dir, path->dentry, delegated_inode); -+ lockdep_on(); -+ if (!err) { -+ struct path tmp = *path; -+ int did; -+ -+ /* fuse has different memory inode for the same inumber */ -+ vfsub_update_h_iattr(&tmp, &did); -+ if (did) { -+ tmp.dentry = path->dentry->d_parent; -+ vfsub_update_h_iattr(&tmp, /*did*/NULL); -+ tmp.dentry = src_dentry; -+ vfsub_update_h_iattr(&tmp, /*did*/NULL); -+ } -+ /*ignore*/ -+ } -+ -+out: -+ return err; -+} -+ -+int vfsub_rename(struct inode *src_dir, struct dentry *src_dentry, -+ struct inode *dir, struct path *path, -+ struct inode **delegated_inode) -+{ -+ int err; -+ struct path tmp = { -+ .mnt = path->mnt -+ }; -+ struct dentry *d; -+ -+ IMustLock(dir); -+ IMustLock(src_dir); -+ -+ d = path->dentry; -+ path->dentry = d->d_parent; -+ tmp.dentry = src_dentry->d_parent; -+ err = security_path_rename(&tmp, src_dentry, path, d, /*flags*/0); -+ path->dentry = d; -+ if (unlikely(err)) -+ goto out; -+ -+ lockdep_off(); -+ err = vfs_rename(src_dir, src_dentry, dir, path->dentry, -+ delegated_inode, /*flags*/0); -+ lockdep_on(); -+ if (!err) { -+ int did; -+ -+ tmp.dentry = d->d_parent; -+ vfsub_update_h_iattr(&tmp, &did); -+ if (did) { -+ tmp.dentry = src_dentry; -+ vfsub_update_h_iattr(&tmp, /*did*/NULL); -+ tmp.dentry = src_dentry->d_parent; -+ vfsub_update_h_iattr(&tmp, /*did*/NULL); -+ } -+ /*ignore*/ -+ } -+ -+out: -+ return err; -+} -+ -+int vfsub_mkdir(struct inode *dir, struct path *path, int mode) -+{ -+ int err; -+ struct dentry *d; -+ -+ IMustLock(dir); -+ -+ d = path->dentry; -+ path->dentry = d->d_parent; -+ err = security_path_mkdir(path, d, mode); -+ path->dentry = d; -+ if (unlikely(err)) -+ goto out; -+ -+ lockdep_off(); -+ err = vfs_mkdir(dir, path->dentry, mode); -+ lockdep_on(); -+ if (!err) { -+ struct path tmp = *path; -+ int did; -+ -+ vfsub_update_h_iattr(&tmp, &did); -+ if (did) { -+ tmp.dentry = path->dentry->d_parent; -+ vfsub_update_h_iattr(&tmp, /*did*/NULL); -+ } -+ /*ignore*/ -+ } -+ -+out: -+ return err; -+} -+ -+int vfsub_rmdir(struct inode *dir, struct path *path) -+{ -+ int err; -+ struct dentry *d; -+ -+ IMustLock(dir); -+ -+ d = path->dentry; -+ path->dentry = d->d_parent; -+ err = security_path_rmdir(path, d); -+ path->dentry = d; -+ if (unlikely(err)) -+ goto out; -+ -+ lockdep_off(); -+ err = vfs_rmdir(dir, path->dentry); -+ lockdep_on(); -+ if (!err) { -+ struct path tmp = { -+ .dentry = path->dentry->d_parent, -+ .mnt = path->mnt -+ }; -+ -+ vfsub_update_h_iattr(&tmp, /*did*/NULL); /*ignore*/ -+ } -+ -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* todo: support mmap_sem? */ -+ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count, -+ loff_t *ppos) -+{ -+ ssize_t err; -+ -+ lockdep_off(); -+ err = vfs_read(file, ubuf, count, ppos); -+ lockdep_on(); -+ if (err >= 0) -+ vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ -+ return err; -+} -+ -+/* todo: kernel_read()? */ -+ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count, -+ loff_t *ppos) -+{ -+ ssize_t err; -+ mm_segment_t oldfs; -+ union { -+ void *k; -+ char __user *u; -+ } buf; -+ -+ buf.k = kbuf; -+ oldfs = get_fs(); -+ set_fs(KERNEL_DS); -+ err = vfsub_read_u(file, buf.u, count, ppos); -+ set_fs(oldfs); -+ return err; -+} -+ -+ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count, -+ loff_t *ppos) -+{ -+ ssize_t err; -+ -+ lockdep_off(); -+ err = vfs_write(file, ubuf, count, ppos); -+ lockdep_on(); -+ if (err >= 0) -+ vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ -+ return err; -+} -+ -+ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, loff_t *ppos) -+{ -+ ssize_t err; -+ mm_segment_t oldfs; -+ union { -+ void *k; -+ const char __user *u; -+ } buf; -+ -+ buf.k = kbuf; -+ oldfs = get_fs(); -+ set_fs(KERNEL_DS); -+ err = vfsub_write_u(file, buf.u, count, ppos); -+ set_fs(oldfs); -+ return err; -+} -+ -+int vfsub_flush(struct file *file, fl_owner_t id) -+{ -+ int err; -+ -+ err = 0; -+ if (file->f_op->flush) { -+ if (!au_test_nfs(file->f_dentry->d_sb)) -+ err = file->f_op->flush(file, id); -+ else { -+ lockdep_off(); -+ err = file->f_op->flush(file, id); -+ lockdep_on(); -+ } -+ if (!err) -+ vfsub_update_h_iattr(&file->f_path, /*did*/NULL); -+ /*ignore*/ -+ } -+ return err; -+} -+ -+int vfsub_iterate_dir(struct file *file, struct dir_context *ctx) -+{ -+ int err; -+ -+ AuDbg("%pD, ctx{%pf, %llu}\n", file, ctx->actor, ctx->pos); -+ -+ lockdep_off(); -+ err = iterate_dir(file, ctx); -+ lockdep_on(); -+ if (err >= 0) -+ vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ -+ return err; -+} -+ -+long vfsub_splice_to(struct file *in, loff_t *ppos, -+ struct pipe_inode_info *pipe, size_t len, -+ unsigned int flags) -+{ -+ long err; -+ -+ lockdep_off(); -+ err = do_splice_to(in, ppos, pipe, len, flags); -+ lockdep_on(); -+ file_accessed(in); -+ if (err >= 0) -+ vfsub_update_h_iattr(&in->f_path, /*did*/NULL); /*ignore*/ -+ return err; -+} -+ -+long vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out, -+ loff_t *ppos, size_t len, unsigned int flags) -+{ -+ long err; -+ -+ lockdep_off(); -+ err = do_splice_from(pipe, out, ppos, len, flags); -+ lockdep_on(); -+ if (err >= 0) -+ vfsub_update_h_iattr(&out->f_path, /*did*/NULL); /*ignore*/ -+ return err; -+} -+ -+int vfsub_fsync(struct file *file, struct path *path, int datasync) -+{ -+ int err; -+ -+ /* file can be NULL */ -+ lockdep_off(); -+ err = vfs_fsync(file, datasync); -+ lockdep_on(); -+ if (!err) { -+ if (!path) { -+ AuDebugOn(!file); -+ path = &file->f_path; -+ } -+ vfsub_update_h_iattr(path, /*did*/NULL); /*ignore*/ -+ } -+ return err; -+} -+ -+/* cf. open.c:do_sys_truncate() and do_sys_ftruncate() */ -+int vfsub_trunc(struct path *h_path, loff_t length, unsigned int attr, -+ struct file *h_file) -+{ -+ int err; -+ struct inode *h_inode; -+ struct super_block *h_sb; -+ -+ if (!h_file) { -+ err = vfsub_truncate(h_path, length); -+ goto out; -+ } -+ -+ h_inode = h_path->dentry->d_inode; -+ h_sb = h_inode->i_sb; -+ lockdep_off(); -+ sb_start_write(h_sb); -+ lockdep_on(); -+ err = locks_verify_truncate(h_inode, h_file, length); -+ if (!err) -+ err = security_path_truncate(h_path); -+ if (!err) { -+ lockdep_off(); -+ err = do_truncate(h_path->dentry, length, attr, h_file); -+ lockdep_on(); -+ } -+ lockdep_off(); -+ sb_end_write(h_sb); -+ lockdep_on(); -+ -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct au_vfsub_mkdir_args { -+ int *errp; -+ struct inode *dir; -+ struct path *path; -+ int mode; -+}; -+ -+static void au_call_vfsub_mkdir(void *args) -+{ -+ struct au_vfsub_mkdir_args *a = args; -+ *a->errp = vfsub_mkdir(a->dir, a->path, a->mode); -+} -+ -+int vfsub_sio_mkdir(struct inode *dir, struct path *path, int mode) -+{ -+ int err, do_sio, wkq_err; -+ -+ do_sio = au_test_h_perm_sio(dir, MAY_EXEC | MAY_WRITE); -+ if (!do_sio) { -+ lockdep_off(); -+ err = vfsub_mkdir(dir, path, mode); -+ lockdep_on(); -+ } else { -+ struct au_vfsub_mkdir_args args = { -+ .errp = &err, -+ .dir = dir, -+ .path = path, -+ .mode = mode -+ }; -+ wkq_err = au_wkq_wait(au_call_vfsub_mkdir, &args); -+ if (unlikely(wkq_err)) -+ err = wkq_err; -+ } -+ -+ return err; -+} -+ -+struct au_vfsub_rmdir_args { -+ int *errp; -+ struct inode *dir; -+ struct path *path; -+}; -+ -+static void au_call_vfsub_rmdir(void *args) -+{ -+ struct au_vfsub_rmdir_args *a = args; -+ *a->errp = vfsub_rmdir(a->dir, a->path); -+} -+ -+int vfsub_sio_rmdir(struct inode *dir, struct path *path) -+{ -+ int err, do_sio, wkq_err; -+ -+ do_sio = au_test_h_perm_sio(dir, MAY_EXEC | MAY_WRITE); -+ if (!do_sio) { -+ lockdep_off(); -+ err = vfsub_rmdir(dir, path); -+ lockdep_on(); -+ } else { -+ struct au_vfsub_rmdir_args args = { -+ .errp = &err, -+ .dir = dir, -+ .path = path -+ }; -+ wkq_err = au_wkq_wait(au_call_vfsub_rmdir, &args); -+ if (unlikely(wkq_err)) -+ err = wkq_err; -+ } -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct notify_change_args { -+ int *errp; -+ struct path *path; -+ struct iattr *ia; -+ struct inode **delegated_inode; -+}; -+ -+static void call_notify_change(void *args) -+{ -+ struct notify_change_args *a = args; -+ struct inode *h_inode; -+ -+ h_inode = a->path->dentry->d_inode; -+ IMustLock(h_inode); -+ -+ *a->errp = -EPERM; -+ if (!IS_IMMUTABLE(h_inode) && !IS_APPEND(h_inode)) { -+ lockdep_off(); -+ *a->errp = notify_change(a->path->dentry, a->ia, -+ a->delegated_inode); -+ lockdep_on(); -+ if (!*a->errp) -+ vfsub_update_h_iattr(a->path, /*did*/NULL); /*ignore*/ -+ } -+ AuTraceErr(*a->errp); -+} -+ -+int vfsub_notify_change(struct path *path, struct iattr *ia, -+ struct inode **delegated_inode) -+{ -+ int err; -+ struct notify_change_args args = { -+ .errp = &err, -+ .path = path, -+ .ia = ia, -+ .delegated_inode = delegated_inode -+ }; -+ -+ call_notify_change(&args); -+ -+ return err; -+} -+ -+int vfsub_sio_notify_change(struct path *path, struct iattr *ia, -+ struct inode **delegated_inode) -+{ -+ int err, wkq_err; -+ struct notify_change_args args = { -+ .errp = &err, -+ .path = path, -+ .ia = ia, -+ .delegated_inode = delegated_inode -+ }; -+ -+ wkq_err = au_wkq_wait(call_notify_change, &args); -+ if (unlikely(wkq_err)) -+ err = wkq_err; -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct unlink_args { -+ int *errp; -+ struct inode *dir; -+ struct path *path; -+ struct inode **delegated_inode; -+}; -+ -+static void call_unlink(void *args) -+{ -+ struct unlink_args *a = args; -+ struct dentry *d = a->path->dentry; -+ struct inode *h_inode; -+ const int stop_sillyrename = (au_test_nfs(d->d_sb) -+ && au_dcount(d) == 1); -+ -+ IMustLock(a->dir); -+ -+ a->path->dentry = d->d_parent; -+ *a->errp = security_path_unlink(a->path, d); -+ a->path->dentry = d; -+ if (unlikely(*a->errp)) -+ return; -+ -+ if (!stop_sillyrename) -+ dget(d); -+ h_inode = d->d_inode; -+ if (h_inode) -+ ihold(h_inode); -+ -+ lockdep_off(); -+ *a->errp = vfs_unlink(a->dir, d, a->delegated_inode); -+ lockdep_on(); -+ if (!*a->errp) { -+ struct path tmp = { -+ .dentry = d->d_parent, -+ .mnt = a->path->mnt -+ }; -+ vfsub_update_h_iattr(&tmp, /*did*/NULL); /*ignore*/ -+ } -+ -+ if (!stop_sillyrename) -+ dput(d); -+ if (h_inode) -+ iput(h_inode); -+ -+ AuTraceErr(*a->errp); -+} -+ -+/* -+ * @dir: must be locked. -+ * @dentry: target dentry. -+ */ -+int vfsub_unlink(struct inode *dir, struct path *path, -+ struct inode **delegated_inode, int force) -+{ -+ int err; -+ struct unlink_args args = { -+ .errp = &err, -+ .dir = dir, -+ .path = path, -+ .delegated_inode = delegated_inode -+ }; -+ -+ if (!force) -+ call_unlink(&args); -+ else { -+ int wkq_err; -+ -+ wkq_err = au_wkq_wait(call_unlink, &args); -+ if (unlikely(wkq_err)) -+ err = wkq_err; -+ } -+ -+ return err; -+} -diff --git a/fs/aufs/vfsub.h b/fs/aufs/vfsub.h -new file mode 100644 -index 0000000..2c33298 ---- /dev/null -+++ b/fs/aufs/vfsub.h -@@ -0,0 +1,315 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * sub-routines for VFS -+ */ -+ -+#ifndef __AUFS_VFSUB_H__ -+#define __AUFS_VFSUB_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+#include -+#include -+#include -+#include "debug.h" -+ -+/* copied from linux/fs/internal.h */ -+/* todo: BAD approach!! */ -+extern void __mnt_drop_write(struct vfsmount *); -+extern spinlock_t inode_sb_list_lock; -+extern int open_check_o_direct(struct file *f); -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* lock subclass for lower inode */ -+/* default MAX_LOCKDEP_SUBCLASSES(8) is not enough */ -+/* reduce? gave up. */ -+enum { -+ AuLsc_I_Begin = I_MUTEX_PARENT2, /* 5 */ -+ AuLsc_I_PARENT, /* lower inode, parent first */ -+ AuLsc_I_PARENT2, /* copyup dirs */ -+ AuLsc_I_PARENT3, /* copyup wh */ -+ AuLsc_I_CHILD, -+ AuLsc_I_CHILD2, -+ AuLsc_I_End -+}; -+ -+/* to debug easier, do not make them inlined functions */ -+#define MtxMustLock(mtx) AuDebugOn(!mutex_is_locked(mtx)) -+#define IMustLock(i) MtxMustLock(&(i)->i_mutex) -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline void vfsub_drop_nlink(struct inode *inode) -+{ -+ AuDebugOn(!inode->i_nlink); -+ drop_nlink(inode); -+} -+ -+static inline void vfsub_dead_dir(struct inode *inode) -+{ -+ AuDebugOn(!S_ISDIR(inode->i_mode)); -+ inode->i_flags |= S_DEAD; -+ clear_nlink(inode); -+} -+ -+static inline int vfsub_native_ro(struct inode *inode) -+{ -+ return (inode->i_sb->s_flags & MS_RDONLY) -+ || IS_RDONLY(inode) -+ /* || IS_APPEND(inode) */ -+ || IS_IMMUTABLE(inode); -+} -+ -+#ifdef CONFIG_AUFS_BR_FUSE -+int vfsub_test_mntns(struct vfsmount *mnt, struct super_block *h_sb); -+#else -+AuStubInt0(vfsub_test_mntns, struct vfsmount *mnt, struct super_block *h_sb); -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+int vfsub_update_h_iattr(struct path *h_path, int *did); -+struct file *vfsub_dentry_open(struct path *path, int flags); -+struct file *vfsub_filp_open(const char *path, int oflags, int mode); -+struct vfsub_aopen_args { -+ struct file *file; -+ unsigned int open_flag; -+ umode_t create_mode; -+ int *opened; -+}; -+struct au_branch; -+int vfsub_atomic_open(struct inode *dir, struct dentry *dentry, -+ struct vfsub_aopen_args *args, struct au_branch *br); -+int vfsub_kern_path(const char *name, unsigned int flags, struct path *path); -+ -+struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent, -+ int len); -+ -+struct vfsub_lkup_one_args { -+ struct dentry **errp; -+ struct qstr *name; -+ struct dentry *parent; -+}; -+ -+static inline struct dentry *vfsub_lkup_one(struct qstr *name, -+ struct dentry *parent) -+{ -+ return vfsub_lookup_one_len(name->name, parent, name->len); -+} -+ -+void vfsub_call_lkup_one(void *args); -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline int vfsub_mnt_want_write(struct vfsmount *mnt) -+{ -+ int err; -+ -+ lockdep_off(); -+ err = mnt_want_write(mnt); -+ lockdep_on(); -+ return err; -+} -+ -+static inline void vfsub_mnt_drop_write(struct vfsmount *mnt) -+{ -+ lockdep_off(); -+ mnt_drop_write(mnt); -+ lockdep_on(); -+} -+ -+#if 0 /* reserved */ -+static inline void vfsub_mnt_drop_write_file(struct file *file) -+{ -+ lockdep_off(); -+ mnt_drop_write_file(file); -+ lockdep_on(); -+} -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct au_hinode; -+struct dentry *vfsub_lock_rename(struct dentry *d1, struct au_hinode *hdir1, -+ struct dentry *d2, struct au_hinode *hdir2); -+void vfsub_unlock_rename(struct dentry *d1, struct au_hinode *hdir1, -+ struct dentry *d2, struct au_hinode *hdir2); -+ -+int vfsub_create(struct inode *dir, struct path *path, int mode, -+ bool want_excl); -+int vfsub_symlink(struct inode *dir, struct path *path, -+ const char *symname); -+int vfsub_mknod(struct inode *dir, struct path *path, int mode, dev_t dev); -+int vfsub_link(struct dentry *src_dentry, struct inode *dir, -+ struct path *path, struct inode **delegated_inode); -+int vfsub_rename(struct inode *src_hdir, struct dentry *src_dentry, -+ struct inode *hdir, struct path *path, -+ struct inode **delegated_inode); -+int vfsub_mkdir(struct inode *dir, struct path *path, int mode); -+int vfsub_rmdir(struct inode *dir, struct path *path); -+ -+/* ---------------------------------------------------------------------- */ -+ -+ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count, -+ loff_t *ppos); -+ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count, -+ loff_t *ppos); -+ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count, -+ loff_t *ppos); -+ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, -+ loff_t *ppos); -+int vfsub_flush(struct file *file, fl_owner_t id); -+int vfsub_iterate_dir(struct file *file, struct dir_context *ctx); -+ -+/* just for type-check */ -+static inline filldir_t au_diractor(int (*func)(struct dir_context *, -+ const char *, int, loff_t, u64, -+ unsigned)) -+{ -+ return (filldir_t)func; -+} -+ -+static inline loff_t vfsub_f_size_read(struct file *file) -+{ -+ return i_size_read(file_inode(file)); -+} -+ -+static inline unsigned int vfsub_file_flags(struct file *file) -+{ -+ unsigned int flags; -+ -+ spin_lock(&file->f_lock); -+ flags = file->f_flags; -+ spin_unlock(&file->f_lock); -+ -+ return flags; -+} -+ -+#if 0 /* reserved */ -+static inline void vfsub_file_accessed(struct file *h_file) -+{ -+ file_accessed(h_file); -+ vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL); /*ignore*/ -+} -+#endif -+ -+static inline void vfsub_touch_atime(struct vfsmount *h_mnt, -+ struct dentry *h_dentry) -+{ -+ struct path h_path = { -+ .dentry = h_dentry, -+ .mnt = h_mnt -+ }; -+ touch_atime(&h_path); -+ vfsub_update_h_iattr(&h_path, /*did*/NULL); /*ignore*/ -+} -+ -+static inline int vfsub_update_time(struct inode *h_inode, struct timespec *ts, -+ int flags) -+{ -+ return update_time(h_inode, ts, flags); -+ /* no vfsub_update_h_iattr() since we don't have struct path */ -+} -+ -+#ifdef CONFIG_FS_POSIX_ACL -+static inline int vfsub_acl_chmod(struct inode *h_inode, umode_t h_mode) -+{ -+ int err; -+ -+ err = posix_acl_chmod(h_inode, h_mode); -+ if (err == -EOPNOTSUPP) -+ err = 0; -+ return err; -+} -+#else -+AuStubInt0(vfsub_acl_chmod, struct inode *h_inode, umode_t h_mode); -+#endif -+ -+long vfsub_splice_to(struct file *in, loff_t *ppos, -+ struct pipe_inode_info *pipe, size_t len, -+ unsigned int flags); -+long vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out, -+ loff_t *ppos, size_t len, unsigned int flags); -+ -+static inline long vfsub_truncate(struct path *path, loff_t length) -+{ -+ long err; -+ -+ lockdep_off(); -+ err = vfs_truncate(path, length); -+ lockdep_on(); -+ return err; -+} -+ -+int vfsub_trunc(struct path *h_path, loff_t length, unsigned int attr, -+ struct file *h_file); -+int vfsub_fsync(struct file *file, struct path *path, int datasync); -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline loff_t vfsub_llseek(struct file *file, loff_t offset, int origin) -+{ -+ loff_t err; -+ -+ lockdep_off(); -+ err = vfs_llseek(file, offset, origin); -+ lockdep_on(); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int vfsub_sio_mkdir(struct inode *dir, struct path *path, int mode); -+int vfsub_sio_rmdir(struct inode *dir, struct path *path); -+int vfsub_sio_notify_change(struct path *path, struct iattr *ia, -+ struct inode **delegated_inode); -+int vfsub_notify_change(struct path *path, struct iattr *ia, -+ struct inode **delegated_inode); -+int vfsub_unlink(struct inode *dir, struct path *path, -+ struct inode **delegated_inode, int force); -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline int vfsub_setxattr(struct dentry *dentry, const char *name, -+ const void *value, size_t size, int flags) -+{ -+ int err; -+ -+ lockdep_off(); -+ err = vfs_setxattr(dentry, name, value, size, flags); -+ lockdep_on(); -+ -+ return err; -+} -+ -+static inline int vfsub_removexattr(struct dentry *dentry, const char *name) -+{ -+ int err; -+ -+ lockdep_off(); -+ err = vfs_removexattr(dentry, name); -+ lockdep_on(); -+ -+ return err; -+} -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_VFSUB_H__ */ -diff --git a/fs/aufs/wbr_policy.c b/fs/aufs/wbr_policy.c -new file mode 100644 -index 0000000..64cd9fe ---- /dev/null -+++ b/fs/aufs/wbr_policy.c -@@ -0,0 +1,765 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * policies for selecting one among multiple writable branches -+ */ -+ -+#include -+#include "aufs.h" -+ -+/* subset of cpup_attr() */ -+static noinline_for_stack -+int au_cpdown_attr(struct path *h_path, struct dentry *h_src) -+{ -+ int err, sbits; -+ struct iattr ia; -+ struct inode *h_isrc; -+ -+ h_isrc = h_src->d_inode; -+ ia.ia_valid = ATTR_FORCE | ATTR_MODE | ATTR_UID | ATTR_GID; -+ ia.ia_mode = h_isrc->i_mode; -+ ia.ia_uid = h_isrc->i_uid; -+ ia.ia_gid = h_isrc->i_gid; -+ sbits = !!(ia.ia_mode & (S_ISUID | S_ISGID)); -+ au_cpup_attr_flags(h_path->dentry->d_inode, h_isrc->i_flags); -+ /* no delegation since it is just created */ -+ err = vfsub_sio_notify_change(h_path, &ia, /*delegated*/NULL); -+ -+ /* is this nfs only? */ -+ if (!err && sbits && au_test_nfs(h_path->dentry->d_sb)) { -+ ia.ia_valid = ATTR_FORCE | ATTR_MODE; -+ ia.ia_mode = h_isrc->i_mode; -+ err = vfsub_sio_notify_change(h_path, &ia, /*delegated*/NULL); -+ } -+ -+ return err; -+} -+ -+#define AuCpdown_PARENT_OPQ 1 -+#define AuCpdown_WHED (1 << 1) -+#define AuCpdown_MADE_DIR (1 << 2) -+#define AuCpdown_DIROPQ (1 << 3) -+#define au_ftest_cpdown(flags, name) ((flags) & AuCpdown_##name) -+#define au_fset_cpdown(flags, name) \ -+ do { (flags) |= AuCpdown_##name; } while (0) -+#define au_fclr_cpdown(flags, name) \ -+ do { (flags) &= ~AuCpdown_##name; } while (0) -+ -+static int au_cpdown_dir_opq(struct dentry *dentry, aufs_bindex_t bdst, -+ unsigned int *flags) -+{ -+ int err; -+ struct dentry *opq_dentry; -+ -+ opq_dentry = au_diropq_create(dentry, bdst); -+ err = PTR_ERR(opq_dentry); -+ if (IS_ERR(opq_dentry)) -+ goto out; -+ dput(opq_dentry); -+ au_fset_cpdown(*flags, DIROPQ); -+ -+out: -+ return err; -+} -+ -+static int au_cpdown_dir_wh(struct dentry *dentry, struct dentry *h_parent, -+ struct inode *dir, aufs_bindex_t bdst) -+{ -+ int err; -+ struct path h_path; -+ struct au_branch *br; -+ -+ br = au_sbr(dentry->d_sb, bdst); -+ h_path.dentry = au_wh_lkup(h_parent, &dentry->d_name, br); -+ err = PTR_ERR(h_path.dentry); -+ if (IS_ERR(h_path.dentry)) -+ goto out; -+ -+ err = 0; -+ if (h_path.dentry->d_inode) { -+ h_path.mnt = au_br_mnt(br); -+ err = au_wh_unlink_dentry(au_h_iptr(dir, bdst), &h_path, -+ dentry); -+ } -+ dput(h_path.dentry); -+ -+out: -+ return err; -+} -+ -+static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t bdst, -+ struct au_pin *pin, -+ struct dentry *h_parent, void *arg) -+{ -+ int err, rerr; -+ aufs_bindex_t bopq, bstart; -+ struct path h_path; -+ struct dentry *parent; -+ struct inode *h_dir, *h_inode, *inode, *dir; -+ unsigned int *flags = arg; -+ -+ bstart = au_dbstart(dentry); -+ /* dentry is di-locked */ -+ parent = dget_parent(dentry); -+ dir = parent->d_inode; -+ h_dir = h_parent->d_inode; -+ AuDebugOn(h_dir != au_h_iptr(dir, bdst)); -+ IMustLock(h_dir); -+ -+ err = au_lkup_neg(dentry, bdst, /*wh*/0); -+ if (unlikely(err < 0)) -+ goto out; -+ h_path.dentry = au_h_dptr(dentry, bdst); -+ h_path.mnt = au_sbr_mnt(dentry->d_sb, bdst); -+ err = vfsub_sio_mkdir(au_h_iptr(dir, bdst), &h_path, -+ S_IRWXU | S_IRUGO | S_IXUGO); -+ if (unlikely(err)) -+ goto out_put; -+ au_fset_cpdown(*flags, MADE_DIR); -+ -+ bopq = au_dbdiropq(dentry); -+ au_fclr_cpdown(*flags, WHED); -+ au_fclr_cpdown(*flags, DIROPQ); -+ if (au_dbwh(dentry) == bdst) -+ au_fset_cpdown(*flags, WHED); -+ if (!au_ftest_cpdown(*flags, PARENT_OPQ) && bopq <= bdst) -+ au_fset_cpdown(*flags, PARENT_OPQ); -+ h_inode = h_path.dentry->d_inode; -+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); -+ if (au_ftest_cpdown(*flags, WHED)) { -+ err = au_cpdown_dir_opq(dentry, bdst, flags); -+ if (unlikely(err)) { -+ mutex_unlock(&h_inode->i_mutex); -+ goto out_dir; -+ } -+ } -+ -+ err = au_cpdown_attr(&h_path, au_h_dptr(dentry, bstart)); -+ mutex_unlock(&h_inode->i_mutex); -+ if (unlikely(err)) -+ goto out_opq; -+ -+ if (au_ftest_cpdown(*flags, WHED)) { -+ err = au_cpdown_dir_wh(dentry, h_parent, dir, bdst); -+ if (unlikely(err)) -+ goto out_opq; -+ } -+ -+ inode = dentry->d_inode; -+ if (au_ibend(inode) < bdst) -+ au_set_ibend(inode, bdst); -+ au_set_h_iptr(inode, bdst, au_igrab(h_inode), -+ au_hi_flags(inode, /*isdir*/1)); -+ au_fhsm_wrote(dentry->d_sb, bdst, /*force*/0); -+ goto out; /* success */ -+ -+ /* revert */ -+out_opq: -+ if (au_ftest_cpdown(*flags, DIROPQ)) { -+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); -+ rerr = au_diropq_remove(dentry, bdst); -+ mutex_unlock(&h_inode->i_mutex); -+ if (unlikely(rerr)) { -+ AuIOErr("failed removing diropq for %pd b%d (%d)\n", -+ dentry, bdst, rerr); -+ err = -EIO; -+ goto out; -+ } -+ } -+out_dir: -+ if (au_ftest_cpdown(*flags, MADE_DIR)) { -+ rerr = vfsub_sio_rmdir(au_h_iptr(dir, bdst), &h_path); -+ if (unlikely(rerr)) { -+ AuIOErr("failed removing %pd b%d (%d)\n", -+ dentry, bdst, rerr); -+ err = -EIO; -+ } -+ } -+out_put: -+ au_set_h_dptr(dentry, bdst, NULL); -+ if (au_dbend(dentry) == bdst) -+ au_update_dbend(dentry); -+out: -+ dput(parent); -+ return err; -+} -+ -+int au_cpdown_dirs(struct dentry *dentry, aufs_bindex_t bdst) -+{ -+ int err; -+ unsigned int flags; -+ -+ flags = 0; -+ err = au_cp_dirs(dentry, bdst, au_cpdown_dir, &flags); -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* policies for create */ -+ -+int au_wbr_nonopq(struct dentry *dentry, aufs_bindex_t bindex) -+{ -+ int err, i, j, ndentry; -+ aufs_bindex_t bopq; -+ struct au_dcsub_pages dpages; -+ struct au_dpage *dpage; -+ struct dentry **dentries, *parent, *d; -+ -+ err = au_dpages_init(&dpages, GFP_NOFS); -+ if (unlikely(err)) -+ goto out; -+ parent = dget_parent(dentry); -+ err = au_dcsub_pages_rev_aufs(&dpages, parent, /*do_include*/0); -+ if (unlikely(err)) -+ goto out_free; -+ -+ err = bindex; -+ for (i = 0; i < dpages.ndpage; i++) { -+ dpage = dpages.dpages + i; -+ dentries = dpage->dentries; -+ ndentry = dpage->ndentry; -+ for (j = 0; j < ndentry; j++) { -+ d = dentries[j]; -+ di_read_lock_parent2(d, !AuLock_IR); -+ bopq = au_dbdiropq(d); -+ di_read_unlock(d, !AuLock_IR); -+ if (bopq >= 0 && bopq < err) -+ err = bopq; -+ } -+ } -+ -+out_free: -+ dput(parent); -+ au_dpages_free(&dpages); -+out: -+ return err; -+} -+ -+static int au_wbr_bu(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ for (; bindex >= 0; bindex--) -+ if (!au_br_rdonly(au_sbr(sb, bindex))) -+ return bindex; -+ return -EROFS; -+} -+ -+/* top down parent */ -+static int au_wbr_create_tdp(struct dentry *dentry, -+ unsigned int flags __maybe_unused) -+{ -+ int err; -+ aufs_bindex_t bstart, bindex; -+ struct super_block *sb; -+ struct dentry *parent, *h_parent; -+ -+ sb = dentry->d_sb; -+ bstart = au_dbstart(dentry); -+ err = bstart; -+ if (!au_br_rdonly(au_sbr(sb, bstart))) -+ goto out; -+ -+ err = -EROFS; -+ parent = dget_parent(dentry); -+ for (bindex = au_dbstart(parent); bindex < bstart; bindex++) { -+ h_parent = au_h_dptr(parent, bindex); -+ if (!h_parent || !h_parent->d_inode) -+ continue; -+ -+ if (!au_br_rdonly(au_sbr(sb, bindex))) { -+ err = bindex; -+ break; -+ } -+ } -+ dput(parent); -+ -+ /* bottom up here */ -+ if (unlikely(err < 0)) { -+ err = au_wbr_bu(sb, bstart - 1); -+ if (err >= 0) -+ err = au_wbr_nonopq(dentry, err); -+ } -+ -+out: -+ AuDbg("b%d\n", err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* an exception for the policy other than tdp */ -+static int au_wbr_create_exp(struct dentry *dentry) -+{ -+ int err; -+ aufs_bindex_t bwh, bdiropq; -+ struct dentry *parent; -+ -+ err = -1; -+ bwh = au_dbwh(dentry); -+ parent = dget_parent(dentry); -+ bdiropq = au_dbdiropq(parent); -+ if (bwh >= 0) { -+ if (bdiropq >= 0) -+ err = min(bdiropq, bwh); -+ else -+ err = bwh; -+ AuDbg("%d\n", err); -+ } else if (bdiropq >= 0) { -+ err = bdiropq; -+ AuDbg("%d\n", err); -+ } -+ dput(parent); -+ -+ if (err >= 0) -+ err = au_wbr_nonopq(dentry, err); -+ -+ if (err >= 0 && au_br_rdonly(au_sbr(dentry->d_sb, err))) -+ err = -1; -+ -+ AuDbg("%d\n", err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* round robin */ -+static int au_wbr_create_init_rr(struct super_block *sb) -+{ -+ int err; -+ -+ err = au_wbr_bu(sb, au_sbend(sb)); -+ atomic_set(&au_sbi(sb)->si_wbr_rr_next, -err); /* less important */ -+ /* smp_mb(); */ -+ -+ AuDbg("b%d\n", err); -+ return err; -+} -+ -+static int au_wbr_create_rr(struct dentry *dentry, unsigned int flags) -+{ -+ int err, nbr; -+ unsigned int u; -+ aufs_bindex_t bindex, bend; -+ struct super_block *sb; -+ atomic_t *next; -+ -+ err = au_wbr_create_exp(dentry); -+ if (err >= 0) -+ goto out; -+ -+ sb = dentry->d_sb; -+ next = &au_sbi(sb)->si_wbr_rr_next; -+ bend = au_sbend(sb); -+ nbr = bend + 1; -+ for (bindex = 0; bindex <= bend; bindex++) { -+ if (!au_ftest_wbr(flags, DIR)) { -+ err = atomic_dec_return(next) + 1; -+ /* modulo for 0 is meaningless */ -+ if (unlikely(!err)) -+ err = atomic_dec_return(next) + 1; -+ } else -+ err = atomic_read(next); -+ AuDbg("%d\n", err); -+ u = err; -+ err = u % nbr; -+ AuDbg("%d\n", err); -+ if (!au_br_rdonly(au_sbr(sb, err))) -+ break; -+ err = -EROFS; -+ } -+ -+ if (err >= 0) -+ err = au_wbr_nonopq(dentry, err); -+ -+out: -+ AuDbg("%d\n", err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* most free space */ -+static void au_mfs(struct dentry *dentry, struct dentry *parent) -+{ -+ struct super_block *sb; -+ struct au_branch *br; -+ struct au_wbr_mfs *mfs; -+ struct dentry *h_parent; -+ aufs_bindex_t bindex, bend; -+ int err; -+ unsigned long long b, bavail; -+ struct path h_path; -+ /* reduce the stack usage */ -+ struct kstatfs *st; -+ -+ st = kmalloc(sizeof(*st), GFP_NOFS); -+ if (unlikely(!st)) { -+ AuWarn1("failed updating mfs(%d), ignored\n", -ENOMEM); -+ return; -+ } -+ -+ bavail = 0; -+ sb = dentry->d_sb; -+ mfs = &au_sbi(sb)->si_wbr_mfs; -+ MtxMustLock(&mfs->mfs_lock); -+ mfs->mfs_bindex = -EROFS; -+ mfs->mfsrr_bytes = 0; -+ if (!parent) { -+ bindex = 0; -+ bend = au_sbend(sb); -+ } else { -+ bindex = au_dbstart(parent); -+ bend = au_dbtaildir(parent); -+ } -+ -+ for (; bindex <= bend; bindex++) { -+ if (parent) { -+ h_parent = au_h_dptr(parent, bindex); -+ if (!h_parent || !h_parent->d_inode) -+ continue; -+ } -+ br = au_sbr(sb, bindex); -+ if (au_br_rdonly(br)) -+ continue; -+ -+ /* sb->s_root for NFS is unreliable */ -+ h_path.mnt = au_br_mnt(br); -+ h_path.dentry = h_path.mnt->mnt_root; -+ err = vfs_statfs(&h_path, st); -+ if (unlikely(err)) { -+ AuWarn1("failed statfs, b%d, %d\n", bindex, err); -+ continue; -+ } -+ -+ /* when the available size is equal, select the lower one */ -+ BUILD_BUG_ON(sizeof(b) < sizeof(st->f_bavail) -+ || sizeof(b) < sizeof(st->f_bsize)); -+ b = st->f_bavail * st->f_bsize; -+ br->br_wbr->wbr_bytes = b; -+ if (b >= bavail) { -+ bavail = b; -+ mfs->mfs_bindex = bindex; -+ mfs->mfs_jiffy = jiffies; -+ } -+ } -+ -+ mfs->mfsrr_bytes = bavail; -+ AuDbg("b%d\n", mfs->mfs_bindex); -+ kfree(st); -+} -+ -+static int au_wbr_create_mfs(struct dentry *dentry, unsigned int flags) -+{ -+ int err; -+ struct dentry *parent; -+ struct super_block *sb; -+ struct au_wbr_mfs *mfs; -+ -+ err = au_wbr_create_exp(dentry); -+ if (err >= 0) -+ goto out; -+ -+ sb = dentry->d_sb; -+ parent = NULL; -+ if (au_ftest_wbr(flags, PARENT)) -+ parent = dget_parent(dentry); -+ mfs = &au_sbi(sb)->si_wbr_mfs; -+ mutex_lock(&mfs->mfs_lock); -+ if (time_after(jiffies, mfs->mfs_jiffy + mfs->mfs_expire) -+ || mfs->mfs_bindex < 0 -+ || au_br_rdonly(au_sbr(sb, mfs->mfs_bindex))) -+ au_mfs(dentry, parent); -+ mutex_unlock(&mfs->mfs_lock); -+ err = mfs->mfs_bindex; -+ dput(parent); -+ -+ if (err >= 0) -+ err = au_wbr_nonopq(dentry, err); -+ -+out: -+ AuDbg("b%d\n", err); -+ return err; -+} -+ -+static int au_wbr_create_init_mfs(struct super_block *sb) -+{ -+ struct au_wbr_mfs *mfs; -+ -+ mfs = &au_sbi(sb)->si_wbr_mfs; -+ mutex_init(&mfs->mfs_lock); -+ mfs->mfs_jiffy = 0; -+ mfs->mfs_bindex = -EROFS; -+ -+ return 0; -+} -+ -+static int au_wbr_create_fin_mfs(struct super_block *sb __maybe_unused) -+{ -+ mutex_destroy(&au_sbi(sb)->si_wbr_mfs.mfs_lock); -+ return 0; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* most free space and then round robin */ -+static int au_wbr_create_mfsrr(struct dentry *dentry, unsigned int flags) -+{ -+ int err; -+ struct au_wbr_mfs *mfs; -+ -+ err = au_wbr_create_mfs(dentry, flags); -+ if (err >= 0) { -+ mfs = &au_sbi(dentry->d_sb)->si_wbr_mfs; -+ mutex_lock(&mfs->mfs_lock); -+ if (mfs->mfsrr_bytes < mfs->mfsrr_watermark) -+ err = au_wbr_create_rr(dentry, flags); -+ mutex_unlock(&mfs->mfs_lock); -+ } -+ -+ AuDbg("b%d\n", err); -+ return err; -+} -+ -+static int au_wbr_create_init_mfsrr(struct super_block *sb) -+{ -+ int err; -+ -+ au_wbr_create_init_mfs(sb); /* ignore */ -+ err = au_wbr_create_init_rr(sb); -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* top down parent and most free space */ -+static int au_wbr_create_pmfs(struct dentry *dentry, unsigned int flags) -+{ -+ int err, e2; -+ unsigned long long b; -+ aufs_bindex_t bindex, bstart, bend; -+ struct super_block *sb; -+ struct dentry *parent, *h_parent; -+ struct au_branch *br; -+ -+ err = au_wbr_create_tdp(dentry, flags); -+ if (unlikely(err < 0)) -+ goto out; -+ parent = dget_parent(dentry); -+ bstart = au_dbstart(parent); -+ bend = au_dbtaildir(parent); -+ if (bstart == bend) -+ goto out_parent; /* success */ -+ -+ e2 = au_wbr_create_mfs(dentry, flags); -+ if (e2 < 0) -+ goto out_parent; /* success */ -+ -+ /* when the available size is equal, select upper one */ -+ sb = dentry->d_sb; -+ br = au_sbr(sb, err); -+ b = br->br_wbr->wbr_bytes; -+ AuDbg("b%d, %llu\n", err, b); -+ -+ for (bindex = bstart; bindex <= bend; bindex++) { -+ h_parent = au_h_dptr(parent, bindex); -+ if (!h_parent || !h_parent->d_inode) -+ continue; -+ -+ br = au_sbr(sb, bindex); -+ if (!au_br_rdonly(br) && br->br_wbr->wbr_bytes > b) { -+ b = br->br_wbr->wbr_bytes; -+ err = bindex; -+ AuDbg("b%d, %llu\n", err, b); -+ } -+ } -+ -+ if (err >= 0) -+ err = au_wbr_nonopq(dentry, err); -+ -+out_parent: -+ dput(parent); -+out: -+ AuDbg("b%d\n", err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * - top down parent -+ * - most free space with parent -+ * - most free space round-robin regardless parent -+ */ -+static int au_wbr_create_pmfsrr(struct dentry *dentry, unsigned int flags) -+{ -+ int err; -+ unsigned long long watermark; -+ struct super_block *sb; -+ struct au_branch *br; -+ struct au_wbr_mfs *mfs; -+ -+ err = au_wbr_create_pmfs(dentry, flags | AuWbr_PARENT); -+ if (unlikely(err < 0)) -+ goto out; -+ -+ sb = dentry->d_sb; -+ br = au_sbr(sb, err); -+ mfs = &au_sbi(sb)->si_wbr_mfs; -+ mutex_lock(&mfs->mfs_lock); -+ watermark = mfs->mfsrr_watermark; -+ mutex_unlock(&mfs->mfs_lock); -+ if (br->br_wbr->wbr_bytes < watermark) -+ /* regardless the parent dir */ -+ err = au_wbr_create_mfsrr(dentry, flags); -+ -+out: -+ AuDbg("b%d\n", err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* policies for copyup */ -+ -+/* top down parent */ -+static int au_wbr_copyup_tdp(struct dentry *dentry) -+{ -+ return au_wbr_create_tdp(dentry, /*flags, anything is ok*/0); -+} -+ -+/* bottom up parent */ -+static int au_wbr_copyup_bup(struct dentry *dentry) -+{ -+ int err; -+ aufs_bindex_t bindex, bstart; -+ struct dentry *parent, *h_parent; -+ struct super_block *sb; -+ -+ err = -EROFS; -+ sb = dentry->d_sb; -+ parent = dget_parent(dentry); -+ bstart = au_dbstart(parent); -+ for (bindex = au_dbstart(dentry); bindex >= bstart; bindex--) { -+ h_parent = au_h_dptr(parent, bindex); -+ if (!h_parent || !h_parent->d_inode) -+ continue; -+ -+ if (!au_br_rdonly(au_sbr(sb, bindex))) { -+ err = bindex; -+ break; -+ } -+ } -+ dput(parent); -+ -+ /* bottom up here */ -+ if (unlikely(err < 0)) -+ err = au_wbr_bu(sb, bstart - 1); -+ -+ AuDbg("b%d\n", err); -+ return err; -+} -+ -+/* bottom up */ -+int au_wbr_do_copyup_bu(struct dentry *dentry, aufs_bindex_t bstart) -+{ -+ int err; -+ -+ err = au_wbr_bu(dentry->d_sb, bstart); -+ AuDbg("b%d\n", err); -+ if (err > bstart) -+ err = au_wbr_nonopq(dentry, err); -+ -+ AuDbg("b%d\n", err); -+ return err; -+} -+ -+static int au_wbr_copyup_bu(struct dentry *dentry) -+{ -+ int err; -+ aufs_bindex_t bstart; -+ -+ bstart = au_dbstart(dentry); -+ err = au_wbr_do_copyup_bu(dentry, bstart); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct au_wbr_copyup_operations au_wbr_copyup_ops[] = { -+ [AuWbrCopyup_TDP] = { -+ .copyup = au_wbr_copyup_tdp -+ }, -+ [AuWbrCopyup_BUP] = { -+ .copyup = au_wbr_copyup_bup -+ }, -+ [AuWbrCopyup_BU] = { -+ .copyup = au_wbr_copyup_bu -+ } -+}; -+ -+struct au_wbr_create_operations au_wbr_create_ops[] = { -+ [AuWbrCreate_TDP] = { -+ .create = au_wbr_create_tdp -+ }, -+ [AuWbrCreate_RR] = { -+ .create = au_wbr_create_rr, -+ .init = au_wbr_create_init_rr -+ }, -+ [AuWbrCreate_MFS] = { -+ .create = au_wbr_create_mfs, -+ .init = au_wbr_create_init_mfs, -+ .fin = au_wbr_create_fin_mfs -+ }, -+ [AuWbrCreate_MFSV] = { -+ .create = au_wbr_create_mfs, -+ .init = au_wbr_create_init_mfs, -+ .fin = au_wbr_create_fin_mfs -+ }, -+ [AuWbrCreate_MFSRR] = { -+ .create = au_wbr_create_mfsrr, -+ .init = au_wbr_create_init_mfsrr, -+ .fin = au_wbr_create_fin_mfs -+ }, -+ [AuWbrCreate_MFSRRV] = { -+ .create = au_wbr_create_mfsrr, -+ .init = au_wbr_create_init_mfsrr, -+ .fin = au_wbr_create_fin_mfs -+ }, -+ [AuWbrCreate_PMFS] = { -+ .create = au_wbr_create_pmfs, -+ .init = au_wbr_create_init_mfs, -+ .fin = au_wbr_create_fin_mfs -+ }, -+ [AuWbrCreate_PMFSV] = { -+ .create = au_wbr_create_pmfs, -+ .init = au_wbr_create_init_mfs, -+ .fin = au_wbr_create_fin_mfs -+ }, -+ [AuWbrCreate_PMFSRR] = { -+ .create = au_wbr_create_pmfsrr, -+ .init = au_wbr_create_init_mfsrr, -+ .fin = au_wbr_create_fin_mfs -+ }, -+ [AuWbrCreate_PMFSRRV] = { -+ .create = au_wbr_create_pmfsrr, -+ .init = au_wbr_create_init_mfsrr, -+ .fin = au_wbr_create_fin_mfs -+ } -+}; -diff --git a/fs/aufs/whout.c b/fs/aufs/whout.c -new file mode 100644 -index 0000000..fb667ee ---- /dev/null -+++ b/fs/aufs/whout.c -@@ -0,0 +1,1061 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * whiteout for logical deletion and opaque directory -+ */ -+ -+#include "aufs.h" -+ -+#define WH_MASK S_IRUGO -+ -+/* -+ * If a directory contains this file, then it is opaque. We start with the -+ * .wh. flag so that it is blocked by lookup. -+ */ -+static struct qstr diropq_name = QSTR_INIT(AUFS_WH_DIROPQ, -+ sizeof(AUFS_WH_DIROPQ) - 1); -+ -+/* -+ * generate whiteout name, which is NOT terminated by NULL. -+ * @name: original d_name.name -+ * @len: original d_name.len -+ * @wh: whiteout qstr -+ * returns zero when succeeds, otherwise error. -+ * succeeded value as wh->name should be freed by kfree(). -+ */ -+int au_wh_name_alloc(struct qstr *wh, const struct qstr *name) -+{ -+ char *p; -+ -+ if (unlikely(name->len > PATH_MAX - AUFS_WH_PFX_LEN)) -+ return -ENAMETOOLONG; -+ -+ wh->len = name->len + AUFS_WH_PFX_LEN; -+ p = kmalloc(wh->len, GFP_NOFS); -+ wh->name = p; -+ if (p) { -+ memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); -+ memcpy(p + AUFS_WH_PFX_LEN, name->name, name->len); -+ /* smp_mb(); */ -+ return 0; -+ } -+ return -ENOMEM; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * test if the @wh_name exists under @h_parent. -+ * @try_sio specifies the necessary of super-io. -+ */ -+int au_wh_test(struct dentry *h_parent, struct qstr *wh_name, int try_sio) -+{ -+ int err; -+ struct dentry *wh_dentry; -+ -+ if (!try_sio) -+ wh_dentry = vfsub_lkup_one(wh_name, h_parent); -+ else -+ wh_dentry = au_sio_lkup_one(wh_name, h_parent); -+ err = PTR_ERR(wh_dentry); -+ if (IS_ERR(wh_dentry)) { -+ if (err == -ENAMETOOLONG) -+ err = 0; -+ goto out; -+ } -+ -+ err = 0; -+ if (!wh_dentry->d_inode) -+ goto out_wh; /* success */ -+ -+ err = 1; -+ if (S_ISREG(wh_dentry->d_inode->i_mode)) -+ goto out_wh; /* success */ -+ -+ err = -EIO; -+ AuIOErr("%pd Invalid whiteout entry type 0%o.\n", -+ wh_dentry, wh_dentry->d_inode->i_mode); -+ -+out_wh: -+ dput(wh_dentry); -+out: -+ return err; -+} -+ -+/* -+ * test if the @h_dentry sets opaque or not. -+ */ -+int au_diropq_test(struct dentry *h_dentry) -+{ -+ int err; -+ struct inode *h_dir; -+ -+ h_dir = h_dentry->d_inode; -+ err = au_wh_test(h_dentry, &diropq_name, -+ au_test_h_perm_sio(h_dir, MAY_EXEC)); -+ return err; -+} -+ -+/* -+ * returns a negative dentry whose name is unique and temporary. -+ */ -+struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br, -+ struct qstr *prefix) -+{ -+ struct dentry *dentry; -+ int i; -+ char defname[NAME_MAX - AUFS_MAX_NAMELEN + DNAME_INLINE_LEN + 1], -+ *name, *p; -+ /* strict atomic_t is unnecessary here */ -+ static unsigned short cnt; -+ struct qstr qs; -+ -+ BUILD_BUG_ON(sizeof(cnt) * 2 > AUFS_WH_TMP_LEN); -+ -+ name = defname; -+ qs.len = sizeof(defname) - DNAME_INLINE_LEN + prefix->len - 1; -+ if (unlikely(prefix->len > DNAME_INLINE_LEN)) { -+ dentry = ERR_PTR(-ENAMETOOLONG); -+ if (unlikely(qs.len > NAME_MAX)) -+ goto out; -+ dentry = ERR_PTR(-ENOMEM); -+ name = kmalloc(qs.len + 1, GFP_NOFS); -+ if (unlikely(!name)) -+ goto out; -+ } -+ -+ /* doubly whiteout-ed */ -+ memcpy(name, AUFS_WH_PFX AUFS_WH_PFX, AUFS_WH_PFX_LEN * 2); -+ p = name + AUFS_WH_PFX_LEN * 2; -+ memcpy(p, prefix->name, prefix->len); -+ p += prefix->len; -+ *p++ = '.'; -+ AuDebugOn(name + qs.len + 1 - p <= AUFS_WH_TMP_LEN); -+ -+ qs.name = name; -+ for (i = 0; i < 3; i++) { -+ sprintf(p, "%.*x", AUFS_WH_TMP_LEN, cnt++); -+ dentry = au_sio_lkup_one(&qs, h_parent); -+ if (IS_ERR(dentry) || !dentry->d_inode) -+ goto out_name; -+ dput(dentry); -+ } -+ /* pr_warn("could not get random name\n"); */ -+ dentry = ERR_PTR(-EEXIST); -+ AuDbg("%.*s\n", AuLNPair(&qs)); -+ BUG(); -+ -+out_name: -+ if (name != defname) -+ kfree(name); -+out: -+ AuTraceErrPtr(dentry); -+ return dentry; -+} -+ -+/* -+ * rename the @h_dentry on @br to the whiteouted temporary name. -+ */ -+int au_whtmp_ren(struct dentry *h_dentry, struct au_branch *br) -+{ -+ int err; -+ struct path h_path = { -+ .mnt = au_br_mnt(br) -+ }; -+ struct inode *h_dir, *delegated; -+ struct dentry *h_parent; -+ -+ h_parent = h_dentry->d_parent; /* dir inode is locked */ -+ h_dir = h_parent->d_inode; -+ IMustLock(h_dir); -+ -+ h_path.dentry = au_whtmp_lkup(h_parent, br, &h_dentry->d_name); -+ err = PTR_ERR(h_path.dentry); -+ if (IS_ERR(h_path.dentry)) -+ goto out; -+ -+ /* under the same dir, no need to lock_rename() */ -+ delegated = NULL; -+ err = vfsub_rename(h_dir, h_dentry, h_dir, &h_path, &delegated); -+ AuTraceErr(err); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal rename\n"); -+ iput(delegated); -+ } -+ dput(h_path.dentry); -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+/* -+ * functions for removing a whiteout -+ */ -+ -+static int do_unlink_wh(struct inode *h_dir, struct path *h_path) -+{ -+ int err, force; -+ struct inode *delegated; -+ -+ /* -+ * forces superio when the dir has a sticky bit. -+ * this may be a violation of unix fs semantics. -+ */ -+ force = (h_dir->i_mode & S_ISVTX) -+ && !uid_eq(current_fsuid(), h_path->dentry->d_inode->i_uid); -+ delegated = NULL; -+ err = vfsub_unlink(h_dir, h_path, &delegated, force); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal unlink\n"); -+ iput(delegated); -+ } -+ return err; -+} -+ -+int au_wh_unlink_dentry(struct inode *h_dir, struct path *h_path, -+ struct dentry *dentry) -+{ -+ int err; -+ -+ err = do_unlink_wh(h_dir, h_path); -+ if (!err && dentry) -+ au_set_dbwh(dentry, -1); -+ -+ return err; -+} -+ -+static int unlink_wh_name(struct dentry *h_parent, struct qstr *wh, -+ struct au_branch *br) -+{ -+ int err; -+ struct path h_path = { -+ .mnt = au_br_mnt(br) -+ }; -+ -+ err = 0; -+ h_path.dentry = vfsub_lkup_one(wh, h_parent); -+ if (IS_ERR(h_path.dentry)) -+ err = PTR_ERR(h_path.dentry); -+ else { -+ if (h_path.dentry->d_inode -+ && S_ISREG(h_path.dentry->d_inode->i_mode)) -+ err = do_unlink_wh(h_parent->d_inode, &h_path); -+ dput(h_path.dentry); -+ } -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+/* -+ * initialize/clean whiteout for a branch -+ */ -+ -+static void au_wh_clean(struct inode *h_dir, struct path *whpath, -+ const int isdir) -+{ -+ int err; -+ struct inode *delegated; -+ -+ if (!whpath->dentry->d_inode) -+ return; -+ -+ if (isdir) -+ err = vfsub_rmdir(h_dir, whpath); -+ else { -+ delegated = NULL; -+ err = vfsub_unlink(h_dir, whpath, &delegated, /*force*/0); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal unlink\n"); -+ iput(delegated); -+ } -+ } -+ if (unlikely(err)) -+ pr_warn("failed removing %pd (%d), ignored.\n", -+ whpath->dentry, err); -+} -+ -+static int test_linkable(struct dentry *h_root) -+{ -+ struct inode *h_dir = h_root->d_inode; -+ -+ if (h_dir->i_op->link) -+ return 0; -+ -+ pr_err("%pd (%s) doesn't support link(2), use noplink and rw+nolwh\n", -+ h_root, au_sbtype(h_root->d_sb)); -+ return -ENOSYS; -+} -+ -+/* todo: should this mkdir be done in /sbin/mount.aufs helper? */ -+static int au_whdir(struct inode *h_dir, struct path *path) -+{ -+ int err; -+ -+ err = -EEXIST; -+ if (!path->dentry->d_inode) { -+ int mode = S_IRWXU; -+ -+ if (au_test_nfs(path->dentry->d_sb)) -+ mode |= S_IXUGO; -+ err = vfsub_mkdir(h_dir, path, mode); -+ } else if (d_is_dir(path->dentry)) -+ err = 0; -+ else -+ pr_err("unknown %pd exists\n", path->dentry); -+ -+ return err; -+} -+ -+struct au_wh_base { -+ const struct qstr *name; -+ struct dentry *dentry; -+}; -+ -+static void au_wh_init_ro(struct inode *h_dir, struct au_wh_base base[], -+ struct path *h_path) -+{ -+ h_path->dentry = base[AuBrWh_BASE].dentry; -+ au_wh_clean(h_dir, h_path, /*isdir*/0); -+ h_path->dentry = base[AuBrWh_PLINK].dentry; -+ au_wh_clean(h_dir, h_path, /*isdir*/1); -+ h_path->dentry = base[AuBrWh_ORPH].dentry; -+ au_wh_clean(h_dir, h_path, /*isdir*/1); -+} -+ -+/* -+ * returns tri-state, -+ * minus: error, caller should print the message -+ * zero: succuess -+ * plus: error, caller should NOT print the message -+ */ -+static int au_wh_init_rw_nolink(struct dentry *h_root, struct au_wbr *wbr, -+ int do_plink, struct au_wh_base base[], -+ struct path *h_path) -+{ -+ int err; -+ struct inode *h_dir; -+ -+ h_dir = h_root->d_inode; -+ h_path->dentry = base[AuBrWh_BASE].dentry; -+ au_wh_clean(h_dir, h_path, /*isdir*/0); -+ h_path->dentry = base[AuBrWh_PLINK].dentry; -+ if (do_plink) { -+ err = test_linkable(h_root); -+ if (unlikely(err)) { -+ err = 1; -+ goto out; -+ } -+ -+ err = au_whdir(h_dir, h_path); -+ if (unlikely(err)) -+ goto out; -+ wbr->wbr_plink = dget(base[AuBrWh_PLINK].dentry); -+ } else -+ au_wh_clean(h_dir, h_path, /*isdir*/1); -+ h_path->dentry = base[AuBrWh_ORPH].dentry; -+ err = au_whdir(h_dir, h_path); -+ if (unlikely(err)) -+ goto out; -+ wbr->wbr_orph = dget(base[AuBrWh_ORPH].dentry); -+ -+out: -+ return err; -+} -+ -+/* -+ * for the moment, aufs supports the branch filesystem which does not support -+ * link(2). testing on FAT which does not support i_op->setattr() fully either, -+ * copyup failed. finally, such filesystem will not be used as the writable -+ * branch. -+ * -+ * returns tri-state, see above. -+ */ -+static int au_wh_init_rw(struct dentry *h_root, struct au_wbr *wbr, -+ int do_plink, struct au_wh_base base[], -+ struct path *h_path) -+{ -+ int err; -+ struct inode *h_dir; -+ -+ WbrWhMustWriteLock(wbr); -+ -+ err = test_linkable(h_root); -+ if (unlikely(err)) { -+ err = 1; -+ goto out; -+ } -+ -+ /* -+ * todo: should this create be done in /sbin/mount.aufs helper? -+ */ -+ err = -EEXIST; -+ h_dir = h_root->d_inode; -+ if (!base[AuBrWh_BASE].dentry->d_inode) { -+ h_path->dentry = base[AuBrWh_BASE].dentry; -+ err = vfsub_create(h_dir, h_path, WH_MASK, /*want_excl*/true); -+ } else if (S_ISREG(base[AuBrWh_BASE].dentry->d_inode->i_mode)) -+ err = 0; -+ else -+ pr_err("unknown %pd2 exists\n", base[AuBrWh_BASE].dentry); -+ if (unlikely(err)) -+ goto out; -+ -+ h_path->dentry = base[AuBrWh_PLINK].dentry; -+ if (do_plink) { -+ err = au_whdir(h_dir, h_path); -+ if (unlikely(err)) -+ goto out; -+ wbr->wbr_plink = dget(base[AuBrWh_PLINK].dentry); -+ } else -+ au_wh_clean(h_dir, h_path, /*isdir*/1); -+ wbr->wbr_whbase = dget(base[AuBrWh_BASE].dentry); -+ -+ h_path->dentry = base[AuBrWh_ORPH].dentry; -+ err = au_whdir(h_dir, h_path); -+ if (unlikely(err)) -+ goto out; -+ wbr->wbr_orph = dget(base[AuBrWh_ORPH].dentry); -+ -+out: -+ return err; -+} -+ -+/* -+ * initialize the whiteout base file/dir for @br. -+ */ -+int au_wh_init(struct au_branch *br, struct super_block *sb) -+{ -+ int err, i; -+ const unsigned char do_plink -+ = !!au_opt_test(au_mntflags(sb), PLINK); -+ struct inode *h_dir; -+ struct path path = br->br_path; -+ struct dentry *h_root = path.dentry; -+ struct au_wbr *wbr = br->br_wbr; -+ static const struct qstr base_name[] = { -+ [AuBrWh_BASE] = QSTR_INIT(AUFS_BASE_NAME, -+ sizeof(AUFS_BASE_NAME) - 1), -+ [AuBrWh_PLINK] = QSTR_INIT(AUFS_PLINKDIR_NAME, -+ sizeof(AUFS_PLINKDIR_NAME) - 1), -+ [AuBrWh_ORPH] = QSTR_INIT(AUFS_ORPHDIR_NAME, -+ sizeof(AUFS_ORPHDIR_NAME) - 1) -+ }; -+ struct au_wh_base base[] = { -+ [AuBrWh_BASE] = { -+ .name = base_name + AuBrWh_BASE, -+ .dentry = NULL -+ }, -+ [AuBrWh_PLINK] = { -+ .name = base_name + AuBrWh_PLINK, -+ .dentry = NULL -+ }, -+ [AuBrWh_ORPH] = { -+ .name = base_name + AuBrWh_ORPH, -+ .dentry = NULL -+ } -+ }; -+ -+ if (wbr) -+ WbrWhMustWriteLock(wbr); -+ -+ for (i = 0; i < AuBrWh_Last; i++) { -+ /* doubly whiteouted */ -+ struct dentry *d; -+ -+ d = au_wh_lkup(h_root, (void *)base[i].name, br); -+ err = PTR_ERR(d); -+ if (IS_ERR(d)) -+ goto out; -+ -+ base[i].dentry = d; -+ AuDebugOn(wbr -+ && wbr->wbr_wh[i] -+ && wbr->wbr_wh[i] != base[i].dentry); -+ } -+ -+ if (wbr) -+ for (i = 0; i < AuBrWh_Last; i++) { -+ dput(wbr->wbr_wh[i]); -+ wbr->wbr_wh[i] = NULL; -+ } -+ -+ err = 0; -+ if (!au_br_writable(br->br_perm)) { -+ h_dir = h_root->d_inode; -+ au_wh_init_ro(h_dir, base, &path); -+ } else if (!au_br_wh_linkable(br->br_perm)) { -+ err = au_wh_init_rw_nolink(h_root, wbr, do_plink, base, &path); -+ if (err > 0) -+ goto out; -+ else if (err) -+ goto out_err; -+ } else { -+ err = au_wh_init_rw(h_root, wbr, do_plink, base, &path); -+ if (err > 0) -+ goto out; -+ else if (err) -+ goto out_err; -+ } -+ goto out; /* success */ -+ -+out_err: -+ pr_err("an error(%d) on the writable branch %pd(%s)\n", -+ err, h_root, au_sbtype(h_root->d_sb)); -+out: -+ for (i = 0; i < AuBrWh_Last; i++) -+ dput(base[i].dentry); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+/* -+ * whiteouts are all hard-linked usually. -+ * when its link count reaches a ceiling, we create a new whiteout base -+ * asynchronously. -+ */ -+ -+struct reinit_br_wh { -+ struct super_block *sb; -+ struct au_branch *br; -+}; -+ -+static void reinit_br_wh(void *arg) -+{ -+ int err; -+ aufs_bindex_t bindex; -+ struct path h_path; -+ struct reinit_br_wh *a = arg; -+ struct au_wbr *wbr; -+ struct inode *dir, *delegated; -+ struct dentry *h_root; -+ struct au_hinode *hdir; -+ -+ err = 0; -+ wbr = a->br->br_wbr; -+ /* big aufs lock */ -+ si_noflush_write_lock(a->sb); -+ if (!au_br_writable(a->br->br_perm)) -+ goto out; -+ bindex = au_br_index(a->sb, a->br->br_id); -+ if (unlikely(bindex < 0)) -+ goto out; -+ -+ di_read_lock_parent(a->sb->s_root, AuLock_IR); -+ dir = a->sb->s_root->d_inode; -+ hdir = au_hi(dir, bindex); -+ h_root = au_h_dptr(a->sb->s_root, bindex); -+ AuDebugOn(h_root != au_br_dentry(a->br)); -+ -+ au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); -+ wbr_wh_write_lock(wbr); -+ err = au_h_verify(wbr->wbr_whbase, au_opt_udba(a->sb), hdir->hi_inode, -+ h_root, a->br); -+ if (!err) { -+ h_path.dentry = wbr->wbr_whbase; -+ h_path.mnt = au_br_mnt(a->br); -+ delegated = NULL; -+ err = vfsub_unlink(hdir->hi_inode, &h_path, &delegated, -+ /*force*/0); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal unlink\n"); -+ iput(delegated); -+ } -+ } else { -+ pr_warn("%pd is moved, ignored\n", wbr->wbr_whbase); -+ err = 0; -+ } -+ dput(wbr->wbr_whbase); -+ wbr->wbr_whbase = NULL; -+ if (!err) -+ err = au_wh_init(a->br, a->sb); -+ wbr_wh_write_unlock(wbr); -+ au_hn_imtx_unlock(hdir); -+ di_read_unlock(a->sb->s_root, AuLock_IR); -+ if (!err) -+ au_fhsm_wrote(a->sb, bindex, /*force*/0); -+ -+out: -+ if (wbr) -+ atomic_dec(&wbr->wbr_wh_running); -+ atomic_dec(&a->br->br_count); -+ si_write_unlock(a->sb); -+ au_nwt_done(&au_sbi(a->sb)->si_nowait); -+ kfree(arg); -+ if (unlikely(err)) -+ AuIOErr("err %d\n", err); -+} -+ -+static void kick_reinit_br_wh(struct super_block *sb, struct au_branch *br) -+{ -+ int do_dec, wkq_err; -+ struct reinit_br_wh *arg; -+ -+ do_dec = 1; -+ if (atomic_inc_return(&br->br_wbr->wbr_wh_running) != 1) -+ goto out; -+ -+ /* ignore ENOMEM */ -+ arg = kmalloc(sizeof(*arg), GFP_NOFS); -+ if (arg) { -+ /* -+ * dec(wh_running), kfree(arg) and dec(br_count) -+ * in reinit function -+ */ -+ arg->sb = sb; -+ arg->br = br; -+ atomic_inc(&br->br_count); -+ wkq_err = au_wkq_nowait(reinit_br_wh, arg, sb, /*flags*/0); -+ if (unlikely(wkq_err)) { -+ atomic_dec(&br->br_wbr->wbr_wh_running); -+ atomic_dec(&br->br_count); -+ kfree(arg); -+ } -+ do_dec = 0; -+ } -+ -+out: -+ if (do_dec) -+ atomic_dec(&br->br_wbr->wbr_wh_running); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * create the whiteout @wh. -+ */ -+static int link_or_create_wh(struct super_block *sb, aufs_bindex_t bindex, -+ struct dentry *wh) -+{ -+ int err; -+ struct path h_path = { -+ .dentry = wh -+ }; -+ struct au_branch *br; -+ struct au_wbr *wbr; -+ struct dentry *h_parent; -+ struct inode *h_dir, *delegated; -+ -+ h_parent = wh->d_parent; /* dir inode is locked */ -+ h_dir = h_parent->d_inode; -+ IMustLock(h_dir); -+ -+ br = au_sbr(sb, bindex); -+ h_path.mnt = au_br_mnt(br); -+ wbr = br->br_wbr; -+ wbr_wh_read_lock(wbr); -+ if (wbr->wbr_whbase) { -+ delegated = NULL; -+ err = vfsub_link(wbr->wbr_whbase, h_dir, &h_path, &delegated); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal link\n"); -+ iput(delegated); -+ } -+ if (!err || err != -EMLINK) -+ goto out; -+ -+ /* link count full. re-initialize br_whbase. */ -+ kick_reinit_br_wh(sb, br); -+ } -+ -+ /* return this error in this context */ -+ err = vfsub_create(h_dir, &h_path, WH_MASK, /*want_excl*/true); -+ if (!err) -+ au_fhsm_wrote(sb, bindex, /*force*/0); -+ -+out: -+ wbr_wh_read_unlock(wbr); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * create or remove the diropq. -+ */ -+static struct dentry *do_diropq(struct dentry *dentry, aufs_bindex_t bindex, -+ unsigned int flags) -+{ -+ struct dentry *opq_dentry, *h_dentry; -+ struct super_block *sb; -+ struct au_branch *br; -+ int err; -+ -+ sb = dentry->d_sb; -+ br = au_sbr(sb, bindex); -+ h_dentry = au_h_dptr(dentry, bindex); -+ opq_dentry = vfsub_lkup_one(&diropq_name, h_dentry); -+ if (IS_ERR(opq_dentry)) -+ goto out; -+ -+ if (au_ftest_diropq(flags, CREATE)) { -+ err = link_or_create_wh(sb, bindex, opq_dentry); -+ if (!err) { -+ au_set_dbdiropq(dentry, bindex); -+ goto out; /* success */ -+ } -+ } else { -+ struct path tmp = { -+ .dentry = opq_dentry, -+ .mnt = au_br_mnt(br) -+ }; -+ err = do_unlink_wh(au_h_iptr(dentry->d_inode, bindex), &tmp); -+ if (!err) -+ au_set_dbdiropq(dentry, -1); -+ } -+ dput(opq_dentry); -+ opq_dentry = ERR_PTR(err); -+ -+out: -+ return opq_dentry; -+} -+ -+struct do_diropq_args { -+ struct dentry **errp; -+ struct dentry *dentry; -+ aufs_bindex_t bindex; -+ unsigned int flags; -+}; -+ -+static void call_do_diropq(void *args) -+{ -+ struct do_diropq_args *a = args; -+ *a->errp = do_diropq(a->dentry, a->bindex, a->flags); -+} -+ -+struct dentry *au_diropq_sio(struct dentry *dentry, aufs_bindex_t bindex, -+ unsigned int flags) -+{ -+ struct dentry *diropq, *h_dentry; -+ -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (!au_test_h_perm_sio(h_dentry->d_inode, MAY_EXEC | MAY_WRITE)) -+ diropq = do_diropq(dentry, bindex, flags); -+ else { -+ int wkq_err; -+ struct do_diropq_args args = { -+ .errp = &diropq, -+ .dentry = dentry, -+ .bindex = bindex, -+ .flags = flags -+ }; -+ -+ wkq_err = au_wkq_wait(call_do_diropq, &args); -+ if (unlikely(wkq_err)) -+ diropq = ERR_PTR(wkq_err); -+ } -+ -+ return diropq; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * lookup whiteout dentry. -+ * @h_parent: lower parent dentry which must exist and be locked -+ * @base_name: name of dentry which will be whiteouted -+ * returns dentry for whiteout. -+ */ -+struct dentry *au_wh_lkup(struct dentry *h_parent, struct qstr *base_name, -+ struct au_branch *br) -+{ -+ int err; -+ struct qstr wh_name; -+ struct dentry *wh_dentry; -+ -+ err = au_wh_name_alloc(&wh_name, base_name); -+ wh_dentry = ERR_PTR(err); -+ if (!err) { -+ wh_dentry = vfsub_lkup_one(&wh_name, h_parent); -+ kfree(wh_name.name); -+ } -+ return wh_dentry; -+} -+ -+/* -+ * link/create a whiteout for @dentry on @bindex. -+ */ -+struct dentry *au_wh_create(struct dentry *dentry, aufs_bindex_t bindex, -+ struct dentry *h_parent) -+{ -+ struct dentry *wh_dentry; -+ struct super_block *sb; -+ int err; -+ -+ sb = dentry->d_sb; -+ wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, au_sbr(sb, bindex)); -+ if (!IS_ERR(wh_dentry) && !wh_dentry->d_inode) { -+ err = link_or_create_wh(sb, bindex, wh_dentry); -+ if (!err) { -+ au_set_dbwh(dentry, bindex); -+ au_fhsm_wrote(sb, bindex, /*force*/0); -+ } else { -+ dput(wh_dentry); -+ wh_dentry = ERR_PTR(err); -+ } -+ } -+ -+ return wh_dentry; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* Delete all whiteouts in this directory on branch bindex. */ -+static int del_wh_children(struct dentry *h_dentry, struct au_nhash *whlist, -+ aufs_bindex_t bindex, struct au_branch *br) -+{ -+ int err; -+ unsigned long ul, n; -+ struct qstr wh_name; -+ char *p; -+ struct hlist_head *head; -+ struct au_vdir_wh *pos; -+ struct au_vdir_destr *str; -+ -+ err = -ENOMEM; -+ p = (void *)__get_free_page(GFP_NOFS); -+ wh_name.name = p; -+ if (unlikely(!wh_name.name)) -+ goto out; -+ -+ err = 0; -+ memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); -+ p += AUFS_WH_PFX_LEN; -+ n = whlist->nh_num; -+ head = whlist->nh_head; -+ for (ul = 0; !err && ul < n; ul++, head++) { -+ hlist_for_each_entry(pos, head, wh_hash) { -+ if (pos->wh_bindex != bindex) -+ continue; -+ -+ str = &pos->wh_str; -+ if (str->len + AUFS_WH_PFX_LEN <= PATH_MAX) { -+ memcpy(p, str->name, str->len); -+ wh_name.len = AUFS_WH_PFX_LEN + str->len; -+ err = unlink_wh_name(h_dentry, &wh_name, br); -+ if (!err) -+ continue; -+ break; -+ } -+ AuIOErr("whiteout name too long %.*s\n", -+ str->len, str->name); -+ err = -EIO; -+ break; -+ } -+ } -+ free_page((unsigned long)wh_name.name); -+ -+out: -+ return err; -+} -+ -+struct del_wh_children_args { -+ int *errp; -+ struct dentry *h_dentry; -+ struct au_nhash *whlist; -+ aufs_bindex_t bindex; -+ struct au_branch *br; -+}; -+ -+static void call_del_wh_children(void *args) -+{ -+ struct del_wh_children_args *a = args; -+ *a->errp = del_wh_children(a->h_dentry, a->whlist, a->bindex, a->br); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct au_whtmp_rmdir *au_whtmp_rmdir_alloc(struct super_block *sb, gfp_t gfp) -+{ -+ struct au_whtmp_rmdir *whtmp; -+ int err; -+ unsigned int rdhash; -+ -+ SiMustAnyLock(sb); -+ -+ whtmp = kzalloc(sizeof(*whtmp), gfp); -+ if (unlikely(!whtmp)) { -+ whtmp = ERR_PTR(-ENOMEM); -+ goto out; -+ } -+ -+ /* no estimation for dir size */ -+ rdhash = au_sbi(sb)->si_rdhash; -+ if (!rdhash) -+ rdhash = AUFS_RDHASH_DEF; -+ err = au_nhash_alloc(&whtmp->whlist, rdhash, gfp); -+ if (unlikely(err)) { -+ kfree(whtmp); -+ whtmp = ERR_PTR(err); -+ } -+ -+out: -+ return whtmp; -+} -+ -+void au_whtmp_rmdir_free(struct au_whtmp_rmdir *whtmp) -+{ -+ if (whtmp->br) -+ atomic_dec(&whtmp->br->br_count); -+ dput(whtmp->wh_dentry); -+ iput(whtmp->dir); -+ au_nhash_wh_free(&whtmp->whlist); -+ kfree(whtmp); -+} -+ -+/* -+ * rmdir the whiteouted temporary named dir @h_dentry. -+ * @whlist: whiteouted children. -+ */ -+int au_whtmp_rmdir(struct inode *dir, aufs_bindex_t bindex, -+ struct dentry *wh_dentry, struct au_nhash *whlist) -+{ -+ int err; -+ unsigned int h_nlink; -+ struct path h_tmp; -+ struct inode *wh_inode, *h_dir; -+ struct au_branch *br; -+ -+ h_dir = wh_dentry->d_parent->d_inode; /* dir inode is locked */ -+ IMustLock(h_dir); -+ -+ br = au_sbr(dir->i_sb, bindex); -+ wh_inode = wh_dentry->d_inode; -+ mutex_lock_nested(&wh_inode->i_mutex, AuLsc_I_CHILD); -+ -+ /* -+ * someone else might change some whiteouts while we were sleeping. -+ * it means this whlist may have an obsoleted entry. -+ */ -+ if (!au_test_h_perm_sio(wh_inode, MAY_EXEC | MAY_WRITE)) -+ err = del_wh_children(wh_dentry, whlist, bindex, br); -+ else { -+ int wkq_err; -+ struct del_wh_children_args args = { -+ .errp = &err, -+ .h_dentry = wh_dentry, -+ .whlist = whlist, -+ .bindex = bindex, -+ .br = br -+ }; -+ -+ wkq_err = au_wkq_wait(call_del_wh_children, &args); -+ if (unlikely(wkq_err)) -+ err = wkq_err; -+ } -+ mutex_unlock(&wh_inode->i_mutex); -+ -+ if (!err) { -+ h_tmp.dentry = wh_dentry; -+ h_tmp.mnt = au_br_mnt(br); -+ h_nlink = h_dir->i_nlink; -+ err = vfsub_rmdir(h_dir, &h_tmp); -+ /* some fs doesn't change the parent nlink in some cases */ -+ h_nlink -= h_dir->i_nlink; -+ } -+ -+ if (!err) { -+ if (au_ibstart(dir) == bindex) { -+ /* todo: dir->i_mutex is necessary */ -+ au_cpup_attr_timesizes(dir); -+ if (h_nlink) -+ vfsub_drop_nlink(dir); -+ } -+ return 0; /* success */ -+ } -+ -+ pr_warn("failed removing %pd(%d), ignored\n", wh_dentry, err); -+ return err; -+} -+ -+static void call_rmdir_whtmp(void *args) -+{ -+ int err; -+ aufs_bindex_t bindex; -+ struct au_whtmp_rmdir *a = args; -+ struct super_block *sb; -+ struct dentry *h_parent; -+ struct inode *h_dir; -+ struct au_hinode *hdir; -+ -+ /* rmdir by nfsd may cause deadlock with this i_mutex */ -+ /* mutex_lock(&a->dir->i_mutex); */ -+ err = -EROFS; -+ sb = a->dir->i_sb; -+ si_read_lock(sb, !AuLock_FLUSH); -+ if (!au_br_writable(a->br->br_perm)) -+ goto out; -+ bindex = au_br_index(sb, a->br->br_id); -+ if (unlikely(bindex < 0)) -+ goto out; -+ -+ err = -EIO; -+ ii_write_lock_parent(a->dir); -+ h_parent = dget_parent(a->wh_dentry); -+ h_dir = h_parent->d_inode; -+ hdir = au_hi(a->dir, bindex); -+ err = vfsub_mnt_want_write(au_br_mnt(a->br)); -+ if (unlikely(err)) -+ goto out_mnt; -+ au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); -+ err = au_h_verify(a->wh_dentry, au_opt_udba(sb), h_dir, h_parent, -+ a->br); -+ if (!err) -+ err = au_whtmp_rmdir(a->dir, bindex, a->wh_dentry, &a->whlist); -+ au_hn_imtx_unlock(hdir); -+ vfsub_mnt_drop_write(au_br_mnt(a->br)); -+ -+out_mnt: -+ dput(h_parent); -+ ii_write_unlock(a->dir); -+out: -+ /* mutex_unlock(&a->dir->i_mutex); */ -+ au_whtmp_rmdir_free(a); -+ si_read_unlock(sb); -+ au_nwt_done(&au_sbi(sb)->si_nowait); -+ if (unlikely(err)) -+ AuIOErr("err %d\n", err); -+} -+ -+void au_whtmp_kick_rmdir(struct inode *dir, aufs_bindex_t bindex, -+ struct dentry *wh_dentry, struct au_whtmp_rmdir *args) -+{ -+ int wkq_err; -+ struct super_block *sb; -+ -+ IMustLock(dir); -+ -+ /* all post-process will be done in do_rmdir_whtmp(). */ -+ sb = dir->i_sb; -+ args->dir = au_igrab(dir); -+ args->br = au_sbr(sb, bindex); -+ atomic_inc(&args->br->br_count); -+ args->wh_dentry = dget(wh_dentry); -+ wkq_err = au_wkq_nowait(call_rmdir_whtmp, args, sb, /*flags*/0); -+ if (unlikely(wkq_err)) { -+ pr_warn("rmdir error %pd (%d), ignored\n", wh_dentry, wkq_err); -+ au_whtmp_rmdir_free(args); -+ } -+} -diff --git a/fs/aufs/whout.h b/fs/aufs/whout.h -new file mode 100644 -index 0000000..5a5c378 ---- /dev/null -+++ b/fs/aufs/whout.h -@@ -0,0 +1,85 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * whiteout for logical deletion and opaque directory -+ */ -+ -+#ifndef __AUFS_WHOUT_H__ -+#define __AUFS_WHOUT_H__ -+ -+#ifdef __KERNEL__ -+ -+#include "dir.h" -+ -+/* whout.c */ -+int au_wh_name_alloc(struct qstr *wh, const struct qstr *name); -+int au_wh_test(struct dentry *h_parent, struct qstr *wh_name, int try_sio); -+int au_diropq_test(struct dentry *h_dentry); -+struct au_branch; -+struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br, -+ struct qstr *prefix); -+int au_whtmp_ren(struct dentry *h_dentry, struct au_branch *br); -+int au_wh_unlink_dentry(struct inode *h_dir, struct path *h_path, -+ struct dentry *dentry); -+int au_wh_init(struct au_branch *br, struct super_block *sb); -+ -+/* diropq flags */ -+#define AuDiropq_CREATE 1 -+#define au_ftest_diropq(flags, name) ((flags) & AuDiropq_##name) -+#define au_fset_diropq(flags, name) \ -+ do { (flags) |= AuDiropq_##name; } while (0) -+#define au_fclr_diropq(flags, name) \ -+ do { (flags) &= ~AuDiropq_##name; } while (0) -+ -+struct dentry *au_diropq_sio(struct dentry *dentry, aufs_bindex_t bindex, -+ unsigned int flags); -+struct dentry *au_wh_lkup(struct dentry *h_parent, struct qstr *base_name, -+ struct au_branch *br); -+struct dentry *au_wh_create(struct dentry *dentry, aufs_bindex_t bindex, -+ struct dentry *h_parent); -+ -+/* real rmdir for the whiteout-ed dir */ -+struct au_whtmp_rmdir { -+ struct inode *dir; -+ struct au_branch *br; -+ struct dentry *wh_dentry; -+ struct au_nhash whlist; -+}; -+ -+struct au_whtmp_rmdir *au_whtmp_rmdir_alloc(struct super_block *sb, gfp_t gfp); -+void au_whtmp_rmdir_free(struct au_whtmp_rmdir *whtmp); -+int au_whtmp_rmdir(struct inode *dir, aufs_bindex_t bindex, -+ struct dentry *wh_dentry, struct au_nhash *whlist); -+void au_whtmp_kick_rmdir(struct inode *dir, aufs_bindex_t bindex, -+ struct dentry *wh_dentry, struct au_whtmp_rmdir *args); -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline struct dentry *au_diropq_create(struct dentry *dentry, -+ aufs_bindex_t bindex) -+{ -+ return au_diropq_sio(dentry, bindex, AuDiropq_CREATE); -+} -+ -+static inline int au_diropq_remove(struct dentry *dentry, aufs_bindex_t bindex) -+{ -+ return PTR_ERR(au_diropq_sio(dentry, bindex, !AuDiropq_CREATE)); -+} -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_WHOUT_H__ */ -diff --git a/fs/aufs/wkq.c b/fs/aufs/wkq.c -new file mode 100644 -index 0000000..a4e1b92 ---- /dev/null -+++ b/fs/aufs/wkq.c -@@ -0,0 +1,213 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * workqueue for asynchronous/super-io operations -+ * todo: try new dredential scheme -+ */ -+ -+#include -+#include "aufs.h" -+ -+/* internal workqueue named AUFS_WKQ_NAME */ -+ -+static struct workqueue_struct *au_wkq; -+ -+struct au_wkinfo { -+ struct work_struct wk; -+ struct kobject *kobj; -+ -+ unsigned int flags; /* see wkq.h */ -+ -+ au_wkq_func_t func; -+ void *args; -+ -+ struct completion *comp; -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void wkq_func(struct work_struct *wk) -+{ -+ struct au_wkinfo *wkinfo = container_of(wk, struct au_wkinfo, wk); -+ -+ AuDebugOn(!uid_eq(current_fsuid(), GLOBAL_ROOT_UID)); -+ AuDebugOn(rlimit(RLIMIT_FSIZE) != RLIM_INFINITY); -+ -+ wkinfo->func(wkinfo->args); -+ if (au_ftest_wkq(wkinfo->flags, WAIT)) -+ complete(wkinfo->comp); -+ else { -+ kobject_put(wkinfo->kobj); -+ module_put(THIS_MODULE); /* todo: ?? */ -+ kfree(wkinfo); -+ } -+} -+ -+/* -+ * Since struct completion is large, try allocating it dynamically. -+ */ -+#if 1 /* defined(CONFIG_4KSTACKS) || defined(AuTest4KSTACKS) */ -+#define AuWkqCompDeclare(name) struct completion *comp = NULL -+ -+static int au_wkq_comp_alloc(struct au_wkinfo *wkinfo, struct completion **comp) -+{ -+ *comp = kmalloc(sizeof(**comp), GFP_NOFS); -+ if (*comp) { -+ init_completion(*comp); -+ wkinfo->comp = *comp; -+ return 0; -+ } -+ return -ENOMEM; -+} -+ -+static void au_wkq_comp_free(struct completion *comp) -+{ -+ kfree(comp); -+} -+ -+#else -+ -+/* no braces */ -+#define AuWkqCompDeclare(name) \ -+ DECLARE_COMPLETION_ONSTACK(_ ## name); \ -+ struct completion *comp = &_ ## name -+ -+static int au_wkq_comp_alloc(struct au_wkinfo *wkinfo, struct completion **comp) -+{ -+ wkinfo->comp = *comp; -+ return 0; -+} -+ -+static void au_wkq_comp_free(struct completion *comp __maybe_unused) -+{ -+ /* empty */ -+} -+#endif /* 4KSTACKS */ -+ -+static void au_wkq_run(struct au_wkinfo *wkinfo) -+{ -+ if (au_ftest_wkq(wkinfo->flags, NEST)) { -+ if (au_wkq_test()) { -+ AuWarn1("wkq from wkq, unless silly-rename on NFS," -+ " due to a dead dir by UDBA?\n"); -+ AuDebugOn(au_ftest_wkq(wkinfo->flags, WAIT)); -+ } -+ } else -+ au_dbg_verify_kthread(); -+ -+ if (au_ftest_wkq(wkinfo->flags, WAIT)) { -+ INIT_WORK_ONSTACK(&wkinfo->wk, wkq_func); -+ queue_work(au_wkq, &wkinfo->wk); -+ } else { -+ INIT_WORK(&wkinfo->wk, wkq_func); -+ schedule_work(&wkinfo->wk); -+ } -+} -+ -+/* -+ * Be careful. It is easy to make deadlock happen. -+ * processA: lock, wkq and wait -+ * processB: wkq and wait, lock in wkq -+ * --> deadlock -+ */ -+int au_wkq_do_wait(unsigned int flags, au_wkq_func_t func, void *args) -+{ -+ int err; -+ AuWkqCompDeclare(comp); -+ struct au_wkinfo wkinfo = { -+ .flags = flags, -+ .func = func, -+ .args = args -+ }; -+ -+ err = au_wkq_comp_alloc(&wkinfo, &comp); -+ if (!err) { -+ au_wkq_run(&wkinfo); -+ /* no timeout, no interrupt */ -+ wait_for_completion(wkinfo.comp); -+ au_wkq_comp_free(comp); -+ destroy_work_on_stack(&wkinfo.wk); -+ } -+ -+ return err; -+ -+} -+ -+/* -+ * Note: dget/dput() in func for aufs dentries are not supported. It will be a -+ * problem in a concurrent umounting. -+ */ -+int au_wkq_nowait(au_wkq_func_t func, void *args, struct super_block *sb, -+ unsigned int flags) -+{ -+ int err; -+ struct au_wkinfo *wkinfo; -+ -+ atomic_inc(&au_sbi(sb)->si_nowait.nw_len); -+ -+ /* -+ * wkq_func() must free this wkinfo. -+ * it highly depends upon the implementation of workqueue. -+ */ -+ err = 0; -+ wkinfo = kmalloc(sizeof(*wkinfo), GFP_NOFS); -+ if (wkinfo) { -+ wkinfo->kobj = &au_sbi(sb)->si_kobj; -+ wkinfo->flags = flags & ~AuWkq_WAIT; -+ wkinfo->func = func; -+ wkinfo->args = args; -+ wkinfo->comp = NULL; -+ kobject_get(wkinfo->kobj); -+ __module_get(THIS_MODULE); /* todo: ?? */ -+ -+ au_wkq_run(wkinfo); -+ } else { -+ err = -ENOMEM; -+ au_nwt_done(&au_sbi(sb)->si_nowait); -+ } -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void au_nwt_init(struct au_nowait_tasks *nwt) -+{ -+ atomic_set(&nwt->nw_len, 0); -+ /* smp_mb(); */ /* atomic_set */ -+ init_waitqueue_head(&nwt->nw_wq); -+} -+ -+void au_wkq_fin(void) -+{ -+ destroy_workqueue(au_wkq); -+} -+ -+int __init au_wkq_init(void) -+{ -+ int err; -+ -+ err = 0; -+ au_wkq = alloc_workqueue(AUFS_WKQ_NAME, 0, WQ_DFL_ACTIVE); -+ if (IS_ERR(au_wkq)) -+ err = PTR_ERR(au_wkq); -+ else if (!au_wkq) -+ err = -ENOMEM; -+ -+ return err; -+} -diff --git a/fs/aufs/wkq.h b/fs/aufs/wkq.h -new file mode 100644 -index 0000000..830123c ---- /dev/null -+++ b/fs/aufs/wkq.h -@@ -0,0 +1,91 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * workqueue for asynchronous/super-io operations -+ * todo: try new credentials management scheme -+ */ -+ -+#ifndef __AUFS_WKQ_H__ -+#define __AUFS_WKQ_H__ -+ -+#ifdef __KERNEL__ -+ -+struct super_block; -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * in the next operation, wait for the 'nowait' tasks in system-wide workqueue -+ */ -+struct au_nowait_tasks { -+ atomic_t nw_len; -+ wait_queue_head_t nw_wq; -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+typedef void (*au_wkq_func_t)(void *args); -+ -+/* wkq flags */ -+#define AuWkq_WAIT 1 -+#define AuWkq_NEST (1 << 1) -+#define au_ftest_wkq(flags, name) ((flags) & AuWkq_##name) -+#define au_fset_wkq(flags, name) \ -+ do { (flags) |= AuWkq_##name; } while (0) -+#define au_fclr_wkq(flags, name) \ -+ do { (flags) &= ~AuWkq_##name; } while (0) -+ -+#ifndef CONFIG_AUFS_HNOTIFY -+#undef AuWkq_NEST -+#define AuWkq_NEST 0 -+#endif -+ -+/* wkq.c */ -+int au_wkq_do_wait(unsigned int flags, au_wkq_func_t func, void *args); -+int au_wkq_nowait(au_wkq_func_t func, void *args, struct super_block *sb, -+ unsigned int flags); -+void au_nwt_init(struct au_nowait_tasks *nwt); -+int __init au_wkq_init(void); -+void au_wkq_fin(void); -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline int au_wkq_test(void) -+{ -+ return current->flags & PF_WQ_WORKER; -+} -+ -+static inline int au_wkq_wait(au_wkq_func_t func, void *args) -+{ -+ return au_wkq_do_wait(AuWkq_WAIT, func, args); -+} -+ -+static inline void au_nwt_done(struct au_nowait_tasks *nwt) -+{ -+ if (atomic_dec_and_test(&nwt->nw_len)) -+ wake_up_all(&nwt->nw_wq); -+} -+ -+static inline int au_nwt_flush(struct au_nowait_tasks *nwt) -+{ -+ wait_event(nwt->nw_wq, !atomic_read(&nwt->nw_len)); -+ return 0; -+} -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_WKQ_H__ */ -diff --git a/fs/aufs/xattr.c b/fs/aufs/xattr.c -new file mode 100644 -index 0000000..e16beea ---- /dev/null -+++ b/fs/aufs/xattr.c -@@ -0,0 +1,344 @@ -+/* -+ * Copyright (C) 2014-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * handling xattr functions -+ */ -+ -+#include -+#include "aufs.h" -+ -+static int au_xattr_ignore(int err, char *name, unsigned int ignore_flags) -+{ -+ if (!ignore_flags) -+ goto out; -+ switch (err) { -+ case -ENOMEM: -+ case -EDQUOT: -+ goto out; -+ } -+ -+ if ((ignore_flags & AuBrAttr_ICEX) == AuBrAttr_ICEX) { -+ err = 0; -+ goto out; -+ } -+ -+#define cmp(brattr, prefix) do { \ -+ if (!strncmp(name, XATTR_##prefix##_PREFIX, \ -+ XATTR_##prefix##_PREFIX_LEN)) { \ -+ if (ignore_flags & AuBrAttr_ICEX_##brattr) \ -+ err = 0; \ -+ goto out; \ -+ } \ -+ } while (0) -+ -+ cmp(SEC, SECURITY); -+ cmp(SYS, SYSTEM); -+ cmp(TR, TRUSTED); -+ cmp(USR, USER); -+#undef cmp -+ -+ if (ignore_flags & AuBrAttr_ICEX_OTH) -+ err = 0; -+ -+out: -+ return err; -+} -+ -+static const int au_xattr_out_of_list = AuBrAttr_ICEX_OTH << 1; -+ -+static int au_do_cpup_xattr(struct dentry *h_dst, struct dentry *h_src, -+ char *name, char **buf, unsigned int ignore_flags, -+ unsigned int verbose) -+{ -+ int err; -+ ssize_t ssz; -+ struct inode *h_idst; -+ -+ ssz = vfs_getxattr_alloc(h_src, name, buf, 0, GFP_NOFS); -+ err = ssz; -+ if (unlikely(err <= 0)) { -+ if (err == -ENODATA -+ || (err == -EOPNOTSUPP -+ && ((ignore_flags & au_xattr_out_of_list) -+ || (au_test_nfs_noacl(h_src->d_inode) -+ && (!strcmp(name, XATTR_NAME_POSIX_ACL_ACCESS) -+ || !strcmp(name, -+ XATTR_NAME_POSIX_ACL_DEFAULT)))) -+ )) -+ err = 0; -+ if (err && (verbose || au_debug_test())) -+ pr_err("%s, err %d\n", name, err); -+ goto out; -+ } -+ -+ /* unlock it temporary */ -+ h_idst = h_dst->d_inode; -+ mutex_unlock(&h_idst->i_mutex); -+ err = vfsub_setxattr(h_dst, name, *buf, ssz, /*flags*/0); -+ mutex_lock_nested(&h_idst->i_mutex, AuLsc_I_CHILD2); -+ if (unlikely(err)) { -+ if (verbose || au_debug_test()) -+ pr_err("%s, err %d\n", name, err); -+ err = au_xattr_ignore(err, name, ignore_flags); -+ } -+ -+out: -+ return err; -+} -+ -+int au_cpup_xattr(struct dentry *h_dst, struct dentry *h_src, int ignore_flags, -+ unsigned int verbose) -+{ -+ int err, unlocked, acl_access, acl_default; -+ ssize_t ssz; -+ struct inode *h_isrc, *h_idst; -+ char *value, *p, *o, *e; -+ -+ /* try stopping to update the source inode while we are referencing */ -+ /* there should not be the parent-child relationship between them */ -+ h_isrc = h_src->d_inode; -+ h_idst = h_dst->d_inode; -+ mutex_unlock(&h_idst->i_mutex); -+ mutex_lock_nested(&h_isrc->i_mutex, AuLsc_I_CHILD); -+ mutex_lock_nested(&h_idst->i_mutex, AuLsc_I_CHILD2); -+ unlocked = 0; -+ -+ /* some filesystems don't list POSIX ACL, for example tmpfs */ -+ ssz = vfs_listxattr(h_src, NULL, 0); -+ err = ssz; -+ if (unlikely(err < 0)) { -+ AuTraceErr(err); -+ if (err == -ENODATA -+ || err == -EOPNOTSUPP) -+ err = 0; /* ignore */ -+ goto out; -+ } -+ -+ err = 0; -+ p = NULL; -+ o = NULL; -+ if (ssz) { -+ err = -ENOMEM; -+ p = kmalloc(ssz, GFP_NOFS); -+ o = p; -+ if (unlikely(!p)) -+ goto out; -+ err = vfs_listxattr(h_src, p, ssz); -+ } -+ mutex_unlock(&h_isrc->i_mutex); -+ unlocked = 1; -+ AuDbg("err %d, ssz %zd\n", err, ssz); -+ if (unlikely(err < 0)) -+ goto out_free; -+ -+ err = 0; -+ e = p + ssz; -+ value = NULL; -+ acl_access = 0; -+ acl_default = 0; -+ while (!err && p < e) { -+ acl_access |= !strncmp(p, XATTR_NAME_POSIX_ACL_ACCESS, -+ sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1); -+ acl_default |= !strncmp(p, XATTR_NAME_POSIX_ACL_DEFAULT, -+ sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) -+ - 1); -+ err = au_do_cpup_xattr(h_dst, h_src, p, &value, ignore_flags, -+ verbose); -+ p += strlen(p) + 1; -+ } -+ AuTraceErr(err); -+ ignore_flags |= au_xattr_out_of_list; -+ if (!err && !acl_access) { -+ err = au_do_cpup_xattr(h_dst, h_src, -+ XATTR_NAME_POSIX_ACL_ACCESS, &value, -+ ignore_flags, verbose); -+ AuTraceErr(err); -+ } -+ if (!err && !acl_default) { -+ err = au_do_cpup_xattr(h_dst, h_src, -+ XATTR_NAME_POSIX_ACL_DEFAULT, &value, -+ ignore_flags, verbose); -+ AuTraceErr(err); -+ } -+ -+ kfree(value); -+ -+out_free: -+ kfree(o); -+out: -+ if (!unlocked) -+ mutex_unlock(&h_isrc->i_mutex); -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+enum { -+ AU_XATTR_LIST, -+ AU_XATTR_GET -+}; -+ -+struct au_lgxattr { -+ int type; -+ union { -+ struct { -+ char *list; -+ size_t size; -+ } list; -+ struct { -+ const char *name; -+ void *value; -+ size_t size; -+ } get; -+ } u; -+}; -+ -+static ssize_t au_lgxattr(struct dentry *dentry, struct au_lgxattr *arg) -+{ -+ ssize_t err; -+ struct path h_path; -+ struct super_block *sb; -+ -+ sb = dentry->d_sb; -+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ if (unlikely(err)) -+ goto out; -+ err = au_h_path_getattr(dentry, /*force*/1, &h_path); -+ if (unlikely(err)) -+ goto out_si; -+ if (unlikely(!h_path.dentry)) -+ /* illegally overlapped or something */ -+ goto out_di; /* pretending success */ -+ -+ /* always topmost entry only */ -+ switch (arg->type) { -+ case AU_XATTR_LIST: -+ err = vfs_listxattr(h_path.dentry, -+ arg->u.list.list, arg->u.list.size); -+ break; -+ case AU_XATTR_GET: -+ err = vfs_getxattr(h_path.dentry, -+ arg->u.get.name, arg->u.get.value, -+ arg->u.get.size); -+ break; -+ } -+ -+out_di: -+ di_read_unlock(dentry, AuLock_IR); -+out_si: -+ si_read_unlock(sb); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+ssize_t aufs_listxattr(struct dentry *dentry, char *list, size_t size) -+{ -+ struct au_lgxattr arg = { -+ .type = AU_XATTR_LIST, -+ .u.list = { -+ .list = list, -+ .size = size -+ }, -+ }; -+ -+ return au_lgxattr(dentry, &arg); -+} -+ -+ssize_t aufs_getxattr(struct dentry *dentry, const char *name, void *value, -+ size_t size) -+{ -+ struct au_lgxattr arg = { -+ .type = AU_XATTR_GET, -+ .u.get = { -+ .name = name, -+ .value = value, -+ .size = size -+ }, -+ }; -+ -+ return au_lgxattr(dentry, &arg); -+} -+ -+int aufs_setxattr(struct dentry *dentry, const char *name, const void *value, -+ size_t size, int flags) -+{ -+ struct au_srxattr arg = { -+ .type = AU_XATTR_SET, -+ .u.set = { -+ .name = name, -+ .value = value, -+ .size = size, -+ .flags = flags -+ }, -+ }; -+ -+ return au_srxattr(dentry, &arg); -+} -+ -+int aufs_removexattr(struct dentry *dentry, const char *name) -+{ -+ struct au_srxattr arg = { -+ .type = AU_XATTR_REMOVE, -+ .u.remove = { -+ .name = name -+ }, -+ }; -+ -+ return au_srxattr(dentry, &arg); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+#if 0 -+static size_t au_xattr_list(struct dentry *dentry, char *list, size_t list_size, -+ const char *name, size_t name_len, int type) -+{ -+ return aufs_listxattr(dentry, list, list_size); -+} -+ -+static int au_xattr_get(struct dentry *dentry, const char *name, void *buffer, -+ size_t size, int type) -+{ -+ return aufs_getxattr(dentry, name, buffer, size); -+} -+ -+static int au_xattr_set(struct dentry *dentry, const char *name, -+ const void *value, size_t size, int flags, int type) -+{ -+ return aufs_setxattr(dentry, name, value, size, flags); -+} -+ -+static const struct xattr_handler au_xattr_handler = { -+ /* no prefix, no flags */ -+ .list = au_xattr_list, -+ .get = au_xattr_get, -+ .set = au_xattr_set -+ /* why no remove? */ -+}; -+ -+static const struct xattr_handler *au_xattr_handlers[] = { -+ &au_xattr_handler -+}; -+ -+void au_xattr_init(struct super_block *sb) -+{ -+ /* sb->s_xattr = au_xattr_handlers; */ -+} -+#endif -diff --git a/fs/aufs/xino.c b/fs/aufs/xino.c -new file mode 100644 -index 0000000..50ab4ca ---- /dev/null -+++ b/fs/aufs/xino.c -@@ -0,0 +1,1343 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * external inode number translation table and bitmap -+ */ -+ -+#include -+#include -+#include "aufs.h" -+ -+/* todo: unnecessary to support mmap_sem since kernel-space? */ -+ssize_t xino_fread(au_readf_t func, struct file *file, void *kbuf, size_t size, -+ loff_t *pos) -+{ -+ ssize_t err; -+ mm_segment_t oldfs; -+ union { -+ void *k; -+ char __user *u; -+ } buf; -+ -+ buf.k = kbuf; -+ oldfs = get_fs(); -+ set_fs(KERNEL_DS); -+ do { -+ /* todo: signal_pending? */ -+ err = func(file, buf.u, size, pos); -+ } while (err == -EAGAIN || err == -EINTR); -+ set_fs(oldfs); -+ -+#if 0 /* reserved for future use */ -+ if (err > 0) -+ fsnotify_access(file->f_dentry); -+#endif -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static ssize_t xino_fwrite_wkq(au_writef_t func, struct file *file, void *buf, -+ size_t size, loff_t *pos); -+ -+static ssize_t do_xino_fwrite(au_writef_t func, struct file *file, void *kbuf, -+ size_t size, loff_t *pos) -+{ -+ ssize_t err; -+ mm_segment_t oldfs; -+ union { -+ void *k; -+ const char __user *u; -+ } buf; -+ int i; -+ const int prevent_endless = 10; -+ -+ i = 0; -+ buf.k = kbuf; -+ oldfs = get_fs(); -+ set_fs(KERNEL_DS); -+ do { -+ err = func(file, buf.u, size, pos); -+ if (err == -EINTR -+ && !au_wkq_test() -+ && fatal_signal_pending(current)) { -+ set_fs(oldfs); -+ err = xino_fwrite_wkq(func, file, kbuf, size, pos); -+ BUG_ON(err == -EINTR); -+ oldfs = get_fs(); -+ set_fs(KERNEL_DS); -+ } -+ } while (i++ < prevent_endless -+ && (err == -EAGAIN || err == -EINTR)); -+ set_fs(oldfs); -+ -+#if 0 /* reserved for future use */ -+ if (err > 0) -+ fsnotify_modify(file->f_dentry); -+#endif -+ -+ return err; -+} -+ -+struct do_xino_fwrite_args { -+ ssize_t *errp; -+ au_writef_t func; -+ struct file *file; -+ void *buf; -+ size_t size; -+ loff_t *pos; -+}; -+ -+static void call_do_xino_fwrite(void *args) -+{ -+ struct do_xino_fwrite_args *a = args; -+ *a->errp = do_xino_fwrite(a->func, a->file, a->buf, a->size, a->pos); -+} -+ -+static ssize_t xino_fwrite_wkq(au_writef_t func, struct file *file, void *buf, -+ size_t size, loff_t *pos) -+{ -+ ssize_t err; -+ int wkq_err; -+ struct do_xino_fwrite_args args = { -+ .errp = &err, -+ .func = func, -+ .file = file, -+ .buf = buf, -+ .size = size, -+ .pos = pos -+ }; -+ -+ /* -+ * it breaks RLIMIT_FSIZE and normal user's limit, -+ * users should care about quota and real 'filesystem full.' -+ */ -+ wkq_err = au_wkq_wait(call_do_xino_fwrite, &args); -+ if (unlikely(wkq_err)) -+ err = wkq_err; -+ -+ return err; -+} -+ -+ssize_t xino_fwrite(au_writef_t func, struct file *file, void *buf, size_t size, -+ loff_t *pos) -+{ -+ ssize_t err; -+ -+ if (rlimit(RLIMIT_FSIZE) == RLIM_INFINITY) { -+ lockdep_off(); -+ err = do_xino_fwrite(func, file, buf, size, pos); -+ lockdep_on(); -+ } else -+ err = xino_fwrite_wkq(func, file, buf, size, pos); -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * create a new xinofile at the same place/path as @base_file. -+ */ -+struct file *au_xino_create2(struct file *base_file, struct file *copy_src) -+{ -+ struct file *file; -+ struct dentry *base, *parent; -+ struct inode *dir, *delegated; -+ struct qstr *name; -+ struct path path; -+ int err; -+ -+ base = base_file->f_dentry; -+ parent = base->d_parent; /* dir inode is locked */ -+ dir = parent->d_inode; -+ IMustLock(dir); -+ -+ file = ERR_PTR(-EINVAL); -+ name = &base->d_name; -+ path.dentry = vfsub_lookup_one_len(name->name, parent, name->len); -+ if (IS_ERR(path.dentry)) { -+ file = (void *)path.dentry; -+ pr_err("%pd lookup err %ld\n", -+ base, PTR_ERR(path.dentry)); -+ goto out; -+ } -+ -+ /* no need to mnt_want_write() since we call dentry_open() later */ -+ err = vfs_create(dir, path.dentry, S_IRUGO | S_IWUGO, NULL); -+ if (unlikely(err)) { -+ file = ERR_PTR(err); -+ pr_err("%pd create err %d\n", base, err); -+ goto out_dput; -+ } -+ -+ path.mnt = base_file->f_path.mnt; -+ file = vfsub_dentry_open(&path, -+ O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE -+ /* | __FMODE_NONOTIFY */); -+ if (IS_ERR(file)) { -+ pr_err("%pd open err %ld\n", base, PTR_ERR(file)); -+ goto out_dput; -+ } -+ -+ delegated = NULL; -+ err = vfsub_unlink(dir, &file->f_path, &delegated, /*force*/0); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal unlink\n"); -+ iput(delegated); -+ } -+ if (unlikely(err)) { -+ pr_err("%pd unlink err %d\n", base, err); -+ goto out_fput; -+ } -+ -+ if (copy_src) { -+ /* no one can touch copy_src xino */ -+ err = au_copy_file(file, copy_src, vfsub_f_size_read(copy_src)); -+ if (unlikely(err)) { -+ pr_err("%pd copy err %d\n", base, err); -+ goto out_fput; -+ } -+ } -+ goto out_dput; /* success */ -+ -+out_fput: -+ fput(file); -+ file = ERR_PTR(err); -+out_dput: -+ dput(path.dentry); -+out: -+ return file; -+} -+ -+struct au_xino_lock_dir { -+ struct au_hinode *hdir; -+ struct dentry *parent; -+ struct mutex *mtx; -+}; -+ -+static void au_xino_lock_dir(struct super_block *sb, struct file *xino, -+ struct au_xino_lock_dir *ldir) -+{ -+ aufs_bindex_t brid, bindex; -+ -+ ldir->hdir = NULL; -+ bindex = -1; -+ brid = au_xino_brid(sb); -+ if (brid >= 0) -+ bindex = au_br_index(sb, brid); -+ if (bindex >= 0) { -+ ldir->hdir = au_hi(sb->s_root->d_inode, bindex); -+ au_hn_imtx_lock_nested(ldir->hdir, AuLsc_I_PARENT); -+ } else { -+ ldir->parent = dget_parent(xino->f_dentry); -+ ldir->mtx = &ldir->parent->d_inode->i_mutex; -+ mutex_lock_nested(ldir->mtx, AuLsc_I_PARENT); -+ } -+} -+ -+static void au_xino_unlock_dir(struct au_xino_lock_dir *ldir) -+{ -+ if (ldir->hdir) -+ au_hn_imtx_unlock(ldir->hdir); -+ else { -+ mutex_unlock(ldir->mtx); -+ dput(ldir->parent); -+ } -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* trucate xino files asynchronously */ -+ -+int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ int err; -+ unsigned long jiffy; -+ blkcnt_t blocks; -+ aufs_bindex_t bi, bend; -+ struct kstatfs *st; -+ struct au_branch *br; -+ struct file *new_xino, *file; -+ struct super_block *h_sb; -+ struct au_xino_lock_dir ldir; -+ -+ err = -ENOMEM; -+ st = kmalloc(sizeof(*st), GFP_NOFS); -+ if (unlikely(!st)) -+ goto out; -+ -+ err = -EINVAL; -+ bend = au_sbend(sb); -+ if (unlikely(bindex < 0 || bend < bindex)) -+ goto out_st; -+ br = au_sbr(sb, bindex); -+ file = br->br_xino.xi_file; -+ if (!file) -+ goto out_st; -+ -+ err = vfs_statfs(&file->f_path, st); -+ if (unlikely(err)) -+ AuErr1("statfs err %d, ignored\n", err); -+ jiffy = jiffies; -+ blocks = file_inode(file)->i_blocks; -+ pr_info("begin truncating xino(b%d), ib%llu, %llu/%llu free blks\n", -+ bindex, (u64)blocks, st->f_bfree, st->f_blocks); -+ -+ au_xino_lock_dir(sb, file, &ldir); -+ /* mnt_want_write() is unnecessary here */ -+ new_xino = au_xino_create2(file, file); -+ au_xino_unlock_dir(&ldir); -+ err = PTR_ERR(new_xino); -+ if (IS_ERR(new_xino)) { -+ pr_err("err %d, ignored\n", err); -+ goto out_st; -+ } -+ err = 0; -+ fput(file); -+ br->br_xino.xi_file = new_xino; -+ -+ h_sb = au_br_sb(br); -+ for (bi = 0; bi <= bend; bi++) { -+ if (unlikely(bi == bindex)) -+ continue; -+ br = au_sbr(sb, bi); -+ if (au_br_sb(br) != h_sb) -+ continue; -+ -+ fput(br->br_xino.xi_file); -+ br->br_xino.xi_file = new_xino; -+ get_file(new_xino); -+ } -+ -+ err = vfs_statfs(&new_xino->f_path, st); -+ if (!err) { -+ pr_info("end truncating xino(b%d), ib%llu, %llu/%llu free blks\n", -+ bindex, (u64)file_inode(new_xino)->i_blocks, -+ st->f_bfree, st->f_blocks); -+ if (file_inode(new_xino)->i_blocks < blocks) -+ au_sbi(sb)->si_xino_jiffy = jiffy; -+ } else -+ AuErr1("statfs err %d, ignored\n", err); -+ -+out_st: -+ kfree(st); -+out: -+ return err; -+} -+ -+struct xino_do_trunc_args { -+ struct super_block *sb; -+ struct au_branch *br; -+}; -+ -+static void xino_do_trunc(void *_args) -+{ -+ struct xino_do_trunc_args *args = _args; -+ struct super_block *sb; -+ struct au_branch *br; -+ struct inode *dir; -+ int err; -+ aufs_bindex_t bindex; -+ -+ err = 0; -+ sb = args->sb; -+ dir = sb->s_root->d_inode; -+ br = args->br; -+ -+ si_noflush_write_lock(sb); -+ ii_read_lock_parent(dir); -+ bindex = au_br_index(sb, br->br_id); -+ err = au_xino_trunc(sb, bindex); -+ ii_read_unlock(dir); -+ if (unlikely(err)) -+ pr_warn("err b%d, (%d)\n", bindex, err); -+ atomic_dec(&br->br_xino_running); -+ atomic_dec(&br->br_count); -+ si_write_unlock(sb); -+ au_nwt_done(&au_sbi(sb)->si_nowait); -+ kfree(args); -+} -+ -+static int xino_trunc_test(struct super_block *sb, struct au_branch *br) -+{ -+ int err; -+ struct kstatfs st; -+ struct au_sbinfo *sbinfo; -+ -+ /* todo: si_xino_expire and the ratio should be customizable */ -+ sbinfo = au_sbi(sb); -+ if (time_before(jiffies, -+ sbinfo->si_xino_jiffy + sbinfo->si_xino_expire)) -+ return 0; -+ -+ /* truncation border */ -+ err = vfs_statfs(&br->br_xino.xi_file->f_path, &st); -+ if (unlikely(err)) { -+ AuErr1("statfs err %d, ignored\n", err); -+ return 0; -+ } -+ if (div64_u64(st.f_bfree * 100, st.f_blocks) >= AUFS_XINO_DEF_TRUNC) -+ return 0; -+ -+ return 1; -+} -+ -+static void xino_try_trunc(struct super_block *sb, struct au_branch *br) -+{ -+ struct xino_do_trunc_args *args; -+ int wkq_err; -+ -+ if (!xino_trunc_test(sb, br)) -+ return; -+ -+ if (atomic_inc_return(&br->br_xino_running) > 1) -+ goto out; -+ -+ /* lock and kfree() will be called in trunc_xino() */ -+ args = kmalloc(sizeof(*args), GFP_NOFS); -+ if (unlikely(!args)) { -+ AuErr1("no memory\n"); -+ goto out_args; -+ } -+ -+ atomic_inc(&br->br_count); -+ args->sb = sb; -+ args->br = br; -+ wkq_err = au_wkq_nowait(xino_do_trunc, args, sb, /*flags*/0); -+ if (!wkq_err) -+ return; /* success */ -+ -+ pr_err("wkq %d\n", wkq_err); -+ atomic_dec(&br->br_count); -+ -+out_args: -+ kfree(args); -+out: -+ atomic_dec(&br->br_xino_running); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_xino_do_write(au_writef_t write, struct file *file, -+ ino_t h_ino, ino_t ino) -+{ -+ loff_t pos; -+ ssize_t sz; -+ -+ pos = h_ino; -+ if (unlikely(au_loff_max / sizeof(ino) - 1 < pos)) { -+ AuIOErr1("too large hi%lu\n", (unsigned long)h_ino); -+ return -EFBIG; -+ } -+ pos *= sizeof(ino); -+ sz = xino_fwrite(write, file, &ino, sizeof(ino), &pos); -+ if (sz == sizeof(ino)) -+ return 0; /* success */ -+ -+ AuIOErr("write failed (%zd)\n", sz); -+ return -EIO; -+} -+ -+/* -+ * write @ino to the xinofile for the specified branch{@sb, @bindex} -+ * at the position of @h_ino. -+ * even if @ino is zero, it is written to the xinofile and means no entry. -+ * if the size of the xino file on a specific filesystem exceeds the watermark, -+ * try truncating it. -+ */ -+int au_xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, -+ ino_t ino) -+{ -+ int err; -+ unsigned int mnt_flags; -+ struct au_branch *br; -+ -+ BUILD_BUG_ON(sizeof(long long) != sizeof(au_loff_max) -+ || ((loff_t)-1) > 0); -+ SiMustAnyLock(sb); -+ -+ mnt_flags = au_mntflags(sb); -+ if (!au_opt_test(mnt_flags, XINO)) -+ return 0; -+ -+ br = au_sbr(sb, bindex); -+ err = au_xino_do_write(au_sbi(sb)->si_xwrite, br->br_xino.xi_file, -+ h_ino, ino); -+ if (!err) { -+ if (au_opt_test(mnt_flags, TRUNC_XINO) -+ && au_test_fs_trunc_xino(au_br_sb(br))) -+ xino_try_trunc(sb, br); -+ return 0; /* success */ -+ } -+ -+ AuIOErr("write failed (%d)\n", err); -+ return -EIO; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* aufs inode number bitmap */ -+ -+static const int page_bits = (int)PAGE_SIZE * BITS_PER_BYTE; -+static ino_t xib_calc_ino(unsigned long pindex, int bit) -+{ -+ ino_t ino; -+ -+ AuDebugOn(bit < 0 || page_bits <= bit); -+ ino = AUFS_FIRST_INO + pindex * page_bits + bit; -+ return ino; -+} -+ -+static void xib_calc_bit(ino_t ino, unsigned long *pindex, int *bit) -+{ -+ AuDebugOn(ino < AUFS_FIRST_INO); -+ ino -= AUFS_FIRST_INO; -+ *pindex = ino / page_bits; -+ *bit = ino % page_bits; -+} -+ -+static int xib_pindex(struct super_block *sb, unsigned long pindex) -+{ -+ int err; -+ loff_t pos; -+ ssize_t sz; -+ struct au_sbinfo *sbinfo; -+ struct file *xib; -+ unsigned long *p; -+ -+ sbinfo = au_sbi(sb); -+ MtxMustLock(&sbinfo->si_xib_mtx); -+ AuDebugOn(pindex > ULONG_MAX / PAGE_SIZE -+ || !au_opt_test(sbinfo->si_mntflags, XINO)); -+ -+ if (pindex == sbinfo->si_xib_last_pindex) -+ return 0; -+ -+ xib = sbinfo->si_xib; -+ p = sbinfo->si_xib_buf; -+ pos = sbinfo->si_xib_last_pindex; -+ pos *= PAGE_SIZE; -+ sz = xino_fwrite(sbinfo->si_xwrite, xib, p, PAGE_SIZE, &pos); -+ if (unlikely(sz != PAGE_SIZE)) -+ goto out; -+ -+ pos = pindex; -+ pos *= PAGE_SIZE; -+ if (vfsub_f_size_read(xib) >= pos + PAGE_SIZE) -+ sz = xino_fread(sbinfo->si_xread, xib, p, PAGE_SIZE, &pos); -+ else { -+ memset(p, 0, PAGE_SIZE); -+ sz = xino_fwrite(sbinfo->si_xwrite, xib, p, PAGE_SIZE, &pos); -+ } -+ if (sz == PAGE_SIZE) { -+ sbinfo->si_xib_last_pindex = pindex; -+ return 0; /* success */ -+ } -+ -+out: -+ AuIOErr1("write failed (%zd)\n", sz); -+ err = sz; -+ if (sz >= 0) -+ err = -EIO; -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void au_xib_clear_bit(struct inode *inode) -+{ -+ int err, bit; -+ unsigned long pindex; -+ struct super_block *sb; -+ struct au_sbinfo *sbinfo; -+ -+ AuDebugOn(inode->i_nlink); -+ -+ sb = inode->i_sb; -+ xib_calc_bit(inode->i_ino, &pindex, &bit); -+ AuDebugOn(page_bits <= bit); -+ sbinfo = au_sbi(sb); -+ mutex_lock(&sbinfo->si_xib_mtx); -+ err = xib_pindex(sb, pindex); -+ if (!err) { -+ clear_bit(bit, sbinfo->si_xib_buf); -+ sbinfo->si_xib_next_bit = bit; -+ } -+ mutex_unlock(&sbinfo->si_xib_mtx); -+} -+ -+/* for s_op->delete_inode() */ -+void au_xino_delete_inode(struct inode *inode, const int unlinked) -+{ -+ int err; -+ unsigned int mnt_flags; -+ aufs_bindex_t bindex, bend, bi; -+ unsigned char try_trunc; -+ struct au_iinfo *iinfo; -+ struct super_block *sb; -+ struct au_hinode *hi; -+ struct inode *h_inode; -+ struct au_branch *br; -+ au_writef_t xwrite; -+ -+ sb = inode->i_sb; -+ mnt_flags = au_mntflags(sb); -+ if (!au_opt_test(mnt_flags, XINO) -+ || inode->i_ino == AUFS_ROOT_INO) -+ return; -+ -+ if (unlinked) { -+ au_xigen_inc(inode); -+ au_xib_clear_bit(inode); -+ } -+ -+ iinfo = au_ii(inode); -+ if (!iinfo) -+ return; -+ -+ bindex = iinfo->ii_bstart; -+ if (bindex < 0) -+ return; -+ -+ xwrite = au_sbi(sb)->si_xwrite; -+ try_trunc = !!au_opt_test(mnt_flags, TRUNC_XINO); -+ hi = iinfo->ii_hinode + bindex; -+ bend = iinfo->ii_bend; -+ for (; bindex <= bend; bindex++, hi++) { -+ h_inode = hi->hi_inode; -+ if (!h_inode -+ || (!unlinked && h_inode->i_nlink)) -+ continue; -+ -+ /* inode may not be revalidated */ -+ bi = au_br_index(sb, hi->hi_id); -+ if (bi < 0) -+ continue; -+ -+ br = au_sbr(sb, bi); -+ err = au_xino_do_write(xwrite, br->br_xino.xi_file, -+ h_inode->i_ino, /*ino*/0); -+ if (!err && try_trunc -+ && au_test_fs_trunc_xino(au_br_sb(br))) -+ xino_try_trunc(sb, br); -+ } -+} -+ -+/* get an unused inode number from bitmap */ -+ino_t au_xino_new_ino(struct super_block *sb) -+{ -+ ino_t ino; -+ unsigned long *p, pindex, ul, pend; -+ struct au_sbinfo *sbinfo; -+ struct file *file; -+ int free_bit, err; -+ -+ if (!au_opt_test(au_mntflags(sb), XINO)) -+ return iunique(sb, AUFS_FIRST_INO); -+ -+ sbinfo = au_sbi(sb); -+ mutex_lock(&sbinfo->si_xib_mtx); -+ p = sbinfo->si_xib_buf; -+ free_bit = sbinfo->si_xib_next_bit; -+ if (free_bit < page_bits && !test_bit(free_bit, p)) -+ goto out; /* success */ -+ free_bit = find_first_zero_bit(p, page_bits); -+ if (free_bit < page_bits) -+ goto out; /* success */ -+ -+ pindex = sbinfo->si_xib_last_pindex; -+ for (ul = pindex - 1; ul < ULONG_MAX; ul--) { -+ err = xib_pindex(sb, ul); -+ if (unlikely(err)) -+ goto out_err; -+ free_bit = find_first_zero_bit(p, page_bits); -+ if (free_bit < page_bits) -+ goto out; /* success */ -+ } -+ -+ file = sbinfo->si_xib; -+ pend = vfsub_f_size_read(file) / PAGE_SIZE; -+ for (ul = pindex + 1; ul <= pend; ul++) { -+ err = xib_pindex(sb, ul); -+ if (unlikely(err)) -+ goto out_err; -+ free_bit = find_first_zero_bit(p, page_bits); -+ if (free_bit < page_bits) -+ goto out; /* success */ -+ } -+ BUG(); -+ -+out: -+ set_bit(free_bit, p); -+ sbinfo->si_xib_next_bit = free_bit + 1; -+ pindex = sbinfo->si_xib_last_pindex; -+ mutex_unlock(&sbinfo->si_xib_mtx); -+ ino = xib_calc_ino(pindex, free_bit); -+ AuDbg("i%lu\n", (unsigned long)ino); -+ return ino; -+out_err: -+ mutex_unlock(&sbinfo->si_xib_mtx); -+ AuDbg("i0\n"); -+ return 0; -+} -+ -+/* -+ * read @ino from xinofile for the specified branch{@sb, @bindex} -+ * at the position of @h_ino. -+ * if @ino does not exist and @do_new is true, get new one. -+ */ -+int au_xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, -+ ino_t *ino) -+{ -+ int err; -+ ssize_t sz; -+ loff_t pos; -+ struct file *file; -+ struct au_sbinfo *sbinfo; -+ -+ *ino = 0; -+ if (!au_opt_test(au_mntflags(sb), XINO)) -+ return 0; /* no xino */ -+ -+ err = 0; -+ sbinfo = au_sbi(sb); -+ pos = h_ino; -+ if (unlikely(au_loff_max / sizeof(*ino) - 1 < pos)) { -+ AuIOErr1("too large hi%lu\n", (unsigned long)h_ino); -+ return -EFBIG; -+ } -+ pos *= sizeof(*ino); -+ -+ file = au_sbr(sb, bindex)->br_xino.xi_file; -+ if (vfsub_f_size_read(file) < pos + sizeof(*ino)) -+ return 0; /* no ino */ -+ -+ sz = xino_fread(sbinfo->si_xread, file, ino, sizeof(*ino), &pos); -+ if (sz == sizeof(*ino)) -+ return 0; /* success */ -+ -+ err = sz; -+ if (unlikely(sz >= 0)) { -+ err = -EIO; -+ AuIOErr("xino read error (%zd)\n", sz); -+ } -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* create and set a new xino file */ -+ -+struct file *au_xino_create(struct super_block *sb, char *fname, int silent) -+{ -+ struct file *file; -+ struct dentry *h_parent, *d; -+ struct inode *h_dir, *inode; -+ int err; -+ -+ /* -+ * at mount-time, and the xino file is the default path, -+ * hnotify is disabled so we have no notify events to ignore. -+ * when a user specified the xino, we cannot get au_hdir to be ignored. -+ */ -+ file = vfsub_filp_open(fname, O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE -+ /* | __FMODE_NONOTIFY */, -+ S_IRUGO | S_IWUGO); -+ if (IS_ERR(file)) { -+ if (!silent) -+ pr_err("open %s(%ld)\n", fname, PTR_ERR(file)); -+ return file; -+ } -+ -+ /* keep file count */ -+ err = 0; -+ inode = file_inode(file); -+ h_parent = dget_parent(file->f_dentry); -+ h_dir = h_parent->d_inode; -+ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); -+ /* mnt_want_write() is unnecessary here */ -+ /* no delegation since it is just created */ -+ if (inode->i_nlink) -+ err = vfsub_unlink(h_dir, &file->f_path, /*delegated*/NULL, -+ /*force*/0); -+ mutex_unlock(&h_dir->i_mutex); -+ dput(h_parent); -+ if (unlikely(err)) { -+ if (!silent) -+ pr_err("unlink %s(%d)\n", fname, err); -+ goto out; -+ } -+ -+ err = -EINVAL; -+ d = file->f_dentry; -+ if (unlikely(sb == d->d_sb)) { -+ if (!silent) -+ pr_err("%s must be outside\n", fname); -+ goto out; -+ } -+ if (unlikely(au_test_fs_bad_xino(d->d_sb))) { -+ if (!silent) -+ pr_err("xino doesn't support %s(%s)\n", -+ fname, au_sbtype(d->d_sb)); -+ goto out; -+ } -+ return file; /* success */ -+ -+out: -+ fput(file); -+ file = ERR_PTR(err); -+ return file; -+} -+ -+/* -+ * find another branch who is on the same filesystem of the specified -+ * branch{@btgt}. search until @bend. -+ */ -+static int is_sb_shared(struct super_block *sb, aufs_bindex_t btgt, -+ aufs_bindex_t bend) -+{ -+ aufs_bindex_t bindex; -+ struct super_block *tgt_sb = au_sbr_sb(sb, btgt); -+ -+ for (bindex = 0; bindex < btgt; bindex++) -+ if (unlikely(tgt_sb == au_sbr_sb(sb, bindex))) -+ return bindex; -+ for (bindex++; bindex <= bend; bindex++) -+ if (unlikely(tgt_sb == au_sbr_sb(sb, bindex))) -+ return bindex; -+ return -1; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * initialize the xinofile for the specified branch @br -+ * at the place/path where @base_file indicates. -+ * test whether another branch is on the same filesystem or not, -+ * if @do_test is true. -+ */ -+int au_xino_br(struct super_block *sb, struct au_branch *br, ino_t h_ino, -+ struct file *base_file, int do_test) -+{ -+ int err; -+ ino_t ino; -+ aufs_bindex_t bend, bindex; -+ struct au_branch *shared_br, *b; -+ struct file *file; -+ struct super_block *tgt_sb; -+ -+ shared_br = NULL; -+ bend = au_sbend(sb); -+ if (do_test) { -+ tgt_sb = au_br_sb(br); -+ for (bindex = 0; bindex <= bend; bindex++) { -+ b = au_sbr(sb, bindex); -+ if (tgt_sb == au_br_sb(b)) { -+ shared_br = b; -+ break; -+ } -+ } -+ } -+ -+ if (!shared_br || !shared_br->br_xino.xi_file) { -+ struct au_xino_lock_dir ldir; -+ -+ au_xino_lock_dir(sb, base_file, &ldir); -+ /* mnt_want_write() is unnecessary here */ -+ file = au_xino_create2(base_file, NULL); -+ au_xino_unlock_dir(&ldir); -+ err = PTR_ERR(file); -+ if (IS_ERR(file)) -+ goto out; -+ br->br_xino.xi_file = file; -+ } else { -+ br->br_xino.xi_file = shared_br->br_xino.xi_file; -+ get_file(br->br_xino.xi_file); -+ } -+ -+ ino = AUFS_ROOT_INO; -+ err = au_xino_do_write(au_sbi(sb)->si_xwrite, br->br_xino.xi_file, -+ h_ino, ino); -+ if (unlikely(err)) { -+ fput(br->br_xino.xi_file); -+ br->br_xino.xi_file = NULL; -+ } -+ -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* trucate a xino bitmap file */ -+ -+/* todo: slow */ -+static int do_xib_restore(struct super_block *sb, struct file *file, void *page) -+{ -+ int err, bit; -+ ssize_t sz; -+ unsigned long pindex; -+ loff_t pos, pend; -+ struct au_sbinfo *sbinfo; -+ au_readf_t func; -+ ino_t *ino; -+ unsigned long *p; -+ -+ err = 0; -+ sbinfo = au_sbi(sb); -+ MtxMustLock(&sbinfo->si_xib_mtx); -+ p = sbinfo->si_xib_buf; -+ func = sbinfo->si_xread; -+ pend = vfsub_f_size_read(file); -+ pos = 0; -+ while (pos < pend) { -+ sz = xino_fread(func, file, page, PAGE_SIZE, &pos); -+ err = sz; -+ if (unlikely(sz <= 0)) -+ goto out; -+ -+ err = 0; -+ for (ino = page; sz > 0; ino++, sz -= sizeof(ino)) { -+ if (unlikely(*ino < AUFS_FIRST_INO)) -+ continue; -+ -+ xib_calc_bit(*ino, &pindex, &bit); -+ AuDebugOn(page_bits <= bit); -+ err = xib_pindex(sb, pindex); -+ if (!err) -+ set_bit(bit, p); -+ else -+ goto out; -+ } -+ } -+ -+out: -+ return err; -+} -+ -+static int xib_restore(struct super_block *sb) -+{ -+ int err; -+ aufs_bindex_t bindex, bend; -+ void *page; -+ -+ err = -ENOMEM; -+ page = (void *)__get_free_page(GFP_NOFS); -+ if (unlikely(!page)) -+ goto out; -+ -+ err = 0; -+ bend = au_sbend(sb); -+ for (bindex = 0; !err && bindex <= bend; bindex++) -+ if (!bindex || is_sb_shared(sb, bindex, bindex - 1) < 0) -+ err = do_xib_restore -+ (sb, au_sbr(sb, bindex)->br_xino.xi_file, page); -+ else -+ AuDbg("b%d\n", bindex); -+ free_page((unsigned long)page); -+ -+out: -+ return err; -+} -+ -+int au_xib_trunc(struct super_block *sb) -+{ -+ int err; -+ ssize_t sz; -+ loff_t pos; -+ struct au_xino_lock_dir ldir; -+ struct au_sbinfo *sbinfo; -+ unsigned long *p; -+ struct file *file; -+ -+ SiMustWriteLock(sb); -+ -+ err = 0; -+ sbinfo = au_sbi(sb); -+ if (!au_opt_test(sbinfo->si_mntflags, XINO)) -+ goto out; -+ -+ file = sbinfo->si_xib; -+ if (vfsub_f_size_read(file) <= PAGE_SIZE) -+ goto out; -+ -+ au_xino_lock_dir(sb, file, &ldir); -+ /* mnt_want_write() is unnecessary here */ -+ file = au_xino_create2(sbinfo->si_xib, NULL); -+ au_xino_unlock_dir(&ldir); -+ err = PTR_ERR(file); -+ if (IS_ERR(file)) -+ goto out; -+ fput(sbinfo->si_xib); -+ sbinfo->si_xib = file; -+ -+ p = sbinfo->si_xib_buf; -+ memset(p, 0, PAGE_SIZE); -+ pos = 0; -+ sz = xino_fwrite(sbinfo->si_xwrite, sbinfo->si_xib, p, PAGE_SIZE, &pos); -+ if (unlikely(sz != PAGE_SIZE)) { -+ err = sz; -+ AuIOErr("err %d\n", err); -+ if (sz >= 0) -+ err = -EIO; -+ goto out; -+ } -+ -+ mutex_lock(&sbinfo->si_xib_mtx); -+ /* mnt_want_write() is unnecessary here */ -+ err = xib_restore(sb); -+ mutex_unlock(&sbinfo->si_xib_mtx); -+ -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * xino mount option handlers -+ */ -+static au_readf_t find_readf(struct file *h_file) -+{ -+ const struct file_operations *fop = h_file->f_op; -+ -+ if (fop->read) -+ return fop->read; -+ if (fop->aio_read) -+ return do_sync_read; -+ if (fop->read_iter) -+ return new_sync_read; -+ return ERR_PTR(-ENOSYS); -+} -+ -+static au_writef_t find_writef(struct file *h_file) -+{ -+ const struct file_operations *fop = h_file->f_op; -+ -+ if (fop->write) -+ return fop->write; -+ if (fop->aio_write) -+ return do_sync_write; -+ if (fop->write_iter) -+ return new_sync_write; -+ return ERR_PTR(-ENOSYS); -+} -+ -+/* xino bitmap */ -+static void xino_clear_xib(struct super_block *sb) -+{ -+ struct au_sbinfo *sbinfo; -+ -+ SiMustWriteLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ sbinfo->si_xread = NULL; -+ sbinfo->si_xwrite = NULL; -+ if (sbinfo->si_xib) -+ fput(sbinfo->si_xib); -+ sbinfo->si_xib = NULL; -+ free_page((unsigned long)sbinfo->si_xib_buf); -+ sbinfo->si_xib_buf = NULL; -+} -+ -+static int au_xino_set_xib(struct super_block *sb, struct file *base) -+{ -+ int err; -+ loff_t pos; -+ struct au_sbinfo *sbinfo; -+ struct file *file; -+ -+ SiMustWriteLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ file = au_xino_create2(base, sbinfo->si_xib); -+ err = PTR_ERR(file); -+ if (IS_ERR(file)) -+ goto out; -+ if (sbinfo->si_xib) -+ fput(sbinfo->si_xib); -+ sbinfo->si_xib = file; -+ sbinfo->si_xread = find_readf(file); -+ sbinfo->si_xwrite = find_writef(file); -+ -+ err = -ENOMEM; -+ if (!sbinfo->si_xib_buf) -+ sbinfo->si_xib_buf = (void *)get_zeroed_page(GFP_NOFS); -+ if (unlikely(!sbinfo->si_xib_buf)) -+ goto out_unset; -+ -+ sbinfo->si_xib_last_pindex = 0; -+ sbinfo->si_xib_next_bit = 0; -+ if (vfsub_f_size_read(file) < PAGE_SIZE) { -+ pos = 0; -+ err = xino_fwrite(sbinfo->si_xwrite, file, sbinfo->si_xib_buf, -+ PAGE_SIZE, &pos); -+ if (unlikely(err != PAGE_SIZE)) -+ goto out_free; -+ } -+ err = 0; -+ goto out; /* success */ -+ -+out_free: -+ free_page((unsigned long)sbinfo->si_xib_buf); -+ sbinfo->si_xib_buf = NULL; -+ if (err >= 0) -+ err = -EIO; -+out_unset: -+ fput(sbinfo->si_xib); -+ sbinfo->si_xib = NULL; -+ sbinfo->si_xread = NULL; -+ sbinfo->si_xwrite = NULL; -+out: -+ return err; -+} -+ -+/* xino for each branch */ -+static void xino_clear_br(struct super_block *sb) -+{ -+ aufs_bindex_t bindex, bend; -+ struct au_branch *br; -+ -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ if (!br || !br->br_xino.xi_file) -+ continue; -+ -+ fput(br->br_xino.xi_file); -+ br->br_xino.xi_file = NULL; -+ } -+} -+ -+static int au_xino_set_br(struct super_block *sb, struct file *base) -+{ -+ int err; -+ ino_t ino; -+ aufs_bindex_t bindex, bend, bshared; -+ struct { -+ struct file *old, *new; -+ } *fpair, *p; -+ struct au_branch *br; -+ struct inode *inode; -+ au_writef_t writef; -+ -+ SiMustWriteLock(sb); -+ -+ err = -ENOMEM; -+ bend = au_sbend(sb); -+ fpair = kcalloc(bend + 1, sizeof(*fpair), GFP_NOFS); -+ if (unlikely(!fpair)) -+ goto out; -+ -+ inode = sb->s_root->d_inode; -+ ino = AUFS_ROOT_INO; -+ writef = au_sbi(sb)->si_xwrite; -+ for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) { -+ br = au_sbr(sb, bindex); -+ bshared = is_sb_shared(sb, bindex, bindex - 1); -+ if (bshared >= 0) { -+ /* shared xino */ -+ *p = fpair[bshared]; -+ get_file(p->new); -+ } -+ -+ if (!p->new) { -+ /* new xino */ -+ p->old = br->br_xino.xi_file; -+ p->new = au_xino_create2(base, br->br_xino.xi_file); -+ err = PTR_ERR(p->new); -+ if (IS_ERR(p->new)) { -+ p->new = NULL; -+ goto out_pair; -+ } -+ } -+ -+ err = au_xino_do_write(writef, p->new, -+ au_h_iptr(inode, bindex)->i_ino, ino); -+ if (unlikely(err)) -+ goto out_pair; -+ } -+ -+ for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) { -+ br = au_sbr(sb, bindex); -+ if (br->br_xino.xi_file) -+ fput(br->br_xino.xi_file); -+ get_file(p->new); -+ br->br_xino.xi_file = p->new; -+ } -+ -+out_pair: -+ for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) -+ if (p->new) -+ fput(p->new); -+ else -+ break; -+ kfree(fpair); -+out: -+ return err; -+} -+ -+void au_xino_clr(struct super_block *sb) -+{ -+ struct au_sbinfo *sbinfo; -+ -+ au_xigen_clr(sb); -+ xino_clear_xib(sb); -+ xino_clear_br(sb); -+ sbinfo = au_sbi(sb); -+ /* lvalue, do not call au_mntflags() */ -+ au_opt_clr(sbinfo->si_mntflags, XINO); -+} -+ -+int au_xino_set(struct super_block *sb, struct au_opt_xino *xino, int remount) -+{ -+ int err, skip; -+ struct dentry *parent, *cur_parent; -+ struct qstr *dname, *cur_name; -+ struct file *cur_xino; -+ struct inode *dir; -+ struct au_sbinfo *sbinfo; -+ -+ SiMustWriteLock(sb); -+ -+ err = 0; -+ sbinfo = au_sbi(sb); -+ parent = dget_parent(xino->file->f_dentry); -+ if (remount) { -+ skip = 0; -+ dname = &xino->file->f_dentry->d_name; -+ cur_xino = sbinfo->si_xib; -+ if (cur_xino) { -+ cur_parent = dget_parent(cur_xino->f_dentry); -+ cur_name = &cur_xino->f_dentry->d_name; -+ skip = (cur_parent == parent -+ && au_qstreq(dname, cur_name)); -+ dput(cur_parent); -+ } -+ if (skip) -+ goto out; -+ } -+ -+ au_opt_set(sbinfo->si_mntflags, XINO); -+ dir = parent->d_inode; -+ mutex_lock_nested(&dir->i_mutex, AuLsc_I_PARENT); -+ /* mnt_want_write() is unnecessary here */ -+ err = au_xino_set_xib(sb, xino->file); -+ if (!err) -+ err = au_xigen_set(sb, xino->file); -+ if (!err) -+ err = au_xino_set_br(sb, xino->file); -+ mutex_unlock(&dir->i_mutex); -+ if (!err) -+ goto out; /* success */ -+ -+ /* reset all */ -+ AuIOErr("failed creating xino(%d).\n", err); -+ au_xigen_clr(sb); -+ xino_clear_xib(sb); -+ -+out: -+ dput(parent); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * create a xinofile at the default place/path. -+ */ -+struct file *au_xino_def(struct super_block *sb) -+{ -+ struct file *file; -+ char *page, *p; -+ struct au_branch *br; -+ struct super_block *h_sb; -+ struct path path; -+ aufs_bindex_t bend, bindex, bwr; -+ -+ br = NULL; -+ bend = au_sbend(sb); -+ bwr = -1; -+ for (bindex = 0; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ if (au_br_writable(br->br_perm) -+ && !au_test_fs_bad_xino(au_br_sb(br))) { -+ bwr = bindex; -+ break; -+ } -+ } -+ -+ if (bwr >= 0) { -+ file = ERR_PTR(-ENOMEM); -+ page = (void *)__get_free_page(GFP_NOFS); -+ if (unlikely(!page)) -+ goto out; -+ path.mnt = au_br_mnt(br); -+ path.dentry = au_h_dptr(sb->s_root, bwr); -+ p = d_path(&path, page, PATH_MAX - sizeof(AUFS_XINO_FNAME)); -+ file = (void *)p; -+ if (!IS_ERR(p)) { -+ strcat(p, "/" AUFS_XINO_FNAME); -+ AuDbg("%s\n", p); -+ file = au_xino_create(sb, p, /*silent*/0); -+ if (!IS_ERR(file)) -+ au_xino_brid_set(sb, br->br_id); -+ } -+ free_page((unsigned long)page); -+ } else { -+ file = au_xino_create(sb, AUFS_XINO_DEFPATH, /*silent*/0); -+ if (IS_ERR(file)) -+ goto out; -+ h_sb = file->f_dentry->d_sb; -+ if (unlikely(au_test_fs_bad_xino(h_sb))) { -+ pr_err("xino doesn't support %s(%s)\n", -+ AUFS_XINO_DEFPATH, au_sbtype(h_sb)); -+ fput(file); -+ file = ERR_PTR(-EINVAL); -+ } -+ if (!IS_ERR(file)) -+ au_xino_brid_set(sb, -1); -+ } -+ -+out: -+ return file; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int au_xino_path(struct seq_file *seq, struct file *file) -+{ -+ int err; -+ -+ err = au_seq_path(seq, &file->f_path); -+ if (unlikely(err)) -+ goto out; -+ -+#define Deleted "\\040(deleted)" -+ seq->count -= sizeof(Deleted) - 1; -+ AuDebugOn(memcmp(seq->buf + seq->count, Deleted, -+ sizeof(Deleted) - 1)); -+#undef Deleted -+ -+out: -+ return err; -+} -diff --git a/fs/buffer.c b/fs/buffer.c -index 20805db..363569f 100644 ---- a/fs/buffer.c -+++ b/fs/buffer.c -@@ -2450,7 +2450,7 @@ int block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf, - * Update file times before taking page lock. We may end up failing the - * fault so this update may be superfluous but who really cares... - */ -- file_update_time(vma->vm_file); -+ vma_file_update_time(vma); - - ret = __block_page_mkwrite(vma, vmf, get_block); - sb_end_pagefault(sb); -diff --git a/fs/dcache.c b/fs/dcache.c -index d25f8fd..857990a 100644 ---- a/fs/dcache.c -+++ b/fs/dcache.c -@@ -1022,7 +1022,7 @@ enum d_walk_ret { - * - * The @enter() and @finish() callbacks are called with d_lock held. - */ --static void d_walk(struct dentry *parent, void *data, -+void d_walk(struct dentry *parent, void *data, - enum d_walk_ret (*enter)(void *, struct dentry *), - void (*finish)(void *)) - { -diff --git a/fs/fcntl.c b/fs/fcntl.c -index 99d440a..de1a407 100644 ---- a/fs/fcntl.c -+++ b/fs/fcntl.c -@@ -29,7 +29,7 @@ - - #define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | O_DIRECT | O_NOATIME) - --static int setfl(int fd, struct file * filp, unsigned long arg) -+int setfl(int fd, struct file * filp, unsigned long arg) - { - struct inode * inode = file_inode(filp); - int error = 0; -@@ -59,6 +59,8 @@ static int setfl(int fd, struct file * filp, unsigned long arg) - - if (filp->f_op->check_flags) - error = filp->f_op->check_flags(arg); -+ if (!error && filp->f_op->setfl) -+ error = filp->f_op->setfl(filp, arg); - if (error) - return error; - -diff --git a/fs/inode.c b/fs/inode.c -index 56d1d2b..2998e86 100644 ---- a/fs/inode.c -+++ b/fs/inode.c -@@ -1497,7 +1497,7 @@ static int relatime_need_update(struct vfsmount *mnt, struct inode *inode, - * This does the actual work of updating an inodes time or version. Must have - * had called mnt_want_write() before calling this. - */ --static int update_time(struct inode *inode, struct timespec *time, int flags) -+int update_time(struct inode *inode, struct timespec *time, int flags) - { - if (inode->i_op->update_time) - return inode->i_op->update_time(inode, time, flags); -diff --git a/fs/proc/base.c b/fs/proc/base.c -index 7dc3ea8..b368ad5 100644 ---- a/fs/proc/base.c -+++ b/fs/proc/base.c -@@ -1735,7 +1735,7 @@ static int proc_map_files_get_link(struct dentry *dentry, struct path *path) - down_read(&mm->mmap_sem); - vma = find_exact_vma(mm, vm_start, vm_end); - if (vma && vma->vm_file) { -- *path = vma->vm_file->f_path; -+ *path = vma_pr_or_file(vma)->f_path; - path_get(path); - rc = 0; - } -diff --git a/fs/proc/nommu.c b/fs/proc/nommu.c -index d4a3574..1397181 100644 ---- a/fs/proc/nommu.c -+++ b/fs/proc/nommu.c -@@ -45,7 +45,10 @@ static int nommu_region_show(struct seq_file *m, struct vm_region *region) - file = region->vm_file; - - if (file) { -- struct inode *inode = file_inode(region->vm_file); -+ struct inode *inode; -+ -+ file = vmr_pr_or_file(region); -+ inode = file_inode(file); - dev = inode->i_sb->s_dev; - ino = inode->i_ino; - } -diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c -index 69aa378..426b962 100644 ---- a/fs/proc/task_mmu.c -+++ b/fs/proc/task_mmu.c -@@ -276,7 +276,10 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid) - const char *name = NULL; - - if (file) { -- struct inode *inode = file_inode(vma->vm_file); -+ struct inode *inode; -+ -+ file = vma_pr_or_file(vma); -+ inode = file_inode(file); - dev = inode->i_sb->s_dev; - ino = inode->i_ino; - pgoff = ((loff_t)vma->vm_pgoff) << PAGE_SHIFT; -@@ -1447,7 +1450,7 @@ static int show_numa_map(struct seq_file *m, void *v, int is_pid) - struct proc_maps_private *proc_priv = &numa_priv->proc_maps; - struct vm_area_struct *vma = v; - struct numa_maps *md = &numa_priv->md; -- struct file *file = vma->vm_file; -+ struct file *file = vma_pr_or_file(vma); - struct mm_struct *mm = vma->vm_mm; - struct mm_walk walk = {}; - struct mempolicy *pol; -diff --git a/fs/proc/task_nommu.c b/fs/proc/task_nommu.c -index 599ec2e..1740207 100644 ---- a/fs/proc/task_nommu.c -+++ b/fs/proc/task_nommu.c -@@ -160,7 +160,10 @@ static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma, - file = vma->vm_file; - - if (file) { -- struct inode *inode = file_inode(vma->vm_file); -+ struct inode *inode; -+ -+ file = vma_pr_or_file(vma); -+ inode = file_inode(file); - dev = inode->i_sb->s_dev; - ino = inode->i_ino; - pgoff = (loff_t)vma->vm_pgoff << PAGE_SHIFT; -diff --git a/fs/splice.c b/fs/splice.c -index 75c6058..619359a 100644 ---- a/fs/splice.c -+++ b/fs/splice.c -@@ -1114,8 +1114,8 @@ EXPORT_SYMBOL(generic_splice_sendpage); - /* - * Attempt to initiate a splice from pipe to file. - */ --static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, -- loff_t *ppos, size_t len, unsigned int flags) -+long do_splice_from(struct pipe_inode_info *pipe, struct file *out, -+ loff_t *ppos, size_t len, unsigned int flags) - { - ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, - loff_t *, size_t, unsigned int); -@@ -1131,9 +1131,9 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, - /* - * Attempt to initiate a splice from a file to a pipe. - */ --static long do_splice_to(struct file *in, loff_t *ppos, -- struct pipe_inode_info *pipe, size_t len, -- unsigned int flags) -+long do_splice_to(struct file *in, loff_t *ppos, -+ struct pipe_inode_info *pipe, size_t len, -+ unsigned int flags) - { - ssize_t (*splice_read)(struct file *, loff_t *, - struct pipe_inode_info *, size_t, unsigned int); -diff --git a/include/asm-generic/msi.h b/include/asm-generic/msi.h -new file mode 100644 -index 0000000..61c58d8 ---- /dev/null -+++ b/include/asm-generic/msi.h -@@ -0,0 +1,32 @@ -+#ifndef __ASM_GENERIC_MSI_H -+#define __ASM_GENERIC_MSI_H -+ -+#include -+ -+#ifndef NUM_MSI_ALLOC_SCRATCHPAD_REGS -+# define NUM_MSI_ALLOC_SCRATCHPAD_REGS 2 -+#endif -+ -+struct msi_desc; -+ -+/** -+ * struct msi_alloc_info - Default structure for MSI interrupt allocation. -+ * @desc: Pointer to msi descriptor -+ * @hwirq: Associated hw interrupt number in the domain -+ * @scratchpad: Storage for implementation specific scratch data -+ * -+ * Architectures can provide their own implementation by not including -+ * asm-generic/msi.h into their arch specific header file. -+ */ -+typedef struct msi_alloc_info { -+ struct msi_desc *desc; -+ irq_hw_number_t hwirq; -+ union { -+ unsigned long ul; -+ void *ptr; -+ } scratchpad[NUM_MSI_ALLOC_SCRATCHPAD_REGS]; -+} msi_alloc_info_t; -+ -+#define GENERIC_MSI_DOMAIN_OPS 1 -+ -+#endif -diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h -index aa70cbd..bee5d68 100644 ---- a/include/asm-generic/vmlinux.lds.h -+++ b/include/asm-generic/vmlinux.lds.h -@@ -164,6 +164,7 @@ - #define CLKSRC_OF_TABLES() OF_TABLE(CONFIG_CLKSRC_OF, clksrc) - #define IRQCHIP_OF_MATCH_TABLE() OF_TABLE(CONFIG_IRQCHIP, irqchip) - #define CLK_OF_TABLES() OF_TABLE(CONFIG_COMMON_CLK, clk) -+#define IOMMU_OF_TABLES() OF_TABLE(CONFIG_OF_IOMMU, iommu) - #define RESERVEDMEM_OF_TABLES() OF_TABLE(CONFIG_OF_RESERVED_MEM, reservedmem) - #define CPU_METHOD_OF_TABLES() OF_TABLE(CONFIG_SMP, cpu_method) - #define EARLYCON_OF_TABLES() OF_TABLE(CONFIG_SERIAL_EARLYCON, earlycon) -@@ -497,6 +498,7 @@ - CLK_OF_TABLES() \ - RESERVEDMEM_OF_TABLES() \ - CLKSRC_OF_TABLES() \ -+ IOMMU_OF_TABLES() \ - CPU_METHOD_OF_TABLES() \ - KERNEL_DTB() \ - IRQCHIP_OF_MATCH_TABLE() \ -diff --git a/include/linux/acpi.h b/include/linux/acpi.h -index 1c7eaa7..d017dbf 100644 ---- a/include/linux/acpi.h -+++ b/include/linux/acpi.h -@@ -27,6 +27,7 @@ - - #include - #include /* for struct resource */ -+#include - #include - #include - -@@ -290,11 +291,6 @@ unsigned long acpi_dev_irq_flags(u8 triggering, u8 polarity, u8 shareable); - bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index, - struct resource *res); - --struct resource_list_entry { -- struct list_head node; -- struct resource res; --}; -- - void acpi_dev_free_resource_list(struct list_head *list); - int acpi_dev_get_resources(struct acpi_device *adev, struct list_head *list, - int (*preproc)(struct acpi_resource *, void *), -diff --git a/include/linux/device.h b/include/linux/device.h -index ce1f216..941d97b 100644 ---- a/include/linux/device.h -+++ b/include/linux/device.h -@@ -690,6 +690,8 @@ struct acpi_dev_node { - * along with subsystem-level and driver-level callbacks. - * @pins: For device pin management. - * See Documentation/pinctrl.txt for details. -+ * @msi_list: Hosts MSI descriptors -+ * @msi_domain: The generic MSI domain this device is using. - * @numa_node: NUMA node this device is close to. - * @dma_mask: Dma mask (if dma'ble device). - * @coherent_dma_mask: Like dma_mask, but for alloc_coherent mapping as not all -@@ -750,9 +752,15 @@ struct device { - struct dev_pm_info power; - struct dev_pm_domain *pm_domain; - -+#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN -+ struct irq_domain *msi_domain; -+#endif - #ifdef CONFIG_PINCTRL - struct dev_pin_info *pins; - #endif -+#ifdef CONFIG_GENERIC_MSI_IRQ -+ struct list_head msi_list; -+#endif - - #ifdef CONFIG_NUMA - int numa_node; /* NUMA node this device is close to */ -@@ -837,6 +845,22 @@ static inline void set_dev_node(struct device *dev, int node) - } - #endif - -+static inline struct irq_domain *dev_get_msi_domain(const struct device *dev) -+{ -+#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN -+ return dev->msi_domain; -+#else -+ return NULL; -+#endif -+} -+ -+static inline void dev_set_msi_domain(struct device *dev, struct irq_domain *d) -+{ -+#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN -+ dev->msi_domain = d; -+#endif -+} -+ - static inline void *dev_get_drvdata(const struct device *dev) - { - return dev->driver_data; -diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h -index d5d3881..c3007cb 100644 ---- a/include/linux/dma-mapping.h -+++ b/include/linux/dma-mapping.h -@@ -129,11 +129,14 @@ static inline int dma_coerce_mask_and_coherent(struct device *dev, u64 mask) - - extern u64 dma_get_required_mask(struct device *dev); - --#ifndef set_arch_dma_coherent_ops --static inline int set_arch_dma_coherent_ops(struct device *dev) --{ -- return 0; --} -+#ifndef arch_setup_dma_ops -+static inline void arch_setup_dma_ops(struct device *dev, u64 dma_base, -+ u64 size, struct iommu_ops *iommu, -+ bool coherent) { } -+#endif -+ -+#ifndef arch_teardown_dma_ops -+static inline void arch_teardown_dma_ops(struct device *dev) { } - #endif - - static inline unsigned int dma_get_max_seg_size(struct device *dev) -diff --git a/include/linux/file.h b/include/linux/file.h -index 4d69123..62cffc0 100644 ---- a/include/linux/file.h -+++ b/include/linux/file.h -@@ -19,6 +19,7 @@ struct dentry; - struct path; - extern struct file *alloc_file(struct path *, fmode_t mode, - const struct file_operations *fop); -+extern struct file *get_empty_filp(void); - - static inline void fput_light(struct file *file, int fput_needed) - { -diff --git a/include/linux/fs.h b/include/linux/fs.h -index 6fd017e..c44d25d 100644 ---- a/include/linux/fs.h -+++ b/include/linux/fs.h -@@ -1149,6 +1149,7 @@ extern void fasync_free(struct fasync_struct *); - /* can be called from interrupts */ - extern void kill_fasync(struct fasync_struct **, int, int); - -+extern int setfl(int fd, struct file * filp, unsigned long arg); - extern void __f_setown(struct file *filp, struct pid *, enum pid_type, int force); - extern void f_setown(struct file *filp, unsigned long arg, int force); - extern void f_delown(struct file *filp); -@@ -1507,6 +1508,7 @@ struct file_operations { - ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); - unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); - int (*check_flags)(int); -+ int (*setfl)(struct file *, unsigned long); - int (*flock) (struct file *, int, struct file_lock *); - ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); - ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); -@@ -2662,6 +2664,7 @@ 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 update_time(struct inode *, struct timespec *, int); - extern int file_update_time(struct file *file); - - extern int generic_show_options(struct seq_file *m, struct dentry *root); -diff --git a/include/linux/fsl/guts.h b/include/linux/fsl/guts.h -new file mode 100644 -index 0000000..f13b12e ---- /dev/null -+++ b/include/linux/fsl/guts.h -@@ -0,0 +1,195 @@ -+/** -+ * Freecale 85xx and 86xx Global Utilties register set -+ * -+ * Authors: Jeff Brown -+ * Timur Tabi -+ * -+ * Copyright 2004,2007,2012 Freescale Semiconductor, 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. -+ */ -+ -+#ifndef __FSL_GUTS_H__ -+#define __FSL_GUTS_H__ -+ -+#include -+ -+/** -+ * Global Utility Registers. -+ * -+ * Not all registers defined in this structure are available on all chips, so -+ * you are expected to know whether a given register actually exists on your -+ * chip before you access it. -+ * -+ * Also, some registers are similar on different chips but have slightly -+ * different names. In these cases, one name is chosen to avoid extraneous -+ * #ifdefs. -+ */ -+struct ccsr_guts { -+ u32 porpllsr; /* 0x.0000 - POR PLL Ratio Status Register */ -+ u32 porbmsr; /* 0x.0004 - POR Boot Mode Status Register */ -+ u32 porimpscr; /* 0x.0008 - POR I/O Impedance Status and Control Register */ -+ u32 pordevsr; /* 0x.000c - POR I/O Device Status Register */ -+ u32 pordbgmsr; /* 0x.0010 - POR Debug Mode Status Register */ -+ u32 pordevsr2; /* 0x.0014 - POR device status register 2 */ -+ u8 res018[0x20 - 0x18]; -+ u32 porcir; /* 0x.0020 - POR Configuration Information Register */ -+ u8 res024[0x30 - 0x24]; -+ u32 gpiocr; /* 0x.0030 - GPIO Control Register */ -+ u8 res034[0x40 - 0x34]; -+ u32 gpoutdr; /* 0x.0040 - General-Purpose Output Data Register */ -+ u8 res044[0x50 - 0x44]; -+ u32 gpindr; /* 0x.0050 - General-Purpose Input Data Register */ -+ u8 res054[0x60 - 0x54]; -+ u32 pmuxcr; /* 0x.0060 - Alternate Function Signal Multiplex Control */ -+ u32 pmuxcr2; /* 0x.0064 - Alternate function signal multiplex control 2 */ -+ u32 dmuxcr; /* 0x.0068 - DMA Mux Control Register */ -+ u8 res06c[0x70 - 0x6c]; -+ u32 devdisr; /* 0x.0070 - Device Disable Control */ -+#define CCSR_GUTS_DEVDISR_TB1 0x00001000 -+#define CCSR_GUTS_DEVDISR_TB0 0x00004000 -+ u32 devdisr2; /* 0x.0074 - Device Disable Control 2 */ -+ u8 res078[0x7c - 0x78]; -+ u32 pmjcr; /* 0x.007c - 4 Power Management Jog Control Register */ -+ u32 powmgtcsr; /* 0x.0080 - Power Management Status and Control Register */ -+ u32 pmrccr; /* 0x.0084 - Power Management Reset Counter Configuration Register */ -+ u32 pmpdccr; /* 0x.0088 - Power Management Power Down Counter Configuration Register */ -+ u32 pmcdr; /* 0x.008c - 4Power management clock disable register */ -+ u32 mcpsumr; /* 0x.0090 - Machine Check Summary Register */ -+ u32 rstrscr; /* 0x.0094 - Reset Request Status and Control Register */ -+ u32 ectrstcr; /* 0x.0098 - Exception reset control register */ -+ u32 autorstsr; /* 0x.009c - Automatic reset status register */ -+ u32 pvr; /* 0x.00a0 - Processor Version Register */ -+ u32 svr; /* 0x.00a4 - System Version Register */ -+ u8 res0a8[0xb0 - 0xa8]; -+ u32 rstcr; /* 0x.00b0 - Reset Control Register */ -+ u8 res0b4[0xc0 - 0xb4]; -+ u32 iovselsr; /* 0x.00c0 - I/O voltage select status register -+ Called 'elbcvselcr' on 86xx SOCs */ -+ u8 res0c4[0x100 - 0xc4]; -+ u32 rcwsr[16]; /* 0x.0100 - Reset Control Word Status registers -+ There are 16 registers */ -+ u8 res140[0x224 - 0x140]; -+ u32 iodelay1; /* 0x.0224 - IO delay control register 1 */ -+ u32 iodelay2; /* 0x.0228 - IO delay control register 2 */ -+ u8 res22c[0x604 - 0x22c]; -+ u32 pamubypenr; /* 0x.604 - PAMU bypass enable register */ -+ u8 res608[0x800 - 0x608]; -+ u32 clkdvdr; /* 0x.0800 - Clock Divide Register */ -+ u8 res804[0x900 - 0x804]; -+ u32 ircr; /* 0x.0900 - Infrared Control Register */ -+ u8 res904[0x908 - 0x904]; -+ u32 dmacr; /* 0x.0908 - DMA Control Register */ -+ u8 res90c[0x914 - 0x90c]; -+ u32 elbccr; /* 0x.0914 - eLBC Control Register */ -+ u8 res918[0xb20 - 0x918]; -+ u32 ddr1clkdr; /* 0x.0b20 - DDR1 Clock Disable Register */ -+ u32 ddr2clkdr; /* 0x.0b24 - DDR2 Clock Disable Register */ -+ u32 ddrclkdr; /* 0x.0b28 - DDR Clock Disable Register */ -+ u8 resb2c[0xe00 - 0xb2c]; -+ u32 clkocr; /* 0x.0e00 - Clock Out Select Register */ -+ u8 rese04[0xe10 - 0xe04]; -+ u32 ddrdllcr; /* 0x.0e10 - DDR DLL Control Register */ -+ u8 rese14[0xe20 - 0xe14]; -+ u32 lbcdllcr; /* 0x.0e20 - LBC DLL Control Register */ -+ u32 cpfor; /* 0x.0e24 - L2 charge pump fuse override register */ -+ u8 rese28[0xf04 - 0xe28]; -+ u32 srds1cr0; /* 0x.0f04 - SerDes1 Control Register 0 */ -+ u32 srds1cr1; /* 0x.0f08 - SerDes1 Control Register 0 */ -+ u8 resf0c[0xf2c - 0xf0c]; -+ u32 itcr; /* 0x.0f2c - Internal transaction control register */ -+ u8 resf30[0xf40 - 0xf30]; -+ u32 srds2cr0; /* 0x.0f40 - SerDes2 Control Register 0 */ -+ u32 srds2cr1; /* 0x.0f44 - SerDes2 Control Register 0 */ -+} __attribute__ ((packed)); -+ -+#ifdef CONFIG_FSL_GUTS -+extern u32 guts_get_svr(void); -+#endif -+ -+/* Alternate function signal multiplex control */ -+#define MPC85xx_PMUXCR_QE(x) (0x8000 >> (x)) -+ -+#ifdef CONFIG_PPC_86xx -+ -+#define CCSR_GUTS_DMACR_DEV_SSI 0 /* DMA controller/channel set to SSI */ -+#define CCSR_GUTS_DMACR_DEV_IR 1 /* DMA controller/channel set to IR */ -+ -+/* -+ * Set the DMACR register in the GUTS -+ * -+ * The DMACR register determines the source of initiated transfers for each -+ * channel on each DMA controller. Rather than have a bunch of repetitive -+ * macros for the bit patterns, we just have a function that calculates -+ * them. -+ * -+ * guts: Pointer to GUTS structure -+ * co: The DMA controller (0 or 1) -+ * ch: The channel on the DMA controller (0, 1, 2, or 3) -+ * device: The device to set as the source (CCSR_GUTS_DMACR_DEV_xx) -+ */ -+static inline void guts_set_dmacr(struct ccsr_guts __iomem *guts, -+ unsigned int co, unsigned int ch, unsigned int device) -+{ -+ unsigned int shift = 16 + (8 * (1 - co) + 2 * (3 - ch)); -+ -+ clrsetbits_be32(&guts->dmacr, 3 << shift, device << shift); -+} -+ -+#define CCSR_GUTS_PMUXCR_LDPSEL 0x00010000 -+#define CCSR_GUTS_PMUXCR_SSI1_MASK 0x0000C000 /* Bitmask for SSI1 */ -+#define CCSR_GUTS_PMUXCR_SSI1_LA 0x00000000 /* Latched address */ -+#define CCSR_GUTS_PMUXCR_SSI1_HI 0x00004000 /* High impedance */ -+#define CCSR_GUTS_PMUXCR_SSI1_SSI 0x00008000 /* Used for SSI1 */ -+#define CCSR_GUTS_PMUXCR_SSI2_MASK 0x00003000 /* Bitmask for SSI2 */ -+#define CCSR_GUTS_PMUXCR_SSI2_LA 0x00000000 /* Latched address */ -+#define CCSR_GUTS_PMUXCR_SSI2_HI 0x00001000 /* High impedance */ -+#define CCSR_GUTS_PMUXCR_SSI2_SSI 0x00002000 /* Used for SSI2 */ -+#define CCSR_GUTS_PMUXCR_LA_22_25_LA 0x00000000 /* Latched Address */ -+#define CCSR_GUTS_PMUXCR_LA_22_25_HI 0x00000400 /* High impedance */ -+#define CCSR_GUTS_PMUXCR_DBGDRV 0x00000200 /* Signals not driven */ -+#define CCSR_GUTS_PMUXCR_DMA2_0 0x00000008 -+#define CCSR_GUTS_PMUXCR_DMA2_3 0x00000004 -+#define CCSR_GUTS_PMUXCR_DMA1_0 0x00000002 -+#define CCSR_GUTS_PMUXCR_DMA1_3 0x00000001 -+ -+/* -+ * Set the DMA external control bits in the GUTS -+ * -+ * The DMA external control bits in the PMUXCR are only meaningful for -+ * channels 0 and 3. Any other channels are ignored. -+ * -+ * guts: Pointer to GUTS structure -+ * co: The DMA controller (0 or 1) -+ * ch: The channel on the DMA controller (0, 1, 2, or 3) -+ * value: the new value for the bit (0 or 1) -+ */ -+static inline void guts_set_pmuxcr_dma(struct ccsr_guts __iomem *guts, -+ unsigned int co, unsigned int ch, unsigned int value) -+{ -+ if ((ch == 0) || (ch == 3)) { -+ unsigned int shift = 2 * (co + 1) - (ch & 1) - 1; -+ -+ clrsetbits_be32(&guts->pmuxcr, 1 << shift, value << shift); -+ } -+} -+ -+#define CCSR_GUTS_CLKDVDR_PXCKEN 0x80000000 -+#define CCSR_GUTS_CLKDVDR_SSICKEN 0x20000000 -+#define CCSR_GUTS_CLKDVDR_PXCKINV 0x10000000 -+#define CCSR_GUTS_CLKDVDR_PXCKDLY_SHIFT 25 -+#define CCSR_GUTS_CLKDVDR_PXCKDLY_MASK 0x06000000 -+#define CCSR_GUTS_CLKDVDR_PXCKDLY(x) \ -+ (((x) & 3) << CCSR_GUTS_CLKDVDR_PXCKDLY_SHIFT) -+#define CCSR_GUTS_CLKDVDR_PXCLK_SHIFT 16 -+#define CCSR_GUTS_CLKDVDR_PXCLK_MASK 0x001F0000 -+#define CCSR_GUTS_CLKDVDR_PXCLK(x) (((x) & 31) << CCSR_GUTS_CLKDVDR_PXCLK_SHIFT) -+#define CCSR_GUTS_CLKDVDR_SSICLK_MASK 0x000000FF -+#define CCSR_GUTS_CLKDVDR_SSICLK(x) ((x) & CCSR_GUTS_CLKDVDR_SSICLK_MASK) -+ -+#endif -+ -+#endif -diff --git a/include/linux/fsl/svr.h b/include/linux/fsl/svr.h -new file mode 100644 -index 0000000..8d13836 ---- /dev/null -+++ b/include/linux/fsl/svr.h -@@ -0,0 +1,95 @@ -+/* -+ * MPC85xx cpu type detection -+ * -+ * Copyright 2011-2012 Freescale Semiconductor, Inc. -+ * -+ * This 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. -+ */ -+ -+#ifndef FSL_SVR_H -+#define FSL_SVR_H -+ -+#define SVR_REV(svr) ((svr) & 0xFF) /* SOC design resision */ -+#define SVR_MAJ(svr) (((svr) >> 4) & 0xF) /* Major revision field*/ -+#define SVR_MIN(svr) (((svr) >> 0) & 0xF) /* Minor revision field*/ -+ -+/* Some parts define SVR[0:23] as the SOC version */ -+#define SVR_SOC_VER(svr) (((svr) >> 8) & 0xFFF7FF) /* SOC Version fields */ -+ -+#define SVR_8533 0x803400 -+#define SVR_8535 0x803701 -+#define SVR_8536 0x803700 -+#define SVR_8540 0x803000 -+#define SVR_8541 0x807200 -+#define SVR_8543 0x803200 -+#define SVR_8544 0x803401 -+#define SVR_8545 0x803102 -+#define SVR_8547 0x803101 -+#define SVR_8548 0x803100 -+#define SVR_8555 0x807100 -+#define SVR_8560 0x807000 -+#define SVR_8567 0x807501 -+#define SVR_8568 0x807500 -+#define SVR_8569 0x808000 -+#define SVR_8572 0x80E000 -+#define SVR_P1010 0x80F100 -+#define SVR_P1011 0x80E500 -+#define SVR_P1012 0x80E501 -+#define SVR_P1013 0x80E700 -+#define SVR_P1014 0x80F101 -+#define SVR_P1017 0x80F700 -+#define SVR_P1020 0x80E400 -+#define SVR_P1021 0x80E401 -+#define SVR_P1022 0x80E600 -+#define SVR_P1023 0x80F600 -+#define SVR_P1024 0x80E402 -+#define SVR_P1025 0x80E403 -+#define SVR_P2010 0x80E300 -+#define SVR_P2020 0x80E200 -+#define SVR_P2040 0x821000 -+#define SVR_P2041 0x821001 -+#define SVR_P3041 0x821103 -+#define SVR_P4040 0x820100 -+#define SVR_P4080 0x820000 -+#define SVR_P5010 0x822100 -+#define SVR_P5020 0x822000 -+#define SVR_P5021 0X820500 -+#define SVR_P5040 0x820400 -+#define SVR_T4240 0x824000 -+#define SVR_T4120 0x824001 -+#define SVR_T4160 0x824100 -+#define SVR_T4080 0x824102 -+#define SVR_C291 0x850000 -+#define SVR_C292 0x850020 -+#define SVR_C293 0x850030 -+#define SVR_B4860 0X868000 -+#define SVR_G4860 0x868001 -+#define SVR_G4060 0x868003 -+#define SVR_B4440 0x868100 -+#define SVR_G4440 0x868101 -+#define SVR_B4420 0x868102 -+#define SVR_B4220 0x868103 -+#define SVR_T1040 0x852000 -+#define SVR_T1041 0x852001 -+#define SVR_T1042 0x852002 -+#define SVR_T1020 0x852100 -+#define SVR_T1021 0x852101 -+#define SVR_T1022 0x852102 -+#define SVR_T2080 0x853000 -+#define SVR_T2081 0x853100 -+ -+#define SVR_8610 0x80A000 -+#define SVR_8641 0x809000 -+#define SVR_8641D 0x809001 -+ -+#define SVR_9130 0x860001 -+#define SVR_9131 0x860000 -+#define SVR_9132 0x861000 -+#define SVR_9232 0x861400 -+ -+#define SVR_Unknown 0xFFFFFF -+ -+#endif -diff --git a/include/linux/fsl_ifc.h b/include/linux/fsl_ifc.h -index 84d60cb..3f9778c 100644 ---- a/include/linux/fsl_ifc.h -+++ b/include/linux/fsl_ifc.h -@@ -29,7 +29,20 @@ - #include - #include - --#define FSL_IFC_BANK_COUNT 4 -+/* -+ * The actual number of banks implemented depends on the IFC version -+ * - IFC version 1.0 implements 4 banks. -+ * - IFC version 1.1 onward implements 8 banks. -+ */ -+#define FSL_IFC_BANK_COUNT 8 -+ -+#define FSL_IFC_VERSION_MASK 0x0F0F0000 -+#define FSL_IFC_VERSION_1_0_0 0x01000000 -+#define FSL_IFC_VERSION_1_1_0 0x01010000 -+#define FSL_IFC_VERSION_2_0_0 0x02000000 -+ -+#define PGOFFSET_64K (64*1024) -+#define PGOFFSET_4K (4*1024) - - /* - * CSPR - Chip Select Property Register -@@ -714,20 +727,26 @@ struct fsl_ifc_nand { - __be32 nand_evter_en; - u32 res17[0x2]; - __be32 nand_evter_intr_en; -- u32 res18[0x2]; -+ __be32 nand_vol_addr_stat; -+ u32 res18; - __be32 nand_erattr0; - __be32 nand_erattr1; - u32 res19[0x10]; - __be32 nand_fsr; -- u32 res20; -- __be32 nand_eccstat[4]; -- u32 res21[0x20]; -+ u32 res20[0x3]; -+ __be32 nand_eccstat[6]; -+ u32 res21[0x1c]; - __be32 nanndcr; - u32 res22[0x2]; - __be32 nand_autoboot_trgr; - u32 res23; - __be32 nand_mdr; -- u32 res24[0x5C]; -+ u32 res24[0x1C]; -+ __be32 nand_dll_lowcfg0; -+ __be32 nand_dll_lowcfg1; -+ u32 res25; -+ __be32 nand_dll_lowstat; -+ u32 res26[0x3c]; - }; - - /* -@@ -762,13 +781,12 @@ struct fsl_ifc_gpcm { - __be32 gpcm_erattr1; - __be32 gpcm_erattr2; - __be32 gpcm_stat; -- u32 res4[0x1F3]; - }; - - /* - * IFC Controller Registers - */ --struct fsl_ifc_regs { -+struct fsl_ifc_global { - __be32 ifc_rev; - u32 res1[0x2]; - struct { -@@ -776,39 +794,44 @@ struct fsl_ifc_regs { - __be32 cspr; - u32 res2; - } cspr_cs[FSL_IFC_BANK_COUNT]; -- u32 res3[0x19]; -+ u32 res3[0xd]; - struct { - __be32 amask; - u32 res4[0x2]; - } amask_cs[FSL_IFC_BANK_COUNT]; -- u32 res5[0x18]; -+ u32 res5[0xc]; - struct { - __be32 csor; - __be32 csor_ext; - u32 res6; - } csor_cs[FSL_IFC_BANK_COUNT]; -- u32 res7[0x18]; -+ u32 res7[0xc]; - struct { - __be32 ftim[4]; - u32 res8[0x8]; - } ftim_cs[FSL_IFC_BANK_COUNT]; -- u32 res9[0x60]; -+ u32 res9[0x30]; - __be32 rb_stat; -- u32 res10[0x2]; -+ __be32 rb_map; -+ __be32 wb_map; - __be32 ifc_gcr; -- u32 res11[0x2]; -+ u32 res10[0x2]; - __be32 cm_evter_stat; -- u32 res12[0x2]; -+ u32 res11[0x2]; - __be32 cm_evter_en; -- u32 res13[0x2]; -+ u32 res12[0x2]; - __be32 cm_evter_intr_en; -- u32 res14[0x2]; -+ u32 res13[0x2]; - __be32 cm_erattr0; - __be32 cm_erattr1; -- u32 res15[0x2]; -+ u32 res14[0x2]; - __be32 ifc_ccr; - __be32 ifc_csr; -- u32 res16[0x2EB]; -+ __be32 ddr_ccr_low; -+}; -+ -+ -+struct fsl_ifc_runtime { - struct fsl_ifc_nand ifc_nand; - struct fsl_ifc_nor ifc_nor; - struct fsl_ifc_gpcm ifc_gpcm; -@@ -822,17 +845,70 @@ extern int fsl_ifc_find(phys_addr_t addr_base); - struct fsl_ifc_ctrl { - /* device info */ - struct device *dev; -- struct fsl_ifc_regs __iomem *regs; -+ struct fsl_ifc_global __iomem *gregs; -+ struct fsl_ifc_runtime __iomem *rregs; - int irq; - int nand_irq; - spinlock_t lock; - void *nand; -+ int version; -+ int banks; - - u32 nand_stat; - wait_queue_head_t nand_wait; -+ bool little_endian; - }; - - extern struct fsl_ifc_ctrl *fsl_ifc_ctrl_dev; - -+static inline u32 ifc_in32(void __iomem *addr) -+{ -+ u32 val; -+ -+ if (fsl_ifc_ctrl_dev->little_endian) -+ val = ioread32(addr); -+ else -+ val = ioread32be(addr); -+ -+ return val; -+} -+ -+static inline u16 ifc_in16(void __iomem *addr) -+{ -+ u16 val; -+ -+ if (fsl_ifc_ctrl_dev->little_endian) -+ val = ioread16(addr); -+ else -+ val = ioread16be(addr); -+ -+ return val; -+} -+ -+static inline u8 ifc_in8(void __iomem *addr) -+{ -+ return ioread8(addr); -+} -+ -+static inline void ifc_out32(u32 val, void __iomem *addr) -+{ -+ if (fsl_ifc_ctrl_dev->little_endian) -+ iowrite32(val, addr); -+ else -+ iowrite32be(val, addr); -+} -+ -+static inline void ifc_out16(u16 val, void __iomem *addr) -+{ -+ if (fsl_ifc_ctrl_dev->little_endian) -+ iowrite16(val, addr); -+ else -+ iowrite16be(val, addr); -+} -+ -+static inline void ifc_out8(u8 val, void __iomem *addr) -+{ -+ iowrite8(val, addr); -+} - - #endif /* __ASM_FSL_IFC_H */ -diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h -index 69517a2..cbbe6a2 100644 ---- a/include/linux/interrupt.h -+++ b/include/linux/interrupt.h -@@ -356,6 +356,20 @@ static inline int disable_irq_wake(unsigned int irq) - return irq_set_irq_wake(irq, 0); - } - -+/* -+ * irq_get_irqchip_state/irq_set_irqchip_state specific flags -+ */ -+enum irqchip_irq_state { -+ IRQCHIP_STATE_PENDING, /* Is interrupt pending? */ -+ IRQCHIP_STATE_ACTIVE, /* Is interrupt in progress? */ -+ IRQCHIP_STATE_MASKED, /* Is interrupt masked? */ -+ IRQCHIP_STATE_LINE_LEVEL, /* Is IRQ line high? */ -+}; -+ -+extern int irq_get_irqchip_state(unsigned int irq, enum irqchip_irq_state which, -+ bool *state); -+extern int irq_set_irqchip_state(unsigned int irq, enum irqchip_irq_state which, -+ bool state); - - #ifdef CONFIG_IRQ_FORCED_THREADING - extern bool force_irqthreads; -diff --git a/include/linux/iommu.h b/include/linux/iommu.h -index e6a7c9f..7421bdf 100644 ---- a/include/linux/iommu.h -+++ b/include/linux/iommu.h -@@ -21,13 +21,16 @@ - - #include - #include -+#include - #include -+#include - #include - - #define IOMMU_READ (1 << 0) - #define IOMMU_WRITE (1 << 1) - #define IOMMU_CACHE (1 << 2) /* DMA cache coherency */ --#define IOMMU_EXEC (1 << 3) -+#define IOMMU_NOEXEC (1 << 3) -+#define IOMMU_MMIO (1 << 4) /* Device memory access */ - - struct iommu_ops; - struct iommu_group; -@@ -49,9 +52,33 @@ struct iommu_domain_geometry { - bool force_aperture; /* DMA only allowed in mappable range? */ - }; - -+/* Domain feature flags */ -+#define __IOMMU_DOMAIN_PAGING (1U << 0) /* Support for iommu_map/unmap */ -+#define __IOMMU_DOMAIN_DMA_API (1U << 1) /* Domain for use in DMA-API -+ implementation */ -+#define __IOMMU_DOMAIN_PT (1U << 2) /* Domain is identity mapped */ -+ -+/* -+ * This are the possible domain-types -+ * -+ * IOMMU_DOMAIN_BLOCKED - All DMA is blocked, can be used to isolate -+ * devices -+ * IOMMU_DOMAIN_IDENTITY - DMA addresses are system physical addresses -+ * IOMMU_DOMAIN_UNMANAGED - DMA mappings managed by IOMMU-API user, used -+ * for VMs -+ * IOMMU_DOMAIN_DMA - Internally used for DMA-API implementations. -+ * This flag allows IOMMU drivers to implement -+ * certain optimizations for these domains -+ */ -+#define IOMMU_DOMAIN_BLOCKED (0U) -+#define IOMMU_DOMAIN_IDENTITY (__IOMMU_DOMAIN_PT) -+#define IOMMU_DOMAIN_UNMANAGED (__IOMMU_DOMAIN_PAGING) -+#define IOMMU_DOMAIN_DMA (__IOMMU_DOMAIN_PAGING | \ -+ __IOMMU_DOMAIN_DMA_API) -+ - struct iommu_domain { -+ unsigned type; - const struct iommu_ops *ops; -- void *priv; - iommu_fault_handler_t handler; - void *handler_token; - struct iommu_domain_geometry geometry; -@@ -61,6 +88,7 @@ enum iommu_cap { - IOMMU_CAP_CACHE_COHERENCY, /* IOMMU can enforce cache coherent DMA - transactions */ - IOMMU_CAP_INTR_REMAP, /* IOMMU supports interrupt isolation */ -+ IOMMU_CAP_NOEXEC, /* IOMMU_NOEXEC flag */ - }; - - /* -@@ -97,23 +125,32 @@ enum iommu_attr { - * @detach_dev: detach device from an iommu domain - * @map: map a physically contiguous memory region to an iommu domain - * @unmap: unmap a physically contiguous memory region from an iommu domain -+ * @map_sg: map a scatter-gather list of physically contiguous memory chunks -+ * to an iommu domain - * @iova_to_phys: translate iova to physical address - * @add_device: add device to iommu grouping - * @remove_device: remove device from iommu grouping - * @domain_get_attr: Query domain attributes - * @domain_set_attr: Change domain attributes -+ * @of_xlate: add OF master IDs to iommu grouping - * @pgsize_bitmap: bitmap of supported page sizes -+ * @priv: per-instance data private to the iommu driver - */ - struct iommu_ops { - bool (*capable)(enum iommu_cap); -- int (*domain_init)(struct iommu_domain *domain); -- void (*domain_destroy)(struct iommu_domain *domain); -+ -+ /* Domain allocation and freeing by the iommu driver */ -+ struct iommu_domain *(*domain_alloc)(unsigned iommu_domain_type); -+ void (*domain_free)(struct iommu_domain *); -+ - int (*attach_dev)(struct iommu_domain *domain, struct device *dev); - void (*detach_dev)(struct iommu_domain *domain, struct device *dev); - int (*map)(struct iommu_domain *domain, unsigned long iova, - phys_addr_t paddr, size_t size, int prot); - size_t (*unmap)(struct iommu_domain *domain, unsigned long iova, - size_t size); -+ size_t (*map_sg)(struct iommu_domain *domain, unsigned long iova, -+ struct scatterlist *sg, unsigned int nents, int prot); - phys_addr_t (*iova_to_phys)(struct iommu_domain *domain, dma_addr_t iova); - int (*add_device)(struct device *dev); - void (*remove_device)(struct device *dev); -@@ -131,8 +168,14 @@ struct iommu_ops { - int (*domain_set_windows)(struct iommu_domain *domain, u32 w_count); - /* Get the numer of window per domain */ - u32 (*domain_get_windows)(struct iommu_domain *domain); -+ struct iommu_domain *(*get_dev_iommu_domain)(struct device *dev); -+ -+#ifdef CONFIG_OF_IOMMU -+ int (*of_xlate)(struct device *dev, struct of_phandle_args *args); -+#endif - - unsigned long pgsize_bitmap; -+ void *priv; - }; - - #define IOMMU_GROUP_NOTIFY_ADD_DEVICE 1 /* Device added */ -@@ -156,6 +199,9 @@ extern int iommu_map(struct iommu_domain *domain, unsigned long iova, - phys_addr_t paddr, size_t size, int prot); - extern size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova, - size_t size); -+extern size_t default_iommu_map_sg(struct iommu_domain *domain, unsigned long iova, -+ struct scatterlist *sg,unsigned int nents, -+ int prot); - extern phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova); - extern void iommu_set_fault_handler(struct iommu_domain *domain, - iommu_fault_handler_t handler, void *token); -@@ -200,6 +246,9 @@ extern int iommu_domain_window_enable(struct iommu_domain *domain, u32 wnd_nr, - phys_addr_t offset, u64 size, - int prot); - extern void iommu_domain_window_disable(struct iommu_domain *domain, u32 wnd_nr); -+ -+extern struct iommu_domain *iommu_get_dev_domain(struct device *dev); -+ - /** - * report_iommu_fault() - report about an IOMMU fault to the IOMMU framework - * @domain: the iommu domain where the fault has happened -@@ -241,6 +290,13 @@ static inline int report_iommu_fault(struct iommu_domain *domain, - return ret; - } - -+static inline size_t iommu_map_sg(struct iommu_domain *domain, -+ unsigned long iova, struct scatterlist *sg, -+ unsigned int nents, int prot) -+{ -+ return domain->ops->map_sg(domain, iova, sg, nents, prot); -+} -+ - #else /* CONFIG_IOMMU_API */ - - struct iommu_ops {}; -@@ -293,6 +349,13 @@ static inline int iommu_unmap(struct iommu_domain *domain, unsigned long iova, - return -ENODEV; - } - -+static inline size_t iommu_map_sg(struct iommu_domain *domain, -+ unsigned long iova, struct scatterlist *sg, -+ unsigned int nents, int prot) -+{ -+ return -ENODEV; -+} -+ - static inline int iommu_domain_window_enable(struct iommu_domain *domain, - u32 wnd_nr, phys_addr_t paddr, - u64 size, int prot) -@@ -424,6 +487,11 @@ static inline void iommu_device_unlink(struct device *dev, struct device *link) - { - } - -+static inline struct iommu_domain *iommu_get_dev_domain(struct device *dev) -+{ -+ return NULL; -+} -+ - #endif /* CONFIG_IOMMU_API */ - - #endif /* __LINUX_IOMMU_H */ -diff --git a/include/linux/iopoll.h b/include/linux/iopoll.h -new file mode 100644 -index 0000000..1c30014 ---- /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 -+#include -+#include -+#include -+#include -+#include -+ -+/** -+ * 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(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/irq.h b/include/linux/irq.h -index 03f48d9..4931a8b 100644 ---- a/include/linux/irq.h -+++ b/include/linux/irq.h -@@ -15,11 +15,13 @@ - #include - #include - #include -+#include - #include - #include - #include - #include - #include -+#include - - #include - #include -@@ -27,11 +29,8 @@ - - struct seq_file; - struct module; --struct irq_desc; --struct irq_data; --typedef void (*irq_flow_handler_t)(unsigned int irq, -- struct irq_desc *desc); --typedef void (*irq_preflow_handler_t)(struct irq_data *data); -+struct msi_msg; -+enum irqchip_irq_state; - - /* - * IRQ line status. -@@ -113,10 +112,14 @@ enum { - * - * IRQ_SET_MASK_OK - OK, core updates irq_data.affinity - * IRQ_SET_MASK_NOCPY - OK, chip did update irq_data.affinity -+ * IRQ_SET_MASK_OK_DONE - Same as IRQ_SET_MASK_OK for core. Special code to -+ * support stacked irqchips, which indicates skipping -+ * all descendent irqchips. - */ - enum { - IRQ_SET_MASK_OK = 0, - IRQ_SET_MASK_OK_NOCOPY, -+ IRQ_SET_MASK_OK_DONE, - }; - - struct msi_desc; -@@ -133,6 +136,8 @@ struct irq_domain; - * @chip: low level interrupt hardware access - * @domain: Interrupt translation domain; responsible for mapping - * between hwirq number and linux irq number. -+ * @parent_data: pointer to parent struct irq_data to support hierarchy -+ * irq_domain - * @handler_data: per-IRQ data for the irq_chip methods - * @chip_data: platform-specific per-chip private data for the chip - * methods, to allow shared chip implementations -@@ -151,6 +156,9 @@ struct irq_data { - unsigned int state_use_accessors; - struct irq_chip *chip; - struct irq_domain *domain; -+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY -+ struct irq_data *parent_data; -+#endif - void *handler_data; - void *chip_data; - struct msi_desc *msi_desc; -@@ -315,6 +323,10 @@ static inline irq_hw_number_t irqd_to_hwirq(struct irq_data *d) - * any other callback related to this irq - * @irq_release_resources: optional to release resources acquired with - * irq_request_resources -+ * @irq_compose_msi_msg: optional to compose message content for MSI -+ * @irq_write_msi_msg: optional to write message content for MSI -+ * @irq_get_irqchip_state: return the internal state of an interrupt -+ * @irq_set_irqchip_state: set the internal state of a interrupt - * @flags: chip specific flags - */ - struct irq_chip { -@@ -351,6 +363,12 @@ struct irq_chip { - int (*irq_request_resources)(struct irq_data *data); - void (*irq_release_resources)(struct irq_data *data); - -+ void (*irq_compose_msi_msg)(struct irq_data *data, struct msi_msg *msg); -+ void (*irq_write_msi_msg)(struct irq_data *data, struct msi_msg *msg); -+ -+ int (*irq_get_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool *state); -+ int (*irq_set_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool state); -+ - unsigned long flags; - }; - -@@ -438,6 +456,20 @@ extern void handle_percpu_devid_irq(unsigned int irq, struct irq_desc *desc); - extern void handle_bad_irq(unsigned int irq, struct irq_desc *desc); - extern void handle_nested_irq(unsigned int irq); - -+extern int irq_chip_compose_msi_msg(struct irq_data *data, struct msi_msg *msg); -+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY -+extern void irq_chip_ack_parent(struct irq_data *data); -+extern int irq_chip_retrigger_hierarchy(struct irq_data *data); -+extern void irq_chip_mask_parent(struct irq_data *data); -+extern void irq_chip_unmask_parent(struct irq_data *data); -+extern void irq_chip_eoi_parent(struct irq_data *data); -+extern int irq_chip_set_affinity_parent(struct irq_data *data, -+ const struct cpumask *dest, -+ bool force); -+extern int irq_chip_set_wake_parent(struct irq_data *data, unsigned int on); -+extern int irq_chip_set_type_parent(struct irq_data *data, unsigned int type); -+#endif -+ - /* Handling of unhandled and spurious interrupts: */ - extern void note_interrupt(unsigned int irq, struct irq_desc *desc, - irqreturn_t action_ret); -@@ -582,7 +614,7 @@ static inline struct msi_desc *irq_get_msi_desc(unsigned int irq) - return d ? d->msi_desc : NULL; - } - --static inline struct msi_desc *irq_data_get_msi(struct irq_data *d) -+static inline struct msi_desc *irq_data_get_msi_desc(struct irq_data *d) - { - return d->msi_desc; - } -@@ -639,13 +671,6 @@ void arch_teardown_hwirq(unsigned int irq); - void irq_init_desc(unsigned int irq); - #endif - --#ifndef irq_reg_writel --# define irq_reg_writel(val, addr) writel(val, addr) --#endif --#ifndef irq_reg_readl --# define irq_reg_readl(addr) readl(addr) --#endif -- - /** - * struct irq_chip_regs - register offsets for struct irq_gci - * @enable: Enable register offset to reg_base -@@ -692,6 +717,8 @@ struct irq_chip_type { - * struct irq_chip_generic - Generic irq chip data structure - * @lock: Lock to protect register and cache data access - * @reg_base: Register base address (virtual) -+ * @reg_readl: Alternate I/O accessor (defaults to readl if NULL) -+ * @reg_writel: Alternate I/O accessor (defaults to writel if NULL) - * @irq_base: Interrupt base nr for this chip - * @irq_cnt: Number of interrupts handled by this chip - * @mask_cache: Cached mask register shared between all chip types -@@ -716,6 +743,8 @@ struct irq_chip_type { - struct irq_chip_generic { - raw_spinlock_t lock; - void __iomem *reg_base; -+ u32 (*reg_readl)(void __iomem *addr); -+ void (*reg_writel)(u32 val, void __iomem *addr); - unsigned int irq_base; - unsigned int irq_cnt; - u32 mask_cache; -@@ -740,12 +769,14 @@ struct irq_chip_generic { - * the parent irq. Usually GPIO implementations - * @IRQ_GC_MASK_CACHE_PER_TYPE: Mask cache is chip type private - * @IRQ_GC_NO_MASK: Do not calculate irq_data->mask -+ * @IRQ_GC_BE_IO: Use big-endian register accesses (default: LE) - */ - enum irq_gc_flags { - IRQ_GC_INIT_MASK_CACHE = 1 << 0, - IRQ_GC_INIT_NESTED_LOCK = 1 << 1, - IRQ_GC_MASK_CACHE_PER_TYPE = 1 << 2, - IRQ_GC_NO_MASK = 1 << 3, -+ IRQ_GC_BE_IO = 1 << 4, - }; - - /* -@@ -821,4 +852,22 @@ static inline void irq_gc_lock(struct irq_chip_generic *gc) { } - static inline void irq_gc_unlock(struct irq_chip_generic *gc) { } - #endif - -+static inline void irq_reg_writel(struct irq_chip_generic *gc, -+ u32 val, int reg_offset) -+{ -+ if (gc->reg_writel) -+ gc->reg_writel(val, gc->reg_base + reg_offset); -+ else -+ writel(val, gc->reg_base + reg_offset); -+} -+ -+static inline u32 irq_reg_readl(struct irq_chip_generic *gc, -+ int reg_offset) -+{ -+ if (gc->reg_readl) -+ return gc->reg_readl(gc->reg_base + reg_offset); -+ else -+ return readl(gc->reg_base + reg_offset); -+} -+ - #endif /* _LINUX_IRQ_H */ -diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h -index 03a4ea3..36caf46 100644 ---- a/include/linux/irqchip/arm-gic-v3.h -+++ b/include/linux/irqchip/arm-gic-v3.h -@@ -49,6 +49,10 @@ - #define GICD_CTLR_ENABLE_G1A (1U << 1) - #define GICD_CTLR_ENABLE_G1 (1U << 0) - -+#define GICD_TYPER_ID_BITS(typer) ((((typer) >> 19) & 0x1f) + 1) -+#define GICD_TYPER_IRQS(typer) ((((typer) & 0x1f) + 1) * 32) -+#define GICD_TYPER_LPIS (1U << 17) -+ - #define GICD_IROUTER_SPI_MODE_ONE (0U << 31) - #define GICD_IROUTER_SPI_MODE_ANY (1U << 31) - -@@ -76,9 +80,42 @@ - #define GICR_MOVALLR 0x0110 - #define GICR_PIDR2 GICD_PIDR2 - -+#define GICR_CTLR_ENABLE_LPIS (1UL << 0) -+ -+#define GICR_TYPER_CPU_NUMBER(r) (((r) >> 8) & 0xffff) -+ - #define GICR_WAKER_ProcessorSleep (1U << 1) - #define GICR_WAKER_ChildrenAsleep (1U << 2) - -+#define GICR_PROPBASER_NonShareable (0U << 10) -+#define GICR_PROPBASER_InnerShareable (1U << 10) -+#define GICR_PROPBASER_OuterShareable (2U << 10) -+#define GICR_PROPBASER_SHAREABILITY_MASK (3UL << 10) -+#define GICR_PROPBASER_nCnB (0U << 7) -+#define GICR_PROPBASER_nC (1U << 7) -+#define GICR_PROPBASER_RaWt (2U << 7) -+#define GICR_PROPBASER_RaWb (3U << 7) -+#define GICR_PROPBASER_WaWt (4U << 7) -+#define GICR_PROPBASER_WaWb (5U << 7) -+#define GICR_PROPBASER_RaWaWt (6U << 7) -+#define GICR_PROPBASER_RaWaWb (7U << 7) -+#define GICR_PROPBASER_CACHEABILITY_MASK (7U << 7) -+#define GICR_PROPBASER_IDBITS_MASK (0x1f) -+ -+#define GICR_PENDBASER_NonShareable (0U << 10) -+#define GICR_PENDBASER_InnerShareable (1U << 10) -+#define GICR_PENDBASER_OuterShareable (2U << 10) -+#define GICR_PENDBASER_SHAREABILITY_MASK (3UL << 10) -+#define GICR_PENDBASER_nCnB (0U << 7) -+#define GICR_PENDBASER_nC (1U << 7) -+#define GICR_PENDBASER_RaWt (2U << 7) -+#define GICR_PENDBASER_RaWb (3U << 7) -+#define GICR_PENDBASER_WaWt (4U << 7) -+#define GICR_PENDBASER_WaWb (5U << 7) -+#define GICR_PENDBASER_RaWaWt (6U << 7) -+#define GICR_PENDBASER_RaWaWb (7U << 7) -+#define GICR_PENDBASER_CACHEABILITY_MASK (7U << 7) -+ - /* - * Re-Distributor registers, offsets from SGI_base - */ -@@ -91,9 +128,100 @@ - #define GICR_IPRIORITYR0 GICD_IPRIORITYR - #define GICR_ICFGR0 GICD_ICFGR - -+#define GICR_TYPER_PLPIS (1U << 0) - #define GICR_TYPER_VLPIS (1U << 1) - #define GICR_TYPER_LAST (1U << 4) - -+#define LPI_PROP_GROUP1 (1 << 1) -+#define LPI_PROP_ENABLED (1 << 0) -+ -+/* -+ * ITS registers, offsets from ITS_base -+ */ -+#define GITS_CTLR 0x0000 -+#define GITS_IIDR 0x0004 -+#define GITS_TYPER 0x0008 -+#define GITS_CBASER 0x0080 -+#define GITS_CWRITER 0x0088 -+#define GITS_CREADR 0x0090 -+#define GITS_BASER 0x0100 -+#define GITS_PIDR2 GICR_PIDR2 -+ -+#define GITS_TRANSLATER 0x10040 -+ -+#define GITS_CTLR_ENABLE (1U << 0) -+#define GITS_CTLR_QUIESCENT (1U << 31) -+ -+#define GITS_TYPER_DEVBITS_SHIFT 13 -+#define GITS_TYPER_DEVBITS(r) ((((r) >> GITS_TYPER_DEVBITS_SHIFT) & 0x1f) + 1) -+#define GITS_TYPER_PTA (1UL << 19) -+ -+#define GITS_CBASER_VALID (1UL << 63) -+#define GITS_CBASER_nCnB (0UL << 59) -+#define GITS_CBASER_nC (1UL << 59) -+#define GITS_CBASER_RaWt (2UL << 59) -+#define GITS_CBASER_RaWb (3UL << 59) -+#define GITS_CBASER_WaWt (4UL << 59) -+#define GITS_CBASER_WaWb (5UL << 59) -+#define GITS_CBASER_RaWaWt (6UL << 59) -+#define GITS_CBASER_RaWaWb (7UL << 59) -+#define GITS_CBASER_CACHEABILITY_MASK (7UL << 59) -+#define GITS_CBASER_NonShareable (0UL << 10) -+#define GITS_CBASER_InnerShareable (1UL << 10) -+#define GITS_CBASER_OuterShareable (2UL << 10) -+#define GITS_CBASER_SHAREABILITY_MASK (3UL << 10) -+ -+#define GITS_BASER_NR_REGS 8 -+ -+#define GITS_BASER_VALID (1UL << 63) -+#define GITS_BASER_nCnB (0UL << 59) -+#define GITS_BASER_nC (1UL << 59) -+#define GITS_BASER_RaWt (2UL << 59) -+#define GITS_BASER_RaWb (3UL << 59) -+#define GITS_BASER_WaWt (4UL << 59) -+#define GITS_BASER_WaWb (5UL << 59) -+#define GITS_BASER_RaWaWt (6UL << 59) -+#define GITS_BASER_RaWaWb (7UL << 59) -+#define GITS_BASER_CACHEABILITY_MASK (7UL << 59) -+#define GITS_BASER_TYPE_SHIFT (56) -+#define GITS_BASER_TYPE(r) (((r) >> GITS_BASER_TYPE_SHIFT) & 7) -+#define GITS_BASER_ENTRY_SIZE_SHIFT (48) -+#define GITS_BASER_ENTRY_SIZE(r) ((((r) >> GITS_BASER_ENTRY_SIZE_SHIFT) & 0xff) + 1) -+#define GITS_BASER_NonShareable (0UL << 10) -+#define GITS_BASER_InnerShareable (1UL << 10) -+#define GITS_BASER_OuterShareable (2UL << 10) -+#define GITS_BASER_SHAREABILITY_SHIFT (10) -+#define GITS_BASER_SHAREABILITY_MASK (3UL << GITS_BASER_SHAREABILITY_SHIFT) -+#define GITS_BASER_PAGE_SIZE_SHIFT (8) -+#define GITS_BASER_PAGE_SIZE_4K (0UL << GITS_BASER_PAGE_SIZE_SHIFT) -+#define GITS_BASER_PAGE_SIZE_16K (1UL << GITS_BASER_PAGE_SIZE_SHIFT) -+#define GITS_BASER_PAGE_SIZE_64K (2UL << GITS_BASER_PAGE_SIZE_SHIFT) -+#define GITS_BASER_PAGE_SIZE_MASK (3UL << GITS_BASER_PAGE_SIZE_SHIFT) -+ -+#define GITS_BASER_TYPE_NONE 0 -+#define GITS_BASER_TYPE_DEVICE 1 -+#define GITS_BASER_TYPE_VCPU 2 -+#define GITS_BASER_TYPE_CPU 3 -+#define GITS_BASER_TYPE_COLLECTION 4 -+#define GITS_BASER_TYPE_RESERVED5 5 -+#define GITS_BASER_TYPE_RESERVED6 6 -+#define GITS_BASER_TYPE_RESERVED7 7 -+ -+/* -+ * ITS commands -+ */ -+#define GITS_CMD_MAPD 0x08 -+#define GITS_CMD_MAPC 0x09 -+#define GITS_CMD_MAPVI 0x0a -+#define GITS_CMD_MOVI 0x01 -+#define GITS_CMD_DISCARD 0x0f -+#define GITS_CMD_INV 0x0c -+#define GITS_CMD_MOVALL 0x0e -+#define GITS_CMD_INVALL 0x0d -+#define GITS_CMD_INT 0x03 -+#define GITS_CMD_CLEAR 0x04 -+#define GITS_CMD_SYNC 0x05 -+ - /* - * CPU interface registers - */ -@@ -142,6 +270,18 @@ - #define ICC_SRE_EL2_SRE (1 << 0) - #define ICC_SRE_EL2_ENABLE (1 << 3) - -+#define ICC_SGI1R_TARGET_LIST_SHIFT 0 -+#define ICC_SGI1R_TARGET_LIST_MASK (0xffff << ICC_SGI1R_TARGET_LIST_SHIFT) -+#define ICC_SGI1R_AFFINITY_1_SHIFT 16 -+#define ICC_SGI1R_AFFINITY_1_MASK (0xff << ICC_SGI1R_AFFINITY_1_SHIFT) -+#define ICC_SGI1R_SGI_ID_SHIFT 24 -+#define ICC_SGI1R_SGI_ID_MASK (0xff << ICC_SGI1R_SGI_ID_SHIFT) -+#define ICC_SGI1R_AFFINITY_2_SHIFT 32 -+#define ICC_SGI1R_AFFINITY_2_MASK (0xffULL << ICC_SGI1R_AFFINITY_1_SHIFT) -+#define ICC_SGI1R_IRQ_ROUTING_MODE_BIT 40 -+#define ICC_SGI1R_AFFINITY_3_SHIFT 48 -+#define ICC_SGI1R_AFFINITY_3_MASK (0xffULL << ICC_SGI1R_AFFINITY_1_SHIFT) -+ - /* - * System register definitions - */ -@@ -188,6 +328,24 @@ - #ifndef __ASSEMBLY__ - - #include -+#include -+ -+/* -+ * We need a value to serve as a irq-type for LPIs. Choose one that will -+ * hopefully pique the interest of the reviewer. -+ */ -+#define GIC_IRQ_TYPE_LPI 0xa110c8ed -+ -+struct rdists { -+ struct { -+ void __iomem *rd_base; -+ struct page *pend_page; -+ phys_addr_t phys_base; -+ } __percpu *rdist; -+ struct page *prop_page; -+ int id_bits; -+ u64 flags; -+}; - - static inline void gic_write_eoir(u64 irq) - { -@@ -195,6 +353,13 @@ static inline void gic_write_eoir(u64 irq) - isb(); - } - -+struct irq_domain; -+int its_cpu_init(void); -+int its_init(struct device_node *node, struct rdists *rdists, -+ struct irq_domain *domain); -+int __its_msi_prepare(struct irq_domain *domain, u32 dev_id, -+ struct device *dev, int nvec, msi_alloc_info_t *info); -+ - #endif - - #endif -diff --git a/include/linux/irqchip/arm-gic.h b/include/linux/irqchip/arm-gic.h -index 13eed92..60b09ed 100644 ---- a/include/linux/irqchip/arm-gic.h -+++ b/include/linux/irqchip/arm-gic.h -@@ -106,6 +106,8 @@ static inline void gic_init(unsigned int nr, int start, - gic_init_bases(nr, start, dist, cpu, 0, NULL); - } - -+int gicv2m_of_init(struct device_node *node, struct irq_domain *parent); -+ - void gic_send_sgi(unsigned int cpu_id, unsigned int irq); - int gic_get_cpu_id(unsigned int cpu); - void gic_migrate_target(unsigned int new_cpu_id); -diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h -index b0f9d16..3c5ca45 100644 ---- a/include/linux/irqdomain.h -+++ b/include/linux/irqdomain.h -@@ -33,15 +33,32 @@ - #define _LINUX_IRQDOMAIN_H - - #include -+#include - #include - - struct device_node; - struct irq_domain; - struct of_device_id; -+struct irq_chip; -+struct irq_data; - - /* Number of irqs reserved for a legacy isa controller */ - #define NUM_ISA_INTERRUPTS 16 - -+/* -+ * Should several domains have the same device node, but serve -+ * different purposes (for example one domain is for PCI/MSI, and the -+ * other for wired IRQs), they can be distinguished using a -+ * bus-specific token. Most domains are expected to only carry -+ * DOMAIN_BUS_ANY. -+ */ -+enum irq_domain_bus_token { -+ DOMAIN_BUS_ANY = 0, -+ DOMAIN_BUS_PCI_MSI, -+ DOMAIN_BUS_PLATFORM_MSI, -+ DOMAIN_BUS_NEXUS, -+}; -+ - /** - * struct irq_domain_ops - Methods for irq_domain objects - * @match: Match an interrupt controller device node to a host, returns -@@ -58,12 +75,23 @@ struct of_device_id; - * to setup the irq_desc when returning from map(). - */ - struct irq_domain_ops { -- int (*match)(struct irq_domain *d, struct device_node *node); -+ int (*match)(struct irq_domain *d, struct device_node *node, -+ enum irq_domain_bus_token bus_token); - int (*map)(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw); - void (*unmap)(struct irq_domain *d, unsigned int virq); - int (*xlate)(struct irq_domain *d, struct device_node *node, - const u32 *intspec, unsigned int intsize, - unsigned long *out_hwirq, unsigned int *out_type); -+ -+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY -+ /* extended V2 interfaces to support hierarchy irq_domains */ -+ int (*alloc)(struct irq_domain *d, unsigned int virq, -+ unsigned int nr_irqs, void *arg); -+ void (*free)(struct irq_domain *d, unsigned int virq, -+ unsigned int nr_irqs); -+ void (*activate)(struct irq_domain *d, struct irq_data *irq_data); -+ void (*deactivate)(struct irq_domain *d, struct irq_data *irq_data); -+#endif - }; - - extern struct irq_domain_ops irq_generic_chip_ops; -@@ -77,6 +105,7 @@ struct irq_domain_chip_generic; - * @ops: pointer to irq_domain methods - * @host_data: private data pointer for use by owner. Not touched by irq_domain - * core code. -+ * @flags: host per irq_domain flags - * - * Optional elements - * @of_node: Pointer to device tree nodes associated with the irq_domain. Used -@@ -84,6 +113,7 @@ struct irq_domain_chip_generic; - * @gc: Pointer to a list of generic chips. There is a helper function for - * setting up one or more generic chips for interrupt controllers - * drivers using the generic chip library which uses this pointer. -+ * @parent: Pointer to parent irq_domain to support hierarchy irq_domains - * - * Revmap data, used internally by irq_domain - * @revmap_direct_max_irq: The largest hwirq that can be set for controllers that -@@ -97,10 +127,15 @@ struct irq_domain { - const char *name; - const struct irq_domain_ops *ops; - void *host_data; -+ unsigned int flags; - - /* Optional data */ - struct device_node *of_node; -+ enum irq_domain_bus_token bus_token; - struct irq_domain_chip_generic *gc; -+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY -+ struct irq_domain *parent; -+#endif - - /* reverse map data. The linear map gets appended to the irq_domain */ - irq_hw_number_t hwirq_max; -@@ -110,6 +145,22 @@ struct irq_domain { - unsigned int linear_revmap[]; - }; - -+/* Irq domain flags */ -+enum { -+ /* Irq domain is hierarchical */ -+ IRQ_DOMAIN_FLAG_HIERARCHY = (1 << 0), -+ -+ /* Core calls alloc/free recursive through the domain hierarchy. */ -+ IRQ_DOMAIN_FLAG_AUTO_RECURSIVE = (1 << 1), -+ -+ /* -+ * Flags starting from IRQ_DOMAIN_FLAG_NONCORE are reserved -+ * for implementation specific purposes and ignored by the -+ * core code. -+ */ -+ IRQ_DOMAIN_FLAG_NONCORE = (1 << 16), -+}; -+ - #ifdef CONFIG_IRQ_DOMAIN - struct irq_domain *__irq_domain_add(struct device_node *of_node, int size, - irq_hw_number_t hwirq_max, int direct_max, -@@ -126,9 +177,15 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node, - irq_hw_number_t first_hwirq, - const struct irq_domain_ops *ops, - void *host_data); --extern struct irq_domain *irq_find_host(struct device_node *node); -+extern struct irq_domain *irq_find_matching_host(struct device_node *node, -+ enum irq_domain_bus_token bus_token); - extern void irq_set_default_host(struct irq_domain *host); - -+static inline struct irq_domain *irq_find_host(struct device_node *node) -+{ -+ return irq_find_matching_host(node, DOMAIN_BUS_ANY); -+} -+ - /** - * irq_domain_add_linear() - Allocate and register a linear revmap irq_domain. - * @of_node: pointer to interrupt controller's device tree node. -@@ -220,8 +277,74 @@ int irq_domain_xlate_onetwocell(struct irq_domain *d, struct device_node *ctrlr, - const u32 *intspec, unsigned int intsize, - irq_hw_number_t *out_hwirq, unsigned int *out_type); - -+/* V2 interfaces to support hierarchy IRQ domains. */ -+extern struct irq_data *irq_domain_get_irq_data(struct irq_domain *domain, -+ unsigned int virq); -+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY -+extern struct irq_domain *irq_domain_add_hierarchy(struct irq_domain *parent, -+ unsigned int flags, unsigned int size, -+ struct device_node *node, -+ const struct irq_domain_ops *ops, void *host_data); -+extern int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base, -+ unsigned int nr_irqs, int node, void *arg, -+ bool realloc); -+extern void irq_domain_free_irqs(unsigned int virq, unsigned int nr_irqs); -+extern void irq_domain_activate_irq(struct irq_data *irq_data); -+extern void irq_domain_deactivate_irq(struct irq_data *irq_data); -+ -+static inline int irq_domain_alloc_irqs(struct irq_domain *domain, -+ unsigned int nr_irqs, int node, void *arg) -+{ -+ return __irq_domain_alloc_irqs(domain, -1, nr_irqs, node, arg, false); -+} -+ -+extern int irq_domain_set_hwirq_and_chip(struct irq_domain *domain, -+ unsigned int virq, -+ irq_hw_number_t hwirq, -+ struct irq_chip *chip, -+ void *chip_data); -+extern void irq_domain_set_info(struct irq_domain *domain, unsigned int virq, -+ irq_hw_number_t hwirq, struct irq_chip *chip, -+ void *chip_data, irq_flow_handler_t handler, -+ void *handler_data, const char *handler_name); -+extern void irq_domain_reset_irq_data(struct irq_data *irq_data); -+extern void irq_domain_free_irqs_common(struct irq_domain *domain, -+ unsigned int virq, -+ unsigned int nr_irqs); -+extern void irq_domain_free_irqs_top(struct irq_domain *domain, -+ unsigned int virq, unsigned int nr_irqs); -+ -+extern int irq_domain_alloc_irqs_parent(struct irq_domain *domain, -+ unsigned int irq_base, -+ unsigned int nr_irqs, void *arg); -+ -+extern void irq_domain_free_irqs_parent(struct irq_domain *domain, -+ unsigned int irq_base, -+ unsigned int nr_irqs); -+ -+static inline bool irq_domain_is_hierarchy(struct irq_domain *domain) -+{ -+ return domain->flags & IRQ_DOMAIN_FLAG_HIERARCHY; -+} -+#else /* CONFIG_IRQ_DOMAIN_HIERARCHY */ -+static inline void irq_domain_activate_irq(struct irq_data *data) { } -+static inline void irq_domain_deactivate_irq(struct irq_data *data) { } -+static inline int irq_domain_alloc_irqs(struct irq_domain *domain, -+ unsigned int nr_irqs, int node, void *arg) -+{ -+ return -1; -+} -+ -+static inline bool irq_domain_is_hierarchy(struct irq_domain *domain) -+{ -+ return false; -+} -+#endif /* CONFIG_IRQ_DOMAIN_HIERARCHY */ -+ - #else /* CONFIG_IRQ_DOMAIN */ - static inline void irq_dispose_mapping(unsigned int virq) { } -+static inline void irq_domain_activate_irq(struct irq_data *data) { } -+static inline void irq_domain_deactivate_irq(struct irq_data *data) { } - #endif /* !CONFIG_IRQ_DOMAIN */ - - #endif /* _LINUX_IRQDOMAIN_H */ -diff --git a/include/linux/irqhandler.h b/include/linux/irqhandler.h -new file mode 100644 -index 0000000..62d5430 ---- /dev/null -+++ b/include/linux/irqhandler.h -@@ -0,0 +1,14 @@ -+#ifndef _LINUX_IRQHANDLER_H -+#define _LINUX_IRQHANDLER_H -+ -+/* -+ * Interrupt flow handler typedefs are defined here to avoid circular -+ * include dependencies. -+ */ -+ -+struct irq_desc; -+struct irq_data; -+typedef void (*irq_flow_handler_t)(unsigned int irq, struct irq_desc *desc); -+typedef void (*irq_preflow_handler_t)(struct irq_data *data); -+ -+#endif -diff --git a/include/linux/mm.h b/include/linux/mm.h -index 86a977b..a2d0dbb 100644 ---- a/include/linux/mm.h -+++ b/include/linux/mm.h -@@ -1208,6 +1208,28 @@ static inline int fixup_user_fault(struct task_struct *tsk, - } - #endif - -+extern void vma_do_file_update_time(struct vm_area_struct *, const char[], int); -+extern struct file *vma_do_pr_or_file(struct vm_area_struct *, const char[], -+ int); -+extern void vma_do_get_file(struct vm_area_struct *, const char[], int); -+extern void vma_do_fput(struct vm_area_struct *, const char[], int); -+ -+#define vma_file_update_time(vma) vma_do_file_update_time(vma, __func__, \ -+ __LINE__) -+#define vma_pr_or_file(vma) vma_do_pr_or_file(vma, __func__, \ -+ __LINE__) -+#define vma_get_file(vma) vma_do_get_file(vma, __func__, __LINE__) -+#define vma_fput(vma) vma_do_fput(vma, __func__, __LINE__) -+ -+#ifndef CONFIG_MMU -+extern struct file *vmr_do_pr_or_file(struct vm_region *, const char[], int); -+extern void vmr_do_fput(struct vm_region *, const char[], int); -+ -+#define vmr_pr_or_file(region) vmr_do_pr_or_file(region, __func__, \ -+ __LINE__) -+#define vmr_fput(region) vmr_do_fput(region, __func__, __LINE__) -+#endif /* !CONFIG_MMU */ -+ - extern int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write); - extern int access_remote_vm(struct mm_struct *mm, unsigned long addr, - void *buf, int len, int write); -diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h -index 6e0b286..8f374ed 100644 ---- a/include/linux/mm_types.h -+++ b/include/linux/mm_types.h -@@ -232,6 +232,7 @@ struct vm_region { - unsigned long vm_top; /* region allocated to here */ - unsigned long vm_pgoff; /* the offset in vm_file corresponding to vm_start */ - struct file *vm_file; /* the backing file or NULL */ -+ struct file *vm_prfile; /* the virtual backing file or NULL */ - - int vm_usage; /* region usage count (access under nommu_region_sem) */ - bool vm_icache_flushed : 1; /* true if the icache has been flushed for -@@ -300,6 +301,7 @@ struct vm_area_struct { - unsigned long vm_pgoff; /* Offset (within vm_file) in PAGE_SIZE - units, *not* PAGE_CACHE_SIZE */ - struct file * vm_file; /* File we map to (can be NULL). */ -+ struct file *vm_prfile; /* shadow of vm_file */ - void * vm_private_data; /* was vm_pte (shared mem) */ - - #ifndef CONFIG_MMU -diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h -index dba793e..62d966a 100644 ---- a/include/linux/mmc/sdhci.h -+++ b/include/linux/mmc/sdhci.h -@@ -100,6 +100,10 @@ struct sdhci_host { - #define SDHCI_QUIRK2_BROKEN_DDR50 (1<<7) - /* Stop command (CMD12) can set Transfer Complete when not using MMC_RSP_BUSY */ - #define SDHCI_QUIRK2_STOP_WITH_TC (1<<8) -+/* Controller does not support 64-bit DMA */ -+#define SDHCI_QUIRK2_BROKEN_64_BIT_DMA (1<<9) -+/* Controller broken with using ACMD23 */ -+#define SDHCI_QUIRK2_ACMD23_BROKEN (1<<14) - - int irq; /* Device IRQ */ - void __iomem *ioaddr; /* Mapped address */ -@@ -130,6 +134,7 @@ struct sdhci_host { - #define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */ - #define SDHCI_SDR104_NEEDS_TUNING (1<<10) /* SDR104/HS200 needs tuning */ - #define SDHCI_USING_RETUNING_TIMER (1<<11) /* Host is using a retuning timer for the card */ -+#define SDHCI_USE_64_BIT_DMA (1<<12) /* Use 64-bit DMA */ - - unsigned int version; /* SDHCI spec. version */ - -@@ -155,12 +160,19 @@ struct sdhci_host { - - int sg_count; /* Mapped sg entries */ - -- u8 *adma_desc; /* ADMA descriptor table */ -- u8 *align_buffer; /* Bounce buffer */ -+ void *adma_table; /* ADMA descriptor table */ -+ void *align_buffer; /* Bounce buffer */ -+ -+ size_t adma_table_sz; /* ADMA descriptor table size */ -+ size_t align_buffer_sz; /* Bounce buffer size */ - - dma_addr_t adma_addr; /* Mapped ADMA descr. table */ - dma_addr_t align_addr; /* Mapped bounce buffer */ - -+ unsigned int desc_sz; /* ADMA descriptor size */ -+ unsigned int align_sz; /* ADMA alignment */ -+ unsigned int align_mask; /* ADMA alignment mask */ -+ - struct tasklet_struct finish_tasklet; /* Tasklet structures */ - - struct timer_list timer; /* Timer for timeouts */ -diff --git a/include/linux/msi.h b/include/linux/msi.h -index 44f4746..788d65b 100644 ---- a/include/linux/msi.h -+++ b/include/linux/msi.h -@@ -10,17 +10,13 @@ struct msi_msg { - u32 data; /* 16 bits of msi message data */ - }; - -+extern int pci_msi_ignore_mask; - /* Helper functions */ - struct irq_data; - struct msi_desc; --void mask_msi_irq(struct irq_data *data); --void unmask_msi_irq(struct irq_data *data); --void __read_msi_msg(struct msi_desc *entry, struct msi_msg *msg); -+struct pci_dev; - void __get_cached_msi_msg(struct msi_desc *entry, struct msi_msg *msg); --void __write_msi_msg(struct msi_desc *entry, struct msi_msg *msg); --void read_msi_msg(unsigned int irq, struct msi_msg *msg); - void get_cached_msi_msg(unsigned int irq, struct msi_msg *msg); --void write_msi_msg(unsigned int irq, struct msi_msg *msg); - - struct msi_desc { - struct { -@@ -42,12 +38,63 @@ struct msi_desc { - void __iomem *mask_base; - u8 mask_pos; - }; -- struct pci_dev *dev; -+ struct device *dev; - - /* Last set MSI message */ - struct msi_msg msg; - }; - -+/* Helpers to hide struct msi_desc implementation details */ -+#define msi_desc_to_dev(desc) ((desc)->dev) -+#define dev_to_msi_list(dev) (&(dev)->msi_list) -+#define first_msi_entry(dev) \ -+ list_first_entry(dev_to_msi_list((dev)), struct msi_desc, list) -+#define for_each_msi_entry(desc, dev) \ -+ list_for_each_entry((desc), dev_to_msi_list((dev)), list) -+ -+#ifdef CONFIG_PCI_MSI -+#define first_pci_msi_entry(pdev) first_msi_entry(&(pdev)->dev) -+#define for_each_pci_msi_entry(desc, pdev) \ -+ for_each_msi_entry((desc), &(pdev)->dev) -+ -+struct pci_dev *msi_desc_to_pci_dev(struct msi_desc *desc); -+void *msi_desc_to_pci_sysdata(struct msi_desc *desc); -+#else /* CONFIG_PCI_MSI */ -+static inline void *msi_desc_to_pci_sysdata(struct msi_desc *desc) -+{ -+ return NULL; -+} -+#endif /* CONFIG_PCI_MSI */ -+ -+struct msi_desc *alloc_msi_entry(struct device *dev); -+void free_msi_entry(struct msi_desc *entry); -+void __pci_read_msi_msg(struct msi_desc *entry, struct msi_msg *msg); -+void __pci_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg); -+void pci_write_msi_msg(unsigned int irq, struct msi_msg *msg); -+ -+u32 __pci_msix_desc_mask_irq(struct msi_desc *desc, u32 flag); -+u32 __pci_msi_desc_mask_irq(struct msi_desc *desc, u32 mask, u32 flag); -+void pci_msi_mask_irq(struct irq_data *data); -+void pci_msi_unmask_irq(struct irq_data *data); -+ -+/* Conversion helpers. Should be removed after merging */ -+static inline void __write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) -+{ -+ __pci_write_msi_msg(entry, msg); -+} -+static inline void write_msi_msg(int irq, struct msi_msg *msg) -+{ -+ pci_write_msi_msg(irq, msg); -+} -+static inline void mask_msi_irq(struct irq_data *data) -+{ -+ pci_msi_mask_irq(data); -+} -+static inline void unmask_msi_irq(struct irq_data *data) -+{ -+ pci_msi_unmask_irq(data); -+} -+ - /* - * The arch hooks to setup up msi irqs. Those functions are - * implemented as weak symbols so that they /can/ be overriden by -@@ -61,18 +108,146 @@ void arch_restore_msi_irqs(struct pci_dev *dev); - - void default_teardown_msi_irqs(struct pci_dev *dev); - void default_restore_msi_irqs(struct pci_dev *dev); --u32 default_msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag); --u32 default_msix_mask_irq(struct msi_desc *desc, u32 flag); -+#define default_msi_mask_irq __msi_mask_irq -+#define default_msix_mask_irq __msix_mask_irq - --struct msi_chip { -+struct msi_controller { - struct module *owner; - struct device *dev; - struct device_node *of_node; - struct list_head list; -+#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN -+ struct irq_domain *domain; -+#endif - -- int (*setup_irq)(struct msi_chip *chip, struct pci_dev *dev, -+ int (*setup_irq)(struct msi_controller *chip, struct pci_dev *dev, - struct msi_desc *desc); -- void (*teardown_irq)(struct msi_chip *chip, unsigned int irq); -+ int (*setup_irqs)(struct msi_controller *chip, struct pci_dev *dev, -+ int nvec, int type); -+ void (*teardown_irq)(struct msi_controller *chip, unsigned int irq); - }; - -+#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN -+ -+#include -+#include -+ -+struct irq_domain; -+struct irq_chip; -+struct device_node; -+struct msi_domain_info; -+ -+/** -+ * struct msi_domain_ops - MSI interrupt domain callbacks -+ * @get_hwirq: Retrieve the resulting hw irq number -+ * @msi_init: Domain specific init function for MSI interrupts -+ * @msi_free: Domain specific function to free a MSI interrupts -+ * @msi_check: Callback for verification of the domain/info/dev data -+ * @msi_prepare: Prepare the allocation of the interrupts in the domain -+ * @msi_finish: Optional callbacl to finalize the allocation -+ * @set_desc: Set the msi descriptor for an interrupt -+ * @handle_error: Optional error handler if the allocation fails -+ * -+ * @get_hwirq, @msi_init and @msi_free are callbacks used by -+ * msi_create_irq_domain() and related interfaces -+ * -+ * @msi_check, @msi_prepare, @msi_finish, @set_desc and @handle_error -+ * are callbacks used by msi_irq_domain_alloc_irqs() and related -+ * interfaces which are based on msi_desc. -+ */ -+struct msi_domain_ops { -+ irq_hw_number_t (*get_hwirq)(struct msi_domain_info *info, -+ msi_alloc_info_t *arg); -+ int (*msi_init)(struct irq_domain *domain, -+ struct msi_domain_info *info, -+ unsigned int virq, irq_hw_number_t hwirq, -+ msi_alloc_info_t *arg); -+ void (*msi_free)(struct irq_domain *domain, -+ struct msi_domain_info *info, -+ unsigned int virq); -+ int (*msi_check)(struct irq_domain *domain, -+ struct msi_domain_info *info, -+ struct device *dev); -+ int (*msi_prepare)(struct irq_domain *domain, -+ struct device *dev, int nvec, -+ msi_alloc_info_t *arg); -+ void (*msi_finish)(msi_alloc_info_t *arg, int retval); -+ void (*set_desc)(msi_alloc_info_t *arg, -+ struct msi_desc *desc); -+ int (*handle_error)(struct irq_domain *domain, -+ struct msi_desc *desc, int error); -+}; -+ -+/** -+ * struct msi_domain_info - MSI interrupt domain data -+ * @flags: Flags to decribe features and capabilities -+ * @ops: The callback data structure -+ * @chip: Optional: associated interrupt chip -+ * @chip_data: Optional: associated interrupt chip data -+ * @handler: Optional: associated interrupt flow handler -+ * @handler_data: Optional: associated interrupt flow handler data -+ * @handler_name: Optional: associated interrupt flow handler name -+ * @data: Optional: domain specific data -+ */ -+struct msi_domain_info { -+ u32 flags; -+ struct msi_domain_ops *ops; -+ struct irq_chip *chip; -+ void *chip_data; -+ irq_flow_handler_t handler; -+ void *handler_data; -+ const char *handler_name; -+ void *data; -+}; -+ -+/* Flags for msi_domain_info */ -+enum { -+ /* -+ * Init non implemented ops callbacks with default MSI domain -+ * callbacks. -+ */ -+ MSI_FLAG_USE_DEF_DOM_OPS = (1 << 0), -+ /* -+ * Init non implemented chip callbacks with default MSI chip -+ * callbacks. -+ */ -+ MSI_FLAG_USE_DEF_CHIP_OPS = (1 << 1), -+ /* Build identity map between hwirq and irq */ -+ MSI_FLAG_IDENTITY_MAP = (1 << 2), -+ /* Support multiple PCI MSI interrupts */ -+ MSI_FLAG_MULTI_PCI_MSI = (1 << 3), -+ /* Support PCI MSIX interrupts */ -+ MSI_FLAG_PCI_MSIX = (1 << 4), -+}; -+ -+int msi_domain_set_affinity(struct irq_data *data, const struct cpumask *mask, -+ bool force); -+ -+struct irq_domain *msi_create_irq_domain(struct device_node *of_node, -+ struct msi_domain_info *info, -+ struct irq_domain *parent); -+int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, -+ int nvec); -+void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev); -+struct msi_domain_info *msi_get_domain_info(struct irq_domain *domain); -+ -+#endif /* CONFIG_GENERIC_MSI_IRQ_DOMAIN */ -+ -+#ifdef CONFIG_PCI_MSI_IRQ_DOMAIN -+void pci_msi_domain_write_msg(struct irq_data *irq_data, struct msi_msg *msg); -+struct irq_domain *pci_msi_create_irq_domain(struct device_node *node, -+ struct msi_domain_info *info, -+ struct irq_domain *parent); -+int pci_msi_domain_alloc_irqs(struct irq_domain *domain, struct pci_dev *dev, -+ int nvec, int type); -+void pci_msi_domain_free_irqs(struct irq_domain *domain, struct pci_dev *dev); -+struct irq_domain *pci_msi_create_default_irq_domain(struct device_node *node, -+ struct msi_domain_info *info, struct irq_domain *parent); -+ -+irq_hw_number_t pci_msi_domain_calc_hwirq(struct pci_dev *dev, -+ struct msi_desc *desc); -+int pci_msi_domain_check_cap(struct irq_domain *domain, -+ struct msi_domain_info *info, struct device *dev); -+#endif /* CONFIG_PCI_MSI_IRQ_DOMAIN */ -+ - #endif /* LINUX_MSI_H */ -diff --git a/include/linux/of.h b/include/linux/of.h -index 4a6a489..25111fb 100644 ---- a/include/linux/of.h -+++ b/include/linux/of.h -@@ -57,7 +57,6 @@ struct device_node { - struct device_node *child; - struct device_node *sibling; - struct device_node *next; /* next device of same type */ -- struct device_node *allnext; /* next in list of all nodes */ - struct kobject kobj; - unsigned long _flags; - void *data; -@@ -109,7 +108,7 @@ static inline void of_node_put(struct device_node *node) { } - #ifdef CONFIG_OF - - /* Pointer for first entry in chain of all nodes. */ --extern struct device_node *of_allnodes; -+extern struct device_node *of_root; - extern struct device_node *of_chosen; - extern struct device_node *of_aliases; - extern struct device_node *of_stdout; -@@ -117,7 +116,7 @@ extern raw_spinlock_t devtree_lock; - - static inline bool of_have_populated_dt(void) - { -- return of_allnodes != NULL; -+ return of_root != NULL; - } - - static inline bool of_node_is_root(const struct device_node *node) -@@ -161,6 +160,7 @@ static inline void of_property_clear_flag(struct property *p, unsigned long flag - clear_bit(flag, &p->_flags); - } - -+extern struct device_node *__of_find_all_nodes(struct device_node *prev); - extern struct device_node *of_find_all_nodes(struct device_node *prev); - - /* -@@ -216,8 +216,9 @@ static inline const char *of_node_full_name(const struct device_node *np) - return np ? np->full_name : ""; - } - --#define for_each_of_allnodes(dn) \ -- for (dn = of_allnodes; dn; dn = dn->allnext) -+#define for_each_of_allnodes_from(from, dn) \ -+ for (dn = __of_find_all_nodes(from); dn; dn = __of_find_all_nodes(dn)) -+#define for_each_of_allnodes(dn) for_each_of_allnodes_from(NULL, dn) - extern struct device_node *of_find_node_by_name(struct device_node *from, - const char *name); - extern struct device_node *of_find_node_by_type(struct device_node *from, -diff --git a/include/linux/of_device.h b/include/linux/of_device.h -index ef37021..22801b1 100644 ---- a/include/linux/of_device.h -+++ b/include/linux/of_device.h -@@ -53,6 +53,7 @@ static inline struct device_node *of_cpu_device_node_get(int cpu) - return of_node_get(cpu_dev->of_node); - } - -+void of_dma_configure(struct device *dev, struct device_node *np); - #else /* CONFIG_OF */ - - static inline int of_driver_match_device(struct device *dev, -@@ -90,6 +91,8 @@ static inline struct device_node *of_cpu_device_node_get(int cpu) - { - return NULL; - } -+static inline void of_dma_configure(struct device *dev, struct device_node *np) -+{} - #endif /* CONFIG_OF */ - - #endif /* _LINUX_OF_DEVICE_H */ -diff --git a/include/linux/of_iommu.h b/include/linux/of_iommu.h -index 51a560f..ffbe470 100644 ---- a/include/linux/of_iommu.h -+++ b/include/linux/of_iommu.h -@@ -1,12 +1,20 @@ - #ifndef __OF_IOMMU_H - #define __OF_IOMMU_H - -+#include -+#include -+#include -+ - #ifdef CONFIG_OF_IOMMU - - extern int of_get_dma_window(struct device_node *dn, const char *prefix, - int index, unsigned long *busno, dma_addr_t *addr, - size_t *size); - -+extern void of_iommu_init(void); -+extern struct iommu_ops *of_iommu_configure(struct device *dev, -+ struct device_node *master_np); -+ - #else - - static inline int of_get_dma_window(struct device_node *dn, const char *prefix, -@@ -16,6 +24,23 @@ static inline int of_get_dma_window(struct device_node *dn, const char *prefix, - return -EINVAL; - } - -+static inline void of_iommu_init(void) { } -+static inline struct iommu_ops *of_iommu_configure(struct device *dev, -+ struct device_node *master_np) -+{ -+ return NULL; -+} -+ - #endif /* CONFIG_OF_IOMMU */ - -+void of_iommu_set_ops(struct device_node *np, struct iommu_ops *ops); -+struct iommu_ops *of_iommu_get_ops(struct device_node *np); -+ -+extern struct of_device_id __iommu_of_table; -+ -+typedef int (*of_iommu_init_fn)(struct device_node *); -+ -+#define IOMMU_OF_DECLARE(name, compat, fn) \ -+ _OF_DECLARE(iommu, name, compat, fn, of_iommu_init_fn) -+ - #endif /* __OF_IOMMU_H */ -diff --git a/include/linux/of_irq.h b/include/linux/of_irq.h -index bfec136..563ad28 100644 ---- a/include/linux/of_irq.h -+++ b/include/linux/of_irq.h -@@ -69,6 +69,7 @@ static inline int of_irq_get_byname(struct device_node *dev, const char *name) - */ - extern unsigned int irq_of_parse_and_map(struct device_node *node, int index); - extern struct device_node *of_irq_find_parent(struct device_node *child); -+extern void of_msi_configure(struct device *dev, struct device_node *np); - - #else /* !CONFIG_OF */ - static inline unsigned int irq_of_parse_and_map(struct device_node *dev, -diff --git a/include/linux/of_pci.h b/include/linux/of_pci.h -index 1fd207e..29fd3fe 100644 ---- a/include/linux/of_pci.h -+++ b/include/linux/of_pci.h -@@ -16,6 +16,7 @@ int of_pci_get_devfn(struct device_node *np); - int of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin); - int of_pci_parse_bus_range(struct device_node *node, struct resource *res); - int of_get_pci_domain_nr(struct device_node *node); -+void of_pci_dma_configure(struct pci_dev *pci_dev); - #else - static inline int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq) - { -@@ -50,6 +51,8 @@ of_get_pci_domain_nr(struct device_node *node) - { - return -1; - } -+ -+static inline void of_pci_dma_configure(struct pci_dev *pci_dev) { } - #endif - - #if defined(CONFIG_OF_ADDRESS) -@@ -59,13 +62,13 @@ int of_pci_get_host_bridge_resources(struct device_node *dev, - #endif - - #if defined(CONFIG_OF) && defined(CONFIG_PCI_MSI) --int of_pci_msi_chip_add(struct msi_chip *chip); --void of_pci_msi_chip_remove(struct msi_chip *chip); --struct msi_chip *of_pci_find_msi_chip_by_node(struct device_node *of_node); -+int of_pci_msi_chip_add(struct msi_controller *chip); -+void of_pci_msi_chip_remove(struct msi_controller *chip); -+struct msi_controller *of_pci_find_msi_chip_by_node(struct device_node *of_node); - #else --static inline int of_pci_msi_chip_add(struct msi_chip *chip) { return -EINVAL; } --static inline void of_pci_msi_chip_remove(struct msi_chip *chip) { } --static inline struct msi_chip * -+static inline int of_pci_msi_chip_add(struct msi_controller *chip) { return -EINVAL; } -+static inline void of_pci_msi_chip_remove(struct msi_controller *chip) { } -+static inline struct msi_controller * - of_pci_find_msi_chip_by_node(struct device_node *of_node) { return NULL; } - #endif - -diff --git a/include/linux/of_pdt.h b/include/linux/of_pdt.h -index c65a18a..7e09244 100644 ---- a/include/linux/of_pdt.h -+++ b/include/linux/of_pdt.h -@@ -39,7 +39,6 @@ extern void *prom_early_alloc(unsigned long size); - /* for building the device tree */ - extern void of_pdt_build_devicetree(phandle root_node, struct of_pdt_ops *ops); - --extern void (*of_pdt_build_more)(struct device_node *dp, -- struct device_node ***nextp); -+extern void (*of_pdt_build_more)(struct device_node *dp); - - #endif /* _LINUX_OF_PDT_H */ -diff --git a/include/linux/of_platform.h b/include/linux/of_platform.h -index c2b0627..8a860f0 100644 ---- a/include/linux/of_platform.h -+++ b/include/linux/of_platform.h -@@ -84,4 +84,10 @@ static inline int of_platform_populate(struct device_node *root, - static inline void of_platform_depopulate(struct device *parent) { } - #endif - -+#ifdef CONFIG_OF_DYNAMIC -+extern void of_platform_register_reconfig_notifier(void); -+#else -+static inline void of_platform_register_reconfig_notifier(void) { } -+#endif -+ - #endif /* _LINUX_OF_PLATFORM_H */ -diff --git a/include/linux/pci.h b/include/linux/pci.h -index 7a34844..f28c88b 100644 ---- a/include/linux/pci.h -+++ b/include/linux/pci.h -@@ -29,6 +29,7 @@ - #include - #include - #include -+#include - #include - - #include -@@ -171,8 +172,8 @@ enum pci_dev_flags { - PCI_DEV_FLAGS_ASSIGNED = (__force pci_dev_flags_t) (1 << 2), - /* Flag for quirk use to store if quirk-specific ACS is enabled */ - PCI_DEV_FLAGS_ACS_ENABLED_QUIRK = (__force pci_dev_flags_t) (1 << 3), -- /* Flag to indicate the device uses dma_alias_devfn */ -- PCI_DEV_FLAGS_DMA_ALIAS_DEVFN = (__force pci_dev_flags_t) (1 << 4), -+ /* Flag to indicate the device uses dma_alias_devid */ -+ PCI_DEV_FLAGS_DMA_ALIAS_DEVID = (__force pci_dev_flags_t) (1 << 4), - /* Use a PCIe-to-PCI bridge alias even if !pci_is_pcie */ - PCI_DEV_FLAG_PCIE_BRIDGE_ALIAS = (__force pci_dev_flags_t) (1 << 5), - /* Do not use bus resets for device */ -@@ -278,7 +279,7 @@ struct pci_dev { - u8 rom_base_reg; /* which config register controls the ROM */ - u8 pin; /* which interrupt pin this device uses */ - u16 pcie_flags_reg; /* cached PCIe Capabilities Register */ -- u8 dma_alias_devfn;/* devfn of DMA alias, if any */ -+ u32 dma_alias_devid;/* devid of DMA alias */ - - struct pci_driver *driver; /* which driver has allocated this device */ - u64 dma_mask; /* Mask of the bits of bus address this -@@ -365,7 +366,6 @@ struct pci_dev { - struct bin_attribute *res_attr[DEVICE_COUNT_RESOURCE]; /* sysfs file for resources */ - struct bin_attribute *res_attr_wc[DEVICE_COUNT_RESOURCE]; /* sysfs file for WC mapping of resources */ - #ifdef CONFIG_PCI_MSI -- struct list_head msi_list; - const struct attribute_group **msi_irq_groups; - #endif - struct pci_vpd *vpd; -@@ -400,16 +400,10 @@ static inline int pci_channel_offline(struct pci_dev *pdev) - return (pdev->error_state != pci_channel_io_normal); - } - --struct pci_host_bridge_window { -- struct list_head list; -- struct resource *res; /* host bridge aperture (CPU address) */ -- resource_size_t offset; /* bus address + offset = CPU address */ --}; -- - struct pci_host_bridge { - struct device dev; - struct pci_bus *bus; /* root bus */ -- struct list_head windows; /* pci_host_bridge_windows */ -+ struct list_head windows; /* resource_entry */ - void (*release_fn)(struct pci_host_bridge *); - void *release_data; - }; -@@ -456,7 +450,7 @@ struct pci_bus { - struct resource busn_res; /* bus numbers routed to this bus */ - - struct pci_ops *ops; /* configuration access functions */ -- struct msi_chip *msi; /* MSI controller */ -+ struct msi_controller *msi; /* MSI controller */ - void *sysdata; /* hook for sys-specific extension */ - struct proc_dir_entry *procdir; /* directory entry in /proc/bus/pci */ - -@@ -516,6 +510,9 @@ static inline struct pci_dev *pci_upstream_bridge(struct pci_dev *dev) - return dev->bus->self; - } - -+struct device *pci_get_host_bridge_device(struct pci_dev *dev); -+void pci_put_host_bridge_device(struct device *dev); -+ - #ifdef CONFIG_PCI_MSI - static inline bool pci_dev_msi_enabled(struct pci_dev *pci_dev) - { -@@ -565,6 +562,7 @@ static inline int pcibios_err_to_errno(int err) - /* Low-level architecture-dependent routines */ - - struct pci_ops { -+ void __iomem *(*map_bus)(struct pci_bus *bus, unsigned int devfn, int where); - int (*read)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val); - int (*write)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val); - }; -@@ -862,6 +860,16 @@ int pci_bus_write_config_word(struct pci_bus *bus, unsigned int devfn, - int where, u16 val); - int pci_bus_write_config_dword(struct pci_bus *bus, unsigned int devfn, - int where, u32 val); -+ -+int pci_generic_config_read(struct pci_bus *bus, unsigned int devfn, -+ int where, int size, u32 *val); -+int pci_generic_config_write(struct pci_bus *bus, unsigned int devfn, -+ int where, int size, u32 val); -+int pci_generic_config_read32(struct pci_bus *bus, unsigned int devfn, -+ int where, int size, u32 *val); -+int pci_generic_config_write32(struct pci_bus *bus, unsigned int devfn, -+ int where, int size, u32 val); -+ - struct pci_ops *pci_bus_set_ops(struct pci_bus *bus, struct pci_ops *ops); - - static inline int pci_read_config_byte(const struct pci_dev *dev, int where, u8 *val) -diff --git a/include/linux/phy.h b/include/linux/phy.h -index d090cfc..eda18a8 100644 ---- a/include/linux/phy.h -+++ b/include/linux/phy.h -@@ -700,6 +700,7 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, - struct phy_c45_device_ids *c45_ids); - struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45); - int phy_device_register(struct phy_device *phy); -+void phy_device_remove(struct phy_device *phydev); - int phy_init_hw(struct phy_device *phydev); - int phy_suspend(struct phy_device *phydev); - int phy_resume(struct phy_device *phydev); -diff --git a/include/linux/phy_fixed.h b/include/linux/phy_fixed.h -index f2ca1b4..fe5732d 100644 ---- a/include/linux/phy_fixed.h -+++ b/include/linux/phy_fixed.h -@@ -11,7 +11,7 @@ struct fixed_phy_status { - - struct device_node; - --#ifdef CONFIG_FIXED_PHY -+#if IS_ENABLED(CONFIG_FIXED_PHY) - extern int fixed_phy_add(unsigned int irq, int phy_id, - struct fixed_phy_status *status); - extern struct phy_device *fixed_phy_register(unsigned int irq, -@@ -21,6 +21,9 @@ extern void fixed_phy_del(int phy_addr); - extern int fixed_phy_set_link_update(struct phy_device *phydev, - int (*link_update)(struct net_device *, - struct fixed_phy_status *)); -+extern int fixed_phy_update_state(struct phy_device *phydev, -+ const struct fixed_phy_status *status, -+ const struct fixed_phy_status *changed); - #else - static inline int fixed_phy_add(unsigned int irq, int phy_id, - struct fixed_phy_status *status) -@@ -43,6 +46,12 @@ static inline int fixed_phy_set_link_update(struct phy_device *phydev, - { - return -ENODEV; - } -+static inline int fixed_phy_update_state(struct phy_device *phydev, -+ const struct fixed_phy_status *status, -+ const struct fixed_phy_status *changed) -+{ -+ return -ENODEV; -+} - #endif /* CONFIG_FIXED_PHY */ - - #endif /* __PHY_FIXED_H */ -diff --git a/include/linux/resource_ext.h b/include/linux/resource_ext.h -new file mode 100644 -index 0000000..e2bf63d ---- /dev/null -+++ b/include/linux/resource_ext.h -@@ -0,0 +1,77 @@ -+/* -+ * Copyright (C) 2015, Intel Corporation -+ * Author: Jiang Liu -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT -+ * 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_RESOURCE_EXT_H -+#define _LINUX_RESOURCE_EXT_H -+#include -+#include -+#include -+#include -+ -+/* Represent resource window for bridge devices */ -+struct resource_win { -+ struct resource res; /* In master (CPU) address space */ -+ resource_size_t offset; /* Translation offset for bridge */ -+}; -+ -+/* -+ * Common resource list management data structure and interfaces to support -+ * ACPI, PNP and PCI host bridge etc. -+ */ -+struct resource_entry { -+ struct list_head node; -+ struct resource *res; /* In master (CPU) address space */ -+ resource_size_t offset; /* Translation offset for bridge */ -+ struct resource __res; /* Default storage for res */ -+}; -+ -+extern struct resource_entry * -+resource_list_create_entry(struct resource *res, size_t extra_size); -+extern void resource_list_free(struct list_head *head); -+ -+static inline void resource_list_add(struct resource_entry *entry, -+ struct list_head *head) -+{ -+ list_add(&entry->node, head); -+} -+ -+static inline void resource_list_add_tail(struct resource_entry *entry, -+ struct list_head *head) -+{ -+ list_add_tail(&entry->node, head); -+} -+ -+static inline void resource_list_del(struct resource_entry *entry) -+{ -+ list_del(&entry->node); -+} -+ -+static inline void resource_list_free_entry(struct resource_entry *entry) -+{ -+ kfree(entry); -+} -+ -+static inline void -+resource_list_destroy_entry(struct resource_entry *entry) -+{ -+ resource_list_del(entry); -+ resource_list_free_entry(entry); -+} -+ -+#define resource_list_for_each_entry(entry, list) \ -+ list_for_each_entry((entry), (list), node) -+ -+#define resource_list_for_each_entry_safe(entry, tmp, list) \ -+ list_for_each_entry_safe((entry), (tmp), (list), node) -+ -+#endif /* _LINUX_RESOURCE_EXT_H */ -diff --git a/include/linux/splice.h b/include/linux/splice.h -index da2751d..2e0fca6 100644 ---- a/include/linux/splice.h -+++ b/include/linux/splice.h -@@ -83,4 +83,10 @@ extern void splice_shrink_spd(struct splice_pipe_desc *); - extern void spd_release_page(struct splice_pipe_desc *, unsigned int); - - extern const struct pipe_buf_operations page_cache_pipe_buf_ops; -+ -+extern long do_splice_from(struct pipe_inode_info *pipe, struct file *out, -+ loff_t *ppos, size_t len, unsigned int flags); -+extern long do_splice_to(struct file *in, loff_t *ppos, -+ struct pipe_inode_info *pipe, size_t len, -+ unsigned int flags); - #endif -diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h -index 9948c87..1d0043d 100644 ---- a/include/linux/usb/quirks.h -+++ b/include/linux/usb/quirks.h -@@ -47,4 +47,7 @@ - /* device generates spurious wakeup, ignore remote wakeup capability */ - #define USB_QUIRK_IGNORE_REMOTE_WAKEUP BIT(9) - -+/* device can't handle Link Power Management */ -+#define USB_QUIRK_NO_LPM BIT(10) -+ - #endif /* __LINUX_USB_QUIRKS_H */ -diff --git a/include/trace/events/iommu.h b/include/trace/events/iommu.h -index a8f5c32..2c7befb 100644 ---- a/include/trace/events/iommu.h -+++ b/include/trace/events/iommu.h -@@ -83,7 +83,7 @@ DEFINE_EVENT(iommu_device_event, detach_device_from_domain, - TP_ARGS(dev) - ); - --DECLARE_EVENT_CLASS(iommu_map_unmap, -+TRACE_EVENT(map, - - TP_PROTO(unsigned long iova, phys_addr_t paddr, size_t size), - -@@ -92,7 +92,7 @@ DECLARE_EVENT_CLASS(iommu_map_unmap, - TP_STRUCT__entry( - __field(u64, iova) - __field(u64, paddr) -- __field(int, size) -+ __field(size_t, size) - ), - - TP_fast_assign( -@@ -101,26 +101,31 @@ DECLARE_EVENT_CLASS(iommu_map_unmap, - __entry->size = size; - ), - -- TP_printk("IOMMU: iova=0x%016llx paddr=0x%016llx size=0x%x", -+ TP_printk("IOMMU: iova=0x%016llx paddr=0x%016llx size=%zu", - __entry->iova, __entry->paddr, __entry->size - ) - ); - --DEFINE_EVENT(iommu_map_unmap, map, -+TRACE_EVENT(unmap, - -- TP_PROTO(unsigned long iova, phys_addr_t paddr, size_t size), -- -- TP_ARGS(iova, paddr, size) --); -+ TP_PROTO(unsigned long iova, size_t size, size_t unmapped_size), - --DEFINE_EVENT_PRINT(iommu_map_unmap, unmap, -+ TP_ARGS(iova, size, unmapped_size), - -- TP_PROTO(unsigned long iova, phys_addr_t paddr, size_t size), -+ TP_STRUCT__entry( -+ __field(u64, iova) -+ __field(size_t, size) -+ __field(size_t, unmapped_size) -+ ), - -- TP_ARGS(iova, paddr, size), -+ TP_fast_assign( -+ __entry->iova = iova; -+ __entry->size = size; -+ __entry->unmapped_size = unmapped_size; -+ ), - -- TP_printk("IOMMU: iova=0x%016llx size=0x%x", -- __entry->iova, __entry->size -+ TP_printk("IOMMU: iova=0x%016llx size=%zu unmapped_size=%zu", -+ __entry->iova, __entry->size, __entry->unmapped_size - ) - ); - -diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild -index 8523f9b..11f8f74 100644 ---- a/include/uapi/linux/Kbuild -+++ b/include/uapi/linux/Kbuild -@@ -56,6 +56,7 @@ header-y += atmppp.h - header-y += atmsap.h - header-y += atmsvc.h - header-y += audit.h -+header-y += aufs_type.h - header-y += auto_fs.h - header-y += auto_fs4.h - header-y += auxvec.h -diff --git a/include/uapi/linux/aufs_type.h b/include/uapi/linux/aufs_type.h -new file mode 100644 -index 0000000..75915f8 ---- /dev/null -+++ b/include/uapi/linux/aufs_type.h -@@ -0,0 +1,419 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+#ifndef __AUFS_TYPE_H__ -+#define __AUFS_TYPE_H__ -+ -+#define AUFS_NAME "aufs" -+ -+#ifdef __KERNEL__ -+/* -+ * define it before including all other headers. -+ * sched.h may use pr_* macros before defining "current", so define the -+ * no-current version first, and re-define later. -+ */ -+#define pr_fmt(fmt) AUFS_NAME " %s:%d: " fmt, __func__, __LINE__ -+#include -+#undef pr_fmt -+#define pr_fmt(fmt) \ -+ AUFS_NAME " %s:%d:%.*s[%d]: " fmt, __func__, __LINE__, \ -+ (int)sizeof(current->comm), current->comm, current->pid -+#else -+#include -+#include -+#endif /* __KERNEL__ */ -+ -+#include -+ -+#define AUFS_VERSION "3.18.25+-20160509" -+ -+/* todo? move this to linux-2.6.19/include/magic.h */ -+#define AUFS_SUPER_MAGIC ('a' << 24 | 'u' << 16 | 'f' << 8 | 's') -+ -+/* ---------------------------------------------------------------------- */ -+ -+#ifdef CONFIG_AUFS_BRANCH_MAX_127 -+typedef int8_t aufs_bindex_t; -+#define AUFS_BRANCH_MAX 127 -+#else -+typedef int16_t aufs_bindex_t; -+#ifdef CONFIG_AUFS_BRANCH_MAX_511 -+#define AUFS_BRANCH_MAX 511 -+#elif defined(CONFIG_AUFS_BRANCH_MAX_1023) -+#define AUFS_BRANCH_MAX 1023 -+#elif defined(CONFIG_AUFS_BRANCH_MAX_32767) -+#define AUFS_BRANCH_MAX 32767 -+#endif -+#endif -+ -+#ifdef __KERNEL__ -+#ifndef AUFS_BRANCH_MAX -+#error unknown CONFIG_AUFS_BRANCH_MAX value -+#endif -+#endif /* __KERNEL__ */ -+ -+/* ---------------------------------------------------------------------- */ -+ -+#define AUFS_FSTYPE AUFS_NAME -+ -+#define AUFS_ROOT_INO 2 -+#define AUFS_FIRST_INO 11 -+ -+#define AUFS_WH_PFX ".wh." -+#define AUFS_WH_PFX_LEN ((int)sizeof(AUFS_WH_PFX) - 1) -+#define AUFS_WH_TMP_LEN 4 -+/* a limit for rmdir/rename a dir and copyup */ -+#define AUFS_MAX_NAMELEN (NAME_MAX \ -+ - AUFS_WH_PFX_LEN * 2 /* doubly whiteouted */\ -+ - 1 /* dot */\ -+ - AUFS_WH_TMP_LEN) /* hex */ -+#define AUFS_XINO_FNAME "." AUFS_NAME ".xino" -+#define AUFS_XINO_DEFPATH "/tmp/" AUFS_XINO_FNAME -+#define AUFS_XINO_DEF_SEC 30 /* seconds */ -+#define AUFS_XINO_DEF_TRUNC 45 /* percentage */ -+#define AUFS_DIRWH_DEF 3 -+#define AUFS_RDCACHE_DEF 10 /* seconds */ -+#define AUFS_RDCACHE_MAX 3600 /* seconds */ -+#define AUFS_RDBLK_DEF 512 /* bytes */ -+#define AUFS_RDHASH_DEF 32 -+#define AUFS_WKQ_NAME AUFS_NAME "d" -+#define AUFS_MFS_DEF_SEC 30 /* seconds */ -+#define AUFS_MFS_MAX_SEC 3600 /* seconds */ -+#define AUFS_FHSM_CACHE_DEF_SEC 30 /* seconds */ -+#define AUFS_PLINK_WARN 50 /* number of plinks in a single bucket */ -+ -+/* pseudo-link maintenace under /proc */ -+#define AUFS_PLINK_MAINT_NAME "plink_maint" -+#define AUFS_PLINK_MAINT_DIR "fs/" AUFS_NAME -+#define AUFS_PLINK_MAINT_PATH AUFS_PLINK_MAINT_DIR "/" AUFS_PLINK_MAINT_NAME -+ -+#define AUFS_DIROPQ_NAME AUFS_WH_PFX ".opq" /* whiteouted doubly */ -+#define AUFS_WH_DIROPQ AUFS_WH_PFX AUFS_DIROPQ_NAME -+ -+#define AUFS_BASE_NAME AUFS_WH_PFX AUFS_NAME -+#define AUFS_PLINKDIR_NAME AUFS_WH_PFX "plnk" -+#define AUFS_ORPHDIR_NAME AUFS_WH_PFX "orph" -+ -+/* doubly whiteouted */ -+#define AUFS_WH_BASE AUFS_WH_PFX AUFS_BASE_NAME -+#define AUFS_WH_PLINKDIR AUFS_WH_PFX AUFS_PLINKDIR_NAME -+#define AUFS_WH_ORPHDIR AUFS_WH_PFX AUFS_ORPHDIR_NAME -+ -+/* branch permissions and attributes */ -+#define AUFS_BRPERM_RW "rw" -+#define AUFS_BRPERM_RO "ro" -+#define AUFS_BRPERM_RR "rr" -+#define AUFS_BRATTR_COO_REG "coo_reg" -+#define AUFS_BRATTR_COO_ALL "coo_all" -+#define AUFS_BRATTR_FHSM "fhsm" -+#define AUFS_BRATTR_UNPIN "unpin" -+#define AUFS_BRATTR_ICEX "icex" -+#define AUFS_BRATTR_ICEX_SEC "icexsec" -+#define AUFS_BRATTR_ICEX_SYS "icexsys" -+#define AUFS_BRATTR_ICEX_TR "icextr" -+#define AUFS_BRATTR_ICEX_USR "icexusr" -+#define AUFS_BRATTR_ICEX_OTH "icexoth" -+#define AUFS_BRRATTR_WH "wh" -+#define AUFS_BRWATTR_NLWH "nolwh" -+#define AUFS_BRWATTR_MOO "moo" -+ -+#define AuBrPerm_RW 1 /* writable, hardlinkable wh */ -+#define AuBrPerm_RO (1 << 1) /* readonly */ -+#define AuBrPerm_RR (1 << 2) /* natively readonly */ -+#define AuBrPerm_Mask (AuBrPerm_RW | AuBrPerm_RO | AuBrPerm_RR) -+ -+#define AuBrAttr_COO_REG (1 << 3) /* copy-up on open */ -+#define AuBrAttr_COO_ALL (1 << 4) -+#define AuBrAttr_COO_Mask (AuBrAttr_COO_REG | AuBrAttr_COO_ALL) -+ -+#define AuBrAttr_FHSM (1 << 5) /* file-based hsm */ -+#define AuBrAttr_UNPIN (1 << 6) /* rename-able top dir of -+ branch. meaningless since -+ linux-3.18-rc1 */ -+ -+/* ignore error in copying XATTR */ -+#define AuBrAttr_ICEX_SEC (1 << 7) -+#define AuBrAttr_ICEX_SYS (1 << 8) -+#define AuBrAttr_ICEX_TR (1 << 9) -+#define AuBrAttr_ICEX_USR (1 << 10) -+#define AuBrAttr_ICEX_OTH (1 << 11) -+#define AuBrAttr_ICEX (AuBrAttr_ICEX_SEC \ -+ | AuBrAttr_ICEX_SYS \ -+ | AuBrAttr_ICEX_TR \ -+ | AuBrAttr_ICEX_USR \ -+ | AuBrAttr_ICEX_OTH) -+ -+#define AuBrRAttr_WH (1 << 12) /* whiteout-able */ -+#define AuBrRAttr_Mask AuBrRAttr_WH -+ -+#define AuBrWAttr_NoLinkWH (1 << 13) /* un-hardlinkable whiteouts */ -+#define AuBrWAttr_MOO (1 << 14) /* move-up on open */ -+#define AuBrWAttr_Mask (AuBrWAttr_NoLinkWH | AuBrWAttr_MOO) -+ -+#define AuBrAttr_CMOO_Mask (AuBrAttr_COO_Mask | AuBrWAttr_MOO) -+ -+/* #warning test userspace */ -+#ifdef __KERNEL__ -+#ifndef CONFIG_AUFS_FHSM -+#undef AuBrAttr_FHSM -+#define AuBrAttr_FHSM 0 -+#endif -+#ifndef CONFIG_AUFS_XATTR -+#undef AuBrAttr_ICEX -+#define AuBrAttr_ICEX 0 -+#undef AuBrAttr_ICEX_SEC -+#define AuBrAttr_ICEX_SEC 0 -+#undef AuBrAttr_ICEX_SYS -+#define AuBrAttr_ICEX_SYS 0 -+#undef AuBrAttr_ICEX_TR -+#define AuBrAttr_ICEX_TR 0 -+#undef AuBrAttr_ICEX_USR -+#define AuBrAttr_ICEX_USR 0 -+#undef AuBrAttr_ICEX_OTH -+#define AuBrAttr_ICEX_OTH 0 -+#endif -+#endif -+ -+/* the longest combination */ -+/* AUFS_BRATTR_ICEX and AUFS_BRATTR_ICEX_TR don't affect here */ -+#define AuBrPermStrSz sizeof(AUFS_BRPERM_RW \ -+ "+" AUFS_BRATTR_COO_REG \ -+ "+" AUFS_BRATTR_FHSM \ -+ "+" AUFS_BRATTR_UNPIN \ -+ "+" AUFS_BRATTR_ICEX_SEC \ -+ "+" AUFS_BRATTR_ICEX_SYS \ -+ "+" AUFS_BRATTR_ICEX_USR \ -+ "+" AUFS_BRATTR_ICEX_OTH \ -+ "+" AUFS_BRWATTR_NLWH) -+ -+typedef struct { -+ char a[AuBrPermStrSz]; -+} au_br_perm_str_t; -+ -+static inline int au_br_writable(int brperm) -+{ -+ return brperm & AuBrPerm_RW; -+} -+ -+static inline int au_br_whable(int brperm) -+{ -+ return brperm & (AuBrPerm_RW | AuBrRAttr_WH); -+} -+ -+static inline int au_br_wh_linkable(int brperm) -+{ -+ return !(brperm & AuBrWAttr_NoLinkWH); -+} -+ -+static inline int au_br_cmoo(int brperm) -+{ -+ return brperm & AuBrAttr_CMOO_Mask; -+} -+ -+static inline int au_br_fhsm(int brperm) -+{ -+ return brperm & AuBrAttr_FHSM; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* ioctl */ -+enum { -+ /* readdir in userspace */ -+ AuCtl_RDU, -+ AuCtl_RDU_INO, -+ -+ AuCtl_WBR_FD, /* pathconf wrapper */ -+ AuCtl_IBUSY, /* busy inode */ -+ AuCtl_MVDOWN, /* move-down */ -+ AuCtl_BR, /* info about branches */ -+ AuCtl_FHSM_FD /* connection for fhsm */ -+}; -+ -+/* borrowed from linux/include/linux/kernel.h */ -+#ifndef ALIGN -+#define ALIGN(x, a) __ALIGN_MASK(x, (typeof(x))(a)-1) -+#define __ALIGN_MASK(x, mask) (((x)+(mask))&~(mask)) -+#endif -+ -+/* borrowed from linux/include/linux/compiler-gcc3.h */ -+#ifndef __aligned -+#define __aligned(x) __attribute__((aligned(x))) -+#endif -+ -+#ifdef __KERNEL__ -+#ifndef __packed -+#define __packed __attribute__((packed)) -+#endif -+#endif -+ -+struct au_rdu_cookie { -+ uint64_t h_pos; -+ int16_t bindex; -+ uint8_t flags; -+ uint8_t pad; -+ uint32_t generation; -+} __aligned(8); -+ -+struct au_rdu_ent { -+ uint64_t ino; -+ int16_t bindex; -+ uint8_t type; -+ uint8_t nlen; -+ uint8_t wh; -+ char name[0]; -+} __aligned(8); -+ -+static inline int au_rdu_len(int nlen) -+{ -+ /* include the terminating NULL */ -+ return ALIGN(sizeof(struct au_rdu_ent) + nlen + 1, -+ sizeof(uint64_t)); -+} -+ -+union au_rdu_ent_ul { -+ struct au_rdu_ent __user *e; -+ uint64_t ul; -+}; -+ -+enum { -+ AufsCtlRduV_SZ, -+ AufsCtlRduV_End -+}; -+ -+struct aufs_rdu { -+ /* input */ -+ union { -+ uint64_t sz; /* AuCtl_RDU */ -+ uint64_t nent; /* AuCtl_RDU_INO */ -+ }; -+ union au_rdu_ent_ul ent; -+ uint16_t verify[AufsCtlRduV_End]; -+ -+ /* input/output */ -+ uint32_t blk; -+ -+ /* output */ -+ union au_rdu_ent_ul tail; -+ /* number of entries which were added in a single call */ -+ uint64_t rent; -+ uint8_t full; -+ uint8_t shwh; -+ -+ struct au_rdu_cookie cookie; -+} __aligned(8); -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct aufs_wbr_fd { -+ uint32_t oflags; -+ int16_t brid; -+} __aligned(8); -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct aufs_ibusy { -+ uint64_t ino, h_ino; -+ int16_t bindex; -+} __aligned(8); -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* error code for move-down */ -+/* the actual message strings are implemented in aufs-util.git */ -+enum { -+ EAU_MVDOWN_OPAQUE = 1, -+ EAU_MVDOWN_WHITEOUT, -+ EAU_MVDOWN_UPPER, -+ EAU_MVDOWN_BOTTOM, -+ EAU_MVDOWN_NOUPPER, -+ EAU_MVDOWN_NOLOWERBR, -+ EAU_Last -+}; -+ -+/* flags for move-down */ -+#define AUFS_MVDOWN_DMSG 1 -+#define AUFS_MVDOWN_OWLOWER (1 << 1) /* overwrite lower */ -+#define AUFS_MVDOWN_KUPPER (1 << 2) /* keep upper */ -+#define AUFS_MVDOWN_ROLOWER (1 << 3) /* do even if lower is RO */ -+#define AUFS_MVDOWN_ROLOWER_R (1 << 4) /* did on lower RO */ -+#define AUFS_MVDOWN_ROUPPER (1 << 5) /* do even if upper is RO */ -+#define AUFS_MVDOWN_ROUPPER_R (1 << 6) /* did on upper RO */ -+#define AUFS_MVDOWN_BRID_UPPER (1 << 7) /* upper brid */ -+#define AUFS_MVDOWN_BRID_LOWER (1 << 8) /* lower brid */ -+#define AUFS_MVDOWN_FHSM_LOWER (1 << 9) /* find fhsm attr for lower */ -+#define AUFS_MVDOWN_STFS (1 << 10) /* req. stfs */ -+#define AUFS_MVDOWN_STFS_FAILED (1 << 11) /* output: stfs is unusable */ -+#define AUFS_MVDOWN_BOTTOM (1 << 12) /* output: no more lowers */ -+ -+/* index for move-down */ -+enum { -+ AUFS_MVDOWN_UPPER, -+ AUFS_MVDOWN_LOWER, -+ AUFS_MVDOWN_NARRAY -+}; -+ -+/* -+ * additional info of move-down -+ * number of free blocks and inodes. -+ * subset of struct kstatfs, but smaller and always 64bit. -+ */ -+struct aufs_stfs { -+ uint64_t f_blocks; -+ uint64_t f_bavail; -+ uint64_t f_files; -+ uint64_t f_ffree; -+}; -+ -+struct aufs_stbr { -+ int16_t brid; /* optional input */ -+ int16_t bindex; /* output */ -+ struct aufs_stfs stfs; /* output when AUFS_MVDOWN_STFS set */ -+} __aligned(8); -+ -+struct aufs_mvdown { -+ uint32_t flags; /* input/output */ -+ struct aufs_stbr stbr[AUFS_MVDOWN_NARRAY]; /* input/output */ -+ int8_t au_errno; /* output */ -+} __aligned(8); -+ -+/* ---------------------------------------------------------------------- */ -+ -+union aufs_brinfo { -+ /* PATH_MAX may differ between kernel-space and user-space */ -+ char _spacer[4096]; -+ struct { -+ int16_t id; -+ int perm; -+ char path[0]; -+ }; -+} __aligned(8); -+ -+/* ---------------------------------------------------------------------- */ -+ -+#define AuCtlType 'A' -+#define AUFS_CTL_RDU _IOWR(AuCtlType, AuCtl_RDU, struct aufs_rdu) -+#define AUFS_CTL_RDU_INO _IOWR(AuCtlType, AuCtl_RDU_INO, struct aufs_rdu) -+#define AUFS_CTL_WBR_FD _IOW(AuCtlType, AuCtl_WBR_FD, \ -+ struct aufs_wbr_fd) -+#define AUFS_CTL_IBUSY _IOWR(AuCtlType, AuCtl_IBUSY, struct aufs_ibusy) -+#define AUFS_CTL_MVDOWN _IOWR(AuCtlType, AuCtl_MVDOWN, \ -+ struct aufs_mvdown) -+#define AUFS_CTL_BRINFO _IOW(AuCtlType, AuCtl_BR, union aufs_brinfo) -+#define AUFS_CTL_FHSM_FD _IOW(AuCtlType, AuCtl_FHSM_FD, int) -+ -+#endif /* __AUFS_TYPE_H__ */ -diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h -index 29715d2..6eca3ee 100644 ---- a/include/uapi/linux/vfio.h -+++ b/include/uapi/linux/vfio.h -@@ -160,6 +160,10 @@ struct vfio_device_info { - __u32 flags; - #define VFIO_DEVICE_FLAGS_RESET (1 << 0) /* Device supports reset */ - #define VFIO_DEVICE_FLAGS_PCI (1 << 1) /* vfio-pci device */ -+#define VFIO_DEVICE_FLAGS_PLATFORM (1 << 2) /* vfio-platform device */ -+#define VFIO_DEVICE_FLAGS_AMBA (1 << 3) /* vfio-amba device */ -+#define VFIO_DEVICE_FLAGS_FSL_MC (1 << 4) /* vfio Freescale MC device */ -+ - __u32 num_regions; /* Max region index + 1 */ - __u32 num_irqs; /* Max IRQ index + 1 */ - }; -@@ -404,6 +408,7 @@ struct vfio_iommu_type1_dma_map { - __u32 flags; - #define VFIO_DMA_MAP_FLAG_READ (1 << 0) /* readable from device */ - #define VFIO_DMA_MAP_FLAG_WRITE (1 << 1) /* writable from device */ -+#define VFIO_DMA_MAP_FLAG_MMIO (1 << 2) /* non-cachable device region */ - __u64 vaddr; /* Process virtual address */ - __u64 iova; /* IO virtual address */ - __u64 size; /* Size of mapping (bytes) */ -diff --git a/kernel/fork.c b/kernel/fork.c -index 0a4f601..67ecb91 100644 ---- a/kernel/fork.c -+++ b/kernel/fork.c -@@ -430,7 +430,7 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) - struct inode *inode = file_inode(file); - struct address_space *mapping = file->f_mapping; - -- get_file(file); -+ vma_get_file(tmp); - if (tmp->vm_flags & VM_DENYWRITE) - atomic_dec(&inode->i_writecount); - mutex_lock(&mapping->i_mmap_mutex); -diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig -index 225086b..9a76e3b 100644 ---- a/kernel/irq/Kconfig -+++ b/kernel/irq/Kconfig -@@ -55,6 +55,21 @@ config GENERIC_IRQ_CHIP - config IRQ_DOMAIN - bool - -+# Support for hierarchical irq domains -+config IRQ_DOMAIN_HIERARCHY -+ bool -+ select IRQ_DOMAIN -+ -+# Generic MSI interrupt support -+config GENERIC_MSI_IRQ -+ bool -+ -+# Generic MSI hierarchical interrupt domain support -+config GENERIC_MSI_IRQ_DOMAIN -+ bool -+ select IRQ_DOMAIN_HIERARCHY -+ select GENERIC_MSI_IRQ -+ - config HANDLE_DOMAIN_IRQ - bool - -diff --git a/kernel/irq/Makefile b/kernel/irq/Makefile -index fff1738..d121235 100644 ---- a/kernel/irq/Makefile -+++ b/kernel/irq/Makefile -@@ -6,3 +6,4 @@ obj-$(CONFIG_IRQ_DOMAIN) += irqdomain.o - obj-$(CONFIG_PROC_FS) += proc.o - obj-$(CONFIG_GENERIC_PENDING_IRQ) += migration.o - obj-$(CONFIG_PM_SLEEP) += pm.o -+obj-$(CONFIG_GENERIC_MSI_IRQ) += msi.o -diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c -index e5202f0..55dd2fb 100644 ---- a/kernel/irq/chip.c -+++ b/kernel/irq/chip.c -@@ -15,6 +15,7 @@ - #include - #include - #include -+#include - - #include - -@@ -178,6 +179,7 @@ int irq_startup(struct irq_desc *desc, bool resend) - irq_state_clr_disabled(desc); - desc->depth = 0; - -+ irq_domain_activate_irq(&desc->irq_data); - if (desc->irq_data.chip->irq_startup) { - ret = desc->irq_data.chip->irq_startup(&desc->irq_data); - irq_state_clr_masked(desc); -@@ -199,6 +201,7 @@ void irq_shutdown(struct irq_desc *desc) - desc->irq_data.chip->irq_disable(&desc->irq_data); - else - desc->irq_data.chip->irq_mask(&desc->irq_data); -+ irq_domain_deactivate_irq(&desc->irq_data); - irq_state_set_masked(desc); - } - -@@ -728,7 +731,30 @@ __irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained, - if (!handle) { - handle = handle_bad_irq; - } else { -- if (WARN_ON(desc->irq_data.chip == &no_irq_chip)) -+ struct irq_data *irq_data = &desc->irq_data; -+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY -+ /* -+ * With hierarchical domains we might run into a -+ * situation where the outermost chip is not yet set -+ * up, but the inner chips are there. Instead of -+ * bailing we install the handler, but obviously we -+ * cannot enable/startup the interrupt at this point. -+ */ -+ while (irq_data) { -+ if (irq_data->chip != &no_irq_chip) -+ break; -+ /* -+ * Bail out if the outer chip is not set up -+ * and the interrrupt supposed to be started -+ * right away. -+ */ -+ if (WARN_ON(is_chained)) -+ goto out; -+ /* Try the parent */ -+ irq_data = irq_data->parent_data; -+ } -+#endif -+ if (WARN_ON(!irq_data || irq_data->chip == &no_irq_chip)) - goto out; - } - -@@ -847,3 +873,138 @@ void irq_cpu_offline(void) - raw_spin_unlock_irqrestore(&desc->lock, flags); - } - } -+ -+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY -+/** -+ * irq_chip_ack_parent - Acknowledge the parent interrupt -+ * @data: Pointer to interrupt specific data -+ */ -+void irq_chip_ack_parent(struct irq_data *data) -+{ -+ data = data->parent_data; -+ data->chip->irq_ack(data); -+} -+ -+/** -+ * irq_chip_mask_parent - Mask the parent interrupt -+ * @data: Pointer to interrupt specific data -+ */ -+void irq_chip_mask_parent(struct irq_data *data) -+{ -+ data = data->parent_data; -+ data->chip->irq_mask(data); -+} -+ -+/** -+ * irq_chip_unmask_parent - Unmask the parent interrupt -+ * @data: Pointer to interrupt specific data -+ */ -+void irq_chip_unmask_parent(struct irq_data *data) -+{ -+ data = data->parent_data; -+ data->chip->irq_unmask(data); -+} -+ -+/** -+ * irq_chip_eoi_parent - Invoke EOI on the parent interrupt -+ * @data: Pointer to interrupt specific data -+ */ -+void irq_chip_eoi_parent(struct irq_data *data) -+{ -+ data = data->parent_data; -+ data->chip->irq_eoi(data); -+} -+ -+/** -+ * irq_chip_set_affinity_parent - Set affinity on the parent interrupt -+ * @data: Pointer to interrupt specific data -+ * @dest: The affinity mask to set -+ * @force: Flag to enforce setting (disable online checks) -+ * -+ * Conditinal, as the underlying parent chip might not implement it. -+ */ -+int irq_chip_set_affinity_parent(struct irq_data *data, -+ const struct cpumask *dest, bool force) -+{ -+ data = data->parent_data; -+ if (data->chip->irq_set_affinity) -+ return data->chip->irq_set_affinity(data, dest, force); -+ -+ return -ENOSYS; -+} -+ -+/** -+ * irq_chip_set_type_parent - Set IRQ type on the parent interrupt -+ * @data: Pointer to interrupt specific data -+ * @type: IRQ_TYPE_{LEVEL,EDGE}_* value - see include/linux/irq.h -+ * -+ * Conditional, as the underlying parent chip might not implement it. -+ */ -+int irq_chip_set_type_parent(struct irq_data *data, unsigned int type) -+{ -+ data = data->parent_data; -+ -+ if (data->chip->irq_set_type) -+ return data->chip->irq_set_type(data, type); -+ -+ return -ENOSYS; -+} -+ -+/** -+ * irq_chip_retrigger_hierarchy - Retrigger an interrupt in hardware -+ * @data: Pointer to interrupt specific data -+ * -+ * Iterate through the domain hierarchy of the interrupt and check -+ * whether a hw retrigger function exists. If yes, invoke it. -+ */ -+int irq_chip_retrigger_hierarchy(struct irq_data *data) -+{ -+ for (data = data->parent_data; data; data = data->parent_data) -+ if (data->chip && data->chip->irq_retrigger) -+ return data->chip->irq_retrigger(data); -+ -+ return -ENOSYS; -+} -+ -+/** -+ * irq_chip_set_wake_parent - Set/reset wake-up on the parent interrupt -+ * @data: Pointer to interrupt specific data -+ * @on: Whether to set or reset the wake-up capability of this irq -+ * -+ * Conditional, as the underlying parent chip might not implement it. -+ */ -+int irq_chip_set_wake_parent(struct irq_data *data, unsigned int on) -+{ -+ data = data->parent_data; -+ if (data->chip->irq_set_wake) -+ return data->chip->irq_set_wake(data, on); -+ -+ return -ENOSYS; -+} -+#endif -+ -+/** -+ * irq_chip_compose_msi_msg - Componse msi message for a irq chip -+ * @data: Pointer to interrupt specific data -+ * @msg: Pointer to the MSI message -+ * -+ * For hierarchical domains we find the first chip in the hierarchy -+ * which implements the irq_compose_msi_msg callback. For non -+ * hierarchical we use the top level chip. -+ */ -+int irq_chip_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) -+{ -+ struct irq_data *pos = NULL; -+ -+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY -+ for (; data; data = data->parent_data) -+#endif -+ if (data->chip && data->chip->irq_compose_msi_msg) -+ pos = data; -+ if (!pos) -+ return -ENOSYS; -+ -+ pos->chip->irq_compose_msi_msg(pos, msg); -+ -+ return 0; -+} -diff --git a/kernel/irq/generic-chip.c b/kernel/irq/generic-chip.c -index cf80e7b..61024e8 100644 ---- a/kernel/irq/generic-chip.c -+++ b/kernel/irq/generic-chip.c -@@ -39,7 +39,7 @@ void irq_gc_mask_disable_reg(struct irq_data *d) - u32 mask = d->mask; - - irq_gc_lock(gc); -- irq_reg_writel(mask, gc->reg_base + ct->regs.disable); -+ irq_reg_writel(gc, mask, ct->regs.disable); - *ct->mask_cache &= ~mask; - irq_gc_unlock(gc); - } -@@ -59,7 +59,7 @@ void irq_gc_mask_set_bit(struct irq_data *d) - - irq_gc_lock(gc); - *ct->mask_cache |= mask; -- irq_reg_writel(*ct->mask_cache, gc->reg_base + ct->regs.mask); -+ irq_reg_writel(gc, *ct->mask_cache, ct->regs.mask); - irq_gc_unlock(gc); - } - EXPORT_SYMBOL_GPL(irq_gc_mask_set_bit); -@@ -79,7 +79,7 @@ void irq_gc_mask_clr_bit(struct irq_data *d) - - irq_gc_lock(gc); - *ct->mask_cache &= ~mask; -- irq_reg_writel(*ct->mask_cache, gc->reg_base + ct->regs.mask); -+ irq_reg_writel(gc, *ct->mask_cache, ct->regs.mask); - irq_gc_unlock(gc); - } - EXPORT_SYMBOL_GPL(irq_gc_mask_clr_bit); -@@ -98,7 +98,7 @@ void irq_gc_unmask_enable_reg(struct irq_data *d) - u32 mask = d->mask; - - irq_gc_lock(gc); -- irq_reg_writel(mask, gc->reg_base + ct->regs.enable); -+ irq_reg_writel(gc, mask, ct->regs.enable); - *ct->mask_cache |= mask; - irq_gc_unlock(gc); - } -@@ -114,7 +114,7 @@ void irq_gc_ack_set_bit(struct irq_data *d) - u32 mask = d->mask; - - irq_gc_lock(gc); -- irq_reg_writel(mask, gc->reg_base + ct->regs.ack); -+ irq_reg_writel(gc, mask, ct->regs.ack); - irq_gc_unlock(gc); - } - EXPORT_SYMBOL_GPL(irq_gc_ack_set_bit); -@@ -130,7 +130,7 @@ void irq_gc_ack_clr_bit(struct irq_data *d) - u32 mask = ~d->mask; - - irq_gc_lock(gc); -- irq_reg_writel(mask, gc->reg_base + ct->regs.ack); -+ irq_reg_writel(gc, mask, ct->regs.ack); - irq_gc_unlock(gc); - } - -@@ -145,8 +145,8 @@ void irq_gc_mask_disable_reg_and_ack(struct irq_data *d) - u32 mask = d->mask; - - irq_gc_lock(gc); -- irq_reg_writel(mask, gc->reg_base + ct->regs.mask); -- irq_reg_writel(mask, gc->reg_base + ct->regs.ack); -+ irq_reg_writel(gc, mask, ct->regs.mask); -+ irq_reg_writel(gc, mask, ct->regs.ack); - irq_gc_unlock(gc); - } - -@@ -161,7 +161,7 @@ void irq_gc_eoi(struct irq_data *d) - u32 mask = d->mask; - - irq_gc_lock(gc); -- irq_reg_writel(mask, gc->reg_base + ct->regs.eoi); -+ irq_reg_writel(gc, mask, ct->regs.eoi); - irq_gc_unlock(gc); - } - -@@ -191,6 +191,16 @@ int irq_gc_set_wake(struct irq_data *d, unsigned int on) - return 0; - } - -+static u32 irq_readl_be(void __iomem *addr) -+{ -+ return ioread32be(addr); -+} -+ -+static void irq_writel_be(u32 val, void __iomem *addr) -+{ -+ iowrite32be(val, addr); -+} -+ - static void - irq_init_generic_chip(struct irq_chip_generic *gc, const char *name, - int num_ct, unsigned int irq_base, -@@ -245,7 +255,7 @@ irq_gc_init_mask_cache(struct irq_chip_generic *gc, enum irq_gc_flags flags) - } - ct[i].mask_cache = mskptr; - if (flags & IRQ_GC_INIT_MASK_CACHE) -- *mskptr = irq_reg_readl(gc->reg_base + mskreg); -+ *mskptr = irq_reg_readl(gc, mskreg); - } - } - -@@ -300,7 +310,13 @@ int irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip, - dgc->gc[i] = gc = tmp; - irq_init_generic_chip(gc, name, num_ct, i * irqs_per_chip, - NULL, handler); -+ - gc->domain = d; -+ if (gcflags & IRQ_GC_BE_IO) { -+ gc->reg_readl = &irq_readl_be; -+ gc->reg_writel = &irq_writel_be; -+ } -+ - raw_spin_lock_irqsave(&gc_lock, flags); - list_add_tail(&gc->list, &gc_list); - raw_spin_unlock_irqrestore(&gc_lock, flags); -diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c -index 6534ff6..021f823 100644 ---- a/kernel/irq/irqdomain.c -+++ b/kernel/irq/irqdomain.c -@@ -23,6 +23,10 @@ static DEFINE_MUTEX(irq_domain_mutex); - static DEFINE_MUTEX(revmap_trees_mutex); - static struct irq_domain *irq_default_domain; - -+static int irq_domain_alloc_descs(int virq, unsigned int nr_irqs, -+ irq_hw_number_t hwirq, int node); -+static void irq_domain_check_hierarchy(struct irq_domain *domain); -+ - /** - * __irq_domain_add() - Allocate a new irq_domain data structure - * @of_node: optional device-tree node of the interrupt controller -@@ -30,7 +34,7 @@ static struct irq_domain *irq_default_domain; - * @hwirq_max: Maximum number of interrupts supported by controller - * @direct_max: Maximum value of direct maps; Use ~0 for no limit; 0 for no - * direct mapping -- * @ops: map/unmap domain callbacks -+ * @ops: domain callbacks - * @host_data: Controller private data pointer - * - * Allocates and initialize and irq_domain structure. -@@ -56,6 +60,7 @@ struct irq_domain *__irq_domain_add(struct device_node *of_node, int size, - domain->hwirq_max = hwirq_max; - domain->revmap_size = size; - domain->revmap_direct_max_irq = direct_max; -+ irq_domain_check_hierarchy(domain); - - mutex_lock(&irq_domain_mutex); - list_add(&domain->link, &irq_domain_list); -@@ -109,7 +114,7 @@ EXPORT_SYMBOL_GPL(irq_domain_remove); - * @first_irq: first number of irq block assigned to the domain, - * pass zero to assign irqs on-the-fly. If first_irq is non-zero, then - * pre-map all of the irqs in the domain to virqs starting at first_irq. -- * @ops: map/unmap domain callbacks -+ * @ops: domain callbacks - * @host_data: Controller private data pointer - * - * Allocates an irq_domain, and optionally if first_irq is positive then also -@@ -174,20 +179,20 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node, - - domain = __irq_domain_add(of_node, first_hwirq + size, - first_hwirq + size, 0, ops, host_data); -- if (!domain) -- return NULL; -- -- irq_domain_associate_many(domain, first_irq, first_hwirq, size); -+ if (domain) -+ irq_domain_associate_many(domain, first_irq, first_hwirq, size); - - return domain; - } - EXPORT_SYMBOL_GPL(irq_domain_add_legacy); - - /** -- * irq_find_host() - Locates a domain for a given device node -+ * irq_find_matching_host() - Locates a domain for a given device node - * @node: device-tree node of the interrupt controller -+ * @bus_token: domain-specific data - */ --struct irq_domain *irq_find_host(struct device_node *node) -+struct irq_domain *irq_find_matching_host(struct device_node *node, -+ enum irq_domain_bus_token bus_token) - { - struct irq_domain *h, *found = NULL; - int rc; -@@ -196,13 +201,19 @@ struct irq_domain *irq_find_host(struct device_node *node) - * it might potentially be set to match all interrupts in - * the absence of a device node. This isn't a problem so far - * yet though... -+ * -+ * bus_token == DOMAIN_BUS_ANY matches any domain, any other -+ * values must generate an exact match for the domain to be -+ * selected. - */ - mutex_lock(&irq_domain_mutex); - list_for_each_entry(h, &irq_domain_list, link) { - if (h->ops->match) -- rc = h->ops->match(h, node); -+ rc = h->ops->match(h, node, bus_token); - else -- rc = (h->of_node != NULL) && (h->of_node == node); -+ rc = ((h->of_node != NULL) && (h->of_node == node) && -+ ((bus_token == DOMAIN_BUS_ANY) || -+ (h->bus_token == bus_token))); - - if (rc) { - found = h; -@@ -212,7 +223,7 @@ struct irq_domain *irq_find_host(struct device_node *node) - mutex_unlock(&irq_domain_mutex); - return found; - } --EXPORT_SYMBOL_GPL(irq_find_host); -+EXPORT_SYMBOL_GPL(irq_find_matching_host); - - /** - * irq_set_default_host() - Set a "default" irq domain -@@ -388,7 +399,6 @@ EXPORT_SYMBOL_GPL(irq_create_direct_mapping); - unsigned int irq_create_mapping(struct irq_domain *domain, - irq_hw_number_t hwirq) - { -- unsigned int hint; - int virq; - - pr_debug("irq_create_mapping(0x%p, 0x%lx)\n", domain, hwirq); -@@ -410,12 +420,8 @@ unsigned int irq_create_mapping(struct irq_domain *domain, - } - - /* Allocate a virtual interrupt number */ -- hint = hwirq % nr_irqs; -- if (hint == 0) -- hint++; -- virq = irq_alloc_desc_from(hint, of_node_to_nid(domain->of_node)); -- if (virq <= 0) -- virq = irq_alloc_desc_from(1, of_node_to_nid(domain->of_node)); -+ virq = irq_domain_alloc_descs(-1, 1, hwirq, -+ of_node_to_nid(domain->of_node)); - if (virq <= 0) { - pr_debug("-> virq allocation failed\n"); - return 0; -@@ -471,7 +477,7 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data) - struct irq_domain *domain; - irq_hw_number_t hwirq; - unsigned int type = IRQ_TYPE_NONE; -- unsigned int virq; -+ int virq; - - domain = irq_data->np ? irq_find_host(irq_data->np) : irq_default_domain; - if (!domain) { -@@ -489,10 +495,24 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data) - return 0; - } - -- /* Create mapping */ -- virq = irq_create_mapping(domain, hwirq); -- if (!virq) -- return virq; -+ if (irq_domain_is_hierarchy(domain)) { -+ /* -+ * If we've already configured this interrupt, -+ * don't do it again, or hell will break loose. -+ */ -+ virq = irq_find_mapping(domain, hwirq); -+ if (virq) -+ return virq; -+ -+ virq = irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, irq_data); -+ if (virq <= 0) -+ return 0; -+ } else { -+ /* Create mapping */ -+ virq = irq_create_mapping(domain, hwirq); -+ if (!virq) -+ return virq; -+ } - - /* Set type if specified and different than the current one */ - if (type != IRQ_TYPE_NONE && -@@ -540,8 +560,8 @@ unsigned int irq_find_mapping(struct irq_domain *domain, - return 0; - - if (hwirq < domain->revmap_direct_max_irq) { -- data = irq_get_irq_data(hwirq); -- if (data && (data->domain == domain) && (data->hwirq == hwirq)) -+ data = irq_domain_get_irq_data(domain, hwirq); -+ if (data && data->hwirq == hwirq) - return hwirq; - } - -@@ -709,3 +729,518 @@ const struct irq_domain_ops irq_domain_simple_ops = { - .xlate = irq_domain_xlate_onetwocell, - }; - EXPORT_SYMBOL_GPL(irq_domain_simple_ops); -+ -+static int irq_domain_alloc_descs(int virq, unsigned int cnt, -+ irq_hw_number_t hwirq, int node) -+{ -+ unsigned int hint; -+ -+ if (virq >= 0) { -+ virq = irq_alloc_descs(virq, virq, cnt, node); -+ } else { -+ hint = hwirq % nr_irqs; -+ if (hint == 0) -+ hint++; -+ virq = irq_alloc_descs_from(hint, cnt, node); -+ if (virq <= 0 && hint > 1) -+ virq = irq_alloc_descs_from(1, cnt, node); -+ } -+ -+ return virq; -+} -+ -+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY -+/** -+ * irq_domain_add_hierarchy - Add a irqdomain into the hierarchy -+ * @parent: Parent irq domain to associate with the new domain -+ * @flags: Irq domain flags associated to the domain -+ * @size: Size of the domain. See below -+ * @node: Optional device-tree node of the interrupt controller -+ * @ops: Pointer to the interrupt domain callbacks -+ * @host_data: Controller private data pointer -+ * -+ * If @size is 0 a tree domain is created, otherwise a linear domain. -+ * -+ * If successful the parent is associated to the new domain and the -+ * domain flags are set. -+ * Returns pointer to IRQ domain, or NULL on failure. -+ */ -+struct irq_domain *irq_domain_add_hierarchy(struct irq_domain *parent, -+ unsigned int flags, -+ unsigned int size, -+ struct device_node *node, -+ const struct irq_domain_ops *ops, -+ void *host_data) -+{ -+ struct irq_domain *domain; -+ -+ if (size) -+ domain = irq_domain_add_linear(node, size, ops, host_data); -+ else -+ domain = irq_domain_add_tree(node, ops, host_data); -+ if (domain) { -+ domain->parent = parent; -+ domain->flags |= flags; -+ } -+ -+ return domain; -+} -+ -+static void irq_domain_insert_irq(int virq) -+{ -+ struct irq_data *data; -+ -+ for (data = irq_get_irq_data(virq); data; data = data->parent_data) { -+ struct irq_domain *domain = data->domain; -+ irq_hw_number_t hwirq = data->hwirq; -+ -+ if (hwirq < domain->revmap_size) { -+ domain->linear_revmap[hwirq] = virq; -+ } else { -+ mutex_lock(&revmap_trees_mutex); -+ radix_tree_insert(&domain->revmap_tree, hwirq, data); -+ mutex_unlock(&revmap_trees_mutex); -+ } -+ -+ /* If not already assigned, give the domain the chip's name */ -+ if (!domain->name && data->chip) -+ domain->name = data->chip->name; -+ } -+ -+ irq_clear_status_flags(virq, IRQ_NOREQUEST); -+} -+ -+static void irq_domain_remove_irq(int virq) -+{ -+ struct irq_data *data; -+ -+ irq_set_status_flags(virq, IRQ_NOREQUEST); -+ irq_set_chip_and_handler(virq, NULL, NULL); -+ synchronize_irq(virq); -+ smp_mb(); -+ -+ for (data = irq_get_irq_data(virq); data; data = data->parent_data) { -+ struct irq_domain *domain = data->domain; -+ irq_hw_number_t hwirq = data->hwirq; -+ -+ if (hwirq < domain->revmap_size) { -+ domain->linear_revmap[hwirq] = 0; -+ } else { -+ mutex_lock(&revmap_trees_mutex); -+ radix_tree_delete(&domain->revmap_tree, hwirq); -+ mutex_unlock(&revmap_trees_mutex); -+ } -+ } -+} -+ -+static struct irq_data *irq_domain_insert_irq_data(struct irq_domain *domain, -+ struct irq_data *child) -+{ -+ struct irq_data *irq_data; -+ -+ irq_data = kzalloc_node(sizeof(*irq_data), GFP_KERNEL, child->node); -+ if (irq_data) { -+ child->parent_data = irq_data; -+ irq_data->irq = child->irq; -+ irq_data->node = child->node; -+ irq_data->domain = domain; -+ } -+ -+ return irq_data; -+} -+ -+static void irq_domain_free_irq_data(unsigned int virq, unsigned int nr_irqs) -+{ -+ struct irq_data *irq_data, *tmp; -+ int i; -+ -+ for (i = 0; i < nr_irqs; i++) { -+ irq_data = irq_get_irq_data(virq + i); -+ tmp = irq_data->parent_data; -+ irq_data->parent_data = NULL; -+ irq_data->domain = NULL; -+ -+ while (tmp) { -+ irq_data = tmp; -+ tmp = tmp->parent_data; -+ kfree(irq_data); -+ } -+ } -+} -+ -+static int irq_domain_alloc_irq_data(struct irq_domain *domain, -+ unsigned int virq, unsigned int nr_irqs) -+{ -+ struct irq_data *irq_data; -+ struct irq_domain *parent; -+ int i; -+ -+ /* The outermost irq_data is embedded in struct irq_desc */ -+ for (i = 0; i < nr_irqs; i++) { -+ irq_data = irq_get_irq_data(virq + i); -+ irq_data->domain = domain; -+ -+ for (parent = domain->parent; parent; parent = parent->parent) { -+ irq_data = irq_domain_insert_irq_data(parent, irq_data); -+ if (!irq_data) { -+ irq_domain_free_irq_data(virq, i + 1); -+ return -ENOMEM; -+ } -+ } -+ } -+ -+ return 0; -+} -+ -+/** -+ * irq_domain_get_irq_data - Get irq_data associated with @virq and @domain -+ * @domain: domain to match -+ * @virq: IRQ number to get irq_data -+ */ -+struct irq_data *irq_domain_get_irq_data(struct irq_domain *domain, -+ unsigned int virq) -+{ -+ struct irq_data *irq_data; -+ -+ for (irq_data = irq_get_irq_data(virq); irq_data; -+ irq_data = irq_data->parent_data) -+ if (irq_data->domain == domain) -+ return irq_data; -+ -+ return NULL; -+} -+ -+/** -+ * irq_domain_set_hwirq_and_chip - Set hwirq and irqchip of @virq at @domain -+ * @domain: Interrupt domain to match -+ * @virq: IRQ number -+ * @hwirq: The hwirq number -+ * @chip: The associated interrupt chip -+ * @chip_data: The associated chip data -+ */ -+int irq_domain_set_hwirq_and_chip(struct irq_domain *domain, unsigned int virq, -+ irq_hw_number_t hwirq, struct irq_chip *chip, -+ void *chip_data) -+{ -+ struct irq_data *irq_data = irq_domain_get_irq_data(domain, virq); -+ -+ if (!irq_data) -+ return -ENOENT; -+ -+ irq_data->hwirq = hwirq; -+ irq_data->chip = chip ? chip : &no_irq_chip; -+ irq_data->chip_data = chip_data; -+ -+ return 0; -+} -+ -+/** -+ * irq_domain_set_info - Set the complete data for a @virq in @domain -+ * @domain: Interrupt domain to match -+ * @virq: IRQ number -+ * @hwirq: The hardware interrupt number -+ * @chip: The associated interrupt chip -+ * @chip_data: The associated interrupt chip data -+ * @handler: The interrupt flow handler -+ * @handler_data: The interrupt flow handler data -+ * @handler_name: The interrupt handler name -+ */ -+void irq_domain_set_info(struct irq_domain *domain, unsigned int virq, -+ irq_hw_number_t hwirq, struct irq_chip *chip, -+ void *chip_data, irq_flow_handler_t handler, -+ void *handler_data, const char *handler_name) -+{ -+ irq_domain_set_hwirq_and_chip(domain, virq, hwirq, chip, chip_data); -+ __irq_set_handler(virq, handler, 0, handler_name); -+ irq_set_handler_data(virq, handler_data); -+} -+ -+/** -+ * irq_domain_reset_irq_data - Clear hwirq, chip and chip_data in @irq_data -+ * @irq_data: The pointer to irq_data -+ */ -+void irq_domain_reset_irq_data(struct irq_data *irq_data) -+{ -+ irq_data->hwirq = 0; -+ irq_data->chip = &no_irq_chip; -+ irq_data->chip_data = NULL; -+} -+ -+/** -+ * irq_domain_free_irqs_common - Clear irq_data and free the parent -+ * @domain: Interrupt domain to match -+ * @virq: IRQ number to start with -+ * @nr_irqs: The number of irqs to free -+ */ -+void irq_domain_free_irqs_common(struct irq_domain *domain, unsigned int virq, -+ unsigned int nr_irqs) -+{ -+ struct irq_data *irq_data; -+ int i; -+ -+ for (i = 0; i < nr_irqs; i++) { -+ irq_data = irq_domain_get_irq_data(domain, virq + i); -+ if (irq_data) -+ irq_domain_reset_irq_data(irq_data); -+ } -+ irq_domain_free_irqs_parent(domain, virq, nr_irqs); -+} -+ -+/** -+ * irq_domain_free_irqs_top - Clear handler and handler data, clear irqdata and free parent -+ * @domain: Interrupt domain to match -+ * @virq: IRQ number to start with -+ * @nr_irqs: The number of irqs to free -+ */ -+void irq_domain_free_irqs_top(struct irq_domain *domain, unsigned int virq, -+ unsigned int nr_irqs) -+{ -+ int i; -+ -+ for (i = 0; i < nr_irqs; i++) { -+ irq_set_handler_data(virq + i, NULL); -+ irq_set_handler(virq + i, NULL); -+ } -+ irq_domain_free_irqs_common(domain, virq, nr_irqs); -+} -+ -+static bool irq_domain_is_auto_recursive(struct irq_domain *domain) -+{ -+ return domain->flags & IRQ_DOMAIN_FLAG_AUTO_RECURSIVE; -+} -+ -+static void irq_domain_free_irqs_recursive(struct irq_domain *domain, -+ unsigned int irq_base, -+ unsigned int nr_irqs) -+{ -+ domain->ops->free(domain, irq_base, nr_irqs); -+ if (irq_domain_is_auto_recursive(domain)) { -+ BUG_ON(!domain->parent); -+ irq_domain_free_irqs_recursive(domain->parent, irq_base, -+ nr_irqs); -+ } -+} -+ -+static int irq_domain_alloc_irqs_recursive(struct irq_domain *domain, -+ unsigned int irq_base, -+ unsigned int nr_irqs, void *arg) -+{ -+ int ret = 0; -+ struct irq_domain *parent = domain->parent; -+ bool recursive = irq_domain_is_auto_recursive(domain); -+ -+ BUG_ON(recursive && !parent); -+ if (recursive) -+ ret = irq_domain_alloc_irqs_recursive(parent, irq_base, -+ nr_irqs, arg); -+ if (ret >= 0) -+ ret = domain->ops->alloc(domain, irq_base, nr_irqs, arg); -+ if (ret < 0 && recursive) -+ irq_domain_free_irqs_recursive(parent, irq_base, nr_irqs); -+ -+ return ret; -+} -+ -+/** -+ * __irq_domain_alloc_irqs - Allocate IRQs from domain -+ * @domain: domain to allocate from -+ * @irq_base: allocate specified IRQ nubmer if irq_base >= 0 -+ * @nr_irqs: number of IRQs to allocate -+ * @node: NUMA node id for memory allocation -+ * @arg: domain specific argument -+ * @realloc: IRQ descriptors have already been allocated if true -+ * -+ * Allocate IRQ numbers and initialized all data structures to support -+ * hierarchy IRQ domains. -+ * Parameter @realloc is mainly to support legacy IRQs. -+ * Returns error code or allocated IRQ number -+ * -+ * The whole process to setup an IRQ has been split into two steps. -+ * The first step, __irq_domain_alloc_irqs(), is to allocate IRQ -+ * descriptor and required hardware resources. The second step, -+ * irq_domain_activate_irq(), is to program hardwares with preallocated -+ * resources. In this way, it's easier to rollback when failing to -+ * allocate resources. -+ */ -+int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base, -+ unsigned int nr_irqs, int node, void *arg, -+ bool realloc) -+{ -+ int i, ret, virq; -+ -+ if (domain == NULL) { -+ domain = irq_default_domain; -+ if (WARN(!domain, "domain is NULL; cannot allocate IRQ\n")) -+ return -EINVAL; -+ } -+ -+ if (!domain->ops->alloc) { -+ pr_debug("domain->ops->alloc() is NULL\n"); -+ return -ENOSYS; -+ } -+ -+ if (realloc && irq_base >= 0) { -+ virq = irq_base; -+ } else { -+ virq = irq_domain_alloc_descs(irq_base, nr_irqs, 0, node); -+ if (virq < 0) { -+ pr_debug("cannot allocate IRQ(base %d, count %d)\n", -+ irq_base, nr_irqs); -+ return virq; -+ } -+ } -+ -+ if (irq_domain_alloc_irq_data(domain, virq, nr_irqs)) { -+ pr_debug("cannot allocate memory for IRQ%d\n", virq); -+ ret = -ENOMEM; -+ goto out_free_desc; -+ } -+ -+ mutex_lock(&irq_domain_mutex); -+ ret = irq_domain_alloc_irqs_recursive(domain, virq, nr_irqs, arg); -+ if (ret < 0) { -+ mutex_unlock(&irq_domain_mutex); -+ goto out_free_irq_data; -+ } -+ for (i = 0; i < nr_irqs; i++) -+ irq_domain_insert_irq(virq + i); -+ mutex_unlock(&irq_domain_mutex); -+ -+ return virq; -+ -+out_free_irq_data: -+ irq_domain_free_irq_data(virq, nr_irqs); -+out_free_desc: -+ irq_free_descs(virq, nr_irqs); -+ return ret; -+} -+ -+/** -+ * irq_domain_free_irqs - Free IRQ number and associated data structures -+ * @virq: base IRQ number -+ * @nr_irqs: number of IRQs to free -+ */ -+void irq_domain_free_irqs(unsigned int virq, unsigned int nr_irqs) -+{ -+ struct irq_data *data = irq_get_irq_data(virq); -+ int i; -+ -+ if (WARN(!data || !data->domain || !data->domain->ops->free, -+ "NULL pointer, cannot free irq\n")) -+ return; -+ -+ mutex_lock(&irq_domain_mutex); -+ for (i = 0; i < nr_irqs; i++) -+ irq_domain_remove_irq(virq + i); -+ irq_domain_free_irqs_recursive(data->domain, virq, nr_irqs); -+ mutex_unlock(&irq_domain_mutex); -+ -+ irq_domain_free_irq_data(virq, nr_irqs); -+ irq_free_descs(virq, nr_irqs); -+} -+ -+/** -+ * irq_domain_alloc_irqs_parent - Allocate interrupts from parent domain -+ * @irq_base: Base IRQ number -+ * @nr_irqs: Number of IRQs to allocate -+ * @arg: Allocation data (arch/domain specific) -+ * -+ * Check whether the domain has been setup recursive. If not allocate -+ * through the parent domain. -+ */ -+int irq_domain_alloc_irqs_parent(struct irq_domain *domain, -+ unsigned int irq_base, unsigned int nr_irqs, -+ void *arg) -+{ -+ /* irq_domain_alloc_irqs_recursive() has called parent's alloc() */ -+ if (irq_domain_is_auto_recursive(domain)) -+ return 0; -+ -+ domain = domain->parent; -+ if (domain) -+ return irq_domain_alloc_irqs_recursive(domain, irq_base, -+ nr_irqs, arg); -+ return -ENOSYS; -+} -+ -+/** -+ * irq_domain_free_irqs_parent - Free interrupts from parent domain -+ * @irq_base: Base IRQ number -+ * @nr_irqs: Number of IRQs to free -+ * -+ * Check whether the domain has been setup recursive. If not free -+ * through the parent domain. -+ */ -+void irq_domain_free_irqs_parent(struct irq_domain *domain, -+ unsigned int irq_base, unsigned int nr_irqs) -+{ -+ /* irq_domain_free_irqs_recursive() will call parent's free */ -+ if (!irq_domain_is_auto_recursive(domain) && domain->parent) -+ irq_domain_free_irqs_recursive(domain->parent, irq_base, -+ nr_irqs); -+} -+ -+/** -+ * irq_domain_activate_irq - Call domain_ops->activate recursively to activate -+ * interrupt -+ * @irq_data: outermost irq_data associated with interrupt -+ * -+ * This is the second step to call domain_ops->activate to program interrupt -+ * controllers, so the interrupt could actually get delivered. -+ */ -+void irq_domain_activate_irq(struct irq_data *irq_data) -+{ -+ if (irq_data && irq_data->domain) { -+ struct irq_domain *domain = irq_data->domain; -+ -+ if (irq_data->parent_data) -+ irq_domain_activate_irq(irq_data->parent_data); -+ if (domain->ops->activate) -+ domain->ops->activate(domain, irq_data); -+ } -+} -+ -+/** -+ * irq_domain_deactivate_irq - Call domain_ops->deactivate recursively to -+ * deactivate interrupt -+ * @irq_data: outermost irq_data associated with interrupt -+ * -+ * It calls domain_ops->deactivate to program interrupt controllers to disable -+ * interrupt delivery. -+ */ -+void irq_domain_deactivate_irq(struct irq_data *irq_data) -+{ -+ if (irq_data && irq_data->domain) { -+ struct irq_domain *domain = irq_data->domain; -+ -+ if (domain->ops->deactivate) -+ domain->ops->deactivate(domain, irq_data); -+ if (irq_data->parent_data) -+ irq_domain_deactivate_irq(irq_data->parent_data); -+ } -+} -+ -+static void irq_domain_check_hierarchy(struct irq_domain *domain) -+{ -+ /* Hierarchy irq_domains must implement callback alloc() */ -+ if (domain->ops->alloc) -+ domain->flags |= IRQ_DOMAIN_FLAG_HIERARCHY; -+} -+#else /* CONFIG_IRQ_DOMAIN_HIERARCHY */ -+/** -+ * irq_domain_get_irq_data - Get irq_data associated with @virq and @domain -+ * @domain: domain to match -+ * @virq: IRQ number to get irq_data -+ */ -+struct irq_data *irq_domain_get_irq_data(struct irq_domain *domain, -+ unsigned int virq) -+{ -+ struct irq_data *irq_data = irq_get_irq_data(virq); -+ -+ return (irq_data && irq_data->domain == domain) ? irq_data : NULL; -+} -+ -+static void irq_domain_check_hierarchy(struct irq_domain *domain) -+{ -+} -+#endif /* CONFIG_IRQ_DOMAIN_HIERARCHY */ -diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c -index 0a9104b..acb401f 100644 ---- a/kernel/irq/manage.c -+++ b/kernel/irq/manage.c -@@ -183,6 +183,7 @@ int irq_do_set_affinity(struct irq_data *data, const struct cpumask *mask, - ret = chip->irq_set_affinity(data, mask, force); - switch (ret) { - case IRQ_SET_MASK_OK: -+ case IRQ_SET_MASK_OK_DONE: - cpumask_copy(data->affinity, mask); - case IRQ_SET_MASK_OK_NOCOPY: - irq_set_thread_affinity(desc); -@@ -600,6 +601,7 @@ int __irq_set_trigger(struct irq_desc *desc, unsigned int irq, - - switch (ret) { - case IRQ_SET_MASK_OK: -+ case IRQ_SET_MASK_OK_DONE: - irqd_clear(&desc->irq_data, IRQD_TRIGGER_MASK); - irqd_set(&desc->irq_data, flags); - -@@ -1756,3 +1758,94 @@ int request_percpu_irq(unsigned int irq, irq_handler_t handler, - - return retval; - } -+ -+/** -+ * irq_get_irqchip_state - returns the irqchip state of a interrupt. -+ * @irq: Interrupt line that is forwarded to a VM -+ * @which: One of IRQCHIP_STATE_* the caller wants to know about -+ * @state: a pointer to a boolean where the state is to be storeed -+ * -+ * This call snapshots the internal irqchip state of an -+ * interrupt, returning into @state the bit corresponding to -+ * stage @which -+ * -+ * This function should be called with preemption disabled if the -+ * interrupt controller has per-cpu registers. -+ */ -+int irq_get_irqchip_state(unsigned int irq, enum irqchip_irq_state which, -+ bool *state) -+{ -+ struct irq_desc *desc; -+ struct irq_data *data; -+ struct irq_chip *chip; -+ unsigned long flags; -+ int err = -EINVAL; -+ -+ desc = irq_get_desc_buslock(irq, &flags, 0); -+ if (!desc) -+ return err; -+ -+ data = irq_desc_get_irq_data(desc); -+ -+ do { -+ chip = irq_data_get_irq_chip(data); -+ if (chip->irq_get_irqchip_state) -+ break; -+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY -+ data = data->parent_data; -+#else -+ data = NULL; -+#endif -+ } while (data); -+ -+ if (data) -+ err = chip->irq_get_irqchip_state(data, which, state); -+ -+ irq_put_desc_busunlock(desc, flags); -+ return err; -+} -+ -+/** -+ * irq_set_irqchip_state - set the state of a forwarded interrupt. -+ * @irq: Interrupt line that is forwarded to a VM -+ * @which: State to be restored (one of IRQCHIP_STATE_*) -+ * @val: Value corresponding to @which -+ * -+ * This call sets the internal irqchip state of an interrupt, -+ * depending on the value of @which. -+ * -+ * This function should be called with preemption disabled if the -+ * interrupt controller has per-cpu registers. -+ */ -+int irq_set_irqchip_state(unsigned int irq, enum irqchip_irq_state which, -+ bool val) -+{ -+ struct irq_desc *desc; -+ struct irq_data *data; -+ struct irq_chip *chip; -+ unsigned long flags; -+ int err = -EINVAL; -+ -+ desc = irq_get_desc_buslock(irq, &flags, 0); -+ if (!desc) -+ return err; -+ -+ data = irq_desc_get_irq_data(desc); -+ -+ do { -+ chip = irq_data_get_irq_chip(data); -+ if (chip->irq_set_irqchip_state) -+ break; -+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY -+ data = data->parent_data; -+#else -+ data = NULL; -+#endif -+ } while (data); -+ -+ if (data) -+ err = chip->irq_set_irqchip_state(data, which, val); -+ -+ irq_put_desc_busunlock(desc, flags); -+ return err; -+} -diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c -new file mode 100644 -index 0000000..54433c2 ---- /dev/null -+++ b/kernel/irq/msi.c -@@ -0,0 +1,356 @@ -+/* -+ * linux/kernel/irq/msi.c -+ * -+ * Copyright (C) 2014 Intel Corp. -+ * Author: Jiang Liu -+ * -+ * This file is licensed under GPLv2. -+ * -+ * This file contains common code to support Message Signalled Interrupt for -+ * PCI compatible and non PCI compatible devices. -+ */ -+#include -+#include -+#include -+#include -+#include -+ -+/* Temparory solution for building, will be removed later */ -+#include -+ -+struct msi_desc *alloc_msi_entry(struct device *dev) -+{ -+ struct msi_desc *desc = kzalloc(sizeof(*desc), GFP_KERNEL); -+ if (!desc) -+ return NULL; -+ -+ INIT_LIST_HEAD(&desc->list); -+ desc->dev = dev; -+ -+ return desc; -+} -+ -+void free_msi_entry(struct msi_desc *entry) -+{ -+ kfree(entry); -+} -+ -+void __get_cached_msi_msg(struct msi_desc *entry, struct msi_msg *msg) -+{ -+ *msg = entry->msg; -+} -+ -+void get_cached_msi_msg(unsigned int irq, struct msi_msg *msg) -+{ -+ struct msi_desc *entry = irq_get_msi_desc(irq); -+ -+ __get_cached_msi_msg(entry, msg); -+} -+EXPORT_SYMBOL_GPL(get_cached_msi_msg); -+ -+#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN -+static inline void irq_chip_write_msi_msg(struct irq_data *data, -+ struct msi_msg *msg) -+{ -+ data->chip->irq_write_msi_msg(data, msg); -+} -+ -+/** -+ * msi_domain_set_affinity - Generic affinity setter function for MSI domains -+ * @irq_data: The irq data associated to the interrupt -+ * @mask: The affinity mask to set -+ * @force: Flag to enforce setting (disable online checks) -+ * -+ * Intended to be used by MSI interrupt controllers which are -+ * implemented with hierarchical domains. -+ */ -+int msi_domain_set_affinity(struct irq_data *irq_data, -+ const struct cpumask *mask, bool force) -+{ -+ struct irq_data *parent = irq_data->parent_data; -+ struct msi_msg msg; -+ int ret; -+ -+ ret = parent->chip->irq_set_affinity(parent, mask, force); -+ if (ret >= 0 && ret != IRQ_SET_MASK_OK_DONE) { -+ BUG_ON(irq_chip_compose_msi_msg(irq_data, &msg)); -+ irq_chip_write_msi_msg(irq_data, &msg); -+ } -+ -+ return ret; -+} -+ -+static void msi_domain_activate(struct irq_domain *domain, -+ struct irq_data *irq_data) -+{ -+ struct msi_msg msg; -+ -+ BUG_ON(irq_chip_compose_msi_msg(irq_data, &msg)); -+ irq_chip_write_msi_msg(irq_data, &msg); -+} -+ -+static void msi_domain_deactivate(struct irq_domain *domain, -+ struct irq_data *irq_data) -+{ -+ struct msi_msg msg; -+ -+ memset(&msg, 0, sizeof(msg)); -+ irq_chip_write_msi_msg(irq_data, &msg); -+} -+ -+static int msi_domain_alloc(struct irq_domain *domain, unsigned int virq, -+ unsigned int nr_irqs, void *arg) -+{ -+ struct msi_domain_info *info = domain->host_data; -+ struct msi_domain_ops *ops = info->ops; -+ irq_hw_number_t hwirq = ops->get_hwirq(info, arg); -+ int i, ret; -+ -+#if 0 -+ if (irq_find_mapping(domain, hwirq) > 0) -+ return -EEXIST; -+#endif -+ -+ ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg); -+ if (ret < 0) -+ return ret; -+ -+ for (i = 0; i < nr_irqs; i++) { -+ ret = ops->msi_init(domain, info, virq + i, hwirq + i, arg); -+ if (ret < 0) { -+ if (ops->msi_free) { -+ for (i--; i > 0; i--) -+ ops->msi_free(domain, info, virq + i); -+ } -+ irq_domain_free_irqs_top(domain, virq, nr_irqs); -+ return ret; -+ } -+ } -+ -+ return 0; -+} -+ -+static void msi_domain_free(struct irq_domain *domain, unsigned int virq, -+ unsigned int nr_irqs) -+{ -+ struct msi_domain_info *info = domain->host_data; -+ int i; -+ -+ if (info->ops->msi_free) { -+ for (i = 0; i < nr_irqs; i++) -+ info->ops->msi_free(domain, info, virq + i); -+ } -+ irq_domain_free_irqs_top(domain, virq, nr_irqs); -+} -+ -+static struct irq_domain_ops msi_domain_ops = { -+ .alloc = msi_domain_alloc, -+ .free = msi_domain_free, -+ .activate = msi_domain_activate, -+ .deactivate = msi_domain_deactivate, -+}; -+ -+#ifdef GENERIC_MSI_DOMAIN_OPS -+static irq_hw_number_t msi_domain_ops_get_hwirq(struct msi_domain_info *info, -+ msi_alloc_info_t *arg) -+{ -+ return arg->hwirq; -+} -+ -+static int msi_domain_ops_prepare(struct irq_domain *domain, struct device *dev, -+ int nvec, msi_alloc_info_t *arg) -+{ -+ memset(arg, 0, sizeof(*arg)); -+ return 0; -+} -+ -+static void msi_domain_ops_set_desc(msi_alloc_info_t *arg, -+ struct msi_desc *desc) -+{ -+ arg->desc = desc; -+} -+#else -+#define msi_domain_ops_get_hwirq NULL -+#define msi_domain_ops_prepare NULL -+#define msi_domain_ops_set_desc NULL -+#endif /* !GENERIC_MSI_DOMAIN_OPS */ -+ -+static int msi_domain_ops_init(struct irq_domain *domain, -+ struct msi_domain_info *info, -+ unsigned int virq, irq_hw_number_t hwirq, -+ msi_alloc_info_t *arg) -+{ -+ irq_domain_set_hwirq_and_chip(domain, virq, hwirq, info->chip, -+ info->chip_data); -+ if (info->handler && info->handler_name) { -+ __irq_set_handler(virq, info->handler, 0, info->handler_name); -+ if (info->handler_data) -+ irq_set_handler_data(virq, info->handler_data); -+ } -+ return 0; -+} -+ -+static int msi_domain_ops_check(struct irq_domain *domain, -+ struct msi_domain_info *info, -+ struct device *dev) -+{ -+ return 0; -+} -+ -+static struct msi_domain_ops msi_domain_ops_default = { -+ .get_hwirq = msi_domain_ops_get_hwirq, -+ .msi_init = msi_domain_ops_init, -+ .msi_check = msi_domain_ops_check, -+ .msi_prepare = msi_domain_ops_prepare, -+ .set_desc = msi_domain_ops_set_desc, -+}; -+ -+static void msi_domain_update_dom_ops(struct msi_domain_info *info) -+{ -+ struct msi_domain_ops *ops = info->ops; -+ -+ if (ops == NULL) { -+ info->ops = &msi_domain_ops_default; -+ return; -+ } -+ -+ if (ops->get_hwirq == NULL) -+ ops->get_hwirq = msi_domain_ops_default.get_hwirq; -+ if (ops->msi_init == NULL) -+ ops->msi_init = msi_domain_ops_default.msi_init; -+ if (ops->msi_check == NULL) -+ ops->msi_check = msi_domain_ops_default.msi_check; -+ if (ops->msi_prepare == NULL) -+ ops->msi_prepare = msi_domain_ops_default.msi_prepare; -+ if (ops->set_desc == NULL) -+ ops->set_desc = msi_domain_ops_default.set_desc; -+} -+ -+static void msi_domain_update_chip_ops(struct msi_domain_info *info) -+{ -+ struct irq_chip *chip = info->chip; -+ -+ BUG_ON(!chip); -+ if (!chip->irq_mask) -+ chip->irq_mask = pci_msi_mask_irq; -+ if (!chip->irq_unmask) -+ chip->irq_unmask = pci_msi_unmask_irq; -+ if (!chip->irq_set_affinity) -+ chip->irq_set_affinity = msi_domain_set_affinity; -+} -+ -+/** -+ * msi_create_irq_domain - Create a MSI interrupt domain -+ * @of_node: Optional device-tree node of the interrupt controller -+ * @info: MSI domain info -+ * @parent: Parent irq domain -+ */ -+struct irq_domain *msi_create_irq_domain(struct device_node *node, -+ struct msi_domain_info *info, -+ struct irq_domain *parent) -+{ -+ if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS) -+ msi_domain_update_dom_ops(info); -+ if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS) -+ msi_domain_update_chip_ops(info); -+ -+ return irq_domain_add_hierarchy(parent, 0, 0, node, &msi_domain_ops, -+ info); -+} -+ -+/** -+ * msi_domain_alloc_irqs - Allocate interrupts from a MSI interrupt domain -+ * @domain: The domain to allocate from -+ * @dev: Pointer to device struct of the device for which the interrupts -+ * are allocated -+ * @nvec: The number of interrupts to allocate -+ * -+ * Returns 0 on success or an error code. -+ */ -+int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, -+ int nvec) -+{ -+ struct msi_domain_info *info = domain->host_data; -+ struct msi_domain_ops *ops = info->ops; -+ msi_alloc_info_t arg; -+ struct msi_desc *desc; -+ int i, ret, virq = -1; -+ -+ ret = ops->msi_check(domain, info, dev); -+ if (ret == 0) -+ ret = ops->msi_prepare(domain, dev, nvec, &arg); -+ if (ret) -+ return ret; -+ -+ for_each_msi_entry(desc, dev) { -+ ops->set_desc(&arg, desc); -+ if (info->flags & MSI_FLAG_IDENTITY_MAP) -+ virq = (int)ops->get_hwirq(info, &arg); -+ else -+ virq = -1; -+ -+ virq = __irq_domain_alloc_irqs(domain, virq, desc->nvec_used, -+ dev_to_node(dev), &arg, false); -+ if (virq < 0) { -+ ret = -ENOSPC; -+ if (ops->handle_error) -+ ret = ops->handle_error(domain, desc, ret); -+ if (ops->msi_finish) -+ ops->msi_finish(&arg, ret); -+ return ret; -+ } -+ -+ for (i = 0; i < desc->nvec_used; i++) -+ irq_set_msi_desc_off(virq, i, desc); -+ } -+ -+ if (ops->msi_finish) -+ ops->msi_finish(&arg, 0); -+ -+ for_each_msi_entry(desc, dev) { -+ if (desc->nvec_used == 1) -+ dev_dbg(dev, "irq %d for MSI\n", virq); -+ else -+ dev_dbg(dev, "irq [%d-%d] for MSI\n", -+ virq, virq + desc->nvec_used - 1); -+ } -+ -+ return 0; -+} -+ -+/** -+ * msi_domain_free_irqs - Free interrupts from a MSI interrupt @domain associated tp @dev -+ * @domain: The domain to managing the interrupts -+ * @dev: Pointer to device struct of the device for which the interrupts -+ * are free -+ */ -+void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev) -+{ -+ struct msi_desc *desc; -+ -+ for_each_msi_entry(desc, dev) { -+ /* -+ * We might have failed to allocate an MSI early -+ * enough that there is no IRQ associated to this -+ * entry. If that's the case, don't do anything. -+ */ -+ if (desc->irq) { -+ irq_domain_free_irqs(desc->irq, desc->nvec_used); -+ desc->irq = 0; -+ } -+ } -+} -+ -+/** -+ * msi_get_domain_info - Get the MSI interrupt domain info for @domain -+ * @domain: The interrupt domain to retrieve data from -+ * -+ * Returns the pointer to the msi_domain_info stored in -+ * @domain->host_data. -+ */ -+struct msi_domain_info *msi_get_domain_info(struct irq_domain *domain) -+{ -+ return (struct msi_domain_info *)domain->host_data; -+} -+ -+#endif /* CONFIG_GENERIC_MSI_IRQ_DOMAIN */ -diff --git a/kernel/resource.c b/kernel/resource.c -index 0bcebff..19f2357 100644 ---- a/kernel/resource.c -+++ b/kernel/resource.c -@@ -22,6 +22,7 @@ - #include - #include - #include -+#include - #include - - -@@ -1529,6 +1530,30 @@ int iomem_is_exclusive(u64 addr) - return err; - } - -+struct resource_entry *resource_list_create_entry(struct resource *res, -+ size_t extra_size) -+{ -+ struct resource_entry *entry; -+ -+ entry = kzalloc(sizeof(*entry) + extra_size, GFP_KERNEL); -+ if (entry) { -+ INIT_LIST_HEAD(&entry->node); -+ entry->res = res ? res : &entry->__res; -+ } -+ -+ return entry; -+} -+EXPORT_SYMBOL(resource_list_create_entry); -+ -+void resource_list_free(struct list_head *head) -+{ -+ struct resource_entry *entry, *tmp; -+ -+ list_for_each_entry_safe(entry, tmp, head, node) -+ resource_list_destroy_entry(entry); -+} -+EXPORT_SYMBOL(resource_list_free); -+ - static int __init strict_iomem(char *str) - { - if (strstr(str, "relaxed")) -diff --git a/mm/Makefile b/mm/Makefile -index 8405eb0..e0bda2d 100644 ---- a/mm/Makefile -+++ b/mm/Makefile -@@ -18,7 +18,7 @@ obj-y := filemap.o mempool.o oom_kill.o \ - mm_init.o mmu_context.o percpu.o slab_common.o \ - compaction.o vmacache.o \ - interval_tree.o list_lru.o workingset.o \ -- iov_iter.o debug.o $(mmu-y) -+ iov_iter.o prfile.o debug.o $(mmu-y) - - obj-y += init-mm.o - -diff --git a/mm/filemap.c b/mm/filemap.c -index 7e6ab98..2fe1e57 100644 ---- a/mm/filemap.c -+++ b/mm/filemap.c -@@ -2063,7 +2063,7 @@ int filemap_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) - int ret = VM_FAULT_LOCKED; - - sb_start_pagefault(inode->i_sb); -- file_update_time(vma->vm_file); -+ vma_file_update_time(vma); - lock_page(page); - if (page->mapping != inode->i_mapping) { - unlock_page(page); -diff --git a/mm/fremap.c b/mm/fremap.c -index 72b8fa3..a00bbf0 100644 ---- a/mm/fremap.c -+++ b/mm/fremap.c -@@ -224,16 +224,28 @@ get_write_lock: - */ - if (mapping_cap_account_dirty(mapping)) { - unsigned long addr; -- struct file *file = get_file(vma->vm_file); -+ struct file *file = vma->vm_file, -+ *prfile = vma->vm_prfile; -+ - /* mmap_region may free vma; grab the info now */ - vm_flags = vma->vm_flags; - -+ vma_get_file(vma); - addr = mmap_region(file, start, size, vm_flags, pgoff); -- fput(file); -+ vma_fput(vma); - if (IS_ERR_VALUE(addr)) { - err = addr; - } else { - BUG_ON(addr != start); -+ if (prfile) { -+ struct vm_area_struct *new_vma; -+ -+ new_vma = find_vma(mm, addr); -+ if (!new_vma->vm_prfile) -+ new_vma->vm_prfile = prfile; -+ if (new_vma != vma) -+ get_file(prfile); -+ } - err = 0; - } - goto out_freed; -diff --git a/mm/memory.c b/mm/memory.c -index 90fb265..844df2e 100644 ---- a/mm/memory.c -+++ b/mm/memory.c -@@ -2156,7 +2156,7 @@ reuse: - - /* file_update_time outside page_lock */ - if (vma->vm_file) -- file_update_time(vma->vm_file); -+ vma_file_update_time(vma); - } - put_page(dirty_page); - if (page_mkwrite) { -diff --git a/mm/mmap.c b/mm/mmap.c -index f88b4f9..9994987 100644 ---- a/mm/mmap.c -+++ b/mm/mmap.c -@@ -277,7 +277,7 @@ static struct vm_area_struct *remove_vma(struct vm_area_struct *vma) - if (vma->vm_ops && vma->vm_ops->close) - vma->vm_ops->close(vma); - if (vma->vm_file) -- fput(vma->vm_file); -+ vma_fput(vma); - mpol_put(vma_policy(vma)); - kmem_cache_free(vm_area_cachep, vma); - return next; -@@ -895,7 +895,7 @@ again: remove_next = 1 + (end > next->vm_end); - if (remove_next) { - if (file) { - uprobe_munmap(next, next->vm_start, next->vm_end); -- fput(file); -+ vma_fput(vma); - } - if (next->anon_vma) - anon_vma_merge(vma, next); -@@ -1680,8 +1680,8 @@ out: - return addr; - - unmap_and_free_vma: -+ vma_fput(vma); - vma->vm_file = NULL; -- fput(file); - - /* Undo any partial mapping done by a device driver. */ - unmap_region(mm, vma, prev, vma->vm_start, vma->vm_end); -@@ -2480,7 +2480,7 @@ static int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma, - goto out_free_mpol; - - if (new->vm_file) -- get_file(new->vm_file); -+ vma_get_file(new); - - if (new->vm_ops && new->vm_ops->open) - new->vm_ops->open(new); -@@ -2499,7 +2499,7 @@ static int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma, - if (new->vm_ops && new->vm_ops->close) - new->vm_ops->close(new); - if (new->vm_file) -- fput(new->vm_file); -+ vma_fput(new); - unlink_anon_vmas(new); - out_free_mpol: - mpol_put(vma_policy(new)); -@@ -2889,7 +2889,7 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap, - if (anon_vma_clone(new_vma, vma)) - goto out_free_mempol; - if (new_vma->vm_file) -- get_file(new_vma->vm_file); -+ vma_get_file(new_vma); - if (new_vma->vm_ops && new_vma->vm_ops->open) - new_vma->vm_ops->open(new_vma); - vma_link(mm, new_vma, prev, rb_link, rb_parent); -diff --git a/mm/nommu.c b/mm/nommu.c -index b5ba5bc..a7662fc 100644 ---- a/mm/nommu.c -+++ b/mm/nommu.c -@@ -658,7 +658,7 @@ static void __put_nommu_region(struct vm_region *region) - up_write(&nommu_region_sem); - - if (region->vm_file) -- fput(region->vm_file); -+ vmr_fput(region); - - /* IO memory and memory shared directly out of the pagecache - * from ramfs/tmpfs mustn't be released here */ -@@ -823,7 +823,7 @@ static void delete_vma(struct mm_struct *mm, struct vm_area_struct *vma) - if (vma->vm_ops && vma->vm_ops->close) - vma->vm_ops->close(vma); - if (vma->vm_file) -- fput(vma->vm_file); -+ vma_fput(vma); - put_nommu_region(vma->vm_region); - kmem_cache_free(vm_area_cachep, vma); - } -@@ -1385,7 +1385,7 @@ unsigned long do_mmap_pgoff(struct file *file, - goto error_just_free; - } - } -- fput(region->vm_file); -+ vmr_fput(region); - kmem_cache_free(vm_region_jar, region); - region = pregion; - result = start; -@@ -1461,10 +1461,10 @@ error_just_free: - up_write(&nommu_region_sem); - error: - if (region->vm_file) -- fput(region->vm_file); -+ vmr_fput(region); - kmem_cache_free(vm_region_jar, region); - if (vma->vm_file) -- fput(vma->vm_file); -+ vma_fput(vma); - kmem_cache_free(vm_area_cachep, vma); - kleave(" = %d", ret); - return ret; -diff --git a/mm/prfile.c b/mm/prfile.c -new file mode 100644 -index 0000000..532e518 ---- /dev/null -+++ b/mm/prfile.c -@@ -0,0 +1,86 @@ -+/* -+ * Mainly for aufs which mmap(2) diffrent file and wants to print different path -+ * in /proc/PID/maps. -+ * Call these functions via macros defined in linux/mm.h. -+ * -+ * See Documentation/filesystems/aufs/design/06mmap.txt -+ * -+ * Copyright (c) 2014 Junjro R. Okajima -+ * Copyright (c) 2014 Ian Campbell -+ */ -+ -+#include -+#include -+#include -+ -+/* #define PRFILE_TRACE */ -+static inline void prfile_trace(struct file *f, struct file *pr, -+ const char func[], int line, const char func2[]) -+{ -+#ifdef PRFILE_TRACE -+ if (pr) -+ pr_info("%s:%d: %s, %s\n", func, line, func2, -+ f ? (char *)f->f_dentry->d_name.name : "(null)"); -+#endif -+} -+ -+void vma_do_file_update_time(struct vm_area_struct *vma, const char func[], -+ int line) -+{ -+ struct file *f = vma->vm_file, *pr = vma->vm_prfile; -+ -+ prfile_trace(f, pr, func, line, __func__); -+ file_update_time(f); -+ if (f && pr) -+ file_update_time(pr); -+} -+ -+struct file *vma_do_pr_or_file(struct vm_area_struct *vma, const char func[], -+ int line) -+{ -+ struct file *f = vma->vm_file, *pr = vma->vm_prfile; -+ -+ prfile_trace(f, pr, func, line, __func__); -+ return (f && pr) ? pr : f; -+} -+ -+void vma_do_get_file(struct vm_area_struct *vma, const char func[], int line) -+{ -+ struct file *f = vma->vm_file, *pr = vma->vm_prfile; -+ -+ prfile_trace(f, pr, func, line, __func__); -+ get_file(f); -+ if (f && pr) -+ get_file(pr); -+} -+ -+void vma_do_fput(struct vm_area_struct *vma, const char func[], int line) -+{ -+ struct file *f = vma->vm_file, *pr = vma->vm_prfile; -+ -+ prfile_trace(f, pr, func, line, __func__); -+ fput(f); -+ if (f && pr) -+ fput(pr); -+} -+ -+#ifndef CONFIG_MMU -+struct file *vmr_do_pr_or_file(struct vm_region *region, const char func[], -+ int line) -+{ -+ struct file *f = region->vm_file, *pr = region->vm_prfile; -+ -+ prfile_trace(f, pr, func, line, __func__); -+ return (f && pr) ? pr : f; -+} -+ -+void vmr_do_fput(struct vm_region *region, const char func[], int line) -+{ -+ struct file *f = region->vm_file, *pr = region->vm_prfile; -+ -+ prfile_trace(f, pr, func, line, __func__); -+ fput(f); -+ if (f && pr) -+ fput(pr); -+} -+#endif /* !CONFIG_MMU */ -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..5117552 100644 ---- a/scripts/Makefile.lib -+++ b/scripts/Makefile.lib -@@ -283,18 +283,6 @@ $(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)) -- - # Bzip2 - # --------------------------------------------------------------------------- - -diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c -index fa756d0..ad57f0c 100644 ---- a/sound/soc/fsl/mpc8610_hpcd.c -+++ b/sound/soc/fsl/mpc8610_hpcd.c -@@ -12,11 +12,11 @@ - - #include - #include -+#include - #include - #include - #include - #include --#include - - #include "fsl_dma.h" - #include "fsl_ssi.h" -diff --git a/sound/soc/fsl/p1022_ds.c b/sound/soc/fsl/p1022_ds.c -index f75c3cf..64a0bb6 100644 ---- a/sound/soc/fsl/p1022_ds.c -+++ b/sound/soc/fsl/p1022_ds.c -@@ -11,12 +11,12 @@ - */ - - #include -+#include - #include - #include - #include - #include - #include --#include - - #include "fsl_dma.h" - #include "fsl_ssi.h" -diff --git a/sound/soc/fsl/p1022_rdk.c b/sound/soc/fsl/p1022_rdk.c -index 9d89bb0..4ce4aff 100644 ---- a/sound/soc/fsl/p1022_rdk.c -+++ b/sound/soc/fsl/p1022_rdk.c -@@ -18,12 +18,12 @@ - */ - - #include -+#include - #include - #include - #include - #include - #include --#include - - #include "fsl_dma.h" - #include "fsl_ssi.h" --- -2.7.4 - diff --git a/packages/base/any/kernels/3.18.25/patches/add-fsl-dpaa2-and-fsl-mc-support-based-on-3.18.25.patch b/packages/base/any/kernels/3.18.25/patches/add-fsl-dpaa2-and-fsl-mc-support-based-on-3.18.25.patch deleted file mode 100644 index 5d493c1d..00000000 --- a/packages/base/any/kernels/3.18.25/patches/add-fsl-dpaa2-and-fsl-mc-support-based-on-3.18.25.patch +++ /dev/null @@ -1,35045 +0,0 @@ -From 340daa3e4a9851ab640062065eff4501e6f7cc61 Mon Sep 17 00:00:00 2001 -From: Shengzhou Liu -Date: Fri, 23 Sep 2016 13:45:59 +0800 -Subject: [PATCH 1/2] Add fsl-dpaa2 and fsl-mc support based on 3.18.25 - -This patch integrated a ton of patches to support DPAA2.0 & MC -networking which is used on LS2080A/LS2088A RDB. ---- - MAINTAINERS | 27 + - arch/arm64/include/asm/io.h | 1 + - arch/arm64/include/asm/pgtable.h | 1 + - drivers/net/ethernet/freescale/Kconfig | 8 +- - drivers/net/ethernet/freescale/fec_mpc52xx.c | 2 +- - drivers/net/ethernet/freescale/fec_mpc52xx_phy.c | 2 +- - .../net/ethernet/freescale/fs_enet/fs_enet-main.c | 4 +- - .../net/ethernet/freescale/fs_enet/mii-bitbang.c | 2 +- - drivers/net/ethernet/freescale/fs_enet/mii-fec.c | 4 +- - drivers/net/ethernet/freescale/fsl_pq_mdio.c | 2 +- - drivers/net/ethernet/freescale/gianfar.c | 2 +- - drivers/net/ethernet/freescale/gianfar_ptp.c | 2 +- - drivers/net/ethernet/freescale/ucc_geth.c | 2 +- - drivers/net/ethernet/freescale/xgmac_mdio.c | 194 +- - drivers/net/phy/Kconfig | 5 + - drivers/net/phy/Makefile | 1 + - drivers/net/phy/aquantia.c | 201 ++ - drivers/net/phy/fsl_10gkr.c | 1467 ++++++++++ - drivers/net/phy/teranetics.c | 135 + - drivers/staging/Kconfig | 4 + - drivers/staging/Makefile | 2 + - drivers/staging/fsl-dpaa2/Kconfig | 12 + - drivers/staging/fsl-dpaa2/Makefile | 6 + - drivers/staging/fsl-dpaa2/ethernet/Kconfig | 36 + - drivers/staging/fsl-dpaa2/ethernet/Makefile | 21 + - .../staging/fsl-dpaa2/ethernet/dpaa2-eth-debugfs.c | 317 +++ - .../staging/fsl-dpaa2/ethernet/dpaa2-eth-debugfs.h | 61 + - .../staging/fsl-dpaa2/ethernet/dpaa2-eth-trace.h | 185 ++ - drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c | 2836 ++++++++++++++++++++ - drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.h | 377 +++ - drivers/staging/fsl-dpaa2/ethernet/dpaa2-ethtool.c | 861 ++++++ - drivers/staging/fsl-dpaa2/ethernet/dpkg.h | 175 ++ - drivers/staging/fsl-dpaa2/ethernet/dpni-cmd.h | 1058 ++++++++ - drivers/staging/fsl-dpaa2/ethernet/dpni.c | 1907 +++++++++++++ - drivers/staging/fsl-dpaa2/ethernet/dpni.h | 2581 ++++++++++++++++++ - drivers/staging/fsl-dpaa2/mac/Kconfig | 24 + - drivers/staging/fsl-dpaa2/mac/Makefile | 10 + - drivers/staging/fsl-dpaa2/mac/dpmac-cmd.h | 195 ++ - drivers/staging/fsl-dpaa2/mac/dpmac.c | 422 +++ - drivers/staging/fsl-dpaa2/mac/dpmac.h | 593 ++++ - drivers/staging/fsl-dpaa2/mac/mac.c | 694 +++++ - drivers/staging/fsl-mc/Kconfig | 1 + - drivers/staging/fsl-mc/Makefile | 2 + - drivers/staging/fsl-mc/TODO | 13 + - drivers/staging/fsl-mc/bus/Kconfig | 45 + - drivers/staging/fsl-mc/bus/Makefile | 24 + - drivers/staging/fsl-mc/bus/dpbp.c | 459 ++++ - drivers/staging/fsl-mc/bus/dpcon.c | 407 +++ - drivers/staging/fsl-mc/bus/dpio/Makefile | 9 + - drivers/staging/fsl-mc/bus/dpio/dpio-drv.c | 401 +++ - drivers/staging/fsl-mc/bus/dpio/dpio-drv.h | 33 + - drivers/staging/fsl-mc/bus/dpio/dpio.c | 468 ++++ - drivers/staging/fsl-mc/bus/dpio/dpio_service.c | 801 ++++++ - drivers/staging/fsl-mc/bus/dpio/fsl_dpio.h | 460 ++++ - drivers/staging/fsl-mc/bus/dpio/fsl_dpio_cmd.h | 184 ++ - drivers/staging/fsl-mc/bus/dpio/fsl_qbman_base.h | 123 + - drivers/staging/fsl-mc/bus/dpio/fsl_qbman_portal.h | 753 ++++++ - drivers/staging/fsl-mc/bus/dpio/qbman_debug.c | 846 ++++++ - drivers/staging/fsl-mc/bus/dpio/qbman_debug.h | 136 + - drivers/staging/fsl-mc/bus/dpio/qbman_portal.c | 1212 +++++++++ - drivers/staging/fsl-mc/bus/dpio/qbman_portal.h | 261 ++ - drivers/staging/fsl-mc/bus/dpio/qbman_private.h | 173 ++ - drivers/staging/fsl-mc/bus/dpio/qbman_sys.h | 307 +++ - drivers/staging/fsl-mc/bus/dpio/qbman_sys_decl.h | 86 + - drivers/staging/fsl-mc/bus/dpio/qbman_test.c | 664 +++++ - drivers/staging/fsl-mc/bus/dpmcp-cmd.h | 56 + - drivers/staging/fsl-mc/bus/dpmcp.c | 318 +++ - drivers/staging/fsl-mc/bus/dpmcp.h | 323 +++ - drivers/staging/fsl-mc/bus/dpmng-cmd.h | 47 + - drivers/staging/fsl-mc/bus/dpmng.c | 85 + - drivers/staging/fsl-mc/bus/dprc-cmd.h | 87 + - drivers/staging/fsl-mc/bus/dprc-driver.c | 1084 ++++++++ - drivers/staging/fsl-mc/bus/dprc.c | 1218 +++++++++ - drivers/staging/fsl-mc/bus/mc-allocator.c | 716 +++++ - drivers/staging/fsl-mc/bus/mc-bus.c | 1347 ++++++++++ - drivers/staging/fsl-mc/bus/mc-ioctl.h | 25 + - drivers/staging/fsl-mc/bus/mc-restool.c | 312 +++ - drivers/staging/fsl-mc/bus/mc-sys.c | 677 +++++ - drivers/staging/fsl-mc/include/dpbp-cmd.h | 62 + - drivers/staging/fsl-mc/include/dpbp.h | 438 +++ - drivers/staging/fsl-mc/include/dpcon-cmd.h | 162 ++ - drivers/staging/fsl-mc/include/dpcon.h | 407 +++ - drivers/staging/fsl-mc/include/dpmac-cmd.h | 192 ++ - drivers/staging/fsl-mc/include/dpmac.h | 528 ++++ - drivers/staging/fsl-mc/include/dpmng.h | 80 + - drivers/staging/fsl-mc/include/dprc.h | 990 +++++++ - drivers/staging/fsl-mc/include/fsl_dpaa2_fd.h | 774 ++++++ - drivers/staging/fsl-mc/include/fsl_dpaa2_io.h | 619 +++++ - drivers/staging/fsl-mc/include/mc-cmd.h | 133 + - drivers/staging/fsl-mc/include/mc-private.h | 168 ++ - drivers/staging/fsl-mc/include/mc-sys.h | 128 + - drivers/staging/fsl-mc/include/mc.h | 244 ++ - drivers/staging/fsl-mc/include/net.h | 481 ++++ - scripts/Makefile.dtbinst | 51 + - 94 files changed, 33975 insertions(+), 84 deletions(-) - create mode 100644 drivers/net/phy/aquantia.c - create mode 100644 drivers/net/phy/fsl_10gkr.c - create mode 100644 drivers/net/phy/teranetics.c - create mode 100644 drivers/staging/fsl-dpaa2/Kconfig - create mode 100644 drivers/staging/fsl-dpaa2/Makefile - create mode 100644 drivers/staging/fsl-dpaa2/ethernet/Kconfig - create mode 100644 drivers/staging/fsl-dpaa2/ethernet/Makefile - create mode 100644 drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-debugfs.c - create mode 100644 drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-debugfs.h - create mode 100644 drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-trace.h - create mode 100644 drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c - create mode 100644 drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.h - create mode 100644 drivers/staging/fsl-dpaa2/ethernet/dpaa2-ethtool.c - create mode 100644 drivers/staging/fsl-dpaa2/ethernet/dpkg.h - create mode 100644 drivers/staging/fsl-dpaa2/ethernet/dpni-cmd.h - create mode 100644 drivers/staging/fsl-dpaa2/ethernet/dpni.c - create mode 100644 drivers/staging/fsl-dpaa2/ethernet/dpni.h - create mode 100644 drivers/staging/fsl-dpaa2/mac/Kconfig - create mode 100644 drivers/staging/fsl-dpaa2/mac/Makefile - create mode 100644 drivers/staging/fsl-dpaa2/mac/dpmac-cmd.h - create mode 100644 drivers/staging/fsl-dpaa2/mac/dpmac.c - create mode 100644 drivers/staging/fsl-dpaa2/mac/dpmac.h - create mode 100644 drivers/staging/fsl-dpaa2/mac/mac.c - create mode 100644 drivers/staging/fsl-mc/Kconfig - create mode 100644 drivers/staging/fsl-mc/Makefile - create mode 100644 drivers/staging/fsl-mc/TODO - create mode 100644 drivers/staging/fsl-mc/bus/Kconfig - create mode 100644 drivers/staging/fsl-mc/bus/Makefile - create mode 100644 drivers/staging/fsl-mc/bus/dpbp.c - create mode 100644 drivers/staging/fsl-mc/bus/dpcon.c - create mode 100644 drivers/staging/fsl-mc/bus/dpio/Makefile - create mode 100644 drivers/staging/fsl-mc/bus/dpio/dpio-drv.c - create mode 100644 drivers/staging/fsl-mc/bus/dpio/dpio-drv.h - create mode 100644 drivers/staging/fsl-mc/bus/dpio/dpio.c - create mode 100644 drivers/staging/fsl-mc/bus/dpio/dpio_service.c - create mode 100644 drivers/staging/fsl-mc/bus/dpio/fsl_dpio.h - create mode 100644 drivers/staging/fsl-mc/bus/dpio/fsl_dpio_cmd.h - create mode 100644 drivers/staging/fsl-mc/bus/dpio/fsl_qbman_base.h - create mode 100644 drivers/staging/fsl-mc/bus/dpio/fsl_qbman_portal.h - create mode 100644 drivers/staging/fsl-mc/bus/dpio/qbman_debug.c - create mode 100644 drivers/staging/fsl-mc/bus/dpio/qbman_debug.h - create mode 100644 drivers/staging/fsl-mc/bus/dpio/qbman_portal.c - create mode 100644 drivers/staging/fsl-mc/bus/dpio/qbman_portal.h - create mode 100644 drivers/staging/fsl-mc/bus/dpio/qbman_private.h - create mode 100644 drivers/staging/fsl-mc/bus/dpio/qbman_sys.h - create mode 100644 drivers/staging/fsl-mc/bus/dpio/qbman_sys_decl.h - create mode 100644 drivers/staging/fsl-mc/bus/dpio/qbman_test.c - create mode 100644 drivers/staging/fsl-mc/bus/dpmcp-cmd.h - create mode 100644 drivers/staging/fsl-mc/bus/dpmcp.c - create mode 100644 drivers/staging/fsl-mc/bus/dpmcp.h - create mode 100644 drivers/staging/fsl-mc/bus/dpmng-cmd.h - create mode 100644 drivers/staging/fsl-mc/bus/dpmng.c - create mode 100644 drivers/staging/fsl-mc/bus/dprc-cmd.h - create mode 100644 drivers/staging/fsl-mc/bus/dprc-driver.c - create mode 100644 drivers/staging/fsl-mc/bus/dprc.c - create mode 100644 drivers/staging/fsl-mc/bus/mc-allocator.c - create mode 100644 drivers/staging/fsl-mc/bus/mc-bus.c - create mode 100644 drivers/staging/fsl-mc/bus/mc-ioctl.h - create mode 100644 drivers/staging/fsl-mc/bus/mc-restool.c - create mode 100644 drivers/staging/fsl-mc/bus/mc-sys.c - create mode 100644 drivers/staging/fsl-mc/include/dpbp-cmd.h - create mode 100644 drivers/staging/fsl-mc/include/dpbp.h - create mode 100644 drivers/staging/fsl-mc/include/dpcon-cmd.h - create mode 100644 drivers/staging/fsl-mc/include/dpcon.h - create mode 100644 drivers/staging/fsl-mc/include/dpmac-cmd.h - create mode 100644 drivers/staging/fsl-mc/include/dpmac.h - create mode 100644 drivers/staging/fsl-mc/include/dpmng.h - create mode 100644 drivers/staging/fsl-mc/include/dprc.h - create mode 100644 drivers/staging/fsl-mc/include/fsl_dpaa2_fd.h - create mode 100644 drivers/staging/fsl-mc/include/fsl_dpaa2_io.h - create mode 100644 drivers/staging/fsl-mc/include/mc-cmd.h - create mode 100644 drivers/staging/fsl-mc/include/mc-private.h - create mode 100644 drivers/staging/fsl-mc/include/mc-sys.h - create mode 100644 drivers/staging/fsl-mc/include/mc.h - create mode 100644 drivers/staging/fsl-mc/include/net.h - create mode 100644 scripts/Makefile.dtbinst - -diff --git a/MAINTAINERS b/MAINTAINERS -index 1ae7362..63a796c 100644 ---- a/MAINTAINERS -+++ b/MAINTAINERS -@@ -3973,6 +3973,33 @@ F: sound/soc/fsl/fsl* - F: sound/soc/fsl/imx* - F: sound/soc/fsl/mpc8610_hpcd.c - -+FREESCALE QORIQ MANAGEMENT COMPLEX DRIVER -+M: J. German Rivera -+L: linux-kernel@vger.kernel.org -+S: Maintained -+F: drivers/staging/fsl-mc/ -+ -+FREESCALE DPAA2 ETH DRIVER -+M: Ioana Radulescu -+M: Bogdan Hamciuc -+M: Cristian Sovaiala -+L: linux-kernel@vger.kernel.org -+S: Maintained -+F: drivers/staging/fsl-dpaa2/ethernet/ -+ -+FREESCALE QORIQ MANAGEMENT COMPLEX RESTOOL DRIVER -+M: Lijun Pan -+L: linux-kernel@vger.kernel.org -+S: Maintained -+F: drivers/staging/fsl-mc/bus/mc-ioctl.h -+F: drivers/staging/fsl-mc/bus/mc-restool.c -+ -+FREESCALE DPAA2 MAC/PHY INTERFACE DRIVER -+M: Alex Marginean -+L: linux-kernel@vger.kernel.org -+S: Maintained -+F: drivers/staging/fsl-dpaa2/mac/ -+ - FREEVXFS FILESYSTEM - M: Christoph Hellwig - W: ftp://ftp.openlinux.org/pub/people/hch/vxfs -diff --git a/arch/arm64/include/asm/io.h b/arch/arm64/include/asm/io.h -index 75825b6..f58e31a 100644 ---- a/arch/arm64/include/asm/io.h -+++ b/arch/arm64/include/asm/io.h -@@ -249,6 +249,7 @@ extern void __iomem *ioremap_cache(phys_addr_t phys_addr, size_t size); - #define ioremap(addr, size) __ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE)) - #define ioremap_nocache(addr, size) __ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE)) - #define ioremap_wc(addr, size) __ioremap((addr), (size), __pgprot(PROT_NORMAL_NC)) -+#define ioremap_cache_ns(addr, size) __ioremap((addr), (size), __pgprot(PROT_NORMAL_NS)) - #define iounmap __iounmap - - #define ARCH_HAS_IOREMAP_WC -diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h -index 41a43bf..009f690 100644 ---- a/arch/arm64/include/asm/pgtable.h -+++ b/arch/arm64/include/asm/pgtable.h -@@ -65,6 +65,7 @@ extern void __pgd_error(const char *file, int line, unsigned long val); - #define PROT_DEVICE_nGnRE (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_ATTRINDX(MT_DEVICE_nGnRE)) - #define PROT_NORMAL_NC (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_ATTRINDX(MT_NORMAL_NC)) - #define PROT_NORMAL (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_ATTRINDX(MT_NORMAL)) -+#define PROT_NORMAL_NS (PTE_TYPE_PAGE | PTE_AF | PTE_PXN | PTE_UXN | PTE_ATTRINDX(MT_NORMAL)) - - #define PROT_SECT_DEVICE_nGnRE (PROT_SECT_DEFAULT | PMD_SECT_PXN | PMD_SECT_UXN | PMD_ATTRINDX(MT_DEVICE_nGnRE)) - #define PROT_SECT_NORMAL (PROT_SECT_DEFAULT | PMD_SECT_PXN | PMD_SECT_UXN | PMD_ATTRINDX(MT_NORMAL)) -diff --git a/drivers/net/ethernet/freescale/Kconfig b/drivers/net/ethernet/freescale/Kconfig -index 2703083..0c1c97d 100644 ---- a/drivers/net/ethernet/freescale/Kconfig -+++ b/drivers/net/ethernet/freescale/Kconfig -@@ -7,7 +7,8 @@ config NET_VENDOR_FREESCALE - default y - depends on FSL_SOC || QUICC_ENGINE || CPM1 || CPM2 || PPC_MPC512x || \ - M523x || M527x || M5272 || M528x || M520x || M532x || \ -- ARCH_MXC || ARCH_MXS || (PPC_MPC52xx && PPC_BESTCOMM) -+ ARCH_MXC || ARCH_MXS || (PPC_MPC52xx && PPC_BESTCOMM) || \ -+ ARCH_LAYERSCAPE - ---help--- - If you have a network (Ethernet) card belonging to this class, say Y - and read the Ethernet-HOWTO, available from -@@ -58,18 +59,17 @@ source "drivers/net/ethernet/freescale/fs_enet/Kconfig" - - config FSL_PQ_MDIO - tristate "Freescale PQ MDIO" -- depends on FSL_SOC - select PHYLIB - ---help--- - This driver supports the MDIO bus used by the gianfar and UCC drivers. - - config FSL_XGMAC_MDIO - tristate "Freescale XGMAC MDIO" -- depends on FSL_SOC - select PHYLIB - select OF_MDIO - ---help--- -- This driver supports the MDIO bus on the Fman 10G Ethernet MACs. -+ This driver supports the MDIO bus on the Fman 10G Ethernet MACs and -+ on mEMAC (which supports both Clauses 22 and 45) - - config UCC_GETH - tristate "Freescale QE Gigabit Ethernet" -diff --git a/drivers/net/ethernet/freescale/fec_mpc52xx.c b/drivers/net/ethernet/freescale/fec_mpc52xx.c -index ff55fbb..76ff046 100644 ---- a/drivers/net/ethernet/freescale/fec_mpc52xx.c -+++ b/drivers/net/ethernet/freescale/fec_mpc52xx.c -@@ -1057,7 +1057,7 @@ static int mpc52xx_fec_of_resume(struct platform_device *op) - } - #endif - --static struct of_device_id mpc52xx_fec_match[] = { -+static const struct of_device_id mpc52xx_fec_match[] = { - { .compatible = "fsl,mpc5200b-fec", }, - { .compatible = "fsl,mpc5200-fec", }, - { .compatible = "mpc5200-fec", }, -diff --git a/drivers/net/ethernet/freescale/fec_mpc52xx_phy.c b/drivers/net/ethernet/freescale/fec_mpc52xx_phy.c -index e052890..1e647be 100644 ---- a/drivers/net/ethernet/freescale/fec_mpc52xx_phy.c -+++ b/drivers/net/ethernet/freescale/fec_mpc52xx_phy.c -@@ -134,7 +134,7 @@ static int mpc52xx_fec_mdio_remove(struct platform_device *of) - return 0; - } - --static struct of_device_id mpc52xx_fec_mdio_match[] = { -+static const struct of_device_id mpc52xx_fec_mdio_match[] = { - { .compatible = "fsl,mpc5200b-mdio", }, - { .compatible = "fsl,mpc5200-mdio", }, - { .compatible = "mpc5200b-fec-phy", }, -diff --git a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c -index c92c3b7..dc0da6c 100644 ---- a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c -+++ b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c -@@ -886,7 +886,7 @@ static const struct net_device_ops fs_enet_netdev_ops = { - #endif - }; - --static struct of_device_id fs_enet_match[]; -+static const struct of_device_id fs_enet_match[]; - static int fs_enet_probe(struct platform_device *ofdev) - { - const struct of_device_id *match; -@@ -1047,7 +1047,7 @@ static int fs_enet_remove(struct platform_device *ofdev) - return 0; - } - --static struct of_device_id fs_enet_match[] = { -+static const struct of_device_id fs_enet_match[] = { - #ifdef CONFIG_FS_ENET_HAS_SCC - { - .compatible = "fsl,cpm1-scc-enet", -diff --git a/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c b/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c -index 3d3fde6..9ec396b 100644 ---- a/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c -+++ b/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c -@@ -213,7 +213,7 @@ static int fs_enet_mdio_remove(struct platform_device *ofdev) - return 0; - } - --static struct of_device_id fs_enet_mdio_bb_match[] = { -+static const struct of_device_id fs_enet_mdio_bb_match[] = { - { - .compatible = "fsl,cpm2-mdio-bitbang", - }, -diff --git a/drivers/net/ethernet/freescale/fs_enet/mii-fec.c b/drivers/net/ethernet/freescale/fs_enet/mii-fec.c -index ebf5d64..72205b0 100644 ---- a/drivers/net/ethernet/freescale/fs_enet/mii-fec.c -+++ b/drivers/net/ethernet/freescale/fs_enet/mii-fec.c -@@ -95,7 +95,7 @@ static int fs_enet_fec_mii_write(struct mii_bus *bus, int phy_id, int location, - - } - --static struct of_device_id fs_enet_mdio_fec_match[]; -+static const struct of_device_id fs_enet_mdio_fec_match[]; - static int fs_enet_mdio_probe(struct platform_device *ofdev) - { - const struct of_device_id *match; -@@ -208,7 +208,7 @@ static int fs_enet_mdio_remove(struct platform_device *ofdev) - return 0; - } - --static struct of_device_id fs_enet_mdio_fec_match[] = { -+static const struct of_device_id fs_enet_mdio_fec_match[] = { - { - .compatible = "fsl,pq1-fec-mdio", - }, -diff --git a/drivers/net/ethernet/freescale/fsl_pq_mdio.c b/drivers/net/ethernet/freescale/fsl_pq_mdio.c -index 964c6bf..f94fa63 100644 ---- a/drivers/net/ethernet/freescale/fsl_pq_mdio.c -+++ b/drivers/net/ethernet/freescale/fsl_pq_mdio.c -@@ -294,7 +294,7 @@ static void ucc_configure(phys_addr_t start, phys_addr_t end) - - #endif - --static struct of_device_id fsl_pq_mdio_match[] = { -+static const struct of_device_id fsl_pq_mdio_match[] = { - #if defined(CONFIG_GIANFAR) || defined(CONFIG_GIANFAR_MODULE) - { - .compatible = "fsl,gianfar-tbi", -diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c -index 4fdf0aa..a4a7396 100644 ---- a/drivers/net/ethernet/freescale/gianfar.c -+++ b/drivers/net/ethernet/freescale/gianfar.c -@@ -3455,7 +3455,7 @@ static noinline void gfar_update_link_state(struct gfar_private *priv) - phy_print_status(phydev); - } - --static struct of_device_id gfar_match[] = -+static const struct of_device_id gfar_match[] = - { - { - .type = "network", -diff --git a/drivers/net/ethernet/freescale/gianfar_ptp.c b/drivers/net/ethernet/freescale/gianfar_ptp.c -index bb56800..c7c75de 100644 ---- a/drivers/net/ethernet/freescale/gianfar_ptp.c -+++ b/drivers/net/ethernet/freescale/gianfar_ptp.c -@@ -554,7 +554,7 @@ static int gianfar_ptp_remove(struct platform_device *dev) - return 0; - } - --static struct of_device_id match_table[] = { -+static const struct of_device_id match_table[] = { - { .compatible = "fsl,etsec-ptp" }, - {}, - }; -diff --git a/drivers/net/ethernet/freescale/ucc_geth.c b/drivers/net/ethernet/freescale/ucc_geth.c -index 3cf0478..741a7d4 100644 ---- a/drivers/net/ethernet/freescale/ucc_geth.c -+++ b/drivers/net/ethernet/freescale/ucc_geth.c -@@ -3930,7 +3930,7 @@ static int ucc_geth_remove(struct platform_device* ofdev) - return 0; - } - --static struct of_device_id ucc_geth_match[] = { -+static const struct of_device_id ucc_geth_match[] = { - { - .type = "network", - .compatible = "ucc_geth", -diff --git a/drivers/net/ethernet/freescale/xgmac_mdio.c b/drivers/net/ethernet/freescale/xgmac_mdio.c -index 6e7db66..7b8fe86 100644 ---- a/drivers/net/ethernet/freescale/xgmac_mdio.c -+++ b/drivers/net/ethernet/freescale/xgmac_mdio.c -@@ -32,31 +32,62 @@ struct tgec_mdio_controller { - __be32 mdio_addr; /* MDIO address */ - } __packed; - -+#define MDIO_STAT_ENC BIT(6) - #define MDIO_STAT_CLKDIV(x) (((x>>1) & 0xff) << 8) --#define MDIO_STAT_BSY (1 << 0) --#define MDIO_STAT_RD_ER (1 << 1) -+#define MDIO_STAT_BSY BIT(0) -+#define MDIO_STAT_RD_ER BIT(1) - #define MDIO_CTL_DEV_ADDR(x) (x & 0x1f) - #define MDIO_CTL_PORT_ADDR(x) ((x & 0x1f) << 5) --#define MDIO_CTL_PRE_DIS (1 << 10) --#define MDIO_CTL_SCAN_EN (1 << 11) --#define MDIO_CTL_POST_INC (1 << 14) --#define MDIO_CTL_READ (1 << 15) -+#define MDIO_CTL_PRE_DIS BIT(10) -+#define MDIO_CTL_SCAN_EN BIT(11) -+#define MDIO_CTL_POST_INC BIT(14) -+#define MDIO_CTL_READ BIT(15) - - #define MDIO_DATA(x) (x & 0xffff) --#define MDIO_DATA_BSY (1 << 31) -+#define MDIO_DATA_BSY BIT(31) -+ -+struct mdio_fsl_priv { -+ struct tgec_mdio_controller __iomem *mdio_base; -+ bool is_little_endian; -+}; -+ -+static u32 xgmac_read32(void __iomem *regs, -+ bool is_little_endian) -+{ -+ if (is_little_endian) -+ return ioread32(regs); -+ else -+ return ioread32be(regs); -+} -+ -+static void xgmac_write32(u32 value, -+ void __iomem *regs, -+ bool is_little_endian) -+{ -+ if (is_little_endian) -+ iowrite32(value, regs); -+ else -+ iowrite32be(value, regs); -+} - - /* - * Wait until the MDIO bus is free - */ - static int xgmac_wait_until_free(struct device *dev, -- struct tgec_mdio_controller __iomem *regs) -+ struct tgec_mdio_controller __iomem *regs, -+ bool is_little_endian) - { -- uint32_t status; -+ unsigned int timeout; - - /* Wait till the bus is free */ -- status = spin_event_timeout( -- !((in_be32(®s->mdio_stat)) & MDIO_STAT_BSY), TIMEOUT, 0); -- if (!status) { -+ timeout = TIMEOUT; -+ while ((xgmac_read32(®s->mdio_stat, is_little_endian) & -+ MDIO_STAT_BSY) && timeout) { -+ cpu_relax(); -+ timeout--; -+ } -+ -+ if (!timeout) { - dev_err(dev, "timeout waiting for bus to be free\n"); - return -ETIMEDOUT; - } -@@ -68,14 +99,20 @@ static int xgmac_wait_until_free(struct device *dev, - * Wait till the MDIO read or write operation is complete - */ - static int xgmac_wait_until_done(struct device *dev, -- struct tgec_mdio_controller __iomem *regs) -+ struct tgec_mdio_controller __iomem *regs, -+ bool is_little_endian) - { -- uint32_t status; -+ unsigned int timeout; - - /* Wait till the MDIO write is complete */ -- status = spin_event_timeout( -- !((in_be32(®s->mdio_data)) & MDIO_DATA_BSY), TIMEOUT, 0); -- if (!status) { -+ timeout = TIMEOUT; -+ while ((xgmac_read32(®s->mdio_stat, is_little_endian) & -+ MDIO_STAT_BSY) && timeout) { -+ cpu_relax(); -+ timeout--; -+ } -+ -+ if (!timeout) { - dev_err(dev, "timeout waiting for operation to complete\n"); - return -ETIMEDOUT; - } -@@ -90,32 +127,47 @@ static int xgmac_wait_until_done(struct device *dev, - */ - static int xgmac_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 value) - { -- struct tgec_mdio_controller __iomem *regs = bus->priv; -- uint16_t dev_addr = regnum >> 16; -+ struct mdio_fsl_priv *priv = (struct mdio_fsl_priv *)bus->priv; -+ struct tgec_mdio_controller __iomem *regs = priv->mdio_base; -+ uint16_t dev_addr; -+ u32 mdio_ctl, mdio_stat; - int ret; -+ bool endian = priv->is_little_endian; -+ -+ mdio_stat = xgmac_read32(®s->mdio_stat, endian); -+ if (regnum & MII_ADDR_C45) { -+ /* Clause 45 (ie 10G) */ -+ dev_addr = (regnum >> 16) & 0x1f; -+ mdio_stat |= MDIO_STAT_ENC; -+ } else { -+ /* Clause 22 (ie 1G) */ -+ dev_addr = regnum & 0x1f; -+ mdio_stat &= ~MDIO_STAT_ENC; -+ } - -- /* Setup the MII Mgmt clock speed */ -- out_be32(®s->mdio_stat, MDIO_STAT_CLKDIV(100)); -+ xgmac_write32(mdio_stat, ®s->mdio_stat, endian); - -- ret = xgmac_wait_until_free(&bus->dev, regs); -+ ret = xgmac_wait_until_free(&bus->dev, regs, endian); - if (ret) - return ret; - - /* Set the port and dev addr */ -- out_be32(®s->mdio_ctl, -- MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr)); -+ mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr); -+ xgmac_write32(mdio_ctl, ®s->mdio_ctl, endian); - - /* Set the register address */ -- out_be32(®s->mdio_addr, regnum & 0xffff); -+ if (regnum & MII_ADDR_C45) { -+ xgmac_write32(regnum & 0xffff, ®s->mdio_addr, endian); - -- ret = xgmac_wait_until_free(&bus->dev, regs); -- if (ret) -- return ret; -+ ret = xgmac_wait_until_free(&bus->dev, regs, endian); -+ if (ret) -+ return ret; -+ } - - /* Write the value to the register */ -- out_be32(®s->mdio_data, MDIO_DATA(value)); -+ xgmac_write32(MDIO_DATA(value), ®s->mdio_data, endian); - -- ret = xgmac_wait_until_done(&bus->dev, regs); -+ ret = xgmac_wait_until_done(&bus->dev, regs, endian); - if (ret) - return ret; - -@@ -129,74 +181,70 @@ static int xgmac_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 val - */ - static int xgmac_mdio_read(struct mii_bus *bus, int phy_id, int regnum) - { -- struct tgec_mdio_controller __iomem *regs = bus->priv; -- uint16_t dev_addr = regnum >> 16; -+ struct mdio_fsl_priv *priv = (struct mdio_fsl_priv *)bus->priv; -+ struct tgec_mdio_controller __iomem *regs = priv->mdio_base; -+ uint16_t dev_addr; -+ uint32_t mdio_stat; - uint32_t mdio_ctl; - uint16_t value; - int ret; -+ bool endian = priv->is_little_endian; -+ -+ mdio_stat = xgmac_read32(®s->mdio_stat, endian); -+ if (regnum & MII_ADDR_C45) { -+ dev_addr = (regnum >> 16) & 0x1f; -+ mdio_stat |= MDIO_STAT_ENC; -+ } else { -+ dev_addr = regnum & 0x1f; -+ mdio_stat &= ~MDIO_STAT_ENC; -+ } - -- /* Setup the MII Mgmt clock speed */ -- out_be32(®s->mdio_stat, MDIO_STAT_CLKDIV(100)); -+ xgmac_write32(mdio_stat, ®s->mdio_stat, endian); - -- ret = xgmac_wait_until_free(&bus->dev, regs); -+ ret = xgmac_wait_until_free(&bus->dev, regs, endian); - if (ret) - return ret; - - /* Set the Port and Device Addrs */ - mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr); -- out_be32(®s->mdio_ctl, mdio_ctl); -+ xgmac_write32(mdio_ctl, ®s->mdio_ctl, endian); - - /* Set the register address */ -- out_be32(®s->mdio_addr, regnum & 0xffff); -+ if (regnum & MII_ADDR_C45) { -+ xgmac_write32(regnum & 0xffff, ®s->mdio_addr, endian); - -- ret = xgmac_wait_until_free(&bus->dev, regs); -- if (ret) -- return ret; -+ ret = xgmac_wait_until_free(&bus->dev, regs, endian); -+ if (ret) -+ return ret; -+ } - - /* Initiate the read */ -- out_be32(®s->mdio_ctl, mdio_ctl | MDIO_CTL_READ); -+ xgmac_write32(mdio_ctl | MDIO_CTL_READ, ®s->mdio_ctl, endian); - -- ret = xgmac_wait_until_done(&bus->dev, regs); -+ ret = xgmac_wait_until_done(&bus->dev, regs, endian); - if (ret) - return ret; - - /* Return all Fs if nothing was there */ -- if (in_be32(®s->mdio_stat) & MDIO_STAT_RD_ER) { -+ if (xgmac_read32(®s->mdio_stat, endian) & MDIO_STAT_RD_ER) { - dev_err(&bus->dev, - "Error while reading PHY%d reg at %d.%hhu\n", - phy_id, dev_addr, regnum); - return 0xffff; - } - -- value = in_be32(®s->mdio_data) & 0xffff; -+ value = xgmac_read32(®s->mdio_data, endian) & 0xffff; - dev_dbg(&bus->dev, "read %04x\n", value); - - return value; - } - --/* Reset the MIIM registers, and wait for the bus to free */ --static int xgmac_mdio_reset(struct mii_bus *bus) --{ -- struct tgec_mdio_controller __iomem *regs = bus->priv; -- int ret; -- -- mutex_lock(&bus->mdio_lock); -- -- /* Setup the MII Mgmt clock speed */ -- out_be32(®s->mdio_stat, MDIO_STAT_CLKDIV(100)); -- -- ret = xgmac_wait_until_free(&bus->dev, regs); -- -- mutex_unlock(&bus->mdio_lock); -- -- return ret; --} -- - static int xgmac_mdio_probe(struct platform_device *pdev) - { - struct device_node *np = pdev->dev.of_node; - struct mii_bus *bus; - struct resource res; -+ struct mdio_fsl_priv *priv; - int ret; - - ret = of_address_to_resource(np, 0, &res); -@@ -205,25 +253,30 @@ static int xgmac_mdio_probe(struct platform_device *pdev) - return ret; - } - -- bus = mdiobus_alloc_size(PHY_MAX_ADDR * sizeof(int)); -+ bus = mdiobus_alloc_size(sizeof(struct mdio_fsl_priv)); - if (!bus) - return -ENOMEM; - - bus->name = "Freescale XGMAC MDIO Bus"; - bus->read = xgmac_mdio_read; - bus->write = xgmac_mdio_write; -- bus->reset = xgmac_mdio_reset; -- bus->irq = bus->priv; - bus->parent = &pdev->dev; - snprintf(bus->id, MII_BUS_ID_SIZE, "%llx", (unsigned long long)res.start); - - /* Set the PHY base address */ -- bus->priv = of_iomap(np, 0); -- if (!bus->priv) { -+ priv = bus->priv; -+ priv->mdio_base = of_iomap(np, 0); -+ if (!priv->mdio_base) { - ret = -ENOMEM; - goto err_ioremap; - } - -+ if (of_get_property(pdev->dev.of_node, -+ "little-endian", NULL)) -+ priv->is_little_endian = true; -+ else -+ priv->is_little_endian = false; -+ - ret = of_mdiobus_register(bus, np); - if (ret) { - dev_err(&pdev->dev, "cannot register MDIO bus\n"); -@@ -235,7 +288,7 @@ static int xgmac_mdio_probe(struct platform_device *pdev) - return 0; - - err_registration: -- iounmap(bus->priv); -+ iounmap(priv->mdio_base); - - err_ioremap: - mdiobus_free(bus); -@@ -254,10 +307,13 @@ static int xgmac_mdio_remove(struct platform_device *pdev) - return 0; - } - --static struct of_device_id xgmac_mdio_match[] = { -+static const struct of_device_id xgmac_mdio_match[] = { - { - .compatible = "fsl,fman-xmdio", - }, -+ { -+ .compatible = "fsl,fman-memac-mdio", -+ }, - {}, - }; - MODULE_DEVICE_TABLE(of, xgmac_mdio_match); -diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig -index 75472cf..2973c60 100644 ---- a/drivers/net/phy/Kconfig -+++ b/drivers/net/phy/Kconfig -@@ -14,6 +14,11 @@ if PHYLIB - - comment "MII PHY device drivers" - -+config AQUANTIA_PHY -+ tristate "Drivers for the Aquantia PHYs" -+ ---help--- -+ Currently supports the Aquantia AQ1202, AQ2104, AQR105, AQR405 -+ - config AT803X_PHY - tristate "Drivers for Atheros AT803X PHYs" - ---help--- -diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile -index eb3b18b..b5c8f9f 100644 ---- a/drivers/net/phy/Makefile -+++ b/drivers/net/phy/Makefile -@@ -3,6 +3,7 @@ - libphy-objs := phy.o phy_device.o mdio_bus.o - - obj-$(CONFIG_PHYLIB) += libphy.o -+obj-$(CONFIG_AQUANTIA_PHY) += aquantia.o - obj-$(CONFIG_MARVELL_PHY) += marvell.o - obj-$(CONFIG_DAVICOM_PHY) += davicom.o - obj-$(CONFIG_CICADA_PHY) += cicada.o -diff --git a/drivers/net/phy/aquantia.c b/drivers/net/phy/aquantia.c -new file mode 100644 -index 0000000..d6111af ---- /dev/null -+++ b/drivers/net/phy/aquantia.c -@@ -0,0 +1,201 @@ -+/* -+ * Driver for Aquantia PHY -+ * -+ * Author: Shaohui Xie -+ * -+ * Copyright 2015 Freescale Semiconductor, Inc. -+ * -+ * This file is licensed under the terms of the GNU General Public License -+ * version 2. This program is licensed "as is" without any warranty of any -+ * kind, whether express or implied. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define PHY_ID_AQ1202 0x03a1b445 -+#define PHY_ID_AQ2104 0x03a1b460 -+#define PHY_ID_AQR105 0x03a1b4a2 -+#define PHY_ID_AQR405 0x03a1b4b0 -+ -+#define PHY_AQUANTIA_FEATURES (SUPPORTED_10000baseT_Full | \ -+ SUPPORTED_1000baseT_Full | \ -+ SUPPORTED_100baseT_Full | \ -+ PHY_DEFAULT_FEATURES) -+ -+static int aquantia_config_aneg(struct phy_device *phydev) -+{ -+ phydev->supported = PHY_AQUANTIA_FEATURES; -+ phydev->advertising = phydev->supported; -+ -+ return 0; -+} -+ -+static int aquantia_aneg_done(struct phy_device *phydev) -+{ -+ int reg; -+ -+ reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); -+ return (reg < 0) ? reg : (reg & BMSR_ANEGCOMPLETE); -+} -+ -+static int aquantia_config_intr(struct phy_device *phydev) -+{ -+ int err; -+ -+ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { -+ err = phy_write_mmd(phydev, MDIO_MMD_AN, 0xd401, 1); -+ if (err < 0) -+ return err; -+ -+ err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff00, 1); -+ if (err < 0) -+ return err; -+ -+ err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff01, 0x1001); -+ } else { -+ err = phy_write_mmd(phydev, MDIO_MMD_AN, 0xd401, 0); -+ if (err < 0) -+ return err; -+ -+ err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff00, 0); -+ if (err < 0) -+ return err; -+ -+ err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff01, 0); -+ } -+ -+ return err; -+} -+ -+static int aquantia_ack_interrupt(struct phy_device *phydev) -+{ -+ int reg; -+ -+ reg = phy_read_mmd(phydev, MDIO_MMD_AN, 0xcc01); -+ return (reg < 0) ? reg : 0; -+} -+ -+static int aquantia_read_status(struct phy_device *phydev) -+{ -+ int reg; -+ -+ reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); -+ reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); -+ if (reg & MDIO_STAT1_LSTATUS) -+ phydev->link = 1; -+ else -+ phydev->link = 0; -+ -+ reg = phy_read_mmd(phydev, MDIO_MMD_AN, 0xc800); -+ mdelay(10); -+ reg = phy_read_mmd(phydev, MDIO_MMD_AN, 0xc800); -+ -+ switch (reg) { -+ case 0x9: -+ phydev->speed = SPEED_2500; -+ break; -+ case 0x5: -+ phydev->speed = SPEED_1000; -+ break; -+ case 0x3: -+ phydev->speed = SPEED_100; -+ break; -+ case 0x7: -+ default: -+ phydev->speed = SPEED_10000; -+ break; -+ } -+ phydev->duplex = DUPLEX_FULL; -+ -+ return 0; -+} -+ -+static struct phy_driver aquantia_driver[] = { -+{ -+ .phy_id = PHY_ID_AQ1202, -+ .phy_id_mask = 0xfffffff0, -+ .name = "Aquantia AQ1202", -+ .features = PHY_AQUANTIA_FEATURES, -+ .flags = PHY_HAS_INTERRUPT, -+ .aneg_done = aquantia_aneg_done, -+ .config_aneg = aquantia_config_aneg, -+ .config_intr = aquantia_config_intr, -+ .ack_interrupt = aquantia_ack_interrupt, -+ .read_status = aquantia_read_status, -+ .driver = { .owner = THIS_MODULE,}, -+}, -+{ -+ .phy_id = PHY_ID_AQ2104, -+ .phy_id_mask = 0xfffffff0, -+ .name = "Aquantia AQ2104", -+ .features = PHY_AQUANTIA_FEATURES, -+ .flags = PHY_HAS_INTERRUPT, -+ .aneg_done = aquantia_aneg_done, -+ .config_aneg = aquantia_config_aneg, -+ .config_intr = aquantia_config_intr, -+ .ack_interrupt = aquantia_ack_interrupt, -+ .read_status = aquantia_read_status, -+ .driver = { .owner = THIS_MODULE,}, -+}, -+{ -+ .phy_id = PHY_ID_AQR105, -+ .phy_id_mask = 0xfffffff0, -+ .name = "Aquantia AQR105", -+ .features = PHY_AQUANTIA_FEATURES, -+ .flags = PHY_HAS_INTERRUPT, -+ .aneg_done = aquantia_aneg_done, -+ .config_aneg = aquantia_config_aneg, -+ .config_intr = aquantia_config_intr, -+ .ack_interrupt = aquantia_ack_interrupt, -+ .read_status = aquantia_read_status, -+ .driver = { .owner = THIS_MODULE,}, -+}, -+{ -+ .phy_id = PHY_ID_AQR405, -+ .phy_id_mask = 0xfffffff0, -+ .name = "Aquantia AQR405", -+ .features = PHY_AQUANTIA_FEATURES, -+ .flags = PHY_HAS_INTERRUPT, -+ .aneg_done = aquantia_aneg_done, -+ .config_aneg = aquantia_config_aneg, -+ .config_intr = aquantia_config_intr, -+ .ack_interrupt = aquantia_ack_interrupt, -+ .read_status = aquantia_read_status, -+ .driver = { .owner = THIS_MODULE,}, -+}, -+}; -+ -+static int __init aquantia_init(void) -+{ -+ return phy_drivers_register(aquantia_driver, -+ ARRAY_SIZE(aquantia_driver)); -+} -+ -+static void __exit aquantia_exit(void) -+{ -+ return phy_drivers_unregister(aquantia_driver, -+ ARRAY_SIZE(aquantia_driver)); -+} -+ -+module_init(aquantia_init); -+module_exit(aquantia_exit); -+ -+static struct mdio_device_id __maybe_unused aquantia_tbl[] = { -+ { PHY_ID_AQ1202, 0xfffffff0 }, -+ { PHY_ID_AQ2104, 0xfffffff0 }, -+ { PHY_ID_AQR105, 0xfffffff0 }, -+ { PHY_ID_AQR405, 0xfffffff0 }, -+ { } -+}; -+ -+MODULE_DEVICE_TABLE(mdio, aquantia_tbl); -+ -+MODULE_DESCRIPTION("Aquantia PHY driver"); -+MODULE_AUTHOR("Shaohui Xie "); -+MODULE_LICENSE("GPL v2"); -diff --git a/drivers/net/phy/fsl_10gkr.c b/drivers/net/phy/fsl_10gkr.c -new file mode 100644 -index 0000000..3713726 ---- /dev/null -+++ b/drivers/net/phy/fsl_10gkr.c -@@ -0,0 +1,1467 @@ -+/* Freescale XFI 10GBASE-KR driver. -+ * Author: Shaohui Xie -+ * -+ * Copyright 2014 Freescale Semiconductor, Inc. -+ * -+ * Licensed under the GPL-2 or later. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define FSL_XFI_PCS_PHY_ID 0x7C000012 -+#define FSL_XFI_PCS_PHY_ID2 0x0083e400 -+ -+/* Freescale XFI PCS MMD */ -+#define FSL_XFI_PMD 0x1 -+#define FSL_XFI_PCS 0x3 -+#define FSL_XFI_AN 0x7 -+#define FSL_XFI_VS1 0x1e -+ -+/* Freescale XFI PMD registers */ -+#define FSL_XFI_PMD_CTRL 0x0 -+#define FSL_XFI_KR_PMD_CTRL 0x0096 -+#define FSL_XFI_KR_PMD_STATUS 0x0097 -+#define FSL_XFI_KR_LP_CU 0x0098 -+#define FSL_XFI_KR_LP_STATUS 0x0099 -+#define FSL_XFI_KR_LD_CU 0x009a -+#define FSL_XFI_KR_LD_STATUS 0x009b -+ -+/* PMD define */ -+#define PMD_RESET 0x1 -+#define PMD_STATUS_SUP_STAT 0x4 -+#define PMD_STATUS_FRAME_LOCK 0x2 -+#define TRAIN_EN 0x3 -+#define TRAIN_DISABLE 0x1 -+#define RX_STAT 0x1 -+ -+/* Freescale XFI PCS registers */ -+#define FSL_XFI_PCS_CTRL 0x0 -+#define FSL_XFI_PCS_STATUS 0x1 -+ -+/* Freescale XFI Auto-Negotiation Registers */ -+#define FSL_XFI_AN_CTRL 0x0000 -+#define FSL_XFI_LNK_STATUS 0x0001 -+#define FSL_XFI_AN_AD_1 0x0011 -+#define FSL_XFI_BP_STATUS 0x0030 -+ -+#define XFI_AN_AD1 0x85 -+#define XF_AN_RESTART 0x1200 -+#define XFI_AN_LNK_STAT_UP 0x4 -+ -+/* Freescale XFI Vendor-Specific 1 Registers */ -+#define FSL_XFI_PCS_INTR_EVENT 0x0002 -+#define FSL_XFI_PCS_INTR_MASK 0x0003 -+#define FSL_XFI_AN_INTR_EVENT 0x0004 -+#define FSL_XFI_AN_INTR_MASK 0x0005 -+#define FSL_XFI_LT_INTR_EVENT 0x0006 -+#define FSL_XFI_LT_INTR_MASK 0x0007 -+ -+/* C(-1) */ -+#define BIN_M1 0 -+/* C(1) */ -+#define BIN_LONG 1 -+#define BIN_M1_SEL 6 -+#define BIN_Long_SEL 7 -+#define CDR_SEL_MASK 0x00070000 -+#define BIN_SNAPSHOT_NUM 5 -+#define BIN_M1_THRESHOLD 3 -+#define BIN_LONG_THRESHOLD 2 -+ -+#define PRE_COE_MASK 0x03c00000 -+#define POST_COE_MASK 0x001f0000 -+#define ZERO_COE_MASK 0x00003f00 -+#define PRE_COE_SHIFT 22 -+#define POST_COE_SHIFT 16 -+#define ZERO_COE_SHIFT 8 -+ -+#define PRE_COE_MAX 0x0 -+#define PRE_COE_MIN 0x8 -+#define POST_COE_MAX 0x0 -+#define POST_COE_MIN 0x10 -+#define ZERO_COE_MAX 0x30 -+#define ZERO_COE_MIN 0x0 -+ -+#define TECR0_INIT 0x24200000 -+#define RATIO_PREQ 0x3 -+#define RATIO_PST1Q 0xd -+#define RATIO_EQ 0x20 -+ -+#define GCR1_CTL_SNP_START_MASK 0x00002000 -+#define GCR1_SNP_START_MASK 0x00000040 -+#define RECR1_SNP_DONE_MASK 0x00000004 -+#define RECR1_CTL_SNP_DONE_MASK 0x00000002 -+#define TCSR1_SNP_DATA_MASK 0x0000ffc0 -+#define TCSR1_SNP_DATA_SHIFT 6 -+#define TCSR1_EQ_SNPBIN_SIGN_MASK 0x100 -+ -+#define RECR1_GAINK2_MASK 0x0f000000 -+#define RECR1_GAINK2_SHIFT 24 -+#define RECR1_GAINK3_MASK 0x000f0000 -+#define RECR1_GAINK3_SHIFT 16 -+#define RECR1_OFFSET_MASK 0x00003f80 -+#define RECR1_OFFSET_SHIFT 7 -+#define RECR1_BLW_MASK 0x00000f80 -+#define RECR1_BLW_SHIFT 7 -+#define EYE_CTRL_SHIFT 12 -+#define BASE_WAND_SHIFT 10 -+ -+#define XGKR_TIMEOUT 1050 -+#define AN_ABILITY_MASK 0x9 -+#define AN_10GKR_MASK 0x8 -+#define LT_10GKR_MASK 0x4 -+#define TRAIN_FAIL 0x8 -+ -+#define INCREMENT 1 -+#define DECREMENT 2 -+#define TIMEOUT_LONG 3 -+#define TIMEOUT_M1 3 -+ -+#define RX_READY_MASK 0x8000 -+#define PRESET_MASK 0x2000 -+#define INIT_MASK 0x1000 -+#define COP1_MASK 0x30 -+#define COP1_SHIFT 4 -+#define COZ_MASK 0xc -+#define COZ_SHIFT 2 -+#define COM1_MASK 0x3 -+#define COM1_SHIFT 0 -+#define REQUEST_MASK 0x3f -+#define LD_ALL_MASK (PRESET_MASK | INIT_MASK | \ -+ COP1_MASK | COZ_MASK | COM1_MASK) -+ -+#define FSL_SERDES_INSTANCE1_BASE 0xffe0ea000 -+#define FSL_SERDES_INSTANCE2_BASE 0xffe0eb000 -+#define FSL_LANE_A_BASE 0x800 -+#define FSL_LANE_B_BASE 0x840 -+#define FSL_LANE_C_BASE 0x880 -+#define FSL_LANE_D_BASE 0x8C0 -+#define FSL_LANE_E_BASE 0x900 -+#define FSL_LANE_F_BASE 0x940 -+#define FSL_LANE_G_BASE 0x980 -+#define FSL_LANE_H_BASE 0x9C0 -+#define GCR0_RESET_MASK 0x600000 -+ -+#define NEW_ALGORITHM_TRAIN_TX -+#ifdef NEW_ALGORITHM_TRAIN_TX -+#define FORCE_INC_COP1_NUMBER 0 -+#define FORCE_INC_COM1_NUMBER 1 -+#endif -+ -+enum fsl_xgkr_driver { -+ FSL_XGKR_REV1, -+ FSL_XGKR_REV2, -+ FSL_XGKR_INV -+}; -+ -+static struct phy_driver fsl_xgkr_driver[FSL_XGKR_INV]; -+ -+enum coe_filed { -+ COE_COP1, -+ COE_COZ, -+ COE_COM -+}; -+ -+enum coe_update { -+ COE_NOTUPDATED, -+ COE_UPDATED, -+ COE_MIN, -+ COE_MAX, -+ COE_INV -+}; -+ -+enum serdes_inst { -+ SERDES_1, -+ SERDES_2, -+ SERDES_MAX -+}; -+ -+enum lane_inst { -+ LANE_A, -+ LANE_B, -+ LANE_C, -+ LANE_D, -+ LANE_E, -+ LANE_F, -+ LANE_G, -+ LANE_H, -+ LANE_MAX -+}; -+ -+struct serdes_map { -+ const char *serdes_name; -+ unsigned long serdes_base; -+}; -+ -+struct lane_map { -+ const char *lane_name; -+ unsigned long lane_base; -+}; -+ -+const struct serdes_map s_map[SERDES_MAX] = { -+ {"serdes-1", FSL_SERDES_INSTANCE1_BASE}, -+ {"serdes-2", FSL_SERDES_INSTANCE2_BASE} -+}; -+ -+const struct lane_map l_map[LANE_MAX] = { -+ {"lane-a", FSL_LANE_A_BASE}, -+ {"lane-b", FSL_LANE_B_BASE}, -+ {"lane-c", FSL_LANE_C_BASE}, -+ {"lane-d", FSL_LANE_D_BASE}, -+ {"lane-e", FSL_LANE_E_BASE}, -+ {"lane-f", FSL_LANE_F_BASE}, -+ {"lane-g", FSL_LANE_G_BASE}, -+ {"lane-h", FSL_LANE_H_BASE} -+}; -+ -+struct per_lane_ctrl_status { -+ __be32 gcr0; /* 0x.000 - General Control Register 0 */ -+ __be32 gcr1; /* 0x.004 - General Control Register 1 */ -+ __be32 gcr2; /* 0x.008 - General Control Register 2 */ -+ __be32 resv1; /* 0x.00C - Reserved */ -+ __be32 recr0; /* 0x.010 - Receive Equalization Control Register 0 */ -+ __be32 recr1; /* 0x.014 - Receive Equalization Control Register 1 */ -+ __be32 tecr0; /* 0x.018 - Transmit Equalization Control Register 0 */ -+ __be32 resv2; /* 0x.01C - Reserved */ -+ __be32 tlcr0; /* 0x.020 - TTL Control Register 0 */ -+ __be32 tlcr1; /* 0x.024 - TTL Control Register 1 */ -+ __be32 tlcr2; /* 0x.028 - TTL Control Register 2 */ -+ __be32 tlcr3; /* 0x.02C - TTL Control Register 3 */ -+ __be32 tcsr0; /* 0x.030 - Test Control/Status Register 0 */ -+ __be32 tcsr1; /* 0x.034 - Test Control/Status Register 1 */ -+ __be32 tcsr2; /* 0x.038 - Test Control/Status Register 2 */ -+ __be32 tcsr3; /* 0x.03C - Test Control/Status Register 3 */ -+}; -+ -+struct training_state_machine { -+ bool bin_m1_late_early; -+ bool bin_long_late_early; -+ bool bin_m1_stop; -+ bool bin_long_stop; -+ bool tx_complete; -+ bool an_ok; -+ bool link_up; -+ bool running; -+ bool sent_init; -+ int m1_min_max_cnt; -+ int long_min_max_cnt; -+#ifdef NEW_ALGORITHM_TRAIN_TX -+ int pre_inc; -+ int post_inc; -+#endif -+}; -+ -+struct fsl_xgkr_inst { -+ void *reg_base; -+ struct mii_bus *bus; -+ struct phy_device *phydev; -+ struct training_state_machine t_s_m; -+ u32 ld_update; -+ u32 ld_status; -+ u32 ratio_preq; -+ u32 ratio_pst1q; -+ u32 adpt_eq; -+}; -+ -+struct fsl_xgkr_wk { -+ struct work_struct xgkr_wk; -+ struct list_head xgkr_list; -+ struct fsl_xgkr_inst *xgkr_inst; -+}; -+ -+LIST_HEAD(fsl_xgkr_list); -+ -+static struct timer_list xgkr_timer; -+static int fire_timer; -+static struct workqueue_struct *xgkr_wq; -+ -+static void init_state_machine(struct training_state_machine *s_m) -+{ -+ s_m->bin_m1_late_early = true; -+ s_m->bin_long_late_early = false; -+ s_m->bin_m1_stop = false; -+ s_m->bin_long_stop = false; -+ s_m->tx_complete = false; -+ s_m->an_ok = false; -+ s_m->link_up = false; -+ s_m->running = false; -+ s_m->sent_init = false; -+ s_m->m1_min_max_cnt = 0; -+ s_m->long_min_max_cnt = 0; -+#ifdef NEW_ALGORITHM_TRAIN_TX -+ s_m->pre_inc = FORCE_INC_COM1_NUMBER; -+ s_m->post_inc = FORCE_INC_COP1_NUMBER; -+#endif -+} -+ -+void tune_tecr0(struct fsl_xgkr_inst *inst) -+{ -+ struct per_lane_ctrl_status *reg_base; -+ u32 val; -+ -+ reg_base = (struct per_lane_ctrl_status *)inst->reg_base; -+ -+ val = TECR0_INIT | -+ inst->adpt_eq << ZERO_COE_SHIFT | -+ inst->ratio_preq << PRE_COE_SHIFT | -+ inst->ratio_pst1q << POST_COE_SHIFT; -+ -+ /* reset the lane */ -+ iowrite32be(ioread32be(®_base->gcr0) & ~GCR0_RESET_MASK, -+ ®_base->gcr0); -+ udelay(1); -+ iowrite32be(val, ®_base->tecr0); -+ udelay(1); -+ /* unreset the lane */ -+ iowrite32be(ioread32be(®_base->gcr0) | GCR0_RESET_MASK, -+ ®_base->gcr0); -+ udelay(1); -+} -+ -+static void start_lt(struct phy_device *phydev) -+{ -+ phy_write_mmd(phydev, FSL_XFI_PMD, FSL_XFI_KR_PMD_CTRL, TRAIN_EN); -+} -+ -+static void stop_lt(struct phy_device *phydev) -+{ -+ phy_write_mmd(phydev, FSL_XFI_PMD, FSL_XFI_KR_PMD_CTRL, TRAIN_DISABLE); -+} -+ -+static void reset_gcr0(struct fsl_xgkr_inst *inst) -+{ -+ struct per_lane_ctrl_status *reg_base; -+ -+ reg_base = (struct per_lane_ctrl_status *)inst->reg_base; -+ -+ iowrite32be(ioread32be(®_base->gcr0) & ~GCR0_RESET_MASK, -+ ®_base->gcr0); -+ udelay(1); -+ iowrite32be(ioread32be(®_base->gcr0) | GCR0_RESET_MASK, -+ ®_base->gcr0); -+ udelay(1); -+} -+ -+static void reset_lt(struct phy_device *phydev) -+{ -+ phy_write_mmd(phydev, FSL_XFI_PMD, FSL_XFI_PMD_CTRL, PMD_RESET); -+ phy_write_mmd(phydev, FSL_XFI_PMD, FSL_XFI_KR_PMD_CTRL, TRAIN_DISABLE); -+ phy_write_mmd(phydev, FSL_XFI_PMD, FSL_XFI_KR_LD_CU, 0); -+ phy_write_mmd(phydev, FSL_XFI_PMD, FSL_XFI_KR_LD_STATUS, 0); -+ phy_write_mmd(phydev, FSL_XFI_PMD, FSL_XFI_KR_PMD_STATUS, 0); -+ phy_write_mmd(phydev, FSL_XFI_PMD, FSL_XFI_KR_LP_CU, 0); -+ phy_write_mmd(phydev, FSL_XFI_PMD, FSL_XFI_KR_LP_STATUS, 0); -+} -+ -+static void start_an(struct phy_device *phydev) -+{ -+ reset_lt(phydev); -+ phy_write_mmd(phydev, FSL_XFI_AN, FSL_XFI_AN_AD_1, XFI_AN_AD1); -+ phy_write_mmd(phydev, FSL_XFI_AN, FSL_XFI_AN_CTRL, XF_AN_RESTART); -+} -+ -+static void ld_coe_status(struct fsl_xgkr_inst *inst) -+{ -+ phy_write_mmd(inst->phydev, FSL_XFI_PMD, -+ FSL_XFI_KR_LD_STATUS, inst->ld_status); -+} -+ -+static void ld_coe_update(struct fsl_xgkr_inst *inst) -+{ -+ phy_write_mmd(inst->phydev, FSL_XFI_PMD, -+ FSL_XFI_KR_LD_CU, inst->ld_update); -+} -+ -+static void init_inst(struct fsl_xgkr_inst *inst, int reset) -+{ -+ if (reset) { -+ inst->ratio_preq = RATIO_PREQ; -+ inst->ratio_pst1q = RATIO_PST1Q; -+ inst->adpt_eq = RATIO_EQ; -+ tune_tecr0(inst); -+ } -+ -+ inst->ld_status &= RX_READY_MASK; -+ ld_coe_status(inst); -+ -+ /* init state machine */ -+ init_state_machine(&inst->t_s_m); -+ -+ inst->ld_update = 0; -+ ld_coe_update(inst); -+ -+ inst->ld_status &= ~RX_READY_MASK; -+ ld_coe_status(inst); -+} -+ -+#ifdef NEW_ALGORITHM_TRAIN_TX -+static int get_median_gaink2(u32 *reg) -+{ -+ int gaink2_snap_shot[BIN_SNAPSHOT_NUM]; -+ u32 rx_eq_snp; -+ struct per_lane_ctrl_status *reg_base; -+ int timeout; -+ int i, j, tmp, pos; -+ -+ reg_base = (struct per_lane_ctrl_status *)reg; -+ -+ for (i = 0; i < BIN_SNAPSHOT_NUM; i++) { -+ /* wait RECR1_CTL_SNP_DONE_MASK has cleared */ -+ timeout = 100; -+ while (ioread32be(®_base->recr1) & -+ RECR1_CTL_SNP_DONE_MASK) { -+ udelay(1); -+ timeout--; -+ if (timeout == 0) -+ break; -+ } -+ -+ /* start snap shot */ -+ iowrite32be((ioread32be(®_base->gcr1) | -+ GCR1_CTL_SNP_START_MASK), -+ ®_base->gcr1); -+ -+ /* wait for SNP done */ -+ timeout = 100; -+ while (!(ioread32be(®_base->recr1) & -+ RECR1_CTL_SNP_DONE_MASK)) { -+ udelay(1); -+ timeout--; -+ if (timeout == 0) -+ break; -+ } -+ -+ /* read and save the snap shot */ -+ rx_eq_snp = ioread32be(®_base->recr1); -+ gaink2_snap_shot[i] = (rx_eq_snp & RECR1_GAINK2_MASK) >> -+ RECR1_GAINK2_SHIFT; -+ -+ /* terminate the snap shot by setting GCR1[REQ_CTL_SNP] */ -+ iowrite32be((ioread32be(®_base->gcr1) & -+ ~GCR1_CTL_SNP_START_MASK), -+ ®_base->gcr1); -+ } -+ -+ /* get median of the 5 snap shot */ -+ for (i = 0; i < BIN_SNAPSHOT_NUM - 1; i++) { -+ tmp = gaink2_snap_shot[i]; -+ pos = i; -+ for (j = i + 1; j < BIN_SNAPSHOT_NUM; j++) { -+ if (gaink2_snap_shot[j] < tmp) { -+ tmp = gaink2_snap_shot[j]; -+ pos = j; -+ } -+ } -+ -+ gaink2_snap_shot[pos] = gaink2_snap_shot[i]; -+ gaink2_snap_shot[i] = tmp; -+ } -+ -+ return gaink2_snap_shot[2]; -+} -+#endif -+ -+static bool is_bin_early(int bin_sel, void __iomem *reg) -+{ -+ bool early = false; -+ int bin_snap_shot[BIN_SNAPSHOT_NUM]; -+ int i, negative_count = 0; -+ struct per_lane_ctrl_status *reg_base; -+ int timeout; -+ -+ reg_base = (struct per_lane_ctrl_status *)reg; -+ -+ for (i = 0; i < BIN_SNAPSHOT_NUM; i++) { -+ /* wait RECR1_SNP_DONE_MASK has cleared */ -+ timeout = 100; -+ while ((ioread32be(®_base->recr1) & RECR1_SNP_DONE_MASK)) { -+ udelay(1); -+ timeout--; -+ if (timeout == 0) -+ break; -+ } -+ -+ /* set TCSR1[CDR_SEL] to BinM1/BinLong */ -+ if (bin_sel == BIN_M1) { -+ iowrite32be((ioread32be(®_base->tcsr1) & -+ ~CDR_SEL_MASK) | BIN_M1_SEL, -+ ®_base->tcsr1); -+ } else { -+ iowrite32be((ioread32be(®_base->tcsr1) & -+ ~CDR_SEL_MASK) | BIN_Long_SEL, -+ ®_base->tcsr1); -+ } -+ -+ /* start snap shot */ -+ iowrite32be(ioread32be(®_base->gcr1) | GCR1_SNP_START_MASK, -+ ®_base->gcr1); -+ -+ /* wait for SNP done */ -+ timeout = 100; -+ while (!(ioread32be(®_base->recr1) & RECR1_SNP_DONE_MASK)) { -+ udelay(1); -+ timeout--; -+ if (timeout == 0) -+ break; -+ } -+ -+ /* read and save the snap shot */ -+ bin_snap_shot[i] = (ioread32be(®_base->tcsr1) & -+ TCSR1_SNP_DATA_MASK) >> TCSR1_SNP_DATA_SHIFT; -+ if (bin_snap_shot[i] & TCSR1_EQ_SNPBIN_SIGN_MASK) -+ negative_count++; -+ -+ /* terminate the snap shot by setting GCR1[REQ_CTL_SNP] */ -+ iowrite32be(ioread32be(®_base->gcr1) & ~GCR1_SNP_START_MASK, -+ ®_base->gcr1); -+ } -+ -+ if (((bin_sel == BIN_M1) && negative_count > BIN_M1_THRESHOLD) || -+ ((bin_sel == BIN_LONG && negative_count > BIN_LONG_THRESHOLD))) { -+ early = true; -+ } -+ -+ return early; -+} -+ -+static void train_tx(struct fsl_xgkr_inst *inst) -+{ -+ struct phy_device *phydev = inst->phydev; -+ struct training_state_machine *s_m = &inst->t_s_m; -+ bool bin_m1_early, bin_long_early; -+ u32 lp_status, old_ld_update; -+ u32 status_cop1, status_coz, status_com1; -+ u32 req_cop1, req_coz, req_com1, req_preset, req_init; -+ u32 temp; -+#ifdef NEW_ALGORITHM_TRAIN_TX -+ u32 median_gaink2; -+#endif -+ -+recheck: -+ if (s_m->bin_long_stop && s_m->bin_m1_stop) { -+ s_m->tx_complete = true; -+ inst->ld_status |= RX_READY_MASK; -+ ld_coe_status(inst); -+ /* tell LP we are ready */ -+ phy_write_mmd(phydev, FSL_XFI_PMD, -+ FSL_XFI_KR_PMD_STATUS, RX_STAT); -+ return; -+ } -+ -+ /* We start by checking the current LP status. If we got any responses, -+ * we can clear up the appropriate update request so that the -+ * subsequent code may easily issue new update requests if needed. -+ */ -+ lp_status = phy_read_mmd(phydev, FSL_XFI_PMD, FSL_XFI_KR_LP_STATUS) & -+ REQUEST_MASK; -+ status_cop1 = (lp_status & COP1_MASK) >> COP1_SHIFT; -+ status_coz = (lp_status & COZ_MASK) >> COZ_SHIFT; -+ status_com1 = (lp_status & COM1_MASK) >> COM1_SHIFT; -+ -+ old_ld_update = inst->ld_update; -+ req_cop1 = (old_ld_update & COP1_MASK) >> COP1_SHIFT; -+ req_coz = (old_ld_update & COZ_MASK) >> COZ_SHIFT; -+ req_com1 = (old_ld_update & COM1_MASK) >> COM1_SHIFT; -+ req_preset = old_ld_update & PRESET_MASK; -+ req_init = old_ld_update & INIT_MASK; -+ -+ /* IEEE802.3-2008, 72.6.10.2.3.1 -+ * We may clear PRESET when all coefficients show UPDATED or MAX. -+ */ -+ if (req_preset) { -+ if ((status_cop1 == COE_UPDATED || status_cop1 == COE_MAX) && -+ (status_coz == COE_UPDATED || status_coz == COE_MAX) && -+ (status_com1 == COE_UPDATED || status_com1 == COE_MAX)) { -+ inst->ld_update &= ~PRESET_MASK; -+ } -+ } -+ -+ /* IEEE802.3-2008, 72.6.10.2.3.2 -+ * We may clear INITIALIZE when no coefficients show NOT UPDATED. -+ */ -+ if (req_init) { -+ if (status_cop1 != COE_NOTUPDATED && -+ status_coz != COE_NOTUPDATED && -+ status_com1 != COE_NOTUPDATED) { -+ inst->ld_update &= ~INIT_MASK; -+ } -+ } -+ -+ /* IEEE802.3-2008, 72.6.10.2.3.2 -+ * we send initialize to the other side to ensure default settings -+ * for the LP. Naturally, we should do this only once. -+ */ -+ if (!s_m->sent_init) { -+ if (!lp_status && !(old_ld_update & (LD_ALL_MASK))) { -+ inst->ld_update |= INIT_MASK; -+ s_m->sent_init = true; -+ } -+ } -+ -+ /* IEEE802.3-2008, 72.6.10.2.3.3 -+ * We set coefficient requests to HOLD when we get the information -+ * about any updates On clearing our prior response, we also update -+ * our internal status. -+ */ -+ if (status_cop1 != COE_NOTUPDATED) { -+ if (req_cop1) { -+ inst->ld_update &= ~COP1_MASK; -+#ifdef NEW_ALGORITHM_TRAIN_TX -+ if (s_m->post_inc) { -+ if (req_cop1 == INCREMENT && -+ status_cop1 == COE_MAX) { -+ s_m->post_inc = 0; -+ s_m->bin_long_stop = true; -+ s_m->bin_m1_stop = true; -+ } else { -+ s_m->post_inc -= 1; -+ } -+ -+ ld_coe_update(inst); -+ goto recheck; -+ } -+#endif -+ if ((req_cop1 == DECREMENT && status_cop1 == COE_MIN) || -+ (req_cop1 == INCREMENT && status_cop1 == COE_MAX)) { -+ s_m->long_min_max_cnt++; -+ if (s_m->long_min_max_cnt >= TIMEOUT_LONG) { -+ s_m->bin_long_stop = true; -+ ld_coe_update(inst); -+ goto recheck; -+ } -+ } -+ } -+ } -+ -+ if (status_coz != COE_NOTUPDATED) { -+ if (req_coz) -+ inst->ld_update &= ~COZ_MASK; -+ } -+ -+ if (status_com1 != COE_NOTUPDATED) { -+ if (req_com1) { -+ inst->ld_update &= ~COM1_MASK; -+#ifdef NEW_ALGORITHM_TRAIN_TX -+ if (s_m->pre_inc) { -+ if (req_com1 == INCREMENT && -+ status_com1 == COE_MAX) -+ s_m->pre_inc = 0; -+ else -+ s_m->pre_inc -= 1; -+ -+ ld_coe_update(inst); -+ goto recheck; -+ } -+#endif -+ /* Stop If we have reached the limit for a parameter. */ -+ if ((req_com1 == DECREMENT && status_com1 == COE_MIN) || -+ (req_com1 == INCREMENT && status_com1 == COE_MAX)) { -+ s_m->m1_min_max_cnt++; -+ if (s_m->m1_min_max_cnt >= TIMEOUT_M1) { -+ s_m->bin_m1_stop = true; -+ ld_coe_update(inst); -+ goto recheck; -+ } -+ } -+ } -+ } -+ -+ if (old_ld_update != inst->ld_update) { -+ ld_coe_update(inst); -+ /* Redo these status checks and updates until we have no more -+ * changes, to speed up the overall process. -+ */ -+ goto recheck; -+ } -+ -+ /* Do nothing if we have pending request. */ -+ if ((req_coz || req_com1 || req_cop1)) -+ return; -+ else if (lp_status) -+ /* No pending request but LP status was not reverted to -+ * not updated. -+ */ -+ return; -+ -+#ifdef NEW_ALGORITHM_TRAIN_TX -+ if (!(inst->ld_update & (PRESET_MASK | INIT_MASK))) { -+ if (s_m->pre_inc) { -+ inst->ld_update = INCREMENT << COM1_SHIFT; -+ ld_coe_update(inst); -+ return; -+ } -+ -+ if (status_cop1 != COE_MAX) { -+ median_gaink2 = get_median_gaink2(inst->reg_base); -+ if (median_gaink2 == 0xf) { -+ s_m->post_inc = 1; -+ } else { -+ /* Gaink2 median lower than "F" */ -+ s_m->bin_m1_stop = true; -+ s_m->bin_long_stop = true; -+ goto recheck; -+ } -+ } else { -+ /* C1 MAX */ -+ s_m->bin_m1_stop = true; -+ s_m->bin_long_stop = true; -+ goto recheck; -+ } -+ -+ if (s_m->post_inc) { -+ inst->ld_update = INCREMENT << COP1_SHIFT; -+ ld_coe_update(inst); -+ return; -+ } -+ } -+#endif -+ -+ /* snapshot and select bin */ -+ bin_m1_early = is_bin_early(BIN_M1, inst->reg_base); -+ bin_long_early = is_bin_early(BIN_LONG, inst->reg_base); -+ -+ if (!s_m->bin_m1_stop && !s_m->bin_m1_late_early && bin_m1_early) { -+ s_m->bin_m1_stop = true; -+ goto recheck; -+ } -+ -+ if (!s_m->bin_long_stop && -+ s_m->bin_long_late_early && !bin_long_early) { -+ s_m->bin_long_stop = true; -+ goto recheck; -+ } -+ -+ /* IEEE802.3-2008, 72.6.10.2.3.3 -+ * We only request coefficient updates when no PRESET/INITIALIZE is -+ * pending! We also only request coefficient updates when the -+ * corresponding status is NOT UPDATED and nothing is pending. -+ */ -+ if (!(inst->ld_update & (PRESET_MASK | INIT_MASK))) { -+ if (!s_m->bin_long_stop) { -+ /* BinM1 correction means changing COM1 */ -+ if (!status_com1 && !(inst->ld_update & COM1_MASK)) { -+ /* Avoid BinM1Late by requesting an -+ * immediate decrement. -+ */ -+ if (!bin_m1_early) { -+ /* request decrement c(-1) */ -+ temp = DECREMENT << COM1_SHIFT; -+ inst->ld_update |= temp; -+ ld_coe_update(inst); -+ s_m->bin_m1_late_early = bin_m1_early; -+ return; -+ } -+ } -+ -+ /* BinLong correction means changing COP1 */ -+ if (!status_cop1 && !(inst->ld_update & COP1_MASK)) { -+ /* Locate BinLong transition point (if any) -+ * while avoiding BinM1Late. -+ */ -+ if (bin_long_early) { -+ /* request increment c(1) */ -+ temp = INCREMENT << COP1_SHIFT; -+ inst->ld_update |= temp; -+ } else { -+ /* request decrement c(1) */ -+ temp = DECREMENT << COP1_SHIFT; -+ inst->ld_update |= temp; -+ } -+ -+ ld_coe_update(inst); -+ s_m->bin_long_late_early = bin_long_early; -+ } -+ /* We try to finish BinLong before we do BinM1 */ -+ return; -+ } -+ -+ if (!s_m->bin_m1_stop) { -+ /* BinM1 correction means changing COM1 */ -+ if (!status_com1 && !(inst->ld_update & COM1_MASK)) { -+ /* Locate BinM1 transition point (if any) */ -+ if (bin_m1_early) { -+ /* request increment c(-1) */ -+ temp = INCREMENT << COM1_SHIFT; -+ inst->ld_update |= temp; -+ } else { -+ /* request decrement c(-1) */ -+ temp = DECREMENT << COM1_SHIFT; -+ inst->ld_update |= temp; -+ } -+ -+ ld_coe_update(inst); -+ s_m->bin_m1_late_early = bin_m1_early; -+ } -+ } -+ } -+} -+ -+static int check_an_link(struct phy_device *phydev) -+{ -+ int val; -+ int timeout = 100; -+ -+ while (timeout--) { -+ val = phy_read_mmd(phydev, FSL_XFI_AN, FSL_XFI_LNK_STATUS); -+ if (val & XFI_AN_LNK_STAT_UP) -+ return 1; -+ usleep_range(100, 500); -+ } -+ -+ return 0; -+} -+ -+static int is_link_training_fail(struct phy_device *phydev) -+{ -+ int val; -+ -+ val = phy_read_mmd(phydev, FSL_XFI_PMD, FSL_XFI_KR_PMD_STATUS); -+ if (!(val & TRAIN_FAIL) && (val & RX_STAT)) { -+ /* check LNK_STAT for sure */ -+ if (check_an_link(phydev)) -+ return 0; -+ return 1; -+ } -+ return 1; -+} -+ -+static int check_rx(struct phy_device *phydev) -+{ -+ return phy_read_mmd(phydev, FSL_XFI_PMD, FSL_XFI_KR_LP_STATUS) & -+ RX_READY_MASK; -+} -+ -+/* Coefficient values have hardware restrictions */ -+static int is_ld_valid(u32 *ld_coe) -+{ -+ u32 ratio_pst1q = *ld_coe; -+ u32 adpt_eq = *(ld_coe + 1); -+ u32 ratio_preq = *(ld_coe + 2); -+ -+ if ((ratio_pst1q + adpt_eq + ratio_preq) > 48) -+ return 0; -+ -+ if (((ratio_pst1q + adpt_eq + ratio_preq) * 4) >= -+ ((adpt_eq - ratio_pst1q - ratio_preq) * 17)) -+ return 0; -+ -+ if (ratio_preq > ratio_pst1q) -+ return 0; -+ -+ if (ratio_preq > 8) -+ return 0; -+ -+ if (adpt_eq < 26) -+ return 0; -+ -+ if (ratio_pst1q > 16) -+ return 0; -+ -+ return 1; -+} -+ -+#define VAL_INVALID 0xff -+ -+static const u32 preq_table[] = {0x0, 0x1, 0x3, 0x5, -+ 0x7, 0x9, 0xb, 0xc, VAL_INVALID}; -+static const u32 pst1q_table[] = {0x0, 0x1, 0x3, 0x5, -+ 0x7, 0x9, 0xb, 0xd, 0xf, 0x10, VAL_INVALID}; -+ -+static int is_value_allowed(const u32 *val_table, u32 val) -+{ -+ int i; -+ -+ for (i = 0;; i++) { -+ if (*(val_table + i) == VAL_INVALID) -+ return 0; -+ if (*(val_table + i) == val) -+ return 1; -+ } -+} -+ -+static int inc_dec(struct fsl_xgkr_inst *inst, int field, int request) -+{ -+ u32 ld_limit[3], ld_coe[3], step[3]; -+ -+ ld_coe[0] = inst->ratio_pst1q; -+ ld_coe[1] = inst->adpt_eq; -+ ld_coe[2] = inst->ratio_preq; -+ -+ /* Information specific to the Freescale SerDes for 10GBase-KR: -+ * Incrementing C(+1) means *decrementing* RATIO_PST1Q -+ * Incrementing C(0) means incrementing ADPT_EQ -+ * Incrementing C(-1) means *decrementing* RATIO_PREQ -+ */ -+ step[0] = -1; -+ step[1] = 1; -+ step[2] = -1; -+ -+ switch (request) { -+ case INCREMENT: -+ ld_limit[0] = POST_COE_MAX; -+ ld_limit[1] = ZERO_COE_MAX; -+ ld_limit[2] = PRE_COE_MAX; -+ if (ld_coe[field] != ld_limit[field]) -+ ld_coe[field] += step[field]; -+ else -+ /* MAX */ -+ return 2; -+ break; -+ case DECREMENT: -+ ld_limit[0] = POST_COE_MIN; -+ ld_limit[1] = ZERO_COE_MIN; -+ ld_limit[2] = PRE_COE_MIN; -+ if (ld_coe[field] != ld_limit[field]) -+ ld_coe[field] -= step[field]; -+ else -+ /* MIN */ -+ return 1; -+ break; -+ default: -+ break; -+ } -+ -+ if (is_ld_valid(ld_coe)) { -+ /* accept new ld */ -+ inst->ratio_pst1q = ld_coe[0]; -+ inst->adpt_eq = ld_coe[1]; -+ inst->ratio_preq = ld_coe[2]; -+ /* only some values for preq and pst1q can be used. -+ * for preq: 0x0, 0x1, 0x3, 0x5, 0x7, 0x9, 0xb, 0xc. -+ * for pst1q: 0x0, 0x1, 0x3, 0x5, 0x7, 0x9, 0xb, 0xd, 0xf, 0x10. -+ */ -+ if (!is_value_allowed((const u32 *)&preq_table, ld_coe[2])) { -+ dev_dbg(&inst->phydev->dev, -+ "preq skipped value: %d.\n", ld_coe[2]); -+ return 0; -+ } -+ -+ if (!is_value_allowed((const u32 *)&pst1q_table, ld_coe[0])) { -+ dev_dbg(&inst->phydev->dev, -+ "pst1q skipped value: %d.\n", ld_coe[0]); -+ return 0; -+ } -+ -+ tune_tecr0(inst); -+ } else { -+ if (request == DECREMENT) -+ /* MIN */ -+ return 1; -+ if (request == INCREMENT) -+ /* MAX */ -+ return 2; -+ } -+ -+ return 0; -+} -+ -+static void min_max_updated(struct fsl_xgkr_inst *inst, int field, int new_ld) -+{ -+ u32 ld_coe[] = {COE_UPDATED, COE_MIN, COE_MAX}; -+ u32 mask, val; -+ -+ switch (field) { -+ case COE_COP1: -+ mask = COP1_MASK; -+ val = ld_coe[new_ld] << COP1_SHIFT; -+ break; -+ case COE_COZ: -+ mask = COZ_MASK; -+ val = ld_coe[new_ld] << COZ_SHIFT; -+ break; -+ case COE_COM: -+ mask = COM1_MASK; -+ val = ld_coe[new_ld] << COM1_SHIFT; -+ break; -+ default: -+ return; -+ break; -+ } -+ -+ inst->ld_status &= ~mask; -+ inst->ld_status |= val; -+} -+ -+static void check_request(struct fsl_xgkr_inst *inst, int request) -+{ -+ int cop1_req, coz_req, com_req; -+ int old_status, new_ld_sta; -+ -+ cop1_req = (request & COP1_MASK) >> COP1_SHIFT; -+ coz_req = (request & COZ_MASK) >> COZ_SHIFT; -+ com_req = (request & COM1_MASK) >> COM1_SHIFT; -+ -+ /* IEEE802.3-2008, 72.6.10.2.5 -+ * Ensure we only act on INCREMENT/DECREMENT when we are in NOT UPDATED! -+ */ -+ old_status = inst->ld_status; -+ -+ if (cop1_req && !(inst->ld_status & COP1_MASK)) { -+ new_ld_sta = inc_dec(inst, COE_COP1, cop1_req); -+ min_max_updated(inst, COE_COP1, new_ld_sta); -+ } -+ -+ if (coz_req && !(inst->ld_status & COZ_MASK)) { -+ new_ld_sta = inc_dec(inst, COE_COZ, coz_req); -+ min_max_updated(inst, COE_COZ, new_ld_sta); -+ } -+ -+ if (com_req && !(inst->ld_status & COM1_MASK)) { -+ new_ld_sta = inc_dec(inst, COE_COM, com_req); -+ min_max_updated(inst, COE_COM, new_ld_sta); -+ } -+ -+ if (old_status != inst->ld_status) -+ ld_coe_status(inst); -+ -+} -+ -+static void preset(struct fsl_xgkr_inst *inst) -+{ -+ /* These are all MAX values from the IEEE802.3 perspective! */ -+ inst->ratio_pst1q = POST_COE_MAX; -+ inst->adpt_eq = ZERO_COE_MAX; -+ inst->ratio_preq = PRE_COE_MAX; -+ -+ tune_tecr0(inst); -+ inst->ld_status &= ~(COP1_MASK | COZ_MASK | COM1_MASK); -+ inst->ld_status |= COE_MAX << COP1_SHIFT | -+ COE_MAX << COZ_SHIFT | -+ COE_MAX << COM1_SHIFT; -+ ld_coe_status(inst); -+} -+ -+static void initialize(struct fsl_xgkr_inst *inst) -+{ -+ inst->ratio_preq = RATIO_PREQ; -+ inst->ratio_pst1q = RATIO_PST1Q; -+ inst->adpt_eq = RATIO_EQ; -+ -+ tune_tecr0(inst); -+ inst->ld_status &= ~(COP1_MASK | COZ_MASK | COM1_MASK); -+ inst->ld_status |= COE_UPDATED << COP1_SHIFT | -+ COE_UPDATED << COZ_SHIFT | -+ COE_UPDATED << COM1_SHIFT; -+ ld_coe_status(inst); -+} -+ -+static void train_rx(struct fsl_xgkr_inst *inst) -+{ -+ struct phy_device *phydev = inst->phydev; -+ int request, old_ld_status; -+ -+ /* get request from LP */ -+ request = phy_read_mmd(phydev, FSL_XFI_PMD, FSL_XFI_KR_LP_CU) & -+ (LD_ALL_MASK); -+ old_ld_status = inst->ld_status; -+ -+ /* IEEE802.3-2008, 72.6.10.2.5 -+ * Ensure we always go to NOT UDPATED for status reporting in -+ * response to HOLD requests. -+ * IEEE802.3-2008, 72.6.10.2.3.1/2 -+ * ... but only if PRESET/INITIALIZE are not active to ensure -+ * we keep status until they are released! -+ */ -+ if (!(request & (PRESET_MASK | INIT_MASK))) { -+ if (!(request & COP1_MASK)) -+ inst->ld_status &= ~COP1_MASK; -+ -+ if (!(request & COZ_MASK)) -+ inst->ld_status &= ~COZ_MASK; -+ -+ if (!(request & COM1_MASK)) -+ inst->ld_status &= ~COM1_MASK; -+ -+ if (old_ld_status != inst->ld_status) -+ ld_coe_status(inst); -+ -+ } -+ -+ /* As soon as the LP shows ready, no need to do any more updates. */ -+ if (check_rx(phydev)) { -+ /* LP receiver is ready */ -+ if (inst->ld_status & (COP1_MASK | COZ_MASK | COM1_MASK)) { -+ inst->ld_status &= ~(COP1_MASK | COZ_MASK | COM1_MASK); -+ ld_coe_status(inst); -+ } -+ } else { -+ /* IEEE802.3-2008, 72.6.10.2.3.1/2 -+ * only act on PRESET/INITIALIZE if all status is NOT UPDATED. -+ */ -+ if (request & (PRESET_MASK | INIT_MASK)) { -+ if (!(inst->ld_status & -+ (COP1_MASK | COZ_MASK | COM1_MASK))) { -+ if (request & PRESET_MASK) -+ preset(inst); -+ -+ if (request & INIT_MASK) -+ initialize(inst); -+ } -+ } -+ -+ /* LP Coefficient are not in HOLD */ -+ if (request & REQUEST_MASK) -+ check_request(inst, request & REQUEST_MASK); -+ } -+} -+ -+static void xgkr_wq_state_machine(struct work_struct *work) -+{ -+ struct fsl_xgkr_wk *wk = container_of(work, -+ struct fsl_xgkr_wk, xgkr_wk); -+ struct fsl_xgkr_inst *inst = wk->xgkr_inst; -+ struct training_state_machine *s_m = &inst->t_s_m; -+ struct phy_device *phydev = inst->phydev; -+ int val = 0, i; -+ int an_state, lt_state; -+ unsigned long dead_line; -+ int rx_ok, tx_ok; -+ -+ if (s_m->link_up) { -+ /* check abnormal link down events when link is up, for ex. -+ * the cable is pulled out or link partner is down. -+ */ -+ an_state = phy_read_mmd(phydev, FSL_XFI_AN, FSL_XFI_LNK_STATUS); -+ if (!(an_state & XFI_AN_LNK_STAT_UP)) { -+ dev_info(&phydev->dev, -+ "Detect hotplug, restart training!\n"); -+ init_inst(inst, 1); -+ start_an(phydev); -+ } -+ s_m->running = false; -+ return; -+ } -+ -+ if (!s_m->an_ok) { -+ an_state = phy_read_mmd(phydev, FSL_XFI_AN, FSL_XFI_BP_STATUS); -+ if (!(an_state & AN_10GKR_MASK)) { -+ s_m->running = false; -+ return; -+ } else -+ s_m->an_ok = true; -+ } -+ -+ dev_info(&phydev->dev, "is training.\n"); -+ -+ start_lt(phydev); -+ for (i = 0; i < 2;) { -+ /* i < 1 also works, but start one more try immediately when -+ * failed can adjust our training frequency to match other -+ * devices. This can help the link being established more -+ * quickly. -+ */ -+ dead_line = jiffies + msecs_to_jiffies(500); -+ while (time_before(jiffies, dead_line)) { -+ val = phy_read_mmd(phydev, FSL_XFI_PMD, -+ FSL_XFI_KR_PMD_STATUS); -+ if (val & TRAIN_FAIL) { -+ /* LT failed already, reset lane to avoid -+ * it run into hanging, then start LT again. -+ */ -+ reset_gcr0(inst); -+ start_lt(phydev); -+ } else if (val & PMD_STATUS_SUP_STAT && -+ val & PMD_STATUS_FRAME_LOCK) -+ break; -+ usleep_range(100, 500); -+ } -+ -+ if (!(val & PMD_STATUS_FRAME_LOCK && -+ val & PMD_STATUS_SUP_STAT)) { -+ i++; -+ continue; -+ } -+ -+ /* init process */ -+ rx_ok = tx_ok = false; -+ /* the LT should be finished in 500ms, failed or OK. */ -+ dead_line = jiffies + msecs_to_jiffies(500); -+ -+ while (time_before(jiffies, dead_line)) { -+ /* check if the LT is already failed */ -+ lt_state = phy_read_mmd(phydev, FSL_XFI_PMD, -+ FSL_XFI_KR_PMD_STATUS); -+ if (lt_state & TRAIN_FAIL) { -+ reset_gcr0(inst); -+ break; -+ } -+ -+ rx_ok = check_rx(phydev); -+ tx_ok = s_m->tx_complete; -+ -+ if (rx_ok && tx_ok) -+ break; -+ -+ if (!rx_ok) -+ train_rx(inst); -+ -+ if (!tx_ok) -+ train_tx(inst); -+ usleep_range(100, 500); -+ } -+ -+ i++; -+ /* check LT result */ -+ if (is_link_training_fail(phydev)) { -+ /* reset state machine */ -+ init_inst(inst, 0); -+ continue; -+ } else { -+ stop_lt(phydev); -+ s_m->running = false; -+ s_m->link_up = true; -+ dev_info(&phydev->dev, "LT training is SUCCEEDED!\n"); -+ break; -+ } -+ } -+ -+ if (!s_m->link_up) { -+ /* reset state machine */ -+ init_inst(inst, 0); -+ } -+} -+ -+static void xgkr_timer_handle(unsigned long arg) -+{ -+ struct list_head *pos; -+ struct fsl_xgkr_wk *wk; -+ struct fsl_xgkr_inst *xgkr_inst; -+ struct phy_device *phydev; -+ struct training_state_machine *s_m; -+ -+ list_for_each(pos, &fsl_xgkr_list) { -+ wk = list_entry(pos, struct fsl_xgkr_wk, xgkr_list); -+ xgkr_inst = wk->xgkr_inst; -+ phydev = xgkr_inst->phydev; -+ s_m = &xgkr_inst->t_s_m; -+ -+ if (!s_m->running && (!s_m->an_ok || s_m->link_up)) { -+ s_m->running = true; -+ queue_work(xgkr_wq, (struct work_struct *)wk); -+ } -+ } -+ -+ if (!list_empty(&fsl_xgkr_list)) -+ mod_timer(&xgkr_timer, -+ jiffies + msecs_to_jiffies(XGKR_TIMEOUT)); -+} -+ -+static int fsl_xgkr_bind_serdes(const char *lane_name, -+ struct phy_device *phydev) -+{ -+ unsigned long serdes_base; -+ unsigned long lane_base; -+ int i; -+ -+ for (i = 0; i < SERDES_MAX; i++) { -+ if (strstr(lane_name, s_map[i].serdes_name)) { -+ serdes_base = s_map[i].serdes_base; -+ break; -+ } -+ } -+ -+ if (i == SERDES_MAX) -+ goto serdes_err; -+ -+ for (i = 0; i < LANE_MAX; i++) { -+ if (strstr(lane_name, l_map[i].lane_name)) { -+ lane_base = l_map[i].lane_base; -+ break; -+ } -+ } -+ -+ if (i == LANE_MAX) -+ goto lane_err; -+ -+ phydev->priv = ioremap(serdes_base + lane_base, -+ sizeof(struct per_lane_ctrl_status)); -+ if (!phydev->priv) -+ return -ENOMEM; -+ -+ return 0; -+ -+serdes_err: -+ dev_err(&phydev->dev, "Unknown SerDes name"); -+ return -EINVAL; -+lane_err: -+ dev_err(&phydev->dev, "Unknown Lane name"); -+ return -EINVAL; -+} -+ -+static int fsl_xgkr_probe(struct phy_device *phydev) -+{ -+ struct fsl_xgkr_inst *xgkr_inst; -+ struct fsl_xgkr_wk *xgkr_wk; -+ struct device_node *child; -+ const char *lane_name; -+ int len; -+ -+ child = phydev->dev.of_node; -+ -+ /* if there is lane-instance property, 10G-KR need to run */ -+ lane_name = of_get_property(child, "lane-instance", &len); -+ if (!lane_name || (fsl_xgkr_bind_serdes(lane_name, phydev))) -+ return 0; -+ -+ xgkr_inst = kzalloc(sizeof(struct fsl_xgkr_inst), GFP_KERNEL); -+ if (!xgkr_inst) -+ goto mem_err1; -+ -+ xgkr_inst->reg_base = phydev->priv; -+ -+ xgkr_inst->bus = phydev->bus; -+ -+ xgkr_inst->phydev = phydev; -+ -+ init_inst(xgkr_inst, 1); -+ -+ xgkr_wk = kzalloc(sizeof(struct fsl_xgkr_wk), GFP_KERNEL); -+ if (!xgkr_wk) -+ goto mem_err2; -+ -+ xgkr_wk->xgkr_inst = xgkr_inst; -+ phydev->priv = xgkr_wk; -+ -+ list_add(&xgkr_wk->xgkr_list, &fsl_xgkr_list); -+ -+ if (!fire_timer) { -+ setup_timer(&xgkr_timer, xgkr_timer_handle, -+ (unsigned long)&fsl_xgkr_list); -+ mod_timer(&xgkr_timer, -+ jiffies + msecs_to_jiffies(XGKR_TIMEOUT)); -+ fire_timer = 1; -+ xgkr_wq = create_workqueue("fsl_xgkr"); -+ } -+ INIT_WORK((struct work_struct *)xgkr_wk, xgkr_wq_state_machine); -+ -+ /* start auto-negotiation to detect link partner */ -+ start_an(phydev); -+ -+ return 0; -+mem_err2: -+ kfree(xgkr_inst); -+mem_err1: -+ dev_err(&phydev->dev, "failed to allocate memory!\n"); -+ return -ENOMEM; -+} -+ -+static int fsl_xgkr_config_init(struct phy_device *phydev) -+{ -+ return 0; -+} -+ -+static int fsl_xgkr_config_aneg(struct phy_device *phydev) -+{ -+ return 0; -+} -+ -+static void fsl_xgkr_remove(struct phy_device *phydev) -+{ -+ struct fsl_xgkr_wk *wk = (struct fsl_xgkr_wk *)phydev->priv; -+ struct fsl_xgkr_inst *xgkr_inst = wk->xgkr_inst; -+ struct list_head *this, *next; -+ struct fsl_xgkr_wk *tmp; -+ -+ list_for_each_safe(this, next, &fsl_xgkr_list) { -+ tmp = list_entry(this, struct fsl_xgkr_wk, xgkr_list); -+ if (tmp == wk) { -+ cancel_work_sync((struct work_struct *)wk); -+ list_del(this); -+ } -+ } -+ -+ if (list_empty(&fsl_xgkr_list)) -+ del_timer(&xgkr_timer); -+ -+ if (xgkr_inst->reg_base) -+ iounmap(xgkr_inst->reg_base); -+ -+ kfree(xgkr_inst); -+ kfree(wk); -+} -+ -+static int fsl_xgkr_read_status(struct phy_device *phydev) -+{ -+ int val = phy_read_mmd(phydev, FSL_XFI_AN, FSL_XFI_LNK_STATUS); -+ -+ phydev->speed = SPEED_10000; -+ phydev->duplex = 1; -+ -+ if (val & XFI_AN_LNK_STAT_UP) -+ phydev->link = 1; -+ else -+ phydev->link = 0; -+ -+ return 0; -+} -+ -+static int fsl_xgkr_match_phy_device(struct phy_device *phydev) -+{ -+ return phydev->c45_ids.device_ids[3] == FSL_XFI_PCS_PHY_ID; -+} -+ -+static int fsl_xgkr_match_phy_device2(struct phy_device *phydev) -+{ -+ return phydev->c45_ids.device_ids[3] == FSL_XFI_PCS_PHY_ID2; -+} -+ -+static struct phy_driver fsl_xgkr_driver[] = { -+ { -+ .phy_id = FSL_XFI_PCS_PHY_ID, -+ .name = "Freescale 10G KR Rev1", -+ .phy_id_mask = 0xffffffff, -+ .features = PHY_GBIT_FEATURES, -+ .flags = PHY_HAS_INTERRUPT, -+ .probe = fsl_xgkr_probe, -+ .config_init = &fsl_xgkr_config_init, -+ .config_aneg = &fsl_xgkr_config_aneg, -+ .read_status = &fsl_xgkr_read_status, -+ .match_phy_device = fsl_xgkr_match_phy_device, -+ .remove = fsl_xgkr_remove, -+ .driver = { .owner = THIS_MODULE,}, -+ }, -+ { -+ .phy_id = FSL_XFI_PCS_PHY_ID2, -+ .name = "Freescale 10G KR Rev2", -+ .phy_id_mask = 0xffffffff, -+ .features = PHY_GBIT_FEATURES, -+ .flags = PHY_HAS_INTERRUPT, -+ .probe = fsl_xgkr_probe, -+ .config_init = &fsl_xgkr_config_init, -+ .config_aneg = &fsl_xgkr_config_aneg, -+ .read_status = &fsl_xgkr_read_status, -+ .match_phy_device = fsl_xgkr_match_phy_device2, -+ .remove = fsl_xgkr_remove, -+ .driver = { .owner = THIS_MODULE,}, -+ }, -+}; -+ -+static int __init fsl_xgkr_init(void) -+{ -+ return phy_drivers_register(fsl_xgkr_driver, -+ ARRAY_SIZE(fsl_xgkr_driver)); -+} -+ -+static void __exit fsl_xgkr_exit(void) -+{ -+ phy_drivers_unregister(fsl_xgkr_driver, -+ ARRAY_SIZE(fsl_xgkr_driver)); -+} -+ -+module_init(fsl_xgkr_init); -+module_exit(fsl_xgkr_exit); -+ -+static struct mdio_device_id __maybe_unused freescale_tbl[] = { -+ { FSL_XFI_PCS_PHY_ID, 0xffffffff }, -+ { FSL_XFI_PCS_PHY_ID2, 0xffffffff }, -+ { } -+}; -+ -+MODULE_DEVICE_TABLE(mdio, freescale_tbl); -diff --git a/drivers/net/phy/teranetics.c b/drivers/net/phy/teranetics.c -new file mode 100644 -index 0000000..91e1bec ---- /dev/null -+++ b/drivers/net/phy/teranetics.c -@@ -0,0 +1,135 @@ -+/* -+ * Driver for Teranetics PHY -+ * -+ * Author: Shaohui Xie -+ * -+ * Copyright 2015 Freescale Semiconductor, Inc. -+ * -+ * This file is licensed under the terms of the GNU General Public License -+ * version 2. This program is licensed "as is" without any warranty of any -+ * kind, whether express or implied. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+MODULE_DESCRIPTION("Teranetics PHY driver"); -+MODULE_AUTHOR("Shaohui Xie "); -+MODULE_LICENSE("GPL v2"); -+ -+#define PHY_ID_TN2020 0x00a19410 -+#define MDIO_PHYXS_LNSTAT_SYNC0 0x0001 -+#define MDIO_PHYXS_LNSTAT_SYNC1 0x0002 -+#define MDIO_PHYXS_LNSTAT_SYNC2 0x0004 -+#define MDIO_PHYXS_LNSTAT_SYNC3 0x0008 -+#define MDIO_PHYXS_LNSTAT_ALIGN 0x1000 -+ -+#define MDIO_PHYXS_LANE_READY (MDIO_PHYXS_LNSTAT_SYNC0 | \ -+ MDIO_PHYXS_LNSTAT_SYNC1 | \ -+ MDIO_PHYXS_LNSTAT_SYNC2 | \ -+ MDIO_PHYXS_LNSTAT_SYNC3 | \ -+ MDIO_PHYXS_LNSTAT_ALIGN) -+ -+static int teranetics_config_init(struct phy_device *phydev) -+{ -+ phydev->supported = SUPPORTED_10000baseT_Full; -+ phydev->advertising = SUPPORTED_10000baseT_Full; -+ -+ return 0; -+} -+ -+static int teranetics_soft_reset(struct phy_device *phydev) -+{ -+ return 0; -+} -+ -+static int teranetics_aneg_done(struct phy_device *phydev) -+{ -+ int reg; -+ -+ /* auto negotiation state can only be checked when using copper -+ * port, if using fiber port, just lie it's done. -+ */ -+ if (!phy_read_mmd(phydev, MDIO_MMD_VEND1, 93)) { -+ reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); -+ return (reg < 0) ? reg : (reg & BMSR_ANEGCOMPLETE); -+ } -+ -+ return 1; -+} -+ -+static int teranetics_config_aneg(struct phy_device *phydev) -+{ -+ return 0; -+} -+ -+static int teranetics_read_status(struct phy_device *phydev) -+{ -+ int reg; -+ -+ phydev->link = 1; -+ -+ phydev->speed = SPEED_10000; -+ phydev->duplex = DUPLEX_FULL; -+ -+ if (!phy_read_mmd(phydev, MDIO_MMD_VEND1, 93)) { -+ reg = phy_read_mmd(phydev, MDIO_MMD_PHYXS, MDIO_PHYXS_LNSTAT); -+ if (reg < 0 || -+ !((reg & MDIO_PHYXS_LANE_READY) == MDIO_PHYXS_LANE_READY)) { -+ phydev->link = 0; -+ return 0; -+ } -+ -+ reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); -+ if (reg < 0 || !(reg & MDIO_STAT1_LSTATUS)) -+ phydev->link = 0; -+ } -+ -+ return 0; -+} -+ -+static int teranetics_match_phy_device(struct phy_device *phydev) -+{ -+ return phydev->c45_ids.device_ids[3] == PHY_ID_TN2020; -+} -+ -+static struct phy_driver teranetics_driver[] = { -+{ -+ .phy_id = PHY_ID_TN2020, -+ .phy_id_mask = 0xffffffff, -+ .name = "Teranetics TN2020", -+ .soft_reset = teranetics_soft_reset, -+ .aneg_done = teranetics_aneg_done, -+ .config_init = teranetics_config_init, -+ .config_aneg = teranetics_config_aneg, -+ .read_status = teranetics_read_status, -+ .match_phy_device = teranetics_match_phy_device, -+ .driver = { .owner = THIS_MODULE,}, -+}, -+}; -+ -+static int __init teranetics_init(void) -+{ -+ return phy_drivers_register(teranetics_driver, -+ ARRAY_SIZE(teranetics_driver)); -+} -+ -+static void __exit teranetics_exit(void) -+{ -+ return phy_drivers_unregister(teranetics_driver, -+ ARRAY_SIZE(teranetics_driver)); -+} -+ -+module_init(teranetics_init); -+module_exit(teranetics_exit); -+ -+static struct mdio_device_id __maybe_unused teranetics_tbl[] = { -+ { PHY_ID_TN2020, 0xffffffff }, -+ { } -+}; -+ -+MODULE_DEVICE_TABLE(mdio, teranetics_tbl); -diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig -index 4690ae9..43ff2b5 100644 ---- a/drivers/staging/Kconfig -+++ b/drivers/staging/Kconfig -@@ -108,4 +108,8 @@ source "drivers/staging/skein/Kconfig" - - source "drivers/staging/unisys/Kconfig" - -+source "drivers/staging/fsl-mc/Kconfig" -+ -+source "drivers/staging/fsl-dpaa2/Kconfig" -+ - endif # STAGING -diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile -index c780a0e..a9bd303 100644 ---- a/drivers/staging/Makefile -+++ b/drivers/staging/Makefile -@@ -46,3 +46,5 @@ obj-$(CONFIG_MTD_SPINAND_MT29F) += mt29f_spinand/ - obj-$(CONFIG_GS_FPGABOOT) += gs_fpgaboot/ - obj-$(CONFIG_CRYPTO_SKEIN) += skein/ - obj-$(CONFIG_UNISYSSPAR) += unisys/ -+obj-$(CONFIG_FSL_MC_BUS) += fsl-mc/ -+obj-$(CONFIG_FSL_DPAA2) += fsl-dpaa2/ -diff --git a/drivers/staging/fsl-dpaa2/Kconfig b/drivers/staging/fsl-dpaa2/Kconfig -new file mode 100644 -index 0000000..3fe47bc ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/Kconfig -@@ -0,0 +1,12 @@ -+# -+# Freescale device configuration -+# -+ -+config FSL_DPAA2 -+ bool "Freescale DPAA2 devices" -+ depends on FSL_MC_BUS -+ ---help--- -+ Build drivers for Freescale DataPath Acceleration Architecture (DPAA2) family of SoCs. -+# TODO move DPIO driver in-here? -+source "drivers/staging/fsl-dpaa2/ethernet/Kconfig" -+source "drivers/staging/fsl-dpaa2/mac/Kconfig" -diff --git a/drivers/staging/fsl-dpaa2/Makefile b/drivers/staging/fsl-dpaa2/Makefile -new file mode 100644 -index 0000000..bc687a1 ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/Makefile -@@ -0,0 +1,6 @@ -+# -+# Makefile for the Freescale network device drivers. -+# -+ -+obj-$(CONFIG_FSL_DPAA2_ETH) += ethernet/ -+obj-$(CONFIG_FSL_DPAA2_MAC) += mac/ -diff --git a/drivers/staging/fsl-dpaa2/ethernet/Kconfig b/drivers/staging/fsl-dpaa2/ethernet/Kconfig -new file mode 100644 -index 0000000..df91da2 ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/ethernet/Kconfig -@@ -0,0 +1,36 @@ -+# -+# Freescale DPAA Ethernet driver configuration -+# -+# Copyright (C) 2014-2015 Freescale Semiconductor, Inc. -+# -+# This file is released under the GPLv2 -+# -+ -+menuconfig FSL_DPAA2_ETH -+ tristate "Freescale DPAA2 Ethernet" -+ depends on FSL_DPAA2 && FSL_MC_BUS && FSL_MC_DPIO -+ select FSL_DPAA2_MAC -+ default y -+ ---help--- -+ Freescale Data Path Acceleration Architecture Ethernet -+ driver, using the Freescale MC bus driver. -+ -+if FSL_DPAA2_ETH -+ -+config FSL_DPAA2_ETH_USE_ERR_QUEUE -+ bool "Enable Rx error queue" -+ default n -+ ---help--- -+ Allow Rx error frames to be enqueued on an error queue -+ and processed by the driver (by default they are dropped -+ in hardware). -+ This may impact performance, recommended for debugging -+ purposes only. -+ -+config FSL_DPAA2_ETH_DEBUGFS -+ depends on DEBUG_FS && FSL_QBMAN_DEBUG -+ bool "Enable debugfs support" -+ default n -+ ---help--- -+ Enable advanced statistics through debugfs interface. -+endif -diff --git a/drivers/staging/fsl-dpaa2/ethernet/Makefile b/drivers/staging/fsl-dpaa2/ethernet/Makefile -new file mode 100644 -index 0000000..74bff15 ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/ethernet/Makefile -@@ -0,0 +1,21 @@ -+# -+# Makefile for the Freescale DPAA Ethernet controllers -+# -+# Copyright (C) 2014-2015 Freescale Semiconductor, Inc. -+# -+# This file is released under the GPLv2 -+# -+ -+ccflags-y += -DVERSION=\"\" -+ -+obj-$(CONFIG_FSL_DPAA2_ETH) += fsl-dpaa2-eth.o -+ -+fsl-dpaa2-eth-objs := dpaa2-eth.o dpaa2-ethtool.o dpni.o -+fsl-dpaa2-eth-${CONFIG_FSL_DPAA2_ETH_DEBUGFS} += dpaa2-eth-debugfs.o -+ -+#Needed by the tracing framework -+CFLAGS_dpaa2-eth.o := -I$(src) -+ -+ifeq ($(CONFIG_FSL_DPAA2_ETH_GCOV),y) -+ GCOV_PROFILE := y -+endif -diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-debugfs.c b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-debugfs.c -new file mode 100644 -index 0000000..c397983 ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-debugfs.c -@@ -0,0 +1,317 @@ -+ -+/* Copyright 2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 -+#include -+#include "dpaa2-eth.h" -+#include "dpaa2-eth-debugfs.h" -+ -+#define DPAA2_ETH_DBG_ROOT "dpaa2-eth" -+ -+static struct dentry *dpaa2_dbg_root; -+ -+static int dpaa2_dbg_cpu_show(struct seq_file *file, void *offset) -+{ -+ struct dpaa2_eth_priv *priv = (struct dpaa2_eth_priv *)file->private; -+ struct rtnl_link_stats64 *stats; -+ struct dpaa2_eth_drv_stats *extras; -+ int i; -+ -+ seq_printf(file, "Per-CPU stats for %s\n", priv->net_dev->name); -+ seq_printf(file, "%s%16s%16s%16s%16s%16s%16s%16s%16s\n", -+ "CPU", "Rx", "Rx Err", "Rx SG", "Tx", "Tx Err", "Tx conf", -+ "Tx SG", "Enq busy"); -+ -+ for_each_online_cpu(i) { -+ stats = per_cpu_ptr(priv->percpu_stats, i); -+ extras = per_cpu_ptr(priv->percpu_extras, i); -+ seq_printf(file, "%3d%16llu%16llu%16llu%16llu%16llu%16llu%16llu%16llu\n", -+ i, -+ stats->rx_packets, -+ stats->rx_errors, -+ extras->rx_sg_frames, -+ stats->tx_packets, -+ stats->tx_errors, -+ extras->tx_conf_frames, -+ extras->tx_sg_frames, -+ extras->tx_portal_busy); -+ } -+ -+ return 0; -+} -+ -+static int dpaa2_dbg_cpu_open(struct inode *inode, struct file *file) -+{ -+ int err; -+ struct dpaa2_eth_priv *priv = (struct dpaa2_eth_priv *)inode->i_private; -+ -+ err = single_open(file, dpaa2_dbg_cpu_show, priv); -+ if (err < 0) -+ netdev_err(priv->net_dev, "single_open() failed\n"); -+ -+ return err; -+} -+ -+static const struct file_operations dpaa2_dbg_cpu_ops = { -+ .open = dpaa2_dbg_cpu_open, -+ .read = seq_read, -+ .llseek = seq_lseek, -+ .release = single_release, -+}; -+ -+static char *fq_type_to_str(struct dpaa2_eth_fq *fq) -+{ -+ switch (fq->type) { -+ case DPAA2_RX_FQ: -+ return "Rx"; -+ case DPAA2_TX_CONF_FQ: -+ return "Tx conf"; -+ case DPAA2_RX_ERR_FQ: -+ return "Rx err"; -+ default: -+ return "N/A"; -+ } -+} -+ -+static int dpaa2_dbg_fqs_show(struct seq_file *file, void *offset) -+{ -+ struct dpaa2_eth_priv *priv = (struct dpaa2_eth_priv *)file->private; -+ struct dpaa2_eth_fq *fq; -+ u32 fcnt, bcnt; -+ int i, err; -+ -+ seq_printf(file, "FQ stats for %s:\n", priv->net_dev->name); -+ seq_printf(file, "%s%16s%16s%16s%16s\n", -+ "VFQID", "CPU", "Type", "Frames", "Pending frames"); -+ -+ for (i = 0; i < priv->num_fqs; i++) { -+ fq = &priv->fq[i]; -+ err = dpaa2_io_query_fq_count(NULL, fq->fqid, &fcnt, &bcnt); -+ if (err) -+ fcnt = 0; -+ -+ seq_printf(file, "%5d%16d%16s%16llu%16u\n", -+ fq->fqid, -+ fq->target_cpu, -+ fq_type_to_str(fq), -+ fq->stats.frames, -+ fcnt); -+ } -+ -+ return 0; -+} -+ -+static int dpaa2_dbg_fqs_open(struct inode *inode, struct file *file) -+{ -+ int err; -+ struct dpaa2_eth_priv *priv = (struct dpaa2_eth_priv *)inode->i_private; -+ -+ err = single_open(file, dpaa2_dbg_fqs_show, priv); -+ if (err < 0) -+ netdev_err(priv->net_dev, "single_open() failed\n"); -+ -+ return err; -+} -+ -+static const struct file_operations dpaa2_dbg_fq_ops = { -+ .open = dpaa2_dbg_fqs_open, -+ .read = seq_read, -+ .llseek = seq_lseek, -+ .release = single_release, -+}; -+ -+static int dpaa2_dbg_ch_show(struct seq_file *file, void *offset) -+{ -+ struct dpaa2_eth_priv *priv = (struct dpaa2_eth_priv *)file->private; -+ struct dpaa2_eth_channel *ch; -+ int i; -+ -+ seq_printf(file, "Channel stats for %s:\n", priv->net_dev->name); -+ seq_printf(file, "%s%16s%16s%16s%16s%16s\n", -+ "CHID", "CPU", "Deq busy", "Frames", "CDANs", -+ "Avg frm/CDAN"); -+ -+ for (i = 0; i < priv->num_channels; i++) { -+ ch = priv->channel[i]; -+ seq_printf(file, "%4d%16d%16llu%16llu%16llu%16llu\n", -+ ch->ch_id, -+ ch->nctx.desired_cpu, -+ ch->stats.dequeue_portal_busy, -+ ch->stats.frames, -+ ch->stats.cdan, -+ ch->stats.frames / ch->stats.cdan); -+ } -+ -+ return 0; -+} -+ -+static int dpaa2_dbg_ch_open(struct inode *inode, struct file *file) -+{ -+ int err; -+ struct dpaa2_eth_priv *priv = (struct dpaa2_eth_priv *)inode->i_private; -+ -+ err = single_open(file, dpaa2_dbg_ch_show, priv); -+ if (err < 0) -+ netdev_err(priv->net_dev, "single_open() failed\n"); -+ -+ return err; -+} -+ -+static const struct file_operations dpaa2_dbg_ch_ops = { -+ .open = dpaa2_dbg_ch_open, -+ .read = seq_read, -+ .llseek = seq_lseek, -+ .release = single_release, -+}; -+ -+static ssize_t dpaa2_dbg_reset_write(struct file *file, const char __user *buf, -+ size_t count, loff_t *offset) -+{ -+ struct dpaa2_eth_priv *priv = file->private_data; -+ struct rtnl_link_stats64 *percpu_stats; -+ struct dpaa2_eth_drv_stats *percpu_extras; -+ struct dpaa2_eth_fq *fq; -+ struct dpaa2_eth_channel *ch; -+ int i; -+ -+ for_each_online_cpu(i) { -+ percpu_stats = per_cpu_ptr(priv->percpu_stats, i); -+ memset(percpu_stats, 0, sizeof(*percpu_stats)); -+ -+ percpu_extras = per_cpu_ptr(priv->percpu_extras, i); -+ memset(percpu_extras, 0, sizeof(*percpu_extras)); -+ } -+ -+ for (i = 0; i < priv->num_fqs; i++) { -+ fq = &priv->fq[i]; -+ memset(&fq->stats, 0, sizeof(fq->stats)); -+ } -+ -+ for_each_cpu(i, &priv->dpio_cpumask) { -+ ch = priv->channel[i]; -+ memset(&ch->stats, 0, sizeof(ch->stats)); -+ } -+ -+ return count; -+} -+ -+static const struct file_operations dpaa2_dbg_reset_ops = { -+ .open = simple_open, -+ .write = dpaa2_dbg_reset_write, -+}; -+ -+void dpaa2_dbg_add(struct dpaa2_eth_priv *priv) -+{ -+ if (!dpaa2_dbg_root) -+ return; -+ -+ /* Create a directory for the interface */ -+ priv->dbg.dir = debugfs_create_dir(priv->net_dev->name, -+ dpaa2_dbg_root); -+ if (!priv->dbg.dir) { -+ netdev_err(priv->net_dev, "debugfs_create_dir() failed\n"); -+ return; -+ } -+ -+ /* per-cpu stats file */ -+ priv->dbg.cpu_stats = debugfs_create_file("cpu_stats", S_IRUGO, -+ priv->dbg.dir, priv, -+ &dpaa2_dbg_cpu_ops); -+ if (!priv->dbg.cpu_stats) { -+ netdev_err(priv->net_dev, "debugfs_create_file() failed\n"); -+ goto err_cpu_stats; -+ } -+ -+ /* per-fq stats file */ -+ priv->dbg.fq_stats = debugfs_create_file("fq_stats", S_IRUGO, -+ priv->dbg.dir, priv, -+ &dpaa2_dbg_fq_ops); -+ if (!priv->dbg.fq_stats) { -+ netdev_err(priv->net_dev, "debugfs_create_file() failed\n"); -+ goto err_fq_stats; -+ } -+ -+ /* per-fq stats file */ -+ priv->dbg.ch_stats = debugfs_create_file("ch_stats", S_IRUGO, -+ priv->dbg.dir, priv, -+ &dpaa2_dbg_ch_ops); -+ if (!priv->dbg.fq_stats) { -+ netdev_err(priv->net_dev, "debugfs_create_file() failed\n"); -+ goto err_ch_stats; -+ } -+ -+ /* reset stats */ -+ priv->dbg.reset_stats = debugfs_create_file("reset_stats", S_IWUSR, -+ priv->dbg.dir, priv, -+ &dpaa2_dbg_reset_ops); -+ if (!priv->dbg.reset_stats) { -+ netdev_err(priv->net_dev, "debugfs_create_file() failed\n"); -+ goto err_reset_stats; -+ } -+ -+ return; -+ -+err_reset_stats: -+ debugfs_remove(priv->dbg.ch_stats); -+err_ch_stats: -+ debugfs_remove(priv->dbg.fq_stats); -+err_fq_stats: -+ debugfs_remove(priv->dbg.cpu_stats); -+err_cpu_stats: -+ debugfs_remove(priv->dbg.dir); -+} -+ -+void dpaa2_dbg_remove(struct dpaa2_eth_priv *priv) -+{ -+ debugfs_remove(priv->dbg.reset_stats); -+ debugfs_remove(priv->dbg.fq_stats); -+ debugfs_remove(priv->dbg.ch_stats); -+ debugfs_remove(priv->dbg.cpu_stats); -+ debugfs_remove(priv->dbg.dir); -+} -+ -+void dpaa2_eth_dbg_init(void) -+{ -+ dpaa2_dbg_root = debugfs_create_dir(DPAA2_ETH_DBG_ROOT, NULL); -+ if (!dpaa2_dbg_root) { -+ pr_err("DPAA2-ETH: debugfs create failed\n"); -+ return; -+ } -+ -+ pr_info("DPAA2-ETH: debugfs created\n"); -+} -+ -+void __exit dpaa2_eth_dbg_exit(void) -+{ -+ debugfs_remove(dpaa2_dbg_root); -+} -+ -diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-debugfs.h b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-debugfs.h -new file mode 100644 -index 0000000..7ba706c ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-debugfs.h -@@ -0,0 +1,61 @@ -+/* Copyright 2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 DPAA2_ETH_DEBUGFS_H -+#define DPAA2_ETH_DEBUGFS_H -+ -+#include -+#include "dpaa2-eth.h" -+ -+extern struct dpaa2_eth_priv *priv; -+ -+struct dpaa2_debugfs { -+ struct dentry *dir; -+ struct dentry *fq_stats; -+ struct dentry *ch_stats; -+ struct dentry *cpu_stats; -+ struct dentry *reset_stats; -+}; -+ -+#ifdef CONFIG_FSL_DPAA2_ETH_DEBUGFS -+void dpaa2_eth_dbg_init(void); -+void dpaa2_eth_dbg_exit(void); -+void dpaa2_dbg_add(struct dpaa2_eth_priv *priv); -+void dpaa2_dbg_remove(struct dpaa2_eth_priv *priv); -+#else -+static inline void dpaa2_eth_dbg_init(void) {} -+static inline void dpaa2_eth_dbg_exit(void) {} -+static inline void dpaa2_dbg_add(struct dpaa2_eth_priv *priv) {} -+static inline void dpaa2_dbg_remove(struct dpaa2_eth_priv *priv) {} -+#endif /* CONFIG_FSL_DPAA2_ETH_DEBUGFS */ -+ -+#endif /* DPAA2_ETH_DEBUGFS_H */ -+ -diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-trace.h b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-trace.h -new file mode 100644 -index 0000000..3b040e8 ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-trace.h -@@ -0,0 +1,185 @@ -+/* Copyright 2014-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. -+ */ -+ -+#undef TRACE_SYSTEM -+#define TRACE_SYSTEM dpaa2_eth -+ -+#if !defined(_DPAA2_ETH_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) -+#define _DPAA2_ETH_TRACE_H -+ -+#include -+#include -+#include "dpaa2-eth.h" -+#include -+ -+#define TR_FMT "[%s] fd: addr=0x%llx, len=%u, off=%u" -+/* trace_printk format for raw buffer event class */ -+#define TR_BUF_FMT "[%s] vaddr=%p size=%zu dma_addr=%pad map_size=%zu bpid=%d" -+ -+/* This is used to declare a class of events. -+ * individual events of this type will be defined below. -+ */ -+ -+/* Store details about a frame descriptor */ -+DECLARE_EVENT_CLASS(dpaa2_eth_fd, -+ /* Trace function prototype */ -+ TP_PROTO(struct net_device *netdev, -+ const struct dpaa2_fd *fd), -+ -+ /* Repeat argument list here */ -+ TP_ARGS(netdev, fd), -+ -+ /* A structure containing the relevant information we want -+ * to record. Declare name and type for each normal element, -+ * name, type and size for arrays. Use __string for variable -+ * length strings. -+ */ -+ TP_STRUCT__entry( -+ __field(u64, fd_addr) -+ __field(u32, fd_len) -+ __field(u16, fd_offset) -+ __string(name, netdev->name) -+ ), -+ -+ /* The function that assigns values to the above declared -+ * fields -+ */ -+ TP_fast_assign( -+ __entry->fd_addr = dpaa2_fd_get_addr(fd); -+ __entry->fd_len = dpaa2_fd_get_len(fd); -+ __entry->fd_offset = dpaa2_fd_get_offset(fd); -+ __assign_str(name, netdev->name); -+ ), -+ -+ /* This is what gets printed when the trace event is -+ * triggered. -+ */ -+ TP_printk(TR_FMT, -+ __get_str(name), -+ __entry->fd_addr, -+ __entry->fd_len, -+ __entry->fd_offset) -+); -+ -+/* Now declare events of the above type. Format is: -+ * DEFINE_EVENT(class, name, proto, args), with proto and args same as for class -+ */ -+ -+/* Tx (egress) fd */ -+DEFINE_EVENT(dpaa2_eth_fd, dpaa2_tx_fd, -+ TP_PROTO(struct net_device *netdev, -+ const struct dpaa2_fd *fd), -+ -+ TP_ARGS(netdev, fd) -+); -+ -+/* Rx fd */ -+DEFINE_EVENT(dpaa2_eth_fd, dpaa2_rx_fd, -+ TP_PROTO(struct net_device *netdev, -+ const struct dpaa2_fd *fd), -+ -+ TP_ARGS(netdev, fd) -+); -+ -+/* Tx confirmation fd */ -+DEFINE_EVENT(dpaa2_eth_fd, dpaa2_tx_conf_fd, -+ TP_PROTO(struct net_device *netdev, -+ const struct dpaa2_fd *fd), -+ -+ TP_ARGS(netdev, fd) -+); -+ -+/* Log data about raw buffers. Useful for tracing DPBP content. */ -+TRACE_EVENT(dpaa2_eth_buf_seed, -+ /* Trace function prototype */ -+ TP_PROTO(struct net_device *netdev, -+ /* virtual address and size */ -+ void *vaddr, -+ size_t size, -+ /* dma map address and size */ -+ dma_addr_t dma_addr, -+ size_t map_size, -+ /* buffer pool id, if relevant */ -+ u16 bpid), -+ -+ /* Repeat argument list here */ -+ TP_ARGS(netdev, vaddr, size, dma_addr, map_size, bpid), -+ -+ /* A structure containing the relevant information we want -+ * to record. Declare name and type for each normal element, -+ * name, type and size for arrays. Use __string for variable -+ * length strings. -+ */ -+ TP_STRUCT__entry( -+ __field(void *, vaddr) -+ __field(size_t, size) -+ __field(dma_addr_t, dma_addr) -+ __field(size_t, map_size) -+ __field(u16, bpid) -+ __string(name, netdev->name) -+ ), -+ -+ /* The function that assigns values to the above declared -+ * fields -+ */ -+ TP_fast_assign( -+ __entry->vaddr = vaddr; -+ __entry->size = size; -+ __entry->dma_addr = dma_addr; -+ __entry->map_size = map_size; -+ __entry->bpid = bpid; -+ __assign_str(name, netdev->name); -+ ), -+ -+ /* This is what gets printed when the trace event is -+ * triggered. -+ */ -+ TP_printk(TR_BUF_FMT, -+ __get_str(name), -+ __entry->vaddr, -+ __entry->size, -+ &__entry->dma_addr, -+ __entry->map_size, -+ __entry->bpid) -+); -+ -+/* If only one event of a certain type needs to be declared, use TRACE_EVENT(). -+ * The syntax is the same as for DECLARE_EVENT_CLASS(). -+ */ -+ -+#endif /* _DPAA2_ETH_TRACE_H */ -+ -+/* This must be outside ifdef _DPAA2_ETH_TRACE_H */ -+#undef TRACE_INCLUDE_PATH -+#define TRACE_INCLUDE_PATH . -+#undef TRACE_INCLUDE_FILE -+#define TRACE_INCLUDE_FILE dpaa2-eth-trace -+#include -diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c -new file mode 100644 -index 0000000..27d1a91 ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c -@@ -0,0 +1,2836 @@ -+/* Copyright 2014-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "../../fsl-mc/include/mc.h" -+#include "../../fsl-mc/include/mc-sys.h" -+#include "dpaa2-eth.h" -+ -+/* CREATE_TRACE_POINTS only needs to be defined once. Other dpa files -+ * using trace events only need to #include -+ */ -+#define CREATE_TRACE_POINTS -+#include "dpaa2-eth-trace.h" -+ -+MODULE_LICENSE("Dual BSD/GPL"); -+MODULE_AUTHOR("Freescale Semiconductor, Inc"); -+MODULE_DESCRIPTION("Freescale DPAA2 Ethernet Driver"); -+ -+/* Oldest DPAA2 objects version we are compatible with */ -+#define DPAA2_SUPPORTED_DPNI_VERSION 6 -+#define DPAA2_SUPPORTED_DPBP_VERSION 2 -+#define DPAA2_SUPPORTED_DPCON_VERSION 2 -+ -+static void validate_rx_csum(struct dpaa2_eth_priv *priv, -+ u32 fd_status, -+ struct sk_buff *skb) -+{ -+ skb_checksum_none_assert(skb); -+ -+ /* HW checksum validation is disabled, nothing to do here */ -+ if (!(priv->net_dev->features & NETIF_F_RXCSUM)) -+ return; -+ -+ /* Read checksum validation bits */ -+ if (!((fd_status & DPAA2_FAS_L3CV) && -+ (fd_status & DPAA2_FAS_L4CV))) -+ return; -+ -+ /* Inform the stack there's no need to compute L3/L4 csum anymore */ -+ skb->ip_summed = CHECKSUM_UNNECESSARY; -+} -+ -+/* Free a received FD. -+ * Not to be used for Tx conf FDs or on any other paths. -+ */ -+static void free_rx_fd(struct dpaa2_eth_priv *priv, -+ const struct dpaa2_fd *fd, -+ void *vaddr) -+{ -+ struct device *dev = priv->net_dev->dev.parent; -+ dma_addr_t addr = dpaa2_fd_get_addr(fd); -+ u8 fd_format = dpaa2_fd_get_format(fd); -+ struct dpaa2_sg_entry *sgt; -+ void *sg_vaddr; -+ int i; -+ -+ /* If single buffer frame, just free the data buffer */ -+ if (fd_format == dpaa2_fd_single) -+ goto free_buf; -+ -+ /* For S/G frames, we first need to free all SG entries */ -+ sgt = vaddr + dpaa2_fd_get_offset(fd); -+ for (i = 0; i < DPAA2_ETH_MAX_SG_ENTRIES; i++) { -+ dpaa2_sg_le_to_cpu(&sgt[i]); -+ -+ addr = dpaa2_sg_get_addr(&sgt[i]); -+ dma_unmap_single(dev, addr, DPAA2_ETH_RX_BUF_SIZE, -+ DMA_FROM_DEVICE); -+ -+ sg_vaddr = phys_to_virt(addr); -+ put_page(virt_to_head_page(sg_vaddr)); -+ -+ if (dpaa2_sg_is_final(&sgt[i])) -+ break; -+ } -+ -+free_buf: -+ put_page(virt_to_head_page(vaddr)); -+} -+ -+/* Build a linear skb based on a single-buffer frame descriptor */ -+static struct sk_buff *build_linear_skb(struct dpaa2_eth_priv *priv, -+ struct dpaa2_eth_channel *ch, -+ const struct dpaa2_fd *fd, -+ void *fd_vaddr) -+{ -+ struct sk_buff *skb = NULL; -+ u16 fd_offset = dpaa2_fd_get_offset(fd); -+ u32 fd_length = dpaa2_fd_get_len(fd); -+ -+ skb = build_skb(fd_vaddr, DPAA2_ETH_RX_BUF_SIZE + -+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info))); -+ if (unlikely(!skb)) -+ return NULL; -+ -+ skb_reserve(skb, fd_offset); -+ skb_put(skb, fd_length); -+ -+ ch->buf_count--; -+ -+ return skb; -+} -+ -+/* Build a non linear (fragmented) skb based on a S/G table */ -+static struct sk_buff *build_frag_skb(struct dpaa2_eth_priv *priv, -+ struct dpaa2_eth_channel *ch, -+ struct dpaa2_sg_entry *sgt) -+{ -+ struct sk_buff *skb = NULL; -+ struct device *dev = priv->net_dev->dev.parent; -+ void *sg_vaddr; -+ dma_addr_t sg_addr; -+ u16 sg_offset; -+ u32 sg_length; -+ struct page *page, *head_page; -+ int page_offset; -+ int i; -+ -+ for (i = 0; i < DPAA2_ETH_MAX_SG_ENTRIES; i++) { -+ struct dpaa2_sg_entry *sge = &sgt[i]; -+ -+ dpaa2_sg_le_to_cpu(sge); -+ -+ /* NOTE: We only support SG entries in dpaa2_sg_single format, -+ * but this is the only format we may receive from HW anyway -+ */ -+ -+ /* Get the address and length from the S/G entry */ -+ sg_addr = dpaa2_sg_get_addr(sge); -+ dma_unmap_single(dev, sg_addr, DPAA2_ETH_RX_BUF_SIZE, -+ DMA_FROM_DEVICE); -+ -+ sg_vaddr = phys_to_virt(sg_addr); -+ sg_length = dpaa2_sg_get_len(sge); -+ -+ if (i == 0) { -+ /* We build the skb around the first data buffer */ -+ skb = build_skb(sg_vaddr, DPAA2_ETH_RX_BUF_SIZE + -+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info))); -+ if (unlikely(!skb)) -+ return NULL; -+ -+ sg_offset = dpaa2_sg_get_offset(sge); -+ skb_reserve(skb, sg_offset); -+ skb_put(skb, sg_length); -+ } else { -+ /* Rest of the data buffers are stored as skb frags */ -+ page = virt_to_page(sg_vaddr); -+ head_page = virt_to_head_page(sg_vaddr); -+ -+ /* Offset in page (which may be compound). -+ * Data in subsequent SG entries is stored from the -+ * beginning of the buffer, so we don't need to add the -+ * sg_offset. -+ */ -+ page_offset = ((unsigned long)sg_vaddr & -+ (PAGE_SIZE - 1)) + -+ (page_address(page) - page_address(head_page)); -+ -+ skb_add_rx_frag(skb, i - 1, head_page, page_offset, -+ sg_length, DPAA2_ETH_RX_BUF_SIZE); -+ } -+ -+ if (dpaa2_sg_is_final(sge)) -+ break; -+ } -+ -+ /* Count all data buffers + SG table buffer */ -+ ch->buf_count -= i + 2; -+ -+ return skb; -+} -+ -+/* Main Rx frame processing routine */ -+static void dpaa2_eth_rx(struct dpaa2_eth_priv *priv, -+ struct dpaa2_eth_channel *ch, -+ const struct dpaa2_fd *fd, -+ struct napi_struct *napi) -+{ -+ dma_addr_t addr = dpaa2_fd_get_addr(fd); -+ u8 fd_format = dpaa2_fd_get_format(fd); -+ void *vaddr; -+ struct sk_buff *skb; -+ struct rtnl_link_stats64 *percpu_stats; -+ struct dpaa2_eth_drv_stats *percpu_extras; -+ struct device *dev = priv->net_dev->dev.parent; -+ struct dpaa2_fas *fas; -+ u32 status = 0; -+ -+ /* Tracing point */ -+ trace_dpaa2_rx_fd(priv->net_dev, fd); -+ -+ dma_unmap_single(dev, addr, DPAA2_ETH_RX_BUF_SIZE, DMA_FROM_DEVICE); -+ vaddr = phys_to_virt(addr); -+ -+ prefetch(vaddr + priv->buf_layout.private_data_size); -+ prefetch(vaddr + dpaa2_fd_get_offset(fd)); -+ -+ percpu_stats = this_cpu_ptr(priv->percpu_stats); -+ percpu_extras = this_cpu_ptr(priv->percpu_extras); -+ -+ if (fd_format == dpaa2_fd_single) { -+ skb = build_linear_skb(priv, ch, fd, vaddr); -+ } else if (fd_format == dpaa2_fd_sg) { -+ struct dpaa2_sg_entry *sgt = -+ vaddr + dpaa2_fd_get_offset(fd); -+ skb = build_frag_skb(priv, ch, sgt); -+ put_page(virt_to_head_page(vaddr)); -+ percpu_extras->rx_sg_frames++; -+ percpu_extras->rx_sg_bytes += dpaa2_fd_get_len(fd); -+ } else { -+ /* We don't support any other format */ -+ goto err_frame_format; -+ } -+ -+ if (unlikely(!skb)) -+ goto err_build_skb; -+ -+ prefetch(skb->data); -+ -+ /* Get the timestamp value */ -+ if (priv->ts_rx_en) { -+ struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb); -+ u64 *ns = (u64 *)(vaddr + -+ priv->buf_layout.private_data_size + -+ sizeof(struct dpaa2_fas)); -+ -+ *ns = DPAA2_PTP_NOMINAL_FREQ_PERIOD_NS * (*ns); -+ memset(shhwtstamps, 0, sizeof(*shhwtstamps)); -+ shhwtstamps->hwtstamp = ns_to_ktime(*ns); -+ } -+ -+ /* Check if we need to validate the L4 csum */ -+ if (likely(fd->simple.frc & DPAA2_FD_FRC_FASV)) { -+ fas = (struct dpaa2_fas *) -+ (vaddr + priv->buf_layout.private_data_size); -+ status = le32_to_cpu(fas->status); -+ validate_rx_csum(priv, status, skb); -+ } -+ -+ skb->protocol = eth_type_trans(skb, priv->net_dev); -+ -+ percpu_stats->rx_packets++; -+ percpu_stats->rx_bytes += skb->len; -+ -+ if (priv->net_dev->features & NETIF_F_GRO) -+ napi_gro_receive(napi, skb); -+ else -+ netif_receive_skb(skb); -+ -+ return; -+err_frame_format: -+err_build_skb: -+ free_rx_fd(priv, fd, vaddr); -+ percpu_stats->rx_dropped++; -+} -+ -+#ifdef CONFIG_FSL_DPAA2_ETH_USE_ERR_QUEUE -+/* Processing of Rx frames received on the error FQ -+ * We check and print the error bits and then free the frame -+ */ -+static void dpaa2_eth_rx_err(struct dpaa2_eth_priv *priv, -+ struct dpaa2_eth_channel *ch, -+ const struct dpaa2_fd *fd, -+ struct napi_struct *napi __always_unused) -+{ -+ struct device *dev = priv->net_dev->dev.parent; -+ dma_addr_t addr = dpaa2_fd_get_addr(fd); -+ void *vaddr; -+ struct rtnl_link_stats64 *percpu_stats; -+ struct dpaa2_fas *fas; -+ u32 status = 0; -+ -+ dma_unmap_single(dev, addr, DPAA2_ETH_RX_BUF_SIZE, DMA_FROM_DEVICE); -+ vaddr = phys_to_virt(addr); -+ -+ if (fd->simple.frc & DPAA2_FD_FRC_FASV) { -+ fas = (struct dpaa2_fas *) -+ (vaddr + priv->buf_layout.private_data_size); -+ status = le32_to_cpu(fas->status); -+ if (net_ratelimit()) -+ netdev_warn(priv->net_dev, "Rx frame error: 0x%08x\n", -+ status & DPAA2_ETH_RX_ERR_MASK); -+ } -+ free_rx_fd(priv, fd, vaddr); -+ -+ percpu_stats = this_cpu_ptr(priv->percpu_stats); -+ percpu_stats->rx_errors++; -+} -+#endif -+ -+/* Consume all frames pull-dequeued into the store. This is the simplest way to -+ * make sure we don't accidentally issue another volatile dequeue which would -+ * overwrite (leak) frames already in the store. -+ * -+ * Observance of NAPI budget is not our concern, leaving that to the caller. -+ */ -+static int consume_frames(struct dpaa2_eth_channel *ch) -+{ -+ struct dpaa2_eth_priv *priv = ch->priv; -+ struct dpaa2_eth_fq *fq; -+ struct dpaa2_dq *dq; -+ const struct dpaa2_fd *fd; -+ int cleaned = 0; -+ int is_last; -+ -+ do { -+ dq = dpaa2_io_store_next(ch->store, &is_last); -+ if (unlikely(!dq)) { -+ /* If we're here, we *must* have placed a -+ * volatile dequeue comnmand, so keep reading through -+ * the store until we get some sort of valid response -+ * token (either a valid frame or an "empty dequeue") -+ */ -+ continue; -+ } -+ -+ fd = dpaa2_dq_fd(dq); -+ fq = (struct dpaa2_eth_fq *)dpaa2_dq_fqd_ctx(dq); -+ fq->stats.frames++; -+ -+ fq->consume(priv, ch, fd, &ch->napi); -+ cleaned++; -+ } while (!is_last); -+ -+ return cleaned; -+} -+ -+/* Create a frame descriptor based on a fragmented skb */ -+static int build_sg_fd(struct dpaa2_eth_priv *priv, -+ struct sk_buff *skb, -+ struct dpaa2_fd *fd) -+{ -+ struct device *dev = priv->net_dev->dev.parent; -+ void *sgt_buf = NULL; -+ dma_addr_t addr; -+ int nr_frags = skb_shinfo(skb)->nr_frags; -+ struct dpaa2_sg_entry *sgt; -+ int i, j, err; -+ int sgt_buf_size; -+ struct scatterlist *scl, *crt_scl; -+ int num_sg; -+ int num_dma_bufs; -+ struct dpaa2_eth_swa *swa; -+ -+ /* Create and map scatterlist. -+ * We don't advertise NETIF_F_FRAGLIST, so skb_to_sgvec() will not have -+ * to go beyond nr_frags+1. -+ * Note: We don't support chained scatterlists -+ */ -+ if (unlikely(PAGE_SIZE / sizeof(struct scatterlist) < nr_frags + 1)) -+ return -EINVAL; -+ -+ scl = kcalloc(nr_frags + 1, sizeof(struct scatterlist), GFP_ATOMIC); -+ if (unlikely(!scl)) -+ return -ENOMEM; -+ -+ sg_init_table(scl, nr_frags + 1); -+ num_sg = skb_to_sgvec(skb, scl, 0, skb->len); -+ num_dma_bufs = dma_map_sg(dev, scl, num_sg, DMA_TO_DEVICE); -+ if (unlikely(!num_dma_bufs)) { -+ err = -ENOMEM; -+ goto dma_map_sg_failed; -+ } -+ -+ /* Prepare the HW SGT structure */ -+ sgt_buf_size = priv->tx_data_offset + -+ sizeof(struct dpaa2_sg_entry) * (1 + num_dma_bufs); -+ sgt_buf = kzalloc(sgt_buf_size + DPAA2_ETH_TX_BUF_ALIGN, GFP_ATOMIC); -+ if (unlikely(!sgt_buf)) { -+ err = -ENOMEM; -+ goto sgt_buf_alloc_failed; -+ } -+ sgt_buf = PTR_ALIGN(sgt_buf, DPAA2_ETH_TX_BUF_ALIGN); -+ -+ /* PTA from egress side is passed as is to the confirmation side so -+ * we need to clear some fields here in order to find consistent values -+ * on TX confirmation. We are clearing FAS (Frame Annotation Status) -+ * field here. -+ */ -+ memset(sgt_buf + priv->buf_layout.private_data_size, 0, 8); -+ -+ sgt = (struct dpaa2_sg_entry *)(sgt_buf + priv->tx_data_offset); -+ -+ /* Fill in the HW SGT structure. -+ * -+ * sgt_buf is zeroed out, so the following fields are implicit -+ * in all sgt entries: -+ * - offset is 0 -+ * - format is 'dpaa2_sg_single' -+ */ -+ for_each_sg(scl, crt_scl, num_dma_bufs, i) { -+ dpaa2_sg_set_addr(&sgt[i], sg_dma_address(crt_scl)); -+ dpaa2_sg_set_len(&sgt[i], sg_dma_len(crt_scl)); -+ } -+ dpaa2_sg_set_final(&sgt[i - 1], true); -+ -+ /* Store the skb backpointer in the SGT buffer. -+ * Fit the scatterlist and the number of buffers alongside the -+ * skb backpointer in the SWA. We'll need all of them on Tx Conf. -+ */ -+ swa = (struct dpaa2_eth_swa *)sgt_buf; -+ swa->skb = skb; -+ swa->scl = scl; -+ swa->num_sg = num_sg; -+ swa->num_dma_bufs = num_dma_bufs; -+ -+ /* Hardware expects the SG table to be in little endian format */ -+ for (j = 0; j < i; j++) -+ dpaa2_sg_cpu_to_le(&sgt[j]); -+ -+ /* Separately map the SGT buffer */ -+ addr = dma_map_single(dev, sgt_buf, sgt_buf_size, DMA_TO_DEVICE); -+ if (unlikely(dma_mapping_error(dev, addr))) { -+ err = -ENOMEM; -+ goto dma_map_single_failed; -+ } -+ dpaa2_fd_set_offset(fd, priv->tx_data_offset); -+ dpaa2_fd_set_format(fd, dpaa2_fd_sg); -+ dpaa2_fd_set_addr(fd, addr); -+ dpaa2_fd_set_len(fd, skb->len); -+ -+ fd->simple.ctrl = DPAA2_FD_CTRL_ASAL | DPAA2_FD_CTRL_PTA | -+ DPAA2_FD_CTRL_PTV1; -+ -+ return 0; -+ -+dma_map_single_failed: -+ kfree(sgt_buf); -+sgt_buf_alloc_failed: -+ dma_unmap_sg(dev, scl, num_sg, DMA_TO_DEVICE); -+dma_map_sg_failed: -+ kfree(scl); -+ return err; -+} -+ -+/* Create a frame descriptor based on a linear skb */ -+static int build_single_fd(struct dpaa2_eth_priv *priv, -+ struct sk_buff *skb, -+ struct dpaa2_fd *fd) -+{ -+ struct device *dev = priv->net_dev->dev.parent; -+ u8 *buffer_start; -+ struct sk_buff **skbh; -+ dma_addr_t addr; -+ -+ buffer_start = PTR_ALIGN(skb->data - priv->tx_data_offset - -+ DPAA2_ETH_TX_BUF_ALIGN, -+ DPAA2_ETH_TX_BUF_ALIGN); -+ -+ /* PTA from egress side is passed as is to the confirmation side so -+ * we need to clear some fields here in order to find consistent values -+ * on TX confirmation. We are clearing FAS (Frame Annotation Status) -+ * field here. -+ */ -+ memset(buffer_start + priv->buf_layout.private_data_size, 0, 8); -+ -+ /* Store a backpointer to the skb at the beginning of the buffer -+ * (in the private data area) such that we can release it -+ * on Tx confirm -+ */ -+ skbh = (struct sk_buff **)buffer_start; -+ *skbh = skb; -+ -+ addr = dma_map_single(dev, buffer_start, -+ skb_tail_pointer(skb) - buffer_start, -+ DMA_TO_DEVICE); -+ if (unlikely(dma_mapping_error(dev, addr))) -+ return -ENOMEM; -+ -+ dpaa2_fd_set_addr(fd, addr); -+ dpaa2_fd_set_offset(fd, (u16)(skb->data - buffer_start)); -+ dpaa2_fd_set_len(fd, skb->len); -+ dpaa2_fd_set_format(fd, dpaa2_fd_single); -+ -+ fd->simple.ctrl = DPAA2_FD_CTRL_ASAL | DPAA2_FD_CTRL_PTA | -+ DPAA2_FD_CTRL_PTV1; -+ -+ return 0; -+} -+ -+/* FD freeing routine on the Tx path -+ * -+ * DMA-unmap and free FD and possibly SGT buffer allocated on Tx. The skb -+ * back-pointed to is also freed. -+ * This can be called either from dpaa2_eth_tx_conf() or on the error path of -+ * dpaa2_eth_tx(). -+ * Optionally, return the frame annotation status word (FAS), which needs -+ * to be checked if we're on the confirmation path. -+ */ -+static void free_tx_fd(const struct dpaa2_eth_priv *priv, -+ const struct dpaa2_fd *fd, -+ u32 *status) -+{ -+ struct device *dev = priv->net_dev->dev.parent; -+ dma_addr_t fd_addr; -+ struct sk_buff **skbh, *skb; -+ unsigned char *buffer_start; -+ int unmap_size; -+ struct scatterlist *scl; -+ int num_sg, num_dma_bufs; -+ struct dpaa2_eth_swa *swa; -+ bool fd_single; -+ struct dpaa2_fas *fas; -+ -+ fd_addr = dpaa2_fd_get_addr(fd); -+ skbh = phys_to_virt(fd_addr); -+ fd_single = (dpaa2_fd_get_format(fd) == dpaa2_fd_single); -+ -+ if (fd_single) { -+ skb = *skbh; -+ buffer_start = (unsigned char *)skbh; -+ /* Accessing the skb buffer is safe before dma unmap, because -+ * we didn't map the actual skb shell. -+ */ -+ dma_unmap_single(dev, fd_addr, -+ skb_tail_pointer(skb) - buffer_start, -+ DMA_TO_DEVICE); -+ } else { -+ swa = (struct dpaa2_eth_swa *)skbh; -+ skb = swa->skb; -+ scl = swa->scl; -+ num_sg = swa->num_sg; -+ num_dma_bufs = swa->num_dma_bufs; -+ -+ /* Unmap the scatterlist */ -+ dma_unmap_sg(dev, scl, num_sg, DMA_TO_DEVICE); -+ kfree(scl); -+ -+ /* Unmap the SGT buffer */ -+ unmap_size = priv->tx_data_offset + -+ sizeof(struct dpaa2_sg_entry) * (1 + num_dma_bufs); -+ dma_unmap_single(dev, fd_addr, unmap_size, DMA_TO_DEVICE); -+ } -+ -+ /* Get the timestamp value */ -+ if (priv->ts_tx_en && skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) { -+ struct skb_shared_hwtstamps shhwtstamps; -+ u64 *ns; -+ -+ memset(&shhwtstamps, 0, sizeof(shhwtstamps)); -+ -+ ns = (u64 *)((void *)skbh + -+ priv->buf_layout.private_data_size + -+ sizeof(struct dpaa2_fas)); -+ *ns = DPAA2_PTP_NOMINAL_FREQ_PERIOD_NS * (*ns); -+ shhwtstamps.hwtstamp = ns_to_ktime(*ns); -+ skb_tstamp_tx(skb, &shhwtstamps); -+ } -+ -+ /* Read the status from the Frame Annotation after we unmap the first -+ * buffer but before we free it. The caller function is responsible -+ * for checking the status value. -+ */ -+ if (status && (fd->simple.frc & DPAA2_FD_FRC_FASV)) { -+ fas = (struct dpaa2_fas *) -+ ((void *)skbh + priv->buf_layout.private_data_size); -+ *status = le32_to_cpu(fas->status); -+ } -+ -+ /* Free SGT buffer kmalloc'ed on tx */ -+ if (!fd_single) -+ kfree(skbh); -+ -+ /* Move on with skb release */ -+ dev_kfree_skb(skb); -+} -+ -+static int dpaa2_eth_tx(struct sk_buff *skb, struct net_device *net_dev) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ struct dpaa2_fd fd; -+ struct rtnl_link_stats64 *percpu_stats; -+ struct dpaa2_eth_drv_stats *percpu_extras; -+ u16 queue_mapping, flow_id; -+ int err, i; -+ -+ percpu_stats = this_cpu_ptr(priv->percpu_stats); -+ percpu_extras = this_cpu_ptr(priv->percpu_extras); -+ -+ if (unlikely(skb_headroom(skb) < DPAA2_ETH_NEEDED_HEADROOM(priv))) { -+ struct sk_buff *ns; -+ -+ ns = skb_realloc_headroom(skb, DPAA2_ETH_NEEDED_HEADROOM(priv)); -+ if (unlikely(!ns)) { -+ percpu_stats->tx_dropped++; -+ goto err_alloc_headroom; -+ } -+ dev_kfree_skb(skb); -+ skb = ns; -+ } -+ -+ /* We'll be holding a back-reference to the skb until Tx Confirmation; -+ * we don't want that overwritten by a concurrent Tx with a cloned skb. -+ */ -+ skb = skb_unshare(skb, GFP_ATOMIC); -+ if (unlikely(!skb)) { -+ /* skb_unshare() has already freed the skb */ -+ percpu_stats->tx_dropped++; -+ return NETDEV_TX_OK; -+ } -+ -+ /* Setup the FD fields */ -+ memset(&fd, 0, sizeof(fd)); -+ -+ if (skb_is_nonlinear(skb)) { -+ err = build_sg_fd(priv, skb, &fd); -+ percpu_extras->tx_sg_frames++; -+ percpu_extras->tx_sg_bytes += skb->len; -+ } else { -+ err = build_single_fd(priv, skb, &fd); -+ } -+ -+ if (unlikely(err)) { -+ percpu_stats->tx_dropped++; -+ goto err_build_fd; -+ } -+ -+ /* Tracing point */ -+ trace_dpaa2_tx_fd(net_dev, &fd); -+ -+ /* TxConf FQ selection primarily based on cpu affinity; this is -+ * non-migratable context, so it's safe to call smp_processor_id(). -+ */ -+ queue_mapping = smp_processor_id() % priv->dpni_attrs.max_senders; -+ flow_id = priv->fq[queue_mapping].flowid; -+ for (i = 0; i < (DPAA2_ETH_MAX_TX_QUEUES << 1); i++) { -+ err = dpaa2_io_service_enqueue_qd(NULL, priv->tx_qdid, 0, -+ flow_id, &fd); -+ if (err != -EBUSY) -+ break; -+ } -+ percpu_extras->tx_portal_busy += i; -+ if (unlikely(err < 0)) { -+ percpu_stats->tx_errors++; -+ /* Clean up everything, including freeing the skb */ -+ free_tx_fd(priv, &fd, NULL); -+ } else { -+ percpu_stats->tx_packets++; -+ percpu_stats->tx_bytes += skb->len; -+ } -+ -+ return NETDEV_TX_OK; -+ -+err_build_fd: -+err_alloc_headroom: -+ dev_kfree_skb(skb); -+ -+ return NETDEV_TX_OK; -+} -+ -+/* Tx confirmation frame processing routine */ -+static void dpaa2_eth_tx_conf(struct dpaa2_eth_priv *priv, -+ struct dpaa2_eth_channel *ch, -+ const struct dpaa2_fd *fd, -+ struct napi_struct *napi __always_unused) -+{ -+ struct rtnl_link_stats64 *percpu_stats; -+ struct dpaa2_eth_drv_stats *percpu_extras; -+ u32 status = 0; -+ -+ /* Tracing point */ -+ trace_dpaa2_tx_conf_fd(priv->net_dev, fd); -+ -+ percpu_extras = this_cpu_ptr(priv->percpu_extras); -+ percpu_extras->tx_conf_frames++; -+ percpu_extras->tx_conf_bytes += dpaa2_fd_get_len(fd); -+ -+ free_tx_fd(priv, fd, &status); -+ -+ if (unlikely(status & DPAA2_ETH_TXCONF_ERR_MASK)) { -+ percpu_stats = this_cpu_ptr(priv->percpu_stats); -+ /* Tx-conf logically pertains to the egress path. */ -+ percpu_stats->tx_errors++; -+ } -+} -+ -+static int set_rx_csum(struct dpaa2_eth_priv *priv, bool enable) -+{ -+ int err; -+ -+ err = dpni_set_l3_chksum_validation(priv->mc_io, 0, priv->mc_token, -+ enable); -+ if (err) { -+ netdev_err(priv->net_dev, -+ "dpni_set_l3_chksum_validation() failed\n"); -+ return err; -+ } -+ -+ err = dpni_set_l4_chksum_validation(priv->mc_io, 0, priv->mc_token, -+ enable); -+ if (err) { -+ netdev_err(priv->net_dev, -+ "dpni_set_l4_chksum_validation failed\n"); -+ return err; -+ } -+ -+ return 0; -+} -+ -+static int set_tx_csum(struct dpaa2_eth_priv *priv, bool enable) -+{ -+ struct dpaa2_eth_fq *fq; -+ struct dpni_tx_flow_cfg tx_flow_cfg; -+ int err; -+ int i; -+ -+ memset(&tx_flow_cfg, 0, sizeof(tx_flow_cfg)); -+ tx_flow_cfg.options = DPNI_TX_FLOW_OPT_L3_CHKSUM_GEN | -+ DPNI_TX_FLOW_OPT_L4_CHKSUM_GEN; -+ tx_flow_cfg.l3_chksum_gen = enable; -+ tx_flow_cfg.l4_chksum_gen = enable; -+ -+ for (i = 0; i < priv->num_fqs; i++) { -+ fq = &priv->fq[i]; -+ if (fq->type != DPAA2_TX_CONF_FQ) -+ continue; -+ -+ /* The Tx flowid is kept in the corresponding TxConf FQ. */ -+ err = dpni_set_tx_flow(priv->mc_io, 0, priv->mc_token, -+ &fq->flowid, &tx_flow_cfg); -+ if (err) { -+ netdev_err(priv->net_dev, "dpni_set_tx_flow failed\n"); -+ return err; -+ } -+ } -+ -+ return 0; -+} -+ -+/* Perform a single release command to add buffers -+ * to the specified buffer pool -+ */ -+static int add_bufs(struct dpaa2_eth_priv *priv, u16 bpid) -+{ -+ struct device *dev = priv->net_dev->dev.parent; -+ u64 buf_array[DPAA2_ETH_BUFS_PER_CMD]; -+ void *buf; -+ dma_addr_t addr; -+ int i; -+ -+ for (i = 0; i < DPAA2_ETH_BUFS_PER_CMD; i++) { -+ /* Allocate buffer visible to WRIOP + skb shared info + -+ * alignment padding -+ */ -+ buf = napi_alloc_frag(DPAA2_ETH_BUF_RAW_SIZE); -+ if (unlikely(!buf)) -+ goto err_alloc; -+ -+ buf = PTR_ALIGN(buf, DPAA2_ETH_RX_BUF_ALIGN); -+ -+ addr = dma_map_single(dev, buf, DPAA2_ETH_RX_BUF_SIZE, -+ DMA_FROM_DEVICE); -+ if (unlikely(dma_mapping_error(dev, addr))) -+ goto err_map; -+ -+ buf_array[i] = addr; -+ -+ /* tracing point */ -+ trace_dpaa2_eth_buf_seed(priv->net_dev, -+ buf, DPAA2_ETH_BUF_RAW_SIZE, -+ addr, DPAA2_ETH_RX_BUF_SIZE, -+ bpid); -+ } -+ -+release_bufs: -+ /* In case the portal is busy, retry until successful. -+ * The buffer release function would only fail if the QBMan portal -+ * was busy, which implies portal contention (i.e. more CPUs than -+ * portals, i.e. GPPs w/o affine DPIOs). For all practical purposes, -+ * there is little we can realistically do, short of giving up - -+ * in which case we'd risk depleting the buffer pool and never again -+ * receiving the Rx interrupt which would kick-start the refill logic. -+ * So just keep retrying, at the risk of being moved to ksoftirqd. -+ */ -+ while (dpaa2_io_service_release(NULL, bpid, buf_array, i)) -+ cpu_relax(); -+ return i; -+ -+err_map: -+ put_page(virt_to_head_page(buf)); -+err_alloc: -+ if (i) -+ goto release_bufs; -+ -+ return 0; -+} -+ -+static int seed_pool(struct dpaa2_eth_priv *priv, u16 bpid) -+{ -+ int i, j; -+ int new_count; -+ -+ /* This is the lazy seeding of Rx buffer pools. -+ * dpaa2_add_bufs() is also used on the Rx hotpath and calls -+ * napi_alloc_frag(). The trouble with that is that it in turn ends up -+ * calling this_cpu_ptr(), which mandates execution in atomic context. -+ * Rather than splitting up the code, do a one-off preempt disable. -+ */ -+ preempt_disable(); -+ for (j = 0; j < priv->num_channels; j++) { -+ for (i = 0; i < DPAA2_ETH_NUM_BUFS; -+ i += DPAA2_ETH_BUFS_PER_CMD) { -+ new_count = add_bufs(priv, bpid); -+ priv->channel[j]->buf_count += new_count; -+ -+ if (new_count < DPAA2_ETH_BUFS_PER_CMD) { -+ preempt_enable(); -+ return -ENOMEM; -+ } -+ } -+ } -+ preempt_enable(); -+ -+ return 0; -+} -+ -+/** -+ * Drain the specified number of buffers from the DPNI's private buffer pool. -+ * @count must not exceeed DPAA2_ETH_BUFS_PER_CMD -+ */ -+static void drain_bufs(struct dpaa2_eth_priv *priv, int count) -+{ -+ struct device *dev = priv->net_dev->dev.parent; -+ u64 buf_array[DPAA2_ETH_BUFS_PER_CMD]; -+ void *vaddr; -+ int ret, i; -+ -+ do { -+ ret = dpaa2_io_service_acquire(NULL, priv->dpbp_attrs.bpid, -+ buf_array, count); -+ if (ret < 0) { -+ netdev_err(priv->net_dev, "dpaa2_io_service_acquire() failed\n"); -+ return; -+ } -+ for (i = 0; i < ret; i++) { -+ /* Same logic as on regular Rx path */ -+ dma_unmap_single(dev, buf_array[i], -+ DPAA2_ETH_RX_BUF_SIZE, -+ DMA_FROM_DEVICE); -+ vaddr = phys_to_virt(buf_array[i]); -+ put_page(virt_to_head_page(vaddr)); -+ } -+ } while (ret); -+} -+ -+static void drain_pool(struct dpaa2_eth_priv *priv) -+{ -+ int i; -+ -+ drain_bufs(priv, DPAA2_ETH_BUFS_PER_CMD); -+ drain_bufs(priv, 1); -+ -+ for (i = 0; i < priv->num_channels; i++) -+ priv->channel[i]->buf_count = 0; -+} -+ -+/* Function is called from softirq context only, so we don't need to guard -+ * the access to percpu count -+ */ -+static int refill_pool(struct dpaa2_eth_priv *priv, -+ struct dpaa2_eth_channel *ch, -+ u16 bpid) -+{ -+ int new_count; -+ -+ if (likely(ch->buf_count >= DPAA2_ETH_REFILL_THRESH)) -+ return 0; -+ -+ do { -+ new_count = add_bufs(priv, bpid); -+ if (unlikely(!new_count)) { -+ /* Out of memory; abort for now, we'll try later on */ -+ break; -+ } -+ ch->buf_count += new_count; -+ } while (ch->buf_count < DPAA2_ETH_NUM_BUFS); -+ -+ if (unlikely(ch->buf_count < DPAA2_ETH_NUM_BUFS)) -+ return -ENOMEM; -+ -+ return 0; -+} -+ -+static int pull_channel(struct dpaa2_eth_channel *ch) -+{ -+ int err; -+ int dequeues = -1; -+ -+ /* Retry while portal is busy */ -+ do { -+ err = dpaa2_io_service_pull_channel(NULL, ch->ch_id, ch->store); -+ dequeues++; -+ cpu_relax(); -+ } while (err == -EBUSY); -+ -+ ch->stats.dequeue_portal_busy += dequeues; -+ if (unlikely(err)) -+ ch->stats.pull_err++; -+ -+ return err; -+} -+ -+/* NAPI poll routine -+ * -+ * Frames are dequeued from the QMan channel associated with this NAPI context. -+ * Rx, Tx confirmation and (if configured) Rx error frames all count -+ * towards the NAPI budget. -+ */ -+static int dpaa2_eth_poll(struct napi_struct *napi, int budget) -+{ -+ struct dpaa2_eth_channel *ch; -+ int cleaned = 0, store_cleaned; -+ struct dpaa2_eth_priv *priv; -+ int err; -+ -+ ch = container_of(napi, struct dpaa2_eth_channel, napi); -+ priv = ch->priv; -+ -+ while (cleaned < budget) { -+ err = pull_channel(ch); -+ if (unlikely(err)) -+ break; -+ -+ /* Refill pool if appropriate */ -+ refill_pool(priv, ch, priv->dpbp_attrs.bpid); -+ -+ store_cleaned = consume_frames(ch); -+ cleaned += store_cleaned; -+ -+ /* If we have enough budget left for a full store, -+ * try a new pull dequeue, otherwise we're done here -+ */ -+ if (store_cleaned == 0 || -+ cleaned > budget - DPAA2_ETH_STORE_SIZE) -+ break; -+ } -+ -+ if (cleaned < budget) { -+ napi_complete_done(napi, cleaned); -+ /* Re-enable data available notifications */ -+ do { -+ err = dpaa2_io_service_rearm(NULL, &ch->nctx); -+ cpu_relax(); -+ } while (err == -EBUSY); -+ } -+ -+ ch->stats.frames += cleaned; -+ -+ return cleaned; -+} -+ -+static void enable_ch_napi(struct dpaa2_eth_priv *priv) -+{ -+ struct dpaa2_eth_channel *ch; -+ int i; -+ -+ for (i = 0; i < priv->num_channels; i++) { -+ ch = priv->channel[i]; -+ napi_enable(&ch->napi); -+ } -+} -+ -+static void disable_ch_napi(struct dpaa2_eth_priv *priv) -+{ -+ struct dpaa2_eth_channel *ch; -+ int i; -+ -+ for (i = 0; i < priv->num_channels; i++) { -+ ch = priv->channel[i]; -+ napi_disable(&ch->napi); -+ } -+} -+ -+static int link_state_update(struct dpaa2_eth_priv *priv) -+{ -+ struct dpni_link_state state; -+ int err; -+ -+ err = dpni_get_link_state(priv->mc_io, 0, priv->mc_token, &state); -+ if (unlikely(err)) { -+ netdev_err(priv->net_dev, -+ "dpni_get_link_state() failed\n"); -+ return err; -+ } -+ -+ /* Chech link state; speed / duplex changes are not treated yet */ -+ if (priv->link_state.up == state.up) -+ return 0; -+ -+ priv->link_state = state; -+ if (state.up) { -+ netif_carrier_on(priv->net_dev); -+ netif_tx_start_all_queues(priv->net_dev); -+ } else { -+ netif_tx_stop_all_queues(priv->net_dev); -+ netif_carrier_off(priv->net_dev); -+ } -+ -+ netdev_info(priv->net_dev, "Link Event: state %s", -+ state.up ? "up" : "down"); -+ -+ return 0; -+} -+ -+static int dpaa2_eth_open(struct net_device *net_dev) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ int err; -+ -+ err = seed_pool(priv, priv->dpbp_attrs.bpid); -+ if (err) { -+ /* Not much to do; the buffer pool, though not filled up, -+ * may still contain some buffers which would enable us -+ * to limp on. -+ */ -+ netdev_err(net_dev, "Buffer seeding failed for DPBP %d (bpid=%d)\n", -+ priv->dpbp_dev->obj_desc.id, priv->dpbp_attrs.bpid); -+ } -+ -+ /* We'll only start the txqs when the link is actually ready; make sure -+ * we don't race against the link up notification, which may come -+ * immediately after dpni_enable(); -+ */ -+ netif_tx_stop_all_queues(net_dev); -+ enable_ch_napi(priv); -+ /* Also, explicitly set carrier off, otherwise netif_carrier_ok() will -+ * return true and cause 'ip link show' to report the LOWER_UP flag, -+ * even though the link notification wasn't even received. -+ */ -+ netif_carrier_off(net_dev); -+ -+ err = dpni_enable(priv->mc_io, 0, priv->mc_token); -+ if (err < 0) { -+ netdev_err(net_dev, "dpni_enable() failed\n"); -+ goto enable_err; -+ } -+ -+ /* If the DPMAC object has already processed the link up interrupt, -+ * we have to learn the link state ourselves. -+ */ -+ err = link_state_update(priv); -+ if (err < 0) { -+ netdev_err(net_dev, "Can't update link state\n"); -+ goto link_state_err; -+ } -+ -+ return 0; -+ -+link_state_err: -+enable_err: -+ disable_ch_napi(priv); -+ drain_pool(priv); -+ return err; -+} -+ -+/* The DPIO store must be empty when we call this, -+ * at the end of every NAPI cycle. -+ */ -+static u32 drain_channel(struct dpaa2_eth_priv *priv, -+ struct dpaa2_eth_channel *ch) -+{ -+ u32 drained = 0, total = 0; -+ -+ do { -+ pull_channel(ch); -+ drained = consume_frames(ch); -+ total += drained; -+ } while (drained); -+ -+ return total; -+} -+ -+static u32 drain_ingress_frames(struct dpaa2_eth_priv *priv) -+{ -+ struct dpaa2_eth_channel *ch; -+ int i; -+ u32 drained = 0; -+ -+ for (i = 0; i < priv->num_channels; i++) { -+ ch = priv->channel[i]; -+ drained += drain_channel(priv, ch); -+ } -+ -+ return drained; -+} -+ -+static int dpaa2_eth_stop(struct net_device *net_dev) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ int dpni_enabled; -+ int retries = 10; -+ u32 drained; -+ -+ netif_tx_stop_all_queues(net_dev); -+ netif_carrier_off(net_dev); -+ -+ /* Loop while dpni_disable() attempts to drain the egress FQs -+ * and confirm them back to us. -+ */ -+ do { -+ dpni_disable(priv->mc_io, 0, priv->mc_token); -+ dpni_is_enabled(priv->mc_io, 0, priv->mc_token, &dpni_enabled); -+ if (dpni_enabled) -+ /* Allow the MC some slack */ -+ msleep(100); -+ } while (dpni_enabled && --retries); -+ if (!retries) { -+ netdev_warn(net_dev, "Retry count exceeded disabling DPNI\n"); -+ /* Must go on and disable NAPI nonetheless, so we don't crash at -+ * the next "ifconfig up" -+ */ -+ } -+ -+ /* Wait for NAPI to complete on every core and disable it. -+ * In particular, this will also prevent NAPI from being rescheduled if -+ * a new CDAN is serviced, effectively discarding the CDAN. We therefore -+ * don't even need to disarm the channels, except perhaps for the case -+ * of a huge coalescing value. -+ */ -+ disable_ch_napi(priv); -+ -+ /* Manually drain the Rx and TxConf queues */ -+ drained = drain_ingress_frames(priv); -+ if (drained) -+ netdev_dbg(net_dev, "Drained %d frames.\n", drained); -+ -+ /* Empty the buffer pool */ -+ drain_pool(priv); -+ -+ return 0; -+} -+ -+static int dpaa2_eth_init(struct net_device *net_dev) -+{ -+ u64 supported = 0; -+ u64 not_supported = 0; -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ u32 options = priv->dpni_attrs.options; -+ -+ /* Capabilities listing */ -+ supported |= IFF_LIVE_ADDR_CHANGE | IFF_PROMISC | IFF_ALLMULTI; -+ -+ if (options & DPNI_OPT_UNICAST_FILTER) -+ supported |= IFF_UNICAST_FLT; -+ else -+ not_supported |= IFF_UNICAST_FLT; -+ -+ if (options & DPNI_OPT_MULTICAST_FILTER) -+ supported |= IFF_MULTICAST; -+ else -+ not_supported |= IFF_MULTICAST; -+ -+ net_dev->priv_flags |= supported; -+ net_dev->priv_flags &= ~not_supported; -+ -+ /* Features */ -+ net_dev->features = NETIF_F_RXCSUM | -+ NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | -+ NETIF_F_SG | NETIF_F_HIGHDMA | -+ NETIF_F_LLTX; -+ net_dev->hw_features = net_dev->features; -+ -+ return 0; -+} -+ -+static int dpaa2_eth_set_addr(struct net_device *net_dev, void *addr) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ struct device *dev = net_dev->dev.parent; -+ int err; -+ -+ err = eth_mac_addr(net_dev, addr); -+ if (err < 0) { -+ dev_err(dev, "eth_mac_addr() failed with error %d\n", err); -+ return err; -+ } -+ -+ err = dpni_set_primary_mac_addr(priv->mc_io, 0, priv->mc_token, -+ net_dev->dev_addr); -+ if (err) { -+ dev_err(dev, "dpni_set_primary_mac_addr() failed (%d)\n", err); -+ return err; -+ } -+ -+ return 0; -+} -+ -+/** Fill in counters maintained by the GPP driver. These may be different from -+ * the hardware counters obtained by ethtool. -+ */ -+static struct rtnl_link_stats64 -+*dpaa2_eth_get_stats(struct net_device *net_dev, -+ struct rtnl_link_stats64 *stats) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ struct rtnl_link_stats64 *percpu_stats; -+ u64 *cpustats; -+ u64 *netstats = (u64 *)stats; -+ int i, j; -+ int num = sizeof(struct rtnl_link_stats64) / sizeof(u64); -+ -+ for_each_possible_cpu(i) { -+ percpu_stats = per_cpu_ptr(priv->percpu_stats, i); -+ cpustats = (u64 *)percpu_stats; -+ for (j = 0; j < num; j++) -+ netstats[j] += cpustats[j]; -+ } -+ -+ return stats; -+} -+ -+static int dpaa2_eth_change_mtu(struct net_device *net_dev, int mtu) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ int err; -+ -+ if (mtu < 68 || mtu > DPAA2_ETH_MAX_MTU) { -+ netdev_err(net_dev, "Invalid MTU %d. Valid range is: 68..%d\n", -+ mtu, DPAA2_ETH_MAX_MTU); -+ return -EINVAL; -+ } -+ -+ /* Set the maximum Rx frame length to match the transmit side; -+ * account for L2 headers when computing the MFL -+ */ -+ err = dpni_set_max_frame_length(priv->mc_io, 0, priv->mc_token, -+ (u16)DPAA2_ETH_L2_MAX_FRM(mtu)); -+ if (err) { -+ netdev_err(net_dev, "dpni_set_max_frame_length() failed\n"); -+ return err; -+ } -+ -+ net_dev->mtu = mtu; -+ return 0; -+} -+ -+/* Copy mac unicast addresses from @net_dev to @priv. -+ * Its sole purpose is to make dpaa2_eth_set_rx_mode() more readable. -+ */ -+static void add_uc_hw_addr(const struct net_device *net_dev, -+ struct dpaa2_eth_priv *priv) -+{ -+ struct netdev_hw_addr *ha; -+ int err; -+ -+ netdev_for_each_uc_addr(ha, net_dev) { -+ err = dpni_add_mac_addr(priv->mc_io, 0, priv->mc_token, -+ ha->addr); -+ if (err) -+ netdev_warn(priv->net_dev, -+ "Could not add ucast MAC %pM to the filtering table (err %d)\n", -+ ha->addr, err); -+ } -+} -+ -+/* Copy mac multicast addresses from @net_dev to @priv -+ * Its sole purpose is to make dpaa2_eth_set_rx_mode() more readable. -+ */ -+static void add_mc_hw_addr(const struct net_device *net_dev, -+ struct dpaa2_eth_priv *priv) -+{ -+ struct netdev_hw_addr *ha; -+ int err; -+ -+ netdev_for_each_mc_addr(ha, net_dev) { -+ err = dpni_add_mac_addr(priv->mc_io, 0, priv->mc_token, -+ ha->addr); -+ if (err) -+ netdev_warn(priv->net_dev, -+ "Could not add mcast MAC %pM to the filtering table (err %d)\n", -+ ha->addr, err); -+ } -+} -+ -+static void dpaa2_eth_set_rx_mode(struct net_device *net_dev) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ int uc_count = netdev_uc_count(net_dev); -+ int mc_count = netdev_mc_count(net_dev); -+ u8 max_uc = priv->dpni_attrs.max_unicast_filters; -+ u8 max_mc = priv->dpni_attrs.max_multicast_filters; -+ u32 options = priv->dpni_attrs.options; -+ u16 mc_token = priv->mc_token; -+ struct fsl_mc_io *mc_io = priv->mc_io; -+ int err; -+ -+ /* Basic sanity checks; these probably indicate a misconfiguration */ -+ if (!(options & DPNI_OPT_UNICAST_FILTER) && max_uc != 0) -+ netdev_info(net_dev, -+ "max_unicast_filters=%d, DPNI_OPT_UNICAST_FILTER option must be enabled\n", -+ max_uc); -+ if (!(options & DPNI_OPT_MULTICAST_FILTER) && max_mc != 0) -+ netdev_info(net_dev, -+ "max_multicast_filters=%d, DPNI_OPT_MULTICAST_FILTER option must be enabled\n", -+ max_mc); -+ -+ /* Force promiscuous if the uc or mc counts exceed our capabilities. */ -+ if (uc_count > max_uc) { -+ netdev_info(net_dev, -+ "Unicast addr count reached %d, max allowed is %d; forcing promisc\n", -+ uc_count, max_uc); -+ goto force_promisc; -+ } -+ if (mc_count > max_mc) { -+ netdev_info(net_dev, -+ "Multicast addr count reached %d, max allowed is %d; forcing promisc\n", -+ mc_count, max_mc); -+ goto force_mc_promisc; -+ } -+ -+ /* Adjust promisc settings due to flag combinations */ -+ if (net_dev->flags & IFF_PROMISC) -+ goto force_promisc; -+ if (net_dev->flags & IFF_ALLMULTI) { -+ /* First, rebuild unicast filtering table. This should be done -+ * in promisc mode, in order to avoid frame loss while we -+ * progressively add entries to the table. -+ * We don't know whether we had been in promisc already, and -+ * making an MC call to find it is expensive; so set uc promisc -+ * nonetheless. -+ */ -+ err = dpni_set_unicast_promisc(mc_io, 0, mc_token, 1); -+ if (err) -+ netdev_warn(net_dev, "Can't set uc promisc\n"); -+ -+ /* Actual uc table reconstruction. */ -+ err = dpni_clear_mac_filters(mc_io, 0, mc_token, 1, 0); -+ if (err) -+ netdev_warn(net_dev, "Can't clear uc filters\n"); -+ add_uc_hw_addr(net_dev, priv); -+ -+ /* Finally, clear uc promisc and set mc promisc as requested. */ -+ err = dpni_set_unicast_promisc(mc_io, 0, mc_token, 0); -+ if (err) -+ netdev_warn(net_dev, "Can't clear uc promisc\n"); -+ goto force_mc_promisc; -+ } -+ -+ /* Neither unicast, nor multicast promisc will be on... eventually. -+ * For now, rebuild mac filtering tables while forcing both of them on. -+ */ -+ err = dpni_set_unicast_promisc(mc_io, 0, mc_token, 1); -+ if (err) -+ netdev_warn(net_dev, "Can't set uc promisc (%d)\n", err); -+ err = dpni_set_multicast_promisc(mc_io, 0, mc_token, 1); -+ if (err) -+ netdev_warn(net_dev, "Can't set mc promisc (%d)\n", err); -+ -+ /* Actual mac filtering tables reconstruction */ -+ err = dpni_clear_mac_filters(mc_io, 0, mc_token, 1, 1); -+ if (err) -+ netdev_warn(net_dev, "Can't clear mac filters\n"); -+ add_mc_hw_addr(net_dev, priv); -+ add_uc_hw_addr(net_dev, priv); -+ -+ /* Now we can clear both ucast and mcast promisc, without risking -+ * to drop legitimate frames anymore. -+ */ -+ err = dpni_set_unicast_promisc(mc_io, 0, mc_token, 0); -+ if (err) -+ netdev_warn(net_dev, "Can't clear ucast promisc\n"); -+ err = dpni_set_multicast_promisc(mc_io, 0, mc_token, 0); -+ if (err) -+ netdev_warn(net_dev, "Can't clear mcast promisc\n"); -+ -+ return; -+ -+force_promisc: -+ err = dpni_set_unicast_promisc(mc_io, 0, mc_token, 1); -+ if (err) -+ netdev_warn(net_dev, "Can't set ucast promisc\n"); -+force_mc_promisc: -+ err = dpni_set_multicast_promisc(mc_io, 0, mc_token, 1); -+ if (err) -+ netdev_warn(net_dev, "Can't set mcast promisc\n"); -+} -+ -+static int dpaa2_eth_set_features(struct net_device *net_dev, -+ netdev_features_t features) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ netdev_features_t changed = features ^ net_dev->features; -+ bool enable; -+ int err; -+ -+ if (changed & NETIF_F_RXCSUM) { -+ enable = !!(features & NETIF_F_RXCSUM); -+ err = set_rx_csum(priv, enable); -+ if (err) -+ return err; -+ } -+ -+ if (changed & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) { -+ enable = !!(features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)); -+ err = set_tx_csum(priv, enable); -+ if (err) -+ return err; -+ } -+ -+ return 0; -+} -+ -+static int dpaa2_eth_ts_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(dev); -+ struct hwtstamp_config config; -+ -+ if (copy_from_user(&config, rq->ifr_data, sizeof(config))) -+ return -EFAULT; -+ -+ switch (config.tx_type) { -+ case HWTSTAMP_TX_OFF: -+ priv->ts_tx_en = false; -+ break; -+ case HWTSTAMP_TX_ON: -+ priv->ts_tx_en = true; -+ break; -+ default: -+ return -ERANGE; -+ } -+ -+ if (config.rx_filter == HWTSTAMP_FILTER_NONE) { -+ priv->ts_rx_en = false; -+ } else { -+ priv->ts_rx_en = true; -+ /* TS is set for all frame types, not only those requested */ -+ config.rx_filter = HWTSTAMP_FILTER_ALL; -+ } -+ -+ return copy_to_user(rq->ifr_data, &config, sizeof(config)) ? -+ -EFAULT : 0; -+} -+ -+static int dpaa2_eth_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -+{ -+ if (cmd == SIOCSHWTSTAMP) -+ return dpaa2_eth_ts_ioctl(dev, rq, cmd); -+ -+ return -EINVAL; -+} -+ -+static const struct net_device_ops dpaa2_eth_ops = { -+ .ndo_open = dpaa2_eth_open, -+ .ndo_start_xmit = dpaa2_eth_tx, -+ .ndo_stop = dpaa2_eth_stop, -+ .ndo_init = dpaa2_eth_init, -+ .ndo_set_mac_address = dpaa2_eth_set_addr, -+ .ndo_get_stats64 = dpaa2_eth_get_stats, -+ .ndo_change_mtu = dpaa2_eth_change_mtu, -+ .ndo_set_rx_mode = dpaa2_eth_set_rx_mode, -+ .ndo_set_features = dpaa2_eth_set_features, -+ .ndo_do_ioctl = dpaa2_eth_ioctl, -+}; -+ -+static void cdan_cb(struct dpaa2_io_notification_ctx *ctx) -+{ -+ struct dpaa2_eth_channel *ch; -+ -+ ch = container_of(ctx, struct dpaa2_eth_channel, nctx); -+ -+ /* Update NAPI statistics */ -+ ch->stats.cdan++; -+ -+ napi_schedule_irqoff(&ch->napi); -+} -+ -+/* Verify that the FLIB API version of various MC objects is supported -+ * by our driver -+ */ -+static int check_obj_version(struct fsl_mc_device *ls_dev, u16 mc_version) -+{ -+ char *name = ls_dev->obj_desc.type; -+ struct device *dev = &ls_dev->dev; -+ u16 supported_version, flib_version; -+ -+ if (strcmp(name, "dpni") == 0) { -+ flib_version = DPNI_VER_MAJOR; -+ supported_version = DPAA2_SUPPORTED_DPNI_VERSION; -+ } else if (strcmp(name, "dpbp") == 0) { -+ flib_version = DPBP_VER_MAJOR; -+ supported_version = DPAA2_SUPPORTED_DPBP_VERSION; -+ } else if (strcmp(name, "dpcon") == 0) { -+ flib_version = DPCON_VER_MAJOR; -+ supported_version = DPAA2_SUPPORTED_DPCON_VERSION; -+ } else { -+ dev_err(dev, "invalid object type (%s)\n", name); -+ return -EINVAL; -+ } -+ -+ /* Check that the FLIB-defined version matches the one reported by MC */ -+ if (mc_version != flib_version) { -+ dev_err(dev, "%s FLIB version mismatch: MC reports %d, we have %d\n", -+ name, mc_version, flib_version); -+ return -EINVAL; -+ } -+ -+ /* ... and that we actually support it */ -+ if (mc_version < supported_version) { -+ dev_err(dev, "Unsupported %s FLIB version (%d)\n", -+ name, mc_version); -+ return -EINVAL; -+ } -+ dev_dbg(dev, "Using %s FLIB version %d\n", name, mc_version); -+ -+ return 0; -+} -+ -+/* Allocate and configure a DPCON object */ -+static struct fsl_mc_device *setup_dpcon(struct dpaa2_eth_priv *priv) -+{ -+ struct fsl_mc_device *dpcon; -+ struct device *dev = priv->net_dev->dev.parent; -+ struct dpcon_attr attrs; -+ int err; -+ -+ err = fsl_mc_object_allocate(to_fsl_mc_device(dev), -+ FSL_MC_POOL_DPCON, &dpcon); -+ if (err) { -+ dev_info(dev, "Not enough DPCONs, will go on as-is\n"); -+ return NULL; -+ } -+ -+ err = dpcon_open(priv->mc_io, 0, dpcon->obj_desc.id, &dpcon->mc_handle); -+ if (err) { -+ dev_err(dev, "dpcon_open() failed\n"); -+ goto err_open; -+ } -+ -+ err = dpcon_get_attributes(priv->mc_io, 0, dpcon->mc_handle, &attrs); -+ if (err) { -+ dev_err(dev, "dpcon_get_attributes() failed\n"); -+ goto err_get_attr; -+ } -+ -+ err = check_obj_version(dpcon, attrs.version.major); -+ if (err) -+ goto err_dpcon_ver; -+ -+ err = dpcon_enable(priv->mc_io, 0, dpcon->mc_handle); -+ if (err) { -+ dev_err(dev, "dpcon_enable() failed\n"); -+ goto err_enable; -+ } -+ -+ return dpcon; -+ -+err_enable: -+err_dpcon_ver: -+err_get_attr: -+ dpcon_close(priv->mc_io, 0, dpcon->mc_handle); -+err_open: -+ fsl_mc_object_free(dpcon); -+ -+ return NULL; -+} -+ -+static void free_dpcon(struct dpaa2_eth_priv *priv, -+ struct fsl_mc_device *dpcon) -+{ -+ dpcon_disable(priv->mc_io, 0, dpcon->mc_handle); -+ dpcon_close(priv->mc_io, 0, dpcon->mc_handle); -+ fsl_mc_object_free(dpcon); -+} -+ -+static struct dpaa2_eth_channel * -+alloc_channel(struct dpaa2_eth_priv *priv) -+{ -+ struct dpaa2_eth_channel *channel; -+ struct dpcon_attr attr; -+ struct device *dev = priv->net_dev->dev.parent; -+ int err; -+ -+ channel = kzalloc(sizeof(*channel), GFP_ATOMIC); -+ if (!channel) -+ return NULL; -+ -+ channel->dpcon = setup_dpcon(priv); -+ if (!channel->dpcon) -+ goto err_setup; -+ -+ err = dpcon_get_attributes(priv->mc_io, 0, channel->dpcon->mc_handle, -+ &attr); -+ if (err) { -+ dev_err(dev, "dpcon_get_attributes() failed\n"); -+ goto err_get_attr; -+ } -+ -+ channel->dpcon_id = attr.id; -+ channel->ch_id = attr.qbman_ch_id; -+ channel->priv = priv; -+ -+ return channel; -+ -+err_get_attr: -+ free_dpcon(priv, channel->dpcon); -+err_setup: -+ kfree(channel); -+ return NULL; -+} -+ -+static void free_channel(struct dpaa2_eth_priv *priv, -+ struct dpaa2_eth_channel *channel) -+{ -+ free_dpcon(priv, channel->dpcon); -+ kfree(channel); -+} -+ -+/* DPIO setup: allocate and configure QBMan channels, setup core affinity -+ * and register data availability notifications -+ */ -+static int setup_dpio(struct dpaa2_eth_priv *priv) -+{ -+ struct dpaa2_io_notification_ctx *nctx; -+ struct dpaa2_eth_channel *channel; -+ struct dpcon_notification_cfg dpcon_notif_cfg; -+ struct device *dev = priv->net_dev->dev.parent; -+ int i, err; -+ -+ /* Don't allocate more channels than strictly necessary and assign -+ * them to cores starting from the first one available in -+ * cpu_online_mask. -+ * If the number of channels is lower than the number of cores, -+ * there will be no rx/tx conf processing on the last cores in the mask. -+ */ -+ cpumask_clear(&priv->dpio_cpumask); -+ for_each_online_cpu(i) { -+ /* Try to allocate a channel */ -+ channel = alloc_channel(priv); -+ if (!channel) -+ goto err_alloc_ch; -+ -+ priv->channel[priv->num_channels] = channel; -+ -+ nctx = &channel->nctx; -+ nctx->is_cdan = 1; -+ nctx->cb = cdan_cb; -+ nctx->id = channel->ch_id; -+ nctx->desired_cpu = i; -+ -+ /* Register the new context */ -+ err = dpaa2_io_service_register(NULL, nctx); -+ if (err) { -+ dev_info(dev, "No affine DPIO for core %d\n", i); -+ /* This core doesn't have an affine DPIO, but there's -+ * a chance another one does, so keep trying -+ */ -+ free_channel(priv, channel); -+ continue; -+ } -+ -+ /* Register DPCON notification with MC */ -+ dpcon_notif_cfg.dpio_id = nctx->dpio_id; -+ dpcon_notif_cfg.priority = 0; -+ dpcon_notif_cfg.user_ctx = nctx->qman64; -+ err = dpcon_set_notification(priv->mc_io, 0, -+ channel->dpcon->mc_handle, -+ &dpcon_notif_cfg); -+ if (err) { -+ dev_err(dev, "dpcon_set_notification failed()\n"); -+ goto err_set_cdan; -+ } -+ -+ /* If we managed to allocate a channel and also found an affine -+ * DPIO for this core, add it to the final mask -+ */ -+ cpumask_set_cpu(i, &priv->dpio_cpumask); -+ priv->num_channels++; -+ -+ if (priv->num_channels == dpaa2_eth_max_channels(priv)) -+ break; -+ } -+ -+ /* Tx confirmation queues can only be serviced by cpus -+ * with an affine DPIO/channel -+ */ -+ cpumask_copy(&priv->txconf_cpumask, &priv->dpio_cpumask); -+ -+ return 0; -+ -+err_set_cdan: -+ dpaa2_io_service_deregister(NULL, nctx); -+ free_channel(priv, channel); -+err_alloc_ch: -+ if (cpumask_empty(&priv->dpio_cpumask)) { -+ dev_err(dev, "No cpu with an affine DPIO/DPCON\n"); -+ return -ENODEV; -+ } -+ cpumask_copy(&priv->txconf_cpumask, &priv->dpio_cpumask); -+ -+ return 0; -+} -+ -+static void free_dpio(struct dpaa2_eth_priv *priv) -+{ -+ int i; -+ struct dpaa2_eth_channel *ch; -+ -+ /* deregister CDAN notifications and free channels */ -+ for (i = 0; i < priv->num_channels; i++) { -+ ch = priv->channel[i]; -+ dpaa2_io_service_deregister(NULL, &ch->nctx); -+ free_channel(priv, ch); -+ } -+} -+ -+static struct dpaa2_eth_channel *get_affine_channel(struct dpaa2_eth_priv *priv, -+ int cpu) -+{ -+ struct device *dev = priv->net_dev->dev.parent; -+ int i; -+ -+ for (i = 0; i < priv->num_channels; i++) -+ if (priv->channel[i]->nctx.desired_cpu == cpu) -+ return priv->channel[i]; -+ -+ /* We should never get here. Issue a warning and return -+ * the first channel, because it's still better than nothing -+ */ -+ dev_warn(dev, "No affine channel found for cpu %d\n", cpu); -+ -+ return priv->channel[0]; -+} -+ -+static void set_fq_affinity(struct dpaa2_eth_priv *priv) -+{ -+ struct device *dev = priv->net_dev->dev.parent; -+ struct dpaa2_eth_fq *fq; -+ int rx_cpu, txc_cpu; -+ int i; -+ -+ /* For each FQ, pick one channel/CPU to deliver frames to. -+ * This may well change at runtime, either through irqbalance or -+ * through direct user intervention. -+ */ -+ rx_cpu = cpumask_first(&priv->dpio_cpumask); -+ txc_cpu = cpumask_first(&priv->txconf_cpumask); -+ -+ for (i = 0; i < priv->num_fqs; i++) { -+ fq = &priv->fq[i]; -+ switch (fq->type) { -+ case DPAA2_RX_FQ: -+ case DPAA2_RX_ERR_FQ: -+ fq->target_cpu = rx_cpu; -+ rx_cpu = cpumask_next(rx_cpu, &priv->dpio_cpumask); -+ if (rx_cpu >= nr_cpu_ids) -+ rx_cpu = cpumask_first(&priv->dpio_cpumask); -+ break; -+ case DPAA2_TX_CONF_FQ: -+ fq->target_cpu = txc_cpu; -+ txc_cpu = cpumask_next(txc_cpu, &priv->txconf_cpumask); -+ if (txc_cpu >= nr_cpu_ids) -+ txc_cpu = cpumask_first(&priv->txconf_cpumask); -+ break; -+ default: -+ dev_err(dev, "Unknown FQ type: %d\n", fq->type); -+ } -+ fq->channel = get_affine_channel(priv, fq->target_cpu); -+ } -+} -+ -+static void setup_fqs(struct dpaa2_eth_priv *priv) -+{ -+ int i; -+ -+ /* We have one TxConf FQ per Tx flow */ -+ for (i = 0; i < priv->dpni_attrs.max_senders; i++) { -+ priv->fq[priv->num_fqs].type = DPAA2_TX_CONF_FQ; -+ priv->fq[priv->num_fqs].consume = dpaa2_eth_tx_conf; -+ priv->fq[priv->num_fqs++].flowid = DPNI_NEW_FLOW_ID; -+ } -+ -+ /* The number of Rx queues (Rx distribution width) may be different from -+ * the number of cores. -+ * We only support one traffic class for now. -+ */ -+ for (i = 0; i < dpaa2_eth_queue_count(priv); i++) { -+ priv->fq[priv->num_fqs].type = DPAA2_RX_FQ; -+ priv->fq[priv->num_fqs].consume = dpaa2_eth_rx; -+ priv->fq[priv->num_fqs++].flowid = (u16)i; -+ } -+ -+#ifdef CONFIG_FSL_DPAA2_ETH_USE_ERR_QUEUE -+ /* We have exactly one Rx error queue per DPNI */ -+ priv->fq[priv->num_fqs].type = DPAA2_RX_ERR_FQ; -+ priv->fq[priv->num_fqs++].consume = dpaa2_eth_rx_err; -+#endif -+ -+ /* For each FQ, decide on which core to process incoming frames */ -+ set_fq_affinity(priv); -+} -+ -+/* Allocate and configure one buffer pool for each interface */ -+static int setup_dpbp(struct dpaa2_eth_priv *priv) -+{ -+ int err; -+ struct fsl_mc_device *dpbp_dev; -+ struct device *dev = priv->net_dev->dev.parent; -+ -+ err = fsl_mc_object_allocate(to_fsl_mc_device(dev), FSL_MC_POOL_DPBP, -+ &dpbp_dev); -+ if (err) { -+ dev_err(dev, "DPBP device allocation failed\n"); -+ return err; -+ } -+ -+ priv->dpbp_dev = dpbp_dev; -+ -+ err = dpbp_open(priv->mc_io, 0, priv->dpbp_dev->obj_desc.id, -+ &dpbp_dev->mc_handle); -+ if (err) { -+ dev_err(dev, "dpbp_open() failed\n"); -+ goto err_open; -+ } -+ -+ err = dpbp_enable(priv->mc_io, 0, dpbp_dev->mc_handle); -+ if (err) { -+ dev_err(dev, "dpbp_enable() failed\n"); -+ goto err_enable; -+ } -+ -+ err = dpbp_get_attributes(priv->mc_io, 0, dpbp_dev->mc_handle, -+ &priv->dpbp_attrs); -+ if (err) { -+ dev_err(dev, "dpbp_get_attributes() failed\n"); -+ goto err_get_attr; -+ } -+ -+ err = check_obj_version(dpbp_dev, priv->dpbp_attrs.version.major); -+ if (err) -+ goto err_dpbp_ver; -+ -+ return 0; -+ -+err_dpbp_ver: -+err_get_attr: -+ dpbp_disable(priv->mc_io, 0, dpbp_dev->mc_handle); -+err_enable: -+ dpbp_close(priv->mc_io, 0, dpbp_dev->mc_handle); -+err_open: -+ fsl_mc_object_free(dpbp_dev); -+ -+ return err; -+} -+ -+static void free_dpbp(struct dpaa2_eth_priv *priv) -+{ -+ drain_pool(priv); -+ dpbp_disable(priv->mc_io, 0, priv->dpbp_dev->mc_handle); -+ dpbp_close(priv->mc_io, 0, priv->dpbp_dev->mc_handle); -+ fsl_mc_object_free(priv->dpbp_dev); -+} -+ -+/* Configure the DPNI object this interface is associated with */ -+static int setup_dpni(struct fsl_mc_device *ls_dev) -+{ -+ struct device *dev = &ls_dev->dev; -+ struct dpaa2_eth_priv *priv; -+ struct net_device *net_dev; -+ void *dma_mem; -+ int err; -+ -+ net_dev = dev_get_drvdata(dev); -+ priv = netdev_priv(net_dev); -+ -+ priv->dpni_id = ls_dev->obj_desc.id; -+ -+ /* get a handle for the DPNI object */ -+ err = dpni_open(priv->mc_io, 0, priv->dpni_id, &priv->mc_token); -+ if (err) { -+ dev_err(dev, "dpni_open() failed\n"); -+ goto err_open; -+ } -+ -+ ls_dev->mc_io = priv->mc_io; -+ ls_dev->mc_handle = priv->mc_token; -+ -+ /* Map a memory region which will be used by MC to pass us an -+ * attribute structure -+ */ -+ dma_mem = kzalloc(DPAA2_EXT_CFG_SIZE, GFP_DMA | GFP_KERNEL); -+ if (!dma_mem) -+ goto err_alloc; -+ -+ priv->dpni_attrs.ext_cfg_iova = dma_map_single(dev, dma_mem, -+ DPAA2_EXT_CFG_SIZE, -+ DMA_FROM_DEVICE); -+ if (dma_mapping_error(dev, priv->dpni_attrs.ext_cfg_iova)) { -+ dev_err(dev, "dma mapping for dpni_ext_cfg failed\n"); -+ goto err_dma_map; -+ } -+ -+ err = dpni_get_attributes(priv->mc_io, 0, priv->mc_token, -+ &priv->dpni_attrs); -+ -+ /* We'll check the return code after unmapping, as we need to -+ * do this anyway -+ */ -+ dma_unmap_single(dev, priv->dpni_attrs.ext_cfg_iova, -+ DPAA2_EXT_CFG_SIZE, DMA_FROM_DEVICE); -+ -+ if (err) { -+ dev_err(dev, "dpni_get_attributes() failed (err=%d)\n", err); -+ goto err_get_attr; -+ } -+ -+ err = check_obj_version(ls_dev, priv->dpni_attrs.version.major); -+ if (err) -+ goto err_dpni_ver; -+ -+ memset(&priv->dpni_ext_cfg, 0, sizeof(priv->dpni_ext_cfg)); -+ err = dpni_extract_extended_cfg(&priv->dpni_ext_cfg, dma_mem); -+ if (err) { -+ dev_err(dev, "dpni_extract_extended_cfg() failed\n"); -+ goto err_extract; -+ } -+ -+ /* Configure our buffers' layout */ -+ priv->buf_layout.options = DPNI_BUF_LAYOUT_OPT_PARSER_RESULT | -+ DPNI_BUF_LAYOUT_OPT_FRAME_STATUS | -+ DPNI_BUF_LAYOUT_OPT_PRIVATE_DATA_SIZE | -+ DPNI_BUF_LAYOUT_OPT_DATA_ALIGN; -+ priv->buf_layout.pass_parser_result = true; -+ priv->buf_layout.pass_frame_status = true; -+ priv->buf_layout.private_data_size = DPAA2_ETH_SWA_SIZE; -+ /* HW erratum mandates data alignment in multiples of 256 */ -+ priv->buf_layout.data_align = DPAA2_ETH_RX_BUF_ALIGN; -+ -+ /* rx buffer */ -+ err = dpni_set_rx_buffer_layout(priv->mc_io, 0, priv->mc_token, -+ &priv->buf_layout); -+ if (err) { -+ dev_err(dev, "dpni_set_rx_buffer_layout() failed"); -+ goto err_buf_layout; -+ } -+ /* tx buffer: remove Rx-only options */ -+ priv->buf_layout.options &= ~(DPNI_BUF_LAYOUT_OPT_DATA_ALIGN | -+ DPNI_BUF_LAYOUT_OPT_PARSER_RESULT); -+ err = dpni_set_tx_buffer_layout(priv->mc_io, 0, priv->mc_token, -+ &priv->buf_layout); -+ if (err) { -+ dev_err(dev, "dpni_set_tx_buffer_layout() failed"); -+ goto err_buf_layout; -+ } -+ /* tx-confirm: same options as tx */ -+ priv->buf_layout.options &= ~DPNI_BUF_LAYOUT_OPT_PRIVATE_DATA_SIZE; -+ priv->buf_layout.options |= DPNI_BUF_LAYOUT_OPT_TIMESTAMP; -+ priv->buf_layout.pass_timestamp = 1; -+ err = dpni_set_tx_conf_buffer_layout(priv->mc_io, 0, priv->mc_token, -+ &priv->buf_layout); -+ if (err) { -+ dev_err(dev, "dpni_set_tx_conf_buffer_layout() failed"); -+ goto err_buf_layout; -+ } -+ /* Now that we've set our tx buffer layout, retrieve the minimum -+ * required tx data offset. -+ */ -+ err = dpni_get_tx_data_offset(priv->mc_io, 0, priv->mc_token, -+ &priv->tx_data_offset); -+ if (err) { -+ dev_err(dev, "dpni_get_tx_data_offset() failed\n"); -+ goto err_data_offset; -+ } -+ -+ if ((priv->tx_data_offset % 64) != 0) -+ dev_warn(dev, "Tx data offset (%d) not a multiple of 64B", -+ priv->tx_data_offset); -+ -+ /* Accommodate SWA space. */ -+ priv->tx_data_offset += DPAA2_ETH_SWA_SIZE; -+ -+ /* allocate classification rule space */ -+ priv->cls_rule = kzalloc(sizeof(*priv->cls_rule) * -+ DPAA2_CLASSIFIER_ENTRY_COUNT, GFP_KERNEL); -+ if (!priv->cls_rule) -+ goto err_cls_rule; -+ -+ kfree(dma_mem); -+ -+ return 0; -+ -+err_cls_rule: -+err_data_offset: -+err_buf_layout: -+err_extract: -+err_dpni_ver: -+err_get_attr: -+err_dma_map: -+ kfree(dma_mem); -+err_alloc: -+ dpni_close(priv->mc_io, 0, priv->mc_token); -+err_open: -+ return err; -+} -+ -+static void free_dpni(struct dpaa2_eth_priv *priv) -+{ -+ int err; -+ -+ err = dpni_reset(priv->mc_io, 0, priv->mc_token); -+ if (err) -+ netdev_warn(priv->net_dev, "dpni_reset() failed (err %d)\n", -+ err); -+ -+ dpni_close(priv->mc_io, 0, priv->mc_token); -+} -+ -+static int setup_rx_flow(struct dpaa2_eth_priv *priv, -+ struct dpaa2_eth_fq *fq) -+{ -+ struct device *dev = priv->net_dev->dev.parent; -+ struct dpni_queue_attr rx_queue_attr; -+ struct dpni_queue_cfg queue_cfg; -+ int err; -+ -+ memset(&queue_cfg, 0, sizeof(queue_cfg)); -+ queue_cfg.options = DPNI_QUEUE_OPT_USER_CTX | DPNI_QUEUE_OPT_DEST | -+ DPNI_QUEUE_OPT_TAILDROP_THRESHOLD; -+ queue_cfg.dest_cfg.dest_type = DPNI_DEST_DPCON; -+ queue_cfg.dest_cfg.priority = 1; -+ queue_cfg.user_ctx = (u64)fq; -+ queue_cfg.dest_cfg.dest_id = fq->channel->dpcon_id; -+ queue_cfg.tail_drop_threshold = DPAA2_ETH_TAILDROP_THRESH; -+ err = dpni_set_rx_flow(priv->mc_io, 0, priv->mc_token, 0, fq->flowid, -+ &queue_cfg); -+ if (err) { -+ dev_err(dev, "dpni_set_rx_flow() failed\n"); -+ return err; -+ } -+ -+ /* Get the actual FQID that was assigned by MC */ -+ err = dpni_get_rx_flow(priv->mc_io, 0, priv->mc_token, 0, fq->flowid, -+ &rx_queue_attr); -+ if (err) { -+ dev_err(dev, "dpni_get_rx_flow() failed\n"); -+ return err; -+ } -+ fq->fqid = rx_queue_attr.fqid; -+ -+ return 0; -+} -+ -+static int setup_tx_flow(struct dpaa2_eth_priv *priv, -+ struct dpaa2_eth_fq *fq) -+{ -+ struct device *dev = priv->net_dev->dev.parent; -+ struct dpni_tx_flow_cfg tx_flow_cfg; -+ struct dpni_tx_conf_cfg tx_conf_cfg; -+ struct dpni_tx_conf_attr tx_conf_attr; -+ int err; -+ -+ memset(&tx_flow_cfg, 0, sizeof(tx_flow_cfg)); -+ tx_flow_cfg.options = DPNI_TX_FLOW_OPT_TX_CONF_ERROR; -+ tx_flow_cfg.use_common_tx_conf_queue = 0; -+ err = dpni_set_tx_flow(priv->mc_io, 0, priv->mc_token, -+ &fq->flowid, &tx_flow_cfg); -+ if (err) { -+ dev_err(dev, "dpni_set_tx_flow() failed\n"); -+ return err; -+ } -+ -+ tx_conf_cfg.errors_only = 0; -+ tx_conf_cfg.queue_cfg.options = DPNI_QUEUE_OPT_USER_CTX | -+ DPNI_QUEUE_OPT_DEST; -+ tx_conf_cfg.queue_cfg.user_ctx = (u64)fq; -+ tx_conf_cfg.queue_cfg.dest_cfg.dest_type = DPNI_DEST_DPCON; -+ tx_conf_cfg.queue_cfg.dest_cfg.dest_id = fq->channel->dpcon_id; -+ tx_conf_cfg.queue_cfg.dest_cfg.priority = 0; -+ -+ err = dpni_set_tx_conf(priv->mc_io, 0, priv->mc_token, fq->flowid, -+ &tx_conf_cfg); -+ if (err) { -+ dev_err(dev, "dpni_set_tx_conf() failed\n"); -+ return err; -+ } -+ -+ err = dpni_get_tx_conf(priv->mc_io, 0, priv->mc_token, fq->flowid, -+ &tx_conf_attr); -+ if (err) { -+ dev_err(dev, "dpni_get_tx_conf() failed\n"); -+ return err; -+ } -+ -+ fq->fqid = tx_conf_attr.queue_attr.fqid; -+ -+ return 0; -+} -+ -+#ifdef CONFIG_FSL_DPAA2_ETH_USE_ERR_QUEUE -+static int setup_rx_err_flow(struct dpaa2_eth_priv *priv, -+ struct dpaa2_eth_fq *fq) -+{ -+ struct dpni_queue_attr queue_attr; -+ struct dpni_queue_cfg queue_cfg; -+ int err; -+ -+ /* Configure the Rx error queue to generate CDANs, -+ * just like the Rx queues -+ */ -+ queue_cfg.options = DPNI_QUEUE_OPT_USER_CTX | DPNI_QUEUE_OPT_DEST; -+ queue_cfg.dest_cfg.dest_type = DPNI_DEST_DPCON; -+ queue_cfg.dest_cfg.priority = 1; -+ queue_cfg.user_ctx = (u64)fq; -+ queue_cfg.dest_cfg.dest_id = fq->channel->dpcon_id; -+ err = dpni_set_rx_err_queue(priv->mc_io, 0, priv->mc_token, &queue_cfg); -+ if (err) { -+ netdev_err(priv->net_dev, "dpni_set_rx_err_queue() failed\n"); -+ return err; -+ } -+ -+ /* Get the FQID */ -+ err = dpni_get_rx_err_queue(priv->mc_io, 0, priv->mc_token, -+ &queue_attr); -+ if (err) { -+ netdev_err(priv->net_dev, "dpni_get_rx_err_queue() failed\n"); -+ return err; -+ } -+ fq->fqid = queue_attr.fqid; -+ -+ return 0; -+} -+#endif -+ -+/* Bind the DPNI to its needed objects and resources: buffer pool, DPIOs, -+ * frame queues and channels -+ */ -+static int bind_dpni(struct dpaa2_eth_priv *priv) -+{ -+ struct net_device *net_dev = priv->net_dev; -+ struct device *dev = net_dev->dev.parent; -+ struct dpni_pools_cfg pools_params; -+ struct dpni_error_cfg err_cfg; -+ int err = 0; -+ int i; -+ -+ pools_params.num_dpbp = 1; -+ pools_params.pools[0].dpbp_id = priv->dpbp_dev->obj_desc.id; -+ pools_params.pools[0].backup_pool = 0; -+ pools_params.pools[0].buffer_size = DPAA2_ETH_RX_BUF_SIZE; -+ err = dpni_set_pools(priv->mc_io, 0, priv->mc_token, &pools_params); -+ if (err) { -+ dev_err(dev, "dpni_set_pools() failed\n"); -+ return err; -+ } -+ -+ check_fs_support(net_dev); -+ -+ /* have the interface implicitly distribute traffic based on supported -+ * header fields -+ */ -+ if (dpaa2_eth_hash_enabled(priv)) { -+ err = dpaa2_eth_set_hash(net_dev, DPAA2_RXH_SUPPORTED); -+ if (err) -+ return err; -+ } -+ -+ /* Configure handling of error frames */ -+ err_cfg.errors = DPAA2_ETH_RX_ERR_MASK; -+ err_cfg.set_frame_annotation = 1; -+#ifdef CONFIG_FSL_DPAA2_ETH_USE_ERR_QUEUE -+ err_cfg.error_action = DPNI_ERROR_ACTION_SEND_TO_ERROR_QUEUE; -+#else -+ err_cfg.error_action = DPNI_ERROR_ACTION_DISCARD; -+#endif -+ err = dpni_set_errors_behavior(priv->mc_io, 0, priv->mc_token, -+ &err_cfg); -+ if (err) { -+ dev_err(dev, "dpni_set_errors_behavior failed\n"); -+ return err; -+ } -+ -+ /* Configure Rx and Tx conf queues to generate CDANs */ -+ for (i = 0; i < priv->num_fqs; i++) { -+ switch (priv->fq[i].type) { -+ case DPAA2_RX_FQ: -+ err = setup_rx_flow(priv, &priv->fq[i]); -+ break; -+ case DPAA2_TX_CONF_FQ: -+ err = setup_tx_flow(priv, &priv->fq[i]); -+ break; -+#ifdef CONFIG_FSL_DPAA2_ETH_USE_ERR_QUEUE -+ case DPAA2_RX_ERR_FQ: -+ err = setup_rx_err_flow(priv, &priv->fq[i]); -+ break; -+#endif -+ default: -+ dev_err(dev, "Invalid FQ type %d\n", priv->fq[i].type); -+ return -EINVAL; -+ } -+ if (err) -+ return err; -+ } -+ -+ err = dpni_get_qdid(priv->mc_io, 0, priv->mc_token, &priv->tx_qdid); -+ if (err) { -+ dev_err(dev, "dpni_get_qdid() failed\n"); -+ return err; -+ } -+ -+ return 0; -+} -+ -+/* Allocate rings for storing incoming frame descriptors */ -+static int alloc_rings(struct dpaa2_eth_priv *priv) -+{ -+ struct net_device *net_dev = priv->net_dev; -+ struct device *dev = net_dev->dev.parent; -+ int i; -+ -+ for (i = 0; i < priv->num_channels; i++) { -+ priv->channel[i]->store = -+ dpaa2_io_store_create(DPAA2_ETH_STORE_SIZE, dev); -+ if (!priv->channel[i]->store) { -+ netdev_err(net_dev, "dpaa2_io_store_create() failed\n"); -+ goto err_ring; -+ } -+ } -+ -+ return 0; -+ -+err_ring: -+ for (i = 0; i < priv->num_channels; i++) { -+ if (!priv->channel[i]->store) -+ break; -+ dpaa2_io_store_destroy(priv->channel[i]->store); -+ } -+ -+ return -ENOMEM; -+} -+ -+static void free_rings(struct dpaa2_eth_priv *priv) -+{ -+ int i; -+ -+ for (i = 0; i < priv->num_channels; i++) -+ dpaa2_io_store_destroy(priv->channel[i]->store); -+} -+ -+static int netdev_init(struct net_device *net_dev) -+{ -+ int err; -+ struct device *dev = net_dev->dev.parent; -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ u8 mac_addr[ETH_ALEN]; -+ u8 bcast_addr[ETH_ALEN]; -+ -+ net_dev->netdev_ops = &dpaa2_eth_ops; -+ -+ /* If the DPNI attributes contain an all-0 mac_addr, -+ * set a random hardware address -+ */ -+ err = dpni_get_primary_mac_addr(priv->mc_io, 0, priv->mc_token, -+ mac_addr); -+ if (err) { -+ dev_err(dev, "dpni_get_primary_mac_addr() failed (%d)", err); -+ return err; -+ } -+ if (is_zero_ether_addr(mac_addr)) { -+ /* Fills in net_dev->dev_addr, as required by -+ * register_netdevice() -+ */ -+ eth_hw_addr_random(net_dev); -+ /* Make the user aware, without cluttering the boot log */ -+ pr_info_once(KBUILD_MODNAME " device(s) have all-zero hwaddr, replaced with random"); -+ err = dpni_set_primary_mac_addr(priv->mc_io, 0, priv->mc_token, -+ net_dev->dev_addr); -+ if (err) { -+ dev_err(dev, "dpni_set_primary_mac_addr(): %d\n", err); -+ return err; -+ } -+ /* Override NET_ADDR_RANDOM set by eth_hw_addr_random(); for all -+ * practical purposes, this will be our "permanent" mac address, -+ * at least until the next reboot. This move will also permit -+ * register_netdevice() to properly fill up net_dev->perm_addr. -+ */ -+ net_dev->addr_assign_type = NET_ADDR_PERM; -+ } else { -+ /* NET_ADDR_PERM is default, all we have to do is -+ * fill in the device addr. -+ */ -+ memcpy(net_dev->dev_addr, mac_addr, net_dev->addr_len); -+ } -+ -+ /* Explicitly add the broadcast address to the MAC filtering table; -+ * the MC won't do that for us. -+ */ -+ eth_broadcast_addr(bcast_addr); -+ err = dpni_add_mac_addr(priv->mc_io, 0, priv->mc_token, bcast_addr); -+ if (err) { -+ dev_warn(dev, "dpni_add_mac_addr() failed (%d)\n", err); -+ /* Won't return an error; at least, we'd have egress traffic */ -+ } -+ -+ /* Reserve enough space to align buffer as per hardware requirement; -+ * NOTE: priv->tx_data_offset MUST be initialized at this point. -+ */ -+ net_dev->needed_headroom = DPAA2_ETH_NEEDED_HEADROOM(priv); -+ -+ /* Our .ndo_init will be called herein */ -+ err = register_netdev(net_dev); -+ if (err < 0) { -+ dev_err(dev, "register_netdev() = %d\n", err); -+ return err; -+ } -+ -+ return 0; -+} -+ -+static int poll_link_state(void *arg) -+{ -+ struct dpaa2_eth_priv *priv = (struct dpaa2_eth_priv *)arg; -+ int err; -+ -+ while (!kthread_should_stop()) { -+ err = link_state_update(priv); -+ if (unlikely(err)) -+ return err; -+ -+ msleep(DPAA2_ETH_LINK_STATE_REFRESH); -+ } -+ -+ return 0; -+} -+ -+static irqreturn_t dpni_irq0_handler(int irq_num, void *arg) -+{ -+ return IRQ_WAKE_THREAD; -+} -+ -+static irqreturn_t dpni_irq0_handler_thread(int irq_num, void *arg) -+{ -+ u8 irq_index = DPNI_IRQ_INDEX; -+ u32 status, clear = 0; -+ struct device *dev = (struct device *)arg; -+ struct fsl_mc_device *dpni_dev = to_fsl_mc_device(dev); -+ struct net_device *net_dev = dev_get_drvdata(dev); -+ int err; -+ -+ err = dpni_get_irq_status(dpni_dev->mc_io, 0, dpni_dev->mc_handle, -+ irq_index, &status); -+ if (unlikely(err)) { -+ netdev_err(net_dev, "Can't get irq status (err %d)", err); -+ clear = 0xffffffff; -+ goto out; -+ } -+ -+ if (status & DPNI_IRQ_EVENT_LINK_CHANGED) { -+ clear |= DPNI_IRQ_EVENT_LINK_CHANGED; -+ link_state_update(netdev_priv(net_dev)); -+ } -+ -+out: -+ dpni_clear_irq_status(dpni_dev->mc_io, 0, dpni_dev->mc_handle, -+ irq_index, clear); -+ return IRQ_HANDLED; -+} -+ -+static int setup_irqs(struct fsl_mc_device *ls_dev) -+{ -+ int err = 0; -+ struct fsl_mc_device_irq *irq; -+ u8 irq_index = DPNI_IRQ_INDEX; -+ u32 mask = DPNI_IRQ_EVENT_LINK_CHANGED; -+ -+ err = fsl_mc_allocate_irqs(ls_dev); -+ if (err) { -+ dev_err(&ls_dev->dev, "MC irqs allocation failed\n"); -+ return err; -+ } -+ -+ irq = ls_dev->irqs[0]; -+ err = devm_request_threaded_irq(&ls_dev->dev, irq->irq_number, -+ dpni_irq0_handler, -+ dpni_irq0_handler_thread, -+ IRQF_NO_SUSPEND | IRQF_ONESHOT, -+ dev_name(&ls_dev->dev), &ls_dev->dev); -+ if (err < 0) { -+ dev_err(&ls_dev->dev, "devm_request_threaded_irq(): %d", err); -+ goto free_mc_irq; -+ } -+ -+ err = dpni_set_irq_mask(ls_dev->mc_io, 0, ls_dev->mc_handle, -+ irq_index, mask); -+ if (err < 0) { -+ dev_err(&ls_dev->dev, "dpni_set_irq_mask(): %d", err); -+ goto free_irq; -+ } -+ -+ err = dpni_set_irq_enable(ls_dev->mc_io, 0, ls_dev->mc_handle, -+ irq_index, 1); -+ if (err < 0) { -+ dev_err(&ls_dev->dev, "dpni_set_irq_enable(): %d", err); -+ goto free_irq; -+ } -+ -+ return 0; -+ -+free_irq: -+ devm_free_irq(&ls_dev->dev, irq->irq_number, &ls_dev->dev); -+free_mc_irq: -+ fsl_mc_free_irqs(ls_dev); -+ -+ return err; -+} -+ -+static void add_ch_napi(struct dpaa2_eth_priv *priv) -+{ -+ int i; -+ struct dpaa2_eth_channel *ch; -+ -+ for (i = 0; i < priv->num_channels; i++) { -+ ch = priv->channel[i]; -+ /* NAPI weight *MUST* be a multiple of DPAA2_ETH_STORE_SIZE */ -+ netif_napi_add(priv->net_dev, &ch->napi, dpaa2_eth_poll, -+ NAPI_POLL_WEIGHT); -+ } -+} -+ -+static void del_ch_napi(struct dpaa2_eth_priv *priv) -+{ -+ int i; -+ struct dpaa2_eth_channel *ch; -+ -+ for (i = 0; i < priv->num_channels; i++) { -+ ch = priv->channel[i]; -+ netif_napi_del(&ch->napi); -+ } -+} -+ -+/* SysFS support */ -+static ssize_t dpaa2_eth_show_tx_shaping(struct device *dev, -+ struct device_attribute *attr, -+ char *buf) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(to_net_dev(dev)); -+ /* No MC API for getting the shaping config. We're stateful. */ -+ struct dpni_tx_shaping_cfg *scfg = &priv->shaping_cfg; -+ -+ return sprintf(buf, "%u %hu\n", scfg->rate_limit, scfg->max_burst_size); -+} -+ -+static ssize_t dpaa2_eth_write_tx_shaping(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, -+ size_t count) -+{ -+ int err, items; -+ struct dpaa2_eth_priv *priv = netdev_priv(to_net_dev(dev)); -+ struct dpni_tx_shaping_cfg scfg; -+ -+ items = sscanf(buf, "%u %hu", &scfg.rate_limit, &scfg.max_burst_size); -+ if (items != 2) { -+ pr_err("Expected format: \"rate_limit(Mbps) max_burst_size(bytes)\"\n"); -+ return -EINVAL; -+ } -+ /* Size restriction as per MC API documentation */ -+ if (scfg.max_burst_size > 64000) { -+ pr_err("max_burst_size must be <= 64000, thanks.\n"); -+ return -EINVAL; -+ } -+ -+ err = dpni_set_tx_shaping(priv->mc_io, 0, priv->mc_token, &scfg); -+ if (err) { -+ dev_err(dev, "dpni_set_tx_shaping() failed\n"); -+ return -EPERM; -+ } -+ /* If successful, save the current configuration for future inquiries */ -+ priv->shaping_cfg = scfg; -+ -+ return count; -+} -+ -+static ssize_t dpaa2_eth_show_txconf_cpumask(struct device *dev, -+ struct device_attribute *attr, -+ char *buf) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(to_net_dev(dev)); -+ -+ return cpumap_print_to_pagebuf(1, buf, &priv->txconf_cpumask); -+} -+ -+static ssize_t dpaa2_eth_write_txconf_cpumask(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, -+ size_t count) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(to_net_dev(dev)); -+ struct dpaa2_eth_fq *fq; -+ bool running = netif_running(priv->net_dev); -+ int i, err; -+ -+ err = cpulist_parse(buf, &priv->txconf_cpumask); -+ if (err) -+ return err; -+ -+ /* Only accept CPUs that have an affine DPIO */ -+ if (!cpumask_subset(&priv->txconf_cpumask, &priv->dpio_cpumask)) { -+ netdev_info(priv->net_dev, -+ "cpumask must be a subset of 0x%lx\n", -+ *cpumask_bits(&priv->dpio_cpumask)); -+ cpumask_and(&priv->txconf_cpumask, &priv->dpio_cpumask, -+ &priv->txconf_cpumask); -+ } -+ -+ /* Rewiring the TxConf FQs requires interface shutdown. -+ */ -+ if (running) { -+ err = dpaa2_eth_stop(priv->net_dev); -+ if (err) -+ return -ENODEV; -+ } -+ -+ /* Set the new TxConf FQ affinities */ -+ set_fq_affinity(priv); -+ -+ /* dpaa2_eth_open() below will *stop* the Tx queues until an explicit -+ * link up notification is received. Give the polling thread enough time -+ * to detect the link state change, or else we'll end up with the -+ * transmission side forever shut down. -+ */ -+ if (priv->do_link_poll) -+ msleep(2 * DPAA2_ETH_LINK_STATE_REFRESH); -+ -+ for (i = 0; i < priv->num_fqs; i++) { -+ fq = &priv->fq[i]; -+ if (fq->type != DPAA2_TX_CONF_FQ) -+ continue; -+ setup_tx_flow(priv, fq); -+ } -+ -+ if (running) { -+ err = dpaa2_eth_open(priv->net_dev); -+ if (err) -+ return -ENODEV; -+ } -+ -+ return count; -+} -+ -+static struct device_attribute dpaa2_eth_attrs[] = { -+ __ATTR(txconf_cpumask, -+ S_IRUSR | S_IWUSR, -+ dpaa2_eth_show_txconf_cpumask, -+ dpaa2_eth_write_txconf_cpumask), -+ -+ __ATTR(tx_shaping, -+ S_IRUSR | S_IWUSR, -+ dpaa2_eth_show_tx_shaping, -+ dpaa2_eth_write_tx_shaping), -+}; -+ -+void dpaa2_eth_sysfs_init(struct device *dev) -+{ -+ int i, err; -+ -+ for (i = 0; i < ARRAY_SIZE(dpaa2_eth_attrs); i++) { -+ err = device_create_file(dev, &dpaa2_eth_attrs[i]); -+ if (err) { -+ dev_err(dev, "ERROR creating sysfs file\n"); -+ goto undo; -+ } -+ } -+ return; -+ -+undo: -+ while (i > 0) -+ device_remove_file(dev, &dpaa2_eth_attrs[--i]); -+} -+ -+void dpaa2_eth_sysfs_remove(struct device *dev) -+{ -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(dpaa2_eth_attrs); i++) -+ device_remove_file(dev, &dpaa2_eth_attrs[i]); -+} -+ -+static int dpaa2_eth_probe(struct fsl_mc_device *dpni_dev) -+{ -+ struct device *dev; -+ struct net_device *net_dev = NULL; -+ struct dpaa2_eth_priv *priv = NULL; -+ int err = 0; -+ -+ dev = &dpni_dev->dev; -+ -+ /* Net device */ -+ net_dev = alloc_etherdev_mq(sizeof(*priv), DPAA2_ETH_MAX_TX_QUEUES); -+ if (!net_dev) { -+ dev_err(dev, "alloc_etherdev_mq() failed\n"); -+ return -ENOMEM; -+ } -+ -+ SET_NETDEV_DEV(net_dev, dev); -+ dev_set_drvdata(dev, net_dev); -+ -+ priv = netdev_priv(net_dev); -+ priv->net_dev = net_dev; -+ -+ /* Obtain a MC portal */ -+ err = fsl_mc_portal_allocate(dpni_dev, FSL_MC_IO_ATOMIC_CONTEXT_PORTAL, -+ &priv->mc_io); -+ if (err) { -+ dev_err(dev, "MC portal allocation failed\n"); -+ goto err_portal_alloc; -+ } -+ -+ /* MC objects initialization and configuration */ -+ err = setup_dpni(dpni_dev); -+ if (err) -+ goto err_dpni_setup; -+ -+ err = setup_dpio(priv); -+ if (err) -+ goto err_dpio_setup; -+ -+ setup_fqs(priv); -+ -+ err = setup_dpbp(priv); -+ if (err) -+ goto err_dpbp_setup; -+ -+ err = bind_dpni(priv); -+ if (err) -+ goto err_bind; -+ -+ /* Add a NAPI context for each channel */ -+ add_ch_napi(priv); -+ -+ /* Percpu statistics */ -+ priv->percpu_stats = alloc_percpu(*priv->percpu_stats); -+ if (!priv->percpu_stats) { -+ dev_err(dev, "alloc_percpu(percpu_stats) failed\n"); -+ err = -ENOMEM; -+ goto err_alloc_percpu_stats; -+ } -+ priv->percpu_extras = alloc_percpu(*priv->percpu_extras); -+ if (!priv->percpu_extras) { -+ dev_err(dev, "alloc_percpu(percpu_extras) failed\n"); -+ err = -ENOMEM; -+ goto err_alloc_percpu_extras; -+ } -+ -+ snprintf(net_dev->name, IFNAMSIZ, "ni%d", dpni_dev->obj_desc.id); -+ if (!dev_valid_name(net_dev->name)) { -+ dev_warn(&net_dev->dev, -+ "netdevice name \"%s\" cannot be used, reverting to default..\n", -+ net_dev->name); -+ dev_alloc_name(net_dev, "eth%d"); -+ dev_warn(&net_dev->dev, "using name \"%s\"\n", net_dev->name); -+ } -+ -+ err = netdev_init(net_dev); -+ if (err) -+ goto err_netdev_init; -+ -+ /* Configure checksum offload based on current interface flags */ -+ err = set_rx_csum(priv, !!(net_dev->features & NETIF_F_RXCSUM)); -+ if (err) -+ goto err_csum; -+ -+ err = set_tx_csum(priv, !!(net_dev->features & -+ (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM))); -+ if (err) -+ goto err_csum; -+ -+ err = alloc_rings(priv); -+ if (err) -+ goto err_alloc_rings; -+ -+ net_dev->ethtool_ops = &dpaa2_ethtool_ops; -+ -+ err = setup_irqs(dpni_dev); -+ if (err) { -+ netdev_warn(net_dev, "Failed to set link interrupt, fall back to polling\n"); -+ priv->poll_thread = kthread_run(poll_link_state, priv, -+ "%s_poll_link", net_dev->name); -+ if (IS_ERR(priv->poll_thread)) { -+ netdev_err(net_dev, "Error starting polling thread\n"); -+ goto err_poll_thread; -+ } -+ priv->do_link_poll = true; -+ } -+ -+ dpaa2_eth_sysfs_init(&net_dev->dev); -+ dpaa2_dbg_add(priv); -+ -+ dev_info(dev, "Probed interface %s\n", net_dev->name); -+ return 0; -+ -+err_poll_thread: -+ free_rings(priv); -+err_alloc_rings: -+err_csum: -+ unregister_netdev(net_dev); -+err_netdev_init: -+ free_percpu(priv->percpu_extras); -+err_alloc_percpu_extras: -+ free_percpu(priv->percpu_stats); -+err_alloc_percpu_stats: -+ del_ch_napi(priv); -+err_bind: -+ free_dpbp(priv); -+err_dpbp_setup: -+ free_dpio(priv); -+err_dpio_setup: -+ kfree(priv->cls_rule); -+ dpni_close(priv->mc_io, 0, priv->mc_token); -+err_dpni_setup: -+ fsl_mc_portal_free(priv->mc_io); -+err_portal_alloc: -+ dev_set_drvdata(dev, NULL); -+ free_netdev(net_dev); -+ -+ return err; -+} -+ -+static int dpaa2_eth_remove(struct fsl_mc_device *ls_dev) -+{ -+ struct device *dev; -+ struct net_device *net_dev; -+ struct dpaa2_eth_priv *priv; -+ -+ dev = &ls_dev->dev; -+ net_dev = dev_get_drvdata(dev); -+ priv = netdev_priv(net_dev); -+ -+ dpaa2_dbg_remove(priv); -+ dpaa2_eth_sysfs_remove(&net_dev->dev); -+ -+ unregister_netdev(net_dev); -+ dev_info(net_dev->dev.parent, "Removed interface %s\n", net_dev->name); -+ -+ free_dpio(priv); -+ free_rings(priv); -+ del_ch_napi(priv); -+ free_dpbp(priv); -+ free_dpni(priv); -+ -+ fsl_mc_portal_free(priv->mc_io); -+ -+ free_percpu(priv->percpu_stats); -+ free_percpu(priv->percpu_extras); -+ -+ if (priv->do_link_poll) -+ kthread_stop(priv->poll_thread); -+ else -+ fsl_mc_free_irqs(ls_dev); -+ -+ kfree(priv->cls_rule); -+ -+ dev_set_drvdata(dev, NULL); -+ free_netdev(net_dev); -+ -+ return 0; -+} -+ -+static const struct fsl_mc_device_match_id dpaa2_eth_match_id_table[] = { -+ { -+ .vendor = FSL_MC_VENDOR_FREESCALE, -+ .obj_type = "dpni", -+ .ver_major = DPNI_VER_MAJOR, -+ .ver_minor = DPNI_VER_MINOR -+ }, -+ { .vendor = 0x0 } -+}; -+ -+static struct fsl_mc_driver dpaa2_eth_driver = { -+ .driver = { -+ .name = KBUILD_MODNAME, -+ .owner = THIS_MODULE, -+ }, -+ .probe = dpaa2_eth_probe, -+ .remove = dpaa2_eth_remove, -+ .match_id_table = dpaa2_eth_match_id_table -+}; -+ -+static int __init dpaa2_eth_driver_init(void) -+{ -+ int err; -+ -+ dpaa2_eth_dbg_init(); -+ -+ err = fsl_mc_driver_register(&dpaa2_eth_driver); -+ if (err) { -+ dpaa2_eth_dbg_exit(); -+ return err; -+ } -+ -+ return 0; -+} -+ -+static void __exit dpaa2_eth_driver_exit(void) -+{ -+ fsl_mc_driver_unregister(&dpaa2_eth_driver); -+ dpaa2_eth_dbg_exit(); -+} -+ -+module_init(dpaa2_eth_driver_init); -+module_exit(dpaa2_eth_driver_exit); -diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.h b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.h -new file mode 100644 -index 0000000..7274fbe ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.h -@@ -0,0 +1,377 @@ -+/* Copyright 2014-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 __DPAA2_ETH_H -+#define __DPAA2_ETH_H -+ -+#include -+#include -+#include "../../fsl-mc/include/fsl_dpaa2_io.h" -+#include "../../fsl-mc/include/fsl_dpaa2_fd.h" -+#include "../../fsl-mc/include/dpbp.h" -+#include "../../fsl-mc/include/dpbp-cmd.h" -+#include "../../fsl-mc/include/dpcon.h" -+#include "../../fsl-mc/include/dpcon-cmd.h" -+#include "../../fsl-mc/include/dpmng.h" -+#include "dpni.h" -+#include "dpni-cmd.h" -+ -+#include "dpaa2-eth-trace.h" -+#include "dpaa2-eth-debugfs.h" -+ -+#define DPAA2_ETH_STORE_SIZE 16 -+ -+/* Maximum number of scatter-gather entries in an ingress frame, -+ * considering the maximum receive frame size is 64K -+ */ -+#define DPAA2_ETH_MAX_SG_ENTRIES ((64 * 1024) / DPAA2_ETH_RX_BUF_SIZE) -+ -+/* Maximum acceptable MTU value. It is in direct relation with the MC-enforced -+ * Max Frame Length (currently 10k). -+ */ -+#define DPAA2_ETH_MFL (10 * 1024) -+#define DPAA2_ETH_MAX_MTU (DPAA2_ETH_MFL - VLAN_ETH_HLEN) -+/* Convert L3 MTU to L2 MFL */ -+#define DPAA2_ETH_L2_MAX_FRM(mtu) (mtu + VLAN_ETH_HLEN) -+ -+/* Set the taildrop threshold (in bytes) to allow the enqueue of several jumbo -+ * frames in the Rx queues (length of the current frame is not -+ * taken into account when making the taildrop decision) -+ */ -+#define DPAA2_ETH_TAILDROP_THRESH (64 * 1024) -+ -+/* Buffer quota per queue. Must be large enough such that for minimum sized -+ * frames taildrop kicks in before the bpool gets depleted, so we compute -+ * how many 64B frames fit inside the taildrop threshold and add a margin -+ * to accommodate the buffer refill delay. -+ */ -+#define DPAA2_ETH_MAX_FRAMES_PER_QUEUE (DPAA2_ETH_TAILDROP_THRESH / 64) -+#define DPAA2_ETH_NUM_BUFS (DPAA2_ETH_MAX_FRAMES_PER_QUEUE + 256) -+#define DPAA2_ETH_REFILL_THRESH DPAA2_ETH_MAX_FRAMES_PER_QUEUE -+ -+/* Maximum number of buffers that can be acquired/released through a single -+ * QBMan command -+ */ -+#define DPAA2_ETH_BUFS_PER_CMD 7 -+ -+/* Hardware requires alignment for ingress/egress buffer addresses -+ * and ingress buffer lengths. -+ */ -+#define DPAA2_ETH_RX_BUF_SIZE 2048 -+#define DPAA2_ETH_TX_BUF_ALIGN 64 -+#define DPAA2_ETH_RX_BUF_ALIGN 256 -+#define DPAA2_ETH_NEEDED_HEADROOM(p_priv) \ -+ ((p_priv)->tx_data_offset + DPAA2_ETH_TX_BUF_ALIGN) -+ -+/* Hardware only sees DPAA2_ETH_RX_BUF_SIZE, but we need to allocate ingress -+ * buffers large enough to allow building an skb around them and also account -+ * for alignment restrictions -+ */ -+#define DPAA2_ETH_BUF_RAW_SIZE \ -+ (DPAA2_ETH_RX_BUF_SIZE + \ -+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + \ -+ DPAA2_ETH_RX_BUF_ALIGN) -+ -+/* PTP nominal frequency 1MHz */ -+#define DPAA2_PTP_NOMINAL_FREQ_PERIOD_NS 1000 -+ -+/* We are accommodating a skb backpointer and some S/G info -+ * in the frame's software annotation. The hardware -+ * options are either 0 or 64, so we choose the latter. -+ */ -+#define DPAA2_ETH_SWA_SIZE 64 -+ -+/* Must keep this struct smaller than DPAA2_ETH_SWA_SIZE */ -+struct dpaa2_eth_swa { -+ struct sk_buff *skb; -+ struct scatterlist *scl; -+ int num_sg; -+ int num_dma_bufs; -+}; -+ -+/* Annotation valid bits in FD FRC */ -+#define DPAA2_FD_FRC_FASV 0x8000 -+#define DPAA2_FD_FRC_FAEADV 0x4000 -+#define DPAA2_FD_FRC_FAPRV 0x2000 -+#define DPAA2_FD_FRC_FAIADV 0x1000 -+#define DPAA2_FD_FRC_FASWOV 0x0800 -+#define DPAA2_FD_FRC_FAICFDV 0x0400 -+ -+/* Annotation bits in FD CTRL */ -+#define DPAA2_FD_CTRL_ASAL 0x00020000 /* ASAL = 128 */ -+#define DPAA2_FD_CTRL_PTA 0x00800000 -+#define DPAA2_FD_CTRL_PTV1 0x00400000 -+ -+/* Frame annotation status */ -+struct dpaa2_fas { -+ u8 reserved; -+ u8 ppid; -+ __le16 ifpid; -+ __le32 status; -+} __packed; -+ -+/* Error and status bits in the frame annotation status word */ -+/* Debug frame, otherwise supposed to be discarded */ -+#define DPAA2_FAS_DISC 0x80000000 -+/* MACSEC frame */ -+#define DPAA2_FAS_MS 0x40000000 -+#define DPAA2_FAS_PTP 0x08000000 -+/* Ethernet multicast frame */ -+#define DPAA2_FAS_MC 0x04000000 -+/* Ethernet broadcast frame */ -+#define DPAA2_FAS_BC 0x02000000 -+#define DPAA2_FAS_KSE 0x00040000 -+#define DPAA2_FAS_EOFHE 0x00020000 -+#define DPAA2_FAS_MNLE 0x00010000 -+#define DPAA2_FAS_TIDE 0x00008000 -+#define DPAA2_FAS_PIEE 0x00004000 -+/* Frame length error */ -+#define DPAA2_FAS_FLE 0x00002000 -+/* Frame physical error */ -+#define DPAA2_FAS_FPE 0x00001000 -+#define DPAA2_FAS_PTE 0x00000080 -+#define DPAA2_FAS_ISP 0x00000040 -+#define DPAA2_FAS_PHE 0x00000020 -+#define DPAA2_FAS_BLE 0x00000010 -+/* L3 csum validation performed */ -+#define DPAA2_FAS_L3CV 0x00000008 -+/* L3 csum error */ -+#define DPAA2_FAS_L3CE 0x00000004 -+/* L4 csum validation performed */ -+#define DPAA2_FAS_L4CV 0x00000002 -+/* L4 csum error */ -+#define DPAA2_FAS_L4CE 0x00000001 -+/* Possible errors on the ingress path */ -+#define DPAA2_ETH_RX_ERR_MASK (DPAA2_FAS_KSE | \ -+ DPAA2_FAS_EOFHE | \ -+ DPAA2_FAS_MNLE | \ -+ DPAA2_FAS_TIDE | \ -+ DPAA2_FAS_PIEE | \ -+ DPAA2_FAS_FLE | \ -+ DPAA2_FAS_FPE | \ -+ DPAA2_FAS_PTE | \ -+ DPAA2_FAS_ISP | \ -+ DPAA2_FAS_PHE | \ -+ DPAA2_FAS_BLE | \ -+ DPAA2_FAS_L3CE | \ -+ DPAA2_FAS_L4CE) -+/* Tx errors */ -+#define DPAA2_ETH_TXCONF_ERR_MASK (DPAA2_FAS_KSE | \ -+ DPAA2_FAS_EOFHE | \ -+ DPAA2_FAS_MNLE | \ -+ DPAA2_FAS_TIDE) -+ -+/* Time in milliseconds between link state updates */ -+#define DPAA2_ETH_LINK_STATE_REFRESH 1000 -+ -+/* Driver statistics, other than those in struct rtnl_link_stats64. -+ * These are usually collected per-CPU and aggregated by ethtool. -+ */ -+struct dpaa2_eth_drv_stats { -+ __u64 tx_conf_frames; -+ __u64 tx_conf_bytes; -+ __u64 tx_sg_frames; -+ __u64 tx_sg_bytes; -+ __u64 rx_sg_frames; -+ __u64 rx_sg_bytes; -+ /* Enqueues retried due to portal busy */ -+ __u64 tx_portal_busy; -+}; -+ -+/* Per-FQ statistics */ -+struct dpaa2_eth_fq_stats { -+ /* Number of frames received on this queue */ -+ __u64 frames; -+}; -+ -+/* Per-channel statistics */ -+struct dpaa2_eth_ch_stats { -+ /* Volatile dequeues retried due to portal busy */ -+ __u64 dequeue_portal_busy; -+ /* Number of CDANs; useful to estimate avg NAPI len */ -+ __u64 cdan; -+ /* Number of frames received on queues from this channel */ -+ __u64 frames; -+ /* Pull errors */ -+ __u64 pull_err; -+}; -+ -+/* Maximum number of queues associated with a DPNI */ -+#define DPAA2_ETH_MAX_RX_QUEUES 16 -+#define DPAA2_ETH_MAX_TX_QUEUES NR_CPUS -+#define DPAA2_ETH_MAX_RX_ERR_QUEUES 1 -+#define DPAA2_ETH_MAX_QUEUES (DPAA2_ETH_MAX_RX_QUEUES + \ -+ DPAA2_ETH_MAX_TX_QUEUES + \ -+ DPAA2_ETH_MAX_RX_ERR_QUEUES) -+ -+#define DPAA2_ETH_MAX_DPCONS NR_CPUS -+ -+enum dpaa2_eth_fq_type { -+ DPAA2_RX_FQ = 0, -+ DPAA2_TX_CONF_FQ, -+ DPAA2_RX_ERR_FQ -+}; -+ -+struct dpaa2_eth_priv; -+ -+struct dpaa2_eth_fq { -+ u32 fqid; -+ u16 flowid; -+ int target_cpu; -+ struct dpaa2_eth_channel *channel; -+ enum dpaa2_eth_fq_type type; -+ -+ void (*consume)(struct dpaa2_eth_priv *, -+ struct dpaa2_eth_channel *, -+ const struct dpaa2_fd *, -+ struct napi_struct *); -+ struct dpaa2_eth_fq_stats stats; -+}; -+ -+struct dpaa2_eth_channel { -+ struct dpaa2_io_notification_ctx nctx; -+ struct fsl_mc_device *dpcon; -+ int dpcon_id; -+ int ch_id; -+ int dpio_id; -+ struct napi_struct napi; -+ struct dpaa2_io_store *store; -+ struct dpaa2_eth_priv *priv; -+ int buf_count; -+ struct dpaa2_eth_ch_stats stats; -+}; -+ -+struct dpaa2_eth_cls_rule { -+ struct ethtool_rx_flow_spec fs; -+ bool in_use; -+}; -+ -+/* Driver private data */ -+struct dpaa2_eth_priv { -+ struct net_device *net_dev; -+ -+ u8 num_fqs; -+ struct dpaa2_eth_fq fq[DPAA2_ETH_MAX_QUEUES]; -+ -+ u8 num_channels; -+ struct dpaa2_eth_channel *channel[DPAA2_ETH_MAX_DPCONS]; -+ -+ int dpni_id; -+ struct dpni_attr dpni_attrs; -+ struct dpni_extended_cfg dpni_ext_cfg; -+ /* Insofar as the MC is concerned, we're using one layout on all 3 types -+ * of buffers (Rx, Tx, Tx-Conf). -+ */ -+ struct dpni_buffer_layout buf_layout; -+ u16 tx_data_offset; -+ -+ struct fsl_mc_device *dpbp_dev; -+ struct dpbp_attr dpbp_attrs; -+ -+ u16 tx_qdid; -+ struct fsl_mc_io *mc_io; -+ /* SysFS-controlled affinity mask for TxConf FQs */ -+ struct cpumask txconf_cpumask; -+ /* Cores which have an affine DPIO/DPCON. -+ * This is the cpu set on which Rx frames are processed; -+ * Tx confirmation frames are processed on a subset of this, -+ * depending on user settings. -+ */ -+ struct cpumask dpio_cpumask; -+ -+ /* Standard statistics */ -+ struct rtnl_link_stats64 __percpu *percpu_stats; -+ /* Extra stats, in addition to the ones known by the kernel */ -+ struct dpaa2_eth_drv_stats __percpu *percpu_extras; -+ -+ u16 mc_token; -+ -+ struct dpni_link_state link_state; -+ bool do_link_poll; -+ struct task_struct *poll_thread; -+ -+ /* enabled ethtool hashing bits */ -+ u64 rx_hash_fields; -+ -+#ifdef CONFIG_FSL_DPAA2_ETH_DEBUGFS -+ struct dpaa2_debugfs dbg; -+#endif -+ -+ /* array of classification rules */ -+ struct dpaa2_eth_cls_rule *cls_rule; -+ -+ struct dpni_tx_shaping_cfg shaping_cfg; -+ -+ bool ts_tx_en; /* Tx timestamping enabled */ -+ bool ts_rx_en; /* Rx timestamping enabled */ -+}; -+ -+/* default Rx hash options, set during probing */ -+#define DPAA2_RXH_SUPPORTED (RXH_L2DA | RXH_VLAN | RXH_L3_PROTO \ -+ | RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 \ -+ | RXH_L4_B_2_3) -+ -+#define dpaa2_eth_hash_enabled(priv) \ -+ ((priv)->dpni_attrs.options & DPNI_OPT_DIST_HASH) -+ -+#define dpaa2_eth_fs_enabled(priv) \ -+ ((priv)->dpni_attrs.options & DPNI_OPT_DIST_FS) -+ -+#define DPAA2_CLASSIFIER_ENTRY_COUNT 16 -+ -+/* Required by struct dpni_attr::ext_cfg_iova */ -+#define DPAA2_EXT_CFG_SIZE 256 -+ -+extern const struct ethtool_ops dpaa2_ethtool_ops; -+ -+int dpaa2_eth_set_hash(struct net_device *net_dev, u64 flags); -+ -+static int dpaa2_eth_queue_count(struct dpaa2_eth_priv *priv) -+{ -+ if (!dpaa2_eth_hash_enabled(priv)) -+ return 1; -+ -+ return priv->dpni_ext_cfg.tc_cfg[0].max_dist; -+} -+ -+static inline int dpaa2_eth_max_channels(struct dpaa2_eth_priv *priv) -+{ -+ /* Ideally, we want a number of channels large enough -+ * to accommodate both the Rx distribution size -+ * and the max number of Tx confirmation queues -+ */ -+ return max_t(int, dpaa2_eth_queue_count(priv), -+ priv->dpni_attrs.max_senders); -+} -+ -+void check_fs_support(struct net_device *); -+ -+#endif /* __DPAA2_H */ -diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-ethtool.c b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-ethtool.c -new file mode 100644 -index 0000000..fdab07f ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-ethtool.c -@@ -0,0 +1,861 @@ -+/* Copyright 2014-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 "dpni.h" /* DPNI_LINK_OPT_* */ -+#include "dpaa2-eth.h" -+ -+/* size of DMA memory used to pass configuration to classifier, in bytes */ -+#define DPAA2_CLASSIFIER_DMA_SIZE 256 -+ -+/* To be kept in sync with 'enum dpni_counter' */ -+char dpaa2_ethtool_stats[][ETH_GSTRING_LEN] = { -+ "rx frames", -+ "rx bytes", -+ /* rx frames filtered/policed */ -+ "rx filtered frames", -+ /* rx frames dropped with errors */ -+ "rx discarded frames", -+ "rx mcast frames", -+ "rx mcast bytes", -+ "rx bcast frames", -+ "rx bcast bytes", -+ "tx frames", -+ "tx bytes", -+ /* tx frames dropped with errors */ -+ "tx discarded frames", -+}; -+ -+#define DPAA2_ETH_NUM_STATS ARRAY_SIZE(dpaa2_ethtool_stats) -+ -+/* To be kept in sync with 'struct dpaa2_eth_drv_stats' */ -+char dpaa2_ethtool_extras[][ETH_GSTRING_LEN] = { -+ /* per-cpu stats */ -+ -+ "tx conf frames", -+ "tx conf bytes", -+ "tx sg frames", -+ "tx sg bytes", -+ "rx sg frames", -+ "rx sg bytes", -+ /* how many times we had to retry the enqueue command */ -+ "enqueue portal busy", -+ -+ /* Channel stats */ -+ /* How many times we had to retry the volatile dequeue command */ -+ "dequeue portal busy", -+ "channel pull errors", -+ /* Number of notifications received */ -+ "cdan", -+#ifdef CONFIG_FSL_QBMAN_DEBUG -+ /* FQ stats */ -+ "rx pending frames", -+ "rx pending bytes", -+ "tx conf pending frames", -+ "tx conf pending bytes", -+ "buffer count" -+#endif -+}; -+ -+#define DPAA2_ETH_NUM_EXTRA_STATS ARRAY_SIZE(dpaa2_ethtool_extras) -+ -+static void dpaa2_eth_get_drvinfo(struct net_device *net_dev, -+ struct ethtool_drvinfo *drvinfo) -+{ -+ struct mc_version mc_ver; -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ char fw_version[ETHTOOL_FWVERS_LEN]; -+ char version[32]; -+ int err; -+ -+ err = mc_get_version(priv->mc_io, 0, &mc_ver); -+ if (err) { -+ strlcpy(drvinfo->fw_version, "Error retrieving MC version", -+ sizeof(drvinfo->fw_version)); -+ } else { -+ scnprintf(fw_version, sizeof(fw_version), "%d.%d.%d", -+ mc_ver.major, mc_ver.minor, mc_ver.revision); -+ strlcpy(drvinfo->fw_version, fw_version, -+ sizeof(drvinfo->fw_version)); -+ } -+ -+ scnprintf(version, sizeof(version), "%d.%d", DPNI_VER_MAJOR, -+ DPNI_VER_MINOR); -+ strlcpy(drvinfo->version, version, sizeof(drvinfo->version)); -+ -+ strlcpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver)); -+ strlcpy(drvinfo->bus_info, dev_name(net_dev->dev.parent->parent), -+ sizeof(drvinfo->bus_info)); -+} -+ -+static int dpaa2_eth_get_settings(struct net_device *net_dev, -+ struct ethtool_cmd *cmd) -+{ -+ struct dpni_link_state state = {0}; -+ int err = 0; -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ -+ err = dpni_get_link_state(priv->mc_io, 0, priv->mc_token, &state); -+ if (err) { -+ netdev_err(net_dev, "ERROR %d getting link state", err); -+ goto out; -+ } -+ -+ /* At the moment, we have no way of interrogating the DPMAC -+ * from the DPNI side - and for that matter there may exist -+ * no DPMAC at all. So for now we just don't report anything -+ * beyond the DPNI attributes. -+ */ -+ if (state.options & DPNI_LINK_OPT_AUTONEG) -+ cmd->autoneg = AUTONEG_ENABLE; -+ if (!(state.options & DPNI_LINK_OPT_HALF_DUPLEX)) -+ cmd->duplex = DUPLEX_FULL; -+ ethtool_cmd_speed_set(cmd, state.rate); -+ -+out: -+ return err; -+} -+ -+static int dpaa2_eth_set_settings(struct net_device *net_dev, -+ struct ethtool_cmd *cmd) -+{ -+ struct dpni_link_cfg cfg = {0}; -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ int err = 0; -+ -+ netdev_dbg(net_dev, "Setting link parameters..."); -+ -+ /* Due to a temporary firmware limitation, the DPNI must be down -+ * in order to be able to change link settings. Taking steps to let -+ * the user know that. -+ */ -+ if (netif_running(net_dev)) { -+ netdev_info(net_dev, "Sorry, interface must be brought down first.\n"); -+ return -EACCES; -+ } -+ -+ cfg.rate = ethtool_cmd_speed(cmd); -+ if (cmd->autoneg == AUTONEG_ENABLE) -+ cfg.options |= DPNI_LINK_OPT_AUTONEG; -+ else -+ cfg.options &= ~DPNI_LINK_OPT_AUTONEG; -+ if (cmd->duplex == DUPLEX_HALF) -+ cfg.options |= DPNI_LINK_OPT_HALF_DUPLEX; -+ else -+ cfg.options &= ~DPNI_LINK_OPT_HALF_DUPLEX; -+ -+ err = dpni_set_link_cfg(priv->mc_io, 0, priv->mc_token, &cfg); -+ if (err) -+ /* ethtool will be loud enough if we return an error; no point -+ * in putting our own error message on the console by default -+ */ -+ netdev_dbg(net_dev, "ERROR %d setting link cfg", err); -+ -+ return err; -+} -+ -+static void dpaa2_eth_get_strings(struct net_device *netdev, u32 stringset, -+ u8 *data) -+{ -+ u8 *p = data; -+ int i; -+ -+ switch (stringset) { -+ case ETH_SS_STATS: -+ for (i = 0; i < DPAA2_ETH_NUM_STATS; i++) { -+ strlcpy(p, dpaa2_ethtool_stats[i], ETH_GSTRING_LEN); -+ p += ETH_GSTRING_LEN; -+ } -+ for (i = 0; i < DPAA2_ETH_NUM_EXTRA_STATS; i++) { -+ strlcpy(p, dpaa2_ethtool_extras[i], ETH_GSTRING_LEN); -+ p += ETH_GSTRING_LEN; -+ } -+ break; -+ } -+} -+ -+static int dpaa2_eth_get_sset_count(struct net_device *net_dev, int sset) -+{ -+ switch (sset) { -+ case ETH_SS_STATS: /* ethtool_get_stats(), ethtool_get_drvinfo() */ -+ return DPAA2_ETH_NUM_STATS + DPAA2_ETH_NUM_EXTRA_STATS; -+ default: -+ return -EOPNOTSUPP; -+ } -+} -+ -+/** Fill in hardware counters, as returned by the MC firmware. -+ */ -+static void dpaa2_eth_get_ethtool_stats(struct net_device *net_dev, -+ struct ethtool_stats *stats, -+ u64 *data) -+{ -+ int i; /* Current index in the data array */ -+ int j, k, err; -+ -+#ifdef CONFIG_FSL_QBMAN_DEBUG -+ u32 fcnt, bcnt; -+ u32 fcnt_rx_total = 0, fcnt_tx_total = 0; -+ u32 bcnt_rx_total = 0, bcnt_tx_total = 0; -+ u32 buf_cnt; -+#endif -+ u64 cdan = 0; -+ u64 portal_busy = 0, pull_err = 0; -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ struct dpaa2_eth_drv_stats *extras; -+ struct dpaa2_eth_ch_stats *ch_stats; -+ -+ memset(data, 0, -+ sizeof(u64) * (DPAA2_ETH_NUM_STATS + DPAA2_ETH_NUM_EXTRA_STATS)); -+ -+ /* Print standard counters, from DPNI statistics */ -+ for (i = 0; i < DPAA2_ETH_NUM_STATS; i++) { -+ err = dpni_get_counter(priv->mc_io, 0, priv->mc_token, i, -+ data + i); -+ if (err != 0) -+ netdev_warn(net_dev, "Err %d getting DPNI counter %d", -+ err, i); -+ } -+ -+ /* Print per-cpu extra stats */ -+ for_each_online_cpu(k) { -+ extras = per_cpu_ptr(priv->percpu_extras, k); -+ for (j = 0; j < sizeof(*extras) / sizeof(__u64); j++) -+ *((__u64 *)data + i + j) += *((__u64 *)extras + j); -+ } -+ i += j; -+ -+ /* We may be using fewer DPIOs than actual CPUs */ -+ for_each_cpu(j, &priv->dpio_cpumask) { -+ ch_stats = &priv->channel[j]->stats; -+ cdan += ch_stats->cdan; -+ portal_busy += ch_stats->dequeue_portal_busy; -+ pull_err += ch_stats->pull_err; -+ } -+ -+ *(data + i++) = portal_busy; -+ *(data + i++) = pull_err; -+ *(data + i++) = cdan; -+ -+#ifdef CONFIG_FSL_QBMAN_DEBUG -+ for (j = 0; j < priv->num_fqs; j++) { -+ /* Print FQ instantaneous counts */ -+ err = dpaa2_io_query_fq_count(NULL, priv->fq[j].fqid, -+ &fcnt, &bcnt); -+ if (err) { -+ netdev_warn(net_dev, "FQ query error %d", err); -+ return; -+ } -+ -+ if (priv->fq[j].type == DPAA2_TX_CONF_FQ) { -+ fcnt_tx_total += fcnt; -+ bcnt_tx_total += bcnt; -+ } else { -+ fcnt_rx_total += fcnt; -+ bcnt_rx_total += bcnt; -+ } -+ } -+ *(data + i++) = fcnt_rx_total; -+ *(data + i++) = bcnt_rx_total; -+ *(data + i++) = fcnt_tx_total; -+ *(data + i++) = bcnt_tx_total; -+ -+ err = dpaa2_io_query_bp_count(NULL, priv->dpbp_attrs.bpid, &buf_cnt); -+ if (err) { -+ netdev_warn(net_dev, "Buffer count query error %d\n", err); -+ return; -+ } -+ *(data + i++) = buf_cnt; -+#endif -+} -+ -+static const struct dpaa2_eth_hash_fields { -+ u64 rxnfc_field; -+ enum net_prot cls_prot; -+ int cls_field; -+ int size; -+} hash_fields[] = { -+ { -+ /* L2 header */ -+ .rxnfc_field = RXH_L2DA, -+ .cls_prot = NET_PROT_ETH, -+ .cls_field = NH_FLD_ETH_DA, -+ .size = 6, -+ }, { -+ /* VLAN header */ -+ .rxnfc_field = RXH_VLAN, -+ .cls_prot = NET_PROT_VLAN, -+ .cls_field = NH_FLD_VLAN_TCI, -+ .size = 2, -+ }, { -+ /* IP header */ -+ .rxnfc_field = RXH_IP_SRC, -+ .cls_prot = NET_PROT_IP, -+ .cls_field = NH_FLD_IP_SRC, -+ .size = 4, -+ }, { -+ .rxnfc_field = RXH_IP_DST, -+ .cls_prot = NET_PROT_IP, -+ .cls_field = NH_FLD_IP_DST, -+ .size = 4, -+ }, { -+ .rxnfc_field = RXH_L3_PROTO, -+ .cls_prot = NET_PROT_IP, -+ .cls_field = NH_FLD_IP_PROTO, -+ .size = 1, -+ }, { -+ /* Using UDP ports, this is functionally equivalent to raw -+ * byte pairs from L4 header. -+ */ -+ .rxnfc_field = RXH_L4_B_0_1, -+ .cls_prot = NET_PROT_UDP, -+ .cls_field = NH_FLD_UDP_PORT_SRC, -+ .size = 2, -+ }, { -+ .rxnfc_field = RXH_L4_B_2_3, -+ .cls_prot = NET_PROT_UDP, -+ .cls_field = NH_FLD_UDP_PORT_DST, -+ .size = 2, -+ }, -+}; -+ -+static int cls_is_enabled(struct net_device *net_dev, u64 flag) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ -+ return !!(priv->rx_hash_fields & flag); -+} -+ -+static int cls_key_off(struct net_device *net_dev, u64 flag) -+{ -+ int i, off = 0; -+ -+ for (i = 0; i < ARRAY_SIZE(hash_fields); i++) { -+ if (hash_fields[i].rxnfc_field & flag) -+ return off; -+ if (cls_is_enabled(net_dev, hash_fields[i].rxnfc_field)) -+ off += hash_fields[i].size; -+ } -+ -+ return -1; -+} -+ -+static u8 cls_key_size(struct net_device *net_dev) -+{ -+ u8 i, size = 0; -+ -+ for (i = 0; i < ARRAY_SIZE(hash_fields); i++) { -+ if (!cls_is_enabled(net_dev, hash_fields[i].rxnfc_field)) -+ continue; -+ size += hash_fields[i].size; -+ } -+ -+ return size; -+} -+ -+static u8 cls_max_key_size(struct net_device *net_dev) -+{ -+ u8 i, size = 0; -+ -+ for (i = 0; i < ARRAY_SIZE(hash_fields); i++) -+ size += hash_fields[i].size; -+ -+ return size; -+} -+ -+void check_fs_support(struct net_device *net_dev) -+{ -+ u8 key_size = cls_max_key_size(net_dev); -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ -+ if (priv->dpni_attrs.options & DPNI_OPT_DIST_FS && -+ priv->dpni_attrs.max_dist_key_size < key_size) { -+ dev_err(&net_dev->dev, -+ "max_dist_key_size = %d, expected %d. Steering is disabled\n", -+ priv->dpni_attrs.max_dist_key_size, -+ key_size); -+ priv->dpni_attrs.options &= ~DPNI_OPT_DIST_FS; -+ } -+} -+ -+/* Set RX hash options -+ * flags is a combination of RXH_ bits -+ */ -+int dpaa2_eth_set_hash(struct net_device *net_dev, u64 flags) -+{ -+ struct device *dev = net_dev->dev.parent; -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ struct dpkg_profile_cfg cls_cfg; -+ struct dpni_rx_tc_dist_cfg dist_cfg; -+ u8 *dma_mem; -+ u64 enabled_flags = 0; -+ int i; -+ int err = 0; -+ -+ if (!dpaa2_eth_hash_enabled(priv)) { -+ dev_err(dev, "Hashing support is not enabled\n"); -+ return -EOPNOTSUPP; -+ } -+ -+ if (flags & ~DPAA2_RXH_SUPPORTED) { -+ /* RXH_DISCARD is not supported */ -+ dev_err(dev, "unsupported option selected, supported options are: mvtsdfn\n"); -+ return -EOPNOTSUPP; -+ } -+ -+ memset(&cls_cfg, 0, sizeof(cls_cfg)); -+ -+ for (i = 0; i < ARRAY_SIZE(hash_fields); i++) { -+ struct dpkg_extract *key = -+ &cls_cfg.extracts[cls_cfg.num_extracts]; -+ -+ if (!(flags & hash_fields[i].rxnfc_field)) -+ continue; -+ -+ if (cls_cfg.num_extracts >= DPKG_MAX_NUM_OF_EXTRACTS) { -+ dev_err(dev, "error adding key extraction rule, too many rules?\n"); -+ return -E2BIG; -+ } -+ -+ key->type = DPKG_EXTRACT_FROM_HDR; -+ key->extract.from_hdr.prot = hash_fields[i].cls_prot; -+ key->extract.from_hdr.type = DPKG_FULL_FIELD; -+ key->extract.from_hdr.field = hash_fields[i].cls_field; -+ cls_cfg.num_extracts++; -+ -+ enabled_flags |= hash_fields[i].rxnfc_field; -+ } -+ -+ dma_mem = kzalloc(DPAA2_CLASSIFIER_DMA_SIZE, GFP_DMA | GFP_KERNEL); -+ if (!dma_mem) -+ return -ENOMEM; -+ -+ err = dpni_prepare_key_cfg(&cls_cfg, dma_mem); -+ if (err) { -+ dev_err(dev, "dpni_prepare_key_cfg error %d", err); -+ return err; -+ } -+ -+ memset(&dist_cfg, 0, sizeof(dist_cfg)); -+ -+ /* Prepare for setting the rx dist */ -+ dist_cfg.key_cfg_iova = dma_map_single(net_dev->dev.parent, dma_mem, -+ DPAA2_CLASSIFIER_DMA_SIZE, -+ DMA_TO_DEVICE); -+ if (dma_mapping_error(net_dev->dev.parent, dist_cfg.key_cfg_iova)) { -+ dev_err(dev, "DMA mapping failed\n"); -+ kfree(dma_mem); -+ return -ENOMEM; -+ } -+ -+ dist_cfg.dist_size = dpaa2_eth_queue_count(priv); -+ if (dpaa2_eth_fs_enabled(priv)) { -+ dist_cfg.dist_mode = DPNI_DIST_MODE_FS; -+ dist_cfg.fs_cfg.miss_action = DPNI_FS_MISS_HASH; -+ } else { -+ dist_cfg.dist_mode = DPNI_DIST_MODE_HASH; -+ } -+ -+ err = dpni_set_rx_tc_dist(priv->mc_io, 0, priv->mc_token, 0, &dist_cfg); -+ dma_unmap_single(net_dev->dev.parent, dist_cfg.key_cfg_iova, -+ DPAA2_CLASSIFIER_DMA_SIZE, DMA_TO_DEVICE); -+ kfree(dma_mem); -+ if (err) { -+ dev_err(dev, "dpni_set_rx_tc_dist() error %d\n", err); -+ return err; -+ } -+ -+ priv->rx_hash_fields = enabled_flags; -+ -+ return 0; -+} -+ -+static int prep_cls_rule(struct net_device *net_dev, -+ struct ethtool_rx_flow_spec *fs, -+ void *key) -+{ -+ struct ethtool_tcpip4_spec *l4ip4_h, *l4ip4_m; -+ struct ethhdr *eth_h, *eth_m; -+ struct ethtool_flow_ext *ext_h, *ext_m; -+ const u8 key_size = cls_key_size(net_dev); -+ void *msk = key + key_size; -+ -+ memset(key, 0, key_size * 2); -+ -+ /* This code is a major mess, it has to be cleaned up after the -+ * classification mask issue is fixed and key format will be made static -+ */ -+ -+ switch (fs->flow_type & 0xff) { -+ case TCP_V4_FLOW: -+ l4ip4_h = &fs->h_u.tcp_ip4_spec; -+ l4ip4_m = &fs->m_u.tcp_ip4_spec; -+ /* TODO: ethertype to match IPv4 and protocol to match TCP */ -+ goto l4ip4; -+ -+ case UDP_V4_FLOW: -+ l4ip4_h = &fs->h_u.udp_ip4_spec; -+ l4ip4_m = &fs->m_u.udp_ip4_spec; -+ goto l4ip4; -+ -+ case SCTP_V4_FLOW: -+ l4ip4_h = &fs->h_u.sctp_ip4_spec; -+ l4ip4_m = &fs->m_u.sctp_ip4_spec; -+ -+l4ip4: -+ if (l4ip4_m->tos) { -+ netdev_err(net_dev, -+ "ToS is not supported for IPv4 L4\n"); -+ return -EOPNOTSUPP; -+ } -+ if (l4ip4_m->ip4src && !cls_is_enabled(net_dev, RXH_IP_SRC)) { -+ netdev_err(net_dev, "IP SRC not supported!\n"); -+ return -EOPNOTSUPP; -+ } -+ if (l4ip4_m->ip4dst && !cls_is_enabled(net_dev, RXH_IP_DST)) { -+ netdev_err(net_dev, "IP DST not supported!\n"); -+ return -EOPNOTSUPP; -+ } -+ if (l4ip4_m->psrc && !cls_is_enabled(net_dev, RXH_L4_B_0_1)) { -+ netdev_err(net_dev, "PSRC not supported, ignored\n"); -+ return -EOPNOTSUPP; -+ } -+ if (l4ip4_m->pdst && !cls_is_enabled(net_dev, RXH_L4_B_2_3)) { -+ netdev_err(net_dev, "PDST not supported, ignored\n"); -+ return -EOPNOTSUPP; -+ } -+ -+ if (cls_is_enabled(net_dev, RXH_IP_SRC)) { -+ *(u32 *)(key + cls_key_off(net_dev, RXH_IP_SRC)) -+ = l4ip4_h->ip4src; -+ *(u32 *)(msk + cls_key_off(net_dev, RXH_IP_SRC)) -+ = l4ip4_m->ip4src; -+ } -+ if (cls_is_enabled(net_dev, RXH_IP_DST)) { -+ *(u32 *)(key + cls_key_off(net_dev, RXH_IP_DST)) -+ = l4ip4_h->ip4dst; -+ *(u32 *)(msk + cls_key_off(net_dev, RXH_IP_DST)) -+ = l4ip4_m->ip4dst; -+ } -+ -+ if (cls_is_enabled(net_dev, RXH_L4_B_0_1)) { -+ *(u32 *)(key + cls_key_off(net_dev, RXH_L4_B_0_1)) -+ = l4ip4_h->psrc; -+ *(u32 *)(msk + cls_key_off(net_dev, RXH_L4_B_0_1)) -+ = l4ip4_m->psrc; -+ } -+ -+ if (cls_is_enabled(net_dev, RXH_L4_B_2_3)) { -+ *(u32 *)(key + cls_key_off(net_dev, RXH_L4_B_2_3)) -+ = l4ip4_h->pdst; -+ *(u32 *)(msk + cls_key_off(net_dev, RXH_L4_B_2_3)) -+ = l4ip4_m->pdst; -+ } -+ break; -+ -+ case ETHER_FLOW: -+ eth_h = &fs->h_u.ether_spec; -+ eth_m = &fs->m_u.ether_spec; -+ -+ if (eth_m->h_proto) { -+ netdev_err(net_dev, "Ethertype is not supported!\n"); -+ return -EOPNOTSUPP; -+ } -+ -+ if (!is_zero_ether_addr(eth_m->h_source)) { -+ netdev_err(net_dev, "ETH SRC is not supported!\n"); -+ return -EOPNOTSUPP; -+ } -+ -+ if (cls_is_enabled(net_dev, RXH_L2DA)) { -+ ether_addr_copy(key + cls_key_off(net_dev, RXH_L2DA), -+ eth_h->h_dest); -+ ether_addr_copy(msk + cls_key_off(net_dev, RXH_L2DA), -+ eth_m->h_dest); -+ } else { -+ if (!is_zero_ether_addr(eth_m->h_dest)) { -+ netdev_err(net_dev, -+ "ETH DST is not supported!\n"); -+ return -EOPNOTSUPP; -+ } -+ } -+ break; -+ -+ default: -+ /* TODO: IP user flow, AH, ESP */ -+ return -EOPNOTSUPP; -+ } -+ -+ if (fs->flow_type & FLOW_EXT) { -+ /* TODO: ETH data, VLAN ethertype, VLAN TCI .. */ -+ return -EOPNOTSUPP; -+ } -+ -+ if (fs->flow_type & FLOW_MAC_EXT) { -+ ext_h = &fs->h_ext; -+ ext_m = &fs->m_ext; -+ -+ if (cls_is_enabled(net_dev, RXH_L2DA)) { -+ ether_addr_copy(key + cls_key_off(net_dev, RXH_L2DA), -+ ext_h->h_dest); -+ ether_addr_copy(msk + cls_key_off(net_dev, RXH_L2DA), -+ ext_m->h_dest); -+ } else { -+ if (!is_zero_ether_addr(ext_m->h_dest)) { -+ netdev_err(net_dev, -+ "ETH DST is not supported!\n"); -+ return -EOPNOTSUPP; -+ } -+ } -+ } -+ return 0; -+} -+ -+static int do_cls(struct net_device *net_dev, -+ struct ethtool_rx_flow_spec *fs, -+ bool add) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ const int rule_cnt = DPAA2_CLASSIFIER_ENTRY_COUNT; -+ struct dpni_rule_cfg rule_cfg; -+ void *dma_mem; -+ int err = 0; -+ -+ if (!dpaa2_eth_fs_enabled(priv)) { -+ netdev_err(net_dev, "dev does not support steering!\n"); -+ /* dev doesn't support steering */ -+ return -EOPNOTSUPP; -+ } -+ -+ if ((fs->ring_cookie != RX_CLS_FLOW_DISC && -+ fs->ring_cookie >= dpaa2_eth_queue_count(priv)) || -+ fs->location >= rule_cnt) -+ return -EINVAL; -+ -+ memset(&rule_cfg, 0, sizeof(rule_cfg)); -+ rule_cfg.key_size = cls_key_size(net_dev); -+ -+ /* allocate twice the key size, for the actual key and for mask */ -+ dma_mem = kzalloc(rule_cfg.key_size * 2, GFP_DMA | GFP_KERNEL); -+ if (!dma_mem) -+ return -ENOMEM; -+ -+ err = prep_cls_rule(net_dev, fs, dma_mem); -+ if (err) -+ goto err_free_mem; -+ -+ rule_cfg.key_iova = dma_map_single(net_dev->dev.parent, dma_mem, -+ rule_cfg.key_size * 2, -+ DMA_TO_DEVICE); -+ -+ rule_cfg.mask_iova = rule_cfg.key_iova + rule_cfg.key_size; -+ -+ if (!(priv->dpni_attrs.options & DPNI_OPT_FS_MASK_SUPPORT)) { -+ int i; -+ u8 *mask = dma_mem + rule_cfg.key_size; -+ -+ /* check that nothing is masked out, otherwise it won't work */ -+ for (i = 0; i < rule_cfg.key_size; i++) { -+ if (mask[i] == 0xff) -+ continue; -+ netdev_err(net_dev, "dev does not support masking!\n"); -+ err = -EOPNOTSUPP; -+ goto err_free_mem; -+ } -+ rule_cfg.mask_iova = 0; -+ } -+ -+ /* No way to control rule order in firmware */ -+ if (add) -+ err = dpni_add_fs_entry(priv->mc_io, 0, priv->mc_token, 0, -+ &rule_cfg, (u16)fs->ring_cookie); -+ else -+ err = dpni_remove_fs_entry(priv->mc_io, 0, priv->mc_token, 0, -+ &rule_cfg); -+ -+ dma_unmap_single(net_dev->dev.parent, rule_cfg.key_iova, -+ rule_cfg.key_size * 2, DMA_TO_DEVICE); -+ if (err) { -+ netdev_err(net_dev, "dpaa2_add_cls() error %d\n", err); -+ goto err_free_mem; -+ } -+ -+ priv->cls_rule[fs->location].fs = *fs; -+ priv->cls_rule[fs->location].in_use = true; -+ -+err_free_mem: -+ kfree(dma_mem); -+ -+ return err; -+} -+ -+static int add_cls(struct net_device *net_dev, -+ struct ethtool_rx_flow_spec *fs) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ int err; -+ -+ err = do_cls(net_dev, fs, true); -+ if (err) -+ return err; -+ -+ priv->cls_rule[fs->location].in_use = true; -+ priv->cls_rule[fs->location].fs = *fs; -+ -+ return 0; -+} -+ -+static int del_cls(struct net_device *net_dev, int location) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ int err; -+ -+ err = do_cls(net_dev, &priv->cls_rule[location].fs, false); -+ if (err) -+ return err; -+ -+ priv->cls_rule[location].in_use = false; -+ -+ return 0; -+} -+ -+static void clear_cls(struct net_device *net_dev) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ int i, err; -+ -+ for (i = 0; i < DPAA2_CLASSIFIER_ENTRY_COUNT; i++) { -+ if (!priv->cls_rule[i].in_use) -+ continue; -+ -+ err = del_cls(net_dev, i); -+ if (err) -+ netdev_warn(net_dev, -+ "err trying to delete classification entry %d\n", -+ i); -+ } -+} -+ -+static int dpaa2_eth_set_rxnfc(struct net_device *net_dev, -+ struct ethtool_rxnfc *rxnfc) -+{ -+ int err = 0; -+ -+ switch (rxnfc->cmd) { -+ case ETHTOOL_SRXFH: -+ /* first off clear ALL classification rules, chaging key -+ * composition will break them anyway -+ */ -+ clear_cls(net_dev); -+ /* we purposely ignore cmd->flow_type for now, because the -+ * classifier only supports a single set of fields for all -+ * protocols -+ */ -+ err = dpaa2_eth_set_hash(net_dev, rxnfc->data); -+ break; -+ case ETHTOOL_SRXCLSRLINS: -+ err = add_cls(net_dev, &rxnfc->fs); -+ break; -+ -+ case ETHTOOL_SRXCLSRLDEL: -+ err = del_cls(net_dev, rxnfc->fs.location); -+ break; -+ -+ default: -+ err = -EOPNOTSUPP; -+ } -+ -+ return err; -+} -+ -+static int dpaa2_eth_get_rxnfc(struct net_device *net_dev, -+ struct ethtool_rxnfc *rxnfc, u32 *rule_locs) -+{ -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ const int rule_cnt = DPAA2_CLASSIFIER_ENTRY_COUNT; -+ int i, j; -+ -+ switch (rxnfc->cmd) { -+ case ETHTOOL_GRXFH: -+ /* we purposely ignore cmd->flow_type for now, because the -+ * classifier only supports a single set of fields for all -+ * protocols -+ */ -+ rxnfc->data = priv->rx_hash_fields; -+ break; -+ -+ case ETHTOOL_GRXRINGS: -+ rxnfc->data = dpaa2_eth_queue_count(priv); -+ break; -+ -+ case ETHTOOL_GRXCLSRLCNT: -+ for (i = 0, rxnfc->rule_cnt = 0; i < rule_cnt; i++) -+ if (priv->cls_rule[i].in_use) -+ rxnfc->rule_cnt++; -+ rxnfc->data = rule_cnt; -+ break; -+ -+ case ETHTOOL_GRXCLSRULE: -+ if (!priv->cls_rule[rxnfc->fs.location].in_use) -+ return -EINVAL; -+ -+ rxnfc->fs = priv->cls_rule[rxnfc->fs.location].fs; -+ break; -+ -+ case ETHTOOL_GRXCLSRLALL: -+ for (i = 0, j = 0; i < rule_cnt; i++) { -+ if (!priv->cls_rule[i].in_use) -+ continue; -+ if (j == rxnfc->rule_cnt) -+ return -EMSGSIZE; -+ rule_locs[j++] = i; -+ } -+ rxnfc->rule_cnt = j; -+ rxnfc->data = rule_cnt; -+ break; -+ -+ default: -+ return -EOPNOTSUPP; -+ } -+ -+ return 0; -+} -+ -+const struct ethtool_ops dpaa2_ethtool_ops = { -+ .get_drvinfo = dpaa2_eth_get_drvinfo, -+ .get_link = ethtool_op_get_link, -+ .get_settings = dpaa2_eth_get_settings, -+ .set_settings = dpaa2_eth_set_settings, -+ .get_sset_count = dpaa2_eth_get_sset_count, -+ .get_ethtool_stats = dpaa2_eth_get_ethtool_stats, -+ .get_strings = dpaa2_eth_get_strings, -+ .get_rxnfc = dpaa2_eth_get_rxnfc, -+ .set_rxnfc = dpaa2_eth_set_rxnfc, -+}; -diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpkg.h b/drivers/staging/fsl-dpaa2/ethernet/dpkg.h -new file mode 100644 -index 0000000..92ec12b ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/ethernet/dpkg.h -@@ -0,0 +1,175 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef __FSL_DPKG_H_ -+#define __FSL_DPKG_H_ -+ -+#include -+#include "../../fsl-mc/include/net.h" -+ -+/* Data Path Key Generator API -+ * Contains initialization APIs and runtime APIs for the Key Generator -+ */ -+ -+/** Key Generator properties */ -+ -+/** -+ * Number of masks per key extraction -+ */ -+#define DPKG_NUM_OF_MASKS 4 -+/** -+ * Number of extractions per key profile -+ */ -+#define DPKG_MAX_NUM_OF_EXTRACTS 10 -+ -+/** -+ * enum dpkg_extract_from_hdr_type - Selecting extraction by header types -+ * @DPKG_FROM_HDR: Extract selected bytes from header, by offset -+ * @DPKG_FROM_FIELD: Extract selected bytes from header, by offset from field -+ * @DPKG_FULL_FIELD: Extract a full field -+ */ -+enum dpkg_extract_from_hdr_type { -+ DPKG_FROM_HDR = 0, -+ DPKG_FROM_FIELD = 1, -+ DPKG_FULL_FIELD = 2 -+}; -+ -+/** -+ * enum dpkg_extract_type - Enumeration for selecting extraction type -+ * @DPKG_EXTRACT_FROM_HDR: Extract from the header -+ * @DPKG_EXTRACT_FROM_DATA: Extract from data not in specific header -+ * @DPKG_EXTRACT_FROM_PARSE: Extract from parser-result; -+ * e.g. can be used to extract header existence; -+ * please refer to 'Parse Result definition' section in the parser BG -+ */ -+enum dpkg_extract_type { -+ DPKG_EXTRACT_FROM_HDR = 0, -+ DPKG_EXTRACT_FROM_DATA = 1, -+ DPKG_EXTRACT_FROM_PARSE = 3 -+}; -+ -+/** -+ * struct dpkg_mask - A structure for defining a single extraction mask -+ * @mask: Byte mask for the extracted content -+ * @offset: Offset within the extracted content -+ */ -+struct dpkg_mask { -+ uint8_t mask; -+ uint8_t offset; -+}; -+ -+/** -+ * struct dpkg_extract - A structure for defining a single extraction -+ * @type: Determines how the union below is interpreted: -+ * DPKG_EXTRACT_FROM_HDR: selects 'from_hdr'; -+ * DPKG_EXTRACT_FROM_DATA: selects 'from_data'; -+ * DPKG_EXTRACT_FROM_PARSE: selects 'from_parse' -+ * @extract: Selects extraction method -+ * @num_of_byte_masks: Defines the number of valid entries in the array below; -+ * This is also the number of bytes to be used as masks -+ * @masks: Masks parameters -+ */ -+struct dpkg_extract { -+ enum dpkg_extract_type type; -+ /** -+ * union extract - Selects extraction method -+ * @from_hdr - Used when 'type = DPKG_EXTRACT_FROM_HDR' -+ * @from_data - Used when 'type = DPKG_EXTRACT_FROM_DATA' -+ * @from_parse - Used when 'type = DPKG_EXTRACT_FROM_PARSE' -+ */ -+ union { -+ /** -+ * struct from_hdr - Used when 'type = DPKG_EXTRACT_FROM_HDR' -+ * @prot: Any of the supported headers -+ * @type: Defines the type of header extraction: -+ * DPKG_FROM_HDR: use size & offset below; -+ * DPKG_FROM_FIELD: use field, size and offset below; -+ * DPKG_FULL_FIELD: use field below -+ * @field: One of the supported fields (NH_FLD_) -+ * -+ * @size: Size in bytes -+ * @offset: Byte offset -+ * @hdr_index: Clear for cases not listed below; -+ * Used for protocols that may have more than a single -+ * header, 0 indicates an outer header; -+ * Supported protocols (possible values): -+ * NET_PROT_VLAN (0, HDR_INDEX_LAST); -+ * NET_PROT_MPLS (0, 1, HDR_INDEX_LAST); -+ * NET_PROT_IP(0, HDR_INDEX_LAST); -+ * NET_PROT_IPv4(0, HDR_INDEX_LAST); -+ * NET_PROT_IPv6(0, HDR_INDEX_LAST); -+ */ -+ -+ struct { -+ enum net_prot prot; -+ enum dpkg_extract_from_hdr_type type; -+ uint32_t field; -+ uint8_t size; -+ uint8_t offset; -+ uint8_t hdr_index; -+ } from_hdr; -+ /** -+ * struct from_data - Used when 'type = DPKG_EXTRACT_FROM_DATA' -+ * @size: Size in bytes -+ * @offset: Byte offset -+ */ -+ struct { -+ uint8_t size; -+ uint8_t offset; -+ } from_data; -+ -+ /** -+ * struct from_parse - Used when 'type = DPKG_EXTRACT_FROM_PARSE' -+ * @size: Size in bytes -+ * @offset: Byte offset -+ */ -+ struct { -+ uint8_t size; -+ uint8_t offset; -+ } from_parse; -+ } extract; -+ -+ uint8_t num_of_byte_masks; -+ struct dpkg_mask masks[DPKG_NUM_OF_MASKS]; -+}; -+ -+/** -+ * struct dpkg_profile_cfg - A structure for defining a full Key Generation -+ * profile (rule) -+ * @num_extracts: Defines the number of valid entries in the array below -+ * @extracts: Array of required extractions -+ */ -+struct dpkg_profile_cfg { -+ uint8_t num_extracts; -+ struct dpkg_extract extracts[DPKG_MAX_NUM_OF_EXTRACTS]; -+}; -+ -+#endif /* __FSL_DPKG_H_ */ -diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpni-cmd.h b/drivers/staging/fsl-dpaa2/ethernet/dpni-cmd.h -new file mode 100644 -index 0000000..c0f8af0 ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/ethernet/dpni-cmd.h -@@ -0,0 +1,1058 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef _FSL_DPNI_CMD_H -+#define _FSL_DPNI_CMD_H -+ -+/* DPNI Version */ -+#define DPNI_VER_MAJOR 6 -+#define DPNI_VER_MINOR 0 -+ -+/* Command IDs */ -+#define DPNI_CMDID_OPEN 0x801 -+#define DPNI_CMDID_CLOSE 0x800 -+#define DPNI_CMDID_CREATE 0x901 -+#define DPNI_CMDID_DESTROY 0x900 -+ -+#define DPNI_CMDID_ENABLE 0x002 -+#define DPNI_CMDID_DISABLE 0x003 -+#define DPNI_CMDID_GET_ATTR 0x004 -+#define DPNI_CMDID_RESET 0x005 -+#define DPNI_CMDID_IS_ENABLED 0x006 -+ -+#define DPNI_CMDID_SET_IRQ 0x010 -+#define DPNI_CMDID_GET_IRQ 0x011 -+#define DPNI_CMDID_SET_IRQ_ENABLE 0x012 -+#define DPNI_CMDID_GET_IRQ_ENABLE 0x013 -+#define DPNI_CMDID_SET_IRQ_MASK 0x014 -+#define DPNI_CMDID_GET_IRQ_MASK 0x015 -+#define DPNI_CMDID_GET_IRQ_STATUS 0x016 -+#define DPNI_CMDID_CLEAR_IRQ_STATUS 0x017 -+ -+#define DPNI_CMDID_SET_POOLS 0x200 -+#define DPNI_CMDID_GET_RX_BUFFER_LAYOUT 0x201 -+#define DPNI_CMDID_SET_RX_BUFFER_LAYOUT 0x202 -+#define DPNI_CMDID_GET_TX_BUFFER_LAYOUT 0x203 -+#define DPNI_CMDID_SET_TX_BUFFER_LAYOUT 0x204 -+#define DPNI_CMDID_SET_TX_CONF_BUFFER_LAYOUT 0x205 -+#define DPNI_CMDID_GET_TX_CONF_BUFFER_LAYOUT 0x206 -+#define DPNI_CMDID_SET_L3_CHKSUM_VALIDATION 0x207 -+#define DPNI_CMDID_GET_L3_CHKSUM_VALIDATION 0x208 -+#define DPNI_CMDID_SET_L4_CHKSUM_VALIDATION 0x209 -+#define DPNI_CMDID_GET_L4_CHKSUM_VALIDATION 0x20A -+#define DPNI_CMDID_SET_ERRORS_BEHAVIOR 0x20B -+#define DPNI_CMDID_SET_TX_CONF_REVOKE 0x20C -+ -+#define DPNI_CMDID_GET_QDID 0x210 -+#define DPNI_CMDID_GET_SP_INFO 0x211 -+#define DPNI_CMDID_GET_TX_DATA_OFFSET 0x212 -+#define DPNI_CMDID_GET_COUNTER 0x213 -+#define DPNI_CMDID_SET_COUNTER 0x214 -+#define DPNI_CMDID_GET_LINK_STATE 0x215 -+#define DPNI_CMDID_SET_MAX_FRAME_LENGTH 0x216 -+#define DPNI_CMDID_GET_MAX_FRAME_LENGTH 0x217 -+#define DPNI_CMDID_SET_MTU 0x218 -+#define DPNI_CMDID_GET_MTU 0x219 -+#define DPNI_CMDID_SET_LINK_CFG 0x21A -+#define DPNI_CMDID_SET_TX_SHAPING 0x21B -+ -+#define DPNI_CMDID_SET_MCAST_PROMISC 0x220 -+#define DPNI_CMDID_GET_MCAST_PROMISC 0x221 -+#define DPNI_CMDID_SET_UNICAST_PROMISC 0x222 -+#define DPNI_CMDID_GET_UNICAST_PROMISC 0x223 -+#define DPNI_CMDID_SET_PRIM_MAC 0x224 -+#define DPNI_CMDID_GET_PRIM_MAC 0x225 -+#define DPNI_CMDID_ADD_MAC_ADDR 0x226 -+#define DPNI_CMDID_REMOVE_MAC_ADDR 0x227 -+#define DPNI_CMDID_CLR_MAC_FILTERS 0x228 -+ -+#define DPNI_CMDID_SET_VLAN_FILTERS 0x230 -+#define DPNI_CMDID_ADD_VLAN_ID 0x231 -+#define DPNI_CMDID_REMOVE_VLAN_ID 0x232 -+#define DPNI_CMDID_CLR_VLAN_FILTERS 0x233 -+ -+#define DPNI_CMDID_SET_RX_TC_DIST 0x235 -+#define DPNI_CMDID_SET_TX_FLOW 0x236 -+#define DPNI_CMDID_GET_TX_FLOW 0x237 -+#define DPNI_CMDID_SET_RX_FLOW 0x238 -+#define DPNI_CMDID_GET_RX_FLOW 0x239 -+#define DPNI_CMDID_SET_RX_ERR_QUEUE 0x23A -+#define DPNI_CMDID_GET_RX_ERR_QUEUE 0x23B -+ -+#define DPNI_CMDID_SET_RX_TC_POLICING 0x23E -+#define DPNI_CMDID_SET_RX_TC_EARLY_DROP 0x23F -+ -+#define DPNI_CMDID_SET_QOS_TBL 0x240 -+#define DPNI_CMDID_ADD_QOS_ENT 0x241 -+#define DPNI_CMDID_REMOVE_QOS_ENT 0x242 -+#define DPNI_CMDID_CLR_QOS_TBL 0x243 -+#define DPNI_CMDID_ADD_FS_ENT 0x244 -+#define DPNI_CMDID_REMOVE_FS_ENT 0x245 -+#define DPNI_CMDID_CLR_FS_ENT 0x246 -+#define DPNI_CMDID_SET_VLAN_INSERTION 0x247 -+#define DPNI_CMDID_SET_VLAN_REMOVAL 0x248 -+#define DPNI_CMDID_SET_IPR 0x249 -+#define DPNI_CMDID_SET_IPF 0x24A -+ -+#define DPNI_CMDID_SET_TX_SELECTION 0x250 -+#define DPNI_CMDID_GET_RX_TC_POLICING 0x251 -+#define DPNI_CMDID_GET_RX_TC_EARLY_DROP 0x252 -+#define DPNI_CMDID_SET_RX_TC_CONGESTION_NOTIFICATION 0x253 -+#define DPNI_CMDID_GET_RX_TC_CONGESTION_NOTIFICATION 0x254 -+#define DPNI_CMDID_SET_TX_TC_CONGESTION_NOTIFICATION 0x255 -+#define DPNI_CMDID_GET_TX_TC_CONGESTION_NOTIFICATION 0x256 -+#define DPNI_CMDID_SET_TX_CONF 0x257 -+#define DPNI_CMDID_GET_TX_CONF 0x258 -+#define DPNI_CMDID_SET_TX_CONF_CONGESTION_NOTIFICATION 0x259 -+#define DPNI_CMDID_GET_TX_CONF_CONGESTION_NOTIFICATION 0x25A -+#define DPNI_CMDID_SET_TX_TC_EARLY_DROP 0x25B -+#define DPNI_CMDID_GET_TX_TC_EARLY_DROP 0x25C -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_OPEN(cmd, dpni_id) \ -+ MC_CMD_OP(cmd, 0, 0, 32, int, dpni_id) -+ -+#define DPNI_PREP_EXTENDED_CFG(ext, cfg) \ -+do { \ -+ MC_PREP_OP(ext, 0, 0, 16, uint16_t, cfg->tc_cfg[0].max_dist); \ -+ MC_PREP_OP(ext, 0, 16, 16, uint16_t, cfg->tc_cfg[0].max_fs_entries); \ -+ MC_PREP_OP(ext, 0, 32, 16, uint16_t, cfg->tc_cfg[1].max_dist); \ -+ MC_PREP_OP(ext, 0, 48, 16, uint16_t, cfg->tc_cfg[1].max_fs_entries); \ -+ MC_PREP_OP(ext, 1, 0, 16, uint16_t, cfg->tc_cfg[2].max_dist); \ -+ MC_PREP_OP(ext, 1, 16, 16, uint16_t, cfg->tc_cfg[2].max_fs_entries); \ -+ MC_PREP_OP(ext, 1, 32, 16, uint16_t, cfg->tc_cfg[3].max_dist); \ -+ MC_PREP_OP(ext, 1, 48, 16, uint16_t, cfg->tc_cfg[3].max_fs_entries); \ -+ MC_PREP_OP(ext, 2, 0, 16, uint16_t, cfg->tc_cfg[4].max_dist); \ -+ MC_PREP_OP(ext, 2, 16, 16, uint16_t, cfg->tc_cfg[4].max_fs_entries); \ -+ MC_PREP_OP(ext, 2, 32, 16, uint16_t, cfg->tc_cfg[5].max_dist); \ -+ MC_PREP_OP(ext, 2, 48, 16, uint16_t, cfg->tc_cfg[5].max_fs_entries); \ -+ MC_PREP_OP(ext, 3, 0, 16, uint16_t, cfg->tc_cfg[6].max_dist); \ -+ MC_PREP_OP(ext, 3, 16, 16, uint16_t, cfg->tc_cfg[6].max_fs_entries); \ -+ MC_PREP_OP(ext, 3, 32, 16, uint16_t, cfg->tc_cfg[7].max_dist); \ -+ MC_PREP_OP(ext, 3, 48, 16, uint16_t, cfg->tc_cfg[7].max_fs_entries); \ -+ MC_PREP_OP(ext, 4, 0, 16, uint16_t, \ -+ cfg->ipr_cfg.max_open_frames_ipv4); \ -+ MC_PREP_OP(ext, 4, 16, 16, uint16_t, \ -+ cfg->ipr_cfg.max_open_frames_ipv6); \ -+ MC_PREP_OP(ext, 4, 32, 16, uint16_t, \ -+ cfg->ipr_cfg.max_reass_frm_size); \ -+ MC_PREP_OP(ext, 5, 0, 16, uint16_t, \ -+ cfg->ipr_cfg.min_frag_size_ipv4); \ -+ MC_PREP_OP(ext, 5, 16, 16, uint16_t, \ -+ cfg->ipr_cfg.min_frag_size_ipv6); \ -+} while (0) -+ -+#define DPNI_EXT_EXTENDED_CFG(ext, cfg) \ -+do { \ -+ MC_EXT_OP(ext, 0, 0, 16, uint16_t, cfg->tc_cfg[0].max_dist); \ -+ MC_EXT_OP(ext, 0, 16, 16, uint16_t, cfg->tc_cfg[0].max_fs_entries); \ -+ MC_EXT_OP(ext, 0, 32, 16, uint16_t, cfg->tc_cfg[1].max_dist); \ -+ MC_EXT_OP(ext, 0, 48, 16, uint16_t, cfg->tc_cfg[1].max_fs_entries); \ -+ MC_EXT_OP(ext, 1, 0, 16, uint16_t, cfg->tc_cfg[2].max_dist); \ -+ MC_EXT_OP(ext, 1, 16, 16, uint16_t, cfg->tc_cfg[2].max_fs_entries); \ -+ MC_EXT_OP(ext, 1, 32, 16, uint16_t, cfg->tc_cfg[3].max_dist); \ -+ MC_EXT_OP(ext, 1, 48, 16, uint16_t, cfg->tc_cfg[3].max_fs_entries); \ -+ MC_EXT_OP(ext, 2, 0, 16, uint16_t, cfg->tc_cfg[4].max_dist); \ -+ MC_EXT_OP(ext, 2, 16, 16, uint16_t, cfg->tc_cfg[4].max_fs_entries); \ -+ MC_EXT_OP(ext, 2, 32, 16, uint16_t, cfg->tc_cfg[5].max_dist); \ -+ MC_EXT_OP(ext, 2, 48, 16, uint16_t, cfg->tc_cfg[5].max_fs_entries); \ -+ MC_EXT_OP(ext, 3, 0, 16, uint16_t, cfg->tc_cfg[6].max_dist); \ -+ MC_EXT_OP(ext, 3, 16, 16, uint16_t, cfg->tc_cfg[6].max_fs_entries); \ -+ MC_EXT_OP(ext, 3, 32, 16, uint16_t, cfg->tc_cfg[7].max_dist); \ -+ MC_EXT_OP(ext, 3, 48, 16, uint16_t, cfg->tc_cfg[7].max_fs_entries); \ -+ MC_EXT_OP(ext, 4, 0, 16, uint16_t, \ -+ cfg->ipr_cfg.max_open_frames_ipv4); \ -+ MC_EXT_OP(ext, 4, 16, 16, uint16_t, \ -+ cfg->ipr_cfg.max_open_frames_ipv6); \ -+ MC_EXT_OP(ext, 4, 32, 16, uint16_t, \ -+ cfg->ipr_cfg.max_reass_frm_size); \ -+ MC_EXT_OP(ext, 5, 0, 16, uint16_t, \ -+ cfg->ipr_cfg.min_frag_size_ipv4); \ -+ MC_EXT_OP(ext, 5, 16, 16, uint16_t, \ -+ cfg->ipr_cfg.min_frag_size_ipv6); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_CREATE(cmd, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, cfg->adv.max_tcs); \ -+ MC_CMD_OP(cmd, 0, 8, 8, uint8_t, cfg->adv.max_senders); \ -+ MC_CMD_OP(cmd, 0, 16, 8, uint8_t, cfg->mac_addr[5]); \ -+ MC_CMD_OP(cmd, 0, 24, 8, uint8_t, cfg->mac_addr[4]); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, cfg->mac_addr[3]); \ -+ MC_CMD_OP(cmd, 0, 40, 8, uint8_t, cfg->mac_addr[2]); \ -+ MC_CMD_OP(cmd, 0, 48, 8, uint8_t, cfg->mac_addr[1]); \ -+ MC_CMD_OP(cmd, 0, 56, 8, uint8_t, cfg->mac_addr[0]); \ -+ MC_CMD_OP(cmd, 1, 0, 32, uint32_t, cfg->adv.options); \ -+ MC_CMD_OP(cmd, 2, 0, 8, uint8_t, cfg->adv.max_unicast_filters); \ -+ MC_CMD_OP(cmd, 2, 8, 8, uint8_t, cfg->adv.max_multicast_filters); \ -+ MC_CMD_OP(cmd, 2, 16, 8, uint8_t, cfg->adv.max_vlan_filters); \ -+ MC_CMD_OP(cmd, 2, 24, 8, uint8_t, cfg->adv.max_qos_entries); \ -+ MC_CMD_OP(cmd, 2, 32, 8, uint8_t, cfg->adv.max_qos_key_size); \ -+ MC_CMD_OP(cmd, 2, 48, 8, uint8_t, cfg->adv.max_dist_key_size); \ -+ MC_CMD_OP(cmd, 2, 56, 8, enum net_prot, cfg->adv.start_hdr); \ -+ MC_CMD_OP(cmd, 4, 48, 8, uint8_t, cfg->adv.max_policers); \ -+ MC_CMD_OP(cmd, 4, 56, 8, uint8_t, cfg->adv.max_congestion_ctrl); \ -+ MC_CMD_OP(cmd, 5, 0, 64, uint64_t, cfg->adv.ext_cfg_iova); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_POOLS(cmd, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, cfg->num_dpbp); \ -+ MC_CMD_OP(cmd, 0, 8, 1, int, cfg->pools[0].backup_pool); \ -+ MC_CMD_OP(cmd, 0, 9, 1, int, cfg->pools[1].backup_pool); \ -+ MC_CMD_OP(cmd, 0, 10, 1, int, cfg->pools[2].backup_pool); \ -+ MC_CMD_OP(cmd, 0, 11, 1, int, cfg->pools[3].backup_pool); \ -+ MC_CMD_OP(cmd, 0, 12, 1, int, cfg->pools[4].backup_pool); \ -+ MC_CMD_OP(cmd, 0, 13, 1, int, cfg->pools[5].backup_pool); \ -+ MC_CMD_OP(cmd, 0, 14, 1, int, cfg->pools[6].backup_pool); \ -+ MC_CMD_OP(cmd, 0, 15, 1, int, cfg->pools[7].backup_pool); \ -+ MC_CMD_OP(cmd, 0, 32, 32, int, cfg->pools[0].dpbp_id); \ -+ MC_CMD_OP(cmd, 4, 32, 16, uint16_t, cfg->pools[0].buffer_size);\ -+ MC_CMD_OP(cmd, 1, 0, 32, int, cfg->pools[1].dpbp_id); \ -+ MC_CMD_OP(cmd, 4, 48, 16, uint16_t, cfg->pools[1].buffer_size);\ -+ MC_CMD_OP(cmd, 1, 32, 32, int, cfg->pools[2].dpbp_id); \ -+ MC_CMD_OP(cmd, 5, 0, 16, uint16_t, cfg->pools[2].buffer_size);\ -+ MC_CMD_OP(cmd, 2, 0, 32, int, cfg->pools[3].dpbp_id); \ -+ MC_CMD_OP(cmd, 5, 16, 16, uint16_t, cfg->pools[3].buffer_size);\ -+ MC_CMD_OP(cmd, 2, 32, 32, int, cfg->pools[4].dpbp_id); \ -+ MC_CMD_OP(cmd, 5, 32, 16, uint16_t, cfg->pools[4].buffer_size);\ -+ MC_CMD_OP(cmd, 3, 0, 32, int, cfg->pools[5].dpbp_id); \ -+ MC_CMD_OP(cmd, 5, 48, 16, uint16_t, cfg->pools[5].buffer_size);\ -+ MC_CMD_OP(cmd, 3, 32, 32, int, cfg->pools[6].dpbp_id); \ -+ MC_CMD_OP(cmd, 6, 0, 16, uint16_t, cfg->pools[6].buffer_size);\ -+ MC_CMD_OP(cmd, 4, 0, 32, int, cfg->pools[7].dpbp_id); \ -+ MC_CMD_OP(cmd, 6, 16, 16, uint16_t, cfg->pools[7].buffer_size);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_IS_ENABLED(cmd, en) \ -+ MC_RSP_OP(cmd, 0, 0, 1, int, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_IRQ(cmd, irq_index, irq_cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, irq_cfg->val); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index); \ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, irq_cfg->addr); \ -+ MC_CMD_OP(cmd, 2, 0, 32, int, irq_cfg->irq_num); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_GET_IRQ(cmd, irq_index) \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_IRQ(cmd, type, irq_cfg) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, irq_cfg->val); \ -+ MC_RSP_OP(cmd, 1, 0, 64, uint64_t, irq_cfg->addr); \ -+ MC_RSP_OP(cmd, 2, 0, 32, int, irq_cfg->irq_num); \ -+ MC_RSP_OP(cmd, 2, 32, 32, int, type); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_IRQ_ENABLE(cmd, irq_index, en) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, en); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_GET_IRQ_ENABLE(cmd, irq_index) \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_IRQ_ENABLE(cmd, en) \ -+ MC_RSP_OP(cmd, 0, 0, 8, uint8_t, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_IRQ_MASK(cmd, irq_index, mask) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, mask); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_GET_IRQ_MASK(cmd, irq_index) \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_IRQ_MASK(cmd, mask) \ -+ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, mask) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_GET_IRQ_STATUS(cmd, irq_index, status) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, status);\ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_IRQ_STATUS(cmd, status) \ -+ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, status) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_CLEAR_IRQ_STATUS(cmd, irq_index, status) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, status); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_GET_ATTR(cmd, attr) \ -+ MC_CMD_OP(cmd, 6, 0, 64, uint64_t, attr->ext_cfg_iova) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_ATTR(cmd, attr) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 32, int, attr->id);\ -+ MC_RSP_OP(cmd, 0, 32, 8, uint8_t, attr->max_tcs); \ -+ MC_RSP_OP(cmd, 0, 40, 8, uint8_t, attr->max_senders); \ -+ MC_RSP_OP(cmd, 0, 48, 8, enum net_prot, attr->start_hdr); \ -+ MC_RSP_OP(cmd, 1, 0, 32, uint32_t, attr->options); \ -+ MC_RSP_OP(cmd, 2, 0, 8, uint8_t, attr->max_unicast_filters); \ -+ MC_RSP_OP(cmd, 2, 8, 8, uint8_t, attr->max_multicast_filters);\ -+ MC_RSP_OP(cmd, 2, 16, 8, uint8_t, attr->max_vlan_filters); \ -+ MC_RSP_OP(cmd, 2, 24, 8, uint8_t, attr->max_qos_entries); \ -+ MC_RSP_OP(cmd, 2, 32, 8, uint8_t, attr->max_qos_key_size); \ -+ MC_RSP_OP(cmd, 2, 40, 8, uint8_t, attr->max_dist_key_size); \ -+ MC_RSP_OP(cmd, 4, 48, 8, uint8_t, attr->max_policers); \ -+ MC_RSP_OP(cmd, 4, 56, 8, uint8_t, attr->max_congestion_ctrl); \ -+ MC_RSP_OP(cmd, 5, 32, 16, uint16_t, attr->version.major);\ -+ MC_RSP_OP(cmd, 5, 48, 16, uint16_t, attr->version.minor);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_ERRORS_BEHAVIOR(cmd, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, cfg->errors); \ -+ MC_CMD_OP(cmd, 0, 32, 4, enum dpni_error_action, cfg->error_action); \ -+ MC_CMD_OP(cmd, 0, 36, 1, int, cfg->set_frame_annotation); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_RX_BUFFER_LAYOUT(cmd, layout) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 16, uint16_t, layout->private_data_size); \ -+ MC_RSP_OP(cmd, 0, 16, 16, uint16_t, layout->data_align); \ -+ MC_RSP_OP(cmd, 1, 0, 1, int, layout->pass_timestamp); \ -+ MC_RSP_OP(cmd, 1, 1, 1, int, layout->pass_parser_result); \ -+ MC_RSP_OP(cmd, 1, 2, 1, int, layout->pass_frame_status); \ -+ MC_RSP_OP(cmd, 1, 16, 16, uint16_t, layout->data_head_room); \ -+ MC_RSP_OP(cmd, 1, 32, 16, uint16_t, layout->data_tail_room); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_RX_BUFFER_LAYOUT(cmd, layout) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 16, uint16_t, layout->private_data_size); \ -+ MC_CMD_OP(cmd, 0, 16, 16, uint16_t, layout->data_align); \ -+ MC_CMD_OP(cmd, 0, 32, 32, uint32_t, layout->options); \ -+ MC_CMD_OP(cmd, 1, 0, 1, int, layout->pass_timestamp); \ -+ MC_CMD_OP(cmd, 1, 1, 1, int, layout->pass_parser_result); \ -+ MC_CMD_OP(cmd, 1, 2, 1, int, layout->pass_frame_status); \ -+ MC_CMD_OP(cmd, 1, 16, 16, uint16_t, layout->data_head_room); \ -+ MC_CMD_OP(cmd, 1, 32, 16, uint16_t, layout->data_tail_room); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_TX_BUFFER_LAYOUT(cmd, layout) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 16, uint16_t, layout->private_data_size); \ -+ MC_RSP_OP(cmd, 0, 16, 16, uint16_t, layout->data_align); \ -+ MC_RSP_OP(cmd, 1, 0, 1, int, layout->pass_timestamp); \ -+ MC_RSP_OP(cmd, 1, 1, 1, int, layout->pass_parser_result); \ -+ MC_RSP_OP(cmd, 1, 2, 1, int, layout->pass_frame_status); \ -+ MC_RSP_OP(cmd, 1, 16, 16, uint16_t, layout->data_head_room); \ -+ MC_RSP_OP(cmd, 1, 32, 16, uint16_t, layout->data_tail_room); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_TX_BUFFER_LAYOUT(cmd, layout) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 16, uint16_t, layout->private_data_size); \ -+ MC_CMD_OP(cmd, 0, 16, 16, uint16_t, layout->data_align); \ -+ MC_CMD_OP(cmd, 0, 32, 32, uint32_t, layout->options); \ -+ MC_CMD_OP(cmd, 1, 0, 1, int, layout->pass_timestamp); \ -+ MC_CMD_OP(cmd, 1, 1, 1, int, layout->pass_parser_result); \ -+ MC_CMD_OP(cmd, 1, 2, 1, int, layout->pass_frame_status); \ -+ MC_CMD_OP(cmd, 1, 16, 16, uint16_t, layout->data_head_room); \ -+ MC_CMD_OP(cmd, 1, 32, 16, uint16_t, layout->data_tail_room); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_TX_CONF_BUFFER_LAYOUT(cmd, layout) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 16, uint16_t, layout->private_data_size); \ -+ MC_RSP_OP(cmd, 0, 16, 16, uint16_t, layout->data_align); \ -+ MC_RSP_OP(cmd, 1, 0, 1, int, layout->pass_timestamp); \ -+ MC_RSP_OP(cmd, 1, 1, 1, int, layout->pass_parser_result); \ -+ MC_RSP_OP(cmd, 1, 2, 1, int, layout->pass_frame_status); \ -+ MC_RSP_OP(cmd, 1, 16, 16, uint16_t, layout->data_head_room); \ -+ MC_RSP_OP(cmd, 1, 32, 16, uint16_t, layout->data_tail_room); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_TX_CONF_BUFFER_LAYOUT(cmd, layout) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 16, uint16_t, layout->private_data_size); \ -+ MC_CMD_OP(cmd, 0, 16, 16, uint16_t, layout->data_align); \ -+ MC_CMD_OP(cmd, 0, 32, 32, uint32_t, layout->options); \ -+ MC_CMD_OP(cmd, 1, 0, 1, int, layout->pass_timestamp); \ -+ MC_CMD_OP(cmd, 1, 1, 1, int, layout->pass_parser_result); \ -+ MC_CMD_OP(cmd, 1, 2, 1, int, layout->pass_frame_status); \ -+ MC_CMD_OP(cmd, 1, 16, 16, uint16_t, layout->data_head_room); \ -+ MC_CMD_OP(cmd, 1, 32, 16, uint16_t, layout->data_tail_room); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_L3_CHKSUM_VALIDATION(cmd, en) \ -+ MC_CMD_OP(cmd, 0, 0, 1, int, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_L3_CHKSUM_VALIDATION(cmd, en) \ -+ MC_RSP_OP(cmd, 0, 0, 1, int, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_L4_CHKSUM_VALIDATION(cmd, en) \ -+ MC_CMD_OP(cmd, 0, 0, 1, int, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_L4_CHKSUM_VALIDATION(cmd, en) \ -+ MC_RSP_OP(cmd, 0, 0, 1, int, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_QDID(cmd, qdid) \ -+ MC_RSP_OP(cmd, 0, 0, 16, uint16_t, qdid) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_SP_INFO(cmd, sp_info) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 16, uint16_t, sp_info->spids[0]); \ -+ MC_RSP_OP(cmd, 0, 16, 16, uint16_t, sp_info->spids[1]); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_TX_DATA_OFFSET(cmd, data_offset) \ -+ MC_RSP_OP(cmd, 0, 0, 16, uint16_t, data_offset) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_GET_COUNTER(cmd, counter) \ -+ MC_CMD_OP(cmd, 0, 0, 16, enum dpni_counter, counter) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_COUNTER(cmd, value) \ -+ MC_RSP_OP(cmd, 1, 0, 64, uint64_t, value) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_COUNTER(cmd, counter, value) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 16, enum dpni_counter, counter); \ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, value); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_LINK_CFG(cmd, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 1, 0, 32, uint32_t, cfg->rate);\ -+ MC_CMD_OP(cmd, 2, 0, 64, uint64_t, cfg->options);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_LINK_STATE(cmd, state) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 32, 1, int, state->up);\ -+ MC_RSP_OP(cmd, 1, 0, 32, uint32_t, state->rate);\ -+ MC_RSP_OP(cmd, 2, 0, 64, uint64_t, state->options);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_TX_SHAPING(cmd, tx_shaper) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 16, uint16_t, tx_shaper->max_burst_size);\ -+ MC_CMD_OP(cmd, 1, 0, 32, uint32_t, tx_shaper->rate_limit);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_MAX_FRAME_LENGTH(cmd, max_frame_length) \ -+ MC_CMD_OP(cmd, 0, 0, 16, uint16_t, max_frame_length) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_MAX_FRAME_LENGTH(cmd, max_frame_length) \ -+ MC_RSP_OP(cmd, 0, 0, 16, uint16_t, max_frame_length) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_MTU(cmd, mtu) \ -+ MC_CMD_OP(cmd, 0, 0, 16, uint16_t, mtu) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_MTU(cmd, mtu) \ -+ MC_RSP_OP(cmd, 0, 0, 16, uint16_t, mtu) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_MULTICAST_PROMISC(cmd, en) \ -+ MC_CMD_OP(cmd, 0, 0, 1, int, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_MULTICAST_PROMISC(cmd, en) \ -+ MC_RSP_OP(cmd, 0, 0, 1, int, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_UNICAST_PROMISC(cmd, en) \ -+ MC_CMD_OP(cmd, 0, 0, 1, int, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_UNICAST_PROMISC(cmd, en) \ -+ MC_RSP_OP(cmd, 0, 0, 1, int, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_PRIMARY_MAC_ADDR(cmd, mac_addr) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 16, 8, uint8_t, mac_addr[5]); \ -+ MC_CMD_OP(cmd, 0, 24, 8, uint8_t, mac_addr[4]); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, mac_addr[3]); \ -+ MC_CMD_OP(cmd, 0, 40, 8, uint8_t, mac_addr[2]); \ -+ MC_CMD_OP(cmd, 0, 48, 8, uint8_t, mac_addr[1]); \ -+ MC_CMD_OP(cmd, 0, 56, 8, uint8_t, mac_addr[0]); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_PRIMARY_MAC_ADDR(cmd, mac_addr) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 16, 8, uint8_t, mac_addr[5]); \ -+ MC_RSP_OP(cmd, 0, 24, 8, uint8_t, mac_addr[4]); \ -+ MC_RSP_OP(cmd, 0, 32, 8, uint8_t, mac_addr[3]); \ -+ MC_RSP_OP(cmd, 0, 40, 8, uint8_t, mac_addr[2]); \ -+ MC_RSP_OP(cmd, 0, 48, 8, uint8_t, mac_addr[1]); \ -+ MC_RSP_OP(cmd, 0, 56, 8, uint8_t, mac_addr[0]); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_ADD_MAC_ADDR(cmd, mac_addr) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 16, 8, uint8_t, mac_addr[5]); \ -+ MC_CMD_OP(cmd, 0, 24, 8, uint8_t, mac_addr[4]); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, mac_addr[3]); \ -+ MC_CMD_OP(cmd, 0, 40, 8, uint8_t, mac_addr[2]); \ -+ MC_CMD_OP(cmd, 0, 48, 8, uint8_t, mac_addr[1]); \ -+ MC_CMD_OP(cmd, 0, 56, 8, uint8_t, mac_addr[0]); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_REMOVE_MAC_ADDR(cmd, mac_addr) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 16, 8, uint8_t, mac_addr[5]); \ -+ MC_CMD_OP(cmd, 0, 24, 8, uint8_t, mac_addr[4]); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, mac_addr[3]); \ -+ MC_CMD_OP(cmd, 0, 40, 8, uint8_t, mac_addr[2]); \ -+ MC_CMD_OP(cmd, 0, 48, 8, uint8_t, mac_addr[1]); \ -+ MC_CMD_OP(cmd, 0, 56, 8, uint8_t, mac_addr[0]); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_CLEAR_MAC_FILTERS(cmd, unicast, multicast) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 1, int, unicast); \ -+ MC_CMD_OP(cmd, 0, 1, 1, int, multicast); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_VLAN_FILTERS(cmd, en) \ -+ MC_CMD_OP(cmd, 0, 0, 1, int, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_ADD_VLAN_ID(cmd, vlan_id) \ -+ MC_CMD_OP(cmd, 0, 32, 16, uint16_t, vlan_id) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_REMOVE_VLAN_ID(cmd, vlan_id) \ -+ MC_CMD_OP(cmd, 0, 32, 16, uint16_t, vlan_id) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_TX_SELECTION(cmd, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 16, uint16_t, cfg->tc_sched[0].delta_bandwidth);\ -+ MC_CMD_OP(cmd, 0, 16, 4, enum dpni_tx_schedule_mode, \ -+ cfg->tc_sched[0].mode); \ -+ MC_CMD_OP(cmd, 0, 32, 16, uint16_t, cfg->tc_sched[1].delta_bandwidth);\ -+ MC_CMD_OP(cmd, 0, 48, 4, enum dpni_tx_schedule_mode, \ -+ cfg->tc_sched[1].mode); \ -+ MC_CMD_OP(cmd, 1, 0, 16, uint16_t, cfg->tc_sched[2].delta_bandwidth);\ -+ MC_CMD_OP(cmd, 1, 16, 4, enum dpni_tx_schedule_mode, \ -+ cfg->tc_sched[2].mode); \ -+ MC_CMD_OP(cmd, 1, 32, 16, uint16_t, cfg->tc_sched[3].delta_bandwidth);\ -+ MC_CMD_OP(cmd, 1, 48, 4, enum dpni_tx_schedule_mode, \ -+ cfg->tc_sched[3].mode); \ -+ MC_CMD_OP(cmd, 2, 0, 16, uint16_t, cfg->tc_sched[4].delta_bandwidth);\ -+ MC_CMD_OP(cmd, 2, 16, 4, enum dpni_tx_schedule_mode, \ -+ cfg->tc_sched[4].mode); \ -+ MC_CMD_OP(cmd, 2, 32, 16, uint16_t, cfg->tc_sched[5].delta_bandwidth);\ -+ MC_CMD_OP(cmd, 2, 48, 4, enum dpni_tx_schedule_mode, \ -+ cfg->tc_sched[5].mode); \ -+ MC_CMD_OP(cmd, 3, 0, 16, uint16_t, cfg->tc_sched[6].delta_bandwidth);\ -+ MC_CMD_OP(cmd, 3, 16, 4, enum dpni_tx_schedule_mode, \ -+ cfg->tc_sched[6].mode); \ -+ MC_CMD_OP(cmd, 3, 32, 16, uint16_t, cfg->tc_sched[7].delta_bandwidth);\ -+ MC_CMD_OP(cmd, 3, 48, 4, enum dpni_tx_schedule_mode, \ -+ cfg->tc_sched[7].mode); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_RX_TC_DIST(cmd, tc_id, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 16, uint16_t, cfg->dist_size); \ -+ MC_CMD_OP(cmd, 0, 16, 8, uint8_t, tc_id); \ -+ MC_CMD_OP(cmd, 0, 24, 4, enum dpni_dist_mode, cfg->dist_mode); \ -+ MC_CMD_OP(cmd, 0, 28, 4, enum dpni_fs_miss_action, \ -+ cfg->fs_cfg.miss_action); \ -+ MC_CMD_OP(cmd, 0, 48, 16, uint16_t, cfg->fs_cfg.default_flow_id); \ -+ MC_CMD_OP(cmd, 6, 0, 64, uint64_t, cfg->key_cfg_iova); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_TX_FLOW(cmd, flow_id, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 43, 1, int, cfg->l3_chksum_gen);\ -+ MC_CMD_OP(cmd, 0, 44, 1, int, cfg->l4_chksum_gen);\ -+ MC_CMD_OP(cmd, 0, 45, 1, int, cfg->use_common_tx_conf_queue);\ -+ MC_CMD_OP(cmd, 0, 48, 16, uint16_t, flow_id);\ -+ MC_CMD_OP(cmd, 2, 0, 32, uint32_t, cfg->options);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_SET_TX_FLOW(cmd, flow_id) \ -+ MC_RSP_OP(cmd, 0, 48, 16, uint16_t, flow_id) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_GET_TX_FLOW(cmd, flow_id) \ -+ MC_CMD_OP(cmd, 0, 48, 16, uint16_t, flow_id) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_TX_FLOW(cmd, attr) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 43, 1, int, attr->l3_chksum_gen);\ -+ MC_RSP_OP(cmd, 0, 44, 1, int, attr->l4_chksum_gen);\ -+ MC_RSP_OP(cmd, 0, 45, 1, int, attr->use_common_tx_conf_queue);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_RX_FLOW(cmd, tc_id, flow_id, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, int, cfg->dest_cfg.dest_id); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, cfg->dest_cfg.priority);\ -+ MC_CMD_OP(cmd, 0, 40, 2, enum dpni_dest, cfg->dest_cfg.dest_type);\ -+ MC_CMD_OP(cmd, 0, 42, 1, int, cfg->order_preservation_en);\ -+ MC_CMD_OP(cmd, 0, 48, 16, uint16_t, flow_id); \ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, cfg->user_ctx); \ -+ MC_CMD_OP(cmd, 2, 16, 8, uint8_t, tc_id); \ -+ MC_CMD_OP(cmd, 2, 32, 32, uint32_t, cfg->options); \ -+ MC_CMD_OP(cmd, 3, 0, 4, enum dpni_flc_type, cfg->flc_cfg.flc_type); \ -+ MC_CMD_OP(cmd, 3, 4, 4, enum dpni_stash_size, \ -+ cfg->flc_cfg.frame_data_size);\ -+ MC_CMD_OP(cmd, 3, 8, 4, enum dpni_stash_size, \ -+ cfg->flc_cfg.flow_context_size);\ -+ MC_CMD_OP(cmd, 3, 32, 32, uint32_t, cfg->flc_cfg.options);\ -+ MC_CMD_OP(cmd, 4, 0, 64, uint64_t, cfg->flc_cfg.flow_context);\ -+ MC_CMD_OP(cmd, 5, 0, 32, uint32_t, cfg->tail_drop_threshold); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_GET_RX_FLOW(cmd, tc_id, flow_id) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 16, 8, uint8_t, tc_id); \ -+ MC_CMD_OP(cmd, 0, 48, 16, uint16_t, flow_id); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_RX_FLOW(cmd, attr) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 32, int, attr->dest_cfg.dest_id); \ -+ MC_RSP_OP(cmd, 0, 32, 8, uint8_t, attr->dest_cfg.priority);\ -+ MC_RSP_OP(cmd, 0, 40, 2, enum dpni_dest, attr->dest_cfg.dest_type); \ -+ MC_RSP_OP(cmd, 0, 42, 1, int, attr->order_preservation_en);\ -+ MC_RSP_OP(cmd, 1, 0, 64, uint64_t, attr->user_ctx); \ -+ MC_RSP_OP(cmd, 2, 0, 32, uint32_t, attr->tail_drop_threshold); \ -+ MC_RSP_OP(cmd, 2, 32, 32, uint32_t, attr->fqid); \ -+ MC_RSP_OP(cmd, 3, 0, 4, enum dpni_flc_type, attr->flc_cfg.flc_type); \ -+ MC_RSP_OP(cmd, 3, 4, 4, enum dpni_stash_size, \ -+ attr->flc_cfg.frame_data_size);\ -+ MC_RSP_OP(cmd, 3, 8, 4, enum dpni_stash_size, \ -+ attr->flc_cfg.flow_context_size);\ -+ MC_RSP_OP(cmd, 3, 32, 32, uint32_t, attr->flc_cfg.options);\ -+ MC_RSP_OP(cmd, 4, 0, 64, uint64_t, attr->flc_cfg.flow_context);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_RX_ERR_QUEUE(cmd, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, int, cfg->dest_cfg.dest_id); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, cfg->dest_cfg.priority);\ -+ MC_CMD_OP(cmd, 0, 40, 2, enum dpni_dest, cfg->dest_cfg.dest_type);\ -+ MC_CMD_OP(cmd, 0, 42, 1, int, cfg->order_preservation_en);\ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, cfg->user_ctx); \ -+ MC_CMD_OP(cmd, 2, 0, 32, uint32_t, cfg->options); \ -+ MC_CMD_OP(cmd, 2, 32, 32, uint32_t, cfg->tail_drop_threshold); \ -+ MC_CMD_OP(cmd, 3, 0, 4, enum dpni_flc_type, cfg->flc_cfg.flc_type); \ -+ MC_CMD_OP(cmd, 3, 4, 4, enum dpni_stash_size, \ -+ cfg->flc_cfg.frame_data_size);\ -+ MC_CMD_OP(cmd, 3, 8, 4, enum dpni_stash_size, \ -+ cfg->flc_cfg.flow_context_size);\ -+ MC_CMD_OP(cmd, 3, 32, 32, uint32_t, cfg->flc_cfg.options);\ -+ MC_CMD_OP(cmd, 4, 0, 64, uint64_t, cfg->flc_cfg.flow_context);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_RX_ERR_QUEUE(cmd, attr) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 32, int, attr->dest_cfg.dest_id); \ -+ MC_RSP_OP(cmd, 0, 32, 8, uint8_t, attr->dest_cfg.priority);\ -+ MC_RSP_OP(cmd, 0, 40, 2, enum dpni_dest, attr->dest_cfg.dest_type);\ -+ MC_RSP_OP(cmd, 0, 42, 1, int, attr->order_preservation_en);\ -+ MC_RSP_OP(cmd, 1, 0, 64, uint64_t, attr->user_ctx); \ -+ MC_RSP_OP(cmd, 2, 0, 32, uint32_t, attr->tail_drop_threshold); \ -+ MC_RSP_OP(cmd, 2, 32, 32, uint32_t, attr->fqid); \ -+ MC_RSP_OP(cmd, 3, 0, 4, enum dpni_flc_type, attr->flc_cfg.flc_type); \ -+ MC_RSP_OP(cmd, 3, 4, 4, enum dpni_stash_size, \ -+ attr->flc_cfg.frame_data_size);\ -+ MC_RSP_OP(cmd, 3, 8, 4, enum dpni_stash_size, \ -+ attr->flc_cfg.flow_context_size);\ -+ MC_RSP_OP(cmd, 3, 32, 32, uint32_t, attr->flc_cfg.options);\ -+ MC_RSP_OP(cmd, 4, 0, 64, uint64_t, attr->flc_cfg.flow_context);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_TX_CONF_REVOKE(cmd, revoke) \ -+ MC_CMD_OP(cmd, 0, 0, 1, int, revoke) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_QOS_TABLE(cmd, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, cfg->default_tc); \ -+ MC_CMD_OP(cmd, 0, 40, 1, int, cfg->discard_on_miss); \ -+ MC_CMD_OP(cmd, 6, 0, 64, uint64_t, cfg->key_cfg_iova); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_ADD_QOS_ENTRY(cmd, cfg, tc_id) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 16, 8, uint8_t, tc_id); \ -+ MC_CMD_OP(cmd, 0, 24, 8, uint8_t, cfg->key_size); \ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, cfg->key_iova); \ -+ MC_CMD_OP(cmd, 2, 0, 64, uint64_t, cfg->mask_iova); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_REMOVE_QOS_ENTRY(cmd, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 24, 8, uint8_t, cfg->key_size); \ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, cfg->key_iova); \ -+ MC_CMD_OP(cmd, 2, 0, 64, uint64_t, cfg->mask_iova); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_ADD_FS_ENTRY(cmd, tc_id, cfg, flow_id) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 16, 8, uint8_t, tc_id); \ -+ MC_CMD_OP(cmd, 0, 48, 16, uint16_t, flow_id); \ -+ MC_CMD_OP(cmd, 0, 24, 8, uint8_t, cfg->key_size); \ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, cfg->key_iova); \ -+ MC_CMD_OP(cmd, 2, 0, 64, uint64_t, cfg->mask_iova); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_REMOVE_FS_ENTRY(cmd, tc_id, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 16, 8, uint8_t, tc_id); \ -+ MC_CMD_OP(cmd, 0, 24, 8, uint8_t, cfg->key_size); \ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, cfg->key_iova); \ -+ MC_CMD_OP(cmd, 2, 0, 64, uint64_t, cfg->mask_iova); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_CLEAR_FS_ENTRIES(cmd, tc_id) \ -+ MC_CMD_OP(cmd, 0, 16, 8, uint8_t, tc_id) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_VLAN_INSERTION(cmd, en) \ -+ MC_CMD_OP(cmd, 0, 0, 1, int, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_VLAN_REMOVAL(cmd, en) \ -+ MC_CMD_OP(cmd, 0, 0, 1, int, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_IPR(cmd, en) \ -+ MC_CMD_OP(cmd, 0, 0, 1, int, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_IPF(cmd, en) \ -+ MC_CMD_OP(cmd, 0, 0, 1, int, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_RX_TC_POLICING(cmd, tc_id, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 4, enum dpni_policer_mode, cfg->mode); \ -+ MC_CMD_OP(cmd, 0, 4, 4, enum dpni_policer_color, cfg->default_color); \ -+ MC_CMD_OP(cmd, 0, 8, 4, enum dpni_policer_unit, cfg->units); \ -+ MC_CMD_OP(cmd, 0, 16, 8, uint8_t, tc_id); \ -+ MC_CMD_OP(cmd, 0, 32, 32, uint32_t, cfg->options); \ -+ MC_CMD_OP(cmd, 1, 0, 32, uint32_t, cfg->cir); \ -+ MC_CMD_OP(cmd, 1, 32, 32, uint32_t, cfg->cbs); \ -+ MC_CMD_OP(cmd, 2, 0, 32, uint32_t, cfg->eir); \ -+ MC_CMD_OP(cmd, 2, 32, 32, uint32_t, cfg->ebs);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_GET_RX_TC_POLICING(cmd, tc_id) \ -+ MC_CMD_OP(cmd, 0, 16, 8, uint8_t, tc_id) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_RSP_GET_RX_TC_POLICING(cmd, cfg) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 4, enum dpni_policer_mode, cfg->mode); \ -+ MC_RSP_OP(cmd, 0, 4, 4, enum dpni_policer_color, cfg->default_color); \ -+ MC_RSP_OP(cmd, 0, 8, 4, enum dpni_policer_unit, cfg->units); \ -+ MC_RSP_OP(cmd, 0, 32, 32, uint32_t, cfg->options); \ -+ MC_RSP_OP(cmd, 1, 0, 32, uint32_t, cfg->cir); \ -+ MC_RSP_OP(cmd, 1, 32, 32, uint32_t, cfg->cbs); \ -+ MC_RSP_OP(cmd, 2, 0, 32, uint32_t, cfg->eir); \ -+ MC_RSP_OP(cmd, 2, 32, 32, uint32_t, cfg->ebs);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_PREP_EARLY_DROP(ext, cfg) \ -+do { \ -+ MC_PREP_OP(ext, 0, 0, 2, enum dpni_early_drop_mode, cfg->mode); \ -+ MC_PREP_OP(ext, 0, 2, 2, \ -+ enum dpni_congestion_unit, cfg->units); \ -+ MC_PREP_OP(ext, 0, 32, 32, uint32_t, cfg->tail_drop_threshold); \ -+ MC_PREP_OP(ext, 1, 0, 8, uint8_t, cfg->green.drop_probability); \ -+ MC_PREP_OP(ext, 2, 0, 64, uint64_t, cfg->green.max_threshold); \ -+ MC_PREP_OP(ext, 3, 0, 64, uint64_t, cfg->green.min_threshold); \ -+ MC_PREP_OP(ext, 5, 0, 8, uint8_t, cfg->yellow.drop_probability);\ -+ MC_PREP_OP(ext, 6, 0, 64, uint64_t, cfg->yellow.max_threshold); \ -+ MC_PREP_OP(ext, 7, 0, 64, uint64_t, cfg->yellow.min_threshold); \ -+ MC_PREP_OP(ext, 9, 0, 8, uint8_t, cfg->red.drop_probability); \ -+ MC_PREP_OP(ext, 10, 0, 64, uint64_t, cfg->red.max_threshold); \ -+ MC_PREP_OP(ext, 11, 0, 64, uint64_t, cfg->red.min_threshold); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_EXT_EARLY_DROP(ext, cfg) \ -+do { \ -+ MC_EXT_OP(ext, 0, 0, 2, enum dpni_early_drop_mode, cfg->mode); \ -+ MC_EXT_OP(ext, 0, 2, 2, \ -+ enum dpni_congestion_unit, cfg->units); \ -+ MC_EXT_OP(ext, 0, 32, 32, uint32_t, cfg->tail_drop_threshold); \ -+ MC_EXT_OP(ext, 1, 0, 8, uint8_t, cfg->green.drop_probability); \ -+ MC_EXT_OP(ext, 2, 0, 64, uint64_t, cfg->green.max_threshold); \ -+ MC_EXT_OP(ext, 3, 0, 64, uint64_t, cfg->green.min_threshold); \ -+ MC_EXT_OP(ext, 5, 0, 8, uint8_t, cfg->yellow.drop_probability);\ -+ MC_EXT_OP(ext, 6, 0, 64, uint64_t, cfg->yellow.max_threshold); \ -+ MC_EXT_OP(ext, 7, 0, 64, uint64_t, cfg->yellow.min_threshold); \ -+ MC_EXT_OP(ext, 9, 0, 8, uint8_t, cfg->red.drop_probability); \ -+ MC_EXT_OP(ext, 10, 0, 64, uint64_t, cfg->red.max_threshold); \ -+ MC_EXT_OP(ext, 11, 0, 64, uint64_t, cfg->red.min_threshold); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_RX_TC_EARLY_DROP(cmd, tc_id, early_drop_iova) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 8, 8, uint8_t, tc_id); \ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, early_drop_iova); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_GET_RX_TC_EARLY_DROP(cmd, tc_id, early_drop_iova) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 8, 8, uint8_t, tc_id); \ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, early_drop_iova); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_SET_TX_TC_EARLY_DROP(cmd, tc_id, early_drop_iova) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 8, 8, uint8_t, tc_id); \ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, early_drop_iova); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPNI_CMD_GET_TX_TC_EARLY_DROP(cmd, tc_id, early_drop_iova) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 8, 8, uint8_t, tc_id); \ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, early_drop_iova); \ -+} while (0) -+ -+#define DPNI_CMD_SET_RX_TC_CONGESTION_NOTIFICATION(cmd, tc_id, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 2, enum dpni_congestion_unit, cfg->units); \ -+ MC_CMD_OP(cmd, 0, 4, 4, enum dpni_dest, cfg->dest_cfg.dest_type); \ -+ MC_CMD_OP(cmd, 0, 8, 8, uint8_t, tc_id); \ -+ MC_CMD_OP(cmd, 0, 16, 8, uint8_t, cfg->dest_cfg.priority); \ -+ MC_CMD_OP(cmd, 1, 0, 32, uint32_t, cfg->threshold_entry); \ -+ MC_CMD_OP(cmd, 1, 32, 32, uint32_t, cfg->threshold_exit); \ -+ MC_CMD_OP(cmd, 2, 0, 16, uint16_t, cfg->options); \ -+ MC_CMD_OP(cmd, 2, 32, 32, int, cfg->dest_cfg.dest_id); \ -+ MC_CMD_OP(cmd, 3, 0, 64, uint64_t, cfg->message_ctx); \ -+ MC_CMD_OP(cmd, 4, 0, 64, uint64_t, cfg->message_iova); \ -+} while (0) -+ -+#define DPNI_CMD_GET_RX_TC_CONGESTION_NOTIFICATION(cmd, tc_id) \ -+ MC_CMD_OP(cmd, 0, 8, 8, uint8_t, tc_id) -+ -+#define DPNI_RSP_GET_RX_TC_CONGESTION_NOTIFICATION(cmd, cfg) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 2, enum dpni_congestion_unit, cfg->units); \ -+ MC_RSP_OP(cmd, 0, 4, 4, enum dpni_dest, cfg->dest_cfg.dest_type); \ -+ MC_RSP_OP(cmd, 0, 16, 8, uint8_t, cfg->dest_cfg.priority); \ -+ MC_RSP_OP(cmd, 1, 0, 32, uint32_t, cfg->threshold_entry); \ -+ MC_RSP_OP(cmd, 1, 32, 32, uint32_t, cfg->threshold_exit); \ -+ MC_RSP_OP(cmd, 2, 0, 16, uint16_t, cfg->options); \ -+ MC_RSP_OP(cmd, 2, 32, 32, int, cfg->dest_cfg.dest_id); \ -+ MC_RSP_OP(cmd, 3, 0, 64, uint64_t, cfg->message_ctx); \ -+ MC_RSP_OP(cmd, 4, 0, 64, uint64_t, cfg->message_iova); \ -+} while (0) -+ -+#define DPNI_CMD_SET_TX_TC_CONGESTION_NOTIFICATION(cmd, tc_id, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 2, enum dpni_congestion_unit, cfg->units); \ -+ MC_CMD_OP(cmd, 0, 4, 4, enum dpni_dest, cfg->dest_cfg.dest_type); \ -+ MC_CMD_OP(cmd, 0, 8, 8, uint8_t, tc_id); \ -+ MC_CMD_OP(cmd, 0, 16, 8, uint8_t, cfg->dest_cfg.priority); \ -+ MC_CMD_OP(cmd, 1, 0, 32, uint32_t, cfg->threshold_entry); \ -+ MC_CMD_OP(cmd, 1, 32, 32, uint32_t, cfg->threshold_exit); \ -+ MC_CMD_OP(cmd, 2, 0, 16, uint16_t, cfg->options); \ -+ MC_CMD_OP(cmd, 2, 32, 32, int, cfg->dest_cfg.dest_id); \ -+ MC_CMD_OP(cmd, 3, 0, 64, uint64_t, cfg->message_ctx); \ -+ MC_CMD_OP(cmd, 4, 0, 64, uint64_t, cfg->message_iova); \ -+} while (0) -+ -+#define DPNI_CMD_GET_TX_TC_CONGESTION_NOTIFICATION(cmd, tc_id) \ -+ MC_CMD_OP(cmd, 0, 8, 8, uint8_t, tc_id) -+ -+#define DPNI_RSP_GET_TX_TC_CONGESTION_NOTIFICATION(cmd, cfg) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 2, enum dpni_congestion_unit, cfg->units); \ -+ MC_RSP_OP(cmd, 0, 4, 4, enum dpni_dest, cfg->dest_cfg.dest_type); \ -+ MC_RSP_OP(cmd, 0, 16, 8, uint8_t, cfg->dest_cfg.priority); \ -+ MC_RSP_OP(cmd, 1, 0, 32, uint32_t, cfg->threshold_entry); \ -+ MC_RSP_OP(cmd, 1, 32, 32, uint32_t, cfg->threshold_exit); \ -+ MC_RSP_OP(cmd, 2, 0, 16, uint16_t, cfg->options); \ -+ MC_RSP_OP(cmd, 2, 32, 32, int, cfg->dest_cfg.dest_id); \ -+ MC_RSP_OP(cmd, 3, 0, 64, uint64_t, cfg->message_ctx); \ -+ MC_RSP_OP(cmd, 4, 0, 64, uint64_t, cfg->message_iova); \ -+} while (0) -+ -+#define DPNI_CMD_SET_TX_CONF(cmd, flow_id, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, cfg->queue_cfg.dest_cfg.priority); \ -+ MC_CMD_OP(cmd, 0, 40, 2, enum dpni_dest, \ -+ cfg->queue_cfg.dest_cfg.dest_type); \ -+ MC_CMD_OP(cmd, 0, 42, 1, int, cfg->errors_only); \ -+ MC_CMD_OP(cmd, 0, 46, 1, int, cfg->queue_cfg.order_preservation_en); \ -+ MC_CMD_OP(cmd, 0, 48, 16, uint16_t, flow_id); \ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, cfg->queue_cfg.user_ctx); \ -+ MC_CMD_OP(cmd, 2, 0, 32, uint32_t, cfg->queue_cfg.options); \ -+ MC_CMD_OP(cmd, 2, 32, 32, int, cfg->queue_cfg.dest_cfg.dest_id); \ -+ MC_CMD_OP(cmd, 3, 0, 32, uint32_t, \ -+ cfg->queue_cfg.tail_drop_threshold); \ -+ MC_CMD_OP(cmd, 4, 0, 4, enum dpni_flc_type, \ -+ cfg->queue_cfg.flc_cfg.flc_type); \ -+ MC_CMD_OP(cmd, 4, 4, 4, enum dpni_stash_size, \ -+ cfg->queue_cfg.flc_cfg.frame_data_size); \ -+ MC_CMD_OP(cmd, 4, 8, 4, enum dpni_stash_size, \ -+ cfg->queue_cfg.flc_cfg.flow_context_size); \ -+ MC_CMD_OP(cmd, 4, 32, 32, uint32_t, cfg->queue_cfg.flc_cfg.options); \ -+ MC_CMD_OP(cmd, 5, 0, 64, uint64_t, \ -+ cfg->queue_cfg.flc_cfg.flow_context); \ -+} while (0) -+ -+#define DPNI_CMD_GET_TX_CONF(cmd, flow_id) \ -+ MC_CMD_OP(cmd, 0, 48, 16, uint16_t, flow_id) -+ -+#define DPNI_RSP_GET_TX_CONF(cmd, attr) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 32, 8, uint8_t, \ -+ attr->queue_attr.dest_cfg.priority); \ -+ MC_RSP_OP(cmd, 0, 40, 2, enum dpni_dest, \ -+ attr->queue_attr.dest_cfg.dest_type); \ -+ MC_RSP_OP(cmd, 0, 42, 1, int, attr->errors_only); \ -+ MC_RSP_OP(cmd, 0, 46, 1, int, \ -+ attr->queue_attr.order_preservation_en); \ -+ MC_RSP_OP(cmd, 1, 0, 64, uint64_t, attr->queue_attr.user_ctx); \ -+ MC_RSP_OP(cmd, 2, 32, 32, int, attr->queue_attr.dest_cfg.dest_id); \ -+ MC_RSP_OP(cmd, 3, 0, 32, uint32_t, \ -+ attr->queue_attr.tail_drop_threshold); \ -+ MC_RSP_OP(cmd, 3, 32, 32, uint32_t, attr->queue_attr.fqid); \ -+ MC_RSP_OP(cmd, 4, 0, 4, enum dpni_flc_type, \ -+ attr->queue_attr.flc_cfg.flc_type); \ -+ MC_RSP_OP(cmd, 4, 4, 4, enum dpni_stash_size, \ -+ attr->queue_attr.flc_cfg.frame_data_size); \ -+ MC_RSP_OP(cmd, 4, 8, 4, enum dpni_stash_size, \ -+ attr->queue_attr.flc_cfg.flow_context_size); \ -+ MC_RSP_OP(cmd, 4, 32, 32, uint32_t, attr->queue_attr.flc_cfg.options); \ -+ MC_RSP_OP(cmd, 5, 0, 64, uint64_t, \ -+ attr->queue_attr.flc_cfg.flow_context); \ -+} while (0) -+ -+#define DPNI_CMD_SET_TX_CONF_CONGESTION_NOTIFICATION(cmd, flow_id, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 2, enum dpni_congestion_unit, cfg->units); \ -+ MC_CMD_OP(cmd, 0, 4, 4, enum dpni_dest, cfg->dest_cfg.dest_type); \ -+ MC_CMD_OP(cmd, 0, 16, 8, uint8_t, cfg->dest_cfg.priority); \ -+ MC_CMD_OP(cmd, 0, 48, 16, uint16_t, flow_id); \ -+ MC_CMD_OP(cmd, 1, 0, 32, uint32_t, cfg->threshold_entry); \ -+ MC_CMD_OP(cmd, 1, 32, 32, uint32_t, cfg->threshold_exit); \ -+ MC_CMD_OP(cmd, 2, 0, 16, uint16_t, cfg->options); \ -+ MC_CMD_OP(cmd, 2, 32, 32, int, cfg->dest_cfg.dest_id); \ -+ MC_CMD_OP(cmd, 3, 0, 64, uint64_t, cfg->message_ctx); \ -+ MC_CMD_OP(cmd, 4, 0, 64, uint64_t, cfg->message_iova); \ -+} while (0) -+ -+#define DPNI_CMD_GET_TX_CONF_CONGESTION_NOTIFICATION(cmd, flow_id) \ -+ MC_CMD_OP(cmd, 0, 48, 16, uint16_t, flow_id) -+ -+#define DPNI_RSP_GET_TX_CONF_CONGESTION_NOTIFICATION(cmd, cfg) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 2, enum dpni_congestion_unit, cfg->units); \ -+ MC_RSP_OP(cmd, 0, 4, 4, enum dpni_dest, cfg->dest_cfg.dest_type); \ -+ MC_RSP_OP(cmd, 0, 16, 8, uint8_t, cfg->dest_cfg.priority); \ -+ MC_RSP_OP(cmd, 1, 0, 32, uint32_t, cfg->threshold_entry); \ -+ MC_RSP_OP(cmd, 1, 32, 32, uint32_t, cfg->threshold_exit); \ -+ MC_RSP_OP(cmd, 2, 0, 16, uint16_t, cfg->options); \ -+ MC_RSP_OP(cmd, 2, 32, 32, int, cfg->dest_cfg.dest_id); \ -+ MC_RSP_OP(cmd, 3, 0, 64, uint64_t, cfg->message_ctx); \ -+ MC_RSP_OP(cmd, 4, 0, 64, uint64_t, cfg->message_iova); \ -+} while (0) -+ -+#endif /* _FSL_DPNI_CMD_H */ -diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpni.c b/drivers/staging/fsl-dpaa2/ethernet/dpni.c -new file mode 100644 -index 0000000..c228ce5 ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/ethernet/dpni.c -@@ -0,0 +1,1907 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#include "../../fsl-mc/include/mc-sys.h" -+#include "../../fsl-mc/include/mc-cmd.h" -+#include "dpni.h" -+#include "dpni-cmd.h" -+ -+int dpni_prepare_key_cfg(const struct dpkg_profile_cfg *cfg, -+ uint8_t *key_cfg_buf) -+{ -+ int i, j; -+ int offset = 0; -+ int param = 1; -+ uint64_t *params = (uint64_t *)key_cfg_buf; -+ -+ if (!key_cfg_buf || !cfg) -+ return -EINVAL; -+ -+ params[0] |= mc_enc(0, 8, cfg->num_extracts); -+ params[0] = cpu_to_le64(params[0]); -+ -+ if (cfg->num_extracts >= DPKG_MAX_NUM_OF_EXTRACTS) -+ return -EINVAL; -+ -+ for (i = 0; i < cfg->num_extracts; i++) { -+ switch (cfg->extracts[i].type) { -+ case DPKG_EXTRACT_FROM_HDR: -+ params[param] |= mc_enc(0, 8, -+ cfg->extracts[i].extract.from_hdr.prot); -+ params[param] |= mc_enc(8, 4, -+ cfg->extracts[i].extract.from_hdr.type); -+ params[param] |= mc_enc(16, 8, -+ cfg->extracts[i].extract.from_hdr.size); -+ params[param] |= mc_enc(24, 8, -+ cfg->extracts[i].extract. -+ from_hdr.offset); -+ params[param] |= mc_enc(32, 32, -+ cfg->extracts[i].extract. -+ from_hdr.field); -+ params[param] = cpu_to_le64(params[param]); -+ param++; -+ params[param] |= mc_enc(0, 8, -+ cfg->extracts[i].extract. -+ from_hdr.hdr_index); -+ break; -+ case DPKG_EXTRACT_FROM_DATA: -+ params[param] |= mc_enc(16, 8, -+ cfg->extracts[i].extract. -+ from_data.size); -+ params[param] |= mc_enc(24, 8, -+ cfg->extracts[i].extract. -+ from_data.offset); -+ params[param] = cpu_to_le64(params[param]); -+ param++; -+ break; -+ case DPKG_EXTRACT_FROM_PARSE: -+ params[param] |= mc_enc(16, 8, -+ cfg->extracts[i].extract. -+ from_parse.size); -+ params[param] |= mc_enc(24, 8, -+ cfg->extracts[i].extract. -+ from_parse.offset); -+ params[param] = cpu_to_le64(params[param]); -+ param++; -+ break; -+ default: -+ return -EINVAL; -+ } -+ params[param] |= mc_enc( -+ 24, 8, cfg->extracts[i].num_of_byte_masks); -+ params[param] |= mc_enc(32, 4, cfg->extracts[i].type); -+ params[param] = cpu_to_le64(params[param]); -+ param++; -+ for (offset = 0, j = 0; -+ j < DPKG_NUM_OF_MASKS; -+ offset += 16, j++) { -+ params[param] |= mc_enc( -+ (offset), 8, cfg->extracts[i].masks[j].mask); -+ params[param] |= mc_enc( -+ (offset + 8), 8, -+ cfg->extracts[i].masks[j].offset); -+ } -+ params[param] = cpu_to_le64(params[param]); -+ param++; -+ } -+ return 0; -+} -+ -+int dpni_prepare_extended_cfg(const struct dpni_extended_cfg *cfg, -+ uint8_t *ext_cfg_buf) -+{ -+ uint64_t *ext_params = (uint64_t *)ext_cfg_buf; -+ -+ DPNI_PREP_EXTENDED_CFG(ext_params, cfg); -+ -+ return 0; -+} -+ -+int dpni_extract_extended_cfg(struct dpni_extended_cfg *cfg, -+ const uint8_t *ext_cfg_buf) -+{ -+ const uint64_t *ext_params = (const uint64_t *)ext_cfg_buf; -+ -+ DPNI_EXT_EXTENDED_CFG(ext_params, cfg); -+ -+ return 0; -+} -+ -+int dpni_open(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ int dpni_id, -+ uint16_t *token) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_OPEN, -+ cmd_flags, -+ 0); -+ DPNI_CMD_OPEN(cmd, dpni_id); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *token = MC_CMD_HDR_READ_TOKEN(cmd.header); -+ -+ return 0; -+} -+ -+int dpni_close(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_CLOSE, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_create(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ const struct dpni_cfg *cfg, -+ uint16_t *token) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_CREATE, -+ cmd_flags, -+ 0); -+ DPNI_CMD_CREATE(cmd, cfg); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *token = MC_CMD_HDR_READ_TOKEN(cmd.header); -+ -+ return 0; -+} -+ -+int dpni_destroy(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_DESTROY, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_set_pools(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_pools_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_POOLS, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_POOLS(cmd, cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_ENABLE, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_disable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_DISABLE, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_is_enabled(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *en) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_IS_ENABLED, cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_IS_ENABLED(cmd, *en); -+ -+ return 0; -+} -+ -+int dpni_reset(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_RESET, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_set_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ struct dpni_irq_cfg *irq_cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_IRQ, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_IRQ(cmd, irq_index, irq_cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ int *type, -+ struct dpni_irq_cfg *irq_cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_IRQ, -+ cmd_flags, -+ token); -+ DPNI_CMD_GET_IRQ(cmd, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_IRQ(cmd, *type, irq_cfg); -+ -+ return 0; -+} -+ -+int dpni_set_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t en) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_IRQ_ENABLE, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_IRQ_ENABLE(cmd, irq_index, en); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t *en) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_IRQ_ENABLE, -+ cmd_flags, -+ token); -+ DPNI_CMD_GET_IRQ_ENABLE(cmd, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_IRQ_ENABLE(cmd, *en); -+ -+ return 0; -+} -+ -+int dpni_set_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t mask) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_IRQ_MASK, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_IRQ_MASK(cmd, irq_index, mask); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *mask) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_IRQ_MASK, -+ cmd_flags, -+ token); -+ DPNI_CMD_GET_IRQ_MASK(cmd, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_IRQ_MASK(cmd, *mask); -+ -+ return 0; -+} -+ -+int dpni_get_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *status) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_IRQ_STATUS, -+ cmd_flags, -+ token); -+ DPNI_CMD_GET_IRQ_STATUS(cmd, irq_index, *status); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_IRQ_STATUS(cmd, *status); -+ -+ return 0; -+} -+ -+int dpni_clear_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t status) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_CLEAR_IRQ_STATUS, -+ cmd_flags, -+ token); -+ DPNI_CMD_CLEAR_IRQ_STATUS(cmd, irq_index, status); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_attributes(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpni_attr *attr) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_ATTR, -+ cmd_flags, -+ token); -+ DPNI_CMD_GET_ATTR(cmd, attr); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_ATTR(cmd, attr); -+ -+ return 0; -+} -+ -+int dpni_set_errors_behavior(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpni_error_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_ERRORS_BEHAVIOR, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_ERRORS_BEHAVIOR(cmd, cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_rx_buffer_layout(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpni_buffer_layout *layout) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_RX_BUFFER_LAYOUT, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_RX_BUFFER_LAYOUT(cmd, layout); -+ -+ return 0; -+} -+ -+int dpni_set_rx_buffer_layout(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_buffer_layout *layout) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_RX_BUFFER_LAYOUT, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_RX_BUFFER_LAYOUT(cmd, layout); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_tx_buffer_layout(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpni_buffer_layout *layout) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_TX_BUFFER_LAYOUT, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_TX_BUFFER_LAYOUT(cmd, layout); -+ -+ return 0; -+} -+ -+int dpni_set_tx_buffer_layout(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_buffer_layout *layout) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_TX_BUFFER_LAYOUT, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_TX_BUFFER_LAYOUT(cmd, layout); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_tx_conf_buffer_layout(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpni_buffer_layout *layout) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_TX_CONF_BUFFER_LAYOUT, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_TX_CONF_BUFFER_LAYOUT(cmd, layout); -+ -+ return 0; -+} -+ -+int dpni_set_tx_conf_buffer_layout(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_buffer_layout *layout) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_TX_CONF_BUFFER_LAYOUT, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_TX_CONF_BUFFER_LAYOUT(cmd, layout); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_l3_chksum_validation(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *en) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_L3_CHKSUM_VALIDATION, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_L3_CHKSUM_VALIDATION(cmd, *en); -+ -+ return 0; -+} -+ -+int dpni_set_l3_chksum_validation(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_L3_CHKSUM_VALIDATION, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_L3_CHKSUM_VALIDATION(cmd, en); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_l4_chksum_validation(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *en) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_L4_CHKSUM_VALIDATION, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_L4_CHKSUM_VALIDATION(cmd, *en); -+ -+ return 0; -+} -+ -+int dpni_set_l4_chksum_validation(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_L4_CHKSUM_VALIDATION, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_L4_CHKSUM_VALIDATION(cmd, en); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_qdid(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t *qdid) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_QDID, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_QDID(cmd, *qdid); -+ -+ return 0; -+} -+ -+int dpni_get_sp_info(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpni_sp_info *sp_info) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_SP_INFO, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_SP_INFO(cmd, sp_info); -+ -+ return 0; -+} -+ -+int dpni_get_tx_data_offset(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t *data_offset) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_TX_DATA_OFFSET, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_TX_DATA_OFFSET(cmd, *data_offset); -+ -+ return 0; -+} -+ -+int dpni_get_counter(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ enum dpni_counter counter, -+ uint64_t *value) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_COUNTER, -+ cmd_flags, -+ token); -+ DPNI_CMD_GET_COUNTER(cmd, counter); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_COUNTER(cmd, *value); -+ -+ return 0; -+} -+ -+int dpni_set_counter(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ enum dpni_counter counter, -+ uint64_t value) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_COUNTER, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_COUNTER(cmd, counter, value); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_set_link_cfg(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_link_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_LINK_CFG, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_LINK_CFG(cmd, cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_link_state(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpni_link_state *state) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_LINK_STATE, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_LINK_STATE(cmd, state); -+ -+ return 0; -+} -+ -+int dpni_set_tx_shaping(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_tx_shaping_cfg *tx_shaper) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_TX_SHAPING, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_TX_SHAPING(cmd, tx_shaper); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_set_max_frame_length(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t max_frame_length) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_MAX_FRAME_LENGTH, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_MAX_FRAME_LENGTH(cmd, max_frame_length); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_max_frame_length(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t *max_frame_length) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_MAX_FRAME_LENGTH, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_MAX_FRAME_LENGTH(cmd, *max_frame_length); -+ -+ return 0; -+} -+ -+int dpni_set_mtu(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t mtu) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_MTU, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_MTU(cmd, mtu); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_mtu(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t *mtu) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_MTU, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_MTU(cmd, *mtu); -+ -+ return 0; -+} -+ -+int dpni_set_multicast_promisc(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_MCAST_PROMISC, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_MULTICAST_PROMISC(cmd, en); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_multicast_promisc(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *en) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_MCAST_PROMISC, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_MULTICAST_PROMISC(cmd, *en); -+ -+ return 0; -+} -+ -+int dpni_set_unicast_promisc(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_UNICAST_PROMISC, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_UNICAST_PROMISC(cmd, en); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_unicast_promisc(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *en) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_UNICAST_PROMISC, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_UNICAST_PROMISC(cmd, *en); -+ -+ return 0; -+} -+ -+int dpni_set_primary_mac_addr(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const uint8_t mac_addr[6]) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_PRIM_MAC, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_PRIMARY_MAC_ADDR(cmd, mac_addr); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_primary_mac_addr(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t mac_addr[6]) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_PRIM_MAC, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_PRIMARY_MAC_ADDR(cmd, mac_addr); -+ -+ return 0; -+} -+ -+int dpni_add_mac_addr(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const uint8_t mac_addr[6]) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_ADD_MAC_ADDR, -+ cmd_flags, -+ token); -+ DPNI_CMD_ADD_MAC_ADDR(cmd, mac_addr); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_remove_mac_addr(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const uint8_t mac_addr[6]) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_REMOVE_MAC_ADDR, -+ cmd_flags, -+ token); -+ DPNI_CMD_REMOVE_MAC_ADDR(cmd, mac_addr); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_clear_mac_filters(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int unicast, -+ int multicast) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_CLR_MAC_FILTERS, -+ cmd_flags, -+ token); -+ DPNI_CMD_CLEAR_MAC_FILTERS(cmd, unicast, multicast); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_set_vlan_filters(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_VLAN_FILTERS, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_VLAN_FILTERS(cmd, en); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_add_vlan_id(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t vlan_id) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_ADD_VLAN_ID, -+ cmd_flags, -+ token); -+ DPNI_CMD_ADD_VLAN_ID(cmd, vlan_id); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_remove_vlan_id(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t vlan_id) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_REMOVE_VLAN_ID, -+ cmd_flags, -+ token); -+ DPNI_CMD_REMOVE_VLAN_ID(cmd, vlan_id); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_clear_vlan_filters(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_CLR_VLAN_FILTERS, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_set_tx_selection(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_tx_selection_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_TX_SELECTION, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_TX_SELECTION(cmd, cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_set_rx_tc_dist(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ const struct dpni_rx_tc_dist_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_RX_TC_DIST, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_RX_TC_DIST(cmd, tc_id, cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_set_tx_flow(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t *flow_id, -+ const struct dpni_tx_flow_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_TX_FLOW, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_TX_FLOW(cmd, *flow_id, cfg); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_SET_TX_FLOW(cmd, *flow_id); -+ -+ return 0; -+} -+ -+int dpni_get_tx_flow(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t flow_id, -+ struct dpni_tx_flow_attr *attr) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_TX_FLOW, -+ cmd_flags, -+ token); -+ DPNI_CMD_GET_TX_FLOW(cmd, flow_id); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_TX_FLOW(cmd, attr); -+ -+ return 0; -+} -+ -+int dpni_set_rx_flow(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ uint16_t flow_id, -+ const struct dpni_queue_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_RX_FLOW, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_RX_FLOW(cmd, tc_id, flow_id, cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_rx_flow(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ uint16_t flow_id, -+ struct dpni_queue_attr *attr) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_RX_FLOW, -+ cmd_flags, -+ token); -+ DPNI_CMD_GET_RX_FLOW(cmd, tc_id, flow_id); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_RX_FLOW(cmd, attr); -+ -+ return 0; -+} -+ -+int dpni_set_rx_err_queue(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_queue_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_RX_ERR_QUEUE, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_RX_ERR_QUEUE(cmd, cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_rx_err_queue(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpni_queue_attr *attr) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_RX_ERR_QUEUE, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPNI_RSP_GET_RX_ERR_QUEUE(cmd, attr); -+ -+ return 0; -+} -+ -+int dpni_set_tx_conf_revoke(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int revoke) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_TX_CONF_REVOKE, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_TX_CONF_REVOKE(cmd, revoke); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_set_qos_table(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_qos_tbl_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_QOS_TBL, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_QOS_TABLE(cmd, cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_add_qos_entry(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_rule_cfg *cfg, -+ uint8_t tc_id) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_ADD_QOS_ENT, -+ cmd_flags, -+ token); -+ DPNI_CMD_ADD_QOS_ENTRY(cmd, cfg, tc_id); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_remove_qos_entry(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_rule_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_REMOVE_QOS_ENT, -+ cmd_flags, -+ token); -+ DPNI_CMD_REMOVE_QOS_ENTRY(cmd, cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_clear_qos_table(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_CLR_QOS_TBL, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_add_fs_entry(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ const struct dpni_rule_cfg *cfg, -+ uint16_t flow_id) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_ADD_FS_ENT, -+ cmd_flags, -+ token); -+ DPNI_CMD_ADD_FS_ENTRY(cmd, tc_id, cfg, flow_id); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_remove_fs_entry(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ const struct dpni_rule_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_REMOVE_FS_ENT, -+ cmd_flags, -+ token); -+ DPNI_CMD_REMOVE_FS_ENTRY(cmd, tc_id, cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_clear_fs_entries(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_CLR_FS_ENT, -+ cmd_flags, -+ token); -+ DPNI_CMD_CLEAR_FS_ENTRIES(cmd, tc_id); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_set_vlan_insertion(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_VLAN_INSERTION, -+ cmd_flags, token); -+ DPNI_CMD_SET_VLAN_INSERTION(cmd, en); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_set_vlan_removal(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_VLAN_REMOVAL, -+ cmd_flags, token); -+ DPNI_CMD_SET_VLAN_REMOVAL(cmd, en); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_set_ipr(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_IPR, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_IPR(cmd, en); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_set_ipf(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_IPF, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_IPF(cmd, en); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_set_rx_tc_policing(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ const struct dpni_rx_tc_policing_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_RX_TC_POLICING, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_RX_TC_POLICING(cmd, tc_id, cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_rx_tc_policing(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ struct dpni_rx_tc_policing_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_RX_TC_POLICING, -+ cmd_flags, -+ token); -+ DPNI_CMD_GET_RX_TC_POLICING(cmd, tc_id); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ DPNI_RSP_GET_RX_TC_POLICING(cmd, cfg); -+ -+ return 0; -+} -+ -+void dpni_prepare_early_drop(const struct dpni_early_drop_cfg *cfg, -+ uint8_t *early_drop_buf) -+{ -+ uint64_t *ext_params = (uint64_t *)early_drop_buf; -+ -+ DPNI_PREP_EARLY_DROP(ext_params, cfg); -+} -+ -+void dpni_extract_early_drop(struct dpni_early_drop_cfg *cfg, -+ const uint8_t *early_drop_buf) -+{ -+ const uint64_t *ext_params = (const uint64_t *)early_drop_buf; -+ -+ DPNI_EXT_EARLY_DROP(ext_params, cfg); -+} -+ -+int dpni_set_rx_tc_early_drop(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ uint64_t early_drop_iova) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_RX_TC_EARLY_DROP, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_RX_TC_EARLY_DROP(cmd, tc_id, early_drop_iova); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_rx_tc_early_drop(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ uint64_t early_drop_iova) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_RX_TC_EARLY_DROP, -+ cmd_flags, -+ token); -+ DPNI_CMD_GET_RX_TC_EARLY_DROP(cmd, tc_id, early_drop_iova); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_set_tx_tc_early_drop(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ uint64_t early_drop_iova) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_TX_TC_EARLY_DROP, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_TX_TC_EARLY_DROP(cmd, tc_id, early_drop_iova); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_tx_tc_early_drop(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ uint64_t early_drop_iova) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_TX_TC_EARLY_DROP, -+ cmd_flags, -+ token); -+ DPNI_CMD_GET_TX_TC_EARLY_DROP(cmd, tc_id, early_drop_iova); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_set_rx_tc_congestion_notification(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ const struct dpni_congestion_notification_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header( -+ DPNI_CMDID_SET_RX_TC_CONGESTION_NOTIFICATION, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_RX_TC_CONGESTION_NOTIFICATION(cmd, tc_id, cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_rx_tc_congestion_notification(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ struct dpni_congestion_notification_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header( -+ DPNI_CMDID_GET_RX_TC_CONGESTION_NOTIFICATION, -+ cmd_flags, -+ token); -+ DPNI_CMD_GET_RX_TC_CONGESTION_NOTIFICATION(cmd, tc_id); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ DPNI_RSP_GET_RX_TC_CONGESTION_NOTIFICATION(cmd, cfg); -+ -+ return 0; -+} -+ -+int dpni_set_tx_tc_congestion_notification(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ const struct dpni_congestion_notification_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header( -+ DPNI_CMDID_SET_TX_TC_CONGESTION_NOTIFICATION, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_TX_TC_CONGESTION_NOTIFICATION(cmd, tc_id, cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_tx_tc_congestion_notification(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ struct dpni_congestion_notification_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header( -+ DPNI_CMDID_GET_TX_TC_CONGESTION_NOTIFICATION, -+ cmd_flags, -+ token); -+ DPNI_CMD_GET_TX_TC_CONGESTION_NOTIFICATION(cmd, tc_id); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ DPNI_RSP_GET_TX_TC_CONGESTION_NOTIFICATION(cmd, cfg); -+ -+ return 0; -+} -+ -+int dpni_set_tx_conf(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t flow_id, -+ const struct dpni_tx_conf_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_TX_CONF, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_TX_CONF(cmd, flow_id, cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_tx_conf(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t flow_id, -+ struct dpni_tx_conf_attr *attr) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_TX_CONF, -+ cmd_flags, -+ token); -+ DPNI_CMD_GET_TX_CONF(cmd, flow_id); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ DPNI_RSP_GET_TX_CONF(cmd, attr); -+ -+ return 0; -+} -+ -+int dpni_set_tx_conf_congestion_notification(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t flow_id, -+ const struct dpni_congestion_notification_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header( -+ DPNI_CMDID_SET_TX_CONF_CONGESTION_NOTIFICATION, -+ cmd_flags, -+ token); -+ DPNI_CMD_SET_TX_CONF_CONGESTION_NOTIFICATION(cmd, flow_id, cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpni_get_tx_conf_congestion_notification(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t flow_id, -+ struct dpni_congestion_notification_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header( -+ DPNI_CMDID_GET_TX_CONF_CONGESTION_NOTIFICATION, -+ cmd_flags, -+ token); -+ DPNI_CMD_GET_TX_CONF_CONGESTION_NOTIFICATION(cmd, flow_id); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ DPNI_RSP_GET_TX_CONF_CONGESTION_NOTIFICATION(cmd, cfg); -+ -+ return 0; -+} -diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpni.h b/drivers/staging/fsl-dpaa2/ethernet/dpni.h -new file mode 100644 -index 0000000..fca426d ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/ethernet/dpni.h -@@ -0,0 +1,2581 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef __FSL_DPNI_H -+#define __FSL_DPNI_H -+ -+#include "dpkg.h" -+ -+struct fsl_mc_io; -+ -+/** -+ * Data Path Network Interface API -+ * Contains initialization APIs and runtime control APIs for DPNI -+ */ -+ -+/** General DPNI macros */ -+ -+/** -+ * Maximum number of traffic classes -+ */ -+#define DPNI_MAX_TC 8 -+/** -+ * Maximum number of buffer pools per DPNI -+ */ -+#define DPNI_MAX_DPBP 8 -+/** -+ * Maximum number of storage-profiles per DPNI -+ */ -+#define DPNI_MAX_SP 2 -+ -+/** -+ * All traffic classes considered; see dpni_set_rx_flow() -+ */ -+#define DPNI_ALL_TCS (uint8_t)(-1) -+/** -+ * All flows within traffic class considered; see dpni_set_rx_flow() -+ */ -+#define DPNI_ALL_TC_FLOWS (uint16_t)(-1) -+/** -+ * Generate new flow ID; see dpni_set_tx_flow() -+ */ -+#define DPNI_NEW_FLOW_ID (uint16_t)(-1) -+/* use for common tx-conf queue; see dpni_set_tx_conf_() */ -+#define DPNI_COMMON_TX_CONF (uint16_t)(-1) -+ -+/** -+ * dpni_open() - Open a control session for the specified object -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @dpni_id: DPNI unique ID -+ * @token: Returned token; use in subsequent API calls -+ * -+ * This function can be used to open a control session for an -+ * already created object; an object may have been declared in -+ * the DPL or by calling the dpni_create() function. -+ * This function returns a unique authentication token, -+ * associated with the specific object ID and the specific MC -+ * portal; this token must be used in all subsequent commands for -+ * this specific object. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_open(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ int dpni_id, -+ uint16_t *token); -+ -+/** -+ * dpni_close() - Close the control session of the object -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * -+ * After this function is called, no further operations are -+ * allowed on the object without opening a new control session. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_close(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/* DPNI configuration options */ -+ -+/** -+ * Allow different distribution key profiles for different traffic classes; -+ * if not set, a single key profile is assumed -+ */ -+#define DPNI_OPT_ALLOW_DIST_KEY_PER_TC 0x00000001 -+ -+/** -+ * Disable all non-error transmit confirmation; error frames are reported -+ * back to a common Tx error queue -+ */ -+#define DPNI_OPT_TX_CONF_DISABLED 0x00000002 -+ -+/** -+ * Disable per-sender private Tx confirmation/error queue -+ */ -+#define DPNI_OPT_PRIVATE_TX_CONF_ERROR_DISABLED 0x00000004 -+ -+/** -+ * Support distribution based on hashed key; -+ * allows statistical distribution over receive queues in a traffic class -+ */ -+#define DPNI_OPT_DIST_HASH 0x00000010 -+ -+/** -+ * DEPRECATED - if this flag is selected and and all new 'max_fs_entries' are -+ * '0' then backward compatibility is preserved; -+ * Support distribution based on flow steering; -+ * allows explicit control of distribution over receive queues in a traffic -+ * class -+ */ -+#define DPNI_OPT_DIST_FS 0x00000020 -+ -+/** -+ * Unicast filtering support -+ */ -+#define DPNI_OPT_UNICAST_FILTER 0x00000080 -+/** -+ * Multicast filtering support -+ */ -+#define DPNI_OPT_MULTICAST_FILTER 0x00000100 -+/** -+ * VLAN filtering support -+ */ -+#define DPNI_OPT_VLAN_FILTER 0x00000200 -+/** -+ * Support IP reassembly on received packets -+ */ -+#define DPNI_OPT_IPR 0x00000800 -+/** -+ * Support IP fragmentation on transmitted packets -+ */ -+#define DPNI_OPT_IPF 0x00001000 -+/** -+ * VLAN manipulation support -+ */ -+#define DPNI_OPT_VLAN_MANIPULATION 0x00010000 -+/** -+ * Support masking of QoS lookup keys -+ */ -+#define DPNI_OPT_QOS_MASK_SUPPORT 0x00020000 -+/** -+ * Support masking of Flow Steering lookup keys -+ */ -+#define DPNI_OPT_FS_MASK_SUPPORT 0x00040000 -+ -+/** -+ * struct dpni_extended_cfg - Structure representing extended DPNI configuration -+ * @tc_cfg: TCs configuration -+ * @ipr_cfg: IP reassembly configuration -+ */ -+struct dpni_extended_cfg { -+ /** -+ * struct tc_cfg - TC configuration -+ * @max_dist: Maximum distribution size for Rx traffic class; -+ * supported values: 1,2,3,4,6,7,8,12,14,16,24,28,32,48,56,64,96, -+ * 112,128,192,224,256,384,448,512,768,896,1024; -+ * value '0' will be treated as '1'. -+ * other unsupported values will be round down to the nearest -+ * supported value. -+ * @max_fs_entries: Maximum FS entries for Rx traffic class; -+ * '0' means no support for this TC; -+ */ -+ struct { -+ uint16_t max_dist; -+ uint16_t max_fs_entries; -+ } tc_cfg[DPNI_MAX_TC]; -+ /** -+ * struct ipr_cfg - Structure representing IP reassembly configuration -+ * @max_reass_frm_size: Maximum size of the reassembled frame -+ * @min_frag_size_ipv4: Minimum fragment size of IPv4 fragments -+ * @min_frag_size_ipv6: Minimum fragment size of IPv6 fragments -+ * @max_open_frames_ipv4: Maximum concurrent IPv4 packets in reassembly -+ * process -+ * @max_open_frames_ipv6: Maximum concurrent IPv6 packets in reassembly -+ * process -+ */ -+ struct { -+ uint16_t max_reass_frm_size; -+ uint16_t min_frag_size_ipv4; -+ uint16_t min_frag_size_ipv6; -+ uint16_t max_open_frames_ipv4; -+ uint16_t max_open_frames_ipv6; -+ } ipr_cfg; -+}; -+ -+/** -+ * dpni_prepare_extended_cfg() - function prepare extended parameters -+ * @cfg: extended structure -+ * @ext_cfg_buf: Zeroed 256 bytes of memory before mapping it to DMA -+ * -+ * This function has to be called before dpni_create() -+ */ -+int dpni_prepare_extended_cfg(const struct dpni_extended_cfg *cfg, -+ uint8_t *ext_cfg_buf); -+ -+/** -+ * struct dpni_cfg - Structure representing DPNI configuration -+ * @mac_addr: Primary MAC address -+ * @adv: Advanced parameters; default is all zeros; -+ * use this structure to change default settings -+ */ -+struct dpni_cfg { -+ uint8_t mac_addr[6]; -+ /** -+ * struct adv - Advanced parameters -+ * @options: Mask of available options; use 'DPNI_OPT_' values -+ * @start_hdr: Selects the packet starting header for parsing; -+ * 'NET_PROT_NONE' is treated as default: 'NET_PROT_ETH' -+ * @max_senders: Maximum number of different senders; used as the number -+ * of dedicated Tx flows; Non-power-of-2 values are rounded -+ * up to the next power-of-2 value as hardware demands it; -+ * '0' will be treated as '1' -+ * @max_tcs: Maximum number of traffic classes (for both Tx and Rx); -+ * '0' will e treated as '1' -+ * @max_unicast_filters: Maximum number of unicast filters; -+ * '0' is treated as '16' -+ * @max_multicast_filters: Maximum number of multicast filters; -+ * '0' is treated as '64' -+ * @max_qos_entries: if 'max_tcs > 1', declares the maximum entries in -+ * the QoS table; '0' is treated as '64' -+ * @max_qos_key_size: Maximum key size for the QoS look-up; -+ * '0' is treated as '24' which is enough for IPv4 -+ * 5-tuple -+ * @max_dist_key_size: Maximum key size for the distribution; -+ * '0' is treated as '24' which is enough for IPv4 5-tuple -+ * @max_policers: Maximum number of policers; -+ * should be between '0' and max_tcs -+ * @max_congestion_ctrl: Maximum number of congestion control groups -+ * (CGs); covers early drop and congestion notification -+ * requirements; -+ * should be between '0' and ('max_tcs' + 'max_senders') -+ * @ext_cfg_iova: I/O virtual address of 256 bytes DMA-able memory -+ * filled with the extended configuration by calling -+ * dpni_prepare_extended_cfg() -+ */ -+ struct { -+ uint32_t options; -+ enum net_prot start_hdr; -+ uint8_t max_senders; -+ uint8_t max_tcs; -+ uint8_t max_unicast_filters; -+ uint8_t max_multicast_filters; -+ uint8_t max_vlan_filters; -+ uint8_t max_qos_entries; -+ uint8_t max_qos_key_size; -+ uint8_t max_dist_key_size; -+ uint8_t max_policers; -+ uint8_t max_congestion_ctrl; -+ uint64_t ext_cfg_iova; -+ } adv; -+}; -+ -+/** -+ * dpni_create() - Create the DPNI object -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @cfg: Configuration structure -+ * @token: Returned token; use in subsequent API calls -+ * -+ * Create the DPNI object, allocate required resources and -+ * perform required initialization. -+ * -+ * The object can be created either by declaring it in the -+ * DPL file, or by calling this function. -+ * -+ * This function returns a unique authentication token, -+ * associated with the specific object ID and the specific MC -+ * portal; this token must be used in all subsequent calls to -+ * this specific object. For objects that are created using the -+ * DPL file, call dpni_open() function to get an authentication -+ * token first. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_create(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ const struct dpni_cfg *cfg, -+ uint16_t *token); -+ -+/** -+ * dpni_destroy() - Destroy the DPNI object and release all its resources. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpni_destroy(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * struct dpni_pools_cfg - Structure representing buffer pools configuration -+ * @num_dpbp: Number of DPBPs -+ * @pools: Array of buffer pools parameters; The number of valid entries -+ * must match 'num_dpbp' value -+ */ -+struct dpni_pools_cfg { -+ uint8_t num_dpbp; -+ /** -+ * struct pools - Buffer pools parameters -+ * @dpbp_id: DPBP object ID -+ * @buffer_size: Buffer size -+ * @backup_pool: Backup pool -+ */ -+ struct { -+ int dpbp_id; -+ uint16_t buffer_size; -+ int backup_pool; -+ } pools[DPNI_MAX_DPBP]; -+}; -+ -+/** -+ * dpni_set_pools() - Set buffer pools configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @cfg: Buffer pools configuration -+ * -+ * mandatory for DPNI operation -+ * warning:Allowed only when DPNI is disabled -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_pools(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_pools_cfg *cfg); -+ -+/** -+ * dpni_enable() - Enable the DPNI, allow sending and receiving frames. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * dpni_disable() - Disable the DPNI, stop sending and receiving frames. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_disable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * dpni_is_enabled() - Check if the DPNI is enabled. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @en: Returns '1' if object is enabled; '0' otherwise -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_is_enabled(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *en); -+ -+/** -+ * dpni_reset() - Reset the DPNI, returns the object to initial state. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_reset(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * DPNI IRQ Index and Events -+ */ -+ -+/** -+ * IRQ index -+ */ -+#define DPNI_IRQ_INDEX 0 -+/** -+ * IRQ event - indicates a change in link state -+ */ -+#define DPNI_IRQ_EVENT_LINK_CHANGED 0x00000001 -+ -+/** -+ * struct dpni_irq_cfg - IRQ configuration -+ * @addr: Address that must be written to signal a message-based interrupt -+ * @val: Value to write into irq_addr address -+ * @irq_num: A user defined number associated with this IRQ -+ */ -+struct dpni_irq_cfg { -+ uint64_t addr; -+ uint32_t val; -+ int irq_num; -+}; -+ -+/** -+ * dpni_set_irq() - Set IRQ information for the DPNI to trigger an interrupt. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @irq_index: Identifies the interrupt index to configure -+ * @irq_cfg: IRQ configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ struct dpni_irq_cfg *irq_cfg); -+ -+/** -+ * dpni_get_irq() - Get IRQ information from the DPNI. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @irq_index: The interrupt index to configure -+ * @type: Interrupt type: 0 represents message interrupt -+ * type (both irq_addr and irq_val are valid) -+ * @irq_cfg: IRQ attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ int *type, -+ struct dpni_irq_cfg *irq_cfg); -+ -+/** -+ * dpni_set_irq_enable() - Set overall interrupt state. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @irq_index: The interrupt index to configure -+ * @en: Interrupt state: - enable = 1, disable = 0 -+ * -+ * Allows GPP software to control when interrupts are generated. -+ * Each interrupt can have up to 32 causes. The enable/disable control's the -+ * overall interrupt state. if the interrupt is disabled no causes will cause -+ * an interrupt. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t en); -+ -+/** -+ * dpni_get_irq_enable() - Get overall interrupt state -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @irq_index: The interrupt index to configure -+ * @en: Returned interrupt state - enable = 1, disable = 0 -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t *en); -+ -+/** -+ * dpni_set_irq_mask() - Set interrupt mask. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @irq_index: The interrupt index to configure -+ * @mask: event mask to trigger interrupt; -+ * each bit: -+ * 0 = ignore event -+ * 1 = consider event for asserting IRQ -+ * -+ * Every interrupt can have up to 32 causes and the interrupt model supports -+ * masking/unmasking each cause independently -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t mask); -+ -+/** -+ * dpni_get_irq_mask() - Get interrupt mask. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @irq_index: The interrupt index to configure -+ * @mask: Returned event mask to trigger interrupt -+ * -+ * Every interrupt can have up to 32 causes and the interrupt model supports -+ * masking/unmasking each cause independently -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *mask); -+ -+/** -+ * dpni_get_irq_status() - Get the current status of any pending interrupts. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @irq_index: The interrupt index to configure -+ * @status: Returned interrupts status - one bit per cause: -+ * 0 = no interrupt pending -+ * 1 = interrupt pending -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *status); -+ -+/** -+ * dpni_clear_irq_status() - Clear a pending interrupt's status -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @irq_index: The interrupt index to configure -+ * @status: bits to clear (W1C) - one bit per cause: -+ * 0 = don't change -+ * 1 = clear status bit -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_clear_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t status); -+ -+/** -+ * struct dpni_attr - Structure representing DPNI attributes -+ * @id: DPNI object ID -+ * @version: DPNI version -+ * @start_hdr: Indicates the packet starting header for parsing -+ * @options: Mask of available options; reflects the value as was given in -+ * object's creation -+ * @max_senders: Maximum number of different senders; used as the number -+ * of dedicated Tx flows; -+ * @max_tcs: Maximum number of traffic classes (for both Tx and Rx) -+ * @max_unicast_filters: Maximum number of unicast filters -+ * @max_multicast_filters: Maximum number of multicast filters -+ * @max_vlan_filters: Maximum number of VLAN filters -+ * @max_qos_entries: if 'max_tcs > 1', declares the maximum entries in QoS table -+ * @max_qos_key_size: Maximum key size for the QoS look-up -+ * @max_dist_key_size: Maximum key size for the distribution look-up -+ * @max_policers: Maximum number of policers; -+ * @max_congestion_ctrl: Maximum number of congestion control groups (CGs); -+ * @ext_cfg_iova: I/O virtual address of 256 bytes DMA-able memory; -+ * call dpni_extract_extended_cfg() to extract the extended configuration -+ */ -+struct dpni_attr { -+ int id; -+ /** -+ * struct version - DPNI version -+ * @major: DPNI major version -+ * @minor: DPNI minor version -+ */ -+ struct { -+ uint16_t major; -+ uint16_t minor; -+ } version; -+ enum net_prot start_hdr; -+ uint32_t options; -+ uint8_t max_senders; -+ uint8_t max_tcs; -+ uint8_t max_unicast_filters; -+ uint8_t max_multicast_filters; -+ uint8_t max_vlan_filters; -+ uint8_t max_qos_entries; -+ uint8_t max_qos_key_size; -+ uint8_t max_dist_key_size; -+ uint8_t max_policers; -+ uint8_t max_congestion_ctrl; -+ uint64_t ext_cfg_iova; -+}; -+ -+/** -+ * dpni_get_attributes() - Retrieve DPNI attributes. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @attr: Object's attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_attributes(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpni_attr *attr); -+ -+/** -+ * dpni_extract_extended_cfg() - extract the extended parameters -+ * @cfg: extended structure -+ * @ext_cfg_buf: 256 bytes of DMA-able memory -+ * -+ * This function has to be called after dpni_get_attributes() -+ */ -+int dpni_extract_extended_cfg(struct dpni_extended_cfg *cfg, -+ const uint8_t *ext_cfg_buf); -+ -+/** -+ * DPNI errors -+ */ -+ -+/** -+ * Extract out of frame header error -+ */ -+#define DPNI_ERROR_EOFHE 0x00020000 -+/** -+ * Frame length error -+ */ -+#define DPNI_ERROR_FLE 0x00002000 -+/** -+ * Frame physical error -+ */ -+#define DPNI_ERROR_FPE 0x00001000 -+/** -+ * Parsing header error -+ */ -+#define DPNI_ERROR_PHE 0x00000020 -+/** -+ * Parser L3 checksum error -+ */ -+#define DPNI_ERROR_L3CE 0x00000004 -+/** -+ * Parser L3 checksum error -+ */ -+#define DPNI_ERROR_L4CE 0x00000001 -+ -+/** -+ * enum dpni_error_action - Defines DPNI behavior for errors -+ * @DPNI_ERROR_ACTION_DISCARD: Discard the frame -+ * @DPNI_ERROR_ACTION_CONTINUE: Continue with the normal flow -+ * @DPNI_ERROR_ACTION_SEND_TO_ERROR_QUEUE: Send the frame to the error queue -+ */ -+enum dpni_error_action { -+ DPNI_ERROR_ACTION_DISCARD = 0, -+ DPNI_ERROR_ACTION_CONTINUE = 1, -+ DPNI_ERROR_ACTION_SEND_TO_ERROR_QUEUE = 2 -+}; -+ -+/** -+ * struct dpni_error_cfg - Structure representing DPNI errors treatment -+ * @errors: Errors mask; use 'DPNI_ERROR__ -+ * @error_action: The desired action for the errors mask -+ * @set_frame_annotation: Set to '1' to mark the errors in frame annotation -+ * status (FAS); relevant only for the non-discard action -+ */ -+struct dpni_error_cfg { -+ uint32_t errors; -+ enum dpni_error_action error_action; -+ int set_frame_annotation; -+}; -+ -+/** -+ * dpni_set_errors_behavior() - Set errors behavior -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @cfg: Errors configuration -+ * -+ * this function may be called numerous times with different -+ * error masks -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_errors_behavior(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpni_error_cfg *cfg); -+ -+/** -+ * DPNI buffer layout modification options -+ */ -+ -+/** -+ * Select to modify the time-stamp setting -+ */ -+#define DPNI_BUF_LAYOUT_OPT_TIMESTAMP 0x00000001 -+/** -+ * Select to modify the parser-result setting; not applicable for Tx -+ */ -+#define DPNI_BUF_LAYOUT_OPT_PARSER_RESULT 0x00000002 -+/** -+ * Select to modify the frame-status setting -+ */ -+#define DPNI_BUF_LAYOUT_OPT_FRAME_STATUS 0x00000004 -+/** -+ * Select to modify the private-data-size setting -+ */ -+#define DPNI_BUF_LAYOUT_OPT_PRIVATE_DATA_SIZE 0x00000008 -+/** -+ * Select to modify the data-alignment setting -+ */ -+#define DPNI_BUF_LAYOUT_OPT_DATA_ALIGN 0x00000010 -+/** -+ * Select to modify the data-head-room setting -+ */ -+#define DPNI_BUF_LAYOUT_OPT_DATA_HEAD_ROOM 0x00000020 -+/** -+ * Select to modify the data-tail-room setting -+ */ -+#define DPNI_BUF_LAYOUT_OPT_DATA_TAIL_ROOM 0x00000040 -+ -+/** -+ * struct dpni_buffer_layout - Structure representing DPNI buffer layout -+ * @options: Flags representing the suggested modifications to the buffer -+ * layout; Use any combination of 'DPNI_BUF_LAYOUT_OPT_' flags -+ * @pass_timestamp: Pass timestamp value -+ * @pass_parser_result: Pass parser results -+ * @pass_frame_status: Pass frame status -+ * @private_data_size: Size kept for private data (in bytes) -+ * @data_align: Data alignment -+ * @data_head_room: Data head room -+ * @data_tail_room: Data tail room -+ */ -+struct dpni_buffer_layout { -+ uint32_t options; -+ int pass_timestamp; -+ int pass_parser_result; -+ int pass_frame_status; -+ uint16_t private_data_size; -+ uint16_t data_align; -+ uint16_t data_head_room; -+ uint16_t data_tail_room; -+}; -+ -+/** -+ * dpni_get_rx_buffer_layout() - Retrieve Rx buffer layout attributes. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @layout: Returns buffer layout attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_rx_buffer_layout(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpni_buffer_layout *layout); -+ -+/** -+ * dpni_set_rx_buffer_layout() - Set Rx buffer layout configuration. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @layout: Buffer layout configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ * -+ * @warning Allowed only when DPNI is disabled -+ */ -+int dpni_set_rx_buffer_layout(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_buffer_layout *layout); -+ -+/** -+ * dpni_get_tx_buffer_layout() - Retrieve Tx buffer layout attributes. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @layout: Returns buffer layout attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_tx_buffer_layout(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpni_buffer_layout *layout); -+ -+/** -+ * dpni_set_tx_buffer_layout() - Set Tx buffer layout configuration. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @layout: Buffer layout configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ * -+ * @warning Allowed only when DPNI is disabled -+ */ -+int dpni_set_tx_buffer_layout(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_buffer_layout *layout); -+ -+/** -+ * dpni_get_tx_conf_buffer_layout() - Retrieve Tx confirmation buffer layout -+ * attributes. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @layout: Returns buffer layout attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_tx_conf_buffer_layout(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpni_buffer_layout *layout); -+ -+/** -+ * dpni_set_tx_conf_buffer_layout() - Set Tx confirmation buffer layout -+ * configuration. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @layout: Buffer layout configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ * -+ * @warning Allowed only when DPNI is disabled -+ */ -+int dpni_set_tx_conf_buffer_layout(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_buffer_layout *layout); -+ -+/** -+ * dpni_set_l3_chksum_validation() - Enable/disable L3 checksum validation -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @en: Set to '1' to enable; '0' to disable -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_l3_chksum_validation(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en); -+ -+/** -+ * dpni_get_l3_chksum_validation() - Get L3 checksum validation mode -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @en: Returns '1' if enabled; '0' otherwise -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_l3_chksum_validation(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *en); -+ -+/** -+ * dpni_set_l4_chksum_validation() - Enable/disable L4 checksum validation -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @en: Set to '1' to enable; '0' to disable -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_l4_chksum_validation(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en); -+ -+/** -+ * dpni_get_l4_chksum_validation() - Get L4 checksum validation mode -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @en: Returns '1' if enabled; '0' otherwise -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_l4_chksum_validation(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *en); -+ -+/** -+ * dpni_get_qdid() - Get the Queuing Destination ID (QDID) that should be used -+ * for enqueue operations -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @qdid: Returned virtual QDID value that should be used as an argument -+ * in all enqueue operations -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_qdid(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t *qdid); -+ -+/** -+ * struct dpni_sp_info - Structure representing DPNI storage-profile information -+ * (relevant only for DPNI owned by AIOP) -+ * @spids: array of storage-profiles -+ */ -+struct dpni_sp_info { -+ uint16_t spids[DPNI_MAX_SP]; -+}; -+ -+/** -+ * dpni_get_spids() - Get the AIOP storage profile IDs associated with the DPNI -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @sp_info: Returned AIOP storage-profile information -+ * -+ * Return: '0' on Success; Error code otherwise. -+ * -+ * @warning Only relevant for DPNI that belongs to AIOP container. -+ */ -+int dpni_get_sp_info(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpni_sp_info *sp_info); -+ -+/** -+ * dpni_get_tx_data_offset() - Get the Tx data offset (from start of buffer) -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @data_offset: Tx data offset (from start of buffer) -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_tx_data_offset(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t *data_offset); -+ -+/** -+ * enum dpni_counter - DPNI counter types -+ * @DPNI_CNT_ING_FRAME: Counts ingress frames -+ * @DPNI_CNT_ING_BYTE: Counts ingress bytes -+ * @DPNI_CNT_ING_FRAME_DROP: Counts ingress frames dropped due to explicit -+ * 'drop' setting -+ * @DPNI_CNT_ING_FRAME_DISCARD: Counts ingress frames discarded due to errors -+ * @DPNI_CNT_ING_MCAST_FRAME: Counts ingress multicast frames -+ * @DPNI_CNT_ING_MCAST_BYTE: Counts ingress multicast bytes -+ * @DPNI_CNT_ING_BCAST_FRAME: Counts ingress broadcast frames -+ * @DPNI_CNT_ING_BCAST_BYTES: Counts ingress broadcast bytes -+ * @DPNI_CNT_EGR_FRAME: Counts egress frames -+ * @DPNI_CNT_EGR_BYTE: Counts egress bytes -+ * @DPNI_CNT_EGR_FRAME_DISCARD: Counts egress frames discarded due to errors -+ */ -+enum dpni_counter { -+ DPNI_CNT_ING_FRAME = 0x0, -+ DPNI_CNT_ING_BYTE = 0x1, -+ DPNI_CNT_ING_FRAME_DROP = 0x2, -+ DPNI_CNT_ING_FRAME_DISCARD = 0x3, -+ DPNI_CNT_ING_MCAST_FRAME = 0x4, -+ DPNI_CNT_ING_MCAST_BYTE = 0x5, -+ DPNI_CNT_ING_BCAST_FRAME = 0x6, -+ DPNI_CNT_ING_BCAST_BYTES = 0x7, -+ DPNI_CNT_EGR_FRAME = 0x8, -+ DPNI_CNT_EGR_BYTE = 0x9, -+ DPNI_CNT_EGR_FRAME_DISCARD = 0xa -+}; -+ -+/** -+ * dpni_get_counter() - Read a specific DPNI counter -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @counter: The requested counter -+ * @value: Returned counter's current value -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_counter(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ enum dpni_counter counter, -+ uint64_t *value); -+ -+/** -+ * dpni_set_counter() - Set (or clear) a specific DPNI counter -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @counter: The requested counter -+ * @value: New counter value; typically pass '0' for resetting -+ * the counter. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_counter(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ enum dpni_counter counter, -+ uint64_t value); -+ -+/** -+ * Enable auto-negotiation -+ */ -+#define DPNI_LINK_OPT_AUTONEG 0x0000000000000001ULL -+/** -+ * Enable half-duplex mode -+ */ -+#define DPNI_LINK_OPT_HALF_DUPLEX 0x0000000000000002ULL -+/** -+ * Enable pause frames -+ */ -+#define DPNI_LINK_OPT_PAUSE 0x0000000000000004ULL -+/** -+ * Enable a-symmetric pause frames -+ */ -+#define DPNI_LINK_OPT_ASYM_PAUSE 0x0000000000000008ULL -+ -+/** -+ * struct - Structure representing DPNI link configuration -+ * @rate: Rate -+ * @options: Mask of available options; use 'DPNI_LINK_OPT_' values -+ */ -+struct dpni_link_cfg { -+ uint32_t rate; -+ uint64_t options; -+}; -+ -+/** -+ * dpni_set_link_cfg() - set the link configuration. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @cfg: Link configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_link_cfg(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_link_cfg *cfg); -+ -+/** -+ * struct dpni_link_state - Structure representing DPNI link state -+ * @rate: Rate -+ * @options: Mask of available options; use 'DPNI_LINK_OPT_' values -+ * @up: Link state; '0' for down, '1' for up -+ */ -+struct dpni_link_state { -+ uint32_t rate; -+ uint64_t options; -+ int up; -+}; -+ -+/** -+ * dpni_get_link_state() - Return the link state (either up or down) -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @state: Returned link state; -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_link_state(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpni_link_state *state); -+ -+/** -+ * struct dpni_tx_shaping - Structure representing DPNI tx shaping configuration -+ * @rate_limit: rate in Mbps -+ * @max_burst_size: burst size in bytes (up to 64KB) -+ */ -+struct dpni_tx_shaping_cfg { -+ uint32_t rate_limit; -+ uint16_t max_burst_size; -+}; -+ -+/** -+ * dpni_set_tx_shaping() - Set the transmit shaping -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @tx_shaper: tx shaping configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_tx_shaping(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_tx_shaping_cfg *tx_shaper); -+ -+/** -+ * dpni_set_max_frame_length() - Set the maximum received frame length. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @max_frame_length: Maximum received frame length (in -+ * bytes); frame is discarded if its -+ * length exceeds this value -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_max_frame_length(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t max_frame_length); -+ -+/** -+ * dpni_get_max_frame_length() - Get the maximum received frame length. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @max_frame_length: Maximum received frame length (in -+ * bytes); frame is discarded if its -+ * length exceeds this value -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_max_frame_length(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t *max_frame_length); -+ -+/** -+ * dpni_set_mtu() - Set the MTU for the interface. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @mtu: MTU length (in bytes) -+ * -+ * MTU determines the maximum fragment size for performing IP -+ * fragmentation on egress packets. -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_mtu(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t mtu); -+ -+/** -+ * dpni_get_mtu() - Get the MTU. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @mtu: Returned MTU length (in bytes) -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_mtu(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t *mtu); -+ -+/** -+ * dpni_set_multicast_promisc() - Enable/disable multicast promiscuous mode -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @en: Set to '1' to enable; '0' to disable -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_multicast_promisc(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en); -+ -+/** -+ * dpni_get_multicast_promisc() - Get multicast promiscuous mode -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @en: Returns '1' if enabled; '0' otherwise -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_multicast_promisc(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *en); -+ -+/** -+ * dpni_set_unicast_promisc() - Enable/disable unicast promiscuous mode -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @en: Set to '1' to enable; '0' to disable -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_unicast_promisc(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en); -+ -+/** -+ * dpni_get_unicast_promisc() - Get unicast promiscuous mode -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @en: Returns '1' if enabled; '0' otherwise -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_unicast_promisc(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *en); -+ -+/** -+ * dpni_set_primary_mac_addr() - Set the primary MAC address -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @mac_addr: MAC address to set as primary address -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_primary_mac_addr(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const uint8_t mac_addr[6]); -+ -+/** -+ * dpni_get_primary_mac_addr() - Get the primary MAC address -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @mac_addr: Returned MAC address -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_primary_mac_addr(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t mac_addr[6]); -+ -+/** -+ * dpni_add_mac_addr() - Add MAC address filter -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @mac_addr: MAC address to add -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_add_mac_addr(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const uint8_t mac_addr[6]); -+ -+/** -+ * dpni_remove_mac_addr() - Remove MAC address filter -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @mac_addr: MAC address to remove -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_remove_mac_addr(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const uint8_t mac_addr[6]); -+ -+/** -+ * dpni_clear_mac_filters() - Clear all unicast and/or multicast MAC filters -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @unicast: Set to '1' to clear unicast addresses -+ * @multicast: Set to '1' to clear multicast addresses -+ * -+ * The primary MAC address is not cleared by this operation. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_clear_mac_filters(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int unicast, -+ int multicast); -+ -+/** -+ * dpni_set_vlan_filters() - Enable/disable VLAN filtering mode -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @en: Set to '1' to enable; '0' to disable -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_vlan_filters(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en); -+ -+/** -+ * dpni_add_vlan_id() - Add VLAN ID filter -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @vlan_id: VLAN ID to add -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_add_vlan_id(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t vlan_id); -+ -+/** -+ * dpni_remove_vlan_id() - Remove VLAN ID filter -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @vlan_id: VLAN ID to remove -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_remove_vlan_id(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t vlan_id); -+ -+/** -+ * dpni_clear_vlan_filters() - Clear all VLAN filters -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_clear_vlan_filters(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * enum dpni_tx_schedule_mode - DPNI Tx scheduling mode -+ * @DPNI_TX_SCHED_STRICT_PRIORITY: strict priority -+ * @DPNI_TX_SCHED_WEIGHTED: weighted based scheduling -+ */ -+enum dpni_tx_schedule_mode { -+ DPNI_TX_SCHED_STRICT_PRIORITY, -+ DPNI_TX_SCHED_WEIGHTED, -+}; -+ -+/** -+ * struct dpni_tx_schedule_cfg - Structure representing Tx -+ * scheduling configuration -+ * @mode: scheduling mode -+ * @delta_bandwidth: Bandwidth represented in weights from 100 to 10000; -+ * not applicable for 'strict-priority' mode; -+ */ -+struct dpni_tx_schedule_cfg { -+ enum dpni_tx_schedule_mode mode; -+ uint16_t delta_bandwidth; -+}; -+ -+/** -+ * struct dpni_tx_selection_cfg - Structure representing transmission -+ * selection configuration -+ * @tc_sched: an array of traffic-classes -+ */ -+struct dpni_tx_selection_cfg { -+ struct dpni_tx_schedule_cfg tc_sched[DPNI_MAX_TC]; -+}; -+ -+/** -+ * dpni_set_tx_selection() - Set transmission selection configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @cfg: transmission selection configuration -+ * -+ * warning: Allowed only when DPNI is disabled -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_tx_selection(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_tx_selection_cfg *cfg); -+ -+/** -+ * enum dpni_dist_mode - DPNI distribution mode -+ * @DPNI_DIST_MODE_NONE: No distribution -+ * @DPNI_DIST_MODE_HASH: Use hash distribution; only relevant if -+ * the 'DPNI_OPT_DIST_HASH' option was set at DPNI creation -+ * @DPNI_DIST_MODE_FS: Use explicit flow steering; only relevant if -+ * the 'DPNI_OPT_DIST_FS' option was set at DPNI creation -+ */ -+enum dpni_dist_mode { -+ DPNI_DIST_MODE_NONE = 0, -+ DPNI_DIST_MODE_HASH = 1, -+ DPNI_DIST_MODE_FS = 2 -+}; -+ -+/** -+ * enum dpni_fs_miss_action - DPNI Flow Steering miss action -+ * @DPNI_FS_MISS_DROP: In case of no-match, drop the frame -+ * @DPNI_FS_MISS_EXPLICIT_FLOWID: In case of no-match, use explicit flow-id -+ * @DPNI_FS_MISS_HASH: In case of no-match, distribute using hash -+ */ -+enum dpni_fs_miss_action { -+ DPNI_FS_MISS_DROP = 0, -+ DPNI_FS_MISS_EXPLICIT_FLOWID = 1, -+ DPNI_FS_MISS_HASH = 2 -+}; -+ -+/** -+ * struct dpni_fs_tbl_cfg - Flow Steering table configuration -+ * @miss_action: Miss action selection -+ * @default_flow_id: Used when 'miss_action = DPNI_FS_MISS_EXPLICIT_FLOWID' -+ */ -+struct dpni_fs_tbl_cfg { -+ enum dpni_fs_miss_action miss_action; -+ uint16_t default_flow_id; -+}; -+ -+/** -+ * dpni_prepare_key_cfg() - function prepare extract parameters -+ * @cfg: defining a full Key Generation profile (rule) -+ * @key_cfg_buf: Zeroed 256 bytes of memory before mapping it to DMA -+ * -+ * This function has to be called before the following functions: -+ * - dpni_set_rx_tc_dist() -+ * - dpni_set_qos_table() -+ */ -+int dpni_prepare_key_cfg(const struct dpkg_profile_cfg *cfg, -+ uint8_t *key_cfg_buf); -+ -+/** -+ * struct dpni_rx_tc_dist_cfg - Rx traffic class distribution configuration -+ * @dist_size: Set the distribution size; -+ * supported values: 1,2,3,4,6,7,8,12,14,16,24,28,32,48,56,64,96, -+ * 112,128,192,224,256,384,448,512,768,896,1024 -+ * @dist_mode: Distribution mode -+ * @key_cfg_iova: I/O virtual address of 256 bytes DMA-able memory filled with -+ * the extractions to be used for the distribution key by calling -+ * dpni_prepare_key_cfg() relevant only when -+ * 'dist_mode != DPNI_DIST_MODE_NONE', otherwise it can be '0' -+ * @fs_cfg: Flow Steering table configuration; only relevant if -+ * 'dist_mode = DPNI_DIST_MODE_FS' -+ */ -+struct dpni_rx_tc_dist_cfg { -+ uint16_t dist_size; -+ enum dpni_dist_mode dist_mode; -+ uint64_t key_cfg_iova; -+ struct dpni_fs_tbl_cfg fs_cfg; -+}; -+ -+/** -+ * dpni_set_rx_tc_dist() - Set Rx traffic class distribution configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @tc_id: Traffic class selection (0-7) -+ * @cfg: Traffic class distribution configuration -+ * -+ * warning: if 'dist_mode != DPNI_DIST_MODE_NONE', call dpni_prepare_key_cfg() -+ * first to prepare the key_cfg_iova parameter -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpni_set_rx_tc_dist(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ const struct dpni_rx_tc_dist_cfg *cfg); -+ -+/** -+ * Set to select color aware mode (otherwise - color blind) -+ */ -+#define DPNI_POLICER_OPT_COLOR_AWARE 0x00000001 -+/** -+ * Set to discard frame with RED color -+ */ -+#define DPNI_POLICER_OPT_DISCARD_RED 0x00000002 -+ -+/** -+ * enum dpni_policer_mode - selecting the policer mode -+ * @DPNI_POLICER_MODE_NONE: Policer is disabled -+ * @DPNI_POLICER_MODE_PASS_THROUGH: Policer pass through -+ * @DPNI_POLICER_MODE_RFC_2698: Policer algorithm RFC 2698 -+ * @DPNI_POLICER_MODE_RFC_4115: Policer algorithm RFC 4115 -+ */ -+enum dpni_policer_mode { -+ DPNI_POLICER_MODE_NONE = 0, -+ DPNI_POLICER_MODE_PASS_THROUGH, -+ DPNI_POLICER_MODE_RFC_2698, -+ DPNI_POLICER_MODE_RFC_4115 -+}; -+ -+/** -+ * enum dpni_policer_unit - DPNI policer units -+ * @DPNI_POLICER_UNIT_BYTES: bytes units -+ * @DPNI_POLICER_UNIT_FRAMES: frames units -+ */ -+enum dpni_policer_unit { -+ DPNI_POLICER_UNIT_BYTES = 0, -+ DPNI_POLICER_UNIT_FRAMES -+}; -+ -+/** -+ * enum dpni_policer_color - selecting the policer color -+ * @DPNI_POLICER_COLOR_GREEN: Green color -+ * @DPNI_POLICER_COLOR_YELLOW: Yellow color -+ * @DPNI_POLICER_COLOR_RED: Red color -+ */ -+enum dpni_policer_color { -+ DPNI_POLICER_COLOR_GREEN = 0, -+ DPNI_POLICER_COLOR_YELLOW, -+ DPNI_POLICER_COLOR_RED -+}; -+ -+/** -+ * struct dpni_rx_tc_policing_cfg - Policer configuration -+ * @options: Mask of available options; use 'DPNI_POLICER_OPT_' values -+ * @mode: policer mode -+ * @default_color: For pass-through mode the policer re-colors with this -+ * color any incoming packets. For Color aware non-pass-through mode: -+ * policer re-colors with this color all packets with FD[DROPP]>2. -+ * @units: Bytes or Packets -+ * @cir: Committed information rate (CIR) in Kbps or packets/second -+ * @cbs: Committed burst size (CBS) in bytes or packets -+ * @eir: Peak information rate (PIR, rfc2698) in Kbps or packets/second -+ * Excess information rate (EIR, rfc4115) in Kbps or packets/second -+ * @ebs: Peak burst size (PBS, rfc2698) in bytes or packets -+ * Excess burst size (EBS, rfc4115) in bytes or packets -+ */ -+struct dpni_rx_tc_policing_cfg { -+ uint32_t options; -+ enum dpni_policer_mode mode; -+ enum dpni_policer_unit units; -+ enum dpni_policer_color default_color; -+ uint32_t cir; -+ uint32_t cbs; -+ uint32_t eir; -+ uint32_t ebs; -+}; -+ -+/** -+ * dpni_set_rx_tc_policing() - Set Rx traffic class policing configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @tc_id: Traffic class selection (0-7) -+ * @cfg: Traffic class policing configuration -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpni_set_rx_tc_policing(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ const struct dpni_rx_tc_policing_cfg *cfg); -+ -+/** -+ * dpni_get_rx_tc_policing() - Get Rx traffic class policing configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @tc_id: Traffic class selection (0-7) -+ * @cfg: Traffic class policing configuration -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpni_get_rx_tc_policing(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ struct dpni_rx_tc_policing_cfg *cfg); -+ -+/** -+ * enum dpni_congestion_unit - DPNI congestion units -+ * @DPNI_CONGESTION_UNIT_BYTES: bytes units -+ * @DPNI_CONGESTION_UNIT_FRAMES: frames units -+ */ -+enum dpni_congestion_unit { -+ DPNI_CONGESTION_UNIT_BYTES = 0, -+ DPNI_CONGESTION_UNIT_FRAMES -+}; -+ -+/** -+ * enum dpni_early_drop_mode - DPNI early drop mode -+ * @DPNI_EARLY_DROP_MODE_NONE: early drop is disabled -+ * @DPNI_EARLY_DROP_MODE_TAIL: early drop in taildrop mode -+ * @DPNI_EARLY_DROP_MODE_WRED: early drop in WRED mode -+ */ -+enum dpni_early_drop_mode { -+ DPNI_EARLY_DROP_MODE_NONE = 0, -+ DPNI_EARLY_DROP_MODE_TAIL, -+ DPNI_EARLY_DROP_MODE_WRED -+}; -+ -+/** -+ * struct dpni_wred_cfg - WRED configuration -+ * @max_threshold: maximum threshold that packets may be discarded. Above this -+ * threshold all packets are discarded; must be less than 2^39; -+ * approximated to be expressed as (x+256)*2^(y-1) due to HW -+ * implementation. -+ * @min_threshold: minimum threshold that packets may be discarded at -+ * @drop_probability: probability that a packet will be discarded (1-100, -+ * associated with the max_threshold). -+ */ -+struct dpni_wred_cfg { -+ uint64_t max_threshold; -+ uint64_t min_threshold; -+ uint8_t drop_probability; -+}; -+ -+/** -+ * struct dpni_early_drop_cfg - early-drop configuration -+ * @mode: drop mode -+ * @units: units type -+ * @green: WRED - 'green' configuration -+ * @yellow: WRED - 'yellow' configuration -+ * @red: WRED - 'red' configuration -+ * @tail_drop_threshold: tail drop threshold -+ */ -+struct dpni_early_drop_cfg { -+ enum dpni_early_drop_mode mode; -+ enum dpni_congestion_unit units; -+ -+ struct dpni_wred_cfg green; -+ struct dpni_wred_cfg yellow; -+ struct dpni_wred_cfg red; -+ -+ uint32_t tail_drop_threshold; -+}; -+ -+/** -+ * dpni_prepare_early_drop() - prepare an early drop. -+ * @cfg: Early-drop configuration -+ * @early_drop_buf: Zeroed 256 bytes of memory before mapping it to DMA -+ * -+ * This function has to be called before dpni_set_rx_tc_early_drop or -+ * dpni_set_tx_tc_early_drop -+ * -+ */ -+void dpni_prepare_early_drop(const struct dpni_early_drop_cfg *cfg, -+ uint8_t *early_drop_buf); -+ -+/** -+ * dpni_extract_early_drop() - extract the early drop configuration. -+ * @cfg: Early-drop configuration -+ * @early_drop_buf: Zeroed 256 bytes of memory before mapping it to DMA -+ * -+ * This function has to be called after dpni_get_rx_tc_early_drop or -+ * dpni_get_tx_tc_early_drop -+ * -+ */ -+void dpni_extract_early_drop(struct dpni_early_drop_cfg *cfg, -+ const uint8_t *early_drop_buf); -+ -+/** -+ * dpni_set_rx_tc_early_drop() - Set Rx traffic class early-drop configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @tc_id: Traffic class selection (0-7) -+ * @early_drop_iova: I/O virtual address of 256 bytes DMA-able memory filled -+ * with the early-drop configuration by calling dpni_prepare_early_drop() -+ * -+ * warning: Before calling this function, call dpni_prepare_early_drop() to -+ * prepare the early_drop_iova parameter -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpni_set_rx_tc_early_drop(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ uint64_t early_drop_iova); -+ -+/** -+ * dpni_get_rx_tc_early_drop() - Get Rx traffic class early-drop configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @tc_id: Traffic class selection (0-7) -+ * @early_drop_iova: I/O virtual address of 256 bytes DMA-able memory -+ * -+ * warning: After calling this function, call dpni_extract_early_drop() to -+ * get the early drop configuration -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpni_get_rx_tc_early_drop(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ uint64_t early_drop_iova); -+ -+/** -+ * dpni_set_tx_tc_early_drop() - Set Tx traffic class early-drop configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @tc_id: Traffic class selection (0-7) -+ * @early_drop_iova: I/O virtual address of 256 bytes DMA-able memory filled -+ * with the early-drop configuration by calling dpni_prepare_early_drop() -+ * -+ * warning: Before calling this function, call dpni_prepare_early_drop() to -+ * prepare the early_drop_iova parameter -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpni_set_tx_tc_early_drop(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ uint64_t early_drop_iova); -+ -+/** -+ * dpni_get_tx_tc_early_drop() - Get Tx traffic class early-drop configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @tc_id: Traffic class selection (0-7) -+ * @early_drop_iova: I/O virtual address of 256 bytes DMA-able memory -+ * -+ * warning: After calling this function, call dpni_extract_early_drop() to -+ * get the early drop configuration -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpni_get_tx_tc_early_drop(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ uint64_t early_drop_iova); -+ -+/** -+ * enum dpni_dest - DPNI destination types -+ * @DPNI_DEST_NONE: Unassigned destination; The queue is set in parked mode and -+ * does not generate FQDAN notifications; user is expected to -+ * dequeue from the queue based on polling or other user-defined -+ * method -+ * @DPNI_DEST_DPIO: The queue is set in schedule mode and generates FQDAN -+ * notifications to the specified DPIO; user is expected to dequeue -+ * from the queue only after notification is received -+ * @DPNI_DEST_DPCON: The queue is set in schedule mode and does not generate -+ * FQDAN notifications, but is connected to the specified DPCON -+ * object; user is expected to dequeue from the DPCON channel -+ */ -+enum dpni_dest { -+ DPNI_DEST_NONE = 0, -+ DPNI_DEST_DPIO = 1, -+ DPNI_DEST_DPCON = 2 -+}; -+ -+/** -+ * struct dpni_dest_cfg - Structure representing DPNI destination parameters -+ * @dest_type: Destination type -+ * @dest_id: Either DPIO ID or DPCON ID, depending on the destination type -+ * @priority: Priority selection within the DPIO or DPCON channel; valid values -+ * are 0-1 or 0-7, depending on the number of priorities in that -+ * channel; not relevant for 'DPNI_DEST_NONE' option -+ */ -+struct dpni_dest_cfg { -+ enum dpni_dest dest_type; -+ int dest_id; -+ uint8_t priority; -+}; -+ -+/* DPNI congestion options */ -+ -+/** -+ * CSCN message is written to message_iova once entering a -+ * congestion state (see 'threshold_entry') -+ */ -+#define DPNI_CONG_OPT_WRITE_MEM_ON_ENTER 0x00000001 -+/** -+ * CSCN message is written to message_iova once exiting a -+ * congestion state (see 'threshold_exit') -+ */ -+#define DPNI_CONG_OPT_WRITE_MEM_ON_EXIT 0x00000002 -+/** -+ * CSCN write will attempt to allocate into a cache (coherent write); -+ * valid only if 'DPNI_CONG_OPT_WRITE_MEM_' is selected -+ */ -+#define DPNI_CONG_OPT_COHERENT_WRITE 0x00000004 -+/** -+ * if 'dest_cfg.dest_type != DPNI_DEST_NONE' CSCN message is sent to -+ * DPIO/DPCON's WQ channel once entering a congestion state -+ * (see 'threshold_entry') -+ */ -+#define DPNI_CONG_OPT_NOTIFY_DEST_ON_ENTER 0x00000008 -+/** -+ * if 'dest_cfg.dest_type != DPNI_DEST_NONE' CSCN message is sent to -+ * DPIO/DPCON's WQ channel once exiting a congestion state -+ * (see 'threshold_exit') -+ */ -+#define DPNI_CONG_OPT_NOTIFY_DEST_ON_EXIT 0x00000010 -+/** -+ * if 'dest_cfg.dest_type != DPNI_DEST_NONE' when the CSCN is written to the -+ * sw-portal's DQRR, the DQRI interrupt is asserted immediately (if enabled) -+ */ -+#define DPNI_CONG_OPT_INTR_COALESCING_DISABLED 0x00000020 -+ -+/** -+ * struct dpni_congestion_notification_cfg - congestion notification -+ * configuration -+ * @units: units type -+ * @threshold_entry: above this threshold we enter a congestion state. -+ * set it to '0' to disable it -+ * @threshold_exit: below this threshold we exit the congestion state. -+ * @message_ctx: The context that will be part of the CSCN message -+ * @message_iova: I/O virtual address (must be in DMA-able memory), -+ * must be 16B aligned; valid only if 'DPNI_CONG_OPT_WRITE_MEM_' is -+ * contained in 'options' -+ * @dest_cfg: CSCN can be send to either DPIO or DPCON WQ channel -+ * @options: Mask of available options; use 'DPNI_CONG_OPT_' values -+ */ -+ -+struct dpni_congestion_notification_cfg { -+ enum dpni_congestion_unit units; -+ uint32_t threshold_entry; -+ uint32_t threshold_exit; -+ uint64_t message_ctx; -+ uint64_t message_iova; -+ struct dpni_dest_cfg dest_cfg; -+ uint16_t options; -+}; -+ -+/** -+ * dpni_set_rx_tc_congestion_notification() - Set Rx traffic class congestion -+ * notification configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @tc_id: Traffic class selection (0-7) -+ * @cfg: congestion notification configuration -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpni_set_rx_tc_congestion_notification(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ const struct dpni_congestion_notification_cfg *cfg); -+ -+/** -+ * dpni_get_rx_tc_congestion_notification() - Get Rx traffic class congestion -+ * notification configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @tc_id: Traffic class selection (0-7) -+ * @cfg: congestion notification configuration -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpni_get_rx_tc_congestion_notification(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ struct dpni_congestion_notification_cfg *cfg); -+ -+/** -+ * dpni_set_tx_tc_congestion_notification() - Set Tx traffic class congestion -+ * notification configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @tc_id: Traffic class selection (0-7) -+ * @cfg: congestion notification configuration -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpni_set_tx_tc_congestion_notification(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ const struct dpni_congestion_notification_cfg *cfg); -+ -+/** -+ * dpni_get_tx_tc_congestion_notification() - Get Tx traffic class congestion -+ * notification configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @tc_id: Traffic class selection (0-7) -+ * @cfg: congestion notification configuration -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpni_get_tx_tc_congestion_notification(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ struct dpni_congestion_notification_cfg *cfg); -+ -+/** -+ * enum dpni_flc_type - DPNI FLC types -+ * @DPNI_FLC_USER_DEFINED: select the FLC to be used for user defined value -+ * @DPNI_FLC_STASH: select the FLC to be used for stash control -+ */ -+enum dpni_flc_type { -+ DPNI_FLC_USER_DEFINED = 0, -+ DPNI_FLC_STASH = 1, -+}; -+ -+/** -+ * enum dpni_stash_size - DPNI FLC stashing size -+ * @DPNI_STASH_SIZE_0B: no stash -+ * @DPNI_STASH_SIZE_64B: stashes 64 bytes -+ * @DPNI_STASH_SIZE_128B: stashes 128 bytes -+ * @DPNI_STASH_SIZE_192B: stashes 192 bytes -+ */ -+enum dpni_stash_size { -+ DPNI_STASH_SIZE_0B = 0, -+ DPNI_STASH_SIZE_64B = 1, -+ DPNI_STASH_SIZE_128B = 2, -+ DPNI_STASH_SIZE_192B = 3, -+}; -+ -+/* DPNI FLC stash options */ -+ -+/** -+ * stashes the whole annotation area (up to 192 bytes) -+ */ -+#define DPNI_FLC_STASH_FRAME_ANNOTATION 0x00000001 -+ -+/** -+ * struct dpni_flc_cfg - Structure representing DPNI FLC configuration -+ * @flc_type: FLC type -+ * @options: Mask of available options; -+ * use 'DPNI_FLC_STASH_' values -+ * @frame_data_size: Size of frame data to be stashed -+ * @flow_context_size: Size of flow context to be stashed -+ * @flow_context: 1. In case flc_type is 'DPNI_FLC_USER_DEFINED': -+ * this value will be provided in the frame descriptor -+ * (FD[FLC]) -+ * 2. In case flc_type is 'DPNI_FLC_STASH': -+ * this value will be I/O virtual address of the -+ * flow-context; -+ * Must be cacheline-aligned and DMA-able memory -+ */ -+struct dpni_flc_cfg { -+ enum dpni_flc_type flc_type; -+ uint32_t options; -+ enum dpni_stash_size frame_data_size; -+ enum dpni_stash_size flow_context_size; -+ uint64_t flow_context; -+}; -+ -+/** -+ * DPNI queue modification options -+ */ -+ -+/** -+ * Select to modify the user's context associated with the queue -+ */ -+#define DPNI_QUEUE_OPT_USER_CTX 0x00000001 -+/** -+ * Select to modify the queue's destination -+ */ -+#define DPNI_QUEUE_OPT_DEST 0x00000002 -+/** Select to modify the flow-context parameters; -+ * not applicable for Tx-conf/Err queues as the FD comes from the user -+ */ -+#define DPNI_QUEUE_OPT_FLC 0x00000004 -+/** -+ * Select to modify the queue's order preservation -+ */ -+#define DPNI_QUEUE_OPT_ORDER_PRESERVATION 0x00000008 -+/* Select to modify the queue's tail-drop threshold */ -+#define DPNI_QUEUE_OPT_TAILDROP_THRESHOLD 0x00000010 -+ -+/** -+ * struct dpni_queue_cfg - Structure representing queue configuration -+ * @options: Flags representing the suggested modifications to the queue; -+ * Use any combination of 'DPNI_QUEUE_OPT_' flags -+ * @user_ctx: User context value provided in the frame descriptor of each -+ * dequeued frame; valid only if 'DPNI_QUEUE_OPT_USER_CTX' -+ * is contained in 'options' -+ * @dest_cfg: Queue destination parameters; -+ * valid only if 'DPNI_QUEUE_OPT_DEST' is contained in 'options' -+ * @flc_cfg: Flow context configuration; in case the TC's distribution -+ * is either NONE or HASH the FLC's settings of flow#0 are used. -+ * in the case of FS (flow-steering) the flow's FLC settings -+ * are used. -+ * valid only if 'DPNI_QUEUE_OPT_FLC' is contained in 'options' -+ * @order_preservation_en: enable/disable order preservation; -+ * valid only if 'DPNI_QUEUE_OPT_ORDER_PRESERVATION' is contained -+ * in 'options' -+ * @tail_drop_threshold: set the queue's tail drop threshold in bytes; -+ * '0' value disable the threshold; maximum value is 0xE000000; -+ * valid only if 'DPNI_QUEUE_OPT_TAILDROP_THRESHOLD' is contained -+ * in 'options' -+ */ -+struct dpni_queue_cfg { -+ uint32_t options; -+ uint64_t user_ctx; -+ struct dpni_dest_cfg dest_cfg; -+ struct dpni_flc_cfg flc_cfg; -+ int order_preservation_en; -+ uint32_t tail_drop_threshold; -+}; -+ -+/** -+ * struct dpni_queue_attr - Structure representing queue attributes -+ * @user_ctx: User context value provided in the frame descriptor of each -+ * dequeued frame -+ * @dest_cfg: Queue destination configuration -+ * @flc_cfg: Flow context configuration -+ * @order_preservation_en: enable/disable order preservation -+ * @tail_drop_threshold: queue's tail drop threshold in bytes; -+ * @fqid: Virtual fqid value to be used for dequeue operations -+ */ -+struct dpni_queue_attr { -+ uint64_t user_ctx; -+ struct dpni_dest_cfg dest_cfg; -+ struct dpni_flc_cfg flc_cfg; -+ int order_preservation_en; -+ uint32_t tail_drop_threshold; -+ -+ uint32_t fqid; -+}; -+ -+/** -+ * DPNI Tx flow modification options -+ */ -+ -+/** -+ * Select to modify the settings for dedicate Tx confirmation/error -+ */ -+#define DPNI_TX_FLOW_OPT_TX_CONF_ERROR 0x00000001 -+/** -+ * Select to modify the L3 checksum generation setting -+ */ -+#define DPNI_TX_FLOW_OPT_L3_CHKSUM_GEN 0x00000010 -+/** -+ * Select to modify the L4 checksum generation setting -+ */ -+#define DPNI_TX_FLOW_OPT_L4_CHKSUM_GEN 0x00000020 -+ -+/** -+ * struct dpni_tx_flow_cfg - Structure representing Tx flow configuration -+ * @options: Flags representing the suggested modifications to the Tx flow; -+ * Use any combination 'DPNI_TX_FLOW_OPT_' flags -+ * @use_common_tx_conf_queue: Set to '1' to use the common (default) Tx -+ * confirmation and error queue; Set to '0' to use the private -+ * Tx confirmation and error queue; valid only if -+ * 'DPNI_OPT_PRIVATE_TX_CONF_ERROR_DISABLED' wasn't set at DPNI creation -+ * and 'DPNI_TX_FLOW_OPT_TX_CONF_ERROR' is contained in 'options' -+ * @l3_chksum_gen: Set to '1' to enable L3 checksum generation; '0' to disable; -+ * valid only if 'DPNI_TX_FLOW_OPT_L3_CHKSUM_GEN' is contained in 'options' -+ * @l4_chksum_gen: Set to '1' to enable L4 checksum generation; '0' to disable; -+ * valid only if 'DPNI_TX_FLOW_OPT_L4_CHKSUM_GEN' is contained in 'options' -+ */ -+struct dpni_tx_flow_cfg { -+ uint32_t options; -+ int use_common_tx_conf_queue; -+ int l3_chksum_gen; -+ int l4_chksum_gen; -+}; -+ -+/** -+ * dpni_set_tx_flow() - Set Tx flow configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @flow_id: Provides (or returns) the sender's flow ID; -+ * for each new sender set (*flow_id) to 'DPNI_NEW_FLOW_ID' to generate -+ * a new flow_id; this ID should be used as the QDBIN argument -+ * in enqueue operations -+ * @cfg: Tx flow configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_tx_flow(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t *flow_id, -+ const struct dpni_tx_flow_cfg *cfg); -+ -+/** -+ * struct dpni_tx_flow_attr - Structure representing Tx flow attributes -+ * @use_common_tx_conf_queue: '1' if using common (default) Tx confirmation and -+ * error queue; '0' if using private Tx confirmation and error queue -+ * @l3_chksum_gen: '1' if L3 checksum generation is enabled; '0' if disabled -+ * @l4_chksum_gen: '1' if L4 checksum generation is enabled; '0' if disabled -+ */ -+struct dpni_tx_flow_attr { -+ int use_common_tx_conf_queue; -+ int l3_chksum_gen; -+ int l4_chksum_gen; -+}; -+ -+/** -+ * dpni_get_tx_flow() - Get Tx flow attributes -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @flow_id: The sender's flow ID, as returned by the -+ * dpni_set_tx_flow() function -+ * @attr: Returned Tx flow attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_tx_flow(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t flow_id, -+ struct dpni_tx_flow_attr *attr); -+ -+/** -+ * struct dpni_tx_conf_cfg - Structure representing Tx conf configuration -+ * @errors_only: Set to '1' to report back only error frames; -+ * Set to '0' to confirm transmission/error for all transmitted frames; -+ * @queue_cfg: Queue configuration -+ */ -+struct dpni_tx_conf_cfg { -+ int errors_only; -+ struct dpni_queue_cfg queue_cfg; -+}; -+ -+/** -+ * dpni_set_tx_conf() - Set Tx confirmation and error queue configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @flow_id: The sender's flow ID, as returned by the -+ * dpni_set_tx_flow() function; -+ * use 'DPNI_COMMON_TX_CONF' for common tx-conf -+ * @cfg: Queue configuration -+ * -+ * If either 'DPNI_OPT_TX_CONF_DISABLED' or -+ * 'DPNI_OPT_PRIVATE_TX_CONF_ERROR_DISABLED' were selected at DPNI creation, -+ * this function can ONLY be used with 'flow_id == DPNI_COMMON_TX_CONF'; -+ * i.e. only serve the common tx-conf-err queue; -+ * if 'DPNI_OPT_TX_CONF_DISABLED' was selected, only error frames are reported -+ * back - successfully transmitted frames are not confirmed. Otherwise, all -+ * transmitted frames are sent for confirmation. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_tx_conf(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t flow_id, -+ const struct dpni_tx_conf_cfg *cfg); -+ -+/** -+ * struct dpni_tx_conf_attr - Structure representing Tx conf attributes -+ * @errors_only: '1' if only error frames are reported back; '0' if all -+ * transmitted frames are confirmed -+ * @queue_attr: Queue attributes -+ */ -+struct dpni_tx_conf_attr { -+ int errors_only; -+ struct dpni_queue_attr queue_attr; -+}; -+ -+/** -+ * dpni_get_tx_conf() - Get Tx confirmation and error queue attributes -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @flow_id: The sender's flow ID, as returned by the -+ * dpni_set_tx_flow() function; -+ * use 'DPNI_COMMON_TX_CONF' for common tx-conf -+ * @attr: Returned tx-conf attributes -+ * -+ * If either 'DPNI_OPT_TX_CONF_DISABLED' or -+ * 'DPNI_OPT_PRIVATE_TX_CONF_ERROR_DISABLED' were selected at DPNI creation, -+ * this function can ONLY be used with 'flow_id == DPNI_COMMON_TX_CONF'; -+ * i.e. only serve the common tx-conf-err queue; -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_tx_conf(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t flow_id, -+ struct dpni_tx_conf_attr *attr); -+ -+/** -+ * dpni_set_tx_conf_congestion_notification() - Set Tx conf congestion -+ * notification configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @flow_id: The sender's flow ID, as returned by the -+ * dpni_set_tx_flow() function; -+ * use 'DPNI_COMMON_TX_CONF' for common tx-conf -+ * @cfg: congestion notification configuration -+ * -+ * If either 'DPNI_OPT_TX_CONF_DISABLED' or -+ * 'DPNI_OPT_PRIVATE_TX_CONF_ERROR_DISABLED' were selected at DPNI creation, -+ * this function can ONLY be used with 'flow_id == DPNI_COMMON_TX_CONF'; -+ * i.e. only serve the common tx-conf-err queue; -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpni_set_tx_conf_congestion_notification(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t flow_id, -+ const struct dpni_congestion_notification_cfg *cfg); -+ -+/** -+ * dpni_get_tx_conf_congestion_notification() - Get Tx conf congestion -+ * notification configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @flow_id: The sender's flow ID, as returned by the -+ * dpni_set_tx_flow() function; -+ * use 'DPNI_COMMON_TX_CONF' for common tx-conf -+ * @cfg: congestion notification -+ * -+ * If either 'DPNI_OPT_TX_CONF_DISABLED' or -+ * 'DPNI_OPT_PRIVATE_TX_CONF_ERROR_DISABLED' were selected at DPNI creation, -+ * this function can ONLY be used with 'flow_id == DPNI_COMMON_TX_CONF'; -+ * i.e. only serve the common tx-conf-err queue; -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpni_get_tx_conf_congestion_notification(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint16_t flow_id, -+ struct dpni_congestion_notification_cfg *cfg); -+ -+/** -+ * dpni_set_tx_conf_revoke() - Tx confirmation revocation -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @revoke: revoke or not -+ * -+ * This function is useful only when 'DPNI_OPT_TX_CONF_DISABLED' is not -+ * selected at DPNI creation. -+ * Calling this function with 'revoke' set to '1' disables all transmit -+ * confirmation (including the private confirmation queues), regardless of -+ * previous settings; Note that in this case, Tx error frames are still -+ * enqueued to the general transmit errors queue. -+ * Calling this function with 'revoke' set to '0' restores the previous -+ * settings for both general and private transmit confirmation. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_tx_conf_revoke(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int revoke); -+ -+/** -+ * dpni_set_rx_flow() - Set Rx flow configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @tc_id: Traffic class selection (0-7); -+ * use 'DPNI_ALL_TCS' to set all TCs and all flows -+ * @flow_id: Rx flow id within the traffic class; use -+ * 'DPNI_ALL_TC_FLOWS' to set all flows within -+ * this tc_id; ignored if tc_id is set to -+ * 'DPNI_ALL_TCS'; -+ * @cfg: Rx flow configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_rx_flow(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ uint16_t flow_id, -+ const struct dpni_queue_cfg *cfg); -+ -+/** -+ * dpni_get_rx_flow() - Get Rx flow attributes -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @tc_id: Traffic class selection (0-7) -+ * @flow_id: Rx flow id within the traffic class -+ * @attr: Returned Rx flow attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_rx_flow(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ uint16_t flow_id, -+ struct dpni_queue_attr *attr); -+ -+/** -+ * dpni_set_rx_err_queue() - Set Rx error queue configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @cfg: Queue configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_rx_err_queue(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_queue_cfg *cfg); -+ -+/** -+ * dpni_get_rx_err_queue() - Get Rx error queue attributes -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @attr: Returned Queue attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_get_rx_err_queue(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpni_queue_attr *attr); -+ -+/** -+ * struct dpni_qos_tbl_cfg - Structure representing QOS table configuration -+ * @key_cfg_iova: I/O virtual address of 256 bytes DMA-able memory filled with -+ * key extractions to be used as the QoS criteria by calling -+ * dpni_prepare_key_cfg() -+ * @discard_on_miss: Set to '1' to discard frames in case of no match (miss); -+ * '0' to use the 'default_tc' in such cases -+ * @default_tc: Used in case of no-match and 'discard_on_miss'= 0 -+ */ -+struct dpni_qos_tbl_cfg { -+ uint64_t key_cfg_iova; -+ int discard_on_miss; -+ uint8_t default_tc; -+}; -+ -+/** -+ * dpni_set_qos_table() - Set QoS mapping table -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @cfg: QoS table configuration -+ * -+ * This function and all QoS-related functions require that -+ *'max_tcs > 1' was set at DPNI creation. -+ * -+ * warning: Before calling this function, call dpni_prepare_key_cfg() to -+ * prepare the key_cfg_iova parameter -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_qos_table(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_qos_tbl_cfg *cfg); -+ -+/** -+ * struct dpni_rule_cfg - Rule configuration for table lookup -+ * @key_iova: I/O virtual address of the key (must be in DMA-able memory) -+ * @mask_iova: I/O virtual address of the mask (must be in DMA-able memory) -+ * @key_size: key and mask size (in bytes) -+ */ -+struct dpni_rule_cfg { -+ uint64_t key_iova; -+ uint64_t mask_iova; -+ uint8_t key_size; -+}; -+ -+/** -+ * dpni_add_qos_entry() - Add QoS mapping entry (to select a traffic class) -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @cfg: QoS rule to add -+ * @tc_id: Traffic class selection (0-7) -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_add_qos_entry(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_rule_cfg *cfg, -+ uint8_t tc_id); -+ -+/** -+ * dpni_remove_qos_entry() - Remove QoS mapping entry -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @cfg: QoS rule to remove -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_remove_qos_entry(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dpni_rule_cfg *cfg); -+ -+/** -+ * dpni_clear_qos_table() - Clear all QoS mapping entries -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * -+ * Following this function call, all frames are directed to -+ * the default traffic class (0) -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_clear_qos_table(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * dpni_add_fs_entry() - Add Flow Steering entry for a specific traffic class -+ * (to select a flow ID) -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @tc_id: Traffic class selection (0-7) -+ * @cfg: Flow steering rule to add -+ * @flow_id: Flow id selection (must be smaller than the -+ * distribution size of the traffic class) -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_add_fs_entry(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ const struct dpni_rule_cfg *cfg, -+ uint16_t flow_id); -+ -+/** -+ * dpni_remove_fs_entry() - Remove Flow Steering entry from a specific -+ * traffic class -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @tc_id: Traffic class selection (0-7) -+ * @cfg: Flow steering rule to remove -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_remove_fs_entry(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id, -+ const struct dpni_rule_cfg *cfg); -+ -+/** -+ * dpni_clear_fs_entries() - Clear all Flow Steering entries of a specific -+ * traffic class -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @tc_id: Traffic class selection (0-7) -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_clear_fs_entries(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t tc_id); -+ -+/** -+ * dpni_set_vlan_insertion() - Enable/disable VLAN insertion for egress frames -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @en: Set to '1' to enable; '0' to disable -+ * -+ * Requires that the 'DPNI_OPT_VLAN_MANIPULATION' option is set -+ * at DPNI creation. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_vlan_insertion(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en); -+ -+/** -+ * dpni_set_vlan_removal() - Enable/disable VLAN removal for ingress frames -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @en: Set to '1' to enable; '0' to disable -+ * -+ * Requires that the 'DPNI_OPT_VLAN_MANIPULATION' option is set -+ * at DPNI creation. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_vlan_removal(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en); -+ -+/** -+ * dpni_set_ipr() - Enable/disable IP reassembly of ingress frames -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @en: Set to '1' to enable; '0' to disable -+ * -+ * Requires that the 'DPNI_OPT_IPR' option is set at DPNI creation. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_ipr(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en); -+ -+/** -+ * dpni_set_ipf() - Enable/disable IP fragmentation of egress frames -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPNI object -+ * @en: Set to '1' to enable; '0' to disable -+ * -+ * Requires that the 'DPNI_OPT_IPF' option is set at DPNI -+ * creation. Fragmentation is performed according to MTU value -+ * set by dpni_set_mtu() function -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpni_set_ipf(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int en); -+ -+#endif /* __FSL_DPNI_H */ -diff --git a/drivers/staging/fsl-dpaa2/mac/Kconfig b/drivers/staging/fsl-dpaa2/mac/Kconfig -new file mode 100644 -index 0000000..174a9cd ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/mac/Kconfig -@@ -0,0 +1,24 @@ -+config FSL_DPAA2_MAC -+ tristate "DPAA2 MAC / PHY interface" -+ depends on FSL_MC_BUS && FSL_DPAA2 -+ select MDIO_BUS_MUX_MMIOREG -+ select FSL_XGMAC_MDIO -+ select FIXED_PHY -+ ---help--- -+ Prototype driver for DPAA2 MAC / PHY interface object. -+ This driver works as a proxy between phylib including phy drivers and -+ the MC firmware. It receives updates on link state changes from PHY -+ lib and forwards them to MC and receives interrupt from MC whenever -+ a request is made to change the link state. -+ -+ -+config FSL_DPAA2_MAC_NETDEVS -+ bool "Expose net interfaces for PHYs" -+ default n -+ depends on FSL_DPAA2_MAC -+ ---help--- -+ Exposes macX net interfaces which allow direct control over MACs and -+ PHYs. -+ . -+ Leave disabled if unsure. -+ -diff --git a/drivers/staging/fsl-dpaa2/mac/Makefile b/drivers/staging/fsl-dpaa2/mac/Makefile -new file mode 100644 -index 0000000..bda9410 ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/mac/Makefile -@@ -0,0 +1,10 @@ -+ -+obj-$(CONFIG_FSL_DPAA2_MAC) += dpaa2-mac.o -+ -+dpaa2-mac-objs := mac.o dpmac.o -+ -+all: -+ make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules -+ -+clean: -+ make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean -diff --git a/drivers/staging/fsl-dpaa2/mac/dpmac-cmd.h b/drivers/staging/fsl-dpaa2/mac/dpmac-cmd.h -new file mode 100644 -index 0000000..dc00590 ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/mac/dpmac-cmd.h -@@ -0,0 +1,195 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef _FSL_DPMAC_CMD_H -+#define _FSL_DPMAC_CMD_H -+ -+/* DPMAC Version */ -+#define DPMAC_VER_MAJOR 3 -+#define DPMAC_VER_MINOR 2 -+ -+/* Command IDs */ -+#define DPMAC_CMDID_CLOSE 0x800 -+#define DPMAC_CMDID_OPEN 0x80c -+#define DPMAC_CMDID_CREATE 0x90c -+#define DPMAC_CMDID_DESTROY 0x900 -+ -+#define DPMAC_CMDID_GET_ATTR 0x004 -+#define DPMAC_CMDID_RESET 0x005 -+ -+#define DPMAC_CMDID_SET_IRQ 0x010 -+#define DPMAC_CMDID_GET_IRQ 0x011 -+#define DPMAC_CMDID_SET_IRQ_ENABLE 0x012 -+#define DPMAC_CMDID_GET_IRQ_ENABLE 0x013 -+#define DPMAC_CMDID_SET_IRQ_MASK 0x014 -+#define DPMAC_CMDID_GET_IRQ_MASK 0x015 -+#define DPMAC_CMDID_GET_IRQ_STATUS 0x016 -+#define DPMAC_CMDID_CLEAR_IRQ_STATUS 0x017 -+ -+#define DPMAC_CMDID_MDIO_READ 0x0c0 -+#define DPMAC_CMDID_MDIO_WRITE 0x0c1 -+#define DPMAC_CMDID_GET_LINK_CFG 0x0c2 -+#define DPMAC_CMDID_SET_LINK_STATE 0x0c3 -+#define DPMAC_CMDID_GET_COUNTER 0x0c4 -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_CREATE(cmd, cfg) \ -+ MC_CMD_OP(cmd, 0, 0, 32, int, cfg->mac_id) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_OPEN(cmd, dpmac_id) \ -+ MC_CMD_OP(cmd, 0, 0, 32, int, dpmac_id) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_SET_IRQ(cmd, irq_index, irq_cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, irq_index);\ -+ MC_CMD_OP(cmd, 0, 32, 32, uint32_t, irq_cfg->val);\ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, irq_cfg->addr); \ -+ MC_CMD_OP(cmd, 2, 0, 32, int, irq_cfg->irq_num); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_GET_IRQ(cmd, irq_index) \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_RSP_GET_IRQ(cmd, type, irq_cfg) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, irq_cfg->val); \ -+ MC_RSP_OP(cmd, 1, 0, 64, uint64_t, irq_cfg->addr); \ -+ MC_RSP_OP(cmd, 2, 0, 32, int, irq_cfg->irq_num); \ -+ MC_RSP_OP(cmd, 2, 32, 32, int, type); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_SET_IRQ_ENABLE(cmd, irq_index, en) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, en); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_GET_IRQ_ENABLE(cmd, irq_index) \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_RSP_GET_IRQ_ENABLE(cmd, en) \ -+ MC_RSP_OP(cmd, 0, 0, 8, uint8_t, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_SET_IRQ_MASK(cmd, irq_index, mask) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, mask);\ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_GET_IRQ_MASK(cmd, irq_index) \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_RSP_GET_IRQ_MASK(cmd, mask) \ -+ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, mask) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_GET_IRQ_STATUS(cmd, irq_index, status) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, status);\ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_RSP_GET_IRQ_STATUS(cmd, status) \ -+ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, status) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_CLEAR_IRQ_STATUS(cmd, irq_index, status) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, status); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_RSP_GET_ATTRIBUTES(cmd, attr) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 32, int, attr->phy_id);\ -+ MC_RSP_OP(cmd, 0, 32, 32, int, attr->id);\ -+ MC_RSP_OP(cmd, 1, 0, 16, uint16_t, attr->version.major);\ -+ MC_RSP_OP(cmd, 1, 16, 16, uint16_t, attr->version.minor);\ -+ MC_RSP_OP(cmd, 1, 32, 8, enum dpmac_link_type, attr->link_type);\ -+ MC_RSP_OP(cmd, 1, 40, 8, enum dpmac_eth_if, attr->eth_if);\ -+ MC_RSP_OP(cmd, 2, 0, 32, uint32_t, attr->max_rate);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_MDIO_READ(cmd, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, cfg->phy_addr); \ -+ MC_CMD_OP(cmd, 0, 8, 8, uint8_t, cfg->reg); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_RSP_MDIO_READ(cmd, data) \ -+ MC_RSP_OP(cmd, 0, 16, 16, uint16_t, data) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_MDIO_WRITE(cmd, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, cfg->phy_addr); \ -+ MC_CMD_OP(cmd, 0, 8, 8, uint8_t, cfg->reg); \ -+ MC_CMD_OP(cmd, 0, 16, 16, uint16_t, cfg->data); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_RSP_GET_LINK_CFG(cmd, cfg) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 64, uint64_t, cfg->options); \ -+ MC_RSP_OP(cmd, 1, 0, 32, uint32_t, cfg->rate); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_SET_LINK_STATE(cmd, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 64, uint64_t, cfg->options); \ -+ MC_CMD_OP(cmd, 1, 0, 32, uint32_t, cfg->rate); \ -+ MC_CMD_OP(cmd, 2, 0, 1, int, cfg->up); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_GET_COUNTER(cmd, type) \ -+ MC_CMD_OP(cmd, 0, 0, 8, enum dpmac_counter, type) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_RSP_GET_COUNTER(cmd, counter) \ -+ MC_RSP_OP(cmd, 1, 0, 64, uint64_t, counter) -+ -+#endif /* _FSL_DPMAC_CMD_H */ -diff --git a/drivers/staging/fsl-dpaa2/mac/dpmac.c b/drivers/staging/fsl-dpaa2/mac/dpmac.c -new file mode 100644 -index 0000000..fc23b40 ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/mac/dpmac.c -@@ -0,0 +1,422 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#include "../../fsl-mc/include/mc-sys.h" -+#include "../../fsl-mc/include/mc-cmd.h" -+#include "dpmac.h" -+#include "dpmac-cmd.h" -+ -+int dpmac_open(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ int dpmac_id, -+ uint16_t *token) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_OPEN, -+ cmd_flags, -+ 0); -+ DPMAC_CMD_OPEN(cmd, dpmac_id); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *token = MC_CMD_HDR_READ_TOKEN(cmd.header); -+ -+ return err; -+} -+ -+int dpmac_close(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_CLOSE, cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpmac_create(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ const struct dpmac_cfg *cfg, -+ uint16_t *token) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_CREATE, -+ cmd_flags, -+ 0); -+ DPMAC_CMD_CREATE(cmd, cfg); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *token = MC_CMD_HDR_READ_TOKEN(cmd.header); -+ -+ return 0; -+} -+ -+int dpmac_destroy(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_DESTROY, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpmac_set_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ struct dpmac_irq_cfg *irq_cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_SET_IRQ, -+ cmd_flags, -+ token); -+ DPMAC_CMD_SET_IRQ(cmd, irq_index, irq_cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpmac_get_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ int *type, -+ struct dpmac_irq_cfg *irq_cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_GET_IRQ, -+ cmd_flags, -+ token); -+ DPMAC_CMD_GET_IRQ(cmd, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPMAC_RSP_GET_IRQ(cmd, *type, irq_cfg); -+ -+ return 0; -+} -+ -+int dpmac_set_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t en) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_SET_IRQ_ENABLE, -+ cmd_flags, -+ token); -+ DPMAC_CMD_SET_IRQ_ENABLE(cmd, irq_index, en); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpmac_get_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t *en) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_GET_IRQ_ENABLE, -+ cmd_flags, -+ token); -+ DPMAC_CMD_GET_IRQ_ENABLE(cmd, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPMAC_RSP_GET_IRQ_ENABLE(cmd, *en); -+ -+ return 0; -+} -+ -+int dpmac_set_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t mask) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_SET_IRQ_MASK, -+ cmd_flags, -+ token); -+ DPMAC_CMD_SET_IRQ_MASK(cmd, irq_index, mask); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpmac_get_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *mask) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_GET_IRQ_MASK, -+ cmd_flags, -+ token); -+ DPMAC_CMD_GET_IRQ_MASK(cmd, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPMAC_RSP_GET_IRQ_MASK(cmd, *mask); -+ -+ return 0; -+} -+ -+int dpmac_get_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *status) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_GET_IRQ_STATUS, -+ cmd_flags, -+ token); -+ DPMAC_CMD_GET_IRQ_STATUS(cmd, irq_index, *status); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPMAC_RSP_GET_IRQ_STATUS(cmd, *status); -+ -+ return 0; -+} -+ -+int dpmac_clear_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t status) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_CLEAR_IRQ_STATUS, -+ cmd_flags, -+ token); -+ DPMAC_CMD_CLEAR_IRQ_STATUS(cmd, irq_index, status); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpmac_get_attributes(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpmac_attr *attr) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_GET_ATTR, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPMAC_RSP_GET_ATTRIBUTES(cmd, attr); -+ -+ return 0; -+} -+ -+int dpmac_mdio_read(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpmac_mdio_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_MDIO_READ, -+ cmd_flags, -+ token); -+ DPMAC_CMD_MDIO_READ(cmd, cfg); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPMAC_RSP_MDIO_READ(cmd, cfg->data); -+ -+ return 0; -+} -+ -+int dpmac_mdio_write(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpmac_mdio_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_MDIO_WRITE, -+ cmd_flags, -+ token); -+ DPMAC_CMD_MDIO_WRITE(cmd, cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpmac_get_link_cfg(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpmac_link_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ int err = 0; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_GET_LINK_CFG, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ DPMAC_RSP_GET_LINK_CFG(cmd, cfg); -+ -+ return 0; -+} -+ -+int dpmac_set_link_state(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpmac_link_state *link_state) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_SET_LINK_STATE, -+ cmd_flags, -+ token); -+ DPMAC_CMD_SET_LINK_STATE(cmd, link_state); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpmac_get_counter(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ enum dpmac_counter type, -+ uint64_t *counter) -+{ -+ struct mc_command cmd = { 0 }; -+ int err = 0; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMAC_CMDID_GET_COUNTER, -+ cmd_flags, -+ token); -+ DPMAC_CMD_GET_COUNTER(cmd, type); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ DPMAC_RSP_GET_COUNTER(cmd, *counter); -+ -+ return 0; -+} -diff --git a/drivers/staging/fsl-dpaa2/mac/dpmac.h b/drivers/staging/fsl-dpaa2/mac/dpmac.h -new file mode 100644 -index 0000000..ad27772 ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/mac/dpmac.h -@@ -0,0 +1,593 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef __FSL_DPMAC_H -+#define __FSL_DPMAC_H -+ -+/* Data Path MAC API -+ * Contains initialization APIs and runtime control APIs for DPMAC -+ */ -+ -+struct fsl_mc_io; -+ -+/** -+ * dpmac_open() - Open a control session for the specified object. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @dpmac_id: DPMAC unique ID -+ * @token: Returned token; use in subsequent API calls -+ * -+ * This function can be used to open a control session for an -+ * already created object; an object may have been declared in -+ * the DPL or by calling the dpmac_create function. -+ * This function returns a unique authentication token, -+ * associated with the specific object ID and the specific MC -+ * portal; this token must be used in all subsequent commands for -+ * this specific object -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_open(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ int dpmac_id, -+ uint16_t *token); -+ -+/** -+ * dpmac_close() - Close the control session of the object -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMAC object -+ * -+ * After this function is called, no further operations are -+ * allowed on the object without opening a new control session. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_close(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * enum dpmac_link_type - DPMAC link type -+ * @DPMAC_LINK_TYPE_NONE: No link -+ * @DPMAC_LINK_TYPE_FIXED: Link is fixed type -+ * @DPMAC_LINK_TYPE_PHY: Link by PHY ID -+ * @DPMAC_LINK_TYPE_BACKPLANE: Backplane link type -+ */ -+enum dpmac_link_type { -+ DPMAC_LINK_TYPE_NONE, -+ DPMAC_LINK_TYPE_FIXED, -+ DPMAC_LINK_TYPE_PHY, -+ DPMAC_LINK_TYPE_BACKPLANE -+}; -+ -+/** -+ * enum dpmac_eth_if - DPMAC Ethrnet interface -+ * @DPMAC_ETH_IF_MII: MII interface -+ * @DPMAC_ETH_IF_RMII: RMII interface -+ * @DPMAC_ETH_IF_SMII: SMII interface -+ * @DPMAC_ETH_IF_GMII: GMII interface -+ * @DPMAC_ETH_IF_RGMII: RGMII interface -+ * @DPMAC_ETH_IF_SGMII: SGMII interface -+ * @DPMAC_ETH_IF_QSGMII: QSGMII interface -+ * @DPMAC_ETH_IF_XAUI: XAUI interface -+ * @DPMAC_ETH_IF_XFI: XFI interface -+ */ -+enum dpmac_eth_if { -+ DPMAC_ETH_IF_MII, -+ DPMAC_ETH_IF_RMII, -+ DPMAC_ETH_IF_SMII, -+ DPMAC_ETH_IF_GMII, -+ DPMAC_ETH_IF_RGMII, -+ DPMAC_ETH_IF_SGMII, -+ DPMAC_ETH_IF_QSGMII, -+ DPMAC_ETH_IF_XAUI, -+ DPMAC_ETH_IF_XFI -+}; -+ -+/** -+ * struct dpmac_cfg - Structure representing DPMAC configuration -+ * @mac_id: Represents the Hardware MAC ID; in case of multiple WRIOP, -+ * the MAC IDs are continuous. -+ * For example: 2 WRIOPs, 16 MACs in each: -+ * MAC IDs for the 1st WRIOP: 1-16, -+ * MAC IDs for the 2nd WRIOP: 17-32. -+ */ -+struct dpmac_cfg { -+ int mac_id; -+}; -+ -+/** -+ * dpmac_create() - Create the DPMAC object. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @cfg: Configuration structure -+ * @token: Returned token; use in subsequent API calls -+ * -+ * Create the DPMAC object, allocate required resources and -+ * perform required initialization. -+ * -+ * The object can be created either by declaring it in the -+ * DPL file, or by calling this function. -+ * This function returns a unique authentication token, -+ * associated with the specific object ID and the specific MC -+ * portal; this token must be used in all subsequent calls to -+ * this specific object. For objects that are created using the -+ * DPL file, call dpmac_open function to get an authentication -+ * token first. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_create(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ const struct dpmac_cfg *cfg, -+ uint16_t *token); -+ -+/** -+ * dpmac_destroy() - Destroy the DPMAC object and release all its resources. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMAC object -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpmac_destroy(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * DPMAC IRQ Index and Events -+ */ -+ -+/** -+ * IRQ index -+ */ -+#define DPMAC_IRQ_INDEX 0 -+/** -+ * IRQ event - indicates a change in link state -+ */ -+#define DPMAC_IRQ_EVENT_LINK_CFG_REQ 0x00000001 -+/** -+ * IRQ event - Indicates that the link state changed -+ */ -+#define DPMAC_IRQ_EVENT_LINK_CHANGED 0x00000002 -+ -+/** -+ * struct dpmac_irq_cfg - IRQ configuration -+ * @addr: Address that must be written to signal a message-based interrupt -+ * @val: Value to write into irq_addr address -+ * @irq_num: A user defined number associated with this IRQ -+ */ -+struct dpmac_irq_cfg { -+ uint64_t addr; -+ uint32_t val; -+ int irq_num; -+}; -+ -+/** -+ * dpmac_set_irq() - Set IRQ information for the DPMAC to trigger an interrupt. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMAC object -+ * @irq_index: Identifies the interrupt index to configure -+ * @irq_cfg: IRQ configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_set_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ struct dpmac_irq_cfg *irq_cfg); -+ -+/** -+ * dpmac_get_irq() - Get IRQ information from the DPMAC. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMAC object -+ * @irq_index: The interrupt index to configure -+ * @type: Interrupt type: 0 represents message interrupt -+ * type (both irq_addr and irq_val are valid) -+ * @irq_cfg: IRQ attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_get_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ int *type, -+ struct dpmac_irq_cfg *irq_cfg); -+ -+/** -+ * dpmac_set_irq_enable() - Set overall interrupt state. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMAC object -+ * @irq_index: The interrupt index to configure -+ * @en: Interrupt state - enable = 1, disable = 0 -+ * -+ * Allows GPP software to control when interrupts are generated. -+ * Each interrupt can have up to 32 causes. The enable/disable control's the -+ * overall interrupt state. if the interrupt is disabled no causes will cause -+ * an interrupt. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_set_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t en); -+ -+/** -+ * dpmac_get_irq_enable() - Get overall interrupt state -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMAC object -+ * @irq_index: The interrupt index to configure -+ * @en: Returned interrupt state - enable = 1, disable = 0 -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_get_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t *en); -+ -+/** -+ * dpmac_set_irq_mask() - Set interrupt mask. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMAC object -+ * @irq_index: The interrupt index to configure -+ * @mask: Event mask to trigger interrupt; -+ * each bit: -+ * 0 = ignore event -+ * 1 = consider event for asserting IRQ -+ * -+ * Every interrupt can have up to 32 causes and the interrupt model supports -+ * masking/unmasking each cause independently -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_set_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t mask); -+ -+/** -+ * dpmac_get_irq_mask() - Get interrupt mask. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMAC object -+ * @irq_index: The interrupt index to configure -+ * @mask: Returned event mask to trigger interrupt -+ * -+ * Every interrupt can have up to 32 causes and the interrupt model supports -+ * masking/unmasking each cause independently -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_get_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *mask); -+ -+/** -+ * dpmac_get_irq_status() - Get the current status of any pending interrupts. -+ * -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMAC object -+ * @irq_index: The interrupt index to configure -+ * @status: Returned interrupts status - one bit per cause: -+ * 0 = no interrupt pending -+ * 1 = interrupt pending -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_get_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *status); -+ -+/** -+ * dpmac_clear_irq_status() - Clear a pending interrupt's status -+ * -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMAC object -+ * @irq_index: The interrupt index to configure -+ * @status: Bits to clear (W1C) - one bit per cause: -+ * 0 = don't change -+ * 1 = clear status bit -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_clear_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t status); -+ -+/** -+ * struct dpmac_attr - Structure representing DPMAC attributes -+ * @id: DPMAC object ID -+ * @phy_id: PHY ID -+ * @link_type: link type -+ * @eth_if: Ethernet interface -+ * @max_rate: Maximum supported rate - in Mbps -+ * @version: DPMAC version -+ */ -+struct dpmac_attr { -+ int id; -+ int phy_id; -+ enum dpmac_link_type link_type; -+ enum dpmac_eth_if eth_if; -+ uint32_t max_rate; -+ /** -+ * struct version - Structure representing DPMAC version -+ * @major: DPMAC major version -+ * @minor: DPMAC minor version -+ */ -+ struct { -+ uint16_t major; -+ uint16_t minor; -+ } version; -+}; -+ -+/** -+ * dpmac_get_attributes - Retrieve DPMAC attributes. -+ * -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMAC object -+ * @attr: Returned object's attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_get_attributes(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpmac_attr *attr); -+ -+/** -+ * struct dpmac_mdio_cfg - DPMAC MDIO read/write parameters -+ * @phy_addr: MDIO device address -+ * @reg: Address of the register within the Clause 45 PHY device from which data -+ * is to be read -+ * @data: Data read/write from/to MDIO -+ */ -+struct dpmac_mdio_cfg { -+ uint8_t phy_addr; -+ uint8_t reg; -+ uint16_t data; -+}; -+ -+/** -+ * dpmac_mdio_read() - Perform MDIO read transaction -+ * @mc_io: Pointer to opaque I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMAC object -+ * @cfg: Structure with MDIO transaction parameters -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_mdio_read(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpmac_mdio_cfg *cfg); -+ -+/** -+ * dpmac_mdio_write() - Perform MDIO write transaction -+ * @mc_io: Pointer to opaque I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMAC object -+ * @cfg: Structure with MDIO transaction parameters -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_mdio_write(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpmac_mdio_cfg *cfg); -+ -+/** -+ * DPMAC link configuration/state options -+ */ -+ -+/** -+ * Enable auto-negotiation -+ */ -+#define DPMAC_LINK_OPT_AUTONEG 0x0000000000000001ULL -+/** -+ * Enable half-duplex mode -+ */ -+#define DPMAC_LINK_OPT_HALF_DUPLEX 0x0000000000000002ULL -+/** -+ * Enable pause frames -+ */ -+#define DPMAC_LINK_OPT_PAUSE 0x0000000000000004ULL -+/** -+ * Enable a-symmetric pause frames -+ */ -+#define DPMAC_LINK_OPT_ASYM_PAUSE 0x0000000000000008ULL -+ -+/** -+ * struct dpmac_link_cfg - Structure representing DPMAC link configuration -+ * @rate: Link's rate - in Mbps -+ * @options: Enable/Disable DPMAC link cfg features (bitmap) -+ */ -+struct dpmac_link_cfg { -+ uint32_t rate; -+ uint64_t options; -+}; -+ -+/** -+ * dpmac_get_link_cfg() - Get Ethernet link configuration -+ * @mc_io: Pointer to opaque I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMAC object -+ * @cfg: Returned structure with the link configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_get_link_cfg(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpmac_link_cfg *cfg); -+ -+/** -+ * struct dpmac_link_state - DPMAC link configuration request -+ * @rate: Rate in Mbps -+ * @options: Enable/Disable DPMAC link cfg features (bitmap) -+ * @up: Link state -+ */ -+struct dpmac_link_state { -+ uint32_t rate; -+ uint64_t options; -+ int up; -+}; -+ -+/** -+ * dpmac_set_link_state() - Set the Ethernet link status -+ * @mc_io: Pointer to opaque I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMAC object -+ * @link_state: Link state configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_set_link_state(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpmac_link_state *link_state); -+ -+/** -+ * enum dpmac_counter - DPMAC counter types -+ * @DPMAC_CNT_ING_FRAME_64: counts 64-bytes frames, good or bad. -+ * @DPMAC_CNT_ING_FRAME_127: counts 65- to 127-bytes frames, good or bad. -+ * @DPMAC_CNT_ING_FRAME_255: counts 128- to 255-bytes frames, good or bad. -+ * @DPMAC_CNT_ING_FRAME_511: counts 256- to 511-bytes frames, good or bad. -+ * @DPMAC_CNT_ING_FRAME_1023: counts 512- to 1023-bytes frames, good or bad. -+ * @DPMAC_CNT_ING_FRAME_1518: counts 1024- to 1518-bytes frames, good or bad. -+ * @DPMAC_CNT_ING_FRAME_1519_MAX: counts 1519-bytes frames and larger -+ * (up to max frame length specified), -+ * good or bad. -+ * @DPMAC_CNT_ING_FRAG: counts frames which are shorter than 64 bytes received -+ * with a wrong CRC -+ * @DPMAC_CNT_ING_JABBER: counts frames longer than the maximum frame length -+ * specified, with a bad frame check sequence. -+ * @DPMAC_CNT_ING_FRAME_DISCARD: counts dropped frames due to internal errors. -+ * Occurs when a receive FIFO overflows. -+ * Includes also frames truncated as a result of -+ * the receive FIFO overflow. -+ * @DPMAC_CNT_ING_ALIGN_ERR: counts frames with an alignment error -+ * (optional used for wrong SFD). -+ * @DPMAC_CNT_EGR_UNDERSIZED: counts frames transmitted that was less than 64 -+ * bytes long with a good CRC. -+ * @DPMAC_CNT_ING_OVERSIZED: counts frames longer than the maximum frame length -+ * specified, with a good frame check sequence. -+ * @DPMAC_CNT_ING_VALID_PAUSE_FRAME: counts valid pause frames (regular and PFC) -+ * @DPMAC_CNT_EGR_VALID_PAUSE_FRAME: counts valid pause frames transmitted -+ * (regular and PFC). -+ * @DPMAC_CNT_ING_BYTE: counts bytes received except preamble for all valid -+ * frames and valid pause frames. -+ * @DPMAC_CNT_ING_MCAST_FRAME: counts received multicast frames. -+ * @DPMAC_CNT_ING_BCAST_FRAME: counts received broadcast frames. -+ * @DPMAC_CNT_ING_ALL_FRAME: counts each good or bad frames received. -+ * @DPMAC_CNT_ING_UCAST_FRAME: counts received unicast frames. -+ * @DPMAC_CNT_ING_ERR_FRAME: counts frames received with an error -+ * (except for undersized/fragment frame). -+ * @DPMAC_CNT_EGR_BYTE: counts bytes transmitted except preamble for all valid -+ * frames and valid pause frames transmitted. -+ * @DPMAC_CNT_EGR_MCAST_FRAME: counts transmitted multicast frames. -+ * @DPMAC_CNT_EGR_BCAST_FRAME: counts transmitted broadcast frames. -+ * @DPMAC_CNT_EGR_UCAST_FRAME: counts transmitted unicast frames. -+ * @DPMAC_CNT_EGR_ERR_FRAME: counts frames transmitted with an error. -+ * @DPMAC_CNT_ING_GOOD_FRAME: counts frames received without error, including -+ * pause frames. -+ * @DPMAC_CNT_ENG_GOOD_FRAME: counts frames transmitted without error, including -+ * pause frames. -+ */ -+enum dpmac_counter { -+ DPMAC_CNT_ING_FRAME_64, -+ DPMAC_CNT_ING_FRAME_127, -+ DPMAC_CNT_ING_FRAME_255, -+ DPMAC_CNT_ING_FRAME_511, -+ DPMAC_CNT_ING_FRAME_1023, -+ DPMAC_CNT_ING_FRAME_1518, -+ DPMAC_CNT_ING_FRAME_1519_MAX, -+ DPMAC_CNT_ING_FRAG, -+ DPMAC_CNT_ING_JABBER, -+ DPMAC_CNT_ING_FRAME_DISCARD, -+ DPMAC_CNT_ING_ALIGN_ERR, -+ DPMAC_CNT_EGR_UNDERSIZED, -+ DPMAC_CNT_ING_OVERSIZED, -+ DPMAC_CNT_ING_VALID_PAUSE_FRAME, -+ DPMAC_CNT_EGR_VALID_PAUSE_FRAME, -+ DPMAC_CNT_ING_BYTE, -+ DPMAC_CNT_ING_MCAST_FRAME, -+ DPMAC_CNT_ING_BCAST_FRAME, -+ DPMAC_CNT_ING_ALL_FRAME, -+ DPMAC_CNT_ING_UCAST_FRAME, -+ DPMAC_CNT_ING_ERR_FRAME, -+ DPMAC_CNT_EGR_BYTE, -+ DPMAC_CNT_EGR_MCAST_FRAME, -+ DPMAC_CNT_EGR_BCAST_FRAME, -+ DPMAC_CNT_EGR_UCAST_FRAME, -+ DPMAC_CNT_EGR_ERR_FRAME, -+ DPMAC_CNT_ING_GOOD_FRAME, -+ DPMAC_CNT_ENG_GOOD_FRAME -+}; -+ -+/** -+ * dpmac_get_counter() - Read a specific DPMAC counter -+ * @mc_io: Pointer to opaque I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMAC object -+ * @type: The requested counter -+ * @counter: Returned counter value -+ * -+ * Return: The requested counter; '0' otherwise. -+ */ -+int dpmac_get_counter(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ enum dpmac_counter type, -+ uint64_t *counter); -+ -+#endif /* __FSL_DPMAC_H */ -diff --git a/drivers/staging/fsl-dpaa2/mac/mac.c b/drivers/staging/fsl-dpaa2/mac/mac.c -new file mode 100644 -index 0000000..366ad4c ---- /dev/null -+++ b/drivers/staging/fsl-dpaa2/mac/mac.c -@@ -0,0 +1,694 @@ -+/* Copyright 2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 -+ -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include "../../fsl-mc/include/mc.h" -+#include "../../fsl-mc/include/mc-sys.h" -+ -+#include "dpmac.h" -+#include "dpmac-cmd.h" -+ -+#define DPAA2_SUPPORTED_DPMAC_VERSION 3 -+ -+struct dpaa2_mac_priv { -+ struct net_device *netdev; -+ struct fsl_mc_device *mc_dev; -+ struct dpmac_attr attr; -+ struct dpmac_link_state old_state; -+}; -+ -+/* TODO: fix the 10G modes, mapping can't be right: -+ * XGMII is paralel -+ * XAUI is serial, using 8b/10b encoding -+ * XFI is also serial but using 64b/66b encoding -+ * they can't all map to XGMII... -+ * -+ * This must be kept in sync with enum dpmac_eth_if. -+ */ -+static phy_interface_t dpaa2_mac_iface_mode[] = { -+ /* DPMAC_ETH_IF_MII */ -+ PHY_INTERFACE_MODE_MII, -+ /* DPMAC_ETH_IF_RMII */ -+ PHY_INTERFACE_MODE_RMII, -+ /* DPMAC_ETH_IF_SMII */ -+ PHY_INTERFACE_MODE_SMII, -+ /* DPMAC_ETH_IF_GMII */ -+ PHY_INTERFACE_MODE_GMII, -+ /* DPMAC_ETH_IF_RGMII */ -+ PHY_INTERFACE_MODE_RGMII, -+ /* DPMAC_ETH_IF_SGMII */ -+ PHY_INTERFACE_MODE_SGMII, -+ /* DPMAC_ETH_IF_QSGMII */ -+ PHY_INTERFACE_MODE_QSGMII, -+ /* DPMAC_ETH_IF_XAUI */ -+ PHY_INTERFACE_MODE_XGMII, -+ /* DPMAC_ETH_IF_XFI */ -+ PHY_INTERFACE_MODE_XGMII, -+}; -+ -+static void dpaa2_mac_link_changed(struct net_device *netdev) -+{ -+ struct phy_device *phydev; -+ struct dpmac_link_state state = { 0 }; -+ struct dpaa2_mac_priv *priv = netdev_priv(netdev); -+ int err; -+ -+ /* the PHY just notified us of link state change */ -+ phydev = netdev->phydev; -+ -+ state.up = !!phydev->link; -+ if (phydev->link) { -+ state.rate = phydev->speed; -+ -+ if (!phydev->duplex) -+ state.options |= DPMAC_LINK_OPT_HALF_DUPLEX; -+ if (phydev->autoneg) -+ state.options |= DPMAC_LINK_OPT_AUTONEG; -+ -+ netif_carrier_on(netdev); -+ } else { -+ netif_carrier_off(netdev); -+ } -+ -+ if (priv->old_state.up != state.up || -+ priv->old_state.rate != state.rate || -+ priv->old_state.options != state.options) { -+ priv->old_state = state; -+ phy_print_status(phydev); -+ } -+ -+ /* We must call into the MC firmware at all times, because we don't know -+ * when and whether a potential DPNI may have read the link state. -+ */ -+ err = dpmac_set_link_state(priv->mc_dev->mc_io, 0, -+ priv->mc_dev->mc_handle, &state); -+ if (unlikely(err)) -+ dev_err(&priv->mc_dev->dev, "dpmac_set_link_state: %d\n", err); -+} -+ -+/* IRQ bits that we handle */ -+static const u32 dpmac_irq_mask = DPMAC_IRQ_EVENT_LINK_CFG_REQ; -+ -+#ifdef CONFIG_FSL_DPAA2_MAC_NETDEVS -+static netdev_tx_t dpaa2_mac_drop_frame(struct sk_buff *skb, -+ struct net_device *dev) -+{ -+ /* we don't support I/O for now, drop the frame */ -+ dev_kfree_skb_any(skb); -+ return NETDEV_TX_OK; -+} -+ -+static int dpaa2_mac_open(struct net_device *netdev) -+{ -+ /* start PHY state machine */ -+ phy_start(netdev->phydev); -+ -+ return 0; -+} -+ -+static int dpaa2_mac_stop(struct net_device *netdev) -+{ -+ if (!netdev->phydev) -+ goto done; -+ -+ /* stop PHY state machine */ -+ phy_stop(netdev->phydev); -+ -+ /* signal link down to firmware */ -+ netdev->phydev->link = 0; -+ dpaa2_mac_link_changed(netdev); -+ -+done: -+ return 0; -+} -+ -+static int dpaa2_mac_get_settings(struct net_device *netdev, -+ struct ethtool_cmd *cmd) -+{ -+ return phy_ethtool_gset(netdev->phydev, cmd); -+} -+ -+static int dpaa2_mac_set_settings(struct net_device *netdev, -+ struct ethtool_cmd *cmd) -+{ -+ return phy_ethtool_sset(netdev->phydev, cmd); -+} -+ -+static struct rtnl_link_stats64 -+*dpaa2_mac_get_stats(struct net_device *netdev, -+ struct rtnl_link_stats64 *storage) -+{ -+ struct dpaa2_mac_priv *priv = netdev_priv(netdev); -+ u64 tmp; -+ int err; -+ -+ err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, -+ DPMAC_CNT_EGR_MCAST_FRAME, -+ &storage->tx_packets); -+ if (err) -+ goto error; -+ err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, -+ DPMAC_CNT_EGR_BCAST_FRAME, &tmp); -+ if (err) -+ goto error; -+ storage->tx_packets += tmp; -+ err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, -+ DPMAC_CNT_EGR_UCAST_FRAME, &tmp); -+ if (err) -+ goto error; -+ storage->tx_packets += tmp; -+ -+ err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, -+ DPMAC_CNT_EGR_UNDERSIZED, &storage->tx_dropped); -+ if (err) -+ goto error; -+ err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, -+ DPMAC_CNT_EGR_BYTE, &storage->tx_bytes); -+ if (err) -+ goto error; -+ err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, -+ DPMAC_CNT_EGR_ERR_FRAME, &storage->tx_errors); -+ if (err) -+ goto error; -+ -+ err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, -+ DPMAC_CNT_ING_ALL_FRAME, &storage->rx_packets); -+ if (err) -+ goto error; -+ err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, -+ DPMAC_CNT_ING_MCAST_FRAME, &storage->multicast); -+ if (err) -+ goto error; -+ err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, -+ DPMAC_CNT_ING_FRAME_DISCARD, -+ &storage->rx_dropped); -+ if (err) -+ goto error; -+ err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, -+ DPMAC_CNT_ING_ALIGN_ERR, &storage->rx_errors); -+ if (err) -+ goto error; -+ err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, -+ DPMAC_CNT_ING_OVERSIZED, &tmp); -+ if (err) -+ goto error; -+ storage->rx_errors += tmp; -+ err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, -+ DPMAC_CNT_ING_BYTE, &storage->rx_bytes); -+ if (err) -+ goto error; -+ -+ return storage; -+ -+error: -+ netdev_err(netdev, "dpmac_get_counter err %d\n", err); -+ return storage; -+} -+ -+static struct { -+ enum dpmac_counter id; -+ char name[ETH_GSTRING_LEN]; -+} dpaa2_mac_counters[] = { -+ {DPMAC_CNT_ING_ALL_FRAME, "rx all frames"}, -+ {DPMAC_CNT_ING_GOOD_FRAME, "rx frames ok"}, -+ {DPMAC_CNT_ING_ERR_FRAME, "rx frame errors"}, -+ {DPMAC_CNT_ING_FRAME_DISCARD, "rx frame discards"}, -+ {DPMAC_CNT_ING_UCAST_FRAME, "rx u-cast"}, -+ {DPMAC_CNT_ING_BCAST_FRAME, "rx b-cast"}, -+ {DPMAC_CNT_ING_MCAST_FRAME, "rx m-cast"}, -+ {DPMAC_CNT_ING_FRAME_64, "rx 64 bytes"}, -+ {DPMAC_CNT_ING_FRAME_127, "rx 65-127 bytes"}, -+ {DPMAC_CNT_ING_FRAME_255, "rx 128-255 bytes"}, -+ {DPMAC_CNT_ING_FRAME_511, "rx 256-511 bytes"}, -+ {DPMAC_CNT_ING_FRAME_1023, "rx 512-1023 bytes"}, -+ {DPMAC_CNT_ING_FRAME_1518, "rx 1024-1518 bytes"}, -+ {DPMAC_CNT_ING_FRAME_1519_MAX, "rx 1519-max bytes"}, -+ {DPMAC_CNT_ING_FRAG, "rx frags"}, -+ {DPMAC_CNT_ING_JABBER, "rx jabber"}, -+ {DPMAC_CNT_ING_ALIGN_ERR, "rx align errors"}, -+ {DPMAC_CNT_ING_OVERSIZED, "rx oversized"}, -+ {DPMAC_CNT_ING_VALID_PAUSE_FRAME, "rx pause"}, -+ {DPMAC_CNT_ING_BYTE, "rx bytes"}, -+ {DPMAC_CNT_ENG_GOOD_FRAME, "tx frames ok"}, -+ {DPMAC_CNT_EGR_UCAST_FRAME, "tx u-cast"}, -+ {DPMAC_CNT_EGR_MCAST_FRAME, "tx m-cast"}, -+ {DPMAC_CNT_EGR_BCAST_FRAME, "tx b-cast"}, -+ {DPMAC_CNT_EGR_ERR_FRAME, "tx frame errors"}, -+ {DPMAC_CNT_EGR_UNDERSIZED, "tx undersized"}, -+ {DPMAC_CNT_EGR_VALID_PAUSE_FRAME, "tx b-pause"}, -+ {DPMAC_CNT_EGR_BYTE, "tx bytes"}, -+ -+}; -+ -+static void dpaa2_mac_get_strings(struct net_device *netdev, -+ u32 stringset, u8 *data) -+{ -+ int i; -+ -+ switch (stringset) { -+ case ETH_SS_STATS: -+ for (i = 0; i < ARRAY_SIZE(dpaa2_mac_counters); i++) -+ memcpy(data + i * ETH_GSTRING_LEN, -+ dpaa2_mac_counters[i].name, -+ ETH_GSTRING_LEN); -+ break; -+ } -+} -+ -+static void dpaa2_mac_get_ethtool_stats(struct net_device *netdev, -+ struct ethtool_stats *stats, -+ u64 *data) -+{ -+ struct dpaa2_mac_priv *priv = netdev_priv(netdev); -+ int i; -+ int err; -+ -+ for (i = 0; i < ARRAY_SIZE(dpaa2_mac_counters); i++) { -+ err = dpmac_get_counter(priv->mc_dev->mc_io, -+ 0, -+ priv->mc_dev->mc_handle, -+ dpaa2_mac_counters[i].id, &data[i]); -+ if (err) -+ netdev_err(netdev, "dpmac_get_counter[%s] err %d\n", -+ dpaa2_mac_counters[i].name, err); -+ } -+} -+ -+static int dpaa2_mac_get_sset_count(struct net_device *dev, int sset) -+{ -+ switch (sset) { -+ case ETH_SS_STATS: -+ return ARRAY_SIZE(dpaa2_mac_counters); -+ default: -+ return -EOPNOTSUPP; -+ } -+} -+ -+static const struct net_device_ops dpaa2_mac_ndo_ops = { -+ .ndo_start_xmit = &dpaa2_mac_drop_frame, -+ .ndo_open = &dpaa2_mac_open, -+ .ndo_stop = &dpaa2_mac_stop, -+ .ndo_get_stats64 = &dpaa2_mac_get_stats, -+}; -+ -+static const struct ethtool_ops dpaa2_mac_ethtool_ops = { -+ .get_settings = &dpaa2_mac_get_settings, -+ .set_settings = &dpaa2_mac_set_settings, -+ .get_strings = &dpaa2_mac_get_strings, -+ .get_ethtool_stats = &dpaa2_mac_get_ethtool_stats, -+ .get_sset_count = &dpaa2_mac_get_sset_count, -+}; -+#endif /* CONFIG_FSL_DPAA2_MAC_NETDEVS */ -+ -+static void configure_link(struct dpaa2_mac_priv *priv, -+ struct dpmac_link_cfg *cfg) -+{ -+ struct phy_device *phydev = priv->netdev->phydev; -+ -+ if (unlikely(!phydev)) -+ return; -+ -+ phydev->speed = cfg->rate; -+ phydev->duplex = !!(cfg->options & DPMAC_LINK_OPT_HALF_DUPLEX); -+ -+ if (cfg->options & DPMAC_LINK_OPT_AUTONEG) { -+ phydev->autoneg = 1; -+ phydev->advertising |= ADVERTISED_Autoneg; -+ } else { -+ phydev->autoneg = 0; -+ phydev->advertising &= ~ADVERTISED_Autoneg; -+ } -+ -+ phy_start_aneg(phydev); -+} -+ -+static irqreturn_t dpaa2_mac_irq_handler(int irq_num, void *arg) -+{ -+ struct device *dev = (struct device *)arg; -+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); -+ struct dpaa2_mac_priv *priv = dev_get_drvdata(dev); -+ struct dpmac_link_cfg link_cfg; -+ u32 status; -+ int err; -+ -+ err = dpmac_get_irq_status(mc_dev->mc_io, 0, mc_dev->mc_handle, -+ DPMAC_IRQ_INDEX, &status); -+ if (unlikely(err || !status)) -+ return IRQ_NONE; -+ -+ /* DPNI-initiated link configuration; 'ifconfig up' also calls this */ -+ if (status & DPMAC_IRQ_EVENT_LINK_CFG_REQ) { -+ err = dpmac_get_link_cfg(mc_dev->mc_io, 0, mc_dev->mc_handle, -+ &link_cfg); -+ if (unlikely(err)) -+ goto out; -+ -+ configure_link(priv, &link_cfg); -+ } -+ -+out: -+ dpmac_clear_irq_status(mc_dev->mc_io, 0, mc_dev->mc_handle, -+ DPMAC_IRQ_INDEX, status); -+ -+ return IRQ_HANDLED; -+} -+ -+static int setup_irqs(struct fsl_mc_device *mc_dev) -+{ -+ int err; -+ -+ err = fsl_mc_allocate_irqs(mc_dev); -+ if (err) { -+ dev_err(&mc_dev->dev, "fsl_mc_allocate_irqs err %d\n", err); -+ return err; -+ } -+ -+ err = devm_request_threaded_irq(&mc_dev->dev, -+ mc_dev->irqs[0]->irq_number, -+ NULL, &dpaa2_mac_irq_handler, -+ IRQF_NO_SUSPEND | IRQF_ONESHOT, -+ dev_name(&mc_dev->dev), &mc_dev->dev); -+ if (err) { -+ dev_err(&mc_dev->dev, "devm_request_threaded_irq err %d\n", -+ err); -+ goto free_irq; -+ } -+ -+ err = dpmac_set_irq_mask(mc_dev->mc_io, 0, mc_dev->mc_handle, -+ DPMAC_IRQ_INDEX, dpmac_irq_mask); -+ if (err) { -+ dev_err(&mc_dev->dev, "dpmac_set_irq_mask err %d\n", err); -+ goto free_irq; -+ } -+ err = dpmac_set_irq_enable(mc_dev->mc_io, 0, mc_dev->mc_handle, -+ DPMAC_IRQ_INDEX, 1); -+ if (err) { -+ dev_err(&mc_dev->dev, "dpmac_set_irq_enable err %d\n", err); -+ goto free_irq; -+ } -+ -+ return 0; -+ -+free_irq: -+ fsl_mc_free_irqs(mc_dev); -+ -+ return err; -+} -+ -+static void teardown_irqs(struct fsl_mc_device *mc_dev) -+{ -+ int err; -+ -+ err = dpmac_set_irq_mask(mc_dev->mc_io, 0, mc_dev->mc_handle, -+ DPMAC_IRQ_INDEX, dpmac_irq_mask); -+ if (err) -+ dev_err(&mc_dev->dev, "dpmac_set_irq_mask err %d\n", err); -+ -+ err = dpmac_set_irq_enable(mc_dev->mc_io, 0, mc_dev->mc_handle, -+ DPMAC_IRQ_INDEX, 0); -+ if (err) -+ dev_err(&mc_dev->dev, "dpmac_set_irq_enable err %d\n", err); -+ -+ fsl_mc_free_irqs(mc_dev); -+} -+ -+static struct device_node *lookup_node(struct device *dev, int dpmac_id) -+{ -+ struct device_node *dpmacs, *dpmac = NULL; -+ struct device_node *mc_node = dev->of_node; -+ u32 id; -+ int err; -+ -+ dpmacs = of_find_node_by_name(mc_node, "dpmacs"); -+ if (!dpmacs) { -+ dev_err(dev, "No dpmacs subnode in device-tree\n"); -+ return NULL; -+ } -+ -+ while ((dpmac = of_get_next_child(dpmacs, dpmac))) { -+ err = of_property_read_u32(dpmac, "reg", &id); -+ if (err) -+ continue; -+ if (id == dpmac_id) -+ return dpmac; -+ } -+ -+ return NULL; -+} -+ -+static int dpaa2_mac_probe(struct fsl_mc_device *mc_dev) -+{ -+ struct device *dev; -+ struct dpaa2_mac_priv *priv = NULL; -+ struct device_node *phy_node, *dpmac_node; -+ struct net_device *netdev; -+ phy_interface_t if_mode; -+ int err = 0; -+ -+ dev = &mc_dev->dev; -+ -+ /* prepare a net_dev structure to make the phy lib API happy */ -+ netdev = alloc_etherdev(sizeof(*priv)); -+ if (!netdev) { -+ dev_err(dev, "alloc_etherdev error\n"); -+ err = -ENOMEM; -+ goto err_exit; -+ } -+ priv = netdev_priv(netdev); -+ priv->mc_dev = mc_dev; -+ priv->netdev = netdev; -+ -+ SET_NETDEV_DEV(netdev, dev); -+ -+#ifdef CONFIG_FSL_DPAA2_MAC_NETDEVS -+ snprintf(netdev->name, IFNAMSIZ, "mac%d", mc_dev->obj_desc.id); -+#endif -+ -+ dev_set_drvdata(dev, priv); -+ -+ err = fsl_mc_portal_allocate(mc_dev, 0, &mc_dev->mc_io); -+ if (err || !mc_dev->mc_io) { -+ dev_err(dev, "fsl_mc_portal_allocate error: %d\n", err); -+ err = -ENODEV; -+ goto err_free_netdev; -+ } -+ -+ err = dpmac_open(mc_dev->mc_io, 0, mc_dev->obj_desc.id, -+ &mc_dev->mc_handle); -+ if (err || !mc_dev->mc_handle) { -+ dev_err(dev, "dpmac_open error: %d\n", err); -+ err = -ENODEV; -+ goto err_free_mcp; -+ } -+ -+ err = dpmac_get_attributes(mc_dev->mc_io, 0, -+ mc_dev->mc_handle, &priv->attr); -+ if (err) { -+ dev_err(dev, "dpmac_get_attributes err %d\n", err); -+ err = -EINVAL; -+ goto err_close; -+ } -+ -+ dev_info_once(dev, "Using DPMAC API %d.%d\n", -+ priv->attr.version.major, priv->attr.version.minor); -+ -+ /* Look up the DPMAC node in the device-tree. */ -+ dpmac_node = lookup_node(dev, priv->attr.id); -+ if (!dpmac_node) { -+ dev_err(dev, "No dpmac@%d subnode found.\n", priv->attr.id); -+ err = -ENODEV; -+ goto err_close; -+ } -+ -+ err = setup_irqs(mc_dev); -+ if (err) { -+ err = -EFAULT; -+ goto err_close; -+ } -+ -+#ifdef CONFIG_FSL_DPAA2_MAC_NETDEVS -+ /* OPTIONAL, register netdev just to make it visible to the user */ -+ netdev->netdev_ops = &dpaa2_mac_ndo_ops; -+ netdev->ethtool_ops = &dpaa2_mac_ethtool_ops; -+ -+ /* phy starts up enabled so netdev should be up too */ -+ netdev->flags |= IFF_UP; -+ -+ err = register_netdev(priv->netdev); -+ if (err < 0) { -+ dev_err(dev, "register_netdev error %d\n", err); -+ err = -ENODEV; -+ goto err_free_irq; -+ } -+#endif /* CONFIG_FSL_DPAA2_MAC_NETDEVS */ -+ -+ /* probe the PHY as a fixed-link if the link type declared in DPC -+ * explicitly mandates this -+ */ -+ if (priv->attr.link_type == DPMAC_LINK_TYPE_FIXED) -+ goto probe_fixed_link; -+ -+ if (priv->attr.eth_if < ARRAY_SIZE(dpaa2_mac_iface_mode)) { -+ if_mode = dpaa2_mac_iface_mode[priv->attr.eth_if]; -+ dev_dbg(dev, "\tusing if mode %s for eth_if %d\n", -+ phy_modes(if_mode), priv->attr.eth_if); -+ } else { -+ dev_warn(dev, "Unexpected interface mode %d, will probe as fixed link\n", -+ priv->attr.eth_if); -+ goto probe_fixed_link; -+ } -+ -+ /* try to connect to the PHY */ -+ phy_node = of_parse_phandle(dpmac_node, "phy-handle", 0); -+ if (!phy_node) { -+ if (!phy_node) { -+ dev_err(dev, "dpmac node has no phy-handle property\n"); -+ err = -ENODEV; -+ goto err_no_phy; -+ } -+ } -+ netdev->phydev = of_phy_connect(netdev, phy_node, -+ &dpaa2_mac_link_changed, 0, if_mode); -+ if (!netdev->phydev) { -+ /* No need for dev_err(); the kernel's loud enough as it is. */ -+ dev_dbg(dev, "Can't of_phy_connect() now.\n"); -+ /* We might be waiting for the MDIO MUX to probe, so defer -+ * our own probing. -+ */ -+ err = -EPROBE_DEFER; -+ goto err_defer; -+ } -+ dev_info(dev, "Connected to %s PHY.\n", phy_modes(if_mode)); -+ -+probe_fixed_link: -+ if (!netdev->phydev) { -+ struct fixed_phy_status status = { -+ .link = 1, -+ /* fixed-phys don't support 10Gbps speed for now */ -+ .speed = 1000, -+ .duplex = 1, -+ }; -+ -+ /* try to register a fixed link phy */ -+ netdev->phydev = fixed_phy_register(PHY_POLL, &status, NULL); -+ if (!netdev->phydev || IS_ERR(netdev->phydev)) { -+ dev_err(dev, "error trying to register fixed PHY\n"); -+ /* So we don't crash unregister_netdev() later on */ -+ netdev->phydev = NULL; -+ err = -EFAULT; -+ goto err_no_phy; -+ } -+ dev_info(dev, "Registered fixed PHY.\n"); -+ } -+ -+ /* start PHY state machine */ -+#ifdef CONFIG_FSL_DPAA2_MAC_NETDEVS -+ dpaa2_mac_open(netdev); -+#else /* CONFIG_FSL_DPAA2_MAC_NETDEVS */ -+ phy_start(netdev->phydev); -+#endif /* CONFIG_FSL_DPAA2_MAC_NETDEVS */ -+ return 0; -+ -+err_defer: -+err_no_phy: -+#ifdef CONFIG_FSL_DPAA2_MAC_NETDEVS -+ unregister_netdev(netdev); -+err_free_irq: -+#endif -+ teardown_irqs(mc_dev); -+err_close: -+ dpmac_close(mc_dev->mc_io, 0, mc_dev->mc_handle); -+err_free_mcp: -+ fsl_mc_portal_free(mc_dev->mc_io); -+err_free_netdev: -+ free_netdev(netdev); -+err_exit: -+ return err; -+} -+ -+static int dpaa2_mac_remove(struct fsl_mc_device *mc_dev) -+{ -+ struct device *dev = &mc_dev->dev; -+ struct dpaa2_mac_priv *priv = dev_get_drvdata(dev); -+ -+#ifdef CONFIG_FSL_DPAA2_MAC_NETDEVS -+ unregister_netdev(priv->netdev); -+#endif -+ teardown_irqs(priv->mc_dev); -+ dpmac_close(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle); -+ fsl_mc_portal_free(priv->mc_dev->mc_io); -+ free_netdev(priv->netdev); -+ -+ dev_set_drvdata(dev, NULL); -+ kfree(priv); -+ -+ return 0; -+} -+ -+static const struct fsl_mc_device_match_id dpaa2_mac_match_id_table[] = { -+ { -+ .vendor = FSL_MC_VENDOR_FREESCALE, -+ .obj_type = "dpmac", -+ .ver_major = DPMAC_VER_MAJOR, -+ .ver_minor = DPMAC_VER_MINOR, -+ }, -+ {} -+}; -+ -+static struct fsl_mc_driver dpaa2_mac_drv = { -+ .driver = { -+ .name = KBUILD_MODNAME, -+ .owner = THIS_MODULE, -+ }, -+ .probe = dpaa2_mac_probe, -+ .remove = dpaa2_mac_remove, -+ .match_id_table = dpaa2_mac_match_id_table, -+}; -+ -+module_fsl_mc_driver(dpaa2_mac_drv); -+ -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("DPAA2 PHY proxy interface driver"); -diff --git a/drivers/staging/fsl-mc/Kconfig b/drivers/staging/fsl-mc/Kconfig -new file mode 100644 -index 0000000..32df07b ---- /dev/null -+++ b/drivers/staging/fsl-mc/Kconfig -@@ -0,0 +1 @@ -+source "drivers/staging/fsl-mc/bus/Kconfig" -diff --git a/drivers/staging/fsl-mc/Makefile b/drivers/staging/fsl-mc/Makefile -new file mode 100644 -index 0000000..9c6a001 ---- /dev/null -+++ b/drivers/staging/fsl-mc/Makefile -@@ -0,0 +1,2 @@ -+# Freescale Management Complex (MC) bus drivers -+obj-$(CONFIG_FSL_MC_BUS) += bus/ -diff --git a/drivers/staging/fsl-mc/TODO b/drivers/staging/fsl-mc/TODO -new file mode 100644 -index 0000000..d78288b ---- /dev/null -+++ b/drivers/staging/fsl-mc/TODO -@@ -0,0 +1,13 @@ -+* Add README file (with ASCII art) describing relationships between -+ DPAA2 objects and how combine them to make a NIC, an LS2 switch, etc. -+ Also, define all acronyms used. -+ -+* Decide if multiple root fsl-mc buses will be supported per Linux instance, -+ and if so add support for this. -+ -+* Add at least one device driver for a DPAA2 object (child device of the -+ fsl-mc bus). -+ -+Please send any patches to Greg Kroah-Hartman , -+german.rivera@freescale.com, devel@driverdev.osuosl.org, -+linux-kernel@vger.kernel.org -diff --git a/drivers/staging/fsl-mc/bus/Kconfig b/drivers/staging/fsl-mc/bus/Kconfig -new file mode 100644 -index 0000000..8bef5b8 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/Kconfig -@@ -0,0 +1,45 @@ -+# -+# Freescale Management Complex (MC) bus drivers -+# -+# Copyright (C) 2014 Freescale Semiconductor, Inc. -+# -+# This file is released under the GPLv2 -+# -+ -+config FSL_MC_BUS -+ tristate "Freescale Management Complex (MC) bus driver" -+ depends on OF && ARM64 -+ help -+ Driver to enable the bus infrastructure for the Freescale -+ QorIQ Management Complex (fsl-mc). The fsl-mc is a hardware -+ module of the QorIQ LS2 SoCs, that does resource management -+ for hardware building-blocks in the SoC that can be used -+ to dynamically create networking hardware objects such as -+ network interfaces (NICs), crypto accelerator instances, -+ or L2 switches. -+ -+ Only enable this option when building the kernel for -+ Freescale QorQIQ LS2xxxx SoCs. -+ -+config FSL_MC_RESTOOL -+ tristate "Freescale Management Complex (MC) restool driver" -+ depends on FSL_MC_BUS -+ help -+ Driver that provides kernel support for the Freescale Management -+ Complex resource manager user-space tool. -+ -+config FSL_MC_DPIO -+ tristate "Freescale Data Path I/O (DPIO) driver" -+ depends on FSL_MC_BUS -+ help -+ Driver for Freescale Data Path I/O (DPIO) devices. -+ A DPIO device provides queue and buffer management facilities -+ for software to interact with other Data Path devices. This -+ driver does not expose the DPIO device individually, but -+ groups them under a service layer API. -+ -+config FSL_QBMAN_DEBUG -+ tristate "Freescale QBMAN Debug APIs" -+ depends on FSL_MC_DPIO -+ help -+ QBMan debug assistant APIs. -diff --git a/drivers/staging/fsl-mc/bus/Makefile b/drivers/staging/fsl-mc/bus/Makefile -new file mode 100644 -index 0000000..f29399c ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/Makefile -@@ -0,0 +1,24 @@ -+# -+# Freescale Management Complex (MC) bus drivers -+# -+# Copyright (C) 2014 Freescale Semiconductor, Inc. -+# -+# This file is released under the GPLv2 -+# -+obj-$(CONFIG_FSL_MC_BUS) += mc-bus-driver.o -+ -+mc-bus-driver-objs := mc-bus.o \ -+ mc-sys.o \ -+ dprc.o \ -+ dpmng.o \ -+ dprc-driver.o \ -+ mc-allocator.o \ -+ dpmcp.o \ -+ dpbp.o \ -+ dpcon.o -+ -+# MC restool kernel support -+obj-$(CONFIG_FSL_MC_RESTOOL) += mc-restool.o -+ -+# MC DPIO driver -+obj-$(CONFIG_FSL_MC_DPIO) += dpio/ -diff --git a/drivers/staging/fsl-mc/bus/dpbp.c b/drivers/staging/fsl-mc/bus/dpbp.c -new file mode 100644 -index 0000000..f183121 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpbp.c -@@ -0,0 +1,459 @@ -+/* Copyright 2013-2014 Freescale Semiconductor Inc. -+* -+* Redistribution and use in source and binary forms, with or without -+* modification, are permitted provided that the following conditions are met: -+* * Redistributions of source code must retain the above copyright -+* notice, this list of conditions and the following disclaimer. -+* * 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. -+* * Neither the name of the above-listed copyright holders nor the -+* names of any contributors may be used to endorse or promote products -+* derived from this software without specific prior written permission. -+* -+* -+* ALTERNATIVELY, this software may be distributed under the terms of the -+* GNU General Public License ("GPL") as published by the Free Software -+* Foundation, either version 2 of that License or (at your option) any -+* later version. -+* -+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+*/ -+#include "../include/mc-sys.h" -+#include "../include/mc-cmd.h" -+#include "../include/dpbp.h" -+#include "../include/dpbp-cmd.h" -+ -+int dpbp_open(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ int dpbp_id, -+ uint16_t *token) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_OPEN, -+ cmd_flags, -+ 0); -+ -+ cmd.params[0] |= mc_enc(0, 32, dpbp_id); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *token = MC_CMD_HDR_READ_TOKEN(cmd.header); -+ -+ return err; -+} -+EXPORT_SYMBOL(dpbp_open); -+ -+int dpbp_close(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_CLOSE, cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dpbp_close); -+ -+int dpbp_create(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ const struct dpbp_cfg *cfg, -+ uint16_t *token) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ (void)(cfg); /* unused */ -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_CREATE, -+ cmd_flags, -+ 0); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *token = MC_CMD_HDR_READ_TOKEN(cmd.header); -+ -+ return 0; -+} -+ -+int dpbp_destroy(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_DESTROY, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpbp_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_ENABLE, cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dpbp_enable); -+ -+int dpbp_disable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_DISABLE, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dpbp_disable); -+ -+int dpbp_is_enabled(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *en) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_IS_ENABLED, cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *en = (int)mc_dec(cmd.params[0], 0, 1); -+ -+ return 0; -+} -+ -+int dpbp_reset(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_RESET, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpbp_set_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ struct dpbp_irq_cfg *irq_cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_SET_IRQ, -+ cmd_flags, -+ token); -+ -+ cmd.params[0] |= mc_enc(0, 8, irq_index); -+ cmd.params[0] |= mc_enc(32, 32, irq_cfg->val); -+ cmd.params[1] |= mc_enc(0, 64, irq_cfg->addr); -+ cmd.params[2] |= mc_enc(0, 32, irq_cfg->irq_num); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpbp_get_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ int *type, -+ struct dpbp_irq_cfg *irq_cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_GET_IRQ, -+ cmd_flags, -+ token); -+ -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ irq_cfg->val = (uint32_t)mc_dec(cmd.params[0], 0, 32); -+ irq_cfg->addr = (uint64_t)mc_dec(cmd.params[1], 0, 64); -+ irq_cfg->irq_num = (int)mc_dec(cmd.params[2], 0, 32); -+ *type = (int)mc_dec(cmd.params[2], 32, 32); -+ -+ return 0; -+} -+ -+int dpbp_set_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t en) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_SET_IRQ_ENABLE, -+ cmd_flags, -+ token); -+ -+ cmd.params[0] |= mc_enc(0, 8, en); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpbp_get_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t *en) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_GET_IRQ_ENABLE, -+ cmd_flags, -+ token); -+ -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *en = (uint8_t)mc_dec(cmd.params[0], 0, 8); -+ return 0; -+} -+ -+int dpbp_set_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t mask) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_SET_IRQ_MASK, -+ cmd_flags, -+ token); -+ -+ cmd.params[0] |= mc_enc(0, 32, mask); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpbp_get_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *mask) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_GET_IRQ_MASK, -+ cmd_flags, -+ token); -+ -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *mask = (uint32_t)mc_dec(cmd.params[0], 0, 32); -+ return 0; -+} -+ -+int dpbp_get_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *status) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_GET_IRQ_STATUS, -+ cmd_flags, -+ token); -+ -+ cmd.params[0] |= mc_enc(0, 32, *status); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *status = (uint32_t)mc_dec(cmd.params[0], 0, 32); -+ return 0; -+} -+ -+int dpbp_clear_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t status) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_CLEAR_IRQ_STATUS, -+ cmd_flags, -+ token); -+ -+ cmd.params[0] |= mc_enc(0, 32, status); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpbp_get_attributes(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpbp_attr *attr) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_GET_ATTR, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ attr->bpid = (uint16_t)mc_dec(cmd.params[0], 16, 16); -+ attr->id = (int)mc_dec(cmd.params[0], 32, 32); -+ attr->version.major = (uint16_t)mc_dec(cmd.params[1], 0, 16); -+ attr->version.minor = (uint16_t)mc_dec(cmd.params[1], 16, 16); -+ return 0; -+} -+EXPORT_SYMBOL(dpbp_get_attributes); -+ -+int dpbp_set_notifications(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpbp_notification_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_SET_NOTIFICATIONS, -+ cmd_flags, -+ token); -+ -+ cmd.params[0] |= mc_enc(0, 32, cfg->depletion_entry); -+ cmd.params[0] |= mc_enc(32, 32, cfg->depletion_exit); -+ cmd.params[1] |= mc_enc(0, 32, cfg->surplus_entry); -+ cmd.params[1] |= mc_enc(32, 32, cfg->surplus_exit); -+ cmd.params[2] |= mc_enc(0, 16, cfg->options); -+ cmd.params[3] |= mc_enc(0, 64, cfg->message_ctx); -+ cmd.params[4] |= mc_enc(0, 64, cfg->message_iova); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpbp_get_notifications(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpbp_notification_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_GET_NOTIFICATIONS, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ cfg->depletion_entry = (uint32_t)mc_dec(cmd.params[0], 0, 32); -+ cfg->depletion_exit = (uint32_t)mc_dec(cmd.params[0], 32, 32); -+ cfg->surplus_entry = (uint32_t)mc_dec(cmd.params[1], 0, 32); -+ cfg->surplus_exit = (uint32_t)mc_dec(cmd.params[1], 32, 32); -+ cfg->options = (uint16_t)mc_dec(cmd.params[2], 0, 16); -+ cfg->message_ctx = (uint64_t)mc_dec(cmd.params[3], 0, 64); -+ cfg->message_iova = (uint64_t)mc_dec(cmd.params[4], 0, 64); -+ -+ return 0; -+} -diff --git a/drivers/staging/fsl-mc/bus/dpcon.c b/drivers/staging/fsl-mc/bus/dpcon.c -new file mode 100644 -index 0000000..7965284 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpcon.c -@@ -0,0 +1,407 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#include "../include/mc-sys.h" -+#include "../include/mc-cmd.h" -+#include "../include/dpcon.h" -+#include "../include/dpcon-cmd.h" -+ -+int dpcon_open(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ int dpcon_id, -+ uint16_t *token) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_OPEN, -+ cmd_flags, -+ 0); -+ DPCON_CMD_OPEN(cmd, dpcon_id); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *token = MC_CMD_HDR_READ_TOKEN(cmd.header); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dpcon_open); -+ -+int dpcon_close(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_CLOSE, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dpcon_close); -+ -+int dpcon_create(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ const struct dpcon_cfg *cfg, -+ uint16_t *token) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_CREATE, -+ cmd_flags, -+ 0); -+ DPCON_CMD_CREATE(cmd, cfg); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *token = MC_CMD_HDR_READ_TOKEN(cmd.header); -+ -+ return 0; -+} -+ -+int dpcon_destroy(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_DESTROY, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpcon_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_ENABLE, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dpcon_enable); -+ -+int dpcon_disable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_DISABLE, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dpcon_disable); -+ -+int dpcon_is_enabled(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *en) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_IS_ENABLED, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPCON_RSP_IS_ENABLED(cmd, *en); -+ -+ return 0; -+} -+ -+int dpcon_reset(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_RESET, -+ cmd_flags, token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpcon_set_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ struct dpcon_irq_cfg *irq_cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_SET_IRQ, -+ cmd_flags, -+ token); -+ DPCON_CMD_SET_IRQ(cmd, irq_index, irq_cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpcon_get_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ int *type, -+ struct dpcon_irq_cfg *irq_cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_GET_IRQ, -+ cmd_flags, -+ token); -+ DPCON_CMD_GET_IRQ(cmd, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPCON_RSP_GET_IRQ(cmd, *type, irq_cfg); -+ -+ return 0; -+} -+ -+int dpcon_set_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t en) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_SET_IRQ_ENABLE, -+ cmd_flags, -+ token); -+ DPCON_CMD_SET_IRQ_ENABLE(cmd, irq_index, en); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpcon_get_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t *en) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_GET_IRQ_ENABLE, -+ cmd_flags, -+ token); -+ DPCON_CMD_GET_IRQ_ENABLE(cmd, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPCON_RSP_GET_IRQ_ENABLE(cmd, *en); -+ -+ return 0; -+} -+ -+int dpcon_set_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t mask) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_SET_IRQ_MASK, -+ cmd_flags, -+ token); -+ DPCON_CMD_SET_IRQ_MASK(cmd, irq_index, mask); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpcon_get_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *mask) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_GET_IRQ_MASK, -+ cmd_flags, -+ token); -+ DPCON_CMD_GET_IRQ_MASK(cmd, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPCON_RSP_GET_IRQ_MASK(cmd, *mask); -+ -+ return 0; -+} -+ -+int dpcon_get_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *status) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_GET_IRQ_STATUS, -+ cmd_flags, -+ token); -+ DPCON_CMD_GET_IRQ_STATUS(cmd, irq_index, *status); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPCON_RSP_GET_IRQ_STATUS(cmd, *status); -+ -+ return 0; -+} -+ -+int dpcon_clear_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t status) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_CLEAR_IRQ_STATUS, -+ cmd_flags, -+ token); -+ DPCON_CMD_CLEAR_IRQ_STATUS(cmd, irq_index, status); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpcon_get_attributes(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpcon_attr *attr) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_GET_ATTR, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPCON_RSP_GET_ATTR(cmd, attr); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dpcon_get_attributes); -+ -+int dpcon_set_notification(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpcon_notification_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_SET_NOTIFICATION, -+ cmd_flags, -+ token); -+ DPCON_CMD_SET_NOTIFICATION(cmd, cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dpcon_set_notification); -+ -diff --git a/drivers/staging/fsl-mc/bus/dpio/Makefile b/drivers/staging/fsl-mc/bus/dpio/Makefile -new file mode 100644 -index 0000000..c20356b ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpio/Makefile -@@ -0,0 +1,9 @@ -+# -+# Freescale DPIO driver -+# -+ -+obj-$(CONFIG_FSL_MC_BUS) += fsl-dpio-drv.o -+ -+fsl-dpio-drv-objs := dpio-drv.o dpio_service.o dpio.o qbman_portal.o -+ -+obj-$(CONFIG_FSL_QBMAN_DEBUG) += qbman_debug.o -diff --git a/drivers/staging/fsl-mc/bus/dpio/dpio-drv.c b/drivers/staging/fsl-mc/bus/dpio/dpio-drv.c -new file mode 100644 -index 0000000..80add27 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpio/dpio-drv.c -@@ -0,0 +1,401 @@ -+/* Copyright 2014 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "../../include/mc.h" -+#include "../../include/fsl_dpaa2_io.h" -+ -+#include "fsl_qbman_portal.h" -+#include "fsl_dpio.h" -+#include "fsl_dpio_cmd.h" -+ -+#include "dpio-drv.h" -+ -+#define DPIO_DESCRIPTION "DPIO Driver" -+ -+MODULE_LICENSE("Dual BSD/GPL"); -+MODULE_AUTHOR("Freescale Semiconductor, Inc"); -+MODULE_DESCRIPTION(DPIO_DESCRIPTION); -+ -+#define MAX_DPIO_IRQ_NAME 16 /* Big enough for "FSL DPIO %d" */ -+ -+struct dpio_priv { -+ struct dpaa2_io *io; -+ char irq_name[MAX_DPIO_IRQ_NAME]; -+ struct task_struct *thread; -+}; -+ -+static int dpio_thread(void *data) -+{ -+ struct dpaa2_io *io = data; -+ -+ while (!kthread_should_stop()) { -+ int err = dpaa2_io_poll(io); -+ -+ if (err) { -+ pr_err("dpaa2_io_poll() failed\n"); -+ return err; -+ } -+ msleep(50); -+ } -+ return 0; -+} -+ -+static irqreturn_t dpio_irq_handler(int irq_num, void *arg) -+{ -+ struct device *dev = (struct device *)arg; -+ struct dpio_priv *priv = dev_get_drvdata(dev); -+ -+ return dpaa2_io_irq(priv->io); -+} -+ -+static void unregister_dpio_irq_handlers(struct fsl_mc_device *ls_dev) -+{ -+ int i; -+ struct fsl_mc_device_irq *irq; -+ int irq_count = ls_dev->obj_desc.irq_count; -+ -+ for (i = 0; i < irq_count; i++) { -+ irq = ls_dev->irqs[i]; -+ devm_free_irq(&ls_dev->dev, irq->irq_number, &ls_dev->dev); -+ } -+} -+ -+static int register_dpio_irq_handlers(struct fsl_mc_device *ls_dev, int cpu) -+{ -+ struct dpio_priv *priv; -+ unsigned int i; -+ int error; -+ struct fsl_mc_device_irq *irq; -+ unsigned int num_irq_handlers_registered = 0; -+ int irq_count = ls_dev->obj_desc.irq_count; -+ cpumask_t mask; -+ -+ priv = dev_get_drvdata(&ls_dev->dev); -+ -+ if (WARN_ON(irq_count != 1)) -+ return -EINVAL; -+ -+ for (i = 0; i < irq_count; i++) { -+ irq = ls_dev->irqs[i]; -+ error = devm_request_irq(&ls_dev->dev, -+ irq->irq_number, -+ dpio_irq_handler, -+ 0, -+ priv->irq_name, -+ &ls_dev->dev); -+ if (error < 0) { -+ dev_err(&ls_dev->dev, -+ "devm_request_irq() failed: %d\n", -+ error); -+ goto error_unregister_irq_handlers; -+ } -+ -+ /* Set the IRQ affinity */ -+ cpumask_clear(&mask); -+ cpumask_set_cpu(cpu, &mask); -+ if (irq_set_affinity(irq->irq_number, &mask)) -+ pr_err("irq_set_affinity failed irq %d cpu %d\n", -+ irq->irq_number, cpu); -+ -+ num_irq_handlers_registered++; -+ } -+ -+ return 0; -+ -+error_unregister_irq_handlers: -+ for (i = 0; i < num_irq_handlers_registered; i++) { -+ irq = ls_dev->irqs[i]; -+ devm_free_irq(&ls_dev->dev, irq->irq_number, -+ &ls_dev->dev); -+ } -+ -+ return error; -+} -+ -+static int __cold -+dpaa2_dpio_probe(struct fsl_mc_device *ls_dev) -+{ -+ struct dpio_attr dpio_attrs; -+ struct dpaa2_io_desc desc; -+ struct dpio_priv *priv; -+ int err = -ENOMEM; -+ struct device *dev = &ls_dev->dev; -+ struct dpaa2_io *defservice; -+ bool irq_allocated = false; -+ static int next_cpu; -+ -+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ goto err_priv_alloc; -+ -+ dev_set_drvdata(dev, priv); -+ -+ err = fsl_mc_portal_allocate(ls_dev, 0, &ls_dev->mc_io); -+ if (err) { -+ dev_err(dev, "MC portal allocation failed\n"); -+ err = -EPROBE_DEFER; -+ goto err_mcportal; -+ } -+ -+ err = dpio_open(ls_dev->mc_io, 0, ls_dev->obj_desc.id, -+ &ls_dev->mc_handle); -+ if (err) { -+ dev_err(dev, "dpio_open() failed\n"); -+ goto err_open; -+ } -+ -+ err = dpio_get_attributes(ls_dev->mc_io, 0, ls_dev->mc_handle, -+ &dpio_attrs); -+ if (err) { -+ dev_err(dev, "dpio_get_attributes() failed %d\n", err); -+ goto err_get_attr; -+ } -+ err = dpio_enable(ls_dev->mc_io, 0, ls_dev->mc_handle); -+ if (err) { -+ dev_err(dev, "dpio_enable() failed %d\n", err); -+ goto err_get_attr; -+ } -+ pr_info("ce_paddr=0x%llx, ci_paddr=0x%llx, portalid=%d, prios=%d\n", -+ ls_dev->regions[0].start, -+ ls_dev->regions[1].start, -+ dpio_attrs.qbman_portal_id, -+ dpio_attrs.num_priorities); -+ -+ pr_info("ce_size=0x%llx, ci_size=0x%llx\n", -+ resource_size(&ls_dev->regions[0]), -+ resource_size(&ls_dev->regions[1])); -+ -+ desc.qman_version = dpio_attrs.qbman_version; -+ /* Build DPIO driver object out of raw MC object */ -+ desc.receives_notifications = dpio_attrs.num_priorities ? 1 : 0; -+ desc.has_irq = 1; -+ desc.will_poll = 1; -+ desc.has_8prio = dpio_attrs.num_priorities == 8 ? 1 : 0; -+ desc.cpu = next_cpu; -+ desc.stash_affinity = next_cpu; -+ next_cpu = (next_cpu + 1) % num_active_cpus(); -+ desc.dpio_id = ls_dev->obj_desc.id; -+ desc.regs_cena = ioremap_cache_ns(ls_dev->regions[0].start, -+ resource_size(&ls_dev->regions[0])); -+ desc.regs_cinh = ioremap(ls_dev->regions[1].start, -+ resource_size(&ls_dev->regions[1])); -+ -+ err = fsl_mc_allocate_irqs(ls_dev); -+ if (err) { -+ dev_err(dev, "DPIO fsl_mc_allocate_irqs failed\n"); -+ desc.has_irq = 0; -+ } else { -+ irq_allocated = true; -+ -+ snprintf(priv->irq_name, MAX_DPIO_IRQ_NAME, "FSL DPIO %d", -+ desc.dpio_id); -+ -+ err = register_dpio_irq_handlers(ls_dev, desc.cpu); -+ if (err) -+ desc.has_irq = 0; -+ } -+ -+ priv->io = dpaa2_io_create(&desc); -+ if (!priv->io) { -+ dev_err(dev, "DPIO setup failed\n"); -+ goto err_dpaa2_io_create; -+ } -+ -+ /* If no irq then go to poll mode */ -+ if (desc.has_irq == 0) { -+ dev_info(dev, "Using polling mode for DPIO %d\n", -+ desc.dpio_id); -+ /* goto err_register_dpio_irq; */ -+ /* TEMP: Start polling if IRQ could not -+ be registered. This will go away once -+ KVM support for MSI is present */ -+ if (irq_allocated == true) -+ fsl_mc_free_irqs(ls_dev); -+ -+ if (desc.stash_affinity) -+ priv->thread = kthread_create_on_cpu(dpio_thread, -+ priv->io, -+ desc.cpu, -+ "dpio_aff%u"); -+ else -+ priv->thread = -+ kthread_create(dpio_thread, -+ priv->io, -+ "dpio_non%u", -+ dpio_attrs.qbman_portal_id); -+ if (IS_ERR(priv->thread)) { -+ dev_err(dev, "DPIO thread failure\n"); -+ err = PTR_ERR(priv->thread); -+ goto err_dpaa_thread; -+ } -+ wake_up_process(priv->thread); -+ } -+ -+ defservice = dpaa2_io_default_service(); -+ err = dpaa2_io_service_add(defservice, priv->io); -+ dpaa2_io_down(defservice); -+ if (err) { -+ dev_err(dev, "DPIO add-to-service failed\n"); -+ goto err_dpaa2_io_add; -+ } -+ -+ dev_info(dev, "dpio: probed object %d\n", ls_dev->obj_desc.id); -+ dev_info(dev, " receives_notifications = %d\n", -+ desc.receives_notifications); -+ dev_info(dev, " has_irq = %d\n", desc.has_irq); -+ dpio_close(ls_dev->mc_io, 0, ls_dev->mc_handle); -+ fsl_mc_portal_free(ls_dev->mc_io); -+ return 0; -+ -+err_dpaa2_io_add: -+ unregister_dpio_irq_handlers(ls_dev); -+/* TEMP: To be restored once polling is removed -+ err_register_dpio_irq: -+ fsl_mc_free_irqs(ls_dev); -+*/ -+err_dpaa_thread: -+err_dpaa2_io_create: -+ dpio_disable(ls_dev->mc_io, 0, ls_dev->mc_handle); -+err_get_attr: -+ dpio_close(ls_dev->mc_io, 0, ls_dev->mc_handle); -+err_open: -+ fsl_mc_portal_free(ls_dev->mc_io); -+err_mcportal: -+ dev_set_drvdata(dev, NULL); -+ devm_kfree(dev, priv); -+err_priv_alloc: -+ return err; -+} -+ -+/* -+ * Tear down interrupts for a given DPIO object -+ */ -+static void dpio_teardown_irqs(struct fsl_mc_device *ls_dev) -+{ -+ /* (void)disable_dpio_irqs(ls_dev); */ -+ unregister_dpio_irq_handlers(ls_dev); -+ fsl_mc_free_irqs(ls_dev); -+} -+ -+static int __cold -+dpaa2_dpio_remove(struct fsl_mc_device *ls_dev) -+{ -+ struct device *dev; -+ struct dpio_priv *priv; -+ int err; -+ -+ dev = &ls_dev->dev; -+ priv = dev_get_drvdata(dev); -+ -+ /* there is no implementation yet for pulling a DPIO object out of a -+ * running service (and they're currently always running). -+ */ -+ dev_crit(dev, "DPIO unplugging is broken, the service holds onto it\n"); -+ -+ if (priv->thread) -+ kthread_stop(priv->thread); -+ else -+ dpio_teardown_irqs(ls_dev); -+ -+ err = fsl_mc_portal_allocate(ls_dev, 0, &ls_dev->mc_io); -+ if (err) { -+ dev_err(dev, "MC portal allocation failed\n"); -+ goto err_mcportal; -+ } -+ -+ err = dpio_open(ls_dev->mc_io, 0, ls_dev->obj_desc.id, -+ &ls_dev->mc_handle); -+ if (err) { -+ dev_err(dev, "dpio_open() failed\n"); -+ goto err_open; -+ } -+ -+ dev_set_drvdata(dev, NULL); -+ dpaa2_io_down(priv->io); -+ -+ err = 0; -+ -+ dpio_disable(ls_dev->mc_io, 0, ls_dev->mc_handle); -+ dpio_close(ls_dev->mc_io, 0, ls_dev->mc_handle); -+err_open: -+ fsl_mc_portal_free(ls_dev->mc_io); -+err_mcportal: -+ return err; -+} -+ -+static const struct fsl_mc_device_match_id dpaa2_dpio_match_id_table[] = { -+ { -+ .vendor = FSL_MC_VENDOR_FREESCALE, -+ .obj_type = "dpio", -+ .ver_major = DPIO_VER_MAJOR, -+ .ver_minor = DPIO_VER_MINOR -+ }, -+ { .vendor = 0x0 } -+}; -+ -+static struct fsl_mc_driver dpaa2_dpio_driver = { -+ .driver = { -+ .name = KBUILD_MODNAME, -+ .owner = THIS_MODULE, -+ }, -+ .probe = dpaa2_dpio_probe, -+ .remove = dpaa2_dpio_remove, -+ .match_id_table = dpaa2_dpio_match_id_table -+}; -+ -+static int dpio_driver_init(void) -+{ -+ int err; -+ -+ err = dpaa2_io_service_driver_init(); -+ if (!err) { -+ err = fsl_mc_driver_register(&dpaa2_dpio_driver); -+ if (err) -+ dpaa2_io_service_driver_exit(); -+ } -+ return err; -+} -+static void dpio_driver_exit(void) -+{ -+ fsl_mc_driver_unregister(&dpaa2_dpio_driver); -+ dpaa2_io_service_driver_exit(); -+} -+module_init(dpio_driver_init); -+module_exit(dpio_driver_exit); -diff --git a/drivers/staging/fsl-mc/bus/dpio/dpio-drv.h b/drivers/staging/fsl-mc/bus/dpio/dpio-drv.h -new file mode 100644 -index 0000000..fe8d40b ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpio/dpio-drv.h -@@ -0,0 +1,33 @@ -+/* Copyright 2014 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. -+ */ -+ -+int dpaa2_io_service_driver_init(void); -+void dpaa2_io_service_driver_exit(void); -diff --git a/drivers/staging/fsl-mc/bus/dpio/dpio.c b/drivers/staging/fsl-mc/bus/dpio/dpio.c -new file mode 100644 -index 0000000..b63edd6 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpio/dpio.c -@@ -0,0 +1,468 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#include "../../include/mc-sys.h" -+#include "../../include/mc-cmd.h" -+#include "fsl_dpio.h" -+#include "fsl_dpio_cmd.h" -+ -+int dpio_open(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ int dpio_id, -+ uint16_t *token) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_OPEN, -+ cmd_flags, -+ 0); -+ DPIO_CMD_OPEN(cmd, dpio_id); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *token = MC_CMD_HDR_READ_TOKEN(cmd.header); -+ -+ return 0; -+} -+ -+int dpio_close(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_CLOSE, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpio_create(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ const struct dpio_cfg *cfg, -+ uint16_t *token) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_CREATE, -+ cmd_flags, -+ 0); -+ DPIO_CMD_CREATE(cmd, cfg); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *token = MC_CMD_HDR_READ_TOKEN(cmd.header); -+ -+ return 0; -+} -+ -+int dpio_destroy(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_DESTROY, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpio_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_ENABLE, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpio_disable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_DISABLE, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpio_is_enabled(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *en) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_IS_ENABLED, cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPIO_RSP_IS_ENABLED(cmd, *en); -+ -+ return 0; -+} -+ -+int dpio_reset(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_RESET, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpio_set_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ struct dpio_irq_cfg *irq_cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_SET_IRQ, -+ cmd_flags, -+ token); -+ DPIO_CMD_SET_IRQ(cmd, irq_index, irq_cfg); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpio_get_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ int *type, -+ struct dpio_irq_cfg *irq_cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_GET_IRQ, -+ cmd_flags, -+ token); -+ DPIO_CMD_GET_IRQ(cmd, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPIO_RSP_GET_IRQ(cmd, *type, irq_cfg); -+ -+ return 0; -+} -+ -+int dpio_set_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t en) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_SET_IRQ_ENABLE, -+ cmd_flags, -+ token); -+ DPIO_CMD_SET_IRQ_ENABLE(cmd, irq_index, en); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpio_get_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t *en) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_GET_IRQ_ENABLE, -+ cmd_flags, -+ token); -+ DPIO_CMD_GET_IRQ_ENABLE(cmd, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPIO_RSP_GET_IRQ_ENABLE(cmd, *en); -+ -+ return 0; -+} -+ -+int dpio_set_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t mask) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_SET_IRQ_MASK, -+ cmd_flags, -+ token); -+ DPIO_CMD_SET_IRQ_MASK(cmd, irq_index, mask); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpio_get_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *mask) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_GET_IRQ_MASK, -+ cmd_flags, -+ token); -+ DPIO_CMD_GET_IRQ_MASK(cmd, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPIO_RSP_GET_IRQ_MASK(cmd, *mask); -+ -+ return 0; -+} -+ -+int dpio_get_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *status) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_GET_IRQ_STATUS, -+ cmd_flags, -+ token); -+ DPIO_CMD_GET_IRQ_STATUS(cmd, irq_index, *status); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPIO_RSP_GET_IRQ_STATUS(cmd, *status); -+ -+ return 0; -+} -+ -+int dpio_clear_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t status) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_CLEAR_IRQ_STATUS, -+ cmd_flags, -+ token); -+ DPIO_CMD_CLEAR_IRQ_STATUS(cmd, irq_index, status); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpio_get_attributes(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpio_attr *attr) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_GET_ATTR, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPIO_RSP_GET_ATTR(cmd, attr); -+ -+ return 0; -+} -+ -+int dpio_set_stashing_destination(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t sdest) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_SET_STASHING_DEST, -+ cmd_flags, -+ token); -+ DPIO_CMD_SET_STASHING_DEST(cmd, sdest); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpio_get_stashing_destination(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t *sdest) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_GET_STASHING_DEST, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPIO_RSP_GET_STASHING_DEST(cmd, *sdest); -+ -+ return 0; -+} -+ -+int dpio_add_static_dequeue_channel(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int dpcon_id, -+ uint8_t *channel_index) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPIO_CMDID_ADD_STATIC_DEQUEUE_CHANNEL, -+ cmd_flags, -+ token); -+ DPIO_CMD_ADD_STATIC_DEQUEUE_CHANNEL(cmd, dpcon_id); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ DPIO_RSP_ADD_STATIC_DEQUEUE_CHANNEL(cmd, *channel_index); -+ -+ return 0; -+} -+ -+int dpio_remove_static_dequeue_channel(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int dpcon_id) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header( -+ DPIO_CMDID_REMOVE_STATIC_DEQUEUE_CHANNEL, -+ cmd_flags, -+ token); -+ DPIO_CMD_REMOVE_STATIC_DEQUEUE_CHANNEL(cmd, dpcon_id); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -diff --git a/drivers/staging/fsl-mc/bus/dpio/dpio_service.c b/drivers/staging/fsl-mc/bus/dpio/dpio_service.c -new file mode 100644 -index 0000000..ebcfd59 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpio/dpio_service.c -@@ -0,0 +1,801 @@ -+/* Copyright 2014 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 -+#include "fsl_qbman_portal.h" -+#include "../../include/mc.h" -+#include "../../include/fsl_dpaa2_io.h" -+#include "fsl_dpio.h" -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "dpio-drv.h" -+#include "qbman_debug.h" -+ -+#define UNIMPLEMENTED() pr_err("FOO: %s unimplemented!\n", __func__) -+ -+#define MAGIC_SERVICE 0xabcd9876 -+#define MAGIC_OBJECT 0x1234fedc -+ -+struct dpaa2_io { -+ /* If MAGIC_SERVICE, this is a group of objects, use the 'service' part -+ * of the union. If MAGIC_OBJECT, use the 'object' part of the union. If -+ * it's neither, something got corrupted. This is mainly to satisfy -+ * dpaa2_io_from_registration(), which dereferences a caller- -+ * instantiated struct and so warrants a bug-checking step - hence the -+ * magic rather than a boolean. -+ */ -+ unsigned int magic; -+ atomic_t refs; -+ union { -+ struct dpaa2_io_service { -+ spinlock_t lock; -+ struct list_head list; -+ /* for targeted dpaa2_io selection */ -+ struct dpaa2_io *objects_by_cpu[NR_CPUS]; -+ cpumask_t cpus_notifications; -+ cpumask_t cpus_stashing; -+ int has_nonaffine; -+ /* slight hack. record the special case of the -+ * "default service", because that's the case where we -+ * need to avoid a kfree() ... */ -+ int is_defservice; -+ } service; -+ struct dpaa2_io_object { -+ struct dpaa2_io_desc dpio_desc; -+ struct qbman_swp_desc swp_desc; -+ struct qbman_swp *swp; -+ /* If the object is part of a service, this is it (and -+ * 'node' is linked into the service's list) */ -+ struct dpaa2_io *service; -+ struct list_head node; -+ /* Interrupt mask, as used with -+ * qbman_swp_interrupt_[gs]et_vanish(). This isn't -+ * locked, because the higher layer is driving all -+ * "ingress" processing. */ -+ uint32_t irq_mask; -+ /* As part of simplifying assumptions, we provide an -+ * irq-safe lock for each type of DPIO operation that -+ * isn't innately lockless. The selection algorithms -+ * (which are simplified) require this, whereas -+ * eventually adherence to cpu-affinity will presumably -+ * relax the locking requirements. */ -+ spinlock_t lock_mgmt_cmd; -+ spinlock_t lock_notifications; -+ struct list_head notifications; -+ } object; -+ }; -+}; -+ -+struct dpaa2_io_store { -+ unsigned int max; -+ dma_addr_t paddr; -+ struct dpaa2_dq *vaddr; -+ void *alloced_addr; /* the actual return from kmalloc as it may -+ be adjusted for alignment purposes */ -+ unsigned int idx; /* position of the next-to-be-returned entry */ -+ struct qbman_swp *swp; /* portal used to issue VDQCR */ -+ struct device *dev; /* device used for DMA mapping */ -+}; -+ -+static struct dpaa2_io def_serv; -+ -+/**********************/ -+/* Internal functions */ -+/**********************/ -+ -+static void service_init(struct dpaa2_io *d, int is_defservice) -+{ -+ struct dpaa2_io_service *s = &d->service; -+ -+ d->magic = MAGIC_SERVICE; -+ atomic_set(&d->refs, 1); -+ spin_lock_init(&s->lock); -+ INIT_LIST_HEAD(&s->list); -+ cpumask_clear(&s->cpus_notifications); -+ cpumask_clear(&s->cpus_stashing); -+ s->has_nonaffine = 0; -+ s->is_defservice = is_defservice; -+} -+ -+/* Selection algorithms, stupid ones at that. These are to handle the case where -+ * the given dpaa2_io is a service, by choosing the non-service dpaa2_io within -+ * it to use. -+ */ -+static struct dpaa2_io *_service_select_by_cpu_slow(struct dpaa2_io_service *ss, -+ int cpu) -+{ -+ struct dpaa2_io *o; -+ unsigned long irqflags; -+ -+ spin_lock_irqsave(&ss->lock, irqflags); -+ /* TODO: this is about the dumbest and slowest selection algorithm you -+ * could imagine. (We're looking for something working first, and -+ * something efficient second...) -+ */ -+ list_for_each_entry(o, &ss->list, object.node) -+ if (o->object.dpio_desc.cpu == cpu) -+ goto found; -+ -+ /* No joy. Try the first nonaffine portal (bleurgh) */ -+ if (ss->has_nonaffine) -+ list_for_each_entry(o, &ss->list, object.node) -+ if (!o->object.dpio_desc.stash_affinity) -+ goto found; -+ -+ /* No joy. Try the first object. Told you it was horrible. */ -+ if (!list_empty(&ss->list)) -+ o = list_entry(ss->list.next, struct dpaa2_io, object.node); -+ else -+ o = NULL; -+ -+found: -+ spin_unlock_irqrestore(&ss->lock, irqflags); -+ return o; -+} -+ -+static struct dpaa2_io *service_select_by_cpu(struct dpaa2_io *d, int cpu) -+{ -+ struct dpaa2_io_service *ss; -+ unsigned long irqflags; -+ -+ if (!d) -+ d = &def_serv; -+ else if (d->magic == MAGIC_OBJECT) -+ return d; -+ BUG_ON(d->magic != MAGIC_SERVICE); -+ -+ ss = &d->service; -+ -+ /* If cpu==-1, choose the current cpu, with no guarantees about -+ * potentially being migrated away. -+ */ -+ if (unlikely(cpu < 0)) { -+ spin_lock_irqsave(&ss->lock, irqflags); -+ cpu = smp_processor_id(); -+ spin_unlock_irqrestore(&ss->lock, irqflags); -+ -+ return _service_select_by_cpu_slow(ss, cpu); -+ } -+ -+ /* If a specific cpu was requested, pick it up immediately */ -+ return ss->objects_by_cpu[cpu]; -+} -+ -+static inline struct dpaa2_io *service_select_any(struct dpaa2_io *d) -+{ -+ struct dpaa2_io_service *ss; -+ struct dpaa2_io *o; -+ unsigned long irqflags; -+ -+ if (!d) -+ d = &def_serv; -+ else if (d->magic == MAGIC_OBJECT) -+ return d; -+ BUG_ON(d->magic != MAGIC_SERVICE); -+ -+ /* -+ * Lock the service, looking for the first DPIO object in the list, -+ * ignore everything else about that DPIO, and choose it to do the -+ * operation! As a post-selection step, move the DPIO to the end of -+ * the list. It should improve load-balancing a little, although it -+ * might also incur a performance hit, given that the lock is *global* -+ * and this may be called on the fast-path... -+ */ -+ ss = &d->service; -+ spin_lock_irqsave(&ss->lock, irqflags); -+ if (!list_empty(&ss->list)) { -+ o = list_entry(ss->list.next, struct dpaa2_io, object.node); -+ list_del(&o->object.node); -+ list_add_tail(&o->object.node, &ss->list); -+ } else -+ o = NULL; -+ spin_unlock_irqrestore(&ss->lock, irqflags); -+ return o; -+} -+ -+/* If the context is not preemptible, select the service affine to the -+ * current cpu. Otherwise, "select any". -+ */ -+static inline struct dpaa2_io *_service_select(struct dpaa2_io *d) -+{ -+ struct dpaa2_io *temp = d; -+ -+ if (likely(!preemptible())) { -+ d = service_select_by_cpu(d, smp_processor_id()); -+ if (likely(d)) -+ return d; -+ } -+ return service_select_any(temp); -+} -+ -+/**********************/ -+/* Exported functions */ -+/**********************/ -+ -+struct dpaa2_io *dpaa2_io_create(const struct dpaa2_io_desc *desc) -+{ -+ struct dpaa2_io *ret = kmalloc(sizeof(*ret), GFP_KERNEL); -+ struct dpaa2_io_object *o = &ret->object; -+ -+ if (!ret) -+ return NULL; -+ ret->magic = MAGIC_OBJECT; -+ atomic_set(&ret->refs, 1); -+ o->dpio_desc = *desc; -+ o->swp_desc.cena_bar = o->dpio_desc.regs_cena; -+ o->swp_desc.cinh_bar = o->dpio_desc.regs_cinh; -+ o->swp_desc.qman_version = o->dpio_desc.qman_version; -+ o->swp = qbman_swp_init(&o->swp_desc); -+ o->service = NULL; -+ if (!o->swp) { -+ kfree(ret); -+ return NULL; -+ } -+ INIT_LIST_HEAD(&o->node); -+ spin_lock_init(&o->lock_mgmt_cmd); -+ spin_lock_init(&o->lock_notifications); -+ INIT_LIST_HEAD(&o->notifications); -+ if (!o->dpio_desc.has_irq) -+ qbman_swp_interrupt_set_vanish(o->swp, 0xffffffff); -+ else { -+ /* For now only enable DQRR interrupts */ -+ qbman_swp_interrupt_set_trigger(o->swp, -+ QBMAN_SWP_INTERRUPT_DQRI); -+ } -+ qbman_swp_interrupt_clear_status(o->swp, 0xffffffff); -+ if (o->dpio_desc.receives_notifications) -+ qbman_swp_push_set(o->swp, 0, 1); -+ return ret; -+} -+EXPORT_SYMBOL(dpaa2_io_create); -+ -+struct dpaa2_io *dpaa2_io_create_service(void) -+{ -+ struct dpaa2_io *ret = kmalloc(sizeof(*ret), GFP_KERNEL); -+ -+ if (ret) -+ service_init(ret, 0); -+ return ret; -+} -+EXPORT_SYMBOL(dpaa2_io_create_service); -+ -+struct dpaa2_io *dpaa2_io_default_service(void) -+{ -+ atomic_inc(&def_serv.refs); -+ return &def_serv; -+} -+EXPORT_SYMBOL(dpaa2_io_default_service); -+ -+void dpaa2_io_down(struct dpaa2_io *d) -+{ -+ if (!atomic_dec_and_test(&d->refs)) -+ return; -+ if (d->magic == MAGIC_SERVICE) { -+ BUG_ON(!list_empty(&d->service.list)); -+ if (d->service.is_defservice) -+ /* avoid the kfree()! */ -+ return; -+ } else { -+ BUG_ON(d->magic != MAGIC_OBJECT); -+ BUG_ON(d->object.service); -+ BUG_ON(!list_empty(&d->object.notifications)); -+ } -+ kfree(d); -+} -+EXPORT_SYMBOL(dpaa2_io_down); -+ -+int dpaa2_io_service_add(struct dpaa2_io *s, struct dpaa2_io *o) -+{ -+ struct dpaa2_io_service *ss = &s->service; -+ struct dpaa2_io_object *oo = &o->object; -+ int res = -EINVAL; -+ -+ if ((s->magic != MAGIC_SERVICE) || (o->magic != MAGIC_OBJECT)) -+ return res; -+ atomic_inc(&o->refs); -+ atomic_inc(&s->refs); -+ spin_lock(&ss->lock); -+ /* 'obj' must not already be associated with a service */ -+ if (!oo->service) { -+ oo->service = s; -+ list_add(&oo->node, &ss->list); -+ if (oo->dpio_desc.receives_notifications) { -+ cpumask_set_cpu(oo->dpio_desc.cpu, -+ &ss->cpus_notifications); -+ /* Update the fast-access array */ -+ ss->objects_by_cpu[oo->dpio_desc.cpu] = -+ container_of(oo, struct dpaa2_io, object); -+ } -+ if (oo->dpio_desc.stash_affinity) -+ cpumask_set_cpu(oo->dpio_desc.cpu, -+ &ss->cpus_stashing); -+ if (!oo->dpio_desc.stash_affinity) -+ ss->has_nonaffine = 1; -+ /* success */ -+ res = 0; -+ } -+ spin_unlock(&ss->lock); -+ if (res) { -+ dpaa2_io_down(s); -+ dpaa2_io_down(o); -+ } -+ return res; -+} -+EXPORT_SYMBOL(dpaa2_io_service_add); -+ -+int dpaa2_io_get_descriptor(struct dpaa2_io *obj, struct dpaa2_io_desc *desc) -+{ -+ if (obj->magic == MAGIC_SERVICE) -+ return -EINVAL; -+ BUG_ON(obj->magic != MAGIC_OBJECT); -+ *desc = obj->object.dpio_desc; -+ return 0; -+} -+EXPORT_SYMBOL(dpaa2_io_get_descriptor); -+ -+#define DPAA_POLL_MAX 32 -+ -+int dpaa2_io_poll(struct dpaa2_io *obj) -+{ -+ const struct dpaa2_dq *dq; -+ struct qbman_swp *swp; -+ int max = 0; -+ -+ if (obj->magic != MAGIC_OBJECT) -+ return -EINVAL; -+ swp = obj->object.swp; -+ dq = qbman_swp_dqrr_next(swp); -+ while (dq) { -+ if (qbman_result_is_SCN(dq)) { -+ struct dpaa2_io_notification_ctx *ctx; -+ uint64_t q64; -+ -+ q64 = qbman_result_SCN_ctx(dq); -+ ctx = (void *)q64; -+ ctx->cb(ctx); -+ } else -+ pr_crit("Unrecognised/ignored DQRR entry\n"); -+ qbman_swp_dqrr_consume(swp, dq); -+ ++max; -+ if (max > DPAA_POLL_MAX) -+ return 0; -+ dq = qbman_swp_dqrr_next(swp); -+ } -+ return 0; -+} -+EXPORT_SYMBOL(dpaa2_io_poll); -+ -+int dpaa2_io_irq(struct dpaa2_io *obj) -+{ -+ struct qbman_swp *swp; -+ uint32_t status; -+ -+ if (obj->magic != MAGIC_OBJECT) -+ return -EINVAL; -+ swp = obj->object.swp; -+ status = qbman_swp_interrupt_read_status(swp); -+ if (!status) -+ return IRQ_NONE; -+ dpaa2_io_poll(obj); -+ qbman_swp_interrupt_clear_status(swp, status); -+ qbman_swp_interrupt_set_inhibit(swp, 0); -+ return IRQ_HANDLED; -+} -+EXPORT_SYMBOL(dpaa2_io_irq); -+ -+int dpaa2_io_pause_poll(struct dpaa2_io *obj) -+{ -+ UNIMPLEMENTED(); -+ return -EINVAL; -+} -+EXPORT_SYMBOL(dpaa2_io_pause_poll); -+ -+int dpaa2_io_resume_poll(struct dpaa2_io *obj) -+{ -+ UNIMPLEMENTED(); -+ return -EINVAL; -+} -+EXPORT_SYMBOL(dpaa2_io_resume_poll); -+ -+void dpaa2_io_service_notifications(struct dpaa2_io *s, cpumask_t *mask) -+{ -+ struct dpaa2_io_service *ss = &s->service; -+ -+ BUG_ON(s->magic != MAGIC_SERVICE); -+ cpumask_copy(mask, &ss->cpus_notifications); -+} -+EXPORT_SYMBOL(dpaa2_io_service_notifications); -+ -+void dpaa2_io_service_stashing(struct dpaa2_io *s, cpumask_t *mask) -+{ -+ struct dpaa2_io_service *ss = &s->service; -+ -+ BUG_ON(s->magic != MAGIC_SERVICE); -+ cpumask_copy(mask, &ss->cpus_stashing); -+} -+EXPORT_SYMBOL(dpaa2_io_service_stashing); -+ -+int dpaa2_io_service_has_nonaffine(struct dpaa2_io *s) -+{ -+ struct dpaa2_io_service *ss = &s->service; -+ -+ BUG_ON(s->magic != MAGIC_SERVICE); -+ return ss->has_nonaffine; -+} -+EXPORT_SYMBOL(dpaa2_io_service_has_nonaffine); -+ -+int dpaa2_io_service_register(struct dpaa2_io *d, -+ struct dpaa2_io_notification_ctx *ctx) -+{ -+ unsigned long irqflags; -+ -+ d = service_select_by_cpu(d, ctx->desired_cpu); -+ if (!d) -+ return -ENODEV; -+ ctx->dpio_id = d->object.dpio_desc.dpio_id; -+ ctx->qman64 = (uint64_t)ctx; -+ ctx->dpio_private = d; -+ spin_lock_irqsave(&d->object.lock_notifications, irqflags); -+ list_add(&ctx->node, &d->object.notifications); -+ spin_unlock_irqrestore(&d->object.lock_notifications, irqflags); -+ if (ctx->is_cdan) -+ /* Enable the generation of CDAN notifications */ -+ qbman_swp_CDAN_set_context_enable(d->object.swp, -+ (uint16_t)ctx->id, -+ ctx->qman64); -+ return 0; -+} -+EXPORT_SYMBOL(dpaa2_io_service_register); -+ -+int dpaa2_io_service_deregister(struct dpaa2_io *service, -+ struct dpaa2_io_notification_ctx *ctx) -+{ -+ struct dpaa2_io *d = ctx->dpio_private; -+ unsigned long irqflags; -+ -+ if (!service) -+ service = &def_serv; -+ BUG_ON((service != d) && (service != d->object.service)); -+ if (ctx->is_cdan) -+ qbman_swp_CDAN_disable(d->object.swp, -+ (uint16_t)ctx->id); -+ spin_lock_irqsave(&d->object.lock_notifications, irqflags); -+ list_del(&ctx->node); -+ spin_unlock_irqrestore(&d->object.lock_notifications, irqflags); -+ return 0; -+} -+EXPORT_SYMBOL(dpaa2_io_service_deregister); -+ -+int dpaa2_io_service_rearm(struct dpaa2_io *d, -+ struct dpaa2_io_notification_ctx *ctx) -+{ -+ unsigned long irqflags; -+ int err; -+ -+ d = _service_select(d); -+ if (!d) -+ return -ENODEV; -+ spin_lock_irqsave(&d->object.lock_mgmt_cmd, irqflags); -+ if (ctx->is_cdan) -+ err = qbman_swp_CDAN_enable(d->object.swp, (uint16_t)ctx->id); -+ else -+ err = qbman_swp_fq_schedule(d->object.swp, ctx->id); -+ spin_unlock_irqrestore(&d->object.lock_mgmt_cmd, irqflags); -+ return err; -+} -+EXPORT_SYMBOL(dpaa2_io_service_rearm); -+ -+int dpaa2_io_from_registration(struct dpaa2_io_notification_ctx *ctx, -+ struct dpaa2_io **io) -+{ -+ struct dpaa2_io_notification_ctx *tmp; -+ struct dpaa2_io *d = ctx->dpio_private; -+ unsigned long irqflags; -+ int ret = 0; -+ -+ BUG_ON(d->magic != MAGIC_OBJECT); -+ /* Iterate the notifications associated with 'd' looking for a match. If -+ * not, we've been passed an unregistered ctx! */ -+ spin_lock_irqsave(&d->object.lock_notifications, irqflags); -+ list_for_each_entry(tmp, &d->object.notifications, node) -+ if (tmp == ctx) -+ goto found; -+ ret = -EINVAL; -+found: -+ spin_unlock_irqrestore(&d->object.lock_notifications, irqflags); -+ if (!ret) { -+ atomic_inc(&d->refs); -+ *io = d; -+ } -+ return ret; -+} -+EXPORT_SYMBOL(dpaa2_io_from_registration); -+ -+int dpaa2_io_service_get_persistent(struct dpaa2_io *service, int cpu, -+ struct dpaa2_io **ret) -+{ -+ if (cpu == -1) -+ *ret = service_select_any(service); -+ else -+ *ret = service_select_by_cpu(service, cpu); -+ if (*ret) { -+ atomic_inc(&(*ret)->refs); -+ return 0; -+ } -+ return -ENODEV; -+} -+EXPORT_SYMBOL(dpaa2_io_service_get_persistent); -+ -+int dpaa2_io_service_pull_fq(struct dpaa2_io *d, uint32_t fqid, -+ struct dpaa2_io_store *s) -+{ -+ struct qbman_pull_desc pd; -+ int err; -+ -+ qbman_pull_desc_clear(&pd); -+ qbman_pull_desc_set_storage(&pd, s->vaddr, s->paddr, 1); -+ qbman_pull_desc_set_numframes(&pd, (uint8_t)s->max); -+ qbman_pull_desc_set_fq(&pd, fqid); -+ d = _service_select(d); -+ if (!d) -+ return -ENODEV; -+ s->swp = d->object.swp; -+ err = qbman_swp_pull(d->object.swp, &pd); -+ if (err) -+ s->swp = NULL; -+ return err; -+} -+EXPORT_SYMBOL(dpaa2_io_service_pull_fq); -+ -+int dpaa2_io_service_pull_channel(struct dpaa2_io *d, uint32_t channelid, -+ struct dpaa2_io_store *s) -+{ -+ struct qbman_pull_desc pd; -+ int err; -+ -+ qbman_pull_desc_clear(&pd); -+ qbman_pull_desc_set_storage(&pd, s->vaddr, s->paddr, 1); -+ qbman_pull_desc_set_numframes(&pd, (uint8_t)s->max); -+ qbman_pull_desc_set_channel(&pd, channelid, qbman_pull_type_prio); -+ d = _service_select(d); -+ if (!d) -+ return -ENODEV; -+ s->swp = d->object.swp; -+ err = qbman_swp_pull(d->object.swp, &pd); -+ if (err) -+ s->swp = NULL; -+ return err; -+} -+EXPORT_SYMBOL(dpaa2_io_service_pull_channel); -+ -+int dpaa2_io_service_enqueue_fq(struct dpaa2_io *d, -+ uint32_t fqid, -+ const struct dpaa2_fd *fd) -+{ -+ struct qbman_eq_desc ed; -+ -+ d = _service_select(d); -+ if (!d) -+ return -ENODEV; -+ qbman_eq_desc_clear(&ed); -+ qbman_eq_desc_set_no_orp(&ed, 0); -+ qbman_eq_desc_set_fq(&ed, fqid); -+ return qbman_swp_enqueue(d->object.swp, &ed, -+ (const struct qbman_fd *)fd); -+} -+EXPORT_SYMBOL(dpaa2_io_service_enqueue_fq); -+ -+int dpaa2_io_service_enqueue_qd(struct dpaa2_io *d, -+ uint32_t qdid, uint8_t prio, uint16_t qdbin, -+ const struct dpaa2_fd *fd) -+{ -+ struct qbman_eq_desc ed; -+ -+ d = _service_select(d); -+ if (!d) -+ return -ENODEV; -+ qbman_eq_desc_clear(&ed); -+ qbman_eq_desc_set_no_orp(&ed, 0); -+ qbman_eq_desc_set_qd(&ed, qdid, qdbin, prio); -+ return qbman_swp_enqueue(d->object.swp, &ed, -+ (const struct qbman_fd *)fd); -+} -+EXPORT_SYMBOL(dpaa2_io_service_enqueue_qd); -+ -+int dpaa2_io_service_release(struct dpaa2_io *d, -+ uint32_t bpid, -+ const uint64_t *buffers, -+ unsigned int num_buffers) -+{ -+ struct qbman_release_desc rd; -+ -+ d = _service_select(d); -+ if (!d) -+ return -ENODEV; -+ qbman_release_desc_clear(&rd); -+ qbman_release_desc_set_bpid(&rd, bpid); -+ return qbman_swp_release(d->object.swp, &rd, buffers, num_buffers); -+} -+EXPORT_SYMBOL(dpaa2_io_service_release); -+ -+int dpaa2_io_service_acquire(struct dpaa2_io *d, -+ uint32_t bpid, -+ uint64_t *buffers, -+ unsigned int num_buffers) -+{ -+ unsigned long irqflags; -+ int err; -+ -+ d = _service_select(d); -+ if (!d) -+ return -ENODEV; -+ spin_lock_irqsave(&d->object.lock_mgmt_cmd, irqflags); -+ err = qbman_swp_acquire(d->object.swp, bpid, buffers, num_buffers); -+ spin_unlock_irqrestore(&d->object.lock_mgmt_cmd, irqflags); -+ return err; -+} -+EXPORT_SYMBOL(dpaa2_io_service_acquire); -+ -+struct dpaa2_io_store *dpaa2_io_store_create(unsigned int max_frames, -+ struct device *dev) -+{ -+ struct dpaa2_io_store *ret = kmalloc(sizeof(*ret), GFP_KERNEL); -+ size_t size; -+ -+ BUG_ON(!max_frames || (max_frames > 16)); -+ if (!ret) -+ return NULL; -+ ret->max = max_frames; -+ size = max_frames * sizeof(struct dpaa2_dq) + 64; -+ ret->alloced_addr = kmalloc(size, GFP_KERNEL); -+ if (!ret->alloced_addr) { -+ kfree(ret); -+ return NULL; -+ } -+ ret->vaddr = PTR_ALIGN(ret->alloced_addr, 64); -+ ret->paddr = dma_map_single(dev, ret->vaddr, -+ sizeof(struct dpaa2_dq) * max_frames, -+ DMA_FROM_DEVICE); -+ if (dma_mapping_error(dev, ret->paddr)) { -+ kfree(ret->alloced_addr); -+ kfree(ret); -+ return NULL; -+ } -+ ret->idx = 0; -+ ret->dev = dev; -+ return ret; -+} -+EXPORT_SYMBOL(dpaa2_io_store_create); -+ -+void dpaa2_io_store_destroy(struct dpaa2_io_store *s) -+{ -+ dma_unmap_single(s->dev, s->paddr, sizeof(struct dpaa2_dq) * s->max, -+ DMA_FROM_DEVICE); -+ kfree(s->alloced_addr); -+ kfree(s); -+} -+EXPORT_SYMBOL(dpaa2_io_store_destroy); -+ -+struct dpaa2_dq *dpaa2_io_store_next(struct dpaa2_io_store *s, int *is_last) -+{ -+ int match; -+ struct dpaa2_dq *ret = &s->vaddr[s->idx]; -+ -+ match = qbman_result_has_new_result(s->swp, ret); -+ if (!match) { -+ *is_last = 0; -+ return NULL; -+ } -+ BUG_ON(!qbman_result_is_DQ(ret)); -+ s->idx++; -+ if (dpaa2_dq_is_pull_complete(ret)) { -+ *is_last = 1; -+ s->idx = 0; -+ /* If we get an empty dequeue result to terminate a zero-results -+ * vdqcr, return NULL to the caller rather than expecting him to -+ * check non-NULL results every time. */ -+ if (!(dpaa2_dq_flags(ret) & DPAA2_DQ_STAT_VALIDFRAME)) -+ ret = NULL; -+ } else -+ *is_last = 0; -+ return ret; -+} -+EXPORT_SYMBOL(dpaa2_io_store_next); -+ -+#ifdef CONFIG_FSL_QBMAN_DEBUG -+int dpaa2_io_query_fq_count(struct dpaa2_io *d, uint32_t fqid, -+ uint32_t *fcnt, uint32_t *bcnt) -+{ -+ struct qbman_attr state; -+ struct qbman_swp *swp; -+ unsigned long irqflags; -+ int ret; -+ -+ d = service_select_any(d); -+ if (!d) -+ return -ENODEV; -+ -+ swp = d->object.swp; -+ spin_lock_irqsave(&d->object.lock_mgmt_cmd, irqflags); -+ ret = qbman_fq_query_state(swp, fqid, &state); -+ spin_unlock_irqrestore(&d->object.lock_mgmt_cmd, irqflags); -+ if (ret) -+ return ret; -+ *fcnt = qbman_fq_state_frame_count(&state); -+ *bcnt = qbman_fq_state_byte_count(&state); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dpaa2_io_query_fq_count); -+ -+int dpaa2_io_query_bp_count(struct dpaa2_io *d, uint32_t bpid, -+ uint32_t *num) -+{ -+ struct qbman_attr state; -+ struct qbman_swp *swp; -+ unsigned long irqflags; -+ int ret; -+ -+ d = service_select_any(d); -+ if (!d) -+ return -ENODEV; -+ -+ swp = d->object.swp; -+ spin_lock_irqsave(&d->object.lock_mgmt_cmd, irqflags); -+ ret = qbman_bp_query(swp, bpid, &state); -+ spin_unlock_irqrestore(&d->object.lock_mgmt_cmd, irqflags); -+ if (ret) -+ return ret; -+ *num = qbman_bp_info_num_free_bufs(&state); -+ return 0; -+} -+EXPORT_SYMBOL(dpaa2_io_query_bp_count); -+ -+#endif -+ -+/* module init/exit hooks called from dpio-drv.c. These are declared in -+ * dpio-drv.h. -+ */ -+int dpaa2_io_service_driver_init(void) -+{ -+ service_init(&def_serv, 1); -+ return 0; -+} -+ -+void dpaa2_io_service_driver_exit(void) -+{ -+ if (atomic_read(&def_serv.refs) != 1) -+ pr_err("default DPIO service leaves dangling DPIO objects!\n"); -+} -diff --git a/drivers/staging/fsl-mc/bus/dpio/fsl_dpio.h b/drivers/staging/fsl-mc/bus/dpio/fsl_dpio.h -new file mode 100644 -index 0000000..88a492f ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpio/fsl_dpio.h -@@ -0,0 +1,460 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef __FSL_DPIO_H -+#define __FSL_DPIO_H -+ -+/* Data Path I/O Portal API -+ * Contains initialization APIs and runtime control APIs for DPIO -+ */ -+ -+struct fsl_mc_io; -+ -+/** -+ * dpio_open() - Open a control session for the specified object -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @dpio_id: DPIO unique ID -+ * @token: Returned token; use in subsequent API calls -+ * -+ * This function can be used to open a control session for an -+ * already created object; an object may have been declared in -+ * the DPL or by calling the dpio_create() function. -+ * This function returns a unique authentication token, -+ * associated with the specific object ID and the specific MC -+ * portal; this token must be used in all subsequent commands for -+ * this specific object. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpio_open(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ int dpio_id, -+ uint16_t *token); -+ -+/** -+ * dpio_close() - Close the control session of the object -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpio_close(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * enum dpio_channel_mode - DPIO notification channel mode -+ * @DPIO_NO_CHANNEL: No support for notification channel -+ * @DPIO_LOCAL_CHANNEL: Notifications on data availability can be received by a -+ * dedicated channel in the DPIO; user should point the queue's -+ * destination in the relevant interface to this DPIO -+ */ -+enum dpio_channel_mode { -+ DPIO_NO_CHANNEL = 0, -+ DPIO_LOCAL_CHANNEL = 1, -+}; -+ -+/** -+ * struct dpio_cfg - Structure representing DPIO configuration -+ * @channel_mode: Notification channel mode -+ * @num_priorities: Number of priorities for the notification channel (1-8); -+ * relevant only if 'channel_mode = DPIO_LOCAL_CHANNEL' -+ */ -+struct dpio_cfg { -+ enum dpio_channel_mode channel_mode; -+ uint8_t num_priorities; -+}; -+ -+/** -+ * dpio_create() - Create the DPIO object. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @cfg: Configuration structure -+ * @token: Returned token; use in subsequent API calls -+ * -+ * Create the DPIO object, allocate required resources and -+ * perform required initialization. -+ * -+ * The object can be created either by declaring it in the -+ * DPL file, or by calling this function. -+ * -+ * This function returns a unique authentication token, -+ * associated with the specific object ID and the specific MC -+ * portal; this token must be used in all subsequent calls to -+ * this specific object. For objects that are created using the -+ * DPL file, call dpio_open() function to get an authentication -+ * token first. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpio_create(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ const struct dpio_cfg *cfg, -+ uint16_t *token); -+ -+/** -+ * dpio_destroy() - Destroy the DPIO object and release all its resources. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * -+ * Return: '0' on Success; Error code otherwise -+ */ -+int dpio_destroy(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * dpio_enable() - Enable the DPIO, allow I/O portal operations. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * -+ * Return: '0' on Success; Error code otherwise -+ */ -+int dpio_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * dpio_disable() - Disable the DPIO, stop any I/O portal operation. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * -+ * Return: '0' on Success; Error code otherwise -+ */ -+int dpio_disable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * dpio_is_enabled() - Check if the DPIO is enabled. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * @en: Returns '1' if object is enabled; '0' otherwise -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpio_is_enabled(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *en); -+ -+/** -+ * dpio_reset() - Reset the DPIO, returns the object to initial state. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpio_reset(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * dpio_set_stashing_destination() - Set the stashing destination. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * @sdest: stashing destination value -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpio_set_stashing_destination(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t sdest); -+ -+/** -+ * dpio_get_stashing_destination() - Get the stashing destination.. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * @sdest: Returns the stashing destination value -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpio_get_stashing_destination(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t *sdest); -+ -+/** -+ * dpio_add_static_dequeue_channel() - Add a static dequeue channel. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * @dpcon_id: DPCON object ID -+ * @channel_index: Returned channel index to be used in qbman API -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpio_add_static_dequeue_channel(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int dpcon_id, -+ uint8_t *channel_index); -+ -+/** -+ * dpio_remove_static_dequeue_channel() - Remove a static dequeue channel. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * @dpcon_id: DPCON object ID -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpio_remove_static_dequeue_channel(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int dpcon_id); -+ -+/** -+ * DPIO IRQ Index and Events -+ */ -+ -+/** -+ * Irq software-portal index -+ */ -+#define DPIO_IRQ_SWP_INDEX 0 -+ -+/** -+ * struct dpio_irq_cfg - IRQ configuration -+ * @addr: Address that must be written to signal a message-based interrupt -+ * @val: Value to write into irq_addr address -+ * @irq_num: A user defined number associated with this IRQ -+ */ -+struct dpio_irq_cfg { -+ uint64_t addr; -+ uint32_t val; -+ int irq_num; -+}; -+ -+/** -+ * dpio_set_irq() - Set IRQ information for the DPIO to trigger an interrupt. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * @irq_index: Identifies the interrupt index to configure -+ * @irq_cfg: IRQ configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpio_set_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ struct dpio_irq_cfg *irq_cfg); -+ -+/** -+ * dpio_get_irq() - Get IRQ information from the DPIO. -+ * -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * @irq_index: The interrupt index to configure -+ * @type: Interrupt type: 0 represents message interrupt -+ * type (both irq_addr and irq_val are valid) -+ * @irq_cfg: IRQ attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpio_get_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ int *type, -+ struct dpio_irq_cfg *irq_cfg); -+ -+/** -+ * dpio_set_irq_enable() - Set overall interrupt state. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * @irq_index: The interrupt index to configure -+ * @en: Interrupt state - enable = 1, disable = 0 -+ * -+ * Allows GPP software to control when interrupts are generated. -+ * Each interrupt can have up to 32 causes. The enable/disable control's the -+ * overall interrupt state. if the interrupt is disabled no causes will cause -+ * an interrupt. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpio_set_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t en); -+ -+/** -+ * dpio_get_irq_enable() - Get overall interrupt state -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * @irq_index: The interrupt index to configure -+ * @en: Returned interrupt state - enable = 1, disable = 0 -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpio_get_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t *en); -+ -+/** -+ * dpio_set_irq_mask() - Set interrupt mask. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * @irq_index: The interrupt index to configure -+ * @mask: event mask to trigger interrupt; -+ * each bit: -+ * 0 = ignore event -+ * 1 = consider event for asserting IRQ -+ * -+ * Every interrupt can have up to 32 causes and the interrupt model supports -+ * masking/unmasking each cause independently -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpio_set_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t mask); -+ -+/** -+ * dpio_get_irq_mask() - Get interrupt mask. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * @irq_index: The interrupt index to configure -+ * @mask: Returned event mask to trigger interrupt -+ * -+ * Every interrupt can have up to 32 causes and the interrupt model supports -+ * masking/unmasking each cause independently -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpio_get_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *mask); -+ -+/** -+ * dpio_get_irq_status() - Get the current status of any pending interrupts. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * @irq_index: The interrupt index to configure -+ * @status: Returned interrupts status - one bit per cause: -+ * 0 = no interrupt pending -+ * 1 = interrupt pending -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpio_get_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *status); -+ -+/** -+ * dpio_clear_irq_status() - Clear a pending interrupt's status -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * @irq_index: The interrupt index to configure -+ * @status: bits to clear (W1C) - one bit per cause: -+ * 0 = don't change -+ * 1 = clear status bit -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpio_clear_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t status); -+ -+/** -+ * struct dpio_attr - Structure representing DPIO attributes -+ * @id: DPIO object ID -+ * @version: DPIO version -+ * @qbman_portal_ce_offset: offset of the software portal cache-enabled area -+ * @qbman_portal_ci_offset: offset of the software portal cache-inhibited area -+ * @qbman_portal_id: Software portal ID -+ * @channel_mode: Notification channel mode -+ * @num_priorities: Number of priorities for the notification channel (1-8); -+ * relevant only if 'channel_mode = DPIO_LOCAL_CHANNEL' -+ * @qbman_version: QBMAN version -+ */ -+struct dpio_attr { -+ int id; -+ /** -+ * struct version - DPIO version -+ * @major: DPIO major version -+ * @minor: DPIO minor version -+ */ -+ struct { -+ uint16_t major; -+ uint16_t minor; -+ } version; -+ uint64_t qbman_portal_ce_offset; -+ uint64_t qbman_portal_ci_offset; -+ uint16_t qbman_portal_id; -+ enum dpio_channel_mode channel_mode; -+ uint8_t num_priorities; -+ uint32_t qbman_version; -+}; -+ -+/** -+ * dpio_get_attributes() - Retrieve DPIO attributes -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPIO object -+ * @attr: Returned object's attributes -+ * -+ * Return: '0' on Success; Error code otherwise -+ */ -+int dpio_get_attributes(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpio_attr *attr); -+#endif /* __FSL_DPIO_H */ -diff --git a/drivers/staging/fsl-mc/bus/dpio/fsl_dpio_cmd.h b/drivers/staging/fsl-mc/bus/dpio/fsl_dpio_cmd.h -new file mode 100644 -index 0000000..f339cd6 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpio/fsl_dpio_cmd.h -@@ -0,0 +1,184 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef _FSL_DPIO_CMD_H -+#define _FSL_DPIO_CMD_H -+ -+/* DPIO Version */ -+#define DPIO_VER_MAJOR 3 -+#define DPIO_VER_MINOR 2 -+ -+/* Command IDs */ -+#define DPIO_CMDID_CLOSE 0x800 -+#define DPIO_CMDID_OPEN 0x803 -+#define DPIO_CMDID_CREATE 0x903 -+#define DPIO_CMDID_DESTROY 0x900 -+ -+#define DPIO_CMDID_ENABLE 0x002 -+#define DPIO_CMDID_DISABLE 0x003 -+#define DPIO_CMDID_GET_ATTR 0x004 -+#define DPIO_CMDID_RESET 0x005 -+#define DPIO_CMDID_IS_ENABLED 0x006 -+ -+#define DPIO_CMDID_SET_IRQ 0x010 -+#define DPIO_CMDID_GET_IRQ 0x011 -+#define DPIO_CMDID_SET_IRQ_ENABLE 0x012 -+#define DPIO_CMDID_GET_IRQ_ENABLE 0x013 -+#define DPIO_CMDID_SET_IRQ_MASK 0x014 -+#define DPIO_CMDID_GET_IRQ_MASK 0x015 -+#define DPIO_CMDID_GET_IRQ_STATUS 0x016 -+#define DPIO_CMDID_CLEAR_IRQ_STATUS 0x017 -+ -+#define DPIO_CMDID_SET_STASHING_DEST 0x120 -+#define DPIO_CMDID_GET_STASHING_DEST 0x121 -+#define DPIO_CMDID_ADD_STATIC_DEQUEUE_CHANNEL 0x122 -+#define DPIO_CMDID_REMOVE_STATIC_DEQUEUE_CHANNEL 0x123 -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_CMD_OPEN(cmd, dpio_id) \ -+ MC_CMD_OP(cmd, 0, 0, 32, int, dpio_id) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_CMD_CREATE(cmd, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 16, 2, enum dpio_channel_mode, \ -+ cfg->channel_mode);\ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, cfg->num_priorities);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_RSP_IS_ENABLED(cmd, en) \ -+ MC_RSP_OP(cmd, 0, 0, 1, int, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_CMD_SET_IRQ(cmd, irq_index, irq_cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, irq_index);\ -+ MC_CMD_OP(cmd, 0, 32, 32, uint32_t, irq_cfg->val);\ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, irq_cfg->addr);\ -+ MC_CMD_OP(cmd, 2, 0, 32, int, irq_cfg->irq_num); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_CMD_GET_IRQ(cmd, irq_index) \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_RSP_GET_IRQ(cmd, type, irq_cfg) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, irq_cfg->val); \ -+ MC_RSP_OP(cmd, 1, 0, 64, uint64_t, irq_cfg->addr); \ -+ MC_RSP_OP(cmd, 2, 0, 32, int, irq_cfg->irq_num); \ -+ MC_RSP_OP(cmd, 2, 32, 32, int, type); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_CMD_SET_IRQ_ENABLE(cmd, irq_index, en) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, en); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_CMD_GET_IRQ_ENABLE(cmd, irq_index) \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_RSP_GET_IRQ_ENABLE(cmd, en) \ -+ MC_RSP_OP(cmd, 0, 0, 8, uint8_t, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_CMD_SET_IRQ_MASK(cmd, irq_index, mask) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, mask); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_CMD_GET_IRQ_MASK(cmd, irq_index) \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_RSP_GET_IRQ_MASK(cmd, mask) \ -+ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, mask) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_CMD_GET_IRQ_STATUS(cmd, irq_index, status) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, status);\ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_RSP_GET_IRQ_STATUS(cmd, status) \ -+ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, status) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_CMD_CLEAR_IRQ_STATUS(cmd, irq_index, status) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, status); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_RSP_GET_ATTR(cmd, attr) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 32, int, attr->id);\ -+ MC_RSP_OP(cmd, 0, 32, 16, uint16_t, attr->qbman_portal_id);\ -+ MC_RSP_OP(cmd, 0, 48, 8, uint8_t, attr->num_priorities);\ -+ MC_RSP_OP(cmd, 0, 56, 4, enum dpio_channel_mode, attr->channel_mode);\ -+ MC_RSP_OP(cmd, 1, 0, 64, uint64_t, attr->qbman_portal_ce_offset);\ -+ MC_RSP_OP(cmd, 2, 0, 64, uint64_t, attr->qbman_portal_ci_offset);\ -+ MC_RSP_OP(cmd, 3, 0, 16, uint16_t, attr->version.major);\ -+ MC_RSP_OP(cmd, 3, 16, 16, uint16_t, attr->version.minor);\ -+ MC_RSP_OP(cmd, 3, 32, 32, uint32_t, attr->qbman_version);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_CMD_SET_STASHING_DEST(cmd, sdest) \ -+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, sdest) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_RSP_GET_STASHING_DEST(cmd, sdest) \ -+ MC_RSP_OP(cmd, 0, 0, 8, uint8_t, sdest) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_CMD_ADD_STATIC_DEQUEUE_CHANNEL(cmd, dpcon_id) \ -+ MC_CMD_OP(cmd, 0, 0, 32, int, dpcon_id) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_RSP_ADD_STATIC_DEQUEUE_CHANNEL(cmd, channel_index) \ -+ MC_RSP_OP(cmd, 0, 0, 8, uint8_t, channel_index) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPIO_CMD_REMOVE_STATIC_DEQUEUE_CHANNEL(cmd, dpcon_id) \ -+ MC_CMD_OP(cmd, 0, 0, 32, int, dpcon_id) -+#endif /* _FSL_DPIO_CMD_H */ -diff --git a/drivers/staging/fsl-mc/bus/dpio/fsl_qbman_base.h b/drivers/staging/fsl-mc/bus/dpio/fsl_qbman_base.h -new file mode 100644 -index 0000000..2874ff8 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpio/fsl_qbman_base.h -@@ -0,0 +1,123 @@ -+/* Copyright (C) 2014 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 _FSL_QBMAN_BASE_H -+#define _FSL_QBMAN_BASE_H -+ -+/** -+ * struct qbman_block_desc - qbman block descriptor structure -+ * -+ * Descriptor for a QBMan instance on the SoC. On partitions/targets that do not -+ * control this QBMan instance, these values may simply be place-holders. The -+ * idea is simply that we be able to distinguish between them, eg. so that SWP -+ * descriptors can identify which QBMan instance they belong to. -+ */ -+struct qbman_block_desc { -+ void *ccsr_reg_bar; /* CCSR register map */ -+ int irq_rerr; /* Recoverable error interrupt line */ -+ int irq_nrerr; /* Non-recoverable error interrupt line */ -+}; -+ -+/** -+ * struct qbman_swp_desc - qbman software portal descriptor structure -+ * -+ * Descriptor for a QBMan software portal, expressed in terms that make sense to -+ * the user context. Ie. on MC, this information is likely to be true-physical, -+ * and instantiated statically at compile-time. On GPP, this information is -+ * likely to be obtained via "discovery" over a partition's "layerscape bus" -+ * (ie. in response to a MC portal command), and would take into account any -+ * virtualisation of the GPP user's address space and/or interrupt numbering. -+ */ -+struct qbman_swp_desc { -+ const struct qbman_block_desc *block; /* The QBMan instance */ -+ void *cena_bar; /* Cache-enabled portal register map */ -+ void *cinh_bar; /* Cache-inhibited portal register map */ -+ uint32_t qman_version; -+}; -+ -+/* Driver object for managing a QBMan portal */ -+struct qbman_swp; -+ -+/** -+ * struct qbman_fd - basci structure for qbman frame descriptor -+ * -+ * Place-holder for FDs, we represent it via the simplest form that we need for -+ * now. Different overlays may be needed to support different options, etc. (It -+ * is impractical to define One True Struct, because the resulting encoding -+ * routines (lots of read-modify-writes) would be worst-case performance whether -+ * or not circumstances required them.) -+ * -+ * Note, as with all data-structures exchanged between software and hardware (be -+ * they located in the portal register map or DMA'd to and from main-memory), -+ * the driver ensures that the caller of the driver API sees the data-structures -+ * in host-endianness. "struct qbman_fd" is no exception. The 32-bit words -+ * contained within this structure are represented in host-endianness, even if -+ * hardware always treats them as little-endian. As such, if any of these fields -+ * are interpreted in a binary (rather than numerical) fashion by hardware -+ * blocks (eg. accelerators), then the user should be careful. We illustrate -+ * with an example; -+ * -+ * Suppose the desired behaviour of an accelerator is controlled by the "frc" -+ * field of the FDs that are sent to it. Suppose also that the behaviour desired -+ * by the user corresponds to an "frc" value which is expressed as the literal -+ * sequence of bytes 0xfe, 0xed, 0xab, and 0xba. So "frc" should be the 32-bit -+ * value in which 0xfe is the first byte and 0xba is the last byte, and as -+ * hardware is little-endian, this amounts to a 32-bit "value" of 0xbaabedfe. If -+ * the software is little-endian also, this can simply be achieved by setting -+ * frc=0xbaabedfe. On the other hand, if software is big-endian, it should set -+ * frc=0xfeedabba! The best away of avoiding trouble with this sort of thing is -+ * to treat the 32-bit words as numerical values, in which the offset of a field -+ * from the beginning of the first byte (as required or generated by hardware) -+ * is numerically encoded by a left-shift (ie. by raising the field to a -+ * corresponding power of 2). Ie. in the current example, software could set -+ * "frc" in the following way, and it would work correctly on both little-endian -+ * and big-endian operation; -+ * fd.frc = (0xfe << 0) | (0xed << 8) | (0xab << 16) | (0xba << 24); -+ */ -+struct qbman_fd { -+ union { -+ uint32_t words[8]; -+ struct qbman_fd_simple { -+ uint32_t addr_lo; -+ uint32_t addr_hi; -+ uint32_t len; -+ /* offset in the MS 16 bits, BPID in the LS 16 bits */ -+ uint32_t bpid_offset; -+ uint32_t frc; /* frame context */ -+ /* "err", "va", "cbmt", "asal", [...] */ -+ uint32_t ctrl; -+ /* flow context */ -+ uint32_t flc_lo; -+ uint32_t flc_hi; -+ } simple; -+ }; -+}; -+ -+#endif /* !_FSL_QBMAN_BASE_H */ -diff --git a/drivers/staging/fsl-mc/bus/dpio/fsl_qbman_portal.h b/drivers/staging/fsl-mc/bus/dpio/fsl_qbman_portal.h -new file mode 100644 -index 0000000..c9e543e ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpio/fsl_qbman_portal.h -@@ -0,0 +1,753 @@ -+/* Copyright (C) 2014 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 _FSL_QBMAN_PORTAL_H -+#define _FSL_QBMAN_PORTAL_H -+ -+#include "fsl_qbman_base.h" -+ -+/** -+ * qbman_swp_init() - Create a functional object representing the given -+ * QBMan portal descriptor. -+ * @d: the given qbman swp descriptor -+ * -+ * Return qbman_swp portal object for success, NULL if the object cannot -+ * be created. -+ */ -+struct qbman_swp *qbman_swp_init(const struct qbman_swp_desc *d); -+/** -+ * qbman_swp_finish() - Create and destroy a functional object representing -+ * the given QBMan portal descriptor. -+ * @p: the qbman_swp object to be destroyed. -+ * -+ */ -+void qbman_swp_finish(struct qbman_swp *p); -+ -+/** -+ * qbman_swp_get_desc() - Get the descriptor of the given portal object. -+ * @p: the given portal object. -+ * -+ * Return the descriptor for this portal. -+ */ -+const struct qbman_swp_desc *qbman_swp_get_desc(struct qbman_swp *p); -+ -+ /**************/ -+ /* Interrupts */ -+ /**************/ -+ -+/* See the QBMan driver API documentation for details on the interrupt -+ * mechanisms. */ -+#define QBMAN_SWP_INTERRUPT_EQRI ((uint32_t)0x00000001) -+#define QBMAN_SWP_INTERRUPT_EQDI ((uint32_t)0x00000002) -+#define QBMAN_SWP_INTERRUPT_DQRI ((uint32_t)0x00000004) -+#define QBMAN_SWP_INTERRUPT_RCRI ((uint32_t)0x00000008) -+#define QBMAN_SWP_INTERRUPT_RCDI ((uint32_t)0x00000010) -+#define QBMAN_SWP_INTERRUPT_VDCI ((uint32_t)0x00000020) -+ -+/** -+ * qbman_swp_interrupt_get_vanish() -+ * qbman_swp_interrupt_set_vanish() - Get/Set the data in software portal -+ * interrupt status disable register. -+ * @p: the given software portal object. -+ * @mask: The mask to set in SWP_IDSR register. -+ * -+ * Return the settings in SWP_ISDR register for Get function. -+ */ -+uint32_t qbman_swp_interrupt_get_vanish(struct qbman_swp *p); -+void qbman_swp_interrupt_set_vanish(struct qbman_swp *p, uint32_t mask); -+ -+/** -+ * qbman_swp_interrupt_read_status() -+ * qbman_swp_interrupt_clear_status() - Get/Set the data in software portal -+ * interrupt status register. -+ * @p: the given software portal object. -+ * @mask: The mask to set in SWP_ISR register. -+ * -+ * Return the settings in SWP_ISR register for Get function. -+ * -+ */ -+uint32_t qbman_swp_interrupt_read_status(struct qbman_swp *p); -+void qbman_swp_interrupt_clear_status(struct qbman_swp *p, uint32_t mask); -+ -+/** -+ * qbman_swp_interrupt_get_trigger() -+ * qbman_swp_interrupt_set_trigger() - Get/Set the data in software portal -+ * interrupt enable register. -+ * @p: the given software portal object. -+ * @mask: The mask to set in SWP_IER register. -+ * -+ * Return the settings in SWP_IER register for Get function. -+ */ -+uint32_t qbman_swp_interrupt_get_trigger(struct qbman_swp *p); -+void qbman_swp_interrupt_set_trigger(struct qbman_swp *p, uint32_t mask); -+ -+/** -+ * qbman_swp_interrupt_get_inhibit() -+ * qbman_swp_interrupt_set_inhibit() - Set/Set the data in software portal -+ * interrupt inhibit register. -+ * @p: the given software portal object. -+ * @mask: The mask to set in SWP_IIR register. -+ * -+ * Return the settings in SWP_IIR register for Get function. -+ */ -+int qbman_swp_interrupt_get_inhibit(struct qbman_swp *p); -+void qbman_swp_interrupt_set_inhibit(struct qbman_swp *p, int inhibit); -+ -+ /************/ -+ /* Dequeues */ -+ /************/ -+ -+/* See the QBMan driver API documentation for details on the enqueue -+ * mechanisms. NB: the use of a 'dpaa2_' prefix for this type is because it is -+ * primarily used by the "DPIO" layer that sits above (and hides) the QBMan -+ * driver. The structure is defined in the DPIO interface, but to avoid circular -+ * dependencies we just pre/re-declare it here opaquely. */ -+struct dpaa2_dq; -+ -+/* ------------------- */ -+/* Push-mode dequeuing */ -+/* ------------------- */ -+ -+/** -+ * qbman_swp_push_get() - Get the push dequeue setup. -+ * @p: the software portal object. -+ * @channel_idx: the channel index to query. -+ * @enabled: returned boolean to show whether the push dequeue is enabled for -+ * the given channel. -+ */ -+void qbman_swp_push_get(struct qbman_swp *, uint8_t channel_idx, int *enabled); -+/** -+ * qbman_swp_push_set() - Enable or disable push dequeue. -+ * @p: the software portal object. -+ * @channel_idx: the channel index.. -+ * @enable: enable or disable push dequeue. -+ * -+ * The user of a portal can enable and disable push-mode dequeuing of up to 16 -+ * channels independently. It does not specify this toggling by channel IDs, but -+ * rather by specifying the index (from 0 to 15) that has been mapped to the -+ * desired channel. -+ */ -+void qbman_swp_push_set(struct qbman_swp *, uint8_t channel_idx, int enable); -+ -+/* ------------------- */ -+/* Pull-mode dequeuing */ -+/* ------------------- */ -+ -+/** -+ * struct qbman_pull_desc - the structure for pull dequeue descriptor -+ */ -+struct qbman_pull_desc { -+ uint32_t dont_manipulate_directly[6]; -+}; -+ -+enum qbman_pull_type_e { -+ /* dequeue with priority precedence, respect intra-class scheduling */ -+ qbman_pull_type_prio = 1, -+ /* dequeue with active FQ precedence, respect ICS */ -+ qbman_pull_type_active, -+ /* dequeue with active FQ precedence, no ICS */ -+ qbman_pull_type_active_noics -+}; -+ -+/** -+ * qbman_pull_desc_clear() - Clear the contents of a descriptor to -+ * default/starting state. -+ * @d: the pull dequeue descriptor to be cleared. -+ */ -+void qbman_pull_desc_clear(struct qbman_pull_desc *d); -+ -+/** -+ * qbman_pull_desc_set_storage()- Set the pull dequeue storage -+ * @d: the pull dequeue descriptor to be set. -+ * @storage: the pointer of the memory to store the dequeue result. -+ * @storage_phys: the physical address of the storage memory. -+ * @stash: to indicate whether write allocate is enabled. -+ * -+ * If not called, or if called with 'storage' as NULL, the result pull dequeues -+ * will produce results to DQRR. If 'storage' is non-NULL, then results are -+ * produced to the given memory location (using the physical/DMA address which -+ * the caller provides in 'storage_phys'), and 'stash' controls whether or not -+ * those writes to main-memory express a cache-warming attribute. -+ */ -+void qbman_pull_desc_set_storage(struct qbman_pull_desc *d, -+ struct dpaa2_dq *storage, -+ dma_addr_t storage_phys, -+ int stash); -+/** -+ * qbman_pull_desc_set_numframes() - Set the number of frames to be dequeued. -+ * @d: the pull dequeue descriptor to be set. -+ * @numframes: number of frames to be set, must be between 1 and 16, inclusive. -+ */ -+void qbman_pull_desc_set_numframes(struct qbman_pull_desc *, uint8_t numframes); -+ -+/** -+ * qbman_pull_desc_set_fq() - Set fqid from which the dequeue command dequeues. -+ * @fqid: the frame queue index of the given FQ. -+ * -+ * qbman_pull_desc_set_wq() - Set wqid from which the dequeue command dequeues. -+ * @wqid: composed of channel id and wqid within the channel. -+ * @dct: the dequeue command type. -+ * -+ * qbman_pull_desc_set_channel() - Set channelid from which the dequeue command -+ * dequeues. -+ * @chid: the channel id to be dequeued. -+ * @dct: the dequeue command type. -+ * -+ * Exactly one of the following descriptor "actions" should be set. (Calling any -+ * one of these will replace the effect of any prior call to one of these.) -+ * - pull dequeue from the given frame queue (FQ) -+ * - pull dequeue from any FQ in the given work queue (WQ) -+ * - pull dequeue from any FQ in any WQ in the given channel -+ */ -+void qbman_pull_desc_set_fq(struct qbman_pull_desc *, uint32_t fqid); -+void qbman_pull_desc_set_wq(struct qbman_pull_desc *, uint32_t wqid, -+ enum qbman_pull_type_e dct); -+void qbman_pull_desc_set_channel(struct qbman_pull_desc *, uint32_t chid, -+ enum qbman_pull_type_e dct); -+ -+/** -+ * qbman_swp_pull() - Issue the pull dequeue command -+ * @s: the software portal object. -+ * @d: the software portal descriptor which has been configured with -+ * the set of qbman_pull_desc_set_*() calls. -+ * -+ * Return 0 for success, and -EBUSY if the software portal is not ready -+ * to do pull dequeue. -+ */ -+int qbman_swp_pull(struct qbman_swp *, struct qbman_pull_desc *d); -+ -+/* -------------------------------- */ -+/* Polling DQRR for dequeue results */ -+/* -------------------------------- */ -+ -+/** -+ * qbman_swp_dqrr_next() - Get an valid DQRR entry. -+ * @s: the software portal object. -+ * -+ * Return NULL if there are no unconsumed DQRR entries. Return a DQRR entry -+ * only once, so repeated calls can return a sequence of DQRR entries, without -+ * requiring they be consumed immediately or in any particular order. -+ */ -+const struct dpaa2_dq *qbman_swp_dqrr_next(struct qbman_swp *s); -+ -+/** -+ * qbman_swp_dqrr_consume() - Consume DQRR entries previously returned from -+ * qbman_swp_dqrr_next(). -+ * @s: the software portal object. -+ * @dq: the DQRR entry to be consumed. -+ */ -+void qbman_swp_dqrr_consume(struct qbman_swp *s, const struct dpaa2_dq *dq); -+ -+/* ------------------------------------------------- */ -+/* Polling user-provided storage for dequeue results */ -+/* ------------------------------------------------- */ -+/** -+ * qbman_result_has_new_result() - Check and get the dequeue response from the -+ * dq storage memory set in pull dequeue command -+ * @s: the software portal object. -+ * @dq: the dequeue result read from the memory. -+ * -+ * Only used for user-provided storage of dequeue results, not DQRR. For -+ * efficiency purposes, the driver will perform any required endianness -+ * conversion to ensure that the user's dequeue result storage is in host-endian -+ * format (whether or not that is the same as the little-endian format that -+ * hardware DMA'd to the user's storage). As such, once the user has called -+ * qbman_result_has_new_result() and been returned a valid dequeue result, -+ * they should not call it again on the same memory location (except of course -+ * if another dequeue command has been executed to produce a new result to that -+ * location). -+ * -+ * Return 1 for getting a valid dequeue result, or 0 for not getting a valid -+ * dequeue result. -+ */ -+int qbman_result_has_new_result(struct qbman_swp *, -+ const struct dpaa2_dq *); -+ -+/* -------------------------------------------------------- */ -+/* Parsing dequeue entries (DQRR and user-provided storage) */ -+/* -------------------------------------------------------- */ -+ -+/** -+ * qbman_result_is_DQ() - check the dequeue result is a dequeue response or not -+ * @dq: the dequeue result to be checked. -+ * -+ * DQRR entries may contain non-dequeue results, ie. notifications -+ */ -+int qbman_result_is_DQ(const struct dpaa2_dq *); -+ -+/** -+ * qbman_result_is_SCN() - Check the dequeue result is notification or not -+ * @dq: the dequeue result to be checked. -+ * -+ * All the non-dequeue results (FQDAN/CDAN/CSCN/...) are "state change -+ * notifications" of one type or another. Some APIs apply to all of them, of the -+ * form qbman_result_SCN_***(). -+ */ -+static inline int qbman_result_is_SCN(const struct dpaa2_dq *dq) -+{ -+ return !qbman_result_is_DQ(dq); -+} -+ -+/** -+ * Recognise different notification types, only required if the user allows for -+ * these to occur, and cares about them when they do. -+ */ -+int qbman_result_is_FQDAN(const struct dpaa2_dq *); -+ /* FQ Data Availability */ -+int qbman_result_is_CDAN(const struct dpaa2_dq *); -+ /* Channel Data Availability */ -+int qbman_result_is_CSCN(const struct dpaa2_dq *); -+ /* Congestion State Change */ -+int qbman_result_is_BPSCN(const struct dpaa2_dq *); -+ /* Buffer Pool State Change */ -+int qbman_result_is_CGCU(const struct dpaa2_dq *); -+ /* Congestion Group Count Update */ -+/* Frame queue state change notifications; (FQDAN in theory counts too as it -+ * leaves a FQ parked, but it is primarily a data availability notification) */ -+int qbman_result_is_FQRN(const struct dpaa2_dq *); /* Retirement */ -+int qbman_result_is_FQRNI(const struct dpaa2_dq *); -+ /* Retirement Immediate */ -+int qbman_result_is_FQPN(const struct dpaa2_dq *); /* Park */ -+ -+/* NB: for parsing dequeue results (when "is_DQ" is TRUE), use the higher-layer -+ * dpaa2_dq_*() functions. */ -+ -+/* State-change notifications (FQDAN/CDAN/CSCN/...). */ -+/** -+ * qbman_result_SCN_state() - Get the state field in State-change notification -+ */ -+uint8_t qbman_result_SCN_state(const struct dpaa2_dq *); -+/** -+ * qbman_result_SCN_rid() - Get the resource id in State-change notification -+ */ -+uint32_t qbman_result_SCN_rid(const struct dpaa2_dq *); -+/** -+ * qbman_result_SCN_ctx() - Get the context data in State-change notification -+ */ -+uint64_t qbman_result_SCN_ctx(const struct dpaa2_dq *); -+/** -+ * qbman_result_SCN_state_in_mem() - Get the state field in State-change -+ * notification which is written to memory instead of DQRR. -+ */ -+uint8_t qbman_result_SCN_state_in_mem(const struct dpaa2_dq *); -+/** -+ * qbman_result_SCN_rid_in_mem() - Get the resource id in State-change -+ * notification which is written to memory instead of DQRR. -+ */ -+uint32_t qbman_result_SCN_rid_in_mem(const struct dpaa2_dq *); -+ -+/* Type-specific "resource IDs". Mainly for illustration purposes, though it -+ * also gives the appropriate type widths. */ -+#define qbman_result_FQDAN_fqid(dq) qbman_result_SCN_rid(dq) -+#define qbman_result_FQRN_fqid(dq) qbman_result_SCN_rid(dq) -+#define qbman_result_FQRNI_fqid(dq) qbman_result_SCN_rid(dq) -+#define qbman_result_FQPN_fqid(dq) qbman_result_SCN_rid(dq) -+#define qbman_result_CDAN_cid(dq) ((uint16_t)qbman_result_SCN_rid(dq)) -+#define qbman_result_CSCN_cgid(dq) ((uint16_t)qbman_result_SCN_rid(dq)) -+ -+/** -+ * qbman_result_bpscn_bpid() - Get the bpid from BPSCN -+ * -+ * Return the buffer pool id. -+ */ -+uint16_t qbman_result_bpscn_bpid(const struct dpaa2_dq *); -+/** -+ * qbman_result_bpscn_has_free_bufs() - Check whether there are free -+ * buffers in the pool from BPSCN. -+ * -+ * Return the number of free buffers. -+ */ -+int qbman_result_bpscn_has_free_bufs(const struct dpaa2_dq *); -+/** -+ * qbman_result_bpscn_is_depleted() - Check BPSCN to see whether the -+ * buffer pool is depleted. -+ * -+ * Return the status of buffer pool depletion. -+ */ -+int qbman_result_bpscn_is_depleted(const struct dpaa2_dq *); -+/** -+ * qbman_result_bpscn_is_surplus() - Check BPSCN to see whether the buffer -+ * pool is surplus or not. -+ * -+ * Return the status of buffer pool surplus. -+ */ -+int qbman_result_bpscn_is_surplus(const struct dpaa2_dq *); -+/** -+ * qbman_result_bpscn_ctx() - Get the BPSCN CTX from BPSCN message -+ * -+ * Return the BPSCN context. -+ */ -+uint64_t qbman_result_bpscn_ctx(const struct dpaa2_dq *); -+ -+/* Parsing CGCU */ -+/** -+ * qbman_result_cgcu_cgid() - Check CGCU resouce id, i.e. cgid -+ * -+ * Return the CGCU resource id. -+ */ -+uint16_t qbman_result_cgcu_cgid(const struct dpaa2_dq *); -+/** -+ * qbman_result_cgcu_icnt() - Get the I_CNT from CGCU -+ * -+ * Return instantaneous count in the CGCU notification. -+ */ -+uint64_t qbman_result_cgcu_icnt(const struct dpaa2_dq *); -+ -+ /************/ -+ /* Enqueues */ -+ /************/ -+/** -+ * struct qbman_eq_desc - structure of enqueue descriptor -+ */ -+struct qbman_eq_desc { -+ uint32_t dont_manipulate_directly[8]; -+}; -+ -+/** -+ * struct qbman_eq_response - structure of enqueue response -+ */ -+struct qbman_eq_response { -+ uint32_t dont_manipulate_directly[16]; -+}; -+ -+/** -+ * qbman_eq_desc_clear() - Clear the contents of a descriptor to -+ * default/starting state. -+ */ -+void qbman_eq_desc_clear(struct qbman_eq_desc *); -+ -+/* Exactly one of the following descriptor "actions" should be set. (Calling -+ * any one of these will replace the effect of any prior call to one of these.) -+ * - enqueue without order-restoration -+ * - enqueue with order-restoration -+ * - fill a hole in the order-restoration sequence, without any enqueue -+ * - advance NESN (Next Expected Sequence Number), without any enqueue -+ * 'respond_success' indicates whether an enqueue response should be DMA'd -+ * after success (otherwise a response is DMA'd only after failure). -+ * 'incomplete' indicates that other fragments of the same 'seqnum' are yet to -+ * be enqueued. -+ */ -+/** -+ * qbman_eq_desc_set_no_orp() - Set enqueue descriptor without orp -+ * @d: the enqueue descriptor. -+ * @response_success: 1 = enqueue with response always; 0 = enqueue with -+ * rejections returned on a FQ. -+ */ -+void qbman_eq_desc_set_no_orp(struct qbman_eq_desc *d, int respond_success); -+ -+/** -+ * qbman_eq_desc_set_orp() - Set order-resotration in the enqueue descriptor -+ * @d: the enqueue descriptor. -+ * @response_success: 1 = enqueue with response always; 0 = enqueue with -+ * rejections returned on a FQ. -+ * @opr_id: the order point record id. -+ * @seqnum: the order restoration sequence number. -+ * @incomplete: indiates whether this is the last fragments using the same -+ * sequeue number. -+ */ -+void qbman_eq_desc_set_orp(struct qbman_eq_desc *d, int respond_success, -+ uint32_t opr_id, uint32_t seqnum, int incomplete); -+ -+/** -+ * qbman_eq_desc_set_orp_hole() - fill a hole in the order-restoration sequence -+ * without any enqueue -+ * @d: the enqueue descriptor. -+ * @opr_id: the order point record id. -+ * @seqnum: the order restoration sequence number. -+ */ -+void qbman_eq_desc_set_orp_hole(struct qbman_eq_desc *d, uint32_t opr_id, -+ uint32_t seqnum); -+ -+/** -+ * qbman_eq_desc_set_orp_nesn() - advance NESN (Next Expected Sequence Number) -+ * without any enqueue -+ * @d: the enqueue descriptor. -+ * @opr_id: the order point record id. -+ * @seqnum: the order restoration sequence number. -+ */ -+void qbman_eq_desc_set_orp_nesn(struct qbman_eq_desc *d, uint32_t opr_id, -+ uint32_t seqnum); -+ -+/** -+ * qbman_eq_desc_set_response() - Set the enqueue response info. -+ * @d: the enqueue descriptor -+ * @storage_phys: the physical address of the enqueue response in memory. -+ * @stash: indicate that the write allocation enabled or not. -+ * -+ * In the case where an enqueue response is DMA'd, this determines where that -+ * response should go. (The physical/DMA address is given for hardware's -+ * benefit, but software should interpret it as a "struct qbman_eq_response" -+ * data structure.) 'stash' controls whether or not the write to main-memory -+ * expresses a cache-warming attribute. -+ */ -+void qbman_eq_desc_set_response(struct qbman_eq_desc *d, -+ dma_addr_t storage_phys, -+ int stash); -+/** -+ * qbman_eq_desc_set_token() - Set token for the enqueue command -+ * @d: the enqueue descriptor -+ * @token: the token to be set. -+ * -+ * token is the value that shows up in an enqueue response that can be used to -+ * detect when the results have been published. The easiest technique is to zero -+ * result "storage" before issuing an enqueue, and use any non-zero 'token' -+ * value. -+ */ -+void qbman_eq_desc_set_token(struct qbman_eq_desc *d, uint8_t token); -+ -+/** -+ * qbman_eq_desc_set_fq() -+ * qbman_eq_desc_set_qd() - Set eithe FQ or Queuing Destination for the enqueue -+ * command. -+ * @d: the enqueue descriptor -+ * @fqid: the id of the frame queue to be enqueued. -+ * @qdid: the id of the queuing destination to be enqueued. -+ * @qd_bin: the queuing destination bin -+ * @qd_prio: the queuing destination priority. -+ * -+ * Exactly one of the following descriptor "targets" should be set. (Calling any -+ * one of these will replace the effect of any prior call to one of these.) -+ * - enqueue to a frame queue -+ * - enqueue to a queuing destination -+ * Note, that none of these will have any affect if the "action" type has been -+ * set to "orp_hole" or "orp_nesn". -+ */ -+void qbman_eq_desc_set_fq(struct qbman_eq_desc *, uint32_t fqid); -+void qbman_eq_desc_set_qd(struct qbman_eq_desc *, uint32_t qdid, -+ uint32_t qd_bin, uint32_t qd_prio); -+ -+/** -+ * qbman_eq_desc_set_eqdi() - enable/disable EQDI interrupt -+ * @d: the enqueue descriptor -+ * @enable: boolean to enable/disable EQDI -+ * -+ * Determines whether or not the portal's EQDI interrupt source should be -+ * asserted after the enqueue command is completed. -+ */ -+void qbman_eq_desc_set_eqdi(struct qbman_eq_desc *, int enable); -+ -+/** -+ * qbman_eq_desc_set_dca() - Set DCA mode in the enqueue command. -+ * @d: the enqueue descriptor. -+ * @enable: enabled/disable DCA mode. -+ * @dqrr_idx: DCAP_CI, the DCAP consumer index. -+ * @park: determine the whether park the FQ or not -+ * -+ * Determines whether or not a portal DQRR entry should be consumed once the -+ * enqueue command is completed. (And if so, and the DQRR entry corresponds -+ * to a held-active (order-preserving) FQ, whether the FQ should be parked -+ * instead of being rescheduled.) -+ */ -+void qbman_eq_desc_set_dca(struct qbman_eq_desc *, int enable, -+ uint32_t dqrr_idx, int park); -+ -+/** -+ * qbman_swp_enqueue() - Issue an enqueue command. -+ * @s: the software portal used for enqueue. -+ * @d: the enqueue descriptor. -+ * @fd: the frame descriptor to be enqueued. -+ * -+ * Please note that 'fd' should only be NULL if the "action" of the -+ * descriptor is "orp_hole" or "orp_nesn". -+ * -+ * Return 0 for successful enqueue, -EBUSY if the EQCR is not ready. -+ */ -+int qbman_swp_enqueue(struct qbman_swp *, const struct qbman_eq_desc *, -+ const struct qbman_fd *fd); -+ -+/** -+ * qbman_swp_enqueue_thresh() - Set the threshold for EQRI interrupt. -+ * -+ * An EQRI interrupt can be generated when the fill-level of EQCR falls below -+ * the 'thresh' value set here. Setting thresh==0 (the default) disables. -+ */ -+int qbman_swp_enqueue_thresh(struct qbman_swp *, unsigned int thresh); -+ -+ /*******************/ -+ /* Buffer releases */ -+ /*******************/ -+/** -+ * struct qbman_release_desc - The structure for buffer release descriptor -+ */ -+struct qbman_release_desc { -+ uint32_t dont_manipulate_directly[1]; -+}; -+ -+/** -+ * qbman_release_desc_clear() - Clear the contents of a descriptor to -+ * default/starting state. -+ */ -+void qbman_release_desc_clear(struct qbman_release_desc *); -+ -+/** -+ * qbman_release_desc_set_bpid() - Set the ID of the buffer pool to release to -+ */ -+void qbman_release_desc_set_bpid(struct qbman_release_desc *, uint32_t bpid); -+ -+/** -+ * qbman_release_desc_set_rcdi() - Determines whether or not the portal's RCDI -+ * interrupt source should be asserted after the release command is completed. -+ */ -+void qbman_release_desc_set_rcdi(struct qbman_release_desc *, int enable); -+ -+/** -+ * qbman_swp_release() - Issue a buffer release command. -+ * @s: the software portal object. -+ * @d: the release descriptor. -+ * @buffers: a pointer pointing to the buffer address to be released. -+ * @num_buffers: number of buffers to be released, must be less than 8. -+ * -+ * Return 0 for success, -EBUSY if the release command ring is not ready. -+ */ -+int qbman_swp_release(struct qbman_swp *s, const struct qbman_release_desc *d, -+ const uint64_t *buffers, unsigned int num_buffers); -+ -+ /*******************/ -+ /* Buffer acquires */ -+ /*******************/ -+ -+/** -+ * qbman_swp_acquire() - Issue a buffer acquire command. -+ * @s: the software portal object. -+ * @bpid: the buffer pool index. -+ * @buffers: a pointer pointing to the acquired buffer address|es. -+ * @num_buffers: number of buffers to be acquired, must be less than 8. -+ * -+ * Return 0 for success, or negative error code if the acquire command -+ * fails. -+ */ -+int qbman_swp_acquire(struct qbman_swp *, uint32_t bpid, uint64_t *buffers, -+ unsigned int num_buffers); -+ -+ /*****************/ -+ /* FQ management */ -+ /*****************/ -+ -+/** -+ * qbman_swp_fq_schedule() - Move the fq to the scheduled state. -+ * @s: the software portal object. -+ * @fqid: the index of frame queue to be scheduled. -+ * -+ * There are a couple of different ways that a FQ can end up parked state, -+ * This schedules it. -+ * -+ * Return 0 for success, or negative error code for failure. -+ */ -+int qbman_swp_fq_schedule(struct qbman_swp *s, uint32_t fqid); -+ -+/** -+ * qbman_swp_fq_force() - Force the FQ to fully scheduled state. -+ * @s: the software portal object. -+ * @fqid: the index of frame queue to be forced. -+ * -+ * Force eligible will force a tentatively-scheduled FQ to be fully-scheduled -+ * and thus be available for selection by any channel-dequeuing behaviour (push -+ * or pull). If the FQ is subsequently "dequeued" from the channel and is still -+ * empty at the time this happens, the resulting dq_entry will have no FD. -+ * (qbman_result_DQ_fd() will return NULL.) -+ * -+ * Return 0 for success, or negative error code for failure. -+ */ -+int qbman_swp_fq_force(struct qbman_swp *s, uint32_t fqid); -+ -+/** -+ * qbman_swp_fq_xon() -+ * qbman_swp_fq_xoff() - XON/XOFF the frame queue. -+ * @s: the software portal object. -+ * @fqid: the index of frame queue. -+ * -+ * These functions change the FQ flow-control stuff between XON/XOFF. (The -+ * default is XON.) This setting doesn't affect enqueues to the FQ, just -+ * dequeues. XOFF FQs will remain in the tenatively-scheduled state, even when -+ * non-empty, meaning they won't be selected for scheduled dequeuing. If a FQ is -+ * changed to XOFF after it had already become truly-scheduled to a channel, and -+ * a pull dequeue of that channel occurs that selects that FQ for dequeuing, -+ * then the resulting dq_entry will have no FD. (qbman_result_DQ_fd() will -+ * return NULL.) -+ * -+ * Return 0 for success, or negative error code for failure. -+ */ -+int qbman_swp_fq_xon(struct qbman_swp *s, uint32_t fqid); -+int qbman_swp_fq_xoff(struct qbman_swp *s, uint32_t fqid); -+ -+ /**********************/ -+ /* Channel management */ -+ /**********************/ -+ -+/* If the user has been allocated a channel object that is going to generate -+ * CDANs to another channel, then these functions will be necessary. -+ * CDAN-enabled channels only generate a single CDAN notification, after which -+ * it they need to be reenabled before they'll generate another. (The idea is -+ * that pull dequeuing will occur in reaction to the CDAN, followed by a -+ * reenable step.) Each function generates a distinct command to hardware, so a -+ * combination function is provided if the user wishes to modify the "context" -+ * (which shows up in each CDAN message) each time they reenable, as a single -+ * command to hardware. */ -+/** -+ * qbman_swp_CDAN_set_context() - Set CDAN context -+ * @s: the software portal object. -+ * @channelid: the channel index. -+ * @ctx: the context to be set in CDAN. -+ * -+ * Return 0 for success, or negative error code for failure. -+ */ -+int qbman_swp_CDAN_set_context(struct qbman_swp *, uint16_t channelid, -+ uint64_t ctx); -+ -+/** -+ * qbman_swp_CDAN_enable() - Enable CDAN for the channel. -+ * @s: the software portal object. -+ * @channelid: the index of the channel to generate CDAN. -+ * -+ * Return 0 for success, or negative error code for failure. -+ */ -+int qbman_swp_CDAN_enable(struct qbman_swp *, uint16_t channelid); -+ -+/** -+ * qbman_swp_CDAN_disable() - disable CDAN for the channel. -+ * @s: the software portal object. -+ * @channelid: the index of the channel to generate CDAN. -+ * -+ * Return 0 for success, or negative error code for failure. -+ */ -+int qbman_swp_CDAN_disable(struct qbman_swp *, uint16_t channelid); -+ -+/** -+ * qbman_swp_CDAN_set_context_enable() - Set CDAN contest and enable CDAN -+ * @s: the software portal object. -+ * @channelid: the index of the channel to generate CDAN. -+ * @ctx: the context set in CDAN. -+ * -+ * Return 0 for success, or negative error code for failure. -+ */ -+int qbman_swp_CDAN_set_context_enable(struct qbman_swp *, uint16_t channelid, -+ uint64_t ctx); -+ -+#endif /* !_FSL_QBMAN_PORTAL_H */ -diff --git a/drivers/staging/fsl-mc/bus/dpio/qbman_debug.c b/drivers/staging/fsl-mc/bus/dpio/qbman_debug.c -new file mode 100644 -index 0000000..12e33d3 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpio/qbman_debug.c -@@ -0,0 +1,846 @@ -+/* Copyright (C) 2015 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 "qbman_portal.h" -+#include "qbman_debug.h" -+#include "fsl_qbman_portal.h" -+ -+/* QBMan portal management command code */ -+#define QBMAN_BP_QUERY 0x32 -+#define QBMAN_FQ_QUERY 0x44 -+#define QBMAN_FQ_QUERY_NP 0x45 -+#define QBMAN_CGR_QUERY 0x51 -+#define QBMAN_WRED_QUERY 0x54 -+#define QBMAN_CGR_STAT_QUERY 0x55 -+#define QBMAN_CGR_STAT_QUERY_CLR 0x56 -+ -+enum qbman_attr_usage_e { -+ qbman_attr_usage_fq, -+ qbman_attr_usage_bpool, -+ qbman_attr_usage_cgr, -+}; -+ -+struct int_qbman_attr { -+ uint32_t words[32]; -+ enum qbman_attr_usage_e usage; -+}; -+ -+#define attr_type_set(a, e) \ -+{ \ -+ struct qbman_attr *__attr = a; \ -+ enum qbman_attr_usage_e __usage = e; \ -+ ((struct int_qbman_attr *)__attr)->usage = __usage; \ -+} -+ -+#define ATTR32(d) (&(d)->dont_manipulate_directly[0]) -+#define ATTR32_1(d) (&(d)->dont_manipulate_directly[16]) -+ -+static struct qb_attr_code code_bp_bpid = QB_CODE(0, 16, 16); -+static struct qb_attr_code code_bp_bdi = QB_CODE(1, 16, 1); -+static struct qb_attr_code code_bp_va = QB_CODE(1, 17, 1); -+static struct qb_attr_code code_bp_wae = QB_CODE(1, 18, 1); -+static struct qb_attr_code code_bp_swdet = QB_CODE(4, 0, 16); -+static struct qb_attr_code code_bp_swdxt = QB_CODE(4, 16, 16); -+static struct qb_attr_code code_bp_hwdet = QB_CODE(5, 0, 16); -+static struct qb_attr_code code_bp_hwdxt = QB_CODE(5, 16, 16); -+static struct qb_attr_code code_bp_swset = QB_CODE(6, 0, 16); -+static struct qb_attr_code code_bp_swsxt = QB_CODE(6, 16, 16); -+static struct qb_attr_code code_bp_vbpid = QB_CODE(7, 0, 14); -+static struct qb_attr_code code_bp_icid = QB_CODE(7, 16, 15); -+static struct qb_attr_code code_bp_pl = QB_CODE(7, 31, 1); -+static struct qb_attr_code code_bp_bpscn_addr_lo = QB_CODE(8, 0, 32); -+static struct qb_attr_code code_bp_bpscn_addr_hi = QB_CODE(9, 0, 32); -+static struct qb_attr_code code_bp_bpscn_ctx_lo = QB_CODE(10, 0, 32); -+static struct qb_attr_code code_bp_bpscn_ctx_hi = QB_CODE(11, 0, 32); -+static struct qb_attr_code code_bp_hw_targ = QB_CODE(12, 0, 16); -+static struct qb_attr_code code_bp_state = QB_CODE(1, 24, 3); -+static struct qb_attr_code code_bp_fill = QB_CODE(2, 0, 32); -+static struct qb_attr_code code_bp_hdptr = QB_CODE(3, 0, 32); -+static struct qb_attr_code code_bp_sdcnt = QB_CODE(13, 0, 8); -+static struct qb_attr_code code_bp_hdcnt = QB_CODE(13, 1, 8); -+static struct qb_attr_code code_bp_sscnt = QB_CODE(13, 2, 8); -+ -+void qbman_bp_attr_clear(struct qbman_attr *a) -+{ -+ memset(a, 0, sizeof(*a)); -+ attr_type_set(a, qbman_attr_usage_bpool); -+} -+ -+int qbman_bp_query(struct qbman_swp *s, uint32_t bpid, -+ struct qbman_attr *a) -+{ -+ uint32_t *p; -+ uint32_t verb, rslt; -+ uint32_t *attr = ATTR32(a); -+ -+ qbman_bp_attr_clear(a); -+ -+ /* Start the management command */ -+ p = qbman_swp_mc_start(s); -+ if (!p) -+ return -EBUSY; -+ -+ /* Encode the caller-provided attributes */ -+ qb_attr_code_encode(&code_bp_bpid, p, bpid); -+ -+ /* Complete the management command */ -+ p = qbman_swp_mc_complete(s, p, p[0] | QBMAN_BP_QUERY); -+ -+ /* Decode the outcome */ -+ verb = qb_attr_code_decode(&code_generic_verb, p); -+ rslt = qb_attr_code_decode(&code_generic_rslt, p); -+ BUG_ON(verb != QBMAN_BP_QUERY); -+ -+ /* Determine success or failure */ -+ if (unlikely(rslt != QBMAN_MC_RSLT_OK)) { -+ pr_err("Query of BPID 0x%x failed, code=0x%02x\n", bpid, rslt); -+ return -EIO; -+ } -+ -+ /* For the query, word[0] of the result contains only the -+ * verb/rslt fields, so skip word[0]. -+ */ -+ word_copy(&attr[1], &p[1], 15); -+ return 0; -+} -+ -+void qbman_bp_attr_get_bdi(struct qbman_attr *a, int *bdi, int *va, int *wae) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ *bdi = !!qb_attr_code_decode(&code_bp_bdi, p); -+ *va = !!qb_attr_code_decode(&code_bp_va, p); -+ *wae = !!qb_attr_code_decode(&code_bp_wae, p); -+} -+ -+static uint32_t qbman_bp_thresh_to_value(uint32_t val) -+{ -+ return (val & 0xff) << ((val & 0xf00) >> 8); -+} -+ -+void qbman_bp_attr_get_swdet(struct qbman_attr *a, uint32_t *swdet) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ *swdet = qbman_bp_thresh_to_value(qb_attr_code_decode(&code_bp_swdet, -+ p)); -+} -+void qbman_bp_attr_get_swdxt(struct qbman_attr *a, uint32_t *swdxt) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ *swdxt = qbman_bp_thresh_to_value(qb_attr_code_decode(&code_bp_swdxt, -+ p)); -+} -+void qbman_bp_attr_get_hwdet(struct qbman_attr *a, uint32_t *hwdet) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ *hwdet = qbman_bp_thresh_to_value(qb_attr_code_decode(&code_bp_hwdet, -+ p)); -+} -+void qbman_bp_attr_get_hwdxt(struct qbman_attr *a, uint32_t *hwdxt) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ *hwdxt = qbman_bp_thresh_to_value(qb_attr_code_decode(&code_bp_hwdxt, -+ p)); -+} -+ -+void qbman_bp_attr_get_swset(struct qbman_attr *a, uint32_t *swset) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ *swset = qbman_bp_thresh_to_value(qb_attr_code_decode(&code_bp_swset, -+ p)); -+} -+ -+void qbman_bp_attr_get_swsxt(struct qbman_attr *a, uint32_t *swsxt) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ *swsxt = qbman_bp_thresh_to_value(qb_attr_code_decode(&code_bp_swsxt, -+ p)); -+} -+ -+void qbman_bp_attr_get_vbpid(struct qbman_attr *a, uint32_t *vbpid) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ *vbpid = qb_attr_code_decode(&code_bp_vbpid, p); -+} -+ -+void qbman_bp_attr_get_icid(struct qbman_attr *a, uint32_t *icid, int *pl) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ *icid = qb_attr_code_decode(&code_bp_icid, p); -+ *pl = !!qb_attr_code_decode(&code_bp_pl, p); -+} -+ -+void qbman_bp_attr_get_bpscn_addr(struct qbman_attr *a, uint64_t *bpscn_addr) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ *bpscn_addr = ((uint64_t)qb_attr_code_decode(&code_bp_bpscn_addr_hi, -+ p) << 32) | -+ (uint64_t)qb_attr_code_decode(&code_bp_bpscn_addr_lo, -+ p); -+} -+ -+void qbman_bp_attr_get_bpscn_ctx(struct qbman_attr *a, uint64_t *bpscn_ctx) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ *bpscn_ctx = ((uint64_t)qb_attr_code_decode(&code_bp_bpscn_ctx_hi, p) -+ << 32) | -+ (uint64_t)qb_attr_code_decode(&code_bp_bpscn_ctx_lo, -+ p); -+} -+ -+void qbman_bp_attr_get_hw_targ(struct qbman_attr *a, uint32_t *hw_targ) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ *hw_targ = qb_attr_code_decode(&code_bp_hw_targ, p); -+} -+ -+int qbman_bp_info_has_free_bufs(struct qbman_attr *a) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ return !(int)(qb_attr_code_decode(&code_bp_state, p) & 0x1); -+} -+ -+int qbman_bp_info_is_depleted(struct qbman_attr *a) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ return (int)(qb_attr_code_decode(&code_bp_state, p) & 0x2); -+} -+ -+int qbman_bp_info_is_surplus(struct qbman_attr *a) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ return (int)(qb_attr_code_decode(&code_bp_state, p) & 0x4); -+} -+ -+uint32_t qbman_bp_info_num_free_bufs(struct qbman_attr *a) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ return qb_attr_code_decode(&code_bp_fill, p); -+} -+ -+uint32_t qbman_bp_info_hdptr(struct qbman_attr *a) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ return qb_attr_code_decode(&code_bp_hdptr, p); -+} -+ -+uint32_t qbman_bp_info_sdcnt(struct qbman_attr *a) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ return qb_attr_code_decode(&code_bp_sdcnt, p); -+} -+ -+uint32_t qbman_bp_info_hdcnt(struct qbman_attr *a) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ return qb_attr_code_decode(&code_bp_hdcnt, p); -+} -+ -+uint32_t qbman_bp_info_sscnt(struct qbman_attr *a) -+{ -+ uint32_t *p = ATTR32(a); -+ -+ return qb_attr_code_decode(&code_bp_sscnt, p); -+} -+ -+static struct qb_attr_code code_fq_fqid = QB_CODE(1, 0, 24); -+static struct qb_attr_code code_fq_cgrid = QB_CODE(2, 16, 16); -+static struct qb_attr_code code_fq_destwq = QB_CODE(3, 0, 15); -+static struct qb_attr_code code_fq_fqctrl = QB_CODE(3, 24, 8); -+static struct qb_attr_code code_fq_icscred = QB_CODE(4, 0, 15); -+static struct qb_attr_code code_fq_tdthresh = QB_CODE(4, 16, 13); -+static struct qb_attr_code code_fq_oa_len = QB_CODE(5, 0, 12); -+static struct qb_attr_code code_fq_oa_ics = QB_CODE(5, 14, 1); -+static struct qb_attr_code code_fq_oa_cgr = QB_CODE(5, 15, 1); -+static struct qb_attr_code code_fq_mctl_bdi = QB_CODE(5, 24, 1); -+static struct qb_attr_code code_fq_mctl_ff = QB_CODE(5, 25, 1); -+static struct qb_attr_code code_fq_mctl_va = QB_CODE(5, 26, 1); -+static struct qb_attr_code code_fq_mctl_ps = QB_CODE(5, 27, 1); -+static struct qb_attr_code code_fq_ctx_lower32 = QB_CODE(6, 0, 32); -+static struct qb_attr_code code_fq_ctx_upper32 = QB_CODE(7, 0, 32); -+static struct qb_attr_code code_fq_icid = QB_CODE(8, 0, 15); -+static struct qb_attr_code code_fq_pl = QB_CODE(8, 15, 1); -+static struct qb_attr_code code_fq_vfqid = QB_CODE(9, 0, 24); -+static struct qb_attr_code code_fq_erfqid = QB_CODE(10, 0, 24); -+ -+void qbman_fq_attr_clear(struct qbman_attr *a) -+{ -+ memset(a, 0, sizeof(*a)); -+ attr_type_set(a, qbman_attr_usage_fq); -+} -+ -+/* FQ query function for programmable fields */ -+int qbman_fq_query(struct qbman_swp *s, uint32_t fqid, struct qbman_attr *desc) -+{ -+ uint32_t *p; -+ uint32_t verb, rslt; -+ uint32_t *d = ATTR32(desc); -+ -+ qbman_fq_attr_clear(desc); -+ -+ p = qbman_swp_mc_start(s); -+ if (!p) -+ return -EBUSY; -+ qb_attr_code_encode(&code_fq_fqid, p, fqid); -+ p = qbman_swp_mc_complete(s, p, QBMAN_FQ_QUERY); -+ -+ /* Decode the outcome */ -+ verb = qb_attr_code_decode(&code_generic_verb, p); -+ rslt = qb_attr_code_decode(&code_generic_rslt, p); -+ BUG_ON(verb != QBMAN_FQ_QUERY); -+ -+ /* Determine success or failure */ -+ if (unlikely(rslt != QBMAN_MC_RSLT_OK)) { -+ pr_err("Query of FQID 0x%x failed, code=0x%02x\n", -+ fqid, rslt); -+ return -EIO; -+ } -+ /* For the configure, word[0] of the command contains only the WE-mask. -+ * For the query, word[0] of the result contains only the verb/rslt -+ * fields. Skip word[0] in the latter case. */ -+ word_copy(&d[1], &p[1], 15); -+ return 0; -+} -+ -+void qbman_fq_attr_get_fqctrl(struct qbman_attr *d, uint32_t *fqctrl) -+{ -+ uint32_t *p = ATTR32(d); -+ -+ *fqctrl = qb_attr_code_decode(&code_fq_fqctrl, p); -+} -+ -+void qbman_fq_attr_get_cgrid(struct qbman_attr *d, uint32_t *cgrid) -+{ -+ uint32_t *p = ATTR32(d); -+ -+ *cgrid = qb_attr_code_decode(&code_fq_cgrid, p); -+} -+ -+void qbman_fq_attr_get_destwq(struct qbman_attr *d, uint32_t *destwq) -+{ -+ uint32_t *p = ATTR32(d); -+ -+ *destwq = qb_attr_code_decode(&code_fq_destwq, p); -+} -+ -+void qbman_fq_attr_get_icscred(struct qbman_attr *d, uint32_t *icscred) -+{ -+ uint32_t *p = ATTR32(d); -+ -+ *icscred = qb_attr_code_decode(&code_fq_icscred, p); -+} -+ -+static struct qb_attr_code code_tdthresh_exp = QB_CODE(0, 0, 5); -+static struct qb_attr_code code_tdthresh_mant = QB_CODE(0, 5, 8); -+static uint32_t qbman_thresh_to_value(uint32_t val) -+{ -+ uint32_t m, e; -+ -+ m = qb_attr_code_decode(&code_tdthresh_mant, &val); -+ e = qb_attr_code_decode(&code_tdthresh_exp, &val); -+ return m << e; -+} -+ -+void qbman_fq_attr_get_tdthresh(struct qbman_attr *d, uint32_t *tdthresh) -+{ -+ uint32_t *p = ATTR32(d); -+ -+ *tdthresh = qbman_thresh_to_value(qb_attr_code_decode(&code_fq_tdthresh, -+ p)); -+} -+ -+void qbman_fq_attr_get_oa(struct qbman_attr *d, -+ int *oa_ics, int *oa_cgr, int32_t *oa_len) -+{ -+ uint32_t *p = ATTR32(d); -+ -+ *oa_ics = !!qb_attr_code_decode(&code_fq_oa_ics, p); -+ *oa_cgr = !!qb_attr_code_decode(&code_fq_oa_cgr, p); -+ *oa_len = qb_attr_code_makesigned(&code_fq_oa_len, -+ qb_attr_code_decode(&code_fq_oa_len, p)); -+} -+ -+void qbman_fq_attr_get_mctl(struct qbman_attr *d, -+ int *bdi, int *ff, int *va, int *ps) -+{ -+ uint32_t *p = ATTR32(d); -+ -+ *bdi = !!qb_attr_code_decode(&code_fq_mctl_bdi, p); -+ *ff = !!qb_attr_code_decode(&code_fq_mctl_ff, p); -+ *va = !!qb_attr_code_decode(&code_fq_mctl_va, p); -+ *ps = !!qb_attr_code_decode(&code_fq_mctl_ps, p); -+} -+ -+void qbman_fq_attr_get_ctx(struct qbman_attr *d, uint32_t *hi, uint32_t *lo) -+{ -+ uint32_t *p = ATTR32(d); -+ -+ *hi = qb_attr_code_decode(&code_fq_ctx_upper32, p); -+ *lo = qb_attr_code_decode(&code_fq_ctx_lower32, p); -+} -+ -+void qbman_fq_attr_get_icid(struct qbman_attr *d, uint32_t *icid, int *pl) -+{ -+ uint32_t *p = ATTR32(d); -+ -+ *icid = qb_attr_code_decode(&code_fq_icid, p); -+ *pl = !!qb_attr_code_decode(&code_fq_pl, p); -+} -+ -+void qbman_fq_attr_get_vfqid(struct qbman_attr *d, uint32_t *vfqid) -+{ -+ uint32_t *p = ATTR32(d); -+ -+ *vfqid = qb_attr_code_decode(&code_fq_vfqid, p); -+} -+ -+void qbman_fq_attr_get_erfqid(struct qbman_attr *d, uint32_t *erfqid) -+{ -+ uint32_t *p = ATTR32(d); -+ -+ *erfqid = qb_attr_code_decode(&code_fq_erfqid, p); -+} -+ -+/* Query FQ Non-Programmalbe Fields */ -+static struct qb_attr_code code_fq_np_state = QB_CODE(0, 16, 3); -+static struct qb_attr_code code_fq_np_fe = QB_CODE(0, 19, 1); -+static struct qb_attr_code code_fq_np_x = QB_CODE(0, 20, 1); -+static struct qb_attr_code code_fq_np_r = QB_CODE(0, 21, 1); -+static struct qb_attr_code code_fq_np_oe = QB_CODE(0, 22, 1); -+static struct qb_attr_code code_fq_np_frm_cnt = QB_CODE(6, 0, 24); -+static struct qb_attr_code code_fq_np_byte_cnt = QB_CODE(7, 0, 32); -+ -+int qbman_fq_query_state(struct qbman_swp *s, uint32_t fqid, -+ struct qbman_attr *state) -+{ -+ uint32_t *p; -+ uint32_t verb, rslt; -+ uint32_t *d = ATTR32(state); -+ -+ qbman_fq_attr_clear(state); -+ -+ p = qbman_swp_mc_start(s); -+ if (!p) -+ return -EBUSY; -+ qb_attr_code_encode(&code_fq_fqid, p, fqid); -+ p = qbman_swp_mc_complete(s, p, QBMAN_FQ_QUERY_NP); -+ -+ /* Decode the outcome */ -+ verb = qb_attr_code_decode(&code_generic_verb, p); -+ rslt = qb_attr_code_decode(&code_generic_rslt, p); -+ BUG_ON(verb != QBMAN_FQ_QUERY_NP); -+ -+ /* Determine success or failure */ -+ if (unlikely(rslt != QBMAN_MC_RSLT_OK)) { -+ pr_err("Query NP fields of FQID 0x%x failed, code=0x%02x\n", -+ fqid, rslt); -+ return -EIO; -+ } -+ word_copy(&d[0], &p[0], 16); -+ return 0; -+} -+ -+uint32_t qbman_fq_state_schedstate(const struct qbman_attr *state) -+{ -+ const uint32_t *p = ATTR32(state); -+ -+ return qb_attr_code_decode(&code_fq_np_state, p); -+} -+ -+int qbman_fq_state_force_eligible(const struct qbman_attr *state) -+{ -+ const uint32_t *p = ATTR32(state); -+ -+ return !!qb_attr_code_decode(&code_fq_np_fe, p); -+} -+ -+int qbman_fq_state_xoff(const struct qbman_attr *state) -+{ -+ const uint32_t *p = ATTR32(state); -+ -+ return !!qb_attr_code_decode(&code_fq_np_x, p); -+} -+ -+int qbman_fq_state_retirement_pending(const struct qbman_attr *state) -+{ -+ const uint32_t *p = ATTR32(state); -+ -+ return !!qb_attr_code_decode(&code_fq_np_r, p); -+} -+ -+int qbman_fq_state_overflow_error(const struct qbman_attr *state) -+{ -+ const uint32_t *p = ATTR32(state); -+ -+ return !!qb_attr_code_decode(&code_fq_np_oe, p); -+} -+ -+uint32_t qbman_fq_state_frame_count(const struct qbman_attr *state) -+{ -+ const uint32_t *p = ATTR32(state); -+ -+ return qb_attr_code_decode(&code_fq_np_frm_cnt, p); -+} -+ -+uint32_t qbman_fq_state_byte_count(const struct qbman_attr *state) -+{ -+ const uint32_t *p = ATTR32(state); -+ -+ return qb_attr_code_decode(&code_fq_np_byte_cnt, p); -+} -+ -+/* Query CGR */ -+static struct qb_attr_code code_cgr_cgid = QB_CODE(0, 16, 16); -+static struct qb_attr_code code_cgr_cscn_wq_en_enter = QB_CODE(2, 0, 1); -+static struct qb_attr_code code_cgr_cscn_wq_en_exit = QB_CODE(2, 1, 1); -+static struct qb_attr_code code_cgr_cscn_wq_icd = QB_CODE(2, 2, 1); -+static struct qb_attr_code code_cgr_mode = QB_CODE(3, 16, 2); -+static struct qb_attr_code code_cgr_rej_cnt_mode = QB_CODE(3, 18, 1); -+static struct qb_attr_code code_cgr_cscn_bdi = QB_CODE(3, 19, 1); -+static struct qb_attr_code code_cgr_cscn_wr_en_enter = QB_CODE(3, 24, 1); -+static struct qb_attr_code code_cgr_cscn_wr_en_exit = QB_CODE(3, 25, 1); -+static struct qb_attr_code code_cgr_cg_wr_ae = QB_CODE(3, 26, 1); -+static struct qb_attr_code code_cgr_cscn_dcp_en = QB_CODE(3, 27, 1); -+static struct qb_attr_code code_cgr_cg_wr_va = QB_CODE(3, 28, 1); -+static struct qb_attr_code code_cgr_i_cnt_wr_en = QB_CODE(4, 0, 1); -+static struct qb_attr_code code_cgr_i_cnt_wr_bnd = QB_CODE(4, 1, 5); -+static struct qb_attr_code code_cgr_td_en = QB_CODE(4, 8, 1); -+static struct qb_attr_code code_cgr_cs_thres = QB_CODE(4, 16, 13); -+static struct qb_attr_code code_cgr_cs_thres_x = QB_CODE(5, 0, 13); -+static struct qb_attr_code code_cgr_td_thres = QB_CODE(5, 16, 13); -+static struct qb_attr_code code_cgr_cscn_tdcp = QB_CODE(6, 0, 16); -+static struct qb_attr_code code_cgr_cscn_wqid = QB_CODE(6, 16, 16); -+static struct qb_attr_code code_cgr_cscn_vcgid = QB_CODE(7, 0, 16); -+static struct qb_attr_code code_cgr_cg_icid = QB_CODE(7, 16, 15); -+static struct qb_attr_code code_cgr_cg_pl = QB_CODE(7, 31, 1); -+static struct qb_attr_code code_cgr_cg_wr_addr_lo = QB_CODE(8, 0, 32); -+static struct qb_attr_code code_cgr_cg_wr_addr_hi = QB_CODE(9, 0, 32); -+static struct qb_attr_code code_cgr_cscn_ctx_lo = QB_CODE(10, 0, 32); -+static struct qb_attr_code code_cgr_cscn_ctx_hi = QB_CODE(11, 0, 32); -+ -+void qbman_cgr_attr_clear(struct qbman_attr *a) -+{ -+ memset(a, 0, sizeof(*a)); -+ attr_type_set(a, qbman_attr_usage_cgr); -+} -+ -+int qbman_cgr_query(struct qbman_swp *s, uint32_t cgid, struct qbman_attr *attr) -+{ -+ uint32_t *p; -+ uint32_t verb, rslt; -+ uint32_t *d[2]; -+ int i; -+ uint32_t query_verb; -+ -+ d[0] = ATTR32(attr); -+ d[1] = ATTR32_1(attr); -+ -+ qbman_cgr_attr_clear(attr); -+ -+ for (i = 0; i < 2; i++) { -+ p = qbman_swp_mc_start(s); -+ if (!p) -+ return -EBUSY; -+ query_verb = i ? QBMAN_WRED_QUERY : QBMAN_CGR_QUERY; -+ -+ qb_attr_code_encode(&code_cgr_cgid, p, cgid); -+ p = qbman_swp_mc_complete(s, p, p[0] | query_verb); -+ -+ /* Decode the outcome */ -+ verb = qb_attr_code_decode(&code_generic_verb, p); -+ rslt = qb_attr_code_decode(&code_generic_rslt, p); -+ BUG_ON(verb != query_verb); -+ -+ /* Determine success or failure */ -+ if (unlikely(rslt != QBMAN_MC_RSLT_OK)) { -+ pr_err("Query CGID 0x%x failed,", cgid); -+ pr_err(" verb=0x%02x, code=0x%02x\n", verb, rslt); -+ return -EIO; -+ } -+ /* For the configure, word[0] of the command contains only the -+ * verb/cgid. For the query, word[0] of the result contains -+ * only the verb/rslt fields. Skip word[0] in the latter case. -+ */ -+ word_copy(&d[i][1], &p[1], 15); -+ } -+ return 0; -+} -+ -+void qbman_cgr_attr_get_ctl1(struct qbman_attr *d, int *cscn_wq_en_enter, -+ int *cscn_wq_en_exit, int *cscn_wq_icd) -+ { -+ uint32_t *p = ATTR32(d); -+ *cscn_wq_en_enter = !!qb_attr_code_decode(&code_cgr_cscn_wq_en_enter, -+ p); -+ *cscn_wq_en_exit = !!qb_attr_code_decode(&code_cgr_cscn_wq_en_exit, p); -+ *cscn_wq_icd = !!qb_attr_code_decode(&code_cgr_cscn_wq_icd, p); -+} -+ -+void qbman_cgr_attr_get_mode(struct qbman_attr *d, uint32_t *mode, -+ int *rej_cnt_mode, int *cscn_bdi) -+{ -+ uint32_t *p = ATTR32(d); -+ *mode = qb_attr_code_decode(&code_cgr_mode, p); -+ *rej_cnt_mode = !!qb_attr_code_decode(&code_cgr_rej_cnt_mode, p); -+ *cscn_bdi = !!qb_attr_code_decode(&code_cgr_cscn_bdi, p); -+} -+ -+void qbman_cgr_attr_get_ctl2(struct qbman_attr *d, int *cscn_wr_en_enter, -+ int *cscn_wr_en_exit, int *cg_wr_ae, -+ int *cscn_dcp_en, int *cg_wr_va) -+{ -+ uint32_t *p = ATTR32(d); -+ *cscn_wr_en_enter = !!qb_attr_code_decode(&code_cgr_cscn_wr_en_enter, -+ p); -+ *cscn_wr_en_exit = !!qb_attr_code_decode(&code_cgr_cscn_wr_en_exit, p); -+ *cg_wr_ae = !!qb_attr_code_decode(&code_cgr_cg_wr_ae, p); -+ *cscn_dcp_en = !!qb_attr_code_decode(&code_cgr_cscn_dcp_en, p); -+ *cg_wr_va = !!qb_attr_code_decode(&code_cgr_cg_wr_va, p); -+} -+ -+void qbman_cgr_attr_get_iwc(struct qbman_attr *d, int *i_cnt_wr_en, -+ uint32_t *i_cnt_wr_bnd) -+{ -+ uint32_t *p = ATTR32(d); -+ *i_cnt_wr_en = !!qb_attr_code_decode(&code_cgr_i_cnt_wr_en, p); -+ *i_cnt_wr_bnd = qb_attr_code_decode(&code_cgr_i_cnt_wr_bnd, p); -+} -+ -+void qbman_cgr_attr_get_tdc(struct qbman_attr *d, int *td_en) -+{ -+ uint32_t *p = ATTR32(d); -+ *td_en = !!qb_attr_code_decode(&code_cgr_td_en, p); -+} -+ -+void qbman_cgr_attr_get_cs_thres(struct qbman_attr *d, uint32_t *cs_thres) -+{ -+ uint32_t *p = ATTR32(d); -+ *cs_thres = qbman_thresh_to_value(qb_attr_code_decode( -+ &code_cgr_cs_thres, p)); -+} -+ -+void qbman_cgr_attr_get_cs_thres_x(struct qbman_attr *d, -+ uint32_t *cs_thres_x) -+{ -+ uint32_t *p = ATTR32(d); -+ *cs_thres_x = qbman_thresh_to_value(qb_attr_code_decode( -+ &code_cgr_cs_thres_x, p)); -+} -+ -+void qbman_cgr_attr_get_td_thres(struct qbman_attr *d, uint32_t *td_thres) -+{ -+ uint32_t *p = ATTR32(d); -+ *td_thres = qbman_thresh_to_value(qb_attr_code_decode( -+ &code_cgr_td_thres, p)); -+} -+ -+void qbman_cgr_attr_get_cscn_tdcp(struct qbman_attr *d, uint32_t *cscn_tdcp) -+{ -+ uint32_t *p = ATTR32(d); -+ *cscn_tdcp = qb_attr_code_decode(&code_cgr_cscn_tdcp, p); -+} -+ -+void qbman_cgr_attr_get_cscn_wqid(struct qbman_attr *d, uint32_t *cscn_wqid) -+{ -+ uint32_t *p = ATTR32(d); -+ *cscn_wqid = qb_attr_code_decode(&code_cgr_cscn_wqid, p); -+} -+ -+void qbman_cgr_attr_get_cscn_vcgid(struct qbman_attr *d, -+ uint32_t *cscn_vcgid) -+{ -+ uint32_t *p = ATTR32(d); -+ *cscn_vcgid = qb_attr_code_decode(&code_cgr_cscn_vcgid, p); -+} -+ -+void qbman_cgr_attr_get_cg_icid(struct qbman_attr *d, uint32_t *icid, -+ int *pl) -+{ -+ uint32_t *p = ATTR32(d); -+ *icid = qb_attr_code_decode(&code_cgr_cg_icid, p); -+ *pl = !!qb_attr_code_decode(&code_cgr_cg_pl, p); -+} -+ -+void qbman_cgr_attr_get_cg_wr_addr(struct qbman_attr *d, -+ uint64_t *cg_wr_addr) -+{ -+ uint32_t *p = ATTR32(d); -+ *cg_wr_addr = ((uint64_t)qb_attr_code_decode(&code_cgr_cg_wr_addr_hi, -+ p) << 32) | -+ (uint64_t)qb_attr_code_decode(&code_cgr_cg_wr_addr_lo, -+ p); -+} -+ -+void qbman_cgr_attr_get_cscn_ctx(struct qbman_attr *d, uint64_t *cscn_ctx) -+{ -+ uint32_t *p = ATTR32(d); -+ *cscn_ctx = ((uint64_t)qb_attr_code_decode(&code_cgr_cscn_ctx_hi, p) -+ << 32) | -+ (uint64_t)qb_attr_code_decode(&code_cgr_cscn_ctx_lo, p); -+} -+ -+#define WRED_EDP_WORD(n) (18 + n/4) -+#define WRED_EDP_OFFSET(n) (8 * (n % 4)) -+#define WRED_PARM_DP_WORD(n) (n + 20) -+#define WRED_WE_EDP(n) (16 + n * 2) -+#define WRED_WE_PARM_DP(n) (17 + n * 2) -+void qbman_cgr_attr_wred_get_edp(struct qbman_attr *d, uint32_t idx, -+ int *edp) -+{ -+ uint32_t *p = ATTR32(d); -+ struct qb_attr_code code_wred_edp = QB_CODE(WRED_EDP_WORD(idx), -+ WRED_EDP_OFFSET(idx), 8); -+ *edp = (int)qb_attr_code_decode(&code_wred_edp, p); -+} -+ -+void qbman_cgr_attr_wred_dp_decompose(uint32_t dp, uint64_t *minth, -+ uint64_t *maxth, uint8_t *maxp) -+{ -+ uint8_t ma, mn, step_i, step_s, pn; -+ -+ ma = (uint8_t)(dp >> 24); -+ mn = (uint8_t)(dp >> 19) & 0x1f; -+ step_i = (uint8_t)(dp >> 11); -+ step_s = (uint8_t)(dp >> 6) & 0x1f; -+ pn = (uint8_t)dp & 0x3f; -+ -+ *maxp = ((pn<<2) * 100)/256; -+ -+ if (mn == 0) -+ *maxth = ma; -+ else -+ *maxth = ((ma+256) * (1<<(mn-1))); -+ -+ if (step_s == 0) -+ *minth = *maxth - step_i; -+ else -+ *minth = *maxth - (256 + step_i) * (1<<(step_s - 1)); -+} -+ -+void qbman_cgr_attr_wred_get_parm_dp(struct qbman_attr *d, uint32_t idx, -+ uint32_t *dp) -+{ -+ uint32_t *p = ATTR32(d); -+ struct qb_attr_code code_wred_parm_dp = QB_CODE(WRED_PARM_DP_WORD(idx), -+ 0, 8); -+ *dp = qb_attr_code_decode(&code_wred_parm_dp, p); -+} -+ -+/* Query CGR/CCGR/CQ statistics */ -+static struct qb_attr_code code_cgr_stat_ct = QB_CODE(4, 0, 32); -+static struct qb_attr_code code_cgr_stat_frame_cnt_lo = QB_CODE(4, 0, 32); -+static struct qb_attr_code code_cgr_stat_frame_cnt_hi = QB_CODE(5, 0, 8); -+static struct qb_attr_code code_cgr_stat_byte_cnt_lo = QB_CODE(6, 0, 32); -+static struct qb_attr_code code_cgr_stat_byte_cnt_hi = QB_CODE(7, 0, 16); -+static int qbman_cgr_statistics_query(struct qbman_swp *s, uint32_t cgid, -+ int clear, uint32_t command_type, -+ uint64_t *frame_cnt, uint64_t *byte_cnt) -+{ -+ uint32_t *p; -+ uint32_t verb, rslt; -+ uint32_t query_verb; -+ uint32_t hi, lo; -+ -+ p = qbman_swp_mc_start(s); -+ if (!p) -+ return -EBUSY; -+ -+ qb_attr_code_encode(&code_cgr_cgid, p, cgid); -+ if (command_type < 2) -+ qb_attr_code_encode(&code_cgr_stat_ct, p, command_type); -+ query_verb = clear ? -+ QBMAN_CGR_STAT_QUERY_CLR : QBMAN_CGR_STAT_QUERY; -+ p = qbman_swp_mc_complete(s, p, p[0] | query_verb); -+ -+ /* Decode the outcome */ -+ verb = qb_attr_code_decode(&code_generic_verb, p); -+ rslt = qb_attr_code_decode(&code_generic_rslt, p); -+ BUG_ON(verb != query_verb); -+ -+ /* Determine success or failure */ -+ if (unlikely(rslt != QBMAN_MC_RSLT_OK)) { -+ pr_err("Query statistics of CGID 0x%x failed,", cgid); -+ pr_err(" verb=0x%02x code=0x%02x\n", verb, rslt); -+ return -EIO; -+ } -+ -+ if (*frame_cnt) { -+ hi = qb_attr_code_decode(&code_cgr_stat_frame_cnt_hi, p); -+ lo = qb_attr_code_decode(&code_cgr_stat_frame_cnt_lo, p); -+ *frame_cnt = ((uint64_t)hi << 32) | (uint64_t)lo; -+ } -+ if (*byte_cnt) { -+ hi = qb_attr_code_decode(&code_cgr_stat_byte_cnt_hi, p); -+ lo = qb_attr_code_decode(&code_cgr_stat_byte_cnt_lo, p); -+ *byte_cnt = ((uint64_t)hi << 32) | (uint64_t)lo; -+ } -+ -+ return 0; -+} -+ -+int qbman_cgr_reject_statistics(struct qbman_swp *s, uint32_t cgid, int clear, -+ uint64_t *frame_cnt, uint64_t *byte_cnt) -+{ -+ return qbman_cgr_statistics_query(s, cgid, clear, 0xff, -+ frame_cnt, byte_cnt); -+} -+ -+int qbman_ccgr_reject_statistics(struct qbman_swp *s, uint32_t cgid, int clear, -+ uint64_t *frame_cnt, uint64_t *byte_cnt) -+{ -+ return qbman_cgr_statistics_query(s, cgid, clear, 1, -+ frame_cnt, byte_cnt); -+} -+ -+int qbman_cq_dequeue_statistics(struct qbman_swp *s, uint32_t cgid, int clear, -+ uint64_t *frame_cnt, uint64_t *byte_cnt) -+{ -+ return qbman_cgr_statistics_query(s, cgid, clear, 0, -+ frame_cnt, byte_cnt); -+} -diff --git a/drivers/staging/fsl-mc/bus/dpio/qbman_debug.h b/drivers/staging/fsl-mc/bus/dpio/qbman_debug.h -new file mode 100644 -index 0000000..1e6b002 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpio/qbman_debug.h -@@ -0,0 +1,136 @@ -+/* Copyright (C) 2015 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. -+ */ -+ -+struct qbman_attr { -+ uint32_t dont_manipulate_directly[40]; -+}; -+ -+/* Buffer pool query commands */ -+int qbman_bp_query(struct qbman_swp *s, uint32_t bpid, -+ struct qbman_attr *a); -+void qbman_bp_attr_get_bdi(struct qbman_attr *a, int *bdi, int *va, int *wae); -+void qbman_bp_attr_get_swdet(struct qbman_attr *a, uint32_t *swdet); -+void qbman_bp_attr_get_swdxt(struct qbman_attr *a, uint32_t *swdxt); -+void qbman_bp_attr_get_hwdet(struct qbman_attr *a, uint32_t *hwdet); -+void qbman_bp_attr_get_hwdxt(struct qbman_attr *a, uint32_t *hwdxt); -+void qbman_bp_attr_get_swset(struct qbman_attr *a, uint32_t *swset); -+void qbman_bp_attr_get_swsxt(struct qbman_attr *a, uint32_t *swsxt); -+void qbman_bp_attr_get_vbpid(struct qbman_attr *a, uint32_t *vbpid); -+void qbman_bp_attr_get_icid(struct qbman_attr *a, uint32_t *icid, int *pl); -+void qbman_bp_attr_get_bpscn_addr(struct qbman_attr *a, uint64_t *bpscn_addr); -+void qbman_bp_attr_get_bpscn_ctx(struct qbman_attr *a, uint64_t *bpscn_ctx); -+void qbman_bp_attr_get_hw_targ(struct qbman_attr *a, uint32_t *hw_targ); -+int qbman_bp_info_has_free_bufs(struct qbman_attr *a); -+int qbman_bp_info_is_depleted(struct qbman_attr *a); -+int qbman_bp_info_is_surplus(struct qbman_attr *a); -+uint32_t qbman_bp_info_num_free_bufs(struct qbman_attr *a); -+uint32_t qbman_bp_info_hdptr(struct qbman_attr *a); -+uint32_t qbman_bp_info_sdcnt(struct qbman_attr *a); -+uint32_t qbman_bp_info_hdcnt(struct qbman_attr *a); -+uint32_t qbman_bp_info_sscnt(struct qbman_attr *a); -+ -+/* FQ query function for programmable fields */ -+int qbman_fq_query(struct qbman_swp *s, uint32_t fqid, -+ struct qbman_attr *desc); -+void qbman_fq_attr_get_fqctrl(struct qbman_attr *d, uint32_t *fqctrl); -+void qbman_fq_attr_get_cgrid(struct qbman_attr *d, uint32_t *cgrid); -+void qbman_fq_attr_get_destwq(struct qbman_attr *d, uint32_t *destwq); -+void qbman_fq_attr_get_icscred(struct qbman_attr *d, uint32_t *icscred); -+void qbman_fq_attr_get_tdthresh(struct qbman_attr *d, uint32_t *tdthresh); -+void qbman_fq_attr_get_oa(struct qbman_attr *d, -+ int *oa_ics, int *oa_cgr, int32_t *oa_len); -+void qbman_fq_attr_get_mctl(struct qbman_attr *d, -+ int *bdi, int *ff, int *va, int *ps); -+void qbman_fq_attr_get_ctx(struct qbman_attr *d, uint32_t *hi, uint32_t *lo); -+void qbman_fq_attr_get_icid(struct qbman_attr *d, uint32_t *icid, int *pl); -+void qbman_fq_attr_get_vfqid(struct qbman_attr *d, uint32_t *vfqid); -+void qbman_fq_attr_get_erfqid(struct qbman_attr *d, uint32_t *erfqid); -+ -+/* FQ query command for non-programmable fields*/ -+enum qbman_fq_schedstate_e { -+ qbman_fq_schedstate_oos = 0, -+ qbman_fq_schedstate_retired, -+ qbman_fq_schedstate_tentatively_scheduled, -+ qbman_fq_schedstate_truly_scheduled, -+ qbman_fq_schedstate_parked, -+ qbman_fq_schedstate_held_active, -+}; -+ -+int qbman_fq_query_state(struct qbman_swp *s, uint32_t fqid, -+ struct qbman_attr *state); -+uint32_t qbman_fq_state_schedstate(const struct qbman_attr *state); -+int qbman_fq_state_force_eligible(const struct qbman_attr *state); -+int qbman_fq_state_xoff(const struct qbman_attr *state); -+int qbman_fq_state_retirement_pending(const struct qbman_attr *state); -+int qbman_fq_state_overflow_error(const struct qbman_attr *state); -+uint32_t qbman_fq_state_frame_count(const struct qbman_attr *state); -+uint32_t qbman_fq_state_byte_count(const struct qbman_attr *state); -+ -+/* CGR query */ -+int qbman_cgr_query(struct qbman_swp *s, uint32_t cgid, -+ struct qbman_attr *attr); -+void qbman_cgr_attr_get_ctl1(struct qbman_attr *d, int *cscn_wq_en_enter, -+ int *cscn_wq_en_exit, int *cscn_wq_icd); -+void qbman_cgr_attr_get_mode(struct qbman_attr *d, uint32_t *mode, -+ int *rej_cnt_mode, int *cscn_bdi); -+void qbman_cgr_attr_get_ctl2(struct qbman_attr *d, int *cscn_wr_en_enter, -+ int *cscn_wr_en_exit, int *cg_wr_ae, -+ int *cscn_dcp_en, int *cg_wr_va); -+void qbman_cgr_attr_get_iwc(struct qbman_attr *d, int *i_cnt_wr_en, -+ uint32_t *i_cnt_wr_bnd); -+void qbman_cgr_attr_get_tdc(struct qbman_attr *d, int *td_en); -+void qbman_cgr_attr_get_cs_thres(struct qbman_attr *d, uint32_t *cs_thres); -+void qbman_cgr_attr_get_cs_thres_x(struct qbman_attr *d, -+ uint32_t *cs_thres_x); -+void qbman_cgr_attr_get_td_thres(struct qbman_attr *d, uint32_t *td_thres); -+void qbman_cgr_attr_get_cscn_tdcp(struct qbman_attr *d, uint32_t *cscn_tdcp); -+void qbman_cgr_attr_get_cscn_wqid(struct qbman_attr *d, uint32_t *cscn_wqid); -+void qbman_cgr_attr_get_cscn_vcgid(struct qbman_attr *d, -+ uint32_t *cscn_vcgid); -+void qbman_cgr_attr_get_cg_icid(struct qbman_attr *d, uint32_t *icid, -+ int *pl); -+void qbman_cgr_attr_get_cg_wr_addr(struct qbman_attr *d, -+ uint64_t *cg_wr_addr); -+void qbman_cgr_attr_get_cscn_ctx(struct qbman_attr *d, uint64_t *cscn_ctx); -+void qbman_cgr_attr_wred_get_edp(struct qbman_attr *d, uint32_t idx, -+ int *edp); -+void qbman_cgr_attr_wred_dp_decompose(uint32_t dp, uint64_t *minth, -+ uint64_t *maxth, uint8_t *maxp); -+void qbman_cgr_attr_wred_get_parm_dp(struct qbman_attr *d, uint32_t idx, -+ uint32_t *dp); -+ -+/* CGR/CCGR/CQ statistics query */ -+int qbman_cgr_reject_statistics(struct qbman_swp *s, uint32_t cgid, int clear, -+ uint64_t *frame_cnt, uint64_t *byte_cnt); -+int qbman_ccgr_reject_statistics(struct qbman_swp *s, uint32_t cgid, int clear, -+ uint64_t *frame_cnt, uint64_t *byte_cnt); -+int qbman_cq_dequeue_statistics(struct qbman_swp *s, uint32_t cgid, int clear, -+ uint64_t *frame_cnt, uint64_t *byte_cnt); -diff --git a/drivers/staging/fsl-mc/bus/dpio/qbman_portal.c b/drivers/staging/fsl-mc/bus/dpio/qbman_portal.c -new file mode 100644 -index 0000000..6c5638b ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpio/qbman_portal.c -@@ -0,0 +1,1212 @@ -+/* Copyright (C) 2014 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 "qbman_portal.h" -+ -+/* QBMan portal management command codes */ -+#define QBMAN_MC_ACQUIRE 0x30 -+#define QBMAN_WQCHAN_CONFIGURE 0x46 -+ -+/* CINH register offsets */ -+#define QBMAN_CINH_SWP_EQAR 0x8c0 -+#define QBMAN_CINH_SWP_DQPI 0xa00 -+#define QBMAN_CINH_SWP_DCAP 0xac0 -+#define QBMAN_CINH_SWP_SDQCR 0xb00 -+#define QBMAN_CINH_SWP_RAR 0xcc0 -+#define QBMAN_CINH_SWP_ISR 0xe00 -+#define QBMAN_CINH_SWP_IER 0xe40 -+#define QBMAN_CINH_SWP_ISDR 0xe80 -+#define QBMAN_CINH_SWP_IIR 0xec0 -+ -+/* CENA register offsets */ -+#define QBMAN_CENA_SWP_EQCR(n) (0x000 + ((uint32_t)(n) << 6)) -+#define QBMAN_CENA_SWP_DQRR(n) (0x200 + ((uint32_t)(n) << 6)) -+#define QBMAN_CENA_SWP_RCR(n) (0x400 + ((uint32_t)(n) << 6)) -+#define QBMAN_CENA_SWP_CR 0x600 -+#define QBMAN_CENA_SWP_RR(vb) (0x700 + ((uint32_t)(vb) >> 1)) -+#define QBMAN_CENA_SWP_VDQCR 0x780 -+ -+/* Reverse mapping of QBMAN_CENA_SWP_DQRR() */ -+#define QBMAN_IDX_FROM_DQRR(p) (((unsigned long)p & 0x1ff) >> 6) -+ -+/* QBMan FQ management command codes */ -+#define QBMAN_FQ_SCHEDULE 0x48 -+#define QBMAN_FQ_FORCE 0x49 -+#define QBMAN_FQ_XON 0x4d -+#define QBMAN_FQ_XOFF 0x4e -+ -+/*******************************/ -+/* Pre-defined attribute codes */ -+/*******************************/ -+ -+struct qb_attr_code code_generic_verb = QB_CODE(0, 0, 7); -+struct qb_attr_code code_generic_rslt = QB_CODE(0, 8, 8); -+ -+/*************************/ -+/* SDQCR attribute codes */ -+/*************************/ -+ -+/* we put these here because at least some of them are required by -+ * qbman_swp_init() */ -+struct qb_attr_code code_sdqcr_dct = QB_CODE(0, 24, 2); -+struct qb_attr_code code_sdqcr_fc = QB_CODE(0, 29, 1); -+struct qb_attr_code code_sdqcr_tok = QB_CODE(0, 16, 8); -+#define CODE_SDQCR_DQSRC(n) QB_CODE(0, n, 1) -+enum qbman_sdqcr_dct { -+ qbman_sdqcr_dct_null = 0, -+ qbman_sdqcr_dct_prio_ics, -+ qbman_sdqcr_dct_active_ics, -+ qbman_sdqcr_dct_active -+}; -+enum qbman_sdqcr_fc { -+ qbman_sdqcr_fc_one = 0, -+ qbman_sdqcr_fc_up_to_3 = 1 -+}; -+struct qb_attr_code code_sdqcr_dqsrc = QB_CODE(0, 0, 16); -+ -+/*********************************/ -+/* Portal constructor/destructor */ -+/*********************************/ -+ -+/* Software portals should always be in the power-on state when we initialise, -+ * due to the CCSR-based portal reset functionality that MC has. -+ * -+ * Erk! Turns out that QMan versions prior to 4.1 do not correctly reset DQRR -+ * valid-bits, so we need to support a workaround where we don't trust -+ * valid-bits when detecting new entries until any stale ring entries have been -+ * overwritten at least once. The idea is that we read PI for the first few -+ * entries, then switch to valid-bit after that. The trick is to clear the -+ * bug-work-around boolean once the PI wraps around the ring for the first time. -+ * -+ * Note: this still carries a slight additional cost once the decrementer hits -+ * zero, so ideally the workaround should only be compiled in if the compiled -+ * image needs to support affected chips. We use WORKAROUND_DQRR_RESET_BUG for -+ * this. -+ */ -+struct qbman_swp *qbman_swp_init(const struct qbman_swp_desc *d) -+{ -+ int ret; -+ struct qbman_swp *p = kmalloc(sizeof(*p), GFP_KERNEL); -+ -+ if (!p) -+ return NULL; -+ p->desc = d; -+#ifdef QBMAN_CHECKING -+ p->mc.check = swp_mc_can_start; -+#endif -+ p->mc.valid_bit = QB_VALID_BIT; -+ p->sdq = 0; -+ qb_attr_code_encode(&code_sdqcr_dct, &p->sdq, qbman_sdqcr_dct_prio_ics); -+ qb_attr_code_encode(&code_sdqcr_fc, &p->sdq, qbman_sdqcr_fc_up_to_3); -+ qb_attr_code_encode(&code_sdqcr_tok, &p->sdq, 0xbb); -+ atomic_set(&p->vdq.busy, 1); -+ p->vdq.valid_bit = QB_VALID_BIT; -+ p->dqrr.next_idx = 0; -+ p->dqrr.valid_bit = QB_VALID_BIT; -+ /* TODO: should also read PI/CI type registers and check that they're on -+ * PoR values. If we're asked to initialise portals that aren't in reset -+ * state, bad things will follow. */ -+#ifdef WORKAROUND_DQRR_RESET_BUG -+ p->dqrr.reset_bug = 1; -+#endif -+ if ((p->desc->qman_version & 0xFFFF0000) < QMAN_REV_4100) -+ p->dqrr.dqrr_size = 4; -+ else -+ p->dqrr.dqrr_size = 8; -+ ret = qbman_swp_sys_init(&p->sys, d, p->dqrr.dqrr_size); -+ if (ret) { -+ kfree(p); -+ pr_err("qbman_swp_sys_init() failed %d\n", ret); -+ return NULL; -+ } -+ /* SDQCR needs to be initialized to 0 when no channels are -+ being dequeued from or else the QMan HW will indicate an -+ error. The values that were calculated above will be -+ applied when dequeues from a specific channel are enabled */ -+ qbman_cinh_write(&p->sys, QBMAN_CINH_SWP_SDQCR, 0); -+ return p; -+} -+ -+void qbman_swp_finish(struct qbman_swp *p) -+{ -+#ifdef QBMAN_CHECKING -+ BUG_ON(p->mc.check != swp_mc_can_start); -+#endif -+ qbman_swp_sys_finish(&p->sys); -+ kfree(p); -+} -+ -+const struct qbman_swp_desc *qbman_swp_get_desc(struct qbman_swp *p) -+{ -+ return p->desc; -+} -+ -+/**************/ -+/* Interrupts */ -+/**************/ -+ -+uint32_t qbman_swp_interrupt_get_vanish(struct qbman_swp *p) -+{ -+ return qbman_cinh_read(&p->sys, QBMAN_CINH_SWP_ISDR); -+} -+ -+void qbman_swp_interrupt_set_vanish(struct qbman_swp *p, uint32_t mask) -+{ -+ qbman_cinh_write(&p->sys, QBMAN_CINH_SWP_ISDR, mask); -+} -+ -+uint32_t qbman_swp_interrupt_read_status(struct qbman_swp *p) -+{ -+ return qbman_cinh_read(&p->sys, QBMAN_CINH_SWP_ISR); -+} -+ -+void qbman_swp_interrupt_clear_status(struct qbman_swp *p, uint32_t mask) -+{ -+ qbman_cinh_write(&p->sys, QBMAN_CINH_SWP_ISR, mask); -+} -+ -+uint32_t qbman_swp_interrupt_get_trigger(struct qbman_swp *p) -+{ -+ return qbman_cinh_read(&p->sys, QBMAN_CINH_SWP_IER); -+} -+ -+void qbman_swp_interrupt_set_trigger(struct qbman_swp *p, uint32_t mask) -+{ -+ qbman_cinh_write(&p->sys, QBMAN_CINH_SWP_IER, mask); -+} -+ -+int qbman_swp_interrupt_get_inhibit(struct qbman_swp *p) -+{ -+ return qbman_cinh_read(&p->sys, QBMAN_CINH_SWP_IIR); -+} -+ -+void qbman_swp_interrupt_set_inhibit(struct qbman_swp *p, int inhibit) -+{ -+ qbman_cinh_write(&p->sys, QBMAN_CINH_SWP_IIR, inhibit ? 0xffffffff : 0); -+} -+ -+/***********************/ -+/* Management commands */ -+/***********************/ -+ -+/* -+ * Internal code common to all types of management commands. -+ */ -+ -+void *qbman_swp_mc_start(struct qbman_swp *p) -+{ -+ void *ret; -+#ifdef QBMAN_CHECKING -+ BUG_ON(p->mc.check != swp_mc_can_start); -+#endif -+ ret = qbman_cena_write_start(&p->sys, QBMAN_CENA_SWP_CR); -+#ifdef QBMAN_CHECKING -+ if (!ret) -+ p->mc.check = swp_mc_can_submit; -+#endif -+ return ret; -+} -+ -+void qbman_swp_mc_submit(struct qbman_swp *p, void *cmd, uint32_t cmd_verb) -+{ -+ uint32_t *v = cmd; -+#ifdef QBMAN_CHECKING -+ BUG_ON(!p->mc.check != swp_mc_can_submit); -+#endif -+ /* TBD: "|=" is going to hurt performance. Need to move as many fields -+ * out of word zero, and for those that remain, the "OR" needs to occur -+ * at the caller side. This debug check helps to catch cases where the -+ * caller wants to OR but has forgotten to do so. */ -+ BUG_ON((*v & cmd_verb) != *v); -+ *v = cmd_verb | p->mc.valid_bit; -+ qbman_cena_write_complete(&p->sys, QBMAN_CENA_SWP_CR, cmd); -+#ifdef QBMAN_CHECKING -+ p->mc.check = swp_mc_can_poll; -+#endif -+} -+ -+void *qbman_swp_mc_result(struct qbman_swp *p) -+{ -+ uint32_t *ret, verb; -+#ifdef QBMAN_CHECKING -+ BUG_ON(p->mc.check != swp_mc_can_poll); -+#endif -+ qbman_cena_invalidate_prefetch(&p->sys, -+ QBMAN_CENA_SWP_RR(p->mc.valid_bit)); -+ ret = qbman_cena_read(&p->sys, QBMAN_CENA_SWP_RR(p->mc.valid_bit)); -+ /* Remove the valid-bit - command completed iff the rest is non-zero */ -+ verb = ret[0] & ~QB_VALID_BIT; -+ if (!verb) -+ return NULL; -+#ifdef QBMAN_CHECKING -+ p->mc.check = swp_mc_can_start; -+#endif -+ p->mc.valid_bit ^= QB_VALID_BIT; -+ return ret; -+} -+ -+/***********/ -+/* Enqueue */ -+/***********/ -+ -+/* These should be const, eventually */ -+static struct qb_attr_code code_eq_cmd = QB_CODE(0, 0, 2); -+static struct qb_attr_code code_eq_eqdi = QB_CODE(0, 3, 1); -+static struct qb_attr_code code_eq_dca_en = QB_CODE(0, 15, 1); -+static struct qb_attr_code code_eq_dca_pk = QB_CODE(0, 14, 1); -+static struct qb_attr_code code_eq_dca_idx = QB_CODE(0, 8, 2); -+static struct qb_attr_code code_eq_orp_en = QB_CODE(0, 2, 1); -+static struct qb_attr_code code_eq_orp_is_nesn = QB_CODE(0, 31, 1); -+static struct qb_attr_code code_eq_orp_nlis = QB_CODE(0, 30, 1); -+static struct qb_attr_code code_eq_orp_seqnum = QB_CODE(0, 16, 14); -+static struct qb_attr_code code_eq_opr_id = QB_CODE(1, 0, 16); -+static struct qb_attr_code code_eq_tgt_id = QB_CODE(2, 0, 24); -+/* static struct qb_attr_code code_eq_tag = QB_CODE(3, 0, 32); */ -+static struct qb_attr_code code_eq_qd_en = QB_CODE(0, 4, 1); -+static struct qb_attr_code code_eq_qd_bin = QB_CODE(4, 0, 16); -+static struct qb_attr_code code_eq_qd_pri = QB_CODE(4, 16, 4); -+static struct qb_attr_code code_eq_rsp_stash = QB_CODE(5, 16, 1); -+static struct qb_attr_code code_eq_rsp_id = QB_CODE(5, 24, 8); -+static struct qb_attr_code code_eq_rsp_lo = QB_CODE(6, 0, 32); -+ -+enum qbman_eq_cmd_e { -+ /* No enqueue, primarily for plugging ORP gaps for dropped frames */ -+ qbman_eq_cmd_empty, -+ /* DMA an enqueue response once complete */ -+ qbman_eq_cmd_respond, -+ /* DMA an enqueue response only if the enqueue fails */ -+ qbman_eq_cmd_respond_reject -+}; -+ -+void qbman_eq_desc_clear(struct qbman_eq_desc *d) -+{ -+ memset(d, 0, sizeof(*d)); -+} -+ -+void qbman_eq_desc_set_no_orp(struct qbman_eq_desc *d, int respond_success) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ qb_attr_code_encode(&code_eq_orp_en, cl, 0); -+ qb_attr_code_encode(&code_eq_cmd, cl, -+ respond_success ? qbman_eq_cmd_respond : -+ qbman_eq_cmd_respond_reject); -+} -+ -+void qbman_eq_desc_set_orp(struct qbman_eq_desc *d, int respond_success, -+ uint32_t opr_id, uint32_t seqnum, int incomplete) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ qb_attr_code_encode(&code_eq_orp_en, cl, 1); -+ qb_attr_code_encode(&code_eq_cmd, cl, -+ respond_success ? qbman_eq_cmd_respond : -+ qbman_eq_cmd_respond_reject); -+ qb_attr_code_encode(&code_eq_opr_id, cl, opr_id); -+ qb_attr_code_encode(&code_eq_orp_seqnum, cl, seqnum); -+ qb_attr_code_encode(&code_eq_orp_nlis, cl, !!incomplete); -+} -+ -+void qbman_eq_desc_set_orp_hole(struct qbman_eq_desc *d, uint32_t opr_id, -+ uint32_t seqnum) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ qb_attr_code_encode(&code_eq_orp_en, cl, 1); -+ qb_attr_code_encode(&code_eq_cmd, cl, qbman_eq_cmd_empty); -+ qb_attr_code_encode(&code_eq_opr_id, cl, opr_id); -+ qb_attr_code_encode(&code_eq_orp_seqnum, cl, seqnum); -+ qb_attr_code_encode(&code_eq_orp_nlis, cl, 0); -+ qb_attr_code_encode(&code_eq_orp_is_nesn, cl, 0); -+} -+ -+void qbman_eq_desc_set_orp_nesn(struct qbman_eq_desc *d, uint32_t opr_id, -+ uint32_t seqnum) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ qb_attr_code_encode(&code_eq_orp_en, cl, 1); -+ qb_attr_code_encode(&code_eq_cmd, cl, qbman_eq_cmd_empty); -+ qb_attr_code_encode(&code_eq_opr_id, cl, opr_id); -+ qb_attr_code_encode(&code_eq_orp_seqnum, cl, seqnum); -+ qb_attr_code_encode(&code_eq_orp_nlis, cl, 0); -+ qb_attr_code_encode(&code_eq_orp_is_nesn, cl, 1); -+} -+ -+void qbman_eq_desc_set_response(struct qbman_eq_desc *d, -+ dma_addr_t storage_phys, -+ int stash) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ qb_attr_code_encode_64(&code_eq_rsp_lo, (uint64_t *)cl, storage_phys); -+ qb_attr_code_encode(&code_eq_rsp_stash, cl, !!stash); -+} -+ -+void qbman_eq_desc_set_token(struct qbman_eq_desc *d, uint8_t token) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ qb_attr_code_encode(&code_eq_rsp_id, cl, (uint32_t)token); -+} -+ -+void qbman_eq_desc_set_fq(struct qbman_eq_desc *d, uint32_t fqid) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ qb_attr_code_encode(&code_eq_qd_en, cl, 0); -+ qb_attr_code_encode(&code_eq_tgt_id, cl, fqid); -+} -+ -+void qbman_eq_desc_set_qd(struct qbman_eq_desc *d, uint32_t qdid, -+ uint32_t qd_bin, uint32_t qd_prio) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ qb_attr_code_encode(&code_eq_qd_en, cl, 1); -+ qb_attr_code_encode(&code_eq_tgt_id, cl, qdid); -+ qb_attr_code_encode(&code_eq_qd_bin, cl, qd_bin); -+ qb_attr_code_encode(&code_eq_qd_pri, cl, qd_prio); -+} -+ -+void qbman_eq_desc_set_eqdi(struct qbman_eq_desc *d, int enable) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ qb_attr_code_encode(&code_eq_eqdi, cl, !!enable); -+} -+ -+void qbman_eq_desc_set_dca(struct qbman_eq_desc *d, int enable, -+ uint32_t dqrr_idx, int park) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ qb_attr_code_encode(&code_eq_dca_en, cl, !!enable); -+ if (enable) { -+ qb_attr_code_encode(&code_eq_dca_pk, cl, !!park); -+ qb_attr_code_encode(&code_eq_dca_idx, cl, dqrr_idx); -+ } -+} -+ -+#define EQAR_IDX(eqar) ((eqar) & 0x7) -+#define EQAR_VB(eqar) ((eqar) & 0x80) -+#define EQAR_SUCCESS(eqar) ((eqar) & 0x100) -+ -+int qbman_swp_enqueue(struct qbman_swp *s, const struct qbman_eq_desc *d, -+ const struct qbman_fd *fd) -+{ -+ uint32_t *p; -+ const uint32_t *cl = qb_cl(d); -+ uint32_t eqar = qbman_cinh_read(&s->sys, QBMAN_CINH_SWP_EQAR); -+ -+ pr_debug("EQAR=%08x\n", eqar); -+ if (!EQAR_SUCCESS(eqar)) -+ return -EBUSY; -+ p = qbman_cena_write_start(&s->sys, -+ QBMAN_CENA_SWP_EQCR(EQAR_IDX(eqar))); -+ word_copy(&p[1], &cl[1], 7); -+ word_copy(&p[8], fd, sizeof(*fd) >> 2); -+ /* Set the verb byte, have to substitute in the valid-bit */ -+ p[0] = cl[0] | EQAR_VB(eqar); -+ qbman_cena_write_complete(&s->sys, -+ QBMAN_CENA_SWP_EQCR(EQAR_IDX(eqar)), -+ p); -+ return 0; -+} -+ -+/*************************/ -+/* Static (push) dequeue */ -+/*************************/ -+ -+void qbman_swp_push_get(struct qbman_swp *s, uint8_t channel_idx, int *enabled) -+{ -+ struct qb_attr_code code = CODE_SDQCR_DQSRC(channel_idx); -+ -+ BUG_ON(channel_idx > 15); -+ *enabled = (int)qb_attr_code_decode(&code, &s->sdq); -+} -+ -+void qbman_swp_push_set(struct qbman_swp *s, uint8_t channel_idx, int enable) -+{ -+ uint16_t dqsrc; -+ struct qb_attr_code code = CODE_SDQCR_DQSRC(channel_idx); -+ -+ BUG_ON(channel_idx > 15); -+ qb_attr_code_encode(&code, &s->sdq, !!enable); -+ /* Read make the complete src map. If no channels are enabled -+ the SDQCR must be 0 or else QMan will assert errors */ -+ dqsrc = (uint16_t)qb_attr_code_decode(&code_sdqcr_dqsrc, &s->sdq); -+ if (dqsrc != 0) -+ qbman_cinh_write(&s->sys, QBMAN_CINH_SWP_SDQCR, s->sdq); -+ else -+ qbman_cinh_write(&s->sys, QBMAN_CINH_SWP_SDQCR, 0); -+} -+ -+/***************************/ -+/* Volatile (pull) dequeue */ -+/***************************/ -+ -+/* These should be const, eventually */ -+static struct qb_attr_code code_pull_dct = QB_CODE(0, 0, 2); -+static struct qb_attr_code code_pull_dt = QB_CODE(0, 2, 2); -+static struct qb_attr_code code_pull_rls = QB_CODE(0, 4, 1); -+static struct qb_attr_code code_pull_stash = QB_CODE(0, 5, 1); -+static struct qb_attr_code code_pull_numframes = QB_CODE(0, 8, 4); -+static struct qb_attr_code code_pull_token = QB_CODE(0, 16, 8); -+static struct qb_attr_code code_pull_dqsource = QB_CODE(1, 0, 24); -+static struct qb_attr_code code_pull_rsp_lo = QB_CODE(2, 0, 32); -+ -+enum qb_pull_dt_e { -+ qb_pull_dt_channel, -+ qb_pull_dt_workqueue, -+ qb_pull_dt_framequeue -+}; -+ -+void qbman_pull_desc_clear(struct qbman_pull_desc *d) -+{ -+ memset(d, 0, sizeof(*d)); -+} -+ -+void qbman_pull_desc_set_storage(struct qbman_pull_desc *d, -+ struct dpaa2_dq *storage, -+ dma_addr_t storage_phys, -+ int stash) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ /* Squiggle the pointer 'storage' into the extra 2 words of the -+ * descriptor (which aren't copied to the hw command) */ -+ *(void **)&cl[4] = storage; -+ if (!storage) { -+ qb_attr_code_encode(&code_pull_rls, cl, 0); -+ return; -+ } -+ qb_attr_code_encode(&code_pull_rls, cl, 1); -+ qb_attr_code_encode(&code_pull_stash, cl, !!stash); -+ qb_attr_code_encode_64(&code_pull_rsp_lo, (uint64_t *)cl, storage_phys); -+} -+ -+void qbman_pull_desc_set_numframes(struct qbman_pull_desc *d, uint8_t numframes) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ BUG_ON(!numframes || (numframes > 16)); -+ qb_attr_code_encode(&code_pull_numframes, cl, -+ (uint32_t)(numframes - 1)); -+} -+ -+void qbman_pull_desc_set_token(struct qbman_pull_desc *d, uint8_t token) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ qb_attr_code_encode(&code_pull_token, cl, token); -+} -+ -+void qbman_pull_desc_set_fq(struct qbman_pull_desc *d, uint32_t fqid) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ qb_attr_code_encode(&code_pull_dct, cl, 1); -+ qb_attr_code_encode(&code_pull_dt, cl, qb_pull_dt_framequeue); -+ qb_attr_code_encode(&code_pull_dqsource, cl, fqid); -+} -+ -+void qbman_pull_desc_set_wq(struct qbman_pull_desc *d, uint32_t wqid, -+ enum qbman_pull_type_e dct) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ qb_attr_code_encode(&code_pull_dct, cl, dct); -+ qb_attr_code_encode(&code_pull_dt, cl, qb_pull_dt_workqueue); -+ qb_attr_code_encode(&code_pull_dqsource, cl, wqid); -+} -+ -+void qbman_pull_desc_set_channel(struct qbman_pull_desc *d, uint32_t chid, -+ enum qbman_pull_type_e dct) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ qb_attr_code_encode(&code_pull_dct, cl, dct); -+ qb_attr_code_encode(&code_pull_dt, cl, qb_pull_dt_channel); -+ qb_attr_code_encode(&code_pull_dqsource, cl, chid); -+} -+ -+int qbman_swp_pull(struct qbman_swp *s, struct qbman_pull_desc *d) -+{ -+ uint32_t *p; -+ uint32_t *cl = qb_cl(d); -+ -+ if (!atomic_dec_and_test(&s->vdq.busy)) { -+ atomic_inc(&s->vdq.busy); -+ return -EBUSY; -+ } -+ s->vdq.storage = *(void **)&cl[4]; -+ qb_attr_code_encode(&code_pull_token, cl, 1); -+ p = qbman_cena_write_start(&s->sys, QBMAN_CENA_SWP_VDQCR); -+ word_copy(&p[1], &cl[1], 3); -+ /* Set the verb byte, have to substitute in the valid-bit */ -+ p[0] = cl[0] | s->vdq.valid_bit; -+ s->vdq.valid_bit ^= QB_VALID_BIT; -+ qbman_cena_write_complete(&s->sys, QBMAN_CENA_SWP_VDQCR, p); -+ return 0; -+} -+ -+/****************/ -+/* Polling DQRR */ -+/****************/ -+ -+static struct qb_attr_code code_dqrr_verb = QB_CODE(0, 0, 8); -+static struct qb_attr_code code_dqrr_response = QB_CODE(0, 0, 7); -+static struct qb_attr_code code_dqrr_stat = QB_CODE(0, 8, 8); -+static struct qb_attr_code code_dqrr_seqnum = QB_CODE(0, 16, 14); -+static struct qb_attr_code code_dqrr_odpid = QB_CODE(1, 0, 16); -+/* static struct qb_attr_code code_dqrr_tok = QB_CODE(1, 24, 8); */ -+static struct qb_attr_code code_dqrr_fqid = QB_CODE(2, 0, 24); -+static struct qb_attr_code code_dqrr_byte_count = QB_CODE(4, 0, 32); -+static struct qb_attr_code code_dqrr_frame_count = QB_CODE(5, 0, 24); -+static struct qb_attr_code code_dqrr_ctx_lo = QB_CODE(6, 0, 32); -+ -+#define QBMAN_RESULT_DQ 0x60 -+#define QBMAN_RESULT_FQRN 0x21 -+#define QBMAN_RESULT_FQRNI 0x22 -+#define QBMAN_RESULT_FQPN 0x24 -+#define QBMAN_RESULT_FQDAN 0x25 -+#define QBMAN_RESULT_CDAN 0x26 -+#define QBMAN_RESULT_CSCN_MEM 0x27 -+#define QBMAN_RESULT_CGCU 0x28 -+#define QBMAN_RESULT_BPSCN 0x29 -+#define QBMAN_RESULT_CSCN_WQ 0x2a -+ -+static struct qb_attr_code code_dqpi_pi = QB_CODE(0, 0, 4); -+ -+/* NULL return if there are no unconsumed DQRR entries. Returns a DQRR entry -+ * only once, so repeated calls can return a sequence of DQRR entries, without -+ * requiring they be consumed immediately or in any particular order. */ -+const struct dpaa2_dq *qbman_swp_dqrr_next(struct qbman_swp *s) -+{ -+ uint32_t verb; -+ uint32_t response_verb; -+ uint32_t flags; -+ const struct dpaa2_dq *dq; -+ const uint32_t *p; -+ -+ /* Before using valid-bit to detect if something is there, we have to -+ * handle the case of the DQRR reset bug... */ -+#ifdef WORKAROUND_DQRR_RESET_BUG -+ if (unlikely(s->dqrr.reset_bug)) { -+ /* We pick up new entries by cache-inhibited producer index, -+ * which means that a non-coherent mapping would require us to -+ * invalidate and read *only* once that PI has indicated that -+ * there's an entry here. The first trip around the DQRR ring -+ * will be much less efficient than all subsequent trips around -+ * it... -+ */ -+ uint32_t dqpi = qbman_cinh_read(&s->sys, QBMAN_CINH_SWP_DQPI); -+ uint32_t pi = qb_attr_code_decode(&code_dqpi_pi, &dqpi); -+ /* there are new entries iff pi != next_idx */ -+ if (pi == s->dqrr.next_idx) -+ return NULL; -+ /* if next_idx is/was the last ring index, and 'pi' is -+ * different, we can disable the workaround as all the ring -+ * entries have now been DMA'd to so valid-bit checking is -+ * repaired. Note: this logic needs to be based on next_idx -+ * (which increments one at a time), rather than on pi (which -+ * can burst and wrap-around between our snapshots of it). -+ */ -+ if (s->dqrr.next_idx == (s->dqrr.dqrr_size - 1)) { -+ pr_debug("DEBUG: next_idx=%d, pi=%d, clear reset bug\n", -+ s->dqrr.next_idx, pi); -+ s->dqrr.reset_bug = 0; -+ } -+ qbman_cena_invalidate_prefetch(&s->sys, -+ QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx)); -+ } -+#endif -+ -+ dq = qbman_cena_read(&s->sys, QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx)); -+ p = qb_cl(dq); -+ verb = qb_attr_code_decode(&code_dqrr_verb, p); -+ -+ /* If the valid-bit isn't of the expected polarity, nothing there. Note, -+ * in the DQRR reset bug workaround, we shouldn't need to skip these -+ * check, because we've already determined that a new entry is available -+ * and we've invalidated the cacheline before reading it, so the -+ * valid-bit behaviour is repaired and should tell us what we already -+ * knew from reading PI. -+ */ -+ if ((verb & QB_VALID_BIT) != s->dqrr.valid_bit) { -+ qbman_cena_invalidate_prefetch(&s->sys, -+ QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx)); -+ return NULL; -+ } -+ /* There's something there. Move "next_idx" attention to the next ring -+ * entry (and prefetch it) before returning what we found. */ -+ s->dqrr.next_idx++; -+ s->dqrr.next_idx &= s->dqrr.dqrr_size - 1; /* Wrap around */ -+ /* TODO: it's possible to do all this without conditionals, optimise it -+ * later. */ -+ if (!s->dqrr.next_idx) -+ s->dqrr.valid_bit ^= QB_VALID_BIT; -+ -+ /* If this is the final response to a volatile dequeue command -+ indicate that the vdq is no longer busy */ -+ flags = dpaa2_dq_flags(dq); -+ response_verb = qb_attr_code_decode(&code_dqrr_response, &verb); -+ if ((response_verb == QBMAN_RESULT_DQ) && -+ (flags & DPAA2_DQ_STAT_VOLATILE) && -+ (flags & DPAA2_DQ_STAT_EXPIRED)) -+ atomic_inc(&s->vdq.busy); -+ -+ qbman_cena_invalidate_prefetch(&s->sys, -+ QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx)); -+ return dq; -+} -+ -+/* Consume DQRR entries previously returned from qbman_swp_dqrr_next(). */ -+void qbman_swp_dqrr_consume(struct qbman_swp *s, const struct dpaa2_dq *dq) -+{ -+ qbman_cinh_write(&s->sys, QBMAN_CINH_SWP_DCAP, QBMAN_IDX_FROM_DQRR(dq)); -+} -+ -+/*********************************/ -+/* Polling user-provided storage */ -+/*********************************/ -+ -+int qbman_result_has_new_result(struct qbman_swp *s, -+ const struct dpaa2_dq *dq) -+{ -+ /* To avoid converting the little-endian DQ entry to host-endian prior -+ * to us knowing whether there is a valid entry or not (and run the -+ * risk of corrupting the incoming hardware LE write), we detect in -+ * hardware endianness rather than host. This means we need a different -+ * "code" depending on whether we are BE or LE in software, which is -+ * where DQRR_TOK_OFFSET comes in... */ -+ static struct qb_attr_code code_dqrr_tok_detect = -+ QB_CODE(0, DQRR_TOK_OFFSET, 8); -+ /* The user trying to poll for a result treats "dq" as const. It is -+ * however the same address that was provided to us non-const in the -+ * first place, for directing hardware DMA to. So we can cast away the -+ * const because it is mutable from our perspective. */ -+ uint32_t *p = qb_cl((struct dpaa2_dq *)dq); -+ uint32_t token; -+ -+ token = qb_attr_code_decode(&code_dqrr_tok_detect, &p[1]); -+ if (token != 1) -+ return 0; -+ qb_attr_code_encode(&code_dqrr_tok_detect, &p[1], 0); -+ -+ /* Only now do we convert from hardware to host endianness. Also, as we -+ * are returning success, the user has promised not to call us again, so -+ * there's no risk of us converting the endianness twice... */ -+ make_le32_n(p, 16); -+ -+ /* VDQCR "no longer busy" hook - not quite the same as DQRR, because the -+ * fact "VDQCR" shows busy doesn't mean that the result we're looking at -+ * is from the same command. Eg. we may be looking at our 10th dequeue -+ * result from our first VDQCR command, yet the second dequeue command -+ * could have been kicked off already, after seeing the 1st result. Ie. -+ * the result we're looking at is not necessarily proof that we can -+ * reset "busy". We instead base the decision on whether the current -+ * result is sitting at the first 'storage' location of the busy -+ * command. */ -+ if (s->vdq.storage == dq) { -+ s->vdq.storage = NULL; -+ atomic_inc(&s->vdq.busy); -+ } -+ return 1; -+} -+ -+/********************************/ -+/* Categorising qbman_result */ -+/********************************/ -+ -+static struct qb_attr_code code_result_in_mem = -+ QB_CODE(0, QBMAN_RESULT_VERB_OFFSET_IN_MEM, 7); -+ -+static inline int __qbman_result_is_x(const struct dpaa2_dq *dq, uint32_t x) -+{ -+ const uint32_t *p = qb_cl(dq); -+ uint32_t response_verb = qb_attr_code_decode(&code_dqrr_response, p); -+ -+ return response_verb == x; -+} -+ -+static inline int __qbman_result_is_x_in_mem(const struct dpaa2_dq *dq, -+ uint32_t x) -+{ -+ const uint32_t *p = qb_cl(dq); -+ uint32_t response_verb = qb_attr_code_decode(&code_result_in_mem, p); -+ -+ return (response_verb == x); -+} -+ -+int qbman_result_is_DQ(const struct dpaa2_dq *dq) -+{ -+ return __qbman_result_is_x(dq, QBMAN_RESULT_DQ); -+} -+ -+int qbman_result_is_FQDAN(const struct dpaa2_dq *dq) -+{ -+ return __qbman_result_is_x(dq, QBMAN_RESULT_FQDAN); -+} -+ -+int qbman_result_is_CDAN(const struct dpaa2_dq *dq) -+{ -+ return __qbman_result_is_x(dq, QBMAN_RESULT_CDAN); -+} -+ -+int qbman_result_is_CSCN(const struct dpaa2_dq *dq) -+{ -+ return __qbman_result_is_x_in_mem(dq, QBMAN_RESULT_CSCN_MEM) || -+ __qbman_result_is_x(dq, QBMAN_RESULT_CSCN_WQ); -+} -+ -+int qbman_result_is_BPSCN(const struct dpaa2_dq *dq) -+{ -+ return __qbman_result_is_x_in_mem(dq, QBMAN_RESULT_BPSCN); -+} -+ -+int qbman_result_is_CGCU(const struct dpaa2_dq *dq) -+{ -+ return __qbman_result_is_x_in_mem(dq, QBMAN_RESULT_CGCU); -+} -+ -+int qbman_result_is_FQRN(const struct dpaa2_dq *dq) -+{ -+ return __qbman_result_is_x_in_mem(dq, QBMAN_RESULT_FQRN); -+} -+ -+int qbman_result_is_FQRNI(const struct dpaa2_dq *dq) -+{ -+ return __qbman_result_is_x_in_mem(dq, QBMAN_RESULT_FQRNI); -+} -+ -+int qbman_result_is_FQPN(const struct dpaa2_dq *dq) -+{ -+ return __qbman_result_is_x(dq, QBMAN_RESULT_FQPN); -+} -+ -+/*********************************/ -+/* Parsing frame dequeue results */ -+/*********************************/ -+ -+/* These APIs assume qbman_result_is_DQ() is TRUE */ -+ -+uint32_t dpaa2_dq_flags(const struct dpaa2_dq *dq) -+{ -+ const uint32_t *p = qb_cl(dq); -+ -+ return qb_attr_code_decode(&code_dqrr_stat, p); -+} -+ -+uint16_t dpaa2_dq_seqnum(const struct dpaa2_dq *dq) -+{ -+ const uint32_t *p = qb_cl(dq); -+ -+ return (uint16_t)qb_attr_code_decode(&code_dqrr_seqnum, p); -+} -+ -+uint16_t dpaa2_dq_odpid(const struct dpaa2_dq *dq) -+{ -+ const uint32_t *p = qb_cl(dq); -+ -+ return (uint16_t)qb_attr_code_decode(&code_dqrr_odpid, p); -+} -+ -+uint32_t dpaa2_dq_fqid(const struct dpaa2_dq *dq) -+{ -+ const uint32_t *p = qb_cl(dq); -+ -+ return qb_attr_code_decode(&code_dqrr_fqid, p); -+} -+ -+uint32_t dpaa2_dq_byte_count(const struct dpaa2_dq *dq) -+{ -+ const uint32_t *p = qb_cl(dq); -+ -+ return qb_attr_code_decode(&code_dqrr_byte_count, p); -+} -+ -+uint32_t dpaa2_dq_frame_count(const struct dpaa2_dq *dq) -+{ -+ const uint32_t *p = qb_cl(dq); -+ -+ return qb_attr_code_decode(&code_dqrr_frame_count, p); -+} -+ -+uint64_t dpaa2_dq_fqd_ctx(const struct dpaa2_dq *dq) -+{ -+ const uint64_t *p = (uint64_t *)qb_cl(dq); -+ -+ return qb_attr_code_decode_64(&code_dqrr_ctx_lo, p); -+} -+EXPORT_SYMBOL(dpaa2_dq_fqd_ctx); -+ -+const struct dpaa2_fd *dpaa2_dq_fd(const struct dpaa2_dq *dq) -+{ -+ const uint32_t *p = qb_cl(dq); -+ -+ return (const struct dpaa2_fd *)&p[8]; -+} -+EXPORT_SYMBOL(dpaa2_dq_fd); -+ -+/**************************************/ -+/* Parsing state-change notifications */ -+/**************************************/ -+ -+static struct qb_attr_code code_scn_state = QB_CODE(0, 16, 8); -+static struct qb_attr_code code_scn_rid = QB_CODE(1, 0, 24); -+static struct qb_attr_code code_scn_state_in_mem = -+ QB_CODE(0, SCN_STATE_OFFSET_IN_MEM, 8); -+static struct qb_attr_code code_scn_rid_in_mem = -+ QB_CODE(1, SCN_RID_OFFSET_IN_MEM, 24); -+static struct qb_attr_code code_scn_ctx_lo = QB_CODE(2, 0, 32); -+ -+uint8_t qbman_result_SCN_state(const struct dpaa2_dq *scn) -+{ -+ const uint32_t *p = qb_cl(scn); -+ -+ return (uint8_t)qb_attr_code_decode(&code_scn_state, p); -+} -+ -+uint32_t qbman_result_SCN_rid(const struct dpaa2_dq *scn) -+{ -+ const uint32_t *p = qb_cl(scn); -+ -+ return qb_attr_code_decode(&code_scn_rid, p); -+} -+ -+uint64_t qbman_result_SCN_ctx(const struct dpaa2_dq *scn) -+{ -+ const uint64_t *p = (uint64_t *)qb_cl(scn); -+ -+ return qb_attr_code_decode_64(&code_scn_ctx_lo, p); -+} -+ -+uint8_t qbman_result_SCN_state_in_mem(const struct dpaa2_dq *scn) -+{ -+ const uint32_t *p = qb_cl(scn); -+ -+ return (uint8_t)qb_attr_code_decode(&code_scn_state_in_mem, p); -+} -+ -+uint32_t qbman_result_SCN_rid_in_mem(const struct dpaa2_dq *scn) -+{ -+ const uint32_t *p = qb_cl(scn); -+ uint32_t result_rid; -+ -+ result_rid = qb_attr_code_decode(&code_scn_rid_in_mem, p); -+ return make_le24(result_rid); -+} -+ -+/*****************/ -+/* Parsing BPSCN */ -+/*****************/ -+uint16_t qbman_result_bpscn_bpid(const struct dpaa2_dq *scn) -+{ -+ return (uint16_t)qbman_result_SCN_rid_in_mem(scn) & 0x3FFF; -+} -+ -+int qbman_result_bpscn_has_free_bufs(const struct dpaa2_dq *scn) -+{ -+ return !(int)(qbman_result_SCN_state_in_mem(scn) & 0x1); -+} -+ -+int qbman_result_bpscn_is_depleted(const struct dpaa2_dq *scn) -+{ -+ return (int)(qbman_result_SCN_state_in_mem(scn) & 0x2); -+} -+ -+int qbman_result_bpscn_is_surplus(const struct dpaa2_dq *scn) -+{ -+ return (int)(qbman_result_SCN_state_in_mem(scn) & 0x4); -+} -+ -+uint64_t qbman_result_bpscn_ctx(const struct dpaa2_dq *scn) -+{ -+ return qbman_result_SCN_ctx(scn); -+} -+ -+/*****************/ -+/* Parsing CGCU */ -+/*****************/ -+uint16_t qbman_result_cgcu_cgid(const struct dpaa2_dq *scn) -+{ -+ return (uint16_t)qbman_result_SCN_rid_in_mem(scn) & 0xFFFF; -+} -+ -+uint64_t qbman_result_cgcu_icnt(const struct dpaa2_dq *scn) -+{ -+ return qbman_result_SCN_ctx(scn) & 0xFFFFFFFFFF; -+} -+ -+/******************/ -+/* Buffer release */ -+/******************/ -+ -+/* These should be const, eventually */ -+/* static struct qb_attr_code code_release_num = QB_CODE(0, 0, 3); */ -+static struct qb_attr_code code_release_set_me = QB_CODE(0, 5, 1); -+static struct qb_attr_code code_release_rcdi = QB_CODE(0, 6, 1); -+static struct qb_attr_code code_release_bpid = QB_CODE(0, 16, 16); -+ -+void qbman_release_desc_clear(struct qbman_release_desc *d) -+{ -+ uint32_t *cl; -+ -+ memset(d, 0, sizeof(*d)); -+ cl = qb_cl(d); -+ qb_attr_code_encode(&code_release_set_me, cl, 1); -+} -+ -+void qbman_release_desc_set_bpid(struct qbman_release_desc *d, uint32_t bpid) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ qb_attr_code_encode(&code_release_bpid, cl, bpid); -+} -+ -+void qbman_release_desc_set_rcdi(struct qbman_release_desc *d, int enable) -+{ -+ uint32_t *cl = qb_cl(d); -+ -+ qb_attr_code_encode(&code_release_rcdi, cl, !!enable); -+} -+ -+#define RAR_IDX(rar) ((rar) & 0x7) -+#define RAR_VB(rar) ((rar) & 0x80) -+#define RAR_SUCCESS(rar) ((rar) & 0x100) -+ -+int qbman_swp_release(struct qbman_swp *s, const struct qbman_release_desc *d, -+ const uint64_t *buffers, unsigned int num_buffers) -+{ -+ uint32_t *p; -+ const uint32_t *cl = qb_cl(d); -+ uint32_t rar = qbman_cinh_read(&s->sys, QBMAN_CINH_SWP_RAR); -+ -+ pr_debug("RAR=%08x\n", rar); -+ if (!RAR_SUCCESS(rar)) -+ return -EBUSY; -+ BUG_ON(!num_buffers || (num_buffers > 7)); -+ /* Start the release command */ -+ p = qbman_cena_write_start(&s->sys, -+ QBMAN_CENA_SWP_RCR(RAR_IDX(rar))); -+ /* Copy the caller's buffer pointers to the command */ -+ u64_to_le32_copy(&p[2], buffers, num_buffers); -+ /* Set the verb byte, have to substitute in the valid-bit and the number -+ * of buffers. */ -+ p[0] = cl[0] | RAR_VB(rar) | num_buffers; -+ qbman_cena_write_complete(&s->sys, -+ QBMAN_CENA_SWP_RCR(RAR_IDX(rar)), -+ p); -+ return 0; -+} -+ -+/*******************/ -+/* Buffer acquires */ -+/*******************/ -+ -+/* These should be const, eventually */ -+static struct qb_attr_code code_acquire_bpid = QB_CODE(0, 16, 16); -+static struct qb_attr_code code_acquire_num = QB_CODE(1, 0, 3); -+static struct qb_attr_code code_acquire_r_num = QB_CODE(1, 0, 3); -+ -+int qbman_swp_acquire(struct qbman_swp *s, uint32_t bpid, uint64_t *buffers, -+ unsigned int num_buffers) -+{ -+ uint32_t *p; -+ uint32_t verb, rslt, num; -+ -+ BUG_ON(!num_buffers || (num_buffers > 7)); -+ -+ /* Start the management command */ -+ p = qbman_swp_mc_start(s); -+ -+ if (!p) -+ return -EBUSY; -+ -+ /* Encode the caller-provided attributes */ -+ qb_attr_code_encode(&code_acquire_bpid, p, bpid); -+ qb_attr_code_encode(&code_acquire_num, p, num_buffers); -+ -+ /* Complete the management command */ -+ p = qbman_swp_mc_complete(s, p, p[0] | QBMAN_MC_ACQUIRE); -+ -+ /* Decode the outcome */ -+ verb = qb_attr_code_decode(&code_generic_verb, p); -+ rslt = qb_attr_code_decode(&code_generic_rslt, p); -+ num = qb_attr_code_decode(&code_acquire_r_num, p); -+ BUG_ON(verb != QBMAN_MC_ACQUIRE); -+ -+ /* Determine success or failure */ -+ if (unlikely(rslt != QBMAN_MC_RSLT_OK)) { -+ pr_err("Acquire buffers from BPID 0x%x failed, code=0x%02x\n", -+ bpid, rslt); -+ return -EIO; -+ } -+ BUG_ON(num > num_buffers); -+ /* Copy the acquired buffers to the caller's array */ -+ u64_from_le32_copy(buffers, &p[2], num); -+ return (int)num; -+} -+ -+/*****************/ -+/* FQ management */ -+/*****************/ -+ -+static struct qb_attr_code code_fqalt_fqid = QB_CODE(1, 0, 32); -+ -+static int qbman_swp_alt_fq_state(struct qbman_swp *s, uint32_t fqid, -+ uint8_t alt_fq_verb) -+{ -+ uint32_t *p; -+ uint32_t verb, rslt; -+ -+ /* Start the management command */ -+ p = qbman_swp_mc_start(s); -+ if (!p) -+ return -EBUSY; -+ -+ qb_attr_code_encode(&code_fqalt_fqid, p, fqid); -+ /* Complete the management command */ -+ p = qbman_swp_mc_complete(s, p, p[0] | alt_fq_verb); -+ -+ /* Decode the outcome */ -+ verb = qb_attr_code_decode(&code_generic_verb, p); -+ rslt = qb_attr_code_decode(&code_generic_rslt, p); -+ BUG_ON(verb != alt_fq_verb); -+ -+ /* Determine success or failure */ -+ if (unlikely(rslt != QBMAN_MC_RSLT_OK)) { -+ pr_err("ALT FQID %d failed: verb = 0x%08x, code = 0x%02x\n", -+ fqid, alt_fq_verb, rslt); -+ return -EIO; -+ } -+ -+ return 0; -+} -+ -+int qbman_swp_fq_schedule(struct qbman_swp *s, uint32_t fqid) -+{ -+ return qbman_swp_alt_fq_state(s, fqid, QBMAN_FQ_SCHEDULE); -+} -+ -+int qbman_swp_fq_force(struct qbman_swp *s, uint32_t fqid) -+{ -+ return qbman_swp_alt_fq_state(s, fqid, QBMAN_FQ_FORCE); -+} -+ -+int qbman_swp_fq_xon(struct qbman_swp *s, uint32_t fqid) -+{ -+ return qbman_swp_alt_fq_state(s, fqid, QBMAN_FQ_XON); -+} -+ -+int qbman_swp_fq_xoff(struct qbman_swp *s, uint32_t fqid) -+{ -+ return qbman_swp_alt_fq_state(s, fqid, QBMAN_FQ_XOFF); -+} -+ -+/**********************/ -+/* Channel management */ -+/**********************/ -+ -+static struct qb_attr_code code_cdan_cid = QB_CODE(0, 16, 12); -+static struct qb_attr_code code_cdan_we = QB_CODE(1, 0, 8); -+static struct qb_attr_code code_cdan_en = QB_CODE(1, 8, 1); -+static struct qb_attr_code code_cdan_ctx_lo = QB_CODE(2, 0, 32); -+ -+/* Hide "ICD" for now as we don't use it, don't set it, and don't test it, so it -+ * would be irresponsible to expose it. */ -+#define CODE_CDAN_WE_EN 0x1 -+#define CODE_CDAN_WE_CTX 0x4 -+ -+static int qbman_swp_CDAN_set(struct qbman_swp *s, uint16_t channelid, -+ uint8_t we_mask, uint8_t cdan_en, -+ uint64_t ctx) -+{ -+ uint32_t *p; -+ uint32_t verb, rslt; -+ -+ /* Start the management command */ -+ p = qbman_swp_mc_start(s); -+ if (!p) -+ return -EBUSY; -+ -+ /* Encode the caller-provided attributes */ -+ qb_attr_code_encode(&code_cdan_cid, p, channelid); -+ qb_attr_code_encode(&code_cdan_we, p, we_mask); -+ qb_attr_code_encode(&code_cdan_en, p, cdan_en); -+ qb_attr_code_encode_64(&code_cdan_ctx_lo, (uint64_t *)p, ctx); -+ /* Complete the management command */ -+ p = qbman_swp_mc_complete(s, p, p[0] | QBMAN_WQCHAN_CONFIGURE); -+ -+ /* Decode the outcome */ -+ verb = qb_attr_code_decode(&code_generic_verb, p); -+ rslt = qb_attr_code_decode(&code_generic_rslt, p); -+ BUG_ON(verb != QBMAN_WQCHAN_CONFIGURE); -+ -+ /* Determine success or failure */ -+ if (unlikely(rslt != QBMAN_MC_RSLT_OK)) { -+ pr_err("CDAN cQID %d failed: code = 0x%02x\n", -+ channelid, rslt); -+ return -EIO; -+ } -+ -+ return 0; -+} -+ -+int qbman_swp_CDAN_set_context(struct qbman_swp *s, uint16_t channelid, -+ uint64_t ctx) -+{ -+ return qbman_swp_CDAN_set(s, channelid, -+ CODE_CDAN_WE_CTX, -+ 0, ctx); -+} -+ -+int qbman_swp_CDAN_enable(struct qbman_swp *s, uint16_t channelid) -+{ -+ return qbman_swp_CDAN_set(s, channelid, -+ CODE_CDAN_WE_EN, -+ 1, 0); -+} -+int qbman_swp_CDAN_disable(struct qbman_swp *s, uint16_t channelid) -+{ -+ return qbman_swp_CDAN_set(s, channelid, -+ CODE_CDAN_WE_EN, -+ 0, 0); -+} -+int qbman_swp_CDAN_set_context_enable(struct qbman_swp *s, uint16_t channelid, -+ uint64_t ctx) -+{ -+ return qbman_swp_CDAN_set(s, channelid, -+ CODE_CDAN_WE_EN | CODE_CDAN_WE_CTX, -+ 1, ctx); -+} -diff --git a/drivers/staging/fsl-mc/bus/dpio/qbman_portal.h b/drivers/staging/fsl-mc/bus/dpio/qbman_portal.h -new file mode 100644 -index 0000000..65ebf3f ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpio/qbman_portal.h -@@ -0,0 +1,261 @@ -+/* Copyright (C) 2014 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 "qbman_private.h" -+#include "fsl_qbman_portal.h" -+#include "../../include/fsl_dpaa2_fd.h" -+ -+/* All QBMan command and result structures use this "valid bit" encoding */ -+#define QB_VALID_BIT ((uint32_t)0x80) -+ -+/* Management command result codes */ -+#define QBMAN_MC_RSLT_OK 0xf0 -+ -+/* TBD: as of QBMan 4.1, DQRR will be 8 rather than 4! */ -+#define QBMAN_DQRR_SIZE 4 -+ -+/* DQRR valid-bit reset bug. See qbman_portal.c::qbman_swp_init(). */ -+#define WORKAROUND_DQRR_RESET_BUG -+ -+/* --------------------- */ -+/* portal data structure */ -+/* --------------------- */ -+ -+struct qbman_swp { -+ const struct qbman_swp_desc *desc; -+ /* The qbman_sys (ie. arch/OS-specific) support code can put anything it -+ * needs in here. */ -+ struct qbman_swp_sys sys; -+ /* Management commands */ -+ struct { -+#ifdef QBMAN_CHECKING -+ enum swp_mc_check { -+ swp_mc_can_start, /* call __qbman_swp_mc_start() */ -+ swp_mc_can_submit, /* call __qbman_swp_mc_submit() */ -+ swp_mc_can_poll, /* call __qbman_swp_mc_result() */ -+ } check; -+#endif -+ uint32_t valid_bit; /* 0x00 or 0x80 */ -+ } mc; -+ /* Push dequeues */ -+ uint32_t sdq; -+ /* Volatile dequeues */ -+ struct { -+ /* VDQCR supports a "1 deep pipeline", meaning that if you know -+ * the last-submitted command is already executing in the -+ * hardware (as evidenced by at least 1 valid dequeue result), -+ * you can write another dequeue command to the register, the -+ * hardware will start executing it as soon as the -+ * already-executing command terminates. (This minimises latency -+ * and stalls.) With that in mind, this "busy" variable refers -+ * to whether or not a command can be submitted, not whether or -+ * not a previously-submitted command is still executing. In -+ * other words, once proof is seen that the previously-submitted -+ * command is executing, "vdq" is no longer "busy". -+ */ -+ atomic_t busy; -+ uint32_t valid_bit; /* 0x00 or 0x80 */ -+ /* We need to determine when vdq is no longer busy. This depends -+ * on whether the "busy" (last-submitted) dequeue command is -+ * targeting DQRR or main-memory, and detected is based on the -+ * presence of the dequeue command's "token" showing up in -+ * dequeue entries in DQRR or main-memory (respectively). */ -+ struct dpaa2_dq *storage; /* NULL if DQRR */ -+ } vdq; -+ /* DQRR */ -+ struct { -+ uint32_t next_idx; -+ uint32_t valid_bit; -+ uint8_t dqrr_size; -+#ifdef WORKAROUND_DQRR_RESET_BUG -+ int reset_bug; -+#endif -+ } dqrr; -+}; -+ -+/* -------------------------- */ -+/* portal management commands */ -+/* -------------------------- */ -+ -+/* Different management commands all use this common base layer of code to issue -+ * commands and poll for results. The first function returns a pointer to where -+ * the caller should fill in their MC command (though they should ignore the -+ * verb byte), the second function commits merges in the caller-supplied command -+ * verb (which should not include the valid-bit) and submits the command to -+ * hardware, and the third function checks for a completed response (returns -+ * non-NULL if only if the response is complete). */ -+void *qbman_swp_mc_start(struct qbman_swp *p); -+void qbman_swp_mc_submit(struct qbman_swp *p, void *cmd, uint32_t cmd_verb); -+void *qbman_swp_mc_result(struct qbman_swp *p); -+ -+/* Wraps up submit + poll-for-result */ -+static inline void *qbman_swp_mc_complete(struct qbman_swp *swp, void *cmd, -+ uint32_t cmd_verb) -+{ -+ int loopvar; -+ -+ qbman_swp_mc_submit(swp, cmd, cmd_verb); -+ DBG_POLL_START(loopvar); -+ do { -+ DBG_POLL_CHECK(loopvar); -+ cmd = qbman_swp_mc_result(swp); -+ } while (!cmd); -+ return cmd; -+} -+ -+/* ------------ */ -+/* qb_attr_code */ -+/* ------------ */ -+ -+/* This struct locates a sub-field within a QBMan portal (CENA) cacheline which -+ * is either serving as a configuration command or a query result. The -+ * representation is inherently little-endian, as the indexing of the words is -+ * itself little-endian in nature and layerscape is little endian for anything -+ * that crosses a word boundary too (64-bit fields are the obvious examples). -+ */ -+struct qb_attr_code { -+ unsigned int word; /* which uint32_t[] array member encodes the field */ -+ unsigned int lsoffset; /* encoding offset from ls-bit */ -+ unsigned int width; /* encoding width. (bool must be 1.) */ -+}; -+ -+/* Some pre-defined codes */ -+extern struct qb_attr_code code_generic_verb; -+extern struct qb_attr_code code_generic_rslt; -+ -+/* Macros to define codes */ -+#define QB_CODE(a, b, c) { a, b, c} -+#define QB_CODE_NULL \ -+ QB_CODE((unsigned int)-1, (unsigned int)-1, (unsigned int)-1) -+ -+/* Rotate a code "ms", meaning that it moves from less-significant bytes to -+ * more-significant, from less-significant words to more-significant, etc. The -+ * "ls" version does the inverse, from more-significant towards -+ * less-significant. -+ */ -+static inline void qb_attr_code_rotate_ms(struct qb_attr_code *code, -+ unsigned int bits) -+{ -+ code->lsoffset += bits; -+ while (code->lsoffset > 31) { -+ code->word++; -+ code->lsoffset -= 32; -+ } -+} -+static inline void qb_attr_code_rotate_ls(struct qb_attr_code *code, -+ unsigned int bits) -+{ -+ /* Don't be fooled, this trick should work because the types are -+ * unsigned. So the case that interests the while loop (the rotate has -+ * gone too far and the word count needs to compensate for it), is -+ * manifested when lsoffset is negative. But that equates to a really -+ * large unsigned value, starting with lots of "F"s. As such, we can -+ * continue adding 32 back to it until it wraps back round above zero, -+ * to a value of 31 or less... -+ */ -+ code->lsoffset -= bits; -+ while (code->lsoffset > 31) { -+ code->word--; -+ code->lsoffset += 32; -+ } -+} -+/* Implement a loop of code rotations until 'expr' evaluates to FALSE (0). */ -+#define qb_attr_code_for_ms(code, bits, expr) \ -+ for (; expr; qb_attr_code_rotate_ms(code, bits)) -+#define qb_attr_code_for_ls(code, bits, expr) \ -+ for (; expr; qb_attr_code_rotate_ls(code, bits)) -+ -+/* decode a field from a cacheline */ -+static inline uint32_t qb_attr_code_decode(const struct qb_attr_code *code, -+ const uint32_t *cacheline) -+{ -+ return d32_uint32_t(code->lsoffset, code->width, cacheline[code->word]); -+} -+static inline uint64_t qb_attr_code_decode_64(const struct qb_attr_code *code, -+ const uint64_t *cacheline) -+{ -+ uint64_t res; -+ u64_from_le32_copy(&res, &cacheline[code->word/2], 1); -+ return res; -+} -+ -+/* encode a field to a cacheline */ -+static inline void qb_attr_code_encode(const struct qb_attr_code *code, -+ uint32_t *cacheline, uint32_t val) -+{ -+ cacheline[code->word] = -+ r32_uint32_t(code->lsoffset, code->width, cacheline[code->word]) -+ | e32_uint32_t(code->lsoffset, code->width, val); -+} -+static inline void qb_attr_code_encode_64(const struct qb_attr_code *code, -+ uint64_t *cacheline, uint64_t val) -+{ -+ u64_to_le32_copy(&cacheline[code->word/2], &val, 1); -+} -+ -+/* Small-width signed values (two's-complement) will decode into medium-width -+ * positives. (Eg. for an 8-bit signed field, which stores values from -128 to -+ * +127, a setting of -7 would appear to decode to the 32-bit unsigned value -+ * 249. Likewise -120 would decode as 136.) This function allows the caller to -+ * "re-sign" such fields to 32-bit signed. (Eg. -7, which was 249 with an 8-bit -+ * encoding, will become 0xfffffff9 if you cast the return value to uint32_t). -+ */ -+static inline int32_t qb_attr_code_makesigned(const struct qb_attr_code *code, -+ uint32_t val) -+{ -+ BUG_ON(val >= (1 << code->width)); -+ /* If the high bit was set, it was encoding a negative */ -+ if (val >= (1 << (code->width - 1))) -+ return (int32_t)0 - (int32_t)(((uint32_t)1 << code->width) - -+ val); -+ /* Otherwise, it was encoding a positive */ -+ return (int32_t)val; -+} -+ -+/* ---------------------- */ -+/* Descriptors/cachelines */ -+/* ---------------------- */ -+ -+/* To avoid needless dynamic allocation, the driver API often gives the caller -+ * a "descriptor" type that the caller can instantiate however they like. -+ * Ultimately though, it is just a cacheline of binary storage (or something -+ * smaller when it is known that the descriptor doesn't need all 64 bytes) for -+ * holding pre-formatted pieces of hardware commands. The performance-critical -+ * code can then copy these descriptors directly into hardware command -+ * registers more efficiently than trying to construct/format commands -+ * on-the-fly. The API user sees the descriptor as an array of 32-bit words in -+ * order for the compiler to know its size, but the internal details are not -+ * exposed. The following macro is used within the driver for converting *any* -+ * descriptor pointer to a usable array pointer. The use of a macro (instead of -+ * an inline) is necessary to work with different descriptor types and to work -+ * correctly with const and non-const inputs (and similarly-qualified outputs). -+ */ -+#define qb_cl(d) (&(d)->dont_manipulate_directly[0]) -diff --git a/drivers/staging/fsl-mc/bus/dpio/qbman_private.h b/drivers/staging/fsl-mc/bus/dpio/qbman_private.h -new file mode 100644 -index 0000000..e376b80 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpio/qbman_private.h -@@ -0,0 +1,173 @@ -+/* Copyright (C) 2014 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. -+*/ -+ -+/* Perform extra checking */ -+#define QBMAN_CHECKING -+ -+/* To maximise the amount of logic that is common between the Linux driver and -+ * other targets (such as the embedded MC firmware), we pivot here between the -+ * inclusion of two platform-specific headers. -+ * -+ * The first, qbman_sys_decl.h, includes any and all required system headers as -+ * well as providing any definitions for the purposes of compatibility. The -+ * second, qbman_sys.h, is where platform-specific routines go. -+ * -+ * The point of the split is that the platform-independent code (including this -+ * header) may depend on platform-specific declarations, yet other -+ * platform-specific routines may depend on platform-independent definitions. -+ */ -+ -+#include "qbman_sys_decl.h" -+ -+#define QMAN_REV_4000 0x04000000 -+#define QMAN_REV_4100 0x04010000 -+#define QMAN_REV_4101 0x04010001 -+ -+/* When things go wrong, it is a convenient trick to insert a few FOO() -+ * statements in the code to trace progress. TODO: remove this once we are -+ * hacking the code less actively. -+ */ -+#define FOO() fsl_os_print("FOO: %s:%d\n", __FILE__, __LINE__) -+ -+/* Any time there is a register interface which we poll on, this provides a -+ * "break after x iterations" scheme for it. It's handy for debugging, eg. -+ * where you don't want millions of lines of log output from a polling loop -+ * that won't, because such things tend to drown out the earlier log output -+ * that might explain what caused the problem. (NB: put ";" after each macro!) -+ * TODO: we should probably remove this once we're done sanitising the -+ * simulator... -+ */ -+#define DBG_POLL_START(loopvar) (loopvar = 10) -+#define DBG_POLL_CHECK(loopvar) \ -+ do {if (!(loopvar--)) BUG_ON(1); } while (0) -+ -+/* For CCSR or portal-CINH registers that contain fields at arbitrary offsets -+ * and widths, these macro-generated encode/decode/isolate/remove inlines can -+ * be used. -+ * -+ * Eg. to "d"ecode a 14-bit field out of a register (into a "uint16_t" type), -+ * where the field is located 3 bits "up" from the least-significant bit of the -+ * register (ie. the field location within the 32-bit register corresponds to a -+ * mask of 0x0001fff8), you would do; -+ * uint16_t field = d32_uint16_t(3, 14, reg_value); -+ * -+ * Or to "e"ncode a 1-bit boolean value (input type is "int", zero is FALSE, -+ * non-zero is TRUE, so must convert all non-zero inputs to 1, hence the "!!" -+ * operator) into a register at bit location 0x00080000 (19 bits "in" from the -+ * LS bit), do; -+ * reg_value |= e32_int(19, 1, !!field); -+ * -+ * If you wish to read-modify-write a register, such that you leave the 14-bit -+ * field as-is but have all other fields set to zero, then "i"solate the 14-bit -+ * value using; -+ * reg_value = i32_uint16_t(3, 14, reg_value); -+ * -+ * Alternatively, you could "r"emove the 1-bit boolean field (setting it to -+ * zero) but leaving all other fields as-is; -+ * reg_val = r32_int(19, 1, reg_value); -+ * -+ */ -+#define MAKE_MASK32(width) (width == 32 ? 0xffffffff : \ -+ (uint32_t)((1 << width) - 1)) -+#define DECLARE_CODEC32(t) \ -+static inline uint32_t e32_##t(uint32_t lsoffset, uint32_t width, t val) \ -+{ \ -+ BUG_ON(width > (sizeof(t) * 8)); \ -+ return ((uint32_t)val & MAKE_MASK32(width)) << lsoffset; \ -+} \ -+static inline t d32_##t(uint32_t lsoffset, uint32_t width, uint32_t val) \ -+{ \ -+ BUG_ON(width > (sizeof(t) * 8)); \ -+ return (t)((val >> lsoffset) & MAKE_MASK32(width)); \ -+} \ -+static inline uint32_t i32_##t(uint32_t lsoffset, uint32_t width, \ -+ uint32_t val) \ -+{ \ -+ BUG_ON(width > (sizeof(t) * 8)); \ -+ return e32_##t(lsoffset, width, d32_##t(lsoffset, width, val)); \ -+} \ -+static inline uint32_t r32_##t(uint32_t lsoffset, uint32_t width, \ -+ uint32_t val) \ -+{ \ -+ BUG_ON(width > (sizeof(t) * 8)); \ -+ return ~(MAKE_MASK32(width) << lsoffset) & val; \ -+} -+DECLARE_CODEC32(uint32_t) -+DECLARE_CODEC32(uint16_t) -+DECLARE_CODEC32(uint8_t) -+DECLARE_CODEC32(int) -+ -+ /*********************/ -+ /* Debugging assists */ -+ /*********************/ -+ -+static inline void __hexdump(unsigned long start, unsigned long end, -+ unsigned long p, size_t sz, const unsigned char *c) -+{ -+ while (start < end) { -+ unsigned int pos = 0; -+ char buf[64]; -+ int nl = 0; -+ -+ pos += sprintf(buf + pos, "%08lx: ", start); -+ do { -+ if ((start < p) || (start >= (p + sz))) -+ pos += sprintf(buf + pos, ".."); -+ else -+ pos += sprintf(buf + pos, "%02x", *(c++)); -+ if (!(++start & 15)) { -+ buf[pos++] = '\n'; -+ nl = 1; -+ } else { -+ nl = 0; -+ if (!(start & 1)) -+ buf[pos++] = ' '; -+ if (!(start & 3)) -+ buf[pos++] = ' '; -+ } -+ } while (start & 15); -+ if (!nl) -+ buf[pos++] = '\n'; -+ buf[pos] = '\0'; -+ pr_info("%s", buf); -+ } -+} -+static inline void hexdump(const void *ptr, size_t sz) -+{ -+ unsigned long p = (unsigned long)ptr; -+ unsigned long start = p & ~(unsigned long)15; -+ unsigned long end = (p + sz + 15) & ~(unsigned long)15; -+ const unsigned char *c = ptr; -+ -+ __hexdump(start, end, p, sz, c); -+} -+ -+#include "qbman_sys.h" -diff --git a/drivers/staging/fsl-mc/bus/dpio/qbman_sys.h b/drivers/staging/fsl-mc/bus/dpio/qbman_sys.h -new file mode 100644 -index 0000000..4849212 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpio/qbman_sys.h -@@ -0,0 +1,307 @@ -+/* Copyright (C) 2014 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. -+ */ -+/* qbman_sys_decl.h and qbman_sys.h are the two platform-specific files in the -+ * driver. They are only included via qbman_private.h, which is itself a -+ * platform-independent file and is included by all the other driver source. -+ * -+ * qbman_sys_decl.h is included prior to all other declarations and logic, and -+ * it exists to provide compatibility with any linux interfaces our -+ * single-source driver code is dependent on (eg. kmalloc). Ie. this file -+ * provides linux compatibility. -+ * -+ * This qbman_sys.h header, on the other hand, is included *after* any common -+ * and platform-neutral declarations and logic in qbman_private.h, and exists to -+ * implement any platform-specific logic of the qbman driver itself. Ie. it is -+ * *not* to provide linux compatibility. -+ */ -+ -+/* Trace the 3 different classes of read/write access to QBMan. #undef as -+ * required. */ -+#undef QBMAN_CCSR_TRACE -+#undef QBMAN_CINH_TRACE -+#undef QBMAN_CENA_TRACE -+ -+static inline void word_copy(void *d, const void *s, unsigned int cnt) -+{ -+ uint32_t *dd = d; -+ const uint32_t *ss = s; -+ -+ while (cnt--) -+ *(dd++) = *(ss++); -+} -+ -+/* Currently, the CENA support code expects each 32-bit word to be written in -+ * host order, and these are converted to hardware (little-endian) order on -+ * command submission. However, 64-bit quantities are must be written (and read) -+ * as two 32-bit words with the least-significant word first, irrespective of -+ * host endianness. */ -+static inline void u64_to_le32_copy(void *d, const uint64_t *s, -+ unsigned int cnt) -+{ -+ uint32_t *dd = d; -+ const uint32_t *ss = (const uint32_t *)s; -+ -+ while (cnt--) { -+ /* TBD: the toolchain was choking on the use of 64-bit types up -+ * until recently so this works entirely with 32-bit variables. -+ * When 64-bit types become usable again, investigate better -+ * ways of doing this. */ -+#if defined(__BIG_ENDIAN) -+ *(dd++) = ss[1]; -+ *(dd++) = ss[0]; -+ ss += 2; -+#else -+ *(dd++) = *(ss++); -+ *(dd++) = *(ss++); -+#endif -+ } -+} -+static inline void u64_from_le32_copy(uint64_t *d, const void *s, -+ unsigned int cnt) -+{ -+ const uint32_t *ss = s; -+ uint32_t *dd = (uint32_t *)d; -+ -+ while (cnt--) { -+#if defined(__BIG_ENDIAN) -+ dd[1] = *(ss++); -+ dd[0] = *(ss++); -+ dd += 2; -+#else -+ *(dd++) = *(ss++); -+ *(dd++) = *(ss++); -+#endif -+ } -+} -+ -+/* Convert a host-native 32bit value into little endian */ -+#if defined(__BIG_ENDIAN) -+static inline uint32_t make_le32(uint32_t val) -+{ -+ return ((val & 0xff) << 24) | ((val & 0xff00) << 8) | -+ ((val & 0xff0000) >> 8) | ((val & 0xff000000) >> 24); -+} -+static inline uint32_t make_le24(uint32_t val) -+{ -+ return (((val & 0xff) << 16) | (val & 0xff00) | -+ ((val & 0xff0000) >> 16)); -+} -+#else -+#define make_le32(val) (val) -+#define make_le24(val) (val) -+#endif -+static inline void make_le32_n(uint32_t *val, unsigned int num) -+{ -+ while (num--) { -+ *val = make_le32(*val); -+ val++; -+ } -+} -+ -+ /******************/ -+ /* Portal access */ -+ /******************/ -+struct qbman_swp_sys { -+ /* On GPP, the sys support for qbman_swp is here. The CENA region isi -+ * not an mmap() of the real portal registers, but an allocated -+ * place-holder, because the actual writes/reads to/from the portal are -+ * marshalled from these allocated areas using QBMan's "MC access -+ * registers". CINH accesses are atomic so there's no need for a -+ * place-holder. */ -+ void *cena; -+ void __iomem *addr_cena; -+ void __iomem *addr_cinh; -+}; -+ -+/* P_OFFSET is (ACCESS_CMD,0,12) - offset within the portal -+ * C is (ACCESS_CMD,12,1) - is inhibited? (0==CENA, 1==CINH) -+ * SWP_IDX is (ACCESS_CMD,16,10) - Software portal index -+ * P is (ACCESS_CMD,28,1) - (0==special portal, 1==any portal) -+ * T is (ACCESS_CMD,29,1) - Command type (0==READ, 1==WRITE) -+ * E is (ACCESS_CMD,31,1) - Command execute (1 to issue, poll for 0==complete) -+ */ -+ -+static inline void qbman_cinh_write(struct qbman_swp_sys *s, uint32_t offset, -+ uint32_t val) -+{ -+ -+ writel_relaxed(val, s->addr_cinh + offset); -+#ifdef QBMAN_CINH_TRACE -+ pr_info("qbman_cinh_write(%p:0x%03x) 0x%08x\n", -+ s->addr_cinh, offset, val); -+#endif -+} -+ -+static inline uint32_t qbman_cinh_read(struct qbman_swp_sys *s, uint32_t offset) -+{ -+ uint32_t reg = readl_relaxed(s->addr_cinh + offset); -+ -+#ifdef QBMAN_CINH_TRACE -+ pr_info("qbman_cinh_read(%p:0x%03x) 0x%08x\n", -+ s->addr_cinh, offset, reg); -+#endif -+ return reg; -+} -+ -+static inline void *qbman_cena_write_start(struct qbman_swp_sys *s, -+ uint32_t offset) -+{ -+ void *shadow = s->cena + offset; -+ -+#ifdef QBMAN_CENA_TRACE -+ pr_info("qbman_cena_write_start(%p:0x%03x) %p\n", -+ s->addr_cena, offset, shadow); -+#endif -+ BUG_ON(offset & 63); -+ dcbz(shadow); -+ return shadow; -+} -+ -+static inline void qbman_cena_write_complete(struct qbman_swp_sys *s, -+ uint32_t offset, void *cmd) -+{ -+ const uint32_t *shadow = cmd; -+ int loop; -+ -+#ifdef QBMAN_CENA_TRACE -+ pr_info("qbman_cena_write_complete(%p:0x%03x) %p\n", -+ s->addr_cena, offset, shadow); -+ hexdump(cmd, 64); -+#endif -+ for (loop = 15; loop >= 1; loop--) -+ writel_relaxed(shadow[loop], s->addr_cena + -+ offset + loop * 4); -+ lwsync(); -+ writel_relaxed(shadow[0], s->addr_cena + offset); -+ dcbf(s->addr_cena + offset); -+} -+ -+static inline void *qbman_cena_read(struct qbman_swp_sys *s, uint32_t offset) -+{ -+ uint32_t *shadow = s->cena + offset; -+ unsigned int loop; -+ -+#ifdef QBMAN_CENA_TRACE -+ pr_info("qbman_cena_read(%p:0x%03x) %p\n", -+ s->addr_cena, offset, shadow); -+#endif -+ -+ for (loop = 0; loop < 16; loop++) -+ shadow[loop] = readl_relaxed(s->addr_cena + offset -+ + loop * 4); -+#ifdef QBMAN_CENA_TRACE -+ hexdump(shadow, 64); -+#endif -+ return shadow; -+} -+ -+static inline void qbman_cena_invalidate_prefetch(struct qbman_swp_sys *s, -+ uint32_t offset) -+{ -+ dcivac(s->addr_cena + offset); -+ prefetch_for_load(s->addr_cena + offset); -+} -+ -+ /******************/ -+ /* Portal support */ -+ /******************/ -+ -+/* The SWP_CFG portal register is special, in that it is used by the -+ * platform-specific code rather than the platform-independent code in -+ * qbman_portal.c. So use of it is declared locally here. */ -+#define QBMAN_CINH_SWP_CFG 0xd00 -+ -+/* For MC portal use, we always configure with -+ * DQRR_MF is (SWP_CFG,20,3) - DQRR max fill (<- 0x4) -+ * EST is (SWP_CFG,16,3) - EQCR_CI stashing threshold (<- 0x0) -+ * RPM is (SWP_CFG,12,2) - RCR production notification mode (<- 0x3) -+ * DCM is (SWP_CFG,10,2) - DQRR consumption notification mode (<- 0x2) -+ * EPM is (SWP_CFG,8,2) - EQCR production notification mode (<- 0x3) -+ * SD is (SWP_CFG,5,1) - memory stashing drop enable (<- FALSE) -+ * SP is (SWP_CFG,4,1) - memory stashing priority (<- TRUE) -+ * SE is (SWP_CFG,3,1) - memory stashing enable (<- 0x0) -+ * DP is (SWP_CFG,2,1) - dequeue stashing priority (<- TRUE) -+ * DE is (SWP_CFG,1,1) - dequeue stashing enable (<- 0x0) -+ * EP is (SWP_CFG,0,1) - EQCR_CI stashing priority (<- FALSE) -+ */ -+static inline uint32_t qbman_set_swp_cfg(uint8_t max_fill, uint8_t wn, -+ uint8_t est, uint8_t rpm, uint8_t dcm, -+ uint8_t epm, int sd, int sp, int se, -+ int dp, int de, int ep) -+{ -+ uint32_t reg; -+ -+ reg = e32_uint8_t(20, (uint32_t)(3 + (max_fill >> 3)), max_fill) | -+ e32_uint8_t(16, 3, est) | e32_uint8_t(12, 2, rpm) | -+ e32_uint8_t(10, 2, dcm) | e32_uint8_t(8, 2, epm) | -+ e32_int(5, 1, sd) | e32_int(4, 1, sp) | e32_int(3, 1, se) | -+ e32_int(2, 1, dp) | e32_int(1, 1, de) | e32_int(0, 1, ep) | -+ e32_uint8_t(14, 1, wn); -+ return reg; -+} -+ -+static inline int qbman_swp_sys_init(struct qbman_swp_sys *s, -+ const struct qbman_swp_desc *d, -+ uint8_t dqrr_size) -+{ -+ uint32_t reg; -+ -+ s->addr_cena = d->cena_bar; -+ s->addr_cinh = d->cinh_bar; -+ s->cena = (void *)get_zeroed_page(GFP_KERNEL); -+ if (!s->cena) { -+ pr_err("Could not allocate page for cena shadow\n"); -+ return -1; -+ } -+ -+#ifdef QBMAN_CHECKING -+ /* We should never be asked to initialise for a portal that isn't in -+ * the power-on state. (Ie. don't forget to reset portals when they are -+ * decommissioned!) -+ */ -+ reg = qbman_cinh_read(s, QBMAN_CINH_SWP_CFG); -+ BUG_ON(reg); -+#endif -+ reg = qbman_set_swp_cfg(dqrr_size, 0, 0, 3, 2, 3, 0, 1, 0, 1, 0, 0); -+ qbman_cinh_write(s, QBMAN_CINH_SWP_CFG, reg); -+ reg = qbman_cinh_read(s, QBMAN_CINH_SWP_CFG); -+ if (!reg) { -+ pr_err("The portal is not enabled!\n"); -+ kfree(s->cena); -+ return -1; -+ } -+ return 0; -+} -+ -+static inline void qbman_swp_sys_finish(struct qbman_swp_sys *s) -+{ -+ free_page((unsigned long)s->cena); -+} -diff --git a/drivers/staging/fsl-mc/bus/dpio/qbman_sys_decl.h b/drivers/staging/fsl-mc/bus/dpio/qbman_sys_decl.h -new file mode 100644 -index 0000000..5b3a224 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpio/qbman_sys_decl.h -@@ -0,0 +1,86 @@ -+/* Copyright (C) 2014 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "fsl_qbman_base.h" -+ -+/* The platform-independent code shouldn't need endianness, except for -+ * weird/fast-path cases like qbman_result_has_token(), which needs to -+ * perform a passive and endianness-specific test on a read-only data structure -+ * very quickly. It's an exception, and this symbol is used for that case. */ -+#if defined(__BIG_ENDIAN) -+#define DQRR_TOK_OFFSET 0 -+#define QBMAN_RESULT_VERB_OFFSET_IN_MEM 24 -+#define SCN_STATE_OFFSET_IN_MEM 8 -+#define SCN_RID_OFFSET_IN_MEM 8 -+#else -+#define DQRR_TOK_OFFSET 24 -+#define QBMAN_RESULT_VERB_OFFSET_IN_MEM 0 -+#define SCN_STATE_OFFSET_IN_MEM 16 -+#define SCN_RID_OFFSET_IN_MEM 0 -+#endif -+ -+/* Similarly-named functions */ -+#define upper32(a) upper_32_bits(a) -+#define lower32(a) lower_32_bits(a) -+ -+ /****************/ -+ /* arch assists */ -+ /****************/ -+ -+#define dcbz(p) { asm volatile("dc zva, %0" : : "r" (p) : "memory"); } -+#define lwsync() { asm volatile("dmb st" : : : "memory"); } -+#define dcbf(p) { asm volatile("dc cvac, %0;" : : "r" (p) : "memory"); } -+#define dcivac(p) { asm volatile("dc ivac, %0" : : "r"(p) : "memory"); } -+static inline void prefetch_for_load(void *p) -+{ -+ asm volatile("prfm pldl1keep, [%0, #64]" : : "r" (p)); -+} -+static inline void prefetch_for_store(void *p) -+{ -+ asm volatile("prfm pstl1keep, [%0, #64]" : : "r" (p)); -+} -diff --git a/drivers/staging/fsl-mc/bus/dpio/qbman_test.c b/drivers/staging/fsl-mc/bus/dpio/qbman_test.c -new file mode 100644 -index 0000000..28396e7 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpio/qbman_test.c -@@ -0,0 +1,664 @@ -+/* Copyright (C) 2014 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 -+#include -+#include -+ -+#include "qbman_private.h" -+#include "fsl_qbman_portal.h" -+#include "qbman_debug.h" -+#include "../../include/fsl_dpaa2_fd.h" -+ -+#define QBMAN_SWP_CENA_BASE 0x818000000 -+#define QBMAN_SWP_CINH_BASE 0x81c000000 -+ -+#define QBMAN_PORTAL_IDX 2 -+#define QBMAN_TEST_FQID 19 -+#define QBMAN_TEST_BPID 23 -+#define QBMAN_USE_QD -+#ifdef QBMAN_USE_QD -+#define QBMAN_TEST_QDID 1 -+#endif -+#define QBMAN_TEST_LFQID 0xf00010 -+ -+#define NUM_EQ_FRAME 10 -+#define NUM_DQ_FRAME 10 -+#define NUM_DQ_IN_DQRR 5 -+#define NUM_DQ_IN_MEM (NUM_DQ_FRAME - NUM_DQ_IN_DQRR) -+ -+static struct qbman_swp *swp; -+static struct qbman_eq_desc eqdesc; -+static struct qbman_pull_desc pulldesc; -+static struct qbman_release_desc releasedesc; -+static struct qbman_eq_response eq_storage[1]; -+static struct dpaa2_dq dq_storage[NUM_DQ_IN_MEM] __aligned(64); -+static dma_addr_t eq_storage_phys; -+static dma_addr_t dq_storage_phys; -+ -+/* FQ ctx attribute values for the test code. */ -+#define FQCTX_HI 0xabbaf00d -+#define FQCTX_LO 0x98765432 -+#define FQ_VFQID 0x123456 -+ -+/* Sample frame descriptor */ -+static struct qbman_fd_simple fd = { -+ .addr_lo = 0xbabaf33d, -+ .addr_hi = 0x01234567, -+ .len = 0x7777, -+ .frc = 0xdeadbeef, -+ .flc_lo = 0xcafecafe, -+ .flc_hi = 0xbeadabba -+}; -+ -+static void fd_inc(struct qbman_fd_simple *_fd) -+{ -+ _fd->addr_lo += _fd->len; -+ _fd->flc_lo += 0x100; -+ _fd->frc += 0x10; -+} -+ -+static int fd_cmp(struct qbman_fd *fda, struct qbman_fd *fdb) -+{ -+ int i; -+ -+ for (i = 0; i < 8; i++) -+ if (fda->words[i] - fdb->words[i]) -+ return 1; -+ return 0; -+} -+ -+struct qbman_fd fd_eq[NUM_EQ_FRAME]; -+struct qbman_fd fd_dq[NUM_DQ_FRAME]; -+ -+/* "Buffers" to be released (and storage for buffers to be acquired) */ -+static uint64_t rbufs[320]; -+static uint64_t abufs[320]; -+ -+static void do_enqueue(struct qbman_swp *swp) -+{ -+ int i, j, ret; -+ -+#ifdef QBMAN_USE_QD -+ pr_info("*****QBMan_test: Enqueue %d frames to QD %d\n", -+ NUM_EQ_FRAME, QBMAN_TEST_QDID); -+#else -+ pr_info("*****QBMan_test: Enqueue %d frames to FQ %d\n", -+ NUM_EQ_FRAME, QBMAN_TEST_FQID); -+#endif -+ for (i = 0; i < NUM_EQ_FRAME; i++) { -+ /*********************************/ -+ /* Prepare a enqueue descriptor */ -+ /*********************************/ -+ memset(eq_storage, 0, sizeof(eq_storage)); -+ eq_storage_phys = virt_to_phys(eq_storage); -+ qbman_eq_desc_clear(&eqdesc); -+ qbman_eq_desc_set_no_orp(&eqdesc, 0); -+ qbman_eq_desc_set_response(&eqdesc, eq_storage_phys, 0); -+ qbman_eq_desc_set_token(&eqdesc, 0x99); -+#ifdef QBMAN_USE_QD -+ /**********************************/ -+ /* Prepare a Queueing Destination */ -+ /**********************************/ -+ qbman_eq_desc_set_qd(&eqdesc, QBMAN_TEST_QDID, 0, 3); -+#else -+ qbman_eq_desc_set_fq(&eqdesc, QBMAN_TEST_FQID); -+#endif -+ -+ /******************/ -+ /* Try an enqueue */ -+ /******************/ -+ ret = qbman_swp_enqueue(swp, &eqdesc, -+ (const struct qbman_fd *)&fd); -+ BUG_ON(ret); -+ for (j = 0; j < 8; j++) -+ fd_eq[i].words[j] = *((uint32_t *)&fd + j); -+ fd_inc(&fd); -+ } -+} -+ -+static void do_push_dequeue(struct qbman_swp *swp) -+{ -+ int i, j; -+ const struct dpaa2_dq *dq_storage1; -+ const struct qbman_fd *__fd; -+ int loopvar; -+ -+ pr_info("*****QBMan_test: Start push dequeue\n"); -+ for (i = 0; i < NUM_DQ_FRAME; i++) { -+ DBG_POLL_START(loopvar); -+ do { -+ DBG_POLL_CHECK(loopvar); -+ dq_storage1 = qbman_swp_dqrr_next(swp); -+ } while (!dq_storage1); -+ if (dq_storage1) { -+ __fd = (const struct qbman_fd *) -+ dpaa2_dq_fd(dq_storage1); -+ for (j = 0; j < 8; j++) -+ fd_dq[i].words[j] = __fd->words[j]; -+ if (fd_cmp(&fd_eq[i], &fd_dq[i])) { -+ pr_info("enqueue FD is\n"); -+ hexdump(&fd_eq[i], 32); -+ pr_info("dequeue FD is\n"); -+ hexdump(&fd_dq[i], 32); -+ } -+ qbman_swp_dqrr_consume(swp, dq_storage1); -+ } else { -+ pr_info("The push dequeue fails\n"); -+ } -+ } -+} -+ -+static void do_pull_dequeue(struct qbman_swp *swp) -+{ -+ int i, j, ret; -+ const struct dpaa2_dq *dq_storage1; -+ const struct qbman_fd *__fd; -+ int loopvar; -+ -+ pr_info("*****QBMan_test: Dequeue %d frames with dq entry in DQRR\n", -+ NUM_DQ_IN_DQRR); -+ for (i = 0; i < NUM_DQ_IN_DQRR; i++) { -+ qbman_pull_desc_clear(&pulldesc); -+ qbman_pull_desc_set_storage(&pulldesc, NULL, 0, 0); -+ qbman_pull_desc_set_numframes(&pulldesc, 1); -+ qbman_pull_desc_set_fq(&pulldesc, QBMAN_TEST_FQID); -+ -+ ret = qbman_swp_pull(swp, &pulldesc); -+ BUG_ON(ret); -+ DBG_POLL_START(loopvar); -+ do { -+ DBG_POLL_CHECK(loopvar); -+ dq_storage1 = qbman_swp_dqrr_next(swp); -+ } while (!dq_storage1); -+ -+ if (dq_storage1) { -+ __fd = (const struct qbman_fd *) -+ dpaa2_dq_fd(dq_storage1); -+ for (j = 0; j < 8; j++) -+ fd_dq[i].words[j] = __fd->words[j]; -+ if (fd_cmp(&fd_eq[i], &fd_dq[i])) { -+ pr_info("enqueue FD is\n"); -+ hexdump(&fd_eq[i], 32); -+ pr_info("dequeue FD is\n"); -+ hexdump(&fd_dq[i], 32); -+ } -+ qbman_swp_dqrr_consume(swp, dq_storage1); -+ } else { -+ pr_info("Dequeue with dq entry in DQRR fails\n"); -+ } -+ } -+ -+ pr_info("*****QBMan_test: Dequeue %d frames with dq entry in memory\n", -+ NUM_DQ_IN_MEM); -+ for (i = 0; i < NUM_DQ_IN_MEM; i++) { -+ dq_storage_phys = virt_to_phys(&dq_storage[i]); -+ qbman_pull_desc_clear(&pulldesc); -+ qbman_pull_desc_set_storage(&pulldesc, &dq_storage[i], -+ dq_storage_phys, 1); -+ qbman_pull_desc_set_numframes(&pulldesc, 1); -+ qbman_pull_desc_set_fq(&pulldesc, QBMAN_TEST_FQID); -+ ret = qbman_swp_pull(swp, &pulldesc); -+ BUG_ON(ret); -+ -+ DBG_POLL_START(loopvar); -+ do { -+ DBG_POLL_CHECK(loopvar); -+ ret = qbman_result_has_new_result(swp, -+ &dq_storage[i]); -+ } while (!ret); -+ -+ if (ret) { -+ for (j = 0; j < 8; j++) -+ fd_dq[i + NUM_DQ_IN_DQRR].words[j] = -+ dq_storage[i].dont_manipulate_directly[j + 8]; -+ j = i + NUM_DQ_IN_DQRR; -+ if (fd_cmp(&fd_eq[j], &fd_dq[j])) { -+ pr_info("enqueue FD is\n"); -+ hexdump(&fd_eq[i + NUM_DQ_IN_DQRR], 32); -+ pr_info("dequeue FD is\n"); -+ hexdump(&fd_dq[i + NUM_DQ_IN_DQRR], 32); -+ hexdump(&dq_storage[i], 64); -+ } -+ } else { -+ pr_info("Dequeue with dq entry in memory fails\n"); -+ } -+ } -+} -+ -+static void release_buffer(struct qbman_swp *swp, unsigned int num) -+{ -+ int ret; -+ unsigned int i, j; -+ -+ qbman_release_desc_clear(&releasedesc); -+ qbman_release_desc_set_bpid(&releasedesc, QBMAN_TEST_BPID); -+ pr_info("*****QBMan_test: Release %d buffers to BP %d\n", -+ num, QBMAN_TEST_BPID); -+ for (i = 0; i < (num / 7 + 1); i++) { -+ j = ((num - i * 7) > 7) ? 7 : (num - i * 7); -+ ret = qbman_swp_release(swp, &releasedesc, &rbufs[i * 7], j); -+ BUG_ON(ret); -+ } -+} -+ -+static void acquire_buffer(struct qbman_swp *swp, unsigned int num) -+{ -+ int ret; -+ unsigned int i, j; -+ -+ pr_info("*****QBMan_test: Acquire %d buffers from BP %d\n", -+ num, QBMAN_TEST_BPID); -+ -+ for (i = 0; i < (num / 7 + 1); i++) { -+ j = ((num - i * 7) > 7) ? 7 : (num - i * 7); -+ ret = qbman_swp_acquire(swp, QBMAN_TEST_BPID, &abufs[i * 7], j); -+ BUG_ON(ret != j); -+ } -+} -+ -+static void buffer_pool_test(struct qbman_swp *swp) -+{ -+ struct qbman_attr info; -+ struct dpaa2_dq *bpscn_message; -+ dma_addr_t bpscn_phys; -+ uint64_t bpscn_ctx; -+ uint64_t ctx = 0xbbccddaadeadbeefull; -+ int i, ret; -+ uint32_t hw_targ; -+ -+ pr_info("*****QBMan_test: test buffer pool management\n"); -+ ret = qbman_bp_query(swp, QBMAN_TEST_BPID, &info); -+ qbman_bp_attr_get_bpscn_addr(&info, &bpscn_phys); -+ pr_info("The bpscn is %llx, info_phys is %llx\n", bpscn_phys, -+ virt_to_phys(&info)); -+ bpscn_message = phys_to_virt(bpscn_phys); -+ -+ for (i = 0; i < 320; i++) -+ rbufs[i] = 0xf00dabba01234567ull + i * 0x40; -+ -+ release_buffer(swp, 320); -+ -+ pr_info("QBMan_test: query the buffer pool\n"); -+ qbman_bp_query(swp, QBMAN_TEST_BPID, &info); -+ hexdump(&info, 64); -+ qbman_bp_attr_get_hw_targ(&info, &hw_targ); -+ pr_info("hw_targ is %d\n", hw_targ); -+ -+ /* Acquire buffers to trigger BPSCN */ -+ acquire_buffer(swp, 300); -+ /* BPSCN should be written to the memory */ -+ qbman_bp_query(swp, QBMAN_TEST_BPID, &info); -+ hexdump(&info, 64); -+ hexdump(bpscn_message, 64); -+ BUG_ON(!qbman_result_is_BPSCN(bpscn_message)); -+ /* There should be free buffers in the pool */ -+ BUG_ON(!(qbman_result_bpscn_has_free_bufs(bpscn_message))); -+ /* Buffer pool is depleted */ -+ BUG_ON(!qbman_result_bpscn_is_depleted(bpscn_message)); -+ /* The ctx should match */ -+ bpscn_ctx = qbman_result_bpscn_ctx(bpscn_message); -+ pr_info("BPSCN test: ctx %llx, bpscn_ctx %llx\n", ctx, bpscn_ctx); -+ BUG_ON(ctx != bpscn_ctx); -+ memset(bpscn_message, 0, sizeof(struct dpaa2_dq)); -+ -+ /* Re-seed the buffer pool to trigger BPSCN */ -+ release_buffer(swp, 240); -+ /* BPSCN should be written to the memory */ -+ BUG_ON(!qbman_result_is_BPSCN(bpscn_message)); -+ /* There should be free buffers in the pool */ -+ BUG_ON(!(qbman_result_bpscn_has_free_bufs(bpscn_message))); -+ /* Buffer pool is not depleted */ -+ BUG_ON(qbman_result_bpscn_is_depleted(bpscn_message)); -+ memset(bpscn_message, 0, sizeof(struct dpaa2_dq)); -+ -+ acquire_buffer(swp, 260); -+ /* BPSCN should be written to the memory */ -+ BUG_ON(!qbman_result_is_BPSCN(bpscn_message)); -+ /* There should be free buffers in the pool while BPSCN generated */ -+ BUG_ON(!(qbman_result_bpscn_has_free_bufs(bpscn_message))); -+ /* Buffer pool is depletion */ -+ BUG_ON(!qbman_result_bpscn_is_depleted(bpscn_message)); -+} -+ -+static void ceetm_test(struct qbman_swp *swp) -+{ -+ int i, j, ret; -+ -+ qbman_eq_desc_clear(&eqdesc); -+ qbman_eq_desc_set_no_orp(&eqdesc, 0); -+ qbman_eq_desc_set_fq(&eqdesc, QBMAN_TEST_LFQID); -+ pr_info("*****QBMan_test: Enqueue to LFQID %x\n", -+ QBMAN_TEST_LFQID); -+ for (i = 0; i < NUM_EQ_FRAME; i++) { -+ ret = qbman_swp_enqueue(swp, &eqdesc, -+ (const struct qbman_fd *)&fd); -+ BUG_ON(ret); -+ for (j = 0; j < 8; j++) -+ fd_eq[i].words[j] = *((uint32_t *)&fd + j); -+ fd_inc(&fd); -+ } -+} -+ -+int qbman_test(void) -+{ -+ struct qbman_swp_desc pd; -+ uint32_t reg; -+ -+ pd.cena_bar = ioremap_cache_ns(QBMAN_SWP_CENA_BASE + -+ QBMAN_PORTAL_IDX * 0x10000, 0x10000); -+ pd.cinh_bar = ioremap(QBMAN_SWP_CINH_BASE + -+ QBMAN_PORTAL_IDX * 0x10000, 0x10000); -+ -+ /* Detect whether the mc image is the test image with GPP setup */ -+ reg = readl_relaxed(pd.cena_bar + 0x4); -+ if (reg != 0xdeadbeef) { -+ pr_err("The MC image doesn't have GPP test setup, stop!\n"); -+ iounmap(pd.cena_bar); -+ iounmap(pd.cinh_bar); -+ return -1; -+ } -+ -+ pr_info("*****QBMan_test: Init QBMan SWP %d\n", QBMAN_PORTAL_IDX); -+ swp = qbman_swp_init(&pd); -+ if (!swp) { -+ iounmap(pd.cena_bar); -+ iounmap(pd.cinh_bar); -+ return -1; -+ } -+ -+ /*******************/ -+ /* Enqueue frames */ -+ /*******************/ -+ do_enqueue(swp); -+ -+ /*******************/ -+ /* Do pull dequeue */ -+ /*******************/ -+ do_pull_dequeue(swp); -+ -+ /*******************/ -+ /* Enqueue frames */ -+ /*******************/ -+ qbman_swp_push_set(swp, 0, 1); -+ qbman_swp_fq_schedule(swp, QBMAN_TEST_FQID); -+ do_enqueue(swp); -+ -+ /*******************/ -+ /* Do push dequeue */ -+ /*******************/ -+ do_push_dequeue(swp); -+ -+ /**************************/ -+ /* Test buffer pool funcs */ -+ /**************************/ -+ buffer_pool_test(swp); -+ -+ /******************/ -+ /* CEETM test */ -+ /******************/ -+ ceetm_test(swp); -+ -+ qbman_swp_finish(swp); -+ pr_info("*****QBMan_test: Kernel test Passed\n"); -+ return 0; -+} -+ -+/* user-space test-case, definitions: -+ * -+ * 1 portal only, using portal index 3. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define QBMAN_TEST_US_SWP 3 /* portal index for user space */ -+ -+#define QBMAN_TEST_MAGIC 'q' -+struct qbman_test_swp_ioctl { -+ unsigned long portal1_cinh; -+ unsigned long portal1_cena; -+}; -+struct qbman_test_dma_ioctl { -+ unsigned long ptr; -+ uint64_t phys_addr; -+}; -+ -+struct qbman_test_priv { -+ int has_swp_map; -+ int has_dma_map; -+ unsigned long pgoff; -+}; -+ -+#define QBMAN_TEST_SWP_MAP \ -+ _IOR(QBMAN_TEST_MAGIC, 0x01, struct qbman_test_swp_ioctl) -+#define QBMAN_TEST_SWP_UNMAP \ -+ _IOR(QBMAN_TEST_MAGIC, 0x02, struct qbman_test_swp_ioctl) -+#define QBMAN_TEST_DMA_MAP \ -+ _IOR(QBMAN_TEST_MAGIC, 0x03, struct qbman_test_dma_ioctl) -+#define QBMAN_TEST_DMA_UNMAP \ -+ _IOR(QBMAN_TEST_MAGIC, 0x04, struct qbman_test_dma_ioctl) -+ -+#define TEST_PORTAL1_CENA_PGOFF ((QBMAN_SWP_CENA_BASE + QBMAN_TEST_US_SWP * \ -+ 0x10000) >> PAGE_SHIFT) -+#define TEST_PORTAL1_CINH_PGOFF ((QBMAN_SWP_CINH_BASE + QBMAN_TEST_US_SWP * \ -+ 0x10000) >> PAGE_SHIFT) -+ -+static int qbman_test_open(struct inode *inode, struct file *filp) -+{ -+ struct qbman_test_priv *priv; -+ -+ priv = kmalloc(sizeof(struct qbman_test_priv), GFP_KERNEL); -+ if (!priv) -+ return -EIO; -+ filp->private_data = priv; -+ priv->has_swp_map = 0; -+ priv->has_dma_map = 0; -+ priv->pgoff = 0; -+ return 0; -+} -+ -+static int qbman_test_mmap(struct file *filp, struct vm_area_struct *vma) -+{ -+ int ret; -+ struct qbman_test_priv *priv = filp->private_data; -+ -+ BUG_ON(!priv); -+ -+ if (vma->vm_pgoff == TEST_PORTAL1_CINH_PGOFF) -+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); -+ else if (vma->vm_pgoff == TEST_PORTAL1_CENA_PGOFF) -+ vma->vm_page_prot = pgprot_cached_ns(vma->vm_page_prot); -+ else if (vma->vm_pgoff == priv->pgoff) -+ vma->vm_page_prot = pgprot_cached(vma->vm_page_prot); -+ else { -+ pr_err("Damn, unrecognised pg_off!!\n"); -+ return -EINVAL; -+ } -+ ret = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, -+ vma->vm_end - vma->vm_start, -+ vma->vm_page_prot); -+ return ret; -+} -+ -+static long qbman_test_ioctl(struct file *fp, unsigned int cmd, -+ unsigned long arg) -+{ -+ void __user *a = (void __user *)arg; -+ unsigned long longret, populate; -+ int ret = 0; -+ struct qbman_test_priv *priv = fp->private_data; -+ -+ BUG_ON(!priv); -+ -+ switch (cmd) { -+ case QBMAN_TEST_SWP_MAP: -+ { -+ struct qbman_test_swp_ioctl params; -+ -+ if (priv->has_swp_map) -+ return -EINVAL; -+ down_write(¤t->mm->mmap_sem); -+ /* Map portal1 CINH */ -+ longret = do_mmap_pgoff(fp, PAGE_SIZE, 0x10000, -+ PROT_READ | PROT_WRITE, MAP_SHARED, -+ TEST_PORTAL1_CINH_PGOFF, &populate); -+ if (longret & ~PAGE_MASK) { -+ ret = (int)longret; -+ goto out; -+ } -+ params.portal1_cinh = longret; -+ /* Map portal1 CENA */ -+ longret = do_mmap_pgoff(fp, PAGE_SIZE, 0x10000, -+ PROT_READ | PROT_WRITE, MAP_SHARED, -+ TEST_PORTAL1_CENA_PGOFF, &populate); -+ if (longret & ~PAGE_MASK) { -+ ret = (int)longret; -+ goto out; -+ } -+ params.portal1_cena = longret; -+ priv->has_swp_map = 1; -+out: -+ up_write(¤t->mm->mmap_sem); -+ if (!ret && copy_to_user(a, ¶ms, sizeof(params))) -+ return -EFAULT; -+ return ret; -+ } -+ case QBMAN_TEST_SWP_UNMAP: -+ { -+ struct qbman_test_swp_ioctl params; -+ -+ if (!priv->has_swp_map) -+ return -EINVAL; -+ -+ if (copy_from_user(¶ms, a, sizeof(params))) -+ return -EFAULT; -+ down_write(¤t->mm->mmap_sem); -+ do_munmap(current->mm, params.portal1_cena, 0x10000); -+ do_munmap(current->mm, params.portal1_cinh, 0x10000); -+ up_write(¤t->mm->mmap_sem); -+ priv->has_swp_map = 0; -+ return 0; -+ } -+ case QBMAN_TEST_DMA_MAP: -+ { -+ struct qbman_test_dma_ioctl params; -+ void *vaddr; -+ -+ if (priv->has_dma_map) -+ return -EINVAL; -+ vaddr = (void *)get_zeroed_page(GFP_KERNEL); -+ params.phys_addr = virt_to_phys(vaddr); -+ priv->pgoff = (unsigned long)params.phys_addr >> PAGE_SHIFT; -+ down_write(¤t->mm->mmap_sem); -+ longret = do_mmap_pgoff(fp, PAGE_SIZE, PAGE_SIZE, -+ PROT_READ | PROT_WRITE, MAP_SHARED, -+ priv->pgoff, &populate); -+ if (longret & ~PAGE_MASK) { -+ ret = (int)longret; -+ return ret; -+ } -+ params.ptr = longret; -+ priv->has_dma_map = 1; -+ up_write(¤t->mm->mmap_sem); -+ if (copy_to_user(a, ¶ms, sizeof(params))) -+ return -EFAULT; -+ return 0; -+ } -+ case QBMAN_TEST_DMA_UNMAP: -+ { -+ struct qbman_test_dma_ioctl params; -+ -+ if (!priv->has_dma_map) -+ return -EINVAL; -+ if (copy_from_user(¶ms, a, sizeof(params))) -+ return -EFAULT; -+ down_write(¤t->mm->mmap_sem); -+ do_munmap(current->mm, params.ptr, PAGE_SIZE); -+ up_write(¤t->mm->mmap_sem); -+ free_page((unsigned long)phys_to_virt(params.phys_addr)); -+ priv->has_dma_map = 0; -+ return 0; -+ } -+ default: -+ pr_err("Bad ioctl cmd!\n"); -+ } -+ return -EINVAL; -+} -+ -+static const struct file_operations qbman_fops = { -+ .open = qbman_test_open, -+ .mmap = qbman_test_mmap, -+ .unlocked_ioctl = qbman_test_ioctl -+}; -+ -+static struct miscdevice qbman_miscdev = { -+ .name = "qbman-test", -+ .fops = &qbman_fops, -+ .minor = MISC_DYNAMIC_MINOR, -+}; -+ -+static int qbman_miscdev_init; -+ -+static int test_init(void) -+{ -+ int ret = qbman_test(); -+ -+ if (!ret) { -+ /* MC image supports the test cases, so instantiate the -+ * character devic that the user-space test case will use to do -+ * its memory mappings. */ -+ ret = misc_register(&qbman_miscdev); -+ if (ret) { -+ pr_err("qbman-test: failed to register misc device\n"); -+ return ret; -+ } -+ pr_info("qbman-test: misc device registered!\n"); -+ qbman_miscdev_init = 1; -+ } -+ return 0; -+} -+ -+static void test_exit(void) -+{ -+ if (qbman_miscdev_init) { -+ misc_deregister(&qbman_miscdev); -+ qbman_miscdev_init = 0; -+ } -+} -+ -+module_init(test_init); -+module_exit(test_exit); -diff --git a/drivers/staging/fsl-mc/bus/dpmcp-cmd.h b/drivers/staging/fsl-mc/bus/dpmcp-cmd.h -new file mode 100644 -index 0000000..c9b52dd ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpmcp-cmd.h -@@ -0,0 +1,56 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef _FSL_DPMCP_CMD_H -+#define _FSL_DPMCP_CMD_H -+ -+/* Minimal supported DPMCP Version */ -+#define DPMCP_MIN_VER_MAJOR 3 -+#define DPMCP_MIN_VER_MINOR 0 -+ -+/* Command IDs */ -+#define DPMCP_CMDID_CLOSE 0x800 -+#define DPMCP_CMDID_OPEN 0x80b -+#define DPMCP_CMDID_CREATE 0x90b -+#define DPMCP_CMDID_DESTROY 0x900 -+ -+#define DPMCP_CMDID_GET_ATTR 0x004 -+#define DPMCP_CMDID_RESET 0x005 -+ -+#define DPMCP_CMDID_SET_IRQ 0x010 -+#define DPMCP_CMDID_GET_IRQ 0x011 -+#define DPMCP_CMDID_SET_IRQ_ENABLE 0x012 -+#define DPMCP_CMDID_GET_IRQ_ENABLE 0x013 -+#define DPMCP_CMDID_SET_IRQ_MASK 0x014 -+#define DPMCP_CMDID_GET_IRQ_MASK 0x015 -+#define DPMCP_CMDID_GET_IRQ_STATUS 0x016 -+ -+#endif /* _FSL_DPMCP_CMD_H */ -diff --git a/drivers/staging/fsl-mc/bus/dpmcp.c b/drivers/staging/fsl-mc/bus/dpmcp.c -new file mode 100644 -index 0000000..e23592a ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpmcp.c -@@ -0,0 +1,318 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#include "../include/mc-sys.h" -+#include "../include/mc-cmd.h" -+#include "dpmcp.h" -+#include "dpmcp-cmd.h" -+ -+int dpmcp_open(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ int dpmcp_id, -+ uint16_t *token) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_OPEN, -+ cmd_flags, -+ 0); -+ cmd.params[0] |= mc_enc(0, 32, dpmcp_id); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *token = MC_CMD_HDR_READ_TOKEN(cmd.header); -+ -+ return err; -+} -+ -+int dpmcp_close(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_CLOSE, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpmcp_create(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ const struct dpmcp_cfg *cfg, -+ uint16_t *token) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_CREATE, -+ cmd_flags, -+ 0); -+ cmd.params[0] |= mc_enc(0, 32, cfg->portal_id); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *token = MC_CMD_HDR_READ_TOKEN(cmd.header); -+ -+ return 0; -+} -+ -+int dpmcp_destroy(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_DESTROY, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpmcp_reset(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_RESET, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpmcp_set_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ struct dpmcp_irq_cfg *irq_cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_SET_IRQ, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 8, irq_index); -+ cmd.params[0] |= mc_enc(32, 32, irq_cfg->val); -+ cmd.params[1] |= mc_enc(0, 64, irq_cfg->paddr); -+ cmd.params[2] |= mc_enc(0, 32, irq_cfg->irq_num); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpmcp_get_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ int *type, -+ struct dpmcp_irq_cfg *irq_cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_GET_IRQ, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ irq_cfg->val = (uint32_t)mc_dec(cmd.params[0], 0, 32); -+ irq_cfg->paddr = (uint64_t)mc_dec(cmd.params[1], 0, 64); -+ irq_cfg->irq_num = (int)mc_dec(cmd.params[2], 0, 32); -+ *type = (int)mc_dec(cmd.params[2], 32, 32); -+ return 0; -+} -+ -+int dpmcp_set_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t en) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_SET_IRQ_ENABLE, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 8, en); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpmcp_get_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t *en) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_GET_IRQ_ENABLE, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *en = (uint8_t)mc_dec(cmd.params[0], 0, 8); -+ return 0; -+} -+ -+int dpmcp_set_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t mask) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_SET_IRQ_MASK, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, mask); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+ -+int dpmcp_get_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *mask) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_GET_IRQ_MASK, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *mask = (uint32_t)mc_dec(cmd.params[0], 0, 32); -+ return 0; -+} -+ -+int dpmcp_get_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *status) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_GET_IRQ_STATUS, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *status = (uint32_t)mc_dec(cmd.params[0], 0, 32); -+ return 0; -+} -+ -+int dpmcp_get_attributes(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpmcp_attr *attr) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_GET_ATTR, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ attr->id = (int)mc_dec(cmd.params[0], 32, 32); -+ attr->version.major = (uint16_t)mc_dec(cmd.params[1], 0, 16); -+ attr->version.minor = (uint16_t)mc_dec(cmd.params[1], 16, 16); -+ return 0; -+} -diff --git a/drivers/staging/fsl-mc/bus/dpmcp.h b/drivers/staging/fsl-mc/bus/dpmcp.h -new file mode 100644 -index 0000000..e434a24 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpmcp.h -@@ -0,0 +1,323 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef __FSL_DPMCP_H -+#define __FSL_DPMCP_H -+ -+/* Data Path Management Command Portal API -+ * Contains initialization APIs and runtime control APIs for DPMCP -+ */ -+ -+struct fsl_mc_io; -+ -+/** -+ * dpmcp_open() - Open a control session for the specified object. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @dpmcp_id: DPMCP unique ID -+ * @token: Returned token; use in subsequent API calls -+ * -+ * This function can be used to open a control session for an -+ * already created object; an object may have been declared in -+ * the DPL or by calling the dpmcp_create function. -+ * This function returns a unique authentication token, -+ * associated with the specific object ID and the specific MC -+ * portal; this token must be used in all subsequent commands for -+ * this specific object -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmcp_open(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ int dpmcp_id, -+ uint16_t *token); -+ -+/* Get portal ID from pool */ -+#define DPMCP_GET_PORTAL_ID_FROM_POOL (-1) -+ -+/** -+ * dpmcp_close() - Close the control session of the object -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMCP object -+ * -+ * After this function is called, no further operations are -+ * allowed on the object without opening a new control session. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmcp_close(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * struct dpmcp_cfg - Structure representing DPMCP configuration -+ * @portal_id: Portal ID; 'DPMCP_GET_PORTAL_ID_FROM_POOL' to get the portal ID -+ * from pool -+ */ -+struct dpmcp_cfg { -+ int portal_id; -+}; -+ -+/** -+ * dpmcp_create() - Create the DPMCP object. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @cfg: Configuration structure -+ * @token: Returned token; use in subsequent API calls -+ * -+ * Create the DPMCP object, allocate required resources and -+ * perform required initialization. -+ * -+ * The object can be created either by declaring it in the -+ * DPL file, or by calling this function. -+ * This function returns a unique authentication token, -+ * associated with the specific object ID and the specific MC -+ * portal; this token must be used in all subsequent calls to -+ * this specific object. For objects that are created using the -+ * DPL file, call dpmcp_open function to get an authentication -+ * token first. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmcp_create(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ const struct dpmcp_cfg *cfg, -+ uint16_t *token); -+ -+/** -+ * dpmcp_destroy() - Destroy the DPMCP object and release all its resources. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMCP object -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpmcp_destroy(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * dpmcp_reset() - Reset the DPMCP, returns the object to initial state. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMCP object -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmcp_reset(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/* IRQ */ -+/* IRQ Index */ -+#define DPMCP_IRQ_INDEX 0 -+/* irq event - Indicates that the link state changed */ -+#define DPMCP_IRQ_EVENT_CMD_DONE 0x00000001 -+ -+/** -+ * struct dpmcp_irq_cfg - IRQ configuration -+ * @paddr: Address that must be written to signal a message-based interrupt -+ * @val: Value to write into irq_addr address -+ * @irq_num: A user defined number associated with this IRQ -+ */ -+struct dpmcp_irq_cfg { -+ uint64_t paddr; -+ uint32_t val; -+ int irq_num; -+}; -+ -+/** -+ * dpmcp_set_irq() - Set IRQ information for the DPMCP to trigger an interrupt. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMCP object -+ * @irq_index: Identifies the interrupt index to configure -+ * @irq_cfg: IRQ configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmcp_set_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ struct dpmcp_irq_cfg *irq_cfg); -+ -+/** -+ * dpmcp_get_irq() - Get IRQ information from the DPMCP. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMCP object -+ * @irq_index: The interrupt index to configure -+ * @type: Interrupt type: 0 represents message interrupt -+ * type (both irq_addr and irq_val are valid) -+ * @irq_cfg: IRQ attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmcp_get_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ int *type, -+ struct dpmcp_irq_cfg *irq_cfg); -+ -+/** -+ * dpmcp_set_irq_enable() - Set overall interrupt state. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMCP object -+ * @irq_index: The interrupt index to configure -+ * @en: Interrupt state - enable = 1, disable = 0 -+ * -+ * Allows GPP software to control when interrupts are generated. -+ * Each interrupt can have up to 32 causes. The enable/disable control's the -+ * overall interrupt state. if the interrupt is disabled no causes will cause -+ * an interrupt. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmcp_set_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t en); -+ -+/** -+ * dpmcp_get_irq_enable() - Get overall interrupt state -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMCP object -+ * @irq_index: The interrupt index to configure -+ * @en: Returned interrupt state - enable = 1, disable = 0 -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmcp_get_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t *en); -+ -+/** -+ * dpmcp_set_irq_mask() - Set interrupt mask. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMCP object -+ * @irq_index: The interrupt index to configure -+ * @mask: Event mask to trigger interrupt; -+ * each bit: -+ * 0 = ignore event -+ * 1 = consider event for asserting IRQ -+ * -+ * Every interrupt can have up to 32 causes and the interrupt model supports -+ * masking/unmasking each cause independently -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmcp_set_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t mask); -+ -+/** -+ * dpmcp_get_irq_mask() - Get interrupt mask. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMCP object -+ * @irq_index: The interrupt index to configure -+ * @mask: Returned event mask to trigger interrupt -+ * -+ * Every interrupt can have up to 32 causes and the interrupt model supports -+ * masking/unmasking each cause independently -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmcp_get_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *mask); -+ -+/** -+ * dpmcp_get_irq_status() - Get the current status of any pending interrupts. -+ * -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMCP object -+ * @irq_index: The interrupt index to configure -+ * @status: Returned interrupts status - one bit per cause: -+ * 0 = no interrupt pending -+ * 1 = interrupt pending -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmcp_get_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *status); -+ -+/** -+ * struct dpmcp_attr - Structure representing DPMCP attributes -+ * @id: DPMCP object ID -+ * @version: DPMCP version -+ */ -+struct dpmcp_attr { -+ int id; -+ /** -+ * struct version - Structure representing DPMCP version -+ * @major: DPMCP major version -+ * @minor: DPMCP minor version -+ */ -+ struct { -+ uint16_t major; -+ uint16_t minor; -+ } version; -+}; -+ -+/** -+ * dpmcp_get_attributes - Retrieve DPMCP attributes. -+ * -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPMCP object -+ * @attr: Returned object's attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmcp_get_attributes(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpmcp_attr *attr); -+ -+#endif /* __FSL_DPMCP_H */ -diff --git a/drivers/staging/fsl-mc/bus/dpmng-cmd.h b/drivers/staging/fsl-mc/bus/dpmng-cmd.h -new file mode 100644 -index 0000000..ba8cfa9 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpmng-cmd.h -@@ -0,0 +1,47 @@ -+/* Copyright 2013-2014 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+ -+/*************************************************************************//* -+ dpmng-cmd.h -+ -+ defines portal commands -+ -+ *//**************************************************************************/ -+ -+#ifndef __FSL_DPMNG_CMD_H -+#define __FSL_DPMNG_CMD_H -+ -+/* Command IDs */ -+#define DPMNG_CMDID_GET_CONT_ID 0x830 -+#define DPMNG_CMDID_GET_VERSION 0x831 -+ -+#endif /* __FSL_DPMNG_CMD_H */ -diff --git a/drivers/staging/fsl-mc/bus/dpmng.c b/drivers/staging/fsl-mc/bus/dpmng.c -new file mode 100644 -index 0000000..387390b ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dpmng.c -@@ -0,0 +1,85 @@ -+/* Copyright 2013-2014 Freescale Semiconductor Inc. -+* -+* Redistribution and use in source and binary forms, with or without -+* modification, are permitted provided that the following conditions are met: -+* * Redistributions of source code must retain the above copyright -+* notice, this list of conditions and the following disclaimer. -+* * 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. -+* * Neither the name of the above-listed copyright holders nor the -+* names of any contributors may be used to endorse or promote products -+* derived from this software without specific prior written permission. -+* -+* -+* ALTERNATIVELY, this software may be distributed under the terms of the -+* GNU General Public License ("GPL") as published by the Free Software -+* Foundation, either version 2 of that License or (at your option) any -+* later version. -+* -+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+*/ -+#include "../include/mc-sys.h" -+#include "../include/mc-cmd.h" -+#include "../include/dpmng.h" -+#include "dpmng-cmd.h" -+ -+int mc_get_version(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ struct mc_version *mc_ver_info) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMNG_CMDID_GET_VERSION, -+ cmd_flags, -+ 0); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ mc_ver_info->revision = mc_dec(cmd.params[0], 0, 32); -+ mc_ver_info->major = mc_dec(cmd.params[0], 32, 32); -+ mc_ver_info->minor = mc_dec(cmd.params[1], 0, 32); -+ -+ return 0; -+} -+EXPORT_SYMBOL(mc_get_version); -+ -+int dpmng_get_container_id(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ int *container_id) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPMNG_CMDID_GET_CONT_ID, -+ cmd_flags, -+ 0); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *container_id = mc_dec(cmd.params[0], 0, 32); -+ -+ return 0; -+} -+ -diff --git a/drivers/staging/fsl-mc/bus/dprc-cmd.h b/drivers/staging/fsl-mc/bus/dprc-cmd.h -new file mode 100644 -index 0000000..9b854fa ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dprc-cmd.h -@@ -0,0 +1,87 @@ -+/* Copyright 2013-2014 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+ -+/*************************************************************************//* -+ dprc-cmd.h -+ -+ defines dprc portal commands -+ -+ *//**************************************************************************/ -+ -+#ifndef _FSL_DPRC_CMD_H -+#define _FSL_DPRC_CMD_H -+ -+/* Minimal supported DPRC Version */ -+#define DPRC_MIN_VER_MAJOR 5 -+#define DPRC_MIN_VER_MINOR 0 -+ -+/* Command IDs */ -+#define DPRC_CMDID_CLOSE 0x800 -+#define DPRC_CMDID_OPEN 0x805 -+#define DPRC_CMDID_CREATE 0x905 -+ -+#define DPRC_CMDID_GET_ATTR 0x004 -+#define DPRC_CMDID_RESET_CONT 0x005 -+ -+#define DPRC_CMDID_SET_IRQ 0x010 -+#define DPRC_CMDID_GET_IRQ 0x011 -+#define DPRC_CMDID_SET_IRQ_ENABLE 0x012 -+#define DPRC_CMDID_GET_IRQ_ENABLE 0x013 -+#define DPRC_CMDID_SET_IRQ_MASK 0x014 -+#define DPRC_CMDID_GET_IRQ_MASK 0x015 -+#define DPRC_CMDID_GET_IRQ_STATUS 0x016 -+#define DPRC_CMDID_CLEAR_IRQ_STATUS 0x017 -+ -+#define DPRC_CMDID_CREATE_CONT 0x151 -+#define DPRC_CMDID_DESTROY_CONT 0x152 -+#define DPRC_CMDID_SET_RES_QUOTA 0x155 -+#define DPRC_CMDID_GET_RES_QUOTA 0x156 -+#define DPRC_CMDID_ASSIGN 0x157 -+#define DPRC_CMDID_UNASSIGN 0x158 -+#define DPRC_CMDID_GET_OBJ_COUNT 0x159 -+#define DPRC_CMDID_GET_OBJ 0x15A -+#define DPRC_CMDID_GET_RES_COUNT 0x15B -+#define DPRC_CMDID_GET_RES_IDS 0x15C -+#define DPRC_CMDID_GET_OBJ_REG 0x15E -+#define DPRC_CMDID_SET_OBJ_IRQ 0x15F -+#define DPRC_CMDID_GET_OBJ_IRQ 0x160 -+#define DPRC_CMDID_SET_OBJ_LABEL 0x161 -+#define DPRC_CMDID_GET_OBJ_DESC 0x162 -+ -+#define DPRC_CMDID_CONNECT 0x167 -+#define DPRC_CMDID_DISCONNECT 0x168 -+#define DPRC_CMDID_GET_POOL 0x169 -+#define DPRC_CMDID_GET_POOL_COUNT 0x16A -+ -+#define DPRC_CMDID_GET_CONNECTION 0x16C -+ -+#endif /* _FSL_DPRC_CMD_H */ -diff --git a/drivers/staging/fsl-mc/bus/dprc-driver.c b/drivers/staging/fsl-mc/bus/dprc-driver.c -new file mode 100644 -index 0000000..f8d8cbe ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dprc-driver.c -@@ -0,0 +1,1084 @@ -+/* -+ * Freescale data path resource container (DPRC) driver -+ * -+ * Copyright (C) 2014 Freescale Semiconductor, Inc. -+ * Author: German Rivera -+ * -+ * This file is licensed under the terms of the GNU General Public -+ * License version 2. This program is licensed "as is" without any -+ * warranty of any kind, whether express or implied. -+ */ -+ -+#include "../include/mc-private.h" -+#include "../include/mc-sys.h" -+#include -+#include -+#include -+#include "dprc-cmd.h" -+#include "dpmcp.h" -+ -+struct dprc_child_objs { -+ int child_count; -+ struct dprc_obj_desc *child_array; -+}; -+ -+static int __fsl_mc_device_remove_if_not_in_mc(struct device *dev, void *data) -+{ -+ int i; -+ struct dprc_child_objs *objs; -+ struct fsl_mc_device *mc_dev; -+ -+ WARN_ON(!dev); -+ WARN_ON(!data); -+ mc_dev = to_fsl_mc_device(dev); -+ objs = data; -+ -+ for (i = 0; i < objs->child_count; i++) { -+ struct dprc_obj_desc *obj_desc = &objs->child_array[i]; -+ -+ if (strlen(obj_desc->type) != 0 && -+ FSL_MC_DEVICE_MATCH(mc_dev, obj_desc)) -+ break; -+ } -+ -+ if (i == objs->child_count) -+ fsl_mc_device_remove(mc_dev); -+ -+ return 0; -+} -+ -+static int __fsl_mc_device_remove(struct device *dev, void *data) -+{ -+ WARN_ON(!dev); -+ WARN_ON(data); -+ fsl_mc_device_remove(to_fsl_mc_device(dev)); -+ return 0; -+} -+ -+/** -+ * dprc_remove_devices - Removes devices for objects removed from a DPRC -+ * -+ * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object -+ * @obj_desc_array: array of object descriptors for child objects currently -+ * present in the DPRC in the MC. -+ * @num_child_objects_in_mc: number of entries in obj_desc_array -+ * -+ * Synchronizes the state of the Linux bus driver with the actual state of -+ * the MC by removing devices that represent MC objects that have -+ * been dynamically removed in the physical DPRC. -+ */ -+static void dprc_remove_devices(struct fsl_mc_device *mc_bus_dev, -+ struct dprc_obj_desc *obj_desc_array, -+ int num_child_objects_in_mc) -+{ -+ if (num_child_objects_in_mc != 0) { -+ /* -+ * Remove child objects that are in the DPRC in Linux, -+ * but not in the MC: -+ */ -+ struct dprc_child_objs objs; -+ -+ objs.child_count = num_child_objects_in_mc; -+ objs.child_array = obj_desc_array; -+ device_for_each_child(&mc_bus_dev->dev, &objs, -+ __fsl_mc_device_remove_if_not_in_mc); -+ } else { -+ /* -+ * There are no child objects for this DPRC in the MC. -+ * So, remove all the child devices from Linux: -+ */ -+ device_for_each_child(&mc_bus_dev->dev, NULL, -+ __fsl_mc_device_remove); -+ } -+} -+ -+static int __fsl_mc_device_match(struct device *dev, void *data) -+{ -+ struct dprc_obj_desc *obj_desc = data; -+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); -+ -+ return FSL_MC_DEVICE_MATCH(mc_dev, obj_desc); -+} -+ -+static struct fsl_mc_device *fsl_mc_device_lookup(struct dprc_obj_desc -+ *obj_desc, -+ struct fsl_mc_device -+ *mc_bus_dev) -+{ -+ struct device *dev; -+ -+ dev = device_find_child(&mc_bus_dev->dev, obj_desc, -+ __fsl_mc_device_match); -+ -+ return dev ? to_fsl_mc_device(dev) : NULL; -+} -+ -+/** -+ * check_plugged_state_change - Check change in an MC object's plugged state -+ * -+ * @mc_dev: pointer to the fsl-mc device for a given MC object -+ * @obj_desc: pointer to the MC object's descriptor in the MC -+ * -+ * If the plugged state has changed from unplugged to plugged, the fsl-mc -+ * device is bound to the corresponding device driver. -+ * If the plugged state has changed from plugged to unplugged, the fsl-mc -+ * device is unbound from the corresponding device driver. -+ */ -+static void check_plugged_state_change(struct fsl_mc_device *mc_dev, -+ struct dprc_obj_desc *obj_desc) -+{ -+ int error; -+ uint32_t plugged_flag_at_mc = -+ (obj_desc->state & DPRC_OBJ_STATE_PLUGGED); -+ -+ if (plugged_flag_at_mc != -+ (mc_dev->obj_desc.state & DPRC_OBJ_STATE_PLUGGED)) { -+ if (plugged_flag_at_mc) { -+ mc_dev->obj_desc.state |= DPRC_OBJ_STATE_PLUGGED; -+ error = device_attach(&mc_dev->dev); -+ if (error < 0) { -+ dev_err(&mc_dev->dev, -+ "device_attach() failed: %d\n", -+ error); -+ } -+ } else { -+ mc_dev->obj_desc.state &= ~DPRC_OBJ_STATE_PLUGGED; -+ device_release_driver(&mc_dev->dev); -+ } -+ } -+} -+ -+/** -+ * dprc_add_new_devices - Adds devices to the logical bus for a DPRC -+ * -+ * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object -+ * @driver_override: driver override to apply to new objects found in the DPRC, -+ * or NULL, if none. -+ * @obj_desc_array: array of device descriptors for child devices currently -+ * present in the physical DPRC. -+ * @num_child_objects_in_mc: number of entries in obj_desc_array -+ * -+ * Synchronizes the state of the Linux bus driver with the actual -+ * state of the MC by adding objects that have been newly discovered -+ * in the physical DPRC. -+ */ -+static void dprc_add_new_devices(struct fsl_mc_device *mc_bus_dev, -+ const char *driver_override, -+ struct dprc_obj_desc *obj_desc_array, -+ int num_child_objects_in_mc) -+{ -+ int error; -+ int i; -+ -+ for (i = 0; i < num_child_objects_in_mc; i++) { -+ struct fsl_mc_device *child_dev; -+ struct dprc_obj_desc *obj_desc = &obj_desc_array[i]; -+ -+ if (strlen(obj_desc->type) == 0) -+ continue; -+ -+ /* -+ * Check if device is already known to Linux: -+ */ -+ child_dev = fsl_mc_device_lookup(obj_desc, mc_bus_dev); -+ if (child_dev) { -+ check_plugged_state_change(child_dev, obj_desc); -+ continue; -+ } -+ -+ error = fsl_mc_device_add(obj_desc, NULL, &mc_bus_dev->dev, -+ driver_override, &child_dev); -+ if (error < 0) -+ continue; -+ } -+} -+ -+void dprc_init_all_resource_pools(struct fsl_mc_device *mc_bus_dev) -+{ -+ int pool_type; -+ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); -+ -+ for (pool_type = 0; pool_type < FSL_MC_NUM_POOL_TYPES; pool_type++) { -+ struct fsl_mc_resource_pool *res_pool = -+ &mc_bus->resource_pools[pool_type]; -+ -+ res_pool->type = pool_type; -+ res_pool->max_count = 0; -+ res_pool->free_count = 0; -+ res_pool->mc_bus = mc_bus; -+ INIT_LIST_HEAD(&res_pool->free_list); -+ mutex_init(&res_pool->mutex); -+ } -+} -+ -+static void dprc_cleanup_resource_pool(struct fsl_mc_device *mc_bus_dev, -+ enum fsl_mc_pool_type pool_type) -+{ -+ struct fsl_mc_resource *resource; -+ struct fsl_mc_resource *next; -+ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); -+ struct fsl_mc_resource_pool *res_pool = -+ &mc_bus->resource_pools[pool_type]; -+ int free_count = 0; -+ -+ WARN_ON(res_pool->type != pool_type); -+ WARN_ON(res_pool->free_count != res_pool->max_count); -+ -+ list_for_each_entry_safe(resource, next, &res_pool->free_list, node) { -+ free_count++; -+ WARN_ON(resource->type != res_pool->type); -+ WARN_ON(resource->parent_pool != res_pool); -+ devm_kfree(&mc_bus_dev->dev, resource); -+ } -+ -+ WARN_ON(free_count != res_pool->free_count); -+} -+ -+/* -+ * Clean up all resource pools other than the IRQ pool -+ */ -+void dprc_cleanup_all_resource_pools(struct fsl_mc_device *mc_bus_dev) -+{ -+ int pool_type; -+ -+ for (pool_type = 0; pool_type < FSL_MC_NUM_POOL_TYPES; pool_type++) { -+ if (pool_type != FSL_MC_POOL_IRQ) -+ dprc_cleanup_resource_pool(mc_bus_dev, pool_type); -+ } -+} -+ -+/** -+ * dprc_scan_objects - Discover objects in a DPRC -+ * -+ * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object -+ * @driver_override: driver override to apply to new objects found in the DPRC, -+ * or NULL, if none. -+ * @total_irq_count: total number of IRQs needed by objects in the DPRC. -+ * -+ * Detects objects added and removed from a DPRC and synchronizes the -+ * state of the Linux bus driver, MC by adding and removing -+ * devices accordingly. -+ * Two types of devices can be found in a DPRC: allocatable objects (e.g., -+ * dpbp, dpmcp) and non-allocatable devices (e.g., dprc, dpni). -+ * All allocatable devices needed to be probed before all non-allocatable -+ * devices, to ensure that device drivers for non-allocatable -+ * devices can allocate any type of allocatable devices. -+ * That is, we need to ensure that the corresponding resource pools are -+ * populated before they can get allocation requests from probe callbacks -+ * of the device drivers for the non-allocatable devices. -+ */ -+int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev, -+ const char *driver_override, -+ unsigned int *total_irq_count) -+{ -+ int num_child_objects; -+ int dprc_get_obj_failures; -+ int error; -+ unsigned int irq_count = mc_bus_dev->obj_desc.irq_count; -+ struct dprc_obj_desc *child_obj_desc_array = NULL; -+ -+ error = dprc_get_obj_count(mc_bus_dev->mc_io, -+ 0, -+ mc_bus_dev->mc_handle, -+ &num_child_objects); -+ if (error < 0) { -+ dev_err(&mc_bus_dev->dev, "dprc_get_obj_count() failed: %d\n", -+ error); -+ return error; -+ } -+ -+ if (num_child_objects != 0) { -+ int i; -+ -+ child_obj_desc_array = -+ devm_kmalloc_array(&mc_bus_dev->dev, num_child_objects, -+ sizeof(*child_obj_desc_array), -+ GFP_KERNEL); -+ if (!child_obj_desc_array) -+ return -ENOMEM; -+ -+ /* -+ * Discover objects currently present in the physical DPRC: -+ */ -+ dprc_get_obj_failures = 0; -+ for (i = 0; i < num_child_objects; i++) { -+ struct dprc_obj_desc *obj_desc = -+ &child_obj_desc_array[i]; -+ -+ error = dprc_get_obj(mc_bus_dev->mc_io, -+ 0, -+ mc_bus_dev->mc_handle, -+ i, obj_desc); -+ -+ /* -+ * -ENXIO means object index was invalid. -+ * This is caused when the DPRC was changed at -+ * the MC during the scan. In this case, -+ * abort the current scan. -+ */ -+ if (error == -ENXIO) -+ return error; -+ -+ if (error < 0) { -+ dev_err(&mc_bus_dev->dev, -+ "dprc_get_obj(i=%d) failed: %d\n", -+ i, error); -+ /* -+ * Mark the obj entry as "invalid", by using the -+ * empty string as obj type: -+ */ -+ obj_desc->type[0] = '\0'; -+ obj_desc->id = error; -+ dprc_get_obj_failures++; -+ continue; -+ } -+ -+ /* -+ * for DPRC versions that do not support the -+ * shareability attribute, make simplifying assumption -+ * that only SEC is not shareable. -+ */ -+ if ((strcmp(obj_desc->type, "dpseci") == 0) && -+ (obj_desc->ver_major < 4)) -+ obj_desc->flags |= -+ DPRC_OBJ_FLAG_NO_MEM_SHAREABILITY; -+ -+ irq_count += obj_desc->irq_count; -+ dev_dbg(&mc_bus_dev->dev, -+ "Discovered object: type %s, id %d\n", -+ obj_desc->type, obj_desc->id); -+ } -+ -+ if (dprc_get_obj_failures != 0) { -+ dev_err(&mc_bus_dev->dev, -+ "%d out of %d devices could not be retrieved\n", -+ dprc_get_obj_failures, num_child_objects); -+ } -+ } -+ -+ *total_irq_count = irq_count; -+ dprc_remove_devices(mc_bus_dev, child_obj_desc_array, -+ num_child_objects); -+ -+ dprc_add_new_devices(mc_bus_dev, driver_override, child_obj_desc_array, -+ num_child_objects); -+ -+ if (child_obj_desc_array) -+ devm_kfree(&mc_bus_dev->dev, child_obj_desc_array); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(dprc_scan_objects); -+ -+/** -+ * dprc_scan_container - Scans a physical DPRC and synchronizes Linux bus state -+ * -+ * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object -+ * -+ * Scans the physical DPRC and synchronizes the state of the Linux -+ * bus driver with the actual state of the MC by adding and removing -+ * devices as appropriate. -+ */ -+static int dprc_scan_container(struct fsl_mc_device *mc_bus_dev) -+{ -+ int error; -+ unsigned int irq_count; -+ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); -+ -+ dprc_init_all_resource_pools(mc_bus_dev); -+ -+ /* -+ * Discover objects in the DPRC: -+ */ -+ mutex_lock(&mc_bus->scan_mutex); -+ error = dprc_scan_objects(mc_bus_dev, NULL, &irq_count); -+ mutex_unlock(&mc_bus->scan_mutex); -+ if (error < 0) -+ goto error; -+ -+ if (fsl_mc_interrupts_supported() && !mc_bus->irq_resources) { -+ irq_count += FSL_MC_IRQ_POOL_MAX_EXTRA_IRQS; -+ error = fsl_mc_populate_irq_pool(mc_bus, irq_count); -+ if (error < 0) -+ goto error; -+ } -+ -+ return 0; -+error: -+ device_for_each_child(&mc_bus_dev->dev, NULL, __fsl_mc_device_remove); -+ dprc_cleanup_all_resource_pools(mc_bus_dev); -+ return error; -+} -+ -+/** -+ * dprc_irq0_handler - Regular ISR for DPRC interrupt 0 -+ * -+ * @irq: IRQ number of the interrupt being handled -+ * @arg: Pointer to device structure -+ */ -+static irqreturn_t dprc_irq0_handler(int irq_num, void *arg) -+{ -+ return IRQ_WAKE_THREAD; -+} -+ -+/** -+ * dprc_irq0_handler_thread - Handler thread function for DPRC interrupt 0 -+ * -+ * @irq: IRQ number of the interrupt being handled -+ * @arg: Pointer to device structure -+ */ -+static irqreturn_t dprc_irq0_handler_thread(int irq_num, void *arg) -+{ -+ int error; -+ uint32_t status; -+ struct device *dev = (struct device *)arg; -+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); -+ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev); -+ struct fsl_mc_io *mc_io = mc_dev->mc_io; -+ int irq_index = 0; -+ -+ dev_dbg(dev, "DPRC IRQ %d triggered on CPU %u\n", -+ irq_num, smp_processor_id()); -+ if (WARN_ON(!(mc_dev->flags & FSL_MC_IS_DPRC))) -+ return IRQ_HANDLED; -+ -+ mutex_lock(&mc_bus->scan_mutex); -+ if (WARN_ON(mc_dev->irqs[irq_index]->irq_number != (uint32_t)irq_num)) -+ goto out; -+ -+ status = 0; -+ error = dprc_get_irq_status(mc_io, 0, mc_dev->mc_handle, irq_index, -+ &status); -+ if (error < 0) { -+ dev_err(dev, -+ "dprc_get_irq_status() failed: %d\n", error); -+ goto out; -+ } -+ -+ error = dprc_clear_irq_status(mc_io, 0, mc_dev->mc_handle, irq_index, -+ status); -+ if (error < 0) { -+ dev_err(dev, -+ "dprc_clear_irq_status() failed: %d\n", error); -+ goto out; -+ } -+ -+ if (status & (DPRC_IRQ_EVENT_OBJ_ADDED | -+ DPRC_IRQ_EVENT_OBJ_REMOVED | -+ DPRC_IRQ_EVENT_CONTAINER_DESTROYED | -+ DPRC_IRQ_EVENT_OBJ_DESTROYED | -+ DPRC_IRQ_EVENT_OBJ_CREATED)) { -+ unsigned int irq_count; -+ -+ error = dprc_scan_objects(mc_dev, NULL, &irq_count); -+ if (error < 0) { -+ if (error != -ENXIO) /* don't need to report aborted scan */ -+ dev_err(dev, "dprc_scan_objects() failed: %d\n", error); -+ goto out; -+ } -+ -+ WARN_ON((int16_t)irq_count < 0); -+ -+ if ((int16_t)irq_count > -+ mc_bus->resource_pools[FSL_MC_POOL_IRQ].max_count) { -+ dev_warn(dev, -+ "IRQs needed (%u) exceed IRQs preallocated (%u)\n", -+ irq_count, -+ mc_bus->resource_pools[FSL_MC_POOL_IRQ]. -+ max_count); -+ } -+ } -+ -+out: -+ mutex_unlock(&mc_bus->scan_mutex); -+ return IRQ_HANDLED; -+} -+ -+/* -+ * Disable and clear interrupts for a given DPRC object -+ */ -+static int disable_dprc_irqs(struct fsl_mc_device *mc_dev) -+{ -+ int i; -+ int error; -+ struct fsl_mc_io *mc_io = mc_dev->mc_io; -+ int irq_count = mc_dev->obj_desc.irq_count; -+ -+ if (WARN_ON(irq_count == 0)) -+ return -EINVAL; -+ -+ for (i = 0; i < irq_count; i++) { -+ /* -+ * Disable generation of interrupt i, while we configure it: -+ */ -+ error = dprc_set_irq_enable(mc_io, 0, mc_dev->mc_handle, i, 0); -+ if (error < 0) { -+ dev_err(&mc_dev->dev, -+ "Disabling DPRC IRQ %d failed: dprc_set_irq_enable() failed: %d\n", -+ i, error); -+ -+ return error; -+ } -+ -+ /* -+ * Disable all interrupt causes for interrupt i: -+ */ -+ error = dprc_set_irq_mask(mc_io, 0, mc_dev->mc_handle, i, 0x0); -+ if (error < 0) { -+ dev_err(&mc_dev->dev, -+ "Disabling DPRC IRQ %d failed: dprc_set_irq_mask() failed: %d\n", -+ i, error); -+ -+ return error; -+ } -+ -+ /* -+ * Clear any leftover interrupt i: -+ */ -+ error = dprc_clear_irq_status(mc_io, 0, mc_dev->mc_handle, i, -+ ~0x0U); -+ if (error < 0) { -+ dev_err(&mc_dev->dev, -+ "Disabling DPRC IRQ %d failed: dprc_clear_irq_status() failed: %d\n", -+ i, error); -+ -+ return error; -+ } -+ } -+ -+ return 0; -+} -+ -+static void unregister_dprc_irq_handlers(struct fsl_mc_device *mc_dev) -+{ -+ int i; -+ struct fsl_mc_device_irq *irq; -+ int irq_count = mc_dev->obj_desc.irq_count; -+ -+ for (i = 0; i < irq_count; i++) { -+ irq = mc_dev->irqs[i]; -+ devm_free_irq(&mc_dev->dev, irq->irq_number, -+ &mc_dev->dev); -+ } -+} -+ -+static int register_dprc_irq_handlers(struct fsl_mc_device *mc_dev) -+{ -+ static const struct irq_handler { -+ irq_handler_t irq_handler; -+ irq_handler_t irq_handler_thread; -+ const char *irq_name; -+ } irq_handlers[] = { -+ [0] = { -+ .irq_handler = dprc_irq0_handler, -+ .irq_handler_thread = dprc_irq0_handler_thread, -+ .irq_name = "FSL MC DPRC irq0", -+ }, -+ }; -+ -+ unsigned int i; -+ int error; -+ struct fsl_mc_device_irq *irq; -+ unsigned int num_irq_handlers_registered = 0; -+ int irq_count = mc_dev->obj_desc.irq_count; -+ -+ if (WARN_ON(irq_count != ARRAY_SIZE(irq_handlers))) -+ return -EINVAL; -+ -+ for (i = 0; i < ARRAY_SIZE(irq_handlers); i++) { -+ irq = mc_dev->irqs[i]; -+ -+ /* -+ * NOTE: devm_request_threaded_irq() invokes the device-specific -+ * function that programs the MSI physically in the device -+ */ -+ error = devm_request_threaded_irq(&mc_dev->dev, -+ irq->irq_number, -+ irq_handlers[i].irq_handler, -+ irq_handlers[i]. -+ irq_handler_thread, -+ IRQF_NO_SUSPEND | -+ IRQF_ONESHOT, -+ irq_handlers[i].irq_name, -+ &mc_dev->dev); -+ if (error < 0) { -+ dev_err(&mc_dev->dev, -+ "devm_request_threaded_irq() failed: %d\n", -+ error); -+ goto error_unregister_irq_handlers; -+ } -+ -+ num_irq_handlers_registered++; -+ } -+ -+ return 0; -+ -+error_unregister_irq_handlers: -+ for (i = 0; i < num_irq_handlers_registered; i++) { -+ irq = mc_dev->irqs[i]; -+ devm_free_irq(&mc_dev->dev, irq->irq_number, -+ &mc_dev->dev); -+ } -+ -+ return error; -+} -+ -+static int enable_dprc_irqs(struct fsl_mc_device *mc_dev) -+{ -+ int i; -+ int error; -+ int irq_count = mc_dev->obj_desc.irq_count; -+ -+ for (i = 0; i < irq_count; i++) { -+ /* -+ * Enable all interrupt causes for the interrupt: -+ */ -+ error = dprc_set_irq_mask(mc_dev->mc_io, -+ 0, -+ mc_dev->mc_handle, -+ i, -+ ~0x0u); -+ if (error < 0) { -+ dev_err(&mc_dev->dev, -+ "Enabling DPRC IRQ %d failed: dprc_set_irq_mask() failed: %d\n", -+ i, error); -+ -+ return error; -+ } -+ -+ /* -+ * Enable generation of the interrupt: -+ */ -+ error = dprc_set_irq_enable(mc_dev->mc_io, -+ 0, -+ mc_dev->mc_handle, -+ i, 1); -+ if (error < 0) { -+ dev_err(&mc_dev->dev, -+ "Enabling DPRC IRQ %d failed: dprc_set_irq_enable() failed: %d\n", -+ i, error); -+ -+ return error; -+ } -+ } -+ -+ return 0; -+} -+ -+/* -+ * Setup interrupts for a given DPRC device -+ */ -+static int dprc_setup_irqs(struct fsl_mc_device *mc_dev) -+{ -+ int error; -+ -+ error = fsl_mc_allocate_irqs(mc_dev); -+ if (error < 0) -+ return error; -+ -+ error = disable_dprc_irqs(mc_dev); -+ if (error < 0) -+ goto error_free_irqs; -+ -+ error = register_dprc_irq_handlers(mc_dev); -+ if (error < 0) -+ goto error_free_irqs; -+ -+ error = enable_dprc_irqs(mc_dev); -+ if (error < 0) -+ goto error_unregister_irq_handlers; -+ -+ return 0; -+ -+error_unregister_irq_handlers: -+ unregister_dprc_irq_handlers(mc_dev); -+ -+error_free_irqs: -+ fsl_mc_free_irqs(mc_dev); -+ return error; -+} -+ -+/* -+ * Creates a DPMCP for a DPRC's built-in MC portal -+ */ -+static int dprc_create_dpmcp(struct fsl_mc_device *dprc_dev) -+{ -+ int error; -+ struct dpmcp_cfg dpmcp_cfg; -+ uint16_t dpmcp_handle; -+ struct dprc_res_req res_req; -+ struct dpmcp_attr dpmcp_attr; -+ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(dprc_dev); -+ -+ dpmcp_cfg.portal_id = mc_bus->dprc_attr.portal_id; -+ error = dpmcp_create(dprc_dev->mc_io, -+ MC_CMD_FLAG_INTR_DIS, -+ &dpmcp_cfg, -+ &dpmcp_handle); -+ if (error < 0) { -+ dev_err(&dprc_dev->dev, "dpmcp_create() failed: %d\n", -+ error); -+ return error; -+ } -+ -+ /* -+ * Set the state of the newly created DPMCP object to be "plugged": -+ */ -+ -+ error = dpmcp_get_attributes(dprc_dev->mc_io, -+ MC_CMD_FLAG_INTR_DIS, -+ dpmcp_handle, -+ &dpmcp_attr); -+ if (error < 0) { -+ dev_err(&dprc_dev->dev, "dpmcp_get_attributes() failed: %d\n", -+ error); -+ goto error_destroy_dpmcp; -+ } -+ -+ if (WARN_ON(dpmcp_attr.id != mc_bus->dprc_attr.portal_id)) { -+ error = -EINVAL; -+ goto error_destroy_dpmcp; -+ } -+ -+ strcpy(res_req.type, "dpmcp"); -+ res_req.num = 1; -+ res_req.options = -+ (DPRC_RES_REQ_OPT_EXPLICIT | DPRC_RES_REQ_OPT_PLUGGED); -+ res_req.id_base_align = dpmcp_attr.id; -+ -+ error = dprc_assign(dprc_dev->mc_io, -+ MC_CMD_FLAG_INTR_DIS, -+ dprc_dev->mc_handle, -+ dprc_dev->obj_desc.id, -+ &res_req); -+ -+ if (error < 0) { -+ dev_err(&dprc_dev->dev, "dprc_assign() failed: %d\n", error); -+ goto error_destroy_dpmcp; -+ } -+ -+ (void)dpmcp_close(dprc_dev->mc_io, -+ MC_CMD_FLAG_INTR_DIS, -+ dpmcp_handle); -+ return 0; -+ -+error_destroy_dpmcp: -+ (void)dpmcp_destroy(dprc_dev->mc_io, -+ MC_CMD_FLAG_INTR_DIS, -+ dpmcp_handle); -+ return error; -+} -+ -+/* -+ * Destroys the DPMCP for a DPRC's built-in MC portal -+ */ -+static void dprc_destroy_dpmcp(struct fsl_mc_device *dprc_dev) -+{ -+ int error; -+ uint16_t dpmcp_handle; -+ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(dprc_dev); -+ -+ if (WARN_ON(!dprc_dev->mc_io || dprc_dev->mc_io->dpmcp_dev)) -+ return; -+ -+ error = dpmcp_open(dprc_dev->mc_io, -+ MC_CMD_FLAG_INTR_DIS, -+ mc_bus->dprc_attr.portal_id, -+ &dpmcp_handle); -+ if (error < 0) { -+ dev_err(&dprc_dev->dev, "dpmcp_open() failed: %d\n", -+ error); -+ return; -+ } -+ -+ error = dpmcp_destroy(dprc_dev->mc_io, -+ MC_CMD_FLAG_INTR_DIS, -+ dpmcp_handle); -+ if (error < 0) { -+ dev_err(&dprc_dev->dev, "dpmcp_destroy() failed: %d\n", -+ error); -+ return; -+ } -+} -+ -+/** -+ * dprc_probe - callback invoked when a DPRC is being bound to this driver -+ * -+ * @mc_dev: Pointer to fsl-mc device representing a DPRC -+ * -+ * It opens the physical DPRC in the MC. -+ * It scans the DPRC to discover the MC objects contained in it. -+ * It creates the interrupt pool for the MC bus associated with the DPRC. -+ * It configures the interrupts for the DPRC device itself. -+ */ -+static int dprc_probe(struct fsl_mc_device *mc_dev) -+{ -+ int error; -+ size_t region_size; -+ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev); -+ bool mc_io_created = false; -+ bool dev_root_set = false; -+ -+ if (WARN_ON(strcmp(mc_dev->obj_desc.type, "dprc") != 0)) -+ return -EINVAL; -+ -+ if (mc_dev->mc_io) { -+ /* -+ * This is the root DPRC -+ */ -+ if (WARN_ON(fsl_mc_bus_type.dev_root)) -+ return -EINVAL; -+ -+ fsl_mc_bus_type.dev_root = &mc_dev->dev; -+ dev_root_set = true; -+ } else { -+ /* -+ * This is a child DPRC -+ */ -+ if (WARN_ON(!fsl_mc_bus_type.dev_root)) -+ return -EINVAL; -+ -+ if (WARN_ON(mc_dev->obj_desc.region_count == 0)) -+ return -EINVAL; -+ -+ region_size = mc_dev->regions[0].end - -+ mc_dev->regions[0].start + 1; -+ -+ error = fsl_create_mc_io(&mc_dev->dev, -+ mc_dev->regions[0].start, -+ region_size, -+ NULL, 0, &mc_dev->mc_io); -+ if (error < 0) -+ return error; -+ -+ mc_io_created = true; -+ } -+ -+ error = dprc_open(mc_dev->mc_io, 0, mc_dev->obj_desc.id, -+ &mc_dev->mc_handle); -+ if (error < 0) { -+ dev_err(&mc_dev->dev, "dprc_open() failed: %d\n", error); -+ goto error_cleanup_mc_io; -+ } -+ -+ error = dprc_get_attributes(mc_dev->mc_io, 0, mc_dev->mc_handle, -+ &mc_bus->dprc_attr); -+ if (error < 0) { -+ dev_err(&mc_dev->dev, "dprc_get_attributes() failed: %d\n", -+ error); -+ goto error_cleanup_open; -+ } -+ -+ if (mc_bus->dprc_attr.version.major < DPRC_MIN_VER_MAJOR || -+ (mc_bus->dprc_attr.version.major == DPRC_MIN_VER_MAJOR && -+ mc_bus->dprc_attr.version.minor < DPRC_MIN_VER_MINOR)) { -+ dev_err(&mc_dev->dev, -+ "ERROR: DPRC version %d.%d not supported\n", -+ mc_bus->dprc_attr.version.major, -+ mc_bus->dprc_attr.version.minor); -+ error = -ENOTSUPP; -+ goto error_cleanup_open; -+ } -+ -+ if (fsl_mc_interrupts_supported()) { -+ /* -+ * Create DPMCP for the DPRC's built-in portal: -+ */ -+ error = dprc_create_dpmcp(mc_dev); -+ if (error < 0) -+ goto error_cleanup_open; -+ } -+ -+ mutex_init(&mc_bus->scan_mutex); -+ -+ /* -+ * Discover MC objects in the DPRC object: -+ */ -+ error = dprc_scan_container(mc_dev); -+ if (error < 0) -+ goto error_destroy_dpmcp; -+ -+ if (fsl_mc_interrupts_supported()) { -+ /* -+ * The fsl_mc_device object associated with the DPMCP object -+ * created above was created as part of the -+ * dprc_scan_container() call above: -+ */ -+ if (WARN_ON(!mc_dev->mc_io->dpmcp_dev)) { -+ error = -EINVAL; -+ goto error_cleanup_dprc_scan; -+ } -+ -+ /* -+ * Allocate MC portal to be used in atomic context -+ * (e.g., to program MSIs from program_msi_at_mc()) -+ */ -+ error = fsl_mc_portal_allocate(NULL, -+ FSL_MC_IO_ATOMIC_CONTEXT_PORTAL, -+ &mc_bus->atomic_mc_io); -+ if (error < 0) -+ goto error_cleanup_dprc_scan; -+ -+ pr_info("fsl-mc: Allocated dpmcp.%d to dprc.%d for atomic MC I/O\n", -+ mc_bus->atomic_mc_io->dpmcp_dev->obj_desc.id, -+ mc_dev->obj_desc.id); -+ -+ /* -+ * Open DPRC handle to be used with mc_bus->atomic_mc_io: -+ */ -+ error = dprc_open(mc_bus->atomic_mc_io, 0, mc_dev->obj_desc.id, -+ &mc_bus->atomic_dprc_handle); -+ if (error < 0) { -+ dev_err(&mc_dev->dev, "dprc_open() failed: %d\n", -+ error); -+ goto error_cleanup_atomic_mc_io; -+ } -+ -+ /* -+ * Configure interrupt for the DPMCP object associated with the -+ * DPRC object's built-in portal: -+ * -+ * NOTE: We have to do this after calling dprc_scan_container(), -+ * since dprc_scan_container() populates the IRQ pool for -+ * this DPRC. -+ */ -+ error = fsl_mc_io_setup_dpmcp_irq(mc_dev->mc_io); -+ if (error < 0) -+ goto error_cleanup_atomic_dprc_handle; -+ -+ /* -+ * Configure interrupts for the DPRC object associated with -+ * this MC bus: -+ */ -+ error = dprc_setup_irqs(mc_dev); -+ if (error < 0) -+ goto error_cleanup_atomic_dprc_handle; -+ } -+ -+ dev_info(&mc_dev->dev, "DPRC device bound to driver"); -+ return 0; -+ -+error_cleanup_atomic_dprc_handle: -+ (void)dprc_close(mc_bus->atomic_mc_io, 0, mc_bus->atomic_dprc_handle); -+ -+error_cleanup_atomic_mc_io: -+ fsl_mc_portal_free(mc_bus->atomic_mc_io); -+ -+error_cleanup_dprc_scan: -+ fsl_mc_io_unset_dpmcp(mc_dev->mc_io); -+ device_for_each_child(&mc_dev->dev, NULL, __fsl_mc_device_remove); -+ dprc_cleanup_all_resource_pools(mc_dev); -+ if (fsl_mc_interrupts_supported()) -+ fsl_mc_cleanup_irq_pool(mc_bus); -+ -+error_destroy_dpmcp: -+ dprc_destroy_dpmcp(mc_dev); -+ -+error_cleanup_open: -+ (void)dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle); -+ -+error_cleanup_mc_io: -+ if (mc_io_created) { -+ fsl_destroy_mc_io(mc_dev->mc_io); -+ mc_dev->mc_io = NULL; -+ } -+ -+ if (dev_root_set) -+ fsl_mc_bus_type.dev_root = NULL; -+ -+ return error; -+} -+ -+/* -+ * Tear down interrupts for a given DPRC object -+ */ -+static void dprc_teardown_irqs(struct fsl_mc_device *mc_dev) -+{ -+ (void)disable_dprc_irqs(mc_dev); -+ unregister_dprc_irq_handlers(mc_dev); -+ fsl_mc_free_irqs(mc_dev); -+} -+ -+/** -+ * dprc_remove - callback invoked when a DPRC is being unbound from this driver -+ * -+ * @mc_dev: Pointer to fsl-mc device representing the DPRC -+ * -+ * It removes the DPRC's child objects from Linux (not from the MC) and -+ * closes the DPRC device in the MC. -+ * It tears down the interrupts that were configured for the DPRC device. -+ * It destroys the interrupt pool associated with this MC bus. -+ */ -+static int dprc_remove(struct fsl_mc_device *mc_dev) -+{ -+ int error; -+ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev); -+ -+ if (WARN_ON(strcmp(mc_dev->obj_desc.type, "dprc") != 0)) -+ return -EINVAL; -+ if (WARN_ON(!mc_dev->mc_io)) -+ return -EINVAL; -+ -+ if (WARN_ON(!mc_bus->irq_resources)) -+ return -EINVAL; -+ -+ if (fsl_mc_interrupts_supported()) { -+ dprc_teardown_irqs(mc_dev); -+ error = dprc_close(mc_bus->atomic_mc_io, 0, -+ mc_bus->atomic_dprc_handle); -+ if (error < 0) { -+ dev_err(&mc_dev->dev, "dprc_close() failed: %d\n", -+ error); -+ } -+ -+ fsl_mc_portal_free(mc_bus->atomic_mc_io); -+ } -+ -+ fsl_mc_io_unset_dpmcp(mc_dev->mc_io); -+ device_for_each_child(&mc_dev->dev, NULL, __fsl_mc_device_remove); -+ dprc_cleanup_all_resource_pools(mc_dev); -+ dprc_destroy_dpmcp(mc_dev); -+ error = dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle); -+ if (error < 0) -+ dev_err(&mc_dev->dev, "dprc_close() failed: %d\n", error); -+ -+ if (fsl_mc_interrupts_supported()) -+ fsl_mc_cleanup_irq_pool(mc_bus); -+ -+ fsl_destroy_mc_io(mc_dev->mc_io); -+ mc_dev->mc_io = NULL; -+ -+ if (&mc_dev->dev == fsl_mc_bus_type.dev_root) -+ fsl_mc_bus_type.dev_root = NULL; -+ -+ dev_info(&mc_dev->dev, "DPRC device unbound from driver"); -+ return 0; -+} -+ -+static const struct fsl_mc_device_match_id match_id_table[] = { -+ { -+ .vendor = FSL_MC_VENDOR_FREESCALE, -+ .obj_type = "dprc"}, -+ {.vendor = 0x0}, -+}; -+ -+static struct fsl_mc_driver dprc_driver = { -+ .driver = { -+ .name = FSL_MC_DPRC_DRIVER_NAME, -+ .owner = THIS_MODULE, -+ .pm = NULL, -+ }, -+ .match_id_table = match_id_table, -+ .probe = dprc_probe, -+ .remove = dprc_remove, -+}; -+ -+int __init dprc_driver_init(void) -+{ -+ return fsl_mc_driver_register(&dprc_driver); -+} -+ -+void __exit dprc_driver_exit(void) -+{ -+ fsl_mc_driver_unregister(&dprc_driver); -+} -diff --git a/drivers/staging/fsl-mc/bus/dprc.c b/drivers/staging/fsl-mc/bus/dprc.c -new file mode 100644 -index 0000000..4d86438 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/dprc.c -@@ -0,0 +1,1218 @@ -+/* Copyright 2013-2014 Freescale Semiconductor Inc. -+* -+* Redistribution and use in source and binary forms, with or without -+* modification, are permitted provided that the following conditions are met: -+* * Redistributions of source code must retain the above copyright -+* notice, this list of conditions and the following disclaimer. -+* * 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. -+* * Neither the name of the above-listed copyright holders nor the -+* names of any contributors may be used to endorse or promote products -+* derived from this software without specific prior written permission. -+* -+* -+* ALTERNATIVELY, this software may be distributed under the terms of the -+* GNU General Public License ("GPL") as published by the Free Software -+* Foundation, either version 2 of that License or (at your option) any -+* later version. -+* -+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+*/ -+#include "../include/mc-sys.h" -+#include "../include/mc-cmd.h" -+#include "../include/dprc.h" -+#include "dprc-cmd.h" -+ -+int dprc_open(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ int container_id, -+ uint16_t *token) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_OPEN, cmd_flags, -+ 0); -+ cmd.params[0] |= mc_enc(0, 32, container_id); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *token = MC_CMD_HDR_READ_TOKEN(cmd.header); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dprc_open); -+ -+int dprc_close(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_CLOSE, cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dprc_close); -+ -+int dprc_create_container(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dprc_cfg *cfg, -+ int *child_container_id, -+ uint64_t *child_portal_offset) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.params[0] |= mc_enc(32, 16, cfg->icid); -+ cmd.params[0] |= mc_enc(0, 32, cfg->options); -+ cmd.params[1] |= mc_enc(32, 32, cfg->portal_id); -+ cmd.params[2] |= mc_enc(0, 8, cfg->label[0]); -+ cmd.params[2] |= mc_enc(8, 8, cfg->label[1]); -+ cmd.params[2] |= mc_enc(16, 8, cfg->label[2]); -+ cmd.params[2] |= mc_enc(24, 8, cfg->label[3]); -+ cmd.params[2] |= mc_enc(32, 8, cfg->label[4]); -+ cmd.params[2] |= mc_enc(40, 8, cfg->label[5]); -+ cmd.params[2] |= mc_enc(48, 8, cfg->label[6]); -+ cmd.params[2] |= mc_enc(56, 8, cfg->label[7]); -+ cmd.params[3] |= mc_enc(0, 8, cfg->label[8]); -+ cmd.params[3] |= mc_enc(8, 8, cfg->label[9]); -+ cmd.params[3] |= mc_enc(16, 8, cfg->label[10]); -+ cmd.params[3] |= mc_enc(24, 8, cfg->label[11]); -+ cmd.params[3] |= mc_enc(32, 8, cfg->label[12]); -+ cmd.params[3] |= mc_enc(40, 8, cfg->label[13]); -+ cmd.params[3] |= mc_enc(48, 8, cfg->label[14]); -+ cmd.params[3] |= mc_enc(56, 8, cfg->label[15]); -+ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_CREATE_CONT, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *child_container_id = mc_dec(cmd.params[1], 0, 32); -+ *child_portal_offset = mc_dec(cmd.params[2], 0, 64); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dprc_create_container); -+ -+int dprc_destroy_container(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int child_container_id) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_DESTROY_CONT, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, child_container_id); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dprc_destroy_container); -+ -+int dprc_reset_container(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int child_container_id) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_RESET_CONT, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, child_container_id); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dprc_reset_container); -+ -+int dprc_get_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ int *type, -+ struct dprc_irq_cfg *irq_cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_IRQ, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ irq_cfg->val = mc_dec(cmd.params[0], 0, 32); -+ irq_cfg->paddr = mc_dec(cmd.params[1], 0, 64); -+ irq_cfg->irq_num = mc_dec(cmd.params[2], 0, 32); -+ *type = mc_dec(cmd.params[2], 32, 32); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dprc_get_irq); -+ -+int dprc_set_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ struct dprc_irq_cfg *irq_cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_SET_IRQ, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ cmd.params[0] |= mc_enc(0, 32, irq_cfg->val); -+ cmd.params[1] |= mc_enc(0, 64, irq_cfg->paddr); -+ cmd.params[2] |= mc_enc(0, 32, irq_cfg->irq_num); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dprc_set_irq); -+ -+int dprc_get_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t *en) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_IRQ_ENABLE, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *en = mc_dec(cmd.params[0], 0, 8); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dprc_get_irq_enable); -+ -+int dprc_set_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t en) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_SET_IRQ_ENABLE, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 8, en); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dprc_set_irq_enable); -+ -+int dprc_get_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *mask) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_IRQ_MASK, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *mask = mc_dec(cmd.params[0], 0, 32); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dprc_get_irq_mask); -+ -+int dprc_set_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t mask) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_SET_IRQ_MASK, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, mask); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dprc_set_irq_mask); -+ -+int dprc_get_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *status) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_IRQ_STATUS, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, *status); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *status = mc_dec(cmd.params[0], 0, 32); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dprc_get_irq_status); -+ -+int dprc_clear_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t status) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_CLEAR_IRQ_STATUS, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, status); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dprc_clear_irq_status); -+ -+int dprc_get_attributes(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dprc_attributes *attr) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_ATTR, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ attr->container_id = mc_dec(cmd.params[0], 0, 32); -+ attr->icid = mc_dec(cmd.params[0], 32, 16); -+ attr->options = mc_dec(cmd.params[1], 0, 32); -+ attr->portal_id = mc_dec(cmd.params[1], 32, 32); -+ attr->version.major = mc_dec(cmd.params[2], 0, 16); -+ attr->version.minor = mc_dec(cmd.params[2], 16, 16); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dprc_get_attributes); -+ -+int dprc_set_res_quota(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int child_container_id, -+ char *type, -+ uint16_t quota) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_SET_RES_QUOTA, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, child_container_id); -+ cmd.params[0] |= mc_enc(32, 16, quota); -+ cmd.params[1] |= mc_enc(0, 8, type[0]); -+ cmd.params[1] |= mc_enc(8, 8, type[1]); -+ cmd.params[1] |= mc_enc(16, 8, type[2]); -+ cmd.params[1] |= mc_enc(24, 8, type[3]); -+ cmd.params[1] |= mc_enc(32, 8, type[4]); -+ cmd.params[1] |= mc_enc(40, 8, type[5]); -+ cmd.params[1] |= mc_enc(48, 8, type[6]); -+ cmd.params[1] |= mc_enc(56, 8, type[7]); -+ cmd.params[2] |= mc_enc(0, 8, type[8]); -+ cmd.params[2] |= mc_enc(8, 8, type[9]); -+ cmd.params[2] |= mc_enc(16, 8, type[10]); -+ cmd.params[2] |= mc_enc(24, 8, type[11]); -+ cmd.params[2] |= mc_enc(32, 8, type[12]); -+ cmd.params[2] |= mc_enc(40, 8, type[13]); -+ cmd.params[2] |= mc_enc(48, 8, type[14]); -+ cmd.params[2] |= mc_enc(56, 8, '\0'); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dprc_set_res_quota); -+ -+int dprc_get_res_quota(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int child_container_id, -+ char *type, -+ uint16_t *quota) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_RES_QUOTA, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, child_container_id); -+ cmd.params[1] |= mc_enc(0, 8, type[0]); -+ cmd.params[1] |= mc_enc(8, 8, type[1]); -+ cmd.params[1] |= mc_enc(16, 8, type[2]); -+ cmd.params[1] |= mc_enc(24, 8, type[3]); -+ cmd.params[1] |= mc_enc(32, 8, type[4]); -+ cmd.params[1] |= mc_enc(40, 8, type[5]); -+ cmd.params[1] |= mc_enc(48, 8, type[6]); -+ cmd.params[1] |= mc_enc(56, 8, type[7]); -+ cmd.params[2] |= mc_enc(0, 8, type[8]); -+ cmd.params[2] |= mc_enc(8, 8, type[9]); -+ cmd.params[2] |= mc_enc(16, 8, type[10]); -+ cmd.params[2] |= mc_enc(24, 8, type[11]); -+ cmd.params[2] |= mc_enc(32, 8, type[12]); -+ cmd.params[2] |= mc_enc(40, 8, type[13]); -+ cmd.params[2] |= mc_enc(48, 8, type[14]); -+ cmd.params[2] |= mc_enc(56, 8, '\0'); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *quota = mc_dec(cmd.params[0], 32, 16); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dprc_get_res_quota); -+ -+int dprc_assign(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int container_id, -+ struct dprc_res_req *res_req) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_ASSIGN, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, container_id); -+ cmd.params[0] |= mc_enc(32, 32, res_req->options); -+ cmd.params[1] |= mc_enc(0, 32, res_req->num); -+ cmd.params[1] |= mc_enc(32, 32, res_req->id_base_align); -+ cmd.params[2] |= mc_enc(0, 8, res_req->type[0]); -+ cmd.params[2] |= mc_enc(8, 8, res_req->type[1]); -+ cmd.params[2] |= mc_enc(16, 8, res_req->type[2]); -+ cmd.params[2] |= mc_enc(24, 8, res_req->type[3]); -+ cmd.params[2] |= mc_enc(32, 8, res_req->type[4]); -+ cmd.params[2] |= mc_enc(40, 8, res_req->type[5]); -+ cmd.params[2] |= mc_enc(48, 8, res_req->type[6]); -+ cmd.params[2] |= mc_enc(56, 8, res_req->type[7]); -+ cmd.params[3] |= mc_enc(0, 8, res_req->type[8]); -+ cmd.params[3] |= mc_enc(8, 8, res_req->type[9]); -+ cmd.params[3] |= mc_enc(16, 8, res_req->type[10]); -+ cmd.params[3] |= mc_enc(24, 8, res_req->type[11]); -+ cmd.params[3] |= mc_enc(32, 8, res_req->type[12]); -+ cmd.params[3] |= mc_enc(40, 8, res_req->type[13]); -+ cmd.params[3] |= mc_enc(48, 8, res_req->type[14]); -+ cmd.params[3] |= mc_enc(56, 8, res_req->type[15]); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dprc_assign); -+ -+int dprc_unassign(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int child_container_id, -+ struct dprc_res_req *res_req) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_UNASSIGN, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, child_container_id); -+ cmd.params[0] |= mc_enc(32, 32, res_req->options); -+ cmd.params[1] |= mc_enc(0, 32, res_req->num); -+ cmd.params[1] |= mc_enc(32, 32, res_req->id_base_align); -+ cmd.params[2] |= mc_enc(0, 8, res_req->type[0]); -+ cmd.params[2] |= mc_enc(8, 8, res_req->type[1]); -+ cmd.params[2] |= mc_enc(16, 8, res_req->type[2]); -+ cmd.params[2] |= mc_enc(24, 8, res_req->type[3]); -+ cmd.params[2] |= mc_enc(32, 8, res_req->type[4]); -+ cmd.params[2] |= mc_enc(40, 8, res_req->type[5]); -+ cmd.params[2] |= mc_enc(48, 8, res_req->type[6]); -+ cmd.params[2] |= mc_enc(56, 8, res_req->type[7]); -+ cmd.params[3] |= mc_enc(0, 8, res_req->type[8]); -+ cmd.params[3] |= mc_enc(8, 8, res_req->type[9]); -+ cmd.params[3] |= mc_enc(16, 8, res_req->type[10]); -+ cmd.params[3] |= mc_enc(24, 8, res_req->type[11]); -+ cmd.params[3] |= mc_enc(32, 8, res_req->type[12]); -+ cmd.params[3] |= mc_enc(40, 8, res_req->type[13]); -+ cmd.params[3] |= mc_enc(48, 8, res_req->type[14]); -+ cmd.params[3] |= mc_enc(56, 8, res_req->type[15]); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dprc_unassign); -+ -+int dprc_get_pool_count(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *pool_count) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_POOL_COUNT, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *pool_count = mc_dec(cmd.params[0], 0, 32); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dprc_get_pool_count); -+ -+int dprc_get_pool(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int pool_index, -+ char *type) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_POOL, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, pool_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ type[0] = mc_dec(cmd.params[1], 0, 8); -+ type[1] = mc_dec(cmd.params[1], 8, 8); -+ type[2] = mc_dec(cmd.params[1], 16, 8); -+ type[3] = mc_dec(cmd.params[1], 24, 8); -+ type[4] = mc_dec(cmd.params[1], 32, 8); -+ type[5] = mc_dec(cmd.params[1], 40, 8); -+ type[6] = mc_dec(cmd.params[1], 48, 8); -+ type[7] = mc_dec(cmd.params[1], 56, 8); -+ type[8] = mc_dec(cmd.params[2], 0, 8); -+ type[9] = mc_dec(cmd.params[2], 8, 8); -+ type[10] = mc_dec(cmd.params[2], 16, 8); -+ type[11] = mc_dec(cmd.params[2], 24, 8); -+ type[12] = mc_dec(cmd.params[2], 32, 8); -+ type[13] = mc_dec(cmd.params[2], 40, 8); -+ type[14] = mc_dec(cmd.params[2], 48, 8); -+ type[15] = '\0'; -+ -+ return 0; -+} -+EXPORT_SYMBOL(dprc_get_pool); -+ -+int dprc_get_obj_count(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *obj_count) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_OBJ_COUNT, -+ cmd_flags, -+ token); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *obj_count = mc_dec(cmd.params[0], 32, 32); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dprc_get_obj_count); -+ -+int dprc_get_obj(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int obj_index, -+ struct dprc_obj_desc *obj_desc) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_OBJ, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, obj_index); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ obj_desc->id = mc_dec(cmd.params[0], 32, 32); -+ obj_desc->vendor = mc_dec(cmd.params[1], 0, 16); -+ obj_desc->irq_count = mc_dec(cmd.params[1], 16, 8); -+ obj_desc->region_count = mc_dec(cmd.params[1], 24, 8); -+ obj_desc->state = mc_dec(cmd.params[1], 32, 32); -+ obj_desc->ver_major = mc_dec(cmd.params[2], 0, 16); -+ obj_desc->ver_minor = mc_dec(cmd.params[2], 16, 16); -+ obj_desc->flags = mc_dec(cmd.params[2], 32, 16); -+ obj_desc->type[0] = mc_dec(cmd.params[3], 0, 8); -+ obj_desc->type[1] = mc_dec(cmd.params[3], 8, 8); -+ obj_desc->type[2] = mc_dec(cmd.params[3], 16, 8); -+ obj_desc->type[3] = mc_dec(cmd.params[3], 24, 8); -+ obj_desc->type[4] = mc_dec(cmd.params[3], 32, 8); -+ obj_desc->type[5] = mc_dec(cmd.params[3], 40, 8); -+ obj_desc->type[6] = mc_dec(cmd.params[3], 48, 8); -+ obj_desc->type[7] = mc_dec(cmd.params[3], 56, 8); -+ obj_desc->type[8] = mc_dec(cmd.params[4], 0, 8); -+ obj_desc->type[9] = mc_dec(cmd.params[4], 8, 8); -+ obj_desc->type[10] = mc_dec(cmd.params[4], 16, 8); -+ obj_desc->type[11] = mc_dec(cmd.params[4], 24, 8); -+ obj_desc->type[12] = mc_dec(cmd.params[4], 32, 8); -+ obj_desc->type[13] = mc_dec(cmd.params[4], 40, 8); -+ obj_desc->type[14] = mc_dec(cmd.params[4], 48, 8); -+ obj_desc->type[15] = '\0'; -+ obj_desc->label[0] = mc_dec(cmd.params[5], 0, 8); -+ obj_desc->label[1] = mc_dec(cmd.params[5], 8, 8); -+ obj_desc->label[2] = mc_dec(cmd.params[5], 16, 8); -+ obj_desc->label[3] = mc_dec(cmd.params[5], 24, 8); -+ obj_desc->label[4] = mc_dec(cmd.params[5], 32, 8); -+ obj_desc->label[5] = mc_dec(cmd.params[5], 40, 8); -+ obj_desc->label[6] = mc_dec(cmd.params[5], 48, 8); -+ obj_desc->label[7] = mc_dec(cmd.params[5], 56, 8); -+ obj_desc->label[8] = mc_dec(cmd.params[6], 0, 8); -+ obj_desc->label[9] = mc_dec(cmd.params[6], 8, 8); -+ obj_desc->label[10] = mc_dec(cmd.params[6], 16, 8); -+ obj_desc->label[11] = mc_dec(cmd.params[6], 24, 8); -+ obj_desc->label[12] = mc_dec(cmd.params[6], 32, 8); -+ obj_desc->label[13] = mc_dec(cmd.params[6], 40, 8); -+ obj_desc->label[14] = mc_dec(cmd.params[6], 48, 8); -+ obj_desc->label[15] = '\0'; -+ return 0; -+} -+EXPORT_SYMBOL(dprc_get_obj); -+ -+int dprc_get_obj_desc(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ char *obj_type, -+ int obj_id, -+ struct dprc_obj_desc *obj_desc) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_OBJ_DESC, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, obj_id); -+ cmd.params[1] |= mc_enc(0, 8, obj_type[0]); -+ cmd.params[1] |= mc_enc(8, 8, obj_type[1]); -+ cmd.params[1] |= mc_enc(16, 8, obj_type[2]); -+ cmd.params[1] |= mc_enc(24, 8, obj_type[3]); -+ cmd.params[1] |= mc_enc(32, 8, obj_type[4]); -+ cmd.params[1] |= mc_enc(40, 8, obj_type[5]); -+ cmd.params[1] |= mc_enc(48, 8, obj_type[6]); -+ cmd.params[1] |= mc_enc(56, 8, obj_type[7]); -+ cmd.params[2] |= mc_enc(0, 8, obj_type[8]); -+ cmd.params[2] |= mc_enc(8, 8, obj_type[9]); -+ cmd.params[2] |= mc_enc(16, 8, obj_type[10]); -+ cmd.params[2] |= mc_enc(24, 8, obj_type[11]); -+ cmd.params[2] |= mc_enc(32, 8, obj_type[12]); -+ cmd.params[2] |= mc_enc(40, 8, obj_type[13]); -+ cmd.params[2] |= mc_enc(48, 8, obj_type[14]); -+ cmd.params[2] |= mc_enc(56, 8, obj_type[15]); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ obj_desc->id = (int)mc_dec(cmd.params[0], 32, 32); -+ obj_desc->vendor = (uint16_t)mc_dec(cmd.params[1], 0, 16); -+ obj_desc->vendor = (uint8_t)mc_dec(cmd.params[1], 16, 8); -+ obj_desc->region_count = (uint8_t)mc_dec(cmd.params[1], 24, 8); -+ obj_desc->state = (uint32_t)mc_dec(cmd.params[1], 32, 32); -+ obj_desc->ver_major = (uint16_t)mc_dec(cmd.params[2], 0, 16); -+ obj_desc->ver_minor = (uint16_t)mc_dec(cmd.params[2], 16, 16); -+ obj_desc->flags = mc_dec(cmd.params[2], 32, 16); -+ obj_desc->type[0] = (char)mc_dec(cmd.params[3], 0, 8); -+ obj_desc->type[1] = (char)mc_dec(cmd.params[3], 8, 8); -+ obj_desc->type[2] = (char)mc_dec(cmd.params[3], 16, 8); -+ obj_desc->type[3] = (char)mc_dec(cmd.params[3], 24, 8); -+ obj_desc->type[4] = (char)mc_dec(cmd.params[3], 32, 8); -+ obj_desc->type[5] = (char)mc_dec(cmd.params[3], 40, 8); -+ obj_desc->type[6] = (char)mc_dec(cmd.params[3], 48, 8); -+ obj_desc->type[7] = (char)mc_dec(cmd.params[3], 56, 8); -+ obj_desc->type[8] = (char)mc_dec(cmd.params[4], 0, 8); -+ obj_desc->type[9] = (char)mc_dec(cmd.params[4], 8, 8); -+ obj_desc->type[10] = (char)mc_dec(cmd.params[4], 16, 8); -+ obj_desc->type[11] = (char)mc_dec(cmd.params[4], 24, 8); -+ obj_desc->type[12] = (char)mc_dec(cmd.params[4], 32, 8); -+ obj_desc->type[13] = (char)mc_dec(cmd.params[4], 40, 8); -+ obj_desc->type[14] = (char)mc_dec(cmd.params[4], 48, 8); -+ obj_desc->type[15] = (char)mc_dec(cmd.params[4], 56, 8); -+ obj_desc->label[0] = (char)mc_dec(cmd.params[5], 0, 8); -+ obj_desc->label[1] = (char)mc_dec(cmd.params[5], 8, 8); -+ obj_desc->label[2] = (char)mc_dec(cmd.params[5], 16, 8); -+ obj_desc->label[3] = (char)mc_dec(cmd.params[5], 24, 8); -+ obj_desc->label[4] = (char)mc_dec(cmd.params[5], 32, 8); -+ obj_desc->label[5] = (char)mc_dec(cmd.params[5], 40, 8); -+ obj_desc->label[6] = (char)mc_dec(cmd.params[5], 48, 8); -+ obj_desc->label[7] = (char)mc_dec(cmd.params[5], 56, 8); -+ obj_desc->label[8] = (char)mc_dec(cmd.params[6], 0, 8); -+ obj_desc->label[9] = (char)mc_dec(cmd.params[6], 8, 8); -+ obj_desc->label[10] = (char)mc_dec(cmd.params[6], 16, 8); -+ obj_desc->label[11] = (char)mc_dec(cmd.params[6], 24, 8); -+ obj_desc->label[12] = (char)mc_dec(cmd.params[6], 32, 8); -+ obj_desc->label[13] = (char)mc_dec(cmd.params[6], 40, 8); -+ obj_desc->label[14] = (char)mc_dec(cmd.params[6], 48, 8); -+ obj_desc->label[15] = (char)mc_dec(cmd.params[6], 56, 8); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dprc_get_obj_desc); -+ -+int dprc_set_obj_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ char *obj_type, -+ int obj_id, -+ uint8_t irq_index, -+ struct dprc_irq_cfg *irq_cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_SET_OBJ_IRQ, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ cmd.params[0] |= mc_enc(0, 32, irq_cfg->val); -+ cmd.params[1] |= mc_enc(0, 64, irq_cfg->paddr); -+ cmd.params[2] |= mc_enc(0, 32, irq_cfg->irq_num); -+ cmd.params[2] |= mc_enc(32, 32, obj_id); -+ cmd.params[3] |= mc_enc(0, 8, obj_type[0]); -+ cmd.params[3] |= mc_enc(8, 8, obj_type[1]); -+ cmd.params[3] |= mc_enc(16, 8, obj_type[2]); -+ cmd.params[3] |= mc_enc(24, 8, obj_type[3]); -+ cmd.params[3] |= mc_enc(32, 8, obj_type[4]); -+ cmd.params[3] |= mc_enc(40, 8, obj_type[5]); -+ cmd.params[3] |= mc_enc(48, 8, obj_type[6]); -+ cmd.params[3] |= mc_enc(56, 8, obj_type[7]); -+ cmd.params[4] |= mc_enc(0, 8, obj_type[8]); -+ cmd.params[4] |= mc_enc(8, 8, obj_type[9]); -+ cmd.params[4] |= mc_enc(16, 8, obj_type[10]); -+ cmd.params[4] |= mc_enc(24, 8, obj_type[11]); -+ cmd.params[4] |= mc_enc(32, 8, obj_type[12]); -+ cmd.params[4] |= mc_enc(40, 8, obj_type[13]); -+ cmd.params[4] |= mc_enc(48, 8, obj_type[14]); -+ cmd.params[4] |= mc_enc(56, 8, obj_type[15]); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dprc_set_obj_irq); -+ -+int dprc_get_obj_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ char *obj_type, -+ int obj_id, -+ uint8_t irq_index, -+ int *type, -+ struct dprc_irq_cfg *irq_cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_OBJ_IRQ, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, obj_id); -+ cmd.params[0] |= mc_enc(32, 8, irq_index); -+ cmd.params[1] |= mc_enc(0, 8, obj_type[0]); -+ cmd.params[1] |= mc_enc(8, 8, obj_type[1]); -+ cmd.params[1] |= mc_enc(16, 8, obj_type[2]); -+ cmd.params[1] |= mc_enc(24, 8, obj_type[3]); -+ cmd.params[1] |= mc_enc(32, 8, obj_type[4]); -+ cmd.params[1] |= mc_enc(40, 8, obj_type[5]); -+ cmd.params[1] |= mc_enc(48, 8, obj_type[6]); -+ cmd.params[1] |= mc_enc(56, 8, obj_type[7]); -+ cmd.params[2] |= mc_enc(0, 8, obj_type[8]); -+ cmd.params[2] |= mc_enc(8, 8, obj_type[9]); -+ cmd.params[2] |= mc_enc(16, 8, obj_type[10]); -+ cmd.params[2] |= mc_enc(24, 8, obj_type[11]); -+ cmd.params[2] |= mc_enc(32, 8, obj_type[12]); -+ cmd.params[2] |= mc_enc(40, 8, obj_type[13]); -+ cmd.params[2] |= mc_enc(48, 8, obj_type[14]); -+ cmd.params[2] |= mc_enc(56, 8, obj_type[15]); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ irq_cfg->val = (uint32_t)mc_dec(cmd.params[0], 0, 32); -+ irq_cfg->paddr = (uint64_t)mc_dec(cmd.params[1], 0, 64); -+ irq_cfg->irq_num = (int)mc_dec(cmd.params[2], 0, 32); -+ *type = (int)mc_dec(cmd.params[2], 32, 32); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dprc_get_obj_irq); -+ -+int dprc_get_res_count(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ char *type, -+ int *res_count) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ *res_count = 0; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_RES_COUNT, -+ cmd_flags, -+ token); -+ cmd.params[1] |= mc_enc(0, 8, type[0]); -+ cmd.params[1] |= mc_enc(8, 8, type[1]); -+ cmd.params[1] |= mc_enc(16, 8, type[2]); -+ cmd.params[1] |= mc_enc(24, 8, type[3]); -+ cmd.params[1] |= mc_enc(32, 8, type[4]); -+ cmd.params[1] |= mc_enc(40, 8, type[5]); -+ cmd.params[1] |= mc_enc(48, 8, type[6]); -+ cmd.params[1] |= mc_enc(56, 8, type[7]); -+ cmd.params[2] |= mc_enc(0, 8, type[8]); -+ cmd.params[2] |= mc_enc(8, 8, type[9]); -+ cmd.params[2] |= mc_enc(16, 8, type[10]); -+ cmd.params[2] |= mc_enc(24, 8, type[11]); -+ cmd.params[2] |= mc_enc(32, 8, type[12]); -+ cmd.params[2] |= mc_enc(40, 8, type[13]); -+ cmd.params[2] |= mc_enc(48, 8, type[14]); -+ cmd.params[2] |= mc_enc(56, 8, '\0'); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ *res_count = mc_dec(cmd.params[0], 0, 32); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dprc_get_res_count); -+ -+int dprc_get_res_ids(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ char *type, -+ struct dprc_res_ids_range_desc *range_desc) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_RES_IDS, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(42, 7, range_desc->iter_status); -+ cmd.params[1] |= mc_enc(0, 32, range_desc->base_id); -+ cmd.params[1] |= mc_enc(32, 32, range_desc->last_id); -+ cmd.params[2] |= mc_enc(0, 8, type[0]); -+ cmd.params[2] |= mc_enc(8, 8, type[1]); -+ cmd.params[2] |= mc_enc(16, 8, type[2]); -+ cmd.params[2] |= mc_enc(24, 8, type[3]); -+ cmd.params[2] |= mc_enc(32, 8, type[4]); -+ cmd.params[2] |= mc_enc(40, 8, type[5]); -+ cmd.params[2] |= mc_enc(48, 8, type[6]); -+ cmd.params[2] |= mc_enc(56, 8, type[7]); -+ cmd.params[3] |= mc_enc(0, 8, type[8]); -+ cmd.params[3] |= mc_enc(8, 8, type[9]); -+ cmd.params[3] |= mc_enc(16, 8, type[10]); -+ cmd.params[3] |= mc_enc(24, 8, type[11]); -+ cmd.params[3] |= mc_enc(32, 8, type[12]); -+ cmd.params[3] |= mc_enc(40, 8, type[13]); -+ cmd.params[3] |= mc_enc(48, 8, type[14]); -+ cmd.params[3] |= mc_enc(56, 8, '\0'); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ range_desc->iter_status = mc_dec(cmd.params[0], 42, 7); -+ range_desc->base_id = mc_dec(cmd.params[1], 0, 32); -+ range_desc->last_id = mc_dec(cmd.params[1], 32, 32); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dprc_get_res_ids); -+ -+int dprc_get_obj_region(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ char *obj_type, -+ int obj_id, -+ uint8_t region_index, -+ struct dprc_region_desc *region_desc) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_OBJ_REG, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, obj_id); -+ cmd.params[0] |= mc_enc(48, 8, region_index); -+ cmd.params[3] |= mc_enc(0, 8, obj_type[0]); -+ cmd.params[3] |= mc_enc(8, 8, obj_type[1]); -+ cmd.params[3] |= mc_enc(16, 8, obj_type[2]); -+ cmd.params[3] |= mc_enc(24, 8, obj_type[3]); -+ cmd.params[3] |= mc_enc(32, 8, obj_type[4]); -+ cmd.params[3] |= mc_enc(40, 8, obj_type[5]); -+ cmd.params[3] |= mc_enc(48, 8, obj_type[6]); -+ cmd.params[3] |= mc_enc(56, 8, obj_type[7]); -+ cmd.params[4] |= mc_enc(0, 8, obj_type[8]); -+ cmd.params[4] |= mc_enc(8, 8, obj_type[9]); -+ cmd.params[4] |= mc_enc(16, 8, obj_type[10]); -+ cmd.params[4] |= mc_enc(24, 8, obj_type[11]); -+ cmd.params[4] |= mc_enc(32, 8, obj_type[12]); -+ cmd.params[4] |= mc_enc(40, 8, obj_type[13]); -+ cmd.params[4] |= mc_enc(48, 8, obj_type[14]); -+ cmd.params[4] |= mc_enc(56, 8, '\0'); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ region_desc->base_offset = mc_dec(cmd.params[1], 0, 64); -+ region_desc->size = mc_dec(cmd.params[2], 0, 32); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dprc_get_obj_region); -+ -+int dprc_set_obj_label(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ char *obj_type, -+ int obj_id, -+ char *label) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_SET_OBJ_LABEL, -+ cmd_flags, -+ token); -+ -+ cmd.params[0] |= mc_enc(0, 32, obj_id); -+ cmd.params[1] |= mc_enc(0, 8, label[0]); -+ cmd.params[1] |= mc_enc(8, 8, label[1]); -+ cmd.params[1] |= mc_enc(16, 8, label[2]); -+ cmd.params[1] |= mc_enc(24, 8, label[3]); -+ cmd.params[1] |= mc_enc(32, 8, label[4]); -+ cmd.params[1] |= mc_enc(40, 8, label[5]); -+ cmd.params[1] |= mc_enc(48, 8, label[6]); -+ cmd.params[1] |= mc_enc(56, 8, label[7]); -+ cmd.params[2] |= mc_enc(0, 8, label[8]); -+ cmd.params[2] |= mc_enc(8, 8, label[9]); -+ cmd.params[2] |= mc_enc(16, 8, label[10]); -+ cmd.params[2] |= mc_enc(24, 8, label[11]); -+ cmd.params[2] |= mc_enc(32, 8, label[12]); -+ cmd.params[2] |= mc_enc(40, 8, label[13]); -+ cmd.params[2] |= mc_enc(48, 8, label[14]); -+ cmd.params[2] |= mc_enc(56, 8, label[15]); -+ cmd.params[3] |= mc_enc(0, 8, obj_type[0]); -+ cmd.params[3] |= mc_enc(8, 8, obj_type[1]); -+ cmd.params[3] |= mc_enc(16, 8, obj_type[2]); -+ cmd.params[3] |= mc_enc(24, 8, obj_type[3]); -+ cmd.params[3] |= mc_enc(32, 8, obj_type[4]); -+ cmd.params[3] |= mc_enc(40, 8, obj_type[5]); -+ cmd.params[3] |= mc_enc(48, 8, obj_type[6]); -+ cmd.params[3] |= mc_enc(56, 8, obj_type[7]); -+ cmd.params[4] |= mc_enc(0, 8, obj_type[8]); -+ cmd.params[4] |= mc_enc(8, 8, obj_type[9]); -+ cmd.params[4] |= mc_enc(16, 8, obj_type[10]); -+ cmd.params[4] |= mc_enc(24, 8, obj_type[11]); -+ cmd.params[4] |= mc_enc(32, 8, obj_type[12]); -+ cmd.params[4] |= mc_enc(40, 8, obj_type[13]); -+ cmd.params[4] |= mc_enc(48, 8, obj_type[14]); -+ cmd.params[4] |= mc_enc(56, 8, obj_type[15]); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dprc_set_obj_label); -+ -+int dprc_connect(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dprc_endpoint *endpoint1, -+ const struct dprc_endpoint *endpoint2, -+ const struct dprc_connection_cfg *cfg) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_CONNECT, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, endpoint1->id); -+ cmd.params[0] |= mc_enc(32, 32, endpoint1->if_id); -+ cmd.params[1] |= mc_enc(0, 32, endpoint2->id); -+ cmd.params[1] |= mc_enc(32, 32, endpoint2->if_id); -+ cmd.params[2] |= mc_enc(0, 8, endpoint1->type[0]); -+ cmd.params[2] |= mc_enc(8, 8, endpoint1->type[1]); -+ cmd.params[2] |= mc_enc(16, 8, endpoint1->type[2]); -+ cmd.params[2] |= mc_enc(24, 8, endpoint1->type[3]); -+ cmd.params[2] |= mc_enc(32, 8, endpoint1->type[4]); -+ cmd.params[2] |= mc_enc(40, 8, endpoint1->type[5]); -+ cmd.params[2] |= mc_enc(48, 8, endpoint1->type[6]); -+ cmd.params[2] |= mc_enc(56, 8, endpoint1->type[7]); -+ cmd.params[3] |= mc_enc(0, 8, endpoint1->type[8]); -+ cmd.params[3] |= mc_enc(8, 8, endpoint1->type[9]); -+ cmd.params[3] |= mc_enc(16, 8, endpoint1->type[10]); -+ cmd.params[3] |= mc_enc(24, 8, endpoint1->type[11]); -+ cmd.params[3] |= mc_enc(32, 8, endpoint1->type[12]); -+ cmd.params[3] |= mc_enc(40, 8, endpoint1->type[13]); -+ cmd.params[3] |= mc_enc(48, 8, endpoint1->type[14]); -+ cmd.params[3] |= mc_enc(56, 8, endpoint1->type[15]); -+ cmd.params[4] |= mc_enc(0, 32, cfg->max_rate); -+ cmd.params[4] |= mc_enc(32, 32, cfg->committed_rate); -+ cmd.params[5] |= mc_enc(0, 8, endpoint2->type[0]); -+ cmd.params[5] |= mc_enc(8, 8, endpoint2->type[1]); -+ cmd.params[5] |= mc_enc(16, 8, endpoint2->type[2]); -+ cmd.params[5] |= mc_enc(24, 8, endpoint2->type[3]); -+ cmd.params[5] |= mc_enc(32, 8, endpoint2->type[4]); -+ cmd.params[5] |= mc_enc(40, 8, endpoint2->type[5]); -+ cmd.params[5] |= mc_enc(48, 8, endpoint2->type[6]); -+ cmd.params[5] |= mc_enc(56, 8, endpoint2->type[7]); -+ cmd.params[6] |= mc_enc(0, 8, endpoint2->type[8]); -+ cmd.params[6] |= mc_enc(8, 8, endpoint2->type[9]); -+ cmd.params[6] |= mc_enc(16, 8, endpoint2->type[10]); -+ cmd.params[6] |= mc_enc(24, 8, endpoint2->type[11]); -+ cmd.params[6] |= mc_enc(32, 8, endpoint2->type[12]); -+ cmd.params[6] |= mc_enc(40, 8, endpoint2->type[13]); -+ cmd.params[6] |= mc_enc(48, 8, endpoint2->type[14]); -+ cmd.params[6] |= mc_enc(56, 8, endpoint2->type[15]); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dprc_connect); -+ -+int dprc_disconnect(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dprc_endpoint *endpoint) -+{ -+ struct mc_command cmd = { 0 }; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_DISCONNECT, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, endpoint->id); -+ cmd.params[0] |= mc_enc(32, 32, endpoint->if_id); -+ cmd.params[1] |= mc_enc(0, 8, endpoint->type[0]); -+ cmd.params[1] |= mc_enc(8, 8, endpoint->type[1]); -+ cmd.params[1] |= mc_enc(16, 8, endpoint->type[2]); -+ cmd.params[1] |= mc_enc(24, 8, endpoint->type[3]); -+ cmd.params[1] |= mc_enc(32, 8, endpoint->type[4]); -+ cmd.params[1] |= mc_enc(40, 8, endpoint->type[5]); -+ cmd.params[1] |= mc_enc(48, 8, endpoint->type[6]); -+ cmd.params[1] |= mc_enc(56, 8, endpoint->type[7]); -+ cmd.params[2] |= mc_enc(0, 8, endpoint->type[8]); -+ cmd.params[2] |= mc_enc(8, 8, endpoint->type[9]); -+ cmd.params[2] |= mc_enc(16, 8, endpoint->type[10]); -+ cmd.params[2] |= mc_enc(24, 8, endpoint->type[11]); -+ cmd.params[2] |= mc_enc(32, 8, endpoint->type[12]); -+ cmd.params[2] |= mc_enc(40, 8, endpoint->type[13]); -+ cmd.params[2] |= mc_enc(48, 8, endpoint->type[14]); -+ cmd.params[2] |= mc_enc(56, 8, endpoint->type[15]); -+ -+ /* send command to mc*/ -+ return mc_send_command(mc_io, &cmd); -+} -+EXPORT_SYMBOL(dprc_disconnect); -+ -+int dprc_get_connection(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dprc_endpoint *endpoint1, -+ struct dprc_endpoint *endpoint2, -+ int *state) -+{ -+ struct mc_command cmd = { 0 }; -+ int err; -+ -+ /* prepare command */ -+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_CONNECTION, -+ cmd_flags, -+ token); -+ cmd.params[0] |= mc_enc(0, 32, endpoint1->id); -+ cmd.params[0] |= mc_enc(32, 32, endpoint1->if_id); -+ cmd.params[1] |= mc_enc(0, 8, endpoint1->type[0]); -+ cmd.params[1] |= mc_enc(8, 8, endpoint1->type[1]); -+ cmd.params[1] |= mc_enc(16, 8, endpoint1->type[2]); -+ cmd.params[1] |= mc_enc(24, 8, endpoint1->type[3]); -+ cmd.params[1] |= mc_enc(32, 8, endpoint1->type[4]); -+ cmd.params[1] |= mc_enc(40, 8, endpoint1->type[5]); -+ cmd.params[1] |= mc_enc(48, 8, endpoint1->type[6]); -+ cmd.params[1] |= mc_enc(56, 8, endpoint1->type[7]); -+ cmd.params[2] |= mc_enc(0, 8, endpoint1->type[8]); -+ cmd.params[2] |= mc_enc(8, 8, endpoint1->type[9]); -+ cmd.params[2] |= mc_enc(16, 8, endpoint1->type[10]); -+ cmd.params[2] |= mc_enc(24, 8, endpoint1->type[11]); -+ cmd.params[2] |= mc_enc(32, 8, endpoint1->type[12]); -+ cmd.params[2] |= mc_enc(40, 8, endpoint1->type[13]); -+ cmd.params[2] |= mc_enc(48, 8, endpoint1->type[14]); -+ cmd.params[2] |= mc_enc(56, 8, endpoint1->type[15]); -+ -+ /* send command to mc*/ -+ err = mc_send_command(mc_io, &cmd); -+ if (err) -+ return err; -+ -+ /* retrieve response parameters */ -+ endpoint2->id = mc_dec(cmd.params[3], 0, 32); -+ endpoint2->if_id = mc_dec(cmd.params[3], 32, 32); -+ endpoint2->type[0] = mc_dec(cmd.params[4], 0, 8); -+ endpoint2->type[1] = mc_dec(cmd.params[4], 8, 8); -+ endpoint2->type[2] = mc_dec(cmd.params[4], 16, 8); -+ endpoint2->type[3] = mc_dec(cmd.params[4], 24, 8); -+ endpoint2->type[4] = mc_dec(cmd.params[4], 32, 8); -+ endpoint2->type[5] = mc_dec(cmd.params[4], 40, 8); -+ endpoint2->type[6] = mc_dec(cmd.params[4], 48, 8); -+ endpoint2->type[7] = mc_dec(cmd.params[4], 56, 8); -+ endpoint2->type[8] = mc_dec(cmd.params[5], 0, 8); -+ endpoint2->type[9] = mc_dec(cmd.params[5], 8, 8); -+ endpoint2->type[10] = mc_dec(cmd.params[5], 16, 8); -+ endpoint2->type[11] = mc_dec(cmd.params[5], 24, 8); -+ endpoint2->type[12] = mc_dec(cmd.params[5], 32, 8); -+ endpoint2->type[13] = mc_dec(cmd.params[5], 40, 8); -+ endpoint2->type[14] = mc_dec(cmd.params[5], 48, 8); -+ endpoint2->type[15] = mc_dec(cmd.params[5], 56, 8); -+ *state = mc_dec(cmd.params[6], 0, 32); -+ -+ return 0; -+} -+EXPORT_SYMBOL(dprc_get_connection); -diff --git a/drivers/staging/fsl-mc/bus/mc-allocator.c b/drivers/staging/fsl-mc/bus/mc-allocator.c -new file mode 100644 -index 0000000..a3940a0 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/mc-allocator.c -@@ -0,0 +1,716 @@ -+/* -+ * Freescale MC object device allocator driver -+ * -+ * Copyright (C) 2013 Freescale Semiconductor, Inc. -+ * -+ * This file is licensed under the terms of the GNU General Public -+ * License version 2. This program is licensed "as is" without any -+ * warranty of any kind, whether express or implied. -+ */ -+ -+#include "../include/mc-private.h" -+#include "../include/mc-sys.h" -+#include -+#include "../include/dpbp-cmd.h" -+#include "../include/dpcon-cmd.h" -+#include "dpmcp-cmd.h" -+#include "dpmcp.h" -+ -+/** -+ * fsl_mc_resource_pool_add_device - add allocatable device to a resource -+ * pool of a given MC bus -+ * -+ * @mc_bus: pointer to the MC bus -+ * @pool_type: MC bus pool type -+ * @mc_dev: Pointer to allocatable MC object device -+ * -+ * It adds an allocatable MC object device to a container's resource pool of -+ * the given resource type -+ */ -+static int __must_check fsl_mc_resource_pool_add_device(struct fsl_mc_bus -+ *mc_bus, -+ enum fsl_mc_pool_type -+ pool_type, -+ struct fsl_mc_device -+ *mc_dev) -+{ -+ struct fsl_mc_resource_pool *res_pool; -+ struct fsl_mc_resource *resource; -+ struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev; -+ int error = -EINVAL; -+ bool mutex_locked = false; -+ -+ if (WARN_ON(pool_type < 0 || pool_type >= FSL_MC_NUM_POOL_TYPES)) -+ goto out; -+ if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type))) -+ goto out; -+ if (WARN_ON(mc_dev->resource)) -+ goto out; -+ -+ res_pool = &mc_bus->resource_pools[pool_type]; -+ if (WARN_ON(res_pool->type != pool_type)) -+ goto out; -+ if (WARN_ON(res_pool->mc_bus != mc_bus)) -+ goto out; -+ -+ mutex_lock(&res_pool->mutex); -+ mutex_locked = true; -+ -+ if (WARN_ON(res_pool->max_count < 0)) -+ goto out; -+ if (WARN_ON(res_pool->free_count < 0 || -+ res_pool->free_count > res_pool->max_count)) -+ goto out; -+ -+ resource = devm_kzalloc(&mc_bus_dev->dev, sizeof(*resource), -+ GFP_KERNEL); -+ if (!resource) { -+ error = -ENOMEM; -+ dev_err(&mc_bus_dev->dev, -+ "Failed to allocate memory for fsl_mc_resource\n"); -+ goto out; -+ } -+ -+ resource->type = pool_type; -+ resource->id = mc_dev->obj_desc.id; -+ resource->data = mc_dev; -+ resource->parent_pool = res_pool; -+ INIT_LIST_HEAD(&resource->node); -+ list_add_tail(&resource->node, &res_pool->free_list); -+ mc_dev->resource = resource; -+ res_pool->free_count++; -+ res_pool->max_count++; -+ error = 0; -+out: -+ if (mutex_locked) -+ mutex_unlock(&res_pool->mutex); -+ -+ return error; -+} -+ -+/** -+ * fsl_mc_resource_pool_remove_device - remove an allocatable device from a -+ * resource pool -+ * -+ * @mc_dev: Pointer to allocatable MC object device -+ * -+ * It permanently removes an allocatable MC object device from the resource -+ * pool, the device is currently in, as long as it is in the pool's free list. -+ */ -+static int __must_check fsl_mc_resource_pool_remove_device(struct fsl_mc_device -+ *mc_dev) -+{ -+ struct fsl_mc_device *mc_bus_dev; -+ struct fsl_mc_bus *mc_bus; -+ struct fsl_mc_resource_pool *res_pool; -+ struct fsl_mc_resource *resource; -+ int error = -EINVAL; -+ bool mutex_locked = false; -+ -+ if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type))) -+ goto out; -+ -+ resource = mc_dev->resource; -+ if (WARN_ON(!resource || resource->data != mc_dev)) -+ goto out; -+ -+ mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); -+ mc_bus = to_fsl_mc_bus(mc_bus_dev); -+ res_pool = resource->parent_pool; -+ if (WARN_ON(res_pool != &mc_bus->resource_pools[resource->type])) -+ goto out; -+ -+ mutex_lock(&res_pool->mutex); -+ mutex_locked = true; -+ -+ if (WARN_ON(res_pool->max_count <= 0)) -+ goto out; -+ if (WARN_ON(res_pool->free_count <= 0 || -+ res_pool->free_count > res_pool->max_count)) -+ goto out; -+ -+ /* -+ * If the device is currently allocated, its resource is not -+ * in the free list and thus, the device cannot be removed. -+ */ -+ if (list_empty(&resource->node)) { -+ error = -EBUSY; -+ dev_err(&mc_bus_dev->dev, -+ "Device %s cannot be removed from resource pool\n", -+ dev_name(&mc_dev->dev)); -+ goto out; -+ } -+ -+ list_del(&resource->node); -+ INIT_LIST_HEAD(&resource->node); -+ res_pool->free_count--; -+ res_pool->max_count--; -+ -+ devm_kfree(&mc_bus_dev->dev, resource); -+ mc_dev->resource = NULL; -+ error = 0; -+out: -+ if (mutex_locked) -+ mutex_unlock(&res_pool->mutex); -+ -+ return error; -+} -+ -+static const char *const fsl_mc_pool_type_strings[] = { -+ [FSL_MC_POOL_DPMCP] = "dpmcp", -+ [FSL_MC_POOL_DPBP] = "dpbp", -+ [FSL_MC_POOL_DPCON] = "dpcon", -+ [FSL_MC_POOL_IRQ] = "irq", -+}; -+ -+static int __must_check object_type_to_pool_type(const char *object_type, -+ enum fsl_mc_pool_type -+ *pool_type) -+{ -+ unsigned int i; -+ -+ for (i = 0; i < ARRAY_SIZE(fsl_mc_pool_type_strings); i++) { -+ if (strcmp(object_type, fsl_mc_pool_type_strings[i]) == 0) { -+ *pool_type = i; -+ return 0; -+ } -+ } -+ -+ return -EINVAL; -+} -+ -+int __must_check fsl_mc_resource_allocate(struct fsl_mc_bus *mc_bus, -+ enum fsl_mc_pool_type pool_type, -+ struct fsl_mc_resource **new_resource) -+{ -+ struct fsl_mc_resource_pool *res_pool; -+ struct fsl_mc_resource *resource; -+ struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev; -+ int error = -EINVAL; -+ bool mutex_locked = false; -+ -+ BUILD_BUG_ON(ARRAY_SIZE(fsl_mc_pool_type_strings) != -+ FSL_MC_NUM_POOL_TYPES); -+ -+ *new_resource = NULL; -+ if (WARN_ON(pool_type < 0 || pool_type >= FSL_MC_NUM_POOL_TYPES)) -+ goto error; -+ -+ res_pool = &mc_bus->resource_pools[pool_type]; -+ if (WARN_ON(res_pool->mc_bus != mc_bus)) -+ goto error; -+ -+ mutex_lock(&res_pool->mutex); -+ mutex_locked = true; -+ resource = list_first_entry_or_null(&res_pool->free_list, -+ struct fsl_mc_resource, node); -+ -+ if (!resource) { -+ WARN_ON(res_pool->free_count != 0); -+ error = -ENXIO; -+ dev_err(&mc_bus_dev->dev, -+ "No more resources of type %s left\n", -+ fsl_mc_pool_type_strings[pool_type]); -+ goto error; -+ } -+ -+ if (WARN_ON(resource->type != pool_type)) -+ goto error; -+ if (WARN_ON(resource->parent_pool != res_pool)) -+ goto error; -+ if (WARN_ON(res_pool->free_count <= 0 || -+ res_pool->free_count > res_pool->max_count)) -+ goto error; -+ -+ list_del(&resource->node); -+ INIT_LIST_HEAD(&resource->node); -+ -+ res_pool->free_count--; -+ mutex_unlock(&res_pool->mutex); -+ *new_resource = resource; -+ return 0; -+error: -+ if (mutex_locked) -+ mutex_unlock(&res_pool->mutex); -+ -+ return error; -+} -+EXPORT_SYMBOL_GPL(fsl_mc_resource_allocate); -+ -+void fsl_mc_resource_free(struct fsl_mc_resource *resource) -+{ -+ struct fsl_mc_resource_pool *res_pool; -+ bool mutex_locked = false; -+ -+ res_pool = resource->parent_pool; -+ if (WARN_ON(resource->type != res_pool->type)) -+ goto out; -+ -+ mutex_lock(&res_pool->mutex); -+ mutex_locked = true; -+ if (WARN_ON(res_pool->free_count < 0 || -+ res_pool->free_count >= res_pool->max_count)) -+ goto out; -+ -+ if (WARN_ON(!list_empty(&resource->node))) -+ goto out; -+ -+ list_add_tail(&resource->node, &res_pool->free_list); -+ res_pool->free_count++; -+out: -+ if (mutex_locked) -+ mutex_unlock(&res_pool->mutex); -+} -+EXPORT_SYMBOL_GPL(fsl_mc_resource_free); -+ -+/** -+ * fsl_mc_portal_allocate - Allocates an MC portal -+ * -+ * @mc_dev: MC device for which the MC portal is to be allocated -+ * @mc_io_flags: Flags for the fsl_mc_io object that wraps the allocated -+ * MC portal. -+ * @new_mc_io: Pointer to area where the pointer to the fsl_mc_io object -+ * that wraps the allocated MC portal is to be returned -+ * -+ * This function allocates an MC portal from the device's parent DPRC, -+ * from the corresponding MC bus' pool of MC portals and wraps -+ * it in a new fsl_mc_io object. If 'mc_dev' is a DPRC itself, the -+ * portal is allocated from its own MC bus. -+ */ -+int __must_check fsl_mc_portal_allocate(struct fsl_mc_device *mc_dev, -+ uint16_t mc_io_flags, -+ struct fsl_mc_io **new_mc_io) -+{ -+ struct fsl_mc_device *mc_bus_dev; -+ struct fsl_mc_bus *mc_bus; -+ phys_addr_t mc_portal_phys_addr; -+ size_t mc_portal_size; -+ struct fsl_mc_device *dpmcp_dev; -+ int error = -EINVAL; -+ struct fsl_mc_resource *resource = NULL; -+ struct fsl_mc_io *mc_io = NULL; -+ -+ if (!mc_dev) { -+ if (WARN_ON(!fsl_mc_bus_type.dev_root)) -+ return error; -+ -+ mc_bus_dev = to_fsl_mc_device(fsl_mc_bus_type.dev_root); -+ } else if (mc_dev->flags & FSL_MC_IS_DPRC) { -+ mc_bus_dev = mc_dev; -+ } else { -+ if (WARN_ON(mc_dev->dev.parent->bus != &fsl_mc_bus_type)) -+ return error; -+ -+ mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); -+ } -+ -+ mc_bus = to_fsl_mc_bus(mc_bus_dev); -+ *new_mc_io = NULL; -+ error = fsl_mc_resource_allocate(mc_bus, FSL_MC_POOL_DPMCP, &resource); -+ if (error < 0) -+ return error; -+ -+ error = -EINVAL; -+ dpmcp_dev = resource->data; -+ if (WARN_ON(!dpmcp_dev || -+ strcmp(dpmcp_dev->obj_desc.type, "dpmcp") != 0)) -+ goto error_cleanup_resource; -+ -+ if (dpmcp_dev->obj_desc.ver_major < DPMCP_MIN_VER_MAJOR || -+ (dpmcp_dev->obj_desc.ver_major == DPMCP_MIN_VER_MAJOR && -+ dpmcp_dev->obj_desc.ver_minor < DPMCP_MIN_VER_MINOR)) { -+ dev_err(&dpmcp_dev->dev, -+ "ERROR: Version %d.%d of DPMCP not supported.\n", -+ dpmcp_dev->obj_desc.ver_major, -+ dpmcp_dev->obj_desc.ver_minor); -+ error = -ENOTSUPP; -+ goto error_cleanup_resource; -+ } -+ -+ if (WARN_ON(dpmcp_dev->obj_desc.region_count == 0)) -+ goto error_cleanup_resource; -+ -+ mc_portal_phys_addr = dpmcp_dev->regions[0].start; -+ mc_portal_size = dpmcp_dev->regions[0].end - -+ dpmcp_dev->regions[0].start + 1; -+ -+ if (WARN_ON(mc_portal_size != mc_bus_dev->mc_io->portal_size)) -+ goto error_cleanup_resource; -+ -+ error = fsl_create_mc_io(&mc_bus_dev->dev, -+ mc_portal_phys_addr, -+ mc_portal_size, dpmcp_dev, -+ mc_io_flags, &mc_io); -+ if (error < 0) -+ goto error_cleanup_resource; -+ -+ *new_mc_io = mc_io; -+ return 0; -+ -+error_cleanup_resource: -+ fsl_mc_resource_free(resource); -+ return error; -+} -+EXPORT_SYMBOL_GPL(fsl_mc_portal_allocate); -+ -+/** -+ * fsl_mc_portal_free - Returns an MC portal to the pool of free MC portals -+ * of a given MC bus -+ * -+ * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free -+ */ -+void fsl_mc_portal_free(struct fsl_mc_io *mc_io) -+{ -+ struct fsl_mc_device *dpmcp_dev; -+ struct fsl_mc_resource *resource; -+ -+ /* -+ * Every mc_io obtained by calling fsl_mc_portal_allocate() is supposed -+ * to have a DPMCP object associated with. -+ */ -+ dpmcp_dev = mc_io->dpmcp_dev; -+ if (WARN_ON(!dpmcp_dev)) -+ return; -+ if (WARN_ON(strcmp(dpmcp_dev->obj_desc.type, "dpmcp") != 0)) -+ return; -+ if (WARN_ON(dpmcp_dev->mc_io != mc_io)) -+ return; -+ -+ resource = dpmcp_dev->resource; -+ if (WARN_ON(!resource || resource->type != FSL_MC_POOL_DPMCP)) -+ return; -+ -+ if (WARN_ON(resource->data != dpmcp_dev)) -+ return; -+ -+ fsl_destroy_mc_io(mc_io); -+ fsl_mc_resource_free(resource); -+} -+EXPORT_SYMBOL_GPL(fsl_mc_portal_free); -+ -+/** -+ * fsl_mc_portal_reset - Resets the dpmcp object for a given fsl_mc_io object -+ * -+ * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free -+ */ -+int fsl_mc_portal_reset(struct fsl_mc_io *mc_io) -+{ -+ int error; -+ struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev; -+ -+ if (WARN_ON(!dpmcp_dev)) -+ return -EINVAL; -+ -+ error = dpmcp_reset(mc_io, 0, dpmcp_dev->mc_handle); -+ if (error < 0) { -+ dev_err(&dpmcp_dev->dev, "dpmcp_reset() failed: %d\n", error); -+ return error; -+ } -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(fsl_mc_portal_reset); -+ -+/** -+ * fsl_mc_object_allocate - Allocates a MC object device of the given -+ * pool type from a given MC bus -+ * -+ * @mc_dev: MC device for which the MC object device is to be allocated -+ * @pool_type: MC bus resource pool type -+ * @new_mc_dev: Pointer to area where the pointer to the allocated -+ * MC object device is to be returned -+ * -+ * This function allocates a MC object device from the device's parent DPRC, -+ * from the corresponding MC bus' pool of allocatable MC object devices of -+ * the given resource type. mc_dev cannot be a DPRC itself. -+ * -+ * NOTE: pool_type must be different from FSL_MC_POOL_MCP, since MC -+ * portals are allocated using fsl_mc_portal_allocate(), instead of -+ * this function. -+ */ -+int __must_check fsl_mc_object_allocate(struct fsl_mc_device *mc_dev, -+ enum fsl_mc_pool_type pool_type, -+ struct fsl_mc_device **new_mc_adev) -+{ -+ struct fsl_mc_device *mc_bus_dev; -+ struct fsl_mc_bus *mc_bus; -+ struct fsl_mc_device *mc_adev; -+ int error = -EINVAL; -+ struct fsl_mc_resource *resource = NULL; -+ -+ *new_mc_adev = NULL; -+ if (WARN_ON(mc_dev->flags & FSL_MC_IS_DPRC)) -+ goto error; -+ -+ if (WARN_ON(mc_dev->dev.parent->bus != &fsl_mc_bus_type)) -+ goto error; -+ -+ if (WARN_ON(pool_type == FSL_MC_POOL_DPMCP)) -+ goto error; -+ -+ mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); -+ mc_bus = to_fsl_mc_bus(mc_bus_dev); -+ error = fsl_mc_resource_allocate(mc_bus, pool_type, &resource); -+ if (error < 0) -+ goto error; -+ -+ mc_adev = resource->data; -+ if (WARN_ON(!mc_adev)) -+ goto error; -+ -+ *new_mc_adev = mc_adev; -+ return 0; -+error: -+ if (resource) -+ fsl_mc_resource_free(resource); -+ -+ return error; -+} -+EXPORT_SYMBOL_GPL(fsl_mc_object_allocate); -+ -+/** -+ * fsl_mc_object_free - Returns an allocatable MC object device to the -+ * corresponding resource pool of a given MC bus. -+ * -+ * @mc_adev: Pointer to the MC object device -+ */ -+void fsl_mc_object_free(struct fsl_mc_device *mc_adev) -+{ -+ struct fsl_mc_resource *resource; -+ -+ resource = mc_adev->resource; -+ if (WARN_ON(resource->type == FSL_MC_POOL_DPMCP)) -+ return; -+ if (WARN_ON(resource->data != mc_adev)) -+ return; -+ -+ fsl_mc_resource_free(resource); -+} -+EXPORT_SYMBOL_GPL(fsl_mc_object_free); -+ -+/** -+ * It allocates the IRQs required by a given MC object device. The -+ * IRQs are allocated from the interrupt pool associated with the -+ * MC bus that contains the device, if the device is not a DPRC device. -+ * Otherwise, the IRQs are allocated from the interrupt pool associated -+ * with the MC bus that represents the DPRC device itself. -+ */ -+int __must_check fsl_mc_allocate_irqs(struct fsl_mc_device *mc_dev) -+{ -+ int i; -+ int irq_count; -+ int res_allocated_count = 0; -+ int error = -EINVAL; -+ struct fsl_mc_device_irq **irqs = NULL; -+ struct fsl_mc_bus *mc_bus; -+ struct fsl_mc_resource_pool *res_pool; -+ struct fsl_mc *mc = dev_get_drvdata(fsl_mc_bus_type.dev_root->parent); -+ -+ if (!mc->gic_supported) -+ return -ENOTSUPP; -+ -+ if (WARN_ON(mc_dev->irqs)) -+ goto error; -+ -+ irq_count = mc_dev->obj_desc.irq_count; -+ if (WARN_ON(irq_count == 0)) -+ goto error; -+ -+ if (strcmp(mc_dev->obj_desc.type, "dprc") == 0) -+ mc_bus = to_fsl_mc_bus(mc_dev); -+ else -+ mc_bus = to_fsl_mc_bus(to_fsl_mc_device(mc_dev->dev.parent)); -+ -+ if (WARN_ON(!mc_bus->irq_resources)) -+ goto error; -+ -+ res_pool = &mc_bus->resource_pools[FSL_MC_POOL_IRQ]; -+ if (res_pool->free_count < irq_count) { -+ dev_err(&mc_dev->dev, -+ "Not able to allocate %u irqs for device\n", irq_count); -+ error = -ENOSPC; -+ goto error; -+ } -+ -+ irqs = devm_kzalloc(&mc_dev->dev, irq_count * sizeof(irqs[0]), -+ GFP_KERNEL); -+ if (!irqs) { -+ error = -ENOMEM; -+ dev_err(&mc_dev->dev, "No memory to allocate irqs[]\n"); -+ goto error; -+ } -+ -+ for (i = 0; i < irq_count; i++) { -+ struct fsl_mc_resource *resource; -+ -+ error = fsl_mc_resource_allocate(mc_bus, FSL_MC_POOL_IRQ, -+ &resource); -+ if (error < 0) -+ goto error; -+ -+ irqs[i] = to_fsl_mc_irq(resource); -+ res_allocated_count++; -+ -+ WARN_ON(irqs[i]->mc_dev); -+ irqs[i]->mc_dev = mc_dev; -+ irqs[i]->dev_irq_index = i; -+ } -+ -+ mc_dev->irqs = irqs; -+ return 0; -+error: -+ for (i = 0; i < res_allocated_count; i++) { -+ irqs[i]->mc_dev = NULL; -+ fsl_mc_resource_free(&irqs[i]->resource); -+ } -+ -+ if (irqs) -+ devm_kfree(&mc_dev->dev, irqs); -+ -+ return error; -+} -+EXPORT_SYMBOL_GPL(fsl_mc_allocate_irqs); -+ -+/* -+ * It frees the IRQs that were allocated for a MC object device, by -+ * returning them to the corresponding interrupt pool. -+ */ -+void fsl_mc_free_irqs(struct fsl_mc_device *mc_dev) -+{ -+ int i; -+ int irq_count; -+ struct fsl_mc_bus *mc_bus; -+ struct fsl_mc_device_irq **irqs = mc_dev->irqs; -+ -+ if (WARN_ON(!irqs)) -+ return; -+ -+ irq_count = mc_dev->obj_desc.irq_count; -+ -+ if (strcmp(mc_dev->obj_desc.type, "dprc") == 0) -+ mc_bus = to_fsl_mc_bus(mc_dev); -+ else -+ mc_bus = to_fsl_mc_bus(to_fsl_mc_device(mc_dev->dev.parent)); -+ -+ if (WARN_ON(!mc_bus->irq_resources)) -+ return; -+ -+ for (i = 0; i < irq_count; i++) { -+ WARN_ON(!irqs[i]->mc_dev); -+ irqs[i]->mc_dev = NULL; -+ fsl_mc_resource_free(&irqs[i]->resource); -+ } -+ -+ devm_kfree(&mc_dev->dev, mc_dev->irqs); -+ mc_dev->irqs = NULL; -+} -+EXPORT_SYMBOL_GPL(fsl_mc_free_irqs); -+ -+/** -+ * fsl_mc_allocator_probe - callback invoked when an allocatable device is -+ * being added to the system -+ */ -+static int fsl_mc_allocator_probe(struct fsl_mc_device *mc_dev) -+{ -+ enum fsl_mc_pool_type pool_type; -+ struct fsl_mc_device *mc_bus_dev; -+ struct fsl_mc_bus *mc_bus; -+ int error = -EINVAL; -+ -+ if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type))) -+ goto error; -+ -+ mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); -+ if (WARN_ON(mc_bus_dev->dev.bus != &fsl_mc_bus_type)) -+ goto error; -+ -+ mc_bus = to_fsl_mc_bus(mc_bus_dev); -+ -+ /* -+ * If mc_dev is the DPMCP object for the parent DPRC's built-in -+ * portal, we don't add this DPMCP to the DPMCP object pool, -+ * but instead allocate it directly to the parent DPRC (mc_bus_dev): -+ */ -+ if (strcmp(mc_dev->obj_desc.type, "dpmcp") == 0 && -+ mc_dev->obj_desc.id == mc_bus->dprc_attr.portal_id) { -+ error = fsl_mc_io_set_dpmcp(mc_bus_dev->mc_io, mc_dev); -+ if (error < 0) -+ goto error; -+ } else { -+ error = object_type_to_pool_type(mc_dev->obj_desc.type, -+ &pool_type); -+ if (error < 0) -+ goto error; -+ -+ error = fsl_mc_resource_pool_add_device(mc_bus, pool_type, -+ mc_dev); -+ if (error < 0) -+ goto error; -+ } -+ -+ dev_dbg(&mc_dev->dev, -+ "Allocatable MC object device bound to fsl_mc_allocator driver"); -+ return 0; -+error: -+ -+ return error; -+} -+ -+/** -+ * fsl_mc_allocator_remove - callback invoked when an allocatable device is -+ * being removed from the system -+ */ -+static int fsl_mc_allocator_remove(struct fsl_mc_device *mc_dev) -+{ -+ int error; -+ -+ if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type))) -+ return -EINVAL; -+ -+ if (mc_dev->resource) { -+ error = fsl_mc_resource_pool_remove_device(mc_dev); -+ if (error < 0) -+ return error; -+ } -+ -+ dev_dbg(&mc_dev->dev, -+ "Allocatable MC object device unbound from fsl_mc_allocator driver"); -+ return 0; -+} -+ -+static const struct fsl_mc_device_match_id match_id_table[] = { -+ { -+ .vendor = FSL_MC_VENDOR_FREESCALE, -+ .obj_type = "dpbp", -+ }, -+ { -+ .vendor = FSL_MC_VENDOR_FREESCALE, -+ .obj_type = "dpmcp", -+ }, -+ { -+ .vendor = FSL_MC_VENDOR_FREESCALE, -+ .obj_type = "dpcon", -+ }, -+ {.vendor = 0x0}, -+}; -+ -+static struct fsl_mc_driver fsl_mc_allocator_driver = { -+ .driver = { -+ .name = "fsl_mc_allocator", -+ .owner = THIS_MODULE, -+ .pm = NULL, -+ }, -+ .match_id_table = match_id_table, -+ .probe = fsl_mc_allocator_probe, -+ .remove = fsl_mc_allocator_remove, -+}; -+ -+int __init fsl_mc_allocator_driver_init(void) -+{ -+ return fsl_mc_driver_register(&fsl_mc_allocator_driver); -+} -+ -+void __exit fsl_mc_allocator_driver_exit(void) -+{ -+ fsl_mc_driver_unregister(&fsl_mc_allocator_driver); -+} -diff --git a/drivers/staging/fsl-mc/bus/mc-bus.c b/drivers/staging/fsl-mc/bus/mc-bus.c -new file mode 100644 -index 0000000..f173b35 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/mc-bus.c -@@ -0,0 +1,1347 @@ -+/* -+ * Freescale Management Complex (MC) bus driver -+ * -+ * Copyright (C) 2014 Freescale Semiconductor, Inc. -+ * Author: German Rivera -+ * -+ * This file is licensed under the terms of the GNU General Public -+ * License version 2. This program is licensed "as is" without any -+ * warranty of any kind, whether express or implied. -+ */ -+ -+#include "../include/mc-private.h" -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "../include/dpmng.h" -+#include "../include/mc-sys.h" -+#include "dprc-cmd.h" -+ -+/* -+ * IOMMU stream ID flags -+ */ -+#define STREAM_ID_PL_MASK BIT(9) /* privilege level */ -+#define STREAM_ID_BMT_MASK BIT(8) /* bypass memory translation */ -+#define STREAM_ID_VA_MASK BIT(7) /* virtual address translation -+ * (two-stage translation) */ -+#define STREAM_ID_ICID_MASK (BIT(7) - 1) /* isolation context ID -+ * (translation context) */ -+ -+#define MAX_STREAM_ID_ICID STREAM_ID_ICID_MASK -+ -+static struct kmem_cache *mc_dev_cache; -+ -+/** -+ * fsl_mc_bus_match - device to driver matching callback -+ * @dev: the MC object device structure to match against -+ * @drv: the device driver to search for matching MC object device id -+ * structures -+ * -+ * Returns 1 on success, 0 otherwise. -+ */ -+static int fsl_mc_bus_match(struct device *dev, struct device_driver *drv) -+{ -+ const struct fsl_mc_device_match_id *id; -+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); -+ struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(drv); -+ bool found = false; -+ -+ /* When driver_override is set, only bind to the matching driver */ -+ if (mc_dev->driver_override) { -+ found = !strcmp(mc_dev->driver_override, mc_drv->driver.name); -+ goto out; -+ } -+ -+ if (!mc_drv->match_id_table) -+ goto out; -+ -+ /* -+ * If the object is not 'plugged' don't match. -+ * Only exception is the root DPRC, which is a special case. -+ * -+ * NOTE: Only when this function is invoked for the root DPRC, -+ * mc_dev->mc_io is not NULL -+ */ -+ if ((mc_dev->obj_desc.state & DPRC_OBJ_STATE_PLUGGED) == 0 && -+ !mc_dev->mc_io) -+ goto out; -+ -+ /* -+ * Traverse the match_id table of the given driver, trying to find -+ * a matching for the given MC object device. -+ */ -+ for (id = mc_drv->match_id_table; id->vendor != 0x0; id++) { -+ if (id->vendor == mc_dev->obj_desc.vendor && -+ strcmp(id->obj_type, mc_dev->obj_desc.type) == 0) { -+ found = true; -+ -+ break; -+ } -+ } -+ -+out: -+ dev_dbg(dev, "%smatched\n", found ? "" : "not "); -+ return found; -+} -+ -+/** -+ * fsl_mc_bus_uevent - callback invoked when a device is added -+ */ -+static int fsl_mc_bus_uevent(struct device *dev, struct kobj_uevent_env *env) -+{ -+ pr_debug("%s invoked\n", __func__); -+ return 0; -+} -+ -+static ssize_t driver_override_store(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); -+ const char *driver_override, *old = mc_dev->driver_override; -+ char *cp; -+ -+ if (WARN_ON(dev->bus != &fsl_mc_bus_type)) -+ return -EINVAL; -+ -+ if (count > PATH_MAX) -+ return -EINVAL; -+ -+ driver_override = kstrndup(buf, count, GFP_KERNEL); -+ if (!driver_override) -+ return -ENOMEM; -+ -+ cp = strchr(driver_override, '\n'); -+ if (cp) -+ *cp = '\0'; -+ -+ if (strlen(driver_override)) { -+ mc_dev->driver_override = driver_override; -+ } else { -+ kfree(driver_override); -+ mc_dev->driver_override = NULL; -+ } -+ -+ kfree(old); -+ -+ return count; -+} -+ -+static ssize_t driver_override_show(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); -+ -+ return sprintf(buf, "%s\n", mc_dev->driver_override); -+} -+ -+static DEVICE_ATTR_RW(driver_override); -+ -+static ssize_t rescan_store(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ unsigned long val; -+ unsigned int irq_count; -+ struct fsl_mc_device *root_mc_dev; -+ struct fsl_mc_bus *root_mc_bus; -+ -+ if (!is_root_dprc(dev)) -+ return -EINVAL; -+ -+ root_mc_dev = to_fsl_mc_device(dev); -+ root_mc_bus = to_fsl_mc_bus(root_mc_dev); -+ -+ if (kstrtoul(buf, 0, &val) < 0) -+ return -EINVAL; -+ -+ if (val) { -+ mutex_lock(&root_mc_bus->scan_mutex); -+ dprc_scan_objects(root_mc_dev, NULL, &irq_count); -+ mutex_unlock(&root_mc_bus->scan_mutex); -+ } -+ -+ return count; -+} -+ -+static DEVICE_ATTR_WO(rescan); -+ -+static struct attribute *fsl_mc_dev_attrs[] = { -+ &dev_attr_driver_override.attr, -+ &dev_attr_rescan.attr, -+ NULL, -+}; -+ -+static const struct attribute_group fsl_mc_dev_group = { -+ .attrs = fsl_mc_dev_attrs, -+}; -+ -+static const struct attribute_group *fsl_mc_dev_groups[] = { -+ &fsl_mc_dev_group, -+ NULL, -+}; -+ -+static int scan_fsl_mc_bus(struct device *dev, void *data) -+{ -+ unsigned int irq_count; -+ struct fsl_mc_device *root_mc_dev; -+ struct fsl_mc_bus *root_mc_bus; -+ -+ if (is_root_dprc(dev)) { -+ root_mc_dev = to_fsl_mc_device(dev); -+ root_mc_bus = to_fsl_mc_bus(root_mc_dev); -+ mutex_lock(&root_mc_bus->scan_mutex); -+ dprc_scan_objects(root_mc_dev, NULL, &irq_count); -+ mutex_unlock(&root_mc_bus->scan_mutex); -+ } -+ -+ return 0; -+} -+ -+static ssize_t bus_rescan_store(struct bus_type *bus, -+ const char *buf, size_t count) -+{ -+ unsigned long val; -+ -+ if (kstrtoul(buf, 0, &val) < 0) -+ return -EINVAL; -+ -+ if (val) -+ bus_for_each_dev(bus, NULL, NULL, scan_fsl_mc_bus); -+ -+ return count; -+} -+static BUS_ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, bus_rescan_store); -+ -+static struct attribute *fsl_mc_bus_attrs[] = { -+ &bus_attr_rescan.attr, -+ NULL, -+}; -+ -+static const struct attribute_group fsl_mc_bus_group = { -+ .attrs = fsl_mc_bus_attrs, -+}; -+ -+static const struct attribute_group *fsl_mc_bus_groups[] = { -+ &fsl_mc_bus_group, -+ NULL, -+}; -+ -+struct bus_type fsl_mc_bus_type = { -+ .name = "fsl-mc", -+ .match = fsl_mc_bus_match, -+ .uevent = fsl_mc_bus_uevent, -+ .dev_groups = fsl_mc_dev_groups, -+ .bus_groups = fsl_mc_bus_groups, -+}; -+EXPORT_SYMBOL_GPL(fsl_mc_bus_type); -+ -+static int fsl_mc_driver_probe(struct device *dev) -+{ -+ struct fsl_mc_driver *mc_drv; -+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); -+ int error; -+ -+ if (WARN_ON(!dev->driver)) -+ return -EINVAL; -+ -+ mc_drv = to_fsl_mc_driver(dev->driver); -+ if (WARN_ON(!mc_drv->probe)) -+ return -EINVAL; -+ -+ error = mc_drv->probe(mc_dev); -+ if (error < 0) { -+ dev_err(dev, "MC object device probe callback failed: %d\n", -+ error); -+ return error; -+ } -+ -+ return 0; -+} -+ -+static int fsl_mc_driver_remove(struct device *dev) -+{ -+ struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver); -+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); -+ int error; -+ -+ if (WARN_ON(!dev->driver)) -+ return -EINVAL; -+ -+ error = mc_drv->remove(mc_dev); -+ if (error < 0) { -+ dev_err(dev, -+ "MC object device remove callback failed: %d\n", -+ error); -+ return error; -+ } -+ -+ return 0; -+} -+ -+static void fsl_mc_driver_shutdown(struct device *dev) -+{ -+ struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver); -+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); -+ -+ mc_drv->shutdown(mc_dev); -+} -+ -+/** -+ * __fsl_mc_driver_register - registers a child device driver with the -+ * MC bus -+ * -+ * This function is implicitly invoked from the registration function of -+ * fsl_mc device drivers, which is generated by the -+ * module_fsl_mc_driver() macro. -+ */ -+int __fsl_mc_driver_register(struct fsl_mc_driver *mc_driver, -+ struct module *owner) -+{ -+ int error; -+ -+ mc_driver->driver.owner = owner; -+ mc_driver->driver.bus = &fsl_mc_bus_type; -+ -+ if (mc_driver->probe) -+ mc_driver->driver.probe = fsl_mc_driver_probe; -+ -+ if (mc_driver->remove) -+ mc_driver->driver.remove = fsl_mc_driver_remove; -+ -+ if (mc_driver->shutdown) -+ mc_driver->driver.shutdown = fsl_mc_driver_shutdown; -+ -+ error = driver_register(&mc_driver->driver); -+ if (error < 0) { -+ pr_err("driver_register() failed for %s: %d\n", -+ mc_driver->driver.name, error); -+ return error; -+ } -+ -+ pr_info("MC object device driver %s registered\n", -+ mc_driver->driver.name); -+ return 0; -+} -+EXPORT_SYMBOL_GPL(__fsl_mc_driver_register); -+ -+/** -+ * fsl_mc_driver_unregister - unregisters a device driver from the -+ * MC bus -+ */ -+void fsl_mc_driver_unregister(struct fsl_mc_driver *mc_driver) -+{ -+ driver_unregister(&mc_driver->driver); -+} -+EXPORT_SYMBOL_GPL(fsl_mc_driver_unregister); -+ -+bool fsl_mc_interrupts_supported(void) -+{ -+ struct fsl_mc *mc = dev_get_drvdata(fsl_mc_bus_type.dev_root->parent); -+ -+ return mc->gic_supported; -+} -+EXPORT_SYMBOL_GPL(fsl_mc_interrupts_supported); -+ -+static int get_dprc_attr(struct fsl_mc_io *mc_io, -+ int container_id, struct dprc_attributes *attr) -+{ -+ uint16_t dprc_handle; -+ int error; -+ -+ error = dprc_open(mc_io, 0, container_id, &dprc_handle); -+ if (error < 0) { -+ pr_err("dprc_open() failed: %d\n", error); -+ return error; -+ } -+ -+ memset(attr, 0, sizeof(struct dprc_attributes)); -+ error = dprc_get_attributes(mc_io, 0, dprc_handle, attr); -+ if (error < 0) { -+ pr_err("dprc_get_attributes() failed: %d\n", error); -+ goto common_cleanup; -+ } -+ -+ error = 0; -+ -+common_cleanup: -+ (void)dprc_close(mc_io, 0, dprc_handle); -+ return error; -+} -+ -+static int get_dprc_icid(struct fsl_mc_io *mc_io, -+ int container_id, uint16_t *icid) -+{ -+ struct dprc_attributes attr; -+ int error; -+ -+ error = get_dprc_attr(mc_io, container_id, &attr); -+ if (error == 0) -+ *icid = attr.icid; -+ -+ return error; -+} -+ -+static int get_dprc_version(struct fsl_mc_io *mc_io, -+ int container_id, uint16_t *major, uint16_t *minor) -+{ -+ struct dprc_attributes attr; -+ int error; -+ -+ error = get_dprc_attr(mc_io, container_id, &attr); -+ if (error == 0) { -+ *major = attr.version.major; -+ *minor = attr.version.minor; -+ } -+ -+ return error; -+} -+ -+static int translate_mc_addr(enum fsl_mc_region_types mc_region_type, -+ uint64_t mc_offset, phys_addr_t *phys_addr) -+{ -+ int i; -+ struct fsl_mc *mc = dev_get_drvdata(fsl_mc_bus_type.dev_root->parent); -+ -+ if (mc->num_translation_ranges == 0) { -+ /* -+ * Do identity mapping: -+ */ -+ *phys_addr = mc_offset; -+ return 0; -+ } -+ -+ for (i = 0; i < mc->num_translation_ranges; i++) { -+ struct fsl_mc_addr_translation_range *range = -+ &mc->translation_ranges[i]; -+ -+ if (mc_region_type == range->mc_region_type && -+ mc_offset >= range->start_mc_offset && -+ mc_offset < range->end_mc_offset) { -+ *phys_addr = range->start_phys_addr + -+ (mc_offset - range->start_mc_offset); -+ return 0; -+ } -+ } -+ -+ return -EFAULT; -+} -+ -+static int fsl_mc_device_get_mmio_regions(struct fsl_mc_device *mc_dev, -+ struct fsl_mc_device *mc_bus_dev) -+{ -+ int i; -+ int error; -+ struct resource *regions; -+ struct dprc_obj_desc *obj_desc = &mc_dev->obj_desc; -+ struct device *parent_dev = mc_dev->dev.parent; -+ enum fsl_mc_region_types mc_region_type; -+ -+ if (strcmp(obj_desc->type, "dprc") == 0 || -+ strcmp(obj_desc->type, "dpmcp") == 0) { -+ mc_region_type = FSL_MC_PORTAL; -+ } else if (strcmp(obj_desc->type, "dpio") == 0) { -+ mc_region_type = FSL_QBMAN_PORTAL; -+ } else { -+ /* -+ * This function should not have been called for this MC object -+ * type, as this object type is not supposed to have MMIO -+ * regions -+ */ -+ WARN_ON(true); -+ return -EINVAL; -+ } -+ -+ regions = kmalloc_array(obj_desc->region_count, -+ sizeof(regions[0]), GFP_KERNEL); -+ if (!regions) -+ return -ENOMEM; -+ -+ for (i = 0; i < obj_desc->region_count; i++) { -+ struct dprc_region_desc region_desc; -+ -+ error = dprc_get_obj_region(mc_bus_dev->mc_io, -+ 0, -+ mc_bus_dev->mc_handle, -+ obj_desc->type, -+ obj_desc->id, i, ®ion_desc); -+ if (error < 0) { -+ dev_err(parent_dev, -+ "dprc_get_obj_region() failed: %d\n", error); -+ goto error_cleanup_regions; -+ } -+ -+ WARN_ON(region_desc.size == 0); -+ error = translate_mc_addr(mc_region_type, -+ region_desc.base_offset, -+ ®ions[i].start); -+ if (error < 0) { -+ dev_err(parent_dev, -+ "Invalid MC offset: %#x (for %s.%d\'s region %d)\n", -+ region_desc.base_offset, -+ obj_desc->type, obj_desc->id, i); -+ goto error_cleanup_regions; -+ } -+ -+ regions[i].end = regions[i].start + region_desc.size - 1; -+ regions[i].name = "fsl-mc object MMIO region"; -+ regions[i].flags = IORESOURCE_IO; -+ if (region_desc.flags & DPRC_REGION_CACHEABLE) -+ regions[i].flags |= IORESOURCE_CACHEABLE; -+ } -+ -+ mc_dev->regions = regions; -+ return 0; -+ -+error_cleanup_regions: -+ kfree(regions); -+ return error; -+} -+ -+/** -+ * Add a newly discovered MC object device to be visible in Linux -+ */ -+int fsl_mc_device_add(struct dprc_obj_desc *obj_desc, -+ struct fsl_mc_io *mc_io, -+ struct device *parent_dev, -+ const char *driver_override, -+ struct fsl_mc_device **new_mc_dev) -+{ -+ int error; -+ struct fsl_mc_device *mc_dev = NULL; -+ struct fsl_mc_bus *mc_bus = NULL; -+ struct fsl_mc_device *parent_mc_dev; -+ -+ if (parent_dev->bus == &fsl_mc_bus_type) -+ parent_mc_dev = to_fsl_mc_device(parent_dev); -+ else -+ parent_mc_dev = NULL; -+ -+ if (strcmp(obj_desc->type, "dprc") == 0) { -+ /* -+ * Allocate an MC bus device object: -+ */ -+ mc_bus = devm_kzalloc(parent_dev, sizeof(*mc_bus), GFP_KERNEL); -+ if (!mc_bus) -+ return -ENOMEM; -+ -+ mc_dev = &mc_bus->mc_dev; -+ } else { -+ /* -+ * Allocate a regular fsl_mc_device object: -+ */ -+ mc_dev = kmem_cache_zalloc(mc_dev_cache, GFP_KERNEL); -+ if (!mc_dev) -+ return -ENOMEM; -+ } -+ -+ mc_dev->obj_desc = *obj_desc; -+ mc_dev->mc_io = mc_io; -+ if (driver_override) { -+ /* -+ * We trust driver_override, so we don't need to use -+ * kstrndup() here -+ */ -+ mc_dev->driver_override = kstrdup(driver_override, GFP_KERNEL); -+ if (!mc_dev->driver_override) { -+ error = -ENOMEM; -+ goto error_cleanup_dev; -+ } -+ } -+ -+ device_initialize(&mc_dev->dev); -+ INIT_LIST_HEAD(&mc_dev->dev.msi_list); -+ mc_dev->dev.parent = parent_dev; -+ mc_dev->dev.bus = &fsl_mc_bus_type; -+ dev_set_name(&mc_dev->dev, "%s.%d", obj_desc->type, obj_desc->id); -+ -+ if (strcmp(obj_desc->type, "dprc") == 0) { -+ struct fsl_mc_io *mc_io2; -+ -+ mc_dev->flags |= FSL_MC_IS_DPRC; -+ -+ /* -+ * To get the DPRC's ICID, we need to open the DPRC -+ * in get_dprc_icid(). For child DPRCs, we do so using the -+ * parent DPRC's MC portal instead of the child DPRC's MC -+ * portal, in case the child DPRC is already opened with -+ * its own portal (e.g., the DPRC used by AIOP). -+ * -+ * NOTE: There cannot be more than one active open for a -+ * given MC object, using the same MC portal. -+ */ -+ if (parent_mc_dev) { -+ /* -+ * device being added is a child DPRC device -+ */ -+ mc_io2 = parent_mc_dev->mc_io; -+ } else { -+ /* -+ * device being added is the root DPRC device -+ */ -+ if (WARN_ON(!mc_io)) { -+ error = -EINVAL; -+ goto error_cleanup_dev; -+ } -+ -+ mc_io2 = mc_io; -+ } -+ -+ error = get_dprc_icid(mc_io2, obj_desc->id, &mc_dev->icid); -+ if (error < 0) -+ goto error_cleanup_dev; -+ } else { -+ /* -+ * A non-DPRC MC object device has to be a child of another -+ * MC object (specifically a DPRC object) -+ */ -+ mc_dev->icid = parent_mc_dev->icid; -+ mc_dev->dma_mask = FSL_MC_DEFAULT_DMA_MASK; -+ mc_dev->dev.dma_mask = &mc_dev->dma_mask; -+ } -+ -+ /* -+ * Get MMIO regions for the device from the MC: -+ * -+ * NOTE: the root DPRC is a special case as its MMIO region is -+ * obtained from the device tree -+ */ -+ if (parent_mc_dev && obj_desc->region_count != 0) { -+ error = fsl_mc_device_get_mmio_regions(mc_dev, -+ parent_mc_dev); -+ if (error < 0) -+ goto error_cleanup_dev; -+ } -+ -+ /* -+ * Objects are coherent, unless 'no shareability' flag set. -+ * FIXME: fill up @dma_base, @size, @iommu -+ */ -+ if (!(obj_desc->flags & DPRC_OBJ_FLAG_NO_MEM_SHAREABILITY)) -+ arch_setup_dma_ops(&mc_dev->dev, 0, 0, NULL, true); -+ -+ /* -+ * The device-specific probe callback will get invoked by device_add() -+ */ -+ error = device_add(&mc_dev->dev); -+ if (error < 0) { -+ dev_err(parent_dev, -+ "device_add() failed for device %s: %d\n", -+ dev_name(&mc_dev->dev), error); -+ goto error_cleanup_dev; -+ } -+ -+ (void)get_device(&mc_dev->dev); -+ dev_dbg(parent_dev, "Added MC object device %s\n", -+ dev_name(&mc_dev->dev)); -+ -+ *new_mc_dev = mc_dev; -+ return 0; -+ -+error_cleanup_dev: -+ kfree(mc_dev->regions); -+ if (mc_bus) -+ devm_kfree(parent_dev, mc_bus); -+ else -+ kmem_cache_free(mc_dev_cache, mc_dev); -+ -+ return error; -+} -+EXPORT_SYMBOL_GPL(fsl_mc_device_add); -+ -+/** -+ * fsl_mc_device_remove - Remove a MC object device from being visible to -+ * Linux -+ * -+ * @mc_dev: Pointer to a MC object device object -+ */ -+void fsl_mc_device_remove(struct fsl_mc_device *mc_dev) -+{ -+ struct fsl_mc_bus *mc_bus = NULL; -+ -+ kfree(mc_dev->regions); -+ -+ /* -+ * The device-specific remove callback will get invoked by device_del() -+ */ -+ device_del(&mc_dev->dev); -+ put_device(&mc_dev->dev); -+ -+ if (strcmp(mc_dev->obj_desc.type, "dprc") == 0) { -+ mc_bus = to_fsl_mc_bus(mc_dev); -+ -+ if (&mc_dev->dev == fsl_mc_bus_type.dev_root) -+ fsl_mc_bus_type.dev_root = NULL; -+ } else -+ WARN_ON(mc_dev->mc_io != NULL); -+ -+ kfree(mc_dev->driver_override); -+ mc_dev->driver_override = NULL; -+ if (mc_bus) -+ devm_kfree(mc_dev->dev.parent, mc_bus); -+ else -+ kmem_cache_free(mc_dev_cache, mc_dev); -+} -+EXPORT_SYMBOL_GPL(fsl_mc_device_remove); -+ -+static int mc_bus_msi_prepare(struct irq_domain *domain, struct device *dev, -+ int nvec, msi_alloc_info_t *info) -+{ -+ int error; -+ u32 its_dev_id; -+ struct dprc_attributes dprc_attr; -+ struct fsl_mc_device *mc_bus_dev = to_fsl_mc_device(dev); -+ -+ if (WARN_ON(!(mc_bus_dev->flags & FSL_MC_IS_DPRC))) -+ return -EINVAL; -+ -+ error = dprc_get_attributes(mc_bus_dev->mc_io, -+ 0, -+ mc_bus_dev->mc_handle, &dprc_attr); -+ if (error < 0) { -+ dev_err(&mc_bus_dev->dev, -+ "dprc_get_attributes() failed: %d\n", -+ error); -+ return error; -+ } -+ -+ /* -+ * Build the device Id to be passed to the GIC-ITS: -+ * -+ * NOTE: This device id corresponds to the IOMMU stream ID -+ * associated with the DPRC object. -+ */ -+ its_dev_id = mc_bus_dev->icid; -+ if (its_dev_id > STREAM_ID_ICID_MASK) { -+ dev_err(&mc_bus_dev->dev, -+ "Invalid ICID: %#x\n", its_dev_id); -+ return -ERANGE; -+ } -+ -+ if (dprc_attr.options & DPRC_CFG_OPT_AIOP) -+ its_dev_id |= STREAM_ID_PL_MASK | STREAM_ID_BMT_MASK; -+ -+ return __its_msi_prepare(domain, its_dev_id, dev, nvec, info); -+} -+ -+static void mc_bus_mask_msi_irq(struct irq_data *d) -+{ -+ /* Bus specefic Mask */ -+ irq_chip_mask_parent(d); -+} -+ -+static void mc_bus_unmask_msi_irq(struct irq_data *d) -+{ -+ /* Bus specefic unmask */ -+ irq_chip_unmask_parent(d); -+} -+ -+static void program_msi_at_mc(struct fsl_mc_device *mc_bus_dev, -+ struct fsl_mc_device_irq *irq) -+{ -+ int error; -+ struct fsl_mc_device *owner_mc_dev = irq->mc_dev; -+ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); -+ struct dprc_irq_cfg irq_cfg; -+ -+ /* -+ * irq->msi_paddr is 0x0 when this function is invoked in the -+ * free_irq() code path. In this case, for the MC, we don't -+ * really need to "unprogram" the MSI, so we just return. -+ * This helps avoid subtle ordering problems in the MC -+ * bus IRQ teardown logic. -+ * FIXME: evaluate whether there is a better way to address -+ * the underlying issue (upstreamability concern) -+ */ -+ if (irq->msi_paddr == 0x0) -+ return; -+ -+ if (WARN_ON(!owner_mc_dev)) -+ return; -+ -+ irq_cfg.paddr = irq->msi_paddr; -+ irq_cfg.val = irq->msi_value; -+ irq_cfg.irq_num = irq->irq_number; -+ -+ if (owner_mc_dev == mc_bus_dev) { -+ /* -+ * IRQ is for the mc_bus_dev's DPRC itself -+ */ -+ error = dprc_set_irq(mc_bus->atomic_mc_io, -+ MC_CMD_FLAG_INTR_DIS | MC_CMD_FLAG_PRI, -+ mc_bus->atomic_dprc_handle, -+ irq->dev_irq_index, -+ &irq_cfg); -+ if (error < 0) { -+ dev_err(&owner_mc_dev->dev, -+ "dprc_set_irq() failed: %d\n", error); -+ } -+ } else { -+ error = dprc_set_obj_irq(mc_bus->atomic_mc_io, -+ MC_CMD_FLAG_INTR_DIS | MC_CMD_FLAG_PRI, -+ mc_bus->atomic_dprc_handle, -+ owner_mc_dev->obj_desc.type, -+ owner_mc_dev->obj_desc.id, -+ irq->dev_irq_index, -+ &irq_cfg); -+ if (error < 0) { -+ dev_err(&owner_mc_dev->dev, -+ "dprc_obj_set_irq() failed: %d\n", error); -+ } -+ } -+} -+ -+/* -+ * This function is invoked from devm_request_irq(), -+ * devm_request_threaded_irq(), dev_free_irq() -+ */ -+static void mc_bus_msi_domain_write_msg(struct irq_data *irq_data, -+ struct msi_msg *msg) -+{ -+ struct msi_desc *msi_entry = irq_data->msi_desc; -+ struct fsl_mc_device *mc_bus_dev = to_fsl_mc_device(msi_entry->dev); -+ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); -+ struct fsl_mc_device_irq *irq_res = -+ &mc_bus->irq_resources[msi_entry->msi_attrib.entry_nr]; -+ -+ /* -+ * NOTE: This function is invoked with interrupts disabled -+ */ -+ -+ if (irq_res->irq_number == irq_data->irq) { -+ irq_res->msi_paddr = -+ ((u64)msg->address_hi << 32) | msg->address_lo; -+ -+ irq_res->msi_value = msg->data; -+ -+ /* -+ * Program the MSI (paddr, value) pair in the device: -+ */ -+ program_msi_at_mc(mc_bus_dev, irq_res); -+ } -+} -+ -+static struct irq_chip mc_bus_msi_irq_chip = { -+ .name = "fsl-mc-bus-msi", -+ .irq_unmask = mc_bus_unmask_msi_irq, -+ .irq_mask = mc_bus_mask_msi_irq, -+ .irq_eoi = irq_chip_eoi_parent, -+ .irq_write_msi_msg = mc_bus_msi_domain_write_msg, -+}; -+ -+static struct msi_domain_ops mc_bus_msi_ops = { -+ .msi_prepare = mc_bus_msi_prepare, -+}; -+ -+static struct msi_domain_info mc_bus_msi_domain_info = { -+ .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | -+ MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX), -+ .ops = &mc_bus_msi_ops, -+ .chip = &mc_bus_msi_irq_chip, -+}; -+ -+static int create_mc_irq_domain(struct platform_device *mc_pdev, -+ struct irq_domain **new_irq_domain) -+{ -+ int error; -+ struct device_node *its_of_node; -+ struct irq_domain *its_domain; -+ struct irq_domain *irq_domain; -+ struct device_node *mc_of_node = mc_pdev->dev.of_node; -+ -+ its_of_node = of_parse_phandle(mc_of_node, "msi-parent", 0); -+ if (!its_of_node) { -+ dev_err(&mc_pdev->dev, -+ "msi-parent phandle missing for %s\n", -+ mc_of_node->full_name); -+ return -ENOENT; -+ } -+ -+ /* -+ * Extract MSI parent node: -+ */ -+ its_domain = irq_find_host(its_of_node); -+ if (!its_domain) { -+ dev_err(&mc_pdev->dev, "Unable to find parent domain\n"); -+ error = -ENOENT; -+ goto cleanup_its_of_node; -+ } -+ -+ irq_domain = msi_create_irq_domain(mc_of_node, &mc_bus_msi_domain_info, -+ its_domain->parent); -+ if (!irq_domain) { -+ dev_err(&mc_pdev->dev, "Failed to allocate msi_domain\n"); -+ error = -ENOMEM; -+ goto cleanup_its_of_node; -+ } -+ -+ dev_dbg(&mc_pdev->dev, "Allocated MSI domain\n"); -+ *new_irq_domain = irq_domain; -+ return 0; -+ -+cleanup_its_of_node: -+ of_node_put(its_of_node); -+ return error; -+} -+ -+/* -+ * Initialize the interrupt pool associated with a MC bus. -+ * It allocates a block of IRQs from the GIC-ITS -+ */ -+int __must_check fsl_mc_populate_irq_pool(struct fsl_mc_bus *mc_bus, -+ unsigned int irq_count) -+{ -+ unsigned int i; -+ struct msi_desc *msi_entry; -+ struct msi_desc *next_msi_entry; -+ struct fsl_mc_device_irq *irq_resources; -+ struct fsl_mc_device_irq *irq_res; -+ int error; -+ struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev; -+ struct fsl_mc *mc = dev_get_drvdata(fsl_mc_bus_type.dev_root->parent); -+ struct fsl_mc_resource_pool *res_pool = -+ &mc_bus->resource_pools[FSL_MC_POOL_IRQ]; -+ -+ /* -+ * Detect duplicate invocations of this function: -+ */ -+ if (WARN_ON(!list_empty(&mc_bus_dev->dev.msi_list))) -+ return -EINVAL; -+ -+ if (WARN_ON(irq_count == 0 || -+ irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS)) -+ return -EINVAL; -+ -+ irq_resources = -+ devm_kzalloc(&mc_bus_dev->dev, -+ sizeof(*irq_resources) * irq_count, -+ GFP_KERNEL); -+ if (!irq_resources) -+ return -ENOMEM; -+ -+ for (i = 0; i < irq_count; i++) { -+ irq_res = &irq_resources[i]; -+ msi_entry = alloc_msi_entry(&mc_bus_dev->dev); -+ if (!msi_entry) { -+ dev_err(&mc_bus_dev->dev, "Failed to allocate msi entry\n"); -+ error = -ENOMEM; -+ goto cleanup_msi_entries; -+ } -+ -+ msi_entry->msi_attrib.is_msix = 1; -+ msi_entry->msi_attrib.is_64 = 1; -+ msi_entry->msi_attrib.entry_nr = i; -+ msi_entry->nvec_used = 1; -+ list_add_tail(&msi_entry->list, &mc_bus_dev->dev.msi_list); -+ -+ /* -+ * NOTE: irq_res->msi_paddr will be set by the -+ * mc_bus_msi_domain_write_msg() callback -+ */ -+ irq_res->resource.type = res_pool->type; -+ irq_res->resource.data = irq_res; -+ irq_res->resource.parent_pool = res_pool; -+ INIT_LIST_HEAD(&irq_res->resource.node); -+ list_add_tail(&irq_res->resource.node, &res_pool->free_list); -+ } -+ -+ /* -+ * NOTE: Calling this function will trigger the invocation of the -+ * mc_bus_msi_prepare() callback -+ */ -+ error = msi_domain_alloc_irqs(mc->irq_domain, -+ &mc_bus_dev->dev, irq_count); -+ -+ if (error) { -+ dev_err(&mc_bus_dev->dev, "Failed to allocate IRQs\n"); -+ goto cleanup_msi_entries; -+ } -+ -+ for_each_msi_entry(msi_entry, &mc_bus_dev->dev) { -+ u32 irq_num = msi_entry->irq; -+ -+ irq_res = &irq_resources[msi_entry->msi_attrib.entry_nr]; -+ irq_res->irq_number = irq_num; -+ irq_res->resource.id = irq_num; -+ } -+ -+ res_pool->max_count = irq_count; -+ res_pool->free_count = irq_count; -+ mc_bus->irq_resources = irq_resources; -+ return 0; -+ -+cleanup_msi_entries: -+ list_for_each_entry_safe(msi_entry, next_msi_entry, -+ &mc_bus_dev->dev.msi_list, list) { -+ list_del(&msi_entry->list); -+ kfree(msi_entry); -+ } -+ -+ devm_kfree(&mc_bus_dev->dev, irq_resources); -+ return error; -+} -+EXPORT_SYMBOL_GPL(fsl_mc_populate_irq_pool); -+ -+/** -+ * Teardown the interrupt pool associated with an MC bus. -+ * It frees the IRQs that were allocated to the pool, back to the GIC-ITS. -+ */ -+void fsl_mc_cleanup_irq_pool(struct fsl_mc_bus *mc_bus) -+{ -+ struct msi_desc *msi_entry; -+ struct msi_desc *next_msi_entry; -+ struct fsl_mc *mc = dev_get_drvdata(fsl_mc_bus_type.dev_root->parent); -+ struct fsl_mc_resource_pool *res_pool = -+ &mc_bus->resource_pools[FSL_MC_POOL_IRQ]; -+ -+ if (WARN_ON(!mc_bus->irq_resources)) -+ return; -+ -+ if (WARN_ON(res_pool->max_count == 0)) -+ return; -+ -+ if (WARN_ON(res_pool->free_count != res_pool->max_count)) -+ return; -+ -+ msi_domain_free_irqs(mc->irq_domain, &mc_bus->mc_dev.dev); -+ list_for_each_entry_safe(msi_entry, next_msi_entry, -+ &mc_bus->mc_dev.dev.msi_list, list) { -+ list_del(&msi_entry->list); -+ kfree(msi_entry); -+ } -+ -+ devm_kfree(&mc_bus->mc_dev.dev, mc_bus->irq_resources); -+ res_pool->max_count = 0; -+ res_pool->free_count = 0; -+ mc_bus->irq_resources = NULL; -+} -+EXPORT_SYMBOL_GPL(fsl_mc_cleanup_irq_pool); -+ -+static int parse_mc_ranges(struct device *dev, -+ int *paddr_cells, -+ int *mc_addr_cells, -+ int *mc_size_cells, -+ const __be32 **ranges_start, -+ uint8_t *num_ranges) -+{ -+ const __be32 *prop; -+ int range_tuple_cell_count; -+ int ranges_len; -+ int tuple_len; -+ struct device_node *mc_node = dev->of_node; -+ -+ *ranges_start = of_get_property(mc_node, "ranges", &ranges_len); -+ if (!(*ranges_start) || !ranges_len) { -+ dev_warn(dev, -+ "missing or empty ranges property for device tree node '%s'\n", -+ mc_node->name); -+ -+ *num_ranges = 0; -+ return 0; -+ } -+ -+ *paddr_cells = of_n_addr_cells(mc_node); -+ -+ prop = of_get_property(mc_node, "#address-cells", NULL); -+ if (prop) -+ *mc_addr_cells = be32_to_cpup(prop); -+ else -+ *mc_addr_cells = *paddr_cells; -+ -+ prop = of_get_property(mc_node, "#size-cells", NULL); -+ if (prop) -+ *mc_size_cells = be32_to_cpup(prop); -+ else -+ *mc_size_cells = of_n_size_cells(mc_node); -+ -+ range_tuple_cell_count = *paddr_cells + *mc_addr_cells + -+ *mc_size_cells; -+ -+ tuple_len = range_tuple_cell_count * sizeof(__be32); -+ if (ranges_len % tuple_len != 0) { -+ dev_err(dev, "malformed ranges property '%s'\n", mc_node->name); -+ return -EINVAL; -+ } -+ -+ *num_ranges = ranges_len / tuple_len; -+ return 0; -+} -+ -+static int get_mc_addr_translation_ranges(struct device *dev, -+ struct fsl_mc_addr_translation_range -+ **ranges, -+ uint8_t *num_ranges) -+{ -+ int error; -+ int paddr_cells; -+ int mc_addr_cells; -+ int mc_size_cells; -+ int i; -+ const __be32 *ranges_start; -+ const __be32 *cell; -+ -+ error = parse_mc_ranges(dev, -+ &paddr_cells, -+ &mc_addr_cells, -+ &mc_size_cells, -+ &ranges_start, -+ num_ranges); -+ if (error < 0) -+ return error; -+ -+ if (!(*num_ranges)) { -+ /* -+ * Missing or empty ranges property ("ranges;") for the -+ * 'fsl,qoriq-mc' node. In this case, identity mapping -+ * will be used. -+ */ -+ *ranges = NULL; -+ return 0; -+ } -+ -+ *ranges = devm_kcalloc(dev, *num_ranges, -+ sizeof(struct fsl_mc_addr_translation_range), -+ GFP_KERNEL); -+ if (!(*ranges)) -+ return -ENOMEM; -+ -+ cell = ranges_start; -+ for (i = 0; i < *num_ranges; ++i) { -+ struct fsl_mc_addr_translation_range *range = &(*ranges)[i]; -+ -+ range->mc_region_type = of_read_number(cell, 1); -+ range->start_mc_offset = of_read_number(cell + 1, -+ mc_addr_cells - 1); -+ cell += mc_addr_cells; -+ range->start_phys_addr = of_read_number(cell, paddr_cells); -+ cell += paddr_cells; -+ range->end_mc_offset = range->start_mc_offset + -+ of_read_number(cell, mc_size_cells); -+ -+ cell += mc_size_cells; -+ } -+ -+ return 0; -+} -+ -+/** -+ * fsl_mc_bus_probe - callback invoked when the root MC bus is being -+ * added -+ */ -+static int fsl_mc_bus_probe(struct platform_device *pdev) -+{ -+ struct dprc_obj_desc obj_desc; -+ int error; -+ struct fsl_mc *mc; -+ struct fsl_mc_device *mc_bus_dev = NULL; -+ struct fsl_mc_io *mc_io = NULL; -+ int container_id; -+ phys_addr_t mc_portal_phys_addr; -+ uint32_t mc_portal_size; -+ struct mc_version mc_version; -+ struct resource res; -+ -+ dev_info(&pdev->dev, "Root MC bus device probed"); -+ -+ mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL); -+ if (!mc) -+ return -ENOMEM; -+ -+ platform_set_drvdata(pdev, mc); -+ error = create_mc_irq_domain(pdev, &mc->irq_domain); -+ if (error < 0) { -+ dev_warn(&pdev->dev, -+ "WARNING: MC bus driver will run without interrupt support\n"); -+ } else { -+ mc->gic_supported = true; -+ } -+ -+ /* -+ * Get physical address of MC portal for the root DPRC: -+ */ -+ error = of_address_to_resource(pdev->dev.of_node, 0, &res); -+ if (error < 0) { -+ dev_err(&pdev->dev, -+ "of_address_to_resource() failed for %s\n", -+ pdev->dev.of_node->full_name); -+ goto error_cleanup_irq_domain; -+ } -+ -+ mc_portal_phys_addr = res.start; -+ mc_portal_size = resource_size(&res); -+ error = fsl_create_mc_io(&pdev->dev, mc_portal_phys_addr, -+ mc_portal_size, NULL, 0, &mc_io); -+ if (error < 0) -+ goto error_cleanup_irq_domain; -+ -+ error = mc_get_version(mc_io, 0, &mc_version); -+ if (error != 0) { -+ dev_err(&pdev->dev, -+ "mc_get_version() failed with error %d\n", error); -+ goto error_cleanup_mc_io; -+ } -+ -+ dev_info(&pdev->dev, -+ "Freescale Management Complex Firmware version: %u.%u.%u\n", -+ mc_version.major, mc_version.minor, mc_version.revision); -+ -+ error = get_mc_addr_translation_ranges(&pdev->dev, -+ &mc->translation_ranges, -+ &mc->num_translation_ranges); -+ if (error < 0) -+ goto error_cleanup_mc_io; -+ -+ error = dpmng_get_container_id(mc_io, 0, &container_id); -+ if (error < 0) { -+ dev_err(&pdev->dev, -+ "dpmng_get_container_id() failed: %d\n", error); -+ goto error_cleanup_mc_io; -+ } -+ -+ memset(&obj_desc, 0, sizeof(struct dprc_obj_desc)); -+ error = get_dprc_version(mc_io, container_id, -+ &obj_desc.ver_major, &obj_desc.ver_minor); -+ if (error < 0) -+ goto error_cleanup_mc_io; -+ -+ obj_desc.vendor = FSL_MC_VENDOR_FREESCALE; -+ strcpy(obj_desc.type, "dprc"); -+ obj_desc.id = container_id; -+ obj_desc.irq_count = 1; -+ obj_desc.region_count = 0; -+ -+ error = fsl_mc_device_add(&obj_desc, mc_io, &pdev->dev, NULL, -+ &mc_bus_dev); -+ if (error < 0) -+ goto error_cleanup_mc_io; -+ -+ mc->root_mc_bus_dev = mc_bus_dev; -+ return 0; -+ -+error_cleanup_mc_io: -+ fsl_destroy_mc_io(mc_io); -+ -+error_cleanup_irq_domain: -+ if (mc->gic_supported) -+ irq_domain_remove(mc->irq_domain); -+ -+ return error; -+} -+ -+/** -+ * fsl_mc_bus_remove - callback invoked when the root MC bus is being -+ * removed -+ */ -+static int fsl_mc_bus_remove(struct platform_device *pdev) -+{ -+ struct fsl_mc *mc = platform_get_drvdata(pdev); -+ -+ if (WARN_ON(&mc->root_mc_bus_dev->dev != fsl_mc_bus_type.dev_root)) -+ return -EINVAL; -+ -+ if (mc->gic_supported) -+ irq_domain_remove(mc->irq_domain); -+ -+ fsl_mc_device_remove(mc->root_mc_bus_dev); -+ fsl_destroy_mc_io(mc->root_mc_bus_dev->mc_io); -+ mc->root_mc_bus_dev->mc_io = NULL; -+ -+ dev_info(&pdev->dev, "Root MC bus device removed"); -+ return 0; -+} -+ -+static const struct of_device_id fsl_mc_bus_match_table[] = { -+ {.compatible = "fsl,qoriq-mc",}, -+ {}, -+}; -+ -+MODULE_DEVICE_TABLE(of, fsl_mc_bus_match_table); -+ -+static struct platform_driver fsl_mc_bus_driver = { -+ .driver = { -+ .name = "fsl_mc_bus", -+ .owner = THIS_MODULE, -+ .pm = NULL, -+ .of_match_table = fsl_mc_bus_match_table, -+ }, -+ .probe = fsl_mc_bus_probe, -+ .remove = fsl_mc_bus_remove, -+}; -+ -+static int __init fsl_mc_bus_driver_init(void) -+{ -+ int error; -+ -+ mc_dev_cache = kmem_cache_create("fsl_mc_device", -+ sizeof(struct fsl_mc_device), 0, 0, -+ NULL); -+ if (!mc_dev_cache) { -+ pr_err("Could not create fsl_mc_device cache\n"); -+ return -ENOMEM; -+ } -+ -+ error = bus_register(&fsl_mc_bus_type); -+ if (error < 0) { -+ pr_err("fsl-mc bus type registration failed: %d\n", error); -+ goto error_cleanup_cache; -+ } -+ -+ pr_info("fsl-mc bus type registered\n"); -+ -+ error = platform_driver_register(&fsl_mc_bus_driver); -+ if (error < 0) { -+ pr_err("platform_driver_register() failed: %d\n", error); -+ goto error_cleanup_bus; -+ } -+ -+ error = dprc_driver_init(); -+ if (error < 0) -+ goto error_cleanup_driver; -+ -+ error = fsl_mc_allocator_driver_init(); -+ if (error < 0) -+ goto error_cleanup_dprc_driver; -+ -+ return 0; -+ -+error_cleanup_dprc_driver: -+ dprc_driver_exit(); -+ -+error_cleanup_driver: -+ platform_driver_unregister(&fsl_mc_bus_driver); -+ -+error_cleanup_bus: -+ bus_unregister(&fsl_mc_bus_type); -+ -+error_cleanup_cache: -+ kmem_cache_destroy(mc_dev_cache); -+ return error; -+} -+ -+postcore_initcall(fsl_mc_bus_driver_init); -+ -+static void __exit fsl_mc_bus_driver_exit(void) -+{ -+ if (WARN_ON(!mc_dev_cache)) -+ return; -+ -+ fsl_mc_allocator_driver_exit(); -+ dprc_driver_exit(); -+ platform_driver_unregister(&fsl_mc_bus_driver); -+ bus_unregister(&fsl_mc_bus_type); -+ kmem_cache_destroy(mc_dev_cache); -+ pr_info("MC bus unregistered\n"); -+} -+ -+module_exit(fsl_mc_bus_driver_exit); -+ -+MODULE_AUTHOR("Freescale Semiconductor Inc."); -+MODULE_DESCRIPTION("Freescale Management Complex (MC) bus driver"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/staging/fsl-mc/bus/mc-ioctl.h b/drivers/staging/fsl-mc/bus/mc-ioctl.h -new file mode 100644 -index 0000000..d5c1bc3 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/mc-ioctl.h -@@ -0,0 +1,25 @@ -+/* -+ * Freescale Management Complex (MC) ioclt interface -+ * -+ * Copyright (C) 2014 Freescale Semiconductor, Inc. -+ * Author: German Rivera -+ * Lijun Pan -+ * -+ * This file is licensed under the terms of the GNU General Public -+ * License version 2. This program is licensed "as is" without any -+ * warranty of any kind, whether express or implied. -+ */ -+#ifndef _FSL_MC_IOCTL_H_ -+#define _FSL_MC_IOCTL_H_ -+ -+#include -+ -+#define RESTOOL_IOCTL_TYPE 'R' -+ -+#define RESTOOL_GET_ROOT_DPRC_INFO \ -+ _IOR(RESTOOL_IOCTL_TYPE, 0x1, uint32_t) -+ -+#define RESTOOL_SEND_MC_COMMAND \ -+ _IOWR(RESTOOL_IOCTL_TYPE, 0x4, struct mc_command) -+ -+#endif /* _FSL_MC_IOCTL_H_ */ -diff --git a/drivers/staging/fsl-mc/bus/mc-restool.c b/drivers/staging/fsl-mc/bus/mc-restool.c -new file mode 100644 -index 0000000..d261c1a ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/mc-restool.c -@@ -0,0 +1,312 @@ -+/* -+ * Freescale Management Complex (MC) restool driver -+ * -+ * Copyright (C) 2014 Freescale Semiconductor, Inc. -+ * Author: German Rivera -+ * Lijun Pan -+ * This file is licensed under the terms of the GNU General Public -+ * License version 2. This program is licensed "as is" without any -+ * warranty of any kind, whether express or implied. -+ */ -+ -+#include "../include/mc-private.h" -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "mc-ioctl.h" -+#include "../include/mc-sys.h" -+#include "../include/mc-cmd.h" -+#include "../include/dpmng.h" -+ -+/** -+ * Maximum number of DPRCs that can be opened at the same time -+ */ -+#define MAX_DPRC_HANDLES 64 -+ -+/** -+ * struct fsl_mc_restool - Management Complex (MC) resource manager object -+ * @tool_mc_io: pointer to the MC I/O object used by the restool -+ */ -+struct fsl_mc_restool { -+ struct fsl_mc_io *tool_mc_io; -+}; -+ -+/** -+ * struct global_state - indicating the number of static and dynamic instance -+ * @dynamic_instance_count - number of dynamically created instances -+ * @static_instance_in_use - static instance is in use or not -+ * @mutex - mutex lock to serialze the operations -+ */ -+struct global_state { -+ uint32_t dynamic_instance_count; -+ bool static_instance_in_use; -+ struct mutex mutex; -+}; -+ -+static struct fsl_mc_restool fsl_mc_restool = { 0 }; -+static struct global_state global_state = { 0 }; -+ -+static int fsl_mc_restool_dev_open(struct inode *inode, struct file *filep) -+{ -+ struct fsl_mc_device *root_mc_dev; -+ int error = 0; -+ struct fsl_mc_restool *fsl_mc_restool_new = NULL; -+ -+ mutex_lock(&global_state.mutex); -+ -+ if (WARN_ON(fsl_mc_bus_type.dev_root == NULL)) { -+ error = -EINVAL; -+ goto error; -+ } -+ -+ if (!global_state.static_instance_in_use) { -+ global_state.static_instance_in_use = true; -+ filep->private_data = &fsl_mc_restool; -+ } else { -+ fsl_mc_restool_new = kmalloc(sizeof(struct fsl_mc_restool), -+ GFP_KERNEL); -+ if (fsl_mc_restool_new == NULL) { -+ error = -ENOMEM; -+ goto error; -+ } -+ memset(fsl_mc_restool_new, 0, sizeof(*fsl_mc_restool_new)); -+ -+ root_mc_dev = to_fsl_mc_device(fsl_mc_bus_type.dev_root); -+ error = fsl_mc_portal_allocate(root_mc_dev, 0, -+ &fsl_mc_restool_new->tool_mc_io); -+ if (error < 0) { -+ pr_err("Not able to allocate MC portal\n"); -+ goto error; -+ } -+ ++global_state.dynamic_instance_count; -+ filep->private_data = fsl_mc_restool_new; -+ } -+ -+ mutex_unlock(&global_state.mutex); -+ return 0; -+error: -+ if (fsl_mc_restool_new != NULL && -+ fsl_mc_restool_new->tool_mc_io != NULL) { -+ fsl_mc_portal_free(fsl_mc_restool_new->tool_mc_io); -+ fsl_mc_restool_new->tool_mc_io = NULL; -+ } -+ -+ kfree(fsl_mc_restool_new); -+ mutex_unlock(&global_state.mutex); -+ return error; -+} -+ -+static int fsl_mc_restool_dev_release(struct inode *inode, struct file *filep) -+{ -+ struct fsl_mc_restool *fsl_mc_restool_local = filep->private_data; -+ -+ if (WARN_ON(filep->private_data == NULL)) -+ return -EINVAL; -+ -+ mutex_lock(&global_state.mutex); -+ -+ if (WARN_ON(global_state.dynamic_instance_count == 0 && -+ !global_state.static_instance_in_use)) { -+ mutex_unlock(&global_state.mutex); -+ return -EINVAL; -+ } -+ -+ /* Globally clean up opened/untracked handles */ -+ fsl_mc_portal_reset(fsl_mc_restool_local->tool_mc_io); -+ -+ pr_debug("dynamic instance count: %d\n", -+ global_state.dynamic_instance_count); -+ pr_debug("static instance count: %d\n", -+ global_state.static_instance_in_use); -+ -+ /* -+ * must check -+ * whether fsl_mc_restool_local is dynamic or global instance -+ * Otherwise it will free up the reserved portal by accident -+ * or even not free up the dynamic allocated portal -+ * if 2 or more instances running concurrently -+ */ -+ if (fsl_mc_restool_local == &fsl_mc_restool) { -+ pr_debug("this is reserved portal"); -+ pr_debug("reserved portal not in use\n"); -+ global_state.static_instance_in_use = false; -+ } else { -+ pr_debug("this is dynamically allocated portal"); -+ pr_debug("free one dynamically allocated portal\n"); -+ fsl_mc_portal_free(fsl_mc_restool_local->tool_mc_io); -+ kfree(filep->private_data); -+ --global_state.dynamic_instance_count; -+ } -+ -+ filep->private_data = NULL; -+ mutex_unlock(&global_state.mutex); -+ return 0; -+} -+ -+static int restool_get_root_dprc_info(unsigned long arg) -+{ -+ int error = -EINVAL; -+ uint32_t root_dprc_id; -+ struct fsl_mc_device *root_mc_dev; -+ -+ root_mc_dev = to_fsl_mc_device(fsl_mc_bus_type.dev_root); -+ root_dprc_id = root_mc_dev->obj_desc.id; -+ error = copy_to_user((void __user *)arg, &root_dprc_id, -+ sizeof(root_dprc_id)); -+ if (error < 0) { -+ pr_err("copy_to_user() failed with error %d\n", error); -+ goto error; -+ } -+ -+ return 0; -+error: -+ return error; -+} -+ -+static int restool_send_mc_command(unsigned long arg, -+ struct fsl_mc_restool *fsl_mc_restool) -+{ -+ int error = -EINVAL; -+ struct mc_command mc_cmd; -+ -+ error = copy_from_user(&mc_cmd, (void __user *)arg, sizeof(mc_cmd)); -+ if (error < 0) { -+ pr_err("copy_to_user() failed with error %d\n", error); -+ goto error; -+ } -+ -+ /* -+ * Send MC command to the MC: -+ */ -+ error = mc_send_command(fsl_mc_restool->tool_mc_io, &mc_cmd); -+ if (error < 0) -+ goto error; -+ -+ error = copy_to_user((void __user *)arg, &mc_cmd, sizeof(mc_cmd)); -+ if (error < 0) { -+ pr_err("copy_to_user() failed with error %d\n", error); -+ goto error; -+ } -+ -+ return 0; -+error: -+ return error; -+} -+ -+static long -+fsl_mc_restool_dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -+{ -+ int error = -EINVAL; -+ -+ if (WARN_ON(fsl_mc_bus_type.dev_root == NULL)) -+ goto out; -+ -+ switch (cmd) { -+ case RESTOOL_GET_ROOT_DPRC_INFO: -+ error = restool_get_root_dprc_info(arg); -+ break; -+ -+ case RESTOOL_SEND_MC_COMMAND: -+ error = restool_send_mc_command(arg, file->private_data); -+ break; -+ default: -+ error = -EINVAL; -+ } -+out: -+ return error; -+} -+ -+static const struct file_operations fsl_mc_restool_dev_fops = { -+ .owner = THIS_MODULE, -+ .open = fsl_mc_restool_dev_open, -+ .release = fsl_mc_restool_dev_release, -+ .unlocked_ioctl = fsl_mc_restool_dev_ioctl, -+ .compat_ioctl = fsl_mc_restool_dev_ioctl, -+}; -+ -+static struct miscdevice fsl_mc_restool_dev = { -+ .minor = MISC_DYNAMIC_MINOR, -+ .name = "mc_restool", -+ .fops = &fsl_mc_restool_dev_fops -+}; -+ -+static int __init fsl_mc_restool_driver_init(void) -+{ -+ struct fsl_mc_device *root_mc_dev; -+ int error = -EINVAL; -+ bool restool_dev_registered = false; -+ -+ mutex_init(&global_state.mutex); -+ -+ if (WARN_ON(fsl_mc_restool.tool_mc_io != NULL)) -+ goto error; -+ -+ if (WARN_ON(global_state.dynamic_instance_count != 0)) -+ goto error; -+ -+ if (WARN_ON(global_state.static_instance_in_use)) -+ goto error; -+ -+ if (fsl_mc_bus_type.dev_root == NULL) { -+ pr_err("fsl-mc bus not found, restool driver registration failed\n"); -+ goto error; -+ } -+ -+ root_mc_dev = to_fsl_mc_device(fsl_mc_bus_type.dev_root); -+ error = fsl_mc_portal_allocate(root_mc_dev, 0, -+ &fsl_mc_restool.tool_mc_io); -+ if (error < 0) { -+ pr_err("Not able to allocate MC portal\n"); -+ goto error; -+ } -+ -+ error = misc_register(&fsl_mc_restool_dev); -+ if (error < 0) { -+ pr_err("misc_register() failed: %d\n", error); -+ goto error; -+ } -+ -+ restool_dev_registered = true; -+ pr_info("%s driver registered\n", fsl_mc_restool_dev.name); -+ return 0; -+error: -+ if (restool_dev_registered) -+ misc_deregister(&fsl_mc_restool_dev); -+ -+ if (fsl_mc_restool.tool_mc_io != NULL) { -+ fsl_mc_portal_free(fsl_mc_restool.tool_mc_io); -+ fsl_mc_restool.tool_mc_io = NULL; -+ } -+ -+ return error; -+} -+ -+module_init(fsl_mc_restool_driver_init); -+ -+static void __exit fsl_mc_restool_driver_exit(void) -+{ -+ if (WARN_ON(fsl_mc_restool.tool_mc_io == NULL)) -+ return; -+ -+ if (WARN_ON(global_state.dynamic_instance_count != 0)) -+ return; -+ -+ if (WARN_ON(global_state.static_instance_in_use)) -+ return; -+ -+ misc_deregister(&fsl_mc_restool_dev); -+ fsl_mc_portal_free(fsl_mc_restool.tool_mc_io); -+ fsl_mc_restool.tool_mc_io = NULL; -+ pr_info("%s driver unregistered\n", fsl_mc_restool_dev.name); -+} -+ -+module_exit(fsl_mc_restool_driver_exit); -+ -+MODULE_AUTHOR("Freescale Semiconductor Inc."); -+MODULE_DESCRIPTION("Freescale's MC restool driver"); -+MODULE_LICENSE("GPL"); -+ -diff --git a/drivers/staging/fsl-mc/bus/mc-sys.c b/drivers/staging/fsl-mc/bus/mc-sys.c -new file mode 100644 -index 0000000..d3b6940 ---- /dev/null -+++ b/drivers/staging/fsl-mc/bus/mc-sys.c -@@ -0,0 +1,677 @@ -+/* Copyright 2013-2014 Freescale Semiconductor Inc. -+ * -+ * I/O services to send MC commands to the MC hardware -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+ -+#include "../include/mc-sys.h" -+#include "../include/mc-cmd.h" -+#include "../include/mc.h" -+#include -+#include -+#include -+#include -+#include -+#include "dpmcp.h" -+ -+/** -+ * Timeout in milliseconds to wait for the completion of an MC command -+ * 5000 ms is barely enough for dpsw/dpdmux creation -+ * TODO: if MC firmware could response faster, we should decrease this value -+ */ -+#define MC_CMD_COMPLETION_TIMEOUT_MS 5000 -+ -+/* -+ * usleep_range() min and max values used to throttle down polling -+ * iterations while waiting for MC command completion -+ */ -+#define MC_CMD_COMPLETION_POLLING_MIN_SLEEP_USECS 10 -+#define MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS 500 -+ -+#define MC_CMD_HDR_READ_CMDID(_hdr) \ -+ ((uint16_t)mc_dec((_hdr), MC_CMD_HDR_CMDID_O, MC_CMD_HDR_CMDID_S)) -+ -+/** -+ * dpmcp_irq0_handler - Regular ISR for DPMCP interrupt 0 -+ * -+ * @irq: IRQ number of the interrupt being handled -+ * @arg: Pointer to device structure -+ */ -+static irqreturn_t dpmcp_irq0_handler(int irq_num, void *arg) -+{ -+ struct device *dev = (struct device *)arg; -+ struct fsl_mc_device *dpmcp_dev = to_fsl_mc_device(dev); -+ struct fsl_mc_io *mc_io = dpmcp_dev->mc_io; -+ -+ dev_dbg(dev, "DPMCP IRQ %d triggered on CPU %u\n", irq_num, -+ smp_processor_id()); -+ -+ if (WARN_ON(dpmcp_dev->irqs[0]->irq_number != (uint32_t)irq_num)) -+ goto out; -+ -+ if (WARN_ON(!mc_io)) -+ goto out; -+ -+ complete(&mc_io->mc_command_done_completion); -+out: -+ return IRQ_HANDLED; -+} -+ -+/* -+ * Disable and clear interrupts for a given DPMCP object -+ */ -+static int disable_dpmcp_irq(struct fsl_mc_device *dpmcp_dev) -+{ -+ int error; -+ -+ /* -+ * Disable generation of the DPMCP interrupt: -+ */ -+ error = dpmcp_set_irq_enable(dpmcp_dev->mc_io, -+ MC_CMD_FLAG_INTR_DIS, -+ dpmcp_dev->mc_handle, -+ DPMCP_IRQ_INDEX, 0); -+ if (error < 0) { -+ dev_err(&dpmcp_dev->dev, -+ "dpmcp_set_irq_enable() failed: %d\n", error); -+ -+ return error; -+ } -+ -+ /* -+ * Disable all DPMCP interrupt causes: -+ */ -+ error = dpmcp_set_irq_mask(dpmcp_dev->mc_io, -+ MC_CMD_FLAG_INTR_DIS, -+ dpmcp_dev->mc_handle, -+ DPMCP_IRQ_INDEX, 0x0); -+ if (error < 0) { -+ dev_err(&dpmcp_dev->dev, -+ "dpmcp_set_irq_mask() failed: %d\n", error); -+ -+ return error; -+ } -+ -+ return 0; -+} -+ -+static void unregister_dpmcp_irq_handler(struct fsl_mc_device *dpmcp_dev) -+{ -+ struct fsl_mc_device_irq *irq = dpmcp_dev->irqs[DPMCP_IRQ_INDEX]; -+ -+ devm_free_irq(&dpmcp_dev->dev, irq->irq_number, &dpmcp_dev->dev); -+} -+ -+static int register_dpmcp_irq_handler(struct fsl_mc_device *dpmcp_dev) -+{ -+ int error; -+ struct fsl_mc_device_irq *irq = dpmcp_dev->irqs[DPMCP_IRQ_INDEX]; -+ -+ error = devm_request_irq(&dpmcp_dev->dev, -+ irq->irq_number, -+ dpmcp_irq0_handler, -+ IRQF_NO_SUSPEND | IRQF_ONESHOT, -+ "FSL MC DPMCP irq0", -+ &dpmcp_dev->dev); -+ if (error < 0) { -+ dev_err(&dpmcp_dev->dev, -+ "devm_request_irq() failed: %d\n", -+ error); -+ return error; -+ } -+ -+ return 0; -+} -+ -+static int enable_dpmcp_irq(struct fsl_mc_device *dpmcp_dev) -+{ -+ int error; -+ -+ /* -+ * Enable MC command completion event to trigger DPMCP interrupt: -+ */ -+ error = dpmcp_set_irq_mask(dpmcp_dev->mc_io, -+ MC_CMD_FLAG_INTR_DIS, -+ dpmcp_dev->mc_handle, -+ DPMCP_IRQ_INDEX, -+ DPMCP_IRQ_EVENT_CMD_DONE); -+ if (error < 0) { -+ dev_err(&dpmcp_dev->dev, -+ "dpmcp_set_irq_mask() failed: %d\n", error); -+ -+ return error; -+ } -+ -+ /* -+ * Enable generation of the interrupt: -+ */ -+ error = dpmcp_set_irq_enable(dpmcp_dev->mc_io, -+ MC_CMD_FLAG_INTR_DIS, -+ dpmcp_dev->mc_handle, -+ DPMCP_IRQ_INDEX, 1); -+ if (error < 0) { -+ dev_err(&dpmcp_dev->dev, -+ "dpmcp_set_irq_enable() failed: %d\n", error); -+ -+ return error; -+ } -+ -+ return 0; -+} -+ -+/* -+ * Setup MC command completion interrupt for the DPMCP device associated with a -+ * given fsl_mc_io object -+ */ -+int fsl_mc_io_setup_dpmcp_irq(struct fsl_mc_io *mc_io) -+{ -+ int error; -+ struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev; -+ -+ if (WARN_ON(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)) -+ return -EINVAL; -+ -+ if (WARN_ON(!dpmcp_dev)) -+ return -EINVAL; -+ -+ if (WARN_ON(!fsl_mc_interrupts_supported())) -+ return -EINVAL; -+ -+ if (WARN_ON(dpmcp_dev->obj_desc.irq_count != 1)) -+ return -EINVAL; -+ -+ if (WARN_ON(dpmcp_dev->mc_io != mc_io)) -+ return -EINVAL; -+ -+ error = fsl_mc_allocate_irqs(dpmcp_dev); -+ if (error < 0) -+ return error; -+ -+ error = disable_dpmcp_irq(dpmcp_dev); -+ if (error < 0) -+ goto error_free_irqs; -+ -+ error = register_dpmcp_irq_handler(dpmcp_dev); -+ if (error < 0) -+ goto error_free_irqs; -+ -+ error = enable_dpmcp_irq(dpmcp_dev); -+ if (error < 0) -+ goto error_unregister_irq_handler; -+ -+ mc_io->mc_command_done_irq_armed = true; -+ return 0; -+ -+error_unregister_irq_handler: -+ unregister_dpmcp_irq_handler(dpmcp_dev); -+ -+error_free_irqs: -+ fsl_mc_free_irqs(dpmcp_dev); -+ -+ return error; -+} -+EXPORT_SYMBOL_GPL(fsl_mc_io_setup_dpmcp_irq); -+ -+/* -+ * Tear down interrupts for the DPMCP device associated with a given fsl_mc_io -+ * object -+ */ -+static void teardown_dpmcp_irq(struct fsl_mc_io *mc_io) -+{ -+ struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev; -+ -+ if (WARN_ON(!dpmcp_dev)) -+ return; -+ if (WARN_ON(!fsl_mc_interrupts_supported())) -+ return; -+ if (WARN_ON(!dpmcp_dev->irqs)) -+ return; -+ -+ mc_io->mc_command_done_irq_armed = false; -+ (void)disable_dpmcp_irq(dpmcp_dev); -+ unregister_dpmcp_irq_handler(dpmcp_dev); -+ fsl_mc_free_irqs(dpmcp_dev); -+} -+ -+/** -+ * Creates an MC I/O object -+ * -+ * @dev: device to be associated with the MC I/O object -+ * @mc_portal_phys_addr: physical address of the MC portal to use -+ * @mc_portal_size: size in bytes of the MC portal -+ * @resource: Pointer to MC bus object allocator resource associated -+ * with this MC I/O object or NULL if none. -+ * @flags: flags for the new MC I/O object -+ * @new_mc_io: Area to return pointer to newly created MC I/O object -+ * -+ * Returns '0' on Success; Error code otherwise. -+ */ -+int __must_check fsl_create_mc_io(struct device *dev, -+ phys_addr_t mc_portal_phys_addr, -+ uint32_t mc_portal_size, -+ struct fsl_mc_device *dpmcp_dev, -+ uint32_t flags, struct fsl_mc_io **new_mc_io) -+{ -+ int error; -+ struct fsl_mc_io *mc_io; -+ void __iomem *mc_portal_virt_addr; -+ struct resource *res; -+ -+ mc_io = devm_kzalloc(dev, sizeof(*mc_io), GFP_KERNEL); -+ if (!mc_io) -+ return -ENOMEM; -+ -+ mc_io->dev = dev; -+ mc_io->flags = flags; -+ mc_io->portal_phys_addr = mc_portal_phys_addr; -+ mc_io->portal_size = mc_portal_size; -+ mc_io->mc_command_done_irq_armed = false; -+ if (flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL) { -+ spin_lock_init(&mc_io->spinlock); -+ } else { -+ mutex_init(&mc_io->mutex); -+ init_completion(&mc_io->mc_command_done_completion); -+ } -+ -+ res = devm_request_mem_region(dev, -+ mc_portal_phys_addr, -+ mc_portal_size, -+ "mc_portal"); -+ if (!res) { -+ dev_err(dev, -+ "devm_request_mem_region failed for MC portal %#llx\n", -+ mc_portal_phys_addr); -+ return -EBUSY; -+ } -+ -+ mc_portal_virt_addr = devm_ioremap_nocache(dev, -+ mc_portal_phys_addr, -+ mc_portal_size); -+ if (!mc_portal_virt_addr) { -+ dev_err(dev, -+ "devm_ioremap_nocache failed for MC portal %#llx\n", -+ mc_portal_phys_addr); -+ return -ENXIO; -+ } -+ -+ mc_io->portal_virt_addr = mc_portal_virt_addr; -+ if (dpmcp_dev) { -+ error = fsl_mc_io_set_dpmcp(mc_io, dpmcp_dev); -+ if (error < 0) -+ goto error_destroy_mc_io; -+ -+ if (!(flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL) && -+ fsl_mc_interrupts_supported()) { -+ error = fsl_mc_io_setup_dpmcp_irq(mc_io); -+ if (error < 0) -+ goto error_destroy_mc_io; -+ } -+ } -+ -+ *new_mc_io = mc_io; -+ return 0; -+ -+error_destroy_mc_io: -+ fsl_destroy_mc_io(mc_io); -+ return error; -+ -+} -+EXPORT_SYMBOL_GPL(fsl_create_mc_io); -+ -+/** -+ * Destroys an MC I/O object -+ * -+ * @mc_io: MC I/O object to destroy -+ */ -+void fsl_destroy_mc_io(struct fsl_mc_io *mc_io) -+{ -+ struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev; -+ -+ if (dpmcp_dev) -+ fsl_mc_io_unset_dpmcp(mc_io); -+ -+ devm_iounmap(mc_io->dev, mc_io->portal_virt_addr); -+ devm_release_mem_region(mc_io->dev, -+ mc_io->portal_phys_addr, -+ mc_io->portal_size); -+ -+ mc_io->portal_virt_addr = NULL; -+ devm_kfree(mc_io->dev, mc_io); -+} -+EXPORT_SYMBOL_GPL(fsl_destroy_mc_io); -+ -+int fsl_mc_io_set_dpmcp(struct fsl_mc_io *mc_io, -+ struct fsl_mc_device *dpmcp_dev) -+{ -+ int error; -+ -+ if (WARN_ON(!dpmcp_dev)) -+ return -EINVAL; -+ -+ if (WARN_ON(mc_io->dpmcp_dev)) -+ return -EINVAL; -+ -+ if (WARN_ON(dpmcp_dev->mc_io)) -+ return -EINVAL; -+ -+ if (!(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)) { -+ error = dpmcp_open(mc_io, -+ 0, -+ dpmcp_dev->obj_desc.id, -+ &dpmcp_dev->mc_handle); -+ if (error < 0) -+ return error; -+ } -+ -+ mc_io->dpmcp_dev = dpmcp_dev; -+ dpmcp_dev->mc_io = mc_io; -+ return 0; -+} -+EXPORT_SYMBOL_GPL(fsl_mc_io_set_dpmcp); -+ -+void fsl_mc_io_unset_dpmcp(struct fsl_mc_io *mc_io) -+{ -+ int error; -+ struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev; -+ -+ if (WARN_ON(!dpmcp_dev)) -+ return; -+ -+ if (WARN_ON(dpmcp_dev->mc_io != mc_io)) -+ return; -+ -+ if (!(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)) { -+ if (dpmcp_dev->irqs) -+ teardown_dpmcp_irq(mc_io); -+ -+ error = dpmcp_close(mc_io, -+ 0, -+ dpmcp_dev->mc_handle); -+ if (error < 0) { -+ dev_err(&dpmcp_dev->dev, "dpmcp_close() failed: %d\n", -+ error); -+ } -+ } -+ -+ mc_io->dpmcp_dev = NULL; -+ dpmcp_dev->mc_io = NULL; -+} -+EXPORT_SYMBOL_GPL(fsl_mc_io_unset_dpmcp); -+ -+static int mc_status_to_error(enum mc_cmd_status status) -+{ -+ static const int mc_status_to_error_map[] = { -+ [MC_CMD_STATUS_OK] = 0, -+ [MC_CMD_STATUS_AUTH_ERR] = -EACCES, -+ [MC_CMD_STATUS_NO_PRIVILEGE] = -EPERM, -+ [MC_CMD_STATUS_DMA_ERR] = -EIO, -+ [MC_CMD_STATUS_CONFIG_ERR] = -ENXIO, -+ [MC_CMD_STATUS_TIMEOUT] = -ETIMEDOUT, -+ [MC_CMD_STATUS_NO_RESOURCE] = -ENAVAIL, -+ [MC_CMD_STATUS_NO_MEMORY] = -ENOMEM, -+ [MC_CMD_STATUS_BUSY] = -EBUSY, -+ [MC_CMD_STATUS_UNSUPPORTED_OP] = -ENOTSUPP, -+ [MC_CMD_STATUS_INVALID_STATE] = -ENODEV, -+ }; -+ -+ if (WARN_ON((u32)status >= ARRAY_SIZE(mc_status_to_error_map))) -+ return -EINVAL; -+ -+ return mc_status_to_error_map[status]; -+} -+ -+static const char *mc_status_to_string(enum mc_cmd_status status) -+{ -+ static const char *const status_strings[] = { -+ [MC_CMD_STATUS_OK] = "Command completed successfully", -+ [MC_CMD_STATUS_READY] = "Command ready to be processed", -+ [MC_CMD_STATUS_AUTH_ERR] = "Authentication error", -+ [MC_CMD_STATUS_NO_PRIVILEGE] = "No privilege", -+ [MC_CMD_STATUS_DMA_ERR] = "DMA or I/O error", -+ [MC_CMD_STATUS_CONFIG_ERR] = "Configuration error", -+ [MC_CMD_STATUS_TIMEOUT] = "Operation timed out", -+ [MC_CMD_STATUS_NO_RESOURCE] = "No resources", -+ [MC_CMD_STATUS_NO_MEMORY] = "No memory available", -+ [MC_CMD_STATUS_BUSY] = "Device is busy", -+ [MC_CMD_STATUS_UNSUPPORTED_OP] = "Unsupported operation", -+ [MC_CMD_STATUS_INVALID_STATE] = "Invalid state" -+ }; -+ -+ if ((unsigned int)status >= ARRAY_SIZE(status_strings)) -+ return "Unknown MC error"; -+ -+ return status_strings[status]; -+} -+ -+/** -+ * mc_write_command - writes a command to a Management Complex (MC) portal -+ * -+ * @portal: pointer to an MC portal -+ * @cmd: pointer to a filled command -+ */ -+static inline void mc_write_command(struct mc_command __iomem *portal, -+ struct mc_command *cmd) -+{ -+ int i; -+ -+ /* copy command parameters into the portal */ -+ for (i = 0; i < MC_CMD_NUM_OF_PARAMS; i++) -+ writeq(cmd->params[i], &portal->params[i]); -+ -+ /* submit the command by writing the header */ -+ writeq(cmd->header, &portal->header); -+} -+ -+/** -+ * mc_read_response - reads the response for the last MC command from a -+ * Management Complex (MC) portal -+ * -+ * @portal: pointer to an MC portal -+ * @resp: pointer to command response buffer -+ * -+ * Returns MC_CMD_STATUS_OK on Success; Error code otherwise. -+ */ -+static inline enum mc_cmd_status mc_read_response(struct mc_command __iomem * -+ portal, -+ struct mc_command *resp) -+{ -+ int i; -+ enum mc_cmd_status status; -+ -+ /* Copy command response header from MC portal: */ -+ resp->header = readq(&portal->header); -+ status = MC_CMD_HDR_READ_STATUS(resp->header); -+ if (status != MC_CMD_STATUS_OK) -+ return status; -+ -+ /* Copy command response data from MC portal: */ -+ for (i = 0; i < MC_CMD_NUM_OF_PARAMS; i++) -+ resp->params[i] = readq(&portal->params[i]); -+ -+ return status; -+} -+ -+static int mc_completion_wait(struct fsl_mc_io *mc_io, struct mc_command *cmd, -+ enum mc_cmd_status *mc_status) -+{ -+ enum mc_cmd_status status; -+ unsigned long jiffies_left; -+ unsigned long timeout_jiffies = -+ msecs_to_jiffies(MC_CMD_COMPLETION_TIMEOUT_MS); -+ -+ if (WARN_ON(!mc_io->dpmcp_dev)) -+ return -EINVAL; -+ -+ if (WARN_ON(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)) -+ return -EINVAL; -+ -+ for (;;) { -+ status = mc_read_response(mc_io->portal_virt_addr, cmd); -+ if (status != MC_CMD_STATUS_READY) -+ break; -+ -+ jiffies_left = wait_for_completion_timeout( -+ &mc_io->mc_command_done_completion, -+ timeout_jiffies); -+ if (jiffies_left == 0) -+ return -ETIMEDOUT; -+ } -+ -+ *mc_status = status; -+ return 0; -+} -+ -+static int mc_polling_wait_preemptible(struct fsl_mc_io *mc_io, -+ struct mc_command *cmd, -+ enum mc_cmd_status *mc_status) -+{ -+ enum mc_cmd_status status; -+ unsigned long jiffies_until_timeout = -+ jiffies + msecs_to_jiffies(MC_CMD_COMPLETION_TIMEOUT_MS); -+ -+ for (;;) { -+ status = mc_read_response(mc_io->portal_virt_addr, cmd); -+ if (status != MC_CMD_STATUS_READY) -+ break; -+ -+ usleep_range(MC_CMD_COMPLETION_POLLING_MIN_SLEEP_USECS, -+ MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS); -+ -+ if (time_after_eq(jiffies, jiffies_until_timeout)) -+ return -ETIMEDOUT; -+ } -+ -+ *mc_status = status; -+ return 0; -+} -+ -+static int mc_polling_wait_atomic(struct fsl_mc_io *mc_io, -+ struct mc_command *cmd, -+ enum mc_cmd_status *mc_status) -+{ -+ enum mc_cmd_status status; -+ unsigned long timeout_usecs = MC_CMD_COMPLETION_TIMEOUT_MS * 1000; -+ -+ BUILD_BUG_ON((MC_CMD_COMPLETION_TIMEOUT_MS * 1000) % -+ MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS != 0); -+ -+ for (;;) { -+ status = mc_read_response(mc_io->portal_virt_addr, cmd); -+ if (status != MC_CMD_STATUS_READY) -+ break; -+ -+ udelay(MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS); -+ timeout_usecs -= MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS; -+ if (timeout_usecs == 0) -+ return -ETIMEDOUT; -+ } -+ -+ *mc_status = status; -+ return 0; -+} -+ -+/** -+ * Sends a command to the MC device using the given MC I/O object -+ * -+ * @mc_io: MC I/O object to be used -+ * @cmd: command to be sent -+ * -+ * Returns '0' on Success; Error code otherwise. -+ */ -+int mc_send_command(struct fsl_mc_io *mc_io, struct mc_command *cmd) -+{ -+ int error; -+ enum mc_cmd_status status; -+ unsigned long irq_flags = 0; -+ bool dpmcp_completion_intr_disabled = -+ (MC_CMD_HDR_READ_FLAGS(cmd->header) & MC_CMD_FLAG_INTR_DIS); -+ -+ if (WARN_ON(in_irq() && -+ (!dpmcp_completion_intr_disabled || -+ !(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)))) -+ return -EINVAL; -+ -+ if (mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL) -+ spin_lock_irqsave(&mc_io->spinlock, irq_flags); -+ else -+ mutex_lock(&mc_io->mutex); -+ -+ /* -+ * Send command to the MC hardware: -+ */ -+ mc_write_command(mc_io->portal_virt_addr, cmd); -+ -+ /* -+ * Wait for response from the MC hardware: -+ */ -+ if (mc_io->mc_command_done_irq_armed && !dpmcp_completion_intr_disabled) -+ error = mc_completion_wait(mc_io, cmd, &status); -+ else if (!(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)) -+ error = mc_polling_wait_preemptible(mc_io, cmd, &status); -+ else -+ error = mc_polling_wait_atomic(mc_io, cmd, &status); -+ -+ if (error < 0) { -+ if (error == -ETIMEDOUT) { -+ pr_debug("MC command timed out (portal: %#llx, obj handle: %#x, command: %#x)\n", -+ mc_io->portal_phys_addr, -+ (unsigned int) -+ MC_CMD_HDR_READ_TOKEN(cmd->header), -+ (unsigned int) -+ MC_CMD_HDR_READ_CMDID(cmd->header)); -+ } -+ goto common_exit; -+ -+ } -+ -+ if (status != MC_CMD_STATUS_OK) { -+ pr_debug("MC command failed: portal: %#llx, obj handle: %#x, command: %#x, status: %s (%#x)\n", -+ mc_io->portal_phys_addr, -+ (unsigned int)MC_CMD_HDR_READ_TOKEN(cmd->header), -+ (unsigned int)MC_CMD_HDR_READ_CMDID(cmd->header), -+ mc_status_to_string(status), -+ (unsigned int)status); -+ -+ error = mc_status_to_error(status); -+ goto common_exit; -+ } -+ -+ error = 0; -+ -+common_exit: -+ if (mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL) -+ spin_unlock_irqrestore(&mc_io->spinlock, irq_flags); -+ else -+ mutex_unlock(&mc_io->mutex); -+ -+ return error; -+} -+EXPORT_SYMBOL(mc_send_command); -diff --git a/drivers/staging/fsl-mc/include/dpbp-cmd.h b/drivers/staging/fsl-mc/include/dpbp-cmd.h -new file mode 100644 -index 0000000..1ec04e4 ---- /dev/null -+++ b/drivers/staging/fsl-mc/include/dpbp-cmd.h -@@ -0,0 +1,62 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef _FSL_DPBP_CMD_H -+#define _FSL_DPBP_CMD_H -+ -+/* DPBP Version */ -+#define DPBP_VER_MAJOR 2 -+#define DPBP_VER_MINOR 2 -+ -+/* Command IDs */ -+#define DPBP_CMDID_CLOSE 0x800 -+#define DPBP_CMDID_OPEN 0x804 -+#define DPBP_CMDID_CREATE 0x904 -+#define DPBP_CMDID_DESTROY 0x900 -+ -+#define DPBP_CMDID_ENABLE 0x002 -+#define DPBP_CMDID_DISABLE 0x003 -+#define DPBP_CMDID_GET_ATTR 0x004 -+#define DPBP_CMDID_RESET 0x005 -+#define DPBP_CMDID_IS_ENABLED 0x006 -+ -+#define DPBP_CMDID_SET_IRQ 0x010 -+#define DPBP_CMDID_GET_IRQ 0x011 -+#define DPBP_CMDID_SET_IRQ_ENABLE 0x012 -+#define DPBP_CMDID_GET_IRQ_ENABLE 0x013 -+#define DPBP_CMDID_SET_IRQ_MASK 0x014 -+#define DPBP_CMDID_GET_IRQ_MASK 0x015 -+#define DPBP_CMDID_GET_IRQ_STATUS 0x016 -+#define DPBP_CMDID_CLEAR_IRQ_STATUS 0x017 -+ -+#define DPBP_CMDID_SET_NOTIFICATIONS 0x01b0 -+#define DPBP_CMDID_GET_NOTIFICATIONS 0x01b1 -+#endif /* _FSL_DPBP_CMD_H */ -diff --git a/drivers/staging/fsl-mc/include/dpbp.h b/drivers/staging/fsl-mc/include/dpbp.h -new file mode 100644 -index 0000000..9856bb8 ---- /dev/null -+++ b/drivers/staging/fsl-mc/include/dpbp.h -@@ -0,0 +1,438 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef __FSL_DPBP_H -+#define __FSL_DPBP_H -+ -+/* Data Path Buffer Pool API -+ * Contains initialization APIs and runtime control APIs for DPBP -+ */ -+ -+struct fsl_mc_io; -+ -+/** -+ * dpbp_open() - Open a control session for the specified object. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @dpbp_id: DPBP unique ID -+ * @token: Returned token; use in subsequent API calls -+ * -+ * This function can be used to open a control session for an -+ * already created object; an object may have been declared in -+ * the DPL or by calling the dpbp_create function. -+ * This function returns a unique authentication token, -+ * associated with the specific object ID and the specific MC -+ * portal; this token must be used in all subsequent commands for -+ * this specific object -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_open(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ int dpbp_id, -+ uint16_t *token); -+ -+/** -+ * dpbp_close() - Close the control session of the object -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPBP object -+ * -+ * After this function is called, no further operations are -+ * allowed on the object without opening a new control session. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_close(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * struct dpbp_cfg - Structure representing DPBP configuration -+ * @options: place holder -+ */ -+struct dpbp_cfg { -+ uint32_t options; -+}; -+ -+/** -+ * dpbp_create() - Create the DPBP object. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @cfg: Configuration structure -+ * @token: Returned token; use in subsequent API calls -+ * -+ * Create the DPBP object, allocate required resources and -+ * perform required initialization. -+ * -+ * The object can be created either by declaring it in the -+ * DPL file, or by calling this function. -+ * This function returns a unique authentication token, -+ * associated with the specific object ID and the specific MC -+ * portal; this token must be used in all subsequent calls to -+ * this specific object. For objects that are created using the -+ * DPL file, call dpbp_open function to get an authentication -+ * token first. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_create(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ const struct dpbp_cfg *cfg, -+ uint16_t *token); -+ -+/** -+ * dpbp_destroy() - Destroy the DPBP object and release all its resources. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPBP object -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpbp_destroy(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * dpbp_enable() - Enable the DPBP. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPBP object -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * dpbp_disable() - Disable the DPBP. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPBP object -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_disable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * dpbp_is_enabled() - Check if the DPBP is enabled. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPBP object -+ * @en: Returns '1' if object is enabled; '0' otherwise -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_is_enabled(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *en); -+ -+/** -+ * dpbp_reset() - Reset the DPBP, returns the object to initial state. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPBP object -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_reset(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * struct dpbp_irq_cfg - IRQ configuration -+ * @addr: Address that must be written to signal a message-based interrupt -+ * @val: Value to write into irq_addr address -+ * @irq_num: A user defined number associated with this IRQ -+ */ -+struct dpbp_irq_cfg { -+ uint64_t addr; -+ uint32_t val; -+ int irq_num; -+}; -+ -+/** -+ * dpbp_set_irq() - Set IRQ information for the DPBP to trigger an interrupt. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPBP object -+ * @irq_index: Identifies the interrupt index to configure -+ * @irq_cfg: IRQ configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_set_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ struct dpbp_irq_cfg *irq_cfg); -+ -+/** -+ * dpbp_get_irq() - Get IRQ information from the DPBP. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPBP object -+ * @irq_index: The interrupt index to configure -+ * @type: Interrupt type: 0 represents message interrupt -+ * type (both irq_addr and irq_val are valid) -+ * @irq_cfg: IRQ attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_get_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ int *type, -+ struct dpbp_irq_cfg *irq_cfg); -+ -+/** -+ * dpbp_set_irq_enable() - Set overall interrupt state. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPBP object -+ * @irq_index: The interrupt index to configure -+ * @en: Interrupt state - enable = 1, disable = 0 -+ * -+ * Allows GPP software to control when interrupts are generated. -+ * Each interrupt can have up to 32 causes. The enable/disable control's the -+ * overall interrupt state. if the interrupt is disabled no causes will cause -+ * an interrupt. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_set_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t en); -+ -+/** -+ * dpbp_get_irq_enable() - Get overall interrupt state -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPBP object -+ * @irq_index: The interrupt index to configure -+ * @en: Returned interrupt state - enable = 1, disable = 0 -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_get_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t *en); -+ -+/** -+ * dpbp_set_irq_mask() - Set interrupt mask. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPBP object -+ * @irq_index: The interrupt index to configure -+ * @mask: Event mask to trigger interrupt; -+ * each bit: -+ * 0 = ignore event -+ * 1 = consider event for asserting IRQ -+ * -+ * Every interrupt can have up to 32 causes and the interrupt model supports -+ * masking/unmasking each cause independently -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_set_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t mask); -+ -+/** -+ * dpbp_get_irq_mask() - Get interrupt mask. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPBP object -+ * @irq_index: The interrupt index to configure -+ * @mask: Returned event mask to trigger interrupt -+ * -+ * Every interrupt can have up to 32 causes and the interrupt model supports -+ * masking/unmasking each cause independently -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_get_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *mask); -+ -+/** -+ * dpbp_get_irq_status() - Get the current status of any pending interrupts. -+ * -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPBP object -+ * @irq_index: The interrupt index to configure -+ * @status: Returned interrupts status - one bit per cause: -+ * 0 = no interrupt pending -+ * 1 = interrupt pending -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_get_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *status); -+ -+/** -+ * dpbp_clear_irq_status() - Clear a pending interrupt's status -+ * -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPBP object -+ * @irq_index: The interrupt index to configure -+ * @status: Bits to clear (W1C) - one bit per cause: -+ * 0 = don't change -+ * 1 = clear status bit -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_clear_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t status); -+ -+/** -+ * struct dpbp_attr - Structure representing DPBP attributes -+ * @id: DPBP object ID -+ * @version: DPBP version -+ * @bpid: Hardware buffer pool ID; should be used as an argument in -+ * acquire/release operations on buffers -+ */ -+struct dpbp_attr { -+ int id; -+ /** -+ * struct version - Structure representing DPBP version -+ * @major: DPBP major version -+ * @minor: DPBP minor version -+ */ -+ struct { -+ uint16_t major; -+ uint16_t minor; -+ } version; -+ uint16_t bpid; -+}; -+ -+/** -+ * dpbp_get_attributes - Retrieve DPBP attributes. -+ * -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPBP object -+ * @attr: Returned object's attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_get_attributes(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpbp_attr *attr); -+ -+/** -+ * DPBP notifications options -+ */ -+ -+/** -+ * BPSCN write will attempt to allocate into a cache (coherent write) -+ */ -+#define DPBP_NOTIF_OPT_COHERENT_WRITE 0x00000001 -+ -+/** -+ * struct dpbp_notification_cfg - Structure representing DPBP notifications -+ * towards software -+ * @depletion_entry: below this threshold the pool is "depleted"; -+ * set it to '0' to disable it -+ * @depletion_exit: greater than or equal to this threshold the pool exit its -+ * "depleted" state -+ * @surplus_entry: above this threshold the pool is in "surplus" state; -+ * set it to '0' to disable it -+ * @surplus_exit: less than or equal to this threshold the pool exit its -+ * "surplus" state -+ * @message_iova: MUST be given if either 'depletion_entry' or 'surplus_entry' -+ * is not '0' (enable); I/O virtual address (must be in DMA-able memory), -+ * must be 16B aligned. -+ * @message_ctx: The context that will be part of the BPSCN message and will -+ * be written to 'message_iova' -+ * @options: Mask of available options; use 'DPBP_NOTIF_OPT_' values -+ */ -+struct dpbp_notification_cfg { -+ uint32_t depletion_entry; -+ uint32_t depletion_exit; -+ uint32_t surplus_entry; -+ uint32_t surplus_exit; -+ uint64_t message_iova; -+ uint64_t message_ctx; -+ uint16_t options; -+}; -+ -+/** -+ * dpbp_set_notifications() - Set notifications towards software -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPBP object -+ * @cfg: notifications configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_set_notifications(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpbp_notification_cfg *cfg); -+ -+/** -+ * dpbp_get_notifications() - Get the notifications configuration -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPBP object -+ * @cfg: notifications configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpbp_get_notifications(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpbp_notification_cfg *cfg); -+ -+#endif /* __FSL_DPBP_H */ -diff --git a/drivers/staging/fsl-mc/include/dpcon-cmd.h b/drivers/staging/fsl-mc/include/dpcon-cmd.h -new file mode 100644 -index 0000000..ecb40d0 ---- /dev/null -+++ b/drivers/staging/fsl-mc/include/dpcon-cmd.h -@@ -0,0 +1,162 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef _FSL_DPCON_CMD_H -+#define _FSL_DPCON_CMD_H -+ -+/* DPCON Version */ -+#define DPCON_VER_MAJOR 2 -+#define DPCON_VER_MINOR 2 -+ -+/* Command IDs */ -+#define DPCON_CMDID_CLOSE 0x800 -+#define DPCON_CMDID_OPEN 0x808 -+#define DPCON_CMDID_CREATE 0x908 -+#define DPCON_CMDID_DESTROY 0x900 -+ -+#define DPCON_CMDID_ENABLE 0x002 -+#define DPCON_CMDID_DISABLE 0x003 -+#define DPCON_CMDID_GET_ATTR 0x004 -+#define DPCON_CMDID_RESET 0x005 -+#define DPCON_CMDID_IS_ENABLED 0x006 -+ -+#define DPCON_CMDID_SET_IRQ 0x010 -+#define DPCON_CMDID_GET_IRQ 0x011 -+#define DPCON_CMDID_SET_IRQ_ENABLE 0x012 -+#define DPCON_CMDID_GET_IRQ_ENABLE 0x013 -+#define DPCON_CMDID_SET_IRQ_MASK 0x014 -+#define DPCON_CMDID_GET_IRQ_MASK 0x015 -+#define DPCON_CMDID_GET_IRQ_STATUS 0x016 -+#define DPCON_CMDID_CLEAR_IRQ_STATUS 0x017 -+ -+#define DPCON_CMDID_SET_NOTIFICATION 0x100 -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPCON_CMD_OPEN(cmd, dpcon_id) \ -+ MC_CMD_OP(cmd, 0, 0, 32, int, dpcon_id) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPCON_CMD_CREATE(cmd, cfg) \ -+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, cfg->num_priorities) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPCON_RSP_IS_ENABLED(cmd, en) \ -+ MC_RSP_OP(cmd, 0, 0, 1, int, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPCON_CMD_SET_IRQ(cmd, irq_index, irq_cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, irq_index);\ -+ MC_CMD_OP(cmd, 0, 32, 32, uint32_t, irq_cfg->val);\ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, irq_cfg->addr);\ -+ MC_CMD_OP(cmd, 2, 0, 32, int, irq_cfg->irq_num); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPCON_CMD_GET_IRQ(cmd, irq_index) \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPCON_RSP_GET_IRQ(cmd, type, irq_cfg) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, irq_cfg->val);\ -+ MC_RSP_OP(cmd, 1, 0, 64, uint64_t, irq_cfg->addr);\ -+ MC_RSP_OP(cmd, 2, 0, 32, int, irq_cfg->irq_num); \ -+ MC_RSP_OP(cmd, 2, 32, 32, int, type);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPCON_CMD_SET_IRQ_ENABLE(cmd, irq_index, en) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, en); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPCON_CMD_GET_IRQ_ENABLE(cmd, irq_index) \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPCON_RSP_GET_IRQ_ENABLE(cmd, en) \ -+ MC_RSP_OP(cmd, 0, 0, 8, uint8_t, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPCON_CMD_SET_IRQ_MASK(cmd, irq_index, mask) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, mask); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPCON_CMD_GET_IRQ_MASK(cmd, irq_index) \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPCON_RSP_GET_IRQ_MASK(cmd, mask) \ -+ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, mask) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPCON_CMD_GET_IRQ_STATUS(cmd, irq_index, status) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, status);\ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPCON_RSP_GET_IRQ_STATUS(cmd, status) \ -+ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, status) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPCON_CMD_CLEAR_IRQ_STATUS(cmd, irq_index, status) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, status); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPCON_RSP_GET_ATTR(cmd, attr) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 32, int, attr->id);\ -+ MC_RSP_OP(cmd, 0, 32, 16, uint16_t, attr->qbman_ch_id);\ -+ MC_RSP_OP(cmd, 0, 48, 8, uint8_t, attr->num_priorities);\ -+ MC_RSP_OP(cmd, 1, 0, 16, uint16_t, attr->version.major);\ -+ MC_RSP_OP(cmd, 1, 16, 16, uint16_t, attr->version.minor);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPCON_CMD_SET_NOTIFICATION(cmd, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, int, cfg->dpio_id);\ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, cfg->priority);\ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, cfg->user_ctx);\ -+} while (0) -+ -+#endif /* _FSL_DPCON_CMD_H */ -diff --git a/drivers/staging/fsl-mc/include/dpcon.h b/drivers/staging/fsl-mc/include/dpcon.h -new file mode 100644 -index 0000000..2555be5 ---- /dev/null -+++ b/drivers/staging/fsl-mc/include/dpcon.h -@@ -0,0 +1,407 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef __FSL_DPCON_H -+#define __FSL_DPCON_H -+ -+/* Data Path Concentrator API -+ * Contains initialization APIs and runtime control APIs for DPCON -+ */ -+ -+struct fsl_mc_io; -+ -+/** General DPCON macros */ -+ -+/** -+ * Use it to disable notifications; see dpcon_set_notification() -+ */ -+#define DPCON_INVALID_DPIO_ID (int)(-1) -+ -+/** -+ * dpcon_open() - Open a control session for the specified object -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @dpcon_id: DPCON unique ID -+ * @token: Returned token; use in subsequent API calls -+ * -+ * This function can be used to open a control session for an -+ * already created object; an object may have been declared in -+ * the DPL or by calling the dpcon_create() function. -+ * This function returns a unique authentication token, -+ * associated with the specific object ID and the specific MC -+ * portal; this token must be used in all subsequent commands for -+ * this specific object. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpcon_open(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ int dpcon_id, -+ uint16_t *token); -+ -+/** -+ * dpcon_close() - Close the control session of the object -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPCON object -+ * -+ * After this function is called, no further operations are -+ * allowed on the object without opening a new control session. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpcon_close(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * struct dpcon_cfg - Structure representing DPCON configuration -+ * @num_priorities: Number of priorities for the DPCON channel (1-8) -+ */ -+struct dpcon_cfg { -+ uint8_t num_priorities; -+}; -+ -+/** -+ * dpcon_create() - Create the DPCON object. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @cfg: Configuration structure -+ * @token: Returned token; use in subsequent API calls -+ * -+ * Create the DPCON object, allocate required resources and -+ * perform required initialization. -+ * -+ * The object can be created either by declaring it in the -+ * DPL file, or by calling this function. -+ * -+ * This function returns a unique authentication token, -+ * associated with the specific object ID and the specific MC -+ * portal; this token must be used in all subsequent calls to -+ * this specific object. For objects that are created using the -+ * DPL file, call dpcon_open() function to get an authentication -+ * token first. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpcon_create(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ const struct dpcon_cfg *cfg, -+ uint16_t *token); -+ -+/** -+ * dpcon_destroy() - Destroy the DPCON object and release all its resources. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPCON object -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpcon_destroy(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * dpcon_enable() - Enable the DPCON -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPCON object -+ * -+ * Return: '0' on Success; Error code otherwise -+ */ -+int dpcon_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * dpcon_disable() - Disable the DPCON -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPCON object -+ * -+ * Return: '0' on Success; Error code otherwise -+ */ -+int dpcon_disable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * dpcon_is_enabled() - Check if the DPCON is enabled. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPCON object -+ * @en: Returns '1' if object is enabled; '0' otherwise -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpcon_is_enabled(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *en); -+ -+/** -+ * dpcon_reset() - Reset the DPCON, returns the object to initial state. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPCON object -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpcon_reset(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * struct dpcon_irq_cfg - IRQ configuration -+ * @addr: Address that must be written to signal a message-based interrupt -+ * @val: Value to write into irq_addr address -+ * @irq_num: A user defined number associated with this IRQ -+ */ -+struct dpcon_irq_cfg { -+ uint64_t addr; -+ uint32_t val; -+ int irq_num; -+}; -+ -+/** -+ * dpcon_set_irq() - Set IRQ information for the DPCON to trigger an interrupt. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPCON object -+ * @irq_index: Identifies the interrupt index to configure -+ * @irq_cfg: IRQ configuration -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpcon_set_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ struct dpcon_irq_cfg *irq_cfg); -+ -+/** -+ * dpcon_get_irq() - Get IRQ information from the DPCON. -+ * -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPCON object -+ * @irq_index: The interrupt index to configure -+ * @type: Interrupt type: 0 represents message interrupt -+ * type (both irq_addr and irq_val are valid) -+ * @irq_cfg: IRQ attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpcon_get_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ int *type, -+ struct dpcon_irq_cfg *irq_cfg); -+ -+/** -+ * dpcon_set_irq_enable() - Set overall interrupt state. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPCON object -+ * @irq_index: The interrupt index to configure -+ * @en: Interrupt state - enable = 1, disable = 0 -+ * -+ * Allows GPP software to control when interrupts are generated. -+ * Each interrupt can have up to 32 causes. The enable/disable control's the -+ * overall interrupt state. if the interrupt is disabled no causes will cause -+ * an interrupt. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpcon_set_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t en); -+ -+/** -+ * dpcon_get_irq_enable() - Get overall interrupt state. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPCON object -+ * @irq_index: The interrupt index to configure -+ * @en: Returned interrupt state - enable = 1, disable = 0 -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpcon_get_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t *en); -+ -+/** -+ * dpcon_set_irq_mask() - Set interrupt mask. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPCON object -+ * @irq_index: The interrupt index to configure -+ * @mask: Event mask to trigger interrupt; -+ * each bit: -+ * 0 = ignore event -+ * 1 = consider event for asserting IRQ -+ * -+ * Every interrupt can have up to 32 causes and the interrupt model supports -+ * masking/unmasking each cause independently -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpcon_set_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t mask); -+ -+/** -+ * dpcon_get_irq_mask() - Get interrupt mask. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPCON object -+ * @irq_index: The interrupt index to configure -+ * @mask: Returned event mask to trigger interrupt -+ * -+ * Every interrupt can have up to 32 causes and the interrupt model supports -+ * masking/unmasking each cause independently -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpcon_get_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *mask); -+ -+/** -+ * dpcon_get_irq_status() - Get the current status of any pending interrupts. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPCON object -+ * @irq_index: The interrupt index to configure -+ * @status: interrupts status - one bit per cause: -+ * 0 = no interrupt pending -+ * 1 = interrupt pending -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpcon_get_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *status); -+ -+/** -+ * dpcon_clear_irq_status() - Clear a pending interrupt's status -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPCON object -+ * @irq_index: The interrupt index to configure -+ * @status: bits to clear (W1C) - one bit per cause: -+ * 0 = don't change -+ * 1 = clear status bit -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpcon_clear_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t status); -+ -+/** -+ * struct dpcon_attr - Structure representing DPCON attributes -+ * @id: DPCON object ID -+ * @version: DPCON version -+ * @qbman_ch_id: Channel ID to be used by dequeue operation -+ * @num_priorities: Number of priorities for the DPCON channel (1-8) -+ */ -+struct dpcon_attr { -+ int id; -+ /** -+ * struct version - DPCON version -+ * @major: DPCON major version -+ * @minor: DPCON minor version -+ */ -+ struct { -+ uint16_t major; -+ uint16_t minor; -+ } version; -+ uint16_t qbman_ch_id; -+ uint8_t num_priorities; -+}; -+ -+/** -+ * dpcon_get_attributes() - Retrieve DPCON attributes. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPCON object -+ * @attr: Object's attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpcon_get_attributes(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpcon_attr *attr); -+ -+/** -+ * struct dpcon_notification_cfg - Structure representing notification parameters -+ * @dpio_id: DPIO object ID; must be configured with a notification channel; -+ * to disable notifications set it to 'DPCON_INVALID_DPIO_ID'; -+ * @priority: Priority selection within the DPIO channel; valid values -+ * are 0-7, depending on the number of priorities in that channel -+ * @user_ctx: User context value provided with each CDAN message -+ */ -+struct dpcon_notification_cfg { -+ int dpio_id; -+ uint8_t priority; -+ uint64_t user_ctx; -+}; -+ -+/** -+ * dpcon_set_notification() - Set DPCON notification destination -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPCON object -+ * @cfg: Notification parameters -+ * -+ * Return: '0' on Success; Error code otherwise -+ */ -+int dpcon_set_notification(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dpcon_notification_cfg *cfg); -+ -+#endif /* __FSL_DPCON_H */ -diff --git a/drivers/staging/fsl-mc/include/dpmac-cmd.h b/drivers/staging/fsl-mc/include/dpmac-cmd.h -new file mode 100644 -index 0000000..c123aab ---- /dev/null -+++ b/drivers/staging/fsl-mc/include/dpmac-cmd.h -@@ -0,0 +1,192 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef _FSL_DPMAC_CMD_H -+#define _FSL_DPMAC_CMD_H -+ -+/* DPMAC Version */ -+#define DPMAC_VER_MAJOR 3 -+#define DPMAC_VER_MINOR 0 -+ -+/* Command IDs */ -+#define DPMAC_CMDID_CLOSE 0x800 -+#define DPMAC_CMDID_OPEN 0x80c -+#define DPMAC_CMDID_CREATE 0x90c -+#define DPMAC_CMDID_DESTROY 0x900 -+ -+#define DPMAC_CMDID_GET_ATTR 0x004 -+#define DPMAC_CMDID_RESET 0x005 -+ -+#define DPMAC_CMDID_SET_IRQ 0x010 -+#define DPMAC_CMDID_GET_IRQ 0x011 -+#define DPMAC_CMDID_SET_IRQ_ENABLE 0x012 -+#define DPMAC_CMDID_GET_IRQ_ENABLE 0x013 -+#define DPMAC_CMDID_SET_IRQ_MASK 0x014 -+#define DPMAC_CMDID_GET_IRQ_MASK 0x015 -+#define DPMAC_CMDID_GET_IRQ_STATUS 0x016 -+#define DPMAC_CMDID_CLEAR_IRQ_STATUS 0x017 -+ -+#define DPMAC_CMDID_MDIO_READ 0x0c0 -+#define DPMAC_CMDID_MDIO_WRITE 0x0c1 -+#define DPMAC_CMDID_GET_LINK_CFG 0x0c2 -+#define DPMAC_CMDID_SET_LINK_STATE 0x0c3 -+#define DPMAC_CMDID_GET_COUNTER 0x0c4 -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_CREATE(cmd, cfg) \ -+ MC_CMD_OP(cmd, 0, 0, 32, int, cfg->mac_id) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_OPEN(cmd, dpmac_id) \ -+ MC_CMD_OP(cmd, 0, 0, 32, int, dpmac_id) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_SET_IRQ(cmd, irq_index, irq_addr, irq_val, user_irq_id) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, irq_index);\ -+ MC_CMD_OP(cmd, 0, 32, 32, uint32_t, irq_val);\ -+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, irq_addr); \ -+ MC_CMD_OP(cmd, 2, 0, 32, int, user_irq_id); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_GET_IRQ(cmd, irq_index) \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_RSP_GET_IRQ(cmd, type, irq_addr, irq_val, user_irq_id) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, irq_val); \ -+ MC_RSP_OP(cmd, 1, 0, 64, uint64_t, irq_addr); \ -+ MC_RSP_OP(cmd, 2, 0, 32, int, user_irq_id); \ -+ MC_RSP_OP(cmd, 2, 32, 32, int, type); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_SET_IRQ_ENABLE(cmd, irq_index, en) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, en); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_GET_IRQ_ENABLE(cmd, irq_index) \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_RSP_GET_IRQ_ENABLE(cmd, en) \ -+ MC_RSP_OP(cmd, 0, 0, 8, uint8_t, en) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_SET_IRQ_MASK(cmd, irq_index, mask) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, mask);\ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_GET_IRQ_MASK(cmd, irq_index) \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_RSP_GET_IRQ_MASK(cmd, mask) \ -+ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, mask) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_GET_IRQ_STATUS(cmd, irq_index) \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_RSP_GET_IRQ_STATUS(cmd, status) \ -+ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, status) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_CLEAR_IRQ_STATUS(cmd, irq_index, status) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, status); \ -+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_RSP_GET_ATTRIBUTES(cmd, attr) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 32, int, attr->phy_id);\ -+ MC_RSP_OP(cmd, 0, 32, 32, int, attr->id);\ -+ MC_RSP_OP(cmd, 1, 0, 16, uint16_t, attr->version.major);\ -+ MC_RSP_OP(cmd, 1, 16, 16, uint16_t, attr->version.minor);\ -+ MC_RSP_OP(cmd, 1, 32, 8, enum dpmac_link_type, attr->link_type);\ -+ MC_RSP_OP(cmd, 1, 40, 8, enum dpmac_eth_if, attr->eth_if);\ -+ MC_RSP_OP(cmd, 2, 0, 32, uint32_t, attr->max_rate);\ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_MDIO_READ(cmd, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, cfg->phy_addr); \ -+ MC_CMD_OP(cmd, 0, 8, 8, uint8_t, cfg->reg); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_RSP_MDIO_READ(cmd, data) \ -+ MC_RSP_OP(cmd, 0, 16, 16, uint16_t, data) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_MDIO_WRITE(cmd, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, cfg->phy_addr); \ -+ MC_CMD_OP(cmd, 0, 8, 8, uint8_t, cfg->reg); \ -+ MC_CMD_OP(cmd, 0, 16, 16, uint16_t, cfg->data); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_RSP_GET_LINK_CFG(cmd, cfg) \ -+do { \ -+ MC_RSP_OP(cmd, 0, 0, 64, uint64_t, cfg->options); \ -+ MC_RSP_OP(cmd, 1, 0, 32, uint32_t, cfg->rate); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_SET_LINK_STATE(cmd, cfg) \ -+do { \ -+ MC_CMD_OP(cmd, 0, 0, 64, uint64_t, cfg->options); \ -+ MC_CMD_OP(cmd, 1, 0, 32, uint32_t, cfg->rate); \ -+ MC_CMD_OP(cmd, 2, 0, 1, int, cfg->up); \ -+} while (0) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_CMD_GET_COUNTER(cmd, type) \ -+ MC_CMD_OP(cmd, 0, 0, 8, enum dpmac_counter, type) -+ -+/* cmd, param, offset, width, type, arg_name */ -+#define DPMAC_RSP_GET_COUNTER(cmd, counter) \ -+ MC_RSP_OP(cmd, 1, 0, 64, uint64_t, counter) -+ -+#endif /* _FSL_DPMAC_CMD_H */ -diff --git a/drivers/staging/fsl-mc/include/dpmac.h b/drivers/staging/fsl-mc/include/dpmac.h -new file mode 100644 -index 0000000..88091b5 ---- /dev/null -+++ b/drivers/staging/fsl-mc/include/dpmac.h -@@ -0,0 +1,528 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef __FSL_DPMAC_H -+#define __FSL_DPMAC_H -+ -+/* Data Path MAC API -+ * Contains initialization APIs and runtime control APIs for DPMAC -+ */ -+ -+struct fsl_mc_io; -+ -+/** -+ * dpmac_open() - Open a control session for the specified object. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @dpmac_id: DPMAC unique ID -+ * @token: Returned token; use in subsequent API calls -+ * -+ * This function can be used to open a control session for an -+ * already created object; an object may have been declared in -+ * the DPL or by calling the dpmac_create function. -+ * This function returns a unique authentication token, -+ * associated with the specific object ID and the specific MC -+ * portal; this token must be used in all subsequent commands for -+ * this specific object -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_open(struct fsl_mc_io *mc_io, int dpmac_id, uint16_t *token); -+ -+/** -+ * dpmac_close() - Close the control session of the object -+ * @mc_io: Pointer to MC portal's I/O object -+ * @token: Token of DPMAC object -+ * -+ * After this function is called, no further operations are -+ * allowed on the object without opening a new control session. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_close(struct fsl_mc_io *mc_io, uint16_t token); -+ -+/** -+ * enum dpmac_link_type - DPMAC link type -+ * @DPMAC_LINK_TYPE_NONE: No link -+ * @DPMAC_LINK_TYPE_FIXED: Link is fixed type -+ * @DPMAC_LINK_TYPE_PHY: Link by PHY ID -+ * @DPMAC_LINK_TYPE_BACKPLANE: Backplane link type -+ */ -+enum dpmac_link_type { -+ DPMAC_LINK_TYPE_NONE, -+ DPMAC_LINK_TYPE_FIXED, -+ DPMAC_LINK_TYPE_PHY, -+ DPMAC_LINK_TYPE_BACKPLANE -+}; -+ -+/** -+ * enum dpmac_eth_if - DPMAC Ethrnet interface -+ * @DPMAC_ETH_IF_MII: MII interface -+ * @DPMAC_ETH_IF_RMII: RMII interface -+ * @DPMAC_ETH_IF_SMII: SMII interface -+ * @DPMAC_ETH_IF_GMII: GMII interface -+ * @DPMAC_ETH_IF_RGMII: RGMII interface -+ * @DPMAC_ETH_IF_SGMII: SGMII interface -+ * @DPMAC_ETH_IF_XGMII: XGMII interface -+ * @DPMAC_ETH_IF_QSGMII: QSGMII interface -+ * @DPMAC_ETH_IF_XAUI: XAUI interface -+ * @DPMAC_ETH_IF_XFI: XFI interface -+ */ -+enum dpmac_eth_if { -+ DPMAC_ETH_IF_MII, -+ DPMAC_ETH_IF_RMII, -+ DPMAC_ETH_IF_SMII, -+ DPMAC_ETH_IF_GMII, -+ DPMAC_ETH_IF_RGMII, -+ DPMAC_ETH_IF_SGMII, -+ DPMAC_ETH_IF_XGMII, -+ DPMAC_ETH_IF_QSGMII, -+ DPMAC_ETH_IF_XAUI, -+ DPMAC_ETH_IF_XFI -+}; -+ -+/** -+ * struct dpmac_cfg() - Structure representing DPMAC configuration -+ * @mac_id: Represents the Hardware MAC ID; in case of multiple WRIOP, -+ * the MAC IDs are continuous. -+ * For example: 2 WRIOPs, 16 MACs in each: -+ * MAC IDs for the 1st WRIOP: 1-16, -+ * MAC IDs for the 2nd WRIOP: 17-32. -+ */ -+struct dpmac_cfg { -+ int mac_id; -+}; -+ -+/** -+ * dpmac_create() - Create the DPMAC object. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cfg: Configuration structure -+ * @token: Returned token; use in subsequent API calls -+ * -+ * Create the DPMAC object, allocate required resources and -+ * perform required initialization. -+ * -+ * The object can be created either by declaring it in the -+ * DPL file, or by calling this function. -+ * This function returns a unique authentication token, -+ * associated with the specific object ID and the specific MC -+ * portal; this token must be used in all subsequent calls to -+ * this specific object. For objects that are created using the -+ * DPL file, call dpmac_open function to get an authentication -+ * token first. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_create(struct fsl_mc_io *mc_io, -+ const struct dpmac_cfg *cfg, -+ uint16_t *token); -+ -+/** -+ * dpmac_destroy() - Destroy the DPMAC object and release all its resources. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @token: Token of DPMAC object -+ * -+ * Return: '0' on Success; error code otherwise. -+ */ -+int dpmac_destroy(struct fsl_mc_io *mc_io, uint16_t token); -+ -+/* DPMAC IRQ Index and Events */ -+ -+/* IRQ index */ -+#define DPMAC_IRQ_INDEX 0 -+/* IRQ event - indicates a change in link state */ -+#define DPMAC_IRQ_EVENT_LINK_CFG_REQ 0x00000001 -+/* irq event - Indicates that the link state changed */ -+#define DPMAC_IRQ_EVENT_LINK_CHANGED 0x00000002 -+ -+/** -+ * dpmac_set_irq() - Set IRQ information for the DPMAC to trigger an interrupt. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @token: Token of DPMAC object -+ * @irq_index: Identifies the interrupt index to configure -+ * @irq_addr: Address that must be written to -+ * signal a message-based interrupt -+ * @irq_val: Value to write into irq_addr address -+ * @user_irq_id: A user defined number associated with this IRQ -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_set_irq(struct fsl_mc_io *mc_io, -+ uint16_t token, -+ uint8_t irq_index, -+ uint64_t irq_addr, -+ uint32_t irq_val, -+ int user_irq_id); -+ -+/** -+ * dpmac_get_irq() - Get IRQ information from the DPMAC. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @token: Token of DPMAC object -+ * @irq_index: The interrupt index to configure -+ * @type: Interrupt type: 0 represents message interrupt -+ * type (both irq_addr and irq_val are valid) -+ * @irq_addr: Returned address that must be written to -+ * signal the message-based interrupt -+ * @irq_val: Value to write into irq_addr address -+ * @user_irq_id: A user defined number associated with this IRQ -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_get_irq(struct fsl_mc_io *mc_io, -+ uint16_t token, -+ uint8_t irq_index, -+ int *type, -+ uint64_t *irq_addr, -+ uint32_t *irq_val, -+ int *user_irq_id); -+ -+/** -+ * dpmac_set_irq_enable() - Set overall interrupt state. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @token: Token of DPMAC object -+ * @irq_index: The interrupt index to configure -+ * @en: Interrupt state - enable = 1, disable = 0 -+ * -+ * Allows GPP software to control when interrupts are generated. -+ * Each interrupt can have up to 32 causes. The enable/disable control's the -+ * overall interrupt state. if the interrupt is disabled no causes will cause -+ * an interrupt. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_set_irq_enable(struct fsl_mc_io *mc_io, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t en); -+ -+/** -+ * dpmac_get_irq_enable() - Get overall interrupt state -+ * @mc_io: Pointer to MC portal's I/O object -+ * @token: Token of DPMAC object -+ * @irq_index: The interrupt index to configure -+ * @en: Returned interrupt state - enable = 1, disable = 0 -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_get_irq_enable(struct fsl_mc_io *mc_io, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t *en); -+ -+/** -+ * dpmac_set_irq_mask() - Set interrupt mask. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @token: Token of DPMAC object -+ * @irq_index: The interrupt index to configure -+ * @mask: Event mask to trigger interrupt; -+ * each bit: -+ * 0 = ignore event -+ * 1 = consider event for asserting IRQ -+ * -+ * Every interrupt can have up to 32 causes and the interrupt model supports -+ * masking/unmasking each cause independently -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_set_irq_mask(struct fsl_mc_io *mc_io, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t mask); -+ -+/** -+ * dpmac_get_irq_mask() - Get interrupt mask. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @token: Token of DPMAC object -+ * @irq_index: The interrupt index to configure -+ * @mask: Returned event mask to trigger interrupt -+ * -+ * Every interrupt can have up to 32 causes and the interrupt model supports -+ * masking/unmasking each cause independently -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_get_irq_mask(struct fsl_mc_io *mc_io, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *mask); -+ -+/** -+ * dpmac_get_irq_status() - Get the current status of any pending interrupts. -+ * -+ * @mc_io: Pointer to MC portal's I/O object -+ * @token: Token of DPMAC object -+ * @irq_index: The interrupt index to configure -+ * @status: Returned interrupts status - one bit per cause: -+ * 0 = no interrupt pending -+ * 1 = interrupt pending -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_get_irq_status(struct fsl_mc_io *mc_io, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *status); -+ -+/** -+ * dpmac_clear_irq_status() - Clear a pending interrupt's status -+ * -+ * @mc_io: Pointer to MC portal's I/O object -+ * @token: Token of DPMAC object -+ * @irq_index: The interrupt index to configure -+ * @status: Bits to clear (W1C) - one bit per cause: -+ * 0 = don't change -+ * 1 = clear status bit -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_clear_irq_status(struct fsl_mc_io *mc_io, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t status); -+ -+/** -+ * struct dpmac_attr - Structure representing DPMAC attributes -+ * @id: DPMAC object ID -+ * @phy_id: PHY ID -+ * @link_type: link type -+ * @eth_if: Ethernet interface -+ * @max_rate: Maximum supported rate - in Mbps -+ * @version: DPMAC version -+ */ -+struct dpmac_attr { -+ int id; -+ int phy_id; -+ enum dpmac_link_type link_type; -+ enum dpmac_eth_if eth_if; -+ uint32_t max_rate; -+ /** -+ * struct version - Structure representing DPMAC version -+ * @major: DPMAC major version -+ * @minor: DPMAC minor version -+ */ -+ struct { -+ uint16_t major; -+ uint16_t minor; -+ } version; -+}; -+ -+/** -+ * dpmac_get_attributes - Retrieve DPMAC attributes. -+ * -+ * @mc_io: Pointer to MC portal's I/O object -+ * @token: Token of DPMAC object -+ * @attr: Returned object's attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_get_attributes(struct fsl_mc_io *mc_io, -+ uint16_t token, -+ struct dpmac_attr *attr); -+ -+/** -+ * struct dpmac_mdio_cfg - DPMAC MDIO read/write parameters -+ * @phy_addr: MDIO device address -+ * @reg: Address of the register within the Clause 45 PHY device from which data -+ * is to be read -+ * @data: Data read/write from/to MDIO -+ */ -+struct dpmac_mdio_cfg { -+ uint8_t phy_addr; -+ uint8_t reg; -+ uint16_t data; -+}; -+ -+/** -+ * dpmac_mdio_read() - Perform MDIO read transaction -+ * @mc_io: Pointer to opaque I/O object -+ * @token: Token of DPMAC object -+ * @cfg: Structure with MDIO transaction parameters -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_mdio_read(struct fsl_mc_io *mc_io, uint16_t token, -+ struct dpmac_mdio_cfg *cfg); -+ -+ -+/** -+ * dpmac_mdio_write() - Perform MDIO write transaction -+ * @mc_io: Pointer to opaque I/O object -+ * @token: Token of DPMAC object -+ * @cfg: Structure with MDIO transaction parameters -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_mdio_write(struct fsl_mc_io *mc_io, uint16_t token, -+ struct dpmac_mdio_cfg *cfg); -+ -+/* DPMAC link configuration/state options */ -+ -+/* Enable auto-negotiation */ -+#define DPMAC_LINK_OPT_AUTONEG 0x0000000000000001ULL -+/* Enable half-duplex mode */ -+#define DPMAC_LINK_OPT_HALF_DUPLEX 0x0000000000000002ULL -+/* Enable pause frames */ -+#define DPMAC_LINK_OPT_PAUSE 0x0000000000000004ULL -+/* Enable a-symmetric pause frames */ -+#define DPMAC_LINK_OPT_ASYM_PAUSE 0x0000000000000008ULL -+ -+/** -+ * struct dpmac_link_cfg - Structure representing DPMAC link configuration -+ * @rate: Link's rate - in Mbps -+ * @options: Enable/Disable DPMAC link cfg features (bitmap) -+ */ -+struct dpmac_link_cfg { -+ uint32_t rate; -+ uint64_t options; -+}; -+ -+/** -+ * dpmac_get_link_cfg() - Get Ethernet link configuration -+ * @mc_io: Pointer to opaque I/O object -+ * @token: Token of DPMAC object -+ * @cfg: Returned structure with the link configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_get_link_cfg(struct fsl_mc_io *mc_io, uint16_t token, -+ struct dpmac_link_cfg *cfg); -+ -+/** -+ * struct dpmac_link_state - DPMAC link configuration request -+ * @rate: Rate in Mbps -+ * @options: Enable/Disable DPMAC link cfg features (bitmap) -+ * @up: Link state -+ */ -+struct dpmac_link_state { -+ uint32_t rate; -+ uint64_t options; -+ int up; -+}; -+ -+/** -+ * dpmac_set_link_state() - Set the Ethernet link status -+ * @mc_io: Pointer to opaque I/O object -+ * @token: Token of DPMAC object -+ * @link_state: Link state configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmac_set_link_state(struct fsl_mc_io *mc_io, uint16_t token, -+ struct dpmac_link_state *link_state); -+ -+/** -+ * enum dpni_counter - DPNI counter types -+ * @DPMAC_CNT_ING_FRAME_64: counts 64-octet frame, good or bad. -+ * @DPMAC_CNT_ING_FRAME_127: counts 65- to 127-octet frame, good or bad. -+ * @DPMAC_CNT_ING_FRAME_255: counts 128- to 255-octet frame, good or bad. -+ * @DPMAC_CNT_ING_FRAME_511: counts 256- to 511-octet frame, good or bad. -+ * @DPMAC_CNT_ING_FRAME_1023: counts 512- to 1023-octet frame, good or bad. -+ * @DPMAC_CNT_ING_FRAME_1518: counts 1024- to 1518-octet frame, good or bad. -+ * @DPMAC_CNT_ING_FRAME_1519_MAX: counts 1519-octet frame and larger -+ * (up to max frame length specified), -+ * good or bad. -+ * @DPMAC_CNT_ING_FRAG: counts packet which is shorter than 64 octets received -+ * with a wrong CRC -+ * @DPMAC_CNT_ING_JABBER: counts packet longer than the maximum frame length -+ * specified, with a bad frame check sequence. -+ * @DPMAC_CNT_ING_FRAME_DISCARD: counts dropped packet due to internal errors. -+ * Occurs when a receive FIFO overflows. -+ * Includes also packets truncated as a result of -+ * the receive FIFO overflow. -+ * @DPMAC_CNT_ING_ALIGN_ERR: counts frame with an alignment error -+ * (optional used for wrong SFD) -+ * @DPMAC_CNT_EGR_UNDERSIZED: counts packet transmitted that was less than 64 -+ * octets long with a good CRC. -+ * @DPMAC_CNT_ING_OVERSIZED: counts packet longer than the maximum frame length -+ * specified, with a good frame check sequence. -+ * @DPMAC_CNT_ING_VALID_PAUSE_FRAME: counts valid pause frame (regular and PFC). -+ * @DPMAC_CNT_EGR_VALID_PAUSE_FRAME: counts valid pause frame transmitted -+ * (regular and PFC). -+ * @DPMAC_CNT_ING_BYTE: counts octet received except preamble for all valid -+ frames and valid pause frames. -+ * @DPMAC_CNT_ING_MCAST_FRAME: counts received multicast frame -+ * @DPMAC_CNT_ING_BCAST_FRAME: counts received broadcast frame -+ * @DPMAC_CNT_ING_ALL_FRAME: counts each good or bad packet received. -+ * @DPMAC_CNT_ING_UCAST_FRAME: counts received unicast frame -+ * @DPMAC_CNT_ING_ERR_FRAME: counts frame received with an error -+ * (except for undersized/fragment frame) -+ * @DPMAC_CNT_EGR_BYTE: counts octet transmitted except preamble for all valid -+ * frames and valid pause frames transmitted. -+ * @DPMAC_CNT_EGR_MCAST_FRAME: counts transmitted multicast frame -+ * @DPMAC_CNT_EGR_BCAST_FRAME: counts transmitted broadcast frame -+ * @DPMAC_CNT_EGR_UCAST_FRAME: counts transmitted unicast frame -+ * @DPMAC_CNT_EGR_ERR_FRAME: counts frame transmitted with an error -+ * @DPMAC_CNT_ING_GOOD_FRAME: counts frame received without error, including -+ * pause frames. -+ */ -+enum dpmac_counter { -+ DPMAC_CNT_ING_FRAME_64, -+ DPMAC_CNT_ING_FRAME_127, -+ DPMAC_CNT_ING_FRAME_255, -+ DPMAC_CNT_ING_FRAME_511, -+ DPMAC_CNT_ING_FRAME_1023, -+ DPMAC_CNT_ING_FRAME_1518, -+ DPMAC_CNT_ING_FRAME_1519_MAX, -+ DPMAC_CNT_ING_FRAG, -+ DPMAC_CNT_ING_JABBER, -+ DPMAC_CNT_ING_FRAME_DISCARD, -+ DPMAC_CNT_ING_ALIGN_ERR, -+ DPMAC_CNT_EGR_UNDERSIZED, -+ DPMAC_CNT_ING_OVERSIZED, -+ DPMAC_CNT_ING_VALID_PAUSE_FRAME, -+ DPMAC_CNT_EGR_VALID_PAUSE_FRAME, -+ DPMAC_CNT_ING_BYTE, -+ DPMAC_CNT_ING_MCAST_FRAME, -+ DPMAC_CNT_ING_BCAST_FRAME, -+ DPMAC_CNT_ING_ALL_FRAME, -+ DPMAC_CNT_ING_UCAST_FRAME, -+ DPMAC_CNT_ING_ERR_FRAME, -+ DPMAC_CNT_EGR_BYTE, -+ DPMAC_CNT_EGR_MCAST_FRAME, -+ DPMAC_CNT_EGR_BCAST_FRAME, -+ DPMAC_CNT_EGR_UCAST_FRAME, -+ DPMAC_CNT_EGR_ERR_FRAME, -+ DPMAC_CNT_ING_GOOD_FRAME -+}; -+ -+/** -+ * dpmac_get_counter() - Read a specific DPMAC counter -+ * @mc_io: Pointer to opaque I/O object -+ * @token: Token of DPMAC object -+ * @type: The requested counter -+ * @counter: Returned counter value -+ * -+ * Return: The requested counter; '0' otherwise. -+ */ -+int dpmac_get_counter(struct fsl_mc_io *mc_io, uint16_t token, -+ enum dpmac_counter type, -+ uint64_t *counter); -+ -+#endif /* __FSL_DPMAC_H */ -diff --git a/drivers/staging/fsl-mc/include/dpmng.h b/drivers/staging/fsl-mc/include/dpmng.h -new file mode 100644 -index 0000000..d1c4588 ---- /dev/null -+++ b/drivers/staging/fsl-mc/include/dpmng.h -@@ -0,0 +1,80 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef __FSL_DPMNG_H -+#define __FSL_DPMNG_H -+ -+/* Management Complex General API -+ * Contains general API for the Management Complex firmware -+ */ -+ -+struct fsl_mc_io; -+ -+/** -+ * struct mc_version -+ * @major: Major version number: incremented on API compatibility changes -+ * @minor: Minor version number: incremented on API additions (that are -+ * backward compatible); reset when major version is incremented -+ * @revision: Internal revision number: incremented on implementation changes -+ * and/or bug fixes that have no impact on API -+ */ -+struct mc_version { -+ uint32_t major; -+ uint32_t minor; -+ uint32_t revision; -+}; -+ -+/** -+ * mc_get_version() - Retrieves the Management Complex firmware -+ * version information -+ * @mc_io: Pointer to opaque I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @mc_ver_info: Returned version information structure -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int mc_get_version(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ struct mc_version *mc_ver_info); -+ -+/** -+ * dpmng_get_container_id() - Get container ID associated with a given portal. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @container_id: Requested container ID -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dpmng_get_container_id(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ int *container_id); -+ -+#endif /* __FSL_DPMNG_H */ -diff --git a/drivers/staging/fsl-mc/include/dprc.h b/drivers/staging/fsl-mc/include/dprc.h -new file mode 100644 -index 0000000..810ded0 ---- /dev/null -+++ b/drivers/staging/fsl-mc/include/dprc.h -@@ -0,0 +1,990 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef _FSL_DPRC_H -+#define _FSL_DPRC_H -+ -+#include "mc-cmd.h" -+ -+/* Data Path Resource Container API -+ * Contains DPRC API for managing and querying DPAA resources -+ */ -+ -+struct fsl_mc_io; -+ -+/** -+ * Set this value as the icid value in dprc_cfg structure when creating a -+ * container, in case the ICID is not selected by the user and should be -+ * allocated by the DPRC from the pool of ICIDs. -+ */ -+#define DPRC_GET_ICID_FROM_POOL (uint16_t)(~(0)) -+ -+/** -+ * Set this value as the portal_id value in dprc_cfg structure when creating a -+ * container, in case the portal ID is not specifically selected by the -+ * user and should be allocated by the DPRC from the pool of portal ids. -+ */ -+#define DPRC_GET_PORTAL_ID_FROM_POOL (int)(~(0)) -+ -+/** -+ * dprc_open() - Open DPRC object for use -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @container_id: Container ID to open -+ * @token: Returned token of DPRC object -+ * -+ * Return: '0' on Success; Error code otherwise. -+ * -+ * @warning Required before any operation on the object. -+ */ -+int dprc_open(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ int container_id, -+ uint16_t *token); -+ -+/** -+ * dprc_close() - Close the control session of the object -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * -+ * After this function is called, no further operations are -+ * allowed on the object without opening a new control session. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_close(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token); -+ -+/** -+ * Container general options -+ * -+ * These options may be selected at container creation by the container creator -+ * and can be retrieved using dprc_get_attributes() -+ */ -+ -+/* Spawn Policy Option allowed - Indicates that the new container is allowed -+ * to spawn and have its own child containers. -+ */ -+#define DPRC_CFG_OPT_SPAWN_ALLOWED 0x00000001 -+ -+/* General Container allocation policy - Indicates that the new container is -+ * allowed to allocate requested resources from its parent container; if not -+ * set, the container is only allowed to use resources in its own pools; Note -+ * that this is a container's global policy, but the parent container may -+ * override it and set specific quota per resource type. -+ */ -+#define DPRC_CFG_OPT_ALLOC_ALLOWED 0x00000002 -+ -+/* Object initialization allowed - software context associated with this -+ * container is allowed to invoke object initialization operations. -+ */ -+#define DPRC_CFG_OPT_OBJ_CREATE_ALLOWED 0x00000004 -+ -+/* Topology change allowed - software context associated with this -+ * container is allowed to invoke topology operations, such as attach/detach -+ * of network objects. -+ */ -+#define DPRC_CFG_OPT_TOPOLOGY_CHANGES_ALLOWED 0x00000008 -+ -+/* AIOP - Indicates that container belongs to AIOP. */ -+#define DPRC_CFG_OPT_AIOP 0x00000020 -+ -+/* IRQ Config - Indicates that the container allowed to configure its IRQs. */ -+#define DPRC_CFG_OPT_IRQ_CFG_ALLOWED 0x00000040 -+ -+/** -+ * struct dprc_cfg - Container configuration options -+ * @icid: Container's ICID; if set to 'DPRC_GET_ICID_FROM_POOL', a free -+ * ICID value is allocated by the DPRC -+ * @portal_id: Portal ID; if set to 'DPRC_GET_PORTAL_ID_FROM_POOL', a free -+ * portal ID is allocated by the DPRC -+ * @options: Combination of 'DPRC_CFG_OPT_' options -+ * @label: Object's label -+ */ -+struct dprc_cfg { -+ uint16_t icid; -+ int portal_id; -+ uint64_t options; -+ char label[16]; -+}; -+ -+/** -+ * dprc_create_container() - Create child container -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @cfg: Child container configuration -+ * @child_container_id: Returned child container ID -+ * @child_portal_offset: Returned child portal offset from MC portal base -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_create_container(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dprc_cfg *cfg, -+ int *child_container_id, -+ uint64_t *child_portal_offset); -+ -+/** -+ * dprc_destroy_container() - Destroy child container. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @child_container_id: ID of the container to destroy -+ * -+ * This function terminates the child container, so following this call the -+ * child container ID becomes invalid. -+ * -+ * Notes: -+ * - All resources and objects of the destroyed container are returned to the -+ * parent container or destroyed if were created be the destroyed container. -+ * - This function destroy all the child containers of the specified -+ * container prior to destroying the container itself. -+ * -+ * warning: Only the parent container is allowed to destroy a child policy -+ * Container 0 can't be destroyed -+ * -+ * Return: '0' on Success; Error code otherwise. -+ * -+ */ -+int dprc_destroy_container(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int child_container_id); -+ -+/** -+ * dprc_reset_container - Reset child container. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @child_container_id: ID of the container to reset -+ * -+ * In case a software context crashes or becomes non-responsive, the parent -+ * may wish to reset its resources container before the software context is -+ * restarted. -+ * -+ * This routine informs all objects assigned to the child container that the -+ * container is being reset, so they may perform any cleanup operations that are -+ * needed. All objects handles that were owned by the child container shall be -+ * closed. -+ * -+ * Note that such request may be submitted even if the child software context -+ * has not crashed, but the resulting object cleanup operations will not be -+ * aware of that. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_reset_container(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int child_container_id); -+ -+/* IRQ */ -+ -+/* IRQ index */ -+#define DPRC_IRQ_INDEX 0 -+ -+/* Number of dprc's IRQs */ -+#define DPRC_NUM_OF_IRQS 1 -+ -+/* DPRC IRQ events */ -+ -+/* IRQ event - Indicates that a new object added to the container */ -+#define DPRC_IRQ_EVENT_OBJ_ADDED 0x00000001 -+ -+/* IRQ event - Indicates that an object was removed from the container */ -+#define DPRC_IRQ_EVENT_OBJ_REMOVED 0x00000002 -+ -+/* IRQ event - Indicates that resources added to the container */ -+#define DPRC_IRQ_EVENT_RES_ADDED 0x00000004 -+ -+/* IRQ event - Indicates that resources removed from the container */ -+#define DPRC_IRQ_EVENT_RES_REMOVED 0x00000008 -+ -+/* IRQ event - Indicates that one of the descendant containers that opened by -+ * this container is destroyed -+ */ -+#define DPRC_IRQ_EVENT_CONTAINER_DESTROYED 0x00000010 -+ -+/* IRQ event - Indicates that on one of the container's opened object is -+ * destroyed -+ */ -+#define DPRC_IRQ_EVENT_OBJ_DESTROYED 0x00000020 -+ -+/* Irq event - Indicates that object is created at the container */ -+#define DPRC_IRQ_EVENT_OBJ_CREATED 0x00000040 -+ -+/** -+ * struct dprc_irq_cfg - IRQ configuration -+ * @paddr: Address that must be written to signal a message-based interrupt -+ * @val: Value to write into irq_addr address -+ * @irq_num: A user defined number associated with this IRQ -+ */ -+struct dprc_irq_cfg { -+ uint64_t paddr; -+ uint32_t val; -+ int irq_num; -+}; -+ -+/** -+ * dprc_set_irq() - Set IRQ information for the DPRC to trigger an interrupt. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @irq_index: Identifies the interrupt index to configure -+ * @irq_cfg: IRQ configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_set_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ struct dprc_irq_cfg *irq_cfg); -+ -+/** -+ * dprc_get_irq() - Get IRQ information from the DPRC. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @irq_index: The interrupt index to configure -+ * @type: Interrupt type: 0 represents message interrupt -+ * type (both irq_addr and irq_val are valid) -+ * @irq_cfg: IRQ attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_get_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ int *type, -+ struct dprc_irq_cfg *irq_cfg); -+ -+/** -+ * dprc_set_irq_enable() - Set overall interrupt state. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @irq_index: The interrupt index to configure -+ * @en: Interrupt state - enable = 1, disable = 0 -+ * -+ * Allows GPP software to control when interrupts are generated. -+ * Each interrupt can have up to 32 causes. The enable/disable control's the -+ * overall interrupt state. if the interrupt is disabled no causes will cause -+ * an interrupt. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_set_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t en); -+ -+/** -+ * dprc_get_irq_enable() - Get overall interrupt state. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @irq_index: The interrupt index to configure -+ * @en: Returned interrupt state - enable = 1, disable = 0 -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_get_irq_enable(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint8_t *en); -+ -+/** -+ * dprc_set_irq_mask() - Set interrupt mask. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @irq_index: The interrupt index to configure -+ * @mask: event mask to trigger interrupt; -+ * each bit: -+ * 0 = ignore event -+ * 1 = consider event for asserting irq -+ * -+ * Every interrupt can have up to 32 causes and the interrupt model supports -+ * masking/unmasking each cause independently -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_set_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t mask); -+ -+/** -+ * dprc_get_irq_mask() - Get interrupt mask. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @irq_index: The interrupt index to configure -+ * @mask: Returned event mask to trigger interrupt -+ * -+ * Every interrupt can have up to 32 causes and the interrupt model supports -+ * masking/unmasking each cause independently -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_get_irq_mask(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *mask); -+ -+/** -+ * dprc_get_irq_status() - Get the current status of any pending interrupts. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @irq_index: The interrupt index to configure -+ * @status: Returned interrupts status - one bit per cause: -+ * 0 = no interrupt pending -+ * 1 = interrupt pending -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_get_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t *status); -+ -+/** -+ * dprc_clear_irq_status() - Clear a pending interrupt's status -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @irq_index: The interrupt index to configure -+ * @status: bits to clear (W1C) - one bit per cause: -+ * 0 = don't change -+ * 1 = clear status bit -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_clear_irq_status(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ uint8_t irq_index, -+ uint32_t status); -+ -+/** -+ * struct dprc_attributes - Container attributes -+ * @container_id: Container's ID -+ * @icid: Container's ICID -+ * @portal_id: Container's portal ID -+ * @options: Container's options as set at container's creation -+ * @version: DPRC version -+ */ -+struct dprc_attributes { -+ int container_id; -+ uint16_t icid; -+ int portal_id; -+ uint64_t options; -+ /** -+ * struct version - DPRC version -+ * @major: DPRC major version -+ * @minor: DPRC minor version -+ */ -+ struct { -+ uint16_t major; -+ uint16_t minor; -+ } version; -+}; -+ -+/** -+ * dprc_get_attributes() - Obtains container attributes -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @attributes: Returned container attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_get_attributes(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ struct dprc_attributes *attributes); -+ -+/** -+ * dprc_set_res_quota() - Set allocation policy for a specific resource/object -+ * type in a child container -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @child_container_id: ID of the child container -+ * @type: Resource/object type -+ * @quota: Sets the maximum number of resources of the selected type -+ * that the child container is allowed to allocate from its parent; -+ * when quota is set to -1, the policy is the same as container's -+ * general policy. -+ * -+ * Allocation policy determines whether or not a container may allocate -+ * resources from its parent. Each container has a 'global' allocation policy -+ * that is set when the container is created. -+ * -+ * This function sets allocation policy for a specific resource type. -+ * The default policy for all resource types matches the container's 'global' -+ * allocation policy. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ * -+ * @warning Only the parent container is allowed to change a child policy. -+ */ -+int dprc_set_res_quota(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int child_container_id, -+ char *type, -+ uint16_t quota); -+ -+/** -+ * dprc_get_res_quota() - Gets the allocation policy of a specific -+ * resource/object type in a child container -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @child_container_id: ID of the child container -+ * @type: resource/object type -+ * @quota: Returnes the maximum number of resources of the selected type -+ * that the child container is allowed to allocate from the parent; -+ * when quota is set to -1, the policy is the same as container's -+ * general policy. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_get_res_quota(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int child_container_id, -+ char *type, -+ uint16_t *quota); -+ -+/* Resource request options */ -+ -+/* Explicit resource ID request - The requested objects/resources -+ * are explicit and sequential (in case of resources). -+ * The base ID is given at res_req at base_align field -+ */ -+#define DPRC_RES_REQ_OPT_EXPLICIT 0x00000001 -+ -+/* Aligned resources request - Relevant only for resources -+ * request (and not objects). Indicates that resources base ID should be -+ * sequential and aligned to the value given at dprc_res_req base_align field -+ */ -+#define DPRC_RES_REQ_OPT_ALIGNED 0x00000002 -+ -+/* Plugged Flag - Relevant only for object assignment request. -+ * Indicates that after all objects assigned. An interrupt will be invoked at -+ * the relevant GPP. The assigned object will be marked as plugged. -+ * plugged objects can't be assigned from their container -+ */ -+#define DPRC_RES_REQ_OPT_PLUGGED 0x00000004 -+ -+/** -+ * struct dprc_res_req - Resource request descriptor, to be used in assignment -+ * or un-assignment of resources and objects. -+ * @type: Resource/object type: Represent as a NULL terminated string. -+ * This string may received by using dprc_get_pool() to get resource -+ * type and dprc_get_obj() to get object type; -+ * Note: it is not possible to assign/un-assign DPRC objects -+ * @num: Number of resources -+ * @options: Request options: combination of DPRC_RES_REQ_OPT_ options -+ * @id_base_align: In case of explicit assignment (DPRC_RES_REQ_OPT_EXPLICIT -+ * is set at option), this field represents the required base ID -+ * for resource allocation; In case of aligned assignment -+ * (DPRC_RES_REQ_OPT_ALIGNED is set at option), this field -+ * indicates the required alignment for the resource ID(s) - -+ * use 0 if there is no alignment or explicit ID requirements -+ */ -+struct dprc_res_req { -+ char type[16]; -+ uint32_t num; -+ uint32_t options; -+ int id_base_align; -+}; -+ -+/** -+ * dprc_assign() - Assigns objects or resource to a child container. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @container_id: ID of the child container -+ * @res_req: Describes the type and amount of resources to -+ * assign to the given container -+ * -+ * Assignment is usually done by a parent (this DPRC) to one of its child -+ * containers. -+ * -+ * According to the DPRC allocation policy, the assigned resources may be taken -+ * (allocated) from the container's ancestors, if not enough resources are -+ * available in the container itself. -+ * -+ * The type of assignment depends on the dprc_res_req options, as follows: -+ * - DPRC_RES_REQ_OPT_EXPLICIT: indicates that assigned resources should have -+ * the explicit base ID specified at the id_base_align field of res_req. -+ * - DPRC_RES_REQ_OPT_ALIGNED: indicates that the assigned resources should be -+ * aligned to the value given at id_base_align field of res_req. -+ * - DPRC_RES_REQ_OPT_PLUGGED: Relevant only for object assignment, -+ * and indicates that the object must be set to the plugged state. -+ * -+ * A container may use this function with its own ID in order to change a -+ * object state to plugged or unplugged. -+ * -+ * If IRQ information has been set in the child DPRC, it will signal an -+ * interrupt following every change in its object assignment. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_assign(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int container_id, -+ struct dprc_res_req *res_req); -+ -+/** -+ * dprc_unassign() - Un-assigns objects or resources from a child container -+ * and moves them into this (parent) DPRC. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @child_container_id: ID of the child container -+ * @res_req: Describes the type and amount of resources to un-assign from -+ * the child container -+ * -+ * Un-assignment of objects can succeed only if the object is not in the -+ * plugged or opened state. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_unassign(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int child_container_id, -+ struct dprc_res_req *res_req); -+ -+/** -+ * dprc_get_pool_count() - Get the number of dprc's pools -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @mc_io: Pointer to MC portal's I/O object -+ * @token: Token of DPRC object -+ * @pool_count: Returned number of resource pools in the dprc -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_get_pool_count(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *pool_count); -+ -+/** -+ * dprc_get_pool() - Get the type (string) of a certain dprc's pool -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @pool_index: Index of the pool to be queried (< pool_count) -+ * @type: The type of the pool -+ * -+ * The pool types retrieved one by one by incrementing -+ * pool_index up to (not including) the value of pool_count returned -+ * from dprc_get_pool_count(). dprc_get_pool_count() must -+ * be called prior to dprc_get_pool(). -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_get_pool(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int pool_index, -+ char *type); -+ -+/** -+ * dprc_get_obj_count() - Obtains the number of objects in the DPRC -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @obj_count: Number of objects assigned to the DPRC -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_get_obj_count(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int *obj_count); -+ -+/* Objects Attributes Flags */ -+ -+/* Opened state - Indicates that an object is open by at least one owner */ -+#define DPRC_OBJ_STATE_OPEN 0x00000001 -+/* Plugged state - Indicates that the object is plugged */ -+#define DPRC_OBJ_STATE_PLUGGED 0x00000002 -+ -+/** -+ * Shareability flag - Object flag indicating no memory shareability. -+ * the object generates memory accesses that are non coherent with other -+ * masters; -+ * user is responsible for proper memory handling through IOMMU configuration. -+ */ -+#define DPRC_OBJ_FLAG_NO_MEM_SHAREABILITY 0x0001 -+ -+/** -+ * struct dprc_obj_desc - Object descriptor, returned from dprc_get_obj() -+ * @type: Type of object: NULL terminated string -+ * @id: ID of logical object resource -+ * @vendor: Object vendor identifier -+ * @ver_major: Major version number -+ * @ver_minor: Minor version number -+ * @irq_count: Number of interrupts supported by the object -+ * @region_count: Number of mappable regions supported by the object -+ * @state: Object state: combination of DPRC_OBJ_STATE_ states -+ * @label: Object label -+ * @flags: Object's flags -+ */ -+struct dprc_obj_desc { -+ char type[16]; -+ int id; -+ uint16_t vendor; -+ uint16_t ver_major; -+ uint16_t ver_minor; -+ uint8_t irq_count; -+ uint8_t region_count; -+ uint32_t state; -+ char label[16]; -+ uint16_t flags; -+}; -+ -+/** -+ * dprc_get_obj() - Get general information on an object -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @obj_index: Index of the object to be queried (< obj_count) -+ * @obj_desc: Returns the requested object descriptor -+ * -+ * The object descriptors are retrieved one by one by incrementing -+ * obj_index up to (not including) the value of obj_count returned -+ * from dprc_get_obj_count(). dprc_get_obj_count() must -+ * be called prior to dprc_get_obj(). -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_get_obj(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ int obj_index, -+ struct dprc_obj_desc *obj_desc); -+ -+/** -+ * dprc_get_obj_desc() - Get object descriptor. -+ * -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @obj_type: The type of the object to get its descriptor. -+ * @obj_id: The id of the object to get its descriptor -+ * @obj_desc: The returned descriptor to fill and return to the user -+ * -+ * Return: '0' on Success; Error code otherwise. -+ * -+ */ -+int dprc_get_obj_desc(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ char *obj_type, -+ int obj_id, -+ struct dprc_obj_desc *obj_desc); -+ -+/** -+ * dprc_set_obj_irq() - Set IRQ information for object to trigger an interrupt. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @obj_type: Type of the object to set its IRQ -+ * @obj_id: ID of the object to set its IRQ -+ * @irq_index: The interrupt index to configure -+ * @irq_cfg: IRQ configuration -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_set_obj_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ char *obj_type, -+ int obj_id, -+ uint8_t irq_index, -+ struct dprc_irq_cfg *irq_cfg); -+ -+/** -+ * dprc_get_obj_irq() - Get IRQ information from object. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @obj_type: Type od the object to get its IRQ -+ * @obj_id: ID of the object to get its IRQ -+ * @irq_index: The interrupt index to configure -+ * @type: Interrupt type: 0 represents message interrupt -+ * type (both irq_addr and irq_val are valid) -+ * @irq_cfg: The returned IRQ attributes -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_get_obj_irq(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ char *obj_type, -+ int obj_id, -+ uint8_t irq_index, -+ int *type, -+ struct dprc_irq_cfg *irq_cfg); -+ -+/** -+ * dprc_get_res_count() - Obtains the number of free resources that are assigned -+ * to this container, by pool type -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @type: pool type -+ * @res_count: Returned number of free resources of the given -+ * resource type that are assigned to this DPRC -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_get_res_count(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ char *type, -+ int *res_count); -+ -+/** -+ * enum dprc_iter_status - Iteration status -+ * @DPRC_ITER_STATUS_FIRST: Perform first iteration -+ * @DPRC_ITER_STATUS_MORE: Indicates more/next iteration is needed -+ * @DPRC_ITER_STATUS_LAST: Indicates last iteration -+ */ -+enum dprc_iter_status { -+ DPRC_ITER_STATUS_FIRST = 0, -+ DPRC_ITER_STATUS_MORE = 1, -+ DPRC_ITER_STATUS_LAST = 2 -+}; -+ -+/** -+ * struct dprc_res_ids_range_desc - Resource ID range descriptor -+ * @base_id: Base resource ID of this range -+ * @last_id: Last resource ID of this range -+ * @iter_status: Iteration status - should be set to DPRC_ITER_STATUS_FIRST at -+ * first iteration; while the returned marker is DPRC_ITER_STATUS_MORE, -+ * additional iterations are needed, until the returned marker is -+ * DPRC_ITER_STATUS_LAST -+ */ -+struct dprc_res_ids_range_desc { -+ int base_id; -+ int last_id; -+ enum dprc_iter_status iter_status; -+}; -+ -+/** -+ * dprc_get_res_ids() - Obtains IDs of free resources in the container -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @type: pool type -+ * @range_desc: range descriptor -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_get_res_ids(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ char *type, -+ struct dprc_res_ids_range_desc *range_desc); -+ -+/* Region flags */ -+/* Cacheable - Indicates that region should be mapped as cacheable */ -+#define DPRC_REGION_CACHEABLE 0x00000001 -+ -+/** -+ * enum dprc_region_type - Region type -+ * @DPRC_REGION_TYPE_MC_PORTAL: MC portal region -+ * @DPRC_REGION_TYPE_QBMAN_PORTAL: Qbman portal region -+ */ -+enum dprc_region_type { -+ DPRC_REGION_TYPE_MC_PORTAL, -+ DPRC_REGION_TYPE_QBMAN_PORTAL -+}; -+ -+/** -+ * struct dprc_region_desc - Mappable region descriptor -+ * @base_offset: Region offset from region's base address. -+ * For DPMCP and DPRC objects, region base is offset from SoC MC portals -+ * base address; For DPIO, region base is offset from SoC QMan portals -+ * base address -+ * @size: Region size (in bytes) -+ * @flags: Region attributes -+ * @type: Portal region type -+ */ -+struct dprc_region_desc { -+ uint32_t base_offset; -+ uint32_t size; -+ uint32_t flags; -+ enum dprc_region_type type; -+}; -+ -+/** -+ * dprc_get_obj_region() - Get region information for a specified object. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @obj_type: Object type as returned in dprc_get_obj() -+ * @obj_id: Unique object instance as returned in dprc_get_obj() -+ * @region_index: The specific region to query -+ * @region_desc: Returns the requested region descriptor -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_get_obj_region(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ char *obj_type, -+ int obj_id, -+ uint8_t region_index, -+ struct dprc_region_desc *region_desc); -+ -+/** -+ * dprc_set_obj_label() - Set object label. -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @obj_type: Object's type -+ * @obj_id: Object's ID -+ * @label: The required label. The maximum length is 16 chars. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_set_obj_label(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ char *obj_type, -+ int obj_id, -+ char *label); -+ -+/** -+ * struct dprc_endpoint - Endpoint description for link connect/disconnect -+ * operations -+ * @type: Endpoint object type: NULL terminated string -+ * @id: Endpoint object ID -+ * @if_id: Interface ID; should be set for endpoints with multiple -+ * interfaces ("dpsw", "dpdmux"); for others, always set to 0 -+ */ -+struct dprc_endpoint { -+ char type[16]; -+ int id; -+ int if_id; -+}; -+ -+/** -+ * struct dprc_connection_cfg - Connection configuration. -+ * Used for virtual connections only -+ * @committed_rate: Committed rate (Mbits/s) -+ * @max_rate: Maximum rate (Mbits/s) -+ */ -+struct dprc_connection_cfg { -+ uint32_t committed_rate; -+ uint32_t max_rate; -+}; -+ -+/** -+ * dprc_connect() - Connect two endpoints to create a network link between them -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @endpoint1: Endpoint 1 configuration parameters -+ * @endpoint2: Endpoint 2 configuration parameters -+ * @cfg: Connection configuration. The connection configuration is ignored for -+ * connections made to DPMAC objects, where rate is retrieved from the -+ * MAC configuration. -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_connect(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dprc_endpoint *endpoint1, -+ const struct dprc_endpoint *endpoint2, -+ const struct dprc_connection_cfg *cfg); -+ -+/** -+ * dprc_disconnect() - Disconnect one endpoint to remove its network connection -+ * @mc_io: Pointer to MC portal's I/O object -+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+ * @token: Token of DPRC object -+ * @endpoint: Endpoint configuration parameters -+ * -+ * Return: '0' on Success; Error code otherwise. -+ */ -+int dprc_disconnect(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dprc_endpoint *endpoint); -+ -+/** -+* dprc_get_connection() - Get connected endpoint and link status if connection -+* exists. -+* @mc_io: Pointer to MC portal's I/O object -+* @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' -+* @token: Token of DPRC object -+* @endpoint1: Endpoint 1 configuration parameters -+* @endpoint2: Returned endpoint 2 configuration parameters -+* @state: Returned link state: -+* 1 - link is up; -+* 0 - link is down; -+* -1 - no connection (endpoint2 information is irrelevant) -+* -+* Return: '0' on Success; -ENAVAIL if connection does not exist. -+*/ -+int dprc_get_connection(struct fsl_mc_io *mc_io, -+ uint32_t cmd_flags, -+ uint16_t token, -+ const struct dprc_endpoint *endpoint1, -+ struct dprc_endpoint *endpoint2, -+ int *state); -+ -+#endif /* _FSL_DPRC_H */ -+ -diff --git a/drivers/staging/fsl-mc/include/fsl_dpaa2_fd.h b/drivers/staging/fsl-mc/include/fsl_dpaa2_fd.h -new file mode 100644 -index 0000000..3e9af59 ---- /dev/null -+++ b/drivers/staging/fsl-mc/include/fsl_dpaa2_fd.h -@@ -0,0 +1,774 @@ -+/* Copyright 2014 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 __FSL_DPAA2_FD_H -+#define __FSL_DPAA2_FD_H -+ -+/** -+ * DOC: DPAA2 FD - Frame Descriptor APIs for DPAA2 -+ * -+ * Frame Descriptors (FDs) are used to describe frame data in the DPAA2. -+ * Frames can be enqueued and dequeued to Frame Queues which are consumed -+ * by the various DPAA accelerators (WRIOP, SEC, PME, DCE) -+ * -+ * There are three types of frames: Single, Scatter Gather and Frame Lists. -+ * -+ * The set of APIs in this file must be used to create, manipulate and -+ * query Frame Descriptor. -+ * -+ */ -+ -+/** -+ * struct dpaa2_fd - Place-holder for FDs. -+ * @words: for easier/faster copying the whole FD structure. -+ * @addr_lo: the lower 32 bits of the address in FD. -+ * @addr_hi: the upper 32 bits of the address in FD. -+ * @len: the length field in FD. -+ * @bpid_offset: represent the bpid and offset fields in FD -+ * @frc: frame context -+ * @ctrl: the 32bit control bits including dd, sc,... va, err. -+ * @flc_lo: the lower 32bit of flow context. -+ * @flc_hi: the upper 32bits of flow context. -+ * -+ * This structure represents the basic Frame Descriptor used in the system. -+ * We represent it via the simplest form that we need for now. Different -+ * overlays may be needed to support different options, etc. (It is impractical -+ * to define One True Struct, because the resulting encoding routines (lots of -+ * read-modify-writes) would be worst-case performance whether or not -+ * circumstances required them.) -+ */ -+struct dpaa2_fd { -+ union { -+ u32 words[8]; -+ struct dpaa2_fd_simple { -+ u32 addr_lo; -+ u32 addr_hi; -+ u32 len; -+ /* offset in the MS 16 bits, BPID in the LS 16 bits */ -+ u32 bpid_offset; -+ u32 frc; /* frame context */ -+ /* "err", "va", "cbmt", "asal", [...] */ -+ u32 ctrl; -+ /* flow context */ -+ u32 flc_lo; -+ u32 flc_hi; -+ } simple; -+ }; -+}; -+ -+enum dpaa2_fd_format { -+ dpaa2_fd_single = 0, -+ dpaa2_fd_list, -+ dpaa2_fd_sg -+}; -+ -+/* Accessors for SG entry fields -+ * -+ * These setters and getters assume little endian format. For converting -+ * between LE and cpu endianness, the specific conversion functions must be -+ * called before the SGE contents are accessed by the core (on Rx), -+ * respectively before the SG table is sent to hardware (on Tx) -+ */ -+ -+/** -+ * dpaa2_fd_get_addr() - get the addr field of frame descriptor -+ * @fd: the given frame descriptor. -+ * -+ * Return the address in the frame descriptor. -+ */ -+static inline dma_addr_t dpaa2_fd_get_addr(const struct dpaa2_fd *fd) -+{ -+ return (dma_addr_t)((((uint64_t)fd->simple.addr_hi) << 32) -+ + fd->simple.addr_lo); -+} -+ -+/** -+ * dpaa2_fd_set_addr() - Set the addr field of frame descriptor -+ * @fd: the given frame descriptor. -+ * @addr: the address needs to be set in frame descriptor. -+ */ -+static inline void dpaa2_fd_set_addr(struct dpaa2_fd *fd, dma_addr_t addr) -+{ -+ fd->simple.addr_hi = upper_32_bits(addr); -+ fd->simple.addr_lo = lower_32_bits(addr); -+} -+ -+/** -+ * dpaa2_fd_get_frc() - Get the frame context in the frame descriptor -+ * @fd: the given frame descriptor. -+ * -+ * Return the frame context field in the frame descriptor. -+ */ -+static inline u32 dpaa2_fd_get_frc(const struct dpaa2_fd *fd) -+{ -+ return fd->simple.frc; -+} -+ -+/** -+ * dpaa2_fd_set_frc() - Set the frame context in the frame descriptor -+ * @fd: the given frame descriptor. -+ * @frc: the frame context needs to be set in frame descriptor. -+ */ -+static inline void dpaa2_fd_set_frc(struct dpaa2_fd *fd, u32 frc) -+{ -+ fd->simple.frc = frc; -+} -+ -+/** -+ * dpaa2_fd_get_flc() - Get the flow context in the frame descriptor -+ * @fd: the given frame descriptor. -+ * -+ * Return the flow context in the frame descriptor. -+ */ -+static inline dma_addr_t dpaa2_fd_get_flc(const struct dpaa2_fd *fd) -+{ -+ return (dma_addr_t)((((uint64_t)fd->simple.flc_hi) << 32) + -+ fd->simple.flc_lo); -+} -+ -+/** -+ * dpaa2_fd_set_flc() - Set the flow context field of frame descriptor -+ * @fd: the given frame descriptor. -+ * @flc_addr: the flow context needs to be set in frame descriptor. -+ */ -+static inline void dpaa2_fd_set_flc(struct dpaa2_fd *fd, dma_addr_t flc_addr) -+{ -+ fd->simple.flc_hi = upper_32_bits(flc_addr); -+ fd->simple.flc_lo = lower_32_bits(flc_addr); -+} -+ -+/** -+ * dpaa2_fd_get_len() - Get the length in the frame descriptor -+ * @fd: the given frame descriptor. -+ * -+ * Return the length field in the frame descriptor. -+ */ -+static inline u32 dpaa2_fd_get_len(const struct dpaa2_fd *fd) -+{ -+ return fd->simple.len; -+} -+ -+/** -+ * dpaa2_fd_set_len() - Set the length field of frame descriptor -+ * @fd: the given frame descriptor. -+ * @len: the length needs to be set in frame descriptor. -+ */ -+static inline void dpaa2_fd_set_len(struct dpaa2_fd *fd, u32 len) -+{ -+ fd->simple.len = len; -+} -+ -+/** -+ * dpaa2_fd_get_offset() - Get the offset field in the frame descriptor -+ * @fd: the given frame descriptor. -+ * -+ * Return the offset. -+ */ -+static inline uint16_t dpaa2_fd_get_offset(const struct dpaa2_fd *fd) -+{ -+ return (uint16_t)(fd->simple.bpid_offset >> 16) & 0x0FFF; -+} -+ -+/** -+ * dpaa2_fd_set_offset() - Set the offset field of frame descriptor -+ * -+ * @fd: the given frame descriptor. -+ * @offset: the offset needs to be set in frame descriptor. -+ */ -+static inline void dpaa2_fd_set_offset(struct dpaa2_fd *fd, uint16_t offset) -+{ -+ fd->simple.bpid_offset &= 0xF000FFFF; -+ fd->simple.bpid_offset |= (u32)offset << 16; -+} -+ -+/** -+ * dpaa2_fd_get_format() - Get the format field in the frame descriptor -+ * @fd: the given frame descriptor. -+ * -+ * Return the format. -+ */ -+static inline enum dpaa2_fd_format dpaa2_fd_get_format( -+ const struct dpaa2_fd *fd) -+{ -+ return (enum dpaa2_fd_format)((fd->simple.bpid_offset >> 28) & 0x3); -+} -+ -+/** -+ * dpaa2_fd_set_format() - Set the format field of frame descriptor -+ * -+ * @fd: the given frame descriptor. -+ * @format: the format needs to be set in frame descriptor. -+ */ -+static inline void dpaa2_fd_set_format(struct dpaa2_fd *fd, -+ enum dpaa2_fd_format format) -+{ -+ fd->simple.bpid_offset &= 0xCFFFFFFF; -+ fd->simple.bpid_offset |= (u32)format << 28; -+} -+ -+/** -+ * dpaa2_fd_get_bpid() - Get the bpid field in the frame descriptor -+ * @fd: the given frame descriptor. -+ * -+ * Return the bpid. -+ */ -+static inline uint16_t dpaa2_fd_get_bpid(const struct dpaa2_fd *fd) -+{ -+ return (uint16_t)(fd->simple.bpid_offset & 0xFFFF); -+} -+ -+/** -+ * dpaa2_fd_set_bpid() - Set the bpid field of frame descriptor -+ * -+ * @fd: the given frame descriptor. -+ * @bpid: the bpid needs to be set in frame descriptor. -+ */ -+static inline void dpaa2_fd_set_bpid(struct dpaa2_fd *fd, uint16_t bpid) -+{ -+ fd->simple.bpid_offset &= 0xFFFF0000; -+ fd->simple.bpid_offset |= (u32)bpid; -+} -+ -+/** -+ * struct dpaa2_sg_entry - the scatter-gathering structure -+ * @addr_lo: the lower 32bit of address -+ * @addr_hi: the upper 32bit of address -+ * @len: the length in this sg entry. -+ * @bpid_offset: offset in the MS 16 bits, BPID in the LS 16 bits. -+ */ -+struct dpaa2_sg_entry { -+ u32 addr_lo; -+ u32 addr_hi; -+ u32 len; -+ u32 bpid_offset; -+}; -+ -+enum dpaa2_sg_format { -+ dpaa2_sg_single = 0, -+ dpaa2_sg_frame_data, -+ dpaa2_sg_sgt_ext -+}; -+ -+/** -+ * dpaa2_sg_get_addr() - Get the address from SG entry -+ * @sg: the given scatter-gathering object. -+ * -+ * Return the address. -+ */ -+static inline dma_addr_t dpaa2_sg_get_addr(const struct dpaa2_sg_entry *sg) -+{ -+ return (dma_addr_t)((((u64)sg->addr_hi) << 32) + sg->addr_lo); -+} -+ -+/** -+ * dpaa2_sg_set_addr() - Set the address in SG entry -+ * @sg: the given scatter-gathering object. -+ * @addr: the address to be set. -+ */ -+static inline void dpaa2_sg_set_addr(struct dpaa2_sg_entry *sg, dma_addr_t addr) -+{ -+ sg->addr_hi = upper_32_bits(addr); -+ sg->addr_lo = lower_32_bits(addr); -+} -+ -+ -+static inline bool dpaa2_sg_short_len(const struct dpaa2_sg_entry *sg) -+{ -+ return (sg->bpid_offset >> 30) & 0x1; -+} -+ -+/** -+ * dpaa2_sg_get_len() - Get the length in SG entry -+ * @sg: the given scatter-gathering object. -+ * -+ * Return the length. -+ */ -+static inline u32 dpaa2_sg_get_len(const struct dpaa2_sg_entry *sg) -+{ -+ if (dpaa2_sg_short_len(sg)) -+ return sg->len & 0x1FFFF; -+ return sg->len; -+} -+ -+/** -+ * dpaa2_sg_set_len() - Set the length in SG entry -+ * @sg: the given scatter-gathering object. -+ * @len: the length to be set. -+ */ -+static inline void dpaa2_sg_set_len(struct dpaa2_sg_entry *sg, u32 len) -+{ -+ sg->len = len; -+} -+ -+/** -+ * dpaa2_sg_get_offset() - Get the offset in SG entry -+ * @sg: the given scatter-gathering object. -+ * -+ * Return the offset. -+ */ -+static inline u16 dpaa2_sg_get_offset(const struct dpaa2_sg_entry *sg) -+{ -+ return (u16)(sg->bpid_offset >> 16) & 0x0FFF; -+} -+ -+/** -+ * dpaa2_sg_set_offset() - Set the offset in SG entry -+ * @sg: the given scatter-gathering object. -+ * @offset: the offset to be set. -+ */ -+static inline void dpaa2_sg_set_offset(struct dpaa2_sg_entry *sg, -+ u16 offset) -+{ -+ sg->bpid_offset &= 0xF000FFFF; -+ sg->bpid_offset |= (u32)offset << 16; -+} -+ -+/** -+ * dpaa2_sg_get_format() - Get the SG format in SG entry -+ * @sg: the given scatter-gathering object. -+ * -+ * Return the format. -+ */ -+static inline enum dpaa2_sg_format -+ dpaa2_sg_get_format(const struct dpaa2_sg_entry *sg) -+{ -+ return (enum dpaa2_sg_format)((sg->bpid_offset >> 28) & 0x3); -+} -+ -+/** -+ * dpaa2_sg_set_format() - Set the SG format in SG entry -+ * @sg: the given scatter-gathering object. -+ * @format: the format to be set. -+ */ -+static inline void dpaa2_sg_set_format(struct dpaa2_sg_entry *sg, -+ enum dpaa2_sg_format format) -+{ -+ sg->bpid_offset &= 0xCFFFFFFF; -+ sg->bpid_offset |= (u32)format << 28; -+} -+ -+/** -+ * dpaa2_sg_get_bpid() - Get the buffer pool id in SG entry -+ * @sg: the given scatter-gathering object. -+ * -+ * Return the bpid. -+ */ -+static inline u16 dpaa2_sg_get_bpid(const struct dpaa2_sg_entry *sg) -+{ -+ return (u16)(sg->bpid_offset & 0x3FFF); -+} -+ -+/** -+ * dpaa2_sg_set_bpid() - Set the buffer pool id in SG entry -+ * @sg: the given scatter-gathering object. -+ * @bpid: the bpid to be set. -+ */ -+static inline void dpaa2_sg_set_bpid(struct dpaa2_sg_entry *sg, u16 bpid) -+{ -+ sg->bpid_offset &= 0xFFFFC000; -+ sg->bpid_offset |= (u32)bpid; -+} -+ -+/** -+ * dpaa2_sg_is_final() - Check final bit in SG entry -+ * @sg: the given scatter-gathering object. -+ * -+ * Return bool. -+ */ -+static inline bool dpaa2_sg_is_final(const struct dpaa2_sg_entry *sg) -+{ -+ return !!(sg->bpid_offset >> 31); -+} -+ -+/** -+ * dpaa2_sg_set_final() - Set the final bit in SG entry -+ * @sg: the given scatter-gathering object. -+ * @final: the final boolean to be set. -+ */ -+static inline void dpaa2_sg_set_final(struct dpaa2_sg_entry *sg, bool final) -+{ -+ sg->bpid_offset &= 0x7FFFFFFF; -+ sg->bpid_offset |= (u32)final << 31; -+} -+ -+/* Endianness conversion helper functions -+ * The accelerator drivers which construct / read scatter gather entries -+ * need to call these in order to account for endianness mismatches between -+ * hardware and cpu -+ */ -+#ifdef __BIG_ENDIAN -+/** -+ * dpaa2_sg_cpu_to_le() - convert scatter gather entry from native cpu -+ * format little endian format. -+ * @sg: the given scatter gather entry. -+ */ -+static inline void dpaa2_sg_cpu_to_le(struct dpaa2_sg_entry *sg) -+{ -+ uint32_t *p = (uint32_t *)sg; -+ int i; -+ -+ for (i = 0; i < sizeof(*sg) / sizeof(u32); i++) -+ cpu_to_le32s(p++); -+} -+ -+/** -+ * dpaa2_sg_le_to_cpu() - convert scatter gather entry from little endian -+ * format to native cpu format. -+ * @sg: the given scatter gather entry. -+ */ -+static inline void dpaa2_sg_le_to_cpu(struct dpaa2_sg_entry *sg) -+{ -+ uint32_t *p = (uint32_t *)sg; -+ int i; -+ -+ for (i = 0; i < sizeof(*sg) / sizeof(u32); i++) -+ le32_to_cpus(p++); -+} -+#else -+#define dpaa2_sg_cpu_to_le(sg) -+#define dpaa2_sg_le_to_cpu(sg) -+#endif /* __BIG_ENDIAN */ -+ -+ -+/** -+ * struct dpaa2_fl_entry - structure for frame list entry. -+ * @addr_lo: the lower 32bit of address -+ * @addr_hi: the upper 32bit of address -+ * @len: the length in this sg entry. -+ * @bpid_offset: offset in the MS 16 bits, BPID in the LS 16 bits. -+ * @frc: frame context -+ * @ctrl: the 32bit control bits including dd, sc,... va, err. -+ * @flc_lo: the lower 32bit of flow context. -+ * @flc_hi: the upper 32bits of flow context. -+ * -+ * Frame List Entry (FLE) -+ * Identical to dpaa2_fd.simple layout, but some bits are different -+ */ -+struct dpaa2_fl_entry { -+ u32 addr_lo; -+ u32 addr_hi; -+ u32 len; -+ u32 bpid_offset; -+ u32 frc; -+ u32 ctrl; -+ u32 flc_lo; -+ u32 flc_hi; -+}; -+ -+enum dpaa2_fl_format { -+ dpaa2_fl_single = 0, -+ dpaa2_fl_res, -+ dpaa2_fl_sg -+}; -+ -+/** -+ * dpaa2_fl_get_addr() - Get address in the frame list entry -+ * @fle: the given frame list entry. -+ * -+ * Return address for the get function. -+ */ -+static inline dma_addr_t dpaa2_fl_get_addr(const struct dpaa2_fl_entry *fle) -+{ -+ return (dma_addr_t)((((uint64_t)fle->addr_hi) << 32) + fle->addr_lo); -+} -+ -+/** -+ * dpaa2_fl_set_addr() - Set the address in the frame list entry -+ * @fle: the given frame list entry. -+ * @addr: the address needs to be set. -+ * -+ */ -+static inline void dpaa2_fl_set_addr(struct dpaa2_fl_entry *fle, -+ dma_addr_t addr) -+{ -+ fle->addr_hi = upper_32_bits(addr); -+ fle->addr_lo = lower_32_bits(addr); -+} -+ -+/** -+ * dpaa2_fl_get_flc() - Get the flow context in the frame list entry -+ * @fle: the given frame list entry. -+ * -+ * Return flow context for the get function. -+ */ -+static inline dma_addr_t dpaa2_fl_get_flc(const struct dpaa2_fl_entry *fle) -+{ -+ return (dma_addr_t)((((uint64_t)fle->flc_hi) << 32) + fle->flc_lo); -+} -+ -+/** -+ * dpaa2_fl_set_flc() - Set the flow context in the frame list entry -+ * @fle: the given frame list entry. -+ * @flc_addr: the flow context address needs to be set. -+ * -+ */ -+static inline void dpaa2_fl_set_flc(struct dpaa2_fl_entry *fle, -+ dma_addr_t flc_addr) -+{ -+ fle->flc_hi = upper_32_bits(flc_addr); -+ fle->flc_lo = lower_32_bits(flc_addr); -+} -+ -+/** -+ * dpaa2_fl_get_len() - Get the length in the frame list entry -+ * @fle: the given frame list entry. -+ * -+ * Return length for the get function. -+ */ -+static inline u32 dpaa2_fl_get_len(const struct dpaa2_fl_entry *fle) -+{ -+ return fle->len; -+} -+ -+/** -+ * dpaa2_fl_set_len() - Set the length in the frame list entry -+ * @fle: the given frame list entry. -+ * @len: the length needs to be set. -+ * -+ */ -+static inline void dpaa2_fl_set_len(struct dpaa2_fl_entry *fle, u32 len) -+{ -+ fle->len = len; -+} -+ -+/** -+ * dpaa2_fl_get_offset() - Get/Set the offset in the frame list entry -+ * @fle: the given frame list entry. -+ * -+ * Return offset for the get function. -+ */ -+static inline uint16_t dpaa2_fl_get_offset(const struct dpaa2_fl_entry *fle) -+{ -+ return (uint16_t)(fle->bpid_offset >> 16) & 0x0FFF; -+} -+ -+/** -+ * dpaa2_fl_set_offset() - Set the offset in the frame list entry -+ * @fle: the given frame list entry. -+ * @offset: the offset needs to be set. -+ * -+ */ -+static inline void dpaa2_fl_set_offset(struct dpaa2_fl_entry *fle, -+ uint16_t offset) -+{ -+ fle->bpid_offset &= 0xF000FFFF; -+ fle->bpid_offset |= (u32)(offset & 0x0FFF) << 16; -+} -+ -+/** -+ * dpaa2_fl_get_format() - Get the format in the frame list entry -+ * @fle: the given frame list entry. -+ * -+ * Return frame list format for the get function. -+ */ -+static inline enum dpaa2_fl_format dpaa2_fl_get_format( -+ const struct dpaa2_fl_entry *fle) -+{ -+ return (enum dpaa2_fl_format)((fle->bpid_offset >> 28) & 0x3); -+} -+ -+/** -+ * dpaa2_fl_set_format() - Set the format in the frame list entry -+ * @fle: the given frame list entry. -+ * @format: the frame list format needs to be set. -+ * -+ */ -+static inline void dpaa2_fl_set_format(struct dpaa2_fl_entry *fle, -+ enum dpaa2_fl_format format) -+{ -+ fle->bpid_offset &= 0xCFFFFFFF; -+ fle->bpid_offset |= (u32)(format & 0x3) << 28; -+} -+ -+/** -+ * dpaa2_fl_get_bpid() - Get the buffer pool id in the frame list entry -+ * @fle: the given frame list entry. -+ * -+ * Return bpid for the get function. -+ */ -+static inline uint16_t dpaa2_fl_get_bpid(const struct dpaa2_fl_entry *fle) -+{ -+ return (uint16_t)(fle->bpid_offset & 0x3FFF); -+} -+ -+/** -+ * dpaa2_fl_set_bpid() - Set the buffer pool id in the frame list entry -+ * @fle: the given frame list entry. -+ * @bpid: the buffer pool id needs to be set. -+ * -+ */ -+static inline void dpaa2_fl_set_bpid(struct dpaa2_fl_entry *fle, uint16_t bpid) -+{ -+ fle->bpid_offset &= 0xFFFFC000; -+ fle->bpid_offset |= (u32)bpid; -+} -+ -+/** dpaa2_fl_is_final() - check the final bit is set or not in the frame list. -+ * @fle: the given frame list entry. -+ * -+ * Return final bit settting. -+ */ -+static inline bool dpaa2_fl_is_final(const struct dpaa2_fl_entry *fle) -+{ -+ return !!(fle->bpid_offset >> 31); -+} -+ -+/** -+ * dpaa2_fl_set_final() - Set the final bit in the frame list entry -+ * @fle: the given frame list entry. -+ * @final: the final bit needs to be set. -+ * -+ */ -+static inline void dpaa2_fl_set_final(struct dpaa2_fl_entry *fle, bool final) -+{ -+ fle->bpid_offset &= 0x7FFFFFFF; -+ fle->bpid_offset |= (u32)final << 31; -+} -+ -+/** -+ * struct dpaa2_dq - the qman result structure -+ * @dont_manipulate_directly: the 16 32bit data to represent the whole -+ * possible qman dequeue result. -+ * -+ * When frames are dequeued, the FDs show up inside "dequeue" result structures -+ * (if at all, not all dequeue results contain valid FDs). This structure type -+ * is intentionally defined without internal detail, and the only reason it -+ * isn't declared opaquely (without size) is to allow the user to provide -+ * suitably-sized (and aligned) memory for these entries. -+ */ -+struct dpaa2_dq { -+ uint32_t dont_manipulate_directly[16]; -+}; -+ -+/* Parsing frame dequeue results */ -+/* FQ empty */ -+#define DPAA2_DQ_STAT_FQEMPTY 0x80 -+/* FQ held active */ -+#define DPAA2_DQ_STAT_HELDACTIVE 0x40 -+/* FQ force eligible */ -+#define DPAA2_DQ_STAT_FORCEELIGIBLE 0x20 -+/* Valid frame */ -+#define DPAA2_DQ_STAT_VALIDFRAME 0x10 -+/* FQ ODP enable */ -+#define DPAA2_DQ_STAT_ODPVALID 0x04 -+/* Volatile dequeue */ -+#define DPAA2_DQ_STAT_VOLATILE 0x02 -+/* volatile dequeue command is expired */ -+#define DPAA2_DQ_STAT_EXPIRED 0x01 -+ -+/** -+ * dpaa2_dq_flags() - Get the stat field of dequeue response -+ * @dq: the dequeue result. -+ */ -+uint32_t dpaa2_dq_flags(const struct dpaa2_dq *dq); -+ -+/** -+ * dpaa2_dq_is_pull() - Check whether the dq response is from a pull -+ * command. -+ * @dq: the dequeue result. -+ * -+ * Return 1 for volatile(pull) dequeue, 0 for static dequeue. -+ */ -+static inline int dpaa2_dq_is_pull(const struct dpaa2_dq *dq) -+{ -+ return (int)(dpaa2_dq_flags(dq) & DPAA2_DQ_STAT_VOLATILE); -+} -+ -+/** -+ * dpaa2_dq_is_pull_complete() - Check whether the pull command is completed. -+ * @dq: the dequeue result. -+ * -+ * Return boolean. -+ */ -+static inline int dpaa2_dq_is_pull_complete( -+ const struct dpaa2_dq *dq) -+{ -+ return (int)(dpaa2_dq_flags(dq) & DPAA2_DQ_STAT_EXPIRED); -+} -+ -+/** -+ * dpaa2_dq_seqnum() - Get the seqnum field in dequeue response -+ * seqnum is valid only if VALIDFRAME flag is TRUE -+ * @dq: the dequeue result. -+ * -+ * Return seqnum. -+ */ -+uint16_t dpaa2_dq_seqnum(const struct dpaa2_dq *dq); -+ -+/** -+ * dpaa2_dq_odpid() - Get the seqnum field in dequeue response -+ * odpid is valid only if ODPVAILD flag is TRUE. -+ * @dq: the dequeue result. -+ * -+ * Return odpid. -+ */ -+uint16_t dpaa2_dq_odpid(const struct dpaa2_dq *dq); -+ -+/** -+ * dpaa2_dq_fqid() - Get the fqid in dequeue response -+ * @dq: the dequeue result. -+ * -+ * Return fqid. -+ */ -+uint32_t dpaa2_dq_fqid(const struct dpaa2_dq *dq); -+ -+/** -+ * dpaa2_dq_byte_count() - Get the byte count in dequeue response -+ * @dq: the dequeue result. -+ * -+ * Return the byte count remaining in the FQ. -+ */ -+uint32_t dpaa2_dq_byte_count(const struct dpaa2_dq *dq); -+ -+/** -+ * dpaa2_dq_frame_count() - Get the frame count in dequeue response -+ * @dq: the dequeue result. -+ * -+ * Return the frame count remaining in the FQ. -+ */ -+uint32_t dpaa2_dq_frame_count(const struct dpaa2_dq *dq); -+ -+/** -+ * dpaa2_dq_fd_ctx() - Get the frame queue context in dequeue response -+ * @dq: the dequeue result. -+ * -+ * Return the frame queue context. -+ */ -+uint64_t dpaa2_dq_fqd_ctx(const struct dpaa2_dq *dq); -+ -+/** -+ * dpaa2_dq_fd() - Get the frame descriptor in dequeue response -+ * @dq: the dequeue result. -+ * -+ * Return the frame descriptor. -+ */ -+const struct dpaa2_fd *dpaa2_dq_fd(const struct dpaa2_dq *dq); -+ -+#endif /* __FSL_DPAA2_FD_H */ -diff --git a/drivers/staging/fsl-mc/include/fsl_dpaa2_io.h b/drivers/staging/fsl-mc/include/fsl_dpaa2_io.h -new file mode 100644 -index 0000000..6ea2ff9 ---- /dev/null -+++ b/drivers/staging/fsl-mc/include/fsl_dpaa2_io.h -@@ -0,0 +1,619 @@ -+/* Copyright 2014 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 __FSL_DPAA2_IO_H -+#define __FSL_DPAA2_IO_H -+ -+#include "fsl_dpaa2_fd.h" -+ -+struct dpaa2_io; -+struct dpaa2_io_store; -+ -+/** -+ * DOC: DPIO Service Management -+ * -+ * The DPIO service provides APIs for users to interact with the datapath -+ * by enqueueing and dequeing frame descriptors. -+ * -+ * The following set of APIs can be used to enqueue and dequeue frames -+ * as well as producing notification callbacks when data is available -+ * for dequeue. -+ */ -+ -+/** -+ * struct dpaa2_io_desc - The DPIO descriptor. -+ * @receives_notifications: Use notificaton mode. -+ * @has_irq: use irq-based proessing. -+ * @will_poll: use poll processing. -+ * @has_8prio: set for channel with 8 priority WQs. -+ * @cpu: the cpu index that at least interrupt handlers will execute on. -+ * @stash_affinity: the stash affinity for this portal favour 'cpu' -+ * @regs_cena: the cache enabled regs. -+ * @regs_cinh: the cache inhibited regs. -+ * @dpio_id: The dpio index. -+ * @qman_version: the qman version -+ * -+ * Describe the attributes and features of the DPIO object. -+ */ -+struct dpaa2_io_desc { -+ /* non-zero iff the DPIO has a channel */ -+ int receives_notifications; -+ /* non-zero if the DPIO portal interrupt is handled. If so, the -+ * caller/OS handles the interrupt and calls dpaa2_io_service_irq(). */ -+ int has_irq; -+ /* non-zero if the caller/OS is prepared to called the -+ * dpaa2_io_service_poll() routine as part of its run-to-completion (or -+ * scheduling) loop. If so, the DPIO service may dynamically switch some -+ * of its processing between polling-based and irq-based. It is illegal -+ * combination to have (!has_irq && !will_poll). */ -+ int will_poll; -+ /* ignored unless 'receives_notifications'. Non-zero iff the channel has -+ * 8 priority WQs, otherwise the channel has 2. */ -+ int has_8prio; -+ /* the cpu index that at least interrupt handlers will execute on. And -+ * if 'stash_affinity' is non-zero, the cache targeted by stash -+ * transactions is affine to this cpu. */ -+ int cpu; -+ /* non-zero if stash transactions for this portal favour 'cpu' over -+ * other CPUs. (Eg. zero if there's no stashing, or stashing is to -+ * shared cache.) */ -+ int stash_affinity; -+ /* Caller-provided flags, determined by bus-scanning and/or creation of -+ * DPIO objects via MC commands. */ -+ void *regs_cena; -+ void *regs_cinh; -+ int dpio_id; -+ uint32_t qman_version; -+}; -+ -+/** -+ * dpaa2_io_create() - create a dpaa2_io object. -+ * @desc: the dpaa2_io descriptor -+ * -+ * Activates a "struct dpaa2_io" corresponding to the given config of an actual -+ * DPIO object. This handle can be used on it's own (like a one-portal "DPIO -+ * service") or later be added to a service-type "struct dpaa2_io" object. Note, -+ * the information required on 'cfg' is copied so the caller is free to do as -+ * they wish with the input parameter upon return. -+ * -+ * Return a valid dpaa2_io object for success, or NULL for failure. -+ */ -+struct dpaa2_io *dpaa2_io_create(const struct dpaa2_io_desc *desc); -+ -+/** -+ * dpaa2_io_create_service() - Create an (initially empty) DPIO service. -+ * -+ * Return a valid dpaa2_io object for success, or NULL for failure. -+ */ -+struct dpaa2_io *dpaa2_io_create_service(void); -+ -+/** -+ * dpaa2_io_default_service() - Use the driver's own global (and initially -+ * empty) DPIO service. -+ * -+ * This increments the reference count, so don't forget to use dpaa2_io_down() -+ * for each time this function is called. -+ * -+ * Return a valid dpaa2_io object for success, or NULL for failure. -+ */ -+struct dpaa2_io *dpaa2_io_default_service(void); -+ -+/** -+ * dpaa2_io_down() - release the dpaa2_io object. -+ * @d: the dpaa2_io object to be released. -+ * -+ * The "struct dpaa2_io" type can represent an individual DPIO object (as -+ * described by "struct dpaa2_io_desc") or an instance of a "DPIO service", -+ * which can be used to group/encapsulate multiple DPIO objects. In all cases, -+ * each handle obtained should be released using this function. -+ */ -+void dpaa2_io_down(struct dpaa2_io *d); -+ -+/** -+ * dpaa2_io_service_add() - Add the given DPIO object to the given DPIO service. -+ * @service: the given DPIO service. -+ * @obj: the given DPIO object. -+ * -+ * 'service' must have been created by dpaa2_io_create_service() and 'obj' -+ * must have been created by dpaa2_io_create(). This increments the reference -+ * count on the object that 'obj' refers to, so the user could call -+ * dpaa2_io_down(obj) after this and the object will persist within the service -+ * (and will be destroyed when the service is destroyed). -+ * -+ * Return 0 for success, or -EINVAL for failure. -+ */ -+int dpaa2_io_service_add(struct dpaa2_io *service, struct dpaa2_io *obj); -+ -+/** -+ * dpaa2_io_get_descriptor() - Get the DPIO descriptor of the given DPIO object. -+ * @obj: the given DPIO object. -+ * @desc: the returned DPIO descriptor. -+ * -+ * This function will return failure if the given dpaa2_io struct represents a -+ * service rather than an individual DPIO object, otherwise it returns zero and -+ * the given 'cfg' structure is filled in. -+ * -+ * Return 0 for success, or -EINVAL for failure. -+ */ -+int dpaa2_io_get_descriptor(struct dpaa2_io *obj, struct dpaa2_io_desc *desc); -+ -+/** -+ * dpaa2_io_poll() - Process any notifications and h/w-initiated events that -+ * are polling-driven. -+ * @obj: the given DPIO object. -+ * -+ * Obligatory for DPIO objects that have dpaa2_io_desc::will_poll non-zero. -+ * -+ * Return 0 for success, or -EINVAL for failure. -+ */ -+int dpaa2_io_poll(struct dpaa2_io *obj); -+ -+/** -+ * dpaa2_io_irq() - Process any notifications and h/w-initiated events that are -+ * irq-driven. -+ * @obj: the given DPIO object. -+ * -+ * Obligatory for DPIO objects that have dpaa2_io_desc::has_irq non-zero. -+ * -+ * Return IRQ_HANDLED for success, or -EINVAL for failure. -+ */ -+int dpaa2_io_irq(struct dpaa2_io *obj); -+ -+/** -+ * dpaa2_io_pause_poll() - Used to stop polling. -+ * @obj: the given DPIO object. -+ * -+ * If a polling application is going to stop polling for a period of time and -+ * supports interrupt processing, it can call this function to convert all -+ * processing to IRQ. (Eg. when sleeping.) -+ * -+ * Return -EINVAL. -+ */ -+int dpaa2_io_pause_poll(struct dpaa2_io *obj); -+ -+/** -+ * dpaa2_io_resume_poll() - Resume polling -+ * @obj: the given DPIO object. -+ * -+ * Return -EINVAL. -+ */ -+int dpaa2_io_resume_poll(struct dpaa2_io *obj); -+ -+/** -+ * dpaa2_io_service_notifications() - Get a mask of cpus that the DPIO service -+ * can receive notifications on. -+ * @s: the given DPIO object. -+ * @mask: the mask of cpus. -+ * -+ * Note that this is a run-time snapshot. If things like cpu-hotplug are -+ * supported in the target system, then an attempt to register notifications -+ * for a cpu that appears present in the given mask might fail if that cpu has -+ * gone offline in the mean time. -+ */ -+void dpaa2_io_service_notifications(struct dpaa2_io *s, cpumask_t *mask); -+ -+/** -+ * dpaa2_io_service_stashing - Get a mask of cpus that the DPIO service has stash -+ * affinity to. -+ * @s: the given DPIO object. -+ * @mask: the mask of cpus. -+ */ -+void dpaa2_io_service_stashing(struct dpaa2_io *s, cpumask_t *mask); -+ -+/** -+ * dpaa2_io_service_nonaffine() - Check the DPIO service's cpu affinity -+ * for stashing. -+ * @s: the given DPIO object. -+ * -+ * Return a boolean, whether or not the DPIO service has resources that have no -+ * particular cpu affinity for stashing. (Useful to know if you wish to operate -+ * on CPUs that the service has no affinity to, you would choose to use -+ * resources that are neutral, rather than affine to a different CPU.) Unlike -+ * other service-specific APIs, this one doesn't return an error if it is passed -+ * a non-service object. So don't do it. -+ */ -+int dpaa2_io_service_has_nonaffine(struct dpaa2_io *s); -+ -+/*************************/ -+/* Notification handling */ -+/*************************/ -+ -+/** -+ * struct dpaa2_io_notification_ctx - The DPIO notification context structure. -+ * @cb: the callback to be invoked when the notification arrives. -+ * @is_cdan: Zero/FALSE for FQDAN, non-zero/TRUE for CDAN. -+ * @id: FQID or channel ID, needed for rearm. -+ * @desired_cpu: the cpu on which the notifications will show up. -+ * @actual_cpu: the cpu the notification actually shows up. -+ * @migration_cb: callback function used for migration. -+ * @dpio_id: the dpio index. -+ * @qman64: the 64-bit context value shows up in the FQDAN/CDAN. -+ * @node: the list node. -+ * @dpio_private: the dpio object internal to dpio_service. -+ * -+ * When a FQDAN/CDAN registration is made (eg. by DPNI/DPCON/DPAI code), a -+ * context of the following type is used. The caller can embed it within a -+ * larger structure in order to add state that is tracked along with the -+ * notification (this may be useful when callbacks are invoked that pass this -+ * notification context as a parameter). -+ */ -+struct dpaa2_io_notification_ctx { -+ void (*cb)(struct dpaa2_io_notification_ctx *); -+ int is_cdan; -+ uint32_t id; -+ /* This specifies which cpu the user wants notifications to show up on -+ * (ie. to execute 'cb'). If notification-handling on that cpu is not -+ * available at the time of notification registration, the registration -+ * will fail. */ -+ int desired_cpu; -+ /* If the target platform supports cpu-hotplug or other features -+ * (related to power-management, one would expect) that can migrate IRQ -+ * handling of a given DPIO object, then this value will potentially be -+ * different to 'desired_cpu' at run-time. */ -+ int actual_cpu; -+ /* And if migration does occur and this callback is non-NULL, it will -+ * be invoked prior to any futher notification callbacks executing on -+ * 'newcpu'. Note that 'oldcpu' is what 'actual_cpu' was prior to the -+ * migration, and 'newcpu' is what it is now. Both could conceivably be -+ * different to 'desired_cpu'. */ -+ void (*migration_cb)(struct dpaa2_io_notification_ctx *, -+ int oldcpu, int newcpu); -+ /* These are returned from dpaa2_io_service_register(). -+ * 'dpio_id' is the dpaa2_io_desc::dpio_id value of the DPIO object that -+ * has been selected by the service for receiving the notifications. The -+ * caller can use this value in the MC command that attaches the FQ (or -+ * channel) of their DPNI (or DPCON, respectively) to this DPIO for -+ * notification-generation. -+ * 'qman64' is the 64-bit context value that needs to be sent in the -+ * same MC command in order to be programmed into the FQ or channel - -+ * this is the 64-bit value that shows up in the FQDAN/CDAN messages to -+ * the DPIO object, and the DPIO service specifies this value back to -+ * the caller so that the notifications that show up will be -+ * comprensible/demux-able to the DPIO service. */ -+ int dpio_id; -+ uint64_t qman64; -+ /* These fields are internal to the DPIO service once the context is -+ * registered. TBD: may require more internal state fields. */ -+ struct list_head node; -+ void *dpio_private; -+}; -+ -+/** -+ * dpaa2_io_service_register() - Prepare for servicing of FQDAN or CDAN -+ * notifications on the given DPIO service. -+ * @service: the given DPIO service. -+ * @ctx: the notification context. -+ * -+ * The MC command to attach the caller's DPNI/DPCON/DPAI device to a -+ * DPIO object is performed after this function is called. In that way, (a) the -+ * DPIO service is "ready" to handle a notification arrival (which might happen -+ * before the "attach" command to MC has returned control of execution back to -+ * the caller), and (b) the DPIO service can provide back to the caller the -+ * 'dpio_id' and 'qman64' parameters that it should pass along in the MC command -+ * in order for the DPNI/DPCON/DPAI resources to be configured to produce the -+ * right notification fields to the DPIO service. -+ * -+ * Return 0 for success, or -ENODEV for failure. -+ */ -+int dpaa2_io_service_register(struct dpaa2_io *service, -+ struct dpaa2_io_notification_ctx *ctx); -+ -+/** -+ * dpaa2_io_service_deregister - The opposite of 'register'. -+ * @service: the given DPIO service. -+ * @ctx: the notification context. -+ * -+ * Note that 'register' should be called *before* -+ * making the MC call to attach the notification-producing device to the -+ * notification-handling DPIO service, the 'unregister' function should be -+ * called *after* making the MC call to detach the notification-producing -+ * device. -+ * -+ * Return 0 for success. -+ */ -+int dpaa2_io_service_deregister(struct dpaa2_io *service, -+ struct dpaa2_io_notification_ctx *ctx); -+ -+/** -+ * dpaa2_io_service_rearm() - Rearm the notification for the given DPIO service. -+ * @service: the given DPIO service. -+ * @ctx: the notification context. -+ * -+ * Once a FQDAN/CDAN has been produced, the corresponding FQ/channel is -+ * considered "disarmed". Ie. the user can issue pull dequeue operations on that -+ * traffic source for as long as it likes. Eventually it may wish to "rearm" -+ * that source to allow it to produce another FQDAN/CDAN, that's what this -+ * function achieves. -+ * -+ * Return 0 for success, or -ENODEV if no service available, -EBUSY/-EIO for not -+ * being able to implement the rearm the notifiaton due to setting CDAN or -+ * scheduling fq. -+ */ -+int dpaa2_io_service_rearm(struct dpaa2_io *service, -+ struct dpaa2_io_notification_ctx *ctx); -+ -+/** -+ * dpaa2_io_from_registration() - Get the DPIO object from the given notification -+ * context. -+ * @ctx: the given notifiation context. -+ * @ret: the returned DPIO object. -+ * -+ * Like 'dpaa2_io_service_get_persistent()' (see below), except that the -+ * returned handle is not selected based on a 'cpu' argument, but is the same -+ * DPIO object that the given notification context is registered against. The -+ * returned handle carries a reference count, so a corresponding dpaa2_io_down() -+ * would be required when the reference is no longer needed. -+ * -+ * Return 0 for success, or -EINVAL for failure. -+ */ -+int dpaa2_io_from_registration(struct dpaa2_io_notification_ctx *ctx, -+ struct dpaa2_io **ret); -+ -+/**********************************/ -+/* General usage of DPIO services */ -+/**********************************/ -+ -+/** -+ * dpaa2_io_service_get_persistent() - Get the DPIO resource from the given -+ * notification context and cpu. -+ * @service: the DPIO service. -+ * @cpu: the cpu that the DPIO resource has stashing affinity to. -+ * @ret: the returned DPIO resource. -+ * -+ * The various DPIO interfaces can accept a "struct dpaa2_io" handle that refers -+ * to an individual DPIO object or to a whole service. In the latter case, an -+ * internal choice is made for each operation. This function supports the former -+ * case, by selecting an individual DPIO object *from* the service in order for -+ * it to be used multiple times to provide "persistence". The returned handle -+ * also carries a reference count, so a corresponding dpaa2_io_down() would be -+ * required when the reference is no longer needed. Note, a parameter of -1 for -+ * 'cpu' will select a DPIO resource that has no particular stashing affinity to -+ * any cpu (eg. one that stashes to platform cache). -+ * -+ * Return 0 for success, or -ENODEV for failure. -+ */ -+int dpaa2_io_service_get_persistent(struct dpaa2_io *service, int cpu, -+ struct dpaa2_io **ret); -+ -+/*****************/ -+/* Pull dequeues */ -+/*****************/ -+ -+/** -+ * dpaa2_io_service_pull_fq() - pull dequeue functions from a fq. -+ * @d: the given DPIO service. -+ * @fqid: the given frame queue id. -+ * @s: the dpaa2_io_store object for the result. -+ * -+ * To support DCA/order-preservation, it will be necessary to support an -+ * alternative form, because they must ultimately dequeue to DQRR rather than a -+ * user-supplied dpaa2_io_store. Furthermore, those dequeue results will -+ * "complete" using a caller-provided callback (from DQRR processing) rather -+ * than the caller explicitly looking at their dpaa2_io_store for results. Eg. -+ * the alternative form will likely take a callback parameter rather than a -+ * store parameter. Ignoring it for now to keep the picture clearer. -+ * -+ * Return 0 for success, or error code for failure. -+ */ -+int dpaa2_io_service_pull_fq(struct dpaa2_io *d, uint32_t fqid, -+ struct dpaa2_io_store *s); -+ -+/** -+ * dpaa2_io_service_pull_channel() - pull dequeue functions from a channel. -+ * @d: the given DPIO service. -+ * @channelid: the given channel id. -+ * @s: the dpaa2_io_store object for the result. -+ * -+ * To support DCA/order-preservation, it will be necessary to support an -+ * alternative form, because they must ultimately dequeue to DQRR rather than a -+ * user-supplied dpaa2_io_store. Furthermore, those dequeue results will -+ * "complete" using a caller-provided callback (from DQRR processing) rather -+ * than the caller explicitly looking at their dpaa2_io_store for results. Eg. -+ * the alternative form will likely take a callback parameter rather than a -+ * store parameter. Ignoring it for now to keep the picture clearer. -+ * -+ * Return 0 for success, or error code for failure. -+ */ -+int dpaa2_io_service_pull_channel(struct dpaa2_io *d, uint32_t channelid, -+ struct dpaa2_io_store *s); -+ -+/************/ -+/* Enqueues */ -+/************/ -+ -+/** -+ * dpaa2_io_service_enqueue_fq() - Enqueue a frame to a frame queue. -+ * @d: the given DPIO service. -+ * @fqid: the given frame queue id. -+ * @fd: the frame descriptor which is enqueued. -+ * -+ * This definition bypasses some features that are not expected to be priority-1 -+ * features, and may not be needed at all via current assumptions (QBMan's -+ * feature set is wider than the MC object model is intendeding to support, -+ * initially at least). Plus, keeping them out (for now) keeps the API view -+ * simpler. Missing features are; -+ * - enqueue confirmation (results DMA'd back to the user) -+ * - ORP -+ * - DCA/order-preservation (see note in "pull dequeues") -+ * - enqueue consumption interrupts -+ * -+ * Return 0 for successful enqueue, or -EBUSY if the enqueue ring is not ready, -+ * or -ENODEV if there is no dpio service. -+ */ -+int dpaa2_io_service_enqueue_fq(struct dpaa2_io *d, -+ uint32_t fqid, -+ const struct dpaa2_fd *fd); -+ -+/** -+ * dpaa2_io_service_enqueue_qd() - Enqueue a frame to a QD. -+ * @d: the given DPIO service. -+ * @qdid: the given queuing destination id. -+ * @prio: the given queuing priority. -+ * @qdbin: the given queuing destination bin. -+ * @fd: the frame descriptor which is enqueued. -+ * -+ * This definition bypasses some features that are not expected to be priority-1 -+ * features, and may not be needed at all via current assumptions (QBMan's -+ * feature set is wider than the MC object model is intendeding to support, -+ * initially at least). Plus, keeping them out (for now) keeps the API view -+ * simpler. Missing features are; -+ * - enqueue confirmation (results DMA'd back to the user) -+ * - ORP -+ * - DCA/order-preservation (see note in "pull dequeues") -+ * - enqueue consumption interrupts -+ * -+ * Return 0 for successful enqueue, or -EBUSY if the enqueue ring is not ready, -+ * or -ENODEV if there is no dpio service. -+ */ -+int dpaa2_io_service_enqueue_qd(struct dpaa2_io *d, -+ uint32_t qdid, uint8_t prio, uint16_t qdbin, -+ const struct dpaa2_fd *fd); -+ -+/*******************/ -+/* Buffer handling */ -+/*******************/ -+ -+/** -+ * dpaa2_io_service_release() - Release buffers to a buffer pool. -+ * @d: the given DPIO object. -+ * @bpid: the buffer pool id. -+ * @buffers: the buffers to be released. -+ * @num_buffers: the number of the buffers to be released. -+ * -+ * Return 0 for success, and negative error code for failure. -+ */ -+int dpaa2_io_service_release(struct dpaa2_io *d, -+ uint32_t bpid, -+ const uint64_t *buffers, -+ unsigned int num_buffers); -+ -+/** -+ * dpaa2_io_service_acquire() - Acquire buffers from a buffer pool. -+ * @d: the given DPIO object. -+ * @bpid: the buffer pool id. -+ * @buffers: the buffer addresses for acquired buffers. -+ * @num_buffers: the expected number of the buffers to acquire. -+ * -+ * Return a negative error code if the command failed, otherwise it returns -+ * the number of buffers acquired, which may be less than the number requested. -+ * Eg. if the buffer pool is empty, this will return zero. -+ */ -+int dpaa2_io_service_acquire(struct dpaa2_io *d, -+ uint32_t bpid, -+ uint64_t *buffers, -+ unsigned int num_buffers); -+ -+/***************/ -+/* DPIO stores */ -+/***************/ -+ -+/* These are reusable memory blocks for retrieving dequeue results into, and to -+ * assist with parsing those results once they show up. They also hide the -+ * details of how to use "tokens" to make detection of DMA results possible (ie. -+ * comparing memory before the DMA and after it) while minimising the needless -+ * clearing/rewriting of those memory locations between uses. -+ */ -+ -+/** -+ * dpaa2_io_store_create() - Create the dma memory storage for dequeue -+ * result. -+ * @max_frames: the maximum number of dequeued result for frames, must be <= 16. -+ * @dev: the device to allow mapping/unmapping the DMAable region. -+ * -+ * Constructor - max_frames must be <= 16. The user provides the -+ * device struct to allow mapping/unmapping of the DMAable region. Area for -+ * storage will be allocated during create. The size of this storage is -+ * "max_frames*sizeof(struct dpaa2_dq)". The 'dpaa2_io_store' returned is a -+ * wrapper structure allocated within the DPIO code, which owns and manages -+ * allocated store. -+ * -+ * Return dpaa2_io_store struct for successfuly created storage memory, or NULL -+ * if not getting the stroage for dequeue result in create API. -+ */ -+struct dpaa2_io_store *dpaa2_io_store_create(unsigned int max_frames, -+ struct device *dev); -+ -+/** -+ * dpaa2_io_store_destroy() - Destroy the dma memory storage for dequeue -+ * result. -+ * @s: the storage memory to be destroyed. -+ * -+ * Frees to specified storage memory. -+ */ -+void dpaa2_io_store_destroy(struct dpaa2_io_store *s); -+ -+/** -+ * dpaa2_io_store_next() - Determine when the next dequeue result is available. -+ * @s: the dpaa2_io_store object. -+ * @is_last: indicate whether this is the last frame in the pull command. -+ * -+ * Once dpaa2_io_store has been passed to a function that performs dequeues to -+ * it, like dpaa2_ni_rx(), this function can be used to determine when the next -+ * frame result is available. Once this function returns non-NULL, a subsequent -+ * call to it will try to find the *next* dequeue result. -+ * -+ * Note that if a pull-dequeue has a null result because the target FQ/channel -+ * was empty, then this function will return NULL rather than expect the caller -+ * to always check for this on his own side. As such, "is_last" can be used to -+ * differentiate between "end-of-empty-dequeue" and "still-waiting". -+ * -+ * Return dequeue result for a valid dequeue result, or NULL for empty dequeue. -+ */ -+struct dpaa2_dq *dpaa2_io_store_next(struct dpaa2_io_store *s, int *is_last); -+ -+#ifdef CONFIG_FSL_QBMAN_DEBUG -+/** -+ * dpaa2_io_query_fq_count() - Get the frame and byte count for a given fq. -+ * @d: the given DPIO object. -+ * @fqid: the id of frame queue to be queried. -+ * @fcnt: the queried frame count. -+ * @bcnt: the queried byte count. -+ * -+ * Knowing the FQ count at run-time can be useful in debugging situations. -+ * The instantaneous frame- and byte-count are hereby returned. -+ * -+ * Return 0 for a successful query, and negative error code if query fails. -+ */ -+int dpaa2_io_query_fq_count(struct dpaa2_io *d, uint32_t fqid, -+ uint32_t *fcnt, uint32_t *bcnt); -+ -+/** -+ * dpaa2_io_query_bp_count() - Query the number of buffers currenty in a -+ * buffer pool. -+ * @d: the given DPIO object. -+ * @bpid: the index of buffer pool to be queried. -+ * @num: the queried number of buffers in the buffer pool. -+ * -+ * Return 0 for a sucessful query, and negative error code if query fails. -+ */ -+int dpaa2_io_query_bp_count(struct dpaa2_io *d, uint32_t bpid, -+ uint32_t *num); -+#endif -+#endif /* __FSL_DPAA2_IO_H */ -diff --git a/drivers/staging/fsl-mc/include/mc-cmd.h b/drivers/staging/fsl-mc/include/mc-cmd.h -new file mode 100644 -index 0000000..00f0b74 ---- /dev/null -+++ b/drivers/staging/fsl-mc/include/mc-cmd.h -@@ -0,0 +1,133 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef __FSL_MC_CMD_H -+#define __FSL_MC_CMD_H -+ -+#define MC_CMD_NUM_OF_PARAMS 7 -+ -+#define MAKE_UMASK64(_width) \ -+ ((uint64_t)((_width) < 64 ? ((uint64_t)1 << (_width)) - 1 : \ -+ (uint64_t)-1)) -+ -+static inline uint64_t mc_enc(int lsoffset, int width, uint64_t val) -+{ -+ return (uint64_t)(((uint64_t)val & MAKE_UMASK64(width)) << lsoffset); -+} -+ -+static inline uint64_t mc_dec(uint64_t val, int lsoffset, int width) -+{ -+ return (uint64_t)((val >> lsoffset) & MAKE_UMASK64(width)); -+} -+ -+struct mc_command { -+ uint64_t header; -+ uint64_t params[MC_CMD_NUM_OF_PARAMS]; -+}; -+ -+enum mc_cmd_status { -+ MC_CMD_STATUS_OK = 0x0, /* Completed successfully */ -+ MC_CMD_STATUS_READY = 0x1, /* Ready to be processed */ -+ MC_CMD_STATUS_AUTH_ERR = 0x3, /* Authentication error */ -+ MC_CMD_STATUS_NO_PRIVILEGE = 0x4, /* No privilege */ -+ MC_CMD_STATUS_DMA_ERR = 0x5, /* DMA or I/O error */ -+ MC_CMD_STATUS_CONFIG_ERR = 0x6, /* Configuration error */ -+ MC_CMD_STATUS_TIMEOUT = 0x7, /* Operation timed out */ -+ MC_CMD_STATUS_NO_RESOURCE = 0x8, /* No resources */ -+ MC_CMD_STATUS_NO_MEMORY = 0x9, /* No memory available */ -+ MC_CMD_STATUS_BUSY = 0xA, /* Device is busy */ -+ MC_CMD_STATUS_UNSUPPORTED_OP = 0xB, /* Unsupported operation */ -+ MC_CMD_STATUS_INVALID_STATE = 0xC /* Invalid state */ -+}; -+ -+/* -+ * MC command flags -+ */ -+ -+/* High priority flag */ -+#define MC_CMD_FLAG_PRI 0x00008000 -+/* Command completion flag */ -+#define MC_CMD_FLAG_INTR_DIS 0x01000000 -+ -+/* TODO Remove following two defines after completion of flib 8.0.0 -+integration */ -+#define MC_CMD_PRI_LOW 0 /*!< Low Priority command indication */ -+#define MC_CMD_PRI_HIGH 1 /*!< High Priority command indication */ -+ -+#define MC_CMD_HDR_CMDID_O 52 /* Command ID field offset */ -+#define MC_CMD_HDR_CMDID_S 12 /* Command ID field size */ -+#define MC_CMD_HDR_TOKEN_O 38 /* Token field offset */ -+#define MC_CMD_HDR_TOKEN_S 10 /* Token field size */ -+#define MC_CMD_HDR_STATUS_O 16 /* Status field offset */ -+#define MC_CMD_HDR_STATUS_S 8 /* Status field size*/ -+#define MC_CMD_HDR_FLAGS_O 0 /* Flags field offset */ -+#define MC_CMD_HDR_FLAGS_S 32 /* Flags field size*/ -+#define MC_CMD_HDR_FLAGS_MASK 0xFF00FF00 /* Command flags mask */ -+ -+#define MC_CMD_HDR_READ_STATUS(_hdr) \ -+ ((enum mc_cmd_status)mc_dec((_hdr), \ -+ MC_CMD_HDR_STATUS_O, MC_CMD_HDR_STATUS_S)) -+ -+#define MC_CMD_HDR_READ_TOKEN(_hdr) \ -+ ((uint16_t)mc_dec((_hdr), MC_CMD_HDR_TOKEN_O, MC_CMD_HDR_TOKEN_S)) -+ -+#define MC_CMD_HDR_READ_FLAGS(_hdr) \ -+ ((uint32_t)mc_dec((_hdr), MC_CMD_HDR_FLAGS_O, MC_CMD_HDR_FLAGS_S)) -+ -+#define MC_PREP_OP(_ext, _param, _offset, _width, _type, _arg) \ -+ ((_ext)[_param] |= cpu_to_le64(mc_enc((_offset), (_width), _arg))) -+ -+#define MC_EXT_OP(_ext, _param, _offset, _width, _type, _arg) \ -+ (_arg = (_type)mc_dec(cpu_to_le64(_ext[_param]), (_offset), (_width))) -+ -+#define MC_CMD_OP(_cmd, _param, _offset, _width, _type, _arg) \ -+ ((_cmd).params[_param] |= mc_enc((_offset), (_width), _arg)) -+ -+#define MC_RSP_OP(_cmd, _param, _offset, _width, _type, _arg) \ -+ (_arg = (_type)mc_dec(_cmd.params[_param], (_offset), (_width))) -+ -+static inline uint64_t mc_encode_cmd_header(uint16_t cmd_id, -+ uint32_t cmd_flags, -+ uint16_t token) -+{ -+ uint64_t hdr; -+ -+ hdr = mc_enc(MC_CMD_HDR_CMDID_O, MC_CMD_HDR_CMDID_S, cmd_id); -+ hdr |= mc_enc(MC_CMD_HDR_FLAGS_O, MC_CMD_HDR_FLAGS_S, -+ (cmd_flags & MC_CMD_HDR_FLAGS_MASK)); -+ hdr |= mc_enc(MC_CMD_HDR_TOKEN_O, MC_CMD_HDR_TOKEN_S, token); -+ hdr |= mc_enc(MC_CMD_HDR_STATUS_O, MC_CMD_HDR_STATUS_S, -+ MC_CMD_STATUS_READY); -+ -+ return hdr; -+} -+ -+#endif /* __FSL_MC_CMD_H */ -diff --git a/drivers/staging/fsl-mc/include/mc-private.h b/drivers/staging/fsl-mc/include/mc-private.h -new file mode 100644 -index 0000000..1246ca8 ---- /dev/null -+++ b/drivers/staging/fsl-mc/include/mc-private.h -@@ -0,0 +1,168 @@ -+/* -+ * Freescale Management Complex (MC) bus private declarations -+ * -+ * Copyright (C) 2014 Freescale Semiconductor, Inc. -+ * Author: German Rivera -+ * -+ * This file is licensed under the terms of the GNU General Public -+ * License version 2. This program is licensed "as is" without any -+ * warranty of any kind, whether express or implied. -+ */ -+#ifndef _FSL_MC_PRIVATE_H_ -+#define _FSL_MC_PRIVATE_H_ -+ -+#include "../include/mc.h" -+#include -+#include -+ -+#define FSL_MC_DPRC_DRIVER_NAME "fsl_mc_dprc" -+ -+#define FSL_MC_DEVICE_MATCH(_mc_dev, _obj_desc) \ -+ (strcmp((_mc_dev)->obj_desc.type, (_obj_desc)->type) == 0 && \ -+ (_mc_dev)->obj_desc.id == (_obj_desc)->id) -+ -+#define FSL_MC_IS_ALLOCATABLE(_obj_type) \ -+ (strcmp(_obj_type, "dpbp") == 0 || \ -+ strcmp(_obj_type, "dpmcp") == 0 || \ -+ strcmp(_obj_type, "dpcon") == 0) -+ -+/** -+ * Maximum number of total IRQs that can be pre-allocated for an MC bus' -+ * IRQ pool -+ */ -+#define FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS 256 -+ -+/** -+ * Maximum number of extra IRQs pre-reallocated for an MC bus' IRQ pool, -+ * to be used by dynamically created MC objects -+ */ -+#define FSL_MC_IRQ_POOL_MAX_EXTRA_IRQS 64 -+ -+/** -+ * struct fsl_mc - Private data of a "fsl,qoriq-mc" platform device -+ * @root_mc_bus_dev: MC object device representing the root DPRC -+ * @irq_domain: IRQ domain for the fsl-mc bus type -+ * @gic_supported: boolean flag that indicates if the GIC interrupt controller -+ * is supported. -+ * @num_translation_ranges: number of entries in addr_translation_ranges -+ * @addr_translation_ranges: array of bus to system address translation ranges -+ */ -+struct fsl_mc { -+ struct fsl_mc_device *root_mc_bus_dev; -+ struct irq_domain *irq_domain; -+ bool gic_supported; -+ uint8_t num_translation_ranges; -+ struct fsl_mc_addr_translation_range *translation_ranges; -+}; -+ -+/** -+ * enum mc_region_types - Types of MC MMIO regions -+ */ -+enum fsl_mc_region_types { -+ FSL_MC_PORTAL = 0x0, -+ FSL_QBMAN_PORTAL, -+ -+ /* -+ * New offset types must be added above this entry -+ */ -+ FSL_NUM_MC_OFFSET_TYPES -+}; -+ -+/** -+ * struct fsl_mc_addr_translation_range - bus to system address translation -+ * range -+ * @mc_region_type: Type of MC region for the range being translated -+ * @start_mc_offset: Start MC offset of the range being translated -+ * @end_mc_offset: MC offset of the first byte after the range (last MC -+ * offset of the range is end_mc_offset - 1) -+ * @start_phys_addr: system physical address corresponding to start_mc_addr -+ */ -+struct fsl_mc_addr_translation_range { -+ enum fsl_mc_region_types mc_region_type; -+ uint64_t start_mc_offset; -+ uint64_t end_mc_offset; -+ phys_addr_t start_phys_addr; -+}; -+ -+/** -+ * struct fsl_mc_resource_pool - Pool of MC resources of a given -+ * type -+ * @type: type of resources in the pool -+ * @max_count: maximum number of resources in the pool -+ * @free_count: number of free resources in the pool -+ * @mutex: mutex to serialize access to the pool's free list -+ * @free_list: anchor node of list of free resources in the pool -+ * @mc_bus: pointer to the MC bus that owns this resource pool -+ */ -+struct fsl_mc_resource_pool { -+ enum fsl_mc_pool_type type; -+ int16_t max_count; -+ int16_t free_count; -+ struct mutex mutex; /* serializes access to free_list */ -+ struct list_head free_list; -+ struct fsl_mc_bus *mc_bus; -+}; -+ -+/** -+ * struct fsl_mc_bus - logical bus that corresponds to a physical DPRC -+ * @mc_dev: fsl-mc device for the bus device itself. -+ * @resource_pools: array of resource pools (one pool per resource type) -+ * for this MC bus. These resources represent allocatable entities -+ * from the physical DPRC. -+ * @atomic_mc_io: mc_io object to be used to send DPRC commands to the MC -+ * in atomic context (e.g., when programming MSIs in program_msi_at_mc()). -+ * @atomic_dprc_handle: DPRC handle opened using the atomic_mc_io's portal. -+ * @irq_resources: Pointer to array of IRQ objects for the IRQ pool. -+ * @scan_mutex: Serializes bus scanning -+ * @dprc_attr: DPRC attributes -+ */ -+struct fsl_mc_bus { -+ struct fsl_mc_device mc_dev; -+ struct fsl_mc_resource_pool resource_pools[FSL_MC_NUM_POOL_TYPES]; -+ struct fsl_mc_device_irq *irq_resources; -+ struct fsl_mc_io *atomic_mc_io; -+ uint16_t atomic_dprc_handle; -+ struct mutex scan_mutex; /* serializes bus scanning */ -+ struct dprc_attributes dprc_attr; -+}; -+ -+#define to_fsl_mc_bus(_mc_dev) \ -+ container_of(_mc_dev, struct fsl_mc_bus, mc_dev) -+ -+int __must_check fsl_mc_device_add(struct dprc_obj_desc *obj_desc, -+ struct fsl_mc_io *mc_io, -+ struct device *parent_dev, -+ const char *driver_override, -+ struct fsl_mc_device **new_mc_dev); -+ -+void fsl_mc_device_remove(struct fsl_mc_device *mc_dev); -+ -+int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev, -+ const char *driver_override, -+ unsigned int *total_irq_count); -+ -+int __init dprc_driver_init(void); -+ -+void __exit dprc_driver_exit(void); -+ -+int __init fsl_mc_allocator_driver_init(void); -+ -+void __exit fsl_mc_allocator_driver_exit(void); -+ -+int __must_check fsl_mc_resource_allocate(struct fsl_mc_bus *mc_bus, -+ enum fsl_mc_pool_type pool_type, -+ struct fsl_mc_resource -+ **new_resource); -+ -+void fsl_mc_resource_free(struct fsl_mc_resource *resource); -+ -+int __must_check fsl_mc_populate_irq_pool(struct fsl_mc_bus *mc_bus, -+ unsigned int irq_count); -+ -+void fsl_mc_cleanup_irq_pool(struct fsl_mc_bus *mc_bus); -+ -+void dprc_init_all_resource_pools(struct fsl_mc_device *mc_bus_dev); -+ -+void dprc_cleanup_all_resource_pools(struct fsl_mc_device *mc_bus_dev); -+ -+#endif /* _FSL_MC_PRIVATE_H_ */ -diff --git a/drivers/staging/fsl-mc/include/mc-sys.h b/drivers/staging/fsl-mc/include/mc-sys.h -new file mode 100644 -index 0000000..b08df85 ---- /dev/null -+++ b/drivers/staging/fsl-mc/include/mc-sys.h -@@ -0,0 +1,128 @@ -+/* Copyright 2013-2014 Freescale Semiconductor Inc. -+ * -+ * Interface of the I/O services to send MC commands to the MC hardware -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+ -+#ifndef _FSL_MC_SYS_H -+#define _FSL_MC_SYS_H -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/** -+ * Bit masks for a MC I/O object (struct fsl_mc_io) flags -+ */ -+#define FSL_MC_IO_ATOMIC_CONTEXT_PORTAL 0x0001 -+ -+struct fsl_mc_resource; -+struct mc_command; -+ -+/** -+ * struct fsl_mc_io - MC I/O object to be passed-in to mc_send_command() -+ * @dev: device associated with this Mc I/O object -+ * @flags: flags for mc_send_command() -+ * @portal_size: MC command portal size in bytes -+ * @portal_phys_addr: MC command portal physical address -+ * @portal_virt_addr: MC command portal virtual address -+ * @dpmcp_dev: pointer to the DPMCP device associated with the MC portal. -+ * @mc_command_done_irq_armed: Flag indicating that the MC command done IRQ -+ * is currently armed. -+ * @mc_command_done_completion: Completion variable to be signaled when an MC -+ * command sent to the MC fw is completed. -+ * -+ * Fields are only meaningful if the FSL_MC_IO_ATOMIC_CONTEXT_PORTAL flag is not -+ * set: -+ * @mutex: Mutex to serialize mc_send_command() calls that use the same MC -+ * portal, if the fsl_mc_io object was created with the -+ * FSL_MC_IO_ATOMIC_CONTEXT_PORTAL flag off. mc_send_command() calls for this -+ * fsl_mc_io object must be made only from non-atomic context. -+ * @mc_command_done_completion: Linux completion variable to be signaled -+ * when a DPMCP command completion interrupts is received. -+ * @mc_command_done_irq_armed: Boolean flag that indicates if interrupts have -+ * been successfully configured for the corresponding DPMCP object. -+ * -+ * Fields are only meaningful if the FSL_MC_IO_ATOMIC_CONTEXT_PORTAL flag is -+ * set: -+ * @spinlock: Spinlock to serialize mc_send_command() calls that use the same MC -+ * portal, if the fsl_mc_io object was created with the -+ * FSL_MC_IO_ATOMIC_CONTEXT_PORTAL flag on. mc_send_command() calls for this -+ * fsl_mc_io object can be made from atomic or non-atomic context. -+ */ -+struct fsl_mc_io { -+ struct device *dev; -+ uint16_t flags; -+ uint16_t portal_size; -+ phys_addr_t portal_phys_addr; -+ void __iomem *portal_virt_addr; -+ struct fsl_mc_device *dpmcp_dev; -+ union { -+ /* -+ * These fields are only meaningful if the -+ * FSL_MC_IO_ATOMIC_CONTEXT_PORTAL flag is not set -+ */ -+ struct { -+ struct mutex mutex; /* serializes mc_send_command() */ -+ struct completion mc_command_done_completion; -+ bool mc_command_done_irq_armed; -+ }; -+ -+ /* -+ * This field is only meaningful if the -+ * FSL_MC_IO_ATOMIC_CONTEXT_PORTAL flag is set -+ */ -+ spinlock_t spinlock; /* serializes mc_send_command() */ -+ }; -+}; -+ -+int __must_check fsl_create_mc_io(struct device *dev, -+ phys_addr_t mc_portal_phys_addr, -+ uint32_t mc_portal_size, -+ struct fsl_mc_device *dpmcp_dev, -+ uint32_t flags, struct fsl_mc_io **new_mc_io); -+ -+void fsl_destroy_mc_io(struct fsl_mc_io *mc_io); -+ -+int fsl_mc_io_set_dpmcp(struct fsl_mc_io *mc_io, -+ struct fsl_mc_device *dpmcp_dev); -+ -+void fsl_mc_io_unset_dpmcp(struct fsl_mc_io *mc_io); -+ -+int fsl_mc_io_setup_dpmcp_irq(struct fsl_mc_io *mc_io); -+ -+int mc_send_command(struct fsl_mc_io *mc_io, struct mc_command *cmd); -+ -+#endif /* _FSL_MC_SYS_H */ -diff --git a/drivers/staging/fsl-mc/include/mc.h b/drivers/staging/fsl-mc/include/mc.h -new file mode 100644 -index 0000000..bbeb121 ---- /dev/null -+++ b/drivers/staging/fsl-mc/include/mc.h -@@ -0,0 +1,244 @@ -+/* -+ * Freescale Management Complex (MC) bus public interface -+ * -+ * Copyright (C) 2014 Freescale Semiconductor, Inc. -+ * Author: German Rivera -+ * -+ * This file is licensed under the terms of the GNU General Public -+ * License version 2. This program is licensed "as is" without any -+ * warranty of any kind, whether express or implied. -+ */ -+#ifndef _FSL_MC_H_ -+#define _FSL_MC_H_ -+ -+#include -+#include -+#include -+#include -+#include -+#include "../include/dprc.h" -+ -+#define FSL_MC_VENDOR_FREESCALE 0x1957 -+ -+struct fsl_mc_device; -+struct fsl_mc_io; -+ -+/** -+ * struct fsl_mc_driver - MC object device driver object -+ * @driver: Generic device driver -+ * @match_id_table: table of supported device matching Ids -+ * @probe: Function called when a device is added -+ * @remove: Function called when a device is removed -+ * @shutdown: Function called at shutdown time to quiesce the device -+ * @suspend: Function called when a device is stopped -+ * @resume: Function called when a device is resumed -+ * -+ * Generic DPAA device driver object for device drivers that are registered -+ * with a DPRC bus. This structure is to be embedded in each device-specific -+ * driver structure. -+ */ -+struct fsl_mc_driver { -+ struct device_driver driver; -+ const struct fsl_mc_device_match_id *match_id_table; -+ int (*probe)(struct fsl_mc_device *dev); -+ int (*remove)(struct fsl_mc_device *dev); -+ void (*shutdown)(struct fsl_mc_device *dev); -+ int (*suspend)(struct fsl_mc_device *dev, pm_message_t state); -+ int (*resume)(struct fsl_mc_device *dev); -+}; -+ -+#define to_fsl_mc_driver(_drv) \ -+ container_of(_drv, struct fsl_mc_driver, driver) -+ -+/** -+ * struct fsl_mc_device_match_id - MC object device Id entry for driver matching -+ * @vendor: vendor ID -+ * @obj_type: MC object type -+ * @ver_major: MC object version major number -+ * @ver_minor: MC object version minor number -+ * -+ * Type of entries in the "device Id" table for MC object devices supported by -+ * a MC object device driver. The last entry of the table has vendor set to 0x0 -+ */ -+struct fsl_mc_device_match_id { -+ uint16_t vendor; -+ const char obj_type[16]; -+ uint32_t ver_major; -+ uint32_t ver_minor; -+}; -+ -+/** -+ * enum fsl_mc_pool_type - Types of allocatable MC bus resources -+ * -+ * Entries in these enum are used as indices in the array of resource -+ * pools of an fsl_mc_bus object. -+ */ -+enum fsl_mc_pool_type { -+ FSL_MC_POOL_DPMCP = 0x0, /* corresponds to "dpmcp" in the MC */ -+ FSL_MC_POOL_DPBP, /* corresponds to "dpbp" in the MC */ -+ FSL_MC_POOL_DPCON, /* corresponds to "dpcon" in the MC */ -+ FSL_MC_POOL_IRQ, -+ -+ /* -+ * NOTE: New resource pool types must be added before this entry -+ */ -+ FSL_MC_NUM_POOL_TYPES -+}; -+ -+/** -+ * struct fsl_mc_resource - MC generic resource -+ * @type: type of resource -+ * @id: unique MC resource Id within the resources of the same type -+ * @data: pointer to resource-specific data if the resource is currently -+ * allocated, or NULL if the resource is not currently allocated. -+ * @parent_pool: pointer to the parent resource pool from which this -+ * resource is allocated from. -+ * @node: Node in the free list of the corresponding resource pool -+ * -+ * NOTE: This structure is to be embedded as a field of specific -+ * MC resource structures. -+ */ -+struct fsl_mc_resource { -+ enum fsl_mc_pool_type type; -+ int32_t id; -+ void *data; -+ struct fsl_mc_resource_pool *parent_pool; -+ struct list_head node; -+}; -+ -+/** -+ * struct fsl_mc_device_irq - MC object device message-based interrupt -+ * @msi_paddr: message-based interrupt physical address -+ * @msi_value: message-based interrupt data value -+ * @irq_number: Linux IRQ number assigned to the interrupt -+ * @mc_dev: MC object device that owns this interrupt -+ * @dev_irq_index: device-relative IRQ index -+ * @resource: MC generic resource associated with the interrupt -+ */ -+struct fsl_mc_device_irq { -+ phys_addr_t msi_paddr; -+ uint32_t msi_value; -+ uint32_t irq_number; -+ struct fsl_mc_device *mc_dev; -+ uint8_t dev_irq_index; -+ struct fsl_mc_resource resource; -+}; -+ -+#define to_fsl_mc_irq(_mc_resource) \ -+ container_of(_mc_resource, struct fsl_mc_device_irq, resource) -+ -+/** -+ * Bit masks for a MC object device (struct fsl_mc_device) flags -+ */ -+#define FSL_MC_IS_DPRC 0x0001 -+ -+/** -+ * root dprc's parent is a platform device -+ * that platform device's bus type is platform_bus_type. -+ */ -+#define is_root_dprc(dev) \ -+ ((to_fsl_mc_device(dev)->flags & FSL_MC_IS_DPRC) && \ -+ ((dev)->bus == &fsl_mc_bus_type) && \ -+ ((dev)->parent->bus == &platform_bus_type)) -+ -+/** -+ * Default DMA mask for devices on a fsl-mc bus -+ */ -+#define FSL_MC_DEFAULT_DMA_MASK (~0ULL) -+ -+/** -+ * struct fsl_mc_device - MC object device object -+ * @dev: Linux driver model device object -+ * @dma_mask: Default DMA mask -+ * @flags: MC object device flags -+ * @icid: Isolation context ID for the device -+ * @mc_handle: MC handle for the corresponding MC object opened -+ * @mc_io: Pointer to MC IO object assigned to this device or -+ * NULL if none. -+ * @obj_desc: MC description of the DPAA device -+ * @regions: pointer to array of MMIO region entries -+ * @irqs: pointer to array of pointers to interrupts allocated to this device -+ * @resource: generic resource associated with this MC object device, if any. -+ * @driver_override: Driver name to force a match -+ * -+ * Generic device object for MC object devices that are "attached" to a -+ * MC bus. -+ * -+ * NOTES: -+ * - For a non-DPRC object its icid is the same as its parent DPRC's icid. -+ * - The SMMU notifier callback gets invoked after device_add() has been -+ * called for an MC object device, but before the device-specific probe -+ * callback gets called. -+ * - DP_OBJ_DPRC objects are the only MC objects that have built-in MC -+ * portals. For all other MC objects, their device drivers are responsible for -+ * allocating MC portals for them by calling fsl_mc_portal_allocate(). -+ * - Some types of MC objects (e.g., DP_OBJ_DPBP, DP_OBJ_DPCON) are -+ * treated as resources that can be allocated/deallocated from the -+ * corresponding resource pool in the object's parent DPRC, using the -+ * fsl_mc_object_allocate()/fsl_mc_object_free() functions. These MC objects -+ * are known as "allocatable" objects. For them, the corresponding -+ * fsl_mc_device's 'resource' points to the associated resource object. -+ * For MC objects that are not allocatable (e.g., DP_OBJ_DPRC, DP_OBJ_DPNI), -+ * 'resource' is NULL. -+ */ -+struct fsl_mc_device { -+ struct device dev; -+ uint64_t dma_mask; -+ uint16_t flags; -+ uint16_t icid; -+ uint16_t mc_handle; -+ struct fsl_mc_io *mc_io; -+ struct dprc_obj_desc obj_desc; -+ struct resource *regions; -+ struct fsl_mc_device_irq **irqs; -+ struct fsl_mc_resource *resource; -+ const char *driver_override; -+}; -+ -+#define to_fsl_mc_device(_dev) \ -+ container_of(_dev, struct fsl_mc_device, dev) -+ -+/* -+ * module_fsl_mc_driver() - Helper macro for drivers that don't do -+ * anything special in module init/exit. This eliminates a lot of -+ * boilerplate. Each module may only use this macro once, and -+ * calling it replaces module_init() and module_exit() -+ */ -+#define module_fsl_mc_driver(__fsl_mc_driver) \ -+ module_driver(__fsl_mc_driver, fsl_mc_driver_register, \ -+ fsl_mc_driver_unregister) -+ -+/* -+ * Macro to avoid include chaining to get THIS_MODULE -+ */ -+#define fsl_mc_driver_register(drv) \ -+ __fsl_mc_driver_register(drv, THIS_MODULE) -+ -+int __must_check __fsl_mc_driver_register(struct fsl_mc_driver *fsl_mc_driver, -+ struct module *owner); -+ -+void fsl_mc_driver_unregister(struct fsl_mc_driver *driver); -+ -+bool fsl_mc_interrupts_supported(void); -+ -+int __must_check fsl_mc_portal_allocate(struct fsl_mc_device *mc_dev, -+ uint16_t mc_io_flags, -+ struct fsl_mc_io **new_mc_io); -+ -+void fsl_mc_portal_free(struct fsl_mc_io *mc_io); -+ -+int fsl_mc_portal_reset(struct fsl_mc_io *mc_io); -+ -+int __must_check fsl_mc_object_allocate(struct fsl_mc_device *mc_dev, -+ enum fsl_mc_pool_type pool_type, -+ struct fsl_mc_device **new_mc_adev); -+ -+void fsl_mc_object_free(struct fsl_mc_device *mc_adev); -+ -+int __must_check fsl_mc_allocate_irqs(struct fsl_mc_device *mc_dev); -+ -+void fsl_mc_free_irqs(struct fsl_mc_device *mc_dev); -+ -+extern struct bus_type fsl_mc_bus_type; -+ -+#endif /* _FSL_MC_H_ */ -diff --git a/drivers/staging/fsl-mc/include/net.h b/drivers/staging/fsl-mc/include/net.h -new file mode 100644 -index 0000000..7480f6a ---- /dev/null -+++ b/drivers/staging/fsl-mc/include/net.h -@@ -0,0 +1,481 @@ -+/* Copyright 2013-2015 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+#ifndef __FSL_NET_H -+#define __FSL_NET_H -+ -+#define LAST_HDR_INDEX 0xFFFFFFFF -+ -+/*****************************************************************************/ -+/* Protocol fields */ -+/*****************************************************************************/ -+ -+/************************* Ethernet fields *********************************/ -+#define NH_FLD_ETH_DA (1) -+#define NH_FLD_ETH_SA (NH_FLD_ETH_DA << 1) -+#define NH_FLD_ETH_LENGTH (NH_FLD_ETH_DA << 2) -+#define NH_FLD_ETH_TYPE (NH_FLD_ETH_DA << 3) -+#define NH_FLD_ETH_FINAL_CKSUM (NH_FLD_ETH_DA << 4) -+#define NH_FLD_ETH_PADDING (NH_FLD_ETH_DA << 5) -+#define NH_FLD_ETH_ALL_FIELDS ((NH_FLD_ETH_DA << 6) - 1) -+ -+#define NH_FLD_ETH_ADDR_SIZE 6 -+ -+/*************************** VLAN fields ***********************************/ -+#define NH_FLD_VLAN_VPRI (1) -+#define NH_FLD_VLAN_CFI (NH_FLD_VLAN_VPRI << 1) -+#define NH_FLD_VLAN_VID (NH_FLD_VLAN_VPRI << 2) -+#define NH_FLD_VLAN_LENGTH (NH_FLD_VLAN_VPRI << 3) -+#define NH_FLD_VLAN_TYPE (NH_FLD_VLAN_VPRI << 4) -+#define NH_FLD_VLAN_ALL_FIELDS ((NH_FLD_VLAN_VPRI << 5) - 1) -+ -+#define NH_FLD_VLAN_TCI (NH_FLD_VLAN_VPRI | \ -+ NH_FLD_VLAN_CFI | \ -+ NH_FLD_VLAN_VID) -+ -+/************************ IP (generic) fields ******************************/ -+#define NH_FLD_IP_VER (1) -+#define NH_FLD_IP_DSCP (NH_FLD_IP_VER << 2) -+#define NH_FLD_IP_ECN (NH_FLD_IP_VER << 3) -+#define NH_FLD_IP_PROTO (NH_FLD_IP_VER << 4) -+#define NH_FLD_IP_SRC (NH_FLD_IP_VER << 5) -+#define NH_FLD_IP_DST (NH_FLD_IP_VER << 6) -+#define NH_FLD_IP_TOS_TC (NH_FLD_IP_VER << 7) -+#define NH_FLD_IP_ID (NH_FLD_IP_VER << 8) -+#define NH_FLD_IP_ALL_FIELDS ((NH_FLD_IP_VER << 9) - 1) -+ -+#define NH_FLD_IP_PROTO_SIZE 1 -+ -+/***************************** IPV4 fields *********************************/ -+#define NH_FLD_IPV4_VER (1) -+#define NH_FLD_IPV4_HDR_LEN (NH_FLD_IPV4_VER << 1) -+#define NH_FLD_IPV4_TOS (NH_FLD_IPV4_VER << 2) -+#define NH_FLD_IPV4_TOTAL_LEN (NH_FLD_IPV4_VER << 3) -+#define NH_FLD_IPV4_ID (NH_FLD_IPV4_VER << 4) -+#define NH_FLD_IPV4_FLAG_D (NH_FLD_IPV4_VER << 5) -+#define NH_FLD_IPV4_FLAG_M (NH_FLD_IPV4_VER << 6) -+#define NH_FLD_IPV4_OFFSET (NH_FLD_IPV4_VER << 7) -+#define NH_FLD_IPV4_TTL (NH_FLD_IPV4_VER << 8) -+#define NH_FLD_IPV4_PROTO (NH_FLD_IPV4_VER << 9) -+#define NH_FLD_IPV4_CKSUM (NH_FLD_IPV4_VER << 10) -+#define NH_FLD_IPV4_SRC_IP (NH_FLD_IPV4_VER << 11) -+#define NH_FLD_IPV4_DST_IP (NH_FLD_IPV4_VER << 12) -+#define NH_FLD_IPV4_OPTS (NH_FLD_IPV4_VER << 13) -+#define NH_FLD_IPV4_OPTS_COUNT (NH_FLD_IPV4_VER << 14) -+#define NH_FLD_IPV4_ALL_FIELDS ((NH_FLD_IPV4_VER << 15) - 1) -+ -+#define NH_FLD_IPV4_ADDR_SIZE 4 -+#define NH_FLD_IPV4_PROTO_SIZE 1 -+ -+/***************************** IPV6 fields *********************************/ -+#define NH_FLD_IPV6_VER (1) -+#define NH_FLD_IPV6_TC (NH_FLD_IPV6_VER << 1) -+#define NH_FLD_IPV6_SRC_IP (NH_FLD_IPV6_VER << 2) -+#define NH_FLD_IPV6_DST_IP (NH_FLD_IPV6_VER << 3) -+#define NH_FLD_IPV6_NEXT_HDR (NH_FLD_IPV6_VER << 4) -+#define NH_FLD_IPV6_FL (NH_FLD_IPV6_VER << 5) -+#define NH_FLD_IPV6_HOP_LIMIT (NH_FLD_IPV6_VER << 6) -+#define NH_FLD_IPV6_ID (NH_FLD_IPV6_VER << 7) -+#define NH_FLD_IPV6_ALL_FIELDS ((NH_FLD_IPV6_VER << 8) - 1) -+ -+#define NH_FLD_IPV6_ADDR_SIZE 16 -+#define NH_FLD_IPV6_NEXT_HDR_SIZE 1 -+ -+/***************************** ICMP fields *********************************/ -+#define NH_FLD_ICMP_TYPE (1) -+#define NH_FLD_ICMP_CODE (NH_FLD_ICMP_TYPE << 1) -+#define NH_FLD_ICMP_CKSUM (NH_FLD_ICMP_TYPE << 2) -+#define NH_FLD_ICMP_ID (NH_FLD_ICMP_TYPE << 3) -+#define NH_FLD_ICMP_SQ_NUM (NH_FLD_ICMP_TYPE << 4) -+#define NH_FLD_ICMP_ALL_FIELDS ((NH_FLD_ICMP_TYPE << 5) - 1) -+ -+#define NH_FLD_ICMP_CODE_SIZE 1 -+#define NH_FLD_ICMP_TYPE_SIZE 1 -+ -+/***************************** IGMP fields *********************************/ -+#define NH_FLD_IGMP_VERSION (1) -+#define NH_FLD_IGMP_TYPE (NH_FLD_IGMP_VERSION << 1) -+#define NH_FLD_IGMP_CKSUM (NH_FLD_IGMP_VERSION << 2) -+#define NH_FLD_IGMP_DATA (NH_FLD_IGMP_VERSION << 3) -+#define NH_FLD_IGMP_ALL_FIELDS ((NH_FLD_IGMP_VERSION << 4) - 1) -+ -+/***************************** TCP fields **********************************/ -+#define NH_FLD_TCP_PORT_SRC (1) -+#define NH_FLD_TCP_PORT_DST (NH_FLD_TCP_PORT_SRC << 1) -+#define NH_FLD_TCP_SEQ (NH_FLD_TCP_PORT_SRC << 2) -+#define NH_FLD_TCP_ACK (NH_FLD_TCP_PORT_SRC << 3) -+#define NH_FLD_TCP_OFFSET (NH_FLD_TCP_PORT_SRC << 4) -+#define NH_FLD_TCP_FLAGS (NH_FLD_TCP_PORT_SRC << 5) -+#define NH_FLD_TCP_WINDOW (NH_FLD_TCP_PORT_SRC << 6) -+#define NH_FLD_TCP_CKSUM (NH_FLD_TCP_PORT_SRC << 7) -+#define NH_FLD_TCP_URGPTR (NH_FLD_TCP_PORT_SRC << 8) -+#define NH_FLD_TCP_OPTS (NH_FLD_TCP_PORT_SRC << 9) -+#define NH_FLD_TCP_OPTS_COUNT (NH_FLD_TCP_PORT_SRC << 10) -+#define NH_FLD_TCP_ALL_FIELDS ((NH_FLD_TCP_PORT_SRC << 11) - 1) -+ -+#define NH_FLD_TCP_PORT_SIZE 2 -+ -+/***************************** UDP fields **********************************/ -+#define NH_FLD_UDP_PORT_SRC (1) -+#define NH_FLD_UDP_PORT_DST (NH_FLD_UDP_PORT_SRC << 1) -+#define NH_FLD_UDP_LEN (NH_FLD_UDP_PORT_SRC << 2) -+#define NH_FLD_UDP_CKSUM (NH_FLD_UDP_PORT_SRC << 3) -+#define NH_FLD_UDP_ALL_FIELDS ((NH_FLD_UDP_PORT_SRC << 4) - 1) -+ -+#define NH_FLD_UDP_PORT_SIZE 2 -+ -+/*************************** UDP-lite fields *******************************/ -+#define NH_FLD_UDP_LITE_PORT_SRC (1) -+#define NH_FLD_UDP_LITE_PORT_DST (NH_FLD_UDP_LITE_PORT_SRC << 1) -+#define NH_FLD_UDP_LITE_ALL_FIELDS \ -+ ((NH_FLD_UDP_LITE_PORT_SRC << 2) - 1) -+ -+#define NH_FLD_UDP_LITE_PORT_SIZE 2 -+ -+/*************************** UDP-encap-ESP fields **************************/ -+#define NH_FLD_UDP_ENC_ESP_PORT_SRC (1) -+#define NH_FLD_UDP_ENC_ESP_PORT_DST (NH_FLD_UDP_ENC_ESP_PORT_SRC << 1) -+#define NH_FLD_UDP_ENC_ESP_LEN (NH_FLD_UDP_ENC_ESP_PORT_SRC << 2) -+#define NH_FLD_UDP_ENC_ESP_CKSUM (NH_FLD_UDP_ENC_ESP_PORT_SRC << 3) -+#define NH_FLD_UDP_ENC_ESP_SPI (NH_FLD_UDP_ENC_ESP_PORT_SRC << 4) -+#define NH_FLD_UDP_ENC_ESP_SEQUENCE_NUM (NH_FLD_UDP_ENC_ESP_PORT_SRC << 5) -+#define NH_FLD_UDP_ENC_ESP_ALL_FIELDS \ -+ ((NH_FLD_UDP_ENC_ESP_PORT_SRC << 6) - 1) -+ -+#define NH_FLD_UDP_ENC_ESP_PORT_SIZE 2 -+#define NH_FLD_UDP_ENC_ESP_SPI_SIZE 4 -+ -+/***************************** SCTP fields *********************************/ -+#define NH_FLD_SCTP_PORT_SRC (1) -+#define NH_FLD_SCTP_PORT_DST (NH_FLD_SCTP_PORT_SRC << 1) -+#define NH_FLD_SCTP_VER_TAG (NH_FLD_SCTP_PORT_SRC << 2) -+#define NH_FLD_SCTP_CKSUM (NH_FLD_SCTP_PORT_SRC << 3) -+#define NH_FLD_SCTP_ALL_FIELDS ((NH_FLD_SCTP_PORT_SRC << 4) - 1) -+ -+#define NH_FLD_SCTP_PORT_SIZE 2 -+ -+/***************************** DCCP fields *********************************/ -+#define NH_FLD_DCCP_PORT_SRC (1) -+#define NH_FLD_DCCP_PORT_DST (NH_FLD_DCCP_PORT_SRC << 1) -+#define NH_FLD_DCCP_ALL_FIELDS ((NH_FLD_DCCP_PORT_SRC << 2) - 1) -+ -+#define NH_FLD_DCCP_PORT_SIZE 2 -+ -+/***************************** IPHC fields *********************************/ -+#define NH_FLD_IPHC_CID (1) -+#define NH_FLD_IPHC_CID_TYPE (NH_FLD_IPHC_CID << 1) -+#define NH_FLD_IPHC_HCINDEX (NH_FLD_IPHC_CID << 2) -+#define NH_FLD_IPHC_GEN (NH_FLD_IPHC_CID << 3) -+#define NH_FLD_IPHC_D_BIT (NH_FLD_IPHC_CID << 4) -+#define NH_FLD_IPHC_ALL_FIELDS ((NH_FLD_IPHC_CID << 5) - 1) -+ -+/***************************** SCTP fields *********************************/ -+#define NH_FLD_SCTP_CHUNK_DATA_TYPE (1) -+#define NH_FLD_SCTP_CHUNK_DATA_FLAGS (NH_FLD_SCTP_CHUNK_DATA_TYPE << 1) -+#define NH_FLD_SCTP_CHUNK_DATA_LENGTH (NH_FLD_SCTP_CHUNK_DATA_TYPE << 2) -+#define NH_FLD_SCTP_CHUNK_DATA_TSN (NH_FLD_SCTP_CHUNK_DATA_TYPE << 3) -+#define NH_FLD_SCTP_CHUNK_DATA_STREAM_ID (NH_FLD_SCTP_CHUNK_DATA_TYPE << 4) -+#define NH_FLD_SCTP_CHUNK_DATA_STREAM_SQN (NH_FLD_SCTP_CHUNK_DATA_TYPE << 5) -+#define NH_FLD_SCTP_CHUNK_DATA_PAYLOAD_PID (NH_FLD_SCTP_CHUNK_DATA_TYPE << 6) -+#define NH_FLD_SCTP_CHUNK_DATA_UNORDERED (NH_FLD_SCTP_CHUNK_DATA_TYPE << 7) -+#define NH_FLD_SCTP_CHUNK_DATA_BEGGINING (NH_FLD_SCTP_CHUNK_DATA_TYPE << 8) -+#define NH_FLD_SCTP_CHUNK_DATA_END (NH_FLD_SCTP_CHUNK_DATA_TYPE << 9) -+#define NH_FLD_SCTP_CHUNK_DATA_ALL_FIELDS \ -+ ((NH_FLD_SCTP_CHUNK_DATA_TYPE << 10) - 1) -+ -+/*************************** L2TPV2 fields *********************************/ -+#define NH_FLD_L2TPV2_TYPE_BIT (1) -+#define NH_FLD_L2TPV2_LENGTH_BIT (NH_FLD_L2TPV2_TYPE_BIT << 1) -+#define NH_FLD_L2TPV2_SEQUENCE_BIT (NH_FLD_L2TPV2_TYPE_BIT << 2) -+#define NH_FLD_L2TPV2_OFFSET_BIT (NH_FLD_L2TPV2_TYPE_BIT << 3) -+#define NH_FLD_L2TPV2_PRIORITY_BIT (NH_FLD_L2TPV2_TYPE_BIT << 4) -+#define NH_FLD_L2TPV2_VERSION (NH_FLD_L2TPV2_TYPE_BIT << 5) -+#define NH_FLD_L2TPV2_LEN (NH_FLD_L2TPV2_TYPE_BIT << 6) -+#define NH_FLD_L2TPV2_TUNNEL_ID (NH_FLD_L2TPV2_TYPE_BIT << 7) -+#define NH_FLD_L2TPV2_SESSION_ID (NH_FLD_L2TPV2_TYPE_BIT << 8) -+#define NH_FLD_L2TPV2_NS (NH_FLD_L2TPV2_TYPE_BIT << 9) -+#define NH_FLD_L2TPV2_NR (NH_FLD_L2TPV2_TYPE_BIT << 10) -+#define NH_FLD_L2TPV2_OFFSET_SIZE (NH_FLD_L2TPV2_TYPE_BIT << 11) -+#define NH_FLD_L2TPV2_FIRST_BYTE (NH_FLD_L2TPV2_TYPE_BIT << 12) -+#define NH_FLD_L2TPV2_ALL_FIELDS \ -+ ((NH_FLD_L2TPV2_TYPE_BIT << 13) - 1) -+ -+/*************************** L2TPV3 fields *********************************/ -+#define NH_FLD_L2TPV3_CTRL_TYPE_BIT (1) -+#define NH_FLD_L2TPV3_CTRL_LENGTH_BIT (NH_FLD_L2TPV3_CTRL_TYPE_BIT << 1) -+#define NH_FLD_L2TPV3_CTRL_SEQUENCE_BIT (NH_FLD_L2TPV3_CTRL_TYPE_BIT << 2) -+#define NH_FLD_L2TPV3_CTRL_VERSION (NH_FLD_L2TPV3_CTRL_TYPE_BIT << 3) -+#define NH_FLD_L2TPV3_CTRL_LENGTH (NH_FLD_L2TPV3_CTRL_TYPE_BIT << 4) -+#define NH_FLD_L2TPV3_CTRL_CONTROL (NH_FLD_L2TPV3_CTRL_TYPE_BIT << 5) -+#define NH_FLD_L2TPV3_CTRL_SENT (NH_FLD_L2TPV3_CTRL_TYPE_BIT << 6) -+#define NH_FLD_L2TPV3_CTRL_RECV (NH_FLD_L2TPV3_CTRL_TYPE_BIT << 7) -+#define NH_FLD_L2TPV3_CTRL_FIRST_BYTE (NH_FLD_L2TPV3_CTRL_TYPE_BIT << 8) -+#define NH_FLD_L2TPV3_CTRL_ALL_FIELDS \ -+ ((NH_FLD_L2TPV3_CTRL_TYPE_BIT << 9) - 1) -+ -+#define NH_FLD_L2TPV3_SESS_TYPE_BIT (1) -+#define NH_FLD_L2TPV3_SESS_VERSION (NH_FLD_L2TPV3_SESS_TYPE_BIT << 1) -+#define NH_FLD_L2TPV3_SESS_ID (NH_FLD_L2TPV3_SESS_TYPE_BIT << 2) -+#define NH_FLD_L2TPV3_SESS_COOKIE (NH_FLD_L2TPV3_SESS_TYPE_BIT << 3) -+#define NH_FLD_L2TPV3_SESS_ALL_FIELDS \ -+ ((NH_FLD_L2TPV3_SESS_TYPE_BIT << 4) - 1) -+ -+/**************************** PPP fields ***********************************/ -+#define NH_FLD_PPP_PID (1) -+#define NH_FLD_PPP_COMPRESSED (NH_FLD_PPP_PID << 1) -+#define NH_FLD_PPP_ALL_FIELDS ((NH_FLD_PPP_PID << 2) - 1) -+ -+/************************** PPPoE fields ***********************************/ -+#define NH_FLD_PPPOE_VER (1) -+#define NH_FLD_PPPOE_TYPE (NH_FLD_PPPOE_VER << 1) -+#define NH_FLD_PPPOE_CODE (NH_FLD_PPPOE_VER << 2) -+#define NH_FLD_PPPOE_SID (NH_FLD_PPPOE_VER << 3) -+#define NH_FLD_PPPOE_LEN (NH_FLD_PPPOE_VER << 4) -+#define NH_FLD_PPPOE_SESSION (NH_FLD_PPPOE_VER << 5) -+#define NH_FLD_PPPOE_PID (NH_FLD_PPPOE_VER << 6) -+#define NH_FLD_PPPOE_ALL_FIELDS ((NH_FLD_PPPOE_VER << 7) - 1) -+ -+/************************* PPP-Mux fields **********************************/ -+#define NH_FLD_PPPMUX_PID (1) -+#define NH_FLD_PPPMUX_CKSUM (NH_FLD_PPPMUX_PID << 1) -+#define NH_FLD_PPPMUX_COMPRESSED (NH_FLD_PPPMUX_PID << 2) -+#define NH_FLD_PPPMUX_ALL_FIELDS ((NH_FLD_PPPMUX_PID << 3) - 1) -+ -+/*********************** PPP-Mux sub-frame fields **************************/ -+#define NH_FLD_PPPMUX_SUBFRM_PFF (1) -+#define NH_FLD_PPPMUX_SUBFRM_LXT (NH_FLD_PPPMUX_SUBFRM_PFF << 1) -+#define NH_FLD_PPPMUX_SUBFRM_LEN (NH_FLD_PPPMUX_SUBFRM_PFF << 2) -+#define NH_FLD_PPPMUX_SUBFRM_PID (NH_FLD_PPPMUX_SUBFRM_PFF << 3) -+#define NH_FLD_PPPMUX_SUBFRM_USE_PID (NH_FLD_PPPMUX_SUBFRM_PFF << 4) -+#define NH_FLD_PPPMUX_SUBFRM_ALL_FIELDS \ -+ ((NH_FLD_PPPMUX_SUBFRM_PFF << 5) - 1) -+ -+/*************************** LLC fields ************************************/ -+#define NH_FLD_LLC_DSAP (1) -+#define NH_FLD_LLC_SSAP (NH_FLD_LLC_DSAP << 1) -+#define NH_FLD_LLC_CTRL (NH_FLD_LLC_DSAP << 2) -+#define NH_FLD_LLC_ALL_FIELDS ((NH_FLD_LLC_DSAP << 3) - 1) -+ -+/*************************** NLPID fields **********************************/ -+#define NH_FLD_NLPID_NLPID (1) -+#define NH_FLD_NLPID_ALL_FIELDS ((NH_FLD_NLPID_NLPID << 1) - 1) -+ -+/*************************** SNAP fields ***********************************/ -+#define NH_FLD_SNAP_OUI (1) -+#define NH_FLD_SNAP_PID (NH_FLD_SNAP_OUI << 1) -+#define NH_FLD_SNAP_ALL_FIELDS ((NH_FLD_SNAP_OUI << 2) - 1) -+ -+/*************************** LLC SNAP fields *******************************/ -+#define NH_FLD_LLC_SNAP_TYPE (1) -+#define NH_FLD_LLC_SNAP_ALL_FIELDS ((NH_FLD_LLC_SNAP_TYPE << 1) - 1) -+ -+#define NH_FLD_ARP_HTYPE (1) -+#define NH_FLD_ARP_PTYPE (NH_FLD_ARP_HTYPE << 1) -+#define NH_FLD_ARP_HLEN (NH_FLD_ARP_HTYPE << 2) -+#define NH_FLD_ARP_PLEN (NH_FLD_ARP_HTYPE << 3) -+#define NH_FLD_ARP_OPER (NH_FLD_ARP_HTYPE << 4) -+#define NH_FLD_ARP_SHA (NH_FLD_ARP_HTYPE << 5) -+#define NH_FLD_ARP_SPA (NH_FLD_ARP_HTYPE << 6) -+#define NH_FLD_ARP_THA (NH_FLD_ARP_HTYPE << 7) -+#define NH_FLD_ARP_TPA (NH_FLD_ARP_HTYPE << 8) -+#define NH_FLD_ARP_ALL_FIELDS ((NH_FLD_ARP_HTYPE << 9) - 1) -+ -+/*************************** RFC2684 fields ********************************/ -+#define NH_FLD_RFC2684_LLC (1) -+#define NH_FLD_RFC2684_NLPID (NH_FLD_RFC2684_LLC << 1) -+#define NH_FLD_RFC2684_OUI (NH_FLD_RFC2684_LLC << 2) -+#define NH_FLD_RFC2684_PID (NH_FLD_RFC2684_LLC << 3) -+#define NH_FLD_RFC2684_VPN_OUI (NH_FLD_RFC2684_LLC << 4) -+#define NH_FLD_RFC2684_VPN_IDX (NH_FLD_RFC2684_LLC << 5) -+#define NH_FLD_RFC2684_ALL_FIELDS ((NH_FLD_RFC2684_LLC << 6) - 1) -+ -+/*************************** User defined fields ***************************/ -+#define NH_FLD_USER_DEFINED_SRCPORT (1) -+#define NH_FLD_USER_DEFINED_PCDID (NH_FLD_USER_DEFINED_SRCPORT << 1) -+#define NH_FLD_USER_DEFINED_ALL_FIELDS \ -+ ((NH_FLD_USER_DEFINED_SRCPORT << 2) - 1) -+ -+/*************************** Payload fields ********************************/ -+#define NH_FLD_PAYLOAD_BUFFER (1) -+#define NH_FLD_PAYLOAD_SIZE (NH_FLD_PAYLOAD_BUFFER << 1) -+#define NH_FLD_MAX_FRM_SIZE (NH_FLD_PAYLOAD_BUFFER << 2) -+#define NH_FLD_MIN_FRM_SIZE (NH_FLD_PAYLOAD_BUFFER << 3) -+#define NH_FLD_PAYLOAD_TYPE (NH_FLD_PAYLOAD_BUFFER << 4) -+#define NH_FLD_FRAME_SIZE (NH_FLD_PAYLOAD_BUFFER << 5) -+#define NH_FLD_PAYLOAD_ALL_FIELDS ((NH_FLD_PAYLOAD_BUFFER << 6) - 1) -+ -+/*************************** GRE fields ************************************/ -+#define NH_FLD_GRE_TYPE (1) -+#define NH_FLD_GRE_ALL_FIELDS ((NH_FLD_GRE_TYPE << 1) - 1) -+ -+/*************************** MINENCAP fields *******************************/ -+#define NH_FLD_MINENCAP_SRC_IP (1) -+#define NH_FLD_MINENCAP_DST_IP (NH_FLD_MINENCAP_SRC_IP << 1) -+#define NH_FLD_MINENCAP_TYPE (NH_FLD_MINENCAP_SRC_IP << 2) -+#define NH_FLD_MINENCAP_ALL_FIELDS \ -+ ((NH_FLD_MINENCAP_SRC_IP << 3) - 1) -+ -+/*************************** IPSEC AH fields *******************************/ -+#define NH_FLD_IPSEC_AH_SPI (1) -+#define NH_FLD_IPSEC_AH_NH (NH_FLD_IPSEC_AH_SPI << 1) -+#define NH_FLD_IPSEC_AH_ALL_FIELDS ((NH_FLD_IPSEC_AH_SPI << 2) - 1) -+ -+/*************************** IPSEC ESP fields ******************************/ -+#define NH_FLD_IPSEC_ESP_SPI (1) -+#define NH_FLD_IPSEC_ESP_SEQUENCE_NUM (NH_FLD_IPSEC_ESP_SPI << 1) -+#define NH_FLD_IPSEC_ESP_ALL_FIELDS ((NH_FLD_IPSEC_ESP_SPI << 2) - 1) -+ -+#define NH_FLD_IPSEC_ESP_SPI_SIZE 4 -+ -+/*************************** MPLS fields ***********************************/ -+#define NH_FLD_MPLS_LABEL_STACK (1) -+#define NH_FLD_MPLS_LABEL_STACK_ALL_FIELDS \ -+ ((NH_FLD_MPLS_LABEL_STACK << 1) - 1) -+ -+/*************************** MACSEC fields *********************************/ -+#define NH_FLD_MACSEC_SECTAG (1) -+#define NH_FLD_MACSEC_ALL_FIELDS ((NH_FLD_MACSEC_SECTAG << 1) - 1) -+ -+/*************************** GTP fields ************************************/ -+#define NH_FLD_GTP_TEID (1) -+ -+ -+/* Protocol options */ -+ -+/* Ethernet options */ -+#define NH_OPT_ETH_BROADCAST 1 -+#define NH_OPT_ETH_MULTICAST 2 -+#define NH_OPT_ETH_UNICAST 3 -+#define NH_OPT_ETH_BPDU 4 -+ -+#define NH_ETH_IS_MULTICAST_ADDR(addr) (addr[0] & 0x01) -+/* also applicable for broadcast */ -+ -+/* VLAN options */ -+#define NH_OPT_VLAN_CFI 1 -+ -+/* IPV4 options */ -+#define NH_OPT_IPV4_UNICAST 1 -+#define NH_OPT_IPV4_MULTICAST 2 -+#define NH_OPT_IPV4_BROADCAST 3 -+#define NH_OPT_IPV4_OPTION 4 -+#define NH_OPT_IPV4_FRAG 5 -+#define NH_OPT_IPV4_INITIAL_FRAG 6 -+ -+/* IPV6 options */ -+#define NH_OPT_IPV6_UNICAST 1 -+#define NH_OPT_IPV6_MULTICAST 2 -+#define NH_OPT_IPV6_OPTION 3 -+#define NH_OPT_IPV6_FRAG 4 -+#define NH_OPT_IPV6_INITIAL_FRAG 5 -+ -+/* General IP options (may be used for any version) */ -+#define NH_OPT_IP_FRAG 1 -+#define NH_OPT_IP_INITIAL_FRAG 2 -+#define NH_OPT_IP_OPTION 3 -+ -+/* Minenc. options */ -+#define NH_OPT_MINENCAP_SRC_ADDR_PRESENT 1 -+ -+/* GRE. options */ -+#define NH_OPT_GRE_ROUTING_PRESENT 1 -+ -+/* TCP options */ -+#define NH_OPT_TCP_OPTIONS 1 -+#define NH_OPT_TCP_CONTROL_HIGH_BITS 2 -+#define NH_OPT_TCP_CONTROL_LOW_BITS 3 -+ -+/* CAPWAP options */ -+#define NH_OPT_CAPWAP_DTLS 1 -+ -+enum net_prot { -+ NET_PROT_NONE = 0, -+ NET_PROT_PAYLOAD, -+ NET_PROT_ETH, -+ NET_PROT_VLAN, -+ NET_PROT_IPV4, -+ NET_PROT_IPV6, -+ NET_PROT_IP, -+ NET_PROT_TCP, -+ NET_PROT_UDP, -+ NET_PROT_UDP_LITE, -+ NET_PROT_IPHC, -+ NET_PROT_SCTP, -+ NET_PROT_SCTP_CHUNK_DATA, -+ NET_PROT_PPPOE, -+ NET_PROT_PPP, -+ NET_PROT_PPPMUX, -+ NET_PROT_PPPMUX_SUBFRM, -+ NET_PROT_L2TPV2, -+ NET_PROT_L2TPV3_CTRL, -+ NET_PROT_L2TPV3_SESS, -+ NET_PROT_LLC, -+ NET_PROT_LLC_SNAP, -+ NET_PROT_NLPID, -+ NET_PROT_SNAP, -+ NET_PROT_MPLS, -+ NET_PROT_IPSEC_AH, -+ NET_PROT_IPSEC_ESP, -+ NET_PROT_UDP_ENC_ESP, /* RFC 3948 */ -+ NET_PROT_MACSEC, -+ NET_PROT_GRE, -+ NET_PROT_MINENCAP, -+ NET_PROT_DCCP, -+ NET_PROT_ICMP, -+ NET_PROT_IGMP, -+ NET_PROT_ARP, -+ NET_PROT_CAPWAP_DATA, -+ NET_PROT_CAPWAP_CTRL, -+ NET_PROT_RFC2684, -+ NET_PROT_ICMPV6, -+ NET_PROT_FCOE, -+ NET_PROT_FIP, -+ NET_PROT_ISCSI, -+ NET_PROT_GTP, -+ NET_PROT_USER_DEFINED_L2, -+ NET_PROT_USER_DEFINED_L3, -+ NET_PROT_USER_DEFINED_L4, -+ NET_PROT_USER_DEFINED_L5, -+ NET_PROT_USER_DEFINED_SHIM1, -+ NET_PROT_USER_DEFINED_SHIM2, -+ -+ NET_PROT_DUMMY_LAST -+}; -+ -+/*! IEEE8021.Q */ -+#define NH_IEEE8021Q_ETYPE 0x8100 -+#define NH_IEEE8021Q_HDR(etype, pcp, dei, vlan_id) \ -+ ((((uint32_t)(etype & 0xFFFF)) << 16) | \ -+ (((uint32_t)(pcp & 0x07)) << 13) | \ -+ (((uint32_t)(dei & 0x01)) << 12) | \ -+ (((uint32_t)(vlan_id & 0xFFF)))) -+ -+#endif /* __FSL_NET_H */ -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) --- -2.1.0.27.g96db324 - diff --git a/packages/base/any/kernels/3.18.25/patches/add-kernel-patches-for-nxp-arm64-ls2080ardb-based-on.patch b/packages/base/any/kernels/3.18.25/patches/add-kernel-patches-for-nxp-arm64-ls2080ardb-based-on.patch deleted file mode 100644 index 7942b14d..00000000 --- a/packages/base/any/kernels/3.18.25/patches/add-kernel-patches-for-nxp-arm64-ls2080ardb-based-on.patch +++ /dev/null @@ -1,17982 +0,0 @@ -From f64b882ce6cd659cc725a4097c39e5d97441127f Mon Sep 17 00:00:00 2001 -From: Shengzhou Liu -Date: Mon, 1 Aug 2016 12:57:39 +0800 -Subject: [PATCH] Add kernel patches for nxp arm64 ls2080ardb based on 3.18.25 - -This patch integrated a ton of patches to support misc functionalities -(e.g. USB, PCIe, IOMMU, GIC, reboot, etc) on arm64 LS2080ARDB platform. ---- - Documentation/IRQ-domain.txt | 71 + - Documentation/devicetree/bindings/arm/fsl.txt | 15 + - .../devicetree/bindings/pci/designware-pcie.txt | 3 +- - .../devicetree/bindings/powerpc/fsl/board.txt | 14 +- - Documentation/devicetree/bindings/usb/dwc3.txt | 3 +- - MAINTAINERS | 19 + - arch/arm/Kconfig | 3 + - arch/arm/Makefile | 8 +- - arch/arm/boot/dts/Makefile | 12 +- - arch/arm/include/asm/dma-mapping.h | 10 +- - arch/arm/include/asm/mach/pci.h | 12 +- - arch/arm/include/asm/pci.h | 7 - - arch/arm/kernel/bios32.c | 39 +- - arch/arm/mach-iop13xx/msi.c | 10 +- - arch/arm64/Kconfig | 7 +- - arch/arm64/Makefile | 11 +- - arch/arm64/boot/dts/Makefile | 1 + - arch/arm64/boot/dts/arm64-nxp-ls2080ardb-r0.dts | 249 +++ - arch/arm64/boot/dts/fsl-ls2080a.dtsi | 729 +++++++++ - arch/arm64/boot/dts/include/dt-bindings | 1 + - arch/arm64/configs/defconfig | 1 + - arch/arm64/include/asm/mmu_context.h | 43 + - arch/arm64/include/asm/page.h | 6 +- - arch/arm64/include/asm/pgtable-hwdef.h | 7 +- - arch/arm64/kernel/head.S | 37 + - arch/arm64/kernel/smp.c | 1 + - arch/arm64/mm/mmu.c | 7 +- - arch/arm64/mm/proc-macros.S | 10 + - arch/arm64/mm/proc.S | 3 + - arch/ia64/kernel/msi_ia64.c | 8 +- - arch/ia64/sn/kernel/msi_sn.c | 8 +- - arch/mips/pci/msi-octeon.c | 2 +- - arch/mips/pci/msi-xlp.c | 12 +- - arch/mips/pci/pci-xlr.c | 2 +- - arch/powerpc/platforms/512x/mpc5121_ads_cpld.c | 3 +- - arch/powerpc/platforms/cell/axon_msi.c | 8 +- - arch/powerpc/platforms/cell/interrupt.c | 3 +- - arch/powerpc/platforms/embedded6xx/flipper-pic.c | 3 +- - arch/powerpc/platforms/powermac/pic.c | 3 +- - arch/powerpc/platforms/powernv/pci.c | 2 +- - arch/powerpc/platforms/ps3/interrupt.c | 3 +- - arch/powerpc/platforms/pseries/msi.c | 2 +- - arch/powerpc/sysdev/ehv_pic.c | 3 +- - arch/powerpc/sysdev/fsl_msi.c | 6 +- - arch/powerpc/sysdev/i8259.c | 3 +- - arch/powerpc/sysdev/ipic.c | 3 +- - arch/powerpc/sysdev/mpic.c | 3 +- - arch/powerpc/sysdev/mpic_pasemi_msi.c | 6 +- - arch/powerpc/sysdev/mpic_u3msi.c | 6 +- - arch/powerpc/sysdev/ppc4xx_hsta_msi.c | 2 +- - arch/powerpc/sysdev/ppc4xx_msi.c | 2 +- - arch/powerpc/sysdev/qe_lib/qe_ic.c | 3 +- - arch/powerpc/sysdev/xics/ics-opal.c | 2 +- - arch/powerpc/sysdev/xics/ics-rtas.c | 2 +- - arch/powerpc/sysdev/xics/xics-common.c | 3 +- - arch/s390/pci/pci.c | 10 +- - arch/sparc/kernel/pci_msi.c | 10 +- - arch/tile/kernel/pci_gx.c | 8 +- - arch/x86/include/asm/x86_init.h | 3 - - arch/x86/kernel/apic/io_apic.c | 8 +- - arch/x86/kernel/x86_init.c | 10 - - arch/x86/pci/bus_numa.c | 4 +- - arch/x86/pci/xen.c | 19 +- - drivers/acpi/acpi_lpss.c | 8 +- - drivers/acpi/acpi_platform.c | 4 +- - drivers/acpi/resource.c | 17 +- - drivers/base/core.c | 3 + - drivers/base/platform.c | 1 + - drivers/dma/acpi-dma.c | 10 +- - drivers/iommu/Kconfig | 34 +- - drivers/iommu/Makefile | 2 + - drivers/iommu/amd_iommu.c | 6 +- - drivers/iommu/arm-smmu.c | 1382 ++++++++--------- - drivers/iommu/exynos-iommu.c | 2 +- - drivers/iommu/fsl_pamu.c | 1 - - drivers/iommu/intel-iommu.c | 1 + - drivers/iommu/io-pgtable-arm.c | 986 ++++++++++++ - drivers/iommu/io-pgtable.c | 82 + - drivers/iommu/io-pgtable.h | 143 ++ - drivers/iommu/iommu.c | 111 +- - drivers/iommu/ipmmu-vmsa.c | 2 +- - drivers/iommu/irq_remapping.c | 8 - - drivers/iommu/msm_iommu.c | 1 + - drivers/iommu/of_iommu.c | 95 ++ - drivers/iommu/omap-iommu.c | 1 + - drivers/iommu/shmobile-iommu.c | 1 + - drivers/iommu/shmobile-ipmmu.c | 1 - - drivers/iommu/tegra-gart.c | 1 - - drivers/iommu/tegra-smmu.c | 2 +- - drivers/irqchip/Kconfig | 4 + - drivers/irqchip/Makefile | 1 + - drivers/irqchip/irq-armada-370-xp.c | 16 +- - drivers/irqchip/irq-atmel-aic.c | 40 +- - drivers/irqchip/irq-atmel-aic5.c | 65 +- - drivers/irqchip/irq-gic-v3-its.c | 1628 ++++++++++++++++++++ - drivers/irqchip/irq-gic-v3.c | 114 +- - drivers/irqchip/irq-sunxi-nmi.c | 4 +- - drivers/irqchip/irq-tb10x.c | 4 +- - drivers/of/device.c | 84 + - drivers/of/irq.c | 21 + - drivers/of/of_pci.c | 34 +- - drivers/of/platform.c | 139 +- - drivers/pci/Kconfig | 6 + - drivers/pci/bus.c | 18 +- - drivers/pci/host-bridge.c | 22 +- - drivers/pci/host/Kconfig | 17 + - drivers/pci/host/Makefile | 3 + - drivers/pci/host/pci-dra7xx.c | 8 +- - drivers/pci/host/pci-exynos.c | 5 +- - drivers/pci/host/pci-host-generic.c | 229 +-- - drivers/pci/host/pci-keystone-dw.c | 37 +- - drivers/pci/host/pci-keystone.h | 4 +- - drivers/pci/host/pci-layerscape.c | 669 ++++++++ - drivers/pci/host/pci-layerscape.h | 13 + - drivers/pci/host/pci-mvebu.c | 17 +- - drivers/pci/host/pci-tegra.c | 22 +- - drivers/pci/host/pci-xgene-msi.c | 595 +++++++ - drivers/pci/host/pci-xgene.c | 25 +- - drivers/pci/host/pcie-designware.c | 657 +++----- - drivers/pci/host/pcie-designware.h | 23 +- - drivers/pci/host/pcie-rcar.c | 22 +- - drivers/pci/host/pcie-xilinx.c | 64 +- - drivers/pci/msi.c | 528 +++++-- - drivers/pci/pci.h | 21 + - drivers/pci/probe.c | 28 +- - drivers/pci/quirks.c | 10 +- - drivers/pci/search.c | 5 +- - drivers/pci/xen-pcifront.c | 2 +- - drivers/power/reset/Kconfig | 6 + - drivers/power/reset/Makefile | 1 + - drivers/power/reset/ls-reboot.c | 93 ++ - drivers/usb/core/config.c | 3 +- - drivers/usb/core/driver.c | 6 +- - drivers/usb/core/hcd-pci.c | 9 + - drivers/usb/core/hub.c | 66 +- - drivers/usb/core/quirks.c | 6 + - drivers/usb/dwc3/core.c | 76 +- - drivers/usb/dwc3/core.h | 8 + - drivers/usb/dwc3/host.c | 6 + - drivers/usb/host/xhci-pci.c | 114 +- - drivers/usb/host/xhci-ring.c | 6 +- - drivers/usb/host/xhci.c | 28 +- - drivers/usb/host/xhci.h | 3 + - drivers/vfio/pci/vfio_pci_intrs.c | 2 +- - include/asm-generic/msi.h | 32 + - include/asm-generic/vmlinux.lds.h | 2 + - include/linux/acpi.h | 6 +- - include/linux/device.h | 24 + - include/linux/dma-mapping.h | 13 +- - include/linux/fsl/guts.h | 192 +++ - include/linux/iommu.h | 75 +- - include/linux/iopoll.h | 144 ++ - include/linux/irq.h | 67 +- - include/linux/irqchip/arm-gic-v3.h | 153 ++ - include/linux/irqdomain.h | 126 +- - include/linux/irqhandler.h | 14 + - include/linux/msi.h | 199 ++- - include/linux/of_device.h | 3 + - include/linux/of_iommu.h | 25 + - include/linux/of_irq.h | 1 + - include/linux/of_pci.h | 15 +- - include/linux/of_platform.h | 6 + - include/linux/pci.h | 21 +- - include/linux/resource_ext.h | 77 + - include/linux/usb/quirks.h | 3 + - include/trace/events/iommu.h | 31 +- - kernel/irq/Kconfig | 15 + - kernel/irq/Makefile | 1 + - kernel/irq/chip.c | 105 ++ - kernel/irq/generic-chip.c | 36 +- - kernel/irq/irqdomain.c | 585 ++++++- - kernel/irq/manage.c | 2 + - kernel/irq/msi.c | 347 +++++ - kernel/resource.c | 25 + - scripts/Kbuild.include | 6 + - scripts/Makefile.lib | 12 - - 176 files changed, 10196 insertions(+), 2223 deletions(-) - create mode 100644 arch/arm64/boot/dts/arm64-nxp-ls2080ardb-r0.dts - create mode 100644 arch/arm64/boot/dts/fsl-ls2080a.dtsi - create mode 120000 arch/arm64/boot/dts/include/dt-bindings - create mode 100644 drivers/iommu/io-pgtable-arm.c - create mode 100644 drivers/iommu/io-pgtable.c - create mode 100644 drivers/iommu/io-pgtable.h - create mode 100644 drivers/irqchip/irq-gic-v3-its.c - create mode 100644 drivers/pci/host/pci-layerscape.c - create mode 100644 drivers/pci/host/pci-layerscape.h - create mode 100644 drivers/pci/host/pci-xgene-msi.c - create mode 100644 drivers/power/reset/ls-reboot.c - create mode 100644 include/asm-generic/msi.h - create mode 100644 include/linux/fsl/guts.h - create mode 100644 include/linux/iopoll.h - create mode 100644 include/linux/irqhandler.h - create mode 100644 include/linux/resource_ext.h - create mode 100644 kernel/irq/msi.c - -diff --git a/Documentation/IRQ-domain.txt b/Documentation/IRQ-domain.txt -index 8a8b82c..39cfa72 100644 ---- a/Documentation/IRQ-domain.txt -+++ b/Documentation/IRQ-domain.txt -@@ -151,3 +151,74 @@ used and no descriptor gets allocated it is very important to make sure - that the driver using the simple domain call irq_create_mapping() - before any irq_find_mapping() since the latter will actually work - for the static IRQ assignment case. -+ -+==== Hierarchy IRQ domain ==== -+On some architectures, there may be multiple interrupt controllers -+involved in delivering an interrupt from the device to the target CPU. -+Let's look at a typical interrupt delivering path on x86 platforms: -+ -+Device --> IOAPIC -> Interrupt remapping Controller -> Local APIC -> CPU -+ -+There are three interrupt controllers involved: -+1) IOAPIC controller -+2) Interrupt remapping controller -+3) Local APIC controller -+ -+To support such a hardware topology and make software architecture match -+hardware architecture, an irq_domain data structure is built for each -+interrupt controller and those irq_domains are organized into hierarchy. -+When building irq_domain hierarchy, the irq_domain near to the device is -+child and the irq_domain near to CPU is parent. So a hierarchy structure -+as below will be built for the example above. -+ CPU Vector irq_domain (root irq_domain to manage CPU vectors) -+ ^ -+ | -+ Interrupt Remapping irq_domain (manage irq_remapping entries) -+ ^ -+ | -+ IOAPIC irq_domain (manage IOAPIC delivery entries/pins) -+ -+There are four major interfaces to use hierarchy irq_domain: -+1) irq_domain_alloc_irqs(): allocate IRQ descriptors and interrupt -+ controller related resources to deliver these interrupts. -+2) irq_domain_free_irqs(): free IRQ descriptors and interrupt controller -+ related resources associated with these interrupts. -+3) irq_domain_activate_irq(): activate interrupt controller hardware to -+ deliver the interrupt. -+3) irq_domain_deactivate_irq(): deactivate interrupt controller hardware -+ to stop delivering the interrupt. -+ -+Following changes are needed to support hierarchy irq_domain. -+1) a new field 'parent' is added to struct irq_domain; it's used to -+ maintain irq_domain hierarchy information. -+2) a new field 'parent_data' is added to struct irq_data; it's used to -+ build hierarchy irq_data to match hierarchy irq_domains. The irq_data -+ is used to store irq_domain pointer and hardware irq number. -+3) new callbacks are added to struct irq_domain_ops to support hierarchy -+ irq_domain operations. -+ -+With support of hierarchy irq_domain and hierarchy irq_data ready, an -+irq_domain structure is built for each interrupt controller, and an -+irq_data structure is allocated for each irq_domain associated with an -+IRQ. Now we could go one step further to support stacked(hierarchy) -+irq_chip. That is, an irq_chip is associated with each irq_data along -+the hierarchy. A child irq_chip may implement a required action by -+itself or by cooperating with its parent irq_chip. -+ -+With stacked irq_chip, interrupt controller driver only needs to deal -+with the hardware managed by itself and may ask for services from its -+parent irq_chip when needed. So we could achieve a much cleaner -+software architecture. -+ -+For an interrupt controller driver to support hierarchy irq_domain, it -+needs to: -+1) Implement irq_domain_ops.alloc and irq_domain_ops.free -+2) Optionally implement irq_domain_ops.activate and -+ irq_domain_ops.deactivate. -+3) Optionally implement an irq_chip to manage the interrupt controller -+ hardware. -+4) No need to implement irq_domain_ops.map and irq_domain_ops.unmap, -+ they are unused with hierarchy irq_domain. -+ -+Hierarchy irq_domain may also be used to support other architectures, -+such as ARM, ARM64 etc. -diff --git a/Documentation/devicetree/bindings/arm/fsl.txt b/Documentation/devicetree/bindings/arm/fsl.txt -index e935d7d..5c9f338 100644 ---- a/Documentation/devicetree/bindings/arm/fsl.txt -+++ b/Documentation/devicetree/bindings/arm/fsl.txt -@@ -74,3 +74,18 @@ Required root node properties: - i.MX6q generic board - Required root node properties: - - compatible = "fsl,imx6q"; -+ -++Freescale ARMv8 based Layerscape SoC family Device Tree Bindings -++---------------------------------------------------------------- -+ -+LS2080A ARMv8 based Simulator model -+Required root node properties: -+ - compatible = "fsl,ls2080a-simu", "fsl,ls2080a"; -+ -+LS2080A ARMv8 based QDS Board -+Required root node properties: -+ - compatible = "fsl,ls2080a-qds", "fsl,ls2080a"; -+ -+LS2080A ARMv8 based RDB Board -+Required root node properties: -+ - compatible = "fsl,ls2080a-rdb", "fsl,ls2080a"; -diff --git a/Documentation/devicetree/bindings/pci/designware-pcie.txt b/Documentation/devicetree/bindings/pci/designware-pcie.txt -index 9f4faa8..0036ab3 100644 ---- a/Documentation/devicetree/bindings/pci/designware-pcie.txt -+++ b/Documentation/devicetree/bindings/pci/designware-pcie.txt -@@ -14,7 +14,6 @@ Required properties: - - interrupt-map-mask and interrupt-map: standard PCI properties - to define the mapping of the PCIe interface to interrupt - numbers. --- num-lanes: number of lanes to use - - clocks: Must contain an entry for each entry in clock-names. - See ../clocks/clock-bindings.txt for details. - - clock-names: Must include the following entries: -@@ -22,6 +21,8 @@ Required properties: - - "pcie_bus" - - Optional properties: -+- num-lanes: number of lanes to use (this property should be specified unless -+ the link is brought already up in BIOS) - - reset-gpio: gpio pin number of power good signal - - bus-range: PCI bus numbers covered (it is recommended for new devicetrees to - specify this property, to keep backwards compatibility a range of 0x00-0xff -diff --git a/Documentation/devicetree/bindings/powerpc/fsl/board.txt b/Documentation/devicetree/bindings/powerpc/fsl/board.txt -index cff38bd..89c90f4 100644 ---- a/Documentation/devicetree/bindings/powerpc/fsl/board.txt -+++ b/Documentation/devicetree/bindings/powerpc/fsl/board.txt -@@ -21,11 +21,14 @@ Example: - - This is the memory-mapped registers for on board FPGA. - --Required properities: -+Required properties: - - compatible: should be a board-specific string followed by a string - indicating the type of FPGA. Example: -- "fsl,-fpga", "fsl,fpga-pixis" -+ "fsl,-fpga", "fsl,fpga-pixis" or -+ "fsl,-fpga", "fsl,fpga-qixis" - - reg: should contain the address and the length of the FPGA register set. -+ -+Optional properties: - - interrupt-parent: should specify phandle for the interrupt controller. - - interrupts: should specify event (wakeup) IRQ. - -@@ -38,6 +41,13 @@ Example (P1022DS): - interrupts = <8 8 0 0>; - }; - -+Example (LS2080A-RDB): -+ -+ cpld@3,0 { -+ compatible = "fsl,ls2080ardb-fpga", "fsl,fpga-qixis"; -+ reg = <0x3 0 0x10000>; -+ }; -+ - * Freescale BCSR GPIO banks - - Some BCSR registers act as simple GPIO controllers, each such -diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt -index 471366d..1f9900c 100644 ---- a/Documentation/devicetree/bindings/usb/dwc3.txt -+++ b/Documentation/devicetree/bindings/usb/dwc3.txt -@@ -1,6 +1,7 @@ - synopsys DWC3 CORE - --DWC3- USB3 CONTROLLER -+DWC3- USB3 CONTROLLER. Complies to the generic USB binding properties -+ as described in 'usb/generic.txt' - - Required properties: - - compatible: must be "snps,dwc3" -diff --git a/MAINTAINERS b/MAINTAINERS -index c721042..1ae7362 100644 ---- a/MAINTAINERS -+++ b/MAINTAINERS -@@ -1562,6 +1562,7 @@ M: Will Deacon - L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) - S: Maintained - F: drivers/iommu/arm-smmu.c -+F: drivers/iommu/io-pgtable-arm.c - - ARM64 PORT (AARCH64 ARCHITECTURE) - M: Catalin Marinas -@@ -7047,6 +7048,16 @@ S: Maintained - F: Documentation/devicetree/bindings/pci/xgene-pci.txt - F: drivers/pci/host/pci-xgene.c - -+PCI DRIVER FOR FREESCALE LAYERSCAPE -+M: Minghuan Lian -+M: Mingkai Hu -+M: Roy Zang -+L: linuxppc-dev@lists.ozlabs.org -+L: linux-pci@vger.kernel.org -+L: linux-arm-kernel@lists.infradead.org -+S: Maintained -+F: drivers/pci/host/*layerscape* -+ - PCI DRIVER FOR IMX6 - M: Richard Zhu - M: Lucas Stach -@@ -7122,6 +7133,14 @@ L: linux-pci@vger.kernel.org - S: Maintained - F: drivers/pci/host/*spear* - -+PCI MSI DRIVER FOR APPLIEDMICRO XGENE -+M: Duc Dang -+L: linux-pci@vger.kernel.org -+L: linux-arm-kernel@lists.infradead.org -+S: Maintained -+F: Documentation/devicetree/bindings/pci/xgene-pci-msi.txt -+F: drivers/pci/host/pci-xgene-msi.c -+ - PCMCIA SUBSYSTEM - P: Linux PCMCIA Team - L: linux-pcmcia@lists.infradead.org -diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig -index 89c4b5c..29544f0 100644 ---- a/arch/arm/Kconfig -+++ b/arch/arm/Kconfig -@@ -1292,6 +1292,9 @@ config PCI_DOMAINS - bool - depends on PCI - -+config PCI_DOMAINS_GENERIC -+ def_bool PCI_DOMAINS -+ - config PCI_NANOENGINE - bool "BSE nanoEngine PCI support" - depends on SA1100_NANOENGINE -diff --git a/arch/arm/Makefile b/arch/arm/Makefile -index b5d7988..93a30a2 100644 ---- a/arch/arm/Makefile -+++ b/arch/arm/Makefile -@@ -320,8 +320,12 @@ $(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) - - # We use MRPROPER_FILES and CLEAN_FILES now - archclean: -diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index 38c89ca..6e784fa 100644 ---- a/arch/arm/boot/dts/Makefile -+++ b/arch/arm/boot/dts/Makefile -@@ -517,15 +517,7 @@ 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) - 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 -- --dtbs_install: $(addsuffix _dtbinst_, $(dtb-y)) -+always := $(dtb-y) -+clean-files := *.dtb -diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h -index 85738b2..f3c0d95 100644 ---- a/arch/arm/include/asm/dma-mapping.h -+++ b/arch/arm/include/asm/dma-mapping.h -@@ -121,12 +121,14 @@ static inline unsigned long dma_max_pfn(struct device *dev) - } - #define dma_max_pfn(dev) dma_max_pfn(dev) - --static inline int set_arch_dma_coherent_ops(struct device *dev) -+static inline void arch_setup_dma_ops(struct device *dev, u64 dma_base, -+ u64 size, struct iommu_ops *iommu, -+ bool coherent) - { -- set_dma_ops(dev, &arm_coherent_dma_ops); -- return 0; -+ if (coherent) -+ set_dma_ops(dev, &arm_coherent_dma_ops); - } --#define set_arch_dma_coherent_ops(dev) set_arch_dma_coherent_ops(dev) -+#define arch_setup_dma_ops arch_setup_dma_ops - - static inline dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr) - { -diff --git a/arch/arm/include/asm/mach/pci.h b/arch/arm/include/asm/mach/pci.h -index 7fc4278..c074e7a 100644 ---- a/arch/arm/include/asm/mach/pci.h -+++ b/arch/arm/include/asm/mach/pci.h -@@ -19,9 +19,7 @@ struct pci_bus; - struct device; - - struct hw_pci { --#ifdef CONFIG_PCI_DOMAINS -- int domain; --#endif -+ struct msi_controller *msi_ctrl; - struct pci_ops *ops; - int nr_controllers; - void **private_data; -@@ -36,16 +34,14 @@ struct hw_pci { - resource_size_t start, - resource_size_t size, - resource_size_t align); -- void (*add_bus)(struct pci_bus *bus); -- void (*remove_bus)(struct pci_bus *bus); - }; - - /* - * Per-controller structure - */ - struct pci_sys_data { --#ifdef CONFIG_PCI_DOMAINS -- int domain; -+#ifdef CONFIG_PCI_MSI -+ struct msi_controller *msi_ctrl; - #endif - struct list_head node; - int busnr; /* primary bus number */ -@@ -65,8 +61,6 @@ struct pci_sys_data { - resource_size_t start, - resource_size_t size, - resource_size_t align); -- void (*add_bus)(struct pci_bus *bus); -- void (*remove_bus)(struct pci_bus *bus); - void *private_data; /* platform controller private data */ - }; - -diff --git a/arch/arm/include/asm/pci.h b/arch/arm/include/asm/pci.h -index 7e95d85..585dc33 100644 ---- a/arch/arm/include/asm/pci.h -+++ b/arch/arm/include/asm/pci.h -@@ -18,13 +18,6 @@ static inline int pcibios_assign_all_busses(void) - } - - #ifdef CONFIG_PCI_DOMAINS --static inline int pci_domain_nr(struct pci_bus *bus) --{ -- struct pci_sys_data *root = bus->sysdata; -- -- return root->domain; --} -- - static inline int pci_proc_domain(struct pci_bus *bus) - { - return pci_domain_nr(bus); -diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c -index 17a26c1..a5cd259 100644 ---- a/arch/arm/kernel/bios32.c -+++ b/arch/arm/kernel/bios32.c -@@ -18,6 +18,15 @@ - - static int debug_pci; - -+#ifdef CONFIG_PCI_MSI -+struct msi_controller *pcibios_msi_controller(struct pci_dev *dev) -+{ -+ struct pci_sys_data *sysdata = dev->bus->sysdata; -+ -+ return sysdata->msi_ctrl; -+} -+#endif -+ - /* - * We can't use pci_get_device() here since we are - * called from interrupt context. -@@ -360,20 +369,6 @@ void pcibios_fixup_bus(struct pci_bus *bus) - } - EXPORT_SYMBOL(pcibios_fixup_bus); - --void pcibios_add_bus(struct pci_bus *bus) --{ -- struct pci_sys_data *sys = bus->sysdata; -- if (sys->add_bus) -- sys->add_bus(bus); --} -- --void pcibios_remove_bus(struct pci_bus *bus) --{ -- struct pci_sys_data *sys = bus->sysdata; -- if (sys->remove_bus) -- sys->remove_bus(bus); --} -- - /* - * Swizzle the device pin each time we cross a bridge. If a platform does - * not provide a swizzle function, we perform the standard PCI swizzling. -@@ -427,17 +422,16 @@ static int pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) - static int pcibios_init_resources(int busnr, struct pci_sys_data *sys) - { - int ret; -- struct pci_host_bridge_window *window; -+ struct resource_entry *window; - - if (list_empty(&sys->resources)) { - pci_add_resource_offset(&sys->resources, - &iomem_resource, sys->mem_offset); - } - -- list_for_each_entry(window, &sys->resources, list) { -+ resource_list_for_each_entry(window, &sys->resources) - if (resource_type(window->res) == IORESOURCE_IO) - return 0; -- } - - sys->io_res.start = (busnr * SZ_64K) ? : pcibios_min_io; - sys->io_res.end = (busnr + 1) * SZ_64K - 1; -@@ -468,15 +462,13 @@ static void pcibios_init_hw(struct device *parent, struct hw_pci *hw, - if (!sys) - panic("PCI: unable to allocate sys data!"); - --#ifdef CONFIG_PCI_DOMAINS -- sys->domain = hw->domain; -+#ifdef CONFIG_PCI_MSI -+ sys->msi_ctrl = hw->msi_ctrl; - #endif - sys->busnr = busnr; - sys->swizzle = hw->swizzle; - sys->map_irq = hw->map_irq; - sys->align_resource = hw->align_resource; -- sys->add_bus = hw->add_bus; -- sys->remove_bus = hw->remove_bus; - INIT_LIST_HEAD(&sys->resources); - - if (hw->private_data) -@@ -494,8 +486,9 @@ static void pcibios_init_hw(struct device *parent, struct hw_pci *hw, - if (hw->scan) - sys->bus = hw->scan(nr, sys); - else -- sys->bus = pci_scan_root_bus(parent, sys->busnr, -- hw->ops, sys, &sys->resources); -+ sys->bus = pci_scan_root_bus_msi(parent, -+ sys->busnr, hw->ops, sys, -+ &sys->resources, hw->msi_ctrl); - - if (!sys->bus) - panic("PCI: unable to scan bus!"); -diff --git a/arch/arm/mach-iop13xx/msi.c b/arch/arm/mach-iop13xx/msi.c -index e7730cf..9f89e76 100644 ---- a/arch/arm/mach-iop13xx/msi.c -+++ b/arch/arm/mach-iop13xx/msi.c -@@ -126,10 +126,10 @@ static void iop13xx_msi_nop(struct irq_data *d) - static struct irq_chip iop13xx_msi_chip = { - .name = "PCI-MSI", - .irq_ack = iop13xx_msi_nop, -- .irq_enable = unmask_msi_irq, -- .irq_disable = mask_msi_irq, -- .irq_mask = mask_msi_irq, -- .irq_unmask = unmask_msi_irq, -+ .irq_enable = pci_msi_unmask_irq, -+ .irq_disable = pci_msi_mask_irq, -+ .irq_mask = pci_msi_mask_irq, -+ .irq_unmask = pci_msi_unmask_irq, - }; - - int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc) -@@ -153,7 +153,7 @@ int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc) - id = iop13xx_cpu_id(); - msg.data = (id << IOP13XX_MU_MIMR_CORE_SELECT) | (irq & 0x7f); - -- write_msi_msg(irq, &msg); -+ pci_write_msi_msg(irq, &msg); - irq_set_chip_and_handler(irq, &iop13xx_msi_chip, handle_simple_irq); - - return 0; -diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig -index 00b9c48..08e1287 100644 ---- a/arch/arm64/Kconfig -+++ b/arch/arm64/Kconfig -@@ -14,6 +14,7 @@ config ARM64 - select ARM_GIC - select AUDIT_ARCH_COMPAT_GENERIC - select ARM_GIC_V3 -+ select ARM_GIC_V3_ITS if PCI_MSI - select BUILDTIME_EXTABLE_SORT - select CLONE_BACKWARDS - select COMMON_CLK -@@ -166,6 +167,11 @@ config ARCH_XGENE - help - This enables support for AppliedMicro X-Gene SOC Family - -+config ARCH_LAYERSCAPE -+ bool "ARMv8 based Freescale Layerscape SoC family" -+ help -+ This enables support for the Freescale Layerscape SoC family. -+ - endmenu - - menu "Bus support" -@@ -366,7 +372,6 @@ config ARM64_VA_BITS_42 - - config ARM64_VA_BITS_48 - bool "48-bit" -- depends on !ARM_SMMU - - endchoice - -diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile -index 2d54c55..7cf8a29 100644 ---- a/arch/arm64/Makefile -+++ b/arch/arm64/Makefile -@@ -74,8 +74,13 @@ 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 - - PHONY += vdso_install - vdso_install: -@@ -84,11 +89,13 @@ vdso_install: - # We use MRPROPER_FILES and CLEAN_FILES now - archclean: - $(Q)$(MAKE) $(clean)=$(boot) -+ $(Q)$(MAKE) $(clean)=$(boot)/dts - - 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/dts/Makefile b/arch/arm64/boot/dts/Makefile -index f8001a6..93f2fc3 100644 ---- a/arch/arm64/boot/dts/Makefile -+++ b/arch/arm64/boot/dts/Makefile -@@ -1,6 +1,7 @@ - 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 -+dtb-$(CONFIG_ARCH_LAYERSCAPE) += arm64-nxp-ls2080ardb-r0.dtb - - targets += dtbs - targets += $(dtb-y) -diff --git a/arch/arm64/boot/dts/arm64-nxp-ls2080ardb-r0.dts b/arch/arm64/boot/dts/arm64-nxp-ls2080ardb-r0.dts -new file mode 100644 -index 0000000..5da2834 ---- /dev/null -+++ b/arch/arm64/boot/dts/arm64-nxp-ls2080ardb-r0.dts -@@ -0,0 +1,249 @@ -+/* -+ * Device Tree file for NXP LS2080a RDB board -+ * -+ */ -+ -+/dts-v1/; -+ -+#include "fsl-ls2080a.dtsi" -+ -+/ { -+ model = "arm64-nxp-ls2080ardb-r0"; -+ compatible = "fsl,ls2080a-rdb", "fsl,ls2080a"; -+}; -+ -+&esdhc { -+ status = "okay"; -+}; -+ -+&ifc { -+ status = "okay"; -+ #address-cells = <2>; -+ #size-cells = <1>; -+ ranges = <0x0 0x0 0x5 0x80000000 0x08000000 -+ 0x2 0x0 0x5 0x30000000 0x00010000 -+ 0x3 0x0 0x5 0x20000000 0x00010000>; -+ -+ nor@0,0 { -+ #address-cells = <1>; -+ #size-cells = <1>; -+ compatible = "cfi-flash"; -+ reg = <0x0 0x0 0x8000000>; -+ bank-width = <2>; -+ device-width = <1>; -+ -+ partition@0 { -+ /* SoC RCW, this location must not be altered */ -+ reg = <0x0 0x100000>; -+ label = "rcw (RO)"; -+ read-only; -+ }; -+ -+ partition@1 { -+ /* U-Boot image */ -+ reg = <0x100000 0x100000>; -+ label = "uboot"; -+ }; -+ -+ partition@2 { -+ /* U-Boot environment varialbes, 1MB */ -+ reg = <0x200000 0x100000>; -+ label = "uboot-env"; -+ env_size = <0x20000>; -+ }; -+ -+ partition@3 { -+ /* MC firmware, 4MB*/ -+ reg = <0x300000 0x400000>; -+ label = "mc_firmware"; -+ }; -+ -+ partition@4 { -+ /* MC DPL Blob, 1MB */ -+ reg = <0x700000 0x100000>; -+ label = "mc_dpl_blob"; -+ }; -+ -+ partition@5 { -+ /* MC DPC Blob, 1MB */ -+ reg = <0x800000 0x100000>; -+ label = "mc_dpc_blob"; -+ }; -+ -+ partition@6 { -+ /* AIOP FW, 4MB */ -+ reg = <0x900000 0x400000>; -+ label = "aiop_fw"; -+ }; -+ -+ partition@7 { -+ /* DebugServerFW, 2MB */ -+ reg = <0xd00000 0x200000>; -+ label = "DebugServer_fw"; -+ }; -+ }; -+ -+ nand@2,0 { -+ #address-cells = <1>; -+ #size-cells = <1>; -+ compatible = "fsl,ifc-nand"; -+ reg = <0x2 0x0 0x10000>; -+ }; -+ -+ cpld@3,0 { -+ reg = <0x3 0x0 0x10000>; -+ compatible = "fsl,ls2080a-rdb-qixis", "fsl,fpga-qixis"; -+ }; -+ -+}; -+ -+&i2c0 { -+ status = "okay"; -+ pca9547@75 { -+ compatible = "nxp,pca9547"; -+ reg = <0x75>; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ i2c-mux-never-disable; -+ i2c@1 { -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0x01>; -+ rtc@68 { -+ compatible = "dallas,ds3232"; -+ reg = <0x68>; -+ }; -+ }; -+ -+ i2c@3 { -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0x3>; -+ -+ adt7481@4c { -+ compatible = "adi,adt7461"; -+ reg = <0x4c>; -+ }; -+ }; -+ }; -+}; -+ -+&i2c1 { -+ status = "disabled"; -+}; -+ -+&i2c2 { -+ status = "disabled"; -+}; -+ -+&i2c3 { -+ status = "disabled"; -+}; -+ -+&dspi { -+ status = "okay"; -+ dflash0: n25q512a { -+ #address-cells = <1>; -+ #size-cells = <1>; -+ compatible = "st,m25p80"; -+ spi-max-frequency = <3000000>; -+ reg = <0>; -+ }; -+}; -+ -+&qspi { -+ status = "disabled"; -+}; -+ -+&sata0 { -+ status = "okay"; -+}; -+ -+&sata1 { -+ status = "okay"; -+}; -+ -+&usb0 { -+ status = "okay"; -+}; -+ -+&usb1 { -+ status = "okay"; -+}; -+ -+&emdio1 { -+ status = "disabled"; -+ /* CS4340 PHYs */ -+ mdio1_phy1: emdio1_phy@1 { -+ reg = <0x10>; -+ phy-connection-type = "xfi"; -+ }; -+ mdio1_phy2: emdio1_phy@2 { -+ reg = <0x11>; -+ phy-connection-type = "xfi"; -+ }; -+ mdio1_phy3: emdio1_phy@3 { -+ reg = <0x12>; -+ phy-connection-type = "xfi"; -+ }; -+ mdio1_phy4: emdio1_phy@4 { -+ reg = <0x13>; -+ phy-connection-type = "xfi"; -+ }; -+}; -+ -+&emdio2 { -+ /* AQR405 PHYs */ -+ mdio2_phy1: emdio2_phy@1 { -+ compatible = "ethernet-phy-ieee802.3-c45"; -+ interrupts = <0 1 0x4>; /* Level high type */ -+ reg = <0x0>; -+ phy-connection-type = "xfi"; -+ }; -+ mdio2_phy2: emdio2_phy@2 { -+ compatible = "ethernet-phy-ieee802.3-c45"; -+ interrupts = <0 2 0x4>; /* Level high type */ -+ reg = <0x1>; -+ phy-connection-type = "xfi"; -+ }; -+ mdio2_phy3: emdio2_phy@3 { -+ compatible = "ethernet-phy-ieee802.3-c45"; -+ interrupts = <0 4 0x4>; /* Level high type */ -+ reg = <0x2>; -+ phy-connection-type = "xfi"; -+ }; -+ mdio2_phy4: emdio2_phy@4 { -+ compatible = "ethernet-phy-ieee802.3-c45"; -+ interrupts = <0 5 0x4>; /* Level high type */ -+ reg = <0x3>; -+ phy-connection-type = "xfi"; -+ }; -+}; -+ -+/* Update DPMAC connections to external PHYs, under the assumption of -+ * SerDes 0x2a_0x41. This is currently the only SerDes supported on the board. -+ */ -+&dpmac1 { -+ phy-handle = <&mdio1_phy1>; -+}; -+&dpmac2 { -+ phy-handle = <&mdio1_phy2>; -+}; -+&dpmac3 { -+ phy-handle = <&mdio1_phy3>; -+}; -+&dpmac4 { -+ phy-handle = <&mdio1_phy4>; -+}; -+&dpmac5 { -+ phy-handle = <&mdio2_phy1>; -+}; -+&dpmac6 { -+ phy-handle = <&mdio2_phy2>; -+}; -+&dpmac7 { -+ phy-handle = <&mdio2_phy3>; -+}; -+&dpmac8 { -+ phy-handle = <&mdio2_phy4>; -+}; -diff --git a/arch/arm64/boot/dts/fsl-ls2080a.dtsi b/arch/arm64/boot/dts/fsl-ls2080a.dtsi -new file mode 100644 -index 0000000..5e53b04 ---- /dev/null -+++ b/arch/arm64/boot/dts/fsl-ls2080a.dtsi -@@ -0,0 +1,729 @@ -+/* -+ * Device Tree Include file for Freescale Layerscape-2080A family SoC. -+ * -+ * Copyright (C) 2014-2015, Freescale Semiconductor -+ * -+ * Bhupesh Sharma -+ * Harninder Rai -+ * -+ * This file is licensed under the terms of the GNU General Public -+ * License version 2. This program is licensed "as is" without any -+ * warranty of any kind, whether express or implied. -+ */ -+ -+#include -+ -+/memreserve/ 0x80000000 0x00010000; -+ -+/ { -+ compatible = "fsl,ls2080a"; -+ interrupt-parent = <&gic>; -+ #address-cells = <2>; -+ #size-cells = <2>; -+ -+ cpus { -+ #address-cells = <2>; -+ #size-cells = <0>; -+ -+ /* We have 4 clusters having 2 Cortex-A57 cores each */ -+ cpu0: cpu@0 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a57"; -+ reg = <0x0 0x0>; -+ clocks = <&clockgen 1 0>; -+ #cooling-cells = <2>; -+ }; -+ -+ cpu1: cpu@1 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a57"; -+ reg = <0x0 0x1>; -+ clocks = <&clockgen 1 0>; -+ }; -+ -+ cpu2: cpu@100 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a57"; -+ reg = <0x0 0x100>; -+ clocks = <&clockgen 1 1>; -+ #cooling-cells = <2>; -+ }; -+ -+ cpu3: cpu@101 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a57"; -+ reg = <0x0 0x101>; -+ clocks = <&clockgen 1 1>; -+ }; -+ -+ cpu4: cpu@200 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a57"; -+ reg = <0x0 0x200>; -+ clocks = <&clockgen 1 2>; -+ #cooling-cells = <2>; -+ }; -+ -+ cpu5: cpu@201 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a57"; -+ reg = <0x0 0x201>; -+ clocks = <&clockgen 1 2>; -+ }; -+ -+ cpu6: cpu@300 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a57"; -+ reg = <0x0 0x300>; -+ clocks = <&clockgen 1 3>; -+ #cooling-cells = <2>; -+ }; -+ -+ cpu7: cpu@301 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a57"; -+ reg = <0x0 0x301>; -+ clocks = <&clockgen 1 3>; -+ }; -+ }; -+ -+ pmu { -+ compatible = "arm,armv8-pmuv3"; -+ interrupts = <1 7 0x8>; /* PMU PPI, Level low type */ -+ }; -+ -+ gic: interrupt-controller@6000000 { -+ compatible = "arm,gic-v3"; -+ reg = <0x0 0x06000000 0 0x10000>, /* GIC Dist */ -+ <0x0 0x06100000 0 0x100000>, /* GICR (RD_base + SGI_base) */ -+ <0x0 0x0c0c0000 0 0x2000>, /* GICC */ -+ <0x0 0x0c0d0000 0 0x1000>, /* GICH */ -+ <0x0 0x0c0e0000 0 0x20000>; /* GICV */ -+ #interrupt-cells = <3>; -+ #address-cells = <2>; -+ #size-cells = <2>; -+ ranges; -+ interrupt-controller; -+ interrupts = <1 9 0x4>; -+ -+ its: gic-its@6020000 { -+ compatible = "arm,gic-v3-its"; -+ msi-controller; -+ reg = <0x0 0x6020000 0 0x20000>; -+ }; -+ }; -+ -+ sysclk: sysclk { -+ compatible = "fixed-clock"; -+ #clock-cells = <0>; -+ clock-frequency = <100000000>; -+ clock-output-names = "sysclk"; -+ }; -+ -+ clockgen: clocking@1300000 { -+ compatible = "fsl,ls2080a-clockgen"; -+ reg = <0 0x1300000 0 0xa0000>; -+ #clock-cells = <2>; -+ clocks = <&sysclk>; -+ }; -+ -+ tmu: tmu@1f80000 { -+ compatible = "fsl,qoriq-tmu", "fsl,ls2080a-tmu"; -+ reg = <0x0 0x1f80000 0x0 0x10000>; -+ interrupts = <0 23 0x4>; -+ fsl,tmu-range = <0xb0000 0x9002a 0x6004c 0x30062>; -+ fsl,tmu-calibration = <0x00000000 0x00000026 -+ 0x00000001 0x0000002d -+ 0x00000002 0x00000032 -+ 0x00000003 0x00000039 -+ 0x00000004 0x0000003f -+ 0x00000005 0x00000046 -+ 0x00000006 0x0000004d -+ 0x00000007 0x00000054 -+ 0x00000008 0x0000005a -+ 0x00000009 0x00000061 -+ 0x0000000a 0x0000006a -+ 0x0000000b 0x00000071 -+ -+ 0x00010000 0x00000025 -+ 0x00010001 0x0000002c -+ 0x00010002 0x00000035 -+ 0x00010003 0x0000003d -+ 0x00010004 0x00000045 -+ 0x00010005 0x0000004e -+ 0x00010006 0x00000057 -+ 0x00010007 0x00000061 -+ 0x00010008 0x0000006b -+ 0x00010009 0x00000076 -+ -+ 0x00020000 0x00000029 -+ 0x00020001 0x00000033 -+ 0x00020002 0x0000003d -+ 0x00020003 0x00000049 -+ 0x00020004 0x00000056 -+ 0x00020005 0x00000061 -+ 0x00020006 0x0000006d -+ -+ 0x00030000 0x00000021 -+ 0x00030001 0x0000002a -+ 0x00030002 0x0000003c -+ 0x00030003 0x0000004e>; -+ little-endian; -+ #thermal-sensor-cells = <1>; -+ }; -+ -+ thermal-zones { -+ cpu_thermal: cpu-thermal { -+ polling-delay-passive = <1000>; -+ polling-delay = <5000>; -+ -+ thermal-sensors = <&tmu 4>; -+ -+ trips { -+ cpu_alert: cpu-alert { -+ temperature = <75000>; -+ hysteresis = <2000>; -+ type = "passive"; -+ }; -+ cpu_crit: cpu-crit { -+ temperature = <85000>; -+ hysteresis = <2000>; -+ type = "critical"; -+ }; -+ }; -+ -+ cooling-maps { -+ map0 { -+ trip = <&cpu_alert>; -+ cooling-device = -+ <&cpu0 THERMAL_NO_LIMIT -+ THERMAL_NO_LIMIT>; -+ }; -+ map1 { -+ trip = <&cpu_alert>; -+ cooling-device = -+ <&cpu2 THERMAL_NO_LIMIT -+ THERMAL_NO_LIMIT>; -+ }; -+ map2 { -+ trip = <&cpu_alert>; -+ cooling-device = -+ <&cpu4 THERMAL_NO_LIMIT -+ THERMAL_NO_LIMIT>; -+ }; -+ map3 { -+ trip = <&cpu_alert>; -+ cooling-device = -+ <&cpu6 THERMAL_NO_LIMIT -+ THERMAL_NO_LIMIT>; -+ }; -+ }; -+ }; -+ }; -+ -+ serial0: serial@21c0500 { -+ device_type = "serial"; -+ compatible = "fsl,ns16550", "ns16550a"; -+ reg = <0x0 0x21c0500 0x0 0x100>; -+ clocks = <&clockgen 4 3>; -+ interrupts = <0 32 0x4>; /* Level high type */ -+ }; -+ -+ serial1: serial@21c0600 { -+ device_type = "serial"; -+ compatible = "fsl,ns16550", "ns16550a"; -+ reg = <0x0 0x21c0600 0x0 0x100>; -+ clocks = <&clockgen 4 3>; -+ interrupts = <0 32 0x4>; /* Level high type */ -+ }; -+ -+ gpio0: gpio@2300000 { -+ compatible = "fsl,qoriq-gpio"; -+ reg = <0x0 0x2300000 0x0 0x10000>; -+ interrupts = <0 36 0x4>; /* Level high type */ -+ gpio-controller; -+ little-endian; -+ #gpio-cells = <2>; -+ interrupt-controller; -+ #interrupt-cells = <2>; -+ }; -+ -+ gpio1: gpio@2310000 { -+ compatible = "fsl,qoriq-gpio"; -+ reg = <0x0 0x2310000 0x0 0x10000>; -+ interrupts = <0 36 0x4>; /* Level high type */ -+ gpio-controller; -+ little-endian; -+ #gpio-cells = <2>; -+ interrupt-controller; -+ #interrupt-cells = <2>; -+ }; -+ -+ gpio2: gpio@2320000 { -+ compatible = "fsl,qoriq-gpio"; -+ reg = <0x0 0x2320000 0x0 0x10000>; -+ interrupts = <0 37 0x4>; /* Level high type */ -+ gpio-controller; -+ little-endian; -+ #gpio-cells = <2>; -+ interrupt-controller; -+ #interrupt-cells = <2>; -+ }; -+ -+ gpio3: gpio@2330000 { -+ compatible = "fsl,qoriq-gpio"; -+ reg = <0x0 0x2330000 0x0 0x10000>; -+ interrupts = <0 37 0x4>; /* Level high type */ -+ gpio-controller; -+ little-endian; -+ #gpio-cells = <2>; -+ interrupt-controller; -+ #interrupt-cells = <2>; -+ }; -+ -+ /* TODO: WRIOP (CCSR?) */ -+ emdio1: mdio@0x8B96000 { /* WRIOP0: 0x8B8_0000, E-MDIO1: 0x1_6000 */ -+ compatible = "fsl,fman-memac-mdio"; -+ reg = <0x0 0x8B96000 0x0 0x1000>; -+ device_type = "mdio"; /* TODO: is this necessary? */ -+ little-endian; /* force the driver in LE mode */ -+ -+ /* Not necessary on the QDS, but needed on the RDB */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ }; -+ -+ emdio2: mdio@0x8B97000 { /* WRIOP0: 0x8B8_0000, E-MDIO2: 0x1_7000 */ -+ compatible = "fsl,fman-memac-mdio"; -+ reg = <0x0 0x8B97000 0x0 0x1000>; -+ device_type = "mdio"; /* TODO: is this necessary? */ -+ little-endian; /* force the driver in LE mode */ -+ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ }; -+ -+ ifc: ifc@2240000 { -+ compatible = "fsl,ifc", "simple-bus"; -+ reg = <0x0 0x2240000 0x0 0x20000>; -+ interrupts = <0 21 0x4>; /* Level high type */ -+ little-endian; -+ #address-cells = <2>; -+ #size-cells = <1>; -+ -+ ranges = <0 0 0x5 0x80000000 0x08000000 -+ 2 0 0x5 0x30000000 0x00010000 -+ 3 0 0x5 0x20000000 0x00010000>; -+ }; -+ -+ esdhc: esdhc@2140000 { -+ compatible = "fsl,ls2080a-esdhc", "fsl,esdhc"; -+ reg = <0x0 0x2140000 0x0 0x10000>; -+ interrupts = <0 28 0x4>; /* Level high type */ -+ clock-frequency = <0>; -+ voltage-ranges = <1800 1800 3300 3300>; -+ sdhci,auto-cmd12; -+ little-endian; -+ bus-width = <4>; -+ }; -+ -+ ftm0: ftm0@2800000 { -+ compatible = "fsl,ftm-alarm"; -+ reg = <0x0 0x2800000 0x0 0x10000>; -+ interrupts = <0 44 4>; -+ }; -+ -+ reset: reset@1E60000 { -+ compatible = "fsl,ls-reset"; -+ reg = <0x0 0x1E60000 0x0 0x10000>; -+ }; -+ -+ dspi: dspi@2100000 { -+ compatible = "fsl,ls2085a-dspi", "fsl,ls2080a-dspi"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0x0 0x2100000 0x0 0x10000>; -+ interrupts = <0 26 0x4>; /* Level high type */ -+ clocks = <&clockgen 4 3>; -+ clock-names = "dspi"; -+ spi-num-chipselects = <5>; -+ bus-num = <0>; -+ }; -+ -+ i2c0: i2c@2000000 { -+ compatible = "fsl,vf610-i2c"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0x0 0x2000000 0x0 0x10000>; -+ interrupts = <0 34 0x4>; /* Level high type */ -+ clock-names = "i2c"; -+ clocks = <&clockgen 4 3>; -+ }; -+ -+ i2c1: i2c@2010000 { -+ compatible = "fsl,vf610-i2c"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0x0 0x2010000 0x0 0x10000>; -+ interrupts = <0 34 0x4>; /* Level high type */ -+ clock-names = "i2c"; -+ clocks = <&clockgen 4 3>; -+ }; -+ -+ i2c2: i2c@2020000 { -+ compatible = "fsl,vf610-i2c"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0x0 0x2020000 0x0 0x10000>; -+ interrupts = <0 35 0x4>; /* Level high type */ -+ clock-names = "i2c"; -+ clocks = <&clockgen 4 3>; -+ }; -+ -+ i2c3: i2c@2030000 { -+ compatible = "fsl,vf610-i2c"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0x0 0x2030000 0x0 0x10000>; -+ interrupts = <0 35 0x4>; /* Level high type */ -+ clock-names = "i2c"; -+ clocks = <&clockgen 4 3>; -+ }; -+ -+ qspi: quadspi@20c0000 { -+ compatible = "fsl,ls2080a-qspi"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0x0 0x20c0000 0x0 0x10000>, -+ <0x0 0x20000000 0x0 0x10000000>; -+ reg-names = "QuadSPI", "QuadSPI-memory"; -+ interrupts = <0 25 0x4>; /* Level high type */ -+ clocks = <&clockgen 4 3>, <&clockgen 4 3>; -+ clock-names = "qspi_en", "qspi"; -+ }; -+ -+ pcie@3400000 { -+ compatible = "fsl,ls2080a-pcie", "fsl,ls2085a-pcie", -+ "snps,dw-pcie"; -+ reg = <0x00 0x03400000 0x0 0x00100000 /* controller registers */ -+ 0x10 0x00000000 0x0 0x00001000>; /* configuration space */ -+ reg-names = "regs", "config"; -+ interrupts = <0 108 0x4>; /* Level high type */ -+ interrupt-names = "intr"; -+ #address-cells = <3>; -+ #size-cells = <2>; -+ device_type = "pci"; -+ num-lanes = <4>; -+ bus-range = <0x0 0xff>; -+ ranges = <0x81000000 0x0 0x00000000 0x10 0x00010000 0x0 0x00010000 /* downstream I/O */ -+ 0x82000000 0x0 0x40000000 0x10 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */ -+ msi-parent = <&its>; -+ #interrupt-cells = <1>; -+ interrupt-map-mask = <0 0 0 7>; -+ interrupt-map = <0000 0 0 1 &gic 0 0 0 109 4>, -+ <0000 0 0 2 &gic 0 0 0 110 4>, -+ <0000 0 0 3 &gic 0 0 0 111 4>, -+ <0000 0 0 4 &gic 0 0 0 112 4>; -+ }; -+ -+ pcie@3500000 { -+ compatible = "fsl,ls2080a-pcie", "fsl,ls2085a-pcie", -+ "snps,dw-pcie"; -+ reg = <0x00 0x03500000 0x0 0x00100000 /* controller registers */ -+ 0x12 0x00000000 0x0 0x00001000>; /* configuration space */ -+ reg-names = "regs", "config"; -+ interrupts = <0 113 0x4>; /* Level high type */ -+ interrupt-names = "intr"; -+ #address-cells = <3>; -+ #size-cells = <2>; -+ device_type = "pci"; -+ num-lanes = <4>; -+ bus-range = <0x0 0xff>; -+ ranges = <0x81000000 0x0 0x00000000 0x12 0x00010000 0x0 0x00010000 /* downstream I/O */ -+ 0x82000000 0x0 0x40000000 0x12 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */ -+ msi-parent = <&its>; -+ #interrupt-cells = <1>; -+ interrupt-map-mask = <0 0 0 7>; -+ interrupt-map = <0000 0 0 1 &gic 0 0 0 114 4>, -+ <0000 0 0 2 &gic 0 0 0 115 4>, -+ <0000 0 0 3 &gic 0 0 0 116 4>, -+ <0000 0 0 4 &gic 0 0 0 117 4>; -+ }; -+ -+ pcie@3600000 { -+ compatible = "fsl,ls2080a-pcie", "fsl,ls2085a-pcie", -+ "snps,dw-pcie"; -+ reg = <0x00 0x03600000 0x0 0x00100000 /* controller registers */ -+ 0x14 0x00000000 0x0 0x00001000>; /* configuration space */ -+ reg-names = "regs", "config"; -+ interrupts = <0 118 0x4>; /* Level high type */ -+ interrupt-names = "intr"; -+ #address-cells = <3>; -+ #size-cells = <2>; -+ device_type = "pci"; -+ num-lanes = <8>; -+ bus-range = <0x0 0xff>; -+ ranges = <0x81000000 0x0 0x00000000 0x14 0x00010000 0x0 0x00010000 /* downstream I/O */ -+ 0x82000000 0x0 0x40000000 0x14 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */ -+ msi-parent = <&its>; -+ #interrupt-cells = <1>; -+ interrupt-map-mask = <0 0 0 7>; -+ interrupt-map = <0000 0 0 1 &gic 0 0 0 119 4>, -+ <0000 0 0 2 &gic 0 0 0 120 4>, -+ <0000 0 0 3 &gic 0 0 0 121 4>, -+ <0000 0 0 4 &gic 0 0 0 122 4>; -+ }; -+ -+ pcie@3700000 { -+ compatible = "fsl,ls2080a-pcie", "fsl,ls2085a-pcie", -+ "snps,dw-pcie"; -+ reg = <0x00 0x03700000 0x0 0x00100000 /* controller registers */ -+ 0x16 0x00000000 0x0 0x00001000>; /* configuration space */ -+ reg-names = "regs", "config"; -+ interrupts = <0 123 0x4>; /* Level high type */ -+ interrupt-names = "intr"; -+ #address-cells = <3>; -+ #size-cells = <2>; -+ device_type = "pci"; -+ num-lanes = <4>; -+ bus-range = <0x0 0xff>; -+ ranges = <0x81000000 0x0 0x00000000 0x16 0x00010000 0x0 0x00010000 /* downstream I/O */ -+ 0x82000000 0x0 0x40000000 0x16 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */ -+ msi-parent = <&its>; -+ #interrupt-cells = <1>; -+ interrupt-map-mask = <0 0 0 7>; -+ interrupt-map = <0000 0 0 1 &gic 0 0 0 124 4>, -+ <0000 0 0 2 &gic 0 0 0 125 4>, -+ <0000 0 0 3 &gic 0 0 0 126 4>, -+ <0000 0 0 4 &gic 0 0 0 127 4>; -+ }; -+ -+ sata0: sata@3200000 { -+ compatible = "fsl,ls2080a-ahci", "fsl,ls1021a-ahci"; -+ reg = <0x0 0x3200000 0x0 0x10000>; -+ interrupts = <0 133 0x4>; /* Level high type */ -+ clocks = <&clockgen 4 3>; -+ }; -+ -+ sata1: sata@3210000 { -+ compatible = "fsl,ls2080a-ahci", "fsl,ls1021a-ahci"; -+ reg = <0x0 0x3210000 0x0 0x10000>; -+ interrupts = <0 136 0x4>; /* Level high type */ -+ clocks = <&clockgen 4 3>; -+ }; -+ -+ usb0: usb3@3100000 { -+ compatible = "snps,dwc3"; -+ reg = <0x0 0x3100000 0x0 0x10000>; -+ interrupts = <0 80 0x4>; /* Level high type */ -+ dr_mode = "host"; -+ configure-gfladj; -+ }; -+ -+ usb1: usb3@3110000 { -+ compatible = "snps,dwc3"; -+ reg = <0x0 0x3110000 0x0 0x10000>; -+ interrupts = <0 81 0x4>; /* Level high type */ -+ dr_mode = "host"; -+ configure-gfladj; -+ }; -+ -+ smmu: iommu@5000000 { -+ compatible = "arm,mmu-500"; -+ reg = <0 0x5000000 0 0x800000>; -+ #global-interrupts = <12>; -+ interrupts = <0 13 4>, /* global secure fault */ -+ <0 14 4>, /* combined secure interrupt */ -+ <0 15 4>, /* global non-secure fault */ -+ <0 16 4>, /* combined non-secure interrupt */ -+ /* performance counter interrupts 0-7 */ -+ <0 211 4>, -+ <0 212 4>, -+ <0 213 4>, -+ <0 214 4>, -+ <0 215 4>, -+ <0 216 4>, -+ <0 217 4>, -+ <0 218 4>, -+ /* per context interrupt, 64 interrupts */ -+ <0 146 4>, -+ <0 147 4>, -+ <0 148 4>, -+ <0 149 4>, -+ <0 150 4>, -+ <0 151 4>, -+ <0 152 4>, -+ <0 153 4>, -+ <0 154 4>, -+ <0 155 4>, -+ <0 156 4>, -+ <0 157 4>, -+ <0 158 4>, -+ <0 159 4>, -+ <0 160 4>, -+ <0 161 4>, -+ <0 162 4>, -+ <0 163 4>, -+ <0 164 4>, -+ <0 165 4>, -+ <0 166 4>, -+ <0 167 4>, -+ <0 168 4>, -+ <0 169 4>, -+ <0 170 4>, -+ <0 171 4>, -+ <0 172 4>, -+ <0 173 4>, -+ <0 174 4>, -+ <0 175 4>, -+ <0 176 4>, -+ <0 177 4>, -+ <0 178 4>, -+ <0 179 4>, -+ <0 180 4>, -+ <0 181 4>, -+ <0 182 4>, -+ <0 183 4>, -+ <0 184 4>, -+ <0 185 4>, -+ <0 186 4>, -+ <0 187 4>, -+ <0 188 4>, -+ <0 189 4>, -+ <0 190 4>, -+ <0 191 4>, -+ <0 192 4>, -+ <0 193 4>, -+ <0 194 4>, -+ <0 195 4>, -+ <0 196 4>, -+ <0 197 4>, -+ <0 198 4>, -+ <0 199 4>, -+ <0 200 4>, -+ <0 201 4>, -+ <0 202 4>, -+ <0 203 4>, -+ <0 204 4>, -+ <0 205 4>, -+ <0 206 4>, -+ <0 207 4>, -+ <0 208 4>, -+ <0 209 4>; -+ mmu-masters = <&fsl_mc 0x300 0>; -+ }; -+ -+ timer { -+ compatible = "arm,armv8-timer"; -+ interrupts = <1 13 0x1>, /* Physical Secure PPI, edge triggered */ -+ <1 14 0x1>, /* Physical Non-Secure PPI, edge triggered */ -+ <1 11 0x1>, /* Virtual PPI, edge triggered */ -+ <1 10 0x1>; /* Hypervisor PPI, edge triggered */ -+ arm,reread-timer; -+ }; -+ -+ fsl_mc: fsl-mc@80c000000 { -+ compatible = "fsl,qoriq-mc"; -+ #stream-id-cells = <2>; -+ reg = <0x00000008 0x0c000000 0 0x40>, /* MC portal base */ -+ <0x00000000 0x08340000 0 0x40000>; /* MC control reg */ -+ msi-parent = <&its>; -+ #address-cells = <3>; -+ #size-cells = <1>; -+ -+ /* -+ * Region type 0x0 - MC portals -+ * Region type 0x1 - QBMAN portals -+ */ -+ ranges = <0x0 0x0 0x0 0x8 0x0c000000 0x4000000 -+ 0x1 0x0 0x0 0x8 0x18000000 0x8000000>; -+ -+ /* -+ * Define the maximum number of MACs present on the SoC. -+ * They won't necessarily be all probed, since the -+ * Data Path Layout file and the MC firmware can put fewer -+ * actual DPMAC objects on the MC bus. -+ */ -+ dpmacs { -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ dpmac1: dpmac@1 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <1>; -+ }; -+ dpmac2: dpmac@2 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <2>; -+ }; -+ dpmac3: dpmac@3 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <3>; -+ }; -+ dpmac4: dpmac@4 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <4>; -+ }; -+ dpmac5: dpmac@5 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <5>; -+ }; -+ dpmac6: dpmac@6 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <6>; -+ }; -+ dpmac7: dpmac@7 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <7>; -+ }; -+ dpmac8: dpmac@8 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <8>; -+ }; -+ dpmac9: dpmac@9 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <9>; -+ }; -+ dpmac10: dpmac@10 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <0xa>; -+ }; -+ dpmac11: dpmac@11 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <0xb>; -+ }; -+ dpmac12: dpmac@12 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <0xc>; -+ }; -+ dpmac13: dpmac@13 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <0xd>; -+ }; -+ dpmac14: dpmac@14 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <0xe>; -+ }; -+ dpmac15: dpmac@15 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <0xf>; -+ }; -+ dpmac16: dpmac@16 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <0x10>; -+ }; -+ }; -+ }; -+ -+ ccn@4000000 { -+ compatible = "arm,ccn-504"; -+ reg = <0x0 0x04000000 0x0 0x01000000>; -+ interrupts = <0 12 4>; -+ }; -+ -+ memory@80000000 { -+ device_type = "memory"; -+ reg = <0x00000000 0x80000000 0 0x80000000>; -+ /* DRAM space 1 - 2 GB DRAM */ -+ }; -+}; -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/configs/defconfig b/arch/arm64/configs/defconfig -index dd301be..3852a77 100644 ---- a/arch/arm64/configs/defconfig -+++ b/arch/arm64/configs/defconfig -@@ -32,6 +32,7 @@ CONFIG_MODULES=y - CONFIG_MODULE_UNLOAD=y - # CONFIG_BLK_DEV_BSG is not set - # CONFIG_IOSCHED_DEADLINE is not set -+CONFIG_ARCH_LAYERSCAPE=y - CONFIG_ARCH_THUNDER=y - CONFIG_ARCH_VEXPRESS=y - CONFIG_ARCH_XGENE=y -diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h -index 101a42b..8ec41e5 100644 ---- a/arch/arm64/include/asm/mmu_context.h -+++ b/arch/arm64/include/asm/mmu_context.h -@@ -64,6 +64,49 @@ static inline void cpu_set_reserved_ttbr0(void) - : "r" (ttbr)); - } - -+/* -+ * TCR.T0SZ value to use when the ID map is active. Usually equals -+ * TCR_T0SZ(VA_BITS), unless system RAM is positioned very high in -+ * physical memory, in which case it will be smaller. -+ */ -+extern u64 idmap_t0sz; -+ -+static inline bool __cpu_uses_extended_idmap(void) -+{ -+ return (!IS_ENABLED(CONFIG_ARM64_VA_BITS_48) && -+ unlikely(idmap_t0sz != TCR_T0SZ(VA_BITS))); -+} -+ -+static inline void __cpu_set_tcr_t0sz(u64 t0sz) -+{ -+ unsigned long tcr; -+ -+ if (__cpu_uses_extended_idmap()) -+ asm volatile ( -+ " mrs %0, tcr_el1 ;" -+ " bfi %0, %1, %2, %3 ;" -+ " msr tcr_el1, %0 ;" -+ " isb" -+ : "=&r" (tcr) -+ : "r"(t0sz), "I"(TCR_T0SZ_OFFSET), "I"(TCR_TxSZ_WIDTH)); -+} -+ -+/* -+ * Set TCR.T0SZ to the value appropriate for activating the identity map. -+ */ -+static inline void cpu_set_idmap_tcr_t0sz(void) -+{ -+ __cpu_set_tcr_t0sz(idmap_t0sz); -+} -+ -+/* -+ * Set TCR.T0SZ to its default value (based on VA_BITS) -+ */ -+static inline void cpu_set_default_tcr_t0sz(void) -+{ -+ __cpu_set_tcr_t0sz(TCR_T0SZ(VA_BITS)); -+} -+ - static inline void switch_new_context(struct mm_struct *mm) - { - unsigned long flags; -diff --git a/arch/arm64/include/asm/page.h b/arch/arm64/include/asm/page.h -index 22b1623..3d02b18 100644 ---- a/arch/arm64/include/asm/page.h -+++ b/arch/arm64/include/asm/page.h -@@ -33,7 +33,9 @@ - * image. Both require pgd, pud (4 levels only) and pmd tables to (section) - * map the kernel. With the 64K page configuration, swapper and idmap need to - * map to pte level. The swapper also maps the FDT (see __create_page_tables -- * for more information). -+ * for more information). Note that the number of ID map translation levels -+ * could be increased on the fly if system RAM is out of reach for the default -+ * VA range, so 3 pages are reserved in all cases. - */ - #ifdef CONFIG_ARM64_64K_PAGES - #define SWAPPER_PGTABLE_LEVELS (CONFIG_ARM64_PGTABLE_LEVELS) -@@ -42,7 +44,7 @@ - #endif - - #define SWAPPER_DIR_SIZE (SWAPPER_PGTABLE_LEVELS * PAGE_SIZE) --#define IDMAP_DIR_SIZE (SWAPPER_DIR_SIZE) -+#define IDMAP_DIR_SIZE (3 * PAGE_SIZE) - - #ifndef __ASSEMBLY__ - -diff --git a/arch/arm64/include/asm/pgtable-hwdef.h b/arch/arm64/include/asm/pgtable-hwdef.h -index 88174e0..500b74e 100644 ---- a/arch/arm64/include/asm/pgtable-hwdef.h -+++ b/arch/arm64/include/asm/pgtable-hwdef.h -@@ -142,7 +142,12 @@ - /* - * TCR flags. - */ --#define TCR_TxSZ(x) (((UL(64) - (x)) << 16) | ((UL(64) - (x)) << 0)) -+#define TCR_T0SZ_OFFSET 0 -+#define TCR_T1SZ_OFFSET 16 -+#define TCR_T0SZ(x) ((UL(64) - (x)) << TCR_T0SZ_OFFSET) -+#define TCR_T1SZ(x) ((UL(64) - (x)) << TCR_T1SZ_OFFSET) -+#define TCR_TxSZ(x) (TCR_T0SZ(x) | TCR_T1SZ(x)) -+#define TCR_TxSZ_WIDTH 6 - #define TCR_IRGN_NC ((UL(0) << 8) | (UL(0) << 24)) - #define TCR_IRGN_WBWA ((UL(1) << 8) | (UL(1) << 24)) - #define TCR_IRGN_WT ((UL(2) << 8) | (UL(2) << 24)) -diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S -index 2877dd8..ca02239 100644 ---- a/arch/arm64/kernel/head.S -+++ b/arch/arm64/kernel/head.S -@@ -592,6 +592,43 @@ __create_page_tables: - mov x0, x25 // idmap_pg_dir - ldr x3, =KERNEL_START - add x3, x3, x28 // __pa(KERNEL_START) -+ -+#ifndef CONFIG_ARM64_VA_BITS_48 -+#define EXTRA_SHIFT (PGDIR_SHIFT + PAGE_SHIFT - 3) -+#define EXTRA_PTRS (1 << (48 - EXTRA_SHIFT)) -+ -+ /* -+ * If VA_BITS < 48, it may be too small to allow for an ID mapping to be -+ * created that covers system RAM if that is located sufficiently high -+ * in the physical address space. So for the ID map, use an extended -+ * virtual range in that case, by configuring an additional translation -+ * level. -+ * First, we have to verify our assumption that the current value of -+ * VA_BITS was chosen such that all translation levels are fully -+ * utilised, and that lowering T0SZ will always result in an additional -+ * translation level to be configured. -+ */ -+#if VA_BITS != EXTRA_SHIFT -+#error "Mismatch between VA_BITS and page size/number of translation levels" -+#endif -+ -+ /* -+ * Calculate the maximum allowed value for TCR_EL1.T0SZ so that the -+ * entire kernel image can be ID mapped. As T0SZ == (64 - #bits used), -+ * this number conveniently equals the number of leading zeroes in -+ * the physical address of KERNEL_END. -+ */ -+ adrp x5, KERNEL_END -+ clz x5, x5 -+ cmp x5, TCR_T0SZ(VA_BITS) // default T0SZ small enough? -+ b.ge 1f // .. then skip additional level -+ -+ str_l x5, idmap_t0sz, x6 -+ -+ create_table_entry x0, x3, EXTRA_SHIFT, EXTRA_PTRS, x5, x6 -+1: -+#endif -+ - create_pgd_entry x0, x3, x5, x6 - ldr x6, =KERNEL_END - mov x5, x3 // __pa(KERNEL_START) -diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c -index 0ef8789..5657692 100644 ---- a/arch/arm64/kernel/smp.c -+++ b/arch/arm64/kernel/smp.c -@@ -152,6 +152,7 @@ asmlinkage void secondary_start_kernel(void) - */ - cpu_set_reserved_ttbr0(); - flush_tlb_all(); -+ cpu_set_default_tcr_t0sz(); - - preempt_disable(); - trace_hardirqs_off(); -diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c -index f4f8b50..53bbff9 100644 ---- a/arch/arm64/mm/mmu.c -+++ b/arch/arm64/mm/mmu.c -@@ -37,6 +37,8 @@ - - #include "mm.h" - -+u64 idmap_t0sz = TCR_T0SZ(VA_BITS); -+ - /* - * Empty_zero_page is a special page that is used for zero-initialized data - * and COW. -@@ -369,6 +371,7 @@ void __init paging_init(void) - */ - cpu_set_reserved_ttbr0(); - flush_tlb_all(); -+ cpu_set_default_tcr_t0sz(); - } - - /* -@@ -376,8 +379,10 @@ void __init paging_init(void) - */ - void setup_mm_for_reboot(void) - { -- cpu_switch_mm(idmap_pg_dir, &init_mm); -+ cpu_set_reserved_ttbr0(); - flush_tlb_all(); -+ cpu_set_idmap_tcr_t0sz(); -+ cpu_switch_mm(idmap_pg_dir, &init_mm); - } - - /* -diff --git a/arch/arm64/mm/proc-macros.S b/arch/arm64/mm/proc-macros.S -index 005d29e..4c4d93c 100644 ---- a/arch/arm64/mm/proc-macros.S -+++ b/arch/arm64/mm/proc-macros.S -@@ -52,3 +52,13 @@ - mov \reg, #4 // bytes per word - lsl \reg, \reg, \tmp // actual cache line size - .endm -+ -+/* -+ * tcr_set_idmap_t0sz - update TCR.T0SZ so that we can load the ID map -+ */ -+ .macro tcr_set_idmap_t0sz, valreg, tmpreg -+#ifndef CONFIG_ARM64_VA_BITS_48 -+ ldr_l \tmpreg, idmap_t0sz -+ bfi \valreg, \tmpreg, #TCR_T0SZ_OFFSET, #TCR_TxSZ_WIDTH -+#endif -+ .endm -diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S -index 4e778b1..cbea872 100644 ---- a/arch/arm64/mm/proc.S -+++ b/arch/arm64/mm/proc.S -@@ -156,6 +156,7 @@ ENTRY(cpu_do_resume) - msr cpacr_el1, x6 - msr ttbr0_el1, x1 - msr ttbr1_el1, x7 -+ tcr_set_idmap_t0sz x8, x7 - msr tcr_el1, x8 - msr vbar_el1, x9 - msr mdscr_el1, x10 -@@ -233,6 +234,8 @@ ENTRY(__cpu_setup) - */ - ldr x10, =TCR_TxSZ(VA_BITS) | TCR_CACHE_FLAGS | TCR_SMP_FLAGS | \ - TCR_TG_FLAGS | TCR_ASID16 | TCR_TBI0 -+ tcr_set_idmap_t0sz x10, x9 -+ - /* - * Read the PARange bits from ID_AA64MMFR0_EL1 and set the IPS bits in - * TCR_EL1. -diff --git a/arch/ia64/kernel/msi_ia64.c b/arch/ia64/kernel/msi_ia64.c -index 8c3730c..8ae36ea 100644 ---- a/arch/ia64/kernel/msi_ia64.c -+++ b/arch/ia64/kernel/msi_ia64.c -@@ -35,7 +35,7 @@ static int ia64_set_msi_irq_affinity(struct irq_data *idata, - data |= MSI_DATA_VECTOR(irq_to_vector(irq)); - msg.data = data; - -- write_msi_msg(irq, &msg); -+ pci_write_msi_msg(irq, &msg); - cpumask_copy(idata->affinity, cpumask_of(cpu)); - - return 0; -@@ -71,7 +71,7 @@ int ia64_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc) - MSI_DATA_DELIVERY_FIXED | - MSI_DATA_VECTOR(vector); - -- write_msi_msg(irq, &msg); -+ pci_write_msi_msg(irq, &msg); - irq_set_chip_and_handler(irq, &ia64_msi_chip, handle_edge_irq); - - return 0; -@@ -102,8 +102,8 @@ static int ia64_msi_retrigger_irq(struct irq_data *data) - */ - static struct irq_chip ia64_msi_chip = { - .name = "PCI-MSI", -- .irq_mask = mask_msi_irq, -- .irq_unmask = unmask_msi_irq, -+ .irq_mask = pci_msi_mask_irq, -+ .irq_unmask = pci_msi_unmask_irq, - .irq_ack = ia64_ack_msi_irq, - #ifdef CONFIG_SMP - .irq_set_affinity = ia64_set_msi_irq_affinity, -diff --git a/arch/ia64/sn/kernel/msi_sn.c b/arch/ia64/sn/kernel/msi_sn.c -index 446e779..a0eb27b 100644 ---- a/arch/ia64/sn/kernel/msi_sn.c -+++ b/arch/ia64/sn/kernel/msi_sn.c -@@ -145,7 +145,7 @@ int sn_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *entry) - msg.data = 0x100 + irq; - - irq_set_msi_desc(irq, entry); -- write_msi_msg(irq, &msg); -+ pci_write_msi_msg(irq, &msg); - irq_set_chip_and_handler(irq, &sn_msi_chip, handle_edge_irq); - - return 0; -@@ -205,7 +205,7 @@ static int sn_set_msi_irq_affinity(struct irq_data *data, - msg.address_hi = (u32)(bus_addr >> 32); - msg.address_lo = (u32)(bus_addr & 0x00000000ffffffff); - -- write_msi_msg(irq, &msg); -+ pci_write_msi_msg(irq, &msg); - cpumask_copy(data->affinity, cpu_mask); - - return 0; -@@ -228,8 +228,8 @@ static int sn_msi_retrigger_irq(struct irq_data *data) - - static struct irq_chip sn_msi_chip = { - .name = "PCI-MSI", -- .irq_mask = mask_msi_irq, -- .irq_unmask = unmask_msi_irq, -+ .irq_mask = pci_msi_mask_irq, -+ .irq_unmask = pci_msi_unmask_irq, - .irq_ack = sn_ack_msi_irq, - #ifdef CONFIG_SMP - .irq_set_affinity = sn_set_msi_irq_affinity, -diff --git a/arch/mips/pci/msi-octeon.c b/arch/mips/pci/msi-octeon.c -index 63bbe07..cffaaf4 100644 ---- a/arch/mips/pci/msi-octeon.c -+++ b/arch/mips/pci/msi-octeon.c -@@ -178,7 +178,7 @@ msi_irq_allocated: - pci_write_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, control); - - irq_set_msi_desc(irq, desc); -- write_msi_msg(irq, &msg); -+ pci_write_msi_msg(irq, &msg); - return 0; - } - -diff --git a/arch/mips/pci/msi-xlp.c b/arch/mips/pci/msi-xlp.c -index f7ac3ed..6a40f24 100644 ---- a/arch/mips/pci/msi-xlp.c -+++ b/arch/mips/pci/msi-xlp.c -@@ -217,7 +217,7 @@ static void xlp_msix_mask_ack(struct irq_data *d) - - msixvec = nlm_irq_msixvec(d->irq); - link = nlm_irq_msixlink(msixvec); -- mask_msi_irq(d); -+ pci_msi_mask_irq(d); - md = irq_data_get_irq_handler_data(d); - - /* Ack MSI on bridge */ -@@ -239,10 +239,10 @@ static void xlp_msix_mask_ack(struct irq_data *d) - - static struct irq_chip xlp_msix_chip = { - .name = "XLP-MSIX", -- .irq_enable = unmask_msi_irq, -- .irq_disable = mask_msi_irq, -+ .irq_enable = pci_msi_unmask_irq, -+ .irq_disable = pci_msi_mask_irq, - .irq_mask_ack = xlp_msix_mask_ack, -- .irq_unmask = unmask_msi_irq, -+ .irq_unmask = pci_msi_unmask_irq, - }; - - void arch_teardown_msi_irq(unsigned int irq) -@@ -345,7 +345,7 @@ static int xlp_setup_msi(uint64_t lnkbase, int node, int link, - if (ret < 0) - return ret; - -- write_msi_msg(xirq, &msg); -+ pci_write_msi_msg(xirq, &msg); - return 0; - } - -@@ -446,7 +446,7 @@ static int xlp_setup_msix(uint64_t lnkbase, int node, int link, - if (ret < 0) - return ret; - -- write_msi_msg(xirq, &msg); -+ pci_write_msi_msg(xirq, &msg); - return 0; - } - -diff --git a/arch/mips/pci/pci-xlr.c b/arch/mips/pci/pci-xlr.c -index 0dde803..26d2dab 100644 ---- a/arch/mips/pci/pci-xlr.c -+++ b/arch/mips/pci/pci-xlr.c -@@ -260,7 +260,7 @@ int arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc) - if (ret < 0) - return ret; - -- write_msi_msg(irq, &msg); -+ pci_write_msi_msg(irq, &msg); - return 0; - } - #endif -diff --git a/arch/powerpc/platforms/512x/mpc5121_ads_cpld.c b/arch/powerpc/platforms/512x/mpc5121_ads_cpld.c -index ca3a062..11090ab 100644 ---- a/arch/powerpc/platforms/512x/mpc5121_ads_cpld.c -+++ b/arch/powerpc/platforms/512x/mpc5121_ads_cpld.c -@@ -123,7 +123,8 @@ cpld_pic_cascade(unsigned int irq, struct irq_desc *desc) - } - - static int --cpld_pic_host_match(struct irq_domain *h, struct device_node *node) -+cpld_pic_host_match(struct irq_domain *h, struct device_node *node, -+ enum irq_domain_bus_token bus_token) - { - return cpld_pic_node == node; - } -diff --git a/arch/powerpc/platforms/cell/axon_msi.c b/arch/powerpc/platforms/cell/axon_msi.c -index 862b327..0883994 100644 ---- a/arch/powerpc/platforms/cell/axon_msi.c -+++ b/arch/powerpc/platforms/cell/axon_msi.c -@@ -279,7 +279,7 @@ static int axon_msi_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) - - irq_set_msi_desc(virq, entry); - msg.data = virq; -- write_msi_msg(virq, &msg); -+ pci_write_msi_msg(virq, &msg); - } - - return 0; -@@ -301,9 +301,9 @@ static void axon_msi_teardown_msi_irqs(struct pci_dev *dev) - } - - static struct irq_chip msic_irq_chip = { -- .irq_mask = mask_msi_irq, -- .irq_unmask = unmask_msi_irq, -- .irq_shutdown = mask_msi_irq, -+ .irq_mask = pci_msi_mask_irq, -+ .irq_unmask = pci_msi_unmask_irq, -+ .irq_shutdown = pci_msi_mask_irq, - .name = "AXON-MSI", - }; - -diff --git a/arch/powerpc/platforms/cell/interrupt.c b/arch/powerpc/platforms/cell/interrupt.c -index 28e558d..109d236 100644 ---- a/arch/powerpc/platforms/cell/interrupt.c -+++ b/arch/powerpc/platforms/cell/interrupt.c -@@ -222,7 +222,8 @@ void iic_request_IPIs(void) - #endif /* CONFIG_SMP */ - - --static int iic_host_match(struct irq_domain *h, struct device_node *node) -+static int iic_host_match(struct irq_domain *h, struct device_node *node, -+ enum irq_domain_bus_token bus_token) - { - return of_device_is_compatible(node, - "IBM,CBEA-Internal-Interrupt-Controller"); -diff --git a/arch/powerpc/platforms/embedded6xx/flipper-pic.c b/arch/powerpc/platforms/embedded6xx/flipper-pic.c -index 4cde8e7..b7866e0 100644 ---- a/arch/powerpc/platforms/embedded6xx/flipper-pic.c -+++ b/arch/powerpc/platforms/embedded6xx/flipper-pic.c -@@ -108,7 +108,8 @@ static int flipper_pic_map(struct irq_domain *h, unsigned int virq, - return 0; - } - --static int flipper_pic_match(struct irq_domain *h, struct device_node *np) -+static int flipper_pic_match(struct irq_domain *h, struct device_node *np, -+ enum irq_domain_bus_token bus_token) - { - return 1; - } -diff --git a/arch/powerpc/platforms/powermac/pic.c b/arch/powerpc/platforms/powermac/pic.c -index 4c24bf6..246cab4 100644 ---- a/arch/powerpc/platforms/powermac/pic.c -+++ b/arch/powerpc/platforms/powermac/pic.c -@@ -268,7 +268,8 @@ static struct irqaction gatwick_cascade_action = { - .name = "cascade", - }; - --static int pmac_pic_host_match(struct irq_domain *h, struct device_node *node) -+static int pmac_pic_host_match(struct irq_domain *h, struct device_node *node, -+ enum irq_domain_bus_token bus_token) - { - /* We match all, we don't always have a node anyway */ - return 1; -diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c -index 9ff55d5..019991d 100644 ---- a/arch/powerpc/platforms/powernv/pci.c -+++ b/arch/powerpc/platforms/powernv/pci.c -@@ -90,7 +90,7 @@ static int pnv_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) - return rc; - } - irq_set_msi_desc(virq, entry); -- write_msi_msg(virq, &msg); -+ pci_write_msi_msg(virq, &msg); - } - return 0; - } -diff --git a/arch/powerpc/platforms/ps3/interrupt.c b/arch/powerpc/platforms/ps3/interrupt.c -index 5f3b232..df0c086 100644 ---- a/arch/powerpc/platforms/ps3/interrupt.c -+++ b/arch/powerpc/platforms/ps3/interrupt.c -@@ -678,7 +678,8 @@ static int ps3_host_map(struct irq_domain *h, unsigned int virq, - return 0; - } - --static int ps3_host_match(struct irq_domain *h, struct device_node *np) -+static int ps3_host_match(struct irq_domain *h, struct device_node *np, -+ enum irq_domain_bus_token bus_token) - { - /* Match all */ - return 1; -diff --git a/arch/powerpc/platforms/pseries/msi.c b/arch/powerpc/platforms/pseries/msi.c -index 8b909e9..691a154 100644 ---- a/arch/powerpc/platforms/pseries/msi.c -+++ b/arch/powerpc/platforms/pseries/msi.c -@@ -476,7 +476,7 @@ again: - irq_set_msi_desc(virq, entry); - - /* Read config space back so we can restore after reset */ -- __read_msi_msg(entry, &msg); -+ __pci_read_msi_msg(entry, &msg); - entry->msg = msg; - } - -diff --git a/arch/powerpc/sysdev/ehv_pic.c b/arch/powerpc/sysdev/ehv_pic.c -index 2d20f10..eca0b00 100644 ---- a/arch/powerpc/sysdev/ehv_pic.c -+++ b/arch/powerpc/sysdev/ehv_pic.c -@@ -177,7 +177,8 @@ unsigned int ehv_pic_get_irq(void) - return irq_linear_revmap(global_ehv_pic->irqhost, irq); - } - --static int ehv_pic_host_match(struct irq_domain *h, struct device_node *node) -+static int ehv_pic_host_match(struct irq_domain *h, struct device_node *node, -+ enum irq_domain_bus_token bus_token) - { - /* Exact match, unless ehv_pic node is NULL */ - return h->of_node == NULL || h->of_node == node; -diff --git a/arch/powerpc/sysdev/fsl_msi.c b/arch/powerpc/sysdev/fsl_msi.c -index ea6b3a1..f13282c 100644 ---- a/arch/powerpc/sysdev/fsl_msi.c -+++ b/arch/powerpc/sysdev/fsl_msi.c -@@ -82,8 +82,8 @@ static void fsl_msi_print_chip(struct irq_data *irqd, struct seq_file *p) - - - static struct irq_chip fsl_msi_chip = { -- .irq_mask = mask_msi_irq, -- .irq_unmask = unmask_msi_irq, -+ .irq_mask = pci_msi_mask_irq, -+ .irq_unmask = pci_msi_unmask_irq, - .irq_ack = fsl_msi_end_irq, - .irq_print_chip = fsl_msi_print_chip, - }; -@@ -243,7 +243,7 @@ static int fsl_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) - irq_set_msi_desc(virq, entry); - - fsl_compose_msi_msg(pdev, hwirq, &msg, msi_data); -- write_msi_msg(virq, &msg); -+ pci_write_msi_msg(virq, &msg); - } - return 0; - -diff --git a/arch/powerpc/sysdev/i8259.c b/arch/powerpc/sysdev/i8259.c -index 45598da..8c3756c 100644 ---- a/arch/powerpc/sysdev/i8259.c -+++ b/arch/powerpc/sysdev/i8259.c -@@ -162,7 +162,8 @@ static struct resource pic_edgectrl_iores = { - .flags = IORESOURCE_BUSY, - }; - --static int i8259_host_match(struct irq_domain *h, struct device_node *node) -+static int i8259_host_match(struct irq_domain *h, struct device_node *node, -+ enum irq_domain_bus_token bus_token) - { - return h->of_node == NULL || h->of_node == node; - } -diff --git a/arch/powerpc/sysdev/ipic.c b/arch/powerpc/sysdev/ipic.c -index b50f978..1b9b00f 100644 ---- a/arch/powerpc/sysdev/ipic.c -+++ b/arch/powerpc/sysdev/ipic.c -@@ -672,7 +672,8 @@ static struct irq_chip ipic_edge_irq_chip = { - .irq_set_type = ipic_set_irq_type, - }; - --static int ipic_host_match(struct irq_domain *h, struct device_node *node) -+static int ipic_host_match(struct irq_domain *h, struct device_node *node, -+ enum irq_domain_bus_token bus_token) - { - /* Exact match, unless ipic node is NULL */ - return h->of_node == NULL || h->of_node == node; -diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c -index 89cec0e..bf6f77e 100644 ---- a/arch/powerpc/sysdev/mpic.c -+++ b/arch/powerpc/sysdev/mpic.c -@@ -1009,7 +1009,8 @@ static struct irq_chip mpic_irq_ht_chip = { - #endif /* CONFIG_MPIC_U3_HT_IRQS */ - - --static int mpic_host_match(struct irq_domain *h, struct device_node *node) -+static int mpic_host_match(struct irq_domain *h, struct device_node *node, -+ enum irq_domain_bus_token bus_token) - { - /* Exact match, unless mpic node is NULL */ - return h->of_node == NULL || h->of_node == node; -diff --git a/arch/powerpc/sysdev/mpic_pasemi_msi.c b/arch/powerpc/sysdev/mpic_pasemi_msi.c -index a6add4a..5a4c474 100644 ---- a/arch/powerpc/sysdev/mpic_pasemi_msi.c -+++ b/arch/powerpc/sysdev/mpic_pasemi_msi.c -@@ -42,7 +42,7 @@ static struct mpic *msi_mpic; - static void mpic_pasemi_msi_mask_irq(struct irq_data *data) - { - pr_debug("mpic_pasemi_msi_mask_irq %d\n", data->irq); -- mask_msi_irq(data); -+ pci_msi_mask_irq(data); - mpic_mask_irq(data); - } - -@@ -50,7 +50,7 @@ static void mpic_pasemi_msi_unmask_irq(struct irq_data *data) - { - pr_debug("mpic_pasemi_msi_unmask_irq %d\n", data->irq); - mpic_unmask_irq(data); -- unmask_msi_irq(data); -+ pci_msi_unmask_irq(data); - } - - static struct irq_chip mpic_pasemi_msi_chip = { -@@ -138,7 +138,7 @@ static int pasemi_msi_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) - * register to generate MSI [512...1023] - */ - msg.data = hwirq-0x200; -- write_msi_msg(virq, &msg); -+ pci_write_msi_msg(virq, &msg); - } - - return 0; -diff --git a/arch/powerpc/sysdev/mpic_u3msi.c b/arch/powerpc/sysdev/mpic_u3msi.c -index db35a40..65880cc 100644 ---- a/arch/powerpc/sysdev/mpic_u3msi.c -+++ b/arch/powerpc/sysdev/mpic_u3msi.c -@@ -25,14 +25,14 @@ static struct mpic *msi_mpic; - - static void mpic_u3msi_mask_irq(struct irq_data *data) - { -- mask_msi_irq(data); -+ pci_msi_mask_irq(data); - mpic_mask_irq(data); - } - - static void mpic_u3msi_unmask_irq(struct irq_data *data) - { - mpic_unmask_irq(data); -- unmask_msi_irq(data); -+ pci_msi_unmask_irq(data); - } - - static struct irq_chip mpic_u3msi_chip = { -@@ -172,7 +172,7 @@ static int u3msi_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) - printk("u3msi: allocated virq 0x%x (hw 0x%x) addr 0x%lx\n", - virq, hwirq, (unsigned long)addr); - msg.data = hwirq; -- write_msi_msg(virq, &msg); -+ pci_write_msi_msg(virq, &msg); - - hwirq++; - } -diff --git a/arch/powerpc/sysdev/ppc4xx_hsta_msi.c b/arch/powerpc/sysdev/ppc4xx_hsta_msi.c -index a6a4dbd..908105f 100644 ---- a/arch/powerpc/sysdev/ppc4xx_hsta_msi.c -+++ b/arch/powerpc/sysdev/ppc4xx_hsta_msi.c -@@ -85,7 +85,7 @@ static int hsta_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) - msi_bitmap_free_hwirqs(&ppc4xx_hsta_msi.bmp, irq, 1); - return -EINVAL; - } -- write_msi_msg(hwirq, &msg); -+ pci_write_msi_msg(hwirq, &msg); - } - - return 0; -diff --git a/arch/powerpc/sysdev/ppc4xx_msi.c b/arch/powerpc/sysdev/ppc4xx_msi.c -index 85d9c18..c6df3e2 100644 ---- a/arch/powerpc/sysdev/ppc4xx_msi.c -+++ b/arch/powerpc/sysdev/ppc4xx_msi.c -@@ -116,7 +116,7 @@ static int ppc4xx_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) - - irq_set_msi_desc(virq, entry); - msg.data = int_no; -- write_msi_msg(virq, &msg); -+ pci_write_msi_msg(virq, &msg); - } - return 0; - } -diff --git a/arch/powerpc/sysdev/qe_lib/qe_ic.c b/arch/powerpc/sysdev/qe_lib/qe_ic.c -index b2b87c3..a433b3d 100644 ---- a/arch/powerpc/sysdev/qe_lib/qe_ic.c -+++ b/arch/powerpc/sysdev/qe_lib/qe_ic.c -@@ -245,7 +245,8 @@ static struct irq_chip qe_ic_irq_chip = { - .irq_mask_ack = qe_ic_mask_irq, - }; - --static int qe_ic_host_match(struct irq_domain *h, struct device_node *node) -+static int qe_ic_host_match(struct irq_domain *h, struct device_node *node, -+ enum irq_domain_bus_token bus_token) - { - /* Exact match, unless qe_ic node is NULL */ - return h->of_node == NULL || h->of_node == node; -diff --git a/arch/powerpc/sysdev/xics/ics-opal.c b/arch/powerpc/sysdev/xics/ics-opal.c -index 3c6ee1b..4ba554e 100644 ---- a/arch/powerpc/sysdev/xics/ics-opal.c -+++ b/arch/powerpc/sysdev/xics/ics-opal.c -@@ -73,7 +73,7 @@ static unsigned int ics_opal_startup(struct irq_data *d) - * at that level, so we do it here by hand. - */ - if (d->msi_desc) -- unmask_msi_irq(d); -+ pci_msi_unmask_irq(d); - #endif - - /* unmask it */ -diff --git a/arch/powerpc/sysdev/xics/ics-rtas.c b/arch/powerpc/sysdev/xics/ics-rtas.c -index 936575d..bc81335 100644 ---- a/arch/powerpc/sysdev/xics/ics-rtas.c -+++ b/arch/powerpc/sysdev/xics/ics-rtas.c -@@ -76,7 +76,7 @@ static unsigned int ics_rtas_startup(struct irq_data *d) - * at that level, so we do it here by hand. - */ - if (d->msi_desc) -- unmask_msi_irq(d); -+ pci_msi_unmask_irq(d); - #endif - /* unmask it */ - ics_rtas_unmask_irq(d); -diff --git a/arch/powerpc/sysdev/xics/xics-common.c b/arch/powerpc/sysdev/xics/xics-common.c -index fe0cca4..13ab716 100644 ---- a/arch/powerpc/sysdev/xics/xics-common.c -+++ b/arch/powerpc/sysdev/xics/xics-common.c -@@ -300,7 +300,8 @@ int xics_get_irq_server(unsigned int virq, const struct cpumask *cpumask, - } - #endif /* CONFIG_SMP */ - --static int xics_host_match(struct irq_domain *h, struct device_node *node) -+static int xics_host_match(struct irq_domain *h, struct device_node *node, -+ enum irq_domain_bus_token bus_token) - { - struct ics *ics; - -diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c -index 2fa7b14..d59c825 100644 ---- a/arch/s390/pci/pci.c -+++ b/arch/s390/pci/pci.c -@@ -50,8 +50,8 @@ static DEFINE_SPINLOCK(zpci_list_lock); - - static struct irq_chip zpci_irq_chip = { - .name = "zPCI", -- .irq_unmask = unmask_msi_irq, -- .irq_mask = mask_msi_irq, -+ .irq_unmask = pci_msi_unmask_irq, -+ .irq_mask = pci_msi_mask_irq, - }; - - static DECLARE_BITMAP(zpci_domain, ZPCI_NR_DEVICES); -@@ -403,7 +403,7 @@ int arch_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) - msg.data = hwirq; - msg.address_lo = zdev->msi_addr & 0xffffffff; - msg.address_hi = zdev->msi_addr >> 32; -- write_msi_msg(irq, &msg); -+ pci_write_msi_msg(irq, &msg); - airq_iv_set_data(zdev->aibv, hwirq, irq); - hwirq++; - } -@@ -448,9 +448,9 @@ void arch_teardown_msi_irqs(struct pci_dev *pdev) - /* Release MSI interrupts */ - list_for_each_entry(msi, &pdev->msi_list, list) { - if (msi->msi_attrib.is_msix) -- default_msix_mask_irq(msi, 1); -+ __pci_msix_desc_mask_irq(msi, 1); - else -- default_msi_mask_irq(msi, 1, 1); -+ __pci_msi_desc_mask_irq(msi, 1, 1); - irq_set_msi_desc(msi->irq, NULL); - irq_free_desc(msi->irq); - msi->msg.address_lo = 0; -diff --git a/arch/sparc/kernel/pci_msi.c b/arch/sparc/kernel/pci_msi.c -index 580651a..84e16d8 100644 ---- a/arch/sparc/kernel/pci_msi.c -+++ b/arch/sparc/kernel/pci_msi.c -@@ -111,10 +111,10 @@ static void free_msi(struct pci_pbm_info *pbm, int msi_num) - - static struct irq_chip msi_irq = { - .name = "PCI-MSI", -- .irq_mask = mask_msi_irq, -- .irq_unmask = unmask_msi_irq, -- .irq_enable = unmask_msi_irq, -- .irq_disable = mask_msi_irq, -+ .irq_mask = pci_msi_mask_irq, -+ .irq_unmask = pci_msi_unmask_irq, -+ .irq_enable = pci_msi_unmask_irq, -+ .irq_disable = pci_msi_mask_irq, - /* XXX affinity XXX */ - }; - -@@ -161,7 +161,7 @@ static int sparc64_setup_msi_irq(unsigned int *irq_p, - msg.data = msi; - - irq_set_msi_desc(*irq_p, entry); -- write_msi_msg(*irq_p, &msg); -+ pci_write_msi_msg(*irq_p, &msg); - - return 0; - -diff --git a/arch/tile/kernel/pci_gx.c b/arch/tile/kernel/pci_gx.c -index e39f9c5..e717af2 100644 ---- a/arch/tile/kernel/pci_gx.c -+++ b/arch/tile/kernel/pci_gx.c -@@ -1453,7 +1453,7 @@ static struct pci_ops tile_cfg_ops = { - static unsigned int tilegx_msi_startup(struct irq_data *d) - { - if (d->msi_desc) -- unmask_msi_irq(d); -+ pci_msi_unmask_irq(d); - - return 0; - } -@@ -1465,14 +1465,14 @@ static void tilegx_msi_ack(struct irq_data *d) - - static void tilegx_msi_mask(struct irq_data *d) - { -- mask_msi_irq(d); -+ pci_msi_mask_irq(d); - __insn_mtspr(SPR_IPI_MASK_SET_K, 1UL << d->irq); - } - - static void tilegx_msi_unmask(struct irq_data *d) - { - __insn_mtspr(SPR_IPI_MASK_RESET_K, 1UL << d->irq); -- unmask_msi_irq(d); -+ pci_msi_unmask_irq(d); - } - - static struct irq_chip tilegx_msi_chip = { -@@ -1590,7 +1590,7 @@ int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc) - msg.address_hi = msi_addr >> 32; - msg.address_lo = msi_addr & 0xffffffff; - -- write_msi_msg(irq, &msg); -+ pci_write_msi_msg(irq, &msg); - irq_set_chip_and_handler(irq, &tilegx_msi_chip, handle_level_irq); - irq_set_handler_data(irq, controller); - -diff --git a/arch/x86/include/asm/x86_init.h b/arch/x86/include/asm/x86_init.h -index e45e4da..f58a9c7 100644 ---- a/arch/x86/include/asm/x86_init.h -+++ b/arch/x86/include/asm/x86_init.h -@@ -172,7 +172,6 @@ struct x86_platform_ops { - - struct pci_dev; - struct msi_msg; --struct msi_desc; - - struct x86_msi_ops { - int (*setup_msi_irqs)(struct pci_dev *dev, int nvec, int type); -@@ -183,8 +182,6 @@ struct x86_msi_ops { - void (*teardown_msi_irqs)(struct pci_dev *dev); - void (*restore_msi_irqs)(struct pci_dev *dev); - int (*setup_hpet_msi)(unsigned int irq, unsigned int id); -- u32 (*msi_mask_irq)(struct msi_desc *desc, u32 mask, u32 flag); -- u32 (*msix_mask_irq)(struct msi_desc *desc, u32 flag); - }; - - struct IO_APIC_route_entry; -diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c -index 1183d54..7ffe0a2 100644 ---- a/arch/x86/kernel/apic/io_apic.c -+++ b/arch/x86/kernel/apic/io_apic.c -@@ -3158,7 +3158,7 @@ msi_set_affinity(struct irq_data *data, const struct cpumask *mask, bool force) - msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK; - msg.address_lo |= MSI_ADDR_DEST_ID(dest); - -- __write_msi_msg(data->msi_desc, &msg); -+ __pci_write_msi_msg(data->msi_desc, &msg); - - return IRQ_SET_MASK_OK_NOCOPY; - } -@@ -3169,8 +3169,8 @@ msi_set_affinity(struct irq_data *data, const struct cpumask *mask, bool force) - */ - static struct irq_chip msi_chip = { - .name = "PCI-MSI", -- .irq_unmask = unmask_msi_irq, -- .irq_mask = mask_msi_irq, -+ .irq_unmask = pci_msi_unmask_irq, -+ .irq_mask = pci_msi_mask_irq, - .irq_ack = ack_apic_edge, - .irq_set_affinity = msi_set_affinity, - .irq_retrigger = ioapic_retrigger_irq, -@@ -3196,7 +3196,7 @@ int setup_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc, - * MSI message denotes a contiguous group of IRQs, written for 0th IRQ. - */ - if (!irq_offset) -- write_msi_msg(irq, &msg); -+ pci_write_msi_msg(irq, &msg); - - setup_remapped_irq(irq, irq_cfg(irq), chip); - -diff --git a/arch/x86/kernel/x86_init.c b/arch/x86/kernel/x86_init.c -index e48b674..234b072 100644 ---- a/arch/x86/kernel/x86_init.c -+++ b/arch/x86/kernel/x86_init.c -@@ -116,8 +116,6 @@ struct x86_msi_ops x86_msi = { - .teardown_msi_irqs = default_teardown_msi_irqs, - .restore_msi_irqs = default_restore_msi_irqs, - .setup_hpet_msi = default_setup_hpet_msi, -- .msi_mask_irq = default_msi_mask_irq, -- .msix_mask_irq = default_msix_mask_irq, - }; - - /* MSI arch specific hooks */ -@@ -140,14 +138,6 @@ void arch_restore_msi_irqs(struct pci_dev *dev) - { - x86_msi.restore_msi_irqs(dev); - } --u32 arch_msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) --{ -- return x86_msi.msi_mask_irq(desc, mask, flag); --} --u32 arch_msix_mask_irq(struct msi_desc *desc, u32 flag) --{ -- return x86_msi.msix_mask_irq(desc, flag); --} - #endif - - struct x86_io_apic_ops x86_io_apic_ops = { -diff --git a/arch/x86/pci/bus_numa.c b/arch/x86/pci/bus_numa.c -index f3a2cfc..7bcf06a 100644 ---- a/arch/x86/pci/bus_numa.c -+++ b/arch/x86/pci/bus_numa.c -@@ -31,7 +31,7 @@ void x86_pci_root_bus_resources(int bus, struct list_head *resources) - { - struct pci_root_info *info = x86_find_pci_root_info(bus); - struct pci_root_res *root_res; -- struct pci_host_bridge_window *window; -+ struct resource_entry *window; - bool found = false; - - if (!info) -@@ -41,7 +41,7 @@ void x86_pci_root_bus_resources(int bus, struct list_head *resources) - bus); - - /* already added by acpi ? */ -- list_for_each_entry(window, resources, list) -+ resource_list_for_each_entry(window, resources) - if (window->res->flags & IORESOURCE_BUS) { - found = true; - break; -diff --git a/arch/x86/pci/xen.c b/arch/x86/pci/xen.c -index 6b3cf7c..4f6844b 100644 ---- a/arch/x86/pci/xen.c -+++ b/arch/x86/pci/xen.c -@@ -229,7 +229,7 @@ static int xen_hvm_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) - return 1; - - list_for_each_entry(msidesc, &dev->msi_list, list) { -- __read_msi_msg(msidesc, &msg); -+ __pci_read_msi_msg(msidesc, &msg); - pirq = MSI_ADDR_EXT_DEST_ID(msg.address_hi) | - ((msg.address_lo >> MSI_ADDR_DEST_ID_SHIFT) & 0xff); - if (msg.data != XEN_PIRQ_MSI_DATA || -@@ -240,7 +240,7 @@ static int xen_hvm_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) - goto error; - } - xen_msi_compose_msg(dev, pirq, &msg); -- __write_msi_msg(msidesc, &msg); -+ __pci_write_msi_msg(msidesc, &msg); - dev_dbg(&dev->dev, "xen: msi bound to pirq=%d\n", pirq); - } else { - dev_dbg(&dev->dev, -@@ -394,14 +394,7 @@ static void xen_teardown_msi_irq(unsigned int irq) - { - xen_destroy_irq(irq); - } --static u32 xen_nop_msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) --{ -- return 0; --} --static u32 xen_nop_msix_mask_irq(struct msi_desc *desc, u32 flag) --{ -- return 0; --} -+ - #endif - - int __init pci_xen_init(void) -@@ -425,8 +418,7 @@ int __init pci_xen_init(void) - x86_msi.setup_msi_irqs = xen_setup_msi_irqs; - x86_msi.teardown_msi_irq = xen_teardown_msi_irq; - x86_msi.teardown_msi_irqs = xen_teardown_msi_irqs; -- x86_msi.msi_mask_irq = xen_nop_msi_mask_irq; -- x86_msi.msix_mask_irq = xen_nop_msix_mask_irq; -+ pci_msi_ignore_mask = 1; - #endif - return 0; - } -@@ -460,8 +452,7 @@ int __init pci_xen_initial_domain(void) - x86_msi.setup_msi_irqs = xen_initdom_setup_msi_irqs; - x86_msi.teardown_msi_irq = xen_teardown_msi_irq; - x86_msi.restore_msi_irqs = xen_initdom_restore_msi_irqs; -- x86_msi.msi_mask_irq = xen_nop_msi_mask_irq; -- x86_msi.msix_mask_irq = xen_nop_msix_mask_irq; -+ pci_msi_ignore_mask = 1; - #endif - __acpi_register_gsi = acpi_register_gsi_xen; - /* Pre-allocate legacy irqs */ -diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c -index fdb5701..0ad0ce6 100644 ---- a/drivers/acpi/acpi_lpss.c -+++ b/drivers/acpi/acpi_lpss.c -@@ -325,7 +325,7 @@ static int acpi_lpss_create_device(struct acpi_device *adev, - { - struct lpss_device_desc *dev_desc; - struct lpss_private_data *pdata; -- struct resource_list_entry *rentry; -+ struct resource_entry *rentry; - struct list_head resource_list; - struct platform_device *pdev; - int ret; -@@ -345,12 +345,12 @@ static int acpi_lpss_create_device(struct acpi_device *adev, - goto err_out; - - list_for_each_entry(rentry, &resource_list, node) -- if (resource_type(&rentry->res) == IORESOURCE_MEM) { -+ if (resource_type(rentry->res) == IORESOURCE_MEM) { - if (dev_desc->prv_size_override) - pdata->mmio_size = dev_desc->prv_size_override; - else -- pdata->mmio_size = resource_size(&rentry->res); -- pdata->mmio_base = ioremap(rentry->res.start, -+ pdata->mmio_size = resource_size(rentry->res); -+ pdata->mmio_base = ioremap(rentry->res->start, - pdata->mmio_size); - break; - } -diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c -index 6ba8beb..1284138 100644 ---- a/drivers/acpi/acpi_platform.c -+++ b/drivers/acpi/acpi_platform.c -@@ -45,7 +45,7 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev) - struct platform_device *pdev = NULL; - struct acpi_device *acpi_parent; - struct platform_device_info pdevinfo; -- struct resource_list_entry *rentry; -+ struct resource_entry *rentry; - struct list_head resource_list; - struct resource *resources = NULL; - int count; -@@ -71,7 +71,7 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev) - } - count = 0; - list_for_each_entry(rentry, &resource_list, node) -- resources[count++] = rentry->res; -+ resources[count++] = *rentry->res; - - acpi_dev_free_resource_list(&resource_list); - } -diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c -index 2ba8f02..e7f4aa0 100644 ---- a/drivers/acpi/resource.c -+++ b/drivers/acpi/resource.c -@@ -415,12 +415,7 @@ EXPORT_SYMBOL_GPL(acpi_dev_resource_interrupt); - */ - void acpi_dev_free_resource_list(struct list_head *list) - { -- struct resource_list_entry *rentry, *re; -- -- list_for_each_entry_safe(rentry, re, list, node) { -- list_del(&rentry->node); -- kfree(rentry); -- } -+ resource_list_free(list); - } - EXPORT_SYMBOL_GPL(acpi_dev_free_resource_list); - -@@ -435,15 +430,15 @@ struct res_proc_context { - static acpi_status acpi_dev_new_resource_entry(struct resource *r, - struct res_proc_context *c) - { -- struct resource_list_entry *rentry; -+ struct resource_entry *rentry; - -- rentry = kmalloc(sizeof(*rentry), GFP_KERNEL); -+ rentry = resource_list_create_entry(NULL, 0); - if (!rentry) { - c->error = -ENOMEM; - return AE_NO_MEMORY; - } -- rentry->res = *r; -- list_add_tail(&rentry->node, c->list); -+ *rentry->res = *r; -+ resource_list_add_tail(rentry, c->list); - c->count++; - return AE_OK; - } -@@ -503,7 +498,7 @@ static acpi_status acpi_dev_process_resource(struct acpi_resource *ares, - * returned as the final error code. - * - * The resultant struct resource objects are put on the list pointed to by -- * @list, that must be empty initially, as members of struct resource_list_entry -+ * @list, that must be empty initially, as members of struct resource_entry - * objects. Callers of this routine should use %acpi_dev_free_resource_list() to - * free that list. - * -diff --git a/drivers/base/core.c b/drivers/base/core.c -index 842d047..4c7a18f 100644 ---- a/drivers/base/core.c -+++ b/drivers/base/core.c -@@ -661,6 +661,9 @@ void device_initialize(struct device *dev) - INIT_LIST_HEAD(&dev->devres_head); - device_pm_init(dev); - set_dev_node(dev, -1); -+#ifdef CONFIG_GENERIC_MSI_IRQ -+ INIT_LIST_HEAD(&dev->msi_list); -+#endif - } - EXPORT_SYMBOL_GPL(device_initialize); - -diff --git a/drivers/base/platform.c b/drivers/base/platform.c -index 317e0e4..b387fb9 100644 ---- a/drivers/base/platform.c -+++ b/drivers/base/platform.c -@@ -1011,6 +1011,7 @@ int __init platform_bus_init(void) - error = bus_register(&platform_bus_type); - if (error) - device_unregister(&platform_bus); -+ of_platform_register_reconfig_notifier(); - return error; - } - -diff --git a/drivers/dma/acpi-dma.c b/drivers/dma/acpi-dma.c -index de361a1..5a63564 100644 ---- a/drivers/dma/acpi-dma.c -+++ b/drivers/dma/acpi-dma.c -@@ -43,7 +43,7 @@ static int acpi_dma_parse_resource_group(const struct acpi_csrt_group *grp, - { - const struct acpi_csrt_shared_info *si; - struct list_head resource_list; -- struct resource_list_entry *rentry; -+ struct resource_entry *rentry; - resource_size_t mem = 0, irq = 0; - int ret; - -@@ -56,10 +56,10 @@ static int acpi_dma_parse_resource_group(const struct acpi_csrt_group *grp, - return 0; - - list_for_each_entry(rentry, &resource_list, node) { -- if (resource_type(&rentry->res) == IORESOURCE_MEM) -- mem = rentry->res.start; -- else if (resource_type(&rentry->res) == IORESOURCE_IRQ) -- irq = rentry->res.start; -+ if (resource_type(rentry->res) == IORESOURCE_MEM) -+ mem = rentry->res->start; -+ else if (resource_type(rentry->res) == IORESOURCE_IRQ) -+ irq = rentry->res->start; - } - - acpi_dev_free_resource_list(&resource_list); -diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig -index dd51122..2cdcc76 100644 ---- a/drivers/iommu/Kconfig -+++ b/drivers/iommu/Kconfig -@@ -13,9 +13,35 @@ menuconfig IOMMU_SUPPORT - - if IOMMU_SUPPORT - -+menu "Generic IOMMU Pagetable Support" -+ -+# Selected by the actual pagetable implementations -+config IOMMU_IO_PGTABLE -+ bool -+ -+config IOMMU_IO_PGTABLE_LPAE -+ bool "ARMv7/v8 Long Descriptor Format" -+ select IOMMU_IO_PGTABLE -+ help -+ Enable support for the ARM long descriptor pagetable format. -+ This allocator supports 4K/2M/1G, 16K/32M and 64K/512M page -+ sizes at both stage-1 and stage-2, as well as address spaces -+ up to 48-bits in size. -+ -+config IOMMU_IO_PGTABLE_LPAE_SELFTEST -+ bool "LPAE selftests" -+ depends on IOMMU_IO_PGTABLE_LPAE -+ help -+ Enable self-tests for LPAE page table allocator. This performs -+ a series of page-table consistency checks during boot. -+ -+ If unsure, say N here. -+ -+endmenu -+ - config OF_IOMMU - def_bool y -- depends on OF -+ depends on OF && IOMMU_API - - config FSL_PAMU - bool "Freescale IOMMU support" -@@ -291,13 +317,13 @@ config SPAPR_TCE_IOMMU - - config ARM_SMMU - bool "ARM Ltd. System MMU (SMMU) Support" -- depends on ARM64 || (ARM_LPAE && OF) -+ depends on ARM64 || ARM - select IOMMU_API -+ select IOMMU_IO_PGTABLE_LPAE - select ARM_DMA_USE_IOMMU if ARM - help - Support for implementations of the ARM System MMU architecture -- versions 1 and 2. The driver supports both v7l and v8l table -- formats with 4k and 64k page sizes. -+ versions 1 and 2. - - Say Y here if your SoC includes an IOMMU device implementing - the ARM SMMU architecture. -diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile -index 16edef7..269cdd8 100644 ---- a/drivers/iommu/Makefile -+++ b/drivers/iommu/Makefile -@@ -1,6 +1,8 @@ - obj-$(CONFIG_IOMMU_API) += iommu.o - obj-$(CONFIG_IOMMU_API) += iommu-traces.o - obj-$(CONFIG_IOMMU_API) += iommu-sysfs.o -+obj-$(CONFIG_IOMMU_IO_PGTABLE) += io-pgtable.o -+obj-$(CONFIG_IOMMU_IO_PGTABLE_LPAE) += io-pgtable-arm.o - obj-$(CONFIG_OF_IOMMU) += of_iommu.o - obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o - obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o -diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c -index af3daf8..f7131fa 100644 ---- a/drivers/iommu/amd_iommu.c -+++ b/drivers/iommu/amd_iommu.c -@@ -343,8 +343,9 @@ static u16 get_alias(struct device *dev) - */ - if (pci_alias == devid && - PCI_BUS_NUM(ivrs_alias) == pdev->bus->number) { -- pdev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVFN; -- pdev->dma_alias_devfn = ivrs_alias & 0xff; -+ pdev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVID; -+ pdev->dma_alias_devid = PCI_DEVID(pdev->bus->number, -+ ivrs_alias & 0xff); - pr_info("AMD-Vi: Added PCI DMA alias %02x.%d for %s\n", - PCI_SLOT(ivrs_alias), PCI_FUNC(ivrs_alias), - dev_name(dev)); -@@ -3432,6 +3433,7 @@ static const struct iommu_ops amd_iommu_ops = { - .detach_dev = amd_iommu_detach_device, - .map = amd_iommu_map, - .unmap = amd_iommu_unmap, -+ .map_sg = default_iommu_map_sg, - .iova_to_phys = amd_iommu_iova_to_phys, - .pgsize_bitmap = AMD_IOMMU_PGSIZES, - }; -diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c -index 60558f7..10e584b 100644 ---- a/drivers/iommu/arm-smmu.c -+++ b/drivers/iommu/arm-smmu.c -@@ -23,8 +23,6 @@ - * - Stream-matching and stream-indexing - * - v7/v8 long-descriptor format - * - Non-secure access to the SMMU -- * - 4k and 64k pages, with contiguous pte hints. -- * - Up to 48-bit addressing (dependent on VA_BITS) - * - Context fault reporting - */ - -@@ -36,7 +34,7 @@ - #include - #include - #include --#include -+#include - #include - #include - #include -@@ -46,6 +44,16 @@ - - #include - -+#include "io-pgtable.h" -+ -+#ifdef CONFIG_FSL_MC_BUS -+#include <../drivers/staging/fsl-mc/include/mc.h> -+#endif -+ -+#ifdef CONFIG_PCI_LAYERSCAPE -+#include <../drivers/pci/host/pci-layerscape.h> -+#endif -+ - #include - - /* Maximum number of stream IDs assigned to a single device */ -@@ -71,40 +79,6 @@ - ((smmu->options & ARM_SMMU_OPT_SECURE_CFG_ACCESS) \ - ? 0x400 : 0)) - --/* Page table bits */ --#define ARM_SMMU_PTE_XN (((pteval_t)3) << 53) --#define ARM_SMMU_PTE_CONT (((pteval_t)1) << 52) --#define ARM_SMMU_PTE_AF (((pteval_t)1) << 10) --#define ARM_SMMU_PTE_SH_NS (((pteval_t)0) << 8) --#define ARM_SMMU_PTE_SH_OS (((pteval_t)2) << 8) --#define ARM_SMMU_PTE_SH_IS (((pteval_t)3) << 8) --#define ARM_SMMU_PTE_PAGE (((pteval_t)3) << 0) -- --#if PAGE_SIZE == SZ_4K --#define ARM_SMMU_PTE_CONT_ENTRIES 16 --#elif PAGE_SIZE == SZ_64K --#define ARM_SMMU_PTE_CONT_ENTRIES 32 --#else --#define ARM_SMMU_PTE_CONT_ENTRIES 1 --#endif -- --#define ARM_SMMU_PTE_CONT_SIZE (PAGE_SIZE * ARM_SMMU_PTE_CONT_ENTRIES) --#define ARM_SMMU_PTE_CONT_MASK (~(ARM_SMMU_PTE_CONT_SIZE - 1)) -- --/* Stage-1 PTE */ --#define ARM_SMMU_PTE_AP_UNPRIV (((pteval_t)1) << 6) --#define ARM_SMMU_PTE_AP_RDONLY (((pteval_t)2) << 6) --#define ARM_SMMU_PTE_ATTRINDX_SHIFT 2 --#define ARM_SMMU_PTE_nG (((pteval_t)1) << 11) -- --/* Stage-2 PTE */ --#define ARM_SMMU_PTE_HAP_FAULT (((pteval_t)0) << 6) --#define ARM_SMMU_PTE_HAP_READ (((pteval_t)1) << 6) --#define ARM_SMMU_PTE_HAP_WRITE (((pteval_t)2) << 6) --#define ARM_SMMU_PTE_MEMATTR_OIWB (((pteval_t)0xf) << 2) --#define ARM_SMMU_PTE_MEMATTR_NC (((pteval_t)0x5) << 2) --#define ARM_SMMU_PTE_MEMATTR_DEV (((pteval_t)0x1) << 2) -- - /* Configuration registers */ - #define ARM_SMMU_GR0_sCR0 0x0 - #define sCR0_CLIENTPD (1 << 0) -@@ -132,17 +106,12 @@ - #define ARM_SMMU_GR0_sGFSYNR0 0x50 - #define ARM_SMMU_GR0_sGFSYNR1 0x54 - #define ARM_SMMU_GR0_sGFSYNR2 0x58 --#define ARM_SMMU_GR0_PIDR0 0xfe0 --#define ARM_SMMU_GR0_PIDR1 0xfe4 --#define ARM_SMMU_GR0_PIDR2 0xfe8 - - #define ID0_S1TS (1 << 30) - #define ID0_S2TS (1 << 29) - #define ID0_NTS (1 << 28) - #define ID0_SMS (1 << 27) --#define ID0_PTFS_SHIFT 24 --#define ID0_PTFS_MASK 0x2 --#define ID0_PTFS_V8_ONLY 0x2 -+#define ID0_ATOSNS (1 << 26) - #define ID0_CTTW (1 << 14) - #define ID0_NUMIRPT_SHIFT 16 - #define ID0_NUMIRPT_MASK 0xff -@@ -169,11 +138,7 @@ - #define ID2_PTFS_16K (1 << 13) - #define ID2_PTFS_64K (1 << 14) - --#define PIDR2_ARCH_SHIFT 4 --#define PIDR2_ARCH_MASK 0xf -- - /* Global TLB invalidation */ --#define ARM_SMMU_GR0_STLBIALL 0x60 - #define ARM_SMMU_GR0_TLBIVMID 0x64 - #define ARM_SMMU_GR0_TLBIALLNSNH 0x68 - #define ARM_SMMU_GR0_TLBIALLH 0x6c -@@ -231,13 +196,25 @@ - #define ARM_SMMU_CB_TTBCR2 0x10 - #define ARM_SMMU_CB_TTBR0_LO 0x20 - #define ARM_SMMU_CB_TTBR0_HI 0x24 -+#define ARM_SMMU_CB_TTBR1_LO 0x28 -+#define ARM_SMMU_CB_TTBR1_HI 0x2c - #define ARM_SMMU_CB_TTBCR 0x30 - #define ARM_SMMU_CB_S1_MAIR0 0x38 -+#define ARM_SMMU_CB_S1_MAIR1 0x3c -+#define ARM_SMMU_CB_PAR_LO 0x50 -+#define ARM_SMMU_CB_PAR_HI 0x54 - #define ARM_SMMU_CB_FSR 0x58 - #define ARM_SMMU_CB_FAR_LO 0x60 - #define ARM_SMMU_CB_FAR_HI 0x64 - #define ARM_SMMU_CB_FSYNR0 0x68 -+#define ARM_SMMU_CB_S1_TLBIVA 0x600 - #define ARM_SMMU_CB_S1_TLBIASID 0x610 -+#define ARM_SMMU_CB_S1_TLBIVAL 0x620 -+#define ARM_SMMU_CB_S2_TLBIIPAS2 0x630 -+#define ARM_SMMU_CB_S2_TLBIIPAS2L 0x638 -+#define ARM_SMMU_CB_ATS1PR_LO 0x800 -+#define ARM_SMMU_CB_ATS1PR_HI 0x804 -+#define ARM_SMMU_CB_ATSR 0x8f0 - - #define SCTLR_S1_ASIDPNE (1 << 12) - #define SCTLR_CFCFG (1 << 7) -@@ -249,64 +226,17 @@ - #define SCTLR_M (1 << 0) - #define SCTLR_EAE_SBOP (SCTLR_AFE | SCTLR_TRE) - --#define RESUME_RETRY (0 << 0) --#define RESUME_TERMINATE (1 << 0) -- --#define TTBCR_EAE (1 << 31) -+#define CB_PAR_F (1 << 0) - --#define TTBCR_PASIZE_SHIFT 16 --#define TTBCR_PASIZE_MASK 0x7 -+#define ATSR_ACTIVE (1 << 0) - --#define TTBCR_TG0_4K (0 << 14) --#define TTBCR_TG0_64K (1 << 14) -- --#define TTBCR_SH0_SHIFT 12 --#define TTBCR_SH0_MASK 0x3 --#define TTBCR_SH_NS 0 --#define TTBCR_SH_OS 2 --#define TTBCR_SH_IS 3 -- --#define TTBCR_ORGN0_SHIFT 10 --#define TTBCR_IRGN0_SHIFT 8 --#define TTBCR_RGN_MASK 0x3 --#define TTBCR_RGN_NC 0 --#define TTBCR_RGN_WBWA 1 --#define TTBCR_RGN_WT 2 --#define TTBCR_RGN_WB 3 -- --#define TTBCR_SL0_SHIFT 6 --#define TTBCR_SL0_MASK 0x3 --#define TTBCR_SL0_LVL_2 0 --#define TTBCR_SL0_LVL_1 1 -- --#define TTBCR_T1SZ_SHIFT 16 --#define TTBCR_T0SZ_SHIFT 0 --#define TTBCR_SZ_MASK 0xf -+#define RESUME_RETRY (0 << 0) -+#define RESUME_TERMINATE (1 << 0) - - #define TTBCR2_SEP_SHIFT 15 --#define TTBCR2_SEP_MASK 0x7 -- --#define TTBCR2_PASIZE_SHIFT 0 --#define TTBCR2_PASIZE_MASK 0x7 -- --/* Common definitions for PASize and SEP fields */ --#define TTBCR2_ADDR_32 0 --#define TTBCR2_ADDR_36 1 --#define TTBCR2_ADDR_40 2 --#define TTBCR2_ADDR_42 3 --#define TTBCR2_ADDR_44 4 --#define TTBCR2_ADDR_48 5 -- --#define TTBRn_HI_ASID_SHIFT 16 -- --#define MAIR_ATTR_SHIFT(n) ((n) << 3) --#define MAIR_ATTR_MASK 0xff --#define MAIR_ATTR_DEVICE 0x04 --#define MAIR_ATTR_NC 0x44 --#define MAIR_ATTR_WBRWA 0xff --#define MAIR_ATTR_IDX_NC 0 --#define MAIR_ATTR_IDX_CACHE 1 --#define MAIR_ATTR_IDX_DEV 2 -+#define TTBCR2_SEP_UPSTREAM (0x7 << TTBCR2_SEP_SHIFT) -+ -+#define TTBRn_HI_ASID_SHIFT 16 - - #define FSR_MULTI (1 << 31) - #define FSR_SS (1 << 30) -@@ -345,6 +275,7 @@ struct arm_smmu_smr { - struct arm_smmu_master_cfg { - int num_streamids; - u16 streamids[MAX_MASTER_STREAMIDS]; -+ u16 mask; - struct arm_smmu_smr *smrs; - }; - -@@ -366,6 +297,7 @@ struct arm_smmu_device { - #define ARM_SMMU_FEAT_TRANS_S1 (1 << 2) - #define ARM_SMMU_FEAT_TRANS_S2 (1 << 3) - #define ARM_SMMU_FEAT_TRANS_NESTED (1 << 4) -+#define ARM_SMMU_FEAT_TRANS_OPS (1 << 5) - u32 features; - - #define ARM_SMMU_OPT_SECURE_CFG_ACCESS (1 << 0) -@@ -380,10 +312,9 @@ struct arm_smmu_device { - u32 num_mapping_groups; - DECLARE_BITMAP(smr_map, ARM_SMMU_MAX_SMRS); - -- unsigned long s1_input_size; -- unsigned long s1_output_size; -- unsigned long s2_input_size; -- unsigned long s2_output_size; -+ unsigned long va_size; -+ unsigned long ipa_size; -+ unsigned long pa_size; - - u32 num_global_irqs; - u32 num_context_irqs; -@@ -397,19 +328,33 @@ struct arm_smmu_cfg { - u8 cbndx; - u8 irptndx; - u32 cbar; -- pgd_t *pgd; - }; - #define INVALID_IRPTNDX 0xff - - #define ARM_SMMU_CB_ASID(cfg) ((cfg)->cbndx) - #define ARM_SMMU_CB_VMID(cfg) ((cfg)->cbndx + 1) - -+enum arm_smmu_domain_stage { -+ ARM_SMMU_DOMAIN_S1 = 0, -+ ARM_SMMU_DOMAIN_S2, -+ ARM_SMMU_DOMAIN_NESTED, -+}; -+ - struct arm_smmu_domain { - struct arm_smmu_device *smmu; -+ struct io_pgtable_ops *pgtbl_ops; -+ spinlock_t pgtbl_lock; - struct arm_smmu_cfg cfg; -- spinlock_t lock; -+ enum arm_smmu_domain_stage stage; -+ struct mutex init_mutex; /* Protects smmu pointer */ -+ struct iommu_domain domain; - }; - -+static struct iommu_ops arm_smmu_ops; -+#ifdef CONFIG_FSL_MC_BUS -+static struct iommu_ops arm_fsl_mc_smmu_ops; -+#endif -+ - static DEFINE_SPINLOCK(arm_smmu_devices_lock); - static LIST_HEAD(arm_smmu_devices); - -@@ -422,6 +367,43 @@ static struct arm_smmu_option_prop arm_smmu_options[] = { - { ARM_SMMU_OPT_SECURE_CFG_ACCESS, "calxeda,smmu-secure-config-access" }, - { 0, NULL}, - }; -+#define CONFIG_AIOP_ERRATA -+#ifdef CONFIG_AIOP_ERRATA -+/* -+ * PL = 1, BMT = 1, VA = 1 -+ */ -+#define AIOP_SMR_VALUE 0x380 -+/* -+ * Following should be set: -+ * SHCFG: 0x3 -+ * MTCFG: 0x1 -+ * MemAttr: 0xf -+ * Type: 0x1 -+ * RACFG: 0x2 -+ * WACFG: 0x2 -+ */ -+#define AIOP_S2CR_VALUE 0xA1FB00 -+ -+static void arm_smmu_aiop_attr_trans(struct arm_smmu_device *smmu) -+{ -+ void __iomem *gr0_base = ARM_SMMU_GR0(smmu); -+ u16 mask = 0x7c7f; -+ int index; -+ u32 reg; -+ /* reserve one smr group for AIOP */ -+ index = --smmu->num_mapping_groups; -+ -+ reg = SMR_VALID | AIOP_SMR_VALUE << SMR_ID_SHIFT | -+ mask << SMR_MASK_SHIFT; -+ writel(reg, gr0_base + ARM_SMMU_GR0_SMR(index)); -+ writel(AIOP_S2CR_VALUE, gr0_base + ARM_SMMU_GR0_S2CR(index)); -+} -+#endif -+ -+static struct arm_smmu_domain *to_smmu_domain(struct iommu_domain *dom) -+{ -+ return container_of(dom, struct arm_smmu_domain, domain); -+} - - static void parse_driver_options(struct arm_smmu_device *smmu) - { -@@ -447,6 +429,16 @@ static struct device_node *dev_get_dev_node(struct device *dev) - return bus->bridge->parent->of_node; - } - -+#ifdef CONFIG_FSL_MC_BUS -+ if (dev->bus == &fsl_mc_bus_type) { -+ /* -+ * Get to the MC device tree node. -+ */ -+ while (dev->bus == &fsl_mc_bus_type) -+ dev = dev->parent; -+ } -+#endif -+ - return dev->of_node; - } - -@@ -590,7 +582,7 @@ static void __arm_smmu_free_bitmap(unsigned long *map, int idx) - } - - /* Wait for any pending TLB invalidations to complete */ --static void arm_smmu_tlb_sync(struct arm_smmu_device *smmu) -+static void __arm_smmu_tlb_sync(struct arm_smmu_device *smmu) - { - int count = 0; - void __iomem *gr0_base = ARM_SMMU_GR0(smmu); -@@ -608,12 +600,19 @@ static void arm_smmu_tlb_sync(struct arm_smmu_device *smmu) - } - } - --static void arm_smmu_tlb_inv_context(struct arm_smmu_domain *smmu_domain) -+static void arm_smmu_tlb_sync(void *cookie) - { -+ struct arm_smmu_domain *smmu_domain = cookie; -+ __arm_smmu_tlb_sync(smmu_domain->smmu); -+} -+ -+static void arm_smmu_tlb_inv_context(void *cookie) -+{ -+ struct arm_smmu_domain *smmu_domain = cookie; - struct arm_smmu_cfg *cfg = &smmu_domain->cfg; - struct arm_smmu_device *smmu = smmu_domain->smmu; -- void __iomem *base = ARM_SMMU_GR0(smmu); - bool stage1 = cfg->cbar != CBAR_TYPE_S2_TRANS; -+ void __iomem *base; - - if (stage1) { - base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx); -@@ -625,16 +624,83 @@ static void arm_smmu_tlb_inv_context(struct arm_smmu_domain *smmu_domain) - base + ARM_SMMU_GR0_TLBIVMID); - } - -- arm_smmu_tlb_sync(smmu); -+ __arm_smmu_tlb_sync(smmu); -+} -+ -+static void arm_smmu_tlb_inv_range_nosync(unsigned long iova, size_t size, -+ bool leaf, void *cookie) -+{ -+ struct arm_smmu_domain *smmu_domain = cookie; -+ struct arm_smmu_cfg *cfg = &smmu_domain->cfg; -+ struct arm_smmu_device *smmu = smmu_domain->smmu; -+ bool stage1 = cfg->cbar != CBAR_TYPE_S2_TRANS; -+ void __iomem *reg; -+ -+ if (stage1) { -+ reg = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx); -+ reg += leaf ? ARM_SMMU_CB_S1_TLBIVAL : ARM_SMMU_CB_S1_TLBIVA; -+ -+ if (!IS_ENABLED(CONFIG_64BIT) || smmu->version == ARM_SMMU_V1) { -+ iova &= ~12UL; -+ iova |= ARM_SMMU_CB_ASID(cfg); -+ writel_relaxed(iova, reg); -+#ifdef CONFIG_64BIT -+ } else { -+ iova >>= 12; -+ iova |= (u64)ARM_SMMU_CB_ASID(cfg) << 48; -+ writeq_relaxed(iova, reg); -+#endif -+ } -+#ifdef CONFIG_64BIT -+ } else if (smmu->version == ARM_SMMU_V2) { -+ reg = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx); -+ reg += leaf ? ARM_SMMU_CB_S2_TLBIIPAS2L : -+ ARM_SMMU_CB_S2_TLBIIPAS2; -+ writeq_relaxed(iova >> 12, reg); -+#endif -+ } else { -+ reg = ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_TLBIVMID; -+ writel_relaxed(ARM_SMMU_CB_VMID(cfg), reg); -+ } -+} -+ -+static void arm_smmu_flush_pgtable(void *addr, size_t size, void *cookie) -+{ -+ struct arm_smmu_domain *smmu_domain = cookie; -+ struct arm_smmu_device *smmu = smmu_domain->smmu; -+ unsigned long offset = (unsigned long)addr & ~PAGE_MASK; -+ -+ -+ /* Ensure new page tables are visible to the hardware walker */ -+ if (smmu->features & ARM_SMMU_FEAT_COHERENT_WALK) { -+ dsb(ishst); -+ } else { -+ /* -+ * If the SMMU can't walk tables in the CPU caches, treat them -+ * like non-coherent DMA since we need to flush the new entries -+ * all the way out to memory. There's no possibility of -+ * recursion here as the SMMU table walker will not be wired -+ * through another SMMU. -+ */ -+ dma_map_page(smmu->dev, virt_to_page(addr), offset, size, -+ DMA_TO_DEVICE); -+ } - } - -+static struct iommu_gather_ops arm_smmu_gather_ops = { -+ .tlb_flush_all = arm_smmu_tlb_inv_context, -+ .tlb_add_flush = arm_smmu_tlb_inv_range_nosync, -+ .tlb_sync = arm_smmu_tlb_sync, -+ .flush_pgtable = arm_smmu_flush_pgtable, -+}; -+ - static irqreturn_t arm_smmu_context_fault(int irq, void *dev) - { - int flags, ret; - u32 fsr, far, fsynr, resume; - unsigned long iova; - struct iommu_domain *domain = dev; -- struct arm_smmu_domain *smmu_domain = domain->priv; -+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); - struct arm_smmu_cfg *cfg = &smmu_domain->cfg; - struct arm_smmu_device *smmu = smmu_domain->smmu; - void __iomem *cb_base; -@@ -705,29 +771,8 @@ static irqreturn_t arm_smmu_global_fault(int irq, void *dev) - return IRQ_HANDLED; - } - --static void arm_smmu_flush_pgtable(struct arm_smmu_device *smmu, void *addr, -- size_t size) --{ -- unsigned long offset = (unsigned long)addr & ~PAGE_MASK; -- -- -- /* Ensure new page tables are visible to the hardware walker */ -- if (smmu->features & ARM_SMMU_FEAT_COHERENT_WALK) { -- dsb(ishst); -- } else { -- /* -- * If the SMMU can't walk tables in the CPU caches, treat them -- * like non-coherent DMA since we need to flush the new entries -- * all the way out to memory. There's no possibility of -- * recursion here as the SMMU table walker will not be wired -- * through another SMMU. -- */ -- dma_map_page(smmu->dev, virt_to_page(addr), offset, size, -- DMA_TO_DEVICE); -- } --} -- --static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain) -+static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain, -+ struct io_pgtable_cfg *pgtbl_cfg) - { - u32 reg; - bool stage1; -@@ -740,6 +785,20 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain) - stage1 = cfg->cbar != CBAR_TYPE_S2_TRANS; - cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx); - -+ if (smmu->version > ARM_SMMU_V1) { -+ /* -+ * CBA2R. -+ * *Must* be initialised before CBAR thanks to VMID16 -+ * architectural oversight affected some implementations. -+ */ -+#ifdef CONFIG_64BIT -+ reg = CBA2R_RW64_64BIT; -+#else -+ reg = CBA2R_RW64_32BIT; -+#endif -+ writel_relaxed(reg, gr1_base + ARM_SMMU_GR1_CBA2R(cfg->cbndx)); -+ } -+ - /* CBAR */ - reg = cfg->cbar; - if (smmu->version == ARM_SMMU_V1) -@@ -757,135 +816,51 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain) - } - writel_relaxed(reg, gr1_base + ARM_SMMU_GR1_CBAR(cfg->cbndx)); - -- if (smmu->version > ARM_SMMU_V1) { -- /* CBA2R */ --#ifdef CONFIG_64BIT -- reg = CBA2R_RW64_64BIT; --#else -- reg = CBA2R_RW64_32BIT; --#endif -- writel_relaxed(reg, -- gr1_base + ARM_SMMU_GR1_CBA2R(cfg->cbndx)); -- -- /* TTBCR2 */ -- switch (smmu->s1_input_size) { -- case 32: -- reg = (TTBCR2_ADDR_32 << TTBCR2_SEP_SHIFT); -- break; -- case 36: -- reg = (TTBCR2_ADDR_36 << TTBCR2_SEP_SHIFT); -- break; -- case 39: -- case 40: -- reg = (TTBCR2_ADDR_40 << TTBCR2_SEP_SHIFT); -- break; -- case 42: -- reg = (TTBCR2_ADDR_42 << TTBCR2_SEP_SHIFT); -- break; -- case 44: -- reg = (TTBCR2_ADDR_44 << TTBCR2_SEP_SHIFT); -- break; -- case 48: -- reg = (TTBCR2_ADDR_48 << TTBCR2_SEP_SHIFT); -- break; -- } -- -- switch (smmu->s1_output_size) { -- case 32: -- reg |= (TTBCR2_ADDR_32 << TTBCR2_PASIZE_SHIFT); -- break; -- case 36: -- reg |= (TTBCR2_ADDR_36 << TTBCR2_PASIZE_SHIFT); -- break; -- case 39: -- case 40: -- reg |= (TTBCR2_ADDR_40 << TTBCR2_PASIZE_SHIFT); -- break; -- case 42: -- reg |= (TTBCR2_ADDR_42 << TTBCR2_PASIZE_SHIFT); -- break; -- case 44: -- reg |= (TTBCR2_ADDR_44 << TTBCR2_PASIZE_SHIFT); -- break; -- case 48: -- reg |= (TTBCR2_ADDR_48 << TTBCR2_PASIZE_SHIFT); -- break; -- } -- -- if (stage1) -- writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBCR2); -- } -+ /* TTBRs */ -+ if (stage1) { -+ reg = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0]; -+ writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_LO); -+ reg = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0] >> 32; -+ reg |= ARM_SMMU_CB_ASID(cfg) << TTBRn_HI_ASID_SHIFT; -+ writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_HI); - -- /* TTBR0 */ -- arm_smmu_flush_pgtable(smmu, cfg->pgd, -- PTRS_PER_PGD * sizeof(pgd_t)); -- reg = __pa(cfg->pgd); -- writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_LO); -- reg = (phys_addr_t)__pa(cfg->pgd) >> 32; -- if (stage1) -+ reg = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[1]; -+ writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR1_LO); -+ reg = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[1] >> 32; - reg |= ARM_SMMU_CB_ASID(cfg) << TTBRn_HI_ASID_SHIFT; -- writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_HI); -+ writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR1_HI); -+ } else { -+ reg = pgtbl_cfg->arm_lpae_s2_cfg.vttbr; -+ writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_LO); -+ reg = pgtbl_cfg->arm_lpae_s2_cfg.vttbr >> 32; -+ writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_HI); -+ } - -- /* -- * TTBCR -- * We use long descriptor, with inner-shareable WBWA tables in TTBR0. -- */ -- if (smmu->version > ARM_SMMU_V1) { -- if (PAGE_SIZE == SZ_4K) -- reg = TTBCR_TG0_4K; -- else -- reg = TTBCR_TG0_64K; -- -- if (!stage1) { -- reg |= (64 - smmu->s2_input_size) << TTBCR_T0SZ_SHIFT; -- -- switch (smmu->s2_output_size) { -- case 32: -- reg |= (TTBCR2_ADDR_32 << TTBCR_PASIZE_SHIFT); -- break; -- case 36: -- reg |= (TTBCR2_ADDR_36 << TTBCR_PASIZE_SHIFT); -- break; -- case 40: -- reg |= (TTBCR2_ADDR_40 << TTBCR_PASIZE_SHIFT); -- break; -- case 42: -- reg |= (TTBCR2_ADDR_42 << TTBCR_PASIZE_SHIFT); -- break; -- case 44: -- reg |= (TTBCR2_ADDR_44 << TTBCR_PASIZE_SHIFT); -- break; -- case 48: -- reg |= (TTBCR2_ADDR_48 << TTBCR_PASIZE_SHIFT); -- break; -- } -- } else { -- reg |= (64 - smmu->s1_input_size) << TTBCR_T0SZ_SHIFT; -+ /* TTBCR */ -+ if (stage1) { -+ reg = pgtbl_cfg->arm_lpae_s1_cfg.tcr; -+ writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBCR); -+ if (smmu->version > ARM_SMMU_V1) { -+ reg = pgtbl_cfg->arm_lpae_s1_cfg.tcr >> 32; -+ reg |= TTBCR2_SEP_UPSTREAM; -+ writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBCR2); - } - } else { -- reg = 0; -+ reg = pgtbl_cfg->arm_lpae_s2_cfg.vtcr; -+ writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBCR); - } - -- reg |= TTBCR_EAE | -- (TTBCR_SH_IS << TTBCR_SH0_SHIFT) | -- (TTBCR_RGN_WBWA << TTBCR_ORGN0_SHIFT) | -- (TTBCR_RGN_WBWA << TTBCR_IRGN0_SHIFT); -- -- if (!stage1) -- reg |= (TTBCR_SL0_LVL_1 << TTBCR_SL0_SHIFT); -- -- writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBCR); -- -- /* MAIR0 (stage-1 only) */ -+ /* MAIRs (stage-1 only) */ - if (stage1) { -- reg = (MAIR_ATTR_NC << MAIR_ATTR_SHIFT(MAIR_ATTR_IDX_NC)) | -- (MAIR_ATTR_WBRWA << MAIR_ATTR_SHIFT(MAIR_ATTR_IDX_CACHE)) | -- (MAIR_ATTR_DEVICE << MAIR_ATTR_SHIFT(MAIR_ATTR_IDX_DEV)); -+ reg = pgtbl_cfg->arm_lpae_s1_cfg.mair[0]; - writel_relaxed(reg, cb_base + ARM_SMMU_CB_S1_MAIR0); -+ reg = pgtbl_cfg->arm_lpae_s1_cfg.mair[1]; -+ writel_relaxed(reg, cb_base + ARM_SMMU_CB_S1_MAIR1); - } - - /* SCTLR */ -- reg = SCTLR_CFCFG | SCTLR_CFIE | SCTLR_CFRE | SCTLR_M | SCTLR_EAE_SBOP; -+ /* Disable stall mode */ -+ reg = SCTLR_CFIE | SCTLR_CFRE | SCTLR_M | SCTLR_EAE_SBOP; - if (stage1) - reg |= SCTLR_S1_ASIDPNE; - #ifdef __BIG_ENDIAN -@@ -898,27 +873,69 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, - struct arm_smmu_device *smmu) - { - int irq, start, ret = 0; -- unsigned long flags; -- struct arm_smmu_domain *smmu_domain = domain->priv; -+ unsigned long ias, oas; -+ struct io_pgtable_ops *pgtbl_ops; -+ struct io_pgtable_cfg pgtbl_cfg; -+ enum io_pgtable_fmt fmt; -+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); - struct arm_smmu_cfg *cfg = &smmu_domain->cfg; - -- spin_lock_irqsave(&smmu_domain->lock, flags); -+ mutex_lock(&smmu_domain->init_mutex); - if (smmu_domain->smmu) - goto out_unlock; - -- if (smmu->features & ARM_SMMU_FEAT_TRANS_NESTED) { -+ /* -+ * Mapping the requested stage onto what we support is surprisingly -+ * complicated, mainly because the spec allows S1+S2 SMMUs without -+ * support for nested translation. That means we end up with the -+ * following table: -+ * -+ * Requested Supported Actual -+ * S1 N S1 -+ * S1 S1+S2 S1 -+ * S1 S2 S2 -+ * S1 S1 S1 -+ * N N N -+ * N S1+S2 S2 -+ * N S2 S2 -+ * N S1 S1 -+ * -+ * Note that you can't actually request stage-2 mappings. -+ */ -+ if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S1)) -+ smmu_domain->stage = ARM_SMMU_DOMAIN_S2; -+ if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S2)) -+ smmu_domain->stage = ARM_SMMU_DOMAIN_S1; -+ -+ switch (smmu_domain->stage) { -+ case ARM_SMMU_DOMAIN_S1: -+ cfg->cbar = CBAR_TYPE_S1_TRANS_S2_BYPASS; -+ start = smmu->num_s2_context_banks; -+ ias = smmu->va_size; -+ oas = smmu->ipa_size; -+ if (IS_ENABLED(CONFIG_64BIT)) -+ fmt = ARM_64_LPAE_S1; -+ else -+ fmt = ARM_32_LPAE_S1; -+ break; -+ case ARM_SMMU_DOMAIN_NESTED: - /* - * We will likely want to change this if/when KVM gets - * involved. - */ -- cfg->cbar = CBAR_TYPE_S1_TRANS_S2_BYPASS; -- start = smmu->num_s2_context_banks; -- } else if (smmu->features & ARM_SMMU_FEAT_TRANS_S1) { -- cfg->cbar = CBAR_TYPE_S1_TRANS_S2_BYPASS; -- start = smmu->num_s2_context_banks; -- } else { -+ case ARM_SMMU_DOMAIN_S2: - cfg->cbar = CBAR_TYPE_S2_TRANS; - start = 0; -+ ias = smmu->ipa_size; -+ oas = smmu->pa_size; -+ if (IS_ENABLED(CONFIG_64BIT)) -+ fmt = ARM_64_LPAE_S2; -+ else -+ fmt = ARM_32_LPAE_S2; -+ break; -+ default: -+ ret = -EINVAL; -+ goto out_unlock; - } - - ret = __arm_smmu_alloc_bitmap(smmu->context_map, start, -@@ -934,10 +951,33 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, - cfg->irptndx = cfg->cbndx; - } - -- ACCESS_ONCE(smmu_domain->smmu) = smmu; -- arm_smmu_init_context_bank(smmu_domain); -- spin_unlock_irqrestore(&smmu_domain->lock, flags); -+ pgtbl_cfg = (struct io_pgtable_cfg) { -+ .pgsize_bitmap = arm_smmu_ops.pgsize_bitmap, -+ .ias = ias, -+ .oas = oas, -+ .tlb = &arm_smmu_gather_ops, -+ }; -+ -+ smmu_domain->smmu = smmu; -+ pgtbl_ops = alloc_io_pgtable_ops(fmt, &pgtbl_cfg, smmu_domain); -+ if (!pgtbl_ops) { -+ ret = -ENOMEM; -+ goto out_clear_smmu; -+ } -+ -+ /* Update our support page sizes to reflect the page table format */ -+ arm_smmu_ops.pgsize_bitmap = pgtbl_cfg.pgsize_bitmap; -+#ifdef CONFIG_FSL_MC_BUS -+ arm_fsl_mc_smmu_ops.pgsize_bitmap = pgtbl_cfg.pgsize_bitmap; -+#endif -+ -+ /* Initialise the context bank with our page table cfg */ -+ arm_smmu_init_context_bank(smmu_domain, &pgtbl_cfg); - -+ /* -+ * Request context fault interrupt. Do this last to avoid the -+ * handler seeing a half-initialised domain state. -+ */ - irq = smmu->irqs[smmu->num_global_irqs + cfg->irptndx]; - ret = request_irq(irq, arm_smmu_context_fault, IRQF_SHARED, - "arm-smmu-context-fault", domain); -@@ -947,16 +987,22 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, - cfg->irptndx = INVALID_IRPTNDX; - } - -+ mutex_unlock(&smmu_domain->init_mutex); -+ -+ /* Publish page table ops for map/unmap */ -+ smmu_domain->pgtbl_ops = pgtbl_ops; - return 0; - -+out_clear_smmu: -+ smmu_domain->smmu = NULL; - out_unlock: -- spin_unlock_irqrestore(&smmu_domain->lock, flags); -+ mutex_unlock(&smmu_domain->init_mutex); - return ret; - } - - static void arm_smmu_destroy_domain_context(struct iommu_domain *domain) - { -- struct arm_smmu_domain *smmu_domain = domain->priv; -+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); - struct arm_smmu_device *smmu = smmu_domain->smmu; - struct arm_smmu_cfg *cfg = &smmu_domain->cfg; - void __iomem *cb_base; -@@ -965,24 +1011,30 @@ static void arm_smmu_destroy_domain_context(struct iommu_domain *domain) - if (!smmu) - return; - -- /* Disable the context bank and nuke the TLB before freeing it. */ -+ /* -+ * Disable the context bank and free the page tables before freeing -+ * it. -+ */ - cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx); - writel_relaxed(0, cb_base + ARM_SMMU_CB_SCTLR); -- arm_smmu_tlb_inv_context(smmu_domain); - - if (cfg->irptndx != INVALID_IRPTNDX) { - irq = smmu->irqs[smmu->num_global_irqs + cfg->irptndx]; - free_irq(irq, domain); - } - -+ if (smmu_domain->pgtbl_ops) -+ free_io_pgtable_ops(smmu_domain->pgtbl_ops); -+ - __arm_smmu_free_bitmap(smmu->context_map, cfg->cbndx); - } - --static int arm_smmu_domain_init(struct iommu_domain *domain) -+static struct iommu_domain *arm_smmu_domain_alloc(unsigned type) - { - struct arm_smmu_domain *smmu_domain; -- pgd_t *pgd; - -+ if (type != IOMMU_DOMAIN_UNMANAGED) -+ return NULL; - /* - * Allocate the domain and initialise some of its data structures. - * We can't really do anything meaningful until we've added a -@@ -990,95 +1042,23 @@ static int arm_smmu_domain_init(struct iommu_domain *domain) - */ - smmu_domain = kzalloc(sizeof(*smmu_domain), GFP_KERNEL); - if (!smmu_domain) -- return -ENOMEM; -+ return NULL; - -- pgd = kcalloc(PTRS_PER_PGD, sizeof(pgd_t), GFP_KERNEL); -- if (!pgd) -- goto out_free_domain; -- smmu_domain->cfg.pgd = pgd; -+ mutex_init(&smmu_domain->init_mutex); -+ spin_lock_init(&smmu_domain->pgtbl_lock); - -- spin_lock_init(&smmu_domain->lock); -- domain->priv = smmu_domain; -- return 0; -- --out_free_domain: -- kfree(smmu_domain); -- return -ENOMEM; -+ return &smmu_domain->domain; - } - --static void arm_smmu_free_ptes(pmd_t *pmd) -+static void arm_smmu_domain_free(struct iommu_domain *domain) - { -- pgtable_t table = pmd_pgtable(*pmd); -- -- __free_page(table); --} -- --static void arm_smmu_free_pmds(pud_t *pud) --{ -- int i; -- pmd_t *pmd, *pmd_base = pmd_offset(pud, 0); -- -- pmd = pmd_base; -- for (i = 0; i < PTRS_PER_PMD; ++i) { -- if (pmd_none(*pmd)) -- continue; -- -- arm_smmu_free_ptes(pmd); -- pmd++; -- } -- -- pmd_free(NULL, pmd_base); --} -- --static void arm_smmu_free_puds(pgd_t *pgd) --{ -- int i; -- pud_t *pud, *pud_base = pud_offset(pgd, 0); -- -- pud = pud_base; -- for (i = 0; i < PTRS_PER_PUD; ++i) { -- if (pud_none(*pud)) -- continue; -- -- arm_smmu_free_pmds(pud); -- pud++; -- } -- -- pud_free(NULL, pud_base); --} -- --static void arm_smmu_free_pgtables(struct arm_smmu_domain *smmu_domain) --{ -- int i; -- struct arm_smmu_cfg *cfg = &smmu_domain->cfg; -- pgd_t *pgd, *pgd_base = cfg->pgd; -- -- /* -- * Recursively free the page tables for this domain. We don't -- * care about speculative TLB filling because the tables should -- * not be active in any context bank at this point (SCTLR.M is 0). -- */ -- pgd = pgd_base; -- for (i = 0; i < PTRS_PER_PGD; ++i) { -- if (pgd_none(*pgd)) -- continue; -- arm_smmu_free_puds(pgd); -- pgd++; -- } -- -- kfree(pgd_base); --} -- --static void arm_smmu_domain_destroy(struct iommu_domain *domain) --{ -- struct arm_smmu_domain *smmu_domain = domain->priv; -+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); - - /* - * Free the domain resources. We assume that all devices have - * already been detached. - */ - arm_smmu_destroy_domain_context(domain); -- arm_smmu_free_pgtables(smmu_domain); - kfree(smmu_domain); - } - -@@ -1113,7 +1093,7 @@ static int arm_smmu_master_configure_smrs(struct arm_smmu_device *smmu, - - smrs[i] = (struct arm_smmu_smr) { - .idx = idx, -- .mask = 0, /* We don't currently share SMRs */ -+ .mask = cfg->mask, - .id = cfg->streamids[i], - }; - } -@@ -1209,8 +1189,8 @@ static void arm_smmu_domain_remove_master(struct arm_smmu_domain *smmu_domain, - static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) - { - int ret; -- struct arm_smmu_domain *smmu_domain = domain->priv; -- struct arm_smmu_device *smmu, *dom_smmu; -+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); -+ struct arm_smmu_device *smmu; - struct arm_smmu_master_cfg *cfg; - - smmu = find_smmu_for_device(dev); -@@ -1224,21 +1204,16 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) - return -EEXIST; - } - -+ /* Ensure that the domain is finalised */ -+ ret = arm_smmu_init_domain_context(domain, smmu); -+ if (IS_ERR_VALUE(ret)) -+ return ret; -+ - /* - * Sanity check the domain. We don't support domains across - * different SMMUs. - */ -- dom_smmu = ACCESS_ONCE(smmu_domain->smmu); -- if (!dom_smmu) { -- /* Now that we have a master, we can finalise the domain */ -- ret = arm_smmu_init_domain_context(domain, smmu); -- if (IS_ERR_VALUE(ret)) -- return ret; -- -- dom_smmu = smmu_domain->smmu; -- } -- -- if (dom_smmu != smmu) { -+ if (smmu_domain->smmu != smmu) { - dev_err(dev, - "cannot attach to SMMU %s whilst already attached to domain on SMMU %s\n", - dev_name(smmu_domain->smmu->dev), dev_name(smmu->dev)); -@@ -1258,7 +1233,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) - - static void arm_smmu_detach_dev(struct iommu_domain *domain, struct device *dev) - { -- struct arm_smmu_domain *smmu_domain = domain->priv; -+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); - struct arm_smmu_master_cfg *cfg; - - cfg = find_smmu_master_cfg(dev); -@@ -1269,292 +1244,106 @@ static void arm_smmu_detach_dev(struct iommu_domain *domain, struct device *dev) - arm_smmu_domain_remove_master(smmu_domain, cfg); - } - --static bool arm_smmu_pte_is_contiguous_range(unsigned long addr, -- unsigned long end) --{ -- return !(addr & ~ARM_SMMU_PTE_CONT_MASK) && -- (addr + ARM_SMMU_PTE_CONT_SIZE <= end); --} -- --static int arm_smmu_alloc_init_pte(struct arm_smmu_device *smmu, pmd_t *pmd, -- unsigned long addr, unsigned long end, -- unsigned long pfn, int prot, int stage) --{ -- pte_t *pte, *start; -- pteval_t pteval = ARM_SMMU_PTE_PAGE | ARM_SMMU_PTE_AF | ARM_SMMU_PTE_XN; -- -- if (pmd_none(*pmd)) { -- /* Allocate a new set of tables */ -- pgtable_t table = alloc_page(GFP_ATOMIC|__GFP_ZERO); -- -- if (!table) -- return -ENOMEM; -- -- arm_smmu_flush_pgtable(smmu, page_address(table), PAGE_SIZE); -- pmd_populate(NULL, pmd, table); -- arm_smmu_flush_pgtable(smmu, pmd, sizeof(*pmd)); -- } -- -- if (stage == 1) { -- pteval |= ARM_SMMU_PTE_AP_UNPRIV | ARM_SMMU_PTE_nG; -- if (!(prot & IOMMU_WRITE) && (prot & IOMMU_READ)) -- pteval |= ARM_SMMU_PTE_AP_RDONLY; -- -- if (prot & IOMMU_CACHE) -- pteval |= (MAIR_ATTR_IDX_CACHE << -- ARM_SMMU_PTE_ATTRINDX_SHIFT); -- } else { -- pteval |= ARM_SMMU_PTE_HAP_FAULT; -- if (prot & IOMMU_READ) -- pteval |= ARM_SMMU_PTE_HAP_READ; -- if (prot & IOMMU_WRITE) -- pteval |= ARM_SMMU_PTE_HAP_WRITE; -- if (prot & IOMMU_CACHE) -- pteval |= ARM_SMMU_PTE_MEMATTR_OIWB; -- else -- pteval |= ARM_SMMU_PTE_MEMATTR_NC; -- } -- -- /* If no access, create a faulting entry to avoid TLB fills */ -- if (prot & IOMMU_EXEC) -- pteval &= ~ARM_SMMU_PTE_XN; -- else if (!(prot & (IOMMU_READ | IOMMU_WRITE))) -- pteval &= ~ARM_SMMU_PTE_PAGE; -- -- pteval |= ARM_SMMU_PTE_SH_IS; -- start = pmd_page_vaddr(*pmd) + pte_index(addr); -- pte = start; -- -- /* -- * Install the page table entries. This is fairly complicated -- * since we attempt to make use of the contiguous hint in the -- * ptes where possible. The contiguous hint indicates a series -- * of ARM_SMMU_PTE_CONT_ENTRIES ptes mapping a physically -- * contiguous region with the following constraints: -- * -- * - The region start is aligned to ARM_SMMU_PTE_CONT_SIZE -- * - Each pte in the region has the contiguous hint bit set -- * -- * This complicates unmapping (also handled by this code, when -- * neither IOMMU_READ or IOMMU_WRITE are set) because it is -- * possible, yet highly unlikely, that a client may unmap only -- * part of a contiguous range. This requires clearing of the -- * contiguous hint bits in the range before installing the new -- * faulting entries. -- * -- * Note that re-mapping an address range without first unmapping -- * it is not supported, so TLB invalidation is not required here -- * and is instead performed at unmap and domain-init time. -- */ -- do { -- int i = 1; -- -- pteval &= ~ARM_SMMU_PTE_CONT; -- -- if (arm_smmu_pte_is_contiguous_range(addr, end)) { -- i = ARM_SMMU_PTE_CONT_ENTRIES; -- pteval |= ARM_SMMU_PTE_CONT; -- } else if (pte_val(*pte) & -- (ARM_SMMU_PTE_CONT | ARM_SMMU_PTE_PAGE)) { -- int j; -- pte_t *cont_start; -- unsigned long idx = pte_index(addr); -- -- idx &= ~(ARM_SMMU_PTE_CONT_ENTRIES - 1); -- cont_start = pmd_page_vaddr(*pmd) + idx; -- for (j = 0; j < ARM_SMMU_PTE_CONT_ENTRIES; ++j) -- pte_val(*(cont_start + j)) &= -- ~ARM_SMMU_PTE_CONT; -- -- arm_smmu_flush_pgtable(smmu, cont_start, -- sizeof(*pte) * -- ARM_SMMU_PTE_CONT_ENTRIES); -- } -- -- do { -- *pte = pfn_pte(pfn, __pgprot(pteval)); -- } while (pte++, pfn++, addr += PAGE_SIZE, --i); -- } while (addr != end); -- -- arm_smmu_flush_pgtable(smmu, start, sizeof(*pte) * (pte - start)); -- return 0; --} -- --static int arm_smmu_alloc_init_pmd(struct arm_smmu_device *smmu, pud_t *pud, -- unsigned long addr, unsigned long end, -- phys_addr_t phys, int prot, int stage) -+static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova, -+ phys_addr_t paddr, size_t size, int prot) - { - int ret; -- pmd_t *pmd; -- unsigned long next, pfn = __phys_to_pfn(phys); -- --#ifndef __PAGETABLE_PMD_FOLDED -- if (pud_none(*pud)) { -- pmd = (pmd_t *)get_zeroed_page(GFP_ATOMIC); -- if (!pmd) -- return -ENOMEM; -- -- arm_smmu_flush_pgtable(smmu, pmd, PAGE_SIZE); -- pud_populate(NULL, pud, pmd); -- arm_smmu_flush_pgtable(smmu, pud, sizeof(*pud)); -- -- pmd += pmd_index(addr); -- } else --#endif -- pmd = pmd_offset(pud, addr); -+ unsigned long flags; -+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); -+ struct io_pgtable_ops *ops= smmu_domain->pgtbl_ops; - -- do { -- next = pmd_addr_end(addr, end); -- ret = arm_smmu_alloc_init_pte(smmu, pmd, addr, next, pfn, -- prot, stage); -- phys += next - addr; -- pfn = __phys_to_pfn(phys); -- } while (pmd++, addr = next, addr < end); -+ if (!ops) -+ return -ENODEV; - -+ spin_lock_irqsave(&smmu_domain->pgtbl_lock, flags); -+ ret = ops->map(ops, iova, paddr, size, prot); -+ spin_unlock_irqrestore(&smmu_domain->pgtbl_lock, flags); - return ret; - } - --static int arm_smmu_alloc_init_pud(struct arm_smmu_device *smmu, pgd_t *pgd, -- unsigned long addr, unsigned long end, -- phys_addr_t phys, int prot, int stage) -+static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova, -+ size_t size) - { -- int ret = 0; -- pud_t *pud; -- unsigned long next; -- --#ifndef __PAGETABLE_PUD_FOLDED -- if (pgd_none(*pgd)) { -- pud = (pud_t *)get_zeroed_page(GFP_ATOMIC); -- if (!pud) -- return -ENOMEM; -- -- arm_smmu_flush_pgtable(smmu, pud, PAGE_SIZE); -- pgd_populate(NULL, pgd, pud); -- arm_smmu_flush_pgtable(smmu, pgd, sizeof(*pgd)); -- -- pud += pud_index(addr); -- } else --#endif -- pud = pud_offset(pgd, addr); -+ size_t ret; -+ unsigned long flags; -+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); -+ struct io_pgtable_ops *ops= smmu_domain->pgtbl_ops; - -- do { -- next = pud_addr_end(addr, end); -- ret = arm_smmu_alloc_init_pmd(smmu, pud, addr, next, phys, -- prot, stage); -- phys += next - addr; -- } while (pud++, addr = next, addr < end); -+ if (!ops) -+ return 0; - -+ spin_lock_irqsave(&smmu_domain->pgtbl_lock, flags); -+ ret = ops->unmap(ops, iova, size); -+ spin_unlock_irqrestore(&smmu_domain->pgtbl_lock, flags); - return ret; - } - --static int arm_smmu_handle_mapping(struct arm_smmu_domain *smmu_domain, -- unsigned long iova, phys_addr_t paddr, -- size_t size, int prot) -+static phys_addr_t arm_smmu_iova_to_phys_hard(struct iommu_domain *domain, -+ dma_addr_t iova) - { -- int ret, stage; -- unsigned long end; -- phys_addr_t input_mask, output_mask; -+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); - struct arm_smmu_device *smmu = smmu_domain->smmu; - struct arm_smmu_cfg *cfg = &smmu_domain->cfg; -- pgd_t *pgd = cfg->pgd; -- unsigned long flags; -+ struct io_pgtable_ops *ops= smmu_domain->pgtbl_ops; -+ struct device *dev = smmu->dev; -+ void __iomem *cb_base; -+ u32 tmp; -+ u64 phys; -+ -+ cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx); - -- if (cfg->cbar == CBAR_TYPE_S2_TRANS) { -- stage = 2; -- input_mask = (1ULL << smmu->s2_input_size) - 1; -- output_mask = (1ULL << smmu->s2_output_size) - 1; -+ if (smmu->version == 1) { -+ u32 reg = iova & ~0xfff; -+ writel_relaxed(reg, cb_base + ARM_SMMU_CB_ATS1PR_LO); - } else { -- stage = 1; -- input_mask = (1ULL << smmu->s1_input_size) - 1; -- output_mask = (1ULL << smmu->s1_output_size) - 1; -+ u32 reg = iova & ~0xfff; -+ writel_relaxed(reg, cb_base + ARM_SMMU_CB_ATS1PR_LO); -+ reg = ((u64)iova & ~0xfff) >> 32; -+ writel_relaxed(reg, cb_base + ARM_SMMU_CB_ATS1PR_HI); - } - -- if (!pgd) -- return -EINVAL; -- -- if (size & ~PAGE_MASK) -- return -EINVAL; -- -- if ((phys_addr_t)iova & ~input_mask) -- return -ERANGE; -- -- if (paddr & ~output_mask) -- return -ERANGE; -- -- spin_lock_irqsave(&smmu_domain->lock, flags); -- pgd += pgd_index(iova); -- end = iova + size; -- do { -- unsigned long next = pgd_addr_end(iova, end); -- -- ret = arm_smmu_alloc_init_pud(smmu, pgd, iova, next, paddr, -- prot, stage); -- if (ret) -- goto out_unlock; -- -- paddr += next - iova; -- iova = next; -- } while (pgd++, iova != end); -- --out_unlock: -- spin_unlock_irqrestore(&smmu_domain->lock, flags); -- -- return ret; --} -- --static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova, -- phys_addr_t paddr, size_t size, int prot) --{ -- struct arm_smmu_domain *smmu_domain = domain->priv; -- -- if (!smmu_domain) -- return -ENODEV; -+ if (readl_poll_timeout_atomic(cb_base + ARM_SMMU_CB_ATSR, tmp, -+ !(tmp & ATSR_ACTIVE), 5, 50)) { -+ dev_err(dev, -+ "iova to phys timed out on 0x%pad. Falling back to software table walk.\n", -+ &iova); -+ return ops->iova_to_phys(ops, iova); -+ } - -- return arm_smmu_handle_mapping(smmu_domain, iova, paddr, size, prot); --} -+ phys = readl_relaxed(cb_base + ARM_SMMU_CB_PAR_LO); -+ phys |= ((u64)readl_relaxed(cb_base + ARM_SMMU_CB_PAR_HI)) << 32; - --static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova, -- size_t size) --{ -- int ret; -- struct arm_smmu_domain *smmu_domain = domain->priv; -+ if (phys & CB_PAR_F) { -+ dev_err(dev, "translation fault!\n"); -+ dev_err(dev, "PAR = 0x%llx\n", phys); -+ return 0; -+ } - -- ret = arm_smmu_handle_mapping(smmu_domain, iova, 0, size, 0); -- arm_smmu_tlb_inv_context(smmu_domain); -- return ret ? 0 : size; -+ return (phys & GENMASK_ULL(39, 12)) | (iova & 0xfff); - } - - static phys_addr_t arm_smmu_iova_to_phys(struct iommu_domain *domain, -- dma_addr_t iova) -+ dma_addr_t iova) - { -- pgd_t *pgdp, pgd; -- pud_t pud; -- pmd_t pmd; -- pte_t pte; -- struct arm_smmu_domain *smmu_domain = domain->priv; -- struct arm_smmu_cfg *cfg = &smmu_domain->cfg; -- -- pgdp = cfg->pgd; -- if (!pgdp) -- return 0; -- -- pgd = *(pgdp + pgd_index(iova)); -- if (pgd_none(pgd)) -- return 0; -+ phys_addr_t ret; -+ unsigned long flags; -+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); -+ struct io_pgtable_ops *ops= smmu_domain->pgtbl_ops; - -- pud = *pud_offset(&pgd, iova); -- if (pud_none(pud)) -+ if (!ops) - return 0; - -- pmd = *pmd_offset(&pud, iova); -- if (pmd_none(pmd)) -- return 0; -+ spin_lock_irqsave(&smmu_domain->pgtbl_lock, flags); -+ if (smmu_domain->smmu->features & ARM_SMMU_FEAT_TRANS_OPS && -+ smmu_domain->stage == ARM_SMMU_DOMAIN_S1) { -+ ret = arm_smmu_iova_to_phys_hard(domain, iova); -+ } else { -+ ret = ops->iova_to_phys(ops, iova); -+ } - -- pte = *(pmd_page_vaddr(pmd) + pte_index(iova)); -- if (pte_none(pte)) -- return 0; -+ spin_unlock_irqrestore(&smmu_domain->pgtbl_lock, flags); - -- return __pfn_to_phys(pte_pfn(pte)) | (iova & ~PAGE_MASK); -+ return ret; - } - - static bool arm_smmu_capable(enum iommu_cap cap) -@@ -1568,6 +1357,8 @@ static bool arm_smmu_capable(enum iommu_cap cap) - return true; - case IOMMU_CAP_INTR_REMAP: - return true; /* MSIs are just memory writes */ -+ case IOMMU_CAP_NOEXEC: -+ return true; - default: - return false; - } -@@ -1584,81 +1375,248 @@ static void __arm_smmu_release_pci_iommudata(void *data) - kfree(data); - } - --static int arm_smmu_add_device(struct device *dev) -+static int arm_smmu_add_pci_device(struct pci_dev *pdev) - { -- struct arm_smmu_device *smmu; -+ int i, ret; -+ u16 sid; -+ struct iommu_group *group; - struct arm_smmu_master_cfg *cfg; -+#ifdef CONFIG_PCI_LAYERSCAPE -+ u32 streamid; -+#endif -+ -+ group = iommu_group_get_for_dev(&pdev->dev); -+ if (IS_ERR(group)) -+ return PTR_ERR(group); -+ -+ cfg = iommu_group_get_iommudata(group); -+ if (!cfg) { -+ cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); -+ if (!cfg) { -+ ret = -ENOMEM; -+ goto out_put_group; -+ } -+ -+ iommu_group_set_iommudata(group, cfg, -+ __arm_smmu_release_pci_iommudata); -+ } -+ -+ if (cfg->num_streamids >= MAX_MASTER_STREAMIDS) { -+ ret = -ENOSPC; -+ goto out_put_group; -+ } -+ -+ /* -+ * Assume Stream ID == Requester ID for now. -+ * We need a way to describe the ID mappings in FDT. -+ */ -+ pci_for_each_dma_alias(pdev, __arm_smmu_get_pci_sid, &sid); -+ for (i = 0; i < cfg->num_streamids; ++i) -+ if (cfg->streamids[i] == sid) -+ break; -+ -+ /* Avoid duplicate SIDs, as this can lead to SMR conflicts */ -+ if (i == cfg->num_streamids) -+ cfg->streamids[cfg->num_streamids++] = sid; -+ -+#ifdef CONFIG_PCI_LAYERSCAPE -+ streamid = set_pcie_streamid_translation(pdev, sid); -+ if (~streamid == 0) { -+ ret = -ENODEV; -+ goto out_put_group; -+ } -+ cfg->streamids[0] = streamid; -+ cfg->mask = 0x7c00; -+ -+ pdev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVID; -+ pdev->dma_alias_devid = streamid; -+#endif -+ -+ return 0; -+out_put_group: -+ iommu_group_put(group); -+ return ret; -+} -+ -+static int arm_smmu_add_platform_device(struct device *dev) -+{ - struct iommu_group *group; -- void (*releasefn)(void *) = NULL; -- int ret; -+ struct arm_smmu_master *master; -+ struct arm_smmu_device *smmu = find_smmu_for_device(dev); - -- smmu = find_smmu_for_device(dev); - if (!smmu) - return -ENODEV; - -+ master = find_smmu_master(smmu, dev->of_node); -+ if (!master) -+ return -ENODEV; -+ -+ /* No automatic group creation for platform devices */ - group = iommu_group_alloc(); -- if (IS_ERR(group)) { -- dev_err(dev, "Failed to allocate IOMMU group\n"); -+ if (IS_ERR(group)) - return PTR_ERR(group); -+ -+ iommu_group_set_iommudata(group, &master->cfg, NULL); -+ return iommu_group_add_device(group, dev); -+} -+ -+static int arm_smmu_add_device(struct device *dev) -+{ -+ if (dev_is_pci(dev)) -+ return arm_smmu_add_pci_device(to_pci_dev(dev)); -+ -+ return arm_smmu_add_platform_device(dev); -+} -+ -+static void arm_smmu_remove_device(struct device *dev) -+{ -+ iommu_group_remove_device(dev); -+} -+ -+static int arm_smmu_domain_get_attr(struct iommu_domain *domain, -+ enum iommu_attr attr, void *data) -+{ -+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); -+ -+ switch (attr) { -+ case DOMAIN_ATTR_NESTING: -+ *(int *)data = (smmu_domain->stage == ARM_SMMU_DOMAIN_NESTED); -+ return 0; -+ default: -+ return -ENODEV; - } -+} - -- if (dev_is_pci(dev)) { -- struct pci_dev *pdev = to_pci_dev(dev); -+static int arm_smmu_domain_set_attr(struct iommu_domain *domain, -+ enum iommu_attr attr, void *data) -+{ -+ int ret = 0; -+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); - -- cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); -- if (!cfg) { -- ret = -ENOMEM; -- goto out_put_group; -+ mutex_lock(&smmu_domain->init_mutex); -+ -+ switch (attr) { -+ case DOMAIN_ATTR_NESTING: -+ if (smmu_domain->smmu) { -+ ret = -EPERM; -+ goto out_unlock; - } - -- cfg->num_streamids = 1; -+ if (*(int *)data) -+ smmu_domain->stage = ARM_SMMU_DOMAIN_NESTED; -+ else -+ smmu_domain->stage = ARM_SMMU_DOMAIN_S1; -+ -+ break; -+ default: -+ ret = -ENODEV; -+ } -+ -+out_unlock: -+ mutex_unlock(&smmu_domain->init_mutex); -+ return ret; -+} -+ -+static struct iommu_ops arm_smmu_ops = { -+ .capable = arm_smmu_capable, -+ .domain_alloc = arm_smmu_domain_alloc, -+ .domain_free = arm_smmu_domain_free, -+ .attach_dev = arm_smmu_attach_dev, -+ .detach_dev = arm_smmu_detach_dev, -+ .map = arm_smmu_map, -+ .unmap = arm_smmu_unmap, -+ .iova_to_phys = arm_smmu_iova_to_phys, -+ .add_device = arm_smmu_add_device, -+ .remove_device = arm_smmu_remove_device, -+ .domain_get_attr = arm_smmu_domain_get_attr, -+ .domain_set_attr = arm_smmu_domain_set_attr, -+ .pgsize_bitmap = -1UL, /* Restricted during device attach */ -+}; -+ -+#ifdef CONFIG_FSL_MC_BUS -+ -+static void arm_smmu_release_fsl_mc_iommudata(void *data) -+{ -+ kfree(data); -+} -+ -+/* -+ * IOMMU group creation and stream ID programming for -+ * the LS devices -+ * -+ */ -+static int arm_fsl_mc_smmu_add_device(struct device *dev) -+{ -+ struct device *cont_dev; -+ struct fsl_mc_device *mc_dev; -+ struct iommu_group *group; -+ struct arm_smmu_master_cfg *cfg; -+ int ret = 0; -+ -+ mc_dev = to_fsl_mc_device(dev); -+ if (mc_dev->flags & FSL_MC_IS_DPRC) -+ cont_dev = dev; -+ else -+ cont_dev = mc_dev->dev.parent; -+ -+ get_device(cont_dev); -+ group = iommu_group_get(cont_dev); -+ put_device(cont_dev); -+ if (!group) { -+ void (*releasefn)(void *) = NULL; -+ -+ group = iommu_group_alloc(); -+ if (IS_ERR(group)) -+ return PTR_ERR(group); - /* -- * Assume Stream ID == Requester ID for now. -- * We need a way to describe the ID mappings in FDT. -+ * allocate the cfg for the container and associate it with -+ * the iommu group. In the find cfg function we get the cfg -+ * from the iommu group. - */ -- pci_for_each_dma_alias(pdev, __arm_smmu_get_pci_sid, -- &cfg->streamids[0]); -- releasefn = __arm_smmu_release_pci_iommudata; -- } else { -- struct arm_smmu_master *master; -- -- master = find_smmu_master(smmu, dev->of_node); -- if (!master) { -- ret = -ENODEV; -- goto out_put_group; -- } -+ cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); -+ if (!cfg) -+ return -ENOMEM; - -- cfg = &master->cfg; -+ mc_dev = to_fsl_mc_device(cont_dev); -+ cfg->num_streamids = 1; -+ cfg->streamids[0] = mc_dev->icid; -+ cfg->mask = 0x7c00; -+ releasefn = arm_smmu_release_fsl_mc_iommudata; -+ iommu_group_set_iommudata(group, cfg, releasefn); -+ ret = iommu_group_add_device(group, cont_dev); - } - -- iommu_group_set_iommudata(group, cfg, releasefn); -- ret = iommu_group_add_device(group, dev); -+ if (!ret && cont_dev != dev) -+ ret = iommu_group_add_device(group, dev); - --out_put_group: - iommu_group_put(group); -+ - return ret; - } - --static void arm_smmu_remove_device(struct device *dev) -+static void arm_fsl_mc_smmu_remove_device(struct device *dev) - { - iommu_group_remove_device(dev); -+ - } - --static const struct iommu_ops arm_smmu_ops = { -- .capable = arm_smmu_capable, -- .domain_init = arm_smmu_domain_init, -- .domain_destroy = arm_smmu_domain_destroy, -- .attach_dev = arm_smmu_attach_dev, -- .detach_dev = arm_smmu_detach_dev, -- .map = arm_smmu_map, -- .unmap = arm_smmu_unmap, -- .iova_to_phys = arm_smmu_iova_to_phys, -- .add_device = arm_smmu_add_device, -- .remove_device = arm_smmu_remove_device, -- .pgsize_bitmap = (SECTION_SIZE | -- ARM_SMMU_PTE_CONT_SIZE | -- PAGE_SIZE), -+static struct iommu_ops arm_fsl_mc_smmu_ops = { -+ .capable = arm_smmu_capable, -+ .domain_alloc = arm_smmu_domain_alloc, -+ .domain_free = arm_smmu_domain_free, -+ .attach_dev = arm_smmu_attach_dev, -+ .detach_dev = arm_smmu_detach_dev, -+ .map = arm_smmu_map, -+ .unmap = arm_smmu_unmap, -+ .map_sg = default_iommu_map_sg, -+ .iova_to_phys = arm_smmu_iova_to_phys, -+ .add_device = arm_fsl_mc_smmu_add_device, -+ .remove_device = arm_fsl_mc_smmu_remove_device, -+ .domain_get_attr = arm_smmu_domain_get_attr, -+ .domain_set_attr = arm_smmu_domain_set_attr, -+ .pgsize_bitmap = -1UL, /* Restricted during device attach */ - }; -+#endif - - static void arm_smmu_device_reset(struct arm_smmu_device *smmu) - { -@@ -1686,7 +1644,6 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu) - } - - /* Invalidate the TLB, just in case */ -- writel_relaxed(0, gr0_base + ARM_SMMU_GR0_STLBIALL); - writel_relaxed(0, gr0_base + ARM_SMMU_GR0_TLBIALLH); - writel_relaxed(0, gr0_base + ARM_SMMU_GR0_TLBIALLNSNH); - -@@ -1708,7 +1665,7 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu) - reg &= ~(sCR0_BSU_MASK << sCR0_BSU_SHIFT); - - /* Push the button */ -- arm_smmu_tlb_sync(smmu); -+ __arm_smmu_tlb_sync(smmu); - writel(reg, ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0); - } - -@@ -1742,12 +1699,6 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) - - /* ID0 */ - id = readl_relaxed(gr0_base + ARM_SMMU_GR0_ID0); --#ifndef CONFIG_64BIT -- if (((id >> ID0_PTFS_SHIFT) & ID0_PTFS_MASK) == ID0_PTFS_V8_ONLY) { -- dev_err(smmu->dev, "\tno v7 descriptor support!\n"); -- return -ENODEV; -- } --#endif - - /* Restrict available stages based on module parameter */ - if (force_stage == 1) -@@ -1776,6 +1727,11 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) - return -ENODEV; - } - -+ if ((id & ID0_S1TS) && ((smmu->version == 1) || !(id & ID0_ATOSNS))) { -+ smmu->features |= ARM_SMMU_FEAT_TRANS_OPS; -+ dev_notice(smmu->dev, "\taddress translation ops\n"); -+ } -+ - if (id & ID0_CTTW) { - smmu->features |= ARM_SMMU_FEAT_COHERENT_WALK; - dev_notice(smmu->dev, "\tcoherent table walk\n"); -@@ -1820,16 +1776,14 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) - smmu->pgshift = (id & ID1_PAGESIZE) ? 16 : 12; - - /* Check for size mismatch of SMMU address space from mapped region */ -- size = 1 << -- (((id >> ID1_NUMPAGENDXB_SHIFT) & ID1_NUMPAGENDXB_MASK) + 1); -+ size = 1 << (((id >> ID1_NUMPAGENDXB_SHIFT) & ID1_NUMPAGENDXB_MASK) + 1); - size *= 2 << smmu->pgshift; - if (smmu->size != size) - dev_warn(smmu->dev, - "SMMU address space size (0x%lx) differs from mapped region size (0x%lx)!\n", - size, smmu->size); - -- smmu->num_s2_context_banks = (id >> ID1_NUMS2CB_SHIFT) & -- ID1_NUMS2CB_MASK; -+ smmu->num_s2_context_banks = (id >> ID1_NUMS2CB_SHIFT) & ID1_NUMS2CB_MASK; - smmu->num_context_banks = (id >> ID1_NUMCB_SHIFT) & ID1_NUMCB_MASK; - if (smmu->num_s2_context_banks > smmu->num_context_banks) { - dev_err(smmu->dev, "impossible number of S2 context banks!\n"); -@@ -1841,46 +1795,49 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) - /* ID2 */ - id = readl_relaxed(gr0_base + ARM_SMMU_GR0_ID2); - size = arm_smmu_id_size_to_bits((id >> ID2_IAS_SHIFT) & ID2_IAS_MASK); -- smmu->s1_output_size = min_t(unsigned long, PHYS_MASK_SHIFT, size); -- -- /* Stage-2 input size limited due to pgd allocation (PTRS_PER_PGD) */ --#ifdef CONFIG_64BIT -- smmu->s2_input_size = min_t(unsigned long, VA_BITS, size); --#else -- smmu->s2_input_size = min(32UL, size); --#endif -+ smmu->ipa_size = size; - -- /* The stage-2 output mask is also applied for bypass */ -+ /* The output mask is also applied for bypass */ - size = arm_smmu_id_size_to_bits((id >> ID2_OAS_SHIFT) & ID2_OAS_MASK); -- smmu->s2_output_size = min_t(unsigned long, PHYS_MASK_SHIFT, size); -+ smmu->pa_size = size; -+ -+ /* -+ * What the page table walker can address actually depends on which -+ * descriptor format is in use, but since a) we don't know that yet, -+ * and b) it can vary per context bank, this will have to do... -+ */ -+ if (dma_set_mask_and_coherent(smmu->dev, DMA_BIT_MASK(size))) -+ dev_warn(smmu->dev, -+ "failed to set DMA mask for table walker\n"); - - if (smmu->version == ARM_SMMU_V1) { -- smmu->s1_input_size = 32; -+ smmu->va_size = smmu->ipa_size; -+ size = SZ_4K | SZ_2M | SZ_1G; - } else { --#ifdef CONFIG_64BIT - size = (id >> ID2_UBS_SHIFT) & ID2_UBS_MASK; -- size = min(VA_BITS, arm_smmu_id_size_to_bits(size)); --#else -- size = 32; -+ smmu->va_size = arm_smmu_id_size_to_bits(size); -+#ifndef CONFIG_64BIT -+ smmu->va_size = min(32UL, smmu->va_size); - #endif -- smmu->s1_input_size = size; -- -- if ((PAGE_SIZE == SZ_4K && !(id & ID2_PTFS_4K)) || -- (PAGE_SIZE == SZ_64K && !(id & ID2_PTFS_64K)) || -- (PAGE_SIZE != SZ_4K && PAGE_SIZE != SZ_64K)) { -- dev_err(smmu->dev, "CPU page size 0x%lx unsupported\n", -- PAGE_SIZE); -- return -ENODEV; -- } -+ size = 0; -+ if (id & ID2_PTFS_4K) -+ size |= SZ_4K | SZ_2M | SZ_1G; -+ if (id & ID2_PTFS_16K) -+ size |= SZ_16K | SZ_32M; -+ if (id & ID2_PTFS_64K) -+ size |= SZ_64K | SZ_512M; - } - -+ arm_smmu_ops.pgsize_bitmap &= size; -+ dev_notice(smmu->dev, "\tSupported page sizes: 0x%08lx\n", size); -+ - if (smmu->features & ARM_SMMU_FEAT_TRANS_S1) - dev_notice(smmu->dev, "\tStage-1: %lu-bit VA -> %lu-bit IPA\n", -- smmu->s1_input_size, smmu->s1_output_size); -+ smmu->va_size, smmu->ipa_size); - - if (smmu->features & ARM_SMMU_FEAT_TRANS_S2) - dev_notice(smmu->dev, "\tStage-2: %lu-bit IPA -> %lu-bit PA\n", -- smmu->s2_input_size, smmu->s2_output_size); -+ smmu->ipa_size, smmu->pa_size); - - return 0; - } -@@ -2007,6 +1964,10 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev) - spin_unlock(&arm_smmu_devices_lock); - - arm_smmu_device_reset(smmu); -+ /* AIOP Rev1 errata work around */ -+#ifdef CONFIG_AIOP_ERRATA -+ arm_smmu_aiop_attr_trans(smmu); -+#endif - return 0; - - out_free_irqs: -@@ -2062,7 +2023,6 @@ static int arm_smmu_device_remove(struct platform_device *pdev) - - static struct platform_driver arm_smmu_driver = { - .driver = { -- .owner = THIS_MODULE, - .name = "arm-smmu", - .of_match_table = of_match_ptr(arm_smmu_of_match), - }, -@@ -2072,8 +2032,20 @@ static struct platform_driver arm_smmu_driver = { - - static int __init arm_smmu_init(void) - { -+ struct device_node *np; - int ret; - -+ /* -+ * Play nice with systems that don't have an ARM SMMU by checking that -+ * an ARM SMMU exists in the system before proceeding with the driver -+ * and IOMMU bus operation registration. -+ */ -+ np = of_find_matching_node(NULL, arm_smmu_of_match); -+ if (!np) -+ return 0; -+ -+ of_node_put(np); -+ - ret = platform_driver_register(&arm_smmu_driver); - if (ret) - return ret; -@@ -2092,6 +2064,10 @@ static int __init arm_smmu_init(void) - bus_set_iommu(&pci_bus_type, &arm_smmu_ops); - #endif - -+#ifdef CONFIG_FSL_MC_BUS -+ if (!iommu_present(&fsl_mc_bus_type)) -+ bus_set_iommu(&fsl_mc_bus_type, &arm_fsl_mc_smmu_ops); -+#endif - return 0; - } - -diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c -index 7423318..7ce5273 100644 ---- a/drivers/iommu/exynos-iommu.c -+++ b/drivers/iommu/exynos-iommu.c -@@ -684,7 +684,6 @@ static const struct of_device_id sysmmu_of_match[] __initconst = { - static struct platform_driver exynos_sysmmu_driver __refdata = { - .probe = exynos_sysmmu_probe, - .driver = { -- .owner = THIS_MODULE, - .name = "exynos-sysmmu", - .of_match_table = sysmmu_of_match, - } -@@ -1178,6 +1177,7 @@ static const struct iommu_ops exynos_iommu_ops = { - .detach_dev = exynos_iommu_detach_device, - .map = exynos_iommu_map, - .unmap = exynos_iommu_unmap, -+ .map_sg = default_iommu_map_sg, - .iova_to_phys = exynos_iommu_iova_to_phys, - .add_device = exynos_iommu_add_device, - .remove_device = exynos_iommu_remove_device, -diff --git a/drivers/iommu/fsl_pamu.c b/drivers/iommu/fsl_pamu.c -index 2b6ce93..80ac68d 100644 ---- a/drivers/iommu/fsl_pamu.c -+++ b/drivers/iommu/fsl_pamu.c -@@ -1227,7 +1227,6 @@ static const struct of_device_id fsl_of_pamu_ids[] = { - static struct platform_driver fsl_of_pamu_driver = { - .driver = { - .name = "fsl-of-pamu", -- .owner = THIS_MODULE, - }, - .probe = fsl_pamu_probe, - }; -diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c -index 3d1fc73..9e97328 100644 ---- a/drivers/iommu/intel-iommu.c -+++ b/drivers/iommu/intel-iommu.c -@@ -4474,6 +4474,7 @@ static const struct iommu_ops intel_iommu_ops = { - .detach_dev = intel_iommu_detach_device, - .map = intel_iommu_map, - .unmap = intel_iommu_unmap, -+ .map_sg = default_iommu_map_sg, - .iova_to_phys = intel_iommu_iova_to_phys, - .add_device = intel_iommu_add_device, - .remove_device = intel_iommu_remove_device, -diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c -new file mode 100644 -index 0000000..5a500ed ---- /dev/null -+++ b/drivers/iommu/io-pgtable-arm.c -@@ -0,0 +1,986 @@ -+/* -+ * CPU-agnostic ARM page table allocator. -+ * -+ * 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. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ * -+ * Copyright (C) 2014 ARM Limited -+ * -+ * Author: Will Deacon -+ */ -+ -+#define pr_fmt(fmt) "arm-lpae io-pgtable: " fmt -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include "io-pgtable.h" -+ -+#define ARM_LPAE_MAX_ADDR_BITS 48 -+#define ARM_LPAE_S2_MAX_CONCAT_PAGES 16 -+#define ARM_LPAE_MAX_LEVELS 4 -+ -+/* Struct accessors */ -+#define io_pgtable_to_data(x) \ -+ container_of((x), struct arm_lpae_io_pgtable, iop) -+ -+#define io_pgtable_ops_to_pgtable(x) \ -+ container_of((x), struct io_pgtable, ops) -+ -+#define io_pgtable_ops_to_data(x) \ -+ io_pgtable_to_data(io_pgtable_ops_to_pgtable(x)) -+ -+/* -+ * For consistency with the architecture, we always consider -+ * ARM_LPAE_MAX_LEVELS levels, with the walk starting at level n >=0 -+ */ -+#define ARM_LPAE_START_LVL(d) (ARM_LPAE_MAX_LEVELS - (d)->levels) -+ -+/* -+ * Calculate the right shift amount to get to the portion describing level l -+ * in a virtual address mapped by the pagetable in d. -+ */ -+#define ARM_LPAE_LVL_SHIFT(l,d) \ -+ ((((d)->levels - ((l) - ARM_LPAE_START_LVL(d) + 1)) \ -+ * (d)->bits_per_level) + (d)->pg_shift) -+ -+#define ARM_LPAE_PAGES_PER_PGD(d) ((d)->pgd_size >> (d)->pg_shift) -+ -+/* -+ * Calculate the index at level l used to map virtual address a using the -+ * pagetable in d. -+ */ -+#define ARM_LPAE_PGD_IDX(l,d) \ -+ ((l) == ARM_LPAE_START_LVL(d) ? ilog2(ARM_LPAE_PAGES_PER_PGD(d)) : 0) -+ -+#define ARM_LPAE_LVL_IDX(a,l,d) \ -+ (((a) >> ARM_LPAE_LVL_SHIFT(l,d)) & \ -+ ((1 << ((d)->bits_per_level + ARM_LPAE_PGD_IDX(l,d))) - 1)) -+ -+/* Calculate the block/page mapping size at level l for pagetable in d. */ -+#define ARM_LPAE_BLOCK_SIZE(l,d) \ -+ (1 << (ilog2(sizeof(arm_lpae_iopte)) + \ -+ ((ARM_LPAE_MAX_LEVELS - (l)) * (d)->bits_per_level))) -+ -+/* Page table bits */ -+#define ARM_LPAE_PTE_TYPE_SHIFT 0 -+#define ARM_LPAE_PTE_TYPE_MASK 0x3 -+ -+#define ARM_LPAE_PTE_TYPE_BLOCK 1 -+#define ARM_LPAE_PTE_TYPE_TABLE 3 -+#define ARM_LPAE_PTE_TYPE_PAGE 3 -+ -+#define ARM_LPAE_PTE_NSTABLE (((arm_lpae_iopte)1) << 63) -+#define ARM_LPAE_PTE_XN (((arm_lpae_iopte)3) << 53) -+#define ARM_LPAE_PTE_AF (((arm_lpae_iopte)1) << 10) -+#define ARM_LPAE_PTE_SH_NS (((arm_lpae_iopte)0) << 8) -+#define ARM_LPAE_PTE_SH_OS (((arm_lpae_iopte)2) << 8) -+#define ARM_LPAE_PTE_SH_IS (((arm_lpae_iopte)3) << 8) -+#define ARM_LPAE_PTE_NS (((arm_lpae_iopte)1) << 5) -+#define ARM_LPAE_PTE_VALID (((arm_lpae_iopte)1) << 0) -+ -+#define ARM_LPAE_PTE_ATTR_LO_MASK (((arm_lpae_iopte)0x3ff) << 2) -+/* Ignore the contiguous bit for block splitting */ -+#define ARM_LPAE_PTE_ATTR_HI_MASK (((arm_lpae_iopte)6) << 52) -+#define ARM_LPAE_PTE_ATTR_MASK (ARM_LPAE_PTE_ATTR_LO_MASK | \ -+ ARM_LPAE_PTE_ATTR_HI_MASK) -+ -+/* Stage-1 PTE */ -+#define ARM_LPAE_PTE_AP_UNPRIV (((arm_lpae_iopte)1) << 6) -+#define ARM_LPAE_PTE_AP_RDONLY (((arm_lpae_iopte)2) << 6) -+#define ARM_LPAE_PTE_ATTRINDX_SHIFT 2 -+#define ARM_LPAE_PTE_nG (((arm_lpae_iopte)1) << 11) -+ -+/* Stage-2 PTE */ -+#define ARM_LPAE_PTE_HAP_FAULT (((arm_lpae_iopte)0) << 6) -+#define ARM_LPAE_PTE_HAP_READ (((arm_lpae_iopte)1) << 6) -+#define ARM_LPAE_PTE_HAP_WRITE (((arm_lpae_iopte)2) << 6) -+#define ARM_LPAE_PTE_MEMATTR_OIWB (((arm_lpae_iopte)0xf) << 2) -+#define ARM_LPAE_PTE_MEMATTR_NC (((arm_lpae_iopte)0x5) << 2) -+#define ARM_LPAE_PTE_MEMATTR_DEV (((arm_lpae_iopte)0x1) << 2) -+ -+/* Register bits */ -+#define ARM_32_LPAE_TCR_EAE (1 << 31) -+#define ARM_64_LPAE_S2_TCR_RES1 (1 << 31) -+ -+#define ARM_LPAE_TCR_TG0_4K (0 << 14) -+#define ARM_LPAE_TCR_TG0_64K (1 << 14) -+#define ARM_LPAE_TCR_TG0_16K (2 << 14) -+ -+#define ARM_LPAE_TCR_SH0_SHIFT 12 -+#define ARM_LPAE_TCR_SH0_MASK 0x3 -+#define ARM_LPAE_TCR_SH_NS 0 -+#define ARM_LPAE_TCR_SH_OS 2 -+#define ARM_LPAE_TCR_SH_IS 3 -+ -+#define ARM_LPAE_TCR_ORGN0_SHIFT 10 -+#define ARM_LPAE_TCR_IRGN0_SHIFT 8 -+#define ARM_LPAE_TCR_RGN_MASK 0x3 -+#define ARM_LPAE_TCR_RGN_NC 0 -+#define ARM_LPAE_TCR_RGN_WBWA 1 -+#define ARM_LPAE_TCR_RGN_WT 2 -+#define ARM_LPAE_TCR_RGN_WB 3 -+ -+#define ARM_LPAE_TCR_SL0_SHIFT 6 -+#define ARM_LPAE_TCR_SL0_MASK 0x3 -+ -+#define ARM_LPAE_TCR_T0SZ_SHIFT 0 -+#define ARM_LPAE_TCR_SZ_MASK 0xf -+ -+#define ARM_LPAE_TCR_PS_SHIFT 16 -+#define ARM_LPAE_TCR_PS_MASK 0x7 -+ -+#define ARM_LPAE_TCR_IPS_SHIFT 32 -+#define ARM_LPAE_TCR_IPS_MASK 0x7 -+ -+#define ARM_LPAE_TCR_PS_32_BIT 0x0ULL -+#define ARM_LPAE_TCR_PS_36_BIT 0x1ULL -+#define ARM_LPAE_TCR_PS_40_BIT 0x2ULL -+#define ARM_LPAE_TCR_PS_42_BIT 0x3ULL -+#define ARM_LPAE_TCR_PS_44_BIT 0x4ULL -+#define ARM_LPAE_TCR_PS_48_BIT 0x5ULL -+ -+#define ARM_LPAE_MAIR_ATTR_SHIFT(n) ((n) << 3) -+#define ARM_LPAE_MAIR_ATTR_MASK 0xff -+#define ARM_LPAE_MAIR_ATTR_DEVICE 0x04 -+#define ARM_LPAE_MAIR_ATTR_NC 0x44 -+#define ARM_LPAE_MAIR_ATTR_WBRWA 0xff -+#define ARM_LPAE_MAIR_ATTR_IDX_NC 0 -+#define ARM_LPAE_MAIR_ATTR_IDX_CACHE 1 -+#define ARM_LPAE_MAIR_ATTR_IDX_DEV 2 -+ -+/* IOPTE accessors */ -+#define iopte_deref(pte,d) \ -+ (__va((pte) & ((1ULL << ARM_LPAE_MAX_ADDR_BITS) - 1) \ -+ & ~((1ULL << (d)->pg_shift) - 1))) -+ -+#define iopte_type(pte,l) \ -+ (((pte) >> ARM_LPAE_PTE_TYPE_SHIFT) & ARM_LPAE_PTE_TYPE_MASK) -+ -+#define iopte_prot(pte) ((pte) & ARM_LPAE_PTE_ATTR_MASK) -+ -+#define iopte_leaf(pte,l) \ -+ (l == (ARM_LPAE_MAX_LEVELS - 1) ? \ -+ (iopte_type(pte,l) == ARM_LPAE_PTE_TYPE_PAGE) : \ -+ (iopte_type(pte,l) == ARM_LPAE_PTE_TYPE_BLOCK)) -+ -+#define iopte_to_pfn(pte,d) \ -+ (((pte) & ((1ULL << ARM_LPAE_MAX_ADDR_BITS) - 1)) >> (d)->pg_shift) -+ -+#define pfn_to_iopte(pfn,d) \ -+ (((pfn) << (d)->pg_shift) & ((1ULL << ARM_LPAE_MAX_ADDR_BITS) - 1)) -+ -+struct arm_lpae_io_pgtable { -+ struct io_pgtable iop; -+ -+ int levels; -+ size_t pgd_size; -+ unsigned long pg_shift; -+ unsigned long bits_per_level; -+ -+ void *pgd; -+}; -+ -+typedef u64 arm_lpae_iopte; -+ -+static bool selftest_running = false; -+ -+static int arm_lpae_init_pte(struct arm_lpae_io_pgtable *data, -+ unsigned long iova, phys_addr_t paddr, -+ arm_lpae_iopte prot, int lvl, -+ arm_lpae_iopte *ptep) -+{ -+ arm_lpae_iopte pte = prot; -+ -+ /* We require an unmap first */ -+ if (iopte_leaf(*ptep, lvl)) { -+ WARN_ON(!selftest_running); -+ return -EEXIST; -+ } -+ -+ if (data->iop.cfg.quirks & IO_PGTABLE_QUIRK_ARM_NS) -+ pte |= ARM_LPAE_PTE_NS; -+ -+ if (lvl == ARM_LPAE_MAX_LEVELS - 1) -+ pte |= ARM_LPAE_PTE_TYPE_PAGE; -+ else -+ pte |= ARM_LPAE_PTE_TYPE_BLOCK; -+ -+ pte |= ARM_LPAE_PTE_AF | ARM_LPAE_PTE_SH_IS; -+ pte |= pfn_to_iopte(paddr >> data->pg_shift, data); -+ -+ *ptep = pte; -+ data->iop.cfg.tlb->flush_pgtable(ptep, sizeof(*ptep), data->iop.cookie); -+ return 0; -+} -+ -+static int __arm_lpae_map(struct arm_lpae_io_pgtable *data, unsigned long iova, -+ phys_addr_t paddr, size_t size, arm_lpae_iopte prot, -+ int lvl, arm_lpae_iopte *ptep) -+{ -+ arm_lpae_iopte *cptep, pte; -+ void *cookie = data->iop.cookie; -+ size_t block_size = ARM_LPAE_BLOCK_SIZE(lvl, data); -+ -+ /* Find our entry at the current level */ -+ ptep += ARM_LPAE_LVL_IDX(iova, lvl, data); -+ -+ /* If we can install a leaf entry at this level, then do so */ -+ if (size == block_size && (size & data->iop.cfg.pgsize_bitmap)) -+ return arm_lpae_init_pte(data, iova, paddr, prot, lvl, ptep); -+ -+ /* We can't allocate tables at the final level */ -+ if (WARN_ON(lvl >= ARM_LPAE_MAX_LEVELS - 1)) -+ return -EINVAL; -+ -+ /* Grab a pointer to the next level */ -+ pte = *ptep; -+ if (!pte) { -+ cptep = alloc_pages_exact(1UL << data->pg_shift, -+ GFP_ATOMIC | __GFP_ZERO); -+ if (!cptep) -+ return -ENOMEM; -+ -+ data->iop.cfg.tlb->flush_pgtable(cptep, 1UL << data->pg_shift, -+ cookie); -+ pte = __pa(cptep) | ARM_LPAE_PTE_TYPE_TABLE; -+ if (data->iop.cfg.quirks & IO_PGTABLE_QUIRK_ARM_NS) -+ pte |= ARM_LPAE_PTE_NSTABLE; -+ *ptep = pte; -+ data->iop.cfg.tlb->flush_pgtable(ptep, sizeof(*ptep), cookie); -+ } else { -+ cptep = iopte_deref(pte, data); -+ } -+ -+ /* Rinse, repeat */ -+ return __arm_lpae_map(data, iova, paddr, size, prot, lvl + 1, cptep); -+} -+ -+static arm_lpae_iopte arm_lpae_prot_to_pte(struct arm_lpae_io_pgtable *data, -+ int prot) -+{ -+ arm_lpae_iopte pte; -+ -+ if (data->iop.fmt == ARM_64_LPAE_S1 || -+ data->iop.fmt == ARM_32_LPAE_S1) { -+ pte = ARM_LPAE_PTE_AP_UNPRIV | ARM_LPAE_PTE_nG; -+ -+ if (!(prot & IOMMU_WRITE) && (prot & IOMMU_READ)) -+ pte |= ARM_LPAE_PTE_AP_RDONLY; -+ -+ if (prot & IOMMU_CACHE) -+ pte |= (ARM_LPAE_MAIR_ATTR_IDX_CACHE -+ << ARM_LPAE_PTE_ATTRINDX_SHIFT); -+ } else { -+ pte = ARM_LPAE_PTE_HAP_FAULT; -+ if (prot & IOMMU_READ) -+ pte |= ARM_LPAE_PTE_HAP_READ; -+ if (prot & IOMMU_WRITE) -+ pte |= ARM_LPAE_PTE_HAP_WRITE; -+ if (prot & IOMMU_CACHE) -+ pte |= ARM_LPAE_PTE_MEMATTR_OIWB; -+ else -+ pte |= ARM_LPAE_PTE_MEMATTR_NC; -+ } -+ -+ if (prot & IOMMU_NOEXEC) -+ pte |= ARM_LPAE_PTE_XN; -+ -+ return pte; -+} -+ -+static int arm_lpae_map(struct io_pgtable_ops *ops, unsigned long iova, -+ phys_addr_t paddr, size_t size, int iommu_prot) -+{ -+ struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops); -+ arm_lpae_iopte *ptep = data->pgd; -+ int lvl = ARM_LPAE_START_LVL(data); -+ arm_lpae_iopte prot; -+ -+ /* If no access, then nothing to do */ -+ if (!(iommu_prot & (IOMMU_READ | IOMMU_WRITE))) -+ return 0; -+ -+ prot = arm_lpae_prot_to_pte(data, iommu_prot); -+ return __arm_lpae_map(data, iova, paddr, size, prot, lvl, ptep); -+} -+ -+static void __arm_lpae_free_pgtable(struct arm_lpae_io_pgtable *data, int lvl, -+ arm_lpae_iopte *ptep) -+{ -+ arm_lpae_iopte *start, *end; -+ unsigned long table_size; -+ -+ /* Only leaf entries at the last level */ -+ if (lvl == ARM_LPAE_MAX_LEVELS - 1) -+ return; -+ -+ if (lvl == ARM_LPAE_START_LVL(data)) -+ table_size = data->pgd_size; -+ else -+ table_size = 1UL << data->pg_shift; -+ -+ start = ptep; -+ end = (void *)ptep + table_size; -+ -+ while (ptep != end) { -+ arm_lpae_iopte pte = *ptep++; -+ -+ if (!pte || iopte_leaf(pte, lvl)) -+ continue; -+ -+ __arm_lpae_free_pgtable(data, lvl + 1, iopte_deref(pte, data)); -+ } -+ -+ free_pages_exact(start, table_size); -+} -+ -+static void arm_lpae_free_pgtable(struct io_pgtable *iop) -+{ -+ struct arm_lpae_io_pgtable *data = io_pgtable_to_data(iop); -+ -+ __arm_lpae_free_pgtable(data, ARM_LPAE_START_LVL(data), data->pgd); -+ kfree(data); -+} -+ -+static int arm_lpae_split_blk_unmap(struct arm_lpae_io_pgtable *data, -+ unsigned long iova, size_t size, -+ arm_lpae_iopte prot, int lvl, -+ arm_lpae_iopte *ptep, size_t blk_size) -+{ -+ unsigned long blk_start, blk_end; -+ phys_addr_t blk_paddr; -+ arm_lpae_iopte table = 0; -+ void *cookie = data->iop.cookie; -+ const struct iommu_gather_ops *tlb = data->iop.cfg.tlb; -+ -+ blk_start = iova & ~(blk_size - 1); -+ blk_end = blk_start + blk_size; -+ blk_paddr = iopte_to_pfn(*ptep, data) << data->pg_shift; -+ -+ for (; blk_start < blk_end; blk_start += size, blk_paddr += size) { -+ arm_lpae_iopte *tablep; -+ -+ /* Unmap! */ -+ if (blk_start == iova) -+ continue; -+ -+ /* __arm_lpae_map expects a pointer to the start of the table */ -+ tablep = &table - ARM_LPAE_LVL_IDX(blk_start, lvl, data); -+ if (__arm_lpae_map(data, blk_start, blk_paddr, size, prot, lvl, -+ tablep) < 0) { -+ if (table) { -+ /* Free the table we allocated */ -+ tablep = iopte_deref(table, data); -+ __arm_lpae_free_pgtable(data, lvl + 1, tablep); -+ } -+ return 0; /* Bytes unmapped */ -+ } -+ } -+ -+ *ptep = table; -+ tlb->flush_pgtable(ptep, sizeof(*ptep), cookie); -+ iova &= ~(blk_size - 1); -+ tlb->tlb_add_flush(iova, blk_size, true, cookie); -+ return size; -+} -+ -+static int __arm_lpae_unmap(struct arm_lpae_io_pgtable *data, -+ unsigned long iova, size_t size, int lvl, -+ arm_lpae_iopte *ptep) -+{ -+ arm_lpae_iopte pte; -+ const struct iommu_gather_ops *tlb = data->iop.cfg.tlb; -+ void *cookie = data->iop.cookie; -+ size_t blk_size = ARM_LPAE_BLOCK_SIZE(lvl, data); -+ -+ ptep += ARM_LPAE_LVL_IDX(iova, lvl, data); -+ pte = *ptep; -+ -+ /* Something went horribly wrong and we ran out of page table */ -+ if (WARN_ON(!pte || (lvl == ARM_LPAE_MAX_LEVELS))) -+ return 0; -+ -+ /* If the size matches this level, we're in the right place */ -+ if (size == blk_size) { -+ *ptep = 0; -+ tlb->flush_pgtable(ptep, sizeof(*ptep), cookie); -+ -+ if (!iopte_leaf(pte, lvl)) { -+ /* Also flush any partial walks */ -+ tlb->tlb_add_flush(iova, size, false, cookie); -+ tlb->tlb_sync(data->iop.cookie); -+ ptep = iopte_deref(pte, data); -+ __arm_lpae_free_pgtable(data, lvl + 1, ptep); -+ } else { -+ tlb->tlb_add_flush(iova, size, true, cookie); -+ } -+ -+ return size; -+ } else if (iopte_leaf(pte, lvl)) { -+ /* -+ * Insert a table at the next level to map the old region, -+ * minus the part we want to unmap -+ */ -+ return arm_lpae_split_blk_unmap(data, iova, size, -+ iopte_prot(pte), lvl, ptep, -+ blk_size); -+ } -+ -+ /* Keep on walkin' */ -+ ptep = iopte_deref(pte, data); -+ return __arm_lpae_unmap(data, iova, size, lvl + 1, ptep); -+} -+ -+static int arm_lpae_unmap(struct io_pgtable_ops *ops, unsigned long iova, -+ size_t size) -+{ -+ size_t unmapped; -+ struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops); -+ struct io_pgtable *iop = &data->iop; -+ arm_lpae_iopte *ptep = data->pgd; -+ int lvl = ARM_LPAE_START_LVL(data); -+ -+ unmapped = __arm_lpae_unmap(data, iova, size, lvl, ptep); -+ if (unmapped) -+ iop->cfg.tlb->tlb_sync(iop->cookie); -+ -+ return unmapped; -+} -+ -+static phys_addr_t arm_lpae_iova_to_phys(struct io_pgtable_ops *ops, -+ unsigned long iova) -+{ -+ struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops); -+ arm_lpae_iopte pte, *ptep = data->pgd; -+ int lvl = ARM_LPAE_START_LVL(data); -+ -+ do { -+ /* Valid IOPTE pointer? */ -+ if (!ptep) -+ return 0; -+ -+ /* Grab the IOPTE we're interested in */ -+ pte = *(ptep + ARM_LPAE_LVL_IDX(iova, lvl, data)); -+ -+ /* Valid entry? */ -+ if (!pte) -+ return 0; -+ -+ /* Leaf entry? */ -+ if (iopte_leaf(pte,lvl)) -+ goto found_translation; -+ -+ /* Take it to the next level */ -+ ptep = iopte_deref(pte, data); -+ } while (++lvl < ARM_LPAE_MAX_LEVELS); -+ -+ /* Ran out of page tables to walk */ -+ return 0; -+ -+found_translation: -+ iova &= ((1 << data->pg_shift) - 1); -+ return ((phys_addr_t)iopte_to_pfn(pte,data) << data->pg_shift) | iova; -+} -+ -+static void arm_lpae_restrict_pgsizes(struct io_pgtable_cfg *cfg) -+{ -+ unsigned long granule; -+ -+ /* -+ * We need to restrict the supported page sizes to match the -+ * translation regime for a particular granule. Aim to match -+ * the CPU page size if possible, otherwise prefer smaller sizes. -+ * While we're at it, restrict the block sizes to match the -+ * chosen granule. -+ */ -+ if (cfg->pgsize_bitmap & PAGE_SIZE) -+ granule = PAGE_SIZE; -+ else if (cfg->pgsize_bitmap & ~PAGE_MASK) -+ granule = 1UL << __fls(cfg->pgsize_bitmap & ~PAGE_MASK); -+ else if (cfg->pgsize_bitmap & PAGE_MASK) -+ granule = 1UL << __ffs(cfg->pgsize_bitmap & PAGE_MASK); -+ else -+ granule = 0; -+ -+ switch (granule) { -+ case SZ_4K: -+ cfg->pgsize_bitmap &= (SZ_4K | SZ_2M | SZ_1G); -+ break; -+ case SZ_16K: -+ cfg->pgsize_bitmap &= (SZ_16K | SZ_32M); -+ break; -+ case SZ_64K: -+ cfg->pgsize_bitmap &= (SZ_64K | SZ_512M); -+ break; -+ default: -+ cfg->pgsize_bitmap = 0; -+ } -+} -+ -+static struct arm_lpae_io_pgtable * -+arm_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg) -+{ -+ unsigned long va_bits, pgd_bits; -+ struct arm_lpae_io_pgtable *data; -+ -+ arm_lpae_restrict_pgsizes(cfg); -+ -+ if (!(cfg->pgsize_bitmap & (SZ_4K | SZ_16K | SZ_64K))) -+ return NULL; -+ -+ if (cfg->ias > ARM_LPAE_MAX_ADDR_BITS) -+ return NULL; -+ -+ if (cfg->oas > ARM_LPAE_MAX_ADDR_BITS) -+ return NULL; -+ -+ data = kmalloc(sizeof(*data), GFP_KERNEL); -+ if (!data) -+ return NULL; -+ -+ data->pg_shift = __ffs(cfg->pgsize_bitmap); -+ data->bits_per_level = data->pg_shift - ilog2(sizeof(arm_lpae_iopte)); -+ -+ va_bits = cfg->ias - data->pg_shift; -+ data->levels = DIV_ROUND_UP(va_bits, data->bits_per_level); -+ -+ /* Calculate the actual size of our pgd (without concatenation) */ -+ pgd_bits = va_bits - (data->bits_per_level * (data->levels - 1)); -+ data->pgd_size = 1UL << (pgd_bits + ilog2(sizeof(arm_lpae_iopte))); -+ -+ data->iop.ops = (struct io_pgtable_ops) { -+ .map = arm_lpae_map, -+ .unmap = arm_lpae_unmap, -+ .iova_to_phys = arm_lpae_iova_to_phys, -+ }; -+ -+ return data; -+} -+ -+static struct io_pgtable * -+arm_64_lpae_alloc_pgtable_s1(struct io_pgtable_cfg *cfg, void *cookie) -+{ -+ u64 reg; -+ struct arm_lpae_io_pgtable *data = arm_lpae_alloc_pgtable(cfg); -+ -+ if (!data) -+ return NULL; -+ -+ /* TCR */ -+ reg = (ARM_LPAE_TCR_SH_IS << ARM_LPAE_TCR_SH0_SHIFT) | -+ (ARM_LPAE_TCR_RGN_WBWA << ARM_LPAE_TCR_IRGN0_SHIFT) | -+ (ARM_LPAE_TCR_RGN_WBWA << ARM_LPAE_TCR_ORGN0_SHIFT); -+ -+ switch (1 << data->pg_shift) { -+ case SZ_4K: -+ reg |= ARM_LPAE_TCR_TG0_4K; -+ break; -+ case SZ_16K: -+ reg |= ARM_LPAE_TCR_TG0_16K; -+ break; -+ case SZ_64K: -+ reg |= ARM_LPAE_TCR_TG0_64K; -+ break; -+ } -+ -+ switch (cfg->oas) { -+ case 32: -+ reg |= (ARM_LPAE_TCR_PS_32_BIT << ARM_LPAE_TCR_IPS_SHIFT); -+ break; -+ case 36: -+ reg |= (ARM_LPAE_TCR_PS_36_BIT << ARM_LPAE_TCR_IPS_SHIFT); -+ break; -+ case 40: -+ reg |= (ARM_LPAE_TCR_PS_40_BIT << ARM_LPAE_TCR_IPS_SHIFT); -+ break; -+ case 42: -+ reg |= (ARM_LPAE_TCR_PS_42_BIT << ARM_LPAE_TCR_IPS_SHIFT); -+ break; -+ case 44: -+ reg |= (ARM_LPAE_TCR_PS_44_BIT << ARM_LPAE_TCR_IPS_SHIFT); -+ break; -+ case 48: -+ reg |= (ARM_LPAE_TCR_PS_48_BIT << ARM_LPAE_TCR_IPS_SHIFT); -+ break; -+ default: -+ goto out_free_data; -+ } -+ -+ reg |= (64ULL - cfg->ias) << ARM_LPAE_TCR_T0SZ_SHIFT; -+ cfg->arm_lpae_s1_cfg.tcr = reg; -+ -+ /* MAIRs */ -+ reg = (ARM_LPAE_MAIR_ATTR_NC -+ << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_NC)) | -+ (ARM_LPAE_MAIR_ATTR_WBRWA -+ << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_CACHE)) | -+ (ARM_LPAE_MAIR_ATTR_DEVICE -+ << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_DEV)); -+ -+ cfg->arm_lpae_s1_cfg.mair[0] = reg; -+ cfg->arm_lpae_s1_cfg.mair[1] = 0; -+ -+ /* Looking good; allocate a pgd */ -+ data->pgd = alloc_pages_exact(data->pgd_size, GFP_KERNEL | __GFP_ZERO); -+ if (!data->pgd) -+ goto out_free_data; -+ -+ cfg->tlb->flush_pgtable(data->pgd, data->pgd_size, cookie); -+ -+ /* TTBRs */ -+ cfg->arm_lpae_s1_cfg.ttbr[0] = virt_to_phys(data->pgd); -+ cfg->arm_lpae_s1_cfg.ttbr[1] = 0; -+ return &data->iop; -+ -+out_free_data: -+ kfree(data); -+ return NULL; -+} -+ -+static struct io_pgtable * -+arm_64_lpae_alloc_pgtable_s2(struct io_pgtable_cfg *cfg, void *cookie) -+{ -+ u64 reg, sl; -+ struct arm_lpae_io_pgtable *data = arm_lpae_alloc_pgtable(cfg); -+ -+ if (!data) -+ return NULL; -+ -+ /* -+ * Concatenate PGDs at level 1 if possible in order to reduce -+ * the depth of the stage-2 walk. -+ */ -+ if (data->levels == ARM_LPAE_MAX_LEVELS) { -+ unsigned long pgd_pages; -+ -+ pgd_pages = data->pgd_size >> ilog2(sizeof(arm_lpae_iopte)); -+ if (pgd_pages <= ARM_LPAE_S2_MAX_CONCAT_PAGES) { -+ data->pgd_size = pgd_pages << data->pg_shift; -+ data->levels--; -+ } -+ } -+ -+ /* VTCR */ -+ reg = ARM_64_LPAE_S2_TCR_RES1 | -+ (ARM_LPAE_TCR_SH_IS << ARM_LPAE_TCR_SH0_SHIFT) | -+ (ARM_LPAE_TCR_RGN_WBWA << ARM_LPAE_TCR_IRGN0_SHIFT) | -+ (ARM_LPAE_TCR_RGN_WBWA << ARM_LPAE_TCR_ORGN0_SHIFT); -+ -+ sl = ARM_LPAE_START_LVL(data); -+ -+ switch (1 << data->pg_shift) { -+ case SZ_4K: -+ reg |= ARM_LPAE_TCR_TG0_4K; -+ sl++; /* SL0 format is different for 4K granule size */ -+ break; -+ case SZ_16K: -+ reg |= ARM_LPAE_TCR_TG0_16K; -+ break; -+ case SZ_64K: -+ reg |= ARM_LPAE_TCR_TG0_64K; -+ break; -+ } -+ -+ switch (cfg->oas) { -+ case 32: -+ reg |= (ARM_LPAE_TCR_PS_32_BIT << ARM_LPAE_TCR_PS_SHIFT); -+ break; -+ case 36: -+ reg |= (ARM_LPAE_TCR_PS_36_BIT << ARM_LPAE_TCR_PS_SHIFT); -+ break; -+ case 40: -+ reg |= (ARM_LPAE_TCR_PS_40_BIT << ARM_LPAE_TCR_PS_SHIFT); -+ break; -+ case 42: -+ reg |= (ARM_LPAE_TCR_PS_42_BIT << ARM_LPAE_TCR_PS_SHIFT); -+ break; -+ case 44: -+ reg |= (ARM_LPAE_TCR_PS_44_BIT << ARM_LPAE_TCR_PS_SHIFT); -+ break; -+ case 48: -+ reg |= (ARM_LPAE_TCR_PS_48_BIT << ARM_LPAE_TCR_PS_SHIFT); -+ break; -+ default: -+ goto out_free_data; -+ } -+ -+ reg |= (64ULL - cfg->ias) << ARM_LPAE_TCR_T0SZ_SHIFT; -+ reg |= (~sl & ARM_LPAE_TCR_SL0_MASK) << ARM_LPAE_TCR_SL0_SHIFT; -+ cfg->arm_lpae_s2_cfg.vtcr = reg; -+ -+ /* Allocate pgd pages */ -+ data->pgd = alloc_pages_exact(data->pgd_size, GFP_KERNEL | __GFP_ZERO); -+ if (!data->pgd) -+ goto out_free_data; -+ -+ cfg->tlb->flush_pgtable(data->pgd, data->pgd_size, cookie); -+ -+ /* VTTBR */ -+ cfg->arm_lpae_s2_cfg.vttbr = virt_to_phys(data->pgd); -+ return &data->iop; -+ -+out_free_data: -+ kfree(data); -+ return NULL; -+} -+ -+static struct io_pgtable * -+arm_32_lpae_alloc_pgtable_s1(struct io_pgtable_cfg *cfg, void *cookie) -+{ -+ struct io_pgtable *iop; -+ -+ if (cfg->ias > 32 || cfg->oas > 40) -+ return NULL; -+ -+ cfg->pgsize_bitmap &= (SZ_4K | SZ_2M | SZ_1G); -+ iop = arm_64_lpae_alloc_pgtable_s1(cfg, cookie); -+ if (iop) { -+ cfg->arm_lpae_s1_cfg.tcr |= ARM_32_LPAE_TCR_EAE; -+ cfg->arm_lpae_s1_cfg.tcr &= 0xffffffff; -+ } -+ -+ return iop; -+} -+ -+static struct io_pgtable * -+arm_32_lpae_alloc_pgtable_s2(struct io_pgtable_cfg *cfg, void *cookie) -+{ -+ struct io_pgtable *iop; -+ -+ if (cfg->ias > 40 || cfg->oas > 40) -+ return NULL; -+ -+ cfg->pgsize_bitmap &= (SZ_4K | SZ_2M | SZ_1G); -+ iop = arm_64_lpae_alloc_pgtable_s2(cfg, cookie); -+ if (iop) -+ cfg->arm_lpae_s2_cfg.vtcr &= 0xffffffff; -+ -+ return iop; -+} -+ -+struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns = { -+ .alloc = arm_64_lpae_alloc_pgtable_s1, -+ .free = arm_lpae_free_pgtable, -+}; -+ -+struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s2_init_fns = { -+ .alloc = arm_64_lpae_alloc_pgtable_s2, -+ .free = arm_lpae_free_pgtable, -+}; -+ -+struct io_pgtable_init_fns io_pgtable_arm_32_lpae_s1_init_fns = { -+ .alloc = arm_32_lpae_alloc_pgtable_s1, -+ .free = arm_lpae_free_pgtable, -+}; -+ -+struct io_pgtable_init_fns io_pgtable_arm_32_lpae_s2_init_fns = { -+ .alloc = arm_32_lpae_alloc_pgtable_s2, -+ .free = arm_lpae_free_pgtable, -+}; -+ -+#ifdef CONFIG_IOMMU_IO_PGTABLE_LPAE_SELFTEST -+ -+static struct io_pgtable_cfg *cfg_cookie; -+ -+static void dummy_tlb_flush_all(void *cookie) -+{ -+ WARN_ON(cookie != cfg_cookie); -+} -+ -+static void dummy_tlb_add_flush(unsigned long iova, size_t size, bool leaf, -+ void *cookie) -+{ -+ WARN_ON(cookie != cfg_cookie); -+ WARN_ON(!(size & cfg_cookie->pgsize_bitmap)); -+} -+ -+static void dummy_tlb_sync(void *cookie) -+{ -+ WARN_ON(cookie != cfg_cookie); -+} -+ -+static void dummy_flush_pgtable(void *ptr, size_t size, void *cookie) -+{ -+ WARN_ON(cookie != cfg_cookie); -+} -+ -+static struct iommu_gather_ops dummy_tlb_ops __initdata = { -+ .tlb_flush_all = dummy_tlb_flush_all, -+ .tlb_add_flush = dummy_tlb_add_flush, -+ .tlb_sync = dummy_tlb_sync, -+ .flush_pgtable = dummy_flush_pgtable, -+}; -+ -+static void __init arm_lpae_dump_ops(struct io_pgtable_ops *ops) -+{ -+ struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops); -+ struct io_pgtable_cfg *cfg = &data->iop.cfg; -+ -+ pr_err("cfg: pgsize_bitmap 0x%lx, ias %u-bit\n", -+ cfg->pgsize_bitmap, cfg->ias); -+ pr_err("data: %d levels, 0x%zx pgd_size, %lu pg_shift, %lu bits_per_level, pgd @ %p\n", -+ data->levels, data->pgd_size, data->pg_shift, -+ data->bits_per_level, data->pgd); -+} -+ -+#define __FAIL(ops, i) ({ \ -+ WARN(1, "selftest: test failed for fmt idx %d\n", (i)); \ -+ arm_lpae_dump_ops(ops); \ -+ selftest_running = false; \ -+ -EFAULT; \ -+}) -+ -+static int __init arm_lpae_run_tests(struct io_pgtable_cfg *cfg) -+{ -+ static const enum io_pgtable_fmt fmts[] = { -+ ARM_64_LPAE_S1, -+ ARM_64_LPAE_S2, -+ }; -+ -+ int i, j; -+ unsigned long iova; -+ size_t size; -+ struct io_pgtable_ops *ops; -+ -+ selftest_running = true; -+ -+ for (i = 0; i < ARRAY_SIZE(fmts); ++i) { -+ cfg_cookie = cfg; -+ ops = alloc_io_pgtable_ops(fmts[i], cfg, cfg); -+ if (!ops) { -+ pr_err("selftest: failed to allocate io pgtable ops\n"); -+ return -ENOMEM; -+ } -+ -+ /* -+ * Initial sanity checks. -+ * Empty page tables shouldn't provide any translations. -+ */ -+ if (ops->iova_to_phys(ops, 42)) -+ return __FAIL(ops, i); -+ -+ if (ops->iova_to_phys(ops, SZ_1G + 42)) -+ return __FAIL(ops, i); -+ -+ if (ops->iova_to_phys(ops, SZ_2G + 42)) -+ return __FAIL(ops, i); -+ -+ /* -+ * Distinct mappings of different granule sizes. -+ */ -+ iova = 0; -+ j = find_first_bit(&cfg->pgsize_bitmap, BITS_PER_LONG); -+ while (j != BITS_PER_LONG) { -+ size = 1UL << j; -+ -+ if (ops->map(ops, iova, iova, size, IOMMU_READ | -+ IOMMU_WRITE | -+ IOMMU_NOEXEC | -+ IOMMU_CACHE)) -+ return __FAIL(ops, i); -+ -+ /* Overlapping mappings */ -+ if (!ops->map(ops, iova, iova + size, size, -+ IOMMU_READ | IOMMU_NOEXEC)) -+ return __FAIL(ops, i); -+ -+ if (ops->iova_to_phys(ops, iova + 42) != (iova + 42)) -+ return __FAIL(ops, i); -+ -+ iova += SZ_1G; -+ j++; -+ j = find_next_bit(&cfg->pgsize_bitmap, BITS_PER_LONG, j); -+ } -+ -+ /* Partial unmap */ -+ size = 1UL << __ffs(cfg->pgsize_bitmap); -+ if (ops->unmap(ops, SZ_1G + size, size) != size) -+ return __FAIL(ops, i); -+ -+ /* Remap of partial unmap */ -+ if (ops->map(ops, SZ_1G + size, size, size, IOMMU_READ)) -+ return __FAIL(ops, i); -+ -+ if (ops->iova_to_phys(ops, SZ_1G + size + 42) != (size + 42)) -+ return __FAIL(ops, i); -+ -+ /* Full unmap */ -+ iova = 0; -+ j = find_first_bit(&cfg->pgsize_bitmap, BITS_PER_LONG); -+ while (j != BITS_PER_LONG) { -+ size = 1UL << j; -+ -+ if (ops->unmap(ops, iova, size) != size) -+ return __FAIL(ops, i); -+ -+ if (ops->iova_to_phys(ops, iova + 42)) -+ return __FAIL(ops, i); -+ -+ /* Remap full block */ -+ if (ops->map(ops, iova, iova, size, IOMMU_WRITE)) -+ return __FAIL(ops, i); -+ -+ if (ops->iova_to_phys(ops, iova + 42) != (iova + 42)) -+ return __FAIL(ops, i); -+ -+ iova += SZ_1G; -+ j++; -+ j = find_next_bit(&cfg->pgsize_bitmap, BITS_PER_LONG, j); -+ } -+ -+ free_io_pgtable_ops(ops); -+ } -+ -+ selftest_running = false; -+ return 0; -+} -+ -+static int __init arm_lpae_do_selftests(void) -+{ -+ static const unsigned long pgsize[] = { -+ SZ_4K | SZ_2M | SZ_1G, -+ SZ_16K | SZ_32M, -+ SZ_64K | SZ_512M, -+ }; -+ -+ static const unsigned int ias[] = { -+ 32, 36, 40, 42, 44, 48, -+ }; -+ -+ int i, j, pass = 0, fail = 0; -+ struct io_pgtable_cfg cfg = { -+ .tlb = &dummy_tlb_ops, -+ .oas = 48, -+ }; -+ -+ for (i = 0; i < ARRAY_SIZE(pgsize); ++i) { -+ for (j = 0; j < ARRAY_SIZE(ias); ++j) { -+ cfg.pgsize_bitmap = pgsize[i]; -+ cfg.ias = ias[j]; -+ pr_info("selftest: pgsize_bitmap 0x%08lx, IAS %u\n", -+ pgsize[i], ias[j]); -+ if (arm_lpae_run_tests(&cfg)) -+ fail++; -+ else -+ pass++; -+ } -+ } -+ -+ pr_info("selftest: completed with %d PASS %d FAIL\n", pass, fail); -+ return fail ? -EFAULT : 0; -+} -+subsys_initcall(arm_lpae_do_selftests); -+#endif -diff --git a/drivers/iommu/io-pgtable.c b/drivers/iommu/io-pgtable.c -new file mode 100644 -index 0000000..6436fe2 ---- /dev/null -+++ b/drivers/iommu/io-pgtable.c -@@ -0,0 +1,82 @@ -+/* -+ * Generic page table allocator for IOMMUs. -+ * -+ * 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. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ * -+ * Copyright (C) 2014 ARM Limited -+ * -+ * Author: Will Deacon -+ */ -+ -+#include -+#include -+#include -+ -+#include "io-pgtable.h" -+ -+extern struct io_pgtable_init_fns io_pgtable_arm_32_lpae_s1_init_fns; -+extern struct io_pgtable_init_fns io_pgtable_arm_32_lpae_s2_init_fns; -+extern struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns; -+extern struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s2_init_fns; -+ -+static const struct io_pgtable_init_fns * -+io_pgtable_init_table[IO_PGTABLE_NUM_FMTS] = -+{ -+#ifdef CONFIG_IOMMU_IO_PGTABLE_LPAE -+ [ARM_32_LPAE_S1] = &io_pgtable_arm_32_lpae_s1_init_fns, -+ [ARM_32_LPAE_S2] = &io_pgtable_arm_32_lpae_s2_init_fns, -+ [ARM_64_LPAE_S1] = &io_pgtable_arm_64_lpae_s1_init_fns, -+ [ARM_64_LPAE_S2] = &io_pgtable_arm_64_lpae_s2_init_fns, -+#endif -+}; -+ -+struct io_pgtable_ops *alloc_io_pgtable_ops(enum io_pgtable_fmt fmt, -+ struct io_pgtable_cfg *cfg, -+ void *cookie) -+{ -+ struct io_pgtable *iop; -+ const struct io_pgtable_init_fns *fns; -+ -+ if (fmt >= IO_PGTABLE_NUM_FMTS) -+ return NULL; -+ -+ fns = io_pgtable_init_table[fmt]; -+ if (!fns) -+ return NULL; -+ -+ iop = fns->alloc(cfg, cookie); -+ if (!iop) -+ return NULL; -+ -+ iop->fmt = fmt; -+ iop->cookie = cookie; -+ iop->cfg = *cfg; -+ -+ return &iop->ops; -+} -+ -+/* -+ * It is the IOMMU driver's responsibility to ensure that the page table -+ * is no longer accessible to the walker by this point. -+ */ -+void free_io_pgtable_ops(struct io_pgtable_ops *ops) -+{ -+ struct io_pgtable *iop; -+ -+ if (!ops) -+ return; -+ -+ iop = container_of(ops, struct io_pgtable, ops); -+ iop->cfg.tlb->tlb_flush_all(iop->cookie); -+ io_pgtable_init_table[iop->fmt]->free(iop); -+} -diff --git a/drivers/iommu/io-pgtable.h b/drivers/iommu/io-pgtable.h -new file mode 100644 -index 0000000..10e32f6 ---- /dev/null -+++ b/drivers/iommu/io-pgtable.h -@@ -0,0 +1,143 @@ -+#ifndef __IO_PGTABLE_H -+#define __IO_PGTABLE_H -+ -+/* -+ * Public API for use by IOMMU drivers -+ */ -+enum io_pgtable_fmt { -+ ARM_32_LPAE_S1, -+ ARM_32_LPAE_S2, -+ ARM_64_LPAE_S1, -+ ARM_64_LPAE_S2, -+ IO_PGTABLE_NUM_FMTS, -+}; -+ -+/** -+ * struct iommu_gather_ops - IOMMU callbacks for TLB and page table management. -+ * -+ * @tlb_flush_all: Synchronously invalidate the entire TLB context. -+ * @tlb_add_flush: Queue up a TLB invalidation for a virtual address range. -+ * @tlb_sync: Ensure any queue TLB invalidation has taken effect. -+ * @flush_pgtable: Ensure page table updates are visible to the IOMMU. -+ * -+ * Note that these can all be called in atomic context and must therefore -+ * not block. -+ */ -+struct iommu_gather_ops { -+ void (*tlb_flush_all)(void *cookie); -+ void (*tlb_add_flush)(unsigned long iova, size_t size, bool leaf, -+ void *cookie); -+ void (*tlb_sync)(void *cookie); -+ void (*flush_pgtable)(void *ptr, size_t size, void *cookie); -+}; -+ -+/** -+ * struct io_pgtable_cfg - Configuration data for a set of page tables. -+ * -+ * @quirks: A bitmap of hardware quirks that require some special -+ * action by the low-level page table allocator. -+ * @pgsize_bitmap: A bitmap of page sizes supported by this set of page -+ * tables. -+ * @ias: Input address (iova) size, in bits. -+ * @oas: Output address (paddr) size, in bits. -+ * @tlb: TLB management callbacks for this set of tables. -+ */ -+struct io_pgtable_cfg { -+ #define IO_PGTABLE_QUIRK_ARM_NS (1 << 0) /* Set NS bit in PTEs */ -+ int quirks; -+ unsigned long pgsize_bitmap; -+ unsigned int ias; -+ unsigned int oas; -+ const struct iommu_gather_ops *tlb; -+ -+ /* Low-level data specific to the table format */ -+ union { -+ struct { -+ u64 ttbr[2]; -+ u64 tcr; -+ u64 mair[2]; -+ } arm_lpae_s1_cfg; -+ -+ struct { -+ u64 vttbr; -+ u64 vtcr; -+ } arm_lpae_s2_cfg; -+ }; -+}; -+ -+/** -+ * struct io_pgtable_ops - Page table manipulation API for IOMMU drivers. -+ * -+ * @map: Map a physically contiguous memory region. -+ * @unmap: Unmap a physically contiguous memory region. -+ * @iova_to_phys: Translate iova to physical address. -+ * -+ * These functions map directly onto the iommu_ops member functions with -+ * the same names. -+ */ -+struct io_pgtable_ops { -+ int (*map)(struct io_pgtable_ops *ops, unsigned long iova, -+ phys_addr_t paddr, size_t size, int prot); -+ int (*unmap)(struct io_pgtable_ops *ops, unsigned long iova, -+ size_t size); -+ phys_addr_t (*iova_to_phys)(struct io_pgtable_ops *ops, -+ unsigned long iova); -+}; -+ -+/** -+ * alloc_io_pgtable_ops() - Allocate a page table allocator for use by an IOMMU. -+ * -+ * @fmt: The page table format. -+ * @cfg: The page table configuration. This will be modified to represent -+ * the configuration actually provided by the allocator (e.g. the -+ * pgsize_bitmap may be restricted). -+ * @cookie: An opaque token provided by the IOMMU driver and passed back to -+ * the callback routines in cfg->tlb. -+ */ -+struct io_pgtable_ops *alloc_io_pgtable_ops(enum io_pgtable_fmt fmt, -+ struct io_pgtable_cfg *cfg, -+ void *cookie); -+ -+/** -+ * free_io_pgtable_ops() - Free an io_pgtable_ops structure. The caller -+ * *must* ensure that the page table is no longer -+ * live, but the TLB can be dirty. -+ * -+ * @ops: The ops returned from alloc_io_pgtable_ops. -+ */ -+void free_io_pgtable_ops(struct io_pgtable_ops *ops); -+ -+ -+/* -+ * Internal structures for page table allocator implementations. -+ */ -+ -+/** -+ * struct io_pgtable - Internal structure describing a set of page tables. -+ * -+ * @fmt: The page table format. -+ * @cookie: An opaque token provided by the IOMMU driver and passed back to -+ * any callback routines. -+ * @cfg: A copy of the page table configuration. -+ * @ops: The page table operations in use for this set of page tables. -+ */ -+struct io_pgtable { -+ enum io_pgtable_fmt fmt; -+ void *cookie; -+ struct io_pgtable_cfg cfg; -+ struct io_pgtable_ops ops; -+}; -+ -+/** -+ * struct io_pgtable_init_fns - Alloc/free a set of page tables for a -+ * particular format. -+ * -+ * @alloc: Allocate a set of page tables described by cfg. -+ * @free: Free the page tables associated with iop. -+ */ -+struct io_pgtable_init_fns { -+ struct io_pgtable *(*alloc)(struct io_pgtable_cfg *cfg, void *cookie); -+ void (*free)(struct io_pgtable *iop); -+}; -+ -+#endif /* __IO_PGTABLE_H */ -diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c -index ed8b048..8d8e5a7 100644 ---- a/drivers/iommu/iommu.c -+++ b/drivers/iommu/iommu.c -@@ -591,10 +591,10 @@ static struct iommu_group *get_pci_alias_group(struct pci_dev *pdev, - continue; - - /* We alias them or they alias us */ -- if (((pdev->dev_flags & PCI_DEV_FLAGS_DMA_ALIAS_DEVFN) && -- pdev->dma_alias_devfn == tmp->devfn) || -- ((tmp->dev_flags & PCI_DEV_FLAGS_DMA_ALIAS_DEVFN) && -- tmp->dma_alias_devfn == pdev->devfn)) { -+ if (((pdev->dev_flags & PCI_DEV_FLAGS_DMA_ALIAS_DEVID) && -+ (pdev->dma_alias_devid & 0xff) == tmp->devfn) || -+ ((tmp->dev_flags & PCI_DEV_FLAGS_DMA_ALIAS_DEVID) && -+ (tmp->dma_alias_devid & 0xff) == pdev->devfn)) { - - group = get_pci_alias_group(tmp, devfns); - if (group) { -@@ -737,7 +737,7 @@ static int add_iommu_group(struct device *dev, void *data) - const struct iommu_ops *ops = cb->ops; - - if (!ops->add_device) -- return -ENODEV; -+ return 0; - - WARN_ON(dev->iommu_group); - -@@ -818,7 +818,15 @@ static int iommu_bus_init(struct bus_type *bus, const struct iommu_ops *ops) - kfree(nb); - return err; - } -- return bus_for_each_dev(bus, NULL, &cb, add_iommu_group); -+ -+ err = bus_for_each_dev(bus, NULL, &cb, add_iommu_group); -+ if (err) { -+ bus_unregister_notifier(bus, nb); -+ kfree(nb); -+ return err; -+ } -+ -+ return 0; - } - - /** -@@ -836,13 +844,19 @@ static int iommu_bus_init(struct bus_type *bus, const struct iommu_ops *ops) - */ - int bus_set_iommu(struct bus_type *bus, const struct iommu_ops *ops) - { -+ int err; -+ - if (bus->iommu_ops != NULL) - return -EBUSY; - - bus->iommu_ops = ops; - - /* Do IOMMU specific setup for this bus-type */ -- return iommu_bus_init(bus, ops); -+ err = iommu_bus_init(bus, ops); -+ if (err) -+ bus->iommu_ops = NULL; -+ -+ return err; - } - EXPORT_SYMBOL_GPL(bus_set_iommu); - -@@ -887,36 +901,24 @@ EXPORT_SYMBOL_GPL(iommu_set_fault_handler); - struct iommu_domain *iommu_domain_alloc(struct bus_type *bus) - { - struct iommu_domain *domain; -- int ret; - - if (bus == NULL || bus->iommu_ops == NULL) - return NULL; - -- domain = kzalloc(sizeof(*domain), GFP_KERNEL); -+ domain = bus->iommu_ops->domain_alloc(IOMMU_DOMAIN_UNMANAGED); - if (!domain) - return NULL; - -- domain->ops = bus->iommu_ops; -- -- ret = domain->ops->domain_init(domain); -- if (ret) -- goto out_free; -+ domain->ops = bus->iommu_ops; -+ domain->type = IOMMU_DOMAIN_UNMANAGED; - - return domain; -- --out_free: -- kfree(domain); -- -- return NULL; - } - EXPORT_SYMBOL_GPL(iommu_domain_alloc); - - void iommu_domain_free(struct iommu_domain *domain) - { -- if (likely(domain->ops->domain_destroy != NULL)) -- domain->ops->domain_destroy(domain); -- -- kfree(domain); -+ domain->ops->domain_free(domain); - } - EXPORT_SYMBOL_GPL(iommu_domain_free); - -@@ -943,6 +945,16 @@ void iommu_detach_device(struct iommu_domain *domain, struct device *dev) - } - EXPORT_SYMBOL_GPL(iommu_detach_device); - -+struct iommu_domain *iommu_get_dev_domain(struct device *dev) -+{ -+ const struct iommu_ops *ops = dev->bus->iommu_ops; -+ -+ if (unlikely(ops == NULL || ops->get_dev_iommu_domain == NULL)) -+ return NULL; -+ -+ return ops->get_dev_iommu_domain(dev); -+} -+EXPORT_SYMBOL_GPL(iommu_get_dev_domain); - /* - * IOMMU groups are really the natrual working unit of the IOMMU, but - * the IOMMU API works on domains and devices. Bridge that gap by -@@ -1035,6 +1047,9 @@ int iommu_map(struct iommu_domain *domain, unsigned long iova, - domain->ops->pgsize_bitmap == 0UL)) - return -ENODEV; - -+ if (unlikely(!(domain->type & __IOMMU_DOMAIN_PAGING))) -+ return -EINVAL; -+ - /* find out the minimum page size supported */ - min_pagesz = 1 << __ffs(domain->ops->pgsize_bitmap); - -@@ -1070,7 +1085,7 @@ int iommu_map(struct iommu_domain *domain, unsigned long iova, - if (ret) - iommu_unmap(domain, orig_iova, orig_size - size); - else -- trace_map(iova, paddr, size); -+ trace_map(orig_iova, paddr, orig_size); - - return ret; - } -@@ -1080,11 +1095,15 @@ size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size) - { - size_t unmapped_page, unmapped = 0; - unsigned int min_pagesz; -+ unsigned long orig_iova = iova; - - if (unlikely(domain->ops->unmap == NULL || - domain->ops->pgsize_bitmap == 0UL)) - return -ENODEV; - -+ if (unlikely(!(domain->type & __IOMMU_DOMAIN_PAGING))) -+ return -EINVAL; -+ - /* find out the minimum page size supported */ - min_pagesz = 1 << __ffs(domain->ops->pgsize_bitmap); - -@@ -1119,11 +1138,53 @@ size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size) - unmapped += unmapped_page; - } - -- trace_unmap(iova, 0, size); -+ trace_unmap(orig_iova, size, unmapped); - return unmapped; - } - EXPORT_SYMBOL_GPL(iommu_unmap); - -+size_t default_iommu_map_sg(struct iommu_domain *domain, unsigned long iova, -+ struct scatterlist *sg, unsigned int nents, int prot) -+{ -+ struct scatterlist *s; -+ size_t mapped = 0; -+ unsigned int i, min_pagesz; -+ int ret; -+ -+ if (unlikely(domain->ops->pgsize_bitmap == 0UL)) -+ return 0; -+ -+ min_pagesz = 1 << __ffs(domain->ops->pgsize_bitmap); -+ -+ for_each_sg(sg, s, nents, i) { -+ phys_addr_t phys = page_to_phys(sg_page(s)) + s->offset; -+ -+ /* -+ * We are mapping on IOMMU page boundaries, so offset within -+ * the page must be 0. However, the IOMMU may support pages -+ * smaller than PAGE_SIZE, so s->offset may still represent -+ * an offset of that boundary within the CPU page. -+ */ -+ if (!IS_ALIGNED(s->offset, min_pagesz)) -+ goto out_err; -+ -+ ret = iommu_map(domain, iova + mapped, phys, s->length, prot); -+ if (ret) -+ goto out_err; -+ -+ mapped += s->length; -+ } -+ -+ return mapped; -+ -+out_err: -+ /* undo mappings already done */ -+ iommu_unmap(domain, iova, mapped); -+ -+ return 0; -+ -+} -+EXPORT_SYMBOL_GPL(default_iommu_map_sg); - - int iommu_domain_window_enable(struct iommu_domain *domain, u32 wnd_nr, - phys_addr_t paddr, u64 size, int prot) -diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c -index 7dab5cb..f3c5ab6 100644 ---- a/drivers/iommu/ipmmu-vmsa.c -+++ b/drivers/iommu/ipmmu-vmsa.c -@@ -1127,6 +1127,7 @@ static const struct iommu_ops ipmmu_ops = { - .detach_dev = ipmmu_detach_device, - .map = ipmmu_map, - .unmap = ipmmu_unmap, -+ .map_sg = default_iommu_map_sg, - .iova_to_phys = ipmmu_iova_to_phys, - .add_device = ipmmu_add_device, - .remove_device = ipmmu_remove_device, -@@ -1221,7 +1222,6 @@ static int ipmmu_remove(struct platform_device *pdev) - - static struct platform_driver ipmmu_driver = { - .driver = { -- .owner = THIS_MODULE, - .name = "ipmmu-vmsa", - }, - .probe = ipmmu_probe, -diff --git a/drivers/iommu/irq_remapping.c b/drivers/iommu/irq_remapping.c -index 74a1767..2c3f5ad 100644 ---- a/drivers/iommu/irq_remapping.c -+++ b/drivers/iommu/irq_remapping.c -@@ -56,19 +56,13 @@ static int do_setup_msi_irqs(struct pci_dev *dev, int nvec) - unsigned int irq; - struct msi_desc *msidesc; - -- WARN_ON(!list_is_singular(&dev->msi_list)); - msidesc = list_entry(dev->msi_list.next, struct msi_desc, list); -- WARN_ON(msidesc->irq); -- WARN_ON(msidesc->msi_attrib.multiple); -- WARN_ON(msidesc->nvec_used); - - irq = irq_alloc_hwirqs(nvec, dev_to_node(&dev->dev)); - if (irq == 0) - return -ENOSPC; - - nvec_pow2 = __roundup_pow_of_two(nvec); -- msidesc->nvec_used = nvec; -- msidesc->msi_attrib.multiple = ilog2(nvec_pow2); - for (sub_handle = 0; sub_handle < nvec; sub_handle++) { - if (!sub_handle) { - index = msi_alloc_remapped_irq(dev, irq, nvec_pow2); -@@ -96,8 +90,6 @@ error: - * IRQs from tearing down again in default_teardown_msi_irqs() - */ - msidesc->irq = 0; -- msidesc->nvec_used = 0; -- msidesc->msi_attrib.multiple = 0; - - return ret; - } -diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c -index 6e3dcc2..1c7b78e 100644 ---- a/drivers/iommu/msm_iommu.c -+++ b/drivers/iommu/msm_iommu.c -@@ -681,6 +681,7 @@ static const struct iommu_ops msm_iommu_ops = { - .detach_dev = msm_iommu_detach_dev, - .map = msm_iommu_map, - .unmap = msm_iommu_unmap, -+ .map_sg = default_iommu_map_sg, - .iova_to_phys = msm_iommu_iova_to_phys, - .pgsize_bitmap = MSM_IOMMU_PGSIZES, - }; -diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c -index e550ccb..43429ab 100644 ---- a/drivers/iommu/of_iommu.c -+++ b/drivers/iommu/of_iommu.c -@@ -18,9 +18,14 @@ - */ - - #include -+#include - #include - #include - #include -+#include -+ -+static const struct of_device_id __iommu_of_table_sentinel -+ __used __section(__iommu_of_table_end); - - /** - * of_get_dma_window - Parse *dma-window property and returns 0 if found. -@@ -89,3 +94,93 @@ int of_get_dma_window(struct device_node *dn, const char *prefix, int index, - return 0; - } - EXPORT_SYMBOL_GPL(of_get_dma_window); -+ -+struct of_iommu_node { -+ struct list_head list; -+ struct device_node *np; -+ struct iommu_ops *ops; -+}; -+static LIST_HEAD(of_iommu_list); -+static DEFINE_SPINLOCK(of_iommu_lock); -+ -+void of_iommu_set_ops(struct device_node *np, struct iommu_ops *ops) -+{ -+ struct of_iommu_node *iommu = kzalloc(sizeof(*iommu), GFP_KERNEL); -+ -+ if (WARN_ON(!iommu)) -+ return; -+ -+ INIT_LIST_HEAD(&iommu->list); -+ iommu->np = np; -+ iommu->ops = ops; -+ spin_lock(&of_iommu_lock); -+ list_add_tail(&iommu->list, &of_iommu_list); -+ spin_unlock(&of_iommu_lock); -+} -+ -+struct iommu_ops *of_iommu_get_ops(struct device_node *np) -+{ -+ struct of_iommu_node *node; -+ struct iommu_ops *ops = NULL; -+ -+ spin_lock(&of_iommu_lock); -+ list_for_each_entry(node, &of_iommu_list, list) -+ if (node->np == np) { -+ ops = node->ops; -+ break; -+ } -+ spin_unlock(&of_iommu_lock); -+ return ops; -+} -+ -+struct iommu_ops *of_iommu_configure(struct device *dev, -+ struct device_node *master_np) -+{ -+ struct of_phandle_args iommu_spec; -+ struct device_node *np; -+ struct iommu_ops *ops = NULL; -+ int idx = 0; -+ -+ if (dev_is_pci(dev)) { -+ dev_err(dev, "IOMMU is currently not supported for PCI\n"); -+ return NULL; -+ } -+ -+ /* -+ * We don't currently walk up the tree looking for a parent IOMMU. -+ * See the `Notes:' section of -+ * Documentation/devicetree/bindings/iommu/iommu.txt -+ */ -+ while (!of_parse_phandle_with_args(master_np, "iommus", -+ "#iommu-cells", idx, -+ &iommu_spec)) { -+ np = iommu_spec.np; -+ ops = of_iommu_get_ops(np); -+ -+ if (!ops || !ops->of_xlate || ops->of_xlate(dev, &iommu_spec)) -+ goto err_put_node; -+ -+ of_node_put(np); -+ idx++; -+ } -+ -+ return ops; -+ -+err_put_node: -+ of_node_put(np); -+ return NULL; -+} -+ -+void __init of_iommu_init(void) -+{ -+ struct device_node *np; -+ const struct of_device_id *match, *matches = &__iommu_of_table; -+ -+ for_each_matching_node_and_match(np, matches, &match) { -+ const of_iommu_init_fn init_fn = match->data; -+ -+ if (init_fn(np)) -+ pr_err("Failed to initialise IOMMU %s\n", -+ of_node_full_name(np)); -+ } -+} -diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c -index 3627887..18003c0 100644 ---- a/drivers/iommu/omap-iommu.c -+++ b/drivers/iommu/omap-iommu.c -@@ -1288,6 +1288,7 @@ static const struct iommu_ops omap_iommu_ops = { - .detach_dev = omap_iommu_detach_dev, - .map = omap_iommu_map, - .unmap = omap_iommu_unmap, -+ .map_sg = default_iommu_map_sg, - .iova_to_phys = omap_iommu_iova_to_phys, - .add_device = omap_iommu_add_device, - .remove_device = omap_iommu_remove_device, -diff --git a/drivers/iommu/shmobile-iommu.c b/drivers/iommu/shmobile-iommu.c -index 1333e6f..f1b0077 100644 ---- a/drivers/iommu/shmobile-iommu.c -+++ b/drivers/iommu/shmobile-iommu.c -@@ -361,6 +361,7 @@ static const struct iommu_ops shmobile_iommu_ops = { - .detach_dev = shmobile_iommu_detach_device, - .map = shmobile_iommu_map, - .unmap = shmobile_iommu_unmap, -+ .map_sg = default_iommu_map_sg, - .iova_to_phys = shmobile_iommu_iova_to_phys, - .add_device = shmobile_iommu_add_device, - .pgsize_bitmap = SZ_1M | SZ_64K | SZ_4K, -diff --git a/drivers/iommu/shmobile-ipmmu.c b/drivers/iommu/shmobile-ipmmu.c -index bd97ade..951651a 100644 ---- a/drivers/iommu/shmobile-ipmmu.c -+++ b/drivers/iommu/shmobile-ipmmu.c -@@ -118,7 +118,6 @@ static int ipmmu_probe(struct platform_device *pdev) - static struct platform_driver ipmmu_driver = { - .probe = ipmmu_probe, - .driver = { -- .owner = THIS_MODULE, - .name = "ipmmu", - }, - }; -diff --git a/drivers/iommu/tegra-gart.c b/drivers/iommu/tegra-gart.c -index a6d76ab..f722a0c 100644 ---- a/drivers/iommu/tegra-gart.c -+++ b/drivers/iommu/tegra-gart.c -@@ -425,7 +425,6 @@ static struct platform_driver tegra_gart_driver = { - .probe = tegra_gart_probe, - .remove = tegra_gart_remove, - .driver = { -- .owner = THIS_MODULE, - .name = "tegra-gart", - .pm = &tegra_gart_pm_ops, - .of_match_table = tegra_gart_of_match, -diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c -index 3afdf43..cb0c9bf 100644 ---- a/drivers/iommu/tegra-smmu.c -+++ b/drivers/iommu/tegra-smmu.c -@@ -955,6 +955,7 @@ static const struct iommu_ops smmu_iommu_ops = { - .detach_dev = smmu_iommu_detach_dev, - .map = smmu_iommu_map, - .unmap = smmu_iommu_unmap, -+ .map_sg = default_iommu_map_sg, - .iova_to_phys = smmu_iommu_iova_to_phys, - .pgsize_bitmap = SMMU_IOMMU_PGSIZES, - }; -@@ -1269,7 +1270,6 @@ static struct platform_driver tegra_smmu_driver = { - .probe = tegra_smmu_probe, - .remove = tegra_smmu_remove, - .driver = { -- .owner = THIS_MODULE, - .name = "tegra-smmu", - .pm = &tegra_smmu_pm_ops, - .of_match_table = tegra_smmu_of_match, -diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig -index b21f12f..caf590c 100644 ---- a/drivers/irqchip/Kconfig -+++ b/drivers/irqchip/Kconfig -@@ -15,6 +15,10 @@ config ARM_GIC_V3 - select IRQ_DOMAIN - select MULTI_IRQ_HANDLER - -+config ARM_GIC_V3_ITS -+ bool -+ select PCI_MSI_IRQ_DOMAIN -+ - config ARM_NVIC - bool - select IRQ_DOMAIN -diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile -index 173bb5f..ec3621d 100644 ---- a/drivers/irqchip/Makefile -+++ b/drivers/irqchip/Makefile -@@ -20,6 +20,7 @@ obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o - obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o - obj-$(CONFIG_ARM_GIC) += irq-gic.o irq-gic-common.o - obj-$(CONFIG_ARM_GIC_V3) += irq-gic-v3.o irq-gic-common.o -+obj-$(CONFIG_ARM_GIC_V3_ITS) += irq-gic-v3-its.o - obj-$(CONFIG_ARM_NVIC) += irq-nvic.o - obj-$(CONFIG_ARM_VIC) += irq-vic.o - obj-$(CONFIG_ATMEL_AIC_IRQ) += irq-atmel-aic-common.o irq-atmel-aic.o -diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c -index 41ac85a..615075d 100644 ---- a/drivers/irqchip/irq-armada-370-xp.c -+++ b/drivers/irqchip/irq-armada-370-xp.c -@@ -131,7 +131,7 @@ static void armada_370_xp_free_msi(int hwirq) - mutex_unlock(&msi_used_lock); - } - --static int armada_370_xp_setup_msi_irq(struct msi_chip *chip, -+static int armada_370_xp_setup_msi_irq(struct msi_controller *chip, - struct pci_dev *pdev, - struct msi_desc *desc) - { -@@ -158,11 +158,11 @@ static int armada_370_xp_setup_msi_irq(struct msi_chip *chip, - msg.address_hi = 0; - msg.data = 0xf00 | (hwirq + 16); - -- write_msi_msg(virq, &msg); -+ pci_write_msi_msg(virq, &msg); - return 0; - } - --static void armada_370_xp_teardown_msi_irq(struct msi_chip *chip, -+static void armada_370_xp_teardown_msi_irq(struct msi_controller *chip, - unsigned int irq) - { - struct irq_data *d = irq_get_irq_data(irq); -@@ -174,10 +174,10 @@ static void armada_370_xp_teardown_msi_irq(struct msi_chip *chip, - - static struct irq_chip armada_370_xp_msi_irq_chip = { - .name = "armada_370_xp_msi_irq", -- .irq_enable = unmask_msi_irq, -- .irq_disable = mask_msi_irq, -- .irq_mask = mask_msi_irq, -- .irq_unmask = unmask_msi_irq, -+ .irq_enable = pci_msi_unmask_irq, -+ .irq_disable = pci_msi_mask_irq, -+ .irq_mask = pci_msi_mask_irq, -+ .irq_unmask = pci_msi_unmask_irq, - }; - - static int armada_370_xp_msi_map(struct irq_domain *domain, unsigned int virq, -@@ -197,7 +197,7 @@ static const struct irq_domain_ops armada_370_xp_msi_irq_ops = { - static int armada_370_xp_msi_init(struct device_node *node, - phys_addr_t main_int_phys_base) - { -- struct msi_chip *msi_chip; -+ struct msi_controller *msi_chip; - u32 reg; - int ret; - -diff --git a/drivers/irqchip/irq-atmel-aic.c b/drivers/irqchip/irq-atmel-aic.c -index 9a2cf3c..27fdd8c 100644 ---- a/drivers/irqchip/irq-atmel-aic.c -+++ b/drivers/irqchip/irq-atmel-aic.c -@@ -65,11 +65,11 @@ aic_handle(struct pt_regs *regs) - u32 irqnr; - u32 irqstat; - -- irqnr = irq_reg_readl(gc->reg_base + AT91_AIC_IVR); -- irqstat = irq_reg_readl(gc->reg_base + AT91_AIC_ISR); -+ irqnr = irq_reg_readl(gc, AT91_AIC_IVR); -+ irqstat = irq_reg_readl(gc, AT91_AIC_ISR); - - if (!irqstat) -- irq_reg_writel(0, gc->reg_base + AT91_AIC_EOICR); -+ irq_reg_writel(gc, 0, AT91_AIC_EOICR); - else - handle_domain_irq(aic_domain, irqnr, regs); - } -@@ -80,7 +80,7 @@ static int aic_retrigger(struct irq_data *d) - - /* Enable interrupt on AIC5 */ - irq_gc_lock(gc); -- irq_reg_writel(d->mask, gc->reg_base + AT91_AIC_ISCR); -+ irq_reg_writel(gc, d->mask, AT91_AIC_ISCR); - irq_gc_unlock(gc); - - return 0; -@@ -92,12 +92,12 @@ static int aic_set_type(struct irq_data *d, unsigned type) - unsigned int smr; - int ret; - -- smr = irq_reg_readl(gc->reg_base + AT91_AIC_SMR(d->hwirq)); -+ smr = irq_reg_readl(gc, AT91_AIC_SMR(d->hwirq)); - ret = aic_common_set_type(d, type, &smr); - if (ret) - return ret; - -- irq_reg_writel(smr, gc->reg_base + AT91_AIC_SMR(d->hwirq)); -+ irq_reg_writel(gc, smr, AT91_AIC_SMR(d->hwirq)); - - return 0; - } -@@ -108,8 +108,8 @@ static void aic_suspend(struct irq_data *d) - struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); - - irq_gc_lock(gc); -- irq_reg_writel(gc->mask_cache, gc->reg_base + AT91_AIC_IDCR); -- irq_reg_writel(gc->wake_active, gc->reg_base + AT91_AIC_IECR); -+ irq_reg_writel(gc, gc->mask_cache, AT91_AIC_IDCR); -+ irq_reg_writel(gc, gc->wake_active, AT91_AIC_IECR); - irq_gc_unlock(gc); - } - -@@ -118,8 +118,8 @@ static void aic_resume(struct irq_data *d) - struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); - - irq_gc_lock(gc); -- irq_reg_writel(gc->wake_active, gc->reg_base + AT91_AIC_IDCR); -- irq_reg_writel(gc->mask_cache, gc->reg_base + AT91_AIC_IECR); -+ irq_reg_writel(gc, gc->wake_active, AT91_AIC_IDCR); -+ irq_reg_writel(gc, gc->mask_cache, AT91_AIC_IECR); - irq_gc_unlock(gc); - } - -@@ -128,8 +128,8 @@ static void aic_pm_shutdown(struct irq_data *d) - struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); - - irq_gc_lock(gc); -- irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_IDCR); -- irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_ICCR); -+ irq_reg_writel(gc, 0xffffffff, AT91_AIC_IDCR); -+ irq_reg_writel(gc, 0xffffffff, AT91_AIC_ICCR); - irq_gc_unlock(gc); - } - #else -@@ -148,24 +148,24 @@ static void __init aic_hw_init(struct irq_domain *domain) - * will not Lock out nIRQ - */ - for (i = 0; i < 8; i++) -- irq_reg_writel(0, gc->reg_base + AT91_AIC_EOICR); -+ irq_reg_writel(gc, 0, AT91_AIC_EOICR); - - /* - * Spurious Interrupt ID in Spurious Vector Register. - * When there is no current interrupt, the IRQ Vector Register - * reads the value stored in AIC_SPU - */ -- irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_SPU); -+ irq_reg_writel(gc, 0xffffffff, AT91_AIC_SPU); - - /* No debugging in AIC: Debug (Protect) Control Register */ -- irq_reg_writel(0, gc->reg_base + AT91_AIC_DCR); -+ irq_reg_writel(gc, 0, AT91_AIC_DCR); - - /* Disable and clear all interrupts initially */ -- irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_IDCR); -- irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_ICCR); -+ irq_reg_writel(gc, 0xffffffff, AT91_AIC_IDCR); -+ irq_reg_writel(gc, 0xffffffff, AT91_AIC_ICCR); - - for (i = 0; i < 32; i++) -- irq_reg_writel(i, gc->reg_base + AT91_AIC_SVR(i)); -+ irq_reg_writel(gc, i, AT91_AIC_SVR(i)); - } - - static int aic_irq_domain_xlate(struct irq_domain *d, -@@ -195,10 +195,10 @@ static int aic_irq_domain_xlate(struct irq_domain *d, - gc = dgc->gc[idx]; - - irq_gc_lock(gc); -- smr = irq_reg_readl(gc->reg_base + AT91_AIC_SMR(*out_hwirq)); -+ smr = irq_reg_readl(gc, AT91_AIC_SMR(*out_hwirq)); - ret = aic_common_set_priority(intspec[2], &smr); - if (!ret) -- irq_reg_writel(smr, gc->reg_base + AT91_AIC_SMR(*out_hwirq)); -+ irq_reg_writel(gc, smr, AT91_AIC_SMR(*out_hwirq)); - irq_gc_unlock(gc); - - return ret; -diff --git a/drivers/irqchip/irq-atmel-aic5.c b/drivers/irqchip/irq-atmel-aic5.c -index a11aae8..a2e8c3f 100644 ---- a/drivers/irqchip/irq-atmel-aic5.c -+++ b/drivers/irqchip/irq-atmel-aic5.c -@@ -75,11 +75,11 @@ aic5_handle(struct pt_regs *regs) - u32 irqnr; - u32 irqstat; - -- irqnr = irq_reg_readl(gc->reg_base + AT91_AIC5_IVR); -- irqstat = irq_reg_readl(gc->reg_base + AT91_AIC5_ISR); -+ irqnr = irq_reg_readl(gc, AT91_AIC5_IVR); -+ irqstat = irq_reg_readl(gc, AT91_AIC5_ISR); - - if (!irqstat) -- irq_reg_writel(0, gc->reg_base + AT91_AIC5_EOICR); -+ irq_reg_writel(gc, 0, AT91_AIC5_EOICR); - else - handle_domain_irq(aic5_domain, irqnr, regs); - } -@@ -92,8 +92,8 @@ static void aic5_mask(struct irq_data *d) - - /* Disable interrupt on AIC5 */ - irq_gc_lock(gc); -- irq_reg_writel(d->hwirq, gc->reg_base + AT91_AIC5_SSR); -- irq_reg_writel(1, gc->reg_base + AT91_AIC5_IDCR); -+ irq_reg_writel(gc, d->hwirq, AT91_AIC5_SSR); -+ irq_reg_writel(gc, 1, AT91_AIC5_IDCR); - gc->mask_cache &= ~d->mask; - irq_gc_unlock(gc); - } -@@ -106,8 +106,8 @@ static void aic5_unmask(struct irq_data *d) - - /* Enable interrupt on AIC5 */ - irq_gc_lock(gc); -- irq_reg_writel(d->hwirq, gc->reg_base + AT91_AIC5_SSR); -- irq_reg_writel(1, gc->reg_base + AT91_AIC5_IECR); -+ irq_reg_writel(gc, d->hwirq, AT91_AIC5_SSR); -+ irq_reg_writel(gc, 1, AT91_AIC5_IECR); - gc->mask_cache |= d->mask; - irq_gc_unlock(gc); - } -@@ -120,8 +120,8 @@ static int aic5_retrigger(struct irq_data *d) - - /* Enable interrupt on AIC5 */ - irq_gc_lock(gc); -- irq_reg_writel(d->hwirq, gc->reg_base + AT91_AIC5_SSR); -- irq_reg_writel(1, gc->reg_base + AT91_AIC5_ISCR); -+ irq_reg_writel(gc, d->hwirq, AT91_AIC5_SSR); -+ irq_reg_writel(gc, 1, AT91_AIC5_ISCR); - irq_gc_unlock(gc); - - return 0; -@@ -136,11 +136,11 @@ static int aic5_set_type(struct irq_data *d, unsigned type) - int ret; - - irq_gc_lock(gc); -- irq_reg_writel(d->hwirq, gc->reg_base + AT91_AIC5_SSR); -- smr = irq_reg_readl(gc->reg_base + AT91_AIC5_SMR); -+ irq_reg_writel(gc, d->hwirq, AT91_AIC5_SSR); -+ smr = irq_reg_readl(gc, AT91_AIC5_SMR); - ret = aic_common_set_type(d, type, &smr); - if (!ret) -- irq_reg_writel(smr, gc->reg_base + AT91_AIC5_SMR); -+ irq_reg_writel(gc, smr, AT91_AIC5_SMR); - irq_gc_unlock(gc); - - return ret; -@@ -162,12 +162,11 @@ static void aic5_suspend(struct irq_data *d) - if ((mask & gc->mask_cache) == (mask & gc->wake_active)) - continue; - -- irq_reg_writel(i + gc->irq_base, -- bgc->reg_base + AT91_AIC5_SSR); -+ irq_reg_writel(bgc, i + gc->irq_base, AT91_AIC5_SSR); - if (mask & gc->wake_active) -- irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IECR); -+ irq_reg_writel(bgc, 1, AT91_AIC5_IECR); - else -- irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IDCR); -+ irq_reg_writel(bgc, 1, AT91_AIC5_IDCR); - } - irq_gc_unlock(bgc); - } -@@ -187,12 +186,11 @@ static void aic5_resume(struct irq_data *d) - if ((mask & gc->mask_cache) == (mask & gc->wake_active)) - continue; - -- irq_reg_writel(i + gc->irq_base, -- bgc->reg_base + AT91_AIC5_SSR); -+ irq_reg_writel(bgc, i + gc->irq_base, AT91_AIC5_SSR); - if (mask & gc->mask_cache) -- irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IECR); -+ irq_reg_writel(bgc, 1, AT91_AIC5_IECR); - else -- irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IDCR); -+ irq_reg_writel(bgc, 1, AT91_AIC5_IDCR); - } - irq_gc_unlock(bgc); - } -@@ -207,10 +205,9 @@ static void aic5_pm_shutdown(struct irq_data *d) - - irq_gc_lock(bgc); - for (i = 0; i < dgc->irqs_per_chip; i++) { -- irq_reg_writel(i + gc->irq_base, -- bgc->reg_base + AT91_AIC5_SSR); -- irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IDCR); -- irq_reg_writel(1, bgc->reg_base + AT91_AIC5_ICCR); -+ irq_reg_writel(bgc, i + gc->irq_base, AT91_AIC5_SSR); -+ irq_reg_writel(bgc, 1, AT91_AIC5_IDCR); -+ irq_reg_writel(bgc, 1, AT91_AIC5_ICCR); - } - irq_gc_unlock(bgc); - } -@@ -230,24 +227,24 @@ static void __init aic5_hw_init(struct irq_domain *domain) - * will not Lock out nIRQ - */ - for (i = 0; i < 8; i++) -- irq_reg_writel(0, gc->reg_base + AT91_AIC5_EOICR); -+ irq_reg_writel(gc, 0, AT91_AIC5_EOICR); - - /* - * Spurious Interrupt ID in Spurious Vector Register. - * When there is no current interrupt, the IRQ Vector Register - * reads the value stored in AIC_SPU - */ -- irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC5_SPU); -+ irq_reg_writel(gc, 0xffffffff, AT91_AIC5_SPU); - - /* No debugging in AIC: Debug (Protect) Control Register */ -- irq_reg_writel(0, gc->reg_base + AT91_AIC5_DCR); -+ irq_reg_writel(gc, 0, AT91_AIC5_DCR); - - /* Disable and clear all interrupts initially */ - for (i = 0; i < domain->revmap_size; i++) { -- irq_reg_writel(i, gc->reg_base + AT91_AIC5_SSR); -- irq_reg_writel(i, gc->reg_base + AT91_AIC5_SVR); -- irq_reg_writel(1, gc->reg_base + AT91_AIC5_IDCR); -- irq_reg_writel(1, gc->reg_base + AT91_AIC5_ICCR); -+ irq_reg_writel(gc, i, AT91_AIC5_SSR); -+ irq_reg_writel(gc, i, AT91_AIC5_SVR); -+ irq_reg_writel(gc, 1, AT91_AIC5_IDCR); -+ irq_reg_writel(gc, 1, AT91_AIC5_ICCR); - } - } - -@@ -273,11 +270,11 @@ static int aic5_irq_domain_xlate(struct irq_domain *d, - gc = dgc->gc[0]; - - irq_gc_lock(gc); -- irq_reg_writel(*out_hwirq, gc->reg_base + AT91_AIC5_SSR); -- smr = irq_reg_readl(gc->reg_base + AT91_AIC5_SMR); -+ irq_reg_writel(gc, *out_hwirq, AT91_AIC5_SSR); -+ smr = irq_reg_readl(gc, AT91_AIC5_SMR); - ret = aic_common_set_priority(intspec[2], &smr); - if (!ret) -- irq_reg_writel(intspec[2] | smr, gc->reg_base + AT91_AIC5_SMR); -+ irq_reg_writel(gc, intspec[2] | smr, AT91_AIC5_SMR); - irq_gc_unlock(gc); - - return ret; -diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c -new file mode 100644 -index 0000000..43c50ed ---- /dev/null -+++ b/drivers/irqchip/irq-gic-v3-its.c -@@ -0,0 +1,1628 @@ -+/* -+ * Copyright (C) 2013, 2014 ARM Limited, All Rights Reserved. -+ * Author: Marc Zyngier -+ * -+ * 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. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#include -+#include -+#include -+ -+#include "irqchip.h" -+ -+#define ITS_FLAGS_CMDQ_NEEDS_FLUSHING (1 << 0) -+ -+#define RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING (1 << 0) -+ -+/* -+ * Collection structure - just an ID, and a redistributor address to -+ * ping. We use one per CPU as a bag of interrupts assigned to this -+ * CPU. -+ */ -+struct its_collection { -+ u64 target_address; -+ u16 col_id; -+}; -+ -+/* -+ * The ITS structure - contains most of the infrastructure, with the -+ * msi_controller, the command queue, the collections, and the list of -+ * devices writing to it. -+ */ -+struct its_node { -+ raw_spinlock_t lock; -+ struct list_head entry; -+ struct msi_controller msi_chip; -+ struct irq_domain *domain; -+ void __iomem *base; -+ unsigned long phys_base; -+ struct its_cmd_block *cmd_base; -+ struct its_cmd_block *cmd_write; -+ void *tables[GITS_BASER_NR_REGS]; -+ struct its_collection *collections; -+ struct list_head its_device_list; -+ u64 flags; -+ u32 ite_size; -+}; -+ -+#define ITS_ITT_ALIGN SZ_256 -+ -+struct event_lpi_map { -+ unsigned long *lpi_map; -+ u16 *col_map; -+ irq_hw_number_t lpi_base; -+ int nr_lpis; -+}; -+ -+/* -+ * The ITS view of a device - belongs to an ITS, a collection, owns an -+ * interrupt translation table, and a list of interrupts. -+ */ -+struct its_device { -+ struct list_head entry; -+ struct its_node *its; -+ struct event_lpi_map event_map; -+ void *itt; -+ u32 nr_ites; -+ u32 device_id; -+}; -+ -+static LIST_HEAD(its_nodes); -+static DEFINE_SPINLOCK(its_lock); -+static struct device_node *gic_root_node; -+static struct rdists *gic_rdists; -+ -+#define gic_data_rdist() (raw_cpu_ptr(gic_rdists->rdist)) -+#define gic_data_rdist_rd_base() (gic_data_rdist()->rd_base) -+ -+static struct its_collection *dev_event_to_col(struct its_device *its_dev, -+ u32 event) -+{ -+ struct its_node *its = its_dev->its; -+ -+ return its->collections + its_dev->event_map.col_map[event]; -+} -+ -+/* -+ * ITS command descriptors - parameters to be encoded in a command -+ * block. -+ */ -+struct its_cmd_desc { -+ union { -+ struct { -+ struct its_device *dev; -+ u32 event_id; -+ } its_inv_cmd; -+ -+ struct { -+ struct its_device *dev; -+ u32 event_id; -+ } its_int_cmd; -+ -+ struct { -+ struct its_device *dev; -+ int valid; -+ } its_mapd_cmd; -+ -+ struct { -+ struct its_collection *col; -+ int valid; -+ } its_mapc_cmd; -+ -+ struct { -+ struct its_device *dev; -+ u32 phys_id; -+ u32 event_id; -+ } its_mapvi_cmd; -+ -+ struct { -+ struct its_device *dev; -+ struct its_collection *col; -+ u32 event_id; -+ } its_movi_cmd; -+ -+ struct { -+ struct its_device *dev; -+ u32 event_id; -+ } its_discard_cmd; -+ -+ struct { -+ struct its_collection *col; -+ } its_invall_cmd; -+ }; -+}; -+ -+/* -+ * The ITS command block, which is what the ITS actually parses. -+ */ -+struct its_cmd_block { -+ u64 raw_cmd[4]; -+}; -+ -+#define ITS_CMD_QUEUE_SZ SZ_64K -+#define ITS_CMD_QUEUE_NR_ENTRIES (ITS_CMD_QUEUE_SZ / sizeof(struct its_cmd_block)) -+ -+typedef struct its_collection *(*its_cmd_builder_t)(struct its_cmd_block *, -+ struct its_cmd_desc *); -+ -+static void its_encode_cmd(struct its_cmd_block *cmd, u8 cmd_nr) -+{ -+ cmd->raw_cmd[0] &= ~0xffUL; -+ cmd->raw_cmd[0] |= cmd_nr; -+} -+ -+static void its_encode_devid(struct its_cmd_block *cmd, u32 devid) -+{ -+ cmd->raw_cmd[0] &= BIT_ULL(32) - 1; -+ cmd->raw_cmd[0] |= ((u64)devid) << 32; -+} -+ -+static void its_encode_event_id(struct its_cmd_block *cmd, u32 id) -+{ -+ cmd->raw_cmd[1] &= ~0xffffffffUL; -+ cmd->raw_cmd[1] |= id; -+} -+ -+static void its_encode_phys_id(struct its_cmd_block *cmd, u32 phys_id) -+{ -+ cmd->raw_cmd[1] &= 0xffffffffUL; -+ cmd->raw_cmd[1] |= ((u64)phys_id) << 32; -+} -+ -+static void its_encode_size(struct its_cmd_block *cmd, u8 size) -+{ -+ cmd->raw_cmd[1] &= ~0x1fUL; -+ cmd->raw_cmd[1] |= size & 0x1f; -+} -+ -+static void its_encode_itt(struct its_cmd_block *cmd, u64 itt_addr) -+{ -+ cmd->raw_cmd[2] &= ~0xffffffffffffUL; -+ cmd->raw_cmd[2] |= itt_addr & 0xffffffffff00UL; -+} -+ -+static void its_encode_valid(struct its_cmd_block *cmd, int valid) -+{ -+ cmd->raw_cmd[2] &= ~(1UL << 63); -+ cmd->raw_cmd[2] |= ((u64)!!valid) << 63; -+} -+ -+static void its_encode_target(struct its_cmd_block *cmd, u64 target_addr) -+{ -+ cmd->raw_cmd[2] &= ~(0xffffffffUL << 16); -+ cmd->raw_cmd[2] |= (target_addr & (0xffffffffUL << 16)); -+} -+ -+static void its_encode_collection(struct its_cmd_block *cmd, u16 col) -+{ -+ cmd->raw_cmd[2] &= ~0xffffUL; -+ cmd->raw_cmd[2] |= col; -+} -+ -+static inline void its_fixup_cmd(struct its_cmd_block *cmd) -+{ -+ /* Let's fixup BE commands */ -+ cmd->raw_cmd[0] = cpu_to_le64(cmd->raw_cmd[0]); -+ cmd->raw_cmd[1] = cpu_to_le64(cmd->raw_cmd[1]); -+ cmd->raw_cmd[2] = cpu_to_le64(cmd->raw_cmd[2]); -+ cmd->raw_cmd[3] = cpu_to_le64(cmd->raw_cmd[3]); -+} -+ -+static struct its_collection *its_build_mapd_cmd(struct its_cmd_block *cmd, -+ struct its_cmd_desc *desc) -+{ -+ unsigned long itt_addr; -+ u8 size = ilog2(desc->its_mapd_cmd.dev->nr_ites); -+ -+ itt_addr = virt_to_phys(desc->its_mapd_cmd.dev->itt); -+ itt_addr = ALIGN(itt_addr, ITS_ITT_ALIGN); -+ -+ its_encode_cmd(cmd, GITS_CMD_MAPD); -+ its_encode_devid(cmd, desc->its_mapd_cmd.dev->device_id); -+ its_encode_size(cmd, size - 1); -+ its_encode_itt(cmd, itt_addr); -+ its_encode_valid(cmd, desc->its_mapd_cmd.valid); -+ -+ its_fixup_cmd(cmd); -+ -+ return NULL; -+} -+ -+static struct its_collection *its_build_mapc_cmd(struct its_cmd_block *cmd, -+ struct its_cmd_desc *desc) -+{ -+ its_encode_cmd(cmd, GITS_CMD_MAPC); -+ its_encode_collection(cmd, desc->its_mapc_cmd.col->col_id); -+ its_encode_target(cmd, desc->its_mapc_cmd.col->target_address); -+ its_encode_valid(cmd, desc->its_mapc_cmd.valid); -+ -+ its_fixup_cmd(cmd); -+ -+ return desc->its_mapc_cmd.col; -+} -+ -+static struct its_collection *its_build_mapvi_cmd(struct its_cmd_block *cmd, -+ struct its_cmd_desc *desc) -+{ -+ struct its_collection *col; -+ -+ col = dev_event_to_col(desc->its_mapvi_cmd.dev, -+ desc->its_mapvi_cmd.event_id); -+ -+ its_encode_cmd(cmd, GITS_CMD_MAPVI); -+ its_encode_devid(cmd, desc->its_mapvi_cmd.dev->device_id); -+ its_encode_event_id(cmd, desc->its_mapvi_cmd.event_id); -+ its_encode_phys_id(cmd, desc->its_mapvi_cmd.phys_id); -+ its_encode_collection(cmd, col->col_id); -+ -+ its_fixup_cmd(cmd); -+ -+ return col; -+} -+ -+static struct its_collection *its_build_movi_cmd(struct its_cmd_block *cmd, -+ struct its_cmd_desc *desc) -+{ -+ struct its_collection *col; -+ -+ col = dev_event_to_col(desc->its_movi_cmd.dev, -+ desc->its_movi_cmd.event_id); -+ -+ its_encode_cmd(cmd, GITS_CMD_MOVI); -+ its_encode_devid(cmd, desc->its_movi_cmd.dev->device_id); -+ its_encode_event_id(cmd, desc->its_movi_cmd.event_id); -+ its_encode_collection(cmd, desc->its_movi_cmd.col->col_id); -+ -+ its_fixup_cmd(cmd); -+ -+ return col; -+} -+ -+static struct its_collection *its_build_discard_cmd(struct its_cmd_block *cmd, -+ struct its_cmd_desc *desc) -+{ -+ struct its_collection *col; -+ -+ col = dev_event_to_col(desc->its_discard_cmd.dev, -+ desc->its_discard_cmd.event_id); -+ -+ its_encode_cmd(cmd, GITS_CMD_DISCARD); -+ its_encode_devid(cmd, desc->its_discard_cmd.dev->device_id); -+ its_encode_event_id(cmd, desc->its_discard_cmd.event_id); -+ -+ its_fixup_cmd(cmd); -+ -+ return col; -+} -+ -+static struct its_collection *its_build_inv_cmd(struct its_cmd_block *cmd, -+ struct its_cmd_desc *desc) -+{ -+ struct its_collection *col; -+ -+ col = dev_event_to_col(desc->its_inv_cmd.dev, -+ desc->its_inv_cmd.event_id); -+ -+ its_encode_cmd(cmd, GITS_CMD_INV); -+ its_encode_devid(cmd, desc->its_inv_cmd.dev->device_id); -+ its_encode_event_id(cmd, desc->its_inv_cmd.event_id); -+ -+ its_fixup_cmd(cmd); -+ -+ return col; -+} -+ -+static struct its_collection *its_build_invall_cmd(struct its_cmd_block *cmd, -+ struct its_cmd_desc *desc) -+{ -+ its_encode_cmd(cmd, GITS_CMD_INVALL); -+ its_encode_collection(cmd, desc->its_mapc_cmd.col->col_id); -+ -+ its_fixup_cmd(cmd); -+ -+ return NULL; -+} -+ -+static u64 its_cmd_ptr_to_offset(struct its_node *its, -+ struct its_cmd_block *ptr) -+{ -+ return (ptr - its->cmd_base) * sizeof(*ptr); -+} -+ -+static int its_queue_full(struct its_node *its) -+{ -+ int widx; -+ int ridx; -+ -+ widx = its->cmd_write - its->cmd_base; -+ ridx = readl_relaxed(its->base + GITS_CREADR) / sizeof(struct its_cmd_block); -+ -+ /* This is incredibly unlikely to happen, unless the ITS locks up. */ -+ if (((widx + 1) % ITS_CMD_QUEUE_NR_ENTRIES) == ridx) -+ return 1; -+ -+ return 0; -+} -+ -+static struct its_cmd_block *its_allocate_entry(struct its_node *its) -+{ -+ struct its_cmd_block *cmd; -+ u32 count = 1000000; /* 1s! */ -+ -+ while (its_queue_full(its)) { -+ count--; -+ if (!count) { -+ pr_err_ratelimited("ITS queue not draining\n"); -+ return NULL; -+ } -+ cpu_relax(); -+ udelay(1); -+ } -+ -+ cmd = its->cmd_write++; -+ -+ /* Handle queue wrapping */ -+ if (its->cmd_write == (its->cmd_base + ITS_CMD_QUEUE_NR_ENTRIES)) -+ its->cmd_write = its->cmd_base; -+ -+ return cmd; -+} -+ -+static struct its_cmd_block *its_post_commands(struct its_node *its) -+{ -+ u64 wr = its_cmd_ptr_to_offset(its, its->cmd_write); -+ -+ writel_relaxed(wr, its->base + GITS_CWRITER); -+ -+ return its->cmd_write; -+} -+ -+static void its_flush_cmd(struct its_node *its, struct its_cmd_block *cmd) -+{ -+ /* -+ * Make sure the commands written to memory are observable by -+ * the ITS. -+ */ -+ if (its->flags & ITS_FLAGS_CMDQ_NEEDS_FLUSHING) -+ __flush_dcache_area(cmd, sizeof(*cmd)); -+ else -+ dsb(ishst); -+} -+ -+static void its_wait_for_range_completion(struct its_node *its, -+ struct its_cmd_block *from, -+ struct its_cmd_block *to) -+{ -+ u64 rd_idx, from_idx, to_idx; -+ u32 count = 1000000; /* 1s! */ -+ -+ from_idx = its_cmd_ptr_to_offset(its, from); -+ to_idx = its_cmd_ptr_to_offset(its, to); -+ -+ while (1) { -+ rd_idx = readl_relaxed(its->base + GITS_CREADR); -+ if (rd_idx >= to_idx || rd_idx < from_idx) -+ break; -+ -+ count--; -+ if (!count) { -+ pr_err_ratelimited("ITS queue timeout\n"); -+ return; -+ } -+ cpu_relax(); -+ udelay(1); -+ } -+} -+ -+static void its_send_single_command(struct its_node *its, -+ its_cmd_builder_t builder, -+ struct its_cmd_desc *desc) -+{ -+ struct its_cmd_block *cmd, *sync_cmd, *next_cmd; -+ struct its_collection *sync_col; -+ unsigned long flags; -+ -+ raw_spin_lock_irqsave(&its->lock, flags); -+ -+ cmd = its_allocate_entry(its); -+ if (!cmd) { /* We're soooooo screewed... */ -+ pr_err_ratelimited("ITS can't allocate, dropping command\n"); -+ raw_spin_unlock_irqrestore(&its->lock, flags); -+ return; -+ } -+ sync_col = builder(cmd, desc); -+ its_flush_cmd(its, cmd); -+ -+ if (sync_col) { -+ sync_cmd = its_allocate_entry(its); -+ if (!sync_cmd) { -+ pr_err_ratelimited("ITS can't SYNC, skipping\n"); -+ goto post; -+ } -+ its_encode_cmd(sync_cmd, GITS_CMD_SYNC); -+ its_encode_target(sync_cmd, sync_col->target_address); -+ its_fixup_cmd(sync_cmd); -+ its_flush_cmd(its, sync_cmd); -+ } -+ -+post: -+ next_cmd = its_post_commands(its); -+ raw_spin_unlock_irqrestore(&its->lock, flags); -+ -+ its_wait_for_range_completion(its, cmd, next_cmd); -+} -+ -+static void its_send_inv(struct its_device *dev, u32 event_id) -+{ -+ struct its_cmd_desc desc; -+ -+ desc.its_inv_cmd.dev = dev; -+ desc.its_inv_cmd.event_id = event_id; -+ -+ its_send_single_command(dev->its, its_build_inv_cmd, &desc); -+} -+ -+static void its_send_mapd(struct its_device *dev, int valid) -+{ -+ struct its_cmd_desc desc; -+ -+ desc.its_mapd_cmd.dev = dev; -+ desc.its_mapd_cmd.valid = !!valid; -+ -+ its_send_single_command(dev->its, its_build_mapd_cmd, &desc); -+} -+ -+static void its_send_mapc(struct its_node *its, struct its_collection *col, -+ int valid) -+{ -+ struct its_cmd_desc desc; -+ -+ desc.its_mapc_cmd.col = col; -+ desc.its_mapc_cmd.valid = !!valid; -+ -+ its_send_single_command(its, its_build_mapc_cmd, &desc); -+} -+ -+static void its_send_mapvi(struct its_device *dev, u32 irq_id, u32 id) -+{ -+ struct its_cmd_desc desc; -+ -+ desc.its_mapvi_cmd.dev = dev; -+ desc.its_mapvi_cmd.phys_id = irq_id; -+ desc.its_mapvi_cmd.event_id = id; -+ -+ its_send_single_command(dev->its, its_build_mapvi_cmd, &desc); -+} -+ -+static void its_send_movi(struct its_device *dev, -+ struct its_collection *col, u32 id) -+{ -+ struct its_cmd_desc desc; -+ -+ desc.its_movi_cmd.dev = dev; -+ desc.its_movi_cmd.col = col; -+ desc.its_movi_cmd.event_id = id; -+ -+ its_send_single_command(dev->its, its_build_movi_cmd, &desc); -+} -+ -+static void its_send_discard(struct its_device *dev, u32 id) -+{ -+ struct its_cmd_desc desc; -+ -+ desc.its_discard_cmd.dev = dev; -+ desc.its_discard_cmd.event_id = id; -+ -+ its_send_single_command(dev->its, its_build_discard_cmd, &desc); -+} -+ -+static void its_send_invall(struct its_node *its, struct its_collection *col) -+{ -+ struct its_cmd_desc desc; -+ -+ desc.its_invall_cmd.col = col; -+ -+ its_send_single_command(its, its_build_invall_cmd, &desc); -+} -+ -+/* -+ * irqchip functions - assumes MSI, mostly. -+ */ -+ -+static inline u32 its_get_event_id(struct irq_data *d) -+{ -+ struct its_device *its_dev = irq_data_get_irq_chip_data(d); -+ return d->hwirq - its_dev->event_map.lpi_base; -+} -+ -+static void lpi_set_config(struct irq_data *d, bool enable) -+{ -+ struct its_device *its_dev = irq_data_get_irq_chip_data(d); -+ irq_hw_number_t hwirq = d->hwirq; -+ u32 id = its_get_event_id(d); -+ u8 *cfg = page_address(gic_rdists->prop_page) + hwirq - 8192; -+ -+ if (enable) -+ *cfg |= LPI_PROP_ENABLED; -+ else -+ *cfg &= ~LPI_PROP_ENABLED; -+ -+ /* -+ * Make the above write visible to the redistributors. -+ * And yes, we're flushing exactly: One. Single. Byte. -+ * Humpf... -+ */ -+ if (gic_rdists->flags & RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING) -+ __flush_dcache_area(cfg, sizeof(*cfg)); -+ else -+ dsb(ishst); -+ its_send_inv(its_dev, id); -+} -+ -+static void its_mask_irq(struct irq_data *d) -+{ -+ lpi_set_config(d, false); -+} -+ -+static void its_unmask_irq(struct irq_data *d) -+{ -+ lpi_set_config(d, true); -+} -+ -+static void its_eoi_irq(struct irq_data *d) -+{ -+ gic_write_eoir(d->hwirq); -+} -+ -+static int its_set_affinity(struct irq_data *d, const struct cpumask *mask_val, -+ bool force) -+{ -+ unsigned int cpu = cpumask_any_and(mask_val, cpu_online_mask); -+ struct its_device *its_dev = irq_data_get_irq_chip_data(d); -+ struct its_collection *target_col; -+ u32 id = its_get_event_id(d); -+ -+ if (cpu >= nr_cpu_ids) -+ return -EINVAL; -+ -+ target_col = &its_dev->its->collections[cpu]; -+ its_send_movi(its_dev, target_col, id); -+ its_dev->event_map.col_map[id] = cpu; -+ -+ return IRQ_SET_MASK_OK_DONE; -+} -+ -+static void its_irq_compose_msi_msg(struct irq_data *d, struct msi_msg *msg) -+{ -+ struct its_device *its_dev = irq_data_get_irq_chip_data(d); -+ struct its_node *its; -+ u64 addr; -+ -+ its = its_dev->its; -+ addr = its->phys_base + GITS_TRANSLATER; -+ -+ msg->address_lo = addr & ((1UL << 32) - 1); -+ msg->address_hi = addr >> 32; -+ msg->data = its_get_event_id(d); -+} -+ -+static struct irq_chip its_irq_chip = { -+ .name = "ITS", -+ .irq_mask = its_mask_irq, -+ .irq_unmask = its_unmask_irq, -+ .irq_eoi = its_eoi_irq, -+ .irq_set_affinity = its_set_affinity, -+ .irq_compose_msi_msg = its_irq_compose_msi_msg, -+}; -+ -+static void its_mask_msi_irq(struct irq_data *d) -+{ -+ pci_msi_mask_irq(d); -+ irq_chip_mask_parent(d); -+} -+ -+static void its_unmask_msi_irq(struct irq_data *d) -+{ -+ pci_msi_unmask_irq(d); -+ irq_chip_unmask_parent(d); -+} -+ -+static struct irq_chip its_msi_irq_chip = { -+ .name = "ITS-MSI", -+ .irq_unmask = its_unmask_msi_irq, -+ .irq_mask = its_mask_msi_irq, -+ .irq_eoi = irq_chip_eoi_parent, -+ .irq_write_msi_msg = pci_msi_domain_write_msg, -+}; -+ -+/* -+ * How we allocate LPIs: -+ * -+ * The GIC has id_bits bits for interrupt identifiers. From there, we -+ * must subtract 8192 which are reserved for SGIs/PPIs/SPIs. Then, as -+ * we allocate LPIs by chunks of 32, we can shift the whole thing by 5 -+ * bits to the right. -+ * -+ * This gives us (((1UL << id_bits) - 8192) >> 5) possible allocations. -+ */ -+#define IRQS_PER_CHUNK_SHIFT 5 -+#define IRQS_PER_CHUNK (1 << IRQS_PER_CHUNK_SHIFT) -+ -+static unsigned long *lpi_bitmap; -+static u32 lpi_chunks; -+static DEFINE_SPINLOCK(lpi_lock); -+ -+static int its_lpi_to_chunk(int lpi) -+{ -+ return (lpi - 8192) >> IRQS_PER_CHUNK_SHIFT; -+} -+ -+static int its_chunk_to_lpi(int chunk) -+{ -+ return (chunk << IRQS_PER_CHUNK_SHIFT) + 8192; -+} -+ -+static int its_lpi_init(u32 id_bits) -+{ -+ lpi_chunks = its_lpi_to_chunk(1UL << id_bits); -+ -+ lpi_bitmap = kzalloc(BITS_TO_LONGS(lpi_chunks) * sizeof(long), -+ GFP_KERNEL); -+ if (!lpi_bitmap) { -+ lpi_chunks = 0; -+ return -ENOMEM; -+ } -+ -+ pr_info("ITS: Allocated %d chunks for LPIs\n", (int)lpi_chunks); -+ return 0; -+} -+ -+static unsigned long *its_lpi_alloc_chunks(int nr_irqs, int *base, int *nr_ids) -+{ -+ unsigned long *bitmap = NULL; -+ int chunk_id; -+ int nr_chunks; -+ int i; -+ -+ nr_chunks = DIV_ROUND_UP(nr_irqs, IRQS_PER_CHUNK); -+ -+ spin_lock(&lpi_lock); -+ -+ do { -+ chunk_id = bitmap_find_next_zero_area(lpi_bitmap, lpi_chunks, -+ 0, nr_chunks, 0); -+ if (chunk_id < lpi_chunks) -+ break; -+ -+ nr_chunks--; -+ } while (nr_chunks > 0); -+ -+ if (!nr_chunks) -+ goto out; -+ -+ bitmap = kzalloc(BITS_TO_LONGS(nr_chunks * IRQS_PER_CHUNK) * sizeof (long), -+ GFP_ATOMIC); -+ if (!bitmap) -+ goto out; -+ -+ for (i = 0; i < nr_chunks; i++) -+ set_bit(chunk_id + i, lpi_bitmap); -+ -+ *base = its_chunk_to_lpi(chunk_id); -+ *nr_ids = nr_chunks * IRQS_PER_CHUNK; -+ -+out: -+ spin_unlock(&lpi_lock); -+ -+ if (!bitmap) -+ *base = *nr_ids = 0; -+ -+ return bitmap; -+} -+ -+static void its_lpi_free(struct event_lpi_map *map) -+{ -+ int base = map->lpi_base; -+ int nr_ids = map->nr_lpis; -+ int lpi; -+ -+ spin_lock(&lpi_lock); -+ -+ for (lpi = base; lpi < (base + nr_ids); lpi += IRQS_PER_CHUNK) { -+ int chunk = its_lpi_to_chunk(lpi); -+ BUG_ON(chunk > lpi_chunks); -+ if (test_bit(chunk, lpi_bitmap)) { -+ clear_bit(chunk, lpi_bitmap); -+ } else { -+ pr_err("Bad LPI chunk %d\n", chunk); -+ } -+ } -+ -+ spin_unlock(&lpi_lock); -+ -+ kfree(map->lpi_map); -+ kfree(map->col_map); -+} -+ -+/* -+ * We allocate 64kB for PROPBASE. That gives us at most 64K LPIs to -+ * deal with (one configuration byte per interrupt). PENDBASE has to -+ * be 64kB aligned (one bit per LPI, plus 8192 bits for SPI/PPI/SGI). -+ */ -+#define LPI_PROPBASE_SZ SZ_64K -+#define LPI_PENDBASE_SZ (LPI_PROPBASE_SZ / 8 + SZ_1K) -+ -+/* -+ * This is how many bits of ID we need, including the useless ones. -+ */ -+#define LPI_NRBITS ilog2(LPI_PROPBASE_SZ + SZ_8K) -+ -+#define LPI_PROP_DEFAULT_PRIO 0xa0 -+ -+static int __init its_alloc_lpi_tables(void) -+{ -+ phys_addr_t paddr; -+ -+ gic_rdists->prop_page = alloc_pages(GFP_NOWAIT, -+ get_order(LPI_PROPBASE_SZ)); -+ if (!gic_rdists->prop_page) { -+ pr_err("Failed to allocate PROPBASE\n"); -+ return -ENOMEM; -+ } -+ -+ paddr = page_to_phys(gic_rdists->prop_page); -+ pr_info("GIC: using LPI property table @%pa\n", &paddr); -+ -+ /* Priority 0xa0, Group-1, disabled */ -+ memset(page_address(gic_rdists->prop_page), -+ LPI_PROP_DEFAULT_PRIO | LPI_PROP_GROUP1, -+ LPI_PROPBASE_SZ); -+ -+ /* Make sure the GIC will observe the written configuration */ -+ __flush_dcache_area(page_address(gic_rdists->prop_page), LPI_PROPBASE_SZ); -+ -+ return 0; -+} -+ -+static const char *its_base_type_string[] = { -+ [GITS_BASER_TYPE_DEVICE] = "Devices", -+ [GITS_BASER_TYPE_VCPU] = "Virtual CPUs", -+ [GITS_BASER_TYPE_CPU] = "Physical CPUs", -+ [GITS_BASER_TYPE_COLLECTION] = "Interrupt Collections", -+ [GITS_BASER_TYPE_RESERVED5] = "Reserved (5)", -+ [GITS_BASER_TYPE_RESERVED6] = "Reserved (6)", -+ [GITS_BASER_TYPE_RESERVED7] = "Reserved (7)", -+}; -+ -+static void its_free_tables(struct its_node *its) -+{ -+ int i; -+ -+ for (i = 0; i < GITS_BASER_NR_REGS; i++) { -+ if (its->tables[i]) { -+ free_page((unsigned long)its->tables[i]); -+ its->tables[i] = NULL; -+ } -+ } -+} -+ -+static int its_alloc_tables(struct its_node *its) -+{ -+ int err; -+ int i; -+ int psz = SZ_64K; -+ u64 shr = GITS_BASER_InnerShareable; -+ u64 cache = GITS_BASER_WaWb; -+ -+ for (i = 0; i < GITS_BASER_NR_REGS; i++) { -+ u64 val = readq_relaxed(its->base + GITS_BASER + i * 8); -+ u64 type = GITS_BASER_TYPE(val); -+ u64 entry_size = GITS_BASER_ENTRY_SIZE(val); -+ int order = get_order(psz); -+ int alloc_size; -+ u64 tmp; -+ void *base; -+ -+ if (type == GITS_BASER_TYPE_NONE) -+ continue; -+ -+ /* -+ * Allocate as many entries as required to fit the -+ * range of device IDs that the ITS can grok... The ID -+ * space being incredibly sparse, this results in a -+ * massive waste of memory. -+ * -+ * For other tables, only allocate a single page. -+ */ -+ if (type == GITS_BASER_TYPE_DEVICE) { -+ u64 typer = readq_relaxed(its->base + GITS_TYPER); -+ u32 ids = GITS_TYPER_DEVBITS(typer); -+ -+ /* -+ * 'order' was initialized earlier to the default page -+ * granule of the the ITS. We can't have an allocation -+ * smaller than that. If the requested allocation -+ * is smaller, round up to the default page granule. -+ */ -+ order = max(get_order((1UL << ids) * entry_size), -+ order); -+ if (order >= MAX_ORDER) { -+ order = MAX_ORDER - 1; -+ pr_warn("%s: Device Table too large, reduce its page order to %u\n", -+ its->msi_chip.of_node->full_name, order); -+ } -+ } -+ -+ alloc_size = (1 << order) * PAGE_SIZE; -+ base = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, order); -+ if (!base) { -+ err = -ENOMEM; -+ goto out_free; -+ } -+ -+ its->tables[i] = base; -+ -+retry_baser: -+ val = (virt_to_phys(base) | -+ (type << GITS_BASER_TYPE_SHIFT) | -+ ((entry_size - 1) << GITS_BASER_ENTRY_SIZE_SHIFT) | -+ cache | -+ shr | -+ GITS_BASER_VALID); -+ -+ switch (psz) { -+ case SZ_4K: -+ val |= GITS_BASER_PAGE_SIZE_4K; -+ break; -+ case SZ_16K: -+ val |= GITS_BASER_PAGE_SIZE_16K; -+ break; -+ case SZ_64K: -+ val |= GITS_BASER_PAGE_SIZE_64K; -+ break; -+ } -+ -+ val |= (alloc_size / psz) - 1; -+ -+ writeq_relaxed(val, its->base + GITS_BASER + i * 8); -+ tmp = readq_relaxed(its->base + GITS_BASER + i * 8); -+ -+ if ((val ^ tmp) & GITS_BASER_SHAREABILITY_MASK) { -+ /* -+ * Shareability didn't stick. Just use -+ * whatever the read reported, which is likely -+ * to be the only thing this redistributor -+ * supports. If that's zero, make it -+ * non-cacheable as well. -+ */ -+ shr = tmp & GITS_BASER_SHAREABILITY_MASK; -+ if (!shr) { -+ cache = GITS_BASER_nC; -+ __flush_dcache_area(base, alloc_size); -+ } -+ goto retry_baser; -+ } -+ -+ if ((val ^ tmp) & GITS_BASER_PAGE_SIZE_MASK) { -+ /* -+ * Page size didn't stick. Let's try a smaller -+ * size and retry. If we reach 4K, then -+ * something is horribly wrong... -+ */ -+ switch (psz) { -+ case SZ_16K: -+ psz = SZ_4K; -+ goto retry_baser; -+ case SZ_64K: -+ psz = SZ_16K; -+ goto retry_baser; -+ } -+ } -+ -+ if (val != tmp) { -+ pr_err("ITS: %s: GITS_BASER%d doesn't stick: %lx %lx\n", -+ its->msi_chip.of_node->full_name, i, -+ (unsigned long) val, (unsigned long) tmp); -+ err = -ENXIO; -+ goto out_free; -+ } -+ -+ pr_info("ITS: allocated %d %s @%lx (psz %dK, shr %d)\n", -+ (int)(alloc_size / entry_size), -+ its_base_type_string[type], -+ (unsigned long)virt_to_phys(base), -+ psz / SZ_1K, (int)shr >> GITS_BASER_SHAREABILITY_SHIFT); -+ } -+ -+ return 0; -+ -+out_free: -+ its_free_tables(its); -+ -+ return err; -+} -+ -+static int its_alloc_collections(struct its_node *its) -+{ -+ its->collections = kzalloc(nr_cpu_ids * sizeof(*its->collections), -+ GFP_KERNEL); -+ if (!its->collections) -+ return -ENOMEM; -+ -+ return 0; -+} -+ -+static void its_cpu_init_lpis(void) -+{ -+ void __iomem *rbase = gic_data_rdist_rd_base(); -+ struct page *pend_page; -+ u64 val, tmp; -+ -+ /* If we didn't allocate the pending table yet, do it now */ -+ pend_page = gic_data_rdist()->pend_page; -+ if (!pend_page) { -+ phys_addr_t paddr; -+ /* -+ * The pending pages have to be at least 64kB aligned, -+ * hence the 'max(LPI_PENDBASE_SZ, SZ_64K)' below. -+ */ -+ pend_page = alloc_pages(GFP_NOWAIT | __GFP_ZERO, -+ get_order(max(LPI_PENDBASE_SZ, SZ_64K))); -+ if (!pend_page) { -+ pr_err("Failed to allocate PENDBASE for CPU%d\n", -+ smp_processor_id()); -+ return; -+ } -+ -+ /* Make sure the GIC will observe the zero-ed page */ -+ __flush_dcache_area(page_address(pend_page), LPI_PENDBASE_SZ); -+ -+ paddr = page_to_phys(pend_page); -+ pr_info("CPU%d: using LPI pending table @%pa\n", -+ smp_processor_id(), &paddr); -+ gic_data_rdist()->pend_page = pend_page; -+ } -+ -+ /* Disable LPIs */ -+ val = readl_relaxed(rbase + GICR_CTLR); -+ val &= ~GICR_CTLR_ENABLE_LPIS; -+ writel_relaxed(val, rbase + GICR_CTLR); -+ -+ /* -+ * Make sure any change to the table is observable by the GIC. -+ */ -+ dsb(sy); -+ -+ /* set PROPBASE */ -+ val = (page_to_phys(gic_rdists->prop_page) | -+ GICR_PROPBASER_InnerShareable | -+ GICR_PROPBASER_WaWb | -+ ((LPI_NRBITS - 1) & GICR_PROPBASER_IDBITS_MASK)); -+ -+ writeq_relaxed(val, rbase + GICR_PROPBASER); -+ tmp = readq_relaxed(rbase + GICR_PROPBASER); -+ -+ if ((tmp ^ val) & GICR_PROPBASER_SHAREABILITY_MASK) { -+ if (!(tmp & GICR_PROPBASER_SHAREABILITY_MASK)) { -+ /* -+ * The HW reports non-shareable, we must -+ * remove the cacheability attributes as -+ * well. -+ */ -+ val &= ~(GICR_PROPBASER_SHAREABILITY_MASK | -+ GICR_PROPBASER_CACHEABILITY_MASK); -+ val |= GICR_PROPBASER_nC; -+ writeq_relaxed(val, rbase + GICR_PROPBASER); -+ } -+ pr_info_once("GIC: using cache flushing for LPI property table\n"); -+ gic_rdists->flags |= RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING; -+ } -+ -+ /* set PENDBASE */ -+ val = (page_to_phys(pend_page) | -+ GICR_PENDBASER_InnerShareable | -+ GICR_PENDBASER_WaWb); -+ -+ writeq_relaxed(val, rbase + GICR_PENDBASER); -+ tmp = readq_relaxed(rbase + GICR_PENDBASER); -+ -+ if (!(tmp & GICR_PENDBASER_SHAREABILITY_MASK)) { -+ /* -+ * The HW reports non-shareable, we must remove the -+ * cacheability attributes as well. -+ */ -+ val &= ~(GICR_PENDBASER_SHAREABILITY_MASK | -+ GICR_PENDBASER_CACHEABILITY_MASK); -+ val |= GICR_PENDBASER_nC; -+ writeq_relaxed(val, rbase + GICR_PENDBASER); -+ } -+ -+ /* Enable LPIs */ -+ val = readl_relaxed(rbase + GICR_CTLR); -+ val |= GICR_CTLR_ENABLE_LPIS; -+ writel_relaxed(val, rbase + GICR_CTLR); -+ -+ /* Make sure the GIC has seen the above */ -+ dsb(sy); -+} -+ -+static void its_cpu_init_collection(void) -+{ -+ struct its_node *its; -+ int cpu; -+ -+ spin_lock(&its_lock); -+ cpu = smp_processor_id(); -+ -+ list_for_each_entry(its, &its_nodes, entry) { -+ u64 target; -+ -+ /* -+ * We now have to bind each collection to its target -+ * redistributor. -+ */ -+ if (readq_relaxed(its->base + GITS_TYPER) & GITS_TYPER_PTA) { -+ /* -+ * This ITS wants the physical address of the -+ * redistributor. -+ */ -+ target = gic_data_rdist()->phys_base; -+ } else { -+ /* -+ * This ITS wants a linear CPU number. -+ */ -+ target = readq_relaxed(gic_data_rdist_rd_base() + GICR_TYPER); -+ target = GICR_TYPER_CPU_NUMBER(target) << 16; -+ } -+ -+ /* Perform collection mapping */ -+ its->collections[cpu].target_address = target; -+ its->collections[cpu].col_id = cpu; -+ -+ its_send_mapc(its, &its->collections[cpu], 1); -+ its_send_invall(its, &its->collections[cpu]); -+ } -+ -+ spin_unlock(&its_lock); -+} -+ -+static struct its_device *its_find_device(struct its_node *its, u32 dev_id) -+{ -+ struct its_device *its_dev = NULL, *tmp; -+ unsigned long flags; -+ -+ raw_spin_lock_irqsave(&its->lock, flags); -+ -+ list_for_each_entry(tmp, &its->its_device_list, entry) { -+ if (tmp->device_id == dev_id) { -+ its_dev = tmp; -+ break; -+ } -+ } -+ -+ raw_spin_unlock_irqrestore(&its->lock, flags); -+ -+ return its_dev; -+} -+ -+static struct its_device *its_create_device(struct its_node *its, u32 dev_id, -+ int nvecs) -+{ -+ struct its_device *dev; -+ unsigned long *lpi_map; -+ unsigned long flags; -+ u16 *col_map = NULL; -+ void *itt; -+ int lpi_base; -+ int nr_lpis; -+ int nr_ites; -+ int sz; -+ -+ dev = kzalloc(sizeof(*dev), GFP_KERNEL); -+ /* -+ * At least one bit of EventID is being used, hence a minimum -+ * of two entries. No, the architecture doesn't let you -+ * express an ITT with a single entry. -+ */ -+ nr_ites = max(2UL, roundup_pow_of_two(nvecs)); -+ sz = nr_ites * its->ite_size; -+ sz = max(sz, ITS_ITT_ALIGN) + ITS_ITT_ALIGN - 1; -+ itt = kzalloc(sz, GFP_KERNEL); -+ lpi_map = its_lpi_alloc_chunks(nvecs, &lpi_base, &nr_lpis); -+ if (lpi_map) -+ col_map = kzalloc(sizeof(*col_map) * nr_lpis, GFP_KERNEL); -+ -+ if (!dev || !itt || !lpi_map || !col_map) { -+ kfree(dev); -+ kfree(itt); -+ kfree(lpi_map); -+ kfree(col_map); -+ return NULL; -+ } -+ -+ __flush_dcache_area(itt, sz); -+ -+ dev->its = its; -+ dev->itt = itt; -+ dev->nr_ites = nr_ites; -+ dev->event_map.lpi_map = lpi_map; -+ dev->event_map.col_map = col_map; -+ dev->event_map.lpi_base = lpi_base; -+ dev->event_map.nr_lpis = nr_lpis; -+ dev->device_id = dev_id; -+ INIT_LIST_HEAD(&dev->entry); -+ -+ raw_spin_lock_irqsave(&its->lock, flags); -+ list_add(&dev->entry, &its->its_device_list); -+ raw_spin_unlock_irqrestore(&its->lock, flags); -+ -+ /* Map device to its ITT */ -+ its_send_mapd(dev, 1); -+ -+ return dev; -+} -+ -+static void its_free_device(struct its_device *its_dev) -+{ -+ unsigned long flags; -+ -+ raw_spin_lock_irqsave(&its_dev->its->lock, flags); -+ list_del(&its_dev->entry); -+ raw_spin_unlock_irqrestore(&its_dev->its->lock, flags); -+ kfree(its_dev->itt); -+ kfree(its_dev); -+} -+ -+static int its_alloc_device_irq(struct its_device *dev, irq_hw_number_t *hwirq) -+{ -+ int idx; -+ -+ idx = find_first_zero_bit(dev->event_map.lpi_map, -+ dev->event_map.nr_lpis); -+ if (idx == dev->event_map.nr_lpis) -+ return -ENOSPC; -+ -+ *hwirq = dev->event_map.lpi_base + idx; -+ set_bit(idx, dev->event_map.lpi_map); -+ -+ return 0; -+} -+ -+struct its_pci_alias { -+ struct pci_dev *pdev; -+ u32 dev_id; -+ u32 count; -+}; -+ -+static int its_pci_msi_vec_count(struct pci_dev *pdev) -+{ -+ int msi, msix; -+ -+ msi = max(pci_msi_vec_count(pdev), 0); -+ msix = max(pci_msix_vec_count(pdev), 0); -+ -+ return max(msi, msix); -+} -+ -+static int its_get_pci_alias(struct pci_dev *pdev, u16 alias, void *data) -+{ -+ struct its_pci_alias *dev_alias = data; -+ -+ dev_alias->dev_id = alias; -+ if (pdev != dev_alias->pdev) -+ dev_alias->count += its_pci_msi_vec_count(dev_alias->pdev); -+ -+ return 0; -+} -+ -+int __its_msi_prepare(struct irq_domain *domain, u32 dev_id, -+ struct device *dev, int nvec, msi_alloc_info_t *info) -+{ -+ struct its_node *its; -+ struct its_device *its_dev; -+ -+ its = domain->parent->host_data; -+ -+ its_dev = its_find_device(its, dev_id); -+ if (its_dev) { -+ /* -+ * We already have seen this ID, probably through -+ * another alias (PCI bridge of some sort). No need to -+ * create the device. -+ */ -+ dev_dbg(dev, "Reusing ITT for devID %x\n", dev_id); -+ goto out; -+ } -+ -+ its_dev = its_create_device(its, dev_id, nvec); -+ if (!its_dev) -+ return -ENOMEM; -+ -+ dev_dbg(dev, "ITT %d entries, %d bits\n", -+ nvec, ilog2(nvec)); -+out: -+ info->scratchpad[0].ptr = its_dev; -+ info->scratchpad[1].ptr = dev; -+ -+ return 0; -+} -+ -+static int its_msi_prepare(struct irq_domain *domain, struct device *dev, -+ int nvec, msi_alloc_info_t *info) -+{ -+ struct pci_dev *pdev; -+ struct its_pci_alias dev_alias; -+ u32 dev_id; -+ -+ if (!dev_is_pci(dev)) -+ return -EINVAL; -+ -+ pdev = to_pci_dev(dev); -+ dev_alias.pdev = pdev; -+ dev_alias.count = nvec; -+ -+ pci_for_each_dma_alias(pdev, its_get_pci_alias, &dev_alias); -+ -+ dev_dbg(dev, "ITT %d entries, %d bits\n", nvec, ilog2(nvec)); -+ dev_id = PCI_DEVID(pdev->bus->number, pdev->devfn); -+ return __its_msi_prepare(domain->parent, dev_alias.dev_id, dev, dev_alias.count, info); -+} -+ -+static struct msi_domain_ops its_pci_msi_ops = { -+ .msi_prepare = its_msi_prepare, -+}; -+ -+static struct msi_domain_info its_pci_msi_domain_info = { -+ .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | -+ MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX), -+ .ops = &its_pci_msi_ops, -+ .chip = &its_msi_irq_chip, -+}; -+ -+static int its_irq_gic_domain_alloc(struct irq_domain *domain, -+ unsigned int virq, -+ irq_hw_number_t hwirq) -+{ -+ struct of_phandle_args args; -+ -+ args.np = domain->parent->of_node; -+ args.args_count = 3; -+ args.args[0] = GIC_IRQ_TYPE_LPI; -+ args.args[1] = hwirq; -+ args.args[2] = IRQ_TYPE_EDGE_RISING; -+ -+ return irq_domain_alloc_irqs_parent(domain, virq, 1, &args); -+} -+ -+static int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, -+ unsigned int nr_irqs, void *args) -+{ -+ msi_alloc_info_t *info = args; -+ struct its_device *its_dev = info->scratchpad[0].ptr; -+ irq_hw_number_t hwirq; -+ int err; -+ int i; -+ -+ for (i = 0; i < nr_irqs; i++) { -+ err = its_alloc_device_irq(its_dev, &hwirq); -+ if (err) -+ return err; -+ -+ err = its_irq_gic_domain_alloc(domain, virq + i, hwirq); -+ if (err) -+ return err; -+ -+ irq_domain_set_hwirq_and_chip(domain, virq + i, -+ hwirq, &its_irq_chip, its_dev); -+ dev_dbg(info->scratchpad[1].ptr, "ID:%d pID:%d vID:%d\n", -+ (int)(hwirq - its_dev->event_map.lpi_base), -+ (int)hwirq, virq + i); -+ } -+ -+ return 0; -+} -+ -+static void its_irq_domain_activate(struct irq_domain *domain, -+ struct irq_data *d) -+{ -+ struct its_device *its_dev = irq_data_get_irq_chip_data(d); -+ u32 event = its_get_event_id(d); -+ -+ /* Bind the LPI to the first possible CPU */ -+ its_dev->event_map.col_map[event] = cpumask_first(cpu_online_mask); -+ -+ /* Map the GIC IRQ and event to the device */ -+ its_send_mapvi(its_dev, d->hwirq, event); -+} -+ -+static void its_irq_domain_deactivate(struct irq_domain *domain, -+ struct irq_data *d) -+{ -+ struct its_device *its_dev = irq_data_get_irq_chip_data(d); -+ u32 event = its_get_event_id(d); -+ -+ /* Stop the delivery of interrupts */ -+ its_send_discard(its_dev, event); -+} -+ -+static void its_irq_domain_free(struct irq_domain *domain, unsigned int virq, -+ unsigned int nr_irqs) -+{ -+ struct irq_data *d = irq_domain_get_irq_data(domain, virq); -+ struct its_device *its_dev = irq_data_get_irq_chip_data(d); -+ int i; -+ -+ for (i = 0; i < nr_irqs; i++) { -+ struct irq_data *data = irq_domain_get_irq_data(domain, -+ virq + i); -+ u32 event = its_get_event_id(data); -+ -+ /* Mark interrupt index as unused */ -+ clear_bit(event, its_dev->event_map.lpi_map); -+ -+ /* Nuke the entry in the domain */ -+ irq_domain_reset_irq_data(data); -+ } -+ -+ /* If all interrupts have been freed, start mopping the floor */ -+ if (bitmap_empty(its_dev->event_map.lpi_map, -+ its_dev->event_map.nr_lpis)) { -+ its_lpi_free(&its_dev->event_map); -+ -+ /* Unmap device/itt */ -+ its_send_mapd(its_dev, 0); -+ its_free_device(its_dev); -+ } -+ -+ irq_domain_free_irqs_parent(domain, virq, nr_irqs); -+} -+ -+static const struct irq_domain_ops its_domain_ops = { -+ .alloc = its_irq_domain_alloc, -+ .free = its_irq_domain_free, -+ .activate = its_irq_domain_activate, -+ .deactivate = its_irq_domain_deactivate, -+}; -+ -+static int its_force_quiescent(void __iomem *base) -+{ -+ u32 count = 1000000; /* 1s */ -+ u32 val; -+ -+ val = readl_relaxed(base + GITS_CTLR); -+ if (val & GITS_CTLR_QUIESCENT) -+ return 0; -+ -+ /* Disable the generation of all interrupts to this ITS */ -+ val &= ~GITS_CTLR_ENABLE; -+ writel_relaxed(val, base + GITS_CTLR); -+ -+ /* Poll GITS_CTLR and wait until ITS becomes quiescent */ -+ while (1) { -+ val = readl_relaxed(base + GITS_CTLR); -+ if (val & GITS_CTLR_QUIESCENT) -+ return 0; -+ -+ count--; -+ if (!count) -+ return -EBUSY; -+ -+ cpu_relax(); -+ udelay(1); -+ } -+} -+ -+static int its_probe(struct device_node *node, struct irq_domain *parent) -+{ -+ struct resource res; -+ struct its_node *its; -+ void __iomem *its_base; -+ u32 val; -+ u64 baser, tmp; -+ int err; -+ -+ err = of_address_to_resource(node, 0, &res); -+ if (err) { -+ pr_warn("%s: no regs?\n", node->full_name); -+ return -ENXIO; -+ } -+ -+ its_base = ioremap(res.start, resource_size(&res)); -+ if (!its_base) { -+ pr_warn("%s: unable to map registers\n", node->full_name); -+ return -ENOMEM; -+ } -+ -+ val = readl_relaxed(its_base + GITS_PIDR2) & GIC_PIDR2_ARCH_MASK; -+ if (val != 0x30 && val != 0x40) { -+ pr_warn("%s: no ITS detected, giving up\n", node->full_name); -+ err = -ENODEV; -+ goto out_unmap; -+ } -+ -+ err = its_force_quiescent(its_base); -+ if (err) { -+ pr_warn("%s: failed to quiesce, giving up\n", -+ node->full_name); -+ goto out_unmap; -+ } -+ -+ pr_info("ITS: %s\n", node->full_name); -+ -+ its = kzalloc(sizeof(*its), GFP_KERNEL); -+ if (!its) { -+ err = -ENOMEM; -+ goto out_unmap; -+ } -+ -+ raw_spin_lock_init(&its->lock); -+ INIT_LIST_HEAD(&its->entry); -+ INIT_LIST_HEAD(&its->its_device_list); -+ its->base = its_base; -+ its->phys_base = res.start; -+ its->msi_chip.of_node = node; -+ its->ite_size = ((readl_relaxed(its_base + GITS_TYPER) >> 4) & 0xf) + 1; -+ -+ its->cmd_base = kzalloc(ITS_CMD_QUEUE_SZ, GFP_KERNEL); -+ if (!its->cmd_base) { -+ err = -ENOMEM; -+ goto out_free_its; -+ } -+ its->cmd_write = its->cmd_base; -+ -+ err = its_alloc_tables(its); -+ if (err) -+ goto out_free_cmd; -+ -+ err = its_alloc_collections(its); -+ if (err) -+ goto out_free_tables; -+ -+ baser = (virt_to_phys(its->cmd_base) | -+ GITS_CBASER_WaWb | -+ GITS_CBASER_InnerShareable | -+ (ITS_CMD_QUEUE_SZ / SZ_4K - 1) | -+ GITS_CBASER_VALID); -+ -+ writeq_relaxed(baser, its->base + GITS_CBASER); -+ tmp = readq_relaxed(its->base + GITS_CBASER); -+ -+ if ((tmp ^ baser) & GITS_CBASER_SHAREABILITY_MASK) { -+ if (!(tmp & GITS_CBASER_SHAREABILITY_MASK)) { -+ /* -+ * The HW reports non-shareable, we must -+ * remove the cacheability attributes as -+ * well. -+ */ -+ baser &= ~(GITS_CBASER_SHAREABILITY_MASK | -+ GITS_CBASER_CACHEABILITY_MASK); -+ baser |= GITS_CBASER_nC; -+ writeq_relaxed(baser, its->base + GITS_CBASER); -+ } -+ pr_info("ITS: using cache flushing for cmd queue\n"); -+ its->flags |= ITS_FLAGS_CMDQ_NEEDS_FLUSHING; -+ } -+ -+ writeq_relaxed(0, its->base + GITS_CWRITER); -+ writel_relaxed(GITS_CTLR_ENABLE, its->base + GITS_CTLR); -+ -+ if (of_property_read_bool(its->msi_chip.of_node, "msi-controller")) { -+ its->domain = irq_domain_add_tree(NULL, &its_domain_ops, its); -+ if (!its->domain) { -+ err = -ENOMEM; -+ goto out_free_tables; -+ } -+ -+ its->domain->parent = parent; -+ -+ its->msi_chip.domain = pci_msi_create_irq_domain(node, -+ &its_pci_msi_domain_info, -+ its->domain); -+ if (!its->msi_chip.domain) { -+ err = -ENOMEM; -+ goto out_free_domains; -+ } -+ -+ err = of_pci_msi_chip_add(&its->msi_chip); -+ if (err) -+ goto out_free_domains; -+ } -+ -+ spin_lock(&its_lock); -+ list_add(&its->entry, &its_nodes); -+ spin_unlock(&its_lock); -+ -+ return 0; -+ -+out_free_domains: -+ if (its->msi_chip.domain) -+ irq_domain_remove(its->msi_chip.domain); -+ if (its->domain) -+ irq_domain_remove(its->domain); -+out_free_tables: -+ its_free_tables(its); -+out_free_cmd: -+ kfree(its->cmd_base); -+out_free_its: -+ kfree(its); -+out_unmap: -+ iounmap(its_base); -+ pr_err("ITS: failed probing %s (%d)\n", node->full_name, err); -+ return err; -+} -+ -+static bool gic_rdists_supports_plpis(void) -+{ -+ return !!(readl_relaxed(gic_data_rdist_rd_base() + GICR_TYPER) & GICR_TYPER_PLPIS); -+} -+ -+int its_cpu_init(void) -+{ -+ if (!list_empty(&its_nodes)) { -+ if (!gic_rdists_supports_plpis()) { -+ pr_info("CPU%d: LPIs not supported\n", smp_processor_id()); -+ return -ENXIO; -+ } -+ its_cpu_init_lpis(); -+ its_cpu_init_collection(); -+ } -+ -+ return 0; -+} -+ -+static struct of_device_id its_device_id[] = { -+ { .compatible = "arm,gic-v3-its", }, -+ {}, -+}; -+ -+int its_init(struct device_node *node, struct rdists *rdists, -+ struct irq_domain *parent_domain) -+{ -+ struct device_node *np; -+ -+ for (np = of_find_matching_node(node, its_device_id); np; -+ np = of_find_matching_node(np, its_device_id)) { -+ its_probe(np, parent_domain); -+ } -+ -+ if (list_empty(&its_nodes)) { -+ pr_warn("ITS: No ITS available, not enabling LPIs\n"); -+ return -ENXIO; -+ } -+ -+ gic_rdists = rdists; -+ gic_root_node = node; -+ -+ its_alloc_lpi_tables(); -+ its_lpi_init(rdists->id_bits); -+ -+ return 0; -+} -diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c -index aa17ae8..34feda3 100644 ---- a/drivers/irqchip/irq-gic-v3.c -+++ b/drivers/irqchip/irq-gic-v3.c -@@ -34,20 +34,25 @@ - #include "irq-gic-common.h" - #include "irqchip.h" - -+struct redist_region { -+ void __iomem *redist_base; -+ phys_addr_t phys_base; -+}; -+ - struct gic_chip_data { - void __iomem *dist_base; -- void __iomem **redist_base; -- void __iomem * __percpu *rdist; -+ struct redist_region *redist_regions; -+ struct rdists rdists; - struct irq_domain *domain; - u64 redist_stride; -- u32 redist_regions; -+ u32 nr_redist_regions; - unsigned int irq_nr; - }; - - static struct gic_chip_data gic_data __read_mostly; - --#define gic_data_rdist() (this_cpu_ptr(gic_data.rdist)) --#define gic_data_rdist_rd_base() (*gic_data_rdist()) -+#define gic_data_rdist() (this_cpu_ptr(gic_data.rdists.rdist)) -+#define gic_data_rdist_rd_base() (gic_data_rdist()->rd_base) - #define gic_data_rdist_sgi_base() (gic_data_rdist_rd_base() + SZ_64K) - - /* Our default, arbitrary priority value. Linux only uses one anyway. */ -@@ -71,9 +76,6 @@ static inline void __iomem *gic_dist_base(struct irq_data *d) - if (d->hwirq <= 1023) /* SPI -> dist_base */ - return gic_data.dist_base; - -- if (d->hwirq >= 8192) -- BUG(); /* LPI Detected!!! */ -- - return NULL; - } - -@@ -271,11 +273,11 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs - do { - irqnr = gic_read_iar(); - -- if (likely(irqnr > 15 && irqnr < 1020)) { -+ if (likely(irqnr > 15 && irqnr < 1020) || irqnr >= 8192) { - int err; - err = handle_domain_irq(gic_data.domain, irqnr, regs); - if (err) { -- WARN_ONCE(true, "Unexpected SPI received!\n"); -+ WARN_ONCE(true, "Unexpected interrupt received!\n"); - gic_write_eoir(irqnr); - } - continue; -@@ -333,8 +335,8 @@ static int gic_populate_rdist(void) - MPIDR_AFFINITY_LEVEL(mpidr, 1) << 8 | - MPIDR_AFFINITY_LEVEL(mpidr, 0)); - -- for (i = 0; i < gic_data.redist_regions; i++) { -- void __iomem *ptr = gic_data.redist_base[i]; -+ for (i = 0; i < gic_data.nr_redist_regions; i++) { -+ void __iomem *ptr = gic_data.redist_regions[i].redist_base; - u32 reg; - - reg = readl_relaxed(ptr + GICR_PIDR2) & GIC_PIDR2_ARCH_MASK; -@@ -347,10 +349,13 @@ static int gic_populate_rdist(void) - do { - typer = readq_relaxed(ptr + GICR_TYPER); - if ((typer >> 32) == aff) { -+ u64 offset = ptr - gic_data.redist_regions[i].redist_base; - gic_data_rdist_rd_base() = ptr; -- pr_info("CPU%d: found redistributor %llx @%p\n", -+ gic_data_rdist()->phys_base = gic_data.redist_regions[i].phys_base + offset; -+ pr_info("CPU%d: found redistributor %llx region %d:%pa\n", - smp_processor_id(), -- (unsigned long long)mpidr, ptr); -+ (unsigned long long)mpidr, -+ i, &gic_data_rdist()->phys_base); - return 0; - } - -@@ -385,6 +390,11 @@ static void gic_cpu_sys_reg_init(void) - gic_write_grpen1(1); - } - -+static int gic_dist_supports_lpis(void) -+{ -+ return !!(readl_relaxed(gic_data.dist_base + GICD_TYPER) & GICD_TYPER_LPIS); -+} -+ - static void gic_cpu_init(void) - { - void __iomem *rbase; -@@ -399,6 +409,10 @@ static void gic_cpu_init(void) - - gic_cpu_config(rbase, gic_redist_wait_for_rwp); - -+ /* Give LPIs a spin */ -+ if (IS_ENABLED(CONFIG_ARM_GIC_V3_ITS) && gic_dist_supports_lpis()) -+ its_cpu_init(); -+ - /* initialise system registers */ - gic_cpu_sys_reg_init(); - } -@@ -585,12 +599,21 @@ static struct irq_chip gic_chip = { - .irq_set_affinity = gic_set_affinity, - }; - -+#define GIC_ID_NR (1U << gic_data.rdists.id_bits) -+ - static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, - irq_hw_number_t hw) - { - /* SGIs are private to the core kernel */ - if (hw < 16) - return -EPERM; -+ /* Nothing here */ -+ if (hw >= gic_data.irq_nr && hw < 8192) -+ return -EPERM; -+ /* Off limits */ -+ if (hw >= GIC_ID_NR) -+ return -EPERM; -+ - /* PPIs */ - if (hw < 32) { - irq_set_percpu_devid(irq); -@@ -604,7 +627,15 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, - handle_fasteoi_irq); - set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); - } -- irq_set_chip_data(irq, d->host_data); -+ /* LPIs */ -+ if (hw >= 8192 && hw < GIC_ID_NR) { -+ if (!gic_dist_supports_lpis()) -+ return -EPERM; -+ irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data, -+ handle_fasteoi_irq, NULL, NULL); -+ set_irq_flags(irq, IRQF_VALID); -+ } -+ - return 0; - } - -@@ -625,6 +656,9 @@ static int gic_irq_domain_xlate(struct irq_domain *d, - case 1: /* PPI */ - *out_hwirq = intspec[1] + 16; - break; -+ case GIC_IRQ_TYPE_LPI: /* LPI */ -+ *out_hwirq = intspec[1]; -+ break; - default: - return -EINVAL; - } -@@ -641,9 +675,10 @@ static const struct irq_domain_ops gic_irq_domain_ops = { - static int __init gic_of_init(struct device_node *node, struct device_node *parent) - { - void __iomem *dist_base; -- void __iomem **redist_base; -+ struct redist_region *rdist_regs; - u64 redist_stride; -- u32 redist_regions; -+ u32 nr_redist_regions; -+ u32 typer; - u32 reg; - int gic_irqs; - int err; -@@ -664,54 +699,63 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare - goto out_unmap_dist; - } - -- if (of_property_read_u32(node, "#redistributor-regions", &redist_regions)) -- redist_regions = 1; -+ if (of_property_read_u32(node, "#redistributor-regions", &nr_redist_regions)) -+ nr_redist_regions = 1; - -- redist_base = kzalloc(sizeof(*redist_base) * redist_regions, GFP_KERNEL); -- if (!redist_base) { -+ rdist_regs = kzalloc(sizeof(*rdist_regs) * nr_redist_regions, GFP_KERNEL); -+ if (!rdist_regs) { - err = -ENOMEM; - goto out_unmap_dist; - } - -- for (i = 0; i < redist_regions; i++) { -- redist_base[i] = of_iomap(node, 1 + i); -- if (!redist_base[i]) { -+ for (i = 0; i < nr_redist_regions; i++) { -+ struct resource res; -+ int ret; -+ -+ ret = of_address_to_resource(node, 1 + i, &res); -+ rdist_regs[i].redist_base = of_iomap(node, 1 + i); -+ if (ret || !rdist_regs[i].redist_base) { - pr_err("%s: couldn't map region %d\n", - node->full_name, i); - err = -ENODEV; - goto out_unmap_rdist; - } -+ rdist_regs[i].phys_base = res.start; - } - - if (of_property_read_u64(node, "redistributor-stride", &redist_stride)) - redist_stride = 0; - - gic_data.dist_base = dist_base; -- gic_data.redist_base = redist_base; -- gic_data.redist_regions = redist_regions; -+ gic_data.redist_regions = rdist_regs; -+ gic_data.nr_redist_regions = nr_redist_regions; - gic_data.redist_stride = redist_stride; - - /* - * Find out how many interrupts are supported. - * The GIC only supports up to 1020 interrupt sources (SGI+PPI+SPI) - */ -- gic_irqs = readl_relaxed(gic_data.dist_base + GICD_TYPER) & 0x1f; -- gic_irqs = (gic_irqs + 1) * 32; -+ typer = readl_relaxed(gic_data.dist_base + GICD_TYPER); -+ gic_data.rdists.id_bits = GICD_TYPER_ID_BITS(typer); -+ gic_irqs = GICD_TYPER_IRQS(typer); - if (gic_irqs > 1020) - gic_irqs = 1020; - gic_data.irq_nr = gic_irqs; - - gic_data.domain = irq_domain_add_tree(node, &gic_irq_domain_ops, - &gic_data); -- gic_data.rdist = alloc_percpu(typeof(*gic_data.rdist)); -+ gic_data.rdists.rdist = alloc_percpu(typeof(*gic_data.rdists.rdist)); - -- if (WARN_ON(!gic_data.domain) || WARN_ON(!gic_data.rdist)) { -+ if (WARN_ON(!gic_data.domain) || WARN_ON(!gic_data.rdists.rdist)) { - err = -ENOMEM; - goto out_free; - } - - set_handle_irq(gic_handle_irq); - -+ if (IS_ENABLED(CONFIG_ARM_GIC_V3_ITS) && gic_dist_supports_lpis()) -+ its_init(node, &gic_data.rdists, gic_data.domain); -+ - gic_smp_init(); - gic_dist_init(); - gic_cpu_init(); -@@ -722,12 +766,12 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare - out_free: - if (gic_data.domain) - irq_domain_remove(gic_data.domain); -- free_percpu(gic_data.rdist); -+ free_percpu(gic_data.rdists.rdist); - out_unmap_rdist: -- for (i = 0; i < redist_regions; i++) -- if (redist_base[i]) -- iounmap(redist_base[i]); -- kfree(redist_base); -+ for (i = 0; i < nr_redist_regions; i++) -+ if (rdist_regs[i].redist_base) -+ iounmap(rdist_regs[i].redist_base); -+ kfree(rdist_regs); - out_unmap_dist: - iounmap(dist_base); - return err; -diff --git a/drivers/irqchip/irq-sunxi-nmi.c b/drivers/irqchip/irq-sunxi-nmi.c -index eb9b59e..6b2b582 100644 ---- a/drivers/irqchip/irq-sunxi-nmi.c -+++ b/drivers/irqchip/irq-sunxi-nmi.c -@@ -50,12 +50,12 @@ static struct sunxi_sc_nmi_reg_offs sun6i_reg_offs = { - static inline void sunxi_sc_nmi_write(struct irq_chip_generic *gc, u32 off, - u32 val) - { -- irq_reg_writel(val, gc->reg_base + off); -+ irq_reg_writel(gc, val, off); - } - - static inline u32 sunxi_sc_nmi_read(struct irq_chip_generic *gc, u32 off) - { -- return irq_reg_readl(gc->reg_base + off); -+ return irq_reg_readl(gc, off); - } - - static void sunxi_sc_nmi_handle_irq(unsigned int irq, struct irq_desc *desc) -diff --git a/drivers/irqchip/irq-tb10x.c b/drivers/irqchip/irq-tb10x.c -index 7c44c99..accc200 100644 ---- a/drivers/irqchip/irq-tb10x.c -+++ b/drivers/irqchip/irq-tb10x.c -@@ -43,12 +43,12 @@ - static inline void ab_irqctl_writereg(struct irq_chip_generic *gc, u32 reg, - u32 val) - { -- irq_reg_writel(val, gc->reg_base + reg); -+ irq_reg_writel(gc, val, reg); - } - - static inline u32 ab_irqctl_readreg(struct irq_chip_generic *gc, u32 reg) - { -- return irq_reg_readl(gc->reg_base + reg); -+ return irq_reg_readl(gc, reg); - } - - static int tb10x_irq_set_type(struct irq_data *data, unsigned int flow_type) -diff --git a/drivers/of/device.c b/drivers/of/device.c -index 46d6c75..20c1332 100644 ---- a/drivers/of/device.c -+++ b/drivers/of/device.c -@@ -2,6 +2,9 @@ - #include - #include - #include -+#include -+#include -+#include - #include - #include - #include -@@ -66,6 +69,87 @@ int of_device_add(struct platform_device *ofdev) - return device_add(&ofdev->dev); - } - -+/** -+ * of_dma_configure - Setup DMA configuration -+ * @dev: Device to apply DMA configuration -+ * @np: Pointer to OF node having DMA configuration -+ * -+ * Try to get devices's DMA configuration from DT and update it -+ * accordingly. -+ * -+ * If platform code needs to use its own special DMA configuration, it -+ * can use a platform bus notifier and handle BUS_NOTIFY_ADD_DEVICE events -+ * to fix up DMA configuration. -+ */ -+void of_dma_configure(struct device *dev, struct device_node *np) -+{ -+ u64 dma_addr, paddr, size; -+ int ret; -+ bool coherent; -+ unsigned long offset; -+ struct iommu_ops *iommu; -+ -+ /* -+ * Set default coherent_dma_mask to 32 bit. Drivers are expected to -+ * setup the correct supported mask. -+ */ -+ if (!dev->coherent_dma_mask) -+ dev->coherent_dma_mask = DMA_BIT_MASK(32); -+ -+ /* -+ * Set it to coherent_dma_mask by default if the architecture -+ * code has not set it. -+ */ -+ if (!dev->dma_mask) -+ dev->dma_mask = &dev->coherent_dma_mask; -+ -+ ret = of_dma_get_range(np, &dma_addr, &paddr, &size); -+ if (ret < 0) { -+ dma_addr = offset = 0; -+ size = dev->coherent_dma_mask + 1; -+ } else { -+ offset = PFN_DOWN(paddr - dma_addr); -+ -+ /* -+ * Add a work around to treat the size as mask + 1 in case -+ * it is defined in DT as a mask. -+ */ -+ if (size & 1) { -+ dev_warn(dev, "Invalid size 0x%llx for dma-range\n", -+ size); -+ size = size + 1; -+ } -+ -+ if (!size) { -+ dev_err(dev, "Adjusted size 0x%llx invalid\n", size); -+ return; -+ } -+ dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", offset); -+ } -+ -+ dev->dma_pfn_offset = offset; -+ -+ /* -+ * Limit coherent and dma mask based on size and default mask -+ * set by the driver. -+ */ -+ dev->coherent_dma_mask = min(dev->coherent_dma_mask, -+ DMA_BIT_MASK(ilog2(dma_addr + size))); -+ *dev->dma_mask = min((*dev->dma_mask), -+ DMA_BIT_MASK(ilog2(dma_addr + size))); -+ -+ coherent = of_dma_is_coherent(np); -+ dev_dbg(dev, "device is%sdma coherent\n", -+ coherent ? " " : " not "); -+ -+ iommu = of_iommu_configure(dev, np); -+ dev_dbg(dev, "device is%sbehind an iommu\n", -+ iommu ? " " : " not "); -+ -+ arch_setup_dma_ops(dev, dma_addr, size, iommu, coherent); -+} -+EXPORT_SYMBOL_GPL(of_dma_configure); -+ - int of_device_register(struct platform_device *pdev) - { - device_initialize(&pdev->dev); -diff --git a/drivers/of/irq.c b/drivers/of/irq.c -index b97363a..4419e62 100644 ---- a/drivers/of/irq.c -+++ b/drivers/of/irq.c -@@ -18,6 +18,7 @@ - * driver. - */ - -+#include - #include - #include - #include -@@ -576,3 +577,23 @@ err: - kfree(desc); - } - } -+ -+/** -+ * of_msi_configure - Set the msi_domain field of a device -+ * @dev: device structure to associate with an MSI irq domain -+ * @np: device node for that device -+ */ -+void of_msi_configure(struct device *dev, struct device_node *np) -+{ -+ struct device_node *msi_np; -+ struct irq_domain *d; -+ -+ msi_np = of_parse_phandle(np, "msi-parent", 0); -+ if (!msi_np) -+ return; -+ -+ d = irq_find_matching_host(msi_np, DOMAIN_BUS_PLATFORM_MSI); -+ if (!d) -+ d = irq_find_host(msi_np); -+ dev_set_msi_domain(dev, d); -+} -diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c -index ecc5fa5..5751dc5 100644 ---- a/drivers/of/of_pci.c -+++ b/drivers/of/of_pci.c -@@ -2,6 +2,7 @@ - #include - #include - #include -+#include - #include - #include - -@@ -116,6 +117,26 @@ int of_get_pci_domain_nr(struct device_node *node) - } - EXPORT_SYMBOL_GPL(of_get_pci_domain_nr); - -+/** -+ * of_pci_dma_configure - Setup DMA configuration -+ * @dev: ptr to pci_dev struct of the PCI device -+ * -+ * Function to update PCI devices's DMA configuration using the same -+ * info from the OF node of host bridge's parent (if any). -+ */ -+void of_pci_dma_configure(struct pci_dev *pci_dev) -+{ -+ struct device *dev = &pci_dev->dev; -+ struct device *bridge = pci_get_host_bridge_device(pci_dev); -+ -+ if (!bridge->parent) -+ return; -+ -+ of_dma_configure(dev, bridge->parent->of_node); -+ pci_put_host_bridge_device(bridge); -+} -+EXPORT_SYMBOL_GPL(of_pci_dma_configure); -+ - #if defined(CONFIG_OF_ADDRESS) - /** - * of_pci_get_host_bridge_resources - Parse PCI host bridge resources from DT -@@ -140,7 +161,7 @@ int of_pci_get_host_bridge_resources(struct device_node *dev, - unsigned char busno, unsigned char bus_max, - struct list_head *resources, resource_size_t *io_base) - { -- struct pci_host_bridge_window *window; -+ struct resource_entry *window; - struct resource *res; - struct resource *bus_range; - struct of_pci_range range; -@@ -226,10 +247,9 @@ int of_pci_get_host_bridge_resources(struct device_node *dev, - conversion_failed: - kfree(res); - parse_failed: -- list_for_each_entry(window, resources, list) -+ resource_list_for_each_entry(window, resources) - kfree(window->res); - pci_free_resource_list(resources); -- kfree(bus_range); - return err; - } - EXPORT_SYMBOL_GPL(of_pci_get_host_bridge_resources); -@@ -240,7 +260,7 @@ EXPORT_SYMBOL_GPL(of_pci_get_host_bridge_resources); - static LIST_HEAD(of_pci_msi_chip_list); - static DEFINE_MUTEX(of_pci_msi_chip_mutex); - --int of_pci_msi_chip_add(struct msi_chip *chip) -+int of_pci_msi_chip_add(struct msi_controller *chip) - { - if (!of_property_read_bool(chip->of_node, "msi-controller")) - return -EINVAL; -@@ -253,7 +273,7 @@ int of_pci_msi_chip_add(struct msi_chip *chip) - } - EXPORT_SYMBOL_GPL(of_pci_msi_chip_add); - --void of_pci_msi_chip_remove(struct msi_chip *chip) -+void of_pci_msi_chip_remove(struct msi_controller *chip) - { - mutex_lock(&of_pci_msi_chip_mutex); - list_del(&chip->list); -@@ -261,9 +281,9 @@ void of_pci_msi_chip_remove(struct msi_chip *chip) - } - EXPORT_SYMBOL_GPL(of_pci_msi_chip_remove); - --struct msi_chip *of_pci_find_msi_chip_by_node(struct device_node *of_node) -+struct msi_controller *of_pci_find_msi_chip_by_node(struct device_node *of_node) - { -- struct msi_chip *c; -+ struct msi_controller *c; - - mutex_lock(&of_pci_msi_chip_mutex); - list_for_each_entry(c, &of_pci_msi_chip_list, list) { -diff --git a/drivers/of/platform.c b/drivers/of/platform.c -index 3b64d0b..8a002d6 100644 ---- a/drivers/of/platform.c -+++ b/drivers/of/platform.c -@@ -25,6 +25,7 @@ - - const struct of_device_id of_default_bus_match_table[] = { - { .compatible = "simple-bus", }, -+ { .compatible = "simple-mfd", }, - #ifdef CONFIG_ARM_AMBA - { .compatible = "arm,amba-bus", }, - #endif /* CONFIG_ARM_AMBA */ -@@ -138,7 +139,7 @@ struct platform_device *of_device_alloc(struct device_node *np, - } - - dev->dev.of_node = of_node_get(np); -- dev->dev.parent = parent; -+ dev->dev.parent = parent ? : &platform_bus; - - if (bus_id) - dev_set_name(&dev->dev, "%s", bus_id); -@@ -149,57 +150,9 @@ struct platform_device *of_device_alloc(struct device_node *np, - } - EXPORT_SYMBOL(of_device_alloc); - --/** -- * of_dma_configure - Setup DMA configuration -- * @dev: Device to apply DMA configuration -- * -- * Try to get devices's DMA configuration from DT and update it -- * accordingly. -- * -- * In case if platform code need to use own special DMA configuration,it -- * can use Platform bus notifier and handle BUS_NOTIFY_ADD_DEVICE event -- * to fix up DMA configuration. -- */ --static void of_dma_configure(struct device *dev) -+static void of_dma_deconfigure(struct device *dev) - { -- u64 dma_addr, paddr, size; -- int ret; -- -- /* -- * Set default dma-mask to 32 bit. Drivers are expected to setup -- * the correct supported dma_mask. -- */ -- dev->coherent_dma_mask = DMA_BIT_MASK(32); -- -- /* -- * Set it to coherent_dma_mask by default if the architecture -- * code has not set it. -- */ -- if (!dev->dma_mask) -- dev->dma_mask = &dev->coherent_dma_mask; -- -- /* -- * if dma-coherent property exist, call arch hook to setup -- * dma coherent operations. -- */ -- if (of_dma_is_coherent(dev->of_node)) { -- set_arch_dma_coherent_ops(dev); -- dev_dbg(dev, "device is dma coherent\n"); -- } -- -- /* -- * if dma-ranges property doesn't exist - just return else -- * setup the dma offset -- */ -- ret = of_dma_get_range(dev->of_node, &dma_addr, &paddr, &size); -- if (ret < 0) { -- dev_dbg(dev, "no dma range information to setup\n"); -- return; -- } -- -- /* DMA ranges found. Calculate and set dma_pfn_offset */ -- dev->dma_pfn_offset = PFN_DOWN(paddr - dma_addr); -- dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", dev->dma_pfn_offset); -+ arch_teardown_dma_ops(dev); - } - - /** -@@ -228,16 +181,13 @@ static struct platform_device *of_platform_device_create_pdata( - if (!dev) - goto err_clear_flag; - -- of_dma_configure(&dev->dev); - dev->dev.bus = &platform_bus_type; - dev->dev.platform_data = platform_data; -- -- /* We do not fill the DMA ops for platform devices by default. -- * This is currently the responsibility of the platform code -- * to do such, possibly using a device notifier -- */ -+ of_dma_configure(&dev->dev, dev->dev.of_node); -+ of_msi_configure(&dev->dev, dev->dev.of_node); - - if (of_device_add(dev) != 0) { -+ of_dma_deconfigure(&dev->dev); - platform_device_put(dev); - goto err_clear_flag; - } -@@ -291,13 +241,13 @@ static struct amba_device *of_amba_device_create(struct device_node *node, - - /* setup generic device info */ - dev->dev.of_node = of_node_get(node); -- dev->dev.parent = parent; -+ dev->dev.parent = parent ? : &platform_bus; - dev->dev.platform_data = platform_data; - if (bus_id) - dev_set_name(&dev->dev, "%s", bus_id); - else - of_device_make_bus_id(&dev->dev); -- of_dma_configure(&dev->dev); -+ of_dma_configure(&dev->dev, dev->dev.of_node); - - /* Allow the HW Peripheral ID to be overridden */ - prop = of_get_property(node, "arm,primecell-periphid", NULL); -@@ -500,6 +450,7 @@ int of_platform_populate(struct device_node *root, - if (rc) - break; - } -+ of_node_set_flag(root, OF_POPULATED_BUS); - - of_node_put(root); - return rc; -@@ -523,6 +474,7 @@ static int of_platform_device_destroy(struct device *dev, void *data) - amba_device_unregister(to_amba_device(dev)); - #endif - -+ of_dma_deconfigure(dev); - of_node_clear_flag(dev->of_node, OF_POPULATED); - of_node_clear_flag(dev->of_node, OF_POPULATED_BUS); - return 0; -@@ -542,8 +494,75 @@ static int of_platform_device_destroy(struct device *dev, void *data) - */ - void of_platform_depopulate(struct device *parent) - { -- device_for_each_child(parent, NULL, of_platform_device_destroy); -+ if (parent->of_node && of_node_check_flag(parent->of_node, OF_POPULATED_BUS)) { -+ device_for_each_child(parent, NULL, of_platform_device_destroy); -+ of_node_clear_flag(parent->of_node, OF_POPULATED_BUS); -+ } - } - EXPORT_SYMBOL_GPL(of_platform_depopulate); - -+#ifdef CONFIG_OF_DYNAMIC -+static int of_platform_notify(struct notifier_block *nb, -+ unsigned long action, void *arg) -+{ -+ struct of_reconfig_data *rd = arg; -+ struct platform_device *pdev_parent, *pdev; -+ bool children_left; -+ -+ switch (of_reconfig_get_state_change(action, rd)) { -+ case OF_RECONFIG_CHANGE_ADD: -+ /* verify that the parent is a bus */ -+ if (!of_node_check_flag(rd->dn->parent, OF_POPULATED_BUS)) -+ return NOTIFY_OK; /* not for us */ -+ -+ /* already populated? (driver using of_populate manually) */ -+ if (of_node_check_flag(rd->dn, OF_POPULATED)) -+ return NOTIFY_OK; -+ -+ /* pdev_parent may be NULL when no bus platform device */ -+ pdev_parent = of_find_device_by_node(rd->dn->parent); -+ pdev = of_platform_device_create(rd->dn, NULL, -+ pdev_parent ? &pdev_parent->dev : NULL); -+ of_dev_put(pdev_parent); -+ -+ if (pdev == NULL) { -+ pr_err("%s: failed to create for '%s'\n", -+ __func__, rd->dn->full_name); -+ /* of_platform_device_create tosses the error code */ -+ return notifier_from_errno(-EINVAL); -+ } -+ break; -+ -+ case OF_RECONFIG_CHANGE_REMOVE: -+ -+ /* already depopulated? */ -+ if (!of_node_check_flag(rd->dn, OF_POPULATED)) -+ return NOTIFY_OK; -+ -+ /* find our device by node */ -+ pdev = of_find_device_by_node(rd->dn); -+ if (pdev == NULL) -+ return NOTIFY_OK; /* no? not meant for us */ -+ -+ /* unregister takes one ref away */ -+ of_platform_device_destroy(&pdev->dev, &children_left); -+ -+ /* and put the reference of the find */ -+ of_dev_put(pdev); -+ break; -+ } -+ -+ return NOTIFY_OK; -+} -+ -+static struct notifier_block platform_of_notifier = { -+ .notifier_call = of_platform_notify, -+}; -+ -+void of_platform_register_reconfig_notifier(void) -+{ -+ WARN_ON(of_reconfig_notifier_register(&platform_of_notifier)); -+} -+#endif /* CONFIG_OF_DYNAMIC */ -+ - #endif /* CONFIG_OF_ADDRESS */ -diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig -index 893503f..cced842 100644 ---- a/drivers/pci/Kconfig -+++ b/drivers/pci/Kconfig -@@ -4,6 +4,7 @@ - config PCI_MSI - bool "Message Signaled Interrupts (MSI and MSI-X)" - depends on PCI -+ select GENERIC_MSI_IRQ - help - This allows device drivers to enable MSI (Message Signaled - Interrupts). Message Signaled Interrupts enable a device to -@@ -16,6 +17,11 @@ config PCI_MSI - - If you don't know what to do here, say Y. - -+config PCI_MSI_IRQ_DOMAIN -+ bool -+ depends on PCI_MSI -+ select GENERIC_MSI_IRQ_DOMAIN -+ - config PCI_DEBUG - bool "PCI Debugging" - depends on PCI && DEBUG_KERNEL -diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c -index 8fb1618..90fa3a7 100644 ---- a/drivers/pci/bus.c -+++ b/drivers/pci/bus.c -@@ -20,17 +20,16 @@ - void pci_add_resource_offset(struct list_head *resources, struct resource *res, - resource_size_t offset) - { -- struct pci_host_bridge_window *window; -+ struct resource_entry *entry; - -- window = kzalloc(sizeof(struct pci_host_bridge_window), GFP_KERNEL); -- if (!window) { -+ entry = resource_list_create_entry(res, 0); -+ if (!entry) { - printk(KERN_ERR "PCI: can't add host bridge window %pR\n", res); - return; - } - -- window->res = res; -- window->offset = offset; -- list_add_tail(&window->list, resources); -+ entry->offset = offset; -+ resource_list_add_tail(entry, resources); - } - EXPORT_SYMBOL(pci_add_resource_offset); - -@@ -42,12 +41,7 @@ EXPORT_SYMBOL(pci_add_resource); - - void pci_free_resource_list(struct list_head *resources) - { -- struct pci_host_bridge_window *window, *tmp; -- -- list_for_each_entry_safe(window, tmp, resources, list) { -- list_del(&window->list); -- kfree(window); -- } -+ resource_list_free(resources); - } - EXPORT_SYMBOL(pci_free_resource_list); - -diff --git a/drivers/pci/host-bridge.c b/drivers/pci/host-bridge.c -index 0e5f3c9..3e5bbf9 100644 ---- a/drivers/pci/host-bridge.c -+++ b/drivers/pci/host-bridge.c -@@ -23,6 +23,20 @@ static struct pci_host_bridge *find_pci_host_bridge(struct pci_bus *bus) - return to_pci_host_bridge(root_bus->bridge); - } - -+struct device *pci_get_host_bridge_device(struct pci_dev *dev) -+{ -+ struct pci_bus *root_bus = find_pci_root_bus(dev->bus); -+ struct device *bridge = root_bus->bridge; -+ -+ kobject_get(&bridge->kobj); -+ return bridge; -+} -+ -+void pci_put_host_bridge_device(struct device *dev) -+{ -+ kobject_put(&dev->kobj); -+} -+ - void pci_set_host_bridge_release(struct pci_host_bridge *bridge, - void (*release_fn)(struct pci_host_bridge *), - void *release_data) -@@ -35,10 +49,10 @@ void pcibios_resource_to_bus(struct pci_bus *bus, struct pci_bus_region *region, - struct resource *res) - { - struct pci_host_bridge *bridge = find_pci_host_bridge(bus); -- struct pci_host_bridge_window *window; -+ struct resource_entry *window; - resource_size_t offset = 0; - -- list_for_each_entry(window, &bridge->windows, list) { -+ resource_list_for_each_entry(window, &bridge->windows) { - if (resource_contains(window->res, res)) { - offset = window->offset; - break; -@@ -60,10 +74,10 @@ void pcibios_bus_to_resource(struct pci_bus *bus, struct resource *res, - struct pci_bus_region *region) - { - struct pci_host_bridge *bridge = find_pci_host_bridge(bus); -- struct pci_host_bridge_window *window; -+ struct resource_entry *window; - resource_size_t offset = 0; - -- list_for_each_entry(window, &bridge->windows, list) { -+ resource_list_for_each_entry(window, &bridge->windows) { - struct pci_bus_region bus_region; - - if (resource_type(res) != resource_type(window->res)) -diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig -index 3dc25fa..96586b1 100644 ---- a/drivers/pci/host/Kconfig -+++ b/drivers/pci/host/Kconfig -@@ -86,9 +86,26 @@ config PCI_XGENE - depends on ARCH_XGENE - depends on OF - select PCIEPORTBUS -+ select PCI_MSI_IRQ_DOMAIN if PCI_MSI - help - Say Y here if you want internal PCI support on APM X-Gene SoC. - There are 5 internal PCIe ports available. Each port is GEN3 capable - and have varied lanes from x1 to x8. - -+config PCI_XGENE_MSI -+ bool "X-Gene v1 PCIe MSI feature" -+ depends on PCI_XGENE && PCI_MSI -+ default y -+ help -+ Say Y here if you want PCIe MSI support for the APM X-Gene v1 SoC. -+ This MSI driver supports 5 PCIe ports on the APM X-Gene v1 SoC. -+ -+config PCI_LAYERSCAPE -+ bool "Freescale Layerscape PCIe controller" -+ depends on OF && (ARM || ARCH_LAYERSCAPE) -+ select PCIE_DW -+ select MFD_SYSCON -+ help -+ Say Y here if you want PCIe controller support on Layerscape SoCs. -+ - endmenu -diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile -index 26b3461..029685e 100644 ---- a/drivers/pci/host/Makefile -+++ b/drivers/pci/host/Makefile -@@ -1,3 +1,4 @@ -+obj-$(CONFIG_PCIE_DW_BASE) += pcie-designware-base.o - obj-$(CONFIG_PCIE_DW) += pcie-designware.o - obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o - obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o -@@ -11,3 +12,5 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o - obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o - obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o - obj-$(CONFIG_PCI_XGENE) += pci-xgene.o -+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o -+obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o -diff --git a/drivers/pci/host/pci-dra7xx.c b/drivers/pci/host/pci-dra7xx.c -index 52b34fe..84a45cf 100644 ---- a/drivers/pci/host/pci-dra7xx.c -+++ b/drivers/pci/host/pci-dra7xx.c -@@ -61,6 +61,7 @@ - - #define PCIECTRL_DRA7XX_CONF_PHY_CS 0x010C - #define LINK_UP BIT(16) -+#define DRA7XX_CPU_TO_BUS_ADDR 0x0FFFFFFF - - struct dra7xx_pcie { - void __iomem *base; -@@ -144,6 +145,12 @@ static void dra7xx_pcie_enable_interrupts(struct pcie_port *pp) - static void dra7xx_pcie_host_init(struct pcie_port *pp) - { - dw_pcie_setup_rc(pp); -+ -+ pp->io_base &= DRA7XX_CPU_TO_BUS_ADDR; -+ pp->mem_base &= DRA7XX_CPU_TO_BUS_ADDR; -+ pp->cfg0_base &= DRA7XX_CPU_TO_BUS_ADDR; -+ pp->cfg1_base &= DRA7XX_CPU_TO_BUS_ADDR; -+ - dra7xx_pcie_establish_link(pp); - if (IS_ENABLED(CONFIG_PCI_MSI)) - dw_pcie_msi_init(pp); -@@ -160,7 +167,6 @@ static int dra7xx_pcie_intx_map(struct irq_domain *domain, unsigned int irq, - { - irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq); - irq_set_chip_data(irq, domain->host_data); -- set_irq_flags(irq, IRQF_VALID); - - return 0; - } -diff --git a/drivers/pci/host/pci-exynos.c b/drivers/pci/host/pci-exynos.c -index c5d0ca3..2fd6b4e 100644 ---- a/drivers/pci/host/pci-exynos.c -+++ b/drivers/pci/host/pci-exynos.c -@@ -466,7 +466,7 @@ static int exynos_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, - int ret; - - exynos_pcie_sideband_dbi_r_mode(pp, true); -- ret = dw_pcie_cfg_read(pp->dbi_base + (where & ~0x3), where, size, val); -+ ret = dw_pcie_cfg_read(pp->dbi_base + where, size, val); - exynos_pcie_sideband_dbi_r_mode(pp, false); - return ret; - } -@@ -477,8 +477,7 @@ static int exynos_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, - int ret; - - exynos_pcie_sideband_dbi_w_mode(pp, true); -- ret = dw_pcie_cfg_write(pp->dbi_base + (where & ~0x3), -- where, size, val); -+ ret = dw_pcie_cfg_write(pp->dbi_base + where, size, val); - exynos_pcie_sideband_dbi_w_mode(pp, false); - return ret; - } -diff --git a/drivers/pci/host/pci-host-generic.c b/drivers/pci/host/pci-host-generic.c -index 3d2076f..83fb705 100644 ---- a/drivers/pci/host/pci-host-generic.c -+++ b/drivers/pci/host/pci-host-generic.c -@@ -32,13 +32,22 @@ struct gen_pci_cfg_bus_ops { - - struct gen_pci_cfg_windows { - struct resource res; -- struct resource bus_range; -+ struct resource *bus_range; - void __iomem **win; - - const struct gen_pci_cfg_bus_ops *ops; - }; - -+/* -+ * ARM pcibios functions expect the ARM struct pci_sys_data as the PCI -+ * sysdata. Add pci_sys_data as the first element in struct gen_pci so -+ * that when we use a gen_pci pointer as sysdata, it is also a pointer to -+ * a struct pci_sys_data. -+ */ - struct gen_pci { -+#ifdef CONFIG_ARM -+ struct pci_sys_data sys; -+#endif - struct pci_host_bridge host; - struct gen_pci_cfg_windows cfg; - struct list_head resources; -@@ -48,9 +57,8 @@ static void __iomem *gen_pci_map_cfg_bus_cam(struct pci_bus *bus, - unsigned int devfn, - int where) - { -- struct pci_sys_data *sys = bus->sysdata; -- struct gen_pci *pci = sys->private_data; -- resource_size_t idx = bus->number - pci->cfg.bus_range.start; -+ struct gen_pci *pci = bus->sysdata; -+ resource_size_t idx = bus->number - pci->cfg.bus_range->start; - - return pci->cfg.win[idx] + ((devfn << 8) | where); - } -@@ -64,9 +72,8 @@ static void __iomem *gen_pci_map_cfg_bus_ecam(struct pci_bus *bus, - unsigned int devfn, - int where) - { -- struct pci_sys_data *sys = bus->sysdata; -- struct gen_pci *pci = sys->private_data; -- resource_size_t idx = bus->number - pci->cfg.bus_range.start; -+ struct gen_pci *pci = bus->sysdata; -+ resource_size_t idx = bus->number - pci->cfg.bus_range->start; - - return pci->cfg.win[idx] + ((devfn << 12) | where); - } -@@ -76,55 +83,9 @@ static struct gen_pci_cfg_bus_ops gen_pci_cfg_ecam_bus_ops = { - .map_bus = gen_pci_map_cfg_bus_ecam, - }; - --static int gen_pci_config_read(struct pci_bus *bus, unsigned int devfn, -- int where, int size, u32 *val) --{ -- void __iomem *addr; -- struct pci_sys_data *sys = bus->sysdata; -- struct gen_pci *pci = sys->private_data; -- -- addr = pci->cfg.ops->map_bus(bus, devfn, where); -- -- switch (size) { -- case 1: -- *val = readb(addr); -- break; -- case 2: -- *val = readw(addr); -- break; -- default: -- *val = readl(addr); -- } -- -- return PCIBIOS_SUCCESSFUL; --} -- --static int gen_pci_config_write(struct pci_bus *bus, unsigned int devfn, -- int where, int size, u32 val) --{ -- void __iomem *addr; -- struct pci_sys_data *sys = bus->sysdata; -- struct gen_pci *pci = sys->private_data; -- -- addr = pci->cfg.ops->map_bus(bus, devfn, where); -- -- switch (size) { -- case 1: -- writeb(val, addr); -- break; -- case 2: -- writew(val, addr); -- break; -- default: -- writel(val, addr); -- } -- -- return PCIBIOS_SUCCESSFUL; --} -- - static struct pci_ops gen_pci_ops = { -- .read = gen_pci_config_read, -- .write = gen_pci_config_write, -+ .read = pci_generic_config_read, -+ .write = pci_generic_config_write, - }; - - static const struct of_device_id gen_pci_of_match[] = { -@@ -138,106 +99,50 @@ static const struct of_device_id gen_pci_of_match[] = { - }; - MODULE_DEVICE_TABLE(of, gen_pci_of_match); - --static int gen_pci_calc_io_offset(struct device *dev, -- struct of_pci_range *range, -- struct resource *res, -- resource_size_t *offset) --{ -- static atomic_t wins = ATOMIC_INIT(0); -- int err, idx, max_win; -- unsigned int window; -- -- if (!PAGE_ALIGNED(range->cpu_addr)) -- return -EINVAL; -- -- max_win = (IO_SPACE_LIMIT + 1) / SZ_64K; -- idx = atomic_inc_return(&wins); -- if (idx > max_win) -- return -ENOSPC; -- -- window = (idx - 1) * SZ_64K; -- err = pci_ioremap_io(window, range->cpu_addr); -- if (err) -- return err; -- -- of_pci_range_to_resource(range, dev->of_node, res); -- res->start = window; -- res->end = res->start + range->size - 1; -- *offset = window - range->pci_addr; -- return 0; --} -- --static int gen_pci_calc_mem_offset(struct device *dev, -- struct of_pci_range *range, -- struct resource *res, -- resource_size_t *offset) --{ -- of_pci_range_to_resource(range, dev->of_node, res); -- *offset = range->cpu_addr - range->pci_addr; -- return 0; --} -- - static void gen_pci_release_of_pci_ranges(struct gen_pci *pci) - { -- struct pci_host_bridge_window *win; -- -- list_for_each_entry(win, &pci->resources, list) -- release_resource(win->res); -- - pci_free_resource_list(&pci->resources); - } - - static int gen_pci_parse_request_of_pci_ranges(struct gen_pci *pci) - { -- struct of_pci_range range; -- struct of_pci_range_parser parser; - int err, res_valid = 0; - struct device *dev = pci->host.dev.parent; - struct device_node *np = dev->of_node; -+ resource_size_t iobase; -+ struct resource_entry *win; - -- if (of_pci_range_parser_init(&parser, np)) { -- dev_err(dev, "missing \"ranges\" property\n"); -- return -EINVAL; -- } -- -- for_each_of_pci_range(&parser, &range) { -- struct resource *parent, *res; -- resource_size_t offset; -- u32 restype = range.flags & IORESOURCE_TYPE_BITS; -+ err = of_pci_get_host_bridge_resources(np, 0, 0xff, &pci->resources, -+ &iobase); -+ if (err) -+ return err; - -- res = devm_kmalloc(dev, sizeof(*res), GFP_KERNEL); -- if (!res) { -- err = -ENOMEM; -- goto out_release_res; -- } -+ resource_list_for_each_entry(win, &pci->resources) { -+ struct resource *parent, *res = win->res; - -- switch (restype) { -+ switch (resource_type(res)) { - case IORESOURCE_IO: - parent = &ioport_resource; -- err = gen_pci_calc_io_offset(dev, &range, res, &offset); -+ err = pci_remap_iospace(res, iobase); -+ if (err) { -+ dev_warn(dev, "error %d: failed to map resource %pR\n", -+ err, res); -+ continue; -+ } - break; - case IORESOURCE_MEM: - parent = &iomem_resource; -- err = gen_pci_calc_mem_offset(dev, &range, res, &offset); -- res_valid |= !(res->flags & IORESOURCE_PREFETCH || err); -+ res_valid |= !(res->flags & IORESOURCE_PREFETCH); - break; -+ case IORESOURCE_BUS: -+ pci->cfg.bus_range = res; - default: -- err = -EINVAL; - continue; - } - -- if (err) { -- dev_warn(dev, -- "error %d: failed to add resource [type 0x%x, %lld bytes]\n", -- err, restype, range.size); -- continue; -- } -- -- err = request_resource(parent, res); -+ err = devm_request_resource(dev, parent, res); - if (err) - goto out_release_res; -- -- pci_add_resource_offset(&pci->resources, res, offset); - } - - if (!res_valid) { -@@ -262,38 +167,30 @@ static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci) - struct device *dev = pci->host.dev.parent; - struct device_node *np = dev->of_node; - -- if (of_pci_parse_bus_range(np, &pci->cfg.bus_range)) -- pci->cfg.bus_range = (struct resource) { -- .name = np->name, -- .start = 0, -- .end = 0xff, -- .flags = IORESOURCE_BUS, -- }; -- - err = of_address_to_resource(np, 0, &pci->cfg.res); - if (err) { - dev_err(dev, "missing \"reg\" property\n"); - return err; - } - -- pci->cfg.win = devm_kcalloc(dev, resource_size(&pci->cfg.bus_range), -+ /* Limit the bus-range to fit within reg */ -+ bus_max = pci->cfg.bus_range->start + -+ (resource_size(&pci->cfg.res) >> pci->cfg.ops->bus_shift) - 1; -+ pci->cfg.bus_range->end = min_t(resource_size_t, -+ pci->cfg.bus_range->end, bus_max); -+ -+ pci->cfg.win = devm_kcalloc(dev, resource_size(pci->cfg.bus_range), - sizeof(*pci->cfg.win), GFP_KERNEL); - if (!pci->cfg.win) - return -ENOMEM; - -- /* Limit the bus-range to fit within reg */ -- bus_max = pci->cfg.bus_range.start + -- (resource_size(&pci->cfg.res) >> pci->cfg.ops->bus_shift) - 1; -- pci->cfg.bus_range.end = min_t(resource_size_t, pci->cfg.bus_range.end, -- bus_max); -- - /* Map our Configuration Space windows */ - if (!devm_request_mem_region(dev, pci->cfg.res.start, - resource_size(&pci->cfg.res), - "Configuration Space")) - return -ENOMEM; - -- bus_range = &pci->cfg.bus_range; -+ bus_range = pci->cfg.bus_range; - for (busn = bus_range->start; busn <= bus_range->end; ++busn) { - u32 idx = busn - bus_range->start; - u32 sz = 1 << pci->cfg.ops->bus_shift; -@@ -305,18 +202,9 @@ static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci) - return -ENOMEM; - } - -- /* Register bus resource */ -- pci_add_resource(&pci->resources, bus_range); - return 0; - } - --static int gen_pci_setup(int nr, struct pci_sys_data *sys) --{ -- struct gen_pci *pci = sys->private_data; -- list_splice_init(&pci->resources, &sys->resources); -- return 1; --} -- - static int gen_pci_probe(struct platform_device *pdev) - { - int err; -@@ -326,13 +214,7 @@ static int gen_pci_probe(struct platform_device *pdev) - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - struct gen_pci *pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); -- struct hw_pci hw = { -- .nr_controllers = 1, -- .private_data = (void **)&pci, -- .setup = gen_pci_setup, -- .map_irq = of_irq_parse_and_map_pci, -- .ops = &gen_pci_ops, -- }; -+ struct pci_bus *bus, *child; - - if (!pci) - return -ENOMEM; -@@ -353,6 +235,7 @@ static int gen_pci_probe(struct platform_device *pdev) - - of_id = of_match_node(gen_pci_of_match, np); - pci->cfg.ops = of_id->data; -+ gen_pci_ops.map_bus = pci->cfg.ops->map_bus; - pci->host.dev.parent = dev; - INIT_LIST_HEAD(&pci->host.windows); - INIT_LIST_HEAD(&pci->resources); -@@ -369,7 +252,27 @@ static int gen_pci_probe(struct platform_device *pdev) - return err; - } - -- pci_common_init_dev(dev, &hw); -+ /* Do not reassign resources if probe only */ -+ if (!pci_has_flag(PCI_PROBE_ONLY)) -+ pci_add_flags(PCI_REASSIGN_ALL_RSRC | PCI_REASSIGN_ALL_BUS); -+ -+ bus = pci_scan_root_bus(dev, 0, &gen_pci_ops, pci, &pci->resources); -+ if (!bus) { -+ dev_err(dev, "Scanning rootbus failed"); -+ return -ENODEV; -+ } -+ -+ pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci); -+ -+ if (!pci_has_flag(PCI_PROBE_ONLY)) { -+ pci_bus_size_bridges(bus); -+ pci_bus_assign_resources(bus); -+ -+ list_for_each_entry(child, &bus->children, node) -+ pcie_bus_configure_settings(child); -+ } -+ -+ pci_bus_add_devices(bus); - return 0; - } - -diff --git a/drivers/pci/host/pci-keystone-dw.c b/drivers/pci/host/pci-keystone-dw.c -index 34086ce..c1b5980 100644 ---- a/drivers/pci/host/pci-keystone-dw.c -+++ b/drivers/pci/host/pci-keystone-dw.c -@@ -70,7 +70,7 @@ static inline void update_reg_offset_bit_pos(u32 offset, u32 *reg_offset, - *bit_pos = offset >> 3; - } - --u32 ks_dw_pcie_get_msi_addr(struct pcie_port *pp) -+phys_addr_t ks_dw_pcie_get_msi_addr(struct pcie_port *pp) - { - struct keystone_pcie *ks_pcie = to_keystone_pcie(pp); - -@@ -104,14 +104,13 @@ static void ks_dw_pcie_msi_irq_ack(struct irq_data *d) - { - u32 offset, reg_offset, bit_pos; - struct keystone_pcie *ks_pcie; -- unsigned int irq = d->irq; - struct msi_desc *msi; - struct pcie_port *pp; - -- msi = irq_get_msi_desc(irq); -- pp = sys_to_pcie(msi->dev->bus->sysdata); -+ msi = irq_data_get_msi_desc(d); -+ pp = sys_to_pcie(msi_desc_to_pci_sysdata(msi)); - ks_pcie = to_keystone_pcie(pp); -- offset = irq - irq_linear_revmap(pp->irq_domain, 0); -+ offset = d->irq - irq_linear_revmap(pp->irq_domain, 0); - update_reg_offset_bit_pos(offset, ®_offset, &bit_pos); - - writel(BIT(bit_pos), -@@ -142,20 +141,19 @@ void ks_dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq) - static void ks_dw_pcie_msi_irq_mask(struct irq_data *d) - { - struct keystone_pcie *ks_pcie; -- unsigned int irq = d->irq; - struct msi_desc *msi; - struct pcie_port *pp; - u32 offset; - -- msi = irq_get_msi_desc(irq); -- pp = sys_to_pcie(msi->dev->bus->sysdata); -+ msi = irq_data_get_msi_desc(d); -+ pp = sys_to_pcie(msi_desc_to_pci_sysdata(msi)); - ks_pcie = to_keystone_pcie(pp); -- offset = irq - irq_linear_revmap(pp->irq_domain, 0); -+ offset = d->irq - irq_linear_revmap(pp->irq_domain, 0); - - /* Mask the end point if PVM implemented */ - if (IS_ENABLED(CONFIG_PCI_MSI)) { - if (msi->msi_attrib.maskbit) -- mask_msi_irq(d); -+ pci_msi_mask_irq(d); - } - - ks_dw_pcie_msi_clear_irq(pp, offset); -@@ -164,20 +162,19 @@ static void ks_dw_pcie_msi_irq_mask(struct irq_data *d) - static void ks_dw_pcie_msi_irq_unmask(struct irq_data *d) - { - struct keystone_pcie *ks_pcie; -- unsigned int irq = d->irq; - struct msi_desc *msi; - struct pcie_port *pp; - u32 offset; - -- msi = irq_get_msi_desc(irq); -- pp = sys_to_pcie(msi->dev->bus->sysdata); -+ msi = irq_data_get_msi_desc(d); -+ pp = sys_to_pcie(msi_desc_to_pci_sysdata(msi)); - ks_pcie = to_keystone_pcie(pp); -- offset = irq - irq_linear_revmap(pp->irq_domain, 0); -+ offset = d->irq - irq_linear_revmap(pp->irq_domain, 0); - - /* Mask the end point if PVM implemented */ - if (IS_ENABLED(CONFIG_PCI_MSI)) { - if (msi->msi_attrib.maskbit) -- unmask_msi_irq(d); -+ pci_msi_unmask_irq(d); - } - - ks_dw_pcie_msi_set_irq(pp, offset); -@@ -196,7 +193,6 @@ static int ks_dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq, - irq_set_chip_and_handler(irq, &ks_dw_pcie_msi_irq_chip, - handle_level_irq); - irq_set_chip_data(irq, domain->host_data); -- set_irq_flags(irq, IRQF_VALID); - - return 0; - } -@@ -205,7 +201,7 @@ const struct irq_domain_ops ks_dw_pcie_msi_domain_ops = { - .map = ks_dw_pcie_msi_map, - }; - --int ks_dw_pcie_msi_host_init(struct pcie_port *pp, struct msi_chip *chip) -+int ks_dw_pcie_msi_host_init(struct pcie_port *pp, struct msi_controller *chip) - { - struct keystone_pcie *ks_pcie = to_keystone_pcie(pp); - int i; -@@ -277,7 +273,6 @@ static int ks_dw_pcie_init_legacy_irq_map(struct irq_domain *d, - irq_set_chip_and_handler(irq, &ks_dw_pcie_legacy_irq_chip, - handle_level_irq); - irq_set_chip_data(irq, d->host_data); -- set_irq_flags(irq, IRQF_VALID); - - return 0; - } -@@ -327,7 +322,7 @@ static void ks_dw_pcie_clear_dbi_mode(void __iomem *reg_virt) - void ks_dw_pcie_setup_rc_app_regs(struct keystone_pcie *ks_pcie) - { - struct pcie_port *pp = &ks_pcie->pp; -- u32 start = pp->mem.start, end = pp->mem.end; -+ u32 start = pp->mem->start, end = pp->mem->end; - int i, tr_size; - - /* Disable BARs for inbound access */ -@@ -403,7 +398,7 @@ int ks_dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, - - addr = ks_pcie_cfg_setup(ks_pcie, bus_num, devfn); - -- return dw_pcie_cfg_read(addr + (where & ~0x3), where, size, val); -+ return dw_pcie_cfg_read(addr + where, size, val); - } - - int ks_dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, -@@ -415,7 +410,7 @@ int ks_dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, - - addr = ks_pcie_cfg_setup(ks_pcie, bus_num, devfn); - -- return dw_pcie_cfg_write(addr + (where & ~0x3), where, size, val); -+ return dw_pcie_cfg_write(addr + where, size, val); - } - - /** -diff --git a/drivers/pci/host/pci-keystone.h b/drivers/pci/host/pci-keystone.h -index 1fc1fce..f0944e8 100644 ---- a/drivers/pci/host/pci-keystone.h -+++ b/drivers/pci/host/pci-keystone.h -@@ -37,7 +37,7 @@ struct keystone_pcie { - - /* Keystone DW specific MSI controller APIs/definitions */ - void ks_dw_pcie_handle_msi_irq(struct keystone_pcie *ks_pcie, int offset); --u32 ks_dw_pcie_get_msi_addr(struct pcie_port *pp); -+phys_addr_t ks_dw_pcie_get_msi_addr(struct pcie_port *pp); - - /* Keystone specific PCI controller APIs */ - void ks_dw_pcie_enable_legacy_irqs(struct keystone_pcie *ks_pcie); -@@ -55,4 +55,4 @@ void ks_dw_pcie_msi_set_irq(struct pcie_port *pp, int irq); - void ks_dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq); - void ks_dw_pcie_v3_65_scan_bus(struct pcie_port *pp); - int ks_dw_pcie_msi_host_init(struct pcie_port *pp, -- struct msi_chip *chip); -+ struct msi_controller *chip); -diff --git a/drivers/pci/host/pci-layerscape.c b/drivers/pci/host/pci-layerscape.c -new file mode 100644 -index 0000000..d491b0f ---- /dev/null -+++ b/drivers/pci/host/pci-layerscape.c -@@ -0,0 +1,669 @@ -+/* -+ * PCIe host controller driver for Freescale Layerscape SoCs -+ * -+ * Copyright (C) 2014 Freescale Semiconductor. -+ * -+ * Author: Minghuan Lian -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "pcie-designware.h" -+ -+/* PEX1/2 Misc Ports Status Register */ -+#define SCFG_PEXMSCPORTSR(pex_idx) (0x94 + (pex_idx) * 4) -+#define SCFG_PEXPMWRCR(pex_idx) (0x5c + (pex_idx) * 0x64) -+#define LTSSM_STATE_SHIFT 20 -+#define LTSSM_STATE_MASK 0x3f -+#define LTSSM_PCIE_L0 0x11 /* L0 state */ -+#define LTSSM_PCIE_L2_IDLE 0x15 /* L2 idle state */ -+ -+/* PEX Internal Configuration Registers */ -+#define PCIE_STRFMR1 0x71c /* Symbol Timer & Filter Mask Register1 */ -+#define PCIE_DBI_RO_WR_EN 0x8bc /* DBI Read-Only Write Enable Register */ -+ -+/* PEX LUT registers */ -+#define PCIE_LUT_DBG 0x7FC /* PEX LUT Debug Register */ -+#define PCIE_LUT_UDR(n) (0x800 + (n) * 8) -+#define PCIE_LUT_LDR(n) (0x804 + (n) * 8) -+#define PCIE_LUT_MASK_ALL 0xffff -+#define PCIE_LUT_DR_NUM 32 -+#define PCIE_LUT_ENABLE (1 << 31) -+ -+#define PCIE_PM_SCR 0x44 -+#define PCIE_PM_SCR_PMEEN 0x10 -+#define PCIE_PM_SCR_PMEPS_D0 0xfffc -+#define PCIE_PM_SCR_PMEPS_D3 0x3 -+#define PCIE_PM_SCR_PME_STATE 0x8000 -+ -+#define PCIE_PEX_DCR 0x78 -+#define PCIE_PEX_DCR_AUXPOWEREN 0x0400 -+ -+#define PCIE_PEX_SSR 0x8a -+#define PCIE_PEX_SSR_PDS 0x40 -+ -+#define PCIE_PEX_RCR 0x8c -+#define PCIE_PEX_RCR_PMEIE 0x0008 -+ -+#define PCIE_PEX_RSR 0x90 -+#define PCIE_PEX_PMES 0x00010000 -+ -+#define QIXIS_RST_FORCE_3 0x45 -+#define QIXIS_RST_FORCE_3_PCIESLOT 0xe0 -+ -+#define CPLD_RST_PCIE_SLOT 0x14 -+#define CPLD_RST_PCIESLOT 0x3 -+ -+struct ls_pcie; -+ -+struct ls_pcie_pm_data { -+ void __iomem *fpga; -+ void __iomem *cpld; -+}; -+ -+struct ls_pcie_pm_ops { -+ u32 (*get_link_state)(struct ls_pcie *pcie); -+ int (*send_turn_off_message)(struct ls_pcie *pcie); -+ void (*clear_turn_off_message)(struct ls_pcie *pcie); -+ void (*reset_slot)(struct ls_pcie *pcie, -+ struct ls_pcie_pm_data *pm_data); -+}; -+ -+struct ls_pcie_drvdata { -+ u32 lut_offset; -+ u32 ltssm_shift; -+ struct pcie_host_ops *ops; -+ struct ls_pcie_pm_ops *pm; -+}; -+ -+struct ls_pcie { -+ struct list_head list_node; -+ void __iomem *dbi; -+ void __iomem *lut; -+ struct regmap *scfg; -+ struct pcie_port pp; -+ const struct ls_pcie_drvdata *drvdata; -+ struct ls_pcie_pm_data pm_data; -+ int index; -+ const u32 *avail_streamids; -+ int streamid_index; -+ int pme_irq; -+ bool in_slot; -+}; -+ -+#define to_ls_pcie(x) container_of(x, struct ls_pcie, pp) -+ -+u32 set_pcie_streamid_translation(struct pci_dev *pdev, u32 devid) -+{ -+ u32 index, streamid; -+ struct pcie_port *pp = pdev->bus->sysdata; -+ struct ls_pcie *pcie = to_ls_pcie(pp); -+ -+ if (!pcie->avail_streamids || !pcie->streamid_index) -+ return ~(u32)0; -+ -+ index = --pcie->streamid_index; -+ /* mask is set as all zeroes, want to match all bits */ -+ iowrite32((devid << 16), pcie->lut + PCIE_LUT_UDR(index)); -+ streamid = be32_to_cpup(&pcie->avail_streamids[index]); -+ iowrite32(streamid | PCIE_LUT_ENABLE, pcie->lut + PCIE_LUT_LDR(index)); -+ -+ return streamid; -+} -+ -+LIST_HEAD(hose_list); -+ -+static bool ls_pcie_is_bridge(struct ls_pcie *pcie) -+{ -+ u32 header_type; -+ -+ header_type = ioread8(pcie->dbi + PCI_HEADER_TYPE); -+ header_type &= 0x7f; -+ -+ return header_type == PCI_HEADER_TYPE_BRIDGE; -+} -+ -+/* Clear multi-function bit */ -+static void ls_pcie_clear_multifunction(struct ls_pcie *pcie) -+{ -+ iowrite8(PCI_HEADER_TYPE_BRIDGE, pcie->dbi + PCI_HEADER_TYPE); -+} -+ -+/* Fix class value */ -+static void ls_pcie_fix_class(struct ls_pcie *pcie) -+{ -+ iowrite16(PCI_CLASS_BRIDGE_PCI, pcie->dbi + PCI_CLASS_DEVICE); -+} -+ -+/* Drop MSG TLP except for Vendor MSG */ -+static void ls_pcie_drop_msg_tlp(struct ls_pcie *pcie) -+{ -+ u32 val; -+ -+ val = ioread32(pcie->dbi + PCIE_STRFMR1); -+ val &= 0xDFFFFFFF; -+ iowrite32(val, pcie->dbi + PCIE_STRFMR1); -+} -+ -+static int ls1021_pcie_link_up(struct pcie_port *pp) -+{ -+ u32 state; -+ struct ls_pcie *pcie = to_ls_pcie(pp); -+ -+ if (!pcie->scfg) -+ return 0; -+ -+ regmap_read(pcie->scfg, SCFG_PEXMSCPORTSR(pcie->index), &state); -+ state = (state >> LTSSM_STATE_SHIFT) & LTSSM_STATE_MASK; -+ -+ if (state < LTSSM_PCIE_L0) -+ return 0; -+ -+ return 1; -+} -+ -+static u32 ls1021_pcie_get_link_state(struct ls_pcie *pcie) -+{ -+ u32 state; -+ -+ if (!pcie->scfg) -+ return 0; -+ -+ regmap_read(pcie->scfg, SCFG_PEXMSCPORTSR(pcie->index), &state); -+ state = (state >> LTSSM_STATE_SHIFT) & LTSSM_STATE_MASK; -+ -+ return state; -+} -+ -+static int ls1021_pcie_send_turn_off_message(struct ls_pcie *pcie) -+{ -+ u32 val; -+ -+ if (!pcie->scfg) -+ return -EINVAL; -+ -+ /* Send Turn_off message */ -+ regmap_read(pcie->scfg, SCFG_PEXPMWRCR(pcie->index), &val); -+ val |= 0x80000000; -+ regmap_write(pcie->scfg, SCFG_PEXPMWRCR(pcie->index), val); -+ -+ return 0; -+} -+ -+static void ls1021_pcie_clear_turn_off_message(struct ls_pcie *pcie) -+{ -+ u32 val; -+ -+ if (!pcie->scfg) -+ return; -+ -+ /* Clear Turn_off message */ -+ regmap_read(pcie->scfg, SCFG_PEXPMWRCR(pcie->index), &val); -+ val &= 0x00000000; -+ regmap_write(pcie->scfg, SCFG_PEXPMWRCR(pcie->index), val); -+} -+ -+static void ls1021_pcie_reset_slot(struct ls_pcie *pcie, -+ struct ls_pcie_pm_data *pm_data) -+{ -+ u8 val; -+ -+ /* Try to reset PCIe slot to relink EP */ -+ if (pm_data->fpga) { -+ /* PULL DOWN PCIe RST# */ -+ val = ioread8(pm_data->fpga + QIXIS_RST_FORCE_3); -+ val |= QIXIS_RST_FORCE_3_PCIESLOT; -+ iowrite8(val, pm_data->fpga + QIXIS_RST_FORCE_3); -+ -+ /* PULL ON PCIe RST# */ -+ val = ioread8(pm_data->fpga + QIXIS_RST_FORCE_3); -+ val &= 0x0; -+ iowrite8(val, pm_data->fpga + QIXIS_RST_FORCE_3); -+ } -+ -+ if (pm_data->cpld) { -+ /* PULL DOWN PCIe RST# */ -+ val = ioread8(pm_data->cpld + CPLD_RST_PCIE_SLOT); -+ val &= 0x0; -+ iowrite8(val, pm_data->cpld + CPLD_RST_PCIE_SLOT); -+ -+ /* PULL ON PCIe RST# */ -+ val = ioread8(pm_data->cpld + CPLD_RST_PCIE_SLOT); -+ val |= CPLD_RST_PCIESLOT; -+ iowrite8(val, pm_data->cpld + CPLD_RST_PCIE_SLOT); -+ } -+} -+ -+static void ls1021_pcie_host_init(struct pcie_port *pp) -+{ -+ struct ls_pcie *pcie = to_ls_pcie(pp); -+ u32 index[2]; -+ -+ pcie->scfg = syscon_regmap_lookup_by_phandle(pp->dev->of_node, -+ "fsl,pcie-scfg"); -+ if (IS_ERR(pcie->scfg)) { -+ dev_err(pp->dev, "No syscfg phandle specified\n"); -+ pcie->scfg = NULL; -+ return; -+ } -+ -+ if (of_property_read_u32_array(pp->dev->of_node, -+ "fsl,pcie-scfg", index, 2)) { -+ pcie->scfg = NULL; -+ return; -+ } -+ pcie->index = index[1]; -+ -+ dw_pcie_setup_rc(pp); -+ -+ ls_pcie_drop_msg_tlp(pcie); -+} -+ -+static int ls_pcie_link_up(struct pcie_port *pp) -+{ -+ struct ls_pcie *pcie = to_ls_pcie(pp); -+ u32 state; -+ -+ state = (ioread32(pcie->lut + PCIE_LUT_DBG) >> -+ pcie->drvdata->ltssm_shift) & -+ LTSSM_STATE_MASK; -+ -+ if (state < LTSSM_PCIE_L0) -+ return 0; -+ -+ return 1; -+} -+ -+static u32 ls_pcie_get_link_state(struct ls_pcie *pcie) -+{ -+ return (ioread32(pcie->lut + PCIE_LUT_DBG) >> -+ pcie->drvdata->ltssm_shift) & -+ LTSSM_STATE_MASK; -+} -+ -+static void ls_pcie_host_init(struct pcie_port *pp) -+{ -+ struct ls_pcie *pcie = to_ls_pcie(pp); -+ -+ iowrite32(1, pcie->dbi + PCIE_DBI_RO_WR_EN); -+ ls_pcie_fix_class(pcie); -+ ls_pcie_clear_multifunction(pcie); -+ ls_pcie_drop_msg_tlp(pcie); -+ iowrite32(0, pcie->dbi + PCIE_DBI_RO_WR_EN); -+} -+ -+static int ls_pcie_msi_host_init(struct pcie_port *pp, -+ struct msi_controller *chip) -+{ -+ struct device_node *msi_node; -+ struct device_node *np = pp->dev->of_node; -+ -+ /* -+ * The MSI domain is set by the generic of_msi_configure(). This -+ * .msi_host_init() function keeps us from doing the default MSI -+ * domain setup in dw_pcie_host_init() and also enforces the -+ * requirement that "msi-parent" exists. -+ */ -+ msi_node = of_parse_phandle(np, "msi-parent", 0); -+ if (!msi_node) { -+ dev_err(pp->dev, "failed to find msi-parent\n"); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static struct pcie_host_ops ls1021_pcie_host_ops = { -+ .link_up = ls1021_pcie_link_up, -+ .host_init = ls1021_pcie_host_init, -+ .msi_host_init = ls_pcie_msi_host_init, -+}; -+ -+static struct ls_pcie_pm_ops ls1021_pcie_host_pm_ops = { -+ .get_link_state = &ls1021_pcie_get_link_state, -+ .send_turn_off_message = &ls1021_pcie_send_turn_off_message, -+ .clear_turn_off_message = &ls1021_pcie_clear_turn_off_message, -+ .reset_slot = &ls1021_pcie_reset_slot, -+}; -+ -+static struct pcie_host_ops ls_pcie_host_ops = { -+ .link_up = ls_pcie_link_up, -+ .host_init = ls_pcie_host_init, -+ .msi_host_init = ls_pcie_msi_host_init, -+}; -+ -+static struct ls_pcie_pm_ops ls_pcie_host_pm_ops = { -+ .get_link_state = &ls_pcie_get_link_state, -+}; -+ -+static struct ls_pcie_drvdata ls1021_drvdata = { -+ .ops = &ls1021_pcie_host_ops, -+ .pm = &ls1021_pcie_host_pm_ops, -+}; -+ -+static struct ls_pcie_drvdata ls1043_drvdata = { -+ .lut_offset = 0x10000, -+ .ltssm_shift = 24, -+ .ops = &ls_pcie_host_ops, -+ .pm = &ls_pcie_host_pm_ops, -+}; -+ -+static struct ls_pcie_drvdata ls2080_drvdata = { -+ .lut_offset = 0x80000, -+ .ltssm_shift = 0, -+ .ops = &ls_pcie_host_ops, -+ .pm = &ls_pcie_host_pm_ops, -+}; -+ -+static const struct of_device_id ls_pcie_of_match[] = { -+ { .compatible = "fsl,ls1021a-pcie", .data = &ls1021_drvdata }, -+ { .compatible = "fsl,ls1043a-pcie", .data = &ls1043_drvdata }, -+ { .compatible = "fsl,ls2080a-pcie", .data = &ls2080_drvdata }, -+ { .compatible = "fsl,ls2085a-pcie", .data = &ls2080_drvdata }, -+ { }, -+}; -+MODULE_DEVICE_TABLE(of, ls_pcie_of_match); -+ -+static void ls_pcie_host_hack_pm_init(struct ls_pcie *pcie) -+{ -+ struct device_node *np; -+ struct ls_pcie_pm_data *pm_data = &pcie->pm_data; -+ -+ np = of_find_compatible_node(NULL, NULL, "fsl,ls1021aqds-fpga"); -+ if (np) -+ pm_data->fpga = of_iomap(np, 0); -+ -+ of_node_put(np); -+ -+ np = of_find_compatible_node(NULL, NULL, "fsl,ls1021atwr-cpld"); -+ if (np) -+ pm_data->cpld = of_iomap(np, 0); -+ -+ of_node_put(np); -+} -+ -+static irqreturn_t ls_pcie_pme_irq_handler(int irq, void *data) -+{ -+ struct pcie_port *pp = data; -+ struct ls_pcie *pcie = to_ls_pcie(pp); -+ u32 val; -+ -+ if (pcie->drvdata->pm->clear_turn_off_message) -+ pcie->drvdata->pm->clear_turn_off_message(pcie); -+ -+ /* Clear Host root PME_STATE bit */ -+ val = ioread32(pcie->dbi + PCIE_PEX_RSR); -+ val |= PCIE_PEX_PMES; -+ iowrite32(val, pcie->dbi + PCIE_PEX_RSR); -+ -+ return IRQ_HANDLED; -+} -+ -+static int ls_pcie_host_pme_init(struct ls_pcie *pcie, -+ struct platform_device *pdev) -+{ -+ struct pcie_port *pp; -+ int ret; -+ u16 val; -+ -+ pp = &pcie->pp; -+ -+ pcie->pme_irq = platform_get_irq_byname(pdev, "pme"); -+ if (pcie->pme_irq < 0) { -+ dev_err(&pdev->dev, -+ "failed to get PME IRQ: %d\n", pcie->pme_irq); -+ return pcie->pme_irq; -+ } -+ -+ ret = devm_request_irq(pp->dev, pcie->pme_irq, ls_pcie_pme_irq_handler, -+ IRQF_SHARED, "ls-pcie-pme", pp); -+ if (ret) { -+ dev_err(pp->dev, "Failed to request pme irq\n"); -+ return ret; -+ } -+ -+ ls_pcie_host_hack_pm_init(pcie); -+ -+ /* AUX Power PM Enable */ -+ val = ioread16(pcie->dbi + PCIE_PEX_DCR); -+ val |= PCIE_PEX_DCR_AUXPOWEREN; -+ iowrite16(val, pcie->dbi + PCIE_PEX_DCR); -+ -+ /* Enable PME message */ -+ val = ioread16(pcie->dbi + PCIE_PM_SCR); -+ val |= PCIE_PM_SCR_PMEEN; -+ iowrite16(val, pcie->dbi + PCIE_PM_SCR); -+ -+ /* Clear Host PME_STATE bit */ -+ val = ioread16(pcie->dbi + PCIE_PM_SCR); -+ val |= PCIE_PM_SCR_PME_STATE; -+ iowrite16(val, pcie->dbi + PCIE_PM_SCR); -+ -+ /* Enable Host %d interrupt */ -+ val = ioread16(pcie->dbi + PCIE_PEX_RCR); -+ val |= PCIE_PEX_RCR_PMEIE; -+ iowrite16(val, pcie->dbi + PCIE_PEX_RCR); -+ -+ if (dw_pcie_link_up(&pcie->pp)) -+ pcie->in_slot = true; -+ else -+ pcie->in_slot = false; -+ -+ return 0; -+} -+ -+static int __init ls_add_pcie_port(struct pcie_port *pp, -+ struct platform_device *pdev) -+{ -+ int ret; -+ struct ls_pcie *pcie = to_ls_pcie(pp); -+ -+ pp->dev = &pdev->dev; -+ pp->dbi_base = pcie->dbi; -+ pp->ops = pcie->drvdata->ops; -+ -+ ret = dw_pcie_host_init(pp); -+ if (ret) { -+ dev_err(pp->dev, "failed to initialize host\n"); -+ return ret; -+ } -+ -+ ret = ls_pcie_host_pme_init(pcie, pdev); -+ if (ret) -+ dev_warn(pp->dev, "failed to initialize PME\n"); -+ -+ return 0; -+} -+ -+static int ls_pcie_probe(struct platform_device *pdev) -+{ -+ const struct of_device_id *match; -+ struct ls_pcie *pcie; -+ struct resource *dbi_base; -+ int ret; -+ -+ match = of_match_device(ls_pcie_of_match, &pdev->dev); -+ if (!match) -+ return -ENODEV; -+ -+ pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL); -+ if (!pcie) -+ return -ENOMEM; -+ -+ dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); -+ pcie->dbi = devm_ioremap_resource(&pdev->dev, dbi_base); -+ if (IS_ERR(pcie->dbi)) { -+ dev_err(&pdev->dev, "missing *regs* space\n"); -+ return PTR_ERR(pcie->dbi); -+ } -+ -+ pcie->drvdata = match->data; -+ pcie->lut = pcie->dbi + pcie->drvdata->lut_offset; -+ /* Disable LDR zero */ -+ iowrite32(0, pcie->lut + PCIE_LUT_LDR(0)); -+ -+ if (!ls_pcie_is_bridge(pcie)) -+ return -ENODEV; -+ -+ if (of_device_is_compatible(pdev->dev.of_node, "fsl,ls2085a-pcie") || -+ of_device_is_compatible(pdev->dev.of_node, "fsl,ls2080a-pcie")) { -+ int len; -+ const u32 *prop; -+ struct device_node *np; -+ -+ np = pdev->dev.of_node; -+ prop = (u32 *)of_get_property(np, "available-stream-ids", &len); -+ if (prop) { -+ pcie->avail_streamids = prop; -+ pcie->streamid_index = len/sizeof(u32); -+ } else -+ dev_err(&pdev->dev, "PCIe endpoint partitioning not possible\n"); -+ } -+ -+ ret = ls_add_pcie_port(&pcie->pp, pdev); -+ if (ret < 0) -+ return ret; -+ -+ list_add_tail(&pcie->list_node, &hose_list); -+ -+ platform_set_drvdata(pdev, pcie); -+ -+ return 0; -+} -+ -+#ifdef CONFIG_PM_SLEEP -+static int ls_pcie_pm_do_suspend(struct ls_pcie *pcie) -+{ -+ u32 state; -+ int i = 0; -+ int ret; -+ u16 val; -+ -+ if (!pcie->in_slot) -+ return 0; -+ -+ if (!pcie->drvdata->pm->send_turn_off_message) -+ return 0; -+ -+ ret = pcie->drvdata->pm->send_turn_off_message(pcie); -+ if (ret) -+ return -EINVAL; -+ -+ while (i < 100) { -+ state = pcie->drvdata->pm->get_link_state(pcie); -+ if (state == LTSSM_PCIE_L2_IDLE) -+ break; -+ i++; -+ mdelay(1); -+ } -+ -+ /* Put RC in D3 */ -+ val = ioread16(pcie->dbi + PCIE_PM_SCR); -+ val |= PCIE_PM_SCR_PMEPS_D3; -+ iowrite16(val, pcie->dbi + PCIE_PM_SCR); -+ -+ mdelay(10); -+ -+ return 0; -+} -+ -+static int ls_pcie_pm_do_resume(struct ls_pcie *pcie) -+{ -+ u32 state; -+ int i = 0; -+ u16 val; -+ -+ ls_pcie_host_init(&pcie->pp); -+ -+ if (!pcie->in_slot) -+ return 0; -+ -+ /* Put RC in D0 */ -+ val = ioread16(pcie->dbi + PCIE_PM_SCR); -+ val &= PCIE_PM_SCR_PMEPS_D0; -+ iowrite16(val, pcie->dbi + PCIE_PM_SCR); -+ -+ mdelay(10); -+ -+ state = pcie->drvdata->pm->get_link_state(pcie); -+ if (state == LTSSM_PCIE_L0) -+ return 0; -+ -+ if (!pcie->drvdata->pm->reset_slot) -+ return -EINVAL; -+ -+ pcie->drvdata->pm->reset_slot(pcie, &pcie->pm_data); -+ -+ while (i < 100) { -+ state = pcie->drvdata->pm->get_link_state(pcie); -+ if (state == LTSSM_PCIE_L0) -+ return 0; -+ i++; -+ mdelay(1); -+ } -+ -+ return -EINVAL; -+} -+ -+static int ls_pcie_pm_suspend(void) -+{ -+ struct ls_pcie *hose, *tmp; -+ -+ list_for_each_entry_safe(hose, tmp, &hose_list, list_node) -+ ls_pcie_pm_do_suspend(hose); -+ -+ return 0; -+} -+ -+static void ls_pcie_pm_resume(void) -+{ -+ struct ls_pcie *hose, *tmp; -+ -+ list_for_each_entry_safe(hose, tmp, &hose_list, list_node) -+ ls_pcie_pm_do_resume(hose); -+} -+ -+static struct syscore_ops ls_pcie_syscore_pm_ops = { -+ .suspend = ls_pcie_pm_suspend, -+ .resume = ls_pcie_pm_resume, -+}; -+#endif /* CONFIG_PM_SLEEP */ -+ -+static struct platform_driver ls_pcie_driver = { -+ .probe = ls_pcie_probe, -+ .driver = { -+ .name = "layerscape-pcie", -+ .of_match_table = ls_pcie_of_match, -+ }, -+}; -+ -+static int __init fsl_pci_init(void) -+{ -+#ifdef CONFIG_PM_SLEEP -+ register_syscore_ops(&ls_pcie_syscore_pm_ops); -+#endif -+ return platform_driver_register(&ls_pcie_driver); -+} -+module_init(fsl_pci_init); -+ -+MODULE_AUTHOR("Minghuan Lian "); -+MODULE_DESCRIPTION("Freescale Layerscape PCIe host controller driver"); -+MODULE_LICENSE("GPL v2"); -diff --git a/drivers/pci/host/pci-layerscape.h b/drivers/pci/host/pci-layerscape.h -new file mode 100644 -index 0000000..e90e114 ---- /dev/null -+++ b/drivers/pci/host/pci-layerscape.h -@@ -0,0 +1,13 @@ -+/* -+ * 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 _PCI_LAYERSCAPE_H -+#define _PCI_LAYERSCAPE_H -+ -+/* function for setting up stream id to device id translation */ -+u32 set_pcie_streamid_translation(struct pci_dev *pdev, u32 devid); -+ -+#endif /* _PCI_LAYERSCAPE_H */ -diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c -index b1315e1..94b42d1 100644 ---- a/drivers/pci/host/pci-mvebu.c -+++ b/drivers/pci/host/pci-mvebu.c -@@ -99,11 +99,9 @@ struct mvebu_pcie_port; - struct mvebu_pcie { - struct platform_device *pdev; - struct mvebu_pcie_port *ports; -- struct msi_chip *msi; -+ struct msi_controller *msi; - struct resource io; -- char io_name[30]; - struct resource realio; -- char mem_name[30]; - struct resource mem; - struct resource busn; - int nports; -@@ -722,18 +720,9 @@ static int mvebu_pcie_setup(int nr, struct pci_sys_data *sys) - { - struct mvebu_pcie *pcie = sys_to_pcie(sys); - int i; -- int domain = 0; - --#ifdef CONFIG_PCI_DOMAINS -- domain = sys->domain; --#endif -- -- snprintf(pcie->mem_name, sizeof(pcie->mem_name), "PCI MEM %04x", -- domain); -- pcie->mem.name = pcie->mem_name; -- -- snprintf(pcie->io_name, sizeof(pcie->io_name), "PCI I/O %04x", domain); -- pcie->realio.name = pcie->io_name; -+ pcie->mem.name = "PCI MEM"; -+ pcie->realio.name = "PCI I/O"; - - if (request_resource(&iomem_resource, &pcie->mem)) - return 0; -diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c -index 19bb19c..971d8d7 100644 ---- a/drivers/pci/host/pci-tegra.c -+++ b/drivers/pci/host/pci-tegra.c -@@ -238,7 +238,7 @@ - ) - - struct tegra_msi { -- struct msi_chip chip; -+ struct msi_controller chip; - DECLARE_BITMAP(used, INT_PCI_MSI_NR); - struct irq_domain *domain; - unsigned long pages; -@@ -259,7 +259,7 @@ struct tegra_pcie_soc_data { - bool has_gen2; - }; - --static inline struct tegra_msi *to_tegra_msi(struct msi_chip *chip) -+static inline struct tegra_msi *to_tegra_msi(struct msi_controller *chip) - { - return container_of(chip, struct tegra_msi, chip); - } -@@ -1280,8 +1280,8 @@ static irqreturn_t tegra_pcie_msi_irq(int irq, void *data) - return processed > 0 ? IRQ_HANDLED : IRQ_NONE; - } - --static int tegra_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, -- struct msi_desc *desc) -+static int tegra_msi_setup_irq(struct msi_controller *chip, -+ struct pci_dev *pdev, struct msi_desc *desc) - { - struct tegra_msi *msi = to_tegra_msi(chip); - struct msi_msg msg; -@@ -1305,12 +1305,13 @@ static int tegra_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, - msg.address_hi = 0; - msg.data = hwirq; - -- write_msi_msg(irq, &msg); -+ pci_write_msi_msg(irq, &msg); - - return 0; - } - --static void tegra_msi_teardown_irq(struct msi_chip *chip, unsigned int irq) -+static void tegra_msi_teardown_irq(struct msi_controller *chip, -+ unsigned int irq) - { - struct tegra_msi *msi = to_tegra_msi(chip); - struct irq_data *d = irq_get_irq_data(irq); -@@ -1322,10 +1323,10 @@ static void tegra_msi_teardown_irq(struct msi_chip *chip, unsigned int irq) - - static struct irq_chip tegra_msi_irq_chip = { - .name = "Tegra PCIe MSI", -- .irq_enable = unmask_msi_irq, -- .irq_disable = mask_msi_irq, -- .irq_mask = mask_msi_irq, -- .irq_unmask = unmask_msi_irq, -+ .irq_enable = pci_msi_unmask_irq, -+ .irq_disable = pci_msi_mask_irq, -+ .irq_mask = pci_msi_mask_irq, -+ .irq_unmask = pci_msi_unmask_irq, - }; - - static int tegra_msi_map(struct irq_domain *domain, unsigned int irq, -@@ -1333,7 +1334,6 @@ static int tegra_msi_map(struct irq_domain *domain, unsigned int irq, - { - irq_set_chip_and_handler(irq, &tegra_msi_irq_chip, handle_simple_irq); - irq_set_chip_data(irq, domain->host_data); -- set_irq_flags(irq, IRQF_VALID); - - tegra_cpuidle_pcie_irqs_in_use(); - -diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c -new file mode 100644 -index 0000000..8e559d1 ---- /dev/null -+++ b/drivers/pci/host/pci-xgene-msi.c -@@ -0,0 +1,595 @@ -+/* -+ * APM X-Gene MSI Driver -+ * -+ * Copyright (c) 2014, Applied Micro Circuits Corporation -+ * Author: Tanmay Inamdar -+ * Duc Dang -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define MSI_IR0 0x000000 -+#define MSI_INT0 0x800000 -+#define IDX_PER_GROUP 8 -+#define IRQS_PER_IDX 16 -+#define NR_HW_IRQS 16 -+#define NR_MSI_VEC (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS) -+ -+struct xgene_msi_group { -+ struct xgene_msi *msi; -+ int gic_irq; -+ u32 msi_grp; -+}; -+ -+struct xgene_msi { -+ struct device_node *node; -+ struct msi_controller mchip; -+ struct irq_domain *domain; -+ u64 msi_addr; -+ void __iomem *msi_regs; -+ unsigned long *bitmap; -+ struct mutex bitmap_lock; -+ struct xgene_msi_group *msi_groups; -+ int num_cpus; -+}; -+ -+/* Global data */ -+static struct xgene_msi xgene_msi_ctrl; -+ -+static struct irq_chip xgene_msi_top_irq_chip = { -+ .name = "X-Gene1 MSI", -+ .irq_enable = pci_msi_unmask_irq, -+ .irq_disable = pci_msi_mask_irq, -+ .irq_mask = pci_msi_mask_irq, -+ .irq_unmask = pci_msi_unmask_irq, -+}; -+ -+static struct msi_domain_info xgene_msi_domain_info = { -+ .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | -+ MSI_FLAG_PCI_MSIX), -+ .chip = &xgene_msi_top_irq_chip, -+}; -+ -+/* -+ * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where -+ * n is group number (0..F), x is index of registers in each group (0..7) -+ * The register layout is as follows: -+ * MSI0IR0 base_addr -+ * MSI0IR1 base_addr + 0x10000 -+ * ... ... -+ * MSI0IR6 base_addr + 0x60000 -+ * MSI0IR7 base_addr + 0x70000 -+ * MSI1IR0 base_addr + 0x80000 -+ * MSI1IR1 base_addr + 0x90000 -+ * ... ... -+ * MSI1IR7 base_addr + 0xF0000 -+ * MSI2IR0 base_addr + 0x100000 -+ * ... ... -+ * MSIFIR0 base_addr + 0x780000 -+ * MSIFIR1 base_addr + 0x790000 -+ * ... ... -+ * MSIFIR7 base_addr + 0x7F0000 -+ * MSIINT0 base_addr + 0x800000 -+ * MSIINT1 base_addr + 0x810000 -+ * ... ... -+ * MSIINTF base_addr + 0x8F0000 -+ * -+ * Each index register supports 16 MSI vectors (0..15) to generate interrupt. -+ * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination -+ * registers. -+ * -+ * Each MSI termination group has 1 MSIINTn register (n is 0..15) to indicate -+ * the MSI pending status caused by 1 of its 8 index registers. -+ */ -+ -+/* MSInIRx read helper */ -+static u32 xgene_msi_ir_read(struct xgene_msi *msi, -+ u32 msi_grp, u32 msir_idx) -+{ -+ return readl_relaxed(msi->msi_regs + MSI_IR0 + -+ (msi_grp << 19) + (msir_idx << 16)); -+} -+ -+/* MSIINTn read helper */ -+static u32 xgene_msi_int_read(struct xgene_msi *msi, u32 msi_grp) -+{ -+ return readl_relaxed(msi->msi_regs + MSI_INT0 + (msi_grp << 16)); -+} -+ -+/* -+ * With 2048 MSI vectors supported, the MSI message can be constructed using -+ * following scheme: -+ * - Divide into 8 256-vector groups -+ * Group 0: 0-255 -+ * Group 1: 256-511 -+ * Group 2: 512-767 -+ * ... -+ * Group 7: 1792-2047 -+ * - Each 256-vector group is divided into 16 16-vector groups -+ * As an example: 16 16-vector groups for 256-vector group 0-255 is -+ * Group 0: 0-15 -+ * Group 1: 16-32 -+ * ... -+ * Group 15: 240-255 -+ * - The termination address of MSI vector in 256-vector group n and 16-vector -+ * group x is the address of MSIxIRn -+ * - The data for MSI vector in 16-vector group x is x -+ */ -+static u32 hwirq_to_reg_set(unsigned long hwirq) -+{ -+ return (hwirq / (NR_HW_IRQS * IRQS_PER_IDX)); -+} -+ -+static u32 hwirq_to_group(unsigned long hwirq) -+{ -+ return (hwirq % NR_HW_IRQS); -+} -+ -+static u32 hwirq_to_msi_data(unsigned long hwirq) -+{ -+ return ((hwirq / NR_HW_IRQS) % IRQS_PER_IDX); -+} -+ -+static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) -+{ -+ struct xgene_msi *msi = irq_data_get_irq_chip_data(data); -+ u32 reg_set = hwirq_to_reg_set(data->hwirq); -+ u32 group = hwirq_to_group(data->hwirq); -+ u64 target_addr = msi->msi_addr + (((8 * group) + reg_set) << 16); -+ -+ msg->address_hi = upper_32_bits(target_addr); -+ msg->address_lo = lower_32_bits(target_addr); -+ msg->data = hwirq_to_msi_data(data->hwirq); -+} -+ -+/* -+ * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors. To maintain -+ * the expected behaviour of .set_affinity for each MSI interrupt, the 16 -+ * MSI GIC IRQs are statically allocated to 8 X-Gene v1 cores (2 GIC IRQs -+ * for each core). The MSI vector is moved fom 1 MSI GIC IRQ to another -+ * MSI GIC IRQ to steer its MSI interrupt to correct X-Gene v1 core. As a -+ * consequence, the total MSI vectors that X-Gene v1 supports will be -+ * reduced to 256 (2048/8) vectors. -+ */ -+static int hwirq_to_cpu(unsigned long hwirq) -+{ -+ return (hwirq % xgene_msi_ctrl.num_cpus); -+} -+ -+static unsigned long hwirq_to_canonical_hwirq(unsigned long hwirq) -+{ -+ return (hwirq - hwirq_to_cpu(hwirq)); -+} -+ -+static int xgene_msi_set_affinity(struct irq_data *irqdata, -+ const struct cpumask *mask, bool force) -+{ -+ int target_cpu = cpumask_first(mask); -+ int curr_cpu; -+ -+ curr_cpu = hwirq_to_cpu(irqdata->hwirq); -+ if (curr_cpu == target_cpu) -+ return IRQ_SET_MASK_OK_DONE; -+ -+ /* Update MSI number to target the new CPU */ -+ irqdata->hwirq = hwirq_to_canonical_hwirq(irqdata->hwirq) + target_cpu; -+ -+ return IRQ_SET_MASK_OK; -+} -+ -+static struct irq_chip xgene_msi_bottom_irq_chip = { -+ .name = "MSI", -+ .irq_set_affinity = xgene_msi_set_affinity, -+ .irq_compose_msi_msg = xgene_compose_msi_msg, -+}; -+ -+static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, -+ unsigned int nr_irqs, void *args) -+{ -+ struct xgene_msi *msi = domain->host_data; -+ int msi_irq; -+ -+ mutex_lock(&msi->bitmap_lock); -+ -+ msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0, -+ msi->num_cpus, 0); -+ if (msi_irq < NR_MSI_VEC) -+ bitmap_set(msi->bitmap, msi_irq, msi->num_cpus); -+ else -+ msi_irq = -ENOSPC; -+ -+ mutex_unlock(&msi->bitmap_lock); -+ -+ if (msi_irq < 0) -+ return msi_irq; -+ -+ irq_domain_set_info(domain, virq, msi_irq, -+ &xgene_msi_bottom_irq_chip, domain->host_data, -+ handle_simple_irq, NULL, NULL); -+ -+ return 0; -+} -+ -+static void xgene_irq_domain_free(struct irq_domain *domain, -+ unsigned int virq, unsigned int nr_irqs) -+{ -+ struct irq_data *d = irq_domain_get_irq_data(domain, virq); -+ struct xgene_msi *msi = irq_data_get_irq_chip_data(d); -+ u32 hwirq; -+ -+ mutex_lock(&msi->bitmap_lock); -+ -+ hwirq = hwirq_to_canonical_hwirq(d->hwirq); -+ bitmap_clear(msi->bitmap, hwirq, msi->num_cpus); -+ -+ mutex_unlock(&msi->bitmap_lock); -+ -+ irq_domain_free_irqs_parent(domain, virq, nr_irqs); -+} -+ -+static const struct irq_domain_ops msi_domain_ops = { -+ .alloc = xgene_irq_domain_alloc, -+ .free = xgene_irq_domain_free, -+}; -+ -+static int xgene_allocate_domains(struct xgene_msi *msi) -+{ -+ msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC, -+ &msi_domain_ops, msi); -+ if (!msi->domain) -+ return -ENOMEM; -+ -+ msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node, -+ &xgene_msi_domain_info, -+ msi->domain); -+ -+ if (!msi->mchip.domain) { -+ irq_domain_remove(msi->domain); -+ return -ENOMEM; -+ } -+ -+ return 0; -+} -+ -+static void xgene_free_domains(struct xgene_msi *msi) -+{ -+ if (msi->mchip.domain) -+ irq_domain_remove(msi->mchip.domain); -+ if (msi->domain) -+ irq_domain_remove(msi->domain); -+} -+ -+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi) -+{ -+ int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long); -+ -+ xgene_msi->bitmap = kzalloc(size, GFP_KERNEL); -+ if (!xgene_msi->bitmap) -+ return -ENOMEM; -+ -+ mutex_init(&xgene_msi->bitmap_lock); -+ -+ xgene_msi->msi_groups = kcalloc(NR_HW_IRQS, -+ sizeof(struct xgene_msi_group), -+ GFP_KERNEL); -+ if (!xgene_msi->msi_groups) -+ return -ENOMEM; -+ -+ return 0; -+} -+ -+static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc) -+{ -+ struct irq_chip *chip = irq_desc_get_chip(desc); -+ struct xgene_msi_group *msi_groups; -+ struct xgene_msi *xgene_msi; -+ unsigned int virq; -+ int msir_index, msir_val, hw_irq; -+ u32 intr_index, grp_select, msi_grp; -+ -+ chained_irq_enter(chip, desc); -+ -+ msi_groups = irq_desc_get_handler_data(desc); -+ xgene_msi = msi_groups->msi; -+ msi_grp = msi_groups->msi_grp; -+ -+ /* -+ * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt -+ * If bit x of this register is set (x is 0..7), one or more interupts -+ * corresponding to MSInIRx is set. -+ */ -+ grp_select = xgene_msi_int_read(xgene_msi, msi_grp); -+ while (grp_select) { -+ msir_index = ffs(grp_select) - 1; -+ /* -+ * Calculate MSInIRx address to read to check for interrupts -+ * (refer to termination address and data assignment -+ * described in xgene_compose_msi_msg() ) -+ */ -+ msir_val = xgene_msi_ir_read(xgene_msi, msi_grp, msir_index); -+ while (msir_val) { -+ intr_index = ffs(msir_val) - 1; -+ /* -+ * Calculate MSI vector number (refer to the termination -+ * address and data assignment described in -+ * xgene_compose_msi_msg function) -+ */ -+ hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) * -+ NR_HW_IRQS) + msi_grp; -+ /* -+ * As we have multiple hw_irq that maps to single MSI, -+ * always look up the virq using the hw_irq as seen from -+ * CPU0 -+ */ -+ hw_irq = hwirq_to_canonical_hwirq(hw_irq); -+ virq = irq_find_mapping(xgene_msi->domain, hw_irq); -+ WARN_ON(!virq); -+ if (virq != 0) -+ generic_handle_irq(virq); -+ msir_val &= ~(1 << intr_index); -+ } -+ grp_select &= ~(1 << msir_index); -+ -+ if (!grp_select) { -+ /* -+ * We handled all interrupts happened in this group, -+ * resample this group MSI_INTx register in case -+ * something else has been made pending in the meantime -+ */ -+ grp_select = xgene_msi_int_read(xgene_msi, msi_grp); -+ } -+ } -+ -+ chained_irq_exit(chip, desc); -+} -+ -+static int xgene_msi_remove(struct platform_device *pdev) -+{ -+ int virq, i; -+ struct xgene_msi *msi = platform_get_drvdata(pdev); -+ -+ for (i = 0; i < NR_HW_IRQS; i++) { -+ virq = msi->msi_groups[i].gic_irq; -+ if (virq != 0) { -+ irq_set_chained_handler(virq, NULL); -+ irq_set_handler_data(virq, NULL); -+ } -+ } -+ kfree(msi->msi_groups); -+ -+ kfree(msi->bitmap); -+ msi->bitmap = NULL; -+ -+ xgene_free_domains(msi); -+ -+ return 0; -+} -+ -+static int xgene_msi_hwirq_alloc(unsigned int cpu) -+{ -+ struct xgene_msi *msi = &xgene_msi_ctrl; -+ struct xgene_msi_group *msi_group; -+ cpumask_var_t mask; -+ int i; -+ int err; -+ -+ for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) { -+ msi_group = &msi->msi_groups[i]; -+ if (!msi_group->gic_irq) -+ continue; -+ -+ irq_set_chained_handler(msi_group->gic_irq, -+ xgene_msi_isr); -+ err = irq_set_handler_data(msi_group->gic_irq, msi_group); -+ if (err) { -+ pr_err("failed to register GIC IRQ handler\n"); -+ return -EINVAL; -+ } -+ /* -+ * Statically allocate MSI GIC IRQs to each CPU core. -+ * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated -+ * to each core. -+ */ -+ if (alloc_cpumask_var(&mask, GFP_KERNEL)) { -+ cpumask_clear(mask); -+ cpumask_set_cpu(cpu, mask); -+ err = irq_set_affinity(msi_group->gic_irq, mask); -+ if (err) -+ pr_err("failed to set affinity for GIC IRQ"); -+ free_cpumask_var(mask); -+ } else { -+ pr_err("failed to alloc CPU mask for affinity\n"); -+ err = -EINVAL; -+ } -+ -+ if (err) { -+ irq_set_chained_handler(msi_group->gic_irq, NULL); -+ irq_set_handler_data(msi_group->gic_irq, NULL); -+ return err; -+ } -+ } -+ -+ return 0; -+} -+ -+static void xgene_msi_hwirq_free(unsigned int cpu) -+{ -+ struct xgene_msi *msi = &xgene_msi_ctrl; -+ struct xgene_msi_group *msi_group; -+ int i; -+ -+ for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) { -+ msi_group = &msi->msi_groups[i]; -+ if (!msi_group->gic_irq) -+ continue; -+ -+ irq_set_chained_handler(msi_group->gic_irq, NULL); -+ irq_set_handler_data(msi_group->gic_irq, NULL); -+ } -+} -+ -+static int xgene_msi_cpu_callback(struct notifier_block *nfb, -+ unsigned long action, void *hcpu) -+{ -+ unsigned cpu = (unsigned long)hcpu; -+ -+ switch (action) { -+ case CPU_ONLINE: -+ case CPU_ONLINE_FROZEN: -+ xgene_msi_hwirq_alloc(cpu); -+ break; -+ case CPU_DEAD: -+ case CPU_DEAD_FROZEN: -+ xgene_msi_hwirq_free(cpu); -+ break; -+ default: -+ break; -+ } -+ -+ return NOTIFY_OK; -+} -+ -+static struct notifier_block xgene_msi_cpu_notifier = { -+ .notifier_call = xgene_msi_cpu_callback, -+}; -+ -+static const struct of_device_id xgene_msi_match_table[] = { -+ {.compatible = "apm,xgene1-msi"}, -+ {}, -+}; -+ -+static int xgene_msi_probe(struct platform_device *pdev) -+{ -+ struct resource *res; -+ int rc, irq_index; -+ struct xgene_msi *xgene_msi; -+ unsigned int cpu; -+ int virt_msir; -+ u32 msi_val, msi_idx; -+ -+ xgene_msi = &xgene_msi_ctrl; -+ -+ platform_set_drvdata(pdev, xgene_msi); -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res); -+ if (IS_ERR(xgene_msi->msi_regs)) { -+ dev_err(&pdev->dev, "no reg space\n"); -+ rc = -EINVAL; -+ goto error; -+ } -+ xgene_msi->msi_addr = res->start; -+ -+ xgene_msi->num_cpus = num_possible_cpus(); -+ -+ rc = xgene_msi_init_allocator(xgene_msi); -+ if (rc) { -+ dev_err(&pdev->dev, "Error allocating MSI bitmap\n"); -+ goto error; -+ } -+ -+ rc = xgene_allocate_domains(xgene_msi); -+ if (rc) { -+ dev_err(&pdev->dev, "Failed to allocate MSI domain\n"); -+ goto error; -+ } -+ -+ for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) { -+ virt_msir = platform_get_irq(pdev, irq_index); -+ if (virt_msir < 0) { -+ dev_err(&pdev->dev, "Cannot translate IRQ index %d\n", -+ irq_index); -+ rc = -EINVAL; -+ goto error; -+ } -+ xgene_msi->msi_groups[irq_index].gic_irq = virt_msir; -+ xgene_msi->msi_groups[irq_index].msi_grp = irq_index; -+ xgene_msi->msi_groups[irq_index].msi = xgene_msi; -+ } -+ -+ /* -+ * MSInIRx registers are read-to-clear; before registering -+ * interrupt handlers, read all of them to clear spurious -+ * interrupts that may occur before the driver is probed. -+ */ -+ for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) { -+ for (msi_idx = 0; msi_idx < IDX_PER_GROUP; msi_idx++) -+ msi_val = xgene_msi_ir_read(xgene_msi, irq_index, -+ msi_idx); -+ /* Read MSIINTn to confirm */ -+ msi_val = xgene_msi_int_read(xgene_msi, irq_index); -+ if (msi_val) { -+ dev_err(&pdev->dev, "Failed to clear spurious IRQ\n"); -+ rc = -EINVAL; -+ goto error; -+ } -+ } -+ -+ cpu_notifier_register_begin(); -+ -+ for_each_online_cpu(cpu) -+ if (xgene_msi_hwirq_alloc(cpu)) { -+ dev_err(&pdev->dev, "failed to register MSI handlers\n"); -+ cpu_notifier_register_done(); -+ goto error; -+ } -+ -+ rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier); -+ if (rc) { -+ dev_err(&pdev->dev, "failed to add CPU MSI notifier\n"); -+ cpu_notifier_register_done(); -+ goto error; -+ } -+ -+ cpu_notifier_register_done(); -+ -+ xgene_msi->mchip.of_node = pdev->dev.of_node; -+ rc = of_pci_msi_chip_add(&xgene_msi->mchip); -+ if (rc) { -+ dev_err(&pdev->dev, "failed to add MSI controller chip\n"); -+ goto error_notifier; -+ } -+ -+ dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n"); -+ -+ return 0; -+ -+error_notifier: -+ unregister_hotcpu_notifier(&xgene_msi_cpu_notifier); -+error: -+ xgene_msi_remove(pdev); -+ return rc; -+} -+ -+static struct platform_driver xgene_msi_driver = { -+ .driver = { -+ .name = "xgene-msi", -+ .owner = THIS_MODULE, -+ .of_match_table = xgene_msi_match_table, -+ }, -+ .probe = xgene_msi_probe, -+ .remove = xgene_msi_remove, -+}; -+ -+static int __init xgene_pcie_msi_init(void) -+{ -+ return platform_driver_register(&xgene_msi_driver); -+} -+subsys_initcall(xgene_pcie_msi_init); -diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c -index 2988fe1..0dac1fb 100644 ---- a/drivers/pci/host/pci-xgene.c -+++ b/drivers/pci/host/pci-xgene.c -@@ -401,11 +401,11 @@ static int xgene_pcie_map_ranges(struct xgene_pcie_port *port, - struct list_head *res, - resource_size_t io_base) - { -- struct pci_host_bridge_window *window; -+ struct resource_entry *window; - struct device *dev = port->dev; - int ret; - -- list_for_each_entry(window, res, list) { -+ resource_list_for_each_entry(window, res) { - struct resource *res = window->res; - u64 restype = resource_type(res); - -@@ -600,6 +600,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port, - return 0; - } - -+static int xgene_pcie_msi_enable(struct pci_bus *bus) -+{ -+ struct device_node *msi_node; -+ -+ msi_node = of_parse_phandle(bus->dev.of_node, -+ "msi-parent", 0); -+ if (!msi_node) -+ return -ENODEV; -+ -+ bus->msi = of_pci_find_msi_chip_by_node(msi_node); -+ if (!bus->msi) -+ return -ENODEV; -+ -+ bus->msi->dev = &bus->dev; -+ return 0; -+} -+ - static int xgene_pcie_probe_bridge(struct platform_device *pdev) - { - struct device_node *dn = pdev->dev.of_node; -@@ -636,6 +653,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev) - if (!bus) - return -ENOMEM; - -+ if (IS_ENABLED(CONFIG_PCI_MSI)) -+ if (xgene_pcie_msi_enable(bus)) -+ dev_info(port->dev, "failed to enable MSI\n"); -+ - pci_scan_child_bus(bus); - pci_assign_unassigned_bus_resources(bus); - pci_bus_add_devices(bus); -diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c -index f69b0d0..8a9241b 100644 ---- a/drivers/pci/host/pcie-designware.c -+++ b/drivers/pci/host/pcie-designware.c -@@ -15,7 +15,6 @@ - #include - #include - #include --#include - #include - #include - #include -@@ -31,6 +30,7 @@ - #define PORT_LINK_MODE_1_LANES (0x1 << 16) - #define PORT_LINK_MODE_2_LANES (0x3 << 16) - #define PORT_LINK_MODE_4_LANES (0x7 << 16) -+#define PORT_LINK_MODE_8_LANES (0xf << 16) - - #define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C - #define PORT_LOGIC_SPEED_CHANGE (0x1 << 17) -@@ -38,12 +38,7 @@ - #define PORT_LOGIC_LINK_WIDTH_1_LANES (0x1 << 8) - #define PORT_LOGIC_LINK_WIDTH_2_LANES (0x2 << 8) - #define PORT_LOGIC_LINK_WIDTH_4_LANES (0x4 << 8) -- --#define PCIE_MSI_ADDR_LO 0x820 --#define PCIE_MSI_ADDR_HI 0x824 --#define PCIE_MSI_INTR0_ENABLE 0x828 --#define PCIE_MSI_INTR0_MASK 0x82C --#define PCIE_MSI_INTR0_STATUS 0x830 -+#define PORT_LOGIC_LINK_WIDTH_8_LANES (0x8 << 8) - - #define PCIE_ATU_VIEWPORT 0x900 - #define PCIE_ATU_REGION_INBOUND (0x1 << 31) -@@ -67,39 +62,40 @@ - #define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16) - #define PCIE_ATU_UPPER_TARGET 0x91C - --static struct hw_pci dw_pci; -- --static unsigned long global_io_offset; -- --static inline struct pcie_port *sys_to_pcie(struct pci_sys_data *sys) --{ -- BUG_ON(!sys->private_data); -- -- return sys->private_data; --} -+static struct pci_ops dw_pcie_ops; - --int dw_pcie_cfg_read(void __iomem *addr, int where, int size, u32 *val) -+int dw_pcie_cfg_read(void __iomem *addr, int size, u32 *val) - { -- *val = readl(addr); -+ if ((uintptr_t)addr & (size - 1)) { -+ *val = 0; -+ return PCIBIOS_BAD_REGISTER_NUMBER; -+ } - -- if (size == 1) -- *val = (*val >> (8 * (where & 3))) & 0xff; -+ if (size == 4) -+ *val = readl(addr); - else if (size == 2) -- *val = (*val >> (8 * (where & 3))) & 0xffff; -- else if (size != 4) -+ *val = readw(addr); -+ else if (size == 1) -+ *val = readb(addr); -+ else { -+ *val = 0; - return PCIBIOS_BAD_REGISTER_NUMBER; -+ } - - return PCIBIOS_SUCCESSFUL; - } - --int dw_pcie_cfg_write(void __iomem *addr, int where, int size, u32 val) -+int dw_pcie_cfg_write(void __iomem *addr, int size, u32 val) - { -+ if ((uintptr_t)addr & (size - 1)) -+ return PCIBIOS_BAD_REGISTER_NUMBER; -+ - if (size == 4) - writel(val, addr); - else if (size == 2) -- writew(val, addr + (where & 2)); -+ writew(val, addr); - else if (size == 1) -- writeb(val, addr + (where & 3)); -+ writeb(val, addr); - else - return PCIBIOS_BAD_REGISTER_NUMBER; - -@@ -130,8 +126,7 @@ static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, - if (pp->ops->rd_own_conf) - ret = pp->ops->rd_own_conf(pp, where, size, val); - else -- ret = dw_pcie_cfg_read(pp->dbi_base + (where & ~0x3), where, -- size, val); -+ ret = dw_pcie_cfg_read(pp->dbi_base + where, size, val); - - return ret; - } -@@ -144,182 +139,26 @@ static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, - if (pp->ops->wr_own_conf) - ret = pp->ops->wr_own_conf(pp, where, size, val); - else -- ret = dw_pcie_cfg_write(pp->dbi_base + (where & ~0x3), where, -- size, val); -+ ret = dw_pcie_cfg_write(pp->dbi_base + where, size, val); - - return ret; - } - --static struct irq_chip dw_msi_irq_chip = { -- .name = "PCI-MSI", -- .irq_enable = unmask_msi_irq, -- .irq_disable = mask_msi_irq, -- .irq_mask = mask_msi_irq, -- .irq_unmask = unmask_msi_irq, --}; -- --/* MSI int handler */ --irqreturn_t dw_handle_msi_irq(struct pcie_port *pp) -+static void dw_pcie_prog_outbound_atu(struct pcie_port *pp, int index, -+ int type, u64 cpu_addr, u64 pci_addr, u32 size) - { -- unsigned long val; -- int i, pos, irq; -- irqreturn_t ret = IRQ_NONE; -- -- for (i = 0; i < MAX_MSI_CTRLS; i++) { -- dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4, -- (u32 *)&val); -- if (val) { -- ret = IRQ_HANDLED; -- pos = 0; -- while ((pos = find_next_bit(&val, 32, pos)) != 32) { -- irq = irq_find_mapping(pp->irq_domain, -- i * 32 + pos); -- dw_pcie_wr_own_conf(pp, -- PCIE_MSI_INTR0_STATUS + i * 12, -- 4, 1 << pos); -- generic_handle_irq(irq); -- pos++; -- } -- } -- } -- -- return ret; --} -- --void dw_pcie_msi_init(struct pcie_port *pp) --{ -- pp->msi_data = __get_free_pages(GFP_KERNEL, 0); -- -- /* program the msi_data */ -- dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_LO, 4, -- virt_to_phys((void *)pp->msi_data)); -- dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4, 0); --} -- --static void dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq) --{ -- unsigned int res, bit, val; -- -- res = (irq / 32) * 12; -- bit = irq % 32; -- dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); -- val &= ~(1 << bit); -- dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); --} -- --static void clear_irq_range(struct pcie_port *pp, unsigned int irq_base, -- unsigned int nvec, unsigned int pos) --{ -- unsigned int i; -- -- for (i = 0; i < nvec; i++) { -- irq_set_msi_desc_off(irq_base, i, NULL); -- /* Disable corresponding interrupt on MSI controller */ -- if (pp->ops->msi_clear_irq) -- pp->ops->msi_clear_irq(pp, pos + i); -- else -- dw_pcie_msi_clear_irq(pp, pos + i); -- } -- -- bitmap_release_region(pp->msi_irq_in_use, pos, order_base_2(nvec)); --} -- --static void dw_pcie_msi_set_irq(struct pcie_port *pp, int irq) --{ -- unsigned int res, bit, val; -- -- res = (irq / 32) * 12; -- bit = irq % 32; -- dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); -- val |= 1 << bit; -- dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); --} -- --static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) --{ -- int irq, pos0, i; -- struct pcie_port *pp = sys_to_pcie(desc->dev->bus->sysdata); -- -- pos0 = bitmap_find_free_region(pp->msi_irq_in_use, MAX_MSI_IRQS, -- order_base_2(no_irqs)); -- if (pos0 < 0) -- goto no_valid_irq; -- -- irq = irq_find_mapping(pp->irq_domain, pos0); -- if (!irq) -- goto no_valid_irq; -- -- /* -- * irq_create_mapping (called from dw_pcie_host_init) pre-allocates -- * descs so there is no need to allocate descs here. We can therefore -- * assume that if irq_find_mapping above returns non-zero, then the -- * descs are also successfully allocated. -- */ -- -- for (i = 0; i < no_irqs; i++) { -- if (irq_set_msi_desc_off(irq, i, desc) != 0) { -- clear_irq_range(pp, irq, i, pos0); -- goto no_valid_irq; -- } -- /*Enable corresponding interrupt in MSI interrupt controller */ -- if (pp->ops->msi_set_irq) -- pp->ops->msi_set_irq(pp, pos0 + i); -- else -- dw_pcie_msi_set_irq(pp, pos0 + i); -- } -- -- *pos = pos0; -- return irq; -- --no_valid_irq: -- *pos = pos0; -- return -ENOSPC; --} -- --static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, -- struct msi_desc *desc) --{ -- int irq, pos; -- struct msi_msg msg; -- struct pcie_port *pp = sys_to_pcie(pdev->bus->sysdata); -- -- if (desc->msi_attrib.is_msix) -- return -EINVAL; -- -- irq = assign_irq(1, desc, &pos); -- if (irq < 0) -- return irq; -- -- if (pp->ops->get_msi_addr) -- msg.address_lo = pp->ops->get_msi_addr(pp); -- else -- msg.address_lo = virt_to_phys((void *)pp->msi_data); -- msg.address_hi = 0x0; -- -- if (pp->ops->get_msi_data) -- msg.data = pp->ops->get_msi_data(pp, pos); -- else -- msg.data = pos; -- -- write_msi_msg(irq, &msg); -- -- return 0; --} -- --static void dw_msi_teardown_irq(struct msi_chip *chip, unsigned int irq) --{ -- struct irq_data *data = irq_get_irq_data(irq); -- struct msi_desc *msi = irq_data_get_msi(data); -- struct pcie_port *pp = sys_to_pcie(msi->dev->bus->sysdata); -- -- clear_irq_range(pp, irq, 1, data->hwirq); -+ dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | index, -+ PCIE_ATU_VIEWPORT); -+ dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr), PCIE_ATU_LOWER_BASE); -+ dw_pcie_writel_rc(pp, upper_32_bits(cpu_addr), PCIE_ATU_UPPER_BASE); -+ dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr + size - 1), -+ PCIE_ATU_LIMIT); -+ dw_pcie_writel_rc(pp, lower_32_bits(pci_addr), PCIE_ATU_LOWER_TARGET); -+ dw_pcie_writel_rc(pp, upper_32_bits(pci_addr), PCIE_ATU_UPPER_TARGET); -+ dw_pcie_writel_rc(pp, type, PCIE_ATU_CR1); -+ dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2); - } - --static struct msi_chip dw_pcie_msi_chip = { -- .setup_irq = dw_msi_setup_irq, -- .teardown_irq = dw_msi_teardown_irq, --}; -- - int dw_pcie_link_up(struct pcie_port *pp) - { - if (pp->ops->link_up) -@@ -328,36 +167,42 @@ int dw_pcie_link_up(struct pcie_port *pp) - return 0; - } - --static int dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq, -- irq_hw_number_t hwirq) -+static int dw_pcie_msi_ctrl_init(struct pcie_port *pp) - { -- irq_set_chip_and_handler(irq, &dw_msi_irq_chip, handle_simple_irq); -- irq_set_chip_data(irq, domain->host_data); -- set_irq_flags(irq, IRQF_VALID); -+ struct device_node *msi_node; -+ -+ if (!IS_ENABLED(CONFIG_PCI_MSI)) { -+ pp->msi = NULL; -+ return 0; -+ } -+ -+ if (pp->msi) -+ return 0; -+ -+ msi_node = of_parse_phandle(pp->dev->of_node, "msi-parent", 0); -+ if (msi_node) { -+ pp->msi = of_pci_find_msi_chip_by_node(msi_node); -+ if (!pp->msi) { -+ dev_err(pp->dev, "Cannot find msi chip of %s\n", -+ msi_node->full_name); -+ return -ENODEV; -+ } else -+ return 0; -+ } - - return 0; - } - --static const struct irq_domain_ops msi_domain_ops = { -- .map = dw_pcie_msi_map, --}; -- - int dw_pcie_host_init(struct pcie_port *pp) - { - struct device_node *np = pp->dev->of_node; - struct platform_device *pdev = to_platform_device(pp->dev); -- struct of_pci_range range; -- struct of_pci_range_parser parser; -+ struct pci_bus *bus, *child; - struct resource *cfg_res; -- u32 val, na, ns; -- const __be32 *addrp; -- int i, index, ret; -- -- /* Find the address cell size and the number of cells in order to get -- * the untranslated address. -- */ -- of_property_read_u32(np, "#address-cells", &na); -- ns = of_n_size_cells(np); -+ u32 val; -+ int ret; -+ LIST_HEAD(res); -+ struct resource_entry *win; - - cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config"); - if (cfg_res) { -@@ -365,87 +210,61 @@ int dw_pcie_host_init(struct pcie_port *pp) - pp->cfg1_size = resource_size(cfg_res)/2; - pp->cfg0_base = cfg_res->start; - pp->cfg1_base = cfg_res->start + pp->cfg0_size; -- -- /* Find the untranslated configuration space address */ -- index = of_property_match_string(np, "reg-names", "config"); -- addrp = of_get_address(np, index, NULL, NULL); -- pp->cfg0_mod_base = of_read_number(addrp, ns); -- pp->cfg1_mod_base = pp->cfg0_mod_base + pp->cfg0_size; -- } else { -+ } else if (!pp->va_cfg0_base) { - dev_err(pp->dev, "missing *config* reg space\n"); - } - -- if (of_pci_range_parser_init(&parser, np)) { -- dev_err(pp->dev, "missing ranges property\n"); -- return -EINVAL; -- } -+ ret = of_pci_get_host_bridge_resources(np, 0, 0xff, &res, &pp->io_base); -+ if (ret) -+ return ret; - - /* Get the I/O and memory ranges from DT */ -- for_each_of_pci_range(&parser, &range) { -- unsigned long restype = range.flags & IORESOURCE_TYPE_BITS; -- if (restype == IORESOURCE_IO) { -- of_pci_range_to_resource(&range, np, &pp->io); -- pp->io.name = "I/O"; -- pp->io.start = max_t(resource_size_t, -- PCIBIOS_MIN_IO, -- range.pci_addr + global_io_offset); -- pp->io.end = min_t(resource_size_t, -- IO_SPACE_LIMIT, -- range.pci_addr + range.size -- + global_io_offset - 1); -- pp->io_size = resource_size(&pp->io); -- pp->io_bus_addr = range.pci_addr; -- pp->io_base = range.cpu_addr; -- -- /* Find the untranslated IO space address */ -- pp->io_mod_base = of_read_number(parser.range - -- parser.np + na, ns); -- } -- if (restype == IORESOURCE_MEM) { -- of_pci_range_to_resource(&range, np, &pp->mem); -- pp->mem.name = "MEM"; -- pp->mem_size = resource_size(&pp->mem); -- pp->mem_bus_addr = range.pci_addr; -- -- /* Find the untranslated MEM space address */ -- pp->mem_mod_base = of_read_number(parser.range - -- parser.np + na, ns); -- } -- if (restype == 0) { -- of_pci_range_to_resource(&range, np, &pp->cfg); -- pp->cfg0_size = resource_size(&pp->cfg)/2; -- pp->cfg1_size = resource_size(&pp->cfg)/2; -- pp->cfg0_base = pp->cfg.start; -- pp->cfg1_base = pp->cfg.start + pp->cfg0_size; -- -- /* Find the untranslated configuration space address */ -- pp->cfg0_mod_base = of_read_number(parser.range - -- parser.np + na, ns); -- pp->cfg1_mod_base = pp->cfg0_mod_base + -- pp->cfg0_size; -+ resource_list_for_each_entry(win, &res) { -+ switch (resource_type(win->res)) { -+ case IORESOURCE_IO: -+ pp->io = win->res; -+ pp->io->name = "I/O"; -+ pp->io_size = resource_size(pp->io); -+ pp->io_bus_addr = pp->io->start - win->offset; -+ ret = pci_remap_iospace(pp->io, pp->io_base); -+ if (ret) { -+ dev_warn(pp->dev, "error %d: failed to map resource %pR\n", -+ ret, pp->io); -+ continue; -+ } -+ pp->io_base = pp->io->start; -+ break; -+ case IORESOURCE_MEM: -+ pp->mem = win->res; -+ pp->mem->name = "MEM"; -+ pp->mem_size = resource_size(pp->mem); -+ pp->mem_bus_addr = pp->mem->start - win->offset; -+ break; -+ case 0: -+ pp->cfg = win->res; -+ pp->cfg0_size = resource_size(pp->cfg)/2; -+ pp->cfg1_size = resource_size(pp->cfg)/2; -+ pp->cfg0_base = pp->cfg->start; -+ pp->cfg1_base = pp->cfg->start + pp->cfg0_size; -+ break; -+ case IORESOURCE_BUS: -+ pp->busn = win->res; -+ break; -+ default: -+ continue; - } - } - -- ret = of_pci_parse_bus_range(np, &pp->busn); -- if (ret < 0) { -- pp->busn.name = np->name; -- pp->busn.start = 0; -- pp->busn.end = 0xff; -- pp->busn.flags = IORESOURCE_BUS; -- dev_dbg(pp->dev, "failed to parse bus-range property: %d, using default %pR\n", -- ret, &pp->busn); -- } -- - if (!pp->dbi_base) { -- pp->dbi_base = devm_ioremap(pp->dev, pp->cfg.start, -- resource_size(&pp->cfg)); -+ pp->dbi_base = devm_ioremap(pp->dev, pp->cfg->start, -+ resource_size(pp->cfg)); - if (!pp->dbi_base) { - dev_err(pp->dev, "error with ioremap\n"); - return -ENOMEM; - } - } - -- pp->mem_base = pp->mem.start; -+ pp->mem_base = pp->mem->start; - - if (!pp->va_cfg0_base) { - pp->va_cfg0_base = devm_ioremap(pp->dev, pp->cfg0_base, -@@ -465,33 +284,18 @@ int dw_pcie_host_init(struct pcie_port *pp) - } - } - -- if (of_property_read_u32(np, "num-lanes", &pp->lanes)) { -- dev_err(pp->dev, "Failed to parse the number of lanes\n"); -- return -EINVAL; -- } -- -- if (IS_ENABLED(CONFIG_PCI_MSI)) { -- if (!pp->ops->msi_host_init) { -- pp->irq_domain = irq_domain_add_linear(pp->dev->of_node, -- MAX_MSI_IRQS, &msi_domain_ops, -- &dw_pcie_msi_chip); -- if (!pp->irq_domain) { -- dev_err(pp->dev, "irq domain init failed\n"); -- return -ENXIO; -- } -- -- for (i = 0; i < MAX_MSI_IRQS; i++) -- irq_create_mapping(pp->irq_domain, i); -- } else { -- ret = pp->ops->msi_host_init(pp, &dw_pcie_msi_chip); -- if (ret < 0) -- return ret; -- } -- } -+ ret = of_property_read_u32(np, "num-lanes", &pp->lanes); -+ if (ret) -+ pp->lanes = 0; - - if (pp->ops->host_init) - pp->ops->host_init(pp); - -+ if (!pp->ops->rd_other_conf) -+ dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1, -+ PCIE_ATU_TYPE_MEM, pp->mem_base, -+ pp->mem_bus_addr, pp->mem_size); -+ - dw_pcie_wr_own_conf(pp, PCI_BASE_ADDRESS_0, 4, 0); - - /* program correct class for RC */ -@@ -501,126 +305,113 @@ int dw_pcie_host_init(struct pcie_port *pp) - val |= PORT_LOGIC_SPEED_CHANGE; - dw_pcie_wr_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, val); - -- dw_pci.nr_controllers = 1; -- dw_pci.private_data = (void **)&pp; -+ pp->root_bus_nr = pp->busn->start; -+#if 0 -+ bus = pci_scan_root_bus(pp->dev, pp->root_bus_nr, &dw_pcie_ops, -+ pp, &res); -+ if (!bus) -+ return -ENOMEM; -+#else -+ bus = pci_create_root_bus(pp->dev, pp->root_bus_nr, &dw_pcie_ops, -+ pp, &res); -+ if (!bus) -+ return -ENODEV; -+ -+ ret = dw_pcie_msi_ctrl_init(pp); -+ if (ret) -+ return ret; - -- pci_common_init_dev(pp->dev, &dw_pci); --#ifdef CONFIG_PCI_DOMAINS -- dw_pci.domain++; -+ bus->msi = pp->msi; -+ -+ pci_scan_child_bus(bus); - #endif - -- return 0; --} -+ if (pp->ops->scan_bus) -+ pp->ops->scan_bus(pp); - --static void dw_pcie_prog_viewport_cfg0(struct pcie_port *pp, u32 busdev) --{ -- /* Program viewport 0 : OUTBOUND : CFG0 */ -- dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX0, -- PCIE_ATU_VIEWPORT); -- dw_pcie_writel_rc(pp, pp->cfg0_mod_base, PCIE_ATU_LOWER_BASE); -- dw_pcie_writel_rc(pp, (pp->cfg0_mod_base >> 32), PCIE_ATU_UPPER_BASE); -- dw_pcie_writel_rc(pp, pp->cfg0_mod_base + pp->cfg0_size - 1, -- PCIE_ATU_LIMIT); -- dw_pcie_writel_rc(pp, busdev, PCIE_ATU_LOWER_TARGET); -- dw_pcie_writel_rc(pp, 0, PCIE_ATU_UPPER_TARGET); -- dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_CFG0, PCIE_ATU_CR1); -- dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2); --} -+#ifdef CONFIG_ARM -+ /* support old dtbs that incorrectly describe IRQs */ -+ pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci); -+#endif - --static void dw_pcie_prog_viewport_cfg1(struct pcie_port *pp, u32 busdev) --{ -- /* Program viewport 1 : OUTBOUND : CFG1 */ -- dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX1, -- PCIE_ATU_VIEWPORT); -- dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_CFG1, PCIE_ATU_CR1); -- dw_pcie_writel_rc(pp, pp->cfg1_mod_base, PCIE_ATU_LOWER_BASE); -- dw_pcie_writel_rc(pp, (pp->cfg1_mod_base >> 32), PCIE_ATU_UPPER_BASE); -- dw_pcie_writel_rc(pp, pp->cfg1_mod_base + pp->cfg1_size - 1, -- PCIE_ATU_LIMIT); -- dw_pcie_writel_rc(pp, busdev, PCIE_ATU_LOWER_TARGET); -- dw_pcie_writel_rc(pp, 0, PCIE_ATU_UPPER_TARGET); -- dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2); --} -+ if (!pci_has_flag(PCI_PROBE_ONLY)) { -+ pci_bus_size_bridges(bus); -+ pci_bus_assign_resources(bus); - --static void dw_pcie_prog_viewport_mem_outbound(struct pcie_port *pp) --{ -- /* Program viewport 0 : OUTBOUND : MEM */ -- dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX0, -- PCIE_ATU_VIEWPORT); -- dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_MEM, PCIE_ATU_CR1); -- dw_pcie_writel_rc(pp, pp->mem_mod_base, PCIE_ATU_LOWER_BASE); -- dw_pcie_writel_rc(pp, (pp->mem_mod_base >> 32), PCIE_ATU_UPPER_BASE); -- dw_pcie_writel_rc(pp, pp->mem_mod_base + pp->mem_size - 1, -- PCIE_ATU_LIMIT); -- dw_pcie_writel_rc(pp, pp->mem_bus_addr, PCIE_ATU_LOWER_TARGET); -- dw_pcie_writel_rc(pp, upper_32_bits(pp->mem_bus_addr), -- PCIE_ATU_UPPER_TARGET); -- dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2); --} -+ list_for_each_entry(child, &bus->children, node) -+ pcie_bus_configure_settings(child); -+ } - --static void dw_pcie_prog_viewport_io_outbound(struct pcie_port *pp) --{ -- /* Program viewport 1 : OUTBOUND : IO */ -- dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX1, -- PCIE_ATU_VIEWPORT); -- dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_IO, PCIE_ATU_CR1); -- dw_pcie_writel_rc(pp, pp->io_mod_base, PCIE_ATU_LOWER_BASE); -- dw_pcie_writel_rc(pp, (pp->io_mod_base >> 32), PCIE_ATU_UPPER_BASE); -- dw_pcie_writel_rc(pp, pp->io_mod_base + pp->io_size - 1, -- PCIE_ATU_LIMIT); -- dw_pcie_writel_rc(pp, pp->io_bus_addr, PCIE_ATU_LOWER_TARGET); -- dw_pcie_writel_rc(pp, upper_32_bits(pp->io_bus_addr), -- PCIE_ATU_UPPER_TARGET); -- dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2); -+ pci_bus_add_devices(bus); -+ -+ return 0; - } - - static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, - u32 devfn, int where, int size, u32 *val) - { -- int ret = PCIBIOS_SUCCESSFUL; -- u32 address, busdev; -+ int ret, type; -+ u32 busdev, cfg_size; -+ u64 cpu_addr; -+ void __iomem *va_cfg_base; - - busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) | - PCIE_ATU_FUNC(PCI_FUNC(devfn)); -- address = where & ~0x3; - - if (bus->parent->number == pp->root_bus_nr) { -- dw_pcie_prog_viewport_cfg0(pp, busdev); -- ret = dw_pcie_cfg_read(pp->va_cfg0_base + address, where, size, -- val); -- dw_pcie_prog_viewport_mem_outbound(pp); -+ type = PCIE_ATU_TYPE_CFG0; -+ cpu_addr = pp->cfg0_base; -+ cfg_size = pp->cfg0_size; -+ va_cfg_base = pp->va_cfg0_base; - } else { -- dw_pcie_prog_viewport_cfg1(pp, busdev); -- ret = dw_pcie_cfg_read(pp->va_cfg1_base + address, where, size, -- val); -- dw_pcie_prog_viewport_io_outbound(pp); -+ type = PCIE_ATU_TYPE_CFG1; -+ cpu_addr = pp->cfg1_base; -+ cfg_size = pp->cfg1_size; -+ va_cfg_base = pp->va_cfg1_base; - } - -+ dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0, -+ type, cpu_addr, -+ busdev, cfg_size); -+ ret = dw_pcie_cfg_read(va_cfg_base + where, size, val); -+ dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0, -+ PCIE_ATU_TYPE_IO, pp->io_base, -+ pp->io_bus_addr, pp->io_size); -+ - return ret; - } - - static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, - u32 devfn, int where, int size, u32 val) - { -- int ret = PCIBIOS_SUCCESSFUL; -- u32 address, busdev; -+ int ret, type; -+ u32 busdev, cfg_size; -+ u64 cpu_addr; -+ void __iomem *va_cfg_base; - - busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) | - PCIE_ATU_FUNC(PCI_FUNC(devfn)); -- address = where & ~0x3; - - if (bus->parent->number == pp->root_bus_nr) { -- dw_pcie_prog_viewport_cfg0(pp, busdev); -- ret = dw_pcie_cfg_write(pp->va_cfg0_base + address, where, size, -- val); -- dw_pcie_prog_viewport_mem_outbound(pp); -+ type = PCIE_ATU_TYPE_CFG0; -+ cpu_addr = pp->cfg0_base; -+ cfg_size = pp->cfg0_size; -+ va_cfg_base = pp->va_cfg0_base; - } else { -- dw_pcie_prog_viewport_cfg1(pp, busdev); -- ret = dw_pcie_cfg_write(pp->va_cfg1_base + address, where, size, -- val); -- dw_pcie_prog_viewport_io_outbound(pp); -+ type = PCIE_ATU_TYPE_CFG1; -+ cpu_addr = pp->cfg1_base; -+ cfg_size = pp->cfg1_size; -+ va_cfg_base = pp->va_cfg1_base; - } - -+ dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0, -+ type, cpu_addr, -+ busdev, cfg_size); -+ ret = dw_pcie_cfg_write(va_cfg_base + where, size, val); -+ dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0, -+ PCIE_ATU_TYPE_IO, pp->io_base, -+ pp->io_bus_addr, pp->io_size); -+ - return ret; - } - -@@ -650,7 +441,7 @@ static int dw_pcie_valid_config(struct pcie_port *pp, - static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, - int size, u32 *val) - { -- struct pcie_port *pp = sys_to_pcie(bus->sysdata); -+ struct pcie_port *pp = bus->sysdata; - int ret; - - if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) { -@@ -674,7 +465,7 @@ static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, - static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn, - int where, int size, u32 val) - { -- struct pcie_port *pp = sys_to_pcie(bus->sysdata); -+ struct pcie_port *pp = bus->sysdata; - int ret; - - if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) -@@ -698,75 +489,6 @@ static struct pci_ops dw_pcie_ops = { - .write = dw_pcie_wr_conf, - }; - --static int dw_pcie_setup(int nr, struct pci_sys_data *sys) --{ -- struct pcie_port *pp; -- -- pp = sys_to_pcie(sys); -- -- if (global_io_offset < SZ_1M && pp->io_size > 0) { -- sys->io_offset = global_io_offset - pp->io_bus_addr; -- pci_ioremap_io(global_io_offset, pp->io_base); -- global_io_offset += SZ_64K; -- pci_add_resource_offset(&sys->resources, &pp->io, -- sys->io_offset); -- } -- -- sys->mem_offset = pp->mem.start - pp->mem_bus_addr; -- pci_add_resource_offset(&sys->resources, &pp->mem, sys->mem_offset); -- pci_add_resource(&sys->resources, &pp->busn); -- -- return 1; --} -- --static struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys) --{ -- struct pci_bus *bus; -- struct pcie_port *pp = sys_to_pcie(sys); -- -- pp->root_bus_nr = sys->busnr; -- bus = pci_create_root_bus(pp->dev, sys->busnr, -- &dw_pcie_ops, sys, &sys->resources); -- if (!bus) -- return NULL; -- -- pci_scan_child_bus(bus); -- -- if (bus && pp->ops->scan_bus) -- pp->ops->scan_bus(pp); -- -- return bus; --} -- --static int dw_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) --{ -- struct pcie_port *pp = sys_to_pcie(dev->bus->sysdata); -- int irq; -- -- irq = of_irq_parse_and_map_pci(dev, slot, pin); -- if (!irq) -- irq = pp->irq; -- -- return irq; --} -- --static void dw_pcie_add_bus(struct pci_bus *bus) --{ -- if (IS_ENABLED(CONFIG_PCI_MSI)) { -- struct pcie_port *pp = sys_to_pcie(bus->sysdata); -- -- dw_pcie_msi_chip.dev = pp->dev; -- bus->msi = &dw_pcie_msi_chip; -- } --} -- --static struct hw_pci dw_pci = { -- .setup = dw_pcie_setup, -- .scan = dw_pcie_scan_bus, -- .map_irq = dw_pcie_map_irq, -- .add_bus = dw_pcie_add_bus, --}; -- - void dw_pcie_setup_rc(struct pcie_port *pp) - { - u32 val; -@@ -786,6 +508,12 @@ void dw_pcie_setup_rc(struct pcie_port *pp) - case 4: - val |= PORT_LINK_MODE_4_LANES; - break; -+ case 8: -+ val |= PORT_LINK_MODE_8_LANES; -+ break; -+ default: -+ dev_err(pp->dev, "num-lanes %u: invalid value\n", pp->lanes); -+ return; - } - dw_pcie_writel_rc(pp, val, PCIE_PORT_LINK_CONTROL); - -@@ -802,6 +530,9 @@ void dw_pcie_setup_rc(struct pcie_port *pp) - case 4: - val |= PORT_LOGIC_LINK_WIDTH_4_LANES; - break; -+ case 8: -+ val |= PORT_LOGIC_LINK_WIDTH_8_LANES; -+ break; - } - dw_pcie_writel_rc(pp, val, PCIE_LINK_WIDTH_SPEED_CONTROL); - -diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h -index c625675..2f01284 100644 ---- a/drivers/pci/host/pcie-designware.h -+++ b/drivers/pci/host/pcie-designware.h -@@ -27,28 +27,25 @@ struct pcie_port { - u8 root_bus_nr; - void __iomem *dbi_base; - u64 cfg0_base; -- u64 cfg0_mod_base; - void __iomem *va_cfg0_base; - u32 cfg0_size; - u64 cfg1_base; -- u64 cfg1_mod_base; - void __iomem *va_cfg1_base; - u32 cfg1_size; -- u64 io_base; -- u64 io_mod_base; -+ resource_size_t io_base; - phys_addr_t io_bus_addr; - u32 io_size; - u64 mem_base; -- u64 mem_mod_base; - phys_addr_t mem_bus_addr; - u32 mem_size; -- struct resource cfg; -- struct resource io; -- struct resource mem; -- struct resource busn; -+ struct resource *cfg; -+ struct resource *io; -+ struct resource *mem; -+ struct resource *busn; - int irq; - u32 lanes; - struct pcie_host_ops *ops; -+ struct msi_controller *msi; - int msi_irq; - struct irq_domain *irq_domain; - unsigned long msi_data; -@@ -70,14 +67,14 @@ struct pcie_host_ops { - void (*host_init)(struct pcie_port *pp); - void (*msi_set_irq)(struct pcie_port *pp, int irq); - void (*msi_clear_irq)(struct pcie_port *pp, int irq); -- u32 (*get_msi_addr)(struct pcie_port *pp); -+ phys_addr_t (*get_msi_addr)(struct pcie_port *pp); - u32 (*get_msi_data)(struct pcie_port *pp, int pos); - void (*scan_bus)(struct pcie_port *pp); -- int (*msi_host_init)(struct pcie_port *pp, struct msi_chip *chip); -+ int (*msi_host_init)(struct pcie_port *pp, struct msi_controller *chip); - }; - --int dw_pcie_cfg_read(void __iomem *addr, int where, int size, u32 *val); --int dw_pcie_cfg_write(void __iomem *addr, int where, int size, u32 val); -+int dw_pcie_cfg_read(void __iomem *addr, int size, u32 *val); -+int dw_pcie_cfg_write(void __iomem *addr, int size, u32 val); - irqreturn_t dw_handle_msi_irq(struct pcie_port *pp); - void dw_pcie_msi_init(struct pcie_port *pp); - int dw_pcie_link_up(struct pcie_port *pp); -diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c -index 61158e0..f8ec96d 100644 ---- a/drivers/pci/host/pcie-rcar.c -+++ b/drivers/pci/host/pcie-rcar.c -@@ -111,14 +111,14 @@ - struct rcar_msi { - DECLARE_BITMAP(used, INT_PCI_MSI_NR); - struct irq_domain *domain; -- struct msi_chip chip; -+ struct msi_controller chip; - unsigned long pages; - struct mutex lock; - int irq1; - int irq2; - }; - --static inline struct rcar_msi *to_rcar_msi(struct msi_chip *chip) -+static inline struct rcar_msi *to_rcar_msi(struct msi_controller *chip) - { - return container_of(chip, struct rcar_msi, chip); - } -@@ -404,9 +404,6 @@ static void rcar_pcie_enable(struct rcar_pcie *pcie) - rcar_pci.private_data = (void **)&pcie; - - pci_common_init_dev(&pdev->dev, &rcar_pci); --#ifdef CONFIG_PCI_DOMAINS -- rcar_pci.domain++; --#endif - } - - static int phy_wait_for_ack(struct rcar_pcie *pcie) -@@ -622,7 +619,7 @@ static irqreturn_t rcar_pcie_msi_irq(int irq, void *data) - return IRQ_HANDLED; - } - --static int rcar_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, -+static int rcar_msi_setup_irq(struct msi_controller *chip, struct pci_dev *pdev, - struct msi_desc *desc) - { - struct rcar_msi *msi = to_rcar_msi(chip); -@@ -647,12 +644,12 @@ static int rcar_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, - msg.address_hi = rcar_pci_read_reg(pcie, PCIEMSIAUR); - msg.data = hwirq; - -- write_msi_msg(irq, &msg); -+ pci_write_msi_msg(irq, &msg); - - return 0; - } - --static void rcar_msi_teardown_irq(struct msi_chip *chip, unsigned int irq) -+static void rcar_msi_teardown_irq(struct msi_controller *chip, unsigned int irq) - { - struct rcar_msi *msi = to_rcar_msi(chip); - struct irq_data *d = irq_get_irq_data(irq); -@@ -662,10 +659,10 @@ static void rcar_msi_teardown_irq(struct msi_chip *chip, unsigned int irq) - - static struct irq_chip rcar_msi_irq_chip = { - .name = "R-Car PCIe MSI", -- .irq_enable = unmask_msi_irq, -- .irq_disable = mask_msi_irq, -- .irq_mask = mask_msi_irq, -- .irq_unmask = unmask_msi_irq, -+ .irq_enable = pci_msi_unmask_irq, -+ .irq_disable = pci_msi_mask_irq, -+ .irq_mask = pci_msi_mask_irq, -+ .irq_unmask = pci_msi_unmask_irq, - }; - - static int rcar_msi_map(struct irq_domain *domain, unsigned int irq, -@@ -673,7 +670,6 @@ static int rcar_msi_map(struct irq_domain *domain, unsigned int irq, - { - irq_set_chip_and_handler(irq, &rcar_msi_irq_chip, handle_simple_irq); - irq_set_chip_data(irq, domain->host_data); -- set_irq_flags(irq, IRQF_VALID); - - return 0; - } -diff --git a/drivers/pci/host/pcie-xilinx.c b/drivers/pci/host/pcie-xilinx.c -index ccc496b..eef849c 100644 ---- a/drivers/pci/host/pcie-xilinx.c -+++ b/drivers/pci/host/pcie-xilinx.c -@@ -297,18 +297,16 @@ static struct pci_ops xilinx_pcie_ops = { - */ - static void xilinx_pcie_destroy_msi(unsigned int irq) - { -- struct irq_desc *desc; - struct msi_desc *msi; - struct xilinx_pcie_port *port; - -- desc = irq_to_desc(irq); -- msi = irq_desc_get_msi_desc(desc); -- port = sys_to_pcie(msi->dev->bus->sysdata); -- -- if (!test_bit(irq, msi_irq_in_use)) -+ if (!test_bit(irq, msi_irq_in_use)) { -+ msi = irq_get_msi_desc(irq); -+ port = sys_to_pcie(msi_desc_to_pci_sys_data(msi)); - dev_err(port->dev, "Trying to free unused MSI#%d\n", irq); -- else -+ } else { - clear_bit(irq, msi_irq_in_use); -+ } - } - - /** -@@ -335,7 +333,8 @@ static int xilinx_pcie_assign_msi(struct xilinx_pcie_port *port) - * @chip: MSI Chip descriptor - * @irq: MSI IRQ to destroy - */ --static void xilinx_msi_teardown_irq(struct msi_chip *chip, unsigned int irq) -+static void xilinx_msi_teardown_irq(struct msi_controller *chip, -+ unsigned int irq) - { - xilinx_pcie_destroy_msi(irq); - } -@@ -348,7 +347,7 @@ static void xilinx_msi_teardown_irq(struct msi_chip *chip, unsigned int irq) - * - * Return: '0' on success and error value on failure - */ --static int xilinx_pcie_msi_setup_irq(struct msi_chip *chip, -+static int xilinx_pcie_msi_setup_irq(struct msi_controller *chip, - struct pci_dev *pdev, - struct msi_desc *desc) - { -@@ -374,13 +373,13 @@ static int xilinx_pcie_msi_setup_irq(struct msi_chip *chip, - msg.address_lo = msg_addr; - msg.data = irq; - -- write_msi_msg(irq, &msg); -+ pci_write_msi_msg(irq, &msg); - - return 0; - } - - /* MSI Chip Descriptor */ --static struct msi_chip xilinx_pcie_msi_chip = { -+static struct msi_controller xilinx_pcie_msi_chip = { - .setup_irq = xilinx_pcie_msi_setup_irq, - .teardown_irq = xilinx_msi_teardown_irq, - }; -@@ -388,10 +387,10 @@ static struct msi_chip xilinx_pcie_msi_chip = { - /* HW Interrupt Chip Descriptor */ - static struct irq_chip xilinx_msi_irq_chip = { - .name = "Xilinx PCIe MSI", -- .irq_enable = unmask_msi_irq, -- .irq_disable = mask_msi_irq, -- .irq_mask = mask_msi_irq, -- .irq_unmask = unmask_msi_irq, -+ .irq_enable = pci_msi_unmask_irq, -+ .irq_disable = pci_msi_mask_irq, -+ .irq_mask = pci_msi_mask_irq, -+ .irq_unmask = pci_msi_unmask_irq, - }; - - /** -@@ -407,7 +406,6 @@ static int xilinx_pcie_msi_map(struct irq_domain *domain, unsigned int irq, - { - irq_set_chip_and_handler(irq, &xilinx_msi_irq_chip, handle_simple_irq); - irq_set_chip_data(irq, domain->host_data); -- set_irq_flags(irq, IRQF_VALID); - - return 0; - } -@@ -431,20 +429,6 @@ static void xilinx_pcie_enable_msi(struct xilinx_pcie_port *port) - pcie_write(port, msg_addr, XILINX_PCIE_REG_MSIBASE2); - } - --/** -- * xilinx_pcie_add_bus - Add MSI chip info to PCIe bus -- * @bus: PCIe bus -- */ --static void xilinx_pcie_add_bus(struct pci_bus *bus) --{ -- if (IS_ENABLED(CONFIG_PCI_MSI)) { -- struct xilinx_pcie_port *port = sys_to_pcie(bus->sysdata); -- -- xilinx_pcie_msi_chip.dev = port->dev; -- bus->msi = &xilinx_pcie_msi_chip; -- } --} -- - /* INTx Functions */ - - /** -@@ -460,7 +444,6 @@ static int xilinx_pcie_intx_map(struct irq_domain *domain, unsigned int irq, - { - irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq); - irq_set_chip_data(irq, domain->host_data); -- set_irq_flags(irq, IRQF_VALID); - - return 0; - } -@@ -730,9 +713,15 @@ static struct pci_bus *xilinx_pcie_scan_bus(int nr, struct pci_sys_data *sys) - struct pci_bus *bus; - - port->root_busno = sys->busnr; -- bus = pci_scan_root_bus(port->dev, sys->busnr, &xilinx_pcie_ops, -- sys, &sys->resources); - -+ if (IS_ENABLED(CONFIG_PCI_MSI)) -+ bus = pci_scan_root_bus_msi(port->dev, sys->busnr, -+ &xilinx_pcie_ops, sys, -+ &sys->resources, -+ &xilinx_pcie_msi_chip); -+ else -+ bus = pci_scan_root_bus(port->dev, sys->busnr, -+ &xilinx_pcie_ops, sys, &sys->resources); - return bus; - } - -@@ -750,7 +739,7 @@ static int xilinx_pcie_parse_and_add_res(struct xilinx_pcie_port *port) - resource_size_t offset; - struct of_pci_range_parser parser; - struct of_pci_range range; -- struct pci_host_bridge_window *win; -+ struct resource_entry *win; - int err = 0, mem_resno = 0; - - /* Get the ranges */ -@@ -820,7 +809,7 @@ static int xilinx_pcie_parse_and_add_res(struct xilinx_pcie_port *port) - - free_resources: - release_child_resources(&iomem_resource); -- list_for_each_entry(win, &port->resources, list) -+ resource_list_for_each_entry(win, &port->resources) - devm_kfree(dev, win->res); - pci_free_resource_list(&port->resources); - -@@ -924,10 +913,13 @@ static int xilinx_pcie_probe(struct platform_device *pdev) - .private_data = (void **)&port, - .setup = xilinx_pcie_setup, - .map_irq = of_irq_parse_and_map_pci, -- .add_bus = xilinx_pcie_add_bus, - .scan = xilinx_pcie_scan_bus, - .ops = &xilinx_pcie_ops, - }; -+ -+#ifdef CONFIG_PCI_MSI -+ xilinx_pcie_msi_chip.dev = port->dev; -+#endif - pci_common_init_dev(dev, &hw); - - return 0; -diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c -index 084587d..5dd4c96 100644 ---- a/drivers/pci/msi.c -+++ b/drivers/pci/msi.c -@@ -19,19 +19,81 @@ - #include - #include - #include -+#include - - #include "pci.h" - - static int pci_msi_enable = 1; -+int pci_msi_ignore_mask; - - #define msix_table_size(flags) ((flags & PCI_MSIX_FLAGS_QSIZE) + 1) - -+#ifdef CONFIG_PCI_MSI_IRQ_DOMAIN -+static struct irq_domain *pci_msi_default_domain; -+static DEFINE_MUTEX(pci_msi_domain_lock); -+ -+struct irq_domain * __weak arch_get_pci_msi_domain(struct pci_dev *dev) -+{ -+ return pci_msi_default_domain; -+} -+ -+static struct irq_domain *pci_msi_get_domain(struct pci_dev *dev) -+{ -+ struct irq_domain *domain; -+ -+ domain = dev_get_msi_domain(&dev->dev); -+ if (domain) -+ return domain; -+ -+ return arch_get_pci_msi_domain(dev); -+} -+ -+static int pci_msi_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) -+{ -+ struct irq_domain *domain; -+ -+ domain = pci_msi_get_domain(dev); -+ if (domain) -+ return pci_msi_domain_alloc_irqs(domain, dev, nvec, type); -+ -+ return arch_setup_msi_irqs(dev, nvec, type); -+} -+ -+static void pci_msi_teardown_msi_irqs(struct pci_dev *dev) -+{ -+ struct irq_domain *domain; -+ -+ domain = pci_msi_get_domain(dev); -+ if (domain) -+ pci_msi_domain_free_irqs(domain, dev); -+ else -+ arch_teardown_msi_irqs(dev); -+} -+#else -+#define pci_msi_setup_msi_irqs arch_setup_msi_irqs -+#define pci_msi_teardown_msi_irqs arch_teardown_msi_irqs -+#endif - - /* Arch hooks */ - -+struct msi_controller * __weak pcibios_msi_controller(struct pci_dev *dev) -+{ -+ return NULL; -+} -+ -+static struct msi_controller *pci_msi_controller(struct pci_dev *dev) -+{ -+ struct msi_controller *msi_ctrl = dev->bus->msi; -+ -+ if (msi_ctrl) -+ return msi_ctrl; -+ -+ return pcibios_msi_controller(dev); -+} -+ - int __weak arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc) - { -- struct msi_chip *chip = dev->bus->msi; -+ struct msi_controller *chip = pci_msi_controller(dev); - int err; - - if (!chip || !chip->setup_irq) -@@ -48,7 +110,7 @@ int __weak arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc) - - void __weak arch_teardown_msi_irq(unsigned int irq) - { -- struct msi_chip *chip = irq_get_chip_data(irq); -+ struct msi_controller *chip = irq_get_chip_data(irq); - - if (!chip || !chip->teardown_irq) - return; -@@ -58,9 +120,12 @@ void __weak arch_teardown_msi_irq(unsigned int irq) - - int __weak arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) - { -+ struct msi_controller *chip = dev->bus->msi; - struct msi_desc *entry; - int ret; - -+ if (chip && chip->setup_irqs) -+ return chip->setup_irqs(chip, dev, nvec, type); - /* - * If an architecture wants to support multiple MSI, it needs to - * override arch_setup_msi_irqs() -@@ -68,7 +133,7 @@ int __weak arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) - if (type == PCI_CAP_ID_MSI && nvec > 1) - return 1; - -- list_for_each_entry(entry, &dev->msi_list, list) { -+ for_each_pci_msi_entry(entry, dev) { - ret = arch_setup_msi_irq(dev, entry); - if (ret < 0) - return ret; -@@ -85,19 +150,13 @@ int __weak arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) - */ - void default_teardown_msi_irqs(struct pci_dev *dev) - { -+ int i; - struct msi_desc *entry; - -- list_for_each_entry(entry, &dev->msi_list, list) { -- int i, nvec; -- if (entry->irq == 0) -- continue; -- if (entry->nvec_used) -- nvec = entry->nvec_used; -- else -- nvec = 1 << entry->msi_attrib.multiple; -- for (i = 0; i < nvec; i++) -- arch_teardown_msi_irq(entry->irq + i); -- } -+ for_each_pci_msi_entry(entry, dev) -+ if (entry->irq) -+ for (i = 0; i < entry->nvec_used; i++) -+ arch_teardown_msi_irq(entry->irq + i); - } - - void __weak arch_teardown_msi_irqs(struct pci_dev *dev) -@@ -111,7 +170,7 @@ static void default_restore_msi_irq(struct pci_dev *dev, int irq) - - entry = NULL; - if (dev->msix_enabled) { -- list_for_each_entry(entry, &dev->msi_list, list) { -+ for_each_pci_msi_entry(entry, dev) { - if (irq == entry->irq) - break; - } -@@ -120,7 +179,7 @@ static void default_restore_msi_irq(struct pci_dev *dev, int irq) - } - - if (entry) -- __write_msi_msg(entry, &entry->msg); -+ __pci_write_msi_msg(entry, &entry->msg); - } - - void __weak arch_restore_msi_irqs(struct pci_dev *dev) -@@ -128,27 +187,6 @@ void __weak arch_restore_msi_irqs(struct pci_dev *dev) - return default_restore_msi_irqs(dev); - } - --static void msi_set_enable(struct pci_dev *dev, int enable) --{ -- u16 control; -- -- pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control); -- control &= ~PCI_MSI_FLAGS_ENABLE; -- if (enable) -- control |= PCI_MSI_FLAGS_ENABLE; -- pci_write_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, control); --} -- --static void msix_clear_and_set_ctrl(struct pci_dev *dev, u16 clear, u16 set) --{ -- u16 ctrl; -- -- pci_read_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, &ctrl); -- ctrl &= ~clear; -- ctrl |= set; -- pci_write_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, ctrl); --} -- - static inline __attribute_const__ u32 msi_mask(unsigned x) - { - /* Don't shift by >= width of type */ -@@ -163,28 +201,24 @@ static inline __attribute_const__ u32 msi_mask(unsigned x) - * reliably as devices without an INTx disable bit will then generate a - * level IRQ which will never be cleared. - */ --u32 default_msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) -+u32 __pci_msi_desc_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) - { - u32 mask_bits = desc->masked; - -- if (!desc->msi_attrib.maskbit) -+ if (pci_msi_ignore_mask || !desc->msi_attrib.maskbit) - return 0; - - mask_bits &= ~mask; - mask_bits |= flag; -- pci_write_config_dword(desc->dev, desc->mask_pos, mask_bits); -+ pci_write_config_dword(msi_desc_to_pci_dev(desc), desc->mask_pos, -+ mask_bits); - - return mask_bits; - } - --__weak u32 arch_msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) --{ -- return default_msi_mask_irq(desc, mask, flag); --} -- - static void msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) - { -- desc->masked = arch_msi_mask_irq(desc, mask, flag); -+ desc->masked = __pci_msi_desc_mask_irq(desc, mask, flag); - } - - /* -@@ -194,11 +228,15 @@ static void msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) - * file. This saves a few milliseconds when initialising devices with lots - * of MSI-X interrupts. - */ --u32 default_msix_mask_irq(struct msi_desc *desc, u32 flag) -+u32 __pci_msix_desc_mask_irq(struct msi_desc *desc, u32 flag) - { - u32 mask_bits = desc->masked; - unsigned offset = desc->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + - PCI_MSIX_ENTRY_VECTOR_CTRL; -+ -+ if (pci_msi_ignore_mask) -+ return 0; -+ - mask_bits &= ~PCI_MSIX_ENTRY_CTRL_MASKBIT; - if (flag) - mask_bits |= PCI_MSIX_ENTRY_CTRL_MASKBIT; -@@ -207,19 +245,14 @@ u32 default_msix_mask_irq(struct msi_desc *desc, u32 flag) - return mask_bits; - } - --__weak u32 arch_msix_mask_irq(struct msi_desc *desc, u32 flag) --{ -- return default_msix_mask_irq(desc, flag); --} -- - static void msix_mask_irq(struct msi_desc *desc, u32 flag) - { -- desc->masked = arch_msix_mask_irq(desc, flag); -+ desc->masked = __pci_msix_desc_mask_irq(desc, flag); - } - - static void msi_set_mask_bit(struct irq_data *data, u32 flag) - { -- struct msi_desc *desc = irq_data_get_msi(data); -+ struct msi_desc *desc = irq_data_get_msi_desc(data); - - if (desc->msi_attrib.is_msix) { - msix_mask_irq(desc, flag); -@@ -230,12 +263,20 @@ static void msi_set_mask_bit(struct irq_data *data, u32 flag) - } - } - --void mask_msi_irq(struct irq_data *data) -+/** -+ * pci_msi_mask_irq - Generic irq chip callback to mask PCI/MSI interrupts -+ * @data: pointer to irqdata associated to that interrupt -+ */ -+void pci_msi_mask_irq(struct irq_data *data) - { - msi_set_mask_bit(data, 1); - } - --void unmask_msi_irq(struct irq_data *data) -+/** -+ * pci_msi_unmask_irq - Generic irq chip callback to unmask PCI/MSI interrupts -+ * @data: pointer to irqdata associated to that interrupt -+ */ -+void pci_msi_unmask_irq(struct irq_data *data) - { - msi_set_mask_bit(data, 0); - } -@@ -244,14 +285,15 @@ void default_restore_msi_irqs(struct pci_dev *dev) - { - struct msi_desc *entry; - -- list_for_each_entry(entry, &dev->msi_list, list) { -+ for_each_pci_msi_entry(entry, dev) - default_restore_msi_irq(dev, entry->irq); -- } - } - --void __read_msi_msg(struct msi_desc *entry, struct msi_msg *msg) -+void __pci_read_msi_msg(struct msi_desc *entry, struct msi_msg *msg) - { -- BUG_ON(entry->dev->current_state != PCI_D0); -+ struct pci_dev *dev = msi_desc_to_pci_dev(entry); -+ -+ BUG_ON(dev->current_state != PCI_D0); - - if (entry->msi_attrib.is_msix) { - void __iomem *base = entry->mask_base + -@@ -261,7 +303,6 @@ void __read_msi_msg(struct msi_desc *entry, struct msi_msg *msg) - msg->address_hi = readl(base + PCI_MSIX_ENTRY_UPPER_ADDR); - msg->data = readl(base + PCI_MSIX_ENTRY_DATA); - } else { -- struct pci_dev *dev = entry->dev; - int pos = dev->msi_cap; - u16 data; - -@@ -279,34 +320,11 @@ void __read_msi_msg(struct msi_desc *entry, struct msi_msg *msg) - } - } - --void read_msi_msg(unsigned int irq, struct msi_msg *msg) --{ -- struct msi_desc *entry = irq_get_msi_desc(irq); -- -- __read_msi_msg(entry, msg); --} -- --void __get_cached_msi_msg(struct msi_desc *entry, struct msi_msg *msg) --{ -- /* Assert that the cache is valid, assuming that -- * valid messages are not all-zeroes. */ -- BUG_ON(!(entry->msg.address_hi | entry->msg.address_lo | -- entry->msg.data)); -- -- *msg = entry->msg; --} -- --void get_cached_msi_msg(unsigned int irq, struct msi_msg *msg) -+void __pci_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) - { -- struct msi_desc *entry = irq_get_msi_desc(irq); -- -- __get_cached_msi_msg(entry, msg); --} --EXPORT_SYMBOL_GPL(get_cached_msi_msg); -+ struct pci_dev *dev = msi_desc_to_pci_dev(entry); - --void __write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) --{ -- if (entry->dev->current_state != PCI_D0) { -+ if (dev->current_state != PCI_D0) { - /* Don't touch the hardware now */ - } else if (entry->msi_attrib.is_msix) { - void __iomem *base; -@@ -317,7 +335,6 @@ void __write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) - writel(msg->address_hi, base + PCI_MSIX_ENTRY_UPPER_ADDR); - writel(msg->data, base + PCI_MSIX_ENTRY_DATA); - } else { -- struct pci_dev *dev = entry->dev; - int pos = dev->msi_cap; - u16 msgctl; - -@@ -341,38 +358,32 @@ void __write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) - entry->msg = *msg; - } - --void write_msi_msg(unsigned int irq, struct msi_msg *msg) -+void pci_write_msi_msg(unsigned int irq, struct msi_msg *msg) - { - struct msi_desc *entry = irq_get_msi_desc(irq); - -- __write_msi_msg(entry, msg); -+ __pci_write_msi_msg(entry, msg); - } --EXPORT_SYMBOL_GPL(write_msi_msg); -+EXPORT_SYMBOL_GPL(pci_write_msi_msg); - - static void free_msi_irqs(struct pci_dev *dev) - { -+ struct list_head *msi_list = dev_to_msi_list(&dev->dev); - struct msi_desc *entry, *tmp; - struct attribute **msi_attrs; - struct device_attribute *dev_attr; -- int count = 0; -+ int i, count = 0; - -- list_for_each_entry(entry, &dev->msi_list, list) { -- int i, nvec; -- if (!entry->irq) -- continue; -- if (entry->nvec_used) -- nvec = entry->nvec_used; -- else -- nvec = 1 << entry->msi_attrib.multiple; -- for (i = 0; i < nvec; i++) -- BUG_ON(irq_has_action(entry->irq + i)); -- } -+ for_each_pci_msi_entry(entry, dev) -+ if (entry->irq) -+ for (i = 0; i < entry->nvec_used; i++) -+ BUG_ON(irq_has_action(entry->irq + i)); - -- arch_teardown_msi_irqs(dev); -+ pci_msi_teardown_msi_irqs(dev); - -- list_for_each_entry_safe(entry, tmp, &dev->msi_list, list) { -+ list_for_each_entry_safe(entry, tmp, msi_list, list) { - if (entry->msi_attrib.is_msix) { -- if (list_is_last(&entry->list, &dev->msi_list)) -+ if (list_is_last(&entry->list, msi_list)) - iounmap(entry->mask_base); - } - -@@ -397,18 +408,6 @@ static void free_msi_irqs(struct pci_dev *dev) - } - } - --static struct msi_desc *alloc_msi_entry(struct pci_dev *dev) --{ -- struct msi_desc *desc = kzalloc(sizeof(*desc), GFP_KERNEL); -- if (!desc) -- return NULL; -- -- INIT_LIST_HEAD(&desc->list); -- desc->dev = dev; -- -- return desc; --} -- - static void pci_intx_for_msi(struct pci_dev *dev, int enable) - { - if (!(dev->dev_flags & PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG)) -@@ -426,7 +425,7 @@ static void __pci_restore_msi_state(struct pci_dev *dev) - entry = irq_get_msi_desc(dev->irq); - - pci_intx_for_msi(dev, 0); -- msi_set_enable(dev, 0); -+ pci_msi_set_enable(dev, 0); - arch_restore_msi_irqs(dev); - - pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control); -@@ -443,19 +442,18 @@ static void __pci_restore_msix_state(struct pci_dev *dev) - - if (!dev->msix_enabled) - return; -- BUG_ON(list_empty(&dev->msi_list)); -+ BUG_ON(list_empty(dev_to_msi_list(&dev->dev))); - - /* route the table */ - pci_intx_for_msi(dev, 0); -- msix_clear_and_set_ctrl(dev, 0, -+ pci_msix_clear_and_set_ctrl(dev, 0, - PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL); - - arch_restore_msi_irqs(dev); -- list_for_each_entry(entry, &dev->msi_list, list) { -+ for_each_pci_msi_entry(entry, dev) - msix_mask_irq(entry, entry->masked); -- } - -- msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL, 0); -+ pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL, 0); - } - - void pci_restore_msi_state(struct pci_dev *dev) -@@ -497,9 +495,8 @@ static int populate_msi_sysfs(struct pci_dev *pdev) - int count = 0; - - /* Determine how many msi entries we have */ -- list_for_each_entry(entry, &pdev->msi_list, list) { -+ for_each_pci_msi_entry(entry, pdev) - ++num_msi; -- } - if (!num_msi) - return 0; - -@@ -507,7 +504,7 @@ static int populate_msi_sysfs(struct pci_dev *pdev) - msi_attrs = kzalloc(sizeof(void *) * (num_msi + 1), GFP_KERNEL); - if (!msi_attrs) - return -ENOMEM; -- list_for_each_entry(entry, &pdev->msi_list, list) { -+ for_each_pci_msi_entry(entry, pdev) { - msi_dev_attr = kzalloc(sizeof(*msi_dev_attr), GFP_KERNEL); - if (!msi_dev_attr) - goto error_attrs; -@@ -559,13 +556,13 @@ error_attrs: - return ret; - } - --static struct msi_desc *msi_setup_entry(struct pci_dev *dev) -+static struct msi_desc *msi_setup_entry(struct pci_dev *dev, int nvec) - { - u16 control; - struct msi_desc *entry; - - /* MSI Entry Initialization */ -- entry = alloc_msi_entry(dev); -+ entry = alloc_msi_entry(&dev->dev); - if (!entry) - return NULL; - -@@ -577,6 +574,8 @@ static struct msi_desc *msi_setup_entry(struct pci_dev *dev) - entry->msi_attrib.maskbit = !!(control & PCI_MSI_FLAGS_MASKBIT); - entry->msi_attrib.default_irq = dev->irq; /* Save IOAPIC IRQ */ - entry->msi_attrib.multi_cap = (control & PCI_MSI_FLAGS_QMASK) >> 1; -+ entry->msi_attrib.multiple = ilog2(__roundup_pow_of_two(nvec)); -+ entry->nvec_used = nvec; - - if (control & PCI_MSI_FLAGS_64BIT) - entry->mask_pos = dev->msi_cap + PCI_MSI_MASK_64; -@@ -594,7 +593,7 @@ static int msi_verify_entries(struct pci_dev *dev) - { - struct msi_desc *entry; - -- list_for_each_entry(entry, &dev->msi_list, list) { -+ for_each_pci_msi_entry(entry, dev) { - if (!dev->no_64bit_msi || !entry->msg.address_hi) - continue; - dev_err(&dev->dev, "Device has broken 64-bit MSI but arch" -@@ -621,9 +620,9 @@ static int msi_capability_init(struct pci_dev *dev, int nvec) - int ret; - unsigned mask; - -- msi_set_enable(dev, 0); /* Disable MSI during set up */ -+ pci_msi_set_enable(dev, 0); /* Disable MSI during set up */ - -- entry = msi_setup_entry(dev); -+ entry = msi_setup_entry(dev, nvec); - if (!entry) - return -ENOMEM; - -@@ -631,10 +630,10 @@ static int msi_capability_init(struct pci_dev *dev, int nvec) - mask = msi_mask(entry->msi_attrib.multi_cap); - msi_mask_irq(entry, mask, mask); - -- list_add_tail(&entry->list, &dev->msi_list); -+ list_add_tail(&entry->list, dev_to_msi_list(&dev->dev)); - - /* Configure MSI capability structure */ -- ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSI); -+ ret = pci_msi_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSI); - if (ret) { - msi_mask_irq(entry, mask, ~mask); - free_msi_irqs(dev); -@@ -657,7 +656,7 @@ static int msi_capability_init(struct pci_dev *dev, int nvec) - - /* Set MSI enabled bits */ - pci_intx_for_msi(dev, 0); -- msi_set_enable(dev, 1); -+ pci_msi_set_enable(dev, 1); - dev->msi_enabled = 1; - - dev->irq = entry->irq; -@@ -686,7 +685,7 @@ static int msix_setup_entries(struct pci_dev *dev, void __iomem *base, - int i; - - for (i = 0; i < nvec; i++) { -- entry = alloc_msi_entry(dev); -+ entry = alloc_msi_entry(&dev->dev); - if (!entry) { - if (!i) - iounmap(base); -@@ -701,8 +700,9 @@ static int msix_setup_entries(struct pci_dev *dev, void __iomem *base, - entry->msi_attrib.entry_nr = entries[i].entry; - entry->msi_attrib.default_irq = dev->irq; - entry->mask_base = base; -+ entry->nvec_used = 1; - -- list_add_tail(&entry->list, &dev->msi_list); -+ list_add_tail(&entry->list, dev_to_msi_list(&dev->dev)); - } - - return 0; -@@ -714,12 +714,11 @@ static void msix_program_entries(struct pci_dev *dev, - struct msi_desc *entry; - int i = 0; - -- list_for_each_entry(entry, &dev->msi_list, list) { -+ for_each_pci_msi_entry(entry, dev) { - int offset = entries[i].entry * PCI_MSIX_ENTRY_SIZE + - PCI_MSIX_ENTRY_VECTOR_CTRL; - - entries[i].vector = entry->irq; -- irq_set_msi_desc(entry->irq, entry); - entry->masked = readl(entry->mask_base + offset); - msix_mask_irq(entry, 1); - i++; -@@ -744,7 +743,7 @@ static int msix_capability_init(struct pci_dev *dev, - void __iomem *base; - - /* Ensure MSI-X is disabled while it is set up */ -- msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_ENABLE, 0); -+ pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_ENABLE, 0); - - pci_read_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, &control); - /* Request & Map MSI-X table region */ -@@ -756,7 +755,7 @@ static int msix_capability_init(struct pci_dev *dev, - if (ret) - return ret; - -- ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSIX); -+ ret = pci_msi_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSIX); - if (ret) - goto out_avail; - -@@ -770,7 +769,7 @@ static int msix_capability_init(struct pci_dev *dev, - * MSI-X registers. We need to mask all the vectors to prevent - * interrupts coming in before they're fully set up. - */ -- msix_clear_and_set_ctrl(dev, 0, -+ pci_msix_clear_and_set_ctrl(dev, 0, - PCI_MSIX_FLAGS_MASKALL | PCI_MSIX_FLAGS_ENABLE); - - msix_program_entries(dev, entries); -@@ -783,7 +782,7 @@ static int msix_capability_init(struct pci_dev *dev, - pci_intx_for_msi(dev, 0); - dev->msix_enabled = 1; - -- msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL, 0); -+ pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL, 0); - - return 0; - -@@ -796,7 +795,7 @@ out_avail: - struct msi_desc *entry; - int avail = 0; - -- list_for_each_entry(entry, &dev->msi_list, list) { -+ for_each_pci_msi_entry(entry, dev) { - if (entry->irq != 0) - avail++; - } -@@ -885,17 +884,17 @@ void pci_msi_shutdown(struct pci_dev *dev) - if (!pci_msi_enable || !dev || !dev->msi_enabled) - return; - -- BUG_ON(list_empty(&dev->msi_list)); -- desc = list_first_entry(&dev->msi_list, struct msi_desc, list); -+ BUG_ON(list_empty(dev_to_msi_list(&dev->dev))); -+ desc = first_pci_msi_entry(dev); - -- msi_set_enable(dev, 0); -+ pci_msi_set_enable(dev, 0); - pci_intx_for_msi(dev, 1); - dev->msi_enabled = 0; - - /* Return the device with MSI unmasked as initial states */ - mask = msi_mask(desc->msi_attrib.multi_cap); - /* Keep cached state to be restored */ -- arch_msi_mask_irq(desc, mask, ~mask); -+ __pci_msi_desc_mask_irq(desc, mask, ~mask); - - /* Restore dev->irq to its default pin-assertion irq */ - dev->irq = desc->msi_attrib.default_irq; -@@ -991,12 +990,12 @@ void pci_msix_shutdown(struct pci_dev *dev) - return; - - /* Return the device with MSI-X masked as initial states */ -- list_for_each_entry(entry, &dev->msi_list, list) { -+ for_each_pci_msi_entry(entry, dev) { - /* Keep cached states to be restored */ -- arch_msix_mask_irq(entry, 1); -+ __pci_msix_desc_mask_irq(entry, 1); - } - -- msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_ENABLE, 0); -+ pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_ENABLE, 0); - pci_intx_for_msi(dev, 1); - dev->msix_enabled = 0; - } -@@ -1030,19 +1029,6 @@ EXPORT_SYMBOL(pci_msi_enabled); - - void pci_msi_init_pci_dev(struct pci_dev *dev) - { -- INIT_LIST_HEAD(&dev->msi_list); -- -- /* Disable the msi hardware to avoid screaming interrupts -- * during boot. This is the power on reset default so -- * usually this should be a noop. -- */ -- dev->msi_cap = pci_find_capability(dev, PCI_CAP_ID_MSI); -- if (dev->msi_cap) -- msi_set_enable(dev, 0); -- -- dev->msix_cap = pci_find_capability(dev, PCI_CAP_ID_MSIX); -- if (dev->msix_cap) -- msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_ENABLE, 0); - } - - /** -@@ -1138,3 +1124,217 @@ int pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries, - return nvec; - } - EXPORT_SYMBOL(pci_enable_msix_range); -+ -+struct pci_dev *msi_desc_to_pci_dev(struct msi_desc *desc) -+{ -+ return to_pci_dev(desc->dev); -+} -+ -+void *msi_desc_to_pci_sysdata(struct msi_desc *desc) -+{ -+ struct pci_dev *dev = msi_desc_to_pci_dev(desc); -+ -+ return dev->bus->sysdata; -+} -+EXPORT_SYMBOL_GPL(msi_desc_to_pci_sysdata); -+ -+#ifdef CONFIG_PCI_MSI_IRQ_DOMAIN -+/** -+ * pci_msi_domain_write_msg - Helper to write MSI message to PCI config space -+ * @irq_data: Pointer to interrupt data of the MSI interrupt -+ * @msg: Pointer to the message -+ */ -+void pci_msi_domain_write_msg(struct irq_data *irq_data, struct msi_msg *msg) -+{ -+ struct msi_desc *desc = irq_data->msi_desc; -+ -+ /* -+ * For MSI-X desc->irq is always equal to irq_data->irq. For -+ * MSI only the first interrupt of MULTI MSI passes the test. -+ */ -+ if (desc->irq == irq_data->irq) -+ __pci_write_msi_msg(desc, msg); -+} -+ -+/** -+ * pci_msi_domain_calc_hwirq - Generate a unique ID for an MSI source -+ * @dev: Pointer to the PCI device -+ * @desc: Pointer to the msi descriptor -+ * -+ * The ID number is only used within the irqdomain. -+ */ -+irq_hw_number_t pci_msi_domain_calc_hwirq(struct pci_dev *dev, -+ struct msi_desc *desc) -+{ -+ return (irq_hw_number_t)desc->msi_attrib.entry_nr | -+ PCI_DEVID(dev->bus->number, dev->devfn) << 11 | -+ (pci_domain_nr(dev->bus) & 0xFFFFFFFF) << 27; -+} -+ -+static inline bool pci_msi_desc_is_multi_msi(struct msi_desc *desc) -+{ -+ return !desc->msi_attrib.is_msix && desc->nvec_used > 1; -+} -+ -+/** -+ * pci_msi_domain_check_cap - Verify that @domain supports the capabilities for @dev -+ * @domain: The interrupt domain to check -+ * @info: The domain info for verification -+ * @dev: The device to check -+ * -+ * Returns: -+ * 0 if the functionality is supported -+ * 1 if Multi MSI is requested, but the domain does not support it -+ * -ENOTSUPP otherwise -+ */ -+int pci_msi_domain_check_cap(struct irq_domain *domain, -+ struct msi_domain_info *info, struct device *dev) -+{ -+ struct msi_desc *desc = first_pci_msi_entry(to_pci_dev(dev)); -+ -+ /* Special handling to support pci_enable_msi_range() */ -+ if (pci_msi_desc_is_multi_msi(desc) && -+ !(info->flags & MSI_FLAG_MULTI_PCI_MSI)) -+ return 1; -+ else if (desc->msi_attrib.is_msix && !(info->flags & MSI_FLAG_PCI_MSIX)) -+ return -ENOTSUPP; -+ -+ return 0; -+} -+ -+static int pci_msi_domain_handle_error(struct irq_domain *domain, -+ struct msi_desc *desc, int error) -+{ -+ /* Special handling to support pci_enable_msi_range() */ -+ if (pci_msi_desc_is_multi_msi(desc) && error == -ENOSPC) -+ return 1; -+ -+ return error; -+} -+ -+#ifdef GENERIC_MSI_DOMAIN_OPS -+static void pci_msi_domain_set_desc(msi_alloc_info_t *arg, -+ struct msi_desc *desc) -+{ -+ arg->desc = desc; -+ arg->hwirq = pci_msi_domain_calc_hwirq(msi_desc_to_pci_dev(desc), -+ desc); -+} -+#else -+#define pci_msi_domain_set_desc NULL -+#endif -+ -+static struct msi_domain_ops pci_msi_domain_ops_default = { -+ .set_desc = pci_msi_domain_set_desc, -+ .msi_check = pci_msi_domain_check_cap, -+ .handle_error = pci_msi_domain_handle_error, -+}; -+ -+static void pci_msi_domain_update_dom_ops(struct msi_domain_info *info) -+{ -+ struct msi_domain_ops *ops = info->ops; -+ -+ if (ops == NULL) { -+ info->ops = &pci_msi_domain_ops_default; -+ } else { -+ if (ops->set_desc == NULL) -+ ops->set_desc = pci_msi_domain_set_desc; -+ if (ops->msi_check == NULL) -+ ops->msi_check = pci_msi_domain_check_cap; -+ if (ops->handle_error == NULL) -+ ops->handle_error = pci_msi_domain_handle_error; -+ } -+} -+ -+static void pci_msi_domain_update_chip_ops(struct msi_domain_info *info) -+{ -+ struct irq_chip *chip = info->chip; -+ -+ BUG_ON(!chip); -+ if (!chip->irq_write_msi_msg) -+ chip->irq_write_msi_msg = pci_msi_domain_write_msg; -+} -+ -+/** -+ * pci_msi_create_irq_domain - Creat a MSI interrupt domain -+ * @node: Optional device-tree node of the interrupt controller -+ * @info: MSI domain info -+ * @parent: Parent irq domain -+ * -+ * Updates the domain and chip ops and creates a MSI interrupt domain. -+ * -+ * Returns: -+ * A domain pointer or NULL in case of failure. -+ */ -+struct irq_domain *pci_msi_create_irq_domain(struct device_node *node, -+ struct msi_domain_info *info, -+ struct irq_domain *parent) -+{ -+ struct irq_domain *domain; -+ -+ if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS) -+ pci_msi_domain_update_dom_ops(info); -+ if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS) -+ pci_msi_domain_update_chip_ops(info); -+ -+ domain = msi_create_irq_domain(node, info, parent); -+ if (!domain) -+ return NULL; -+ -+ domain->bus_token = DOMAIN_BUS_PCI_MSI; -+ return domain; -+} -+ -+/** -+ * pci_msi_domain_alloc_irqs - Allocate interrupts for @dev in @domain -+ * @domain: The interrupt domain to allocate from -+ * @dev: The device for which to allocate -+ * @nvec: The number of interrupts to allocate -+ * @type: Unused to allow simpler migration from the arch_XXX interfaces -+ * -+ * Returns: -+ * A virtual interrupt number or an error code in case of failure -+ */ -+int pci_msi_domain_alloc_irqs(struct irq_domain *domain, struct pci_dev *dev, -+ int nvec, int type) -+{ -+ return msi_domain_alloc_irqs(domain, &dev->dev, nvec); -+} -+ -+/** -+ * pci_msi_domain_free_irqs - Free interrupts for @dev in @domain -+ * @domain: The interrupt domain -+ * @dev: The device for which to free interrupts -+ */ -+void pci_msi_domain_free_irqs(struct irq_domain *domain, struct pci_dev *dev) -+{ -+ msi_domain_free_irqs(domain, &dev->dev); -+} -+ -+/** -+ * pci_msi_create_default_irq_domain - Create a default MSI interrupt domain -+ * @node: Optional device-tree node of the interrupt controller -+ * @info: MSI domain info -+ * @parent: Parent irq domain -+ * -+ * Returns: A domain pointer or NULL in case of failure. If successful -+ * the default PCI/MSI irqdomain pointer is updated. -+ */ -+struct irq_domain *pci_msi_create_default_irq_domain(struct device_node *node, -+ struct msi_domain_info *info, struct irq_domain *parent) -+{ -+ struct irq_domain *domain; -+ -+ mutex_lock(&pci_msi_domain_lock); -+ if (pci_msi_default_domain) { -+ pr_err("PCI: default irq domain for PCI MSI has already been created.\n"); -+ domain = NULL; -+ } else { -+ domain = pci_msi_create_irq_domain(node, info, parent); -+ pci_msi_default_domain = domain; -+ } -+ mutex_unlock(&pci_msi_domain_lock); -+ -+ return domain; -+} -+#endif /* CONFIG_PCI_MSI_IRQ_DOMAIN */ -diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h -index b5defca..df2169e 100644 ---- a/drivers/pci/pci.h -+++ b/drivers/pci/pci.h -@@ -140,6 +140,27 @@ static inline void pci_no_msi(void) { } - static inline void pci_msi_init_pci_dev(struct pci_dev *dev) { } - #endif - -+static inline void pci_msi_set_enable(struct pci_dev *dev, int enable) -+{ -+ u16 control; -+ -+ pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control); -+ control &= ~PCI_MSI_FLAGS_ENABLE; -+ if (enable) -+ control |= PCI_MSI_FLAGS_ENABLE; -+ pci_write_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, control); -+} -+ -+static inline void pci_msix_clear_and_set_ctrl(struct pci_dev *dev, u16 clear, u16 set) -+{ -+ u16 ctrl; -+ -+ pci_read_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, &ctrl); -+ ctrl &= ~clear; -+ ctrl |= set; -+ pci_write_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, ctrl); -+} -+ - void pci_realloc_get_opt(char *); - - static inline int pci_no_d1d2(struct pci_dev *dev) -diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c -index 3010ffc..6bdeb75 100644 ---- a/drivers/pci/probe.c -+++ b/drivers/pci/probe.c -@@ -1097,6 +1097,22 @@ int pci_cfg_space_size(struct pci_dev *dev) - - #define LEGACY_IO_RESOURCE (IORESOURCE_IO | IORESOURCE_PCI_FIXED) - -+static void pci_msi_setup_pci_dev(struct pci_dev *dev) -+{ -+ /* -+ * Disable the MSI hardware to avoid screaming interrupts -+ * during boot. This is the power on reset default so -+ * usually this should be a noop. -+ */ -+ dev->msi_cap = pci_find_capability(dev, PCI_CAP_ID_MSI); -+ if (dev->msi_cap) -+ pci_msi_set_enable(dev, 0); -+ -+ dev->msix_cap = pci_find_capability(dev, PCI_CAP_ID_MSIX); -+ if (dev->msix_cap) -+ pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_ENABLE, 0); -+} -+ - /** - * pci_setup_device - fill in class and map information of a device - * @dev: the device structure to fill -@@ -1152,6 +1168,8 @@ int pci_setup_device(struct pci_dev *dev) - /* "Unknown power state" */ - dev->current_state = PCI_UNKNOWN; - -+ pci_msi_setup_pci_dev(dev); -+ - /* Early fixups, before probing the BARs */ - pci_fixup_device(pci_fixup_early, dev); - /* device class may be changed after fixup */ -@@ -1908,7 +1926,7 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, - int error; - struct pci_host_bridge *bridge; - struct pci_bus *b, *b2; -- struct pci_host_bridge_window *window, *n; -+ struct resource_entry *window, *n; - struct resource *res; - resource_size_t offset; - char bus_addr[64]; -@@ -1972,8 +1990,8 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, - printk(KERN_INFO "PCI host bridge to bus %s\n", dev_name(&b->dev)); - - /* Add initial resources to the bus */ -- list_for_each_entry_safe(window, n, resources, list) { -- list_move_tail(&window->list, &bridge->windows); -+ resource_list_for_each_entry_safe(window, n, resources) { -+ list_move_tail(&window->node, &bridge->windows); - res = window->res; - offset = window->offset; - if (res->flags & IORESOURCE_BUS) -@@ -2073,12 +2091,12 @@ void pci_bus_release_busn_res(struct pci_bus *b) - struct pci_bus *pci_scan_root_bus(struct device *parent, int bus, - struct pci_ops *ops, void *sysdata, struct list_head *resources) - { -- struct pci_host_bridge_window *window; -+ struct resource_entry *window; - bool found = false; - struct pci_bus *b; - int max; - -- list_for_each_entry(window, resources, list) -+ resource_list_for_each_entry(window, resources) - if (window->res->flags & IORESOURCE_BUS) { - found = true; - break; -diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c -index b6d646a..f3681e2 100644 ---- a/drivers/pci/quirks.c -+++ b/drivers/pci/quirks.c -@@ -3516,8 +3516,9 @@ int pci_dev_specific_reset(struct pci_dev *dev, int probe) - static void quirk_dma_func0_alias(struct pci_dev *dev) - { - if (PCI_FUNC(dev->devfn) != 0) { -- dev->dma_alias_devfn = PCI_DEVFN(PCI_SLOT(dev->devfn), 0); -- dev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVFN; -+ dev->dma_alias_devid = PCI_DEVID(dev->bus->number, -+ PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); -+ dev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVID; - } - } - -@@ -3532,8 +3533,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_RICOH, 0xe476, quirk_dma_func0_alias); - static void quirk_dma_func1_alias(struct pci_dev *dev) - { - if (PCI_FUNC(dev->devfn) != 1) { -- dev->dma_alias_devfn = PCI_DEVFN(PCI_SLOT(dev->devfn), 1); -- dev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVFN; -+ dev->dma_alias_devid = PCI_DEVID(dev->bus->number, -+ PCI_DEVFN(PCI_SLOT(dev->devfn), 1)); -+ dev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVID; - } - } - -diff --git a/drivers/pci/search.c b/drivers/pci/search.c -index a81f413..a00924f 100644 ---- a/drivers/pci/search.c -+++ b/drivers/pci/search.c -@@ -40,9 +40,8 @@ int pci_for_each_dma_alias(struct pci_dev *pdev, - * If the device is broken and uses an alias requester ID for - * DMA, iterate over that too. - */ -- if (unlikely(pdev->dev_flags & PCI_DEV_FLAGS_DMA_ALIAS_DEVFN)) { -- ret = fn(pdev, PCI_DEVID(pdev->bus->number, -- pdev->dma_alias_devfn), data); -+ if (unlikely(pdev->dev_flags & PCI_DEV_FLAGS_DMA_ALIAS_DEVID)) { -+ ret = fn(pdev, pdev->dma_alias_devid, data); - if (ret) - return ret; - } -diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c -index 116ca37..37d4218 100644 ---- a/drivers/pci/xen-pcifront.c -+++ b/drivers/pci/xen-pcifront.c -@@ -267,7 +267,7 @@ static int pci_frontend_enable_msix(struct pci_dev *dev, - } - - i = 0; -- list_for_each_entry(entry, &dev->msi_list, list) { -+ for_each_pci_msi_entry(entry, dev) { - op.msix_entries[i].entry = entry->msi_attrib.entry_nr; - /* Vector is useless at this point. */ - op.msix_entries[i].vector = -1; -diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig -index f65ff49..b56b084 100644 ---- a/drivers/power/reset/Kconfig -+++ b/drivers/power/reset/Kconfig -@@ -150,5 +150,11 @@ config POWER_RESET_SYSCON - help - Reboot support for generic SYSCON mapped register reset. - -+config POWER_RESET_LAYERSCAPE -+ bool "Freescale LayerScape reset driver" -+ depends on ARCH_LAYERSCAPE -+ help -+ Reboot support for the Freescale LayerScape SoCs. -+ - endif - -diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile -index 76ce1c5..d924bdb 100644 ---- a/drivers/power/reset/Makefile -+++ b/drivers/power/reset/Makefile -@@ -17,3 +17,4 @@ obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o - obj-$(CONFIG_POWER_RESET_XGENE) += xgene-reboot.o - obj-$(CONFIG_POWER_RESET_KEYSTONE) += keystone-reset.o - obj-$(CONFIG_POWER_RESET_SYSCON) += syscon-reboot.o -+obj-$(CONFIG_POWER_RESET_LAYERSCAPE) += ls-reboot.o -diff --git a/drivers/power/reset/ls-reboot.c b/drivers/power/reset/ls-reboot.c -new file mode 100644 -index 0000000..fa1152c ---- /dev/null -+++ b/drivers/power/reset/ls-reboot.c -@@ -0,0 +1,93 @@ -+/* -+ * Freescale LayerScape reboot driver -+ * -+ * Copyright (c) 2015, Freescale Semiconductor. -+ * Author: Pankaj Chauhan -+ * -+ * 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. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+struct ls_reboot_priv { -+ struct device *dev; -+ u32 *rstcr; -+}; -+ -+static struct ls_reboot_priv *ls_reboot_priv; -+ -+static void ls_reboot(enum reboot_mode reboot_mode, const char *cmd) -+{ -+ struct ls_reboot_priv *priv = ls_reboot_priv; -+ u32 val; -+ unsigned long timeout; -+ -+ if (ls_reboot_priv) { -+ val = readl(priv->rstcr); -+ val |= 0x02; -+ writel(val, priv->rstcr); -+ } -+ -+ timeout = jiffies + HZ; -+ while (time_before(jiffies, timeout)) -+ cpu_relax(); -+ -+} -+ -+static int ls_reboot_probe(struct platform_device *pdev) -+{ -+ ls_reboot_priv = devm_kzalloc(&pdev->dev, -+ sizeof(*ls_reboot_priv), GFP_KERNEL); -+ if (!ls_reboot_priv) { -+ dev_err(&pdev->dev, "out of memory for context\n"); -+ return -ENODEV; -+ } -+ -+ ls_reboot_priv->rstcr = of_iomap(pdev->dev.of_node, 0); -+ if (!ls_reboot_priv->rstcr) { -+ devm_kfree(&pdev->dev, ls_reboot_priv); -+ dev_err(&pdev->dev, "can not map resource\n"); -+ return -ENODEV; -+ } -+ -+ ls_reboot_priv->dev = &pdev->dev; -+ -+ arm_pm_restart = ls_reboot; -+ -+ return 0; -+} -+ -+static struct of_device_id ls_reboot_of_match[] = { -+ { .compatible = "fsl,ls-reset" }, -+ {} -+}; -+ -+static struct platform_driver ls_reboot_driver = { -+ .probe = ls_reboot_probe, -+ .driver = { -+ .name = "ls-reset", -+ .of_match_table = ls_reboot_of_match, -+ }, -+}; -+ -+static int __init ls_reboot_init(void) -+{ -+ return platform_driver_register(&ls_reboot_driver); -+} -+device_initcall(ls_reboot_init); -diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c -index b9ddf0c..894894f 100644 ---- a/drivers/usb/core/config.c -+++ b/drivers/usb/core/config.c -@@ -115,7 +115,8 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, - USB_SS_MULT(desc->bmAttributes) > 3) { - dev_warn(ddev, "Isoc endpoint has Mult of %d in " - "config %d interface %d altsetting %d ep %d: " -- "setting to 3\n", desc->bmAttributes + 1, -+ "setting to 3\n", -+ USB_SS_MULT(desc->bmAttributes), - cfgno, inum, asnum, ep->desc.bEndpointAddress); - ep->ss_ep_comp.bmAttributes = 2; - } -diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c -index d7a6d8b..66be3b4 100644 ---- a/drivers/usb/core/driver.c -+++ b/drivers/usb/core/driver.c -@@ -499,11 +499,15 @@ static int usb_unbind_interface(struct device *dev) - int usb_driver_claim_interface(struct usb_driver *driver, - struct usb_interface *iface, void *priv) - { -- struct device *dev = &iface->dev; -+ struct device *dev; - struct usb_device *udev; - int retval = 0; - int lpm_disable_error; - -+ if (!iface) -+ return -ENODEV; -+ -+ dev = &iface->dev; - if (dev->driver) - return -EBUSY; - -diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c -index efc9531..a4c0b85 100644 ---- a/drivers/usb/core/hcd-pci.c -+++ b/drivers/usb/core/hcd-pci.c -@@ -74,6 +74,15 @@ static void for_each_companion(struct pci_dev *pdev, struct usb_hcd *hcd, - if (companion->bus != pdev->bus || - PCI_SLOT(companion->devfn) != slot) - continue; -+ -+ /* -+ * Companion device should be either UHCI,OHCI or EHCI host -+ * controller, otherwise skip. -+ */ -+ if (companion->class != CL_UHCI && companion->class != CL_OHCI && -+ companion->class != CL_EHCI) -+ continue; -+ - companion_hcd = pci_get_drvdata(companion); - if (!companion_hcd || !companion_hcd->self.root_hub) - continue; -diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c -index 2222899..d8e1d5c 100644 ---- a/drivers/usb/core/hub.c -+++ b/drivers/usb/core/hub.c -@@ -124,6 +124,10 @@ struct usb_hub *usb_hub_to_struct_hub(struct usb_device *hdev) - - static int usb_device_supports_lpm(struct usb_device *udev) - { -+ /* Some devices have trouble with LPM */ -+ if (udev->quirks & USB_QUIRK_NO_LPM) -+ return 0; -+ - /* USB 2.1 (and greater) devices indicate LPM support through - * their USB 2.0 Extended Capabilities BOS descriptor. - */ -@@ -1030,10 +1034,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 +1245,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 +1267,11 @@ 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 */ -@@ -4222,7 +4242,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, - { - struct usb_device *hdev = hub->hdev; - struct usb_hcd *hcd = bus_to_hcd(hdev->bus); -- int i, j, retval; -+ int retries, operations, retval, i; - unsigned delay = HUB_SHORT_RESET_TIME; - enum usb_device_speed oldspeed = udev->speed; - const char *speed; -@@ -4324,7 +4344,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, - * first 8 bytes of the device descriptor to get the ep0 maxpacket - * value. - */ -- for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) { -+ for (retries = 0; retries < GET_DESCRIPTOR_TRIES; (++retries, msleep(100))) { - bool did_new_scheme = false; - - if (use_new_scheme(udev, retry_counter)) { -@@ -4351,7 +4371,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, - * 255 is for WUSB devices, we actually need to use - * 512 (WUSB1.0[4.8.1]). - */ -- for (j = 0; j < 3; ++j) { -+ for (operations = 0; operations < 3; ++operations) { - buf->bMaxPacketSize0 = 0; - r = usb_control_msg(udev, usb_rcvaddr0pipe(), - USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, -@@ -4371,7 +4391,13 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, - r = -EPROTO; - break; - } -- if (r == 0) -+ /* -+ * Some devices time out if they are powered on -+ * when already connected. They need a second -+ * reset. But only on the first attempt, -+ * lest we get into a time out/reset loop -+ */ -+ if (r == 0 || (r == -ETIMEDOUT && retries == 0)) - break; - } - udev->descriptor.bMaxPacketSize0 = -@@ -4403,7 +4429,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, - * authorization will assign the final address. - */ - if (udev->wusb == 0) { -- for (j = 0; j < SET_ADDRESS_TRIES; ++j) { -+ for (operations = 0; operations < SET_ADDRESS_TRIES; ++operations) { - retval = hub_set_address(udev, devnum); - if (retval >= 0) - break; -@@ -4498,6 +4524,8 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, - goto fail; - } - -+ usb_detect_quirks(udev); -+ - if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) { - retval = usb_get_bos_descriptor(udev); - if (!retval) { -@@ -4692,7 +4720,6 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus, - if (status < 0) - goto loop; - -- usb_detect_quirks(udev); - if (udev->quirks & USB_QUIRK_DELAY_INIT) - msleep(1000); - -@@ -5324,9 +5351,6 @@ static int usb_reset_and_verify_device(struct usb_device *udev) - if (udev->usb2_hw_lpm_enabled == 1) - usb_set_usb2_hardware_lpm(udev, 0); - -- bos = udev->bos; -- udev->bos = NULL; -- - /* Disable LPM and LTM while we reset the device and reinstall the alt - * settings. Device-initiated LPM settings, and system exit latency - * settings are cleared when the device is reset, so we have to set -@@ -5335,15 +5359,17 @@ static int usb_reset_and_verify_device(struct usb_device *udev) - ret = usb_unlocked_disable_lpm(udev); - if (ret) { - dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__); -- goto re_enumerate; -+ goto re_enumerate_no_bos; - } - ret = usb_disable_ltm(udev); - if (ret) { - dev_err(&udev->dev, "%s Failed to disable LTM\n.", - __func__); -- goto re_enumerate; -+ goto re_enumerate_no_bos; - } - -+ bos = udev->bos; -+ - for (i = 0; i < SET_CONFIG_TRIES; ++i) { - - /* ep0 maxpacket size may change; let the HCD know about it. -@@ -5435,15 +5461,19 @@ done: - usb_set_usb2_hardware_lpm(udev, 1); - usb_unlocked_enable_lpm(udev); - usb_enable_ltm(udev); -- usb_release_bos_descriptor(udev); -- udev->bos = bos; -+ /* release the new BOS descriptor allocated by hub_port_init() */ -+ if (udev->bos != bos) { -+ usb_release_bos_descriptor(udev); -+ udev->bos = bos; -+ } - return 0; - - re_enumerate: -- /* LPM state doesn't matter when we're about to destroy the device. */ -- hub_port_logical_disconnect(parent_hub, port1); - usb_release_bos_descriptor(udev); - udev->bos = bos; -+re_enumerate_no_bos: -+ /* LPM state doesn't matter when we're about to destroy the device. */ -+ hub_port_logical_disconnect(parent_hub, port1); - return -ENODEV; - } - -diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c -index 8a77a41..6b53fc3 100644 ---- a/drivers/usb/core/quirks.c -+++ b/drivers/usb/core/quirks.c -@@ -196,6 +196,12 @@ static const struct usb_device_id usb_quirk_list[] = { - { USB_DEVICE(0x1a0a, 0x0200), .driver_info = - USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL }, - -+ /* Blackmagic Design Intensity Shuttle */ -+ { USB_DEVICE(0x1edb, 0xbd3b), .driver_info = USB_QUIRK_NO_LPM }, -+ -+ /* Blackmagic Design UltraStudio SDI */ -+ { USB_DEVICE(0x1edb, 0xbd4f), .driver_info = USB_QUIRK_NO_LPM }, -+ - { } /* terminating entry must be last */ - }; - -diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c -index b0f4d52..17eeab8 100644 ---- a/drivers/usb/dwc3/core.c -+++ b/drivers/usb/dwc3/core.c -@@ -673,22 +673,20 @@ static int dwc3_probe(struct platform_device *pdev) - * since it will be requested by the xhci-plat driver. - */ - regs = devm_ioremap_resource(dev, res); -- if (IS_ERR(regs)) -- return PTR_ERR(regs); -+ if (IS_ERR(regs)) { -+ ret = PTR_ERR(regs); -+ goto err0; -+ } - - dwc->regs = regs; - dwc->regs_size = resource_size(res); -- /* -- * restore res->start back to its original value so that, -- * in case the probe is deferred, we don't end up getting error in -- * request the memory region the next time probe is called. -- */ -- res->start -= DWC3_GLOBALS_REGS_START; - - if (node) { - dwc->maximum_speed = of_usb_get_maximum_speed(node); - - dwc->needs_fifo_resize = of_property_read_bool(node, "tx-fifo-resize"); -+ dwc->configure_gfladj = -+ of_property_read_bool(node, "configure-gfladj"); - dwc->dr_mode = of_usb_get_dr_mode(node); - } else if (pdata) { - dwc->maximum_speed = pdata->maximum_speed; -@@ -703,7 +701,7 @@ static int dwc3_probe(struct platform_device *pdev) - - ret = dwc3_core_get_phy(dwc); - if (ret) -- return ret; -+ goto err0; - - spin_lock_init(&dwc->lock); - platform_set_drvdata(pdev, dwc); -@@ -722,7 +720,25 @@ static int dwc3_probe(struct platform_device *pdev) - if (ret) { - dev_err(dwc->dev, "failed to allocate event buffers\n"); - ret = -ENOMEM; -- goto err0; -+ goto err1; -+ } -+ -+ /* Adjust Frame Length */ -+ if (dwc->configure_gfladj) -+ dwc3_writel(dwc->regs, DWC3_GFLADJ, GFLADJ_30MHZ_REG_SEL | -+ GFLADJ_30MHZ(GFLADJ_30MHZ_DEFAULT)); -+ -+ /* Change burst beat and outstanding pipelined transfers requests */ -+ dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, -+ (dwc3_readl(dwc->regs, DWC3_GSBUSCFG0) & ~0xff) | 0xf); -+ dwc3_writel(dwc->regs, DWC3_GSBUSCFG1, -+ dwc3_readl(dwc->regs, DWC3_GSBUSCFG1) | 0xf00); -+ -+ /* Enable Snooping */ -+ if (node && of_dma_is_coherent(node)) { -+ dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, -+ dwc3_readl(dwc->regs, DWC3_GSBUSCFG0) | 0x22220000); -+ dev_dbg(dev, "enabled snooping for usb\n"); - } - - if (IS_ENABLED(CONFIG_USB_DWC3_HOST)) -@@ -736,65 +752,81 @@ static int dwc3_probe(struct platform_device *pdev) - ret = dwc3_core_init(dwc); - if (ret) { - dev_err(dev, "failed to initialize core\n"); -- goto err0; -+ goto err1; - } - - usb_phy_set_suspend(dwc->usb2_phy, 0); - usb_phy_set_suspend(dwc->usb3_phy, 0); - ret = phy_power_on(dwc->usb2_generic_phy); - if (ret < 0) -- goto err1; -+ goto err2; - - ret = phy_power_on(dwc->usb3_generic_phy); - if (ret < 0) -- goto err_usb2phy_power; -+ goto err3; - - ret = dwc3_event_buffers_setup(dwc); - if (ret) { - dev_err(dwc->dev, "failed to setup event buffers\n"); -- goto err_usb3phy_power; -+ goto err4; - } - - ret = dwc3_core_init_mode(dwc); - if (ret) -- goto err2; -+ goto err5; - - ret = dwc3_debugfs_init(dwc); - if (ret) { - dev_err(dev, "failed to initialize debugfs\n"); -- goto err3; -+ goto err6; - } - - pm_runtime_allow(dev); - - return 0; - --err3: -+err6: - dwc3_core_exit_mode(dwc); - --err2: -+err5: - dwc3_event_buffers_cleanup(dwc); - --err_usb3phy_power: -+err4: - phy_power_off(dwc->usb3_generic_phy); - --err_usb2phy_power: -+err3: - phy_power_off(dwc->usb2_generic_phy); - --err1: -+err2: - usb_phy_set_suspend(dwc->usb2_phy, 1); - usb_phy_set_suspend(dwc->usb3_phy, 1); - dwc3_core_exit(dwc); - --err0: -+err1: - dwc3_free_event_buffers(dwc); - -+err0: -+ /* -+ * restore res->start back to its original value so that, in case the -+ * probe is deferred, we don't end up getting error in request the -+ * memory region the next time probe is called. -+ */ -+ res->start -= DWC3_GLOBALS_REGS_START; -+ - return ret; - } - - static int dwc3_remove(struct platform_device *pdev) - { - struct dwc3 *dwc = platform_get_drvdata(pdev); -+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ -+ /* -+ * restore res->start back to its original value so that, in case the -+ * probe is deferred, we don't end up getting error in request the -+ * memory region the next time probe is called. -+ */ -+ res->start -= DWC3_GLOBALS_REGS_START; - - dwc3_debugfs_exit(dwc); - dwc3_core_exit_mode(dwc); -diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h -index 66f6256..aec8953 100644 ---- a/drivers/usb/dwc3/core.h -+++ b/drivers/usb/dwc3/core.h -@@ -26,6 +26,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -123,6 +124,7 @@ - #define DWC3_GEVNTCOUNT(n) (0xc40c + (n * 0x10)) - - #define DWC3_GHWPARAMS8 0xc600 -+#define DWC3_GFLADJ 0xc630 - - /* Device Registers */ - #define DWC3_DCFG 0xc700 -@@ -210,6 +212,11 @@ - #define DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(n) (((n) & (0x0f << 13)) >> 13) - #define DWC3_MAX_HIBER_SCRATCHBUFS 15 - -+/* Global Frame Length Adjustment Register */ -+#define GFLADJ_30MHZ_REG_SEL (1 << 7) -+#define GFLADJ_30MHZ(n) ((n) & 0x3f) -+#define GFLADJ_30MHZ_DEFAULT 0x20 -+ - /* Device Configuration Register */ - #define DWC3_DCFG_DEVADDR(addr) ((addr) << 3) - #define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f) -@@ -766,6 +773,7 @@ struct dwc3 { - unsigned has_hibernation:1; - unsigned is_selfpowered:1; - unsigned needs_fifo_resize:1; -+ unsigned configure_gfladj:1; - unsigned pullups_connected:1; - unsigned resize_fifos:1; - unsigned setup_packet_pending:1; -diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c -index dcb8ca0..c41d46c 100644 ---- a/drivers/usb/dwc3/host.c -+++ b/drivers/usb/dwc3/host.c -@@ -39,6 +39,12 @@ int dwc3_host_init(struct dwc3 *dwc) - xhci->dev.dma_mask = dwc->dev->dma_mask; - xhci->dev.dma_parms = dwc->dev->dma_parms; - -+ /* set DMA operations */ -+ if (dwc->dev->of_node && of_dma_is_coherent(dwc->dev->of_node)) { -+ xhci->dev.archdata.dma_ops = dwc->dev->archdata.dma_ops; -+ dev_dbg(dwc->dev, "set dma_ops for usb\n"); -+ } -+ - dwc->xhci = xhci; - - ret = platform_device_add_resources(xhci, dwc->xhci_resources, -diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c -index 7e5c90e..c6027ac 100644 ---- a/drivers/usb/host/xhci-pci.c -+++ b/drivers/usb/host/xhci-pci.c -@@ -23,10 +23,17 @@ - #include - #include - #include -+#include - - #include "xhci.h" - #include "xhci-trace.h" - -+#define SSIC_PORT_NUM 2 -+#define SSIC_PORT_CFG2 0x880c -+#define SSIC_PORT_CFG2_OFFSET 0x30 -+#define PROG_DONE (1 << 30) -+#define SSIC_PORT_UNUSED (1 << 31) -+ - /* Device for a quirk */ - #define PCI_VENDOR_ID_FRESCO_LOGIC 0x1b73 - #define PCI_DEVICE_ID_FRESCO_LOGIC_PDK 0x1000 -@@ -40,6 +47,8 @@ - #define PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI 0x22b5 - #define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_XHCI 0xa12f - #define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI 0x9d2f -+#define PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI 0x0aa8 -+#define PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI 0x1aa8 - - static const char hcd_name[] = "xhci_hcd"; - -@@ -140,9 +149,15 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) - if (pdev->vendor == PCI_VENDOR_ID_INTEL && - (pdev->device == PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI || - pdev->device == PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_XHCI || -- pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI)) { -+ pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI || -+ pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI || -+ pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI)) { - xhci->quirks |= XHCI_PME_STUCK_QUIRK; - } -+ if (pdev->vendor == PCI_VENDOR_ID_INTEL && -+ pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI) { -+ xhci->quirks |= XHCI_SSIC_PORT_UNUSED; -+ } - if (pdev->vendor == PCI_VENDOR_ID_ETRON && - pdev->device == PCI_DEVICE_ID_EJ168) { - xhci->quirks |= XHCI_RESET_ON_RESUME; -@@ -169,20 +184,18 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) - "QUIRK: Resetting on resume"); - } - --/* -- * Make sure PME works on some Intel xHCI controllers by writing 1 to clear -- * the Internal PME flag bit in vendor specific PMCTRL register at offset 0x80a4 -- */ --static void xhci_pme_quirk(struct xhci_hcd *xhci) -+#ifdef CONFIG_ACPI -+static void xhci_pme_acpi_rtd3_enable(struct pci_dev *dev) - { -- u32 val; -- void __iomem *reg; -- -- reg = (void __iomem *) xhci->cap_regs + 0x80a4; -- val = readl(reg); -- writel(val | BIT(28), reg); -- readl(reg); -+ static const u8 intel_dsm_uuid[] = { -+ 0xb7, 0x0c, 0x34, 0xac, 0x01, 0xe9, 0xbf, 0x45, -+ 0xb7, 0xe6, 0x2b, 0x34, 0xec, 0x93, 0x1e, 0x23, -+ }; -+ acpi_evaluate_dsm(ACPI_HANDLE(&dev->dev), intel_dsm_uuid, 3, 1, NULL); - } -+#else -+ static void xhci_pme_acpi_rtd3_enable(struct pci_dev *dev) { } -+#endif /* CONFIG_ACPI */ - - /* called during probe() after chip reset completes */ - static int xhci_pci_setup(struct usb_hcd *hcd) -@@ -263,6 +276,9 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) - HCC_MAX_PSA(xhci->hcc_params) >= 4) - xhci->shared_hcd->can_do_streams = 1; - -+ if (xhci->quirks & XHCI_PME_STUCK_QUIRK) -+ xhci_pme_acpi_rtd3_enable(dev); -+ - /* USB-2 and USB-3 roothubs initialized, allow runtime pm suspend */ - pm_runtime_put_noidle(&dev->dev); - -@@ -282,6 +298,7 @@ static void xhci_pci_remove(struct pci_dev *dev) - struct xhci_hcd *xhci; - - xhci = hcd_to_xhci(pci_get_drvdata(dev)); -+ xhci->xhc_state |= XHCI_STATE_REMOVING; - if (xhci->shared_hcd) { - usb_remove_hcd(xhci->shared_hcd); - usb_put_hcd(xhci->shared_hcd); -@@ -296,10 +313,65 @@ static void xhci_pci_remove(struct pci_dev *dev) - } - - #ifdef CONFIG_PM -+/* -+ * In some Intel xHCI controllers, in order to get D3 working, -+ * through a vendor specific SSIC CONFIG register at offset 0x883c, -+ * SSIC PORT need to be marked as "unused" before putting xHCI -+ * into D3. After D3 exit, the SSIC port need to be marked as "used". -+ * Without this change, xHCI might not enter D3 state. -+ */ -+static void xhci_ssic_port_unused_quirk(struct usb_hcd *hcd, bool suspend) -+{ -+ struct xhci_hcd *xhci = hcd_to_xhci(hcd); -+ u32 val; -+ void __iomem *reg; -+ int i; -+ -+ for (i = 0; i < SSIC_PORT_NUM; i++) { -+ reg = (void __iomem *) xhci->cap_regs + -+ SSIC_PORT_CFG2 + -+ i * SSIC_PORT_CFG2_OFFSET; -+ -+ /* Notify SSIC that SSIC profile programming is not done. */ -+ val = readl(reg) & ~PROG_DONE; -+ writel(val, reg); -+ -+ /* Mark SSIC port as unused(suspend) or used(resume) */ -+ val = readl(reg); -+ if (suspend) -+ val |= SSIC_PORT_UNUSED; -+ else -+ val &= ~SSIC_PORT_UNUSED; -+ writel(val, reg); -+ -+ /* Notify SSIC that SSIC profile programming is done */ -+ val = readl(reg) | PROG_DONE; -+ writel(val, reg); -+ readl(reg); -+ } -+} -+ -+/* -+ * Make sure PME works on some Intel xHCI controllers by writing 1 to clear -+ * the Internal PME flag bit in vendor specific PMCTRL register at offset 0x80a4 -+ */ -+static void xhci_pme_quirk(struct usb_hcd *hcd) -+{ -+ struct xhci_hcd *xhci = hcd_to_xhci(hcd); -+ void __iomem *reg; -+ u32 val; -+ -+ reg = (void __iomem *) xhci->cap_regs + 0x80a4; -+ val = readl(reg); -+ writel(val | BIT(28), reg); -+ readl(reg); -+} -+ - static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) - { - struct xhci_hcd *xhci = hcd_to_xhci(hcd); - struct pci_dev *pdev = to_pci_dev(hcd->self.controller); -+ int ret; - - /* - * Systems with the TI redriver that loses port status change events -@@ -309,9 +381,16 @@ static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) - pdev->no_d3cold = true; - - if (xhci->quirks & XHCI_PME_STUCK_QUIRK) -- xhci_pme_quirk(xhci); -+ xhci_pme_quirk(hcd); -+ -+ if (xhci->quirks & XHCI_SSIC_PORT_UNUSED) -+ xhci_ssic_port_unused_quirk(hcd, true); - -- return xhci_suspend(xhci, do_wakeup); -+ ret = xhci_suspend(xhci, do_wakeup); -+ if (ret && (xhci->quirks & XHCI_SSIC_PORT_UNUSED)) -+ xhci_ssic_port_unused_quirk(hcd, false); -+ -+ return ret; - } - - static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated) -@@ -341,8 +420,11 @@ static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated) - if (pdev->vendor == PCI_VENDOR_ID_INTEL) - usb_enable_intel_xhci_ports(pdev); - -+ if (xhci->quirks & XHCI_SSIC_PORT_UNUSED) -+ xhci_ssic_port_unused_quirk(hcd, false); -+ - if (xhci->quirks & XHCI_PME_STUCK_QUIRK) -- xhci_pme_quirk(xhci); -+ xhci_pme_quirk(hcd); - - retval = xhci_resume(xhci, hibernated); - return retval; -diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c -index 1e5fb8c..04e7525 100644 ---- a/drivers/usb/host/xhci-ring.c -+++ b/drivers/usb/host/xhci-ring.c -@@ -3840,8 +3840,12 @@ static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd, - { - int reserved_trbs = xhci->cmd_ring_reserved_trbs; - int ret; -- if (xhci->xhc_state & XHCI_STATE_DYING) -+ -+ if ((xhci->xhc_state & XHCI_STATE_DYING) || -+ (xhci->xhc_state & XHCI_STATE_HALTED)) { -+ xhci_dbg(xhci, "xHCI dying or halted, can't queue_command\n"); - return -ESHUTDOWN; -+ } - - if (!command_must_succeed) - reserved_trbs++; -diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c -index 98380fa..f951b75 100644 ---- a/drivers/usb/host/xhci.c -+++ b/drivers/usb/host/xhci.c -@@ -147,7 +147,8 @@ static int xhci_start(struct xhci_hcd *xhci) - "waited %u microseconds.\n", - XHCI_MAX_HALT_USEC); - if (!ret) -- xhci->xhc_state &= ~(XHCI_STATE_HALTED | XHCI_STATE_DYING); -+ /* clear state flags. Including dying, halted or removing */ -+ xhci->xhc_state = 0; - - return ret; - } -@@ -1102,8 +1103,8 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) - /* Resume root hubs only when have pending events. */ - status = readl(&xhci->op_regs->status); - if (status & STS_EINT) { -- usb_hcd_resume_root_hub(hcd); - usb_hcd_resume_root_hub(xhci->shared_hcd); -+ usb_hcd_resume_root_hub(hcd); - } - } - -@@ -1118,10 +1119,10 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) - - /* Re-enable port polling. */ - xhci_dbg(xhci, "%s: starting port polling.\n", __func__); -- set_bit(HCD_FLAG_POLL_RH, &hcd->flags); -- usb_hcd_poll_rh_status(hcd); - set_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags); - usb_hcd_poll_rh_status(xhci->shared_hcd); -+ set_bit(HCD_FLAG_POLL_RH, &hcd->flags); -+ usb_hcd_poll_rh_status(hcd); - - return retval; - } -@@ -1548,7 +1549,9 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) - xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, - "HW died, freeing TD."); - urb_priv = urb->hcpriv; -- for (i = urb_priv->td_cnt; i < urb_priv->length; i++) { -+ for (i = urb_priv->td_cnt; -+ i < urb_priv->length && xhci->devs[urb->dev->slot_id]; -+ i++) { - td = urb_priv->td[i]; - if (!list_empty(&td->td_list)) - list_del_init(&td->td_list); -@@ -2751,7 +2754,8 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) - if (ret <= 0) - return ret; - xhci = hcd_to_xhci(hcd); -- if (xhci->xhc_state & XHCI_STATE_DYING) -+ if ((xhci->xhc_state & XHCI_STATE_DYING) || -+ (xhci->xhc_state & XHCI_STATE_REMOVING)) - return -ENODEV; - - xhci_dbg(xhci, "%s called for udev %p\n", __func__, udev); -@@ -3793,7 +3797,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, - u64 temp_64; - struct xhci_command *command; - -- if (xhci->xhc_state) /* dying or halted */ -+ if (xhci->xhc_state) /* dying, removing or halted */ - return -EINVAL; - - if (!udev->slot_id) { -@@ -4912,6 +4916,16 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) - goto error; - xhci_dbg(xhci, "Reset complete\n"); - -+ /* -+ * On some xHCI controllers (e.g. R-Car SoCs), the AC64 bit (bit 0) -+ * of HCCPARAMS1 is set to 1. However, the xHCs don't support 64-bit -+ * address memory pointers actually. So, this driver clears the AC64 -+ * bit of xhci->hcc_params to call dma_set_coherent_mask(dev, -+ * DMA_BIT_MASK(32)) in this xhci_gen_setup(). -+ */ -+ if (xhci->quirks & XHCI_NO_64BIT_SUPPORT) -+ xhci->hcc_params &= ~BIT(0); -+ - /* Set dma_mask and coherent_dma_mask to 64-bits, - * if xHC supports 64-bit addressing */ - if (HCC_64BIT_ADDR(xhci->hcc_params) && -diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h -index 54f386f..3850cb2 100644 ---- a/drivers/usb/host/xhci.h -+++ b/drivers/usb/host/xhci.h -@@ -1531,6 +1531,7 @@ struct xhci_hcd { - */ - #define XHCI_STATE_DYING (1 << 0) - #define XHCI_STATE_HALTED (1 << 1) -+#define XHCI_STATE_REMOVING (1 << 2) - /* Statistics */ - int error_bitmask; - unsigned int quirks; -@@ -1565,6 +1566,8 @@ struct xhci_hcd { - /* For controllers with a broken beyond repair streams implementation */ - #define XHCI_BROKEN_STREAMS (1 << 19) - #define XHCI_PME_STUCK_QUIRK (1 << 20) -+#define XHCI_SSIC_PORT_UNUSED (1 << 22) -+#define XHCI_NO_64BIT_SUPPORT (1 << 23) - unsigned int num_active_eps; - unsigned int limit_active_eps; - /* There are two roothubs to keep track of bus suspend info for */ -diff --git a/drivers/vfio/pci/vfio_pci_intrs.c b/drivers/vfio/pci/vfio_pci_intrs.c -index 553212f..e8d695b 100644 ---- a/drivers/vfio/pci/vfio_pci_intrs.c -+++ b/drivers/vfio/pci/vfio_pci_intrs.c -@@ -560,7 +560,7 @@ static int vfio_msi_set_vector_signal(struct vfio_pci_device *vdev, - struct msi_msg msg; - - get_cached_msi_msg(irq, &msg); -- write_msi_msg(irq, &msg); -+ pci_write_msi_msg(irq, &msg); - } - - ret = request_irq(irq, vfio_msihandler, 0, -diff --git a/include/asm-generic/msi.h b/include/asm-generic/msi.h -new file mode 100644 -index 0000000..61c58d8 ---- /dev/null -+++ b/include/asm-generic/msi.h -@@ -0,0 +1,32 @@ -+#ifndef __ASM_GENERIC_MSI_H -+#define __ASM_GENERIC_MSI_H -+ -+#include -+ -+#ifndef NUM_MSI_ALLOC_SCRATCHPAD_REGS -+# define NUM_MSI_ALLOC_SCRATCHPAD_REGS 2 -+#endif -+ -+struct msi_desc; -+ -+/** -+ * struct msi_alloc_info - Default structure for MSI interrupt allocation. -+ * @desc: Pointer to msi descriptor -+ * @hwirq: Associated hw interrupt number in the domain -+ * @scratchpad: Storage for implementation specific scratch data -+ * -+ * Architectures can provide their own implementation by not including -+ * asm-generic/msi.h into their arch specific header file. -+ */ -+typedef struct msi_alloc_info { -+ struct msi_desc *desc; -+ irq_hw_number_t hwirq; -+ union { -+ unsigned long ul; -+ void *ptr; -+ } scratchpad[NUM_MSI_ALLOC_SCRATCHPAD_REGS]; -+} msi_alloc_info_t; -+ -+#define GENERIC_MSI_DOMAIN_OPS 1 -+ -+#endif -diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h -index aa70cbd..bee5d68 100644 ---- a/include/asm-generic/vmlinux.lds.h -+++ b/include/asm-generic/vmlinux.lds.h -@@ -164,6 +164,7 @@ - #define CLKSRC_OF_TABLES() OF_TABLE(CONFIG_CLKSRC_OF, clksrc) - #define IRQCHIP_OF_MATCH_TABLE() OF_TABLE(CONFIG_IRQCHIP, irqchip) - #define CLK_OF_TABLES() OF_TABLE(CONFIG_COMMON_CLK, clk) -+#define IOMMU_OF_TABLES() OF_TABLE(CONFIG_OF_IOMMU, iommu) - #define RESERVEDMEM_OF_TABLES() OF_TABLE(CONFIG_OF_RESERVED_MEM, reservedmem) - #define CPU_METHOD_OF_TABLES() OF_TABLE(CONFIG_SMP, cpu_method) - #define EARLYCON_OF_TABLES() OF_TABLE(CONFIG_SERIAL_EARLYCON, earlycon) -@@ -497,6 +498,7 @@ - CLK_OF_TABLES() \ - RESERVEDMEM_OF_TABLES() \ - CLKSRC_OF_TABLES() \ -+ IOMMU_OF_TABLES() \ - CPU_METHOD_OF_TABLES() \ - KERNEL_DTB() \ - IRQCHIP_OF_MATCH_TABLE() \ -diff --git a/include/linux/acpi.h b/include/linux/acpi.h -index 1c7eaa7..d017dbf 100644 ---- a/include/linux/acpi.h -+++ b/include/linux/acpi.h -@@ -27,6 +27,7 @@ - - #include - #include /* for struct resource */ -+#include - #include - #include - -@@ -290,11 +291,6 @@ unsigned long acpi_dev_irq_flags(u8 triggering, u8 polarity, u8 shareable); - bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index, - struct resource *res); - --struct resource_list_entry { -- struct list_head node; -- struct resource res; --}; -- - void acpi_dev_free_resource_list(struct list_head *list); - int acpi_dev_get_resources(struct acpi_device *adev, struct list_head *list, - int (*preproc)(struct acpi_resource *, void *), -diff --git a/include/linux/device.h b/include/linux/device.h -index ce1f216..941d97b 100644 ---- a/include/linux/device.h -+++ b/include/linux/device.h -@@ -690,6 +690,8 @@ struct acpi_dev_node { - * along with subsystem-level and driver-level callbacks. - * @pins: For device pin management. - * See Documentation/pinctrl.txt for details. -+ * @msi_list: Hosts MSI descriptors -+ * @msi_domain: The generic MSI domain this device is using. - * @numa_node: NUMA node this device is close to. - * @dma_mask: Dma mask (if dma'ble device). - * @coherent_dma_mask: Like dma_mask, but for alloc_coherent mapping as not all -@@ -750,9 +752,15 @@ struct device { - struct dev_pm_info power; - struct dev_pm_domain *pm_domain; - -+#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN -+ struct irq_domain *msi_domain; -+#endif - #ifdef CONFIG_PINCTRL - struct dev_pin_info *pins; - #endif -+#ifdef CONFIG_GENERIC_MSI_IRQ -+ struct list_head msi_list; -+#endif - - #ifdef CONFIG_NUMA - int numa_node; /* NUMA node this device is close to */ -@@ -837,6 +845,22 @@ static inline void set_dev_node(struct device *dev, int node) - } - #endif - -+static inline struct irq_domain *dev_get_msi_domain(const struct device *dev) -+{ -+#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN -+ return dev->msi_domain; -+#else -+ return NULL; -+#endif -+} -+ -+static inline void dev_set_msi_domain(struct device *dev, struct irq_domain *d) -+{ -+#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN -+ dev->msi_domain = d; -+#endif -+} -+ - static inline void *dev_get_drvdata(const struct device *dev) - { - return dev->driver_data; -diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h -index d5d3881..c3007cb 100644 ---- a/include/linux/dma-mapping.h -+++ b/include/linux/dma-mapping.h -@@ -129,11 +129,14 @@ static inline int dma_coerce_mask_and_coherent(struct device *dev, u64 mask) - - extern u64 dma_get_required_mask(struct device *dev); - --#ifndef set_arch_dma_coherent_ops --static inline int set_arch_dma_coherent_ops(struct device *dev) --{ -- return 0; --} -+#ifndef arch_setup_dma_ops -+static inline void arch_setup_dma_ops(struct device *dev, u64 dma_base, -+ u64 size, struct iommu_ops *iommu, -+ bool coherent) { } -+#endif -+ -+#ifndef arch_teardown_dma_ops -+static inline void arch_teardown_dma_ops(struct device *dev) { } - #endif - - static inline unsigned int dma_get_max_seg_size(struct device *dev) -diff --git a/include/linux/fsl/guts.h b/include/linux/fsl/guts.h -new file mode 100644 -index 0000000..84d971f ---- /dev/null -+++ b/include/linux/fsl/guts.h -@@ -0,0 +1,192 @@ -+/** -+ * Freecale 85xx and 86xx Global Utilties register set -+ * -+ * Authors: Jeff Brown -+ * Timur Tabi -+ * -+ * Copyright 2004,2007,2012 Freescale Semiconductor, 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. -+ */ -+ -+#ifndef __FSL_GUTS_H__ -+#define __FSL_GUTS_H__ -+ -+#include -+ -+/** -+ * Global Utility Registers. -+ * -+ * Not all registers defined in this structure are available on all chips, so -+ * you are expected to know whether a given register actually exists on your -+ * chip before you access it. -+ * -+ * Also, some registers are similar on different chips but have slightly -+ * different names. In these cases, one name is chosen to avoid extraneous -+ * #ifdefs. -+ */ -+struct ccsr_guts { -+ __be32 porpllsr; /* 0x.0000 - POR PLL Ratio Status Register */ -+ __be32 porbmsr; /* 0x.0004 - POR Boot Mode Status Register */ -+ __be32 porimpscr; /* 0x.0008 - POR I/O Impedance Status and Control Register */ -+ __be32 pordevsr; /* 0x.000c - POR I/O Device Status Register */ -+ __be32 pordbgmsr; /* 0x.0010 - POR Debug Mode Status Register */ -+ __be32 pordevsr2; /* 0x.0014 - POR device status register 2 */ -+ u8 res018[0x20 - 0x18]; -+ __be32 porcir; /* 0x.0020 - POR Configuration Information Register */ -+ u8 res024[0x30 - 0x24]; -+ __be32 gpiocr; /* 0x.0030 - GPIO Control Register */ -+ u8 res034[0x40 - 0x34]; -+ __be32 gpoutdr; /* 0x.0040 - General-Purpose Output Data Register */ -+ u8 res044[0x50 - 0x44]; -+ __be32 gpindr; /* 0x.0050 - General-Purpose Input Data Register */ -+ u8 res054[0x60 - 0x54]; -+ __be32 pmuxcr; /* 0x.0060 - Alternate Function Signal Multiplex Control */ -+ __be32 pmuxcr2; /* 0x.0064 - Alternate function signal multiplex control 2 */ -+ __be32 dmuxcr; /* 0x.0068 - DMA Mux Control Register */ -+ u8 res06c[0x70 - 0x6c]; -+ __be32 devdisr; /* 0x.0070 - Device Disable Control */ -+#define CCSR_GUTS_DEVDISR_TB1 0x00001000 -+#define CCSR_GUTS_DEVDISR_TB0 0x00004000 -+ __be32 devdisr2; /* 0x.0074 - Device Disable Control 2 */ -+ u8 res078[0x7c - 0x78]; -+ __be32 pmjcr; /* 0x.007c - 4 Power Management Jog Control Register */ -+ __be32 powmgtcsr; /* 0x.0080 - Power Management Status and Control Register */ -+ __be32 pmrccr; /* 0x.0084 - Power Management Reset Counter Configuration Register */ -+ __be32 pmpdccr; /* 0x.0088 - Power Management Power Down Counter Configuration Register */ -+ __be32 pmcdr; /* 0x.008c - 4Power management clock disable register */ -+ __be32 mcpsumr; /* 0x.0090 - Machine Check Summary Register */ -+ __be32 rstrscr; /* 0x.0094 - Reset Request Status and Control Register */ -+ __be32 ectrstcr; /* 0x.0098 - Exception reset control register */ -+ __be32 autorstsr; /* 0x.009c - Automatic reset status register */ -+ __be32 pvr; /* 0x.00a0 - Processor Version Register */ -+ __be32 svr; /* 0x.00a4 - System Version Register */ -+ u8 res0a8[0xb0 - 0xa8]; -+ __be32 rstcr; /* 0x.00b0 - Reset Control Register */ -+ u8 res0b4[0xc0 - 0xb4]; -+ __be32 iovselsr; /* 0x.00c0 - I/O voltage select status register -+ Called 'elbcvselcr' on 86xx SOCs */ -+ u8 res0c4[0x100 - 0xc4]; -+ __be32 rcwsr[16]; /* 0x.0100 - Reset Control Word Status registers -+ There are 16 registers */ -+ u8 res140[0x224 - 0x140]; -+ __be32 iodelay1; /* 0x.0224 - IO delay control register 1 */ -+ __be32 iodelay2; /* 0x.0228 - IO delay control register 2 */ -+ u8 res22c[0x604 - 0x22c]; -+ __be32 pamubypenr; /* 0x.604 - PAMU bypass enable register */ -+ u8 res608[0x800 - 0x608]; -+ __be32 clkdvdr; /* 0x.0800 - Clock Divide Register */ -+ u8 res804[0x900 - 0x804]; -+ __be32 ircr; /* 0x.0900 - Infrared Control Register */ -+ u8 res904[0x908 - 0x904]; -+ __be32 dmacr; /* 0x.0908 - DMA Control Register */ -+ u8 res90c[0x914 - 0x90c]; -+ __be32 elbccr; /* 0x.0914 - eLBC Control Register */ -+ u8 res918[0xb20 - 0x918]; -+ __be32 ddr1clkdr; /* 0x.0b20 - DDR1 Clock Disable Register */ -+ __be32 ddr2clkdr; /* 0x.0b24 - DDR2 Clock Disable Register */ -+ __be32 ddrclkdr; /* 0x.0b28 - DDR Clock Disable Register */ -+ u8 resb2c[0xe00 - 0xb2c]; -+ __be32 clkocr; /* 0x.0e00 - Clock Out Select Register */ -+ u8 rese04[0xe10 - 0xe04]; -+ __be32 ddrdllcr; /* 0x.0e10 - DDR DLL Control Register */ -+ u8 rese14[0xe20 - 0xe14]; -+ __be32 lbcdllcr; /* 0x.0e20 - LBC DLL Control Register */ -+ __be32 cpfor; /* 0x.0e24 - L2 charge pump fuse override register */ -+ u8 rese28[0xf04 - 0xe28]; -+ __be32 srds1cr0; /* 0x.0f04 - SerDes1 Control Register 0 */ -+ __be32 srds1cr1; /* 0x.0f08 - SerDes1 Control Register 0 */ -+ u8 resf0c[0xf2c - 0xf0c]; -+ __be32 itcr; /* 0x.0f2c - Internal transaction control register */ -+ u8 resf30[0xf40 - 0xf30]; -+ __be32 srds2cr0; /* 0x.0f40 - SerDes2 Control Register 0 */ -+ __be32 srds2cr1; /* 0x.0f44 - SerDes2 Control Register 0 */ -+} __attribute__ ((packed)); -+ -+ -+/* Alternate function signal multiplex control */ -+#define MPC85xx_PMUXCR_QE(x) (0x8000 >> (x)) -+ -+#ifdef CONFIG_PPC_86xx -+ -+#define CCSR_GUTS_DMACR_DEV_SSI 0 /* DMA controller/channel set to SSI */ -+#define CCSR_GUTS_DMACR_DEV_IR 1 /* DMA controller/channel set to IR */ -+ -+/* -+ * Set the DMACR register in the GUTS -+ * -+ * The DMACR register determines the source of initiated transfers for each -+ * channel on each DMA controller. Rather than have a bunch of repetitive -+ * macros for the bit patterns, we just have a function that calculates -+ * them. -+ * -+ * guts: Pointer to GUTS structure -+ * co: The DMA controller (0 or 1) -+ * ch: The channel on the DMA controller (0, 1, 2, or 3) -+ * device: The device to set as the source (CCSR_GUTS_DMACR_DEV_xx) -+ */ -+static inline void guts_set_dmacr(struct ccsr_guts __iomem *guts, -+ unsigned int co, unsigned int ch, unsigned int device) -+{ -+ unsigned int shift = 16 + (8 * (1 - co) + 2 * (3 - ch)); -+ -+ clrsetbits_be32(&guts->dmacr, 3 << shift, device << shift); -+} -+ -+#define CCSR_GUTS_PMUXCR_LDPSEL 0x00010000 -+#define CCSR_GUTS_PMUXCR_SSI1_MASK 0x0000C000 /* Bitmask for SSI1 */ -+#define CCSR_GUTS_PMUXCR_SSI1_LA 0x00000000 /* Latched address */ -+#define CCSR_GUTS_PMUXCR_SSI1_HI 0x00004000 /* High impedance */ -+#define CCSR_GUTS_PMUXCR_SSI1_SSI 0x00008000 /* Used for SSI1 */ -+#define CCSR_GUTS_PMUXCR_SSI2_MASK 0x00003000 /* Bitmask for SSI2 */ -+#define CCSR_GUTS_PMUXCR_SSI2_LA 0x00000000 /* Latched address */ -+#define CCSR_GUTS_PMUXCR_SSI2_HI 0x00001000 /* High impedance */ -+#define CCSR_GUTS_PMUXCR_SSI2_SSI 0x00002000 /* Used for SSI2 */ -+#define CCSR_GUTS_PMUXCR_LA_22_25_LA 0x00000000 /* Latched Address */ -+#define CCSR_GUTS_PMUXCR_LA_22_25_HI 0x00000400 /* High impedance */ -+#define CCSR_GUTS_PMUXCR_DBGDRV 0x00000200 /* Signals not driven */ -+#define CCSR_GUTS_PMUXCR_DMA2_0 0x00000008 -+#define CCSR_GUTS_PMUXCR_DMA2_3 0x00000004 -+#define CCSR_GUTS_PMUXCR_DMA1_0 0x00000002 -+#define CCSR_GUTS_PMUXCR_DMA1_3 0x00000001 -+ -+/* -+ * Set the DMA external control bits in the GUTS -+ * -+ * The DMA external control bits in the PMUXCR are only meaningful for -+ * channels 0 and 3. Any other channels are ignored. -+ * -+ * guts: Pointer to GUTS structure -+ * co: The DMA controller (0 or 1) -+ * ch: The channel on the DMA controller (0, 1, 2, or 3) -+ * value: the new value for the bit (0 or 1) -+ */ -+static inline void guts_set_pmuxcr_dma(struct ccsr_guts __iomem *guts, -+ unsigned int co, unsigned int ch, unsigned int value) -+{ -+ if ((ch == 0) || (ch == 3)) { -+ unsigned int shift = 2 * (co + 1) - (ch & 1) - 1; -+ -+ clrsetbits_be32(&guts->pmuxcr, 1 << shift, value << shift); -+ } -+} -+ -+#define CCSR_GUTS_CLKDVDR_PXCKEN 0x80000000 -+#define CCSR_GUTS_CLKDVDR_SSICKEN 0x20000000 -+#define CCSR_GUTS_CLKDVDR_PXCKINV 0x10000000 -+#define CCSR_GUTS_CLKDVDR_PXCKDLY_SHIFT 25 -+#define CCSR_GUTS_CLKDVDR_PXCKDLY_MASK 0x06000000 -+#define CCSR_GUTS_CLKDVDR_PXCKDLY(x) \ -+ (((x) & 3) << CCSR_GUTS_CLKDVDR_PXCKDLY_SHIFT) -+#define CCSR_GUTS_CLKDVDR_PXCLK_SHIFT 16 -+#define CCSR_GUTS_CLKDVDR_PXCLK_MASK 0x001F0000 -+#define CCSR_GUTS_CLKDVDR_PXCLK(x) (((x) & 31) << CCSR_GUTS_CLKDVDR_PXCLK_SHIFT) -+#define CCSR_GUTS_CLKDVDR_SSICLK_MASK 0x000000FF -+#define CCSR_GUTS_CLKDVDR_SSICLK(x) ((x) & CCSR_GUTS_CLKDVDR_SSICLK_MASK) -+ -+#endif -+ -+#endif -diff --git a/include/linux/iommu.h b/include/linux/iommu.h -index e6a7c9f..04229cb 100644 ---- a/include/linux/iommu.h -+++ b/include/linux/iommu.h -@@ -21,13 +21,15 @@ - - #include - #include -+#include - #include -+#include - #include - - #define IOMMU_READ (1 << 0) - #define IOMMU_WRITE (1 << 1) - #define IOMMU_CACHE (1 << 2) /* DMA cache coherency */ --#define IOMMU_EXEC (1 << 3) -+#define IOMMU_NOEXEC (1 << 3) - - struct iommu_ops; - struct iommu_group; -@@ -49,9 +51,33 @@ struct iommu_domain_geometry { - bool force_aperture; /* DMA only allowed in mappable range? */ - }; - -+/* Domain feature flags */ -+#define __IOMMU_DOMAIN_PAGING (1U << 0) /* Support for iommu_map/unmap */ -+#define __IOMMU_DOMAIN_DMA_API (1U << 1) /* Domain for use in DMA-API -+ implementation */ -+#define __IOMMU_DOMAIN_PT (1U << 2) /* Domain is identity mapped */ -+ -+/* -+ * This are the possible domain-types -+ * -+ * IOMMU_DOMAIN_BLOCKED - All DMA is blocked, can be used to isolate -+ * devices -+ * IOMMU_DOMAIN_IDENTITY - DMA addresses are system physical addresses -+ * IOMMU_DOMAIN_UNMANAGED - DMA mappings managed by IOMMU-API user, used -+ * for VMs -+ * IOMMU_DOMAIN_DMA - Internally used for DMA-API implementations. -+ * This flag allows IOMMU drivers to implement -+ * certain optimizations for these domains -+ */ -+#define IOMMU_DOMAIN_BLOCKED (0U) -+#define IOMMU_DOMAIN_IDENTITY (__IOMMU_DOMAIN_PT) -+#define IOMMU_DOMAIN_UNMANAGED (__IOMMU_DOMAIN_PAGING) -+#define IOMMU_DOMAIN_DMA (__IOMMU_DOMAIN_PAGING | \ -+ __IOMMU_DOMAIN_DMA_API) -+ - struct iommu_domain { -+ unsigned type; - const struct iommu_ops *ops; -- void *priv; - iommu_fault_handler_t handler; - void *handler_token; - struct iommu_domain_geometry geometry; -@@ -61,6 +87,7 @@ enum iommu_cap { - IOMMU_CAP_CACHE_COHERENCY, /* IOMMU can enforce cache coherent DMA - transactions */ - IOMMU_CAP_INTR_REMAP, /* IOMMU supports interrupt isolation */ -+ IOMMU_CAP_NOEXEC, /* IOMMU_NOEXEC flag */ - }; - - /* -@@ -97,23 +124,32 @@ enum iommu_attr { - * @detach_dev: detach device from an iommu domain - * @map: map a physically contiguous memory region to an iommu domain - * @unmap: unmap a physically contiguous memory region from an iommu domain -+ * @map_sg: map a scatter-gather list of physically contiguous memory chunks -+ * to an iommu domain - * @iova_to_phys: translate iova to physical address - * @add_device: add device to iommu grouping - * @remove_device: remove device from iommu grouping - * @domain_get_attr: Query domain attributes - * @domain_set_attr: Change domain attributes -+ * @of_xlate: add OF master IDs to iommu grouping - * @pgsize_bitmap: bitmap of supported page sizes -+ * @priv: per-instance data private to the iommu driver - */ - struct iommu_ops { - bool (*capable)(enum iommu_cap); -- int (*domain_init)(struct iommu_domain *domain); -- void (*domain_destroy)(struct iommu_domain *domain); -+ -+ /* Domain allocation and freeing by the iommu driver */ -+ struct iommu_domain *(*domain_alloc)(unsigned iommu_domain_type); -+ void (*domain_free)(struct iommu_domain *); -+ - int (*attach_dev)(struct iommu_domain *domain, struct device *dev); - void (*detach_dev)(struct iommu_domain *domain, struct device *dev); - int (*map)(struct iommu_domain *domain, unsigned long iova, - phys_addr_t paddr, size_t size, int prot); - size_t (*unmap)(struct iommu_domain *domain, unsigned long iova, - size_t size); -+ size_t (*map_sg)(struct iommu_domain *domain, unsigned long iova, -+ struct scatterlist *sg, unsigned int nents, int prot); - phys_addr_t (*iova_to_phys)(struct iommu_domain *domain, dma_addr_t iova); - int (*add_device)(struct device *dev); - void (*remove_device)(struct device *dev); -@@ -131,8 +167,14 @@ struct iommu_ops { - int (*domain_set_windows)(struct iommu_domain *domain, u32 w_count); - /* Get the numer of window per domain */ - u32 (*domain_get_windows)(struct iommu_domain *domain); -+ struct iommu_domain *(*get_dev_iommu_domain)(struct device *dev); -+ -+#ifdef CONFIG_OF_IOMMU -+ int (*of_xlate)(struct device *dev, struct of_phandle_args *args); -+#endif - - unsigned long pgsize_bitmap; -+ void *priv; - }; - - #define IOMMU_GROUP_NOTIFY_ADD_DEVICE 1 /* Device added */ -@@ -156,6 +198,9 @@ extern int iommu_map(struct iommu_domain *domain, unsigned long iova, - phys_addr_t paddr, size_t size, int prot); - extern size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova, - size_t size); -+extern size_t default_iommu_map_sg(struct iommu_domain *domain, unsigned long iova, -+ struct scatterlist *sg,unsigned int nents, -+ int prot); - extern phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova); - extern void iommu_set_fault_handler(struct iommu_domain *domain, - iommu_fault_handler_t handler, void *token); -@@ -200,6 +245,9 @@ extern int iommu_domain_window_enable(struct iommu_domain *domain, u32 wnd_nr, - phys_addr_t offset, u64 size, - int prot); - extern void iommu_domain_window_disable(struct iommu_domain *domain, u32 wnd_nr); -+ -+extern struct iommu_domain *iommu_get_dev_domain(struct device *dev); -+ - /** - * report_iommu_fault() - report about an IOMMU fault to the IOMMU framework - * @domain: the iommu domain where the fault has happened -@@ -241,6 +289,13 @@ static inline int report_iommu_fault(struct iommu_domain *domain, - return ret; - } - -+static inline size_t iommu_map_sg(struct iommu_domain *domain, -+ unsigned long iova, struct scatterlist *sg, -+ unsigned int nents, int prot) -+{ -+ return domain->ops->map_sg(domain, iova, sg, nents, prot); -+} -+ - #else /* CONFIG_IOMMU_API */ - - struct iommu_ops {}; -@@ -293,6 +348,13 @@ static inline int iommu_unmap(struct iommu_domain *domain, unsigned long iova, - return -ENODEV; - } - -+static inline size_t iommu_map_sg(struct iommu_domain *domain, -+ unsigned long iova, struct scatterlist *sg, -+ unsigned int nents, int prot) -+{ -+ return -ENODEV; -+} -+ - static inline int iommu_domain_window_enable(struct iommu_domain *domain, - u32 wnd_nr, phys_addr_t paddr, - u64 size, int prot) -@@ -424,6 +486,11 @@ static inline void iommu_device_unlink(struct device *dev, struct device *link) - { - } - -+static inline struct iommu_domain *iommu_get_dev_domain(struct device *dev) -+{ -+ return NULL; -+} -+ - #endif /* CONFIG_IOMMU_API */ - - #endif /* __LINUX_IOMMU_H */ -diff --git a/include/linux/iopoll.h b/include/linux/iopoll.h -new file mode 100644 -index 0000000..1c30014 ---- /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 -+#include -+#include -+#include -+#include -+#include -+ -+/** -+ * 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(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/irq.h b/include/linux/irq.h -index 03f48d9..9ba173b 100644 ---- a/include/linux/irq.h -+++ b/include/linux/irq.h -@@ -15,11 +15,13 @@ - #include - #include - #include -+#include - #include - #include - #include - #include - #include -+#include - - #include - #include -@@ -27,11 +29,7 @@ - - struct seq_file; - struct module; --struct irq_desc; --struct irq_data; --typedef void (*irq_flow_handler_t)(unsigned int irq, -- struct irq_desc *desc); --typedef void (*irq_preflow_handler_t)(struct irq_data *data); -+struct msi_msg; - - /* - * IRQ line status. -@@ -113,10 +111,14 @@ enum { - * - * IRQ_SET_MASK_OK - OK, core updates irq_data.affinity - * IRQ_SET_MASK_NOCPY - OK, chip did update irq_data.affinity -+ * IRQ_SET_MASK_OK_DONE - Same as IRQ_SET_MASK_OK for core. Special code to -+ * support stacked irqchips, which indicates skipping -+ * all descendent irqchips. - */ - enum { - IRQ_SET_MASK_OK = 0, - IRQ_SET_MASK_OK_NOCOPY, -+ IRQ_SET_MASK_OK_DONE, - }; - - struct msi_desc; -@@ -133,6 +135,8 @@ struct irq_domain; - * @chip: low level interrupt hardware access - * @domain: Interrupt translation domain; responsible for mapping - * between hwirq number and linux irq number. -+ * @parent_data: pointer to parent struct irq_data to support hierarchy -+ * irq_domain - * @handler_data: per-IRQ data for the irq_chip methods - * @chip_data: platform-specific per-chip private data for the chip - * methods, to allow shared chip implementations -@@ -151,6 +155,9 @@ struct irq_data { - unsigned int state_use_accessors; - struct irq_chip *chip; - struct irq_domain *domain; -+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY -+ struct irq_data *parent_data; -+#endif - void *handler_data; - void *chip_data; - struct msi_desc *msi_desc; -@@ -315,6 +322,8 @@ static inline irq_hw_number_t irqd_to_hwirq(struct irq_data *d) - * any other callback related to this irq - * @irq_release_resources: optional to release resources acquired with - * irq_request_resources -+ * @irq_compose_msi_msg: optional to compose message content for MSI -+ * @irq_write_msi_msg: optional to write message content for MSI - * @flags: chip specific flags - */ - struct irq_chip { -@@ -351,6 +360,9 @@ struct irq_chip { - int (*irq_request_resources)(struct irq_data *data); - void (*irq_release_resources)(struct irq_data *data); - -+ void (*irq_compose_msi_msg)(struct irq_data *data, struct msi_msg *msg); -+ void (*irq_write_msi_msg)(struct irq_data *data, struct msi_msg *msg); -+ - unsigned long flags; - }; - -@@ -438,6 +450,18 @@ extern void handle_percpu_devid_irq(unsigned int irq, struct irq_desc *desc); - extern void handle_bad_irq(unsigned int irq, struct irq_desc *desc); - extern void handle_nested_irq(unsigned int irq); - -+extern int irq_chip_compose_msi_msg(struct irq_data *data, struct msi_msg *msg); -+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY -+extern void irq_chip_ack_parent(struct irq_data *data); -+extern int irq_chip_retrigger_hierarchy(struct irq_data *data); -+extern void irq_chip_mask_parent(struct irq_data *data); -+extern void irq_chip_unmask_parent(struct irq_data *data); -+extern void irq_chip_eoi_parent(struct irq_data *data); -+extern int irq_chip_set_affinity_parent(struct irq_data *data, -+ const struct cpumask *dest, -+ bool force); -+#endif -+ - /* Handling of unhandled and spurious interrupts: */ - extern void note_interrupt(unsigned int irq, struct irq_desc *desc, - irqreturn_t action_ret); -@@ -582,7 +606,7 @@ static inline struct msi_desc *irq_get_msi_desc(unsigned int irq) - return d ? d->msi_desc : NULL; - } - --static inline struct msi_desc *irq_data_get_msi(struct irq_data *d) -+static inline struct msi_desc *irq_data_get_msi_desc(struct irq_data *d) - { - return d->msi_desc; - } -@@ -639,13 +663,6 @@ void arch_teardown_hwirq(unsigned int irq); - void irq_init_desc(unsigned int irq); - #endif - --#ifndef irq_reg_writel --# define irq_reg_writel(val, addr) writel(val, addr) --#endif --#ifndef irq_reg_readl --# define irq_reg_readl(addr) readl(addr) --#endif -- - /** - * struct irq_chip_regs - register offsets for struct irq_gci - * @enable: Enable register offset to reg_base -@@ -692,6 +709,8 @@ struct irq_chip_type { - * struct irq_chip_generic - Generic irq chip data structure - * @lock: Lock to protect register and cache data access - * @reg_base: Register base address (virtual) -+ * @reg_readl: Alternate I/O accessor (defaults to readl if NULL) -+ * @reg_writel: Alternate I/O accessor (defaults to writel if NULL) - * @irq_base: Interrupt base nr for this chip - * @irq_cnt: Number of interrupts handled by this chip - * @mask_cache: Cached mask register shared between all chip types -@@ -716,6 +735,8 @@ struct irq_chip_type { - struct irq_chip_generic { - raw_spinlock_t lock; - void __iomem *reg_base; -+ u32 (*reg_readl)(void __iomem *addr); -+ void (*reg_writel)(u32 val, void __iomem *addr); - unsigned int irq_base; - unsigned int irq_cnt; - u32 mask_cache; -@@ -740,12 +761,14 @@ struct irq_chip_generic { - * the parent irq. Usually GPIO implementations - * @IRQ_GC_MASK_CACHE_PER_TYPE: Mask cache is chip type private - * @IRQ_GC_NO_MASK: Do not calculate irq_data->mask -+ * @IRQ_GC_BE_IO: Use big-endian register accesses (default: LE) - */ - enum irq_gc_flags { - IRQ_GC_INIT_MASK_CACHE = 1 << 0, - IRQ_GC_INIT_NESTED_LOCK = 1 << 1, - IRQ_GC_MASK_CACHE_PER_TYPE = 1 << 2, - IRQ_GC_NO_MASK = 1 << 3, -+ IRQ_GC_BE_IO = 1 << 4, - }; - - /* -@@ -821,4 +844,22 @@ static inline void irq_gc_lock(struct irq_chip_generic *gc) { } - static inline void irq_gc_unlock(struct irq_chip_generic *gc) { } - #endif - -+static inline void irq_reg_writel(struct irq_chip_generic *gc, -+ u32 val, int reg_offset) -+{ -+ if (gc->reg_writel) -+ gc->reg_writel(val, gc->reg_base + reg_offset); -+ else -+ writel(val, gc->reg_base + reg_offset); -+} -+ -+static inline u32 irq_reg_readl(struct irq_chip_generic *gc, -+ int reg_offset) -+{ -+ if (gc->reg_readl) -+ return gc->reg_readl(gc->reg_base + reg_offset); -+ else -+ return readl(gc->reg_base + reg_offset); -+} -+ - #endif /* _LINUX_IRQ_H */ -diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h -index 03a4ea3..da1aa15 100644 ---- a/include/linux/irqchip/arm-gic-v3.h -+++ b/include/linux/irqchip/arm-gic-v3.h -@@ -49,6 +49,10 @@ - #define GICD_CTLR_ENABLE_G1A (1U << 1) - #define GICD_CTLR_ENABLE_G1 (1U << 0) - -+#define GICD_TYPER_ID_BITS(typer) ((((typer) >> 19) & 0x1f) + 1) -+#define GICD_TYPER_IRQS(typer) ((((typer) & 0x1f) + 1) * 32) -+#define GICD_TYPER_LPIS (1U << 17) -+ - #define GICD_IROUTER_SPI_MODE_ONE (0U << 31) - #define GICD_IROUTER_SPI_MODE_ANY (1U << 31) - -@@ -76,9 +80,42 @@ - #define GICR_MOVALLR 0x0110 - #define GICR_PIDR2 GICD_PIDR2 - -+#define GICR_CTLR_ENABLE_LPIS (1UL << 0) -+ -+#define GICR_TYPER_CPU_NUMBER(r) (((r) >> 8) & 0xffff) -+ - #define GICR_WAKER_ProcessorSleep (1U << 1) - #define GICR_WAKER_ChildrenAsleep (1U << 2) - -+#define GICR_PROPBASER_NonShareable (0U << 10) -+#define GICR_PROPBASER_InnerShareable (1U << 10) -+#define GICR_PROPBASER_OuterShareable (2U << 10) -+#define GICR_PROPBASER_SHAREABILITY_MASK (3UL << 10) -+#define GICR_PROPBASER_nCnB (0U << 7) -+#define GICR_PROPBASER_nC (1U << 7) -+#define GICR_PROPBASER_RaWt (2U << 7) -+#define GICR_PROPBASER_RaWb (3U << 7) -+#define GICR_PROPBASER_WaWt (4U << 7) -+#define GICR_PROPBASER_WaWb (5U << 7) -+#define GICR_PROPBASER_RaWaWt (6U << 7) -+#define GICR_PROPBASER_RaWaWb (7U << 7) -+#define GICR_PROPBASER_CACHEABILITY_MASK (7U << 7) -+#define GICR_PROPBASER_IDBITS_MASK (0x1f) -+ -+#define GICR_PENDBASER_NonShareable (0U << 10) -+#define GICR_PENDBASER_InnerShareable (1U << 10) -+#define GICR_PENDBASER_OuterShareable (2U << 10) -+#define GICR_PENDBASER_SHAREABILITY_MASK (3UL << 10) -+#define GICR_PENDBASER_nCnB (0U << 7) -+#define GICR_PENDBASER_nC (1U << 7) -+#define GICR_PENDBASER_RaWt (2U << 7) -+#define GICR_PENDBASER_RaWb (3U << 7) -+#define GICR_PENDBASER_WaWt (4U << 7) -+#define GICR_PENDBASER_WaWb (5U << 7) -+#define GICR_PENDBASER_RaWaWt (6U << 7) -+#define GICR_PENDBASER_RaWaWb (7U << 7) -+#define GICR_PENDBASER_CACHEABILITY_MASK (7U << 7) -+ - /* - * Re-Distributor registers, offsets from SGI_base - */ -@@ -91,9 +128,100 @@ - #define GICR_IPRIORITYR0 GICD_IPRIORITYR - #define GICR_ICFGR0 GICD_ICFGR - -+#define GICR_TYPER_PLPIS (1U << 0) - #define GICR_TYPER_VLPIS (1U << 1) - #define GICR_TYPER_LAST (1U << 4) - -+#define LPI_PROP_GROUP1 (1 << 1) -+#define LPI_PROP_ENABLED (1 << 0) -+ -+/* -+ * ITS registers, offsets from ITS_base -+ */ -+#define GITS_CTLR 0x0000 -+#define GITS_IIDR 0x0004 -+#define GITS_TYPER 0x0008 -+#define GITS_CBASER 0x0080 -+#define GITS_CWRITER 0x0088 -+#define GITS_CREADR 0x0090 -+#define GITS_BASER 0x0100 -+#define GITS_PIDR2 GICR_PIDR2 -+ -+#define GITS_TRANSLATER 0x10040 -+ -+#define GITS_CTLR_ENABLE (1U << 0) -+#define GITS_CTLR_QUIESCENT (1U << 31) -+ -+#define GITS_TYPER_DEVBITS_SHIFT 13 -+#define GITS_TYPER_DEVBITS(r) ((((r) >> GITS_TYPER_DEVBITS_SHIFT) & 0x1f) + 1) -+#define GITS_TYPER_PTA (1UL << 19) -+ -+#define GITS_CBASER_VALID (1UL << 63) -+#define GITS_CBASER_nCnB (0UL << 59) -+#define GITS_CBASER_nC (1UL << 59) -+#define GITS_CBASER_RaWt (2UL << 59) -+#define GITS_CBASER_RaWb (3UL << 59) -+#define GITS_CBASER_WaWt (4UL << 59) -+#define GITS_CBASER_WaWb (5UL << 59) -+#define GITS_CBASER_RaWaWt (6UL << 59) -+#define GITS_CBASER_RaWaWb (7UL << 59) -+#define GITS_CBASER_CACHEABILITY_MASK (7UL << 59) -+#define GITS_CBASER_NonShareable (0UL << 10) -+#define GITS_CBASER_InnerShareable (1UL << 10) -+#define GITS_CBASER_OuterShareable (2UL << 10) -+#define GITS_CBASER_SHAREABILITY_MASK (3UL << 10) -+ -+#define GITS_BASER_NR_REGS 8 -+ -+#define GITS_BASER_VALID (1UL << 63) -+#define GITS_BASER_nCnB (0UL << 59) -+#define GITS_BASER_nC (1UL << 59) -+#define GITS_BASER_RaWt (2UL << 59) -+#define GITS_BASER_RaWb (3UL << 59) -+#define GITS_BASER_WaWt (4UL << 59) -+#define GITS_BASER_WaWb (5UL << 59) -+#define GITS_BASER_RaWaWt (6UL << 59) -+#define GITS_BASER_RaWaWb (7UL << 59) -+#define GITS_BASER_CACHEABILITY_MASK (7UL << 59) -+#define GITS_BASER_TYPE_SHIFT (56) -+#define GITS_BASER_TYPE(r) (((r) >> GITS_BASER_TYPE_SHIFT) & 7) -+#define GITS_BASER_ENTRY_SIZE_SHIFT (48) -+#define GITS_BASER_ENTRY_SIZE(r) ((((r) >> GITS_BASER_ENTRY_SIZE_SHIFT) & 0xff) + 1) -+#define GITS_BASER_NonShareable (0UL << 10) -+#define GITS_BASER_InnerShareable (1UL << 10) -+#define GITS_BASER_OuterShareable (2UL << 10) -+#define GITS_BASER_SHAREABILITY_SHIFT (10) -+#define GITS_BASER_SHAREABILITY_MASK (3UL << GITS_BASER_SHAREABILITY_SHIFT) -+#define GITS_BASER_PAGE_SIZE_SHIFT (8) -+#define GITS_BASER_PAGE_SIZE_4K (0UL << GITS_BASER_PAGE_SIZE_SHIFT) -+#define GITS_BASER_PAGE_SIZE_16K (1UL << GITS_BASER_PAGE_SIZE_SHIFT) -+#define GITS_BASER_PAGE_SIZE_64K (2UL << GITS_BASER_PAGE_SIZE_SHIFT) -+#define GITS_BASER_PAGE_SIZE_MASK (3UL << GITS_BASER_PAGE_SIZE_SHIFT) -+ -+#define GITS_BASER_TYPE_NONE 0 -+#define GITS_BASER_TYPE_DEVICE 1 -+#define GITS_BASER_TYPE_VCPU 2 -+#define GITS_BASER_TYPE_CPU 3 -+#define GITS_BASER_TYPE_COLLECTION 4 -+#define GITS_BASER_TYPE_RESERVED5 5 -+#define GITS_BASER_TYPE_RESERVED6 6 -+#define GITS_BASER_TYPE_RESERVED7 7 -+ -+/* -+ * ITS commands -+ */ -+#define GITS_CMD_MAPD 0x08 -+#define GITS_CMD_MAPC 0x09 -+#define GITS_CMD_MAPVI 0x0a -+#define GITS_CMD_MOVI 0x01 -+#define GITS_CMD_DISCARD 0x0f -+#define GITS_CMD_INV 0x0c -+#define GITS_CMD_MOVALL 0x0e -+#define GITS_CMD_INVALL 0x0d -+#define GITS_CMD_INT 0x03 -+#define GITS_CMD_CLEAR 0x04 -+#define GITS_CMD_SYNC 0x05 -+ - /* - * CPU interface registers - */ -@@ -188,6 +316,24 @@ - #ifndef __ASSEMBLY__ - - #include -+#include -+ -+/* -+ * We need a value to serve as a irq-type for LPIs. Choose one that will -+ * hopefully pique the interest of the reviewer. -+ */ -+#define GIC_IRQ_TYPE_LPI 0xa110c8ed -+ -+struct rdists { -+ struct { -+ void __iomem *rd_base; -+ struct page *pend_page; -+ phys_addr_t phys_base; -+ } __percpu *rdist; -+ struct page *prop_page; -+ int id_bits; -+ u64 flags; -+}; - - static inline void gic_write_eoir(u64 irq) - { -@@ -195,6 +341,13 @@ static inline void gic_write_eoir(u64 irq) - isb(); - } - -+struct irq_domain; -+int its_cpu_init(void); -+int its_init(struct device_node *node, struct rdists *rdists, -+ struct irq_domain *domain); -+int __its_msi_prepare(struct irq_domain *domain, u32 dev_id, -+ struct device *dev, int nvec, msi_alloc_info_t *info); -+ - #endif - - #endif -diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h -index b0f9d16..ebace05 100644 ---- a/include/linux/irqdomain.h -+++ b/include/linux/irqdomain.h -@@ -33,15 +33,31 @@ - #define _LINUX_IRQDOMAIN_H - - #include -+#include - #include - - struct device_node; - struct irq_domain; - struct of_device_id; -+struct irq_chip; -+struct irq_data; - - /* Number of irqs reserved for a legacy isa controller */ - #define NUM_ISA_INTERRUPTS 16 - -+/* -+ * Should several domains have the same device node, but serve -+ * different purposes (for example one domain is for PCI/MSI, and the -+ * other for wired IRQs), they can be distinguished using a -+ * bus-specific token. Most domains are expected to only carry -+ * DOMAIN_BUS_ANY. -+ */ -+enum irq_domain_bus_token { -+ DOMAIN_BUS_ANY = 0, -+ DOMAIN_BUS_PCI_MSI, -+ DOMAIN_BUS_PLATFORM_MSI, -+}; -+ - /** - * struct irq_domain_ops - Methods for irq_domain objects - * @match: Match an interrupt controller device node to a host, returns -@@ -58,12 +74,23 @@ struct of_device_id; - * to setup the irq_desc when returning from map(). - */ - struct irq_domain_ops { -- int (*match)(struct irq_domain *d, struct device_node *node); -+ int (*match)(struct irq_domain *d, struct device_node *node, -+ enum irq_domain_bus_token bus_token); - int (*map)(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw); - void (*unmap)(struct irq_domain *d, unsigned int virq); - int (*xlate)(struct irq_domain *d, struct device_node *node, - const u32 *intspec, unsigned int intsize, - unsigned long *out_hwirq, unsigned int *out_type); -+ -+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY -+ /* extended V2 interfaces to support hierarchy irq_domains */ -+ int (*alloc)(struct irq_domain *d, unsigned int virq, -+ unsigned int nr_irqs, void *arg); -+ void (*free)(struct irq_domain *d, unsigned int virq, -+ unsigned int nr_irqs); -+ void (*activate)(struct irq_domain *d, struct irq_data *irq_data); -+ void (*deactivate)(struct irq_domain *d, struct irq_data *irq_data); -+#endif - }; - - extern struct irq_domain_ops irq_generic_chip_ops; -@@ -77,6 +104,7 @@ struct irq_domain_chip_generic; - * @ops: pointer to irq_domain methods - * @host_data: private data pointer for use by owner. Not touched by irq_domain - * core code. -+ * @flags: host per irq_domain flags - * - * Optional elements - * @of_node: Pointer to device tree nodes associated with the irq_domain. Used -@@ -84,6 +112,7 @@ struct irq_domain_chip_generic; - * @gc: Pointer to a list of generic chips. There is a helper function for - * setting up one or more generic chips for interrupt controllers - * drivers using the generic chip library which uses this pointer. -+ * @parent: Pointer to parent irq_domain to support hierarchy irq_domains - * - * Revmap data, used internally by irq_domain - * @revmap_direct_max_irq: The largest hwirq that can be set for controllers that -@@ -97,10 +126,15 @@ struct irq_domain { - const char *name; - const struct irq_domain_ops *ops; - void *host_data; -+ unsigned int flags; - - /* Optional data */ - struct device_node *of_node; -+ enum irq_domain_bus_token bus_token; - struct irq_domain_chip_generic *gc; -+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY -+ struct irq_domain *parent; -+#endif - - /* reverse map data. The linear map gets appended to the irq_domain */ - irq_hw_number_t hwirq_max; -@@ -110,6 +144,22 @@ struct irq_domain { - unsigned int linear_revmap[]; - }; - -+/* Irq domain flags */ -+enum { -+ /* Irq domain is hierarchical */ -+ IRQ_DOMAIN_FLAG_HIERARCHY = (1 << 0), -+ -+ /* Core calls alloc/free recursive through the domain hierarchy. */ -+ IRQ_DOMAIN_FLAG_AUTO_RECURSIVE = (1 << 1), -+ -+ /* -+ * Flags starting from IRQ_DOMAIN_FLAG_NONCORE are reserved -+ * for implementation specific purposes and ignored by the -+ * core code. -+ */ -+ IRQ_DOMAIN_FLAG_NONCORE = (1 << 16), -+}; -+ - #ifdef CONFIG_IRQ_DOMAIN - struct irq_domain *__irq_domain_add(struct device_node *of_node, int size, - irq_hw_number_t hwirq_max, int direct_max, -@@ -126,9 +176,15 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node, - irq_hw_number_t first_hwirq, - const struct irq_domain_ops *ops, - void *host_data); --extern struct irq_domain *irq_find_host(struct device_node *node); -+extern struct irq_domain *irq_find_matching_host(struct device_node *node, -+ enum irq_domain_bus_token bus_token); - extern void irq_set_default_host(struct irq_domain *host); - -+static inline struct irq_domain *irq_find_host(struct device_node *node) -+{ -+ return irq_find_matching_host(node, DOMAIN_BUS_ANY); -+} -+ - /** - * irq_domain_add_linear() - Allocate and register a linear revmap irq_domain. - * @of_node: pointer to interrupt controller's device tree node. -@@ -220,8 +276,74 @@ int irq_domain_xlate_onetwocell(struct irq_domain *d, struct device_node *ctrlr, - const u32 *intspec, unsigned int intsize, - irq_hw_number_t *out_hwirq, unsigned int *out_type); - -+/* V2 interfaces to support hierarchy IRQ domains. */ -+extern struct irq_data *irq_domain_get_irq_data(struct irq_domain *domain, -+ unsigned int virq); -+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY -+extern struct irq_domain *irq_domain_add_hierarchy(struct irq_domain *parent, -+ unsigned int flags, unsigned int size, -+ struct device_node *node, -+ const struct irq_domain_ops *ops, void *host_data); -+extern int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base, -+ unsigned int nr_irqs, int node, void *arg, -+ bool realloc); -+extern void irq_domain_free_irqs(unsigned int virq, unsigned int nr_irqs); -+extern void irq_domain_activate_irq(struct irq_data *irq_data); -+extern void irq_domain_deactivate_irq(struct irq_data *irq_data); -+ -+static inline int irq_domain_alloc_irqs(struct irq_domain *domain, -+ unsigned int nr_irqs, int node, void *arg) -+{ -+ return __irq_domain_alloc_irqs(domain, -1, nr_irqs, node, arg, false); -+} -+ -+extern int irq_domain_set_hwirq_and_chip(struct irq_domain *domain, -+ unsigned int virq, -+ irq_hw_number_t hwirq, -+ struct irq_chip *chip, -+ void *chip_data); -+extern void irq_domain_set_info(struct irq_domain *domain, unsigned int virq, -+ irq_hw_number_t hwirq, struct irq_chip *chip, -+ void *chip_data, irq_flow_handler_t handler, -+ void *handler_data, const char *handler_name); -+extern void irq_domain_reset_irq_data(struct irq_data *irq_data); -+extern void irq_domain_free_irqs_common(struct irq_domain *domain, -+ unsigned int virq, -+ unsigned int nr_irqs); -+extern void irq_domain_free_irqs_top(struct irq_domain *domain, -+ unsigned int virq, unsigned int nr_irqs); -+ -+extern int irq_domain_alloc_irqs_parent(struct irq_domain *domain, -+ unsigned int irq_base, -+ unsigned int nr_irqs, void *arg); -+ -+extern void irq_domain_free_irqs_parent(struct irq_domain *domain, -+ unsigned int irq_base, -+ unsigned int nr_irqs); -+ -+static inline bool irq_domain_is_hierarchy(struct irq_domain *domain) -+{ -+ return domain->flags & IRQ_DOMAIN_FLAG_HIERARCHY; -+} -+#else /* CONFIG_IRQ_DOMAIN_HIERARCHY */ -+static inline void irq_domain_activate_irq(struct irq_data *data) { } -+static inline void irq_domain_deactivate_irq(struct irq_data *data) { } -+static inline int irq_domain_alloc_irqs(struct irq_domain *domain, -+ unsigned int nr_irqs, int node, void *arg) -+{ -+ return -1; -+} -+ -+static inline bool irq_domain_is_hierarchy(struct irq_domain *domain) -+{ -+ return false; -+} -+#endif /* CONFIG_IRQ_DOMAIN_HIERARCHY */ -+ - #else /* CONFIG_IRQ_DOMAIN */ - static inline void irq_dispose_mapping(unsigned int virq) { } -+static inline void irq_domain_activate_irq(struct irq_data *data) { } -+static inline void irq_domain_deactivate_irq(struct irq_data *data) { } - #endif /* !CONFIG_IRQ_DOMAIN */ - - #endif /* _LINUX_IRQDOMAIN_H */ -diff --git a/include/linux/irqhandler.h b/include/linux/irqhandler.h -new file mode 100644 -index 0000000..62d5430 ---- /dev/null -+++ b/include/linux/irqhandler.h -@@ -0,0 +1,14 @@ -+#ifndef _LINUX_IRQHANDLER_H -+#define _LINUX_IRQHANDLER_H -+ -+/* -+ * Interrupt flow handler typedefs are defined here to avoid circular -+ * include dependencies. -+ */ -+ -+struct irq_desc; -+struct irq_data; -+typedef void (*irq_flow_handler_t)(unsigned int irq, struct irq_desc *desc); -+typedef void (*irq_preflow_handler_t)(struct irq_data *data); -+ -+#endif -diff --git a/include/linux/msi.h b/include/linux/msi.h -index 44f4746..788d65b 100644 ---- a/include/linux/msi.h -+++ b/include/linux/msi.h -@@ -10,17 +10,13 @@ struct msi_msg { - u32 data; /* 16 bits of msi message data */ - }; - -+extern int pci_msi_ignore_mask; - /* Helper functions */ - struct irq_data; - struct msi_desc; --void mask_msi_irq(struct irq_data *data); --void unmask_msi_irq(struct irq_data *data); --void __read_msi_msg(struct msi_desc *entry, struct msi_msg *msg); -+struct pci_dev; - void __get_cached_msi_msg(struct msi_desc *entry, struct msi_msg *msg); --void __write_msi_msg(struct msi_desc *entry, struct msi_msg *msg); --void read_msi_msg(unsigned int irq, struct msi_msg *msg); - void get_cached_msi_msg(unsigned int irq, struct msi_msg *msg); --void write_msi_msg(unsigned int irq, struct msi_msg *msg); - - struct msi_desc { - struct { -@@ -42,12 +38,63 @@ struct msi_desc { - void __iomem *mask_base; - u8 mask_pos; - }; -- struct pci_dev *dev; -+ struct device *dev; - - /* Last set MSI message */ - struct msi_msg msg; - }; - -+/* Helpers to hide struct msi_desc implementation details */ -+#define msi_desc_to_dev(desc) ((desc)->dev) -+#define dev_to_msi_list(dev) (&(dev)->msi_list) -+#define first_msi_entry(dev) \ -+ list_first_entry(dev_to_msi_list((dev)), struct msi_desc, list) -+#define for_each_msi_entry(desc, dev) \ -+ list_for_each_entry((desc), dev_to_msi_list((dev)), list) -+ -+#ifdef CONFIG_PCI_MSI -+#define first_pci_msi_entry(pdev) first_msi_entry(&(pdev)->dev) -+#define for_each_pci_msi_entry(desc, pdev) \ -+ for_each_msi_entry((desc), &(pdev)->dev) -+ -+struct pci_dev *msi_desc_to_pci_dev(struct msi_desc *desc); -+void *msi_desc_to_pci_sysdata(struct msi_desc *desc); -+#else /* CONFIG_PCI_MSI */ -+static inline void *msi_desc_to_pci_sysdata(struct msi_desc *desc) -+{ -+ return NULL; -+} -+#endif /* CONFIG_PCI_MSI */ -+ -+struct msi_desc *alloc_msi_entry(struct device *dev); -+void free_msi_entry(struct msi_desc *entry); -+void __pci_read_msi_msg(struct msi_desc *entry, struct msi_msg *msg); -+void __pci_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg); -+void pci_write_msi_msg(unsigned int irq, struct msi_msg *msg); -+ -+u32 __pci_msix_desc_mask_irq(struct msi_desc *desc, u32 flag); -+u32 __pci_msi_desc_mask_irq(struct msi_desc *desc, u32 mask, u32 flag); -+void pci_msi_mask_irq(struct irq_data *data); -+void pci_msi_unmask_irq(struct irq_data *data); -+ -+/* Conversion helpers. Should be removed after merging */ -+static inline void __write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) -+{ -+ __pci_write_msi_msg(entry, msg); -+} -+static inline void write_msi_msg(int irq, struct msi_msg *msg) -+{ -+ pci_write_msi_msg(irq, msg); -+} -+static inline void mask_msi_irq(struct irq_data *data) -+{ -+ pci_msi_mask_irq(data); -+} -+static inline void unmask_msi_irq(struct irq_data *data) -+{ -+ pci_msi_unmask_irq(data); -+} -+ - /* - * The arch hooks to setup up msi irqs. Those functions are - * implemented as weak symbols so that they /can/ be overriden by -@@ -61,18 +108,146 @@ void arch_restore_msi_irqs(struct pci_dev *dev); - - void default_teardown_msi_irqs(struct pci_dev *dev); - void default_restore_msi_irqs(struct pci_dev *dev); --u32 default_msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag); --u32 default_msix_mask_irq(struct msi_desc *desc, u32 flag); -+#define default_msi_mask_irq __msi_mask_irq -+#define default_msix_mask_irq __msix_mask_irq - --struct msi_chip { -+struct msi_controller { - struct module *owner; - struct device *dev; - struct device_node *of_node; - struct list_head list; -+#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN -+ struct irq_domain *domain; -+#endif - -- int (*setup_irq)(struct msi_chip *chip, struct pci_dev *dev, -+ int (*setup_irq)(struct msi_controller *chip, struct pci_dev *dev, - struct msi_desc *desc); -- void (*teardown_irq)(struct msi_chip *chip, unsigned int irq); -+ int (*setup_irqs)(struct msi_controller *chip, struct pci_dev *dev, -+ int nvec, int type); -+ void (*teardown_irq)(struct msi_controller *chip, unsigned int irq); - }; - -+#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN -+ -+#include -+#include -+ -+struct irq_domain; -+struct irq_chip; -+struct device_node; -+struct msi_domain_info; -+ -+/** -+ * struct msi_domain_ops - MSI interrupt domain callbacks -+ * @get_hwirq: Retrieve the resulting hw irq number -+ * @msi_init: Domain specific init function for MSI interrupts -+ * @msi_free: Domain specific function to free a MSI interrupts -+ * @msi_check: Callback for verification of the domain/info/dev data -+ * @msi_prepare: Prepare the allocation of the interrupts in the domain -+ * @msi_finish: Optional callbacl to finalize the allocation -+ * @set_desc: Set the msi descriptor for an interrupt -+ * @handle_error: Optional error handler if the allocation fails -+ * -+ * @get_hwirq, @msi_init and @msi_free are callbacks used by -+ * msi_create_irq_domain() and related interfaces -+ * -+ * @msi_check, @msi_prepare, @msi_finish, @set_desc and @handle_error -+ * are callbacks used by msi_irq_domain_alloc_irqs() and related -+ * interfaces which are based on msi_desc. -+ */ -+struct msi_domain_ops { -+ irq_hw_number_t (*get_hwirq)(struct msi_domain_info *info, -+ msi_alloc_info_t *arg); -+ int (*msi_init)(struct irq_domain *domain, -+ struct msi_domain_info *info, -+ unsigned int virq, irq_hw_number_t hwirq, -+ msi_alloc_info_t *arg); -+ void (*msi_free)(struct irq_domain *domain, -+ struct msi_domain_info *info, -+ unsigned int virq); -+ int (*msi_check)(struct irq_domain *domain, -+ struct msi_domain_info *info, -+ struct device *dev); -+ int (*msi_prepare)(struct irq_domain *domain, -+ struct device *dev, int nvec, -+ msi_alloc_info_t *arg); -+ void (*msi_finish)(msi_alloc_info_t *arg, int retval); -+ void (*set_desc)(msi_alloc_info_t *arg, -+ struct msi_desc *desc); -+ int (*handle_error)(struct irq_domain *domain, -+ struct msi_desc *desc, int error); -+}; -+ -+/** -+ * struct msi_domain_info - MSI interrupt domain data -+ * @flags: Flags to decribe features and capabilities -+ * @ops: The callback data structure -+ * @chip: Optional: associated interrupt chip -+ * @chip_data: Optional: associated interrupt chip data -+ * @handler: Optional: associated interrupt flow handler -+ * @handler_data: Optional: associated interrupt flow handler data -+ * @handler_name: Optional: associated interrupt flow handler name -+ * @data: Optional: domain specific data -+ */ -+struct msi_domain_info { -+ u32 flags; -+ struct msi_domain_ops *ops; -+ struct irq_chip *chip; -+ void *chip_data; -+ irq_flow_handler_t handler; -+ void *handler_data; -+ const char *handler_name; -+ void *data; -+}; -+ -+/* Flags for msi_domain_info */ -+enum { -+ /* -+ * Init non implemented ops callbacks with default MSI domain -+ * callbacks. -+ */ -+ MSI_FLAG_USE_DEF_DOM_OPS = (1 << 0), -+ /* -+ * Init non implemented chip callbacks with default MSI chip -+ * callbacks. -+ */ -+ MSI_FLAG_USE_DEF_CHIP_OPS = (1 << 1), -+ /* Build identity map between hwirq and irq */ -+ MSI_FLAG_IDENTITY_MAP = (1 << 2), -+ /* Support multiple PCI MSI interrupts */ -+ MSI_FLAG_MULTI_PCI_MSI = (1 << 3), -+ /* Support PCI MSIX interrupts */ -+ MSI_FLAG_PCI_MSIX = (1 << 4), -+}; -+ -+int msi_domain_set_affinity(struct irq_data *data, const struct cpumask *mask, -+ bool force); -+ -+struct irq_domain *msi_create_irq_domain(struct device_node *of_node, -+ struct msi_domain_info *info, -+ struct irq_domain *parent); -+int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, -+ int nvec); -+void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev); -+struct msi_domain_info *msi_get_domain_info(struct irq_domain *domain); -+ -+#endif /* CONFIG_GENERIC_MSI_IRQ_DOMAIN */ -+ -+#ifdef CONFIG_PCI_MSI_IRQ_DOMAIN -+void pci_msi_domain_write_msg(struct irq_data *irq_data, struct msi_msg *msg); -+struct irq_domain *pci_msi_create_irq_domain(struct device_node *node, -+ struct msi_domain_info *info, -+ struct irq_domain *parent); -+int pci_msi_domain_alloc_irqs(struct irq_domain *domain, struct pci_dev *dev, -+ int nvec, int type); -+void pci_msi_domain_free_irqs(struct irq_domain *domain, struct pci_dev *dev); -+struct irq_domain *pci_msi_create_default_irq_domain(struct device_node *node, -+ struct msi_domain_info *info, struct irq_domain *parent); -+ -+irq_hw_number_t pci_msi_domain_calc_hwirq(struct pci_dev *dev, -+ struct msi_desc *desc); -+int pci_msi_domain_check_cap(struct irq_domain *domain, -+ struct msi_domain_info *info, struct device *dev); -+#endif /* CONFIG_PCI_MSI_IRQ_DOMAIN */ -+ - #endif /* LINUX_MSI_H */ -diff --git a/include/linux/of_device.h b/include/linux/of_device.h -index ef37021..22801b1 100644 ---- a/include/linux/of_device.h -+++ b/include/linux/of_device.h -@@ -53,6 +53,7 @@ static inline struct device_node *of_cpu_device_node_get(int cpu) - return of_node_get(cpu_dev->of_node); - } - -+void of_dma_configure(struct device *dev, struct device_node *np); - #else /* CONFIG_OF */ - - static inline int of_driver_match_device(struct device *dev, -@@ -90,6 +91,8 @@ static inline struct device_node *of_cpu_device_node_get(int cpu) - { - return NULL; - } -+static inline void of_dma_configure(struct device *dev, struct device_node *np) -+{} - #endif /* CONFIG_OF */ - - #endif /* _LINUX_OF_DEVICE_H */ -diff --git a/include/linux/of_iommu.h b/include/linux/of_iommu.h -index 51a560f..ffbe470 100644 ---- a/include/linux/of_iommu.h -+++ b/include/linux/of_iommu.h -@@ -1,12 +1,20 @@ - #ifndef __OF_IOMMU_H - #define __OF_IOMMU_H - -+#include -+#include -+#include -+ - #ifdef CONFIG_OF_IOMMU - - extern int of_get_dma_window(struct device_node *dn, const char *prefix, - int index, unsigned long *busno, dma_addr_t *addr, - size_t *size); - -+extern void of_iommu_init(void); -+extern struct iommu_ops *of_iommu_configure(struct device *dev, -+ struct device_node *master_np); -+ - #else - - static inline int of_get_dma_window(struct device_node *dn, const char *prefix, -@@ -16,6 +24,23 @@ static inline int of_get_dma_window(struct device_node *dn, const char *prefix, - return -EINVAL; - } - -+static inline void of_iommu_init(void) { } -+static inline struct iommu_ops *of_iommu_configure(struct device *dev, -+ struct device_node *master_np) -+{ -+ return NULL; -+} -+ - #endif /* CONFIG_OF_IOMMU */ - -+void of_iommu_set_ops(struct device_node *np, struct iommu_ops *ops); -+struct iommu_ops *of_iommu_get_ops(struct device_node *np); -+ -+extern struct of_device_id __iommu_of_table; -+ -+typedef int (*of_iommu_init_fn)(struct device_node *); -+ -+#define IOMMU_OF_DECLARE(name, compat, fn) \ -+ _OF_DECLARE(iommu, name, compat, fn, of_iommu_init_fn) -+ - #endif /* __OF_IOMMU_H */ -diff --git a/include/linux/of_irq.h b/include/linux/of_irq.h -index bfec136..563ad28 100644 ---- a/include/linux/of_irq.h -+++ b/include/linux/of_irq.h -@@ -69,6 +69,7 @@ static inline int of_irq_get_byname(struct device_node *dev, const char *name) - */ - extern unsigned int irq_of_parse_and_map(struct device_node *node, int index); - extern struct device_node *of_irq_find_parent(struct device_node *child); -+extern void of_msi_configure(struct device *dev, struct device_node *np); - - #else /* !CONFIG_OF */ - static inline unsigned int irq_of_parse_and_map(struct device_node *dev, -diff --git a/include/linux/of_pci.h b/include/linux/of_pci.h -index 1fd207e..29fd3fe 100644 ---- a/include/linux/of_pci.h -+++ b/include/linux/of_pci.h -@@ -16,6 +16,7 @@ int of_pci_get_devfn(struct device_node *np); - int of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin); - int of_pci_parse_bus_range(struct device_node *node, struct resource *res); - int of_get_pci_domain_nr(struct device_node *node); -+void of_pci_dma_configure(struct pci_dev *pci_dev); - #else - static inline int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq) - { -@@ -50,6 +51,8 @@ of_get_pci_domain_nr(struct device_node *node) - { - return -1; - } -+ -+static inline void of_pci_dma_configure(struct pci_dev *pci_dev) { } - #endif - - #if defined(CONFIG_OF_ADDRESS) -@@ -59,13 +62,13 @@ int of_pci_get_host_bridge_resources(struct device_node *dev, - #endif - - #if defined(CONFIG_OF) && defined(CONFIG_PCI_MSI) --int of_pci_msi_chip_add(struct msi_chip *chip); --void of_pci_msi_chip_remove(struct msi_chip *chip); --struct msi_chip *of_pci_find_msi_chip_by_node(struct device_node *of_node); -+int of_pci_msi_chip_add(struct msi_controller *chip); -+void of_pci_msi_chip_remove(struct msi_controller *chip); -+struct msi_controller *of_pci_find_msi_chip_by_node(struct device_node *of_node); - #else --static inline int of_pci_msi_chip_add(struct msi_chip *chip) { return -EINVAL; } --static inline void of_pci_msi_chip_remove(struct msi_chip *chip) { } --static inline struct msi_chip * -+static inline int of_pci_msi_chip_add(struct msi_controller *chip) { return -EINVAL; } -+static inline void of_pci_msi_chip_remove(struct msi_controller *chip) { } -+static inline struct msi_controller * - of_pci_find_msi_chip_by_node(struct device_node *of_node) { return NULL; } - #endif - -diff --git a/include/linux/of_platform.h b/include/linux/of_platform.h -index c2b0627..8a860f0 100644 ---- a/include/linux/of_platform.h -+++ b/include/linux/of_platform.h -@@ -84,4 +84,10 @@ static inline int of_platform_populate(struct device_node *root, - static inline void of_platform_depopulate(struct device *parent) { } - #endif - -+#ifdef CONFIG_OF_DYNAMIC -+extern void of_platform_register_reconfig_notifier(void); -+#else -+static inline void of_platform_register_reconfig_notifier(void) { } -+#endif -+ - #endif /* _LINUX_OF_PLATFORM_H */ -diff --git a/include/linux/pci.h b/include/linux/pci.h -index 7a34844..a99f301 100644 ---- a/include/linux/pci.h -+++ b/include/linux/pci.h -@@ -29,6 +29,7 @@ - #include - #include - #include -+#include - #include - - #include -@@ -171,8 +172,8 @@ enum pci_dev_flags { - PCI_DEV_FLAGS_ASSIGNED = (__force pci_dev_flags_t) (1 << 2), - /* Flag for quirk use to store if quirk-specific ACS is enabled */ - PCI_DEV_FLAGS_ACS_ENABLED_QUIRK = (__force pci_dev_flags_t) (1 << 3), -- /* Flag to indicate the device uses dma_alias_devfn */ -- PCI_DEV_FLAGS_DMA_ALIAS_DEVFN = (__force pci_dev_flags_t) (1 << 4), -+ /* Flag to indicate the device uses dma_alias_devid */ -+ PCI_DEV_FLAGS_DMA_ALIAS_DEVID = (__force pci_dev_flags_t) (1 << 4), - /* Use a PCIe-to-PCI bridge alias even if !pci_is_pcie */ - PCI_DEV_FLAG_PCIE_BRIDGE_ALIAS = (__force pci_dev_flags_t) (1 << 5), - /* Do not use bus resets for device */ -@@ -278,7 +279,7 @@ struct pci_dev { - u8 rom_base_reg; /* which config register controls the ROM */ - u8 pin; /* which interrupt pin this device uses */ - u16 pcie_flags_reg; /* cached PCIe Capabilities Register */ -- u8 dma_alias_devfn;/* devfn of DMA alias, if any */ -+ u32 dma_alias_devid;/* devid of DMA alias */ - - struct pci_driver *driver; /* which driver has allocated this device */ - u64 dma_mask; /* Mask of the bits of bus address this -@@ -365,7 +366,6 @@ struct pci_dev { - struct bin_attribute *res_attr[DEVICE_COUNT_RESOURCE]; /* sysfs file for resources */ - struct bin_attribute *res_attr_wc[DEVICE_COUNT_RESOURCE]; /* sysfs file for WC mapping of resources */ - #ifdef CONFIG_PCI_MSI -- struct list_head msi_list; - const struct attribute_group **msi_irq_groups; - #endif - struct pci_vpd *vpd; -@@ -400,16 +400,10 @@ static inline int pci_channel_offline(struct pci_dev *pdev) - return (pdev->error_state != pci_channel_io_normal); - } - --struct pci_host_bridge_window { -- struct list_head list; -- struct resource *res; /* host bridge aperture (CPU address) */ -- resource_size_t offset; /* bus address + offset = CPU address */ --}; -- - struct pci_host_bridge { - struct device dev; - struct pci_bus *bus; /* root bus */ -- struct list_head windows; /* pci_host_bridge_windows */ -+ struct list_head windows; /* resource_entry */ - void (*release_fn)(struct pci_host_bridge *); - void *release_data; - }; -@@ -456,7 +450,7 @@ struct pci_bus { - struct resource busn_res; /* bus numbers routed to this bus */ - - struct pci_ops *ops; /* configuration access functions */ -- struct msi_chip *msi; /* MSI controller */ -+ struct msi_controller *msi; /* MSI controller */ - void *sysdata; /* hook for sys-specific extension */ - struct proc_dir_entry *procdir; /* directory entry in /proc/bus/pci */ - -@@ -516,6 +510,9 @@ static inline struct pci_dev *pci_upstream_bridge(struct pci_dev *dev) - return dev->bus->self; - } - -+struct device *pci_get_host_bridge_device(struct pci_dev *dev); -+void pci_put_host_bridge_device(struct device *dev); -+ - #ifdef CONFIG_PCI_MSI - static inline bool pci_dev_msi_enabled(struct pci_dev *pci_dev) - { -diff --git a/include/linux/resource_ext.h b/include/linux/resource_ext.h -new file mode 100644 -index 0000000..e2bf63d ---- /dev/null -+++ b/include/linux/resource_ext.h -@@ -0,0 +1,77 @@ -+/* -+ * Copyright (C) 2015, Intel Corporation -+ * Author: Jiang Liu -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT -+ * 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_RESOURCE_EXT_H -+#define _LINUX_RESOURCE_EXT_H -+#include -+#include -+#include -+#include -+ -+/* Represent resource window for bridge devices */ -+struct resource_win { -+ struct resource res; /* In master (CPU) address space */ -+ resource_size_t offset; /* Translation offset for bridge */ -+}; -+ -+/* -+ * Common resource list management data structure and interfaces to support -+ * ACPI, PNP and PCI host bridge etc. -+ */ -+struct resource_entry { -+ struct list_head node; -+ struct resource *res; /* In master (CPU) address space */ -+ resource_size_t offset; /* Translation offset for bridge */ -+ struct resource __res; /* Default storage for res */ -+}; -+ -+extern struct resource_entry * -+resource_list_create_entry(struct resource *res, size_t extra_size); -+extern void resource_list_free(struct list_head *head); -+ -+static inline void resource_list_add(struct resource_entry *entry, -+ struct list_head *head) -+{ -+ list_add(&entry->node, head); -+} -+ -+static inline void resource_list_add_tail(struct resource_entry *entry, -+ struct list_head *head) -+{ -+ list_add_tail(&entry->node, head); -+} -+ -+static inline void resource_list_del(struct resource_entry *entry) -+{ -+ list_del(&entry->node); -+} -+ -+static inline void resource_list_free_entry(struct resource_entry *entry) -+{ -+ kfree(entry); -+} -+ -+static inline void -+resource_list_destroy_entry(struct resource_entry *entry) -+{ -+ resource_list_del(entry); -+ resource_list_free_entry(entry); -+} -+ -+#define resource_list_for_each_entry(entry, list) \ -+ list_for_each_entry((entry), (list), node) -+ -+#define resource_list_for_each_entry_safe(entry, tmp, list) \ -+ list_for_each_entry_safe((entry), (tmp), (list), node) -+ -+#endif /* _LINUX_RESOURCE_EXT_H */ -diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h -index 9948c87..1d0043d 100644 ---- a/include/linux/usb/quirks.h -+++ b/include/linux/usb/quirks.h -@@ -47,4 +47,7 @@ - /* device generates spurious wakeup, ignore remote wakeup capability */ - #define USB_QUIRK_IGNORE_REMOTE_WAKEUP BIT(9) - -+/* device can't handle Link Power Management */ -+#define USB_QUIRK_NO_LPM BIT(10) -+ - #endif /* __LINUX_USB_QUIRKS_H */ -diff --git a/include/trace/events/iommu.h b/include/trace/events/iommu.h -index a8f5c32..2c7befb 100644 ---- a/include/trace/events/iommu.h -+++ b/include/trace/events/iommu.h -@@ -83,7 +83,7 @@ DEFINE_EVENT(iommu_device_event, detach_device_from_domain, - TP_ARGS(dev) - ); - --DECLARE_EVENT_CLASS(iommu_map_unmap, -+TRACE_EVENT(map, - - TP_PROTO(unsigned long iova, phys_addr_t paddr, size_t size), - -@@ -92,7 +92,7 @@ DECLARE_EVENT_CLASS(iommu_map_unmap, - TP_STRUCT__entry( - __field(u64, iova) - __field(u64, paddr) -- __field(int, size) -+ __field(size_t, size) - ), - - TP_fast_assign( -@@ -101,26 +101,31 @@ DECLARE_EVENT_CLASS(iommu_map_unmap, - __entry->size = size; - ), - -- TP_printk("IOMMU: iova=0x%016llx paddr=0x%016llx size=0x%x", -+ TP_printk("IOMMU: iova=0x%016llx paddr=0x%016llx size=%zu", - __entry->iova, __entry->paddr, __entry->size - ) - ); - --DEFINE_EVENT(iommu_map_unmap, map, -+TRACE_EVENT(unmap, - -- TP_PROTO(unsigned long iova, phys_addr_t paddr, size_t size), -- -- TP_ARGS(iova, paddr, size) --); -+ TP_PROTO(unsigned long iova, size_t size, size_t unmapped_size), - --DEFINE_EVENT_PRINT(iommu_map_unmap, unmap, -+ TP_ARGS(iova, size, unmapped_size), - -- TP_PROTO(unsigned long iova, phys_addr_t paddr, size_t size), -+ TP_STRUCT__entry( -+ __field(u64, iova) -+ __field(size_t, size) -+ __field(size_t, unmapped_size) -+ ), - -- TP_ARGS(iova, paddr, size), -+ TP_fast_assign( -+ __entry->iova = iova; -+ __entry->size = size; -+ __entry->unmapped_size = unmapped_size; -+ ), - -- TP_printk("IOMMU: iova=0x%016llx size=0x%x", -- __entry->iova, __entry->size -+ TP_printk("IOMMU: iova=0x%016llx size=%zu unmapped_size=%zu", -+ __entry->iova, __entry->size, __entry->unmapped_size - ) - ); - -diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig -index 225086b..9a76e3b 100644 ---- a/kernel/irq/Kconfig -+++ b/kernel/irq/Kconfig -@@ -55,6 +55,21 @@ config GENERIC_IRQ_CHIP - config IRQ_DOMAIN - bool - -+# Support for hierarchical irq domains -+config IRQ_DOMAIN_HIERARCHY -+ bool -+ select IRQ_DOMAIN -+ -+# Generic MSI interrupt support -+config GENERIC_MSI_IRQ -+ bool -+ -+# Generic MSI hierarchical interrupt domain support -+config GENERIC_MSI_IRQ_DOMAIN -+ bool -+ select IRQ_DOMAIN_HIERARCHY -+ select GENERIC_MSI_IRQ -+ - config HANDLE_DOMAIN_IRQ - bool - -diff --git a/kernel/irq/Makefile b/kernel/irq/Makefile -index fff1738..d121235 100644 ---- a/kernel/irq/Makefile -+++ b/kernel/irq/Makefile -@@ -6,3 +6,4 @@ obj-$(CONFIG_IRQ_DOMAIN) += irqdomain.o - obj-$(CONFIG_PROC_FS) += proc.o - obj-$(CONFIG_GENERIC_PENDING_IRQ) += migration.o - obj-$(CONFIG_PM_SLEEP) += pm.o -+obj-$(CONFIG_GENERIC_MSI_IRQ) += msi.o -diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c -index e5202f0..63c16d1 100644 ---- a/kernel/irq/chip.c -+++ b/kernel/irq/chip.c -@@ -15,6 +15,7 @@ - #include - #include - #include -+#include - - #include - -@@ -178,6 +179,7 @@ int irq_startup(struct irq_desc *desc, bool resend) - irq_state_clr_disabled(desc); - desc->depth = 0; - -+ irq_domain_activate_irq(&desc->irq_data); - if (desc->irq_data.chip->irq_startup) { - ret = desc->irq_data.chip->irq_startup(&desc->irq_data); - irq_state_clr_masked(desc); -@@ -199,6 +201,7 @@ void irq_shutdown(struct irq_desc *desc) - desc->irq_data.chip->irq_disable(&desc->irq_data); - else - desc->irq_data.chip->irq_mask(&desc->irq_data); -+ irq_domain_deactivate_irq(&desc->irq_data); - irq_state_set_masked(desc); - } - -@@ -847,3 +850,105 @@ void irq_cpu_offline(void) - raw_spin_unlock_irqrestore(&desc->lock, flags); - } - } -+ -+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY -+/** -+ * irq_chip_ack_parent - Acknowledge the parent interrupt -+ * @data: Pointer to interrupt specific data -+ */ -+void irq_chip_ack_parent(struct irq_data *data) -+{ -+ data = data->parent_data; -+ data->chip->irq_ack(data); -+} -+ -+/** -+ * irq_chip_mask_parent - Mask the parent interrupt -+ * @data: Pointer to interrupt specific data -+ */ -+void irq_chip_mask_parent(struct irq_data *data) -+{ -+ data = data->parent_data; -+ data->chip->irq_mask(data); -+} -+ -+/** -+ * irq_chip_unmask_parent - Unmask the parent interrupt -+ * @data: Pointer to interrupt specific data -+ */ -+void irq_chip_unmask_parent(struct irq_data *data) -+{ -+ data = data->parent_data; -+ data->chip->irq_unmask(data); -+} -+ -+/** -+ * irq_chip_eoi_parent - Invoke EOI on the parent interrupt -+ * @data: Pointer to interrupt specific data -+ */ -+void irq_chip_eoi_parent(struct irq_data *data) -+{ -+ data = data->parent_data; -+ data->chip->irq_eoi(data); -+} -+ -+/** -+ * irq_chip_set_affinity_parent - Set affinity on the parent interrupt -+ * @data: Pointer to interrupt specific data -+ * @dest: The affinity mask to set -+ * @force: Flag to enforce setting (disable online checks) -+ * -+ * Conditinal, as the underlying parent chip might not implement it. -+ */ -+int irq_chip_set_affinity_parent(struct irq_data *data, -+ const struct cpumask *dest, bool force) -+{ -+ data = data->parent_data; -+ if (data->chip->irq_set_affinity) -+ return data->chip->irq_set_affinity(data, dest, force); -+ -+ return -ENOSYS; -+} -+ -+/** -+ * irq_chip_retrigger_hierarchy - Retrigger an interrupt in hardware -+ * @data: Pointer to interrupt specific data -+ * -+ * Iterate through the domain hierarchy of the interrupt and check -+ * whether a hw retrigger function exists. If yes, invoke it. -+ */ -+int irq_chip_retrigger_hierarchy(struct irq_data *data) -+{ -+ for (data = data->parent_data; data; data = data->parent_data) -+ if (data->chip && data->chip->irq_retrigger) -+ return data->chip->irq_retrigger(data); -+ -+ return -ENOSYS; -+} -+#endif -+ -+/** -+ * irq_chip_compose_msi_msg - Componse msi message for a irq chip -+ * @data: Pointer to interrupt specific data -+ * @msg: Pointer to the MSI message -+ * -+ * For hierarchical domains we find the first chip in the hierarchy -+ * which implements the irq_compose_msi_msg callback. For non -+ * hierarchical we use the top level chip. -+ */ -+int irq_chip_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) -+{ -+ struct irq_data *pos = NULL; -+ -+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY -+ for (; data; data = data->parent_data) -+#endif -+ if (data->chip && data->chip->irq_compose_msi_msg) -+ pos = data; -+ if (!pos) -+ return -ENOSYS; -+ -+ pos->chip->irq_compose_msi_msg(pos, msg); -+ -+ return 0; -+} -diff --git a/kernel/irq/generic-chip.c b/kernel/irq/generic-chip.c -index cf80e7b..61024e8 100644 ---- a/kernel/irq/generic-chip.c -+++ b/kernel/irq/generic-chip.c -@@ -39,7 +39,7 @@ void irq_gc_mask_disable_reg(struct irq_data *d) - u32 mask = d->mask; - - irq_gc_lock(gc); -- irq_reg_writel(mask, gc->reg_base + ct->regs.disable); -+ irq_reg_writel(gc, mask, ct->regs.disable); - *ct->mask_cache &= ~mask; - irq_gc_unlock(gc); - } -@@ -59,7 +59,7 @@ void irq_gc_mask_set_bit(struct irq_data *d) - - irq_gc_lock(gc); - *ct->mask_cache |= mask; -- irq_reg_writel(*ct->mask_cache, gc->reg_base + ct->regs.mask); -+ irq_reg_writel(gc, *ct->mask_cache, ct->regs.mask); - irq_gc_unlock(gc); - } - EXPORT_SYMBOL_GPL(irq_gc_mask_set_bit); -@@ -79,7 +79,7 @@ void irq_gc_mask_clr_bit(struct irq_data *d) - - irq_gc_lock(gc); - *ct->mask_cache &= ~mask; -- irq_reg_writel(*ct->mask_cache, gc->reg_base + ct->regs.mask); -+ irq_reg_writel(gc, *ct->mask_cache, ct->regs.mask); - irq_gc_unlock(gc); - } - EXPORT_SYMBOL_GPL(irq_gc_mask_clr_bit); -@@ -98,7 +98,7 @@ void irq_gc_unmask_enable_reg(struct irq_data *d) - u32 mask = d->mask; - - irq_gc_lock(gc); -- irq_reg_writel(mask, gc->reg_base + ct->regs.enable); -+ irq_reg_writel(gc, mask, ct->regs.enable); - *ct->mask_cache |= mask; - irq_gc_unlock(gc); - } -@@ -114,7 +114,7 @@ void irq_gc_ack_set_bit(struct irq_data *d) - u32 mask = d->mask; - - irq_gc_lock(gc); -- irq_reg_writel(mask, gc->reg_base + ct->regs.ack); -+ irq_reg_writel(gc, mask, ct->regs.ack); - irq_gc_unlock(gc); - } - EXPORT_SYMBOL_GPL(irq_gc_ack_set_bit); -@@ -130,7 +130,7 @@ void irq_gc_ack_clr_bit(struct irq_data *d) - u32 mask = ~d->mask; - - irq_gc_lock(gc); -- irq_reg_writel(mask, gc->reg_base + ct->regs.ack); -+ irq_reg_writel(gc, mask, ct->regs.ack); - irq_gc_unlock(gc); - } - -@@ -145,8 +145,8 @@ void irq_gc_mask_disable_reg_and_ack(struct irq_data *d) - u32 mask = d->mask; - - irq_gc_lock(gc); -- irq_reg_writel(mask, gc->reg_base + ct->regs.mask); -- irq_reg_writel(mask, gc->reg_base + ct->regs.ack); -+ irq_reg_writel(gc, mask, ct->regs.mask); -+ irq_reg_writel(gc, mask, ct->regs.ack); - irq_gc_unlock(gc); - } - -@@ -161,7 +161,7 @@ void irq_gc_eoi(struct irq_data *d) - u32 mask = d->mask; - - irq_gc_lock(gc); -- irq_reg_writel(mask, gc->reg_base + ct->regs.eoi); -+ irq_reg_writel(gc, mask, ct->regs.eoi); - irq_gc_unlock(gc); - } - -@@ -191,6 +191,16 @@ int irq_gc_set_wake(struct irq_data *d, unsigned int on) - return 0; - } - -+static u32 irq_readl_be(void __iomem *addr) -+{ -+ return ioread32be(addr); -+} -+ -+static void irq_writel_be(u32 val, void __iomem *addr) -+{ -+ iowrite32be(val, addr); -+} -+ - static void - irq_init_generic_chip(struct irq_chip_generic *gc, const char *name, - int num_ct, unsigned int irq_base, -@@ -245,7 +255,7 @@ irq_gc_init_mask_cache(struct irq_chip_generic *gc, enum irq_gc_flags flags) - } - ct[i].mask_cache = mskptr; - if (flags & IRQ_GC_INIT_MASK_CACHE) -- *mskptr = irq_reg_readl(gc->reg_base + mskreg); -+ *mskptr = irq_reg_readl(gc, mskreg); - } - } - -@@ -300,7 +310,13 @@ int irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip, - dgc->gc[i] = gc = tmp; - irq_init_generic_chip(gc, name, num_ct, i * irqs_per_chip, - NULL, handler); -+ - gc->domain = d; -+ if (gcflags & IRQ_GC_BE_IO) { -+ gc->reg_readl = &irq_readl_be; -+ gc->reg_writel = &irq_writel_be; -+ } -+ - raw_spin_lock_irqsave(&gc_lock, flags); - list_add_tail(&gc->list, &gc_list); - raw_spin_unlock_irqrestore(&gc_lock, flags); -diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c -index 6534ff6..021f823 100644 ---- a/kernel/irq/irqdomain.c -+++ b/kernel/irq/irqdomain.c -@@ -23,6 +23,10 @@ static DEFINE_MUTEX(irq_domain_mutex); - static DEFINE_MUTEX(revmap_trees_mutex); - static struct irq_domain *irq_default_domain; - -+static int irq_domain_alloc_descs(int virq, unsigned int nr_irqs, -+ irq_hw_number_t hwirq, int node); -+static void irq_domain_check_hierarchy(struct irq_domain *domain); -+ - /** - * __irq_domain_add() - Allocate a new irq_domain data structure - * @of_node: optional device-tree node of the interrupt controller -@@ -30,7 +34,7 @@ static struct irq_domain *irq_default_domain; - * @hwirq_max: Maximum number of interrupts supported by controller - * @direct_max: Maximum value of direct maps; Use ~0 for no limit; 0 for no - * direct mapping -- * @ops: map/unmap domain callbacks -+ * @ops: domain callbacks - * @host_data: Controller private data pointer - * - * Allocates and initialize and irq_domain structure. -@@ -56,6 +60,7 @@ struct irq_domain *__irq_domain_add(struct device_node *of_node, int size, - domain->hwirq_max = hwirq_max; - domain->revmap_size = size; - domain->revmap_direct_max_irq = direct_max; -+ irq_domain_check_hierarchy(domain); - - mutex_lock(&irq_domain_mutex); - list_add(&domain->link, &irq_domain_list); -@@ -109,7 +114,7 @@ EXPORT_SYMBOL_GPL(irq_domain_remove); - * @first_irq: first number of irq block assigned to the domain, - * pass zero to assign irqs on-the-fly. If first_irq is non-zero, then - * pre-map all of the irqs in the domain to virqs starting at first_irq. -- * @ops: map/unmap domain callbacks -+ * @ops: domain callbacks - * @host_data: Controller private data pointer - * - * Allocates an irq_domain, and optionally if first_irq is positive then also -@@ -174,20 +179,20 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node, - - domain = __irq_domain_add(of_node, first_hwirq + size, - first_hwirq + size, 0, ops, host_data); -- if (!domain) -- return NULL; -- -- irq_domain_associate_many(domain, first_irq, first_hwirq, size); -+ if (domain) -+ irq_domain_associate_many(domain, first_irq, first_hwirq, size); - - return domain; - } - EXPORT_SYMBOL_GPL(irq_domain_add_legacy); - - /** -- * irq_find_host() - Locates a domain for a given device node -+ * irq_find_matching_host() - Locates a domain for a given device node - * @node: device-tree node of the interrupt controller -+ * @bus_token: domain-specific data - */ --struct irq_domain *irq_find_host(struct device_node *node) -+struct irq_domain *irq_find_matching_host(struct device_node *node, -+ enum irq_domain_bus_token bus_token) - { - struct irq_domain *h, *found = NULL; - int rc; -@@ -196,13 +201,19 @@ struct irq_domain *irq_find_host(struct device_node *node) - * it might potentially be set to match all interrupts in - * the absence of a device node. This isn't a problem so far - * yet though... -+ * -+ * bus_token == DOMAIN_BUS_ANY matches any domain, any other -+ * values must generate an exact match for the domain to be -+ * selected. - */ - mutex_lock(&irq_domain_mutex); - list_for_each_entry(h, &irq_domain_list, link) { - if (h->ops->match) -- rc = h->ops->match(h, node); -+ rc = h->ops->match(h, node, bus_token); - else -- rc = (h->of_node != NULL) && (h->of_node == node); -+ rc = ((h->of_node != NULL) && (h->of_node == node) && -+ ((bus_token == DOMAIN_BUS_ANY) || -+ (h->bus_token == bus_token))); - - if (rc) { - found = h; -@@ -212,7 +223,7 @@ struct irq_domain *irq_find_host(struct device_node *node) - mutex_unlock(&irq_domain_mutex); - return found; - } --EXPORT_SYMBOL_GPL(irq_find_host); -+EXPORT_SYMBOL_GPL(irq_find_matching_host); - - /** - * irq_set_default_host() - Set a "default" irq domain -@@ -388,7 +399,6 @@ EXPORT_SYMBOL_GPL(irq_create_direct_mapping); - unsigned int irq_create_mapping(struct irq_domain *domain, - irq_hw_number_t hwirq) - { -- unsigned int hint; - int virq; - - pr_debug("irq_create_mapping(0x%p, 0x%lx)\n", domain, hwirq); -@@ -410,12 +420,8 @@ unsigned int irq_create_mapping(struct irq_domain *domain, - } - - /* Allocate a virtual interrupt number */ -- hint = hwirq % nr_irqs; -- if (hint == 0) -- hint++; -- virq = irq_alloc_desc_from(hint, of_node_to_nid(domain->of_node)); -- if (virq <= 0) -- virq = irq_alloc_desc_from(1, of_node_to_nid(domain->of_node)); -+ virq = irq_domain_alloc_descs(-1, 1, hwirq, -+ of_node_to_nid(domain->of_node)); - if (virq <= 0) { - pr_debug("-> virq allocation failed\n"); - return 0; -@@ -471,7 +477,7 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data) - struct irq_domain *domain; - irq_hw_number_t hwirq; - unsigned int type = IRQ_TYPE_NONE; -- unsigned int virq; -+ int virq; - - domain = irq_data->np ? irq_find_host(irq_data->np) : irq_default_domain; - if (!domain) { -@@ -489,10 +495,24 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data) - return 0; - } - -- /* Create mapping */ -- virq = irq_create_mapping(domain, hwirq); -- if (!virq) -- return virq; -+ if (irq_domain_is_hierarchy(domain)) { -+ /* -+ * If we've already configured this interrupt, -+ * don't do it again, or hell will break loose. -+ */ -+ virq = irq_find_mapping(domain, hwirq); -+ if (virq) -+ return virq; -+ -+ virq = irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, irq_data); -+ if (virq <= 0) -+ return 0; -+ } else { -+ /* Create mapping */ -+ virq = irq_create_mapping(domain, hwirq); -+ if (!virq) -+ return virq; -+ } - - /* Set type if specified and different than the current one */ - if (type != IRQ_TYPE_NONE && -@@ -540,8 +560,8 @@ unsigned int irq_find_mapping(struct irq_domain *domain, - return 0; - - if (hwirq < domain->revmap_direct_max_irq) { -- data = irq_get_irq_data(hwirq); -- if (data && (data->domain == domain) && (data->hwirq == hwirq)) -+ data = irq_domain_get_irq_data(domain, hwirq); -+ if (data && data->hwirq == hwirq) - return hwirq; - } - -@@ -709,3 +729,518 @@ const struct irq_domain_ops irq_domain_simple_ops = { - .xlate = irq_domain_xlate_onetwocell, - }; - EXPORT_SYMBOL_GPL(irq_domain_simple_ops); -+ -+static int irq_domain_alloc_descs(int virq, unsigned int cnt, -+ irq_hw_number_t hwirq, int node) -+{ -+ unsigned int hint; -+ -+ if (virq >= 0) { -+ virq = irq_alloc_descs(virq, virq, cnt, node); -+ } else { -+ hint = hwirq % nr_irqs; -+ if (hint == 0) -+ hint++; -+ virq = irq_alloc_descs_from(hint, cnt, node); -+ if (virq <= 0 && hint > 1) -+ virq = irq_alloc_descs_from(1, cnt, node); -+ } -+ -+ return virq; -+} -+ -+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY -+/** -+ * irq_domain_add_hierarchy - Add a irqdomain into the hierarchy -+ * @parent: Parent irq domain to associate with the new domain -+ * @flags: Irq domain flags associated to the domain -+ * @size: Size of the domain. See below -+ * @node: Optional device-tree node of the interrupt controller -+ * @ops: Pointer to the interrupt domain callbacks -+ * @host_data: Controller private data pointer -+ * -+ * If @size is 0 a tree domain is created, otherwise a linear domain. -+ * -+ * If successful the parent is associated to the new domain and the -+ * domain flags are set. -+ * Returns pointer to IRQ domain, or NULL on failure. -+ */ -+struct irq_domain *irq_domain_add_hierarchy(struct irq_domain *parent, -+ unsigned int flags, -+ unsigned int size, -+ struct device_node *node, -+ const struct irq_domain_ops *ops, -+ void *host_data) -+{ -+ struct irq_domain *domain; -+ -+ if (size) -+ domain = irq_domain_add_linear(node, size, ops, host_data); -+ else -+ domain = irq_domain_add_tree(node, ops, host_data); -+ if (domain) { -+ domain->parent = parent; -+ domain->flags |= flags; -+ } -+ -+ return domain; -+} -+ -+static void irq_domain_insert_irq(int virq) -+{ -+ struct irq_data *data; -+ -+ for (data = irq_get_irq_data(virq); data; data = data->parent_data) { -+ struct irq_domain *domain = data->domain; -+ irq_hw_number_t hwirq = data->hwirq; -+ -+ if (hwirq < domain->revmap_size) { -+ domain->linear_revmap[hwirq] = virq; -+ } else { -+ mutex_lock(&revmap_trees_mutex); -+ radix_tree_insert(&domain->revmap_tree, hwirq, data); -+ mutex_unlock(&revmap_trees_mutex); -+ } -+ -+ /* If not already assigned, give the domain the chip's name */ -+ if (!domain->name && data->chip) -+ domain->name = data->chip->name; -+ } -+ -+ irq_clear_status_flags(virq, IRQ_NOREQUEST); -+} -+ -+static void irq_domain_remove_irq(int virq) -+{ -+ struct irq_data *data; -+ -+ irq_set_status_flags(virq, IRQ_NOREQUEST); -+ irq_set_chip_and_handler(virq, NULL, NULL); -+ synchronize_irq(virq); -+ smp_mb(); -+ -+ for (data = irq_get_irq_data(virq); data; data = data->parent_data) { -+ struct irq_domain *domain = data->domain; -+ irq_hw_number_t hwirq = data->hwirq; -+ -+ if (hwirq < domain->revmap_size) { -+ domain->linear_revmap[hwirq] = 0; -+ } else { -+ mutex_lock(&revmap_trees_mutex); -+ radix_tree_delete(&domain->revmap_tree, hwirq); -+ mutex_unlock(&revmap_trees_mutex); -+ } -+ } -+} -+ -+static struct irq_data *irq_domain_insert_irq_data(struct irq_domain *domain, -+ struct irq_data *child) -+{ -+ struct irq_data *irq_data; -+ -+ irq_data = kzalloc_node(sizeof(*irq_data), GFP_KERNEL, child->node); -+ if (irq_data) { -+ child->parent_data = irq_data; -+ irq_data->irq = child->irq; -+ irq_data->node = child->node; -+ irq_data->domain = domain; -+ } -+ -+ return irq_data; -+} -+ -+static void irq_domain_free_irq_data(unsigned int virq, unsigned int nr_irqs) -+{ -+ struct irq_data *irq_data, *tmp; -+ int i; -+ -+ for (i = 0; i < nr_irqs; i++) { -+ irq_data = irq_get_irq_data(virq + i); -+ tmp = irq_data->parent_data; -+ irq_data->parent_data = NULL; -+ irq_data->domain = NULL; -+ -+ while (tmp) { -+ irq_data = tmp; -+ tmp = tmp->parent_data; -+ kfree(irq_data); -+ } -+ } -+} -+ -+static int irq_domain_alloc_irq_data(struct irq_domain *domain, -+ unsigned int virq, unsigned int nr_irqs) -+{ -+ struct irq_data *irq_data; -+ struct irq_domain *parent; -+ int i; -+ -+ /* The outermost irq_data is embedded in struct irq_desc */ -+ for (i = 0; i < nr_irqs; i++) { -+ irq_data = irq_get_irq_data(virq + i); -+ irq_data->domain = domain; -+ -+ for (parent = domain->parent; parent; parent = parent->parent) { -+ irq_data = irq_domain_insert_irq_data(parent, irq_data); -+ if (!irq_data) { -+ irq_domain_free_irq_data(virq, i + 1); -+ return -ENOMEM; -+ } -+ } -+ } -+ -+ return 0; -+} -+ -+/** -+ * irq_domain_get_irq_data - Get irq_data associated with @virq and @domain -+ * @domain: domain to match -+ * @virq: IRQ number to get irq_data -+ */ -+struct irq_data *irq_domain_get_irq_data(struct irq_domain *domain, -+ unsigned int virq) -+{ -+ struct irq_data *irq_data; -+ -+ for (irq_data = irq_get_irq_data(virq); irq_data; -+ irq_data = irq_data->parent_data) -+ if (irq_data->domain == domain) -+ return irq_data; -+ -+ return NULL; -+} -+ -+/** -+ * irq_domain_set_hwirq_and_chip - Set hwirq and irqchip of @virq at @domain -+ * @domain: Interrupt domain to match -+ * @virq: IRQ number -+ * @hwirq: The hwirq number -+ * @chip: The associated interrupt chip -+ * @chip_data: The associated chip data -+ */ -+int irq_domain_set_hwirq_and_chip(struct irq_domain *domain, unsigned int virq, -+ irq_hw_number_t hwirq, struct irq_chip *chip, -+ void *chip_data) -+{ -+ struct irq_data *irq_data = irq_domain_get_irq_data(domain, virq); -+ -+ if (!irq_data) -+ return -ENOENT; -+ -+ irq_data->hwirq = hwirq; -+ irq_data->chip = chip ? chip : &no_irq_chip; -+ irq_data->chip_data = chip_data; -+ -+ return 0; -+} -+ -+/** -+ * irq_domain_set_info - Set the complete data for a @virq in @domain -+ * @domain: Interrupt domain to match -+ * @virq: IRQ number -+ * @hwirq: The hardware interrupt number -+ * @chip: The associated interrupt chip -+ * @chip_data: The associated interrupt chip data -+ * @handler: The interrupt flow handler -+ * @handler_data: The interrupt flow handler data -+ * @handler_name: The interrupt handler name -+ */ -+void irq_domain_set_info(struct irq_domain *domain, unsigned int virq, -+ irq_hw_number_t hwirq, struct irq_chip *chip, -+ void *chip_data, irq_flow_handler_t handler, -+ void *handler_data, const char *handler_name) -+{ -+ irq_domain_set_hwirq_and_chip(domain, virq, hwirq, chip, chip_data); -+ __irq_set_handler(virq, handler, 0, handler_name); -+ irq_set_handler_data(virq, handler_data); -+} -+ -+/** -+ * irq_domain_reset_irq_data - Clear hwirq, chip and chip_data in @irq_data -+ * @irq_data: The pointer to irq_data -+ */ -+void irq_domain_reset_irq_data(struct irq_data *irq_data) -+{ -+ irq_data->hwirq = 0; -+ irq_data->chip = &no_irq_chip; -+ irq_data->chip_data = NULL; -+} -+ -+/** -+ * irq_domain_free_irqs_common - Clear irq_data and free the parent -+ * @domain: Interrupt domain to match -+ * @virq: IRQ number to start with -+ * @nr_irqs: The number of irqs to free -+ */ -+void irq_domain_free_irqs_common(struct irq_domain *domain, unsigned int virq, -+ unsigned int nr_irqs) -+{ -+ struct irq_data *irq_data; -+ int i; -+ -+ for (i = 0; i < nr_irqs; i++) { -+ irq_data = irq_domain_get_irq_data(domain, virq + i); -+ if (irq_data) -+ irq_domain_reset_irq_data(irq_data); -+ } -+ irq_domain_free_irqs_parent(domain, virq, nr_irqs); -+} -+ -+/** -+ * irq_domain_free_irqs_top - Clear handler and handler data, clear irqdata and free parent -+ * @domain: Interrupt domain to match -+ * @virq: IRQ number to start with -+ * @nr_irqs: The number of irqs to free -+ */ -+void irq_domain_free_irqs_top(struct irq_domain *domain, unsigned int virq, -+ unsigned int nr_irqs) -+{ -+ int i; -+ -+ for (i = 0; i < nr_irqs; i++) { -+ irq_set_handler_data(virq + i, NULL); -+ irq_set_handler(virq + i, NULL); -+ } -+ irq_domain_free_irqs_common(domain, virq, nr_irqs); -+} -+ -+static bool irq_domain_is_auto_recursive(struct irq_domain *domain) -+{ -+ return domain->flags & IRQ_DOMAIN_FLAG_AUTO_RECURSIVE; -+} -+ -+static void irq_domain_free_irqs_recursive(struct irq_domain *domain, -+ unsigned int irq_base, -+ unsigned int nr_irqs) -+{ -+ domain->ops->free(domain, irq_base, nr_irqs); -+ if (irq_domain_is_auto_recursive(domain)) { -+ BUG_ON(!domain->parent); -+ irq_domain_free_irqs_recursive(domain->parent, irq_base, -+ nr_irqs); -+ } -+} -+ -+static int irq_domain_alloc_irqs_recursive(struct irq_domain *domain, -+ unsigned int irq_base, -+ unsigned int nr_irqs, void *arg) -+{ -+ int ret = 0; -+ struct irq_domain *parent = domain->parent; -+ bool recursive = irq_domain_is_auto_recursive(domain); -+ -+ BUG_ON(recursive && !parent); -+ if (recursive) -+ ret = irq_domain_alloc_irqs_recursive(parent, irq_base, -+ nr_irqs, arg); -+ if (ret >= 0) -+ ret = domain->ops->alloc(domain, irq_base, nr_irqs, arg); -+ if (ret < 0 && recursive) -+ irq_domain_free_irqs_recursive(parent, irq_base, nr_irqs); -+ -+ return ret; -+} -+ -+/** -+ * __irq_domain_alloc_irqs - Allocate IRQs from domain -+ * @domain: domain to allocate from -+ * @irq_base: allocate specified IRQ nubmer if irq_base >= 0 -+ * @nr_irqs: number of IRQs to allocate -+ * @node: NUMA node id for memory allocation -+ * @arg: domain specific argument -+ * @realloc: IRQ descriptors have already been allocated if true -+ * -+ * Allocate IRQ numbers and initialized all data structures to support -+ * hierarchy IRQ domains. -+ * Parameter @realloc is mainly to support legacy IRQs. -+ * Returns error code or allocated IRQ number -+ * -+ * The whole process to setup an IRQ has been split into two steps. -+ * The first step, __irq_domain_alloc_irqs(), is to allocate IRQ -+ * descriptor and required hardware resources. The second step, -+ * irq_domain_activate_irq(), is to program hardwares with preallocated -+ * resources. In this way, it's easier to rollback when failing to -+ * allocate resources. -+ */ -+int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base, -+ unsigned int nr_irqs, int node, void *arg, -+ bool realloc) -+{ -+ int i, ret, virq; -+ -+ if (domain == NULL) { -+ domain = irq_default_domain; -+ if (WARN(!domain, "domain is NULL; cannot allocate IRQ\n")) -+ return -EINVAL; -+ } -+ -+ if (!domain->ops->alloc) { -+ pr_debug("domain->ops->alloc() is NULL\n"); -+ return -ENOSYS; -+ } -+ -+ if (realloc && irq_base >= 0) { -+ virq = irq_base; -+ } else { -+ virq = irq_domain_alloc_descs(irq_base, nr_irqs, 0, node); -+ if (virq < 0) { -+ pr_debug("cannot allocate IRQ(base %d, count %d)\n", -+ irq_base, nr_irqs); -+ return virq; -+ } -+ } -+ -+ if (irq_domain_alloc_irq_data(domain, virq, nr_irqs)) { -+ pr_debug("cannot allocate memory for IRQ%d\n", virq); -+ ret = -ENOMEM; -+ goto out_free_desc; -+ } -+ -+ mutex_lock(&irq_domain_mutex); -+ ret = irq_domain_alloc_irqs_recursive(domain, virq, nr_irqs, arg); -+ if (ret < 0) { -+ mutex_unlock(&irq_domain_mutex); -+ goto out_free_irq_data; -+ } -+ for (i = 0; i < nr_irqs; i++) -+ irq_domain_insert_irq(virq + i); -+ mutex_unlock(&irq_domain_mutex); -+ -+ return virq; -+ -+out_free_irq_data: -+ irq_domain_free_irq_data(virq, nr_irqs); -+out_free_desc: -+ irq_free_descs(virq, nr_irqs); -+ return ret; -+} -+ -+/** -+ * irq_domain_free_irqs - Free IRQ number and associated data structures -+ * @virq: base IRQ number -+ * @nr_irqs: number of IRQs to free -+ */ -+void irq_domain_free_irqs(unsigned int virq, unsigned int nr_irqs) -+{ -+ struct irq_data *data = irq_get_irq_data(virq); -+ int i; -+ -+ if (WARN(!data || !data->domain || !data->domain->ops->free, -+ "NULL pointer, cannot free irq\n")) -+ return; -+ -+ mutex_lock(&irq_domain_mutex); -+ for (i = 0; i < nr_irqs; i++) -+ irq_domain_remove_irq(virq + i); -+ irq_domain_free_irqs_recursive(data->domain, virq, nr_irqs); -+ mutex_unlock(&irq_domain_mutex); -+ -+ irq_domain_free_irq_data(virq, nr_irqs); -+ irq_free_descs(virq, nr_irqs); -+} -+ -+/** -+ * irq_domain_alloc_irqs_parent - Allocate interrupts from parent domain -+ * @irq_base: Base IRQ number -+ * @nr_irqs: Number of IRQs to allocate -+ * @arg: Allocation data (arch/domain specific) -+ * -+ * Check whether the domain has been setup recursive. If not allocate -+ * through the parent domain. -+ */ -+int irq_domain_alloc_irqs_parent(struct irq_domain *domain, -+ unsigned int irq_base, unsigned int nr_irqs, -+ void *arg) -+{ -+ /* irq_domain_alloc_irqs_recursive() has called parent's alloc() */ -+ if (irq_domain_is_auto_recursive(domain)) -+ return 0; -+ -+ domain = domain->parent; -+ if (domain) -+ return irq_domain_alloc_irqs_recursive(domain, irq_base, -+ nr_irqs, arg); -+ return -ENOSYS; -+} -+ -+/** -+ * irq_domain_free_irqs_parent - Free interrupts from parent domain -+ * @irq_base: Base IRQ number -+ * @nr_irqs: Number of IRQs to free -+ * -+ * Check whether the domain has been setup recursive. If not free -+ * through the parent domain. -+ */ -+void irq_domain_free_irqs_parent(struct irq_domain *domain, -+ unsigned int irq_base, unsigned int nr_irqs) -+{ -+ /* irq_domain_free_irqs_recursive() will call parent's free */ -+ if (!irq_domain_is_auto_recursive(domain) && domain->parent) -+ irq_domain_free_irqs_recursive(domain->parent, irq_base, -+ nr_irqs); -+} -+ -+/** -+ * irq_domain_activate_irq - Call domain_ops->activate recursively to activate -+ * interrupt -+ * @irq_data: outermost irq_data associated with interrupt -+ * -+ * This is the second step to call domain_ops->activate to program interrupt -+ * controllers, so the interrupt could actually get delivered. -+ */ -+void irq_domain_activate_irq(struct irq_data *irq_data) -+{ -+ if (irq_data && irq_data->domain) { -+ struct irq_domain *domain = irq_data->domain; -+ -+ if (irq_data->parent_data) -+ irq_domain_activate_irq(irq_data->parent_data); -+ if (domain->ops->activate) -+ domain->ops->activate(domain, irq_data); -+ } -+} -+ -+/** -+ * irq_domain_deactivate_irq - Call domain_ops->deactivate recursively to -+ * deactivate interrupt -+ * @irq_data: outermost irq_data associated with interrupt -+ * -+ * It calls domain_ops->deactivate to program interrupt controllers to disable -+ * interrupt delivery. -+ */ -+void irq_domain_deactivate_irq(struct irq_data *irq_data) -+{ -+ if (irq_data && irq_data->domain) { -+ struct irq_domain *domain = irq_data->domain; -+ -+ if (domain->ops->deactivate) -+ domain->ops->deactivate(domain, irq_data); -+ if (irq_data->parent_data) -+ irq_domain_deactivate_irq(irq_data->parent_data); -+ } -+} -+ -+static void irq_domain_check_hierarchy(struct irq_domain *domain) -+{ -+ /* Hierarchy irq_domains must implement callback alloc() */ -+ if (domain->ops->alloc) -+ domain->flags |= IRQ_DOMAIN_FLAG_HIERARCHY; -+} -+#else /* CONFIG_IRQ_DOMAIN_HIERARCHY */ -+/** -+ * irq_domain_get_irq_data - Get irq_data associated with @virq and @domain -+ * @domain: domain to match -+ * @virq: IRQ number to get irq_data -+ */ -+struct irq_data *irq_domain_get_irq_data(struct irq_domain *domain, -+ unsigned int virq) -+{ -+ struct irq_data *irq_data = irq_get_irq_data(virq); -+ -+ return (irq_data && irq_data->domain == domain) ? irq_data : NULL; -+} -+ -+static void irq_domain_check_hierarchy(struct irq_domain *domain) -+{ -+} -+#endif /* CONFIG_IRQ_DOMAIN_HIERARCHY */ -diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c -index 0a9104b..8069237 100644 ---- a/kernel/irq/manage.c -+++ b/kernel/irq/manage.c -@@ -183,6 +183,7 @@ int irq_do_set_affinity(struct irq_data *data, const struct cpumask *mask, - ret = chip->irq_set_affinity(data, mask, force); - switch (ret) { - case IRQ_SET_MASK_OK: -+ case IRQ_SET_MASK_OK_DONE: - cpumask_copy(data->affinity, mask); - case IRQ_SET_MASK_OK_NOCOPY: - irq_set_thread_affinity(desc); -@@ -600,6 +601,7 @@ int __irq_set_trigger(struct irq_desc *desc, unsigned int irq, - - switch (ret) { - case IRQ_SET_MASK_OK: -+ case IRQ_SET_MASK_OK_DONE: - irqd_clear(&desc->irq_data, IRQD_TRIGGER_MASK); - irqd_set(&desc->irq_data, flags); - -diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c -new file mode 100644 -index 0000000..2495ed0 ---- /dev/null -+++ b/kernel/irq/msi.c -@@ -0,0 +1,347 @@ -+/* -+ * linux/kernel/irq/msi.c -+ * -+ * Copyright (C) 2014 Intel Corp. -+ * Author: Jiang Liu -+ * -+ * This file is licensed under GPLv2. -+ * -+ * This file contains common code to support Message Signalled Interrupt for -+ * PCI compatible and non PCI compatible devices. -+ */ -+#include -+#include -+#include -+#include -+#include -+ -+/* Temparory solution for building, will be removed later */ -+#include -+ -+struct msi_desc *alloc_msi_entry(struct device *dev) -+{ -+ struct msi_desc *desc = kzalloc(sizeof(*desc), GFP_KERNEL); -+ if (!desc) -+ return NULL; -+ -+ INIT_LIST_HEAD(&desc->list); -+ desc->dev = dev; -+ -+ return desc; -+} -+ -+void free_msi_entry(struct msi_desc *entry) -+{ -+ kfree(entry); -+} -+ -+void __get_cached_msi_msg(struct msi_desc *entry, struct msi_msg *msg) -+{ -+ *msg = entry->msg; -+} -+ -+void get_cached_msi_msg(unsigned int irq, struct msi_msg *msg) -+{ -+ struct msi_desc *entry = irq_get_msi_desc(irq); -+ -+ __get_cached_msi_msg(entry, msg); -+} -+EXPORT_SYMBOL_GPL(get_cached_msi_msg); -+ -+#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN -+static inline void irq_chip_write_msi_msg(struct irq_data *data, -+ struct msi_msg *msg) -+{ -+ data->chip->irq_write_msi_msg(data, msg); -+} -+ -+/** -+ * msi_domain_set_affinity - Generic affinity setter function for MSI domains -+ * @irq_data: The irq data associated to the interrupt -+ * @mask: The affinity mask to set -+ * @force: Flag to enforce setting (disable online checks) -+ * -+ * Intended to be used by MSI interrupt controllers which are -+ * implemented with hierarchical domains. -+ */ -+int msi_domain_set_affinity(struct irq_data *irq_data, -+ const struct cpumask *mask, bool force) -+{ -+ struct irq_data *parent = irq_data->parent_data; -+ struct msi_msg msg; -+ int ret; -+ -+ ret = parent->chip->irq_set_affinity(parent, mask, force); -+ if (ret >= 0 && ret != IRQ_SET_MASK_OK_DONE) { -+ BUG_ON(irq_chip_compose_msi_msg(irq_data, &msg)); -+ irq_chip_write_msi_msg(irq_data, &msg); -+ } -+ -+ return ret; -+} -+ -+static void msi_domain_activate(struct irq_domain *domain, -+ struct irq_data *irq_data) -+{ -+ struct msi_msg msg; -+ -+ BUG_ON(irq_chip_compose_msi_msg(irq_data, &msg)); -+ irq_chip_write_msi_msg(irq_data, &msg); -+} -+ -+static void msi_domain_deactivate(struct irq_domain *domain, -+ struct irq_data *irq_data) -+{ -+ struct msi_msg msg; -+ -+ memset(&msg, 0, sizeof(msg)); -+ irq_chip_write_msi_msg(irq_data, &msg); -+} -+ -+static int msi_domain_alloc(struct irq_domain *domain, unsigned int virq, -+ unsigned int nr_irqs, void *arg) -+{ -+ struct msi_domain_info *info = domain->host_data; -+ struct msi_domain_ops *ops = info->ops; -+ irq_hw_number_t hwirq = ops->get_hwirq(info, arg); -+ int i, ret; -+ -+ if (irq_find_mapping(domain, hwirq) > 0) -+ return -EEXIST; -+ -+ ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg); -+ if (ret < 0) -+ return ret; -+ -+ for (i = 0; i < nr_irqs; i++) { -+ ret = ops->msi_init(domain, info, virq + i, hwirq + i, arg); -+ if (ret < 0) { -+ if (ops->msi_free) { -+ for (i--; i > 0; i--) -+ ops->msi_free(domain, info, virq + i); -+ } -+ irq_domain_free_irqs_top(domain, virq, nr_irqs); -+ return ret; -+ } -+ } -+ -+ return 0; -+} -+ -+static void msi_domain_free(struct irq_domain *domain, unsigned int virq, -+ unsigned int nr_irqs) -+{ -+ struct msi_domain_info *info = domain->host_data; -+ int i; -+ -+ if (info->ops->msi_free) { -+ for (i = 0; i < nr_irqs; i++) -+ info->ops->msi_free(domain, info, virq + i); -+ } -+ irq_domain_free_irqs_top(domain, virq, nr_irqs); -+} -+ -+static struct irq_domain_ops msi_domain_ops = { -+ .alloc = msi_domain_alloc, -+ .free = msi_domain_free, -+ .activate = msi_domain_activate, -+ .deactivate = msi_domain_deactivate, -+}; -+ -+#ifdef GENERIC_MSI_DOMAIN_OPS -+static irq_hw_number_t msi_domain_ops_get_hwirq(struct msi_domain_info *info, -+ msi_alloc_info_t *arg) -+{ -+ return arg->hwirq; -+} -+ -+static int msi_domain_ops_prepare(struct irq_domain *domain, struct device *dev, -+ int nvec, msi_alloc_info_t *arg) -+{ -+ memset(arg, 0, sizeof(*arg)); -+ return 0; -+} -+ -+static void msi_domain_ops_set_desc(msi_alloc_info_t *arg, -+ struct msi_desc *desc) -+{ -+ arg->desc = desc; -+} -+#else -+#define msi_domain_ops_get_hwirq NULL -+#define msi_domain_ops_prepare NULL -+#define msi_domain_ops_set_desc NULL -+#endif /* !GENERIC_MSI_DOMAIN_OPS */ -+ -+static int msi_domain_ops_init(struct irq_domain *domain, -+ struct msi_domain_info *info, -+ unsigned int virq, irq_hw_number_t hwirq, -+ msi_alloc_info_t *arg) -+{ -+ irq_domain_set_hwirq_and_chip(domain, virq, hwirq, info->chip, -+ info->chip_data); -+ if (info->handler && info->handler_name) { -+ __irq_set_handler(virq, info->handler, 0, info->handler_name); -+ if (info->handler_data) -+ irq_set_handler_data(virq, info->handler_data); -+ } -+ return 0; -+} -+ -+static int msi_domain_ops_check(struct irq_domain *domain, -+ struct msi_domain_info *info, -+ struct device *dev) -+{ -+ return 0; -+} -+ -+static struct msi_domain_ops msi_domain_ops_default = { -+ .get_hwirq = msi_domain_ops_get_hwirq, -+ .msi_init = msi_domain_ops_init, -+ .msi_check = msi_domain_ops_check, -+ .msi_prepare = msi_domain_ops_prepare, -+ .set_desc = msi_domain_ops_set_desc, -+}; -+ -+static void msi_domain_update_dom_ops(struct msi_domain_info *info) -+{ -+ struct msi_domain_ops *ops = info->ops; -+ -+ if (ops == NULL) { -+ info->ops = &msi_domain_ops_default; -+ return; -+ } -+ -+ if (ops->get_hwirq == NULL) -+ ops->get_hwirq = msi_domain_ops_default.get_hwirq; -+ if (ops->msi_init == NULL) -+ ops->msi_init = msi_domain_ops_default.msi_init; -+ if (ops->msi_check == NULL) -+ ops->msi_check = msi_domain_ops_default.msi_check; -+ if (ops->msi_prepare == NULL) -+ ops->msi_prepare = msi_domain_ops_default.msi_prepare; -+ if (ops->set_desc == NULL) -+ ops->set_desc = msi_domain_ops_default.set_desc; -+} -+ -+static void msi_domain_update_chip_ops(struct msi_domain_info *info) -+{ -+ struct irq_chip *chip = info->chip; -+ -+ BUG_ON(!chip); -+ if (!chip->irq_mask) -+ chip->irq_mask = pci_msi_mask_irq; -+ if (!chip->irq_unmask) -+ chip->irq_unmask = pci_msi_unmask_irq; -+ if (!chip->irq_set_affinity) -+ chip->irq_set_affinity = msi_domain_set_affinity; -+} -+ -+/** -+ * msi_create_irq_domain - Create a MSI interrupt domain -+ * @of_node: Optional device-tree node of the interrupt controller -+ * @info: MSI domain info -+ * @parent: Parent irq domain -+ */ -+struct irq_domain *msi_create_irq_domain(struct device_node *node, -+ struct msi_domain_info *info, -+ struct irq_domain *parent) -+{ -+ if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS) -+ msi_domain_update_dom_ops(info); -+ if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS) -+ msi_domain_update_chip_ops(info); -+ -+ return irq_domain_add_hierarchy(parent, 0, 0, node, &msi_domain_ops, -+ info); -+} -+ -+/** -+ * msi_domain_alloc_irqs - Allocate interrupts from a MSI interrupt domain -+ * @domain: The domain to allocate from -+ * @dev: Pointer to device struct of the device for which the interrupts -+ * are allocated -+ * @nvec: The number of interrupts to allocate -+ * -+ * Returns 0 on success or an error code. -+ */ -+int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, -+ int nvec) -+{ -+ struct msi_domain_info *info = domain->host_data; -+ struct msi_domain_ops *ops = info->ops; -+ msi_alloc_info_t arg; -+ struct msi_desc *desc; -+ int i, ret, virq = -1; -+ -+ ret = ops->msi_check(domain, info, dev); -+ if (ret == 0) -+ ret = ops->msi_prepare(domain, dev, nvec, &arg); -+ if (ret) -+ return ret; -+ -+ for_each_msi_entry(desc, dev) { -+ ops->set_desc(&arg, desc); -+ if (info->flags & MSI_FLAG_IDENTITY_MAP) -+ virq = (int)ops->get_hwirq(info, &arg); -+ else -+ virq = -1; -+ -+ virq = __irq_domain_alloc_irqs(domain, virq, desc->nvec_used, -+ dev_to_node(dev), &arg, false); -+ if (virq < 0) { -+ ret = -ENOSPC; -+ if (ops->handle_error) -+ ret = ops->handle_error(domain, desc, ret); -+ if (ops->msi_finish) -+ ops->msi_finish(&arg, ret); -+ return ret; -+ } -+ -+ for (i = 0; i < desc->nvec_used; i++) -+ irq_set_msi_desc_off(virq, i, desc); -+ } -+ -+ if (ops->msi_finish) -+ ops->msi_finish(&arg, 0); -+ -+ for_each_msi_entry(desc, dev) { -+ if (desc->nvec_used == 1) -+ dev_dbg(dev, "irq %d for MSI\n", virq); -+ else -+ dev_dbg(dev, "irq [%d-%d] for MSI\n", -+ virq, virq + desc->nvec_used - 1); -+ } -+ -+ return 0; -+} -+ -+/** -+ * msi_domain_free_irqs - Free interrupts from a MSI interrupt @domain associated tp @dev -+ * @domain: The domain to managing the interrupts -+ * @dev: Pointer to device struct of the device for which the interrupts -+ * are free -+ */ -+void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev) -+{ -+ struct msi_desc *desc; -+ -+ for_each_msi_entry(desc, dev) { -+ irq_domain_free_irqs(desc->irq, desc->nvec_used); -+ desc->irq = 0; -+ } -+} -+ -+/** -+ * msi_get_domain_info - Get the MSI interrupt domain info for @domain -+ * @domain: The interrupt domain to retrieve data from -+ * -+ * Returns the pointer to the msi_domain_info stored in -+ * @domain->host_data. -+ */ -+struct msi_domain_info *msi_get_domain_info(struct irq_domain *domain) -+{ -+ return (struct msi_domain_info *)domain->host_data; -+} -+ -+#endif /* CONFIG_GENERIC_MSI_IRQ_DOMAIN */ -diff --git a/kernel/resource.c b/kernel/resource.c -index 0bcebff..19f2357 100644 ---- a/kernel/resource.c -+++ b/kernel/resource.c -@@ -22,6 +22,7 @@ - #include - #include - #include -+#include - #include - - -@@ -1529,6 +1530,30 @@ int iomem_is_exclusive(u64 addr) - return err; - } - -+struct resource_entry *resource_list_create_entry(struct resource *res, -+ size_t extra_size) -+{ -+ struct resource_entry *entry; -+ -+ entry = kzalloc(sizeof(*entry) + extra_size, GFP_KERNEL); -+ if (entry) { -+ INIT_LIST_HEAD(&entry->node); -+ entry->res = res ? res : &entry->__res; -+ } -+ -+ return entry; -+} -+EXPORT_SYMBOL(resource_list_create_entry); -+ -+void resource_list_free(struct list_head *head) -+{ -+ struct resource_entry *entry, *tmp; -+ -+ list_for_each_entry_safe(entry, tmp, head, node) -+ resource_list_destroy_entry(entry); -+} -+EXPORT_SYMBOL(resource_list_free); -+ - static int __init strict_iomem(char *str) - { - if (strstr(str, "relaxed")) -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.lib b/scripts/Makefile.lib -index 54be19a..5117552 100644 ---- a/scripts/Makefile.lib -+++ b/scripts/Makefile.lib -@@ -283,18 +283,6 @@ $(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)) -- - # Bzip2 - # --------------------------------------------------------------------------- - --- -2.1.0.27.g96db324 - diff --git a/packages/base/any/kernels/3.18.25/patches/add-nxp-arm64-ls2088ardb-device-tree.patch b/packages/base/any/kernels/3.18.25/patches/add-nxp-arm64-ls2088ardb-device-tree.patch deleted file mode 100644 index 28aff876..00000000 --- a/packages/base/any/kernels/3.18.25/patches/add-nxp-arm64-ls2088ardb-device-tree.patch +++ /dev/null @@ -1,1116 +0,0 @@ -From 0b8911d6263d5b70d41fd741bcead8b68a48ed2b Mon Sep 17 00:00:00 2001 -From: Shengzhou Liu -Date: Wed, 24 Aug 2016 16:16:16 +0800 -Subject: [PATCH] add nxp arm64 ls2088ardb device tree - ---- - arch/arm64/boot/dts/arm64-nxp-ls2088ardb-r1.dts | 256 ++++++++ - arch/arm64/boot/dts/fsl-ls2088a.dtsi | 833 ++++++++++++++++++++++++ - 2 files changed, 1089 insertions(+) - create mode 100644 arch/arm64/boot/dts/arm64-nxp-ls2088ardb-r1.dts - create mode 100644 arch/arm64/boot/dts/fsl-ls2088a.dtsi - -diff --git a/arch/arm64/boot/dts/arm64-nxp-ls2088ardb-r1.dts b/arch/arm64/boot/dts/arm64-nxp-ls2088ardb-r1.dts -new file mode 100644 -index 0000000..3e72718 ---- /dev/null -+++ b/arch/arm64/boot/dts/arm64-nxp-ls2088ardb-r1.dts -@@ -0,0 +1,256 @@ -+/* -+ * Device Tree file for NXP LS2088a RDB board -+ * -+ * Copyright (C) 2016, Freescale Semiconductor -+ * -+ * This file is licensed under the terms of the GNU General Public -+ * License version 2. This program is licensed "as is" without any -+ * warranty of any kind, whether express or implied. -+ */ -+ -+/dts-v1/; -+ -+#include "fsl-ls2088a.dtsi" -+ -+/ { -+ model = "arm64-nxp-ls2088ardb-r1"; -+ compatible = "fsl,ls2088a-rdb", "fsl,ls2088a"; -+}; -+ -+&esdhc { -+ status = "okay"; -+}; -+ -+&ifc { -+ status = "okay"; -+ #address-cells = <2>; -+ #size-cells = <1>; -+ ranges = <0x0 0x0 0x5 0x80000000 0x08000000 -+ 0x2 0x0 0x5 0x30000000 0x00010000 -+ 0x3 0x0 0x5 0x20000000 0x00010000>; -+ -+ nor@0,0 { -+ #address-cells = <1>; -+ #size-cells = <1>; -+ compatible = "cfi-flash"; -+ reg = <0x0 0x0 0x8000000>; -+ bank-width = <2>; -+ device-width = <1>; -+ -+ partition@0 { -+ /* SoC RCW, this location must not be altered */ -+ reg = <0x0 0x100000>; -+ label = "rcw (RO)"; -+ read-only; -+ }; -+ -+ partition@1 { -+ /* U-Boot image */ -+ reg = <0x100000 0x100000>; -+ label = "uboot"; -+ }; -+ -+ partition@2 { -+ /* U-Boot environment varialbes, 1MB */ -+ reg = <0x200000 0x100000>; -+ label = "uboot-env"; -+ env_size = <0x20000>; -+ }; -+ -+ partition@3 { -+ /* MC firmware, 4MB*/ -+ reg = <0x300000 0x400000>; -+ label = "mc_firmware"; -+ }; -+ -+ partition@4 { -+ /* MC DPL Blob, 1MB */ -+ reg = <0x700000 0x100000>; -+ label = "mc_dpl_blob"; -+ }; -+ -+ partition@5 { -+ /* MC DPC Blob, 1MB */ -+ reg = <0x800000 0x100000>; -+ label = "mc_dpc_blob"; -+ }; -+ -+ partition@6 { -+ /* AIOP FW, 4MB */ -+ reg = <0x900000 0x400000>; -+ label = "aiop_fw"; -+ }; -+ -+ partition@7 { -+ /* DebugServerFW, 2MB */ -+ reg = <0xd00000 0x200000>; -+ label = "DebugServer_fw"; -+ }; -+ }; -+ -+ nand@2,0 { -+ #address-cells = <1>; -+ #size-cells = <1>; -+ compatible = "fsl,ifc-nand"; -+ reg = <0x2 0x0 0x10000>; -+ }; -+ -+ cpld@3,0 { -+ reg = <0x3 0x0 0x10000>; -+ compatible = "fsl,ls2088a-rdb-qixis", "fsl,fpga-qixis"; -+ }; -+}; -+ -+&ftm0 { -+ status = "okay"; -+}; -+ -+&i2c0 { -+ status = "okay"; -+ pca9547@75 { -+ compatible = "nxp,pca9547"; -+ reg = <0x75>; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ i2c-mux-never-disable; -+ i2c@1 { -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0x01>; -+ rtc@68 { -+ compatible = "dallas,ds3232"; -+ reg = <0x68>; -+ }; -+ }; -+ -+ i2c@3 { -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0x3>; -+ -+ adt7481@4c { -+ compatible = "adi,adt7461"; -+ reg = <0x4c>; -+ }; -+ }; -+ }; -+}; -+ -+&i2c1 { -+ status = "disabled"; -+}; -+ -+&i2c2 { -+ status = "disabled"; -+}; -+ -+&i2c3 { -+ status = "disabled"; -+}; -+ -+&dspi { -+ status = "okay"; -+ dflash0: n25q512a { -+ #address-cells = <1>; -+ #size-cells = <1>; -+ compatible = "st,m25p80"; -+ spi-max-frequency = <3000000>; -+ reg = <0>; -+ }; -+}; -+ -+&qspi { -+ status = "disabled"; -+}; -+ -+&sata0 { -+ status = "okay"; -+}; -+ -+&sata1 { -+ status = "okay"; -+}; -+ -+&usb0 { -+ status = "okay"; -+}; -+ -+&usb1 { -+ status = "okay"; -+}; -+ -+&emdio1 { -+ /* CS4340 PHYs */ -+ mdio1_phy1: emdio1_phy@1 { -+ reg = <0x10>; -+ phy-connection-type = "xfi"; -+ }; -+ mdio1_phy2: emdio1_phy@2 { -+ reg = <0x11>; -+ phy-connection-type = "xfi"; -+ }; -+ mdio1_phy3: emdio1_phy@3 { -+ reg = <0x12>; -+ phy-connection-type = "xfi"; -+ }; -+ mdio1_phy4: emdio1_phy@4 { -+ reg = <0x13>; -+ phy-connection-type = "xfi"; -+ }; -+}; -+ -+&emdio2 { -+ /* AQR405 PHYs */ -+ mdio2_phy1: emdio2_phy@1 { -+ compatible = "ethernet-phy-ieee802.3-c45"; -+ interrupts = <0 1 0x4>; /* Level high type */ -+ reg = <0x0>; -+ phy-connection-type = "xfi"; -+ }; -+ mdio2_phy2: emdio2_phy@2 { -+ compatible = "ethernet-phy-ieee802.3-c45"; -+ interrupts = <0 2 0x4>; /* Level high type */ -+ reg = <0x1>; -+ phy-connection-type = "xfi"; -+ }; -+ mdio2_phy3: emdio2_phy@3 { -+ compatible = "ethernet-phy-ieee802.3-c45"; -+ interrupts = <0 4 0x4>; /* Level high type */ -+ reg = <0x2>; -+ phy-connection-type = "xfi"; -+ }; -+ mdio2_phy4: emdio2_phy@4 { -+ compatible = "ethernet-phy-ieee802.3-c45"; -+ interrupts = <0 5 0x4>; /* Level high type */ -+ reg = <0x3>; -+ phy-connection-type = "xfi"; -+ }; -+}; -+ -+/* Update DPMAC connections to external PHYs, under the assumption of -+ * SerDes 0x2a_0x41. This is currently the only SerDes supported on the board. -+ */ -+&dpmac1 { -+ phy-handle = <&mdio1_phy1>; -+}; -+&dpmac2 { -+ phy-handle = <&mdio1_phy2>; -+}; -+&dpmac3 { -+ phy-handle = <&mdio1_phy3>; -+}; -+&dpmac4 { -+ phy-handle = <&mdio1_phy4>; -+}; -+&dpmac5 { -+ phy-handle = <&mdio2_phy1>; -+}; -+&dpmac6 { -+ phy-handle = <&mdio2_phy2>; -+}; -+&dpmac7 { -+ phy-handle = <&mdio2_phy3>; -+}; -+&dpmac8 { -+ phy-handle = <&mdio2_phy4>; -+}; -diff --git a/arch/arm64/boot/dts/fsl-ls2088a.dtsi b/arch/arm64/boot/dts/fsl-ls2088a.dtsi -new file mode 100644 -index 0000000..892d426 ---- /dev/null -+++ b/arch/arm64/boot/dts/fsl-ls2088a.dtsi -@@ -0,0 +1,833 @@ -+/* -+ * Device Tree Include file for Freescale Layerscape-2088A family SoC. -+ * -+ * Copyright (C) 2016, Freescale Semiconductor -+ * -+ * Abhimanyu Saini -+ * -+ * This file is dual-licensed: you can use it either under the terms -+ * of the GPLv2 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. -+ * -+ * 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. -+ */ -+ -+#include -+ -+/memreserve/ 0x80000000 0x00010000; -+ -+/ { -+ compatible = "fsl,ls2088a"; -+ interrupt-parent = <&gic>; -+ #address-cells = <2>; -+ #size-cells = <2>; -+ -+ cpus { -+ #address-cells = <2>; -+ #size-cells = <0>; -+ -+ cpu0: cpu@0 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a72"; -+ reg = <0x0 0x0>; -+ clocks = <&clockgen 1 0>; -+ #cooling-cells = <2>; -+ }; -+ -+ cpu1: cpu@1 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a72"; -+ reg = <0x0 0x1>; -+ clocks = <&clockgen 1 0>; -+ }; -+ -+ cpu2: cpu@100 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a72"; -+ reg = <0x0 0x100>; -+ clocks = <&clockgen 1 1>; -+ #cooling-cells = <2>; -+ }; -+ -+ cpu3: cpu@101 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a72"; -+ reg = <0x0 0x101>; -+ clocks = <&clockgen 1 1>; -+ }; -+ -+ cpu4: cpu@200 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a72"; -+ reg = <0x0 0x200>; -+ clocks = <&clockgen 1 2>; -+ #cooling-cells = <2>; -+ }; -+ -+ cpu5: cpu@201 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a72"; -+ reg = <0x0 0x201>; -+ clocks = <&clockgen 1 2>; -+ }; -+ -+ cpu6: cpu@300 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a72"; -+ reg = <0x0 0x300>; -+ clocks = <&clockgen 1 3>; -+ #cooling-cells = <2>; -+ }; -+ -+ cpu7: cpu@301 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a72"; -+ reg = <0x0 0x301>; -+ clocks = <&clockgen 1 3>; -+ }; -+ }; -+ -+ pmu { -+ compatible = "arm,armv8-pmuv3"; -+ interrupts = <1 7 0x8>; /* PMU PPI, Level low type */ -+ }; -+ -+ gic: interrupt-controller@6000000 { -+ compatible = "arm,gic-v3"; -+ reg = <0x0 0x06000000 0 0x10000>, /* GIC Dist */ -+ <0x0 0x06100000 0 0x100000>, /* GICR (RD_base + SGI_base) */ -+ <0x0 0x0c0c0000 0 0x2000>, /* GICC */ -+ <0x0 0x0c0d0000 0 0x1000>, /* GICH */ -+ <0x0 0x0c0e0000 0 0x20000>; /* GICV */ -+ #interrupt-cells = <3>; -+ #address-cells = <2>; -+ #size-cells = <2>; -+ ranges; -+ interrupt-controller; -+ interrupts = <1 9 0x4>; -+ -+ its: gic-its@6020000 { -+ compatible = "arm,gic-v3-its"; -+ msi-controller; -+ reg = <0x0 0x6020000 0 0x20000>; -+ }; -+ }; -+ -+ sysclk: sysclk { -+ compatible = "fixed-clock"; -+ #clock-cells = <0>; -+ clock-frequency = <100000000>; -+ clock-output-names = "sysclk"; -+ }; -+ -+ clockgen: clocking@1300000 { -+ compatible = "fsl,ls2088a-clockgen"; -+ reg = <0 0x1300000 0 0xa0000>; -+ #clock-cells = <2>; -+ clocks = <&sysclk>; -+ }; -+ -+ tmu: tmu@1f80000 { -+ compatible = "fsl,qoriq-tmu", "fsl,ls2080a-tmu", "fsl,ls2088a-tmu"; -+ reg = <0x0 0x1f80000 0x0 0x10000>; -+ interrupts = <0 23 0x4>; -+ fsl,tmu-range = <0xb0000 0x9002a 0x6004c 0x30062>; -+ fsl,tmu-calibration = <0x00000000 0x00000026 -+ 0x00000001 0x0000002d -+ 0x00000002 0x00000032 -+ 0x00000003 0x00000039 -+ 0x00000004 0x0000003f -+ 0x00000005 0x00000046 -+ 0x00000006 0x0000004d -+ 0x00000007 0x00000054 -+ 0x00000008 0x0000005a -+ 0x00000009 0x00000061 -+ 0x0000000a 0x0000006a -+ 0x0000000b 0x00000071 -+ -+ 0x00010000 0x00000025 -+ 0x00010001 0x0000002c -+ 0x00010002 0x00000035 -+ 0x00010003 0x0000003d -+ 0x00010004 0x00000045 -+ 0x00010005 0x0000004e -+ 0x00010006 0x00000057 -+ 0x00010007 0x00000061 -+ 0x00010008 0x0000006b -+ 0x00010009 0x00000076 -+ -+ 0x00020000 0x00000029 -+ 0x00020001 0x00000033 -+ 0x00020002 0x0000003d -+ 0x00020003 0x00000049 -+ 0x00020004 0x00000056 -+ 0x00020005 0x00000061 -+ 0x00020006 0x0000006d -+ -+ 0x00030000 0x00000021 -+ 0x00030001 0x0000002a -+ 0x00030002 0x0000003c -+ 0x00030003 0x0000004e>; -+ little-endian; -+ #thermal-sensor-cells = <1>; -+ }; -+ -+ thermal-zones { -+ cpu_thermal: cpu-thermal { -+ polling-delay-passive = <1000>; -+ polling-delay = <5000>; -+ -+ thermal-sensors = <&tmu 4>; -+ -+ trips { -+ cpu_alert: cpu-alert { -+ temperature = <75000>; -+ hysteresis = <2000>; -+ type = "passive"; -+ }; -+ cpu_crit: cpu-crit { -+ temperature = <85000>; -+ hysteresis = <2000>; -+ type = "critical"; -+ }; -+ }; -+ -+ cooling-maps { -+ map0 { -+ trip = <&cpu_alert>; -+ cooling-device = -+ <&cpu0 THERMAL_NO_LIMIT -+ THERMAL_NO_LIMIT>; -+ }; -+ map1 { -+ trip = <&cpu_alert>; -+ cooling-device = -+ <&cpu2 THERMAL_NO_LIMIT -+ THERMAL_NO_LIMIT>; -+ }; -+ map2 { -+ trip = <&cpu_alert>; -+ cooling-device = -+ <&cpu4 THERMAL_NO_LIMIT -+ THERMAL_NO_LIMIT>; -+ }; -+ map3 { -+ trip = <&cpu_alert>; -+ cooling-device = -+ <&cpu6 THERMAL_NO_LIMIT -+ THERMAL_NO_LIMIT>; -+ }; -+ }; -+ }; -+ }; -+ -+ serial0: serial@21c0500 { -+ device_type = "serial"; -+ compatible = "fsl,ns16550", "ns16550a"; -+ reg = <0x0 0x21c0500 0x0 0x100>; -+ clocks = <&clockgen 4 3>; -+ interrupts = <0 32 0x4>; /* Level high type */ -+ }; -+ -+ serial1: serial@21c0600 { -+ device_type = "serial"; -+ compatible = "fsl,ns16550", "ns16550a"; -+ reg = <0x0 0x21c0600 0x0 0x100>; -+ clocks = <&clockgen 4 3>; -+ interrupts = <0 32 0x4>; /* Level high type */ -+ }; -+ cluster1_core0_watchdog: wdt@c000000 { -+ compatible = "arm,sp805-wdt", "arm,primecell"; -+ reg = <0x0 0xc000000 0x0 0x1000>; -+ clocks = <&clockgen 4 3>, <&clockgen 4 3>; -+ clock-names = "apb_pclk", "wdog_clk"; -+ }; -+ -+ cluster1_core1_watchdog: wdt@c010000 { -+ compatible = "arm,sp805-wdt", "arm,primecell"; -+ reg = <0x0 0xc010000 0x0 0x1000>; -+ clocks = <&clockgen 4 3>, <&clockgen 4 3>; -+ clock-names = "apb_pclk", "wdog_clk"; -+ }; -+ -+ cluster2_core0_watchdog: wdt@c100000 { -+ compatible = "arm,sp805-wdt", "arm,primecell"; -+ reg = <0x0 0xc100000 0x0 0x1000>; -+ clocks = <&clockgen 4 3>, <&clockgen 4 3>; -+ clock-names = "apb_pclk", "wdog_clk"; -+ }; -+ -+ cluster2_core1_watchdog: wdt@c110000 { -+ compatible = "arm,sp805-wdt", "arm,primecell"; -+ reg = <0x0 0xc110000 0x0 0x1000>; -+ clocks = <&clockgen 4 3>, <&clockgen 4 3>; -+ clock-names = "apb_pclk", "wdog_clk"; -+ }; -+ -+ cluster3_core0_watchdog: wdt@c200000 { -+ compatible = "arm,sp805-wdt", "arm,primecell"; -+ reg = <0x0 0xc200000 0x0 0x1000>; -+ clocks = <&clockgen 4 3>, <&clockgen 4 3>; -+ clock-names = "apb_pclk", "wdog_clk"; -+ }; -+ -+ cluster3_core1_watchdog: wdt@c210000 { -+ compatible = "arm,sp805-wdt", "arm,primecell"; -+ reg = <0x0 0xc210000 0x0 0x1000>; -+ clocks = <&clockgen 4 3>, <&clockgen 4 3>; -+ clock-names = "apb_pclk", "wdog_clk"; -+ }; -+ -+ cluster4_core0_watchdog: wdt@c300000 { -+ compatible = "arm,sp805-wdt", "arm,primecell"; -+ reg = <0x0 0xc300000 0x0 0x1000>; -+ clocks = <&clockgen 4 3>, <&clockgen 4 3>; -+ clock-names = "apb_pclk", "wdog_clk"; -+ }; -+ -+ cluster4_core1_watchdog: wdt@c310000 { -+ compatible = "arm,sp805-wdt", "arm,primecell"; -+ reg = <0x0 0xc310000 0x0 0x1000>; -+ clocks = <&clockgen 4 3>, <&clockgen 4 3>; -+ clock-names = "apb_pclk", "wdog_clk"; -+ }; -+ -+ gpio0: gpio@2300000 { -+ compatible = "fsl,qoriq-gpio"; -+ reg = <0x0 0x2300000 0x0 0x10000>; -+ interrupts = <0 36 0x4>; /* Level high type */ -+ gpio-controller; -+ little-endian; -+ #gpio-cells = <2>; -+ interrupt-controller; -+ #interrupt-cells = <2>; -+ }; -+ -+ gpio1: gpio@2310000 { -+ compatible = "fsl,qoriq-gpio"; -+ reg = <0x0 0x2310000 0x0 0x10000>; -+ interrupts = <0 36 0x4>; /* Level high type */ -+ gpio-controller; -+ little-endian; -+ #gpio-cells = <2>; -+ interrupt-controller; -+ #interrupt-cells = <2>; -+ }; -+ -+ gpio2: gpio@2320000 { -+ compatible = "fsl,qoriq-gpio"; -+ reg = <0x0 0x2320000 0x0 0x10000>; -+ interrupts = <0 37 0x4>; /* Level high type */ -+ gpio-controller; -+ little-endian; -+ #gpio-cells = <2>; -+ interrupt-controller; -+ #interrupt-cells = <2>; -+ }; -+ -+ gpio3: gpio@2330000 { -+ compatible = "fsl,qoriq-gpio"; -+ reg = <0x0 0x2330000 0x0 0x10000>; -+ interrupts = <0 37 0x4>; /* Level high type */ -+ gpio-controller; -+ little-endian; -+ #gpio-cells = <2>; -+ interrupt-controller; -+ #interrupt-cells = <2>; -+ }; -+ -+ /* TODO: WRIOP (CCSR?) */ -+ emdio1: mdio@0x8B96000 { /* WRIOP0: 0x8B8_0000, E-MDIO1: 0x1_6000 */ -+ compatible = "fsl,fman-memac-mdio"; -+ reg = <0x0 0x8B96000 0x0 0x1000>; -+ device_type = "mdio"; /* TODO: is this necessary? */ -+ little-endian; /* force the driver in LE mode */ -+ -+ /* Not necessary on the QDS, but needed on the RDB */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ }; -+ -+ emdio2: mdio@0x8B97000 { /* WRIOP0: 0x8B8_0000, E-MDIO2: 0x1_7000 */ -+ compatible = "fsl,fman-memac-mdio"; -+ reg = <0x0 0x8B97000 0x0 0x1000>; -+ device_type = "mdio"; /* TODO: is this necessary? */ -+ little-endian; /* force the driver in LE mode */ -+ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ }; -+ -+ ifc: ifc@2240000 { -+ compatible = "fsl,ifc", "simple-bus"; -+ reg = <0x0 0x2240000 0x0 0x20000>; -+ interrupts = <0 21 0x4>; /* Level high type */ -+ little-endian; -+ #address-cells = <2>; -+ #size-cells = <1>; -+ -+ ranges = <0 0 0x5 0x80000000 0x08000000 -+ 2 0 0x5 0x30000000 0x00010000 -+ 3 0 0x5 0x20000000 0x00010000>; -+ }; -+ -+ esdhc: esdhc@2140000 { -+ compatible = "fsl,ls2088a-esdhc", "fsl,ls2080a-esdhc", -+ "fsl,esdhc"; -+ reg = <0x0 0x2140000 0x0 0x10000>; -+ interrupts = <0 28 0x4>; /* Level high type */ -+ clock-frequency = <0>; -+ voltage-ranges = <1800 1800 3300 3300>; -+ sdhci,auto-cmd12; -+ little-endian; -+ bus-width = <4>; -+ }; -+ -+ ftm0: ftm0@2800000 { -+ compatible = "fsl,ftm-alarm"; -+ reg = <0x0 0x2800000 0x0 0x10000>; -+ interrupts = <0 44 4>; -+ }; -+ -+ reset: reset@1E60000 { -+ compatible = "fsl,ls-reset"; -+ reg = <0x0 0x1E60000 0x0 0x10000>; -+ }; -+ -+ dspi: dspi@2100000 { -+ compatible = "fsl,ls2088a-dspi", "fsl,ls2085a-dspi", -+ "fsl,ls2080a-dspi"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0x0 0x2100000 0x0 0x10000>; -+ interrupts = <0 26 0x4>; /* Level high type */ -+ clocks = <&clockgen 4 3>; -+ clock-names = "dspi"; -+ spi-num-chipselects = <5>; -+ bus-num = <0>; -+ }; -+ -+ i2c0: i2c@2000000 { -+ compatible = "fsl,vf610-i2c"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0x0 0x2000000 0x0 0x10000>; -+ interrupts = <0 34 0x4>; /* Level high type */ -+ clock-names = "i2c"; -+ clocks = <&clockgen 4 3>; -+ }; -+ -+ i2c1: i2c@2010000 { -+ compatible = "fsl,vf610-i2c"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0x0 0x2010000 0x0 0x10000>; -+ interrupts = <0 34 0x4>; /* Level high type */ -+ clock-names = "i2c"; -+ clocks = <&clockgen 4 3>; -+ }; -+ -+ i2c2: i2c@2020000 { -+ compatible = "fsl,vf610-i2c"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0x0 0x2020000 0x0 0x10000>; -+ interrupts = <0 35 0x4>; /* Level high type */ -+ clock-names = "i2c"; -+ clocks = <&clockgen 4 3>; -+ }; -+ -+ i2c3: i2c@2030000 { -+ compatible = "fsl,vf610-i2c"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0x0 0x2030000 0x0 0x10000>; -+ interrupts = <0 35 0x4>; /* Level high type */ -+ clock-names = "i2c"; -+ clocks = <&clockgen 4 3>; -+ }; -+ -+ qspi: quadspi@20c0000 { -+ compatible = "fsl,ls2088a-qspi", "fsl,ls2080a-qspi"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0x0 0x20c0000 0x0 0x10000>, -+ <0x0 0x20000000 0x0 0x10000000>; -+ reg-names = "QuadSPI", "QuadSPI-memory"; -+ interrupts = <0 25 0x4>; /* Level high type */ -+ clocks = <&clockgen 4 3>, <&clockgen 4 3>; -+ clock-names = "qspi_en", "qspi"; -+ }; -+ -+ pcie1: pcie@3400000 { -+ compatible = "fsl,ls2088a-pcie", "fsl,ls2080a-pcie", -+ "fsl,ls2085a-pcie", "snps,dw-pcie"; -+ reg = <0x00 0x03400000 0x0 0x00100000 /* controller registers */ -+ 0x20 0x00000000 0x0 0x00001000>; /* configuration space */ -+ reg-names = "regs", "config"; -+ interrupts = <0 108 0x4>; /* Level high type */ -+ interrupt-names = "aer"; -+ #address-cells = <3>; -+ #size-cells = <2>; -+ device_type = "pci"; -+ dma-coherent; -+ fsl,lut_diff; -+ num-lanes = <4>; -+ bus-range = <0x0 0xff>; -+ ranges = <0x81000000 0x0 0x00000000 0x20 0x00010000 0x0 0x00010000 /* downstream I/O */ -+ 0x82000000 0x0 0x40000000 0x20 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */ -+ msi-parent = <&its>; -+ #interrupt-cells = <1>; -+ interrupt-map-mask = <0 0 0 7>; -+ interrupt-map = <0000 0 0 1 &gic 0 0 0 109 4>, -+ <0000 0 0 2 &gic 0 0 0 110 4>, -+ <0000 0 0 3 &gic 0 0 0 111 4>, -+ <0000 0 0 4 &gic 0 0 0 112 4>; -+ }; -+ -+ pcie2: pcie@3500000 { -+ compatible = "fsl,ls2080a-pcie", "fsl,ls2080a-pcie", -+ "fsl,ls2085a-pcie", "snps,dw-pcie"; -+ reg = <0x00 0x03500000 0x0 0x00100000 /* controller registers */ -+ 0x28 0x00000000 0x0 0x00001000>; /* configuration space */ -+ reg-names = "regs", "config"; -+ interrupts = <0 113 0x4>; /* Level high type */ -+ interrupt-names = "aer"; -+ #address-cells = <3>; -+ #size-cells = <2>; -+ device_type = "pci"; -+ dma-coherent; -+ fsl,lut_diff; -+ num-lanes = <4>; -+ bus-range = <0x0 0xff>; -+ ranges = <0x81000000 0x0 0x00000000 0x28 0x00010000 0x0 0x00010000 /* downstream I/O */ -+ 0x82000000 0x0 0x40000000 0x28 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */ -+ msi-parent = <&its>; -+ #interrupt-cells = <1>; -+ interrupt-map-mask = <0 0 0 7>; -+ interrupt-map = <0000 0 0 1 &gic 0 0 0 114 4>, -+ <0000 0 0 2 &gic 0 0 0 115 4>, -+ <0000 0 0 3 &gic 0 0 0 116 4>, -+ <0000 0 0 4 &gic 0 0 0 117 4>; -+ }; -+ -+ pcie3: pcie@3600000 { -+ compatible = "fsl,ls2088a-pcie", "fsl,ls2080a-pcie", -+ "fsl,ls2085a-pcie", "snps,dw-pcie"; -+ reg = <0x00 0x03600000 0x0 0x00100000 /* controller registers */ -+ 0x30 0x00000000 0x0 0x00001000>; /* configuration space */ -+ reg-names = "regs", "config"; -+ interrupts = <0 118 0x4>; /* Level high type */ -+ interrupt-names = "aer"; -+ #address-cells = <3>; -+ #size-cells = <2>; -+ device_type = "pci"; -+ dma-coherent; -+ fsl,lut_diff; -+ num-lanes = <8>; -+ bus-range = <0x0 0xff>; -+ ranges = <0x81000000 0x0 0x00000000 0x30 0x00010000 0x0 0x00010000 /* downstream I/O */ -+ 0x82000000 0x0 0x40000000 0x30 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */ -+ msi-parent = <&its>; -+ #interrupt-cells = <1>; -+ interrupt-map-mask = <0 0 0 7>; -+ interrupt-map = <0000 0 0 1 &gic 0 0 0 119 4>, -+ <0000 0 0 2 &gic 0 0 0 120 4>, -+ <0000 0 0 3 &gic 0 0 0 121 4>, -+ <0000 0 0 4 &gic 0 0 0 122 4>; -+ }; -+ -+ pcie4: pcie@3700000 { -+ compatible = "fsl,ls2080a-pcie", "fsl,ls2080a-pcie", -+ "fsl,ls2085a-pcie", "snps,dw-pcie"; -+ reg = <0x00 0x03700000 0x0 0x00100000 /* controller registers */ -+ 0x38 0x00000000 0x0 0x00001000>; /* configuration space */ -+ reg-names = "regs", "config"; -+ interrupts = <0 123 0x4>; /* Level high type */ -+ interrupt-names = "aer"; -+ #address-cells = <3>; -+ #size-cells = <2>; -+ device_type = "pci"; -+ dma-coherent; -+ fsl,lut_diff; -+ num-lanes = <4>; -+ bus-range = <0x0 0xff>; -+ ranges = <0x81000000 0x0 0x00000000 0x38 0x00010000 0x0 0x00010000 /* downstream I/O */ -+ 0x82000000 0x0 0x40000000 0x38 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */ -+ msi-parent = <&its>; -+ #interrupt-cells = <1>; -+ interrupt-map-mask = <0 0 0 7>; -+ interrupt-map = <0000 0 0 1 &gic 0 0 0 124 4>, -+ <0000 0 0 2 &gic 0 0 0 125 4>, -+ <0000 0 0 3 &gic 0 0 0 126 4>, -+ <0000 0 0 4 &gic 0 0 0 127 4>; -+ }; -+ -+ sata0: sata@3200000 { -+ status = "disabled"; -+ compatible = "fsl,ls2088a-ahci", "fsl,ls2080a-ahci"; -+ reg = <0x0 0x3200000 0x0 0x10000>; -+ interrupts = <0 133 0x4>; /* Level high type */ -+ clocks = <&clockgen 4 3>; -+ }; -+ -+ sata1: sata@3210000 { -+ status = "disabled"; -+ compatible = "fsl,ls2088a-ahci", "fsl,ls2080a-ahci"; -+ reg = <0x0 0x3210000 0x0 0x10000>; -+ interrupts = <0 136 0x4>; /* Level high type */ -+ clocks = <&clockgen 4 3>; -+ }; -+ -+ usb0: usb3@3100000 { -+ status = "disabled"; -+ compatible = "snps,dwc3"; -+ reg = <0x0 0x3100000 0x0 0x10000>; -+ interrupts = <0 80 0x4>; /* Level high type */ -+ dr_mode = "host"; -+ configure-gfladj; -+ snps,dis_rxdet_inp3_quirk; -+ }; -+ -+ usb1: usb3@3110000 { -+ status = "disabled"; -+ compatible = "snps,dwc3"; -+ reg = <0x0 0x3110000 0x0 0x10000>; -+ interrupts = <0 81 0x4>; /* Level high type */ -+ dr_mode = "host"; -+ configure-gfladj; -+ snps,dis_rxdet_inp3_quirk; -+ }; -+ -+ smmu: iommu@5000000 { -+ compatible = "arm,mmu-500"; -+ reg = <0 0x5000000 0 0x800000>; -+ #global-interrupts = <12>; -+ interrupts = <0 13 4>, /* global secure fault */ -+ <0 14 4>, /* combined secure interrupt */ -+ <0 15 4>, /* global non-secure fault */ -+ <0 16 4>, /* combined non-secure interrupt */ -+ /* performance counter interrupts 0-7 */ -+ <0 211 4>, -+ <0 212 4>, -+ <0 213 4>, -+ <0 214 4>, -+ <0 215 4>, -+ <0 216 4>, -+ <0 217 4>, -+ <0 218 4>, -+ /* per context interrupt, 64 interrupts */ -+ <0 146 4>, -+ <0 147 4>, -+ <0 148 4>, -+ <0 149 4>, -+ <0 150 4>, -+ <0 151 4>, -+ <0 152 4>, -+ <0 153 4>, -+ <0 154 4>, -+ <0 155 4>, -+ <0 156 4>, -+ <0 157 4>, -+ <0 158 4>, -+ <0 159 4>, -+ <0 160 4>, -+ <0 161 4>, -+ <0 162 4>, -+ <0 163 4>, -+ <0 164 4>, -+ <0 165 4>, -+ <0 166 4>, -+ <0 167 4>, -+ <0 168 4>, -+ <0 169 4>, -+ <0 170 4>, -+ <0 171 4>, -+ <0 172 4>, -+ <0 173 4>, -+ <0 174 4>, -+ <0 175 4>, -+ <0 176 4>, -+ <0 177 4>, -+ <0 178 4>, -+ <0 179 4>, -+ <0 180 4>, -+ <0 181 4>, -+ <0 182 4>, -+ <0 183 4>, -+ <0 184 4>, -+ <0 185 4>, -+ <0 186 4>, -+ <0 187 4>, -+ <0 188 4>, -+ <0 189 4>, -+ <0 190 4>, -+ <0 191 4>, -+ <0 192 4>, -+ <0 193 4>, -+ <0 194 4>, -+ <0 195 4>, -+ <0 196 4>, -+ <0 197 4>, -+ <0 198 4>, -+ <0 199 4>, -+ <0 200 4>, -+ <0 201 4>, -+ <0 202 4>, -+ <0 203 4>, -+ <0 204 4>, -+ <0 205 4>, -+ <0 206 4>, -+ <0 207 4>, -+ <0 208 4>, -+ <0 209 4>; -+ mmu-masters = <&fsl_mc 0x300 0>; -+ }; -+ -+ timer { -+ compatible = "arm,armv8-timer"; -+ interrupts = <1 13 0x1>, /* Physical Secure PPI, edge triggered */ -+ <1 14 0x1>, /* Physical Non-Secure PPI, edge triggered */ -+ <1 11 0x1>, /* Virtual PPI, edge triggered */ -+ <1 10 0x1>; /* Hypervisor PPI, edge triggered */ -+ arm,reread-timer; -+ fsl,erratum-a008585; -+ }; -+ -+ fsl_mc: fsl-mc@80c000000 { -+ compatible = "fsl,qoriq-mc"; -+ #stream-id-cells = <2>; -+ reg = <0x00000008 0x0c000000 0 0x40>, /* MC portal base */ -+ <0x00000000 0x08340000 0 0x40000>; /* MC control reg */ -+ msi-parent = <&its>; -+ #address-cells = <3>; -+ #size-cells = <1>; -+ -+ /* -+ * Region type 0x0 - MC portals -+ * Region type 0x1 - QBMAN portals -+ */ -+ ranges = <0x0 0x0 0x0 0x8 0x0c000000 0x4000000 -+ 0x1 0x0 0x0 0x8 0x18000000 0x8000000>; -+ -+ /* -+ * Define the maximum number of MACs present on the SoC. -+ * They won't necessarily be all probed, since the -+ * Data Path Layout file and the MC firmware can put fewer -+ * actual DPMAC objects on the MC bus. -+ */ -+ dpmacs { -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ dpmac1: dpmac@1 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <1>; -+ }; -+ dpmac2: dpmac@2 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <2>; -+ }; -+ dpmac3: dpmac@3 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <3>; -+ }; -+ dpmac4: dpmac@4 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <4>; -+ }; -+ dpmac5: dpmac@5 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <5>; -+ }; -+ dpmac6: dpmac@6 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <6>; -+ }; -+ dpmac7: dpmac@7 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <7>; -+ }; -+ dpmac8: dpmac@8 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <8>; -+ }; -+ dpmac9: dpmac@9 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <9>; -+ }; -+ dpmac10: dpmac@10 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <0xa>; -+ }; -+ dpmac11: dpmac@11 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <0xb>; -+ }; -+ dpmac12: dpmac@12 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <0xc>; -+ }; -+ dpmac13: dpmac@13 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <0xd>; -+ }; -+ dpmac14: dpmac@14 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <0xe>; -+ }; -+ dpmac15: dpmac@15 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <0xf>; -+ }; -+ dpmac16: dpmac@16 { -+ compatible = "fsl,qoriq-mc-dpmac"; -+ reg = <0x10>; -+ }; -+ }; -+ }; -+ -+ ccn@4000000 { -+ compatible = "arm,ccn-504"; -+ reg = <0x0 0x04000000 0x0 0x01000000>; -+ interrupts = <0 12 4>; -+ }; -+ -+ memory@80000000 { -+ device_type = "memory"; -+ reg = <0x00000000 0x80000000 0 0x80000000>; -+ /* DRAM space 1 - 2 GB DRAM */ -+ }; -+}; --- -2.1.0.27.g96db324 - diff --git a/packages/base/any/kernels/3.18.25/patches/aufs.patch b/packages/base/any/kernels/3.18.25/patches/aufs.patch deleted file mode 100644 index b0dd85d1..00000000 --- a/packages/base/any/kernels/3.18.25/patches/aufs.patch +++ /dev/null @@ -1,33877 +0,0 @@ -From 9da2de12351d9d38412a0074d49efaf3c5bf6f3f Mon Sep 17 00:00:00 2001 -From: Steven Noble -Date: Thu, 12 May 2016 21:42:28 +0000 -Subject: [PATCH] AUFS patches applied - ---- - MAINTAINERS | 14 + - drivers/block/loop.c | 18 + - fs/Kconfig | 1 + - fs/Makefile | 1 + - fs/aufs/Kconfig | 185 ++++ - fs/aufs/Makefile | 44 + - fs/aufs/aufs.h | 59 ++ - fs/aufs/branch.c | 1402 ++++++++++++++++++++++++++++++ - fs/aufs/branch.h | 279 ++++++ - fs/aufs/conf.mk | 38 + - fs/aufs/cpup.c | 1368 +++++++++++++++++++++++++++++ - fs/aufs/cpup.h | 94 ++ - fs/aufs/dbgaufs.c | 432 +++++++++ - fs/aufs/dbgaufs.h | 48 + - fs/aufs/dcsub.c | 224 +++++ - fs/aufs/dcsub.h | 123 +++ - fs/aufs/debug.c | 436 ++++++++++ - fs/aufs/debug.h | 228 +++++ - fs/aufs/dentry.c | 1129 ++++++++++++++++++++++++ - fs/aufs/dentry.h | 234 +++++ - fs/aufs/dinfo.c | 544 ++++++++++++ - fs/aufs/dir.c | 756 ++++++++++++++++ - fs/aufs/dir.h | 131 +++ - fs/aufs/dynop.c | 379 ++++++++ - fs/aufs/dynop.h | 76 ++ - fs/aufs/export.c | 831 ++++++++++++++++++ - fs/aufs/f_op.c | 781 +++++++++++++++++ - fs/aufs/fhsm.c | 426 +++++++++ - fs/aufs/file.c | 857 ++++++++++++++++++ - fs/aufs/file.h | 291 +++++++ - fs/aufs/finfo.c | 156 ++++ - fs/aufs/fstype.h | 400 +++++++++ - fs/aufs/hfsnotify.c | 288 ++++++ - fs/aufs/hfsplus.c | 56 ++ - fs/aufs/hnotify.c | 714 +++++++++++++++ - fs/aufs/i_op.c | 1460 +++++++++++++++++++++++++++++++ - fs/aufs/i_op_add.c | 930 ++++++++++++++++++++ - fs/aufs/i_op_del.c | 506 +++++++++++ - fs/aufs/i_op_ren.c | 1013 ++++++++++++++++++++++ - fs/aufs/iinfo.c | 277 ++++++ - fs/aufs/inode.c | 522 +++++++++++ - fs/aufs/inode.h | 686 +++++++++++++++ - fs/aufs/ioctl.c | 219 +++++ - fs/aufs/loop.c | 146 ++++ - fs/aufs/loop.h | 52 ++ - fs/aufs/magic.mk | 30 + - fs/aufs/module.c | 222 +++++ - fs/aufs/module.h | 105 +++ - fs/aufs/mvdown.c | 703 +++++++++++++++ - fs/aufs/opts.c | 1878 ++++++++++++++++++++++++++++++++++++++++ - fs/aufs/opts.h | 212 +++++ - fs/aufs/plink.c | 506 +++++++++++ - fs/aufs/poll.c | 52 ++ - fs/aufs/posix_acl.c | 98 +++ - fs/aufs/procfs.c | 169 ++++ - fs/aufs/rdu.c | 388 +++++++++ - fs/aufs/rwsem.h | 191 ++++ - fs/aufs/sbinfo.c | 348 ++++++++ - fs/aufs/spl.h | 111 +++ - fs/aufs/super.c | 1041 ++++++++++++++++++++++ - fs/aufs/super.h | 626 ++++++++++++++ - fs/aufs/sysaufs.c | 104 +++ - fs/aufs/sysaufs.h | 101 +++ - fs/aufs/sysfs.c | 376 ++++++++ - fs/aufs/sysrq.c | 157 ++++ - fs/aufs/vdir.c | 888 +++++++++++++++++++ - fs/aufs/vfsub.c | 864 ++++++++++++++++++ - fs/aufs/vfsub.h | 315 +++++++ - fs/aufs/wbr_policy.c | 765 ++++++++++++++++ - fs/aufs/whout.c | 1061 +++++++++++++++++++++++ - fs/aufs/whout.h | 85 ++ - fs/aufs/wkq.c | 213 +++++ - fs/aufs/wkq.h | 91 ++ - fs/aufs/xattr.c | 344 ++++++++ - fs/aufs/xino.c | 1343 ++++++++++++++++++++++++++++ - fs/buffer.c | 2 +- - fs/dcache.c | 2 +- - fs/fcntl.c | 4 +- - fs/inode.c | 2 +- - fs/proc/base.c | 2 +- - fs/proc/nommu.c | 5 +- - fs/proc/task_mmu.c | 7 +- - fs/proc/task_nommu.c | 5 +- - fs/splice.c | 10 +- - include/linux/file.h | 1 + - include/linux/fs.h | 3 + - include/linux/mm.h | 22 + - include/linux/mm_types.h | 2 + - include/linux/splice.h | 6 + - include/uapi/linux/Kbuild | 1 + - include/uapi/linux/aufs_type.h | 419 +++++++++ - kernel/fork.c | 2 +- - mm/Makefile | 2 +- - mm/filemap.c | 2 +- - mm/fremap.c | 16 +- - mm/memory.c | 2 +- - mm/mmap.c | 12 +- - mm/nommu.c | 10 +- - mm/prfile.c | 86 ++ - 99 files changed, 32835 insertions(+), 31 deletions(-) - create mode 100644 fs/aufs/Kconfig - create mode 100644 fs/aufs/Makefile - create mode 100644 fs/aufs/aufs.h - create mode 100644 fs/aufs/branch.c - create mode 100644 fs/aufs/branch.h - create mode 100644 fs/aufs/conf.mk - create mode 100644 fs/aufs/cpup.c - create mode 100644 fs/aufs/cpup.h - create mode 100644 fs/aufs/dbgaufs.c - create mode 100644 fs/aufs/dbgaufs.h - create mode 100644 fs/aufs/dcsub.c - create mode 100644 fs/aufs/dcsub.h - create mode 100644 fs/aufs/debug.c - create mode 100644 fs/aufs/debug.h - create mode 100644 fs/aufs/dentry.c - create mode 100644 fs/aufs/dentry.h - create mode 100644 fs/aufs/dinfo.c - create mode 100644 fs/aufs/dir.c - create mode 100644 fs/aufs/dir.h - create mode 100644 fs/aufs/dynop.c - create mode 100644 fs/aufs/dynop.h - create mode 100644 fs/aufs/export.c - create mode 100644 fs/aufs/f_op.c - create mode 100644 fs/aufs/fhsm.c - create mode 100644 fs/aufs/file.c - create mode 100644 fs/aufs/file.h - create mode 100644 fs/aufs/finfo.c - create mode 100644 fs/aufs/fstype.h - create mode 100644 fs/aufs/hfsnotify.c - create mode 100644 fs/aufs/hfsplus.c - create mode 100644 fs/aufs/hnotify.c - create mode 100644 fs/aufs/i_op.c - create mode 100644 fs/aufs/i_op_add.c - create mode 100644 fs/aufs/i_op_del.c - create mode 100644 fs/aufs/i_op_ren.c - create mode 100644 fs/aufs/iinfo.c - create mode 100644 fs/aufs/inode.c - create mode 100644 fs/aufs/inode.h - create mode 100644 fs/aufs/ioctl.c - create mode 100644 fs/aufs/loop.c - create mode 100644 fs/aufs/loop.h - create mode 100644 fs/aufs/magic.mk - create mode 100644 fs/aufs/module.c - create mode 100644 fs/aufs/module.h - create mode 100644 fs/aufs/mvdown.c - create mode 100644 fs/aufs/opts.c - create mode 100644 fs/aufs/opts.h - create mode 100644 fs/aufs/plink.c - create mode 100644 fs/aufs/poll.c - create mode 100644 fs/aufs/posix_acl.c - create mode 100644 fs/aufs/procfs.c - create mode 100644 fs/aufs/rdu.c - create mode 100644 fs/aufs/rwsem.h - create mode 100644 fs/aufs/sbinfo.c - create mode 100644 fs/aufs/spl.h - create mode 100644 fs/aufs/super.c - create mode 100644 fs/aufs/super.h - create mode 100644 fs/aufs/sysaufs.c - create mode 100644 fs/aufs/sysaufs.h - create mode 100644 fs/aufs/sysfs.c - create mode 100644 fs/aufs/sysrq.c - create mode 100644 fs/aufs/vdir.c - create mode 100644 fs/aufs/vfsub.c - create mode 100644 fs/aufs/vfsub.h - create mode 100644 fs/aufs/wbr_policy.c - create mode 100644 fs/aufs/whout.c - create mode 100644 fs/aufs/whout.h - create mode 100644 fs/aufs/wkq.c - create mode 100644 fs/aufs/wkq.h - create mode 100644 fs/aufs/xattr.c - create mode 100644 fs/aufs/xino.c - create mode 100644 include/uapi/linux/aufs_type.h - create mode 100644 mm/prfile.c - -diff --git a/MAINTAINERS b/MAINTAINERS -index c721042..83801d0 100644 ---- a/MAINTAINERS -+++ b/MAINTAINERS -@@ -1795,6 +1795,20 @@ F: include/linux/audit.h - F: include/uapi/linux/audit.h - F: kernel/audit* - -+AUFS (advanced multi layered unification filesystem) FILESYSTEM -+M: "J. R. Okajima" -+L: linux-unionfs@vger.kernel.org -+L: aufs-users@lists.sourceforge.net (members only) -+W: http://aufs.sourceforge.net -+T: git://git.code.sf.net/p/aufs/aufs3-linux -+T: git://github.com/sfjro/aufs3-linux.git -+S: Supported -+F: Documentation/filesystems/aufs/ -+F: Documentation/ABI/testing/debugfs-aufs -+F: Documentation/ABI/testing/sysfs-aufs -+F: fs/aufs/ -+F: include/uapi/linux/aufs_type.h -+ - AUXILIARY DISPLAY DRIVERS - M: Miguel Ojeda Sandonis - W: http://miguelojeda.es/auxdisplay.htm -diff --git a/drivers/block/loop.c b/drivers/block/loop.c -index 6cb1beb..12678be 100644 ---- a/drivers/block/loop.c -+++ b/drivers/block/loop.c -@@ -692,6 +692,24 @@ static inline int is_loop_device(struct file *file) - return i && S_ISBLK(i->i_mode) && MAJOR(i->i_rdev) == LOOP_MAJOR; - } - -+/* -+ * for AUFS -+ * no get/put for file. -+ */ -+struct file *loop_backing_file(struct super_block *sb) -+{ -+ struct file *ret; -+ struct loop_device *l; -+ -+ ret = NULL; -+ if (MAJOR(sb->s_dev) == LOOP_MAJOR) { -+ l = sb->s_bdev->bd_disk->private_data; -+ ret = l->lo_backing_file; -+ } -+ return ret; -+} -+EXPORT_SYMBOL_GPL(loop_backing_file); -+ - /* loop sysfs attributes */ - - static ssize_t loop_attr_show(struct device *dev, char *page, -diff --git a/fs/Kconfig b/fs/Kconfig -index 664991a..1481093 100644 ---- a/fs/Kconfig -+++ b/fs/Kconfig -@@ -210,6 +210,7 @@ source "fs/ufs/Kconfig" - source "fs/exofs/Kconfig" - source "fs/f2fs/Kconfig" - source "fs/efivarfs/Kconfig" -+source "fs/aufs/Kconfig" - - endif # MISC_FILESYSTEMS - -diff --git a/fs/Makefile b/fs/Makefile -index da0bbb4..c8bc724 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_AUFS_FS) += aufs/ -diff --git a/fs/aufs/Kconfig b/fs/aufs/Kconfig -new file mode 100644 -index 0000000..63560ce ---- /dev/null -+++ b/fs/aufs/Kconfig -@@ -0,0 +1,185 @@ -+config AUFS_FS -+ tristate "Aufs (Advanced multi layered unification filesystem) support" -+ help -+ Aufs is a stackable unification filesystem such as Unionfs, -+ which unifies several directories and provides a merged single -+ directory. -+ In the early days, aufs was entirely re-designed and -+ re-implemented Unionfs Version 1.x series. Introducing many -+ original ideas, approaches and improvements, it becomes totally -+ different from Unionfs while keeping the basic features. -+ -+if AUFS_FS -+choice -+ prompt "Maximum number of branches" -+ default AUFS_BRANCH_MAX_127 -+ help -+ Specifies the maximum number of branches (or member directories) -+ in a single aufs. The larger value consumes more system -+ resources and has a minor impact to performance. -+config AUFS_BRANCH_MAX_127 -+ bool "127" -+ help -+ Specifies the maximum number of branches (or member directories) -+ in a single aufs. The larger value consumes more system -+ resources and has a minor impact to performance. -+config AUFS_BRANCH_MAX_511 -+ bool "511" -+ help -+ Specifies the maximum number of branches (or member directories) -+ in a single aufs. The larger value consumes more system -+ resources and has a minor impact to performance. -+config AUFS_BRANCH_MAX_1023 -+ bool "1023" -+ help -+ Specifies the maximum number of branches (or member directories) -+ in a single aufs. The larger value consumes more system -+ resources and has a minor impact to performance. -+config AUFS_BRANCH_MAX_32767 -+ bool "32767" -+ help -+ Specifies the maximum number of branches (or member directories) -+ in a single aufs. The larger value consumes more system -+ resources and has a minor impact to performance. -+endchoice -+ -+config AUFS_SBILIST -+ bool -+ depends on AUFS_MAGIC_SYSRQ || PROC_FS -+ default y -+ help -+ Automatic configuration for internal use. -+ When aufs supports Magic SysRq or /proc, enabled automatically. -+ -+config AUFS_HNOTIFY -+ bool "Detect direct branch access (bypassing aufs)" -+ help -+ If you want to modify files on branches directly, eg. bypassing aufs, -+ and want aufs to detect the changes of them fully, then enable this -+ option and use 'udba=notify' mount option. -+ Currently there is only one available configuration, "fsnotify". -+ It will have a negative impact to the performance. -+ See detail in aufs.5. -+ -+choice -+ prompt "method" if AUFS_HNOTIFY -+ default AUFS_HFSNOTIFY -+config AUFS_HFSNOTIFY -+ bool "fsnotify" -+ select FSNOTIFY -+endchoice -+ -+config AUFS_EXPORT -+ bool "NFS-exportable aufs" -+ depends on EXPORTFS -+ help -+ If you want to export your mounted aufs via NFS, then enable this -+ option. There are several requirements for this configuration. -+ See detail in aufs.5. -+ -+config AUFS_INO_T_64 -+ bool -+ depends on AUFS_EXPORT -+ depends on 64BIT && !(ALPHA || S390) -+ default y -+ help -+ Automatic configuration for internal use. -+ /* typedef unsigned long/int __kernel_ino_t */ -+ /* alpha and s390x are int */ -+ -+config AUFS_XATTR -+ bool "support for XATTR/EA (including Security Labels)" -+ help -+ If your branch fs supports XATTR/EA and you want to make them -+ available in aufs too, then enable this opsion and specify the -+ branch attributes for EA. -+ See detail in aufs.5. -+ -+config AUFS_FHSM -+ bool "File-based Hierarchical Storage Management" -+ help -+ Hierarchical Storage Management (or HSM) is a well-known feature -+ in the storage world. Aufs provides this feature as file-based. -+ with multiple branches. -+ These multiple branches are prioritized, ie. the topmost one -+ should be the fastest drive and be used heavily. -+ -+config AUFS_RDU -+ bool "Readdir in userspace" -+ help -+ Aufs has two methods to provide a merged view for a directory, -+ by a user-space library and by kernel-space natively. The latter -+ is always enabled but sometimes large and slow. -+ If you enable this option, install the library in aufs2-util -+ package, and set some environment variables for your readdir(3), -+ then the work will be handled in user-space which generally -+ shows better performance in most cases. -+ See detail in aufs.5. -+ -+config AUFS_SHWH -+ bool "Show whiteouts" -+ help -+ If you want to make the whiteouts in aufs visible, then enable -+ this option and specify 'shwh' mount option. Although it may -+ sounds like philosophy or something, but in technically it -+ simply shows the name of whiteout with keeping its behaviour. -+ -+config AUFS_BR_RAMFS -+ bool "Ramfs (initramfs/rootfs) as an aufs branch" -+ help -+ If you want to use ramfs as an aufs branch fs, then enable this -+ option. Generally tmpfs is recommended. -+ Aufs prohibited them to be a branch fs by default, because -+ initramfs becomes unusable after switch_root or something -+ generally. If you sets initramfs as an aufs branch and boot your -+ system by switch_root, you will meet a problem easily since the -+ files in initramfs may be inaccessible. -+ Unless you are going to use ramfs as an aufs branch fs without -+ switch_root or something, leave it N. -+ -+config AUFS_BR_FUSE -+ bool "Fuse fs as an aufs branch" -+ depends on FUSE_FS -+ select AUFS_POLL -+ help -+ If you want to use fuse-based userspace filesystem as an aufs -+ branch fs, then enable this option. -+ It implements the internal poll(2) operation which is -+ implemented by fuse only (curretnly). -+ -+config AUFS_POLL -+ bool -+ help -+ Automatic configuration for internal use. -+ -+config AUFS_BR_HFSPLUS -+ bool "Hfsplus as an aufs branch" -+ depends on HFSPLUS_FS -+ default y -+ help -+ If you want to use hfsplus fs as an aufs branch fs, then enable -+ this option. This option introduces a small overhead at -+ copying-up a file on hfsplus. -+ -+config AUFS_BDEV_LOOP -+ bool -+ depends on BLK_DEV_LOOP -+ default y -+ help -+ Automatic configuration for internal use. -+ Convert =[ym] into =y. -+ -+config AUFS_DEBUG -+ bool "Debug aufs" -+ help -+ Enable this to compile aufs internal debug code. -+ It will have a negative impact to the performance. -+ -+config AUFS_MAGIC_SYSRQ -+ bool -+ depends on AUFS_DEBUG && MAGIC_SYSRQ -+ default y -+ help -+ Automatic configuration for internal use. -+ When aufs supports Magic SysRq, enabled automatically. -+endif -diff --git a/fs/aufs/Makefile b/fs/aufs/Makefile -new file mode 100644 -index 0000000..c7a501e ---- /dev/null -+++ b/fs/aufs/Makefile -@@ -0,0 +1,44 @@ -+ -+include ${src}/magic.mk -+ifeq (${CONFIG_AUFS_FS},m) -+include ${src}/conf.mk -+endif -+-include ${src}/priv_def.mk -+ -+# cf. include/linux/kernel.h -+# enable pr_debug -+ccflags-y += -DDEBUG -+# sparse requires the full pathname -+ifdef M -+ccflags-y += -include ${M}/../../include/uapi/linux/aufs_type.h -+else -+ccflags-y += -include ${srctree}/include/uapi/linux/aufs_type.h -+endif -+ -+obj-$(CONFIG_AUFS_FS) += aufs.o -+aufs-y := module.o sbinfo.o super.o branch.o xino.o sysaufs.o opts.o \ -+ wkq.o vfsub.o dcsub.o \ -+ cpup.o whout.o wbr_policy.o \ -+ dinfo.o dentry.o \ -+ dynop.o \ -+ finfo.o file.o f_op.o \ -+ dir.o vdir.o \ -+ iinfo.o inode.o i_op.o i_op_add.o i_op_del.o i_op_ren.o \ -+ mvdown.o ioctl.o -+ -+# all are boolean -+aufs-$(CONFIG_PROC_FS) += procfs.o plink.o -+aufs-$(CONFIG_SYSFS) += sysfs.o -+aufs-$(CONFIG_DEBUG_FS) += dbgaufs.o -+aufs-$(CONFIG_AUFS_BDEV_LOOP) += loop.o -+aufs-$(CONFIG_AUFS_HNOTIFY) += hnotify.o -+aufs-$(CONFIG_AUFS_HFSNOTIFY) += hfsnotify.o -+aufs-$(CONFIG_AUFS_EXPORT) += export.o -+aufs-$(CONFIG_AUFS_XATTR) += xattr.o -+aufs-$(CONFIG_FS_POSIX_ACL) += posix_acl.o -+aufs-$(CONFIG_AUFS_FHSM) += fhsm.o -+aufs-$(CONFIG_AUFS_POLL) += poll.o -+aufs-$(CONFIG_AUFS_RDU) += rdu.o -+aufs-$(CONFIG_AUFS_BR_HFSPLUS) += hfsplus.o -+aufs-$(CONFIG_AUFS_DEBUG) += debug.o -+aufs-$(CONFIG_AUFS_MAGIC_SYSRQ) += sysrq.o -diff --git a/fs/aufs/aufs.h b/fs/aufs/aufs.h -new file mode 100644 -index 0000000..e48d268 ---- /dev/null -+++ b/fs/aufs/aufs.h -@@ -0,0 +1,59 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * all header files -+ */ -+ -+#ifndef __AUFS_H__ -+#define __AUFS_H__ -+ -+#ifdef __KERNEL__ -+ -+#define AuStub(type, name, body, ...) \ -+ static inline type name(__VA_ARGS__) { body; } -+ -+#define AuStubVoid(name, ...) \ -+ AuStub(void, name, , __VA_ARGS__) -+#define AuStubInt0(name, ...) \ -+ AuStub(int, name, return 0, __VA_ARGS__) -+ -+#include "debug.h" -+ -+#include "branch.h" -+#include "cpup.h" -+#include "dcsub.h" -+#include "dbgaufs.h" -+#include "dentry.h" -+#include "dir.h" -+#include "dynop.h" -+#include "file.h" -+#include "fstype.h" -+#include "inode.h" -+#include "loop.h" -+#include "module.h" -+#include "opts.h" -+#include "rwsem.h" -+#include "spl.h" -+#include "super.h" -+#include "sysaufs.h" -+#include "vfsub.h" -+#include "whout.h" -+#include "wkq.h" -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_H__ */ -diff --git a/fs/aufs/branch.c b/fs/aufs/branch.c -new file mode 100644 -index 0000000..17210b2 ---- /dev/null -+++ b/fs/aufs/branch.c -@@ -0,0 +1,1402 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * branch management -+ */ -+ -+#include -+#include -+#include "aufs.h" -+ -+/* -+ * free a single branch -+ */ -+static void au_br_do_free(struct au_branch *br) -+{ -+ int i; -+ struct au_wbr *wbr; -+ struct au_dykey **key; -+ -+ au_hnotify_fin_br(br); -+ -+ if (br->br_xino.xi_file) -+ fput(br->br_xino.xi_file); -+ mutex_destroy(&br->br_xino.xi_nondir_mtx); -+ -+ AuDebugOn(atomic_read(&br->br_count)); -+ -+ wbr = br->br_wbr; -+ if (wbr) { -+ for (i = 0; i < AuBrWh_Last; i++) -+ dput(wbr->wbr_wh[i]); -+ AuDebugOn(atomic_read(&wbr->wbr_wh_running)); -+ AuRwDestroy(&wbr->wbr_wh_rwsem); -+ } -+ -+ if (br->br_fhsm) { -+ au_br_fhsm_fin(br->br_fhsm); -+ kfree(br->br_fhsm); -+ } -+ -+ key = br->br_dykey; -+ for (i = 0; i < AuBrDynOp; i++, key++) -+ if (*key) -+ au_dy_put(*key); -+ else -+ break; -+ -+ /* recursive lock, s_umount of branch's */ -+ lockdep_off(); -+ path_put(&br->br_path); -+ lockdep_on(); -+ kfree(wbr); -+ kfree(br); -+} -+ -+/* -+ * frees all branches -+ */ -+void au_br_free(struct au_sbinfo *sbinfo) -+{ -+ aufs_bindex_t bmax; -+ struct au_branch **br; -+ -+ AuRwMustWriteLock(&sbinfo->si_rwsem); -+ -+ bmax = sbinfo->si_bend + 1; -+ br = sbinfo->si_branch; -+ while (bmax--) -+ au_br_do_free(*br++); -+} -+ -+/* -+ * find the index of a branch which is specified by @br_id. -+ */ -+int au_br_index(struct super_block *sb, aufs_bindex_t br_id) -+{ -+ aufs_bindex_t bindex, bend; -+ -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) -+ if (au_sbr_id(sb, bindex) == br_id) -+ return bindex; -+ return -1; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * add a branch -+ */ -+ -+static int test_overlap(struct super_block *sb, struct dentry *h_adding, -+ struct dentry *h_root) -+{ -+ if (unlikely(h_adding == h_root -+ || au_test_loopback_overlap(sb, h_adding))) -+ return 1; -+ if (h_adding->d_sb != h_root->d_sb) -+ return 0; -+ return au_test_subdir(h_adding, h_root) -+ || au_test_subdir(h_root, h_adding); -+} -+ -+/* -+ * returns a newly allocated branch. @new_nbranch is a number of branches -+ * after adding a branch. -+ */ -+static struct au_branch *au_br_alloc(struct super_block *sb, int new_nbranch, -+ int perm) -+{ -+ struct au_branch *add_branch; -+ struct dentry *root; -+ int err; -+ -+ err = -ENOMEM; -+ root = sb->s_root; -+ add_branch = kzalloc(sizeof(*add_branch), GFP_NOFS); -+ if (unlikely(!add_branch)) -+ goto out; -+ -+ err = au_hnotify_init_br(add_branch, perm); -+ if (unlikely(err)) -+ goto out_br; -+ -+ if (au_br_writable(perm)) { -+ /* may be freed separately at changing the branch permission */ -+ add_branch->br_wbr = kzalloc(sizeof(*add_branch->br_wbr), -+ GFP_NOFS); -+ if (unlikely(!add_branch->br_wbr)) -+ goto out_hnotify; -+ } -+ -+ if (au_br_fhsm(perm)) { -+ err = au_fhsm_br_alloc(add_branch); -+ if (unlikely(err)) -+ goto out_wbr; -+ } -+ -+ err = au_sbr_realloc(au_sbi(sb), new_nbranch); -+ if (!err) -+ err = au_di_realloc(au_di(root), new_nbranch); -+ if (!err) -+ err = au_ii_realloc(au_ii(root->d_inode), new_nbranch); -+ if (!err) -+ return add_branch; /* success */ -+ -+out_wbr: -+ kfree(add_branch->br_wbr); -+out_hnotify: -+ au_hnotify_fin_br(add_branch); -+out_br: -+ kfree(add_branch); -+out: -+ return ERR_PTR(err); -+} -+ -+/* -+ * test if the branch permission is legal or not. -+ */ -+static int test_br(struct inode *inode, int brperm, char *path) -+{ -+ int err; -+ -+ err = (au_br_writable(brperm) && IS_RDONLY(inode)); -+ if (!err) -+ goto out; -+ -+ err = -EINVAL; -+ pr_err("write permission for readonly mount or inode, %s\n", path); -+ -+out: -+ return err; -+} -+ -+/* -+ * returns: -+ * 0: success, the caller will add it -+ * plus: success, it is already unified, the caller should ignore it -+ * minus: error -+ */ -+static int test_add(struct super_block *sb, struct au_opt_add *add, int remount) -+{ -+ int err; -+ aufs_bindex_t bend, bindex; -+ struct dentry *root; -+ struct inode *inode, *h_inode; -+ -+ root = sb->s_root; -+ bend = au_sbend(sb); -+ if (unlikely(bend >= 0 -+ && au_find_dbindex(root, add->path.dentry) >= 0)) { -+ err = 1; -+ if (!remount) { -+ err = -EINVAL; -+ pr_err("%s duplicated\n", add->pathname); -+ } -+ goto out; -+ } -+ -+ err = -ENOSPC; /* -E2BIG; */ -+ if (unlikely(AUFS_BRANCH_MAX <= add->bindex -+ || AUFS_BRANCH_MAX - 1 <= bend)) { -+ pr_err("number of branches exceeded %s\n", add->pathname); -+ goto out; -+ } -+ -+ err = -EDOM; -+ if (unlikely(add->bindex < 0 || bend + 1 < add->bindex)) { -+ pr_err("bad index %d\n", add->bindex); -+ goto out; -+ } -+ -+ inode = add->path.dentry->d_inode; -+ err = -ENOENT; -+ if (unlikely(!inode->i_nlink)) { -+ pr_err("no existence %s\n", add->pathname); -+ goto out; -+ } -+ -+ err = -EINVAL; -+ if (unlikely(inode->i_sb == sb)) { -+ pr_err("%s must be outside\n", add->pathname); -+ goto out; -+ } -+ -+ if (unlikely(au_test_fs_unsuppoted(inode->i_sb))) { -+ pr_err("unsupported filesystem, %s (%s)\n", -+ add->pathname, au_sbtype(inode->i_sb)); -+ goto out; -+ } -+ -+ if (unlikely(inode->i_sb->s_stack_depth)) { -+ pr_err("already stacked, %s (%s)\n", -+ add->pathname, au_sbtype(inode->i_sb)); -+ goto out; -+ } -+ -+ err = test_br(add->path.dentry->d_inode, add->perm, add->pathname); -+ if (unlikely(err)) -+ goto out; -+ -+ if (bend < 0) -+ return 0; /* success */ -+ -+ err = -EINVAL; -+ for (bindex = 0; bindex <= bend; bindex++) -+ if (unlikely(test_overlap(sb, add->path.dentry, -+ au_h_dptr(root, bindex)))) { -+ pr_err("%s is overlapped\n", add->pathname); -+ goto out; -+ } -+ -+ err = 0; -+ if (au_opt_test(au_mntflags(sb), WARN_PERM)) { -+ h_inode = au_h_dptr(root, 0)->d_inode; -+ if ((h_inode->i_mode & S_IALLUGO) != (inode->i_mode & S_IALLUGO) -+ || !uid_eq(h_inode->i_uid, inode->i_uid) -+ || !gid_eq(h_inode->i_gid, inode->i_gid)) -+ pr_warn("uid/gid/perm %s %u/%u/0%o, %u/%u/0%o\n", -+ add->pathname, -+ i_uid_read(inode), i_gid_read(inode), -+ (inode->i_mode & S_IALLUGO), -+ i_uid_read(h_inode), i_gid_read(h_inode), -+ (h_inode->i_mode & S_IALLUGO)); -+ } -+ -+out: -+ return err; -+} -+ -+/* -+ * initialize or clean the whiteouts for an adding branch -+ */ -+static int au_br_init_wh(struct super_block *sb, struct au_branch *br, -+ int new_perm) -+{ -+ int err, old_perm; -+ aufs_bindex_t bindex; -+ struct mutex *h_mtx; -+ struct au_wbr *wbr; -+ struct au_hinode *hdir; -+ -+ err = vfsub_mnt_want_write(au_br_mnt(br)); -+ if (unlikely(err)) -+ goto out; -+ -+ wbr = br->br_wbr; -+ old_perm = br->br_perm; -+ br->br_perm = new_perm; -+ hdir = NULL; -+ h_mtx = NULL; -+ bindex = au_br_index(sb, br->br_id); -+ if (0 <= bindex) { -+ hdir = au_hi(sb->s_root->d_inode, bindex); -+ au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); -+ } else { -+ h_mtx = &au_br_dentry(br)->d_inode->i_mutex; -+ mutex_lock_nested(h_mtx, AuLsc_I_PARENT); -+ } -+ if (!wbr) -+ err = au_wh_init(br, sb); -+ else { -+ wbr_wh_write_lock(wbr); -+ err = au_wh_init(br, sb); -+ wbr_wh_write_unlock(wbr); -+ } -+ if (hdir) -+ au_hn_imtx_unlock(hdir); -+ else -+ mutex_unlock(h_mtx); -+ vfsub_mnt_drop_write(au_br_mnt(br)); -+ br->br_perm = old_perm; -+ -+ if (!err && wbr && !au_br_writable(new_perm)) { -+ kfree(wbr); -+ br->br_wbr = NULL; -+ } -+ -+out: -+ return err; -+} -+ -+static int au_wbr_init(struct au_branch *br, struct super_block *sb, -+ int perm) -+{ -+ int err; -+ struct kstatfs kst; -+ struct au_wbr *wbr; -+ -+ wbr = br->br_wbr; -+ au_rw_init(&wbr->wbr_wh_rwsem); -+ atomic_set(&wbr->wbr_wh_running, 0); -+ -+ /* -+ * a limit for rmdir/rename a dir -+ * cf. AUFS_MAX_NAMELEN in include/uapi/linux/aufs_type.h -+ */ -+ err = vfs_statfs(&br->br_path, &kst); -+ if (unlikely(err)) -+ goto out; -+ err = -EINVAL; -+ if (kst.f_namelen >= NAME_MAX) -+ err = au_br_init_wh(sb, br, perm); -+ else -+ pr_err("%pd(%s), unsupported namelen %ld\n", -+ au_br_dentry(br), -+ au_sbtype(au_br_dentry(br)->d_sb), kst.f_namelen); -+ -+out: -+ return err; -+} -+ -+/* initialize a new branch */ -+static int au_br_init(struct au_branch *br, struct super_block *sb, -+ struct au_opt_add *add) -+{ -+ int err; -+ -+ err = 0; -+ mutex_init(&br->br_xino.xi_nondir_mtx); -+ br->br_perm = add->perm; -+ br->br_path = add->path; /* set first, path_get() later */ -+ spin_lock_init(&br->br_dykey_lock); -+ atomic_set(&br->br_count, 0); -+ atomic_set(&br->br_xino_running, 0); -+ br->br_id = au_new_br_id(sb); -+ AuDebugOn(br->br_id < 0); -+ -+ if (au_br_writable(add->perm)) { -+ err = au_wbr_init(br, sb, add->perm); -+ if (unlikely(err)) -+ goto out_err; -+ } -+ -+ if (au_opt_test(au_mntflags(sb), XINO)) { -+ err = au_xino_br(sb, br, add->path.dentry->d_inode->i_ino, -+ au_sbr(sb, 0)->br_xino.xi_file, /*do_test*/1); -+ if (unlikely(err)) { -+ AuDebugOn(br->br_xino.xi_file); -+ goto out_err; -+ } -+ } -+ -+ sysaufs_br_init(br); -+ path_get(&br->br_path); -+ goto out; /* success */ -+ -+out_err: -+ memset(&br->br_path, 0, sizeof(br->br_path)); -+out: -+ return err; -+} -+ -+static void au_br_do_add_brp(struct au_sbinfo *sbinfo, aufs_bindex_t bindex, -+ struct au_branch *br, aufs_bindex_t bend, -+ aufs_bindex_t amount) -+{ -+ struct au_branch **brp; -+ -+ AuRwMustWriteLock(&sbinfo->si_rwsem); -+ -+ brp = sbinfo->si_branch + bindex; -+ memmove(brp + 1, brp, sizeof(*brp) * amount); -+ *brp = br; -+ sbinfo->si_bend++; -+ if (unlikely(bend < 0)) -+ sbinfo->si_bend = 0; -+} -+ -+static void au_br_do_add_hdp(struct au_dinfo *dinfo, aufs_bindex_t bindex, -+ aufs_bindex_t bend, aufs_bindex_t amount) -+{ -+ struct au_hdentry *hdp; -+ -+ AuRwMustWriteLock(&dinfo->di_rwsem); -+ -+ hdp = dinfo->di_hdentry + bindex; -+ memmove(hdp + 1, hdp, sizeof(*hdp) * amount); -+ au_h_dentry_init(hdp); -+ dinfo->di_bend++; -+ if (unlikely(bend < 0)) -+ dinfo->di_bstart = 0; -+} -+ -+static void au_br_do_add_hip(struct au_iinfo *iinfo, aufs_bindex_t bindex, -+ aufs_bindex_t bend, aufs_bindex_t amount) -+{ -+ struct au_hinode *hip; -+ -+ AuRwMustWriteLock(&iinfo->ii_rwsem); -+ -+ hip = iinfo->ii_hinode + bindex; -+ memmove(hip + 1, hip, sizeof(*hip) * amount); -+ hip->hi_inode = NULL; -+ au_hn_init(hip); -+ iinfo->ii_bend++; -+ if (unlikely(bend < 0)) -+ iinfo->ii_bstart = 0; -+} -+ -+static void au_br_do_add(struct super_block *sb, struct au_branch *br, -+ aufs_bindex_t bindex) -+{ -+ struct dentry *root, *h_dentry; -+ struct inode *root_inode; -+ aufs_bindex_t bend, amount; -+ -+ root = sb->s_root; -+ root_inode = root->d_inode; -+ bend = au_sbend(sb); -+ amount = bend + 1 - bindex; -+ h_dentry = au_br_dentry(br); -+ au_sbilist_lock(); -+ au_br_do_add_brp(au_sbi(sb), bindex, br, bend, amount); -+ au_br_do_add_hdp(au_di(root), bindex, bend, amount); -+ au_br_do_add_hip(au_ii(root_inode), bindex, bend, amount); -+ au_set_h_dptr(root, bindex, dget(h_dentry)); -+ au_set_h_iptr(root_inode, bindex, au_igrab(h_dentry->d_inode), -+ /*flags*/0); -+ au_sbilist_unlock(); -+} -+ -+int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount) -+{ -+ int err; -+ aufs_bindex_t bend, add_bindex; -+ struct dentry *root, *h_dentry; -+ struct inode *root_inode; -+ struct au_branch *add_branch; -+ -+ root = sb->s_root; -+ root_inode = root->d_inode; -+ IMustLock(root_inode); -+ err = test_add(sb, add, remount); -+ if (unlikely(err < 0)) -+ goto out; -+ if (err) { -+ err = 0; -+ goto out; /* success */ -+ } -+ -+ bend = au_sbend(sb); -+ add_branch = au_br_alloc(sb, bend + 2, add->perm); -+ err = PTR_ERR(add_branch); -+ if (IS_ERR(add_branch)) -+ goto out; -+ -+ err = au_br_init(add_branch, sb, add); -+ if (unlikely(err)) { -+ au_br_do_free(add_branch); -+ goto out; -+ } -+ -+ add_bindex = add->bindex; -+ if (!remount) -+ au_br_do_add(sb, add_branch, add_bindex); -+ else { -+ sysaufs_brs_del(sb, add_bindex); -+ au_br_do_add(sb, add_branch, add_bindex); -+ sysaufs_brs_add(sb, add_bindex); -+ } -+ -+ h_dentry = add->path.dentry; -+ if (!add_bindex) { -+ au_cpup_attr_all(root_inode, /*force*/1); -+ sb->s_maxbytes = h_dentry->d_sb->s_maxbytes; -+ } else -+ au_add_nlink(root_inode, h_dentry->d_inode); -+ -+ /* -+ * this test/set prevents aufs from handling unnecesary notify events -+ * of xino files, in case of re-adding a writable branch which was -+ * once detached from aufs. -+ */ -+ if (au_xino_brid(sb) < 0 -+ && au_br_writable(add_branch->br_perm) -+ && !au_test_fs_bad_xino(h_dentry->d_sb) -+ && add_branch->br_xino.xi_file -+ && add_branch->br_xino.xi_file->f_dentry->d_parent == h_dentry) -+ au_xino_brid_set(sb, add_branch->br_id); -+ -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static unsigned long long au_farray_cb(void *a, -+ unsigned long long max __maybe_unused, -+ void *arg) -+{ -+ unsigned long long n; -+ struct file **p, *f; -+ struct au_sphlhead *files; -+ struct au_finfo *finfo; -+ struct super_block *sb = arg; -+ -+ n = 0; -+ p = a; -+ files = &au_sbi(sb)->si_files; -+ spin_lock(&files->spin); -+ hlist_for_each_entry(finfo, &files->head, fi_hlist) { -+ f = finfo->fi_file; -+ if (file_count(f) -+ && !special_file(file_inode(f)->i_mode)) { -+ get_file(f); -+ *p++ = f; -+ n++; -+ AuDebugOn(n > max); -+ } -+ } -+ spin_unlock(&files->spin); -+ -+ return n; -+} -+ -+static struct file **au_farray_alloc(struct super_block *sb, -+ unsigned long long *max) -+{ -+ *max = atomic_long_read(&au_sbi(sb)->si_nfiles); -+ return au_array_alloc(max, au_farray_cb, sb); -+} -+ -+static void au_farray_free(struct file **a, unsigned long long max) -+{ -+ unsigned long long ull; -+ -+ for (ull = 0; ull < max; ull++) -+ if (a[ull]) -+ fput(a[ull]); -+ kvfree(a); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * delete a branch -+ */ -+ -+/* to show the line number, do not make it inlined function */ -+#define AuVerbose(do_info, fmt, ...) do { \ -+ if (do_info) \ -+ pr_info(fmt, ##__VA_ARGS__); \ -+} while (0) -+ -+static int au_test_ibusy(struct inode *inode, aufs_bindex_t bstart, -+ aufs_bindex_t bend) -+{ -+ return (inode && !S_ISDIR(inode->i_mode)) || bstart == bend; -+} -+ -+static int au_test_dbusy(struct dentry *dentry, aufs_bindex_t bstart, -+ aufs_bindex_t bend) -+{ -+ return au_test_ibusy(dentry->d_inode, bstart, bend); -+} -+ -+/* -+ * test if the branch is deletable or not. -+ */ -+static int test_dentry_busy(struct dentry *root, aufs_bindex_t bindex, -+ unsigned int sigen, const unsigned int verbose) -+{ -+ int err, i, j, ndentry; -+ aufs_bindex_t bstart, bend; -+ struct au_dcsub_pages dpages; -+ struct au_dpage *dpage; -+ struct dentry *d; -+ -+ err = au_dpages_init(&dpages, GFP_NOFS); -+ if (unlikely(err)) -+ goto out; -+ err = au_dcsub_pages(&dpages, root, NULL, NULL); -+ if (unlikely(err)) -+ goto out_dpages; -+ -+ for (i = 0; !err && i < dpages.ndpage; i++) { -+ dpage = dpages.dpages + i; -+ ndentry = dpage->ndentry; -+ for (j = 0; !err && j < ndentry; j++) { -+ d = dpage->dentries[j]; -+ AuDebugOn(au_dcount(d) <= 0); -+ if (!au_digen_test(d, sigen)) { -+ di_read_lock_child(d, AuLock_IR); -+ if (unlikely(au_dbrange_test(d))) { -+ di_read_unlock(d, AuLock_IR); -+ continue; -+ } -+ } else { -+ di_write_lock_child(d); -+ if (unlikely(au_dbrange_test(d))) { -+ di_write_unlock(d); -+ continue; -+ } -+ err = au_reval_dpath(d, sigen); -+ if (!err) -+ di_downgrade_lock(d, AuLock_IR); -+ else { -+ di_write_unlock(d); -+ break; -+ } -+ } -+ -+ /* AuDbgDentry(d); */ -+ bstart = au_dbstart(d); -+ bend = au_dbend(d); -+ if (bstart <= bindex -+ && bindex <= bend -+ && au_h_dptr(d, bindex) -+ && au_test_dbusy(d, bstart, bend)) { -+ err = -EBUSY; -+ AuVerbose(verbose, "busy %pd\n", d); -+ AuDbgDentry(d); -+ } -+ di_read_unlock(d, AuLock_IR); -+ } -+ } -+ -+out_dpages: -+ au_dpages_free(&dpages); -+out: -+ return err; -+} -+ -+static int test_inode_busy(struct super_block *sb, aufs_bindex_t bindex, -+ unsigned int sigen, const unsigned int verbose) -+{ -+ int err; -+ unsigned long long max, ull; -+ struct inode *i, **array; -+ aufs_bindex_t bstart, bend; -+ -+ array = au_iarray_alloc(sb, &max); -+ err = PTR_ERR(array); -+ if (IS_ERR(array)) -+ goto out; -+ -+ err = 0; -+ AuDbg("b%d\n", bindex); -+ for (ull = 0; !err && ull < max; ull++) { -+ i = array[ull]; -+ if (unlikely(!i)) -+ break; -+ if (i->i_ino == AUFS_ROOT_INO) -+ continue; -+ -+ /* AuDbgInode(i); */ -+ if (au_iigen(i, NULL) == sigen) -+ ii_read_lock_child(i); -+ else { -+ ii_write_lock_child(i); -+ err = au_refresh_hinode_self(i); -+ au_iigen_dec(i); -+ if (!err) -+ ii_downgrade_lock(i); -+ else { -+ ii_write_unlock(i); -+ break; -+ } -+ } -+ -+ bstart = au_ibstart(i); -+ bend = au_ibend(i); -+ if (bstart <= bindex -+ && bindex <= bend -+ && au_h_iptr(i, bindex) -+ && au_test_ibusy(i, bstart, bend)) { -+ err = -EBUSY; -+ AuVerbose(verbose, "busy i%lu\n", i->i_ino); -+ AuDbgInode(i); -+ } -+ ii_read_unlock(i); -+ } -+ au_iarray_free(array, max); -+ -+out: -+ return err; -+} -+ -+static int test_children_busy(struct dentry *root, aufs_bindex_t bindex, -+ const unsigned int verbose) -+{ -+ int err; -+ unsigned int sigen; -+ -+ sigen = au_sigen(root->d_sb); -+ DiMustNoWaiters(root); -+ IiMustNoWaiters(root->d_inode); -+ di_write_unlock(root); -+ err = test_dentry_busy(root, bindex, sigen, verbose); -+ if (!err) -+ err = test_inode_busy(root->d_sb, bindex, sigen, verbose); -+ di_write_lock_child(root); /* aufs_write_lock() calls ..._child() */ -+ -+ return err; -+} -+ -+static int test_dir_busy(struct file *file, aufs_bindex_t br_id, -+ struct file **to_free, int *idx) -+{ -+ int err; -+ unsigned char matched, root; -+ aufs_bindex_t bindex, bend; -+ struct au_fidir *fidir; -+ struct au_hfile *hfile; -+ -+ err = 0; -+ root = IS_ROOT(file->f_dentry); -+ if (root) { -+ get_file(file); -+ to_free[*idx] = file; -+ (*idx)++; -+ goto out; -+ } -+ -+ matched = 0; -+ fidir = au_fi(file)->fi_hdir; -+ AuDebugOn(!fidir); -+ bend = au_fbend_dir(file); -+ for (bindex = au_fbstart(file); bindex <= bend; bindex++) { -+ hfile = fidir->fd_hfile + bindex; -+ if (!hfile->hf_file) -+ continue; -+ -+ if (hfile->hf_br->br_id == br_id) { -+ matched = 1; -+ break; -+ } -+ } -+ if (matched) -+ err = -EBUSY; -+ -+out: -+ return err; -+} -+ -+static int test_file_busy(struct super_block *sb, aufs_bindex_t br_id, -+ struct file **to_free, int opened) -+{ -+ int err, idx; -+ unsigned long long ull, max; -+ aufs_bindex_t bstart; -+ struct file *file, **array; -+ struct dentry *root; -+ struct au_hfile *hfile; -+ -+ array = au_farray_alloc(sb, &max); -+ err = PTR_ERR(array); -+ if (IS_ERR(array)) -+ goto out; -+ -+ err = 0; -+ idx = 0; -+ root = sb->s_root; -+ di_write_unlock(root); -+ for (ull = 0; ull < max; ull++) { -+ file = array[ull]; -+ if (unlikely(!file)) -+ break; -+ -+ /* AuDbg("%pD\n", file); */ -+ fi_read_lock(file); -+ bstart = au_fbstart(file); -+ if (!d_is_dir(file->f_path.dentry)) { -+ hfile = &au_fi(file)->fi_htop; -+ if (hfile->hf_br->br_id == br_id) -+ err = -EBUSY; -+ } else -+ err = test_dir_busy(file, br_id, to_free, &idx); -+ fi_read_unlock(file); -+ if (unlikely(err)) -+ break; -+ } -+ di_write_lock_child(root); -+ au_farray_free(array, max); -+ AuDebugOn(idx > opened); -+ -+out: -+ return err; -+} -+ -+static void br_del_file(struct file **to_free, unsigned long long opened, -+ aufs_bindex_t br_id) -+{ -+ unsigned long long ull; -+ aufs_bindex_t bindex, bstart, bend, bfound; -+ struct file *file; -+ struct au_fidir *fidir; -+ struct au_hfile *hfile; -+ -+ for (ull = 0; ull < opened; ull++) { -+ file = to_free[ull]; -+ if (unlikely(!file)) -+ break; -+ -+ /* AuDbg("%pD\n", file); */ -+ AuDebugOn(!d_is_dir(file->f_path.dentry)); -+ bfound = -1; -+ fidir = au_fi(file)->fi_hdir; -+ AuDebugOn(!fidir); -+ fi_write_lock(file); -+ bstart = au_fbstart(file); -+ bend = au_fbend_dir(file); -+ for (bindex = bstart; bindex <= bend; bindex++) { -+ hfile = fidir->fd_hfile + bindex; -+ if (!hfile->hf_file) -+ continue; -+ -+ if (hfile->hf_br->br_id == br_id) { -+ bfound = bindex; -+ break; -+ } -+ } -+ AuDebugOn(bfound < 0); -+ au_set_h_fptr(file, bfound, NULL); -+ if (bfound == bstart) { -+ for (bstart++; bstart <= bend; bstart++) -+ if (au_hf_dir(file, bstart)) { -+ au_set_fbstart(file, bstart); -+ break; -+ } -+ } -+ fi_write_unlock(file); -+ } -+} -+ -+static void au_br_do_del_brp(struct au_sbinfo *sbinfo, -+ const aufs_bindex_t bindex, -+ const aufs_bindex_t bend) -+{ -+ struct au_branch **brp, **p; -+ -+ AuRwMustWriteLock(&sbinfo->si_rwsem); -+ -+ brp = sbinfo->si_branch + bindex; -+ if (bindex < bend) -+ memmove(brp, brp + 1, sizeof(*brp) * (bend - bindex)); -+ sbinfo->si_branch[0 + bend] = NULL; -+ sbinfo->si_bend--; -+ -+ p = krealloc(sbinfo->si_branch, sizeof(*p) * bend, AuGFP_SBILIST); -+ if (p) -+ sbinfo->si_branch = p; -+ /* harmless error */ -+} -+ -+static void au_br_do_del_hdp(struct au_dinfo *dinfo, const aufs_bindex_t bindex, -+ const aufs_bindex_t bend) -+{ -+ struct au_hdentry *hdp, *p; -+ -+ AuRwMustWriteLock(&dinfo->di_rwsem); -+ -+ hdp = dinfo->di_hdentry; -+ if (bindex < bend) -+ memmove(hdp + bindex, hdp + bindex + 1, -+ sizeof(*hdp) * (bend - bindex)); -+ hdp[0 + bend].hd_dentry = NULL; -+ dinfo->di_bend--; -+ -+ p = krealloc(hdp, sizeof(*p) * bend, AuGFP_SBILIST); -+ if (p) -+ dinfo->di_hdentry = p; -+ /* harmless error */ -+} -+ -+static void au_br_do_del_hip(struct au_iinfo *iinfo, const aufs_bindex_t bindex, -+ const aufs_bindex_t bend) -+{ -+ struct au_hinode *hip, *p; -+ -+ AuRwMustWriteLock(&iinfo->ii_rwsem); -+ -+ hip = iinfo->ii_hinode + bindex; -+ if (bindex < bend) -+ memmove(hip, hip + 1, sizeof(*hip) * (bend - bindex)); -+ iinfo->ii_hinode[0 + bend].hi_inode = NULL; -+ au_hn_init(iinfo->ii_hinode + bend); -+ iinfo->ii_bend--; -+ -+ p = krealloc(iinfo->ii_hinode, sizeof(*p) * bend, AuGFP_SBILIST); -+ if (p) -+ iinfo->ii_hinode = p; -+ /* harmless error */ -+} -+ -+static void au_br_do_del(struct super_block *sb, aufs_bindex_t bindex, -+ struct au_branch *br) -+{ -+ aufs_bindex_t bend; -+ struct au_sbinfo *sbinfo; -+ struct dentry *root, *h_root; -+ struct inode *inode, *h_inode; -+ struct au_hinode *hinode; -+ -+ SiMustWriteLock(sb); -+ -+ root = sb->s_root; -+ inode = root->d_inode; -+ sbinfo = au_sbi(sb); -+ bend = sbinfo->si_bend; -+ -+ h_root = au_h_dptr(root, bindex); -+ hinode = au_hi(inode, bindex); -+ h_inode = au_igrab(hinode->hi_inode); -+ au_hiput(hinode); -+ -+ au_sbilist_lock(); -+ au_br_do_del_brp(sbinfo, bindex, bend); -+ au_br_do_del_hdp(au_di(root), bindex, bend); -+ au_br_do_del_hip(au_ii(inode), bindex, bend); -+ au_sbilist_unlock(); -+ -+ dput(h_root); -+ iput(h_inode); -+ au_br_do_free(br); -+} -+ -+static unsigned long long empty_cb(void *array, unsigned long long max, -+ void *arg) -+{ -+ return max; -+} -+ -+int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount) -+{ -+ int err, rerr, i; -+ unsigned long long opened; -+ unsigned int mnt_flags; -+ aufs_bindex_t bindex, bend, br_id; -+ unsigned char do_wh, verbose; -+ struct au_branch *br; -+ struct au_wbr *wbr; -+ struct dentry *root; -+ struct file **to_free; -+ -+ err = 0; -+ opened = 0; -+ to_free = NULL; -+ root = sb->s_root; -+ bindex = au_find_dbindex(root, del->h_path.dentry); -+ if (bindex < 0) { -+ if (remount) -+ goto out; /* success */ -+ err = -ENOENT; -+ pr_err("%s no such branch\n", del->pathname); -+ goto out; -+ } -+ AuDbg("bindex b%d\n", bindex); -+ -+ err = -EBUSY; -+ mnt_flags = au_mntflags(sb); -+ verbose = !!au_opt_test(mnt_flags, VERBOSE); -+ bend = au_sbend(sb); -+ if (unlikely(!bend)) { -+ AuVerbose(verbose, "no more branches left\n"); -+ goto out; -+ } -+ br = au_sbr(sb, bindex); -+ AuDebugOn(!path_equal(&br->br_path, &del->h_path)); -+ -+ br_id = br->br_id; -+ opened = atomic_read(&br->br_count); -+ if (unlikely(opened)) { -+ to_free = au_array_alloc(&opened, empty_cb, NULL); -+ err = PTR_ERR(to_free); -+ if (IS_ERR(to_free)) -+ goto out; -+ -+ err = test_file_busy(sb, br_id, to_free, opened); -+ if (unlikely(err)) { -+ AuVerbose(verbose, "%llu file(s) opened\n", opened); -+ goto out; -+ } -+ } -+ -+ wbr = br->br_wbr; -+ do_wh = wbr && (wbr->wbr_whbase || wbr->wbr_plink || wbr->wbr_orph); -+ if (do_wh) { -+ /* instead of WbrWhMustWriteLock(wbr) */ -+ SiMustWriteLock(sb); -+ for (i = 0; i < AuBrWh_Last; i++) { -+ dput(wbr->wbr_wh[i]); -+ wbr->wbr_wh[i] = NULL; -+ } -+ } -+ -+ err = test_children_busy(root, bindex, verbose); -+ if (unlikely(err)) { -+ if (do_wh) -+ goto out_wh; -+ goto out; -+ } -+ -+ err = 0; -+ if (to_free) { -+ /* -+ * now we confirmed the branch is deletable. -+ * let's free the remaining opened dirs on the branch. -+ */ -+ di_write_unlock(root); -+ br_del_file(to_free, opened, br_id); -+ di_write_lock_child(root); -+ } -+ -+ if (!remount) -+ au_br_do_del(sb, bindex, br); -+ else { -+ sysaufs_brs_del(sb, bindex); -+ au_br_do_del(sb, bindex, br); -+ sysaufs_brs_add(sb, bindex); -+ } -+ -+ if (!bindex) { -+ au_cpup_attr_all(root->d_inode, /*force*/1); -+ sb->s_maxbytes = au_sbr_sb(sb, 0)->s_maxbytes; -+ } else -+ au_sub_nlink(root->d_inode, del->h_path.dentry->d_inode); -+ if (au_opt_test(mnt_flags, PLINK)) -+ au_plink_half_refresh(sb, br_id); -+ -+ if (au_xino_brid(sb) == br_id) -+ au_xino_brid_set(sb, -1); -+ goto out; /* success */ -+ -+out_wh: -+ /* revert */ -+ rerr = au_br_init_wh(sb, br, br->br_perm); -+ if (rerr) -+ pr_warn("failed re-creating base whiteout, %s. (%d)\n", -+ del->pathname, rerr); -+out: -+ if (to_free) -+ au_farray_free(to_free, opened); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_ibusy(struct super_block *sb, struct aufs_ibusy __user *arg) -+{ -+ int err; -+ aufs_bindex_t bstart, bend; -+ struct aufs_ibusy ibusy; -+ struct inode *inode, *h_inode; -+ -+ err = -EPERM; -+ if (unlikely(!capable(CAP_SYS_ADMIN))) -+ goto out; -+ -+ err = copy_from_user(&ibusy, arg, sizeof(ibusy)); -+ if (!err) -+ err = !access_ok(VERIFY_WRITE, &arg->h_ino, sizeof(arg->h_ino)); -+ if (unlikely(err)) { -+ err = -EFAULT; -+ AuTraceErr(err); -+ goto out; -+ } -+ -+ err = -EINVAL; -+ si_read_lock(sb, AuLock_FLUSH); -+ if (unlikely(ibusy.bindex < 0 || ibusy.bindex > au_sbend(sb))) -+ goto out_unlock; -+ -+ err = 0; -+ ibusy.h_ino = 0; /* invalid */ -+ inode = ilookup(sb, ibusy.ino); -+ if (!inode -+ || inode->i_ino == AUFS_ROOT_INO -+ || is_bad_inode(inode)) -+ goto out_unlock; -+ -+ ii_read_lock_child(inode); -+ bstart = au_ibstart(inode); -+ bend = au_ibend(inode); -+ if (bstart <= ibusy.bindex && ibusy.bindex <= bend) { -+ h_inode = au_h_iptr(inode, ibusy.bindex); -+ if (h_inode && au_test_ibusy(inode, bstart, bend)) -+ ibusy.h_ino = h_inode->i_ino; -+ } -+ ii_read_unlock(inode); -+ iput(inode); -+ -+out_unlock: -+ si_read_unlock(sb); -+ if (!err) { -+ err = __put_user(ibusy.h_ino, &arg->h_ino); -+ if (unlikely(err)) { -+ err = -EFAULT; -+ AuTraceErr(err); -+ } -+ } -+out: -+ return err; -+} -+ -+long au_ibusy_ioctl(struct file *file, unsigned long arg) -+{ -+ return au_ibusy(file->f_dentry->d_sb, (void __user *)arg); -+} -+ -+#ifdef CONFIG_COMPAT -+long au_ibusy_compat_ioctl(struct file *file, unsigned long arg) -+{ -+ return au_ibusy(file->f_dentry->d_sb, compat_ptr(arg)); -+} -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * change a branch permission -+ */ -+ -+static void au_warn_ima(void) -+{ -+#ifdef CONFIG_IMA -+ /* since it doesn't support mark_files_ro() */ -+ AuWarn1("RW -> RO makes IMA to produce wrong message\n"); -+#endif -+} -+ -+static int do_need_sigen_inc(int a, int b) -+{ -+ return au_br_whable(a) && !au_br_whable(b); -+} -+ -+static int need_sigen_inc(int old, int new) -+{ -+ return do_need_sigen_inc(old, new) -+ || do_need_sigen_inc(new, old); -+} -+ -+static int au_br_mod_files_ro(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ int err, do_warn; -+ unsigned int mnt_flags; -+ unsigned long long ull, max; -+ aufs_bindex_t br_id; -+ unsigned char verbose, writer; -+ struct file *file, *hf, **array; -+ struct inode *inode; -+ struct au_hfile *hfile; -+ -+ mnt_flags = au_mntflags(sb); -+ verbose = !!au_opt_test(mnt_flags, VERBOSE); -+ -+ array = au_farray_alloc(sb, &max); -+ err = PTR_ERR(array); -+ if (IS_ERR(array)) -+ goto out; -+ -+ do_warn = 0; -+ br_id = au_sbr_id(sb, bindex); -+ for (ull = 0; ull < max; ull++) { -+ file = array[ull]; -+ if (unlikely(!file)) -+ break; -+ -+ /* AuDbg("%pD\n", file); */ -+ fi_read_lock(file); -+ if (unlikely(au_test_mmapped(file))) { -+ err = -EBUSY; -+ AuVerbose(verbose, "mmapped %pD\n", file); -+ AuDbgFile(file); -+ FiMustNoWaiters(file); -+ fi_read_unlock(file); -+ goto out_array; -+ } -+ -+ inode = file_inode(file); -+ hfile = &au_fi(file)->fi_htop; -+ hf = hfile->hf_file; -+ if (!S_ISREG(inode->i_mode) -+ || !(file->f_mode & FMODE_WRITE) -+ || hfile->hf_br->br_id != br_id -+ || !(hf->f_mode & FMODE_WRITE)) -+ array[ull] = NULL; -+ else { -+ do_warn = 1; -+ get_file(file); -+ } -+ -+ FiMustNoWaiters(file); -+ fi_read_unlock(file); -+ fput(file); -+ } -+ -+ err = 0; -+ if (do_warn) -+ au_warn_ima(); -+ -+ for (ull = 0; ull < max; ull++) { -+ file = array[ull]; -+ if (!file) -+ continue; -+ -+ /* todo: already flushed? */ -+ /* -+ * fs/super.c:mark_files_ro() is gone, but aufs keeps its -+ * approach which resets f_mode and calls mnt_drop_write() and -+ * file_release_write() for each file, because the branch -+ * attribute in aufs world is totally different from the native -+ * fs rw/ro mode. -+ */ -+ /* fi_read_lock(file); */ -+ hfile = &au_fi(file)->fi_htop; -+ hf = hfile->hf_file; -+ /* fi_read_unlock(file); */ -+ spin_lock(&hf->f_lock); -+ writer = !!(hf->f_mode & FMODE_WRITER); -+ hf->f_mode &= ~(FMODE_WRITE | FMODE_WRITER); -+ spin_unlock(&hf->f_lock); -+ if (writer) { -+ put_write_access(file_inode(hf)); -+ __mnt_drop_write(hf->f_path.mnt); -+ } -+ } -+ -+out_array: -+ au_farray_free(array, max); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount, -+ int *do_refresh) -+{ -+ int err, rerr; -+ aufs_bindex_t bindex; -+ struct dentry *root; -+ struct au_branch *br; -+ struct au_br_fhsm *bf; -+ -+ root = sb->s_root; -+ bindex = au_find_dbindex(root, mod->h_root); -+ if (bindex < 0) { -+ if (remount) -+ return 0; /* success */ -+ err = -ENOENT; -+ pr_err("%s no such branch\n", mod->path); -+ goto out; -+ } -+ AuDbg("bindex b%d\n", bindex); -+ -+ err = test_br(mod->h_root->d_inode, mod->perm, mod->path); -+ if (unlikely(err)) -+ goto out; -+ -+ br = au_sbr(sb, bindex); -+ AuDebugOn(mod->h_root != au_br_dentry(br)); -+ if (br->br_perm == mod->perm) -+ return 0; /* success */ -+ -+ /* pre-allocate for non-fhsm --> fhsm */ -+ bf = NULL; -+ if (!au_br_fhsm(br->br_perm) && au_br_fhsm(mod->perm)) { -+ err = au_fhsm_br_alloc(br); -+ if (unlikely(err)) -+ goto out; -+ bf = br->br_fhsm; -+ br->br_fhsm = NULL; -+ } -+ -+ if (au_br_writable(br->br_perm)) { -+ /* remove whiteout base */ -+ err = au_br_init_wh(sb, br, mod->perm); -+ if (unlikely(err)) -+ goto out_bf; -+ -+ if (!au_br_writable(mod->perm)) { -+ /* rw --> ro, file might be mmapped */ -+ DiMustNoWaiters(root); -+ IiMustNoWaiters(root->d_inode); -+ di_write_unlock(root); -+ err = au_br_mod_files_ro(sb, bindex); -+ /* aufs_write_lock() calls ..._child() */ -+ di_write_lock_child(root); -+ -+ if (unlikely(err)) { -+ rerr = -ENOMEM; -+ br->br_wbr = kzalloc(sizeof(*br->br_wbr), -+ GFP_NOFS); -+ if (br->br_wbr) -+ rerr = au_wbr_init(br, sb, br->br_perm); -+ if (unlikely(rerr)) { -+ AuIOErr("nested error %d (%d)\n", -+ rerr, err); -+ br->br_perm = mod->perm; -+ } -+ } -+ } -+ } else if (au_br_writable(mod->perm)) { -+ /* ro --> rw */ -+ err = -ENOMEM; -+ br->br_wbr = kzalloc(sizeof(*br->br_wbr), GFP_NOFS); -+ if (br->br_wbr) { -+ err = au_wbr_init(br, sb, mod->perm); -+ if (unlikely(err)) { -+ kfree(br->br_wbr); -+ br->br_wbr = NULL; -+ } -+ } -+ } -+ if (unlikely(err)) -+ goto out_bf; -+ -+ if (au_br_fhsm(br->br_perm)) { -+ if (!au_br_fhsm(mod->perm)) { -+ /* fhsm --> non-fhsm */ -+ au_br_fhsm_fin(br->br_fhsm); -+ kfree(br->br_fhsm); -+ br->br_fhsm = NULL; -+ } -+ } else if (au_br_fhsm(mod->perm)) -+ /* non-fhsm --> fhsm */ -+ br->br_fhsm = bf; -+ -+ *do_refresh |= need_sigen_inc(br->br_perm, mod->perm); -+ br->br_perm = mod->perm; -+ goto out; /* success */ -+ -+out_bf: -+ kfree(bf); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int au_br_stfs(struct au_branch *br, struct aufs_stfs *stfs) -+{ -+ int err; -+ struct kstatfs kstfs; -+ -+ err = vfs_statfs(&br->br_path, &kstfs); -+ if (!err) { -+ stfs->f_blocks = kstfs.f_blocks; -+ stfs->f_bavail = kstfs.f_bavail; -+ stfs->f_files = kstfs.f_files; -+ stfs->f_ffree = kstfs.f_ffree; -+ } -+ -+ return err; -+} -diff --git a/fs/aufs/branch.h b/fs/aufs/branch.h -new file mode 100644 -index 0000000..6ae006e ---- /dev/null -+++ b/fs/aufs/branch.h -@@ -0,0 +1,279 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * branch filesystems and xino for them -+ */ -+ -+#ifndef __AUFS_BRANCH_H__ -+#define __AUFS_BRANCH_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+#include "dynop.h" -+#include "rwsem.h" -+#include "super.h" -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* a xino file */ -+struct au_xino_file { -+ struct file *xi_file; -+ struct mutex xi_nondir_mtx; -+ -+ /* todo: make xino files an array to support huge inode number */ -+ -+#ifdef CONFIG_DEBUG_FS -+ struct dentry *xi_dbgaufs; -+#endif -+}; -+ -+/* File-based Hierarchical Storage Management */ -+struct au_br_fhsm { -+#ifdef CONFIG_AUFS_FHSM -+ struct mutex bf_lock; -+ unsigned long bf_jiffy; -+ struct aufs_stfs bf_stfs; -+ int bf_readable; -+#endif -+}; -+ -+/* members for writable branch only */ -+enum {AuBrWh_BASE, AuBrWh_PLINK, AuBrWh_ORPH, AuBrWh_Last}; -+struct au_wbr { -+ struct au_rwsem wbr_wh_rwsem; -+ struct dentry *wbr_wh[AuBrWh_Last]; -+ atomic_t wbr_wh_running; -+#define wbr_whbase wbr_wh[AuBrWh_BASE] /* whiteout base */ -+#define wbr_plink wbr_wh[AuBrWh_PLINK] /* pseudo-link dir */ -+#define wbr_orph wbr_wh[AuBrWh_ORPH] /* dir for orphans */ -+ -+ /* mfs mode */ -+ unsigned long long wbr_bytes; -+}; -+ -+/* ext2 has 3 types of operations at least, ext3 has 4 */ -+#define AuBrDynOp (AuDyLast * 4) -+ -+#ifdef CONFIG_AUFS_HFSNOTIFY -+/* support for asynchronous destruction */ -+struct au_br_hfsnotify { -+ struct fsnotify_group *hfsn_group; -+}; -+#endif -+ -+/* sysfs entries */ -+struct au_brsysfs { -+ char name[16]; -+ struct attribute attr; -+}; -+ -+enum { -+ AuBrSysfs_BR, -+ AuBrSysfs_BRID, -+ AuBrSysfs_Last -+}; -+ -+/* protected by superblock rwsem */ -+struct au_branch { -+ struct au_xino_file br_xino; -+ -+ aufs_bindex_t br_id; -+ -+ int br_perm; -+ struct path br_path; -+ spinlock_t br_dykey_lock; -+ struct au_dykey *br_dykey[AuBrDynOp]; -+ atomic_t br_count; -+ -+ struct au_wbr *br_wbr; -+ struct au_br_fhsm *br_fhsm; -+ -+ /* xino truncation */ -+ atomic_t br_xino_running; -+ -+#ifdef CONFIG_AUFS_HFSNOTIFY -+ struct au_br_hfsnotify *br_hfsn; -+#endif -+ -+#ifdef CONFIG_SYSFS -+ /* entries under sysfs per mount-point */ -+ struct au_brsysfs br_sysfs[AuBrSysfs_Last]; -+#endif -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline struct vfsmount *au_br_mnt(struct au_branch *br) -+{ -+ return br->br_path.mnt; -+} -+ -+static inline struct dentry *au_br_dentry(struct au_branch *br) -+{ -+ return br->br_path.dentry; -+} -+ -+static inline struct super_block *au_br_sb(struct au_branch *br) -+{ -+ return au_br_mnt(br)->mnt_sb; -+} -+ -+static inline int au_br_rdonly(struct au_branch *br) -+{ -+ return ((au_br_sb(br)->s_flags & MS_RDONLY) -+ || !au_br_writable(br->br_perm)) -+ ? -EROFS : 0; -+} -+ -+static inline int au_br_hnotifyable(int brperm __maybe_unused) -+{ -+#ifdef CONFIG_AUFS_HNOTIFY -+ return !(brperm & AuBrPerm_RR); -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_br_test_oflag(int oflag, struct au_branch *br) -+{ -+ int err, exec_flag; -+ -+ err = 0; -+ exec_flag = oflag & __FMODE_EXEC; -+ if (unlikely(exec_flag && (au_br_mnt(br)->mnt_flags & MNT_NOEXEC))) -+ err = -EACCES; -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* branch.c */ -+struct au_sbinfo; -+void au_br_free(struct au_sbinfo *sinfo); -+int au_br_index(struct super_block *sb, aufs_bindex_t br_id); -+struct au_opt_add; -+int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount); -+struct au_opt_del; -+int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount); -+long au_ibusy_ioctl(struct file *file, unsigned long arg); -+#ifdef CONFIG_COMPAT -+long au_ibusy_compat_ioctl(struct file *file, unsigned long arg); -+#endif -+struct au_opt_mod; -+int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount, -+ int *do_refresh); -+struct aufs_stfs; -+int au_br_stfs(struct au_branch *br, struct aufs_stfs *stfs); -+ -+/* xino.c */ -+static const loff_t au_loff_max = LLONG_MAX; -+ -+int au_xib_trunc(struct super_block *sb); -+ssize_t xino_fread(au_readf_t func, struct file *file, void *buf, size_t size, -+ loff_t *pos); -+ssize_t xino_fwrite(au_writef_t func, struct file *file, void *buf, size_t size, -+ loff_t *pos); -+struct file *au_xino_create2(struct file *base_file, struct file *copy_src); -+struct file *au_xino_create(struct super_block *sb, char *fname, int silent); -+ino_t au_xino_new_ino(struct super_block *sb); -+void au_xino_delete_inode(struct inode *inode, const int unlinked); -+int au_xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, -+ ino_t ino); -+int au_xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, -+ ino_t *ino); -+int au_xino_br(struct super_block *sb, struct au_branch *br, ino_t hino, -+ struct file *base_file, int do_test); -+int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex); -+ -+struct au_opt_xino; -+int au_xino_set(struct super_block *sb, struct au_opt_xino *xino, int remount); -+void au_xino_clr(struct super_block *sb); -+struct file *au_xino_def(struct super_block *sb); -+int au_xino_path(struct seq_file *seq, struct file *file); -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* Superblock to branch */ -+static inline -+aufs_bindex_t au_sbr_id(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ return au_sbr(sb, bindex)->br_id; -+} -+ -+static inline -+struct vfsmount *au_sbr_mnt(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ return au_br_mnt(au_sbr(sb, bindex)); -+} -+ -+static inline -+struct super_block *au_sbr_sb(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ return au_br_sb(au_sbr(sb, bindex)); -+} -+ -+static inline void au_sbr_put(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ atomic_dec(&au_sbr(sb, bindex)->br_count); -+} -+ -+static inline int au_sbr_perm(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ return au_sbr(sb, bindex)->br_perm; -+} -+ -+static inline int au_sbr_whable(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ return au_br_whable(au_sbr_perm(sb, bindex)); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * wbr_wh_read_lock, wbr_wh_write_lock -+ * wbr_wh_read_unlock, wbr_wh_write_unlock, wbr_wh_downgrade_lock -+ */ -+AuSimpleRwsemFuncs(wbr_wh, struct au_wbr *wbr, &wbr->wbr_wh_rwsem); -+ -+#define WbrWhMustNoWaiters(wbr) AuRwMustNoWaiters(&wbr->wbr_wh_rwsem) -+#define WbrWhMustAnyLock(wbr) AuRwMustAnyLock(&wbr->wbr_wh_rwsem) -+#define WbrWhMustWriteLock(wbr) AuRwMustWriteLock(&wbr->wbr_wh_rwsem) -+ -+/* ---------------------------------------------------------------------- */ -+ -+#ifdef CONFIG_AUFS_FHSM -+static inline void au_br_fhsm_init(struct au_br_fhsm *brfhsm) -+{ -+ mutex_init(&brfhsm->bf_lock); -+ brfhsm->bf_jiffy = 0; -+ brfhsm->bf_readable = 0; -+} -+ -+static inline void au_br_fhsm_fin(struct au_br_fhsm *brfhsm) -+{ -+ mutex_destroy(&brfhsm->bf_lock); -+} -+#else -+AuStubVoid(au_br_fhsm_init, struct au_br_fhsm *brfhsm) -+AuStubVoid(au_br_fhsm_fin, struct au_br_fhsm *brfhsm) -+#endif -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_BRANCH_H__ */ -diff --git a/fs/aufs/conf.mk b/fs/aufs/conf.mk -new file mode 100644 -index 0000000..0bbb2d3 ---- /dev/null -+++ b/fs/aufs/conf.mk -@@ -0,0 +1,38 @@ -+ -+AuConfStr = CONFIG_AUFS_FS=${CONFIG_AUFS_FS} -+ -+define AuConf -+ifdef ${1} -+AuConfStr += ${1}=${${1}} -+endif -+endef -+ -+AuConfAll = BRANCH_MAX_127 BRANCH_MAX_511 BRANCH_MAX_1023 BRANCH_MAX_32767 \ -+ SBILIST \ -+ HNOTIFY HFSNOTIFY \ -+ EXPORT INO_T_64 \ -+ XATTR \ -+ FHSM \ -+ RDU \ -+ SHWH \ -+ BR_RAMFS \ -+ BR_FUSE POLL \ -+ BR_HFSPLUS \ -+ BDEV_LOOP \ -+ DEBUG MAGIC_SYSRQ -+$(foreach i, ${AuConfAll}, \ -+ $(eval $(call AuConf,CONFIG_AUFS_${i}))) -+ -+AuConfName = ${obj}/conf.str -+${AuConfName}.tmp: FORCE -+ @echo ${AuConfStr} | tr ' ' '\n' | sed -e 's/^/"/' -e 's/$$/\\n"/' > $@ -+${AuConfName}: ${AuConfName}.tmp -+ @diff -q $< $@ > /dev/null 2>&1 || { \ -+ echo ' GEN ' $@; \ -+ cp -p $< $@; \ -+ } -+FORCE: -+clean-files += ${AuConfName} ${AuConfName}.tmp -+${obj}/sysfs.o: ${AuConfName} -+ -+-include ${srctree}/${src}/conf_priv.mk -diff --git a/fs/aufs/cpup.c b/fs/aufs/cpup.c -new file mode 100644 -index 0000000..9d8b767 ---- /dev/null -+++ b/fs/aufs/cpup.c -@@ -0,0 +1,1368 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * copy-up functions, see wbr_policy.c for copy-down -+ */ -+ -+#include -+#include -+#include -+#include "aufs.h" -+ -+void au_cpup_attr_flags(struct inode *dst, unsigned int iflags) -+{ -+ const unsigned int mask = S_DEAD | S_SWAPFILE | S_PRIVATE -+ | S_NOATIME | S_NOCMTIME | S_AUTOMOUNT; -+ -+ BUILD_BUG_ON(sizeof(iflags) != sizeof(dst->i_flags)); -+ -+ dst->i_flags |= iflags & ~mask; -+ if (au_test_fs_notime(dst->i_sb)) -+ dst->i_flags |= S_NOATIME | S_NOCMTIME; -+} -+ -+void au_cpup_attr_timesizes(struct inode *inode) -+{ -+ struct inode *h_inode; -+ -+ h_inode = au_h_iptr(inode, au_ibstart(inode)); -+ fsstack_copy_attr_times(inode, h_inode); -+ fsstack_copy_inode_size(inode, h_inode); -+} -+ -+void au_cpup_attr_nlink(struct inode *inode, int force) -+{ -+ struct inode *h_inode; -+ struct super_block *sb; -+ aufs_bindex_t bindex, bend; -+ -+ sb = inode->i_sb; -+ bindex = au_ibstart(inode); -+ h_inode = au_h_iptr(inode, bindex); -+ if (!force -+ && !S_ISDIR(h_inode->i_mode) -+ && au_opt_test(au_mntflags(sb), PLINK) -+ && au_plink_test(inode)) -+ return; -+ -+ /* -+ * 0 can happen in revalidating. -+ * h_inode->i_mutex may not be held here, but it is harmless since once -+ * i_nlink reaches 0, it will never become positive except O_TMPFILE -+ * case. -+ * todo: O_TMPFILE+linkat(AT_SYMLINK_FOLLOW) bypassing aufs may cause -+ * the incorrect link count. -+ */ -+ set_nlink(inode, h_inode->i_nlink); -+ -+ /* -+ * fewer nlink makes find(1) noisy, but larger nlink doesn't. -+ * it may includes whplink directory. -+ */ -+ if (S_ISDIR(h_inode->i_mode)) { -+ bend = au_ibend(inode); -+ for (bindex++; bindex <= bend; bindex++) { -+ h_inode = au_h_iptr(inode, bindex); -+ if (h_inode) -+ au_add_nlink(inode, h_inode); -+ } -+ } -+} -+ -+void au_cpup_attr_changeable(struct inode *inode) -+{ -+ struct inode *h_inode; -+ -+ h_inode = au_h_iptr(inode, au_ibstart(inode)); -+ inode->i_mode = h_inode->i_mode; -+ inode->i_uid = h_inode->i_uid; -+ inode->i_gid = h_inode->i_gid; -+ au_cpup_attr_timesizes(inode); -+ au_cpup_attr_flags(inode, h_inode->i_flags); -+} -+ -+void au_cpup_igen(struct inode *inode, struct inode *h_inode) -+{ -+ struct au_iinfo *iinfo = au_ii(inode); -+ -+ IiMustWriteLock(inode); -+ -+ iinfo->ii_higen = h_inode->i_generation; -+ iinfo->ii_hsb1 = h_inode->i_sb; -+} -+ -+void au_cpup_attr_all(struct inode *inode, int force) -+{ -+ struct inode *h_inode; -+ -+ h_inode = au_h_iptr(inode, au_ibstart(inode)); -+ au_cpup_attr_changeable(inode); -+ if (inode->i_nlink > 0) -+ au_cpup_attr_nlink(inode, force); -+ inode->i_rdev = h_inode->i_rdev; -+ inode->i_blkbits = h_inode->i_blkbits; -+ au_cpup_igen(inode, h_inode); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* Note: dt_dentry and dt_h_dentry are not dget/dput-ed */ -+ -+/* keep the timestamps of the parent dir when cpup */ -+void au_dtime_store(struct au_dtime *dt, struct dentry *dentry, -+ struct path *h_path) -+{ -+ struct inode *h_inode; -+ -+ dt->dt_dentry = dentry; -+ dt->dt_h_path = *h_path; -+ h_inode = h_path->dentry->d_inode; -+ dt->dt_atime = h_inode->i_atime; -+ dt->dt_mtime = h_inode->i_mtime; -+ /* smp_mb(); */ -+} -+ -+void au_dtime_revert(struct au_dtime *dt) -+{ -+ struct iattr attr; -+ int err; -+ -+ attr.ia_atime = dt->dt_atime; -+ attr.ia_mtime = dt->dt_mtime; -+ attr.ia_valid = ATTR_FORCE | ATTR_MTIME | ATTR_MTIME_SET -+ | ATTR_ATIME | ATTR_ATIME_SET; -+ -+ /* no delegation since this is a directory */ -+ err = vfsub_notify_change(&dt->dt_h_path, &attr, /*delegated*/NULL); -+ if (unlikely(err)) -+ pr_warn("restoring timestamps failed(%d). ignored\n", err); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* internal use only */ -+struct au_cpup_reg_attr { -+ int valid; -+ struct kstat st; -+ unsigned int iflags; /* inode->i_flags */ -+}; -+ -+static noinline_for_stack -+int cpup_iattr(struct dentry *dst, aufs_bindex_t bindex, struct dentry *h_src, -+ struct au_cpup_reg_attr *h_src_attr) -+{ -+ int err, sbits, icex; -+ unsigned int mnt_flags; -+ unsigned char verbose; -+ struct iattr ia; -+ struct path h_path; -+ struct inode *h_isrc, *h_idst; -+ struct kstat *h_st; -+ struct au_branch *br; -+ -+ h_path.dentry = au_h_dptr(dst, bindex); -+ h_idst = h_path.dentry->d_inode; -+ br = au_sbr(dst->d_sb, bindex); -+ h_path.mnt = au_br_mnt(br); -+ h_isrc = h_src->d_inode; -+ ia.ia_valid = ATTR_FORCE | ATTR_UID | ATTR_GID -+ | ATTR_ATIME | ATTR_MTIME -+ | ATTR_ATIME_SET | ATTR_MTIME_SET; -+ if (h_src_attr && h_src_attr->valid) { -+ h_st = &h_src_attr->st; -+ ia.ia_uid = h_st->uid; -+ ia.ia_gid = h_st->gid; -+ ia.ia_atime = h_st->atime; -+ ia.ia_mtime = h_st->mtime; -+ if (h_idst->i_mode != h_st->mode -+ && !S_ISLNK(h_idst->i_mode)) { -+ ia.ia_valid |= ATTR_MODE; -+ ia.ia_mode = h_st->mode; -+ } -+ sbits = !!(h_st->mode & (S_ISUID | S_ISGID)); -+ au_cpup_attr_flags(h_idst, h_src_attr->iflags); -+ } else { -+ ia.ia_uid = h_isrc->i_uid; -+ ia.ia_gid = h_isrc->i_gid; -+ ia.ia_atime = h_isrc->i_atime; -+ ia.ia_mtime = h_isrc->i_mtime; -+ if (h_idst->i_mode != h_isrc->i_mode -+ && !S_ISLNK(h_idst->i_mode)) { -+ ia.ia_valid |= ATTR_MODE; -+ ia.ia_mode = h_isrc->i_mode; -+ } -+ sbits = !!(h_isrc->i_mode & (S_ISUID | S_ISGID)); -+ au_cpup_attr_flags(h_idst, h_isrc->i_flags); -+ } -+ /* no delegation since it is just created */ -+ err = vfsub_notify_change(&h_path, &ia, /*delegated*/NULL); -+ -+ /* is this nfs only? */ -+ if (!err && sbits && au_test_nfs(h_path.dentry->d_sb)) { -+ ia.ia_valid = ATTR_FORCE | ATTR_MODE; -+ ia.ia_mode = h_isrc->i_mode; -+ err = vfsub_notify_change(&h_path, &ia, /*delegated*/NULL); -+ } -+ -+ icex = br->br_perm & AuBrAttr_ICEX; -+ if (!err) { -+ mnt_flags = au_mntflags(dst->d_sb); -+ verbose = !!au_opt_test(mnt_flags, VERBOSE); -+ err = au_cpup_xattr(h_path.dentry, h_src, icex, verbose); -+ } -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_do_copy_file(struct file *dst, struct file *src, loff_t len, -+ char *buf, unsigned long blksize) -+{ -+ int err; -+ size_t sz, rbytes, wbytes; -+ unsigned char all_zero; -+ char *p, *zp; -+ struct mutex *h_mtx; -+ /* reduce stack usage */ -+ struct iattr *ia; -+ -+ zp = page_address(ZERO_PAGE(0)); -+ if (unlikely(!zp)) -+ return -ENOMEM; /* possible? */ -+ -+ err = 0; -+ all_zero = 0; -+ while (len) { -+ AuDbg("len %lld\n", len); -+ sz = blksize; -+ if (len < blksize) -+ sz = len; -+ -+ rbytes = 0; -+ /* todo: signal_pending? */ -+ while (!rbytes || err == -EAGAIN || err == -EINTR) { -+ rbytes = vfsub_read_k(src, buf, sz, &src->f_pos); -+ err = rbytes; -+ } -+ if (unlikely(err < 0)) -+ break; -+ -+ all_zero = 0; -+ if (len >= rbytes && rbytes == blksize) -+ all_zero = !memcmp(buf, zp, rbytes); -+ if (!all_zero) { -+ wbytes = rbytes; -+ p = buf; -+ while (wbytes) { -+ size_t b; -+ -+ b = vfsub_write_k(dst, p, wbytes, &dst->f_pos); -+ err = b; -+ /* todo: signal_pending? */ -+ if (unlikely(err == -EAGAIN || err == -EINTR)) -+ continue; -+ if (unlikely(err < 0)) -+ break; -+ wbytes -= b; -+ p += b; -+ } -+ if (unlikely(err < 0)) -+ break; -+ } else { -+ loff_t res; -+ -+ AuLabel(hole); -+ res = vfsub_llseek(dst, rbytes, SEEK_CUR); -+ err = res; -+ if (unlikely(res < 0)) -+ break; -+ } -+ len -= rbytes; -+ err = 0; -+ } -+ -+ /* the last block may be a hole */ -+ if (!err && all_zero) { -+ AuLabel(last hole); -+ -+ err = 1; -+ if (au_test_nfs(dst->f_dentry->d_sb)) { -+ /* nfs requires this step to make last hole */ -+ /* is this only nfs? */ -+ do { -+ /* todo: signal_pending? */ -+ err = vfsub_write_k(dst, "\0", 1, &dst->f_pos); -+ } while (err == -EAGAIN || err == -EINTR); -+ if (err == 1) -+ dst->f_pos--; -+ } -+ -+ if (err == 1) { -+ ia = (void *)buf; -+ ia->ia_size = dst->f_pos; -+ ia->ia_valid = ATTR_SIZE | ATTR_FILE; -+ ia->ia_file = dst; -+ h_mtx = &file_inode(dst)->i_mutex; -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD2); -+ /* no delegation since it is just created */ -+ err = vfsub_notify_change(&dst->f_path, ia, -+ /*delegated*/NULL); -+ mutex_unlock(h_mtx); -+ } -+ } -+ -+ return err; -+} -+ -+int au_copy_file(struct file *dst, struct file *src, loff_t len) -+{ -+ int err; -+ unsigned long blksize; -+ unsigned char do_kfree; -+ char *buf; -+ -+ err = -ENOMEM; -+ blksize = dst->f_dentry->d_sb->s_blocksize; -+ if (!blksize || PAGE_SIZE < blksize) -+ blksize = PAGE_SIZE; -+ AuDbg("blksize %lu\n", blksize); -+ do_kfree = (blksize != PAGE_SIZE && blksize >= sizeof(struct iattr *)); -+ if (do_kfree) -+ buf = kmalloc(blksize, GFP_NOFS); -+ else -+ buf = (void *)__get_free_page(GFP_NOFS); -+ if (unlikely(!buf)) -+ goto out; -+ -+ if (len > (1 << 22)) -+ AuDbg("copying a large file %lld\n", (long long)len); -+ -+ src->f_pos = 0; -+ dst->f_pos = 0; -+ err = au_do_copy_file(dst, src, len, buf, blksize); -+ if (do_kfree) -+ kfree(buf); -+ else -+ free_page((unsigned long)buf); -+ -+out: -+ return err; -+} -+ -+/* -+ * to support a sparse file which is opened with O_APPEND, -+ * we need to close the file. -+ */ -+static int au_cp_regular(struct au_cp_generic *cpg) -+{ -+ int err, i; -+ enum { SRC, DST }; -+ struct { -+ aufs_bindex_t bindex; -+ unsigned int flags; -+ struct dentry *dentry; -+ int force_wr; -+ struct file *file; -+ void *label; -+ } *f, file[] = { -+ { -+ .bindex = cpg->bsrc, -+ .flags = O_RDONLY | O_NOATIME | O_LARGEFILE, -+ .label = &&out -+ }, -+ { -+ .bindex = cpg->bdst, -+ .flags = O_WRONLY | O_NOATIME | O_LARGEFILE, -+ .force_wr = !!au_ftest_cpup(cpg->flags, RWDST), -+ .label = &&out_src -+ } -+ }; -+ struct super_block *sb; -+ struct task_struct *tsk = current; -+ -+ /* bsrc branch can be ro/rw. */ -+ sb = cpg->dentry->d_sb; -+ f = file; -+ for (i = 0; i < 2; i++, f++) { -+ f->dentry = au_h_dptr(cpg->dentry, f->bindex); -+ f->file = au_h_open(cpg->dentry, f->bindex, f->flags, -+ /*file*/NULL, f->force_wr); -+ err = PTR_ERR(f->file); -+ if (IS_ERR(f->file)) -+ goto *f->label; -+ } -+ -+ /* try stopping to update while we copyup */ -+ IMustLock(file[SRC].dentry->d_inode); -+ err = au_copy_file(file[DST].file, file[SRC].file, cpg->len); -+ -+ /* i wonder if we had O_NO_DELAY_FPUT flag */ -+ if (tsk->flags & PF_KTHREAD) -+ __fput_sync(file[DST].file); -+ else { -+ WARN(1, "%pD\nPlease report this warning to aufs-users ML", -+ file[DST].file); -+ fput(file[DST].file); -+ /* -+ * too bad. -+ * we have to call both since we don't know which place the file -+ * was added to. -+ */ -+ task_work_run(); -+ flush_delayed_fput(); -+ } -+ au_sbr_put(sb, file[DST].bindex); -+ -+out_src: -+ fput(file[SRC].file); -+ au_sbr_put(sb, file[SRC].bindex); -+out: -+ return err; -+} -+ -+static int au_do_cpup_regular(struct au_cp_generic *cpg, -+ struct au_cpup_reg_attr *h_src_attr) -+{ -+ int err, rerr; -+ loff_t l; -+ struct path h_path; -+ struct inode *h_src_inode, *h_dst_inode; -+ -+ err = 0; -+ h_src_inode = au_h_iptr(cpg->dentry->d_inode, cpg->bsrc); -+ l = i_size_read(h_src_inode); -+ if (cpg->len == -1 || l < cpg->len) -+ cpg->len = l; -+ if (cpg->len) { -+ /* try stopping to update while we are referencing */ -+ mutex_lock_nested(&h_src_inode->i_mutex, AuLsc_I_CHILD); -+ au_pin_hdir_unlock(cpg->pin); -+ -+ h_path.dentry = au_h_dptr(cpg->dentry, cpg->bsrc); -+ h_path.mnt = au_sbr_mnt(cpg->dentry->d_sb, cpg->bsrc); -+ h_src_attr->iflags = h_src_inode->i_flags; -+ err = vfs_getattr(&h_path, &h_src_attr->st); -+ if (unlikely(err)) { -+ mutex_unlock(&h_src_inode->i_mutex); -+ goto out; -+ } -+ h_src_attr->valid = 1; -+ err = au_cp_regular(cpg); -+ mutex_unlock(&h_src_inode->i_mutex); -+ rerr = au_pin_hdir_relock(cpg->pin); -+ if (!err && rerr) -+ err = rerr; -+ } -+ if (!err && (h_src_inode->i_state & I_LINKABLE)) { -+ h_path.dentry = au_h_dptr(cpg->dentry, cpg->bdst); -+ h_dst_inode = h_path.dentry->d_inode; -+ spin_lock(&h_dst_inode->i_lock); -+ h_dst_inode->i_state |= I_LINKABLE; -+ spin_unlock(&h_dst_inode->i_lock); -+ } -+ -+out: -+ return err; -+} -+ -+static int au_do_cpup_symlink(struct path *h_path, struct dentry *h_src, -+ struct inode *h_dir) -+{ -+ int err, symlen; -+ mm_segment_t old_fs; -+ union { -+ char *k; -+ char __user *u; -+ } sym; -+ -+ err = -ENOSYS; -+ if (unlikely(!h_src->d_inode->i_op->readlink)) -+ goto out; -+ -+ err = -ENOMEM; -+ sym.k = (void *)__get_free_page(GFP_NOFS); -+ if (unlikely(!sym.k)) -+ goto out; -+ -+ /* unnecessary to support mmap_sem since symlink is not mmap-able */ -+ old_fs = get_fs(); -+ set_fs(KERNEL_DS); -+ symlen = h_src->d_inode->i_op->readlink(h_src, sym.u, PATH_MAX); -+ err = symlen; -+ set_fs(old_fs); -+ -+ if (symlen > 0) { -+ sym.k[symlen] = 0; -+ err = vfsub_symlink(h_dir, h_path, sym.k); -+ } -+ free_page((unsigned long)sym.k); -+ -+out: -+ return err; -+} -+ -+/* -+ * regardless 'acl' option, reset all ACL. -+ * All ACL will be copied up later from the original entry on the lower branch. -+ */ -+static int au_reset_acl(struct inode *h_dir, struct path *h_path, umode_t mode) -+{ -+ int err; -+ struct dentry *h_dentry; -+ struct inode *h_inode; -+ -+ h_dentry = h_path->dentry; -+ h_inode = h_dentry->d_inode; -+ /* forget_all_cached_acls(h_inode)); */ -+ err = vfsub_removexattr(h_dentry, XATTR_NAME_POSIX_ACL_ACCESS); -+ AuTraceErr(err); -+ if (err == -EOPNOTSUPP) -+ err = 0; -+ if (!err) -+ err = vfsub_acl_chmod(h_inode, mode); -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+static int au_do_cpup_dir(struct au_cp_generic *cpg, struct dentry *dst_parent, -+ struct inode *h_dir, struct path *h_path) -+{ -+ int err; -+ struct inode *dir; -+ -+ err = vfsub_removexattr(h_path->dentry, XATTR_NAME_POSIX_ACL_DEFAULT); -+ AuTraceErr(err); -+ if (err == -EOPNOTSUPP) -+ err = 0; -+ if (unlikely(err)) -+ goto out; -+ -+ /* -+ * strange behaviour from the users view, -+ * particularry setattr case -+ */ -+ dir = dst_parent->d_inode; -+ if (au_ibstart(dir) == cpg->bdst) -+ au_cpup_attr_nlink(dir, /*force*/1); -+ au_cpup_attr_nlink(cpg->dentry->d_inode, /*force*/1); -+ -+out: -+ return err; -+} -+ -+static noinline_for_stack -+int cpup_entry(struct au_cp_generic *cpg, struct dentry *dst_parent, -+ struct au_cpup_reg_attr *h_src_attr) -+{ -+ int err; -+ umode_t mode; -+ unsigned int mnt_flags; -+ unsigned char isdir, isreg, force; -+ const unsigned char do_dt = !!au_ftest_cpup(cpg->flags, DTIME); -+ struct au_dtime dt; -+ struct path h_path; -+ struct dentry *h_src, *h_dst, *h_parent; -+ struct inode *h_inode, *h_dir; -+ struct super_block *sb; -+ -+ /* bsrc branch can be ro/rw. */ -+ h_src = au_h_dptr(cpg->dentry, cpg->bsrc); -+ h_inode = h_src->d_inode; -+ AuDebugOn(h_inode != au_h_iptr(cpg->dentry->d_inode, cpg->bsrc)); -+ -+ /* try stopping to be referenced while we are creating */ -+ h_dst = au_h_dptr(cpg->dentry, cpg->bdst); -+ if (au_ftest_cpup(cpg->flags, RENAME)) -+ AuDebugOn(strncmp(h_dst->d_name.name, AUFS_WH_PFX, -+ AUFS_WH_PFX_LEN)); -+ h_parent = h_dst->d_parent; /* dir inode is locked */ -+ h_dir = h_parent->d_inode; -+ IMustLock(h_dir); -+ AuDebugOn(h_parent != h_dst->d_parent); -+ -+ sb = cpg->dentry->d_sb; -+ h_path.mnt = au_sbr_mnt(sb, cpg->bdst); -+ if (do_dt) { -+ h_path.dentry = h_parent; -+ au_dtime_store(&dt, dst_parent, &h_path); -+ } -+ h_path.dentry = h_dst; -+ -+ isreg = 0; -+ isdir = 0; -+ mode = h_inode->i_mode; -+ switch (mode & S_IFMT) { -+ case S_IFREG: -+ isreg = 1; -+ err = vfsub_create(h_dir, &h_path, S_IRUSR | S_IWUSR, -+ /*want_excl*/true); -+ if (!err) -+ err = au_do_cpup_regular(cpg, h_src_attr); -+ break; -+ case S_IFDIR: -+ isdir = 1; -+ err = vfsub_mkdir(h_dir, &h_path, mode); -+ if (!err) -+ err = au_do_cpup_dir(cpg, dst_parent, h_dir, &h_path); -+ break; -+ case S_IFLNK: -+ err = au_do_cpup_symlink(&h_path, h_src, h_dir); -+ break; -+ case S_IFCHR: -+ case S_IFBLK: -+ AuDebugOn(!capable(CAP_MKNOD)); -+ /*FALLTHROUGH*/ -+ case S_IFIFO: -+ case S_IFSOCK: -+ err = vfsub_mknod(h_dir, &h_path, mode, h_inode->i_rdev); -+ break; -+ default: -+ AuIOErr("Unknown inode type 0%o\n", mode); -+ err = -EIO; -+ } -+ if (!err) -+ err = au_reset_acl(h_dir, &h_path, mode); -+ -+ mnt_flags = au_mntflags(sb); -+ if (!au_opt_test(mnt_flags, UDBA_NONE) -+ && !isdir -+ && au_opt_test(mnt_flags, XINO) -+ && (h_inode->i_nlink == 1 -+ || (h_inode->i_state & I_LINKABLE)) -+ /* todo: unnecessary? */ -+ /* && cpg->dentry->d_inode->i_nlink == 1 */ -+ && cpg->bdst < cpg->bsrc -+ && !au_ftest_cpup(cpg->flags, KEEPLINO)) -+ au_xino_write(sb, cpg->bsrc, h_inode->i_ino, /*ino*/0); -+ /* ignore this error */ -+ -+ if (!err) { -+ force = 0; -+ if (isreg) { -+ force = !!cpg->len; -+ if (cpg->len == -1) -+ force = !!i_size_read(h_inode); -+ } -+ au_fhsm_wrote(sb, cpg->bdst, force); -+ } -+ -+ if (do_dt) -+ au_dtime_revert(&dt); -+ return err; -+} -+ -+static int au_do_ren_after_cpup(struct au_cp_generic *cpg, struct path *h_path) -+{ -+ int err; -+ struct dentry *dentry, *h_dentry, *h_parent, *parent; -+ struct inode *h_dir; -+ aufs_bindex_t bdst; -+ -+ dentry = cpg->dentry; -+ bdst = cpg->bdst; -+ h_dentry = au_h_dptr(dentry, bdst); -+ if (!au_ftest_cpup(cpg->flags, OVERWRITE)) { -+ dget(h_dentry); -+ au_set_h_dptr(dentry, bdst, NULL); -+ err = au_lkup_neg(dentry, bdst, /*wh*/0); -+ if (!err) -+ h_path->dentry = dget(au_h_dptr(dentry, bdst)); -+ au_set_h_dptr(dentry, bdst, h_dentry); -+ } else { -+ err = 0; -+ parent = dget_parent(dentry); -+ h_parent = au_h_dptr(parent, bdst); -+ dput(parent); -+ h_path->dentry = vfsub_lkup_one(&dentry->d_name, h_parent); -+ if (IS_ERR(h_path->dentry)) -+ err = PTR_ERR(h_path->dentry); -+ } -+ if (unlikely(err)) -+ goto out; -+ -+ h_parent = h_dentry->d_parent; /* dir inode is locked */ -+ h_dir = h_parent->d_inode; -+ IMustLock(h_dir); -+ AuDbg("%pd %pd\n", h_dentry, h_path->dentry); -+ /* no delegation since it is just created */ -+ err = vfsub_rename(h_dir, h_dentry, h_dir, h_path, /*delegated*/NULL); -+ dput(h_path->dentry); -+ -+out: -+ return err; -+} -+ -+/* -+ * copyup the @dentry from @bsrc to @bdst. -+ * the caller must set the both of lower dentries. -+ * @len is for truncating when it is -1 copyup the entire file. -+ * in link/rename cases, @dst_parent may be different from the real one. -+ * basic->bsrc can be larger than basic->bdst. -+ */ -+static int au_cpup_single(struct au_cp_generic *cpg, struct dentry *dst_parent) -+{ -+ int err, rerr; -+ aufs_bindex_t old_ibstart; -+ unsigned char isdir, plink; -+ struct dentry *h_src, *h_dst, *h_parent; -+ struct inode *dst_inode, *h_dir, *inode, *delegated; -+ struct super_block *sb; -+ struct au_branch *br; -+ /* to reuduce stack size */ -+ struct { -+ struct au_dtime dt; -+ struct path h_path; -+ struct au_cpup_reg_attr h_src_attr; -+ } *a; -+ -+ err = -ENOMEM; -+ a = kmalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ a->h_src_attr.valid = 0; -+ -+ sb = cpg->dentry->d_sb; -+ br = au_sbr(sb, cpg->bdst); -+ a->h_path.mnt = au_br_mnt(br); -+ h_dst = au_h_dptr(cpg->dentry, cpg->bdst); -+ h_parent = h_dst->d_parent; /* dir inode is locked */ -+ h_dir = h_parent->d_inode; -+ IMustLock(h_dir); -+ -+ h_src = au_h_dptr(cpg->dentry, cpg->bsrc); -+ inode = cpg->dentry->d_inode; -+ -+ if (!dst_parent) -+ dst_parent = dget_parent(cpg->dentry); -+ else -+ dget(dst_parent); -+ -+ plink = !!au_opt_test(au_mntflags(sb), PLINK); -+ dst_inode = au_h_iptr(inode, cpg->bdst); -+ if (dst_inode) { -+ if (unlikely(!plink)) { -+ err = -EIO; -+ AuIOErr("hi%lu(i%lu) exists on b%d " -+ "but plink is disabled\n", -+ dst_inode->i_ino, inode->i_ino, cpg->bdst); -+ goto out_parent; -+ } -+ -+ if (dst_inode->i_nlink) { -+ const int do_dt = au_ftest_cpup(cpg->flags, DTIME); -+ -+ h_src = au_plink_lkup(inode, cpg->bdst); -+ err = PTR_ERR(h_src); -+ if (IS_ERR(h_src)) -+ goto out_parent; -+ if (unlikely(!h_src->d_inode)) { -+ err = -EIO; -+ AuIOErr("i%lu exists on b%d " -+ "but not pseudo-linked\n", -+ inode->i_ino, cpg->bdst); -+ dput(h_src); -+ goto out_parent; -+ } -+ -+ if (do_dt) { -+ a->h_path.dentry = h_parent; -+ au_dtime_store(&a->dt, dst_parent, &a->h_path); -+ } -+ -+ a->h_path.dentry = h_dst; -+ delegated = NULL; -+ err = vfsub_link(h_src, h_dir, &a->h_path, &delegated); -+ if (!err && au_ftest_cpup(cpg->flags, RENAME)) -+ err = au_do_ren_after_cpup(cpg, &a->h_path); -+ if (do_dt) -+ au_dtime_revert(&a->dt); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal link\n"); -+ iput(delegated); -+ } -+ dput(h_src); -+ goto out_parent; -+ } else -+ /* todo: cpup_wh_file? */ -+ /* udba work */ -+ au_update_ibrange(inode, /*do_put_zero*/1); -+ } -+ -+ isdir = S_ISDIR(inode->i_mode); -+ old_ibstart = au_ibstart(inode); -+ err = cpup_entry(cpg, dst_parent, &a->h_src_attr); -+ if (unlikely(err)) -+ goto out_rev; -+ dst_inode = h_dst->d_inode; -+ mutex_lock_nested(&dst_inode->i_mutex, AuLsc_I_CHILD2); -+ /* todo: necessary? */ -+ /* au_pin_hdir_unlock(cpg->pin); */ -+ -+ err = cpup_iattr(cpg->dentry, cpg->bdst, h_src, &a->h_src_attr); -+ if (unlikely(err)) { -+ /* todo: necessary? */ -+ /* au_pin_hdir_relock(cpg->pin); */ /* ignore an error */ -+ mutex_unlock(&dst_inode->i_mutex); -+ goto out_rev; -+ } -+ -+ if (cpg->bdst < old_ibstart) { -+ if (S_ISREG(inode->i_mode)) { -+ err = au_dy_iaop(inode, cpg->bdst, dst_inode); -+ if (unlikely(err)) { -+ /* ignore an error */ -+ /* au_pin_hdir_relock(cpg->pin); */ -+ mutex_unlock(&dst_inode->i_mutex); -+ goto out_rev; -+ } -+ } -+ au_set_ibstart(inode, cpg->bdst); -+ } else -+ au_set_ibend(inode, cpg->bdst); -+ au_set_h_iptr(inode, cpg->bdst, au_igrab(dst_inode), -+ au_hi_flags(inode, isdir)); -+ -+ /* todo: necessary? */ -+ /* err = au_pin_hdir_relock(cpg->pin); */ -+ mutex_unlock(&dst_inode->i_mutex); -+ if (unlikely(err)) -+ goto out_rev; -+ -+ if (!isdir -+ && (h_src->d_inode->i_nlink > 1 -+ || h_src->d_inode->i_state & I_LINKABLE) -+ && plink) -+ au_plink_append(inode, cpg->bdst, h_dst); -+ -+ if (au_ftest_cpup(cpg->flags, RENAME)) { -+ a->h_path.dentry = h_dst; -+ err = au_do_ren_after_cpup(cpg, &a->h_path); -+ } -+ if (!err) -+ goto out_parent; /* success */ -+ -+ /* revert */ -+out_rev: -+ a->h_path.dentry = h_parent; -+ au_dtime_store(&a->dt, dst_parent, &a->h_path); -+ a->h_path.dentry = h_dst; -+ rerr = 0; -+ if (h_dst->d_inode) { -+ if (!isdir) { -+ /* no delegation since it is just created */ -+ rerr = vfsub_unlink(h_dir, &a->h_path, -+ /*delegated*/NULL, /*force*/0); -+ } else -+ rerr = vfsub_rmdir(h_dir, &a->h_path); -+ } -+ au_dtime_revert(&a->dt); -+ if (rerr) { -+ AuIOErr("failed removing broken entry(%d, %d)\n", err, rerr); -+ err = -EIO; -+ } -+out_parent: -+ dput(dst_parent); -+ kfree(a); -+out: -+ return err; -+} -+ -+#if 0 /* reserved */ -+struct au_cpup_single_args { -+ int *errp; -+ struct au_cp_generic *cpg; -+ struct dentry *dst_parent; -+}; -+ -+static void au_call_cpup_single(void *args) -+{ -+ struct au_cpup_single_args *a = args; -+ -+ au_pin_hdir_acquire_nest(a->cpg->pin); -+ *a->errp = au_cpup_single(a->cpg, a->dst_parent); -+ au_pin_hdir_release(a->cpg->pin); -+} -+#endif -+ -+/* -+ * prevent SIGXFSZ in copy-up. -+ * testing CAP_MKNOD is for generic fs, -+ * but CAP_FSETID is for xfs only, currently. -+ */ -+static int au_cpup_sio_test(struct au_pin *pin, umode_t mode) -+{ -+ int do_sio; -+ struct super_block *sb; -+ struct inode *h_dir; -+ -+ do_sio = 0; -+ sb = au_pinned_parent(pin)->d_sb; -+ if (!au_wkq_test() -+ && (!au_sbi(sb)->si_plink_maint_pid -+ || au_plink_maint(sb, AuLock_NOPLM))) { -+ switch (mode & S_IFMT) { -+ case S_IFREG: -+ /* no condition about RLIMIT_FSIZE and the file size */ -+ do_sio = 1; -+ break; -+ case S_IFCHR: -+ case S_IFBLK: -+ do_sio = !capable(CAP_MKNOD); -+ break; -+ } -+ if (!do_sio) -+ do_sio = ((mode & (S_ISUID | S_ISGID)) -+ && !capable(CAP_FSETID)); -+ /* this workaround may be removed in the future */ -+ if (!do_sio) { -+ h_dir = au_pinned_h_dir(pin); -+ do_sio = h_dir->i_mode & S_ISVTX; -+ } -+ } -+ -+ return do_sio; -+} -+ -+#if 0 /* reserved */ -+int au_sio_cpup_single(struct au_cp_generic *cpg, struct dentry *dst_parent) -+{ -+ int err, wkq_err; -+ struct dentry *h_dentry; -+ -+ h_dentry = au_h_dptr(cpg->dentry, cpg->bsrc); -+ if (!au_cpup_sio_test(pin, h_dentry->d_inode->i_mode)) -+ err = au_cpup_single(cpg, dst_parent); -+ else { -+ struct au_cpup_single_args args = { -+ .errp = &err, -+ .cpg = cpg, -+ .dst_parent = dst_parent -+ }; -+ wkq_err = au_wkq_wait(au_call_cpup_single, &args); -+ if (unlikely(wkq_err)) -+ err = wkq_err; -+ } -+ -+ return err; -+} -+#endif -+ -+/* -+ * copyup the @dentry from the first active lower branch to @bdst, -+ * using au_cpup_single(). -+ */ -+static int au_cpup_simple(struct au_cp_generic *cpg) -+{ -+ int err; -+ unsigned int flags_orig; -+ struct dentry *dentry; -+ -+ AuDebugOn(cpg->bsrc < 0); -+ -+ dentry = cpg->dentry; -+ DiMustWriteLock(dentry); -+ -+ err = au_lkup_neg(dentry, cpg->bdst, /*wh*/1); -+ if (!err) { -+ flags_orig = cpg->flags; -+ au_fset_cpup(cpg->flags, RENAME); -+ err = au_cpup_single(cpg, NULL); -+ cpg->flags = flags_orig; -+ if (!err) -+ return 0; /* success */ -+ -+ /* revert */ -+ au_set_h_dptr(dentry, cpg->bdst, NULL); -+ au_set_dbstart(dentry, cpg->bsrc); -+ } -+ -+ return err; -+} -+ -+struct au_cpup_simple_args { -+ int *errp; -+ struct au_cp_generic *cpg; -+}; -+ -+static void au_call_cpup_simple(void *args) -+{ -+ struct au_cpup_simple_args *a = args; -+ -+ au_pin_hdir_acquire_nest(a->cpg->pin); -+ *a->errp = au_cpup_simple(a->cpg); -+ au_pin_hdir_release(a->cpg->pin); -+} -+ -+static int au_do_sio_cpup_simple(struct au_cp_generic *cpg) -+{ -+ int err, wkq_err; -+ struct dentry *dentry, *parent; -+ struct file *h_file; -+ struct inode *h_dir; -+ -+ dentry = cpg->dentry; -+ h_file = NULL; -+ if (au_ftest_cpup(cpg->flags, HOPEN)) { -+ AuDebugOn(cpg->bsrc < 0); -+ h_file = au_h_open_pre(dentry, cpg->bsrc, /*force_wr*/0); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ } -+ -+ parent = dget_parent(dentry); -+ h_dir = au_h_iptr(parent->d_inode, cpg->bdst); -+ if (!au_test_h_perm_sio(h_dir, MAY_EXEC | MAY_WRITE) -+ && !au_cpup_sio_test(cpg->pin, dentry->d_inode->i_mode)) -+ err = au_cpup_simple(cpg); -+ else { -+ struct au_cpup_simple_args args = { -+ .errp = &err, -+ .cpg = cpg -+ }; -+ wkq_err = au_wkq_wait(au_call_cpup_simple, &args); -+ if (unlikely(wkq_err)) -+ err = wkq_err; -+ } -+ -+ dput(parent); -+ if (h_file) -+ au_h_open_post(dentry, cpg->bsrc, h_file); -+ -+out: -+ return err; -+} -+ -+int au_sio_cpup_simple(struct au_cp_generic *cpg) -+{ -+ aufs_bindex_t bsrc, bend; -+ struct dentry *dentry, *h_dentry; -+ -+ if (cpg->bsrc < 0) { -+ dentry = cpg->dentry; -+ bend = au_dbend(dentry); -+ for (bsrc = cpg->bdst + 1; bsrc <= bend; bsrc++) { -+ h_dentry = au_h_dptr(dentry, bsrc); -+ if (h_dentry) { -+ AuDebugOn(!h_dentry->d_inode); -+ break; -+ } -+ } -+ AuDebugOn(bsrc > bend); -+ cpg->bsrc = bsrc; -+ } -+ AuDebugOn(cpg->bsrc <= cpg->bdst); -+ return au_do_sio_cpup_simple(cpg); -+} -+ -+int au_sio_cpdown_simple(struct au_cp_generic *cpg) -+{ -+ AuDebugOn(cpg->bdst <= cpg->bsrc); -+ return au_do_sio_cpup_simple(cpg); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * copyup the deleted file for writing. -+ */ -+static int au_do_cpup_wh(struct au_cp_generic *cpg, struct dentry *wh_dentry, -+ struct file *file) -+{ -+ int err; -+ unsigned int flags_orig; -+ aufs_bindex_t bsrc_orig; -+ struct dentry *h_d_dst, *h_d_start; -+ struct au_dinfo *dinfo; -+ struct au_hdentry *hdp; -+ -+ dinfo = au_di(cpg->dentry); -+ AuRwMustWriteLock(&dinfo->di_rwsem); -+ -+ bsrc_orig = cpg->bsrc; -+ cpg->bsrc = dinfo->di_bstart; -+ hdp = dinfo->di_hdentry; -+ h_d_dst = hdp[0 + cpg->bdst].hd_dentry; -+ dinfo->di_bstart = cpg->bdst; -+ hdp[0 + cpg->bdst].hd_dentry = wh_dentry; -+ h_d_start = NULL; -+ if (file) { -+ h_d_start = hdp[0 + cpg->bsrc].hd_dentry; -+ hdp[0 + cpg->bsrc].hd_dentry = au_hf_top(file)->f_dentry; -+ } -+ flags_orig = cpg->flags; -+ cpg->flags = !AuCpup_DTIME; -+ err = au_cpup_single(cpg, /*h_parent*/NULL); -+ cpg->flags = flags_orig; -+ if (file) { -+ if (!err) -+ err = au_reopen_nondir(file); -+ hdp[0 + cpg->bsrc].hd_dentry = h_d_start; -+ } -+ hdp[0 + cpg->bdst].hd_dentry = h_d_dst; -+ dinfo->di_bstart = cpg->bsrc; -+ cpg->bsrc = bsrc_orig; -+ -+ return err; -+} -+ -+static int au_cpup_wh(struct au_cp_generic *cpg, struct file *file) -+{ -+ int err; -+ aufs_bindex_t bdst; -+ struct au_dtime dt; -+ struct dentry *dentry, *parent, *h_parent, *wh_dentry; -+ struct au_branch *br; -+ struct path h_path; -+ -+ dentry = cpg->dentry; -+ bdst = cpg->bdst; -+ br = au_sbr(dentry->d_sb, bdst); -+ parent = dget_parent(dentry); -+ h_parent = au_h_dptr(parent, bdst); -+ wh_dentry = au_whtmp_lkup(h_parent, br, &dentry->d_name); -+ err = PTR_ERR(wh_dentry); -+ if (IS_ERR(wh_dentry)) -+ goto out; -+ -+ h_path.dentry = h_parent; -+ h_path.mnt = au_br_mnt(br); -+ au_dtime_store(&dt, parent, &h_path); -+ err = au_do_cpup_wh(cpg, wh_dentry, file); -+ if (unlikely(err)) -+ goto out_wh; -+ -+ dget(wh_dentry); -+ h_path.dentry = wh_dentry; -+ if (!d_is_dir(wh_dentry)) { -+ /* no delegation since it is just created */ -+ err = vfsub_unlink(h_parent->d_inode, &h_path, -+ /*delegated*/NULL, /*force*/0); -+ } else -+ err = vfsub_rmdir(h_parent->d_inode, &h_path); -+ if (unlikely(err)) { -+ AuIOErr("failed remove copied-up tmp file %pd(%d)\n", -+ wh_dentry, err); -+ err = -EIO; -+ } -+ au_dtime_revert(&dt); -+ au_set_hi_wh(dentry->d_inode, bdst, wh_dentry); -+ -+out_wh: -+ dput(wh_dentry); -+out: -+ dput(parent); -+ return err; -+} -+ -+struct au_cpup_wh_args { -+ int *errp; -+ struct au_cp_generic *cpg; -+ struct file *file; -+}; -+ -+static void au_call_cpup_wh(void *args) -+{ -+ struct au_cpup_wh_args *a = args; -+ -+ au_pin_hdir_acquire_nest(a->cpg->pin); -+ *a->errp = au_cpup_wh(a->cpg, a->file); -+ au_pin_hdir_release(a->cpg->pin); -+} -+ -+int au_sio_cpup_wh(struct au_cp_generic *cpg, struct file *file) -+{ -+ int err, wkq_err; -+ aufs_bindex_t bdst; -+ struct dentry *dentry, *parent, *h_orph, *h_parent; -+ struct inode *dir, *h_dir, *h_tmpdir; -+ struct au_wbr *wbr; -+ struct au_pin wh_pin, *pin_orig; -+ -+ dentry = cpg->dentry; -+ bdst = cpg->bdst; -+ parent = dget_parent(dentry); -+ dir = parent->d_inode; -+ h_orph = NULL; -+ h_parent = NULL; -+ h_dir = au_igrab(au_h_iptr(dir, bdst)); -+ h_tmpdir = h_dir; -+ pin_orig = NULL; -+ if (!h_dir->i_nlink) { -+ wbr = au_sbr(dentry->d_sb, bdst)->br_wbr; -+ h_orph = wbr->wbr_orph; -+ -+ h_parent = dget(au_h_dptr(parent, bdst)); -+ au_set_h_dptr(parent, bdst, dget(h_orph)); -+ h_tmpdir = h_orph->d_inode; -+ au_set_h_iptr(dir, bdst, au_igrab(h_tmpdir), /*flags*/0); -+ -+ mutex_lock_nested(&h_tmpdir->i_mutex, AuLsc_I_PARENT3); -+ /* todo: au_h_open_pre()? */ -+ -+ pin_orig = cpg->pin; -+ au_pin_init(&wh_pin, dentry, bdst, AuLsc_DI_PARENT, -+ AuLsc_I_PARENT3, cpg->pin->udba, AuPin_DI_LOCKED); -+ cpg->pin = &wh_pin; -+ } -+ -+ if (!au_test_h_perm_sio(h_tmpdir, MAY_EXEC | MAY_WRITE) -+ && !au_cpup_sio_test(cpg->pin, dentry->d_inode->i_mode)) -+ err = au_cpup_wh(cpg, file); -+ else { -+ struct au_cpup_wh_args args = { -+ .errp = &err, -+ .cpg = cpg, -+ .file = file -+ }; -+ wkq_err = au_wkq_wait(au_call_cpup_wh, &args); -+ if (unlikely(wkq_err)) -+ err = wkq_err; -+ } -+ -+ if (h_orph) { -+ mutex_unlock(&h_tmpdir->i_mutex); -+ /* todo: au_h_open_post()? */ -+ au_set_h_iptr(dir, bdst, au_igrab(h_dir), /*flags*/0); -+ au_set_h_dptr(parent, bdst, h_parent); -+ AuDebugOn(!pin_orig); -+ cpg->pin = pin_orig; -+ } -+ iput(h_dir); -+ dput(parent); -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * generic routine for both of copy-up and copy-down. -+ */ -+/* cf. revalidate function in file.c */ -+int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst, -+ int (*cp)(struct dentry *dentry, aufs_bindex_t bdst, -+ struct au_pin *pin, -+ struct dentry *h_parent, void *arg), -+ void *arg) -+{ -+ int err; -+ struct au_pin pin; -+ struct dentry *d, *parent, *h_parent, *real_parent; -+ -+ err = 0; -+ parent = dget_parent(dentry); -+ if (IS_ROOT(parent)) -+ goto out; -+ -+ au_pin_init(&pin, dentry, bdst, AuLsc_DI_PARENT2, AuLsc_I_PARENT2, -+ au_opt_udba(dentry->d_sb), AuPin_MNT_WRITE); -+ -+ /* do not use au_dpage */ -+ real_parent = parent; -+ while (1) { -+ dput(parent); -+ parent = dget_parent(dentry); -+ h_parent = au_h_dptr(parent, bdst); -+ if (h_parent) -+ goto out; /* success */ -+ -+ /* find top dir which is necessary to cpup */ -+ do { -+ d = parent; -+ dput(parent); -+ parent = dget_parent(d); -+ di_read_lock_parent3(parent, !AuLock_IR); -+ h_parent = au_h_dptr(parent, bdst); -+ di_read_unlock(parent, !AuLock_IR); -+ } while (!h_parent); -+ -+ if (d != real_parent) -+ di_write_lock_child3(d); -+ -+ /* somebody else might create while we were sleeping */ -+ if (!au_h_dptr(d, bdst) || !au_h_dptr(d, bdst)->d_inode) { -+ if (au_h_dptr(d, bdst)) -+ au_update_dbstart(d); -+ -+ au_pin_set_dentry(&pin, d); -+ err = au_do_pin(&pin); -+ if (!err) { -+ err = cp(d, bdst, &pin, h_parent, arg); -+ au_unpin(&pin); -+ } -+ } -+ -+ if (d != real_parent) -+ di_write_unlock(d); -+ if (unlikely(err)) -+ break; -+ } -+ -+out: -+ dput(parent); -+ return err; -+} -+ -+static int au_cpup_dir(struct dentry *dentry, aufs_bindex_t bdst, -+ struct au_pin *pin, -+ struct dentry *h_parent __maybe_unused, -+ void *arg __maybe_unused) -+{ -+ struct au_cp_generic cpg = { -+ .dentry = dentry, -+ .bdst = bdst, -+ .bsrc = -1, -+ .len = 0, -+ .pin = pin, -+ .flags = AuCpup_DTIME -+ }; -+ return au_sio_cpup_simple(&cpg); -+} -+ -+int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst) -+{ -+ return au_cp_dirs(dentry, bdst, au_cpup_dir, NULL); -+} -+ -+int au_test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst) -+{ -+ int err; -+ struct dentry *parent; -+ struct inode *dir; -+ -+ parent = dget_parent(dentry); -+ dir = parent->d_inode; -+ err = 0; -+ if (au_h_iptr(dir, bdst)) -+ goto out; -+ -+ di_read_unlock(parent, AuLock_IR); -+ di_write_lock_parent(parent); -+ /* someone else might change our inode while we were sleeping */ -+ if (!au_h_iptr(dir, bdst)) -+ err = au_cpup_dirs(dentry, bdst); -+ di_downgrade_lock(parent, AuLock_IR); -+ -+out: -+ dput(parent); -+ return err; -+} -diff --git a/fs/aufs/cpup.h b/fs/aufs/cpup.h -new file mode 100644 -index 0000000..7721429 ---- /dev/null -+++ b/fs/aufs/cpup.h -@@ -0,0 +1,94 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * copy-up/down functions -+ */ -+ -+#ifndef __AUFS_CPUP_H__ -+#define __AUFS_CPUP_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+ -+struct inode; -+struct file; -+struct au_pin; -+ -+void au_cpup_attr_flags(struct inode *dst, unsigned int iflags); -+void au_cpup_attr_timesizes(struct inode *inode); -+void au_cpup_attr_nlink(struct inode *inode, int force); -+void au_cpup_attr_changeable(struct inode *inode); -+void au_cpup_igen(struct inode *inode, struct inode *h_inode); -+void au_cpup_attr_all(struct inode *inode, int force); -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct au_cp_generic { -+ struct dentry *dentry; -+ aufs_bindex_t bdst, bsrc; -+ loff_t len; -+ struct au_pin *pin; -+ unsigned int flags; -+}; -+ -+/* cpup flags */ -+#define AuCpup_DTIME 1 /* do dtime_store/revert */ -+#define AuCpup_KEEPLINO (1 << 1) /* do not clear the lower xino, -+ for link(2) */ -+#define AuCpup_RENAME (1 << 2) /* rename after cpup */ -+#define AuCpup_HOPEN (1 << 3) /* call h_open_pre/post() in -+ cpup */ -+#define AuCpup_OVERWRITE (1 << 4) /* allow overwriting the -+ existing entry */ -+#define AuCpup_RWDST (1 << 5) /* force write target even if -+ the branch is marked as RO */ -+ -+#define au_ftest_cpup(flags, name) ((flags) & AuCpup_##name) -+#define au_fset_cpup(flags, name) \ -+ do { (flags) |= AuCpup_##name; } while (0) -+#define au_fclr_cpup(flags, name) \ -+ do { (flags) &= ~AuCpup_##name; } while (0) -+ -+int au_copy_file(struct file *dst, struct file *src, loff_t len); -+int au_sio_cpup_simple(struct au_cp_generic *cpg); -+int au_sio_cpdown_simple(struct au_cp_generic *cpg); -+int au_sio_cpup_wh(struct au_cp_generic *cpg, struct file *file); -+ -+int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst, -+ int (*cp)(struct dentry *dentry, aufs_bindex_t bdst, -+ struct au_pin *pin, -+ struct dentry *h_parent, void *arg), -+ void *arg); -+int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst); -+int au_test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst); -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* keep timestamps when copyup */ -+struct au_dtime { -+ struct dentry *dt_dentry; -+ struct path dt_h_path; -+ struct timespec dt_atime, dt_mtime; -+}; -+void au_dtime_store(struct au_dtime *dt, struct dentry *dentry, -+ struct path *h_path); -+void au_dtime_revert(struct au_dtime *dt); -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_CPUP_H__ */ -diff --git a/fs/aufs/dbgaufs.c b/fs/aufs/dbgaufs.c -new file mode 100644 -index 0000000..b4fdc25 ---- /dev/null -+++ b/fs/aufs/dbgaufs.c -@@ -0,0 +1,432 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * debugfs interface -+ */ -+ -+#include -+#include "aufs.h" -+ -+#ifndef CONFIG_SYSFS -+#error DEBUG_FS depends upon SYSFS -+#endif -+ -+static struct dentry *dbgaufs; -+static const mode_t dbgaufs_mode = S_IRUSR | S_IRGRP | S_IROTH; -+ -+/* 20 is max digits length of ulong 64 */ -+struct dbgaufs_arg { -+ int n; -+ char a[20 * 4]; -+}; -+ -+/* -+ * common function for all XINO files -+ */ -+static int dbgaufs_xi_release(struct inode *inode __maybe_unused, -+ struct file *file) -+{ -+ kfree(file->private_data); -+ return 0; -+} -+ -+static int dbgaufs_xi_open(struct file *xf, struct file *file, int do_fcnt) -+{ -+ int err; -+ struct kstat st; -+ struct dbgaufs_arg *p; -+ -+ err = -ENOMEM; -+ p = kmalloc(sizeof(*p), GFP_NOFS); -+ if (unlikely(!p)) -+ goto out; -+ -+ err = 0; -+ p->n = 0; -+ file->private_data = p; -+ if (!xf) -+ goto out; -+ -+ err = vfs_getattr(&xf->f_path, &st); -+ if (!err) { -+ if (do_fcnt) -+ p->n = snprintf -+ (p->a, sizeof(p->a), "%ld, %llux%lu %lld\n", -+ (long)file_count(xf), st.blocks, st.blksize, -+ (long long)st.size); -+ else -+ p->n = snprintf(p->a, sizeof(p->a), "%llux%lu %lld\n", -+ st.blocks, st.blksize, -+ (long long)st.size); -+ AuDebugOn(p->n >= sizeof(p->a)); -+ } else { -+ p->n = snprintf(p->a, sizeof(p->a), "err %d\n", err); -+ err = 0; -+ } -+ -+out: -+ return err; -+ -+} -+ -+static ssize_t dbgaufs_xi_read(struct file *file, char __user *buf, -+ size_t count, loff_t *ppos) -+{ -+ struct dbgaufs_arg *p; -+ -+ p = file->private_data; -+ return simple_read_from_buffer(buf, count, ppos, p->a, p->n); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct dbgaufs_plink_arg { -+ int n; -+ char a[]; -+}; -+ -+static int dbgaufs_plink_release(struct inode *inode __maybe_unused, -+ struct file *file) -+{ -+ free_page((unsigned long)file->private_data); -+ return 0; -+} -+ -+static int dbgaufs_plink_open(struct inode *inode, struct file *file) -+{ -+ int err, i, limit; -+ unsigned long n, sum; -+ struct dbgaufs_plink_arg *p; -+ struct au_sbinfo *sbinfo; -+ struct super_block *sb; -+ struct au_sphlhead *sphl; -+ -+ err = -ENOMEM; -+ p = (void *)get_zeroed_page(GFP_NOFS); -+ if (unlikely(!p)) -+ goto out; -+ -+ err = -EFBIG; -+ sbinfo = inode->i_private; -+ sb = sbinfo->si_sb; -+ si_noflush_read_lock(sb); -+ if (au_opt_test(au_mntflags(sb), PLINK)) { -+ limit = PAGE_SIZE - sizeof(p->n); -+ -+ /* the number of buckets */ -+ n = snprintf(p->a + p->n, limit, "%d\n", AuPlink_NHASH); -+ p->n += n; -+ limit -= n; -+ -+ sum = 0; -+ for (i = 0, sphl = sbinfo->si_plink; -+ i < AuPlink_NHASH; -+ i++, sphl++) { -+ n = au_sphl_count(sphl); -+ sum += n; -+ -+ n = snprintf(p->a + p->n, limit, "%lu ", n); -+ p->n += n; -+ limit -= n; -+ if (unlikely(limit <= 0)) -+ goto out_free; -+ } -+ p->a[p->n - 1] = '\n'; -+ -+ /* the sum of plinks */ -+ n = snprintf(p->a + p->n, limit, "%lu\n", sum); -+ p->n += n; -+ limit -= n; -+ if (unlikely(limit <= 0)) -+ goto out_free; -+ } else { -+#define str "1\n0\n0\n" -+ p->n = sizeof(str) - 1; -+ strcpy(p->a, str); -+#undef str -+ } -+ si_read_unlock(sb); -+ -+ err = 0; -+ file->private_data = p; -+ goto out; /* success */ -+ -+out_free: -+ free_page((unsigned long)p); -+out: -+ return err; -+} -+ -+static ssize_t dbgaufs_plink_read(struct file *file, char __user *buf, -+ size_t count, loff_t *ppos) -+{ -+ struct dbgaufs_plink_arg *p; -+ -+ p = file->private_data; -+ return simple_read_from_buffer(buf, count, ppos, p->a, p->n); -+} -+ -+static const struct file_operations dbgaufs_plink_fop = { -+ .owner = THIS_MODULE, -+ .open = dbgaufs_plink_open, -+ .release = dbgaufs_plink_release, -+ .read = dbgaufs_plink_read -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int dbgaufs_xib_open(struct inode *inode, struct file *file) -+{ -+ int err; -+ struct au_sbinfo *sbinfo; -+ struct super_block *sb; -+ -+ sbinfo = inode->i_private; -+ sb = sbinfo->si_sb; -+ si_noflush_read_lock(sb); -+ err = dbgaufs_xi_open(sbinfo->si_xib, file, /*do_fcnt*/0); -+ si_read_unlock(sb); -+ return err; -+} -+ -+static const struct file_operations dbgaufs_xib_fop = { -+ .owner = THIS_MODULE, -+ .open = dbgaufs_xib_open, -+ .release = dbgaufs_xi_release, -+ .read = dbgaufs_xi_read -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+#define DbgaufsXi_PREFIX "xi" -+ -+static int dbgaufs_xino_open(struct inode *inode, struct file *file) -+{ -+ int err; -+ long l; -+ struct au_sbinfo *sbinfo; -+ struct super_block *sb; -+ struct file *xf; -+ struct qstr *name; -+ -+ err = -ENOENT; -+ xf = NULL; -+ name = &file->f_dentry->d_name; -+ if (unlikely(name->len < sizeof(DbgaufsXi_PREFIX) -+ || memcmp(name->name, DbgaufsXi_PREFIX, -+ sizeof(DbgaufsXi_PREFIX) - 1))) -+ goto out; -+ err = kstrtol(name->name + sizeof(DbgaufsXi_PREFIX) - 1, 10, &l); -+ if (unlikely(err)) -+ goto out; -+ -+ sbinfo = inode->i_private; -+ sb = sbinfo->si_sb; -+ si_noflush_read_lock(sb); -+ if (l <= au_sbend(sb)) { -+ xf = au_sbr(sb, (aufs_bindex_t)l)->br_xino.xi_file; -+ err = dbgaufs_xi_open(xf, file, /*do_fcnt*/1); -+ } else -+ err = -ENOENT; -+ si_read_unlock(sb); -+ -+out: -+ return err; -+} -+ -+static const struct file_operations dbgaufs_xino_fop = { -+ .owner = THIS_MODULE, -+ .open = dbgaufs_xino_open, -+ .release = dbgaufs_xi_release, -+ .read = dbgaufs_xi_read -+}; -+ -+void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ aufs_bindex_t bend; -+ struct au_branch *br; -+ struct au_xino_file *xi; -+ -+ if (!au_sbi(sb)->si_dbgaufs) -+ return; -+ -+ bend = au_sbend(sb); -+ for (; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ xi = &br->br_xino; -+ debugfs_remove(xi->xi_dbgaufs); -+ xi->xi_dbgaufs = NULL; -+ } -+} -+ -+void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ struct au_sbinfo *sbinfo; -+ struct dentry *parent; -+ struct au_branch *br; -+ struct au_xino_file *xi; -+ aufs_bindex_t bend; -+ char name[sizeof(DbgaufsXi_PREFIX) + 5]; /* "xi" bindex NULL */ -+ -+ sbinfo = au_sbi(sb); -+ parent = sbinfo->si_dbgaufs; -+ if (!parent) -+ return; -+ -+ bend = au_sbend(sb); -+ for (; bindex <= bend; bindex++) { -+ snprintf(name, sizeof(name), DbgaufsXi_PREFIX "%d", bindex); -+ br = au_sbr(sb, bindex); -+ xi = &br->br_xino; -+ AuDebugOn(xi->xi_dbgaufs); -+ xi->xi_dbgaufs = debugfs_create_file(name, dbgaufs_mode, parent, -+ sbinfo, &dbgaufs_xino_fop); -+ /* ignore an error */ -+ if (unlikely(!xi->xi_dbgaufs)) -+ AuWarn1("failed %s under debugfs\n", name); -+ } -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+#ifdef CONFIG_AUFS_EXPORT -+static int dbgaufs_xigen_open(struct inode *inode, struct file *file) -+{ -+ int err; -+ struct au_sbinfo *sbinfo; -+ struct super_block *sb; -+ -+ sbinfo = inode->i_private; -+ sb = sbinfo->si_sb; -+ si_noflush_read_lock(sb); -+ err = dbgaufs_xi_open(sbinfo->si_xigen, file, /*do_fcnt*/0); -+ si_read_unlock(sb); -+ return err; -+} -+ -+static const struct file_operations dbgaufs_xigen_fop = { -+ .owner = THIS_MODULE, -+ .open = dbgaufs_xigen_open, -+ .release = dbgaufs_xi_release, -+ .read = dbgaufs_xi_read -+}; -+ -+static int dbgaufs_xigen_init(struct au_sbinfo *sbinfo) -+{ -+ int err; -+ -+ /* -+ * This function is a dynamic '__init' function actually, -+ * so the tiny check for si_rwsem is unnecessary. -+ */ -+ /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ -+ -+ err = -EIO; -+ sbinfo->si_dbgaufs_xigen = debugfs_create_file -+ ("xigen", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, -+ &dbgaufs_xigen_fop); -+ if (sbinfo->si_dbgaufs_xigen) -+ err = 0; -+ -+ return err; -+} -+#else -+static int dbgaufs_xigen_init(struct au_sbinfo *sbinfo) -+{ -+ return 0; -+} -+#endif /* CONFIG_AUFS_EXPORT */ -+ -+/* ---------------------------------------------------------------------- */ -+ -+void dbgaufs_si_fin(struct au_sbinfo *sbinfo) -+{ -+ /* -+ * This function is a dynamic '__fin' function actually, -+ * so the tiny check for si_rwsem is unnecessary. -+ */ -+ /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ -+ -+ debugfs_remove_recursive(sbinfo->si_dbgaufs); -+ sbinfo->si_dbgaufs = NULL; -+ kobject_put(&sbinfo->si_kobj); -+} -+ -+int dbgaufs_si_init(struct au_sbinfo *sbinfo) -+{ -+ int err; -+ char name[SysaufsSiNameLen]; -+ -+ /* -+ * This function is a dynamic '__init' function actually, -+ * so the tiny check for si_rwsem is unnecessary. -+ */ -+ /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ -+ -+ err = -ENOENT; -+ if (!dbgaufs) { -+ AuErr1("/debug/aufs is uninitialized\n"); -+ goto out; -+ } -+ -+ err = -EIO; -+ sysaufs_name(sbinfo, name); -+ sbinfo->si_dbgaufs = debugfs_create_dir(name, dbgaufs); -+ if (unlikely(!sbinfo->si_dbgaufs)) -+ goto out; -+ kobject_get(&sbinfo->si_kobj); -+ -+ sbinfo->si_dbgaufs_xib = debugfs_create_file -+ ("xib", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, -+ &dbgaufs_xib_fop); -+ if (unlikely(!sbinfo->si_dbgaufs_xib)) -+ goto out_dir; -+ -+ sbinfo->si_dbgaufs_plink = debugfs_create_file -+ ("plink", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, -+ &dbgaufs_plink_fop); -+ if (unlikely(!sbinfo->si_dbgaufs_plink)) -+ goto out_dir; -+ -+ err = dbgaufs_xigen_init(sbinfo); -+ if (!err) -+ goto out; /* success */ -+ -+out_dir: -+ dbgaufs_si_fin(sbinfo); -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void dbgaufs_fin(void) -+{ -+ debugfs_remove(dbgaufs); -+} -+ -+int __init dbgaufs_init(void) -+{ -+ int err; -+ -+ err = -EIO; -+ dbgaufs = debugfs_create_dir(AUFS_NAME, NULL); -+ if (dbgaufs) -+ err = 0; -+ return err; -+} -diff --git a/fs/aufs/dbgaufs.h b/fs/aufs/dbgaufs.h -new file mode 100644 -index 0000000..d1e09bd ---- /dev/null -+++ b/fs/aufs/dbgaufs.h -@@ -0,0 +1,48 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * debugfs interface -+ */ -+ -+#ifndef __DBGAUFS_H__ -+#define __DBGAUFS_H__ -+ -+#ifdef __KERNEL__ -+ -+struct super_block; -+struct au_sbinfo; -+ -+#ifdef CONFIG_DEBUG_FS -+/* dbgaufs.c */ -+void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex); -+void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex); -+void dbgaufs_si_fin(struct au_sbinfo *sbinfo); -+int dbgaufs_si_init(struct au_sbinfo *sbinfo); -+void dbgaufs_fin(void); -+int __init dbgaufs_init(void); -+#else -+AuStubVoid(dbgaufs_brs_del, struct super_block *sb, aufs_bindex_t bindex) -+AuStubVoid(dbgaufs_brs_add, struct super_block *sb, aufs_bindex_t bindex) -+AuStubVoid(dbgaufs_si_fin, struct au_sbinfo *sbinfo) -+AuStubInt0(dbgaufs_si_init, struct au_sbinfo *sbinfo) -+AuStubVoid(dbgaufs_fin, void) -+AuStubInt0(__init dbgaufs_init, void) -+#endif /* CONFIG_DEBUG_FS */ -+ -+#endif /* __KERNEL__ */ -+#endif /* __DBGAUFS_H__ */ -diff --git a/fs/aufs/dcsub.c b/fs/aufs/dcsub.c -new file mode 100644 -index 0000000..832baa4 ---- /dev/null -+++ b/fs/aufs/dcsub.c -@@ -0,0 +1,224 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * sub-routines for dentry cache -+ */ -+ -+#include "aufs.h" -+ -+static void au_dpage_free(struct au_dpage *dpage) -+{ -+ int i; -+ struct dentry **p; -+ -+ p = dpage->dentries; -+ for (i = 0; i < dpage->ndentry; i++) -+ dput(*p++); -+ free_page((unsigned long)dpage->dentries); -+} -+ -+int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp) -+{ -+ int err; -+ void *p; -+ -+ err = -ENOMEM; -+ dpages->dpages = kmalloc(sizeof(*dpages->dpages), gfp); -+ if (unlikely(!dpages->dpages)) -+ goto out; -+ -+ p = (void *)__get_free_page(gfp); -+ if (unlikely(!p)) -+ goto out_dpages; -+ -+ dpages->dpages[0].ndentry = 0; -+ dpages->dpages[0].dentries = p; -+ dpages->ndpage = 1; -+ return 0; /* success */ -+ -+out_dpages: -+ kfree(dpages->dpages); -+out: -+ return err; -+} -+ -+void au_dpages_free(struct au_dcsub_pages *dpages) -+{ -+ int i; -+ struct au_dpage *p; -+ -+ p = dpages->dpages; -+ for (i = 0; i < dpages->ndpage; i++) -+ au_dpage_free(p++); -+ kfree(dpages->dpages); -+} -+ -+static int au_dpages_append(struct au_dcsub_pages *dpages, -+ struct dentry *dentry, gfp_t gfp) -+{ -+ int err, sz; -+ struct au_dpage *dpage; -+ void *p; -+ -+ dpage = dpages->dpages + dpages->ndpage - 1; -+ sz = PAGE_SIZE / sizeof(dentry); -+ if (unlikely(dpage->ndentry >= sz)) { -+ AuLabel(new dpage); -+ err = -ENOMEM; -+ sz = dpages->ndpage * sizeof(*dpages->dpages); -+ p = au_kzrealloc(dpages->dpages, sz, -+ sz + sizeof(*dpages->dpages), gfp); -+ if (unlikely(!p)) -+ goto out; -+ -+ dpages->dpages = p; -+ dpage = dpages->dpages + dpages->ndpage; -+ p = (void *)__get_free_page(gfp); -+ if (unlikely(!p)) -+ goto out; -+ -+ dpage->ndentry = 0; -+ dpage->dentries = p; -+ dpages->ndpage++; -+ } -+ -+ AuDebugOn(au_dcount(dentry) <= 0); -+ dpage->dentries[dpage->ndentry++] = dget_dlock(dentry); -+ return 0; /* success */ -+ -+out: -+ return err; -+} -+ -+/* todo: BAD approach */ -+/* copied from linux/fs/dcache.c */ -+enum d_walk_ret { -+ D_WALK_CONTINUE, -+ D_WALK_QUIT, -+ D_WALK_NORETRY, -+ D_WALK_SKIP, -+}; -+ -+extern void d_walk(struct dentry *parent, void *data, -+ enum d_walk_ret (*enter)(void *, struct dentry *), -+ void (*finish)(void *)); -+ -+struct ac_dpages_arg { -+ int err; -+ struct au_dcsub_pages *dpages; -+ struct super_block *sb; -+ au_dpages_test test; -+ void *arg; -+}; -+ -+static enum d_walk_ret au_call_dpages_append(void *_arg, struct dentry *dentry) -+{ -+ enum d_walk_ret ret; -+ struct ac_dpages_arg *arg = _arg; -+ -+ ret = D_WALK_CONTINUE; -+ if (dentry->d_sb == arg->sb -+ && !IS_ROOT(dentry) -+ && au_dcount(dentry) > 0 -+ && au_di(dentry) -+ && (!arg->test || arg->test(dentry, arg->arg))) { -+ arg->err = au_dpages_append(arg->dpages, dentry, GFP_ATOMIC); -+ if (unlikely(arg->err)) -+ ret = D_WALK_QUIT; -+ } -+ -+ return ret; -+} -+ -+int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root, -+ au_dpages_test test, void *arg) -+{ -+ struct ac_dpages_arg args = { -+ .err = 0, -+ .dpages = dpages, -+ .sb = root->d_sb, -+ .test = test, -+ .arg = arg -+ }; -+ -+ d_walk(root, &args, au_call_dpages_append, NULL); -+ -+ return args.err; -+} -+ -+int au_dcsub_pages_rev(struct au_dcsub_pages *dpages, struct dentry *dentry, -+ int do_include, au_dpages_test test, void *arg) -+{ -+ int err; -+ -+ err = 0; -+ write_seqlock(&rename_lock); -+ spin_lock(&dentry->d_lock); -+ if (do_include -+ && au_dcount(dentry) > 0 -+ && (!test || test(dentry, arg))) -+ err = au_dpages_append(dpages, dentry, GFP_ATOMIC); -+ spin_unlock(&dentry->d_lock); -+ if (unlikely(err)) -+ goto out; -+ -+ /* -+ * RCU for vfsmount is unnecessary since this is a traverse in a single -+ * mount -+ */ -+ while (!IS_ROOT(dentry)) { -+ dentry = dentry->d_parent; /* rename_lock is locked */ -+ spin_lock(&dentry->d_lock); -+ if (au_dcount(dentry) > 0 -+ && (!test || test(dentry, arg))) -+ err = au_dpages_append(dpages, dentry, GFP_ATOMIC); -+ spin_unlock(&dentry->d_lock); -+ if (unlikely(err)) -+ break; -+ } -+ -+out: -+ write_sequnlock(&rename_lock); -+ return err; -+} -+ -+static inline int au_dcsub_dpages_aufs(struct dentry *dentry, void *arg) -+{ -+ return au_di(dentry) && dentry->d_sb == arg; -+} -+ -+int au_dcsub_pages_rev_aufs(struct au_dcsub_pages *dpages, -+ struct dentry *dentry, int do_include) -+{ -+ return au_dcsub_pages_rev(dpages, dentry, do_include, -+ au_dcsub_dpages_aufs, dentry->d_sb); -+} -+ -+int au_test_subdir(struct dentry *d1, struct dentry *d2) -+{ -+ struct path path[2] = { -+ { -+ .dentry = d1 -+ }, -+ { -+ .dentry = d2 -+ } -+ }; -+ -+ return path_is_under(path + 0, path + 1); -+} -diff --git a/fs/aufs/dcsub.h b/fs/aufs/dcsub.h -new file mode 100644 -index 0000000..7997944 ---- /dev/null -+++ b/fs/aufs/dcsub.h -@@ -0,0 +1,123 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * sub-routines for dentry cache -+ */ -+ -+#ifndef __AUFS_DCSUB_H__ -+#define __AUFS_DCSUB_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+#include -+ -+struct au_dpage { -+ int ndentry; -+ struct dentry **dentries; -+}; -+ -+struct au_dcsub_pages { -+ int ndpage; -+ struct au_dpage *dpages; -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* dcsub.c */ -+int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp); -+void au_dpages_free(struct au_dcsub_pages *dpages); -+typedef int (*au_dpages_test)(struct dentry *dentry, void *arg); -+int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root, -+ au_dpages_test test, void *arg); -+int au_dcsub_pages_rev(struct au_dcsub_pages *dpages, struct dentry *dentry, -+ int do_include, au_dpages_test test, void *arg); -+int au_dcsub_pages_rev_aufs(struct au_dcsub_pages *dpages, -+ struct dentry *dentry, int do_include); -+int au_test_subdir(struct dentry *d1, struct dentry *d2); -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * todo: in linux-3.13, several similar (but faster) helpers are added to -+ * include/linux/dcache.h. Try them (in the future). -+ */ -+ -+static inline int au_d_hashed_positive(struct dentry *d) -+{ -+ int err; -+ struct inode *inode = d->d_inode; -+ -+ err = 0; -+ if (unlikely(d_unhashed(d) || !inode || !inode->i_nlink)) -+ err = -ENOENT; -+ return err; -+} -+ -+static inline int au_d_linkable(struct dentry *d) -+{ -+ int err; -+ struct inode *inode = d->d_inode; -+ -+ err = au_d_hashed_positive(d); -+ if (err -+ && inode -+ && (inode->i_state & I_LINKABLE)) -+ err = 0; -+ return err; -+} -+ -+static inline int au_d_alive(struct dentry *d) -+{ -+ int err; -+ struct inode *inode; -+ -+ err = 0; -+ if (!IS_ROOT(d)) -+ err = au_d_hashed_positive(d); -+ else { -+ inode = d->d_inode; -+ if (unlikely(d_unlinked(d) || !inode || !inode->i_nlink)) -+ err = -ENOENT; -+ } -+ return err; -+} -+ -+static inline int au_alive_dir(struct dentry *d) -+{ -+ int err; -+ -+ err = au_d_alive(d); -+ if (unlikely(err || IS_DEADDIR(d->d_inode))) -+ err = -ENOENT; -+ return err; -+} -+ -+static inline int au_qstreq(struct qstr *a, struct qstr *b) -+{ -+ return a->len == b->len -+ && !memcmp(a->name, b->name, a->len); -+} -+ -+static inline int au_dcount(struct dentry *d) -+{ -+ return (int)d_count(d); -+} -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_DCSUB_H__ */ -diff --git a/fs/aufs/debug.c b/fs/aufs/debug.c -new file mode 100644 -index 0000000..2747d13 ---- /dev/null -+++ b/fs/aufs/debug.c -@@ -0,0 +1,436 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * debug print functions -+ */ -+ -+#include "aufs.h" -+ -+/* Returns 0, or -errno. arg is in kp->arg. */ -+static int param_atomic_t_set(const char *val, const struct kernel_param *kp) -+{ -+ int err, n; -+ -+ err = kstrtoint(val, 0, &n); -+ if (!err) { -+ if (n > 0) -+ au_debug_on(); -+ else -+ au_debug_off(); -+ } -+ return err; -+} -+ -+/* Returns length written or -errno. Buffer is 4k (ie. be short!) */ -+static int param_atomic_t_get(char *buffer, const struct kernel_param *kp) -+{ -+ atomic_t *a; -+ -+ a = kp->arg; -+ return sprintf(buffer, "%d", atomic_read(a)); -+} -+ -+static struct kernel_param_ops param_ops_atomic_t = { -+ .set = param_atomic_t_set, -+ .get = param_atomic_t_get -+ /* void (*free)(void *arg) */ -+}; -+ -+atomic_t aufs_debug = ATOMIC_INIT(0); -+MODULE_PARM_DESC(debug, "debug print"); -+module_param_named(debug, aufs_debug, atomic_t, S_IRUGO | S_IWUSR | S_IWGRP); -+ -+DEFINE_MUTEX(au_dbg_mtx); /* just to serialize the dbg msgs */ -+char *au_plevel = KERN_DEBUG; -+#define dpri(fmt, ...) do { \ -+ if ((au_plevel \ -+ && strcmp(au_plevel, KERN_DEBUG)) \ -+ || au_debug_test()) \ -+ printk("%s" fmt, au_plevel, ##__VA_ARGS__); \ -+} while (0) -+ -+/* ---------------------------------------------------------------------- */ -+ -+void au_dpri_whlist(struct au_nhash *whlist) -+{ -+ unsigned long ul, n; -+ struct hlist_head *head; -+ struct au_vdir_wh *pos; -+ -+ n = whlist->nh_num; -+ head = whlist->nh_head; -+ for (ul = 0; ul < n; ul++) { -+ hlist_for_each_entry(pos, head, wh_hash) -+ dpri("b%d, %.*s, %d\n", -+ pos->wh_bindex, -+ pos->wh_str.len, pos->wh_str.name, -+ pos->wh_str.len); -+ head++; -+ } -+} -+ -+void au_dpri_vdir(struct au_vdir *vdir) -+{ -+ unsigned long ul; -+ union au_vdir_deblk_p p; -+ unsigned char *o; -+ -+ if (!vdir || IS_ERR(vdir)) { -+ dpri("err %ld\n", PTR_ERR(vdir)); -+ return; -+ } -+ -+ dpri("deblk %u, nblk %lu, deblk %p, last{%lu, %p}, ver %lu\n", -+ vdir->vd_deblk_sz, vdir->vd_nblk, vdir->vd_deblk, -+ vdir->vd_last.ul, vdir->vd_last.p.deblk, vdir->vd_version); -+ for (ul = 0; ul < vdir->vd_nblk; ul++) { -+ p.deblk = vdir->vd_deblk[ul]; -+ o = p.deblk; -+ dpri("[%lu]: %p\n", ul, o); -+ } -+} -+ -+static int do_pri_inode(aufs_bindex_t bindex, struct inode *inode, int hn, -+ struct dentry *wh) -+{ -+ char *n = NULL; -+ int l = 0; -+ -+ if (!inode || IS_ERR(inode)) { -+ dpri("i%d: err %ld\n", bindex, PTR_ERR(inode)); -+ return -1; -+ } -+ -+ /* the type of i_blocks depends upon CONFIG_LBDAF */ -+ BUILD_BUG_ON(sizeof(inode->i_blocks) != sizeof(unsigned long) -+ && sizeof(inode->i_blocks) != sizeof(u64)); -+ if (wh) { -+ n = (void *)wh->d_name.name; -+ l = wh->d_name.len; -+ } -+ -+ dpri("i%d: %p, i%lu, %s, cnt %d, nl %u, 0%o, sz %llu, blk %llu," -+ " hn %d, ct %lld, np %lu, st 0x%lx, f 0x%x, v %llu, g %x%s%.*s\n", -+ bindex, inode, -+ inode->i_ino, inode->i_sb ? au_sbtype(inode->i_sb) : "??", -+ atomic_read(&inode->i_count), inode->i_nlink, inode->i_mode, -+ i_size_read(inode), (unsigned long long)inode->i_blocks, -+ hn, (long long)timespec_to_ns(&inode->i_ctime) & 0x0ffff, -+ inode->i_mapping ? inode->i_mapping->nrpages : 0, -+ inode->i_state, inode->i_flags, inode->i_version, -+ inode->i_generation, -+ l ? ", wh " : "", l, n); -+ return 0; -+} -+ -+void au_dpri_inode(struct inode *inode) -+{ -+ struct au_iinfo *iinfo; -+ aufs_bindex_t bindex; -+ int err, hn; -+ -+ err = do_pri_inode(-1, inode, -1, NULL); -+ if (err || !au_test_aufs(inode->i_sb)) -+ return; -+ -+ iinfo = au_ii(inode); -+ if (!iinfo) -+ return; -+ dpri("i-1: bstart %d, bend %d, gen %d\n", -+ iinfo->ii_bstart, iinfo->ii_bend, au_iigen(inode, NULL)); -+ if (iinfo->ii_bstart < 0) -+ return; -+ hn = 0; -+ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; bindex++) { -+ hn = !!au_hn(iinfo->ii_hinode + bindex); -+ do_pri_inode(bindex, iinfo->ii_hinode[0 + bindex].hi_inode, hn, -+ iinfo->ii_hinode[0 + bindex].hi_whdentry); -+ } -+} -+ -+void au_dpri_dalias(struct inode *inode) -+{ -+ struct dentry *d; -+ -+ spin_lock(&inode->i_lock); -+ hlist_for_each_entry(d, &inode->i_dentry, d_u.d_alias) -+ au_dpri_dentry(d); -+ spin_unlock(&inode->i_lock); -+} -+ -+static int do_pri_dentry(aufs_bindex_t bindex, struct dentry *dentry) -+{ -+ struct dentry *wh = NULL; -+ int hn; -+ struct au_iinfo *iinfo; -+ -+ if (!dentry || IS_ERR(dentry)) { -+ dpri("d%d: err %ld\n", bindex, PTR_ERR(dentry)); -+ return -1; -+ } -+ /* do not call dget_parent() here */ -+ /* note: access d_xxx without d_lock */ -+ dpri("d%d: %p, %pd2?, %s, cnt %d, flags 0x%x, %shashed\n", -+ bindex, dentry, dentry, -+ dentry->d_sb ? au_sbtype(dentry->d_sb) : "??", -+ au_dcount(dentry), dentry->d_flags, -+ d_unhashed(dentry) ? "un" : ""); -+ hn = -1; -+ if (bindex >= 0 && dentry->d_inode && au_test_aufs(dentry->d_sb)) { -+ iinfo = au_ii(dentry->d_inode); -+ if (iinfo) { -+ hn = !!au_hn(iinfo->ii_hinode + bindex); -+ wh = iinfo->ii_hinode[0 + bindex].hi_whdentry; -+ } -+ } -+ do_pri_inode(bindex, dentry->d_inode, hn, wh); -+ return 0; -+} -+ -+void au_dpri_dentry(struct dentry *dentry) -+{ -+ struct au_dinfo *dinfo; -+ aufs_bindex_t bindex; -+ int err; -+ struct au_hdentry *hdp; -+ -+ err = do_pri_dentry(-1, dentry); -+ if (err || !au_test_aufs(dentry->d_sb)) -+ return; -+ -+ dinfo = au_di(dentry); -+ if (!dinfo) -+ return; -+ dpri("d-1: bstart %d, bend %d, bwh %d, bdiropq %d, gen %d, tmp %d\n", -+ dinfo->di_bstart, dinfo->di_bend, -+ dinfo->di_bwh, dinfo->di_bdiropq, au_digen(dentry), -+ dinfo->di_tmpfile); -+ if (dinfo->di_bstart < 0) -+ return; -+ hdp = dinfo->di_hdentry; -+ for (bindex = dinfo->di_bstart; bindex <= dinfo->di_bend; bindex++) -+ do_pri_dentry(bindex, hdp[0 + bindex].hd_dentry); -+} -+ -+static int do_pri_file(aufs_bindex_t bindex, struct file *file) -+{ -+ char a[32]; -+ -+ if (!file || IS_ERR(file)) { -+ dpri("f%d: err %ld\n", bindex, PTR_ERR(file)); -+ return -1; -+ } -+ a[0] = 0; -+ if (bindex < 0 -+ && !IS_ERR_OR_NULL(file->f_dentry) -+ && au_test_aufs(file->f_dentry->d_sb) -+ && au_fi(file)) -+ snprintf(a, sizeof(a), ", gen %d, mmapped %d", -+ au_figen(file), atomic_read(&au_fi(file)->fi_mmapped)); -+ dpri("f%d: mode 0x%x, flags 0%o, cnt %ld, v %llu, pos %llu%s\n", -+ bindex, file->f_mode, file->f_flags, (long)file_count(file), -+ file->f_version, file->f_pos, a); -+ if (!IS_ERR_OR_NULL(file->f_dentry)) -+ do_pri_dentry(bindex, file->f_dentry); -+ return 0; -+} -+ -+void au_dpri_file(struct file *file) -+{ -+ struct au_finfo *finfo; -+ struct au_fidir *fidir; -+ struct au_hfile *hfile; -+ aufs_bindex_t bindex; -+ int err; -+ -+ err = do_pri_file(-1, file); -+ if (err -+ || IS_ERR_OR_NULL(file->f_dentry) -+ || !au_test_aufs(file->f_dentry->d_sb)) -+ return; -+ -+ finfo = au_fi(file); -+ if (!finfo) -+ return; -+ if (finfo->fi_btop < 0) -+ return; -+ fidir = finfo->fi_hdir; -+ if (!fidir) -+ do_pri_file(finfo->fi_btop, finfo->fi_htop.hf_file); -+ else -+ for (bindex = finfo->fi_btop; -+ bindex >= 0 && bindex <= fidir->fd_bbot; -+ bindex++) { -+ hfile = fidir->fd_hfile + bindex; -+ do_pri_file(bindex, hfile ? hfile->hf_file : NULL); -+ } -+} -+ -+static int do_pri_br(aufs_bindex_t bindex, struct au_branch *br) -+{ -+ struct vfsmount *mnt; -+ struct super_block *sb; -+ -+ if (!br || IS_ERR(br)) -+ goto out; -+ mnt = au_br_mnt(br); -+ if (!mnt || IS_ERR(mnt)) -+ goto out; -+ sb = mnt->mnt_sb; -+ if (!sb || IS_ERR(sb)) -+ goto out; -+ -+ dpri("s%d: {perm 0x%x, id %d, cnt %d, wbr %p}, " -+ "%s, dev 0x%02x%02x, flags 0x%lx, cnt %d, active %d, " -+ "xino %d\n", -+ bindex, br->br_perm, br->br_id, atomic_read(&br->br_count), -+ br->br_wbr, au_sbtype(sb), MAJOR(sb->s_dev), MINOR(sb->s_dev), -+ sb->s_flags, sb->s_count, -+ atomic_read(&sb->s_active), !!br->br_xino.xi_file); -+ return 0; -+ -+out: -+ dpri("s%d: err %ld\n", bindex, PTR_ERR(br)); -+ return -1; -+} -+ -+void au_dpri_sb(struct super_block *sb) -+{ -+ struct au_sbinfo *sbinfo; -+ aufs_bindex_t bindex; -+ int err; -+ /* to reuduce stack size */ -+ struct { -+ struct vfsmount mnt; -+ struct au_branch fake; -+ } *a; -+ -+ /* this function can be called from magic sysrq */ -+ a = kzalloc(sizeof(*a), GFP_ATOMIC); -+ if (unlikely(!a)) { -+ dpri("no memory\n"); -+ return; -+ } -+ -+ a->mnt.mnt_sb = sb; -+ a->fake.br_path.mnt = &a->mnt; -+ atomic_set(&a->fake.br_count, 0); -+ smp_mb(); /* atomic_set */ -+ err = do_pri_br(-1, &a->fake); -+ kfree(a); -+ dpri("dev 0x%x\n", sb->s_dev); -+ if (err || !au_test_aufs(sb)) -+ return; -+ -+ sbinfo = au_sbi(sb); -+ if (!sbinfo) -+ return; -+ dpri("nw %d, gen %u, kobj %d\n", -+ atomic_read(&sbinfo->si_nowait.nw_len), sbinfo->si_generation, -+ atomic_read(&sbinfo->si_kobj.kref.refcount)); -+ for (bindex = 0; bindex <= sbinfo->si_bend; bindex++) -+ do_pri_br(bindex, sbinfo->si_branch[0 + bindex]); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void __au_dbg_verify_dinode(struct dentry *dentry, const char *func, int line) -+{ -+ struct inode *h_inode, *inode = dentry->d_inode; -+ struct dentry *h_dentry; -+ aufs_bindex_t bindex, bend, bi; -+ -+ if (!inode /* || au_di(dentry)->di_lsc == AuLsc_DI_TMP */) -+ return; -+ -+ bend = au_dbend(dentry); -+ bi = au_ibend(inode); -+ if (bi < bend) -+ bend = bi; -+ bindex = au_dbstart(dentry); -+ bi = au_ibstart(inode); -+ if (bi > bindex) -+ bindex = bi; -+ -+ for (; bindex <= bend; bindex++) { -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (!h_dentry) -+ continue; -+ h_inode = au_h_iptr(inode, bindex); -+ if (unlikely(h_inode != h_dentry->d_inode)) { -+ au_debug_on(); -+ AuDbg("b%d, %s:%d\n", bindex, func, line); -+ AuDbgDentry(dentry); -+ AuDbgInode(inode); -+ au_debug_off(); -+ BUG(); -+ } -+ } -+} -+ -+void au_dbg_verify_gen(struct dentry *parent, unsigned int sigen) -+{ -+ int err, i, j; -+ struct au_dcsub_pages dpages; -+ struct au_dpage *dpage; -+ struct dentry **dentries; -+ -+ err = au_dpages_init(&dpages, GFP_NOFS); -+ AuDebugOn(err); -+ err = au_dcsub_pages_rev_aufs(&dpages, parent, /*do_include*/1); -+ AuDebugOn(err); -+ for (i = dpages.ndpage - 1; !err && i >= 0; i--) { -+ dpage = dpages.dpages + i; -+ dentries = dpage->dentries; -+ for (j = dpage->ndentry - 1; !err && j >= 0; j--) -+ AuDebugOn(au_digen_test(dentries[j], sigen)); -+ } -+ au_dpages_free(&dpages); -+} -+ -+void au_dbg_verify_kthread(void) -+{ -+ if (au_wkq_test()) { -+ au_dbg_blocked(); -+ /* -+ * It may be recursive, but udba=notify between two aufs mounts, -+ * where a single ro branch is shared, is not a problem. -+ */ -+ /* WARN_ON(1); */ -+ } -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int __init au_debug_init(void) -+{ -+ aufs_bindex_t bindex; -+ struct au_vdir_destr destr; -+ -+ bindex = -1; -+ AuDebugOn(bindex >= 0); -+ -+ destr.len = -1; -+ AuDebugOn(destr.len < NAME_MAX); -+ -+#ifdef CONFIG_4KSTACKS -+ pr_warn("CONFIG_4KSTACKS is defined.\n"); -+#endif -+ -+ return 0; -+} -diff --git a/fs/aufs/debug.h b/fs/aufs/debug.h -new file mode 100644 -index 0000000..039e6f8 ---- /dev/null -+++ b/fs/aufs/debug.h -@@ -0,0 +1,228 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * debug print functions -+ */ -+ -+#ifndef __AUFS_DEBUG_H__ -+#define __AUFS_DEBUG_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+#include -+#include -+#include -+ -+#ifdef CONFIG_AUFS_DEBUG -+#define AuDebugOn(a) BUG_ON(a) -+ -+/* module parameter */ -+extern atomic_t aufs_debug; -+static inline void au_debug_on(void) -+{ -+ atomic_inc(&aufs_debug); -+} -+static inline void au_debug_off(void) -+{ -+ atomic_dec_if_positive(&aufs_debug); -+} -+ -+static inline int au_debug_test(void) -+{ -+ return atomic_read(&aufs_debug) > 0; -+} -+#else -+#define AuDebugOn(a) do {} while (0) -+AuStubVoid(au_debug_on, void) -+AuStubVoid(au_debug_off, void) -+AuStubInt0(au_debug_test, void) -+#endif /* CONFIG_AUFS_DEBUG */ -+ -+#define param_check_atomic_t(name, p) __param_check(name, p, atomic_t) -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* debug print */ -+ -+#define AuDbg(fmt, ...) do { \ -+ if (au_debug_test()) \ -+ pr_debug("DEBUG: " fmt, ##__VA_ARGS__); \ -+} while (0) -+#define AuLabel(l) AuDbg(#l "\n") -+#define AuIOErr(fmt, ...) pr_err("I/O Error, " fmt, ##__VA_ARGS__) -+#define AuWarn1(fmt, ...) do { \ -+ static unsigned char _c; \ -+ if (!_c++) \ -+ pr_warn(fmt, ##__VA_ARGS__); \ -+} while (0) -+ -+#define AuErr1(fmt, ...) do { \ -+ static unsigned char _c; \ -+ if (!_c++) \ -+ pr_err(fmt, ##__VA_ARGS__); \ -+} while (0) -+ -+#define AuIOErr1(fmt, ...) do { \ -+ static unsigned char _c; \ -+ if (!_c++) \ -+ AuIOErr(fmt, ##__VA_ARGS__); \ -+} while (0) -+ -+#define AuUnsupportMsg "This operation is not supported." \ -+ " Please report this application to aufs-users ML." -+#define AuUnsupport(fmt, ...) do { \ -+ pr_err(AuUnsupportMsg "\n" fmt, ##__VA_ARGS__); \ -+ dump_stack(); \ -+} while (0) -+ -+#define AuTraceErr(e) do { \ -+ if (unlikely((e) < 0)) \ -+ AuDbg("err %d\n", (int)(e)); \ -+} while (0) -+ -+#define AuTraceErrPtr(p) do { \ -+ if (IS_ERR(p)) \ -+ AuDbg("err %ld\n", PTR_ERR(p)); \ -+} while (0) -+ -+/* dirty macros for debug print, use with "%.*s" and caution */ -+#define AuLNPair(qstr) (qstr)->len, (qstr)->name -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct dentry; -+#ifdef CONFIG_AUFS_DEBUG -+extern struct mutex au_dbg_mtx; -+extern char *au_plevel; -+struct au_nhash; -+void au_dpri_whlist(struct au_nhash *whlist); -+struct au_vdir; -+void au_dpri_vdir(struct au_vdir *vdir); -+struct inode; -+void au_dpri_inode(struct inode *inode); -+void au_dpri_dalias(struct inode *inode); -+void au_dpri_dentry(struct dentry *dentry); -+struct file; -+void au_dpri_file(struct file *filp); -+struct super_block; -+void au_dpri_sb(struct super_block *sb); -+ -+#define au_dbg_verify_dinode(d) __au_dbg_verify_dinode(d, __func__, __LINE__) -+void __au_dbg_verify_dinode(struct dentry *dentry, const char *func, int line); -+void au_dbg_verify_gen(struct dentry *parent, unsigned int sigen); -+void au_dbg_verify_kthread(void); -+ -+int __init au_debug_init(void); -+ -+#define AuDbgWhlist(w) do { \ -+ mutex_lock(&au_dbg_mtx); \ -+ AuDbg(#w "\n"); \ -+ au_dpri_whlist(w); \ -+ mutex_unlock(&au_dbg_mtx); \ -+} while (0) -+ -+#define AuDbgVdir(v) do { \ -+ mutex_lock(&au_dbg_mtx); \ -+ AuDbg(#v "\n"); \ -+ au_dpri_vdir(v); \ -+ mutex_unlock(&au_dbg_mtx); \ -+} while (0) -+ -+#define AuDbgInode(i) do { \ -+ mutex_lock(&au_dbg_mtx); \ -+ AuDbg(#i "\n"); \ -+ au_dpri_inode(i); \ -+ mutex_unlock(&au_dbg_mtx); \ -+} while (0) -+ -+#define AuDbgDAlias(i) do { \ -+ mutex_lock(&au_dbg_mtx); \ -+ AuDbg(#i "\n"); \ -+ au_dpri_dalias(i); \ -+ mutex_unlock(&au_dbg_mtx); \ -+} while (0) -+ -+#define AuDbgDentry(d) do { \ -+ mutex_lock(&au_dbg_mtx); \ -+ AuDbg(#d "\n"); \ -+ au_dpri_dentry(d); \ -+ mutex_unlock(&au_dbg_mtx); \ -+} while (0) -+ -+#define AuDbgFile(f) do { \ -+ mutex_lock(&au_dbg_mtx); \ -+ AuDbg(#f "\n"); \ -+ au_dpri_file(f); \ -+ mutex_unlock(&au_dbg_mtx); \ -+} while (0) -+ -+#define AuDbgSb(sb) do { \ -+ mutex_lock(&au_dbg_mtx); \ -+ AuDbg(#sb "\n"); \ -+ au_dpri_sb(sb); \ -+ mutex_unlock(&au_dbg_mtx); \ -+} while (0) -+ -+#define AuDbgSym(addr) do { \ -+ char sym[KSYM_SYMBOL_LEN]; \ -+ sprint_symbol(sym, (unsigned long)addr); \ -+ AuDbg("%s\n", sym); \ -+} while (0) -+#else -+AuStubVoid(au_dbg_verify_dinode, struct dentry *dentry) -+AuStubVoid(au_dbg_verify_dir_parent, struct dentry *dentry, unsigned int sigen) -+AuStubVoid(au_dbg_verify_nondir_parent, struct dentry *dentry, -+ unsigned int sigen) -+AuStubVoid(au_dbg_verify_gen, struct dentry *parent, unsigned int sigen) -+AuStubVoid(au_dbg_verify_kthread, void) -+AuStubInt0(__init au_debug_init, void) -+ -+#define AuDbgWhlist(w) do {} while (0) -+#define AuDbgVdir(v) do {} while (0) -+#define AuDbgInode(i) do {} while (0) -+#define AuDbgDAlias(i) do {} while (0) -+#define AuDbgDentry(d) do {} while (0) -+#define AuDbgFile(f) do {} while (0) -+#define AuDbgSb(sb) do {} while (0) -+#define AuDbgSym(addr) do {} while (0) -+#endif /* CONFIG_AUFS_DEBUG */ -+ -+/* ---------------------------------------------------------------------- */ -+ -+#ifdef CONFIG_AUFS_MAGIC_SYSRQ -+int __init au_sysrq_init(void); -+void au_sysrq_fin(void); -+ -+#ifdef CONFIG_HW_CONSOLE -+#define au_dbg_blocked() do { \ -+ WARN_ON(1); \ -+ handle_sysrq('w'); \ -+} while (0) -+#else -+AuStubVoid(au_dbg_blocked, void) -+#endif -+ -+#else -+AuStubInt0(__init au_sysrq_init, void) -+AuStubVoid(au_sysrq_fin, void) -+AuStubVoid(au_dbg_blocked, void) -+#endif /* CONFIG_AUFS_MAGIC_SYSRQ */ -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_DEBUG_H__ */ -diff --git a/fs/aufs/dentry.c b/fs/aufs/dentry.c -new file mode 100644 -index 0000000..ed56947 ---- /dev/null -+++ b/fs/aufs/dentry.c -@@ -0,0 +1,1129 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * lookup and dentry operations -+ */ -+ -+#include -+#include "aufs.h" -+ -+#define AuLkup_ALLOW_NEG 1 -+#define AuLkup_IGNORE_PERM (1 << 1) -+#define au_ftest_lkup(flags, name) ((flags) & AuLkup_##name) -+#define au_fset_lkup(flags, name) \ -+ do { (flags) |= AuLkup_##name; } while (0) -+#define au_fclr_lkup(flags, name) \ -+ do { (flags) &= ~AuLkup_##name; } while (0) -+ -+struct au_do_lookup_args { -+ unsigned int flags; -+ mode_t type; -+}; -+ -+/* -+ * returns positive/negative dentry, NULL or an error. -+ * NULL means whiteout-ed or not-found. -+ */ -+static struct dentry* -+au_do_lookup(struct dentry *h_parent, struct dentry *dentry, -+ aufs_bindex_t bindex, struct qstr *wh_name, -+ struct au_do_lookup_args *args) -+{ -+ struct dentry *h_dentry; -+ struct inode *h_inode; -+ struct au_branch *br; -+ int wh_found, opq; -+ unsigned char wh_able; -+ const unsigned char allow_neg = !!au_ftest_lkup(args->flags, ALLOW_NEG); -+ const unsigned char ignore_perm = !!au_ftest_lkup(args->flags, -+ IGNORE_PERM); -+ -+ wh_found = 0; -+ br = au_sbr(dentry->d_sb, bindex); -+ wh_able = !!au_br_whable(br->br_perm); -+ if (wh_able) -+ wh_found = au_wh_test(h_parent, wh_name, /*try_sio*/0); -+ h_dentry = ERR_PTR(wh_found); -+ if (!wh_found) -+ goto real_lookup; -+ if (unlikely(wh_found < 0)) -+ goto out; -+ -+ /* We found a whiteout */ -+ /* au_set_dbend(dentry, bindex); */ -+ au_set_dbwh(dentry, bindex); -+ if (!allow_neg) -+ return NULL; /* success */ -+ -+real_lookup: -+ if (!ignore_perm) -+ h_dentry = vfsub_lkup_one(&dentry->d_name, h_parent); -+ else -+ h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent); -+ if (IS_ERR(h_dentry)) { -+ if (PTR_ERR(h_dentry) == -ENAMETOOLONG -+ && !allow_neg) -+ h_dentry = NULL; -+ goto out; -+ } -+ -+ h_inode = h_dentry->d_inode; -+ if (!h_inode) { -+ if (!allow_neg) -+ goto out_neg; -+ } else if (wh_found -+ || (args->type && args->type != (h_inode->i_mode & S_IFMT))) -+ goto out_neg; -+ -+ if (au_dbend(dentry) <= bindex) -+ au_set_dbend(dentry, bindex); -+ if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry)) -+ au_set_dbstart(dentry, bindex); -+ au_set_h_dptr(dentry, bindex, h_dentry); -+ -+ if (!d_is_dir(h_dentry) -+ || !wh_able -+ || (d_is_positive(dentry) && !d_is_dir(dentry))) -+ goto out; /* success */ -+ -+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); -+ opq = au_diropq_test(h_dentry); -+ mutex_unlock(&h_inode->i_mutex); -+ if (opq > 0) -+ au_set_dbdiropq(dentry, bindex); -+ else if (unlikely(opq < 0)) { -+ au_set_h_dptr(dentry, bindex, NULL); -+ h_dentry = ERR_PTR(opq); -+ } -+ goto out; -+ -+out_neg: -+ dput(h_dentry); -+ h_dentry = NULL; -+out: -+ return h_dentry; -+} -+ -+static int au_test_shwh(struct super_block *sb, const struct qstr *name) -+{ -+ if (unlikely(!au_opt_test(au_mntflags(sb), SHWH) -+ && !strncmp(name->name, AUFS_WH_PFX, AUFS_WH_PFX_LEN))) -+ return -EPERM; -+ return 0; -+} -+ -+/* -+ * returns the number of lower positive dentries, -+ * otherwise an error. -+ * can be called at unlinking with @type is zero. -+ */ -+int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type) -+{ -+ int npositive, err; -+ aufs_bindex_t bindex, btail, bdiropq; -+ unsigned char isdir, dirperm1; -+ struct qstr whname; -+ struct au_do_lookup_args args = { -+ .flags = 0, -+ .type = type -+ }; -+ const struct qstr *name = &dentry->d_name; -+ struct dentry *parent; -+ struct inode *inode; -+ struct super_block *sb; -+ -+ sb = dentry->d_sb; -+ err = au_test_shwh(sb, name); -+ if (unlikely(err)) -+ goto out; -+ -+ err = au_wh_name_alloc(&whname, name); -+ if (unlikely(err)) -+ goto out; -+ -+ inode = dentry->d_inode; -+ isdir = !!d_is_dir(dentry); -+ if (!type) -+ au_fset_lkup(args.flags, ALLOW_NEG); -+ dirperm1 = !!au_opt_test(au_mntflags(sb), DIRPERM1); -+ -+ npositive = 0; -+ parent = dget_parent(dentry); -+ btail = au_dbtaildir(parent); -+ for (bindex = bstart; bindex <= btail; bindex++) { -+ struct dentry *h_parent, *h_dentry; -+ struct inode *h_inode, *h_dir; -+ -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (h_dentry) { -+ if (h_dentry->d_inode) -+ npositive++; -+ if (type != S_IFDIR) -+ break; -+ continue; -+ } -+ h_parent = au_h_dptr(parent, bindex); -+ if (!h_parent || !d_is_dir(h_parent)) -+ continue; -+ -+ h_dir = h_parent->d_inode; -+ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); -+ h_dentry = au_do_lookup(h_parent, dentry, bindex, &whname, -+ &args); -+ mutex_unlock(&h_dir->i_mutex); -+ err = PTR_ERR(h_dentry); -+ if (IS_ERR(h_dentry)) -+ goto out_parent; -+ if (h_dentry) -+ au_fclr_lkup(args.flags, ALLOW_NEG); -+ if (dirperm1) -+ au_fset_lkup(args.flags, IGNORE_PERM); -+ -+ if (au_dbwh(dentry) == bindex) -+ break; -+ if (!h_dentry) -+ continue; -+ h_inode = h_dentry->d_inode; -+ if (!h_inode) -+ continue; -+ npositive++; -+ if (!args.type) -+ args.type = h_inode->i_mode & S_IFMT; -+ if (args.type != S_IFDIR) -+ break; -+ else if (isdir) { -+ /* the type of lower may be different */ -+ bdiropq = au_dbdiropq(dentry); -+ if (bdiropq >= 0 && bdiropq <= bindex) -+ break; -+ } -+ } -+ -+ if (npositive) { -+ AuLabel(positive); -+ au_update_dbstart(dentry); -+ } -+ err = npositive; -+ if (unlikely(!au_opt_test(au_mntflags(sb), UDBA_NONE) -+ && au_dbstart(dentry) < 0)) { -+ err = -EIO; -+ AuIOErr("both of real entry and whiteout found, %pd, err %d\n", -+ dentry, err); -+ } -+ -+out_parent: -+ dput(parent); -+ kfree(whname.name); -+out: -+ return err; -+} -+ -+struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent) -+{ -+ struct dentry *dentry; -+ int wkq_err; -+ -+ if (!au_test_h_perm_sio(parent->d_inode, MAY_EXEC)) -+ dentry = vfsub_lkup_one(name, parent); -+ else { -+ struct vfsub_lkup_one_args args = { -+ .errp = &dentry, -+ .name = name, -+ .parent = parent -+ }; -+ -+ wkq_err = au_wkq_wait(vfsub_call_lkup_one, &args); -+ if (unlikely(wkq_err)) -+ dentry = ERR_PTR(wkq_err); -+ } -+ -+ return dentry; -+} -+ -+/* -+ * lookup @dentry on @bindex which should be negative. -+ */ -+int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex, int wh) -+{ -+ int err; -+ struct dentry *parent, *h_parent, *h_dentry; -+ struct au_branch *br; -+ -+ parent = dget_parent(dentry); -+ h_parent = au_h_dptr(parent, bindex); -+ br = au_sbr(dentry->d_sb, bindex); -+ if (wh) -+ h_dentry = au_whtmp_lkup(h_parent, br, &dentry->d_name); -+ else -+ h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent); -+ err = PTR_ERR(h_dentry); -+ if (IS_ERR(h_dentry)) -+ goto out; -+ if (unlikely(h_dentry->d_inode)) { -+ err = -EIO; -+ AuIOErr("%pd should be negative on b%d.\n", h_dentry, bindex); -+ dput(h_dentry); -+ goto out; -+ } -+ -+ err = 0; -+ if (bindex < au_dbstart(dentry)) -+ au_set_dbstart(dentry, bindex); -+ if (au_dbend(dentry) < bindex) -+ au_set_dbend(dentry, bindex); -+ au_set_h_dptr(dentry, bindex, h_dentry); -+ -+out: -+ dput(parent); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* subset of struct inode */ -+struct au_iattr { -+ unsigned long i_ino; -+ /* unsigned int i_nlink; */ -+ kuid_t i_uid; -+ kgid_t i_gid; -+ u64 i_version; -+/* -+ loff_t i_size; -+ blkcnt_t i_blocks; -+*/ -+ umode_t i_mode; -+}; -+ -+static void au_iattr_save(struct au_iattr *ia, struct inode *h_inode) -+{ -+ ia->i_ino = h_inode->i_ino; -+ /* ia->i_nlink = h_inode->i_nlink; */ -+ ia->i_uid = h_inode->i_uid; -+ ia->i_gid = h_inode->i_gid; -+ ia->i_version = h_inode->i_version; -+/* -+ ia->i_size = h_inode->i_size; -+ ia->i_blocks = h_inode->i_blocks; -+*/ -+ ia->i_mode = (h_inode->i_mode & S_IFMT); -+} -+ -+static int au_iattr_test(struct au_iattr *ia, struct inode *h_inode) -+{ -+ return ia->i_ino != h_inode->i_ino -+ /* || ia->i_nlink != h_inode->i_nlink */ -+ || !uid_eq(ia->i_uid, h_inode->i_uid) -+ || !gid_eq(ia->i_gid, h_inode->i_gid) -+ || ia->i_version != h_inode->i_version -+/* -+ || ia->i_size != h_inode->i_size -+ || ia->i_blocks != h_inode->i_blocks -+*/ -+ || ia->i_mode != (h_inode->i_mode & S_IFMT); -+} -+ -+static int au_h_verify_dentry(struct dentry *h_dentry, struct dentry *h_parent, -+ struct au_branch *br) -+{ -+ int err; -+ struct au_iattr ia; -+ struct inode *h_inode; -+ struct dentry *h_d; -+ struct super_block *h_sb; -+ -+ err = 0; -+ memset(&ia, -1, sizeof(ia)); -+ h_sb = h_dentry->d_sb; -+ h_inode = h_dentry->d_inode; -+ if (h_inode) -+ au_iattr_save(&ia, h_inode); -+ else if (au_test_nfs(h_sb) || au_test_fuse(h_sb)) -+ /* nfs d_revalidate may return 0 for negative dentry */ -+ /* fuse d_revalidate always return 0 for negative dentry */ -+ goto out; -+ -+ /* main purpose is namei.c:cached_lookup() and d_revalidate */ -+ h_d = vfsub_lkup_one(&h_dentry->d_name, h_parent); -+ err = PTR_ERR(h_d); -+ if (IS_ERR(h_d)) -+ goto out; -+ -+ err = 0; -+ if (unlikely(h_d != h_dentry -+ || h_d->d_inode != h_inode -+ || (h_inode && au_iattr_test(&ia, h_inode)))) -+ err = au_busy_or_stale(); -+ dput(h_d); -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir, -+ struct dentry *h_parent, struct au_branch *br) -+{ -+ int err; -+ -+ err = 0; -+ if (udba == AuOpt_UDBA_REVAL -+ && !au_test_fs_remote(h_dentry->d_sb)) { -+ IMustLock(h_dir); -+ err = (h_dentry->d_parent->d_inode != h_dir); -+ } else if (udba != AuOpt_UDBA_NONE) -+ err = au_h_verify_dentry(h_dentry, h_parent, br); -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_do_refresh_hdentry(struct dentry *dentry, struct dentry *parent) -+{ -+ int err; -+ aufs_bindex_t new_bindex, bindex, bend, bwh, bdiropq; -+ struct au_hdentry tmp, *p, *q; -+ struct au_dinfo *dinfo; -+ struct super_block *sb; -+ -+ DiMustWriteLock(dentry); -+ -+ sb = dentry->d_sb; -+ dinfo = au_di(dentry); -+ bend = dinfo->di_bend; -+ bwh = dinfo->di_bwh; -+ bdiropq = dinfo->di_bdiropq; -+ p = dinfo->di_hdentry + dinfo->di_bstart; -+ for (bindex = dinfo->di_bstart; bindex <= bend; bindex++, p++) { -+ if (!p->hd_dentry) -+ continue; -+ -+ new_bindex = au_br_index(sb, p->hd_id); -+ if (new_bindex == bindex) -+ continue; -+ -+ if (dinfo->di_bwh == bindex) -+ bwh = new_bindex; -+ if (dinfo->di_bdiropq == bindex) -+ bdiropq = new_bindex; -+ if (new_bindex < 0) { -+ au_hdput(p); -+ p->hd_dentry = NULL; -+ continue; -+ } -+ -+ /* swap two lower dentries, and loop again */ -+ q = dinfo->di_hdentry + new_bindex; -+ tmp = *q; -+ *q = *p; -+ *p = tmp; -+ if (tmp.hd_dentry) { -+ bindex--; -+ p--; -+ } -+ } -+ -+ dinfo->di_bwh = -1; -+ if (bwh >= 0 && bwh <= au_sbend(sb) && au_sbr_whable(sb, bwh)) -+ dinfo->di_bwh = bwh; -+ -+ dinfo->di_bdiropq = -1; -+ if (bdiropq >= 0 -+ && bdiropq <= au_sbend(sb) -+ && au_sbr_whable(sb, bdiropq)) -+ dinfo->di_bdiropq = bdiropq; -+ -+ err = -EIO; -+ dinfo->di_bstart = -1; -+ dinfo->di_bend = -1; -+ bend = au_dbend(parent); -+ p = dinfo->di_hdentry; -+ for (bindex = 0; bindex <= bend; bindex++, p++) -+ if (p->hd_dentry) { -+ dinfo->di_bstart = bindex; -+ break; -+ } -+ -+ if (dinfo->di_bstart >= 0) { -+ p = dinfo->di_hdentry + bend; -+ for (bindex = bend; bindex >= 0; bindex--, p--) -+ if (p->hd_dentry) { -+ dinfo->di_bend = bindex; -+ err = 0; -+ break; -+ } -+ } -+ -+ return err; -+} -+ -+static void au_do_hide(struct dentry *dentry) -+{ -+ struct inode *inode; -+ -+ inode = dentry->d_inode; -+ if (inode) { -+ if (!S_ISDIR(inode->i_mode)) { -+ if (inode->i_nlink && !d_unhashed(dentry)) -+ drop_nlink(inode); -+ } else { -+ clear_nlink(inode); -+ /* stop next lookup */ -+ inode->i_flags |= S_DEAD; -+ } -+ smp_mb(); /* necessary? */ -+ } -+ d_drop(dentry); -+} -+ -+static int au_hide_children(struct dentry *parent) -+{ -+ int err, i, j, ndentry; -+ struct au_dcsub_pages dpages; -+ struct au_dpage *dpage; -+ struct dentry *dentry; -+ -+ err = au_dpages_init(&dpages, GFP_NOFS); -+ if (unlikely(err)) -+ goto out; -+ err = au_dcsub_pages(&dpages, parent, NULL, NULL); -+ if (unlikely(err)) -+ goto out_dpages; -+ -+ /* in reverse order */ -+ for (i = dpages.ndpage - 1; i >= 0; i--) { -+ dpage = dpages.dpages + i; -+ ndentry = dpage->ndentry; -+ for (j = ndentry - 1; j >= 0; j--) { -+ dentry = dpage->dentries[j]; -+ if (dentry != parent) -+ au_do_hide(dentry); -+ } -+ } -+ -+out_dpages: -+ au_dpages_free(&dpages); -+out: -+ return err; -+} -+ -+static void au_hide(struct dentry *dentry) -+{ -+ int err; -+ -+ AuDbgDentry(dentry); -+ if (d_is_dir(dentry)) { -+ /* shrink_dcache_parent(dentry); */ -+ err = au_hide_children(dentry); -+ if (unlikely(err)) -+ AuIOErr("%pd, failed hiding children, ignored %d\n", -+ dentry, err); -+ } -+ au_do_hide(dentry); -+} -+ -+/* -+ * By adding a dirty branch, a cached dentry may be affected in various ways. -+ * -+ * a dirty branch is added -+ * - on the top of layers -+ * - in the middle of layers -+ * - to the bottom of layers -+ * -+ * on the added branch there exists -+ * - a whiteout -+ * - a diropq -+ * - a same named entry -+ * + exist -+ * * negative --> positive -+ * * positive --> positive -+ * - type is unchanged -+ * - type is changed -+ * + doesn't exist -+ * * negative --> negative -+ * * positive --> negative (rejected by au_br_del() for non-dir case) -+ * - none -+ */ -+static int au_refresh_by_dinfo(struct dentry *dentry, struct au_dinfo *dinfo, -+ struct au_dinfo *tmp) -+{ -+ int err; -+ aufs_bindex_t bindex, bend; -+ struct { -+ struct dentry *dentry; -+ struct inode *inode; -+ mode_t mode; -+ } orig_h, tmp_h = { -+ .dentry = NULL -+ }; -+ struct au_hdentry *hd; -+ struct inode *inode, *h_inode; -+ struct dentry *h_dentry; -+ -+ err = 0; -+ AuDebugOn(dinfo->di_bstart < 0); -+ orig_h.dentry = dinfo->di_hdentry[dinfo->di_bstart].hd_dentry; -+ orig_h.inode = orig_h.dentry->d_inode; -+ orig_h.mode = 0; -+ if (orig_h.inode) -+ orig_h.mode = orig_h.inode->i_mode & S_IFMT; -+ if (tmp->di_bstart >= 0) { -+ tmp_h.dentry = tmp->di_hdentry[tmp->di_bstart].hd_dentry; -+ tmp_h.inode = tmp_h.dentry->d_inode; -+ if (tmp_h.inode) -+ tmp_h.mode = tmp_h.inode->i_mode & S_IFMT; -+ } -+ -+ inode = dentry->d_inode; -+ if (!orig_h.inode) { -+ AuDbg("nagative originally\n"); -+ if (inode) { -+ au_hide(dentry); -+ goto out; -+ } -+ AuDebugOn(inode); -+ AuDebugOn(dinfo->di_bstart != dinfo->di_bend); -+ AuDebugOn(dinfo->di_bdiropq != -1); -+ -+ if (!tmp_h.inode) { -+ AuDbg("negative --> negative\n"); -+ /* should have only one negative lower */ -+ if (tmp->di_bstart >= 0 -+ && tmp->di_bstart < dinfo->di_bstart) { -+ AuDebugOn(tmp->di_bstart != tmp->di_bend); -+ AuDebugOn(dinfo->di_bstart != dinfo->di_bend); -+ au_set_h_dptr(dentry, dinfo->di_bstart, NULL); -+ au_di_cp(dinfo, tmp); -+ hd = tmp->di_hdentry + tmp->di_bstart; -+ au_set_h_dptr(dentry, tmp->di_bstart, -+ dget(hd->hd_dentry)); -+ } -+ au_dbg_verify_dinode(dentry); -+ } else { -+ AuDbg("negative --> positive\n"); -+ /* -+ * similar to the behaviour of creating with bypassing -+ * aufs. -+ * unhash it in order to force an error in the -+ * succeeding create operation. -+ * we should not set S_DEAD here. -+ */ -+ d_drop(dentry); -+ /* au_di_swap(tmp, dinfo); */ -+ au_dbg_verify_dinode(dentry); -+ } -+ } else { -+ AuDbg("positive originally\n"); -+ /* inode may be NULL */ -+ AuDebugOn(inode && (inode->i_mode & S_IFMT) != orig_h.mode); -+ if (!tmp_h.inode) { -+ AuDbg("positive --> negative\n"); -+ /* or bypassing aufs */ -+ au_hide(dentry); -+ if (tmp->di_bwh >= 0 && tmp->di_bwh <= dinfo->di_bstart) -+ dinfo->di_bwh = tmp->di_bwh; -+ if (inode) -+ err = au_refresh_hinode_self(inode); -+ au_dbg_verify_dinode(dentry); -+ } else if (orig_h.mode == tmp_h.mode) { -+ AuDbg("positive --> positive, same type\n"); -+ if (!S_ISDIR(orig_h.mode) -+ && dinfo->di_bstart > tmp->di_bstart) { -+ /* -+ * similar to the behaviour of removing and -+ * creating. -+ */ -+ au_hide(dentry); -+ if (inode) -+ err = au_refresh_hinode_self(inode); -+ au_dbg_verify_dinode(dentry); -+ } else { -+ /* fill empty slots */ -+ if (dinfo->di_bstart > tmp->di_bstart) -+ dinfo->di_bstart = tmp->di_bstart; -+ if (dinfo->di_bend < tmp->di_bend) -+ dinfo->di_bend = tmp->di_bend; -+ dinfo->di_bwh = tmp->di_bwh; -+ dinfo->di_bdiropq = tmp->di_bdiropq; -+ hd = tmp->di_hdentry; -+ bend = dinfo->di_bend; -+ for (bindex = tmp->di_bstart; bindex <= bend; -+ bindex++) { -+ if (au_h_dptr(dentry, bindex)) -+ continue; -+ h_dentry = hd[bindex].hd_dentry; -+ if (!h_dentry) -+ continue; -+ h_inode = h_dentry->d_inode; -+ AuDebugOn(!h_inode); -+ AuDebugOn(orig_h.mode -+ != (h_inode->i_mode -+ & S_IFMT)); -+ au_set_h_dptr(dentry, bindex, -+ dget(h_dentry)); -+ } -+ err = au_refresh_hinode(inode, dentry); -+ au_dbg_verify_dinode(dentry); -+ } -+ } else { -+ AuDbg("positive --> positive, different type\n"); -+ /* similar to the behaviour of removing and creating */ -+ au_hide(dentry); -+ if (inode) -+ err = au_refresh_hinode_self(inode); -+ au_dbg_verify_dinode(dentry); -+ } -+ } -+ -+out: -+ return err; -+} -+ -+void au_refresh_dop(struct dentry *dentry, int force_reval) -+{ -+ const struct dentry_operations *dop -+ = force_reval ? &aufs_dop : dentry->d_sb->s_d_op; -+ static const unsigned int mask -+ = DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE; -+ -+ BUILD_BUG_ON(sizeof(mask) != sizeof(dentry->d_flags)); -+ -+ if (dentry->d_op == dop) -+ return; -+ -+ AuDbg("%pd\n", dentry); -+ spin_lock(&dentry->d_lock); -+ if (dop == &aufs_dop) -+ dentry->d_flags |= mask; -+ else -+ dentry->d_flags &= ~mask; -+ dentry->d_op = dop; -+ spin_unlock(&dentry->d_lock); -+} -+ -+int au_refresh_dentry(struct dentry *dentry, struct dentry *parent) -+{ -+ int err, ebrange; -+ unsigned int sigen; -+ struct au_dinfo *dinfo, *tmp; -+ struct super_block *sb; -+ struct inode *inode; -+ -+ DiMustWriteLock(dentry); -+ AuDebugOn(IS_ROOT(dentry)); -+ AuDebugOn(!parent->d_inode); -+ -+ sb = dentry->d_sb; -+ inode = dentry->d_inode; -+ sigen = au_sigen(sb); -+ err = au_digen_test(parent, sigen); -+ if (unlikely(err)) -+ goto out; -+ -+ dinfo = au_di(dentry); -+ err = au_di_realloc(dinfo, au_sbend(sb) + 1); -+ if (unlikely(err)) -+ goto out; -+ ebrange = au_dbrange_test(dentry); -+ if (!ebrange) -+ ebrange = au_do_refresh_hdentry(dentry, parent); -+ -+ if (d_unhashed(dentry) || ebrange /* || dinfo->di_tmpfile */) { -+ AuDebugOn(au_dbstart(dentry) < 0 && au_dbend(dentry) >= 0); -+ if (inode) -+ err = au_refresh_hinode_self(inode); -+ au_dbg_verify_dinode(dentry); -+ if (!err) -+ goto out_dgen; /* success */ -+ goto out; -+ } -+ -+ /* temporary dinfo */ -+ AuDbgDentry(dentry); -+ err = -ENOMEM; -+ tmp = au_di_alloc(sb, AuLsc_DI_TMP); -+ if (unlikely(!tmp)) -+ goto out; -+ au_di_swap(tmp, dinfo); -+ /* returns the number of positive dentries */ -+ /* -+ * if current working dir is removed, it returns an error. -+ * but the dentry is legal. -+ */ -+ err = au_lkup_dentry(dentry, /*bstart*/0, /*type*/0); -+ AuDbgDentry(dentry); -+ au_di_swap(tmp, dinfo); -+ if (err == -ENOENT) -+ err = 0; -+ if (err >= 0) { -+ /* compare/refresh by dinfo */ -+ AuDbgDentry(dentry); -+ err = au_refresh_by_dinfo(dentry, dinfo, tmp); -+ au_dbg_verify_dinode(dentry); -+ AuTraceErr(err); -+ } -+ au_rw_write_unlock(&tmp->di_rwsem); -+ au_di_free(tmp); -+ if (unlikely(err)) -+ goto out; -+ -+out_dgen: -+ au_update_digen(dentry); -+out: -+ if (unlikely(err && !(dentry->d_flags & DCACHE_NFSFS_RENAMED))) { -+ AuIOErr("failed refreshing %pd, %d\n", dentry, err); -+ AuDbgDentry(dentry); -+ } -+ AuTraceErr(err); -+ return err; -+} -+ -+static int au_do_h_d_reval(struct dentry *h_dentry, unsigned int flags, -+ struct dentry *dentry, aufs_bindex_t bindex) -+{ -+ int err, valid; -+ -+ err = 0; -+ if (!(h_dentry->d_flags & DCACHE_OP_REVALIDATE)) -+ goto out; -+ -+ AuDbg("b%d\n", bindex); -+ /* -+ * gave up supporting LOOKUP_CREATE/OPEN for lower fs, -+ * due to whiteout and branch permission. -+ */ -+ flags &= ~(/*LOOKUP_PARENT |*/ LOOKUP_OPEN | LOOKUP_CREATE -+ | LOOKUP_FOLLOW | LOOKUP_EXCL); -+ /* it may return tri-state */ -+ valid = h_dentry->d_op->d_revalidate(h_dentry, flags); -+ -+ if (unlikely(valid < 0)) -+ err = valid; -+ else if (!valid) -+ err = -EINVAL; -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* todo: remove this */ -+static int h_d_revalidate(struct dentry *dentry, struct inode *inode, -+ unsigned int flags, int do_udba) -+{ -+ int err; -+ umode_t mode, h_mode; -+ aufs_bindex_t bindex, btail, bstart, ibs, ibe; -+ unsigned char plus, unhashed, is_root, h_plus, h_nfs, tmpfile; -+ struct inode *h_inode, *h_cached_inode; -+ struct dentry *h_dentry; -+ struct qstr *name, *h_name; -+ -+ err = 0; -+ plus = 0; -+ mode = 0; -+ ibs = -1; -+ ibe = -1; -+ unhashed = !!d_unhashed(dentry); -+ is_root = !!IS_ROOT(dentry); -+ name = &dentry->d_name; -+ tmpfile = au_di(dentry)->di_tmpfile; -+ -+ /* -+ * Theoretically, REVAL test should be unnecessary in case of -+ * {FS,I}NOTIFY. -+ * But {fs,i}notify doesn't fire some necessary events, -+ * IN_ATTRIB for atime/nlink/pageio -+ * Let's do REVAL test too. -+ */ -+ if (do_udba && inode) { -+ mode = (inode->i_mode & S_IFMT); -+ plus = (inode->i_nlink > 0); -+ ibs = au_ibstart(inode); -+ ibe = au_ibend(inode); -+ } -+ -+ bstart = au_dbstart(dentry); -+ btail = bstart; -+ if (inode && S_ISDIR(inode->i_mode)) -+ btail = au_dbtaildir(dentry); -+ for (bindex = bstart; bindex <= btail; bindex++) { -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (!h_dentry) -+ continue; -+ -+ AuDbg("b%d, %pd\n", bindex, h_dentry); -+ h_nfs = !!au_test_nfs(h_dentry->d_sb); -+ spin_lock(&h_dentry->d_lock); -+ h_name = &h_dentry->d_name; -+ if (unlikely(do_udba -+ && !is_root -+ && ((!h_nfs -+ && (unhashed != !!d_unhashed(h_dentry) -+ || (!tmpfile -+ && !au_qstreq(name, h_name)) -+ )) -+ || (h_nfs -+ && !(flags & LOOKUP_OPEN) -+ && (h_dentry->d_flags -+ & DCACHE_NFSFS_RENAMED))) -+ )) { -+ int h_unhashed; -+ -+ h_unhashed = d_unhashed(h_dentry); -+ spin_unlock(&h_dentry->d_lock); -+ AuDbg("unhash 0x%x 0x%x, %pd %pd\n", -+ unhashed, h_unhashed, dentry, h_dentry); -+ goto err; -+ } -+ spin_unlock(&h_dentry->d_lock); -+ -+ err = au_do_h_d_reval(h_dentry, flags, dentry, bindex); -+ if (unlikely(err)) -+ /* do not goto err, to keep the errno */ -+ break; -+ -+ /* todo: plink too? */ -+ if (!do_udba) -+ continue; -+ -+ /* UDBA tests */ -+ h_inode = h_dentry->d_inode; -+ if (unlikely(!!inode != !!h_inode)) -+ goto err; -+ -+ h_plus = plus; -+ h_mode = mode; -+ h_cached_inode = h_inode; -+ if (h_inode) { -+ h_mode = (h_inode->i_mode & S_IFMT); -+ h_plus = (h_inode->i_nlink > 0); -+ } -+ if (inode && ibs <= bindex && bindex <= ibe) -+ h_cached_inode = au_h_iptr(inode, bindex); -+ -+ if (!h_nfs) { -+ if (unlikely(plus != h_plus && !tmpfile)) -+ goto err; -+ } else { -+ if (unlikely(!(h_dentry->d_flags & DCACHE_NFSFS_RENAMED) -+ && !is_root -+ && !IS_ROOT(h_dentry) -+ && unhashed != d_unhashed(h_dentry))) -+ goto err; -+ } -+ if (unlikely(mode != h_mode -+ || h_cached_inode != h_inode)) -+ goto err; -+ continue; -+ -+err: -+ err = -EINVAL; -+ break; -+ } -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+/* todo: consolidate with do_refresh() and au_reval_for_attr() */ -+static int simple_reval_dpath(struct dentry *dentry, unsigned int sigen) -+{ -+ int err; -+ struct dentry *parent; -+ -+ if (!au_digen_test(dentry, sigen)) -+ return 0; -+ -+ parent = dget_parent(dentry); -+ di_read_lock_parent(parent, AuLock_IR); -+ AuDebugOn(au_digen_test(parent, sigen)); -+ au_dbg_verify_gen(parent, sigen); -+ err = au_refresh_dentry(dentry, parent); -+ di_read_unlock(parent, AuLock_IR); -+ dput(parent); -+ AuTraceErr(err); -+ return err; -+} -+ -+int au_reval_dpath(struct dentry *dentry, unsigned int sigen) -+{ -+ int err; -+ struct dentry *d, *parent; -+ struct inode *inode; -+ -+ if (!au_ftest_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIR)) -+ return simple_reval_dpath(dentry, sigen); -+ -+ /* slow loop, keep it simple and stupid */ -+ /* cf: au_cpup_dirs() */ -+ err = 0; -+ parent = NULL; -+ while (au_digen_test(dentry, sigen)) { -+ d = dentry; -+ while (1) { -+ dput(parent); -+ parent = dget_parent(d); -+ if (!au_digen_test(parent, sigen)) -+ break; -+ d = parent; -+ } -+ -+ inode = d->d_inode; -+ if (d != dentry) -+ di_write_lock_child2(d); -+ -+ /* someone might update our dentry while we were sleeping */ -+ if (au_digen_test(d, sigen)) { -+ /* -+ * todo: consolidate with simple_reval_dpath(), -+ * do_refresh() and au_reval_for_attr(). -+ */ -+ di_read_lock_parent(parent, AuLock_IR); -+ err = au_refresh_dentry(d, parent); -+ di_read_unlock(parent, AuLock_IR); -+ } -+ -+ if (d != dentry) -+ di_write_unlock(d); -+ dput(parent); -+ if (unlikely(err)) -+ break; -+ } -+ -+ return err; -+} -+ -+/* -+ * if valid returns 1, otherwise 0. -+ */ -+static int aufs_d_revalidate(struct dentry *dentry, unsigned int flags) -+{ -+ int valid, err; -+ unsigned int sigen; -+ unsigned char do_udba; -+ struct super_block *sb; -+ struct inode *inode; -+ -+ /* todo: support rcu-walk? */ -+ if (flags & LOOKUP_RCU) -+ return -ECHILD; -+ -+ valid = 0; -+ if (unlikely(!au_di(dentry))) -+ goto out; -+ -+ valid = 1; -+ sb = dentry->d_sb; -+ /* -+ * todo: very ugly -+ * i_mutex of parent dir may be held, -+ * but we should not return 'invalid' due to busy. -+ */ -+ err = aufs_read_lock(dentry, AuLock_FLUSH | AuLock_DW | AuLock_NOPLM); -+ if (unlikely(err)) { -+ valid = err; -+ AuTraceErr(err); -+ goto out; -+ } -+ inode = dentry->d_inode; -+ if (unlikely(inode && is_bad_inode(inode))) { -+ err = -EINVAL; -+ AuTraceErr(err); -+ goto out_dgrade; -+ } -+ if (unlikely(au_dbrange_test(dentry))) { -+ err = -EINVAL; -+ AuTraceErr(err); -+ goto out_dgrade; -+ } -+ -+ sigen = au_sigen(sb); -+ if (au_digen_test(dentry, sigen)) { -+ AuDebugOn(IS_ROOT(dentry)); -+ err = au_reval_dpath(dentry, sigen); -+ if (unlikely(err)) { -+ AuTraceErr(err); -+ goto out_dgrade; -+ } -+ } -+ di_downgrade_lock(dentry, AuLock_IR); -+ -+ err = -EINVAL; -+ if (!(flags & (LOOKUP_OPEN | LOOKUP_EMPTY)) -+ && inode -+ && !(inode->i_state && I_LINKABLE) -+ && (IS_DEADDIR(inode) || !inode->i_nlink)) { -+ AuTraceErr(err); -+ goto out_inval; -+ } -+ -+ do_udba = !au_opt_test(au_mntflags(sb), UDBA_NONE); -+ if (do_udba && inode) { -+ aufs_bindex_t bstart = au_ibstart(inode); -+ struct inode *h_inode; -+ -+ if (bstart >= 0) { -+ h_inode = au_h_iptr(inode, bstart); -+ if (h_inode && au_test_higen(inode, h_inode)) { -+ AuTraceErr(err); -+ goto out_inval; -+ } -+ } -+ } -+ -+ err = h_d_revalidate(dentry, inode, flags, do_udba); -+ if (unlikely(!err && do_udba && au_dbstart(dentry) < 0)) { -+ err = -EIO; -+ AuDbg("both of real entry and whiteout found, %p, err %d\n", -+ dentry, err); -+ } -+ goto out_inval; -+ -+out_dgrade: -+ di_downgrade_lock(dentry, AuLock_IR); -+out_inval: -+ aufs_read_unlock(dentry, AuLock_IR); -+ AuTraceErr(err); -+ valid = !err; -+out: -+ if (!valid) { -+ AuDbg("%pd invalid, %d\n", dentry, valid); -+ d_drop(dentry); -+ } -+ return valid; -+} -+ -+static void aufs_d_release(struct dentry *dentry) -+{ -+ if (au_di(dentry)) { -+ au_di_fin(dentry); -+ au_hn_di_reinit(dentry); -+ } -+} -+ -+const struct dentry_operations aufs_dop = { -+ .d_revalidate = aufs_d_revalidate, -+ .d_weak_revalidate = aufs_d_revalidate, -+ .d_release = aufs_d_release -+}; -+ -+/* aufs_dop without d_revalidate */ -+const struct dentry_operations aufs_dop_noreval = { -+ .d_release = aufs_d_release -+}; -diff --git a/fs/aufs/dentry.h b/fs/aufs/dentry.h -new file mode 100644 -index 0000000..4006484 ---- /dev/null -+++ b/fs/aufs/dentry.h -@@ -0,0 +1,234 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * lookup and dentry operations -+ */ -+ -+#ifndef __AUFS_DENTRY_H__ -+#define __AUFS_DENTRY_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+#include "rwsem.h" -+ -+struct au_hdentry { -+ struct dentry *hd_dentry; -+ aufs_bindex_t hd_id; -+}; -+ -+struct au_dinfo { -+ atomic_t di_generation; -+ -+ struct au_rwsem di_rwsem; -+ aufs_bindex_t di_bstart, di_bend, di_bwh, di_bdiropq; -+ unsigned char di_tmpfile; /* to allow the different name */ -+ struct au_hdentry *di_hdentry; -+} ____cacheline_aligned_in_smp; -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* dentry.c */ -+extern const struct dentry_operations aufs_dop, aufs_dop_noreval; -+struct au_branch; -+struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent); -+int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir, -+ struct dentry *h_parent, struct au_branch *br); -+ -+int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type); -+int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex, int wh); -+int au_refresh_dentry(struct dentry *dentry, struct dentry *parent); -+int au_reval_dpath(struct dentry *dentry, unsigned int sigen); -+void au_refresh_dop(struct dentry *dentry, int force_reval); -+ -+/* dinfo.c */ -+void au_di_init_once(void *_di); -+struct au_dinfo *au_di_alloc(struct super_block *sb, unsigned int lsc); -+void au_di_free(struct au_dinfo *dinfo); -+void au_di_swap(struct au_dinfo *a, struct au_dinfo *b); -+void au_di_cp(struct au_dinfo *dst, struct au_dinfo *src); -+int au_di_init(struct dentry *dentry); -+void au_di_fin(struct dentry *dentry); -+int au_di_realloc(struct au_dinfo *dinfo, int nbr); -+ -+void di_read_lock(struct dentry *d, int flags, unsigned int lsc); -+void di_read_unlock(struct dentry *d, int flags); -+void di_downgrade_lock(struct dentry *d, int flags); -+void di_write_lock(struct dentry *d, unsigned int lsc); -+void di_write_unlock(struct dentry *d); -+void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir); -+void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir); -+void di_write_unlock2(struct dentry *d1, struct dentry *d2); -+ -+struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex); -+struct dentry *au_h_d_alias(struct dentry *dentry, aufs_bindex_t bindex); -+aufs_bindex_t au_dbtail(struct dentry *dentry); -+aufs_bindex_t au_dbtaildir(struct dentry *dentry); -+ -+void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex, -+ struct dentry *h_dentry); -+int au_digen_test(struct dentry *dentry, unsigned int sigen); -+int au_dbrange_test(struct dentry *dentry); -+void au_update_digen(struct dentry *dentry); -+void au_update_dbrange(struct dentry *dentry, int do_put_zero); -+void au_update_dbstart(struct dentry *dentry); -+void au_update_dbend(struct dentry *dentry); -+int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry); -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline struct au_dinfo *au_di(struct dentry *dentry) -+{ -+ return dentry->d_fsdata; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* lock subclass for dinfo */ -+enum { -+ AuLsc_DI_CHILD, /* child first */ -+ AuLsc_DI_CHILD2, /* rename(2), link(2), and cpup at hnotify */ -+ AuLsc_DI_CHILD3, /* copyup dirs */ -+ AuLsc_DI_PARENT, -+ AuLsc_DI_PARENT2, -+ AuLsc_DI_PARENT3, -+ AuLsc_DI_TMP /* temp for replacing dinfo */ -+}; -+ -+/* -+ * di_read_lock_child, di_write_lock_child, -+ * di_read_lock_child2, di_write_lock_child2, -+ * di_read_lock_child3, di_write_lock_child3, -+ * di_read_lock_parent, di_write_lock_parent, -+ * di_read_lock_parent2, di_write_lock_parent2, -+ * di_read_lock_parent3, di_write_lock_parent3, -+ */ -+#define AuReadLockFunc(name, lsc) \ -+static inline void di_read_lock_##name(struct dentry *d, int flags) \ -+{ di_read_lock(d, flags, AuLsc_DI_##lsc); } -+ -+#define AuWriteLockFunc(name, lsc) \ -+static inline void di_write_lock_##name(struct dentry *d) \ -+{ di_write_lock(d, AuLsc_DI_##lsc); } -+ -+#define AuRWLockFuncs(name, lsc) \ -+ AuReadLockFunc(name, lsc) \ -+ AuWriteLockFunc(name, lsc) -+ -+AuRWLockFuncs(child, CHILD); -+AuRWLockFuncs(child2, CHILD2); -+AuRWLockFuncs(child3, CHILD3); -+AuRWLockFuncs(parent, PARENT); -+AuRWLockFuncs(parent2, PARENT2); -+AuRWLockFuncs(parent3, PARENT3); -+ -+#undef AuReadLockFunc -+#undef AuWriteLockFunc -+#undef AuRWLockFuncs -+ -+#define DiMustNoWaiters(d) AuRwMustNoWaiters(&au_di(d)->di_rwsem) -+#define DiMustAnyLock(d) AuRwMustAnyLock(&au_di(d)->di_rwsem) -+#define DiMustWriteLock(d) AuRwMustWriteLock(&au_di(d)->di_rwsem) -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* todo: memory barrier? */ -+static inline unsigned int au_digen(struct dentry *d) -+{ -+ return atomic_read(&au_di(d)->di_generation); -+} -+ -+static inline void au_h_dentry_init(struct au_hdentry *hdentry) -+{ -+ hdentry->hd_dentry = NULL; -+} -+ -+static inline void au_hdput(struct au_hdentry *hd) -+{ -+ if (hd) -+ dput(hd->hd_dentry); -+} -+ -+static inline aufs_bindex_t au_dbstart(struct dentry *dentry) -+{ -+ DiMustAnyLock(dentry); -+ return au_di(dentry)->di_bstart; -+} -+ -+static inline aufs_bindex_t au_dbend(struct dentry *dentry) -+{ -+ DiMustAnyLock(dentry); -+ return au_di(dentry)->di_bend; -+} -+ -+static inline aufs_bindex_t au_dbwh(struct dentry *dentry) -+{ -+ DiMustAnyLock(dentry); -+ return au_di(dentry)->di_bwh; -+} -+ -+static inline aufs_bindex_t au_dbdiropq(struct dentry *dentry) -+{ -+ DiMustAnyLock(dentry); -+ return au_di(dentry)->di_bdiropq; -+} -+ -+/* todo: hard/soft set? */ -+static inline void au_set_dbstart(struct dentry *dentry, aufs_bindex_t bindex) -+{ -+ DiMustWriteLock(dentry); -+ au_di(dentry)->di_bstart = bindex; -+} -+ -+static inline void au_set_dbend(struct dentry *dentry, aufs_bindex_t bindex) -+{ -+ DiMustWriteLock(dentry); -+ au_di(dentry)->di_bend = bindex; -+} -+ -+static inline void au_set_dbwh(struct dentry *dentry, aufs_bindex_t bindex) -+{ -+ DiMustWriteLock(dentry); -+ /* dbwh can be outside of bstart - bend range */ -+ au_di(dentry)->di_bwh = bindex; -+} -+ -+static inline void au_set_dbdiropq(struct dentry *dentry, aufs_bindex_t bindex) -+{ -+ DiMustWriteLock(dentry); -+ au_di(dentry)->di_bdiropq = bindex; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+#ifdef CONFIG_AUFS_HNOTIFY -+static inline void au_digen_dec(struct dentry *d) -+{ -+ atomic_dec(&au_di(d)->di_generation); -+} -+ -+static inline void au_hn_di_reinit(struct dentry *dentry) -+{ -+ dentry->d_fsdata = NULL; -+} -+#else -+AuStubVoid(au_hn_di_reinit, struct dentry *dentry __maybe_unused) -+#endif /* CONFIG_AUFS_HNOTIFY */ -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_DENTRY_H__ */ -diff --git a/fs/aufs/dinfo.c b/fs/aufs/dinfo.c -new file mode 100644 -index 0000000..28c02b3 ---- /dev/null -+++ b/fs/aufs/dinfo.c -@@ -0,0 +1,544 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * dentry private data -+ */ -+ -+#include "aufs.h" -+ -+void au_di_init_once(void *_dinfo) -+{ -+ struct au_dinfo *dinfo = _dinfo; -+ static struct lock_class_key aufs_di; -+ -+ au_rw_init(&dinfo->di_rwsem); -+ au_rw_class(&dinfo->di_rwsem, &aufs_di); -+} -+ -+struct au_dinfo *au_di_alloc(struct super_block *sb, unsigned int lsc) -+{ -+ struct au_dinfo *dinfo; -+ int nbr, i; -+ -+ dinfo = au_cache_alloc_dinfo(); -+ if (unlikely(!dinfo)) -+ goto out; -+ -+ nbr = au_sbend(sb) + 1; -+ if (nbr <= 0) -+ nbr = 1; -+ dinfo->di_hdentry = kcalloc(nbr, sizeof(*dinfo->di_hdentry), GFP_NOFS); -+ if (dinfo->di_hdentry) { -+ au_rw_write_lock_nested(&dinfo->di_rwsem, lsc); -+ dinfo->di_bstart = -1; -+ dinfo->di_bend = -1; -+ dinfo->di_bwh = -1; -+ dinfo->di_bdiropq = -1; -+ dinfo->di_tmpfile = 0; -+ for (i = 0; i < nbr; i++) -+ dinfo->di_hdentry[i].hd_id = -1; -+ goto out; -+ } -+ -+ au_cache_free_dinfo(dinfo); -+ dinfo = NULL; -+ -+out: -+ return dinfo; -+} -+ -+void au_di_free(struct au_dinfo *dinfo) -+{ -+ struct au_hdentry *p; -+ aufs_bindex_t bend, bindex; -+ -+ /* dentry may not be revalidated */ -+ bindex = dinfo->di_bstart; -+ if (bindex >= 0) { -+ bend = dinfo->di_bend; -+ p = dinfo->di_hdentry + bindex; -+ while (bindex++ <= bend) -+ au_hdput(p++); -+ } -+ kfree(dinfo->di_hdentry); -+ au_cache_free_dinfo(dinfo); -+} -+ -+void au_di_swap(struct au_dinfo *a, struct au_dinfo *b) -+{ -+ struct au_hdentry *p; -+ aufs_bindex_t bi; -+ -+ AuRwMustWriteLock(&a->di_rwsem); -+ AuRwMustWriteLock(&b->di_rwsem); -+ -+#define DiSwap(v, name) \ -+ do { \ -+ v = a->di_##name; \ -+ a->di_##name = b->di_##name; \ -+ b->di_##name = v; \ -+ } while (0) -+ -+ DiSwap(p, hdentry); -+ DiSwap(bi, bstart); -+ DiSwap(bi, bend); -+ DiSwap(bi, bwh); -+ DiSwap(bi, bdiropq); -+ /* smp_mb(); */ -+ -+#undef DiSwap -+} -+ -+void au_di_cp(struct au_dinfo *dst, struct au_dinfo *src) -+{ -+ AuRwMustWriteLock(&dst->di_rwsem); -+ AuRwMustWriteLock(&src->di_rwsem); -+ -+ dst->di_bstart = src->di_bstart; -+ dst->di_bend = src->di_bend; -+ dst->di_bwh = src->di_bwh; -+ dst->di_bdiropq = src->di_bdiropq; -+ /* smp_mb(); */ -+} -+ -+int au_di_init(struct dentry *dentry) -+{ -+ int err; -+ struct super_block *sb; -+ struct au_dinfo *dinfo; -+ -+ err = 0; -+ sb = dentry->d_sb; -+ dinfo = au_di_alloc(sb, AuLsc_DI_CHILD); -+ if (dinfo) { -+ atomic_set(&dinfo->di_generation, au_sigen(sb)); -+ /* smp_mb(); */ /* atomic_set */ -+ dentry->d_fsdata = dinfo; -+ } else -+ err = -ENOMEM; -+ -+ return err; -+} -+ -+void au_di_fin(struct dentry *dentry) -+{ -+ struct au_dinfo *dinfo; -+ -+ dinfo = au_di(dentry); -+ AuRwDestroy(&dinfo->di_rwsem); -+ au_di_free(dinfo); -+} -+ -+int au_di_realloc(struct au_dinfo *dinfo, int nbr) -+{ -+ int err, sz; -+ struct au_hdentry *hdp; -+ -+ AuRwMustWriteLock(&dinfo->di_rwsem); -+ -+ err = -ENOMEM; -+ sz = sizeof(*hdp) * (dinfo->di_bend + 1); -+ if (!sz) -+ sz = sizeof(*hdp); -+ hdp = au_kzrealloc(dinfo->di_hdentry, sz, sizeof(*hdp) * nbr, GFP_NOFS); -+ if (hdp) { -+ dinfo->di_hdentry = hdp; -+ err = 0; -+ } -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void do_ii_write_lock(struct inode *inode, unsigned int lsc) -+{ -+ switch (lsc) { -+ case AuLsc_DI_CHILD: -+ ii_write_lock_child(inode); -+ break; -+ case AuLsc_DI_CHILD2: -+ ii_write_lock_child2(inode); -+ break; -+ case AuLsc_DI_CHILD3: -+ ii_write_lock_child3(inode); -+ break; -+ case AuLsc_DI_PARENT: -+ ii_write_lock_parent(inode); -+ break; -+ case AuLsc_DI_PARENT2: -+ ii_write_lock_parent2(inode); -+ break; -+ case AuLsc_DI_PARENT3: -+ ii_write_lock_parent3(inode); -+ break; -+ default: -+ BUG(); -+ } -+} -+ -+static void do_ii_read_lock(struct inode *inode, unsigned int lsc) -+{ -+ switch (lsc) { -+ case AuLsc_DI_CHILD: -+ ii_read_lock_child(inode); -+ break; -+ case AuLsc_DI_CHILD2: -+ ii_read_lock_child2(inode); -+ break; -+ case AuLsc_DI_CHILD3: -+ ii_read_lock_child3(inode); -+ break; -+ case AuLsc_DI_PARENT: -+ ii_read_lock_parent(inode); -+ break; -+ case AuLsc_DI_PARENT2: -+ ii_read_lock_parent2(inode); -+ break; -+ case AuLsc_DI_PARENT3: -+ ii_read_lock_parent3(inode); -+ break; -+ default: -+ BUG(); -+ } -+} -+ -+void di_read_lock(struct dentry *d, int flags, unsigned int lsc) -+{ -+ au_rw_read_lock_nested(&au_di(d)->di_rwsem, lsc); -+ if (d->d_inode) { -+ if (au_ftest_lock(flags, IW)) -+ do_ii_write_lock(d->d_inode, lsc); -+ else if (au_ftest_lock(flags, IR)) -+ do_ii_read_lock(d->d_inode, lsc); -+ } -+} -+ -+void di_read_unlock(struct dentry *d, int flags) -+{ -+ if (d->d_inode) { -+ if (au_ftest_lock(flags, IW)) { -+ au_dbg_verify_dinode(d); -+ ii_write_unlock(d->d_inode); -+ } else if (au_ftest_lock(flags, IR)) { -+ au_dbg_verify_dinode(d); -+ ii_read_unlock(d->d_inode); -+ } -+ } -+ au_rw_read_unlock(&au_di(d)->di_rwsem); -+} -+ -+void di_downgrade_lock(struct dentry *d, int flags) -+{ -+ if (d->d_inode && au_ftest_lock(flags, IR)) -+ ii_downgrade_lock(d->d_inode); -+ au_rw_dgrade_lock(&au_di(d)->di_rwsem); -+} -+ -+void di_write_lock(struct dentry *d, unsigned int lsc) -+{ -+ au_rw_write_lock_nested(&au_di(d)->di_rwsem, lsc); -+ if (d->d_inode) -+ do_ii_write_lock(d->d_inode, lsc); -+} -+ -+void di_write_unlock(struct dentry *d) -+{ -+ au_dbg_verify_dinode(d); -+ if (d->d_inode) -+ ii_write_unlock(d->d_inode); -+ au_rw_write_unlock(&au_di(d)->di_rwsem); -+} -+ -+void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir) -+{ -+ AuDebugOn(d1 == d2 -+ || d1->d_inode == d2->d_inode -+ || d1->d_sb != d2->d_sb); -+ -+ if (isdir && au_test_subdir(d1, d2)) { -+ di_write_lock_child(d1); -+ di_write_lock_child2(d2); -+ } else { -+ /* there should be no races */ -+ di_write_lock_child(d2); -+ di_write_lock_child2(d1); -+ } -+} -+ -+void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir) -+{ -+ AuDebugOn(d1 == d2 -+ || d1->d_inode == d2->d_inode -+ || d1->d_sb != d2->d_sb); -+ -+ if (isdir && au_test_subdir(d1, d2)) { -+ di_write_lock_parent(d1); -+ di_write_lock_parent2(d2); -+ } else { -+ /* there should be no races */ -+ di_write_lock_parent(d2); -+ di_write_lock_parent2(d1); -+ } -+} -+ -+void di_write_unlock2(struct dentry *d1, struct dentry *d2) -+{ -+ di_write_unlock(d1); -+ if (d1->d_inode == d2->d_inode) -+ au_rw_write_unlock(&au_di(d2)->di_rwsem); -+ else -+ di_write_unlock(d2); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex) -+{ -+ struct dentry *d; -+ -+ DiMustAnyLock(dentry); -+ -+ if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry)) -+ return NULL; -+ AuDebugOn(bindex < 0); -+ d = au_di(dentry)->di_hdentry[0 + bindex].hd_dentry; -+ AuDebugOn(d && au_dcount(d) <= 0); -+ return d; -+} -+ -+/* -+ * extended version of au_h_dptr(). -+ * returns a hashed and positive (or linkable) h_dentry in bindex, NULL, or -+ * error. -+ */ -+struct dentry *au_h_d_alias(struct dentry *dentry, aufs_bindex_t bindex) -+{ -+ struct dentry *h_dentry; -+ struct inode *inode, *h_inode; -+ -+ inode = dentry->d_inode; -+ AuDebugOn(!inode); -+ -+ h_dentry = NULL; -+ if (au_dbstart(dentry) <= bindex -+ && bindex <= au_dbend(dentry)) -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (h_dentry && !au_d_linkable(h_dentry)) { -+ dget(h_dentry); -+ goto out; /* success */ -+ } -+ -+ AuDebugOn(bindex < au_ibstart(inode)); -+ AuDebugOn(au_ibend(inode) < bindex); -+ h_inode = au_h_iptr(inode, bindex); -+ h_dentry = d_find_alias(h_inode); -+ if (h_dentry) { -+ if (!IS_ERR(h_dentry)) { -+ if (!au_d_linkable(h_dentry)) -+ goto out; /* success */ -+ dput(h_dentry); -+ } else -+ goto out; -+ } -+ -+ if (au_opt_test(au_mntflags(dentry->d_sb), PLINK)) { -+ h_dentry = au_plink_lkup(inode, bindex); -+ AuDebugOn(!h_dentry); -+ if (!IS_ERR(h_dentry)) { -+ if (!au_d_hashed_positive(h_dentry)) -+ goto out; /* success */ -+ dput(h_dentry); -+ h_dentry = NULL; -+ } -+ } -+ -+out: -+ AuDbgDentry(h_dentry); -+ return h_dentry; -+} -+ -+aufs_bindex_t au_dbtail(struct dentry *dentry) -+{ -+ aufs_bindex_t bend, bwh; -+ -+ bend = au_dbend(dentry); -+ if (0 <= bend) { -+ bwh = au_dbwh(dentry); -+ if (!bwh) -+ return bwh; -+ if (0 < bwh && bwh < bend) -+ return bwh - 1; -+ } -+ return bend; -+} -+ -+aufs_bindex_t au_dbtaildir(struct dentry *dentry) -+{ -+ aufs_bindex_t bend, bopq; -+ -+ bend = au_dbtail(dentry); -+ if (0 <= bend) { -+ bopq = au_dbdiropq(dentry); -+ if (0 <= bopq && bopq < bend) -+ bend = bopq; -+ } -+ return bend; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex, -+ struct dentry *h_dentry) -+{ -+ struct au_hdentry *hd = au_di(dentry)->di_hdentry + bindex; -+ struct au_branch *br; -+ -+ DiMustWriteLock(dentry); -+ -+ au_hdput(hd); -+ hd->hd_dentry = h_dentry; -+ if (h_dentry) { -+ br = au_sbr(dentry->d_sb, bindex); -+ hd->hd_id = br->br_id; -+ } -+} -+ -+int au_dbrange_test(struct dentry *dentry) -+{ -+ int err; -+ aufs_bindex_t bstart, bend; -+ -+ err = 0; -+ bstart = au_dbstart(dentry); -+ bend = au_dbend(dentry); -+ if (bstart >= 0) -+ AuDebugOn(bend < 0 && bstart > bend); -+ else { -+ err = -EIO; -+ AuDebugOn(bend >= 0); -+ } -+ -+ return err; -+} -+ -+int au_digen_test(struct dentry *dentry, unsigned int sigen) -+{ -+ int err; -+ -+ err = 0; -+ if (unlikely(au_digen(dentry) != sigen -+ || au_iigen_test(dentry->d_inode, sigen))) -+ err = -EIO; -+ -+ return err; -+} -+ -+void au_update_digen(struct dentry *dentry) -+{ -+ atomic_set(&au_di(dentry)->di_generation, au_sigen(dentry->d_sb)); -+ /* smp_mb(); */ /* atomic_set */ -+} -+ -+void au_update_dbrange(struct dentry *dentry, int do_put_zero) -+{ -+ struct au_dinfo *dinfo; -+ struct dentry *h_d; -+ struct au_hdentry *hdp; -+ -+ DiMustWriteLock(dentry); -+ -+ dinfo = au_di(dentry); -+ if (!dinfo || dinfo->di_bstart < 0) -+ return; -+ -+ hdp = dinfo->di_hdentry; -+ if (do_put_zero) { -+ aufs_bindex_t bindex, bend; -+ -+ bend = dinfo->di_bend; -+ for (bindex = dinfo->di_bstart; bindex <= bend; bindex++) { -+ h_d = hdp[0 + bindex].hd_dentry; -+ if (h_d && !h_d->d_inode) -+ au_set_h_dptr(dentry, bindex, NULL); -+ } -+ } -+ -+ dinfo->di_bstart = -1; -+ while (++dinfo->di_bstart <= dinfo->di_bend) -+ if (hdp[0 + dinfo->di_bstart].hd_dentry) -+ break; -+ if (dinfo->di_bstart > dinfo->di_bend) { -+ dinfo->di_bstart = -1; -+ dinfo->di_bend = -1; -+ return; -+ } -+ -+ dinfo->di_bend++; -+ while (0 <= --dinfo->di_bend) -+ if (hdp[0 + dinfo->di_bend].hd_dentry) -+ break; -+ AuDebugOn(dinfo->di_bstart > dinfo->di_bend || dinfo->di_bend < 0); -+} -+ -+void au_update_dbstart(struct dentry *dentry) -+{ -+ aufs_bindex_t bindex, bend; -+ struct dentry *h_dentry; -+ -+ bend = au_dbend(dentry); -+ for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) { -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (!h_dentry) -+ continue; -+ if (h_dentry->d_inode) { -+ au_set_dbstart(dentry, bindex); -+ return; -+ } -+ au_set_h_dptr(dentry, bindex, NULL); -+ } -+} -+ -+void au_update_dbend(struct dentry *dentry) -+{ -+ aufs_bindex_t bindex, bstart; -+ struct dentry *h_dentry; -+ -+ bstart = au_dbstart(dentry); -+ for (bindex = au_dbend(dentry); bindex >= bstart; bindex--) { -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (!h_dentry) -+ continue; -+ if (h_dentry->d_inode) { -+ au_set_dbend(dentry, bindex); -+ return; -+ } -+ au_set_h_dptr(dentry, bindex, NULL); -+ } -+} -+ -+int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry) -+{ -+ aufs_bindex_t bindex, bend; -+ -+ bend = au_dbend(dentry); -+ for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) -+ if (au_h_dptr(dentry, bindex) == h_dentry) -+ return bindex; -+ return -1; -+} -diff --git a/fs/aufs/dir.c b/fs/aufs/dir.c -new file mode 100644 -index 0000000..3d61b05 ---- /dev/null -+++ b/fs/aufs/dir.c -@@ -0,0 +1,756 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * directory operations -+ */ -+ -+#include -+#include "aufs.h" -+ -+void au_add_nlink(struct inode *dir, struct inode *h_dir) -+{ -+ unsigned int nlink; -+ -+ AuDebugOn(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode)); -+ -+ nlink = dir->i_nlink; -+ nlink += h_dir->i_nlink - 2; -+ if (h_dir->i_nlink < 2) -+ nlink += 2; -+ smp_mb(); /* for i_nlink */ -+ /* 0 can happen in revaliding */ -+ set_nlink(dir, nlink); -+} -+ -+void au_sub_nlink(struct inode *dir, struct inode *h_dir) -+{ -+ unsigned int nlink; -+ -+ AuDebugOn(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode)); -+ -+ nlink = dir->i_nlink; -+ nlink -= h_dir->i_nlink - 2; -+ if (h_dir->i_nlink < 2) -+ nlink -= 2; -+ smp_mb(); /* for i_nlink */ -+ /* nlink == 0 means the branch-fs is broken */ -+ set_nlink(dir, nlink); -+} -+ -+loff_t au_dir_size(struct file *file, struct dentry *dentry) -+{ -+ loff_t sz; -+ aufs_bindex_t bindex, bend; -+ struct file *h_file; -+ struct dentry *h_dentry; -+ -+ sz = 0; -+ if (file) { -+ AuDebugOn(!d_is_dir(file->f_path.dentry)); -+ -+ bend = au_fbend_dir(file); -+ for (bindex = au_fbstart(file); -+ bindex <= bend && sz < KMALLOC_MAX_SIZE; -+ bindex++) { -+ h_file = au_hf_dir(file, bindex); -+ if (h_file && file_inode(h_file)) -+ sz += vfsub_f_size_read(h_file); -+ } -+ } else { -+ AuDebugOn(!dentry); -+ AuDebugOn(!d_is_dir(dentry)); -+ -+ bend = au_dbtaildir(dentry); -+ for (bindex = au_dbstart(dentry); -+ bindex <= bend && sz < KMALLOC_MAX_SIZE; -+ bindex++) { -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (h_dentry && h_dentry->d_inode) -+ sz += i_size_read(h_dentry->d_inode); -+ } -+ } -+ if (sz < KMALLOC_MAX_SIZE) -+ sz = roundup_pow_of_two(sz); -+ if (sz > KMALLOC_MAX_SIZE) -+ sz = KMALLOC_MAX_SIZE; -+ else if (sz < NAME_MAX) { -+ BUILD_BUG_ON(AUFS_RDBLK_DEF < NAME_MAX); -+ sz = AUFS_RDBLK_DEF; -+ } -+ return sz; -+} -+ -+struct au_dir_ts_arg { -+ struct dentry *dentry; -+ aufs_bindex_t brid; -+}; -+ -+static void au_do_dir_ts(void *arg) -+{ -+ struct au_dir_ts_arg *a = arg; -+ struct au_dtime dt; -+ struct path h_path; -+ struct inode *dir, *h_dir; -+ struct super_block *sb; -+ struct au_branch *br; -+ struct au_hinode *hdir; -+ int err; -+ aufs_bindex_t bstart, bindex; -+ -+ sb = a->dentry->d_sb; -+ dir = a->dentry->d_inode; -+ if (!dir) -+ goto out; -+ /* no dir->i_mutex lock */ -+ aufs_read_lock(a->dentry, AuLock_DW); /* noflush */ -+ -+ bstart = au_ibstart(dir); -+ bindex = au_br_index(sb, a->brid); -+ if (bindex < bstart) -+ goto out_unlock; -+ -+ br = au_sbr(sb, bindex); -+ h_path.dentry = au_h_dptr(a->dentry, bindex); -+ if (!h_path.dentry) -+ goto out_unlock; -+ h_path.mnt = au_br_mnt(br); -+ au_dtime_store(&dt, a->dentry, &h_path); -+ -+ br = au_sbr(sb, bstart); -+ if (!au_br_writable(br->br_perm)) -+ goto out_unlock; -+ h_path.dentry = au_h_dptr(a->dentry, bstart); -+ h_path.mnt = au_br_mnt(br); -+ err = vfsub_mnt_want_write(h_path.mnt); -+ if (err) -+ goto out_unlock; -+ hdir = au_hi(dir, bstart); -+ au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); -+ h_dir = au_h_iptr(dir, bstart); -+ if (h_dir->i_nlink -+ && timespec_compare(&h_dir->i_mtime, &dt.dt_mtime) < 0) { -+ dt.dt_h_path = h_path; -+ au_dtime_revert(&dt); -+ } -+ au_hn_imtx_unlock(hdir); -+ vfsub_mnt_drop_write(h_path.mnt); -+ au_cpup_attr_timesizes(dir); -+ -+out_unlock: -+ aufs_read_unlock(a->dentry, AuLock_DW); -+out: -+ dput(a->dentry); -+ au_nwt_done(&au_sbi(sb)->si_nowait); -+ kfree(arg); -+} -+ -+void au_dir_ts(struct inode *dir, aufs_bindex_t bindex) -+{ -+ int perm, wkq_err; -+ aufs_bindex_t bstart; -+ struct au_dir_ts_arg *arg; -+ struct dentry *dentry; -+ struct super_block *sb; -+ -+ IMustLock(dir); -+ -+ dentry = d_find_any_alias(dir); -+ AuDebugOn(!dentry); -+ sb = dentry->d_sb; -+ bstart = au_ibstart(dir); -+ if (bstart == bindex) { -+ au_cpup_attr_timesizes(dir); -+ goto out; -+ } -+ -+ perm = au_sbr_perm(sb, bstart); -+ if (!au_br_writable(perm)) -+ goto out; -+ -+ arg = kmalloc(sizeof(*arg), GFP_NOFS); -+ if (!arg) -+ goto out; -+ -+ arg->dentry = dget(dentry); /* will be dput-ted by au_do_dir_ts() */ -+ arg->brid = au_sbr_id(sb, bindex); -+ wkq_err = au_wkq_nowait(au_do_dir_ts, arg, sb, /*flags*/0); -+ if (unlikely(wkq_err)) { -+ pr_err("wkq %d\n", wkq_err); -+ dput(dentry); -+ kfree(arg); -+ } -+ -+out: -+ dput(dentry); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int reopen_dir(struct file *file) -+{ -+ int err; -+ unsigned int flags; -+ aufs_bindex_t bindex, btail, bstart; -+ struct dentry *dentry, *h_dentry; -+ struct file *h_file; -+ -+ /* open all lower dirs */ -+ dentry = file->f_dentry; -+ bstart = au_dbstart(dentry); -+ for (bindex = au_fbstart(file); bindex < bstart; bindex++) -+ au_set_h_fptr(file, bindex, NULL); -+ au_set_fbstart(file, bstart); -+ -+ btail = au_dbtaildir(dentry); -+ for (bindex = au_fbend_dir(file); btail < bindex; bindex--) -+ au_set_h_fptr(file, bindex, NULL); -+ au_set_fbend_dir(file, btail); -+ -+ flags = vfsub_file_flags(file); -+ for (bindex = bstart; bindex <= btail; bindex++) { -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (!h_dentry) -+ continue; -+ h_file = au_hf_dir(file, bindex); -+ if (h_file) -+ continue; -+ -+ h_file = au_h_open(dentry, bindex, flags, file, /*force_wr*/0); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; /* close all? */ -+ au_set_h_fptr(file, bindex, h_file); -+ } -+ au_update_figen(file); -+ /* todo: necessary? */ -+ /* file->f_ra = h_file->f_ra; */ -+ err = 0; -+ -+out: -+ return err; -+} -+ -+static int do_open_dir(struct file *file, int flags, struct file *h_file) -+{ -+ int err; -+ aufs_bindex_t bindex, btail; -+ struct dentry *dentry, *h_dentry; -+ struct vfsmount *mnt; -+ -+ FiMustWriteLock(file); -+ AuDebugOn(h_file); -+ -+ err = 0; -+ mnt = file->f_path.mnt; -+ dentry = file->f_dentry; -+ file->f_version = dentry->d_inode->i_version; -+ bindex = au_dbstart(dentry); -+ au_set_fbstart(file, bindex); -+ btail = au_dbtaildir(dentry); -+ au_set_fbend_dir(file, btail); -+ for (; !err && bindex <= btail; bindex++) { -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (!h_dentry) -+ continue; -+ -+ err = vfsub_test_mntns(mnt, h_dentry->d_sb); -+ if (unlikely(err)) -+ break; -+ h_file = au_h_open(dentry, bindex, flags, file, /*force_wr*/0); -+ if (IS_ERR(h_file)) { -+ err = PTR_ERR(h_file); -+ break; -+ } -+ au_set_h_fptr(file, bindex, h_file); -+ } -+ au_update_figen(file); -+ /* todo: necessary? */ -+ /* file->f_ra = h_file->f_ra; */ -+ if (!err) -+ return 0; /* success */ -+ -+ /* close all */ -+ for (bindex = au_fbstart(file); bindex <= btail; bindex++) -+ au_set_h_fptr(file, bindex, NULL); -+ au_set_fbstart(file, -1); -+ au_set_fbend_dir(file, -1); -+ -+ return err; -+} -+ -+static int aufs_open_dir(struct inode *inode __maybe_unused, -+ struct file *file) -+{ -+ int err; -+ struct super_block *sb; -+ struct au_fidir *fidir; -+ -+ err = -ENOMEM; -+ sb = file->f_dentry->d_sb; -+ si_read_lock(sb, AuLock_FLUSH); -+ fidir = au_fidir_alloc(sb); -+ if (fidir) { -+ struct au_do_open_args args = { -+ .open = do_open_dir, -+ .fidir = fidir -+ }; -+ err = au_do_open(file, &args); -+ if (unlikely(err)) -+ kfree(fidir); -+ } -+ si_read_unlock(sb); -+ return err; -+} -+ -+static int aufs_release_dir(struct inode *inode __maybe_unused, -+ struct file *file) -+{ -+ struct au_vdir *vdir_cache; -+ struct au_finfo *finfo; -+ struct au_fidir *fidir; -+ aufs_bindex_t bindex, bend; -+ -+ finfo = au_fi(file); -+ fidir = finfo->fi_hdir; -+ if (fidir) { -+ au_sphl_del(&finfo->fi_hlist, -+ &au_sbi(file->f_dentry->d_sb)->si_files); -+ vdir_cache = fidir->fd_vdir_cache; /* lock-free */ -+ if (vdir_cache) -+ au_vdir_free(vdir_cache); -+ -+ bindex = finfo->fi_btop; -+ if (bindex >= 0) { -+ /* -+ * calls fput() instead of filp_close(), -+ * since no dnotify or lock for the lower file. -+ */ -+ bend = fidir->fd_bbot; -+ for (; bindex <= bend; bindex++) -+ au_set_h_fptr(file, bindex, NULL); -+ } -+ kfree(fidir); -+ finfo->fi_hdir = NULL; -+ } -+ au_finfo_fin(file); -+ return 0; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_do_flush_dir(struct file *file, fl_owner_t id) -+{ -+ int err; -+ aufs_bindex_t bindex, bend; -+ struct file *h_file; -+ -+ err = 0; -+ bend = au_fbend_dir(file); -+ for (bindex = au_fbstart(file); !err && bindex <= bend; bindex++) { -+ h_file = au_hf_dir(file, bindex); -+ if (h_file) -+ err = vfsub_flush(h_file, id); -+ } -+ return err; -+} -+ -+static int aufs_flush_dir(struct file *file, fl_owner_t id) -+{ -+ return au_do_flush(file, id, au_do_flush_dir); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_do_fsync_dir_no_file(struct dentry *dentry, int datasync) -+{ -+ int err; -+ aufs_bindex_t bend, bindex; -+ struct inode *inode; -+ struct super_block *sb; -+ -+ err = 0; -+ sb = dentry->d_sb; -+ inode = dentry->d_inode; -+ IMustLock(inode); -+ bend = au_dbend(dentry); -+ for (bindex = au_dbstart(dentry); !err && bindex <= bend; bindex++) { -+ struct path h_path; -+ -+ if (au_test_ro(sb, bindex, inode)) -+ continue; -+ h_path.dentry = au_h_dptr(dentry, bindex); -+ if (!h_path.dentry) -+ continue; -+ -+ h_path.mnt = au_sbr_mnt(sb, bindex); -+ err = vfsub_fsync(NULL, &h_path, datasync); -+ } -+ -+ return err; -+} -+ -+static int au_do_fsync_dir(struct file *file, int datasync) -+{ -+ int err; -+ aufs_bindex_t bend, bindex; -+ struct file *h_file; -+ struct super_block *sb; -+ struct inode *inode; -+ -+ err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1); -+ if (unlikely(err)) -+ goto out; -+ -+ inode = file_inode(file); -+ sb = inode->i_sb; -+ bend = au_fbend_dir(file); -+ for (bindex = au_fbstart(file); !err && bindex <= bend; bindex++) { -+ h_file = au_hf_dir(file, bindex); -+ if (!h_file || au_test_ro(sb, bindex, inode)) -+ continue; -+ -+ err = vfsub_fsync(h_file, &h_file->f_path, datasync); -+ } -+ -+out: -+ return err; -+} -+ -+/* -+ * @file may be NULL -+ */ -+static int aufs_fsync_dir(struct file *file, loff_t start, loff_t end, -+ int datasync) -+{ -+ int err; -+ struct dentry *dentry; -+ struct super_block *sb; -+ struct mutex *mtx; -+ -+ err = 0; -+ dentry = file->f_dentry; -+ mtx = &dentry->d_inode->i_mutex; -+ mutex_lock(mtx); -+ sb = dentry->d_sb; -+ si_noflush_read_lock(sb); -+ if (file) -+ err = au_do_fsync_dir(file, datasync); -+ else { -+ di_write_lock_child(dentry); -+ err = au_do_fsync_dir_no_file(dentry, datasync); -+ } -+ au_cpup_attr_timesizes(dentry->d_inode); -+ di_write_unlock(dentry); -+ if (file) -+ fi_write_unlock(file); -+ -+ si_read_unlock(sb); -+ mutex_unlock(mtx); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int aufs_iterate(struct file *file, struct dir_context *ctx) -+{ -+ int err; -+ struct dentry *dentry; -+ struct inode *inode, *h_inode; -+ struct super_block *sb; -+ -+ AuDbg("%pD, ctx{%pf, %llu}\n", file, ctx->actor, ctx->pos); -+ -+ dentry = file->f_dentry; -+ inode = dentry->d_inode; -+ IMustLock(inode); -+ -+ sb = dentry->d_sb; -+ si_read_lock(sb, AuLock_FLUSH); -+ err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1); -+ if (unlikely(err)) -+ goto out; -+ err = au_alive_dir(dentry); -+ if (!err) -+ err = au_vdir_init(file); -+ di_downgrade_lock(dentry, AuLock_IR); -+ if (unlikely(err)) -+ goto out_unlock; -+ -+ h_inode = au_h_iptr(inode, au_ibstart(inode)); -+ if (!au_test_nfsd()) { -+ err = au_vdir_fill_de(file, ctx); -+ fsstack_copy_attr_atime(inode, h_inode); -+ } else { -+ /* -+ * nfsd filldir may call lookup_one_len(), vfs_getattr(), -+ * encode_fh() and others. -+ */ -+ atomic_inc(&h_inode->i_count); -+ di_read_unlock(dentry, AuLock_IR); -+ si_read_unlock(sb); -+ err = au_vdir_fill_de(file, ctx); -+ fsstack_copy_attr_atime(inode, h_inode); -+ fi_write_unlock(file); -+ iput(h_inode); -+ -+ AuTraceErr(err); -+ return err; -+ } -+ -+out_unlock: -+ di_read_unlock(dentry, AuLock_IR); -+ fi_write_unlock(file); -+out: -+ si_read_unlock(sb); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+#define AuTestEmpty_WHONLY 1 -+#define AuTestEmpty_CALLED (1 << 1) -+#define AuTestEmpty_SHWH (1 << 2) -+#define au_ftest_testempty(flags, name) ((flags) & AuTestEmpty_##name) -+#define au_fset_testempty(flags, name) \ -+ do { (flags) |= AuTestEmpty_##name; } while (0) -+#define au_fclr_testempty(flags, name) \ -+ do { (flags) &= ~AuTestEmpty_##name; } while (0) -+ -+#ifndef CONFIG_AUFS_SHWH -+#undef AuTestEmpty_SHWH -+#define AuTestEmpty_SHWH 0 -+#endif -+ -+struct test_empty_arg { -+ struct dir_context ctx; -+ struct au_nhash *whlist; -+ unsigned int flags; -+ int err; -+ aufs_bindex_t bindex; -+}; -+ -+static int test_empty_cb(struct dir_context *ctx, const char *__name, -+ int namelen, loff_t offset __maybe_unused, u64 ino, -+ unsigned int d_type) -+{ -+ struct test_empty_arg *arg = container_of(ctx, struct test_empty_arg, -+ ctx); -+ char *name = (void *)__name; -+ -+ arg->err = 0; -+ au_fset_testempty(arg->flags, CALLED); -+ /* smp_mb(); */ -+ if (name[0] == '.' -+ && (namelen == 1 || (name[1] == '.' && namelen == 2))) -+ goto out; /* success */ -+ -+ if (namelen <= AUFS_WH_PFX_LEN -+ || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { -+ if (au_ftest_testempty(arg->flags, WHONLY) -+ && !au_nhash_test_known_wh(arg->whlist, name, namelen)) -+ arg->err = -ENOTEMPTY; -+ goto out; -+ } -+ -+ name += AUFS_WH_PFX_LEN; -+ namelen -= AUFS_WH_PFX_LEN; -+ if (!au_nhash_test_known_wh(arg->whlist, name, namelen)) -+ arg->err = au_nhash_append_wh -+ (arg->whlist, name, namelen, ino, d_type, arg->bindex, -+ au_ftest_testempty(arg->flags, SHWH)); -+ -+out: -+ /* smp_mb(); */ -+ AuTraceErr(arg->err); -+ return arg->err; -+} -+ -+static int do_test_empty(struct dentry *dentry, struct test_empty_arg *arg) -+{ -+ int err; -+ struct file *h_file; -+ -+ h_file = au_h_open(dentry, arg->bindex, -+ O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_LARGEFILE, -+ /*file*/NULL, /*force_wr*/0); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ err = 0; -+ if (!au_opt_test(au_mntflags(dentry->d_sb), UDBA_NONE) -+ && !file_inode(h_file)->i_nlink) -+ goto out_put; -+ -+ do { -+ arg->err = 0; -+ au_fclr_testempty(arg->flags, CALLED); -+ /* smp_mb(); */ -+ err = vfsub_iterate_dir(h_file, &arg->ctx); -+ if (err >= 0) -+ err = arg->err; -+ } while (!err && au_ftest_testempty(arg->flags, CALLED)); -+ -+out_put: -+ fput(h_file); -+ au_sbr_put(dentry->d_sb, arg->bindex); -+out: -+ return err; -+} -+ -+struct do_test_empty_args { -+ int *errp; -+ struct dentry *dentry; -+ struct test_empty_arg *arg; -+}; -+ -+static void call_do_test_empty(void *args) -+{ -+ struct do_test_empty_args *a = args; -+ *a->errp = do_test_empty(a->dentry, a->arg); -+} -+ -+static int sio_test_empty(struct dentry *dentry, struct test_empty_arg *arg) -+{ -+ int err, wkq_err; -+ struct dentry *h_dentry; -+ struct inode *h_inode; -+ -+ h_dentry = au_h_dptr(dentry, arg->bindex); -+ h_inode = h_dentry->d_inode; -+ /* todo: i_mode changes anytime? */ -+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); -+ err = au_test_h_perm_sio(h_inode, MAY_EXEC | MAY_READ); -+ mutex_unlock(&h_inode->i_mutex); -+ if (!err) -+ err = do_test_empty(dentry, arg); -+ else { -+ struct do_test_empty_args args = { -+ .errp = &err, -+ .dentry = dentry, -+ .arg = arg -+ }; -+ unsigned int flags = arg->flags; -+ -+ wkq_err = au_wkq_wait(call_do_test_empty, &args); -+ if (unlikely(wkq_err)) -+ err = wkq_err; -+ arg->flags = flags; -+ } -+ -+ return err; -+} -+ -+int au_test_empty_lower(struct dentry *dentry) -+{ -+ int err; -+ unsigned int rdhash; -+ aufs_bindex_t bindex, bstart, btail; -+ struct au_nhash whlist; -+ struct test_empty_arg arg = { -+ .ctx = { -+ .actor = au_diractor(test_empty_cb) -+ } -+ }; -+ int (*test_empty)(struct dentry *dentry, struct test_empty_arg *arg); -+ -+ SiMustAnyLock(dentry->d_sb); -+ -+ rdhash = au_sbi(dentry->d_sb)->si_rdhash; -+ if (!rdhash) -+ rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, dentry)); -+ err = au_nhash_alloc(&whlist, rdhash, GFP_NOFS); -+ if (unlikely(err)) -+ goto out; -+ -+ arg.flags = 0; -+ arg.whlist = &whlist; -+ bstart = au_dbstart(dentry); -+ if (au_opt_test(au_mntflags(dentry->d_sb), SHWH)) -+ au_fset_testempty(arg.flags, SHWH); -+ test_empty = do_test_empty; -+ if (au_opt_test(au_mntflags(dentry->d_sb), DIRPERM1)) -+ test_empty = sio_test_empty; -+ arg.bindex = bstart; -+ err = test_empty(dentry, &arg); -+ if (unlikely(err)) -+ goto out_whlist; -+ -+ au_fset_testempty(arg.flags, WHONLY); -+ btail = au_dbtaildir(dentry); -+ for (bindex = bstart + 1; !err && bindex <= btail; bindex++) { -+ struct dentry *h_dentry; -+ -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (h_dentry && h_dentry->d_inode) { -+ arg.bindex = bindex; -+ err = test_empty(dentry, &arg); -+ } -+ } -+ -+out_whlist: -+ au_nhash_wh_free(&whlist); -+out: -+ return err; -+} -+ -+int au_test_empty(struct dentry *dentry, struct au_nhash *whlist) -+{ -+ int err; -+ struct test_empty_arg arg = { -+ .ctx = { -+ .actor = au_diractor(test_empty_cb) -+ } -+ }; -+ aufs_bindex_t bindex, btail; -+ -+ err = 0; -+ arg.whlist = whlist; -+ arg.flags = AuTestEmpty_WHONLY; -+ if (au_opt_test(au_mntflags(dentry->d_sb), SHWH)) -+ au_fset_testempty(arg.flags, SHWH); -+ btail = au_dbtaildir(dentry); -+ for (bindex = au_dbstart(dentry); !err && bindex <= btail; bindex++) { -+ struct dentry *h_dentry; -+ -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (h_dentry && h_dentry->d_inode) { -+ arg.bindex = bindex; -+ err = sio_test_empty(dentry, &arg); -+ } -+ } -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+const struct file_operations aufs_dir_fop = { -+ .owner = THIS_MODULE, -+ .llseek = default_llseek, -+ .read = generic_read_dir, -+ .iterate = aufs_iterate, -+ .unlocked_ioctl = aufs_ioctl_dir, -+#ifdef CONFIG_COMPAT -+ .compat_ioctl = aufs_compat_ioctl_dir, -+#endif -+ .open = aufs_open_dir, -+ .release = aufs_release_dir, -+ .flush = aufs_flush_dir, -+ .fsync = aufs_fsync_dir -+}; -diff --git a/fs/aufs/dir.h b/fs/aufs/dir.h -new file mode 100644 -index 0000000..16821f9 ---- /dev/null -+++ b/fs/aufs/dir.h -@@ -0,0 +1,131 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * directory operations -+ */ -+ -+#ifndef __AUFS_DIR_H__ -+#define __AUFS_DIR_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* need to be faster and smaller */ -+ -+struct au_nhash { -+ unsigned int nh_num; -+ struct hlist_head *nh_head; -+}; -+ -+struct au_vdir_destr { -+ unsigned char len; -+ unsigned char name[0]; -+} __packed; -+ -+struct au_vdir_dehstr { -+ struct hlist_node hash; -+ struct au_vdir_destr *str; -+} ____cacheline_aligned_in_smp; -+ -+struct au_vdir_de { -+ ino_t de_ino; -+ unsigned char de_type; -+ /* caution: packed */ -+ struct au_vdir_destr de_str; -+} __packed; -+ -+struct au_vdir_wh { -+ struct hlist_node wh_hash; -+#ifdef CONFIG_AUFS_SHWH -+ ino_t wh_ino; -+ aufs_bindex_t wh_bindex; -+ unsigned char wh_type; -+#else -+ aufs_bindex_t wh_bindex; -+#endif -+ /* caution: packed */ -+ struct au_vdir_destr wh_str; -+} __packed; -+ -+union au_vdir_deblk_p { -+ unsigned char *deblk; -+ struct au_vdir_de *de; -+}; -+ -+struct au_vdir { -+ unsigned char **vd_deblk; -+ unsigned long vd_nblk; -+ struct { -+ unsigned long ul; -+ union au_vdir_deblk_p p; -+ } vd_last; -+ -+ unsigned long vd_version; -+ unsigned int vd_deblk_sz; -+ unsigned long vd_jiffy; -+} ____cacheline_aligned_in_smp; -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* dir.c */ -+extern const struct file_operations aufs_dir_fop; -+void au_add_nlink(struct inode *dir, struct inode *h_dir); -+void au_sub_nlink(struct inode *dir, struct inode *h_dir); -+loff_t au_dir_size(struct file *file, struct dentry *dentry); -+void au_dir_ts(struct inode *dir, aufs_bindex_t bsrc); -+int au_test_empty_lower(struct dentry *dentry); -+int au_test_empty(struct dentry *dentry, struct au_nhash *whlist); -+ -+/* vdir.c */ -+unsigned int au_rdhash_est(loff_t sz); -+int au_nhash_alloc(struct au_nhash *nhash, unsigned int num_hash, gfp_t gfp); -+void au_nhash_wh_free(struct au_nhash *whlist); -+int au_nhash_test_longer_wh(struct au_nhash *whlist, aufs_bindex_t btgt, -+ int limit); -+int au_nhash_test_known_wh(struct au_nhash *whlist, char *name, int nlen); -+int au_nhash_append_wh(struct au_nhash *whlist, char *name, int nlen, ino_t ino, -+ unsigned int d_type, aufs_bindex_t bindex, -+ unsigned char shwh); -+void au_vdir_free(struct au_vdir *vdir); -+int au_vdir_init(struct file *file); -+int au_vdir_fill_de(struct file *file, struct dir_context *ctx); -+ -+/* ioctl.c */ -+long aufs_ioctl_dir(struct file *file, unsigned int cmd, unsigned long arg); -+ -+#ifdef CONFIG_AUFS_RDU -+/* rdu.c */ -+long au_rdu_ioctl(struct file *file, unsigned int cmd, unsigned long arg); -+#ifdef CONFIG_COMPAT -+long au_rdu_compat_ioctl(struct file *file, unsigned int cmd, -+ unsigned long arg); -+#endif -+#else -+AuStub(long, au_rdu_ioctl, return -EINVAL, struct file *file, -+ unsigned int cmd, unsigned long arg) -+#ifdef CONFIG_COMPAT -+AuStub(long, au_rdu_compat_ioctl, return -EINVAL, struct file *file, -+ unsigned int cmd, unsigned long arg) -+#endif -+#endif -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_DIR_H__ */ -diff --git a/fs/aufs/dynop.c b/fs/aufs/dynop.c -new file mode 100644 -index 0000000..d758805 ---- /dev/null -+++ b/fs/aufs/dynop.c -@@ -0,0 +1,379 @@ -+/* -+ * Copyright (C) 2010-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * dynamically customizable operations for regular files -+ */ -+ -+#include "aufs.h" -+ -+#define DyPrSym(key) AuDbgSym(key->dk_op.dy_hop) -+ -+/* -+ * How large will these lists be? -+ * Usually just a few elements, 20-30 at most for each, I guess. -+ */ -+static struct au_splhead dynop[AuDyLast]; -+ -+static struct au_dykey *dy_gfind_get(struct au_splhead *spl, const void *h_op) -+{ -+ struct au_dykey *key, *tmp; -+ struct list_head *head; -+ -+ key = NULL; -+ head = &spl->head; -+ rcu_read_lock(); -+ list_for_each_entry_rcu(tmp, head, dk_list) -+ if (tmp->dk_op.dy_hop == h_op) { -+ key = tmp; -+ kref_get(&key->dk_kref); -+ break; -+ } -+ rcu_read_unlock(); -+ -+ return key; -+} -+ -+static struct au_dykey *dy_bradd(struct au_branch *br, struct au_dykey *key) -+{ -+ struct au_dykey **k, *found; -+ const void *h_op = key->dk_op.dy_hop; -+ int i; -+ -+ found = NULL; -+ k = br->br_dykey; -+ for (i = 0; i < AuBrDynOp; i++) -+ if (k[i]) { -+ if (k[i]->dk_op.dy_hop == h_op) { -+ found = k[i]; -+ break; -+ } -+ } else -+ break; -+ if (!found) { -+ spin_lock(&br->br_dykey_lock); -+ for (; i < AuBrDynOp; i++) -+ if (k[i]) { -+ if (k[i]->dk_op.dy_hop == h_op) { -+ found = k[i]; -+ break; -+ } -+ } else { -+ k[i] = key; -+ break; -+ } -+ spin_unlock(&br->br_dykey_lock); -+ BUG_ON(i == AuBrDynOp); /* expand the array */ -+ } -+ -+ return found; -+} -+ -+/* kref_get() if @key is already added */ -+static struct au_dykey *dy_gadd(struct au_splhead *spl, struct au_dykey *key) -+{ -+ struct au_dykey *tmp, *found; -+ struct list_head *head; -+ const void *h_op = key->dk_op.dy_hop; -+ -+ found = NULL; -+ head = &spl->head; -+ spin_lock(&spl->spin); -+ list_for_each_entry(tmp, head, dk_list) -+ if (tmp->dk_op.dy_hop == h_op) { -+ kref_get(&tmp->dk_kref); -+ found = tmp; -+ break; -+ } -+ if (!found) -+ list_add_rcu(&key->dk_list, head); -+ spin_unlock(&spl->spin); -+ -+ if (!found) -+ DyPrSym(key); -+ return found; -+} -+ -+static void dy_free_rcu(struct rcu_head *rcu) -+{ -+ struct au_dykey *key; -+ -+ key = container_of(rcu, struct au_dykey, dk_rcu); -+ DyPrSym(key); -+ kfree(key); -+} -+ -+static void dy_free(struct kref *kref) -+{ -+ struct au_dykey *key; -+ struct au_splhead *spl; -+ -+ key = container_of(kref, struct au_dykey, dk_kref); -+ spl = dynop + key->dk_op.dy_type; -+ au_spl_del_rcu(&key->dk_list, spl); -+ call_rcu(&key->dk_rcu, dy_free_rcu); -+} -+ -+void au_dy_put(struct au_dykey *key) -+{ -+ kref_put(&key->dk_kref, dy_free); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+#define DyDbgSize(cnt, op) AuDebugOn(cnt != sizeof(op)/sizeof(void *)) -+ -+#ifdef CONFIG_AUFS_DEBUG -+#define DyDbgDeclare(cnt) unsigned int cnt = 0 -+#define DyDbgInc(cnt) do { cnt++; } while (0) -+#else -+#define DyDbgDeclare(cnt) do {} while (0) -+#define DyDbgInc(cnt) do {} while (0) -+#endif -+ -+#define DySet(func, dst, src, h_op, h_sb) do { \ -+ DyDbgInc(cnt); \ -+ if (h_op->func) { \ -+ if (src.func) \ -+ dst.func = src.func; \ -+ else \ -+ AuDbg("%s %s\n", au_sbtype(h_sb), #func); \ -+ } \ -+} while (0) -+ -+#define DySetForce(func, dst, src) do { \ -+ AuDebugOn(!src.func); \ -+ DyDbgInc(cnt); \ -+ dst.func = src.func; \ -+} while (0) -+ -+#define DySetAop(func) \ -+ DySet(func, dyaop->da_op, aufs_aop, h_aop, h_sb) -+#define DySetAopForce(func) \ -+ DySetForce(func, dyaop->da_op, aufs_aop) -+ -+static void dy_aop(struct au_dykey *key, const void *h_op, -+ struct super_block *h_sb __maybe_unused) -+{ -+ struct au_dyaop *dyaop = (void *)key; -+ const struct address_space_operations *h_aop = h_op; -+ DyDbgDeclare(cnt); -+ -+ AuDbg("%s\n", au_sbtype(h_sb)); -+ -+ DySetAop(writepage); -+ DySetAopForce(readpage); /* force */ -+ DySetAop(writepages); -+ DySetAop(set_page_dirty); -+ DySetAop(readpages); -+ DySetAop(write_begin); -+ DySetAop(write_end); -+ DySetAop(bmap); -+ DySetAop(invalidatepage); -+ DySetAop(releasepage); -+ DySetAop(freepage); -+ /* these two will be changed according to an aufs mount option */ -+ DySetAop(direct_IO); -+ DySetAop(get_xip_mem); -+ DySetAop(migratepage); -+ DySetAop(launder_page); -+ DySetAop(is_partially_uptodate); -+ DySetAop(is_dirty_writeback); -+ DySetAop(error_remove_page); -+ DySetAop(swap_activate); -+ DySetAop(swap_deactivate); -+ -+ DyDbgSize(cnt, *h_aop); -+ dyaop->da_get_xip_mem = h_aop->get_xip_mem; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void dy_bug(struct kref *kref) -+{ -+ BUG(); -+} -+ -+static struct au_dykey *dy_get(struct au_dynop *op, struct au_branch *br) -+{ -+ struct au_dykey *key, *old; -+ struct au_splhead *spl; -+ struct op { -+ unsigned int sz; -+ void (*set)(struct au_dykey *key, const void *h_op, -+ struct super_block *h_sb __maybe_unused); -+ }; -+ static const struct op a[] = { -+ [AuDy_AOP] = { -+ .sz = sizeof(struct au_dyaop), -+ .set = dy_aop -+ } -+ }; -+ const struct op *p; -+ -+ spl = dynop + op->dy_type; -+ key = dy_gfind_get(spl, op->dy_hop); -+ if (key) -+ goto out_add; /* success */ -+ -+ p = a + op->dy_type; -+ key = kzalloc(p->sz, GFP_NOFS); -+ if (unlikely(!key)) { -+ key = ERR_PTR(-ENOMEM); -+ goto out; -+ } -+ -+ key->dk_op.dy_hop = op->dy_hop; -+ kref_init(&key->dk_kref); -+ p->set(key, op->dy_hop, au_br_sb(br)); -+ old = dy_gadd(spl, key); -+ if (old) { -+ kfree(key); -+ key = old; -+ } -+ -+out_add: -+ old = dy_bradd(br, key); -+ if (old) -+ /* its ref-count should never be zero here */ -+ kref_put(&key->dk_kref, dy_bug); -+out: -+ return key; -+} -+ -+/* ---------------------------------------------------------------------- */ -+/* -+ * Aufs prohibits O_DIRECT by defaut even if the branch supports it. -+ * This behaviour is necessary to return an error from open(O_DIRECT) instead -+ * of the succeeding I/O. The dio mount option enables O_DIRECT and makes -+ * open(O_DIRECT) always succeed, but the succeeding I/O may return an error. -+ * See the aufs manual in detail. -+ * -+ * To keep this behaviour, aufs has to set NULL to ->get_xip_mem too, and the -+ * performance of fadvise() and madvise() may be affected. -+ */ -+static void dy_adx(struct au_dyaop *dyaop, int do_dx) -+{ -+ if (!do_dx) { -+ dyaop->da_op.direct_IO = NULL; -+ dyaop->da_op.get_xip_mem = NULL; -+ } else { -+ dyaop->da_op.direct_IO = aufs_aop.direct_IO; -+ dyaop->da_op.get_xip_mem = aufs_aop.get_xip_mem; -+ if (!dyaop->da_get_xip_mem) -+ dyaop->da_op.get_xip_mem = NULL; -+ } -+} -+ -+static struct au_dyaop *dy_aget(struct au_branch *br, -+ const struct address_space_operations *h_aop, -+ int do_dx) -+{ -+ struct au_dyaop *dyaop; -+ struct au_dynop op; -+ -+ op.dy_type = AuDy_AOP; -+ op.dy_haop = h_aop; -+ dyaop = (void *)dy_get(&op, br); -+ if (IS_ERR(dyaop)) -+ goto out; -+ dy_adx(dyaop, do_dx); -+ -+out: -+ return dyaop; -+} -+ -+int au_dy_iaop(struct inode *inode, aufs_bindex_t bindex, -+ struct inode *h_inode) -+{ -+ int err, do_dx; -+ struct super_block *sb; -+ struct au_branch *br; -+ struct au_dyaop *dyaop; -+ -+ AuDebugOn(!S_ISREG(h_inode->i_mode)); -+ IiMustWriteLock(inode); -+ -+ sb = inode->i_sb; -+ br = au_sbr(sb, bindex); -+ do_dx = !!au_opt_test(au_mntflags(sb), DIO); -+ dyaop = dy_aget(br, h_inode->i_mapping->a_ops, do_dx); -+ err = PTR_ERR(dyaop); -+ if (IS_ERR(dyaop)) -+ /* unnecessary to call dy_fput() */ -+ goto out; -+ -+ err = 0; -+ inode->i_mapping->a_ops = &dyaop->da_op; -+ -+out: -+ return err; -+} -+ -+/* -+ * Is it safe to replace a_ops during the inode/file is in operation? -+ * Yes, I hope so. -+ */ -+int au_dy_irefresh(struct inode *inode) -+{ -+ int err; -+ aufs_bindex_t bstart; -+ struct inode *h_inode; -+ -+ err = 0; -+ if (S_ISREG(inode->i_mode)) { -+ bstart = au_ibstart(inode); -+ h_inode = au_h_iptr(inode, bstart); -+ err = au_dy_iaop(inode, bstart, h_inode); -+ } -+ return err; -+} -+ -+void au_dy_arefresh(int do_dx) -+{ -+ struct au_splhead *spl; -+ struct list_head *head; -+ struct au_dykey *key; -+ -+ spl = dynop + AuDy_AOP; -+ head = &spl->head; -+ spin_lock(&spl->spin); -+ list_for_each_entry(key, head, dk_list) -+ dy_adx((void *)key, do_dx); -+ spin_unlock(&spl->spin); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void __init au_dy_init(void) -+{ -+ int i; -+ -+ /* make sure that 'struct au_dykey *' can be any type */ -+ BUILD_BUG_ON(offsetof(struct au_dyaop, da_key)); -+ -+ for (i = 0; i < AuDyLast; i++) -+ au_spl_init(dynop + i); -+} -+ -+void au_dy_fin(void) -+{ -+ int i; -+ -+ for (i = 0; i < AuDyLast; i++) -+ WARN_ON(!list_empty(&dynop[i].head)); -+} -diff --git a/fs/aufs/dynop.h b/fs/aufs/dynop.h -new file mode 100644 -index 0000000..cdf1499 ---- /dev/null -+++ b/fs/aufs/dynop.h -@@ -0,0 +1,76 @@ -+/* -+ * Copyright (C) 2010-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * dynamically customizable operations (for regular files only) -+ */ -+ -+#ifndef __AUFS_DYNOP_H__ -+#define __AUFS_DYNOP_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+#include -+ -+enum {AuDy_AOP, AuDyLast}; -+ -+struct au_dynop { -+ int dy_type; -+ union { -+ const void *dy_hop; -+ const struct address_space_operations *dy_haop; -+ }; -+}; -+ -+struct au_dykey { -+ union { -+ struct list_head dk_list; -+ struct rcu_head dk_rcu; -+ }; -+ struct au_dynop dk_op; -+ -+ /* -+ * during I am in the branch local array, kref is gotten. when the -+ * branch is removed, kref is put. -+ */ -+ struct kref dk_kref; -+}; -+ -+/* stop unioning since their sizes are very different from each other */ -+struct au_dyaop { -+ struct au_dykey da_key; -+ struct address_space_operations da_op; /* not const */ -+ int (*da_get_xip_mem)(struct address_space *, pgoff_t, int, -+ void **, unsigned long *); -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* dynop.c */ -+struct au_branch; -+void au_dy_put(struct au_dykey *key); -+int au_dy_iaop(struct inode *inode, aufs_bindex_t bindex, -+ struct inode *h_inode); -+int au_dy_irefresh(struct inode *inode); -+void au_dy_arefresh(int do_dio); -+ -+void __init au_dy_init(void); -+void au_dy_fin(void); -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_DYNOP_H__ */ -diff --git a/fs/aufs/export.c b/fs/aufs/export.c -new file mode 100644 -index 0000000..c5bfa76 ---- /dev/null -+++ b/fs/aufs/export.c -@@ -0,0 +1,831 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * export via nfs -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include "../fs/mount.h" -+#include "aufs.h" -+ -+union conv { -+#ifdef CONFIG_AUFS_INO_T_64 -+ __u32 a[2]; -+#else -+ __u32 a[1]; -+#endif -+ ino_t ino; -+}; -+ -+static ino_t decode_ino(__u32 *a) -+{ -+ union conv u; -+ -+ BUILD_BUG_ON(sizeof(u.ino) != sizeof(u.a)); -+ u.a[0] = a[0]; -+#ifdef CONFIG_AUFS_INO_T_64 -+ u.a[1] = a[1]; -+#endif -+ return u.ino; -+} -+ -+static void encode_ino(__u32 *a, ino_t ino) -+{ -+ union conv u; -+ -+ u.ino = ino; -+ a[0] = u.a[0]; -+#ifdef CONFIG_AUFS_INO_T_64 -+ a[1] = u.a[1]; -+#endif -+} -+ -+/* NFS file handle */ -+enum { -+ Fh_br_id, -+ Fh_sigen, -+#ifdef CONFIG_AUFS_INO_T_64 -+ /* support 64bit inode number */ -+ Fh_ino1, -+ Fh_ino2, -+ Fh_dir_ino1, -+ Fh_dir_ino2, -+#else -+ Fh_ino1, -+ Fh_dir_ino1, -+#endif -+ Fh_igen, -+ Fh_h_type, -+ Fh_tail, -+ -+ Fh_ino = Fh_ino1, -+ Fh_dir_ino = Fh_dir_ino1 -+}; -+ -+static int au_test_anon(struct dentry *dentry) -+{ -+ /* note: read d_flags without d_lock */ -+ return !!(dentry->d_flags & DCACHE_DISCONNECTED); -+} -+ -+int au_test_nfsd(void) -+{ -+ int ret; -+ struct task_struct *tsk = current; -+ char comm[sizeof(tsk->comm)]; -+ -+ ret = 0; -+ if (tsk->flags & PF_KTHREAD) { -+ get_task_comm(comm, tsk); -+ ret = !strcmp(comm, "nfsd"); -+ } -+ -+ return ret; -+} -+ -+/* ---------------------------------------------------------------------- */ -+/* inode generation external table */ -+ -+void au_xigen_inc(struct inode *inode) -+{ -+ loff_t pos; -+ ssize_t sz; -+ __u32 igen; -+ struct super_block *sb; -+ struct au_sbinfo *sbinfo; -+ -+ sb = inode->i_sb; -+ AuDebugOn(!au_opt_test(au_mntflags(sb), XINO)); -+ -+ sbinfo = au_sbi(sb); -+ pos = inode->i_ino; -+ pos *= sizeof(igen); -+ igen = inode->i_generation + 1; -+ sz = xino_fwrite(sbinfo->si_xwrite, sbinfo->si_xigen, &igen, -+ sizeof(igen), &pos); -+ if (sz == sizeof(igen)) -+ return; /* success */ -+ -+ if (unlikely(sz >= 0)) -+ AuIOErr("xigen error (%zd)\n", sz); -+} -+ -+int au_xigen_new(struct inode *inode) -+{ -+ int err; -+ loff_t pos; -+ ssize_t sz; -+ struct super_block *sb; -+ struct au_sbinfo *sbinfo; -+ struct file *file; -+ -+ err = 0; -+ /* todo: dirty, at mount time */ -+ if (inode->i_ino == AUFS_ROOT_INO) -+ goto out; -+ sb = inode->i_sb; -+ SiMustAnyLock(sb); -+ if (unlikely(!au_opt_test(au_mntflags(sb), XINO))) -+ goto out; -+ -+ err = -EFBIG; -+ pos = inode->i_ino; -+ if (unlikely(au_loff_max / sizeof(inode->i_generation) - 1 < pos)) { -+ AuIOErr1("too large i%lld\n", pos); -+ goto out; -+ } -+ pos *= sizeof(inode->i_generation); -+ -+ err = 0; -+ sbinfo = au_sbi(sb); -+ file = sbinfo->si_xigen; -+ BUG_ON(!file); -+ -+ if (vfsub_f_size_read(file) -+ < pos + sizeof(inode->i_generation)) { -+ inode->i_generation = atomic_inc_return(&sbinfo->si_xigen_next); -+ sz = xino_fwrite(sbinfo->si_xwrite, file, &inode->i_generation, -+ sizeof(inode->i_generation), &pos); -+ } else -+ sz = xino_fread(sbinfo->si_xread, file, &inode->i_generation, -+ sizeof(inode->i_generation), &pos); -+ if (sz == sizeof(inode->i_generation)) -+ goto out; /* success */ -+ -+ err = sz; -+ if (unlikely(sz >= 0)) { -+ err = -EIO; -+ AuIOErr("xigen error (%zd)\n", sz); -+ } -+ -+out: -+ return err; -+} -+ -+int au_xigen_set(struct super_block *sb, struct file *base) -+{ -+ int err; -+ struct au_sbinfo *sbinfo; -+ struct file *file; -+ -+ SiMustWriteLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ file = au_xino_create2(base, sbinfo->si_xigen); -+ err = PTR_ERR(file); -+ if (IS_ERR(file)) -+ goto out; -+ err = 0; -+ if (sbinfo->si_xigen) -+ fput(sbinfo->si_xigen); -+ sbinfo->si_xigen = file; -+ -+out: -+ return err; -+} -+ -+void au_xigen_clr(struct super_block *sb) -+{ -+ struct au_sbinfo *sbinfo; -+ -+ SiMustWriteLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ if (sbinfo->si_xigen) { -+ fput(sbinfo->si_xigen); -+ sbinfo->si_xigen = NULL; -+ } -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static struct dentry *decode_by_ino(struct super_block *sb, ino_t ino, -+ ino_t dir_ino) -+{ -+ struct dentry *dentry, *d; -+ struct inode *inode; -+ unsigned int sigen; -+ -+ dentry = NULL; -+ inode = ilookup(sb, ino); -+ if (!inode) -+ goto out; -+ -+ dentry = ERR_PTR(-ESTALE); -+ sigen = au_sigen(sb); -+ if (unlikely(is_bad_inode(inode) -+ || IS_DEADDIR(inode) -+ || sigen != au_iigen(inode, NULL))) -+ goto out_iput; -+ -+ dentry = NULL; -+ if (!dir_ino || S_ISDIR(inode->i_mode)) -+ dentry = d_find_alias(inode); -+ else { -+ spin_lock(&inode->i_lock); -+ hlist_for_each_entry(d, &inode->i_dentry, d_u.d_alias) { -+ spin_lock(&d->d_lock); -+ if (!au_test_anon(d) -+ && d->d_parent->d_inode->i_ino == dir_ino) { -+ dentry = dget_dlock(d); -+ spin_unlock(&d->d_lock); -+ break; -+ } -+ spin_unlock(&d->d_lock); -+ } -+ spin_unlock(&inode->i_lock); -+ } -+ if (unlikely(dentry && au_digen_test(dentry, sigen))) { -+ /* need to refresh */ -+ dput(dentry); -+ dentry = NULL; -+ } -+ -+out_iput: -+ iput(inode); -+out: -+ AuTraceErrPtr(dentry); -+ return dentry; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* todo: dirty? */ -+/* if exportfs_decode_fh() passed vfsmount*, we could be happy */ -+ -+struct au_compare_mnt_args { -+ /* input */ -+ struct super_block *sb; -+ -+ /* output */ -+ struct vfsmount *mnt; -+}; -+ -+static int au_compare_mnt(struct vfsmount *mnt, void *arg) -+{ -+ struct au_compare_mnt_args *a = arg; -+ -+ if (mnt->mnt_sb != a->sb) -+ return 0; -+ a->mnt = mntget(mnt); -+ return 1; -+} -+ -+static struct vfsmount *au_mnt_get(struct super_block *sb) -+{ -+ int err; -+ struct path root; -+ struct au_compare_mnt_args args = { -+ .sb = sb -+ }; -+ -+ get_fs_root(current->fs, &root); -+ rcu_read_lock(); -+ err = iterate_mounts(au_compare_mnt, &args, root.mnt); -+ rcu_read_unlock(); -+ path_put(&root); -+ AuDebugOn(!err); -+ AuDebugOn(!args.mnt); -+ return args.mnt; -+} -+ -+struct au_nfsd_si_lock { -+ unsigned int sigen; -+ aufs_bindex_t bindex, br_id; -+ unsigned char force_lock; -+}; -+ -+static int si_nfsd_read_lock(struct super_block *sb, -+ struct au_nfsd_si_lock *nsi_lock) -+{ -+ int err; -+ aufs_bindex_t bindex; -+ -+ si_read_lock(sb, AuLock_FLUSH); -+ -+ /* branch id may be wrapped around */ -+ err = 0; -+ bindex = au_br_index(sb, nsi_lock->br_id); -+ if (bindex >= 0 && nsi_lock->sigen + AUFS_BRANCH_MAX > au_sigen(sb)) -+ goto out; /* success */ -+ -+ err = -ESTALE; -+ bindex = -1; -+ if (!nsi_lock->force_lock) -+ si_read_unlock(sb); -+ -+out: -+ nsi_lock->bindex = bindex; -+ return err; -+} -+ -+struct find_name_by_ino { -+ struct dir_context ctx; -+ int called, found; -+ ino_t ino; -+ char *name; -+ int namelen; -+}; -+ -+static int -+find_name_by_ino(struct dir_context *ctx, const char *name, int namelen, -+ loff_t offset, u64 ino, unsigned int d_type) -+{ -+ struct find_name_by_ino *a = container_of(ctx, struct find_name_by_ino, -+ ctx); -+ -+ a->called++; -+ if (a->ino != ino) -+ return 0; -+ -+ memcpy(a->name, name, namelen); -+ a->namelen = namelen; -+ a->found = 1; -+ return 1; -+} -+ -+static struct dentry *au_lkup_by_ino(struct path *path, ino_t ino, -+ struct au_nfsd_si_lock *nsi_lock) -+{ -+ struct dentry *dentry, *parent; -+ struct file *file; -+ struct inode *dir; -+ struct find_name_by_ino arg = { -+ .ctx = { -+ .actor = au_diractor(find_name_by_ino) -+ } -+ }; -+ int err; -+ -+ parent = path->dentry; -+ if (nsi_lock) -+ si_read_unlock(parent->d_sb); -+ file = vfsub_dentry_open(path, au_dir_roflags); -+ dentry = (void *)file; -+ if (IS_ERR(file)) -+ goto out; -+ -+ dentry = ERR_PTR(-ENOMEM); -+ arg.name = (void *)__get_free_page(GFP_NOFS); -+ if (unlikely(!arg.name)) -+ goto out_file; -+ arg.ino = ino; -+ arg.found = 0; -+ do { -+ arg.called = 0; -+ /* smp_mb(); */ -+ err = vfsub_iterate_dir(file, &arg.ctx); -+ } while (!err && !arg.found && arg.called); -+ dentry = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out_name; -+ /* instead of ENOENT */ -+ dentry = ERR_PTR(-ESTALE); -+ if (!arg.found) -+ goto out_name; -+ -+ /* do not call vfsub_lkup_one() */ -+ dir = parent->d_inode; -+ mutex_lock(&dir->i_mutex); -+ dentry = vfsub_lookup_one_len(arg.name, parent, arg.namelen); -+ mutex_unlock(&dir->i_mutex); -+ AuTraceErrPtr(dentry); -+ if (IS_ERR(dentry)) -+ goto out_name; -+ AuDebugOn(au_test_anon(dentry)); -+ if (unlikely(!dentry->d_inode)) { -+ dput(dentry); -+ dentry = ERR_PTR(-ENOENT); -+ } -+ -+out_name: -+ free_page((unsigned long)arg.name); -+out_file: -+ fput(file); -+out: -+ if (unlikely(nsi_lock -+ && si_nfsd_read_lock(parent->d_sb, nsi_lock) < 0)) -+ if (!IS_ERR(dentry)) { -+ dput(dentry); -+ dentry = ERR_PTR(-ESTALE); -+ } -+ AuTraceErrPtr(dentry); -+ return dentry; -+} -+ -+static struct dentry *decode_by_dir_ino(struct super_block *sb, ino_t ino, -+ ino_t dir_ino, -+ struct au_nfsd_si_lock *nsi_lock) -+{ -+ struct dentry *dentry; -+ struct path path; -+ -+ if (dir_ino != AUFS_ROOT_INO) { -+ path.dentry = decode_by_ino(sb, dir_ino, 0); -+ dentry = path.dentry; -+ if (!path.dentry || IS_ERR(path.dentry)) -+ goto out; -+ AuDebugOn(au_test_anon(path.dentry)); -+ } else -+ path.dentry = dget(sb->s_root); -+ -+ path.mnt = au_mnt_get(sb); -+ dentry = au_lkup_by_ino(&path, ino, nsi_lock); -+ path_put(&path); -+ -+out: -+ AuTraceErrPtr(dentry); -+ return dentry; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int h_acceptable(void *expv, struct dentry *dentry) -+{ -+ return 1; -+} -+ -+static char *au_build_path(struct dentry *h_parent, struct path *h_rootpath, -+ char *buf, int len, struct super_block *sb) -+{ -+ char *p; -+ int n; -+ struct path path; -+ -+ p = d_path(h_rootpath, buf, len); -+ if (IS_ERR(p)) -+ goto out; -+ n = strlen(p); -+ -+ path.mnt = h_rootpath->mnt; -+ path.dentry = h_parent; -+ p = d_path(&path, buf, len); -+ if (IS_ERR(p)) -+ goto out; -+ if (n != 1) -+ p += n; -+ -+ path.mnt = au_mnt_get(sb); -+ path.dentry = sb->s_root; -+ p = d_path(&path, buf, len - strlen(p)); -+ mntput(path.mnt); -+ if (IS_ERR(p)) -+ goto out; -+ if (n != 1) -+ p[strlen(p)] = '/'; -+ -+out: -+ AuTraceErrPtr(p); -+ return p; -+} -+ -+static -+struct dentry *decode_by_path(struct super_block *sb, ino_t ino, __u32 *fh, -+ int fh_len, struct au_nfsd_si_lock *nsi_lock) -+{ -+ struct dentry *dentry, *h_parent, *root; -+ struct super_block *h_sb; -+ char *pathname, *p; -+ struct vfsmount *h_mnt; -+ struct au_branch *br; -+ int err; -+ struct path path; -+ -+ br = au_sbr(sb, nsi_lock->bindex); -+ h_mnt = au_br_mnt(br); -+ h_sb = h_mnt->mnt_sb; -+ /* todo: call lower fh_to_dentry()? fh_to_parent()? */ -+ h_parent = exportfs_decode_fh(h_mnt, (void *)(fh + Fh_tail), -+ fh_len - Fh_tail, fh[Fh_h_type], -+ h_acceptable, /*context*/NULL); -+ dentry = h_parent; -+ if (unlikely(!h_parent || IS_ERR(h_parent))) { -+ AuWarn1("%s decode_fh failed, %ld\n", -+ au_sbtype(h_sb), PTR_ERR(h_parent)); -+ goto out; -+ } -+ dentry = NULL; -+ if (unlikely(au_test_anon(h_parent))) { -+ AuWarn1("%s decode_fh returned a disconnected dentry\n", -+ au_sbtype(h_sb)); -+ goto out_h_parent; -+ } -+ -+ dentry = ERR_PTR(-ENOMEM); -+ pathname = (void *)__get_free_page(GFP_NOFS); -+ if (unlikely(!pathname)) -+ goto out_h_parent; -+ -+ root = sb->s_root; -+ path.mnt = h_mnt; -+ di_read_lock_parent(root, !AuLock_IR); -+ path.dentry = au_h_dptr(root, nsi_lock->bindex); -+ di_read_unlock(root, !AuLock_IR); -+ p = au_build_path(h_parent, &path, pathname, PAGE_SIZE, sb); -+ dentry = (void *)p; -+ if (IS_ERR(p)) -+ goto out_pathname; -+ -+ si_read_unlock(sb); -+ err = vfsub_kern_path(p, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path); -+ dentry = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out_relock; -+ -+ dentry = ERR_PTR(-ENOENT); -+ AuDebugOn(au_test_anon(path.dentry)); -+ if (unlikely(!path.dentry->d_inode)) -+ goto out_path; -+ -+ if (ino != path.dentry->d_inode->i_ino) -+ dentry = au_lkup_by_ino(&path, ino, /*nsi_lock*/NULL); -+ else -+ dentry = dget(path.dentry); -+ -+out_path: -+ path_put(&path); -+out_relock: -+ if (unlikely(si_nfsd_read_lock(sb, nsi_lock) < 0)) -+ if (!IS_ERR(dentry)) { -+ dput(dentry); -+ dentry = ERR_PTR(-ESTALE); -+ } -+out_pathname: -+ free_page((unsigned long)pathname); -+out_h_parent: -+ dput(h_parent); -+out: -+ AuTraceErrPtr(dentry); -+ return dentry; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static struct dentry * -+aufs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len, -+ int fh_type) -+{ -+ struct dentry *dentry; -+ __u32 *fh = fid->raw; -+ struct au_branch *br; -+ ino_t ino, dir_ino; -+ struct au_nfsd_si_lock nsi_lock = { -+ .force_lock = 0 -+ }; -+ -+ dentry = ERR_PTR(-ESTALE); -+ /* it should never happen, but the file handle is unreliable */ -+ if (unlikely(fh_len < Fh_tail)) -+ goto out; -+ nsi_lock.sigen = fh[Fh_sigen]; -+ nsi_lock.br_id = fh[Fh_br_id]; -+ -+ /* branch id may be wrapped around */ -+ br = NULL; -+ if (unlikely(si_nfsd_read_lock(sb, &nsi_lock))) -+ goto out; -+ nsi_lock.force_lock = 1; -+ -+ /* is this inode still cached? */ -+ ino = decode_ino(fh + Fh_ino); -+ /* it should never happen */ -+ if (unlikely(ino == AUFS_ROOT_INO)) -+ goto out_unlock; -+ -+ dir_ino = decode_ino(fh + Fh_dir_ino); -+ dentry = decode_by_ino(sb, ino, dir_ino); -+ if (IS_ERR(dentry)) -+ goto out_unlock; -+ if (dentry) -+ goto accept; -+ -+ /* is the parent dir cached? */ -+ br = au_sbr(sb, nsi_lock.bindex); -+ atomic_inc(&br->br_count); -+ dentry = decode_by_dir_ino(sb, ino, dir_ino, &nsi_lock); -+ if (IS_ERR(dentry)) -+ goto out_unlock; -+ if (dentry) -+ goto accept; -+ -+ /* lookup path */ -+ dentry = decode_by_path(sb, ino, fh, fh_len, &nsi_lock); -+ if (IS_ERR(dentry)) -+ goto out_unlock; -+ if (unlikely(!dentry)) -+ /* todo?: make it ESTALE */ -+ goto out_unlock; -+ -+accept: -+ if (!au_digen_test(dentry, au_sigen(sb)) -+ && dentry->d_inode->i_generation == fh[Fh_igen]) -+ goto out_unlock; /* success */ -+ -+ dput(dentry); -+ dentry = ERR_PTR(-ESTALE); -+out_unlock: -+ if (br) -+ atomic_dec(&br->br_count); -+ si_read_unlock(sb); -+out: -+ AuTraceErrPtr(dentry); -+ return dentry; -+} -+ -+#if 0 /* reserved for future use */ -+/* support subtreecheck option */ -+static struct dentry *aufs_fh_to_parent(struct super_block *sb, struct fid *fid, -+ int fh_len, int fh_type) -+{ -+ struct dentry *parent; -+ __u32 *fh = fid->raw; -+ ino_t dir_ino; -+ -+ dir_ino = decode_ino(fh + Fh_dir_ino); -+ parent = decode_by_ino(sb, dir_ino, 0); -+ if (IS_ERR(parent)) -+ goto out; -+ if (!parent) -+ parent = decode_by_path(sb, au_br_index(sb, fh[Fh_br_id]), -+ dir_ino, fh, fh_len); -+ -+out: -+ AuTraceErrPtr(parent); -+ return parent; -+} -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int aufs_encode_fh(struct inode *inode, __u32 *fh, int *max_len, -+ struct inode *dir) -+{ -+ int err; -+ aufs_bindex_t bindex; -+ struct super_block *sb, *h_sb; -+ struct dentry *dentry, *parent, *h_parent; -+ struct inode *h_dir; -+ struct au_branch *br; -+ -+ err = -ENOSPC; -+ if (unlikely(*max_len <= Fh_tail)) { -+ AuWarn1("NFSv2 client (max_len %d)?\n", *max_len); -+ goto out; -+ } -+ -+ err = FILEID_ROOT; -+ if (inode->i_ino == AUFS_ROOT_INO) { -+ AuDebugOn(inode->i_ino != AUFS_ROOT_INO); -+ goto out; -+ } -+ -+ h_parent = NULL; -+ sb = inode->i_sb; -+ err = si_read_lock(sb, AuLock_FLUSH); -+ if (unlikely(err)) -+ goto out; -+ -+#ifdef CONFIG_AUFS_DEBUG -+ if (unlikely(!au_opt_test(au_mntflags(sb), XINO))) -+ AuWarn1("NFS-exporting requires xino\n"); -+#endif -+ err = -EIO; -+ parent = NULL; -+ ii_read_lock_child(inode); -+ bindex = au_ibstart(inode); -+ if (!dir) { -+ dentry = d_find_any_alias(inode); -+ if (unlikely(!dentry)) -+ goto out_unlock; -+ AuDebugOn(au_test_anon(dentry)); -+ parent = dget_parent(dentry); -+ dput(dentry); -+ if (unlikely(!parent)) -+ goto out_unlock; -+ dir = parent->d_inode; -+ } -+ -+ ii_read_lock_parent(dir); -+ h_dir = au_h_iptr(dir, bindex); -+ ii_read_unlock(dir); -+ if (unlikely(!h_dir)) -+ goto out_parent; -+ h_parent = d_find_any_alias(h_dir); -+ if (unlikely(!h_parent)) -+ goto out_hparent; -+ -+ err = -EPERM; -+ br = au_sbr(sb, bindex); -+ h_sb = au_br_sb(br); -+ if (unlikely(!h_sb->s_export_op)) { -+ AuErr1("%s branch is not exportable\n", au_sbtype(h_sb)); -+ goto out_hparent; -+ } -+ -+ fh[Fh_br_id] = br->br_id; -+ fh[Fh_sigen] = au_sigen(sb); -+ encode_ino(fh + Fh_ino, inode->i_ino); -+ encode_ino(fh + Fh_dir_ino, dir->i_ino); -+ fh[Fh_igen] = inode->i_generation; -+ -+ *max_len -= Fh_tail; -+ fh[Fh_h_type] = exportfs_encode_fh(h_parent, (void *)(fh + Fh_tail), -+ max_len, -+ /*connectable or subtreecheck*/0); -+ err = fh[Fh_h_type]; -+ *max_len += Fh_tail; -+ /* todo: macros? */ -+ if (err != FILEID_INVALID) -+ err = 99; -+ else -+ AuWarn1("%s encode_fh failed\n", au_sbtype(h_sb)); -+ -+out_hparent: -+ dput(h_parent); -+out_parent: -+ dput(parent); -+out_unlock: -+ ii_read_unlock(inode); -+ si_read_unlock(sb); -+out: -+ if (unlikely(err < 0)) -+ err = FILEID_INVALID; -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int aufs_commit_metadata(struct inode *inode) -+{ -+ int err; -+ aufs_bindex_t bindex; -+ struct super_block *sb; -+ struct inode *h_inode; -+ int (*f)(struct inode *inode); -+ -+ sb = inode->i_sb; -+ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); -+ ii_write_lock_child(inode); -+ bindex = au_ibstart(inode); -+ AuDebugOn(bindex < 0); -+ h_inode = au_h_iptr(inode, bindex); -+ -+ f = h_inode->i_sb->s_export_op->commit_metadata; -+ if (f) -+ err = f(h_inode); -+ else { -+ struct writeback_control wbc = { -+ .sync_mode = WB_SYNC_ALL, -+ .nr_to_write = 0 /* metadata only */ -+ }; -+ -+ err = sync_inode(h_inode, &wbc); -+ } -+ -+ au_cpup_attr_timesizes(inode); -+ ii_write_unlock(inode); -+ si_read_unlock(sb); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static struct export_operations aufs_export_op = { -+ .fh_to_dentry = aufs_fh_to_dentry, -+ /* .fh_to_parent = aufs_fh_to_parent, */ -+ .encode_fh = aufs_encode_fh, -+ .commit_metadata = aufs_commit_metadata -+}; -+ -+void au_export_init(struct super_block *sb) -+{ -+ struct au_sbinfo *sbinfo; -+ __u32 u; -+ -+ sb->s_export_op = &aufs_export_op; -+ sbinfo = au_sbi(sb); -+ sbinfo->si_xigen = NULL; -+ get_random_bytes(&u, sizeof(u)); -+ BUILD_BUG_ON(sizeof(u) != sizeof(int)); -+ atomic_set(&sbinfo->si_xigen_next, u); -+} -diff --git a/fs/aufs/f_op.c b/fs/aufs/f_op.c -new file mode 100644 -index 0000000..b08981a ---- /dev/null -+++ b/fs/aufs/f_op.c -@@ -0,0 +1,781 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * file and vm operations -+ */ -+ -+#include -+#include -+#include -+#include -+#include "aufs.h" -+ -+int au_do_open_nondir(struct file *file, int flags, struct file *h_file) -+{ -+ int err; -+ aufs_bindex_t bindex; -+ struct dentry *dentry, *h_dentry; -+ struct au_finfo *finfo; -+ struct inode *h_inode; -+ -+ FiMustWriteLock(file); -+ -+ err = 0; -+ dentry = file->f_dentry; -+ AuDebugOn(IS_ERR_OR_NULL(dentry)); -+ finfo = au_fi(file); -+ memset(&finfo->fi_htop, 0, sizeof(finfo->fi_htop)); -+ atomic_set(&finfo->fi_mmapped, 0); -+ bindex = au_dbstart(dentry); -+ if (!h_file) { -+ h_dentry = au_h_dptr(dentry, bindex); -+ err = vfsub_test_mntns(file->f_path.mnt, h_dentry->d_sb); -+ if (unlikely(err)) -+ goto out; -+ h_file = au_h_open(dentry, bindex, flags, file, /*force_wr*/0); -+ } else { -+ h_dentry = h_file->f_dentry; -+ err = vfsub_test_mntns(file->f_path.mnt, h_dentry->d_sb); -+ if (unlikely(err)) -+ goto out; -+ get_file(h_file); -+ } -+ if (IS_ERR(h_file)) -+ err = PTR_ERR(h_file); -+ else { -+ if ((flags & __O_TMPFILE) -+ && !(flags & O_EXCL)) { -+ h_inode = file_inode(h_file); -+ spin_lock(&h_inode->i_lock); -+ h_inode->i_state |= I_LINKABLE; -+ spin_unlock(&h_inode->i_lock); -+ } -+ au_set_fbstart(file, bindex); -+ au_set_h_fptr(file, bindex, h_file); -+ au_update_figen(file); -+ /* todo: necessary? */ -+ /* file->f_ra = h_file->f_ra; */ -+ } -+ -+out: -+ return err; -+} -+ -+static int aufs_open_nondir(struct inode *inode __maybe_unused, -+ struct file *file) -+{ -+ int err; -+ struct super_block *sb; -+ struct au_do_open_args args = { -+ .open = au_do_open_nondir -+ }; -+ -+ AuDbg("%pD, f_flags 0x%x, f_mode 0x%x\n", -+ file, vfsub_file_flags(file), file->f_mode); -+ -+ sb = file->f_dentry->d_sb; -+ si_read_lock(sb, AuLock_FLUSH); -+ err = au_do_open(file, &args); -+ si_read_unlock(sb); -+ return err; -+} -+ -+int aufs_release_nondir(struct inode *inode __maybe_unused, struct file *file) -+{ -+ struct au_finfo *finfo; -+ aufs_bindex_t bindex; -+ -+ finfo = au_fi(file); -+ au_sphl_del(&finfo->fi_hlist, &au_sbi(file->f_dentry->d_sb)->si_files); -+ bindex = finfo->fi_btop; -+ if (bindex >= 0) -+ au_set_h_fptr(file, bindex, NULL); -+ -+ au_finfo_fin(file); -+ return 0; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_do_flush_nondir(struct file *file, fl_owner_t id) -+{ -+ int err; -+ struct file *h_file; -+ -+ err = 0; -+ h_file = au_hf_top(file); -+ if (h_file) -+ err = vfsub_flush(h_file, id); -+ return err; -+} -+ -+static int aufs_flush_nondir(struct file *file, fl_owner_t id) -+{ -+ return au_do_flush(file, id, au_do_flush_nondir); -+} -+ -+/* ---------------------------------------------------------------------- */ -+/* -+ * read and write functions acquire [fdi]_rwsem once, but release before -+ * mmap_sem. This is because to stop a race condition between mmap(2). -+ * Releasing these aufs-rwsem should be safe, no branch-mamagement (by keeping -+ * si_rwsem), no harmful copy-up should happen. Actually copy-up may happen in -+ * read functions after [fdi]_rwsem are released, but it should be harmless. -+ */ -+ -+/* Callers should call au_read_post() or fput() in the end */ -+struct file *au_read_pre(struct file *file, int keep_fi) -+{ -+ struct file *h_file; -+ int err; -+ -+ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0); -+ if (!err) { -+ di_read_unlock(file->f_dentry, AuLock_IR); -+ h_file = au_hf_top(file); -+ get_file(h_file); -+ if (!keep_fi) -+ fi_read_unlock(file); -+ } else -+ h_file = ERR_PTR(err); -+ -+ return h_file; -+} -+ -+static void au_read_post(struct inode *inode, struct file *h_file) -+{ -+ /* update without lock, I don't think it a problem */ -+ fsstack_copy_attr_atime(inode, file_inode(h_file)); -+ fput(h_file); -+} -+ -+struct au_write_pre { -+ blkcnt_t blks; -+ aufs_bindex_t bstart; -+}; -+ -+/* -+ * return with iinfo is write-locked -+ * callers should call au_write_post() or iinfo_write_unlock() + fput() in the -+ * end -+ */ -+static struct file *au_write_pre(struct file *file, int do_ready, -+ struct au_write_pre *wpre) -+{ -+ struct file *h_file; -+ struct dentry *dentry; -+ int err; -+ struct au_pin pin; -+ -+ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); -+ h_file = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out; -+ -+ dentry = file->f_dentry; -+ if (do_ready) { -+ err = au_ready_to_write(file, -1, &pin); -+ if (unlikely(err)) { -+ h_file = ERR_PTR(err); -+ di_write_unlock(dentry); -+ goto out_fi; -+ } -+ } -+ -+ di_downgrade_lock(dentry, /*flags*/0); -+ if (wpre) -+ wpre->bstart = au_fbstart(file); -+ h_file = au_hf_top(file); -+ get_file(h_file); -+ if (wpre) -+ wpre->blks = file_inode(h_file)->i_blocks; -+ if (do_ready) -+ au_unpin(&pin); -+ di_read_unlock(dentry, /*flags*/0); -+ -+out_fi: -+ fi_write_unlock(file); -+out: -+ return h_file; -+} -+ -+static void au_write_post(struct inode *inode, struct file *h_file, -+ struct au_write_pre *wpre, ssize_t written) -+{ -+ struct inode *h_inode; -+ -+ au_cpup_attr_timesizes(inode); -+ AuDebugOn(au_ibstart(inode) != wpre->bstart); -+ h_inode = file_inode(h_file); -+ inode->i_mode = h_inode->i_mode; -+ ii_write_unlock(inode); -+ fput(h_file); -+ -+ /* AuDbg("blks %llu, %llu\n", (u64)blks, (u64)h_inode->i_blocks); */ -+ if (written > 0) -+ au_fhsm_wrote(inode->i_sb, wpre->bstart, -+ /*force*/h_inode->i_blocks > wpre->blks); -+} -+ -+static ssize_t aufs_read(struct file *file, char __user *buf, size_t count, -+ loff_t *ppos) -+{ -+ ssize_t err; -+ struct inode *inode; -+ struct file *h_file; -+ struct super_block *sb; -+ -+ inode = file_inode(file); -+ sb = inode->i_sb; -+ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); -+ -+ h_file = au_read_pre(file, /*keep_fi*/0); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ /* filedata may be obsoleted by concurrent copyup, but no problem */ -+ err = vfsub_read_u(h_file, buf, count, ppos); -+ /* todo: necessary? */ -+ /* file->f_ra = h_file->f_ra; */ -+ au_read_post(inode, h_file); -+ -+out: -+ si_read_unlock(sb); -+ return err; -+} -+ -+/* -+ * todo: very ugly -+ * it locks both of i_mutex and si_rwsem for read in safe. -+ * if the plink maintenance mode continues forever (that is the problem), -+ * may loop forever. -+ */ -+static void au_mtx_and_read_lock(struct inode *inode) -+{ -+ int err; -+ struct super_block *sb = inode->i_sb; -+ -+ while (1) { -+ mutex_lock(&inode->i_mutex); -+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ if (!err) -+ break; -+ mutex_unlock(&inode->i_mutex); -+ si_read_lock(sb, AuLock_NOPLMW); -+ si_read_unlock(sb); -+ } -+} -+ -+static ssize_t aufs_write(struct file *file, const char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ ssize_t err; -+ struct au_write_pre wpre; -+ struct inode *inode; -+ struct file *h_file; -+ char __user *buf = (char __user *)ubuf; -+ -+ inode = file_inode(file); -+ au_mtx_and_read_lock(inode); -+ -+ h_file = au_write_pre(file, /*do_ready*/1, &wpre); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ err = vfsub_write_u(h_file, buf, count, ppos); -+ au_write_post(inode, h_file, &wpre, err); -+ -+out: -+ si_read_unlock(inode->i_sb); -+ mutex_unlock(&inode->i_mutex); -+ return err; -+} -+ -+static ssize_t au_do_iter(struct file *h_file, int rw, struct kiocb *kio, -+ struct iov_iter *iov_iter) -+{ -+ ssize_t err; -+ struct file *file; -+ ssize_t (*iter)(struct kiocb *, struct iov_iter *); -+ ssize_t (*aio)(struct kiocb *, const struct iovec *, unsigned long, -+ loff_t); -+ -+ err = security_file_permission(h_file, rw); -+ if (unlikely(err)) -+ goto out; -+ -+ err = -ENOSYS; -+ iter = NULL; -+ aio = NULL; -+ if (rw == MAY_READ) { -+ iter = h_file->f_op->read_iter; -+ aio = h_file->f_op->aio_read; -+ } else if (rw == MAY_WRITE) { -+ iter = h_file->f_op->write_iter; -+ aio = h_file->f_op->aio_write; -+ } -+ -+ file = kio->ki_filp; -+ kio->ki_filp = h_file; -+ if (iter) { -+ lockdep_off(); -+ err = iter(kio, iov_iter); -+ lockdep_on(); -+ } else if (aio) { -+ lockdep_off(); -+ err = aio(kio, iov_iter->iov, iov_iter->nr_segs, kio->ki_pos); -+ lockdep_on(); -+ } else -+ /* currently there is no such fs */ -+ WARN_ON_ONCE(1); -+ kio->ki_filp = file; -+ -+out: -+ return err; -+} -+ -+static ssize_t aufs_read_iter(struct kiocb *kio, struct iov_iter *iov_iter) -+{ -+ ssize_t err; -+ struct file *file, *h_file; -+ struct inode *inode; -+ struct super_block *sb; -+ -+ file = kio->ki_filp; -+ inode = file_inode(file); -+ sb = inode->i_sb; -+ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); -+ -+ h_file = au_read_pre(file, /*keep_fi*/0); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ err = au_do_iter(h_file, MAY_READ, kio, iov_iter); -+ /* todo: necessary? */ -+ /* file->f_ra = h_file->f_ra; */ -+ au_read_post(inode, h_file); -+ -+out: -+ si_read_unlock(sb); -+ return err; -+} -+ -+static ssize_t aufs_write_iter(struct kiocb *kio, struct iov_iter *iov_iter) -+{ -+ ssize_t err; -+ struct au_write_pre wpre; -+ struct inode *inode; -+ struct file *file, *h_file; -+ -+ file = kio->ki_filp; -+ inode = file_inode(file); -+ au_mtx_and_read_lock(inode); -+ -+ h_file = au_write_pre(file, /*do_ready*/1, &wpre); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ err = au_do_iter(h_file, MAY_WRITE, kio, iov_iter); -+ au_write_post(inode, h_file, &wpre, err); -+ -+out: -+ si_read_unlock(inode->i_sb); -+ mutex_unlock(&inode->i_mutex); -+ return err; -+} -+ -+static ssize_t aufs_splice_read(struct file *file, loff_t *ppos, -+ struct pipe_inode_info *pipe, size_t len, -+ unsigned int flags) -+{ -+ ssize_t err; -+ struct file *h_file; -+ struct inode *inode; -+ struct super_block *sb; -+ -+ inode = file_inode(file); -+ sb = inode->i_sb; -+ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); -+ -+ h_file = au_read_pre(file, /*keep_fi*/1); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ if (au_test_loopback_kthread()) { -+ au_warn_loopback(h_file->f_dentry->d_sb); -+ if (file->f_mapping != h_file->f_mapping) { -+ file->f_mapping = h_file->f_mapping; -+ smp_mb(); /* unnecessary? */ -+ } -+ } -+ fi_read_unlock(file); -+ -+ err = vfsub_splice_to(h_file, ppos, pipe, len, flags); -+ /* todo: necessasry? */ -+ /* file->f_ra = h_file->f_ra; */ -+ au_read_post(inode, h_file); -+ -+out: -+ si_read_unlock(sb); -+ return err; -+} -+ -+static ssize_t -+aufs_splice_write(struct pipe_inode_info *pipe, struct file *file, loff_t *ppos, -+ size_t len, unsigned int flags) -+{ -+ ssize_t err; -+ struct au_write_pre wpre; -+ struct inode *inode; -+ struct file *h_file; -+ -+ inode = file_inode(file); -+ au_mtx_and_read_lock(inode); -+ -+ h_file = au_write_pre(file, /*do_ready*/1, &wpre); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ err = vfsub_splice_from(pipe, h_file, ppos, len, flags); -+ au_write_post(inode, h_file, &wpre, err); -+ -+out: -+ si_read_unlock(inode->i_sb); -+ mutex_unlock(&inode->i_mutex); -+ return err; -+} -+ -+static long aufs_fallocate(struct file *file, int mode, loff_t offset, -+ loff_t len) -+{ -+ long err; -+ struct au_write_pre wpre; -+ struct inode *inode; -+ struct file *h_file; -+ -+ inode = file_inode(file); -+ au_mtx_and_read_lock(inode); -+ -+ h_file = au_write_pre(file, /*do_ready*/1, &wpre); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ lockdep_off(); -+ err = do_fallocate(h_file, mode, offset, len); -+ lockdep_on(); -+ au_write_post(inode, h_file, &wpre, /*written*/1); -+ -+out: -+ si_read_unlock(inode->i_sb); -+ mutex_unlock(&inode->i_mutex); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * The locking order around current->mmap_sem. -+ * - in most and regular cases -+ * file I/O syscall -- aufs_read() or something -+ * -- si_rwsem for read -- mmap_sem -+ * (Note that [fdi]i_rwsem are released before mmap_sem). -+ * - in mmap case -+ * mmap(2) -- mmap_sem -- aufs_mmap() -- si_rwsem for read -- [fdi]i_rwsem -+ * This AB-BA order is definitly bad, but is not a problem since "si_rwsem for -+ * read" allows muliple processes to acquire it and [fdi]i_rwsem are not held in -+ * file I/O. Aufs needs to stop lockdep in aufs_mmap() though. -+ * It means that when aufs acquires si_rwsem for write, the process should never -+ * acquire mmap_sem. -+ * -+ * Actually aufs_iterate() holds [fdi]i_rwsem before mmap_sem, but this is not a -+ * problem either since any directory is not able to be mmap-ed. -+ * The similar scenario is applied to aufs_readlink() too. -+ */ -+ -+#if 0 /* stop calling security_file_mmap() */ -+/* cf. linux/include/linux/mman.h: calc_vm_prot_bits() */ -+#define AuConv_VM_PROT(f, b) _calc_vm_trans(f, VM_##b, PROT_##b) -+ -+static unsigned long au_arch_prot_conv(unsigned long flags) -+{ -+ /* currently ppc64 only */ -+#ifdef CONFIG_PPC64 -+ /* cf. linux/arch/powerpc/include/asm/mman.h */ -+ AuDebugOn(arch_calc_vm_prot_bits(-1) != VM_SAO); -+ return AuConv_VM_PROT(flags, SAO); -+#else -+ AuDebugOn(arch_calc_vm_prot_bits(-1)); -+ return 0; -+#endif -+} -+ -+static unsigned long au_prot_conv(unsigned long flags) -+{ -+ return AuConv_VM_PROT(flags, READ) -+ | AuConv_VM_PROT(flags, WRITE) -+ | AuConv_VM_PROT(flags, EXEC) -+ | au_arch_prot_conv(flags); -+} -+ -+/* cf. linux/include/linux/mman.h: calc_vm_flag_bits() */ -+#define AuConv_VM_MAP(f, b) _calc_vm_trans(f, VM_##b, MAP_##b) -+ -+static unsigned long au_flag_conv(unsigned long flags) -+{ -+ return AuConv_VM_MAP(flags, GROWSDOWN) -+ | AuConv_VM_MAP(flags, DENYWRITE) -+ | AuConv_VM_MAP(flags, LOCKED); -+} -+#endif -+ -+static int aufs_mmap(struct file *file, struct vm_area_struct *vma) -+{ -+ int err; -+ const unsigned char wlock -+ = (file->f_mode & FMODE_WRITE) && (vma->vm_flags & VM_SHARED); -+ struct super_block *sb; -+ struct file *h_file; -+ struct inode *inode; -+ -+ AuDbgVmRegion(file, vma); -+ -+ inode = file_inode(file); -+ sb = inode->i_sb; -+ lockdep_off(); -+ si_read_lock(sb, AuLock_NOPLMW); -+ -+ h_file = au_write_pre(file, wlock, /*wpre*/NULL); -+ lockdep_on(); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ err = 0; -+ au_set_mmapped(file); -+ au_vm_file_reset(vma, h_file); -+ /* -+ * we cannot call security_mmap_file() here since it may acquire -+ * mmap_sem or i_mutex. -+ * -+ * err = security_mmap_file(h_file, au_prot_conv(vma->vm_flags), -+ * au_flag_conv(vma->vm_flags)); -+ */ -+ if (!err) -+ err = h_file->f_op->mmap(h_file, vma); -+ if (!err) { -+ au_vm_prfile_set(vma, file); -+ fsstack_copy_attr_atime(inode, file_inode(h_file)); -+ goto out_fput; /* success */ -+ } -+ au_unset_mmapped(file); -+ au_vm_file_reset(vma, file); -+ -+out_fput: -+ lockdep_off(); -+ ii_write_unlock(inode); -+ lockdep_on(); -+ fput(h_file); -+out: -+ lockdep_off(); -+ si_read_unlock(sb); -+ lockdep_on(); -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int aufs_fsync_nondir(struct file *file, loff_t start, loff_t end, -+ int datasync) -+{ -+ int err; -+ struct au_write_pre wpre; -+ struct inode *inode; -+ struct file *h_file; -+ -+ err = 0; /* -EBADF; */ /* posix? */ -+ if (unlikely(!(file->f_mode & FMODE_WRITE))) -+ goto out; -+ -+ inode = file_inode(file); -+ au_mtx_and_read_lock(inode); -+ -+ h_file = au_write_pre(file, /*do_ready*/1, &wpre); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out_unlock; -+ -+ err = vfsub_fsync(h_file, &h_file->f_path, datasync); -+ au_write_post(inode, h_file, &wpre, /*written*/0); -+ -+out_unlock: -+ si_read_unlock(inode->i_sb); -+ mutex_unlock(&inode->i_mutex); -+out: -+ return err; -+} -+ -+/* no one supports this operation, currently */ -+#if 0 -+static int aufs_aio_fsync_nondir(struct kiocb *kio, int datasync) -+{ -+ int err; -+ struct au_write_pre wpre; -+ struct inode *inode; -+ struct file *file, *h_file; -+ -+ err = 0; /* -EBADF; */ /* posix? */ -+ if (unlikely(!(file->f_mode & FMODE_WRITE))) -+ goto out; -+ -+ file = kio->ki_filp; -+ inode = file_inode(file); -+ au_mtx_and_read_lock(inode); -+ -+ h_file = au_write_pre(file, /*do_ready*/1, &wpre); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out_unlock; -+ -+ err = -ENOSYS; -+ h_file = au_hf_top(file); -+ if (h_file->f_op->aio_fsync) { -+ struct mutex *h_mtx; -+ -+ h_mtx = &file_inode(h_file)->i_mutex; -+ if (!is_sync_kiocb(kio)) { -+ get_file(h_file); -+ fput(file); -+ } -+ kio->ki_filp = h_file; -+ err = h_file->f_op->aio_fsync(kio, datasync); -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); -+ if (!err) -+ vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL); -+ /*ignore*/ -+ mutex_unlock(h_mtx); -+ } -+ au_write_post(inode, h_file, &wpre, /*written*/0); -+ -+out_unlock: -+ si_read_unlock(inode->sb); -+ mutex_unlock(&inode->i_mutex); -+out: -+ return err; -+} -+#endif -+ -+static int aufs_fasync(int fd, struct file *file, int flag) -+{ -+ int err; -+ struct file *h_file; -+ struct super_block *sb; -+ -+ sb = file->f_dentry->d_sb; -+ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); -+ -+ h_file = au_read_pre(file, /*keep_fi*/0); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ if (h_file->f_op->fasync) -+ err = h_file->f_op->fasync(fd, h_file, flag); -+ fput(h_file); /* instead of au_read_post() */ -+ -+out: -+ si_read_unlock(sb); -+ return err; -+} -+ -+static int aufs_setfl(struct file *file, unsigned long arg) -+{ -+ int err; -+ struct file *h_file; -+ struct super_block *sb; -+ -+ sb = file->f_dentry->d_sb; -+ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); -+ -+ h_file = au_read_pre(file, /*keep_fi*/0); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ arg |= vfsub_file_flags(file) & FASYNC; /* stop calling h_file->fasync */ -+ err = setfl(/*unused fd*/-1, h_file, arg); -+ fput(h_file); /* instead of au_read_post() */ -+ -+out: -+ si_read_unlock(sb); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* no one supports this operation, currently */ -+#if 0 -+static ssize_t aufs_sendpage(struct file *file, struct page *page, int offset, -+ size_t len, loff_t *pos, int more) -+{ -+} -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+const struct file_operations aufs_file_fop = { -+ .owner = THIS_MODULE, -+ -+ .llseek = default_llseek, -+ -+ .read = aufs_read, -+ .write = aufs_write, -+ .read_iter = aufs_read_iter, -+ .write_iter = aufs_write_iter, -+ -+#ifdef CONFIG_AUFS_POLL -+ .poll = aufs_poll, -+#endif -+ .unlocked_ioctl = aufs_ioctl_nondir, -+#ifdef CONFIG_COMPAT -+ .compat_ioctl = aufs_compat_ioctl_nondir, -+#endif -+ .mmap = aufs_mmap, -+ .open = aufs_open_nondir, -+ .flush = aufs_flush_nondir, -+ .release = aufs_release_nondir, -+ .fsync = aufs_fsync_nondir, -+ /* .aio_fsync = aufs_aio_fsync_nondir, */ -+ .fasync = aufs_fasync, -+ /* .sendpage = aufs_sendpage, */ -+ .setfl = aufs_setfl, -+ .splice_write = aufs_splice_write, -+ .splice_read = aufs_splice_read, -+#if 0 -+ .aio_splice_write = aufs_aio_splice_write, -+ .aio_splice_read = aufs_aio_splice_read, -+#endif -+ .fallocate = aufs_fallocate -+}; -diff --git a/fs/aufs/fhsm.c b/fs/aufs/fhsm.c -new file mode 100644 -index 0000000..5b3ad74 ---- /dev/null -+++ b/fs/aufs/fhsm.c -@@ -0,0 +1,426 @@ -+/* -+ * Copyright (C) 2011-2016 Junjiro R. Okajima -+ * -+ * This program, aufs is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -+ */ -+ -+/* -+ * File-based Hierarchy Storage Management -+ */ -+ -+#include -+#include -+#include -+#include -+#include "aufs.h" -+ -+static aufs_bindex_t au_fhsm_bottom(struct super_block *sb) -+{ -+ struct au_sbinfo *sbinfo; -+ struct au_fhsm *fhsm; -+ -+ SiMustAnyLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ fhsm = &sbinfo->si_fhsm; -+ AuDebugOn(!fhsm); -+ return fhsm->fhsm_bottom; -+} -+ -+void au_fhsm_set_bottom(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ struct au_sbinfo *sbinfo; -+ struct au_fhsm *fhsm; -+ -+ SiMustWriteLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ fhsm = &sbinfo->si_fhsm; -+ AuDebugOn(!fhsm); -+ fhsm->fhsm_bottom = bindex; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_fhsm_test_jiffy(struct au_sbinfo *sbinfo, struct au_branch *br) -+{ -+ struct au_br_fhsm *bf; -+ -+ bf = br->br_fhsm; -+ MtxMustLock(&bf->bf_lock); -+ -+ return !bf->bf_readable -+ || time_after(jiffies, -+ bf->bf_jiffy + sbinfo->si_fhsm.fhsm_expire); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void au_fhsm_notify(struct super_block *sb, int val) -+{ -+ struct au_sbinfo *sbinfo; -+ struct au_fhsm *fhsm; -+ -+ SiMustAnyLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ fhsm = &sbinfo->si_fhsm; -+ if (au_fhsm_pid(fhsm) -+ && atomic_read(&fhsm->fhsm_readable) != -1) { -+ atomic_set(&fhsm->fhsm_readable, val); -+ if (val) -+ wake_up(&fhsm->fhsm_wqh); -+ } -+} -+ -+static int au_fhsm_stfs(struct super_block *sb, aufs_bindex_t bindex, -+ struct aufs_stfs *rstfs, int do_lock, int do_notify) -+{ -+ int err; -+ struct au_branch *br; -+ struct au_br_fhsm *bf; -+ -+ br = au_sbr(sb, bindex); -+ AuDebugOn(au_br_rdonly(br)); -+ bf = br->br_fhsm; -+ AuDebugOn(!bf); -+ -+ if (do_lock) -+ mutex_lock(&bf->bf_lock); -+ else -+ MtxMustLock(&bf->bf_lock); -+ -+ /* sb->s_root for NFS is unreliable */ -+ err = au_br_stfs(br, &bf->bf_stfs); -+ if (unlikely(err)) { -+ AuErr1("FHSM failed (%d), b%d, ignored.\n", bindex, err); -+ goto out; -+ } -+ -+ bf->bf_jiffy = jiffies; -+ bf->bf_readable = 1; -+ if (do_notify) -+ au_fhsm_notify(sb, /*val*/1); -+ if (rstfs) -+ *rstfs = bf->bf_stfs; -+ -+out: -+ if (do_lock) -+ mutex_unlock(&bf->bf_lock); -+ au_fhsm_notify(sb, /*val*/1); -+ -+ return err; -+} -+ -+void au_fhsm_wrote(struct super_block *sb, aufs_bindex_t bindex, int force) -+{ -+ int err; -+ struct au_sbinfo *sbinfo; -+ struct au_fhsm *fhsm; -+ struct au_branch *br; -+ struct au_br_fhsm *bf; -+ -+ AuDbg("b%d, force %d\n", bindex, force); -+ SiMustAnyLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ fhsm = &sbinfo->si_fhsm; -+ if (!au_ftest_si(sbinfo, FHSM) -+ || fhsm->fhsm_bottom == bindex) -+ return; -+ -+ br = au_sbr(sb, bindex); -+ bf = br->br_fhsm; -+ AuDebugOn(!bf); -+ mutex_lock(&bf->bf_lock); -+ if (force -+ || au_fhsm_pid(fhsm) -+ || au_fhsm_test_jiffy(sbinfo, br)) -+ err = au_fhsm_stfs(sb, bindex, /*rstfs*/NULL, /*do_lock*/0, -+ /*do_notify*/1); -+ mutex_unlock(&bf->bf_lock); -+} -+ -+void au_fhsm_wrote_all(struct super_block *sb, int force) -+{ -+ aufs_bindex_t bindex, bend; -+ struct au_branch *br; -+ -+ /* exclude the bottom */ -+ bend = au_fhsm_bottom(sb); -+ for (bindex = 0; bindex < bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ if (au_br_fhsm(br->br_perm)) -+ au_fhsm_wrote(sb, bindex, force); -+ } -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static unsigned int au_fhsm_poll(struct file *file, -+ struct poll_table_struct *wait) -+{ -+ unsigned int mask; -+ struct au_sbinfo *sbinfo; -+ struct au_fhsm *fhsm; -+ -+ mask = 0; -+ sbinfo = file->private_data; -+ fhsm = &sbinfo->si_fhsm; -+ poll_wait(file, &fhsm->fhsm_wqh, wait); -+ if (atomic_read(&fhsm->fhsm_readable)) -+ mask = POLLIN /* | POLLRDNORM */; -+ -+ AuTraceErr((int)mask); -+ return mask; -+} -+ -+static int au_fhsm_do_read_one(struct aufs_stbr __user *stbr, -+ struct aufs_stfs *stfs, __s16 brid) -+{ -+ int err; -+ -+ err = copy_to_user(&stbr->stfs, stfs, sizeof(*stfs)); -+ if (!err) -+ err = __put_user(brid, &stbr->brid); -+ if (unlikely(err)) -+ err = -EFAULT; -+ -+ return err; -+} -+ -+static ssize_t au_fhsm_do_read(struct super_block *sb, -+ struct aufs_stbr __user *stbr, size_t count) -+{ -+ ssize_t err; -+ int nstbr; -+ aufs_bindex_t bindex, bend; -+ struct au_branch *br; -+ struct au_br_fhsm *bf; -+ -+ /* except the bottom branch */ -+ err = 0; -+ nstbr = 0; -+ bend = au_fhsm_bottom(sb); -+ for (bindex = 0; !err && bindex < bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ if (!au_br_fhsm(br->br_perm)) -+ continue; -+ -+ bf = br->br_fhsm; -+ mutex_lock(&bf->bf_lock); -+ if (bf->bf_readable) { -+ err = -EFAULT; -+ if (count >= sizeof(*stbr)) -+ err = au_fhsm_do_read_one(stbr++, &bf->bf_stfs, -+ br->br_id); -+ if (!err) { -+ bf->bf_readable = 0; -+ count -= sizeof(*stbr); -+ nstbr++; -+ } -+ } -+ mutex_unlock(&bf->bf_lock); -+ } -+ if (!err) -+ err = sizeof(*stbr) * nstbr; -+ -+ return err; -+} -+ -+static ssize_t au_fhsm_read(struct file *file, char __user *buf, size_t count, -+ loff_t *pos) -+{ -+ ssize_t err; -+ int readable; -+ aufs_bindex_t nfhsm, bindex, bend; -+ struct au_sbinfo *sbinfo; -+ struct au_fhsm *fhsm; -+ struct au_branch *br; -+ struct super_block *sb; -+ -+ err = 0; -+ sbinfo = file->private_data; -+ fhsm = &sbinfo->si_fhsm; -+need_data: -+ spin_lock_irq(&fhsm->fhsm_wqh.lock); -+ if (!atomic_read(&fhsm->fhsm_readable)) { -+ if (vfsub_file_flags(file) & O_NONBLOCK) -+ err = -EAGAIN; -+ else -+ err = wait_event_interruptible_locked_irq -+ (fhsm->fhsm_wqh, -+ atomic_read(&fhsm->fhsm_readable)); -+ } -+ spin_unlock_irq(&fhsm->fhsm_wqh.lock); -+ if (unlikely(err)) -+ goto out; -+ -+ /* sb may already be dead */ -+ au_rw_read_lock(&sbinfo->si_rwsem); -+ readable = atomic_read(&fhsm->fhsm_readable); -+ if (readable > 0) { -+ sb = sbinfo->si_sb; -+ AuDebugOn(!sb); -+ /* exclude the bottom branch */ -+ nfhsm = 0; -+ bend = au_fhsm_bottom(sb); -+ for (bindex = 0; bindex < bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ if (au_br_fhsm(br->br_perm)) -+ nfhsm++; -+ } -+ err = -EMSGSIZE; -+ if (nfhsm * sizeof(struct aufs_stbr) <= count) { -+ atomic_set(&fhsm->fhsm_readable, 0); -+ err = au_fhsm_do_read(sbinfo->si_sb, (void __user *)buf, -+ count); -+ } -+ } -+ au_rw_read_unlock(&sbinfo->si_rwsem); -+ if (!readable) -+ goto need_data; -+ -+out: -+ return err; -+} -+ -+static int au_fhsm_release(struct inode *inode, struct file *file) -+{ -+ struct au_sbinfo *sbinfo; -+ struct au_fhsm *fhsm; -+ -+ /* sb may already be dead */ -+ sbinfo = file->private_data; -+ fhsm = &sbinfo->si_fhsm; -+ spin_lock(&fhsm->fhsm_spin); -+ fhsm->fhsm_pid = 0; -+ spin_unlock(&fhsm->fhsm_spin); -+ kobject_put(&sbinfo->si_kobj); -+ -+ return 0; -+} -+ -+static const struct file_operations au_fhsm_fops = { -+ .owner = THIS_MODULE, -+ .llseek = noop_llseek, -+ .read = au_fhsm_read, -+ .poll = au_fhsm_poll, -+ .release = au_fhsm_release -+}; -+ -+int au_fhsm_fd(struct super_block *sb, int oflags) -+{ -+ int err, fd; -+ struct au_sbinfo *sbinfo; -+ struct au_fhsm *fhsm; -+ -+ err = -EPERM; -+ if (unlikely(!capable(CAP_SYS_ADMIN))) -+ goto out; -+ -+ err = -EINVAL; -+ if (unlikely(oflags & ~(O_CLOEXEC | O_NONBLOCK))) -+ goto out; -+ -+ err = 0; -+ sbinfo = au_sbi(sb); -+ fhsm = &sbinfo->si_fhsm; -+ spin_lock(&fhsm->fhsm_spin); -+ if (!fhsm->fhsm_pid) -+ fhsm->fhsm_pid = current->pid; -+ else -+ err = -EBUSY; -+ spin_unlock(&fhsm->fhsm_spin); -+ if (unlikely(err)) -+ goto out; -+ -+ oflags |= O_RDONLY; -+ /* oflags |= FMODE_NONOTIFY; */ -+ fd = anon_inode_getfd("[aufs_fhsm]", &au_fhsm_fops, sbinfo, oflags); -+ err = fd; -+ if (unlikely(fd < 0)) -+ goto out_pid; -+ -+ /* succeed reglardless 'fhsm' status */ -+ kobject_get(&sbinfo->si_kobj); -+ si_noflush_read_lock(sb); -+ if (au_ftest_si(sbinfo, FHSM)) -+ au_fhsm_wrote_all(sb, /*force*/0); -+ si_read_unlock(sb); -+ goto out; /* success */ -+ -+out_pid: -+ spin_lock(&fhsm->fhsm_spin); -+ fhsm->fhsm_pid = 0; -+ spin_unlock(&fhsm->fhsm_spin); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int au_fhsm_br_alloc(struct au_branch *br) -+{ -+ int err; -+ -+ err = 0; -+ br->br_fhsm = kmalloc(sizeof(*br->br_fhsm), GFP_NOFS); -+ if (br->br_fhsm) -+ au_br_fhsm_init(br->br_fhsm); -+ else -+ err = -ENOMEM; -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void au_fhsm_fin(struct super_block *sb) -+{ -+ au_fhsm_notify(sb, /*val*/-1); -+} -+ -+void au_fhsm_init(struct au_sbinfo *sbinfo) -+{ -+ struct au_fhsm *fhsm; -+ -+ fhsm = &sbinfo->si_fhsm; -+ spin_lock_init(&fhsm->fhsm_spin); -+ init_waitqueue_head(&fhsm->fhsm_wqh); -+ atomic_set(&fhsm->fhsm_readable, 0); -+ fhsm->fhsm_expire -+ = msecs_to_jiffies(AUFS_FHSM_CACHE_DEF_SEC * MSEC_PER_SEC); -+ fhsm->fhsm_bottom = -1; -+} -+ -+void au_fhsm_set(struct au_sbinfo *sbinfo, unsigned int sec) -+{ -+ sbinfo->si_fhsm.fhsm_expire -+ = msecs_to_jiffies(sec * MSEC_PER_SEC); -+} -+ -+void au_fhsm_show(struct seq_file *seq, struct au_sbinfo *sbinfo) -+{ -+ unsigned int u; -+ -+ if (!au_ftest_si(sbinfo, FHSM)) -+ return; -+ -+ u = jiffies_to_msecs(sbinfo->si_fhsm.fhsm_expire) / MSEC_PER_SEC; -+ if (u != AUFS_FHSM_CACHE_DEF_SEC) -+ seq_printf(seq, ",fhsm_sec=%u", u); -+} -diff --git a/fs/aufs/file.c b/fs/aufs/file.c -new file mode 100644 -index 0000000..12c7620 ---- /dev/null -+++ b/fs/aufs/file.c -@@ -0,0 +1,857 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * handling file/dir, and address_space operation -+ */ -+ -+#ifdef CONFIG_AUFS_DEBUG -+#include -+#endif -+#include -+#include "aufs.h" -+ -+/* drop flags for writing */ -+unsigned int au_file_roflags(unsigned int flags) -+{ -+ flags &= ~(O_WRONLY | O_RDWR | O_APPEND | O_CREAT | O_TRUNC); -+ flags |= O_RDONLY | O_NOATIME; -+ return flags; -+} -+ -+/* common functions to regular file and dir */ -+struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags, -+ struct file *file, int force_wr) -+{ -+ struct file *h_file; -+ struct dentry *h_dentry; -+ struct inode *h_inode; -+ struct super_block *sb; -+ struct au_branch *br; -+ struct path h_path; -+ int err; -+ -+ /* a race condition can happen between open and unlink/rmdir */ -+ h_file = ERR_PTR(-ENOENT); -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (au_test_nfsd() && !h_dentry) -+ goto out; -+ h_inode = h_dentry->d_inode; -+ if (au_test_nfsd() && !h_inode) -+ goto out; -+ spin_lock(&h_dentry->d_lock); -+ err = (!d_unhashed(dentry) && d_unlinked(h_dentry)) -+ || !h_inode -+ /* || !dentry->d_inode->i_nlink */ -+ ; -+ spin_unlock(&h_dentry->d_lock); -+ if (unlikely(err)) -+ goto out; -+ -+ sb = dentry->d_sb; -+ br = au_sbr(sb, bindex); -+ err = au_br_test_oflag(flags, br); -+ h_file = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out; -+ -+ /* drop flags for writing */ -+ if (au_test_ro(sb, bindex, dentry->d_inode)) { -+ if (force_wr && !(flags & O_WRONLY)) -+ force_wr = 0; -+ flags = au_file_roflags(flags); -+ if (force_wr) { -+ h_file = ERR_PTR(-EROFS); -+ flags = au_file_roflags(flags); -+ if (unlikely(vfsub_native_ro(h_inode) -+ || IS_APPEND(h_inode))) -+ goto out; -+ flags &= ~O_ACCMODE; -+ flags |= O_WRONLY; -+ } -+ } -+ flags &= ~O_CREAT; -+ atomic_inc(&br->br_count); -+ h_path.dentry = h_dentry; -+ h_path.mnt = au_br_mnt(br); -+ h_file = vfsub_dentry_open(&h_path, flags); -+ if (IS_ERR(h_file)) -+ goto out_br; -+ -+ if (flags & __FMODE_EXEC) { -+ err = deny_write_access(h_file); -+ if (unlikely(err)) { -+ fput(h_file); -+ h_file = ERR_PTR(err); -+ goto out_br; -+ } -+ } -+ fsnotify_open(h_file); -+ goto out; /* success */ -+ -+out_br: -+ atomic_dec(&br->br_count); -+out: -+ return h_file; -+} -+ -+static int au_cmoo(struct dentry *dentry) -+{ -+ int err, cmoo; -+ unsigned int udba; -+ struct path h_path; -+ struct au_pin pin; -+ struct au_cp_generic cpg = { -+ .dentry = dentry, -+ .bdst = -1, -+ .bsrc = -1, -+ .len = -1, -+ .pin = &pin, -+ .flags = AuCpup_DTIME | AuCpup_HOPEN -+ }; -+ struct inode *inode, *delegated; -+ struct super_block *sb; -+ struct au_sbinfo *sbinfo; -+ struct au_fhsm *fhsm; -+ pid_t pid; -+ struct au_branch *br; -+ struct dentry *parent; -+ struct au_hinode *hdir; -+ -+ DiMustWriteLock(dentry); -+ inode = dentry->d_inode; -+ IiMustWriteLock(inode); -+ -+ err = 0; -+ if (IS_ROOT(dentry)) -+ goto out; -+ cpg.bsrc = au_dbstart(dentry); -+ if (!cpg.bsrc) -+ goto out; -+ -+ sb = dentry->d_sb; -+ sbinfo = au_sbi(sb); -+ fhsm = &sbinfo->si_fhsm; -+ pid = au_fhsm_pid(fhsm); -+ if (pid -+ && (current->pid == pid -+ || current->real_parent->pid == pid)) -+ goto out; -+ -+ br = au_sbr(sb, cpg.bsrc); -+ cmoo = au_br_cmoo(br->br_perm); -+ if (!cmoo) -+ goto out; -+ if (!S_ISREG(inode->i_mode)) -+ cmoo &= AuBrAttr_COO_ALL; -+ if (!cmoo) -+ goto out; -+ -+ parent = dget_parent(dentry); -+ di_write_lock_parent(parent); -+ err = au_wbr_do_copyup_bu(dentry, cpg.bsrc - 1); -+ cpg.bdst = err; -+ if (unlikely(err < 0)) { -+ err = 0; /* there is no upper writable branch */ -+ goto out_dgrade; -+ } -+ AuDbg("bsrc %d, bdst %d\n", cpg.bsrc, cpg.bdst); -+ -+ /* do not respect the coo attrib for the target branch */ -+ err = au_cpup_dirs(dentry, cpg.bdst); -+ if (unlikely(err)) -+ goto out_dgrade; -+ -+ di_downgrade_lock(parent, AuLock_IR); -+ udba = au_opt_udba(sb); -+ err = au_pin(&pin, dentry, cpg.bdst, udba, -+ AuPin_DI_LOCKED | AuPin_MNT_WRITE); -+ if (unlikely(err)) -+ goto out_parent; -+ -+ err = au_sio_cpup_simple(&cpg); -+ au_unpin(&pin); -+ if (unlikely(err)) -+ goto out_parent; -+ if (!(cmoo & AuBrWAttr_MOO)) -+ goto out_parent; /* success */ -+ -+ err = au_pin(&pin, dentry, cpg.bsrc, udba, -+ AuPin_DI_LOCKED | AuPin_MNT_WRITE); -+ if (unlikely(err)) -+ goto out_parent; -+ -+ h_path.mnt = au_br_mnt(br); -+ h_path.dentry = au_h_dptr(dentry, cpg.bsrc); -+ hdir = au_hi(parent->d_inode, cpg.bsrc); -+ delegated = NULL; -+ err = vfsub_unlink(hdir->hi_inode, &h_path, &delegated, /*force*/1); -+ au_unpin(&pin); -+ /* todo: keep h_dentry or not? */ -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal unlink\n"); -+ iput(delegated); -+ } -+ if (unlikely(err)) { -+ pr_err("unlink %pd after coo failed (%d), ignored\n", -+ dentry, err); -+ err = 0; -+ } -+ goto out_parent; /* success */ -+ -+out_dgrade: -+ di_downgrade_lock(parent, AuLock_IR); -+out_parent: -+ di_read_unlock(parent, AuLock_IR); -+ dput(parent); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+int au_do_open(struct file *file, struct au_do_open_args *args) -+{ -+ int err, no_lock = args->no_lock; -+ struct dentry *dentry; -+ struct au_finfo *finfo; -+ -+ if (!no_lock) -+ err = au_finfo_init(file, args->fidir); -+ else { -+ lockdep_off(); -+ err = au_finfo_init(file, args->fidir); -+ lockdep_on(); -+ } -+ if (unlikely(err)) -+ goto out; -+ -+ dentry = file->f_dentry; -+ AuDebugOn(IS_ERR_OR_NULL(dentry)); -+ if (!no_lock) { -+ di_write_lock_child(dentry); -+ err = au_cmoo(dentry); -+ di_downgrade_lock(dentry, AuLock_IR); -+ if (!err) -+ err = args->open(file, vfsub_file_flags(file), NULL); -+ di_read_unlock(dentry, AuLock_IR); -+ } else { -+ err = au_cmoo(dentry); -+ if (!err) -+ err = args->open(file, vfsub_file_flags(file), -+ args->h_file); -+ if (!err && au_fbstart(file) != au_dbstart(dentry)) -+ /* -+ * cmoo happens after h_file was opened. -+ * need to refresh file later. -+ */ -+ atomic_dec(&au_fi(file)->fi_generation); -+ } -+ -+ finfo = au_fi(file); -+ if (!err) { -+ finfo->fi_file = file; -+ au_sphl_add(&finfo->fi_hlist, -+ &au_sbi(file->f_dentry->d_sb)->si_files); -+ } -+ if (!no_lock) -+ fi_write_unlock(file); -+ else { -+ lockdep_off(); -+ fi_write_unlock(file); -+ lockdep_on(); -+ } -+ if (unlikely(err)) { -+ finfo->fi_hdir = NULL; -+ au_finfo_fin(file); -+ } -+ -+out: -+ return err; -+} -+ -+int au_reopen_nondir(struct file *file) -+{ -+ int err; -+ aufs_bindex_t bstart; -+ struct dentry *dentry; -+ struct file *h_file, *h_file_tmp; -+ -+ dentry = file->f_dentry; -+ bstart = au_dbstart(dentry); -+ h_file_tmp = NULL; -+ if (au_fbstart(file) == bstart) { -+ h_file = au_hf_top(file); -+ if (file->f_mode == h_file->f_mode) -+ return 0; /* success */ -+ h_file_tmp = h_file; -+ get_file(h_file_tmp); -+ au_set_h_fptr(file, bstart, NULL); -+ } -+ AuDebugOn(au_fi(file)->fi_hdir); -+ /* -+ * it can happen -+ * file exists on both of rw and ro -+ * open --> dbstart and fbstart are both 0 -+ * prepend a branch as rw, "rw" become ro -+ * remove rw/file -+ * delete the top branch, "rw" becomes rw again -+ * --> dbstart is 1, fbstart is still 0 -+ * write --> fbstart is 0 but dbstart is 1 -+ */ -+ /* AuDebugOn(au_fbstart(file) < bstart); */ -+ -+ h_file = au_h_open(dentry, bstart, vfsub_file_flags(file) & ~O_TRUNC, -+ file, /*force_wr*/0); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) { -+ if (h_file_tmp) { -+ atomic_inc(&au_sbr(dentry->d_sb, bstart)->br_count); -+ au_set_h_fptr(file, bstart, h_file_tmp); -+ h_file_tmp = NULL; -+ } -+ goto out; /* todo: close all? */ -+ } -+ -+ err = 0; -+ au_set_fbstart(file, bstart); -+ au_set_h_fptr(file, bstart, h_file); -+ au_update_figen(file); -+ /* todo: necessary? */ -+ /* file->f_ra = h_file->f_ra; */ -+ -+out: -+ if (h_file_tmp) -+ fput(h_file_tmp); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_reopen_wh(struct file *file, aufs_bindex_t btgt, -+ struct dentry *hi_wh) -+{ -+ int err; -+ aufs_bindex_t bstart; -+ struct au_dinfo *dinfo; -+ struct dentry *h_dentry; -+ struct au_hdentry *hdp; -+ -+ dinfo = au_di(file->f_dentry); -+ AuRwMustWriteLock(&dinfo->di_rwsem); -+ -+ bstart = dinfo->di_bstart; -+ dinfo->di_bstart = btgt; -+ hdp = dinfo->di_hdentry; -+ h_dentry = hdp[0 + btgt].hd_dentry; -+ hdp[0 + btgt].hd_dentry = hi_wh; -+ err = au_reopen_nondir(file); -+ hdp[0 + btgt].hd_dentry = h_dentry; -+ dinfo->di_bstart = bstart; -+ -+ return err; -+} -+ -+static int au_ready_to_write_wh(struct file *file, loff_t len, -+ aufs_bindex_t bcpup, struct au_pin *pin) -+{ -+ int err; -+ struct inode *inode, *h_inode; -+ struct dentry *h_dentry, *hi_wh; -+ struct au_cp_generic cpg = { -+ .dentry = file->f_dentry, -+ .bdst = bcpup, -+ .bsrc = -1, -+ .len = len, -+ .pin = pin -+ }; -+ -+ au_update_dbstart(cpg.dentry); -+ inode = cpg.dentry->d_inode; -+ h_inode = NULL; -+ if (au_dbstart(cpg.dentry) <= bcpup -+ && au_dbend(cpg.dentry) >= bcpup) { -+ h_dentry = au_h_dptr(cpg.dentry, bcpup); -+ if (h_dentry) -+ h_inode = h_dentry->d_inode; -+ } -+ hi_wh = au_hi_wh(inode, bcpup); -+ if (!hi_wh && !h_inode) -+ err = au_sio_cpup_wh(&cpg, file); -+ else -+ /* already copied-up after unlink */ -+ err = au_reopen_wh(file, bcpup, hi_wh); -+ -+ if (!err -+ && (inode->i_nlink > 1 -+ || (inode->i_state & I_LINKABLE)) -+ && au_opt_test(au_mntflags(cpg.dentry->d_sb), PLINK)) -+ au_plink_append(inode, bcpup, au_h_dptr(cpg.dentry, bcpup)); -+ -+ return err; -+} -+ -+/* -+ * prepare the @file for writing. -+ */ -+int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin) -+{ -+ int err; -+ aufs_bindex_t dbstart; -+ struct dentry *parent; -+ struct inode *inode; -+ struct super_block *sb; -+ struct file *h_file; -+ struct au_cp_generic cpg = { -+ .dentry = file->f_dentry, -+ .bdst = -1, -+ .bsrc = -1, -+ .len = len, -+ .pin = pin, -+ .flags = AuCpup_DTIME -+ }; -+ -+ sb = cpg.dentry->d_sb; -+ inode = cpg.dentry->d_inode; -+ cpg.bsrc = au_fbstart(file); -+ err = au_test_ro(sb, cpg.bsrc, inode); -+ if (!err && (au_hf_top(file)->f_mode & FMODE_WRITE)) { -+ err = au_pin(pin, cpg.dentry, cpg.bsrc, AuOpt_UDBA_NONE, -+ /*flags*/0); -+ goto out; -+ } -+ -+ /* need to cpup or reopen */ -+ parent = dget_parent(cpg.dentry); -+ di_write_lock_parent(parent); -+ err = AuWbrCopyup(au_sbi(sb), cpg.dentry); -+ cpg.bdst = err; -+ if (unlikely(err < 0)) -+ goto out_dgrade; -+ err = 0; -+ -+ if (!d_unhashed(cpg.dentry) && !au_h_dptr(parent, cpg.bdst)) { -+ err = au_cpup_dirs(cpg.dentry, cpg.bdst); -+ if (unlikely(err)) -+ goto out_dgrade; -+ } -+ -+ err = au_pin(pin, cpg.dentry, cpg.bdst, AuOpt_UDBA_NONE, -+ AuPin_DI_LOCKED | AuPin_MNT_WRITE); -+ if (unlikely(err)) -+ goto out_dgrade; -+ -+ dbstart = au_dbstart(cpg.dentry); -+ if (dbstart <= cpg.bdst) -+ cpg.bsrc = cpg.bdst; -+ -+ if (dbstart <= cpg.bdst /* just reopen */ -+ || !d_unhashed(cpg.dentry) /* copyup and reopen */ -+ ) { -+ h_file = au_h_open_pre(cpg.dentry, cpg.bsrc, /*force_wr*/0); -+ if (IS_ERR(h_file)) -+ err = PTR_ERR(h_file); -+ else { -+ di_downgrade_lock(parent, AuLock_IR); -+ if (dbstart > cpg.bdst) -+ err = au_sio_cpup_simple(&cpg); -+ if (!err) -+ err = au_reopen_nondir(file); -+ au_h_open_post(cpg.dentry, cpg.bsrc, h_file); -+ } -+ } else { /* copyup as wh and reopen */ -+ /* -+ * since writable hfsplus branch is not supported, -+ * h_open_pre/post() are unnecessary. -+ */ -+ err = au_ready_to_write_wh(file, len, cpg.bdst, pin); -+ di_downgrade_lock(parent, AuLock_IR); -+ } -+ -+ if (!err) { -+ au_pin_set_parent_lflag(pin, /*lflag*/0); -+ goto out_dput; /* success */ -+ } -+ au_unpin(pin); -+ goto out_unlock; -+ -+out_dgrade: -+ di_downgrade_lock(parent, AuLock_IR); -+out_unlock: -+ di_read_unlock(parent, AuLock_IR); -+out_dput: -+ dput(parent); -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int au_do_flush(struct file *file, fl_owner_t id, -+ int (*flush)(struct file *file, fl_owner_t id)) -+{ -+ int err; -+ struct super_block *sb; -+ struct inode *inode; -+ -+ inode = file_inode(file); -+ sb = inode->i_sb; -+ si_noflush_read_lock(sb); -+ fi_read_lock(file); -+ ii_read_lock_child(inode); -+ -+ err = flush(file, id); -+ au_cpup_attr_timesizes(inode); -+ -+ ii_read_unlock(inode); -+ fi_read_unlock(file); -+ si_read_unlock(sb); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_file_refresh_by_inode(struct file *file, int *need_reopen) -+{ -+ int err; -+ struct au_pin pin; -+ struct au_finfo *finfo; -+ struct dentry *parent, *hi_wh; -+ struct inode *inode; -+ struct super_block *sb; -+ struct au_cp_generic cpg = { -+ .dentry = file->f_dentry, -+ .bdst = -1, -+ .bsrc = -1, -+ .len = -1, -+ .pin = &pin, -+ .flags = AuCpup_DTIME -+ }; -+ -+ FiMustWriteLock(file); -+ -+ err = 0; -+ finfo = au_fi(file); -+ sb = cpg.dentry->d_sb; -+ inode = cpg.dentry->d_inode; -+ cpg.bdst = au_ibstart(inode); -+ if (cpg.bdst == finfo->fi_btop || IS_ROOT(cpg.dentry)) -+ goto out; -+ -+ parent = dget_parent(cpg.dentry); -+ if (au_test_ro(sb, cpg.bdst, inode)) { -+ di_read_lock_parent(parent, !AuLock_IR); -+ err = AuWbrCopyup(au_sbi(sb), cpg.dentry); -+ cpg.bdst = err; -+ di_read_unlock(parent, !AuLock_IR); -+ if (unlikely(err < 0)) -+ goto out_parent; -+ err = 0; -+ } -+ -+ di_read_lock_parent(parent, AuLock_IR); -+ hi_wh = au_hi_wh(inode, cpg.bdst); -+ if (!S_ISDIR(inode->i_mode) -+ && au_opt_test(au_mntflags(sb), PLINK) -+ && au_plink_test(inode) -+ && !d_unhashed(cpg.dentry) -+ && cpg.bdst < au_dbstart(cpg.dentry)) { -+ err = au_test_and_cpup_dirs(cpg.dentry, cpg.bdst); -+ if (unlikely(err)) -+ goto out_unlock; -+ -+ /* always superio. */ -+ err = au_pin(&pin, cpg.dentry, cpg.bdst, AuOpt_UDBA_NONE, -+ AuPin_DI_LOCKED | AuPin_MNT_WRITE); -+ if (!err) { -+ err = au_sio_cpup_simple(&cpg); -+ au_unpin(&pin); -+ } -+ } else if (hi_wh) { -+ /* already copied-up after unlink */ -+ err = au_reopen_wh(file, cpg.bdst, hi_wh); -+ *need_reopen = 0; -+ } -+ -+out_unlock: -+ di_read_unlock(parent, AuLock_IR); -+out_parent: -+ dput(parent); -+out: -+ return err; -+} -+ -+static void au_do_refresh_dir(struct file *file) -+{ -+ aufs_bindex_t bindex, bend, new_bindex, brid; -+ struct au_hfile *p, tmp, *q; -+ struct au_finfo *finfo; -+ struct super_block *sb; -+ struct au_fidir *fidir; -+ -+ FiMustWriteLock(file); -+ -+ sb = file->f_dentry->d_sb; -+ finfo = au_fi(file); -+ fidir = finfo->fi_hdir; -+ AuDebugOn(!fidir); -+ p = fidir->fd_hfile + finfo->fi_btop; -+ brid = p->hf_br->br_id; -+ bend = fidir->fd_bbot; -+ for (bindex = finfo->fi_btop; bindex <= bend; bindex++, p++) { -+ if (!p->hf_file) -+ continue; -+ -+ new_bindex = au_br_index(sb, p->hf_br->br_id); -+ if (new_bindex == bindex) -+ continue; -+ if (new_bindex < 0) { -+ au_set_h_fptr(file, bindex, NULL); -+ continue; -+ } -+ -+ /* swap two lower inode, and loop again */ -+ q = fidir->fd_hfile + new_bindex; -+ tmp = *q; -+ *q = *p; -+ *p = tmp; -+ if (tmp.hf_file) { -+ bindex--; -+ p--; -+ } -+ } -+ -+ p = fidir->fd_hfile; -+ if (!au_test_mmapped(file) && !d_unlinked(file->f_dentry)) { -+ bend = au_sbend(sb); -+ for (finfo->fi_btop = 0; finfo->fi_btop <= bend; -+ finfo->fi_btop++, p++) -+ if (p->hf_file) { -+ if (file_inode(p->hf_file)) -+ break; -+ au_hfput(p, file); -+ } -+ } else { -+ bend = au_br_index(sb, brid); -+ for (finfo->fi_btop = 0; finfo->fi_btop < bend; -+ finfo->fi_btop++, p++) -+ if (p->hf_file) -+ au_hfput(p, file); -+ bend = au_sbend(sb); -+ } -+ -+ p = fidir->fd_hfile + bend; -+ for (fidir->fd_bbot = bend; fidir->fd_bbot >= finfo->fi_btop; -+ fidir->fd_bbot--, p--) -+ if (p->hf_file) { -+ if (file_inode(p->hf_file)) -+ break; -+ au_hfput(p, file); -+ } -+ AuDebugOn(fidir->fd_bbot < finfo->fi_btop); -+} -+ -+/* -+ * after branch manipulating, refresh the file. -+ */ -+static int refresh_file(struct file *file, int (*reopen)(struct file *file)) -+{ -+ int err, need_reopen; -+ aufs_bindex_t bend, bindex; -+ struct dentry *dentry; -+ struct au_finfo *finfo; -+ struct au_hfile *hfile; -+ -+ dentry = file->f_dentry; -+ finfo = au_fi(file); -+ if (!finfo->fi_hdir) { -+ hfile = &finfo->fi_htop; -+ AuDebugOn(!hfile->hf_file); -+ bindex = au_br_index(dentry->d_sb, hfile->hf_br->br_id); -+ AuDebugOn(bindex < 0); -+ if (bindex != finfo->fi_btop) -+ au_set_fbstart(file, bindex); -+ } else { -+ err = au_fidir_realloc(finfo, au_sbend(dentry->d_sb) + 1); -+ if (unlikely(err)) -+ goto out; -+ au_do_refresh_dir(file); -+ } -+ -+ err = 0; -+ need_reopen = 1; -+ if (!au_test_mmapped(file)) -+ err = au_file_refresh_by_inode(file, &need_reopen); -+ if (!err && need_reopen && !d_unlinked(dentry)) -+ err = reopen(file); -+ if (!err) { -+ au_update_figen(file); -+ goto out; /* success */ -+ } -+ -+ /* error, close all lower files */ -+ if (finfo->fi_hdir) { -+ bend = au_fbend_dir(file); -+ for (bindex = au_fbstart(file); bindex <= bend; bindex++) -+ au_set_h_fptr(file, bindex, NULL); -+ } -+ -+out: -+ return err; -+} -+ -+/* common function to regular file and dir */ -+int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file), -+ int wlock) -+{ -+ int err; -+ unsigned int sigen, figen; -+ aufs_bindex_t bstart; -+ unsigned char pseudo_link; -+ struct dentry *dentry; -+ struct inode *inode; -+ -+ err = 0; -+ dentry = file->f_dentry; -+ inode = dentry->d_inode; -+ sigen = au_sigen(dentry->d_sb); -+ fi_write_lock(file); -+ figen = au_figen(file); -+ di_write_lock_child(dentry); -+ bstart = au_dbstart(dentry); -+ pseudo_link = (bstart != au_ibstart(inode)); -+ if (sigen == figen && !pseudo_link && au_fbstart(file) == bstart) { -+ if (!wlock) { -+ di_downgrade_lock(dentry, AuLock_IR); -+ fi_downgrade_lock(file); -+ } -+ goto out; /* success */ -+ } -+ -+ AuDbg("sigen %d, figen %d\n", sigen, figen); -+ if (au_digen_test(dentry, sigen)) { -+ err = au_reval_dpath(dentry, sigen); -+ AuDebugOn(!err && au_digen_test(dentry, sigen)); -+ } -+ -+ if (!err) -+ err = refresh_file(file, reopen); -+ if (!err) { -+ if (!wlock) { -+ di_downgrade_lock(dentry, AuLock_IR); -+ fi_downgrade_lock(file); -+ } -+ } else { -+ di_write_unlock(dentry); -+ fi_write_unlock(file); -+ } -+ -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* cf. aufs_nopage() */ -+/* for madvise(2) */ -+static int aufs_readpage(struct file *file __maybe_unused, struct page *page) -+{ -+ unlock_page(page); -+ return 0; -+} -+ -+/* it will never be called, but necessary to support O_DIRECT */ -+static ssize_t aufs_direct_IO(int rw, struct kiocb *iocb, -+ struct iov_iter *iter, loff_t offset) -+{ BUG(); return 0; } -+ -+/* -+ * it will never be called, but madvise and fadvise behaves differently -+ * when get_xip_mem is defined -+ */ -+static int aufs_get_xip_mem(struct address_space *mapping, pgoff_t pgoff, -+ int create, void **kmem, unsigned long *pfn) -+{ BUG(); return 0; } -+ -+/* they will never be called. */ -+#ifdef CONFIG_AUFS_DEBUG -+static int aufs_write_begin(struct file *file, struct address_space *mapping, -+ loff_t pos, unsigned len, unsigned flags, -+ struct page **pagep, void **fsdata) -+{ AuUnsupport(); return 0; } -+static int aufs_write_end(struct file *file, struct address_space *mapping, -+ loff_t pos, unsigned len, unsigned copied, -+ struct page *page, void *fsdata) -+{ AuUnsupport(); return 0; } -+static int aufs_writepage(struct page *page, struct writeback_control *wbc) -+{ AuUnsupport(); return 0; } -+ -+static int aufs_set_page_dirty(struct page *page) -+{ AuUnsupport(); return 0; } -+static void aufs_invalidatepage(struct page *page, unsigned int offset, -+ unsigned int length) -+{ AuUnsupport(); } -+static int aufs_releasepage(struct page *page, gfp_t gfp) -+{ AuUnsupport(); return 0; } -+#if 0 /* called by memory compaction regardless file */ -+static int aufs_migratepage(struct address_space *mapping, struct page *newpage, -+ struct page *page, enum migrate_mode mode) -+{ AuUnsupport(); return 0; } -+#endif -+static int aufs_launder_page(struct page *page) -+{ AuUnsupport(); return 0; } -+static int aufs_is_partially_uptodate(struct page *page, -+ unsigned long from, -+ unsigned long count) -+{ AuUnsupport(); return 0; } -+static void aufs_is_dirty_writeback(struct page *page, bool *dirty, -+ bool *writeback) -+{ AuUnsupport(); } -+static int aufs_error_remove_page(struct address_space *mapping, -+ struct page *page) -+{ AuUnsupport(); return 0; } -+static int aufs_swap_activate(struct swap_info_struct *sis, struct file *file, -+ sector_t *span) -+{ AuUnsupport(); return 0; } -+static void aufs_swap_deactivate(struct file *file) -+{ AuUnsupport(); } -+#endif /* CONFIG_AUFS_DEBUG */ -+ -+const struct address_space_operations aufs_aop = { -+ .readpage = aufs_readpage, -+ .direct_IO = aufs_direct_IO, -+ .get_xip_mem = aufs_get_xip_mem, -+#ifdef CONFIG_AUFS_DEBUG -+ .writepage = aufs_writepage, -+ /* no writepages, because of writepage */ -+ .set_page_dirty = aufs_set_page_dirty, -+ /* no readpages, because of readpage */ -+ .write_begin = aufs_write_begin, -+ .write_end = aufs_write_end, -+ /* no bmap, no block device */ -+ .invalidatepage = aufs_invalidatepage, -+ .releasepage = aufs_releasepage, -+ /* is fallback_migrate_page ok? */ -+ /* .migratepage = aufs_migratepage, */ -+ .launder_page = aufs_launder_page, -+ .is_partially_uptodate = aufs_is_partially_uptodate, -+ .is_dirty_writeback = aufs_is_dirty_writeback, -+ .error_remove_page = aufs_error_remove_page, -+ .swap_activate = aufs_swap_activate, -+ .swap_deactivate = aufs_swap_deactivate -+#endif /* CONFIG_AUFS_DEBUG */ -+}; -diff --git a/fs/aufs/file.h b/fs/aufs/file.h -new file mode 100644 -index 0000000..564be91 ---- /dev/null -+++ b/fs/aufs/file.h -@@ -0,0 +1,291 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * file operations -+ */ -+ -+#ifndef __AUFS_FILE_H__ -+#define __AUFS_FILE_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+#include -+#include -+#include "rwsem.h" -+ -+struct au_branch; -+struct au_hfile { -+ struct file *hf_file; -+ struct au_branch *hf_br; -+}; -+ -+struct au_vdir; -+struct au_fidir { -+ aufs_bindex_t fd_bbot; -+ aufs_bindex_t fd_nent; -+ struct au_vdir *fd_vdir_cache; -+ struct au_hfile fd_hfile[]; -+}; -+ -+static inline int au_fidir_sz(int nent) -+{ -+ AuDebugOn(nent < 0); -+ return sizeof(struct au_fidir) + sizeof(struct au_hfile) * nent; -+} -+ -+struct au_finfo { -+ atomic_t fi_generation; -+ -+ struct au_rwsem fi_rwsem; -+ aufs_bindex_t fi_btop; -+ -+ /* do not union them */ -+ struct { /* for non-dir */ -+ struct au_hfile fi_htop; -+ atomic_t fi_mmapped; -+ }; -+ struct au_fidir *fi_hdir; /* for dir only */ -+ -+ struct hlist_node fi_hlist; -+ struct file *fi_file; /* very ugly */ -+} ____cacheline_aligned_in_smp; -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* file.c */ -+extern const struct address_space_operations aufs_aop; -+unsigned int au_file_roflags(unsigned int flags); -+struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags, -+ struct file *file, int force_wr); -+struct au_do_open_args { -+ int no_lock; -+ int (*open)(struct file *file, int flags, -+ struct file *h_file); -+ struct au_fidir *fidir; -+ struct file *h_file; -+}; -+int au_do_open(struct file *file, struct au_do_open_args *args); -+int au_reopen_nondir(struct file *file); -+struct au_pin; -+int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin); -+int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file), -+ int wlock); -+int au_do_flush(struct file *file, fl_owner_t id, -+ int (*flush)(struct file *file, fl_owner_t id)); -+ -+/* poll.c */ -+#ifdef CONFIG_AUFS_POLL -+unsigned int aufs_poll(struct file *file, poll_table *wait); -+#endif -+ -+#ifdef CONFIG_AUFS_BR_HFSPLUS -+/* hfsplus.c */ -+struct file *au_h_open_pre(struct dentry *dentry, aufs_bindex_t bindex, -+ int force_wr); -+void au_h_open_post(struct dentry *dentry, aufs_bindex_t bindex, -+ struct file *h_file); -+#else -+AuStub(struct file *, au_h_open_pre, return NULL, struct dentry *dentry, -+ aufs_bindex_t bindex, int force_wr) -+AuStubVoid(au_h_open_post, struct dentry *dentry, aufs_bindex_t bindex, -+ struct file *h_file); -+#endif -+ -+/* f_op.c */ -+extern const struct file_operations aufs_file_fop; -+int au_do_open_nondir(struct file *file, int flags, struct file *h_file); -+int aufs_release_nondir(struct inode *inode __maybe_unused, struct file *file); -+struct file *au_read_pre(struct file *file, int keep_fi); -+ -+/* finfo.c */ -+void au_hfput(struct au_hfile *hf, struct file *file); -+void au_set_h_fptr(struct file *file, aufs_bindex_t bindex, -+ struct file *h_file); -+ -+void au_update_figen(struct file *file); -+struct au_fidir *au_fidir_alloc(struct super_block *sb); -+int au_fidir_realloc(struct au_finfo *finfo, int nbr); -+ -+void au_fi_init_once(void *_fi); -+void au_finfo_fin(struct file *file); -+int au_finfo_init(struct file *file, struct au_fidir *fidir); -+ -+/* ioctl.c */ -+long aufs_ioctl_nondir(struct file *file, unsigned int cmd, unsigned long arg); -+#ifdef CONFIG_COMPAT -+long aufs_compat_ioctl_dir(struct file *file, unsigned int cmd, -+ unsigned long arg); -+long aufs_compat_ioctl_nondir(struct file *file, unsigned int cmd, -+ unsigned long arg); -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline struct au_finfo *au_fi(struct file *file) -+{ -+ return file->private_data; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * fi_read_lock, fi_write_lock, -+ * fi_read_unlock, fi_write_unlock, fi_downgrade_lock -+ */ -+AuSimpleRwsemFuncs(fi, struct file *f, &au_fi(f)->fi_rwsem); -+ -+#define FiMustNoWaiters(f) AuRwMustNoWaiters(&au_fi(f)->fi_rwsem) -+#define FiMustAnyLock(f) AuRwMustAnyLock(&au_fi(f)->fi_rwsem) -+#define FiMustWriteLock(f) AuRwMustWriteLock(&au_fi(f)->fi_rwsem) -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* todo: hard/soft set? */ -+static inline aufs_bindex_t au_fbstart(struct file *file) -+{ -+ FiMustAnyLock(file); -+ return au_fi(file)->fi_btop; -+} -+ -+static inline aufs_bindex_t au_fbend_dir(struct file *file) -+{ -+ FiMustAnyLock(file); -+ AuDebugOn(!au_fi(file)->fi_hdir); -+ return au_fi(file)->fi_hdir->fd_bbot; -+} -+ -+static inline struct au_vdir *au_fvdir_cache(struct file *file) -+{ -+ FiMustAnyLock(file); -+ AuDebugOn(!au_fi(file)->fi_hdir); -+ return au_fi(file)->fi_hdir->fd_vdir_cache; -+} -+ -+static inline void au_set_fbstart(struct file *file, aufs_bindex_t bindex) -+{ -+ FiMustWriteLock(file); -+ au_fi(file)->fi_btop = bindex; -+} -+ -+static inline void au_set_fbend_dir(struct file *file, aufs_bindex_t bindex) -+{ -+ FiMustWriteLock(file); -+ AuDebugOn(!au_fi(file)->fi_hdir); -+ au_fi(file)->fi_hdir->fd_bbot = bindex; -+} -+ -+static inline void au_set_fvdir_cache(struct file *file, -+ struct au_vdir *vdir_cache) -+{ -+ FiMustWriteLock(file); -+ AuDebugOn(!au_fi(file)->fi_hdir); -+ au_fi(file)->fi_hdir->fd_vdir_cache = vdir_cache; -+} -+ -+static inline struct file *au_hf_top(struct file *file) -+{ -+ FiMustAnyLock(file); -+ AuDebugOn(au_fi(file)->fi_hdir); -+ return au_fi(file)->fi_htop.hf_file; -+} -+ -+static inline struct file *au_hf_dir(struct file *file, aufs_bindex_t bindex) -+{ -+ FiMustAnyLock(file); -+ AuDebugOn(!au_fi(file)->fi_hdir); -+ return au_fi(file)->fi_hdir->fd_hfile[0 + bindex].hf_file; -+} -+ -+/* todo: memory barrier? */ -+static inline unsigned int au_figen(struct file *f) -+{ -+ return atomic_read(&au_fi(f)->fi_generation); -+} -+ -+static inline void au_set_mmapped(struct file *f) -+{ -+ if (atomic_inc_return(&au_fi(f)->fi_mmapped)) -+ return; -+ pr_warn("fi_mmapped wrapped around\n"); -+ while (!atomic_inc_return(&au_fi(f)->fi_mmapped)) -+ ; -+} -+ -+static inline void au_unset_mmapped(struct file *f) -+{ -+ atomic_dec(&au_fi(f)->fi_mmapped); -+} -+ -+static inline int au_test_mmapped(struct file *f) -+{ -+ return atomic_read(&au_fi(f)->fi_mmapped); -+} -+ -+/* customize vma->vm_file */ -+ -+static inline void au_do_vm_file_reset(struct vm_area_struct *vma, -+ struct file *file) -+{ -+ struct file *f; -+ -+ f = vma->vm_file; -+ get_file(file); -+ vma->vm_file = file; -+ fput(f); -+} -+ -+#ifdef CONFIG_MMU -+#define AuDbgVmRegion(file, vma) do {} while (0) -+ -+static inline void au_vm_file_reset(struct vm_area_struct *vma, -+ struct file *file) -+{ -+ au_do_vm_file_reset(vma, file); -+} -+#else -+#define AuDbgVmRegion(file, vma) \ -+ AuDebugOn((vma)->vm_region && (vma)->vm_region->vm_file != (file)) -+ -+static inline void au_vm_file_reset(struct vm_area_struct *vma, -+ struct file *file) -+{ -+ struct file *f; -+ -+ au_do_vm_file_reset(vma, file); -+ f = vma->vm_region->vm_file; -+ get_file(file); -+ vma->vm_region->vm_file = file; -+ fput(f); -+} -+#endif /* CONFIG_MMU */ -+ -+/* handle vma->vm_prfile */ -+static inline void au_vm_prfile_set(struct vm_area_struct *vma, -+ struct file *file) -+{ -+ get_file(file); -+ vma->vm_prfile = file; -+#ifndef CONFIG_MMU -+ get_file(file); -+ vma->vm_region->vm_prfile = file; -+#endif -+} -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_FILE_H__ */ -diff --git a/fs/aufs/finfo.c b/fs/aufs/finfo.c -new file mode 100644 -index 0000000..7e25db3 ---- /dev/null -+++ b/fs/aufs/finfo.c -@@ -0,0 +1,156 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * file private data -+ */ -+ -+#include "aufs.h" -+ -+void au_hfput(struct au_hfile *hf, struct file *file) -+{ -+ /* todo: direct access f_flags */ -+ if (vfsub_file_flags(file) & __FMODE_EXEC) -+ allow_write_access(hf->hf_file); -+ fput(hf->hf_file); -+ hf->hf_file = NULL; -+ atomic_dec(&hf->hf_br->br_count); -+ hf->hf_br = NULL; -+} -+ -+void au_set_h_fptr(struct file *file, aufs_bindex_t bindex, struct file *val) -+{ -+ struct au_finfo *finfo = au_fi(file); -+ struct au_hfile *hf; -+ struct au_fidir *fidir; -+ -+ fidir = finfo->fi_hdir; -+ if (!fidir) { -+ AuDebugOn(finfo->fi_btop != bindex); -+ hf = &finfo->fi_htop; -+ } else -+ hf = fidir->fd_hfile + bindex; -+ -+ if (hf && hf->hf_file) -+ au_hfput(hf, file); -+ if (val) { -+ FiMustWriteLock(file); -+ AuDebugOn(IS_ERR_OR_NULL(file->f_dentry)); -+ hf->hf_file = val; -+ hf->hf_br = au_sbr(file->f_dentry->d_sb, bindex); -+ } -+} -+ -+void au_update_figen(struct file *file) -+{ -+ atomic_set(&au_fi(file)->fi_generation, au_digen(file->f_dentry)); -+ /* smp_mb(); */ /* atomic_set */ -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct au_fidir *au_fidir_alloc(struct super_block *sb) -+{ -+ struct au_fidir *fidir; -+ int nbr; -+ -+ nbr = au_sbend(sb) + 1; -+ if (nbr < 2) -+ nbr = 2; /* initial allocate for 2 branches */ -+ fidir = kzalloc(au_fidir_sz(nbr), GFP_NOFS); -+ if (fidir) { -+ fidir->fd_bbot = -1; -+ fidir->fd_nent = nbr; -+ } -+ -+ return fidir; -+} -+ -+int au_fidir_realloc(struct au_finfo *finfo, int nbr) -+{ -+ int err; -+ struct au_fidir *fidir, *p; -+ -+ AuRwMustWriteLock(&finfo->fi_rwsem); -+ fidir = finfo->fi_hdir; -+ AuDebugOn(!fidir); -+ -+ err = -ENOMEM; -+ p = au_kzrealloc(fidir, au_fidir_sz(fidir->fd_nent), au_fidir_sz(nbr), -+ GFP_NOFS); -+ if (p) { -+ p->fd_nent = nbr; -+ finfo->fi_hdir = p; -+ err = 0; -+ } -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void au_finfo_fin(struct file *file) -+{ -+ struct au_finfo *finfo; -+ -+ au_nfiles_dec(file->f_dentry->d_sb); -+ -+ finfo = au_fi(file); -+ AuDebugOn(finfo->fi_hdir); -+ AuRwDestroy(&finfo->fi_rwsem); -+ au_cache_free_finfo(finfo); -+} -+ -+void au_fi_init_once(void *_finfo) -+{ -+ struct au_finfo *finfo = _finfo; -+ static struct lock_class_key aufs_fi; -+ -+ au_rw_init(&finfo->fi_rwsem); -+ au_rw_class(&finfo->fi_rwsem, &aufs_fi); -+} -+ -+int au_finfo_init(struct file *file, struct au_fidir *fidir) -+{ -+ int err; -+ struct au_finfo *finfo; -+ struct dentry *dentry; -+ -+ err = -ENOMEM; -+ dentry = file->f_dentry; -+ finfo = au_cache_alloc_finfo(); -+ if (unlikely(!finfo)) -+ goto out; -+ -+ err = 0; -+ au_nfiles_inc(dentry->d_sb); -+ /* verbose coding for lock class name */ -+ if (!fidir) -+ au_rw_class(&finfo->fi_rwsem, au_lc_key + AuLcNonDir_FIINFO); -+ else -+ au_rw_class(&finfo->fi_rwsem, au_lc_key + AuLcDir_FIINFO); -+ au_rw_write_lock(&finfo->fi_rwsem); -+ finfo->fi_btop = -1; -+ finfo->fi_hdir = fidir; -+ atomic_set(&finfo->fi_generation, au_digen(dentry)); -+ /* smp_mb(); */ /* atomic_set */ -+ -+ file->private_data = finfo; -+ -+out: -+ return err; -+} -diff --git a/fs/aufs/fstype.h b/fs/aufs/fstype.h -new file mode 100644 -index 0000000..2842400 ---- /dev/null -+++ b/fs/aufs/fstype.h -@@ -0,0 +1,400 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * judging filesystem type -+ */ -+ -+#ifndef __AUFS_FSTYPE_H__ -+#define __AUFS_FSTYPE_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+#include -+#include -+#include -+ -+static inline int au_test_aufs(struct super_block *sb) -+{ -+ return sb->s_magic == AUFS_SUPER_MAGIC; -+} -+ -+static inline const char *au_sbtype(struct super_block *sb) -+{ -+ return sb->s_type->name; -+} -+ -+static inline int au_test_iso9660(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_ISO9660_FS) || defined(CONFIG_ISO9660_FS_MODULE) -+ return sb->s_magic == ISOFS_SUPER_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_romfs(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_ROMFS_FS) || defined(CONFIG_ROMFS_FS_MODULE) -+ return sb->s_magic == ROMFS_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_cramfs(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_CRAMFS) || defined(CONFIG_CRAMFS_MODULE) -+ return sb->s_magic == CRAMFS_MAGIC; -+#endif -+ return 0; -+} -+ -+static inline int au_test_nfs(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_NFS_FS) || defined(CONFIG_NFS_FS_MODULE) -+ return sb->s_magic == NFS_SUPER_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_fuse(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_FUSE_FS) || defined(CONFIG_FUSE_FS_MODULE) -+ return sb->s_magic == FUSE_SUPER_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_xfs(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_XFS_FS) || defined(CONFIG_XFS_FS_MODULE) -+ return sb->s_magic == XFS_SB_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_tmpfs(struct super_block *sb __maybe_unused) -+{ -+#ifdef CONFIG_TMPFS -+ return sb->s_magic == TMPFS_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_ecryptfs(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_ECRYPT_FS) || defined(CONFIG_ECRYPT_FS_MODULE) -+ return !strcmp(au_sbtype(sb), "ecryptfs"); -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_ramfs(struct super_block *sb) -+{ -+ return sb->s_magic == RAMFS_MAGIC; -+} -+ -+static inline int au_test_ubifs(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_UBIFS_FS) || defined(CONFIG_UBIFS_FS_MODULE) -+ return sb->s_magic == UBIFS_SUPER_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_procfs(struct super_block *sb __maybe_unused) -+{ -+#ifdef CONFIG_PROC_FS -+ return sb->s_magic == PROC_SUPER_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_sysfs(struct super_block *sb __maybe_unused) -+{ -+#ifdef CONFIG_SYSFS -+ return sb->s_magic == SYSFS_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_configfs(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_CONFIGFS_FS) || defined(CONFIG_CONFIGFS_FS_MODULE) -+ return sb->s_magic == CONFIGFS_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_minix(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_MINIX_FS) || defined(CONFIG_MINIX_FS_MODULE) -+ return sb->s_magic == MINIX3_SUPER_MAGIC -+ || sb->s_magic == MINIX2_SUPER_MAGIC -+ || sb->s_magic == MINIX2_SUPER_MAGIC2 -+ || sb->s_magic == MINIX_SUPER_MAGIC -+ || sb->s_magic == MINIX_SUPER_MAGIC2; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_fat(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_FAT_FS) || defined(CONFIG_FAT_FS_MODULE) -+ return sb->s_magic == MSDOS_SUPER_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_msdos(struct super_block *sb) -+{ -+ return au_test_fat(sb); -+} -+ -+static inline int au_test_vfat(struct super_block *sb) -+{ -+ return au_test_fat(sb); -+} -+ -+static inline int au_test_securityfs(struct super_block *sb __maybe_unused) -+{ -+#ifdef CONFIG_SECURITYFS -+ return sb->s_magic == SECURITYFS_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_squashfs(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_SQUASHFS) || defined(CONFIG_SQUASHFS_MODULE) -+ return sb->s_magic == SQUASHFS_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_btrfs(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_BTRFS_FS) || defined(CONFIG_BTRFS_FS_MODULE) -+ return sb->s_magic == BTRFS_SUPER_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_xenfs(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_XENFS) || defined(CONFIG_XENFS_MODULE) -+ return sb->s_magic == XENFS_SUPER_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_debugfs(struct super_block *sb __maybe_unused) -+{ -+#ifdef CONFIG_DEBUG_FS -+ return sb->s_magic == DEBUGFS_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_nilfs(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_NILFS) || defined(CONFIG_NILFS_MODULE) -+ return sb->s_magic == NILFS_SUPER_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_hfsplus(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_HFSPLUS_FS) || defined(CONFIG_HFSPLUS_FS_MODULE) -+ return sb->s_magic == HFSPLUS_SUPER_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+/* ---------------------------------------------------------------------- */ -+/* -+ * they can't be an aufs branch. -+ */ -+static inline int au_test_fs_unsuppoted(struct super_block *sb) -+{ -+ return -+#ifndef CONFIG_AUFS_BR_RAMFS -+ au_test_ramfs(sb) || -+#endif -+ au_test_procfs(sb) -+ || au_test_sysfs(sb) -+ || au_test_configfs(sb) -+ || au_test_debugfs(sb) -+ || au_test_securityfs(sb) -+ || au_test_xenfs(sb) -+ || au_test_ecryptfs(sb) -+ /* || !strcmp(au_sbtype(sb), "unionfs") */ -+ || au_test_aufs(sb); /* will be supported in next version */ -+} -+ -+static inline int au_test_fs_remote(struct super_block *sb) -+{ -+ return !au_test_tmpfs(sb) -+#ifdef CONFIG_AUFS_BR_RAMFS -+ && !au_test_ramfs(sb) -+#endif -+ && !(sb->s_type->fs_flags & FS_REQUIRES_DEV); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * Note: these functions (below) are created after reading ->getattr() in all -+ * filesystems under linux/fs. it means we have to do so in every update... -+ */ -+ -+/* -+ * some filesystems require getattr to refresh the inode attributes before -+ * referencing. -+ * in most cases, we can rely on the inode attribute in NFS (or every remote fs) -+ * and leave the work for d_revalidate() -+ */ -+static inline int au_test_fs_refresh_iattr(struct super_block *sb) -+{ -+ return au_test_nfs(sb) -+ || au_test_fuse(sb) -+ /* || au_test_btrfs(sb) */ /* untested */ -+ ; -+} -+ -+/* -+ * filesystems which don't maintain i_size or i_blocks. -+ */ -+static inline int au_test_fs_bad_iattr_size(struct super_block *sb) -+{ -+ return au_test_xfs(sb) -+ || au_test_btrfs(sb) -+ || au_test_ubifs(sb) -+ || au_test_hfsplus(sb) /* maintained, but incorrect */ -+ /* || au_test_minix(sb) */ /* untested */ -+ ; -+} -+ -+/* -+ * filesystems which don't store the correct value in some of their inode -+ * attributes. -+ */ -+static inline int au_test_fs_bad_iattr(struct super_block *sb) -+{ -+ return au_test_fs_bad_iattr_size(sb) -+ || au_test_fat(sb) -+ || au_test_msdos(sb) -+ || au_test_vfat(sb); -+} -+ -+/* they don't check i_nlink in link(2) */ -+static inline int au_test_fs_no_limit_nlink(struct super_block *sb) -+{ -+ return au_test_tmpfs(sb) -+#ifdef CONFIG_AUFS_BR_RAMFS -+ || au_test_ramfs(sb) -+#endif -+ || au_test_ubifs(sb) -+ || au_test_hfsplus(sb); -+} -+ -+/* -+ * filesystems which sets S_NOATIME and S_NOCMTIME. -+ */ -+static inline int au_test_fs_notime(struct super_block *sb) -+{ -+ return au_test_nfs(sb) -+ || au_test_fuse(sb) -+ || au_test_ubifs(sb) -+ ; -+} -+ -+/* temporary support for i#1 in cramfs */ -+static inline int au_test_fs_unique_ino(struct inode *inode) -+{ -+ if (au_test_cramfs(inode->i_sb)) -+ return inode->i_ino != 1; -+ return 1; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * the filesystem where the xino files placed must support i/o after unlink and -+ * maintain i_size and i_blocks. -+ */ -+static inline int au_test_fs_bad_xino(struct super_block *sb) -+{ -+ return au_test_fs_remote(sb) -+ || au_test_fs_bad_iattr_size(sb) -+ /* don't want unnecessary work for xino */ -+ || au_test_aufs(sb) -+ || au_test_ecryptfs(sb) -+ || au_test_nilfs(sb); -+} -+ -+static inline int au_test_fs_trunc_xino(struct super_block *sb) -+{ -+ return au_test_tmpfs(sb) -+ || au_test_ramfs(sb); -+} -+ -+/* -+ * test if the @sb is real-readonly. -+ */ -+static inline int au_test_fs_rr(struct super_block *sb) -+{ -+ return au_test_squashfs(sb) -+ || au_test_iso9660(sb) -+ || au_test_cramfs(sb) -+ || au_test_romfs(sb); -+} -+ -+/* -+ * test if the @inode is nfs with 'noacl' option -+ * NFS always sets MS_POSIXACL regardless its mount option 'noacl.' -+ */ -+static inline int au_test_nfs_noacl(struct inode *inode) -+{ -+ return au_test_nfs(inode->i_sb) -+ /* && IS_POSIXACL(inode) */ -+ && !nfs_server_capable(inode, NFS_CAP_ACLS); -+} -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_FSTYPE_H__ */ -diff --git a/fs/aufs/hfsnotify.c b/fs/aufs/hfsnotify.c -new file mode 100644 -index 0000000..6fa79b0 ---- /dev/null -+++ b/fs/aufs/hfsnotify.c -@@ -0,0 +1,288 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * fsnotify for the lower directories -+ */ -+ -+#include "aufs.h" -+ -+/* FS_IN_IGNORED is unnecessary */ -+static const __u32 AuHfsnMask = (FS_MOVED_TO | FS_MOVED_FROM | FS_DELETE -+ | FS_CREATE | FS_EVENT_ON_CHILD); -+static DECLARE_WAIT_QUEUE_HEAD(au_hfsn_wq); -+static __cacheline_aligned_in_smp atomic64_t au_hfsn_ifree = ATOMIC64_INIT(0); -+ -+static void au_hfsn_free_mark(struct fsnotify_mark *mark) -+{ -+ struct au_hnotify *hn = container_of(mark, struct au_hnotify, -+ hn_mark); -+ AuDbg("here\n"); -+ au_cache_free_hnotify(hn); -+ smp_mb__before_atomic(); -+ if (atomic64_dec_and_test(&au_hfsn_ifree)) -+ wake_up(&au_hfsn_wq); -+} -+ -+static int au_hfsn_alloc(struct au_hinode *hinode) -+{ -+ int err; -+ struct au_hnotify *hn; -+ struct super_block *sb; -+ struct au_branch *br; -+ struct fsnotify_mark *mark; -+ aufs_bindex_t bindex; -+ -+ hn = hinode->hi_notify; -+ sb = hn->hn_aufs_inode->i_sb; -+ bindex = au_br_index(sb, hinode->hi_id); -+ br = au_sbr(sb, bindex); -+ AuDebugOn(!br->br_hfsn); -+ -+ mark = &hn->hn_mark; -+ fsnotify_init_mark(mark, au_hfsn_free_mark); -+ mark->mask = AuHfsnMask; -+ /* -+ * by udba rename or rmdir, aufs assign a new inode to the known -+ * h_inode, so specify 1 to allow dups. -+ */ -+ lockdep_off(); -+ err = fsnotify_add_mark(mark, br->br_hfsn->hfsn_group, hinode->hi_inode, -+ /*mnt*/NULL, /*allow_dups*/1); -+ /* even if err */ -+ fsnotify_put_mark(mark); -+ lockdep_on(); -+ -+ return err; -+} -+ -+static int au_hfsn_free(struct au_hinode *hinode, struct au_hnotify *hn) -+{ -+ struct fsnotify_mark *mark; -+ unsigned long long ull; -+ struct fsnotify_group *group; -+ -+ ull = atomic64_inc_return(&au_hfsn_ifree); -+ BUG_ON(!ull); -+ -+ mark = &hn->hn_mark; -+ spin_lock(&mark->lock); -+ group = mark->group; -+ fsnotify_get_group(group); -+ spin_unlock(&mark->lock); -+ lockdep_off(); -+ fsnotify_destroy_mark(mark, group); -+ fsnotify_put_group(group); -+ lockdep_on(); -+ -+ /* free hn by myself */ -+ return 0; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void au_hfsn_ctl(struct au_hinode *hinode, int do_set) -+{ -+ struct fsnotify_mark *mark; -+ -+ mark = &hinode->hi_notify->hn_mark; -+ spin_lock(&mark->lock); -+ if (do_set) { -+ AuDebugOn(mark->mask & AuHfsnMask); -+ mark->mask |= AuHfsnMask; -+ } else { -+ AuDebugOn(!(mark->mask & AuHfsnMask)); -+ mark->mask &= ~AuHfsnMask; -+ } -+ spin_unlock(&mark->lock); -+ /* fsnotify_recalc_inode_mask(hinode->hi_inode); */ -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* #define AuDbgHnotify */ -+#ifdef AuDbgHnotify -+static char *au_hfsn_name(u32 mask) -+{ -+#ifdef CONFIG_AUFS_DEBUG -+#define test_ret(flag) \ -+ do { \ -+ if (mask & flag) \ -+ return #flag; \ -+ } while (0) -+ test_ret(FS_ACCESS); -+ test_ret(FS_MODIFY); -+ test_ret(FS_ATTRIB); -+ test_ret(FS_CLOSE_WRITE); -+ test_ret(FS_CLOSE_NOWRITE); -+ test_ret(FS_OPEN); -+ test_ret(FS_MOVED_FROM); -+ test_ret(FS_MOVED_TO); -+ test_ret(FS_CREATE); -+ test_ret(FS_DELETE); -+ test_ret(FS_DELETE_SELF); -+ test_ret(FS_MOVE_SELF); -+ test_ret(FS_UNMOUNT); -+ test_ret(FS_Q_OVERFLOW); -+ test_ret(FS_IN_IGNORED); -+ test_ret(FS_ISDIR); -+ test_ret(FS_IN_ONESHOT); -+ test_ret(FS_EVENT_ON_CHILD); -+ return ""; -+#undef test_ret -+#else -+ return "??"; -+#endif -+} -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void au_hfsn_free_group(struct fsnotify_group *group) -+{ -+ struct au_br_hfsnotify *hfsn = group->private; -+ -+ AuDbg("here\n"); -+ kfree(hfsn); -+} -+ -+static int au_hfsn_handle_event(struct fsnotify_group *group, -+ struct inode *inode, -+ struct fsnotify_mark *inode_mark, -+ struct fsnotify_mark *vfsmount_mark, -+ u32 mask, void *data, int data_type, -+ const unsigned char *file_name, u32 cookie) -+{ -+ int err; -+ struct au_hnotify *hnotify; -+ struct inode *h_dir, *h_inode; -+ struct qstr h_child_qstr = QSTR_INIT(file_name, strlen(file_name)); -+ -+ AuDebugOn(data_type != FSNOTIFY_EVENT_INODE); -+ -+ err = 0; -+ /* if FS_UNMOUNT happens, there must be another bug */ -+ AuDebugOn(mask & FS_UNMOUNT); -+ if (mask & (FS_IN_IGNORED | FS_UNMOUNT)) -+ goto out; -+ -+ h_dir = inode; -+ h_inode = NULL; -+#ifdef AuDbgHnotify -+ au_debug_on(); -+ if (1 || h_child_qstr.len != sizeof(AUFS_XINO_FNAME) - 1 -+ || strncmp(h_child_qstr.name, AUFS_XINO_FNAME, h_child_qstr.len)) { -+ AuDbg("i%lu, mask 0x%x %s, hcname %.*s, hi%lu\n", -+ h_dir->i_ino, mask, au_hfsn_name(mask), -+ AuLNPair(&h_child_qstr), h_inode ? h_inode->i_ino : 0); -+ /* WARN_ON(1); */ -+ } -+ au_debug_off(); -+#endif -+ -+ AuDebugOn(!inode_mark); -+ hnotify = container_of(inode_mark, struct au_hnotify, hn_mark); -+ err = au_hnotify(h_dir, hnotify, mask, &h_child_qstr, h_inode); -+ -+out: -+ return err; -+} -+ -+static struct fsnotify_ops au_hfsn_ops = { -+ .handle_event = au_hfsn_handle_event, -+ .free_group_priv = au_hfsn_free_group -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void au_hfsn_fin_br(struct au_branch *br) -+{ -+ struct au_br_hfsnotify *hfsn; -+ -+ hfsn = br->br_hfsn; -+ if (hfsn) { -+ lockdep_off(); -+ fsnotify_put_group(hfsn->hfsn_group); -+ lockdep_on(); -+ } -+} -+ -+static int au_hfsn_init_br(struct au_branch *br, int perm) -+{ -+ int err; -+ struct fsnotify_group *group; -+ struct au_br_hfsnotify *hfsn; -+ -+ err = 0; -+ br->br_hfsn = NULL; -+ if (!au_br_hnotifyable(perm)) -+ goto out; -+ -+ err = -ENOMEM; -+ hfsn = kmalloc(sizeof(*hfsn), GFP_NOFS); -+ if (unlikely(!hfsn)) -+ goto out; -+ -+ err = 0; -+ group = fsnotify_alloc_group(&au_hfsn_ops); -+ if (IS_ERR(group)) { -+ err = PTR_ERR(group); -+ pr_err("fsnotify_alloc_group() failed, %d\n", err); -+ goto out_hfsn; -+ } -+ -+ group->private = hfsn; -+ hfsn->hfsn_group = group; -+ br->br_hfsn = hfsn; -+ goto out; /* success */ -+ -+out_hfsn: -+ kfree(hfsn); -+out: -+ return err; -+} -+ -+static int au_hfsn_reset_br(unsigned int udba, struct au_branch *br, int perm) -+{ -+ int err; -+ -+ err = 0; -+ if (!br->br_hfsn) -+ err = au_hfsn_init_br(br, perm); -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void au_hfsn_fin(void) -+{ -+ AuDbg("au_hfsn_ifree %lld\n", (long long)atomic64_read(&au_hfsn_ifree)); -+ wait_event(au_hfsn_wq, !atomic64_read(&au_hfsn_ifree)); -+} -+ -+const struct au_hnotify_op au_hnotify_op = { -+ .ctl = au_hfsn_ctl, -+ .alloc = au_hfsn_alloc, -+ .free = au_hfsn_free, -+ -+ .fin = au_hfsn_fin, -+ -+ .reset_br = au_hfsn_reset_br, -+ .fin_br = au_hfsn_fin_br, -+ .init_br = au_hfsn_init_br -+}; -diff --git a/fs/aufs/hfsplus.c b/fs/aufs/hfsplus.c -new file mode 100644 -index 0000000..8a54c82 ---- /dev/null -+++ b/fs/aufs/hfsplus.c -@@ -0,0 +1,56 @@ -+/* -+ * Copyright (C) 2010-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * special support for filesystems which aqucires an inode mutex -+ * at final closing a file, eg, hfsplus. -+ * -+ * This trick is very simple and stupid, just to open the file before really -+ * neceeary open to tell hfsplus that this is not the final closing. -+ * The caller should call au_h_open_pre() after acquiring the inode mutex, -+ * and au_h_open_post() after releasing it. -+ */ -+ -+#include "aufs.h" -+ -+struct file *au_h_open_pre(struct dentry *dentry, aufs_bindex_t bindex, -+ int force_wr) -+{ -+ struct file *h_file; -+ struct dentry *h_dentry; -+ -+ h_dentry = au_h_dptr(dentry, bindex); -+ AuDebugOn(!h_dentry); -+ AuDebugOn(!h_dentry->d_inode); -+ -+ h_file = NULL; -+ if (au_test_hfsplus(h_dentry->d_sb) -+ && S_ISREG(h_dentry->d_inode->i_mode)) -+ h_file = au_h_open(dentry, bindex, -+ O_RDONLY | O_NOATIME | O_LARGEFILE, -+ /*file*/NULL, force_wr); -+ return h_file; -+} -+ -+void au_h_open_post(struct dentry *dentry, aufs_bindex_t bindex, -+ struct file *h_file) -+{ -+ if (h_file) { -+ fput(h_file); -+ au_sbr_put(dentry->d_sb, bindex); -+ } -+} -diff --git a/fs/aufs/hnotify.c b/fs/aufs/hnotify.c -new file mode 100644 -index 0000000..1801420 ---- /dev/null -+++ b/fs/aufs/hnotify.c -@@ -0,0 +1,714 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * abstraction to notify the direct changes on lower directories -+ */ -+ -+#include "aufs.h" -+ -+int au_hn_alloc(struct au_hinode *hinode, struct inode *inode) -+{ -+ int err; -+ struct au_hnotify *hn; -+ -+ err = -ENOMEM; -+ hn = au_cache_alloc_hnotify(); -+ if (hn) { -+ hn->hn_aufs_inode = inode; -+ hinode->hi_notify = hn; -+ err = au_hnotify_op.alloc(hinode); -+ AuTraceErr(err); -+ if (unlikely(err)) { -+ hinode->hi_notify = NULL; -+ au_cache_free_hnotify(hn); -+ /* -+ * The upper dir was removed by udba, but the same named -+ * dir left. In this case, aufs assignes a new inode -+ * number and set the monitor again. -+ * For the lower dir, the old monitnor is still left. -+ */ -+ if (err == -EEXIST) -+ err = 0; -+ } -+ } -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+void au_hn_free(struct au_hinode *hinode) -+{ -+ struct au_hnotify *hn; -+ -+ hn = hinode->hi_notify; -+ if (hn) { -+ hinode->hi_notify = NULL; -+ if (au_hnotify_op.free(hinode, hn)) -+ au_cache_free_hnotify(hn); -+ } -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void au_hn_ctl(struct au_hinode *hinode, int do_set) -+{ -+ if (hinode->hi_notify) -+ au_hnotify_op.ctl(hinode, do_set); -+} -+ -+void au_hn_reset(struct inode *inode, unsigned int flags) -+{ -+ aufs_bindex_t bindex, bend; -+ struct inode *hi; -+ struct dentry *iwhdentry; -+ -+ bend = au_ibend(inode); -+ for (bindex = au_ibstart(inode); bindex <= bend; bindex++) { -+ hi = au_h_iptr(inode, bindex); -+ if (!hi) -+ continue; -+ -+ /* mutex_lock_nested(&hi->i_mutex, AuLsc_I_CHILD); */ -+ iwhdentry = au_hi_wh(inode, bindex); -+ if (iwhdentry) -+ dget(iwhdentry); -+ au_igrab(hi); -+ au_set_h_iptr(inode, bindex, NULL, 0); -+ au_set_h_iptr(inode, bindex, au_igrab(hi), -+ flags & ~AuHi_XINO); -+ iput(hi); -+ dput(iwhdentry); -+ /* mutex_unlock(&hi->i_mutex); */ -+ } -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int hn_xino(struct inode *inode, struct inode *h_inode) -+{ -+ int err; -+ aufs_bindex_t bindex, bend, bfound, bstart; -+ struct inode *h_i; -+ -+ err = 0; -+ if (unlikely(inode->i_ino == AUFS_ROOT_INO)) { -+ pr_warn("branch root dir was changed\n"); -+ goto out; -+ } -+ -+ bfound = -1; -+ bend = au_ibend(inode); -+ bstart = au_ibstart(inode); -+#if 0 /* reserved for future use */ -+ if (bindex == bend) { -+ /* keep this ino in rename case */ -+ goto out; -+ } -+#endif -+ for (bindex = bstart; bindex <= bend; bindex++) -+ if (au_h_iptr(inode, bindex) == h_inode) { -+ bfound = bindex; -+ break; -+ } -+ if (bfound < 0) -+ goto out; -+ -+ for (bindex = bstart; bindex <= bend; bindex++) { -+ h_i = au_h_iptr(inode, bindex); -+ if (!h_i) -+ continue; -+ -+ err = au_xino_write(inode->i_sb, bindex, h_i->i_ino, /*ino*/0); -+ /* ignore this error */ -+ /* bad action? */ -+ } -+ -+ /* children inode number will be broken */ -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+static int hn_gen_tree(struct dentry *dentry) -+{ -+ int err, i, j, ndentry; -+ struct au_dcsub_pages dpages; -+ struct au_dpage *dpage; -+ struct dentry **dentries; -+ -+ err = au_dpages_init(&dpages, GFP_NOFS); -+ if (unlikely(err)) -+ goto out; -+ err = au_dcsub_pages(&dpages, dentry, NULL, NULL); -+ if (unlikely(err)) -+ goto out_dpages; -+ -+ for (i = 0; i < dpages.ndpage; i++) { -+ dpage = dpages.dpages + i; -+ dentries = dpage->dentries; -+ ndentry = dpage->ndentry; -+ for (j = 0; j < ndentry; j++) { -+ struct dentry *d; -+ -+ d = dentries[j]; -+ if (IS_ROOT(d)) -+ continue; -+ -+ au_digen_dec(d); -+ if (d->d_inode) -+ /* todo: reset children xino? -+ cached children only? */ -+ au_iigen_dec(d->d_inode); -+ } -+ } -+ -+out_dpages: -+ au_dpages_free(&dpages); -+ -+#if 0 -+ /* discard children */ -+ dentry_unhash(dentry); -+ dput(dentry); -+#endif -+out: -+ return err; -+} -+ -+/* -+ * return 0 if processed. -+ */ -+static int hn_gen_by_inode(char *name, unsigned int nlen, struct inode *inode, -+ const unsigned int isdir) -+{ -+ int err; -+ struct dentry *d; -+ struct qstr *dname; -+ -+ err = 1; -+ if (unlikely(inode->i_ino == AUFS_ROOT_INO)) { -+ pr_warn("branch root dir was changed\n"); -+ err = 0; -+ goto out; -+ } -+ -+ if (!isdir) { -+ AuDebugOn(!name); -+ au_iigen_dec(inode); -+ spin_lock(&inode->i_lock); -+ hlist_for_each_entry(d, &inode->i_dentry, d_u.d_alias) { -+ spin_lock(&d->d_lock); -+ dname = &d->d_name; -+ if (dname->len != nlen -+ && memcmp(dname->name, name, nlen)) { -+ spin_unlock(&d->d_lock); -+ continue; -+ } -+ err = 0; -+ au_digen_dec(d); -+ spin_unlock(&d->d_lock); -+ break; -+ } -+ spin_unlock(&inode->i_lock); -+ } else { -+ au_fset_si(au_sbi(inode->i_sb), FAILED_REFRESH_DIR); -+ d = d_find_any_alias(inode); -+ if (!d) { -+ au_iigen_dec(inode); -+ goto out; -+ } -+ -+ spin_lock(&d->d_lock); -+ dname = &d->d_name; -+ if (dname->len == nlen && !memcmp(dname->name, name, nlen)) { -+ spin_unlock(&d->d_lock); -+ err = hn_gen_tree(d); -+ spin_lock(&d->d_lock); -+ } -+ spin_unlock(&d->d_lock); -+ dput(d); -+ } -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+static int hn_gen_by_name(struct dentry *dentry, const unsigned int isdir) -+{ -+ int err; -+ struct inode *inode; -+ -+ inode = dentry->d_inode; -+ if (IS_ROOT(dentry) -+ /* || (inode && inode->i_ino == AUFS_ROOT_INO) */ -+ ) { -+ pr_warn("branch root dir was changed\n"); -+ return 0; -+ } -+ -+ err = 0; -+ if (!isdir) { -+ au_digen_dec(dentry); -+ if (inode) -+ au_iigen_dec(inode); -+ } else { -+ au_fset_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIR); -+ if (inode) -+ err = hn_gen_tree(dentry); -+ } -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* hnotify job flags */ -+#define AuHnJob_XINO0 1 -+#define AuHnJob_GEN (1 << 1) -+#define AuHnJob_DIRENT (1 << 2) -+#define AuHnJob_ISDIR (1 << 3) -+#define AuHnJob_TRYXINO0 (1 << 4) -+#define AuHnJob_MNTPNT (1 << 5) -+#define au_ftest_hnjob(flags, name) ((flags) & AuHnJob_##name) -+#define au_fset_hnjob(flags, name) \ -+ do { (flags) |= AuHnJob_##name; } while (0) -+#define au_fclr_hnjob(flags, name) \ -+ do { (flags) &= ~AuHnJob_##name; } while (0) -+ -+enum { -+ AuHn_CHILD, -+ AuHn_PARENT, -+ AuHnLast -+}; -+ -+struct au_hnotify_args { -+ struct inode *h_dir, *dir, *h_child_inode; -+ u32 mask; -+ unsigned int flags[AuHnLast]; -+ unsigned int h_child_nlen; -+ char h_child_name[]; -+}; -+ -+struct hn_job_args { -+ unsigned int flags; -+ struct inode *inode, *h_inode, *dir, *h_dir; -+ struct dentry *dentry; -+ char *h_name; -+ int h_nlen; -+}; -+ -+static int hn_job(struct hn_job_args *a) -+{ -+ const unsigned int isdir = au_ftest_hnjob(a->flags, ISDIR); -+ int e; -+ -+ /* reset xino */ -+ if (au_ftest_hnjob(a->flags, XINO0) && a->inode) -+ hn_xino(a->inode, a->h_inode); /* ignore this error */ -+ -+ if (au_ftest_hnjob(a->flags, TRYXINO0) -+ && a->inode -+ && a->h_inode) { -+ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); -+ if (!a->h_inode->i_nlink -+ && !(a->h_inode->i_state & I_LINKABLE)) -+ hn_xino(a->inode, a->h_inode); /* ignore this error */ -+ mutex_unlock(&a->h_inode->i_mutex); -+ } -+ -+ /* make the generation obsolete */ -+ if (au_ftest_hnjob(a->flags, GEN)) { -+ e = -1; -+ if (a->inode) -+ e = hn_gen_by_inode(a->h_name, a->h_nlen, a->inode, -+ isdir); -+ if (e && a->dentry) -+ hn_gen_by_name(a->dentry, isdir); -+ /* ignore this error */ -+ } -+ -+ /* make dir entries obsolete */ -+ if (au_ftest_hnjob(a->flags, DIRENT) && a->inode) { -+ struct au_vdir *vdir; -+ -+ vdir = au_ivdir(a->inode); -+ if (vdir) -+ vdir->vd_jiffy = 0; -+ /* IMustLock(a->inode); */ -+ /* a->inode->i_version++; */ -+ } -+ -+ /* can do nothing but warn */ -+ if (au_ftest_hnjob(a->flags, MNTPNT) -+ && a->dentry -+ && d_mountpoint(a->dentry)) -+ pr_warn("mount-point %pd is removed or renamed\n", a->dentry); -+ -+ return 0; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static struct dentry *lookup_wlock_by_name(char *name, unsigned int nlen, -+ struct inode *dir) -+{ -+ struct dentry *dentry, *d, *parent; -+ struct qstr *dname; -+ -+ parent = d_find_any_alias(dir); -+ if (!parent) -+ return NULL; -+ -+ dentry = NULL; -+ spin_lock(&parent->d_lock); -+ list_for_each_entry(d, &parent->d_subdirs, d_child) { -+ /* AuDbg("%pd\n", d); */ -+ spin_lock_nested(&d->d_lock, DENTRY_D_LOCK_NESTED); -+ dname = &d->d_name; -+ if (dname->len != nlen || memcmp(dname->name, name, nlen)) -+ goto cont_unlock; -+ if (au_di(d)) -+ au_digen_dec(d); -+ else -+ goto cont_unlock; -+ if (au_dcount(d) > 0) { -+ dentry = dget_dlock(d); -+ spin_unlock(&d->d_lock); -+ break; -+ } -+ -+cont_unlock: -+ spin_unlock(&d->d_lock); -+ } -+ spin_unlock(&parent->d_lock); -+ dput(parent); -+ -+ if (dentry) -+ di_write_lock_child(dentry); -+ -+ return dentry; -+} -+ -+static struct inode *lookup_wlock_by_ino(struct super_block *sb, -+ aufs_bindex_t bindex, ino_t h_ino) -+{ -+ struct inode *inode; -+ ino_t ino; -+ int err; -+ -+ inode = NULL; -+ err = au_xino_read(sb, bindex, h_ino, &ino); -+ if (!err && ino) -+ inode = ilookup(sb, ino); -+ if (!inode) -+ goto out; -+ -+ if (unlikely(inode->i_ino == AUFS_ROOT_INO)) { -+ pr_warn("wrong root branch\n"); -+ iput(inode); -+ inode = NULL; -+ goto out; -+ } -+ -+ ii_write_lock_child(inode); -+ -+out: -+ return inode; -+} -+ -+static void au_hn_bh(void *_args) -+{ -+ struct au_hnotify_args *a = _args; -+ struct super_block *sb; -+ aufs_bindex_t bindex, bend, bfound; -+ unsigned char xino, try_iput; -+ int err; -+ struct inode *inode; -+ ino_t h_ino; -+ struct hn_job_args args; -+ struct dentry *dentry; -+ struct au_sbinfo *sbinfo; -+ -+ AuDebugOn(!_args); -+ AuDebugOn(!a->h_dir); -+ AuDebugOn(!a->dir); -+ AuDebugOn(!a->mask); -+ AuDbg("mask 0x%x, i%lu, hi%lu, hci%lu\n", -+ a->mask, a->dir->i_ino, a->h_dir->i_ino, -+ a->h_child_inode ? a->h_child_inode->i_ino : 0); -+ -+ inode = NULL; -+ dentry = NULL; -+ /* -+ * do not lock a->dir->i_mutex here -+ * because of d_revalidate() may cause a deadlock. -+ */ -+ sb = a->dir->i_sb; -+ AuDebugOn(!sb); -+ sbinfo = au_sbi(sb); -+ AuDebugOn(!sbinfo); -+ si_write_lock(sb, AuLock_NOPLMW); -+ -+ ii_read_lock_parent(a->dir); -+ bfound = -1; -+ bend = au_ibend(a->dir); -+ for (bindex = au_ibstart(a->dir); bindex <= bend; bindex++) -+ if (au_h_iptr(a->dir, bindex) == a->h_dir) { -+ bfound = bindex; -+ break; -+ } -+ ii_read_unlock(a->dir); -+ if (unlikely(bfound < 0)) -+ goto out; -+ -+ xino = !!au_opt_test(au_mntflags(sb), XINO); -+ h_ino = 0; -+ if (a->h_child_inode) -+ h_ino = a->h_child_inode->i_ino; -+ -+ if (a->h_child_nlen -+ && (au_ftest_hnjob(a->flags[AuHn_CHILD], GEN) -+ || au_ftest_hnjob(a->flags[AuHn_CHILD], MNTPNT))) -+ dentry = lookup_wlock_by_name(a->h_child_name, a->h_child_nlen, -+ a->dir); -+ try_iput = 0; -+ if (dentry) -+ inode = dentry->d_inode; -+ if (xino && !inode && h_ino -+ && (au_ftest_hnjob(a->flags[AuHn_CHILD], XINO0) -+ || au_ftest_hnjob(a->flags[AuHn_CHILD], TRYXINO0) -+ || au_ftest_hnjob(a->flags[AuHn_CHILD], GEN))) { -+ inode = lookup_wlock_by_ino(sb, bfound, h_ino); -+ try_iput = 1; -+ } -+ -+ args.flags = a->flags[AuHn_CHILD]; -+ args.dentry = dentry; -+ args.inode = inode; -+ args.h_inode = a->h_child_inode; -+ args.dir = a->dir; -+ args.h_dir = a->h_dir; -+ args.h_name = a->h_child_name; -+ args.h_nlen = a->h_child_nlen; -+ err = hn_job(&args); -+ if (dentry) { -+ if (au_di(dentry)) -+ di_write_unlock(dentry); -+ dput(dentry); -+ } -+ if (inode && try_iput) { -+ ii_write_unlock(inode); -+ iput(inode); -+ } -+ -+ ii_write_lock_parent(a->dir); -+ args.flags = a->flags[AuHn_PARENT]; -+ args.dentry = NULL; -+ args.inode = a->dir; -+ args.h_inode = a->h_dir; -+ args.dir = NULL; -+ args.h_dir = NULL; -+ args.h_name = NULL; -+ args.h_nlen = 0; -+ err = hn_job(&args); -+ ii_write_unlock(a->dir); -+ -+out: -+ iput(a->h_child_inode); -+ iput(a->h_dir); -+ iput(a->dir); -+ si_write_unlock(sb); -+ au_nwt_done(&sbinfo->si_nowait); -+ kfree(a); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int au_hnotify(struct inode *h_dir, struct au_hnotify *hnotify, u32 mask, -+ struct qstr *h_child_qstr, struct inode *h_child_inode) -+{ -+ int err, len; -+ unsigned int flags[AuHnLast], f; -+ unsigned char isdir, isroot, wh; -+ struct inode *dir; -+ struct au_hnotify_args *args; -+ char *p, *h_child_name; -+ -+ err = 0; -+ AuDebugOn(!hnotify || !hnotify->hn_aufs_inode); -+ dir = igrab(hnotify->hn_aufs_inode); -+ if (!dir) -+ goto out; -+ -+ isroot = (dir->i_ino == AUFS_ROOT_INO); -+ wh = 0; -+ h_child_name = (void *)h_child_qstr->name; -+ len = h_child_qstr->len; -+ if (h_child_name) { -+ if (len > AUFS_WH_PFX_LEN -+ && !memcmp(h_child_name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { -+ h_child_name += AUFS_WH_PFX_LEN; -+ len -= AUFS_WH_PFX_LEN; -+ wh = 1; -+ } -+ } -+ -+ isdir = 0; -+ if (h_child_inode) -+ isdir = !!S_ISDIR(h_child_inode->i_mode); -+ flags[AuHn_PARENT] = AuHnJob_ISDIR; -+ flags[AuHn_CHILD] = 0; -+ if (isdir) -+ flags[AuHn_CHILD] = AuHnJob_ISDIR; -+ au_fset_hnjob(flags[AuHn_PARENT], DIRENT); -+ au_fset_hnjob(flags[AuHn_CHILD], GEN); -+ switch (mask & FS_EVENTS_POSS_ON_CHILD) { -+ case FS_MOVED_FROM: -+ case FS_MOVED_TO: -+ au_fset_hnjob(flags[AuHn_CHILD], XINO0); -+ au_fset_hnjob(flags[AuHn_CHILD], MNTPNT); -+ /*FALLTHROUGH*/ -+ case FS_CREATE: -+ AuDebugOn(!h_child_name); -+ break; -+ -+ case FS_DELETE: -+ /* -+ * aufs never be able to get this child inode. -+ * revalidation should be in d_revalidate() -+ * by checking i_nlink, i_generation or d_unhashed(). -+ */ -+ AuDebugOn(!h_child_name); -+ au_fset_hnjob(flags[AuHn_CHILD], TRYXINO0); -+ au_fset_hnjob(flags[AuHn_CHILD], MNTPNT); -+ break; -+ -+ default: -+ AuDebugOn(1); -+ } -+ -+ if (wh) -+ h_child_inode = NULL; -+ -+ err = -ENOMEM; -+ /* iput() and kfree() will be called in au_hnotify() */ -+ args = kmalloc(sizeof(*args) + len + 1, GFP_NOFS); -+ if (unlikely(!args)) { -+ AuErr1("no memory\n"); -+ iput(dir); -+ goto out; -+ } -+ args->flags[AuHn_PARENT] = flags[AuHn_PARENT]; -+ args->flags[AuHn_CHILD] = flags[AuHn_CHILD]; -+ args->mask = mask; -+ args->dir = dir; -+ args->h_dir = igrab(h_dir); -+ if (h_child_inode) -+ h_child_inode = igrab(h_child_inode); /* can be NULL */ -+ args->h_child_inode = h_child_inode; -+ args->h_child_nlen = len; -+ if (len) { -+ p = (void *)args; -+ p += sizeof(*args); -+ memcpy(p, h_child_name, len); -+ p[len] = 0; -+ } -+ -+ /* NFS fires the event for silly-renamed one from kworker */ -+ f = 0; -+ if (!dir->i_nlink -+ || (au_test_nfs(h_dir->i_sb) && (mask & FS_DELETE))) -+ f = AuWkq_NEST; -+ err = au_wkq_nowait(au_hn_bh, args, dir->i_sb, f); -+ if (unlikely(err)) { -+ pr_err("wkq %d\n", err); -+ iput(args->h_child_inode); -+ iput(args->h_dir); -+ iput(args->dir); -+ kfree(args); -+ } -+ -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int au_hnotify_reset_br(unsigned int udba, struct au_branch *br, int perm) -+{ -+ int err; -+ -+ AuDebugOn(!(udba & AuOptMask_UDBA)); -+ -+ err = 0; -+ if (au_hnotify_op.reset_br) -+ err = au_hnotify_op.reset_br(udba, br, perm); -+ -+ return err; -+} -+ -+int au_hnotify_init_br(struct au_branch *br, int perm) -+{ -+ int err; -+ -+ err = 0; -+ if (au_hnotify_op.init_br) -+ err = au_hnotify_op.init_br(br, perm); -+ -+ return err; -+} -+ -+void au_hnotify_fin_br(struct au_branch *br) -+{ -+ if (au_hnotify_op.fin_br) -+ au_hnotify_op.fin_br(br); -+} -+ -+static void au_hn_destroy_cache(void) -+{ -+ kmem_cache_destroy(au_cachep[AuCache_HNOTIFY]); -+ au_cachep[AuCache_HNOTIFY] = NULL; -+} -+ -+int __init au_hnotify_init(void) -+{ -+ int err; -+ -+ err = -ENOMEM; -+ au_cachep[AuCache_HNOTIFY] = AuCache(au_hnotify); -+ if (au_cachep[AuCache_HNOTIFY]) { -+ err = 0; -+ if (au_hnotify_op.init) -+ err = au_hnotify_op.init(); -+ if (unlikely(err)) -+ au_hn_destroy_cache(); -+ } -+ AuTraceErr(err); -+ return err; -+} -+ -+void au_hnotify_fin(void) -+{ -+ if (au_hnotify_op.fin) -+ au_hnotify_op.fin(); -+ /* cf. au_cache_fin() */ -+ if (au_cachep[AuCache_HNOTIFY]) -+ au_hn_destroy_cache(); -+} -diff --git a/fs/aufs/i_op.c b/fs/aufs/i_op.c -new file mode 100644 -index 0000000..02dc95a ---- /dev/null -+++ b/fs/aufs/i_op.c -@@ -0,0 +1,1460 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * inode operations (except add/del/rename) -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include "aufs.h" -+ -+static int h_permission(struct inode *h_inode, int mask, -+ struct vfsmount *h_mnt, int brperm) -+{ -+ int err; -+ const unsigned char write_mask = !!(mask & (MAY_WRITE | MAY_APPEND)); -+ -+ err = -EACCES; -+ if ((write_mask && IS_IMMUTABLE(h_inode)) -+ || ((mask & MAY_EXEC) -+ && S_ISREG(h_inode->i_mode) -+ && ((h_mnt->mnt_flags & MNT_NOEXEC) -+ || !(h_inode->i_mode & S_IXUGO)))) -+ goto out; -+ -+ /* -+ * - skip the lower fs test in the case of write to ro branch. -+ * - nfs dir permission write check is optimized, but a policy for -+ * link/rename requires a real check. -+ * - nfs always sets MS_POSIXACL regardless its mount option 'noacl.' -+ * in this case, generic_permission() returns -EOPNOTSUPP. -+ */ -+ if ((write_mask && !au_br_writable(brperm)) -+ || (au_test_nfs(h_inode->i_sb) && S_ISDIR(h_inode->i_mode) -+ && write_mask && !(mask & MAY_READ)) -+ || !h_inode->i_op->permission) { -+ /* AuLabel(generic_permission); */ -+ /* AuDbg("get_acl %pf\n", h_inode->i_op->get_acl); */ -+ err = generic_permission(h_inode, mask); -+ if (err == -EOPNOTSUPP && au_test_nfs_noacl(h_inode)) -+ err = h_inode->i_op->permission(h_inode, mask); -+ AuTraceErr(err); -+ } else { -+ /* AuLabel(h_inode->permission); */ -+ err = h_inode->i_op->permission(h_inode, mask); -+ AuTraceErr(err); -+ } -+ -+ if (!err) -+ err = devcgroup_inode_permission(h_inode, mask); -+ if (!err) -+ err = security_inode_permission(h_inode, mask); -+ -+#if 0 -+ if (!err) { -+ /* todo: do we need to call ima_path_check()? */ -+ struct path h_path = { -+ .dentry = -+ .mnt = h_mnt -+ }; -+ err = ima_path_check(&h_path, -+ mask & (MAY_READ | MAY_WRITE | MAY_EXEC), -+ IMA_COUNT_LEAVE); -+ } -+#endif -+ -+out: -+ return err; -+} -+ -+static int aufs_permission(struct inode *inode, int mask) -+{ -+ int err; -+ aufs_bindex_t bindex, bend; -+ const unsigned char isdir = !!S_ISDIR(inode->i_mode), -+ write_mask = !!(mask & (MAY_WRITE | MAY_APPEND)); -+ struct inode *h_inode; -+ struct super_block *sb; -+ struct au_branch *br; -+ -+ /* todo: support rcu-walk? */ -+ if (mask & MAY_NOT_BLOCK) -+ return -ECHILD; -+ -+ sb = inode->i_sb; -+ si_read_lock(sb, AuLock_FLUSH); -+ ii_read_lock_child(inode); -+#if 0 -+ err = au_iigen_test(inode, au_sigen(sb)); -+ if (unlikely(err)) -+ goto out; -+#endif -+ -+ if (!isdir -+ || write_mask -+ || au_opt_test(au_mntflags(sb), DIRPERM1)) { -+ err = au_busy_or_stale(); -+ h_inode = au_h_iptr(inode, au_ibstart(inode)); -+ if (unlikely(!h_inode -+ || (h_inode->i_mode & S_IFMT) -+ != (inode->i_mode & S_IFMT))) -+ goto out; -+ -+ err = 0; -+ bindex = au_ibstart(inode); -+ br = au_sbr(sb, bindex); -+ err = h_permission(h_inode, mask, au_br_mnt(br), br->br_perm); -+ if (write_mask -+ && !err -+ && !special_file(h_inode->i_mode)) { -+ /* test whether the upper writable branch exists */ -+ err = -EROFS; -+ for (; bindex >= 0; bindex--) -+ if (!au_br_rdonly(au_sbr(sb, bindex))) { -+ err = 0; -+ break; -+ } -+ } -+ goto out; -+ } -+ -+ /* non-write to dir */ -+ err = 0; -+ bend = au_ibend(inode); -+ for (bindex = au_ibstart(inode); !err && bindex <= bend; bindex++) { -+ h_inode = au_h_iptr(inode, bindex); -+ if (h_inode) { -+ err = au_busy_or_stale(); -+ if (unlikely(!S_ISDIR(h_inode->i_mode))) -+ break; -+ -+ br = au_sbr(sb, bindex); -+ err = h_permission(h_inode, mask, au_br_mnt(br), -+ br->br_perm); -+ } -+ } -+ -+out: -+ ii_read_unlock(inode); -+ si_read_unlock(sb); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry, -+ unsigned int flags) -+{ -+ struct dentry *ret, *parent; -+ struct inode *inode; -+ struct super_block *sb; -+ int err, npositive; -+ -+ IMustLock(dir); -+ -+ /* todo: support rcu-walk? */ -+ ret = ERR_PTR(-ECHILD); -+ if (flags & LOOKUP_RCU) -+ goto out; -+ -+ ret = ERR_PTR(-ENAMETOOLONG); -+ if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) -+ goto out; -+ -+ sb = dir->i_sb; -+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ ret = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out; -+ -+ err = au_di_init(dentry); -+ ret = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out_si; -+ -+ inode = NULL; -+ npositive = 0; /* suppress a warning */ -+ parent = dentry->d_parent; /* dir inode is locked */ -+ di_read_lock_parent(parent, AuLock_IR); -+ err = au_alive_dir(parent); -+ if (!err) -+ err = au_digen_test(parent, au_sigen(sb)); -+ if (!err) { -+ npositive = au_lkup_dentry(dentry, au_dbstart(parent), -+ /*type*/0); -+ err = npositive; -+ } -+ di_read_unlock(parent, AuLock_IR); -+ ret = ERR_PTR(err); -+ if (unlikely(err < 0)) -+ goto out_unlock; -+ -+ if (npositive) { -+ inode = au_new_inode(dentry, /*must_new*/0); -+ if (IS_ERR(inode)) { -+ ret = (void *)inode; -+ inode = NULL; -+ goto out_unlock; -+ } -+ } -+ -+ if (inode) -+ atomic_inc(&inode->i_count); -+ ret = d_splice_alias(inode, dentry); -+ if (IS_ERR(ret) -+ && PTR_ERR(ret) == -EIO -+ && inode -+ && S_ISDIR(inode->i_mode)) { -+ atomic_inc(&inode->i_count); -+ ret = d_materialise_unique(dentry, inode); -+ if (!IS_ERR(ret)) -+ ii_write_unlock(inode); -+ } -+#if 0 -+ if (unlikely(d_need_lookup(dentry))) { -+ spin_lock(&dentry->d_lock); -+ dentry->d_flags &= ~DCACHE_NEED_LOOKUP; -+ spin_unlock(&dentry->d_lock); -+ } else -+#endif -+ if (inode) { -+ if (!IS_ERR(ret)) -+ iput(inode); -+ else { -+ ii_write_unlock(inode); -+ iput(inode); -+ inode = NULL; -+ } -+ } -+ -+out_unlock: -+ di_write_unlock(dentry); -+ if (inode) { -+ /* verbose coding for lock class name */ -+ if (unlikely(S_ISLNK(inode->i_mode))) -+ au_rw_class(&au_di(dentry)->di_rwsem, -+ au_lc_key + AuLcSymlink_DIINFO); -+ else if (unlikely(S_ISDIR(inode->i_mode))) -+ au_rw_class(&au_di(dentry)->di_rwsem, -+ au_lc_key + AuLcDir_DIINFO); -+ else /* likely */ -+ au_rw_class(&au_di(dentry)->di_rwsem, -+ au_lc_key + AuLcNonDir_DIINFO); -+ } -+out_si: -+ si_read_unlock(sb); -+out: -+ return ret; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct aopen_node { -+ struct hlist_node hlist; -+ struct file *file, *h_file; -+}; -+ -+static int au_do_aopen(struct inode *inode, struct file *file) -+{ -+ struct au_sphlhead *aopen; -+ struct aopen_node *node; -+ struct au_do_open_args args = { -+ .no_lock = 1, -+ .open = au_do_open_nondir -+ }; -+ -+ aopen = &au_sbi(inode->i_sb)->si_aopen; -+ spin_lock(&aopen->spin); -+ hlist_for_each_entry(node, &aopen->head, hlist) -+ if (node->file == file) { -+ args.h_file = node->h_file; -+ break; -+ } -+ spin_unlock(&aopen->spin); -+ /* AuDebugOn(!args.h_file); */ -+ -+ return au_do_open(file, &args); -+} -+ -+static int aufs_atomic_open(struct inode *dir, struct dentry *dentry, -+ struct file *file, unsigned int open_flag, -+ umode_t create_mode, int *opened) -+{ -+ int err, h_opened = *opened; -+ struct dentry *parent; -+ struct dentry *d; -+ struct au_sphlhead *aopen; -+ struct vfsub_aopen_args args = { -+ .open_flag = open_flag, -+ .create_mode = create_mode, -+ .opened = &h_opened -+ }; -+ struct aopen_node aopen_node = { -+ .file = file -+ }; -+ -+ IMustLock(dir); -+ AuDbg("open_flag 0x%x\n", open_flag); -+ AuDbgDentry(dentry); -+ -+ err = 0; -+ if (!au_di(dentry)) { -+ d = aufs_lookup(dir, dentry, /*flags*/0); -+ if (IS_ERR(d)) { -+ err = PTR_ERR(d); -+ goto out; -+ } else if (d) { -+ /* -+ * obsoleted dentry found. -+ * another error will be returned later. -+ */ -+ d_drop(d); -+ dput(d); -+ AuDbgDentry(d); -+ } -+ AuDbgDentry(dentry); -+ } -+ -+ if (d_is_positive(dentry) -+ || d_unhashed(dentry) -+ || d_unlinked(dentry) -+ || !(open_flag & O_CREAT)) -+ goto out_no_open; -+ -+ err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN); -+ if (unlikely(err)) -+ goto out; -+ -+ parent = dentry->d_parent; /* dir is locked */ -+ di_write_lock_parent(parent); -+ err = au_lkup_dentry(dentry, /*bstart*/0, /*type*/0); -+ if (unlikely(err)) -+ goto out_unlock; -+ -+ AuDbgDentry(dentry); -+ if (d_is_positive(dentry)) -+ goto out_unlock; -+ -+ args.file = get_empty_filp(); -+ err = PTR_ERR(args.file); -+ if (IS_ERR(args.file)) -+ goto out_unlock; -+ -+ args.file->f_flags = file->f_flags; -+ err = au_aopen_or_create(dir, dentry, &args); -+ AuTraceErr(err); -+ AuDbgFile(args.file); -+ if (unlikely(err < 0)) { -+ if (h_opened & FILE_OPENED) -+ fput(args.file); -+ else -+ put_filp(args.file); -+ goto out_unlock; -+ } -+ -+ /* some filesystems don't set FILE_CREATED while succeeded? */ -+ *opened |= FILE_CREATED; -+ if (h_opened & FILE_OPENED) -+ aopen_node.h_file = args.file; -+ else { -+ put_filp(args.file); -+ args.file = NULL; -+ } -+ aopen = &au_sbi(dir->i_sb)->si_aopen; -+ au_sphl_add(&aopen_node.hlist, aopen); -+ err = finish_open(file, dentry, au_do_aopen, opened); -+ au_sphl_del(&aopen_node.hlist, aopen); -+ AuTraceErr(err); -+ AuDbgFile(file); -+ if (aopen_node.h_file) -+ fput(aopen_node.h_file); -+ -+out_unlock: -+ di_write_unlock(parent); -+ aufs_read_unlock(dentry, AuLock_DW); -+ AuDbgDentry(dentry); -+ if (unlikely(err)) -+ goto out; -+out_no_open: -+ if (!err && !(*opened & FILE_CREATED)) { -+ AuLabel(out_no_open); -+ dget(dentry); -+ err = finish_no_open(file, dentry); -+ } -+out: -+ AuDbg("%pd%s%s\n", dentry, -+ (*opened & FILE_CREATED) ? " created" : "", -+ (*opened & FILE_OPENED) ? " opened" : ""); -+ AuTraceErr(err); -+ return err; -+} -+ -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_wr_dir_cpup(struct dentry *dentry, struct dentry *parent, -+ const unsigned char add_entry, aufs_bindex_t bcpup, -+ aufs_bindex_t bstart) -+{ -+ int err; -+ struct dentry *h_parent; -+ struct inode *h_dir; -+ -+ if (add_entry) -+ IMustLock(parent->d_inode); -+ else -+ di_write_lock_parent(parent); -+ -+ err = 0; -+ if (!au_h_dptr(parent, bcpup)) { -+ if (bstart > bcpup) -+ err = au_cpup_dirs(dentry, bcpup); -+ else if (bstart < bcpup) -+ err = au_cpdown_dirs(dentry, bcpup); -+ else -+ BUG(); -+ } -+ if (!err && add_entry && !au_ftest_wrdir(add_entry, TMPFILE)) { -+ h_parent = au_h_dptr(parent, bcpup); -+ h_dir = h_parent->d_inode; -+ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); -+ err = au_lkup_neg(dentry, bcpup, /*wh*/0); -+ /* todo: no unlock here */ -+ mutex_unlock(&h_dir->i_mutex); -+ -+ AuDbg("bcpup %d\n", bcpup); -+ if (!err) { -+ if (!dentry->d_inode) -+ au_set_h_dptr(dentry, bstart, NULL); -+ au_update_dbrange(dentry, /*do_put_zero*/0); -+ } -+ } -+ -+ if (!add_entry) -+ di_write_unlock(parent); -+ if (!err) -+ err = bcpup; /* success */ -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+/* -+ * decide the branch and the parent dir where we will create a new entry. -+ * returns new bindex or an error. -+ * copyup the parent dir if needed. -+ */ -+int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry, -+ struct au_wr_dir_args *args) -+{ -+ int err; -+ unsigned int flags; -+ aufs_bindex_t bcpup, bstart, src_bstart; -+ const unsigned char add_entry -+ = au_ftest_wrdir(args->flags, ADD_ENTRY) -+ | au_ftest_wrdir(args->flags, TMPFILE); -+ struct super_block *sb; -+ struct dentry *parent; -+ struct au_sbinfo *sbinfo; -+ -+ sb = dentry->d_sb; -+ sbinfo = au_sbi(sb); -+ parent = dget_parent(dentry); -+ bstart = au_dbstart(dentry); -+ bcpup = bstart; -+ if (args->force_btgt < 0) { -+ if (src_dentry) { -+ src_bstart = au_dbstart(src_dentry); -+ if (src_bstart < bstart) -+ bcpup = src_bstart; -+ } else if (add_entry) { -+ flags = 0; -+ if (au_ftest_wrdir(args->flags, ISDIR)) -+ au_fset_wbr(flags, DIR); -+ err = AuWbrCreate(sbinfo, dentry, flags); -+ bcpup = err; -+ } -+ -+ if (bcpup < 0 || au_test_ro(sb, bcpup, dentry->d_inode)) { -+ if (add_entry) -+ err = AuWbrCopyup(sbinfo, dentry); -+ else { -+ if (!IS_ROOT(dentry)) { -+ di_read_lock_parent(parent, !AuLock_IR); -+ err = AuWbrCopyup(sbinfo, dentry); -+ di_read_unlock(parent, !AuLock_IR); -+ } else -+ err = AuWbrCopyup(sbinfo, dentry); -+ } -+ bcpup = err; -+ if (unlikely(err < 0)) -+ goto out; -+ } -+ } else { -+ bcpup = args->force_btgt; -+ AuDebugOn(au_test_ro(sb, bcpup, dentry->d_inode)); -+ } -+ -+ AuDbg("bstart %d, bcpup %d\n", bstart, bcpup); -+ err = bcpup; -+ if (bcpup == bstart) -+ goto out; /* success */ -+ -+ /* copyup the new parent into the branch we process */ -+ err = au_wr_dir_cpup(dentry, parent, add_entry, bcpup, bstart); -+ if (err >= 0) { -+ if (!dentry->d_inode) { -+ au_set_h_dptr(dentry, bstart, NULL); -+ au_set_dbstart(dentry, bcpup); -+ au_set_dbend(dentry, bcpup); -+ } -+ AuDebugOn(add_entry -+ && !au_ftest_wrdir(args->flags, TMPFILE) -+ && !au_h_dptr(dentry, bcpup)); -+ } -+ -+out: -+ dput(parent); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void au_pin_hdir_unlock(struct au_pin *p) -+{ -+ if (p->hdir) -+ au_hn_imtx_unlock(p->hdir); -+} -+ -+int au_pin_hdir_lock(struct au_pin *p) -+{ -+ int err; -+ -+ err = 0; -+ if (!p->hdir) -+ goto out; -+ -+ /* even if an error happens later, keep this lock */ -+ au_hn_imtx_lock_nested(p->hdir, p->lsc_hi); -+ -+ err = -EBUSY; -+ if (unlikely(p->hdir->hi_inode != p->h_parent->d_inode)) -+ goto out; -+ -+ err = 0; -+ if (p->h_dentry) -+ err = au_h_verify(p->h_dentry, p->udba, p->hdir->hi_inode, -+ p->h_parent, p->br); -+ -+out: -+ return err; -+} -+ -+int au_pin_hdir_relock(struct au_pin *p) -+{ -+ int err, i; -+ struct inode *h_i; -+ struct dentry *h_d[] = { -+ p->h_dentry, -+ p->h_parent -+ }; -+ -+ err = au_pin_hdir_lock(p); -+ if (unlikely(err)) -+ goto out; -+ -+ for (i = 0; !err && i < sizeof(h_d)/sizeof(*h_d); i++) { -+ if (!h_d[i]) -+ continue; -+ h_i = h_d[i]->d_inode; -+ if (h_i) -+ err = !h_i->i_nlink; -+ } -+ -+out: -+ return err; -+} -+ -+void au_pin_hdir_set_owner(struct au_pin *p, struct task_struct *task) -+{ -+#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_SMP) -+ p->hdir->hi_inode->i_mutex.owner = task; -+#endif -+} -+ -+void au_pin_hdir_acquire_nest(struct au_pin *p) -+{ -+ if (p->hdir) { -+ mutex_acquire_nest(&p->hdir->hi_inode->i_mutex.dep_map, -+ p->lsc_hi, 0, NULL, _RET_IP_); -+ au_pin_hdir_set_owner(p, current); -+ } -+} -+ -+void au_pin_hdir_release(struct au_pin *p) -+{ -+ if (p->hdir) { -+ au_pin_hdir_set_owner(p, p->task); -+ mutex_release(&p->hdir->hi_inode->i_mutex.dep_map, 1, _RET_IP_); -+ } -+} -+ -+struct dentry *au_pinned_h_parent(struct au_pin *pin) -+{ -+ if (pin && pin->parent) -+ return au_h_dptr(pin->parent, pin->bindex); -+ return NULL; -+} -+ -+void au_unpin(struct au_pin *p) -+{ -+ if (p->hdir) -+ au_pin_hdir_unlock(p); -+ if (p->h_mnt && au_ftest_pin(p->flags, MNT_WRITE)) -+ vfsub_mnt_drop_write(p->h_mnt); -+ if (!p->hdir) -+ return; -+ -+ if (!au_ftest_pin(p->flags, DI_LOCKED)) -+ di_read_unlock(p->parent, AuLock_IR); -+ iput(p->hdir->hi_inode); -+ dput(p->parent); -+ p->parent = NULL; -+ p->hdir = NULL; -+ p->h_mnt = NULL; -+ /* do not clear p->task */ -+} -+ -+int au_do_pin(struct au_pin *p) -+{ -+ int err; -+ struct super_block *sb; -+ struct inode *h_dir; -+ -+ err = 0; -+ sb = p->dentry->d_sb; -+ p->br = au_sbr(sb, p->bindex); -+ if (IS_ROOT(p->dentry)) { -+ if (au_ftest_pin(p->flags, MNT_WRITE)) { -+ p->h_mnt = au_br_mnt(p->br); -+ err = vfsub_mnt_want_write(p->h_mnt); -+ if (unlikely(err)) { -+ au_fclr_pin(p->flags, MNT_WRITE); -+ goto out_err; -+ } -+ } -+ goto out; -+ } -+ -+ p->h_dentry = NULL; -+ if (p->bindex <= au_dbend(p->dentry)) -+ p->h_dentry = au_h_dptr(p->dentry, p->bindex); -+ -+ p->parent = dget_parent(p->dentry); -+ if (!au_ftest_pin(p->flags, DI_LOCKED)) -+ di_read_lock(p->parent, AuLock_IR, p->lsc_di); -+ -+ h_dir = NULL; -+ p->h_parent = au_h_dptr(p->parent, p->bindex); -+ p->hdir = au_hi(p->parent->d_inode, p->bindex); -+ if (p->hdir) -+ h_dir = p->hdir->hi_inode; -+ -+ /* -+ * udba case, or -+ * if DI_LOCKED is not set, then p->parent may be different -+ * and h_parent can be NULL. -+ */ -+ if (unlikely(!p->hdir || !h_dir || !p->h_parent)) { -+ err = -EBUSY; -+ if (!au_ftest_pin(p->flags, DI_LOCKED)) -+ di_read_unlock(p->parent, AuLock_IR); -+ dput(p->parent); -+ p->parent = NULL; -+ goto out_err; -+ } -+ -+ if (au_ftest_pin(p->flags, MNT_WRITE)) { -+ p->h_mnt = au_br_mnt(p->br); -+ err = vfsub_mnt_want_write(p->h_mnt); -+ if (unlikely(err)) { -+ au_fclr_pin(p->flags, MNT_WRITE); -+ if (!au_ftest_pin(p->flags, DI_LOCKED)) -+ di_read_unlock(p->parent, AuLock_IR); -+ dput(p->parent); -+ p->parent = NULL; -+ goto out_err; -+ } -+ } -+ -+ au_igrab(h_dir); -+ err = au_pin_hdir_lock(p); -+ if (!err) -+ goto out; /* success */ -+ -+ au_unpin(p); -+ -+out_err: -+ pr_err("err %d\n", err); -+ err = au_busy_or_stale(); -+out: -+ return err; -+} -+ -+void au_pin_init(struct au_pin *p, struct dentry *dentry, -+ aufs_bindex_t bindex, int lsc_di, int lsc_hi, -+ unsigned int udba, unsigned char flags) -+{ -+ p->dentry = dentry; -+ p->udba = udba; -+ p->lsc_di = lsc_di; -+ p->lsc_hi = lsc_hi; -+ p->flags = flags; -+ p->bindex = bindex; -+ -+ p->parent = NULL; -+ p->hdir = NULL; -+ p->h_mnt = NULL; -+ -+ p->h_dentry = NULL; -+ p->h_parent = NULL; -+ p->br = NULL; -+ p->task = current; -+} -+ -+int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex, -+ unsigned int udba, unsigned char flags) -+{ -+ au_pin_init(pin, dentry, bindex, AuLsc_DI_PARENT, AuLsc_I_PARENT2, -+ udba, flags); -+ return au_do_pin(pin); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * ->setattr() and ->getattr() are called in various cases. -+ * chmod, stat: dentry is revalidated. -+ * fchmod, fstat: file and dentry are not revalidated, additionally they may be -+ * unhashed. -+ * for ->setattr(), ia->ia_file is passed from ftruncate only. -+ */ -+/* todo: consolidate with do_refresh() and simple_reval_dpath() */ -+int au_reval_for_attr(struct dentry *dentry, unsigned int sigen) -+{ -+ int err; -+ struct inode *inode; -+ struct dentry *parent; -+ -+ err = 0; -+ inode = dentry->d_inode; -+ if (au_digen_test(dentry, sigen)) { -+ parent = dget_parent(dentry); -+ di_read_lock_parent(parent, AuLock_IR); -+ err = au_refresh_dentry(dentry, parent); -+ di_read_unlock(parent, AuLock_IR); -+ dput(parent); -+ } -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia, -+ struct au_icpup_args *a) -+{ -+ int err; -+ loff_t sz; -+ aufs_bindex_t bstart, ibstart; -+ struct dentry *hi_wh, *parent; -+ struct inode *inode; -+ struct au_wr_dir_args wr_dir_args = { -+ .force_btgt = -1, -+ .flags = 0 -+ }; -+ -+ if (d_is_dir(dentry)) -+ au_fset_wrdir(wr_dir_args.flags, ISDIR); -+ /* plink or hi_wh() case */ -+ bstart = au_dbstart(dentry); -+ inode = dentry->d_inode; -+ ibstart = au_ibstart(inode); -+ if (bstart != ibstart && !au_test_ro(inode->i_sb, ibstart, inode)) -+ wr_dir_args.force_btgt = ibstart; -+ err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args); -+ if (unlikely(err < 0)) -+ goto out; -+ a->btgt = err; -+ if (err != bstart) -+ au_fset_icpup(a->flags, DID_CPUP); -+ -+ err = 0; -+ a->pin_flags = AuPin_MNT_WRITE; -+ parent = NULL; -+ if (!IS_ROOT(dentry)) { -+ au_fset_pin(a->pin_flags, DI_LOCKED); -+ parent = dget_parent(dentry); -+ di_write_lock_parent(parent); -+ } -+ -+ err = au_pin(&a->pin, dentry, a->btgt, a->udba, a->pin_flags); -+ if (unlikely(err)) -+ goto out_parent; -+ -+ a->h_path.dentry = au_h_dptr(dentry, bstart); -+ a->h_inode = a->h_path.dentry->d_inode; -+ sz = -1; -+ if (ia && (ia->ia_valid & ATTR_SIZE)) { -+ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); -+ if (ia->ia_size < i_size_read(a->h_inode)) -+ sz = ia->ia_size; -+ mutex_unlock(&a->h_inode->i_mutex); -+ } -+ -+ hi_wh = NULL; -+ if (au_ftest_icpup(a->flags, DID_CPUP) && d_unlinked(dentry)) { -+ hi_wh = au_hi_wh(inode, a->btgt); -+ if (!hi_wh) { -+ struct au_cp_generic cpg = { -+ .dentry = dentry, -+ .bdst = a->btgt, -+ .bsrc = -1, -+ .len = sz, -+ .pin = &a->pin -+ }; -+ err = au_sio_cpup_wh(&cpg, /*file*/NULL); -+ if (unlikely(err)) -+ goto out_unlock; -+ hi_wh = au_hi_wh(inode, a->btgt); -+ /* todo: revalidate hi_wh? */ -+ } -+ } -+ -+ if (parent) { -+ au_pin_set_parent_lflag(&a->pin, /*lflag*/0); -+ di_downgrade_lock(parent, AuLock_IR); -+ dput(parent); -+ parent = NULL; -+ } -+ if (!au_ftest_icpup(a->flags, DID_CPUP)) -+ goto out; /* success */ -+ -+ if (!d_unhashed(dentry)) { -+ struct au_cp_generic cpg = { -+ .dentry = dentry, -+ .bdst = a->btgt, -+ .bsrc = bstart, -+ .len = sz, -+ .pin = &a->pin, -+ .flags = AuCpup_DTIME | AuCpup_HOPEN -+ }; -+ err = au_sio_cpup_simple(&cpg); -+ if (!err) -+ a->h_path.dentry = au_h_dptr(dentry, a->btgt); -+ } else if (!hi_wh) -+ a->h_path.dentry = au_h_dptr(dentry, a->btgt); -+ else -+ a->h_path.dentry = hi_wh; /* do not dget here */ -+ -+out_unlock: -+ a->h_inode = a->h_path.dentry->d_inode; -+ if (!err) -+ goto out; /* success */ -+ au_unpin(&a->pin); -+out_parent: -+ if (parent) { -+ di_write_unlock(parent); -+ dput(parent); -+ } -+out: -+ if (!err) -+ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); -+ return err; -+} -+ -+static int aufs_setattr(struct dentry *dentry, struct iattr *ia) -+{ -+ int err; -+ struct inode *inode, *delegated; -+ struct super_block *sb; -+ struct file *file; -+ struct au_icpup_args *a; -+ -+ inode = dentry->d_inode; -+ IMustLock(inode); -+ -+ err = -ENOMEM; -+ a = kzalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ -+ if (ia->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) -+ ia->ia_valid &= ~ATTR_MODE; -+ -+ file = NULL; -+ sb = dentry->d_sb; -+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ if (unlikely(err)) -+ goto out_kfree; -+ -+ if (ia->ia_valid & ATTR_FILE) { -+ /* currently ftruncate(2) only */ -+ AuDebugOn(!S_ISREG(inode->i_mode)); -+ file = ia->ia_file; -+ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); -+ if (unlikely(err)) -+ goto out_si; -+ ia->ia_file = au_hf_top(file); -+ a->udba = AuOpt_UDBA_NONE; -+ } else { -+ /* fchmod() doesn't pass ia_file */ -+ a->udba = au_opt_udba(sb); -+ di_write_lock_child(dentry); -+ /* no d_unlinked(), to set UDBA_NONE for root */ -+ if (d_unhashed(dentry)) -+ a->udba = AuOpt_UDBA_NONE; -+ if (a->udba != AuOpt_UDBA_NONE) { -+ AuDebugOn(IS_ROOT(dentry)); -+ err = au_reval_for_attr(dentry, au_sigen(sb)); -+ if (unlikely(err)) -+ goto out_dentry; -+ } -+ } -+ -+ err = au_pin_and_icpup(dentry, ia, a); -+ if (unlikely(err < 0)) -+ goto out_dentry; -+ if (au_ftest_icpup(a->flags, DID_CPUP)) { -+ ia->ia_file = NULL; -+ ia->ia_valid &= ~ATTR_FILE; -+ } -+ -+ a->h_path.mnt = au_sbr_mnt(sb, a->btgt); -+ if ((ia->ia_valid & (ATTR_MODE | ATTR_CTIME)) -+ == (ATTR_MODE | ATTR_CTIME)) { -+ err = security_path_chmod(&a->h_path, ia->ia_mode); -+ if (unlikely(err)) -+ goto out_unlock; -+ } else if ((ia->ia_valid & (ATTR_UID | ATTR_GID)) -+ && (ia->ia_valid & ATTR_CTIME)) { -+ err = security_path_chown(&a->h_path, ia->ia_uid, ia->ia_gid); -+ if (unlikely(err)) -+ goto out_unlock; -+ } -+ -+ if (ia->ia_valid & ATTR_SIZE) { -+ struct file *f; -+ -+ if (ia->ia_size < i_size_read(inode)) -+ /* unmap only */ -+ truncate_setsize(inode, ia->ia_size); -+ -+ f = NULL; -+ if (ia->ia_valid & ATTR_FILE) -+ f = ia->ia_file; -+ mutex_unlock(&a->h_inode->i_mutex); -+ err = vfsub_trunc(&a->h_path, ia->ia_size, ia->ia_valid, f); -+ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); -+ } else { -+ delegated = NULL; -+ while (1) { -+ err = vfsub_notify_change(&a->h_path, ia, &delegated); -+ if (delegated) { -+ err = break_deleg_wait(&delegated); -+ if (!err) -+ continue; -+ } -+ break; -+ } -+ } -+ /* -+ * regardless aufs 'acl' option setting. -+ * why don't all acl-aware fs call this func from their ->setattr()? -+ */ -+ if (!err && (ia->ia_valid & ATTR_MODE)) -+ err = vfsub_acl_chmod(a->h_inode, ia->ia_mode); -+ if (!err) -+ au_cpup_attr_changeable(inode); -+ -+out_unlock: -+ mutex_unlock(&a->h_inode->i_mutex); -+ au_unpin(&a->pin); -+ if (unlikely(err)) -+ au_update_dbstart(dentry); -+out_dentry: -+ di_write_unlock(dentry); -+ if (file) { -+ fi_write_unlock(file); -+ ia->ia_file = file; -+ ia->ia_valid |= ATTR_FILE; -+ } -+out_si: -+ si_read_unlock(sb); -+out_kfree: -+ kfree(a); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+#if IS_ENABLED(CONFIG_AUFS_XATTR) || IS_ENABLED(CONFIG_FS_POSIX_ACL) -+static int au_h_path_to_set_attr(struct dentry *dentry, -+ struct au_icpup_args *a, struct path *h_path) -+{ -+ int err; -+ struct super_block *sb; -+ -+ sb = dentry->d_sb; -+ a->udba = au_opt_udba(sb); -+ /* no d_unlinked(), to set UDBA_NONE for root */ -+ if (d_unhashed(dentry)) -+ a->udba = AuOpt_UDBA_NONE; -+ if (a->udba != AuOpt_UDBA_NONE) { -+ AuDebugOn(IS_ROOT(dentry)); -+ err = au_reval_for_attr(dentry, au_sigen(sb)); -+ if (unlikely(err)) -+ goto out; -+ } -+ err = au_pin_and_icpup(dentry, /*ia*/NULL, a); -+ if (unlikely(err < 0)) -+ goto out; -+ -+ h_path->dentry = a->h_path.dentry; -+ h_path->mnt = au_sbr_mnt(sb, a->btgt); -+ -+out: -+ return err; -+} -+ -+ssize_t au_srxattr(struct dentry *dentry, struct au_srxattr *arg) -+{ -+ int err; -+ struct path h_path; -+ struct super_block *sb; -+ struct au_icpup_args *a; -+ struct inode *inode, *h_inode; -+ -+ inode = dentry->d_inode; -+ IMustLock(inode); -+ -+ err = -ENOMEM; -+ a = kzalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ -+ sb = dentry->d_sb; -+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ if (unlikely(err)) -+ goto out_kfree; -+ -+ h_path.dentry = NULL; /* silence gcc */ -+ di_write_lock_child(dentry); -+ err = au_h_path_to_set_attr(dentry, a, &h_path); -+ if (unlikely(err)) -+ goto out_di; -+ -+ mutex_unlock(&a->h_inode->i_mutex); -+ switch (arg->type) { -+ case AU_XATTR_SET: -+ err = vfsub_setxattr(h_path.dentry, -+ arg->u.set.name, arg->u.set.value, -+ arg->u.set.size, arg->u.set.flags); -+ break; -+ case AU_XATTR_REMOVE: -+ err = vfsub_removexattr(h_path.dentry, arg->u.remove.name); -+ break; -+ case AU_ACL_SET: -+ err = -EOPNOTSUPP; -+ h_inode = h_path.dentry->d_inode; -+ if (h_inode->i_op->set_acl) -+ err = h_inode->i_op->set_acl(h_inode, -+ arg->u.acl_set.acl, -+ arg->u.acl_set.type); -+ break; -+ } -+ if (!err) -+ au_cpup_attr_timesizes(inode); -+ -+ au_unpin(&a->pin); -+ if (unlikely(err)) -+ au_update_dbstart(dentry); -+ -+out_di: -+ di_write_unlock(dentry); -+ si_read_unlock(sb); -+out_kfree: -+ kfree(a); -+out: -+ AuTraceErr(err); -+ return err; -+} -+#endif -+ -+static void au_refresh_iattr(struct inode *inode, struct kstat *st, -+ unsigned int nlink) -+{ -+ unsigned int n; -+ -+ inode->i_mode = st->mode; -+ /* don't i_[ug]id_write() here */ -+ inode->i_uid = st->uid; -+ inode->i_gid = st->gid; -+ inode->i_atime = st->atime; -+ inode->i_mtime = st->mtime; -+ inode->i_ctime = st->ctime; -+ -+ au_cpup_attr_nlink(inode, /*force*/0); -+ if (S_ISDIR(inode->i_mode)) { -+ n = inode->i_nlink; -+ n -= nlink; -+ n += st->nlink; -+ smp_mb(); /* for i_nlink */ -+ /* 0 can happen */ -+ set_nlink(inode, n); -+ } -+ -+ spin_lock(&inode->i_lock); -+ inode->i_blocks = st->blocks; -+ i_size_write(inode, st->size); -+ spin_unlock(&inode->i_lock); -+} -+ -+/* -+ * common routine for aufs_getattr() and aufs_getxattr(). -+ * returns zero or negative (an error). -+ * @dentry will be read-locked in success. -+ */ -+int au_h_path_getattr(struct dentry *dentry, int force, struct path *h_path) -+{ -+ int err; -+ unsigned int mnt_flags, sigen; -+ unsigned char udba_none; -+ aufs_bindex_t bindex; -+ struct super_block *sb, *h_sb; -+ struct inode *inode; -+ -+ h_path->mnt = NULL; -+ h_path->dentry = NULL; -+ -+ err = 0; -+ sb = dentry->d_sb; -+ mnt_flags = au_mntflags(sb); -+ udba_none = !!au_opt_test(mnt_flags, UDBA_NONE); -+ -+ /* support fstat(2) */ -+ if (!d_unlinked(dentry) && !udba_none) { -+ sigen = au_sigen(sb); -+ err = au_digen_test(dentry, sigen); -+ if (!err) { -+ di_read_lock_child(dentry, AuLock_IR); -+ err = au_dbrange_test(dentry); -+ if (unlikely(err)) { -+ di_read_unlock(dentry, AuLock_IR); -+ goto out; -+ } -+ } else { -+ AuDebugOn(IS_ROOT(dentry)); -+ di_write_lock_child(dentry); -+ err = au_dbrange_test(dentry); -+ if (!err) -+ err = au_reval_for_attr(dentry, sigen); -+ if (!err) -+ di_downgrade_lock(dentry, AuLock_IR); -+ else { -+ di_write_unlock(dentry); -+ goto out; -+ } -+ } -+ } else -+ di_read_lock_child(dentry, AuLock_IR); -+ -+ inode = dentry->d_inode; -+ bindex = au_ibstart(inode); -+ h_path->mnt = au_sbr_mnt(sb, bindex); -+ h_sb = h_path->mnt->mnt_sb; -+ if (!force -+ && !au_test_fs_bad_iattr(h_sb) -+ && udba_none) -+ goto out; /* success */ -+ -+ if (au_dbstart(dentry) == bindex) -+ h_path->dentry = au_h_dptr(dentry, bindex); -+ else if (au_opt_test(mnt_flags, PLINK) && au_plink_test(inode)) { -+ h_path->dentry = au_plink_lkup(inode, bindex); -+ if (IS_ERR(h_path->dentry)) -+ /* pretending success */ -+ h_path->dentry = NULL; -+ else -+ dput(h_path->dentry); -+ } -+ -+out: -+ return err; -+} -+ -+static int aufs_getattr(struct vfsmount *mnt __maybe_unused, -+ struct dentry *dentry, struct kstat *st) -+{ -+ int err; -+ unsigned char positive; -+ struct path h_path; -+ struct inode *inode; -+ struct super_block *sb; -+ -+ inode = dentry->d_inode; -+ sb = dentry->d_sb; -+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ if (unlikely(err)) -+ goto out; -+ err = au_h_path_getattr(dentry, /*force*/0, &h_path); -+ if (unlikely(err)) -+ goto out_si; -+ if (unlikely(!h_path.dentry)) -+ /* illegally overlapped or something */ -+ goto out_fill; /* pretending success */ -+ -+ positive = !!h_path.dentry->d_inode; -+ if (positive) -+ err = vfs_getattr(&h_path, st); -+ if (!err) { -+ if (positive) -+ au_refresh_iattr(inode, st, -+ h_path.dentry->d_inode->i_nlink); -+ goto out_fill; /* success */ -+ } -+ AuTraceErr(err); -+ goto out_di; -+ -+out_fill: -+ generic_fillattr(inode, st); -+out_di: -+ di_read_unlock(dentry, AuLock_IR); -+out_si: -+ si_read_unlock(sb); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int h_readlink(struct dentry *dentry, int bindex, char __user *buf, -+ int bufsiz) -+{ -+ int err; -+ struct super_block *sb; -+ struct dentry *h_dentry; -+ -+ err = -EINVAL; -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (unlikely(!h_dentry->d_inode->i_op->readlink)) -+ goto out; -+ -+ err = security_inode_readlink(h_dentry); -+ if (unlikely(err)) -+ goto out; -+ -+ sb = dentry->d_sb; -+ if (!au_test_ro(sb, bindex, dentry->d_inode)) { -+ vfsub_touch_atime(au_sbr_mnt(sb, bindex), h_dentry); -+ fsstack_copy_attr_atime(dentry->d_inode, h_dentry->d_inode); -+ } -+ err = h_dentry->d_inode->i_op->readlink(h_dentry, buf, bufsiz); -+ -+out: -+ return err; -+} -+ -+static int aufs_readlink(struct dentry *dentry, char __user *buf, int bufsiz) -+{ -+ int err; -+ -+ err = aufs_read_lock(dentry, AuLock_IR | AuLock_GEN); -+ if (unlikely(err)) -+ goto out; -+ err = au_d_hashed_positive(dentry); -+ if (!err) -+ err = h_readlink(dentry, au_dbstart(dentry), buf, bufsiz); -+ aufs_read_unlock(dentry, AuLock_IR); -+ -+out: -+ return err; -+} -+ -+static void *aufs_follow_link(struct dentry *dentry, struct nameidata *nd) -+{ -+ int err; -+ mm_segment_t old_fs; -+ union { -+ char *k; -+ char __user *u; -+ } buf; -+ -+ err = -ENOMEM; -+ buf.k = (void *)__get_free_page(GFP_NOFS); -+ if (unlikely(!buf.k)) -+ goto out; -+ -+ err = aufs_read_lock(dentry, AuLock_IR | AuLock_GEN); -+ if (unlikely(err)) -+ goto out_name; -+ -+ err = au_d_hashed_positive(dentry); -+ if (!err) { -+ old_fs = get_fs(); -+ set_fs(KERNEL_DS); -+ err = h_readlink(dentry, au_dbstart(dentry), buf.u, PATH_MAX); -+ set_fs(old_fs); -+ } -+ aufs_read_unlock(dentry, AuLock_IR); -+ -+ if (err >= 0) { -+ buf.k[err] = 0; -+ /* will be freed by put_link */ -+ nd_set_link(nd, buf.k); -+ return NULL; /* success */ -+ } -+ -+out_name: -+ free_page((unsigned long)buf.k); -+out: -+ AuTraceErr(err); -+ return ERR_PTR(err); -+} -+ -+static void aufs_put_link(struct dentry *dentry __maybe_unused, -+ struct nameidata *nd, void *cookie __maybe_unused) -+{ -+ char *p; -+ -+ p = nd_get_link(nd); -+ if (!IS_ERR_OR_NULL(p)) -+ free_page((unsigned long)p); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int aufs_update_time(struct inode *inode, struct timespec *ts, int flags) -+{ -+ int err; -+ struct super_block *sb; -+ struct inode *h_inode; -+ -+ sb = inode->i_sb; -+ /* mmap_sem might be acquired already, cf. aufs_mmap() */ -+ lockdep_off(); -+ si_read_lock(sb, AuLock_FLUSH); -+ ii_write_lock_child(inode); -+ lockdep_on(); -+ h_inode = au_h_iptr(inode, au_ibstart(inode)); -+ err = vfsub_update_time(h_inode, ts, flags); -+ lockdep_off(); -+ if (!err) -+ au_cpup_attr_timesizes(inode); -+ ii_write_unlock(inode); -+ si_read_unlock(sb); -+ lockdep_on(); -+ -+ if (!err && (flags & S_VERSION)) -+ inode_inc_iversion(inode); -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* no getattr version will be set by module.c:aufs_init() */ -+struct inode_operations aufs_iop_nogetattr[AuIop_Last], -+ aufs_iop[] = { -+ [AuIop_SYMLINK] = { -+ .permission = aufs_permission, -+#ifdef CONFIG_FS_POSIX_ACL -+ .get_acl = aufs_get_acl, -+ .set_acl = aufs_set_acl, /* unsupport for symlink? */ -+#endif -+ -+ .setattr = aufs_setattr, -+ .getattr = aufs_getattr, -+ -+#ifdef CONFIG_AUFS_XATTR -+ .setxattr = aufs_setxattr, -+ .getxattr = aufs_getxattr, -+ .listxattr = aufs_listxattr, -+ .removexattr = aufs_removexattr, -+#endif -+ -+ .readlink = aufs_readlink, -+ .follow_link = aufs_follow_link, -+ .put_link = aufs_put_link, -+ -+ /* .update_time = aufs_update_time */ -+ }, -+ [AuIop_DIR] = { -+ .create = aufs_create, -+ .lookup = aufs_lookup, -+ .link = aufs_link, -+ .unlink = aufs_unlink, -+ .symlink = aufs_symlink, -+ .mkdir = aufs_mkdir, -+ .rmdir = aufs_rmdir, -+ .mknod = aufs_mknod, -+ .rename = aufs_rename, -+ -+ .permission = aufs_permission, -+#ifdef CONFIG_FS_POSIX_ACL -+ .get_acl = aufs_get_acl, -+ .set_acl = aufs_set_acl, -+#endif -+ -+ .setattr = aufs_setattr, -+ .getattr = aufs_getattr, -+ -+#ifdef CONFIG_AUFS_XATTR -+ .setxattr = aufs_setxattr, -+ .getxattr = aufs_getxattr, -+ .listxattr = aufs_listxattr, -+ .removexattr = aufs_removexattr, -+#endif -+ -+ .update_time = aufs_update_time, -+ .atomic_open = aufs_atomic_open, -+ .tmpfile = aufs_tmpfile -+ }, -+ [AuIop_OTHER] = { -+ .permission = aufs_permission, -+#ifdef CONFIG_FS_POSIX_ACL -+ .get_acl = aufs_get_acl, -+ .set_acl = aufs_set_acl, -+#endif -+ -+ .setattr = aufs_setattr, -+ .getattr = aufs_getattr, -+ -+#ifdef CONFIG_AUFS_XATTR -+ .setxattr = aufs_setxattr, -+ .getxattr = aufs_getxattr, -+ .listxattr = aufs_listxattr, -+ .removexattr = aufs_removexattr, -+#endif -+ -+ .update_time = aufs_update_time -+ } -+}; -diff --git a/fs/aufs/i_op_add.c b/fs/aufs/i_op_add.c -new file mode 100644 -index 0000000..9e4f65c ---- /dev/null -+++ b/fs/aufs/i_op_add.c -@@ -0,0 +1,930 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * inode operations (add entry) -+ */ -+ -+#include "aufs.h" -+ -+/* -+ * final procedure of adding a new entry, except link(2). -+ * remove whiteout, instantiate, copyup the parent dir's times and size -+ * and update version. -+ * if it failed, re-create the removed whiteout. -+ */ -+static int epilog(struct inode *dir, aufs_bindex_t bindex, -+ struct dentry *wh_dentry, struct dentry *dentry) -+{ -+ int err, rerr; -+ aufs_bindex_t bwh; -+ struct path h_path; -+ struct super_block *sb; -+ struct inode *inode, *h_dir; -+ struct dentry *wh; -+ -+ bwh = -1; -+ sb = dir->i_sb; -+ if (wh_dentry) { -+ h_dir = wh_dentry->d_parent->d_inode; /* dir inode is locked */ -+ IMustLock(h_dir); -+ AuDebugOn(au_h_iptr(dir, bindex) != h_dir); -+ bwh = au_dbwh(dentry); -+ h_path.dentry = wh_dentry; -+ h_path.mnt = au_sbr_mnt(sb, bindex); -+ err = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path, -+ dentry); -+ if (unlikely(err)) -+ goto out; -+ } -+ -+ inode = au_new_inode(dentry, /*must_new*/1); -+ if (!IS_ERR(inode)) { -+ d_instantiate(dentry, inode); -+ dir = dentry->d_parent->d_inode; /* dir inode is locked */ -+ IMustLock(dir); -+ au_dir_ts(dir, bindex); -+ dir->i_version++; -+ au_fhsm_wrote(sb, bindex, /*force*/0); -+ return 0; /* success */ -+ } -+ -+ err = PTR_ERR(inode); -+ if (!wh_dentry) -+ goto out; -+ -+ /* revert */ -+ /* dir inode is locked */ -+ wh = au_wh_create(dentry, bwh, wh_dentry->d_parent); -+ rerr = PTR_ERR(wh); -+ if (IS_ERR(wh)) { -+ AuIOErr("%pd reverting whiteout failed(%d, %d)\n", -+ dentry, err, rerr); -+ err = -EIO; -+ } else -+ dput(wh); -+ -+out: -+ return err; -+} -+ -+static int au_d_may_add(struct dentry *dentry) -+{ -+ int err; -+ -+ err = 0; -+ if (unlikely(d_unhashed(dentry))) -+ err = -ENOENT; -+ if (unlikely(dentry->d_inode)) -+ err = -EEXIST; -+ return err; -+} -+ -+/* -+ * simple tests for the adding inode operations. -+ * following the checks in vfs, plus the parent-child relationship. -+ */ -+int au_may_add(struct dentry *dentry, aufs_bindex_t bindex, -+ struct dentry *h_parent, int isdir) -+{ -+ int err; -+ umode_t h_mode; -+ struct dentry *h_dentry; -+ struct inode *h_inode; -+ -+ err = -ENAMETOOLONG; -+ if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) -+ goto out; -+ -+ h_dentry = au_h_dptr(dentry, bindex); -+ h_inode = h_dentry->d_inode; -+ if (!dentry->d_inode) { -+ err = -EEXIST; -+ if (unlikely(h_inode)) -+ goto out; -+ } else { -+ /* rename(2) case */ -+ err = -EIO; -+ if (unlikely(!h_inode || !h_inode->i_nlink)) -+ goto out; -+ -+ h_mode = h_inode->i_mode; -+ if (!isdir) { -+ err = -EISDIR; -+ if (unlikely(S_ISDIR(h_mode))) -+ goto out; -+ } else if (unlikely(!S_ISDIR(h_mode))) { -+ err = -ENOTDIR; -+ goto out; -+ } -+ } -+ -+ err = 0; -+ /* expected parent dir is locked */ -+ if (unlikely(h_parent != h_dentry->d_parent)) -+ err = -EIO; -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* -+ * initial procedure of adding a new entry. -+ * prepare writable branch and the parent dir, lock it, -+ * and lookup whiteout for the new entry. -+ */ -+static struct dentry* -+lock_hdir_lkup_wh(struct dentry *dentry, struct au_dtime *dt, -+ struct dentry *src_dentry, struct au_pin *pin, -+ struct au_wr_dir_args *wr_dir_args) -+{ -+ struct dentry *wh_dentry, *h_parent; -+ struct super_block *sb; -+ struct au_branch *br; -+ int err; -+ unsigned int udba; -+ aufs_bindex_t bcpup; -+ -+ AuDbg("%pd\n", dentry); -+ -+ err = au_wr_dir(dentry, src_dentry, wr_dir_args); -+ bcpup = err; -+ wh_dentry = ERR_PTR(err); -+ if (unlikely(err < 0)) -+ goto out; -+ -+ sb = dentry->d_sb; -+ udba = au_opt_udba(sb); -+ err = au_pin(pin, dentry, bcpup, udba, -+ AuPin_DI_LOCKED | AuPin_MNT_WRITE); -+ wh_dentry = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out; -+ -+ h_parent = au_pinned_h_parent(pin); -+ if (udba != AuOpt_UDBA_NONE -+ && au_dbstart(dentry) == bcpup) -+ err = au_may_add(dentry, bcpup, h_parent, -+ au_ftest_wrdir(wr_dir_args->flags, ISDIR)); -+ else if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) -+ err = -ENAMETOOLONG; -+ wh_dentry = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out_unpin; -+ -+ br = au_sbr(sb, bcpup); -+ if (dt) { -+ struct path tmp = { -+ .dentry = h_parent, -+ .mnt = au_br_mnt(br) -+ }; -+ au_dtime_store(dt, au_pinned_parent(pin), &tmp); -+ } -+ -+ wh_dentry = NULL; -+ if (bcpup != au_dbwh(dentry)) -+ goto out; /* success */ -+ -+ /* -+ * ENAMETOOLONG here means that if we allowed create such name, then it -+ * would not be able to removed in the future. So we don't allow such -+ * name here and we don't handle ENAMETOOLONG differently here. -+ */ -+ wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, br); -+ -+out_unpin: -+ if (IS_ERR(wh_dentry)) -+ au_unpin(pin); -+out: -+ return wh_dentry; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+enum { Mknod, Symlink, Creat }; -+struct simple_arg { -+ int type; -+ union { -+ struct { -+ umode_t mode; -+ bool want_excl; -+ bool try_aopen; -+ struct vfsub_aopen_args *aopen; -+ } c; -+ struct { -+ const char *symname; -+ } s; -+ struct { -+ umode_t mode; -+ dev_t dev; -+ } m; -+ } u; -+}; -+ -+static int add_simple(struct inode *dir, struct dentry *dentry, -+ struct simple_arg *arg) -+{ -+ int err, rerr; -+ aufs_bindex_t bstart; -+ unsigned char created; -+ const unsigned char try_aopen -+ = (arg->type == Creat && arg->u.c.try_aopen); -+ struct dentry *wh_dentry, *parent; -+ struct inode *h_dir; -+ struct super_block *sb; -+ struct au_branch *br; -+ /* to reuduce stack size */ -+ struct { -+ struct au_dtime dt; -+ struct au_pin pin; -+ struct path h_path; -+ struct au_wr_dir_args wr_dir_args; -+ } *a; -+ -+ AuDbg("%pd\n", dentry); -+ IMustLock(dir); -+ -+ err = -ENOMEM; -+ a = kmalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ a->wr_dir_args.force_btgt = -1; -+ a->wr_dir_args.flags = AuWrDir_ADD_ENTRY; -+ -+ parent = dentry->d_parent; /* dir inode is locked */ -+ if (!try_aopen) { -+ err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); -+ if (unlikely(err)) -+ goto out_free; -+ } -+ err = au_d_may_add(dentry); -+ if (unlikely(err)) -+ goto out_unlock; -+ if (!try_aopen) -+ di_write_lock_parent(parent); -+ wh_dentry = lock_hdir_lkup_wh(dentry, &a->dt, /*src_dentry*/NULL, -+ &a->pin, &a->wr_dir_args); -+ err = PTR_ERR(wh_dentry); -+ if (IS_ERR(wh_dentry)) -+ goto out_parent; -+ -+ bstart = au_dbstart(dentry); -+ sb = dentry->d_sb; -+ br = au_sbr(sb, bstart); -+ a->h_path.dentry = au_h_dptr(dentry, bstart); -+ a->h_path.mnt = au_br_mnt(br); -+ h_dir = au_pinned_h_dir(&a->pin); -+ switch (arg->type) { -+ case Creat: -+ err = 0; -+ if (!try_aopen || !h_dir->i_op->atomic_open) -+ err = vfsub_create(h_dir, &a->h_path, arg->u.c.mode, -+ arg->u.c.want_excl); -+ else -+ err = vfsub_atomic_open(h_dir, a->h_path.dentry, -+ arg->u.c.aopen, br); -+ break; -+ case Symlink: -+ err = vfsub_symlink(h_dir, &a->h_path, arg->u.s.symname); -+ break; -+ case Mknod: -+ err = vfsub_mknod(h_dir, &a->h_path, arg->u.m.mode, -+ arg->u.m.dev); -+ break; -+ default: -+ BUG(); -+ } -+ created = !err; -+ if (!err) -+ err = epilog(dir, bstart, wh_dentry, dentry); -+ -+ /* revert */ -+ if (unlikely(created && err && a->h_path.dentry->d_inode)) { -+ /* no delegation since it is just created */ -+ rerr = vfsub_unlink(h_dir, &a->h_path, /*delegated*/NULL, -+ /*force*/0); -+ if (rerr) { -+ AuIOErr("%pd revert failure(%d, %d)\n", -+ dentry, err, rerr); -+ err = -EIO; -+ } -+ au_dtime_revert(&a->dt); -+ } -+ -+ if (!err && try_aopen && !h_dir->i_op->atomic_open) -+ *arg->u.c.aopen->opened |= FILE_CREATED; -+ -+ au_unpin(&a->pin); -+ dput(wh_dentry); -+ -+out_parent: -+ if (!try_aopen) -+ di_write_unlock(parent); -+out_unlock: -+ if (unlikely(err)) { -+ au_update_dbstart(dentry); -+ d_drop(dentry); -+ } -+ if (!try_aopen) -+ aufs_read_unlock(dentry, AuLock_DW); -+out_free: -+ kfree(a); -+out: -+ return err; -+} -+ -+int aufs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, -+ dev_t dev) -+{ -+ struct simple_arg arg = { -+ .type = Mknod, -+ .u.m = { -+ .mode = mode, -+ .dev = dev -+ } -+ }; -+ return add_simple(dir, dentry, &arg); -+} -+ -+int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) -+{ -+ struct simple_arg arg = { -+ .type = Symlink, -+ .u.s.symname = symname -+ }; -+ return add_simple(dir, dentry, &arg); -+} -+ -+int aufs_create(struct inode *dir, struct dentry *dentry, umode_t mode, -+ bool want_excl) -+{ -+ struct simple_arg arg = { -+ .type = Creat, -+ .u.c = { -+ .mode = mode, -+ .want_excl = want_excl -+ } -+ }; -+ return add_simple(dir, dentry, &arg); -+} -+ -+int au_aopen_or_create(struct inode *dir, struct dentry *dentry, -+ struct vfsub_aopen_args *aopen_args) -+{ -+ struct simple_arg arg = { -+ .type = Creat, -+ .u.c = { -+ .mode = aopen_args->create_mode, -+ .want_excl = aopen_args->open_flag & O_EXCL, -+ .try_aopen = true, -+ .aopen = aopen_args -+ } -+ }; -+ return add_simple(dir, dentry, &arg); -+} -+ -+int aufs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) -+{ -+ int err; -+ aufs_bindex_t bindex; -+ struct super_block *sb; -+ struct dentry *parent, *h_parent, *h_dentry; -+ struct inode *h_dir, *inode; -+ struct vfsmount *h_mnt; -+ struct au_wr_dir_args wr_dir_args = { -+ .force_btgt = -1, -+ .flags = AuWrDir_TMPFILE -+ }; -+ -+ /* copy-up may happen */ -+ mutex_lock(&dir->i_mutex); -+ -+ sb = dir->i_sb; -+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ if (unlikely(err)) -+ goto out; -+ -+ err = au_di_init(dentry); -+ if (unlikely(err)) -+ goto out_si; -+ -+ err = -EBUSY; -+ parent = d_find_any_alias(dir); -+ AuDebugOn(!parent); -+ di_write_lock_parent(parent); -+ if (unlikely(parent->d_inode != dir)) -+ goto out_parent; -+ -+ err = au_digen_test(parent, au_sigen(sb)); -+ if (unlikely(err)) -+ goto out_parent; -+ -+ bindex = au_dbstart(parent); -+ au_set_dbstart(dentry, bindex); -+ au_set_dbend(dentry, bindex); -+ err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args); -+ bindex = err; -+ if (unlikely(err < 0)) -+ goto out_parent; -+ -+ err = -EOPNOTSUPP; -+ h_dir = au_h_iptr(dir, bindex); -+ if (unlikely(!h_dir->i_op->tmpfile)) -+ goto out_parent; -+ -+ h_mnt = au_sbr_mnt(sb, bindex); -+ err = vfsub_mnt_want_write(h_mnt); -+ if (unlikely(err)) -+ goto out_parent; -+ -+ h_parent = au_h_dptr(parent, bindex); -+ err = inode_permission(h_parent->d_inode, MAY_WRITE | MAY_EXEC); -+ if (unlikely(err)) -+ goto out_mnt; -+ -+ err = -ENOMEM; -+ h_dentry = d_alloc(h_parent, &dentry->d_name); -+ if (unlikely(!h_dentry)) -+ goto out_mnt; -+ -+ err = h_dir->i_op->tmpfile(h_dir, h_dentry, mode); -+ if (unlikely(err)) -+ goto out_dentry; -+ -+ au_set_dbstart(dentry, bindex); -+ au_set_dbend(dentry, bindex); -+ au_set_h_dptr(dentry, bindex, dget(h_dentry)); -+ inode = au_new_inode(dentry, /*must_new*/1); -+ if (IS_ERR(inode)) { -+ err = PTR_ERR(inode); -+ au_set_h_dptr(dentry, bindex, NULL); -+ au_set_dbstart(dentry, -1); -+ au_set_dbend(dentry, -1); -+ } else { -+ if (!inode->i_nlink) -+ set_nlink(inode, 1); -+ d_tmpfile(dentry, inode); -+ au_di(dentry)->di_tmpfile = 1; -+ -+ /* update without i_mutex */ -+ if (au_ibstart(dir) == au_dbstart(dentry)) -+ au_cpup_attr_timesizes(dir); -+ } -+ -+out_dentry: -+ dput(h_dentry); -+out_mnt: -+ vfsub_mnt_drop_write(h_mnt); -+out_parent: -+ di_write_unlock(parent); -+ dput(parent); -+ di_write_unlock(dentry); -+ if (!err) -+#if 0 -+ /* verbose coding for lock class name */ -+ au_rw_class(&au_di(dentry)->di_rwsem, -+ au_lc_key + AuLcNonDir_DIINFO); -+#else -+ ; -+#endif -+ else { -+ au_di_fin(dentry); -+ dentry->d_fsdata = NULL; -+ } -+out_si: -+ si_read_unlock(sb); -+out: -+ mutex_unlock(&dir->i_mutex); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct au_link_args { -+ aufs_bindex_t bdst, bsrc; -+ struct au_pin pin; -+ struct path h_path; -+ struct dentry *src_parent, *parent; -+}; -+ -+static int au_cpup_before_link(struct dentry *src_dentry, -+ struct au_link_args *a) -+{ -+ int err; -+ struct dentry *h_src_dentry; -+ struct au_cp_generic cpg = { -+ .dentry = src_dentry, -+ .bdst = a->bdst, -+ .bsrc = a->bsrc, -+ .len = -1, -+ .pin = &a->pin, -+ .flags = AuCpup_DTIME | AuCpup_HOPEN /* | AuCpup_KEEPLINO */ -+ }; -+ -+ di_read_lock_parent(a->src_parent, AuLock_IR); -+ err = au_test_and_cpup_dirs(src_dentry, a->bdst); -+ if (unlikely(err)) -+ goto out; -+ -+ h_src_dentry = au_h_dptr(src_dentry, a->bsrc); -+ err = au_pin(&a->pin, src_dentry, a->bdst, -+ au_opt_udba(src_dentry->d_sb), -+ AuPin_DI_LOCKED | AuPin_MNT_WRITE); -+ if (unlikely(err)) -+ goto out; -+ -+ err = au_sio_cpup_simple(&cpg); -+ au_unpin(&a->pin); -+ -+out: -+ di_read_unlock(a->src_parent, AuLock_IR); -+ return err; -+} -+ -+static int au_cpup_or_link(struct dentry *src_dentry, struct dentry *dentry, -+ struct au_link_args *a) -+{ -+ int err; -+ unsigned char plink; -+ aufs_bindex_t bend; -+ struct dentry *h_src_dentry; -+ struct inode *h_inode, *inode, *delegated; -+ struct super_block *sb; -+ struct file *h_file; -+ -+ plink = 0; -+ h_inode = NULL; -+ sb = src_dentry->d_sb; -+ inode = src_dentry->d_inode; -+ if (au_ibstart(inode) <= a->bdst) -+ h_inode = au_h_iptr(inode, a->bdst); -+ if (!h_inode || !h_inode->i_nlink) { -+ /* copyup src_dentry as the name of dentry. */ -+ bend = au_dbend(dentry); -+ if (bend < a->bsrc) -+ au_set_dbend(dentry, a->bsrc); -+ au_set_h_dptr(dentry, a->bsrc, -+ dget(au_h_dptr(src_dentry, a->bsrc))); -+ dget(a->h_path.dentry); -+ au_set_h_dptr(dentry, a->bdst, NULL); -+ AuDbg("temporary d_inode...\n"); -+ spin_lock(&dentry->d_lock); -+ dentry->d_inode = src_dentry->d_inode; /* tmp */ -+ spin_unlock(&dentry->d_lock); -+ h_file = au_h_open_pre(dentry, a->bsrc, /*force_wr*/0); -+ if (IS_ERR(h_file)) -+ err = PTR_ERR(h_file); -+ else { -+ struct au_cp_generic cpg = { -+ .dentry = dentry, -+ .bdst = a->bdst, -+ .bsrc = -1, -+ .len = -1, -+ .pin = &a->pin, -+ .flags = AuCpup_KEEPLINO -+ }; -+ err = au_sio_cpup_simple(&cpg); -+ au_h_open_post(dentry, a->bsrc, h_file); -+ if (!err) { -+ dput(a->h_path.dentry); -+ a->h_path.dentry = au_h_dptr(dentry, a->bdst); -+ } else -+ au_set_h_dptr(dentry, a->bdst, -+ a->h_path.dentry); -+ } -+ spin_lock(&dentry->d_lock); -+ dentry->d_inode = NULL; /* restore */ -+ spin_unlock(&dentry->d_lock); -+ AuDbg("temporary d_inode...done\n"); -+ au_set_h_dptr(dentry, a->bsrc, NULL); -+ au_set_dbend(dentry, bend); -+ } else { -+ /* the inode of src_dentry already exists on a.bdst branch */ -+ h_src_dentry = d_find_alias(h_inode); -+ if (!h_src_dentry && au_plink_test(inode)) { -+ plink = 1; -+ h_src_dentry = au_plink_lkup(inode, a->bdst); -+ err = PTR_ERR(h_src_dentry); -+ if (IS_ERR(h_src_dentry)) -+ goto out; -+ -+ if (unlikely(!h_src_dentry->d_inode)) { -+ dput(h_src_dentry); -+ h_src_dentry = NULL; -+ } -+ -+ } -+ if (h_src_dentry) { -+ delegated = NULL; -+ err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin), -+ &a->h_path, &delegated); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal link\n"); -+ iput(delegated); -+ } -+ dput(h_src_dentry); -+ } else { -+ AuIOErr("no dentry found for hi%lu on b%d\n", -+ h_inode->i_ino, a->bdst); -+ err = -EIO; -+ } -+ } -+ -+ if (!err && !plink) -+ au_plink_append(inode, a->bdst, a->h_path.dentry); -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+int aufs_link(struct dentry *src_dentry, struct inode *dir, -+ struct dentry *dentry) -+{ -+ int err, rerr; -+ struct au_dtime dt; -+ struct au_link_args *a; -+ struct dentry *wh_dentry, *h_src_dentry; -+ struct inode *inode, *delegated; -+ struct super_block *sb; -+ struct au_wr_dir_args wr_dir_args = { -+ /* .force_btgt = -1, */ -+ .flags = AuWrDir_ADD_ENTRY -+ }; -+ -+ IMustLock(dir); -+ inode = src_dentry->d_inode; -+ IMustLock(inode); -+ -+ err = -ENOMEM; -+ a = kzalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ -+ a->parent = dentry->d_parent; /* dir inode is locked */ -+ err = aufs_read_and_write_lock2(dentry, src_dentry, -+ AuLock_NOPLM | AuLock_GEN); -+ if (unlikely(err)) -+ goto out_kfree; -+ err = au_d_linkable(src_dentry); -+ if (unlikely(err)) -+ goto out_unlock; -+ err = au_d_may_add(dentry); -+ if (unlikely(err)) -+ goto out_unlock; -+ -+ a->src_parent = dget_parent(src_dentry); -+ wr_dir_args.force_btgt = au_ibstart(inode); -+ -+ di_write_lock_parent(a->parent); -+ wr_dir_args.force_btgt = au_wbr(dentry, wr_dir_args.force_btgt); -+ wh_dentry = lock_hdir_lkup_wh(dentry, &dt, src_dentry, &a->pin, -+ &wr_dir_args); -+ err = PTR_ERR(wh_dentry); -+ if (IS_ERR(wh_dentry)) -+ goto out_parent; -+ -+ err = 0; -+ sb = dentry->d_sb; -+ a->bdst = au_dbstart(dentry); -+ a->h_path.dentry = au_h_dptr(dentry, a->bdst); -+ a->h_path.mnt = au_sbr_mnt(sb, a->bdst); -+ a->bsrc = au_ibstart(inode); -+ h_src_dentry = au_h_d_alias(src_dentry, a->bsrc); -+ if (!h_src_dentry && au_di(src_dentry)->di_tmpfile) -+ h_src_dentry = dget(au_hi_wh(inode, a->bsrc)); -+ if (!h_src_dentry) { -+ a->bsrc = au_dbstart(src_dentry); -+ h_src_dentry = au_h_d_alias(src_dentry, a->bsrc); -+ AuDebugOn(!h_src_dentry); -+ } else if (IS_ERR(h_src_dentry)) { -+ err = PTR_ERR(h_src_dentry); -+ goto out_parent; -+ } -+ -+ if (au_opt_test(au_mntflags(sb), PLINK)) { -+ if (a->bdst < a->bsrc -+ /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */) -+ err = au_cpup_or_link(src_dentry, dentry, a); -+ else { -+ delegated = NULL; -+ err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin), -+ &a->h_path, &delegated); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal link\n"); -+ iput(delegated); -+ } -+ } -+ dput(h_src_dentry); -+ } else { -+ /* -+ * copyup src_dentry to the branch we process, -+ * and then link(2) to it. -+ */ -+ dput(h_src_dentry); -+ if (a->bdst < a->bsrc -+ /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */) { -+ au_unpin(&a->pin); -+ di_write_unlock(a->parent); -+ err = au_cpup_before_link(src_dentry, a); -+ di_write_lock_parent(a->parent); -+ if (!err) -+ err = au_pin(&a->pin, dentry, a->bdst, -+ au_opt_udba(sb), -+ AuPin_DI_LOCKED | AuPin_MNT_WRITE); -+ if (unlikely(err)) -+ goto out_wh; -+ } -+ if (!err) { -+ h_src_dentry = au_h_dptr(src_dentry, a->bdst); -+ err = -ENOENT; -+ if (h_src_dentry && h_src_dentry->d_inode) { -+ delegated = NULL; -+ err = vfsub_link(h_src_dentry, -+ au_pinned_h_dir(&a->pin), -+ &a->h_path, &delegated); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry" -+ " for NFSv4 delegation" -+ " for an internal link\n"); -+ iput(delegated); -+ } -+ } -+ } -+ } -+ if (unlikely(err)) -+ goto out_unpin; -+ -+ if (wh_dentry) { -+ a->h_path.dentry = wh_dentry; -+ err = au_wh_unlink_dentry(au_pinned_h_dir(&a->pin), &a->h_path, -+ dentry); -+ if (unlikely(err)) -+ goto out_revert; -+ } -+ -+ au_dir_ts(dir, a->bdst); -+ dir->i_version++; -+ inc_nlink(inode); -+ inode->i_ctime = dir->i_ctime; -+ d_instantiate(dentry, au_igrab(inode)); -+ if (d_unhashed(a->h_path.dentry)) -+ /* some filesystem calls d_drop() */ -+ d_drop(dentry); -+ /* some filesystems consume an inode even hardlink */ -+ au_fhsm_wrote(sb, a->bdst, /*force*/0); -+ goto out_unpin; /* success */ -+ -+out_revert: -+ /* no delegation since it is just created */ -+ rerr = vfsub_unlink(au_pinned_h_dir(&a->pin), &a->h_path, -+ /*delegated*/NULL, /*force*/0); -+ if (unlikely(rerr)) { -+ AuIOErr("%pd reverting failed(%d, %d)\n", dentry, err, rerr); -+ err = -EIO; -+ } -+ au_dtime_revert(&dt); -+out_unpin: -+ au_unpin(&a->pin); -+out_wh: -+ dput(wh_dentry); -+out_parent: -+ di_write_unlock(a->parent); -+ dput(a->src_parent); -+out_unlock: -+ if (unlikely(err)) { -+ au_update_dbstart(dentry); -+ d_drop(dentry); -+ } -+ aufs_read_and_write_unlock2(dentry, src_dentry); -+out_kfree: -+ kfree(a); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+int aufs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) -+{ -+ int err, rerr; -+ aufs_bindex_t bindex; -+ unsigned char diropq; -+ struct path h_path; -+ struct dentry *wh_dentry, *parent, *opq_dentry; -+ struct mutex *h_mtx; -+ struct super_block *sb; -+ struct { -+ struct au_pin pin; -+ struct au_dtime dt; -+ } *a; /* reduce the stack usage */ -+ struct au_wr_dir_args wr_dir_args = { -+ .force_btgt = -1, -+ .flags = AuWrDir_ADD_ENTRY | AuWrDir_ISDIR -+ }; -+ -+ IMustLock(dir); -+ -+ err = -ENOMEM; -+ a = kmalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ -+ err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); -+ if (unlikely(err)) -+ goto out_free; -+ err = au_d_may_add(dentry); -+ if (unlikely(err)) -+ goto out_unlock; -+ -+ parent = dentry->d_parent; /* dir inode is locked */ -+ di_write_lock_parent(parent); -+ wh_dentry = lock_hdir_lkup_wh(dentry, &a->dt, /*src_dentry*/NULL, -+ &a->pin, &wr_dir_args); -+ err = PTR_ERR(wh_dentry); -+ if (IS_ERR(wh_dentry)) -+ goto out_parent; -+ -+ sb = dentry->d_sb; -+ bindex = au_dbstart(dentry); -+ h_path.dentry = au_h_dptr(dentry, bindex); -+ h_path.mnt = au_sbr_mnt(sb, bindex); -+ err = vfsub_mkdir(au_pinned_h_dir(&a->pin), &h_path, mode); -+ if (unlikely(err)) -+ goto out_unpin; -+ -+ /* make the dir opaque */ -+ diropq = 0; -+ h_mtx = &h_path.dentry->d_inode->i_mutex; -+ if (wh_dentry -+ || au_opt_test(au_mntflags(sb), ALWAYS_DIROPQ)) { -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); -+ opq_dentry = au_diropq_create(dentry, bindex); -+ mutex_unlock(h_mtx); -+ err = PTR_ERR(opq_dentry); -+ if (IS_ERR(opq_dentry)) -+ goto out_dir; -+ dput(opq_dentry); -+ diropq = 1; -+ } -+ -+ err = epilog(dir, bindex, wh_dentry, dentry); -+ if (!err) { -+ inc_nlink(dir); -+ goto out_unpin; /* success */ -+ } -+ -+ /* revert */ -+ if (diropq) { -+ AuLabel(revert opq); -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); -+ rerr = au_diropq_remove(dentry, bindex); -+ mutex_unlock(h_mtx); -+ if (rerr) { -+ AuIOErr("%pd reverting diropq failed(%d, %d)\n", -+ dentry, err, rerr); -+ err = -EIO; -+ } -+ } -+ -+out_dir: -+ AuLabel(revert dir); -+ rerr = vfsub_rmdir(au_pinned_h_dir(&a->pin), &h_path); -+ if (rerr) { -+ AuIOErr("%pd reverting dir failed(%d, %d)\n", -+ dentry, err, rerr); -+ err = -EIO; -+ } -+ au_dtime_revert(&a->dt); -+out_unpin: -+ au_unpin(&a->pin); -+ dput(wh_dentry); -+out_parent: -+ di_write_unlock(parent); -+out_unlock: -+ if (unlikely(err)) { -+ au_update_dbstart(dentry); -+ d_drop(dentry); -+ } -+ aufs_read_unlock(dentry, AuLock_DW); -+out_free: -+ kfree(a); -+out: -+ return err; -+} -diff --git a/fs/aufs/i_op_del.c b/fs/aufs/i_op_del.c -new file mode 100644 -index 0000000..b4dd686 ---- /dev/null -+++ b/fs/aufs/i_op_del.c -@@ -0,0 +1,506 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * inode operations (del entry) -+ */ -+ -+#include "aufs.h" -+ -+/* -+ * decide if a new whiteout for @dentry is necessary or not. -+ * when it is necessary, prepare the parent dir for the upper branch whose -+ * branch index is @bcpup for creation. the actual creation of the whiteout will -+ * be done by caller. -+ * return value: -+ * 0: wh is unnecessary -+ * plus: wh is necessary -+ * minus: error -+ */ -+int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup) -+{ -+ int need_wh, err; -+ aufs_bindex_t bstart; -+ struct super_block *sb; -+ -+ sb = dentry->d_sb; -+ bstart = au_dbstart(dentry); -+ if (*bcpup < 0) { -+ *bcpup = bstart; -+ if (au_test_ro(sb, bstart, dentry->d_inode)) { -+ err = AuWbrCopyup(au_sbi(sb), dentry); -+ *bcpup = err; -+ if (unlikely(err < 0)) -+ goto out; -+ } -+ } else -+ AuDebugOn(bstart < *bcpup -+ || au_test_ro(sb, *bcpup, dentry->d_inode)); -+ AuDbg("bcpup %d, bstart %d\n", *bcpup, bstart); -+ -+ if (*bcpup != bstart) { -+ err = au_cpup_dirs(dentry, *bcpup); -+ if (unlikely(err)) -+ goto out; -+ need_wh = 1; -+ } else { -+ struct au_dinfo *dinfo, *tmp; -+ -+ need_wh = -ENOMEM; -+ dinfo = au_di(dentry); -+ tmp = au_di_alloc(sb, AuLsc_DI_TMP); -+ if (tmp) { -+ au_di_cp(tmp, dinfo); -+ au_di_swap(tmp, dinfo); -+ /* returns the number of positive dentries */ -+ need_wh = au_lkup_dentry(dentry, bstart + 1, /*type*/0); -+ au_di_swap(tmp, dinfo); -+ au_rw_write_unlock(&tmp->di_rwsem); -+ au_di_free(tmp); -+ } -+ } -+ AuDbg("need_wh %d\n", need_wh); -+ err = need_wh; -+ -+out: -+ return err; -+} -+ -+/* -+ * simple tests for the del-entry operations. -+ * following the checks in vfs, plus the parent-child relationship. -+ */ -+int au_may_del(struct dentry *dentry, aufs_bindex_t bindex, -+ struct dentry *h_parent, int isdir) -+{ -+ int err; -+ umode_t h_mode; -+ struct dentry *h_dentry, *h_latest; -+ struct inode *h_inode; -+ -+ h_dentry = au_h_dptr(dentry, bindex); -+ h_inode = h_dentry->d_inode; -+ if (dentry->d_inode) { -+ err = -ENOENT; -+ if (unlikely(!h_inode || !h_inode->i_nlink)) -+ goto out; -+ -+ h_mode = h_inode->i_mode; -+ if (!isdir) { -+ err = -EISDIR; -+ if (unlikely(S_ISDIR(h_mode))) -+ goto out; -+ } else if (unlikely(!S_ISDIR(h_mode))) { -+ err = -ENOTDIR; -+ goto out; -+ } -+ } else { -+ /* rename(2) case */ -+ err = -EIO; -+ if (unlikely(h_inode)) -+ goto out; -+ } -+ -+ err = -ENOENT; -+ /* expected parent dir is locked */ -+ if (unlikely(h_parent != h_dentry->d_parent)) -+ goto out; -+ err = 0; -+ -+ /* -+ * rmdir a dir may break the consistency on some filesystem. -+ * let's try heavy test. -+ */ -+ err = -EACCES; -+ if (unlikely(!au_opt_test(au_mntflags(dentry->d_sb), DIRPERM1) -+ && au_test_h_perm(h_parent->d_inode, -+ MAY_EXEC | MAY_WRITE))) -+ goto out; -+ -+ h_latest = au_sio_lkup_one(&dentry->d_name, h_parent); -+ err = -EIO; -+ if (IS_ERR(h_latest)) -+ goto out; -+ if (h_latest == h_dentry) -+ err = 0; -+ dput(h_latest); -+ -+out: -+ return err; -+} -+ -+/* -+ * decide the branch where we operate for @dentry. the branch index will be set -+ * @rbcpup. after diciding it, 'pin' it and store the timestamps of the parent -+ * dir for reverting. -+ * when a new whiteout is necessary, create it. -+ */ -+static struct dentry* -+lock_hdir_create_wh(struct dentry *dentry, int isdir, aufs_bindex_t *rbcpup, -+ struct au_dtime *dt, struct au_pin *pin) -+{ -+ struct dentry *wh_dentry; -+ struct super_block *sb; -+ struct path h_path; -+ int err, need_wh; -+ unsigned int udba; -+ aufs_bindex_t bcpup; -+ -+ need_wh = au_wr_dir_need_wh(dentry, isdir, rbcpup); -+ wh_dentry = ERR_PTR(need_wh); -+ if (unlikely(need_wh < 0)) -+ goto out; -+ -+ sb = dentry->d_sb; -+ udba = au_opt_udba(sb); -+ bcpup = *rbcpup; -+ err = au_pin(pin, dentry, bcpup, udba, -+ AuPin_DI_LOCKED | AuPin_MNT_WRITE); -+ wh_dentry = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out; -+ -+ h_path.dentry = au_pinned_h_parent(pin); -+ if (udba != AuOpt_UDBA_NONE -+ && au_dbstart(dentry) == bcpup) { -+ err = au_may_del(dentry, bcpup, h_path.dentry, isdir); -+ wh_dentry = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out_unpin; -+ } -+ -+ h_path.mnt = au_sbr_mnt(sb, bcpup); -+ au_dtime_store(dt, au_pinned_parent(pin), &h_path); -+ wh_dentry = NULL; -+ if (!need_wh) -+ goto out; /* success, no need to create whiteout */ -+ -+ wh_dentry = au_wh_create(dentry, bcpup, h_path.dentry); -+ if (IS_ERR(wh_dentry)) -+ goto out_unpin; -+ -+ /* returns with the parent is locked and wh_dentry is dget-ed */ -+ goto out; /* success */ -+ -+out_unpin: -+ au_unpin(pin); -+out: -+ return wh_dentry; -+} -+ -+/* -+ * when removing a dir, rename it to a unique temporary whiteout-ed name first -+ * in order to be revertible and save time for removing many child whiteouts -+ * under the dir. -+ * returns 1 when there are too many child whiteout and caller should remove -+ * them asynchronously. returns 0 when the number of children is enough small to -+ * remove now or the branch fs is a remote fs. -+ * otherwise return an error. -+ */ -+static int renwh_and_rmdir(struct dentry *dentry, aufs_bindex_t bindex, -+ struct au_nhash *whlist, struct inode *dir) -+{ -+ int rmdir_later, err, dirwh; -+ struct dentry *h_dentry; -+ struct super_block *sb; -+ -+ sb = dentry->d_sb; -+ SiMustAnyLock(sb); -+ h_dentry = au_h_dptr(dentry, bindex); -+ err = au_whtmp_ren(h_dentry, au_sbr(sb, bindex)); -+ if (unlikely(err)) -+ goto out; -+ -+ /* stop monitoring */ -+ au_hn_free(au_hi(dentry->d_inode, bindex)); -+ -+ if (!au_test_fs_remote(h_dentry->d_sb)) { -+ dirwh = au_sbi(sb)->si_dirwh; -+ rmdir_later = (dirwh <= 1); -+ if (!rmdir_later) -+ rmdir_later = au_nhash_test_longer_wh(whlist, bindex, -+ dirwh); -+ if (rmdir_later) -+ return rmdir_later; -+ } -+ -+ err = au_whtmp_rmdir(dir, bindex, h_dentry, whlist); -+ if (unlikely(err)) { -+ AuIOErr("rmdir %pd, b%d failed, %d. ignored\n", -+ h_dentry, bindex, err); -+ err = 0; -+ } -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* -+ * final procedure for deleting a entry. -+ * maintain dentry and iattr. -+ */ -+static void epilog(struct inode *dir, struct dentry *dentry, -+ aufs_bindex_t bindex) -+{ -+ struct inode *inode; -+ -+ inode = dentry->d_inode; -+ d_drop(dentry); -+ inode->i_ctime = dir->i_ctime; -+ -+ au_dir_ts(dir, bindex); -+ dir->i_version++; -+} -+ -+/* -+ * when an error happened, remove the created whiteout and revert everything. -+ */ -+static int do_revert(int err, struct inode *dir, aufs_bindex_t bindex, -+ aufs_bindex_t bwh, struct dentry *wh_dentry, -+ struct dentry *dentry, struct au_dtime *dt) -+{ -+ int rerr; -+ struct path h_path = { -+ .dentry = wh_dentry, -+ .mnt = au_sbr_mnt(dir->i_sb, bindex) -+ }; -+ -+ rerr = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path, dentry); -+ if (!rerr) { -+ au_set_dbwh(dentry, bwh); -+ au_dtime_revert(dt); -+ return 0; -+ } -+ -+ AuIOErr("%pd reverting whiteout failed(%d, %d)\n", dentry, err, rerr); -+ return -EIO; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int aufs_unlink(struct inode *dir, struct dentry *dentry) -+{ -+ int err; -+ aufs_bindex_t bwh, bindex, bstart; -+ struct inode *inode, *h_dir, *delegated; -+ struct dentry *parent, *wh_dentry; -+ /* to reuduce stack size */ -+ struct { -+ struct au_dtime dt; -+ struct au_pin pin; -+ struct path h_path; -+ } *a; -+ -+ IMustLock(dir); -+ -+ err = -ENOMEM; -+ a = kmalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ -+ err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); -+ if (unlikely(err)) -+ goto out_free; -+ err = au_d_hashed_positive(dentry); -+ if (unlikely(err)) -+ goto out_unlock; -+ inode = dentry->d_inode; -+ IMustLock(inode); -+ err = -EISDIR; -+ if (unlikely(d_is_dir(dentry))) -+ goto out_unlock; /* possible? */ -+ -+ bstart = au_dbstart(dentry); -+ bwh = au_dbwh(dentry); -+ bindex = -1; -+ parent = dentry->d_parent; /* dir inode is locked */ -+ di_write_lock_parent(parent); -+ wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/0, &bindex, &a->dt, -+ &a->pin); -+ err = PTR_ERR(wh_dentry); -+ if (IS_ERR(wh_dentry)) -+ goto out_parent; -+ -+ a->h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart); -+ a->h_path.dentry = au_h_dptr(dentry, bstart); -+ dget(a->h_path.dentry); -+ if (bindex == bstart) { -+ h_dir = au_pinned_h_dir(&a->pin); -+ delegated = NULL; -+ err = vfsub_unlink(h_dir, &a->h_path, &delegated, /*force*/0); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal unlink\n"); -+ iput(delegated); -+ } -+ } else { -+ /* dir inode is locked */ -+ h_dir = wh_dentry->d_parent->d_inode; -+ IMustLock(h_dir); -+ err = 0; -+ } -+ -+ if (!err) { -+ vfsub_drop_nlink(inode); -+ epilog(dir, dentry, bindex); -+ -+ /* update target timestamps */ -+ if (bindex == bstart) { -+ vfsub_update_h_iattr(&a->h_path, /*did*/NULL); -+ /*ignore*/ -+ inode->i_ctime = a->h_path.dentry->d_inode->i_ctime; -+ } else -+ /* todo: this timestamp may be reverted later */ -+ inode->i_ctime = h_dir->i_ctime; -+ goto out_unpin; /* success */ -+ } -+ -+ /* revert */ -+ if (wh_dentry) { -+ int rerr; -+ -+ rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, -+ &a->dt); -+ if (rerr) -+ err = rerr; -+ } -+ -+out_unpin: -+ au_unpin(&a->pin); -+ dput(wh_dentry); -+ dput(a->h_path.dentry); -+out_parent: -+ di_write_unlock(parent); -+out_unlock: -+ aufs_read_unlock(dentry, AuLock_DW); -+out_free: -+ kfree(a); -+out: -+ return err; -+} -+ -+int aufs_rmdir(struct inode *dir, struct dentry *dentry) -+{ -+ int err, rmdir_later; -+ aufs_bindex_t bwh, bindex, bstart; -+ struct inode *inode; -+ struct dentry *parent, *wh_dentry, *h_dentry; -+ struct au_whtmp_rmdir *args; -+ /* to reuduce stack size */ -+ struct { -+ struct au_dtime dt; -+ struct au_pin pin; -+ } *a; -+ -+ IMustLock(dir); -+ -+ err = -ENOMEM; -+ a = kmalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ -+ err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN); -+ if (unlikely(err)) -+ goto out_free; -+ err = au_alive_dir(dentry); -+ if (unlikely(err)) -+ goto out_unlock; -+ inode = dentry->d_inode; -+ IMustLock(inode); -+ err = -ENOTDIR; -+ if (unlikely(!d_is_dir(dentry))) -+ goto out_unlock; /* possible? */ -+ -+ err = -ENOMEM; -+ args = au_whtmp_rmdir_alloc(dir->i_sb, GFP_NOFS); -+ if (unlikely(!args)) -+ goto out_unlock; -+ -+ parent = dentry->d_parent; /* dir inode is locked */ -+ di_write_lock_parent(parent); -+ err = au_test_empty(dentry, &args->whlist); -+ if (unlikely(err)) -+ goto out_parent; -+ -+ bstart = au_dbstart(dentry); -+ bwh = au_dbwh(dentry); -+ bindex = -1; -+ wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/1, &bindex, &a->dt, -+ &a->pin); -+ err = PTR_ERR(wh_dentry); -+ if (IS_ERR(wh_dentry)) -+ goto out_parent; -+ -+ h_dentry = au_h_dptr(dentry, bstart); -+ dget(h_dentry); -+ rmdir_later = 0; -+ if (bindex == bstart) { -+ err = renwh_and_rmdir(dentry, bstart, &args->whlist, dir); -+ if (err > 0) { -+ rmdir_later = err; -+ err = 0; -+ } -+ } else { -+ /* stop monitoring */ -+ au_hn_free(au_hi(inode, bstart)); -+ -+ /* dir inode is locked */ -+ IMustLock(wh_dentry->d_parent->d_inode); -+ err = 0; -+ } -+ -+ if (!err) { -+ vfsub_dead_dir(inode); -+ au_set_dbdiropq(dentry, -1); -+ epilog(dir, dentry, bindex); -+ -+ if (rmdir_later) { -+ au_whtmp_kick_rmdir(dir, bstart, h_dentry, args); -+ args = NULL; -+ } -+ -+ goto out_unpin; /* success */ -+ } -+ -+ /* revert */ -+ AuLabel(revert); -+ if (wh_dentry) { -+ int rerr; -+ -+ rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, -+ &a->dt); -+ if (rerr) -+ err = rerr; -+ } -+ -+out_unpin: -+ au_unpin(&a->pin); -+ dput(wh_dentry); -+ dput(h_dentry); -+out_parent: -+ di_write_unlock(parent); -+ if (args) -+ au_whtmp_rmdir_free(args); -+out_unlock: -+ aufs_read_unlock(dentry, AuLock_DW); -+out_free: -+ kfree(a); -+out: -+ AuTraceErr(err); -+ return err; -+} -diff --git a/fs/aufs/i_op_ren.c b/fs/aufs/i_op_ren.c -new file mode 100644 -index 0000000..6ce2ed6 ---- /dev/null -+++ b/fs/aufs/i_op_ren.c -@@ -0,0 +1,1013 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * inode operation (rename entry) -+ * todo: this is crazy monster -+ */ -+ -+#include "aufs.h" -+ -+enum { AuSRC, AuDST, AuSrcDst }; -+enum { AuPARENT, AuCHILD, AuParentChild }; -+ -+#define AuRen_ISDIR 1 -+#define AuRen_ISSAMEDIR (1 << 1) -+#define AuRen_WHSRC (1 << 2) -+#define AuRen_WHDST (1 << 3) -+#define AuRen_MNT_WRITE (1 << 4) -+#define AuRen_DT_DSTDIR (1 << 5) -+#define AuRen_DIROPQ (1 << 6) -+#define au_ftest_ren(flags, name) ((flags) & AuRen_##name) -+#define au_fset_ren(flags, name) \ -+ do { (flags) |= AuRen_##name; } while (0) -+#define au_fclr_ren(flags, name) \ -+ do { (flags) &= ~AuRen_##name; } while (0) -+ -+struct au_ren_args { -+ struct { -+ struct dentry *dentry, *h_dentry, *parent, *h_parent, -+ *wh_dentry; -+ struct inode *dir, *inode; -+ struct au_hinode *hdir; -+ struct au_dtime dt[AuParentChild]; -+ aufs_bindex_t bstart; -+ } sd[AuSrcDst]; -+ -+#define src_dentry sd[AuSRC].dentry -+#define src_dir sd[AuSRC].dir -+#define src_inode sd[AuSRC].inode -+#define src_h_dentry sd[AuSRC].h_dentry -+#define src_parent sd[AuSRC].parent -+#define src_h_parent sd[AuSRC].h_parent -+#define src_wh_dentry sd[AuSRC].wh_dentry -+#define src_hdir sd[AuSRC].hdir -+#define src_h_dir sd[AuSRC].hdir->hi_inode -+#define src_dt sd[AuSRC].dt -+#define src_bstart sd[AuSRC].bstart -+ -+#define dst_dentry sd[AuDST].dentry -+#define dst_dir sd[AuDST].dir -+#define dst_inode sd[AuDST].inode -+#define dst_h_dentry sd[AuDST].h_dentry -+#define dst_parent sd[AuDST].parent -+#define dst_h_parent sd[AuDST].h_parent -+#define dst_wh_dentry sd[AuDST].wh_dentry -+#define dst_hdir sd[AuDST].hdir -+#define dst_h_dir sd[AuDST].hdir->hi_inode -+#define dst_dt sd[AuDST].dt -+#define dst_bstart sd[AuDST].bstart -+ -+ struct dentry *h_trap; -+ struct au_branch *br; -+ struct au_hinode *src_hinode; -+ struct path h_path; -+ struct au_nhash whlist; -+ aufs_bindex_t btgt, src_bwh, src_bdiropq; -+ -+ unsigned int flags; -+ -+ struct au_whtmp_rmdir *thargs; -+ struct dentry *h_dst; -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * functions for reverting. -+ * when an error happened in a single rename systemcall, we should revert -+ * everything as if nothing happened. -+ * we don't need to revert the copied-up/down the parent dir since they are -+ * harmless. -+ */ -+ -+#define RevertFailure(fmt, ...) do { \ -+ AuIOErr("revert failure: " fmt " (%d, %d)\n", \ -+ ##__VA_ARGS__, err, rerr); \ -+ err = -EIO; \ -+} while (0) -+ -+static void au_ren_rev_diropq(int err, struct au_ren_args *a) -+{ -+ int rerr; -+ -+ au_hn_imtx_lock_nested(a->src_hinode, AuLsc_I_CHILD); -+ rerr = au_diropq_remove(a->src_dentry, a->btgt); -+ au_hn_imtx_unlock(a->src_hinode); -+ au_set_dbdiropq(a->src_dentry, a->src_bdiropq); -+ if (rerr) -+ RevertFailure("remove diropq %pd", a->src_dentry); -+} -+ -+static void au_ren_rev_rename(int err, struct au_ren_args *a) -+{ -+ int rerr; -+ struct inode *delegated; -+ -+ a->h_path.dentry = vfsub_lkup_one(&a->src_dentry->d_name, -+ a->src_h_parent); -+ rerr = PTR_ERR(a->h_path.dentry); -+ if (IS_ERR(a->h_path.dentry)) { -+ RevertFailure("lkup one %pd", a->src_dentry); -+ return; -+ } -+ -+ delegated = NULL; -+ rerr = vfsub_rename(a->dst_h_dir, -+ au_h_dptr(a->src_dentry, a->btgt), -+ a->src_h_dir, &a->h_path, &delegated); -+ if (unlikely(rerr == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal rename\n"); -+ iput(delegated); -+ } -+ d_drop(a->h_path.dentry); -+ dput(a->h_path.dentry); -+ /* au_set_h_dptr(a->src_dentry, a->btgt, NULL); */ -+ if (rerr) -+ RevertFailure("rename %pd", a->src_dentry); -+} -+ -+static void au_ren_rev_whtmp(int err, struct au_ren_args *a) -+{ -+ int rerr; -+ struct inode *delegated; -+ -+ a->h_path.dentry = vfsub_lkup_one(&a->dst_dentry->d_name, -+ a->dst_h_parent); -+ rerr = PTR_ERR(a->h_path.dentry); -+ if (IS_ERR(a->h_path.dentry)) { -+ RevertFailure("lkup one %pd", a->dst_dentry); -+ return; -+ } -+ if (a->h_path.dentry->d_inode) { -+ d_drop(a->h_path.dentry); -+ dput(a->h_path.dentry); -+ return; -+ } -+ -+ delegated = NULL; -+ rerr = vfsub_rename(a->dst_h_dir, a->h_dst, a->dst_h_dir, &a->h_path, -+ &delegated); -+ if (unlikely(rerr == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal rename\n"); -+ iput(delegated); -+ } -+ d_drop(a->h_path.dentry); -+ dput(a->h_path.dentry); -+ if (!rerr) -+ au_set_h_dptr(a->dst_dentry, a->btgt, dget(a->h_dst)); -+ else -+ RevertFailure("rename %pd", a->h_dst); -+} -+ -+static void au_ren_rev_whsrc(int err, struct au_ren_args *a) -+{ -+ int rerr; -+ -+ a->h_path.dentry = a->src_wh_dentry; -+ rerr = au_wh_unlink_dentry(a->src_h_dir, &a->h_path, a->src_dentry); -+ au_set_dbwh(a->src_dentry, a->src_bwh); -+ if (rerr) -+ RevertFailure("unlink %pd", a->src_wh_dentry); -+} -+#undef RevertFailure -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * when we have to copyup the renaming entry, do it with the rename-target name -+ * in order to minimize the cost (the later actual rename is unnecessary). -+ * otherwise rename it on the target branch. -+ */ -+static int au_ren_or_cpup(struct au_ren_args *a) -+{ -+ int err; -+ struct dentry *d; -+ struct inode *delegated; -+ -+ d = a->src_dentry; -+ if (au_dbstart(d) == a->btgt) { -+ a->h_path.dentry = a->dst_h_dentry; -+ if (au_ftest_ren(a->flags, DIROPQ) -+ && au_dbdiropq(d) == a->btgt) -+ au_fclr_ren(a->flags, DIROPQ); -+ AuDebugOn(au_dbstart(d) != a->btgt); -+ delegated = NULL; -+ err = vfsub_rename(a->src_h_dir, au_h_dptr(d, a->btgt), -+ a->dst_h_dir, &a->h_path, &delegated); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal rename\n"); -+ iput(delegated); -+ } -+ } else -+ BUG(); -+ -+ if (!err && a->h_dst) -+ /* it will be set to dinfo later */ -+ dget(a->h_dst); -+ -+ return err; -+} -+ -+/* cf. aufs_rmdir() */ -+static int au_ren_del_whtmp(struct au_ren_args *a) -+{ -+ int err; -+ struct inode *dir; -+ -+ dir = a->dst_dir; -+ SiMustAnyLock(dir->i_sb); -+ if (!au_nhash_test_longer_wh(&a->whlist, a->btgt, -+ au_sbi(dir->i_sb)->si_dirwh) -+ || au_test_fs_remote(a->h_dst->d_sb)) { -+ err = au_whtmp_rmdir(dir, a->btgt, a->h_dst, &a->whlist); -+ if (unlikely(err)) -+ pr_warn("failed removing whtmp dir %pd (%d), " -+ "ignored.\n", a->h_dst, err); -+ } else { -+ au_nhash_wh_free(&a->thargs->whlist); -+ a->thargs->whlist = a->whlist; -+ a->whlist.nh_num = 0; -+ au_whtmp_kick_rmdir(dir, a->btgt, a->h_dst, a->thargs); -+ dput(a->h_dst); -+ a->thargs = NULL; -+ } -+ -+ return 0; -+} -+ -+/* make it 'opaque' dir. */ -+static int au_ren_diropq(struct au_ren_args *a) -+{ -+ int err; -+ struct dentry *diropq; -+ -+ err = 0; -+ a->src_bdiropq = au_dbdiropq(a->src_dentry); -+ a->src_hinode = au_hi(a->src_inode, a->btgt); -+ au_hn_imtx_lock_nested(a->src_hinode, AuLsc_I_CHILD); -+ diropq = au_diropq_create(a->src_dentry, a->btgt); -+ au_hn_imtx_unlock(a->src_hinode); -+ if (IS_ERR(diropq)) -+ err = PTR_ERR(diropq); -+ else -+ dput(diropq); -+ -+ return err; -+} -+ -+static int do_rename(struct au_ren_args *a) -+{ -+ int err; -+ struct dentry *d, *h_d; -+ -+ /* prepare workqueue args for asynchronous rmdir */ -+ h_d = a->dst_h_dentry; -+ if (au_ftest_ren(a->flags, ISDIR) && h_d->d_inode) { -+ err = -ENOMEM; -+ a->thargs = au_whtmp_rmdir_alloc(a->src_dentry->d_sb, GFP_NOFS); -+ if (unlikely(!a->thargs)) -+ goto out; -+ a->h_dst = dget(h_d); -+ } -+ -+ /* create whiteout for src_dentry */ -+ if (au_ftest_ren(a->flags, WHSRC)) { -+ a->src_bwh = au_dbwh(a->src_dentry); -+ AuDebugOn(a->src_bwh >= 0); -+ a->src_wh_dentry -+ = au_wh_create(a->src_dentry, a->btgt, a->src_h_parent); -+ err = PTR_ERR(a->src_wh_dentry); -+ if (IS_ERR(a->src_wh_dentry)) -+ goto out_thargs; -+ } -+ -+ /* lookup whiteout for dentry */ -+ if (au_ftest_ren(a->flags, WHDST)) { -+ h_d = au_wh_lkup(a->dst_h_parent, &a->dst_dentry->d_name, -+ a->br); -+ err = PTR_ERR(h_d); -+ if (IS_ERR(h_d)) -+ goto out_whsrc; -+ if (!h_d->d_inode) -+ dput(h_d); -+ else -+ a->dst_wh_dentry = h_d; -+ } -+ -+ /* rename dentry to tmpwh */ -+ if (a->thargs) { -+ err = au_whtmp_ren(a->dst_h_dentry, a->br); -+ if (unlikely(err)) -+ goto out_whdst; -+ -+ d = a->dst_dentry; -+ au_set_h_dptr(d, a->btgt, NULL); -+ err = au_lkup_neg(d, a->btgt, /*wh*/0); -+ if (unlikely(err)) -+ goto out_whtmp; -+ a->dst_h_dentry = au_h_dptr(d, a->btgt); -+ } -+ -+ BUG_ON(a->dst_h_dentry->d_inode && a->src_bstart != a->btgt); -+ -+ /* rename by vfs_rename or cpup */ -+ d = a->dst_dentry; -+ if (au_ftest_ren(a->flags, ISDIR) -+ && (a->dst_wh_dentry -+ || au_dbdiropq(d) == a->btgt -+ /* hide the lower to keep xino */ -+ || a->btgt < au_dbend(d) -+ || au_opt_test(au_mntflags(d->d_sb), ALWAYS_DIROPQ))) -+ au_fset_ren(a->flags, DIROPQ); -+ err = au_ren_or_cpup(a); -+ if (unlikely(err)) -+ /* leave the copied-up one */ -+ goto out_whtmp; -+ -+ /* make dir opaque */ -+ if (au_ftest_ren(a->flags, DIROPQ)) { -+ err = au_ren_diropq(a); -+ if (unlikely(err)) -+ goto out_rename; -+ } -+ -+ /* update target timestamps */ -+ AuDebugOn(au_dbstart(a->src_dentry) != a->btgt); -+ a->h_path.dentry = au_h_dptr(a->src_dentry, a->btgt); -+ vfsub_update_h_iattr(&a->h_path, /*did*/NULL); /*ignore*/ -+ a->src_inode->i_ctime = a->h_path.dentry->d_inode->i_ctime; -+ -+ /* remove whiteout for dentry */ -+ if (a->dst_wh_dentry) { -+ a->h_path.dentry = a->dst_wh_dentry; -+ err = au_wh_unlink_dentry(a->dst_h_dir, &a->h_path, -+ a->dst_dentry); -+ if (unlikely(err)) -+ goto out_diropq; -+ } -+ -+ /* remove whtmp */ -+ if (a->thargs) -+ au_ren_del_whtmp(a); /* ignore this error */ -+ -+ au_fhsm_wrote(a->src_dentry->d_sb, a->btgt, /*force*/0); -+ err = 0; -+ goto out_success; -+ -+out_diropq: -+ if (au_ftest_ren(a->flags, DIROPQ)) -+ au_ren_rev_diropq(err, a); -+out_rename: -+ au_ren_rev_rename(err, a); -+ dput(a->h_dst); -+out_whtmp: -+ if (a->thargs) -+ au_ren_rev_whtmp(err, a); -+out_whdst: -+ dput(a->dst_wh_dentry); -+ a->dst_wh_dentry = NULL; -+out_whsrc: -+ if (a->src_wh_dentry) -+ au_ren_rev_whsrc(err, a); -+out_success: -+ dput(a->src_wh_dentry); -+ dput(a->dst_wh_dentry); -+out_thargs: -+ if (a->thargs) { -+ dput(a->h_dst); -+ au_whtmp_rmdir_free(a->thargs); -+ a->thargs = NULL; -+ } -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * test if @dentry dir can be rename destination or not. -+ * success means, it is a logically empty dir. -+ */ -+static int may_rename_dstdir(struct dentry *dentry, struct au_nhash *whlist) -+{ -+ return au_test_empty(dentry, whlist); -+} -+ -+/* -+ * test if @dentry dir can be rename source or not. -+ * if it can, return 0 and @children is filled. -+ * success means, -+ * - it is a logically empty dir. -+ * - or, it exists on writable branch and has no children including whiteouts -+ * on the lower branch. -+ */ -+static int may_rename_srcdir(struct dentry *dentry, aufs_bindex_t btgt) -+{ -+ int err; -+ unsigned int rdhash; -+ aufs_bindex_t bstart; -+ -+ bstart = au_dbstart(dentry); -+ if (bstart != btgt) { -+ struct au_nhash whlist; -+ -+ SiMustAnyLock(dentry->d_sb); -+ rdhash = au_sbi(dentry->d_sb)->si_rdhash; -+ if (!rdhash) -+ rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, -+ dentry)); -+ err = au_nhash_alloc(&whlist, rdhash, GFP_NOFS); -+ if (unlikely(err)) -+ goto out; -+ err = au_test_empty(dentry, &whlist); -+ au_nhash_wh_free(&whlist); -+ goto out; -+ } -+ -+ if (bstart == au_dbtaildir(dentry)) -+ return 0; /* success */ -+ -+ err = au_test_empty_lower(dentry); -+ -+out: -+ if (err == -ENOTEMPTY) { -+ AuWarn1("renaming dir who has child(ren) on multiple branches," -+ " is not supported\n"); -+ err = -EXDEV; -+ } -+ return err; -+} -+ -+/* side effect: sets whlist and h_dentry */ -+static int au_ren_may_dir(struct au_ren_args *a) -+{ -+ int err; -+ unsigned int rdhash; -+ struct dentry *d; -+ -+ d = a->dst_dentry; -+ SiMustAnyLock(d->d_sb); -+ -+ err = 0; -+ if (au_ftest_ren(a->flags, ISDIR) && a->dst_inode) { -+ rdhash = au_sbi(d->d_sb)->si_rdhash; -+ if (!rdhash) -+ rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, d)); -+ err = au_nhash_alloc(&a->whlist, rdhash, GFP_NOFS); -+ if (unlikely(err)) -+ goto out; -+ -+ au_set_dbstart(d, a->dst_bstart); -+ err = may_rename_dstdir(d, &a->whlist); -+ au_set_dbstart(d, a->btgt); -+ } -+ a->dst_h_dentry = au_h_dptr(d, au_dbstart(d)); -+ if (unlikely(err)) -+ goto out; -+ -+ d = a->src_dentry; -+ a->src_h_dentry = au_h_dptr(d, au_dbstart(d)); -+ if (au_ftest_ren(a->flags, ISDIR)) { -+ err = may_rename_srcdir(d, a->btgt); -+ if (unlikely(err)) { -+ au_nhash_wh_free(&a->whlist); -+ a->whlist.nh_num = 0; -+ } -+ } -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * simple tests for rename. -+ * following the checks in vfs, plus the parent-child relationship. -+ */ -+static int au_may_ren(struct au_ren_args *a) -+{ -+ int err, isdir; -+ struct inode *h_inode; -+ -+ if (a->src_bstart == a->btgt) { -+ err = au_may_del(a->src_dentry, a->btgt, a->src_h_parent, -+ au_ftest_ren(a->flags, ISDIR)); -+ if (unlikely(err)) -+ goto out; -+ err = -EINVAL; -+ if (unlikely(a->src_h_dentry == a->h_trap)) -+ goto out; -+ } -+ -+ err = 0; -+ if (a->dst_bstart != a->btgt) -+ goto out; -+ -+ err = -ENOTEMPTY; -+ if (unlikely(a->dst_h_dentry == a->h_trap)) -+ goto out; -+ -+ err = -EIO; -+ h_inode = a->dst_h_dentry->d_inode; -+ isdir = !!au_ftest_ren(a->flags, ISDIR); -+ if (!a->dst_dentry->d_inode) { -+ if (unlikely(h_inode)) -+ goto out; -+ err = au_may_add(a->dst_dentry, a->btgt, a->dst_h_parent, -+ isdir); -+ } else { -+ if (unlikely(!h_inode || !h_inode->i_nlink)) -+ goto out; -+ err = au_may_del(a->dst_dentry, a->btgt, a->dst_h_parent, -+ isdir); -+ if (unlikely(err)) -+ goto out; -+ } -+ -+out: -+ if (unlikely(err == -ENOENT || err == -EEXIST)) -+ err = -EIO; -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * locking order -+ * (VFS) -+ * - src_dir and dir by lock_rename() -+ * - inode if exitsts -+ * (aufs) -+ * - lock all -+ * + src_dentry and dentry by aufs_read_and_write_lock2() which calls, -+ * + si_read_lock -+ * + di_write_lock2_child() -+ * + di_write_lock_child() -+ * + ii_write_lock_child() -+ * + di_write_lock_child2() -+ * + ii_write_lock_child2() -+ * + src_parent and parent -+ * + di_write_lock_parent() -+ * + ii_write_lock_parent() -+ * + di_write_lock_parent2() -+ * + ii_write_lock_parent2() -+ * + lower src_dir and dir by vfsub_lock_rename() -+ * + verify the every relationships between child and parent. if any -+ * of them failed, unlock all and return -EBUSY. -+ */ -+static void au_ren_unlock(struct au_ren_args *a) -+{ -+ vfsub_unlock_rename(a->src_h_parent, a->src_hdir, -+ a->dst_h_parent, a->dst_hdir); -+ if (au_ftest_ren(a->flags, MNT_WRITE)) -+ vfsub_mnt_drop_write(au_br_mnt(a->br)); -+} -+ -+static int au_ren_lock(struct au_ren_args *a) -+{ -+ int err; -+ unsigned int udba; -+ -+ err = 0; -+ a->src_h_parent = au_h_dptr(a->src_parent, a->btgt); -+ a->src_hdir = au_hi(a->src_dir, a->btgt); -+ a->dst_h_parent = au_h_dptr(a->dst_parent, a->btgt); -+ a->dst_hdir = au_hi(a->dst_dir, a->btgt); -+ -+ err = vfsub_mnt_want_write(au_br_mnt(a->br)); -+ if (unlikely(err)) -+ goto out; -+ au_fset_ren(a->flags, MNT_WRITE); -+ a->h_trap = vfsub_lock_rename(a->src_h_parent, a->src_hdir, -+ a->dst_h_parent, a->dst_hdir); -+ udba = au_opt_udba(a->src_dentry->d_sb); -+ if (unlikely(a->src_hdir->hi_inode != a->src_h_parent->d_inode -+ || a->dst_hdir->hi_inode != a->dst_h_parent->d_inode)) -+ err = au_busy_or_stale(); -+ if (!err && au_dbstart(a->src_dentry) == a->btgt) -+ err = au_h_verify(a->src_h_dentry, udba, -+ a->src_h_parent->d_inode, a->src_h_parent, -+ a->br); -+ if (!err && au_dbstart(a->dst_dentry) == a->btgt) -+ err = au_h_verify(a->dst_h_dentry, udba, -+ a->dst_h_parent->d_inode, a->dst_h_parent, -+ a->br); -+ if (!err) -+ goto out; /* success */ -+ -+ err = au_busy_or_stale(); -+ au_ren_unlock(a); -+ -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void au_ren_refresh_dir(struct au_ren_args *a) -+{ -+ struct inode *dir; -+ -+ dir = a->dst_dir; -+ dir->i_version++; -+ if (au_ftest_ren(a->flags, ISDIR)) { -+ /* is this updating defined in POSIX? */ -+ au_cpup_attr_timesizes(a->src_inode); -+ au_cpup_attr_nlink(dir, /*force*/1); -+ } -+ -+ au_dir_ts(dir, a->btgt); -+ -+ if (au_ftest_ren(a->flags, ISSAMEDIR)) -+ return; -+ -+ dir = a->src_dir; -+ dir->i_version++; -+ if (au_ftest_ren(a->flags, ISDIR)) -+ au_cpup_attr_nlink(dir, /*force*/1); -+ au_dir_ts(dir, a->btgt); -+} -+ -+static void au_ren_refresh(struct au_ren_args *a) -+{ -+ aufs_bindex_t bend, bindex; -+ struct dentry *d, *h_d; -+ struct inode *i, *h_i; -+ struct super_block *sb; -+ -+ d = a->dst_dentry; -+ d_drop(d); -+ if (a->h_dst) -+ /* already dget-ed by au_ren_or_cpup() */ -+ au_set_h_dptr(d, a->btgt, a->h_dst); -+ -+ i = a->dst_inode; -+ if (i) { -+ if (!au_ftest_ren(a->flags, ISDIR)) -+ vfsub_drop_nlink(i); -+ else { -+ vfsub_dead_dir(i); -+ au_cpup_attr_timesizes(i); -+ } -+ au_update_dbrange(d, /*do_put_zero*/1); -+ } else { -+ bend = a->btgt; -+ for (bindex = au_dbstart(d); bindex < bend; bindex++) -+ au_set_h_dptr(d, bindex, NULL); -+ bend = au_dbend(d); -+ for (bindex = a->btgt + 1; bindex <= bend; bindex++) -+ au_set_h_dptr(d, bindex, NULL); -+ au_update_dbrange(d, /*do_put_zero*/0); -+ } -+ -+ d = a->src_dentry; -+ au_set_dbwh(d, -1); -+ bend = au_dbend(d); -+ for (bindex = a->btgt + 1; bindex <= bend; bindex++) { -+ h_d = au_h_dptr(d, bindex); -+ if (h_d) -+ au_set_h_dptr(d, bindex, NULL); -+ } -+ au_set_dbend(d, a->btgt); -+ -+ sb = d->d_sb; -+ i = a->src_inode; -+ if (au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(i)) -+ return; /* success */ -+ -+ bend = au_ibend(i); -+ for (bindex = a->btgt + 1; bindex <= bend; bindex++) { -+ h_i = au_h_iptr(i, bindex); -+ if (h_i) { -+ au_xino_write(sb, bindex, h_i->i_ino, /*ino*/0); -+ /* ignore this error */ -+ au_set_h_iptr(i, bindex, NULL, 0); -+ } -+ } -+ au_set_ibend(i, a->btgt); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* mainly for link(2) and rename(2) */ -+int au_wbr(struct dentry *dentry, aufs_bindex_t btgt) -+{ -+ aufs_bindex_t bdiropq, bwh; -+ struct dentry *parent; -+ struct au_branch *br; -+ -+ parent = dentry->d_parent; -+ IMustLock(parent->d_inode); /* dir is locked */ -+ -+ bdiropq = au_dbdiropq(parent); -+ bwh = au_dbwh(dentry); -+ br = au_sbr(dentry->d_sb, btgt); -+ if (au_br_rdonly(br) -+ || (0 <= bdiropq && bdiropq < btgt) -+ || (0 <= bwh && bwh < btgt)) -+ btgt = -1; -+ -+ AuDbg("btgt %d\n", btgt); -+ return btgt; -+} -+ -+/* sets src_bstart, dst_bstart and btgt */ -+static int au_ren_wbr(struct au_ren_args *a) -+{ -+ int err; -+ struct au_wr_dir_args wr_dir_args = { -+ /* .force_btgt = -1, */ -+ .flags = AuWrDir_ADD_ENTRY -+ }; -+ -+ a->src_bstart = au_dbstart(a->src_dentry); -+ a->dst_bstart = au_dbstart(a->dst_dentry); -+ if (au_ftest_ren(a->flags, ISDIR)) -+ au_fset_wrdir(wr_dir_args.flags, ISDIR); -+ wr_dir_args.force_btgt = a->src_bstart; -+ if (a->dst_inode && a->dst_bstart < a->src_bstart) -+ wr_dir_args.force_btgt = a->dst_bstart; -+ wr_dir_args.force_btgt = au_wbr(a->dst_dentry, wr_dir_args.force_btgt); -+ err = au_wr_dir(a->dst_dentry, a->src_dentry, &wr_dir_args); -+ a->btgt = err; -+ -+ return err; -+} -+ -+static void au_ren_dt(struct au_ren_args *a) -+{ -+ a->h_path.dentry = a->src_h_parent; -+ au_dtime_store(a->src_dt + AuPARENT, a->src_parent, &a->h_path); -+ if (!au_ftest_ren(a->flags, ISSAMEDIR)) { -+ a->h_path.dentry = a->dst_h_parent; -+ au_dtime_store(a->dst_dt + AuPARENT, a->dst_parent, &a->h_path); -+ } -+ -+ au_fclr_ren(a->flags, DT_DSTDIR); -+ if (!au_ftest_ren(a->flags, ISDIR)) -+ return; -+ -+ a->h_path.dentry = a->src_h_dentry; -+ au_dtime_store(a->src_dt + AuCHILD, a->src_dentry, &a->h_path); -+ if (a->dst_h_dentry->d_inode) { -+ au_fset_ren(a->flags, DT_DSTDIR); -+ a->h_path.dentry = a->dst_h_dentry; -+ au_dtime_store(a->dst_dt + AuCHILD, a->dst_dentry, &a->h_path); -+ } -+} -+ -+static void au_ren_rev_dt(int err, struct au_ren_args *a) -+{ -+ struct dentry *h_d; -+ struct mutex *h_mtx; -+ -+ au_dtime_revert(a->src_dt + AuPARENT); -+ if (!au_ftest_ren(a->flags, ISSAMEDIR)) -+ au_dtime_revert(a->dst_dt + AuPARENT); -+ -+ if (au_ftest_ren(a->flags, ISDIR) && err != -EIO) { -+ h_d = a->src_dt[AuCHILD].dt_h_path.dentry; -+ h_mtx = &h_d->d_inode->i_mutex; -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); -+ au_dtime_revert(a->src_dt + AuCHILD); -+ mutex_unlock(h_mtx); -+ -+ if (au_ftest_ren(a->flags, DT_DSTDIR)) { -+ h_d = a->dst_dt[AuCHILD].dt_h_path.dentry; -+ h_mtx = &h_d->d_inode->i_mutex; -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); -+ au_dtime_revert(a->dst_dt + AuCHILD); -+ mutex_unlock(h_mtx); -+ } -+ } -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry, -+ struct inode *_dst_dir, struct dentry *_dst_dentry) -+{ -+ int err, flags; -+ /* reduce stack space */ -+ struct au_ren_args *a; -+ -+ AuDbg("%pd, %pd\n", _src_dentry, _dst_dentry); -+ IMustLock(_src_dir); -+ IMustLock(_dst_dir); -+ -+ err = -ENOMEM; -+ BUILD_BUG_ON(sizeof(*a) > PAGE_SIZE); -+ a = kzalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ -+ a->src_dir = _src_dir; -+ a->src_dentry = _src_dentry; -+ a->src_inode = a->src_dentry->d_inode; -+ a->src_parent = a->src_dentry->d_parent; /* dir inode is locked */ -+ a->dst_dir = _dst_dir; -+ a->dst_dentry = _dst_dentry; -+ a->dst_inode = a->dst_dentry->d_inode; -+ a->dst_parent = a->dst_dentry->d_parent; /* dir inode is locked */ -+ if (a->dst_inode) { -+ IMustLock(a->dst_inode); -+ au_igrab(a->dst_inode); -+ } -+ -+ err = -ENOTDIR; -+ flags = AuLock_FLUSH | AuLock_NOPLM | AuLock_GEN; -+ if (d_is_dir(a->src_dentry)) { -+ au_fset_ren(a->flags, ISDIR); -+ if (unlikely(d_is_positive(a->dst_dentry) -+ && !d_is_dir(a->dst_dentry))) -+ goto out_free; -+ flags |= AuLock_DIRS; -+ } -+ err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry, flags); -+ if (unlikely(err)) -+ goto out_free; -+ -+ err = au_d_hashed_positive(a->src_dentry); -+ if (unlikely(err)) -+ goto out_unlock; -+ err = -ENOENT; -+ if (a->dst_inode) { -+ /* -+ * If it is a dir, VFS unhash dst_dentry before this -+ * function. It means we cannot rely upon d_unhashed(). -+ */ -+ if (unlikely(!a->dst_inode->i_nlink)) -+ goto out_unlock; -+ if (!S_ISDIR(a->dst_inode->i_mode)) { -+ err = au_d_hashed_positive(a->dst_dentry); -+ if (unlikely(err)) -+ goto out_unlock; -+ } else if (unlikely(IS_DEADDIR(a->dst_inode))) -+ goto out_unlock; -+ } else if (unlikely(d_unhashed(a->dst_dentry))) -+ goto out_unlock; -+ -+ /* -+ * is it possible? -+ * yes, it happened (in linux-3.3-rcN) but I don't know why. -+ * there may exist a problem somewhere else. -+ */ -+ err = -EINVAL; -+ if (unlikely(a->dst_parent->d_inode == a->src_dentry->d_inode)) -+ goto out_unlock; -+ -+ au_fset_ren(a->flags, ISSAMEDIR); /* temporary */ -+ di_write_lock_parent(a->dst_parent); -+ -+ /* which branch we process */ -+ err = au_ren_wbr(a); -+ if (unlikely(err < 0)) -+ goto out_parent; -+ a->br = au_sbr(a->dst_dentry->d_sb, a->btgt); -+ a->h_path.mnt = au_br_mnt(a->br); -+ -+ /* are they available to be renamed */ -+ err = au_ren_may_dir(a); -+ if (unlikely(err)) -+ goto out_children; -+ -+ /* prepare the writable parent dir on the same branch */ -+ if (a->dst_bstart == a->btgt) { -+ au_fset_ren(a->flags, WHDST); -+ } else { -+ err = au_cpup_dirs(a->dst_dentry, a->btgt); -+ if (unlikely(err)) -+ goto out_children; -+ } -+ -+ if (a->src_dir != a->dst_dir) { -+ /* -+ * this temporary unlock is safe, -+ * because both dir->i_mutex are locked. -+ */ -+ di_write_unlock(a->dst_parent); -+ di_write_lock_parent(a->src_parent); -+ err = au_wr_dir_need_wh(a->src_dentry, -+ au_ftest_ren(a->flags, ISDIR), -+ &a->btgt); -+ di_write_unlock(a->src_parent); -+ di_write_lock2_parent(a->src_parent, a->dst_parent, /*isdir*/1); -+ au_fclr_ren(a->flags, ISSAMEDIR); -+ } else -+ err = au_wr_dir_need_wh(a->src_dentry, -+ au_ftest_ren(a->flags, ISDIR), -+ &a->btgt); -+ if (unlikely(err < 0)) -+ goto out_children; -+ if (err) -+ au_fset_ren(a->flags, WHSRC); -+ -+ /* cpup src */ -+ if (a->src_bstart != a->btgt) { -+ struct au_pin pin; -+ -+ err = au_pin(&pin, a->src_dentry, a->btgt, -+ au_opt_udba(a->src_dentry->d_sb), -+ AuPin_DI_LOCKED | AuPin_MNT_WRITE); -+ if (!err) { -+ struct au_cp_generic cpg = { -+ .dentry = a->src_dentry, -+ .bdst = a->btgt, -+ .bsrc = a->src_bstart, -+ .len = -1, -+ .pin = &pin, -+ .flags = AuCpup_DTIME | AuCpup_HOPEN -+ }; -+ AuDebugOn(au_dbstart(a->src_dentry) != a->src_bstart); -+ err = au_sio_cpup_simple(&cpg); -+ au_unpin(&pin); -+ } -+ if (unlikely(err)) -+ goto out_children; -+ a->src_bstart = a->btgt; -+ a->src_h_dentry = au_h_dptr(a->src_dentry, a->btgt); -+ au_fset_ren(a->flags, WHSRC); -+ } -+ -+ /* lock them all */ -+ err = au_ren_lock(a); -+ if (unlikely(err)) -+ /* leave the copied-up one */ -+ goto out_children; -+ -+ if (!au_opt_test(au_mntflags(a->dst_dir->i_sb), UDBA_NONE)) -+ err = au_may_ren(a); -+ else if (unlikely(a->dst_dentry->d_name.len > AUFS_MAX_NAMELEN)) -+ err = -ENAMETOOLONG; -+ if (unlikely(err)) -+ goto out_hdir; -+ -+ /* store timestamps to be revertible */ -+ au_ren_dt(a); -+ -+ /* here we go */ -+ err = do_rename(a); -+ if (unlikely(err)) -+ goto out_dt; -+ -+ /* update dir attributes */ -+ au_ren_refresh_dir(a); -+ -+ /* dput/iput all lower dentries */ -+ au_ren_refresh(a); -+ -+ goto out_hdir; /* success */ -+ -+out_dt: -+ au_ren_rev_dt(err, a); -+out_hdir: -+ au_ren_unlock(a); -+out_children: -+ au_nhash_wh_free(&a->whlist); -+ if (err && a->dst_inode && a->dst_bstart != a->btgt) { -+ AuDbg("bstart %d, btgt %d\n", a->dst_bstart, a->btgt); -+ au_set_h_dptr(a->dst_dentry, a->btgt, NULL); -+ au_set_dbstart(a->dst_dentry, a->dst_bstart); -+ } -+out_parent: -+ if (!err) -+ d_move(a->src_dentry, a->dst_dentry); -+ else { -+ au_update_dbstart(a->dst_dentry); -+ if (!a->dst_inode) -+ d_drop(a->dst_dentry); -+ } -+ if (au_ftest_ren(a->flags, ISSAMEDIR)) -+ di_write_unlock(a->dst_parent); -+ else -+ di_write_unlock2(a->src_parent, a->dst_parent); -+out_unlock: -+ aufs_read_and_write_unlock2(a->dst_dentry, a->src_dentry); -+out_free: -+ iput(a->dst_inode); -+ if (a->thargs) -+ au_whtmp_rmdir_free(a->thargs); -+ kfree(a); -+out: -+ AuTraceErr(err); -+ return err; -+} -diff --git a/fs/aufs/iinfo.c b/fs/aufs/iinfo.c -new file mode 100644 -index 0000000..f889aba ---- /dev/null -+++ b/fs/aufs/iinfo.c -@@ -0,0 +1,277 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * inode private data -+ */ -+ -+#include "aufs.h" -+ -+struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex) -+{ -+ struct inode *h_inode; -+ -+ IiMustAnyLock(inode); -+ -+ h_inode = au_ii(inode)->ii_hinode[0 + bindex].hi_inode; -+ AuDebugOn(h_inode && atomic_read(&h_inode->i_count) <= 0); -+ return h_inode; -+} -+ -+/* todo: hard/soft set? */ -+void au_hiput(struct au_hinode *hinode) -+{ -+ au_hn_free(hinode); -+ dput(hinode->hi_whdentry); -+ iput(hinode->hi_inode); -+} -+ -+unsigned int au_hi_flags(struct inode *inode, int isdir) -+{ -+ unsigned int flags; -+ const unsigned int mnt_flags = au_mntflags(inode->i_sb); -+ -+ flags = 0; -+ if (au_opt_test(mnt_flags, XINO)) -+ au_fset_hi(flags, XINO); -+ if (isdir && au_opt_test(mnt_flags, UDBA_HNOTIFY)) -+ au_fset_hi(flags, HNOTIFY); -+ return flags; -+} -+ -+void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex, -+ struct inode *h_inode, unsigned int flags) -+{ -+ struct au_hinode *hinode; -+ struct inode *hi; -+ struct au_iinfo *iinfo = au_ii(inode); -+ -+ IiMustWriteLock(inode); -+ -+ hinode = iinfo->ii_hinode + bindex; -+ hi = hinode->hi_inode; -+ AuDebugOn(h_inode && atomic_read(&h_inode->i_count) <= 0); -+ -+ if (hi) -+ au_hiput(hinode); -+ hinode->hi_inode = h_inode; -+ if (h_inode) { -+ int err; -+ struct super_block *sb = inode->i_sb; -+ struct au_branch *br; -+ -+ AuDebugOn(inode->i_mode -+ && (h_inode->i_mode & S_IFMT) -+ != (inode->i_mode & S_IFMT)); -+ if (bindex == iinfo->ii_bstart) -+ au_cpup_igen(inode, h_inode); -+ br = au_sbr(sb, bindex); -+ hinode->hi_id = br->br_id; -+ if (au_ftest_hi(flags, XINO)) { -+ err = au_xino_write(sb, bindex, h_inode->i_ino, -+ inode->i_ino); -+ if (unlikely(err)) -+ AuIOErr1("failed au_xino_write() %d\n", err); -+ } -+ -+ if (au_ftest_hi(flags, HNOTIFY) -+ && au_br_hnotifyable(br->br_perm)) { -+ err = au_hn_alloc(hinode, inode); -+ if (unlikely(err)) -+ AuIOErr1("au_hn_alloc() %d\n", err); -+ } -+ } -+} -+ -+void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex, -+ struct dentry *h_wh) -+{ -+ struct au_hinode *hinode; -+ -+ IiMustWriteLock(inode); -+ -+ hinode = au_ii(inode)->ii_hinode + bindex; -+ AuDebugOn(hinode->hi_whdentry); -+ hinode->hi_whdentry = h_wh; -+} -+ -+void au_update_iigen(struct inode *inode, int half) -+{ -+ struct au_iinfo *iinfo; -+ struct au_iigen *iigen; -+ unsigned int sigen; -+ -+ sigen = au_sigen(inode->i_sb); -+ iinfo = au_ii(inode); -+ iigen = &iinfo->ii_generation; -+ spin_lock(&iigen->ig_spin); -+ iigen->ig_generation = sigen; -+ if (half) -+ au_ig_fset(iigen->ig_flags, HALF_REFRESHED); -+ else -+ au_ig_fclr(iigen->ig_flags, HALF_REFRESHED); -+ spin_unlock(&iigen->ig_spin); -+} -+ -+/* it may be called at remount time, too */ -+void au_update_ibrange(struct inode *inode, int do_put_zero) -+{ -+ struct au_iinfo *iinfo; -+ aufs_bindex_t bindex, bend; -+ -+ iinfo = au_ii(inode); -+ if (!iinfo) -+ return; -+ -+ IiMustWriteLock(inode); -+ -+ if (do_put_zero && iinfo->ii_bstart >= 0) { -+ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; -+ bindex++) { -+ struct inode *h_i; -+ -+ h_i = iinfo->ii_hinode[0 + bindex].hi_inode; -+ if (h_i -+ && !h_i->i_nlink -+ && !(h_i->i_state & I_LINKABLE)) -+ au_set_h_iptr(inode, bindex, NULL, 0); -+ } -+ } -+ -+ iinfo->ii_bstart = -1; -+ iinfo->ii_bend = -1; -+ bend = au_sbend(inode->i_sb); -+ for (bindex = 0; bindex <= bend; bindex++) -+ if (iinfo->ii_hinode[0 + bindex].hi_inode) { -+ iinfo->ii_bstart = bindex; -+ break; -+ } -+ if (iinfo->ii_bstart >= 0) -+ for (bindex = bend; bindex >= iinfo->ii_bstart; bindex--) -+ if (iinfo->ii_hinode[0 + bindex].hi_inode) { -+ iinfo->ii_bend = bindex; -+ break; -+ } -+ AuDebugOn(iinfo->ii_bstart > iinfo->ii_bend); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void au_icntnr_init_once(void *_c) -+{ -+ struct au_icntnr *c = _c; -+ struct au_iinfo *iinfo = &c->iinfo; -+ static struct lock_class_key aufs_ii; -+ -+ spin_lock_init(&iinfo->ii_generation.ig_spin); -+ au_rw_init(&iinfo->ii_rwsem); -+ au_rw_class(&iinfo->ii_rwsem, &aufs_ii); -+ inode_init_once(&c->vfs_inode); -+} -+ -+int au_iinfo_init(struct inode *inode) -+{ -+ struct au_iinfo *iinfo; -+ struct super_block *sb; -+ int nbr, i; -+ -+ sb = inode->i_sb; -+ iinfo = &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo); -+ nbr = au_sbend(sb) + 1; -+ if (unlikely(nbr <= 0)) -+ nbr = 1; -+ iinfo->ii_hinode = kcalloc(nbr, sizeof(*iinfo->ii_hinode), GFP_NOFS); -+ if (iinfo->ii_hinode) { -+ au_ninodes_inc(sb); -+ for (i = 0; i < nbr; i++) -+ iinfo->ii_hinode[i].hi_id = -1; -+ -+ iinfo->ii_generation.ig_generation = au_sigen(sb); -+ iinfo->ii_bstart = -1; -+ iinfo->ii_bend = -1; -+ iinfo->ii_vdir = NULL; -+ return 0; -+ } -+ return -ENOMEM; -+} -+ -+int au_ii_realloc(struct au_iinfo *iinfo, int nbr) -+{ -+ int err, sz; -+ struct au_hinode *hip; -+ -+ AuRwMustWriteLock(&iinfo->ii_rwsem); -+ -+ err = -ENOMEM; -+ sz = sizeof(*hip) * (iinfo->ii_bend + 1); -+ if (!sz) -+ sz = sizeof(*hip); -+ hip = au_kzrealloc(iinfo->ii_hinode, sz, sizeof(*hip) * nbr, GFP_NOFS); -+ if (hip) { -+ iinfo->ii_hinode = hip; -+ err = 0; -+ } -+ -+ return err; -+} -+ -+void au_iinfo_fin(struct inode *inode) -+{ -+ struct au_iinfo *iinfo; -+ struct au_hinode *hi; -+ struct super_block *sb; -+ aufs_bindex_t bindex, bend; -+ const unsigned char unlinked = !inode->i_nlink; -+ -+ iinfo = au_ii(inode); -+ /* bad_inode case */ -+ if (!iinfo) -+ return; -+ -+ sb = inode->i_sb; -+ au_ninodes_dec(sb); -+ if (si_pid_test(sb)) -+ au_xino_delete_inode(inode, unlinked); -+ else { -+ /* -+ * it is safe to hide the dependency between sbinfo and -+ * sb->s_umount. -+ */ -+ lockdep_off(); -+ si_noflush_read_lock(sb); -+ au_xino_delete_inode(inode, unlinked); -+ si_read_unlock(sb); -+ lockdep_on(); -+ } -+ -+ if (iinfo->ii_vdir) -+ au_vdir_free(iinfo->ii_vdir); -+ -+ bindex = iinfo->ii_bstart; -+ if (bindex >= 0) { -+ hi = iinfo->ii_hinode + bindex; -+ bend = iinfo->ii_bend; -+ while (bindex++ <= bend) { -+ if (hi->hi_inode) -+ au_hiput(hi); -+ hi++; -+ } -+ } -+ kfree(iinfo->ii_hinode); -+ iinfo->ii_hinode = NULL; -+ AuRwDestroy(&iinfo->ii_rwsem); -+} -diff --git a/fs/aufs/inode.c b/fs/aufs/inode.c -new file mode 100644 -index 0000000..75ec2e5 ---- /dev/null -+++ b/fs/aufs/inode.c -@@ -0,0 +1,522 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * inode functions -+ */ -+ -+#include "aufs.h" -+ -+struct inode *au_igrab(struct inode *inode) -+{ -+ if (inode) { -+ AuDebugOn(!atomic_read(&inode->i_count)); -+ ihold(inode); -+ } -+ return inode; -+} -+ -+static void au_refresh_hinode_attr(struct inode *inode, int do_version) -+{ -+ au_cpup_attr_all(inode, /*force*/0); -+ au_update_iigen(inode, /*half*/1); -+ if (do_version) -+ inode->i_version++; -+} -+ -+static int au_ii_refresh(struct inode *inode, int *update) -+{ -+ int err, e; -+ umode_t type; -+ aufs_bindex_t bindex, new_bindex; -+ struct super_block *sb; -+ struct au_iinfo *iinfo; -+ struct au_hinode *p, *q, tmp; -+ -+ IiMustWriteLock(inode); -+ -+ *update = 0; -+ sb = inode->i_sb; -+ type = inode->i_mode & S_IFMT; -+ iinfo = au_ii(inode); -+ err = au_ii_realloc(iinfo, au_sbend(sb) + 1); -+ if (unlikely(err)) -+ goto out; -+ -+ AuDebugOn(iinfo->ii_bstart < 0); -+ p = iinfo->ii_hinode + iinfo->ii_bstart; -+ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; -+ bindex++, p++) { -+ if (!p->hi_inode) -+ continue; -+ -+ AuDebugOn(type != (p->hi_inode->i_mode & S_IFMT)); -+ new_bindex = au_br_index(sb, p->hi_id); -+ if (new_bindex == bindex) -+ continue; -+ -+ if (new_bindex < 0) { -+ *update = 1; -+ au_hiput(p); -+ p->hi_inode = NULL; -+ continue; -+ } -+ -+ if (new_bindex < iinfo->ii_bstart) -+ iinfo->ii_bstart = new_bindex; -+ if (iinfo->ii_bend < new_bindex) -+ iinfo->ii_bend = new_bindex; -+ /* swap two lower inode, and loop again */ -+ q = iinfo->ii_hinode + new_bindex; -+ tmp = *q; -+ *q = *p; -+ *p = tmp; -+ if (tmp.hi_inode) { -+ bindex--; -+ p--; -+ } -+ } -+ au_update_ibrange(inode, /*do_put_zero*/0); -+ e = au_dy_irefresh(inode); -+ if (unlikely(e && !err)) -+ err = e; -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+void au_refresh_iop(struct inode *inode, int force_getattr) -+{ -+ int type; -+ struct au_sbinfo *sbi = au_sbi(inode->i_sb); -+ const struct inode_operations *iop -+ = force_getattr ? aufs_iop : sbi->si_iop_array; -+ -+ if (inode->i_op == iop) -+ return; -+ -+ switch (inode->i_mode & S_IFMT) { -+ case S_IFDIR: -+ type = AuIop_DIR; -+ break; -+ case S_IFLNK: -+ type = AuIop_SYMLINK; -+ break; -+ default: -+ type = AuIop_OTHER; -+ break; -+ } -+ -+ inode->i_op = iop + type; -+ /* unnecessary smp_wmb() */ -+} -+ -+int au_refresh_hinode_self(struct inode *inode) -+{ -+ int err, update; -+ -+ err = au_ii_refresh(inode, &update); -+ if (!err) -+ au_refresh_hinode_attr(inode, update && S_ISDIR(inode->i_mode)); -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+int au_refresh_hinode(struct inode *inode, struct dentry *dentry) -+{ -+ int err, e, update; -+ unsigned int flags; -+ umode_t mode; -+ aufs_bindex_t bindex, bend; -+ unsigned char isdir; -+ struct au_hinode *p; -+ struct au_iinfo *iinfo; -+ -+ err = au_ii_refresh(inode, &update); -+ if (unlikely(err)) -+ goto out; -+ -+ update = 0; -+ iinfo = au_ii(inode); -+ p = iinfo->ii_hinode + iinfo->ii_bstart; -+ mode = (inode->i_mode & S_IFMT); -+ isdir = S_ISDIR(mode); -+ flags = au_hi_flags(inode, isdir); -+ bend = au_dbend(dentry); -+ for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) { -+ struct inode *h_i; -+ struct dentry *h_d; -+ -+ h_d = au_h_dptr(dentry, bindex); -+ if (!h_d || !h_d->d_inode) -+ continue; -+ -+ AuDebugOn(mode != (h_d->d_inode->i_mode & S_IFMT)); -+ if (iinfo->ii_bstart <= bindex && bindex <= iinfo->ii_bend) { -+ h_i = au_h_iptr(inode, bindex); -+ if (h_i) { -+ if (h_i == h_d->d_inode) -+ continue; -+ err = -EIO; -+ break; -+ } -+ } -+ if (bindex < iinfo->ii_bstart) -+ iinfo->ii_bstart = bindex; -+ if (iinfo->ii_bend < bindex) -+ iinfo->ii_bend = bindex; -+ au_set_h_iptr(inode, bindex, au_igrab(h_d->d_inode), flags); -+ update = 1; -+ } -+ au_update_ibrange(inode, /*do_put_zero*/0); -+ e = au_dy_irefresh(inode); -+ if (unlikely(e && !err)) -+ err = e; -+ if (!err) -+ au_refresh_hinode_attr(inode, update && isdir); -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+static int set_inode(struct inode *inode, struct dentry *dentry) -+{ -+ int err; -+ unsigned int flags; -+ umode_t mode; -+ aufs_bindex_t bindex, bstart, btail; -+ unsigned char isdir; -+ struct dentry *h_dentry; -+ struct inode *h_inode; -+ struct au_iinfo *iinfo; -+ struct inode_operations *iop; -+ -+ IiMustWriteLock(inode); -+ -+ err = 0; -+ isdir = 0; -+ iop = au_sbi(inode->i_sb)->si_iop_array; -+ bstart = au_dbstart(dentry); -+ h_inode = au_h_dptr(dentry, bstart)->d_inode; -+ mode = h_inode->i_mode; -+ switch (mode & S_IFMT) { -+ case S_IFREG: -+ btail = au_dbtail(dentry); -+ inode->i_op = iop + AuIop_OTHER; -+ inode->i_fop = &aufs_file_fop; -+ err = au_dy_iaop(inode, bstart, h_inode); -+ if (unlikely(err)) -+ goto out; -+ break; -+ case S_IFDIR: -+ isdir = 1; -+ btail = au_dbtaildir(dentry); -+ inode->i_op = iop + AuIop_DIR; -+ inode->i_fop = &aufs_dir_fop; -+ break; -+ case S_IFLNK: -+ btail = au_dbtail(dentry); -+ inode->i_op = iop + AuIop_SYMLINK; -+ break; -+ case S_IFBLK: -+ case S_IFCHR: -+ case S_IFIFO: -+ case S_IFSOCK: -+ btail = au_dbtail(dentry); -+ inode->i_op = iop + AuIop_OTHER; -+ init_special_inode(inode, mode, h_inode->i_rdev); -+ break; -+ default: -+ AuIOErr("Unknown file type 0%o\n", mode); -+ err = -EIO; -+ goto out; -+ } -+ -+ /* do not set hnotify for whiteouted dirs (SHWH mode) */ -+ flags = au_hi_flags(inode, isdir); -+ if (au_opt_test(au_mntflags(dentry->d_sb), SHWH) -+ && au_ftest_hi(flags, HNOTIFY) -+ && dentry->d_name.len > AUFS_WH_PFX_LEN -+ && !memcmp(dentry->d_name.name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) -+ au_fclr_hi(flags, HNOTIFY); -+ iinfo = au_ii(inode); -+ iinfo->ii_bstart = bstart; -+ iinfo->ii_bend = btail; -+ for (bindex = bstart; bindex <= btail; bindex++) { -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (h_dentry) -+ au_set_h_iptr(inode, bindex, -+ au_igrab(h_dentry->d_inode), flags); -+ } -+ au_cpup_attr_all(inode, /*force*/1); -+ /* -+ * to force calling aufs_get_acl() every time, -+ * do not call cache_no_acl() for aufs inode. -+ */ -+ -+out: -+ return err; -+} -+ -+/* -+ * successful returns with iinfo write_locked -+ * minus: errno -+ * zero: success, matched -+ * plus: no error, but unmatched -+ */ -+static int reval_inode(struct inode *inode, struct dentry *dentry) -+{ -+ int err; -+ unsigned int gen, igflags; -+ aufs_bindex_t bindex, bend; -+ struct inode *h_inode, *h_dinode; -+ -+ /* -+ * before this function, if aufs got any iinfo lock, it must be only -+ * one, the parent dir. -+ * it can happen by UDBA and the obsoleted inode number. -+ */ -+ err = -EIO; -+ if (unlikely(inode->i_ino == parent_ino(dentry))) -+ goto out; -+ -+ err = 1; -+ ii_write_lock_new_child(inode); -+ h_dinode = au_h_dptr(dentry, au_dbstart(dentry))->d_inode; -+ bend = au_ibend(inode); -+ for (bindex = au_ibstart(inode); bindex <= bend; bindex++) { -+ h_inode = au_h_iptr(inode, bindex); -+ if (!h_inode || h_inode != h_dinode) -+ continue; -+ -+ err = 0; -+ gen = au_iigen(inode, &igflags); -+ if (gen == au_digen(dentry) -+ && !au_ig_ftest(igflags, HALF_REFRESHED)) -+ break; -+ -+ /* fully refresh inode using dentry */ -+ err = au_refresh_hinode(inode, dentry); -+ if (!err) -+ au_update_iigen(inode, /*half*/0); -+ break; -+ } -+ -+ if (unlikely(err)) -+ ii_write_unlock(inode); -+out: -+ return err; -+} -+ -+int au_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, -+ unsigned int d_type, ino_t *ino) -+{ -+ int err; -+ struct mutex *mtx; -+ -+ /* prevent hardlinked inode number from race condition */ -+ mtx = NULL; -+ if (d_type != DT_DIR) { -+ mtx = &au_sbr(sb, bindex)->br_xino.xi_nondir_mtx; -+ mutex_lock(mtx); -+ } -+ err = au_xino_read(sb, bindex, h_ino, ino); -+ if (unlikely(err)) -+ goto out; -+ -+ if (!*ino) { -+ err = -EIO; -+ *ino = au_xino_new_ino(sb); -+ if (unlikely(!*ino)) -+ goto out; -+ err = au_xino_write(sb, bindex, h_ino, *ino); -+ if (unlikely(err)) -+ goto out; -+ } -+ -+out: -+ if (mtx) -+ mutex_unlock(mtx); -+ return err; -+} -+ -+/* successful returns with iinfo write_locked */ -+/* todo: return with unlocked? */ -+struct inode *au_new_inode(struct dentry *dentry, int must_new) -+{ -+ struct inode *inode; -+ struct dentry *h_dentry; -+ struct super_block *sb; -+ struct mutex *mtx; -+ ino_t h_ino, ino; -+ int err; -+ aufs_bindex_t bstart; -+ -+ sb = dentry->d_sb; -+ bstart = au_dbstart(dentry); -+ h_dentry = au_h_dptr(dentry, bstart); -+ h_ino = h_dentry->d_inode->i_ino; -+ -+ /* -+ * stop 'race'-ing between hardlinks under different -+ * parents. -+ */ -+ mtx = NULL; -+ if (!d_is_dir(h_dentry)) -+ mtx = &au_sbr(sb, bstart)->br_xino.xi_nondir_mtx; -+ -+new_ino: -+ if (mtx) -+ mutex_lock(mtx); -+ err = au_xino_read(sb, bstart, h_ino, &ino); -+ inode = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out; -+ -+ if (!ino) { -+ ino = au_xino_new_ino(sb); -+ if (unlikely(!ino)) { -+ inode = ERR_PTR(-EIO); -+ goto out; -+ } -+ } -+ -+ AuDbg("i%lu\n", (unsigned long)ino); -+ inode = au_iget_locked(sb, ino); -+ err = PTR_ERR(inode); -+ if (IS_ERR(inode)) -+ goto out; -+ -+ AuDbg("%lx, new %d\n", inode->i_state, !!(inode->i_state & I_NEW)); -+ if (inode->i_state & I_NEW) { -+ /* verbose coding for lock class name */ -+ if (unlikely(d_is_symlink(h_dentry))) -+ au_rw_class(&au_ii(inode)->ii_rwsem, -+ au_lc_key + AuLcSymlink_IIINFO); -+ else if (unlikely(d_is_dir(h_dentry))) -+ au_rw_class(&au_ii(inode)->ii_rwsem, -+ au_lc_key + AuLcDir_IIINFO); -+ else /* likely */ -+ au_rw_class(&au_ii(inode)->ii_rwsem, -+ au_lc_key + AuLcNonDir_IIINFO); -+ -+ ii_write_lock_new_child(inode); -+ err = set_inode(inode, dentry); -+ if (!err) { -+ unlock_new_inode(inode); -+ goto out; /* success */ -+ } -+ -+ /* -+ * iget_failed() calls iput(), but we need to call -+ * ii_write_unlock() after iget_failed(). so dirty hack for -+ * i_count. -+ */ -+ atomic_inc(&inode->i_count); -+ iget_failed(inode); -+ ii_write_unlock(inode); -+ au_xino_write(sb, bstart, h_ino, /*ino*/0); -+ /* ignore this error */ -+ goto out_iput; -+ } else if (!must_new && !IS_DEADDIR(inode) && inode->i_nlink) { -+ /* -+ * horrible race condition between lookup, readdir and copyup -+ * (or something). -+ */ -+ if (mtx) -+ mutex_unlock(mtx); -+ err = reval_inode(inode, dentry); -+ if (unlikely(err < 0)) { -+ mtx = NULL; -+ goto out_iput; -+ } -+ -+ if (!err) { -+ mtx = NULL; -+ goto out; /* success */ -+ } else if (mtx) -+ mutex_lock(mtx); -+ } -+ -+ if (unlikely(au_test_fs_unique_ino(h_dentry->d_inode))) -+ AuWarn1("Warning: Un-notified UDBA or repeatedly renamed dir," -+ " b%d, %s, %pd, hi%lu, i%lu.\n", -+ bstart, au_sbtype(h_dentry->d_sb), dentry, -+ (unsigned long)h_ino, (unsigned long)ino); -+ ino = 0; -+ err = au_xino_write(sb, bstart, h_ino, /*ino*/0); -+ if (!err) { -+ iput(inode); -+ if (mtx) -+ mutex_unlock(mtx); -+ goto new_ino; -+ } -+ -+out_iput: -+ iput(inode); -+ inode = ERR_PTR(err); -+out: -+ if (mtx) -+ mutex_unlock(mtx); -+ return inode; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int au_test_ro(struct super_block *sb, aufs_bindex_t bindex, -+ struct inode *inode) -+{ -+ int err; -+ struct inode *hi; -+ -+ err = au_br_rdonly(au_sbr(sb, bindex)); -+ -+ /* pseudo-link after flushed may happen out of bounds */ -+ if (!err -+ && inode -+ && au_ibstart(inode) <= bindex -+ && bindex <= au_ibend(inode)) { -+ /* -+ * permission check is unnecessary since vfsub routine -+ * will be called later -+ */ -+ hi = au_h_iptr(inode, bindex); -+ if (hi) -+ err = IS_IMMUTABLE(hi) ? -EROFS : 0; -+ } -+ -+ return err; -+} -+ -+int au_test_h_perm(struct inode *h_inode, int mask) -+{ -+ if (uid_eq(current_fsuid(), GLOBAL_ROOT_UID)) -+ return 0; -+ return inode_permission(h_inode, mask); -+} -+ -+int au_test_h_perm_sio(struct inode *h_inode, int mask) -+{ -+ if (au_test_nfs(h_inode->i_sb) -+ && (mask & MAY_WRITE) -+ && S_ISDIR(h_inode->i_mode)) -+ mask |= MAY_READ; /* force permission check */ -+ return au_test_h_perm(h_inode, mask); -+} -diff --git a/fs/aufs/inode.h b/fs/aufs/inode.h -new file mode 100644 -index 0000000..49d53a2 ---- /dev/null -+++ b/fs/aufs/inode.h -@@ -0,0 +1,686 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * inode operations -+ */ -+ -+#ifndef __AUFS_INODE_H__ -+#define __AUFS_INODE_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+#include "rwsem.h" -+ -+struct vfsmount; -+ -+struct au_hnotify { -+#ifdef CONFIG_AUFS_HNOTIFY -+#ifdef CONFIG_AUFS_HFSNOTIFY -+ /* never use fsnotify_add_vfsmount_mark() */ -+ struct fsnotify_mark hn_mark; -+#endif -+ struct inode *hn_aufs_inode; /* no get/put */ -+#endif -+} ____cacheline_aligned_in_smp; -+ -+struct au_hinode { -+ struct inode *hi_inode; -+ aufs_bindex_t hi_id; -+#ifdef CONFIG_AUFS_HNOTIFY -+ struct au_hnotify *hi_notify; -+#endif -+ -+ /* reference to the copied-up whiteout with get/put */ -+ struct dentry *hi_whdentry; -+}; -+ -+/* ig_flags */ -+#define AuIG_HALF_REFRESHED 1 -+#define au_ig_ftest(flags, name) ((flags) & AuIG_##name) -+#define au_ig_fset(flags, name) \ -+ do { (flags) |= AuIG_##name; } while (0) -+#define au_ig_fclr(flags, name) \ -+ do { (flags) &= ~AuIG_##name; } while (0) -+ -+struct au_iigen { -+ spinlock_t ig_spin; -+ __u32 ig_generation, ig_flags; -+}; -+ -+struct au_vdir; -+struct au_iinfo { -+ struct au_iigen ii_generation; -+ struct super_block *ii_hsb1; /* no get/put */ -+ -+ struct au_rwsem ii_rwsem; -+ aufs_bindex_t ii_bstart, ii_bend; -+ __u32 ii_higen; -+ struct au_hinode *ii_hinode; -+ struct au_vdir *ii_vdir; -+}; -+ -+struct au_icntnr { -+ struct au_iinfo iinfo; -+ struct inode vfs_inode; -+ struct hlist_node plink; -+} ____cacheline_aligned_in_smp; -+ -+/* au_pin flags */ -+#define AuPin_DI_LOCKED 1 -+#define AuPin_MNT_WRITE (1 << 1) -+#define au_ftest_pin(flags, name) ((flags) & AuPin_##name) -+#define au_fset_pin(flags, name) \ -+ do { (flags) |= AuPin_##name; } while (0) -+#define au_fclr_pin(flags, name) \ -+ do { (flags) &= ~AuPin_##name; } while (0) -+ -+struct au_pin { -+ /* input */ -+ struct dentry *dentry; -+ unsigned int udba; -+ unsigned char lsc_di, lsc_hi, flags; -+ aufs_bindex_t bindex; -+ -+ /* output */ -+ struct dentry *parent; -+ struct au_hinode *hdir; -+ struct vfsmount *h_mnt; -+ -+ /* temporary unlock/relock for copyup */ -+ struct dentry *h_dentry, *h_parent; -+ struct au_branch *br; -+ struct task_struct *task; -+}; -+ -+void au_pin_hdir_unlock(struct au_pin *p); -+int au_pin_hdir_lock(struct au_pin *p); -+int au_pin_hdir_relock(struct au_pin *p); -+void au_pin_hdir_set_owner(struct au_pin *p, struct task_struct *task); -+void au_pin_hdir_acquire_nest(struct au_pin *p); -+void au_pin_hdir_release(struct au_pin *p); -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline struct au_iinfo *au_ii(struct inode *inode) -+{ -+ struct au_iinfo *iinfo; -+ -+ iinfo = &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo); -+ if (iinfo->ii_hinode) -+ return iinfo; -+ return NULL; /* debugging bad_inode case */ -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* inode.c */ -+struct inode *au_igrab(struct inode *inode); -+void au_refresh_iop(struct inode *inode, int force_getattr); -+int au_refresh_hinode_self(struct inode *inode); -+int au_refresh_hinode(struct inode *inode, struct dentry *dentry); -+int au_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, -+ unsigned int d_type, ino_t *ino); -+struct inode *au_new_inode(struct dentry *dentry, int must_new); -+int au_test_ro(struct super_block *sb, aufs_bindex_t bindex, -+ struct inode *inode); -+int au_test_h_perm(struct inode *h_inode, int mask); -+int au_test_h_perm_sio(struct inode *h_inode, int mask); -+ -+static inline int au_wh_ino(struct super_block *sb, aufs_bindex_t bindex, -+ ino_t h_ino, unsigned int d_type, ino_t *ino) -+{ -+#ifdef CONFIG_AUFS_SHWH -+ return au_ino(sb, bindex, h_ino, d_type, ino); -+#else -+ return 0; -+#endif -+} -+ -+/* i_op.c */ -+enum { -+ AuIop_SYMLINK, -+ AuIop_DIR, -+ AuIop_OTHER, -+ AuIop_Last -+}; -+extern struct inode_operations aufs_iop[AuIop_Last], -+ aufs_iop_nogetattr[AuIop_Last]; -+ -+/* au_wr_dir flags */ -+#define AuWrDir_ADD_ENTRY 1 -+#define AuWrDir_ISDIR (1 << 1) -+#define AuWrDir_TMPFILE (1 << 2) -+#define au_ftest_wrdir(flags, name) ((flags) & AuWrDir_##name) -+#define au_fset_wrdir(flags, name) \ -+ do { (flags) |= AuWrDir_##name; } while (0) -+#define au_fclr_wrdir(flags, name) \ -+ do { (flags) &= ~AuWrDir_##name; } while (0) -+ -+struct au_wr_dir_args { -+ aufs_bindex_t force_btgt; -+ unsigned char flags; -+}; -+int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry, -+ struct au_wr_dir_args *args); -+ -+struct dentry *au_pinned_h_parent(struct au_pin *pin); -+void au_pin_init(struct au_pin *pin, struct dentry *dentry, -+ aufs_bindex_t bindex, int lsc_di, int lsc_hi, -+ unsigned int udba, unsigned char flags); -+int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex, -+ unsigned int udba, unsigned char flags) __must_check; -+int au_do_pin(struct au_pin *pin) __must_check; -+void au_unpin(struct au_pin *pin); -+int au_reval_for_attr(struct dentry *dentry, unsigned int sigen); -+ -+#define AuIcpup_DID_CPUP 1 -+#define au_ftest_icpup(flags, name) ((flags) & AuIcpup_##name) -+#define au_fset_icpup(flags, name) \ -+ do { (flags) |= AuIcpup_##name; } while (0) -+#define au_fclr_icpup(flags, name) \ -+ do { (flags) &= ~AuIcpup_##name; } while (0) -+ -+struct au_icpup_args { -+ unsigned char flags; -+ unsigned char pin_flags; -+ aufs_bindex_t btgt; -+ unsigned int udba; -+ struct au_pin pin; -+ struct path h_path; -+ struct inode *h_inode; -+}; -+ -+int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia, -+ struct au_icpup_args *a); -+ -+int au_h_path_getattr(struct dentry *dentry, int force, struct path *h_path); -+ -+/* i_op_add.c */ -+int au_may_add(struct dentry *dentry, aufs_bindex_t bindex, -+ struct dentry *h_parent, int isdir); -+int aufs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, -+ dev_t dev); -+int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname); -+int aufs_create(struct inode *dir, struct dentry *dentry, umode_t mode, -+ bool want_excl); -+struct vfsub_aopen_args; -+int au_aopen_or_create(struct inode *dir, struct dentry *dentry, -+ struct vfsub_aopen_args *args); -+int aufs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode); -+int aufs_link(struct dentry *src_dentry, struct inode *dir, -+ struct dentry *dentry); -+int aufs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode); -+ -+/* i_op_del.c */ -+int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup); -+int au_may_del(struct dentry *dentry, aufs_bindex_t bindex, -+ struct dentry *h_parent, int isdir); -+int aufs_unlink(struct inode *dir, struct dentry *dentry); -+int aufs_rmdir(struct inode *dir, struct dentry *dentry); -+ -+/* i_op_ren.c */ -+int au_wbr(struct dentry *dentry, aufs_bindex_t btgt); -+int aufs_rename(struct inode *src_dir, struct dentry *src_dentry, -+ struct inode *dir, struct dentry *dentry); -+ -+/* iinfo.c */ -+struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex); -+void au_hiput(struct au_hinode *hinode); -+void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex, -+ struct dentry *h_wh); -+unsigned int au_hi_flags(struct inode *inode, int isdir); -+ -+/* hinode flags */ -+#define AuHi_XINO 1 -+#define AuHi_HNOTIFY (1 << 1) -+#define au_ftest_hi(flags, name) ((flags) & AuHi_##name) -+#define au_fset_hi(flags, name) \ -+ do { (flags) |= AuHi_##name; } while (0) -+#define au_fclr_hi(flags, name) \ -+ do { (flags) &= ~AuHi_##name; } while (0) -+ -+#ifndef CONFIG_AUFS_HNOTIFY -+#undef AuHi_HNOTIFY -+#define AuHi_HNOTIFY 0 -+#endif -+ -+void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex, -+ struct inode *h_inode, unsigned int flags); -+ -+void au_update_iigen(struct inode *inode, int half); -+void au_update_ibrange(struct inode *inode, int do_put_zero); -+ -+void au_icntnr_init_once(void *_c); -+int au_iinfo_init(struct inode *inode); -+void au_iinfo_fin(struct inode *inode); -+int au_ii_realloc(struct au_iinfo *iinfo, int nbr); -+ -+#ifdef CONFIG_PROC_FS -+/* plink.c */ -+int au_plink_maint(struct super_block *sb, int flags); -+struct au_sbinfo; -+void au_plink_maint_leave(struct au_sbinfo *sbinfo); -+int au_plink_maint_enter(struct super_block *sb); -+#ifdef CONFIG_AUFS_DEBUG -+void au_plink_list(struct super_block *sb); -+#else -+AuStubVoid(au_plink_list, struct super_block *sb) -+#endif -+int au_plink_test(struct inode *inode); -+struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex); -+void au_plink_append(struct inode *inode, aufs_bindex_t bindex, -+ struct dentry *h_dentry); -+void au_plink_put(struct super_block *sb, int verbose); -+void au_plink_clean(struct super_block *sb, int verbose); -+void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id); -+#else -+AuStubInt0(au_plink_maint, struct super_block *sb, int flags); -+AuStubVoid(au_plink_maint_leave, struct au_sbinfo *sbinfo); -+AuStubInt0(au_plink_maint_enter, struct super_block *sb); -+AuStubVoid(au_plink_list, struct super_block *sb); -+AuStubInt0(au_plink_test, struct inode *inode); -+AuStub(struct dentry *, au_plink_lkup, return NULL, -+ struct inode *inode, aufs_bindex_t bindex); -+AuStubVoid(au_plink_append, struct inode *inode, aufs_bindex_t bindex, -+ struct dentry *h_dentry); -+AuStubVoid(au_plink_put, struct super_block *sb, int verbose); -+AuStubVoid(au_plink_clean, struct super_block *sb, int verbose); -+AuStubVoid(au_plink_half_refresh, struct super_block *sb, aufs_bindex_t br_id); -+#endif /* CONFIG_PROC_FS */ -+ -+#ifdef CONFIG_AUFS_XATTR -+/* xattr.c */ -+int au_cpup_xattr(struct dentry *h_dst, struct dentry *h_src, int ignore_flags, -+ unsigned int verbose); -+ssize_t aufs_listxattr(struct dentry *dentry, char *list, size_t size); -+ssize_t aufs_getxattr(struct dentry *dentry, const char *name, void *value, -+ size_t size); -+int aufs_setxattr(struct dentry *dentry, const char *name, const void *value, -+ size_t size, int flags); -+int aufs_removexattr(struct dentry *dentry, const char *name); -+ -+/* void au_xattr_init(struct super_block *sb); */ -+#else -+AuStubInt0(au_cpup_xattr, struct dentry *h_dst, struct dentry *h_src, -+ int ignore_flags, unsigned int verbose); -+/* AuStubVoid(au_xattr_init, struct super_block *sb); */ -+#endif -+ -+#ifdef CONFIG_FS_POSIX_ACL -+struct posix_acl *aufs_get_acl(struct inode *inode, int type); -+int aufs_set_acl(struct inode *inode, struct posix_acl *acl, int type); -+#endif -+ -+#if IS_ENABLED(CONFIG_AUFS_XATTR) || IS_ENABLED(CONFIG_FS_POSIX_ACL) -+enum { -+ AU_XATTR_SET, -+ AU_XATTR_REMOVE, -+ AU_ACL_SET -+}; -+ -+struct au_srxattr { -+ int type; -+ union { -+ struct { -+ const char *name; -+ const void *value; -+ size_t size; -+ int flags; -+ } set; -+ struct { -+ const char *name; -+ } remove; -+ struct { -+ struct posix_acl *acl; -+ int type; -+ } acl_set; -+ } u; -+}; -+ssize_t au_srxattr(struct dentry *dentry, struct au_srxattr *arg); -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* lock subclass for iinfo */ -+enum { -+ AuLsc_II_CHILD, /* child first */ -+ AuLsc_II_CHILD2, /* rename(2), link(2), and cpup at hnotify */ -+ AuLsc_II_CHILD3, /* copyup dirs */ -+ AuLsc_II_PARENT, /* see AuLsc_I_PARENT in vfsub.h */ -+ AuLsc_II_PARENT2, -+ AuLsc_II_PARENT3, /* copyup dirs */ -+ AuLsc_II_NEW_CHILD -+}; -+ -+/* -+ * ii_read_lock_child, ii_write_lock_child, -+ * ii_read_lock_child2, ii_write_lock_child2, -+ * ii_read_lock_child3, ii_write_lock_child3, -+ * ii_read_lock_parent, ii_write_lock_parent, -+ * ii_read_lock_parent2, ii_write_lock_parent2, -+ * ii_read_lock_parent3, ii_write_lock_parent3, -+ * ii_read_lock_new_child, ii_write_lock_new_child, -+ */ -+#define AuReadLockFunc(name, lsc) \ -+static inline void ii_read_lock_##name(struct inode *i) \ -+{ \ -+ au_rw_read_lock_nested(&au_ii(i)->ii_rwsem, AuLsc_II_##lsc); \ -+} -+ -+#define AuWriteLockFunc(name, lsc) \ -+static inline void ii_write_lock_##name(struct inode *i) \ -+{ \ -+ au_rw_write_lock_nested(&au_ii(i)->ii_rwsem, AuLsc_II_##lsc); \ -+} -+ -+#define AuRWLockFuncs(name, lsc) \ -+ AuReadLockFunc(name, lsc) \ -+ AuWriteLockFunc(name, lsc) -+ -+AuRWLockFuncs(child, CHILD); -+AuRWLockFuncs(child2, CHILD2); -+AuRWLockFuncs(child3, CHILD3); -+AuRWLockFuncs(parent, PARENT); -+AuRWLockFuncs(parent2, PARENT2); -+AuRWLockFuncs(parent3, PARENT3); -+AuRWLockFuncs(new_child, NEW_CHILD); -+ -+#undef AuReadLockFunc -+#undef AuWriteLockFunc -+#undef AuRWLockFuncs -+ -+/* -+ * ii_read_unlock, ii_write_unlock, ii_downgrade_lock -+ */ -+AuSimpleUnlockRwsemFuncs(ii, struct inode *i, &au_ii(i)->ii_rwsem); -+ -+#define IiMustNoWaiters(i) AuRwMustNoWaiters(&au_ii(i)->ii_rwsem) -+#define IiMustAnyLock(i) AuRwMustAnyLock(&au_ii(i)->ii_rwsem) -+#define IiMustWriteLock(i) AuRwMustWriteLock(&au_ii(i)->ii_rwsem) -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline void au_icntnr_init(struct au_icntnr *c) -+{ -+#ifdef CONFIG_AUFS_DEBUG -+ c->vfs_inode.i_mode = 0; -+#endif -+} -+ -+static inline unsigned int au_iigen(struct inode *inode, unsigned int *igflags) -+{ -+ unsigned int gen; -+ struct au_iinfo *iinfo; -+ struct au_iigen *iigen; -+ -+ iinfo = au_ii(inode); -+ iigen = &iinfo->ii_generation; -+ spin_lock(&iigen->ig_spin); -+ if (igflags) -+ *igflags = iigen->ig_flags; -+ gen = iigen->ig_generation; -+ spin_unlock(&iigen->ig_spin); -+ -+ return gen; -+} -+ -+/* tiny test for inode number */ -+/* tmpfs generation is too rough */ -+static inline int au_test_higen(struct inode *inode, struct inode *h_inode) -+{ -+ struct au_iinfo *iinfo; -+ -+ iinfo = au_ii(inode); -+ AuRwMustAnyLock(&iinfo->ii_rwsem); -+ return !(iinfo->ii_hsb1 == h_inode->i_sb -+ && iinfo->ii_higen == h_inode->i_generation); -+} -+ -+static inline void au_iigen_dec(struct inode *inode) -+{ -+ struct au_iinfo *iinfo; -+ struct au_iigen *iigen; -+ -+ iinfo = au_ii(inode); -+ iigen = &iinfo->ii_generation; -+ spin_lock(&iigen->ig_spin); -+ iigen->ig_generation--; -+ spin_unlock(&iigen->ig_spin); -+} -+ -+static inline int au_iigen_test(struct inode *inode, unsigned int sigen) -+{ -+ int err; -+ -+ err = 0; -+ if (unlikely(inode && au_iigen(inode, NULL) != sigen)) -+ err = -EIO; -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline aufs_bindex_t au_ii_br_id(struct inode *inode, -+ aufs_bindex_t bindex) -+{ -+ IiMustAnyLock(inode); -+ return au_ii(inode)->ii_hinode[0 + bindex].hi_id; -+} -+ -+static inline aufs_bindex_t au_ibstart(struct inode *inode) -+{ -+ IiMustAnyLock(inode); -+ return au_ii(inode)->ii_bstart; -+} -+ -+static inline aufs_bindex_t au_ibend(struct inode *inode) -+{ -+ IiMustAnyLock(inode); -+ return au_ii(inode)->ii_bend; -+} -+ -+static inline struct au_vdir *au_ivdir(struct inode *inode) -+{ -+ IiMustAnyLock(inode); -+ return au_ii(inode)->ii_vdir; -+} -+ -+static inline struct dentry *au_hi_wh(struct inode *inode, aufs_bindex_t bindex) -+{ -+ IiMustAnyLock(inode); -+ return au_ii(inode)->ii_hinode[0 + bindex].hi_whdentry; -+} -+ -+static inline void au_set_ibstart(struct inode *inode, aufs_bindex_t bindex) -+{ -+ IiMustWriteLock(inode); -+ au_ii(inode)->ii_bstart = bindex; -+} -+ -+static inline void au_set_ibend(struct inode *inode, aufs_bindex_t bindex) -+{ -+ IiMustWriteLock(inode); -+ au_ii(inode)->ii_bend = bindex; -+} -+ -+static inline void au_set_ivdir(struct inode *inode, struct au_vdir *vdir) -+{ -+ IiMustWriteLock(inode); -+ au_ii(inode)->ii_vdir = vdir; -+} -+ -+static inline struct au_hinode *au_hi(struct inode *inode, aufs_bindex_t bindex) -+{ -+ IiMustAnyLock(inode); -+ return au_ii(inode)->ii_hinode + bindex; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline struct dentry *au_pinned_parent(struct au_pin *pin) -+{ -+ if (pin) -+ return pin->parent; -+ return NULL; -+} -+ -+static inline struct inode *au_pinned_h_dir(struct au_pin *pin) -+{ -+ if (pin && pin->hdir) -+ return pin->hdir->hi_inode; -+ return NULL; -+} -+ -+static inline struct au_hinode *au_pinned_hdir(struct au_pin *pin) -+{ -+ if (pin) -+ return pin->hdir; -+ return NULL; -+} -+ -+static inline void au_pin_set_dentry(struct au_pin *pin, struct dentry *dentry) -+{ -+ if (pin) -+ pin->dentry = dentry; -+} -+ -+static inline void au_pin_set_parent_lflag(struct au_pin *pin, -+ unsigned char lflag) -+{ -+ if (pin) { -+ if (lflag) -+ au_fset_pin(pin->flags, DI_LOCKED); -+ else -+ au_fclr_pin(pin->flags, DI_LOCKED); -+ } -+} -+ -+#if 0 /* reserved */ -+static inline void au_pin_set_parent(struct au_pin *pin, struct dentry *parent) -+{ -+ if (pin) { -+ dput(pin->parent); -+ pin->parent = dget(parent); -+ } -+} -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct au_branch; -+#ifdef CONFIG_AUFS_HNOTIFY -+struct au_hnotify_op { -+ void (*ctl)(struct au_hinode *hinode, int do_set); -+ int (*alloc)(struct au_hinode *hinode); -+ -+ /* -+ * if it returns true, the the caller should free hinode->hi_notify, -+ * otherwise ->free() frees it. -+ */ -+ int (*free)(struct au_hinode *hinode, -+ struct au_hnotify *hn) __must_check; -+ -+ void (*fin)(void); -+ int (*init)(void); -+ -+ int (*reset_br)(unsigned int udba, struct au_branch *br, int perm); -+ void (*fin_br)(struct au_branch *br); -+ int (*init_br)(struct au_branch *br, int perm); -+}; -+ -+/* hnotify.c */ -+int au_hn_alloc(struct au_hinode *hinode, struct inode *inode); -+void au_hn_free(struct au_hinode *hinode); -+void au_hn_ctl(struct au_hinode *hinode, int do_set); -+void au_hn_reset(struct inode *inode, unsigned int flags); -+int au_hnotify(struct inode *h_dir, struct au_hnotify *hnotify, u32 mask, -+ struct qstr *h_child_qstr, struct inode *h_child_inode); -+int au_hnotify_reset_br(unsigned int udba, struct au_branch *br, int perm); -+int au_hnotify_init_br(struct au_branch *br, int perm); -+void au_hnotify_fin_br(struct au_branch *br); -+int __init au_hnotify_init(void); -+void au_hnotify_fin(void); -+ -+/* hfsnotify.c */ -+extern const struct au_hnotify_op au_hnotify_op; -+ -+static inline -+void au_hn_init(struct au_hinode *hinode) -+{ -+ hinode->hi_notify = NULL; -+} -+ -+static inline struct au_hnotify *au_hn(struct au_hinode *hinode) -+{ -+ return hinode->hi_notify; -+} -+ -+#else -+AuStub(int, au_hn_alloc, return -EOPNOTSUPP, -+ struct au_hinode *hinode __maybe_unused, -+ struct inode *inode __maybe_unused) -+AuStub(struct au_hnotify *, au_hn, return NULL, struct au_hinode *hinode) -+AuStubVoid(au_hn_free, struct au_hinode *hinode __maybe_unused) -+AuStubVoid(au_hn_ctl, struct au_hinode *hinode __maybe_unused, -+ int do_set __maybe_unused) -+AuStubVoid(au_hn_reset, struct inode *inode __maybe_unused, -+ unsigned int flags __maybe_unused) -+AuStubInt0(au_hnotify_reset_br, unsigned int udba __maybe_unused, -+ struct au_branch *br __maybe_unused, -+ int perm __maybe_unused) -+AuStubInt0(au_hnotify_init_br, struct au_branch *br __maybe_unused, -+ int perm __maybe_unused) -+AuStubVoid(au_hnotify_fin_br, struct au_branch *br __maybe_unused) -+AuStubInt0(__init au_hnotify_init, void) -+AuStubVoid(au_hnotify_fin, void) -+AuStubVoid(au_hn_init, struct au_hinode *hinode __maybe_unused) -+#endif /* CONFIG_AUFS_HNOTIFY */ -+ -+static inline void au_hn_suspend(struct au_hinode *hdir) -+{ -+ au_hn_ctl(hdir, /*do_set*/0); -+} -+ -+static inline void au_hn_resume(struct au_hinode *hdir) -+{ -+ au_hn_ctl(hdir, /*do_set*/1); -+} -+ -+static inline void au_hn_imtx_lock(struct au_hinode *hdir) -+{ -+ mutex_lock(&hdir->hi_inode->i_mutex); -+ au_hn_suspend(hdir); -+} -+ -+static inline void au_hn_imtx_lock_nested(struct au_hinode *hdir, -+ unsigned int sc __maybe_unused) -+{ -+ mutex_lock_nested(&hdir->hi_inode->i_mutex, sc); -+ au_hn_suspend(hdir); -+} -+ -+static inline void au_hn_imtx_unlock(struct au_hinode *hdir) -+{ -+ au_hn_resume(hdir); -+ mutex_unlock(&hdir->hi_inode->i_mutex); -+} -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_INODE_H__ */ -diff --git a/fs/aufs/ioctl.c b/fs/aufs/ioctl.c -new file mode 100644 -index 0000000..10e2315 ---- /dev/null -+++ b/fs/aufs/ioctl.c -@@ -0,0 +1,219 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * ioctl -+ * plink-management and readdir in userspace. -+ * assist the pathconf(3) wrapper library. -+ * move-down -+ * File-based Hierarchical Storage Management. -+ */ -+ -+#include -+#include -+#include "aufs.h" -+ -+static int au_wbr_fd(struct path *path, struct aufs_wbr_fd __user *arg) -+{ -+ int err, fd; -+ aufs_bindex_t wbi, bindex, bend; -+ struct file *h_file; -+ struct super_block *sb; -+ struct dentry *root; -+ struct au_branch *br; -+ struct aufs_wbr_fd wbrfd = { -+ .oflags = au_dir_roflags, -+ .brid = -1 -+ }; -+ const int valid = O_RDONLY | O_NONBLOCK | O_LARGEFILE | O_DIRECTORY -+ | O_NOATIME | O_CLOEXEC; -+ -+ AuDebugOn(wbrfd.oflags & ~valid); -+ -+ if (arg) { -+ err = copy_from_user(&wbrfd, arg, sizeof(wbrfd)); -+ if (unlikely(err)) { -+ err = -EFAULT; -+ goto out; -+ } -+ -+ err = -EINVAL; -+ AuDbg("wbrfd{0%o, %d}\n", wbrfd.oflags, wbrfd.brid); -+ wbrfd.oflags |= au_dir_roflags; -+ AuDbg("0%o\n", wbrfd.oflags); -+ if (unlikely(wbrfd.oflags & ~valid)) -+ goto out; -+ } -+ -+ fd = get_unused_fd(); -+ err = fd; -+ if (unlikely(fd < 0)) -+ goto out; -+ -+ h_file = ERR_PTR(-EINVAL); -+ wbi = 0; -+ br = NULL; -+ sb = path->dentry->d_sb; -+ root = sb->s_root; -+ aufs_read_lock(root, AuLock_IR); -+ bend = au_sbend(sb); -+ if (wbrfd.brid >= 0) { -+ wbi = au_br_index(sb, wbrfd.brid); -+ if (unlikely(wbi < 0 || wbi > bend)) -+ goto out_unlock; -+ } -+ -+ h_file = ERR_PTR(-ENOENT); -+ br = au_sbr(sb, wbi); -+ if (!au_br_writable(br->br_perm)) { -+ if (arg) -+ goto out_unlock; -+ -+ bindex = wbi + 1; -+ wbi = -1; -+ for (; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ if (au_br_writable(br->br_perm)) { -+ wbi = bindex; -+ br = au_sbr(sb, wbi); -+ break; -+ } -+ } -+ } -+ AuDbg("wbi %d\n", wbi); -+ if (wbi >= 0) -+ h_file = au_h_open(root, wbi, wbrfd.oflags, NULL, -+ /*force_wr*/0); -+ -+out_unlock: -+ aufs_read_unlock(root, AuLock_IR); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out_fd; -+ -+ atomic_dec(&br->br_count); /* cf. au_h_open() */ -+ fd_install(fd, h_file); -+ err = fd; -+ goto out; /* success */ -+ -+out_fd: -+ put_unused_fd(fd); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+long aufs_ioctl_dir(struct file *file, unsigned int cmd, unsigned long arg) -+{ -+ long err; -+ struct dentry *dentry; -+ -+ switch (cmd) { -+ case AUFS_CTL_RDU: -+ case AUFS_CTL_RDU_INO: -+ err = au_rdu_ioctl(file, cmd, arg); -+ break; -+ -+ case AUFS_CTL_WBR_FD: -+ err = au_wbr_fd(&file->f_path, (void __user *)arg); -+ break; -+ -+ case AUFS_CTL_IBUSY: -+ err = au_ibusy_ioctl(file, arg); -+ break; -+ -+ case AUFS_CTL_BRINFO: -+ err = au_brinfo_ioctl(file, arg); -+ break; -+ -+ case AUFS_CTL_FHSM_FD: -+ dentry = file->f_dentry; -+ if (IS_ROOT(dentry)) -+ err = au_fhsm_fd(dentry->d_sb, arg); -+ else -+ err = -ENOTTY; -+ break; -+ -+ default: -+ /* do not call the lower */ -+ AuDbg("0x%x\n", cmd); -+ err = -ENOTTY; -+ } -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+long aufs_ioctl_nondir(struct file *file, unsigned int cmd, unsigned long arg) -+{ -+ long err; -+ -+ switch (cmd) { -+ case AUFS_CTL_MVDOWN: -+ err = au_mvdown(file->f_dentry, (void __user *)arg); -+ break; -+ -+ case AUFS_CTL_WBR_FD: -+ err = au_wbr_fd(&file->f_path, (void __user *)arg); -+ break; -+ -+ default: -+ /* do not call the lower */ -+ AuDbg("0x%x\n", cmd); -+ err = -ENOTTY; -+ } -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+#ifdef CONFIG_COMPAT -+long aufs_compat_ioctl_dir(struct file *file, unsigned int cmd, -+ unsigned long arg) -+{ -+ long err; -+ -+ switch (cmd) { -+ case AUFS_CTL_RDU: -+ case AUFS_CTL_RDU_INO: -+ err = au_rdu_compat_ioctl(file, cmd, arg); -+ break; -+ -+ case AUFS_CTL_IBUSY: -+ err = au_ibusy_compat_ioctl(file, arg); -+ break; -+ -+ case AUFS_CTL_BRINFO: -+ err = au_brinfo_compat_ioctl(file, arg); -+ break; -+ -+ default: -+ err = aufs_ioctl_dir(file, cmd, arg); -+ } -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+long aufs_compat_ioctl_nondir(struct file *file, unsigned int cmd, -+ unsigned long arg) -+{ -+ return aufs_ioctl_nondir(file, cmd, (unsigned long)compat_ptr(arg)); -+} -+#endif -diff --git a/fs/aufs/loop.c b/fs/aufs/loop.c -new file mode 100644 -index 0000000..1eaf59f ---- /dev/null -+++ b/fs/aufs/loop.c -@@ -0,0 +1,146 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * support for loopback block device as a branch -+ */ -+ -+#include "aufs.h" -+ -+/* added into drivers/block/loop.c */ -+static struct file *(*backing_file_func)(struct super_block *sb); -+ -+/* -+ * test if two lower dentries have overlapping branches. -+ */ -+int au_test_loopback_overlap(struct super_block *sb, struct dentry *h_adding) -+{ -+ struct super_block *h_sb; -+ struct file *backing_file; -+ -+ if (unlikely(!backing_file_func)) { -+ /* don't load "loop" module here */ -+ backing_file_func = symbol_get(loop_backing_file); -+ if (unlikely(!backing_file_func)) -+ /* "loop" module is not loaded */ -+ return 0; -+ } -+ -+ h_sb = h_adding->d_sb; -+ backing_file = backing_file_func(h_sb); -+ if (!backing_file) -+ return 0; -+ -+ h_adding = backing_file->f_dentry; -+ /* -+ * h_adding can be local NFS. -+ * in this case aufs cannot detect the loop. -+ */ -+ if (unlikely(h_adding->d_sb == sb)) -+ return 1; -+ return !!au_test_subdir(h_adding, sb->s_root); -+} -+ -+/* true if a kernel thread named 'loop[0-9].*' accesses a file */ -+int au_test_loopback_kthread(void) -+{ -+ int ret; -+ struct task_struct *tsk = current; -+ char c, comm[sizeof(tsk->comm)]; -+ -+ ret = 0; -+ if (tsk->flags & PF_KTHREAD) { -+ get_task_comm(comm, tsk); -+ c = comm[4]; -+ ret = ('0' <= c && c <= '9' -+ && !strncmp(comm, "loop", 4)); -+ } -+ -+ return ret; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+#define au_warn_loopback_step 16 -+static int au_warn_loopback_nelem = au_warn_loopback_step; -+static unsigned long *au_warn_loopback_array; -+ -+void au_warn_loopback(struct super_block *h_sb) -+{ -+ int i, new_nelem; -+ unsigned long *a, magic; -+ static DEFINE_SPINLOCK(spin); -+ -+ magic = h_sb->s_magic; -+ spin_lock(&spin); -+ a = au_warn_loopback_array; -+ for (i = 0; i < au_warn_loopback_nelem && *a; i++) -+ if (a[i] == magic) { -+ spin_unlock(&spin); -+ return; -+ } -+ -+ /* h_sb is new to us, print it */ -+ if (i < au_warn_loopback_nelem) { -+ a[i] = magic; -+ goto pr; -+ } -+ -+ /* expand the array */ -+ new_nelem = au_warn_loopback_nelem + au_warn_loopback_step; -+ a = au_kzrealloc(au_warn_loopback_array, -+ au_warn_loopback_nelem * sizeof(unsigned long), -+ new_nelem * sizeof(unsigned long), GFP_ATOMIC); -+ if (a) { -+ au_warn_loopback_nelem = new_nelem; -+ au_warn_loopback_array = a; -+ a[i] = magic; -+ goto pr; -+ } -+ -+ spin_unlock(&spin); -+ AuWarn1("realloc failed, ignored\n"); -+ return; -+ -+pr: -+ spin_unlock(&spin); -+ pr_warn("you may want to try another patch for loopback file " -+ "on %s(0x%lx) branch\n", au_sbtype(h_sb), magic); -+} -+ -+int au_loopback_init(void) -+{ -+ int err; -+ struct super_block *sb __maybe_unused; -+ -+ BUILD_BUG_ON(sizeof(sb->s_magic) != sizeof(unsigned long)); -+ -+ err = 0; -+ au_warn_loopback_array = kcalloc(au_warn_loopback_step, -+ sizeof(unsigned long), GFP_NOFS); -+ if (unlikely(!au_warn_loopback_array)) -+ err = -ENOMEM; -+ -+ return err; -+} -+ -+void au_loopback_fin(void) -+{ -+ if (backing_file_func) -+ symbol_put(loop_backing_file); -+ kfree(au_warn_loopback_array); -+} -diff --git a/fs/aufs/loop.h b/fs/aufs/loop.h -new file mode 100644 -index 0000000..35f7446 ---- /dev/null -+++ b/fs/aufs/loop.h -@@ -0,0 +1,52 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * support for loopback mount as a branch -+ */ -+ -+#ifndef __AUFS_LOOP_H__ -+#define __AUFS_LOOP_H__ -+ -+#ifdef __KERNEL__ -+ -+struct dentry; -+struct super_block; -+ -+#ifdef CONFIG_AUFS_BDEV_LOOP -+/* drivers/block/loop.c */ -+struct file *loop_backing_file(struct super_block *sb); -+ -+/* loop.c */ -+int au_test_loopback_overlap(struct super_block *sb, struct dentry *h_adding); -+int au_test_loopback_kthread(void); -+void au_warn_loopback(struct super_block *h_sb); -+ -+int au_loopback_init(void); -+void au_loopback_fin(void); -+#else -+AuStubInt0(au_test_loopback_overlap, struct super_block *sb, -+ struct dentry *h_adding) -+AuStubInt0(au_test_loopback_kthread, void) -+AuStubVoid(au_warn_loopback, struct super_block *h_sb) -+ -+AuStubInt0(au_loopback_init, void) -+AuStubVoid(au_loopback_fin, void) -+#endif /* BLK_DEV_LOOP */ -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_LOOP_H__ */ -diff --git a/fs/aufs/magic.mk b/fs/aufs/magic.mk -new file mode 100644 -index 0000000..4f83bdf ---- /dev/null -+++ b/fs/aufs/magic.mk -@@ -0,0 +1,30 @@ -+ -+# defined in ${srctree}/fs/fuse/inode.c -+# tristate -+ifdef CONFIG_FUSE_FS -+ccflags-y += -DFUSE_SUPER_MAGIC=0x65735546 -+endif -+ -+# defined in ${srctree}/fs/xfs/xfs_sb.h -+# tristate -+ifdef CONFIG_XFS_FS -+ccflags-y += -DXFS_SB_MAGIC=0x58465342 -+endif -+ -+# defined in ${srctree}/fs/configfs/mount.c -+# tristate -+ifdef CONFIG_CONFIGFS_FS -+ccflags-y += -DCONFIGFS_MAGIC=0x62656570 -+endif -+ -+# defined in ${srctree}/fs/ubifs/ubifs.h -+# tristate -+ifdef CONFIG_UBIFS_FS -+ccflags-y += -DUBIFS_SUPER_MAGIC=0x24051905 -+endif -+ -+# defined in ${srctree}/fs/hfsplus/hfsplus_raw.h -+# tristate -+ifdef CONFIG_HFSPLUS_FS -+ccflags-y += -DHFSPLUS_SUPER_MAGIC=0x482b -+endif -diff --git a/fs/aufs/module.c b/fs/aufs/module.c -new file mode 100644 -index 0000000..e4e04aa ---- /dev/null -+++ b/fs/aufs/module.c -@@ -0,0 +1,222 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * module global variables and operations -+ */ -+ -+#include -+#include -+#include "aufs.h" -+ -+void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp) -+{ -+ if (new_sz <= nused) -+ return p; -+ -+ p = krealloc(p, new_sz, gfp); -+ if (p) -+ memset(p + nused, 0, new_sz - nused); -+ return p; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * aufs caches -+ */ -+struct kmem_cache *au_cachep[AuCache_Last]; -+static int __init au_cache_init(void) -+{ -+ au_cachep[AuCache_DINFO] = AuCacheCtor(au_dinfo, au_di_init_once); -+ if (au_cachep[AuCache_DINFO]) -+ /* SLAB_DESTROY_BY_RCU */ -+ au_cachep[AuCache_ICNTNR] = AuCacheCtor(au_icntnr, -+ au_icntnr_init_once); -+ if (au_cachep[AuCache_ICNTNR]) -+ au_cachep[AuCache_FINFO] = AuCacheCtor(au_finfo, -+ au_fi_init_once); -+ if (au_cachep[AuCache_FINFO]) -+ au_cachep[AuCache_VDIR] = AuCache(au_vdir); -+ if (au_cachep[AuCache_VDIR]) -+ au_cachep[AuCache_DEHSTR] = AuCache(au_vdir_dehstr); -+ if (au_cachep[AuCache_DEHSTR]) -+ return 0; -+ -+ return -ENOMEM; -+} -+ -+static void au_cache_fin(void) -+{ -+ int i; -+ -+ /* -+ * Make sure all delayed rcu free inodes are flushed before we -+ * destroy cache. -+ */ -+ rcu_barrier(); -+ -+ /* excluding AuCache_HNOTIFY */ -+ BUILD_BUG_ON(AuCache_HNOTIFY + 1 != AuCache_Last); -+ for (i = 0; i < AuCache_HNOTIFY; i++) -+ if (au_cachep[i]) { -+ kmem_cache_destroy(au_cachep[i]); -+ au_cachep[i] = NULL; -+ } -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int au_dir_roflags; -+ -+#ifdef CONFIG_AUFS_SBILIST -+/* -+ * iterate_supers_type() doesn't protect us from -+ * remounting (branch management) -+ */ -+struct au_sphlhead au_sbilist; -+#endif -+ -+struct lock_class_key au_lc_key[AuLcKey_Last]; -+ -+/* -+ * functions for module interface. -+ */ -+MODULE_LICENSE("GPL"); -+/* MODULE_LICENSE("GPL v2"); */ -+MODULE_AUTHOR("Junjiro R. Okajima "); -+MODULE_DESCRIPTION(AUFS_NAME -+ " -- Advanced multi layered unification filesystem"); -+MODULE_VERSION(AUFS_VERSION); -+MODULE_ALIAS_FS(AUFS_NAME); -+ -+/* this module parameter has no meaning when SYSFS is disabled */ -+int sysaufs_brs = 1; -+MODULE_PARM_DESC(brs, "use /fs/aufs/si_*/brN"); -+module_param_named(brs, sysaufs_brs, int, S_IRUGO); -+ -+/* this module parameter has no meaning when USER_NS is disabled */ -+bool au_userns; -+MODULE_PARM_DESC(allow_userns, "allow unprivileged to mount under userns"); -+module_param_named(allow_userns, au_userns, bool, S_IRUGO); -+ -+/* ---------------------------------------------------------------------- */ -+ -+static char au_esc_chars[0x20 + 3]; /* 0x01-0x20, backslash, del, and NULL */ -+ -+int au_seq_path(struct seq_file *seq, struct path *path) -+{ -+ int err; -+ -+ err = seq_path(seq, path, au_esc_chars); -+ if (err > 0) -+ err = 0; -+ else if (err < 0) -+ err = -ENOMEM; -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int __init aufs_init(void) -+{ -+ int err, i; -+ char *p; -+ -+ p = au_esc_chars; -+ for (i = 1; i <= ' '; i++) -+ *p++ = i; -+ *p++ = '\\'; -+ *p++ = '\x7f'; -+ *p = 0; -+ -+ au_dir_roflags = au_file_roflags(O_DIRECTORY | O_LARGEFILE); -+ -+ memcpy(aufs_iop_nogetattr, aufs_iop, sizeof(aufs_iop)); -+ for (i = 0; i < AuIop_Last; i++) -+ aufs_iop_nogetattr[i].getattr = NULL; -+ -+ au_sbilist_init(); -+ sysaufs_brs_init(); -+ au_debug_init(); -+ au_dy_init(); -+ err = sysaufs_init(); -+ if (unlikely(err)) -+ goto out; -+ err = au_procfs_init(); -+ if (unlikely(err)) -+ goto out_sysaufs; -+ err = au_wkq_init(); -+ if (unlikely(err)) -+ goto out_procfs; -+ err = au_loopback_init(); -+ if (unlikely(err)) -+ goto out_wkq; -+ err = au_hnotify_init(); -+ if (unlikely(err)) -+ goto out_loopback; -+ err = au_sysrq_init(); -+ if (unlikely(err)) -+ goto out_hin; -+ err = au_cache_init(); -+ if (unlikely(err)) -+ goto out_sysrq; -+ -+ aufs_fs_type.fs_flags |= au_userns ? FS_USERNS_MOUNT : 0; -+ err = register_filesystem(&aufs_fs_type); -+ if (unlikely(err)) -+ goto out_cache; -+ -+ /* since we define pr_fmt, call printk directly */ -+ printk(KERN_INFO AUFS_NAME " " AUFS_VERSION "\n"); -+ goto out; /* success */ -+ -+out_cache: -+ au_cache_fin(); -+out_sysrq: -+ au_sysrq_fin(); -+out_hin: -+ au_hnotify_fin(); -+out_loopback: -+ au_loopback_fin(); -+out_wkq: -+ au_wkq_fin(); -+out_procfs: -+ au_procfs_fin(); -+out_sysaufs: -+ sysaufs_fin(); -+ au_dy_fin(); -+out: -+ return err; -+} -+ -+static void __exit aufs_exit(void) -+{ -+ unregister_filesystem(&aufs_fs_type); -+ au_cache_fin(); -+ au_sysrq_fin(); -+ au_hnotify_fin(); -+ au_loopback_fin(); -+ au_wkq_fin(); -+ au_procfs_fin(); -+ sysaufs_fin(); -+ au_dy_fin(); -+} -+ -+module_init(aufs_init); -+module_exit(aufs_exit); -diff --git a/fs/aufs/module.h b/fs/aufs/module.h -new file mode 100644 -index 0000000..90c3c8f ---- /dev/null -+++ b/fs/aufs/module.h -@@ -0,0 +1,105 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * module initialization and module-global -+ */ -+ -+#ifndef __AUFS_MODULE_H__ -+#define __AUFS_MODULE_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+ -+struct path; -+struct seq_file; -+ -+/* module parameters */ -+extern int sysaufs_brs; -+extern bool au_userns; -+ -+/* ---------------------------------------------------------------------- */ -+ -+extern int au_dir_roflags; -+ -+enum { -+ AuLcNonDir_FIINFO, -+ AuLcNonDir_DIINFO, -+ AuLcNonDir_IIINFO, -+ -+ AuLcDir_FIINFO, -+ AuLcDir_DIINFO, -+ AuLcDir_IIINFO, -+ -+ AuLcSymlink_DIINFO, -+ AuLcSymlink_IIINFO, -+ -+ AuLcKey_Last -+}; -+extern struct lock_class_key au_lc_key[AuLcKey_Last]; -+ -+void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp); -+int au_seq_path(struct seq_file *seq, struct path *path); -+ -+#ifdef CONFIG_PROC_FS -+/* procfs.c */ -+int __init au_procfs_init(void); -+void au_procfs_fin(void); -+#else -+AuStubInt0(au_procfs_init, void); -+AuStubVoid(au_procfs_fin, void); -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* kmem cache */ -+enum { -+ AuCache_DINFO, -+ AuCache_ICNTNR, -+ AuCache_FINFO, -+ AuCache_VDIR, -+ AuCache_DEHSTR, -+ AuCache_HNOTIFY, /* must be last */ -+ AuCache_Last -+}; -+ -+#define AuCacheFlags (SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD) -+#define AuCache(type) KMEM_CACHE(type, AuCacheFlags) -+#define AuCacheCtor(type, ctor) \ -+ kmem_cache_create(#type, sizeof(struct type), \ -+ __alignof__(struct type), AuCacheFlags, ctor) -+ -+extern struct kmem_cache *au_cachep[]; -+ -+#define AuCacheFuncs(name, index) \ -+static inline struct au_##name *au_cache_alloc_##name(void) \ -+{ return kmem_cache_alloc(au_cachep[AuCache_##index], GFP_NOFS); } \ -+static inline void au_cache_free_##name(struct au_##name *p) \ -+{ kmem_cache_free(au_cachep[AuCache_##index], p); } -+ -+AuCacheFuncs(dinfo, DINFO); -+AuCacheFuncs(icntnr, ICNTNR); -+AuCacheFuncs(finfo, FINFO); -+AuCacheFuncs(vdir, VDIR); -+AuCacheFuncs(vdir_dehstr, DEHSTR); -+#ifdef CONFIG_AUFS_HNOTIFY -+AuCacheFuncs(hnotify, HNOTIFY); -+#endif -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_MODULE_H__ */ -diff --git a/fs/aufs/mvdown.c b/fs/aufs/mvdown.c -new file mode 100644 -index 0000000..e660c8f ---- /dev/null -+++ b/fs/aufs/mvdown.c -@@ -0,0 +1,703 @@ -+/* -+ * Copyright (C) 2011-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * move-down, opposite of copy-up -+ */ -+ -+#include "aufs.h" -+ -+struct au_mvd_args { -+ struct { -+ struct super_block *h_sb; -+ struct dentry *h_parent; -+ struct au_hinode *hdir; -+ struct inode *h_dir, *h_inode; -+ struct au_pin pin; -+ } info[AUFS_MVDOWN_NARRAY]; -+ -+ struct aufs_mvdown mvdown; -+ struct dentry *dentry, *parent; -+ struct inode *inode, *dir; -+ struct super_block *sb; -+ aufs_bindex_t bopq, bwh, bfound; -+ unsigned char rename_lock; -+}; -+ -+#define mvd_errno mvdown.au_errno -+#define mvd_bsrc mvdown.stbr[AUFS_MVDOWN_UPPER].bindex -+#define mvd_src_brid mvdown.stbr[AUFS_MVDOWN_UPPER].brid -+#define mvd_bdst mvdown.stbr[AUFS_MVDOWN_LOWER].bindex -+#define mvd_dst_brid mvdown.stbr[AUFS_MVDOWN_LOWER].brid -+ -+#define mvd_h_src_sb info[AUFS_MVDOWN_UPPER].h_sb -+#define mvd_h_src_parent info[AUFS_MVDOWN_UPPER].h_parent -+#define mvd_hdir_src info[AUFS_MVDOWN_UPPER].hdir -+#define mvd_h_src_dir info[AUFS_MVDOWN_UPPER].h_dir -+#define mvd_h_src_inode info[AUFS_MVDOWN_UPPER].h_inode -+#define mvd_pin_src info[AUFS_MVDOWN_UPPER].pin -+ -+#define mvd_h_dst_sb info[AUFS_MVDOWN_LOWER].h_sb -+#define mvd_h_dst_parent info[AUFS_MVDOWN_LOWER].h_parent -+#define mvd_hdir_dst info[AUFS_MVDOWN_LOWER].hdir -+#define mvd_h_dst_dir info[AUFS_MVDOWN_LOWER].h_dir -+#define mvd_h_dst_inode info[AUFS_MVDOWN_LOWER].h_inode -+#define mvd_pin_dst info[AUFS_MVDOWN_LOWER].pin -+ -+#define AU_MVD_PR(flag, ...) do { \ -+ if (flag) \ -+ pr_err(__VA_ARGS__); \ -+ } while (0) -+ -+static int find_lower_writable(struct au_mvd_args *a) -+{ -+ struct super_block *sb; -+ aufs_bindex_t bindex, bend; -+ struct au_branch *br; -+ -+ sb = a->sb; -+ bindex = a->mvd_bsrc; -+ bend = au_sbend(sb); -+ if (a->mvdown.flags & AUFS_MVDOWN_FHSM_LOWER) -+ for (bindex++; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ if (au_br_fhsm(br->br_perm) -+ && (!(au_br_sb(br)->s_flags & MS_RDONLY))) -+ return bindex; -+ } -+ else if (!(a->mvdown.flags & AUFS_MVDOWN_ROLOWER)) -+ for (bindex++; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ if (!au_br_rdonly(br)) -+ return bindex; -+ } -+ else -+ for (bindex++; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ if (!(au_br_sb(br)->s_flags & MS_RDONLY)) { -+ if (au_br_rdonly(br)) -+ a->mvdown.flags -+ |= AUFS_MVDOWN_ROLOWER_R; -+ return bindex; -+ } -+ } -+ -+ return -1; -+} -+ -+/* make the parent dir on bdst */ -+static int au_do_mkdir(const unsigned char dmsg, struct au_mvd_args *a) -+{ -+ int err; -+ -+ err = 0; -+ a->mvd_hdir_src = au_hi(a->dir, a->mvd_bsrc); -+ a->mvd_hdir_dst = au_hi(a->dir, a->mvd_bdst); -+ a->mvd_h_src_parent = au_h_dptr(a->parent, a->mvd_bsrc); -+ a->mvd_h_dst_parent = NULL; -+ if (au_dbend(a->parent) >= a->mvd_bdst) -+ a->mvd_h_dst_parent = au_h_dptr(a->parent, a->mvd_bdst); -+ if (!a->mvd_h_dst_parent) { -+ err = au_cpdown_dirs(a->dentry, a->mvd_bdst); -+ if (unlikely(err)) { -+ AU_MVD_PR(dmsg, "cpdown_dirs failed\n"); -+ goto out; -+ } -+ a->mvd_h_dst_parent = au_h_dptr(a->parent, a->mvd_bdst); -+ } -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* lock them all */ -+static int au_do_lock(const unsigned char dmsg, struct au_mvd_args *a) -+{ -+ int err; -+ struct dentry *h_trap; -+ -+ a->mvd_h_src_sb = au_sbr_sb(a->sb, a->mvd_bsrc); -+ a->mvd_h_dst_sb = au_sbr_sb(a->sb, a->mvd_bdst); -+ err = au_pin(&a->mvd_pin_dst, a->dentry, a->mvd_bdst, -+ au_opt_udba(a->sb), -+ AuPin_MNT_WRITE | AuPin_DI_LOCKED); -+ AuTraceErr(err); -+ if (unlikely(err)) { -+ AU_MVD_PR(dmsg, "pin_dst failed\n"); -+ goto out; -+ } -+ -+ if (a->mvd_h_src_sb != a->mvd_h_dst_sb) { -+ a->rename_lock = 0; -+ au_pin_init(&a->mvd_pin_src, a->dentry, a->mvd_bsrc, -+ AuLsc_DI_PARENT, AuLsc_I_PARENT3, -+ au_opt_udba(a->sb), -+ AuPin_MNT_WRITE | AuPin_DI_LOCKED); -+ err = au_do_pin(&a->mvd_pin_src); -+ AuTraceErr(err); -+ a->mvd_h_src_dir = a->mvd_h_src_parent->d_inode; -+ if (unlikely(err)) { -+ AU_MVD_PR(dmsg, "pin_src failed\n"); -+ goto out_dst; -+ } -+ goto out; /* success */ -+ } -+ -+ a->rename_lock = 1; -+ au_pin_hdir_unlock(&a->mvd_pin_dst); -+ err = au_pin(&a->mvd_pin_src, a->dentry, a->mvd_bsrc, -+ au_opt_udba(a->sb), -+ AuPin_MNT_WRITE | AuPin_DI_LOCKED); -+ AuTraceErr(err); -+ a->mvd_h_src_dir = a->mvd_h_src_parent->d_inode; -+ if (unlikely(err)) { -+ AU_MVD_PR(dmsg, "pin_src failed\n"); -+ au_pin_hdir_lock(&a->mvd_pin_dst); -+ goto out_dst; -+ } -+ au_pin_hdir_unlock(&a->mvd_pin_src); -+ h_trap = vfsub_lock_rename(a->mvd_h_src_parent, a->mvd_hdir_src, -+ a->mvd_h_dst_parent, a->mvd_hdir_dst); -+ if (h_trap) { -+ err = (h_trap != a->mvd_h_src_parent); -+ if (err) -+ err = (h_trap != a->mvd_h_dst_parent); -+ } -+ BUG_ON(err); /* it should never happen */ -+ if (unlikely(a->mvd_h_src_dir != au_pinned_h_dir(&a->mvd_pin_src))) { -+ err = -EBUSY; -+ AuTraceErr(err); -+ vfsub_unlock_rename(a->mvd_h_src_parent, a->mvd_hdir_src, -+ a->mvd_h_dst_parent, a->mvd_hdir_dst); -+ au_pin_hdir_lock(&a->mvd_pin_src); -+ au_unpin(&a->mvd_pin_src); -+ au_pin_hdir_lock(&a->mvd_pin_dst); -+ goto out_dst; -+ } -+ goto out; /* success */ -+ -+out_dst: -+ au_unpin(&a->mvd_pin_dst); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+static void au_do_unlock(const unsigned char dmsg, struct au_mvd_args *a) -+{ -+ if (!a->rename_lock) -+ au_unpin(&a->mvd_pin_src); -+ else { -+ vfsub_unlock_rename(a->mvd_h_src_parent, a->mvd_hdir_src, -+ a->mvd_h_dst_parent, a->mvd_hdir_dst); -+ au_pin_hdir_lock(&a->mvd_pin_src); -+ au_unpin(&a->mvd_pin_src); -+ au_pin_hdir_lock(&a->mvd_pin_dst); -+ } -+ au_unpin(&a->mvd_pin_dst); -+} -+ -+/* copy-down the file */ -+static int au_do_cpdown(const unsigned char dmsg, struct au_mvd_args *a) -+{ -+ int err; -+ struct au_cp_generic cpg = { -+ .dentry = a->dentry, -+ .bdst = a->mvd_bdst, -+ .bsrc = a->mvd_bsrc, -+ .len = -1, -+ .pin = &a->mvd_pin_dst, -+ .flags = AuCpup_DTIME | AuCpup_HOPEN -+ }; -+ -+ AuDbg("b%d, b%d\n", cpg.bsrc, cpg.bdst); -+ if (a->mvdown.flags & AUFS_MVDOWN_OWLOWER) -+ au_fset_cpup(cpg.flags, OVERWRITE); -+ if (a->mvdown.flags & AUFS_MVDOWN_ROLOWER) -+ au_fset_cpup(cpg.flags, RWDST); -+ err = au_sio_cpdown_simple(&cpg); -+ if (unlikely(err)) -+ AU_MVD_PR(dmsg, "cpdown failed\n"); -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+/* -+ * unlink the whiteout on bdst if exist which may be created by UDBA while we -+ * were sleeping -+ */ -+static int au_do_unlink_wh(const unsigned char dmsg, struct au_mvd_args *a) -+{ -+ int err; -+ struct path h_path; -+ struct au_branch *br; -+ struct inode *delegated; -+ -+ br = au_sbr(a->sb, a->mvd_bdst); -+ h_path.dentry = au_wh_lkup(a->mvd_h_dst_parent, &a->dentry->d_name, br); -+ err = PTR_ERR(h_path.dentry); -+ if (IS_ERR(h_path.dentry)) { -+ AU_MVD_PR(dmsg, "wh_lkup failed\n"); -+ goto out; -+ } -+ -+ err = 0; -+ if (h_path.dentry->d_inode) { -+ h_path.mnt = au_br_mnt(br); -+ delegated = NULL; -+ err = vfsub_unlink(a->mvd_h_dst_parent->d_inode, &h_path, -+ &delegated, /*force*/0); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal unlink\n"); -+ iput(delegated); -+ } -+ if (unlikely(err)) -+ AU_MVD_PR(dmsg, "wh_unlink failed\n"); -+ } -+ dput(h_path.dentry); -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* -+ * unlink the topmost h_dentry -+ */ -+static int au_do_unlink(const unsigned char dmsg, struct au_mvd_args *a) -+{ -+ int err; -+ struct path h_path; -+ struct inode *delegated; -+ -+ h_path.mnt = au_sbr_mnt(a->sb, a->mvd_bsrc); -+ h_path.dentry = au_h_dptr(a->dentry, a->mvd_bsrc); -+ delegated = NULL; -+ err = vfsub_unlink(a->mvd_h_src_dir, &h_path, &delegated, /*force*/0); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal unlink\n"); -+ iput(delegated); -+ } -+ if (unlikely(err)) -+ AU_MVD_PR(dmsg, "unlink failed\n"); -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+/* Since mvdown succeeded, we ignore an error of this function */ -+static void au_do_stfs(const unsigned char dmsg, struct au_mvd_args *a) -+{ -+ int err; -+ struct au_branch *br; -+ -+ a->mvdown.flags |= AUFS_MVDOWN_STFS_FAILED; -+ br = au_sbr(a->sb, a->mvd_bsrc); -+ err = au_br_stfs(br, &a->mvdown.stbr[AUFS_MVDOWN_UPPER].stfs); -+ if (!err) { -+ br = au_sbr(a->sb, a->mvd_bdst); -+ a->mvdown.stbr[AUFS_MVDOWN_LOWER].brid = br->br_id; -+ err = au_br_stfs(br, &a->mvdown.stbr[AUFS_MVDOWN_LOWER].stfs); -+ } -+ if (!err) -+ a->mvdown.flags &= ~AUFS_MVDOWN_STFS_FAILED; -+ else -+ AU_MVD_PR(dmsg, "statfs failed (%d), ignored\n", err); -+} -+ -+/* -+ * copy-down the file and unlink the bsrc file. -+ * - unlink the bdst whout if exist -+ * - copy-down the file (with whtmp name and rename) -+ * - unlink the bsrc file -+ */ -+static int au_do_mvdown(const unsigned char dmsg, struct au_mvd_args *a) -+{ -+ int err; -+ -+ err = au_do_mkdir(dmsg, a); -+ if (!err) -+ err = au_do_lock(dmsg, a); -+ if (unlikely(err)) -+ goto out; -+ -+ /* -+ * do not revert the activities we made on bdst since they should be -+ * harmless in aufs. -+ */ -+ -+ err = au_do_cpdown(dmsg, a); -+ if (!err) -+ err = au_do_unlink_wh(dmsg, a); -+ if (!err && !(a->mvdown.flags & AUFS_MVDOWN_KUPPER)) -+ err = au_do_unlink(dmsg, a); -+ if (unlikely(err)) -+ goto out_unlock; -+ -+ AuDbg("%pd2, 0x%x, %d --> %d\n", -+ a->dentry, a->mvdown.flags, a->mvd_bsrc, a->mvd_bdst); -+ if (find_lower_writable(a) < 0) -+ a->mvdown.flags |= AUFS_MVDOWN_BOTTOM; -+ -+ if (a->mvdown.flags & AUFS_MVDOWN_STFS) -+ au_do_stfs(dmsg, a); -+ -+ /* maintain internal array */ -+ if (!(a->mvdown.flags & AUFS_MVDOWN_KUPPER)) { -+ au_set_h_dptr(a->dentry, a->mvd_bsrc, NULL); -+ au_set_dbstart(a->dentry, a->mvd_bdst); -+ au_set_h_iptr(a->inode, a->mvd_bsrc, NULL, /*flags*/0); -+ au_set_ibstart(a->inode, a->mvd_bdst); -+ } else { -+ /* hide the lower */ -+ au_set_h_dptr(a->dentry, a->mvd_bdst, NULL); -+ au_set_dbend(a->dentry, a->mvd_bsrc); -+ au_set_h_iptr(a->inode, a->mvd_bdst, NULL, /*flags*/0); -+ au_set_ibend(a->inode, a->mvd_bsrc); -+ } -+ if (au_dbend(a->dentry) < a->mvd_bdst) -+ au_set_dbend(a->dentry, a->mvd_bdst); -+ if (au_ibend(a->inode) < a->mvd_bdst) -+ au_set_ibend(a->inode, a->mvd_bdst); -+ -+out_unlock: -+ au_do_unlock(dmsg, a); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* make sure the file is idle */ -+static int au_mvd_args_busy(const unsigned char dmsg, struct au_mvd_args *a) -+{ -+ int err, plinked; -+ -+ err = 0; -+ plinked = !!au_opt_test(au_mntflags(a->sb), PLINK); -+ if (au_dbstart(a->dentry) == a->mvd_bsrc -+ && au_dcount(a->dentry) == 1 -+ && atomic_read(&a->inode->i_count) == 1 -+ /* && a->mvd_h_src_inode->i_nlink == 1 */ -+ && (!plinked || !au_plink_test(a->inode)) -+ && a->inode->i_nlink == 1) -+ goto out; -+ -+ err = -EBUSY; -+ AU_MVD_PR(dmsg, -+ "b%d, d{b%d, c%d?}, i{c%d?, l%u}, hi{l%u}, p{%d, %d}\n", -+ a->mvd_bsrc, au_dbstart(a->dentry), au_dcount(a->dentry), -+ atomic_read(&a->inode->i_count), a->inode->i_nlink, -+ a->mvd_h_src_inode->i_nlink, -+ plinked, plinked ? au_plink_test(a->inode) : 0); -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* make sure the parent dir is fine */ -+static int au_mvd_args_parent(const unsigned char dmsg, -+ struct au_mvd_args *a) -+{ -+ int err; -+ aufs_bindex_t bindex; -+ -+ err = 0; -+ if (unlikely(au_alive_dir(a->parent))) { -+ err = -ENOENT; -+ AU_MVD_PR(dmsg, "parent dir is dead\n"); -+ goto out; -+ } -+ -+ a->bopq = au_dbdiropq(a->parent); -+ bindex = au_wbr_nonopq(a->dentry, a->mvd_bdst); -+ AuDbg("b%d\n", bindex); -+ if (unlikely((bindex >= 0 && bindex < a->mvd_bdst) -+ || (a->bopq != -1 && a->bopq < a->mvd_bdst))) { -+ err = -EINVAL; -+ a->mvd_errno = EAU_MVDOWN_OPAQUE; -+ AU_MVD_PR(dmsg, "ancestor is opaque b%d, b%d\n", -+ a->bopq, a->mvd_bdst); -+ } -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+static int au_mvd_args_intermediate(const unsigned char dmsg, -+ struct au_mvd_args *a) -+{ -+ int err; -+ struct au_dinfo *dinfo, *tmp; -+ -+ /* lookup the next lower positive entry */ -+ err = -ENOMEM; -+ tmp = au_di_alloc(a->sb, AuLsc_DI_TMP); -+ if (unlikely(!tmp)) -+ goto out; -+ -+ a->bfound = -1; -+ a->bwh = -1; -+ dinfo = au_di(a->dentry); -+ au_di_cp(tmp, dinfo); -+ au_di_swap(tmp, dinfo); -+ -+ /* returns the number of positive dentries */ -+ err = au_lkup_dentry(a->dentry, a->mvd_bsrc + 1, /*type*/0); -+ if (!err) -+ a->bwh = au_dbwh(a->dentry); -+ else if (err > 0) -+ a->bfound = au_dbstart(a->dentry); -+ -+ au_di_swap(tmp, dinfo); -+ au_rw_write_unlock(&tmp->di_rwsem); -+ au_di_free(tmp); -+ if (unlikely(err < 0)) -+ AU_MVD_PR(dmsg, "failed look-up lower\n"); -+ -+ /* -+ * here, we have these cases. -+ * bfound == -1 -+ * no positive dentry under bsrc. there are more sub-cases. -+ * bwh < 0 -+ * there no whiteout, we can safely move-down. -+ * bwh <= bsrc -+ * impossible -+ * bsrc < bwh && bwh < bdst -+ * there is a whiteout on RO branch. cannot proceed. -+ * bwh == bdst -+ * there is a whiteout on the RW target branch. it should -+ * be removed. -+ * bdst < bwh -+ * there is a whiteout somewhere unrelated branch. -+ * -1 < bfound && bfound <= bsrc -+ * impossible. -+ * bfound < bdst -+ * found, but it is on RO branch between bsrc and bdst. cannot -+ * proceed. -+ * bfound == bdst -+ * found, replace it if AUFS_MVDOWN_FORCE is set. otherwise return -+ * error. -+ * bdst < bfound -+ * found, after we create the file on bdst, it will be hidden. -+ */ -+ -+ AuDebugOn(a->bfound == -1 -+ && a->bwh != -1 -+ && a->bwh <= a->mvd_bsrc); -+ AuDebugOn(-1 < a->bfound -+ && a->bfound <= a->mvd_bsrc); -+ -+ err = -EINVAL; -+ if (a->bfound == -1 -+ && a->mvd_bsrc < a->bwh -+ && a->bwh != -1 -+ && a->bwh < a->mvd_bdst) { -+ a->mvd_errno = EAU_MVDOWN_WHITEOUT; -+ AU_MVD_PR(dmsg, "bsrc %d, bdst %d, bfound %d, bwh %d\n", -+ a->mvd_bsrc, a->mvd_bdst, a->bfound, a->bwh); -+ goto out; -+ } else if (a->bfound != -1 && a->bfound < a->mvd_bdst) { -+ a->mvd_errno = EAU_MVDOWN_UPPER; -+ AU_MVD_PR(dmsg, "bdst %d, bfound %d\n", -+ a->mvd_bdst, a->bfound); -+ goto out; -+ } -+ -+ err = 0; /* success */ -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+static int au_mvd_args_exist(const unsigned char dmsg, struct au_mvd_args *a) -+{ -+ int err; -+ -+ err = 0; -+ if (!(a->mvdown.flags & AUFS_MVDOWN_OWLOWER) -+ && a->bfound == a->mvd_bdst) -+ err = -EEXIST; -+ AuTraceErr(err); -+ return err; -+} -+ -+static int au_mvd_args(const unsigned char dmsg, struct au_mvd_args *a) -+{ -+ int err; -+ struct au_branch *br; -+ -+ err = -EISDIR; -+ if (unlikely(S_ISDIR(a->inode->i_mode))) -+ goto out; -+ -+ err = -EINVAL; -+ if (!(a->mvdown.flags & AUFS_MVDOWN_BRID_UPPER)) -+ a->mvd_bsrc = au_ibstart(a->inode); -+ else { -+ a->mvd_bsrc = au_br_index(a->sb, a->mvd_src_brid); -+ if (unlikely(a->mvd_bsrc < 0 -+ || (a->mvd_bsrc < au_dbstart(a->dentry) -+ || au_dbend(a->dentry) < a->mvd_bsrc -+ || !au_h_dptr(a->dentry, a->mvd_bsrc)) -+ || (a->mvd_bsrc < au_ibstart(a->inode) -+ || au_ibend(a->inode) < a->mvd_bsrc -+ || !au_h_iptr(a->inode, a->mvd_bsrc)))) { -+ a->mvd_errno = EAU_MVDOWN_NOUPPER; -+ AU_MVD_PR(dmsg, "no upper\n"); -+ goto out; -+ } -+ } -+ if (unlikely(a->mvd_bsrc == au_sbend(a->sb))) { -+ a->mvd_errno = EAU_MVDOWN_BOTTOM; -+ AU_MVD_PR(dmsg, "on the bottom\n"); -+ goto out; -+ } -+ a->mvd_h_src_inode = au_h_iptr(a->inode, a->mvd_bsrc); -+ br = au_sbr(a->sb, a->mvd_bsrc); -+ err = au_br_rdonly(br); -+ if (!(a->mvdown.flags & AUFS_MVDOWN_ROUPPER)) { -+ if (unlikely(err)) -+ goto out; -+ } else if (!(vfsub_native_ro(a->mvd_h_src_inode) -+ || IS_APPEND(a->mvd_h_src_inode))) { -+ if (err) -+ a->mvdown.flags |= AUFS_MVDOWN_ROUPPER_R; -+ /* go on */ -+ } else -+ goto out; -+ -+ err = -EINVAL; -+ if (!(a->mvdown.flags & AUFS_MVDOWN_BRID_LOWER)) { -+ a->mvd_bdst = find_lower_writable(a); -+ if (unlikely(a->mvd_bdst < 0)) { -+ a->mvd_errno = EAU_MVDOWN_BOTTOM; -+ AU_MVD_PR(dmsg, "no writable lower branch\n"); -+ goto out; -+ } -+ } else { -+ a->mvd_bdst = au_br_index(a->sb, a->mvd_dst_brid); -+ if (unlikely(a->mvd_bdst < 0 -+ || au_sbend(a->sb) < a->mvd_bdst)) { -+ a->mvd_errno = EAU_MVDOWN_NOLOWERBR; -+ AU_MVD_PR(dmsg, "no lower brid\n"); -+ goto out; -+ } -+ } -+ -+ err = au_mvd_args_busy(dmsg, a); -+ if (!err) -+ err = au_mvd_args_parent(dmsg, a); -+ if (!err) -+ err = au_mvd_args_intermediate(dmsg, a); -+ if (!err) -+ err = au_mvd_args_exist(dmsg, a); -+ if (!err) -+ AuDbg("b%d, b%d\n", a->mvd_bsrc, a->mvd_bdst); -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+int au_mvdown(struct dentry *dentry, struct aufs_mvdown __user *uarg) -+{ -+ int err, e; -+ unsigned char dmsg; -+ struct au_mvd_args *args; -+ struct inode *inode; -+ -+ inode = dentry->d_inode; -+ err = -EPERM; -+ if (unlikely(!capable(CAP_SYS_ADMIN))) -+ goto out; -+ -+ err = -ENOMEM; -+ args = kmalloc(sizeof(*args), GFP_NOFS); -+ if (unlikely(!args)) -+ goto out; -+ -+ err = copy_from_user(&args->mvdown, uarg, sizeof(args->mvdown)); -+ if (!err) -+ err = !access_ok(VERIFY_WRITE, uarg, sizeof(*uarg)); -+ if (unlikely(err)) { -+ err = -EFAULT; -+ AuTraceErr(err); -+ goto out_free; -+ } -+ AuDbg("flags 0x%x\n", args->mvdown.flags); -+ args->mvdown.flags &= ~(AUFS_MVDOWN_ROLOWER_R | AUFS_MVDOWN_ROUPPER_R); -+ args->mvdown.au_errno = 0; -+ args->dentry = dentry; -+ args->inode = inode; -+ args->sb = dentry->d_sb; -+ -+ err = -ENOENT; -+ dmsg = !!(args->mvdown.flags & AUFS_MVDOWN_DMSG); -+ args->parent = dget_parent(dentry); -+ args->dir = args->parent->d_inode; -+ mutex_lock_nested(&args->dir->i_mutex, I_MUTEX_PARENT); -+ dput(args->parent); -+ if (unlikely(args->parent != dentry->d_parent)) { -+ AU_MVD_PR(dmsg, "parent dir is moved\n"); -+ goto out_dir; -+ } -+ -+ mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD); -+ err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_NOPLMW); -+ if (unlikely(err)) -+ goto out_inode; -+ -+ di_write_lock_parent(args->parent); -+ err = au_mvd_args(dmsg, args); -+ if (unlikely(err)) -+ goto out_parent; -+ -+ err = au_do_mvdown(dmsg, args); -+ if (unlikely(err)) -+ goto out_parent; -+ -+ au_cpup_attr_timesizes(args->dir); -+ au_cpup_attr_timesizes(inode); -+ if (!(args->mvdown.flags & AUFS_MVDOWN_KUPPER)) -+ au_cpup_igen(inode, au_h_iptr(inode, args->mvd_bdst)); -+ /* au_digen_dec(dentry); */ -+ -+out_parent: -+ di_write_unlock(args->parent); -+ aufs_read_unlock(dentry, AuLock_DW); -+out_inode: -+ mutex_unlock(&inode->i_mutex); -+out_dir: -+ mutex_unlock(&args->dir->i_mutex); -+out_free: -+ e = copy_to_user(uarg, &args->mvdown, sizeof(args->mvdown)); -+ if (unlikely(e)) -+ err = -EFAULT; -+ kfree(args); -+out: -+ AuTraceErr(err); -+ return err; -+} -diff --git a/fs/aufs/opts.c b/fs/aufs/opts.c -new file mode 100644 -index 0000000..0363f67 ---- /dev/null -+++ b/fs/aufs/opts.c -@@ -0,0 +1,1878 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * mount options/flags -+ */ -+ -+#include -+#include /* a distribution requires */ -+#include -+#include "aufs.h" -+ -+/* ---------------------------------------------------------------------- */ -+ -+enum { -+ Opt_br, -+ Opt_add, Opt_del, Opt_mod, Opt_append, Opt_prepend, -+ Opt_idel, Opt_imod, -+ Opt_dirwh, Opt_rdcache, Opt_rdblk, Opt_rdhash, -+ Opt_rdblk_def, Opt_rdhash_def, -+ Opt_xino, Opt_noxino, -+ Opt_trunc_xino, Opt_trunc_xino_v, Opt_notrunc_xino, -+ Opt_trunc_xino_path, Opt_itrunc_xino, -+ Opt_trunc_xib, Opt_notrunc_xib, -+ Opt_shwh, Opt_noshwh, -+ Opt_plink, Opt_noplink, Opt_list_plink, -+ Opt_udba, -+ Opt_dio, Opt_nodio, -+ Opt_diropq_a, Opt_diropq_w, -+ Opt_warn_perm, Opt_nowarn_perm, -+ Opt_wbr_copyup, Opt_wbr_create, -+ Opt_fhsm_sec, -+ Opt_refrof, Opt_norefrof, -+ Opt_verbose, Opt_noverbose, -+ Opt_sum, Opt_nosum, Opt_wsum, -+ Opt_dirperm1, Opt_nodirperm1, -+ Opt_acl, Opt_noacl, -+ Opt_tail, Opt_ignore, Opt_ignore_silent, Opt_err -+}; -+ -+static match_table_t options = { -+ {Opt_br, "br=%s"}, -+ {Opt_br, "br:%s"}, -+ -+ {Opt_add, "add=%d:%s"}, -+ {Opt_add, "add:%d:%s"}, -+ {Opt_add, "ins=%d:%s"}, -+ {Opt_add, "ins:%d:%s"}, -+ {Opt_append, "append=%s"}, -+ {Opt_append, "append:%s"}, -+ {Opt_prepend, "prepend=%s"}, -+ {Opt_prepend, "prepend:%s"}, -+ -+ {Opt_del, "del=%s"}, -+ {Opt_del, "del:%s"}, -+ /* {Opt_idel, "idel:%d"}, */ -+ {Opt_mod, "mod=%s"}, -+ {Opt_mod, "mod:%s"}, -+ /* {Opt_imod, "imod:%d:%s"}, */ -+ -+ {Opt_dirwh, "dirwh=%d"}, -+ -+ {Opt_xino, "xino=%s"}, -+ {Opt_noxino, "noxino"}, -+ {Opt_trunc_xino, "trunc_xino"}, -+ {Opt_trunc_xino_v, "trunc_xino_v=%d:%d"}, -+ {Opt_notrunc_xino, "notrunc_xino"}, -+ {Opt_trunc_xino_path, "trunc_xino=%s"}, -+ {Opt_itrunc_xino, "itrunc_xino=%d"}, -+ /* {Opt_zxino, "zxino=%s"}, */ -+ {Opt_trunc_xib, "trunc_xib"}, -+ {Opt_notrunc_xib, "notrunc_xib"}, -+ -+#ifdef CONFIG_PROC_FS -+ {Opt_plink, "plink"}, -+#else -+ {Opt_ignore_silent, "plink"}, -+#endif -+ -+ {Opt_noplink, "noplink"}, -+ -+#ifdef CONFIG_AUFS_DEBUG -+ {Opt_list_plink, "list_plink"}, -+#endif -+ -+ {Opt_udba, "udba=%s"}, -+ -+ {Opt_dio, "dio"}, -+ {Opt_nodio, "nodio"}, -+ -+#ifdef CONFIG_AUFS_FHSM -+ {Opt_fhsm_sec, "fhsm_sec=%d"}, -+#else -+ {Opt_ignore_silent, "fhsm_sec=%d"}, -+#endif -+ -+ {Opt_diropq_a, "diropq=always"}, -+ {Opt_diropq_a, "diropq=a"}, -+ {Opt_diropq_w, "diropq=whiteouted"}, -+ {Opt_diropq_w, "diropq=w"}, -+ -+ {Opt_warn_perm, "warn_perm"}, -+ {Opt_nowarn_perm, "nowarn_perm"}, -+ -+ /* keep them temporary */ -+ {Opt_ignore_silent, "nodlgt"}, -+ {Opt_ignore_silent, "clean_plink"}, -+ -+#ifdef CONFIG_AUFS_SHWH -+ {Opt_shwh, "shwh"}, -+#endif -+ {Opt_noshwh, "noshwh"}, -+ -+ {Opt_dirperm1, "dirperm1"}, -+ {Opt_nodirperm1, "nodirperm1"}, -+ -+ {Opt_refrof, "refrof"}, -+ {Opt_norefrof, "norefrof"}, -+ -+ {Opt_verbose, "verbose"}, -+ {Opt_verbose, "v"}, -+ {Opt_noverbose, "noverbose"}, -+ {Opt_noverbose, "quiet"}, -+ {Opt_noverbose, "q"}, -+ {Opt_noverbose, "silent"}, -+ -+ {Opt_sum, "sum"}, -+ {Opt_nosum, "nosum"}, -+ {Opt_wsum, "wsum"}, -+ -+ {Opt_rdcache, "rdcache=%d"}, -+ {Opt_rdblk, "rdblk=%d"}, -+ {Opt_rdblk_def, "rdblk=def"}, -+ {Opt_rdhash, "rdhash=%d"}, -+ {Opt_rdhash_def, "rdhash=def"}, -+ -+ {Opt_wbr_create, "create=%s"}, -+ {Opt_wbr_create, "create_policy=%s"}, -+ {Opt_wbr_copyup, "cpup=%s"}, -+ {Opt_wbr_copyup, "copyup=%s"}, -+ {Opt_wbr_copyup, "copyup_policy=%s"}, -+ -+ /* generic VFS flag */ -+#ifdef CONFIG_FS_POSIX_ACL -+ {Opt_acl, "acl"}, -+ {Opt_noacl, "noacl"}, -+#else -+ {Opt_ignore_silent, "acl"}, -+ {Opt_ignore_silent, "noacl"}, -+#endif -+ -+ /* internal use for the scripts */ -+ {Opt_ignore_silent, "si=%s"}, -+ -+ {Opt_br, "dirs=%s"}, -+ {Opt_ignore, "debug=%d"}, -+ {Opt_ignore, "delete=whiteout"}, -+ {Opt_ignore, "delete=all"}, -+ {Opt_ignore, "imap=%s"}, -+ -+ /* temporary workaround, due to old mount(8)? */ -+ {Opt_ignore_silent, "relatime"}, -+ -+ {Opt_err, NULL} -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+static const char *au_parser_pattern(int val, match_table_t tbl) -+{ -+ struct match_token *p; -+ -+ p = tbl; -+ while (p->pattern) { -+ if (p->token == val) -+ return p->pattern; -+ p++; -+ } -+ BUG(); -+ return "??"; -+} -+ -+static const char *au_optstr(int *val, match_table_t tbl) -+{ -+ struct match_token *p; -+ int v; -+ -+ v = *val; -+ if (!v) -+ goto out; -+ p = tbl; -+ while (p->pattern) { -+ if (p->token -+ && (v & p->token) == p->token) { -+ *val &= ~p->token; -+ return p->pattern; -+ } -+ p++; -+ } -+ -+out: -+ return NULL; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static match_table_t brperm = { -+ {AuBrPerm_RO, AUFS_BRPERM_RO}, -+ {AuBrPerm_RR, AUFS_BRPERM_RR}, -+ {AuBrPerm_RW, AUFS_BRPERM_RW}, -+ {0, NULL} -+}; -+ -+static match_table_t brattr = { -+ /* general */ -+ {AuBrAttr_COO_REG, AUFS_BRATTR_COO_REG}, -+ {AuBrAttr_COO_ALL, AUFS_BRATTR_COO_ALL}, -+ /* 'unpin' attrib is meaningless since linux-3.18-rc1 */ -+ {AuBrAttr_UNPIN, AUFS_BRATTR_UNPIN}, -+#ifdef CONFIG_AUFS_FHSM -+ {AuBrAttr_FHSM, AUFS_BRATTR_FHSM}, -+#endif -+#ifdef CONFIG_AUFS_XATTR -+ {AuBrAttr_ICEX, AUFS_BRATTR_ICEX}, -+ {AuBrAttr_ICEX_SEC, AUFS_BRATTR_ICEX_SEC}, -+ {AuBrAttr_ICEX_SYS, AUFS_BRATTR_ICEX_SYS}, -+ {AuBrAttr_ICEX_TR, AUFS_BRATTR_ICEX_TR}, -+ {AuBrAttr_ICEX_USR, AUFS_BRATTR_ICEX_USR}, -+ {AuBrAttr_ICEX_OTH, AUFS_BRATTR_ICEX_OTH}, -+#endif -+ -+ /* ro/rr branch */ -+ {AuBrRAttr_WH, AUFS_BRRATTR_WH}, -+ -+ /* rw branch */ -+ {AuBrWAttr_MOO, AUFS_BRWATTR_MOO}, -+ {AuBrWAttr_NoLinkWH, AUFS_BRWATTR_NLWH}, -+ -+ {0, NULL} -+}; -+ -+static int br_attr_val(char *str, match_table_t table, substring_t args[]) -+{ -+ int attr, v; -+ char *p; -+ -+ attr = 0; -+ do { -+ p = strchr(str, '+'); -+ if (p) -+ *p = 0; -+ v = match_token(str, table, args); -+ if (v) { -+ if (v & AuBrAttr_CMOO_Mask) -+ attr &= ~AuBrAttr_CMOO_Mask; -+ attr |= v; -+ } else { -+ if (p) -+ *p = '+'; -+ pr_warn("ignored branch attribute %s\n", str); -+ break; -+ } -+ if (p) -+ str = p + 1; -+ } while (p); -+ -+ return attr; -+} -+ -+static int au_do_optstr_br_attr(au_br_perm_str_t *str, int perm) -+{ -+ int sz; -+ const char *p; -+ char *q; -+ -+ q = str->a; -+ *q = 0; -+ p = au_optstr(&perm, brattr); -+ if (p) { -+ sz = strlen(p); -+ memcpy(q, p, sz + 1); -+ q += sz; -+ } else -+ goto out; -+ -+ do { -+ p = au_optstr(&perm, brattr); -+ if (p) { -+ *q++ = '+'; -+ sz = strlen(p); -+ memcpy(q, p, sz + 1); -+ q += sz; -+ } -+ } while (p); -+ -+out: -+ return q - str->a; -+} -+ -+static int noinline_for_stack br_perm_val(char *perm) -+{ -+ int val, bad, sz; -+ char *p; -+ substring_t args[MAX_OPT_ARGS]; -+ au_br_perm_str_t attr; -+ -+ p = strchr(perm, '+'); -+ if (p) -+ *p = 0; -+ val = match_token(perm, brperm, args); -+ if (!val) { -+ if (p) -+ *p = '+'; -+ pr_warn("ignored branch permission %s\n", perm); -+ val = AuBrPerm_RO; -+ goto out; -+ } -+ if (!p) -+ goto out; -+ -+ val |= br_attr_val(p + 1, brattr, args); -+ -+ bad = 0; -+ switch (val & AuBrPerm_Mask) { -+ case AuBrPerm_RO: -+ case AuBrPerm_RR: -+ bad = val & AuBrWAttr_Mask; -+ val &= ~AuBrWAttr_Mask; -+ break; -+ case AuBrPerm_RW: -+ bad = val & AuBrRAttr_Mask; -+ val &= ~AuBrRAttr_Mask; -+ break; -+ } -+ -+ /* -+ * 'unpin' attrib becomes meaningless since linux-3.18-rc1, but aufs -+ * does not treat it as an error, just warning. -+ * this is a tiny guard for the user operation. -+ */ -+ if (val & AuBrAttr_UNPIN) { -+ bad |= AuBrAttr_UNPIN; -+ val &= ~AuBrAttr_UNPIN; -+ } -+ -+ if (unlikely(bad)) { -+ sz = au_do_optstr_br_attr(&attr, bad); -+ AuDebugOn(!sz); -+ pr_warn("ignored branch attribute %s\n", attr.a); -+ } -+ -+out: -+ return val; -+} -+ -+void au_optstr_br_perm(au_br_perm_str_t *str, int perm) -+{ -+ au_br_perm_str_t attr; -+ const char *p; -+ char *q; -+ int sz; -+ -+ q = str->a; -+ p = au_optstr(&perm, brperm); -+ AuDebugOn(!p || !*p); -+ sz = strlen(p); -+ memcpy(q, p, sz + 1); -+ q += sz; -+ -+ sz = au_do_optstr_br_attr(&attr, perm); -+ if (sz) { -+ *q++ = '+'; -+ memcpy(q, attr.a, sz + 1); -+ } -+ -+ AuDebugOn(strlen(str->a) >= sizeof(str->a)); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static match_table_t udbalevel = { -+ {AuOpt_UDBA_REVAL, "reval"}, -+ {AuOpt_UDBA_NONE, "none"}, -+#ifdef CONFIG_AUFS_HNOTIFY -+ {AuOpt_UDBA_HNOTIFY, "notify"}, /* abstraction */ -+#ifdef CONFIG_AUFS_HFSNOTIFY -+ {AuOpt_UDBA_HNOTIFY, "fsnotify"}, -+#endif -+#endif -+ {-1, NULL} -+}; -+ -+static int noinline_for_stack udba_val(char *str) -+{ -+ substring_t args[MAX_OPT_ARGS]; -+ -+ return match_token(str, udbalevel, args); -+} -+ -+const char *au_optstr_udba(int udba) -+{ -+ return au_parser_pattern(udba, udbalevel); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static match_table_t au_wbr_create_policy = { -+ {AuWbrCreate_TDP, "tdp"}, -+ {AuWbrCreate_TDP, "top-down-parent"}, -+ {AuWbrCreate_RR, "rr"}, -+ {AuWbrCreate_RR, "round-robin"}, -+ {AuWbrCreate_MFS, "mfs"}, -+ {AuWbrCreate_MFS, "most-free-space"}, -+ {AuWbrCreate_MFSV, "mfs:%d"}, -+ {AuWbrCreate_MFSV, "most-free-space:%d"}, -+ -+ {AuWbrCreate_MFSRR, "mfsrr:%d"}, -+ {AuWbrCreate_MFSRRV, "mfsrr:%d:%d"}, -+ {AuWbrCreate_PMFS, "pmfs"}, -+ {AuWbrCreate_PMFSV, "pmfs:%d"}, -+ {AuWbrCreate_PMFSRR, "pmfsrr:%d"}, -+ {AuWbrCreate_PMFSRRV, "pmfsrr:%d:%d"}, -+ -+ {-1, NULL} -+}; -+ -+/* -+ * cf. linux/lib/parser.c and cmdline.c -+ * gave up calling memparse() since it uses simple_strtoull() instead of -+ * kstrto...(). -+ */ -+static int noinline_for_stack -+au_match_ull(substring_t *s, unsigned long long *result) -+{ -+ int err; -+ unsigned int len; -+ char a[32]; -+ -+ err = -ERANGE; -+ len = s->to - s->from; -+ if (len + 1 <= sizeof(a)) { -+ memcpy(a, s->from, len); -+ a[len] = '\0'; -+ err = kstrtoull(a, 0, result); -+ } -+ return err; -+} -+ -+static int au_wbr_mfs_wmark(substring_t *arg, char *str, -+ struct au_opt_wbr_create *create) -+{ -+ int err; -+ unsigned long long ull; -+ -+ err = 0; -+ if (!au_match_ull(arg, &ull)) -+ create->mfsrr_watermark = ull; -+ else { -+ pr_err("bad integer in %s\n", str); -+ err = -EINVAL; -+ } -+ -+ return err; -+} -+ -+static int au_wbr_mfs_sec(substring_t *arg, char *str, -+ struct au_opt_wbr_create *create) -+{ -+ int n, err; -+ -+ err = 0; -+ if (!match_int(arg, &n) && 0 <= n && n <= AUFS_MFS_MAX_SEC) -+ create->mfs_second = n; -+ else { -+ pr_err("bad integer in %s\n", str); -+ err = -EINVAL; -+ } -+ -+ return err; -+} -+ -+static int noinline_for_stack -+au_wbr_create_val(char *str, struct au_opt_wbr_create *create) -+{ -+ int err, e; -+ substring_t args[MAX_OPT_ARGS]; -+ -+ err = match_token(str, au_wbr_create_policy, args); -+ create->wbr_create = err; -+ switch (err) { -+ case AuWbrCreate_MFSRRV: -+ case AuWbrCreate_PMFSRRV: -+ e = au_wbr_mfs_wmark(&args[0], str, create); -+ if (!e) -+ e = au_wbr_mfs_sec(&args[1], str, create); -+ if (unlikely(e)) -+ err = e; -+ break; -+ case AuWbrCreate_MFSRR: -+ case AuWbrCreate_PMFSRR: -+ e = au_wbr_mfs_wmark(&args[0], str, create); -+ if (unlikely(e)) { -+ err = e; -+ break; -+ } -+ /*FALLTHROUGH*/ -+ case AuWbrCreate_MFS: -+ case AuWbrCreate_PMFS: -+ create->mfs_second = AUFS_MFS_DEF_SEC; -+ break; -+ case AuWbrCreate_MFSV: -+ case AuWbrCreate_PMFSV: -+ e = au_wbr_mfs_sec(&args[0], str, create); -+ if (unlikely(e)) -+ err = e; -+ break; -+ } -+ -+ return err; -+} -+ -+const char *au_optstr_wbr_create(int wbr_create) -+{ -+ return au_parser_pattern(wbr_create, au_wbr_create_policy); -+} -+ -+static match_table_t au_wbr_copyup_policy = { -+ {AuWbrCopyup_TDP, "tdp"}, -+ {AuWbrCopyup_TDP, "top-down-parent"}, -+ {AuWbrCopyup_BUP, "bup"}, -+ {AuWbrCopyup_BUP, "bottom-up-parent"}, -+ {AuWbrCopyup_BU, "bu"}, -+ {AuWbrCopyup_BU, "bottom-up"}, -+ {-1, NULL} -+}; -+ -+static int noinline_for_stack au_wbr_copyup_val(char *str) -+{ -+ substring_t args[MAX_OPT_ARGS]; -+ -+ return match_token(str, au_wbr_copyup_policy, args); -+} -+ -+const char *au_optstr_wbr_copyup(int wbr_copyup) -+{ -+ return au_parser_pattern(wbr_copyup, au_wbr_copyup_policy); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static const int lkup_dirflags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY; -+ -+static void dump_opts(struct au_opts *opts) -+{ -+#ifdef CONFIG_AUFS_DEBUG -+ /* reduce stack space */ -+ union { -+ struct au_opt_add *add; -+ struct au_opt_del *del; -+ struct au_opt_mod *mod; -+ struct au_opt_xino *xino; -+ struct au_opt_xino_itrunc *xino_itrunc; -+ struct au_opt_wbr_create *create; -+ } u; -+ struct au_opt *opt; -+ -+ opt = opts->opt; -+ while (opt->type != Opt_tail) { -+ switch (opt->type) { -+ case Opt_add: -+ u.add = &opt->add; -+ AuDbg("add {b%d, %s, 0x%x, %p}\n", -+ u.add->bindex, u.add->pathname, u.add->perm, -+ u.add->path.dentry); -+ break; -+ case Opt_del: -+ case Opt_idel: -+ u.del = &opt->del; -+ AuDbg("del {%s, %p}\n", -+ u.del->pathname, u.del->h_path.dentry); -+ break; -+ case Opt_mod: -+ case Opt_imod: -+ u.mod = &opt->mod; -+ AuDbg("mod {%s, 0x%x, %p}\n", -+ u.mod->path, u.mod->perm, u.mod->h_root); -+ break; -+ case Opt_append: -+ u.add = &opt->add; -+ AuDbg("append {b%d, %s, 0x%x, %p}\n", -+ u.add->bindex, u.add->pathname, u.add->perm, -+ u.add->path.dentry); -+ break; -+ case Opt_prepend: -+ u.add = &opt->add; -+ AuDbg("prepend {b%d, %s, 0x%x, %p}\n", -+ u.add->bindex, u.add->pathname, u.add->perm, -+ u.add->path.dentry); -+ break; -+ case Opt_dirwh: -+ AuDbg("dirwh %d\n", opt->dirwh); -+ break; -+ case Opt_rdcache: -+ AuDbg("rdcache %d\n", opt->rdcache); -+ break; -+ case Opt_rdblk: -+ AuDbg("rdblk %u\n", opt->rdblk); -+ break; -+ case Opt_rdblk_def: -+ AuDbg("rdblk_def\n"); -+ break; -+ case Opt_rdhash: -+ AuDbg("rdhash %u\n", opt->rdhash); -+ break; -+ case Opt_rdhash_def: -+ AuDbg("rdhash_def\n"); -+ break; -+ case Opt_xino: -+ u.xino = &opt->xino; -+ AuDbg("xino {%s %pD}\n", u.xino->path, u.xino->file); -+ break; -+ case Opt_trunc_xino: -+ AuLabel(trunc_xino); -+ break; -+ case Opt_notrunc_xino: -+ AuLabel(notrunc_xino); -+ break; -+ case Opt_trunc_xino_path: -+ case Opt_itrunc_xino: -+ u.xino_itrunc = &opt->xino_itrunc; -+ AuDbg("trunc_xino %d\n", u.xino_itrunc->bindex); -+ break; -+ case Opt_noxino: -+ AuLabel(noxino); -+ break; -+ case Opt_trunc_xib: -+ AuLabel(trunc_xib); -+ break; -+ case Opt_notrunc_xib: -+ AuLabel(notrunc_xib); -+ break; -+ case Opt_shwh: -+ AuLabel(shwh); -+ break; -+ case Opt_noshwh: -+ AuLabel(noshwh); -+ break; -+ case Opt_dirperm1: -+ AuLabel(dirperm1); -+ break; -+ case Opt_nodirperm1: -+ AuLabel(nodirperm1); -+ break; -+ case Opt_plink: -+ AuLabel(plink); -+ break; -+ case Opt_noplink: -+ AuLabel(noplink); -+ break; -+ case Opt_list_plink: -+ AuLabel(list_plink); -+ break; -+ case Opt_udba: -+ AuDbg("udba %d, %s\n", -+ opt->udba, au_optstr_udba(opt->udba)); -+ break; -+ case Opt_dio: -+ AuLabel(dio); -+ break; -+ case Opt_nodio: -+ AuLabel(nodio); -+ break; -+ case Opt_diropq_a: -+ AuLabel(diropq_a); -+ break; -+ case Opt_diropq_w: -+ AuLabel(diropq_w); -+ break; -+ case Opt_warn_perm: -+ AuLabel(warn_perm); -+ break; -+ case Opt_nowarn_perm: -+ AuLabel(nowarn_perm); -+ break; -+ case Opt_refrof: -+ AuLabel(refrof); -+ break; -+ case Opt_norefrof: -+ AuLabel(norefrof); -+ break; -+ case Opt_verbose: -+ AuLabel(verbose); -+ break; -+ case Opt_noverbose: -+ AuLabel(noverbose); -+ break; -+ case Opt_sum: -+ AuLabel(sum); -+ break; -+ case Opt_nosum: -+ AuLabel(nosum); -+ break; -+ case Opt_wsum: -+ AuLabel(wsum); -+ break; -+ case Opt_wbr_create: -+ u.create = &opt->wbr_create; -+ AuDbg("create %d, %s\n", u.create->wbr_create, -+ au_optstr_wbr_create(u.create->wbr_create)); -+ switch (u.create->wbr_create) { -+ case AuWbrCreate_MFSV: -+ case AuWbrCreate_PMFSV: -+ AuDbg("%d sec\n", u.create->mfs_second); -+ break; -+ case AuWbrCreate_MFSRR: -+ AuDbg("%llu watermark\n", -+ u.create->mfsrr_watermark); -+ break; -+ case AuWbrCreate_MFSRRV: -+ case AuWbrCreate_PMFSRRV: -+ AuDbg("%llu watermark, %d sec\n", -+ u.create->mfsrr_watermark, -+ u.create->mfs_second); -+ break; -+ } -+ break; -+ case Opt_wbr_copyup: -+ AuDbg("copyup %d, %s\n", opt->wbr_copyup, -+ au_optstr_wbr_copyup(opt->wbr_copyup)); -+ break; -+ case Opt_fhsm_sec: -+ AuDbg("fhsm_sec %u\n", opt->fhsm_second); -+ break; -+ case Opt_acl: -+ AuLabel(acl); -+ break; -+ case Opt_noacl: -+ AuLabel(noacl); -+ break; -+ default: -+ BUG(); -+ } -+ opt++; -+ } -+#endif -+} -+ -+void au_opts_free(struct au_opts *opts) -+{ -+ struct au_opt *opt; -+ -+ opt = opts->opt; -+ while (opt->type != Opt_tail) { -+ switch (opt->type) { -+ case Opt_add: -+ case Opt_append: -+ case Opt_prepend: -+ path_put(&opt->add.path); -+ break; -+ case Opt_del: -+ case Opt_idel: -+ path_put(&opt->del.h_path); -+ break; -+ case Opt_mod: -+ case Opt_imod: -+ dput(opt->mod.h_root); -+ break; -+ case Opt_xino: -+ fput(opt->xino.file); -+ break; -+ } -+ opt++; -+ } -+} -+ -+static int opt_add(struct au_opt *opt, char *opt_str, unsigned long sb_flags, -+ aufs_bindex_t bindex) -+{ -+ int err; -+ struct au_opt_add *add = &opt->add; -+ char *p; -+ -+ add->bindex = bindex; -+ add->perm = AuBrPerm_RO; -+ add->pathname = opt_str; -+ p = strchr(opt_str, '='); -+ if (p) { -+ *p++ = 0; -+ if (*p) -+ add->perm = br_perm_val(p); -+ } -+ -+ err = vfsub_kern_path(add->pathname, lkup_dirflags, &add->path); -+ if (!err) { -+ if (!p) { -+ add->perm = AuBrPerm_RO; -+ if (au_test_fs_rr(add->path.dentry->d_sb)) -+ add->perm = AuBrPerm_RR; -+ else if (!bindex && !(sb_flags & MS_RDONLY)) -+ add->perm = AuBrPerm_RW; -+ } -+ opt->type = Opt_add; -+ goto out; -+ } -+ pr_err("lookup failed %s (%d)\n", add->pathname, err); -+ err = -EINVAL; -+ -+out: -+ return err; -+} -+ -+static int au_opts_parse_del(struct au_opt_del *del, substring_t args[]) -+{ -+ int err; -+ -+ del->pathname = args[0].from; -+ AuDbg("del path %s\n", del->pathname); -+ -+ err = vfsub_kern_path(del->pathname, lkup_dirflags, &del->h_path); -+ if (unlikely(err)) -+ pr_err("lookup failed %s (%d)\n", del->pathname, err); -+ -+ return err; -+} -+ -+#if 0 /* reserved for future use */ -+static int au_opts_parse_idel(struct super_block *sb, aufs_bindex_t bindex, -+ struct au_opt_del *del, substring_t args[]) -+{ -+ int err; -+ struct dentry *root; -+ -+ err = -EINVAL; -+ root = sb->s_root; -+ aufs_read_lock(root, AuLock_FLUSH); -+ if (bindex < 0 || au_sbend(sb) < bindex) { -+ pr_err("out of bounds, %d\n", bindex); -+ goto out; -+ } -+ -+ err = 0; -+ del->h_path.dentry = dget(au_h_dptr(root, bindex)); -+ del->h_path.mnt = mntget(au_sbr_mnt(sb, bindex)); -+ -+out: -+ aufs_read_unlock(root, !AuLock_IR); -+ return err; -+} -+#endif -+ -+static int noinline_for_stack -+au_opts_parse_mod(struct au_opt_mod *mod, substring_t args[]) -+{ -+ int err; -+ struct path path; -+ char *p; -+ -+ err = -EINVAL; -+ mod->path = args[0].from; -+ p = strchr(mod->path, '='); -+ if (unlikely(!p)) { -+ pr_err("no permssion %s\n", args[0].from); -+ goto out; -+ } -+ -+ *p++ = 0; -+ err = vfsub_kern_path(mod->path, lkup_dirflags, &path); -+ if (unlikely(err)) { -+ pr_err("lookup failed %s (%d)\n", mod->path, err); -+ goto out; -+ } -+ -+ mod->perm = br_perm_val(p); -+ AuDbg("mod path %s, perm 0x%x, %s\n", mod->path, mod->perm, p); -+ mod->h_root = dget(path.dentry); -+ path_put(&path); -+ -+out: -+ return err; -+} -+ -+#if 0 /* reserved for future use */ -+static int au_opts_parse_imod(struct super_block *sb, aufs_bindex_t bindex, -+ struct au_opt_mod *mod, substring_t args[]) -+{ -+ int err; -+ struct dentry *root; -+ -+ err = -EINVAL; -+ root = sb->s_root; -+ aufs_read_lock(root, AuLock_FLUSH); -+ if (bindex < 0 || au_sbend(sb) < bindex) { -+ pr_err("out of bounds, %d\n", bindex); -+ goto out; -+ } -+ -+ err = 0; -+ mod->perm = br_perm_val(args[1].from); -+ AuDbg("mod path %s, perm 0x%x, %s\n", -+ mod->path, mod->perm, args[1].from); -+ mod->h_root = dget(au_h_dptr(root, bindex)); -+ -+out: -+ aufs_read_unlock(root, !AuLock_IR); -+ return err; -+} -+#endif -+ -+static int au_opts_parse_xino(struct super_block *sb, struct au_opt_xino *xino, -+ substring_t args[]) -+{ -+ int err; -+ struct file *file; -+ -+ file = au_xino_create(sb, args[0].from, /*silent*/0); -+ err = PTR_ERR(file); -+ if (IS_ERR(file)) -+ goto out; -+ -+ err = -EINVAL; -+ if (unlikely(file->f_dentry->d_sb == sb)) { -+ fput(file); -+ pr_err("%s must be outside\n", args[0].from); -+ goto out; -+ } -+ -+ err = 0; -+ xino->file = file; -+ xino->path = args[0].from; -+ -+out: -+ return err; -+} -+ -+static int noinline_for_stack -+au_opts_parse_xino_itrunc_path(struct super_block *sb, -+ struct au_opt_xino_itrunc *xino_itrunc, -+ substring_t args[]) -+{ -+ int err; -+ aufs_bindex_t bend, bindex; -+ struct path path; -+ struct dentry *root; -+ -+ err = vfsub_kern_path(args[0].from, lkup_dirflags, &path); -+ if (unlikely(err)) { -+ pr_err("lookup failed %s (%d)\n", args[0].from, err); -+ goto out; -+ } -+ -+ xino_itrunc->bindex = -1; -+ root = sb->s_root; -+ aufs_read_lock(root, AuLock_FLUSH); -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { -+ if (au_h_dptr(root, bindex) == path.dentry) { -+ xino_itrunc->bindex = bindex; -+ break; -+ } -+ } -+ aufs_read_unlock(root, !AuLock_IR); -+ path_put(&path); -+ -+ if (unlikely(xino_itrunc->bindex < 0)) { -+ pr_err("no such branch %s\n", args[0].from); -+ err = -EINVAL; -+ } -+ -+out: -+ return err; -+} -+ -+/* called without aufs lock */ -+int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts) -+{ -+ int err, n, token; -+ aufs_bindex_t bindex; -+ unsigned char skipped; -+ struct dentry *root; -+ struct au_opt *opt, *opt_tail; -+ char *opt_str; -+ /* reduce the stack space */ -+ union { -+ struct au_opt_xino_itrunc *xino_itrunc; -+ struct au_opt_wbr_create *create; -+ } u; -+ struct { -+ substring_t args[MAX_OPT_ARGS]; -+ } *a; -+ -+ err = -ENOMEM; -+ a = kmalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ -+ root = sb->s_root; -+ err = 0; -+ bindex = 0; -+ opt = opts->opt; -+ opt_tail = opt + opts->max_opt - 1; -+ opt->type = Opt_tail; -+ while (!err && (opt_str = strsep(&str, ",")) && *opt_str) { -+ err = -EINVAL; -+ skipped = 0; -+ token = match_token(opt_str, options, a->args); -+ switch (token) { -+ case Opt_br: -+ err = 0; -+ while (!err && (opt_str = strsep(&a->args[0].from, ":")) -+ && *opt_str) { -+ err = opt_add(opt, opt_str, opts->sb_flags, -+ bindex++); -+ if (unlikely(!err && ++opt > opt_tail)) { -+ err = -E2BIG; -+ break; -+ } -+ opt->type = Opt_tail; -+ skipped = 1; -+ } -+ break; -+ case Opt_add: -+ if (unlikely(match_int(&a->args[0], &n))) { -+ pr_err("bad integer in %s\n", opt_str); -+ break; -+ } -+ bindex = n; -+ err = opt_add(opt, a->args[1].from, opts->sb_flags, -+ bindex); -+ if (!err) -+ opt->type = token; -+ break; -+ case Opt_append: -+ err = opt_add(opt, a->args[0].from, opts->sb_flags, -+ /*dummy bindex*/1); -+ if (!err) -+ opt->type = token; -+ break; -+ case Opt_prepend: -+ err = opt_add(opt, a->args[0].from, opts->sb_flags, -+ /*bindex*/0); -+ if (!err) -+ opt->type = token; -+ break; -+ case Opt_del: -+ err = au_opts_parse_del(&opt->del, a->args); -+ if (!err) -+ opt->type = token; -+ break; -+#if 0 /* reserved for future use */ -+ case Opt_idel: -+ del->pathname = "(indexed)"; -+ if (unlikely(match_int(&args[0], &n))) { -+ pr_err("bad integer in %s\n", opt_str); -+ break; -+ } -+ err = au_opts_parse_idel(sb, n, &opt->del, a->args); -+ if (!err) -+ opt->type = token; -+ break; -+#endif -+ case Opt_mod: -+ err = au_opts_parse_mod(&opt->mod, a->args); -+ if (!err) -+ opt->type = token; -+ break; -+#ifdef IMOD /* reserved for future use */ -+ case Opt_imod: -+ u.mod->path = "(indexed)"; -+ if (unlikely(match_int(&a->args[0], &n))) { -+ pr_err("bad integer in %s\n", opt_str); -+ break; -+ } -+ err = au_opts_parse_imod(sb, n, &opt->mod, a->args); -+ if (!err) -+ opt->type = token; -+ break; -+#endif -+ case Opt_xino: -+ err = au_opts_parse_xino(sb, &opt->xino, a->args); -+ if (!err) -+ opt->type = token; -+ break; -+ -+ case Opt_trunc_xino_path: -+ err = au_opts_parse_xino_itrunc_path -+ (sb, &opt->xino_itrunc, a->args); -+ if (!err) -+ opt->type = token; -+ break; -+ -+ case Opt_itrunc_xino: -+ u.xino_itrunc = &opt->xino_itrunc; -+ if (unlikely(match_int(&a->args[0], &n))) { -+ pr_err("bad integer in %s\n", opt_str); -+ break; -+ } -+ u.xino_itrunc->bindex = n; -+ aufs_read_lock(root, AuLock_FLUSH); -+ if (n < 0 || au_sbend(sb) < n) { -+ pr_err("out of bounds, %d\n", n); -+ aufs_read_unlock(root, !AuLock_IR); -+ break; -+ } -+ aufs_read_unlock(root, !AuLock_IR); -+ err = 0; -+ opt->type = token; -+ break; -+ -+ case Opt_dirwh: -+ if (unlikely(match_int(&a->args[0], &opt->dirwh))) -+ break; -+ err = 0; -+ opt->type = token; -+ break; -+ -+ case Opt_rdcache: -+ if (unlikely(match_int(&a->args[0], &n))) { -+ pr_err("bad integer in %s\n", opt_str); -+ break; -+ } -+ if (unlikely(n > AUFS_RDCACHE_MAX)) { -+ pr_err("rdcache must be smaller than %d\n", -+ AUFS_RDCACHE_MAX); -+ break; -+ } -+ opt->rdcache = n; -+ err = 0; -+ opt->type = token; -+ break; -+ case Opt_rdblk: -+ if (unlikely(match_int(&a->args[0], &n) -+ || n < 0 -+ || n > KMALLOC_MAX_SIZE)) { -+ pr_err("bad integer in %s\n", opt_str); -+ break; -+ } -+ if (unlikely(n && n < NAME_MAX)) { -+ pr_err("rdblk must be larger than %d\n", -+ NAME_MAX); -+ break; -+ } -+ opt->rdblk = n; -+ err = 0; -+ opt->type = token; -+ break; -+ case Opt_rdhash: -+ if (unlikely(match_int(&a->args[0], &n) -+ || n < 0 -+ || n * sizeof(struct hlist_head) -+ > KMALLOC_MAX_SIZE)) { -+ pr_err("bad integer in %s\n", opt_str); -+ break; -+ } -+ opt->rdhash = n; -+ err = 0; -+ opt->type = token; -+ break; -+ -+ case Opt_trunc_xino: -+ case Opt_notrunc_xino: -+ case Opt_noxino: -+ case Opt_trunc_xib: -+ case Opt_notrunc_xib: -+ case Opt_shwh: -+ case Opt_noshwh: -+ case Opt_dirperm1: -+ case Opt_nodirperm1: -+ case Opt_plink: -+ case Opt_noplink: -+ case Opt_list_plink: -+ case Opt_dio: -+ case Opt_nodio: -+ case Opt_diropq_a: -+ case Opt_diropq_w: -+ case Opt_warn_perm: -+ case Opt_nowarn_perm: -+ case Opt_refrof: -+ case Opt_norefrof: -+ case Opt_verbose: -+ case Opt_noverbose: -+ case Opt_sum: -+ case Opt_nosum: -+ case Opt_wsum: -+ case Opt_rdblk_def: -+ case Opt_rdhash_def: -+ case Opt_acl: -+ case Opt_noacl: -+ err = 0; -+ opt->type = token; -+ break; -+ -+ case Opt_udba: -+ opt->udba = udba_val(a->args[0].from); -+ if (opt->udba >= 0) { -+ err = 0; -+ opt->type = token; -+ } else -+ pr_err("wrong value, %s\n", opt_str); -+ break; -+ -+ case Opt_wbr_create: -+ u.create = &opt->wbr_create; -+ u.create->wbr_create -+ = au_wbr_create_val(a->args[0].from, u.create); -+ if (u.create->wbr_create >= 0) { -+ err = 0; -+ opt->type = token; -+ } else -+ pr_err("wrong value, %s\n", opt_str); -+ break; -+ case Opt_wbr_copyup: -+ opt->wbr_copyup = au_wbr_copyup_val(a->args[0].from); -+ if (opt->wbr_copyup >= 0) { -+ err = 0; -+ opt->type = token; -+ } else -+ pr_err("wrong value, %s\n", opt_str); -+ break; -+ -+ case Opt_fhsm_sec: -+ if (unlikely(match_int(&a->args[0], &n) -+ || n < 0)) { -+ pr_err("bad integer in %s\n", opt_str); -+ break; -+ } -+ if (sysaufs_brs) { -+ opt->fhsm_second = n; -+ opt->type = token; -+ } else -+ pr_warn("ignored %s\n", opt_str); -+ err = 0; -+ break; -+ -+ case Opt_ignore: -+ pr_warn("ignored %s\n", opt_str); -+ /*FALLTHROUGH*/ -+ case Opt_ignore_silent: -+ skipped = 1; -+ err = 0; -+ break; -+ case Opt_err: -+ pr_err("unknown option %s\n", opt_str); -+ break; -+ } -+ -+ if (!err && !skipped) { -+ if (unlikely(++opt > opt_tail)) { -+ err = -E2BIG; -+ opt--; -+ opt->type = Opt_tail; -+ break; -+ } -+ opt->type = Opt_tail; -+ } -+ } -+ -+ kfree(a); -+ dump_opts(opts); -+ if (unlikely(err)) -+ au_opts_free(opts); -+ -+out: -+ return err; -+} -+ -+static int au_opt_wbr_create(struct super_block *sb, -+ struct au_opt_wbr_create *create) -+{ -+ int err; -+ struct au_sbinfo *sbinfo; -+ -+ SiMustWriteLock(sb); -+ -+ err = 1; /* handled */ -+ sbinfo = au_sbi(sb); -+ if (sbinfo->si_wbr_create_ops->fin) { -+ err = sbinfo->si_wbr_create_ops->fin(sb); -+ if (!err) -+ err = 1; -+ } -+ -+ sbinfo->si_wbr_create = create->wbr_create; -+ sbinfo->si_wbr_create_ops = au_wbr_create_ops + create->wbr_create; -+ switch (create->wbr_create) { -+ case AuWbrCreate_MFSRRV: -+ case AuWbrCreate_MFSRR: -+ case AuWbrCreate_PMFSRR: -+ case AuWbrCreate_PMFSRRV: -+ sbinfo->si_wbr_mfs.mfsrr_watermark = create->mfsrr_watermark; -+ /*FALLTHROUGH*/ -+ case AuWbrCreate_MFS: -+ case AuWbrCreate_MFSV: -+ case AuWbrCreate_PMFS: -+ case AuWbrCreate_PMFSV: -+ sbinfo->si_wbr_mfs.mfs_expire -+ = msecs_to_jiffies(create->mfs_second * MSEC_PER_SEC); -+ break; -+ } -+ -+ if (sbinfo->si_wbr_create_ops->init) -+ sbinfo->si_wbr_create_ops->init(sb); /* ignore */ -+ -+ return err; -+} -+ -+/* -+ * returns, -+ * plus: processed without an error -+ * zero: unprocessed -+ */ -+static int au_opt_simple(struct super_block *sb, struct au_opt *opt, -+ struct au_opts *opts) -+{ -+ int err; -+ struct au_sbinfo *sbinfo; -+ -+ SiMustWriteLock(sb); -+ -+ err = 1; /* handled */ -+ sbinfo = au_sbi(sb); -+ switch (opt->type) { -+ case Opt_udba: -+ sbinfo->si_mntflags &= ~AuOptMask_UDBA; -+ sbinfo->si_mntflags |= opt->udba; -+ opts->given_udba |= opt->udba; -+ break; -+ -+ case Opt_plink: -+ au_opt_set(sbinfo->si_mntflags, PLINK); -+ break; -+ case Opt_noplink: -+ if (au_opt_test(sbinfo->si_mntflags, PLINK)) -+ au_plink_put(sb, /*verbose*/1); -+ au_opt_clr(sbinfo->si_mntflags, PLINK); -+ break; -+ case Opt_list_plink: -+ if (au_opt_test(sbinfo->si_mntflags, PLINK)) -+ au_plink_list(sb); -+ break; -+ -+ case Opt_dio: -+ au_opt_set(sbinfo->si_mntflags, DIO); -+ au_fset_opts(opts->flags, REFRESH_DYAOP); -+ break; -+ case Opt_nodio: -+ au_opt_clr(sbinfo->si_mntflags, DIO); -+ au_fset_opts(opts->flags, REFRESH_DYAOP); -+ break; -+ -+ case Opt_fhsm_sec: -+ au_fhsm_set(sbinfo, opt->fhsm_second); -+ break; -+ -+ case Opt_diropq_a: -+ au_opt_set(sbinfo->si_mntflags, ALWAYS_DIROPQ); -+ break; -+ case Opt_diropq_w: -+ au_opt_clr(sbinfo->si_mntflags, ALWAYS_DIROPQ); -+ break; -+ -+ case Opt_warn_perm: -+ au_opt_set(sbinfo->si_mntflags, WARN_PERM); -+ break; -+ case Opt_nowarn_perm: -+ au_opt_clr(sbinfo->si_mntflags, WARN_PERM); -+ break; -+ -+ case Opt_refrof: -+ au_opt_set(sbinfo->si_mntflags, REFROF); -+ break; -+ case Opt_norefrof: -+ au_opt_clr(sbinfo->si_mntflags, REFROF); -+ break; -+ -+ case Opt_verbose: -+ au_opt_set(sbinfo->si_mntflags, VERBOSE); -+ break; -+ case Opt_noverbose: -+ au_opt_clr(sbinfo->si_mntflags, VERBOSE); -+ break; -+ -+ case Opt_sum: -+ au_opt_set(sbinfo->si_mntflags, SUM); -+ break; -+ case Opt_wsum: -+ au_opt_clr(sbinfo->si_mntflags, SUM); -+ au_opt_set(sbinfo->si_mntflags, SUM_W); -+ case Opt_nosum: -+ au_opt_clr(sbinfo->si_mntflags, SUM); -+ au_opt_clr(sbinfo->si_mntflags, SUM_W); -+ break; -+ -+ case Opt_wbr_create: -+ err = au_opt_wbr_create(sb, &opt->wbr_create); -+ break; -+ case Opt_wbr_copyup: -+ sbinfo->si_wbr_copyup = opt->wbr_copyup; -+ sbinfo->si_wbr_copyup_ops = au_wbr_copyup_ops + opt->wbr_copyup; -+ break; -+ -+ case Opt_dirwh: -+ sbinfo->si_dirwh = opt->dirwh; -+ break; -+ -+ case Opt_rdcache: -+ sbinfo->si_rdcache -+ = msecs_to_jiffies(opt->rdcache * MSEC_PER_SEC); -+ break; -+ case Opt_rdblk: -+ sbinfo->si_rdblk = opt->rdblk; -+ break; -+ case Opt_rdblk_def: -+ sbinfo->si_rdblk = AUFS_RDBLK_DEF; -+ break; -+ case Opt_rdhash: -+ sbinfo->si_rdhash = opt->rdhash; -+ break; -+ case Opt_rdhash_def: -+ sbinfo->si_rdhash = AUFS_RDHASH_DEF; -+ break; -+ -+ case Opt_shwh: -+ au_opt_set(sbinfo->si_mntflags, SHWH); -+ break; -+ case Opt_noshwh: -+ au_opt_clr(sbinfo->si_mntflags, SHWH); -+ break; -+ -+ case Opt_dirperm1: -+ au_opt_set(sbinfo->si_mntflags, DIRPERM1); -+ break; -+ case Opt_nodirperm1: -+ au_opt_clr(sbinfo->si_mntflags, DIRPERM1); -+ break; -+ -+ case Opt_trunc_xino: -+ au_opt_set(sbinfo->si_mntflags, TRUNC_XINO); -+ break; -+ case Opt_notrunc_xino: -+ au_opt_clr(sbinfo->si_mntflags, TRUNC_XINO); -+ break; -+ -+ case Opt_trunc_xino_path: -+ case Opt_itrunc_xino: -+ err = au_xino_trunc(sb, opt->xino_itrunc.bindex); -+ if (!err) -+ err = 1; -+ break; -+ -+ case Opt_trunc_xib: -+ au_fset_opts(opts->flags, TRUNC_XIB); -+ break; -+ case Opt_notrunc_xib: -+ au_fclr_opts(opts->flags, TRUNC_XIB); -+ break; -+ -+ case Opt_acl: -+ sb->s_flags |= MS_POSIXACL; -+ break; -+ case Opt_noacl: -+ sb->s_flags &= ~MS_POSIXACL; -+ break; -+ -+ default: -+ err = 0; -+ break; -+ } -+ -+ return err; -+} -+ -+/* -+ * returns tri-state. -+ * plus: processed without an error -+ * zero: unprocessed -+ * minus: error -+ */ -+static int au_opt_br(struct super_block *sb, struct au_opt *opt, -+ struct au_opts *opts) -+{ -+ int err, do_refresh; -+ -+ err = 0; -+ switch (opt->type) { -+ case Opt_append: -+ opt->add.bindex = au_sbend(sb) + 1; -+ if (opt->add.bindex < 0) -+ opt->add.bindex = 0; -+ goto add; -+ case Opt_prepend: -+ opt->add.bindex = 0; -+ add: /* indented label */ -+ case Opt_add: -+ err = au_br_add(sb, &opt->add, -+ au_ftest_opts(opts->flags, REMOUNT)); -+ if (!err) { -+ err = 1; -+ au_fset_opts(opts->flags, REFRESH); -+ } -+ break; -+ -+ case Opt_del: -+ case Opt_idel: -+ err = au_br_del(sb, &opt->del, -+ au_ftest_opts(opts->flags, REMOUNT)); -+ if (!err) { -+ err = 1; -+ au_fset_opts(opts->flags, TRUNC_XIB); -+ au_fset_opts(opts->flags, REFRESH); -+ } -+ break; -+ -+ case Opt_mod: -+ case Opt_imod: -+ err = au_br_mod(sb, &opt->mod, -+ au_ftest_opts(opts->flags, REMOUNT), -+ &do_refresh); -+ if (!err) { -+ err = 1; -+ if (do_refresh) -+ au_fset_opts(opts->flags, REFRESH); -+ } -+ break; -+ } -+ -+ return err; -+} -+ -+static int au_opt_xino(struct super_block *sb, struct au_opt *opt, -+ struct au_opt_xino **opt_xino, -+ struct au_opts *opts) -+{ -+ int err; -+ aufs_bindex_t bend, bindex; -+ struct dentry *root, *parent, *h_root; -+ -+ err = 0; -+ switch (opt->type) { -+ case Opt_xino: -+ err = au_xino_set(sb, &opt->xino, -+ !!au_ftest_opts(opts->flags, REMOUNT)); -+ if (unlikely(err)) -+ break; -+ -+ *opt_xino = &opt->xino; -+ au_xino_brid_set(sb, -1); -+ -+ /* safe d_parent access */ -+ parent = opt->xino.file->f_dentry->d_parent; -+ root = sb->s_root; -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { -+ h_root = au_h_dptr(root, bindex); -+ if (h_root == parent) { -+ au_xino_brid_set(sb, au_sbr_id(sb, bindex)); -+ break; -+ } -+ } -+ break; -+ -+ case Opt_noxino: -+ au_xino_clr(sb); -+ au_xino_brid_set(sb, -1); -+ *opt_xino = (void *)-1; -+ break; -+ } -+ -+ return err; -+} -+ -+int au_opts_verify(struct super_block *sb, unsigned long sb_flags, -+ unsigned int pending) -+{ -+ int err, fhsm; -+ aufs_bindex_t bindex, bend; -+ unsigned char do_plink, skip, do_free, can_no_dreval; -+ struct au_branch *br; -+ struct au_wbr *wbr; -+ struct dentry *root, *dentry; -+ struct inode *dir, *h_dir; -+ struct au_sbinfo *sbinfo; -+ struct au_hinode *hdir; -+ -+ SiMustAnyLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ AuDebugOn(!(sbinfo->si_mntflags & AuOptMask_UDBA)); -+ -+ if (!(sb_flags & MS_RDONLY)) { -+ if (unlikely(!au_br_writable(au_sbr_perm(sb, 0)))) -+ pr_warn("first branch should be rw\n"); -+ if (unlikely(au_opt_test(sbinfo->si_mntflags, SHWH))) -+ pr_warn_once("shwh should be used with ro\n"); -+ } -+ -+ if (au_opt_test((sbinfo->si_mntflags | pending), UDBA_HNOTIFY) -+ && !au_opt_test(sbinfo->si_mntflags, XINO)) -+ pr_warn_once("udba=*notify requires xino\n"); -+ -+ if (au_opt_test(sbinfo->si_mntflags, DIRPERM1)) -+ pr_warn_once("dirperm1 breaks the protection" -+ " by the permission bits on the lower branch\n"); -+ -+ err = 0; -+ fhsm = 0; -+ root = sb->s_root; -+ dir = root->d_inode; -+ do_plink = !!au_opt_test(sbinfo->si_mntflags, PLINK); -+ can_no_dreval = !!au_opt_test((sbinfo->si_mntflags | pending), -+ UDBA_NONE); -+ bend = au_sbend(sb); -+ for (bindex = 0; !err && bindex <= bend; bindex++) { -+ skip = 0; -+ h_dir = au_h_iptr(dir, bindex); -+ br = au_sbr(sb, bindex); -+ -+ if ((br->br_perm & AuBrAttr_ICEX) -+ && !h_dir->i_op->listxattr) -+ br->br_perm &= ~AuBrAttr_ICEX; -+#if 0 -+ if ((br->br_perm & AuBrAttr_ICEX_SEC) -+ && (au_br_sb(br)->s_flags & MS_NOSEC)) -+ br->br_perm &= ~AuBrAttr_ICEX_SEC; -+#endif -+ -+ do_free = 0; -+ wbr = br->br_wbr; -+ if (wbr) -+ wbr_wh_read_lock(wbr); -+ -+ if (!au_br_writable(br->br_perm)) { -+ do_free = !!wbr; -+ skip = (!wbr -+ || (!wbr->wbr_whbase -+ && !wbr->wbr_plink -+ && !wbr->wbr_orph)); -+ } else if (!au_br_wh_linkable(br->br_perm)) { -+ /* skip = (!br->br_whbase && !br->br_orph); */ -+ skip = (!wbr || !wbr->wbr_whbase); -+ if (skip && wbr) { -+ if (do_plink) -+ skip = !!wbr->wbr_plink; -+ else -+ skip = !wbr->wbr_plink; -+ } -+ } else { -+ /* skip = (br->br_whbase && br->br_ohph); */ -+ skip = (wbr && wbr->wbr_whbase); -+ if (skip) { -+ if (do_plink) -+ skip = !!wbr->wbr_plink; -+ else -+ skip = !wbr->wbr_plink; -+ } -+ } -+ if (wbr) -+ wbr_wh_read_unlock(wbr); -+ -+ if (can_no_dreval) { -+ dentry = br->br_path.dentry; -+ spin_lock(&dentry->d_lock); -+ if (dentry->d_flags & -+ (DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE)) -+ can_no_dreval = 0; -+ spin_unlock(&dentry->d_lock); -+ } -+ -+ if (au_br_fhsm(br->br_perm)) { -+ fhsm++; -+ AuDebugOn(!br->br_fhsm); -+ } -+ -+ if (skip) -+ continue; -+ -+ hdir = au_hi(dir, bindex); -+ au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); -+ if (wbr) -+ wbr_wh_write_lock(wbr); -+ err = au_wh_init(br, sb); -+ if (wbr) -+ wbr_wh_write_unlock(wbr); -+ au_hn_imtx_unlock(hdir); -+ -+ if (!err && do_free) { -+ kfree(wbr); -+ br->br_wbr = NULL; -+ } -+ } -+ -+ if (can_no_dreval) -+ au_fset_si(sbinfo, NO_DREVAL); -+ else -+ au_fclr_si(sbinfo, NO_DREVAL); -+ -+ if (fhsm >= 2) { -+ au_fset_si(sbinfo, FHSM); -+ for (bindex = bend; bindex >= 0; bindex--) { -+ br = au_sbr(sb, bindex); -+ if (au_br_fhsm(br->br_perm)) { -+ au_fhsm_set_bottom(sb, bindex); -+ break; -+ } -+ } -+ } else { -+ au_fclr_si(sbinfo, FHSM); -+ au_fhsm_set_bottom(sb, -1); -+ } -+ -+ return err; -+} -+ -+int au_opts_mount(struct super_block *sb, struct au_opts *opts) -+{ -+ int err; -+ unsigned int tmp; -+ aufs_bindex_t bindex, bend; -+ struct au_opt *opt; -+ struct au_opt_xino *opt_xino, xino; -+ struct au_sbinfo *sbinfo; -+ struct au_branch *br; -+ struct inode *dir; -+ -+ SiMustWriteLock(sb); -+ -+ err = 0; -+ opt_xino = NULL; -+ opt = opts->opt; -+ while (err >= 0 && opt->type != Opt_tail) -+ err = au_opt_simple(sb, opt++, opts); -+ if (err > 0) -+ err = 0; -+ else if (unlikely(err < 0)) -+ goto out; -+ -+ /* disable xino and udba temporary */ -+ sbinfo = au_sbi(sb); -+ tmp = sbinfo->si_mntflags; -+ au_opt_clr(sbinfo->si_mntflags, XINO); -+ au_opt_set_udba(sbinfo->si_mntflags, UDBA_REVAL); -+ -+ opt = opts->opt; -+ while (err >= 0 && opt->type != Opt_tail) -+ err = au_opt_br(sb, opt++, opts); -+ if (err > 0) -+ err = 0; -+ else if (unlikely(err < 0)) -+ goto out; -+ -+ bend = au_sbend(sb); -+ if (unlikely(bend < 0)) { -+ err = -EINVAL; -+ pr_err("no branches\n"); -+ goto out; -+ } -+ -+ if (au_opt_test(tmp, XINO)) -+ au_opt_set(sbinfo->si_mntflags, XINO); -+ opt = opts->opt; -+ while (!err && opt->type != Opt_tail) -+ err = au_opt_xino(sb, opt++, &opt_xino, opts); -+ if (unlikely(err)) -+ goto out; -+ -+ err = au_opts_verify(sb, sb->s_flags, tmp); -+ if (unlikely(err)) -+ goto out; -+ -+ /* restore xino */ -+ if (au_opt_test(tmp, XINO) && !opt_xino) { -+ xino.file = au_xino_def(sb); -+ err = PTR_ERR(xino.file); -+ if (IS_ERR(xino.file)) -+ goto out; -+ -+ err = au_xino_set(sb, &xino, /*remount*/0); -+ fput(xino.file); -+ if (unlikely(err)) -+ goto out; -+ } -+ -+ /* restore udba */ -+ tmp &= AuOptMask_UDBA; -+ sbinfo->si_mntflags &= ~AuOptMask_UDBA; -+ sbinfo->si_mntflags |= tmp; -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ err = au_hnotify_reset_br(tmp, br, br->br_perm); -+ if (unlikely(err)) -+ AuIOErr("hnotify failed on br %d, %d, ignored\n", -+ bindex, err); -+ /* go on even if err */ -+ } -+ if (au_opt_test(tmp, UDBA_HNOTIFY)) { -+ dir = sb->s_root->d_inode; -+ au_hn_reset(dir, au_hi_flags(dir, /*isdir*/1) & ~AuHi_XINO); -+ } -+ -+out: -+ return err; -+} -+ -+int au_opts_remount(struct super_block *sb, struct au_opts *opts) -+{ -+ int err, rerr; -+ unsigned char no_dreval; -+ struct inode *dir; -+ struct au_opt_xino *opt_xino; -+ struct au_opt *opt; -+ struct au_sbinfo *sbinfo; -+ -+ SiMustWriteLock(sb); -+ -+ err = 0; -+ dir = sb->s_root->d_inode; -+ sbinfo = au_sbi(sb); -+ opt_xino = NULL; -+ opt = opts->opt; -+ while (err >= 0 && opt->type != Opt_tail) { -+ err = au_opt_simple(sb, opt, opts); -+ if (!err) -+ err = au_opt_br(sb, opt, opts); -+ if (!err) -+ err = au_opt_xino(sb, opt, &opt_xino, opts); -+ opt++; -+ } -+ if (err > 0) -+ err = 0; -+ AuTraceErr(err); -+ /* go on even err */ -+ -+ no_dreval = !!au_ftest_si(sbinfo, NO_DREVAL); -+ rerr = au_opts_verify(sb, opts->sb_flags, /*pending*/0); -+ if (unlikely(rerr && !err)) -+ err = rerr; -+ -+ if (no_dreval != !!au_ftest_si(sbinfo, NO_DREVAL)) -+ au_fset_opts(opts->flags, REFRESH_IDOP); -+ -+ if (au_ftest_opts(opts->flags, TRUNC_XIB)) { -+ rerr = au_xib_trunc(sb); -+ if (unlikely(rerr && !err)) -+ err = rerr; -+ } -+ -+ /* will be handled by the caller */ -+ if (!au_ftest_opts(opts->flags, REFRESH) -+ && (opts->given_udba -+ || au_opt_test(sbinfo->si_mntflags, XINO) -+ || au_ftest_opts(opts->flags, REFRESH_IDOP) -+ )) -+ au_fset_opts(opts->flags, REFRESH); -+ -+ AuDbg("status 0x%x\n", opts->flags); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+unsigned int au_opt_udba(struct super_block *sb) -+{ -+ return au_mntflags(sb) & AuOptMask_UDBA; -+} -diff --git a/fs/aufs/opts.h b/fs/aufs/opts.h -new file mode 100644 -index 0000000..50949a0 ---- /dev/null -+++ b/fs/aufs/opts.h -@@ -0,0 +1,212 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * mount options/flags -+ */ -+ -+#ifndef __AUFS_OPTS_H__ -+#define __AUFS_OPTS_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+ -+struct file; -+struct super_block; -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* mount flags */ -+#define AuOpt_XINO 1 /* external inode number bitmap -+ and translation table */ -+#define AuOpt_TRUNC_XINO (1 << 1) /* truncate xino files */ -+#define AuOpt_UDBA_NONE (1 << 2) /* users direct branch access */ -+#define AuOpt_UDBA_REVAL (1 << 3) -+#define AuOpt_UDBA_HNOTIFY (1 << 4) -+#define AuOpt_SHWH (1 << 5) /* show whiteout */ -+#define AuOpt_PLINK (1 << 6) /* pseudo-link */ -+#define AuOpt_DIRPERM1 (1 << 7) /* ignore the lower dir's perm -+ bits */ -+#define AuOpt_REFROF (1 << 8) /* unimplemented */ -+#define AuOpt_ALWAYS_DIROPQ (1 << 9) /* policy to creating diropq */ -+#define AuOpt_SUM (1 << 10) /* summation for statfs(2) */ -+#define AuOpt_SUM_W (1 << 11) /* unimplemented */ -+#define AuOpt_WARN_PERM (1 << 12) /* warn when add-branch */ -+#define AuOpt_VERBOSE (1 << 13) /* busy inode when del-branch */ -+#define AuOpt_DIO (1 << 14) /* direct io */ -+ -+#ifndef CONFIG_AUFS_HNOTIFY -+#undef AuOpt_UDBA_HNOTIFY -+#define AuOpt_UDBA_HNOTIFY 0 -+#endif -+#ifndef CONFIG_AUFS_SHWH -+#undef AuOpt_SHWH -+#define AuOpt_SHWH 0 -+#endif -+ -+#define AuOpt_Def (AuOpt_XINO \ -+ | AuOpt_UDBA_REVAL \ -+ | AuOpt_PLINK \ -+ /* | AuOpt_DIRPERM1 */ \ -+ | AuOpt_WARN_PERM) -+#define AuOptMask_UDBA (AuOpt_UDBA_NONE \ -+ | AuOpt_UDBA_REVAL \ -+ | AuOpt_UDBA_HNOTIFY) -+ -+#define au_opt_test(flags, name) (flags & AuOpt_##name) -+#define au_opt_set(flags, name) do { \ -+ BUILD_BUG_ON(AuOpt_##name & AuOptMask_UDBA); \ -+ ((flags) |= AuOpt_##name); \ -+} while (0) -+#define au_opt_set_udba(flags, name) do { \ -+ (flags) &= ~AuOptMask_UDBA; \ -+ ((flags) |= AuOpt_##name); \ -+} while (0) -+#define au_opt_clr(flags, name) do { \ -+ ((flags) &= ~AuOpt_##name); \ -+} while (0) -+ -+static inline unsigned int au_opts_plink(unsigned int mntflags) -+{ -+#ifdef CONFIG_PROC_FS -+ return mntflags; -+#else -+ return mntflags & ~AuOpt_PLINK; -+#endif -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* policies to select one among multiple writable branches */ -+enum { -+ AuWbrCreate_TDP, /* top down parent */ -+ AuWbrCreate_RR, /* round robin */ -+ AuWbrCreate_MFS, /* most free space */ -+ AuWbrCreate_MFSV, /* mfs with seconds */ -+ AuWbrCreate_MFSRR, /* mfs then rr */ -+ AuWbrCreate_MFSRRV, /* mfs then rr with seconds */ -+ AuWbrCreate_PMFS, /* parent and mfs */ -+ AuWbrCreate_PMFSV, /* parent and mfs with seconds */ -+ AuWbrCreate_PMFSRR, /* parent, mfs and round-robin */ -+ AuWbrCreate_PMFSRRV, /* plus seconds */ -+ -+ AuWbrCreate_Def = AuWbrCreate_TDP -+}; -+ -+enum { -+ AuWbrCopyup_TDP, /* top down parent */ -+ AuWbrCopyup_BUP, /* bottom up parent */ -+ AuWbrCopyup_BU, /* bottom up */ -+ -+ AuWbrCopyup_Def = AuWbrCopyup_TDP -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct au_opt_add { -+ aufs_bindex_t bindex; -+ char *pathname; -+ int perm; -+ struct path path; -+}; -+ -+struct au_opt_del { -+ char *pathname; -+ struct path h_path; -+}; -+ -+struct au_opt_mod { -+ char *path; -+ int perm; -+ struct dentry *h_root; -+}; -+ -+struct au_opt_xino { -+ char *path; -+ struct file *file; -+}; -+ -+struct au_opt_xino_itrunc { -+ aufs_bindex_t bindex; -+}; -+ -+struct au_opt_wbr_create { -+ int wbr_create; -+ int mfs_second; -+ unsigned long long mfsrr_watermark; -+}; -+ -+struct au_opt { -+ int type; -+ union { -+ struct au_opt_xino xino; -+ struct au_opt_xino_itrunc xino_itrunc; -+ struct au_opt_add add; -+ struct au_opt_del del; -+ struct au_opt_mod mod; -+ int dirwh; -+ int rdcache; -+ unsigned int rdblk; -+ unsigned int rdhash; -+ int udba; -+ struct au_opt_wbr_create wbr_create; -+ int wbr_copyup; -+ unsigned int fhsm_second; -+ }; -+}; -+ -+/* opts flags */ -+#define AuOpts_REMOUNT 1 -+#define AuOpts_REFRESH (1 << 1) -+#define AuOpts_TRUNC_XIB (1 << 2) -+#define AuOpts_REFRESH_DYAOP (1 << 3) -+#define AuOpts_REFRESH_IDOP (1 << 4) -+#define au_ftest_opts(flags, name) ((flags) & AuOpts_##name) -+#define au_fset_opts(flags, name) \ -+ do { (flags) |= AuOpts_##name; } while (0) -+#define au_fclr_opts(flags, name) \ -+ do { (flags) &= ~AuOpts_##name; } while (0) -+ -+struct au_opts { -+ struct au_opt *opt; -+ int max_opt; -+ -+ unsigned int given_udba; -+ unsigned int flags; -+ unsigned long sb_flags; -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* opts.c */ -+void au_optstr_br_perm(au_br_perm_str_t *str, int perm); -+const char *au_optstr_udba(int udba); -+const char *au_optstr_wbr_copyup(int wbr_copyup); -+const char *au_optstr_wbr_create(int wbr_create); -+ -+void au_opts_free(struct au_opts *opts); -+int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts); -+int au_opts_verify(struct super_block *sb, unsigned long sb_flags, -+ unsigned int pending); -+int au_opts_mount(struct super_block *sb, struct au_opts *opts); -+int au_opts_remount(struct super_block *sb, struct au_opts *opts); -+ -+unsigned int au_opt_udba(struct super_block *sb); -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_OPTS_H__ */ -diff --git a/fs/aufs/plink.c b/fs/aufs/plink.c -new file mode 100644 -index 0000000..4f372ec ---- /dev/null -+++ b/fs/aufs/plink.c -@@ -0,0 +1,506 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * pseudo-link -+ */ -+ -+#include "aufs.h" -+ -+/* -+ * the pseudo-link maintenance mode. -+ * during a user process maintains the pseudo-links, -+ * prohibit adding a new plink and branch manipulation. -+ * -+ * Flags -+ * NOPLM: -+ * For entry functions which will handle plink, and i_mutex is already held -+ * in VFS. -+ * They cannot wait and should return an error at once. -+ * Callers has to check the error. -+ * NOPLMW: -+ * For entry functions which will handle plink, but i_mutex is not held -+ * in VFS. -+ * They can wait the plink maintenance mode to finish. -+ * -+ * They behave like F_SETLK and F_SETLKW. -+ * If the caller never handle plink, then both flags are unnecessary. -+ */ -+ -+int au_plink_maint(struct super_block *sb, int flags) -+{ -+ int err; -+ pid_t pid, ppid; -+ struct au_sbinfo *sbi; -+ -+ SiMustAnyLock(sb); -+ -+ err = 0; -+ if (!au_opt_test(au_mntflags(sb), PLINK)) -+ goto out; -+ -+ sbi = au_sbi(sb); -+ pid = sbi->si_plink_maint_pid; -+ if (!pid || pid == current->pid) -+ goto out; -+ -+ /* todo: it highly depends upon /sbin/mount.aufs */ -+ rcu_read_lock(); -+ ppid = task_pid_vnr(rcu_dereference(current->real_parent)); -+ rcu_read_unlock(); -+ if (pid == ppid) -+ goto out; -+ -+ if (au_ftest_lock(flags, NOPLMW)) { -+ /* if there is no i_mutex lock in VFS, we don't need to wait */ -+ /* AuDebugOn(!lockdep_depth(current)); */ -+ while (sbi->si_plink_maint_pid) { -+ si_read_unlock(sb); -+ /* gave up wake_up_bit() */ -+ wait_event(sbi->si_plink_wq, !sbi->si_plink_maint_pid); -+ -+ if (au_ftest_lock(flags, FLUSH)) -+ au_nwt_flush(&sbi->si_nowait); -+ si_noflush_read_lock(sb); -+ } -+ } else if (au_ftest_lock(flags, NOPLM)) { -+ AuDbg("ppid %d, pid %d\n", ppid, pid); -+ err = -EAGAIN; -+ } -+ -+out: -+ return err; -+} -+ -+void au_plink_maint_leave(struct au_sbinfo *sbinfo) -+{ -+ spin_lock(&sbinfo->si_plink_maint_lock); -+ sbinfo->si_plink_maint_pid = 0; -+ spin_unlock(&sbinfo->si_plink_maint_lock); -+ wake_up_all(&sbinfo->si_plink_wq); -+} -+ -+int au_plink_maint_enter(struct super_block *sb) -+{ -+ int err; -+ struct au_sbinfo *sbinfo; -+ -+ err = 0; -+ sbinfo = au_sbi(sb); -+ /* make sure i am the only one in this fs */ -+ si_write_lock(sb, AuLock_FLUSH); -+ if (au_opt_test(au_mntflags(sb), PLINK)) { -+ spin_lock(&sbinfo->si_plink_maint_lock); -+ if (!sbinfo->si_plink_maint_pid) -+ sbinfo->si_plink_maint_pid = current->pid; -+ else -+ err = -EBUSY; -+ spin_unlock(&sbinfo->si_plink_maint_lock); -+ } -+ si_write_unlock(sb); -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+#ifdef CONFIG_AUFS_DEBUG -+void au_plink_list(struct super_block *sb) -+{ -+ int i; -+ struct au_sbinfo *sbinfo; -+ struct hlist_head *plink_hlist; -+ struct au_icntnr *icntnr; -+ -+ SiMustAnyLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); -+ AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); -+ -+ for (i = 0; i < AuPlink_NHASH; i++) { -+ plink_hlist = &sbinfo->si_plink[i].head; -+ rcu_read_lock(); -+ hlist_for_each_entry_rcu(icntnr, plink_hlist, plink) -+ AuDbg("%lu\n", icntnr->vfs_inode.i_ino); -+ rcu_read_unlock(); -+ } -+} -+#endif -+ -+/* is the inode pseudo-linked? */ -+int au_plink_test(struct inode *inode) -+{ -+ int found, i; -+ struct au_sbinfo *sbinfo; -+ struct hlist_head *plink_hlist; -+ struct au_icntnr *icntnr; -+ -+ sbinfo = au_sbi(inode->i_sb); -+ AuRwMustAnyLock(&sbinfo->si_rwsem); -+ AuDebugOn(!au_opt_test(au_mntflags(inode->i_sb), PLINK)); -+ AuDebugOn(au_plink_maint(inode->i_sb, AuLock_NOPLM)); -+ -+ found = 0; -+ i = au_plink_hash(inode->i_ino); -+ plink_hlist = &sbinfo->si_plink[i].head; -+ rcu_read_lock(); -+ hlist_for_each_entry_rcu(icntnr, plink_hlist, plink) -+ if (&icntnr->vfs_inode == inode) { -+ found = 1; -+ break; -+ } -+ rcu_read_unlock(); -+ return found; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * generate a name for plink. -+ * the file will be stored under AUFS_WH_PLINKDIR. -+ */ -+/* 20 is max digits length of ulong 64 */ -+#define PLINK_NAME_LEN ((20 + 1) * 2) -+ -+static int plink_name(char *name, int len, struct inode *inode, -+ aufs_bindex_t bindex) -+{ -+ int rlen; -+ struct inode *h_inode; -+ -+ h_inode = au_h_iptr(inode, bindex); -+ rlen = snprintf(name, len, "%lu.%lu", inode->i_ino, h_inode->i_ino); -+ return rlen; -+} -+ -+struct au_do_plink_lkup_args { -+ struct dentry **errp; -+ struct qstr *tgtname; -+ struct dentry *h_parent; -+ struct au_branch *br; -+}; -+ -+static struct dentry *au_do_plink_lkup(struct qstr *tgtname, -+ struct dentry *h_parent, -+ struct au_branch *br) -+{ -+ struct dentry *h_dentry; -+ struct mutex *h_mtx; -+ -+ h_mtx = &h_parent->d_inode->i_mutex; -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD2); -+ h_dentry = vfsub_lkup_one(tgtname, h_parent); -+ mutex_unlock(h_mtx); -+ return h_dentry; -+} -+ -+static void au_call_do_plink_lkup(void *args) -+{ -+ struct au_do_plink_lkup_args *a = args; -+ *a->errp = au_do_plink_lkup(a->tgtname, a->h_parent, a->br); -+} -+ -+/* lookup the plink-ed @inode under the branch at @bindex */ -+struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex) -+{ -+ struct dentry *h_dentry, *h_parent; -+ struct au_branch *br; -+ struct inode *h_dir; -+ int wkq_err; -+ char a[PLINK_NAME_LEN]; -+ struct qstr tgtname = QSTR_INIT(a, 0); -+ -+ AuDebugOn(au_plink_maint(inode->i_sb, AuLock_NOPLM)); -+ -+ br = au_sbr(inode->i_sb, bindex); -+ h_parent = br->br_wbr->wbr_plink; -+ h_dir = h_parent->d_inode; -+ tgtname.len = plink_name(a, sizeof(a), inode, bindex); -+ -+ if (!uid_eq(current_fsuid(), GLOBAL_ROOT_UID)) { -+ struct au_do_plink_lkup_args args = { -+ .errp = &h_dentry, -+ .tgtname = &tgtname, -+ .h_parent = h_parent, -+ .br = br -+ }; -+ -+ wkq_err = au_wkq_wait(au_call_do_plink_lkup, &args); -+ if (unlikely(wkq_err)) -+ h_dentry = ERR_PTR(wkq_err); -+ } else -+ h_dentry = au_do_plink_lkup(&tgtname, h_parent, br); -+ -+ return h_dentry; -+} -+ -+/* create a pseudo-link */ -+static int do_whplink(struct qstr *tgt, struct dentry *h_parent, -+ struct dentry *h_dentry, struct au_branch *br) -+{ -+ int err; -+ struct path h_path = { -+ .mnt = au_br_mnt(br) -+ }; -+ struct inode *h_dir, *delegated; -+ -+ h_dir = h_parent->d_inode; -+ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_CHILD2); -+again: -+ h_path.dentry = vfsub_lkup_one(tgt, h_parent); -+ err = PTR_ERR(h_path.dentry); -+ if (IS_ERR(h_path.dentry)) -+ goto out; -+ -+ err = 0; -+ /* wh.plink dir is not monitored */ -+ /* todo: is it really safe? */ -+ if (h_path.dentry->d_inode -+ && h_path.dentry->d_inode != h_dentry->d_inode) { -+ delegated = NULL; -+ err = vfsub_unlink(h_dir, &h_path, &delegated, /*force*/0); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal unlink\n"); -+ iput(delegated); -+ } -+ dput(h_path.dentry); -+ h_path.dentry = NULL; -+ if (!err) -+ goto again; -+ } -+ if (!err && !h_path.dentry->d_inode) { -+ delegated = NULL; -+ err = vfsub_link(h_dentry, h_dir, &h_path, &delegated); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal link\n"); -+ iput(delegated); -+ } -+ } -+ dput(h_path.dentry); -+ -+out: -+ mutex_unlock(&h_dir->i_mutex); -+ return err; -+} -+ -+struct do_whplink_args { -+ int *errp; -+ struct qstr *tgt; -+ struct dentry *h_parent; -+ struct dentry *h_dentry; -+ struct au_branch *br; -+}; -+ -+static void call_do_whplink(void *args) -+{ -+ struct do_whplink_args *a = args; -+ *a->errp = do_whplink(a->tgt, a->h_parent, a->h_dentry, a->br); -+} -+ -+static int whplink(struct dentry *h_dentry, struct inode *inode, -+ aufs_bindex_t bindex, struct au_branch *br) -+{ -+ int err, wkq_err; -+ struct au_wbr *wbr; -+ struct dentry *h_parent; -+ struct inode *h_dir; -+ char a[PLINK_NAME_LEN]; -+ struct qstr tgtname = QSTR_INIT(a, 0); -+ -+ wbr = au_sbr(inode->i_sb, bindex)->br_wbr; -+ h_parent = wbr->wbr_plink; -+ h_dir = h_parent->d_inode; -+ tgtname.len = plink_name(a, sizeof(a), inode, bindex); -+ -+ /* always superio. */ -+ if (!uid_eq(current_fsuid(), GLOBAL_ROOT_UID)) { -+ struct do_whplink_args args = { -+ .errp = &err, -+ .tgt = &tgtname, -+ .h_parent = h_parent, -+ .h_dentry = h_dentry, -+ .br = br -+ }; -+ wkq_err = au_wkq_wait(call_do_whplink, &args); -+ if (unlikely(wkq_err)) -+ err = wkq_err; -+ } else -+ err = do_whplink(&tgtname, h_parent, h_dentry, br); -+ -+ return err; -+} -+ -+/* -+ * create a new pseudo-link for @h_dentry on @bindex. -+ * the linked inode is held in aufs @inode. -+ */ -+void au_plink_append(struct inode *inode, aufs_bindex_t bindex, -+ struct dentry *h_dentry) -+{ -+ struct super_block *sb; -+ struct au_sbinfo *sbinfo; -+ struct hlist_head *plink_hlist; -+ struct au_icntnr *icntnr; -+ struct au_sphlhead *sphl; -+ int found, err, cnt, i; -+ -+ sb = inode->i_sb; -+ sbinfo = au_sbi(sb); -+ AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); -+ AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); -+ -+ found = au_plink_test(inode); -+ if (found) -+ return; -+ -+ i = au_plink_hash(inode->i_ino); -+ sphl = sbinfo->si_plink + i; -+ plink_hlist = &sphl->head; -+ au_igrab(inode); -+ -+ spin_lock(&sphl->spin); -+ hlist_for_each_entry(icntnr, plink_hlist, plink) { -+ if (&icntnr->vfs_inode == inode) { -+ found = 1; -+ break; -+ } -+ } -+ if (!found) { -+ icntnr = container_of(inode, struct au_icntnr, vfs_inode); -+ hlist_add_head_rcu(&icntnr->plink, plink_hlist); -+ } -+ spin_unlock(&sphl->spin); -+ if (!found) { -+ cnt = au_sphl_count(sphl); -+#define msg "unexpectedly unblanced or too many pseudo-links" -+ if (cnt > AUFS_PLINK_WARN) -+ AuWarn1(msg ", %d\n", cnt); -+#undef msg -+ err = whplink(h_dentry, inode, bindex, au_sbr(sb, bindex)); -+ if (unlikely(err)) { -+ pr_warn("err %d, damaged pseudo link.\n", err); -+ au_sphl_del_rcu(&icntnr->plink, sphl); -+ iput(&icntnr->vfs_inode); -+ } -+ } else -+ iput(&icntnr->vfs_inode); -+} -+ -+/* free all plinks */ -+void au_plink_put(struct super_block *sb, int verbose) -+{ -+ int i, warned; -+ struct au_sbinfo *sbinfo; -+ struct hlist_head *plink_hlist; -+ struct hlist_node *tmp; -+ struct au_icntnr *icntnr; -+ -+ SiMustWriteLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); -+ AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); -+ -+ /* no spin_lock since sbinfo is write-locked */ -+ warned = 0; -+ for (i = 0; i < AuPlink_NHASH; i++) { -+ plink_hlist = &sbinfo->si_plink[i].head; -+ if (!warned && verbose && !hlist_empty(plink_hlist)) { -+ pr_warn("pseudo-link is not flushed"); -+ warned = 1; -+ } -+ hlist_for_each_entry_safe(icntnr, tmp, plink_hlist, plink) -+ iput(&icntnr->vfs_inode); -+ INIT_HLIST_HEAD(plink_hlist); -+ } -+} -+ -+void au_plink_clean(struct super_block *sb, int verbose) -+{ -+ struct dentry *root; -+ -+ root = sb->s_root; -+ aufs_write_lock(root); -+ if (au_opt_test(au_mntflags(sb), PLINK)) -+ au_plink_put(sb, verbose); -+ aufs_write_unlock(root); -+} -+ -+static int au_plink_do_half_refresh(struct inode *inode, aufs_bindex_t br_id) -+{ -+ int do_put; -+ aufs_bindex_t bstart, bend, bindex; -+ -+ do_put = 0; -+ bstart = au_ibstart(inode); -+ bend = au_ibend(inode); -+ if (bstart >= 0) { -+ for (bindex = bstart; bindex <= bend; bindex++) { -+ if (!au_h_iptr(inode, bindex) -+ || au_ii_br_id(inode, bindex) != br_id) -+ continue; -+ au_set_h_iptr(inode, bindex, NULL, 0); -+ do_put = 1; -+ break; -+ } -+ if (do_put) -+ for (bindex = bstart; bindex <= bend; bindex++) -+ if (au_h_iptr(inode, bindex)) { -+ do_put = 0; -+ break; -+ } -+ } else -+ do_put = 1; -+ -+ return do_put; -+} -+ -+/* free the plinks on a branch specified by @br_id */ -+void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id) -+{ -+ struct au_sbinfo *sbinfo; -+ struct hlist_head *plink_hlist; -+ struct hlist_node *tmp; -+ struct au_icntnr *icntnr; -+ struct inode *inode; -+ int i, do_put; -+ -+ SiMustWriteLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); -+ AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); -+ -+ /* no spin_lock since sbinfo is write-locked */ -+ for (i = 0; i < AuPlink_NHASH; i++) { -+ plink_hlist = &sbinfo->si_plink[i].head; -+ hlist_for_each_entry_safe(icntnr, tmp, plink_hlist, plink) { -+ inode = au_igrab(&icntnr->vfs_inode); -+ ii_write_lock_child(inode); -+ do_put = au_plink_do_half_refresh(inode, br_id); -+ if (do_put) { -+ hlist_del(&icntnr->plink); -+ iput(inode); -+ } -+ ii_write_unlock(inode); -+ iput(inode); -+ } -+ } -+} -diff --git a/fs/aufs/poll.c b/fs/aufs/poll.c -new file mode 100644 -index 0000000..eea19e7 ---- /dev/null -+++ b/fs/aufs/poll.c -@@ -0,0 +1,52 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * poll operation -+ * There is only one filesystem which implements ->poll operation, currently. -+ */ -+ -+#include "aufs.h" -+ -+unsigned int aufs_poll(struct file *file, poll_table *wait) -+{ -+ unsigned int mask; -+ int err; -+ struct file *h_file; -+ struct super_block *sb; -+ -+ /* We should pretend an error happened. */ -+ mask = POLLERR /* | POLLIN | POLLOUT */; -+ sb = file->f_dentry->d_sb; -+ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); -+ -+ h_file = au_read_pre(file, /*keep_fi*/0); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ /* it is not an error if h_file has no operation */ -+ mask = DEFAULT_POLLMASK; -+ if (h_file->f_op->poll) -+ mask = h_file->f_op->poll(h_file, wait); -+ fput(h_file); /* instead of au_read_post() */ -+ -+out: -+ si_read_unlock(sb); -+ AuTraceErr((int)mask); -+ return mask; -+} -diff --git a/fs/aufs/posix_acl.c b/fs/aufs/posix_acl.c -new file mode 100644 -index 0000000..89b4127 ---- /dev/null -+++ b/fs/aufs/posix_acl.c -@@ -0,0 +1,98 @@ -+/* -+ * Copyright (C) 2014-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * posix acl operations -+ */ -+ -+#include -+#include "aufs.h" -+ -+struct posix_acl *aufs_get_acl(struct inode *inode, int type) -+{ -+ struct posix_acl *acl; -+ int err; -+ aufs_bindex_t bindex; -+ struct inode *h_inode; -+ struct super_block *sb; -+ -+ acl = NULL; -+ sb = inode->i_sb; -+ si_read_lock(sb, AuLock_FLUSH); -+ ii_read_lock_child(inode); -+ if (!(sb->s_flags & MS_POSIXACL)) -+ goto out; -+ -+ bindex = au_ibstart(inode); -+ h_inode = au_h_iptr(inode, bindex); -+ if (unlikely(!h_inode -+ || ((h_inode->i_mode & S_IFMT) -+ != (inode->i_mode & S_IFMT)))) { -+ err = au_busy_or_stale(); -+ acl = ERR_PTR(err); -+ goto out; -+ } -+ -+ /* always topmost only */ -+ acl = get_acl(h_inode, type); -+ -+out: -+ ii_read_unlock(inode); -+ si_read_unlock(sb); -+ -+ AuTraceErrPtr(acl); -+ return acl; -+} -+ -+int aufs_set_acl(struct inode *inode, struct posix_acl *acl, int type) -+{ -+ int err; -+ ssize_t ssz; -+ struct dentry *dentry; -+ struct au_srxattr arg = { -+ .type = AU_ACL_SET, -+ .u.acl_set = { -+ .acl = acl, -+ .type = type -+ }, -+ }; -+ -+ mutex_lock(&inode->i_mutex); -+ if (inode->i_ino == AUFS_ROOT_INO) -+ dentry = dget(inode->i_sb->s_root); -+ else { -+ dentry = d_find_alias(inode); -+ if (!dentry) -+ dentry = d_find_any_alias(inode); -+ if (!dentry) { -+ pr_warn("cannot handle this inode, " -+ "please report to aufs-users ML\n"); -+ err = -ENOENT; -+ goto out; -+ } -+ } -+ -+ ssz = au_srxattr(dentry, &arg); -+ dput(dentry); -+ err = ssz; -+ if (ssz >= 0) -+ err = 0; -+ -+out: -+ mutex_unlock(&inode->i_mutex); -+ return err; -+} -diff --git a/fs/aufs/procfs.c b/fs/aufs/procfs.c -new file mode 100644 -index 0000000..a334330 ---- /dev/null -+++ b/fs/aufs/procfs.c -@@ -0,0 +1,169 @@ -+/* -+ * Copyright (C) 2010-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * procfs interfaces -+ */ -+ -+#include -+#include "aufs.h" -+ -+static int au_procfs_plm_release(struct inode *inode, struct file *file) -+{ -+ struct au_sbinfo *sbinfo; -+ -+ sbinfo = file->private_data; -+ if (sbinfo) { -+ au_plink_maint_leave(sbinfo); -+ kobject_put(&sbinfo->si_kobj); -+ } -+ -+ return 0; -+} -+ -+static void au_procfs_plm_write_clean(struct file *file) -+{ -+ struct au_sbinfo *sbinfo; -+ -+ sbinfo = file->private_data; -+ if (sbinfo) -+ au_plink_clean(sbinfo->si_sb, /*verbose*/0); -+} -+ -+static int au_procfs_plm_write_si(struct file *file, unsigned long id) -+{ -+ int err; -+ struct super_block *sb; -+ struct au_sbinfo *sbinfo; -+ -+ err = -EBUSY; -+ if (unlikely(file->private_data)) -+ goto out; -+ -+ sb = NULL; -+ /* don't use au_sbilist_lock() here */ -+ spin_lock(&au_sbilist.spin); -+ hlist_for_each_entry(sbinfo, &au_sbilist.head, si_list) -+ if (id == sysaufs_si_id(sbinfo)) { -+ kobject_get(&sbinfo->si_kobj); -+ sb = sbinfo->si_sb; -+ break; -+ } -+ spin_unlock(&au_sbilist.spin); -+ -+ err = -EINVAL; -+ if (unlikely(!sb)) -+ goto out; -+ -+ err = au_plink_maint_enter(sb); -+ if (!err) -+ /* keep kobject_get() */ -+ file->private_data = sbinfo; -+ else -+ kobject_put(&sbinfo->si_kobj); -+out: -+ return err; -+} -+ -+/* -+ * Accept a valid "si=xxxx" only. -+ * Once it is accepted successfully, accept "clean" too. -+ */ -+static ssize_t au_procfs_plm_write(struct file *file, const char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ ssize_t err; -+ unsigned long id; -+ /* last newline is allowed */ -+ char buf[3 + sizeof(unsigned long) * 2 + 1]; -+ -+ err = -EACCES; -+ if (unlikely(!capable(CAP_SYS_ADMIN))) -+ goto out; -+ -+ err = -EINVAL; -+ if (unlikely(count > sizeof(buf))) -+ goto out; -+ -+ err = copy_from_user(buf, ubuf, count); -+ if (unlikely(err)) { -+ err = -EFAULT; -+ goto out; -+ } -+ buf[count] = 0; -+ -+ err = -EINVAL; -+ if (!strcmp("clean", buf)) { -+ au_procfs_plm_write_clean(file); -+ goto out_success; -+ } else if (unlikely(strncmp("si=", buf, 3))) -+ goto out; -+ -+ err = kstrtoul(buf + 3, 16, &id); -+ if (unlikely(err)) -+ goto out; -+ -+ err = au_procfs_plm_write_si(file, id); -+ if (unlikely(err)) -+ goto out; -+ -+out_success: -+ err = count; /* success */ -+out: -+ return err; -+} -+ -+static const struct file_operations au_procfs_plm_fop = { -+ .write = au_procfs_plm_write, -+ .release = au_procfs_plm_release, -+ .owner = THIS_MODULE -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+static struct proc_dir_entry *au_procfs_dir; -+ -+void au_procfs_fin(void) -+{ -+ remove_proc_entry(AUFS_PLINK_MAINT_NAME, au_procfs_dir); -+ remove_proc_entry(AUFS_PLINK_MAINT_DIR, NULL); -+} -+ -+int __init au_procfs_init(void) -+{ -+ int err; -+ struct proc_dir_entry *entry; -+ -+ err = -ENOMEM; -+ au_procfs_dir = proc_mkdir(AUFS_PLINK_MAINT_DIR, NULL); -+ if (unlikely(!au_procfs_dir)) -+ goto out; -+ -+ entry = proc_create(AUFS_PLINK_MAINT_NAME, S_IFREG | S_IWUSR, -+ au_procfs_dir, &au_procfs_plm_fop); -+ if (unlikely(!entry)) -+ goto out_dir; -+ -+ err = 0; -+ goto out; /* success */ -+ -+ -+out_dir: -+ remove_proc_entry(AUFS_PLINK_MAINT_DIR, NULL); -+out: -+ return err; -+} -diff --git a/fs/aufs/rdu.c b/fs/aufs/rdu.c -new file mode 100644 -index 0000000..d22b2f8 ---- /dev/null -+++ b/fs/aufs/rdu.c -@@ -0,0 +1,388 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * readdir in userspace. -+ */ -+ -+#include -+#include -+#include -+#include "aufs.h" -+ -+/* bits for struct aufs_rdu.flags */ -+#define AuRdu_CALLED 1 -+#define AuRdu_CONT (1 << 1) -+#define AuRdu_FULL (1 << 2) -+#define au_ftest_rdu(flags, name) ((flags) & AuRdu_##name) -+#define au_fset_rdu(flags, name) \ -+ do { (flags) |= AuRdu_##name; } while (0) -+#define au_fclr_rdu(flags, name) \ -+ do { (flags) &= ~AuRdu_##name; } while (0) -+ -+struct au_rdu_arg { -+ struct dir_context ctx; -+ struct aufs_rdu *rdu; -+ union au_rdu_ent_ul ent; -+ unsigned long end; -+ -+ struct super_block *sb; -+ int err; -+}; -+ -+static int au_rdu_fill(struct dir_context *ctx, const char *name, int nlen, -+ loff_t offset, u64 h_ino, unsigned int d_type) -+{ -+ int err, len; -+ struct au_rdu_arg *arg = container_of(ctx, struct au_rdu_arg, ctx); -+ struct aufs_rdu *rdu = arg->rdu; -+ struct au_rdu_ent ent; -+ -+ err = 0; -+ arg->err = 0; -+ au_fset_rdu(rdu->cookie.flags, CALLED); -+ len = au_rdu_len(nlen); -+ if (arg->ent.ul + len < arg->end) { -+ ent.ino = h_ino; -+ ent.bindex = rdu->cookie.bindex; -+ ent.type = d_type; -+ ent.nlen = nlen; -+ if (unlikely(nlen > AUFS_MAX_NAMELEN)) -+ ent.type = DT_UNKNOWN; -+ -+ /* unnecessary to support mmap_sem since this is a dir */ -+ err = -EFAULT; -+ if (copy_to_user(arg->ent.e, &ent, sizeof(ent))) -+ goto out; -+ if (copy_to_user(arg->ent.e->name, name, nlen)) -+ goto out; -+ /* the terminating NULL */ -+ if (__put_user(0, arg->ent.e->name + nlen)) -+ goto out; -+ err = 0; -+ /* AuDbg("%p, %.*s\n", arg->ent.p, nlen, name); */ -+ arg->ent.ul += len; -+ rdu->rent++; -+ } else { -+ err = -EFAULT; -+ au_fset_rdu(rdu->cookie.flags, FULL); -+ rdu->full = 1; -+ rdu->tail = arg->ent; -+ } -+ -+out: -+ /* AuTraceErr(err); */ -+ return err; -+} -+ -+static int au_rdu_do(struct file *h_file, struct au_rdu_arg *arg) -+{ -+ int err; -+ loff_t offset; -+ struct au_rdu_cookie *cookie = &arg->rdu->cookie; -+ -+ /* we don't have to care (FMODE_32BITHASH | FMODE_64BITHASH) for ext4 */ -+ offset = vfsub_llseek(h_file, cookie->h_pos, SEEK_SET); -+ err = offset; -+ if (unlikely(offset != cookie->h_pos)) -+ goto out; -+ -+ err = 0; -+ do { -+ arg->err = 0; -+ au_fclr_rdu(cookie->flags, CALLED); -+ /* smp_mb(); */ -+ err = vfsub_iterate_dir(h_file, &arg->ctx); -+ if (err >= 0) -+ err = arg->err; -+ } while (!err -+ && au_ftest_rdu(cookie->flags, CALLED) -+ && !au_ftest_rdu(cookie->flags, FULL)); -+ cookie->h_pos = h_file->f_pos; -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+static int au_rdu(struct file *file, struct aufs_rdu *rdu) -+{ -+ int err; -+ aufs_bindex_t bend; -+ struct au_rdu_arg arg = { -+ .ctx = { -+ .actor = au_diractor(au_rdu_fill) -+ } -+ }; -+ struct dentry *dentry; -+ struct inode *inode; -+ struct file *h_file; -+ struct au_rdu_cookie *cookie = &rdu->cookie; -+ -+ err = !access_ok(VERIFY_WRITE, rdu->ent.e, rdu->sz); -+ if (unlikely(err)) { -+ err = -EFAULT; -+ AuTraceErr(err); -+ goto out; -+ } -+ rdu->rent = 0; -+ rdu->tail = rdu->ent; -+ rdu->full = 0; -+ arg.rdu = rdu; -+ arg.ent = rdu->ent; -+ arg.end = arg.ent.ul; -+ arg.end += rdu->sz; -+ -+ err = -ENOTDIR; -+ if (unlikely(!file->f_op->iterate)) -+ goto out; -+ -+ err = security_file_permission(file, MAY_READ); -+ AuTraceErr(err); -+ if (unlikely(err)) -+ goto out; -+ -+ dentry = file->f_dentry; -+ inode = dentry->d_inode; -+#if 1 -+ mutex_lock(&inode->i_mutex); -+#else -+ err = mutex_lock_killable(&inode->i_mutex); -+ AuTraceErr(err); -+ if (unlikely(err)) -+ goto out; -+#endif -+ -+ arg.sb = inode->i_sb; -+ err = si_read_lock(arg.sb, AuLock_FLUSH | AuLock_NOPLM); -+ if (unlikely(err)) -+ goto out_mtx; -+ err = au_alive_dir(dentry); -+ if (unlikely(err)) -+ goto out_si; -+ /* todo: reval? */ -+ fi_read_lock(file); -+ -+ err = -EAGAIN; -+ if (unlikely(au_ftest_rdu(cookie->flags, CONT) -+ && cookie->generation != au_figen(file))) -+ goto out_unlock; -+ -+ err = 0; -+ if (!rdu->blk) { -+ rdu->blk = au_sbi(arg.sb)->si_rdblk; -+ if (!rdu->blk) -+ rdu->blk = au_dir_size(file, /*dentry*/NULL); -+ } -+ bend = au_fbstart(file); -+ if (cookie->bindex < bend) -+ cookie->bindex = bend; -+ bend = au_fbend_dir(file); -+ /* AuDbg("b%d, b%d\n", cookie->bindex, bend); */ -+ for (; !err && cookie->bindex <= bend; -+ cookie->bindex++, cookie->h_pos = 0) { -+ h_file = au_hf_dir(file, cookie->bindex); -+ if (!h_file) -+ continue; -+ -+ au_fclr_rdu(cookie->flags, FULL); -+ err = au_rdu_do(h_file, &arg); -+ AuTraceErr(err); -+ if (unlikely(au_ftest_rdu(cookie->flags, FULL) || err)) -+ break; -+ } -+ AuDbg("rent %llu\n", rdu->rent); -+ -+ if (!err && !au_ftest_rdu(cookie->flags, CONT)) { -+ rdu->shwh = !!au_opt_test(au_sbi(arg.sb)->si_mntflags, SHWH); -+ au_fset_rdu(cookie->flags, CONT); -+ cookie->generation = au_figen(file); -+ } -+ -+ ii_read_lock_child(inode); -+ fsstack_copy_attr_atime(inode, au_h_iptr(inode, au_ibstart(inode))); -+ ii_read_unlock(inode); -+ -+out_unlock: -+ fi_read_unlock(file); -+out_si: -+ si_read_unlock(arg.sb); -+out_mtx: -+ mutex_unlock(&inode->i_mutex); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+static int au_rdu_ino(struct file *file, struct aufs_rdu *rdu) -+{ -+ int err; -+ ino_t ino; -+ unsigned long long nent; -+ union au_rdu_ent_ul *u; -+ struct au_rdu_ent ent; -+ struct super_block *sb; -+ -+ err = 0; -+ nent = rdu->nent; -+ u = &rdu->ent; -+ sb = file->f_dentry->d_sb; -+ si_read_lock(sb, AuLock_FLUSH); -+ while (nent-- > 0) { -+ /* unnecessary to support mmap_sem since this is a dir */ -+ err = copy_from_user(&ent, u->e, sizeof(ent)); -+ if (!err) -+ err = !access_ok(VERIFY_WRITE, &u->e->ino, sizeof(ino)); -+ if (unlikely(err)) { -+ err = -EFAULT; -+ AuTraceErr(err); -+ break; -+ } -+ -+ /* AuDbg("b%d, i%llu\n", ent.bindex, ent.ino); */ -+ if (!ent.wh) -+ err = au_ino(sb, ent.bindex, ent.ino, ent.type, &ino); -+ else -+ err = au_wh_ino(sb, ent.bindex, ent.ino, ent.type, -+ &ino); -+ if (unlikely(err)) { -+ AuTraceErr(err); -+ break; -+ } -+ -+ err = __put_user(ino, &u->e->ino); -+ if (unlikely(err)) { -+ err = -EFAULT; -+ AuTraceErr(err); -+ break; -+ } -+ u->ul += au_rdu_len(ent.nlen); -+ } -+ si_read_unlock(sb); -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_rdu_verify(struct aufs_rdu *rdu) -+{ -+ AuDbg("rdu{%llu, %p, %u | %u | %llu, %u, %u | " -+ "%llu, b%d, 0x%x, g%u}\n", -+ rdu->sz, rdu->ent.e, rdu->verify[AufsCtlRduV_SZ], -+ rdu->blk, -+ rdu->rent, rdu->shwh, rdu->full, -+ rdu->cookie.h_pos, rdu->cookie.bindex, rdu->cookie.flags, -+ rdu->cookie.generation); -+ -+ if (rdu->verify[AufsCtlRduV_SZ] == sizeof(*rdu)) -+ return 0; -+ -+ AuDbg("%u:%u\n", -+ rdu->verify[AufsCtlRduV_SZ], (unsigned int)sizeof(*rdu)); -+ return -EINVAL; -+} -+ -+long au_rdu_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -+{ -+ long err, e; -+ struct aufs_rdu rdu; -+ void __user *p = (void __user *)arg; -+ -+ err = copy_from_user(&rdu, p, sizeof(rdu)); -+ if (unlikely(err)) { -+ err = -EFAULT; -+ AuTraceErr(err); -+ goto out; -+ } -+ err = au_rdu_verify(&rdu); -+ if (unlikely(err)) -+ goto out; -+ -+ switch (cmd) { -+ case AUFS_CTL_RDU: -+ err = au_rdu(file, &rdu); -+ if (unlikely(err)) -+ break; -+ -+ e = copy_to_user(p, &rdu, sizeof(rdu)); -+ if (unlikely(e)) { -+ err = -EFAULT; -+ AuTraceErr(err); -+ } -+ break; -+ case AUFS_CTL_RDU_INO: -+ err = au_rdu_ino(file, &rdu); -+ break; -+ -+ default: -+ /* err = -ENOTTY; */ -+ err = -EINVAL; -+ } -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+#ifdef CONFIG_COMPAT -+long au_rdu_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -+{ -+ long err, e; -+ struct aufs_rdu rdu; -+ void __user *p = compat_ptr(arg); -+ -+ /* todo: get_user()? */ -+ err = copy_from_user(&rdu, p, sizeof(rdu)); -+ if (unlikely(err)) { -+ err = -EFAULT; -+ AuTraceErr(err); -+ goto out; -+ } -+ rdu.ent.e = compat_ptr(rdu.ent.ul); -+ err = au_rdu_verify(&rdu); -+ if (unlikely(err)) -+ goto out; -+ -+ switch (cmd) { -+ case AUFS_CTL_RDU: -+ err = au_rdu(file, &rdu); -+ if (unlikely(err)) -+ break; -+ -+ rdu.ent.ul = ptr_to_compat(rdu.ent.e); -+ rdu.tail.ul = ptr_to_compat(rdu.tail.e); -+ e = copy_to_user(p, &rdu, sizeof(rdu)); -+ if (unlikely(e)) { -+ err = -EFAULT; -+ AuTraceErr(err); -+ } -+ break; -+ case AUFS_CTL_RDU_INO: -+ err = au_rdu_ino(file, &rdu); -+ break; -+ -+ default: -+ /* err = -ENOTTY; */ -+ err = -EINVAL; -+ } -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+#endif -diff --git a/fs/aufs/rwsem.h b/fs/aufs/rwsem.h -new file mode 100644 -index 0000000..09ed5a0 ---- /dev/null -+++ b/fs/aufs/rwsem.h -@@ -0,0 +1,191 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * simple read-write semaphore wrappers -+ */ -+ -+#ifndef __AUFS_RWSEM_H__ -+#define __AUFS_RWSEM_H__ -+ -+#ifdef __KERNEL__ -+ -+#include "debug.h" -+ -+struct au_rwsem { -+ struct rw_semaphore rwsem; -+#ifdef CONFIG_AUFS_DEBUG -+ /* just for debugging, not almighty counter */ -+ atomic_t rcnt, wcnt; -+#endif -+}; -+ -+#ifdef CONFIG_AUFS_DEBUG -+#define AuDbgCntInit(rw) do { \ -+ atomic_set(&(rw)->rcnt, 0); \ -+ atomic_set(&(rw)->wcnt, 0); \ -+ smp_mb(); /* atomic set */ \ -+} while (0) -+ -+#define AuDbgRcntInc(rw) atomic_inc(&(rw)->rcnt) -+#define AuDbgRcntDec(rw) WARN_ON(atomic_dec_return(&(rw)->rcnt) < 0) -+#define AuDbgWcntInc(rw) atomic_inc(&(rw)->wcnt) -+#define AuDbgWcntDec(rw) WARN_ON(atomic_dec_return(&(rw)->wcnt) < 0) -+#else -+#define AuDbgCntInit(rw) do {} while (0) -+#define AuDbgRcntInc(rw) do {} while (0) -+#define AuDbgRcntDec(rw) do {} while (0) -+#define AuDbgWcntInc(rw) do {} while (0) -+#define AuDbgWcntDec(rw) do {} while (0) -+#endif /* CONFIG_AUFS_DEBUG */ -+ -+/* to debug easier, do not make them inlined functions */ -+#define AuRwMustNoWaiters(rw) AuDebugOn(!list_empty(&(rw)->rwsem.wait_list)) -+/* rwsem_is_locked() is unusable */ -+#define AuRwMustReadLock(rw) AuDebugOn(atomic_read(&(rw)->rcnt) <= 0) -+#define AuRwMustWriteLock(rw) AuDebugOn(atomic_read(&(rw)->wcnt) <= 0) -+#define AuRwMustAnyLock(rw) AuDebugOn(atomic_read(&(rw)->rcnt) <= 0 \ -+ && atomic_read(&(rw)->wcnt) <= 0) -+#define AuRwDestroy(rw) AuDebugOn(atomic_read(&(rw)->rcnt) \ -+ || atomic_read(&(rw)->wcnt)) -+ -+#define au_rw_class(rw, key) lockdep_set_class(&(rw)->rwsem, key) -+ -+static inline void au_rw_init(struct au_rwsem *rw) -+{ -+ AuDbgCntInit(rw); -+ init_rwsem(&rw->rwsem); -+} -+ -+static inline void au_rw_init_wlock(struct au_rwsem *rw) -+{ -+ au_rw_init(rw); -+ down_write(&rw->rwsem); -+ AuDbgWcntInc(rw); -+} -+ -+static inline void au_rw_init_wlock_nested(struct au_rwsem *rw, -+ unsigned int lsc) -+{ -+ au_rw_init(rw); -+ down_write_nested(&rw->rwsem, lsc); -+ AuDbgWcntInc(rw); -+} -+ -+static inline void au_rw_read_lock(struct au_rwsem *rw) -+{ -+ down_read(&rw->rwsem); -+ AuDbgRcntInc(rw); -+} -+ -+static inline void au_rw_read_lock_nested(struct au_rwsem *rw, unsigned int lsc) -+{ -+ down_read_nested(&rw->rwsem, lsc); -+ AuDbgRcntInc(rw); -+} -+ -+static inline void au_rw_read_unlock(struct au_rwsem *rw) -+{ -+ AuRwMustReadLock(rw); -+ AuDbgRcntDec(rw); -+ up_read(&rw->rwsem); -+} -+ -+static inline void au_rw_dgrade_lock(struct au_rwsem *rw) -+{ -+ AuRwMustWriteLock(rw); -+ AuDbgRcntInc(rw); -+ AuDbgWcntDec(rw); -+ downgrade_write(&rw->rwsem); -+} -+ -+static inline void au_rw_write_lock(struct au_rwsem *rw) -+{ -+ down_write(&rw->rwsem); -+ AuDbgWcntInc(rw); -+} -+ -+static inline void au_rw_write_lock_nested(struct au_rwsem *rw, -+ unsigned int lsc) -+{ -+ down_write_nested(&rw->rwsem, lsc); -+ AuDbgWcntInc(rw); -+} -+ -+static inline void au_rw_write_unlock(struct au_rwsem *rw) -+{ -+ AuRwMustWriteLock(rw); -+ AuDbgWcntDec(rw); -+ up_write(&rw->rwsem); -+} -+ -+/* why is not _nested version defined */ -+static inline int au_rw_read_trylock(struct au_rwsem *rw) -+{ -+ int ret; -+ -+ ret = down_read_trylock(&rw->rwsem); -+ if (ret) -+ AuDbgRcntInc(rw); -+ return ret; -+} -+ -+static inline int au_rw_write_trylock(struct au_rwsem *rw) -+{ -+ int ret; -+ -+ ret = down_write_trylock(&rw->rwsem); -+ if (ret) -+ AuDbgWcntInc(rw); -+ return ret; -+} -+ -+#undef AuDbgCntInit -+#undef AuDbgRcntInc -+#undef AuDbgRcntDec -+#undef AuDbgWcntInc -+#undef AuDbgWcntDec -+ -+#define AuSimpleLockRwsemFuncs(prefix, param, rwsem) \ -+static inline void prefix##_read_lock(param) \ -+{ au_rw_read_lock(rwsem); } \ -+static inline void prefix##_write_lock(param) \ -+{ au_rw_write_lock(rwsem); } \ -+static inline int prefix##_read_trylock(param) \ -+{ return au_rw_read_trylock(rwsem); } \ -+static inline int prefix##_write_trylock(param) \ -+{ return au_rw_write_trylock(rwsem); } -+/* why is not _nested version defined */ -+/* static inline void prefix##_read_trylock_nested(param, lsc) -+{ au_rw_read_trylock_nested(rwsem, lsc)); } -+static inline void prefix##_write_trylock_nestd(param, lsc) -+{ au_rw_write_trylock_nested(rwsem, lsc); } */ -+ -+#define AuSimpleUnlockRwsemFuncs(prefix, param, rwsem) \ -+static inline void prefix##_read_unlock(param) \ -+{ au_rw_read_unlock(rwsem); } \ -+static inline void prefix##_write_unlock(param) \ -+{ au_rw_write_unlock(rwsem); } \ -+static inline void prefix##_downgrade_lock(param) \ -+{ au_rw_dgrade_lock(rwsem); } -+ -+#define AuSimpleRwsemFuncs(prefix, param, rwsem) \ -+ AuSimpleLockRwsemFuncs(prefix, param, rwsem) \ -+ AuSimpleUnlockRwsemFuncs(prefix, param, rwsem) -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_RWSEM_H__ */ -diff --git a/fs/aufs/sbinfo.c b/fs/aufs/sbinfo.c -new file mode 100644 -index 0000000..ff13c9f ---- /dev/null -+++ b/fs/aufs/sbinfo.c -@@ -0,0 +1,348 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * superblock private data -+ */ -+ -+#include "aufs.h" -+ -+/* -+ * they are necessary regardless sysfs is disabled. -+ */ -+void au_si_free(struct kobject *kobj) -+{ -+ int i; -+ struct au_sbinfo *sbinfo; -+ char *locked __maybe_unused; /* debug only */ -+ -+ sbinfo = container_of(kobj, struct au_sbinfo, si_kobj); -+ for (i = 0; i < AuPlink_NHASH; i++) -+ AuDebugOn(!hlist_empty(&sbinfo->si_plink[i].head)); -+ AuDebugOn(atomic_read(&sbinfo->si_nowait.nw_len)); -+ -+ au_rw_write_lock(&sbinfo->si_rwsem); -+ au_br_free(sbinfo); -+ au_rw_write_unlock(&sbinfo->si_rwsem); -+ -+ kfree(sbinfo->si_branch); -+ for (i = 0; i < AU_NPIDMAP; i++) -+ kfree(sbinfo->au_si_pid.pid_bitmap[i]); -+ mutex_destroy(&sbinfo->au_si_pid.pid_mtx); -+ mutex_destroy(&sbinfo->si_xib_mtx); -+ AuRwDestroy(&sbinfo->si_rwsem); -+ -+ kfree(sbinfo); -+} -+ -+int au_si_alloc(struct super_block *sb) -+{ -+ int err, i; -+ struct au_sbinfo *sbinfo; -+ static struct lock_class_key aufs_si; -+ -+ err = -ENOMEM; -+ sbinfo = kzalloc(sizeof(*sbinfo), GFP_NOFS); -+ if (unlikely(!sbinfo)) -+ goto out; -+ -+ /* will be reallocated separately */ -+ sbinfo->si_branch = kzalloc(sizeof(*sbinfo->si_branch), GFP_NOFS); -+ if (unlikely(!sbinfo->si_branch)) -+ goto out_sbinfo; -+ -+ err = sysaufs_si_init(sbinfo); -+ if (unlikely(err)) -+ goto out_br; -+ -+ au_nwt_init(&sbinfo->si_nowait); -+ au_rw_init_wlock(&sbinfo->si_rwsem); -+ au_rw_class(&sbinfo->si_rwsem, &aufs_si); -+ mutex_init(&sbinfo->au_si_pid.pid_mtx); -+ -+ atomic_long_set(&sbinfo->si_ninodes, 0); -+ atomic_long_set(&sbinfo->si_nfiles, 0); -+ -+ sbinfo->si_bend = -1; -+ sbinfo->si_last_br_id = AUFS_BRANCH_MAX / 2; -+ -+ sbinfo->si_wbr_copyup = AuWbrCopyup_Def; -+ sbinfo->si_wbr_create = AuWbrCreate_Def; -+ sbinfo->si_wbr_copyup_ops = au_wbr_copyup_ops + sbinfo->si_wbr_copyup; -+ sbinfo->si_wbr_create_ops = au_wbr_create_ops + sbinfo->si_wbr_create; -+ -+ au_fhsm_init(sbinfo); -+ -+ sbinfo->si_mntflags = au_opts_plink(AuOpt_Def); -+ -+ sbinfo->si_xino_jiffy = jiffies; -+ sbinfo->si_xino_expire -+ = msecs_to_jiffies(AUFS_XINO_DEF_SEC * MSEC_PER_SEC); -+ mutex_init(&sbinfo->si_xib_mtx); -+ sbinfo->si_xino_brid = -1; -+ /* leave si_xib_last_pindex and si_xib_next_bit */ -+ -+ au_sphl_init(&sbinfo->si_aopen); -+ -+ sbinfo->si_rdcache = msecs_to_jiffies(AUFS_RDCACHE_DEF * MSEC_PER_SEC); -+ sbinfo->si_rdblk = AUFS_RDBLK_DEF; -+ sbinfo->si_rdhash = AUFS_RDHASH_DEF; -+ sbinfo->si_dirwh = AUFS_DIRWH_DEF; -+ -+ for (i = 0; i < AuPlink_NHASH; i++) -+ au_sphl_init(sbinfo->si_plink + i); -+ init_waitqueue_head(&sbinfo->si_plink_wq); -+ spin_lock_init(&sbinfo->si_plink_maint_lock); -+ -+ au_sphl_init(&sbinfo->si_files); -+ -+ /* with getattr by default */ -+ sbinfo->si_iop_array = aufs_iop; -+ -+ /* leave other members for sysaufs and si_mnt. */ -+ sbinfo->si_sb = sb; -+ sb->s_fs_info = sbinfo; -+ si_pid_set(sb); -+ return 0; /* success */ -+ -+out_br: -+ kfree(sbinfo->si_branch); -+out_sbinfo: -+ kfree(sbinfo); -+out: -+ return err; -+} -+ -+int au_sbr_realloc(struct au_sbinfo *sbinfo, int nbr) -+{ -+ int err, sz; -+ struct au_branch **brp; -+ -+ AuRwMustWriteLock(&sbinfo->si_rwsem); -+ -+ err = -ENOMEM; -+ sz = sizeof(*brp) * (sbinfo->si_bend + 1); -+ if (unlikely(!sz)) -+ sz = sizeof(*brp); -+ brp = au_kzrealloc(sbinfo->si_branch, sz, sizeof(*brp) * nbr, GFP_NOFS); -+ if (brp) { -+ sbinfo->si_branch = brp; -+ err = 0; -+ } -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+unsigned int au_sigen_inc(struct super_block *sb) -+{ -+ unsigned int gen; -+ -+ SiMustWriteLock(sb); -+ -+ gen = ++au_sbi(sb)->si_generation; -+ au_update_digen(sb->s_root); -+ au_update_iigen(sb->s_root->d_inode, /*half*/0); -+ sb->s_root->d_inode->i_version++; -+ return gen; -+} -+ -+aufs_bindex_t au_new_br_id(struct super_block *sb) -+{ -+ aufs_bindex_t br_id; -+ int i; -+ struct au_sbinfo *sbinfo; -+ -+ SiMustWriteLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ for (i = 0; i <= AUFS_BRANCH_MAX; i++) { -+ br_id = ++sbinfo->si_last_br_id; -+ AuDebugOn(br_id < 0); -+ if (br_id && au_br_index(sb, br_id) < 0) -+ return br_id; -+ } -+ -+ return -1; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* it is ok that new 'nwt' tasks are appended while we are sleeping */ -+int si_read_lock(struct super_block *sb, int flags) -+{ -+ int err; -+ -+ err = 0; -+ if (au_ftest_lock(flags, FLUSH)) -+ au_nwt_flush(&au_sbi(sb)->si_nowait); -+ -+ si_noflush_read_lock(sb); -+ err = au_plink_maint(sb, flags); -+ if (unlikely(err)) -+ si_read_unlock(sb); -+ -+ return err; -+} -+ -+int si_write_lock(struct super_block *sb, int flags) -+{ -+ int err; -+ -+ if (au_ftest_lock(flags, FLUSH)) -+ au_nwt_flush(&au_sbi(sb)->si_nowait); -+ -+ si_noflush_write_lock(sb); -+ err = au_plink_maint(sb, flags); -+ if (unlikely(err)) -+ si_write_unlock(sb); -+ -+ return err; -+} -+ -+/* dentry and super_block lock. call at entry point */ -+int aufs_read_lock(struct dentry *dentry, int flags) -+{ -+ int err; -+ struct super_block *sb; -+ -+ sb = dentry->d_sb; -+ err = si_read_lock(sb, flags); -+ if (unlikely(err)) -+ goto out; -+ -+ if (au_ftest_lock(flags, DW)) -+ di_write_lock_child(dentry); -+ else -+ di_read_lock_child(dentry, flags); -+ -+ if (au_ftest_lock(flags, GEN)) { -+ err = au_digen_test(dentry, au_sigen(sb)); -+ if (!au_opt_test(au_mntflags(sb), UDBA_NONE)) -+ AuDebugOn(!err && au_dbrange_test(dentry)); -+ else if (!err) -+ err = au_dbrange_test(dentry); -+ if (unlikely(err)) -+ aufs_read_unlock(dentry, flags); -+ } -+ -+out: -+ return err; -+} -+ -+void aufs_read_unlock(struct dentry *dentry, int flags) -+{ -+ if (au_ftest_lock(flags, DW)) -+ di_write_unlock(dentry); -+ else -+ di_read_unlock(dentry, flags); -+ si_read_unlock(dentry->d_sb); -+} -+ -+void aufs_write_lock(struct dentry *dentry) -+{ -+ si_write_lock(dentry->d_sb, AuLock_FLUSH | AuLock_NOPLMW); -+ di_write_lock_child(dentry); -+} -+ -+void aufs_write_unlock(struct dentry *dentry) -+{ -+ di_write_unlock(dentry); -+ si_write_unlock(dentry->d_sb); -+} -+ -+int aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int flags) -+{ -+ int err; -+ unsigned int sigen; -+ struct super_block *sb; -+ -+ sb = d1->d_sb; -+ err = si_read_lock(sb, flags); -+ if (unlikely(err)) -+ goto out; -+ -+ di_write_lock2_child(d1, d2, au_ftest_lock(flags, DIRS)); -+ -+ if (au_ftest_lock(flags, GEN)) { -+ sigen = au_sigen(sb); -+ err = au_digen_test(d1, sigen); -+ AuDebugOn(!err && au_dbrange_test(d1)); -+ if (!err) { -+ err = au_digen_test(d2, sigen); -+ AuDebugOn(!err && au_dbrange_test(d2)); -+ } -+ if (unlikely(err)) -+ aufs_read_and_write_unlock2(d1, d2); -+ } -+ -+out: -+ return err; -+} -+ -+void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2) -+{ -+ di_write_unlock2(d1, d2); -+ si_read_unlock(d1->d_sb); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void si_pid_alloc(struct au_si_pid *au_si_pid, int idx) -+{ -+ unsigned long *p; -+ -+ BUILD_BUG_ON(sizeof(unsigned long) != -+ sizeof(*au_si_pid->pid_bitmap)); -+ -+ mutex_lock(&au_si_pid->pid_mtx); -+ p = au_si_pid->pid_bitmap[idx]; -+ while (!p) { -+ /* -+ * bad approach. -+ * but keeping 'si_pid_set()' void is more important. -+ */ -+ p = kcalloc(BITS_TO_LONGS(AU_PIDSTEP), -+ sizeof(*au_si_pid->pid_bitmap), -+ GFP_NOFS); -+ if (p) -+ break; -+ cond_resched(); -+ } -+ au_si_pid->pid_bitmap[idx] = p; -+ mutex_unlock(&au_si_pid->pid_mtx); -+} -+ -+void si_pid_set(struct super_block *sb) -+{ -+ pid_t bit; -+ int idx; -+ unsigned long *bitmap; -+ struct au_si_pid *au_si_pid; -+ -+ si_pid_idx_bit(&idx, &bit); -+ au_si_pid = &au_sbi(sb)->au_si_pid; -+ bitmap = au_si_pid->pid_bitmap[idx]; -+ if (!bitmap) { -+ si_pid_alloc(au_si_pid, idx); -+ bitmap = au_si_pid->pid_bitmap[idx]; -+ } -+ AuDebugOn(test_bit(bit, bitmap)); -+ set_bit(bit, bitmap); -+ /* smp_mb(); */ -+} -diff --git a/fs/aufs/spl.h b/fs/aufs/spl.h -new file mode 100644 -index 0000000..945343a ---- /dev/null -+++ b/fs/aufs/spl.h -@@ -0,0 +1,111 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * simple list protected by a spinlock -+ */ -+ -+#ifndef __AUFS_SPL_H__ -+#define __AUFS_SPL_H__ -+ -+#ifdef __KERNEL__ -+ -+struct au_splhead { -+ spinlock_t spin; -+ struct list_head head; -+}; -+ -+static inline void au_spl_init(struct au_splhead *spl) -+{ -+ spin_lock_init(&spl->spin); -+ INIT_LIST_HEAD(&spl->head); -+} -+ -+static inline void au_spl_add(struct list_head *list, struct au_splhead *spl) -+{ -+ spin_lock(&spl->spin); -+ list_add(list, &spl->head); -+ spin_unlock(&spl->spin); -+} -+ -+static inline void au_spl_del(struct list_head *list, struct au_splhead *spl) -+{ -+ spin_lock(&spl->spin); -+ list_del(list); -+ spin_unlock(&spl->spin); -+} -+ -+static inline void au_spl_del_rcu(struct list_head *list, -+ struct au_splhead *spl) -+{ -+ spin_lock(&spl->spin); -+ list_del_rcu(list); -+ spin_unlock(&spl->spin); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct au_sphlhead { -+ spinlock_t spin; -+ struct hlist_head head; -+}; -+ -+static inline void au_sphl_init(struct au_sphlhead *sphl) -+{ -+ spin_lock_init(&sphl->spin); -+ INIT_HLIST_HEAD(&sphl->head); -+} -+ -+static inline void au_sphl_add(struct hlist_node *hlist, -+ struct au_sphlhead *sphl) -+{ -+ spin_lock(&sphl->spin); -+ hlist_add_head(hlist, &sphl->head); -+ spin_unlock(&sphl->spin); -+} -+ -+static inline void au_sphl_del(struct hlist_node *hlist, -+ struct au_sphlhead *sphl) -+{ -+ spin_lock(&sphl->spin); -+ hlist_del(hlist); -+ spin_unlock(&sphl->spin); -+} -+ -+static inline void au_sphl_del_rcu(struct hlist_node *hlist, -+ struct au_sphlhead *sphl) -+{ -+ spin_lock(&sphl->spin); -+ hlist_del_rcu(hlist); -+ spin_unlock(&sphl->spin); -+} -+ -+static inline unsigned long au_sphl_count(struct au_sphlhead *sphl) -+{ -+ unsigned long cnt; -+ struct hlist_node *pos; -+ -+ cnt = 0; -+ spin_lock(&sphl->spin); -+ hlist_for_each(pos, &sphl->head) -+ cnt++; -+ spin_unlock(&sphl->spin); -+ return cnt; -+} -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_SPL_H__ */ -diff --git a/fs/aufs/super.c b/fs/aufs/super.c -new file mode 100644 -index 0000000..64a6bb4 ---- /dev/null -+++ b/fs/aufs/super.c -@@ -0,0 +1,1041 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * mount and super_block operations -+ */ -+ -+#include -+#include -+#include -+#include -+#include "aufs.h" -+ -+/* -+ * super_operations -+ */ -+static struct inode *aufs_alloc_inode(struct super_block *sb __maybe_unused) -+{ -+ struct au_icntnr *c; -+ -+ c = au_cache_alloc_icntnr(); -+ if (c) { -+ au_icntnr_init(c); -+ c->vfs_inode.i_version = 1; /* sigen(sb); */ -+ c->iinfo.ii_hinode = NULL; -+ return &c->vfs_inode; -+ } -+ return NULL; -+} -+ -+static void aufs_destroy_inode_cb(struct rcu_head *head) -+{ -+ struct inode *inode = container_of(head, struct inode, i_rcu); -+ -+ INIT_HLIST_HEAD(&inode->i_dentry); -+ au_cache_free_icntnr(container_of(inode, struct au_icntnr, vfs_inode)); -+} -+ -+static void aufs_destroy_inode(struct inode *inode) -+{ -+ au_iinfo_fin(inode); -+ call_rcu(&inode->i_rcu, aufs_destroy_inode_cb); -+} -+ -+struct inode *au_iget_locked(struct super_block *sb, ino_t ino) -+{ -+ struct inode *inode; -+ int err; -+ -+ inode = iget_locked(sb, ino); -+ if (unlikely(!inode)) { -+ inode = ERR_PTR(-ENOMEM); -+ goto out; -+ } -+ if (!(inode->i_state & I_NEW)) -+ goto out; -+ -+ err = au_xigen_new(inode); -+ if (!err) -+ err = au_iinfo_init(inode); -+ if (!err) -+ inode->i_version++; -+ else { -+ iget_failed(inode); -+ inode = ERR_PTR(err); -+ } -+ -+out: -+ /* never return NULL */ -+ AuDebugOn(!inode); -+ AuTraceErrPtr(inode); -+ return inode; -+} -+ -+/* lock free root dinfo */ -+static int au_show_brs(struct seq_file *seq, struct super_block *sb) -+{ -+ int err; -+ aufs_bindex_t bindex, bend; -+ struct path path; -+ struct au_hdentry *hdp; -+ struct au_branch *br; -+ au_br_perm_str_t perm; -+ -+ err = 0; -+ bend = au_sbend(sb); -+ hdp = au_di(sb->s_root)->di_hdentry; -+ for (bindex = 0; !err && bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ path.mnt = au_br_mnt(br); -+ path.dentry = hdp[bindex].hd_dentry; -+ err = au_seq_path(seq, &path); -+ if (!err) { -+ au_optstr_br_perm(&perm, br->br_perm); -+ err = seq_printf(seq, "=%s", perm.a); -+ if (err == -1) -+ err = -E2BIG; -+ } -+ if (!err && bindex != bend) -+ err = seq_putc(seq, ':'); -+ } -+ -+ return err; -+} -+ -+static void au_show_wbr_create(struct seq_file *m, int v, -+ struct au_sbinfo *sbinfo) -+{ -+ const char *pat; -+ -+ AuRwMustAnyLock(&sbinfo->si_rwsem); -+ -+ seq_puts(m, ",create="); -+ pat = au_optstr_wbr_create(v); -+ switch (v) { -+ case AuWbrCreate_TDP: -+ case AuWbrCreate_RR: -+ case AuWbrCreate_MFS: -+ case AuWbrCreate_PMFS: -+ seq_puts(m, pat); -+ break; -+ case AuWbrCreate_MFSV: -+ seq_printf(m, /*pat*/"mfs:%lu", -+ jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire) -+ / MSEC_PER_SEC); -+ break; -+ case AuWbrCreate_PMFSV: -+ seq_printf(m, /*pat*/"pmfs:%lu", -+ jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire) -+ / MSEC_PER_SEC); -+ break; -+ case AuWbrCreate_MFSRR: -+ seq_printf(m, /*pat*/"mfsrr:%llu", -+ sbinfo->si_wbr_mfs.mfsrr_watermark); -+ break; -+ case AuWbrCreate_MFSRRV: -+ seq_printf(m, /*pat*/"mfsrr:%llu:%lu", -+ sbinfo->si_wbr_mfs.mfsrr_watermark, -+ jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire) -+ / MSEC_PER_SEC); -+ break; -+ case AuWbrCreate_PMFSRR: -+ seq_printf(m, /*pat*/"pmfsrr:%llu", -+ sbinfo->si_wbr_mfs.mfsrr_watermark); -+ break; -+ case AuWbrCreate_PMFSRRV: -+ seq_printf(m, /*pat*/"pmfsrr:%llu:%lu", -+ sbinfo->si_wbr_mfs.mfsrr_watermark, -+ jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire) -+ / MSEC_PER_SEC); -+ break; -+ } -+} -+ -+static int au_show_xino(struct seq_file *seq, struct super_block *sb) -+{ -+#ifdef CONFIG_SYSFS -+ return 0; -+#else -+ int err; -+ const int len = sizeof(AUFS_XINO_FNAME) - 1; -+ aufs_bindex_t bindex, brid; -+ struct qstr *name; -+ struct file *f; -+ struct dentry *d, *h_root; -+ struct au_hdentry *hdp; -+ -+ AuRwMustAnyLock(&sbinfo->si_rwsem); -+ -+ err = 0; -+ f = au_sbi(sb)->si_xib; -+ if (!f) -+ goto out; -+ -+ /* stop printing the default xino path on the first writable branch */ -+ h_root = NULL; -+ brid = au_xino_brid(sb); -+ if (brid >= 0) { -+ bindex = au_br_index(sb, brid); -+ hdp = au_di(sb->s_root)->di_hdentry; -+ h_root = hdp[0 + bindex].hd_dentry; -+ } -+ d = f->f_dentry; -+ name = &d->d_name; -+ /* safe ->d_parent because the file is unlinked */ -+ if (d->d_parent == h_root -+ && name->len == len -+ && !memcmp(name->name, AUFS_XINO_FNAME, len)) -+ goto out; -+ -+ seq_puts(seq, ",xino="); -+ err = au_xino_path(seq, f); -+ -+out: -+ return err; -+#endif -+} -+ -+/* seq_file will re-call me in case of too long string */ -+static int aufs_show_options(struct seq_file *m, struct dentry *dentry) -+{ -+ int err; -+ unsigned int mnt_flags, v; -+ struct super_block *sb; -+ struct au_sbinfo *sbinfo; -+ -+#define AuBool(name, str) do { \ -+ v = au_opt_test(mnt_flags, name); \ -+ if (v != au_opt_test(AuOpt_Def, name)) \ -+ seq_printf(m, ",%s" #str, v ? "" : "no"); \ -+} while (0) -+ -+#define AuStr(name, str) do { \ -+ v = mnt_flags & AuOptMask_##name; \ -+ if (v != (AuOpt_Def & AuOptMask_##name)) \ -+ seq_printf(m, "," #str "=%s", au_optstr_##str(v)); \ -+} while (0) -+ -+#define AuUInt(name, str, val) do { \ -+ if (val != AUFS_##name##_DEF) \ -+ seq_printf(m, "," #str "=%u", val); \ -+} while (0) -+ -+ sb = dentry->d_sb; -+ if (sb->s_flags & MS_POSIXACL) -+ seq_puts(m, ",acl"); -+ -+ /* lock free root dinfo */ -+ si_noflush_read_lock(sb); -+ sbinfo = au_sbi(sb); -+ seq_printf(m, ",si=%lx", sysaufs_si_id(sbinfo)); -+ -+ mnt_flags = au_mntflags(sb); -+ if (au_opt_test(mnt_flags, XINO)) { -+ err = au_show_xino(m, sb); -+ if (unlikely(err)) -+ goto out; -+ } else -+ seq_puts(m, ",noxino"); -+ -+ AuBool(TRUNC_XINO, trunc_xino); -+ AuStr(UDBA, udba); -+ AuBool(SHWH, shwh); -+ AuBool(PLINK, plink); -+ AuBool(DIO, dio); -+ AuBool(DIRPERM1, dirperm1); -+ /* AuBool(REFROF, refrof); */ -+ -+ v = sbinfo->si_wbr_create; -+ if (v != AuWbrCreate_Def) -+ au_show_wbr_create(m, v, sbinfo); -+ -+ v = sbinfo->si_wbr_copyup; -+ if (v != AuWbrCopyup_Def) -+ seq_printf(m, ",cpup=%s", au_optstr_wbr_copyup(v)); -+ -+ v = au_opt_test(mnt_flags, ALWAYS_DIROPQ); -+ if (v != au_opt_test(AuOpt_Def, ALWAYS_DIROPQ)) -+ seq_printf(m, ",diropq=%c", v ? 'a' : 'w'); -+ -+ AuUInt(DIRWH, dirwh, sbinfo->si_dirwh); -+ -+ v = jiffies_to_msecs(sbinfo->si_rdcache) / MSEC_PER_SEC; -+ AuUInt(RDCACHE, rdcache, v); -+ -+ AuUInt(RDBLK, rdblk, sbinfo->si_rdblk); -+ AuUInt(RDHASH, rdhash, sbinfo->si_rdhash); -+ -+ au_fhsm_show(m, sbinfo); -+ -+ AuBool(SUM, sum); -+ /* AuBool(SUM_W, wsum); */ -+ AuBool(WARN_PERM, warn_perm); -+ AuBool(VERBOSE, verbose); -+ -+out: -+ /* be sure to print "br:" last */ -+ if (!sysaufs_brs) { -+ seq_puts(m, ",br:"); -+ au_show_brs(m, sb); -+ } -+ si_read_unlock(sb); -+ return 0; -+ -+#undef AuBool -+#undef AuStr -+#undef AuUInt -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* sum mode which returns the summation for statfs(2) */ -+ -+static u64 au_add_till_max(u64 a, u64 b) -+{ -+ u64 old; -+ -+ old = a; -+ a += b; -+ if (old <= a) -+ return a; -+ return ULLONG_MAX; -+} -+ -+static u64 au_mul_till_max(u64 a, long mul) -+{ -+ u64 old; -+ -+ old = a; -+ a *= mul; -+ if (old <= a) -+ return a; -+ return ULLONG_MAX; -+} -+ -+static int au_statfs_sum(struct super_block *sb, struct kstatfs *buf) -+{ -+ int err; -+ long bsize, factor; -+ u64 blocks, bfree, bavail, files, ffree; -+ aufs_bindex_t bend, bindex, i; -+ unsigned char shared; -+ struct path h_path; -+ struct super_block *h_sb; -+ -+ err = 0; -+ bsize = LONG_MAX; -+ files = 0; -+ ffree = 0; -+ blocks = 0; -+ bfree = 0; -+ bavail = 0; -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { -+ h_path.mnt = au_sbr_mnt(sb, bindex); -+ h_sb = h_path.mnt->mnt_sb; -+ shared = 0; -+ for (i = 0; !shared && i < bindex; i++) -+ shared = (au_sbr_sb(sb, i) == h_sb); -+ if (shared) -+ continue; -+ -+ /* sb->s_root for NFS is unreliable */ -+ h_path.dentry = h_path.mnt->mnt_root; -+ err = vfs_statfs(&h_path, buf); -+ if (unlikely(err)) -+ goto out; -+ -+ if (bsize > buf->f_bsize) { -+ /* -+ * we will reduce bsize, so we have to expand blocks -+ * etc. to match them again -+ */ -+ factor = (bsize / buf->f_bsize); -+ blocks = au_mul_till_max(blocks, factor); -+ bfree = au_mul_till_max(bfree, factor); -+ bavail = au_mul_till_max(bavail, factor); -+ bsize = buf->f_bsize; -+ } -+ -+ factor = (buf->f_bsize / bsize); -+ blocks = au_add_till_max(blocks, -+ au_mul_till_max(buf->f_blocks, factor)); -+ bfree = au_add_till_max(bfree, -+ au_mul_till_max(buf->f_bfree, factor)); -+ bavail = au_add_till_max(bavail, -+ au_mul_till_max(buf->f_bavail, factor)); -+ files = au_add_till_max(files, buf->f_files); -+ ffree = au_add_till_max(ffree, buf->f_ffree); -+ } -+ -+ buf->f_bsize = bsize; -+ buf->f_blocks = blocks; -+ buf->f_bfree = bfree; -+ buf->f_bavail = bavail; -+ buf->f_files = files; -+ buf->f_ffree = ffree; -+ buf->f_frsize = 0; -+ -+out: -+ return err; -+} -+ -+static int aufs_statfs(struct dentry *dentry, struct kstatfs *buf) -+{ -+ int err; -+ struct path h_path; -+ struct super_block *sb; -+ -+ /* lock free root dinfo */ -+ sb = dentry->d_sb; -+ si_noflush_read_lock(sb); -+ if (!au_opt_test(au_mntflags(sb), SUM)) { -+ /* sb->s_root for NFS is unreliable */ -+ h_path.mnt = au_sbr_mnt(sb, 0); -+ h_path.dentry = h_path.mnt->mnt_root; -+ err = vfs_statfs(&h_path, buf); -+ } else -+ err = au_statfs_sum(sb, buf); -+ si_read_unlock(sb); -+ -+ if (!err) { -+ buf->f_type = AUFS_SUPER_MAGIC; -+ buf->f_namelen = AUFS_MAX_NAMELEN; -+ memset(&buf->f_fsid, 0, sizeof(buf->f_fsid)); -+ } -+ /* buf->f_bsize = buf->f_blocks = buf->f_bfree = buf->f_bavail = -1; */ -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int aufs_sync_fs(struct super_block *sb, int wait) -+{ -+ int err, e; -+ aufs_bindex_t bend, bindex; -+ struct au_branch *br; -+ struct super_block *h_sb; -+ -+ err = 0; -+ si_noflush_read_lock(sb); -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ if (!au_br_writable(br->br_perm)) -+ continue; -+ -+ h_sb = au_sbr_sb(sb, bindex); -+ if (h_sb->s_op->sync_fs) { -+ e = h_sb->s_op->sync_fs(h_sb, wait); -+ if (unlikely(e && !err)) -+ err = e; -+ /* go on even if an error happens */ -+ } -+ } -+ si_read_unlock(sb); -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* final actions when unmounting a file system */ -+static void aufs_put_super(struct super_block *sb) -+{ -+ struct au_sbinfo *sbinfo; -+ -+ sbinfo = au_sbi(sb); -+ if (!sbinfo) -+ return; -+ -+ dbgaufs_si_fin(sbinfo); -+ kobject_put(&sbinfo->si_kobj); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void *au_array_alloc(unsigned long long *hint, au_arraycb_t cb, void *arg) -+{ -+ void *array; -+ unsigned long long n, sz; -+ -+ array = NULL; -+ n = 0; -+ if (!*hint) -+ goto out; -+ -+ if (*hint > ULLONG_MAX / sizeof(array)) { -+ array = ERR_PTR(-EMFILE); -+ pr_err("hint %llu\n", *hint); -+ goto out; -+ } -+ -+ sz = sizeof(array) * *hint; -+ array = kzalloc(sz, GFP_NOFS); -+ if (unlikely(!array)) -+ array = vzalloc(sz); -+ if (unlikely(!array)) { -+ array = ERR_PTR(-ENOMEM); -+ goto out; -+ } -+ -+ n = cb(array, *hint, arg); -+ AuDebugOn(n > *hint); -+ -+out: -+ *hint = n; -+ return array; -+} -+ -+static unsigned long long au_iarray_cb(void *a, -+ unsigned long long max __maybe_unused, -+ void *arg) -+{ -+ unsigned long long n; -+ struct inode **p, *inode; -+ struct list_head *head; -+ -+ n = 0; -+ p = a; -+ head = arg; -+ spin_lock(&inode_sb_list_lock); -+ list_for_each_entry(inode, head, i_sb_list) { -+ if (!is_bad_inode(inode) -+ && au_ii(inode)->ii_bstart >= 0) { -+ spin_lock(&inode->i_lock); -+ if (atomic_read(&inode->i_count)) { -+ au_igrab(inode); -+ *p++ = inode; -+ n++; -+ AuDebugOn(n > max); -+ } -+ spin_unlock(&inode->i_lock); -+ } -+ } -+ spin_unlock(&inode_sb_list_lock); -+ -+ return n; -+} -+ -+struct inode **au_iarray_alloc(struct super_block *sb, unsigned long long *max) -+{ -+ *max = atomic_long_read(&au_sbi(sb)->si_ninodes); -+ return au_array_alloc(max, au_iarray_cb, &sb->s_inodes); -+} -+ -+void au_iarray_free(struct inode **a, unsigned long long max) -+{ -+ unsigned long long ull; -+ -+ for (ull = 0; ull < max; ull++) -+ iput(a[ull]); -+ kvfree(a); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * refresh dentry and inode at remount time. -+ */ -+/* todo: consolidate with simple_reval_dpath() and au_reval_for_attr() */ -+static int au_do_refresh(struct dentry *dentry, unsigned int dir_flags, -+ struct dentry *parent) -+{ -+ int err; -+ -+ di_write_lock_child(dentry); -+ di_read_lock_parent(parent, AuLock_IR); -+ err = au_refresh_dentry(dentry, parent); -+ if (!err && dir_flags) -+ au_hn_reset(dentry->d_inode, dir_flags); -+ di_read_unlock(parent, AuLock_IR); -+ di_write_unlock(dentry); -+ -+ return err; -+} -+ -+static int au_do_refresh_d(struct dentry *dentry, unsigned int sigen, -+ struct au_sbinfo *sbinfo, -+ const unsigned int dir_flags, unsigned int do_idop) -+{ -+ int err; -+ struct dentry *parent; -+ struct inode *inode; -+ -+ err = 0; -+ parent = dget_parent(dentry); -+ if (!au_digen_test(parent, sigen) && au_digen_test(dentry, sigen)) { -+ inode = dentry->d_inode; -+ if (inode) { -+ if (!S_ISDIR(inode->i_mode)) -+ err = au_do_refresh(dentry, /*dir_flags*/0, -+ parent); -+ else { -+ err = au_do_refresh(dentry, dir_flags, parent); -+ if (unlikely(err)) -+ au_fset_si(sbinfo, FAILED_REFRESH_DIR); -+ } -+ } else -+ err = au_do_refresh(dentry, /*dir_flags*/0, parent); -+ AuDbgDentry(dentry); -+ } -+ dput(parent); -+ -+ if (!err) { -+ if (do_idop) -+ au_refresh_dop(dentry, /*force_reval*/0); -+ } else -+ au_refresh_dop(dentry, /*force_reval*/1); -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+static int au_refresh_d(struct super_block *sb, unsigned int do_idop) -+{ -+ int err, i, j, ndentry, e; -+ unsigned int sigen; -+ struct au_dcsub_pages dpages; -+ struct au_dpage *dpage; -+ struct dentry **dentries, *d; -+ struct au_sbinfo *sbinfo; -+ struct dentry *root = sb->s_root; -+ const unsigned int dir_flags = au_hi_flags(root->d_inode, /*isdir*/1); -+ -+ if (do_idop) -+ au_refresh_dop(root, /*force_reval*/0); -+ -+ err = au_dpages_init(&dpages, GFP_NOFS); -+ if (unlikely(err)) -+ goto out; -+ err = au_dcsub_pages(&dpages, root, NULL, NULL); -+ if (unlikely(err)) -+ goto out_dpages; -+ -+ sigen = au_sigen(sb); -+ sbinfo = au_sbi(sb); -+ for (i = 0; i < dpages.ndpage; i++) { -+ dpage = dpages.dpages + i; -+ dentries = dpage->dentries; -+ ndentry = dpage->ndentry; -+ for (j = 0; j < ndentry; j++) { -+ d = dentries[j]; -+ e = au_do_refresh_d(d, sigen, sbinfo, dir_flags, -+ do_idop); -+ if (unlikely(e && !err)) -+ err = e; -+ /* go on even err */ -+ } -+ } -+ -+out_dpages: -+ au_dpages_free(&dpages); -+out: -+ return err; -+} -+ -+static int au_refresh_i(struct super_block *sb, unsigned int do_idop) -+{ -+ int err, e; -+ unsigned int sigen; -+ unsigned long long max, ull; -+ struct inode *inode, **array; -+ -+ array = au_iarray_alloc(sb, &max); -+ err = PTR_ERR(array); -+ if (IS_ERR(array)) -+ goto out; -+ -+ err = 0; -+ sigen = au_sigen(sb); -+ for (ull = 0; ull < max; ull++) { -+ inode = array[ull]; -+ if (unlikely(!inode)) -+ break; -+ -+ e = 0; -+ ii_write_lock_child(inode); -+ if (au_iigen(inode, NULL) != sigen) { -+ e = au_refresh_hinode_self(inode); -+ if (unlikely(e)) { -+ au_refresh_iop(inode, /*force_getattr*/1); -+ pr_err("error %d, i%lu\n", e, inode->i_ino); -+ if (!err) -+ err = e; -+ /* go on even if err */ -+ } -+ } -+ if (!e && do_idop) -+ au_refresh_iop(inode, /*force_getattr*/0); -+ ii_write_unlock(inode); -+ } -+ -+ au_iarray_free(array, max); -+ -+out: -+ return err; -+} -+ -+static void au_remount_refresh(struct super_block *sb, unsigned int do_idop) -+{ -+ int err, e; -+ unsigned int udba; -+ aufs_bindex_t bindex, bend; -+ struct dentry *root; -+ struct inode *inode; -+ struct au_branch *br; -+ struct au_sbinfo *sbi; -+ -+ au_sigen_inc(sb); -+ sbi = au_sbi(sb); -+ au_fclr_si(sbi, FAILED_REFRESH_DIR); -+ -+ root = sb->s_root; -+ DiMustNoWaiters(root); -+ inode = root->d_inode; -+ IiMustNoWaiters(inode); -+ -+ udba = au_opt_udba(sb); -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ err = au_hnotify_reset_br(udba, br, br->br_perm); -+ if (unlikely(err)) -+ AuIOErr("hnotify failed on br %d, %d, ignored\n", -+ bindex, err); -+ /* go on even if err */ -+ } -+ au_hn_reset(inode, au_hi_flags(inode, /*isdir*/1)); -+ -+ if (do_idop) { -+ if (au_ftest_si(sbi, NO_DREVAL)) { -+ AuDebugOn(sb->s_d_op == &aufs_dop_noreval); -+ sb->s_d_op = &aufs_dop_noreval; -+ AuDebugOn(sbi->si_iop_array == aufs_iop_nogetattr); -+ sbi->si_iop_array = aufs_iop_nogetattr; -+ } else { -+ AuDebugOn(sb->s_d_op == &aufs_dop); -+ sb->s_d_op = &aufs_dop; -+ AuDebugOn(sbi->si_iop_array == aufs_iop); -+ sbi->si_iop_array = aufs_iop; -+ } -+ pr_info("reset to %pf and %pf\n", -+ sb->s_d_op, sbi->si_iop_array); -+ } -+ -+ di_write_unlock(root); -+ err = au_refresh_d(sb, do_idop); -+ e = au_refresh_i(sb, do_idop); -+ if (unlikely(e && !err)) -+ err = e; -+ /* aufs_write_lock() calls ..._child() */ -+ di_write_lock_child(root); -+ -+ au_cpup_attr_all(inode, /*force*/1); -+ -+ if (unlikely(err)) -+ AuIOErr("refresh failed, ignored, %d\n", err); -+} -+ -+/* stop extra interpretation of errno in mount(8), and strange error messages */ -+static int cvt_err(int err) -+{ -+ AuTraceErr(err); -+ -+ switch (err) { -+ case -ENOENT: -+ case -ENOTDIR: -+ case -EEXIST: -+ case -EIO: -+ err = -EINVAL; -+ } -+ return err; -+} -+ -+static int aufs_remount_fs(struct super_block *sb, int *flags, char *data) -+{ -+ int err, do_dx; -+ unsigned int mntflags; -+ struct au_opts opts = { -+ .opt = NULL -+ }; -+ struct dentry *root; -+ struct inode *inode; -+ struct au_sbinfo *sbinfo; -+ -+ err = 0; -+ root = sb->s_root; -+ if (!data || !*data) { -+ err = si_write_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ if (!err) { -+ di_write_lock_child(root); -+ err = au_opts_verify(sb, *flags, /*pending*/0); -+ aufs_write_unlock(root); -+ } -+ goto out; -+ } -+ -+ err = -ENOMEM; -+ opts.opt = (void *)__get_free_page(GFP_NOFS); -+ if (unlikely(!opts.opt)) -+ goto out; -+ opts.max_opt = PAGE_SIZE / sizeof(*opts.opt); -+ opts.flags = AuOpts_REMOUNT; -+ opts.sb_flags = *flags; -+ -+ /* parse it before aufs lock */ -+ err = au_opts_parse(sb, data, &opts); -+ if (unlikely(err)) -+ goto out_opts; -+ -+ sbinfo = au_sbi(sb); -+ inode = root->d_inode; -+ mutex_lock(&inode->i_mutex); -+ err = si_write_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ if (unlikely(err)) -+ goto out_mtx; -+ di_write_lock_child(root); -+ -+ /* au_opts_remount() may return an error */ -+ err = au_opts_remount(sb, &opts); -+ au_opts_free(&opts); -+ -+ if (au_ftest_opts(opts.flags, REFRESH)) -+ au_remount_refresh(sb, au_ftest_opts(opts.flags, REFRESH_IDOP)); -+ -+ if (au_ftest_opts(opts.flags, REFRESH_DYAOP)) { -+ mntflags = au_mntflags(sb); -+ do_dx = !!au_opt_test(mntflags, DIO); -+ au_dy_arefresh(do_dx); -+ } -+ -+ au_fhsm_wrote_all(sb, /*force*/1); /* ?? */ -+ aufs_write_unlock(root); -+ -+out_mtx: -+ mutex_unlock(&inode->i_mutex); -+out_opts: -+ free_page((unsigned long)opts.opt); -+out: -+ err = cvt_err(err); -+ AuTraceErr(err); -+ return err; -+} -+ -+static const struct super_operations aufs_sop = { -+ .alloc_inode = aufs_alloc_inode, -+ .destroy_inode = aufs_destroy_inode, -+ /* always deleting, no clearing */ -+ .drop_inode = generic_delete_inode, -+ .show_options = aufs_show_options, -+ .statfs = aufs_statfs, -+ .put_super = aufs_put_super, -+ .sync_fs = aufs_sync_fs, -+ .remount_fs = aufs_remount_fs -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int alloc_root(struct super_block *sb) -+{ -+ int err; -+ struct inode *inode; -+ struct dentry *root; -+ -+ err = -ENOMEM; -+ inode = au_iget_locked(sb, AUFS_ROOT_INO); -+ err = PTR_ERR(inode); -+ if (IS_ERR(inode)) -+ goto out; -+ -+ inode->i_op = aufs_iop + AuIop_DIR; /* with getattr by default */ -+ inode->i_fop = &aufs_dir_fop; -+ inode->i_mode = S_IFDIR; -+ set_nlink(inode, 2); -+ unlock_new_inode(inode); -+ -+ root = d_make_root(inode); -+ if (unlikely(!root)) -+ goto out; -+ err = PTR_ERR(root); -+ if (IS_ERR(root)) -+ goto out; -+ -+ err = au_di_init(root); -+ if (!err) { -+ sb->s_root = root; -+ return 0; /* success */ -+ } -+ dput(root); -+ -+out: -+ return err; -+} -+ -+static int aufs_fill_super(struct super_block *sb, void *raw_data, -+ int silent __maybe_unused) -+{ -+ int err; -+ struct au_opts opts = { -+ .opt = NULL -+ }; -+ struct au_sbinfo *sbinfo; -+ struct dentry *root; -+ struct inode *inode; -+ char *arg = raw_data; -+ -+ if (unlikely(!arg || !*arg)) { -+ err = -EINVAL; -+ pr_err("no arg\n"); -+ goto out; -+ } -+ -+ err = -ENOMEM; -+ opts.opt = (void *)__get_free_page(GFP_NOFS); -+ if (unlikely(!opts.opt)) -+ goto out; -+ opts.max_opt = PAGE_SIZE / sizeof(*opts.opt); -+ opts.sb_flags = sb->s_flags; -+ -+ err = au_si_alloc(sb); -+ if (unlikely(err)) -+ goto out_opts; -+ sbinfo = au_sbi(sb); -+ -+ /* all timestamps always follow the ones on the branch */ -+ sb->s_flags |= MS_NOATIME | MS_NODIRATIME; -+ sb->s_op = &aufs_sop; -+ sb->s_d_op = &aufs_dop; -+ sb->s_magic = AUFS_SUPER_MAGIC; -+ sb->s_maxbytes = 0; -+ sb->s_stack_depth = 1; -+ au_export_init(sb); -+ /* au_xattr_init(sb); */ -+ -+ err = alloc_root(sb); -+ if (unlikely(err)) { -+ si_write_unlock(sb); -+ goto out_info; -+ } -+ root = sb->s_root; -+ inode = root->d_inode; -+ -+ /* -+ * actually we can parse options regardless aufs lock here. -+ * but at remount time, parsing must be done before aufs lock. -+ * so we follow the same rule. -+ */ -+ ii_write_lock_parent(inode); -+ aufs_write_unlock(root); -+ err = au_opts_parse(sb, arg, &opts); -+ if (unlikely(err)) -+ goto out_root; -+ -+ /* lock vfs_inode first, then aufs. */ -+ mutex_lock(&inode->i_mutex); -+ aufs_write_lock(root); -+ err = au_opts_mount(sb, &opts); -+ au_opts_free(&opts); -+ if (!err && au_ftest_si(sbinfo, NO_DREVAL)) { -+ sb->s_d_op = &aufs_dop_noreval; -+ pr_info("%pf\n", sb->s_d_op); -+ au_refresh_dop(root, /*force_reval*/0); -+ sbinfo->si_iop_array = aufs_iop_nogetattr; -+ au_refresh_iop(inode, /*force_getattr*/0); -+ } -+ aufs_write_unlock(root); -+ mutex_unlock(&inode->i_mutex); -+ if (!err) -+ goto out_opts; /* success */ -+ -+out_root: -+ dput(root); -+ sb->s_root = NULL; -+out_info: -+ dbgaufs_si_fin(sbinfo); -+ kobject_put(&sbinfo->si_kobj); -+ sb->s_fs_info = NULL; -+out_opts: -+ free_page((unsigned long)opts.opt); -+out: -+ AuTraceErr(err); -+ err = cvt_err(err); -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static struct dentry *aufs_mount(struct file_system_type *fs_type, int flags, -+ const char *dev_name __maybe_unused, -+ void *raw_data) -+{ -+ struct dentry *root; -+ struct super_block *sb; -+ -+ /* all timestamps always follow the ones on the branch */ -+ /* mnt->mnt_flags |= MNT_NOATIME | MNT_NODIRATIME; */ -+ root = mount_nodev(fs_type, flags, raw_data, aufs_fill_super); -+ if (IS_ERR(root)) -+ goto out; -+ -+ sb = root->d_sb; -+ si_write_lock(sb, !AuLock_FLUSH); -+ sysaufs_brs_add(sb, 0); -+ si_write_unlock(sb); -+ au_sbilist_add(sb); -+ -+out: -+ return root; -+} -+ -+static void aufs_kill_sb(struct super_block *sb) -+{ -+ struct au_sbinfo *sbinfo; -+ -+ sbinfo = au_sbi(sb); -+ if (sbinfo) { -+ au_sbilist_del(sb); -+ aufs_write_lock(sb->s_root); -+ au_fhsm_fin(sb); -+ if (sbinfo->si_wbr_create_ops->fin) -+ sbinfo->si_wbr_create_ops->fin(sb); -+ if (au_opt_test(sbinfo->si_mntflags, UDBA_HNOTIFY)) { -+ au_opt_set_udba(sbinfo->si_mntflags, UDBA_NONE); -+ au_remount_refresh(sb, /*do_idop*/0); -+ } -+ if (au_opt_test(sbinfo->si_mntflags, PLINK)) -+ au_plink_put(sb, /*verbose*/1); -+ au_xino_clr(sb); -+ sbinfo->si_sb = NULL; -+ aufs_write_unlock(sb->s_root); -+ au_nwt_flush(&sbinfo->si_nowait); -+ } -+ kill_anon_super(sb); -+} -+ -+struct file_system_type aufs_fs_type = { -+ .name = AUFS_FSTYPE, -+ /* a race between rename and others */ -+ .fs_flags = FS_RENAME_DOES_D_MOVE, -+ .mount = aufs_mount, -+ .kill_sb = aufs_kill_sb, -+ /* no need to __module_get() and module_put(). */ -+ .owner = THIS_MODULE, -+}; -diff --git a/fs/aufs/super.h b/fs/aufs/super.h -new file mode 100644 -index 0000000..ecd364b ---- /dev/null -+++ b/fs/aufs/super.h -@@ -0,0 +1,626 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * super_block operations -+ */ -+ -+#ifndef __AUFS_SUPER_H__ -+#define __AUFS_SUPER_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+#include "rwsem.h" -+#include "spl.h" -+#include "wkq.h" -+ -+typedef ssize_t (*au_readf_t)(struct file *, char __user *, size_t, loff_t *); -+typedef ssize_t (*au_writef_t)(struct file *, const char __user *, size_t, -+ loff_t *); -+ -+/* policies to select one among multiple writable branches */ -+struct au_wbr_copyup_operations { -+ int (*copyup)(struct dentry *dentry); -+}; -+ -+#define AuWbr_DIR 1 /* target is a dir */ -+#define AuWbr_PARENT (1 << 1) /* always require a parent */ -+ -+#define au_ftest_wbr(flags, name) ((flags) & AuWbr_##name) -+#define au_fset_wbr(flags, name) { (flags) |= AuWbr_##name; } -+#define au_fclr_wbr(flags, name) { (flags) &= ~AuWbr_##name; } -+ -+struct au_wbr_create_operations { -+ int (*create)(struct dentry *dentry, unsigned int flags); -+ int (*init)(struct super_block *sb); -+ int (*fin)(struct super_block *sb); -+}; -+ -+struct au_wbr_mfs { -+ struct mutex mfs_lock; /* protect this structure */ -+ unsigned long mfs_jiffy; -+ unsigned long mfs_expire; -+ aufs_bindex_t mfs_bindex; -+ -+ unsigned long long mfsrr_bytes; -+ unsigned long long mfsrr_watermark; -+}; -+ -+#define AuPlink_NHASH 100 -+static inline int au_plink_hash(ino_t ino) -+{ -+ return ino % AuPlink_NHASH; -+} -+ -+/* File-based Hierarchical Storage Management */ -+struct au_fhsm { -+#ifdef CONFIG_AUFS_FHSM -+ /* allow only one process who can receive the notification */ -+ spinlock_t fhsm_spin; -+ pid_t fhsm_pid; -+ wait_queue_head_t fhsm_wqh; -+ atomic_t fhsm_readable; -+ -+ /* these are protected by si_rwsem */ -+ unsigned long fhsm_expire; -+ aufs_bindex_t fhsm_bottom; -+#endif -+}; -+ -+#define AU_PIDSTEP (int)(BITS_TO_LONGS(PID_MAX_DEFAULT) * BITS_PER_LONG) -+#define AU_NPIDMAP (int)DIV_ROUND_UP(PID_MAX_LIMIT, AU_PIDSTEP) -+struct au_si_pid { -+ unsigned long *pid_bitmap[AU_NPIDMAP]; -+ struct mutex pid_mtx; -+}; -+ -+struct au_branch; -+struct au_sbinfo { -+ /* nowait tasks in the system-wide workqueue */ -+ struct au_nowait_tasks si_nowait; -+ -+ /* -+ * tried sb->s_umount, but failed due to the dependecy between i_mutex. -+ * rwsem for au_sbinfo is necessary. -+ */ -+ struct au_rwsem si_rwsem; -+ -+ /* prevent recursive locking in deleting inode */ -+ struct au_si_pid au_si_pid; -+ -+ /* -+ * dirty approach to protect sb->sb_inodes and ->s_files (gone) from -+ * remount. -+ */ -+ atomic_long_t si_ninodes, si_nfiles; -+ -+ /* branch management */ -+ unsigned int si_generation; -+ -+ /* see AuSi_ flags */ -+ unsigned char au_si_status; -+ -+ aufs_bindex_t si_bend; -+ -+ /* dirty trick to keep br_id plus */ -+ unsigned int si_last_br_id : -+ sizeof(aufs_bindex_t) * BITS_PER_BYTE - 1; -+ struct au_branch **si_branch; -+ -+ /* policy to select a writable branch */ -+ unsigned char si_wbr_copyup; -+ unsigned char si_wbr_create; -+ struct au_wbr_copyup_operations *si_wbr_copyup_ops; -+ struct au_wbr_create_operations *si_wbr_create_ops; -+ -+ /* round robin */ -+ atomic_t si_wbr_rr_next; -+ -+ /* most free space */ -+ struct au_wbr_mfs si_wbr_mfs; -+ -+ /* File-based Hierarchical Storage Management */ -+ struct au_fhsm si_fhsm; -+ -+ /* mount flags */ -+ /* include/asm-ia64/siginfo.h defines a macro named si_flags */ -+ unsigned int si_mntflags; -+ -+ /* external inode number (bitmap and translation table) */ -+ au_readf_t si_xread; -+ au_writef_t si_xwrite; -+ struct file *si_xib; -+ struct mutex si_xib_mtx; /* protect xib members */ -+ unsigned long *si_xib_buf; -+ unsigned long si_xib_last_pindex; -+ int si_xib_next_bit; -+ aufs_bindex_t si_xino_brid; -+ unsigned long si_xino_jiffy; -+ unsigned long si_xino_expire; -+ /* reserved for future use */ -+ /* unsigned long long si_xib_limit; */ /* Max xib file size */ -+ -+#ifdef CONFIG_AUFS_EXPORT -+ /* i_generation */ -+ struct file *si_xigen; -+ atomic_t si_xigen_next; -+#endif -+ -+ /* dirty trick to suppoer atomic_open */ -+ struct au_sphlhead si_aopen; -+ -+ /* vdir parameters */ -+ unsigned long si_rdcache; /* max cache time in jiffies */ -+ unsigned int si_rdblk; /* deblk size */ -+ unsigned int si_rdhash; /* hash size */ -+ -+ /* -+ * If the number of whiteouts are larger than si_dirwh, leave all of -+ * them after au_whtmp_ren to reduce the cost of rmdir(2). -+ * future fsck.aufs or kernel thread will remove them later. -+ * Otherwise, remove all whiteouts and the dir in rmdir(2). -+ */ -+ unsigned int si_dirwh; -+ -+ /* pseudo_link list */ -+ struct au_sphlhead si_plink[AuPlink_NHASH]; -+ wait_queue_head_t si_plink_wq; -+ spinlock_t si_plink_maint_lock; -+ pid_t si_plink_maint_pid; -+ -+ /* file list */ -+ struct au_sphlhead si_files; -+ -+ /* with/without getattr, brother of sb->s_d_op */ -+ struct inode_operations *si_iop_array; -+ -+ /* -+ * sysfs and lifetime management. -+ * this is not a small structure and it may be a waste of memory in case -+ * of sysfs is disabled, particulary when many aufs-es are mounted. -+ * but using sysfs is majority. -+ */ -+ struct kobject si_kobj; -+#ifdef CONFIG_DEBUG_FS -+ struct dentry *si_dbgaufs; -+ struct dentry *si_dbgaufs_plink; -+ struct dentry *si_dbgaufs_xib; -+#ifdef CONFIG_AUFS_EXPORT -+ struct dentry *si_dbgaufs_xigen; -+#endif -+#endif -+ -+#ifdef CONFIG_AUFS_SBILIST -+ struct hlist_node si_list; -+#endif -+ -+ /* dirty, necessary for unmounting, sysfs and sysrq */ -+ struct super_block *si_sb; -+}; -+ -+/* sbinfo status flags */ -+/* -+ * set true when refresh_dirs() failed at remount time. -+ * then try refreshing dirs at access time again. -+ * if it is false, refreshing dirs at access time is unnecesary -+ */ -+#define AuSi_FAILED_REFRESH_DIR 1 -+#define AuSi_FHSM (1 << 1) /* fhsm is active now */ -+#define AuSi_NO_DREVAL (1 << 2) /* disable all d_revalidate */ -+ -+#ifndef CONFIG_AUFS_FHSM -+#undef AuSi_FHSM -+#define AuSi_FHSM 0 -+#endif -+ -+static inline unsigned char au_do_ftest_si(struct au_sbinfo *sbi, -+ unsigned int flag) -+{ -+ AuRwMustAnyLock(&sbi->si_rwsem); -+ return sbi->au_si_status & flag; -+} -+#define au_ftest_si(sbinfo, name) au_do_ftest_si(sbinfo, AuSi_##name) -+#define au_fset_si(sbinfo, name) do { \ -+ AuRwMustWriteLock(&(sbinfo)->si_rwsem); \ -+ (sbinfo)->au_si_status |= AuSi_##name; \ -+} while (0) -+#define au_fclr_si(sbinfo, name) do { \ -+ AuRwMustWriteLock(&(sbinfo)->si_rwsem); \ -+ (sbinfo)->au_si_status &= ~AuSi_##name; \ -+} while (0) -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* policy to select one among writable branches */ -+#define AuWbrCopyup(sbinfo, ...) \ -+ ((sbinfo)->si_wbr_copyup_ops->copyup(__VA_ARGS__)) -+#define AuWbrCreate(sbinfo, ...) \ -+ ((sbinfo)->si_wbr_create_ops->create(__VA_ARGS__)) -+ -+/* flags for si_read_lock()/aufs_read_lock()/di_read_lock() */ -+#define AuLock_DW 1 /* write-lock dentry */ -+#define AuLock_IR (1 << 1) /* read-lock inode */ -+#define AuLock_IW (1 << 2) /* write-lock inode */ -+#define AuLock_FLUSH (1 << 3) /* wait for 'nowait' tasks */ -+#define AuLock_DIRS (1 << 4) /* target is a pair of dirs */ -+#define AuLock_NOPLM (1 << 5) /* return err in plm mode */ -+#define AuLock_NOPLMW (1 << 6) /* wait for plm mode ends */ -+#define AuLock_GEN (1 << 7) /* test digen/iigen */ -+#define au_ftest_lock(flags, name) ((flags) & AuLock_##name) -+#define au_fset_lock(flags, name) \ -+ do { (flags) |= AuLock_##name; } while (0) -+#define au_fclr_lock(flags, name) \ -+ do { (flags) &= ~AuLock_##name; } while (0) -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* super.c */ -+extern struct file_system_type aufs_fs_type; -+struct inode *au_iget_locked(struct super_block *sb, ino_t ino); -+typedef unsigned long long (*au_arraycb_t)(void *array, unsigned long long max, -+ void *arg); -+void *au_array_alloc(unsigned long long *hint, au_arraycb_t cb, void *arg); -+struct inode **au_iarray_alloc(struct super_block *sb, unsigned long long *max); -+void au_iarray_free(struct inode **a, unsigned long long max); -+ -+/* sbinfo.c */ -+void au_si_free(struct kobject *kobj); -+int au_si_alloc(struct super_block *sb); -+int au_sbr_realloc(struct au_sbinfo *sbinfo, int nbr); -+ -+unsigned int au_sigen_inc(struct super_block *sb); -+aufs_bindex_t au_new_br_id(struct super_block *sb); -+ -+int si_read_lock(struct super_block *sb, int flags); -+int si_write_lock(struct super_block *sb, int flags); -+int aufs_read_lock(struct dentry *dentry, int flags); -+void aufs_read_unlock(struct dentry *dentry, int flags); -+void aufs_write_lock(struct dentry *dentry); -+void aufs_write_unlock(struct dentry *dentry); -+int aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int flags); -+void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2); -+ -+/* wbr_policy.c */ -+extern struct au_wbr_copyup_operations au_wbr_copyup_ops[]; -+extern struct au_wbr_create_operations au_wbr_create_ops[]; -+int au_cpdown_dirs(struct dentry *dentry, aufs_bindex_t bdst); -+int au_wbr_nonopq(struct dentry *dentry, aufs_bindex_t bindex); -+int au_wbr_do_copyup_bu(struct dentry *dentry, aufs_bindex_t bstart); -+ -+/* mvdown.c */ -+int au_mvdown(struct dentry *dentry, struct aufs_mvdown __user *arg); -+ -+#ifdef CONFIG_AUFS_FHSM -+/* fhsm.c */ -+ -+static inline pid_t au_fhsm_pid(struct au_fhsm *fhsm) -+{ -+ pid_t pid; -+ -+ spin_lock(&fhsm->fhsm_spin); -+ pid = fhsm->fhsm_pid; -+ spin_unlock(&fhsm->fhsm_spin); -+ -+ return pid; -+} -+ -+void au_fhsm_wrote(struct super_block *sb, aufs_bindex_t bindex, int force); -+void au_fhsm_wrote_all(struct super_block *sb, int force); -+int au_fhsm_fd(struct super_block *sb, int oflags); -+int au_fhsm_br_alloc(struct au_branch *br); -+void au_fhsm_set_bottom(struct super_block *sb, aufs_bindex_t bindex); -+void au_fhsm_fin(struct super_block *sb); -+void au_fhsm_init(struct au_sbinfo *sbinfo); -+void au_fhsm_set(struct au_sbinfo *sbinfo, unsigned int sec); -+void au_fhsm_show(struct seq_file *seq, struct au_sbinfo *sbinfo); -+#else -+AuStubVoid(au_fhsm_wrote, struct super_block *sb, aufs_bindex_t bindex, -+ int force) -+AuStubVoid(au_fhsm_wrote_all, struct super_block *sb, int force) -+AuStub(int, au_fhsm_fd, return -EOPNOTSUPP, struct super_block *sb, int oflags) -+AuStub(pid_t, au_fhsm_pid, return 0, struct au_fhsm *fhsm) -+AuStubInt0(au_fhsm_br_alloc, struct au_branch *br) -+AuStubVoid(au_fhsm_set_bottom, struct super_block *sb, aufs_bindex_t bindex) -+AuStubVoid(au_fhsm_fin, struct super_block *sb) -+AuStubVoid(au_fhsm_init, struct au_sbinfo *sbinfo) -+AuStubVoid(au_fhsm_set, struct au_sbinfo *sbinfo, unsigned int sec) -+AuStubVoid(au_fhsm_show, struct seq_file *seq, struct au_sbinfo *sbinfo) -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline struct au_sbinfo *au_sbi(struct super_block *sb) -+{ -+ return sb->s_fs_info; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+#ifdef CONFIG_AUFS_EXPORT -+int au_test_nfsd(void); -+void au_export_init(struct super_block *sb); -+void au_xigen_inc(struct inode *inode); -+int au_xigen_new(struct inode *inode); -+int au_xigen_set(struct super_block *sb, struct file *base); -+void au_xigen_clr(struct super_block *sb); -+ -+static inline int au_busy_or_stale(void) -+{ -+ if (!au_test_nfsd()) -+ return -EBUSY; -+ return -ESTALE; -+} -+#else -+AuStubInt0(au_test_nfsd, void) -+AuStubVoid(au_export_init, struct super_block *sb) -+AuStubVoid(au_xigen_inc, struct inode *inode) -+AuStubInt0(au_xigen_new, struct inode *inode) -+AuStubInt0(au_xigen_set, struct super_block *sb, struct file *base) -+AuStubVoid(au_xigen_clr, struct super_block *sb) -+AuStub(int, au_busy_or_stale, return -EBUSY, void) -+#endif /* CONFIG_AUFS_EXPORT */ -+ -+/* ---------------------------------------------------------------------- */ -+ -+#ifdef CONFIG_AUFS_SBILIST -+/* module.c */ -+extern struct au_sphlhead au_sbilist; -+ -+static inline void au_sbilist_init(void) -+{ -+ au_sphl_init(&au_sbilist); -+} -+ -+static inline void au_sbilist_add(struct super_block *sb) -+{ -+ au_sphl_add(&au_sbi(sb)->si_list, &au_sbilist); -+} -+ -+static inline void au_sbilist_del(struct super_block *sb) -+{ -+ au_sphl_del(&au_sbi(sb)->si_list, &au_sbilist); -+} -+ -+#ifdef CONFIG_AUFS_MAGIC_SYSRQ -+static inline void au_sbilist_lock(void) -+{ -+ spin_lock(&au_sbilist.spin); -+} -+ -+static inline void au_sbilist_unlock(void) -+{ -+ spin_unlock(&au_sbilist.spin); -+} -+#define AuGFP_SBILIST GFP_ATOMIC -+#else -+AuStubVoid(au_sbilist_lock, void) -+AuStubVoid(au_sbilist_unlock, void) -+#define AuGFP_SBILIST GFP_NOFS -+#endif /* CONFIG_AUFS_MAGIC_SYSRQ */ -+#else -+AuStubVoid(au_sbilist_init, void) -+AuStubVoid(au_sbilist_add, struct super_block *sb) -+AuStubVoid(au_sbilist_del, struct super_block *sb) -+AuStubVoid(au_sbilist_lock, void) -+AuStubVoid(au_sbilist_unlock, void) -+#define AuGFP_SBILIST GFP_NOFS -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline void dbgaufs_si_null(struct au_sbinfo *sbinfo) -+{ -+ /* -+ * This function is a dynamic '__init' function actually, -+ * so the tiny check for si_rwsem is unnecessary. -+ */ -+ /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ -+#ifdef CONFIG_DEBUG_FS -+ sbinfo->si_dbgaufs = NULL; -+ sbinfo->si_dbgaufs_plink = NULL; -+ sbinfo->si_dbgaufs_xib = NULL; -+#ifdef CONFIG_AUFS_EXPORT -+ sbinfo->si_dbgaufs_xigen = NULL; -+#endif -+#endif -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline void si_pid_idx_bit(int *idx, pid_t *bit) -+{ -+ /* the origin of pid is 1, but the bitmap's is 0 */ -+ *bit = current->pid - 1; -+ *idx = *bit / AU_PIDSTEP; -+ *bit %= AU_PIDSTEP; -+} -+ -+static inline int si_pid_test(struct super_block *sb) -+{ -+ pid_t bit; -+ int idx; -+ unsigned long *bitmap; -+ -+ si_pid_idx_bit(&idx, &bit); -+ bitmap = au_sbi(sb)->au_si_pid.pid_bitmap[idx]; -+ if (bitmap) -+ return test_bit(bit, bitmap); -+ return 0; -+} -+ -+static inline void si_pid_clr(struct super_block *sb) -+{ -+ pid_t bit; -+ int idx; -+ unsigned long *bitmap; -+ -+ si_pid_idx_bit(&idx, &bit); -+ bitmap = au_sbi(sb)->au_si_pid.pid_bitmap[idx]; -+ BUG_ON(!bitmap); -+ AuDebugOn(!test_bit(bit, bitmap)); -+ clear_bit(bit, bitmap); -+ /* smp_mb(); */ -+} -+ -+void si_pid_set(struct super_block *sb); -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* lock superblock. mainly for entry point functions */ -+/* -+ * __si_read_lock, __si_write_lock, -+ * __si_read_unlock, __si_write_unlock, __si_downgrade_lock -+ */ -+AuSimpleRwsemFuncs(__si, struct super_block *sb, &au_sbi(sb)->si_rwsem); -+ -+#define SiMustNoWaiters(sb) AuRwMustNoWaiters(&au_sbi(sb)->si_rwsem) -+#define SiMustAnyLock(sb) AuRwMustAnyLock(&au_sbi(sb)->si_rwsem) -+#define SiMustWriteLock(sb) AuRwMustWriteLock(&au_sbi(sb)->si_rwsem) -+ -+static inline void si_noflush_read_lock(struct super_block *sb) -+{ -+ __si_read_lock(sb); -+ si_pid_set(sb); -+} -+ -+static inline int si_noflush_read_trylock(struct super_block *sb) -+{ -+ int locked; -+ -+ locked = __si_read_trylock(sb); -+ if (locked) -+ si_pid_set(sb); -+ return locked; -+} -+ -+static inline void si_noflush_write_lock(struct super_block *sb) -+{ -+ __si_write_lock(sb); -+ si_pid_set(sb); -+} -+ -+static inline int si_noflush_write_trylock(struct super_block *sb) -+{ -+ int locked; -+ -+ locked = __si_write_trylock(sb); -+ if (locked) -+ si_pid_set(sb); -+ return locked; -+} -+ -+#if 0 /* reserved */ -+static inline int si_read_trylock(struct super_block *sb, int flags) -+{ -+ if (au_ftest_lock(flags, FLUSH)) -+ au_nwt_flush(&au_sbi(sb)->si_nowait); -+ return si_noflush_read_trylock(sb); -+} -+#endif -+ -+static inline void si_read_unlock(struct super_block *sb) -+{ -+ si_pid_clr(sb); -+ __si_read_unlock(sb); -+} -+ -+#if 0 /* reserved */ -+static inline int si_write_trylock(struct super_block *sb, int flags) -+{ -+ if (au_ftest_lock(flags, FLUSH)) -+ au_nwt_flush(&au_sbi(sb)->si_nowait); -+ return si_noflush_write_trylock(sb); -+} -+#endif -+ -+static inline void si_write_unlock(struct super_block *sb) -+{ -+ si_pid_clr(sb); -+ __si_write_unlock(sb); -+} -+ -+#if 0 /* reserved */ -+static inline void si_downgrade_lock(struct super_block *sb) -+{ -+ __si_downgrade_lock(sb); -+} -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline aufs_bindex_t au_sbend(struct super_block *sb) -+{ -+ SiMustAnyLock(sb); -+ return au_sbi(sb)->si_bend; -+} -+ -+static inline unsigned int au_mntflags(struct super_block *sb) -+{ -+ SiMustAnyLock(sb); -+ return au_sbi(sb)->si_mntflags; -+} -+ -+static inline unsigned int au_sigen(struct super_block *sb) -+{ -+ SiMustAnyLock(sb); -+ return au_sbi(sb)->si_generation; -+} -+ -+static inline void au_ninodes_inc(struct super_block *sb) -+{ -+ atomic_long_inc(&au_sbi(sb)->si_ninodes); -+} -+ -+static inline void au_ninodes_dec(struct super_block *sb) -+{ -+ AuDebugOn(!atomic_long_read(&au_sbi(sb)->si_ninodes)); -+ atomic_long_dec(&au_sbi(sb)->si_ninodes); -+} -+ -+static inline void au_nfiles_inc(struct super_block *sb) -+{ -+ atomic_long_inc(&au_sbi(sb)->si_nfiles); -+} -+ -+static inline void au_nfiles_dec(struct super_block *sb) -+{ -+ AuDebugOn(!atomic_long_read(&au_sbi(sb)->si_nfiles)); -+ atomic_long_dec(&au_sbi(sb)->si_nfiles); -+} -+ -+static inline struct au_branch *au_sbr(struct super_block *sb, -+ aufs_bindex_t bindex) -+{ -+ SiMustAnyLock(sb); -+ return au_sbi(sb)->si_branch[0 + bindex]; -+} -+ -+static inline void au_xino_brid_set(struct super_block *sb, aufs_bindex_t brid) -+{ -+ SiMustWriteLock(sb); -+ au_sbi(sb)->si_xino_brid = brid; -+} -+ -+static inline aufs_bindex_t au_xino_brid(struct super_block *sb) -+{ -+ SiMustAnyLock(sb); -+ return au_sbi(sb)->si_xino_brid; -+} -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_SUPER_H__ */ -diff --git a/fs/aufs/sysaufs.c b/fs/aufs/sysaufs.c -new file mode 100644 -index 0000000..75c9c24 ---- /dev/null -+++ b/fs/aufs/sysaufs.c -@@ -0,0 +1,104 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * sysfs interface and lifetime management -+ * they are necessary regardless sysfs is disabled. -+ */ -+ -+#include -+#include "aufs.h" -+ -+unsigned long sysaufs_si_mask; -+struct kset *sysaufs_kset; -+ -+#define AuSiAttr(_name) { \ -+ .attr = { .name = __stringify(_name), .mode = 0444 }, \ -+ .show = sysaufs_si_##_name, \ -+} -+ -+static struct sysaufs_si_attr sysaufs_si_attr_xi_path = AuSiAttr(xi_path); -+struct attribute *sysaufs_si_attrs[] = { -+ &sysaufs_si_attr_xi_path.attr, -+ NULL, -+}; -+ -+static const struct sysfs_ops au_sbi_ops = { -+ .show = sysaufs_si_show -+}; -+ -+static struct kobj_type au_sbi_ktype = { -+ .release = au_si_free, -+ .sysfs_ops = &au_sbi_ops, -+ .default_attrs = sysaufs_si_attrs -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+int sysaufs_si_init(struct au_sbinfo *sbinfo) -+{ -+ int err; -+ -+ sbinfo->si_kobj.kset = sysaufs_kset; -+ /* cf. sysaufs_name() */ -+ err = kobject_init_and_add -+ (&sbinfo->si_kobj, &au_sbi_ktype, /*&sysaufs_kset->kobj*/NULL, -+ SysaufsSiNamePrefix "%lx", sysaufs_si_id(sbinfo)); -+ -+ dbgaufs_si_null(sbinfo); -+ if (!err) { -+ err = dbgaufs_si_init(sbinfo); -+ if (unlikely(err)) -+ kobject_put(&sbinfo->si_kobj); -+ } -+ return err; -+} -+ -+void sysaufs_fin(void) -+{ -+ dbgaufs_fin(); -+ sysfs_remove_group(&sysaufs_kset->kobj, sysaufs_attr_group); -+ kset_unregister(sysaufs_kset); -+} -+ -+int __init sysaufs_init(void) -+{ -+ int err; -+ -+ do { -+ get_random_bytes(&sysaufs_si_mask, sizeof(sysaufs_si_mask)); -+ } while (!sysaufs_si_mask); -+ -+ err = -EINVAL; -+ sysaufs_kset = kset_create_and_add(AUFS_NAME, NULL, fs_kobj); -+ if (unlikely(!sysaufs_kset)) -+ goto out; -+ err = PTR_ERR(sysaufs_kset); -+ if (IS_ERR(sysaufs_kset)) -+ goto out; -+ err = sysfs_create_group(&sysaufs_kset->kobj, sysaufs_attr_group); -+ if (unlikely(err)) { -+ kset_unregister(sysaufs_kset); -+ goto out; -+ } -+ -+ err = dbgaufs_init(); -+ if (unlikely(err)) -+ sysaufs_fin(); -+out: -+ return err; -+} -diff --git a/fs/aufs/sysaufs.h b/fs/aufs/sysaufs.h -new file mode 100644 -index 0000000..14975c9 ---- /dev/null -+++ b/fs/aufs/sysaufs.h -@@ -0,0 +1,101 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * sysfs interface and mount lifetime management -+ */ -+ -+#ifndef __SYSAUFS_H__ -+#define __SYSAUFS_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+#include "module.h" -+ -+struct super_block; -+struct au_sbinfo; -+ -+struct sysaufs_si_attr { -+ struct attribute attr; -+ int (*show)(struct seq_file *seq, struct super_block *sb); -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* sysaufs.c */ -+extern unsigned long sysaufs_si_mask; -+extern struct kset *sysaufs_kset; -+extern struct attribute *sysaufs_si_attrs[]; -+int sysaufs_si_init(struct au_sbinfo *sbinfo); -+int __init sysaufs_init(void); -+void sysaufs_fin(void); -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* some people doesn't like to show a pointer in kernel */ -+static inline unsigned long sysaufs_si_id(struct au_sbinfo *sbinfo) -+{ -+ return sysaufs_si_mask ^ (unsigned long)sbinfo; -+} -+ -+#define SysaufsSiNamePrefix "si_" -+#define SysaufsSiNameLen (sizeof(SysaufsSiNamePrefix) + 16) -+static inline void sysaufs_name(struct au_sbinfo *sbinfo, char *name) -+{ -+ snprintf(name, SysaufsSiNameLen, SysaufsSiNamePrefix "%lx", -+ sysaufs_si_id(sbinfo)); -+} -+ -+struct au_branch; -+#ifdef CONFIG_SYSFS -+/* sysfs.c */ -+extern struct attribute_group *sysaufs_attr_group; -+ -+int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb); -+ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr, -+ char *buf); -+long au_brinfo_ioctl(struct file *file, unsigned long arg); -+#ifdef CONFIG_COMPAT -+long au_brinfo_compat_ioctl(struct file *file, unsigned long arg); -+#endif -+ -+void sysaufs_br_init(struct au_branch *br); -+void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex); -+void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex); -+ -+#define sysaufs_brs_init() do {} while (0) -+ -+#else -+#define sysaufs_attr_group NULL -+ -+AuStubInt0(sysaufs_si_xi_path, struct seq_file *seq, struct super_block *sb) -+AuStub(ssize_t, sysaufs_si_show, return 0, struct kobject *kobj, -+ struct attribute *attr, char *buf) -+AuStubVoid(sysaufs_br_init, struct au_branch *br) -+AuStubVoid(sysaufs_brs_add, struct super_block *sb, aufs_bindex_t bindex) -+AuStubVoid(sysaufs_brs_del, struct super_block *sb, aufs_bindex_t bindex) -+ -+static inline void sysaufs_brs_init(void) -+{ -+ sysaufs_brs = 0; -+} -+ -+#endif /* CONFIG_SYSFS */ -+ -+#endif /* __KERNEL__ */ -+#endif /* __SYSAUFS_H__ */ -diff --git a/fs/aufs/sysfs.c b/fs/aufs/sysfs.c -new file mode 100644 -index 0000000..b2d1888 ---- /dev/null -+++ b/fs/aufs/sysfs.c -@@ -0,0 +1,376 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * sysfs interface -+ */ -+ -+#include -+#include -+#include "aufs.h" -+ -+#ifdef CONFIG_AUFS_FS_MODULE -+/* this entry violates the "one line per file" policy of sysfs */ -+static ssize_t config_show(struct kobject *kobj, struct kobj_attribute *attr, -+ char *buf) -+{ -+ ssize_t err; -+ static char *conf = -+/* this file is generated at compiling */ -+#include "conf.str" -+ ; -+ -+ err = snprintf(buf, PAGE_SIZE, conf); -+ if (unlikely(err >= PAGE_SIZE)) -+ err = -EFBIG; -+ return err; -+} -+ -+static struct kobj_attribute au_config_attr = __ATTR_RO(config); -+#endif -+ -+static struct attribute *au_attr[] = { -+#ifdef CONFIG_AUFS_FS_MODULE -+ &au_config_attr.attr, -+#endif -+ NULL, /* need to NULL terminate the list of attributes */ -+}; -+ -+static struct attribute_group sysaufs_attr_group_body = { -+ .attrs = au_attr -+}; -+ -+struct attribute_group *sysaufs_attr_group = &sysaufs_attr_group_body; -+ -+/* ---------------------------------------------------------------------- */ -+ -+int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb) -+{ -+ int err; -+ -+ SiMustAnyLock(sb); -+ -+ err = 0; -+ if (au_opt_test(au_mntflags(sb), XINO)) { -+ err = au_xino_path(seq, au_sbi(sb)->si_xib); -+ seq_putc(seq, '\n'); -+ } -+ return err; -+} -+ -+/* -+ * the lifetime of branch is independent from the entry under sysfs. -+ * sysfs handles the lifetime of the entry, and never call ->show() after it is -+ * unlinked. -+ */ -+static int sysaufs_si_br(struct seq_file *seq, struct super_block *sb, -+ aufs_bindex_t bindex, int idx) -+{ -+ int err; -+ struct path path; -+ struct dentry *root; -+ struct au_branch *br; -+ au_br_perm_str_t perm; -+ -+ AuDbg("b%d\n", bindex); -+ -+ err = 0; -+ root = sb->s_root; -+ di_read_lock_parent(root, !AuLock_IR); -+ br = au_sbr(sb, bindex); -+ -+ switch (idx) { -+ case AuBrSysfs_BR: -+ path.mnt = au_br_mnt(br); -+ path.dentry = au_h_dptr(root, bindex); -+ err = au_seq_path(seq, &path); -+ if (!err) { -+ au_optstr_br_perm(&perm, br->br_perm); -+ err = seq_printf(seq, "=%s\n", perm.a); -+ } -+ break; -+ case AuBrSysfs_BRID: -+ err = seq_printf(seq, "%d\n", br->br_id); -+ break; -+ } -+ di_read_unlock(root, !AuLock_IR); -+ if (err == -1) -+ err = -E2BIG; -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static struct seq_file *au_seq(char *p, ssize_t len) -+{ -+ struct seq_file *seq; -+ -+ seq = kzalloc(sizeof(*seq), GFP_NOFS); -+ if (seq) { -+ /* mutex_init(&seq.lock); */ -+ seq->buf = p; -+ seq->size = len; -+ return seq; /* success */ -+ } -+ -+ seq = ERR_PTR(-ENOMEM); -+ return seq; -+} -+ -+#define SysaufsBr_PREFIX "br" -+#define SysaufsBrid_PREFIX "brid" -+ -+/* todo: file size may exceed PAGE_SIZE */ -+ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr, -+ char *buf) -+{ -+ ssize_t err; -+ int idx; -+ long l; -+ aufs_bindex_t bend; -+ struct au_sbinfo *sbinfo; -+ struct super_block *sb; -+ struct seq_file *seq; -+ char *name; -+ struct attribute **cattr; -+ -+ sbinfo = container_of(kobj, struct au_sbinfo, si_kobj); -+ sb = sbinfo->si_sb; -+ -+ /* -+ * prevent a race condition between sysfs and aufs. -+ * for instance, sysfs_file_read() calls sysfs_get_active_two() which -+ * prohibits maintaining the sysfs entries. -+ * hew we acquire read lock after sysfs_get_active_two(). -+ * on the other hand, the remount process may maintain the sysfs/aufs -+ * entries after acquiring write lock. -+ * it can cause a deadlock. -+ * simply we gave up processing read here. -+ */ -+ err = -EBUSY; -+ if (unlikely(!si_noflush_read_trylock(sb))) -+ goto out; -+ -+ seq = au_seq(buf, PAGE_SIZE); -+ err = PTR_ERR(seq); -+ if (IS_ERR(seq)) -+ goto out_unlock; -+ -+ name = (void *)attr->name; -+ cattr = sysaufs_si_attrs; -+ while (*cattr) { -+ if (!strcmp(name, (*cattr)->name)) { -+ err = container_of(*cattr, struct sysaufs_si_attr, attr) -+ ->show(seq, sb); -+ goto out_seq; -+ } -+ cattr++; -+ } -+ -+ if (!strncmp(name, SysaufsBrid_PREFIX, -+ sizeof(SysaufsBrid_PREFIX) - 1)) { -+ idx = AuBrSysfs_BRID; -+ name += sizeof(SysaufsBrid_PREFIX) - 1; -+ } else if (!strncmp(name, SysaufsBr_PREFIX, -+ sizeof(SysaufsBr_PREFIX) - 1)) { -+ idx = AuBrSysfs_BR; -+ name += sizeof(SysaufsBr_PREFIX) - 1; -+ } else -+ BUG(); -+ -+ err = kstrtol(name, 10, &l); -+ if (!err) { -+ bend = au_sbend(sb); -+ if (l <= bend) -+ err = sysaufs_si_br(seq, sb, (aufs_bindex_t)l, idx); -+ else -+ err = -ENOENT; -+ } -+ -+out_seq: -+ if (!err) { -+ err = seq->count; -+ /* sysfs limit */ -+ if (unlikely(err == PAGE_SIZE)) -+ err = -EFBIG; -+ } -+ kfree(seq); -+out_unlock: -+ si_read_unlock(sb); -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_brinfo(struct super_block *sb, union aufs_brinfo __user *arg) -+{ -+ int err; -+ int16_t brid; -+ aufs_bindex_t bindex, bend; -+ size_t sz; -+ char *buf; -+ struct seq_file *seq; -+ struct au_branch *br; -+ -+ si_read_lock(sb, AuLock_FLUSH); -+ bend = au_sbend(sb); -+ err = bend + 1; -+ if (!arg) -+ goto out; -+ -+ err = -ENOMEM; -+ buf = (void *)__get_free_page(GFP_NOFS); -+ if (unlikely(!buf)) -+ goto out; -+ -+ seq = au_seq(buf, PAGE_SIZE); -+ err = PTR_ERR(seq); -+ if (IS_ERR(seq)) -+ goto out_buf; -+ -+ sz = sizeof(*arg) - offsetof(union aufs_brinfo, path); -+ for (bindex = 0; bindex <= bend; bindex++, arg++) { -+ err = !access_ok(VERIFY_WRITE, arg, sizeof(*arg)); -+ if (unlikely(err)) -+ break; -+ -+ br = au_sbr(sb, bindex); -+ brid = br->br_id; -+ BUILD_BUG_ON(sizeof(brid) != sizeof(arg->id)); -+ err = __put_user(brid, &arg->id); -+ if (unlikely(err)) -+ break; -+ -+ BUILD_BUG_ON(sizeof(br->br_perm) != sizeof(arg->perm)); -+ err = __put_user(br->br_perm, &arg->perm); -+ if (unlikely(err)) -+ break; -+ -+ err = au_seq_path(seq, &br->br_path); -+ if (unlikely(err)) -+ break; -+ err = seq_putc(seq, '\0'); -+ if (!err && seq->count <= sz) { -+ err = copy_to_user(arg->path, seq->buf, seq->count); -+ seq->count = 0; -+ if (unlikely(err)) -+ break; -+ } else { -+ err = -E2BIG; -+ goto out_seq; -+ } -+ } -+ if (unlikely(err)) -+ err = -EFAULT; -+ -+out_seq: -+ kfree(seq); -+out_buf: -+ free_page((unsigned long)buf); -+out: -+ si_read_unlock(sb); -+ return err; -+} -+ -+long au_brinfo_ioctl(struct file *file, unsigned long arg) -+{ -+ return au_brinfo(file->f_dentry->d_sb, (void __user *)arg); -+} -+ -+#ifdef CONFIG_COMPAT -+long au_brinfo_compat_ioctl(struct file *file, unsigned long arg) -+{ -+ return au_brinfo(file->f_dentry->d_sb, compat_ptr(arg)); -+} -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+void sysaufs_br_init(struct au_branch *br) -+{ -+ int i; -+ struct au_brsysfs *br_sysfs; -+ struct attribute *attr; -+ -+ br_sysfs = br->br_sysfs; -+ for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) { -+ attr = &br_sysfs->attr; -+ sysfs_attr_init(attr); -+ attr->name = br_sysfs->name; -+ attr->mode = S_IRUGO; -+ br_sysfs++; -+ } -+} -+ -+void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ struct au_branch *br; -+ struct kobject *kobj; -+ struct au_brsysfs *br_sysfs; -+ int i; -+ aufs_bindex_t bend; -+ -+ dbgaufs_brs_del(sb, bindex); -+ -+ if (!sysaufs_brs) -+ return; -+ -+ kobj = &au_sbi(sb)->si_kobj; -+ bend = au_sbend(sb); -+ for (; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ br_sysfs = br->br_sysfs; -+ for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) { -+ sysfs_remove_file(kobj, &br_sysfs->attr); -+ br_sysfs++; -+ } -+ } -+} -+ -+void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ int err, i; -+ aufs_bindex_t bend; -+ struct kobject *kobj; -+ struct au_branch *br; -+ struct au_brsysfs *br_sysfs; -+ -+ dbgaufs_brs_add(sb, bindex); -+ -+ if (!sysaufs_brs) -+ return; -+ -+ kobj = &au_sbi(sb)->si_kobj; -+ bend = au_sbend(sb); -+ for (; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ br_sysfs = br->br_sysfs; -+ snprintf(br_sysfs[AuBrSysfs_BR].name, sizeof(br_sysfs->name), -+ SysaufsBr_PREFIX "%d", bindex); -+ snprintf(br_sysfs[AuBrSysfs_BRID].name, sizeof(br_sysfs->name), -+ SysaufsBrid_PREFIX "%d", bindex); -+ for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) { -+ err = sysfs_create_file(kobj, &br_sysfs->attr); -+ if (unlikely(err)) -+ pr_warn("failed %s under sysfs(%d)\n", -+ br_sysfs->name, err); -+ br_sysfs++; -+ } -+ } -+} -diff --git a/fs/aufs/sysrq.c b/fs/aufs/sysrq.c -new file mode 100644 -index 0000000..057c23e ---- /dev/null -+++ b/fs/aufs/sysrq.c -@@ -0,0 +1,157 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * magic sysrq hanlder -+ */ -+ -+/* #include */ -+#include -+#include "aufs.h" -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void sysrq_sb(struct super_block *sb) -+{ -+ char *plevel; -+ struct au_sbinfo *sbinfo; -+ struct file *file; -+ struct au_sphlhead *files; -+ struct au_finfo *finfo; -+ -+ plevel = au_plevel; -+ au_plevel = KERN_WARNING; -+ -+ /* since we define pr_fmt, call printk directly */ -+#define pr(str) printk(KERN_WARNING AUFS_NAME ": " str) -+ -+ sbinfo = au_sbi(sb); -+ printk(KERN_WARNING "si=%lx\n", sysaufs_si_id(sbinfo)); -+ pr("superblock\n"); -+ au_dpri_sb(sb); -+ -+#if 0 -+ pr("root dentry\n"); -+ au_dpri_dentry(sb->s_root); -+ pr("root inode\n"); -+ au_dpri_inode(sb->s_root->d_inode); -+#endif -+ -+#if 0 -+ do { -+ int err, i, j, ndentry; -+ struct au_dcsub_pages dpages; -+ struct au_dpage *dpage; -+ -+ err = au_dpages_init(&dpages, GFP_ATOMIC); -+ if (unlikely(err)) -+ break; -+ err = au_dcsub_pages(&dpages, sb->s_root, NULL, NULL); -+ if (!err) -+ for (i = 0; i < dpages.ndpage; i++) { -+ dpage = dpages.dpages + i; -+ ndentry = dpage->ndentry; -+ for (j = 0; j < ndentry; j++) -+ au_dpri_dentry(dpage->dentries[j]); -+ } -+ au_dpages_free(&dpages); -+ } while (0); -+#endif -+ -+#if 1 -+ { -+ struct inode *i; -+ -+ pr("isolated inode\n"); -+ spin_lock(&inode_sb_list_lock); -+ list_for_each_entry(i, &sb->s_inodes, i_sb_list) { -+ spin_lock(&i->i_lock); -+ if (1 || hlist_empty(&i->i_dentry)) -+ au_dpri_inode(i); -+ spin_unlock(&i->i_lock); -+ } -+ spin_unlock(&inode_sb_list_lock); -+ } -+#endif -+ pr("files\n"); -+ files = &au_sbi(sb)->si_files; -+ spin_lock(&files->spin); -+ hlist_for_each_entry(finfo, &files->head, fi_hlist) { -+ umode_t mode; -+ -+ file = finfo->fi_file; -+ mode = file_inode(file)->i_mode; -+ if (!special_file(mode)) -+ au_dpri_file(file); -+ } -+ spin_unlock(&files->spin); -+ pr("done\n"); -+ -+#undef pr -+ au_plevel = plevel; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* module parameter */ -+static char *aufs_sysrq_key = "a"; -+module_param_named(sysrq, aufs_sysrq_key, charp, S_IRUGO); -+MODULE_PARM_DESC(sysrq, "MagicSysRq key for " AUFS_NAME); -+ -+static void au_sysrq(int key __maybe_unused) -+{ -+ struct au_sbinfo *sbinfo; -+ -+ lockdep_off(); -+ au_sbilist_lock(); -+ hlist_for_each_entry(sbinfo, &au_sbilist.head, si_list) -+ sysrq_sb(sbinfo->si_sb); -+ au_sbilist_unlock(); -+ lockdep_on(); -+} -+ -+static struct sysrq_key_op au_sysrq_op = { -+ .handler = au_sysrq, -+ .help_msg = "Aufs", -+ .action_msg = "Aufs", -+ .enable_mask = SYSRQ_ENABLE_DUMP -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+int __init au_sysrq_init(void) -+{ -+ int err; -+ char key; -+ -+ err = -1; -+ key = *aufs_sysrq_key; -+ if ('a' <= key && key <= 'z') -+ err = register_sysrq_key(key, &au_sysrq_op); -+ if (unlikely(err)) -+ pr_err("err %d, sysrq=%c\n", err, key); -+ return err; -+} -+ -+void au_sysrq_fin(void) -+{ -+ int err; -+ -+ err = unregister_sysrq_key(*aufs_sysrq_key, &au_sysrq_op); -+ if (unlikely(err)) -+ pr_err("err %d (ignored)\n", err); -+} -diff --git a/fs/aufs/vdir.c b/fs/aufs/vdir.c -new file mode 100644 -index 0000000..f942d16 ---- /dev/null -+++ b/fs/aufs/vdir.c -@@ -0,0 +1,888 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * virtual or vertical directory -+ */ -+ -+#include "aufs.h" -+ -+static unsigned int calc_size(int nlen) -+{ -+ return ALIGN(sizeof(struct au_vdir_de) + nlen, sizeof(ino_t)); -+} -+ -+static int set_deblk_end(union au_vdir_deblk_p *p, -+ union au_vdir_deblk_p *deblk_end) -+{ -+ if (calc_size(0) <= deblk_end->deblk - p->deblk) { -+ p->de->de_str.len = 0; -+ /* smp_mb(); */ -+ return 0; -+ } -+ return -1; /* error */ -+} -+ -+/* returns true or false */ -+static int is_deblk_end(union au_vdir_deblk_p *p, -+ union au_vdir_deblk_p *deblk_end) -+{ -+ if (calc_size(0) <= deblk_end->deblk - p->deblk) -+ return !p->de->de_str.len; -+ return 1; -+} -+ -+static unsigned char *last_deblk(struct au_vdir *vdir) -+{ -+ return vdir->vd_deblk[vdir->vd_nblk - 1]; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* estimate the appropriate size for name hash table */ -+unsigned int au_rdhash_est(loff_t sz) -+{ -+ unsigned int n; -+ -+ n = UINT_MAX; -+ sz >>= 10; -+ if (sz < n) -+ n = sz; -+ if (sz < AUFS_RDHASH_DEF) -+ n = AUFS_RDHASH_DEF; -+ /* pr_info("n %u\n", n); */ -+ return n; -+} -+ -+/* -+ * the allocated memory has to be freed by -+ * au_nhash_wh_free() or au_nhash_de_free(). -+ */ -+int au_nhash_alloc(struct au_nhash *nhash, unsigned int num_hash, gfp_t gfp) -+{ -+ struct hlist_head *head; -+ unsigned int u; -+ size_t sz; -+ -+ sz = sizeof(*nhash->nh_head) * num_hash; -+ head = kmalloc(sz, gfp); -+ if (head) { -+ nhash->nh_num = num_hash; -+ nhash->nh_head = head; -+ for (u = 0; u < num_hash; u++) -+ INIT_HLIST_HEAD(head++); -+ return 0; /* success */ -+ } -+ -+ return -ENOMEM; -+} -+ -+static void nhash_count(struct hlist_head *head) -+{ -+#if 0 -+ unsigned long n; -+ struct hlist_node *pos; -+ -+ n = 0; -+ hlist_for_each(pos, head) -+ n++; -+ pr_info("%lu\n", n); -+#endif -+} -+ -+static void au_nhash_wh_do_free(struct hlist_head *head) -+{ -+ struct au_vdir_wh *pos; -+ struct hlist_node *node; -+ -+ hlist_for_each_entry_safe(pos, node, head, wh_hash) -+ kfree(pos); -+} -+ -+static void au_nhash_de_do_free(struct hlist_head *head) -+{ -+ struct au_vdir_dehstr *pos; -+ struct hlist_node *node; -+ -+ hlist_for_each_entry_safe(pos, node, head, hash) -+ au_cache_free_vdir_dehstr(pos); -+} -+ -+static void au_nhash_do_free(struct au_nhash *nhash, -+ void (*free)(struct hlist_head *head)) -+{ -+ unsigned int n; -+ struct hlist_head *head; -+ -+ n = nhash->nh_num; -+ if (!n) -+ return; -+ -+ head = nhash->nh_head; -+ while (n-- > 0) { -+ nhash_count(head); -+ free(head++); -+ } -+ kfree(nhash->nh_head); -+} -+ -+void au_nhash_wh_free(struct au_nhash *whlist) -+{ -+ au_nhash_do_free(whlist, au_nhash_wh_do_free); -+} -+ -+static void au_nhash_de_free(struct au_nhash *delist) -+{ -+ au_nhash_do_free(delist, au_nhash_de_do_free); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int au_nhash_test_longer_wh(struct au_nhash *whlist, aufs_bindex_t btgt, -+ int limit) -+{ -+ int num; -+ unsigned int u, n; -+ struct hlist_head *head; -+ struct au_vdir_wh *pos; -+ -+ num = 0; -+ n = whlist->nh_num; -+ head = whlist->nh_head; -+ for (u = 0; u < n; u++, head++) -+ hlist_for_each_entry(pos, head, wh_hash) -+ if (pos->wh_bindex == btgt && ++num > limit) -+ return 1; -+ return 0; -+} -+ -+static struct hlist_head *au_name_hash(struct au_nhash *nhash, -+ unsigned char *name, -+ unsigned int len) -+{ -+ unsigned int v; -+ /* const unsigned int magic_bit = 12; */ -+ -+ AuDebugOn(!nhash->nh_num || !nhash->nh_head); -+ -+ v = 0; -+ while (len--) -+ v += *name++; -+ /* v = hash_long(v, magic_bit); */ -+ v %= nhash->nh_num; -+ return nhash->nh_head + v; -+} -+ -+static int au_nhash_test_name(struct au_vdir_destr *str, const char *name, -+ int nlen) -+{ -+ return str->len == nlen && !memcmp(str->name, name, nlen); -+} -+ -+/* returns found or not */ -+int au_nhash_test_known_wh(struct au_nhash *whlist, char *name, int nlen) -+{ -+ struct hlist_head *head; -+ struct au_vdir_wh *pos; -+ struct au_vdir_destr *str; -+ -+ head = au_name_hash(whlist, name, nlen); -+ hlist_for_each_entry(pos, head, wh_hash) { -+ str = &pos->wh_str; -+ AuDbg("%.*s\n", str->len, str->name); -+ if (au_nhash_test_name(str, name, nlen)) -+ return 1; -+ } -+ return 0; -+} -+ -+/* returns found(true) or not */ -+static int test_known(struct au_nhash *delist, char *name, int nlen) -+{ -+ struct hlist_head *head; -+ struct au_vdir_dehstr *pos; -+ struct au_vdir_destr *str; -+ -+ head = au_name_hash(delist, name, nlen); -+ hlist_for_each_entry(pos, head, hash) { -+ str = pos->str; -+ AuDbg("%.*s\n", str->len, str->name); -+ if (au_nhash_test_name(str, name, nlen)) -+ return 1; -+ } -+ return 0; -+} -+ -+static void au_shwh_init_wh(struct au_vdir_wh *wh, ino_t ino, -+ unsigned char d_type) -+{ -+#ifdef CONFIG_AUFS_SHWH -+ wh->wh_ino = ino; -+ wh->wh_type = d_type; -+#endif -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int au_nhash_append_wh(struct au_nhash *whlist, char *name, int nlen, ino_t ino, -+ unsigned int d_type, aufs_bindex_t bindex, -+ unsigned char shwh) -+{ -+ int err; -+ struct au_vdir_destr *str; -+ struct au_vdir_wh *wh; -+ -+ AuDbg("%.*s\n", nlen, name); -+ AuDebugOn(!whlist->nh_num || !whlist->nh_head); -+ -+ err = -ENOMEM; -+ wh = kmalloc(sizeof(*wh) + nlen, GFP_NOFS); -+ if (unlikely(!wh)) -+ goto out; -+ -+ err = 0; -+ wh->wh_bindex = bindex; -+ if (shwh) -+ au_shwh_init_wh(wh, ino, d_type); -+ str = &wh->wh_str; -+ str->len = nlen; -+ memcpy(str->name, name, nlen); -+ hlist_add_head(&wh->wh_hash, au_name_hash(whlist, name, nlen)); -+ /* smp_mb(); */ -+ -+out: -+ return err; -+} -+ -+static int append_deblk(struct au_vdir *vdir) -+{ -+ int err; -+ unsigned long ul; -+ const unsigned int deblk_sz = vdir->vd_deblk_sz; -+ union au_vdir_deblk_p p, deblk_end; -+ unsigned char **o; -+ -+ err = -ENOMEM; -+ o = krealloc(vdir->vd_deblk, sizeof(*o) * (vdir->vd_nblk + 1), -+ GFP_NOFS); -+ if (unlikely(!o)) -+ goto out; -+ -+ vdir->vd_deblk = o; -+ p.deblk = kmalloc(deblk_sz, GFP_NOFS); -+ if (p.deblk) { -+ ul = vdir->vd_nblk++; -+ vdir->vd_deblk[ul] = p.deblk; -+ vdir->vd_last.ul = ul; -+ vdir->vd_last.p.deblk = p.deblk; -+ deblk_end.deblk = p.deblk + deblk_sz; -+ err = set_deblk_end(&p, &deblk_end); -+ } -+ -+out: -+ return err; -+} -+ -+static int append_de(struct au_vdir *vdir, char *name, int nlen, ino_t ino, -+ unsigned int d_type, struct au_nhash *delist) -+{ -+ int err; -+ unsigned int sz; -+ const unsigned int deblk_sz = vdir->vd_deblk_sz; -+ union au_vdir_deblk_p p, *room, deblk_end; -+ struct au_vdir_dehstr *dehstr; -+ -+ p.deblk = last_deblk(vdir); -+ deblk_end.deblk = p.deblk + deblk_sz; -+ room = &vdir->vd_last.p; -+ AuDebugOn(room->deblk < p.deblk || deblk_end.deblk <= room->deblk -+ || !is_deblk_end(room, &deblk_end)); -+ -+ sz = calc_size(nlen); -+ if (unlikely(sz > deblk_end.deblk - room->deblk)) { -+ err = append_deblk(vdir); -+ if (unlikely(err)) -+ goto out; -+ -+ p.deblk = last_deblk(vdir); -+ deblk_end.deblk = p.deblk + deblk_sz; -+ /* smp_mb(); */ -+ AuDebugOn(room->deblk != p.deblk); -+ } -+ -+ err = -ENOMEM; -+ dehstr = au_cache_alloc_vdir_dehstr(); -+ if (unlikely(!dehstr)) -+ goto out; -+ -+ dehstr->str = &room->de->de_str; -+ hlist_add_head(&dehstr->hash, au_name_hash(delist, name, nlen)); -+ room->de->de_ino = ino; -+ room->de->de_type = d_type; -+ room->de->de_str.len = nlen; -+ memcpy(room->de->de_str.name, name, nlen); -+ -+ err = 0; -+ room->deblk += sz; -+ if (unlikely(set_deblk_end(room, &deblk_end))) -+ err = append_deblk(vdir); -+ /* smp_mb(); */ -+ -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void au_vdir_free(struct au_vdir *vdir) -+{ -+ unsigned char **deblk; -+ -+ deblk = vdir->vd_deblk; -+ while (vdir->vd_nblk--) -+ kfree(*deblk++); -+ kfree(vdir->vd_deblk); -+ au_cache_free_vdir(vdir); -+} -+ -+static struct au_vdir *alloc_vdir(struct file *file) -+{ -+ struct au_vdir *vdir; -+ struct super_block *sb; -+ int err; -+ -+ sb = file->f_dentry->d_sb; -+ SiMustAnyLock(sb); -+ -+ err = -ENOMEM; -+ vdir = au_cache_alloc_vdir(); -+ if (unlikely(!vdir)) -+ goto out; -+ -+ vdir->vd_deblk = kzalloc(sizeof(*vdir->vd_deblk), GFP_NOFS); -+ if (unlikely(!vdir->vd_deblk)) -+ goto out_free; -+ -+ vdir->vd_deblk_sz = au_sbi(sb)->si_rdblk; -+ if (!vdir->vd_deblk_sz) { -+ /* estimate the appropriate size for deblk */ -+ vdir->vd_deblk_sz = au_dir_size(file, /*dentry*/NULL); -+ /* pr_info("vd_deblk_sz %u\n", vdir->vd_deblk_sz); */ -+ } -+ vdir->vd_nblk = 0; -+ vdir->vd_version = 0; -+ vdir->vd_jiffy = 0; -+ err = append_deblk(vdir); -+ if (!err) -+ return vdir; /* success */ -+ -+ kfree(vdir->vd_deblk); -+ -+out_free: -+ au_cache_free_vdir(vdir); -+out: -+ vdir = ERR_PTR(err); -+ return vdir; -+} -+ -+static int reinit_vdir(struct au_vdir *vdir) -+{ -+ int err; -+ union au_vdir_deblk_p p, deblk_end; -+ -+ while (vdir->vd_nblk > 1) { -+ kfree(vdir->vd_deblk[vdir->vd_nblk - 1]); -+ /* vdir->vd_deblk[vdir->vd_nblk - 1] = NULL; */ -+ vdir->vd_nblk--; -+ } -+ p.deblk = vdir->vd_deblk[0]; -+ deblk_end.deblk = p.deblk + vdir->vd_deblk_sz; -+ err = set_deblk_end(&p, &deblk_end); -+ /* keep vd_dblk_sz */ -+ vdir->vd_last.ul = 0; -+ vdir->vd_last.p.deblk = vdir->vd_deblk[0]; -+ vdir->vd_version = 0; -+ vdir->vd_jiffy = 0; -+ /* smp_mb(); */ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+#define AuFillVdir_CALLED 1 -+#define AuFillVdir_WHABLE (1 << 1) -+#define AuFillVdir_SHWH (1 << 2) -+#define au_ftest_fillvdir(flags, name) ((flags) & AuFillVdir_##name) -+#define au_fset_fillvdir(flags, name) \ -+ do { (flags) |= AuFillVdir_##name; } while (0) -+#define au_fclr_fillvdir(flags, name) \ -+ do { (flags) &= ~AuFillVdir_##name; } while (0) -+ -+#ifndef CONFIG_AUFS_SHWH -+#undef AuFillVdir_SHWH -+#define AuFillVdir_SHWH 0 -+#endif -+ -+struct fillvdir_arg { -+ struct dir_context ctx; -+ struct file *file; -+ struct au_vdir *vdir; -+ struct au_nhash delist; -+ struct au_nhash whlist; -+ aufs_bindex_t bindex; -+ unsigned int flags; -+ int err; -+}; -+ -+static int fillvdir(struct dir_context *ctx, const char *__name, int nlen, -+ loff_t offset __maybe_unused, u64 h_ino, -+ unsigned int d_type) -+{ -+ struct fillvdir_arg *arg = container_of(ctx, struct fillvdir_arg, ctx); -+ char *name = (void *)__name; -+ struct super_block *sb; -+ ino_t ino; -+ const unsigned char shwh = !!au_ftest_fillvdir(arg->flags, SHWH); -+ -+ arg->err = 0; -+ sb = arg->file->f_dentry->d_sb; -+ au_fset_fillvdir(arg->flags, CALLED); -+ /* smp_mb(); */ -+ if (nlen <= AUFS_WH_PFX_LEN -+ || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { -+ if (test_known(&arg->delist, name, nlen) -+ || au_nhash_test_known_wh(&arg->whlist, name, nlen)) -+ goto out; /* already exists or whiteouted */ -+ -+ arg->err = au_ino(sb, arg->bindex, h_ino, d_type, &ino); -+ if (!arg->err) { -+ if (unlikely(nlen > AUFS_MAX_NAMELEN)) -+ d_type = DT_UNKNOWN; -+ arg->err = append_de(arg->vdir, name, nlen, ino, -+ d_type, &arg->delist); -+ } -+ } else if (au_ftest_fillvdir(arg->flags, WHABLE)) { -+ name += AUFS_WH_PFX_LEN; -+ nlen -= AUFS_WH_PFX_LEN; -+ if (au_nhash_test_known_wh(&arg->whlist, name, nlen)) -+ goto out; /* already whiteouted */ -+ -+ if (shwh) -+ arg->err = au_wh_ino(sb, arg->bindex, h_ino, d_type, -+ &ino); -+ if (!arg->err) { -+ if (nlen <= AUFS_MAX_NAMELEN + AUFS_WH_PFX_LEN) -+ d_type = DT_UNKNOWN; -+ arg->err = au_nhash_append_wh -+ (&arg->whlist, name, nlen, ino, d_type, -+ arg->bindex, shwh); -+ } -+ } -+ -+out: -+ if (!arg->err) -+ arg->vdir->vd_jiffy = jiffies; -+ /* smp_mb(); */ -+ AuTraceErr(arg->err); -+ return arg->err; -+} -+ -+static int au_handle_shwh(struct super_block *sb, struct au_vdir *vdir, -+ struct au_nhash *whlist, struct au_nhash *delist) -+{ -+#ifdef CONFIG_AUFS_SHWH -+ int err; -+ unsigned int nh, u; -+ struct hlist_head *head; -+ struct au_vdir_wh *pos; -+ struct hlist_node *n; -+ char *p, *o; -+ struct au_vdir_destr *destr; -+ -+ AuDebugOn(!au_opt_test(au_mntflags(sb), SHWH)); -+ -+ err = -ENOMEM; -+ o = p = (void *)__get_free_page(GFP_NOFS); -+ if (unlikely(!p)) -+ goto out; -+ -+ err = 0; -+ nh = whlist->nh_num; -+ memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); -+ p += AUFS_WH_PFX_LEN; -+ for (u = 0; u < nh; u++) { -+ head = whlist->nh_head + u; -+ hlist_for_each_entry_safe(pos, n, head, wh_hash) { -+ destr = &pos->wh_str; -+ memcpy(p, destr->name, destr->len); -+ err = append_de(vdir, o, destr->len + AUFS_WH_PFX_LEN, -+ pos->wh_ino, pos->wh_type, delist); -+ if (unlikely(err)) -+ break; -+ } -+ } -+ -+ free_page((unsigned long)o); -+ -+out: -+ AuTraceErr(err); -+ return err; -+#else -+ return 0; -+#endif -+} -+ -+static int au_do_read_vdir(struct fillvdir_arg *arg) -+{ -+ int err; -+ unsigned int rdhash; -+ loff_t offset; -+ aufs_bindex_t bend, bindex, bstart; -+ unsigned char shwh; -+ struct file *hf, *file; -+ struct super_block *sb; -+ -+ file = arg->file; -+ sb = file->f_dentry->d_sb; -+ SiMustAnyLock(sb); -+ -+ rdhash = au_sbi(sb)->si_rdhash; -+ if (!rdhash) -+ rdhash = au_rdhash_est(au_dir_size(file, /*dentry*/NULL)); -+ err = au_nhash_alloc(&arg->delist, rdhash, GFP_NOFS); -+ if (unlikely(err)) -+ goto out; -+ err = au_nhash_alloc(&arg->whlist, rdhash, GFP_NOFS); -+ if (unlikely(err)) -+ goto out_delist; -+ -+ err = 0; -+ arg->flags = 0; -+ shwh = 0; -+ if (au_opt_test(au_mntflags(sb), SHWH)) { -+ shwh = 1; -+ au_fset_fillvdir(arg->flags, SHWH); -+ } -+ bstart = au_fbstart(file); -+ bend = au_fbend_dir(file); -+ for (bindex = bstart; !err && bindex <= bend; bindex++) { -+ hf = au_hf_dir(file, bindex); -+ if (!hf) -+ continue; -+ -+ offset = vfsub_llseek(hf, 0, SEEK_SET); -+ err = offset; -+ if (unlikely(offset)) -+ break; -+ -+ arg->bindex = bindex; -+ au_fclr_fillvdir(arg->flags, WHABLE); -+ if (shwh -+ || (bindex != bend -+ && au_br_whable(au_sbr_perm(sb, bindex)))) -+ au_fset_fillvdir(arg->flags, WHABLE); -+ do { -+ arg->err = 0; -+ au_fclr_fillvdir(arg->flags, CALLED); -+ /* smp_mb(); */ -+ err = vfsub_iterate_dir(hf, &arg->ctx); -+ if (err >= 0) -+ err = arg->err; -+ } while (!err && au_ftest_fillvdir(arg->flags, CALLED)); -+ -+ /* -+ * dir_relax() may be good for concurrency, but aufs should not -+ * use it since it will cause a lockdep problem. -+ */ -+ } -+ -+ if (!err && shwh) -+ err = au_handle_shwh(sb, arg->vdir, &arg->whlist, &arg->delist); -+ -+ au_nhash_wh_free(&arg->whlist); -+ -+out_delist: -+ au_nhash_de_free(&arg->delist); -+out: -+ return err; -+} -+ -+static int read_vdir(struct file *file, int may_read) -+{ -+ int err; -+ unsigned long expire; -+ unsigned char do_read; -+ struct fillvdir_arg arg = { -+ .ctx = { -+ .actor = au_diractor(fillvdir) -+ } -+ }; -+ struct inode *inode; -+ struct au_vdir *vdir, *allocated; -+ -+ err = 0; -+ inode = file_inode(file); -+ IMustLock(inode); -+ SiMustAnyLock(inode->i_sb); -+ -+ allocated = NULL; -+ do_read = 0; -+ expire = au_sbi(inode->i_sb)->si_rdcache; -+ vdir = au_ivdir(inode); -+ if (!vdir) { -+ do_read = 1; -+ vdir = alloc_vdir(file); -+ err = PTR_ERR(vdir); -+ if (IS_ERR(vdir)) -+ goto out; -+ err = 0; -+ allocated = vdir; -+ } else if (may_read -+ && (inode->i_version != vdir->vd_version -+ || time_after(jiffies, vdir->vd_jiffy + expire))) { -+ do_read = 1; -+ err = reinit_vdir(vdir); -+ if (unlikely(err)) -+ goto out; -+ } -+ -+ if (!do_read) -+ return 0; /* success */ -+ -+ arg.file = file; -+ arg.vdir = vdir; -+ err = au_do_read_vdir(&arg); -+ if (!err) { -+ /* file->f_pos = 0; */ /* todo: ctx->pos? */ -+ vdir->vd_version = inode->i_version; -+ vdir->vd_last.ul = 0; -+ vdir->vd_last.p.deblk = vdir->vd_deblk[0]; -+ if (allocated) -+ au_set_ivdir(inode, allocated); -+ } else if (allocated) -+ au_vdir_free(allocated); -+ -+out: -+ return err; -+} -+ -+static int copy_vdir(struct au_vdir *tgt, struct au_vdir *src) -+{ -+ int err, rerr; -+ unsigned long ul, n; -+ const unsigned int deblk_sz = src->vd_deblk_sz; -+ -+ AuDebugOn(tgt->vd_nblk != 1); -+ -+ err = -ENOMEM; -+ if (tgt->vd_nblk < src->vd_nblk) { -+ unsigned char **p; -+ -+ p = krealloc(tgt->vd_deblk, sizeof(*p) * src->vd_nblk, -+ GFP_NOFS); -+ if (unlikely(!p)) -+ goto out; -+ tgt->vd_deblk = p; -+ } -+ -+ if (tgt->vd_deblk_sz != deblk_sz) { -+ unsigned char *p; -+ -+ tgt->vd_deblk_sz = deblk_sz; -+ p = krealloc(tgt->vd_deblk[0], deblk_sz, GFP_NOFS); -+ if (unlikely(!p)) -+ goto out; -+ tgt->vd_deblk[0] = p; -+ } -+ memcpy(tgt->vd_deblk[0], src->vd_deblk[0], deblk_sz); -+ tgt->vd_version = src->vd_version; -+ tgt->vd_jiffy = src->vd_jiffy; -+ -+ n = src->vd_nblk; -+ for (ul = 1; ul < n; ul++) { -+ tgt->vd_deblk[ul] = kmemdup(src->vd_deblk[ul], deblk_sz, -+ GFP_NOFS); -+ if (unlikely(!tgt->vd_deblk[ul])) -+ goto out; -+ tgt->vd_nblk++; -+ } -+ tgt->vd_nblk = n; -+ tgt->vd_last.ul = tgt->vd_last.ul; -+ tgt->vd_last.p.deblk = tgt->vd_deblk[tgt->vd_last.ul]; -+ tgt->vd_last.p.deblk += src->vd_last.p.deblk -+ - src->vd_deblk[src->vd_last.ul]; -+ /* smp_mb(); */ -+ return 0; /* success */ -+ -+out: -+ rerr = reinit_vdir(tgt); -+ BUG_ON(rerr); -+ return err; -+} -+ -+int au_vdir_init(struct file *file) -+{ -+ int err; -+ struct inode *inode; -+ struct au_vdir *vdir_cache, *allocated; -+ -+ /* test file->f_pos here instead of ctx->pos */ -+ err = read_vdir(file, !file->f_pos); -+ if (unlikely(err)) -+ goto out; -+ -+ allocated = NULL; -+ vdir_cache = au_fvdir_cache(file); -+ if (!vdir_cache) { -+ vdir_cache = alloc_vdir(file); -+ err = PTR_ERR(vdir_cache); -+ if (IS_ERR(vdir_cache)) -+ goto out; -+ allocated = vdir_cache; -+ } else if (!file->f_pos && vdir_cache->vd_version != file->f_version) { -+ /* test file->f_pos here instead of ctx->pos */ -+ err = reinit_vdir(vdir_cache); -+ if (unlikely(err)) -+ goto out; -+ } else -+ return 0; /* success */ -+ -+ inode = file_inode(file); -+ err = copy_vdir(vdir_cache, au_ivdir(inode)); -+ if (!err) { -+ file->f_version = inode->i_version; -+ if (allocated) -+ au_set_fvdir_cache(file, allocated); -+ } else if (allocated) -+ au_vdir_free(allocated); -+ -+out: -+ return err; -+} -+ -+static loff_t calc_offset(struct au_vdir *vdir) -+{ -+ loff_t offset; -+ union au_vdir_deblk_p p; -+ -+ p.deblk = vdir->vd_deblk[vdir->vd_last.ul]; -+ offset = vdir->vd_last.p.deblk - p.deblk; -+ offset += vdir->vd_deblk_sz * vdir->vd_last.ul; -+ return offset; -+} -+ -+/* returns true or false */ -+static int seek_vdir(struct file *file, struct dir_context *ctx) -+{ -+ int valid; -+ unsigned int deblk_sz; -+ unsigned long ul, n; -+ loff_t offset; -+ union au_vdir_deblk_p p, deblk_end; -+ struct au_vdir *vdir_cache; -+ -+ valid = 1; -+ vdir_cache = au_fvdir_cache(file); -+ offset = calc_offset(vdir_cache); -+ AuDbg("offset %lld\n", offset); -+ if (ctx->pos == offset) -+ goto out; -+ -+ vdir_cache->vd_last.ul = 0; -+ vdir_cache->vd_last.p.deblk = vdir_cache->vd_deblk[0]; -+ if (!ctx->pos) -+ goto out; -+ -+ valid = 0; -+ deblk_sz = vdir_cache->vd_deblk_sz; -+ ul = div64_u64(ctx->pos, deblk_sz); -+ AuDbg("ul %lu\n", ul); -+ if (ul >= vdir_cache->vd_nblk) -+ goto out; -+ -+ n = vdir_cache->vd_nblk; -+ for (; ul < n; ul++) { -+ p.deblk = vdir_cache->vd_deblk[ul]; -+ deblk_end.deblk = p.deblk + deblk_sz; -+ offset = ul; -+ offset *= deblk_sz; -+ while (!is_deblk_end(&p, &deblk_end) && offset < ctx->pos) { -+ unsigned int l; -+ -+ l = calc_size(p.de->de_str.len); -+ offset += l; -+ p.deblk += l; -+ } -+ if (!is_deblk_end(&p, &deblk_end)) { -+ valid = 1; -+ vdir_cache->vd_last.ul = ul; -+ vdir_cache->vd_last.p = p; -+ break; -+ } -+ } -+ -+out: -+ /* smp_mb(); */ -+ AuTraceErr(!valid); -+ return valid; -+} -+ -+int au_vdir_fill_de(struct file *file, struct dir_context *ctx) -+{ -+ unsigned int l, deblk_sz; -+ union au_vdir_deblk_p deblk_end; -+ struct au_vdir *vdir_cache; -+ struct au_vdir_de *de; -+ -+ vdir_cache = au_fvdir_cache(file); -+ if (!seek_vdir(file, ctx)) -+ return 0; -+ -+ deblk_sz = vdir_cache->vd_deblk_sz; -+ while (1) { -+ deblk_end.deblk = vdir_cache->vd_deblk[vdir_cache->vd_last.ul]; -+ deblk_end.deblk += deblk_sz; -+ while (!is_deblk_end(&vdir_cache->vd_last.p, &deblk_end)) { -+ de = vdir_cache->vd_last.p.de; -+ AuDbg("%.*s, off%lld, i%lu, dt%d\n", -+ de->de_str.len, de->de_str.name, ctx->pos, -+ (unsigned long)de->de_ino, de->de_type); -+ if (unlikely(!dir_emit(ctx, de->de_str.name, -+ de->de_str.len, de->de_ino, -+ de->de_type))) { -+ /* todo: ignore the error caused by udba? */ -+ /* return err; */ -+ return 0; -+ } -+ -+ l = calc_size(de->de_str.len); -+ vdir_cache->vd_last.p.deblk += l; -+ ctx->pos += l; -+ } -+ if (vdir_cache->vd_last.ul < vdir_cache->vd_nblk - 1) { -+ vdir_cache->vd_last.ul++; -+ vdir_cache->vd_last.p.deblk -+ = vdir_cache->vd_deblk[vdir_cache->vd_last.ul]; -+ ctx->pos = deblk_sz * vdir_cache->vd_last.ul; -+ continue; -+ } -+ break; -+ } -+ -+ /* smp_mb(); */ -+ return 0; -+} -diff --git a/fs/aufs/vfsub.c b/fs/aufs/vfsub.c -new file mode 100644 -index 0000000..5fd008c ---- /dev/null -+++ b/fs/aufs/vfsub.c -@@ -0,0 +1,864 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * sub-routines for VFS -+ */ -+ -+#include -+#include -+#include -+#include -+#include "../fs/mount.h" -+#include "aufs.h" -+ -+#ifdef CONFIG_AUFS_BR_FUSE -+int vfsub_test_mntns(struct vfsmount *mnt, struct super_block *h_sb) -+{ -+ struct nsproxy *ns; -+ -+ if (!au_test_fuse(h_sb) || !au_userns) -+ return 0; -+ -+ ns = current->nsproxy; -+ /* no {get,put}_nsproxy(ns) */ -+ return real_mount(mnt)->mnt_ns == ns->mnt_ns ? 0 : -EACCES; -+} -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+int vfsub_update_h_iattr(struct path *h_path, int *did) -+{ -+ int err; -+ struct kstat st; -+ struct super_block *h_sb; -+ -+ /* for remote fs, leave work for its getattr or d_revalidate */ -+ /* for bad i_attr fs, handle them in aufs_getattr() */ -+ /* still some fs may acquire i_mutex. we need to skip them */ -+ err = 0; -+ if (!did) -+ did = &err; -+ h_sb = h_path->dentry->d_sb; -+ *did = (!au_test_fs_remote(h_sb) && au_test_fs_refresh_iattr(h_sb)); -+ if (*did) -+ err = vfs_getattr(h_path, &st); -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct file *vfsub_dentry_open(struct path *path, int flags) -+{ -+ struct file *file; -+ -+ file = dentry_open(path, flags /* | __FMODE_NONOTIFY */, -+ current_cred()); -+ if (!IS_ERR_OR_NULL(file) -+ && (file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) -+ i_readcount_inc(path->dentry->d_inode); -+ -+ return file; -+} -+ -+struct file *vfsub_filp_open(const char *path, int oflags, int mode) -+{ -+ struct file *file; -+ -+ lockdep_off(); -+ file = filp_open(path, -+ oflags /* | __FMODE_NONOTIFY */, -+ mode); -+ lockdep_on(); -+ if (IS_ERR(file)) -+ goto out; -+ vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ -+ -+out: -+ return file; -+} -+ -+/* -+ * Ideally this function should call VFS:do_last() in order to keep all its -+ * checkings. But it is very hard for aufs to regenerate several VFS internal -+ * structure such as nameidata. This is a second (or third) best approach. -+ * cf. linux/fs/namei.c:do_last(), lookup_open() and atomic_open(). -+ */ -+int vfsub_atomic_open(struct inode *dir, struct dentry *dentry, -+ struct vfsub_aopen_args *args, struct au_branch *br) -+{ -+ int err; -+ struct file *file = args->file; -+ /* copied from linux/fs/namei.c:atomic_open() */ -+ struct dentry *const DENTRY_NOT_SET = (void *)-1UL; -+ -+ IMustLock(dir); -+ AuDebugOn(!dir->i_op->atomic_open); -+ -+ err = au_br_test_oflag(args->open_flag, br); -+ if (unlikely(err)) -+ goto out; -+ -+ args->file->f_path.dentry = DENTRY_NOT_SET; -+ args->file->f_path.mnt = au_br_mnt(br); -+ err = dir->i_op->atomic_open(dir, dentry, file, args->open_flag, -+ args->create_mode, args->opened); -+ if (err >= 0) { -+ /* some filesystems don't set FILE_CREATED while succeeded? */ -+ if (*args->opened & FILE_CREATED) -+ fsnotify_create(dir, dentry); -+ } else -+ goto out; -+ -+ -+ if (!err) { -+ /* todo: call VFS:may_open() here */ -+ err = open_check_o_direct(file); -+ /* todo: ima_file_check() too? */ -+ if (!err && (args->open_flag & __FMODE_EXEC)) -+ err = deny_write_access(file); -+ if (unlikely(err)) -+ /* note that the file is created and still opened */ -+ goto out; -+ } -+ -+ atomic_inc(&br->br_count); -+ fsnotify_open(file); -+ -+out: -+ return err; -+} -+ -+int vfsub_kern_path(const char *name, unsigned int flags, struct path *path) -+{ -+ int err; -+ -+ err = kern_path(name, flags, path); -+ if (!err && path->dentry->d_inode) -+ vfsub_update_h_iattr(path, /*did*/NULL); /*ignore*/ -+ return err; -+} -+ -+struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent, -+ int len) -+{ -+ struct path path = { -+ .mnt = NULL -+ }; -+ -+ /* VFS checks it too, but by WARN_ON_ONCE() */ -+ IMustLock(parent->d_inode); -+ -+ path.dentry = lookup_one_len(name, parent, len); -+ if (IS_ERR(path.dentry)) -+ goto out; -+ if (path.dentry->d_inode) -+ vfsub_update_h_iattr(&path, /*did*/NULL); /*ignore*/ -+ -+out: -+ AuTraceErrPtr(path.dentry); -+ return path.dentry; -+} -+ -+void vfsub_call_lkup_one(void *args) -+{ -+ struct vfsub_lkup_one_args *a = args; -+ *a->errp = vfsub_lkup_one(a->name, a->parent); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct dentry *vfsub_lock_rename(struct dentry *d1, struct au_hinode *hdir1, -+ struct dentry *d2, struct au_hinode *hdir2) -+{ -+ struct dentry *d; -+ -+ lockdep_off(); -+ d = lock_rename(d1, d2); -+ lockdep_on(); -+ au_hn_suspend(hdir1); -+ if (hdir1 != hdir2) -+ au_hn_suspend(hdir2); -+ -+ return d; -+} -+ -+void vfsub_unlock_rename(struct dentry *d1, struct au_hinode *hdir1, -+ struct dentry *d2, struct au_hinode *hdir2) -+{ -+ au_hn_resume(hdir1); -+ if (hdir1 != hdir2) -+ au_hn_resume(hdir2); -+ lockdep_off(); -+ unlock_rename(d1, d2); -+ lockdep_on(); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int vfsub_create(struct inode *dir, struct path *path, int mode, bool want_excl) -+{ -+ int err; -+ struct dentry *d; -+ -+ IMustLock(dir); -+ -+ d = path->dentry; -+ path->dentry = d->d_parent; -+ err = security_path_mknod(path, d, mode, 0); -+ path->dentry = d; -+ if (unlikely(err)) -+ goto out; -+ -+ lockdep_off(); -+ err = vfs_create(dir, path->dentry, mode, want_excl); -+ lockdep_on(); -+ if (!err) { -+ struct path tmp = *path; -+ int did; -+ -+ vfsub_update_h_iattr(&tmp, &did); -+ if (did) { -+ tmp.dentry = path->dentry->d_parent; -+ vfsub_update_h_iattr(&tmp, /*did*/NULL); -+ } -+ /*ignore*/ -+ } -+ -+out: -+ return err; -+} -+ -+int vfsub_symlink(struct inode *dir, struct path *path, const char *symname) -+{ -+ int err; -+ struct dentry *d; -+ -+ IMustLock(dir); -+ -+ d = path->dentry; -+ path->dentry = d->d_parent; -+ err = security_path_symlink(path, d, symname); -+ path->dentry = d; -+ if (unlikely(err)) -+ goto out; -+ -+ lockdep_off(); -+ err = vfs_symlink(dir, path->dentry, symname); -+ lockdep_on(); -+ if (!err) { -+ struct path tmp = *path; -+ int did; -+ -+ vfsub_update_h_iattr(&tmp, &did); -+ if (did) { -+ tmp.dentry = path->dentry->d_parent; -+ vfsub_update_h_iattr(&tmp, /*did*/NULL); -+ } -+ /*ignore*/ -+ } -+ -+out: -+ return err; -+} -+ -+int vfsub_mknod(struct inode *dir, struct path *path, int mode, dev_t dev) -+{ -+ int err; -+ struct dentry *d; -+ -+ IMustLock(dir); -+ -+ d = path->dentry; -+ path->dentry = d->d_parent; -+ err = security_path_mknod(path, d, mode, new_encode_dev(dev)); -+ path->dentry = d; -+ if (unlikely(err)) -+ goto out; -+ -+ lockdep_off(); -+ err = vfs_mknod(dir, path->dentry, mode, dev); -+ lockdep_on(); -+ if (!err) { -+ struct path tmp = *path; -+ int did; -+ -+ vfsub_update_h_iattr(&tmp, &did); -+ if (did) { -+ tmp.dentry = path->dentry->d_parent; -+ vfsub_update_h_iattr(&tmp, /*did*/NULL); -+ } -+ /*ignore*/ -+ } -+ -+out: -+ return err; -+} -+ -+static int au_test_nlink(struct inode *inode) -+{ -+ const unsigned int link_max = UINT_MAX >> 1; /* rough margin */ -+ -+ if (!au_test_fs_no_limit_nlink(inode->i_sb) -+ || inode->i_nlink < link_max) -+ return 0; -+ return -EMLINK; -+} -+ -+int vfsub_link(struct dentry *src_dentry, struct inode *dir, struct path *path, -+ struct inode **delegated_inode) -+{ -+ int err; -+ struct dentry *d; -+ -+ IMustLock(dir); -+ -+ err = au_test_nlink(src_dentry->d_inode); -+ if (unlikely(err)) -+ return err; -+ -+ /* we don't call may_linkat() */ -+ d = path->dentry; -+ path->dentry = d->d_parent; -+ err = security_path_link(src_dentry, path, d); -+ path->dentry = d; -+ if (unlikely(err)) -+ goto out; -+ -+ lockdep_off(); -+ err = vfs_link(src_dentry, dir, path->dentry, delegated_inode); -+ lockdep_on(); -+ if (!err) { -+ struct path tmp = *path; -+ int did; -+ -+ /* fuse has different memory inode for the same inumber */ -+ vfsub_update_h_iattr(&tmp, &did); -+ if (did) { -+ tmp.dentry = path->dentry->d_parent; -+ vfsub_update_h_iattr(&tmp, /*did*/NULL); -+ tmp.dentry = src_dentry; -+ vfsub_update_h_iattr(&tmp, /*did*/NULL); -+ } -+ /*ignore*/ -+ } -+ -+out: -+ return err; -+} -+ -+int vfsub_rename(struct inode *src_dir, struct dentry *src_dentry, -+ struct inode *dir, struct path *path, -+ struct inode **delegated_inode) -+{ -+ int err; -+ struct path tmp = { -+ .mnt = path->mnt -+ }; -+ struct dentry *d; -+ -+ IMustLock(dir); -+ IMustLock(src_dir); -+ -+ d = path->dentry; -+ path->dentry = d->d_parent; -+ tmp.dentry = src_dentry->d_parent; -+ err = security_path_rename(&tmp, src_dentry, path, d, /*flags*/0); -+ path->dentry = d; -+ if (unlikely(err)) -+ goto out; -+ -+ lockdep_off(); -+ err = vfs_rename(src_dir, src_dentry, dir, path->dentry, -+ delegated_inode, /*flags*/0); -+ lockdep_on(); -+ if (!err) { -+ int did; -+ -+ tmp.dentry = d->d_parent; -+ vfsub_update_h_iattr(&tmp, &did); -+ if (did) { -+ tmp.dentry = src_dentry; -+ vfsub_update_h_iattr(&tmp, /*did*/NULL); -+ tmp.dentry = src_dentry->d_parent; -+ vfsub_update_h_iattr(&tmp, /*did*/NULL); -+ } -+ /*ignore*/ -+ } -+ -+out: -+ return err; -+} -+ -+int vfsub_mkdir(struct inode *dir, struct path *path, int mode) -+{ -+ int err; -+ struct dentry *d; -+ -+ IMustLock(dir); -+ -+ d = path->dentry; -+ path->dentry = d->d_parent; -+ err = security_path_mkdir(path, d, mode); -+ path->dentry = d; -+ if (unlikely(err)) -+ goto out; -+ -+ lockdep_off(); -+ err = vfs_mkdir(dir, path->dentry, mode); -+ lockdep_on(); -+ if (!err) { -+ struct path tmp = *path; -+ int did; -+ -+ vfsub_update_h_iattr(&tmp, &did); -+ if (did) { -+ tmp.dentry = path->dentry->d_parent; -+ vfsub_update_h_iattr(&tmp, /*did*/NULL); -+ } -+ /*ignore*/ -+ } -+ -+out: -+ return err; -+} -+ -+int vfsub_rmdir(struct inode *dir, struct path *path) -+{ -+ int err; -+ struct dentry *d; -+ -+ IMustLock(dir); -+ -+ d = path->dentry; -+ path->dentry = d->d_parent; -+ err = security_path_rmdir(path, d); -+ path->dentry = d; -+ if (unlikely(err)) -+ goto out; -+ -+ lockdep_off(); -+ err = vfs_rmdir(dir, path->dentry); -+ lockdep_on(); -+ if (!err) { -+ struct path tmp = { -+ .dentry = path->dentry->d_parent, -+ .mnt = path->mnt -+ }; -+ -+ vfsub_update_h_iattr(&tmp, /*did*/NULL); /*ignore*/ -+ } -+ -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* todo: support mmap_sem? */ -+ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count, -+ loff_t *ppos) -+{ -+ ssize_t err; -+ -+ lockdep_off(); -+ err = vfs_read(file, ubuf, count, ppos); -+ lockdep_on(); -+ if (err >= 0) -+ vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ -+ return err; -+} -+ -+/* todo: kernel_read()? */ -+ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count, -+ loff_t *ppos) -+{ -+ ssize_t err; -+ mm_segment_t oldfs; -+ union { -+ void *k; -+ char __user *u; -+ } buf; -+ -+ buf.k = kbuf; -+ oldfs = get_fs(); -+ set_fs(KERNEL_DS); -+ err = vfsub_read_u(file, buf.u, count, ppos); -+ set_fs(oldfs); -+ return err; -+} -+ -+ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count, -+ loff_t *ppos) -+{ -+ ssize_t err; -+ -+ lockdep_off(); -+ err = vfs_write(file, ubuf, count, ppos); -+ lockdep_on(); -+ if (err >= 0) -+ vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ -+ return err; -+} -+ -+ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, loff_t *ppos) -+{ -+ ssize_t err; -+ mm_segment_t oldfs; -+ union { -+ void *k; -+ const char __user *u; -+ } buf; -+ -+ buf.k = kbuf; -+ oldfs = get_fs(); -+ set_fs(KERNEL_DS); -+ err = vfsub_write_u(file, buf.u, count, ppos); -+ set_fs(oldfs); -+ return err; -+} -+ -+int vfsub_flush(struct file *file, fl_owner_t id) -+{ -+ int err; -+ -+ err = 0; -+ if (file->f_op->flush) { -+ if (!au_test_nfs(file->f_dentry->d_sb)) -+ err = file->f_op->flush(file, id); -+ else { -+ lockdep_off(); -+ err = file->f_op->flush(file, id); -+ lockdep_on(); -+ } -+ if (!err) -+ vfsub_update_h_iattr(&file->f_path, /*did*/NULL); -+ /*ignore*/ -+ } -+ return err; -+} -+ -+int vfsub_iterate_dir(struct file *file, struct dir_context *ctx) -+{ -+ int err; -+ -+ AuDbg("%pD, ctx{%pf, %llu}\n", file, ctx->actor, ctx->pos); -+ -+ lockdep_off(); -+ err = iterate_dir(file, ctx); -+ lockdep_on(); -+ if (err >= 0) -+ vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ -+ return err; -+} -+ -+long vfsub_splice_to(struct file *in, loff_t *ppos, -+ struct pipe_inode_info *pipe, size_t len, -+ unsigned int flags) -+{ -+ long err; -+ -+ lockdep_off(); -+ err = do_splice_to(in, ppos, pipe, len, flags); -+ lockdep_on(); -+ file_accessed(in); -+ if (err >= 0) -+ vfsub_update_h_iattr(&in->f_path, /*did*/NULL); /*ignore*/ -+ return err; -+} -+ -+long vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out, -+ loff_t *ppos, size_t len, unsigned int flags) -+{ -+ long err; -+ -+ lockdep_off(); -+ err = do_splice_from(pipe, out, ppos, len, flags); -+ lockdep_on(); -+ if (err >= 0) -+ vfsub_update_h_iattr(&out->f_path, /*did*/NULL); /*ignore*/ -+ return err; -+} -+ -+int vfsub_fsync(struct file *file, struct path *path, int datasync) -+{ -+ int err; -+ -+ /* file can be NULL */ -+ lockdep_off(); -+ err = vfs_fsync(file, datasync); -+ lockdep_on(); -+ if (!err) { -+ if (!path) { -+ AuDebugOn(!file); -+ path = &file->f_path; -+ } -+ vfsub_update_h_iattr(path, /*did*/NULL); /*ignore*/ -+ } -+ return err; -+} -+ -+/* cf. open.c:do_sys_truncate() and do_sys_ftruncate() */ -+int vfsub_trunc(struct path *h_path, loff_t length, unsigned int attr, -+ struct file *h_file) -+{ -+ int err; -+ struct inode *h_inode; -+ struct super_block *h_sb; -+ -+ if (!h_file) { -+ err = vfsub_truncate(h_path, length); -+ goto out; -+ } -+ -+ h_inode = h_path->dentry->d_inode; -+ h_sb = h_inode->i_sb; -+ lockdep_off(); -+ sb_start_write(h_sb); -+ lockdep_on(); -+ err = locks_verify_truncate(h_inode, h_file, length); -+ if (!err) -+ err = security_path_truncate(h_path); -+ if (!err) { -+ lockdep_off(); -+ err = do_truncate(h_path->dentry, length, attr, h_file); -+ lockdep_on(); -+ } -+ lockdep_off(); -+ sb_end_write(h_sb); -+ lockdep_on(); -+ -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct au_vfsub_mkdir_args { -+ int *errp; -+ struct inode *dir; -+ struct path *path; -+ int mode; -+}; -+ -+static void au_call_vfsub_mkdir(void *args) -+{ -+ struct au_vfsub_mkdir_args *a = args; -+ *a->errp = vfsub_mkdir(a->dir, a->path, a->mode); -+} -+ -+int vfsub_sio_mkdir(struct inode *dir, struct path *path, int mode) -+{ -+ int err, do_sio, wkq_err; -+ -+ do_sio = au_test_h_perm_sio(dir, MAY_EXEC | MAY_WRITE); -+ if (!do_sio) { -+ lockdep_off(); -+ err = vfsub_mkdir(dir, path, mode); -+ lockdep_on(); -+ } else { -+ struct au_vfsub_mkdir_args args = { -+ .errp = &err, -+ .dir = dir, -+ .path = path, -+ .mode = mode -+ }; -+ wkq_err = au_wkq_wait(au_call_vfsub_mkdir, &args); -+ if (unlikely(wkq_err)) -+ err = wkq_err; -+ } -+ -+ return err; -+} -+ -+struct au_vfsub_rmdir_args { -+ int *errp; -+ struct inode *dir; -+ struct path *path; -+}; -+ -+static void au_call_vfsub_rmdir(void *args) -+{ -+ struct au_vfsub_rmdir_args *a = args; -+ *a->errp = vfsub_rmdir(a->dir, a->path); -+} -+ -+int vfsub_sio_rmdir(struct inode *dir, struct path *path) -+{ -+ int err, do_sio, wkq_err; -+ -+ do_sio = au_test_h_perm_sio(dir, MAY_EXEC | MAY_WRITE); -+ if (!do_sio) { -+ lockdep_off(); -+ err = vfsub_rmdir(dir, path); -+ lockdep_on(); -+ } else { -+ struct au_vfsub_rmdir_args args = { -+ .errp = &err, -+ .dir = dir, -+ .path = path -+ }; -+ wkq_err = au_wkq_wait(au_call_vfsub_rmdir, &args); -+ if (unlikely(wkq_err)) -+ err = wkq_err; -+ } -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct notify_change_args { -+ int *errp; -+ struct path *path; -+ struct iattr *ia; -+ struct inode **delegated_inode; -+}; -+ -+static void call_notify_change(void *args) -+{ -+ struct notify_change_args *a = args; -+ struct inode *h_inode; -+ -+ h_inode = a->path->dentry->d_inode; -+ IMustLock(h_inode); -+ -+ *a->errp = -EPERM; -+ if (!IS_IMMUTABLE(h_inode) && !IS_APPEND(h_inode)) { -+ lockdep_off(); -+ *a->errp = notify_change(a->path->dentry, a->ia, -+ a->delegated_inode); -+ lockdep_on(); -+ if (!*a->errp) -+ vfsub_update_h_iattr(a->path, /*did*/NULL); /*ignore*/ -+ } -+ AuTraceErr(*a->errp); -+} -+ -+int vfsub_notify_change(struct path *path, struct iattr *ia, -+ struct inode **delegated_inode) -+{ -+ int err; -+ struct notify_change_args args = { -+ .errp = &err, -+ .path = path, -+ .ia = ia, -+ .delegated_inode = delegated_inode -+ }; -+ -+ call_notify_change(&args); -+ -+ return err; -+} -+ -+int vfsub_sio_notify_change(struct path *path, struct iattr *ia, -+ struct inode **delegated_inode) -+{ -+ int err, wkq_err; -+ struct notify_change_args args = { -+ .errp = &err, -+ .path = path, -+ .ia = ia, -+ .delegated_inode = delegated_inode -+ }; -+ -+ wkq_err = au_wkq_wait(call_notify_change, &args); -+ if (unlikely(wkq_err)) -+ err = wkq_err; -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct unlink_args { -+ int *errp; -+ struct inode *dir; -+ struct path *path; -+ struct inode **delegated_inode; -+}; -+ -+static void call_unlink(void *args) -+{ -+ struct unlink_args *a = args; -+ struct dentry *d = a->path->dentry; -+ struct inode *h_inode; -+ const int stop_sillyrename = (au_test_nfs(d->d_sb) -+ && au_dcount(d) == 1); -+ -+ IMustLock(a->dir); -+ -+ a->path->dentry = d->d_parent; -+ *a->errp = security_path_unlink(a->path, d); -+ a->path->dentry = d; -+ if (unlikely(*a->errp)) -+ return; -+ -+ if (!stop_sillyrename) -+ dget(d); -+ h_inode = d->d_inode; -+ if (h_inode) -+ ihold(h_inode); -+ -+ lockdep_off(); -+ *a->errp = vfs_unlink(a->dir, d, a->delegated_inode); -+ lockdep_on(); -+ if (!*a->errp) { -+ struct path tmp = { -+ .dentry = d->d_parent, -+ .mnt = a->path->mnt -+ }; -+ vfsub_update_h_iattr(&tmp, /*did*/NULL); /*ignore*/ -+ } -+ -+ if (!stop_sillyrename) -+ dput(d); -+ if (h_inode) -+ iput(h_inode); -+ -+ AuTraceErr(*a->errp); -+} -+ -+/* -+ * @dir: must be locked. -+ * @dentry: target dentry. -+ */ -+int vfsub_unlink(struct inode *dir, struct path *path, -+ struct inode **delegated_inode, int force) -+{ -+ int err; -+ struct unlink_args args = { -+ .errp = &err, -+ .dir = dir, -+ .path = path, -+ .delegated_inode = delegated_inode -+ }; -+ -+ if (!force) -+ call_unlink(&args); -+ else { -+ int wkq_err; -+ -+ wkq_err = au_wkq_wait(call_unlink, &args); -+ if (unlikely(wkq_err)) -+ err = wkq_err; -+ } -+ -+ return err; -+} -diff --git a/fs/aufs/vfsub.h b/fs/aufs/vfsub.h -new file mode 100644 -index 0000000..2c33298 ---- /dev/null -+++ b/fs/aufs/vfsub.h -@@ -0,0 +1,315 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * sub-routines for VFS -+ */ -+ -+#ifndef __AUFS_VFSUB_H__ -+#define __AUFS_VFSUB_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+#include -+#include -+#include -+#include "debug.h" -+ -+/* copied from linux/fs/internal.h */ -+/* todo: BAD approach!! */ -+extern void __mnt_drop_write(struct vfsmount *); -+extern spinlock_t inode_sb_list_lock; -+extern int open_check_o_direct(struct file *f); -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* lock subclass for lower inode */ -+/* default MAX_LOCKDEP_SUBCLASSES(8) is not enough */ -+/* reduce? gave up. */ -+enum { -+ AuLsc_I_Begin = I_MUTEX_PARENT2, /* 5 */ -+ AuLsc_I_PARENT, /* lower inode, parent first */ -+ AuLsc_I_PARENT2, /* copyup dirs */ -+ AuLsc_I_PARENT3, /* copyup wh */ -+ AuLsc_I_CHILD, -+ AuLsc_I_CHILD2, -+ AuLsc_I_End -+}; -+ -+/* to debug easier, do not make them inlined functions */ -+#define MtxMustLock(mtx) AuDebugOn(!mutex_is_locked(mtx)) -+#define IMustLock(i) MtxMustLock(&(i)->i_mutex) -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline void vfsub_drop_nlink(struct inode *inode) -+{ -+ AuDebugOn(!inode->i_nlink); -+ drop_nlink(inode); -+} -+ -+static inline void vfsub_dead_dir(struct inode *inode) -+{ -+ AuDebugOn(!S_ISDIR(inode->i_mode)); -+ inode->i_flags |= S_DEAD; -+ clear_nlink(inode); -+} -+ -+static inline int vfsub_native_ro(struct inode *inode) -+{ -+ return (inode->i_sb->s_flags & MS_RDONLY) -+ || IS_RDONLY(inode) -+ /* || IS_APPEND(inode) */ -+ || IS_IMMUTABLE(inode); -+} -+ -+#ifdef CONFIG_AUFS_BR_FUSE -+int vfsub_test_mntns(struct vfsmount *mnt, struct super_block *h_sb); -+#else -+AuStubInt0(vfsub_test_mntns, struct vfsmount *mnt, struct super_block *h_sb); -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+int vfsub_update_h_iattr(struct path *h_path, int *did); -+struct file *vfsub_dentry_open(struct path *path, int flags); -+struct file *vfsub_filp_open(const char *path, int oflags, int mode); -+struct vfsub_aopen_args { -+ struct file *file; -+ unsigned int open_flag; -+ umode_t create_mode; -+ int *opened; -+}; -+struct au_branch; -+int vfsub_atomic_open(struct inode *dir, struct dentry *dentry, -+ struct vfsub_aopen_args *args, struct au_branch *br); -+int vfsub_kern_path(const char *name, unsigned int flags, struct path *path); -+ -+struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent, -+ int len); -+ -+struct vfsub_lkup_one_args { -+ struct dentry **errp; -+ struct qstr *name; -+ struct dentry *parent; -+}; -+ -+static inline struct dentry *vfsub_lkup_one(struct qstr *name, -+ struct dentry *parent) -+{ -+ return vfsub_lookup_one_len(name->name, parent, name->len); -+} -+ -+void vfsub_call_lkup_one(void *args); -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline int vfsub_mnt_want_write(struct vfsmount *mnt) -+{ -+ int err; -+ -+ lockdep_off(); -+ err = mnt_want_write(mnt); -+ lockdep_on(); -+ return err; -+} -+ -+static inline void vfsub_mnt_drop_write(struct vfsmount *mnt) -+{ -+ lockdep_off(); -+ mnt_drop_write(mnt); -+ lockdep_on(); -+} -+ -+#if 0 /* reserved */ -+static inline void vfsub_mnt_drop_write_file(struct file *file) -+{ -+ lockdep_off(); -+ mnt_drop_write_file(file); -+ lockdep_on(); -+} -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct au_hinode; -+struct dentry *vfsub_lock_rename(struct dentry *d1, struct au_hinode *hdir1, -+ struct dentry *d2, struct au_hinode *hdir2); -+void vfsub_unlock_rename(struct dentry *d1, struct au_hinode *hdir1, -+ struct dentry *d2, struct au_hinode *hdir2); -+ -+int vfsub_create(struct inode *dir, struct path *path, int mode, -+ bool want_excl); -+int vfsub_symlink(struct inode *dir, struct path *path, -+ const char *symname); -+int vfsub_mknod(struct inode *dir, struct path *path, int mode, dev_t dev); -+int vfsub_link(struct dentry *src_dentry, struct inode *dir, -+ struct path *path, struct inode **delegated_inode); -+int vfsub_rename(struct inode *src_hdir, struct dentry *src_dentry, -+ struct inode *hdir, struct path *path, -+ struct inode **delegated_inode); -+int vfsub_mkdir(struct inode *dir, struct path *path, int mode); -+int vfsub_rmdir(struct inode *dir, struct path *path); -+ -+/* ---------------------------------------------------------------------- */ -+ -+ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count, -+ loff_t *ppos); -+ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count, -+ loff_t *ppos); -+ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count, -+ loff_t *ppos); -+ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, -+ loff_t *ppos); -+int vfsub_flush(struct file *file, fl_owner_t id); -+int vfsub_iterate_dir(struct file *file, struct dir_context *ctx); -+ -+/* just for type-check */ -+static inline filldir_t au_diractor(int (*func)(struct dir_context *, -+ const char *, int, loff_t, u64, -+ unsigned)) -+{ -+ return (filldir_t)func; -+} -+ -+static inline loff_t vfsub_f_size_read(struct file *file) -+{ -+ return i_size_read(file_inode(file)); -+} -+ -+static inline unsigned int vfsub_file_flags(struct file *file) -+{ -+ unsigned int flags; -+ -+ spin_lock(&file->f_lock); -+ flags = file->f_flags; -+ spin_unlock(&file->f_lock); -+ -+ return flags; -+} -+ -+#if 0 /* reserved */ -+static inline void vfsub_file_accessed(struct file *h_file) -+{ -+ file_accessed(h_file); -+ vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL); /*ignore*/ -+} -+#endif -+ -+static inline void vfsub_touch_atime(struct vfsmount *h_mnt, -+ struct dentry *h_dentry) -+{ -+ struct path h_path = { -+ .dentry = h_dentry, -+ .mnt = h_mnt -+ }; -+ touch_atime(&h_path); -+ vfsub_update_h_iattr(&h_path, /*did*/NULL); /*ignore*/ -+} -+ -+static inline int vfsub_update_time(struct inode *h_inode, struct timespec *ts, -+ int flags) -+{ -+ return update_time(h_inode, ts, flags); -+ /* no vfsub_update_h_iattr() since we don't have struct path */ -+} -+ -+#ifdef CONFIG_FS_POSIX_ACL -+static inline int vfsub_acl_chmod(struct inode *h_inode, umode_t h_mode) -+{ -+ int err; -+ -+ err = posix_acl_chmod(h_inode, h_mode); -+ if (err == -EOPNOTSUPP) -+ err = 0; -+ return err; -+} -+#else -+AuStubInt0(vfsub_acl_chmod, struct inode *h_inode, umode_t h_mode); -+#endif -+ -+long vfsub_splice_to(struct file *in, loff_t *ppos, -+ struct pipe_inode_info *pipe, size_t len, -+ unsigned int flags); -+long vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out, -+ loff_t *ppos, size_t len, unsigned int flags); -+ -+static inline long vfsub_truncate(struct path *path, loff_t length) -+{ -+ long err; -+ -+ lockdep_off(); -+ err = vfs_truncate(path, length); -+ lockdep_on(); -+ return err; -+} -+ -+int vfsub_trunc(struct path *h_path, loff_t length, unsigned int attr, -+ struct file *h_file); -+int vfsub_fsync(struct file *file, struct path *path, int datasync); -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline loff_t vfsub_llseek(struct file *file, loff_t offset, int origin) -+{ -+ loff_t err; -+ -+ lockdep_off(); -+ err = vfs_llseek(file, offset, origin); -+ lockdep_on(); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int vfsub_sio_mkdir(struct inode *dir, struct path *path, int mode); -+int vfsub_sio_rmdir(struct inode *dir, struct path *path); -+int vfsub_sio_notify_change(struct path *path, struct iattr *ia, -+ struct inode **delegated_inode); -+int vfsub_notify_change(struct path *path, struct iattr *ia, -+ struct inode **delegated_inode); -+int vfsub_unlink(struct inode *dir, struct path *path, -+ struct inode **delegated_inode, int force); -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline int vfsub_setxattr(struct dentry *dentry, const char *name, -+ const void *value, size_t size, int flags) -+{ -+ int err; -+ -+ lockdep_off(); -+ err = vfs_setxattr(dentry, name, value, size, flags); -+ lockdep_on(); -+ -+ return err; -+} -+ -+static inline int vfsub_removexattr(struct dentry *dentry, const char *name) -+{ -+ int err; -+ -+ lockdep_off(); -+ err = vfs_removexattr(dentry, name); -+ lockdep_on(); -+ -+ return err; -+} -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_VFSUB_H__ */ -diff --git a/fs/aufs/wbr_policy.c b/fs/aufs/wbr_policy.c -new file mode 100644 -index 0000000..64cd9fe ---- /dev/null -+++ b/fs/aufs/wbr_policy.c -@@ -0,0 +1,765 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * policies for selecting one among multiple writable branches -+ */ -+ -+#include -+#include "aufs.h" -+ -+/* subset of cpup_attr() */ -+static noinline_for_stack -+int au_cpdown_attr(struct path *h_path, struct dentry *h_src) -+{ -+ int err, sbits; -+ struct iattr ia; -+ struct inode *h_isrc; -+ -+ h_isrc = h_src->d_inode; -+ ia.ia_valid = ATTR_FORCE | ATTR_MODE | ATTR_UID | ATTR_GID; -+ ia.ia_mode = h_isrc->i_mode; -+ ia.ia_uid = h_isrc->i_uid; -+ ia.ia_gid = h_isrc->i_gid; -+ sbits = !!(ia.ia_mode & (S_ISUID | S_ISGID)); -+ au_cpup_attr_flags(h_path->dentry->d_inode, h_isrc->i_flags); -+ /* no delegation since it is just created */ -+ err = vfsub_sio_notify_change(h_path, &ia, /*delegated*/NULL); -+ -+ /* is this nfs only? */ -+ if (!err && sbits && au_test_nfs(h_path->dentry->d_sb)) { -+ ia.ia_valid = ATTR_FORCE | ATTR_MODE; -+ ia.ia_mode = h_isrc->i_mode; -+ err = vfsub_sio_notify_change(h_path, &ia, /*delegated*/NULL); -+ } -+ -+ return err; -+} -+ -+#define AuCpdown_PARENT_OPQ 1 -+#define AuCpdown_WHED (1 << 1) -+#define AuCpdown_MADE_DIR (1 << 2) -+#define AuCpdown_DIROPQ (1 << 3) -+#define au_ftest_cpdown(flags, name) ((flags) & AuCpdown_##name) -+#define au_fset_cpdown(flags, name) \ -+ do { (flags) |= AuCpdown_##name; } while (0) -+#define au_fclr_cpdown(flags, name) \ -+ do { (flags) &= ~AuCpdown_##name; } while (0) -+ -+static int au_cpdown_dir_opq(struct dentry *dentry, aufs_bindex_t bdst, -+ unsigned int *flags) -+{ -+ int err; -+ struct dentry *opq_dentry; -+ -+ opq_dentry = au_diropq_create(dentry, bdst); -+ err = PTR_ERR(opq_dentry); -+ if (IS_ERR(opq_dentry)) -+ goto out; -+ dput(opq_dentry); -+ au_fset_cpdown(*flags, DIROPQ); -+ -+out: -+ return err; -+} -+ -+static int au_cpdown_dir_wh(struct dentry *dentry, struct dentry *h_parent, -+ struct inode *dir, aufs_bindex_t bdst) -+{ -+ int err; -+ struct path h_path; -+ struct au_branch *br; -+ -+ br = au_sbr(dentry->d_sb, bdst); -+ h_path.dentry = au_wh_lkup(h_parent, &dentry->d_name, br); -+ err = PTR_ERR(h_path.dentry); -+ if (IS_ERR(h_path.dentry)) -+ goto out; -+ -+ err = 0; -+ if (h_path.dentry->d_inode) { -+ h_path.mnt = au_br_mnt(br); -+ err = au_wh_unlink_dentry(au_h_iptr(dir, bdst), &h_path, -+ dentry); -+ } -+ dput(h_path.dentry); -+ -+out: -+ return err; -+} -+ -+static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t bdst, -+ struct au_pin *pin, -+ struct dentry *h_parent, void *arg) -+{ -+ int err, rerr; -+ aufs_bindex_t bopq, bstart; -+ struct path h_path; -+ struct dentry *parent; -+ struct inode *h_dir, *h_inode, *inode, *dir; -+ unsigned int *flags = arg; -+ -+ bstart = au_dbstart(dentry); -+ /* dentry is di-locked */ -+ parent = dget_parent(dentry); -+ dir = parent->d_inode; -+ h_dir = h_parent->d_inode; -+ AuDebugOn(h_dir != au_h_iptr(dir, bdst)); -+ IMustLock(h_dir); -+ -+ err = au_lkup_neg(dentry, bdst, /*wh*/0); -+ if (unlikely(err < 0)) -+ goto out; -+ h_path.dentry = au_h_dptr(dentry, bdst); -+ h_path.mnt = au_sbr_mnt(dentry->d_sb, bdst); -+ err = vfsub_sio_mkdir(au_h_iptr(dir, bdst), &h_path, -+ S_IRWXU | S_IRUGO | S_IXUGO); -+ if (unlikely(err)) -+ goto out_put; -+ au_fset_cpdown(*flags, MADE_DIR); -+ -+ bopq = au_dbdiropq(dentry); -+ au_fclr_cpdown(*flags, WHED); -+ au_fclr_cpdown(*flags, DIROPQ); -+ if (au_dbwh(dentry) == bdst) -+ au_fset_cpdown(*flags, WHED); -+ if (!au_ftest_cpdown(*flags, PARENT_OPQ) && bopq <= bdst) -+ au_fset_cpdown(*flags, PARENT_OPQ); -+ h_inode = h_path.dentry->d_inode; -+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); -+ if (au_ftest_cpdown(*flags, WHED)) { -+ err = au_cpdown_dir_opq(dentry, bdst, flags); -+ if (unlikely(err)) { -+ mutex_unlock(&h_inode->i_mutex); -+ goto out_dir; -+ } -+ } -+ -+ err = au_cpdown_attr(&h_path, au_h_dptr(dentry, bstart)); -+ mutex_unlock(&h_inode->i_mutex); -+ if (unlikely(err)) -+ goto out_opq; -+ -+ if (au_ftest_cpdown(*flags, WHED)) { -+ err = au_cpdown_dir_wh(dentry, h_parent, dir, bdst); -+ if (unlikely(err)) -+ goto out_opq; -+ } -+ -+ inode = dentry->d_inode; -+ if (au_ibend(inode) < bdst) -+ au_set_ibend(inode, bdst); -+ au_set_h_iptr(inode, bdst, au_igrab(h_inode), -+ au_hi_flags(inode, /*isdir*/1)); -+ au_fhsm_wrote(dentry->d_sb, bdst, /*force*/0); -+ goto out; /* success */ -+ -+ /* revert */ -+out_opq: -+ if (au_ftest_cpdown(*flags, DIROPQ)) { -+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); -+ rerr = au_diropq_remove(dentry, bdst); -+ mutex_unlock(&h_inode->i_mutex); -+ if (unlikely(rerr)) { -+ AuIOErr("failed removing diropq for %pd b%d (%d)\n", -+ dentry, bdst, rerr); -+ err = -EIO; -+ goto out; -+ } -+ } -+out_dir: -+ if (au_ftest_cpdown(*flags, MADE_DIR)) { -+ rerr = vfsub_sio_rmdir(au_h_iptr(dir, bdst), &h_path); -+ if (unlikely(rerr)) { -+ AuIOErr("failed removing %pd b%d (%d)\n", -+ dentry, bdst, rerr); -+ err = -EIO; -+ } -+ } -+out_put: -+ au_set_h_dptr(dentry, bdst, NULL); -+ if (au_dbend(dentry) == bdst) -+ au_update_dbend(dentry); -+out: -+ dput(parent); -+ return err; -+} -+ -+int au_cpdown_dirs(struct dentry *dentry, aufs_bindex_t bdst) -+{ -+ int err; -+ unsigned int flags; -+ -+ flags = 0; -+ err = au_cp_dirs(dentry, bdst, au_cpdown_dir, &flags); -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* policies for create */ -+ -+int au_wbr_nonopq(struct dentry *dentry, aufs_bindex_t bindex) -+{ -+ int err, i, j, ndentry; -+ aufs_bindex_t bopq; -+ struct au_dcsub_pages dpages; -+ struct au_dpage *dpage; -+ struct dentry **dentries, *parent, *d; -+ -+ err = au_dpages_init(&dpages, GFP_NOFS); -+ if (unlikely(err)) -+ goto out; -+ parent = dget_parent(dentry); -+ err = au_dcsub_pages_rev_aufs(&dpages, parent, /*do_include*/0); -+ if (unlikely(err)) -+ goto out_free; -+ -+ err = bindex; -+ for (i = 0; i < dpages.ndpage; i++) { -+ dpage = dpages.dpages + i; -+ dentries = dpage->dentries; -+ ndentry = dpage->ndentry; -+ for (j = 0; j < ndentry; j++) { -+ d = dentries[j]; -+ di_read_lock_parent2(d, !AuLock_IR); -+ bopq = au_dbdiropq(d); -+ di_read_unlock(d, !AuLock_IR); -+ if (bopq >= 0 && bopq < err) -+ err = bopq; -+ } -+ } -+ -+out_free: -+ dput(parent); -+ au_dpages_free(&dpages); -+out: -+ return err; -+} -+ -+static int au_wbr_bu(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ for (; bindex >= 0; bindex--) -+ if (!au_br_rdonly(au_sbr(sb, bindex))) -+ return bindex; -+ return -EROFS; -+} -+ -+/* top down parent */ -+static int au_wbr_create_tdp(struct dentry *dentry, -+ unsigned int flags __maybe_unused) -+{ -+ int err; -+ aufs_bindex_t bstart, bindex; -+ struct super_block *sb; -+ struct dentry *parent, *h_parent; -+ -+ sb = dentry->d_sb; -+ bstart = au_dbstart(dentry); -+ err = bstart; -+ if (!au_br_rdonly(au_sbr(sb, bstart))) -+ goto out; -+ -+ err = -EROFS; -+ parent = dget_parent(dentry); -+ for (bindex = au_dbstart(parent); bindex < bstart; bindex++) { -+ h_parent = au_h_dptr(parent, bindex); -+ if (!h_parent || !h_parent->d_inode) -+ continue; -+ -+ if (!au_br_rdonly(au_sbr(sb, bindex))) { -+ err = bindex; -+ break; -+ } -+ } -+ dput(parent); -+ -+ /* bottom up here */ -+ if (unlikely(err < 0)) { -+ err = au_wbr_bu(sb, bstart - 1); -+ if (err >= 0) -+ err = au_wbr_nonopq(dentry, err); -+ } -+ -+out: -+ AuDbg("b%d\n", err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* an exception for the policy other than tdp */ -+static int au_wbr_create_exp(struct dentry *dentry) -+{ -+ int err; -+ aufs_bindex_t bwh, bdiropq; -+ struct dentry *parent; -+ -+ err = -1; -+ bwh = au_dbwh(dentry); -+ parent = dget_parent(dentry); -+ bdiropq = au_dbdiropq(parent); -+ if (bwh >= 0) { -+ if (bdiropq >= 0) -+ err = min(bdiropq, bwh); -+ else -+ err = bwh; -+ AuDbg("%d\n", err); -+ } else if (bdiropq >= 0) { -+ err = bdiropq; -+ AuDbg("%d\n", err); -+ } -+ dput(parent); -+ -+ if (err >= 0) -+ err = au_wbr_nonopq(dentry, err); -+ -+ if (err >= 0 && au_br_rdonly(au_sbr(dentry->d_sb, err))) -+ err = -1; -+ -+ AuDbg("%d\n", err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* round robin */ -+static int au_wbr_create_init_rr(struct super_block *sb) -+{ -+ int err; -+ -+ err = au_wbr_bu(sb, au_sbend(sb)); -+ atomic_set(&au_sbi(sb)->si_wbr_rr_next, -err); /* less important */ -+ /* smp_mb(); */ -+ -+ AuDbg("b%d\n", err); -+ return err; -+} -+ -+static int au_wbr_create_rr(struct dentry *dentry, unsigned int flags) -+{ -+ int err, nbr; -+ unsigned int u; -+ aufs_bindex_t bindex, bend; -+ struct super_block *sb; -+ atomic_t *next; -+ -+ err = au_wbr_create_exp(dentry); -+ if (err >= 0) -+ goto out; -+ -+ sb = dentry->d_sb; -+ next = &au_sbi(sb)->si_wbr_rr_next; -+ bend = au_sbend(sb); -+ nbr = bend + 1; -+ for (bindex = 0; bindex <= bend; bindex++) { -+ if (!au_ftest_wbr(flags, DIR)) { -+ err = atomic_dec_return(next) + 1; -+ /* modulo for 0 is meaningless */ -+ if (unlikely(!err)) -+ err = atomic_dec_return(next) + 1; -+ } else -+ err = atomic_read(next); -+ AuDbg("%d\n", err); -+ u = err; -+ err = u % nbr; -+ AuDbg("%d\n", err); -+ if (!au_br_rdonly(au_sbr(sb, err))) -+ break; -+ err = -EROFS; -+ } -+ -+ if (err >= 0) -+ err = au_wbr_nonopq(dentry, err); -+ -+out: -+ AuDbg("%d\n", err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* most free space */ -+static void au_mfs(struct dentry *dentry, struct dentry *parent) -+{ -+ struct super_block *sb; -+ struct au_branch *br; -+ struct au_wbr_mfs *mfs; -+ struct dentry *h_parent; -+ aufs_bindex_t bindex, bend; -+ int err; -+ unsigned long long b, bavail; -+ struct path h_path; -+ /* reduce the stack usage */ -+ struct kstatfs *st; -+ -+ st = kmalloc(sizeof(*st), GFP_NOFS); -+ if (unlikely(!st)) { -+ AuWarn1("failed updating mfs(%d), ignored\n", -ENOMEM); -+ return; -+ } -+ -+ bavail = 0; -+ sb = dentry->d_sb; -+ mfs = &au_sbi(sb)->si_wbr_mfs; -+ MtxMustLock(&mfs->mfs_lock); -+ mfs->mfs_bindex = -EROFS; -+ mfs->mfsrr_bytes = 0; -+ if (!parent) { -+ bindex = 0; -+ bend = au_sbend(sb); -+ } else { -+ bindex = au_dbstart(parent); -+ bend = au_dbtaildir(parent); -+ } -+ -+ for (; bindex <= bend; bindex++) { -+ if (parent) { -+ h_parent = au_h_dptr(parent, bindex); -+ if (!h_parent || !h_parent->d_inode) -+ continue; -+ } -+ br = au_sbr(sb, bindex); -+ if (au_br_rdonly(br)) -+ continue; -+ -+ /* sb->s_root for NFS is unreliable */ -+ h_path.mnt = au_br_mnt(br); -+ h_path.dentry = h_path.mnt->mnt_root; -+ err = vfs_statfs(&h_path, st); -+ if (unlikely(err)) { -+ AuWarn1("failed statfs, b%d, %d\n", bindex, err); -+ continue; -+ } -+ -+ /* when the available size is equal, select the lower one */ -+ BUILD_BUG_ON(sizeof(b) < sizeof(st->f_bavail) -+ || sizeof(b) < sizeof(st->f_bsize)); -+ b = st->f_bavail * st->f_bsize; -+ br->br_wbr->wbr_bytes = b; -+ if (b >= bavail) { -+ bavail = b; -+ mfs->mfs_bindex = bindex; -+ mfs->mfs_jiffy = jiffies; -+ } -+ } -+ -+ mfs->mfsrr_bytes = bavail; -+ AuDbg("b%d\n", mfs->mfs_bindex); -+ kfree(st); -+} -+ -+static int au_wbr_create_mfs(struct dentry *dentry, unsigned int flags) -+{ -+ int err; -+ struct dentry *parent; -+ struct super_block *sb; -+ struct au_wbr_mfs *mfs; -+ -+ err = au_wbr_create_exp(dentry); -+ if (err >= 0) -+ goto out; -+ -+ sb = dentry->d_sb; -+ parent = NULL; -+ if (au_ftest_wbr(flags, PARENT)) -+ parent = dget_parent(dentry); -+ mfs = &au_sbi(sb)->si_wbr_mfs; -+ mutex_lock(&mfs->mfs_lock); -+ if (time_after(jiffies, mfs->mfs_jiffy + mfs->mfs_expire) -+ || mfs->mfs_bindex < 0 -+ || au_br_rdonly(au_sbr(sb, mfs->mfs_bindex))) -+ au_mfs(dentry, parent); -+ mutex_unlock(&mfs->mfs_lock); -+ err = mfs->mfs_bindex; -+ dput(parent); -+ -+ if (err >= 0) -+ err = au_wbr_nonopq(dentry, err); -+ -+out: -+ AuDbg("b%d\n", err); -+ return err; -+} -+ -+static int au_wbr_create_init_mfs(struct super_block *sb) -+{ -+ struct au_wbr_mfs *mfs; -+ -+ mfs = &au_sbi(sb)->si_wbr_mfs; -+ mutex_init(&mfs->mfs_lock); -+ mfs->mfs_jiffy = 0; -+ mfs->mfs_bindex = -EROFS; -+ -+ return 0; -+} -+ -+static int au_wbr_create_fin_mfs(struct super_block *sb __maybe_unused) -+{ -+ mutex_destroy(&au_sbi(sb)->si_wbr_mfs.mfs_lock); -+ return 0; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* most free space and then round robin */ -+static int au_wbr_create_mfsrr(struct dentry *dentry, unsigned int flags) -+{ -+ int err; -+ struct au_wbr_mfs *mfs; -+ -+ err = au_wbr_create_mfs(dentry, flags); -+ if (err >= 0) { -+ mfs = &au_sbi(dentry->d_sb)->si_wbr_mfs; -+ mutex_lock(&mfs->mfs_lock); -+ if (mfs->mfsrr_bytes < mfs->mfsrr_watermark) -+ err = au_wbr_create_rr(dentry, flags); -+ mutex_unlock(&mfs->mfs_lock); -+ } -+ -+ AuDbg("b%d\n", err); -+ return err; -+} -+ -+static int au_wbr_create_init_mfsrr(struct super_block *sb) -+{ -+ int err; -+ -+ au_wbr_create_init_mfs(sb); /* ignore */ -+ err = au_wbr_create_init_rr(sb); -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* top down parent and most free space */ -+static int au_wbr_create_pmfs(struct dentry *dentry, unsigned int flags) -+{ -+ int err, e2; -+ unsigned long long b; -+ aufs_bindex_t bindex, bstart, bend; -+ struct super_block *sb; -+ struct dentry *parent, *h_parent; -+ struct au_branch *br; -+ -+ err = au_wbr_create_tdp(dentry, flags); -+ if (unlikely(err < 0)) -+ goto out; -+ parent = dget_parent(dentry); -+ bstart = au_dbstart(parent); -+ bend = au_dbtaildir(parent); -+ if (bstart == bend) -+ goto out_parent; /* success */ -+ -+ e2 = au_wbr_create_mfs(dentry, flags); -+ if (e2 < 0) -+ goto out_parent; /* success */ -+ -+ /* when the available size is equal, select upper one */ -+ sb = dentry->d_sb; -+ br = au_sbr(sb, err); -+ b = br->br_wbr->wbr_bytes; -+ AuDbg("b%d, %llu\n", err, b); -+ -+ for (bindex = bstart; bindex <= bend; bindex++) { -+ h_parent = au_h_dptr(parent, bindex); -+ if (!h_parent || !h_parent->d_inode) -+ continue; -+ -+ br = au_sbr(sb, bindex); -+ if (!au_br_rdonly(br) && br->br_wbr->wbr_bytes > b) { -+ b = br->br_wbr->wbr_bytes; -+ err = bindex; -+ AuDbg("b%d, %llu\n", err, b); -+ } -+ } -+ -+ if (err >= 0) -+ err = au_wbr_nonopq(dentry, err); -+ -+out_parent: -+ dput(parent); -+out: -+ AuDbg("b%d\n", err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * - top down parent -+ * - most free space with parent -+ * - most free space round-robin regardless parent -+ */ -+static int au_wbr_create_pmfsrr(struct dentry *dentry, unsigned int flags) -+{ -+ int err; -+ unsigned long long watermark; -+ struct super_block *sb; -+ struct au_branch *br; -+ struct au_wbr_mfs *mfs; -+ -+ err = au_wbr_create_pmfs(dentry, flags | AuWbr_PARENT); -+ if (unlikely(err < 0)) -+ goto out; -+ -+ sb = dentry->d_sb; -+ br = au_sbr(sb, err); -+ mfs = &au_sbi(sb)->si_wbr_mfs; -+ mutex_lock(&mfs->mfs_lock); -+ watermark = mfs->mfsrr_watermark; -+ mutex_unlock(&mfs->mfs_lock); -+ if (br->br_wbr->wbr_bytes < watermark) -+ /* regardless the parent dir */ -+ err = au_wbr_create_mfsrr(dentry, flags); -+ -+out: -+ AuDbg("b%d\n", err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* policies for copyup */ -+ -+/* top down parent */ -+static int au_wbr_copyup_tdp(struct dentry *dentry) -+{ -+ return au_wbr_create_tdp(dentry, /*flags, anything is ok*/0); -+} -+ -+/* bottom up parent */ -+static int au_wbr_copyup_bup(struct dentry *dentry) -+{ -+ int err; -+ aufs_bindex_t bindex, bstart; -+ struct dentry *parent, *h_parent; -+ struct super_block *sb; -+ -+ err = -EROFS; -+ sb = dentry->d_sb; -+ parent = dget_parent(dentry); -+ bstart = au_dbstart(parent); -+ for (bindex = au_dbstart(dentry); bindex >= bstart; bindex--) { -+ h_parent = au_h_dptr(parent, bindex); -+ if (!h_parent || !h_parent->d_inode) -+ continue; -+ -+ if (!au_br_rdonly(au_sbr(sb, bindex))) { -+ err = bindex; -+ break; -+ } -+ } -+ dput(parent); -+ -+ /* bottom up here */ -+ if (unlikely(err < 0)) -+ err = au_wbr_bu(sb, bstart - 1); -+ -+ AuDbg("b%d\n", err); -+ return err; -+} -+ -+/* bottom up */ -+int au_wbr_do_copyup_bu(struct dentry *dentry, aufs_bindex_t bstart) -+{ -+ int err; -+ -+ err = au_wbr_bu(dentry->d_sb, bstart); -+ AuDbg("b%d\n", err); -+ if (err > bstart) -+ err = au_wbr_nonopq(dentry, err); -+ -+ AuDbg("b%d\n", err); -+ return err; -+} -+ -+static int au_wbr_copyup_bu(struct dentry *dentry) -+{ -+ int err; -+ aufs_bindex_t bstart; -+ -+ bstart = au_dbstart(dentry); -+ err = au_wbr_do_copyup_bu(dentry, bstart); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct au_wbr_copyup_operations au_wbr_copyup_ops[] = { -+ [AuWbrCopyup_TDP] = { -+ .copyup = au_wbr_copyup_tdp -+ }, -+ [AuWbrCopyup_BUP] = { -+ .copyup = au_wbr_copyup_bup -+ }, -+ [AuWbrCopyup_BU] = { -+ .copyup = au_wbr_copyup_bu -+ } -+}; -+ -+struct au_wbr_create_operations au_wbr_create_ops[] = { -+ [AuWbrCreate_TDP] = { -+ .create = au_wbr_create_tdp -+ }, -+ [AuWbrCreate_RR] = { -+ .create = au_wbr_create_rr, -+ .init = au_wbr_create_init_rr -+ }, -+ [AuWbrCreate_MFS] = { -+ .create = au_wbr_create_mfs, -+ .init = au_wbr_create_init_mfs, -+ .fin = au_wbr_create_fin_mfs -+ }, -+ [AuWbrCreate_MFSV] = { -+ .create = au_wbr_create_mfs, -+ .init = au_wbr_create_init_mfs, -+ .fin = au_wbr_create_fin_mfs -+ }, -+ [AuWbrCreate_MFSRR] = { -+ .create = au_wbr_create_mfsrr, -+ .init = au_wbr_create_init_mfsrr, -+ .fin = au_wbr_create_fin_mfs -+ }, -+ [AuWbrCreate_MFSRRV] = { -+ .create = au_wbr_create_mfsrr, -+ .init = au_wbr_create_init_mfsrr, -+ .fin = au_wbr_create_fin_mfs -+ }, -+ [AuWbrCreate_PMFS] = { -+ .create = au_wbr_create_pmfs, -+ .init = au_wbr_create_init_mfs, -+ .fin = au_wbr_create_fin_mfs -+ }, -+ [AuWbrCreate_PMFSV] = { -+ .create = au_wbr_create_pmfs, -+ .init = au_wbr_create_init_mfs, -+ .fin = au_wbr_create_fin_mfs -+ }, -+ [AuWbrCreate_PMFSRR] = { -+ .create = au_wbr_create_pmfsrr, -+ .init = au_wbr_create_init_mfsrr, -+ .fin = au_wbr_create_fin_mfs -+ }, -+ [AuWbrCreate_PMFSRRV] = { -+ .create = au_wbr_create_pmfsrr, -+ .init = au_wbr_create_init_mfsrr, -+ .fin = au_wbr_create_fin_mfs -+ } -+}; -diff --git a/fs/aufs/whout.c b/fs/aufs/whout.c -new file mode 100644 -index 0000000..fb667ee ---- /dev/null -+++ b/fs/aufs/whout.c -@@ -0,0 +1,1061 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * whiteout for logical deletion and opaque directory -+ */ -+ -+#include "aufs.h" -+ -+#define WH_MASK S_IRUGO -+ -+/* -+ * If a directory contains this file, then it is opaque. We start with the -+ * .wh. flag so that it is blocked by lookup. -+ */ -+static struct qstr diropq_name = QSTR_INIT(AUFS_WH_DIROPQ, -+ sizeof(AUFS_WH_DIROPQ) - 1); -+ -+/* -+ * generate whiteout name, which is NOT terminated by NULL. -+ * @name: original d_name.name -+ * @len: original d_name.len -+ * @wh: whiteout qstr -+ * returns zero when succeeds, otherwise error. -+ * succeeded value as wh->name should be freed by kfree(). -+ */ -+int au_wh_name_alloc(struct qstr *wh, const struct qstr *name) -+{ -+ char *p; -+ -+ if (unlikely(name->len > PATH_MAX - AUFS_WH_PFX_LEN)) -+ return -ENAMETOOLONG; -+ -+ wh->len = name->len + AUFS_WH_PFX_LEN; -+ p = kmalloc(wh->len, GFP_NOFS); -+ wh->name = p; -+ if (p) { -+ memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); -+ memcpy(p + AUFS_WH_PFX_LEN, name->name, name->len); -+ /* smp_mb(); */ -+ return 0; -+ } -+ return -ENOMEM; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * test if the @wh_name exists under @h_parent. -+ * @try_sio specifies the necessary of super-io. -+ */ -+int au_wh_test(struct dentry *h_parent, struct qstr *wh_name, int try_sio) -+{ -+ int err; -+ struct dentry *wh_dentry; -+ -+ if (!try_sio) -+ wh_dentry = vfsub_lkup_one(wh_name, h_parent); -+ else -+ wh_dentry = au_sio_lkup_one(wh_name, h_parent); -+ err = PTR_ERR(wh_dentry); -+ if (IS_ERR(wh_dentry)) { -+ if (err == -ENAMETOOLONG) -+ err = 0; -+ goto out; -+ } -+ -+ err = 0; -+ if (!wh_dentry->d_inode) -+ goto out_wh; /* success */ -+ -+ err = 1; -+ if (S_ISREG(wh_dentry->d_inode->i_mode)) -+ goto out_wh; /* success */ -+ -+ err = -EIO; -+ AuIOErr("%pd Invalid whiteout entry type 0%o.\n", -+ wh_dentry, wh_dentry->d_inode->i_mode); -+ -+out_wh: -+ dput(wh_dentry); -+out: -+ return err; -+} -+ -+/* -+ * test if the @h_dentry sets opaque or not. -+ */ -+int au_diropq_test(struct dentry *h_dentry) -+{ -+ int err; -+ struct inode *h_dir; -+ -+ h_dir = h_dentry->d_inode; -+ err = au_wh_test(h_dentry, &diropq_name, -+ au_test_h_perm_sio(h_dir, MAY_EXEC)); -+ return err; -+} -+ -+/* -+ * returns a negative dentry whose name is unique and temporary. -+ */ -+struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br, -+ struct qstr *prefix) -+{ -+ struct dentry *dentry; -+ int i; -+ char defname[NAME_MAX - AUFS_MAX_NAMELEN + DNAME_INLINE_LEN + 1], -+ *name, *p; -+ /* strict atomic_t is unnecessary here */ -+ static unsigned short cnt; -+ struct qstr qs; -+ -+ BUILD_BUG_ON(sizeof(cnt) * 2 > AUFS_WH_TMP_LEN); -+ -+ name = defname; -+ qs.len = sizeof(defname) - DNAME_INLINE_LEN + prefix->len - 1; -+ if (unlikely(prefix->len > DNAME_INLINE_LEN)) { -+ dentry = ERR_PTR(-ENAMETOOLONG); -+ if (unlikely(qs.len > NAME_MAX)) -+ goto out; -+ dentry = ERR_PTR(-ENOMEM); -+ name = kmalloc(qs.len + 1, GFP_NOFS); -+ if (unlikely(!name)) -+ goto out; -+ } -+ -+ /* doubly whiteout-ed */ -+ memcpy(name, AUFS_WH_PFX AUFS_WH_PFX, AUFS_WH_PFX_LEN * 2); -+ p = name + AUFS_WH_PFX_LEN * 2; -+ memcpy(p, prefix->name, prefix->len); -+ p += prefix->len; -+ *p++ = '.'; -+ AuDebugOn(name + qs.len + 1 - p <= AUFS_WH_TMP_LEN); -+ -+ qs.name = name; -+ for (i = 0; i < 3; i++) { -+ sprintf(p, "%.*x", AUFS_WH_TMP_LEN, cnt++); -+ dentry = au_sio_lkup_one(&qs, h_parent); -+ if (IS_ERR(dentry) || !dentry->d_inode) -+ goto out_name; -+ dput(dentry); -+ } -+ /* pr_warn("could not get random name\n"); */ -+ dentry = ERR_PTR(-EEXIST); -+ AuDbg("%.*s\n", AuLNPair(&qs)); -+ BUG(); -+ -+out_name: -+ if (name != defname) -+ kfree(name); -+out: -+ AuTraceErrPtr(dentry); -+ return dentry; -+} -+ -+/* -+ * rename the @h_dentry on @br to the whiteouted temporary name. -+ */ -+int au_whtmp_ren(struct dentry *h_dentry, struct au_branch *br) -+{ -+ int err; -+ struct path h_path = { -+ .mnt = au_br_mnt(br) -+ }; -+ struct inode *h_dir, *delegated; -+ struct dentry *h_parent; -+ -+ h_parent = h_dentry->d_parent; /* dir inode is locked */ -+ h_dir = h_parent->d_inode; -+ IMustLock(h_dir); -+ -+ h_path.dentry = au_whtmp_lkup(h_parent, br, &h_dentry->d_name); -+ err = PTR_ERR(h_path.dentry); -+ if (IS_ERR(h_path.dentry)) -+ goto out; -+ -+ /* under the same dir, no need to lock_rename() */ -+ delegated = NULL; -+ err = vfsub_rename(h_dir, h_dentry, h_dir, &h_path, &delegated); -+ AuTraceErr(err); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal rename\n"); -+ iput(delegated); -+ } -+ dput(h_path.dentry); -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+/* -+ * functions for removing a whiteout -+ */ -+ -+static int do_unlink_wh(struct inode *h_dir, struct path *h_path) -+{ -+ int err, force; -+ struct inode *delegated; -+ -+ /* -+ * forces superio when the dir has a sticky bit. -+ * this may be a violation of unix fs semantics. -+ */ -+ force = (h_dir->i_mode & S_ISVTX) -+ && !uid_eq(current_fsuid(), h_path->dentry->d_inode->i_uid); -+ delegated = NULL; -+ err = vfsub_unlink(h_dir, h_path, &delegated, force); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal unlink\n"); -+ iput(delegated); -+ } -+ return err; -+} -+ -+int au_wh_unlink_dentry(struct inode *h_dir, struct path *h_path, -+ struct dentry *dentry) -+{ -+ int err; -+ -+ err = do_unlink_wh(h_dir, h_path); -+ if (!err && dentry) -+ au_set_dbwh(dentry, -1); -+ -+ return err; -+} -+ -+static int unlink_wh_name(struct dentry *h_parent, struct qstr *wh, -+ struct au_branch *br) -+{ -+ int err; -+ struct path h_path = { -+ .mnt = au_br_mnt(br) -+ }; -+ -+ err = 0; -+ h_path.dentry = vfsub_lkup_one(wh, h_parent); -+ if (IS_ERR(h_path.dentry)) -+ err = PTR_ERR(h_path.dentry); -+ else { -+ if (h_path.dentry->d_inode -+ && S_ISREG(h_path.dentry->d_inode->i_mode)) -+ err = do_unlink_wh(h_parent->d_inode, &h_path); -+ dput(h_path.dentry); -+ } -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+/* -+ * initialize/clean whiteout for a branch -+ */ -+ -+static void au_wh_clean(struct inode *h_dir, struct path *whpath, -+ const int isdir) -+{ -+ int err; -+ struct inode *delegated; -+ -+ if (!whpath->dentry->d_inode) -+ return; -+ -+ if (isdir) -+ err = vfsub_rmdir(h_dir, whpath); -+ else { -+ delegated = NULL; -+ err = vfsub_unlink(h_dir, whpath, &delegated, /*force*/0); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal unlink\n"); -+ iput(delegated); -+ } -+ } -+ if (unlikely(err)) -+ pr_warn("failed removing %pd (%d), ignored.\n", -+ whpath->dentry, err); -+} -+ -+static int test_linkable(struct dentry *h_root) -+{ -+ struct inode *h_dir = h_root->d_inode; -+ -+ if (h_dir->i_op->link) -+ return 0; -+ -+ pr_err("%pd (%s) doesn't support link(2), use noplink and rw+nolwh\n", -+ h_root, au_sbtype(h_root->d_sb)); -+ return -ENOSYS; -+} -+ -+/* todo: should this mkdir be done in /sbin/mount.aufs helper? */ -+static int au_whdir(struct inode *h_dir, struct path *path) -+{ -+ int err; -+ -+ err = -EEXIST; -+ if (!path->dentry->d_inode) { -+ int mode = S_IRWXU; -+ -+ if (au_test_nfs(path->dentry->d_sb)) -+ mode |= S_IXUGO; -+ err = vfsub_mkdir(h_dir, path, mode); -+ } else if (d_is_dir(path->dentry)) -+ err = 0; -+ else -+ pr_err("unknown %pd exists\n", path->dentry); -+ -+ return err; -+} -+ -+struct au_wh_base { -+ const struct qstr *name; -+ struct dentry *dentry; -+}; -+ -+static void au_wh_init_ro(struct inode *h_dir, struct au_wh_base base[], -+ struct path *h_path) -+{ -+ h_path->dentry = base[AuBrWh_BASE].dentry; -+ au_wh_clean(h_dir, h_path, /*isdir*/0); -+ h_path->dentry = base[AuBrWh_PLINK].dentry; -+ au_wh_clean(h_dir, h_path, /*isdir*/1); -+ h_path->dentry = base[AuBrWh_ORPH].dentry; -+ au_wh_clean(h_dir, h_path, /*isdir*/1); -+} -+ -+/* -+ * returns tri-state, -+ * minus: error, caller should print the message -+ * zero: succuess -+ * plus: error, caller should NOT print the message -+ */ -+static int au_wh_init_rw_nolink(struct dentry *h_root, struct au_wbr *wbr, -+ int do_plink, struct au_wh_base base[], -+ struct path *h_path) -+{ -+ int err; -+ struct inode *h_dir; -+ -+ h_dir = h_root->d_inode; -+ h_path->dentry = base[AuBrWh_BASE].dentry; -+ au_wh_clean(h_dir, h_path, /*isdir*/0); -+ h_path->dentry = base[AuBrWh_PLINK].dentry; -+ if (do_plink) { -+ err = test_linkable(h_root); -+ if (unlikely(err)) { -+ err = 1; -+ goto out; -+ } -+ -+ err = au_whdir(h_dir, h_path); -+ if (unlikely(err)) -+ goto out; -+ wbr->wbr_plink = dget(base[AuBrWh_PLINK].dentry); -+ } else -+ au_wh_clean(h_dir, h_path, /*isdir*/1); -+ h_path->dentry = base[AuBrWh_ORPH].dentry; -+ err = au_whdir(h_dir, h_path); -+ if (unlikely(err)) -+ goto out; -+ wbr->wbr_orph = dget(base[AuBrWh_ORPH].dentry); -+ -+out: -+ return err; -+} -+ -+/* -+ * for the moment, aufs supports the branch filesystem which does not support -+ * link(2). testing on FAT which does not support i_op->setattr() fully either, -+ * copyup failed. finally, such filesystem will not be used as the writable -+ * branch. -+ * -+ * returns tri-state, see above. -+ */ -+static int au_wh_init_rw(struct dentry *h_root, struct au_wbr *wbr, -+ int do_plink, struct au_wh_base base[], -+ struct path *h_path) -+{ -+ int err; -+ struct inode *h_dir; -+ -+ WbrWhMustWriteLock(wbr); -+ -+ err = test_linkable(h_root); -+ if (unlikely(err)) { -+ err = 1; -+ goto out; -+ } -+ -+ /* -+ * todo: should this create be done in /sbin/mount.aufs helper? -+ */ -+ err = -EEXIST; -+ h_dir = h_root->d_inode; -+ if (!base[AuBrWh_BASE].dentry->d_inode) { -+ h_path->dentry = base[AuBrWh_BASE].dentry; -+ err = vfsub_create(h_dir, h_path, WH_MASK, /*want_excl*/true); -+ } else if (S_ISREG(base[AuBrWh_BASE].dentry->d_inode->i_mode)) -+ err = 0; -+ else -+ pr_err("unknown %pd2 exists\n", base[AuBrWh_BASE].dentry); -+ if (unlikely(err)) -+ goto out; -+ -+ h_path->dentry = base[AuBrWh_PLINK].dentry; -+ if (do_plink) { -+ err = au_whdir(h_dir, h_path); -+ if (unlikely(err)) -+ goto out; -+ wbr->wbr_plink = dget(base[AuBrWh_PLINK].dentry); -+ } else -+ au_wh_clean(h_dir, h_path, /*isdir*/1); -+ wbr->wbr_whbase = dget(base[AuBrWh_BASE].dentry); -+ -+ h_path->dentry = base[AuBrWh_ORPH].dentry; -+ err = au_whdir(h_dir, h_path); -+ if (unlikely(err)) -+ goto out; -+ wbr->wbr_orph = dget(base[AuBrWh_ORPH].dentry); -+ -+out: -+ return err; -+} -+ -+/* -+ * initialize the whiteout base file/dir for @br. -+ */ -+int au_wh_init(struct au_branch *br, struct super_block *sb) -+{ -+ int err, i; -+ const unsigned char do_plink -+ = !!au_opt_test(au_mntflags(sb), PLINK); -+ struct inode *h_dir; -+ struct path path = br->br_path; -+ struct dentry *h_root = path.dentry; -+ struct au_wbr *wbr = br->br_wbr; -+ static const struct qstr base_name[] = { -+ [AuBrWh_BASE] = QSTR_INIT(AUFS_BASE_NAME, -+ sizeof(AUFS_BASE_NAME) - 1), -+ [AuBrWh_PLINK] = QSTR_INIT(AUFS_PLINKDIR_NAME, -+ sizeof(AUFS_PLINKDIR_NAME) - 1), -+ [AuBrWh_ORPH] = QSTR_INIT(AUFS_ORPHDIR_NAME, -+ sizeof(AUFS_ORPHDIR_NAME) - 1) -+ }; -+ struct au_wh_base base[] = { -+ [AuBrWh_BASE] = { -+ .name = base_name + AuBrWh_BASE, -+ .dentry = NULL -+ }, -+ [AuBrWh_PLINK] = { -+ .name = base_name + AuBrWh_PLINK, -+ .dentry = NULL -+ }, -+ [AuBrWh_ORPH] = { -+ .name = base_name + AuBrWh_ORPH, -+ .dentry = NULL -+ } -+ }; -+ -+ if (wbr) -+ WbrWhMustWriteLock(wbr); -+ -+ for (i = 0; i < AuBrWh_Last; i++) { -+ /* doubly whiteouted */ -+ struct dentry *d; -+ -+ d = au_wh_lkup(h_root, (void *)base[i].name, br); -+ err = PTR_ERR(d); -+ if (IS_ERR(d)) -+ goto out; -+ -+ base[i].dentry = d; -+ AuDebugOn(wbr -+ && wbr->wbr_wh[i] -+ && wbr->wbr_wh[i] != base[i].dentry); -+ } -+ -+ if (wbr) -+ for (i = 0; i < AuBrWh_Last; i++) { -+ dput(wbr->wbr_wh[i]); -+ wbr->wbr_wh[i] = NULL; -+ } -+ -+ err = 0; -+ if (!au_br_writable(br->br_perm)) { -+ h_dir = h_root->d_inode; -+ au_wh_init_ro(h_dir, base, &path); -+ } else if (!au_br_wh_linkable(br->br_perm)) { -+ err = au_wh_init_rw_nolink(h_root, wbr, do_plink, base, &path); -+ if (err > 0) -+ goto out; -+ else if (err) -+ goto out_err; -+ } else { -+ err = au_wh_init_rw(h_root, wbr, do_plink, base, &path); -+ if (err > 0) -+ goto out; -+ else if (err) -+ goto out_err; -+ } -+ goto out; /* success */ -+ -+out_err: -+ pr_err("an error(%d) on the writable branch %pd(%s)\n", -+ err, h_root, au_sbtype(h_root->d_sb)); -+out: -+ for (i = 0; i < AuBrWh_Last; i++) -+ dput(base[i].dentry); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+/* -+ * whiteouts are all hard-linked usually. -+ * when its link count reaches a ceiling, we create a new whiteout base -+ * asynchronously. -+ */ -+ -+struct reinit_br_wh { -+ struct super_block *sb; -+ struct au_branch *br; -+}; -+ -+static void reinit_br_wh(void *arg) -+{ -+ int err; -+ aufs_bindex_t bindex; -+ struct path h_path; -+ struct reinit_br_wh *a = arg; -+ struct au_wbr *wbr; -+ struct inode *dir, *delegated; -+ struct dentry *h_root; -+ struct au_hinode *hdir; -+ -+ err = 0; -+ wbr = a->br->br_wbr; -+ /* big aufs lock */ -+ si_noflush_write_lock(a->sb); -+ if (!au_br_writable(a->br->br_perm)) -+ goto out; -+ bindex = au_br_index(a->sb, a->br->br_id); -+ if (unlikely(bindex < 0)) -+ goto out; -+ -+ di_read_lock_parent(a->sb->s_root, AuLock_IR); -+ dir = a->sb->s_root->d_inode; -+ hdir = au_hi(dir, bindex); -+ h_root = au_h_dptr(a->sb->s_root, bindex); -+ AuDebugOn(h_root != au_br_dentry(a->br)); -+ -+ au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); -+ wbr_wh_write_lock(wbr); -+ err = au_h_verify(wbr->wbr_whbase, au_opt_udba(a->sb), hdir->hi_inode, -+ h_root, a->br); -+ if (!err) { -+ h_path.dentry = wbr->wbr_whbase; -+ h_path.mnt = au_br_mnt(a->br); -+ delegated = NULL; -+ err = vfsub_unlink(hdir->hi_inode, &h_path, &delegated, -+ /*force*/0); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal unlink\n"); -+ iput(delegated); -+ } -+ } else { -+ pr_warn("%pd is moved, ignored\n", wbr->wbr_whbase); -+ err = 0; -+ } -+ dput(wbr->wbr_whbase); -+ wbr->wbr_whbase = NULL; -+ if (!err) -+ err = au_wh_init(a->br, a->sb); -+ wbr_wh_write_unlock(wbr); -+ au_hn_imtx_unlock(hdir); -+ di_read_unlock(a->sb->s_root, AuLock_IR); -+ if (!err) -+ au_fhsm_wrote(a->sb, bindex, /*force*/0); -+ -+out: -+ if (wbr) -+ atomic_dec(&wbr->wbr_wh_running); -+ atomic_dec(&a->br->br_count); -+ si_write_unlock(a->sb); -+ au_nwt_done(&au_sbi(a->sb)->si_nowait); -+ kfree(arg); -+ if (unlikely(err)) -+ AuIOErr("err %d\n", err); -+} -+ -+static void kick_reinit_br_wh(struct super_block *sb, struct au_branch *br) -+{ -+ int do_dec, wkq_err; -+ struct reinit_br_wh *arg; -+ -+ do_dec = 1; -+ if (atomic_inc_return(&br->br_wbr->wbr_wh_running) != 1) -+ goto out; -+ -+ /* ignore ENOMEM */ -+ arg = kmalloc(sizeof(*arg), GFP_NOFS); -+ if (arg) { -+ /* -+ * dec(wh_running), kfree(arg) and dec(br_count) -+ * in reinit function -+ */ -+ arg->sb = sb; -+ arg->br = br; -+ atomic_inc(&br->br_count); -+ wkq_err = au_wkq_nowait(reinit_br_wh, arg, sb, /*flags*/0); -+ if (unlikely(wkq_err)) { -+ atomic_dec(&br->br_wbr->wbr_wh_running); -+ atomic_dec(&br->br_count); -+ kfree(arg); -+ } -+ do_dec = 0; -+ } -+ -+out: -+ if (do_dec) -+ atomic_dec(&br->br_wbr->wbr_wh_running); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * create the whiteout @wh. -+ */ -+static int link_or_create_wh(struct super_block *sb, aufs_bindex_t bindex, -+ struct dentry *wh) -+{ -+ int err; -+ struct path h_path = { -+ .dentry = wh -+ }; -+ struct au_branch *br; -+ struct au_wbr *wbr; -+ struct dentry *h_parent; -+ struct inode *h_dir, *delegated; -+ -+ h_parent = wh->d_parent; /* dir inode is locked */ -+ h_dir = h_parent->d_inode; -+ IMustLock(h_dir); -+ -+ br = au_sbr(sb, bindex); -+ h_path.mnt = au_br_mnt(br); -+ wbr = br->br_wbr; -+ wbr_wh_read_lock(wbr); -+ if (wbr->wbr_whbase) { -+ delegated = NULL; -+ err = vfsub_link(wbr->wbr_whbase, h_dir, &h_path, &delegated); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal link\n"); -+ iput(delegated); -+ } -+ if (!err || err != -EMLINK) -+ goto out; -+ -+ /* link count full. re-initialize br_whbase. */ -+ kick_reinit_br_wh(sb, br); -+ } -+ -+ /* return this error in this context */ -+ err = vfsub_create(h_dir, &h_path, WH_MASK, /*want_excl*/true); -+ if (!err) -+ au_fhsm_wrote(sb, bindex, /*force*/0); -+ -+out: -+ wbr_wh_read_unlock(wbr); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * create or remove the diropq. -+ */ -+static struct dentry *do_diropq(struct dentry *dentry, aufs_bindex_t bindex, -+ unsigned int flags) -+{ -+ struct dentry *opq_dentry, *h_dentry; -+ struct super_block *sb; -+ struct au_branch *br; -+ int err; -+ -+ sb = dentry->d_sb; -+ br = au_sbr(sb, bindex); -+ h_dentry = au_h_dptr(dentry, bindex); -+ opq_dentry = vfsub_lkup_one(&diropq_name, h_dentry); -+ if (IS_ERR(opq_dentry)) -+ goto out; -+ -+ if (au_ftest_diropq(flags, CREATE)) { -+ err = link_or_create_wh(sb, bindex, opq_dentry); -+ if (!err) { -+ au_set_dbdiropq(dentry, bindex); -+ goto out; /* success */ -+ } -+ } else { -+ struct path tmp = { -+ .dentry = opq_dentry, -+ .mnt = au_br_mnt(br) -+ }; -+ err = do_unlink_wh(au_h_iptr(dentry->d_inode, bindex), &tmp); -+ if (!err) -+ au_set_dbdiropq(dentry, -1); -+ } -+ dput(opq_dentry); -+ opq_dentry = ERR_PTR(err); -+ -+out: -+ return opq_dentry; -+} -+ -+struct do_diropq_args { -+ struct dentry **errp; -+ struct dentry *dentry; -+ aufs_bindex_t bindex; -+ unsigned int flags; -+}; -+ -+static void call_do_diropq(void *args) -+{ -+ struct do_diropq_args *a = args; -+ *a->errp = do_diropq(a->dentry, a->bindex, a->flags); -+} -+ -+struct dentry *au_diropq_sio(struct dentry *dentry, aufs_bindex_t bindex, -+ unsigned int flags) -+{ -+ struct dentry *diropq, *h_dentry; -+ -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (!au_test_h_perm_sio(h_dentry->d_inode, MAY_EXEC | MAY_WRITE)) -+ diropq = do_diropq(dentry, bindex, flags); -+ else { -+ int wkq_err; -+ struct do_diropq_args args = { -+ .errp = &diropq, -+ .dentry = dentry, -+ .bindex = bindex, -+ .flags = flags -+ }; -+ -+ wkq_err = au_wkq_wait(call_do_diropq, &args); -+ if (unlikely(wkq_err)) -+ diropq = ERR_PTR(wkq_err); -+ } -+ -+ return diropq; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * lookup whiteout dentry. -+ * @h_parent: lower parent dentry which must exist and be locked -+ * @base_name: name of dentry which will be whiteouted -+ * returns dentry for whiteout. -+ */ -+struct dentry *au_wh_lkup(struct dentry *h_parent, struct qstr *base_name, -+ struct au_branch *br) -+{ -+ int err; -+ struct qstr wh_name; -+ struct dentry *wh_dentry; -+ -+ err = au_wh_name_alloc(&wh_name, base_name); -+ wh_dentry = ERR_PTR(err); -+ if (!err) { -+ wh_dentry = vfsub_lkup_one(&wh_name, h_parent); -+ kfree(wh_name.name); -+ } -+ return wh_dentry; -+} -+ -+/* -+ * link/create a whiteout for @dentry on @bindex. -+ */ -+struct dentry *au_wh_create(struct dentry *dentry, aufs_bindex_t bindex, -+ struct dentry *h_parent) -+{ -+ struct dentry *wh_dentry; -+ struct super_block *sb; -+ int err; -+ -+ sb = dentry->d_sb; -+ wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, au_sbr(sb, bindex)); -+ if (!IS_ERR(wh_dentry) && !wh_dentry->d_inode) { -+ err = link_or_create_wh(sb, bindex, wh_dentry); -+ if (!err) { -+ au_set_dbwh(dentry, bindex); -+ au_fhsm_wrote(sb, bindex, /*force*/0); -+ } else { -+ dput(wh_dentry); -+ wh_dentry = ERR_PTR(err); -+ } -+ } -+ -+ return wh_dentry; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* Delete all whiteouts in this directory on branch bindex. */ -+static int del_wh_children(struct dentry *h_dentry, struct au_nhash *whlist, -+ aufs_bindex_t bindex, struct au_branch *br) -+{ -+ int err; -+ unsigned long ul, n; -+ struct qstr wh_name; -+ char *p; -+ struct hlist_head *head; -+ struct au_vdir_wh *pos; -+ struct au_vdir_destr *str; -+ -+ err = -ENOMEM; -+ p = (void *)__get_free_page(GFP_NOFS); -+ wh_name.name = p; -+ if (unlikely(!wh_name.name)) -+ goto out; -+ -+ err = 0; -+ memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); -+ p += AUFS_WH_PFX_LEN; -+ n = whlist->nh_num; -+ head = whlist->nh_head; -+ for (ul = 0; !err && ul < n; ul++, head++) { -+ hlist_for_each_entry(pos, head, wh_hash) { -+ if (pos->wh_bindex != bindex) -+ continue; -+ -+ str = &pos->wh_str; -+ if (str->len + AUFS_WH_PFX_LEN <= PATH_MAX) { -+ memcpy(p, str->name, str->len); -+ wh_name.len = AUFS_WH_PFX_LEN + str->len; -+ err = unlink_wh_name(h_dentry, &wh_name, br); -+ if (!err) -+ continue; -+ break; -+ } -+ AuIOErr("whiteout name too long %.*s\n", -+ str->len, str->name); -+ err = -EIO; -+ break; -+ } -+ } -+ free_page((unsigned long)wh_name.name); -+ -+out: -+ return err; -+} -+ -+struct del_wh_children_args { -+ int *errp; -+ struct dentry *h_dentry; -+ struct au_nhash *whlist; -+ aufs_bindex_t bindex; -+ struct au_branch *br; -+}; -+ -+static void call_del_wh_children(void *args) -+{ -+ struct del_wh_children_args *a = args; -+ *a->errp = del_wh_children(a->h_dentry, a->whlist, a->bindex, a->br); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct au_whtmp_rmdir *au_whtmp_rmdir_alloc(struct super_block *sb, gfp_t gfp) -+{ -+ struct au_whtmp_rmdir *whtmp; -+ int err; -+ unsigned int rdhash; -+ -+ SiMustAnyLock(sb); -+ -+ whtmp = kzalloc(sizeof(*whtmp), gfp); -+ if (unlikely(!whtmp)) { -+ whtmp = ERR_PTR(-ENOMEM); -+ goto out; -+ } -+ -+ /* no estimation for dir size */ -+ rdhash = au_sbi(sb)->si_rdhash; -+ if (!rdhash) -+ rdhash = AUFS_RDHASH_DEF; -+ err = au_nhash_alloc(&whtmp->whlist, rdhash, gfp); -+ if (unlikely(err)) { -+ kfree(whtmp); -+ whtmp = ERR_PTR(err); -+ } -+ -+out: -+ return whtmp; -+} -+ -+void au_whtmp_rmdir_free(struct au_whtmp_rmdir *whtmp) -+{ -+ if (whtmp->br) -+ atomic_dec(&whtmp->br->br_count); -+ dput(whtmp->wh_dentry); -+ iput(whtmp->dir); -+ au_nhash_wh_free(&whtmp->whlist); -+ kfree(whtmp); -+} -+ -+/* -+ * rmdir the whiteouted temporary named dir @h_dentry. -+ * @whlist: whiteouted children. -+ */ -+int au_whtmp_rmdir(struct inode *dir, aufs_bindex_t bindex, -+ struct dentry *wh_dentry, struct au_nhash *whlist) -+{ -+ int err; -+ unsigned int h_nlink; -+ struct path h_tmp; -+ struct inode *wh_inode, *h_dir; -+ struct au_branch *br; -+ -+ h_dir = wh_dentry->d_parent->d_inode; /* dir inode is locked */ -+ IMustLock(h_dir); -+ -+ br = au_sbr(dir->i_sb, bindex); -+ wh_inode = wh_dentry->d_inode; -+ mutex_lock_nested(&wh_inode->i_mutex, AuLsc_I_CHILD); -+ -+ /* -+ * someone else might change some whiteouts while we were sleeping. -+ * it means this whlist may have an obsoleted entry. -+ */ -+ if (!au_test_h_perm_sio(wh_inode, MAY_EXEC | MAY_WRITE)) -+ err = del_wh_children(wh_dentry, whlist, bindex, br); -+ else { -+ int wkq_err; -+ struct del_wh_children_args args = { -+ .errp = &err, -+ .h_dentry = wh_dentry, -+ .whlist = whlist, -+ .bindex = bindex, -+ .br = br -+ }; -+ -+ wkq_err = au_wkq_wait(call_del_wh_children, &args); -+ if (unlikely(wkq_err)) -+ err = wkq_err; -+ } -+ mutex_unlock(&wh_inode->i_mutex); -+ -+ if (!err) { -+ h_tmp.dentry = wh_dentry; -+ h_tmp.mnt = au_br_mnt(br); -+ h_nlink = h_dir->i_nlink; -+ err = vfsub_rmdir(h_dir, &h_tmp); -+ /* some fs doesn't change the parent nlink in some cases */ -+ h_nlink -= h_dir->i_nlink; -+ } -+ -+ if (!err) { -+ if (au_ibstart(dir) == bindex) { -+ /* todo: dir->i_mutex is necessary */ -+ au_cpup_attr_timesizes(dir); -+ if (h_nlink) -+ vfsub_drop_nlink(dir); -+ } -+ return 0; /* success */ -+ } -+ -+ pr_warn("failed removing %pd(%d), ignored\n", wh_dentry, err); -+ return err; -+} -+ -+static void call_rmdir_whtmp(void *args) -+{ -+ int err; -+ aufs_bindex_t bindex; -+ struct au_whtmp_rmdir *a = args; -+ struct super_block *sb; -+ struct dentry *h_parent; -+ struct inode *h_dir; -+ struct au_hinode *hdir; -+ -+ /* rmdir by nfsd may cause deadlock with this i_mutex */ -+ /* mutex_lock(&a->dir->i_mutex); */ -+ err = -EROFS; -+ sb = a->dir->i_sb; -+ si_read_lock(sb, !AuLock_FLUSH); -+ if (!au_br_writable(a->br->br_perm)) -+ goto out; -+ bindex = au_br_index(sb, a->br->br_id); -+ if (unlikely(bindex < 0)) -+ goto out; -+ -+ err = -EIO; -+ ii_write_lock_parent(a->dir); -+ h_parent = dget_parent(a->wh_dentry); -+ h_dir = h_parent->d_inode; -+ hdir = au_hi(a->dir, bindex); -+ err = vfsub_mnt_want_write(au_br_mnt(a->br)); -+ if (unlikely(err)) -+ goto out_mnt; -+ au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); -+ err = au_h_verify(a->wh_dentry, au_opt_udba(sb), h_dir, h_parent, -+ a->br); -+ if (!err) -+ err = au_whtmp_rmdir(a->dir, bindex, a->wh_dentry, &a->whlist); -+ au_hn_imtx_unlock(hdir); -+ vfsub_mnt_drop_write(au_br_mnt(a->br)); -+ -+out_mnt: -+ dput(h_parent); -+ ii_write_unlock(a->dir); -+out: -+ /* mutex_unlock(&a->dir->i_mutex); */ -+ au_whtmp_rmdir_free(a); -+ si_read_unlock(sb); -+ au_nwt_done(&au_sbi(sb)->si_nowait); -+ if (unlikely(err)) -+ AuIOErr("err %d\n", err); -+} -+ -+void au_whtmp_kick_rmdir(struct inode *dir, aufs_bindex_t bindex, -+ struct dentry *wh_dentry, struct au_whtmp_rmdir *args) -+{ -+ int wkq_err; -+ struct super_block *sb; -+ -+ IMustLock(dir); -+ -+ /* all post-process will be done in do_rmdir_whtmp(). */ -+ sb = dir->i_sb; -+ args->dir = au_igrab(dir); -+ args->br = au_sbr(sb, bindex); -+ atomic_inc(&args->br->br_count); -+ args->wh_dentry = dget(wh_dentry); -+ wkq_err = au_wkq_nowait(call_rmdir_whtmp, args, sb, /*flags*/0); -+ if (unlikely(wkq_err)) { -+ pr_warn("rmdir error %pd (%d), ignored\n", wh_dentry, wkq_err); -+ au_whtmp_rmdir_free(args); -+ } -+} -diff --git a/fs/aufs/whout.h b/fs/aufs/whout.h -new file mode 100644 -index 0000000..5a5c378 ---- /dev/null -+++ b/fs/aufs/whout.h -@@ -0,0 +1,85 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * whiteout for logical deletion and opaque directory -+ */ -+ -+#ifndef __AUFS_WHOUT_H__ -+#define __AUFS_WHOUT_H__ -+ -+#ifdef __KERNEL__ -+ -+#include "dir.h" -+ -+/* whout.c */ -+int au_wh_name_alloc(struct qstr *wh, const struct qstr *name); -+int au_wh_test(struct dentry *h_parent, struct qstr *wh_name, int try_sio); -+int au_diropq_test(struct dentry *h_dentry); -+struct au_branch; -+struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br, -+ struct qstr *prefix); -+int au_whtmp_ren(struct dentry *h_dentry, struct au_branch *br); -+int au_wh_unlink_dentry(struct inode *h_dir, struct path *h_path, -+ struct dentry *dentry); -+int au_wh_init(struct au_branch *br, struct super_block *sb); -+ -+/* diropq flags */ -+#define AuDiropq_CREATE 1 -+#define au_ftest_diropq(flags, name) ((flags) & AuDiropq_##name) -+#define au_fset_diropq(flags, name) \ -+ do { (flags) |= AuDiropq_##name; } while (0) -+#define au_fclr_diropq(flags, name) \ -+ do { (flags) &= ~AuDiropq_##name; } while (0) -+ -+struct dentry *au_diropq_sio(struct dentry *dentry, aufs_bindex_t bindex, -+ unsigned int flags); -+struct dentry *au_wh_lkup(struct dentry *h_parent, struct qstr *base_name, -+ struct au_branch *br); -+struct dentry *au_wh_create(struct dentry *dentry, aufs_bindex_t bindex, -+ struct dentry *h_parent); -+ -+/* real rmdir for the whiteout-ed dir */ -+struct au_whtmp_rmdir { -+ struct inode *dir; -+ struct au_branch *br; -+ struct dentry *wh_dentry; -+ struct au_nhash whlist; -+}; -+ -+struct au_whtmp_rmdir *au_whtmp_rmdir_alloc(struct super_block *sb, gfp_t gfp); -+void au_whtmp_rmdir_free(struct au_whtmp_rmdir *whtmp); -+int au_whtmp_rmdir(struct inode *dir, aufs_bindex_t bindex, -+ struct dentry *wh_dentry, struct au_nhash *whlist); -+void au_whtmp_kick_rmdir(struct inode *dir, aufs_bindex_t bindex, -+ struct dentry *wh_dentry, struct au_whtmp_rmdir *args); -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline struct dentry *au_diropq_create(struct dentry *dentry, -+ aufs_bindex_t bindex) -+{ -+ return au_diropq_sio(dentry, bindex, AuDiropq_CREATE); -+} -+ -+static inline int au_diropq_remove(struct dentry *dentry, aufs_bindex_t bindex) -+{ -+ return PTR_ERR(au_diropq_sio(dentry, bindex, !AuDiropq_CREATE)); -+} -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_WHOUT_H__ */ -diff --git a/fs/aufs/wkq.c b/fs/aufs/wkq.c -new file mode 100644 -index 0000000..a4e1b92 ---- /dev/null -+++ b/fs/aufs/wkq.c -@@ -0,0 +1,213 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * workqueue for asynchronous/super-io operations -+ * todo: try new dredential scheme -+ */ -+ -+#include -+#include "aufs.h" -+ -+/* internal workqueue named AUFS_WKQ_NAME */ -+ -+static struct workqueue_struct *au_wkq; -+ -+struct au_wkinfo { -+ struct work_struct wk; -+ struct kobject *kobj; -+ -+ unsigned int flags; /* see wkq.h */ -+ -+ au_wkq_func_t func; -+ void *args; -+ -+ struct completion *comp; -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void wkq_func(struct work_struct *wk) -+{ -+ struct au_wkinfo *wkinfo = container_of(wk, struct au_wkinfo, wk); -+ -+ AuDebugOn(!uid_eq(current_fsuid(), GLOBAL_ROOT_UID)); -+ AuDebugOn(rlimit(RLIMIT_FSIZE) != RLIM_INFINITY); -+ -+ wkinfo->func(wkinfo->args); -+ if (au_ftest_wkq(wkinfo->flags, WAIT)) -+ complete(wkinfo->comp); -+ else { -+ kobject_put(wkinfo->kobj); -+ module_put(THIS_MODULE); /* todo: ?? */ -+ kfree(wkinfo); -+ } -+} -+ -+/* -+ * Since struct completion is large, try allocating it dynamically. -+ */ -+#if 1 /* defined(CONFIG_4KSTACKS) || defined(AuTest4KSTACKS) */ -+#define AuWkqCompDeclare(name) struct completion *comp = NULL -+ -+static int au_wkq_comp_alloc(struct au_wkinfo *wkinfo, struct completion **comp) -+{ -+ *comp = kmalloc(sizeof(**comp), GFP_NOFS); -+ if (*comp) { -+ init_completion(*comp); -+ wkinfo->comp = *comp; -+ return 0; -+ } -+ return -ENOMEM; -+} -+ -+static void au_wkq_comp_free(struct completion *comp) -+{ -+ kfree(comp); -+} -+ -+#else -+ -+/* no braces */ -+#define AuWkqCompDeclare(name) \ -+ DECLARE_COMPLETION_ONSTACK(_ ## name); \ -+ struct completion *comp = &_ ## name -+ -+static int au_wkq_comp_alloc(struct au_wkinfo *wkinfo, struct completion **comp) -+{ -+ wkinfo->comp = *comp; -+ return 0; -+} -+ -+static void au_wkq_comp_free(struct completion *comp __maybe_unused) -+{ -+ /* empty */ -+} -+#endif /* 4KSTACKS */ -+ -+static void au_wkq_run(struct au_wkinfo *wkinfo) -+{ -+ if (au_ftest_wkq(wkinfo->flags, NEST)) { -+ if (au_wkq_test()) { -+ AuWarn1("wkq from wkq, unless silly-rename on NFS," -+ " due to a dead dir by UDBA?\n"); -+ AuDebugOn(au_ftest_wkq(wkinfo->flags, WAIT)); -+ } -+ } else -+ au_dbg_verify_kthread(); -+ -+ if (au_ftest_wkq(wkinfo->flags, WAIT)) { -+ INIT_WORK_ONSTACK(&wkinfo->wk, wkq_func); -+ queue_work(au_wkq, &wkinfo->wk); -+ } else { -+ INIT_WORK(&wkinfo->wk, wkq_func); -+ schedule_work(&wkinfo->wk); -+ } -+} -+ -+/* -+ * Be careful. It is easy to make deadlock happen. -+ * processA: lock, wkq and wait -+ * processB: wkq and wait, lock in wkq -+ * --> deadlock -+ */ -+int au_wkq_do_wait(unsigned int flags, au_wkq_func_t func, void *args) -+{ -+ int err; -+ AuWkqCompDeclare(comp); -+ struct au_wkinfo wkinfo = { -+ .flags = flags, -+ .func = func, -+ .args = args -+ }; -+ -+ err = au_wkq_comp_alloc(&wkinfo, &comp); -+ if (!err) { -+ au_wkq_run(&wkinfo); -+ /* no timeout, no interrupt */ -+ wait_for_completion(wkinfo.comp); -+ au_wkq_comp_free(comp); -+ destroy_work_on_stack(&wkinfo.wk); -+ } -+ -+ return err; -+ -+} -+ -+/* -+ * Note: dget/dput() in func for aufs dentries are not supported. It will be a -+ * problem in a concurrent umounting. -+ */ -+int au_wkq_nowait(au_wkq_func_t func, void *args, struct super_block *sb, -+ unsigned int flags) -+{ -+ int err; -+ struct au_wkinfo *wkinfo; -+ -+ atomic_inc(&au_sbi(sb)->si_nowait.nw_len); -+ -+ /* -+ * wkq_func() must free this wkinfo. -+ * it highly depends upon the implementation of workqueue. -+ */ -+ err = 0; -+ wkinfo = kmalloc(sizeof(*wkinfo), GFP_NOFS); -+ if (wkinfo) { -+ wkinfo->kobj = &au_sbi(sb)->si_kobj; -+ wkinfo->flags = flags & ~AuWkq_WAIT; -+ wkinfo->func = func; -+ wkinfo->args = args; -+ wkinfo->comp = NULL; -+ kobject_get(wkinfo->kobj); -+ __module_get(THIS_MODULE); /* todo: ?? */ -+ -+ au_wkq_run(wkinfo); -+ } else { -+ err = -ENOMEM; -+ au_nwt_done(&au_sbi(sb)->si_nowait); -+ } -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void au_nwt_init(struct au_nowait_tasks *nwt) -+{ -+ atomic_set(&nwt->nw_len, 0); -+ /* smp_mb(); */ /* atomic_set */ -+ init_waitqueue_head(&nwt->nw_wq); -+} -+ -+void au_wkq_fin(void) -+{ -+ destroy_workqueue(au_wkq); -+} -+ -+int __init au_wkq_init(void) -+{ -+ int err; -+ -+ err = 0; -+ au_wkq = alloc_workqueue(AUFS_WKQ_NAME, 0, WQ_DFL_ACTIVE); -+ if (IS_ERR(au_wkq)) -+ err = PTR_ERR(au_wkq); -+ else if (!au_wkq) -+ err = -ENOMEM; -+ -+ return err; -+} -diff --git a/fs/aufs/wkq.h b/fs/aufs/wkq.h -new file mode 100644 -index 0000000..830123c ---- /dev/null -+++ b/fs/aufs/wkq.h -@@ -0,0 +1,91 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * workqueue for asynchronous/super-io operations -+ * todo: try new credentials management scheme -+ */ -+ -+#ifndef __AUFS_WKQ_H__ -+#define __AUFS_WKQ_H__ -+ -+#ifdef __KERNEL__ -+ -+struct super_block; -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * in the next operation, wait for the 'nowait' tasks in system-wide workqueue -+ */ -+struct au_nowait_tasks { -+ atomic_t nw_len; -+ wait_queue_head_t nw_wq; -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+typedef void (*au_wkq_func_t)(void *args); -+ -+/* wkq flags */ -+#define AuWkq_WAIT 1 -+#define AuWkq_NEST (1 << 1) -+#define au_ftest_wkq(flags, name) ((flags) & AuWkq_##name) -+#define au_fset_wkq(flags, name) \ -+ do { (flags) |= AuWkq_##name; } while (0) -+#define au_fclr_wkq(flags, name) \ -+ do { (flags) &= ~AuWkq_##name; } while (0) -+ -+#ifndef CONFIG_AUFS_HNOTIFY -+#undef AuWkq_NEST -+#define AuWkq_NEST 0 -+#endif -+ -+/* wkq.c */ -+int au_wkq_do_wait(unsigned int flags, au_wkq_func_t func, void *args); -+int au_wkq_nowait(au_wkq_func_t func, void *args, struct super_block *sb, -+ unsigned int flags); -+void au_nwt_init(struct au_nowait_tasks *nwt); -+int __init au_wkq_init(void); -+void au_wkq_fin(void); -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline int au_wkq_test(void) -+{ -+ return current->flags & PF_WQ_WORKER; -+} -+ -+static inline int au_wkq_wait(au_wkq_func_t func, void *args) -+{ -+ return au_wkq_do_wait(AuWkq_WAIT, func, args); -+} -+ -+static inline void au_nwt_done(struct au_nowait_tasks *nwt) -+{ -+ if (atomic_dec_and_test(&nwt->nw_len)) -+ wake_up_all(&nwt->nw_wq); -+} -+ -+static inline int au_nwt_flush(struct au_nowait_tasks *nwt) -+{ -+ wait_event(nwt->nw_wq, !atomic_read(&nwt->nw_len)); -+ return 0; -+} -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_WKQ_H__ */ -diff --git a/fs/aufs/xattr.c b/fs/aufs/xattr.c -new file mode 100644 -index 0000000..e16beea ---- /dev/null -+++ b/fs/aufs/xattr.c -@@ -0,0 +1,344 @@ -+/* -+ * Copyright (C) 2014-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * handling xattr functions -+ */ -+ -+#include -+#include "aufs.h" -+ -+static int au_xattr_ignore(int err, char *name, unsigned int ignore_flags) -+{ -+ if (!ignore_flags) -+ goto out; -+ switch (err) { -+ case -ENOMEM: -+ case -EDQUOT: -+ goto out; -+ } -+ -+ if ((ignore_flags & AuBrAttr_ICEX) == AuBrAttr_ICEX) { -+ err = 0; -+ goto out; -+ } -+ -+#define cmp(brattr, prefix) do { \ -+ if (!strncmp(name, XATTR_##prefix##_PREFIX, \ -+ XATTR_##prefix##_PREFIX_LEN)) { \ -+ if (ignore_flags & AuBrAttr_ICEX_##brattr) \ -+ err = 0; \ -+ goto out; \ -+ } \ -+ } while (0) -+ -+ cmp(SEC, SECURITY); -+ cmp(SYS, SYSTEM); -+ cmp(TR, TRUSTED); -+ cmp(USR, USER); -+#undef cmp -+ -+ if (ignore_flags & AuBrAttr_ICEX_OTH) -+ err = 0; -+ -+out: -+ return err; -+} -+ -+static const int au_xattr_out_of_list = AuBrAttr_ICEX_OTH << 1; -+ -+static int au_do_cpup_xattr(struct dentry *h_dst, struct dentry *h_src, -+ char *name, char **buf, unsigned int ignore_flags, -+ unsigned int verbose) -+{ -+ int err; -+ ssize_t ssz; -+ struct inode *h_idst; -+ -+ ssz = vfs_getxattr_alloc(h_src, name, buf, 0, GFP_NOFS); -+ err = ssz; -+ if (unlikely(err <= 0)) { -+ if (err == -ENODATA -+ || (err == -EOPNOTSUPP -+ && ((ignore_flags & au_xattr_out_of_list) -+ || (au_test_nfs_noacl(h_src->d_inode) -+ && (!strcmp(name, XATTR_NAME_POSIX_ACL_ACCESS) -+ || !strcmp(name, -+ XATTR_NAME_POSIX_ACL_DEFAULT)))) -+ )) -+ err = 0; -+ if (err && (verbose || au_debug_test())) -+ pr_err("%s, err %d\n", name, err); -+ goto out; -+ } -+ -+ /* unlock it temporary */ -+ h_idst = h_dst->d_inode; -+ mutex_unlock(&h_idst->i_mutex); -+ err = vfsub_setxattr(h_dst, name, *buf, ssz, /*flags*/0); -+ mutex_lock_nested(&h_idst->i_mutex, AuLsc_I_CHILD2); -+ if (unlikely(err)) { -+ if (verbose || au_debug_test()) -+ pr_err("%s, err %d\n", name, err); -+ err = au_xattr_ignore(err, name, ignore_flags); -+ } -+ -+out: -+ return err; -+} -+ -+int au_cpup_xattr(struct dentry *h_dst, struct dentry *h_src, int ignore_flags, -+ unsigned int verbose) -+{ -+ int err, unlocked, acl_access, acl_default; -+ ssize_t ssz; -+ struct inode *h_isrc, *h_idst; -+ char *value, *p, *o, *e; -+ -+ /* try stopping to update the source inode while we are referencing */ -+ /* there should not be the parent-child relationship between them */ -+ h_isrc = h_src->d_inode; -+ h_idst = h_dst->d_inode; -+ mutex_unlock(&h_idst->i_mutex); -+ mutex_lock_nested(&h_isrc->i_mutex, AuLsc_I_CHILD); -+ mutex_lock_nested(&h_idst->i_mutex, AuLsc_I_CHILD2); -+ unlocked = 0; -+ -+ /* some filesystems don't list POSIX ACL, for example tmpfs */ -+ ssz = vfs_listxattr(h_src, NULL, 0); -+ err = ssz; -+ if (unlikely(err < 0)) { -+ AuTraceErr(err); -+ if (err == -ENODATA -+ || err == -EOPNOTSUPP) -+ err = 0; /* ignore */ -+ goto out; -+ } -+ -+ err = 0; -+ p = NULL; -+ o = NULL; -+ if (ssz) { -+ err = -ENOMEM; -+ p = kmalloc(ssz, GFP_NOFS); -+ o = p; -+ if (unlikely(!p)) -+ goto out; -+ err = vfs_listxattr(h_src, p, ssz); -+ } -+ mutex_unlock(&h_isrc->i_mutex); -+ unlocked = 1; -+ AuDbg("err %d, ssz %zd\n", err, ssz); -+ if (unlikely(err < 0)) -+ goto out_free; -+ -+ err = 0; -+ e = p + ssz; -+ value = NULL; -+ acl_access = 0; -+ acl_default = 0; -+ while (!err && p < e) { -+ acl_access |= !strncmp(p, XATTR_NAME_POSIX_ACL_ACCESS, -+ sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1); -+ acl_default |= !strncmp(p, XATTR_NAME_POSIX_ACL_DEFAULT, -+ sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) -+ - 1); -+ err = au_do_cpup_xattr(h_dst, h_src, p, &value, ignore_flags, -+ verbose); -+ p += strlen(p) + 1; -+ } -+ AuTraceErr(err); -+ ignore_flags |= au_xattr_out_of_list; -+ if (!err && !acl_access) { -+ err = au_do_cpup_xattr(h_dst, h_src, -+ XATTR_NAME_POSIX_ACL_ACCESS, &value, -+ ignore_flags, verbose); -+ AuTraceErr(err); -+ } -+ if (!err && !acl_default) { -+ err = au_do_cpup_xattr(h_dst, h_src, -+ XATTR_NAME_POSIX_ACL_DEFAULT, &value, -+ ignore_flags, verbose); -+ AuTraceErr(err); -+ } -+ -+ kfree(value); -+ -+out_free: -+ kfree(o); -+out: -+ if (!unlocked) -+ mutex_unlock(&h_isrc->i_mutex); -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+enum { -+ AU_XATTR_LIST, -+ AU_XATTR_GET -+}; -+ -+struct au_lgxattr { -+ int type; -+ union { -+ struct { -+ char *list; -+ size_t size; -+ } list; -+ struct { -+ const char *name; -+ void *value; -+ size_t size; -+ } get; -+ } u; -+}; -+ -+static ssize_t au_lgxattr(struct dentry *dentry, struct au_lgxattr *arg) -+{ -+ ssize_t err; -+ struct path h_path; -+ struct super_block *sb; -+ -+ sb = dentry->d_sb; -+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ if (unlikely(err)) -+ goto out; -+ err = au_h_path_getattr(dentry, /*force*/1, &h_path); -+ if (unlikely(err)) -+ goto out_si; -+ if (unlikely(!h_path.dentry)) -+ /* illegally overlapped or something */ -+ goto out_di; /* pretending success */ -+ -+ /* always topmost entry only */ -+ switch (arg->type) { -+ case AU_XATTR_LIST: -+ err = vfs_listxattr(h_path.dentry, -+ arg->u.list.list, arg->u.list.size); -+ break; -+ case AU_XATTR_GET: -+ err = vfs_getxattr(h_path.dentry, -+ arg->u.get.name, arg->u.get.value, -+ arg->u.get.size); -+ break; -+ } -+ -+out_di: -+ di_read_unlock(dentry, AuLock_IR); -+out_si: -+ si_read_unlock(sb); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+ssize_t aufs_listxattr(struct dentry *dentry, char *list, size_t size) -+{ -+ struct au_lgxattr arg = { -+ .type = AU_XATTR_LIST, -+ .u.list = { -+ .list = list, -+ .size = size -+ }, -+ }; -+ -+ return au_lgxattr(dentry, &arg); -+} -+ -+ssize_t aufs_getxattr(struct dentry *dentry, const char *name, void *value, -+ size_t size) -+{ -+ struct au_lgxattr arg = { -+ .type = AU_XATTR_GET, -+ .u.get = { -+ .name = name, -+ .value = value, -+ .size = size -+ }, -+ }; -+ -+ return au_lgxattr(dentry, &arg); -+} -+ -+int aufs_setxattr(struct dentry *dentry, const char *name, const void *value, -+ size_t size, int flags) -+{ -+ struct au_srxattr arg = { -+ .type = AU_XATTR_SET, -+ .u.set = { -+ .name = name, -+ .value = value, -+ .size = size, -+ .flags = flags -+ }, -+ }; -+ -+ return au_srxattr(dentry, &arg); -+} -+ -+int aufs_removexattr(struct dentry *dentry, const char *name) -+{ -+ struct au_srxattr arg = { -+ .type = AU_XATTR_REMOVE, -+ .u.remove = { -+ .name = name -+ }, -+ }; -+ -+ return au_srxattr(dentry, &arg); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+#if 0 -+static size_t au_xattr_list(struct dentry *dentry, char *list, size_t list_size, -+ const char *name, size_t name_len, int type) -+{ -+ return aufs_listxattr(dentry, list, list_size); -+} -+ -+static int au_xattr_get(struct dentry *dentry, const char *name, void *buffer, -+ size_t size, int type) -+{ -+ return aufs_getxattr(dentry, name, buffer, size); -+} -+ -+static int au_xattr_set(struct dentry *dentry, const char *name, -+ const void *value, size_t size, int flags, int type) -+{ -+ return aufs_setxattr(dentry, name, value, size, flags); -+} -+ -+static const struct xattr_handler au_xattr_handler = { -+ /* no prefix, no flags */ -+ .list = au_xattr_list, -+ .get = au_xattr_get, -+ .set = au_xattr_set -+ /* why no remove? */ -+}; -+ -+static const struct xattr_handler *au_xattr_handlers[] = { -+ &au_xattr_handler -+}; -+ -+void au_xattr_init(struct super_block *sb) -+{ -+ /* sb->s_xattr = au_xattr_handlers; */ -+} -+#endif -diff --git a/fs/aufs/xino.c b/fs/aufs/xino.c -new file mode 100644 -index 0000000..50ab4ca ---- /dev/null -+++ b/fs/aufs/xino.c -@@ -0,0 +1,1343 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+/* -+ * external inode number translation table and bitmap -+ */ -+ -+#include -+#include -+#include "aufs.h" -+ -+/* todo: unnecessary to support mmap_sem since kernel-space? */ -+ssize_t xino_fread(au_readf_t func, struct file *file, void *kbuf, size_t size, -+ loff_t *pos) -+{ -+ ssize_t err; -+ mm_segment_t oldfs; -+ union { -+ void *k; -+ char __user *u; -+ } buf; -+ -+ buf.k = kbuf; -+ oldfs = get_fs(); -+ set_fs(KERNEL_DS); -+ do { -+ /* todo: signal_pending? */ -+ err = func(file, buf.u, size, pos); -+ } while (err == -EAGAIN || err == -EINTR); -+ set_fs(oldfs); -+ -+#if 0 /* reserved for future use */ -+ if (err > 0) -+ fsnotify_access(file->f_dentry); -+#endif -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static ssize_t xino_fwrite_wkq(au_writef_t func, struct file *file, void *buf, -+ size_t size, loff_t *pos); -+ -+static ssize_t do_xino_fwrite(au_writef_t func, struct file *file, void *kbuf, -+ size_t size, loff_t *pos) -+{ -+ ssize_t err; -+ mm_segment_t oldfs; -+ union { -+ void *k; -+ const char __user *u; -+ } buf; -+ int i; -+ const int prevent_endless = 10; -+ -+ i = 0; -+ buf.k = kbuf; -+ oldfs = get_fs(); -+ set_fs(KERNEL_DS); -+ do { -+ err = func(file, buf.u, size, pos); -+ if (err == -EINTR -+ && !au_wkq_test() -+ && fatal_signal_pending(current)) { -+ set_fs(oldfs); -+ err = xino_fwrite_wkq(func, file, kbuf, size, pos); -+ BUG_ON(err == -EINTR); -+ oldfs = get_fs(); -+ set_fs(KERNEL_DS); -+ } -+ } while (i++ < prevent_endless -+ && (err == -EAGAIN || err == -EINTR)); -+ set_fs(oldfs); -+ -+#if 0 /* reserved for future use */ -+ if (err > 0) -+ fsnotify_modify(file->f_dentry); -+#endif -+ -+ return err; -+} -+ -+struct do_xino_fwrite_args { -+ ssize_t *errp; -+ au_writef_t func; -+ struct file *file; -+ void *buf; -+ size_t size; -+ loff_t *pos; -+}; -+ -+static void call_do_xino_fwrite(void *args) -+{ -+ struct do_xino_fwrite_args *a = args; -+ *a->errp = do_xino_fwrite(a->func, a->file, a->buf, a->size, a->pos); -+} -+ -+static ssize_t xino_fwrite_wkq(au_writef_t func, struct file *file, void *buf, -+ size_t size, loff_t *pos) -+{ -+ ssize_t err; -+ int wkq_err; -+ struct do_xino_fwrite_args args = { -+ .errp = &err, -+ .func = func, -+ .file = file, -+ .buf = buf, -+ .size = size, -+ .pos = pos -+ }; -+ -+ /* -+ * it breaks RLIMIT_FSIZE and normal user's limit, -+ * users should care about quota and real 'filesystem full.' -+ */ -+ wkq_err = au_wkq_wait(call_do_xino_fwrite, &args); -+ if (unlikely(wkq_err)) -+ err = wkq_err; -+ -+ return err; -+} -+ -+ssize_t xino_fwrite(au_writef_t func, struct file *file, void *buf, size_t size, -+ loff_t *pos) -+{ -+ ssize_t err; -+ -+ if (rlimit(RLIMIT_FSIZE) == RLIM_INFINITY) { -+ lockdep_off(); -+ err = do_xino_fwrite(func, file, buf, size, pos); -+ lockdep_on(); -+ } else -+ err = xino_fwrite_wkq(func, file, buf, size, pos); -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * create a new xinofile at the same place/path as @base_file. -+ */ -+struct file *au_xino_create2(struct file *base_file, struct file *copy_src) -+{ -+ struct file *file; -+ struct dentry *base, *parent; -+ struct inode *dir, *delegated; -+ struct qstr *name; -+ struct path path; -+ int err; -+ -+ base = base_file->f_dentry; -+ parent = base->d_parent; /* dir inode is locked */ -+ dir = parent->d_inode; -+ IMustLock(dir); -+ -+ file = ERR_PTR(-EINVAL); -+ name = &base->d_name; -+ path.dentry = vfsub_lookup_one_len(name->name, parent, name->len); -+ if (IS_ERR(path.dentry)) { -+ file = (void *)path.dentry; -+ pr_err("%pd lookup err %ld\n", -+ base, PTR_ERR(path.dentry)); -+ goto out; -+ } -+ -+ /* no need to mnt_want_write() since we call dentry_open() later */ -+ err = vfs_create(dir, path.dentry, S_IRUGO | S_IWUGO, NULL); -+ if (unlikely(err)) { -+ file = ERR_PTR(err); -+ pr_err("%pd create err %d\n", base, err); -+ goto out_dput; -+ } -+ -+ path.mnt = base_file->f_path.mnt; -+ file = vfsub_dentry_open(&path, -+ O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE -+ /* | __FMODE_NONOTIFY */); -+ if (IS_ERR(file)) { -+ pr_err("%pd open err %ld\n", base, PTR_ERR(file)); -+ goto out_dput; -+ } -+ -+ delegated = NULL; -+ err = vfsub_unlink(dir, &file->f_path, &delegated, /*force*/0); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal unlink\n"); -+ iput(delegated); -+ } -+ if (unlikely(err)) { -+ pr_err("%pd unlink err %d\n", base, err); -+ goto out_fput; -+ } -+ -+ if (copy_src) { -+ /* no one can touch copy_src xino */ -+ err = au_copy_file(file, copy_src, vfsub_f_size_read(copy_src)); -+ if (unlikely(err)) { -+ pr_err("%pd copy err %d\n", base, err); -+ goto out_fput; -+ } -+ } -+ goto out_dput; /* success */ -+ -+out_fput: -+ fput(file); -+ file = ERR_PTR(err); -+out_dput: -+ dput(path.dentry); -+out: -+ return file; -+} -+ -+struct au_xino_lock_dir { -+ struct au_hinode *hdir; -+ struct dentry *parent; -+ struct mutex *mtx; -+}; -+ -+static void au_xino_lock_dir(struct super_block *sb, struct file *xino, -+ struct au_xino_lock_dir *ldir) -+{ -+ aufs_bindex_t brid, bindex; -+ -+ ldir->hdir = NULL; -+ bindex = -1; -+ brid = au_xino_brid(sb); -+ if (brid >= 0) -+ bindex = au_br_index(sb, brid); -+ if (bindex >= 0) { -+ ldir->hdir = au_hi(sb->s_root->d_inode, bindex); -+ au_hn_imtx_lock_nested(ldir->hdir, AuLsc_I_PARENT); -+ } else { -+ ldir->parent = dget_parent(xino->f_dentry); -+ ldir->mtx = &ldir->parent->d_inode->i_mutex; -+ mutex_lock_nested(ldir->mtx, AuLsc_I_PARENT); -+ } -+} -+ -+static void au_xino_unlock_dir(struct au_xino_lock_dir *ldir) -+{ -+ if (ldir->hdir) -+ au_hn_imtx_unlock(ldir->hdir); -+ else { -+ mutex_unlock(ldir->mtx); -+ dput(ldir->parent); -+ } -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* trucate xino files asynchronously */ -+ -+int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ int err; -+ unsigned long jiffy; -+ blkcnt_t blocks; -+ aufs_bindex_t bi, bend; -+ struct kstatfs *st; -+ struct au_branch *br; -+ struct file *new_xino, *file; -+ struct super_block *h_sb; -+ struct au_xino_lock_dir ldir; -+ -+ err = -ENOMEM; -+ st = kmalloc(sizeof(*st), GFP_NOFS); -+ if (unlikely(!st)) -+ goto out; -+ -+ err = -EINVAL; -+ bend = au_sbend(sb); -+ if (unlikely(bindex < 0 || bend < bindex)) -+ goto out_st; -+ br = au_sbr(sb, bindex); -+ file = br->br_xino.xi_file; -+ if (!file) -+ goto out_st; -+ -+ err = vfs_statfs(&file->f_path, st); -+ if (unlikely(err)) -+ AuErr1("statfs err %d, ignored\n", err); -+ jiffy = jiffies; -+ blocks = file_inode(file)->i_blocks; -+ pr_info("begin truncating xino(b%d), ib%llu, %llu/%llu free blks\n", -+ bindex, (u64)blocks, st->f_bfree, st->f_blocks); -+ -+ au_xino_lock_dir(sb, file, &ldir); -+ /* mnt_want_write() is unnecessary here */ -+ new_xino = au_xino_create2(file, file); -+ au_xino_unlock_dir(&ldir); -+ err = PTR_ERR(new_xino); -+ if (IS_ERR(new_xino)) { -+ pr_err("err %d, ignored\n", err); -+ goto out_st; -+ } -+ err = 0; -+ fput(file); -+ br->br_xino.xi_file = new_xino; -+ -+ h_sb = au_br_sb(br); -+ for (bi = 0; bi <= bend; bi++) { -+ if (unlikely(bi == bindex)) -+ continue; -+ br = au_sbr(sb, bi); -+ if (au_br_sb(br) != h_sb) -+ continue; -+ -+ fput(br->br_xino.xi_file); -+ br->br_xino.xi_file = new_xino; -+ get_file(new_xino); -+ } -+ -+ err = vfs_statfs(&new_xino->f_path, st); -+ if (!err) { -+ pr_info("end truncating xino(b%d), ib%llu, %llu/%llu free blks\n", -+ bindex, (u64)file_inode(new_xino)->i_blocks, -+ st->f_bfree, st->f_blocks); -+ if (file_inode(new_xino)->i_blocks < blocks) -+ au_sbi(sb)->si_xino_jiffy = jiffy; -+ } else -+ AuErr1("statfs err %d, ignored\n", err); -+ -+out_st: -+ kfree(st); -+out: -+ return err; -+} -+ -+struct xino_do_trunc_args { -+ struct super_block *sb; -+ struct au_branch *br; -+}; -+ -+static void xino_do_trunc(void *_args) -+{ -+ struct xino_do_trunc_args *args = _args; -+ struct super_block *sb; -+ struct au_branch *br; -+ struct inode *dir; -+ int err; -+ aufs_bindex_t bindex; -+ -+ err = 0; -+ sb = args->sb; -+ dir = sb->s_root->d_inode; -+ br = args->br; -+ -+ si_noflush_write_lock(sb); -+ ii_read_lock_parent(dir); -+ bindex = au_br_index(sb, br->br_id); -+ err = au_xino_trunc(sb, bindex); -+ ii_read_unlock(dir); -+ if (unlikely(err)) -+ pr_warn("err b%d, (%d)\n", bindex, err); -+ atomic_dec(&br->br_xino_running); -+ atomic_dec(&br->br_count); -+ si_write_unlock(sb); -+ au_nwt_done(&au_sbi(sb)->si_nowait); -+ kfree(args); -+} -+ -+static int xino_trunc_test(struct super_block *sb, struct au_branch *br) -+{ -+ int err; -+ struct kstatfs st; -+ struct au_sbinfo *sbinfo; -+ -+ /* todo: si_xino_expire and the ratio should be customizable */ -+ sbinfo = au_sbi(sb); -+ if (time_before(jiffies, -+ sbinfo->si_xino_jiffy + sbinfo->si_xino_expire)) -+ return 0; -+ -+ /* truncation border */ -+ err = vfs_statfs(&br->br_xino.xi_file->f_path, &st); -+ if (unlikely(err)) { -+ AuErr1("statfs err %d, ignored\n", err); -+ return 0; -+ } -+ if (div64_u64(st.f_bfree * 100, st.f_blocks) >= AUFS_XINO_DEF_TRUNC) -+ return 0; -+ -+ return 1; -+} -+ -+static void xino_try_trunc(struct super_block *sb, struct au_branch *br) -+{ -+ struct xino_do_trunc_args *args; -+ int wkq_err; -+ -+ if (!xino_trunc_test(sb, br)) -+ return; -+ -+ if (atomic_inc_return(&br->br_xino_running) > 1) -+ goto out; -+ -+ /* lock and kfree() will be called in trunc_xino() */ -+ args = kmalloc(sizeof(*args), GFP_NOFS); -+ if (unlikely(!args)) { -+ AuErr1("no memory\n"); -+ goto out_args; -+ } -+ -+ atomic_inc(&br->br_count); -+ args->sb = sb; -+ args->br = br; -+ wkq_err = au_wkq_nowait(xino_do_trunc, args, sb, /*flags*/0); -+ if (!wkq_err) -+ return; /* success */ -+ -+ pr_err("wkq %d\n", wkq_err); -+ atomic_dec(&br->br_count); -+ -+out_args: -+ kfree(args); -+out: -+ atomic_dec(&br->br_xino_running); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_xino_do_write(au_writef_t write, struct file *file, -+ ino_t h_ino, ino_t ino) -+{ -+ loff_t pos; -+ ssize_t sz; -+ -+ pos = h_ino; -+ if (unlikely(au_loff_max / sizeof(ino) - 1 < pos)) { -+ AuIOErr1("too large hi%lu\n", (unsigned long)h_ino); -+ return -EFBIG; -+ } -+ pos *= sizeof(ino); -+ sz = xino_fwrite(write, file, &ino, sizeof(ino), &pos); -+ if (sz == sizeof(ino)) -+ return 0; /* success */ -+ -+ AuIOErr("write failed (%zd)\n", sz); -+ return -EIO; -+} -+ -+/* -+ * write @ino to the xinofile for the specified branch{@sb, @bindex} -+ * at the position of @h_ino. -+ * even if @ino is zero, it is written to the xinofile and means no entry. -+ * if the size of the xino file on a specific filesystem exceeds the watermark, -+ * try truncating it. -+ */ -+int au_xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, -+ ino_t ino) -+{ -+ int err; -+ unsigned int mnt_flags; -+ struct au_branch *br; -+ -+ BUILD_BUG_ON(sizeof(long long) != sizeof(au_loff_max) -+ || ((loff_t)-1) > 0); -+ SiMustAnyLock(sb); -+ -+ mnt_flags = au_mntflags(sb); -+ if (!au_opt_test(mnt_flags, XINO)) -+ return 0; -+ -+ br = au_sbr(sb, bindex); -+ err = au_xino_do_write(au_sbi(sb)->si_xwrite, br->br_xino.xi_file, -+ h_ino, ino); -+ if (!err) { -+ if (au_opt_test(mnt_flags, TRUNC_XINO) -+ && au_test_fs_trunc_xino(au_br_sb(br))) -+ xino_try_trunc(sb, br); -+ return 0; /* success */ -+ } -+ -+ AuIOErr("write failed (%d)\n", err); -+ return -EIO; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* aufs inode number bitmap */ -+ -+static const int page_bits = (int)PAGE_SIZE * BITS_PER_BYTE; -+static ino_t xib_calc_ino(unsigned long pindex, int bit) -+{ -+ ino_t ino; -+ -+ AuDebugOn(bit < 0 || page_bits <= bit); -+ ino = AUFS_FIRST_INO + pindex * page_bits + bit; -+ return ino; -+} -+ -+static void xib_calc_bit(ino_t ino, unsigned long *pindex, int *bit) -+{ -+ AuDebugOn(ino < AUFS_FIRST_INO); -+ ino -= AUFS_FIRST_INO; -+ *pindex = ino / page_bits; -+ *bit = ino % page_bits; -+} -+ -+static int xib_pindex(struct super_block *sb, unsigned long pindex) -+{ -+ int err; -+ loff_t pos; -+ ssize_t sz; -+ struct au_sbinfo *sbinfo; -+ struct file *xib; -+ unsigned long *p; -+ -+ sbinfo = au_sbi(sb); -+ MtxMustLock(&sbinfo->si_xib_mtx); -+ AuDebugOn(pindex > ULONG_MAX / PAGE_SIZE -+ || !au_opt_test(sbinfo->si_mntflags, XINO)); -+ -+ if (pindex == sbinfo->si_xib_last_pindex) -+ return 0; -+ -+ xib = sbinfo->si_xib; -+ p = sbinfo->si_xib_buf; -+ pos = sbinfo->si_xib_last_pindex; -+ pos *= PAGE_SIZE; -+ sz = xino_fwrite(sbinfo->si_xwrite, xib, p, PAGE_SIZE, &pos); -+ if (unlikely(sz != PAGE_SIZE)) -+ goto out; -+ -+ pos = pindex; -+ pos *= PAGE_SIZE; -+ if (vfsub_f_size_read(xib) >= pos + PAGE_SIZE) -+ sz = xino_fread(sbinfo->si_xread, xib, p, PAGE_SIZE, &pos); -+ else { -+ memset(p, 0, PAGE_SIZE); -+ sz = xino_fwrite(sbinfo->si_xwrite, xib, p, PAGE_SIZE, &pos); -+ } -+ if (sz == PAGE_SIZE) { -+ sbinfo->si_xib_last_pindex = pindex; -+ return 0; /* success */ -+ } -+ -+out: -+ AuIOErr1("write failed (%zd)\n", sz); -+ err = sz; -+ if (sz >= 0) -+ err = -EIO; -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void au_xib_clear_bit(struct inode *inode) -+{ -+ int err, bit; -+ unsigned long pindex; -+ struct super_block *sb; -+ struct au_sbinfo *sbinfo; -+ -+ AuDebugOn(inode->i_nlink); -+ -+ sb = inode->i_sb; -+ xib_calc_bit(inode->i_ino, &pindex, &bit); -+ AuDebugOn(page_bits <= bit); -+ sbinfo = au_sbi(sb); -+ mutex_lock(&sbinfo->si_xib_mtx); -+ err = xib_pindex(sb, pindex); -+ if (!err) { -+ clear_bit(bit, sbinfo->si_xib_buf); -+ sbinfo->si_xib_next_bit = bit; -+ } -+ mutex_unlock(&sbinfo->si_xib_mtx); -+} -+ -+/* for s_op->delete_inode() */ -+void au_xino_delete_inode(struct inode *inode, const int unlinked) -+{ -+ int err; -+ unsigned int mnt_flags; -+ aufs_bindex_t bindex, bend, bi; -+ unsigned char try_trunc; -+ struct au_iinfo *iinfo; -+ struct super_block *sb; -+ struct au_hinode *hi; -+ struct inode *h_inode; -+ struct au_branch *br; -+ au_writef_t xwrite; -+ -+ sb = inode->i_sb; -+ mnt_flags = au_mntflags(sb); -+ if (!au_opt_test(mnt_flags, XINO) -+ || inode->i_ino == AUFS_ROOT_INO) -+ return; -+ -+ if (unlinked) { -+ au_xigen_inc(inode); -+ au_xib_clear_bit(inode); -+ } -+ -+ iinfo = au_ii(inode); -+ if (!iinfo) -+ return; -+ -+ bindex = iinfo->ii_bstart; -+ if (bindex < 0) -+ return; -+ -+ xwrite = au_sbi(sb)->si_xwrite; -+ try_trunc = !!au_opt_test(mnt_flags, TRUNC_XINO); -+ hi = iinfo->ii_hinode + bindex; -+ bend = iinfo->ii_bend; -+ for (; bindex <= bend; bindex++, hi++) { -+ h_inode = hi->hi_inode; -+ if (!h_inode -+ || (!unlinked && h_inode->i_nlink)) -+ continue; -+ -+ /* inode may not be revalidated */ -+ bi = au_br_index(sb, hi->hi_id); -+ if (bi < 0) -+ continue; -+ -+ br = au_sbr(sb, bi); -+ err = au_xino_do_write(xwrite, br->br_xino.xi_file, -+ h_inode->i_ino, /*ino*/0); -+ if (!err && try_trunc -+ && au_test_fs_trunc_xino(au_br_sb(br))) -+ xino_try_trunc(sb, br); -+ } -+} -+ -+/* get an unused inode number from bitmap */ -+ino_t au_xino_new_ino(struct super_block *sb) -+{ -+ ino_t ino; -+ unsigned long *p, pindex, ul, pend; -+ struct au_sbinfo *sbinfo; -+ struct file *file; -+ int free_bit, err; -+ -+ if (!au_opt_test(au_mntflags(sb), XINO)) -+ return iunique(sb, AUFS_FIRST_INO); -+ -+ sbinfo = au_sbi(sb); -+ mutex_lock(&sbinfo->si_xib_mtx); -+ p = sbinfo->si_xib_buf; -+ free_bit = sbinfo->si_xib_next_bit; -+ if (free_bit < page_bits && !test_bit(free_bit, p)) -+ goto out; /* success */ -+ free_bit = find_first_zero_bit(p, page_bits); -+ if (free_bit < page_bits) -+ goto out; /* success */ -+ -+ pindex = sbinfo->si_xib_last_pindex; -+ for (ul = pindex - 1; ul < ULONG_MAX; ul--) { -+ err = xib_pindex(sb, ul); -+ if (unlikely(err)) -+ goto out_err; -+ free_bit = find_first_zero_bit(p, page_bits); -+ if (free_bit < page_bits) -+ goto out; /* success */ -+ } -+ -+ file = sbinfo->si_xib; -+ pend = vfsub_f_size_read(file) / PAGE_SIZE; -+ for (ul = pindex + 1; ul <= pend; ul++) { -+ err = xib_pindex(sb, ul); -+ if (unlikely(err)) -+ goto out_err; -+ free_bit = find_first_zero_bit(p, page_bits); -+ if (free_bit < page_bits) -+ goto out; /* success */ -+ } -+ BUG(); -+ -+out: -+ set_bit(free_bit, p); -+ sbinfo->si_xib_next_bit = free_bit + 1; -+ pindex = sbinfo->si_xib_last_pindex; -+ mutex_unlock(&sbinfo->si_xib_mtx); -+ ino = xib_calc_ino(pindex, free_bit); -+ AuDbg("i%lu\n", (unsigned long)ino); -+ return ino; -+out_err: -+ mutex_unlock(&sbinfo->si_xib_mtx); -+ AuDbg("i0\n"); -+ return 0; -+} -+ -+/* -+ * read @ino from xinofile for the specified branch{@sb, @bindex} -+ * at the position of @h_ino. -+ * if @ino does not exist and @do_new is true, get new one. -+ */ -+int au_xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, -+ ino_t *ino) -+{ -+ int err; -+ ssize_t sz; -+ loff_t pos; -+ struct file *file; -+ struct au_sbinfo *sbinfo; -+ -+ *ino = 0; -+ if (!au_opt_test(au_mntflags(sb), XINO)) -+ return 0; /* no xino */ -+ -+ err = 0; -+ sbinfo = au_sbi(sb); -+ pos = h_ino; -+ if (unlikely(au_loff_max / sizeof(*ino) - 1 < pos)) { -+ AuIOErr1("too large hi%lu\n", (unsigned long)h_ino); -+ return -EFBIG; -+ } -+ pos *= sizeof(*ino); -+ -+ file = au_sbr(sb, bindex)->br_xino.xi_file; -+ if (vfsub_f_size_read(file) < pos + sizeof(*ino)) -+ return 0; /* no ino */ -+ -+ sz = xino_fread(sbinfo->si_xread, file, ino, sizeof(*ino), &pos); -+ if (sz == sizeof(*ino)) -+ return 0; /* success */ -+ -+ err = sz; -+ if (unlikely(sz >= 0)) { -+ err = -EIO; -+ AuIOErr("xino read error (%zd)\n", sz); -+ } -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* create and set a new xino file */ -+ -+struct file *au_xino_create(struct super_block *sb, char *fname, int silent) -+{ -+ struct file *file; -+ struct dentry *h_parent, *d; -+ struct inode *h_dir, *inode; -+ int err; -+ -+ /* -+ * at mount-time, and the xino file is the default path, -+ * hnotify is disabled so we have no notify events to ignore. -+ * when a user specified the xino, we cannot get au_hdir to be ignored. -+ */ -+ file = vfsub_filp_open(fname, O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE -+ /* | __FMODE_NONOTIFY */, -+ S_IRUGO | S_IWUGO); -+ if (IS_ERR(file)) { -+ if (!silent) -+ pr_err("open %s(%ld)\n", fname, PTR_ERR(file)); -+ return file; -+ } -+ -+ /* keep file count */ -+ err = 0; -+ inode = file_inode(file); -+ h_parent = dget_parent(file->f_dentry); -+ h_dir = h_parent->d_inode; -+ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); -+ /* mnt_want_write() is unnecessary here */ -+ /* no delegation since it is just created */ -+ if (inode->i_nlink) -+ err = vfsub_unlink(h_dir, &file->f_path, /*delegated*/NULL, -+ /*force*/0); -+ mutex_unlock(&h_dir->i_mutex); -+ dput(h_parent); -+ if (unlikely(err)) { -+ if (!silent) -+ pr_err("unlink %s(%d)\n", fname, err); -+ goto out; -+ } -+ -+ err = -EINVAL; -+ d = file->f_dentry; -+ if (unlikely(sb == d->d_sb)) { -+ if (!silent) -+ pr_err("%s must be outside\n", fname); -+ goto out; -+ } -+ if (unlikely(au_test_fs_bad_xino(d->d_sb))) { -+ if (!silent) -+ pr_err("xino doesn't support %s(%s)\n", -+ fname, au_sbtype(d->d_sb)); -+ goto out; -+ } -+ return file; /* success */ -+ -+out: -+ fput(file); -+ file = ERR_PTR(err); -+ return file; -+} -+ -+/* -+ * find another branch who is on the same filesystem of the specified -+ * branch{@btgt}. search until @bend. -+ */ -+static int is_sb_shared(struct super_block *sb, aufs_bindex_t btgt, -+ aufs_bindex_t bend) -+{ -+ aufs_bindex_t bindex; -+ struct super_block *tgt_sb = au_sbr_sb(sb, btgt); -+ -+ for (bindex = 0; bindex < btgt; bindex++) -+ if (unlikely(tgt_sb == au_sbr_sb(sb, bindex))) -+ return bindex; -+ for (bindex++; bindex <= bend; bindex++) -+ if (unlikely(tgt_sb == au_sbr_sb(sb, bindex))) -+ return bindex; -+ return -1; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * initialize the xinofile for the specified branch @br -+ * at the place/path where @base_file indicates. -+ * test whether another branch is on the same filesystem or not, -+ * if @do_test is true. -+ */ -+int au_xino_br(struct super_block *sb, struct au_branch *br, ino_t h_ino, -+ struct file *base_file, int do_test) -+{ -+ int err; -+ ino_t ino; -+ aufs_bindex_t bend, bindex; -+ struct au_branch *shared_br, *b; -+ struct file *file; -+ struct super_block *tgt_sb; -+ -+ shared_br = NULL; -+ bend = au_sbend(sb); -+ if (do_test) { -+ tgt_sb = au_br_sb(br); -+ for (bindex = 0; bindex <= bend; bindex++) { -+ b = au_sbr(sb, bindex); -+ if (tgt_sb == au_br_sb(b)) { -+ shared_br = b; -+ break; -+ } -+ } -+ } -+ -+ if (!shared_br || !shared_br->br_xino.xi_file) { -+ struct au_xino_lock_dir ldir; -+ -+ au_xino_lock_dir(sb, base_file, &ldir); -+ /* mnt_want_write() is unnecessary here */ -+ file = au_xino_create2(base_file, NULL); -+ au_xino_unlock_dir(&ldir); -+ err = PTR_ERR(file); -+ if (IS_ERR(file)) -+ goto out; -+ br->br_xino.xi_file = file; -+ } else { -+ br->br_xino.xi_file = shared_br->br_xino.xi_file; -+ get_file(br->br_xino.xi_file); -+ } -+ -+ ino = AUFS_ROOT_INO; -+ err = au_xino_do_write(au_sbi(sb)->si_xwrite, br->br_xino.xi_file, -+ h_ino, ino); -+ if (unlikely(err)) { -+ fput(br->br_xino.xi_file); -+ br->br_xino.xi_file = NULL; -+ } -+ -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* trucate a xino bitmap file */ -+ -+/* todo: slow */ -+static int do_xib_restore(struct super_block *sb, struct file *file, void *page) -+{ -+ int err, bit; -+ ssize_t sz; -+ unsigned long pindex; -+ loff_t pos, pend; -+ struct au_sbinfo *sbinfo; -+ au_readf_t func; -+ ino_t *ino; -+ unsigned long *p; -+ -+ err = 0; -+ sbinfo = au_sbi(sb); -+ MtxMustLock(&sbinfo->si_xib_mtx); -+ p = sbinfo->si_xib_buf; -+ func = sbinfo->si_xread; -+ pend = vfsub_f_size_read(file); -+ pos = 0; -+ while (pos < pend) { -+ sz = xino_fread(func, file, page, PAGE_SIZE, &pos); -+ err = sz; -+ if (unlikely(sz <= 0)) -+ goto out; -+ -+ err = 0; -+ for (ino = page; sz > 0; ino++, sz -= sizeof(ino)) { -+ if (unlikely(*ino < AUFS_FIRST_INO)) -+ continue; -+ -+ xib_calc_bit(*ino, &pindex, &bit); -+ AuDebugOn(page_bits <= bit); -+ err = xib_pindex(sb, pindex); -+ if (!err) -+ set_bit(bit, p); -+ else -+ goto out; -+ } -+ } -+ -+out: -+ return err; -+} -+ -+static int xib_restore(struct super_block *sb) -+{ -+ int err; -+ aufs_bindex_t bindex, bend; -+ void *page; -+ -+ err = -ENOMEM; -+ page = (void *)__get_free_page(GFP_NOFS); -+ if (unlikely(!page)) -+ goto out; -+ -+ err = 0; -+ bend = au_sbend(sb); -+ for (bindex = 0; !err && bindex <= bend; bindex++) -+ if (!bindex || is_sb_shared(sb, bindex, bindex - 1) < 0) -+ err = do_xib_restore -+ (sb, au_sbr(sb, bindex)->br_xino.xi_file, page); -+ else -+ AuDbg("b%d\n", bindex); -+ free_page((unsigned long)page); -+ -+out: -+ return err; -+} -+ -+int au_xib_trunc(struct super_block *sb) -+{ -+ int err; -+ ssize_t sz; -+ loff_t pos; -+ struct au_xino_lock_dir ldir; -+ struct au_sbinfo *sbinfo; -+ unsigned long *p; -+ struct file *file; -+ -+ SiMustWriteLock(sb); -+ -+ err = 0; -+ sbinfo = au_sbi(sb); -+ if (!au_opt_test(sbinfo->si_mntflags, XINO)) -+ goto out; -+ -+ file = sbinfo->si_xib; -+ if (vfsub_f_size_read(file) <= PAGE_SIZE) -+ goto out; -+ -+ au_xino_lock_dir(sb, file, &ldir); -+ /* mnt_want_write() is unnecessary here */ -+ file = au_xino_create2(sbinfo->si_xib, NULL); -+ au_xino_unlock_dir(&ldir); -+ err = PTR_ERR(file); -+ if (IS_ERR(file)) -+ goto out; -+ fput(sbinfo->si_xib); -+ sbinfo->si_xib = file; -+ -+ p = sbinfo->si_xib_buf; -+ memset(p, 0, PAGE_SIZE); -+ pos = 0; -+ sz = xino_fwrite(sbinfo->si_xwrite, sbinfo->si_xib, p, PAGE_SIZE, &pos); -+ if (unlikely(sz != PAGE_SIZE)) { -+ err = sz; -+ AuIOErr("err %d\n", err); -+ if (sz >= 0) -+ err = -EIO; -+ goto out; -+ } -+ -+ mutex_lock(&sbinfo->si_xib_mtx); -+ /* mnt_want_write() is unnecessary here */ -+ err = xib_restore(sb); -+ mutex_unlock(&sbinfo->si_xib_mtx); -+ -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * xino mount option handlers -+ */ -+static au_readf_t find_readf(struct file *h_file) -+{ -+ const struct file_operations *fop = h_file->f_op; -+ -+ if (fop->read) -+ return fop->read; -+ if (fop->aio_read) -+ return do_sync_read; -+ if (fop->read_iter) -+ return new_sync_read; -+ return ERR_PTR(-ENOSYS); -+} -+ -+static au_writef_t find_writef(struct file *h_file) -+{ -+ const struct file_operations *fop = h_file->f_op; -+ -+ if (fop->write) -+ return fop->write; -+ if (fop->aio_write) -+ return do_sync_write; -+ if (fop->write_iter) -+ return new_sync_write; -+ return ERR_PTR(-ENOSYS); -+} -+ -+/* xino bitmap */ -+static void xino_clear_xib(struct super_block *sb) -+{ -+ struct au_sbinfo *sbinfo; -+ -+ SiMustWriteLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ sbinfo->si_xread = NULL; -+ sbinfo->si_xwrite = NULL; -+ if (sbinfo->si_xib) -+ fput(sbinfo->si_xib); -+ sbinfo->si_xib = NULL; -+ free_page((unsigned long)sbinfo->si_xib_buf); -+ sbinfo->si_xib_buf = NULL; -+} -+ -+static int au_xino_set_xib(struct super_block *sb, struct file *base) -+{ -+ int err; -+ loff_t pos; -+ struct au_sbinfo *sbinfo; -+ struct file *file; -+ -+ SiMustWriteLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ file = au_xino_create2(base, sbinfo->si_xib); -+ err = PTR_ERR(file); -+ if (IS_ERR(file)) -+ goto out; -+ if (sbinfo->si_xib) -+ fput(sbinfo->si_xib); -+ sbinfo->si_xib = file; -+ sbinfo->si_xread = find_readf(file); -+ sbinfo->si_xwrite = find_writef(file); -+ -+ err = -ENOMEM; -+ if (!sbinfo->si_xib_buf) -+ sbinfo->si_xib_buf = (void *)get_zeroed_page(GFP_NOFS); -+ if (unlikely(!sbinfo->si_xib_buf)) -+ goto out_unset; -+ -+ sbinfo->si_xib_last_pindex = 0; -+ sbinfo->si_xib_next_bit = 0; -+ if (vfsub_f_size_read(file) < PAGE_SIZE) { -+ pos = 0; -+ err = xino_fwrite(sbinfo->si_xwrite, file, sbinfo->si_xib_buf, -+ PAGE_SIZE, &pos); -+ if (unlikely(err != PAGE_SIZE)) -+ goto out_free; -+ } -+ err = 0; -+ goto out; /* success */ -+ -+out_free: -+ free_page((unsigned long)sbinfo->si_xib_buf); -+ sbinfo->si_xib_buf = NULL; -+ if (err >= 0) -+ err = -EIO; -+out_unset: -+ fput(sbinfo->si_xib); -+ sbinfo->si_xib = NULL; -+ sbinfo->si_xread = NULL; -+ sbinfo->si_xwrite = NULL; -+out: -+ return err; -+} -+ -+/* xino for each branch */ -+static void xino_clear_br(struct super_block *sb) -+{ -+ aufs_bindex_t bindex, bend; -+ struct au_branch *br; -+ -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ if (!br || !br->br_xino.xi_file) -+ continue; -+ -+ fput(br->br_xino.xi_file); -+ br->br_xino.xi_file = NULL; -+ } -+} -+ -+static int au_xino_set_br(struct super_block *sb, struct file *base) -+{ -+ int err; -+ ino_t ino; -+ aufs_bindex_t bindex, bend, bshared; -+ struct { -+ struct file *old, *new; -+ } *fpair, *p; -+ struct au_branch *br; -+ struct inode *inode; -+ au_writef_t writef; -+ -+ SiMustWriteLock(sb); -+ -+ err = -ENOMEM; -+ bend = au_sbend(sb); -+ fpair = kcalloc(bend + 1, sizeof(*fpair), GFP_NOFS); -+ if (unlikely(!fpair)) -+ goto out; -+ -+ inode = sb->s_root->d_inode; -+ ino = AUFS_ROOT_INO; -+ writef = au_sbi(sb)->si_xwrite; -+ for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) { -+ br = au_sbr(sb, bindex); -+ bshared = is_sb_shared(sb, bindex, bindex - 1); -+ if (bshared >= 0) { -+ /* shared xino */ -+ *p = fpair[bshared]; -+ get_file(p->new); -+ } -+ -+ if (!p->new) { -+ /* new xino */ -+ p->old = br->br_xino.xi_file; -+ p->new = au_xino_create2(base, br->br_xino.xi_file); -+ err = PTR_ERR(p->new); -+ if (IS_ERR(p->new)) { -+ p->new = NULL; -+ goto out_pair; -+ } -+ } -+ -+ err = au_xino_do_write(writef, p->new, -+ au_h_iptr(inode, bindex)->i_ino, ino); -+ if (unlikely(err)) -+ goto out_pair; -+ } -+ -+ for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) { -+ br = au_sbr(sb, bindex); -+ if (br->br_xino.xi_file) -+ fput(br->br_xino.xi_file); -+ get_file(p->new); -+ br->br_xino.xi_file = p->new; -+ } -+ -+out_pair: -+ for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) -+ if (p->new) -+ fput(p->new); -+ else -+ break; -+ kfree(fpair); -+out: -+ return err; -+} -+ -+void au_xino_clr(struct super_block *sb) -+{ -+ struct au_sbinfo *sbinfo; -+ -+ au_xigen_clr(sb); -+ xino_clear_xib(sb); -+ xino_clear_br(sb); -+ sbinfo = au_sbi(sb); -+ /* lvalue, do not call au_mntflags() */ -+ au_opt_clr(sbinfo->si_mntflags, XINO); -+} -+ -+int au_xino_set(struct super_block *sb, struct au_opt_xino *xino, int remount) -+{ -+ int err, skip; -+ struct dentry *parent, *cur_parent; -+ struct qstr *dname, *cur_name; -+ struct file *cur_xino; -+ struct inode *dir; -+ struct au_sbinfo *sbinfo; -+ -+ SiMustWriteLock(sb); -+ -+ err = 0; -+ sbinfo = au_sbi(sb); -+ parent = dget_parent(xino->file->f_dentry); -+ if (remount) { -+ skip = 0; -+ dname = &xino->file->f_dentry->d_name; -+ cur_xino = sbinfo->si_xib; -+ if (cur_xino) { -+ cur_parent = dget_parent(cur_xino->f_dentry); -+ cur_name = &cur_xino->f_dentry->d_name; -+ skip = (cur_parent == parent -+ && au_qstreq(dname, cur_name)); -+ dput(cur_parent); -+ } -+ if (skip) -+ goto out; -+ } -+ -+ au_opt_set(sbinfo->si_mntflags, XINO); -+ dir = parent->d_inode; -+ mutex_lock_nested(&dir->i_mutex, AuLsc_I_PARENT); -+ /* mnt_want_write() is unnecessary here */ -+ err = au_xino_set_xib(sb, xino->file); -+ if (!err) -+ err = au_xigen_set(sb, xino->file); -+ if (!err) -+ err = au_xino_set_br(sb, xino->file); -+ mutex_unlock(&dir->i_mutex); -+ if (!err) -+ goto out; /* success */ -+ -+ /* reset all */ -+ AuIOErr("failed creating xino(%d).\n", err); -+ au_xigen_clr(sb); -+ xino_clear_xib(sb); -+ -+out: -+ dput(parent); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * create a xinofile at the default place/path. -+ */ -+struct file *au_xino_def(struct super_block *sb) -+{ -+ struct file *file; -+ char *page, *p; -+ struct au_branch *br; -+ struct super_block *h_sb; -+ struct path path; -+ aufs_bindex_t bend, bindex, bwr; -+ -+ br = NULL; -+ bend = au_sbend(sb); -+ bwr = -1; -+ for (bindex = 0; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ if (au_br_writable(br->br_perm) -+ && !au_test_fs_bad_xino(au_br_sb(br))) { -+ bwr = bindex; -+ break; -+ } -+ } -+ -+ if (bwr >= 0) { -+ file = ERR_PTR(-ENOMEM); -+ page = (void *)__get_free_page(GFP_NOFS); -+ if (unlikely(!page)) -+ goto out; -+ path.mnt = au_br_mnt(br); -+ path.dentry = au_h_dptr(sb->s_root, bwr); -+ p = d_path(&path, page, PATH_MAX - sizeof(AUFS_XINO_FNAME)); -+ file = (void *)p; -+ if (!IS_ERR(p)) { -+ strcat(p, "/" AUFS_XINO_FNAME); -+ AuDbg("%s\n", p); -+ file = au_xino_create(sb, p, /*silent*/0); -+ if (!IS_ERR(file)) -+ au_xino_brid_set(sb, br->br_id); -+ } -+ free_page((unsigned long)page); -+ } else { -+ file = au_xino_create(sb, AUFS_XINO_DEFPATH, /*silent*/0); -+ if (IS_ERR(file)) -+ goto out; -+ h_sb = file->f_dentry->d_sb; -+ if (unlikely(au_test_fs_bad_xino(h_sb))) { -+ pr_err("xino doesn't support %s(%s)\n", -+ AUFS_XINO_DEFPATH, au_sbtype(h_sb)); -+ fput(file); -+ file = ERR_PTR(-EINVAL); -+ } -+ if (!IS_ERR(file)) -+ au_xino_brid_set(sb, -1); -+ } -+ -+out: -+ return file; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int au_xino_path(struct seq_file *seq, struct file *file) -+{ -+ int err; -+ -+ err = au_seq_path(seq, &file->f_path); -+ if (unlikely(err)) -+ goto out; -+ -+#define Deleted "\\040(deleted)" -+ seq->count -= sizeof(Deleted) - 1; -+ AuDebugOn(memcmp(seq->buf + seq->count, Deleted, -+ sizeof(Deleted) - 1)); -+#undef Deleted -+ -+out: -+ return err; -+} -diff --git a/fs/buffer.c b/fs/buffer.c -index 20805db..363569f 100644 ---- a/fs/buffer.c -+++ b/fs/buffer.c -@@ -2450,7 +2450,7 @@ int block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf, - * Update file times before taking page lock. We may end up failing the - * fault so this update may be superfluous but who really cares... - */ -- file_update_time(vma->vm_file); -+ vma_file_update_time(vma); - - ret = __block_page_mkwrite(vma, vmf, get_block); - sb_end_pagefault(sb); -diff --git a/fs/dcache.c b/fs/dcache.c -index d25f8fd..857990a 100644 ---- a/fs/dcache.c -+++ b/fs/dcache.c -@@ -1022,7 +1022,7 @@ enum d_walk_ret { - * - * The @enter() and @finish() callbacks are called with d_lock held. - */ --static void d_walk(struct dentry *parent, void *data, -+void d_walk(struct dentry *parent, void *data, - enum d_walk_ret (*enter)(void *, struct dentry *), - void (*finish)(void *)) - { -diff --git a/fs/fcntl.c b/fs/fcntl.c -index 99d440a..de1a407 100644 ---- a/fs/fcntl.c -+++ b/fs/fcntl.c -@@ -29,7 +29,7 @@ - - #define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | O_DIRECT | O_NOATIME) - --static int setfl(int fd, struct file * filp, unsigned long arg) -+int setfl(int fd, struct file * filp, unsigned long arg) - { - struct inode * inode = file_inode(filp); - int error = 0; -@@ -59,6 +59,8 @@ static int setfl(int fd, struct file * filp, unsigned long arg) - - if (filp->f_op->check_flags) - error = filp->f_op->check_flags(arg); -+ if (!error && filp->f_op->setfl) -+ error = filp->f_op->setfl(filp, arg); - if (error) - return error; - -diff --git a/fs/inode.c b/fs/inode.c -index 56d1d2b..2998e86 100644 ---- a/fs/inode.c -+++ b/fs/inode.c -@@ -1497,7 +1497,7 @@ static int relatime_need_update(struct vfsmount *mnt, struct inode *inode, - * This does the actual work of updating an inodes time or version. Must have - * had called mnt_want_write() before calling this. - */ --static int update_time(struct inode *inode, struct timespec *time, int flags) -+int update_time(struct inode *inode, struct timespec *time, int flags) - { - if (inode->i_op->update_time) - return inode->i_op->update_time(inode, time, flags); -diff --git a/fs/proc/base.c b/fs/proc/base.c -index 7dc3ea8..b368ad5 100644 ---- a/fs/proc/base.c -+++ b/fs/proc/base.c -@@ -1735,7 +1735,7 @@ static int proc_map_files_get_link(struct dentry *dentry, struct path *path) - down_read(&mm->mmap_sem); - vma = find_exact_vma(mm, vm_start, vm_end); - if (vma && vma->vm_file) { -- *path = vma->vm_file->f_path; -+ *path = vma_pr_or_file(vma)->f_path; - path_get(path); - rc = 0; - } -diff --git a/fs/proc/nommu.c b/fs/proc/nommu.c -index d4a3574..1397181 100644 ---- a/fs/proc/nommu.c -+++ b/fs/proc/nommu.c -@@ -45,7 +45,10 @@ static int nommu_region_show(struct seq_file *m, struct vm_region *region) - file = region->vm_file; - - if (file) { -- struct inode *inode = file_inode(region->vm_file); -+ struct inode *inode; -+ -+ file = vmr_pr_or_file(region); -+ inode = file_inode(file); - dev = inode->i_sb->s_dev; - ino = inode->i_ino; - } -diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c -index 69aa378..426b962 100644 ---- a/fs/proc/task_mmu.c -+++ b/fs/proc/task_mmu.c -@@ -276,7 +276,10 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid) - const char *name = NULL; - - if (file) { -- struct inode *inode = file_inode(vma->vm_file); -+ struct inode *inode; -+ -+ file = vma_pr_or_file(vma); -+ inode = file_inode(file); - dev = inode->i_sb->s_dev; - ino = inode->i_ino; - pgoff = ((loff_t)vma->vm_pgoff) << PAGE_SHIFT; -@@ -1447,7 +1450,7 @@ static int show_numa_map(struct seq_file *m, void *v, int is_pid) - struct proc_maps_private *proc_priv = &numa_priv->proc_maps; - struct vm_area_struct *vma = v; - struct numa_maps *md = &numa_priv->md; -- struct file *file = vma->vm_file; -+ struct file *file = vma_pr_or_file(vma); - struct mm_struct *mm = vma->vm_mm; - struct mm_walk walk = {}; - struct mempolicy *pol; -diff --git a/fs/proc/task_nommu.c b/fs/proc/task_nommu.c -index 599ec2e..1740207 100644 ---- a/fs/proc/task_nommu.c -+++ b/fs/proc/task_nommu.c -@@ -160,7 +160,10 @@ static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma, - file = vma->vm_file; - - if (file) { -- struct inode *inode = file_inode(vma->vm_file); -+ struct inode *inode; -+ -+ file = vma_pr_or_file(vma); -+ inode = file_inode(file); - dev = inode->i_sb->s_dev; - ino = inode->i_ino; - pgoff = (loff_t)vma->vm_pgoff << PAGE_SHIFT; -diff --git a/fs/splice.c b/fs/splice.c -index 75c6058..619359a 100644 ---- a/fs/splice.c -+++ b/fs/splice.c -@@ -1114,8 +1114,8 @@ EXPORT_SYMBOL(generic_splice_sendpage); - /* - * Attempt to initiate a splice from pipe to file. - */ --static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, -- loff_t *ppos, size_t len, unsigned int flags) -+long do_splice_from(struct pipe_inode_info *pipe, struct file *out, -+ loff_t *ppos, size_t len, unsigned int flags) - { - ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, - loff_t *, size_t, unsigned int); -@@ -1131,9 +1131,9 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, - /* - * Attempt to initiate a splice from a file to a pipe. - */ --static long do_splice_to(struct file *in, loff_t *ppos, -- struct pipe_inode_info *pipe, size_t len, -- unsigned int flags) -+long do_splice_to(struct file *in, loff_t *ppos, -+ struct pipe_inode_info *pipe, size_t len, -+ unsigned int flags) - { - ssize_t (*splice_read)(struct file *, loff_t *, - struct pipe_inode_info *, size_t, unsigned int); -diff --git a/include/linux/file.h b/include/linux/file.h -index 4d69123..62cffc0 100644 ---- a/include/linux/file.h -+++ b/include/linux/file.h -@@ -19,6 +19,7 @@ struct dentry; - struct path; - extern struct file *alloc_file(struct path *, fmode_t mode, - const struct file_operations *fop); -+extern struct file *get_empty_filp(void); - - static inline void fput_light(struct file *file, int fput_needed) - { -diff --git a/include/linux/fs.h b/include/linux/fs.h -index 6fd017e..c44d25d 100644 ---- a/include/linux/fs.h -+++ b/include/linux/fs.h -@@ -1149,6 +1149,7 @@ extern void fasync_free(struct fasync_struct *); - /* can be called from interrupts */ - extern void kill_fasync(struct fasync_struct **, int, int); - -+extern int setfl(int fd, struct file * filp, unsigned long arg); - extern void __f_setown(struct file *filp, struct pid *, enum pid_type, int force); - extern void f_setown(struct file *filp, unsigned long arg, int force); - extern void f_delown(struct file *filp); -@@ -1507,6 +1508,7 @@ struct file_operations { - ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); - unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); - int (*check_flags)(int); -+ int (*setfl)(struct file *, unsigned long); - int (*flock) (struct file *, int, struct file_lock *); - ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); - ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); -@@ -2662,6 +2664,7 @@ 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 update_time(struct inode *, struct timespec *, int); - extern int file_update_time(struct file *file); - - extern int generic_show_options(struct seq_file *m, struct dentry *root); -diff --git a/include/linux/mm.h b/include/linux/mm.h -index 86a977b..a2d0dbb 100644 ---- a/include/linux/mm.h -+++ b/include/linux/mm.h -@@ -1208,6 +1208,28 @@ static inline int fixup_user_fault(struct task_struct *tsk, - } - #endif - -+extern void vma_do_file_update_time(struct vm_area_struct *, const char[], int); -+extern struct file *vma_do_pr_or_file(struct vm_area_struct *, const char[], -+ int); -+extern void vma_do_get_file(struct vm_area_struct *, const char[], int); -+extern void vma_do_fput(struct vm_area_struct *, const char[], int); -+ -+#define vma_file_update_time(vma) vma_do_file_update_time(vma, __func__, \ -+ __LINE__) -+#define vma_pr_or_file(vma) vma_do_pr_or_file(vma, __func__, \ -+ __LINE__) -+#define vma_get_file(vma) vma_do_get_file(vma, __func__, __LINE__) -+#define vma_fput(vma) vma_do_fput(vma, __func__, __LINE__) -+ -+#ifndef CONFIG_MMU -+extern struct file *vmr_do_pr_or_file(struct vm_region *, const char[], int); -+extern void vmr_do_fput(struct vm_region *, const char[], int); -+ -+#define vmr_pr_or_file(region) vmr_do_pr_or_file(region, __func__, \ -+ __LINE__) -+#define vmr_fput(region) vmr_do_fput(region, __func__, __LINE__) -+#endif /* !CONFIG_MMU */ -+ - extern int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write); - extern int access_remote_vm(struct mm_struct *mm, unsigned long addr, - void *buf, int len, int write); -diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h -index 6e0b286..8f374ed 100644 ---- a/include/linux/mm_types.h -+++ b/include/linux/mm_types.h -@@ -232,6 +232,7 @@ struct vm_region { - unsigned long vm_top; /* region allocated to here */ - unsigned long vm_pgoff; /* the offset in vm_file corresponding to vm_start */ - struct file *vm_file; /* the backing file or NULL */ -+ struct file *vm_prfile; /* the virtual backing file or NULL */ - - int vm_usage; /* region usage count (access under nommu_region_sem) */ - bool vm_icache_flushed : 1; /* true if the icache has been flushed for -@@ -300,6 +301,7 @@ struct vm_area_struct { - unsigned long vm_pgoff; /* Offset (within vm_file) in PAGE_SIZE - units, *not* PAGE_CACHE_SIZE */ - struct file * vm_file; /* File we map to (can be NULL). */ -+ struct file *vm_prfile; /* shadow of vm_file */ - void * vm_private_data; /* was vm_pte (shared mem) */ - - #ifndef CONFIG_MMU -diff --git a/include/linux/splice.h b/include/linux/splice.h -index da2751d..2e0fca6 100644 ---- a/include/linux/splice.h -+++ b/include/linux/splice.h -@@ -83,4 +83,10 @@ extern void splice_shrink_spd(struct splice_pipe_desc *); - extern void spd_release_page(struct splice_pipe_desc *, unsigned int); - - extern const struct pipe_buf_operations page_cache_pipe_buf_ops; -+ -+extern long do_splice_from(struct pipe_inode_info *pipe, struct file *out, -+ loff_t *ppos, size_t len, unsigned int flags); -+extern long do_splice_to(struct file *in, loff_t *ppos, -+ struct pipe_inode_info *pipe, size_t len, -+ unsigned int flags); - #endif -diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild -index 8523f9b..11f8f74 100644 ---- a/include/uapi/linux/Kbuild -+++ b/include/uapi/linux/Kbuild -@@ -56,6 +56,7 @@ header-y += atmppp.h - header-y += atmsap.h - header-y += atmsvc.h - header-y += audit.h -+header-y += aufs_type.h - header-y += auto_fs.h - header-y += auto_fs4.h - header-y += auxvec.h -diff --git a/include/uapi/linux/aufs_type.h b/include/uapi/linux/aufs_type.h -new file mode 100644 -index 0000000..75915f8 ---- /dev/null -+++ b/include/uapi/linux/aufs_type.h -@@ -0,0 +1,419 @@ -+/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima -+ * -+ * This program, aufs 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 . -+ */ -+ -+#ifndef __AUFS_TYPE_H__ -+#define __AUFS_TYPE_H__ -+ -+#define AUFS_NAME "aufs" -+ -+#ifdef __KERNEL__ -+/* -+ * define it before including all other headers. -+ * sched.h may use pr_* macros before defining "current", so define the -+ * no-current version first, and re-define later. -+ */ -+#define pr_fmt(fmt) AUFS_NAME " %s:%d: " fmt, __func__, __LINE__ -+#include -+#undef pr_fmt -+#define pr_fmt(fmt) \ -+ AUFS_NAME " %s:%d:%.*s[%d]: " fmt, __func__, __LINE__, \ -+ (int)sizeof(current->comm), current->comm, current->pid -+#else -+#include -+#include -+#endif /* __KERNEL__ */ -+ -+#include -+ -+#define AUFS_VERSION "3.18.25+-20160509" -+ -+/* todo? move this to linux-2.6.19/include/magic.h */ -+#define AUFS_SUPER_MAGIC ('a' << 24 | 'u' << 16 | 'f' << 8 | 's') -+ -+/* ---------------------------------------------------------------------- */ -+ -+#ifdef CONFIG_AUFS_BRANCH_MAX_127 -+typedef int8_t aufs_bindex_t; -+#define AUFS_BRANCH_MAX 127 -+#else -+typedef int16_t aufs_bindex_t; -+#ifdef CONFIG_AUFS_BRANCH_MAX_511 -+#define AUFS_BRANCH_MAX 511 -+#elif defined(CONFIG_AUFS_BRANCH_MAX_1023) -+#define AUFS_BRANCH_MAX 1023 -+#elif defined(CONFIG_AUFS_BRANCH_MAX_32767) -+#define AUFS_BRANCH_MAX 32767 -+#endif -+#endif -+ -+#ifdef __KERNEL__ -+#ifndef AUFS_BRANCH_MAX -+#error unknown CONFIG_AUFS_BRANCH_MAX value -+#endif -+#endif /* __KERNEL__ */ -+ -+/* ---------------------------------------------------------------------- */ -+ -+#define AUFS_FSTYPE AUFS_NAME -+ -+#define AUFS_ROOT_INO 2 -+#define AUFS_FIRST_INO 11 -+ -+#define AUFS_WH_PFX ".wh." -+#define AUFS_WH_PFX_LEN ((int)sizeof(AUFS_WH_PFX) - 1) -+#define AUFS_WH_TMP_LEN 4 -+/* a limit for rmdir/rename a dir and copyup */ -+#define AUFS_MAX_NAMELEN (NAME_MAX \ -+ - AUFS_WH_PFX_LEN * 2 /* doubly whiteouted */\ -+ - 1 /* dot */\ -+ - AUFS_WH_TMP_LEN) /* hex */ -+#define AUFS_XINO_FNAME "." AUFS_NAME ".xino" -+#define AUFS_XINO_DEFPATH "/tmp/" AUFS_XINO_FNAME -+#define AUFS_XINO_DEF_SEC 30 /* seconds */ -+#define AUFS_XINO_DEF_TRUNC 45 /* percentage */ -+#define AUFS_DIRWH_DEF 3 -+#define AUFS_RDCACHE_DEF 10 /* seconds */ -+#define AUFS_RDCACHE_MAX 3600 /* seconds */ -+#define AUFS_RDBLK_DEF 512 /* bytes */ -+#define AUFS_RDHASH_DEF 32 -+#define AUFS_WKQ_NAME AUFS_NAME "d" -+#define AUFS_MFS_DEF_SEC 30 /* seconds */ -+#define AUFS_MFS_MAX_SEC 3600 /* seconds */ -+#define AUFS_FHSM_CACHE_DEF_SEC 30 /* seconds */ -+#define AUFS_PLINK_WARN 50 /* number of plinks in a single bucket */ -+ -+/* pseudo-link maintenace under /proc */ -+#define AUFS_PLINK_MAINT_NAME "plink_maint" -+#define AUFS_PLINK_MAINT_DIR "fs/" AUFS_NAME -+#define AUFS_PLINK_MAINT_PATH AUFS_PLINK_MAINT_DIR "/" AUFS_PLINK_MAINT_NAME -+ -+#define AUFS_DIROPQ_NAME AUFS_WH_PFX ".opq" /* whiteouted doubly */ -+#define AUFS_WH_DIROPQ AUFS_WH_PFX AUFS_DIROPQ_NAME -+ -+#define AUFS_BASE_NAME AUFS_WH_PFX AUFS_NAME -+#define AUFS_PLINKDIR_NAME AUFS_WH_PFX "plnk" -+#define AUFS_ORPHDIR_NAME AUFS_WH_PFX "orph" -+ -+/* doubly whiteouted */ -+#define AUFS_WH_BASE AUFS_WH_PFX AUFS_BASE_NAME -+#define AUFS_WH_PLINKDIR AUFS_WH_PFX AUFS_PLINKDIR_NAME -+#define AUFS_WH_ORPHDIR AUFS_WH_PFX AUFS_ORPHDIR_NAME -+ -+/* branch permissions and attributes */ -+#define AUFS_BRPERM_RW "rw" -+#define AUFS_BRPERM_RO "ro" -+#define AUFS_BRPERM_RR "rr" -+#define AUFS_BRATTR_COO_REG "coo_reg" -+#define AUFS_BRATTR_COO_ALL "coo_all" -+#define AUFS_BRATTR_FHSM "fhsm" -+#define AUFS_BRATTR_UNPIN "unpin" -+#define AUFS_BRATTR_ICEX "icex" -+#define AUFS_BRATTR_ICEX_SEC "icexsec" -+#define AUFS_BRATTR_ICEX_SYS "icexsys" -+#define AUFS_BRATTR_ICEX_TR "icextr" -+#define AUFS_BRATTR_ICEX_USR "icexusr" -+#define AUFS_BRATTR_ICEX_OTH "icexoth" -+#define AUFS_BRRATTR_WH "wh" -+#define AUFS_BRWATTR_NLWH "nolwh" -+#define AUFS_BRWATTR_MOO "moo" -+ -+#define AuBrPerm_RW 1 /* writable, hardlinkable wh */ -+#define AuBrPerm_RO (1 << 1) /* readonly */ -+#define AuBrPerm_RR (1 << 2) /* natively readonly */ -+#define AuBrPerm_Mask (AuBrPerm_RW | AuBrPerm_RO | AuBrPerm_RR) -+ -+#define AuBrAttr_COO_REG (1 << 3) /* copy-up on open */ -+#define AuBrAttr_COO_ALL (1 << 4) -+#define AuBrAttr_COO_Mask (AuBrAttr_COO_REG | AuBrAttr_COO_ALL) -+ -+#define AuBrAttr_FHSM (1 << 5) /* file-based hsm */ -+#define AuBrAttr_UNPIN (1 << 6) /* rename-able top dir of -+ branch. meaningless since -+ linux-3.18-rc1 */ -+ -+/* ignore error in copying XATTR */ -+#define AuBrAttr_ICEX_SEC (1 << 7) -+#define AuBrAttr_ICEX_SYS (1 << 8) -+#define AuBrAttr_ICEX_TR (1 << 9) -+#define AuBrAttr_ICEX_USR (1 << 10) -+#define AuBrAttr_ICEX_OTH (1 << 11) -+#define AuBrAttr_ICEX (AuBrAttr_ICEX_SEC \ -+ | AuBrAttr_ICEX_SYS \ -+ | AuBrAttr_ICEX_TR \ -+ | AuBrAttr_ICEX_USR \ -+ | AuBrAttr_ICEX_OTH) -+ -+#define AuBrRAttr_WH (1 << 12) /* whiteout-able */ -+#define AuBrRAttr_Mask AuBrRAttr_WH -+ -+#define AuBrWAttr_NoLinkWH (1 << 13) /* un-hardlinkable whiteouts */ -+#define AuBrWAttr_MOO (1 << 14) /* move-up on open */ -+#define AuBrWAttr_Mask (AuBrWAttr_NoLinkWH | AuBrWAttr_MOO) -+ -+#define AuBrAttr_CMOO_Mask (AuBrAttr_COO_Mask | AuBrWAttr_MOO) -+ -+/* #warning test userspace */ -+#ifdef __KERNEL__ -+#ifndef CONFIG_AUFS_FHSM -+#undef AuBrAttr_FHSM -+#define AuBrAttr_FHSM 0 -+#endif -+#ifndef CONFIG_AUFS_XATTR -+#undef AuBrAttr_ICEX -+#define AuBrAttr_ICEX 0 -+#undef AuBrAttr_ICEX_SEC -+#define AuBrAttr_ICEX_SEC 0 -+#undef AuBrAttr_ICEX_SYS -+#define AuBrAttr_ICEX_SYS 0 -+#undef AuBrAttr_ICEX_TR -+#define AuBrAttr_ICEX_TR 0 -+#undef AuBrAttr_ICEX_USR -+#define AuBrAttr_ICEX_USR 0 -+#undef AuBrAttr_ICEX_OTH -+#define AuBrAttr_ICEX_OTH 0 -+#endif -+#endif -+ -+/* the longest combination */ -+/* AUFS_BRATTR_ICEX and AUFS_BRATTR_ICEX_TR don't affect here */ -+#define AuBrPermStrSz sizeof(AUFS_BRPERM_RW \ -+ "+" AUFS_BRATTR_COO_REG \ -+ "+" AUFS_BRATTR_FHSM \ -+ "+" AUFS_BRATTR_UNPIN \ -+ "+" AUFS_BRATTR_ICEX_SEC \ -+ "+" AUFS_BRATTR_ICEX_SYS \ -+ "+" AUFS_BRATTR_ICEX_USR \ -+ "+" AUFS_BRATTR_ICEX_OTH \ -+ "+" AUFS_BRWATTR_NLWH) -+ -+typedef struct { -+ char a[AuBrPermStrSz]; -+} au_br_perm_str_t; -+ -+static inline int au_br_writable(int brperm) -+{ -+ return brperm & AuBrPerm_RW; -+} -+ -+static inline int au_br_whable(int brperm) -+{ -+ return brperm & (AuBrPerm_RW | AuBrRAttr_WH); -+} -+ -+static inline int au_br_wh_linkable(int brperm) -+{ -+ return !(brperm & AuBrWAttr_NoLinkWH); -+} -+ -+static inline int au_br_cmoo(int brperm) -+{ -+ return brperm & AuBrAttr_CMOO_Mask; -+} -+ -+static inline int au_br_fhsm(int brperm) -+{ -+ return brperm & AuBrAttr_FHSM; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* ioctl */ -+enum { -+ /* readdir in userspace */ -+ AuCtl_RDU, -+ AuCtl_RDU_INO, -+ -+ AuCtl_WBR_FD, /* pathconf wrapper */ -+ AuCtl_IBUSY, /* busy inode */ -+ AuCtl_MVDOWN, /* move-down */ -+ AuCtl_BR, /* info about branches */ -+ AuCtl_FHSM_FD /* connection for fhsm */ -+}; -+ -+/* borrowed from linux/include/linux/kernel.h */ -+#ifndef ALIGN -+#define ALIGN(x, a) __ALIGN_MASK(x, (typeof(x))(a)-1) -+#define __ALIGN_MASK(x, mask) (((x)+(mask))&~(mask)) -+#endif -+ -+/* borrowed from linux/include/linux/compiler-gcc3.h */ -+#ifndef __aligned -+#define __aligned(x) __attribute__((aligned(x))) -+#endif -+ -+#ifdef __KERNEL__ -+#ifndef __packed -+#define __packed __attribute__((packed)) -+#endif -+#endif -+ -+struct au_rdu_cookie { -+ uint64_t h_pos; -+ int16_t bindex; -+ uint8_t flags; -+ uint8_t pad; -+ uint32_t generation; -+} __aligned(8); -+ -+struct au_rdu_ent { -+ uint64_t ino; -+ int16_t bindex; -+ uint8_t type; -+ uint8_t nlen; -+ uint8_t wh; -+ char name[0]; -+} __aligned(8); -+ -+static inline int au_rdu_len(int nlen) -+{ -+ /* include the terminating NULL */ -+ return ALIGN(sizeof(struct au_rdu_ent) + nlen + 1, -+ sizeof(uint64_t)); -+} -+ -+union au_rdu_ent_ul { -+ struct au_rdu_ent __user *e; -+ uint64_t ul; -+}; -+ -+enum { -+ AufsCtlRduV_SZ, -+ AufsCtlRduV_End -+}; -+ -+struct aufs_rdu { -+ /* input */ -+ union { -+ uint64_t sz; /* AuCtl_RDU */ -+ uint64_t nent; /* AuCtl_RDU_INO */ -+ }; -+ union au_rdu_ent_ul ent; -+ uint16_t verify[AufsCtlRduV_End]; -+ -+ /* input/output */ -+ uint32_t blk; -+ -+ /* output */ -+ union au_rdu_ent_ul tail; -+ /* number of entries which were added in a single call */ -+ uint64_t rent; -+ uint8_t full; -+ uint8_t shwh; -+ -+ struct au_rdu_cookie cookie; -+} __aligned(8); -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct aufs_wbr_fd { -+ uint32_t oflags; -+ int16_t brid; -+} __aligned(8); -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct aufs_ibusy { -+ uint64_t ino, h_ino; -+ int16_t bindex; -+} __aligned(8); -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* error code for move-down */ -+/* the actual message strings are implemented in aufs-util.git */ -+enum { -+ EAU_MVDOWN_OPAQUE = 1, -+ EAU_MVDOWN_WHITEOUT, -+ EAU_MVDOWN_UPPER, -+ EAU_MVDOWN_BOTTOM, -+ EAU_MVDOWN_NOUPPER, -+ EAU_MVDOWN_NOLOWERBR, -+ EAU_Last -+}; -+ -+/* flags for move-down */ -+#define AUFS_MVDOWN_DMSG 1 -+#define AUFS_MVDOWN_OWLOWER (1 << 1) /* overwrite lower */ -+#define AUFS_MVDOWN_KUPPER (1 << 2) /* keep upper */ -+#define AUFS_MVDOWN_ROLOWER (1 << 3) /* do even if lower is RO */ -+#define AUFS_MVDOWN_ROLOWER_R (1 << 4) /* did on lower RO */ -+#define AUFS_MVDOWN_ROUPPER (1 << 5) /* do even if upper is RO */ -+#define AUFS_MVDOWN_ROUPPER_R (1 << 6) /* did on upper RO */ -+#define AUFS_MVDOWN_BRID_UPPER (1 << 7) /* upper brid */ -+#define AUFS_MVDOWN_BRID_LOWER (1 << 8) /* lower brid */ -+#define AUFS_MVDOWN_FHSM_LOWER (1 << 9) /* find fhsm attr for lower */ -+#define AUFS_MVDOWN_STFS (1 << 10) /* req. stfs */ -+#define AUFS_MVDOWN_STFS_FAILED (1 << 11) /* output: stfs is unusable */ -+#define AUFS_MVDOWN_BOTTOM (1 << 12) /* output: no more lowers */ -+ -+/* index for move-down */ -+enum { -+ AUFS_MVDOWN_UPPER, -+ AUFS_MVDOWN_LOWER, -+ AUFS_MVDOWN_NARRAY -+}; -+ -+/* -+ * additional info of move-down -+ * number of free blocks and inodes. -+ * subset of struct kstatfs, but smaller and always 64bit. -+ */ -+struct aufs_stfs { -+ uint64_t f_blocks; -+ uint64_t f_bavail; -+ uint64_t f_files; -+ uint64_t f_ffree; -+}; -+ -+struct aufs_stbr { -+ int16_t brid; /* optional input */ -+ int16_t bindex; /* output */ -+ struct aufs_stfs stfs; /* output when AUFS_MVDOWN_STFS set */ -+} __aligned(8); -+ -+struct aufs_mvdown { -+ uint32_t flags; /* input/output */ -+ struct aufs_stbr stbr[AUFS_MVDOWN_NARRAY]; /* input/output */ -+ int8_t au_errno; /* output */ -+} __aligned(8); -+ -+/* ---------------------------------------------------------------------- */ -+ -+union aufs_brinfo { -+ /* PATH_MAX may differ between kernel-space and user-space */ -+ char _spacer[4096]; -+ struct { -+ int16_t id; -+ int perm; -+ char path[0]; -+ }; -+} __aligned(8); -+ -+/* ---------------------------------------------------------------------- */ -+ -+#define AuCtlType 'A' -+#define AUFS_CTL_RDU _IOWR(AuCtlType, AuCtl_RDU, struct aufs_rdu) -+#define AUFS_CTL_RDU_INO _IOWR(AuCtlType, AuCtl_RDU_INO, struct aufs_rdu) -+#define AUFS_CTL_WBR_FD _IOW(AuCtlType, AuCtl_WBR_FD, \ -+ struct aufs_wbr_fd) -+#define AUFS_CTL_IBUSY _IOWR(AuCtlType, AuCtl_IBUSY, struct aufs_ibusy) -+#define AUFS_CTL_MVDOWN _IOWR(AuCtlType, AuCtl_MVDOWN, \ -+ struct aufs_mvdown) -+#define AUFS_CTL_BRINFO _IOW(AuCtlType, AuCtl_BR, union aufs_brinfo) -+#define AUFS_CTL_FHSM_FD _IOW(AuCtlType, AuCtl_FHSM_FD, int) -+ -+#endif /* __AUFS_TYPE_H__ */ -diff --git a/kernel/fork.c b/kernel/fork.c -index 0a4f601..67ecb91 100644 ---- a/kernel/fork.c -+++ b/kernel/fork.c -@@ -430,7 +430,7 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) - struct inode *inode = file_inode(file); - struct address_space *mapping = file->f_mapping; - -- get_file(file); -+ vma_get_file(tmp); - if (tmp->vm_flags & VM_DENYWRITE) - atomic_dec(&inode->i_writecount); - mutex_lock(&mapping->i_mmap_mutex); -diff --git a/mm/Makefile b/mm/Makefile -index 8405eb0..e0bda2d 100644 ---- a/mm/Makefile -+++ b/mm/Makefile -@@ -18,7 +18,7 @@ obj-y := filemap.o mempool.o oom_kill.o \ - mm_init.o mmu_context.o percpu.o slab_common.o \ - compaction.o vmacache.o \ - interval_tree.o list_lru.o workingset.o \ -- iov_iter.o debug.o $(mmu-y) -+ iov_iter.o prfile.o debug.o $(mmu-y) - - obj-y += init-mm.o - -diff --git a/mm/filemap.c b/mm/filemap.c -index 7e6ab98..2fe1e57 100644 ---- a/mm/filemap.c -+++ b/mm/filemap.c -@@ -2063,7 +2063,7 @@ int filemap_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) - int ret = VM_FAULT_LOCKED; - - sb_start_pagefault(inode->i_sb); -- file_update_time(vma->vm_file); -+ vma_file_update_time(vma); - lock_page(page); - if (page->mapping != inode->i_mapping) { - unlock_page(page); -diff --git a/mm/fremap.c b/mm/fremap.c -index 72b8fa3..a00bbf0 100644 ---- a/mm/fremap.c -+++ b/mm/fremap.c -@@ -224,16 +224,28 @@ get_write_lock: - */ - if (mapping_cap_account_dirty(mapping)) { - unsigned long addr; -- struct file *file = get_file(vma->vm_file); -+ struct file *file = vma->vm_file, -+ *prfile = vma->vm_prfile; -+ - /* mmap_region may free vma; grab the info now */ - vm_flags = vma->vm_flags; - -+ vma_get_file(vma); - addr = mmap_region(file, start, size, vm_flags, pgoff); -- fput(file); -+ vma_fput(vma); - if (IS_ERR_VALUE(addr)) { - err = addr; - } else { - BUG_ON(addr != start); -+ if (prfile) { -+ struct vm_area_struct *new_vma; -+ -+ new_vma = find_vma(mm, addr); -+ if (!new_vma->vm_prfile) -+ new_vma->vm_prfile = prfile; -+ if (new_vma != vma) -+ get_file(prfile); -+ } - err = 0; - } - goto out_freed; -diff --git a/mm/memory.c b/mm/memory.c -index 90fb265..844df2e 100644 ---- a/mm/memory.c -+++ b/mm/memory.c -@@ -2156,7 +2156,7 @@ reuse: - - /* file_update_time outside page_lock */ - if (vma->vm_file) -- file_update_time(vma->vm_file); -+ vma_file_update_time(vma); - } - put_page(dirty_page); - if (page_mkwrite) { -diff --git a/mm/mmap.c b/mm/mmap.c -index f88b4f9..9994987 100644 ---- a/mm/mmap.c -+++ b/mm/mmap.c -@@ -277,7 +277,7 @@ static struct vm_area_struct *remove_vma(struct vm_area_struct *vma) - if (vma->vm_ops && vma->vm_ops->close) - vma->vm_ops->close(vma); - if (vma->vm_file) -- fput(vma->vm_file); -+ vma_fput(vma); - mpol_put(vma_policy(vma)); - kmem_cache_free(vm_area_cachep, vma); - return next; -@@ -895,7 +895,7 @@ again: remove_next = 1 + (end > next->vm_end); - if (remove_next) { - if (file) { - uprobe_munmap(next, next->vm_start, next->vm_end); -- fput(file); -+ vma_fput(vma); - } - if (next->anon_vma) - anon_vma_merge(vma, next); -@@ -1680,8 +1680,8 @@ out: - return addr; - - unmap_and_free_vma: -+ vma_fput(vma); - vma->vm_file = NULL; -- fput(file); - - /* Undo any partial mapping done by a device driver. */ - unmap_region(mm, vma, prev, vma->vm_start, vma->vm_end); -@@ -2480,7 +2480,7 @@ static int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma, - goto out_free_mpol; - - if (new->vm_file) -- get_file(new->vm_file); -+ vma_get_file(new); - - if (new->vm_ops && new->vm_ops->open) - new->vm_ops->open(new); -@@ -2499,7 +2499,7 @@ static int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma, - if (new->vm_ops && new->vm_ops->close) - new->vm_ops->close(new); - if (new->vm_file) -- fput(new->vm_file); -+ vma_fput(new); - unlink_anon_vmas(new); - out_free_mpol: - mpol_put(vma_policy(new)); -@@ -2889,7 +2889,7 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap, - if (anon_vma_clone(new_vma, vma)) - goto out_free_mempol; - if (new_vma->vm_file) -- get_file(new_vma->vm_file); -+ vma_get_file(new_vma); - if (new_vma->vm_ops && new_vma->vm_ops->open) - new_vma->vm_ops->open(new_vma); - vma_link(mm, new_vma, prev, rb_link, rb_parent); -diff --git a/mm/nommu.c b/mm/nommu.c -index b5ba5bc..a7662fc 100644 ---- a/mm/nommu.c -+++ b/mm/nommu.c -@@ -658,7 +658,7 @@ static void __put_nommu_region(struct vm_region *region) - up_write(&nommu_region_sem); - - if (region->vm_file) -- fput(region->vm_file); -+ vmr_fput(region); - - /* IO memory and memory shared directly out of the pagecache - * from ramfs/tmpfs mustn't be released here */ -@@ -823,7 +823,7 @@ static void delete_vma(struct mm_struct *mm, struct vm_area_struct *vma) - if (vma->vm_ops && vma->vm_ops->close) - vma->vm_ops->close(vma); - if (vma->vm_file) -- fput(vma->vm_file); -+ vma_fput(vma); - put_nommu_region(vma->vm_region); - kmem_cache_free(vm_area_cachep, vma); - } -@@ -1385,7 +1385,7 @@ unsigned long do_mmap_pgoff(struct file *file, - goto error_just_free; - } - } -- fput(region->vm_file); -+ vmr_fput(region); - kmem_cache_free(vm_region_jar, region); - region = pregion; - result = start; -@@ -1461,10 +1461,10 @@ error_just_free: - up_write(&nommu_region_sem); - error: - if (region->vm_file) -- fput(region->vm_file); -+ vmr_fput(region); - kmem_cache_free(vm_region_jar, region); - if (vma->vm_file) -- fput(vma->vm_file); -+ vma_fput(vma); - kmem_cache_free(vm_area_cachep, vma); - kleave(" = %d", ret); - return ret; -diff --git a/mm/prfile.c b/mm/prfile.c -new file mode 100644 -index 0000000..532e518 ---- /dev/null -+++ b/mm/prfile.c -@@ -0,0 +1,86 @@ -+/* -+ * Mainly for aufs which mmap(2) diffrent file and wants to print different path -+ * in /proc/PID/maps. -+ * Call these functions via macros defined in linux/mm.h. -+ * -+ * See Documentation/filesystems/aufs/design/06mmap.txt -+ * -+ * Copyright (c) 2014 Junjro R. Okajima -+ * Copyright (c) 2014 Ian Campbell -+ */ -+ -+#include -+#include -+#include -+ -+/* #define PRFILE_TRACE */ -+static inline void prfile_trace(struct file *f, struct file *pr, -+ const char func[], int line, const char func2[]) -+{ -+#ifdef PRFILE_TRACE -+ if (pr) -+ pr_info("%s:%d: %s, %s\n", func, line, func2, -+ f ? (char *)f->f_dentry->d_name.name : "(null)"); -+#endif -+} -+ -+void vma_do_file_update_time(struct vm_area_struct *vma, const char func[], -+ int line) -+{ -+ struct file *f = vma->vm_file, *pr = vma->vm_prfile; -+ -+ prfile_trace(f, pr, func, line, __func__); -+ file_update_time(f); -+ if (f && pr) -+ file_update_time(pr); -+} -+ -+struct file *vma_do_pr_or_file(struct vm_area_struct *vma, const char func[], -+ int line) -+{ -+ struct file *f = vma->vm_file, *pr = vma->vm_prfile; -+ -+ prfile_trace(f, pr, func, line, __func__); -+ return (f && pr) ? pr : f; -+} -+ -+void vma_do_get_file(struct vm_area_struct *vma, const char func[], int line) -+{ -+ struct file *f = vma->vm_file, *pr = vma->vm_prfile; -+ -+ prfile_trace(f, pr, func, line, __func__); -+ get_file(f); -+ if (f && pr) -+ get_file(pr); -+} -+ -+void vma_do_fput(struct vm_area_struct *vma, const char func[], int line) -+{ -+ struct file *f = vma->vm_file, *pr = vma->vm_prfile; -+ -+ prfile_trace(f, pr, func, line, __func__); -+ fput(f); -+ if (f && pr) -+ fput(pr); -+} -+ -+#ifndef CONFIG_MMU -+struct file *vmr_do_pr_or_file(struct vm_region *region, const char func[], -+ int line) -+{ -+ struct file *f = region->vm_file, *pr = region->vm_prfile; -+ -+ prfile_trace(f, pr, func, line, __func__); -+ return (f && pr) ? pr : f; -+} -+ -+void vmr_do_fput(struct vm_region *region, const char func[], int line) -+{ -+ struct file *f = region->vm_file, *pr = region->vm_prfile; -+ -+ prfile_trace(f, pr, func, line, __func__); -+ fput(f); -+ if (f && pr) -+ fput(pr); -+} -+#endif /* !CONFIG_MMU */ --- -2.1.4 - diff --git a/packages/base/any/kernels/3.18.25/patches/backport-some-kernel-patches-based-on-3.18.25.patch b/packages/base/any/kernels/3.18.25/patches/backport-some-kernel-patches-based-on-3.18.25.patch deleted file mode 100644 index 6a6e36f5..00000000 --- a/packages/base/any/kernels/3.18.25/patches/backport-some-kernel-patches-based-on-3.18.25.patch +++ /dev/null @@ -1,11095 +0,0 @@ -From fdf22b15468bed6aac4e52e83903d8e010fbe60b Mon Sep 17 00:00:00 2001 -From: Shengzhou Liu -Date: Fri, 23 Sep 2016 14:58:06 +0800 -Subject: [PATCH 2/2] Backport some kernel patches based on 3.18.25 - -Fixup dpaa2-eth, phy, pcie, gicv3, sdhc, i2c. -Verified on LS2080A/LS2088A RDB. ---- - Documentation/devicetree/bindings/arm/gic.txt | 8 +- - .../devicetree/bindings/clock/qoriq-clock.txt | 64 +- - Documentation/devicetree/bindings/i2c/i2c-imx.txt | 11 + - .../devicetree/bindings/i2c/i2c-mux-pca954x.txt | 3 + - .../bindings/memory-controllers/fsl/ifc.txt | 3 + - Documentation/devicetree/of_selftest.txt | 20 +- - Documentation/devicetree/todo.txt | 1 - - arch/arm64/Kconfig | 1 + - arch/arm64/include/asm/device.h | 1 + - arch/arm64/include/asm/dma-mapping.h | 16 +- - arch/powerpc/include/asm/mpc85xx.h | 94 -- - arch/powerpc/platforms/85xx/mpc85xx_mds.c | 2 +- - arch/powerpc/platforms/85xx/mpc85xx_rdb.c | 2 +- - arch/powerpc/platforms/85xx/p1022_ds.c | 2 +- - arch/powerpc/platforms/85xx/p1022_rdk.c | 2 +- - arch/powerpc/platforms/85xx/smp.c | 2 +- - arch/powerpc/platforms/85xx/twr_p102x.c | 2 +- - arch/powerpc/platforms/86xx/mpc8610_hpcd.c | 2 +- - arch/x86/pci/xen.c | 4 + - drivers/clk/Kconfig | 10 +- - drivers/clk/Makefile | 2 +- - drivers/clk/clk-qoriq.c | 1256 ++++++++++++++++++++ - drivers/cpufreq/Kconfig.powerpc | 2 +- - drivers/i2c/busses/Kconfig | 4 +- - drivers/i2c/busses/i2c-imx.c | 373 +++++- - drivers/i2c/muxes/i2c-mux-pca9541.c | 4 +- - drivers/i2c/muxes/i2c-mux-pca954x.c | 57 +- - drivers/iommu/fsl_pamu.c | 2 +- - drivers/iommu/io-pgtable-arm.c | 15 +- - drivers/irqchip/Kconfig | 8 + - drivers/irqchip/Makefile | 1 + - drivers/irqchip/irq-gic-common.c | 18 +- - drivers/irqchip/irq-gic-common.h | 2 +- - drivers/irqchip/irq-gic-v2m.c | 333 ++++++ - drivers/irqchip/irq-gic-v3-its.c | 6 +- - drivers/irqchip/irq-gic-v3.c | 66 +- - drivers/irqchip/irq-gic.c | 90 +- - drivers/irqchip/irq-hip04.c | 9 +- - drivers/memory/Kconfig | 2 +- - drivers/memory/fsl_ifc.c | 77 +- - drivers/mfd/vexpress-sysreg.c | 2 +- - drivers/mmc/card/block.c | 4 + - drivers/mmc/host/Kconfig | 10 +- - drivers/mmc/host/sdhci-esdhc.h | 9 +- - drivers/mmc/host/sdhci-of-esdhc.c | 680 +++++++++-- - drivers/mmc/host/sdhci.c | 250 ++-- - drivers/mmc/host/sdhci.h | 42 + - drivers/mtd/nand/Kconfig | 2 +- - drivers/mtd/nand/fsl_ifc_nand.c | 301 ++--- - drivers/net/ethernet/freescale/gianfar.c | 6 +- - drivers/net/phy/Kconfig | 14 +- - drivers/net/phy/Makefile | 4 +- - drivers/net/phy/at803x.c | 4 + - drivers/net/phy/fixed.c | 336 ------ - drivers/net/phy/fixed_phy.c | 370 ++++++ - drivers/net/phy/marvell.c | 11 + - drivers/net/phy/mdio_bus.c | 34 +- - drivers/net/phy/phy.c | 19 +- - drivers/net/phy/phy_device.c | 90 +- - drivers/net/phy/realtek.c | 82 +- - drivers/of/base.c | 53 +- - drivers/of/dynamic.c | 13 - - drivers/of/fdt.c | 30 +- - drivers/of/pdt.c | 27 +- - drivers/of/selftest.c | 71 +- - drivers/pci/Makefile | 1 + - drivers/pci/access.c | 87 ++ - drivers/pci/host/Kconfig | 2 +- - drivers/pci/host/pci-layerscape.c | 86 +- - drivers/pci/host/pcie-designware.c | 14 + - drivers/pci/host/pcie-designware.h | 1 + - drivers/pci/msi.c | 5 + - drivers/pci/pci.c | 1 + - drivers/pci/pcie/portdrv_core.c | 31 +- - drivers/pci/probe.c | 1 + - drivers/pci/remove.c | 2 + - drivers/pci/setup-bus.c | 1 + - drivers/pci/setup-irq.c | 1 + - drivers/soc/Kconfig | 13 + - drivers/soc/Makefile | 1 + - drivers/soc/fsl/Kconfig | 6 + - drivers/soc/fsl/Kconfig.arm | 25 + - drivers/soc/fsl/Makefile | 6 + - drivers/soc/fsl/guts.c | 123 ++ - drivers/soc/fsl/ls1/Kconfig | 11 + - drivers/soc/fsl/ls1/Makefile | 1 + - drivers/soc/fsl/ls1/ftm_alarm.c | 274 +++++ - drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c | 273 +++-- - drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.h | 48 +- - drivers/staging/fsl-dpaa2/ethernet/dpaa2-ethtool.c | 553 ++++----- - drivers/staging/fsl-dpaa2/mac/mac.c | 4 +- - drivers/staging/fsl-mc/bus/dprc-driver.c | 2 +- - drivers/staging/fsl-mc/include/mc-private.h | 2 +- - drivers/usb/host/xhci.c | 6 +- - include/linux/fsl/guts.h | 99 +- - include/linux/fsl/svr.h | 95 ++ - include/linux/fsl_ifc.h | 116 +- - include/linux/interrupt.h | 14 + - include/linux/iommu.h | 1 + - include/linux/irq.h | 8 + - include/linux/irqchip/arm-gic-v3.h | 12 + - include/linux/irqchip/arm-gic.h | 2 + - include/linux/irqdomain.h | 1 + - include/linux/mmc/sdhci.h | 16 +- - include/linux/of.h | 11 +- - include/linux/of_pdt.h | 3 +- - include/linux/pci.h | 11 + - include/linux/phy.h | 1 + - include/linux/phy_fixed.h | 11 +- - kernel/irq/chip.c | 58 +- - kernel/irq/manage.c | 91 ++ - kernel/irq/msi.c | 13 +- - sound/soc/fsl/mpc8610_hpcd.c | 2 +- - sound/soc/fsl/p1022_ds.c | 2 +- - sound/soc/fsl/p1022_rdk.c | 2 +- - 115 files changed, 5570 insertions(+), 1621 deletions(-) - delete mode 100644 arch/powerpc/include/asm/mpc85xx.h - create mode 100644 drivers/clk/clk-qoriq.c - create mode 100644 drivers/irqchip/irq-gic-v2m.c - delete mode 100644 drivers/net/phy/fixed.c - create mode 100644 drivers/net/phy/fixed_phy.c - create mode 100644 drivers/soc/fsl/Kconfig - create mode 100644 drivers/soc/fsl/Kconfig.arm - create mode 100644 drivers/soc/fsl/Makefile - create mode 100644 drivers/soc/fsl/guts.c - create mode 100644 drivers/soc/fsl/ls1/Kconfig - create mode 100644 drivers/soc/fsl/ls1/Makefile - create mode 100644 drivers/soc/fsl/ls1/ftm_alarm.c - create mode 100644 include/linux/fsl/svr.h - -diff --git a/Documentation/devicetree/bindings/arm/gic.txt b/Documentation/devicetree/bindings/arm/gic.txt -index c7d2fa1..e87d3d7 100644 ---- a/Documentation/devicetree/bindings/arm/gic.txt -+++ b/Documentation/devicetree/bindings/arm/gic.txt -@@ -31,12 +31,16 @@ Main node required properties: - The 3rd cell is the flags, encoded as follows: - bits[3:0] trigger type and level flags. - 1 = low-to-high edge triggered -- 2 = high-to-low edge triggered -+ 2 = high-to-low edge triggered (invalid for SPIs) - 4 = active high level-sensitive -- 8 = active low level-sensitive -+ 8 = active low level-sensitive (invalid for SPIs). - bits[15:8] PPI interrupt cpu mask. Each bit corresponds to each of - the 8 possible cpus attached to the GIC. A bit set to '1' indicated - the interrupt is wired to that CPU. Only valid for PPI interrupts. -+ Also note that the configurability of PPI interrupts is IMPLEMENTATION -+ DEFINED and as such not guaranteed to be present (most SoC available -+ in 2014 seem to ignore the setting of this flag and use the hardware -+ default value). - - - reg : Specifies base physical address(s) and size of the GIC registers. The - first region is the GIC distributor register base and size. The 2nd region is -diff --git a/Documentation/devicetree/bindings/clock/qoriq-clock.txt b/Documentation/devicetree/bindings/clock/qoriq-clock.txt -index 5666812..128fc72 100644 ---- a/Documentation/devicetree/bindings/clock/qoriq-clock.txt -+++ b/Documentation/devicetree/bindings/clock/qoriq-clock.txt -@@ -1,6 +1,6 @@ --* Clock Block on Freescale CoreNet Platforms -+* Clock Block on Freescale QorIQ Platforms - --Freescale CoreNet chips take primary clocking input from the external -+Freescale QorIQ chips take primary clocking input from the external - SYSCLK signal. The SYSCLK input (frequency) is multiplied using - multiple phase locked loops (PLL) to create a variety of frequencies - which can then be passed to a variety of internal logic, including -@@ -13,14 +13,16 @@ which the chip complies. - Chassis Version Example Chips - --------------- ------------- - 1.0 p4080, p5020, p5040 --2.0 t4240, b4860, t1040 -+2.0 t4240, b4860 - - 1. Clock Block Binding - - Required properties: --- compatible: Should contain a specific clock block compatible string -- and a single chassis clock compatible string. -- Clock block strings include, but not limited to, one of the: -+- compatible: Should contain a chip-specific clock block compatible -+ string and (if applicable) may contain a chassis-version clock -+ compatible string. -+ -+ Chip-specific strings are of the form "fsl,-clockgen", such as: - * "fsl,p2041-clockgen" - * "fsl,p3041-clockgen" - * "fsl,p4080-clockgen" -@@ -29,15 +31,15 @@ Required properties: - * "fsl,t4240-clockgen" - * "fsl,b4420-clockgen" - * "fsl,b4860-clockgen" -- Chassis clock strings include: -+ * "fsl,ls1021a-clockgen" -+ Chassis-version clock strings include: - * "fsl,qoriq-clockgen-1.0": for chassis 1.0 clocks - * "fsl,qoriq-clockgen-2.0": for chassis 2.0 clocks - - reg: Describes the address of the device's resources within the - address space defined by its parent bus, and resource zero - represents the clock register set --- clock-frequency: Input system clock frequency - --Recommended properties: -+Optional properties: - - ranges: Allows valid translation between child's address space and - parent's. Must be present if the device has sub-nodes. - - #address-cells: Specifies the number of cells used to represent -@@ -46,8 +48,46 @@ Recommended properties: - - #size-cells: Specifies the number of cells used to represent - the size of an address. Must be present if the device has - sub-nodes and set to 1 if present -+- clock-frequency: Input system clock frequency (SYSCLK) -+- clocks: If clock-frequency is not specified, sysclk may be provided -+ as an input clock. Either clock-frequency or clocks must be -+ provided. -+ -+2. Clock Provider -+ -+The clockgen node should act as a clock provider, though in older device -+trees the children of the clockgen node are the clock providers. -+ -+When the clockgen node is a clock provider, #clock-cells = <2>. -+The first cell of the clock specifier is the clock type, and the -+second cell is the clock index for the specified type. -+ -+ Type# Name Index Cell -+ 0 sysclk must be 0 -+ 1 cmux index (n in CLKCnCSR) -+ 2 hwaccel index (n in CLKCGnHWACSR) -+ 3 fman 0 for fm1, 1 for fm2 -+ 4 platform pll 0=pll, 1=pll/2, 2=pll/3, 3=pll/4 -+ -+3. Example -+ -+ clockgen: global-utilities@e1000 { -+ compatible = "fsl,p5020-clockgen", "fsl,qoriq-clockgen-1.0"; -+ clock-frequency = <133333333>; -+ reg = <0xe1000 0x1000>; -+ #clock-cells = <2>; -+ }; -+ -+ fman@400000 { -+ ... -+ clocks = <&clockgen 3 0>; -+ ... -+ }; -+} -+4. Legacy Child Nodes - --2. Clock Provider/Consumer Binding -+NOTE: These nodes are deprecated. Kernels should continue to support -+device trees with these nodes, but new device trees should not use them. - - Most of the bindings are from the common clock binding[1]. - [1] Documentation/devicetree/bindings/clock/clock-bindings.txt -@@ -79,7 +119,7 @@ Recommended properties: - - reg: Should be the offset and length of clock block base address. - The length should be 4. - --Example for clock block and clock provider: -+Legacy Example: - / { - clockgen: global-utilities@e1000 { - compatible = "fsl,p5020-clockgen", "fsl,qoriq-clockgen-1.0"; -@@ -131,7 +171,7 @@ Example for clock block and clock provider: - }; - } - --Example for clock consumer: -+Example for legacy clock consumer: - - / { - cpu0: PowerPC,e5500@0 { -diff --git a/Documentation/devicetree/bindings/i2c/i2c-imx.txt b/Documentation/devicetree/bindings/i2c/i2c-imx.txt -index 4a8513e..52d37fd 100644 ---- a/Documentation/devicetree/bindings/i2c/i2c-imx.txt -+++ b/Documentation/devicetree/bindings/i2c/i2c-imx.txt -@@ -11,6 +11,8 @@ Required properties: - Optional properties: - - clock-frequency : Constains desired I2C/HS-I2C bus clock frequency in Hz. - The absence of the propoerty indicates the default frequency 100 kHz. -+- dmas: A list of two dma specifiers, one for each entry in dma-names. -+- dma-names: should contain "tx" and "rx". - - Examples: - -@@ -26,3 +28,12 @@ i2c@70038000 { /* HS-I2C on i.MX51 */ - interrupts = <64>; - clock-frequency = <400000>; - }; -+ -+i2c0: i2c@40066000 { /* i2c0 on vf610 */ -+ compatible = "fsl,vf610-i2c"; -+ reg = <0x40066000 0x1000>; -+ interrupts =<0 71 0x04>; -+ dmas = <&edma0 0 50>, -+ <&edma0 0 51>; -+ dma-names = "rx","tx"; -+}; -diff --git a/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.txt b/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.txt -index 34a3fb6..cf53d5f 100644 ---- a/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.txt -+++ b/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.txt -@@ -16,6 +16,9 @@ Required Properties: - Optional Properties: - - - reset-gpios: Reference to the GPIO connected to the reset input. -+ - i2c-mux-idle-disconnect: Boolean; if defined, forces mux to disconnect all -+ children in idle state. This is necessary for example, if there are several -+ multiplexers on the bus and the devices behind them use same I2C addresses. - - - Example: -diff --git a/Documentation/devicetree/bindings/memory-controllers/fsl/ifc.txt b/Documentation/devicetree/bindings/memory-controllers/fsl/ifc.txt -index d5e3704..89427b0 100644 ---- a/Documentation/devicetree/bindings/memory-controllers/fsl/ifc.txt -+++ b/Documentation/devicetree/bindings/memory-controllers/fsl/ifc.txt -@@ -18,6 +18,8 @@ Properties: - interrupt (NAND_EVTER_STAT). If there is only one, - that interrupt reports both types of event. - -+- little-endian : If this property is absent, the big-endian mode will -+ be in use as default for registers. - - - ranges : Each range corresponds to a single chipselect, and covers - the entire access window as configured. -@@ -34,6 +36,7 @@ Example: - #size-cells = <1>; - reg = <0x0 0xffe1e000 0 0x2000>; - interrupts = <16 2 19 2>; -+ little-endian; - - /* NOR, NAND Flashes and CPLD on board */ - ranges = <0x0 0x0 0x0 0xee000000 0x02000000 -diff --git a/Documentation/devicetree/of_selftest.txt b/Documentation/devicetree/of_selftest.txt -index 1e3d5c9..57a808b 100644 ---- a/Documentation/devicetree/of_selftest.txt -+++ b/Documentation/devicetree/of_selftest.txt -@@ -63,7 +63,6 @@ struct device_node { - struct device_node *parent; - struct device_node *child; - struct device_node *sibling; -- struct device_node *allnext; /* next in list of all nodes */ - ... - }; - -@@ -99,12 +98,6 @@ child11 -> sibling12 -> sibling13 -> sibling14 -> null - Figure 1: Generic structure of un-flattened device tree - - --*allnext: it is used to link all the nodes of DT into a list. So, for the -- above tree the list would be as follows: -- --root->child1->child11->sibling12->sibling13->child131->sibling14->sibling2-> --child21->sibling22->sibling23->sibling3->child31->sibling32->sibling4->null -- - Before executing OF selftest, it is required to attach the test data to - machine's device tree (if present). So, when selftest_data_add() is called, - at first it reads the flattened device tree data linked into the kernel image -@@ -131,11 +124,6 @@ root ('/') - test-child01 null null null - - --allnext list: -- --root->testcase-data->test-child0->test-child01->test-sibling1->test-sibling2 --->test-sibling3->null -- - Figure 2: Example test data tree to be attached to live tree. - - According to the scenario above, the live tree is already present so it isn't -@@ -204,8 +192,6 @@ detached and then moving up the parent nodes are removed, and eventually the - whole tree). selftest_data_remove() calls detach_node_and_children() that uses - of_detach_node() to detach the nodes from the live device tree. - --To detach a node, of_detach_node() first updates all_next linked list, by --attaching the previous node's allnext to current node's allnext pointer. And --then, it either updates the child pointer of given node's parent to its --sibling or attaches the previous sibling to the given node's sibling, as --appropriate. That is it :) -+To detach a node, of_detach_node() either updates the child pointer of given -+node's parent to its sibling or attaches the previous sibling to the given -+node's sibling, as appropriate. That is it :) -diff --git a/Documentation/devicetree/todo.txt b/Documentation/devicetree/todo.txt -index c3cf065..b5139d1 100644 ---- a/Documentation/devicetree/todo.txt -+++ b/Documentation/devicetree/todo.txt -@@ -2,7 +2,6 @@ Todo list for devicetree: - - === General structure === - - Switch from custom lists to (h)list_head for nodes and properties structure --- Remove of_allnodes list and iterate using list of child nodes alone - - === CONFIG_OF_DYNAMIC === - - Switch to RCU for tree updates and get rid of global spinlock -diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig -index 08e1287..329f5f4 100644 ---- a/arch/arm64/Kconfig -+++ b/arch/arm64/Kconfig -@@ -13,6 +13,7 @@ config ARM64 - select ARM_ARCH_TIMER - select ARM_GIC - select AUDIT_ARCH_COMPAT_GENERIC -+ select ARM_GIC_V2M if PCI_MSI - select ARM_GIC_V3 - select ARM_GIC_V3_ITS if PCI_MSI - select BUILDTIME_EXTABLE_SORT -diff --git a/arch/arm64/include/asm/device.h b/arch/arm64/include/asm/device.h -index cf98b36..243ef25 100644 ---- a/arch/arm64/include/asm/device.h -+++ b/arch/arm64/include/asm/device.h -@@ -21,6 +21,7 @@ struct dev_archdata { - #ifdef CONFIG_IOMMU_API - void *iommu; /* private IOMMU data */ - #endif -+ bool dma_coherent; - }; - - struct pdev_archdata { -diff --git a/arch/arm64/include/asm/dma-mapping.h b/arch/arm64/include/asm/dma-mapping.h -index adeae3f..9ce3e68 100644 ---- a/arch/arm64/include/asm/dma-mapping.h -+++ b/arch/arm64/include/asm/dma-mapping.h -@@ -52,12 +52,20 @@ static inline void set_dma_ops(struct device *dev, struct dma_map_ops *ops) - dev->archdata.dma_ops = ops; - } - --static inline int set_arch_dma_coherent_ops(struct device *dev) -+static inline void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size, -+ struct iommu_ops *iommu, bool coherent) - { -- set_dma_ops(dev, &coherent_swiotlb_dma_ops); -- return 0; -+ dev->archdata.dma_coherent = coherent; -+ if (coherent) -+ set_dma_ops(dev, &coherent_swiotlb_dma_ops); -+} -+#define arch_setup_dma_ops arch_setup_dma_ops -+ -+/* do not use this function in a driver */ -+static inline bool is_device_dma_coherent(struct device *dev) -+{ -+ return dev->archdata.dma_coherent; - } --#define set_arch_dma_coherent_ops set_arch_dma_coherent_ops - - #include - -diff --git a/arch/powerpc/include/asm/mpc85xx.h b/arch/powerpc/include/asm/mpc85xx.h -deleted file mode 100644 -index 3bef74a..0000000 ---- a/arch/powerpc/include/asm/mpc85xx.h -+++ /dev/null -@@ -1,94 +0,0 @@ --/* -- * MPC85xx cpu type detection -- * -- * Copyright 2011-2012 Freescale Semiconductor, Inc. -- * -- * This 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. -- */ -- --#ifndef __ASM_PPC_MPC85XX_H --#define __ASM_PPC_MPC85XX_H -- --#define SVR_REV(svr) ((svr) & 0xFF) /* SOC design resision */ --#define SVR_MAJ(svr) (((svr) >> 4) & 0xF) /* Major revision field*/ --#define SVR_MIN(svr) (((svr) >> 0) & 0xF) /* Minor revision field*/ -- --/* Some parts define SVR[0:23] as the SOC version */ --#define SVR_SOC_VER(svr) (((svr) >> 8) & 0xFFF7FF) /* SOC Version fields */ -- --#define SVR_8533 0x803400 --#define SVR_8535 0x803701 --#define SVR_8536 0x803700 --#define SVR_8540 0x803000 --#define SVR_8541 0x807200 --#define SVR_8543 0x803200 --#define SVR_8544 0x803401 --#define SVR_8545 0x803102 --#define SVR_8547 0x803101 --#define SVR_8548 0x803100 --#define SVR_8555 0x807100 --#define SVR_8560 0x807000 --#define SVR_8567 0x807501 --#define SVR_8568 0x807500 --#define SVR_8569 0x808000 --#define SVR_8572 0x80E000 --#define SVR_P1010 0x80F100 --#define SVR_P1011 0x80E500 --#define SVR_P1012 0x80E501 --#define SVR_P1013 0x80E700 --#define SVR_P1014 0x80F101 --#define SVR_P1017 0x80F700 --#define SVR_P1020 0x80E400 --#define SVR_P1021 0x80E401 --#define SVR_P1022 0x80E600 --#define SVR_P1023 0x80F600 --#define SVR_P1024 0x80E402 --#define SVR_P1025 0x80E403 --#define SVR_P2010 0x80E300 --#define SVR_P2020 0x80E200 --#define SVR_P2040 0x821000 --#define SVR_P2041 0x821001 --#define SVR_P3041 0x821103 --#define SVR_P4040 0x820100 --#define SVR_P4080 0x820000 --#define SVR_P5010 0x822100 --#define SVR_P5020 0x822000 --#define SVR_P5021 0X820500 --#define SVR_P5040 0x820400 --#define SVR_T4240 0x824000 --#define SVR_T4120 0x824001 --#define SVR_T4160 0x824100 --#define SVR_C291 0x850000 --#define SVR_C292 0x850020 --#define SVR_C293 0x850030 --#define SVR_B4860 0X868000 --#define SVR_G4860 0x868001 --#define SVR_G4060 0x868003 --#define SVR_B4440 0x868100 --#define SVR_G4440 0x868101 --#define SVR_B4420 0x868102 --#define SVR_B4220 0x868103 --#define SVR_T1040 0x852000 --#define SVR_T1041 0x852001 --#define SVR_T1042 0x852002 --#define SVR_T1020 0x852100 --#define SVR_T1021 0x852101 --#define SVR_T1022 0x852102 --#define SVR_T2080 0x853000 --#define SVR_T2081 0x853100 -- --#define SVR_8610 0x80A000 --#define SVR_8641 0x809000 --#define SVR_8641D 0x809001 -- --#define SVR_9130 0x860001 --#define SVR_9131 0x860000 --#define SVR_9132 0x861000 --#define SVR_9232 0x861400 -- --#define SVR_Unknown 0xFFFFFF -- --#endif -diff --git a/arch/powerpc/platforms/85xx/mpc85xx_mds.c b/arch/powerpc/platforms/85xx/mpc85xx_mds.c -index a392e94..f0be439 100644 ---- a/arch/powerpc/platforms/85xx/mpc85xx_mds.c -+++ b/arch/powerpc/platforms/85xx/mpc85xx_mds.c -@@ -34,6 +34,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -51,7 +52,6 @@ - #include - #include - #include --#include - #include "smp.h" - - #include "mpc85xx.h" -diff --git a/arch/powerpc/platforms/85xx/mpc85xx_rdb.c b/arch/powerpc/platforms/85xx/mpc85xx_rdb.c -index e358bed..50dcc00 100644 ---- a/arch/powerpc/platforms/85xx/mpc85xx_rdb.c -+++ b/arch/powerpc/platforms/85xx/mpc85xx_rdb.c -@@ -17,6 +17,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -27,7 +28,6 @@ - #include - #include - #include --#include - - #include - #include -diff --git a/arch/powerpc/platforms/85xx/p1022_ds.c b/arch/powerpc/platforms/85xx/p1022_ds.c -index 6ac986d..371df82 100644 ---- a/arch/powerpc/platforms/85xx/p1022_ds.c -+++ b/arch/powerpc/platforms/85xx/p1022_ds.c -@@ -16,6 +16,7 @@ - * kind, whether express or implied. - */ - -+#include - #include - #include - #include -@@ -25,7 +26,6 @@ - #include - #include - #include --#include - #include - #include "smp.h" - -diff --git a/arch/powerpc/platforms/85xx/p1022_rdk.c b/arch/powerpc/platforms/85xx/p1022_rdk.c -index 7a180f0..4f8fc5f 100644 ---- a/arch/powerpc/platforms/85xx/p1022_rdk.c -+++ b/arch/powerpc/platforms/85xx/p1022_rdk.c -@@ -12,6 +12,7 @@ - * kind, whether express or implied. - */ - -+#include - #include - #include - #include -@@ -21,7 +22,6 @@ - #include - #include - #include --#include - #include "smp.h" - - #include "mpc85xx.h" -diff --git a/arch/powerpc/platforms/85xx/smp.c b/arch/powerpc/platforms/85xx/smp.c -index d7c1e69..3956455 100644 ---- a/arch/powerpc/platforms/85xx/smp.c -+++ b/arch/powerpc/platforms/85xx/smp.c -@@ -19,6 +19,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -26,7 +27,6 @@ - #include - #include - #include --#include - #include - #include - -diff --git a/arch/powerpc/platforms/85xx/twr_p102x.c b/arch/powerpc/platforms/85xx/twr_p102x.c -index 1eadb6d..2799120 100644 ---- a/arch/powerpc/platforms/85xx/twr_p102x.c -+++ b/arch/powerpc/platforms/85xx/twr_p102x.c -@@ -15,6 +15,7 @@ - #include - #include - #include -+#include - #include - #include - -@@ -23,7 +24,6 @@ - #include - #include - #include --#include - - #include - #include -diff --git a/arch/powerpc/platforms/86xx/mpc8610_hpcd.c b/arch/powerpc/platforms/86xx/mpc8610_hpcd.c -index 55413a5..437a9c3 100644 ---- a/arch/powerpc/platforms/86xx/mpc8610_hpcd.c -+++ b/arch/powerpc/platforms/86xx/mpc8610_hpcd.c -@@ -24,6 +24,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -38,7 +39,6 @@ - #include - #include - #include --#include - - #include "mpc86xx.h" - -diff --git a/arch/x86/pci/xen.c b/arch/x86/pci/xen.c -index 4f6844b..878fb8e 100644 ---- a/arch/x86/pci/xen.c -+++ b/arch/x86/pci/xen.c -@@ -296,12 +296,16 @@ static int xen_initdom_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) - map_irq.entry_nr = nvec; - } else if (type == PCI_CAP_ID_MSIX) { - int pos; -+ unsigned long flags; - u32 table_offset, bir; - - pos = dev->msix_cap; - pci_read_config_dword(dev, pos + PCI_MSIX_TABLE, - &table_offset); - bir = (u8)(table_offset & PCI_MSIX_TABLE_BIR); -+ flags = pci_resource_flags(dev, bir); -+ if (!flags || (flags & IORESOURCE_UNSET)) -+ return -EINVAL; - - map_irq.table_base = pci_resource_start(dev, bir); - map_irq.entry_nr = msidesc->msi_attrib.entry_nr; -diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig -index 455fd17..38c8814 100644 ---- a/drivers/clk/Kconfig -+++ b/drivers/clk/Kconfig -@@ -101,12 +101,12 @@ config COMMON_CLK_AXI_CLKGEN - Support for the Analog Devices axi-clkgen pcore clock generator for Xilinx - FPGAs. It is commonly used in Analog Devices' reference designs. - --config CLK_PPC_CORENET -- bool "Clock driver for PowerPC corenet platforms" -- depends on PPC_E500MC && OF -+config CLK_QORIQ -+ bool "Clock driver for Freescale QorIQ platforms" -+ depends on (PPC_E500MC || ARM || ARM64) && OF - ---help--- -- This adds the clock driver support for Freescale PowerPC corenet -- platforms using common clock framework. -+ This adds the clock driver support for Freescale QorIQ platforms -+ using common clock framework. - - config COMMON_CLK_XGENE - bool "Clock driver for APM XGene SoC" -diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile -index d5fba5b..4ff94cd 100644 ---- a/drivers/clk/Makefile -+++ b/drivers/clk/Makefile -@@ -30,7 +30,7 @@ obj-$(CONFIG_ARCH_MOXART) += clk-moxart.o - obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o - obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o - obj-$(CONFIG_COMMON_CLK_PALMAS) += clk-palmas.o --obj-$(CONFIG_CLK_PPC_CORENET) += clk-ppc-corenet.o -+obj-$(CONFIG_CLK_QORIQ) += clk-qoriq.o - obj-$(CONFIG_COMMON_CLK_RK808) += clk-rk808.o - obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o - obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o -diff --git a/drivers/clk/clk-qoriq.c b/drivers/clk/clk-qoriq.c -new file mode 100644 -index 0000000..74051c9 ---- /dev/null -+++ b/drivers/clk/clk-qoriq.c -@@ -0,0 +1,1256 @@ -+/* -+ * Copyright 2013 Freescale Semiconductor, 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. -+ * -+ * clock driver for Freescale QorIQ SoCs. -+ */ -+ -+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define PLL_DIV1 0 -+#define PLL_DIV2 1 -+#define PLL_DIV3 2 -+#define PLL_DIV4 3 -+ -+#define PLATFORM_PLL 0 -+#define CGA_PLL1 1 -+#define CGA_PLL2 2 -+#define CGA_PLL3 3 -+#define CGA_PLL4 4 /* only on clockgen-1.0, which lacks CGB */ -+#define CGB_PLL1 4 -+#define CGB_PLL2 5 -+ -+struct clockgen_pll_div { -+ struct clk *clk; -+ char name[32]; -+}; -+ -+struct clockgen_pll { -+ struct clockgen_pll_div div[4]; -+}; -+ -+#define CLKSEL_VALID 1 -+#define CLKSEL_80PCT 2 /* Only allowed if PLL <= 80% of max cpu freq */ -+ -+struct clockgen_sourceinfo { -+ u32 flags; /* CLKSEL_xxx */ -+ int pll; /* CGx_PLLn */ -+ int div; /* PLL_DIVn */ -+}; -+ -+#define NUM_MUX_PARENTS 16 -+ -+struct clockgen_muxinfo { -+ struct clockgen_sourceinfo clksel[NUM_MUX_PARENTS]; -+}; -+ -+#define NUM_HWACCEL 5 -+#define NUM_CMUX 8 -+ -+struct clockgen; -+ -+/* -+ * cmux freq must be >= platform pll. -+ * If not set, cmux freq must be >= platform pll/2 -+ */ -+#define CG_CMUX_GE_PLAT 1 -+ -+#define CG_PLL_8BIT 2 /* PLLCnGSR[CFG] is 8 bits, not 6 */ -+#define CG_VER3 4 /* version 3 cg: reg layout different */ -+#define CG_LITTLE_ENDIAN 8 -+ -+struct clockgen_chipinfo { -+ const char *compat, *guts_compat; -+ const struct clockgen_muxinfo *cmux_groups[2]; -+ const struct clockgen_muxinfo *hwaccel[NUM_HWACCEL]; -+ void (*init_periph)(struct clockgen *cg); -+ int cmux_to_group[NUM_CMUX]; /* -1 terminates if fewer than NUM_CMUX */ -+ u32 pll_mask; /* 1 << n bit set if PLL n is valid */ -+ u32 flags; /* CG_xxx */ -+}; -+ -+struct clockgen { -+ struct device_node *node; -+ void __iomem *regs; -+ struct clockgen_chipinfo info; /* mutable copy */ -+ struct clk *sysclk; -+ struct clockgen_pll pll[6]; -+ struct clk *cmux[NUM_CMUX]; -+ struct clk *hwaccel[NUM_HWACCEL]; -+ struct clk *fman[2]; -+ struct ccsr_guts __iomem *guts; -+}; -+ -+static struct clockgen clockgen; -+ -+static void cg_out(struct clockgen *cg, u32 val, u32 __iomem *reg) -+{ -+ if (cg->info.flags & CG_LITTLE_ENDIAN) -+ iowrite32(val, reg); -+ else -+ iowrite32be(val, reg); -+} -+ -+static u32 cg_in(struct clockgen *cg, u32 __iomem *reg) -+{ -+ u32 val; -+ -+ if (cg->info.flags & CG_LITTLE_ENDIAN) -+ val = ioread32(reg); -+ else -+ val = ioread32be(reg); -+ -+ return val; -+} -+ -+static const struct clockgen_muxinfo p2041_cmux_grp1 = { -+ { -+ [0] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 }, -+ [1] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 }, -+ [4] = { CLKSEL_VALID, CGA_PLL2, PLL_DIV1 }, -+ } -+}; -+ -+static const struct clockgen_muxinfo p2041_cmux_grp2 = { -+ { -+ [0] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 }, -+ [4] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 }, -+ [5] = { CLKSEL_VALID, CGA_PLL2, PLL_DIV1 }, -+ } -+}; -+ -+static const struct clockgen_muxinfo p5020_cmux_grp1 = { -+ { -+ [0] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 }, -+ [1] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 }, -+ [4] = { CLKSEL_VALID | CLKSEL_80PCT, CGA_PLL2, PLL_DIV1 }, -+ } -+}; -+ -+static const struct clockgen_muxinfo p5020_cmux_grp2 = { -+ { -+ [0] = { CLKSEL_VALID | CLKSEL_80PCT, CGA_PLL1, PLL_DIV1 }, -+ [4] = { CLKSEL_VALID, CGA_PLL2, PLL_DIV1 }, -+ [5] = { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 }, -+ } -+}; -+ -+static const struct clockgen_muxinfo p5040_cmux_grp1 = { -+ { -+ [0] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 }, -+ [1] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 }, -+ [4] = { CLKSEL_VALID | CLKSEL_80PCT, CGA_PLL2, PLL_DIV1 }, -+ [5] = { CLKSEL_VALID | CLKSEL_80PCT, CGA_PLL2, PLL_DIV2 }, -+ } -+}; -+ -+static const struct clockgen_muxinfo p5040_cmux_grp2 = { -+ { -+ [0] = { CLKSEL_VALID | CLKSEL_80PCT, CGA_PLL1, PLL_DIV1 }, -+ [1] = { CLKSEL_VALID | CLKSEL_80PCT, CGA_PLL1, PLL_DIV2 }, -+ [4] = { CLKSEL_VALID, CGA_PLL2, PLL_DIV1 }, -+ [5] = { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 }, -+ } -+}; -+ -+static const struct clockgen_muxinfo p4080_cmux_grp1 = { -+ { -+ [0] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 }, -+ [1] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 }, -+ [4] = { CLKSEL_VALID, CGA_PLL2, PLL_DIV1 }, -+ [5] = { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 }, -+ [8] = { CLKSEL_VALID | CLKSEL_80PCT, CGA_PLL3, PLL_DIV1 }, -+ } -+}; -+ -+static const struct clockgen_muxinfo p4080_cmux_grp2 = { -+ { -+ [0] = { CLKSEL_VALID | CLKSEL_80PCT, CGA_PLL1, PLL_DIV1 }, -+ [8] = { CLKSEL_VALID, CGA_PLL3, PLL_DIV1 }, -+ [9] = { CLKSEL_VALID, CGA_PLL3, PLL_DIV2 }, -+ [12] = { CLKSEL_VALID, CGA_PLL4, PLL_DIV1 }, -+ [13] = { CLKSEL_VALID, CGA_PLL4, PLL_DIV2 }, -+ } -+}; -+ -+static const struct clockgen_muxinfo t1023_cmux = { -+ { -+ [0] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 }, -+ [1] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 }, -+ } -+}; -+ -+static const struct clockgen_muxinfo t1040_cmux = { -+ { -+ [0] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 }, -+ [1] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 }, -+ [4] = { CLKSEL_VALID, CGA_PLL2, PLL_DIV1 }, -+ [5] = { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 }, -+ } -+}; -+ -+ -+static const struct clockgen_muxinfo clockgen2_cmux_cga = { -+ { -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 }, -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 }, -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV4 }, -+ {}, -+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV1 }, -+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 }, -+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV4 }, -+ {}, -+ { CLKSEL_VALID, CGA_PLL3, PLL_DIV1 }, -+ { CLKSEL_VALID, CGA_PLL3, PLL_DIV2 }, -+ { CLKSEL_VALID, CGA_PLL3, PLL_DIV4 }, -+ }, -+}; -+ -+static const struct clockgen_muxinfo clockgen2_cmux_cga12 = { -+ { -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 }, -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 }, -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV4 }, -+ {}, -+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV1 }, -+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 }, -+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV4 }, -+ }, -+}; -+ -+static const struct clockgen_muxinfo clockgen2_cmux_cgb = { -+ { -+ { CLKSEL_VALID, CGB_PLL1, PLL_DIV1 }, -+ { CLKSEL_VALID, CGB_PLL1, PLL_DIV2 }, -+ { CLKSEL_VALID, CGB_PLL1, PLL_DIV4 }, -+ {}, -+ { CLKSEL_VALID, CGB_PLL2, PLL_DIV1 }, -+ { CLKSEL_VALID, CGB_PLL2, PLL_DIV2 }, -+ { CLKSEL_VALID, CGB_PLL2, PLL_DIV4 }, -+ }, -+}; -+ -+static const struct clockgen_muxinfo t1023_hwa1 = { -+ { -+ {}, -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 }, -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 }, -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV3 }, -+ }, -+}; -+ -+static const struct clockgen_muxinfo t1023_hwa2 = { -+ { -+ [6] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 }, -+ }, -+}; -+ -+static const struct clockgen_muxinfo t2080_hwa1 = { -+ { -+ {}, -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 }, -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 }, -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV3 }, -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV4 }, -+ { CLKSEL_VALID, PLATFORM_PLL, PLL_DIV1 }, -+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 }, -+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV3 }, -+ }, -+}; -+ -+static const struct clockgen_muxinfo t2080_hwa2 = { -+ { -+ {}, -+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV1 }, -+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 }, -+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV3 }, -+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV4 }, -+ { CLKSEL_VALID, PLATFORM_PLL, PLL_DIV1 }, -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 }, -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV3 }, -+ }, -+}; -+ -+static const struct clockgen_muxinfo t4240_hwa1 = { -+ { -+ { CLKSEL_VALID, PLATFORM_PLL, PLL_DIV2 }, -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 }, -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 }, -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV3 }, -+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV4 }, -+ {}, -+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 }, -+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV3 }, -+ }, -+}; -+ -+static const struct clockgen_muxinfo t4240_hwa4 = { -+ { -+ [2] = { CLKSEL_VALID, CGB_PLL1, PLL_DIV2 }, -+ [3] = { CLKSEL_VALID, CGB_PLL1, PLL_DIV3 }, -+ [4] = { CLKSEL_VALID, CGB_PLL1, PLL_DIV4 }, -+ [5] = { CLKSEL_VALID, PLATFORM_PLL, PLL_DIV1 }, -+ [6] = { CLKSEL_VALID, CGB_PLL2, PLL_DIV2 }, -+ }, -+}; -+ -+static const struct clockgen_muxinfo t4240_hwa5 = { -+ { -+ [2] = { CLKSEL_VALID, CGB_PLL2, PLL_DIV2 }, -+ [3] = { CLKSEL_VALID, CGB_PLL2, PLL_DIV3 }, -+ [4] = { CLKSEL_VALID, CGB_PLL2, PLL_DIV4 }, -+ [5] = { CLKSEL_VALID, PLATFORM_PLL, PLL_DIV1 }, -+ [6] = { CLKSEL_VALID, CGB_PLL1, PLL_DIV2 }, -+ [7] = { CLKSEL_VALID, CGB_PLL1, PLL_DIV3 }, -+ }, -+}; -+ -+#define RCWSR7_FM1_CLK_SEL 0x40000000 -+#define RCWSR7_FM2_CLK_SEL 0x20000000 -+#define RCWSR7_HWA_ASYNC_DIV 0x04000000 -+ -+static void __init p2041_init_periph(struct clockgen *cg) -+{ -+ u32 reg; -+ -+ reg = ioread32be(&cg->guts->rcwsr[7]); -+ -+ if (reg & RCWSR7_FM1_CLK_SEL) -+ cg->fman[0] = cg->pll[CGA_PLL2].div[PLL_DIV2].clk; -+ else -+ cg->fman[0] = cg->pll[PLATFORM_PLL].div[PLL_DIV2].clk; -+} -+ -+static void __init p4080_init_periph(struct clockgen *cg) -+{ -+ u32 reg; -+ -+ reg = ioread32be(&cg->guts->rcwsr[7]); -+ -+ if (reg & RCWSR7_FM1_CLK_SEL) -+ cg->fman[0] = cg->pll[CGA_PLL3].div[PLL_DIV2].clk; -+ else -+ cg->fman[0] = cg->pll[PLATFORM_PLL].div[PLL_DIV2].clk; -+ -+ if (reg & RCWSR7_FM2_CLK_SEL) -+ cg->fman[1] = cg->pll[CGA_PLL3].div[PLL_DIV2].clk; -+ else -+ cg->fman[1] = cg->pll[PLATFORM_PLL].div[PLL_DIV2].clk; -+} -+ -+static void __init p5020_init_periph(struct clockgen *cg) -+{ -+ u32 reg; -+ int div = PLL_DIV2; -+ -+ reg = ioread32be(&cg->guts->rcwsr[7]); -+ if (reg & RCWSR7_HWA_ASYNC_DIV) -+ div = PLL_DIV4; -+ -+ if (reg & RCWSR7_FM1_CLK_SEL) -+ cg->fman[0] = cg->pll[CGA_PLL2].div[div].clk; -+ else -+ cg->fman[0] = cg->pll[PLATFORM_PLL].div[PLL_DIV2].clk; -+} -+ -+static void __init p5040_init_periph(struct clockgen *cg) -+{ -+ u32 reg; -+ int div = PLL_DIV2; -+ -+ reg = ioread32be(&cg->guts->rcwsr[7]); -+ if (reg & RCWSR7_HWA_ASYNC_DIV) -+ div = PLL_DIV4; -+ -+ if (reg & RCWSR7_FM1_CLK_SEL) -+ cg->fman[0] = cg->pll[CGA_PLL3].div[div].clk; -+ else -+ cg->fman[0] = cg->pll[PLATFORM_PLL].div[PLL_DIV2].clk; -+ -+ if (reg & RCWSR7_FM2_CLK_SEL) -+ cg->fman[1] = cg->pll[CGA_PLL3].div[div].clk; -+ else -+ cg->fman[1] = cg->pll[PLATFORM_PLL].div[PLL_DIV2].clk; -+} -+ -+static void __init t1023_init_periph(struct clockgen *cg) -+{ -+ cg->fman[0] = cg->hwaccel[1]; -+} -+ -+static void __init t1040_init_periph(struct clockgen *cg) -+{ -+ cg->fman[0] = cg->pll[PLATFORM_PLL].div[PLL_DIV1].clk; -+} -+ -+static void __init t2080_init_periph(struct clockgen *cg) -+{ -+ cg->fman[0] = cg->hwaccel[0]; -+} -+ -+static void __init t4240_init_periph(struct clockgen *cg) -+{ -+ cg->fman[0] = cg->hwaccel[3]; -+ cg->fman[1] = cg->hwaccel[4]; -+} -+ -+static const struct clockgen_chipinfo chipinfo[] = { -+ { -+ .compat = "fsl,b4420-clockgen", -+ .guts_compat = "fsl,b4860-device-config", -+ .init_periph = t2080_init_periph, -+ .cmux_groups = { -+ &clockgen2_cmux_cga12, &clockgen2_cmux_cgb -+ }, -+ .hwaccel = { -+ &t2080_hwa1 -+ }, -+ .cmux_to_group = { -+ 0, 1, 1, 1, -1 -+ }, -+ .pll_mask = 0x3f, -+ .flags = CG_PLL_8BIT, -+ }, -+ { -+ .compat = "fsl,b4860-clockgen", -+ .guts_compat = "fsl,b4860-device-config", -+ .init_periph = t2080_init_periph, -+ .cmux_groups = { -+ &clockgen2_cmux_cga12, &clockgen2_cmux_cgb -+ }, -+ .hwaccel = { -+ &t2080_hwa1 -+ }, -+ .cmux_to_group = { -+ 0, 1, 1, 1, -1 -+ }, -+ .pll_mask = 0x3f, -+ .flags = CG_PLL_8BIT, -+ }, -+ { -+ .compat = "fsl,ls1021a-clockgen", -+ .cmux_groups = { -+ &t1023_cmux -+ }, -+ .cmux_to_group = { -+ 0, -1 -+ }, -+ .pll_mask = 0x03, -+ }, -+ { -+ .compat = "fsl,ls2080a-clockgen", -+ .cmux_groups = { -+ &clockgen2_cmux_cga12, &clockgen2_cmux_cgb -+ }, -+ .cmux_to_group = { -+ 0, 0, 1, 1, -1 -+ }, -+ .pll_mask = 0x37, -+ .flags = CG_VER3 | CG_LITTLE_ENDIAN, -+ }, -+ { -+ .compat = "fsl,ls2088a-clockgen", -+ .cmux_groups = { -+ &clockgen2_cmux_cga12, &clockgen2_cmux_cgb -+ }, -+ .cmux_to_group = { -+ 0, 0, 1, 1, -1 -+ }, -+ .pll_mask = 0x37, -+ .flags = CG_VER3 | CG_LITTLE_ENDIAN, -+ }, -+ { -+ .compat = "fsl,p2041-clockgen", -+ .guts_compat = "fsl,qoriq-device-config-1.0", -+ .init_periph = p2041_init_periph, -+ .cmux_groups = { -+ &p2041_cmux_grp1, &p2041_cmux_grp2 -+ }, -+ .cmux_to_group = { -+ 0, 0, 1, 1, -1 -+ }, -+ .pll_mask = 0x07, -+ }, -+ { -+ .compat = "fsl,p3041-clockgen", -+ .guts_compat = "fsl,qoriq-device-config-1.0", -+ .init_periph = p2041_init_periph, -+ .cmux_groups = { -+ &p2041_cmux_grp1, &p2041_cmux_grp2 -+ }, -+ .cmux_to_group = { -+ 0, 0, 1, 1, -1 -+ }, -+ .pll_mask = 0x07, -+ }, -+ { -+ .compat = "fsl,p4080-clockgen", -+ .guts_compat = "fsl,qoriq-device-config-1.0", -+ .init_periph = p4080_init_periph, -+ .cmux_groups = { -+ &p4080_cmux_grp1, &p4080_cmux_grp2 -+ }, -+ .cmux_to_group = { -+ 0, 0, 0, 0, 1, 1, 1, 1 -+ }, -+ .pll_mask = 0x1f, -+ }, -+ { -+ .compat = "fsl,p5020-clockgen", -+ .guts_compat = "fsl,qoriq-device-config-1.0", -+ .init_periph = p5020_init_periph, -+ .cmux_groups = { -+ &p2041_cmux_grp1, &p2041_cmux_grp2 -+ }, -+ .cmux_to_group = { -+ 0, 1, -1 -+ }, -+ .pll_mask = 0x07, -+ }, -+ { -+ .compat = "fsl,p5040-clockgen", -+ .guts_compat = "fsl,p5040-device-config", -+ .init_periph = p5040_init_periph, -+ .cmux_groups = { -+ &p5040_cmux_grp1, &p5040_cmux_grp2 -+ }, -+ .cmux_to_group = { -+ 0, 0, 1, 1, -1 -+ }, -+ .pll_mask = 0x0f, -+ }, -+ { -+ .compat = "fsl,t1023-clockgen", -+ .guts_compat = "fsl,t1023-device-config", -+ .init_periph = t1023_init_periph, -+ .cmux_groups = { -+ &t1023_cmux -+ }, -+ .hwaccel = { -+ &t1023_hwa1, &t1023_hwa2 -+ }, -+ .cmux_to_group = { -+ 0, 0, -1 -+ }, -+ .pll_mask = 0x03, -+ .flags = CG_PLL_8BIT, -+ }, -+ { -+ .compat = "fsl,t1040-clockgen", -+ .guts_compat = "fsl,t1040-device-config", -+ .init_periph = t1040_init_periph, -+ .cmux_groups = { -+ &t1040_cmux -+ }, -+ .cmux_to_group = { -+ 0, 0, 0, 0, -1 -+ }, -+ .pll_mask = 0x07, -+ .flags = CG_PLL_8BIT, -+ }, -+ { -+ .compat = "fsl,t2080-clockgen", -+ .guts_compat = "fsl,t2080-device-config", -+ .init_periph = t2080_init_periph, -+ .cmux_groups = { -+ &clockgen2_cmux_cga12 -+ }, -+ .hwaccel = { -+ &t2080_hwa1, &t2080_hwa2 -+ }, -+ .cmux_to_group = { -+ 0, -1 -+ }, -+ .pll_mask = 0x07, -+ .flags = CG_PLL_8BIT, -+ }, -+ { -+ .compat = "fsl,t4240-clockgen", -+ .guts_compat = "fsl,t4240-device-config", -+ .init_periph = t4240_init_periph, -+ .cmux_groups = { -+ &clockgen2_cmux_cga, &clockgen2_cmux_cgb -+ }, -+ .hwaccel = { -+ &t4240_hwa1, NULL, NULL, &t4240_hwa4, &t4240_hwa5 -+ }, -+ .cmux_to_group = { -+ 0, 0, 1, -1 -+ }, -+ .pll_mask = 0x3f, -+ .flags = CG_PLL_8BIT, -+ }, -+ {}, -+}; -+ -+struct mux_hwclock { -+ struct clk_hw hw; -+ struct clockgen *cg; -+ const struct clockgen_muxinfo *info; -+ u32 __iomem *reg; -+ u8 parent_to_clksel[NUM_MUX_PARENTS]; -+ s8 clksel_to_parent[NUM_MUX_PARENTS]; -+ int num_parents; -+}; -+ -+#define to_mux_hwclock(p) container_of(p, struct mux_hwclock, hw) -+#define CLKSEL_MASK 0x78000000 -+#define CLKSEL_SHIFT 27 -+ -+static int mux_set_parent(struct clk_hw *hw, u8 idx) -+{ -+ struct mux_hwclock *hwc = to_mux_hwclock(hw); -+ u32 clksel; -+ -+ if (idx >= hwc->num_parents) -+ return -EINVAL; -+ -+ clksel = hwc->parent_to_clksel[idx]; -+ cg_out(hwc->cg, (clksel << CLKSEL_SHIFT) & CLKSEL_MASK, hwc->reg); -+ -+ return 0; -+} -+ -+static u8 mux_get_parent(struct clk_hw *hw) -+{ -+ struct mux_hwclock *hwc = to_mux_hwclock(hw); -+ u32 clksel; -+ s8 ret; -+ -+ clksel = (cg_in(hwc->cg, hwc->reg) & CLKSEL_MASK) >> CLKSEL_SHIFT; -+ -+ ret = hwc->clksel_to_parent[clksel]; -+ if (ret < 0) { -+ pr_err("%s: mux at %p has bad clksel\n", __func__, hwc->reg); -+ return 0; -+ } -+ -+ return ret; -+} -+ -+static const struct clk_ops cmux_ops = { -+ .get_parent = mux_get_parent, -+ .set_parent = mux_set_parent, -+}; -+ -+/* -+ * Don't allow setting for now, as the clock options haven't been -+ * sanitized for additional restrictions. -+ */ -+static const struct clk_ops hwaccel_ops = { -+ .get_parent = mux_get_parent, -+}; -+ -+static const struct clockgen_pll_div *get_pll_div(struct clockgen *cg, -+ struct mux_hwclock *hwc, -+ int idx) -+{ -+ int pll, div; -+ -+ if (!(hwc->info->clksel[idx].flags & CLKSEL_VALID)) -+ return NULL; -+ -+ pll = hwc->info->clksel[idx].pll; -+ div = hwc->info->clksel[idx].div; -+ -+ return &cg->pll[pll].div[div]; -+} -+ -+static struct clk * __init create_mux_common(struct clockgen *cg, -+ struct mux_hwclock *hwc, -+ const struct clk_ops *ops, -+ unsigned long min_rate, -+ unsigned long pct80_rate, -+ const char *fmt, int idx) -+{ -+ struct clk_init_data init = {}; -+ struct clk *clk; -+ const struct clockgen_pll_div *div; -+ const char *parent_names[NUM_MUX_PARENTS]; -+ char name[32]; -+ int i, j; -+ -+ snprintf(name, sizeof(name), fmt, idx); -+ -+ for (i = 0, j = 0; i < NUM_MUX_PARENTS; i++) { -+ unsigned long rate; -+ -+ hwc->clksel_to_parent[i] = -1; -+ -+ div = get_pll_div(cg, hwc, i); -+ if (!div) -+ continue; -+ -+ rate = clk_get_rate(div->clk); -+ -+ if (hwc->info->clksel[i].flags & CLKSEL_80PCT && -+ rate > pct80_rate) -+ continue; -+ if (rate < min_rate) -+ continue; -+ -+ parent_names[j] = div->name; -+ hwc->parent_to_clksel[j] = i; -+ hwc->clksel_to_parent[i] = j; -+ j++; -+ } -+ -+ init.name = name; -+ init.ops = ops; -+ init.parent_names = parent_names; -+ init.num_parents = hwc->num_parents = j; -+ init.flags = 0; -+ hwc->hw.init = &init; -+ hwc->cg = cg; -+ -+ clk = clk_register(NULL, &hwc->hw); -+ if (IS_ERR(clk)) { -+ pr_err("%s: Couldn't register %s: %ld\n", __func__, name, -+ PTR_ERR(clk)); -+ kfree(hwc); -+ return NULL; -+ } -+ -+ return clk; -+} -+ -+static struct clk * __init create_one_cmux(struct clockgen *cg, int idx) -+{ -+ struct mux_hwclock *hwc; -+ const struct clockgen_pll_div *div; -+ unsigned long plat_rate, min_rate; -+ u64 pct80_rate; -+ u32 clksel; -+ -+ hwc = kzalloc(sizeof(*hwc), GFP_KERNEL); -+ if (!hwc) -+ return NULL; -+ -+ if (cg->info.flags & CG_VER3) -+ hwc->reg = cg->regs + 0x70000 + 0x20 * idx; -+ else -+ hwc->reg = cg->regs + 0x20 * idx; -+ -+ hwc->info = cg->info.cmux_groups[cg->info.cmux_to_group[idx]]; -+ -+ /* -+ * Find the rate for the default clksel, and treat it as the -+ * maximum rated core frequency. If this is an incorrect -+ * assumption, certain clock options (possibly including the -+ * default clksel) may be inappropriately excluded on certain -+ * chips. -+ */ -+ clksel = (cg_in(cg, hwc->reg) & CLKSEL_MASK) >> CLKSEL_SHIFT; -+ div = get_pll_div(cg, hwc, clksel); -+ if (!div) -+ return NULL; -+ -+ pct80_rate = clk_get_rate(div->clk); -+ pct80_rate *= 8; -+ do_div(pct80_rate, 10); -+ -+ plat_rate = clk_get_rate(cg->pll[PLATFORM_PLL].div[PLL_DIV1].clk); -+ -+ if (cg->info.flags & CG_CMUX_GE_PLAT) -+ min_rate = plat_rate; -+ else -+ min_rate = plat_rate / 2; -+ -+ return create_mux_common(cg, hwc, &cmux_ops, min_rate, -+ pct80_rate, "cg-cmux%d", idx); -+} -+ -+static struct clk * __init create_one_hwaccel(struct clockgen *cg, int idx) -+{ -+ struct mux_hwclock *hwc; -+ -+ hwc = kzalloc(sizeof(*hwc), GFP_KERNEL); -+ if (!hwc) -+ return NULL; -+ -+ hwc->reg = cg->regs + 0x20 * idx + 0x10; -+ hwc->info = cg->info.hwaccel[idx]; -+ -+ return create_mux_common(cg, hwc, &hwaccel_ops, 0, 0, -+ "cg-hwaccel%d", idx); -+} -+ -+static void __init create_muxes(struct clockgen *cg) -+{ -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(cg->cmux); i++) { -+ if (cg->info.cmux_to_group[i] < 0) -+ break; -+ if (cg->info.cmux_to_group[i] >= -+ ARRAY_SIZE(cg->info.cmux_groups)) { -+ WARN_ON_ONCE(1); -+ continue; -+ } -+ -+ cg->cmux[i] = create_one_cmux(cg, i); -+ } -+ -+ for (i = 0; i < ARRAY_SIZE(cg->hwaccel); i++) { -+ if (!cg->info.hwaccel[i]) -+ continue; -+ -+ cg->hwaccel[i] = create_one_hwaccel(cg, i); -+ } -+} -+ -+static void __init clockgen_init(struct device_node *np); -+ -+/* Legacy nodes may get probed before the parent clockgen node */ -+static void __init legacy_init_clockgen(struct device_node *np) -+{ -+ if (!clockgen.node) -+ clockgen_init(of_get_parent(np)); -+} -+ -+/* Legacy node */ -+static void __init core_mux_init(struct device_node *np) -+{ -+ struct clk *clk; -+ struct resource res; -+ int idx, rc; -+ -+ legacy_init_clockgen(np); -+ -+ if (of_address_to_resource(np, 0, &res)) -+ return; -+ -+ idx = (res.start & 0xf0) >> 5; -+ clk = clockgen.cmux[idx]; -+ -+ rc = of_clk_add_provider(np, of_clk_src_simple_get, clk); -+ if (rc) { -+ pr_err("%s: Couldn't register clk provider for node %s: %d\n", -+ __func__, np->name, rc); -+ return; -+ } -+} -+ -+static struct clk *sysclk_from_fixed(struct device_node *node, const char *name) -+{ -+ u32 rate; -+ -+ if (of_property_read_u32(node, "clock-frequency", &rate)) -+ return ERR_PTR(-ENODEV); -+ -+ return clk_register_fixed_rate(NULL, name, NULL, CLK_IS_ROOT, rate); -+} -+ -+static struct clk *sysclk_from_parent(const char *name) -+{ -+ struct clk *clk; -+ const char *parent_name; -+ -+ clk = of_clk_get(clockgen.node, 0); -+ if (IS_ERR(clk)) -+ return clk; -+ -+ /* Register the input clock under the desired name. */ -+ parent_name = __clk_get_name(clk); -+ clk = clk_register_fixed_factor(NULL, name, parent_name, -+ 0, 1, 1); -+ if (IS_ERR(clk)) -+ pr_err("%s: Couldn't register %s: %ld\n", __func__, name, -+ PTR_ERR(clk)); -+ -+ return clk; -+} -+ -+static struct clk * __init create_sysclk(const char *name) -+{ -+ struct device_node *sysclk; -+ struct clk *clk; -+ -+ clk = sysclk_from_fixed(clockgen.node, name); -+ if (!IS_ERR(clk)) -+ return clk; -+ -+ clk = sysclk_from_parent(name); -+ if (!IS_ERR(clk)) -+ return clk; -+ -+ sysclk = of_get_child_by_name(clockgen.node, "sysclk"); -+ if (sysclk) { -+ clk = sysclk_from_fixed(sysclk, name); -+ if (!IS_ERR(clk)) -+ return clk; -+ } -+ -+ pr_err("%s: No input clock\n", __func__); -+ return NULL; -+} -+ -+/* Legacy node */ -+static void __init sysclk_init(struct device_node *node) -+{ -+ struct clk *clk; -+ -+ legacy_init_clockgen(node); -+ -+ clk = clockgen.sysclk; -+ if (clk) -+ of_clk_add_provider(node, of_clk_src_simple_get, clk); -+} -+ -+#define PLL_KILL BIT(31) -+ -+static void __init create_one_pll(struct clockgen *cg, int idx) -+{ -+ u32 __iomem *reg; -+ u32 mult; -+ struct clockgen_pll *pll = &cg->pll[idx]; -+ int i; -+ -+ if (!(cg->info.pll_mask & (1 << idx))) -+ return; -+ -+ if (cg->info.flags & CG_VER3) { -+ switch (idx) { -+ case PLATFORM_PLL: -+ reg = cg->regs + 0x60080; -+ break; -+ case CGA_PLL1: -+ reg = cg->regs + 0x80; -+ break; -+ case CGA_PLL2: -+ reg = cg->regs + 0xa0; -+ break; -+ case CGB_PLL1: -+ reg = cg->regs + 0x10080; -+ break; -+ case CGB_PLL2: -+ reg = cg->regs + 0x100a0; -+ break; -+ default: -+ WARN_ONCE(1, "index %d\n", idx); -+ return; -+ } -+ } else { -+ if (idx == PLATFORM_PLL) -+ reg = cg->regs + 0xc00; -+ else -+ reg = cg->regs + 0x800 + 0x20 * (idx - 1); -+ } -+ -+ /* Get the multiple of PLL */ -+ mult = cg_in(cg, reg); -+ -+ /* Check if this PLL is disabled */ -+ if (mult & PLL_KILL) { -+ pr_debug("%s(): pll %p disabled\n", __func__, reg); -+ return; -+ } -+ -+ if ((cg->info.flags & CG_VER3) || -+ ((cg->info.flags & CG_PLL_8BIT) && idx != PLATFORM_PLL)) -+ mult = (mult & GENMASK(8, 1)) >> 1; -+ else -+ mult = (mult & GENMASK(6, 1)) >> 1; -+ -+ for (i = 0; i < ARRAY_SIZE(pll->div); i++) { -+ struct clk *clk; -+ -+ snprintf(pll->div[i].name, sizeof(pll->div[i].name), -+ "cg-pll%d-div%d", idx, i + 1); -+ -+ clk = clk_register_fixed_factor(NULL, -+ pll->div[i].name, "cg-sysclk", 0, mult, i + 1); -+ if (IS_ERR(clk)) { -+ pr_err("%s: %s: register failed %ld\n", -+ __func__, pll->div[i].name, PTR_ERR(clk)); -+ continue; -+ } -+ -+ pll->div[i].clk = clk; -+ } -+} -+ -+static void __init create_plls(struct clockgen *cg) -+{ -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(cg->pll); i++) -+ create_one_pll(cg, i); -+} -+ -+static void __init legacy_pll_init(struct device_node *np, int idx) -+{ -+ struct clockgen_pll *pll; -+ struct clk_onecell_data *onecell_data; -+ struct clk **subclks; -+ int count, rc; -+ -+ legacy_init_clockgen(np); -+ -+ pll = &clockgen.pll[idx]; -+ count = of_property_count_strings(np, "clock-output-names"); -+ -+ BUILD_BUG_ON(ARRAY_SIZE(pll->div) < 4); -+ subclks = kcalloc(4, sizeof(struct clk *), GFP_KERNEL); -+ if (!subclks) -+ return; -+ -+ onecell_data = kmalloc(sizeof(*onecell_data), GFP_KERNEL); -+ if (!onecell_data) -+ goto err_clks; -+ -+ if (count <= 3) { -+ subclks[0] = pll->div[0].clk; -+ subclks[1] = pll->div[1].clk; -+ subclks[2] = pll->div[3].clk; -+ } else { -+ subclks[0] = pll->div[0].clk; -+ subclks[1] = pll->div[1].clk; -+ subclks[2] = pll->div[2].clk; -+ subclks[3] = pll->div[3].clk; -+ } -+ -+ onecell_data->clks = subclks; -+ onecell_data->clk_num = count; -+ -+ rc = of_clk_add_provider(np, of_clk_src_onecell_get, onecell_data); -+ if (rc) { -+ pr_err("%s: Couldn't register clk provider for node %s: %d\n", -+ __func__, np->name, rc); -+ goto err_cell; -+ } -+ -+ return; -+err_cell: -+ kfree(onecell_data); -+err_clks: -+ kfree(subclks); -+} -+ -+/* Legacy node */ -+static void __init pltfrm_pll_init(struct device_node *np) -+{ -+ legacy_pll_init(np, PLATFORM_PLL); -+} -+ -+/* Legacy node */ -+static void __init core_pll_init(struct device_node *np) -+{ -+ struct resource res; -+ int idx; -+ -+ if (of_address_to_resource(np, 0, &res)) -+ return; -+ -+ if ((res.start & 0xfff) == 0xc00) { -+ /* -+ * ls1021a devtree labels the platform PLL -+ * with the core PLL compatible -+ */ -+ pltfrm_pll_init(np); -+ } else { -+ idx = (res.start & 0xf0) >> 5; -+ legacy_pll_init(np, CGA_PLL1 + idx); -+ } -+} -+ -+static struct clk *clockgen_clk_get(struct of_phandle_args *clkspec, void *data) -+{ -+ struct clockgen *cg = data; -+ struct clk *clk; -+ struct clockgen_pll *pll; -+ u32 type, idx; -+ -+ if (clkspec->args_count < 2) { -+ pr_err("%s: insufficient phandle args\n", __func__); -+ return ERR_PTR(-EINVAL); -+ } -+ -+ type = clkspec->args[0]; -+ idx = clkspec->args[1]; -+ -+ switch (type) { -+ case 0: -+ if (idx != 0) -+ goto bad_args; -+ clk = cg->sysclk; -+ break; -+ case 1: -+ if (idx >= ARRAY_SIZE(cg->cmux)) -+ goto bad_args; -+ clk = cg->cmux[idx]; -+ break; -+ case 2: -+ if (idx >= ARRAY_SIZE(cg->hwaccel)) -+ goto bad_args; -+ clk = cg->hwaccel[idx]; -+ break; -+ case 3: -+ if (idx >= ARRAY_SIZE(cg->fman)) -+ goto bad_args; -+ clk = cg->fman[idx]; -+ break; -+ case 4: -+ pll = &cg->pll[PLATFORM_PLL]; -+ if (idx >= ARRAY_SIZE(pll->div)) -+ goto bad_args; -+ clk = pll->div[idx].clk; -+ break; -+ default: -+ goto bad_args; -+ } -+ -+ if (!clk) -+ return ERR_PTR(-ENOENT); -+ return clk; -+ -+bad_args: -+ pr_err("%s: Bad phandle args %u %u\n", __func__, type, idx); -+ return ERR_PTR(-EINVAL); -+} -+ -+#ifdef CONFIG_PPC -+ -+static const u32 a4510_svrs[] __initconst = { -+ (SVR_P2040 << 8) | 0x10, /* P2040 1.0 */ -+ (SVR_P2040 << 8) | 0x11, /* P2040 1.1 */ -+ (SVR_P2041 << 8) | 0x10, /* P2041 1.0 */ -+ (SVR_P2041 << 8) | 0x11, /* P2041 1.1 */ -+ (SVR_P3041 << 8) | 0x10, /* P3041 1.0 */ -+ (SVR_P3041 << 8) | 0x11, /* P3041 1.1 */ -+ (SVR_P4040 << 8) | 0x20, /* P4040 2.0 */ -+ (SVR_P4080 << 8) | 0x20, /* P4080 2.0 */ -+ (SVR_P5010 << 8) | 0x10, /* P5010 1.0 */ -+ (SVR_P5010 << 8) | 0x20, /* P5010 2.0 */ -+ (SVR_P5020 << 8) | 0x10, /* P5020 1.0 */ -+ (SVR_P5021 << 8) | 0x10, /* P5021 1.0 */ -+ (SVR_P5040 << 8) | 0x10, /* P5040 1.0 */ -+}; -+ -+#define SVR_SECURITY 0x80000 /* The Security (E) bit */ -+ -+static bool __init has_erratum_a4510(void) -+{ -+ u32 svr = mfspr(SPRN_SVR); -+ int i; -+ -+ svr &= ~SVR_SECURITY; -+ -+ for (i = 0; i < ARRAY_SIZE(a4510_svrs); i++) { -+ if (svr == a4510_svrs[i]) -+ return true; -+ } -+ -+ return false; -+} -+#else -+static bool __init has_erratum_a4510(void) -+{ -+ return false; -+} -+#endif -+ -+static void __init clockgen_init(struct device_node *np) -+{ -+ int i, ret; -+ bool is_old_ls1021a = false; -+ -+ /* May have already been called by a legacy probe */ -+ if (clockgen.node) -+ return; -+ -+ clockgen.node = np; -+ clockgen.regs = of_iomap(np, 0); -+ if (!clockgen.regs && -+ of_device_is_compatible(of_root, "fsl,ls1021a")) { -+ /* Compatibility hack for old, broken device trees */ -+ clockgen.regs = ioremap(0x1ee1000, 0x1000); -+ is_old_ls1021a = true; -+ } -+ if (!clockgen.regs) { -+ pr_err("%s(): %s: of_iomap() failed\n", __func__, np->name); -+ return; -+ } -+ -+ for (i = 0; i < ARRAY_SIZE(chipinfo); i++) { -+ if (of_device_is_compatible(np, chipinfo[i].compat)) -+ break; -+ if (is_old_ls1021a && -+ !strcmp(chipinfo[i].compat, "fsl,ls1021a-clockgen")) -+ break; -+ } -+ -+ if (i == ARRAY_SIZE(chipinfo)) { -+ pr_err("%s: unknown clockgen node %s\n", __func__, -+ np->full_name); -+ goto err; -+ } -+ clockgen.info = chipinfo[i]; -+ -+ if (clockgen.info.guts_compat) { -+ struct device_node *guts; -+ -+ guts = of_find_compatible_node(NULL, NULL, -+ clockgen.info.guts_compat); -+ if (guts) { -+ clockgen.guts = of_iomap(guts, 0); -+ if (!clockgen.guts) { -+ pr_err("%s: Couldn't map %s regs\n", __func__, -+ guts->full_name); -+ } -+ } -+ -+ } -+ -+ if (has_erratum_a4510()) -+ clockgen.info.flags |= CG_CMUX_GE_PLAT; -+ -+ clockgen.sysclk = create_sysclk("cg-sysclk"); -+ create_plls(&clockgen); -+ create_muxes(&clockgen); -+ -+ if (clockgen.info.init_periph) -+ clockgen.info.init_periph(&clockgen); -+ -+ ret = of_clk_add_provider(np, clockgen_clk_get, &clockgen); -+ if (ret) { -+ pr_err("%s: Couldn't register clk provider for node %s: %d\n", -+ __func__, np->name, ret); -+ } -+ -+ return; -+err: -+ iounmap(clockgen.regs); -+ clockgen.regs = NULL; -+} -+ -+CLK_OF_DECLARE(qoriq_clockgen_1, "fsl,qoriq-clockgen-1.0", clockgen_init); -+CLK_OF_DECLARE(qoriq_clockgen_2, "fsl,qoriq-clockgen-2.0", clockgen_init); -+CLK_OF_DECLARE(qoriq_clockgen_ls1021a, "fsl,ls1021a-clockgen", clockgen_init); -+CLK_OF_DECLARE(qoriq_clockgen_ls2080a, "fsl,ls2080a-clockgen", clockgen_init); -+CLK_OF_DECLARE(qoriq_clockgen_ls2088a, "fsl,ls2088a-clockgen", clockgen_init); -+ -+/* Legacy nodes */ -+CLK_OF_DECLARE(qoriq_sysclk_1, "fsl,qoriq-sysclk-1.0", sysclk_init); -+CLK_OF_DECLARE(qoriq_sysclk_2, "fsl,qoriq-sysclk-2.0", sysclk_init); -+CLK_OF_DECLARE(qoriq_core_pll_1, "fsl,qoriq-core-pll-1.0", core_pll_init); -+CLK_OF_DECLARE(qoriq_core_pll_2, "fsl,qoriq-core-pll-2.0", core_pll_init); -+CLK_OF_DECLARE(qoriq_core_mux_1, "fsl,qoriq-core-mux-1.0", core_mux_init); -+CLK_OF_DECLARE(qoriq_core_mux_2, "fsl,qoriq-core-mux-2.0", core_mux_init); -+CLK_OF_DECLARE(qoriq_pltfrm_pll_1, "fsl,qoriq-platform-pll-1.0", pltfrm_pll_init); -+CLK_OF_DECLARE(qoriq_pltfrm_pll_2, "fsl,qoriq-platform-pll-2.0", pltfrm_pll_init); -diff --git a/drivers/cpufreq/Kconfig.powerpc b/drivers/cpufreq/Kconfig.powerpc -index 72564b7..7ea2441 100644 ---- a/drivers/cpufreq/Kconfig.powerpc -+++ b/drivers/cpufreq/Kconfig.powerpc -@@ -26,7 +26,7 @@ config CPU_FREQ_MAPLE - config PPC_CORENET_CPUFREQ - tristate "CPU frequency scaling driver for Freescale E500MC SoCs" - depends on PPC_E500MC && OF && COMMON_CLK -- select CLK_PPC_CORENET -+ select CLK_QORIQ - help - This adds the CPUFreq driver support for Freescale e500mc, - e5500 and e6500 series SoCs which are capable of changing -diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig -index 06e99eb..bbf8ae4 100644 ---- a/drivers/i2c/busses/Kconfig -+++ b/drivers/i2c/busses/Kconfig -@@ -526,10 +526,10 @@ config I2C_IBM_IIC - - config I2C_IMX - tristate "IMX I2C interface" -- depends on ARCH_MXC -+ depends on ARCH_MXC || ARCH_LAYERSCAPE - help - Say Y here if you want to use the IIC bus controller on -- the Freescale i.MX/MXC processors. -+ the Freescale i.MX/MXC and layerscape processors. - - This driver can also be built as a module. If so, the module - will be called i2c-imx. -diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c -index e9fb7cf..13f88f9 100644 ---- a/drivers/i2c/busses/i2c-imx.c -+++ b/drivers/i2c/busses/i2c-imx.c -@@ -33,6 +33,10 @@ - *******************************************************************************/ - - #include -+#include -+#include -+#include -+#include - #include - #include - #include -@@ -47,6 +51,7 @@ - #include - #include - #include -+#include - #include - - /** Defines ******************************************************************** -@@ -58,6 +63,15 @@ - /* Default value */ - #define IMX_I2C_BIT_RATE 100000 /* 100kHz */ - -+/* -+ * Enable DMA if transfer byte size is bigger than this threshold. -+ * As the hardware request, it must bigger than 4 bytes.\ -+ * I have set '16' here, maybe it's not the best but I think it's -+ * the appropriate. -+ */ -+#define DMA_THRESHOLD 16 -+#define DMA_TIMEOUT 1000 -+ - /* IMX I2C registers: - * the I2C register offset is different between SoCs, - * to provid support for all these chips, split the -@@ -83,6 +97,7 @@ - #define I2SR_IBB 0x20 - #define I2SR_IAAS 0x40 - #define I2SR_ICF 0x80 -+#define I2CR_DMAEN 0x02 - #define I2CR_RSTA 0x04 - #define I2CR_TXAK 0x08 - #define I2CR_MTX 0x10 -@@ -169,6 +184,17 @@ struct imx_i2c_hwdata { - unsigned i2cr_ien_opcode; - }; - -+struct imx_i2c_dma { -+ struct dma_chan *chan_tx; -+ struct dma_chan *chan_rx; -+ struct dma_chan *chan_using; -+ struct completion cmd_complete; -+ dma_addr_t dma_buf; -+ unsigned int dma_len; -+ enum dma_transfer_direction dma_transfer_dir; -+ enum dma_data_direction dma_data_dir; -+}; -+ - struct imx_i2c_struct { - struct i2c_adapter adapter; - struct clk *clk; -@@ -181,6 +207,8 @@ struct imx_i2c_struct { - unsigned int cur_clk; - unsigned int bitrate; - const struct imx_i2c_hwdata *hwdata; -+ -+ struct imx_i2c_dma *dma; - }; - - static const struct imx_i2c_hwdata imx1_i2c_hwdata = { -@@ -251,6 +279,162 @@ static inline unsigned char imx_i2c_read_reg(struct imx_i2c_struct *i2c_imx, - return readb(i2c_imx->base + (reg << i2c_imx->hwdata->regshift)); - } - -+/* Functions for DMA support */ -+static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx, -+ dma_addr_t phy_addr) -+{ -+ struct imx_i2c_dma *dma; -+ struct dma_slave_config dma_sconfig; -+ struct device *dev = &i2c_imx->adapter.dev; -+ int ret; -+ -+ dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); -+ if (!dma) -+ return; -+ -+ dma->chan_tx = dma_request_slave_channel(dev, "tx"); -+ if (!dma->chan_tx) { -+ dev_dbg(dev, "can't request DMA tx channel\n"); -+ goto fail_al; -+ } -+ -+ dma_sconfig.dst_addr = phy_addr + -+ (IMX_I2C_I2DR << i2c_imx->hwdata->regshift); -+ dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; -+ dma_sconfig.dst_maxburst = 1; -+ dma_sconfig.direction = DMA_MEM_TO_DEV; -+ ret = dmaengine_slave_config(dma->chan_tx, &dma_sconfig); -+ if (ret < 0) { -+ dev_dbg(dev, "can't configure tx channel\n"); -+ goto fail_tx; -+ } -+ -+ dma->chan_rx = dma_request_slave_channel(dev, "rx"); -+ if (!dma->chan_rx) { -+ dev_dbg(dev, "can't request DMA rx channel\n"); -+ goto fail_tx; -+ } -+ -+ dma_sconfig.src_addr = phy_addr + -+ (IMX_I2C_I2DR << i2c_imx->hwdata->regshift); -+ dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; -+ dma_sconfig.src_maxburst = 1; -+ dma_sconfig.direction = DMA_DEV_TO_MEM; -+ ret = dmaengine_slave_config(dma->chan_rx, &dma_sconfig); -+ if (ret < 0) { -+ dev_dbg(dev, "can't configure rx channel\n"); -+ goto fail_rx; -+ } -+ -+ i2c_imx->dma = dma; -+ init_completion(&dma->cmd_complete); -+ dev_info(dev, "using %s (tx) and %s (rx) for DMA transfers\n", -+ dma_chan_name(dma->chan_tx), dma_chan_name(dma->chan_rx)); -+ -+ return; -+ -+fail_rx: -+ dma_release_channel(dma->chan_rx); -+fail_tx: -+ dma_release_channel(dma->chan_tx); -+fail_al: -+ devm_kfree(dev, dma); -+ dev_info(dev, "can't use DMA\n"); -+} -+ -+static void i2c_imx_dma_callback(void *arg) -+{ -+ struct imx_i2c_struct *i2c_imx = (struct imx_i2c_struct *)arg; -+ struct imx_i2c_dma *dma = i2c_imx->dma; -+ -+ dma_unmap_single(dma->chan_using->device->dev, dma->dma_buf, -+ dma->dma_len, dma->dma_data_dir); -+ complete(&dma->cmd_complete); -+} -+ -+static int i2c_imx_dma_xfer(struct imx_i2c_struct *i2c_imx, -+ struct i2c_msg *msgs) -+{ -+ struct imx_i2c_dma *dma = i2c_imx->dma; -+ struct dma_async_tx_descriptor *txdesc; -+ struct device *dev = &i2c_imx->adapter.dev; -+ struct device *chan_dev = dma->chan_using->device->dev; -+ -+ dma->dma_buf = dma_map_single(chan_dev, msgs->buf, -+ dma->dma_len, dma->dma_data_dir); -+ if (dma_mapping_error(chan_dev, dma->dma_buf)) { -+ dev_err(dev, "DMA mapping failed\n"); -+ goto err_map; -+ } -+ -+ txdesc = dmaengine_prep_slave_single(dma->chan_using, dma->dma_buf, -+ dma->dma_len, dma->dma_transfer_dir, -+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK); -+ if (!txdesc) { -+ dev_err(dev, "Not able to get desc for DMA xfer\n"); -+ goto err_desc; -+ } -+ -+ txdesc->callback = i2c_imx_dma_callback; -+ txdesc->callback_param = i2c_imx; -+ if (dma_submit_error(dmaengine_submit(txdesc))) { -+ dev_err(dev, "DMA submit failed\n"); -+ goto err_submit; -+ } -+ -+ dma_async_issue_pending(dma->chan_using); -+ return 0; -+ -+err_submit: -+err_desc: -+ dma_unmap_single(chan_dev, dma->dma_buf, -+ dma->dma_len, dma->dma_data_dir); -+err_map: -+ return -EINVAL; -+} -+ -+static void i2c_imx_dma_free(struct imx_i2c_struct *i2c_imx) -+{ -+ struct imx_i2c_dma *dma = i2c_imx->dma; -+ -+ dma->dma_buf = 0; -+ dma->dma_len = 0; -+ -+ dma_release_channel(dma->chan_tx); -+ dma->chan_tx = NULL; -+ -+ dma_release_channel(dma->chan_rx); -+ dma->chan_rx = NULL; -+ -+ dma->chan_using = NULL; -+} -+ -+/* -+ * When a system reset does not cause all I2C devices to be reset, it is -+ * sometimes necessary to force the I2C module to become the I2C bus master -+ * out of reset and drive SCL A slave can hold bus low to cause bus hang. -+ * Thus, SDA can be driven low by another I2C device while this I2C module -+ * is coming out of reset and will stay low indefinitely. -+ * The I2C master has to generate 9 clock pulses to get the bus free or idle. -+ */ -+static void imx_i2c_fixup(struct imx_i2c_struct *i2c_imx) -+{ -+ int k; -+ u32 delay_val = 1000000 / i2c_imx->cur_clk + 1; -+ -+ if (delay_val < 2) -+ delay_val = 2; -+ -+ for (k = 9; k; k--) { -+ imx_i2c_write_reg(I2CR_IEN, i2c_imx, IMX_I2C_I2CR); -+ imx_i2c_write_reg((I2CR_MSTA | I2CR_MTX) & (~I2CR_IEN), -+ i2c_imx, IMX_I2C_I2CR); -+ imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); -+ imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2CR); -+ udelay(delay_val << 1); -+ } -+} -+ - /** Functions for IMX I2C adapter driver *************************************** - *******************************************************************************/ - -@@ -276,8 +460,15 @@ static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy) - if (!for_busy && !(temp & I2SR_IBB)) - break; - if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) { -+ u8 status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR); -+ - dev_dbg(&i2c_imx->adapter.dev, - "<%s> I2C bus is busy\n", __func__); -+ if ((status & (I2SR_ICF | I2SR_IBB | I2CR_TXAK)) != 0) { -+ imx_i2c_write_reg(status & ~I2SR_IAL, i2c_imx, -+ IMX_I2C_I2CR); -+ imx_i2c_fixup(i2c_imx); -+ } - return -ETIMEDOUT; - } - schedule(); -@@ -382,6 +573,7 @@ static int i2c_imx_start(struct imx_i2c_struct *i2c_imx) - i2c_imx->stopped = 0; - - temp |= I2CR_IIEN | I2CR_MTX | I2CR_TXAK; -+ temp &= ~I2CR_DMAEN; - imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); - return result; - } -@@ -395,6 +587,8 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx) - dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__); - temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); - temp &= ~(I2CR_MSTA | I2CR_MTX); -+ if (i2c_imx->dma) -+ temp &= ~I2CR_DMAEN; - imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); - } - if (is_imx1_i2c(i2c_imx)) { -@@ -435,6 +629,157 @@ static irqreturn_t i2c_imx_isr(int irq, void *dev_id) - return IRQ_NONE; - } - -+static int i2c_imx_dma_write(struct imx_i2c_struct *i2c_imx, -+ struct i2c_msg *msgs) -+{ -+ int result; -+ unsigned long time_left; -+ unsigned int temp = 0; -+ unsigned long orig_jiffies = jiffies; -+ struct imx_i2c_dma *dma = i2c_imx->dma; -+ struct device *dev = &i2c_imx->adapter.dev; -+ -+ dma->chan_using = dma->chan_tx; -+ dma->dma_transfer_dir = DMA_MEM_TO_DEV; -+ dma->dma_data_dir = DMA_TO_DEVICE; -+ dma->dma_len = msgs->len - 1; -+ result = i2c_imx_dma_xfer(i2c_imx, msgs); -+ if (result) -+ return result; -+ -+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); -+ temp |= I2CR_DMAEN; -+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); -+ -+ /* -+ * Write slave address. -+ * The first byte must be transmitted by the CPU. -+ */ -+ imx_i2c_write_reg(msgs->addr << 1, i2c_imx, IMX_I2C_I2DR); -+ reinit_completion(&i2c_imx->dma->cmd_complete); -+ time_left = wait_for_completion_timeout( -+ &i2c_imx->dma->cmd_complete, -+ msecs_to_jiffies(DMA_TIMEOUT)); -+ if (time_left == 0) { -+ dmaengine_terminate_all(dma->chan_using); -+ return -ETIMEDOUT; -+ } -+ -+ /* Waiting for transfer complete. */ -+ while (1) { -+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR); -+ if (temp & I2SR_ICF) -+ break; -+ if (time_after(jiffies, orig_jiffies + -+ msecs_to_jiffies(DMA_TIMEOUT))) { -+ dev_dbg(dev, "<%s> Timeout\n", __func__); -+ return -ETIMEDOUT; -+ } -+ schedule(); -+ } -+ -+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); -+ temp &= ~I2CR_DMAEN; -+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); -+ -+ /* The last data byte must be transferred by the CPU. */ -+ imx_i2c_write_reg(msgs->buf[msgs->len-1], -+ i2c_imx, IMX_I2C_I2DR); -+ result = i2c_imx_trx_complete(i2c_imx); -+ if (result) -+ return result; -+ -+ return i2c_imx_acked(i2c_imx); -+} -+ -+static int i2c_imx_dma_read(struct imx_i2c_struct *i2c_imx, -+ struct i2c_msg *msgs, bool is_lastmsg) -+{ -+ int result; -+ unsigned long time_left; -+ unsigned int temp; -+ unsigned long orig_jiffies = jiffies; -+ struct imx_i2c_dma *dma = i2c_imx->dma; -+ struct device *dev = &i2c_imx->adapter.dev; -+ -+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); -+ temp |= I2CR_DMAEN; -+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); -+ -+ dma->chan_using = dma->chan_rx; -+ dma->dma_transfer_dir = DMA_DEV_TO_MEM; -+ dma->dma_data_dir = DMA_FROM_DEVICE; -+ /* The last two data bytes must be transferred by the CPU. */ -+ dma->dma_len = msgs->len - 2; -+ result = i2c_imx_dma_xfer(i2c_imx, msgs); -+ if (result) -+ return result; -+ -+ reinit_completion(&i2c_imx->dma->cmd_complete); -+ time_left = wait_for_completion_timeout( -+ &i2c_imx->dma->cmd_complete, -+ msecs_to_jiffies(DMA_TIMEOUT)); -+ if (time_left == 0) { -+ dmaengine_terminate_all(dma->chan_using); -+ return -ETIMEDOUT; -+ } -+ -+ /* waiting for transfer complete. */ -+ while (1) { -+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR); -+ if (temp & I2SR_ICF) -+ break; -+ if (time_after(jiffies, orig_jiffies + -+ msecs_to_jiffies(DMA_TIMEOUT))) { -+ dev_dbg(dev, "<%s> Timeout\n", __func__); -+ return -ETIMEDOUT; -+ } -+ schedule(); -+ } -+ -+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); -+ temp &= ~I2CR_DMAEN; -+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); -+ -+ /* read n-1 byte data */ -+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); -+ temp |= I2CR_TXAK; -+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); -+ -+ msgs->buf[msgs->len-2] = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); -+ /* read n byte data */ -+ result = i2c_imx_trx_complete(i2c_imx); -+ if (result) -+ return result; -+ -+ if (is_lastmsg) { -+ /* -+ * It must generate STOP before read I2DR to prevent -+ * controller from generating another clock cycle -+ */ -+ dev_dbg(dev, "<%s> clear MSTA\n", __func__); -+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); -+ temp &= ~(I2CR_MSTA | I2CR_MTX); -+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); -+ i2c_imx_bus_busy(i2c_imx, 0); -+ i2c_imx->stopped = 1; -+ } else { -+ /* -+ * For i2c master receiver repeat restart operation like: -+ * read -> repeat MSTA -> read/write -+ * The controller must set MTX before read the last byte in -+ * the first read operation, otherwise the first read cost -+ * one extra clock cycle. -+ */ -+ temp = readb(i2c_imx->base + IMX_I2C_I2CR); -+ temp |= I2CR_MTX; -+ writeb(temp, i2c_imx->base + IMX_I2C_I2CR); -+ } -+ msgs->buf[msgs->len-1] = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); -+ -+ return 0; -+} -+ - static int i2c_imx_write(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs) - { - int i, result; -@@ -504,6 +849,9 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, bo - - dev_dbg(&i2c_imx->adapter.dev, "<%s> read data\n", __func__); - -+ if (i2c_imx->dma && msgs->len >= DMA_THRESHOLD && !block_data) -+ return i2c_imx_dma_read(i2c_imx, msgs, is_lastmsg); -+ - /* read data */ - for (i = 0; i < msgs->len; i++) { - u8 len = 0; -@@ -577,6 +925,13 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter, - - dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__); - -+ /* workround for ERR010027: ensure that the I2C BUS is idle -+ before switching to master mode and attempting a Start cycle -+ */ -+ result = i2c_imx_bus_busy(i2c_imx, 0); -+ if (result) -+ goto fail0; -+ - /* Start I2C transfer */ - result = i2c_imx_start(i2c_imx); - if (result) -@@ -618,8 +973,12 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter, - #endif - if (msgs[i].flags & I2C_M_RD) - result = i2c_imx_read(i2c_imx, &msgs[i], is_lastmsg); -- else -- result = i2c_imx_write(i2c_imx, &msgs[i]); -+ else { -+ if (i2c_imx->dma && msgs[i].len >= DMA_THRESHOLD) -+ result = i2c_imx_dma_write(i2c_imx, &msgs[i]); -+ else -+ result = i2c_imx_write(i2c_imx, &msgs[i]); -+ } - if (result) - goto fail0; - } -@@ -654,6 +1013,7 @@ static int i2c_imx_probe(struct platform_device *pdev) - struct imxi2c_platform_data *pdata = dev_get_platdata(&pdev->dev); - void __iomem *base; - int irq, ret; -+ dma_addr_t phy_addr; - - dev_dbg(&pdev->dev, "<%s>\n", __func__); - -@@ -668,6 +1028,7 @@ static int i2c_imx_probe(struct platform_device *pdev) - if (IS_ERR(base)) - return PTR_ERR(base); - -+ phy_addr = (dma_addr_t)res->start; - i2c_imx = devm_kzalloc(&pdev->dev, sizeof(struct imx_i2c_struct), - GFP_KERNEL); - if (!i2c_imx) -@@ -701,7 +1062,7 @@ static int i2c_imx_probe(struct platform_device *pdev) - return ret; - } - /* Request IRQ */ -- ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr, 0, -+ ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr, IRQF_SHARED, - pdev->name, i2c_imx); - if (ret) { - dev_err(&pdev->dev, "can't claim irq %d\n", irq); -@@ -743,6 +1104,9 @@ static int i2c_imx_probe(struct platform_device *pdev) - i2c_imx->adapter.name); - dev_info(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n"); - -+ /* Init DMA config if support*/ -+ i2c_imx_dma_request(i2c_imx, phy_addr); -+ - return 0; /* Return OK */ - - clk_disable: -@@ -758,6 +1122,9 @@ static int i2c_imx_remove(struct platform_device *pdev) - dev_dbg(&i2c_imx->adapter.dev, "adapter removed\n"); - i2c_del_adapter(&i2c_imx->adapter); - -+ if (i2c_imx->dma) -+ i2c_imx_dma_free(i2c_imx); -+ - /* setup chip registers to defaults */ - imx_i2c_write_reg(0, i2c_imx, IMX_I2C_IADR); - imx_i2c_write_reg(0, i2c_imx, IMX_I2C_IFDR); -diff --git a/drivers/i2c/muxes/i2c-mux-pca9541.c b/drivers/i2c/muxes/i2c-mux-pca9541.c -index cb77277..0c8d4d2 100644 ---- a/drivers/i2c/muxes/i2c-mux-pca9541.c -+++ b/drivers/i2c/muxes/i2c-mux-pca9541.c -@@ -104,7 +104,7 @@ static int pca9541_reg_write(struct i2c_client *client, u8 command, u8 val) - buf[0] = command; - buf[1] = val; - msg.buf = buf; -- ret = adap->algo->master_xfer(adap, &msg, 1); -+ ret = __i2c_transfer(adap, &msg, 1); - } else { - union i2c_smbus_data data; - -@@ -144,7 +144,7 @@ static int pca9541_reg_read(struct i2c_client *client, u8 command) - .buf = &val - } - }; -- ret = adap->algo->master_xfer(adap, msg, 2); -+ ret = __i2c_transfer(adap, msg, 2); - if (ret == 2) - ret = val; - else if (ret >= 0) -diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c -index ec11b40..28540a4 100644 ---- a/drivers/i2c/muxes/i2c-mux-pca954x.c -+++ b/drivers/i2c/muxes/i2c-mux-pca954x.c -@@ -41,6 +41,7 @@ - #include - #include - #include -+#include - #include - #include - -@@ -62,6 +63,7 @@ struct pca954x { - struct i2c_adapter *virt_adaps[PCA954X_MAX_NCHANS]; - - u8 last_chan; /* last register value */ -+ u8 disable_mux; /* do not disable mux if val not 0 */ - }; - - struct chip_desc { -@@ -133,7 +135,7 @@ static int pca954x_reg_write(struct i2c_adapter *adap, - msg.len = 1; - buf[0] = val; - msg.buf = buf; -- ret = adap->algo->master_xfer(adap, &msg, 1); -+ ret = __i2c_transfer(adap, &msg, 1); - } else { - union i2c_smbus_data data; - ret = adap->algo->smbus_xfer(adap, client->addr, -@@ -173,6 +175,13 @@ static int pca954x_deselect_mux(struct i2c_adapter *adap, - { - struct pca954x *data = i2c_get_clientdata(client); - -+#ifdef CONFIG_ARCH_LAYERSCAPE -+ if (data->disable_mux != 0) -+ data->last_chan = chips[data->type].nchans; -+ else -+ data->last_chan = 0; -+ return pca954x_reg_write(adap, client, data->disable_mux); -+#endif - /* Deselect active channel */ - data->last_chan = 0; - return pca954x_reg_write(adap, client, data->last_chan); -@@ -186,6 +195,8 @@ static int pca954x_probe(struct i2c_client *client, - { - struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent); - struct pca954x_platform_data *pdata = dev_get_platdata(&client->dev); -+ struct device_node *of_node = client->dev.of_node; -+ bool idle_disconnect_dt; - struct gpio_desc *gpio; - int num, force, class; - struct pca954x *data; -@@ -198,27 +209,55 @@ static int pca954x_probe(struct i2c_client *client, - if (!data) - return -ENOMEM; - -+#ifdef CONFIG_ARCH_LAYERSCAPE -+ /* The point here is that you must not disable a mux if there -+ * are no pullups on the input or you mess up the I2C. This -+ * needs to be put into the DTS really as the kernel cannot -+ * know this otherwise. -+ */ -+ data->type = id->driver_data; -+ data->disable_mux = of_node && -+ of_property_read_bool(of_node, "i2c-mux-never-disable") && -+ chips[data->type].muxtype == pca954x_ismux ? -+ chips[data->type].enable : 0; -+ /* force the first selection */ -+ if (data->disable_mux != 0) -+ data->last_chan = chips[data->type].nchans; -+ else -+ data->last_chan = 0; -+#endif - i2c_set_clientdata(client, data); - - /* Get the mux out of reset if a reset GPIO is specified. */ -- gpio = devm_gpiod_get(&client->dev, "reset"); -- if (!IS_ERR(gpio)) -- gpiod_direction_output(gpio, 0); -+ gpio = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_LOW); -+ if (IS_ERR(gpio)) -+ return PTR_ERR(gpio); - - /* Write the mux register at addr to verify - * that the mux is in fact present. This also - * initializes the mux to disconnected state. - */ -+#ifdef CONFIG_ARCH_LAYERSCAPE -+ if (i2c_smbus_write_byte(client, data->disable_mux) < 0) { -+#else - if (i2c_smbus_write_byte(client, 0) < 0) { -+#endif - dev_warn(&client->dev, "probe failed\n"); - return -ENODEV; - } - -+#ifndef CONFIG_ARCH_LAYERSCAPE - data->type = id->driver_data; - data->last_chan = 0; /* force the first selection */ -+#endif -+ -+ idle_disconnect_dt = of_node && -+ of_property_read_bool(of_node, "i2c-mux-idle-disconnect"); - - /* Now create an adapter for each channel */ - for (num = 0; num < chips[data->type].nchans; num++) { -+ bool idle_disconnect_pd = false; -+ - force = 0; /* dynamic adap number */ - class = 0; /* no class by default */ - if (pdata) { -@@ -229,12 +268,13 @@ static int pca954x_probe(struct i2c_client *client, - } else - /* discard unconfigured channels */ - break; -+ idle_disconnect_pd = pdata->modes[num].deselect_on_exit; - } - - data->virt_adaps[num] = - i2c_add_mux_adapter(adap, &client->dev, client, - force, num, class, pca954x_select_chan, -- (pdata && pdata->modes[num].deselect_on_exit) -+ (idle_disconnect_pd || idle_disconnect_dt) - ? pca954x_deselect_mux : NULL); - - if (data->virt_adaps[num] == NULL) { -@@ -280,6 +320,13 @@ static int pca954x_resume(struct device *dev) - struct i2c_client *client = to_i2c_client(dev); - struct pca954x *data = i2c_get_clientdata(client); - -+#ifdef CONFIG_ARCH_LAYERSCAPE -+ if (data->disable_mux != 0) -+ data->last_chan = chips[data->type].nchans; -+ else -+ data->last_chan = 0; -+ return i2c_smbus_write_byte(client, data->disable_mux); -+#endif - data->last_chan = 0; - return i2c_smbus_write_byte(client, 0); - } -diff --git a/drivers/iommu/fsl_pamu.c b/drivers/iommu/fsl_pamu.c -index 80ac68d..9396c85 100644 ---- a/drivers/iommu/fsl_pamu.c -+++ b/drivers/iommu/fsl_pamu.c -@@ -31,7 +31,7 @@ - #include - #include - #include --#include -+#include - - #include "fsl_pamu.h" - -diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c -index 5a500ed..fd6dd22 100644 ---- a/drivers/iommu/io-pgtable-arm.c -+++ b/drivers/iommu/io-pgtable-arm.c -@@ -56,7 +56,8 @@ - ((((d)->levels - ((l) - ARM_LPAE_START_LVL(d) + 1)) \ - * (d)->bits_per_level) + (d)->pg_shift) - --#define ARM_LPAE_PAGES_PER_PGD(d) ((d)->pgd_size >> (d)->pg_shift) -+#define ARM_LPAE_PAGES_PER_PGD(d) \ -+ DIV_ROUND_UP((d)->pgd_size, 1UL << (d)->pg_shift) - - /* - * Calculate the index at level l used to map virtual address a using the -@@ -66,7 +67,7 @@ - ((l) == ARM_LPAE_START_LVL(d) ? ilog2(ARM_LPAE_PAGES_PER_PGD(d)) : 0) - - #define ARM_LPAE_LVL_IDX(a,l,d) \ -- (((a) >> ARM_LPAE_LVL_SHIFT(l,d)) & \ -+ (((u64)(a) >> ARM_LPAE_LVL_SHIFT(l,d)) & \ - ((1 << ((d)->bits_per_level + ARM_LPAE_PGD_IDX(l,d))) - 1)) - - /* Calculate the block/page mapping size at level l for pagetable in d. */ -@@ -115,6 +116,8 @@ - #define ARM_32_LPAE_TCR_EAE (1 << 31) - #define ARM_64_LPAE_S2_TCR_RES1 (1 << 31) - -+#define ARM_LPAE_TCR_EPD1 (1 << 23) -+ - #define ARM_LPAE_TCR_TG0_4K (0 << 14) - #define ARM_LPAE_TCR_TG0_64K (1 << 14) - #define ARM_LPAE_TCR_TG0_16K (2 << 14) -@@ -283,6 +286,9 @@ static arm_lpae_iopte arm_lpae_prot_to_pte(struct arm_lpae_io_pgtable *data, - if (prot & IOMMU_CACHE) - pte |= (ARM_LPAE_MAIR_ATTR_IDX_CACHE - << ARM_LPAE_PTE_ATTRINDX_SHIFT); -+ else if (prot & IOMMU_MMIO) -+ pte |= (ARM_LPAE_MAIR_ATTR_IDX_DEV -+ << ARM_LPAE_PTE_ATTRINDX_SHIFT); - } else { - pte = ARM_LPAE_PTE_HAP_FAULT; - if (prot & IOMMU_READ) -@@ -291,6 +297,8 @@ static arm_lpae_iopte arm_lpae_prot_to_pte(struct arm_lpae_io_pgtable *data, - pte |= ARM_LPAE_PTE_HAP_WRITE; - if (prot & IOMMU_CACHE) - pte |= ARM_LPAE_PTE_MEMATTR_OIWB; -+ else if (prot & IOMMU_MMIO) -+ pte |= ARM_LPAE_PTE_MEMATTR_DEV; - else - pte |= ARM_LPAE_PTE_MEMATTR_NC; - } -@@ -620,6 +628,9 @@ arm_64_lpae_alloc_pgtable_s1(struct io_pgtable_cfg *cfg, void *cookie) - } - - reg |= (64ULL - cfg->ias) << ARM_LPAE_TCR_T0SZ_SHIFT; -+ -+ /* Disable speculative walks through TTBR1 */ -+ reg |= ARM_LPAE_TCR_EPD1; - cfg->arm_lpae_s1_cfg.tcr = reg; - - /* MAIRs */ -diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig -index caf590c..e72e239 100644 ---- a/drivers/irqchip/Kconfig -+++ b/drivers/irqchip/Kconfig -@@ -5,8 +5,15 @@ config IRQCHIP - config ARM_GIC - bool - select IRQ_DOMAIN -+ select IRQ_DOMAIN_HIERARCHY - select MULTI_IRQ_HANDLER - -+config ARM_GIC_V2M -+ bool -+ depends on ARM_GIC -+ depends on PCI && PCI_MSI -+ select PCI_MSI_IRQ_DOMAIN -+ - config GIC_NON_BANKED - bool - -@@ -14,6 +21,7 @@ config ARM_GIC_V3 - bool - select IRQ_DOMAIN - select MULTI_IRQ_HANDLER -+ select IRQ_DOMAIN_HIERARCHY - - config ARM_GIC_V3_ITS - bool -diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile -index ec3621d..1c4f9a4 100644 ---- a/drivers/irqchip/Makefile -+++ b/drivers/irqchip/Makefile -@@ -19,6 +19,7 @@ obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o - obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o - obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o - obj-$(CONFIG_ARM_GIC) += irq-gic.o irq-gic-common.o -+obj-$(CONFIG_ARM_GIC_V2M) += irq-gic-v2m.o - obj-$(CONFIG_ARM_GIC_V3) += irq-gic-v3.o irq-gic-common.o - obj-$(CONFIG_ARM_GIC_V3_ITS) += irq-gic-v3-its.o - obj-$(CONFIG_ARM_NVIC) += irq-nvic.o -diff --git a/drivers/irqchip/irq-gic-common.c b/drivers/irqchip/irq-gic-common.c -index 61541ff..ad96ebb 100644 ---- a/drivers/irqchip/irq-gic-common.c -+++ b/drivers/irqchip/irq-gic-common.c -@@ -21,7 +21,7 @@ - - #include "irq-gic-common.h" - --void gic_configure_irq(unsigned int irq, unsigned int type, -+int gic_configure_irq(unsigned int irq, unsigned int type, - void __iomem *base, void (*sync_access)(void)) - { - u32 enablemask = 1 << (irq % 32); -@@ -29,16 +29,17 @@ void gic_configure_irq(unsigned int irq, unsigned int type, - u32 confmask = 0x2 << ((irq % 16) * 2); - u32 confoff = (irq / 16) * 4; - bool enabled = false; -- u32 val; -+ u32 val, oldval; -+ int ret = 0; - - /* - * Read current configuration register, and insert the config - * for "irq", depending on "type". - */ -- val = readl_relaxed(base + GIC_DIST_CONFIG + confoff); -- if (type == IRQ_TYPE_LEVEL_HIGH) -+ val = oldval = readl_relaxed(base + GIC_DIST_CONFIG + confoff); -+ if (type & IRQ_TYPE_LEVEL_MASK) - val &= ~confmask; -- else if (type == IRQ_TYPE_EDGE_RISING) -+ else if (type & IRQ_TYPE_EDGE_BOTH) - val |= confmask; - - /* -@@ -54,15 +55,20 @@ void gic_configure_irq(unsigned int irq, unsigned int type, - - /* - * Write back the new configuration, and possibly re-enable -- * the interrupt. -+ * the interrupt. If we tried to write a new configuration and failed, -+ * return an error. - */ - writel_relaxed(val, base + GIC_DIST_CONFIG + confoff); -+ if (readl_relaxed(base + GIC_DIST_CONFIG + confoff) != val && val != oldval) -+ ret = -EINVAL; - - if (enabled) - writel_relaxed(enablemask, base + GIC_DIST_ENABLE_SET + enableoff); - - if (sync_access) - sync_access(); -+ -+ return ret; - } - - void __init gic_dist_config(void __iomem *base, int gic_irqs, -diff --git a/drivers/irqchip/irq-gic-common.h b/drivers/irqchip/irq-gic-common.h -index b41f024..35a9884 100644 ---- a/drivers/irqchip/irq-gic-common.h -+++ b/drivers/irqchip/irq-gic-common.h -@@ -20,7 +20,7 @@ - #include - #include - --void gic_configure_irq(unsigned int irq, unsigned int type, -+int gic_configure_irq(unsigned int irq, unsigned int type, - void __iomem *base, void (*sync_access)(void)); - void gic_dist_config(void __iomem *base, int gic_irqs, - void (*sync_access)(void)); -diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c -new file mode 100644 -index 0000000..fdf7065 ---- /dev/null -+++ b/drivers/irqchip/irq-gic-v2m.c -@@ -0,0 +1,333 @@ -+/* -+ * ARM GIC v2m MSI(-X) support -+ * Support for Message Signaled Interrupts for systems that -+ * implement ARM Generic Interrupt Controller: GICv2m. -+ * -+ * Copyright (C) 2014 Advanced Micro Devices, Inc. -+ * Authors: Suravee Suthikulpanit -+ * Harish Kasiviswanathan -+ * Brandon Anderson -+ * -+ * 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) "GICv2m: " fmt -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* -+* MSI_TYPER: -+* [31:26] Reserved -+* [25:16] lowest SPI assigned to MSI -+* [15:10] Reserved -+* [9:0] Numer of SPIs assigned to MSI -+*/ -+#define V2M_MSI_TYPER 0x008 -+#define V2M_MSI_TYPER_BASE_SHIFT 16 -+#define V2M_MSI_TYPER_BASE_MASK 0x3FF -+#define V2M_MSI_TYPER_NUM_MASK 0x3FF -+#define V2M_MSI_SETSPI_NS 0x040 -+#define V2M_MIN_SPI 32 -+#define V2M_MAX_SPI 1019 -+ -+#define V2M_MSI_TYPER_BASE_SPI(x) \ -+ (((x) >> V2M_MSI_TYPER_BASE_SHIFT) & V2M_MSI_TYPER_BASE_MASK) -+ -+#define V2M_MSI_TYPER_NUM_SPI(x) ((x) & V2M_MSI_TYPER_NUM_MASK) -+ -+struct v2m_data { -+ spinlock_t msi_cnt_lock; -+ struct msi_controller mchip; -+ struct resource res; /* GICv2m resource */ -+ void __iomem *base; /* GICv2m virt address */ -+ u32 spi_start; /* The SPI number that MSIs start */ -+ u32 nr_spis; /* The number of SPIs for MSIs */ -+ unsigned long *bm; /* MSI vector bitmap */ -+ struct irq_domain *domain; -+}; -+ -+static void gicv2m_mask_msi_irq(struct irq_data *d) -+{ -+ pci_msi_mask_irq(d); -+ irq_chip_mask_parent(d); -+} -+ -+static void gicv2m_unmask_msi_irq(struct irq_data *d) -+{ -+ pci_msi_unmask_irq(d); -+ irq_chip_unmask_parent(d); -+} -+ -+static struct irq_chip gicv2m_msi_irq_chip = { -+ .name = "MSI", -+ .irq_mask = gicv2m_mask_msi_irq, -+ .irq_unmask = gicv2m_unmask_msi_irq, -+ .irq_eoi = irq_chip_eoi_parent, -+ .irq_write_msi_msg = pci_msi_domain_write_msg, -+}; -+ -+static struct msi_domain_info gicv2m_msi_domain_info = { -+ .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | -+ MSI_FLAG_PCI_MSIX), -+ .chip = &gicv2m_msi_irq_chip, -+}; -+ -+static int gicv2m_set_affinity(struct irq_data *irq_data, -+ const struct cpumask *mask, bool force) -+{ -+ int ret; -+ -+ ret = irq_chip_set_affinity_parent(irq_data, mask, force); -+ if (ret == IRQ_SET_MASK_OK) -+ ret = IRQ_SET_MASK_OK_DONE; -+ -+ return ret; -+} -+ -+static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) -+{ -+ struct v2m_data *v2m = irq_data_get_irq_chip_data(data); -+ phys_addr_t addr = v2m->res.start + V2M_MSI_SETSPI_NS; -+ -+ msg->address_hi = (u32) (addr >> 32); -+ msg->address_lo = (u32) (addr); -+ msg->data = data->hwirq; -+} -+ -+static struct irq_chip gicv2m_irq_chip = { -+ .name = "GICv2m", -+ .irq_mask = irq_chip_mask_parent, -+ .irq_unmask = irq_chip_unmask_parent, -+ .irq_eoi = irq_chip_eoi_parent, -+ .irq_set_affinity = gicv2m_set_affinity, -+ .irq_compose_msi_msg = gicv2m_compose_msi_msg, -+}; -+ -+static int gicv2m_irq_gic_domain_alloc(struct irq_domain *domain, -+ unsigned int virq, -+ irq_hw_number_t hwirq) -+{ -+ struct of_phandle_args args; -+ struct irq_data *d; -+ int err; -+ -+ args.np = domain->parent->of_node; -+ args.args_count = 3; -+ args.args[0] = 0; -+ args.args[1] = hwirq - 32; -+ args.args[2] = IRQ_TYPE_EDGE_RISING; -+ -+ err = irq_domain_alloc_irqs_parent(domain, virq, 1, &args); -+ if (err) -+ return err; -+ -+ /* Configure the interrupt line to be edge */ -+ d = irq_domain_get_irq_data(domain->parent, virq); -+ d->chip->irq_set_type(d, IRQ_TYPE_EDGE_RISING); -+ return 0; -+} -+ -+static void gicv2m_unalloc_msi(struct v2m_data *v2m, unsigned int hwirq) -+{ -+ int pos; -+ -+ pos = hwirq - v2m->spi_start; -+ if (pos < 0 || pos >= v2m->nr_spis) { -+ pr_err("Failed to teardown msi. Invalid hwirq %d\n", hwirq); -+ return; -+ } -+ -+ spin_lock(&v2m->msi_cnt_lock); -+ __clear_bit(pos, v2m->bm); -+ spin_unlock(&v2m->msi_cnt_lock); -+} -+ -+static int gicv2m_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, -+ unsigned int nr_irqs, void *args) -+{ -+ struct v2m_data *v2m = domain->host_data; -+ int hwirq, offset, err = 0; -+ -+ spin_lock(&v2m->msi_cnt_lock); -+ offset = find_first_zero_bit(v2m->bm, v2m->nr_spis); -+ if (offset < v2m->nr_spis) -+ __set_bit(offset, v2m->bm); -+ else -+ err = -ENOSPC; -+ spin_unlock(&v2m->msi_cnt_lock); -+ -+ if (err) -+ return err; -+ -+ hwirq = v2m->spi_start + offset; -+ -+ err = gicv2m_irq_gic_domain_alloc(domain, virq, hwirq); -+ if (err) { -+ gicv2m_unalloc_msi(v2m, hwirq); -+ return err; -+ } -+ -+ irq_domain_set_hwirq_and_chip(domain, virq, hwirq, -+ &gicv2m_irq_chip, v2m); -+ -+ return 0; -+} -+ -+static void gicv2m_irq_domain_free(struct irq_domain *domain, -+ unsigned int virq, unsigned int nr_irqs) -+{ -+ struct irq_data *d = irq_domain_get_irq_data(domain, virq); -+ struct v2m_data *v2m = irq_data_get_irq_chip_data(d); -+ -+ BUG_ON(nr_irqs != 1); -+ gicv2m_unalloc_msi(v2m, d->hwirq); -+ irq_domain_free_irqs_parent(domain, virq, nr_irqs); -+} -+ -+static const struct irq_domain_ops gicv2m_domain_ops = { -+ .alloc = gicv2m_irq_domain_alloc, -+ .free = gicv2m_irq_domain_free, -+}; -+ -+static bool is_msi_spi_valid(u32 base, u32 num) -+{ -+ if (base < V2M_MIN_SPI) { -+ pr_err("Invalid MSI base SPI (base:%u)\n", base); -+ return false; -+ } -+ -+ if ((num == 0) || (base + num > V2M_MAX_SPI)) { -+ pr_err("Number of SPIs (%u) exceed maximum (%u)\n", -+ num, V2M_MAX_SPI - V2M_MIN_SPI + 1); -+ return false; -+ } -+ -+ return true; -+} -+ -+static int __init gicv2m_init_one(struct device_node *node, -+ struct irq_domain *parent) -+{ -+ int ret; -+ struct v2m_data *v2m; -+ -+ v2m = kzalloc(sizeof(struct v2m_data), GFP_KERNEL); -+ if (!v2m) { -+ pr_err("Failed to allocate struct v2m_data.\n"); -+ return -ENOMEM; -+ } -+ -+ ret = of_address_to_resource(node, 0, &v2m->res); -+ if (ret) { -+ pr_err("Failed to allocate v2m resource.\n"); -+ goto err_free_v2m; -+ } -+ -+ v2m->base = ioremap(v2m->res.start, resource_size(&v2m->res)); -+ if (!v2m->base) { -+ pr_err("Failed to map GICv2m resource\n"); -+ ret = -ENOMEM; -+ goto err_free_v2m; -+ } -+ -+ if (!of_property_read_u32(node, "arm,msi-base-spi", &v2m->spi_start) && -+ !of_property_read_u32(node, "arm,msi-num-spis", &v2m->nr_spis)) { -+ pr_info("Overriding V2M MSI_TYPER (base:%u, num:%u)\n", -+ v2m->spi_start, v2m->nr_spis); -+ } else { -+ u32 typer = readl_relaxed(v2m->base + V2M_MSI_TYPER); -+ -+ v2m->spi_start = V2M_MSI_TYPER_BASE_SPI(typer); -+ v2m->nr_spis = V2M_MSI_TYPER_NUM_SPI(typer); -+ } -+ -+ if (!is_msi_spi_valid(v2m->spi_start, v2m->nr_spis)) { -+ ret = -EINVAL; -+ goto err_iounmap; -+ } -+ -+ v2m->bm = kzalloc(sizeof(long) * BITS_TO_LONGS(v2m->nr_spis), -+ GFP_KERNEL); -+ if (!v2m->bm) { -+ ret = -ENOMEM; -+ goto err_iounmap; -+ } -+ -+ v2m->domain = irq_domain_add_tree(NULL, &gicv2m_domain_ops, v2m); -+ if (!v2m->domain) { -+ pr_err("Failed to create GICv2m domain\n"); -+ ret = -ENOMEM; -+ goto err_free_bm; -+ } -+ -+ v2m->domain->parent = parent; -+ v2m->mchip.of_node = node; -+ v2m->mchip.domain = pci_msi_create_irq_domain(node, -+ &gicv2m_msi_domain_info, -+ v2m->domain); -+ if (!v2m->mchip.domain) { -+ pr_err("Failed to create MSI domain\n"); -+ ret = -ENOMEM; -+ goto err_free_domains; -+ } -+ -+ spin_lock_init(&v2m->msi_cnt_lock); -+ -+ ret = of_pci_msi_chip_add(&v2m->mchip); -+ if (ret) { -+ pr_err("Failed to add msi_chip.\n"); -+ goto err_free_domains; -+ } -+ -+ pr_info("Node %s: range[%#lx:%#lx], SPI[%d:%d]\n", node->name, -+ (unsigned long)v2m->res.start, (unsigned long)v2m->res.end, -+ v2m->spi_start, (v2m->spi_start + v2m->nr_spis)); -+ -+ return 0; -+ -+err_free_domains: -+ if (v2m->mchip.domain) -+ irq_domain_remove(v2m->mchip.domain); -+ if (v2m->domain) -+ irq_domain_remove(v2m->domain); -+err_free_bm: -+ kfree(v2m->bm); -+err_iounmap: -+ iounmap(v2m->base); -+err_free_v2m: -+ kfree(v2m); -+ return ret; -+} -+ -+static struct of_device_id gicv2m_device_id[] = { -+ { .compatible = "arm,gic-v2m-frame", }, -+ {}, -+}; -+ -+int __init gicv2m_of_init(struct device_node *node, struct irq_domain *parent) -+{ -+ int ret = 0; -+ struct device_node *child; -+ -+ for (child = of_find_matching_node(node, gicv2m_device_id); child; -+ child = of_find_matching_node(child, gicv2m_device_id)) { -+ if (!of_find_property(child, "msi-controller", NULL)) -+ continue; -+ -+ ret = gicv2m_init_one(child, parent); -+ if (ret) { -+ of_node_put(node); -+ break; -+ } -+ } -+ -+ return ret; -+} -diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c -index 43c50ed..d689158 100644 ---- a/drivers/irqchip/irq-gic-v3-its.c -+++ b/drivers/irqchip/irq-gic-v3-its.c -@@ -1293,7 +1293,8 @@ static int its_msi_prepare(struct irq_domain *domain, struct device *dev, - - dev_dbg(dev, "ITT %d entries, %d bits\n", nvec, ilog2(nvec)); - dev_id = PCI_DEVID(pdev->bus->number, pdev->devfn); -- return __its_msi_prepare(domain->parent, dev_alias.dev_id, dev, dev_alias.count, info); -+ return __its_msi_prepare(domain, dev_alias.dev_id, -+ dev, dev_alias.count, info); - } - - static struct msi_domain_ops its_pci_msi_ops = { -@@ -1535,13 +1536,14 @@ static int its_probe(struct device_node *node, struct irq_domain *parent) - writel_relaxed(GITS_CTLR_ENABLE, its->base + GITS_CTLR); - - if (of_property_read_bool(its->msi_chip.of_node, "msi-controller")) { -- its->domain = irq_domain_add_tree(NULL, &its_domain_ops, its); -+ its->domain = irq_domain_add_tree(node, &its_domain_ops, its); - if (!its->domain) { - err = -ENOMEM; - goto out_free_tables; - } - - its->domain->parent = parent; -+ its->domain->bus_token = DOMAIN_BUS_NEXUS; - - its->msi_chip.domain = pci_msi_create_irq_domain(node, - &its_pci_msi_domain_info, -diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c -index 34feda3..fd8850d 100644 ---- a/drivers/irqchip/irq-gic-v3.c -+++ b/drivers/irqchip/irq-gic-v3.c -@@ -238,7 +238,9 @@ static int gic_set_type(struct irq_data *d, unsigned int type) - if (irq < 16) - return -EINVAL; - -- if (type != IRQ_TYPE_LEVEL_HIGH && type != IRQ_TYPE_EDGE_RISING) -+ /* SPIs have restrictions on the supported types */ -+ if (irq >= 32 && type != IRQ_TYPE_LEVEL_HIGH && -+ type != IRQ_TYPE_EDGE_RISING) - return -EINVAL; - - if (gic_irq_in_rdist(d)) { -@@ -249,9 +251,7 @@ static int gic_set_type(struct irq_data *d, unsigned int type) - rwp_wait = gic_dist_wait_for_rwp; - } - -- gic_configure_irq(irq, type, base, rwp_wait); -- -- return 0; -+ return gic_configure_irq(irq, type, base, rwp_wait); - } - - static u64 gic_mpidr_to_affinity(u64 mpidr) -@@ -466,7 +466,7 @@ static u16 gic_compute_target_list(int *base_cpu, const struct cpumask *mask, - tlist |= 1 << (mpidr & 0xf); - - cpu = cpumask_next(cpu, mask); -- if (cpu == nr_cpu_ids) -+ if (cpu >= nr_cpu_ids) - goto out; - - mpidr = cpu_logical_map(cpu); -@@ -481,15 +481,19 @@ out: - return tlist; - } - -+#define MPIDR_TO_SGI_AFFINITY(cluster_id, level) \ -+ (MPIDR_AFFINITY_LEVEL(cluster_id, level) \ -+ << ICC_SGI1R_AFFINITY_## level ##_SHIFT) -+ - static void gic_send_sgi(u64 cluster_id, u16 tlist, unsigned int irq) - { - u64 val; - -- val = (MPIDR_AFFINITY_LEVEL(cluster_id, 3) << 48 | -- MPIDR_AFFINITY_LEVEL(cluster_id, 2) << 32 | -- irq << 24 | -- MPIDR_AFFINITY_LEVEL(cluster_id, 1) << 16 | -- tlist); -+ val = (MPIDR_TO_SGI_AFFINITY(cluster_id, 3) | -+ MPIDR_TO_SGI_AFFINITY(cluster_id, 2) | -+ irq << ICC_SGI1R_SGI_ID_SHIFT | -+ MPIDR_TO_SGI_AFFINITY(cluster_id, 1) | -+ tlist << ICC_SGI1R_TARGET_LIST_SHIFT); - - pr_debug("CPU%d: ICC_SGI1R_EL1 %llx\n", smp_processor_id(), val); - gic_write_sgi1r(val); -@@ -617,14 +621,14 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, - /* PPIs */ - if (hw < 32) { - irq_set_percpu_devid(irq); -- irq_set_chip_and_handler(irq, &gic_chip, -- handle_percpu_devid_irq); -+ irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data, -+ handle_percpu_devid_irq, NULL, NULL); - set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN); - } - /* SPIs */ - if (hw >= 32 && hw < gic_data.irq_nr) { -- irq_set_chip_and_handler(irq, &gic_chip, -- handle_fasteoi_irq); -+ irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data, -+ handle_fasteoi_irq, NULL, NULL); - set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); - } - /* LPIs */ -@@ -667,9 +671,41 @@ static int gic_irq_domain_xlate(struct irq_domain *d, - return 0; - } - -+static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, -+ unsigned int nr_irqs, void *arg) -+{ -+ int i, ret; -+ irq_hw_number_t hwirq; -+ unsigned int type = IRQ_TYPE_NONE; -+ struct of_phandle_args *irq_data = arg; -+ -+ ret = gic_irq_domain_xlate(domain, irq_data->np, irq_data->args, -+ irq_data->args_count, &hwirq, &type); -+ if (ret) -+ return ret; -+ -+ for (i = 0; i < nr_irqs; i++) -+ gic_irq_domain_map(domain, virq + i, hwirq + i); -+ -+ return 0; -+} -+ -+static void gic_irq_domain_free(struct irq_domain *domain, unsigned int virq, -+ unsigned int nr_irqs) -+{ -+ int i; -+ -+ for (i = 0; i < nr_irqs; i++) { -+ struct irq_data *d = irq_domain_get_irq_data(domain, virq + i); -+ irq_set_handler(virq + i, NULL); -+ irq_domain_reset_irq_data(d); -+ } -+} -+ - static const struct irq_domain_ops gic_irq_domain_ops = { -- .map = gic_irq_domain_map, - .xlate = gic_irq_domain_xlate, -+ .alloc = gic_irq_domain_alloc, -+ .free = gic_irq_domain_free, - }; - - static int __init gic_of_init(struct device_node *node, struct device_node *parent) -diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c -index 38493ff..ab0b1cb 100644 ---- a/drivers/irqchip/irq-gic.c -+++ b/drivers/irqchip/irq-gic.c -@@ -188,12 +188,15 @@ static int gic_set_type(struct irq_data *d, unsigned int type) - { - void __iomem *base = gic_dist_base(d); - unsigned int gicirq = gic_irq(d); -+ int ret; - - /* Interrupt configuration for SGIs can't be changed */ - if (gicirq < 16) - return -EINVAL; - -- if (type != IRQ_TYPE_LEVEL_HIGH && type != IRQ_TYPE_EDGE_RISING) -+ /* SPIs have restrictions on the supported types */ -+ if (gicirq >= 32 && type != IRQ_TYPE_LEVEL_HIGH && -+ type != IRQ_TYPE_EDGE_RISING) - return -EINVAL; - - raw_spin_lock(&irq_controller_lock); -@@ -201,11 +204,11 @@ static int gic_set_type(struct irq_data *d, unsigned int type) - if (gic_arch_extn.irq_set_type) - gic_arch_extn.irq_set_type(d, type); - -- gic_configure_irq(gicirq, type, base, NULL); -+ ret = gic_configure_irq(gicirq, type, base, NULL); - - raw_spin_unlock(&irq_controller_lock); - -- return 0; -+ return ret; - } - - static int gic_retrigger(struct irq_data *d) -@@ -788,17 +791,16 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, - { - if (hw < 32) { - irq_set_percpu_devid(irq); -- irq_set_chip_and_handler(irq, &gic_chip, -- handle_percpu_devid_irq); -+ irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data, -+ handle_percpu_devid_irq, NULL, NULL); - set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN); - } else { -- irq_set_chip_and_handler(irq, &gic_chip, -- handle_fasteoi_irq); -+ irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data, -+ handle_fasteoi_irq, NULL, NULL); - set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); - - gic_routable_irq_domain_ops->map(d, irq, hw); - } -- irq_set_chip_data(irq, d->host_data); - return 0; - } - -@@ -858,6 +860,31 @@ static struct notifier_block gic_cpu_notifier = { - }; - #endif - -+static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, -+ unsigned int nr_irqs, void *arg) -+{ -+ int i, ret; -+ irq_hw_number_t hwirq; -+ unsigned int type = IRQ_TYPE_NONE; -+ struct of_phandle_args *irq_data = arg; -+ -+ ret = gic_irq_domain_xlate(domain, irq_data->np, irq_data->args, -+ irq_data->args_count, &hwirq, &type); -+ if (ret) -+ return ret; -+ -+ for (i = 0; i < nr_irqs; i++) -+ gic_irq_domain_map(domain, virq + i, hwirq + i); -+ -+ return 0; -+} -+ -+static const struct irq_domain_ops gic_irq_domain_hierarchy_ops = { -+ .xlate = gic_irq_domain_xlate, -+ .alloc = gic_irq_domain_alloc, -+ .free = irq_domain_free_irqs_top, -+}; -+ - static const struct irq_domain_ops gic_irq_domain_ops = { - .map = gic_irq_domain_map, - .unmap = gic_irq_domain_unmap, -@@ -948,18 +975,6 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, - gic_cpu_map[i] = 0xff; - - /* -- * For primary GICs, skip over SGIs. -- * For secondary GICs, skip over PPIs, too. -- */ -- if (gic_nr == 0 && (irq_start & 31) > 0) { -- hwirq_base = 16; -- if (irq_start != -1) -- irq_start = (irq_start & ~31) + 16; -- } else { -- hwirq_base = 32; -- } -- -- /* - * Find out how many interrupts are supported. - * The GIC only supports up to 1020 interrupt sources. - */ -@@ -969,10 +984,31 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, - gic_irqs = 1020; - gic->gic_irqs = gic_irqs; - -- gic_irqs -= hwirq_base; /* calculate # of irqs to allocate */ -+ if (node) { /* DT case */ -+ const struct irq_domain_ops *ops = &gic_irq_domain_hierarchy_ops; -+ -+ if (!of_property_read_u32(node, "arm,routable-irqs", -+ &nr_routable_irqs)) { -+ ops = &gic_irq_domain_ops; -+ gic_irqs = nr_routable_irqs; -+ } -+ -+ gic->domain = irq_domain_add_linear(node, gic_irqs, ops, gic); -+ } else { /* Non-DT case */ -+ /* -+ * For primary GICs, skip over SGIs. -+ * For secondary GICs, skip over PPIs, too. -+ */ -+ if (gic_nr == 0 && (irq_start & 31) > 0) { -+ hwirq_base = 16; -+ if (irq_start != -1) -+ irq_start = (irq_start & ~31) + 16; -+ } else { -+ hwirq_base = 32; -+ } -+ -+ gic_irqs -= hwirq_base; /* calculate # of irqs to allocate */ - -- if (of_property_read_u32(node, "arm,routable-irqs", -- &nr_routable_irqs)) { - irq_base = irq_alloc_descs(irq_start, 16, gic_irqs, - numa_node_id()); - if (IS_ERR_VALUE(irq_base)) { -@@ -983,10 +1019,6 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, - - gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base, - hwirq_base, &gic_irq_domain_ops, gic); -- } else { -- gic->domain = irq_domain_add_linear(node, nr_routable_irqs, -- &gic_irq_domain_ops, -- gic); - } - - if (WARN_ON(!gic->domain)) -@@ -1037,6 +1069,10 @@ gic_of_init(struct device_node *node, struct device_node *parent) - irq = irq_of_parse_and_map(node, 0); - gic_cascade_irq(gic_cnt, irq); - } -+ -+ if (IS_ENABLED(CONFIG_ARM_GIC_V2M)) -+ gicv2m_of_init(node, gic_data[gic_cnt].domain); -+ - gic_cnt++; - return 0; - } -diff --git a/drivers/irqchip/irq-hip04.c b/drivers/irqchip/irq-hip04.c -index 9c8f833..5507a0c 100644 ---- a/drivers/irqchip/irq-hip04.c -+++ b/drivers/irqchip/irq-hip04.c -@@ -120,21 +120,24 @@ static int hip04_irq_set_type(struct irq_data *d, unsigned int type) - { - void __iomem *base = hip04_dist_base(d); - unsigned int irq = hip04_irq(d); -+ int ret; - - /* Interrupt configuration for SGIs can't be changed */ - if (irq < 16) - return -EINVAL; - -- if (type != IRQ_TYPE_LEVEL_HIGH && type != IRQ_TYPE_EDGE_RISING) -+ /* SPIs have restrictions on the supported types */ -+ if (irq >= 32 && type != IRQ_TYPE_LEVEL_HIGH && -+ type != IRQ_TYPE_EDGE_RISING) - return -EINVAL; - - raw_spin_lock(&irq_controller_lock); - -- gic_configure_irq(irq, type, base, NULL); -+ ret = gic_configure_irq(irq, type, base, NULL); - - raw_spin_unlock(&irq_controller_lock); - -- return 0; -+ return ret; - } - - #ifdef CONFIG_SMP -diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig -index 6d91c27..d6af99f 100644 ---- a/drivers/memory/Kconfig -+++ b/drivers/memory/Kconfig -@@ -83,6 +83,6 @@ config FSL_CORENET_CF - - config FSL_IFC - bool -- depends on FSL_SOC -+ depends on FSL_SOC || ARCH_LAYERSCAPE - - endif -diff --git a/drivers/memory/fsl_ifc.c b/drivers/memory/fsl_ifc.c -index 3d5d792..1b182b1 100644 ---- a/drivers/memory/fsl_ifc.c -+++ b/drivers/memory/fsl_ifc.c -@@ -22,6 +22,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -30,7 +31,9 @@ - #include - #include - #include --#include -+#include -+#include -+#include - - struct fsl_ifc_ctrl *fsl_ifc_ctrl_dev; - EXPORT_SYMBOL(fsl_ifc_ctrl_dev); -@@ -58,11 +61,11 @@ int fsl_ifc_find(phys_addr_t addr_base) - { - int i = 0; - -- if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->regs) -+ if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->gregs) - return -ENODEV; - -- for (i = 0; i < ARRAY_SIZE(fsl_ifc_ctrl_dev->regs->cspr_cs); i++) { -- u32 cspr = in_be32(&fsl_ifc_ctrl_dev->regs->cspr_cs[i].cspr); -+ for (i = 0; i < fsl_ifc_ctrl_dev->banks; i++) { -+ u32 cspr = ifc_in32(&fsl_ifc_ctrl_dev->gregs->cspr_cs[i].cspr); - if (cspr & CSPR_V && (cspr & CSPR_BA) == - convert_ifc_address(addr_base)) - return i; -@@ -74,21 +77,21 @@ EXPORT_SYMBOL(fsl_ifc_find); - - static int fsl_ifc_ctrl_init(struct fsl_ifc_ctrl *ctrl) - { -- struct fsl_ifc_regs __iomem *ifc = ctrl->regs; -+ struct fsl_ifc_global __iomem *ifc = ctrl->gregs; - - /* - * Clear all the common status and event registers - */ -- if (in_be32(&ifc->cm_evter_stat) & IFC_CM_EVTER_STAT_CSER) -- out_be32(&ifc->cm_evter_stat, IFC_CM_EVTER_STAT_CSER); -+ if (ifc_in32(&ifc->cm_evter_stat) & IFC_CM_EVTER_STAT_CSER) -+ ifc_out32(IFC_CM_EVTER_STAT_CSER, &ifc->cm_evter_stat); - - /* enable all error and events */ -- out_be32(&ifc->cm_evter_en, IFC_CM_EVTER_EN_CSEREN); -+ ifc_out32(IFC_CM_EVTER_EN_CSEREN, &ifc->cm_evter_en); - - /* enable all error and event interrupts */ -- out_be32(&ifc->cm_evter_intr_en, IFC_CM_EVTER_INTR_EN_CSERIREN); -- out_be32(&ifc->cm_erattr0, 0x0); -- out_be32(&ifc->cm_erattr1, 0x0); -+ ifc_out32(IFC_CM_EVTER_INTR_EN_CSERIREN, &ifc->cm_evter_intr_en); -+ ifc_out32(0x0, &ifc->cm_erattr0); -+ ifc_out32(0x0, &ifc->cm_erattr1); - - return 0; - } -@@ -103,7 +106,7 @@ static int fsl_ifc_ctrl_remove(struct platform_device *dev) - irq_dispose_mapping(ctrl->nand_irq); - irq_dispose_mapping(ctrl->irq); - -- iounmap(ctrl->regs); -+ iounmap(ctrl->gregs); - - dev_set_drvdata(&dev->dev, NULL); - kfree(ctrl); -@@ -121,15 +124,15 @@ static DEFINE_SPINLOCK(nand_irq_lock); - - static u32 check_nand_stat(struct fsl_ifc_ctrl *ctrl) - { -- struct fsl_ifc_regs __iomem *ifc = ctrl->regs; -+ struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; - unsigned long flags; - u32 stat; - - spin_lock_irqsave(&nand_irq_lock, flags); - -- stat = in_be32(&ifc->ifc_nand.nand_evter_stat); -+ stat = ifc_in32(&ifc->ifc_nand.nand_evter_stat); - if (stat) { -- out_be32(&ifc->ifc_nand.nand_evter_stat, stat); -+ ifc_out32(stat, &ifc->ifc_nand.nand_evter_stat); - ctrl->nand_stat = stat; - wake_up(&ctrl->nand_wait); - } -@@ -156,21 +159,21 @@ static irqreturn_t fsl_ifc_nand_irq(int irqno, void *data) - static irqreturn_t fsl_ifc_ctrl_irq(int irqno, void *data) - { - struct fsl_ifc_ctrl *ctrl = data; -- struct fsl_ifc_regs __iomem *ifc = ctrl->regs; -+ struct fsl_ifc_global __iomem *ifc = ctrl->gregs; - u32 err_axiid, err_srcid, status, cs_err, err_addr; - irqreturn_t ret = IRQ_NONE; - - /* read for chip select error */ -- cs_err = in_be32(&ifc->cm_evter_stat); -+ cs_err = ifc_in32(&ifc->cm_evter_stat); - if (cs_err) { - dev_err(ctrl->dev, "transaction sent to IFC is not mapped to" - "any memory bank 0x%08X\n", cs_err); - /* clear the chip select error */ -- out_be32(&ifc->cm_evter_stat, IFC_CM_EVTER_STAT_CSER); -+ ifc_out32(IFC_CM_EVTER_STAT_CSER, &ifc->cm_evter_stat); - - /* read error attribute registers print the error information */ -- status = in_be32(&ifc->cm_erattr0); -- err_addr = in_be32(&ifc->cm_erattr1); -+ status = ifc_in32(&ifc->cm_erattr0); -+ err_addr = ifc_in32(&ifc->cm_erattr1); - - if (status & IFC_CM_ERATTR0_ERTYP_READ) - dev_err(ctrl->dev, "Read transaction error" -@@ -213,7 +216,8 @@ static irqreturn_t fsl_ifc_ctrl_irq(int irqno, void *data) - static int fsl_ifc_ctrl_probe(struct platform_device *dev) - { - int ret = 0; -- -+ int version, banks; -+ void __iomem *addr; - - dev_info(&dev->dev, "Freescale Integrated Flash Controller\n"); - -@@ -224,16 +228,41 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev) - dev_set_drvdata(&dev->dev, fsl_ifc_ctrl_dev); - - /* IOMAP the entire IFC region */ -- fsl_ifc_ctrl_dev->regs = of_iomap(dev->dev.of_node, 0); -- if (!fsl_ifc_ctrl_dev->regs) { -+ fsl_ifc_ctrl_dev->gregs = of_iomap(dev->dev.of_node, 0); -+ if (!fsl_ifc_ctrl_dev->gregs) { - dev_err(&dev->dev, "failed to get memory region\n"); - ret = -ENODEV; - goto err; - } - -+ if (of_property_read_bool(dev->dev.of_node, "little-endian")) { -+ fsl_ifc_ctrl_dev->little_endian = true; -+ dev_dbg(&dev->dev, "IFC REGISTERS are LITTLE endian\n"); -+ } else { -+ fsl_ifc_ctrl_dev->little_endian = false; -+ dev_dbg(&dev->dev, "IFC REGISTERS are BIG endian\n"); -+ } -+ -+ version = ifc_in32(&fsl_ifc_ctrl_dev->gregs->ifc_rev) & -+ FSL_IFC_VERSION_MASK; -+ -+ banks = (version == FSL_IFC_VERSION_1_0_0) ? 4 : 8; -+ dev_info(&dev->dev, "IFC version %d.%d, %d banks\n", -+ version >> 24, (version >> 16) & 0xf, banks); -+ -+ fsl_ifc_ctrl_dev->version = version; -+ fsl_ifc_ctrl_dev->banks = banks; -+ -+ addr = fsl_ifc_ctrl_dev->gregs; -+ if (version >= FSL_IFC_VERSION_2_0_0) -+ addr += PGOFFSET_64K; -+ else -+ addr += PGOFFSET_4K; -+ fsl_ifc_ctrl_dev->rregs = addr; -+ - /* get the Controller level irq */ - fsl_ifc_ctrl_dev->irq = irq_of_parse_and_map(dev->dev.of_node, 0); -- if (fsl_ifc_ctrl_dev->irq == NO_IRQ) { -+ if (fsl_ifc_ctrl_dev->irq == 0) { - dev_err(&dev->dev, "failed to get irq resource " - "for IFC\n"); - ret = -ENODEV; -diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c -index 9e21e4f..8f43ab8 100644 ---- a/drivers/mfd/vexpress-sysreg.c -+++ b/drivers/mfd/vexpress-sysreg.c -@@ -223,7 +223,7 @@ static int vexpress_sysreg_probe(struct platform_device *pdev) - vexpress_config_set_master(vexpress_sysreg_get_master()); - - /* Confirm board type against DT property, if available */ -- if (of_property_read_u32(of_allnodes, "arm,hbi", &dt_hbi) == 0) { -+ if (of_property_read_u32(of_root, "arm,hbi", &dt_hbi) == 0) { - u32 id = vexpress_get_procid(VEXPRESS_SITE_MASTER); - u32 hbi = (id >> SYS_PROCIDx_HBI_SHIFT) & SYS_HBI_MASK; - -diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c -index 10ecc0a..d356dbc 100644 ---- a/drivers/mmc/card/block.c -+++ b/drivers/mmc/card/block.c -@@ -2402,6 +2402,10 @@ static const struct mmc_fixup blk_fixups[] = - * - * N.B. This doesn't affect SD cards. - */ -+ MMC_FIXUP("SDMB-32", CID_MANFID_SANDISK, CID_OEMID_ANY, add_quirk_mmc, -+ MMC_QUIRK_BLK_NO_CMD23), -+ MMC_FIXUP("SDM032", CID_MANFID_SANDISK, CID_OEMID_ANY, add_quirk_mmc, -+ MMC_QUIRK_BLK_NO_CMD23), - MMC_FIXUP("MMC08G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc, - MMC_QUIRK_BLK_NO_CMD23), - MMC_FIXUP("MMC16G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc, -diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig -index 1386065..b8c9b73 100644 ---- a/drivers/mmc/host/Kconfig -+++ b/drivers/mmc/host/Kconfig -@@ -66,7 +66,7 @@ config MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER - has the effect of scrambling the addresses and formats of data - accessed in sizes other than the datum size. - -- This is the case for the Freescale eSDHC and Nintendo Wii SDHCI. -+ This is the case for the Nintendo Wii SDHCI. - - config MMC_SDHCI_PCI - tristate "SDHCI support on PCI bus" -@@ -130,8 +130,10 @@ config MMC_SDHCI_OF_ARASAN - config MMC_SDHCI_OF_ESDHC - tristate "SDHCI OF support for the Freescale eSDHC controller" - depends on MMC_SDHCI_PLTFM -- depends on PPC_OF -- select MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER -+ depends on PPC || ARCH_MXC || ARCH_LAYERSCAPE -+ select MMC_SDHCI_IO_ACCESSORS -+ select FSL_SOC_DRIVERS -+ select FSL_GUTS - help - This selects the Freescale eSDHC controller support. - -@@ -142,7 +144,7 @@ config MMC_SDHCI_OF_ESDHC - config MMC_SDHCI_OF_HLWD - tristate "SDHCI OF support for the Nintendo Wii SDHCI controllers" - depends on MMC_SDHCI_PLTFM -- depends on PPC_OF -+ depends on PPC - select MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER - help - This selects the Secure Digital Host Controller Interface (SDHCI) -diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h -index a870c42..f2baede 100644 ---- a/drivers/mmc/host/sdhci-esdhc.h -+++ b/drivers/mmc/host/sdhci-esdhc.h -@@ -21,16 +21,23 @@ - #define ESDHC_DEFAULT_QUIRKS (SDHCI_QUIRK_FORCE_BLK_SZ_2048 | \ - SDHCI_QUIRK_NO_BUSY_IRQ | \ - SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | \ -- SDHCI_QUIRK_PIO_NEEDS_DELAY) -+ SDHCI_QUIRK_PIO_NEEDS_DELAY | \ -+ SDHCI_QUIRK_NO_HISPD_BIT) -+ -+#define ESDHC_PROCTL 0x28 - - #define ESDHC_SYSTEM_CONTROL 0x2c - #define ESDHC_CLOCK_MASK 0x0000fff0 - #define ESDHC_PREDIV_SHIFT 8 - #define ESDHC_DIVIDER_SHIFT 4 -+#define ESDHC_CLOCK_CRDEN 0x00000008 - #define ESDHC_CLOCK_PEREN 0x00000004 - #define ESDHC_CLOCK_HCKEN 0x00000002 - #define ESDHC_CLOCK_IPGEN 0x00000001 - -+#define ESDHC_PRESENT_STATE 0x24 -+#define ESDHC_CLOCK_STABLE 0x00000008 -+ - /* pltfm-specific */ - #define ESDHC_HOST_CONTROL_LE 0x20 - -diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c -index 8872c85..4a4a693 100644 ---- a/drivers/mmc/host/sdhci-of-esdhc.c -+++ b/drivers/mmc/host/sdhci-of-esdhc.c -@@ -18,128 +18,334 @@ - #include - #include - #include -+#include -+#include - #include - #include "sdhci-pltfm.h" - #include "sdhci-esdhc.h" - - #define VENDOR_V_22 0x12 - #define VENDOR_V_23 0x13 --static u32 esdhc_readl(struct sdhci_host *host, int reg) -+ -+struct sdhci_esdhc { -+ u8 vendor_ver; -+ u8 spec_ver; -+ u32 soc_ver; -+ u8 soc_rev; -+}; -+ -+/** -+ * esdhc_read*_fixup - Fixup the value read from incompatible eSDHC register -+ * to make it compatible with SD spec. -+ * -+ * @host: pointer to sdhci_host -+ * @spec_reg: SD spec register address -+ * @value: 32bit eSDHC register value on spec_reg address -+ * -+ * In SD spec, there are 8/16/32/64 bits registers, while all of eSDHC -+ * registers are 32 bits. There are differences in register size, register -+ * address, register function, bit position and function between eSDHC spec -+ * and SD spec. -+ * -+ * Return a fixed up register value -+ */ -+static u32 esdhc_readl_fixup(struct sdhci_host *host, -+ int spec_reg, u32 value) - { -+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); -+ struct sdhci_esdhc *esdhc = pltfm_host->priv; - u32 ret; - -- ret = in_be32(host->ioaddr + reg); - /* - * The bit of ADMA flag in eSDHC is not compatible with standard - * SDHC register, so set fake flag SDHCI_CAN_DO_ADMA2 when ADMA is - * supported by eSDHC. - * And for many FSL eSDHC controller, the reset value of field -- * SDHCI_CAN_DO_ADMA1 is one, but some of them can't support ADMA, -+ * SDHCI_CAN_DO_ADMA1 is 1, but some of them can't support ADMA, - * only these vendor version is greater than 2.2/0x12 support ADMA. -- * For FSL eSDHC, must aligned 4-byte, so use 0xFC to read the -- * the verdor version number, oxFE is SDHCI_HOST_VERSION. - */ -- if ((reg == SDHCI_CAPABILITIES) && (ret & SDHCI_CAN_DO_ADMA1)) { -- u32 tmp = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS); -- tmp = (tmp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; -- if (tmp > VENDOR_V_22) -- ret |= SDHCI_CAN_DO_ADMA2; -+ if ((spec_reg == SDHCI_CAPABILITIES) && (value & SDHCI_CAN_DO_ADMA1)) { -+ if (esdhc->vendor_ver > VENDOR_V_22) { -+ ret = value | SDHCI_CAN_DO_ADMA2; -+ return ret; -+ } - } -- -+ ret = value; - return ret; - } - --static u16 esdhc_readw(struct sdhci_host *host, int reg) -+static u16 esdhc_readw_fixup(struct sdhci_host *host, -+ int spec_reg, u32 value) - { - u16 ret; -- int base = reg & ~0x3; -- int shift = (reg & 0x2) * 8; -+ int shift = (spec_reg & 0x2) * 8; - -- if (unlikely(reg == SDHCI_HOST_VERSION)) -- ret = in_be32(host->ioaddr + base) & 0xffff; -+ if (spec_reg == SDHCI_HOST_VERSION) -+ ret = value & 0xffff; - else -- ret = (in_be32(host->ioaddr + base) >> shift) & 0xffff; -+ ret = (value >> shift) & 0xffff; - return ret; - } - --static u8 esdhc_readb(struct sdhci_host *host, int reg) -+static u8 esdhc_readb_fixup(struct sdhci_host *host, -+ int spec_reg, u32 value) - { -- int base = reg & ~0x3; -- int shift = (reg & 0x3) * 8; -- u8 ret = (in_be32(host->ioaddr + base) >> shift) & 0xff; -+ u8 ret; -+ u8 dma_bits; -+ int shift = (spec_reg & 0x3) * 8; -+ -+ ret = (value >> shift) & 0xff; - - /* - * "DMA select" locates at offset 0x28 in SD specification, but on - * P5020 or P3041, it locates at 0x29. - */ -- if (reg == SDHCI_HOST_CONTROL) { -- u32 dma_bits; -- -- dma_bits = in_be32(host->ioaddr + reg); -+ if (spec_reg == SDHCI_HOST_CONTROL) { - /* DMA select is 22,23 bits in Protocol Control Register */ -- dma_bits = (dma_bits >> 5) & SDHCI_CTRL_DMA_MASK; -- -+ dma_bits = (value >> 5) & SDHCI_CTRL_DMA_MASK; - /* fixup the result */ - ret &= ~SDHCI_CTRL_DMA_MASK; - ret |= dma_bits; - } -- - return ret; - } - --static void esdhc_writel(struct sdhci_host *host, u32 val, int reg) -+/** -+ * esdhc_write*_fixup - Fixup the SD spec register value so that it could be -+ * written into eSDHC register. -+ * -+ * @host: pointer to sdhci_host -+ * @spec_reg: SD spec register address -+ * @value: 8/16/32bit SD spec register value that would be written -+ * @old_value: 32bit eSDHC register value on spec_reg address -+ * -+ * In SD spec, there are 8/16/32/64 bits registers, while all of eSDHC -+ * registers are 32 bits. There are differences in register size, register -+ * address, register function, bit position and function between eSDHC spec -+ * and SD spec. -+ * -+ * Return a fixed up register value -+ */ -+static u32 esdhc_writel_fixup(struct sdhci_host *host, -+ int spec_reg, u32 value, u32 old_value) - { -+ u32 ret; -+ - /* -- * Enable IRQSTATEN[BGESEN] is just to set IRQSTAT[BGE] -- * when SYSCTL[RSTD]) is set for some special operations. -- * No any impact other operation. -+ * Enabling IRQSTATEN[BGESEN] is just to set IRQSTAT[BGE] -+ * when SYSCTL[RSTD] is set for some special operations. -+ * No any impact on other operation. - */ -- if (reg == SDHCI_INT_ENABLE) -- val |= SDHCI_INT_BLK_GAP; -- sdhci_be32bs_writel(host, val, reg); -+ if (spec_reg == SDHCI_INT_ENABLE) -+ ret = value | SDHCI_INT_BLK_GAP; -+ else -+ ret = value; -+ -+ return ret; - } - --static void esdhc_writew(struct sdhci_host *host, u16 val, int reg) -+static u32 esdhc_writew_fixup(struct sdhci_host *host, -+ int spec_reg, u16 value, u32 old_value) - { -- if (reg == SDHCI_BLOCK_SIZE) { -+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); -+ int shift = (spec_reg & 0x2) * 8; -+ u32 ret; -+ -+ switch (spec_reg) { -+ case SDHCI_TRANSFER_MODE: -+ /* -+ * Postpone this write, we must do it together with a -+ * command write that is down below. Return old value. -+ */ -+ pltfm_host->xfer_mode_shadow = value; -+ return old_value; -+ case SDHCI_COMMAND: -+ ret = (value << 16) | pltfm_host->xfer_mode_shadow; -+ return ret; -+ } -+ -+ ret = old_value & (~(0xffff << shift)); -+ ret |= (value << shift); -+ -+ if (spec_reg == SDHCI_BLOCK_SIZE) { - /* - * Two last DMA bits are reserved, and first one is used for - * non-standard blksz of 4096 bytes that we don't support - * yet. So clear the DMA boundary bits. - */ -- val &= ~SDHCI_MAKE_BLKSZ(0x7, 0); -+ ret &= (~SDHCI_MAKE_BLKSZ(0x7, 0)); - } -- sdhci_be32bs_writew(host, val, reg); -+ return ret; - } - --static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg) -+static u32 esdhc_writeb_fixup(struct sdhci_host *host, -+ int spec_reg, u8 value, u32 old_value) - { -+ u32 ret; -+ u32 dma_bits; -+ u8 tmp; -+ int shift = (spec_reg & 0x3) * 8; -+ -+ /* -+ * eSDHC doesn't have a standard power control register, so we do -+ * nothing here to avoid incorrect operation. -+ */ -+ if (spec_reg == SDHCI_POWER_CONTROL) -+ return old_value; - /* - * "DMA select" location is offset 0x28 in SD specification, but on - * P5020 or P3041, it's located at 0x29. - */ -- if (reg == SDHCI_HOST_CONTROL) { -- u32 dma_bits; -- -+ if (spec_reg == SDHCI_HOST_CONTROL) { - /* - * If host control register is not standard, exit - * this function - */ - if (host->quirks2 & SDHCI_QUIRK2_BROKEN_HOST_CONTROL) -- return; -+ return old_value; - - /* DMA select is 22,23 bits in Protocol Control Register */ -- dma_bits = (val & SDHCI_CTRL_DMA_MASK) << 5; -- clrsetbits_be32(host->ioaddr + reg , SDHCI_CTRL_DMA_MASK << 5, -- dma_bits); -- val &= ~SDHCI_CTRL_DMA_MASK; -- val |= in_be32(host->ioaddr + reg) & SDHCI_CTRL_DMA_MASK; -+ dma_bits = (value & SDHCI_CTRL_DMA_MASK) << 5; -+ ret = (old_value & (~(SDHCI_CTRL_DMA_MASK << 5))) | dma_bits; -+ tmp = (value & (~SDHCI_CTRL_DMA_MASK)) | -+ (old_value & SDHCI_CTRL_DMA_MASK); -+ ret = (ret & (~0xff)) | tmp; -+ -+ /* Prevent SDHCI core from writing reserved bits (e.g. HISPD) */ -+ ret &= ~ESDHC_HOST_CONTROL_RES; -+ return ret; - } - -- /* Prevent SDHCI core from writing reserved bits (e.g. HISPD). */ -- if (reg == SDHCI_HOST_CONTROL) -- val &= ~ESDHC_HOST_CONTROL_RES; -- sdhci_be32bs_writeb(host, val, reg); -+ ret = (old_value & (~(0xff << shift))) | (value << shift); -+ return ret; -+} -+ -+static u32 esdhc_be_readl(struct sdhci_host *host, int reg) -+{ -+ u32 ret; -+ u32 value; -+ -+ value = ioread32be(host->ioaddr + reg); -+ ret = esdhc_readl_fixup(host, reg, value); -+ -+ return ret; -+} -+ -+static u32 esdhc_le_readl(struct sdhci_host *host, int reg) -+{ -+ u32 ret; -+ u32 value; -+ -+ value = ioread32(host->ioaddr + reg); -+ ret = esdhc_readl_fixup(host, reg, value); -+ -+ return ret; -+} -+ -+static u16 esdhc_be_readw(struct sdhci_host *host, int reg) -+{ -+ u16 ret; -+ u32 value; -+ int base = reg & ~0x3; -+ -+ value = ioread32be(host->ioaddr + base); -+ ret = esdhc_readw_fixup(host, reg, value); -+ return ret; -+} -+ -+static u16 esdhc_le_readw(struct sdhci_host *host, int reg) -+{ -+ u16 ret; -+ u32 value; -+ int base = reg & ~0x3; -+ -+ value = ioread32(host->ioaddr + base); -+ ret = esdhc_readw_fixup(host, reg, value); -+ return ret; -+} -+ -+static u8 esdhc_be_readb(struct sdhci_host *host, int reg) -+{ -+ u8 ret; -+ u32 value; -+ int base = reg & ~0x3; -+ -+ value = ioread32be(host->ioaddr + base); -+ ret = esdhc_readb_fixup(host, reg, value); -+ return ret; -+} -+ -+static u8 esdhc_le_readb(struct sdhci_host *host, int reg) -+{ -+ u8 ret; -+ u32 value; -+ int base = reg & ~0x3; -+ -+ value = ioread32(host->ioaddr + base); -+ ret = esdhc_readb_fixup(host, reg, value); -+ return ret; -+} -+ -+static void esdhc_be_writel(struct sdhci_host *host, u32 val, int reg) -+{ -+ u32 value; -+ -+ value = esdhc_writel_fixup(host, reg, val, 0); -+ iowrite32be(value, host->ioaddr + reg); -+} -+ -+static void esdhc_le_writel(struct sdhci_host *host, u32 val, int reg) -+{ -+ u32 value; -+ -+ value = esdhc_writel_fixup(host, reg, val, 0); -+ iowrite32(value, host->ioaddr + reg); -+} -+ -+static void esdhc_be_writew(struct sdhci_host *host, u16 val, int reg) -+{ -+ int base = reg & ~0x3; -+ u32 value; -+ u32 ret; -+ -+ value = ioread32be(host->ioaddr + base); -+ ret = esdhc_writew_fixup(host, reg, val, value); -+ if (reg != SDHCI_TRANSFER_MODE) -+ iowrite32be(ret, host->ioaddr + base); -+} -+ -+static void esdhc_le_writew(struct sdhci_host *host, u16 val, int reg) -+{ -+ int base = reg & ~0x3; -+ u32 value; -+ u32 ret; -+ -+ value = ioread32(host->ioaddr + base); -+ ret = esdhc_writew_fixup(host, reg, val, value); -+ if (reg != SDHCI_TRANSFER_MODE) -+ iowrite32(ret, host->ioaddr + base); -+} -+ -+static void esdhc_be_writeb(struct sdhci_host *host, u8 val, int reg) -+{ -+ int base = reg & ~0x3; -+ u32 value; -+ u32 ret; -+ -+ value = ioread32be(host->ioaddr + base); -+ ret = esdhc_writeb_fixup(host, reg, val, value); -+ iowrite32be(ret, host->ioaddr + base); -+} -+ -+static void esdhc_le_writeb(struct sdhci_host *host, u8 val, int reg) -+{ -+ int base = reg & ~0x3; -+ u32 value; -+ u32 ret; -+ -+ value = ioread32(host->ioaddr + base); -+ ret = esdhc_writeb_fixup(host, reg, val, value); -+ iowrite32(ret, host->ioaddr + base); - } - - /* -@@ -149,37 +355,116 @@ static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg) - * For Continue, apply soft reset for data(SYSCTL[RSTD]); - * and re-issue the entire read transaction from beginning. - */ --static void esdhci_of_adma_workaround(struct sdhci_host *host, u32 intmask) -+static void esdhc_of_adma_workaround(struct sdhci_host *host, u32 intmask) - { -- u32 tmp; -+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); -+ struct sdhci_esdhc *esdhc = pltfm_host->priv; - bool applicable; - dma_addr_t dmastart; - dma_addr_t dmanow; - -- tmp = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS); -- tmp = (tmp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; -- - applicable = (intmask & SDHCI_INT_DATA_END) && -- (intmask & SDHCI_INT_BLK_GAP) && -- (tmp == VENDOR_V_23); -- if (!applicable) -+ (intmask & SDHCI_INT_BLK_GAP) && -+ (esdhc->vendor_ver == VENDOR_V_23); -+ if (applicable) { -+ -+ sdhci_reset(host, SDHCI_RESET_DATA); -+ host->data->error = 0; -+ dmastart = sg_dma_address(host->data->sg); -+ dmanow = dmastart + host->data->bytes_xfered; -+ /* -+ * Force update to the next DMA block boundary. -+ */ -+ dmanow = (dmanow & ~(SDHCI_DEFAULT_BOUNDARY_SIZE - 1)) + -+ SDHCI_DEFAULT_BOUNDARY_SIZE; -+ host->data->bytes_xfered = dmanow - dmastart; -+ sdhci_writel(host, dmanow, SDHCI_DMA_ADDRESS); -+ - return; -+ } - -- host->data->error = 0; -- dmastart = sg_dma_address(host->data->sg); -- dmanow = dmastart + host->data->bytes_xfered; - /* -- * Force update to the next DMA block boundary. -+ * Check for A-004388: eSDHC DMA might not stop if error -+ * occurs on system transaction -+ * Impact list: -+ * T4240-4160-R1.0 B4860-4420-R1.0-R2.0 P1010-1014-R1.0 -+ * P3041-R1.0-R2.0-R1.1 P2041-2040-R1.0-R1.1-R2.0 -+ * P5020-5010-R2.0-R1.0 P5040-5021-R2.0-R2.1 - */ -- dmanow = (dmanow & ~(SDHCI_DEFAULT_BOUNDARY_SIZE - 1)) + -- SDHCI_DEFAULT_BOUNDARY_SIZE; -- host->data->bytes_xfered = dmanow - dmastart; -- sdhci_writel(host, dmanow, SDHCI_DMA_ADDRESS); -+ if (!(((esdhc->soc_ver == SVR_T4240) && (esdhc->soc_rev == 0x10)) || -+ ((esdhc->soc_ver == SVR_T4160) && (esdhc->soc_rev == 0x10)) || -+ ((esdhc->soc_ver == SVR_B4860) && (esdhc->soc_rev == 0x10)) || -+ ((esdhc->soc_ver == SVR_B4860) && (esdhc->soc_rev == 0x20)) || -+ ((esdhc->soc_ver == SVR_B4420) && (esdhc->soc_rev == 0x10)) || -+ ((esdhc->soc_ver == SVR_B4420) && (esdhc->soc_rev == 0x20)) || -+ ((esdhc->soc_ver == SVR_P1010) && (esdhc->soc_rev == 0x10)) || -+ ((esdhc->soc_ver == SVR_P1014) && (esdhc->soc_rev == 0x10)) || -+ ((esdhc->soc_ver == SVR_P3041) && (esdhc->soc_rev <= 0x20)) || -+ ((esdhc->soc_ver == SVR_P2041) && (esdhc->soc_rev <= 0x20)) || -+ ((esdhc->soc_ver == SVR_P2040) && (esdhc->soc_rev <= 0x20)) || -+ ((esdhc->soc_ver == SVR_P5020) && (esdhc->soc_rev <= 0x20)) || -+ ((esdhc->soc_ver == SVR_P5010) && (esdhc->soc_rev <= 0x20)) || -+ ((esdhc->soc_ver == SVR_P5040) && (esdhc->soc_rev <= 0x21)) || -+ ((esdhc->soc_ver == SVR_P5021) && (esdhc->soc_rev <= 0x21)))) -+ return; -+ -+ sdhci_reset(host, SDHCI_RESET_DATA); -+ -+ if (host->flags & SDHCI_USE_ADMA) { -+ u32 mod, i, offset; -+ u8 *desc; -+ dma_addr_t addr; -+ struct scatterlist *sg; -+ __le32 *dataddr; -+ __le32 *cmdlen; -+ -+ /* -+ * If block count was enabled, in case read transfer there -+ * is no data was corrupted -+ */ -+ mod = sdhci_readl(host, SDHCI_TRANSFER_MODE); -+ if ((mod & SDHCI_TRNS_BLK_CNT_EN) && -+ (host->data->flags & MMC_DATA_READ)) -+ host->data->error = 0; -+ -+ BUG_ON(!host->data); -+ desc = host->adma_table; -+ for_each_sg(host->data->sg, sg, host->sg_count, i) { -+ addr = sg_dma_address(sg); -+ offset = (4 - (addr & 0x3)) & 0x3; -+ if (offset) -+ desc += 8; -+ desc += 8; -+ } -+ -+ /* -+ * Add an extra zero descriptor next to the -+ * terminating descriptor. -+ */ -+ desc += 8; -+ WARN_ON((desc - (u8 *)(host->adma_table)) > (128 * 2 + 1) * 4); -+ -+ dataddr = (__le32 __force *)(desc + 4); -+ cmdlen = (__le32 __force *)desc; -+ -+ cmdlen[0] = cpu_to_le32(0); -+ dataddr[0] = cpu_to_le32(0); -+ } -+ -+ if ((host->flags & SDHCI_USE_SDMA) && -+ (host->data->flags & MMC_DATA_READ)) -+ host->data->error = 0; -+ -+ return; - } - - static int esdhc_of_enable_dma(struct sdhci_host *host) - { -- setbits32(host->ioaddr + ESDHC_DMA_SYSCTL, ESDHC_DMA_SNOOP); -+ u32 value; -+ -+ value = sdhci_readl(host, ESDHC_DMA_SYSCTL); -+ value |= ESDHC_DMA_SNOOP; -+ sdhci_writel(host, value, ESDHC_DMA_SYSCTL); - return 0; - } - -@@ -199,15 +484,22 @@ static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host) - - static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) - { -- int pre_div = 2; -+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); -+ struct sdhci_esdhc *esdhc = pltfm_host->priv; -+ int pre_div = 1; - int div = 1; - u32 temp; -+ u32 timeout; - - host->mmc->actual_clock = 0; - - if (clock == 0) - return; - -+ /* Workaround to start pre_div at 2 for VNN < VENDOR_V_23 */ -+ if (esdhc->vendor_ver < VENDOR_V_23) -+ pre_div = 2; -+ - /* Workaround to reduce the clock frequency for p1010 esdhc */ - if (of_find_compatible_node(NULL, NULL, "fsl,p1010-esdhc")) { - if (clock > 20000000) -@@ -218,7 +510,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) - - temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); - temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN -- | ESDHC_CLOCK_MASK); -+ | ESDHC_CLOCK_CRDEN | ESDHC_CLOCK_MASK); - sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); - - while (host->max_clk / pre_div / 16 > clock && pre_div < 256) -@@ -229,7 +521,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) - - dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n", - clock, host->max_clk / pre_div / div); -- -+ host->mmc->actual_clock = host->max_clk / pre_div / div; - pre_div >>= 1; - div--; - -@@ -238,70 +530,117 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) - | (div << ESDHC_DIVIDER_SHIFT) - | (pre_div << ESDHC_PREDIV_SHIFT)); - sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); -- mdelay(1); --} - --static void esdhc_of_platform_init(struct sdhci_host *host) --{ -- u32 vvn; -- -- vvn = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS); -- vvn = (vvn & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; -- if (vvn == VENDOR_V_22) -- host->quirks2 |= SDHCI_QUIRK2_HOST_NO_CMD23; -+ /* Wait max 20 ms */ -+ timeout = 20; -+ while (!(sdhci_readl(host, ESDHC_PRESENT_STATE) & ESDHC_CLOCK_STABLE)) { -+ if (timeout == 0) { -+ pr_err("%s: Internal clock never stabilised.\n", -+ mmc_hostname(host->mmc)); -+ return; -+ } -+ timeout--; -+ mdelay(1); -+ } - -- if (vvn > VENDOR_V_22) -- host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ; -+ temp |= ESDHC_CLOCK_CRDEN; -+ sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); - } - - static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width) - { - u32 ctrl; - -+ ctrl = sdhci_readl(host, ESDHC_PROCTL); -+ ctrl &= (~ESDHC_CTRL_BUSWIDTH_MASK); - switch (width) { - case MMC_BUS_WIDTH_8: -- ctrl = ESDHC_CTRL_8BITBUS; -+ ctrl |= ESDHC_CTRL_8BITBUS; - break; - - case MMC_BUS_WIDTH_4: -- ctrl = ESDHC_CTRL_4BITBUS; -+ ctrl |= ESDHC_CTRL_4BITBUS; - break; - - default: -- ctrl = 0; - break; - } - -- clrsetbits_be32(host->ioaddr + SDHCI_HOST_CONTROL, -- ESDHC_CTRL_BUSWIDTH_MASK, ctrl); -+ sdhci_writel(host, ctrl, ESDHC_PROCTL); - } - --static const struct sdhci_ops sdhci_esdhc_ops = { -- .read_l = esdhc_readl, -- .read_w = esdhc_readw, -- .read_b = esdhc_readb, -- .write_l = esdhc_writel, -- .write_w = esdhc_writew, -- .write_b = esdhc_writeb, -- .set_clock = esdhc_of_set_clock, -- .enable_dma = esdhc_of_enable_dma, -- .get_max_clock = esdhc_of_get_max_clock, -- .get_min_clock = esdhc_of_get_min_clock, -- .platform_init = esdhc_of_platform_init, -- .adma_workaround = esdhci_of_adma_workaround, -- .set_bus_width = esdhc_pltfm_set_bus_width, -- .reset = sdhci_reset, -- .set_uhs_signaling = sdhci_set_uhs_signaling, --}; -+/* -+ * A-003980: SDHC: Glitch is generated on the card clock with software reset -+ * or clock divider change -+ * Workaround: -+ * A simple workaround is to disable the SD card clock before the software -+ * reset, and enable it when the module resumes normal operation. The Host -+ * and the SD card are in a master-slave relationship. The Host provides -+ * clock and control transfer across the interface. Therefore, any existing -+ * operation is discarded when the Host controller is reset. -+ */ -+static int esdhc_of_reset_workaround(struct sdhci_host *host, u8 mask) -+{ -+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); -+ struct sdhci_esdhc *esdhc = pltfm_host->priv; -+ bool disable_clk_before_reset = false; -+ u32 temp; - --#ifdef CONFIG_PM -+ /* -+ * Check for A-003980 -+ * Impact list: -+ * T4240-4160-R1.0-R2.0 B4860-4420-R1.0-R2.0 P5040-5021-R1.0-R2.0-R2.1 -+ * P5020-5010-R1.0-R2.0 P3041-R1.0-R1.1-R2.0 P2041-2040-R1.0-R1.1-R2.0 -+ * P1010-1014-R1.0 -+ */ -+ if (((esdhc->soc_ver == SVR_T4240) && (esdhc->soc_rev == 0x10)) || -+ ((esdhc->soc_ver == SVR_T4240) && (esdhc->soc_rev == 0x20)) || -+ ((esdhc->soc_ver == SVR_T4160) && (esdhc->soc_rev == 0x10)) || -+ ((esdhc->soc_ver == SVR_T4160) && (esdhc->soc_rev == 0x20)) || -+ ((esdhc->soc_ver == SVR_B4860) && (esdhc->soc_rev == 0x10)) || -+ ((esdhc->soc_ver == SVR_B4860) && (esdhc->soc_rev == 0x20)) || -+ ((esdhc->soc_ver == SVR_B4420) && (esdhc->soc_rev == 0x10)) || -+ ((esdhc->soc_ver == SVR_B4420) && (esdhc->soc_rev == 0x20)) || -+ ((esdhc->soc_ver == SVR_P5040) && (esdhc->soc_rev <= 0x21)) || -+ ((esdhc->soc_ver == SVR_P5021) && (esdhc->soc_rev <= 0x21)) || -+ ((esdhc->soc_ver == SVR_P5020) && (esdhc->soc_rev <= 0x20)) || -+ ((esdhc->soc_ver == SVR_P5010) && (esdhc->soc_rev <= 0x20)) || -+ ((esdhc->soc_ver == SVR_P3041) && (esdhc->soc_rev <= 0x20)) || -+ ((esdhc->soc_ver == SVR_P2041) && (esdhc->soc_rev <= 0x20)) || -+ ((esdhc->soc_ver == SVR_P2040) && (esdhc->soc_rev <= 0x20)) || -+ ((esdhc->soc_ver == SVR_P1014) && (esdhc->soc_rev == 0x10)) || -+ ((esdhc->soc_ver == SVR_P1010) && (esdhc->soc_rev == 0x10))) -+ disable_clk_before_reset = true; -+ -+ if (disable_clk_before_reset && (mask & SDHCI_RESET_ALL)) { -+ temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); -+ temp &= ~ESDHC_CLOCK_CRDEN; -+ sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); -+ sdhci_reset(host, mask); -+ temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); -+ temp |= ESDHC_CLOCK_CRDEN; -+ sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); -+ return 1; -+ } -+ return 0; -+} -+ -+static void esdhc_reset(struct sdhci_host *host, u8 mask) -+{ -+ if (!esdhc_of_reset_workaround(host, mask)) -+ sdhci_reset(host, mask); - -+ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); -+ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); -+} -+ -+#ifdef CONFIG_PM - static u32 esdhc_proctl; - static int esdhc_of_suspend(struct device *dev) - { - struct sdhci_host *host = dev_get_drvdata(dev); - -- esdhc_proctl = sdhci_be32bs_readl(host, SDHCI_HOST_CONTROL); -+ esdhc_proctl = sdhci_readl(host, SDHCI_HOST_CONTROL); - - return sdhci_suspend_host(host); - } -@@ -311,11 +650,8 @@ static int esdhc_of_resume(struct device *dev) - struct sdhci_host *host = dev_get_drvdata(dev); - int ret = sdhci_resume_host(host); - -- if (ret == 0) { -- /* Isn't this already done by sdhci_resume_host() ? --rmk */ -- esdhc_of_enable_dma(host); -- sdhci_be32bs_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL); -- } -+ if (ret == 0) -+ sdhci_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL); - - return ret; - } -@@ -329,30 +665,120 @@ static const struct dev_pm_ops esdhc_pmops = { - #define ESDHC_PMOPS NULL - #endif - --static const struct sdhci_pltfm_data sdhci_esdhc_pdata = { -- /* -- * card detection could be handled via GPIO -- * eSDHC cannot support End Attribute in NOP ADMA descriptor -- */ -+static const struct sdhci_ops sdhci_esdhc_be_ops = { -+ .read_l = esdhc_be_readl, -+ .read_w = esdhc_be_readw, -+ .read_b = esdhc_be_readb, -+ .write_l = esdhc_be_writel, -+ .write_w = esdhc_be_writew, -+ .write_b = esdhc_be_writeb, -+ .set_clock = esdhc_of_set_clock, -+ .enable_dma = esdhc_of_enable_dma, -+ .get_max_clock = esdhc_of_get_max_clock, -+ .get_min_clock = esdhc_of_get_min_clock, -+ .adma_workaround = esdhc_of_adma_workaround, -+ .set_bus_width = esdhc_pltfm_set_bus_width, -+ .reset = esdhc_reset, -+ .set_uhs_signaling = sdhci_set_uhs_signaling, -+}; -+ -+static const struct sdhci_ops sdhci_esdhc_le_ops = { -+ .read_l = esdhc_le_readl, -+ .read_w = esdhc_le_readw, -+ .read_b = esdhc_le_readb, -+ .write_l = esdhc_le_writel, -+ .write_w = esdhc_le_writew, -+ .write_b = esdhc_le_writeb, -+ .set_clock = esdhc_of_set_clock, -+ .enable_dma = esdhc_of_enable_dma, -+ .get_max_clock = esdhc_of_get_max_clock, -+ .get_min_clock = esdhc_of_get_min_clock, -+ .adma_workaround = esdhc_of_adma_workaround, -+ .set_bus_width = esdhc_pltfm_set_bus_width, -+ .reset = esdhc_reset, -+ .set_uhs_signaling = sdhci_set_uhs_signaling, -+}; -+ -+static const struct sdhci_pltfm_data sdhci_esdhc_be_pdata = { - .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_CARD_DETECTION - | SDHCI_QUIRK_NO_CARD_NO_RESET - | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, -- .ops = &sdhci_esdhc_ops, -+ .ops = &sdhci_esdhc_be_ops, - }; - -+static const struct sdhci_pltfm_data sdhci_esdhc_le_pdata = { -+ .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_CARD_DETECTION -+ | SDHCI_QUIRK_NO_CARD_NO_RESET -+ | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, -+ .ops = &sdhci_esdhc_le_ops, -+}; -+ -+static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host) -+{ -+ struct sdhci_pltfm_host *pltfm_host; -+ struct sdhci_esdhc *esdhc; -+ u16 host_ver; -+ u32 svr; -+ -+ pltfm_host = sdhci_priv(host); -+ esdhc = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_esdhc), -+ GFP_KERNEL); -+ pltfm_host->priv = esdhc; -+ -+ svr = guts_get_svr(); -+ esdhc->soc_ver = SVR_SOC_VER(svr); -+ esdhc->soc_rev = SVR_REV(svr); -+ -+ host_ver = sdhci_readw(host, SDHCI_HOST_VERSION); -+ esdhc->vendor_ver = (host_ver & SDHCI_VENDOR_VER_MASK) >> -+ SDHCI_VENDOR_VER_SHIFT; -+ esdhc->spec_ver = host_ver & SDHCI_SPEC_VER_MASK; -+} -+ - static int sdhci_esdhc_probe(struct platform_device *pdev) - { - struct sdhci_host *host; - struct device_node *np; -+ struct sdhci_pltfm_host *pltfm_host; -+ struct sdhci_esdhc *esdhc; - int ret; - -- host = sdhci_pltfm_init(pdev, &sdhci_esdhc_pdata, 0); -+ np = pdev->dev.of_node; -+ -+ if (of_get_property(np, "little-endian", NULL)) -+ host = sdhci_pltfm_init(pdev, &sdhci_esdhc_le_pdata, 0); -+ else -+ host = sdhci_pltfm_init(pdev, &sdhci_esdhc_be_pdata, 0); -+ - if (IS_ERR(host)) - return PTR_ERR(host); - -+ esdhc_init(pdev, host); -+ - sdhci_get_of_property(pdev); - -- np = pdev->dev.of_node; -+ pltfm_host = sdhci_priv(host); -+ esdhc = pltfm_host->priv; -+ if (esdhc->vendor_ver == VENDOR_V_22) -+ host->quirks2 |= SDHCI_QUIRK2_HOST_NO_CMD23; -+ -+ if (esdhc->vendor_ver > VENDOR_V_22) -+ host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ; -+ -+ if (of_device_is_compatible(np, "fsl,p5040-esdhc") || -+ of_device_is_compatible(np, "fsl,p5020-esdhc") || -+ of_device_is_compatible(np, "fsl,p4080-esdhc") || -+ of_device_is_compatible(np, "fsl,p1020-esdhc") || -+ of_device_is_compatible(np, "fsl,t1040-esdhc") || -+ of_device_is_compatible(np, "fsl,ls1021a-esdhc") || -+ of_device_is_compatible(np, "fsl,ls2080a-esdhc") || -+ of_device_is_compatible(np, "fsl,ls2085a-esdhc") || -+ of_device_is_compatible(np, "fsl,ls1043a-esdhc")) -+ host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; -+ -+ if (of_device_is_compatible(np, "fsl,ls1021a-esdhc")) -+ host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; -+ - if (of_device_is_compatible(np, "fsl,p2020-esdhc")) { - /* - * Freescale messed up with P2020 as it has a non-standard -@@ -362,13 +788,19 @@ static int sdhci_esdhc_probe(struct platform_device *pdev) - } - - /* call to generic mmc_of_parse to support additional capabilities */ -- mmc_of_parse(host->mmc); -+ ret = mmc_of_parse(host->mmc); -+ if (ret) -+ goto err; -+ - mmc_of_parse_voltage(np, &host->ocr_mask); - - ret = sdhci_add_host(host); - if (ret) -- sdhci_pltfm_free(pdev); -+ goto err; - -+ return 0; -+ err: -+ sdhci_pltfm_free(pdev); - return ret; - } - -diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c -index 023c201..8af38a6 100644 ---- a/drivers/mmc/host/sdhci.c -+++ b/drivers/mmc/host/sdhci.c -@@ -44,8 +44,6 @@ - - #define MAX_TUNING_LOOP 40 - --#define ADMA_SIZE ((128 * 2 + 1) * 4) -- - static unsigned int debug_quirks = 0; - static unsigned int debug_quirks2; - -@@ -119,10 +117,17 @@ static void sdhci_dumpregs(struct sdhci_host *host) - pr_debug(DRIVER_NAME ": Host ctl2: 0x%08x\n", - sdhci_readw(host, SDHCI_HOST_CONTROL2)); - -- if (host->flags & SDHCI_USE_ADMA) -- pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n", -- readl(host->ioaddr + SDHCI_ADMA_ERROR), -- readl(host->ioaddr + SDHCI_ADMA_ADDRESS)); -+ if (host->flags & SDHCI_USE_ADMA) { -+ if (host->flags & SDHCI_USE_64_BIT_DMA) -+ pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x%08x\n", -+ readl(host->ioaddr + SDHCI_ADMA_ERROR), -+ readl(host->ioaddr + SDHCI_ADMA_ADDRESS_HI), -+ readl(host->ioaddr + SDHCI_ADMA_ADDRESS)); -+ else -+ pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n", -+ readl(host->ioaddr + SDHCI_ADMA_ERROR), -+ readl(host->ioaddr + SDHCI_ADMA_ADDRESS)); -+ } - - pr_debug(DRIVER_NAME ": ===========================================\n"); - } -@@ -231,6 +236,9 @@ static void sdhci_init(struct sdhci_host *host, int soft) - SDHCI_INT_TIMEOUT | SDHCI_INT_DATA_END | - SDHCI_INT_RESPONSE; - -+ if (host->flags & SDHCI_AUTO_CMD12) -+ host->ier |= SDHCI_INT_ACMD12ERR; -+ - sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); - sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); - -@@ -448,18 +456,26 @@ static void sdhci_kunmap_atomic(void *buffer, unsigned long *flags) - local_irq_restore(*flags); - } - --static void sdhci_set_adma_desc(u8 *desc, u32 addr, int len, unsigned cmd) -+static void sdhci_adma_write_desc(struct sdhci_host *host, void *desc, -+ dma_addr_t addr, int len, unsigned cmd) - { -- __le32 *dataddr = (__le32 __force *)(desc + 4); -- __le16 *cmdlen = (__le16 __force *)desc; -+ struct sdhci_adma2_64_desc *dma_desc = desc; -+ -+ /* 32-bit and 64-bit descriptors have these members in same position */ -+ dma_desc->cmd = cpu_to_le16(cmd); -+ dma_desc->len = cpu_to_le16(len); -+ dma_desc->addr_lo = cpu_to_le32((u32)addr); - -- /* SDHCI specification says ADMA descriptors should be 4 byte -- * aligned, so using 16 or 32bit operations should be safe. */ -+ if (host->flags & SDHCI_USE_64_BIT_DMA) -+ dma_desc->addr_hi = cpu_to_le32((u64)addr >> 32); -+} - -- cmdlen[0] = cpu_to_le16(cmd); -- cmdlen[1] = cpu_to_le16(len); -+static void sdhci_adma_mark_end(void *desc) -+{ -+ struct sdhci_adma2_64_desc *dma_desc = desc; - -- dataddr[0] = cpu_to_le32(addr); -+ /* 32-bit and 64-bit descriptors have 'cmd' in same position */ -+ dma_desc->cmd |= cpu_to_le16(ADMA2_END); - } - - static int sdhci_adma_table_pre(struct sdhci_host *host, -@@ -467,8 +483,8 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, - { - int direction; - -- u8 *desc; -- u8 *align; -+ void *desc; -+ void *align; - dma_addr_t addr; - dma_addr_t align_addr; - int len, offset; -@@ -489,17 +505,17 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, - direction = DMA_TO_DEVICE; - - host->align_addr = dma_map_single(mmc_dev(host->mmc), -- host->align_buffer, 128 * 4, direction); -+ host->align_buffer, host->align_buffer_sz, direction); - if (dma_mapping_error(mmc_dev(host->mmc), host->align_addr)) - goto fail; -- BUG_ON(host->align_addr & 0x3); -+ BUG_ON(host->align_addr & host->align_mask); - - host->sg_count = dma_map_sg(mmc_dev(host->mmc), - data->sg, data->sg_len, direction); - if (host->sg_count == 0) - goto unmap_align; - -- desc = host->adma_desc; -+ desc = host->adma_table; - align = host->align_buffer; - - align_addr = host->align_addr; -@@ -515,24 +531,27 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, - * the (up to three) bytes that screw up the - * alignment. - */ -- offset = (4 - (addr & 0x3)) & 0x3; -+ offset = (host->align_sz - (addr & host->align_mask)) & -+ host->align_mask; - if (offset) { - if (data->flags & MMC_DATA_WRITE) { - buffer = sdhci_kmap_atomic(sg, &flags); -- WARN_ON(((long)buffer & PAGE_MASK) > (PAGE_SIZE - 3)); -+ WARN_ON(((long)buffer & (PAGE_SIZE - 1)) > -+ (PAGE_SIZE - offset)); - memcpy(align, buffer, offset); - sdhci_kunmap_atomic(buffer, &flags); - } - - /* tran, valid */ -- sdhci_set_adma_desc(desc, align_addr, offset, 0x21); -+ sdhci_adma_write_desc(host, desc, align_addr, offset, -+ ADMA2_TRAN_VALID); - - BUG_ON(offset > 65536); - -- align += 4; -- align_addr += 4; -+ align += host->align_sz; -+ align_addr += host->align_sz; - -- desc += 8; -+ desc += host->desc_sz; - - addr += offset; - len -= offset; -@@ -541,23 +560,23 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, - BUG_ON(len > 65536); - - /* tran, valid */ -- sdhci_set_adma_desc(desc, addr, len, 0x21); -- desc += 8; -+ sdhci_adma_write_desc(host, desc, addr, len, ADMA2_TRAN_VALID); -+ desc += host->desc_sz; - - /* - * If this triggers then we have a calculation bug - * somewhere. :/ - */ -- WARN_ON((desc - host->adma_desc) > ADMA_SIZE); -+ WARN_ON((desc - host->adma_table) >= host->adma_table_sz); - } - - if (host->quirks & SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC) { - /* - * Mark the last descriptor as the terminating descriptor - */ -- if (desc != host->adma_desc) { -- desc -= 8; -- desc[0] |= 0x2; /* end */ -+ if (desc != host->adma_table) { -+ desc -= host->desc_sz; -+ sdhci_adma_mark_end(desc); - } - } else { - /* -@@ -565,7 +584,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, - */ - - /* nop, end, valid */ -- sdhci_set_adma_desc(desc, 0, 0, 0x3); -+ sdhci_adma_write_desc(host, desc, 0, 0, ADMA2_NOP_END_VALID); - } - - /* -@@ -573,14 +592,14 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, - */ - if (data->flags & MMC_DATA_WRITE) { - dma_sync_single_for_device(mmc_dev(host->mmc), -- host->align_addr, 128 * 4, direction); -+ host->align_addr, host->align_buffer_sz, direction); - } - - return 0; - - unmap_align: - dma_unmap_single(mmc_dev(host->mmc), host->align_addr, -- 128 * 4, direction); -+ host->align_buffer_sz, direction); - fail: - return -EINVAL; - } -@@ -592,7 +611,7 @@ static void sdhci_adma_table_post(struct sdhci_host *host, - - struct scatterlist *sg; - int i, size; -- u8 *align; -+ void *align; - char *buffer; - unsigned long flags; - bool has_unaligned; -@@ -603,12 +622,12 @@ static void sdhci_adma_table_post(struct sdhci_host *host, - direction = DMA_TO_DEVICE; - - dma_unmap_single(mmc_dev(host->mmc), host->align_addr, -- 128 * 4, direction); -+ host->align_buffer_sz, direction); - - /* Do a quick scan of the SG list for any unaligned mappings */ - has_unaligned = false; - for_each_sg(data->sg, sg, host->sg_count, i) -- if (sg_dma_address(sg) & 3) { -+ if (sg_dma_address(sg) & host->align_mask) { - has_unaligned = true; - break; - } -@@ -620,15 +639,17 @@ static void sdhci_adma_table_post(struct sdhci_host *host, - align = host->align_buffer; - - for_each_sg(data->sg, sg, host->sg_count, i) { -- if (sg_dma_address(sg) & 0x3) { -- size = 4 - (sg_dma_address(sg) & 0x3); -+ if (sg_dma_address(sg) & host->align_mask) { -+ size = host->align_sz - -+ (sg_dma_address(sg) & host->align_mask); - - buffer = sdhci_kmap_atomic(sg, &flags); -- WARN_ON(((long)buffer & PAGE_MASK) > (PAGE_SIZE - 3)); -+ WARN_ON(((long)buffer & (PAGE_SIZE - 1)) > -+ (PAGE_SIZE - size)); - memcpy(buffer, align, size); - sdhci_kunmap_atomic(buffer, &flags); - -- align += 4; -+ align += host->align_sz; - } - } - } -@@ -822,6 +843,10 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) - } else { - sdhci_writel(host, host->adma_addr, - SDHCI_ADMA_ADDRESS); -+ if (host->flags & SDHCI_USE_64_BIT_DMA) -+ sdhci_writel(host, -+ (u64)host->adma_addr >> 32, -+ SDHCI_ADMA_ADDRESS_HI); - } - } else { - int sg_cnt; -@@ -855,10 +880,14 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) - ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); - ctrl &= ~SDHCI_CTRL_DMA_MASK; - if ((host->flags & SDHCI_REQ_USE_DMA) && -- (host->flags & SDHCI_USE_ADMA)) -- ctrl |= SDHCI_CTRL_ADMA32; -- else -+ (host->flags & SDHCI_USE_ADMA)) { -+ if (host->flags & SDHCI_USE_64_BIT_DMA) -+ ctrl |= SDHCI_CTRL_ADMA64; -+ else -+ ctrl |= SDHCI_CTRL_ADMA32; -+ } else { - ctrl |= SDHCI_CTRL_SDMA; -+ } - sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); - } - -@@ -1797,6 +1826,10 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, - ctrl |= SDHCI_CTRL_VDD_180; - sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); - -+ /* Some controller need to do more when switching */ -+ if (host->ops->voltage_switch) -+ host->ops->voltage_switch(host); -+ - /* 1.8V regulator output should be stable within 5 ms */ - ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); - if (ctrl & SDHCI_CTRL_VDD_180) -@@ -2250,7 +2283,7 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *mask) - if (intmask & SDHCI_INT_TIMEOUT) - host->cmd->error = -ETIMEDOUT; - else if (intmask & (SDHCI_INT_CRC | SDHCI_INT_END_BIT | -- SDHCI_INT_INDEX)) -+ SDHCI_INT_INDEX | SDHCI_INT_ACMD12ERR)) - host->cmd->error = -EILSEQ; - - if (host->cmd->error) { -@@ -2292,32 +2325,36 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *mask) - } - - #ifdef CONFIG_MMC_DEBUG --static void sdhci_show_adma_error(struct sdhci_host *host) -+static void sdhci_adma_show_error(struct sdhci_host *host) - { - const char *name = mmc_hostname(host->mmc); -- u8 *desc = host->adma_desc; -- __le32 *dma; -- __le16 *len; -- u8 attr; -+ void *desc = host->adma_table; - - sdhci_dumpregs(host); - - while (true) { -- dma = (__le32 *)(desc + 4); -- len = (__le16 *)(desc + 2); -- attr = *desc; -- -- DBG("%s: %p: DMA 0x%08x, LEN 0x%04x, Attr=0x%02x\n", -- name, desc, le32_to_cpu(*dma), le16_to_cpu(*len), attr); -+ struct sdhci_adma2_64_desc *dma_desc = desc; -+ -+ if (host->flags & SDHCI_USE_64_BIT_DMA) -+ DBG("%s: %p: DMA 0x%08x%08x, LEN 0x%04x, Attr=0x%02x\n", -+ name, desc, le32_to_cpu(dma_desc->addr_hi), -+ le32_to_cpu(dma_desc->addr_lo), -+ le16_to_cpu(dma_desc->len), -+ le16_to_cpu(dma_desc->cmd)); -+ else -+ DBG("%s: %p: DMA 0x%08x, LEN 0x%04x, Attr=0x%02x\n", -+ name, desc, le32_to_cpu(dma_desc->addr_lo), -+ le16_to_cpu(dma_desc->len), -+ le16_to_cpu(dma_desc->cmd)); - -- desc += 8; -+ desc += host->desc_sz; - -- if (attr & 2) -+ if (dma_desc->cmd & cpu_to_le16(ADMA2_END)) - break; - } - } - #else --static void sdhci_show_adma_error(struct sdhci_host *host) { } -+static void sdhci_adma_show_error(struct sdhci_host *host) { } - #endif - - static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) -@@ -2380,7 +2417,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) - host->data->error = -EILSEQ; - else if (intmask & SDHCI_INT_ADMA_ERROR) { - pr_err("%s: ADMA error\n", mmc_hostname(host->mmc)); -- sdhci_show_adma_error(host); -+ sdhci_adma_show_error(host); - host->data->error = -EIO; - if (host->ops->adma_workaround) - host->ops->adma_workaround(host, intmask); -@@ -2859,6 +2896,16 @@ int sdhci_add_host(struct sdhci_host *host) - host->flags &= ~SDHCI_USE_ADMA; - } - -+ /* -+ * It is assumed that a 64-bit capable device has set a 64-bit DMA mask -+ * and *must* do 64-bit DMA. A driver has the opportunity to change -+ * that during the first call to ->enable_dma(). Similarly -+ * SDHCI_QUIRK2_BROKEN_64_BIT_DMA must be left to the drivers to -+ * implement. -+ */ -+ if (sdhci_readl(host, SDHCI_CAPABILITIES) & SDHCI_CAN_64BIT) -+ host->flags |= SDHCI_USE_64_BIT_DMA; -+ - if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { - if (host->ops->enable_dma) { - if (host->ops->enable_dma(host)) { -@@ -2870,33 +2917,59 @@ int sdhci_add_host(struct sdhci_host *host) - } - } - -+ /* SDMA does not support 64-bit DMA */ -+ if (host->flags & SDHCI_USE_64_BIT_DMA) -+ host->flags &= ~SDHCI_USE_SDMA; -+ - if (host->flags & SDHCI_USE_ADMA) { - /* -- * We need to allocate descriptors for all sg entries -- * (128) and potentially one alignment transfer for -- * each of those entries. -+ * The DMA descriptor table size is calculated as the maximum -+ * number of segments times 2, to allow for an alignment -+ * descriptor for each segment, plus 1 for a nop end descriptor, -+ * all multipled by the descriptor size. - */ -- host->adma_desc = dma_alloc_coherent(mmc_dev(mmc), -- ADMA_SIZE, &host->adma_addr, -- GFP_KERNEL); -- host->align_buffer = kmalloc(128 * 4, GFP_KERNEL); -- if (!host->adma_desc || !host->align_buffer) { -- dma_free_coherent(mmc_dev(mmc), ADMA_SIZE, -- host->adma_desc, host->adma_addr); -+ if (host->flags & SDHCI_USE_64_BIT_DMA) { -+ host->adma_table_sz = (SDHCI_MAX_SEGS * 2 + 1) * -+ SDHCI_ADMA2_64_DESC_SZ; -+ host->align_buffer_sz = SDHCI_MAX_SEGS * -+ SDHCI_ADMA2_64_ALIGN; -+ host->desc_sz = SDHCI_ADMA2_64_DESC_SZ; -+ host->align_sz = SDHCI_ADMA2_64_ALIGN; -+ host->align_mask = SDHCI_ADMA2_64_ALIGN - 1; -+ } else { -+ host->adma_table_sz = (SDHCI_MAX_SEGS * 2 + 1) * -+ SDHCI_ADMA2_32_DESC_SZ; -+ host->align_buffer_sz = SDHCI_MAX_SEGS * -+ SDHCI_ADMA2_32_ALIGN; -+ host->desc_sz = SDHCI_ADMA2_32_DESC_SZ; -+ host->align_sz = SDHCI_ADMA2_32_ALIGN; -+ host->align_mask = SDHCI_ADMA2_32_ALIGN - 1; -+ } -+ host->adma_table = dma_alloc_coherent(mmc_dev(mmc), -+ host->adma_table_sz, -+ &host->adma_addr, -+ GFP_KERNEL); -+ host->align_buffer = kmalloc(host->align_buffer_sz, GFP_KERNEL); -+ if (!host->adma_table || !host->align_buffer) { -+ if (host->adma_table) -+ dma_free_coherent(mmc_dev(mmc), -+ host->adma_table_sz, -+ host->adma_table, -+ host->adma_addr); - kfree(host->align_buffer); - pr_warn("%s: Unable to allocate ADMA buffers - falling back to standard DMA\n", - mmc_hostname(mmc)); - host->flags &= ~SDHCI_USE_ADMA; -- host->adma_desc = NULL; -+ host->adma_table = NULL; - host->align_buffer = NULL; -- } else if (host->adma_addr & 3) { -+ } else if (host->adma_addr & host->align_mask) { - pr_warn("%s: unable to allocate aligned ADMA descriptor\n", - mmc_hostname(mmc)); - host->flags &= ~SDHCI_USE_ADMA; -- dma_free_coherent(mmc_dev(mmc), ADMA_SIZE, -- host->adma_desc, host->adma_addr); -+ dma_free_coherent(mmc_dev(mmc), host->adma_table_sz, -+ host->adma_table, host->adma_addr); - kfree(host->align_buffer); -- host->adma_desc = NULL; -+ host->adma_table = NULL; - host->align_buffer = NULL; - } - } -@@ -2995,7 +3068,8 @@ int sdhci_add_host(struct sdhci_host *host) - /* Auto-CMD23 stuff only works in ADMA or PIO. */ - if ((host->version >= SDHCI_SPEC_300) && - ((host->flags & SDHCI_USE_ADMA) || -- !(host->flags & SDHCI_USE_SDMA))) { -+ !(host->flags & SDHCI_USE_SDMA)) && -+ !(host->quirks2 & SDHCI_QUIRK2_ACMD23_BROKEN)) { - host->flags |= SDHCI_AUTO_CMD23; - DBG("%s: Auto-CMD23 available\n", mmc_hostname(mmc)); - } else { -@@ -3152,13 +3226,14 @@ int sdhci_add_host(struct sdhci_host *host) - SDHCI_MAX_CURRENT_MULTIPLIER; - } - -- /* If OCR set by external regulators, use it instead */ -+ /* If OCR set by host, use it instead. */ -+ if (host->ocr_mask) -+ ocr_avail = host->ocr_mask; -+ -+ /* If OCR set by external regulators, give it highest prio. */ - if (mmc->ocr_avail) - ocr_avail = mmc->ocr_avail; - -- if (host->ocr_mask) -- ocr_avail &= host->ocr_mask; -- - mmc->ocr_avail = ocr_avail; - mmc->ocr_avail_sdio = ocr_avail; - if (host->ocr_avail_sdio) -@@ -3185,11 +3260,11 @@ int sdhci_add_host(struct sdhci_host *host) - * can do scatter/gather or not. - */ - if (host->flags & SDHCI_USE_ADMA) -- mmc->max_segs = 128; -+ mmc->max_segs = SDHCI_MAX_SEGS; - else if (host->flags & SDHCI_USE_SDMA) - mmc->max_segs = 1; - else /* PIO */ -- mmc->max_segs = 128; -+ mmc->max_segs = SDHCI_MAX_SEGS; - - /* - * Maximum number of sectors in one transfer. Limited by DMA boundary -@@ -3287,7 +3362,8 @@ int sdhci_add_host(struct sdhci_host *host) - - pr_info("%s: SDHCI controller on %s [%s] using %s\n", - mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)), -- (host->flags & SDHCI_USE_ADMA) ? "ADMA" : -+ (host->flags & SDHCI_USE_ADMA) ? -+ (host->flags & SDHCI_USE_64_BIT_DMA) ? "ADMA 64-bit" : "ADMA" : - (host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO"); - - sdhci_enable_card_detection(host); -@@ -3355,12 +3431,12 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) - if (!IS_ERR(mmc->supply.vqmmc)) - regulator_disable(mmc->supply.vqmmc); - -- if (host->adma_desc) -- dma_free_coherent(mmc_dev(mmc), ADMA_SIZE, -- host->adma_desc, host->adma_addr); -+ if (host->adma_table) -+ dma_free_coherent(mmc_dev(mmc), host->adma_table_sz, -+ host->adma_table, host->adma_addr); - kfree(host->align_buffer); - -- host->adma_desc = NULL; -+ host->adma_table = NULL; - host->align_buffer = NULL; - } - -diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h -index 31896a7..5220f36 100644 ---- a/drivers/mmc/host/sdhci.h -+++ b/drivers/mmc/host/sdhci.h -@@ -227,6 +227,7 @@ - /* 55-57 reserved */ - - #define SDHCI_ADMA_ADDRESS 0x58 -+#define SDHCI_ADMA_ADDRESS_HI 0x5C - - /* 60-FB reserved */ - -@@ -266,6 +267,46 @@ - #define SDHCI_DEFAULT_BOUNDARY_SIZE (512 * 1024) - #define SDHCI_DEFAULT_BOUNDARY_ARG (ilog2(SDHCI_DEFAULT_BOUNDARY_SIZE) - 12) - -+/* ADMA2 32-bit DMA descriptor size */ -+#define SDHCI_ADMA2_32_DESC_SZ 8 -+ -+/* ADMA2 32-bit DMA alignment */ -+#define SDHCI_ADMA2_32_ALIGN 4 -+ -+/* ADMA2 32-bit descriptor */ -+struct sdhci_adma2_32_desc { -+ __le16 cmd; -+ __le16 len; -+ __le32 addr; -+} __packed __aligned(SDHCI_ADMA2_32_ALIGN); -+ -+/* ADMA2 64-bit DMA descriptor size */ -+#define SDHCI_ADMA2_64_DESC_SZ 12 -+ -+/* ADMA2 64-bit DMA alignment */ -+#define SDHCI_ADMA2_64_ALIGN 8 -+ -+/* -+ * ADMA2 64-bit descriptor. Note 12-byte descriptor can't always be 8-byte -+ * aligned. -+ */ -+struct sdhci_adma2_64_desc { -+ __le16 cmd; -+ __le16 len; -+ __le32 addr_lo; -+ __le32 addr_hi; -+} __packed __aligned(4); -+ -+#define ADMA2_TRAN_VALID 0x21 -+#define ADMA2_NOP_END_VALID 0x3 -+#define ADMA2_END 0x2 -+ -+/* -+ * Maximum segments assuming a 512KiB maximum requisition size and a minimum -+ * 4KiB page size. -+ */ -+#define SDHCI_MAX_SEGS 128 -+ - struct sdhci_ops { - #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS - u32 (*read_l)(struct sdhci_host *host, int reg); -@@ -296,6 +337,7 @@ struct sdhci_ops { - void (*adma_workaround)(struct sdhci_host *host, u32 intmask); - void (*platform_init)(struct sdhci_host *host); - void (*card_event)(struct sdhci_host *host); -+ void (*voltage_switch)(struct sdhci_host *host); - }; - - #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS -diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig -index dd10646..34ce759 100644 ---- a/drivers/mtd/nand/Kconfig -+++ b/drivers/mtd/nand/Kconfig -@@ -429,7 +429,7 @@ config MTD_NAND_FSL_ELBC - - config MTD_NAND_FSL_IFC - tristate "NAND support for Freescale IFC controller" -- depends on MTD_NAND && FSL_SOC -+ depends on MTD_NAND && (FSL_SOC || ARCH_LAYERSCAPE) - select FSL_IFC - select MEMORY - help -diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c -index 2338124..c8be272 100644 ---- a/drivers/mtd/nand/fsl_ifc_nand.c -+++ b/drivers/mtd/nand/fsl_ifc_nand.c -@@ -31,7 +31,6 @@ - #include - #include - --#define FSL_IFC_V1_1_0 0x01010000 - #define ERR_BYTE 0xFF /* Value returned for read - bytes when read failed */ - #define IFC_TIMEOUT_MSECS 500 /* Maximum number of mSecs to wait -@@ -234,13 +233,13 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob) - struct nand_chip *chip = mtd->priv; - struct fsl_ifc_mtd *priv = chip->priv; - struct fsl_ifc_ctrl *ctrl = priv->ctrl; -- struct fsl_ifc_regs __iomem *ifc = ctrl->regs; -+ struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; - int buf_num; - - ifc_nand_ctrl->page = page_addr; - /* Program ROW0/COL0 */ -- iowrite32be(page_addr, &ifc->ifc_nand.row0); -- iowrite32be((oob ? IFC_NAND_COL_MS : 0) | column, &ifc->ifc_nand.col0); -+ ifc_out32(page_addr, &ifc->ifc_nand.row0); -+ ifc_out32((oob ? IFC_NAND_COL_MS : 0) | column, &ifc->ifc_nand.col0); - - buf_num = page_addr & priv->bufnum_mask; - -@@ -297,28 +296,28 @@ static void fsl_ifc_run_command(struct mtd_info *mtd) - struct fsl_ifc_mtd *priv = chip->priv; - struct fsl_ifc_ctrl *ctrl = priv->ctrl; - struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl; -- struct fsl_ifc_regs __iomem *ifc = ctrl->regs; -+ struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; - u32 eccstat[4]; - int i; - - /* set the chip select for NAND Transaction */ -- iowrite32be(priv->bank << IFC_NAND_CSEL_SHIFT, -- &ifc->ifc_nand.nand_csel); -+ ifc_out32(priv->bank << IFC_NAND_CSEL_SHIFT, -+ &ifc->ifc_nand.nand_csel); - - dev_vdbg(priv->dev, - "%s: fir0=%08x fcr0=%08x\n", - __func__, -- ioread32be(&ifc->ifc_nand.nand_fir0), -- ioread32be(&ifc->ifc_nand.nand_fcr0)); -+ ifc_in32(&ifc->ifc_nand.nand_fir0), -+ ifc_in32(&ifc->ifc_nand.nand_fcr0)); - - ctrl->nand_stat = 0; - - /* start read/write seq */ -- iowrite32be(IFC_NAND_SEQ_STRT_FIR_STRT, &ifc->ifc_nand.nandseq_strt); -+ ifc_out32(IFC_NAND_SEQ_STRT_FIR_STRT, &ifc->ifc_nand.nandseq_strt); - - /* wait for command complete flag or timeout */ - wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat, -- IFC_TIMEOUT_MSECS * HZ/1000); -+ msecs_to_jiffies(IFC_TIMEOUT_MSECS)); - - /* ctrl->nand_stat will be updated from IRQ context */ - if (!ctrl->nand_stat) -@@ -337,7 +336,7 @@ static void fsl_ifc_run_command(struct mtd_info *mtd) - int sector_end = sector + chip->ecc.steps - 1; - - for (i = sector / 4; i <= sector_end / 4; i++) -- eccstat[i] = ioread32be(&ifc->ifc_nand.nand_eccstat[i]); -+ eccstat[i] = ifc_in32(&ifc->ifc_nand.nand_eccstat[i]); - - for (i = sector; i <= sector_end; i++) { - errors = check_read_ecc(mtd, ctrl, eccstat, i); -@@ -373,37 +372,37 @@ static void fsl_ifc_do_read(struct nand_chip *chip, - { - struct fsl_ifc_mtd *priv = chip->priv; - struct fsl_ifc_ctrl *ctrl = priv->ctrl; -- struct fsl_ifc_regs __iomem *ifc = ctrl->regs; -+ struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; - - /* Program FIR/IFC_NAND_FCR0 for Small/Large page */ - if (mtd->writesize > 512) { -- iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | -- (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | -- (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | -- (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) | -- (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP4_SHIFT), -- &ifc->ifc_nand.nand_fir0); -- iowrite32be(0x0, &ifc->ifc_nand.nand_fir1); -- -- iowrite32be((NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) | -- (NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT), -- &ifc->ifc_nand.nand_fcr0); -+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | -+ (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | -+ (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | -+ (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) | -+ (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP4_SHIFT), -+ &ifc->ifc_nand.nand_fir0); -+ ifc_out32(0x0, &ifc->ifc_nand.nand_fir1); -+ -+ ifc_out32((NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) | -+ (NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT), -+ &ifc->ifc_nand.nand_fcr0); - } else { -- iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | -- (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | -- (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | -- (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP3_SHIFT), -- &ifc->ifc_nand.nand_fir0); -- iowrite32be(0x0, &ifc->ifc_nand.nand_fir1); -+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | -+ (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | -+ (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | -+ (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP3_SHIFT), -+ &ifc->ifc_nand.nand_fir0); -+ ifc_out32(0x0, &ifc->ifc_nand.nand_fir1); - - if (oob) -- iowrite32be(NAND_CMD_READOOB << -- IFC_NAND_FCR0_CMD0_SHIFT, -- &ifc->ifc_nand.nand_fcr0); -+ ifc_out32(NAND_CMD_READOOB << -+ IFC_NAND_FCR0_CMD0_SHIFT, -+ &ifc->ifc_nand.nand_fcr0); - else -- iowrite32be(NAND_CMD_READ0 << -- IFC_NAND_FCR0_CMD0_SHIFT, -- &ifc->ifc_nand.nand_fcr0); -+ ifc_out32(NAND_CMD_READ0 << -+ IFC_NAND_FCR0_CMD0_SHIFT, -+ &ifc->ifc_nand.nand_fcr0); - } - } - -@@ -413,7 +412,7 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, - struct nand_chip *chip = mtd->priv; - struct fsl_ifc_mtd *priv = chip->priv; - struct fsl_ifc_ctrl *ctrl = priv->ctrl; -- struct fsl_ifc_regs __iomem *ifc = ctrl->regs; -+ struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; - - /* clear the read buffer */ - ifc_nand_ctrl->read_bytes = 0; -@@ -423,7 +422,7 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, - switch (command) { - /* READ0 read the entire buffer to use hardware ECC. */ - case NAND_CMD_READ0: -- iowrite32be(0, &ifc->ifc_nand.nand_fbcr); -+ ifc_out32(0, &ifc->ifc_nand.nand_fbcr); - set_addr(mtd, 0, page_addr, 0); - - ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize; -@@ -438,7 +437,7 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, - - /* READOOB reads only the OOB because no ECC is performed. */ - case NAND_CMD_READOOB: -- iowrite32be(mtd->oobsize - column, &ifc->ifc_nand.nand_fbcr); -+ ifc_out32(mtd->oobsize - column, &ifc->ifc_nand.nand_fbcr); - set_addr(mtd, column, page_addr, 1); - - ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize; -@@ -454,19 +453,19 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, - if (command == NAND_CMD_PARAM) - timing = IFC_FIR_OP_RBCD; - -- iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | -- (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | -- (timing << IFC_NAND_FIR0_OP2_SHIFT), -- &ifc->ifc_nand.nand_fir0); -- iowrite32be(command << IFC_NAND_FCR0_CMD0_SHIFT, -- &ifc->ifc_nand.nand_fcr0); -- iowrite32be(column, &ifc->ifc_nand.row3); -+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | -+ (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | -+ (timing << IFC_NAND_FIR0_OP2_SHIFT), -+ &ifc->ifc_nand.nand_fir0); -+ ifc_out32(command << IFC_NAND_FCR0_CMD0_SHIFT, -+ &ifc->ifc_nand.nand_fcr0); -+ ifc_out32(column, &ifc->ifc_nand.row3); - - /* - * although currently it's 8 bytes for READID, we always read - * the maximum 256 bytes(for PARAM) - */ -- iowrite32be(256, &ifc->ifc_nand.nand_fbcr); -+ ifc_out32(256, &ifc->ifc_nand.nand_fbcr); - ifc_nand_ctrl->read_bytes = 256; - - set_addr(mtd, 0, 0, 0); -@@ -481,16 +480,16 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, - - /* ERASE2 uses the block and page address from ERASE1 */ - case NAND_CMD_ERASE2: -- iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | -- (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP1_SHIFT) | -- (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP2_SHIFT), -- &ifc->ifc_nand.nand_fir0); -+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | -+ (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP1_SHIFT) | -+ (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP2_SHIFT), -+ &ifc->ifc_nand.nand_fir0); - -- iowrite32be((NAND_CMD_ERASE1 << IFC_NAND_FCR0_CMD0_SHIFT) | -- (NAND_CMD_ERASE2 << IFC_NAND_FCR0_CMD1_SHIFT), -- &ifc->ifc_nand.nand_fcr0); -+ ifc_out32((NAND_CMD_ERASE1 << IFC_NAND_FCR0_CMD0_SHIFT) | -+ (NAND_CMD_ERASE2 << IFC_NAND_FCR0_CMD1_SHIFT), -+ &ifc->ifc_nand.nand_fcr0); - -- iowrite32be(0, &ifc->ifc_nand.nand_fbcr); -+ ifc_out32(0, &ifc->ifc_nand.nand_fbcr); - ifc_nand_ctrl->read_bytes = 0; - fsl_ifc_run_command(mtd); - return; -@@ -507,19 +506,18 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, - (NAND_CMD_STATUS << IFC_NAND_FCR0_CMD1_SHIFT) | - (NAND_CMD_PAGEPROG << IFC_NAND_FCR0_CMD2_SHIFT); - -- iowrite32be( -- (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | -- (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | -- (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | -- (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP3_SHIFT) | -- (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP4_SHIFT), -- &ifc->ifc_nand.nand_fir0); -- iowrite32be( -- (IFC_FIR_OP_CW1 << IFC_NAND_FIR1_OP5_SHIFT) | -- (IFC_FIR_OP_RDSTAT << -- IFC_NAND_FIR1_OP6_SHIFT) | -- (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP7_SHIFT), -- &ifc->ifc_nand.nand_fir1); -+ ifc_out32( -+ (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | -+ (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | -+ (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | -+ (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP3_SHIFT) | -+ (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP4_SHIFT), -+ &ifc->ifc_nand.nand_fir0); -+ ifc_out32( -+ (IFC_FIR_OP_CW1 << IFC_NAND_FIR1_OP5_SHIFT) | -+ (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR1_OP6_SHIFT) | -+ (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP7_SHIFT), -+ &ifc->ifc_nand.nand_fir1); - } else { - nand_fcr0 = ((NAND_CMD_PAGEPROG << - IFC_NAND_FCR0_CMD1_SHIFT) | -@@ -528,20 +526,19 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, - (NAND_CMD_STATUS << - IFC_NAND_FCR0_CMD3_SHIFT)); - -- iowrite32be( -+ ifc_out32( - (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP1_SHIFT) | - (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP2_SHIFT) | - (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP3_SHIFT) | - (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP4_SHIFT), - &ifc->ifc_nand.nand_fir0); -- iowrite32be( -- (IFC_FIR_OP_CMD1 << IFC_NAND_FIR1_OP5_SHIFT) | -- (IFC_FIR_OP_CW3 << IFC_NAND_FIR1_OP6_SHIFT) | -- (IFC_FIR_OP_RDSTAT << -- IFC_NAND_FIR1_OP7_SHIFT) | -- (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP8_SHIFT), -- &ifc->ifc_nand.nand_fir1); -+ ifc_out32( -+ (IFC_FIR_OP_CMD1 << IFC_NAND_FIR1_OP5_SHIFT) | -+ (IFC_FIR_OP_CW3 << IFC_NAND_FIR1_OP6_SHIFT) | -+ (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR1_OP7_SHIFT) | -+ (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP8_SHIFT), -+ &ifc->ifc_nand.nand_fir1); - - if (column >= mtd->writesize) - nand_fcr0 |= -@@ -556,7 +553,7 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, - column -= mtd->writesize; - ifc_nand_ctrl->oob = 1; - } -- iowrite32be(nand_fcr0, &ifc->ifc_nand.nand_fcr0); -+ ifc_out32(nand_fcr0, &ifc->ifc_nand.nand_fcr0); - set_addr(mtd, column, page_addr, ifc_nand_ctrl->oob); - return; - } -@@ -564,24 +561,26 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, - /* PAGEPROG reuses all of the setup from SEQIN and adds the length */ - case NAND_CMD_PAGEPROG: { - if (ifc_nand_ctrl->oob) { -- iowrite32be(ifc_nand_ctrl->index - -- ifc_nand_ctrl->column, -- &ifc->ifc_nand.nand_fbcr); -+ ifc_out32(ifc_nand_ctrl->index - -+ ifc_nand_ctrl->column, -+ &ifc->ifc_nand.nand_fbcr); - } else { -- iowrite32be(0, &ifc->ifc_nand.nand_fbcr); -+ ifc_out32(0, &ifc->ifc_nand.nand_fbcr); - } - - fsl_ifc_run_command(mtd); - return; - } - -- case NAND_CMD_STATUS: -- iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | -- (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP1_SHIFT), -- &ifc->ifc_nand.nand_fir0); -- iowrite32be(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT, -- &ifc->ifc_nand.nand_fcr0); -- iowrite32be(1, &ifc->ifc_nand.nand_fbcr); -+ case NAND_CMD_STATUS: { -+ void __iomem *addr; -+ -+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | -+ (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP1_SHIFT), -+ &ifc->ifc_nand.nand_fir0); -+ ifc_out32(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT, -+ &ifc->ifc_nand.nand_fcr0); -+ ifc_out32(1, &ifc->ifc_nand.nand_fbcr); - set_addr(mtd, 0, 0, 0); - ifc_nand_ctrl->read_bytes = 1; - -@@ -591,17 +590,19 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, - * The chip always seems to report that it is - * write-protected, even when it is not. - */ -+ addr = ifc_nand_ctrl->addr; - if (chip->options & NAND_BUSWIDTH_16) -- setbits16(ifc_nand_ctrl->addr, NAND_STATUS_WP); -+ ifc_out16(ifc_in16(addr) | (NAND_STATUS_WP), addr); - else -- setbits8(ifc_nand_ctrl->addr, NAND_STATUS_WP); -+ ifc_out8(ifc_in8(addr) | (NAND_STATUS_WP), addr); - return; -+ } - - case NAND_CMD_RESET: -- iowrite32be(IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT, -- &ifc->ifc_nand.nand_fir0); -- iowrite32be(NAND_CMD_RESET << IFC_NAND_FCR0_CMD0_SHIFT, -- &ifc->ifc_nand.nand_fcr0); -+ ifc_out32(IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT, -+ &ifc->ifc_nand.nand_fir0); -+ ifc_out32(NAND_CMD_RESET << IFC_NAND_FCR0_CMD0_SHIFT, -+ &ifc->ifc_nand.nand_fcr0); - fsl_ifc_run_command(mtd); - return; - -@@ -659,7 +660,7 @@ static uint8_t fsl_ifc_read_byte(struct mtd_info *mtd) - */ - if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) { - offset = ifc_nand_ctrl->index++; -- return in_8(ifc_nand_ctrl->addr + offset); -+ return ifc_in8(ifc_nand_ctrl->addr + offset); - } - - dev_err(priv->dev, "%s: beyond end of buffer\n", __func__); -@@ -681,7 +682,7 @@ static uint8_t fsl_ifc_read_byte16(struct mtd_info *mtd) - * next byte. - */ - if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) { -- data = in_be16(ifc_nand_ctrl->addr + ifc_nand_ctrl->index); -+ data = ifc_in16(ifc_nand_ctrl->addr + ifc_nand_ctrl->index); - ifc_nand_ctrl->index += 2; - return (uint8_t) data; - } -@@ -723,22 +724,22 @@ static int fsl_ifc_wait(struct mtd_info *mtd, struct nand_chip *chip) - { - struct fsl_ifc_mtd *priv = chip->priv; - struct fsl_ifc_ctrl *ctrl = priv->ctrl; -- struct fsl_ifc_regs __iomem *ifc = ctrl->regs; -+ struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; - u32 nand_fsr; - - /* Use READ_STATUS command, but wait for the device to be ready */ -- iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | -- (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR0_OP1_SHIFT), -- &ifc->ifc_nand.nand_fir0); -- iowrite32be(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT, -- &ifc->ifc_nand.nand_fcr0); -- iowrite32be(1, &ifc->ifc_nand.nand_fbcr); -+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | -+ (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR0_OP1_SHIFT), -+ &ifc->ifc_nand.nand_fir0); -+ ifc_out32(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT, -+ &ifc->ifc_nand.nand_fcr0); -+ ifc_out32(1, &ifc->ifc_nand.nand_fbcr); - set_addr(mtd, 0, 0, 0); - ifc_nand_ctrl->read_bytes = 1; - - fsl_ifc_run_command(mtd); - -- nand_fsr = ioread32be(&ifc->ifc_nand.nand_fsr); -+ nand_fsr = ifc_in32(&ifc->ifc_nand.nand_fsr); - - /* - * The chip always seems to report that it is -@@ -825,67 +826,72 @@ static int fsl_ifc_chip_init_tail(struct mtd_info *mtd) - static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv) - { - struct fsl_ifc_ctrl *ctrl = priv->ctrl; -- struct fsl_ifc_regs __iomem *ifc = ctrl->regs; -+ struct fsl_ifc_runtime __iomem *ifc_runtime = ctrl->rregs; -+ struct fsl_ifc_global __iomem *ifc_global = ctrl->gregs; - uint32_t csor = 0, csor_8k = 0, csor_ext = 0; - uint32_t cs = priv->bank; - - /* Save CSOR and CSOR_ext */ -- csor = ioread32be(&ifc->csor_cs[cs].csor); -- csor_ext = ioread32be(&ifc->csor_cs[cs].csor_ext); -+ csor = ifc_in32(&ifc_global->csor_cs[cs].csor); -+ csor_ext = ifc_in32(&ifc_global->csor_cs[cs].csor_ext); - - /* chage PageSize 8K and SpareSize 1K*/ - csor_8k = (csor & ~(CSOR_NAND_PGS_MASK)) | 0x0018C000; -- iowrite32be(csor_8k, &ifc->csor_cs[cs].csor); -- iowrite32be(0x0000400, &ifc->csor_cs[cs].csor_ext); -+ ifc_out32(csor_8k, &ifc_global->csor_cs[cs].csor); -+ ifc_out32(0x0000400, &ifc_global->csor_cs[cs].csor_ext); - - /* READID */ -- iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | -+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | - (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT), -- &ifc->ifc_nand.nand_fir0); -- iowrite32be(NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT, -- &ifc->ifc_nand.nand_fcr0); -- iowrite32be(0x0, &ifc->ifc_nand.row3); -+ &ifc_runtime->ifc_nand.nand_fir0); -+ ifc_out32(NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT, -+ &ifc_runtime->ifc_nand.nand_fcr0); -+ ifc_out32(0x0, &ifc_runtime->ifc_nand.row3); - -- iowrite32be(0x0, &ifc->ifc_nand.nand_fbcr); -+ ifc_out32(0x0, &ifc_runtime->ifc_nand.nand_fbcr); - - /* Program ROW0/COL0 */ -- iowrite32be(0x0, &ifc->ifc_nand.row0); -- iowrite32be(0x0, &ifc->ifc_nand.col0); -+ ifc_out32(0x0, &ifc_runtime->ifc_nand.row0); -+ ifc_out32(0x0, &ifc_runtime->ifc_nand.col0); - - /* set the chip select for NAND Transaction */ -- iowrite32be(cs << IFC_NAND_CSEL_SHIFT, &ifc->ifc_nand.nand_csel); -+ ifc_out32(cs << IFC_NAND_CSEL_SHIFT, -+ &ifc_runtime->ifc_nand.nand_csel); - - /* start read seq */ -- iowrite32be(IFC_NAND_SEQ_STRT_FIR_STRT, &ifc->ifc_nand.nandseq_strt); -+ ifc_out32(IFC_NAND_SEQ_STRT_FIR_STRT, -+ &ifc_runtime->ifc_nand.nandseq_strt); - - /* wait for command complete flag or timeout */ - wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat, -- IFC_TIMEOUT_MSECS * HZ/1000); -+ msecs_to_jiffies(IFC_TIMEOUT_MSECS)); - - if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC) - printk(KERN_ERR "fsl-ifc: Failed to Initialise SRAM\n"); - - /* Restore CSOR and CSOR_ext */ -- iowrite32be(csor, &ifc->csor_cs[cs].csor); -- iowrite32be(csor_ext, &ifc->csor_cs[cs].csor_ext); -+ ifc_out32(csor, &ifc_global->csor_cs[cs].csor); -+ ifc_out32(csor_ext, &ifc_global->csor_cs[cs].csor_ext); - } - - static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) - { - struct fsl_ifc_ctrl *ctrl = priv->ctrl; -- struct fsl_ifc_regs __iomem *ifc = ctrl->regs; -+ struct fsl_ifc_global __iomem *ifc_global = ctrl->gregs; -+ struct fsl_ifc_runtime __iomem *ifc_runtime = ctrl->rregs; - struct nand_chip *chip = &priv->chip; - struct nand_ecclayout *layout; -- u32 csor, ver; -+ u32 csor; - - /* Fill in fsl_ifc_mtd structure */ - priv->mtd.priv = chip; -- priv->mtd.owner = THIS_MODULE; -+ priv->mtd.dev.parent = priv->dev; - - /* fill in nand_chip structure */ - /* set up function call table */ -- if ((ioread32be(&ifc->cspr_cs[priv->bank].cspr)) & CSPR_PORT_SIZE_16) -+ if ((ifc_in32(&ifc_global->cspr_cs[priv->bank].cspr)) -+ & CSPR_PORT_SIZE_16) - chip->read_byte = fsl_ifc_read_byte16; - else - chip->read_byte = fsl_ifc_read_byte; -@@ -899,13 +905,14 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) - chip->bbt_td = &bbt_main_descr; - chip->bbt_md = &bbt_mirror_descr; - -- iowrite32be(0x0, &ifc->ifc_nand.ncfgr); -+ ifc_out32(0x0, &ifc_runtime->ifc_nand.ncfgr); - - /* set up nand options */ - chip->bbt_options = NAND_BBT_USE_FLASH; - chip->options = NAND_NO_SUBPAGE_WRITE; - -- if (ioread32be(&ifc->cspr_cs[priv->bank].cspr) & CSPR_PORT_SIZE_16) { -+ if (ifc_in32(&ifc_global->cspr_cs[priv->bank].cspr) -+ & CSPR_PORT_SIZE_16) { - chip->read_byte = fsl_ifc_read_byte16; - chip->options |= NAND_BUSWIDTH_16; - } else { -@@ -918,7 +925,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) - chip->ecc.read_page = fsl_ifc_read_page; - chip->ecc.write_page = fsl_ifc_write_page; - -- csor = ioread32be(&ifc->csor_cs[priv->bank].csor); -+ csor = ifc_in32(&ifc_global->csor_cs[priv->bank].csor); - - /* Hardware generates ECC per 512 Bytes */ - chip->ecc.size = 512; -@@ -984,8 +991,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) - chip->ecc.mode = NAND_ECC_SOFT; - } - -- ver = ioread32be(&ifc->ifc_rev); -- if (ver == FSL_IFC_V1_1_0) -+ if (ctrl->version == FSL_IFC_VERSION_1_1_0) - fsl_ifc_sram_init(priv); - - return 0; -@@ -1005,10 +1011,10 @@ static int fsl_ifc_chip_remove(struct fsl_ifc_mtd *priv) - return 0; - } - --static int match_bank(struct fsl_ifc_regs __iomem *ifc, int bank, -+static int match_bank(struct fsl_ifc_global __iomem *ifc_global, int bank, - phys_addr_t addr) - { -- u32 cspr = ioread32be(&ifc->cspr_cs[bank].cspr); -+ u32 cspr = ifc_in32(&ifc_global->cspr_cs[bank].cspr); - - if (!(cspr & CSPR_V)) - return 0; -@@ -1022,7 +1028,7 @@ static DEFINE_MUTEX(fsl_ifc_nand_mutex); - - static int fsl_ifc_nand_probe(struct platform_device *dev) - { -- struct fsl_ifc_regs __iomem *ifc; -+ struct fsl_ifc_runtime __iomem *ifc; - struct fsl_ifc_mtd *priv; - struct resource res; - static const char *part_probe_types[] -@@ -1033,9 +1039,9 @@ static int fsl_ifc_nand_probe(struct platform_device *dev) - struct mtd_part_parser_data ppdata; - - ppdata.of_node = dev->dev.of_node; -- if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->regs) -+ if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->rregs) - return -ENODEV; -- ifc = fsl_ifc_ctrl_dev->regs; -+ ifc = fsl_ifc_ctrl_dev->rregs; - - /* get, allocate and map the memory resource */ - ret = of_address_to_resource(node, 0, &res); -@@ -1045,12 +1051,12 @@ static int fsl_ifc_nand_probe(struct platform_device *dev) - } - - /* find which chip select it is connected to */ -- for (bank = 0; bank < FSL_IFC_BANK_COUNT; bank++) { -- if (match_bank(ifc, bank, res.start)) -+ for (bank = 0; bank < fsl_ifc_ctrl_dev->banks; bank++) { -+ if (match_bank(fsl_ifc_ctrl_dev->gregs, bank, res.start)) - break; - } - -- if (bank >= FSL_IFC_BANK_COUNT) { -+ if (bank >= fsl_ifc_ctrl_dev->banks) { - dev_err(&dev->dev, "%s: address did not match any chip selects\n", - __func__); - return -ENODEV; -@@ -1094,16 +1100,16 @@ static int fsl_ifc_nand_probe(struct platform_device *dev) - - dev_set_drvdata(priv->dev, priv); - -- iowrite32be(IFC_NAND_EVTER_EN_OPC_EN | -- IFC_NAND_EVTER_EN_FTOER_EN | -- IFC_NAND_EVTER_EN_WPER_EN, -- &ifc->ifc_nand.nand_evter_en); -+ ifc_out32(IFC_NAND_EVTER_EN_OPC_EN | -+ IFC_NAND_EVTER_EN_FTOER_EN | -+ IFC_NAND_EVTER_EN_WPER_EN, -+ &ifc->ifc_nand.nand_evter_en); - - /* enable NAND Machine Interrupts */ -- iowrite32be(IFC_NAND_EVTER_INTR_OPCIR_EN | -- IFC_NAND_EVTER_INTR_FTOERIR_EN | -- IFC_NAND_EVTER_INTR_WPERIR_EN, -- &ifc->ifc_nand.nand_evter_intr_en); -+ ifc_out32(IFC_NAND_EVTER_INTR_OPCIR_EN | -+ IFC_NAND_EVTER_INTR_FTOERIR_EN | -+ IFC_NAND_EVTER_INTR_WPERIR_EN, -+ &ifc->ifc_nand.nand_evter_intr_en); - priv->mtd.name = kasprintf(GFP_KERNEL, "%llx.flash", (u64)res.start); - if (!priv->mtd.name) { - ret = -ENOMEM; -@@ -1163,6 +1169,7 @@ static const struct of_device_id fsl_ifc_nand_match[] = { - }, - {} - }; -+MODULE_DEVICE_TABLE(of, fsl_ifc_nand_match); - - static struct platform_driver fsl_ifc_nand_driver = { - .driver = { -diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c -index a4a7396..0359cfd 100644 ---- a/drivers/net/ethernet/freescale/gianfar.c -+++ b/drivers/net/ethernet/freescale/gianfar.c -@@ -86,11 +86,11 @@ - #include - #include - #include -+#include - - #include - #ifdef CONFIG_PPC - #include --#include - #endif - #include - #include -@@ -1720,8 +1720,10 @@ static void gfar_configure_serdes(struct net_device *dev) - * everything for us? Resetting it takes the link down and requires - * several seconds for it to come back. - */ -- if (phy_read(tbiphy, MII_BMSR) & BMSR_LSTATUS) -+ if (phy_read(tbiphy, MII_BMSR) & BMSR_LSTATUS) { -+ put_device(&tbiphy->dev); - return; -+ } - - /* Single clk mode, mii mode off(for serdes communication) */ - phy_write(tbiphy, MII_TBICON, TBICON_CLK_SELECT); -diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig -index 2973c60..cdc9f8a 100644 ---- a/drivers/net/phy/Kconfig -+++ b/drivers/net/phy/Kconfig -@@ -65,6 +65,11 @@ config VITESSE_PHY - ---help--- - Currently supports the vsc8244 - -+config TERANETICS_PHY -+ tristate "Drivers for the Teranetics PHYs" -+ ---help--- -+ Currently supports the Teranetics TN2020 -+ - config SMSC_PHY - tristate "Drivers for SMSC PHYs" - ---help--- -@@ -124,8 +129,8 @@ config MICREL_PHY - Supports the KSZ9021, VSC8201, KS8001 PHYs. - - config FIXED_PHY -- bool "Driver for MDIO Bus/PHY emulation with fixed speed/link PHYs" -- depends on PHYLIB=y -+ tristate "Driver for MDIO Bus/PHY emulation with fixed speed/link PHYs" -+ depends on PHYLIB - ---help--- - Adds the platform "fixed" MDIO Bus to cover the boards that use - PHYs that are not connected to the real MDIO bus. -@@ -207,6 +212,11 @@ config MDIO_BUS_MUX_MMIOREG - the FPGA's registers. - - Currently, only 8-bit registers are supported. -+config FSL_10GBASE_KR -+ tristate "Support for 10GBASE-KR on Freescale XFI interface" -+ depends on OF_MDIO -+ help -+ This module provides a driver for Freescale XFI's 10GBASE-KR. - - config MDIO_BCM_UNIMAC - tristate "Broadcom UniMAC MDIO bus controller" -diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile -index b5c8f9f..8ad4ac6 100644 ---- a/drivers/net/phy/Makefile -+++ b/drivers/net/phy/Makefile -@@ -10,6 +10,7 @@ obj-$(CONFIG_CICADA_PHY) += cicada.o - obj-$(CONFIG_LXT_PHY) += lxt.o - obj-$(CONFIG_QSEMI_PHY) += qsemi.o - obj-$(CONFIG_SMSC_PHY) += smsc.o -+obj-$(CONFIG_TERANETICS_PHY) += teranetics.o - obj-$(CONFIG_VITESSE_PHY) += vitesse.o - obj-$(CONFIG_BROADCOM_PHY) += broadcom.o - obj-$(CONFIG_BCM63XX_PHY) += bcm63xx.o -@@ -18,7 +19,7 @@ obj-$(CONFIG_BCM87XX_PHY) += bcm87xx.o - obj-$(CONFIG_ICPLUS_PHY) += icplus.o - obj-$(CONFIG_REALTEK_PHY) += realtek.o - obj-$(CONFIG_LSI_ET1011C_PHY) += et1011c.o --obj-$(CONFIG_FIXED_PHY) += fixed.o -+obj-$(CONFIG_FIXED_PHY) += fixed_phy.o - obj-$(CONFIG_MDIO_BITBANG) += mdio-bitbang.o - obj-$(CONFIG_MDIO_GPIO) += mdio-gpio.o - obj-$(CONFIG_NATIONAL_PHY) += national.o -@@ -32,6 +33,7 @@ obj-$(CONFIG_AMD_PHY) += amd.o - obj-$(CONFIG_MDIO_BUS_MUX) += mdio-mux.o - obj-$(CONFIG_MDIO_BUS_MUX_GPIO) += mdio-mux-gpio.o - obj-$(CONFIG_MDIO_BUS_MUX_MMIOREG) += mdio-mux-mmioreg.o -+obj-$(CONFIG_FSL_10GBASE_KR) += fsl_10gkr.o - obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o - obj-$(CONFIG_MDIO_MOXART) += mdio-moxart.o - obj-$(CONFIG_AMD_XGBE_PHY) += amd-xgbe-phy.o -diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c -index fdc1b41..a4f0886 100644 ---- a/drivers/net/phy/at803x.c -+++ b/drivers/net/phy/at803x.c -@@ -307,6 +307,8 @@ static struct phy_driver at803x_driver[] = { - .flags = PHY_HAS_INTERRUPT, - .config_aneg = genphy_config_aneg, - .read_status = genphy_read_status, -+ .ack_interrupt = at803x_ack_interrupt, -+ .config_intr = at803x_config_intr, - .driver = { - .owner = THIS_MODULE, - }, -@@ -326,6 +328,8 @@ static struct phy_driver at803x_driver[] = { - .flags = PHY_HAS_INTERRUPT, - .config_aneg = genphy_config_aneg, - .read_status = genphy_read_status, -+ .ack_interrupt = at803x_ack_interrupt, -+ .config_intr = at803x_config_intr, - .driver = { - .owner = THIS_MODULE, - }, -diff --git a/drivers/net/phy/fixed.c b/drivers/net/phy/fixed.c -deleted file mode 100644 -index 47872ca..0000000 ---- a/drivers/net/phy/fixed.c -+++ /dev/null -@@ -1,336 +0,0 @@ --/* -- * Fixed MDIO bus (MDIO bus emulation with fixed PHYs) -- * -- * Author: Vitaly Bordug -- * Anton Vorontsov -- * -- * Copyright (c) 2006-2007 MontaVista Software, 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 --#include --#include --#include --#include --#include --#include --#include --#include --#include -- --#define MII_REGS_NUM 29 -- --struct fixed_mdio_bus { -- int irqs[PHY_MAX_ADDR]; -- struct mii_bus *mii_bus; -- struct list_head phys; --}; -- --struct fixed_phy { -- int addr; -- u16 regs[MII_REGS_NUM]; -- struct phy_device *phydev; -- struct fixed_phy_status status; -- int (*link_update)(struct net_device *, struct fixed_phy_status *); -- struct list_head node; --}; -- --static struct platform_device *pdev; --static struct fixed_mdio_bus platform_fmb = { -- .phys = LIST_HEAD_INIT(platform_fmb.phys), --}; -- --static int fixed_phy_update_regs(struct fixed_phy *fp) --{ -- u16 bmsr = BMSR_ANEGCAPABLE; -- u16 bmcr = 0; -- u16 lpagb = 0; -- u16 lpa = 0; -- -- if (fp->status.duplex) { -- bmcr |= BMCR_FULLDPLX; -- -- switch (fp->status.speed) { -- case 1000: -- bmsr |= BMSR_ESTATEN; -- bmcr |= BMCR_SPEED1000; -- lpagb |= LPA_1000FULL; -- break; -- case 100: -- bmsr |= BMSR_100FULL; -- bmcr |= BMCR_SPEED100; -- lpa |= LPA_100FULL; -- break; -- case 10: -- bmsr |= BMSR_10FULL; -- lpa |= LPA_10FULL; -- break; -- default: -- pr_warn("fixed phy: unknown speed\n"); -- return -EINVAL; -- } -- } else { -- switch (fp->status.speed) { -- case 1000: -- bmsr |= BMSR_ESTATEN; -- bmcr |= BMCR_SPEED1000; -- lpagb |= LPA_1000HALF; -- break; -- case 100: -- bmsr |= BMSR_100HALF; -- bmcr |= BMCR_SPEED100; -- lpa |= LPA_100HALF; -- break; -- case 10: -- bmsr |= BMSR_10HALF; -- lpa |= LPA_10HALF; -- break; -- default: -- pr_warn("fixed phy: unknown speed\n"); -- return -EINVAL; -- } -- } -- -- if (fp->status.link) -- bmsr |= BMSR_LSTATUS | BMSR_ANEGCOMPLETE; -- -- if (fp->status.pause) -- lpa |= LPA_PAUSE_CAP; -- -- if (fp->status.asym_pause) -- lpa |= LPA_PAUSE_ASYM; -- -- fp->regs[MII_PHYSID1] = 0; -- fp->regs[MII_PHYSID2] = 0; -- -- fp->regs[MII_BMSR] = bmsr; -- fp->regs[MII_BMCR] = bmcr; -- fp->regs[MII_LPA] = lpa; -- fp->regs[MII_STAT1000] = lpagb; -- -- return 0; --} -- --static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num) --{ -- struct fixed_mdio_bus *fmb = bus->priv; -- struct fixed_phy *fp; -- -- if (reg_num >= MII_REGS_NUM) -- return -1; -- -- /* We do not support emulating Clause 45 over Clause 22 register reads -- * return an error instead of bogus data. -- */ -- switch (reg_num) { -- case MII_MMD_CTRL: -- case MII_MMD_DATA: -- return -1; -- default: -- break; -- } -- -- list_for_each_entry(fp, &fmb->phys, node) { -- if (fp->addr == phy_addr) { -- /* Issue callback if user registered it. */ -- if (fp->link_update) { -- fp->link_update(fp->phydev->attached_dev, -- &fp->status); -- fixed_phy_update_regs(fp); -- } -- return fp->regs[reg_num]; -- } -- } -- -- return 0xFFFF; --} -- --static int fixed_mdio_write(struct mii_bus *bus, int phy_addr, int reg_num, -- u16 val) --{ -- return 0; --} -- --/* -- * If something weird is required to be done with link/speed, -- * network driver is able to assign a function to implement this. -- * May be useful for PHY's that need to be software-driven. -- */ --int fixed_phy_set_link_update(struct phy_device *phydev, -- int (*link_update)(struct net_device *, -- struct fixed_phy_status *)) --{ -- struct fixed_mdio_bus *fmb = &platform_fmb; -- struct fixed_phy *fp; -- -- if (!link_update || !phydev || !phydev->bus) -- return -EINVAL; -- -- list_for_each_entry(fp, &fmb->phys, node) { -- if (fp->addr == phydev->addr) { -- fp->link_update = link_update; -- fp->phydev = phydev; -- return 0; -- } -- } -- -- return -ENOENT; --} --EXPORT_SYMBOL_GPL(fixed_phy_set_link_update); -- --int fixed_phy_add(unsigned int irq, int phy_addr, -- struct fixed_phy_status *status) --{ -- int ret; -- struct fixed_mdio_bus *fmb = &platform_fmb; -- struct fixed_phy *fp; -- -- fp = kzalloc(sizeof(*fp), GFP_KERNEL); -- if (!fp) -- return -ENOMEM; -- -- memset(fp->regs, 0xFF, sizeof(fp->regs[0]) * MII_REGS_NUM); -- -- fmb->irqs[phy_addr] = irq; -- -- fp->addr = phy_addr; -- fp->status = *status; -- -- ret = fixed_phy_update_regs(fp); -- if (ret) -- goto err_regs; -- -- list_add_tail(&fp->node, &fmb->phys); -- -- return 0; -- --err_regs: -- kfree(fp); -- return ret; --} --EXPORT_SYMBOL_GPL(fixed_phy_add); -- --void fixed_phy_del(int phy_addr) --{ -- struct fixed_mdio_bus *fmb = &platform_fmb; -- struct fixed_phy *fp, *tmp; -- -- list_for_each_entry_safe(fp, tmp, &fmb->phys, node) { -- if (fp->addr == phy_addr) { -- list_del(&fp->node); -- kfree(fp); -- return; -- } -- } --} --EXPORT_SYMBOL_GPL(fixed_phy_del); -- --static int phy_fixed_addr; --static DEFINE_SPINLOCK(phy_fixed_addr_lock); -- --struct phy_device *fixed_phy_register(unsigned int irq, -- struct fixed_phy_status *status, -- struct device_node *np) --{ -- struct fixed_mdio_bus *fmb = &platform_fmb; -- struct phy_device *phy; -- int phy_addr; -- int ret; -- -- /* Get the next available PHY address, up to PHY_MAX_ADDR */ -- spin_lock(&phy_fixed_addr_lock); -- if (phy_fixed_addr == PHY_MAX_ADDR) { -- spin_unlock(&phy_fixed_addr_lock); -- return ERR_PTR(-ENOSPC); -- } -- phy_addr = phy_fixed_addr++; -- spin_unlock(&phy_fixed_addr_lock); -- -- ret = fixed_phy_add(PHY_POLL, phy_addr, status); -- if (ret < 0) -- return ERR_PTR(ret); -- -- phy = get_phy_device(fmb->mii_bus, phy_addr, false); -- if (!phy || IS_ERR(phy)) { -- fixed_phy_del(phy_addr); -- return ERR_PTR(-EINVAL); -- } -- -- of_node_get(np); -- phy->dev.of_node = np; -- -- ret = phy_device_register(phy); -- if (ret) { -- phy_device_free(phy); -- of_node_put(np); -- fixed_phy_del(phy_addr); -- return ERR_PTR(ret); -- } -- -- return phy; --} -- --static int __init fixed_mdio_bus_init(void) --{ -- struct fixed_mdio_bus *fmb = &platform_fmb; -- int ret; -- -- pdev = platform_device_register_simple("Fixed MDIO bus", 0, NULL, 0); -- if (IS_ERR(pdev)) { -- ret = PTR_ERR(pdev); -- goto err_pdev; -- } -- -- fmb->mii_bus = mdiobus_alloc(); -- if (fmb->mii_bus == NULL) { -- ret = -ENOMEM; -- goto err_mdiobus_reg; -- } -- -- snprintf(fmb->mii_bus->id, MII_BUS_ID_SIZE, "fixed-0"); -- fmb->mii_bus->name = "Fixed MDIO Bus"; -- fmb->mii_bus->priv = fmb; -- fmb->mii_bus->parent = &pdev->dev; -- fmb->mii_bus->read = &fixed_mdio_read; -- fmb->mii_bus->write = &fixed_mdio_write; -- fmb->mii_bus->irq = fmb->irqs; -- -- ret = mdiobus_register(fmb->mii_bus); -- if (ret) -- goto err_mdiobus_alloc; -- -- return 0; -- --err_mdiobus_alloc: -- mdiobus_free(fmb->mii_bus); --err_mdiobus_reg: -- platform_device_unregister(pdev); --err_pdev: -- return ret; --} --module_init(fixed_mdio_bus_init); -- --static void __exit fixed_mdio_bus_exit(void) --{ -- struct fixed_mdio_bus *fmb = &platform_fmb; -- struct fixed_phy *fp, *tmp; -- -- mdiobus_unregister(fmb->mii_bus); -- mdiobus_free(fmb->mii_bus); -- platform_device_unregister(pdev); -- -- list_for_each_entry_safe(fp, tmp, &fmb->phys, node) { -- list_del(&fp->node); -- kfree(fp); -- } --} --module_exit(fixed_mdio_bus_exit); -- --MODULE_DESCRIPTION("Fixed MDIO bus (MDIO bus emulation with fixed PHYs)"); --MODULE_AUTHOR("Vitaly Bordug"); --MODULE_LICENSE("GPL"); -diff --git a/drivers/net/phy/fixed_phy.c b/drivers/net/phy/fixed_phy.c -new file mode 100644 -index 0000000..88b8194 ---- /dev/null -+++ b/drivers/net/phy/fixed_phy.c -@@ -0,0 +1,370 @@ -+/* -+ * Fixed MDIO bus (MDIO bus emulation with fixed PHYs) -+ * -+ * Author: Vitaly Bordug -+ * Anton Vorontsov -+ * -+ * Copyright (c) 2006-2007 MontaVista Software, 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define MII_REGS_NUM 29 -+ -+struct fixed_mdio_bus { -+ int irqs[PHY_MAX_ADDR]; -+ struct mii_bus *mii_bus; -+ struct list_head phys; -+}; -+ -+struct fixed_phy { -+ int addr; -+ u16 regs[MII_REGS_NUM]; -+ struct phy_device *phydev; -+ struct fixed_phy_status status; -+ int (*link_update)(struct net_device *, struct fixed_phy_status *); -+ struct list_head node; -+}; -+ -+static struct platform_device *pdev; -+static struct fixed_mdio_bus platform_fmb = { -+ .phys = LIST_HEAD_INIT(platform_fmb.phys), -+}; -+ -+static int fixed_phy_update_regs(struct fixed_phy *fp) -+{ -+ u16 bmsr = BMSR_ANEGCAPABLE; -+ u16 bmcr = 0; -+ u16 lpagb = 0; -+ u16 lpa = 0; -+ -+ if (fp->status.duplex) { -+ bmcr |= BMCR_FULLDPLX; -+ -+ switch (fp->status.speed) { -+ case 10000: -+ break; -+ case 1000: -+ bmsr |= BMSR_ESTATEN; -+ bmcr |= BMCR_SPEED1000; -+ lpagb |= LPA_1000FULL; -+ break; -+ case 100: -+ bmsr |= BMSR_100FULL; -+ bmcr |= BMCR_SPEED100; -+ lpa |= LPA_100FULL; -+ break; -+ case 10: -+ bmsr |= BMSR_10FULL; -+ lpa |= LPA_10FULL; -+ break; -+ default: -+ pr_warn("fixed phy: unknown speed\n"); -+ return -EINVAL; -+ } -+ } else { -+ switch (fp->status.speed) { -+ case 10000: -+ break; -+ case 1000: -+ bmsr |= BMSR_ESTATEN; -+ bmcr |= BMCR_SPEED1000; -+ lpagb |= LPA_1000HALF; -+ break; -+ case 100: -+ bmsr |= BMSR_100HALF; -+ bmcr |= BMCR_SPEED100; -+ lpa |= LPA_100HALF; -+ break; -+ case 10: -+ bmsr |= BMSR_10HALF; -+ lpa |= LPA_10HALF; -+ break; -+ default: -+ pr_warn("fixed phy: unknown speed\n"); -+ return -EINVAL; -+ } -+ } -+ -+ if (fp->status.link) -+ bmsr |= BMSR_LSTATUS | BMSR_ANEGCOMPLETE; -+ -+ if (fp->status.pause) -+ lpa |= LPA_PAUSE_CAP; -+ -+ if (fp->status.asym_pause) -+ lpa |= LPA_PAUSE_ASYM; -+ -+ fp->regs[MII_PHYSID1] = 0; -+ fp->regs[MII_PHYSID2] = 0; -+ -+ fp->regs[MII_BMSR] = bmsr; -+ fp->regs[MII_BMCR] = bmcr; -+ fp->regs[MII_LPA] = lpa; -+ fp->regs[MII_STAT1000] = lpagb; -+ -+ return 0; -+} -+ -+static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num) -+{ -+ struct fixed_mdio_bus *fmb = bus->priv; -+ struct fixed_phy *fp; -+ -+ if (reg_num >= MII_REGS_NUM) -+ return -1; -+ -+ /* We do not support emulating Clause 45 over Clause 22 register reads -+ * return an error instead of bogus data. -+ */ -+ switch (reg_num) { -+ case MII_MMD_CTRL: -+ case MII_MMD_DATA: -+ return -1; -+ default: -+ break; -+ } -+ -+ list_for_each_entry(fp, &fmb->phys, node) { -+ if (fp->addr == phy_addr) { -+ /* Issue callback if user registered it. */ -+ if (fp->link_update) { -+ fp->link_update(fp->phydev->attached_dev, -+ &fp->status); -+ fixed_phy_update_regs(fp); -+ } -+ return fp->regs[reg_num]; -+ } -+ } -+ -+ return 0xFFFF; -+} -+ -+static int fixed_mdio_write(struct mii_bus *bus, int phy_addr, int reg_num, -+ u16 val) -+{ -+ return 0; -+} -+ -+/* -+ * If something weird is required to be done with link/speed, -+ * network driver is able to assign a function to implement this. -+ * May be useful for PHY's that need to be software-driven. -+ */ -+int fixed_phy_set_link_update(struct phy_device *phydev, -+ int (*link_update)(struct net_device *, -+ struct fixed_phy_status *)) -+{ -+ struct fixed_mdio_bus *fmb = &platform_fmb; -+ struct fixed_phy *fp; -+ -+ if (!phydev || !phydev->bus) -+ return -EINVAL; -+ -+ list_for_each_entry(fp, &fmb->phys, node) { -+ if (fp->addr == phydev->addr) { -+ fp->link_update = link_update; -+ fp->phydev = phydev; -+ return 0; -+ } -+ } -+ -+ return -ENOENT; -+} -+EXPORT_SYMBOL_GPL(fixed_phy_set_link_update); -+ -+int fixed_phy_update_state(struct phy_device *phydev, -+ const struct fixed_phy_status *status, -+ const struct fixed_phy_status *changed) -+{ -+ struct fixed_mdio_bus *fmb = &platform_fmb; -+ struct fixed_phy *fp; -+ -+ if (!phydev || !phydev->bus) -+ return -EINVAL; -+ -+ list_for_each_entry(fp, &fmb->phys, node) { -+ if (fp->addr == phydev->addr) { -+#define _UPD(x) if (changed->x) \ -+ fp->status.x = status->x -+ _UPD(link); -+ _UPD(speed); -+ _UPD(duplex); -+ _UPD(pause); -+ _UPD(asym_pause); -+#undef _UPD -+ fixed_phy_update_regs(fp); -+ return 0; -+ } -+ } -+ -+ return -ENOENT; -+} -+EXPORT_SYMBOL(fixed_phy_update_state); -+ -+int fixed_phy_add(unsigned int irq, int phy_addr, -+ struct fixed_phy_status *status) -+{ -+ int ret; -+ struct fixed_mdio_bus *fmb = &platform_fmb; -+ struct fixed_phy *fp; -+ -+ fp = kzalloc(sizeof(*fp), GFP_KERNEL); -+ if (!fp) -+ return -ENOMEM; -+ -+ memset(fp->regs, 0xFF, sizeof(fp->regs[0]) * MII_REGS_NUM); -+ -+ fmb->irqs[phy_addr] = irq; -+ -+ fp->addr = phy_addr; -+ fp->status = *status; -+ -+ ret = fixed_phy_update_regs(fp); -+ if (ret) -+ goto err_regs; -+ -+ list_add_tail(&fp->node, &fmb->phys); -+ -+ return 0; -+ -+err_regs: -+ kfree(fp); -+ return ret; -+} -+EXPORT_SYMBOL_GPL(fixed_phy_add); -+ -+void fixed_phy_del(int phy_addr) -+{ -+ struct fixed_mdio_bus *fmb = &platform_fmb; -+ struct fixed_phy *fp, *tmp; -+ -+ list_for_each_entry_safe(fp, tmp, &fmb->phys, node) { -+ if (fp->addr == phy_addr) { -+ list_del(&fp->node); -+ kfree(fp); -+ return; -+ } -+ } -+} -+EXPORT_SYMBOL_GPL(fixed_phy_del); -+ -+static int phy_fixed_addr; -+static DEFINE_SPINLOCK(phy_fixed_addr_lock); -+ -+struct phy_device *fixed_phy_register(unsigned int irq, -+ struct fixed_phy_status *status, -+ struct device_node *np) -+{ -+ struct fixed_mdio_bus *fmb = &platform_fmb; -+ struct phy_device *phy; -+ int phy_addr; -+ int ret; -+ -+ /* Get the next available PHY address, up to PHY_MAX_ADDR */ -+ spin_lock(&phy_fixed_addr_lock); -+ if (phy_fixed_addr == PHY_MAX_ADDR) { -+ spin_unlock(&phy_fixed_addr_lock); -+ return ERR_PTR(-ENOSPC); -+ } -+ phy_addr = phy_fixed_addr++; -+ spin_unlock(&phy_fixed_addr_lock); -+ -+ ret = fixed_phy_add(PHY_POLL, phy_addr, status); -+ if (ret < 0) -+ return ERR_PTR(ret); -+ -+ phy = get_phy_device(fmb->mii_bus, phy_addr, false); -+ if (!phy || IS_ERR(phy)) { -+ fixed_phy_del(phy_addr); -+ return ERR_PTR(-EINVAL); -+ } -+ -+ of_node_get(np); -+ phy->dev.of_node = np; -+ -+ ret = phy_device_register(phy); -+ if (ret) { -+ phy_device_free(phy); -+ of_node_put(np); -+ fixed_phy_del(phy_addr); -+ return ERR_PTR(ret); -+ } -+ -+ return phy; -+} -+EXPORT_SYMBOL_GPL(fixed_phy_register); -+ -+static int __init fixed_mdio_bus_init(void) -+{ -+ struct fixed_mdio_bus *fmb = &platform_fmb; -+ int ret; -+ -+ pdev = platform_device_register_simple("Fixed MDIO bus", 0, NULL, 0); -+ if (IS_ERR(pdev)) { -+ ret = PTR_ERR(pdev); -+ goto err_pdev; -+ } -+ -+ fmb->mii_bus = mdiobus_alloc(); -+ if (fmb->mii_bus == NULL) { -+ ret = -ENOMEM; -+ goto err_mdiobus_reg; -+ } -+ -+ snprintf(fmb->mii_bus->id, MII_BUS_ID_SIZE, "fixed-0"); -+ fmb->mii_bus->name = "Fixed MDIO Bus"; -+ fmb->mii_bus->priv = fmb; -+ fmb->mii_bus->parent = &pdev->dev; -+ fmb->mii_bus->read = &fixed_mdio_read; -+ fmb->mii_bus->write = &fixed_mdio_write; -+ fmb->mii_bus->irq = fmb->irqs; -+ -+ ret = mdiobus_register(fmb->mii_bus); -+ if (ret) -+ goto err_mdiobus_alloc; -+ -+ return 0; -+ -+err_mdiobus_alloc: -+ mdiobus_free(fmb->mii_bus); -+err_mdiobus_reg: -+ platform_device_unregister(pdev); -+err_pdev: -+ return ret; -+} -+module_init(fixed_mdio_bus_init); -+ -+static void __exit fixed_mdio_bus_exit(void) -+{ -+ struct fixed_mdio_bus *fmb = &platform_fmb; -+ struct fixed_phy *fp, *tmp; -+ -+ mdiobus_unregister(fmb->mii_bus); -+ mdiobus_free(fmb->mii_bus); -+ platform_device_unregister(pdev); -+ -+ list_for_each_entry_safe(fp, tmp, &fmb->phys, node) { -+ list_del(&fp->node); -+ kfree(fp); -+ } -+} -+module_exit(fixed_mdio_bus_exit); -+ -+MODULE_DESCRIPTION("Fixed MDIO bus (MDIO bus emulation with fixed PHYs)"); -+MODULE_AUTHOR("Vitaly Bordug"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c -index 225c033..969a198 100644 ---- a/drivers/net/phy/marvell.c -+++ b/drivers/net/phy/marvell.c -@@ -50,6 +50,7 @@ - #define MII_M1011_PHY_SCR 0x10 - #define MII_M1011_PHY_SCR_AUTO_CROSS 0x0060 - -+#define MII_M1145_PHY_EXT_ADDR_PAGE 0x16 - #define MII_M1145_PHY_EXT_SR 0x1b - #define MII_M1145_PHY_EXT_CR 0x14 - #define MII_M1145_RGMII_RX_DELAY 0x0080 -@@ -495,6 +496,16 @@ static int m88e1111_config_init(struct phy_device *phydev) - err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp); - if (err < 0) - return err; -+ -+ /* make sure copper is selected */ -+ err = phy_read(phydev, MII_M1145_PHY_EXT_ADDR_PAGE); -+ if (err < 0) -+ return err; -+ -+ err = phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE, -+ err & (~0xff)); -+ if (err < 0) -+ return err; - } - - if (phydev->interface == PHY_INTERFACE_MODE_RTBI) { -diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c -index 50051f2..accd605 100644 ---- a/drivers/net/phy/mdio_bus.c -+++ b/drivers/net/phy/mdio_bus.c -@@ -288,8 +288,11 @@ int mdiobus_register(struct mii_bus *bus) - - error: - while (--i >= 0) { -- if (bus->phy_map[i]) -- device_unregister(&bus->phy_map[i]->dev); -+ struct phy_device *phydev = bus->phy_map[i]; -+ if (phydev) { -+ phy_device_remove(phydev); -+ phy_device_free(phydev); -+ } - } - device_del(&bus->dev); - return err; -@@ -305,9 +308,11 @@ void mdiobus_unregister(struct mii_bus *bus) - - device_del(&bus->dev); - for (i = 0; i < PHY_MAX_ADDR; i++) { -- if (bus->phy_map[i]) -- device_unregister(&bus->phy_map[i]->dev); -- bus->phy_map[i] = NULL; -+ struct phy_device *phydev = bus->phy_map[i]; -+ if (phydev) { -+ phy_device_remove(phydev); -+ phy_device_free(phydev); -+ } - } - } - EXPORT_SYMBOL(mdiobus_unregister); -@@ -421,6 +426,8 @@ static int mdio_bus_match(struct device *dev, struct device_driver *drv) - { - struct phy_device *phydev = to_phy_device(dev); - struct phy_driver *phydrv = to_phy_driver(drv); -+ const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids); -+ int i; - - if (of_driver_match_device(dev, drv)) - return 1; -@@ -428,8 +435,21 @@ static int mdio_bus_match(struct device *dev, struct device_driver *drv) - if (phydrv->match_phy_device) - return phydrv->match_phy_device(phydev); - -- return (phydrv->phy_id & phydrv->phy_id_mask) == -- (phydev->phy_id & phydrv->phy_id_mask); -+ if (phydev->is_c45) { -+ for (i = 1; i < num_ids; i++) { -+ if (!(phydev->c45_ids.devices_in_package & (1 << i))) -+ continue; -+ -+ if ((phydrv->phy_id & phydrv->phy_id_mask) == -+ (phydev->c45_ids.device_ids[i] & -+ phydrv->phy_id_mask)) -+ return 1; -+ } -+ return 0; -+ } else { -+ return (phydrv->phy_id & phydrv->phy_id_mask) == -+ (phydev->phy_id & phydrv->phy_id_mask); -+ } - } - - #ifdef CONFIG_PM -diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c -index 91d6d03..840075e 100644 ---- a/drivers/net/phy/phy.c -+++ b/drivers/net/phy/phy.c -@@ -768,6 +768,7 @@ void phy_state_machine(struct work_struct *work) - container_of(dwork, struct phy_device, state_queue); - bool needs_aneg = false, do_suspend = false, do_resume = false; - int err = 0; -+ int old_link; - - mutex_lock(&phydev->lock); - -@@ -814,6 +815,9 @@ void phy_state_machine(struct work_struct *work) - needs_aneg = true; - break; - case PHY_NOLINK: -+ if (phy_interrupt_is_valid(phydev)) -+ break; -+ - err = phy_read_status(phydev); - if (err) - break; -@@ -851,11 +855,18 @@ void phy_state_machine(struct work_struct *work) - phydev->adjust_link(phydev->attached_dev); - break; - case PHY_RUNNING: -- /* Only register a CHANGE if we are -- * polling or ignoring interrupts -+ /* Only register a CHANGE if we are polling or ignoring -+ * interrupts and link changed since latest checking. - */ -- if (!phy_interrupt_is_valid(phydev)) -- phydev->state = PHY_CHANGELINK; -+ if (!phy_interrupt_is_valid(phydev)) { -+ old_link = phydev->link; -+ err = phy_read_status(phydev); -+ if (err) -+ break; -+ -+ if (old_link != phydev->link) -+ phydev->state = PHY_CHANGELINK; -+ } - break; - case PHY_CHANGELINK: - err = phy_read_status(phydev); -diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c -index 70a0d88..07b1aa9 100644 ---- a/drivers/net/phy/phy_device.c -+++ b/drivers/net/phy/phy_device.c -@@ -205,6 +205,37 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, - } - EXPORT_SYMBOL(phy_device_create); - -+/* get_phy_c45_devs_in_pkg - reads a MMD's devices in package registers. -+ * @bus: the target MII bus -+ * @addr: PHY address on the MII bus -+ * @dev_addr: MMD address in the PHY. -+ * @devices_in_package: where to store the devices in package information. -+ * -+ * Description: reads devices in package registers of a MMD at @dev_addr -+ * from PHY at @addr on @bus. -+ * -+ * Returns: 0 on success, -EIO on failure. -+ */ -+static int get_phy_c45_devs_in_pkg(struct mii_bus *bus, int addr, int dev_addr, -+ u32 *devices_in_package) -+{ -+ int phy_reg, reg_addr; -+ -+ reg_addr = MII_ADDR_C45 | dev_addr << 16 | MDIO_DEVS2; -+ phy_reg = mdiobus_read(bus, addr, reg_addr); -+ if (phy_reg < 0) -+ return -EIO; -+ *devices_in_package = (phy_reg & 0xffff) << 16; -+ -+ reg_addr = MII_ADDR_C45 | dev_addr << 16 | MDIO_DEVS1; -+ phy_reg = mdiobus_read(bus, addr, reg_addr); -+ if (phy_reg < 0) -+ return -EIO; -+ *devices_in_package |= (phy_reg & 0xffff); -+ -+ return 0; -+} -+ - /** - * get_phy_c45_ids - reads the specified addr for its 802.3-c45 IDs. - * @bus: the target MII bus -@@ -223,31 +254,32 @@ static int get_phy_c45_ids(struct mii_bus *bus, int addr, u32 *phy_id, - int phy_reg; - int i, reg_addr; - const int num_ids = ARRAY_SIZE(c45_ids->device_ids); -+ u32 *devs = &c45_ids->devices_in_package; - -- /* Find first non-zero Devices In package. Device -- * zero is reserved, so don't probe it. -+ /* Find first non-zero Devices In package. Device zero is reserved -+ * for 802.3 c45 complied PHYs, so don't probe it at first. - */ -- for (i = 1; -- i < num_ids && c45_ids->devices_in_package == 0; -- i++) { -- reg_addr = MII_ADDR_C45 | i << 16 | MDIO_DEVS2; -- phy_reg = mdiobus_read(bus, addr, reg_addr); -- if (phy_reg < 0) -- return -EIO; -- c45_ids->devices_in_package = (phy_reg & 0xffff) << 16; -- -- reg_addr = MII_ADDR_C45 | i << 16 | MDIO_DEVS1; -- phy_reg = mdiobus_read(bus, addr, reg_addr); -+ for (i = 1; i < num_ids && *devs == 0; i++) { -+ phy_reg = get_phy_c45_devs_in_pkg(bus, addr, i, devs); - if (phy_reg < 0) - return -EIO; -- c45_ids->devices_in_package |= (phy_reg & 0xffff); - -- /* If mostly Fs, there is no device there, -- * let's get out of here. -- */ -- if ((c45_ids->devices_in_package & 0x1fffffff) == 0x1fffffff) { -- *phy_id = 0xffffffff; -- return 0; -+ if ((*devs & 0x1fffffff) == 0x1fffffff) { -+ /* If mostly Fs, there is no device there, -+ * then let's continue to probe more, as some -+ * 10G PHYs have zero Devices In package, -+ * e.g. Cortina CS4315/CS4340 PHY. -+ */ -+ phy_reg = get_phy_c45_devs_in_pkg(bus, addr, 0, devs); -+ if (phy_reg < 0) -+ return -EIO; -+ /* no device there, let's get out of here */ -+ if ((*devs & 0x1fffffff) == 0x1fffffff) { -+ *phy_id = 0xffffffff; -+ return 0; -+ } else { -+ break; -+ } - } - } - -@@ -376,6 +408,24 @@ int phy_device_register(struct phy_device *phydev) - EXPORT_SYMBOL(phy_device_register); - - /** -+ * phy_device_remove - Remove a previously registered phy device from the MDIO bus -+ * @phydev: phy_device structure to remove -+ * -+ * This doesn't free the phy_device itself, it merely reverses the effects -+ * of phy_device_register(). Use phy_device_free() to free the device -+ * after calling this function. -+ */ -+void phy_device_remove(struct phy_device *phydev) -+{ -+ struct mii_bus *bus = phydev->bus; -+ int addr = phydev->addr; -+ -+ device_del(&phydev->dev); -+ bus->phy_map[addr] = NULL; -+} -+EXPORT_SYMBOL(phy_device_remove); -+ -+/** - * phy_find_first - finds the first PHY device on the bus - * @bus: the target MII bus - */ -diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c -index 45483fd..badcf24 100644 ---- a/drivers/net/phy/realtek.c -+++ b/drivers/net/phy/realtek.c -@@ -22,8 +22,12 @@ - #define RTL821x_INER 0x12 - #define RTL821x_INER_INIT 0x6400 - #define RTL821x_INSR 0x13 -+#define RTL8211E_INER_LINK_STATUS 0x400 - --#define RTL8211E_INER_LINK_STATUS 0x400 -+#define RTL8211F_INER_LINK_STATUS 0x0010 -+#define RTL8211F_INSR 0x1d -+#define RTL8211F_PAGE_SELECT 0x1f -+#define RTL8211F_TX_DELAY 0x100 - - MODULE_DESCRIPTION("Realtek PHY driver"); - MODULE_AUTHOR("Johnson Leung"); -@@ -38,6 +42,18 @@ static int rtl821x_ack_interrupt(struct phy_device *phydev) - return (err < 0) ? err : 0; - } - -+static int rtl8211f_ack_interrupt(struct phy_device *phydev) -+{ -+ int err; -+ -+ phy_write(phydev, RTL8211F_PAGE_SELECT, 0xa43); -+ err = phy_read(phydev, RTL8211F_INSR); -+ /* restore to default page 0 */ -+ phy_write(phydev, RTL8211F_PAGE_SELECT, 0x0); -+ -+ return (err < 0) ? err : 0; -+} -+ - static int rtl8211b_config_intr(struct phy_device *phydev) - { - int err; -@@ -64,6 +80,41 @@ static int rtl8211e_config_intr(struct phy_device *phydev) - return err; - } - -+static int rtl8211f_config_intr(struct phy_device *phydev) -+{ -+ int err; -+ -+ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) -+ err = phy_write(phydev, RTL821x_INER, -+ RTL8211F_INER_LINK_STATUS); -+ else -+ err = phy_write(phydev, RTL821x_INER, 0); -+ -+ return err; -+} -+ -+static int rtl8211f_config_init(struct phy_device *phydev) -+{ -+ int ret; -+ u16 reg; -+ -+ ret = genphy_config_init(phydev); -+ if (ret < 0) -+ return ret; -+ -+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII) { -+ /* enable TXDLY */ -+ phy_write(phydev, RTL8211F_PAGE_SELECT, 0xd08); -+ reg = phy_read(phydev, 0x11); -+ reg |= RTL8211F_TX_DELAY; -+ phy_write(phydev, 0x11, reg); -+ /* restore to default page 0 */ -+ phy_write(phydev, RTL8211F_PAGE_SELECT, 0x0); -+ } -+ -+ return 0; -+} -+ - static struct phy_driver realtek_drvs[] = { - { - .phy_id = 0x00008201, -@@ -86,6 +137,19 @@ static struct phy_driver realtek_drvs[] = { - .config_intr = &rtl8211b_config_intr, - .driver = { .owner = THIS_MODULE,}, - }, { -+ .phy_id = 0x001cc914, -+ .name = "RTL8211DN Gigabit Ethernet", -+ .phy_id_mask = 0x001fffff, -+ .features = PHY_GBIT_FEATURES, -+ .flags = PHY_HAS_INTERRUPT, -+ .config_aneg = genphy_config_aneg, -+ .read_status = genphy_read_status, -+ .ack_interrupt = rtl821x_ack_interrupt, -+ .config_intr = rtl8211e_config_intr, -+ .suspend = genphy_suspend, -+ .resume = genphy_resume, -+ .driver = { .owner = THIS_MODULE,}, -+ }, { - .phy_id = 0x001cc915, - .name = "RTL8211E Gigabit Ethernet", - .phy_id_mask = 0x001fffff, -@@ -98,6 +162,20 @@ static struct phy_driver realtek_drvs[] = { - .suspend = genphy_suspend, - .resume = genphy_resume, - .driver = { .owner = THIS_MODULE,}, -+ }, { -+ .phy_id = 0x001cc916, -+ .name = "RTL8211F Gigabit Ethernet", -+ .phy_id_mask = 0x001fffff, -+ .features = PHY_GBIT_FEATURES, -+ .flags = PHY_HAS_INTERRUPT, -+ .config_aneg = &genphy_config_aneg, -+ .config_init = &rtl8211f_config_init, -+ .read_status = &genphy_read_status, -+ .ack_interrupt = &rtl8211f_ack_interrupt, -+ .config_intr = &rtl8211f_config_intr, -+ .suspend = genphy_suspend, -+ .resume = genphy_resume, -+ .driver = { .owner = THIS_MODULE }, - }, - }; - -@@ -116,7 +194,9 @@ module_exit(realtek_exit); - - static struct mdio_device_id __maybe_unused realtek_tbl[] = { - { 0x001cc912, 0x001fffff }, -+ { 0x001cc914, 0x001fffff }, - { 0x001cc915, 0x001fffff }, -+ { 0x001cc916, 0x001fffff }, - { } - }; - -diff --git a/drivers/of/base.c b/drivers/of/base.c -index 469d2b7..210c876 100644 ---- a/drivers/of/base.c -+++ b/drivers/of/base.c -@@ -32,8 +32,8 @@ - - LIST_HEAD(aliases_lookup); - --struct device_node *of_allnodes; --EXPORT_SYMBOL(of_allnodes); -+struct device_node *of_root; -+EXPORT_SYMBOL(of_root); - struct device_node *of_chosen; - struct device_node *of_aliases; - struct device_node *of_stdout; -@@ -48,7 +48,7 @@ struct kset *of_kset; - */ - DEFINE_MUTEX(of_mutex); - --/* use when traversing tree through the allnext, child, sibling, -+/* use when traversing tree through the child, sibling, - * or parent members of struct device_node. - */ - DEFINE_RAW_SPINLOCK(devtree_lock); -@@ -204,7 +204,7 @@ static int __init of_init(void) - mutex_unlock(&of_mutex); - - /* Symlink in /proc as required by userspace ABI */ -- if (of_allnodes) -+ if (of_root) - proc_symlink("device-tree", NULL, "/sys/firmware/devicetree/base"); - - return 0; -@@ -245,6 +245,23 @@ struct property *of_find_property(const struct device_node *np, - } - EXPORT_SYMBOL(of_find_property); - -+struct device_node *__of_find_all_nodes(struct device_node *prev) -+{ -+ struct device_node *np; -+ if (!prev) { -+ np = of_root; -+ } else if (prev->child) { -+ np = prev->child; -+ } else { -+ /* Walk back up looking for a sibling, or the end of the structure */ -+ np = prev; -+ while (np->parent && !np->sibling) -+ np = np->parent; -+ np = np->sibling; /* Might be null at the end of the tree */ -+ } -+ return np; -+} -+ - /** - * of_find_all_nodes - Get next node in global list - * @prev: Previous node or NULL to start iteration -@@ -259,10 +276,8 @@ struct device_node *of_find_all_nodes(struct device_node *prev) - unsigned long flags; - - raw_spin_lock_irqsave(&devtree_lock, flags); -- np = prev ? prev->allnext : of_allnodes; -- for (; np != NULL; np = np->allnext) -- if (of_node_get(np)) -- break; -+ np = __of_find_all_nodes(prev); -+ of_node_get(np); - of_node_put(prev); - raw_spin_unlock_irqrestore(&devtree_lock, flags); - return np; -@@ -736,7 +751,7 @@ struct device_node *of_find_node_by_path(const char *path) - unsigned long flags; - - if (strcmp(path, "/") == 0) -- return of_node_get(of_allnodes); -+ return of_node_get(of_root); - - /* The path could begin with an alias */ - if (*path != '/') { -@@ -761,7 +776,7 @@ struct device_node *of_find_node_by_path(const char *path) - /* Step down the tree matching path components */ - raw_spin_lock_irqsave(&devtree_lock, flags); - if (!np) -- np = of_node_get(of_allnodes); -+ np = of_node_get(of_root); - while (np && *path == '/') { - path++; /* Increment past '/' delimiter */ - np = __of_find_node_by_path(np, path); -@@ -790,8 +805,7 @@ struct device_node *of_find_node_by_name(struct device_node *from, - unsigned long flags; - - raw_spin_lock_irqsave(&devtree_lock, flags); -- np = from ? from->allnext : of_allnodes; -- for (; np; np = np->allnext) -+ for_each_of_allnodes_from(from, np) - if (np->name && (of_node_cmp(np->name, name) == 0) - && of_node_get(np)) - break; -@@ -820,8 +834,7 @@ struct device_node *of_find_node_by_type(struct device_node *from, - unsigned long flags; - - raw_spin_lock_irqsave(&devtree_lock, flags); -- np = from ? from->allnext : of_allnodes; -- for (; np; np = np->allnext) -+ for_each_of_allnodes_from(from, np) - if (np->type && (of_node_cmp(np->type, type) == 0) - && of_node_get(np)) - break; -@@ -852,12 +865,10 @@ struct device_node *of_find_compatible_node(struct device_node *from, - unsigned long flags; - - raw_spin_lock_irqsave(&devtree_lock, flags); -- np = from ? from->allnext : of_allnodes; -- for (; np; np = np->allnext) { -+ for_each_of_allnodes_from(from, np) - if (__of_device_is_compatible(np, compatible, type, NULL) && - of_node_get(np)) - break; -- } - of_node_put(from); - raw_spin_unlock_irqrestore(&devtree_lock, flags); - return np; -@@ -884,8 +895,7 @@ struct device_node *of_find_node_with_property(struct device_node *from, - unsigned long flags; - - raw_spin_lock_irqsave(&devtree_lock, flags); -- np = from ? from->allnext : of_allnodes; -- for (; np; np = np->allnext) { -+ for_each_of_allnodes_from(from, np) { - for (pp = np->properties; pp; pp = pp->next) { - if (of_prop_cmp(pp->name, prop_name) == 0) { - of_node_get(np); -@@ -967,8 +977,7 @@ struct device_node *of_find_matching_node_and_match(struct device_node *from, - *match = NULL; - - raw_spin_lock_irqsave(&devtree_lock, flags); -- np = from ? from->allnext : of_allnodes; -- for (; np; np = np->allnext) { -+ for_each_of_allnodes_from(from, np) { - m = __of_match_node(matches, np); - if (m && of_node_get(np)) { - if (match) -@@ -1025,7 +1034,7 @@ struct device_node *of_find_node_by_phandle(phandle handle) - return NULL; - - raw_spin_lock_irqsave(&devtree_lock, flags); -- for (np = of_allnodes; np; np = np->allnext) -+ for_each_of_allnodes(np) - if (np->phandle == handle) - break; - of_node_get(np); -diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c -index d499417..d43f305 100644 ---- a/drivers/of/dynamic.c -+++ b/drivers/of/dynamic.c -@@ -117,8 +117,6 @@ void __of_attach_node(struct device_node *np) - - np->child = NULL; - np->sibling = np->parent->child; -- np->allnext = np->parent->allnext; -- np->parent->allnext = np; - np->parent->child = np; - of_node_clear_flag(np, OF_DETACHED); - } -@@ -154,17 +152,6 @@ void __of_detach_node(struct device_node *np) - if (WARN_ON(!parent)) - return; - -- if (of_allnodes == np) -- of_allnodes = np->allnext; -- else { -- struct device_node *prev; -- for (prev = of_allnodes; -- prev->allnext != np; -- prev = prev->allnext) -- ; -- prev->allnext = np->allnext; -- } -- - if (parent->child == np) - parent->child = np->sibling; - else { -diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c -index d134710..f6eda02 100644 ---- a/drivers/of/fdt.c -+++ b/drivers/of/fdt.c -@@ -145,15 +145,15 @@ static void *unflatten_dt_alloc(void **mem, unsigned long size, - * @mem: Memory chunk to use for allocating device nodes and properties - * @p: pointer to node in flat tree - * @dad: Parent struct device_node -- * @allnextpp: pointer to ->allnext from last allocated device_node - * @fpsize: Size of the node path up at the current depth. - */ - static void * unflatten_dt_node(void *blob, - void *mem, - int *poffset, - struct device_node *dad, -- struct device_node ***allnextpp, -- unsigned long fpsize) -+ struct device_node **nodepp, -+ unsigned long fpsize, -+ bool dryrun) - { - const __be32 *p; - struct device_node *np; -@@ -200,7 +200,7 @@ static void * unflatten_dt_node(void *blob, - - np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl, - __alignof__(struct device_node)); -- if (allnextpp) { -+ if (!dryrun) { - char *fn; - of_node_init(np); - np->full_name = fn = ((char *)np) + sizeof(*np); -@@ -222,8 +222,6 @@ static void * unflatten_dt_node(void *blob, - memcpy(fn, pathp, l); - - prev_pp = &np->properties; -- **allnextpp = np; -- *allnextpp = &np->allnext; - if (dad != NULL) { - np->parent = dad; - /* we temporarily use the next field as `last_child'*/ -@@ -254,7 +252,7 @@ static void * unflatten_dt_node(void *blob, - has_name = 1; - pp = unflatten_dt_alloc(&mem, sizeof(struct property), - __alignof__(struct property)); -- if (allnextpp) { -+ if (!dryrun) { - /* We accept flattened tree phandles either in - * ePAPR-style "phandle" properties, or the - * legacy "linux,phandle" properties. If both -@@ -296,7 +294,7 @@ static void * unflatten_dt_node(void *blob, - sz = (pa - ps) + 1; - pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz, - __alignof__(struct property)); -- if (allnextpp) { -+ if (!dryrun) { - pp->name = "name"; - pp->length = sz; - pp->value = pp + 1; -@@ -308,7 +306,7 @@ static void * unflatten_dt_node(void *blob, - (char *)pp->value); - } - } -- if (allnextpp) { -+ if (!dryrun) { - *prev_pp = NULL; - np->name = of_get_property(np, "name", NULL); - np->type = of_get_property(np, "device_type", NULL); -@@ -324,11 +322,13 @@ static void * unflatten_dt_node(void *blob, - if (depth < 0) - depth = 0; - while (*poffset > 0 && depth > old_depth) -- mem = unflatten_dt_node(blob, mem, poffset, np, allnextpp, -- fpsize); -+ mem = unflatten_dt_node(blob, mem, poffset, np, NULL, -+ fpsize, dryrun); - - if (*poffset < 0 && *poffset != -FDT_ERR_NOTFOUND) - pr_err("unflatten: error %d processing FDT\n", *poffset); -+ if (nodepp) -+ *nodepp = np; - - return mem; - } -@@ -352,7 +352,6 @@ static void __unflatten_device_tree(void *blob, - unsigned long size; - int start; - void *mem; -- struct device_node **allnextp = mynodes; - - pr_debug(" -> unflatten_device_tree()\n"); - -@@ -373,7 +372,7 @@ static void __unflatten_device_tree(void *blob, - - /* First pass, scan for size */ - start = 0; -- size = (unsigned long)unflatten_dt_node(blob, NULL, &start, NULL, NULL, 0); -+ size = (unsigned long)unflatten_dt_node(blob, NULL, &start, NULL, NULL, 0, true); - size = ALIGN(size, 4); - - pr_debug(" size is %lx, allocating...\n", size); -@@ -388,11 +387,10 @@ static void __unflatten_device_tree(void *blob, - - /* Second pass, do actual unflattening */ - start = 0; -- unflatten_dt_node(blob, mem, &start, NULL, &allnextp, 0); -+ unflatten_dt_node(blob, mem, &start, NULL, mynodes, 0, false); - if (be32_to_cpup(mem + size) != 0xdeadbeef) - pr_warning("End of tree marker overwritten: %08x\n", - be32_to_cpup(mem + size)); -- *allnextp = NULL; - - pr_debug(" <- unflatten_device_tree()\n"); - } -@@ -1039,7 +1037,7 @@ bool __init early_init_dt_scan(void *params) - */ - void __init unflatten_device_tree(void) - { -- __unflatten_device_tree(initial_boot_params, &of_allnodes, -+ __unflatten_device_tree(initial_boot_params, &of_root, - early_init_dt_alloc_memory_arch); - - /* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */ -diff --git a/drivers/of/pdt.c b/drivers/of/pdt.c -index 36b4035..d2acae8 100644 ---- a/drivers/of/pdt.c -+++ b/drivers/of/pdt.c -@@ -25,8 +25,7 @@ - - static struct of_pdt_ops *of_pdt_prom_ops __initdata; - --void __initdata (*of_pdt_build_more)(struct device_node *dp, -- struct device_node ***nextp); -+void __initdata (*of_pdt_build_more)(struct device_node *dp); - - #if defined(CONFIG_SPARC) - unsigned int of_pdt_unique_id __initdata; -@@ -192,8 +191,7 @@ static struct device_node * __init of_pdt_create_node(phandle node, - } - - static struct device_node * __init of_pdt_build_tree(struct device_node *parent, -- phandle node, -- struct device_node ***nextp) -+ phandle node) - { - struct device_node *ret = NULL, *prev_sibling = NULL; - struct device_node *dp; -@@ -210,16 +208,12 @@ static struct device_node * __init of_pdt_build_tree(struct device_node *parent, - ret = dp; - prev_sibling = dp; - -- *(*nextp) = dp; -- *nextp = &dp->allnext; -- - dp->full_name = of_pdt_build_full_name(dp); - -- dp->child = of_pdt_build_tree(dp, -- of_pdt_prom_ops->getchild(node), nextp); -+ dp->child = of_pdt_build_tree(dp, of_pdt_prom_ops->getchild(node)); - - if (of_pdt_build_more) -- of_pdt_build_more(dp, nextp); -+ of_pdt_build_more(dp); - - node = of_pdt_prom_ops->getsibling(node); - } -@@ -234,20 +228,17 @@ static void * __init kernel_tree_alloc(u64 size, u64 align) - - void __init of_pdt_build_devicetree(phandle root_node, struct of_pdt_ops *ops) - { -- struct device_node **nextp; -- - BUG_ON(!ops); - of_pdt_prom_ops = ops; - -- of_allnodes = of_pdt_create_node(root_node, NULL); -+ of_root = of_pdt_create_node(root_node, NULL); - #if defined(CONFIG_SPARC) -- of_allnodes->path_component_name = ""; -+ of_root->path_component_name = ""; - #endif -- of_allnodes->full_name = "/"; -+ of_root->full_name = "/"; - -- nextp = &of_allnodes->allnext; -- of_allnodes->child = of_pdt_build_tree(of_allnodes, -- of_pdt_prom_ops->getchild(of_allnodes->phandle), &nextp); -+ of_root->child = of_pdt_build_tree(of_root, -+ of_pdt_prom_ops->getchild(of_root->phandle)); - - /* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */ - of_alias_scan(kernel_tree_alloc); -diff --git a/drivers/of/selftest.c b/drivers/of/selftest.c -index e2d79af..e40089e 100644 ---- a/drivers/of/selftest.c -+++ b/drivers/of/selftest.c -@@ -148,7 +148,7 @@ static void __init of_selftest_dynamic(void) - - static int __init of_selftest_check_node_linkage(struct device_node *np) - { -- struct device_node *child, *allnext_index = np; -+ struct device_node *child; - int count = 0, rc; - - for_each_child_of_node(np, child) { -@@ -158,14 +158,6 @@ static int __init of_selftest_check_node_linkage(struct device_node *np) - return -EINVAL; - } - -- while (allnext_index && allnext_index != child) -- allnext_index = allnext_index->allnext; -- if (allnext_index != child) { -- pr_err("Node %s is ordered differently in sibling and allnode lists\n", -- child->name); -- return -EINVAL; -- } -- - rc = of_selftest_check_node_linkage(child); - if (rc < 0) - return rc; -@@ -180,12 +172,12 @@ static void __init of_selftest_check_tree_linkage(void) - struct device_node *np; - int allnode_count = 0, child_count; - -- if (!of_allnodes) -+ if (!of_root) - return; - - for_each_of_allnodes(np) - allnode_count++; -- child_count = of_selftest_check_node_linkage(of_allnodes); -+ child_count = of_selftest_check_node_linkage(of_root); - - selftest(child_count > 0, "Device node data structure is corrupted\n"); - selftest(child_count == allnode_count, "allnodes list size (%i) doesn't match" -@@ -775,33 +767,29 @@ static void update_node_properties(struct device_node *np, - */ - static int attach_node_and_children(struct device_node *np) - { -- struct device_node *next, *root = np, *dup; -+ struct device_node *next, *dup, *child; - -- /* skip root node */ -- np = np->child; -- /* storing a copy in temporary node */ -- dup = np; -+ dup = of_find_node_by_path(np->full_name); -+ if (dup) { -+ update_node_properties(np, dup); -+ return 0; -+ } - -- while (dup) { -+ /* Children of the root need to be remembered for removal */ -+ if (np->parent == of_root) { - if (WARN_ON(last_node_index >= NO_OF_NODES)) - return -EINVAL; -- nodes[last_node_index++] = dup; -- dup = dup->sibling; -+ nodes[last_node_index++] = np; - } -- dup = NULL; - -- while (np) { -- next = np->allnext; -- dup = of_find_node_by_path(np->full_name); -- if (dup) -- update_node_properties(np, dup); -- else { -- np->child = NULL; -- if (np->parent == root) -- np->parent = of_allnodes; -- of_attach_node(np); -- } -- np = next; -+ child = np->child; -+ np->child = NULL; -+ np->sibling = NULL; -+ of_attach_node(np); -+ while (child) { -+ next = child->sibling; -+ attach_node_and_children(child); -+ child = next; - } - - return 0; -@@ -846,10 +834,10 @@ static int __init selftest_data_add(void) - return -EINVAL; - } - -- if (!of_allnodes) { -+ if (!of_root) { - /* enabling flag for removing nodes */ - selftest_live_tree = true; -- of_allnodes = selftest_data_node; -+ of_root = selftest_data_node; - - for_each_of_allnodes(np) - __of_attach_node_sysfs(np); -@@ -859,7 +847,14 @@ static int __init selftest_data_add(void) - } - - /* attach the sub-tree to live tree */ -- return attach_node_and_children(selftest_data_node); -+ np = selftest_data_node->child; -+ while (np) { -+ struct device_node *next = np->sibling; -+ np->parent = of_root; -+ attach_node_and_children(np); -+ np = next; -+ } -+ return 0; - } - - /** -@@ -889,10 +884,10 @@ static void selftest_data_remove(void) - of_node_put(of_chosen); - of_aliases = NULL; - of_chosen = NULL; -- for_each_child_of_node(of_allnodes, np) -+ for_each_child_of_node(of_root, np) - detach_node_and_children(np); -- __of_detach_node_sysfs(of_allnodes); -- of_allnodes = NULL; -+ __of_detach_node_sysfs(of_root); -+ of_root = NULL; - return; - } - -diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile -index e04fe2d..e9815ac 100644 ---- a/drivers/pci/Makefile -+++ b/drivers/pci/Makefile -@@ -35,6 +35,7 @@ obj-$(CONFIG_PCI_IOV) += iov.o - # - obj-$(CONFIG_ALPHA) += setup-irq.o - obj-$(CONFIG_ARM) += setup-irq.o -+obj-$(CONFIG_ARM64) += setup-irq.o - obj-$(CONFIG_UNICORE32) += setup-irq.o - obj-$(CONFIG_SUPERH) += setup-irq.o - obj-$(CONFIG_MIPS) += setup-irq.o -diff --git a/drivers/pci/access.c b/drivers/pci/access.c -index 7f249b9..b965c12 100644 ---- a/drivers/pci/access.c -+++ b/drivers/pci/access.c -@@ -67,6 +67,93 @@ EXPORT_SYMBOL(pci_bus_write_config_byte); - EXPORT_SYMBOL(pci_bus_write_config_word); - EXPORT_SYMBOL(pci_bus_write_config_dword); - -+int pci_generic_config_read(struct pci_bus *bus, unsigned int devfn, -+ int where, int size, u32 *val) -+{ -+ void __iomem *addr; -+ -+ addr = bus->ops->map_bus(bus, devfn, where); -+ if (!addr) { -+ *val = ~0; -+ return PCIBIOS_DEVICE_NOT_FOUND; -+ } -+ -+ if (size == 1) -+ *val = readb(addr); -+ else if (size == 2) -+ *val = readw(addr); -+ else -+ *val = readl(addr); -+ -+ return PCIBIOS_SUCCESSFUL; -+} -+EXPORT_SYMBOL_GPL(pci_generic_config_read); -+ -+int pci_generic_config_write(struct pci_bus *bus, unsigned int devfn, -+ int where, int size, u32 val) -+{ -+ void __iomem *addr; -+ -+ addr = bus->ops->map_bus(bus, devfn, where); -+ if (!addr) -+ return PCIBIOS_DEVICE_NOT_FOUND; -+ -+ if (size == 1) -+ writeb(val, addr); -+ else if (size == 2) -+ writew(val, addr); -+ else -+ writel(val, addr); -+ -+ return PCIBIOS_SUCCESSFUL; -+} -+EXPORT_SYMBOL_GPL(pci_generic_config_write); -+ -+int pci_generic_config_read32(struct pci_bus *bus, unsigned int devfn, -+ int where, int size, u32 *val) -+{ -+ void __iomem *addr; -+ -+ addr = bus->ops->map_bus(bus, devfn, where & ~0x3); -+ if (!addr) { -+ *val = ~0; -+ return PCIBIOS_DEVICE_NOT_FOUND; -+ } -+ -+ *val = readl(addr); -+ -+ if (size <= 2) -+ *val = (*val >> (8 * (where & 3))) & ((1 << (size * 8)) - 1); -+ -+ return PCIBIOS_SUCCESSFUL; -+} -+EXPORT_SYMBOL_GPL(pci_generic_config_read32); -+ -+int pci_generic_config_write32(struct pci_bus *bus, unsigned int devfn, -+ int where, int size, u32 val) -+{ -+ void __iomem *addr; -+ u32 mask, tmp; -+ -+ addr = bus->ops->map_bus(bus, devfn, where & ~0x3); -+ if (!addr) -+ return PCIBIOS_DEVICE_NOT_FOUND; -+ -+ if (size == 4) { -+ writel(val, addr); -+ return PCIBIOS_SUCCESSFUL; -+ } else { -+ mask = ~(((1 << (size * 8)) - 1) << ((where & 0x3) * 8)); -+ } -+ -+ tmp = readl(addr) & mask; -+ tmp |= val << ((where & 0x3) * 8); -+ writel(tmp, addr); -+ -+ return PCIBIOS_SUCCESSFUL; -+} -+EXPORT_SYMBOL_GPL(pci_generic_config_write32); -+ - /** - * pci_bus_set_ops - Set raw operations of pci bus - * @bus: pci bus struct -diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig -index 96586b1..dafa3dc 100644 ---- a/drivers/pci/host/Kconfig -+++ b/drivers/pci/host/Kconfig -@@ -50,7 +50,7 @@ config PCI_RCAR_GEN2_PCIE - - config PCI_HOST_GENERIC - bool "Generic PCI host controller" -- depends on ARM && OF -+ depends on (ARM || ARM64) && OF - help - Say Y here if you want to support a simple generic PCI host - controller, such as the one emulated by kvmtool. -diff --git a/drivers/pci/host/pci-layerscape.c b/drivers/pci/host/pci-layerscape.c -index d491b0f..baa1232 100644 ---- a/drivers/pci/host/pci-layerscape.c -+++ b/drivers/pci/host/pci-layerscape.c -@@ -36,12 +36,21 @@ - #define LTSSM_PCIE_L0 0x11 /* L0 state */ - #define LTSSM_PCIE_L2_IDLE 0x15 /* L2 idle state */ - -+#define PCIE_SRIOV_OFFSET 0x178 -+ -+/* CS2 */ -+#define PCIE_CS2_OFFSET 0x1000 /* For PCIe without SR-IOV */ -+#define PCIE_ENABLE_CS2 0x80000000 /* For PCIe with SR-IOV */ -+ - /* PEX Internal Configuration Registers */ - #define PCIE_STRFMR1 0x71c /* Symbol Timer & Filter Mask Register1 */ - #define PCIE_DBI_RO_WR_EN 0x8bc /* DBI Read-Only Write Enable Register */ -+#define PCIE_ABSERR 0x8d0 /* Bridge Slave Error Response Register */ -+#define PCIE_ABSERR_SETTING 0x9401 /* Forward error of non-posted request */ - - /* PEX LUT registers */ - #define PCIE_LUT_DBG 0x7FC /* PEX LUT Debug Register */ -+#define PCIE_LUT_CTRL0 0x7f8 - #define PCIE_LUT_UDR(n) (0x800 + (n) * 8) - #define PCIE_LUT_LDR(n) (0x804 + (n) * 8) - #define PCIE_LUT_MASK_ALL 0xffff -@@ -72,6 +81,8 @@ - #define CPLD_RST_PCIE_SLOT 0x14 - #define CPLD_RST_PCIESLOT 0x3 - -+#define PCIE_IATU_NUM 6 -+ - struct ls_pcie; - - struct ls_pcie_pm_data { -@@ -111,6 +122,8 @@ struct ls_pcie { - - #define to_ls_pcie(x) container_of(x, struct ls_pcie, pp) - -+static void ls_pcie_host_init(struct pcie_port *pp); -+ - u32 set_pcie_streamid_translation(struct pci_dev *pdev, u32 devid) - { - u32 index, streamid; -@@ -163,6 +176,42 @@ static void ls_pcie_drop_msg_tlp(struct ls_pcie *pcie) - iowrite32(val, pcie->dbi + PCIE_STRFMR1); - } - -+/* Disable all bars in RC mode */ -+static void ls_pcie_disable_bars(struct ls_pcie *pcie) -+{ -+ u32 header; -+ -+ header = ioread32(pcie->dbi + PCIE_SRIOV_OFFSET); -+ if (PCI_EXT_CAP_ID(header) == PCI_EXT_CAP_ID_SRIOV) { -+ iowrite32(PCIE_ENABLE_CS2, pcie->lut + PCIE_LUT_CTRL0); -+ iowrite32(0, pcie->dbi + PCI_BASE_ADDRESS_0); -+ iowrite32(0, pcie->dbi + PCI_BASE_ADDRESS_1); -+ iowrite32(0, pcie->dbi + PCI_ROM_ADDRESS1); -+ iowrite32(0, pcie->lut + PCIE_LUT_CTRL0); -+ } else { -+ iowrite32(0, -+ pcie->dbi + PCIE_CS2_OFFSET + PCI_BASE_ADDRESS_0); -+ iowrite32(0, -+ pcie->dbi + PCIE_CS2_OFFSET + PCI_BASE_ADDRESS_1); -+ iowrite32(0, -+ pcie->dbi + PCIE_CS2_OFFSET + PCI_ROM_ADDRESS1); -+ } -+} -+ -+static void ls_pcie_disable_outbound_atus(struct ls_pcie *pcie) -+{ -+ int i; -+ -+ for (i = 0; i < PCIE_IATU_NUM; i++) -+ dw_pcie_disable_outbound_atu(&pcie->pp, i); -+} -+ -+/* Forward error response of outbound non-posted requests */ -+static void ls_pcie_fix_error_response(struct ls_pcie *pcie) -+{ -+ iowrite32(PCIE_ABSERR_SETTING, pcie->dbi + PCIE_ABSERR); -+} -+ - static int ls1021_pcie_link_up(struct pcie_port *pp) - { - u32 state; -@@ -272,19 +321,24 @@ static void ls1021_pcie_host_init(struct pcie_port *pp) - } - pcie->index = index[1]; - -- dw_pcie_setup_rc(pp); -+ ls_pcie_host_init(pp); - -- ls_pcie_drop_msg_tlp(pcie); -+ dw_pcie_setup_rc(pp); - } - - static int ls_pcie_link_up(struct pcie_port *pp) - { - struct ls_pcie *pcie = to_ls_pcie(pp); -- u32 state; -+ u32 state, offset; - -- state = (ioread32(pcie->lut + PCIE_LUT_DBG) >> -- pcie->drvdata->ltssm_shift) & -- LTSSM_STATE_MASK; -+ if (of_get_property(pp->dev->of_node, "fsl,lut_diff", NULL)) -+ offset = 0x407fc; -+ else -+ offset = PCIE_LUT_DBG; -+ -+ state = (ioread32(pcie->lut + offset) >> -+ pcie->drvdata->ltssm_shift) & -+ LTSSM_STATE_MASK; - - if (state < LTSSM_PCIE_L0) - return 0; -@@ -308,6 +362,10 @@ static void ls_pcie_host_init(struct pcie_port *pp) - ls_pcie_clear_multifunction(pcie); - ls_pcie_drop_msg_tlp(pcie); - iowrite32(0, pcie->dbi + PCIE_DBI_RO_WR_EN); -+ -+ ls_pcie_disable_bars(pcie); -+ ls_pcie_disable_outbound_atus(pcie); -+ ls_pcie_fix_error_response(pcie); - } - - static int ls_pcie_msi_host_init(struct pcie_port *pp, -@@ -426,6 +484,11 @@ static int ls_pcie_host_pme_init(struct ls_pcie *pcie, - - pp = &pcie->pp; - -+ if (dw_pcie_link_up(&pcie->pp)) -+ pcie->in_slot = true; -+ else -+ pcie->in_slot = false; -+ - pcie->pme_irq = platform_get_irq_byname(pdev, "pme"); - if (pcie->pme_irq < 0) { - dev_err(&pdev->dev, -@@ -462,11 +525,6 @@ static int ls_pcie_host_pme_init(struct ls_pcie *pcie, - val |= PCIE_PEX_RCR_PMEIE; - iowrite16(val, pcie->dbi + PCIE_PEX_RCR); - -- if (dw_pcie_link_up(&pcie->pp)) -- pcie->in_slot = true; -- else -- pcie->in_slot = false; -- - return 0; - } - -@@ -590,12 +648,14 @@ static int ls_pcie_pm_do_resume(struct ls_pcie *pcie) - u32 state; - int i = 0; - u16 val; -- -- ls_pcie_host_init(&pcie->pp); -+ struct pcie_port *pp = &pcie->pp; - - if (!pcie->in_slot) - return 0; - -+ dw_pcie_setup_rc(pp); -+ ls_pcie_host_init(pp); -+ - /* Put RC in D0 */ - val = ioread16(pcie->dbi + PCIE_PM_SCR); - val &= PCIE_PM_SCR_PMEPS_D0; -diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c -index 8a9241b..0961ffc 100644 ---- a/drivers/pci/host/pcie-designware.c -+++ b/drivers/pci/host/pcie-designware.c -@@ -159,6 +159,13 @@ static void dw_pcie_prog_outbound_atu(struct pcie_port *pp, int index, - dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2); - } - -+void dw_pcie_disable_outbound_atu(struct pcie_port *pp, int index) -+{ -+ dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | index, -+ PCIE_ATU_VIEWPORT); -+ dw_pcie_writel_rc(pp, 0, PCIE_ATU_CR2); -+} -+ - int dw_pcie_link_up(struct pcie_port *pp) - { - if (pp->ops->link_up) -@@ -495,6 +502,13 @@ void dw_pcie_setup_rc(struct pcie_port *pp) - u32 membase; - u32 memlimit; - -+ dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0, -+ PCIE_ATU_TYPE_IO, pp->io_base, -+ pp->io_bus_addr, pp->io_size); -+ dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1, -+ PCIE_ATU_TYPE_MEM, pp->mem_base, -+ pp->mem_bus_addr, pp->mem_size); -+ - /* set the number of lanes */ - dw_pcie_readl_rc(pp, PCIE_PORT_LINK_CONTROL, &val); - val &= ~PORT_LINK_MODE_MASK; -diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h -index 2f01284..fcd6431 100644 ---- a/drivers/pci/host/pcie-designware.h -+++ b/drivers/pci/host/pcie-designware.h -@@ -80,5 +80,6 @@ void dw_pcie_msi_init(struct pcie_port *pp); - int dw_pcie_link_up(struct pcie_port *pp); - void dw_pcie_setup_rc(struct pcie_port *pp); - int dw_pcie_host_init(struct pcie_port *pp); -+void dw_pcie_disable_outbound_atu(struct pcie_port *pp, int index); - - #endif /* _PCIE_DESIGNWARE_H */ -diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c -index 5dd4c96..5e64d37 100644 ---- a/drivers/pci/msi.c -+++ b/drivers/pci/msi.c -@@ -667,11 +667,16 @@ static void __iomem *msix_map_region(struct pci_dev *dev, unsigned nr_entries) - { - resource_size_t phys_addr; - u32 table_offset; -+ unsigned long flags; - u8 bir; - - pci_read_config_dword(dev, dev->msix_cap + PCI_MSIX_TABLE, - &table_offset); - bir = (u8)(table_offset & PCI_MSIX_TABLE_BIR); -+ flags = pci_resource_flags(dev, bir); -+ if (!flags || (flags & IORESOURCE_UNSET)) -+ return NULL; -+ - table_offset &= PCI_MSIX_TABLE_OFFSET; - phys_addr = pci_resource_start(dev, bir) + table_offset; - -diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c -index ce0aa47..a6783a5 100644 ---- a/drivers/pci/pci.c -+++ b/drivers/pci/pci.c -@@ -2467,6 +2467,7 @@ u8 pci_common_swizzle(struct pci_dev *dev, u8 *pinp) - *pinp = pin; - return PCI_SLOT(dev->devfn); - } -+EXPORT_SYMBOL_GPL(pci_common_swizzle); - - /** - * pci_release_region - Release a PCI bar -diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c -index 2f0ce66..95ef171 100644 ---- a/drivers/pci/pcie/portdrv_core.c -+++ b/drivers/pci/pcie/portdrv_core.c -@@ -15,6 +15,7 @@ - #include - #include - #include -+#include - - #include "../pci.h" - #include "portdrv.h" -@@ -199,6 +200,28 @@ static int pcie_port_enable_msix(struct pci_dev *dev, int *vectors, int mask) - static int init_service_irqs(struct pci_dev *dev, int *irqs, int mask) - { - int i, irq = -1; -+ int ret; -+ struct device_node *np = NULL; -+ -+ for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) -+ irqs[i] = 0; -+ -+ if (dev->bus->dev.of_node) -+ np = dev->bus->dev.of_node; -+ -+ /* If root port doesn't support MSI/MSI-X/INTx in RC mode, -+ * request irq for aer -+ */ -+ if (IS_ENABLED(CONFIG_OF_IRQ) && np && -+ (mask & PCIE_PORT_SERVICE_PME)) { -+ ret = of_irq_get_byname(np, "aer"); -+ if (ret > 0) { -+ irqs[PCIE_PORT_SERVICE_AER_SHIFT] = ret; -+ if (dev->irq) -+ irq = dev->irq; -+ goto no_msi; -+ } -+ } - - /* - * If MSI cannot be used for PCIe PME or hotplug, we have to use -@@ -224,11 +247,13 @@ static int init_service_irqs(struct pci_dev *dev, int *irqs, int mask) - irq = dev->irq; - - no_msi: -- for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) -- irqs[i] = irq; -+ for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) { -+ if (!irqs[i]) -+ irqs[i] = irq; -+ } - irqs[PCIE_PORT_SERVICE_VC_SHIFT] = -1; - -- if (irq < 0) -+ if (irq < 0 && irqs[PCIE_PORT_SERVICE_AER_SHIFT] < 0) - return -ENODEV; - return 0; - } -diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c -index 6bdeb75..0b16384 100644 ---- a/drivers/pci/probe.c -+++ b/drivers/pci/probe.c -@@ -2024,6 +2024,7 @@ err_out: - kfree(b); - return NULL; - } -+EXPORT_SYMBOL_GPL(pci_create_root_bus); - - int pci_bus_insert_busn_res(struct pci_bus *b, int bus, int bus_max) - { -diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c -index 8bd76c9..8a280e9 100644 ---- a/drivers/pci/remove.c -+++ b/drivers/pci/remove.c -@@ -139,6 +139,7 @@ void pci_stop_root_bus(struct pci_bus *bus) - /* stop the host bridge */ - device_release_driver(&host_bridge->dev); - } -+EXPORT_SYMBOL_GPL(pci_stop_root_bus); - - void pci_remove_root_bus(struct pci_bus *bus) - { -@@ -158,3 +159,4 @@ void pci_remove_root_bus(struct pci_bus *bus) - /* remove the host bridge */ - device_unregister(&host_bridge->dev); - } -+EXPORT_SYMBOL_GPL(pci_remove_root_bus); -diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c -index e3e17f3..8169597 100644 ---- a/drivers/pci/setup-bus.c -+++ b/drivers/pci/setup-bus.c -@@ -1750,3 +1750,4 @@ void pci_assign_unassigned_bus_resources(struct pci_bus *bus) - __pci_bus_assign_resources(bus, &add_list, NULL); - BUG_ON(!list_empty(&add_list)); - } -+EXPORT_SYMBOL_GPL(pci_assign_unassigned_bus_resources); -diff --git a/drivers/pci/setup-irq.c b/drivers/pci/setup-irq.c -index 4e2d595..95c225b 100644 ---- a/drivers/pci/setup-irq.c -+++ b/drivers/pci/setup-irq.c -@@ -65,3 +65,4 @@ void pci_fixup_irqs(u8 (*swizzle)(struct pci_dev *, u8 *), - for_each_pci_dev(dev) - pdev_fixup_irq(dev, swizzle, map_irq); - } -+EXPORT_SYMBOL_GPL(pci_fixup_irqs); -diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig -index 76d6bd4..d4bcacf 100644 ---- a/drivers/soc/Kconfig -+++ b/drivers/soc/Kconfig -@@ -4,4 +4,17 @@ source "drivers/soc/qcom/Kconfig" - source "drivers/soc/ti/Kconfig" - source "drivers/soc/versatile/Kconfig" - -+config FSL_SOC_DRIVERS -+ bool "Freescale Soc Drivers" -+ depends on FSL_SOC || ARCH_MXC || ARCH_LAYERSCAPE -+ default n -+ help -+ Say y here to enable Freescale Soc Device Drivers support. -+ The Soc Drivers provides the device driver that is a specific block -+ or feature on Freescale platform. -+ -+if FSL_SOC_DRIVERS -+ source "drivers/soc/fsl/Kconfig" -+endif -+ - endmenu -diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile -index 063113d..ef82e45 100644 ---- a/drivers/soc/Makefile -+++ b/drivers/soc/Makefile -@@ -6,3 +6,4 @@ obj-$(CONFIG_ARCH_QCOM) += qcom/ - obj-$(CONFIG_ARCH_TEGRA) += tegra/ - obj-$(CONFIG_SOC_TI) += ti/ - obj-$(CONFIG_PLAT_VERSATILE) += versatile/ -+obj-$(CONFIG_FSL_SOC_DRIVERS) += fsl/ -diff --git a/drivers/soc/fsl/Kconfig b/drivers/soc/fsl/Kconfig -new file mode 100644 -index 0000000..92a085e ---- /dev/null -+++ b/drivers/soc/fsl/Kconfig -@@ -0,0 +1,6 @@ -+config FSL_GUTS -+ bool -+ -+if ARM || ARM64 -+source "drivers/soc/fsl/Kconfig.arm" -+endif -diff --git a/drivers/soc/fsl/Kconfig.arm b/drivers/soc/fsl/Kconfig.arm -new file mode 100644 -index 0000000..5f2d214 ---- /dev/null -+++ b/drivers/soc/fsl/Kconfig.arm -@@ -0,0 +1,25 @@ -+# -+# Freescale ARM SOC Drivers -+# -+ -+config LS1_SOC_DRIVERS -+ bool "LS1021A Soc Drivers" -+ depends on SOC_LS1021A -+ default n -+ help -+ Say y here to enable Freescale LS1021A Soc Device Drivers support. -+ The Soc Drivers provides the device driver that is a specific block -+ or feature on LS1021A platform. -+ -+config LS_SOC_DRIVERS -+ bool "Layerscape Soc Drivers" -+ depends on ARCH_LAYERSCAPE -+ default n -+ help -+ Say y here to enable Freescale Layerscape Soc Device Drivers support. -+ The Soc Drivers provides the device driver that is a specific block -+ or feature on Layerscape platform. -+ -+if LS1_SOC_DRIVERS -+ source "drivers/soc/fsl/ls1/Kconfig" -+endif -diff --git a/drivers/soc/fsl/Makefile b/drivers/soc/fsl/Makefile -new file mode 100644 -index 0000000..9fc17b3 ---- /dev/null -+++ b/drivers/soc/fsl/Makefile -@@ -0,0 +1,6 @@ -+# -+# Makefile for Freescale Soc specific device drivers. -+# -+ -+obj-$(CONFIG_LS1_SOC_DRIVERS) += ls1/ -+obj-$(CONFIG_FSL_GUTS) += guts.o -diff --git a/drivers/soc/fsl/guts.c b/drivers/soc/fsl/guts.c -new file mode 100644 -index 0000000..11065c2 ---- /dev/null -+++ b/drivers/soc/fsl/guts.c -@@ -0,0 +1,123 @@ -+/* -+ * Freescale QorIQ Platforms GUTS Driver -+ * -+ * Copyright (C) 2016 Freescale Semiconductor, 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 -+#include -+#include -+#include -+#include -+#include -+ -+struct guts { -+ struct ccsr_guts __iomem *regs; -+ bool little_endian; -+}; -+ -+static struct guts *guts; -+ -+u32 guts_get_svr(void) -+{ -+ u32 svr = 0; -+ -+ if ((!guts) || (!(guts->regs))) { -+#ifdef CONFIG_PPC -+ svr = mfspr(SPRN_SVR); -+#endif -+ return svr; -+ } -+ -+ if (guts->little_endian) -+ svr = ioread32(&guts->regs->svr); -+ else -+ svr = ioread32be(&guts->regs->svr); -+ -+ return svr; -+} -+EXPORT_SYMBOL_GPL(guts_get_svr); -+ -+static int guts_probe(struct platform_device *pdev) -+{ -+ struct device_node *np = pdev->dev.of_node; -+ -+ guts = kzalloc(sizeof(*guts), GFP_KERNEL); -+ if (!guts) -+ return -ENOMEM; -+ -+ if (of_property_read_bool(np, "little-endian")) -+ guts->little_endian = true; -+ else -+ guts->little_endian = false; -+ -+ guts->regs = of_iomap(np, 0); -+ if (!(guts->regs)) -+ return -ENOMEM; -+ -+ of_node_put(np); -+ return 0; -+} -+ -+static int guts_remove(struct platform_device *pdev) -+{ -+ iounmap(guts->regs); -+ kfree(guts); -+ return 0; -+} -+ -+/* -+ * Table for matching compatible strings, for device tree -+ * guts node, for Freescale QorIQ SOCs. -+ */ -+static const struct of_device_id guts_of_match[] = { -+ /* For T4 & B4 SOCs */ -+ { .compatible = "fsl,qoriq-device-config-1.0", }, -+ /* For P Series SOCs */ -+ { .compatible = "fsl,qoriq-device-config-2.0", }, -+ { .compatible = "fsl,p1010-guts", }, -+ { .compatible = "fsl,p1020-guts", }, -+ { .compatible = "fsl,p1021-guts", }, -+ { .compatible = "fsl,p1022-guts", }, -+ { .compatible = "fsl,p1023-guts", }, -+ { .compatible = "fsl,p2020-guts", }, -+ /* For BSC Series SOCs */ -+ { .compatible = "fsl,bsc9131-guts", }, -+ { .compatible = "fsl,bsc9132-guts", }, -+ /* For Layerscape Series SOCs */ -+ { .compatible = "fsl,ls1021a-dcfg", }, -+ { .compatible = "fsl,ls1043a-dcfg", }, -+ { .compatible = "fsl,ls2080a-dcfg", }, -+ {} -+}; -+MODULE_DEVICE_TABLE(of, guts_of_match); -+ -+static struct platform_driver guts_driver = { -+ .driver = { -+ .name = "fsl-guts", -+ .of_match_table = guts_of_match, -+ }, -+ .probe = guts_probe, -+ .remove = guts_remove, -+}; -+ -+static int __init guts_drv_init(void) -+{ -+ return platform_driver_register(&guts_driver); -+} -+subsys_initcall(guts_drv_init); -+ -+static void __exit guts_drv_exit(void) -+{ -+ platform_driver_unregister(&guts_driver); -+} -+module_exit(guts_drv_exit); -+ -+MODULE_AUTHOR("Freescale Semiconductor, Inc."); -+MODULE_DESCRIPTION("Freescale QorIQ Platforms GUTS Driver"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/soc/fsl/ls1/Kconfig b/drivers/soc/fsl/ls1/Kconfig -new file mode 100644 -index 0000000..c9b04c4 ---- /dev/null -+++ b/drivers/soc/fsl/ls1/Kconfig -@@ -0,0 +1,11 @@ -+# -+# LS-1 Soc drivers -+# -+config FTM_ALARM -+ bool "FTM alarm driver" -+ depends on SOC_LS1021A -+ default n -+ help -+ Say y here to enable FTM alarm support. The FTM alarm provides -+ alarm functions for wakeup system from deep sleep. There is only -+ one FTM can be used in ALARM(FTM 0). -diff --git a/drivers/soc/fsl/ls1/Makefile b/drivers/soc/fsl/ls1/Makefile -new file mode 100644 -index 0000000..6299aa1 ---- /dev/null -+++ b/drivers/soc/fsl/ls1/Makefile -@@ -0,0 +1 @@ -+obj-$(CONFIG_FTM_ALARM) += ftm_alarm.o -diff --git a/drivers/soc/fsl/ls1/ftm_alarm.c b/drivers/soc/fsl/ls1/ftm_alarm.c -new file mode 100644 -index 0000000..c42b26b ---- /dev/null -+++ b/drivers/soc/fsl/ls1/ftm_alarm.c -@@ -0,0 +1,274 @@ -+/* -+ * Freescale FlexTimer Module (FTM) Alarm driver. -+ * -+ * Copyright 2014 Freescale Semiconductor, 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 -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define FTM_SC 0x00 -+#define FTM_SC_CLK_SHIFT 3 -+#define FTM_SC_CLK_MASK (0x3 << FTM_SC_CLK_SHIFT) -+#define FTM_SC_CLK(c) ((c) << FTM_SC_CLK_SHIFT) -+#define FTM_SC_PS_MASK 0x7 -+#define FTM_SC_TOIE BIT(6) -+#define FTM_SC_TOF BIT(7) -+ -+#define FTM_SC_CLKS_FIXED_FREQ 0x02 -+ -+#define FTM_CNT 0x04 -+#define FTM_MOD 0x08 -+#define FTM_CNTIN 0x4C -+ -+#define FIXED_FREQ_CLK 32000 -+#define MAX_FREQ_DIV (1 << FTM_SC_PS_MASK) -+#define MAX_COUNT_VAL 0xffff -+ -+static void __iomem *ftm1_base; -+static u32 alarm_freq; -+static bool big_endian; -+ -+static inline u32 ftm_readl(void __iomem *addr) -+{ -+ if (big_endian) -+ return ioread32be(addr); -+ -+ return ioread32(addr); -+} -+ -+static inline void ftm_writel(u32 val, void __iomem *addr) -+{ -+ if (big_endian) -+ iowrite32be(val, addr); -+ else -+ iowrite32(val, addr); -+} -+ -+static inline void ftm_counter_enable(void __iomem *base) -+{ -+ u32 val; -+ -+ /* select and enable counter clock source */ -+ val = ftm_readl(base + FTM_SC); -+ val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK); -+ val |= (FTM_SC_PS_MASK | FTM_SC_CLK(FTM_SC_CLKS_FIXED_FREQ)); -+ ftm_writel(val, base + FTM_SC); -+} -+ -+static inline void ftm_counter_disable(void __iomem *base) -+{ -+ u32 val; -+ -+ /* disable counter clock source */ -+ val = ftm_readl(base + FTM_SC); -+ val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK); -+ ftm_writel(val, base + FTM_SC); -+} -+ -+static inline void ftm_irq_acknowledge(void __iomem *base) -+{ -+ u32 val; -+ -+ val = ftm_readl(base + FTM_SC); -+ val &= ~FTM_SC_TOF; -+ ftm_writel(val, base + FTM_SC); -+} -+ -+static inline void ftm_irq_enable(void __iomem *base) -+{ -+ u32 val; -+ -+ val = ftm_readl(base + FTM_SC); -+ val |= FTM_SC_TOIE; -+ ftm_writel(val, base + FTM_SC); -+} -+ -+static inline void ftm_irq_disable(void __iomem *base) -+{ -+ u32 val; -+ -+ val = ftm_readl(base + FTM_SC); -+ val &= ~FTM_SC_TOIE; -+ ftm_writel(val, base + FTM_SC); -+} -+ -+static inline void ftm_reset_counter(void __iomem *base) -+{ -+ /* -+ * The CNT register contains the FTM counter value. -+ * Reset clears the CNT register. Writing any value to COUNT -+ * updates the counter with its initial value, CNTIN. -+ */ -+ ftm_writel(0x00, base + FTM_CNT); -+} -+ -+static u32 time_to_cycle(unsigned long time) -+{ -+ u32 cycle; -+ -+ cycle = time * alarm_freq; -+ if (cycle > MAX_COUNT_VAL) { -+ pr_err("Out of alarm range.\n"); -+ cycle = 0; -+ } -+ -+ return cycle; -+} -+ -+static u32 cycle_to_time(u32 cycle) -+{ -+ return cycle / alarm_freq + 1; -+} -+ -+static void ftm_clean_alarm(void) -+{ -+ ftm_counter_disable(ftm1_base); -+ -+ ftm_writel(0x00, ftm1_base + FTM_CNTIN); -+ ftm_writel(~0UL, ftm1_base + FTM_MOD); -+ -+ ftm_reset_counter(ftm1_base); -+} -+ -+static int ftm_set_alarm(u64 cycle) -+{ -+ ftm_irq_disable(ftm1_base); -+ -+ /* -+ * The counter increments until the value of MOD is reached, -+ * at which point the counter is reloaded with the value of CNTIN. -+ * The TOF (the overflow flag) bit is set when the FTM counter -+ * changes from MOD to CNTIN. So we should using the cycle - 1. -+ */ -+ ftm_writel(cycle - 1, ftm1_base + FTM_MOD); -+ -+ ftm_counter_enable(ftm1_base); -+ -+ ftm_irq_enable(ftm1_base); -+ -+ return 0; -+} -+ -+static irqreturn_t ftm_alarm_interrupt(int irq, void *dev_id) -+{ -+ ftm_irq_acknowledge(ftm1_base); -+ ftm_irq_disable(ftm1_base); -+ ftm_clean_alarm(); -+ -+ return IRQ_HANDLED; -+} -+ -+static ssize_t ftm_alarm_show(struct device *dev, -+ struct device_attribute *attr, -+ char *buf) -+{ -+ u32 count, val; -+ -+ count = ftm_readl(ftm1_base + FTM_MOD); -+ val = ftm_readl(ftm1_base + FTM_CNT); -+ val = (count & MAX_COUNT_VAL) - val; -+ val = cycle_to_time(val); -+ -+ return sprintf(buf, "%u\n", val); -+} -+ -+static ssize_t ftm_alarm_store(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ u32 cycle; -+ unsigned long time; -+ -+ if (kstrtoul(buf, 0, &time)) -+ return -EINVAL; -+ -+ ftm_clean_alarm(); -+ -+ cycle = time_to_cycle(time); -+ if (!cycle) -+ return -EINVAL; -+ -+ ftm_set_alarm(cycle); -+ -+ return count; -+} -+ -+static struct device_attribute ftm_alarm_attributes = __ATTR(ftm_alarm, 0644, -+ ftm_alarm_show, ftm_alarm_store); -+ -+static int ftm_alarm_probe(struct platform_device *pdev) -+{ -+ struct device_node *np = pdev->dev.of_node; -+ struct resource *r; -+ int irq; -+ int ret; -+ -+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (!r) -+ return -ENODEV; -+ -+ ftm1_base = devm_ioremap_resource(&pdev->dev, r); -+ if (IS_ERR(ftm1_base)) -+ return PTR_ERR(ftm1_base); -+ -+ irq = irq_of_parse_and_map(np, 0); -+ if (irq <= 0) { -+ pr_err("ftm: unable to get IRQ from DT, %d\n", irq); -+ return -EINVAL; -+ } -+ -+ big_endian = of_property_read_bool(np, "big-endian"); -+ -+ ret = devm_request_irq(&pdev->dev, irq, ftm_alarm_interrupt, -+ IRQF_NO_SUSPEND, dev_name(&pdev->dev), NULL); -+ if (ret < 0) { -+ dev_err(&pdev->dev, "failed to request irq\n"); -+ return ret; -+ } -+ -+ ret = device_create_file(&pdev->dev, &ftm_alarm_attributes); -+ if (ret) { -+ dev_err(&pdev->dev, "create sysfs fail.\n"); -+ return ret; -+ } -+ -+ alarm_freq = (u32)FIXED_FREQ_CLK / (u32)MAX_FREQ_DIV; -+ -+ ftm_clean_alarm(); -+ -+ device_init_wakeup(&pdev->dev, true); -+ -+ return ret; -+} -+ -+static const struct of_device_id ftm_alarm_match[] = { -+ { .compatible = "fsl,ftm-alarm", }, -+ { .compatible = "fsl,ftm-timer", }, -+ { }, -+}; -+ -+static struct platform_driver ftm_alarm_driver = { -+ .probe = ftm_alarm_probe, -+ .driver = { -+ .name = "ftm-alarm", -+ .owner = THIS_MODULE, -+ .of_match_table = ftm_alarm_match, -+ }, -+}; -+ -+static int __init ftm_alarm_init(void) -+{ -+ return platform_driver_register(&ftm_alarm_driver); -+} -+device_initcall(ftm_alarm_init); -diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c -index 27d1a91..cb52ede 100644 ---- a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c -+++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c -@@ -52,11 +52,6 @@ MODULE_LICENSE("Dual BSD/GPL"); - MODULE_AUTHOR("Freescale Semiconductor, Inc"); - MODULE_DESCRIPTION("Freescale DPAA2 Ethernet Driver"); - --/* Oldest DPAA2 objects version we are compatible with */ --#define DPAA2_SUPPORTED_DPNI_VERSION 6 --#define DPAA2_SUPPORTED_DPBP_VERSION 2 --#define DPAA2_SUPPORTED_DPCON_VERSION 2 -- - static void validate_rx_csum(struct dpaa2_eth_priv *priv, - u32 fd_status, - struct sk_buff *skb) -@@ -261,7 +256,7 @@ static void dpaa2_eth_rx(struct dpaa2_eth_priv *priv, - priv->buf_layout.private_data_size + - sizeof(struct dpaa2_fas)); - -- *ns = DPAA2_PTP_NOMINAL_FREQ_PERIOD_NS * (*ns); -+ *ns = DPAA2_PTP_NOMINAL_FREQ_PERIOD_NS * le64_to_cpup(ns); - memset(shhwtstamps, 0, sizeof(*shhwtstamps)); - shhwtstamps->hwtstamp = ns_to_ktime(*ns); - } -@@ -362,6 +357,25 @@ static int consume_frames(struct dpaa2_eth_channel *ch) - return cleaned; - } - -+/* Configure the egress frame annotation for timestamp update */ -+static void enable_tx_tstamp(struct dpaa2_fd *fd, void *hwa_start) -+{ -+ struct dpaa2_faead *faead; -+ u32 ctrl; -+ u32 frc; -+ -+ /* Mark the egress frame annotation area as valid */ -+ frc = dpaa2_fd_get_frc(fd); -+ dpaa2_fd_set_frc(fd, frc | DPAA2_FD_FRC_FAEADV); -+ -+ /* enable UPD (update prepanded data) bit in FAEAD field of -+ * hardware frame annotation area -+ */ -+ ctrl = DPAA2_FAEAD_A2V | DPAA2_FAEAD_UPDV | DPAA2_FAEAD_UPD; -+ faead = hwa_start + DPAA2_FAEAD_OFFSET; -+ faead->ctrl = cpu_to_le32(ctrl); -+} -+ - /* Create a frame descriptor based on a fragmented skb */ - static int build_sg_fd(struct dpaa2_eth_priv *priv, - struct sk_buff *skb, -@@ -369,6 +383,7 @@ static int build_sg_fd(struct dpaa2_eth_priv *priv, - { - struct device *dev = priv->net_dev->dev.parent; - void *sgt_buf = NULL; -+ void *hwa; - dma_addr_t addr; - int nr_frags = skb_shinfo(skb)->nr_frags; - struct dpaa2_sg_entry *sgt; -@@ -414,7 +429,8 @@ static int build_sg_fd(struct dpaa2_eth_priv *priv, - * on TX confirmation. We are clearing FAS (Frame Annotation Status) - * field here. - */ -- memset(sgt_buf + priv->buf_layout.private_data_size, 0, 8); -+ hwa = sgt_buf + priv->buf_layout.private_data_size; -+ memset(hwa, 0, 8); - - sgt = (struct dpaa2_sg_entry *)(sgt_buf + priv->tx_data_offset); - -@@ -459,6 +475,9 @@ static int build_sg_fd(struct dpaa2_eth_priv *priv, - fd->simple.ctrl = DPAA2_FD_CTRL_ASAL | DPAA2_FD_CTRL_PTA | - DPAA2_FD_CTRL_PTV1; - -+ if (priv->ts_tx_en && skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) -+ enable_tx_tstamp(fd, hwa); -+ - return 0; - - dma_map_single_failed: -@@ -479,6 +498,7 @@ static int build_single_fd(struct dpaa2_eth_priv *priv, - u8 *buffer_start; - struct sk_buff **skbh; - dma_addr_t addr; -+ void *hwa; - - buffer_start = PTR_ALIGN(skb->data - priv->tx_data_offset - - DPAA2_ETH_TX_BUF_ALIGN, -@@ -487,9 +507,10 @@ static int build_single_fd(struct dpaa2_eth_priv *priv, - /* PTA from egress side is passed as is to the confirmation side so - * we need to clear some fields here in order to find consistent values - * on TX confirmation. We are clearing FAS (Frame Annotation Status) -- * field here. -+ * field here - */ -- memset(buffer_start + priv->buf_layout.private_data_size, 0, 8); -+ hwa = buffer_start + priv->buf_layout.private_data_size; -+ memset(hwa, 0, 8); - - /* Store a backpointer to the skb at the beginning of the buffer - * (in the private data area) such that we can release it -@@ -512,6 +533,9 @@ static int build_single_fd(struct dpaa2_eth_priv *priv, - fd->simple.ctrl = DPAA2_FD_CTRL_ASAL | DPAA2_FD_CTRL_PTA | - DPAA2_FD_CTRL_PTV1; - -+ if (priv->ts_tx_en && skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) -+ enable_tx_tstamp(fd, hwa); -+ - return 0; - } - -@@ -579,7 +603,7 @@ static void free_tx_fd(const struct dpaa2_eth_priv *priv, - ns = (u64 *)((void *)skbh + - priv->buf_layout.private_data_size + - sizeof(struct dpaa2_fas)); -- *ns = DPAA2_PTP_NOMINAL_FREQ_PERIOD_NS * (*ns); -+ *ns = DPAA2_PTP_NOMINAL_FREQ_PERIOD_NS * le64_to_cpup(ns); - shhwtstamps.hwtstamp = ns_to_ktime(*ns); - skb_tstamp_tx(skb, &shhwtstamps); - } -@@ -779,7 +803,7 @@ static int add_bufs(struct dpaa2_eth_priv *priv, u16 bpid) - /* Allocate buffer visible to WRIOP + skb shared info + - * alignment padding - */ -- buf = napi_alloc_frag(DPAA2_ETH_BUF_RAW_SIZE); -+ buf = netdev_alloc_frag(DPAA2_ETH_BUF_RAW_SIZE); - if (unlikely(!buf)) - goto err_alloc; - -@@ -973,7 +997,7 @@ static int dpaa2_eth_poll(struct napi_struct *napi, int budget) - } - - if (cleaned < budget) { -- napi_complete_done(napi, cleaned); -+ napi_complete(napi); - /* Re-enable data available notifications */ - do { - err = dpaa2_io_service_rearm(NULL, &ch->nctx); -@@ -1353,7 +1377,7 @@ static void dpaa2_eth_set_rx_mode(struct net_device *net_dev) - * in promisc mode, in order to avoid frame loss while we - * progressively add entries to the table. - * We don't know whether we had been in promisc already, and -- * making an MC call to find it is expensive; so set uc promisc -+ * making an MC call to find out is expensive; so set uc promisc - * nonetheless. - */ - err = dpni_set_unicast_promisc(mc_io, 0, mc_token, 1); -@@ -1498,48 +1522,7 @@ static void cdan_cb(struct dpaa2_io_notification_ctx *ctx) - /* Update NAPI statistics */ - ch->stats.cdan++; - -- napi_schedule_irqoff(&ch->napi); --} -- --/* Verify that the FLIB API version of various MC objects is supported -- * by our driver -- */ --static int check_obj_version(struct fsl_mc_device *ls_dev, u16 mc_version) --{ -- char *name = ls_dev->obj_desc.type; -- struct device *dev = &ls_dev->dev; -- u16 supported_version, flib_version; -- -- if (strcmp(name, "dpni") == 0) { -- flib_version = DPNI_VER_MAJOR; -- supported_version = DPAA2_SUPPORTED_DPNI_VERSION; -- } else if (strcmp(name, "dpbp") == 0) { -- flib_version = DPBP_VER_MAJOR; -- supported_version = DPAA2_SUPPORTED_DPBP_VERSION; -- } else if (strcmp(name, "dpcon") == 0) { -- flib_version = DPCON_VER_MAJOR; -- supported_version = DPAA2_SUPPORTED_DPCON_VERSION; -- } else { -- dev_err(dev, "invalid object type (%s)\n", name); -- return -EINVAL; -- } -- -- /* Check that the FLIB-defined version matches the one reported by MC */ -- if (mc_version != flib_version) { -- dev_err(dev, "%s FLIB version mismatch: MC reports %d, we have %d\n", -- name, mc_version, flib_version); -- return -EINVAL; -- } -- -- /* ... and that we actually support it */ -- if (mc_version < supported_version) { -- dev_err(dev, "Unsupported %s FLIB version (%d)\n", -- name, mc_version); -- return -EINVAL; -- } -- dev_dbg(dev, "Using %s FLIB version %d\n", name, mc_version); -- -- return 0; -+ napi_schedule(&ch->napi); - } - - /* Allocate and configure a DPCON object */ -@@ -1563,16 +1546,18 @@ static struct fsl_mc_device *setup_dpcon(struct dpaa2_eth_priv *priv) - goto err_open; - } - -+ err = dpcon_reset(priv->mc_io, 0, dpcon->mc_handle); -+ if (err) { -+ dev_err(dev, "dpcon_reset() failed\n"); -+ goto err_reset; -+ } -+ - err = dpcon_get_attributes(priv->mc_io, 0, dpcon->mc_handle, &attrs); - if (err) { - dev_err(dev, "dpcon_get_attributes() failed\n"); - goto err_get_attr; - } - -- err = check_obj_version(dpcon, attrs.version.major); -- if (err) -- goto err_dpcon_ver; -- - err = dpcon_enable(priv->mc_io, 0, dpcon->mc_handle); - if (err) { - dev_err(dev, "dpcon_enable() failed\n"); -@@ -1582,8 +1567,8 @@ static struct fsl_mc_device *setup_dpcon(struct dpaa2_eth_priv *priv) - return dpcon; - - err_enable: --err_dpcon_ver: - err_get_attr: -+err_reset: - dpcon_close(priv->mc_io, 0, dpcon->mc_handle); - err_open: - fsl_mc_object_free(dpcon); -@@ -1849,6 +1834,12 @@ static int setup_dpbp(struct dpaa2_eth_priv *priv) - goto err_open; - } - -+ err = dpbp_reset(priv->mc_io, 0, dpbp_dev->mc_handle); -+ if (err) { -+ dev_err(dev, "dpbp_reset() failed\n"); -+ goto err_reset; -+ } -+ - err = dpbp_enable(priv->mc_io, 0, dpbp_dev->mc_handle); - if (err) { - dev_err(dev, "dpbp_enable() failed\n"); -@@ -1862,16 +1853,12 @@ static int setup_dpbp(struct dpaa2_eth_priv *priv) - goto err_get_attr; - } - -- err = check_obj_version(dpbp_dev, priv->dpbp_attrs.version.major); -- if (err) -- goto err_dpbp_ver; -- - return 0; - --err_dpbp_ver: - err_get_attr: - dpbp_disable(priv->mc_io, 0, dpbp_dev->mc_handle); - err_enable: -+err_reset: - dpbp_close(priv->mc_io, 0, dpbp_dev->mc_handle); - err_open: - fsl_mc_object_free(dpbp_dev); -@@ -1911,6 +1898,12 @@ static int setup_dpni(struct fsl_mc_device *ls_dev) - ls_dev->mc_io = priv->mc_io; - ls_dev->mc_handle = priv->mc_token; - -+ err = dpni_reset(priv->mc_io, 0, priv->mc_token); -+ if (err) { -+ dev_err(dev, "dpni_reset() failed\n"); -+ goto err_reset; -+ } -+ - /* Map a memory region which will be used by MC to pass us an - * attribute structure - */ -@@ -1940,10 +1933,6 @@ static int setup_dpni(struct fsl_mc_device *ls_dev) - goto err_get_attr; - } - -- err = check_obj_version(ls_dev, priv->dpni_attrs.version.major); -- if (err) -- goto err_dpni_ver; -- - memset(&priv->dpni_ext_cfg, 0, sizeof(priv->dpni_ext_cfg)); - err = dpni_extract_extended_cfg(&priv->dpni_ext_cfg, dma_mem); - if (err) { -@@ -2019,11 +2008,11 @@ err_cls_rule: - err_data_offset: - err_buf_layout: - err_extract: --err_dpni_ver: - err_get_attr: - err_dma_map: - kfree(dma_mem); - err_alloc: -+err_reset: - dpni_close(priv->mc_io, 0, priv->mc_token); - err_open: - return err; -@@ -2157,6 +2146,131 @@ static int setup_rx_err_flow(struct dpaa2_eth_priv *priv, - } - #endif - -+/* default hash key fields */ -+static struct dpaa2_eth_hash_fields default_hash_fields[] = { -+ { -+ /* L2 header */ -+ .rxnfc_field = RXH_L2DA, -+ .cls_prot = NET_PROT_ETH, -+ .cls_field = NH_FLD_ETH_DA, -+ .size = 6, -+ }, { -+ .cls_prot = NET_PROT_ETH, -+ .cls_field = NH_FLD_ETH_SA, -+ .size = 6, -+ }, { -+ /* This is the last ethertype field parsed: -+ * depending on frame format, it can be the MAC ethertype -+ * or the VLAN etype. -+ */ -+ .cls_prot = NET_PROT_ETH, -+ .cls_field = NH_FLD_ETH_TYPE, -+ .size = 2, -+ }, { -+ /* VLAN header */ -+ .rxnfc_field = RXH_VLAN, -+ .cls_prot = NET_PROT_VLAN, -+ .cls_field = NH_FLD_VLAN_TCI, -+ .size = 2, -+ }, { -+ /* IP header */ -+ .rxnfc_field = RXH_IP_SRC, -+ .cls_prot = NET_PROT_IP, -+ .cls_field = NH_FLD_IP_SRC, -+ .size = 4, -+ }, { -+ .rxnfc_field = RXH_IP_DST, -+ .cls_prot = NET_PROT_IP, -+ .cls_field = NH_FLD_IP_DST, -+ .size = 4, -+ }, { -+ .rxnfc_field = RXH_L3_PROTO, -+ .cls_prot = NET_PROT_IP, -+ .cls_field = NH_FLD_IP_PROTO, -+ .size = 1, -+ }, { -+ /* Using UDP ports, this is functionally equivalent to raw -+ * byte pairs from L4 header. -+ */ -+ .rxnfc_field = RXH_L4_B_0_1, -+ .cls_prot = NET_PROT_UDP, -+ .cls_field = NH_FLD_UDP_PORT_SRC, -+ .size = 2, -+ }, { -+ .rxnfc_field = RXH_L4_B_2_3, -+ .cls_prot = NET_PROT_UDP, -+ .cls_field = NH_FLD_UDP_PORT_DST, -+ .size = 2, -+ }, -+}; -+ -+/* Set RX hash options */ -+int set_hash(struct dpaa2_eth_priv *priv) -+{ -+ struct device *dev = priv->net_dev->dev.parent; -+ struct dpkg_profile_cfg cls_cfg; -+ struct dpni_rx_tc_dist_cfg dist_cfg; -+ u8 *dma_mem; -+ int i; -+ int err = 0; -+ -+ memset(&cls_cfg, 0, sizeof(cls_cfg)); -+ -+ for (i = 0; i < priv->num_hash_fields; i++) { -+ struct dpkg_extract *key = -+ &cls_cfg.extracts[cls_cfg.num_extracts]; -+ -+ key->type = DPKG_EXTRACT_FROM_HDR; -+ key->extract.from_hdr.prot = priv->hash_fields[i].cls_prot; -+ key->extract.from_hdr.type = DPKG_FULL_FIELD; -+ key->extract.from_hdr.field = priv->hash_fields[i].cls_field; -+ cls_cfg.num_extracts++; -+ -+ priv->rx_flow_hash |= priv->hash_fields[i].rxnfc_field; -+ } -+ -+ dma_mem = kzalloc(DPAA2_CLASSIFIER_DMA_SIZE, GFP_DMA | GFP_KERNEL); -+ if (!dma_mem) -+ return -ENOMEM; -+ -+ err = dpni_prepare_key_cfg(&cls_cfg, dma_mem); -+ if (err) { -+ dev_err(dev, "dpni_prepare_key_cfg error %d", err); -+ return err; -+ } -+ -+ memset(&dist_cfg, 0, sizeof(dist_cfg)); -+ -+ /* Prepare for setting the rx dist */ -+ dist_cfg.key_cfg_iova = dma_map_single(dev, dma_mem, -+ DPAA2_CLASSIFIER_DMA_SIZE, -+ DMA_TO_DEVICE); -+ if (dma_mapping_error(dev, dist_cfg.key_cfg_iova)) { -+ dev_err(dev, "DMA mapping failed\n"); -+ kfree(dma_mem); -+ return -ENOMEM; -+ } -+ -+ dist_cfg.dist_size = dpaa2_eth_queue_count(priv); -+ if (dpaa2_eth_fs_enabled(priv)) { -+ dist_cfg.dist_mode = DPNI_DIST_MODE_FS; -+ dist_cfg.fs_cfg.miss_action = DPNI_FS_MISS_HASH; -+ } else { -+ dist_cfg.dist_mode = DPNI_DIST_MODE_HASH; -+ } -+ -+ err = dpni_set_rx_tc_dist(priv->mc_io, 0, priv->mc_token, 0, &dist_cfg); -+ dma_unmap_single(dev, dist_cfg.key_cfg_iova, -+ DPAA2_CLASSIFIER_DMA_SIZE, DMA_TO_DEVICE); -+ kfree(dma_mem); -+ if (err) { -+ dev_err(dev, "dpni_set_rx_tc_dist() error %d\n", err); -+ return err; -+ } -+ -+ return 0; -+} -+ - /* Bind the DPNI to its needed objects and resources: buffer pool, DPIOs, - * frame queues and channels - */ -@@ -2179,15 +2293,22 @@ static int bind_dpni(struct dpaa2_eth_priv *priv) - return err; - } - -- check_fs_support(net_dev); -+ /* Verify classification options and disable hashing and/or -+ * flow steering support in case of invalid configuration values -+ */ -+ check_cls_support(priv); - -- /* have the interface implicitly distribute traffic based on supported -- * header fields -+ /* have the interface implicitly distribute traffic based on -+ * a static hash key - */ - if (dpaa2_eth_hash_enabled(priv)) { -- err = dpaa2_eth_set_hash(net_dev, DPAA2_RXH_SUPPORTED); -- if (err) -+ priv->hash_fields = default_hash_fields; -+ priv->num_hash_fields = ARRAY_SIZE(default_hash_fields); -+ err = set_hash(priv); -+ if (err) { -+ dev_err(dev, "Hashing configuration failed\n"); - return err; -+ } - } - - /* Configure handling of error frames */ -@@ -2512,7 +2633,7 @@ static ssize_t dpaa2_eth_show_txconf_cpumask(struct device *dev, - { - struct dpaa2_eth_priv *priv = netdev_priv(to_net_dev(dev)); - -- return cpumap_print_to_pagebuf(1, buf, &priv->txconf_cpumask); -+ return cpumask_scnprintf(buf, PAGE_SIZE, &priv->txconf_cpumask); - } - - static ssize_t dpaa2_eth_write_txconf_cpumask(struct device *dev, -diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.h b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.h -index 7274fbe..bdcdbd6 100644 ---- a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.h -+++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.h -@@ -40,7 +40,6 @@ - #include "../../fsl-mc/include/dpbp-cmd.h" - #include "../../fsl-mc/include/dpcon.h" - #include "../../fsl-mc/include/dpcon-cmd.h" --#include "../../fsl-mc/include/dpmng.h" - #include "dpni.h" - #include "dpni-cmd.h" - -@@ -54,8 +53,8 @@ - */ - #define DPAA2_ETH_MAX_SG_ENTRIES ((64 * 1024) / DPAA2_ETH_RX_BUF_SIZE) - --/* Maximum acceptable MTU value. It is in direct relation with the MC-enforced -- * Max Frame Length (currently 10k). -+/* Maximum acceptable MTU value. It is in direct relation with the hardware -+ * enforced Max Frame Length (currently 10k). - */ - #define DPAA2_ETH_MFL (10 * 1024) - #define DPAA2_ETH_MAX_MTU (DPAA2_ETH_MFL - VLAN_ETH_HLEN) -@@ -100,8 +99,8 @@ - SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + \ - DPAA2_ETH_RX_BUF_ALIGN) - --/* PTP nominal frequency 1MHz */ --#define DPAA2_PTP_NOMINAL_FREQ_PERIOD_NS 1000 -+/* PTP nominal frequency 1GHz */ -+#define DPAA2_PTP_NOMINAL_FREQ_PERIOD_NS 1 - - /* We are accommodating a skb backpointer and some S/G info - * in the frame's software annotation. The hardware -@@ -138,6 +137,18 @@ struct dpaa2_fas { - __le32 status; - } __packed; - -+/* Frame annotation egress action descriptor */ -+#define DPAA2_FAEAD_OFFSET 0x58 -+ -+struct dpaa2_faead { -+ __le32 conf_fqid; -+ __le32 ctrl; -+}; -+ -+#define DPAA2_FAEAD_A2V 0x20000000 -+#define DPAA2_FAEAD_UPDV 0x00001000 -+#define DPAA2_FAEAD_UPD 0x00000010 -+ - /* Error and status bits in the frame annotation status word */ - /* Debug frame, otherwise supposed to be discarded */ - #define DPAA2_FAS_DISC 0x80000000 -@@ -274,6 +285,14 @@ struct dpaa2_eth_cls_rule { - bool in_use; - }; - -+struct dpaa2_eth_hash_fields { -+ u64 rxnfc_field; -+ enum net_prot cls_prot; -+ int cls_field; -+ int offset; -+ int size; -+}; -+ - /* Driver private data */ - struct dpaa2_eth_priv { - struct net_device *net_dev; -@@ -318,8 +337,10 @@ struct dpaa2_eth_priv { - bool do_link_poll; - struct task_struct *poll_thread; - -+ struct dpaa2_eth_hash_fields *hash_fields; -+ u8 num_hash_fields; - /* enabled ethtool hashing bits */ -- u64 rx_hash_fields; -+ u64 rx_flow_hash; - - #ifdef CONFIG_FSL_DPAA2_ETH_DEBUGFS - struct dpaa2_debugfs dbg; -@@ -334,25 +355,24 @@ struct dpaa2_eth_priv { - bool ts_rx_en; /* Rx timestamping enabled */ - }; - --/* default Rx hash options, set during probing */ --#define DPAA2_RXH_SUPPORTED (RXH_L2DA | RXH_VLAN | RXH_L3_PROTO \ -- | RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 \ -- | RXH_L4_B_2_3) -- - #define dpaa2_eth_hash_enabled(priv) \ - ((priv)->dpni_attrs.options & DPNI_OPT_DIST_HASH) - - #define dpaa2_eth_fs_enabled(priv) \ - ((priv)->dpni_attrs.options & DPNI_OPT_DIST_FS) - -+#define dpaa2_eth_fs_mask_enabled(priv) \ -+ ((priv)->dpni_attrs.options & DPNI_OPT_FS_MASK_SUPPORT) -+ - #define DPAA2_CLASSIFIER_ENTRY_COUNT 16 - - /* Required by struct dpni_attr::ext_cfg_iova */ - #define DPAA2_EXT_CFG_SIZE 256 - --extern const struct ethtool_ops dpaa2_ethtool_ops; -+/* size of DMA memory used to pass configuration to classifier, in bytes */ -+#define DPAA2_CLASSIFIER_DMA_SIZE 256 - --int dpaa2_eth_set_hash(struct net_device *net_dev, u64 flags); -+extern const struct ethtool_ops dpaa2_ethtool_ops; - - static int dpaa2_eth_queue_count(struct dpaa2_eth_priv *priv) - { -@@ -372,6 +392,6 @@ static inline int dpaa2_eth_max_channels(struct dpaa2_eth_priv *priv) - priv->dpni_attrs.max_senders); - } - --void check_fs_support(struct net_device *); -+void check_cls_support(struct dpaa2_eth_priv *priv); - - #endif /* __DPAA2_H */ -diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-ethtool.c b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-ethtool.c -index fdab07f..1d792cd 100644 ---- a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-ethtool.c -+++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-ethtool.c -@@ -32,9 +32,6 @@ - #include "dpni.h" /* DPNI_LINK_OPT_* */ - #include "dpaa2-eth.h" - --/* size of DMA memory used to pass configuration to classifier, in bytes */ --#define DPAA2_CLASSIFIER_DMA_SIZE 256 -- - /* To be kept in sync with 'enum dpni_counter' */ - char dpaa2_ethtool_stats[][ETH_GSTRING_LEN] = { - "rx frames", -@@ -89,28 +86,9 @@ char dpaa2_ethtool_extras[][ETH_GSTRING_LEN] = { - static void dpaa2_eth_get_drvinfo(struct net_device *net_dev, - struct ethtool_drvinfo *drvinfo) - { -- struct mc_version mc_ver; -- struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -- char fw_version[ETHTOOL_FWVERS_LEN]; -- char version[32]; -- int err; -- -- err = mc_get_version(priv->mc_io, 0, &mc_ver); -- if (err) { -- strlcpy(drvinfo->fw_version, "Error retrieving MC version", -- sizeof(drvinfo->fw_version)); -- } else { -- scnprintf(fw_version, sizeof(fw_version), "%d.%d.%d", -- mc_ver.major, mc_ver.minor, mc_ver.revision); -- strlcpy(drvinfo->fw_version, fw_version, -- sizeof(drvinfo->fw_version)); -- } -- -- scnprintf(version, sizeof(version), "%d.%d", DPNI_VER_MAJOR, -- DPNI_VER_MINOR); -- strlcpy(drvinfo->version, version, sizeof(drvinfo->version)); -- - strlcpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver)); -+ strlcpy(drvinfo->version, VERSION, sizeof(drvinfo->version)); -+ strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); - strlcpy(drvinfo->bus_info, dev_name(net_dev->dev.parent->parent), - sizeof(drvinfo->bus_info)); - } -@@ -152,7 +130,7 @@ static int dpaa2_eth_set_settings(struct net_device *net_dev, - - netdev_dbg(net_dev, "Setting link parameters..."); - -- /* Due to a temporary firmware limitation, the DPNI must be down -+ /* Due to a temporary MC limitation, the DPNI must be down - * in order to be able to change link settings. Taking steps to let - * the user know that. - */ -@@ -211,7 +189,7 @@ static int dpaa2_eth_get_sset_count(struct net_device *net_dev, int sset) - } - } - --/** Fill in hardware counters, as returned by the MC firmware. -+/** Fill in hardware counters, as returned by MC. - */ - static void dpaa2_eth_get_ethtool_stats(struct net_device *net_dev, - struct ethtool_stats *stats, -@@ -296,203 +274,223 @@ static void dpaa2_eth_get_ethtool_stats(struct net_device *net_dev, - #endif - } - --static const struct dpaa2_eth_hash_fields { -- u64 rxnfc_field; -- enum net_prot cls_prot; -- int cls_field; -- int size; --} hash_fields[] = { -- { -- /* L2 header */ -- .rxnfc_field = RXH_L2DA, -- .cls_prot = NET_PROT_ETH, -- .cls_field = NH_FLD_ETH_DA, -- .size = 6, -- }, { -- /* VLAN header */ -- .rxnfc_field = RXH_VLAN, -- .cls_prot = NET_PROT_VLAN, -- .cls_field = NH_FLD_VLAN_TCI, -- .size = 2, -- }, { -- /* IP header */ -- .rxnfc_field = RXH_IP_SRC, -- .cls_prot = NET_PROT_IP, -- .cls_field = NH_FLD_IP_SRC, -- .size = 4, -- }, { -- .rxnfc_field = RXH_IP_DST, -- .cls_prot = NET_PROT_IP, -- .cls_field = NH_FLD_IP_DST, -- .size = 4, -- }, { -- .rxnfc_field = RXH_L3_PROTO, -- .cls_prot = NET_PROT_IP, -- .cls_field = NH_FLD_IP_PROTO, -- .size = 1, -- }, { -- /* Using UDP ports, this is functionally equivalent to raw -- * byte pairs from L4 header. -- */ -- .rxnfc_field = RXH_L4_B_0_1, -- .cls_prot = NET_PROT_UDP, -- .cls_field = NH_FLD_UDP_PORT_SRC, -- .size = 2, -- }, { -- .rxnfc_field = RXH_L4_B_2_3, -- .cls_prot = NET_PROT_UDP, -- .cls_field = NH_FLD_UDP_PORT_DST, -- .size = 2, -- }, --}; -- --static int cls_is_enabled(struct net_device *net_dev, u64 flag) --{ -- struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -- -- return !!(priv->rx_hash_fields & flag); --} -- --static int cls_key_off(struct net_device *net_dev, u64 flag) -+static int cls_key_off(struct dpaa2_eth_priv *priv, int prot, int field) - { - int i, off = 0; - -- for (i = 0; i < ARRAY_SIZE(hash_fields); i++) { -- if (hash_fields[i].rxnfc_field & flag) -+ for (i = 0; i < priv->num_hash_fields; i++) { -+ if (priv->hash_fields[i].cls_prot == prot && -+ priv->hash_fields[i].cls_field == field) - return off; -- if (cls_is_enabled(net_dev, hash_fields[i].rxnfc_field)) -- off += hash_fields[i].size; -+ off += priv->hash_fields[i].size; - } - - return -1; - } - --static u8 cls_key_size(struct net_device *net_dev) -+static u8 cls_key_size(struct dpaa2_eth_priv *priv) - { - u8 i, size = 0; - -- for (i = 0; i < ARRAY_SIZE(hash_fields); i++) { -- if (!cls_is_enabled(net_dev, hash_fields[i].rxnfc_field)) -- continue; -- size += hash_fields[i].size; -- } -+ for (i = 0; i < priv->num_hash_fields; i++) -+ size += priv->hash_fields[i].size; - - return size; - } - --static u8 cls_max_key_size(struct net_device *net_dev) -+void check_cls_support(struct dpaa2_eth_priv *priv) - { -- u8 i, size = 0; -+ u8 key_size = cls_key_size(priv); -+ struct device *dev = priv->net_dev->dev.parent; -+ -+ if (dpaa2_eth_hash_enabled(priv)) { -+ if (priv->dpni_attrs.max_dist_key_size < key_size) { -+ dev_dbg(dev, "max_dist_key_size = %d, expected %d. Hashing and steering are disabled\n", -+ priv->dpni_attrs.max_dist_key_size, -+ key_size); -+ goto disable_cls; -+ } -+ if (priv->num_hash_fields > DPKG_MAX_NUM_OF_EXTRACTS) { -+ dev_dbg(dev, "Too many key fields (max = %d). Hashing and steering are disabled\n", -+ DPKG_MAX_NUM_OF_EXTRACTS); -+ goto disable_cls; -+ } -+ } - -- for (i = 0; i < ARRAY_SIZE(hash_fields); i++) -- size += hash_fields[i].size; -+ if (dpaa2_eth_fs_enabled(priv)) { -+ if (!dpaa2_eth_hash_enabled(priv)) { -+ dev_dbg(dev, "DPNI_OPT_DIST_HASH option missing. Steering is disabled\n"); -+ goto disable_cls; -+ } -+ if (!dpaa2_eth_fs_mask_enabled(priv)) { -+ dev_dbg(dev, "Key masks not supported. Steering is disabled\n"); -+ goto disable_fs; -+ } -+ } - -- return size; -+ return; -+ -+disable_cls: -+ priv->dpni_attrs.options &= ~DPNI_OPT_DIST_HASH; -+disable_fs: -+ priv->dpni_attrs.options &= ~(DPNI_OPT_DIST_FS | -+ DPNI_OPT_FS_MASK_SUPPORT); - } - --void check_fs_support(struct net_device *net_dev) -+static int prep_l4_rule(struct dpaa2_eth_priv *priv, -+ struct ethtool_tcpip4_spec *l4_value, -+ struct ethtool_tcpip4_spec *l4_mask, -+ void *key, void *mask, u8 l4_proto) - { -- u8 key_size = cls_max_key_size(net_dev); -- struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ int offset; - -- if (priv->dpni_attrs.options & DPNI_OPT_DIST_FS && -- priv->dpni_attrs.max_dist_key_size < key_size) { -- dev_err(&net_dev->dev, -- "max_dist_key_size = %d, expected %d. Steering is disabled\n", -- priv->dpni_attrs.max_dist_key_size, -- key_size); -- priv->dpni_attrs.options &= ~DPNI_OPT_DIST_FS; -+ if (l4_mask->tos) { -+ netdev_err(priv->net_dev, "ToS is not supported for IPv4 L4\n"); -+ return -EOPNOTSUPP; - } --} - --/* Set RX hash options -- * flags is a combination of RXH_ bits -- */ --int dpaa2_eth_set_hash(struct net_device *net_dev, u64 flags) --{ -- struct device *dev = net_dev->dev.parent; -- struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -- struct dpkg_profile_cfg cls_cfg; -- struct dpni_rx_tc_dist_cfg dist_cfg; -- u8 *dma_mem; -- u64 enabled_flags = 0; -- int i; -- int err = 0; -+ if (l4_mask->ip4src) { -+ offset = cls_key_off(priv, NET_PROT_IP, NH_FLD_IP_SRC); -+ *(u32 *)(key + offset) = l4_value->ip4src; -+ *(u32 *)(mask + offset) = l4_mask->ip4src; -+ } - -- if (!dpaa2_eth_hash_enabled(priv)) { -- dev_err(dev, "Hashing support is not enabled\n"); -- return -EOPNOTSUPP; -+ if (l4_mask->ip4dst) { -+ offset = cls_key_off(priv, NET_PROT_IP, NH_FLD_IP_DST); -+ *(u32 *)(key + offset) = l4_value->ip4dst; -+ *(u32 *)(mask + offset) = l4_mask->ip4dst; - } - -- if (flags & ~DPAA2_RXH_SUPPORTED) { -- /* RXH_DISCARD is not supported */ -- dev_err(dev, "unsupported option selected, supported options are: mvtsdfn\n"); -- return -EOPNOTSUPP; -+ if (l4_mask->psrc) { -+ offset = cls_key_off(priv, NET_PROT_UDP, NH_FLD_UDP_PORT_SRC); -+ *(u32 *)(key + offset) = l4_value->psrc; -+ *(u32 *)(mask + offset) = l4_mask->psrc; - } - -- memset(&cls_cfg, 0, sizeof(cls_cfg)); -+ if (l4_mask->pdst) { -+ offset = cls_key_off(priv, NET_PROT_UDP, NH_FLD_UDP_PORT_DST); -+ *(u32 *)(key + offset) = l4_value->pdst; -+ *(u32 *)(mask + offset) = l4_mask->pdst; -+ } - -- for (i = 0; i < ARRAY_SIZE(hash_fields); i++) { -- struct dpkg_extract *key = -- &cls_cfg.extracts[cls_cfg.num_extracts]; -+ /* Only apply the rule for the user-specified L4 protocol -+ * and if ethertype matches IPv4 -+ */ -+ offset = cls_key_off(priv, NET_PROT_ETH, NH_FLD_ETH_TYPE); -+ *(u16 *)(key + offset) = htons(ETH_P_IP); -+ *(u16 *)(mask + offset) = 0xFFFF; - -- if (!(flags & hash_fields[i].rxnfc_field)) -- continue; -+ offset = cls_key_off(priv, NET_PROT_IP, NH_FLD_IP_PROTO); -+ *(u8 *)(key + offset) = l4_proto; -+ *(u8 *)(mask + offset) = 0xFF; - -- if (cls_cfg.num_extracts >= DPKG_MAX_NUM_OF_EXTRACTS) { -- dev_err(dev, "error adding key extraction rule, too many rules?\n"); -- return -E2BIG; -- } -+ /* TODO: check IP version */ - -- key->type = DPKG_EXTRACT_FROM_HDR; -- key->extract.from_hdr.prot = hash_fields[i].cls_prot; -- key->extract.from_hdr.type = DPKG_FULL_FIELD; -- key->extract.from_hdr.field = hash_fields[i].cls_field; -- cls_cfg.num_extracts++; -+ return 0; -+} -+ -+static int prep_eth_rule(struct dpaa2_eth_priv *priv, -+ struct ethhdr *eth_value, struct ethhdr *eth_mask, -+ void *key, void *mask) -+{ -+ int offset; - -- enabled_flags |= hash_fields[i].rxnfc_field; -+ if (eth_mask->h_proto) { -+ netdev_err(priv->net_dev, "Ethertype is not supported!\n"); -+ return -EOPNOTSUPP; - } - -- dma_mem = kzalloc(DPAA2_CLASSIFIER_DMA_SIZE, GFP_DMA | GFP_KERNEL); -- if (!dma_mem) -- return -ENOMEM; -+ if (!is_zero_ether_addr(eth_mask->h_source)) { -+ offset = cls_key_off(priv, NET_PROT_ETH, NH_FLD_ETH_SA); -+ ether_addr_copy(key + offset, eth_value->h_source); -+ ether_addr_copy(mask + offset, eth_mask->h_source); -+ } - -- err = dpni_prepare_key_cfg(&cls_cfg, dma_mem); -- if (err) { -- dev_err(dev, "dpni_prepare_key_cfg error %d", err); -- return err; -+ if (!is_zero_ether_addr(eth_mask->h_dest)) { -+ offset = cls_key_off(priv, NET_PROT_ETH, NH_FLD_ETH_DA); -+ ether_addr_copy(key + offset, eth_value->h_dest); -+ ether_addr_copy(mask + offset, eth_mask->h_dest); - } - -- memset(&dist_cfg, 0, sizeof(dist_cfg)); -+ return 0; -+} - -- /* Prepare for setting the rx dist */ -- dist_cfg.key_cfg_iova = dma_map_single(net_dev->dev.parent, dma_mem, -- DPAA2_CLASSIFIER_DMA_SIZE, -- DMA_TO_DEVICE); -- if (dma_mapping_error(net_dev->dev.parent, dist_cfg.key_cfg_iova)) { -- dev_err(dev, "DMA mapping failed\n"); -- kfree(dma_mem); -- return -ENOMEM; -+static int prep_user_ip_rule(struct dpaa2_eth_priv *priv, -+ struct ethtool_usrip4_spec *uip_value, -+ struct ethtool_usrip4_spec *uip_mask, -+ void *key, void *mask) -+{ -+ int offset; -+ -+ if (uip_mask->tos) -+ return -EOPNOTSUPP; -+ -+ if (uip_mask->ip4src) { -+ offset = cls_key_off(priv, NET_PROT_IP, NH_FLD_IP_SRC); -+ *(u32 *)(key + offset) = uip_value->ip4src; -+ *(u32 *)(mask + offset) = uip_mask->ip4src; - } - -- dist_cfg.dist_size = dpaa2_eth_queue_count(priv); -- if (dpaa2_eth_fs_enabled(priv)) { -- dist_cfg.dist_mode = DPNI_DIST_MODE_FS; -- dist_cfg.fs_cfg.miss_action = DPNI_FS_MISS_HASH; -- } else { -- dist_cfg.dist_mode = DPNI_DIST_MODE_HASH; -+ if (uip_mask->ip4dst) { -+ offset = cls_key_off(priv, NET_PROT_IP, NH_FLD_IP_DST); -+ *(u32 *)(key + offset) = uip_value->ip4dst; -+ *(u32 *)(mask + offset) = uip_mask->ip4dst; - } - -- err = dpni_set_rx_tc_dist(priv->mc_io, 0, priv->mc_token, 0, &dist_cfg); -- dma_unmap_single(net_dev->dev.parent, dist_cfg.key_cfg_iova, -- DPAA2_CLASSIFIER_DMA_SIZE, DMA_TO_DEVICE); -- kfree(dma_mem); -- if (err) { -- dev_err(dev, "dpni_set_rx_tc_dist() error %d\n", err); -- return err; -+ if (uip_mask->proto) { -+ offset = cls_key_off(priv, NET_PROT_IP, NH_FLD_IP_PROTO); -+ *(u32 *)(key + offset) = uip_value->proto; -+ *(u32 *)(mask + offset) = uip_mask->proto; -+ } -+ if (uip_mask->l4_4_bytes) { -+ offset = cls_key_off(priv, NET_PROT_UDP, NH_FLD_UDP_PORT_SRC); -+ *(u16 *)(key + offset) = uip_value->l4_4_bytes << 16; -+ *(u16 *)(mask + offset) = uip_mask->l4_4_bytes << 16; -+ -+ offset = cls_key_off(priv, NET_PROT_UDP, NH_FLD_UDP_PORT_DST); -+ *(u16 *)(key + offset) = uip_value->l4_4_bytes & 0xFFFF; -+ *(u16 *)(mask + offset) = uip_mask->l4_4_bytes & 0xFFFF; - } - -- priv->rx_hash_fields = enabled_flags; -+ /* Ethertype must be IP */ -+ offset = cls_key_off(priv, NET_PROT_ETH, NH_FLD_ETH_TYPE); -+ *(u16 *)(key + offset) = htons(ETH_P_IP); -+ *(u16 *)(mask + offset) = 0xFFFF; -+ -+ return 0; -+} -+ -+static int prep_ext_rule(struct dpaa2_eth_priv *priv, -+ struct ethtool_flow_ext *ext_value, -+ struct ethtool_flow_ext *ext_mask, -+ void *key, void *mask) -+{ -+ int offset; -+ -+ if (ext_mask->vlan_etype) -+ return -EOPNOTSUPP; -+ -+ if (ext_mask->vlan_tci) { -+ offset = cls_key_off(priv, NET_PROT_VLAN, NH_FLD_VLAN_TCI); -+ *(u16 *)(key + offset) = ext_value->vlan_tci; -+ *(u16 *)(mask + offset) = ext_mask->vlan_tci; -+ } -+ -+ return 0; -+} -+ -+static int prep_mac_ext_rule(struct dpaa2_eth_priv *priv, -+ struct ethtool_flow_ext *ext_value, -+ struct ethtool_flow_ext *ext_mask, -+ void *key, void *mask) -+{ -+ int offset; -+ -+ if (!is_zero_ether_addr(ext_mask->h_dest)) { -+ offset = cls_key_off(priv, NET_PROT_ETH, NH_FLD_ETH_DA); -+ ether_addr_copy(key + offset, ext_value->h_dest); -+ ether_addr_copy(mask + offset, ext_mask->h_dest); -+ } - - return 0; - } -@@ -501,140 +499,56 @@ static int prep_cls_rule(struct net_device *net_dev, - struct ethtool_rx_flow_spec *fs, - void *key) - { -- struct ethtool_tcpip4_spec *l4ip4_h, *l4ip4_m; -- struct ethhdr *eth_h, *eth_m; -- struct ethtool_flow_ext *ext_h, *ext_m; -- const u8 key_size = cls_key_size(net_dev); -+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ const u8 key_size = cls_key_size(priv); - void *msk = key + key_size; -+ int err; - - memset(key, 0, key_size * 2); - -- /* This code is a major mess, it has to be cleaned up after the -- * classification mask issue is fixed and key format will be made static -- */ -- - switch (fs->flow_type & 0xff) { - case TCP_V4_FLOW: -- l4ip4_h = &fs->h_u.tcp_ip4_spec; -- l4ip4_m = &fs->m_u.tcp_ip4_spec; -- /* TODO: ethertype to match IPv4 and protocol to match TCP */ -- goto l4ip4; -- -+ err = prep_l4_rule(priv, &fs->h_u.tcp_ip4_spec, -+ &fs->m_u.tcp_ip4_spec, key, msk, -+ IPPROTO_TCP); -+ break; - case UDP_V4_FLOW: -- l4ip4_h = &fs->h_u.udp_ip4_spec; -- l4ip4_m = &fs->m_u.udp_ip4_spec; -- goto l4ip4; -- -+ err = prep_l4_rule(priv, &fs->h_u.udp_ip4_spec, -+ &fs->m_u.udp_ip4_spec, key, msk, -+ IPPROTO_UDP); -+ break; - case SCTP_V4_FLOW: -- l4ip4_h = &fs->h_u.sctp_ip4_spec; -- l4ip4_m = &fs->m_u.sctp_ip4_spec; -- --l4ip4: -- if (l4ip4_m->tos) { -- netdev_err(net_dev, -- "ToS is not supported for IPv4 L4\n"); -- return -EOPNOTSUPP; -- } -- if (l4ip4_m->ip4src && !cls_is_enabled(net_dev, RXH_IP_SRC)) { -- netdev_err(net_dev, "IP SRC not supported!\n"); -- return -EOPNOTSUPP; -- } -- if (l4ip4_m->ip4dst && !cls_is_enabled(net_dev, RXH_IP_DST)) { -- netdev_err(net_dev, "IP DST not supported!\n"); -- return -EOPNOTSUPP; -- } -- if (l4ip4_m->psrc && !cls_is_enabled(net_dev, RXH_L4_B_0_1)) { -- netdev_err(net_dev, "PSRC not supported, ignored\n"); -- return -EOPNOTSUPP; -- } -- if (l4ip4_m->pdst && !cls_is_enabled(net_dev, RXH_L4_B_2_3)) { -- netdev_err(net_dev, "PDST not supported, ignored\n"); -- return -EOPNOTSUPP; -- } -- -- if (cls_is_enabled(net_dev, RXH_IP_SRC)) { -- *(u32 *)(key + cls_key_off(net_dev, RXH_IP_SRC)) -- = l4ip4_h->ip4src; -- *(u32 *)(msk + cls_key_off(net_dev, RXH_IP_SRC)) -- = l4ip4_m->ip4src; -- } -- if (cls_is_enabled(net_dev, RXH_IP_DST)) { -- *(u32 *)(key + cls_key_off(net_dev, RXH_IP_DST)) -- = l4ip4_h->ip4dst; -- *(u32 *)(msk + cls_key_off(net_dev, RXH_IP_DST)) -- = l4ip4_m->ip4dst; -- } -- -- if (cls_is_enabled(net_dev, RXH_L4_B_0_1)) { -- *(u32 *)(key + cls_key_off(net_dev, RXH_L4_B_0_1)) -- = l4ip4_h->psrc; -- *(u32 *)(msk + cls_key_off(net_dev, RXH_L4_B_0_1)) -- = l4ip4_m->psrc; -- } -- -- if (cls_is_enabled(net_dev, RXH_L4_B_2_3)) { -- *(u32 *)(key + cls_key_off(net_dev, RXH_L4_B_2_3)) -- = l4ip4_h->pdst; -- *(u32 *)(msk + cls_key_off(net_dev, RXH_L4_B_2_3)) -- = l4ip4_m->pdst; -- } -+ err = prep_l4_rule(priv, &fs->h_u.sctp_ip4_spec, -+ &fs->m_u.sctp_ip4_spec, key, msk, -+ IPPROTO_SCTP); - break; -- - case ETHER_FLOW: -- eth_h = &fs->h_u.ether_spec; -- eth_m = &fs->m_u.ether_spec; -- -- if (eth_m->h_proto) { -- netdev_err(net_dev, "Ethertype is not supported!\n"); -- return -EOPNOTSUPP; -- } -- -- if (!is_zero_ether_addr(eth_m->h_source)) { -- netdev_err(net_dev, "ETH SRC is not supported!\n"); -- return -EOPNOTSUPP; -- } -- -- if (cls_is_enabled(net_dev, RXH_L2DA)) { -- ether_addr_copy(key + cls_key_off(net_dev, RXH_L2DA), -- eth_h->h_dest); -- ether_addr_copy(msk + cls_key_off(net_dev, RXH_L2DA), -- eth_m->h_dest); -- } else { -- if (!is_zero_ether_addr(eth_m->h_dest)) { -- netdev_err(net_dev, -- "ETH DST is not supported!\n"); -- return -EOPNOTSUPP; -- } -- } -+ err = prep_eth_rule(priv, &fs->h_u.ether_spec, -+ &fs->m_u.ether_spec, key, msk); -+ break; -+ case IP_USER_FLOW: -+ err = prep_user_ip_rule(priv, &fs->h_u.usr_ip4_spec, -+ &fs->m_u.usr_ip4_spec, key, msk); - break; -- - default: -- /* TODO: IP user flow, AH, ESP */ -+ /* TODO: AH, ESP */ - return -EOPNOTSUPP; - } -+ if (err) -+ return err; - - if (fs->flow_type & FLOW_EXT) { -- /* TODO: ETH data, VLAN ethertype, VLAN TCI .. */ -- return -EOPNOTSUPP; -+ err = prep_ext_rule(priv, &fs->h_ext, &fs->m_ext, key, msk); -+ if (err) -+ return err; - } - - if (fs->flow_type & FLOW_MAC_EXT) { -- ext_h = &fs->h_ext; -- ext_m = &fs->m_ext; -- -- if (cls_is_enabled(net_dev, RXH_L2DA)) { -- ether_addr_copy(key + cls_key_off(net_dev, RXH_L2DA), -- ext_h->h_dest); -- ether_addr_copy(msk + cls_key_off(net_dev, RXH_L2DA), -- ext_m->h_dest); -- } else { -- if (!is_zero_ether_addr(ext_m->h_dest)) { -- netdev_err(net_dev, -- "ETH DST is not supported!\n"); -- return -EOPNOTSUPP; -- } -- } -+ err = prep_mac_ext_rule(priv, &fs->h_ext, &fs->m_ext, key, msk); -+ if (err) -+ return err; - } -+ - return 0; - } - -@@ -643,6 +557,7 @@ static int do_cls(struct net_device *net_dev, - bool add) - { - struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -+ struct device *dev = net_dev->dev.parent; - const int rule_cnt = DPAA2_CLASSIFIER_ENTRY_COUNT; - struct dpni_rule_cfg rule_cfg; - void *dma_mem; -@@ -660,7 +575,7 @@ static int do_cls(struct net_device *net_dev, - return -EINVAL; - - memset(&rule_cfg, 0, sizeof(rule_cfg)); -- rule_cfg.key_size = cls_key_size(net_dev); -+ rule_cfg.key_size = cls_key_size(priv); - - /* allocate twice the key size, for the actual key and for mask */ - dma_mem = kzalloc(rule_cfg.key_size * 2, GFP_DMA | GFP_KERNEL); -@@ -671,27 +586,12 @@ static int do_cls(struct net_device *net_dev, - if (err) - goto err_free_mem; - -- rule_cfg.key_iova = dma_map_single(net_dev->dev.parent, dma_mem, -+ rule_cfg.key_iova = dma_map_single(dev, dma_mem, - rule_cfg.key_size * 2, - DMA_TO_DEVICE); - - rule_cfg.mask_iova = rule_cfg.key_iova + rule_cfg.key_size; - -- if (!(priv->dpni_attrs.options & DPNI_OPT_FS_MASK_SUPPORT)) { -- int i; -- u8 *mask = dma_mem + rule_cfg.key_size; -- -- /* check that nothing is masked out, otherwise it won't work */ -- for (i = 0; i < rule_cfg.key_size; i++) { -- if (mask[i] == 0xff) -- continue; -- netdev_err(net_dev, "dev does not support masking!\n"); -- err = -EOPNOTSUPP; -- goto err_free_mem; -- } -- rule_cfg.mask_iova = 0; -- } -- - /* No way to control rule order in firmware */ - if (add) - err = dpni_add_fs_entry(priv->mc_io, 0, priv->mc_token, 0, -@@ -700,10 +600,10 @@ static int do_cls(struct net_device *net_dev, - err = dpni_remove_fs_entry(priv->mc_io, 0, priv->mc_token, 0, - &rule_cfg); - -- dma_unmap_single(net_dev->dev.parent, rule_cfg.key_iova, -+ dma_unmap_single(dev, rule_cfg.key_iova, - rule_cfg.key_size * 2, DMA_TO_DEVICE); - if (err) { -- netdev_err(net_dev, "dpaa2_add_cls() error %d\n", err); -+ netdev_err(net_dev, "dpaa2_add/remove_cls() error %d\n", err); - goto err_free_mem; - } - -@@ -746,40 +646,12 @@ static int del_cls(struct net_device *net_dev, int location) - return 0; - } - --static void clear_cls(struct net_device *net_dev) --{ -- struct dpaa2_eth_priv *priv = netdev_priv(net_dev); -- int i, err; -- -- for (i = 0; i < DPAA2_CLASSIFIER_ENTRY_COUNT; i++) { -- if (!priv->cls_rule[i].in_use) -- continue; -- -- err = del_cls(net_dev, i); -- if (err) -- netdev_warn(net_dev, -- "err trying to delete classification entry %d\n", -- i); -- } --} -- - static int dpaa2_eth_set_rxnfc(struct net_device *net_dev, - struct ethtool_rxnfc *rxnfc) - { - int err = 0; - - switch (rxnfc->cmd) { -- case ETHTOOL_SRXFH: -- /* first off clear ALL classification rules, chaging key -- * composition will break them anyway -- */ -- clear_cls(net_dev); -- /* we purposely ignore cmd->flow_type for now, because the -- * classifier only supports a single set of fields for all -- * protocols -- */ -- err = dpaa2_eth_set_hash(net_dev, rxnfc->data); -- break; - case ETHTOOL_SRXCLSRLINS: - err = add_cls(net_dev, &rxnfc->fs); - break; -@@ -804,11 +676,10 @@ static int dpaa2_eth_get_rxnfc(struct net_device *net_dev, - - switch (rxnfc->cmd) { - case ETHTOOL_GRXFH: -- /* we purposely ignore cmd->flow_type for now, because the -- * classifier only supports a single set of fields for all -- * protocols -+ /* we purposely ignore cmd->flow_type, because the hashing key -+ * is the same (and fixed) for all protocols - */ -- rxnfc->data = priv->rx_hash_fields; -+ rxnfc->data = priv->rx_flow_hash; - break; - - case ETHTOOL_GRXRINGS: -diff --git a/drivers/staging/fsl-dpaa2/mac/mac.c b/drivers/staging/fsl-dpaa2/mac/mac.c -index 366ad4c..fe16b8b 100644 ---- a/drivers/staging/fsl-dpaa2/mac/mac.c -+++ b/drivers/staging/fsl-dpaa2/mac/mac.c -@@ -120,7 +120,7 @@ static void dpaa2_mac_link_changed(struct net_device *netdev) - phy_print_status(phydev); - } - -- /* We must call into the MC firmware at all times, because we don't know -+ /* We must interrogate MC at all times, because we don't know - * when and whether a potential DPNI may have read the link state. - */ - err = dpmac_set_link_state(priv->mc_dev->mc_io, 0, -@@ -532,7 +532,7 @@ static int dpaa2_mac_probe(struct fsl_mc_device *mc_dev) - goto err_close; - } - -- dev_info_once(dev, "Using DPMAC API %d.%d\n", -+ dev_warn(dev, "Using DPMAC API %d.%d\n", - priv->attr.version.major, priv->attr.version.minor); - - /* Look up the DPMAC node in the device-tree. */ -diff --git a/drivers/staging/fsl-mc/bus/dprc-driver.c b/drivers/staging/fsl-mc/bus/dprc-driver.c -index f8d8cbe..5b6fa1c 100644 ---- a/drivers/staging/fsl-mc/bus/dprc-driver.c -+++ b/drivers/staging/fsl-mc/bus/dprc-driver.c -@@ -1078,7 +1078,7 @@ int __init dprc_driver_init(void) - return fsl_mc_driver_register(&dprc_driver); - } - --void __exit dprc_driver_exit(void) -+void dprc_driver_exit(void) - { - fsl_mc_driver_unregister(&dprc_driver); - } -diff --git a/drivers/staging/fsl-mc/include/mc-private.h b/drivers/staging/fsl-mc/include/mc-private.h -index 1246ca8..58ed441 100644 ---- a/drivers/staging/fsl-mc/include/mc-private.h -+++ b/drivers/staging/fsl-mc/include/mc-private.h -@@ -143,7 +143,7 @@ int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev, - - int __init dprc_driver_init(void); - --void __exit dprc_driver_exit(void); -+void dprc_driver_exit(void); - - int __init fsl_mc_allocator_driver_init(void); - -diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c -index f951b75..600a137 100644 ---- a/drivers/usb/host/xhci.c -+++ b/drivers/usb/host/xhci.c -@@ -1685,8 +1685,10 @@ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev, - cpu_to_le32(EP_STATE_DISABLED)) || - le32_to_cpu(ctrl_ctx->drop_flags) & - xhci_get_endpoint_flag(&ep->desc)) { -- xhci_warn(xhci, "xHCI %s called with disabled ep %p\n", -- __func__, ep); -+ /* Do not warn when called after a usb_device_reset */ -+ if (xhci->devs[udev->slot_id]->eps[ep_index].ring != NULL) -+ xhci_warn(xhci, "xHCI %s called with disabled ep %p\n", -+ __func__, ep); - return 0; - } - -diff --git a/include/linux/fsl/guts.h b/include/linux/fsl/guts.h -index 84d971f..f13b12e 100644 ---- a/include/linux/fsl/guts.h -+++ b/include/linux/fsl/guts.h -@@ -29,83 +29,86 @@ - * #ifdefs. - */ - struct ccsr_guts { -- __be32 porpllsr; /* 0x.0000 - POR PLL Ratio Status Register */ -- __be32 porbmsr; /* 0x.0004 - POR Boot Mode Status Register */ -- __be32 porimpscr; /* 0x.0008 - POR I/O Impedance Status and Control Register */ -- __be32 pordevsr; /* 0x.000c - POR I/O Device Status Register */ -- __be32 pordbgmsr; /* 0x.0010 - POR Debug Mode Status Register */ -- __be32 pordevsr2; /* 0x.0014 - POR device status register 2 */ -+ u32 porpllsr; /* 0x.0000 - POR PLL Ratio Status Register */ -+ u32 porbmsr; /* 0x.0004 - POR Boot Mode Status Register */ -+ u32 porimpscr; /* 0x.0008 - POR I/O Impedance Status and Control Register */ -+ u32 pordevsr; /* 0x.000c - POR I/O Device Status Register */ -+ u32 pordbgmsr; /* 0x.0010 - POR Debug Mode Status Register */ -+ u32 pordevsr2; /* 0x.0014 - POR device status register 2 */ - u8 res018[0x20 - 0x18]; -- __be32 porcir; /* 0x.0020 - POR Configuration Information Register */ -+ u32 porcir; /* 0x.0020 - POR Configuration Information Register */ - u8 res024[0x30 - 0x24]; -- __be32 gpiocr; /* 0x.0030 - GPIO Control Register */ -+ u32 gpiocr; /* 0x.0030 - GPIO Control Register */ - u8 res034[0x40 - 0x34]; -- __be32 gpoutdr; /* 0x.0040 - General-Purpose Output Data Register */ -+ u32 gpoutdr; /* 0x.0040 - General-Purpose Output Data Register */ - u8 res044[0x50 - 0x44]; -- __be32 gpindr; /* 0x.0050 - General-Purpose Input Data Register */ -+ u32 gpindr; /* 0x.0050 - General-Purpose Input Data Register */ - u8 res054[0x60 - 0x54]; -- __be32 pmuxcr; /* 0x.0060 - Alternate Function Signal Multiplex Control */ -- __be32 pmuxcr2; /* 0x.0064 - Alternate function signal multiplex control 2 */ -- __be32 dmuxcr; /* 0x.0068 - DMA Mux Control Register */ -+ u32 pmuxcr; /* 0x.0060 - Alternate Function Signal Multiplex Control */ -+ u32 pmuxcr2; /* 0x.0064 - Alternate function signal multiplex control 2 */ -+ u32 dmuxcr; /* 0x.0068 - DMA Mux Control Register */ - u8 res06c[0x70 - 0x6c]; -- __be32 devdisr; /* 0x.0070 - Device Disable Control */ -+ u32 devdisr; /* 0x.0070 - Device Disable Control */ - #define CCSR_GUTS_DEVDISR_TB1 0x00001000 - #define CCSR_GUTS_DEVDISR_TB0 0x00004000 -- __be32 devdisr2; /* 0x.0074 - Device Disable Control 2 */ -+ u32 devdisr2; /* 0x.0074 - Device Disable Control 2 */ - u8 res078[0x7c - 0x78]; -- __be32 pmjcr; /* 0x.007c - 4 Power Management Jog Control Register */ -- __be32 powmgtcsr; /* 0x.0080 - Power Management Status and Control Register */ -- __be32 pmrccr; /* 0x.0084 - Power Management Reset Counter Configuration Register */ -- __be32 pmpdccr; /* 0x.0088 - Power Management Power Down Counter Configuration Register */ -- __be32 pmcdr; /* 0x.008c - 4Power management clock disable register */ -- __be32 mcpsumr; /* 0x.0090 - Machine Check Summary Register */ -- __be32 rstrscr; /* 0x.0094 - Reset Request Status and Control Register */ -- __be32 ectrstcr; /* 0x.0098 - Exception reset control register */ -- __be32 autorstsr; /* 0x.009c - Automatic reset status register */ -- __be32 pvr; /* 0x.00a0 - Processor Version Register */ -- __be32 svr; /* 0x.00a4 - System Version Register */ -+ u32 pmjcr; /* 0x.007c - 4 Power Management Jog Control Register */ -+ u32 powmgtcsr; /* 0x.0080 - Power Management Status and Control Register */ -+ u32 pmrccr; /* 0x.0084 - Power Management Reset Counter Configuration Register */ -+ u32 pmpdccr; /* 0x.0088 - Power Management Power Down Counter Configuration Register */ -+ u32 pmcdr; /* 0x.008c - 4Power management clock disable register */ -+ u32 mcpsumr; /* 0x.0090 - Machine Check Summary Register */ -+ u32 rstrscr; /* 0x.0094 - Reset Request Status and Control Register */ -+ u32 ectrstcr; /* 0x.0098 - Exception reset control register */ -+ u32 autorstsr; /* 0x.009c - Automatic reset status register */ -+ u32 pvr; /* 0x.00a0 - Processor Version Register */ -+ u32 svr; /* 0x.00a4 - System Version Register */ - u8 res0a8[0xb0 - 0xa8]; -- __be32 rstcr; /* 0x.00b0 - Reset Control Register */ -+ u32 rstcr; /* 0x.00b0 - Reset Control Register */ - u8 res0b4[0xc0 - 0xb4]; -- __be32 iovselsr; /* 0x.00c0 - I/O voltage select status register -+ u32 iovselsr; /* 0x.00c0 - I/O voltage select status register - Called 'elbcvselcr' on 86xx SOCs */ - u8 res0c4[0x100 - 0xc4]; -- __be32 rcwsr[16]; /* 0x.0100 - Reset Control Word Status registers -+ u32 rcwsr[16]; /* 0x.0100 - Reset Control Word Status registers - There are 16 registers */ - u8 res140[0x224 - 0x140]; -- __be32 iodelay1; /* 0x.0224 - IO delay control register 1 */ -- __be32 iodelay2; /* 0x.0228 - IO delay control register 2 */ -+ u32 iodelay1; /* 0x.0224 - IO delay control register 1 */ -+ u32 iodelay2; /* 0x.0228 - IO delay control register 2 */ - u8 res22c[0x604 - 0x22c]; -- __be32 pamubypenr; /* 0x.604 - PAMU bypass enable register */ -+ u32 pamubypenr; /* 0x.604 - PAMU bypass enable register */ - u8 res608[0x800 - 0x608]; -- __be32 clkdvdr; /* 0x.0800 - Clock Divide Register */ -+ u32 clkdvdr; /* 0x.0800 - Clock Divide Register */ - u8 res804[0x900 - 0x804]; -- __be32 ircr; /* 0x.0900 - Infrared Control Register */ -+ u32 ircr; /* 0x.0900 - Infrared Control Register */ - u8 res904[0x908 - 0x904]; -- __be32 dmacr; /* 0x.0908 - DMA Control Register */ -+ u32 dmacr; /* 0x.0908 - DMA Control Register */ - u8 res90c[0x914 - 0x90c]; -- __be32 elbccr; /* 0x.0914 - eLBC Control Register */ -+ u32 elbccr; /* 0x.0914 - eLBC Control Register */ - u8 res918[0xb20 - 0x918]; -- __be32 ddr1clkdr; /* 0x.0b20 - DDR1 Clock Disable Register */ -- __be32 ddr2clkdr; /* 0x.0b24 - DDR2 Clock Disable Register */ -- __be32 ddrclkdr; /* 0x.0b28 - DDR Clock Disable Register */ -+ u32 ddr1clkdr; /* 0x.0b20 - DDR1 Clock Disable Register */ -+ u32 ddr2clkdr; /* 0x.0b24 - DDR2 Clock Disable Register */ -+ u32 ddrclkdr; /* 0x.0b28 - DDR Clock Disable Register */ - u8 resb2c[0xe00 - 0xb2c]; -- __be32 clkocr; /* 0x.0e00 - Clock Out Select Register */ -+ u32 clkocr; /* 0x.0e00 - Clock Out Select Register */ - u8 rese04[0xe10 - 0xe04]; -- __be32 ddrdllcr; /* 0x.0e10 - DDR DLL Control Register */ -+ u32 ddrdllcr; /* 0x.0e10 - DDR DLL Control Register */ - u8 rese14[0xe20 - 0xe14]; -- __be32 lbcdllcr; /* 0x.0e20 - LBC DLL Control Register */ -- __be32 cpfor; /* 0x.0e24 - L2 charge pump fuse override register */ -+ u32 lbcdllcr; /* 0x.0e20 - LBC DLL Control Register */ -+ u32 cpfor; /* 0x.0e24 - L2 charge pump fuse override register */ - u8 rese28[0xf04 - 0xe28]; -- __be32 srds1cr0; /* 0x.0f04 - SerDes1 Control Register 0 */ -- __be32 srds1cr1; /* 0x.0f08 - SerDes1 Control Register 0 */ -+ u32 srds1cr0; /* 0x.0f04 - SerDes1 Control Register 0 */ -+ u32 srds1cr1; /* 0x.0f08 - SerDes1 Control Register 0 */ - u8 resf0c[0xf2c - 0xf0c]; -- __be32 itcr; /* 0x.0f2c - Internal transaction control register */ -+ u32 itcr; /* 0x.0f2c - Internal transaction control register */ - u8 resf30[0xf40 - 0xf30]; -- __be32 srds2cr0; /* 0x.0f40 - SerDes2 Control Register 0 */ -- __be32 srds2cr1; /* 0x.0f44 - SerDes2 Control Register 0 */ -+ u32 srds2cr0; /* 0x.0f40 - SerDes2 Control Register 0 */ -+ u32 srds2cr1; /* 0x.0f44 - SerDes2 Control Register 0 */ - } __attribute__ ((packed)); - -+#ifdef CONFIG_FSL_GUTS -+extern u32 guts_get_svr(void); -+#endif - - /* Alternate function signal multiplex control */ - #define MPC85xx_PMUXCR_QE(x) (0x8000 >> (x)) -diff --git a/include/linux/fsl/svr.h b/include/linux/fsl/svr.h -new file mode 100644 -index 0000000..8d13836 ---- /dev/null -+++ b/include/linux/fsl/svr.h -@@ -0,0 +1,95 @@ -+/* -+ * MPC85xx cpu type detection -+ * -+ * Copyright 2011-2012 Freescale Semiconductor, Inc. -+ * -+ * This 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. -+ */ -+ -+#ifndef FSL_SVR_H -+#define FSL_SVR_H -+ -+#define SVR_REV(svr) ((svr) & 0xFF) /* SOC design resision */ -+#define SVR_MAJ(svr) (((svr) >> 4) & 0xF) /* Major revision field*/ -+#define SVR_MIN(svr) (((svr) >> 0) & 0xF) /* Minor revision field*/ -+ -+/* Some parts define SVR[0:23] as the SOC version */ -+#define SVR_SOC_VER(svr) (((svr) >> 8) & 0xFFF7FF) /* SOC Version fields */ -+ -+#define SVR_8533 0x803400 -+#define SVR_8535 0x803701 -+#define SVR_8536 0x803700 -+#define SVR_8540 0x803000 -+#define SVR_8541 0x807200 -+#define SVR_8543 0x803200 -+#define SVR_8544 0x803401 -+#define SVR_8545 0x803102 -+#define SVR_8547 0x803101 -+#define SVR_8548 0x803100 -+#define SVR_8555 0x807100 -+#define SVR_8560 0x807000 -+#define SVR_8567 0x807501 -+#define SVR_8568 0x807500 -+#define SVR_8569 0x808000 -+#define SVR_8572 0x80E000 -+#define SVR_P1010 0x80F100 -+#define SVR_P1011 0x80E500 -+#define SVR_P1012 0x80E501 -+#define SVR_P1013 0x80E700 -+#define SVR_P1014 0x80F101 -+#define SVR_P1017 0x80F700 -+#define SVR_P1020 0x80E400 -+#define SVR_P1021 0x80E401 -+#define SVR_P1022 0x80E600 -+#define SVR_P1023 0x80F600 -+#define SVR_P1024 0x80E402 -+#define SVR_P1025 0x80E403 -+#define SVR_P2010 0x80E300 -+#define SVR_P2020 0x80E200 -+#define SVR_P2040 0x821000 -+#define SVR_P2041 0x821001 -+#define SVR_P3041 0x821103 -+#define SVR_P4040 0x820100 -+#define SVR_P4080 0x820000 -+#define SVR_P5010 0x822100 -+#define SVR_P5020 0x822000 -+#define SVR_P5021 0X820500 -+#define SVR_P5040 0x820400 -+#define SVR_T4240 0x824000 -+#define SVR_T4120 0x824001 -+#define SVR_T4160 0x824100 -+#define SVR_T4080 0x824102 -+#define SVR_C291 0x850000 -+#define SVR_C292 0x850020 -+#define SVR_C293 0x850030 -+#define SVR_B4860 0X868000 -+#define SVR_G4860 0x868001 -+#define SVR_G4060 0x868003 -+#define SVR_B4440 0x868100 -+#define SVR_G4440 0x868101 -+#define SVR_B4420 0x868102 -+#define SVR_B4220 0x868103 -+#define SVR_T1040 0x852000 -+#define SVR_T1041 0x852001 -+#define SVR_T1042 0x852002 -+#define SVR_T1020 0x852100 -+#define SVR_T1021 0x852101 -+#define SVR_T1022 0x852102 -+#define SVR_T2080 0x853000 -+#define SVR_T2081 0x853100 -+ -+#define SVR_8610 0x80A000 -+#define SVR_8641 0x809000 -+#define SVR_8641D 0x809001 -+ -+#define SVR_9130 0x860001 -+#define SVR_9131 0x860000 -+#define SVR_9132 0x861000 -+#define SVR_9232 0x861400 -+ -+#define SVR_Unknown 0xFFFFFF -+ -+#endif -diff --git a/include/linux/fsl_ifc.h b/include/linux/fsl_ifc.h -index 84d60cb..3f9778c 100644 ---- a/include/linux/fsl_ifc.h -+++ b/include/linux/fsl_ifc.h -@@ -29,7 +29,20 @@ - #include - #include - --#define FSL_IFC_BANK_COUNT 4 -+/* -+ * The actual number of banks implemented depends on the IFC version -+ * - IFC version 1.0 implements 4 banks. -+ * - IFC version 1.1 onward implements 8 banks. -+ */ -+#define FSL_IFC_BANK_COUNT 8 -+ -+#define FSL_IFC_VERSION_MASK 0x0F0F0000 -+#define FSL_IFC_VERSION_1_0_0 0x01000000 -+#define FSL_IFC_VERSION_1_1_0 0x01010000 -+#define FSL_IFC_VERSION_2_0_0 0x02000000 -+ -+#define PGOFFSET_64K (64*1024) -+#define PGOFFSET_4K (4*1024) - - /* - * CSPR - Chip Select Property Register -@@ -714,20 +727,26 @@ struct fsl_ifc_nand { - __be32 nand_evter_en; - u32 res17[0x2]; - __be32 nand_evter_intr_en; -- u32 res18[0x2]; -+ __be32 nand_vol_addr_stat; -+ u32 res18; - __be32 nand_erattr0; - __be32 nand_erattr1; - u32 res19[0x10]; - __be32 nand_fsr; -- u32 res20; -- __be32 nand_eccstat[4]; -- u32 res21[0x20]; -+ u32 res20[0x3]; -+ __be32 nand_eccstat[6]; -+ u32 res21[0x1c]; - __be32 nanndcr; - u32 res22[0x2]; - __be32 nand_autoboot_trgr; - u32 res23; - __be32 nand_mdr; -- u32 res24[0x5C]; -+ u32 res24[0x1C]; -+ __be32 nand_dll_lowcfg0; -+ __be32 nand_dll_lowcfg1; -+ u32 res25; -+ __be32 nand_dll_lowstat; -+ u32 res26[0x3c]; - }; - - /* -@@ -762,13 +781,12 @@ struct fsl_ifc_gpcm { - __be32 gpcm_erattr1; - __be32 gpcm_erattr2; - __be32 gpcm_stat; -- u32 res4[0x1F3]; - }; - - /* - * IFC Controller Registers - */ --struct fsl_ifc_regs { -+struct fsl_ifc_global { - __be32 ifc_rev; - u32 res1[0x2]; - struct { -@@ -776,39 +794,44 @@ struct fsl_ifc_regs { - __be32 cspr; - u32 res2; - } cspr_cs[FSL_IFC_BANK_COUNT]; -- u32 res3[0x19]; -+ u32 res3[0xd]; - struct { - __be32 amask; - u32 res4[0x2]; - } amask_cs[FSL_IFC_BANK_COUNT]; -- u32 res5[0x18]; -+ u32 res5[0xc]; - struct { - __be32 csor; - __be32 csor_ext; - u32 res6; - } csor_cs[FSL_IFC_BANK_COUNT]; -- u32 res7[0x18]; -+ u32 res7[0xc]; - struct { - __be32 ftim[4]; - u32 res8[0x8]; - } ftim_cs[FSL_IFC_BANK_COUNT]; -- u32 res9[0x60]; -+ u32 res9[0x30]; - __be32 rb_stat; -- u32 res10[0x2]; -+ __be32 rb_map; -+ __be32 wb_map; - __be32 ifc_gcr; -- u32 res11[0x2]; -+ u32 res10[0x2]; - __be32 cm_evter_stat; -- u32 res12[0x2]; -+ u32 res11[0x2]; - __be32 cm_evter_en; -- u32 res13[0x2]; -+ u32 res12[0x2]; - __be32 cm_evter_intr_en; -- u32 res14[0x2]; -+ u32 res13[0x2]; - __be32 cm_erattr0; - __be32 cm_erattr1; -- u32 res15[0x2]; -+ u32 res14[0x2]; - __be32 ifc_ccr; - __be32 ifc_csr; -- u32 res16[0x2EB]; -+ __be32 ddr_ccr_low; -+}; -+ -+ -+struct fsl_ifc_runtime { - struct fsl_ifc_nand ifc_nand; - struct fsl_ifc_nor ifc_nor; - struct fsl_ifc_gpcm ifc_gpcm; -@@ -822,17 +845,70 @@ extern int fsl_ifc_find(phys_addr_t addr_base); - struct fsl_ifc_ctrl { - /* device info */ - struct device *dev; -- struct fsl_ifc_regs __iomem *regs; -+ struct fsl_ifc_global __iomem *gregs; -+ struct fsl_ifc_runtime __iomem *rregs; - int irq; - int nand_irq; - spinlock_t lock; - void *nand; -+ int version; -+ int banks; - - u32 nand_stat; - wait_queue_head_t nand_wait; -+ bool little_endian; - }; - - extern struct fsl_ifc_ctrl *fsl_ifc_ctrl_dev; - -+static inline u32 ifc_in32(void __iomem *addr) -+{ -+ u32 val; -+ -+ if (fsl_ifc_ctrl_dev->little_endian) -+ val = ioread32(addr); -+ else -+ val = ioread32be(addr); -+ -+ return val; -+} -+ -+static inline u16 ifc_in16(void __iomem *addr) -+{ -+ u16 val; -+ -+ if (fsl_ifc_ctrl_dev->little_endian) -+ val = ioread16(addr); -+ else -+ val = ioread16be(addr); -+ -+ return val; -+} -+ -+static inline u8 ifc_in8(void __iomem *addr) -+{ -+ return ioread8(addr); -+} -+ -+static inline void ifc_out32(u32 val, void __iomem *addr) -+{ -+ if (fsl_ifc_ctrl_dev->little_endian) -+ iowrite32(val, addr); -+ else -+ iowrite32be(val, addr); -+} -+ -+static inline void ifc_out16(u16 val, void __iomem *addr) -+{ -+ if (fsl_ifc_ctrl_dev->little_endian) -+ iowrite16(val, addr); -+ else -+ iowrite16be(val, addr); -+} -+ -+static inline void ifc_out8(u8 val, void __iomem *addr) -+{ -+ iowrite8(val, addr); -+} - - #endif /* __ASM_FSL_IFC_H */ -diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h -index 69517a2..cbbe6a2 100644 ---- a/include/linux/interrupt.h -+++ b/include/linux/interrupt.h -@@ -356,6 +356,20 @@ static inline int disable_irq_wake(unsigned int irq) - return irq_set_irq_wake(irq, 0); - } - -+/* -+ * irq_get_irqchip_state/irq_set_irqchip_state specific flags -+ */ -+enum irqchip_irq_state { -+ IRQCHIP_STATE_PENDING, /* Is interrupt pending? */ -+ IRQCHIP_STATE_ACTIVE, /* Is interrupt in progress? */ -+ IRQCHIP_STATE_MASKED, /* Is interrupt masked? */ -+ IRQCHIP_STATE_LINE_LEVEL, /* Is IRQ line high? */ -+}; -+ -+extern int irq_get_irqchip_state(unsigned int irq, enum irqchip_irq_state which, -+ bool *state); -+extern int irq_set_irqchip_state(unsigned int irq, enum irqchip_irq_state which, -+ bool state); - - #ifdef CONFIG_IRQ_FORCED_THREADING - extern bool force_irqthreads; -diff --git a/include/linux/iommu.h b/include/linux/iommu.h -index 04229cb..7421bdf 100644 ---- a/include/linux/iommu.h -+++ b/include/linux/iommu.h -@@ -30,6 +30,7 @@ - #define IOMMU_WRITE (1 << 1) - #define IOMMU_CACHE (1 << 2) /* DMA cache coherency */ - #define IOMMU_NOEXEC (1 << 3) -+#define IOMMU_MMIO (1 << 4) /* Device memory access */ - - struct iommu_ops; - struct iommu_group; -diff --git a/include/linux/irq.h b/include/linux/irq.h -index 9ba173b..4931a8b 100644 ---- a/include/linux/irq.h -+++ b/include/linux/irq.h -@@ -30,6 +30,7 @@ - struct seq_file; - struct module; - struct msi_msg; -+enum irqchip_irq_state; - - /* - * IRQ line status. -@@ -324,6 +325,8 @@ static inline irq_hw_number_t irqd_to_hwirq(struct irq_data *d) - * irq_request_resources - * @irq_compose_msi_msg: optional to compose message content for MSI - * @irq_write_msi_msg: optional to write message content for MSI -+ * @irq_get_irqchip_state: return the internal state of an interrupt -+ * @irq_set_irqchip_state: set the internal state of a interrupt - * @flags: chip specific flags - */ - struct irq_chip { -@@ -363,6 +366,9 @@ struct irq_chip { - void (*irq_compose_msi_msg)(struct irq_data *data, struct msi_msg *msg); - void (*irq_write_msi_msg)(struct irq_data *data, struct msi_msg *msg); - -+ int (*irq_get_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool *state); -+ int (*irq_set_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool state); -+ - unsigned long flags; - }; - -@@ -460,6 +466,8 @@ extern void irq_chip_eoi_parent(struct irq_data *data); - extern int irq_chip_set_affinity_parent(struct irq_data *data, - const struct cpumask *dest, - bool force); -+extern int irq_chip_set_wake_parent(struct irq_data *data, unsigned int on); -+extern int irq_chip_set_type_parent(struct irq_data *data, unsigned int type); - #endif - - /* Handling of unhandled and spurious interrupts: */ -diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h -index da1aa15..36caf46 100644 ---- a/include/linux/irqchip/arm-gic-v3.h -+++ b/include/linux/irqchip/arm-gic-v3.h -@@ -270,6 +270,18 @@ - #define ICC_SRE_EL2_SRE (1 << 0) - #define ICC_SRE_EL2_ENABLE (1 << 3) - -+#define ICC_SGI1R_TARGET_LIST_SHIFT 0 -+#define ICC_SGI1R_TARGET_LIST_MASK (0xffff << ICC_SGI1R_TARGET_LIST_SHIFT) -+#define ICC_SGI1R_AFFINITY_1_SHIFT 16 -+#define ICC_SGI1R_AFFINITY_1_MASK (0xff << ICC_SGI1R_AFFINITY_1_SHIFT) -+#define ICC_SGI1R_SGI_ID_SHIFT 24 -+#define ICC_SGI1R_SGI_ID_MASK (0xff << ICC_SGI1R_SGI_ID_SHIFT) -+#define ICC_SGI1R_AFFINITY_2_SHIFT 32 -+#define ICC_SGI1R_AFFINITY_2_MASK (0xffULL << ICC_SGI1R_AFFINITY_1_SHIFT) -+#define ICC_SGI1R_IRQ_ROUTING_MODE_BIT 40 -+#define ICC_SGI1R_AFFINITY_3_SHIFT 48 -+#define ICC_SGI1R_AFFINITY_3_MASK (0xffULL << ICC_SGI1R_AFFINITY_1_SHIFT) -+ - /* - * System register definitions - */ -diff --git a/include/linux/irqchip/arm-gic.h b/include/linux/irqchip/arm-gic.h -index 13eed92..60b09ed 100644 ---- a/include/linux/irqchip/arm-gic.h -+++ b/include/linux/irqchip/arm-gic.h -@@ -106,6 +106,8 @@ static inline void gic_init(unsigned int nr, int start, - gic_init_bases(nr, start, dist, cpu, 0, NULL); - } - -+int gicv2m_of_init(struct device_node *node, struct irq_domain *parent); -+ - void gic_send_sgi(unsigned int cpu_id, unsigned int irq); - int gic_get_cpu_id(unsigned int cpu); - void gic_migrate_target(unsigned int new_cpu_id); -diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h -index ebace05..3c5ca45 100644 ---- a/include/linux/irqdomain.h -+++ b/include/linux/irqdomain.h -@@ -56,6 +56,7 @@ enum irq_domain_bus_token { - DOMAIN_BUS_ANY = 0, - DOMAIN_BUS_PCI_MSI, - DOMAIN_BUS_PLATFORM_MSI, -+ DOMAIN_BUS_NEXUS, - }; - - /** -diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h -index dba793e..62d966a 100644 ---- a/include/linux/mmc/sdhci.h -+++ b/include/linux/mmc/sdhci.h -@@ -100,6 +100,10 @@ struct sdhci_host { - #define SDHCI_QUIRK2_BROKEN_DDR50 (1<<7) - /* Stop command (CMD12) can set Transfer Complete when not using MMC_RSP_BUSY */ - #define SDHCI_QUIRK2_STOP_WITH_TC (1<<8) -+/* Controller does not support 64-bit DMA */ -+#define SDHCI_QUIRK2_BROKEN_64_BIT_DMA (1<<9) -+/* Controller broken with using ACMD23 */ -+#define SDHCI_QUIRK2_ACMD23_BROKEN (1<<14) - - int irq; /* Device IRQ */ - void __iomem *ioaddr; /* Mapped address */ -@@ -130,6 +134,7 @@ struct sdhci_host { - #define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */ - #define SDHCI_SDR104_NEEDS_TUNING (1<<10) /* SDR104/HS200 needs tuning */ - #define SDHCI_USING_RETUNING_TIMER (1<<11) /* Host is using a retuning timer for the card */ -+#define SDHCI_USE_64_BIT_DMA (1<<12) /* Use 64-bit DMA */ - - unsigned int version; /* SDHCI spec. version */ - -@@ -155,12 +160,19 @@ struct sdhci_host { - - int sg_count; /* Mapped sg entries */ - -- u8 *adma_desc; /* ADMA descriptor table */ -- u8 *align_buffer; /* Bounce buffer */ -+ void *adma_table; /* ADMA descriptor table */ -+ void *align_buffer; /* Bounce buffer */ -+ -+ size_t adma_table_sz; /* ADMA descriptor table size */ -+ size_t align_buffer_sz; /* Bounce buffer size */ - - dma_addr_t adma_addr; /* Mapped ADMA descr. table */ - dma_addr_t align_addr; /* Mapped bounce buffer */ - -+ unsigned int desc_sz; /* ADMA descriptor size */ -+ unsigned int align_sz; /* ADMA alignment */ -+ unsigned int align_mask; /* ADMA alignment mask */ -+ - struct tasklet_struct finish_tasklet; /* Tasklet structures */ - - struct timer_list timer; /* Timer for timeouts */ -diff --git a/include/linux/of.h b/include/linux/of.h -index 4a6a489..25111fb 100644 ---- a/include/linux/of.h -+++ b/include/linux/of.h -@@ -57,7 +57,6 @@ struct device_node { - struct device_node *child; - struct device_node *sibling; - struct device_node *next; /* next device of same type */ -- struct device_node *allnext; /* next in list of all nodes */ - struct kobject kobj; - unsigned long _flags; - void *data; -@@ -109,7 +108,7 @@ static inline void of_node_put(struct device_node *node) { } - #ifdef CONFIG_OF - - /* Pointer for first entry in chain of all nodes. */ --extern struct device_node *of_allnodes; -+extern struct device_node *of_root; - extern struct device_node *of_chosen; - extern struct device_node *of_aliases; - extern struct device_node *of_stdout; -@@ -117,7 +116,7 @@ extern raw_spinlock_t devtree_lock; - - static inline bool of_have_populated_dt(void) - { -- return of_allnodes != NULL; -+ return of_root != NULL; - } - - static inline bool of_node_is_root(const struct device_node *node) -@@ -161,6 +160,7 @@ static inline void of_property_clear_flag(struct property *p, unsigned long flag - clear_bit(flag, &p->_flags); - } - -+extern struct device_node *__of_find_all_nodes(struct device_node *prev); - extern struct device_node *of_find_all_nodes(struct device_node *prev); - - /* -@@ -216,8 +216,9 @@ static inline const char *of_node_full_name(const struct device_node *np) - return np ? np->full_name : ""; - } - --#define for_each_of_allnodes(dn) \ -- for (dn = of_allnodes; dn; dn = dn->allnext) -+#define for_each_of_allnodes_from(from, dn) \ -+ for (dn = __of_find_all_nodes(from); dn; dn = __of_find_all_nodes(dn)) -+#define for_each_of_allnodes(dn) for_each_of_allnodes_from(NULL, dn) - extern struct device_node *of_find_node_by_name(struct device_node *from, - const char *name); - extern struct device_node *of_find_node_by_type(struct device_node *from, -diff --git a/include/linux/of_pdt.h b/include/linux/of_pdt.h -index c65a18a..7e09244 100644 ---- a/include/linux/of_pdt.h -+++ b/include/linux/of_pdt.h -@@ -39,7 +39,6 @@ extern void *prom_early_alloc(unsigned long size); - /* for building the device tree */ - extern void of_pdt_build_devicetree(phandle root_node, struct of_pdt_ops *ops); - --extern void (*of_pdt_build_more)(struct device_node *dp, -- struct device_node ***nextp); -+extern void (*of_pdt_build_more)(struct device_node *dp); - - #endif /* _LINUX_OF_PDT_H */ -diff --git a/include/linux/pci.h b/include/linux/pci.h -index a99f301..f28c88b 100644 ---- a/include/linux/pci.h -+++ b/include/linux/pci.h -@@ -562,6 +562,7 @@ static inline int pcibios_err_to_errno(int err) - /* Low-level architecture-dependent routines */ - - struct pci_ops { -+ void __iomem *(*map_bus)(struct pci_bus *bus, unsigned int devfn, int where); - int (*read)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val); - int (*write)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val); - }; -@@ -859,6 +860,16 @@ int pci_bus_write_config_word(struct pci_bus *bus, unsigned int devfn, - int where, u16 val); - int pci_bus_write_config_dword(struct pci_bus *bus, unsigned int devfn, - int where, u32 val); -+ -+int pci_generic_config_read(struct pci_bus *bus, unsigned int devfn, -+ int where, int size, u32 *val); -+int pci_generic_config_write(struct pci_bus *bus, unsigned int devfn, -+ int where, int size, u32 val); -+int pci_generic_config_read32(struct pci_bus *bus, unsigned int devfn, -+ int where, int size, u32 *val); -+int pci_generic_config_write32(struct pci_bus *bus, unsigned int devfn, -+ int where, int size, u32 val); -+ - struct pci_ops *pci_bus_set_ops(struct pci_bus *bus, struct pci_ops *ops); - - static inline int pci_read_config_byte(const struct pci_dev *dev, int where, u8 *val) -diff --git a/include/linux/phy.h b/include/linux/phy.h -index d090cfc..eda18a8 100644 ---- a/include/linux/phy.h -+++ b/include/linux/phy.h -@@ -700,6 +700,7 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, - struct phy_c45_device_ids *c45_ids); - struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45); - int phy_device_register(struct phy_device *phy); -+void phy_device_remove(struct phy_device *phydev); - int phy_init_hw(struct phy_device *phydev); - int phy_suspend(struct phy_device *phydev); - int phy_resume(struct phy_device *phydev); -diff --git a/include/linux/phy_fixed.h b/include/linux/phy_fixed.h -index f2ca1b4..fe5732d 100644 ---- a/include/linux/phy_fixed.h -+++ b/include/linux/phy_fixed.h -@@ -11,7 +11,7 @@ struct fixed_phy_status { - - struct device_node; - --#ifdef CONFIG_FIXED_PHY -+#if IS_ENABLED(CONFIG_FIXED_PHY) - extern int fixed_phy_add(unsigned int irq, int phy_id, - struct fixed_phy_status *status); - extern struct phy_device *fixed_phy_register(unsigned int irq, -@@ -21,6 +21,9 @@ extern void fixed_phy_del(int phy_addr); - extern int fixed_phy_set_link_update(struct phy_device *phydev, - int (*link_update)(struct net_device *, - struct fixed_phy_status *)); -+extern int fixed_phy_update_state(struct phy_device *phydev, -+ const struct fixed_phy_status *status, -+ const struct fixed_phy_status *changed); - #else - static inline int fixed_phy_add(unsigned int irq, int phy_id, - struct fixed_phy_status *status) -@@ -43,6 +46,12 @@ static inline int fixed_phy_set_link_update(struct phy_device *phydev, - { - return -ENODEV; - } -+static inline int fixed_phy_update_state(struct phy_device *phydev, -+ const struct fixed_phy_status *status, -+ const struct fixed_phy_status *changed) -+{ -+ return -ENODEV; -+} - #endif /* CONFIG_FIXED_PHY */ - - #endif /* __PHY_FIXED_H */ -diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c -index 63c16d1..55dd2fb 100644 ---- a/kernel/irq/chip.c -+++ b/kernel/irq/chip.c -@@ -731,7 +731,30 @@ __irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained, - if (!handle) { - handle = handle_bad_irq; - } else { -- if (WARN_ON(desc->irq_data.chip == &no_irq_chip)) -+ struct irq_data *irq_data = &desc->irq_data; -+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY -+ /* -+ * With hierarchical domains we might run into a -+ * situation where the outermost chip is not yet set -+ * up, but the inner chips are there. Instead of -+ * bailing we install the handler, but obviously we -+ * cannot enable/startup the interrupt at this point. -+ */ -+ while (irq_data) { -+ if (irq_data->chip != &no_irq_chip) -+ break; -+ /* -+ * Bail out if the outer chip is not set up -+ * and the interrrupt supposed to be started -+ * right away. -+ */ -+ if (WARN_ON(is_chained)) -+ goto out; -+ /* Try the parent */ -+ irq_data = irq_data->parent_data; -+ } -+#endif -+ if (WARN_ON(!irq_data || irq_data->chip == &no_irq_chip)) - goto out; - } - -@@ -911,6 +934,23 @@ int irq_chip_set_affinity_parent(struct irq_data *data, - } - - /** -+ * irq_chip_set_type_parent - Set IRQ type on the parent interrupt -+ * @data: Pointer to interrupt specific data -+ * @type: IRQ_TYPE_{LEVEL,EDGE}_* value - see include/linux/irq.h -+ * -+ * Conditional, as the underlying parent chip might not implement it. -+ */ -+int irq_chip_set_type_parent(struct irq_data *data, unsigned int type) -+{ -+ data = data->parent_data; -+ -+ if (data->chip->irq_set_type) -+ return data->chip->irq_set_type(data, type); -+ -+ return -ENOSYS; -+} -+ -+/** - * irq_chip_retrigger_hierarchy - Retrigger an interrupt in hardware - * @data: Pointer to interrupt specific data - * -@@ -925,6 +965,22 @@ int irq_chip_retrigger_hierarchy(struct irq_data *data) - - return -ENOSYS; - } -+ -+/** -+ * irq_chip_set_wake_parent - Set/reset wake-up on the parent interrupt -+ * @data: Pointer to interrupt specific data -+ * @on: Whether to set or reset the wake-up capability of this irq -+ * -+ * Conditional, as the underlying parent chip might not implement it. -+ */ -+int irq_chip_set_wake_parent(struct irq_data *data, unsigned int on) -+{ -+ data = data->parent_data; -+ if (data->chip->irq_set_wake) -+ return data->chip->irq_set_wake(data, on); -+ -+ return -ENOSYS; -+} - #endif - - /** -diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c -index 8069237..acb401f 100644 ---- a/kernel/irq/manage.c -+++ b/kernel/irq/manage.c -@@ -1758,3 +1758,94 @@ int request_percpu_irq(unsigned int irq, irq_handler_t handler, - - return retval; - } -+ -+/** -+ * irq_get_irqchip_state - returns the irqchip state of a interrupt. -+ * @irq: Interrupt line that is forwarded to a VM -+ * @which: One of IRQCHIP_STATE_* the caller wants to know about -+ * @state: a pointer to a boolean where the state is to be storeed -+ * -+ * This call snapshots the internal irqchip state of an -+ * interrupt, returning into @state the bit corresponding to -+ * stage @which -+ * -+ * This function should be called with preemption disabled if the -+ * interrupt controller has per-cpu registers. -+ */ -+int irq_get_irqchip_state(unsigned int irq, enum irqchip_irq_state which, -+ bool *state) -+{ -+ struct irq_desc *desc; -+ struct irq_data *data; -+ struct irq_chip *chip; -+ unsigned long flags; -+ int err = -EINVAL; -+ -+ desc = irq_get_desc_buslock(irq, &flags, 0); -+ if (!desc) -+ return err; -+ -+ data = irq_desc_get_irq_data(desc); -+ -+ do { -+ chip = irq_data_get_irq_chip(data); -+ if (chip->irq_get_irqchip_state) -+ break; -+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY -+ data = data->parent_data; -+#else -+ data = NULL; -+#endif -+ } while (data); -+ -+ if (data) -+ err = chip->irq_get_irqchip_state(data, which, state); -+ -+ irq_put_desc_busunlock(desc, flags); -+ return err; -+} -+ -+/** -+ * irq_set_irqchip_state - set the state of a forwarded interrupt. -+ * @irq: Interrupt line that is forwarded to a VM -+ * @which: State to be restored (one of IRQCHIP_STATE_*) -+ * @val: Value corresponding to @which -+ * -+ * This call sets the internal irqchip state of an interrupt, -+ * depending on the value of @which. -+ * -+ * This function should be called with preemption disabled if the -+ * interrupt controller has per-cpu registers. -+ */ -+int irq_set_irqchip_state(unsigned int irq, enum irqchip_irq_state which, -+ bool val) -+{ -+ struct irq_desc *desc; -+ struct irq_data *data; -+ struct irq_chip *chip; -+ unsigned long flags; -+ int err = -EINVAL; -+ -+ desc = irq_get_desc_buslock(irq, &flags, 0); -+ if (!desc) -+ return err; -+ -+ data = irq_desc_get_irq_data(desc); -+ -+ do { -+ chip = irq_data_get_irq_chip(data); -+ if (chip->irq_set_irqchip_state) -+ break; -+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY -+ data = data->parent_data; -+#else -+ data = NULL; -+#endif -+ } while (data); -+ -+ if (data) -+ err = chip->irq_set_irqchip_state(data, which, val); -+ -+ irq_put_desc_busunlock(desc, flags); -+ return err; -+} -diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c -index 2495ed0..54433c2 100644 ---- a/kernel/irq/msi.c -+++ b/kernel/irq/msi.c -@@ -106,8 +106,10 @@ static int msi_domain_alloc(struct irq_domain *domain, unsigned int virq, - irq_hw_number_t hwirq = ops->get_hwirq(info, arg); - int i, ret; - -+#if 0 - if (irq_find_mapping(domain, hwirq) > 0) - return -EEXIST; -+#endif - - ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg); - if (ret < 0) -@@ -327,8 +329,15 @@ void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev) - struct msi_desc *desc; - - for_each_msi_entry(desc, dev) { -- irq_domain_free_irqs(desc->irq, desc->nvec_used); -- desc->irq = 0; -+ /* -+ * We might have failed to allocate an MSI early -+ * enough that there is no IRQ associated to this -+ * entry. If that's the case, don't do anything. -+ */ -+ if (desc->irq) { -+ irq_domain_free_irqs(desc->irq, desc->nvec_used); -+ desc->irq = 0; -+ } - } - } - -diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c -index fa756d0..ad57f0c 100644 ---- a/sound/soc/fsl/mpc8610_hpcd.c -+++ b/sound/soc/fsl/mpc8610_hpcd.c -@@ -12,11 +12,11 @@ - - #include - #include -+#include - #include - #include - #include - #include --#include - - #include "fsl_dma.h" - #include "fsl_ssi.h" -diff --git a/sound/soc/fsl/p1022_ds.c b/sound/soc/fsl/p1022_ds.c -index f75c3cf..64a0bb6 100644 ---- a/sound/soc/fsl/p1022_ds.c -+++ b/sound/soc/fsl/p1022_ds.c -@@ -11,12 +11,12 @@ - */ - - #include -+#include - #include - #include - #include - #include - #include --#include - - #include "fsl_dma.h" - #include "fsl_ssi.h" -diff --git a/sound/soc/fsl/p1022_rdk.c b/sound/soc/fsl/p1022_rdk.c -index 9d89bb0..4ce4aff 100644 ---- a/sound/soc/fsl/p1022_rdk.c -+++ b/sound/soc/fsl/p1022_rdk.c -@@ -18,12 +18,12 @@ - */ - - #include -+#include - #include - #include - #include - #include - #include --#include - - #include "fsl_dma.h" - #include "fsl_ssi.h" --- -2.1.0.27.g96db324 - diff --git a/packages/base/any/kernels/3.18.25/patches/driver-support-intel-igb-bcm54616-phy.patch b/packages/base/any/kernels/3.18.25/patches/driver-support-intel-igb-bcm54616-phy.patch deleted file mode 100644 index c83708ac..00000000 --- a/packages/base/any/kernels/3.18.25/patches/driver-support-intel-igb-bcm54616-phy.patch +++ /dev/null @@ -1,67 +0,0 @@ -diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.c b/drivers/net/ethernet/intel/igb/e1000_82575.c -index 051ea94..2a04baa 100644 ---- a/drivers/net/ethernet/intel/igb/e1000_82575.c -+++ b/drivers/net/ethernet/intel/igb/e1000_82575.c -@@ -286,6 +286,9 @@ static s32 igb_init_phy_params_82575(struct e1000_hw *hw) - phy->ops.set_d3_lplu_state = igb_set_d3_lplu_state_82580; - phy->ops.force_speed_duplex = igb_phy_force_speed_duplex_m88; - break; -+ case BCM54616_E_PHY_ID: -+ phy->type = e1000_phy_bcm54616; -+ break; - default: - ret_val = -E1000_ERR_PHY; - goto out; -@@ -1550,6 +1553,7 @@ static s32 igb_setup_copper_link_82575(struct e1000_hw *hw) - case e1000_i350: - case e1000_i210: - case e1000_i211: -+ case e1000_i354: - phpm_reg = rd32(E1000_82580_PHY_POWER_MGMT); - phpm_reg &= ~E1000_82580_PM_GO_LINKD; - wr32(E1000_82580_PHY_POWER_MGMT, phpm_reg); -@@ -1593,6 +1597,8 @@ static s32 igb_setup_copper_link_82575(struct e1000_hw *hw) - case e1000_phy_82580: - ret_val = igb_copper_link_setup_82580(hw); - break; -+ case e1000_phy_bcm54616: -+ break; - default: - ret_val = -E1000_ERR_PHY; - break; -diff --git a/drivers/net/ethernet/intel/igb/e1000_defines.h b/drivers/net/ethernet/intel/igb/e1000_defines.h -index 217f813..5322fbf 100644 ---- a/drivers/net/ethernet/intel/igb/e1000_defines.h -+++ b/drivers/net/ethernet/intel/igb/e1000_defines.h -@@ -860,6 +860,7 @@ - #define M88_VENDOR 0x0141 - #define I210_I_PHY_ID 0x01410C00 - #define M88E1543_E_PHY_ID 0x01410EA0 -+#define BCM54616_E_PHY_ID 0x3625D10 - - /* M88E1000 Specific Registers */ - #define M88E1000_PHY_SPEC_CTRL 0x10 /* PHY Specific Control Register */ -diff --git a/drivers/net/ethernet/intel/igb/e1000_hw.h b/drivers/net/ethernet/intel/igb/e1000_hw.h -index 2003b37..d82c96b 100644 ---- a/drivers/net/ethernet/intel/igb/e1000_hw.h -+++ b/drivers/net/ethernet/intel/igb/e1000_hw.h -@@ -128,6 +128,7 @@ enum e1000_phy_type { - e1000_phy_ife, - e1000_phy_82580, - e1000_phy_i210, -+ e1000_phy_bcm54616, - }; - - enum e1000_bus_type { -diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c -index e0f3664..013c1f1 100644 ---- a/drivers/net/ethernet/intel/igb/igb_main.c -+++ b/drivers/net/ethernet/intel/igb/igb_main.c -@@ -108,6 +108,7 @@ static const struct pci_device_id igb_pci_tbl[] = { - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82575EB_COPPER), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82575EB_FIBER_SERDES), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82575GB_QUAD_COPPER), board_82575 }, -+ { PCI_VDEVICE(INTEL, E1000_DEV_ID_I354_SGMII), board_82575 }, - /* required last entry */ - {0, } - }; diff --git a/packages/base/any/kernels/3.18.25/patches/ls2_mc_console.patch b/packages/base/any/kernels/3.18.25/patches/ls2_mc_console.patch deleted file mode 100644 index f5eb969e..00000000 --- a/packages/base/any/kernels/3.18.25/patches/ls2_mc_console.patch +++ /dev/null @@ -1,327 +0,0 @@ -diff -uNr a/drivers/soc/fsl/Kconfig.arm b/drivers/soc/fsl/Kconfig.arm ---- a/drivers/soc/fsl/Kconfig.arm 2017-06-05 17:37:14.530348991 +0530 -+++ b/drivers/soc/fsl/Kconfig.arm 2017-06-05 17:32:18.630348990 +0530 -@@ -23,3 +23,7 @@ - if LS1_SOC_DRIVERS - source "drivers/soc/fsl/ls1/Kconfig" - endif -+ -+if LS_SOC_DRIVERS -+ source "drivers/soc/fsl/ls2-console/Kconfig" -+endif -diff -uNr a/drivers/soc/fsl/ls2-console/Kconfig b/drivers/soc/fsl/ls2-console/Kconfig ---- a/drivers/soc/fsl/ls2-console/Kconfig 1970-01-01 05:30:00.000000000 +0530 -+++ b/drivers/soc/fsl/ls2-console/Kconfig 2017-06-05 17:32:52.582348990 +0530 -@@ -0,0 +1,4 @@ -+config FSL_LS2_CONSOLE -+ tristate "Layerscape MC and AIOP console support" -+ depends on ARCH_LAYERSCAPE -+ default y -diff -uNr a/drivers/soc/fsl/ls2-console/ls2-console.c b/drivers/soc/fsl/ls2-console/ls2-console.c ---- a/drivers/soc/fsl/ls2-console/ls2-console.c 1970-01-01 05:30:00.000000000 +0530 -+++ b/drivers/soc/fsl/ls2-console/ls2-console.c 2017-06-05 17:50:42.494348990 +0530 -@@ -0,0 +1,291 @@ -+/* Copyright 2015-2016 Freescale Semiconductor Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * 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. -+ * * Neither the name of the above-listed copyright holders nor the -+ * names of any contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* SoC address for the MC firmware base low/high registers */ -+#define SOC_CCSR_MC_FW_BASE_ADDR_REGS 0x8340020 -+#define SOC_CCSR_MC_FW_BASE_ADDR_REGS_SIZE 2 -+/* MC firmware base low/high registers indexes */ -+#define MCFBALR_OFFSET 0 -+#define MCFBAHR_OFFSET 1 -+ -+/* Bit mask used to obtain the most significant part of the MC base address */ -+#define MC_FW_HIGH_ADDR_MASK 0x1FFFF -+/* Bit mask used to obtain the least significant part of the MC base address */ -+#define MC_FW_LOW_ADDR_MASK 0xE0000000 -+ -+#define MC_BUFFER_OFFSET 0x01000000 -+#define MC_BUFFER_SIZE (1024*1024*16) -+#define MC_OFFSET_DELTA (MC_BUFFER_OFFSET) -+ -+#define AIOP_BUFFER_OFFSET 0x06000000 -+#define AIOP_BUFFER_SIZE (1024*1024*16) -+#define AIOP_OFFSET_DELTA (0) -+ -+struct log_header { -+ char magic_word[8]; /* magic word */ -+ uint32_t buf_start; /* holds the 32-bit little-endian -+ offset of the start of the buffer */ -+ uint32_t buf_length; /* holds the 32-bit little-endian -+ length of the buffer */ -+ uint32_t last_byte; /* holds the 32-bit little-endian offset -+ of the byte after the last byte that was written */ -+ char reserved[44]; -+}; -+ -+#define LOG_HEADER_FLAG_BUFFER_WRAPAROUND 0x80000000 -+#define LOG_VERSION_MAJOR 1 -+#define LOG_VERSION_MINOR 0 -+ -+ -+#define invalidate(p) { asm volatile("dc ivac, %0" : : "r" (p) : "memory"); } -+ -+struct console_data { -+ char *map_addr; -+ struct log_header *hdr; -+ char *start_addr; /* Start of buffer */ -+ char *end_addr; /* End of buffer */ -+ char *end_of_data; /* Current end of data */ -+ char *cur_ptr; /* Last data sent to console */ -+}; -+ -+#define LAST_BYTE(a) ((a) & ~(LOG_HEADER_FLAG_BUFFER_WRAPAROUND)) -+ -+static inline void __adjust_end(struct console_data *cd) -+{ -+ cd->end_of_data = cd->start_addr -+ + LAST_BYTE(le32_to_cpu(cd->hdr->last_byte)); -+} -+ -+static inline void adjust_end(struct console_data *cd) -+{ -+ invalidate(cd->hdr); -+ __adjust_end(cd); -+} -+ -+static inline uint64_t get_mc_fw_base_address(void) { -+ uint32_t* mcfbaregs = (uint32_t*) ioremap(SOC_CCSR_MC_FW_BASE_ADDR_REGS, -+ SOC_CCSR_MC_FW_BASE_ADDR_REGS_SIZE); -+ uint64_t mcfwbase = 0ULL; -+ mcfwbase = readl(mcfbaregs + MCFBAHR_OFFSET) & MC_FW_HIGH_ADDR_MASK; -+ mcfwbase <<= 32; -+ mcfwbase |= readl(mcfbaregs + MCFBALR_OFFSET) & MC_FW_LOW_ADDR_MASK; -+ iounmap(mcfbaregs); -+ pr_info("fsl-ls2-console: MC base address at 0x%016llx\n", mcfwbase); -+ return mcfwbase; -+} -+ -+static int fsl_ls2_generic_console_open(struct inode *node, struct file *fp, -+ u64 offset, u64 size, -+ uint8_t *emagic, uint8_t magic_len, -+ u32 offset_delta) -+{ -+ struct console_data *cd; -+ uint8_t *magic; -+ uint32_t wrapped; -+ -+ cd = kmalloc(sizeof(*cd), GFP_KERNEL); -+ if (cd == NULL) -+ return -ENOMEM; -+ fp->private_data = cd; -+ cd->map_addr = ioremap(get_mc_fw_base_address() + offset, size); -+ -+ cd->hdr = (struct log_header *) cd->map_addr; -+ invalidate(cd->hdr); -+ -+ magic = cd->hdr->magic_word; -+ if (memcmp(magic, emagic, magic_len)) { -+ pr_info("magic didn't match!\n"); -+ pr_info("expected: %02x %02x %02x %02x %02x %02x %02x %02x\n", -+ emagic[0], emagic[1], emagic[2], emagic[3], -+ emagic[4], emagic[5], emagic[6], emagic[7]); -+ pr_info(" seen: %02x %02x %02x %02x %02x %02x %02x %02x\n", -+ magic[0], magic[1], magic[2], magic[3], -+ magic[4], magic[5], magic[6], magic[7]); -+ kfree(cd); -+ iounmap(cd->map_addr); -+ return -EIO; -+ } -+ -+ cd->start_addr = cd->map_addr -+ + le32_to_cpu(cd->hdr->buf_start) - offset_delta; -+ cd->end_addr = cd->start_addr + le32_to_cpu(cd->hdr->buf_length); -+ -+ wrapped = le32_to_cpu(cd->hdr->last_byte) -+ & LOG_HEADER_FLAG_BUFFER_WRAPAROUND; -+ -+ __adjust_end(cd); -+ if (wrapped && (cd->end_of_data != cd->end_addr)) -+ cd->cur_ptr = cd->end_of_data+1; -+ else -+ cd->cur_ptr = cd->start_addr; -+ -+ return 0; -+} -+ -+static int fsl_ls2_mc_console_open(struct inode *node, struct file *fp) -+{ -+ uint8_t magic_word[] = { 0, 1, 'C', 'M' }; -+ -+ return fsl_ls2_generic_console_open(node, fp, -+ MC_BUFFER_OFFSET, MC_BUFFER_SIZE, -+ magic_word, sizeof(magic_word), -+ MC_OFFSET_DELTA); -+} -+ -+static int fsl_ls2_aiop_console_open(struct inode *node, struct file *fp) -+{ -+ uint8_t magic_word[] = { 'P', 'O', 'I', 'A' }; -+ -+ return fsl_ls2_generic_console_open(node, fp, -+ AIOP_BUFFER_OFFSET, AIOP_BUFFER_SIZE, -+ magic_word, sizeof(magic_word), -+ AIOP_OFFSET_DELTA); -+} -+ -+static int fsl_ls2_console_close(struct inode *node, struct file *fp) -+{ -+ struct console_data *cd = fp->private_data; -+ -+ iounmap(cd->map_addr); -+ kfree(cd); -+ return 0; -+} -+ -+ssize_t fsl_ls2_console_read(struct file *fp, char __user *buf, size_t count, -+ loff_t *f_pos) -+{ -+ struct console_data *cd = fp->private_data; -+ size_t bytes = 0; -+ char data; -+ -+ /* Check if we need to adjust the end of data addr */ -+ adjust_end(cd); -+ -+ while ((count != bytes) && (cd->end_of_data != cd->cur_ptr)) { -+ if (((u64)cd->cur_ptr) % 64 == 0) -+ invalidate(cd->cur_ptr); -+ -+ data = *(cd->cur_ptr); -+ if (copy_to_user(&buf[bytes], &data, 1)) -+ return -EFAULT; -+ cd->cur_ptr++; -+ if (cd->cur_ptr >= cd->end_addr) -+ cd->cur_ptr = cd->start_addr; -+ ++bytes; -+ } -+ return bytes; -+} -+ -+static const struct file_operations fsl_ls2_mc_console_fops = { -+ .owner = THIS_MODULE, -+ .open = fsl_ls2_mc_console_open, -+ .release = fsl_ls2_console_close, -+ .read = fsl_ls2_console_read, -+}; -+ -+static struct miscdevice fsl_ls2_mc_console_dev = { -+ .minor = MISC_DYNAMIC_MINOR, -+ .name = "fsl_mc_console", -+ .fops = &fsl_ls2_mc_console_fops -+}; -+ -+static const struct file_operations fsl_ls2_aiop_console_fops = { -+ .owner = THIS_MODULE, -+ .open = fsl_ls2_aiop_console_open, -+ .release = fsl_ls2_console_close, -+ .read = fsl_ls2_console_read, -+}; -+ -+static struct miscdevice fsl_ls2_aiop_console_dev = { -+ .minor = MISC_DYNAMIC_MINOR, -+ .name = "fsl_aiop_console", -+ .fops = &fsl_ls2_aiop_console_fops -+}; -+ -+static int __init fsl_ls2_console_init(void) -+{ -+ int err = 0; -+ -+ pr_info("Freescale LS2 console driver\n"); -+ err = misc_register(&fsl_ls2_mc_console_dev); -+ if (err) { -+ pr_err("fsl_mc_console: cannot register device\n"); -+ return err; -+ } -+ pr_info("fsl-ls2-console: device %s registered\n", -+ fsl_ls2_mc_console_dev.name); -+ -+ err = misc_register(&fsl_ls2_aiop_console_dev); -+ if (err) { -+ pr_err("fsl_aiop_console: cannot register device\n"); -+ return err; -+ } -+ pr_info("fsl-ls2-console: device %s registered\n", -+ fsl_ls2_aiop_console_dev.name); -+ -+ return 0; -+} -+ -+static void __exit fsl_ls2_console_exit(void) -+{ -+ int err = misc_deregister(&fsl_ls2_mc_console_dev); -+ -+ if (err) -+ pr_err("Failed to deregister device %s code %d\n", -+ fsl_ls2_mc_console_dev.name, err); -+ else -+ pr_info("device %s deregistered\n", -+ fsl_ls2_mc_console_dev.name); -+ -+ err = misc_deregister(&fsl_ls2_aiop_console_dev); -+ if (err) -+ pr_err("Failed to deregister device %s code %d\n", -+ fsl_ls2_aiop_console_dev.name, err); -+ else -+ pr_info("device %s deregistered\n", -+ fsl_ls2_aiop_console_dev.name); -+} -+ -+module_init(fsl_ls2_console_init); -+module_exit(fsl_ls2_console_exit); -+ -+MODULE_AUTHOR("Roy Pledge "); -+MODULE_LICENSE("Dual BSD/GPL"); -+MODULE_DESCRIPTION("Freescale LS2 console driver"); -diff -uNr a/drivers/soc/fsl/ls2-console/Makefile b/drivers/soc/fsl/ls2-console/Makefile ---- a/drivers/soc/fsl/ls2-console/Makefile 1970-01-01 05:30:00.000000000 +0530 -+++ b/drivers/soc/fsl/ls2-console/Makefile 2017-06-05 17:32:52.582348990 +0530 -@@ -0,0 +1 @@ -+obj-$(CONFIG_FSL_LS2_CONSOLE) += ls2-console.o -diff -uNr a/drivers/soc/fsl/Makefile b/drivers/soc/fsl/Makefile ---- a/drivers/soc/fsl/Makefile 2017-06-05 17:37:14.530348991 +0530 -+++ b/drivers/soc/fsl/Makefile 2017-06-05 17:33:54.022348991 +0530 -@@ -4,3 +4,4 @@ - - obj-$(CONFIG_LS1_SOC_DRIVERS) += ls1/ - obj-$(CONFIG_FSL_GUTS) += guts.o -+obj-$(CONFIG_LS_SOC_DRIVERS) += ls2-console/ diff --git a/packages/base/any/kernels/3.18.25/patches/series b/packages/base/any/kernels/3.18.25/patches/series deleted file mode 100644 index 6fb98212..00000000 --- a/packages/base/any/kernels/3.18.25/patches/series +++ /dev/null @@ -1,3 +0,0 @@ -aufs.patch -driver-support-intel-igb-bcm54616-phy.patch - diff --git a/packages/base/any/kernels/3.18.25/patches/series.arm64 b/packages/base/any/kernels/3.18.25/patches/series.arm64 deleted file mode 100644 index f9ac0ce2..00000000 --- a/packages/base/any/kernels/3.18.25/patches/series.arm64 +++ /dev/null @@ -1,2 +0,0 @@ -0001-Patch-set-for-booting-ls2088rdb-with-vfio.patch -ls2_mc_console.patch