diff options
author | 2014-01-28 13:40:53 +0000 | |
---|---|---|
committer | 2014-05-09 12:16:23 +0100 | |
commit | 6dae9e2b3031dd196c7a695e75b0746c1bf8e593 (patch) | |
tree | 9d4c15d91a58ca542e00fe90d797e5bd1feddeca /recipes-kernel/linux | |
download | meta-intel-quark-6dae9e2b3031dd196c7a695e75b0746c1bf8e593.tar.gz meta-intel-quark-6dae9e2b3031dd196c7a695e75b0746c1bf8e593.tar.bz2 meta-intel-quark-6dae9e2b3031dd196c7a695e75b0746c1bf8e593.zip |
Initial commit adding combo repositories
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
Diffstat (limited to 'recipes-kernel/linux')
-rw-r--r-- | recipes-kernel/linux/files/clanton-standard.scc | 6 | ||||
-rw-r--r-- | recipes-kernel/linux/files/clanton.cfg | 2938 | ||||
-rw-r--r-- | recipes-kernel/linux/files/clanton.patch | 30644 | ||||
-rw-r--r-- | recipes-kernel/linux/linux-yocto-clanton_3.8.bb | 70 |
4 files changed, 33658 insertions, 0 deletions
diff --git a/recipes-kernel/linux/files/clanton-standard.scc b/recipes-kernel/linux/files/clanton-standard.scc new file mode 100644 index 0000000..07e0758 --- /dev/null +++ b/recipes-kernel/linux/files/clanton-standard.scc @@ -0,0 +1,6 @@ +define KMACHINE clanton +define KTYPE standard +define KARCH i386 + +kconf hardware clanton.cfg + diff --git a/recipes-kernel/linux/files/clanton.cfg b/recipes-kernel/linux/files/clanton.cfg new file mode 100644 index 0000000..1356dc9 --- /dev/null +++ b/recipes-kernel/linux/files/clanton.cfg @@ -0,0 +1,2938 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/i386 3.8.7 Kernel Configuration +# +# CONFIG_64BIT is not set +CONFIG_X86_32=y +CONFIG_X86=y +CONFIG_INSTRUCTION_DECODER=y +CONFIG_OUTPUT_FORMAT="elf32-i386" +CONFIG_ARCH_DEFCONFIG="arch/x86/configs/i386_defconfig" +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_MMU=y +CONFIG_NEED_SG_DMA_LENGTH=y +CONFIG_GENERIC_ISA_DMA=y +CONFIG_GENERIC_BUG=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_GPIO=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_DEFAULT_IDLE=y +CONFIG_ARCH_HAS_CACHE_LINE_SIZE=y +CONFIG_ARCH_HAS_CPU_AUTOPROBE=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_ZONE_DMA32 is not set +# CONFIG_AUDIT_ARCH is not set +CONFIG_ARCH_SUPPORTS_OPTIMIZED_INLINING=y +CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC=y +CONFIG_X86_32_LAZY_GS=y +CONFIG_ARCH_HWEIGHT_CFLAGS="-fcall-saved-ecx -fcall-saved-edx" +CONFIG_ARCH_SUPPORTS_UPROBES=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_HAVE_IRQ_WORK=y +CONFIG_IRQ_WORK=y +CONFIG_BUILDTIME_EXTABLE_SORT=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="" +# 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_KERNEL_GZIP is not set +# CONFIG_KERNEL_BZIP2 is not set +CONFIG_KERNEL_LZMA=y +# CONFIG_KERNEL_XZ is not set +# CONFIG_KERNEL_LZO is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +CONFIG_POSIX_MQUEUE=y +CONFIG_POSIX_MQUEUE_SYSCTL=y +# CONFIG_FHANDLE is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_IRQ_CHIP=y +CONFIG_IRQ_DOMAIN=y +# CONFIG_IRQ_DOMAIN_DEBUG is not set +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_SPARSE_IRQ=y +CONFIG_CLOCKSOURCE_WATCHDOG=y +CONFIG_KTIME_SCALAR=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=y +CONFIG_HIGH_RES_TIMERS=y + +# +# CPU/Task time and stats accounting +# +CONFIG_TICK_CPU_ACCOUNTING=y +# CONFIG_IRQ_TIME_ACCOUNTING is not set +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +# CONFIG_TASKSTATS is not set + +# +# RCU Subsystem +# +CONFIG_TINY_RCU=y +# CONFIG_PREEMPT_RCU is not set +# CONFIG_TREE_RCU_TRACE is not set +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=18 +CONFIG_HAVE_UNSTABLE_SCHED_CLOCK=y +CONFIG_ARCH_SUPPORTS_NUMA_BALANCING=y +CONFIG_ARCH_WANTS_PROT_NUMA_PROT_NONE=y +CONFIG_CGROUPS=y +# CONFIG_CGROUP_DEBUG is not set +CONFIG_CGROUP_FREEZER=y +# CONFIG_CGROUP_DEVICE is not set +CONFIG_CPUSETS=y +CONFIG_PROC_PID_CPUSET=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +# CONFIG_MEMCG is not set +# CONFIG_CGROUP_HUGETLB is not set +# 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=y +CONFIG_IPC_NS=y +# CONFIG_USER_NS is not set +CONFIG_PID_NS=y +CONFIG_NET_NS=y +CONFIG_UIDGID_CONVERTED=y +# CONFIG_UIDGID_STRICT_TYPE_CHECKS is not set +# CONFIG_SCHED_AUTOGROUP is not set +# 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 is not set +# CONFIG_RD_LZO is not set +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_EXPERT=y +CONFIG_HAVE_UID16=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_SYSCTL_EXCEPTION_TRACE=y +CONFIG_KALLSYMS=y +CONFIG_KALLSYMS_ALL=y +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +# CONFIG_PCSPKR_PLATFORM is not set +CONFIG_HAVE_PCSPKR_PLATFORM=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_AIO=y +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_PCI_QUIRKS=y +CONFIG_SLUB_DEBUG=y +# CONFIG_COMPAT_BRK is not set +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +CONFIG_OPROFILE_NMI_TIMER=y +# CONFIG_KPROBES is not set +CONFIG_JUMP_LABEL=y +CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y +CONFIG_HAVE_IOREMAP_PROT=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_OPTPROBES=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_ALIGNED_STRUCT_PAGE=y +CONFIG_HAVE_CMPXCHG_LOCAL=y +CONFIG_HAVE_CMPXCHG_DOUBLE=y +CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y +CONFIG_HAVE_ARCH_SECCOMP_FILTER=y +CONFIG_SECCOMP_FILTER=y +CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y +CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE=y +CONFIG_MODULES_USE_ELF_REL=y +CONFIG_GENERIC_SIGALTSTACK=y +CONFIG_CLONE_BACKWARDS=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 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_BLOCK=y +# CONFIG_LBDAF is not set +CONFIG_BLK_DEV_BSG=y +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_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=y +# 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 is not set +# CONFIG_SYSV68_PARTITION is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +CONFIG_UNINLINE_SPIN_UNLOCK=y +CONFIG_FREEZER=y + +# +# Processor type and features +# +# CONFIG_ZONE_DMA is not set +# CONFIG_SMP is not set +CONFIG_X86_MPPARSE=y +CONFIG_X86_EXTENDED_PLATFORM=y +CONFIG_INTEL_QUARK_X1000_SOC=y +# CONFIG_X86_WANT_INTEL_MID is not set +# CONFIG_X86_RDC321X is not set +CONFIG_X86_SUPPORTS_MEMORY_FAILURE=y + +# +# Intel Media SOC Gen3 support +# +CONFIG_ARCH_GEN3=y +# CONFIG_X86_32_IRIS is not set +CONFIG_SCHED_OMIT_FRAME_POINTER=y +# CONFIG_PARAVIRT_GUEST is not set +CONFIG_NO_BOOTMEM=y +# CONFIG_MEMTEST is not set +# CONFIG_M486 is not set +# CONFIG_M586 is not set +CONFIG_M586TSC=y +# CONFIG_M586MMX is not set +# CONFIG_M686 is not set +# CONFIG_MPENTIUMII is not set +# CONFIG_MPENTIUMIII is not set +# CONFIG_MPENTIUMM is not set +# CONFIG_MPENTIUM4 is not set +# CONFIG_MK6 is not set +# CONFIG_MK7 is not set +# CONFIG_MK8 is not set +# CONFIG_MCRUSOE is not set +# CONFIG_MEFFICEON is not set +# CONFIG_MWINCHIPC6 is not set +# CONFIG_MWINCHIP3D is not set +# CONFIG_MELAN is not set +# CONFIG_MGEODEGX1 is not set +# CONFIG_MGEODE_LX is not set +# CONFIG_MCYRIXIII is not set +# CONFIG_MVIAC3_2 is not set +# CONFIG_MVIAC7 is not set +# CONFIG_MCORE2 is not set +# CONFIG_MATOM is not set +CONFIG_X86_GENERIC=y +CONFIG_X86_INTERNODE_CACHE_SHIFT=6 +CONFIG_X86_L1_CACHE_SHIFT=6 +# CONFIG_X86_PPRO_FENCE is not set +CONFIG_X86_F00F_BUG=y +CONFIG_X86_ALIGNMENT_16=y +CONFIG_X86_INTEL_USERCOPY=y +CONFIG_X86_TSC=y +CONFIG_X86_CMPXCHG64=y +CONFIG_X86_MINIMUM_CPU_FAMILY=5 +# CONFIG_PROCESSOR_SELECT is not set +CONFIG_CPU_SUP_INTEL=y +CONFIG_CPU_SUP_CYRIX_32=y +CONFIG_CPU_SUP_AMD=y +CONFIG_CPU_SUP_CENTAUR=y +CONFIG_CPU_SUP_TRANSMETA_32=y +CONFIG_CPU_SUP_UMC_32=y +CONFIG_HPET_TIMER=y +CONFIG_HPET_EMULATE_RTC=y +# CONFIG_DMI is not set +CONFIG_NR_CPUS=1 +# CONFIG_PREEMPT_NONE is not set +CONFIG_PREEMPT_VOLUNTARY=y +# CONFIG_PREEMPT is not set +CONFIG_X86_UP_APIC=y +CONFIG_X86_UP_IOAPIC=y +CONFIG_X86_LOCAL_APIC=y +CONFIG_X86_IO_APIC=y +# CONFIG_X86_REROUTE_FOR_BROKEN_BOOT_IRQS is not set +CONFIG_X86_MCE=y +CONFIG_X86_MCE_INTEL=y +# CONFIG_X86_MCE_AMD is not set +# CONFIG_X86_ANCIENT_MCE is not set +CONFIG_X86_MCE_THRESHOLD=y +# CONFIG_X86_MCE_INJECT is not set +CONFIG_X86_THERMAL_VECTOR=y +CONFIG_VM86=y +# CONFIG_TOSHIBA is not set +# CONFIG_I8K is not set +CONFIG_X86_REBOOTFIXUPS=y +CONFIG_MICROCODE=y +CONFIG_MICROCODE_INTEL=y +# CONFIG_MICROCODE_AMD is not set +CONFIG_MICROCODE_OLD_INTERFACE=y +CONFIG_X86_MSR=y +CONFIG_X86_CPUID=y +# CONFIG_NOHIGHMEM is not set +# CONFIG_HIGHMEM4G is not set +CONFIG_HIGHMEM64G=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_HIGHMEM=y +CONFIG_X86_PAE=y +CONFIG_ARCH_PHYS_ADDR_T_64BIT=y +CONFIG_ARCH_DMA_ADDR_T_64BIT=y +CONFIG_ARCH_FLATMEM_ENABLE=y +CONFIG_ARCH_SPARSEMEM_ENABLE=y +CONFIG_ARCH_SELECT_MEMORY_MODEL=y +CONFIG_ILLEGAL_POINTER_VALUE=0 +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_SPARSEMEM_STATIC=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_HAVE_MEMBLOCK_NODE_MAP=y +CONFIG_ARCH_DISCARD_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=999999 +# CONFIG_COMPACTION is not set +CONFIG_PHYS_ADDR_T_64BIT=y +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_BOUNCE=y +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_ARCH_SUPPORTS_MEMORY_FAILURE=y +# CONFIG_MEMORY_FAILURE is not set +# CONFIG_TRANSPARENT_HUGEPAGE is not set +CONFIG_CROSS_MEMORY_ATTACH=y +CONFIG_NEED_PER_CPU_KM=y +# CONFIG_CLEANCACHE is not set +# CONFIG_HIGHPTE is not set +# CONFIG_X86_CHECK_BIOS_CORRUPTION is not set +CONFIG_X86_RESERVE_LOW=64 +# CONFIG_MATH_EMULATION is not set +# CONFIG_MTRR is not set +# CONFIG_ARCH_RANDOM is not set +CONFIG_X86_SMAP=y +CONFIG_EFI=y +CONFIG_EFI_STUB=y +CONFIG_EFI_CAPSULE=y +CONFIG_SECCOMP=y +# CONFIG_CC_STACKPROTECTOR is not set +CONFIG_HZ_100=y +# CONFIG_HZ_250 is not set +# CONFIG_HZ_300 is not set +# CONFIG_HZ_1000 is not set +CONFIG_HZ=100 +CONFIG_SCHED_HRTICK=y +CONFIG_KEXEC=y +# CONFIG_CRASH_DUMP is not set +CONFIG_PHYSICAL_START=0x400000 +# CONFIG_RELOCATABLE is not set +CONFIG_PHYSICAL_ALIGN=0x1000000 +# CONFIG_COMPAT_VDSO is not set +# CONFIG_CMDLINE_BOOL is not set +CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y + +# +# Power management and ACPI options +# +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_PM_SLEEP=y +# CONFIG_PM_AUTOSLEEP is not set +# CONFIG_PM_WAKELOCKS is not set +CONFIG_PM_RUNTIME=y +CONFIG_PM=y +CONFIG_PM_DEBUG=y +# CONFIG_PM_ADVANCED_DEBUG is not set +CONFIG_PM_SLEEP_DEBUG=y +CONFIG_PM_TRACE=y +CONFIG_PM_TRACE_RTC=y +CONFIG_ACPI=y +CONFIG_ACPI_SLEEP=y +CONFIG_ACPI_PROCFS=y +CONFIG_ACPI_PROCFS_POWER=y +CONFIG_ACPI_EC_DEBUGFS=y +# CONFIG_ACPI_PROC_EVENT is not set +CONFIG_ACPI_AC=y +# CONFIG_ACPI_BATTERY is not set +CONFIG_ACPI_BUTTON=y +# CONFIG_ACPI_FAN is not set +# CONFIG_ACPI_DOCK is not set +CONFIG_ACPI_I2C=m +CONFIG_ACPI_PROCESSOR=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_BLACKLIST_YEAR=0 +CONFIG_ACPI_DEBUG=y +# CONFIG_ACPI_DEBUG_FUNC_TRACE is not set +CONFIG_ACPI_PCI_SLOT=y +CONFIG_X86_PM_TIMER=y +# CONFIG_ACPI_CONTAINER is not set +# CONFIG_ACPI_SBS is not set +# CONFIG_ACPI_HED is not set +# CONFIG_ACPI_CUSTOM_METHOD is not set +# CONFIG_ACPI_BGRT is not set +# CONFIG_ACPI_APEI is not set +# CONFIG_SFI is not set +# CONFIG_APM is not set + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set +CONFIG_CPU_IDLE=y +# CONFIG_CPU_IDLE_MULTIPLE_DRIVERS is not set +CONFIG_CPU_IDLE_GOV_LADDER=y +CONFIG_CPU_IDLE_GOV_MENU=y +# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set +# CONFIG_INTEL_IDLE is not set + +# +# Bus options (PCI etc.) +# +CONFIG_PCI=y +# CONFIG_PCI_GOBIOS is not set +# CONFIG_PCI_GOMMCONFIG is not set +# CONFIG_PCI_GODIRECT is not set +CONFIG_PCI_GOANY=y +CONFIG_PCI_BIOS=y +CONFIG_PCI_DIRECT=y +CONFIG_PCI_MMCONFIG=y +CONFIG_PCI_DOMAINS=y +# CONFIG_PCI_CNB20LE_QUIRK is not set +CONFIG_PCIEPORTBUS=y +CONFIG_PCIEAER=y +# CONFIG_PCIE_ECRC is not set +# CONFIG_PCIEAER_INJECT is not set +CONFIG_PCIEASPM=y +# CONFIG_PCIEASPM_DEBUG is not set +CONFIG_PCIEASPM_DEFAULT=y +# CONFIG_PCIEASPM_POWERSAVE is not set +# CONFIG_PCIEASPM_PERFORMANCE is not set +CONFIG_PCIE_PME=y +CONFIG_ARCH_SUPPORTS_MSI=y +CONFIG_PCI_MSI=y +CONFIG_PCI_DEBUG=y +# CONFIG_PCI_REALLOC_ENABLE_AUTO is not set +# CONFIG_PCI_STUB is not set +CONFIG_HT_IRQ=y +# CONFIG_PCI_IOV is not set +# CONFIG_PCI_PRI is not set +# CONFIG_PCI_PASID is not set +CONFIG_PCI_IOAPIC=y +CONFIG_PCI_LABEL=y +CONFIG_ISA_DMA_API=y +# CONFIG_ISA is not set +# CONFIG_SCx200 is not set +# CONFIG_ALIX is not set +# CONFIG_NET5501 is not set +CONFIG_AMD_NB=y +# CONFIG_PCCARD is not set +# CONFIG_HOTPLUG_PCI is not set +# CONFIG_RAPIDIO is not set + +# +# Executable file formats / Emulations +# +CONFIG_BINFMT_ELF=y +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set +CONFIG_COREDUMP=y +CONFIG_HAVE_ATOMIC_IOMAP=y +CONFIG_HAVE_TEXT_POKE_SMP=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_DIAG is not set +CONFIG_UNIX=y +# CONFIG_UNIX_DIAG is not set +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +# CONFIG_NET_KEY is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_ARPD is not set +CONFIG_SYN_COOKIES=y +# CONFIG_NET_IPVTI is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=y +CONFIG_INET_XFRM_MODE_BEET=y +CONFIG_INET_LRO=y +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_INET_UDP_DIAG is not set +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +CONFIG_IPV6=m +# CONFIG_IPV6_PRIVACY is not set +# CONFIG_IPV6_ROUTER_PREF is not set +# CONFIG_IPV6_OPTIMISTIC_DAD is not set +# CONFIG_INET6_AH is not set +# CONFIG_INET6_ESP is not set +# CONFIG_INET6_IPCOMP is not set +# CONFIG_IPV6_MIP6 is not set +# CONFIG_INET6_XFRM_TUNNEL is not set +# CONFIG_INET6_TUNNEL is not set +CONFIG_INET6_XFRM_MODE_TRANSPORT=m +CONFIG_INET6_XFRM_MODE_TUNNEL=m +CONFIG_INET6_XFRM_MODE_BEET=m +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +# CONFIG_IPV6_SIT is not set +# CONFIG_IPV6_TUNNEL is not set +# CONFIG_IPV6_GRE is not set +# CONFIG_IPV6_MULTIPLE_TABLES is not set +# CONFIG_IPV6_MROUTE is not set +# CONFIG_NETLABEL is not set +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +# CONFIG_NETFILTER is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +CONFIG_STP=m +CONFIG_GARP=m +# CONFIG_BRIDGE is not set +CONFIG_HAVE_NET_DSA=y +CONFIG_VLAN_8021Q=m +CONFIG_VLAN_8021Q_GVRP=y +# 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_WAN_ROUTER is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set +# CONFIG_DNS_RESOLVER is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_OPENVSWITCH is not set +# CONFIG_NETPRIO_CGROUP is not set +CONFIG_BQL=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=m +CONFIG_BT_RFCOMM=m +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=m +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=m + +# +# Bluetooth device drivers +# +CONFIG_BT_HCIBTUSB=m +# CONFIG_BT_HCIBTSDIO is not set +# CONFIG_BT_HCIUART is not set +# CONFIG_BT_HCIBCM203X is not set +# CONFIG_BT_HCIBPA10X is not set +# CONFIG_BT_HCIBFUSB is not set +CONFIG_BT_HCIVHCI=m +# CONFIG_BT_MRVL is not set +# CONFIG_BT_ATH3K is not set +# CONFIG_AF_RXRPC is not set +CONFIG_WIRELESS=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_CFG80211=m +# CONFIG_NL80211_TESTMODE is not set +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +# CONFIG_CFG80211_CERTIFICATION_ONUS is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_DEBUGFS is not set +# CONFIG_CFG80211_INTERNAL_REGDB is not set +CONFIG_CFG80211_WEXT=y +# CONFIG_LIB80211 is not set +CONFIG_MAC80211=m +CONFIG_MAC80211_HAS_RC=y +# CONFIG_MAC80211_RC_PID is not set +CONFIG_MAC80211_RC_MINSTREL=y +CONFIG_MAC80211_RC_MINSTREL_HT=y +CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y +CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" +# CONFIG_MAC80211_MESH is not set +CONFIG_MAC80211_LEDS=y +# CONFIG_MAC80211_DEBUGFS is not set +# CONFIG_MAC80211_MESSAGE_TRACING is not set +# CONFIG_MAC80211_DEBUG_MENU is not set +# CONFIG_WIMAX is not set +CONFIG_RFKILL=m +CONFIG_RFKILL_LEDS=y +CONFIG_RFKILL_INPUT=y +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set +# CONFIG_CEPH_LIB is not set +# CONFIG_NFC is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +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_DEBUG_DRIVER is not set +CONFIG_DEBUG_DEVRES=y +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_GENERIC_CPU_DEVICES is not set +CONFIG_DMA_SHARED_BUFFER=y +# CONFIG_CMA is not set + +# +# Bus devices +# +# 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 is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=m +CONFIG_MTD_BLKDEVS=m +CONFIG_MTD_BLOCK=m +# CONFIG_MTD_BLOCK_RO is not set +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_SM_FTL is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_TS5500 is not set +# CONFIG_MTD_INTEL_VR_NOR is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_PMC551 is not set +# CONFIG_MTD_DATAFLASH is not set +CONFIG_MTD_M25P80=m +CONFIG_M25PXX_USE_FAST_READ=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_MTD_CLN_ROM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOCG3 is not set +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_PNP=y +# CONFIG_PNP_DEBUG_MESSAGES is not set + +# +# Protocols +# +CONFIG_PNPACPI=y +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_PCIESSD_MTIP32XX is not set +# CONFIG_BLK_CPQ_DA 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=2 +# 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_SX8 is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=1 +CONFIG_BLK_DEV_RAM_SIZE=81920 +# 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 + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_IBM_ASM is not set +# CONFIG_PHANTOM is not set +# CONFIG_INTEL_MID_PTI 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_VMWARE_BALLOON is not set +# CONFIG_BMP085_I2C is not set +# CONFIG_BMP085_SPI is not set +# CONFIG_PCH_PHUB is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +CONFIG_EEPROM_AT24=m +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_EEPROM_93XX46 is not set +# CONFIG_CB710_CORE is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +# CONFIG_SENSORS_LIS3_I2C is not set + +# +# Altera FPGA firmware download module +# +# CONFIG_ALTERA_STAPL is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_TGT is not set +# CONFIG_SCSI_NETLINK 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=y +# CONFIG_CHR_DEV_SCH is not set +# CONFIG_SCSI_MULTI_LUN is not set +CONFIG_SCSI_CONSTANTS=y +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC is not set + +# +# SCSI Transports +# +CONFIG_SCSI_SPI_ATTRS=y +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_SAS_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +# CONFIG_SCSI_LOWLEVEL is not set +# CONFIG_SCSI_DH is not set +# CONFIG_SCSI_OSD_INITIATOR is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +# CONFIG_TARGET_CORE is not set +# CONFIG_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_MACINTOSH_DRIVERS is not set +CONFIG_NETDEVICES=y +CONFIG_NET_CORE=y +# CONFIG_BONDING is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +# CONFIG_NET_FC is not set +CONFIG_MII=y +# CONFIG_NET_TEAM is not set +# CONFIG_MACVLAN is not set +# CONFIG_VXLAN is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +# CONFIG_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_ETHERNET=y +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_NET_VENDOR_ADAPTEC is not set +# CONFIG_NET_VENDOR_ALTEON is not set +# CONFIG_NET_VENDOR_AMD is not set +# CONFIG_NET_VENDOR_ATHEROS is not set +CONFIG_NET_CADENCE=y +# CONFIG_ARM_AT91_ETHER is not set +# CONFIG_MACB is not set +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_VENDOR_BROCADE is not set +# CONFIG_NET_CALXEDA_XGMAC is not set +# CONFIG_NET_VENDOR_CHELSIO is not set +# CONFIG_NET_VENDOR_CISCO 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_HP is not set +CONFIG_NET_VENDOR_INTEL=y +# CONFIG_E100 is not set +CONFIG_E1000=m +# CONFIG_E1000E is not set +# 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_NET_VENDOR_I825XX is not set +# CONFIG_IP1000 is not set +# CONFIG_JME is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MELLANOX is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_MICROCHIP is not set +# CONFIG_NET_VENDOR_MYRI is not set +# CONFIG_FEALNX is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_NET_VENDOR_NVIDIA is not set +# CONFIG_NET_VENDOR_OKI is not set +# CONFIG_ETHOC is not set +# CONFIG_NET_PACKET_ENGINE is not set +# CONFIG_NET_VENDOR_QLOGIC is not set +# CONFIG_NET_VENDOR_REALTEK is not set +# CONFIG_NET_VENDOR_RDC 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=y +CONFIG_STMMAC_ETH=m +# CONFIG_STMMAC_PLATFORM is not set +CONFIG_STMMAC_PCI=y +# CONFIG_STMMAC_DEBUG_FS is not set +CONFIG_STMMAC_DA=y +# CONFIG_STMMAC_PTP is not set +CONFIG_STMMAC_RING=y +# CONFIG_STMMAC_CHAINED is not set +# CONFIG_NET_VENDOR_SUN is not set +# CONFIG_NET_VENDOR_TEHUTI is not set +# CONFIG_NET_VENDOR_TI is not set +# CONFIG_NET_VENDOR_VIA is not set +# CONFIG_NET_VENDOR_WIZNET is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_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 is not set +# CONFIG_DAVICOM_PHY is not set +# CONFIG_QSEMI_PHY is not set +# CONFIG_LXT_PHY is not set +# CONFIG_CICADA_PHY is not set +# CONFIG_VITESSE_PHY is not set +# CONFIG_SMSC_PHY is not set +# CONFIG_BROADCOM_PHY is not set +# CONFIG_BCM87XX_PHY is not set +# CONFIG_ICPLUS_PHY is not set +# CONFIG_REALTEK_PHY is not set +# CONFIG_NATIONAL_PHY is not set +# CONFIG_STE10XP is not set +# CONFIG_LSI_ET1011C_PHY is not set +# CONFIG_MICREL_PHY is not set +# CONFIG_FIXED_PHY is not set +# CONFIG_MDIO_BITBANG is not set +# CONFIG_MICREL_KS8995MA is not set +CONFIG_PPP=m +# CONFIG_PPP_BSDCOMP is not set +CONFIG_PPP_DEFLATE=m +# 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=m +# CONFIG_PPP_SYNC_TTY is not set +# CONFIG_SLIP is not set +CONFIG_SLHC=m + +# +# USB Network Adapters +# +# 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_USBNET is not set +# CONFIG_USB_HSO is not set +# CONFIG_USB_IPHETH is not set +CONFIG_WLAN=y +# CONFIG_LIBERTAS_THINFIRM is not set +# CONFIG_AIRO is not set +# CONFIG_ATMEL is not set +# CONFIG_AT76C50X_USB is not set +# CONFIG_PRISM54 is not set +# CONFIG_USB_ZD1201 is not set +# CONFIG_USB_NET_RNDIS_WLAN is not set +# CONFIG_RTL8180 is not set +# CONFIG_RTL8187 is not set +# CONFIG_ADM8211 is not set +# CONFIG_MAC80211_HWSIM is not set +# CONFIG_MWL8K is not set +# CONFIG_ATH_CARDS is not set +# CONFIG_B43 is not set +# CONFIG_B43LEGACY is not set +# CONFIG_BRCMFMAC is not set +# CONFIG_HOSTAP is not set +# CONFIG_IPW2100 is not set +# CONFIG_IPW2200 is not set +CONFIG_IWLWIFI=m +CONFIG_IWLDVM=m + +# +# Debugging Options +# +# CONFIG_IWLWIFI_DEBUG is not set +CONFIG_IWLWIFI_P2P=y +# CONFIG_IWL4965 is not set +# CONFIG_IWL3945 is not set +# CONFIG_LIBERTAS is not set +# CONFIG_HERMES is not set +# CONFIG_P54_COMMON is not set +# CONFIG_RT2X00 is not set +# CONFIG_RTL8192CE is not set +# CONFIG_RTL8192SE is not set +# CONFIG_RTL8192DE is not set +# CONFIG_RTL8723AE is not set +# CONFIG_RTL8192CU is not set +# CONFIG_WL_TI is not set +# CONFIG_ZD1211RW is not set +# CONFIG_MWIFIEX 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 is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=m +# 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_GAMEPORT is not set + +# +# Character devices +# +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=32 +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=y +# CONFIG_STALDRV is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +# CONFIG_SERIAL_8250_PNP is not set +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_FIX_EARLYCON_MEM=y +CONFIG_SERIAL_8250_PCI=y +CONFIG_SERIAL_8250_NR_UARTS=8 +CONFIG_SERIAL_8250_RUNTIME_UARTS=2 +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_MANY_PORTS=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_8250_DETECT_IRQ=y +CONFIG_SERIAL_8250_RSA=y + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX310X is not set +# CONFIG_SERIAL_MFD_HSU is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_JSM is not set +# CONFIG_SERIAL_SCCNXP is not set +# CONFIG_SERIAL_TIMBERDALE 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_CLN_UART is not set +# CONFIG_SERIAL_PCH_UART is not set +# CONFIG_SERIAL_ARC is not set +# CONFIG_TTY_PRINTK is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_NVRAM is not set +CONFIG_RTC=y +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set +# CONFIG_SONYPI is not set +# CONFIG_MWAVE is not set +# CONFIG_PC8736x_GPIO is not set +# CONFIG_NSC_GPIO is not set +# CONFIG_RAW_DRIVER is not set +CONFIG_HPET=y +# CONFIG_HPET_MMAP is not set +# CONFIG_HANGCHECK_TIMER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_TELCLOCK is not set +CONFIG_DEVPORT=y +CONFIG_I2C=m +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=m +# CONFIG_I2C_MUX is not set +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# PC SMBus host controller drivers +# +# CONFIG_I2C_ALI1535 is not set +# CONFIG_I2C_ALI1563 is not set +# CONFIG_I2C_ALI15X3 is not set +# CONFIG_I2C_AMD756 is not set +# CONFIG_I2C_AMD8111 is not set +# CONFIG_I2C_I801 is not set +# CONFIG_I2C_ISCH is not set +# CONFIG_I2C_PIIX4 is not set +# CONFIG_I2C_NFORCE2 is not set +# CONFIG_I2C_SIS5595 is not set +# CONFIG_I2C_SIS630 is not set +# CONFIG_I2C_SIS96X is not set +# CONFIG_I2C_VIA is not set +# CONFIG_I2C_VIAPRO is not set + +# +# 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_EG20T is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_INTEL_MID 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_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_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_SCx200_ACB is not set +# 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=y +CONFIG_GEN3_SPI=y +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_ALTERA is not set +CONFIG_SPI_BITBANG=y +CONFIG_SPI_GPIO=y +# CONFIG_SPI_OC_TINY is not set +CONFIG_SPI_PXA2XX=m +CONFIG_SPI_PXA2XX_PCI=m +# CONFIG_SPI_CE5XX_SPI_SLAVE is not set +# CONFIG_SPI_SC18IS602 is not set +# CONFIG_SPI_TOPCLIFF_PCH is not set +# CONFIG_SPI_XCOMM is not set +# CONFIG_SPI_XILINX is not set +# CONFIG_SPI_DESIGNWARE is not set +# CONFIG_SPI_LPC_SCH is not set + +# +# SPI Protocol Masters +# +CONFIG_SPI_SPIDEV=m +# CONFIG_SPI_TLE62X0 is not set +# CONFIG_HSI is not set + +# +# PPS support +# +CONFIG_PPS=m +# 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=m + +# +# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. +# +CONFIG_PTP_1588_CLOCK_PCH=m +CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +CONFIG_GPIO_ACPI=y +# CONFIG_DEBUG_GPIO is not set +CONFIG_GPIO_SYSFS=y + +# +# Memory mapped GPIO drivers: +# +# CONFIG_GPIO_GENERIC_PLATFORM is not set +# CONFIG_GPIO_IT8761E is not set +# CONFIG_GPIO_TS5500 is not set +CONFIG_GPIO_SCH=m +# CONFIG_GPIO_ICH is not set +# CONFIG_GPIO_VX855 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_ADP5588 is not set + +# +# PCI GPIO expanders: +# +# CONFIG_GPIO_BT8XX is not set +# CONFIG_GPIO_AMD8111 is not set +# CONFIG_GPIO_LANGWELL is not set +# CONFIG_GPIO_PCH 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: +# + +# +# 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_GENERIC_ADC_BATTERY 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_SMB347 is not set +# CONFIG_POWER_RESET is not set +# CONFIG_POWER_AVS is not set +# CONFIG_HWMON is not set +CONFIG_THERMAL=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_FAIR_SHARE is not set +CONFIG_STEP_WISE=y +# CONFIG_USER_SPACE 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_SM501 is not set +# CONFIG_MFD_RTSX_PCI is not set +# CONFIG_MFD_TI_AM335X_TSCADC is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_MFD_LM3533 is not set +# CONFIG_TPS6105X is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_TPS65912_SPI is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_DA9052_SPI is not set +# CONFIG_MFD_ARIZONA_I2C is not set +# CONFIG_MFD_ARIZONA_SPI is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_MC13XXX_SPI is not set +# CONFIG_MFD_MC13XXX_I2C is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_CS5535 is not set +# CONFIG_MFD_TIMBERDALE is not set +CONFIG_CY8C9540A=m +CONFIG_INTEL_CLN_GIP=m +CONFIG_LPC_SCH=y +# CONFIG_LPC_ICH is not set +# CONFIG_MFD_RDC321X is not set +# CONFIG_MFD_JANZ_CMODIO is not set +# CONFIG_MFD_VX855 is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_VIPERBOARD is not set +# CONFIG_MFD_RETU is not set +# CONFIG_REGULATOR is not set +CONFIG_MEDIA_SUPPORT=m + +# +# Multimedia core support +# +CONFIG_MEDIA_CAMERA_SUPPORT=y +# CONFIG_MEDIA_ANALOG_TV_SUPPORT is not set +# CONFIG_MEDIA_DIGITAL_TV_SUPPORT is not set +# CONFIG_MEDIA_RADIO_SUPPORT is not set +# CONFIG_MEDIA_RC_SUPPORT is not set +# CONFIG_MEDIA_CONTROLLER is not set +CONFIG_VIDEO_DEV=m +CONFIG_VIDEO_V4L2=m +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +CONFIG_VIDEOBUF2_CORE=m +CONFIG_VIDEOBUF2_MEMOPS=m +CONFIG_VIDEOBUF2_VMALLOC=m + +# +# Media drivers +# +CONFIG_MEDIA_USB_SUPPORT=y + +# +# Webcam devices +# +CONFIG_USB_VIDEO_CLASS=m +CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y +# CONFIG_USB_GSPCA is not set +# CONFIG_USB_PWC is not set +# CONFIG_VIDEO_CPIA2 is not set +# CONFIG_USB_ZR364XX is not set +# CONFIG_USB_STKWEBCAM is not set +# CONFIG_USB_S2255 is not set +# CONFIG_USB_SN9C102 is not set + +# +# Webcam, TV (analog/digital) USB devices +# +# CONFIG_VIDEO_EM28XX is not set +# CONFIG_MEDIA_PCI_SUPPORT is not set +# CONFIG_V4L_PLATFORM_DRIVERS is not set +# CONFIG_V4L_MEM2MEM_DRIVERS is not set +# CONFIG_V4L_TEST_DRIVERS is not set + +# +# Supported MMC/SDIO adapters +# +# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set + +# +# Media ancillary drivers (tuners, sensors, i2c, frontends) +# + +# +# Encoders, decoders, sensors and other helper chips +# + +# +# Audio decoders, processors and mixers +# +# CONFIG_VIDEO_TVAUDIO is not set +# CONFIG_VIDEO_TDA7432 is not set +# CONFIG_VIDEO_TDA9840 is not set +# CONFIG_VIDEO_TEA6415C is not set +# CONFIG_VIDEO_TEA6420 is not set +# CONFIG_VIDEO_MSP3400 is not set +# CONFIG_VIDEO_CS5345 is not set +# CONFIG_VIDEO_CS53L32A is not set +# CONFIG_VIDEO_TLV320AIC23B is not set +# CONFIG_VIDEO_WM8775 is not set +# CONFIG_VIDEO_WM8739 is not set +# CONFIG_VIDEO_VP27SMPX is not set + +# +# RDS decoders +# +# CONFIG_VIDEO_SAA6588 is not set + +# +# Video decoders +# +# CONFIG_VIDEO_ADV7180 is not set +# CONFIG_VIDEO_ADV7183 is not set +# CONFIG_VIDEO_BT819 is not set +# CONFIG_VIDEO_BT856 is not set +# CONFIG_VIDEO_BT866 is not set +# CONFIG_VIDEO_KS0127 is not set +# CONFIG_VIDEO_SAA7110 is not set +# CONFIG_VIDEO_SAA711X is not set +# CONFIG_VIDEO_SAA7191 is not set +# CONFIG_VIDEO_TVP514X is not set +# CONFIG_VIDEO_TVP5150 is not set +# CONFIG_VIDEO_TVP7002 is not set +# CONFIG_VIDEO_VPX3220 is not set + +# +# Video and audio decoders +# +# CONFIG_VIDEO_SAA717X is not set +# CONFIG_VIDEO_CX25840 is not set + +# +# MPEG video encoders +# +# CONFIG_VIDEO_CX2341X is not set + +# +# Video encoders +# +# CONFIG_VIDEO_SAA7127 is not set +# CONFIG_VIDEO_SAA7185 is not set +# CONFIG_VIDEO_ADV7170 is not set +# CONFIG_VIDEO_ADV7175 is not set +# CONFIG_VIDEO_ADV7343 is not set +# CONFIG_VIDEO_ADV7393 is not set +# CONFIG_VIDEO_AK881X is not set + +# +# Camera sensor devices +# +# CONFIG_VIDEO_OV7670 is not set +# CONFIG_VIDEO_VS6624 is not set +# CONFIG_VIDEO_MT9V011 is not set +# CONFIG_VIDEO_TCM825X is not set +# CONFIG_VIDEO_SR030PC30 is not set + +# +# Flash devices +# + +# +# Video improvement chips +# +# CONFIG_VIDEO_UPD64031A is not set +# CONFIG_VIDEO_UPD64083 is not set + +# +# Miscelaneous helper chips +# +# CONFIG_VIDEO_THS7303 is not set +# CONFIG_VIDEO_M52790 is not set + +# +# Sensors used on soc_camera driver +# + +# +# Customise DVB Frontends +# +# CONFIG_DVB_AU8522_V4L is not set +# CONFIG_DVB_TUNER_DIB0070 is not set +# CONFIG_DVB_TUNER_DIB0090 is not set + +# +# Tools to develop new frontends +# +# CONFIG_DVB_DUMMY_FE is not set + +# +# Graphics support +# +# CONFIG_AGP is not set +# CONFIG_VGA_ARB is not set +# CONFIG_VGA_SWITCHEROO is not set +# CONFIG_DRM is not set +# CONFIG_STUB_POULSBO is not set +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +# CONFIG_FB is not set +# CONFIG_EXYNOS_VIDEO is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +CONFIG_SOUND=m +# CONFIG_SOUND_OSS_CORE is not set +CONFIG_SND=m +CONFIG_SND_TIMER=m +CONFIG_SND_PCM=m +CONFIG_SND_HWDEP=m +CONFIG_SND_RAWMIDI=m +# CONFIG_SND_SEQUENCER is not set +# CONFIG_SND_MIXER_OSS is not set +# CONFIG_SND_PCM_OSS is not set +# CONFIG_SND_HRTIMER is not set +# CONFIG_SND_RTCTIMER is not set +# CONFIG_SND_DYNAMIC_MINORS is not set +CONFIG_SND_SUPPORT_OLD_API=y +CONFIG_SND_VERBOSE_PROCFS=y +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set +CONFIG_SND_DMA_SGBUF=y +# CONFIG_SND_RAWMIDI_SEQ is not set +# CONFIG_SND_OPL3_LIB_SEQ is not set +# CONFIG_SND_OPL4_LIB_SEQ is not set +# CONFIG_SND_SBAWE_SEQ is not set +# CONFIG_SND_EMU10K1_SEQ is not set +CONFIG_SND_DRIVERS=y +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_ALOOP is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_MPU401 is not set +CONFIG_SND_PCI=y +# CONFIG_SND_AD1889 is not set +# CONFIG_SND_ALS300 is not set +# CONFIG_SND_ALS4000 is not set +# CONFIG_SND_ALI5451 is not set +# CONFIG_SND_ASIHPI is not set +# CONFIG_SND_ATIIXP is not set +# CONFIG_SND_ATIIXP_MODEM is not set +# CONFIG_SND_AU8810 is not set +# CONFIG_SND_AU8820 is not set +# CONFIG_SND_AU8830 is not set +# CONFIG_SND_AW2 is not set +# CONFIG_SND_AZT3328 is not set +# CONFIG_SND_BT87X is not set +# CONFIG_SND_CA0106 is not set +# CONFIG_SND_CMIPCI is not set +# CONFIG_SND_OXYGEN is not set +# CONFIG_SND_CS4281 is not set +# CONFIG_SND_CS46XX is not set +# CONFIG_SND_CS5530 is not set +# CONFIG_SND_CS5535AUDIO is not set +# CONFIG_SND_CTXFI is not set +# CONFIG_SND_DARLA20 is not set +# CONFIG_SND_GINA20 is not set +# CONFIG_SND_LAYLA20 is not set +# CONFIG_SND_DARLA24 is not set +# CONFIG_SND_GINA24 is not set +# CONFIG_SND_LAYLA24 is not set +# CONFIG_SND_MONA is not set +# CONFIG_SND_MIA is not set +# CONFIG_SND_ECHO3G is not set +# CONFIG_SND_INDIGO is not set +# CONFIG_SND_INDIGOIO is not set +# CONFIG_SND_INDIGODJ is not set +# CONFIG_SND_INDIGOIOX is not set +# CONFIG_SND_INDIGODJX is not set +# CONFIG_SND_EMU10K1 is not set +# CONFIG_SND_EMU10K1X is not set +# CONFIG_SND_ENS1370 is not set +# CONFIG_SND_ENS1371 is not set +# CONFIG_SND_ES1938 is not set +# CONFIG_SND_ES1968 is not set +# CONFIG_SND_FM801 is not set +# CONFIG_SND_HDA_INTEL is not set +# CONFIG_SND_HDSP is not set +# CONFIG_SND_HDSPM is not set +# CONFIG_SND_ICE1712 is not set +# CONFIG_SND_ICE1724 is not set +# CONFIG_SND_INTEL8X0 is not set +# CONFIG_SND_INTEL8X0M is not set +# CONFIG_SND_KORG1212 is not set +# CONFIG_SND_LOLA is not set +# CONFIG_SND_LX6464ES is not set +# CONFIG_SND_MAESTRO3 is not set +# CONFIG_SND_MIXART is not set +# CONFIG_SND_NM256 is not set +# CONFIG_SND_PCXHR is not set +# CONFIG_SND_RIPTIDE is not set +# CONFIG_SND_RME32 is not set +# CONFIG_SND_RME96 is not set +# CONFIG_SND_RME9652 is not set +# CONFIG_SND_SIS7019 is not set +# CONFIG_SND_SONICVIBES is not set +# CONFIG_SND_TRIDENT is not set +# CONFIG_SND_VIA82XX is not set +# CONFIG_SND_VIA82XX_MODEM is not set +# CONFIG_SND_VIRTUOSO is not set +# CONFIG_SND_VX222 is not set +# CONFIG_SND_YMFPCI is not set +CONFIG_SND_SPI=y +CONFIG_SND_USB=y +CONFIG_SND_USB_AUDIO=m +# CONFIG_SND_USB_UA101 is not set +# CONFIG_SND_USB_USX2Y is not set +# CONFIG_SND_USB_CAIAQ is not set +# CONFIG_SND_USB_US122L is not set +# CONFIG_SND_USB_6FIRE is not set +# CONFIG_SND_SOC is not set +# CONFIG_SOUND_PRIME 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_AUREAL is not set +# CONFIG_HID_BELKIN is not set +# CONFIG_HID_CHERRY is not set +# CONFIG_HID_CHICONY is not set +# CONFIG_HID_PRODIKEYS 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_EZKEY is not set +# CONFIG_HID_HOLTEK 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_TPKBD 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_PETALYNX is not set +# CONFIG_HID_PICOLCD is not set +# CONFIG_HID_PRIMAX is not set +# CONFIG_HID_PS3REMOTE 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_SUNPLUS 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_WIIMOTE is not set +# CONFIG_HID_ZEROPLUS is not set +# CONFIG_HID_ZYDACRON is not set +# CONFIG_HID_SENSOR_HUB is not set + +# +# USB HID support +# +CONFIG_USB_HID=m +# CONFIG_HID_PID is not set +# CONFIG_USB_HIDDEV is not set + +# +# USB HID Boot Protocol drivers +# +# CONFIG_USB_KBD is not set +# CONFIG_USB_MOUSE is not set + +# +# I2C HID support +# +# CONFIG_I2C_HID is not set +CONFIG_USB_ARCH_HAS_OHCI=y +CONFIG_USB_ARCH_HAS_EHCI=y +CONFIG_USB_ARCH_HAS_XHCI=y +CONFIG_USB_SUPPORT=y +CONFIG_USB_COMMON=m +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB=m +# CONFIG_USB_DEBUG is not set +# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set + +# +# Miscellaneous USB options +# +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_SUSPEND is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_DWC3 is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_XHCI_HCD is not set +CONFIG_USB_EHCI_HCD=m +# CONFIG_USB_EHCI_ROOT_HUB_TT is not set +CONFIG_USB_EHCI_TT_NEWSCHED=y +CONFIG_USB_EHCI_PCI=m +# 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_OHCI_HCD=m +# CONFIG_USB_OHCI_HCD_PLATFORM is not set +# CONFIG_USB_EHCI_HCD_PLATFORM is not set +# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set +# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set +CONFIG_USB_OHCI_LITTLE_ENDIAN=y +CONFIG_USB_UHCI_HCD=m +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_MUSB_HDRC is not set +# CONFIG_USB_CHIPIDEA is not set +# CONFIG_USB_RENESAS_USBHS is not set + +# +# USB Device Class drivers +# +CONFIG_USB_ACM=m +# 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=m +# 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 + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set + +# +# USB port drivers +# +CONFIG_USB_SERIAL=m +# CONFIG_USB_SERIAL_GENERIC 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_FUNSOFT 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_MOTOROLA is not set +# CONFIG_USB_SERIAL_NAVMAN is not set +CONFIG_USB_SERIAL_PL2303=m +# 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_HP4X is not set +# CONFIG_USB_SERIAL_SAFE is not set +# CONFIG_USB_SERIAL_SIEMENS_MPI 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_VIVOPAY_SERIAL is not set +# CONFIG_USB_SERIAL_ZIO is not set +# CONFIG_USB_SERIAL_ZTE 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_ISIGHTFW is not set +# CONFIG_USB_YUREX is not set +# CONFIG_USB_EZUSB_FX2 is not set + +# +# USB Physical Layer drivers +# +# CONFIG_USB_ISP1301 is not set +# CONFIG_USB_RCAR_PHY is not set +CONFIG_USB_GADGET=m +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +# CONFIG_USB_GADGET_DEBUG_FS is not set +CONFIG_USB_GADGET_VBUS_DRAW=2 +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 + +# +# USB Peripheral Controller +# +# CONFIG_USB_R8A66597 is not set +# CONFIG_USB_MV_UDC is not set +# CONFIG_USB_M66592 is not set +# CONFIG_USB_AMD5536UDC is not set +# CONFIG_USB_NET2272 is not set +# CONFIG_USB_NET2280 is not set +# CONFIG_USB_GOKU is not set +CONFIG_USB_EG20T=m +# CONFIG_USB_DUMMY_HCD is not set +CONFIG_USB_LIBCOMPOSITE=m +CONFIG_USB_ZERO=m +# CONFIG_USB_AUDIO is not set +CONFIG_USB_ETH=m +CONFIG_USB_ETH_RNDIS=y +# CONFIG_USB_ETH_EEM is not set +# CONFIG_USB_G_NCM is not set +# CONFIG_USB_GADGETFS is not set +# CONFIG_USB_FUNCTIONFS is not set +CONFIG_USB_MASS_STORAGE=m +CONFIG_USB_G_SERIAL=m +# CONFIG_USB_MIDI_GADGET is not set +# CONFIG_USB_G_PRINTER is not set +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_ACM_MS is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set +# CONFIG_USB_G_WEBCAM is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_NOP_USB_XCEIV is not set +# CONFIG_UWB is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_UNSAFE_RESUME 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_CB710 is not set +# CONFIG_MMC_VIA_SDMMC is not set +# CONFIG_MMC_VUB300 is not set +# CONFIG_MMC_USHC is not set +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=m + +# +# 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_PCA955X is not set +# CONFIG_LEDS_PCA9633 is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_LT3593 is not set +# CONFIG_LEDS_TCA6507 is not set +# CONFIG_LEDS_LM355x is not set +# CONFIG_LEDS_OT200 is not set +# CONFIG_LEDS_BLINKM is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +# CONFIG_LEDS_TRIGGER_TIMER is not set +# CONFIG_LEDS_TRIGGER_ONESHOT is not set +# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_CPU is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_LEDS_TRIGGER_TRANSIENT is not set +# CONFIG_ACCESSIBILITY is not set +# CONFIG_INFINIBAND is not set +# CONFIG_EDAC is not set +# CONFIG_RTC_CLASS is not set +CONFIG_DMADEVICES=y +# CONFIG_DMADEVICES_DEBUG is not set + +# +# DMA Devices +# +CONFIG_INTEL_MID_DMAC=m +CONFIG_INTEL_MID_PCI=m +CONFIG_INTEL_CLN_DMAC=m +# CONFIG_INTEL_IOATDMA is not set +# CONFIG_TIMB_DMA is not set +# CONFIG_PCH_DMA is not set +CONFIG_DMA_ENGINE=y + +# +# DMA Clients +# +# CONFIG_NET_DMA is not set +# CONFIG_ASYNC_TX_DMA is not set +# CONFIG_DMATEST is not set +# CONFIG_AUXDISPLAY is not set +CONFIG_UIO=m +# CONFIG_UIO_CIF is not set +# CONFIG_UIO_PDRV 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 + +# +# Virtio drivers +# +# CONFIG_VIRTIO_PCI is not set +# CONFIG_VIRTIO_MMIO is not set + +# +# Microsoft Hyper-V guest support +# +# CONFIG_HYPERV is not set +CONFIG_STAGING=y +# CONFIG_ET131X is not set +# CONFIG_SLICOSS is not set +# CONFIG_USBIP_CORE is not set +# CONFIG_W35UND is not set +# CONFIG_PRISM2_USB is not set +# CONFIG_ECHO is not set +# CONFIG_COMEDI is not set +# CONFIG_ASUS_OLED is not set +# CONFIG_R8187SE is not set +# CONFIG_RTL8192U is not set +# CONFIG_RTLLIB is not set +# CONFIG_R8712U is not set +# CONFIG_RTS5139 is not set +# CONFIG_TRANZPORT is not set +# CONFIG_LINE6_USB is not set +# CONFIG_USB_SERIAL_QUATECH2 is not set +# CONFIG_VT6655 is not set +# CONFIG_VT6656 is not set +# CONFIG_DX_SEP is not set + +# +# IIO staging drivers +# +# CONFIG_IIO_SW_RING is not set + +# +# Accelerometers +# +# CONFIG_ADIS16201 is not set +# CONFIG_ADIS16203 is not set +# CONFIG_ADIS16204 is not set +# CONFIG_ADIS16209 is not set +# CONFIG_ADIS16220 is not set +# CONFIG_ADIS16240 is not set +# CONFIG_KXSD9 is not set +# CONFIG_LIS3L02DQ is not set +# CONFIG_SCA3000 is not set + +# +# Analog to digital converters +# +# CONFIG_AD7291 is not set +# CONFIG_AD7606 is not set +# CONFIG_AD799X is not set +# CONFIG_AD7780 is not set +# CONFIG_AD7816 is not set +# CONFIG_AD7192 is not set +# CONFIG_ADT7410 is not set +# CONFIG_AD7280 is not set +CONFIG_MAX78M6610_LMU=m + +# +# Analog digital bi-direction converters +# +# CONFIG_ADT7316 is not set + +# +# Capacitance to digital converters +# +# CONFIG_AD7150 is not set +# CONFIG_AD7152 is not set +# CONFIG_AD7746 is not set + +# +# Direct Digital Synthesis +# +# CONFIG_AD5930 is not set +# CONFIG_AD9832 is not set +# CONFIG_AD9834 is not set +# CONFIG_AD9850 is not set +# CONFIG_AD9852 is not set +# CONFIG_AD9910 is not set +# CONFIG_AD9951 is not set + +# +# Digital gyroscope sensors +# +# CONFIG_ADIS16060 is not set +# CONFIG_ADIS16080 is not set +# CONFIG_ADIS16130 is not set +# CONFIG_ADIS16260 is not set +# CONFIG_ADXRS450 is not set + +# +# Network Analyzer, Impedance Converters +# +# CONFIG_AD5933 is not set + +# +# Inertial measurement units +# +# CONFIG_ADIS16400 is not set + +# +# Light sensors +# +# CONFIG_SENSORS_ISL29018 is not set +# CONFIG_SENSORS_ISL29028 is not set +# CONFIG_SENSORS_TSL2563 is not set +# CONFIG_TSL2583 is not set +# CONFIG_TSL2x7x is not set + +# +# Magnetometer sensors +# +# CONFIG_SENSORS_AK8975 is not set +# CONFIG_SENSORS_HMC5843 is not set + +# +# Active energy metering IC +# +# CONFIG_ADE7753 is not set +# CONFIG_ADE7754 is not set +# CONFIG_ADE7758 is not set +# CONFIG_ADE7759 is not set +# CONFIG_ADE7854 is not set + +# +# Resolver to digital converters +# +# CONFIG_AD2S90 is not set +# CONFIG_AD2S1200 is not set +# CONFIG_AD2S1210 is not set + +# +# Triggers - standalone +# +# CONFIG_IIO_GPIO_TRIGGER is not set +CONFIG_IIO_SYSFS_TRIGGER=m +# CONFIG_IIO_SIMPLE_DUMMY is not set +# CONFIG_ZSMALLOC is not set +# CONFIG_CRYSTALHD is not set +# CONFIG_ACPI_QUICKSTART is not set +# CONFIG_USB_ENESTORAGE 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_CSR_WIFI is not set +# CONFIG_NET_VENDOR_SILICOM is not set +# CONFIG_CED1401 is not set +# CONFIG_DGRP is not set +# CONFIG_SB105X is not set +CONFIG_X86_PLATFORM_DEVICES=y +# CONFIG_ACERHDF is not set +# CONFIG_ASUS_LAPTOP is not set +# CONFIG_FUJITSU_TABLET is not set +# CONFIG_AMILO_RFKILL is not set +# CONFIG_HP_ACCEL is not set +# CONFIG_SONY_LAPTOP is not set +# CONFIG_THINKPAD_ACPI is not set +# CONFIG_SENSORS_HDAPS is not set +# CONFIG_INTEL_MENLOW is not set +# CONFIG_ACPI_WMI is not set +# CONFIG_TOPSTAR_LAPTOP is not set +# CONFIG_TOSHIBA_BT_RFKILL is not set +# CONFIG_ACPI_CMPC is not set +CONFIG_INTEL_CLN_ESRAM=y +CONFIG_INTEL_CLN_ECC_REFRESH_PERIOD=24 +CONFIG_INTEL_CLN_ECC_SCRUB=y +# CONFIG_INTEL_CLN_ECC_SCRUB_OVERRIDE_CONFIG is not set +# CONFIG_INTEL_CLN_ECC_SCRUB_S3_CONFIG is not set +CONFIG_INTEL_CLN_THERMAL=y +CONFIG_INTEL_CLN_AUDIO_CTRL=m +# CONFIG_INTEL_IPS is not set +# CONFIG_IBM_RTL is not set +# CONFIG_XO15_EBOOK is not set + +# +# Hardware Spinlock drivers +# +CONFIG_CLKSRC_I8253=y +CONFIG_CLKEVT_I8253=y +CONFIG_CLKBLD_I8253=y +# CONFIG_IOMMU_SUPPORT is not set + +# +# Remoteproc drivers (EXPERIMENTAL) +# +# CONFIG_STE_MODEM_RPROC is not set + +# +# Rpmsg drivers (EXPERIMENTAL) +# +# CONFIG_VIRT_DRIVERS is not set +# CONFIG_PM_DEVFREQ is not set +# CONFIG_EXTCON is not set +# CONFIG_MEMORY is not set +CONFIG_IIO=m +CONFIG_IIO_BUFFER=y +CONFIG_IIO_BUFFER_CB=y +CONFIG_IIO_KFIFO_BUF=m +CONFIG_IIO_TRIGGERED_BUFFER=m +CONFIG_IIO_TRIGGER=y +CONFIG_IIO_CONSUMERS_PER_TRIGGER=2 + +# +# Accelerometers +# +CONFIG_IIO_LIS331DLH_INTEL_CLN=m + +# +# Analog to digital converters +# +# CONFIG_AD7266 is not set +CONFIG_AD7298=m +# CONFIG_AD7791 is not set +# CONFIG_AD7793 is not set +# CONFIG_AD7476 is not set +# CONFIG_AD7887 is not set +# CONFIG_MAX1363 is not set +# CONFIG_TI_ADC081C is not set + +# +# Amplifiers +# +# CONFIG_AD8366 is not set + +# +# STMicro sensors +# +CONFIG_IIO_ST_SENSORS_I2C=m +CONFIG_IIO_ST_SENSORS_SPI=m +CONFIG_IIO_ST_SENSORS_CORE=m + +# +# Digital to analog converters +# +# CONFIG_AD5064 is not set +# CONFIG_AD5360 is not set +# CONFIG_AD5380 is not set +# CONFIG_AD5421 is not set +# CONFIG_AD5624R_SPI is not set +# CONFIG_AD5446 is not set +# CONFIG_AD5449 is not set +# CONFIG_AD5504 is not set +# CONFIG_AD5755 is not set +# CONFIG_AD5764 is not set +# CONFIG_AD5791 is not set +# CONFIG_AD5686 is not set +# CONFIG_MAX517 is not set +# CONFIG_MCP4725 is not set + +# +# Frequency Synthesizers DDS/PLL +# + +# +# Clock Generator/Distribution +# +# CONFIG_AD9523 is not set + +# +# Phase-Locked Loop (PLL) frequency synthesizers +# +# CONFIG_ADF4350 is not set + +# +# Digital gyroscope sensors +# +# CONFIG_ADIS16136 is not set + +# +# Inertial measurement units +# +# CONFIG_ADIS16480 is not set + +# +# Light sensors +# +# CONFIG_ADJD_S311 is not set +# CONFIG_VCNL4000 is not set + +# +# Magnetometer sensors +# +# CONFIG_VME_BUS is not set +CONFIG_PWM=y +CONFIG_PWM_SYSFS=y +# CONFIG_IPACK_BUS is not set + +# +# Firmware Drivers +# +# CONFIG_EDD is not set +CONFIG_FIRMWARE_MEMMAP=y +CONFIG_EFI_VARS=m +# CONFIG_DELL_RBU is not set +# CONFIG_DCDBAS is not set +# CONFIG_ISCSI_IBFT_FIND is not set +# CONFIG_GOOGLE_FIRMWARE is not set + +# +# 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=y +CONFIG_EXT3_FS_POSIX_ACL=y +CONFIG_EXT3_FS_SECURITY=y +# CONFIG_EXT4_FS is not set +CONFIG_JBD=y +# CONFIG_JBD_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_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +CONFIG_FS_POSIX_ACL=y +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set +CONFIG_GENERIC_ACL=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 is not set +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_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=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 is not set +# CONFIG_MISC_FILESYSTEMS is not set +CONFIG_NETWORK_FILESYSTEMS=y +# CONFIG_NFS_FS is not set +# CONFIG_NFSD 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=y +# 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 is not set + +# +# Kernel hacking +# +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_PRINTK_TIME=y +CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 +# CONFIG_ENABLE_WARN_DEPRECATED is not set +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=2048 +CONFIG_MAGIC_SYSRQ=y +# 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=y +# CONFIG_DEBUG_SECTION_MISMATCH is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_PANIC_ON_OOPS is not set +CONFIG_PANIC_ON_OOPS_VALUE=0 +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_SCHED_DEBUG is not set +# CONFIG_SCHEDSTATS is not set +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_SLUB_STATS is not set +CONFIG_HAVE_DEBUG_KMEMLEAK=y +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_MUTEXES=y +CONFIG_DEBUG_LOCK_ALLOC=y +CONFIG_PROVE_LOCKING=y +# CONFIG_PROVE_RCU is not set +# CONFIG_SPARSE_RCU_POINTER is not set +CONFIG_LOCKDEP=y +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_LOCKDEP is not set +CONFIG_TRACE_IRQFLAGS=y +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +CONFIG_STACKTRACE=y +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_HIGHMEM is not set +CONFIG_DEBUG_BUGVERBOSE=y +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_VIRTUAL is not set +# CONFIG_DEBUG_WRITECOUNT is not set +CONFIG_DEBUG_MEMORY_INIT=y +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +CONFIG_ARCH_WANT_FRAME_POINTERS=y +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_LKDTM is not set +# CONFIG_NOTIFIER_ERROR_INJECTION is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_DEBUG_PAGEALLOC is not set +CONFIG_USER_STACKTRACE_SUPPORT=y +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_FP_TEST=y +CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST=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 +# CONFIG_RBTREE_TEST is not set +# CONFIG_INTERVAL_TREE_TEST is not set +# CONFIG_PROVIDE_OHCI1394_DMA_INIT is not set +# CONFIG_BUILD_DOCSRC is not set +# CONFIG_DYNAMIC_DEBUG is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +CONFIG_HAVE_ARCH_KMEMCHECK=y +# CONFIG_KMEMCHECK is not set +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_X86_VERBOSE_BOOTUP=y +CONFIG_EARLY_PRINTK=y +# CONFIG_EARLY_PRINTK_DBGP is not set +CONFIG_DEBUG_STACKOVERFLOW=y +CONFIG_X86_PTDUMP=y +CONFIG_DEBUG_RODATA=y +# CONFIG_DEBUG_RODATA_TEST is not set +CONFIG_DEBUG_SET_MODULE_RONX=y +# CONFIG_DEBUG_NX_TEST is not set +# CONFIG_DOUBLEFAULT 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=y +# CONFIG_CPA_DEBUG is not set +CONFIG_OPTIMIZE_INLINING=y +# CONFIG_DEBUG_STRICT_USER_COPY_CHECKS is not set +# CONFIG_DEBUG_NMI_SELFTEST is not set + +# +# Security options +# +CONFIG_KEYS=y +# CONFIG_ENCRYPTED_KEYS is not set +CONFIG_KEYS_DEBUG_PROC_KEYS=y +# CONFIG_SECURITY_DMESG_RESTRICT is not set +CONFIG_SECURITY=y +# CONFIG_SECURITYFS is not set +CONFIG_SECURITY_NETWORK=y +# CONFIG_SECURITY_NETWORK_XFRM 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_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_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=m +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG=m +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP2=y +CONFIG_CRYPTO_MANAGER=m +CONFIG_CRYPTO_MANAGER2=y +# CONFIG_CRYPTO_USER is not set +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +# CONFIG_CRYPTO_CBC is not set +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_ECB=m +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_CRC32C_INTEL is not set +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +CONFIG_CRYPTO_SHA256=m +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_AES_586 is not set +# CONFIG_CRYPTO_AES_NI_INTEL is not set +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=m +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SALSA20_586 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_SERPENT_SSE2_586 is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set +# CONFIG_CRYPTO_TWOFISH_586 is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +CONFIG_CRYPTO_ANSI_CPRNG=m +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +CONFIG_CRYPTO_HW=y +# CONFIG_CRYPTO_DEV_PADLOCK is not set +# CONFIG_CRYPTO_DEV_GEODE is not set +# CONFIG_ASYMMETRIC_KEY_TYPE is not set +CONFIG_HAVE_KVM=y +# CONFIG_VIRTUALIZATION is not set +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_STRNCPY_FROM_USER=y +CONFIG_GENERIC_STRNLEN_USER=y +CONFIG_GENERIC_FIND_FIRST_BIT=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IOMAP=y +CONFIG_GENERIC_IO=y +CONFIG_PERCPU_RWSEM=y +CONFIG_CRC_CCITT=m +CONFIG_CRC16=y +CONFIG_CRC_T10DIF=y +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +# CONFIG_CRC8 is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=m +# CONFIG_XZ_DEC is not set +# CONFIG_XZ_DEC_BCJ is not set +CONFIG_DECOMPRESS_GZIP=y +CONFIG_DECOMPRESS_BZIP2=y +CONFIG_DECOMPRESS_LZMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_DQL=y +CONFIG_NLATTR=y +CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y +CONFIG_AVERAGE=y +# CONFIG_CORDIC is not set +# CONFIG_DDR is not set diff --git a/recipes-kernel/linux/files/clanton.patch b/recipes-kernel/linux/files/clanton.patch new file mode 100644 index 0000000..437b591 --- /dev/null +++ b/recipes-kernel/linux/files/clanton.patch @@ -0,0 +1,30644 @@ +diff --git a/.gitignore b/.gitignore +index 3b8b9b3..3557999 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -64,11 +64,11 @@ include/generated + arch/*/include/generated + + # stgit generated dirs +-patches-* ++#patches-* + + # quilt's files +-patches +-series ++#patches ++#series + + # cscope files + cscope.* +diff --git a/Documentation/ABI/testing/sysfs-class-pwm b/Documentation/ABI/testing/sysfs-class-pwm +new file mode 100644 +index 0000000..c479d77 +--- /dev/null ++++ b/Documentation/ABI/testing/sysfs-class-pwm +@@ -0,0 +1,79 @@ ++What: /sys/class/pwm/ ++Date: May 2013 ++KernelVersion: 3.11 ++Contact: H Hartley Sweeten <hsweeten@visionengravers.com> ++Description: ++ The pwm/ class sub-directory belongs to the Generic PWM ++ Framework and provides a sysfs interface for using PWM ++ channels. ++ ++What: /sys/class/pwm/pwmchipN/ ++Date: May 2013 ++KernelVersion: 3.11 ++Contact: H Hartley Sweeten <hsweeten@visionengravers.com> ++Description: ++ A /sys/class/pwm/pwmchipN directory is created for each ++ probed PWM controller/chip where N is the base of the ++ PWM chip. ++ ++What: /sys/class/pwm/pwmchipN/npwm ++Date: May 2013 ++KernelVersion: 3.11 ++Contact: H Hartley Sweeten <hsweeten@visionengravers.com> ++Description: ++ The number of PWM channels supported by the PWM chip. ++ ++What: /sys/class/pwm/pwmchipN/export ++Date: May 2013 ++KernelVersion: 3.11 ++Contact: H Hartley Sweeten <hsweeten@visionengravers.com> ++Description: ++ Exports a PWM channel from the PWM chip for sysfs control. ++ Value is between 0 and /sys/class/pwm/pwmchipN/npwm - 1. ++ ++What: /sys/class/pwm/pwmchipN/unexport ++Date: May 2013 ++KernelVersion: 3.11 ++Contact: H Hartley Sweeten <hsweeten@visionengravers.com> ++Description: ++ Unexports a PWM channel. ++ ++What: /sys/class/pwm/pwmchipN/pwmX ++Date: May 2013 ++KernelVersion: 3.11 ++Contact: H Hartley Sweeten <hsweeten@visionengravers.com> ++Description: ++ A /sys/class/pwm/pwmchipN/pwmX directory is created for ++ each exported PWM channel where X is the exported PWM ++ channel number. ++ ++What: /sys/class/pwm/pwmchipN/pwmX/period ++Date: May 2013 ++KernelVersion: 3.11 ++Contact: H Hartley Sweeten <hsweeten@visionengravers.com> ++Description: ++ Sets the PWM signal period in nanoseconds. ++ ++What: /sys/class/pwm/pwmchipN/pwmX/duty_cycle ++Date: May 2013 ++KernelVersion: 3.11 ++Contact: H Hartley Sweeten <hsweeten@visionengravers.com> ++Description: ++ Sets the PWM signal duty cycle in nanoseconds. ++ ++What: /sys/class/pwm/pwmchipN/pwmX/polarity ++Date: May 2013 ++KernelVersion: 3.11 ++Contact: H Hartley Sweeten <hsweeten@visionengravers.com> ++Description: ++ Sets the output polarity of the PWM signal to "normal" or ++ "inversed". ++ ++What: /sys/class/pwm/pwmchipN/pwmX/enable ++Date: May 2013 ++KernelVersion: 3.11 ++Contact: H Hartley Sweeten <hsweeten@visionengravers.com> ++Description: ++ Enable/disable the PWM signal. ++ 0 is disabled ++ 1 is enabled +diff --git a/Documentation/pwm.txt b/Documentation/pwm.txt +index 7d2b4c9..1039b68 100644 +--- a/Documentation/pwm.txt ++++ b/Documentation/pwm.txt +@@ -45,6 +45,43 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns); + + To start/stop toggling the PWM output use pwm_enable()/pwm_disable(). + ++Using PWMs with the sysfs interface ++----------------------------------- ++ ++If CONFIG_SYSFS is enabled in your kernel configuration a simple sysfs ++interface is provided to use the PWMs from userspace. It is exposed at ++/sys/class/pwm/. Each probed PWM controller/chip will be exported as ++pwmchipN, where N is the base of the PWM chip. Inside the directory you ++will find: ++ ++npwm - The number of PWM channels this chip supports (read-only). ++ ++export - Exports a PWM channel for use with sysfs (write-only). ++ ++unexport - Unexports a PWM channel from sysfs (write-only). ++ ++The PWM channels are numbered using a per-chip index from 0 to npwm-1. ++ ++When a PWM channel is exported a pwmX directory will be created in the ++pwmchipN directory it is associated with, where X is the number of the ++channel that was exported. The following properties will then be available: ++ ++period - The total period of the PWM signal (read/write). ++ Value is in nanoseconds and is the sum of the active and inactive ++ time of the PWM. ++ ++duty_cycle - The active time of the PWM signal (read/write). ++ Value is in nanoseconds and must be less than the period. ++ ++polarity - Changes the polarity of the PWM signal (read/write). ++ Writes to this property only work if the PWM chip supports changing ++ the polarity. The polarity can only be changed if the PWM is not ++ enabled. Value is the string "normal" or "inversed". ++ ++enable - Enable/disable the PWM signal (read/write). ++ 0 - disabled ++ 1 - enabled ++ + Implementing a PWM driver + ------------------------- + +diff --git a/Documentation/usb/linux-cdc-acm.inf b/Documentation/usb/linux-cdc-acm.inf +index f0ffc27..e56f074 100644 +--- a/Documentation/usb/linux-cdc-acm.inf ++++ b/Documentation/usb/linux-cdc-acm.inf +@@ -90,10 +90,10 @@ ServiceBinary=%12%\USBSER.sys + [SourceDisksFiles] + [SourceDisksNames] + [DeviceList] +-%DESCRIPTION%=DriverInstall, USB\VID_0525&PID_A4A7, USB\VID_1D6B&PID_0104&MI_02, USB\VID_1D6B&PID_0106&MI_00 ++%DESCRIPTION%=DriverInstall, USB\VID_0525&PID_A4A7, USB\VID_1D6B&PID_0104&MI_02, USB\VID_1D6B&PID_0106&MI_00, USB\VID_8086&PID_BABE + + [DeviceList.NTamd64] +-%DESCRIPTION%=DriverInstall, USB\VID_0525&PID_A4A7, USB\VID_1D6B&PID_0104&MI_02, USB\VID_1D6B&PID_0106&MI_00 ++%DESCRIPTION%=DriverInstall, USB\VID_0525&PID_A4A7, USB\VID_1D6B&PID_0104&MI_02, USB\VID_1D6B&PID_0106&MI_00, USB\VID_8086&PID_BABE + + + ;------------------------------------------------------------------------------ +diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig +index 0694d09..7509992 100644 +--- a/arch/x86/Kconfig ++++ b/arch/x86/Kconfig +@@ -417,6 +417,14 @@ config X86_INTEL_CE + This option compiles in support for the CE4100 SOC for settop + boxes and media devices. + ++config INTEL_QUARK_X1000_SOC ++ bool "Intel Quark X1000 SOC support" ++ depends on M586TSC ++ select ARCH_REQUIRE_GPIOLIB ++ ---help--- ++ Quark X1000 SOC support . This option enables probing for various ++ PCI-IDs of several on-chip devices provided by the X1000 ++ + config X86_WANT_INTEL_MID + bool "Intel MID platform support" + depends on X86_32 +@@ -500,6 +508,13 @@ config X86_SUPPORTS_MEMORY_FAILURE + depends on X86_64 || !SPARSEMEM + select ARCH_SUPPORTS_MEMORY_FAILURE + ++menu "Intel Media SOC Gen3 support" ++ ++config ARCH_GEN3 ++ bool "Enable Intel Media SOC Gen3 support" ++ default y ++ ++endmenu + config X86_VISWS + bool "SGI 320/540 (Visual Workstation)" + depends on X86_32 && PCI && X86_MPPARSE && PCI_GODIRECT +@@ -1524,6 +1539,13 @@ config EFI_STUB + + See Documentation/x86/efi-stub.txt for more information. + ++config EFI_CAPSULE ++ bool "EFI capsule update support" ++ depends on EFI ++ ---help--- ++ This kernel feature allows for loading of EFI capsule code ++ with callbacks into the EDK firmware to execute update ++ + config SECCOMP + def_bool y + prompt "Enable seccomp to safely compute untrusted bytecode" +diff --git a/arch/x86/include/asm/cln.h b/arch/x86/include/asm/cln.h +new file mode 100644 +index 0000000..4a5b0e3 +--- /dev/null ++++ b/arch/x86/include/asm/cln.h +@@ -0,0 +1,85 @@ ++/* ++ * Copyright(c) 2013 Intel Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ */ ++#ifndef _ASM_X86_CLN_H ++#define _ASM_X86_CLN_H ++ ++#include <linux/pci.h> ++#include <linux/msi.h> ++ ++/** ++ * cln_pci_pvm_mask ++ * ++ * Mask PVM bit on a per function basis. Clanton SC components have but one ++ * vector each - so we mask for what we need ++ */ ++static inline void cln_pci_pvm_mask(struct pci_dev * dev) ++{ ++ struct msi_desc *entry; ++ int mask_bits = 1; ++ ++ if(unlikely(dev->msi_enabled == 0)) ++ return; ++ ++ entry = list_first_entry(&dev->msi_list, struct msi_desc, list); ++ ++ if(unlikely(entry == NULL)) ++ return; ++ ++ pci_write_config_dword(dev, entry->mask_pos, mask_bits); ++} ++ ++/** ++ * cln_pci_pvm_mask ++ * ++ * UnMask PVM bit on a per function basis. Clanton SC components have but one ++ * vector each - so we unmask for what we need ++ */ ++static inline void cln_pci_pvm_unmask(struct pci_dev * dev) ++{ ++ struct msi_desc *entry; ++ int mask_bits = 0; ++ ++ if(unlikely(dev->msi_enabled == 0)) ++ return; ++ ++ entry = list_first_entry(&dev->msi_list, struct msi_desc, list); ++ ++ if(unlikely(entry == NULL)) ++ return; ++ ++ pci_write_config_dword(dev, entry->mask_pos, mask_bits); ++} ++ ++/* Convienence macros */ ++#if defined(CONFIG_INTEL_QUARK_X1000_SOC) ++ #define mask_pvm(x) cln_pci_pvm_mask(x) ++ #define unmask_pvm(x) cln_pci_pvm_unmask(x) ++#else ++ #define mask_pvm(x) ++ #define unmask_pvm(x) ++#endif ++ ++/* Serial */ ++#if defined(CONFIG_INTEL_QUARK_X1000_SOC) ++ #define SERIAL_PORT_DFNS ++ #define BASE_BAUD 2764800 ++#endif ++ ++#endif /* _ASM_X86_CLN_H */ +diff --git a/arch/x86/include/asm/imr.h b/arch/x86/include/asm/imr.h +new file mode 100644 +index 0000000..2c17eec +--- /dev/null ++++ b/arch/x86/include/asm/imr.h +@@ -0,0 +1,22 @@ ++/* ++ * imr.h: Intel Clanton platform imr setup code ++ * ++ * (C) Copyright 2012 Intel Corporation ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; version 2 ++ * of the License. ++ */ ++#ifndef _ASM_X86_IMR_H ++#define _ASM_X86_IMR_H ++ ++#if defined(CONFIG_INTEL_QUARK_X1000_SOC) ++ extern int intel_cln_imr_runt_setparams(void); ++ extern int intel_cln_imr_lockall(void); ++#else ++ static void intel_cln_imr_runt_setparams(void){} ++ static void intel_cln_imr_lockall(void){} ++#endif ++ ++#endif /* _ASM_X86_IMR_H */ +diff --git a/arch/x86/include/asm/serial.h b/arch/x86/include/asm/serial.h +index 628c801..3965f86 100644 +--- a/arch/x86/include/asm/serial.h ++++ b/arch/x86/include/asm/serial.h +@@ -1,6 +1,8 @@ + #ifndef _ASM_X86_SERIAL_H + #define _ASM_X86_SERIAL_H + ++#include <asm/cln.h> ++ + /* + * This assumes you have a 1.8432 MHz clock for your UART. + * +@@ -8,7 +10,9 @@ + * clock, since the 16550A is capable of handling a top speed of 1.5 + * megabits/second; but this requires the faster clock. + */ ++#ifndef BASE_BAUD + #define BASE_BAUD ( 1843200 / 16 ) ++#endif + + /* Standard COM flags (except for COM4, because of the 8514 problem) */ + #ifdef CONFIG_SERIAL_DETECT_IRQ +@@ -19,11 +23,13 @@ + #define STD_COM4_FLAGS ASYNC_BOOT_AUTOCONF + #endif + ++#ifndef SERIAL_PORT_DFNS + #define SERIAL_PORT_DFNS \ + /* UART CLK PORT IRQ FLAGS */ \ + { 0, BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS }, /* ttyS0 */ \ + { 0, BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS }, /* ttyS1 */ \ + { 0, BASE_BAUD, 0x3E8, 4, STD_COM_FLAGS }, /* ttyS2 */ \ + { 0, BASE_BAUD, 0x2E8, 3, STD_COM4_FLAGS }, /* ttyS3 */ ++#endif + + #endif /* _ASM_X86_SERIAL_H */ +diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c +index fcaabd0..ea7624c 100644 +--- a/arch/x86/kernel/cpu/intel.c ++++ b/arch/x86/kernel/cpu/intel.c +@@ -143,6 +143,17 @@ static void __cpuinit early_init_intel(struct cpuinfo_x86 *c) + setup_clear_cpu_cap(X86_FEATURE_ERMS); + } + } ++ ++ /* ++ * Quark X1000 PGE is advertised but not implemented. This matters since ++ * cpu_has_pge is used to determine the type of TLB flushing to do. With ++ * PGE not actually doing what it says on the tin writes to CR4.PGE do ++ * nothing when we should be re-writing CR3 like a 486 ++ */ ++ if (c->x86 == 5 && c->x86_model == 9){ ++ printk(KERN_INFO "Disabling PGE capability bit\n"); ++ setup_clear_cpu_cap(X86_FEATURE_PGE); ++ } + } + + #ifdef CONFIG_X86_32 +diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c +index 8b24289..c963186 100644 +--- a/arch/x86/kernel/setup.c ++++ b/arch/x86/kernel/setup.c +@@ -761,7 +761,10 @@ void __init setup_arch(char **cmdline_p) + KERNEL_PGD_PTRS); + + load_cr3(swapper_pg_dir); +- __flush_tlb_all(); ++ if (boot_cpu_data.x86 == 5 && boot_cpu_data.x86_model == 9) ++ __flush_tlb(); ++ else ++ __flush_tlb_all(); + #else + printk(KERN_INFO "Command line: %s\n", boot_command_line); + #endif +diff --git a/arch/x86/platform/efi/Makefile b/arch/x86/platform/efi/Makefile +index 6db1cc4..41ac17c 100644 +--- a/arch/x86/platform/efi/Makefile ++++ b/arch/x86/platform/efi/Makefile +@@ -1,2 +1,3 @@ + obj-$(CONFIG_EFI) += efi.o efi_$(BITS).o efi_stub_$(BITS).o +-obj-$(CONFIG_ACPI_BGRT) += efi-bgrt.o ++obj-$(CONFIG_ACPI_BGRT) += efi-bgrt.o ++obj-$(CONFIG_EFI_CAPSULE) += efi_capsule.o +diff --git a/arch/x86/platform/efi/efi-bgrt.c b/arch/x86/platform/efi/efi-bgrt.c +index d9c1b95..9fd5168 100644 +--- a/arch/x86/platform/efi/efi-bgrt.c ++++ b/arch/x86/platform/efi/efi-bgrt.c +@@ -24,19 +24,29 @@ struct bmp_header { + u32 size; + } __packed; + +-void efi_bgrt_init(void) ++bool __init efi_bgrt_probe(void) + { + acpi_status status; +- void __iomem *image; +- bool ioremapped = false; +- struct bmp_header bmp_header; + + if (acpi_disabled) +- return; ++ return false; + ++ bgrt_tab = NULL; + status = acpi_get_table("BGRT", 0, + (struct acpi_table_header **)&bgrt_tab); + if (ACPI_FAILURE(status)) ++ return false; ++ ++ return true; ++} ++ ++void __init efi_bgrt_init(void) ++{ ++ void __iomem *image; ++ bool ioremapped = false; ++ struct bmp_header bmp_header; ++ ++ if (acpi_disabled || bgrt_tab == NULL) + return; + + if (bgrt_tab->header.length < sizeof(*bgrt_tab)) +diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c +index e2cd38f..0e22b5f4 100644 +--- a/arch/x86/platform/efi/efi.c ++++ b/arch/x86/platform/efi/efi.c +@@ -847,6 +847,7 @@ void __init efi_enter_virtual_mode(void) + u64 end, systab, end_pfn; + void *p, *va, *new_memmap = NULL; + int count = 0; ++ bool bgrt_map; + + efi.systab = NULL; + +@@ -860,6 +861,11 @@ void __init efi_enter_virtual_mode(void) + return; + } + ++ /* ++ * Determine if mapping EFI boot code/data is required for BGRT mapping ++ */ ++ bgrt_map = efi_bgrt_probe(); ++ + /* Merge contiguous regions of the same type and attribute */ + for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { + u64 prev_size; +@@ -889,9 +895,9 @@ void __init efi_enter_virtual_mode(void) + + for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { + md = p; +- if (!(md->attribute & EFI_MEMORY_RUNTIME) && +- md->type != EFI_BOOT_SERVICES_CODE && +- md->type != EFI_BOOT_SERVICES_DATA) ++ if (!((md->attribute & EFI_MEMORY_RUNTIME) || (bgrt_map && ++ (md->type == EFI_BOOT_SERVICES_CODE || ++ md->type == EFI_BOOT_SERVICES_DATA)))) + continue; + + size = md->num_pages << EFI_PAGE_SHIFT; +diff --git a/arch/x86/platform/efi/efi_capsule.c b/arch/x86/platform/efi/efi_capsule.c +new file mode 100644 +index 0000000..d329d6d +--- /dev/null ++++ b/arch/x86/platform/efi/efi_capsule.c +@@ -0,0 +1,320 @@ ++/* ++ * Copyright(c) 2013 Intel Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ */ ++ ++#include <asm/cln.h> ++#include <linux/errno.h> ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/efi.h> ++ ++#define DRIVER_NAME "efi_capsule_update" ++#define PFX "efi-capsupdate: " ++#define MAX_PATH 256 ++#define MAX_CHUNK PAGE_SIZE ++#define CSH_HDR_SIZE 0x400 ++ ++typedef struct { ++ u64 length; ++ union { ++ u64 data_block; ++ u64 continuation_pointer; ++ }; ++} efi_blk_desc_t; ++ ++static struct kobject * efi_capsule_kobj; ++static struct list_head sg_list; ++static char fpath[MAX_PATH]; ++static int csh_jump = CSH_HDR_SIZE; /* Clanton EDK wants CSH jump */ ++ ++/** ++ * efi_capsule_trigger_update ++ * ++ * Trigger the EFI capsule update ++ */ ++static int efi_capsule_trigger_update(void) ++{ ++ struct file *fp = NULL; ++ mm_segment_t old_fs = get_fs(); ++ int ret = 0; ++ u32 nblocks = 0, i = 0, total_size = 0, data_len = 0, offset = 0; ++ efi_capsule_header_t *chdr = NULL; ++ efi_blk_desc_t * desc_block = NULL; ++ u8 ** data = NULL; ++ ++ set_fs (KERNEL_DS); ++ fp = filp_open(fpath, O_RDONLY, 0); ++ ++ /* Sanity check input */ ++ if (IS_ERR(fp)|| fp->f_op == NULL ||fp->f_op->read == NULL || ++ fp->f_dentry->d_inode->i_size == 0){ ++ pr_err(PFX"file open [%s] error!\n", fpath); ++ ret = -EINVAL; ++ goto done; ++ } ++ ++ /* Determine necessary sizes */ ++ nblocks = (fp->f_dentry->d_inode->i_size/MAX_CHUNK) + 2; ++ total_size = fp->f_dentry->d_inode->i_size; ++ ++ pr_info(PFX "nblocks %d total_size %d\n", nblocks, total_size); ++ ++ /* Allocate array of descriptor blocks + 1 for terminator */ ++ desc_block = (efi_blk_desc_t*)kzalloc(nblocks * sizeof(efi_blk_desc_t), GFP_KERNEL); ++ if (desc_block == NULL){ ++ pr_info(PFX"%s failed to allocate %d blocks\n", __func__, nblocks); ++ ret = -ENOMEM; ++ goto done_close; ++ } ++ ++ pr_info(PFX"File %s size %u descriptor blocks %u\n", ++ fpath, total_size, nblocks); ++ ++ data = kmalloc(nblocks, GFP_KERNEL); ++ if (data == NULL){ ++ ret = -ENOMEM; ++ pr_info("Failed to allocate %d bytes\n", nblocks); ++ goto done; ++ } ++ ++ for (i = 0; i < nblocks; i++){ ++ data[i] = kmalloc(MAX_CHUNK, GFP_KERNEL); ++ if (data[i] == NULL){ ++ ret = -ENOMEM; ++ pr_info("Alloc fail %d bytes entry %d\n", ++ nblocks, i); ++ goto done; ++ } ++ ++ } ++ ++ /* Read in data */ ++ for (i = 0; i < nblocks && offset < total_size; i++){ ++ /* Determine read len */ ++ data_len = offset < total_size - MAX_CHUNK ? ++ MAX_CHUNK : total_size - offset; ++ ret = fp->f_op->read(fp, data[i], data_len, &fp->f_pos); ++ if (ret < 0){ ++ pr_err(PFX"Error reading @ data %u\n", offset); ++ ret = -EIO; ++ goto done; ++ } ++ offset += data_len; ++ ++ /* Sanity check */ ++ if (i >= nblocks){ ++ pr_err(PFX"%s Driver bug line %d\n", __func__, __LINE__); ++ ret = -EINVAL; ++ goto done; ++ } ++ ++ /* Validate header as appropriate */ ++ if (chdr == NULL){ ++ chdr = (efi_capsule_header_t*)&data[i][csh_jump]; ++ desc_block[i].data_block = __pa(&data[i][csh_jump]); ++ desc_block[i].length = data_len - csh_jump; ++ pr_info(PFX"hdr offset in file %d bytes\n", csh_jump); ++ pr_info(PFX"hdr size %u flags 0x%08x imagesize 0x%08x\n", ++ chdr->headersize, chdr->flags, chdr->imagesize); ++ ++ }else{ ++ desc_block[i].data_block = __pa(&data[i][0]); ++ desc_block[i].length = data_len; ++ } ++ ++ pr_info(PFX "block %d length %u data @ phys 0x%08x\n", ++ i, (int)desc_block[i].length, ++ (unsigned int)desc_block[i].data_block); ++ } ++ ++ if (i > nblocks-1){ ++ pr_err(PFX"%s Used block %d expected %d !\n", __func__, i, nblocks-1); ++ ret = -EINVAL; ++ goto done; ++ } ++ ++ pr_info(PFX"submitting capsule to EDKII firmware\n"); ++ ++ ret = efi.update_capsule(&chdr, 1, __pa(desc_block)); ++ if(ret != EFI_SUCCESS) { ++ pr_err(PFX"submission fail err=0x%08x\n", ret); ++ }else{ ++ pr_info(PFX"submission success\n"); ++ ret = 0; ++ } ++ ++ if (chdr != NULL && chdr->flags & 0x10000){ ++ pr_info(PFX"capsule persist across S3 skipping capsule free\n"); ++ goto done_close; ++ } ++done: ++ for (i = 0; i < nblocks; i++){ ++ if (data && data[i]) ++ kfree(data[i]); ++ } ++ if (data) ++ kfree(data); ++ ++ if (desc_block != NULL) ++ kfree(desc_block); ++done_close: ++ if (!IS_ERR(fp)) ++ filp_close(fp, NULL); ++ ++ set_fs (old_fs); ++ return ret; ++} ++ ++/** ++ * efi_capsule_csh_jump ++ * ++ * sysfs callback used to show current path ++ */ ++static ssize_t efi_capsule_csh_jump_show(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ return snprintf(buf, sizeof(fpath), "%d\n", csh_jump > 0); ++} ++ ++/** ++ * efi_capsule_path_store ++ * ++ * sysfs callback used to set a new capsule path ++ */ ++static ssize_t efi_capsule_csh_jump_store(struct kobject *kobj, struct kobj_attribute *attr, ++ const char *buf, size_t count) ++{ ++ if (buf != NULL && buf[0] == '0') ++ csh_jump = 0; ++ else ++ csh_jump = CSH_HDR_SIZE; ++ return count; ++} ++ ++static struct kobj_attribute efi_capsule_csh_jump_attr = ++ __ATTR(csh_jump, 0644, efi_capsule_csh_jump_show, efi_capsule_csh_jump_store); ++ ++/** ++ * efi_capsule_path_show ++ * ++ * sysfs callback used to show current path ++ */ ++static ssize_t efi_capsule_path_show(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ return snprintf(buf, sizeof(fpath), fpath); ++} ++ ++/** ++ * efi_capsule_path_store ++ * ++ * sysfs callback used to set a new capsule path ++ */ ++static ssize_t efi_capsule_path_store(struct kobject *kobj, struct kobj_attribute *attr, ++ const char *buf, size_t count) ++{ ++ if (count > MAX_PATH-1) ++ return -EINVAL; ++ ++ memset(fpath, 0x00, sizeof(fpath)); ++ memcpy(fpath, buf, count); ++ ++ return count; ++} ++ ++static struct kobj_attribute efi_capsule_path_attr = ++ __ATTR(capsule_path, 0644, efi_capsule_path_show, efi_capsule_path_store); ++ ++/** ++ * efi_capsule_update_store ++ * ++ * sysfs callback used to initiate update ++ */ ++static ssize_t efi_capsule_update_store(struct kobject *kobj, struct kobj_attribute *attr, ++ const char *buf, size_t count) ++{ int ret = 0; ++ ++ ret = efi_capsule_trigger_update(); ++ return ret == 0 ? count : ret; ++} ++ ++static struct kobj_attribute efi_capsule_update_attr = ++ __ATTR(capsule_update, 0644, NULL, efi_capsule_update_store); ++ ++#define SYSFS_ERRTXT "Error adding sysfs entry!\n" ++/** ++ * intel_cln_capsule_update_init ++ * ++ * @return 0 success < 0 failure ++ * ++ * Module entry point ++ */ ++static int __init efi_capsule_update_init(void) ++{ ++ int retval = 0; ++ extern struct kobject * firmware_kobj; ++ ++ INIT_LIST_HEAD(&sg_list); ++ ++ /* efi_capsule_kobj subordinate of firmware @ /sys/firmware/efi */ ++ efi_capsule_kobj = kobject_create_and_add("efi", firmware_kobj); ++ if (!efi_capsule_kobj) { ++ pr_err(PFX"kset create error\n"); ++ retval = -ENODEV; ++ goto err; ++ } ++ ++ if(sysfs_create_file(efi_capsule_kobj, &efi_capsule_path_attr.attr)) { ++ pr_err(PFX SYSFS_ERRTXT); ++ retval = -ENODEV; ++ goto err; ++ } ++ if(sysfs_create_file(efi_capsule_kobj, &efi_capsule_update_attr.attr)) { ++ pr_err(PFX SYSFS_ERRTXT); ++ retval = -ENODEV; ++ goto err; ++ ++ } ++ if(sysfs_create_file(efi_capsule_kobj, &efi_capsule_csh_jump_attr.attr)) { ++ pr_err(PFX SYSFS_ERRTXT); ++ retval = -ENODEV; ++ goto err; ++ ++ } ++ ++err: ++ return retval; ++} ++ ++/** ++ * intel_cln_esram_exit ++ * ++ * Module exit ++ */ ++static void __exit efi_capsule_update_exit(void) ++{ ++} ++ ++MODULE_AUTHOR("Bryan O'Donoghue <bryan.odonoghue@intel.com>"); ++MODULE_DESCRIPTION("EFI Capsule Update driver"); ++MODULE_LICENSE("Dual BSD/GPL"); ++ ++module_init(efi_capsule_update_init); ++module_exit(efi_capsule_update_exit); +diff --git a/drivers/base/class.c b/drivers/base/class.c +index 03243d4..3ce8454 100644 +--- a/drivers/base/class.c ++++ b/drivers/base/class.c +@@ -420,8 +420,8 @@ EXPORT_SYMBOL_GPL(class_for_each_device); + * code. There's no locking restriction. + */ + struct device *class_find_device(struct class *class, struct device *start, +- void *data, +- int (*match)(struct device *, void *)) ++ const void *data, ++ int (*match)(struct device *, const void *)) + { + struct class_dev_iter iter; + struct device *dev; +diff --git a/drivers/base/core.c b/drivers/base/core.c +index a235085..dda0c7f 100644 +--- a/drivers/base/core.c ++++ b/drivers/base/core.c +@@ -1617,9 +1617,9 @@ struct device *device_create(struct class *class, struct device *parent, + } + EXPORT_SYMBOL_GPL(device_create); + +-static int __match_devt(struct device *dev, void *data) ++static int __match_devt(struct device *dev, const void *data) + { +- dev_t *devt = data; ++ const dev_t *devt = data; + + return dev->devt == *devt; + } +diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig +index d4c1218..955d8fe 100644 +--- a/drivers/dma/Kconfig ++++ b/drivers/dma/Kconfig +@@ -34,18 +34,46 @@ if DMADEVICES + comment "DMA Devices" + + config INTEL_MID_DMAC ++ tristate "Intel DMAC Moorsetown/Medfield/Clanton DMA controllers" ++ depends on X86 ++ default n ++ help ++ Enable support for the Intel(R) MID/Clanton DMA engine present ++ in Intel MID chipsets and Clanton SOC devices ++ ++ Say Y here if you have such a chipset. ++ ++ If unsure, say N. ++ ++if INTEL_MID_DMAC ++ ++config INTEL_MID_PCI + tristate "Intel MID DMA support for Peripheral DMA controllers" +- depends on PCI && X86 ++ depends on X86 && PCI + select DMA_ENGINE + default n + help + Enable support for the Intel(R) MID DMA engine present +- in Intel MID chipsets. ++ in Intel MID chipsets + + Say Y here if you have such a chipset. + + If unsure, say N. + ++config INTEL_CLN_DMAC ++ tristate "Intel CLN DMA support for Peripheral DMA controllers" ++ depends on PCI && X86 && INTEL_QUARK_X1000_SOC ++ default n ++ help ++ Enable support for the Intel(R) Clanton DMA engine present ++ in Intel Clanton DMA enabled UART. This is not a generic DMA ++ driver, instead this enables DMAC regs for Clanton's UART alone ++ ++ Say Y here if you have a Clanton processor. ++ ++ If unsure, say N. ++endif ++ + config ASYNC_TX_ENABLE_CHANNEL_SWITCH + bool + +diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c +index a0de82e..91883c2 100644 +--- a/drivers/dma/intel_mid_dma.c ++++ b/drivers/dma/intel_mid_dma.c +@@ -30,9 +30,9 @@ + #include <linux/module.h> + + #include "dmaengine.h" ++#include "intel_mid_dma_regs.h" + + #define MAX_CHAN 4 /*max ch across controllers*/ +-#include "intel_mid_dma_regs.h" + + #define INTEL_MID_DMAC1_ID 0x0814 + #define INTEL_MID_DMAC2_ID 0x0813 +@@ -43,6 +43,14 @@ + #define LNW_PERIPHRAL_STATUS 0x0 + #define LNW_PERIPHRAL_MASK 0x8 + ++/** ++ * struct intel_mid_dma_probe_info ++ * ++ * @max_chan: maximum channels to probe ++ * @ch_base: offset from register base ++ * @block_size: TBD ++ * @pimr_mask: indicates if mask registers to be mapped ++ */ + struct intel_mid_dma_probe_info { + u8 max_chan; + u8 ch_base; +@@ -1015,7 +1023,7 @@ static void dma_tasklet2(unsigned long data) + * See if this is our interrupt if so then schedule the tasklet + * otherwise ignore + */ +-static irqreturn_t intel_mid_dma_interrupt(int irq, void *data) ++irqreturn_t intel_mid_dma_interrupt(int irq, void *data) + { + struct middma_device *mid = data; + u32 tfr_status, err_status; +@@ -1048,6 +1056,7 @@ static irqreturn_t intel_mid_dma_interrupt(int irq, void *data) + + return IRQ_HANDLED; + } ++EXPORT_SYMBOL(intel_mid_dma_interrupt); + + static irqreturn_t intel_mid_dma_interrupt1(int irq, void *data) + { +diff --git a/drivers/dma/intel_mid_dma/Makefile b/drivers/dma/intel_mid_dma/Makefile +new file mode 100644 +index 0000000..567eca5 +--- /dev/null ++++ b/drivers/dma/intel_mid_dma/Makefile +@@ -0,0 +1,3 @@ ++obj-$(CONFIG_INTEL_MID_DMAC) += intel_mid_dma.o ++intel_mid_dma-objs:= intel_mid_dma_core.o intel_cln_dma_pci.o intel_mid_dma_pci.o ++ +diff --git a/drivers/dma/intel_mid_dma/intel_cln_dma_pci.c b/drivers/dma/intel_mid_dma/intel_cln_dma_pci.c +new file mode 100644 +index 0000000..442c2f2 +--- /dev/null ++++ b/drivers/dma/intel_mid_dma/intel_cln_dma_pci.c +@@ -0,0 +1,153 @@ ++/* ++ * Copyright(c) 2013 Intel Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ */ ++ ++/* ++ * intel_mid_dma.c - Intel Langwell DMA Drivers ++ * ++ * Author: Vinod Koul <bryan.odonoghue@linux.intel.com> ++ * This is an entry point for Intel Clanton based DMAC on Clanton's UART ++ * specifically we don't have a dedicated PCI function, instead we have DMAC ++ * regs hung off of a PCI BAR. This entry/exit allows re-use of the core ++ * DMA API for MID devices manipulated to suit our BAR setup ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ */ ++#include <linux/pci.h> ++#include <linux/interrupt.h> ++#include <linux/pm_runtime.h> ++#include <linux/intel_mid_dma.h> ++#include <linux/module.h> ++ ++#include "intel_mid_dma_core.h" ++#include "intel_mid_dma_regs.h" ++ ++/** ++ * intel_mid_dma_probe - PCI Probe ++ * @pdev: Controller PCI device structure ++ * @id: pci device id structure ++ * ++ * Initialize the PCI device, map BARs, query driver data. ++ * Call intel_setup_dma to complete contoller and chan initilzation ++ */ ++int __devinit intel_cln_dma_probe(struct pci_dev *pdev, ++ struct middma_device *device) ++{ ++ u32 base_addr, bar_size; ++ int err; ++ ++ pr_debug("MDMA: probe for %x\n", pdev->device); ++ pr_debug("MDMA: CH %d, base %d, block len %d, Periphral mask %x\n", ++ device->max_chan, device->chan_base, ++ device->block_size, device->pimr_mask); ++ ++ device->pdev = pci_dev_get(pdev); ++ device->ispci_fn = true; ++ ++ base_addr = pci_resource_start(pdev, 1); ++ bar_size = pci_resource_len(pdev, 1); ++ device->dma_base = ioremap_nocache(base_addr, DMA_REG_SIZE); ++ if (!device->dma_base) { ++ pr_err("ERR_MDMA:ioremap failed\n"); ++ err = -ENOMEM; ++ goto err_ioremap; ++ } ++ ++ err = intel_mid_dma_setup(pdev, device, false); ++ if (err) ++ goto err_dma; ++ ++ return 0; ++ ++err_dma: ++ iounmap(device->dma_base); ++err_ioremap: ++ pr_err("ERR_MDMA:Probe failed %d\n", err); ++ return err; ++} ++EXPORT_SYMBOL(intel_cln_dma_probe); ++ ++/** ++ * intel_mid_dma_remove - PCI remove ++ * @pdev: Controller PCI device structure ++ * ++ * Free up all resources and data ++ * Call shutdown_dma to complete contoller and chan cleanup ++ */ ++void __devexit intel_cln_dma_remove(struct pci_dev *pdev, struct middma_device *device) ++{ ++ intel_mid_dma_shutdown(pdev, device); ++} ++EXPORT_SYMBOL(intel_cln_dma_remove); ++ ++/* Power Management */ ++/* ++* dma_suspend - PCI suspend function ++* ++* @pci: PCI device structure ++* @state: PM message ++* ++* This function is called by OS when a power event occurs ++*/ ++int intel_cln_dma_suspend(struct middma_device *device) ++{ ++ int i = 0; ++ pr_debug("MDMA: dma_suspend called\n"); ++ ++ for (i = 0; i < device->max_chan; i++) { ++ if (device->ch[i].in_use) ++ return -EAGAIN; ++ } ++ dmac1_mask_periphral_intr(device); ++ device->state = SUSPENDED; ++ return 0; ++} ++EXPORT_SYMBOL(intel_cln_dma_suspend); ++ ++/** ++* dma_resume - PCI resume function ++* ++* @pci: PCI device structure ++* ++* This function is called by OS when a power event occurs ++*/ ++int intel_cln_dma_resume(struct middma_device *device) ++{ ++ pr_debug("MDMA: dma_resume called\n"); ++ device->state = RUNNING; ++ iowrite32(REG_BIT0, device->dma_base + DMA_CFG); ++ return 0; ++} ++EXPORT_SYMBOL(intel_cln_dma_resume); ++ ++static int intel_cln_dma_runtime_suspend(struct middma_device *device) ++{ ++ device->state = SUSPENDED; ++ return 0; ++} ++EXPORT_SYMBOL(intel_cln_dma_runtime_suspend); ++ ++static int intel_cln_dma_runtime_resume(struct middma_device *device) ++{ ++ device->state = RUNNING; ++ iowrite32(REG_BIT0, device->dma_base + DMA_CFG); ++ return 0; ++} ++EXPORT_SYMBOL(intel_cln_dma_runtime_resume); ++ ++ +diff --git a/drivers/dma/intel_mid_dma/intel_mid_dma_pci.c b/drivers/dma/intel_mid_dma/intel_mid_dma_pci.c +new file mode 100644 +index 0000000..be7705b +--- /dev/null ++++ b/drivers/dma/intel_mid_dma/intel_mid_dma_pci.c +@@ -0,0 +1,287 @@ ++/* ++ * intel_mid_dma.c - Intel Langwell DMA Drivers ++ * ++ * Copyright (C) 2008-12 Intel Corp ++ * Author: Vinod Koul <vinod.koul@intel.com> ++ * The driver design is based on dw_dmac driver ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * ++ */ ++#include <linux/pci.h> ++#include <linux/interrupt.h> ++#include <linux/pm_runtime.h> ++#include <linux/intel_mid_dma.h> ++#include <linux/module.h> ++ ++#include "intel_mid_dma_regs.h" ++#include "intel_mid_dma_core.h" ++ ++#define INTEL_MID_DMAC1_ID 0x0814 ++#define INTEL_MID_DMAC2_ID 0x0813 ++#define INTEL_MID_GP_DMAC2_ID 0x0827 ++#define INTEL_MFLD_DMAC1_ID 0x0830 ++ ++#define INFO(_max_chan, _ch_base, _block_size, _pimr_mask) \ ++ ((kernel_ulong_t)&(struct intel_mid_dma_probe_info) { \ ++ .max_chan = (_max_chan), \ ++ .ch_base = (_ch_base), \ ++ .block_size = (_block_size), \ ++ .pimr_mask = (_pimr_mask), \ ++ }) ++ ++/** ++ * intel_mid_dma_probe - PCI Probe ++ * @pdev: Controller PCI device structure ++ * @id: pci device id structure ++ * ++ * Initialize the PCI device, map BARs, query driver data. ++ * Call setup_dma to complete contoller and chan initilzation ++ */ ++static int intel_mid_dma_probe(struct pci_dev *pdev, ++ const struct pci_device_id *id) ++{ ++ struct middma_device *device; ++ u32 base_addr, bar_size; ++ struct intel_mid_dma_probe_info *info; ++ int err; ++ ++ pr_debug("MDMA: probe for %x\n", pdev->device); ++ info = (void *)id->driver_data; ++ pr_debug("MDMA: CH %d, base %d, block len %d, Periphral mask %x\n", ++ info->max_chan, info->ch_base, ++ info->block_size, info->pimr_mask); ++ ++ err = pci_enable_device(pdev); ++ if (err) ++ goto err_enable_device; ++ ++ err = pci_request_regions(pdev, "intel_mid_dmac"); ++ if (err) ++ goto err_request_regions; ++ ++ err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); ++ if (err) ++ goto err_set_dma_mask; ++ ++ err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); ++ if (err) ++ goto err_set_dma_mask; ++ ++ device = kzalloc(sizeof(*device), GFP_KERNEL); ++ if (!device) { ++ pr_err("ERR_MDMA:kzalloc failed probe\n"); ++ err = -ENOMEM; ++ goto err_kzalloc; ++ } ++ device->pdev = pci_dev_get(pdev); ++ ++ base_addr = pci_resource_start(pdev, 0); ++ bar_size = pci_resource_len(pdev, 0); ++ device->dma_base = ioremap_nocache(base_addr, DMA_REG_SIZE); ++ if (!device->dma_base) { ++ pr_err("ERR_MDMA:ioremap failed\n"); ++ err = -ENOMEM; ++ goto err_ioremap; ++ } ++ pci_set_drvdata(pdev, device); ++ pci_set_master(pdev); ++ device->max_chan = info->max_chan; ++ device->chan_base = info->ch_base; ++ device->block_size = info->block_size; ++ device->pimr_mask = info->pimr_mask; ++ ++ err = mid_setup_dma(pdev); ++ if (err) ++ goto err_dma; ++ ++ pm_runtime_put_noidle(&pdev->dev); ++ pm_runtime_allow(&pdev->dev); ++ return 0; ++ ++err_dma: ++ iounmap(device->dma_base); ++err_ioremap: ++ pci_dev_put(pdev); ++ kfree(device); ++err_kzalloc: ++err_set_dma_mask: ++ pci_release_regions(pdev); ++ pci_disable_device(pdev); ++err_request_regions: ++err_enable_device: ++ pr_err("ERR_MDMA:Probe failed %d\n", err); ++ return err; ++} ++ ++/** ++ * intel_mid_dma_remove - PCI remove ++ * @pdev: Controller PCI device structure ++ * ++ * Free up all resources and data ++ * Call shutdown_dma to complete contoller and chan cleanup ++ */ ++static void intel_mid_dma_remove(struct pci_dev *pdev) ++{ ++ struct middma_device *device = pci_get_drvdata(pdev); ++ ++ pm_runtime_get_noresume(&pdev->dev); ++ pm_runtime_forbid(&pdev->dev); ++ middma_shutdown(pdev); ++ pci_dev_put(pdev); ++ kfree(device); ++ pci_release_regions(pdev); ++ pci_disable_device(pdev); ++} ++ ++/* Power Management */ ++/* ++* dma_suspend - PCI suspend function ++* ++* @pci: PCI device structure ++* @state: PM message ++* ++* This function is called by OS when a power event occurs ++*/ ++static int dma_suspend(struct device *dev) ++{ ++ struct pci_dev *pci = to_pci_dev(dev); ++ int i; ++ struct middma_device *device = pci_get_drvdata(pci); ++ pr_debug("MDMA: dma_suspend called\n"); ++ ++ for (i = 0; i < device->max_chan; i++) { ++ if (device->ch[i].in_use) ++ return -EAGAIN; ++ } ++ dmac1_mask_periphral_intr(device); ++ device->state = SUSPENDED; ++ pci_save_state(pci); ++ pci_disable_device(pci); ++ pci_set_power_state(pci, PCI_D3hot); ++ return 0; ++} ++ ++/** ++* dma_resume - PCI resume function ++* ++* @pci: PCI device structure ++* ++* This function is called by OS when a power event occurs ++*/ ++int dma_resume(struct device *dev) ++{ ++ struct pci_dev *pci = to_pci_dev(dev); ++ int ret; ++ struct middma_device *device = pci_get_drvdata(pci); ++ ++ pr_debug("MDMA: dma_resume called\n"); ++ pci_set_power_state(pci, PCI_D0); ++ pci_restore_state(pci); ++ ret = pci_enable_device(pci); ++ if (ret) { ++ pr_err("MDMA: device can't be enabled for %x\n", pci->device); ++ return ret; ++ } ++ intel_mid_dma_resume(device); ++ return 0; ++} ++ ++static int dma_runtime_suspend(struct device *dev) ++{ ++ struct pci_dev *pci_dev = to_pci_dev(dev); ++ struct middma_device *device = pci_get_drvdata(pci_dev); ++ ++ device->state = SUSPENDED; ++ return 0; ++} ++ ++static int dma_runtime_resume(struct device *dev) ++{ ++ struct pci_dev *pci_dev = to_pci_dev(dev); ++ struct middma_device *device = pci_get_drvdata(pci_dev); ++ ++ device->state = RUNNING; ++ iowrite32(REG_BIT0, device->dma_base + DMA_CFG); ++ return 0; ++} ++ ++static int dma_runtime_idle(struct device *dev) ++{ ++ struct pci_dev *pdev = to_pci_dev(dev); ++ struct middma_device *device = pci_get_drvdata(pdev); ++ int i; ++ ++ for (i = 0; i < device->max_chan; i++) { ++ if (device->ch[i].in_use) ++ return -EAGAIN; ++ } ++ ++ return pm_schedule_suspend(dev, 0); ++} ++ ++/****************************************************************************** ++* PCI stuff ++*/ ++static struct pci_device_id intel_mid_dma_ids[] = { ++ { PCI_VDEVICE(INTEL, INTEL_MID_DMAC1_ID), INFO(2, 6, 4095, 0x200020)}, ++ { PCI_VDEVICE(INTEL, INTEL_MID_DMAC2_ID), INFO(2, 0, 2047, 0)}, ++ { PCI_VDEVICE(INTEL, INTEL_MID_GP_DMAC2_ID), INFO(2, 0, 2047, 0)}, ++ { PCI_VDEVICE(INTEL, INTEL_MFLD_DMAC1_ID), INFO(4, 0, 4095, 0x400040)}, ++ { 0, } ++}; ++MODULE_DEVICE_TABLE(pci, intel_mid_dma_ids); ++ ++static const struct dev_pm_ops intel_mid_dma_pm = { ++ .runtime_suspend = dma_runtime_suspend, ++ .runtime_resume = dma_runtime_resume, ++ .runtime_idle = dma_runtime_idle, ++ .suspend = dma_suspend, ++ .resume = dma_resume, ++}; ++ ++static struct pci_driver intel_mid_dma_pci_driver = { ++ .name = "Intel MID DMA", ++ .id_table = intel_mid_dma_ids, ++ .probe = intel_mid_dma_probe, ++ .remove = __devexit_p(intel_mid_dma_remove), ++#ifdef CONFIG_PM ++ .driver = { ++ .pm = &intel_mid_dma_pm, ++ }, ++#endif ++}; ++ ++static int __init intel_mid_dma_init(void) ++{ ++ pr_debug("INFO_MDMA: LNW DMA Driver Version %s\n", ++ INTEL_MID_DMA_DRIVER_VERSION); ++ return pci_register_driver(&intel_mid_dma_pci_driver); ++} ++fs_initcall(intel_mid_dma_init); ++ ++static void __exit intel_mid_dma_exit(void) ++{ ++ pci_unregister_driver(&intel_mid_dma_pci_driver); ++} ++module_exit(intel_mid_dma_exit); ++ ++MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>"); ++MODULE_DESCRIPTION("Intel (R) MID DMAC Driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_VERSION(INTEL_MID_DMA_DRIVER_VERSION); +diff --git a/drivers/dma/intel_mid_dma_regs.h b/drivers/dma/intel_mid_dma_regs.h +index 17b4219..4be7a01 100644 +--- a/drivers/dma/intel_mid_dma_regs.h ++++ b/drivers/dma/intel_mid_dma_regs.h +@@ -27,6 +27,7 @@ + + #include <linux/dmaengine.h> + #include <linux/dmapool.h> ++#include <linux/intel_mid_dma.h> + #include <linux/pci_ids.h> + + #define INTEL_MID_DMA_DRIVER_VERSION "1.1.0" +@@ -158,115 +159,17 @@ union intel_mid_dma_cfg_hi { + }; + + +-/** +- * struct intel_mid_dma_chan - internal mid representation of a DMA channel +- * @chan: dma_chan strcture represetation for mid chan +- * @ch_regs: MMIO register space pointer to channel register +- * @dma_base: MMIO register space DMA engine base pointer +- * @ch_id: DMA channel id +- * @lock: channel spinlock +- * @active_list: current active descriptors +- * @queue: current queued up descriptors +- * @free_list: current free descriptors +- * @slave: dma slave structure +- * @descs_allocated: total number of descriptors allocated +- * @dma: dma device structure pointer +- * @busy: bool representing if ch is busy (active txn) or not +- * @in_use: bool representing if ch is in use or not +- * @raw_tfr: raw trf interrupt received +- * @raw_block: raw block interrupt received +- */ +-struct intel_mid_dma_chan { +- struct dma_chan chan; +- void __iomem *ch_regs; +- void __iomem *dma_base; +- int ch_id; +- spinlock_t lock; +- struct list_head active_list; +- struct list_head queue; +- struct list_head free_list; +- unsigned int descs_allocated; +- struct middma_device *dma; +- bool busy; +- bool in_use; +- u32 raw_tfr; +- u32 raw_block; +- struct intel_mid_dma_slave *mid_slave; +-}; +- + static inline struct intel_mid_dma_chan *to_intel_mid_dma_chan( + struct dma_chan *chan) + { + return container_of(chan, struct intel_mid_dma_chan, chan); + } + +-enum intel_mid_dma_state { +- RUNNING = 0, +- SUSPENDED, +-}; +-/** +- * struct middma_device - internal representation of a DMA device +- * @pdev: PCI device +- * @dma_base: MMIO register space pointer of DMA +- * @dma_pool: for allocating DMA descriptors +- * @common: embedded struct dma_device +- * @tasklet: dma tasklet for processing interrupts +- * @ch: per channel data +- * @pci_id: DMA device PCI ID +- * @intr_mask: Interrupt mask to be used +- * @mask_reg: MMIO register for periphral mask +- * @chan_base: Base ch index (read from driver data) +- * @max_chan: max number of chs supported (from drv_data) +- * @block_size: Block size of DMA transfer supported (from drv_data) +- * @pimr_mask: MMIO register addr for periphral interrupt (from drv_data) +- * @state: dma PM device state +- */ +-struct middma_device { +- struct pci_dev *pdev; +- void __iomem *dma_base; +- struct pci_pool *dma_pool; +- struct dma_device common; +- struct tasklet_struct tasklet; +- struct intel_mid_dma_chan ch[MAX_CHAN]; +- unsigned int pci_id; +- unsigned int intr_mask; +- void __iomem *mask_reg; +- int chan_base; +- int max_chan; +- int block_size; +- unsigned int pimr_mask; +- enum intel_mid_dma_state state; +-}; +- + static inline struct middma_device *to_middma_device(struct dma_device *common) + { + return container_of(common, struct middma_device, common); + } + +-struct intel_mid_dma_desc { +- void __iomem *block; /*ch ptr*/ +- struct list_head desc_node; +- struct dma_async_tx_descriptor txd; +- size_t len; +- dma_addr_t sar; +- dma_addr_t dar; +- u32 cfg_hi; +- u32 cfg_lo; +- u32 ctl_lo; +- u32 ctl_hi; +- struct pci_pool *lli_pool; +- struct intel_mid_dma_lli *lli; +- dma_addr_t lli_phys; +- unsigned int lli_length; +- unsigned int current_lli; +- dma_addr_t next; +- enum dma_transfer_direction dirn; +- enum dma_status status; +- enum dma_slave_buswidth width; /*width of DMA txn*/ +- enum intel_mid_dma_mode cfg_mode; /*mode configuration*/ +- +-}; +- + struct intel_mid_dma_lli { + dma_addr_t sar; + dma_addr_t dar; +diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig +index 682de75..97e951b 100644 +--- a/drivers/gpio/Kconfig ++++ b/drivers/gpio/Kconfig +@@ -243,13 +243,14 @@ config GPIO_VR41XX + Say yes here to support the NEC VR4100 series General-purpose I/O Uint + + config GPIO_SCH +- tristate "Intel SCH/TunnelCreek/Centerton GPIO" ++ tristate "Intel SCH/TunnelCreek/Centerton/Clanton GPIO" + depends on PCI && X86 + select MFD_CORE + select LPC_SCH + help + Say yes here to support GPIO interface on Intel Poulsbo SCH, +- Intel Tunnel Creek processor or Intel Centerton processor. ++ Intel Tunnel Creek processor, Intel Centerton processor or Intel ++ Clanton. + The Intel SCH contains a total of 14 GPIO pins. Ten GPIOs are + powered by the core power rail and are turned off during sleep + modes (S3 and higher). The remaining four GPIOs are powered by +@@ -261,6 +262,8 @@ config GPIO_SCH + The Intel Centerton processor has a total of 30 GPIO pins. + Twenty-one are powered by the core power rail and 9 from the + suspend power supply. ++ The Intel Clanton has 2 GPIOs powered by the core power well and 6 ++ form the suspend power well. + + config GPIO_ICH + tristate "Intel ICH GPIO" +diff --git a/drivers/gpio/gpio-sch.c b/drivers/gpio/gpio-sch.c +index edae963..73591be 100644 +--- a/drivers/gpio/gpio-sch.c ++++ b/drivers/gpio/gpio-sch.c +@@ -41,6 +41,13 @@ static DEFINE_SPINLOCK(gpio_lock); + + static unsigned short gpio_ba; + ++static void cln_gpio_restrict_release(struct device *dev) {} ++static struct platform_device cln_gpio_restrict_pdev = ++{ ++ .name = "cln-gpio-restrict-nc", ++ .dev.release = cln_gpio_restrict_release, ++}; ++ + static int sch_gpio_core_direction_in(struct gpio_chip *gc, unsigned gpio_num) + { + u8 curr_dirs; +@@ -240,6 +247,14 @@ static int sch_gpio_probe(struct platform_device *pdev) + sch_gpio_resume.ngpio = 9; + break; + ++ case PCI_DEVICE_ID_INTEL_CLANTON_ILB: ++ sch_gpio_core.base = 0; ++ sch_gpio_core.ngpio = 2; ++ ++ sch_gpio_resume.base = 2; ++ sch_gpio_resume.ngpio = 6; ++ break; ++ + default: + err = -ENODEV; + goto err_sch_gpio_core; +@@ -256,6 +271,10 @@ static int sch_gpio_probe(struct platform_device *pdev) + if (err < 0) + goto err_sch_gpio_resume; + ++ err = platform_device_register(&cln_gpio_restrict_pdev); ++ if (err < 0) ++ goto err_sch_gpio_resume; ++ + return 0; + + err_sch_gpio_resume: +@@ -277,6 +296,8 @@ static int sch_gpio_remove(struct platform_device *pdev) + if (gpio_ba) { + int err; + ++ platform_device_unregister(&cln_gpio_restrict_pdev); ++ + err = gpiochip_remove(&sch_gpio_core); + if (err) + dev_err(&pdev->dev, "%s failed, %d\n", +diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c +index 199fca1..5e897ff 100644 +--- a/drivers/gpio/gpiolib.c ++++ b/drivers/gpio/gpiolib.c +@@ -60,11 +60,17 @@ struct gpio_desc { + #define FLAG_ACTIVE_LOW 7 /* sysfs value has active low */ + #define FLAG_OPEN_DRAIN 8 /* Gpio is open drain type */ + #define FLAG_OPEN_SOURCE 9 /* Gpio is open source type */ ++#define FLAG_PULLUP 10 /* Gpio drive is resistive pullup */ ++#define FLAG_PULLDOWN 11 /* Gpio drive is resistive pulldown */ ++#define FLAG_STRONG 12 /* Gpio drive is strong (fast output) */ ++#define FLAG_HIZ 13 /* Gpio drive is Hi-Z (input) */ + + #define ID_SHIFT 16 /* add new flags before this one */ + + #define GPIO_FLAGS_MASK ((1 << ID_SHIFT) - 1) + #define GPIO_TRIGGER_MASK (BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE)) ++#define GPIO_DRIVE_MASK (BIT(FLAG_PULLUP) | BIT(FLAG_PULLDOWN) \ ++ | BIT(FLAG_STRONG) | BIT(FLAG_HIZ)) + + #ifdef CONFIG_DEBUG_FS + const char *label; +@@ -243,6 +249,10 @@ static DEFINE_MUTEX(sysfs_lock); + * * is read/write as zero/nonzero + * * also affects existing and subsequent "falling" and "rising" + * /edge configuration ++ * /drive ++ * * sets signal drive mode ++ * * is read/write as "pullup", "pulldown", "strong" or "hiz" ++ * + */ + + static ssize_t gpio_direction_show(struct device *dev, +@@ -573,9 +583,85 @@ static ssize_t gpio_active_low_store(struct device *dev, + static const DEVICE_ATTR(active_low, 0644, + gpio_active_low_show, gpio_active_low_store); + ++static ssize_t gpio_drive_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ const struct gpio_desc *desc = dev_get_drvdata(dev); ++ ssize_t status; ++ ++ mutex_lock(&sysfs_lock); ++ ++ if (!test_bit(FLAG_EXPORT, &desc->flags)) { ++ status = -EIO; ++ } else { ++ if (test_bit(FLAG_PULLUP, &desc->flags)) ++ status = sprintf(buf, "pullup\n"); ++ else if (test_bit(FLAG_PULLDOWN, &desc->flags)) ++ status = sprintf(buf, "pulldown\n"); ++ else if (test_bit(FLAG_STRONG, &desc->flags)) ++ status = sprintf(buf, "strong\n"); ++ else if (test_bit(FLAG_HIZ, &desc->flags)) ++ status = sprintf(buf, "hiz\n"); ++ else ++ status = -EINVAL; ++ } ++ ++ mutex_unlock(&sysfs_lock); ++ return status; ++} ++ ++static ssize_t gpio_drive_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t size) ++{ ++ struct gpio_desc *desc = dev_get_drvdata(dev); ++ unsigned gpio = desc - gpio_desc; ++ ssize_t status; ++ ++ mutex_lock(&sysfs_lock); ++ ++ if (!test_bit(FLAG_EXPORT, &desc->flags)) ++ status = -EIO; ++ else { ++ if (sysfs_streq(buf, "pullup")) { ++ status = gpio_set_drive(gpio, GPIOF_DRIVE_PULLUP); ++ if (!status) { ++ desc->flags &= ~GPIO_DRIVE_MASK; ++ set_bit(FLAG_PULLUP, &desc->flags); ++ } ++ } else if (sysfs_streq(buf, "pulldown")) { ++ status = gpio_set_drive(gpio, GPIOF_DRIVE_PULLDOWN); ++ if (!status) { ++ desc->flags &= ~GPIO_DRIVE_MASK; ++ set_bit(FLAG_PULLDOWN, &desc->flags); ++ } ++ } else if (sysfs_streq(buf, "strong")) { ++ status = gpio_set_drive(gpio, GPIOF_DRIVE_STRONG); ++ if (!status) { ++ desc->flags &= ~GPIO_DRIVE_MASK; ++ set_bit(FLAG_STRONG, &desc->flags); ++ } ++ } else if (sysfs_streq(buf, "hiz")) { ++ status = gpio_set_drive(gpio, GPIOF_DRIVE_HIZ); ++ if (!status) { ++ desc->flags &= ~GPIO_DRIVE_MASK; ++ set_bit(FLAG_HIZ, &desc->flags); ++ } ++ } else { ++ status = -EINVAL; ++ } ++ } ++ ++ mutex_unlock(&sysfs_lock); ++ return status ? : size; ++} ++ ++static const DEVICE_ATTR(drive, 0644, ++ gpio_drive_show, gpio_drive_store); ++ + static const struct attribute *gpio_attrs[] = { + &dev_attr_value.attr, + &dev_attr_active_low.attr, ++ &dev_attr_drive.attr, + NULL, + }; + +@@ -806,7 +892,7 @@ fail_unlock: + } + EXPORT_SYMBOL_GPL(gpio_export); + +-static int match_export(struct device *dev, void *data) ++static int match_export(struct device *dev, const void *data) + { + return dev_get_drvdata(dev) == data; + } +@@ -1677,6 +1763,50 @@ fail: + } + EXPORT_SYMBOL_GPL(gpio_set_debounce); + ++/** ++ * gpio_set_drive - sets drive @mode for a @gpio ++ * @gpio: the gpio to set the drive mode ++ * @mode: the drive mode ++ */ ++int gpio_set_drive(unsigned gpio, unsigned mode) ++{ ++ unsigned long flags; ++ struct gpio_chip *chip; ++ struct gpio_desc *desc = &gpio_desc[gpio]; ++ int status = -EINVAL; ++ ++ spin_lock_irqsave(&gpio_lock, flags); ++ ++ if (!gpio_is_valid(gpio)) ++ goto fail; ++ chip = desc->chip; ++ if (!chip || !chip->set || !chip->set_drive) ++ goto fail; ++ gpio -= chip->base; ++ if (gpio >= chip->ngpio) ++ goto fail; ++ status = gpio_ensure_requested(desc, gpio); ++ if (status < 0) ++ goto fail; ++ ++ /* now we know the gpio is valid and chip won't vanish */ ++ ++ spin_unlock_irqrestore(&gpio_lock, flags); ++ ++ might_sleep_if(chip->can_sleep); ++ ++ return chip->set_drive(chip, gpio, mode); ++ ++fail: ++ spin_unlock_irqrestore(&gpio_lock, flags); ++ if (status) ++ pr_debug("%s: gpio-%d status %d\n", ++ __func__, gpio, status); ++ ++ return status; ++} ++EXPORT_SYMBOL_GPL(gpio_set_drive); ++ + /* I/O calls are only valid after configuration completed; the relevant + * "is this a valid GPIO" error checks should already have been done. + * +diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig +index bdca511..010e4fd 100644 +--- a/drivers/i2c/busses/Kconfig ++++ b/drivers/i2c/busses/Kconfig +@@ -22,7 +22,7 @@ config I2C_ALI1535 + + config I2C_ALI1563 + tristate "ALI 1563" +- depends on PCI && EXPERIMENTAL ++ depends on PCI + help + If you say yes to this option, support will be included for the SMB + Host controller on Acer Labs Inc. (ALI) M1563 South Bridges. The SMB +@@ -56,7 +56,7 @@ config I2C_AMD756 + + config I2C_AMD756_S4882 + tristate "SMBus multiplexing on the Tyan S4882" +- depends on I2C_AMD756 && X86 && EXPERIMENTAL ++ depends on I2C_AMD756 && X86 + help + Enabling this option will add specific SMBus support for the Tyan + S4882 motherboard. On this 4-CPU board, the SMBus is multiplexed +@@ -164,7 +164,7 @@ config I2C_NFORCE2 + + config I2C_NFORCE2_S4985 + tristate "SMBus multiplexing on the Tyan S4985" +- depends on I2C_NFORCE2 && X86 && EXPERIMENTAL ++ depends on I2C_NFORCE2 && X86 + help + Enabling this option will add specific SMBus support for the Tyan + S4985 motherboard. On this 4-CPU board, the SMBus is multiplexed +@@ -215,7 +215,7 @@ config I2C_SIS96X + + config I2C_VIA + tristate "VIA VT82C586B" +- depends on PCI && EXPERIMENTAL ++ depends on PCI + select I2C_ALGOBIT + help + If you say yes to this option, support will be included for the VIA +@@ -267,7 +267,7 @@ comment "Mac SMBus host controller drivers" + + config I2C_HYDRA + tristate "CHRP Apple Hydra Mac I/O I2C interface" +- depends on PCI && PPC_CHRP && EXPERIMENTAL ++ depends on PCI && PPC_CHRP + select I2C_ALGOBIT + help + This supports the use of the I2C interface in the Apple Hydra Mac +@@ -293,7 +293,7 @@ comment "I2C system bus drivers (mostly embedded / system-on-chip)" + + config I2C_AT91 + tristate "Atmel AT91 I2C Two-Wire interface (TWI)" +- depends on ARCH_AT91 && EXPERIMENTAL ++ depends on ARCH_AT91 + help + This supports the use of the I2C interface on Atmel AT91 + processors. +@@ -386,7 +386,7 @@ config I2C_DESIGNWARE_PLATFORM + + config I2C_DESIGNWARE_PCI + tristate "Synopsys DesignWare PCI" +- depends on PCI ++ depends on PCI && !INTEL_QUARK_X1000_SOC + select I2C_DESIGNWARE_CORE + help + If you say yes to this option, support will be included for the +@@ -519,7 +519,6 @@ config I2C_NUC900 + + config I2C_OCORES + tristate "OpenCores I2C Controller" +- depends on EXPERIMENTAL + help + If you say yes to this option, support will be included for the + OpenCores I2C controller. For details see +@@ -712,7 +711,7 @@ config I2C_OCTEON + + config I2C_XILINX + tristate "Xilinx I2C Controller" +- depends on EXPERIMENTAL && HAS_IOMEM ++ depends on HAS_IOMEM + help + If you say yes to this option, support will be included for the + Xilinx I2C controller. +@@ -803,7 +802,7 @@ config I2C_PARPORT_LIGHT + + config I2C_TAOS_EVM + tristate "TAOS evaluation module" +- depends on EXPERIMENTAL ++ depends on TTY + select SERIO + select SERIO_SERPORT + default n +diff --git a/drivers/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-core.c +index f5258c2..2a2d1c9 100644 +--- a/drivers/i2c/busses/i2c-designware-core.c ++++ b/drivers/i2c/busses/i2c-designware-core.c +@@ -164,6 +164,29 @@ static char *abort_sources[] = { + "lost arbitration", + }; + ++/* ++ * Bitmask for struct i2c_dw_data_cmd's `cmd' field. ++ * - DW_IC_CMD_READ: read/~write operation ++ * - DW_IC_CMD_STOP: stop condition generation (only for devices requiring ++ * explicit transaction termination) ++ * - DW_IC_CMD_RESTART: (re)start condition generation (only for devices ++ * requiring explicit transaction termination) ++ */ ++#define DW_IC_CMD_READ 0x01 ++#define DW_IC_CMD_STOP 0x02 ++#define DW_IC_CMD_RESTART 0x04 ++ ++/* ++ * Define the IC_DATA_CMD format. ++ */ ++static union i2c_dw_data_cmd { ++ struct fields { ++ u8 data; ++ u8 cmd; ++ } fields; ++ u16 value; ++} data_cmd; ++ + u32 dw_readl(struct dw_i2c_dev *dev, int offset) + { + u32 value; +@@ -344,6 +367,9 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev) + struct i2c_msg *msgs = dev->msgs; + u32 ic_con; + ++ /* Disable interrupts */ ++ i2c_dw_disable_int(dev); ++ + /* Disable the adapter */ + dw_writel(dev, 0, DW_IC_ENABLE); + +@@ -380,6 +406,7 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev) + u32 addr = msgs[dev->msg_write_idx].addr; + u32 buf_len = dev->tx_buf_len; + u8 *buf = dev->tx_buf; ++ int segment_start = 0; + + intr_mask = DW_IC_INTR_DEFAULT_MASK; + +@@ -403,21 +430,65 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev) + break; + } + ++ segment_start = 0; + if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) { + /* new i2c_msg */ + buf = msgs[dev->msg_write_idx].buf; + buf_len = msgs[dev->msg_write_idx].len; ++ segment_start = 1; + } + + tx_limit = dev->tx_fifo_depth - dw_readl(dev, DW_IC_TXFLR); + rx_limit = dev->rx_fifo_depth - dw_readl(dev, DW_IC_RXFLR); + ++ /* ++ * The maximum number of read requests that can be put into TX ++ * FIFO depends on the number read operations already pending ++ * in RX FIFO + the number of outstanding read operations still ++ * queued in the TX FIFO. ++ * This prevents RX FIFO overrun. ++ */ ++ rx_limit -= dev->rx_outstanding; ++ + while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) { ++ data_cmd.fields.data = 0x00; ++ data_cmd.fields.cmd = 0x00; ++ + if (msgs[dev->msg_write_idx].flags & I2C_M_RD) { +- dw_writel(dev, 0x100, DW_IC_DATA_CMD); ++ /* Master-receiver */ ++ data_cmd.fields.cmd = DW_IC_CMD_READ; + rx_limit--; +- } else +- dw_writel(dev, *buf++, DW_IC_DATA_CMD); ++ dev->rx_outstanding++; ++ } else { ++ /* Master-transmitter */ ++ data_cmd.fields.data = *buf; ++ buf++; ++ } ++ ++ if (1 == dev->explicit_stop ++ && 1 == segment_start) { ++ /* ++ * First byte of a transaction segment for a ++ * device requiring explicit transaction ++ * termination: generate (re)start symbol. ++ */ ++ segment_start = 0; ++ data_cmd.fields.cmd |= DW_IC_CMD_RESTART; ++ } ++ ++ if (1 == dev->explicit_stop ++ && dev->msg_write_idx == dev->msgs_num - 1 ++ && 1 == buf_len) { ++ /* ++ * Last byte of last transction segment for a ++ * device requiring explicit transaction ++ * termination: generate stop symbol. ++ */ ++ data_cmd.fields.cmd |= DW_IC_CMD_STOP; ++ } ++ ++ dw_writel(dev, data_cmd.value, DW_IC_DATA_CMD); ++ + tx_limit--; buf_len--; + } + +@@ -468,8 +539,10 @@ i2c_dw_read(struct dw_i2c_dev *dev) + + rx_valid = dw_readl(dev, DW_IC_RXFLR); + +- for (; len > 0 && rx_valid > 0; len--, rx_valid--) ++ for (; len > 0 && rx_valid > 0; len--, rx_valid--) { + *buf++ = dw_readl(dev, DW_IC_DATA_CMD); ++ dev->rx_outstanding--; ++ } + + if (len > 0) { + dev->status |= STATUS_READ_IN_PROGRESS; +@@ -527,6 +600,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) + dev->msg_err = 0; + dev->status = STATUS_IDLE; + dev->abort_source = 0; ++ dev->rx_outstanding = 0; + + ret = i2c_dw_wait_bus_not_busy(dev); + if (ret < 0) +@@ -625,8 +699,6 @@ static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev) + dw_readl(dev, DW_IC_CLR_RX_DONE); + if (stat & DW_IC_INTR_ACTIVITY) + dw_readl(dev, DW_IC_CLR_ACTIVITY); +- if (stat & DW_IC_INTR_STOP_DET) +- dw_readl(dev, DW_IC_CLR_STOP_DET); + if (stat & DW_IC_INTR_START_DET) + dw_readl(dev, DW_IC_CLR_START_DET); + if (stat & DW_IC_INTR_GEN_CALL) +@@ -677,8 +749,21 @@ irqreturn_t i2c_dw_isr(int this_irq, void *dev_id) + * the current transmit status. + */ + ++ /* ++ * Process stop condition after the last transaction segment is ++ * transmitted (and received if appropriate). ++ */ ++ if (dev->msgs_num == dev->msg_write_idx ++ && (DW_IC_INTR_STOP_DET & dw_readl(dev, DW_IC_INTR_STAT)) ++ && 0 == dw_readl(dev, DW_IC_TXFLR) ++ && 0 == dw_readl(dev, DW_IC_RXFLR) ++ && 0 == dev->rx_outstanding) { ++ dw_readl(dev, DW_IC_CLR_STOP_DET); ++ complete(&dev->cmd_complete); ++ } ++ + tx_aborted: +- if ((stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET)) || dev->msg_err) ++ if ((stat & (DW_IC_INTR_TX_ABRT)) || dev->msg_err) + complete(&dev->cmd_complete); + + return IRQ_HANDLED; +diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h +index 9c1840e..691aff8 100644 +--- a/drivers/i2c/busses/i2c-designware-core.h ++++ b/drivers/i2c/busses/i2c-designware-core.h +@@ -34,6 +34,14 @@ + #define DW_IC_CON_RESTART_EN 0x20 + #define DW_IC_CON_SLAVE_DISABLE 0x40 + ++struct dw_pci_controller { ++ u32 bus_num; ++ u32 bus_cfg; ++ u32 tx_fifo_depth; ++ u32 rx_fifo_depth; ++ u32 clk_khz; ++ u8 explicit_stop; ++}; + + /** + * struct dw_i2c_dev - private i2c-designware data +@@ -60,6 +68,8 @@ + * @adapter: i2c subsystem adapter node + * @tx_fifo_depth: depth of the hardware tx fifo + * @rx_fifo_depth: depth of the hardware rx fifo ++ * @rx_outstanding: current outstanding master-receiver bytes in TX FIFO ++ * @explicit_stop: set to 1 if hardware requires explicit stop bit transmission + */ + struct dw_i2c_dev { + struct device *dev; +@@ -88,6 +98,8 @@ struct dw_i2c_dev { + u32 master_cfg; + unsigned int tx_fifo_depth; + unsigned int rx_fifo_depth; ++ int rx_outstanding; ++ u8 explicit_stop; + }; + + #define ACCESS_SWAP 0x00000001 +diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c +index 6add851..62ad7dc 100644 +--- a/drivers/i2c/busses/i2c-designware-pcidrv.c ++++ b/drivers/i2c/busses/i2c-designware-pcidrv.c +@@ -56,14 +56,6 @@ enum dw_pci_ctl_id_t { + medfield_5, + }; + +-struct dw_pci_controller { +- u32 bus_num; +- u32 bus_cfg; +- u32 tx_fifo_depth; +- u32 rx_fifo_depth; +- u32 clk_khz; +-}; +- + #define INTEL_MID_STD_CFG (DW_IC_CON_MASTER | \ + DW_IC_CON_SLAVE_DISABLE | \ + DW_IC_CON_RESTART_EN) +@@ -75,6 +67,7 @@ static struct dw_pci_controller dw_pci_controllers[] = { + .tx_fifo_depth = 32, + .rx_fifo_depth = 32, + .clk_khz = 25000, ++ .explicit_stop = 0, + }, + [moorestown_1] = { + .bus_num = 1, +@@ -82,6 +75,7 @@ static struct dw_pci_controller dw_pci_controllers[] = { + .tx_fifo_depth = 32, + .rx_fifo_depth = 32, + .clk_khz = 25000, ++ .explicit_stop = 0, + }, + [moorestown_2] = { + .bus_num = 2, +@@ -89,6 +83,7 @@ static struct dw_pci_controller dw_pci_controllers[] = { + .tx_fifo_depth = 32, + .rx_fifo_depth = 32, + .clk_khz = 25000, ++ .explicit_stop = 0, + }, + [medfield_0] = { + .bus_num = 0, +@@ -96,6 +91,7 @@ static struct dw_pci_controller dw_pci_controllers[] = { + .tx_fifo_depth = 32, + .rx_fifo_depth = 32, + .clk_khz = 25000, ++ .explicit_stop = 0, + }, + [medfield_1] = { + .bus_num = 1, +@@ -103,6 +99,7 @@ static struct dw_pci_controller dw_pci_controllers[] = { + .tx_fifo_depth = 32, + .rx_fifo_depth = 32, + .clk_khz = 25000, ++ .explicit_stop = 0, + }, + [medfield_2] = { + .bus_num = 2, +@@ -110,6 +107,7 @@ static struct dw_pci_controller dw_pci_controllers[] = { + .tx_fifo_depth = 32, + .rx_fifo_depth = 32, + .clk_khz = 25000, ++ .explicit_stop = 0, + }, + [medfield_3] = { + .bus_num = 3, +@@ -117,6 +115,7 @@ static struct dw_pci_controller dw_pci_controllers[] = { + .tx_fifo_depth = 32, + .rx_fifo_depth = 32, + .clk_khz = 25000, ++ .explicit_stop = 0, + }, + [medfield_4] = { + .bus_num = 4, +@@ -124,6 +123,7 @@ static struct dw_pci_controller dw_pci_controllers[] = { + .tx_fifo_depth = 32, + .rx_fifo_depth = 32, + .clk_khz = 25000, ++ .explicit_stop = 0, + }, + [medfield_5] = { + .bus_num = 5, +@@ -131,6 +131,7 @@ static struct dw_pci_controller dw_pci_controllers[] = { + .tx_fifo_depth = 32, + .rx_fifo_depth = 32, + .clk_khz = 25000, ++ .explicit_stop = 0, + }, + }; + static struct i2c_algorithm i2c_dw_algo = { +@@ -282,6 +283,7 @@ const struct pci_device_id *id) + + dev->tx_fifo_depth = controller->tx_fifo_depth; + dev->rx_fifo_depth = controller->rx_fifo_depth; ++ dev->explicit_stop = controller->explicit_stop; + r = i2c_dw_init(dev); + if (r) + goto err_iounmap; +diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig +index 05e996f..168989b 100644 +--- a/drivers/iio/accel/Kconfig ++++ b/drivers/iio/accel/Kconfig +@@ -3,15 +3,12 @@ + # + menu "Accelerometers" + +-config HID_SENSOR_ACCEL_3D +- depends on HID_SENSOR_HUB +- select IIO_BUFFER +- select IIO_TRIGGERED_BUFFER +- select HID_SENSOR_IIO_COMMON +- select HID_SENSOR_IIO_TRIGGER +- tristate "HID Accelerometers 3D" +- help +- Say yes here to build support for the HID SENSOR +- accelerometers 3D. ++config IIO_LIS331DLH_INTEL_CLN ++ tristate "STMicroelectronics LIS331DLH accelerometer i2c driver for Intel Clanton platform" ++ depends on INTEL_QUARK_X1000_SOC ++ depends on I2C && SYSFS ++ select IIO_ST_SENSORS_CORE ++ help ++ Selects the LIS331DLH accelerometer driver for the Intel Clanton Hill platform + + endmenu +diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile +index 5bc6855..c82ce57 100644 +--- a/drivers/iio/accel/Makefile ++++ b/drivers/iio/accel/Makefile +@@ -1,5 +1,4 @@ + # + # Makefile for industrial I/O accelerometer drivers + # +- +-obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o ++obj-$(CONFIG_IIO_LIS331DLH_INTEL_CLN) += lis331dlh_intel_cln.o +diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c +index 0b0c3c6..563a2fe 100644 +--- a/drivers/iio/accel/hid-sensor-accel-3d.c ++++ b/drivers/iio/accel/hid-sensor-accel-3d.c +@@ -45,6 +45,7 @@ enum accel_3d_channel { + struct accel_3d_state { + struct hid_sensor_hub_callbacks callbacks; + struct hid_sensor_iio_common common_attributes; ++ struct hid_sensor_common common_attributes; + struct hid_sensor_hub_attribute_info accel[ACCEL_3D_CHANNEL_MAX]; + u32 accel_val[ACCEL_3D_CHANNEL_MAX]; + }; +diff --git a/drivers/iio/accel/lis331dlh_intel_cln.c b/drivers/iio/accel/lis331dlh_intel_cln.c +new file mode 100644 +index 0000000..57998d0 +--- /dev/null ++++ b/drivers/iio/accel/lis331dlh_intel_cln.c +@@ -0,0 +1,675 @@ ++/* ++ * Intel Clanton Hill platform accelerometer driver ++ * ++ * Copyright(c) 2013 Intel Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ * ++ * Derived from STMicroelectronics accelerometers driver by Denis Ciocca ++ * ++ * The Intel Clanton Hill platform hardware design includes an ++ * STMicroelectronics LIS331DLH accelerometer, intended to be used mainly for ++ * sensing orientation, movement and sudden impacts (e.g. vehicle collision) ++ * ++ * This driver plugs into the Linux Industrial-IO framework to provide a ++ * standardised user-space application interface for retreiving data and events ++ * from the accelerometer. ++ * ++ * The LIS331DLH is connected via I2C to the host CPU on the Clanton Hill ++ * platform and so this driver registers to the kernel as an I2C device driver ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/errno.h> ++#include <linux/types.h> ++#include <linux/mutex.h> ++#include <linux/interrupt.h> ++#include <linux/i2c.h> ++#include <linux/gpio.h> ++#include <linux/irq.h> ++#include <linux/iio/iio.h> ++#include <linux/iio/sysfs.h> ++#include <linux/iio/trigger.h> ++#include <linux/iio/buffer.h> ++ ++#include <linux/iio/common/st_sensors.h> ++#include <linux/iio/common/st_sensors_i2c.h> ++ ++#include <linux/platform_data/lis331dlh_intel_cln.h> ++ ++/* DEFAULT VALUE FOR SENSORS */ ++#define ST_ACCEL_DEFAULT_OUT_X_L_ADDR 0x28 ++#define ST_ACCEL_DEFAULT_OUT_Y_L_ADDR 0x2a ++#define ST_ACCEL_DEFAULT_OUT_Z_L_ADDR 0x2c ++ ++/* FULLSCALE */ ++#define ST_ACCEL_FS_AVL_2G 2 ++#define ST_ACCEL_FS_AVL_4G 4 ++#define ST_ACCEL_FS_AVL_6G 6 ++#define ST_ACCEL_FS_AVL_8G 8 ++#define ST_ACCEL_FS_AVL_16G 16 ++ ++/* CUSTOM VALUES FOR SENSOR 2 */ ++#define ST_ACCEL_2_WAI_EXP 0x32 ++#define ST_ACCEL_2_ODR_ADDR 0x20 ++#define ST_ACCEL_2_ODR_MASK 0x18 ++#define ST_ACCEL_2_ODR_AVL_50HZ_VAL 0x00 ++#define ST_ACCEL_2_ODR_AVL_100HZ_VAL 0x01 ++#define ST_ACCEL_2_ODR_AVL_400HZ_VAL 0x02 ++#define ST_ACCEL_2_ODR_AVL_1000HZ_VAL 0x03 ++#define ST_ACCEL_2_PW_ADDR 0x20 ++#define ST_ACCEL_2_PW_MASK 0xe0 ++#define ST_ACCEL_2_PW_DOWN 0x00 ++#define ST_ACCEL_2_PW_NORMAL 0x20 ++#define ST_ACCEL_2_CTRL_REG1_XEN 0x01 ++#define ST_ACCEL_2_CTRL_REG1_YEN 0x02 ++#define ST_ACCEL_2_CTRL_REG1_ZEN 0x04 ++#define ST_ACCEL_2_FS_ADDR 0x23 ++#define ST_ACCEL_2_FS_MASK 0x30 ++#define ST_ACCEL_2_FS_AVL_2_VAL 0X00 ++#define ST_ACCEL_2_FS_AVL_4_VAL 0X01 ++#define ST_ACCEL_2_FS_AVL_8_VAL 0x03 ++#define ST_ACCEL_2_FS_AVL_2_GAIN IIO_G_TO_M_S_2(1000) ++#define ST_ACCEL_2_FS_AVL_4_GAIN IIO_G_TO_M_S_2(2000) ++#define ST_ACCEL_2_FS_AVL_8_GAIN IIO_G_TO_M_S_2(3900) ++#define ST_ACCEL_2_BDU_ADDR 0x23 ++#define ST_ACCEL_2_BDU_MASK 0x80 ++#define ST_ACCEL_2_DRDY_IRQ_ADDR 0x22 ++#define ST_ACCEL_2_DRDY_IRQ_MASK 0x02 ++#define ST_ACCEL_2_THRESH_IRQ_ADDR 0x30 ++#define ST_ACCEL_2_THRESH_IRQ_MASK 0x7f ++#define ST_ACCEL_2_INT1_CFG_ADDR 0x30 ++#define ST_ACCEL_2_INT1_SRC_ADDR 0x31 ++#define ST_ACCEL_2_INT1_THRESH_ADDR 0x32 ++#define ST_ACCEL_2_INT1_DURATION_ADDR 0x33 ++#define ST_ACCEL_2_INT2_CFG_ADDR 0x34 ++#define ST_ACCEL_2_INT2_SRC_ADDR 0x35 ++#define ST_ACCEL_2_INT2_THRESH_ADDR 0x36 ++#define ST_ACCEL_2_INT2_DURATION_ADDR 0x37 ++#define ST_ACCEL_2_INT_IA_MASK 0x40 ++#define ST_ACCEL_2_INT_LIR_MASK 0x05 ++#define ST_ACCEL_2_INT_SRC_HIGH_MASK 0x20 ++#define ST_ACCEL_2_INT_CFG_XLIE_EN 0x01 ++#define ST_ACCEL_2_INT_CFG_XHIE_EN 0x02 ++#define ST_ACCEL_2_INT_CFG_YLIE_EN 0x04 ++#define ST_ACCEL_2_INT_CFG_YHIE_EN 0x08 ++#define ST_ACCEL_2_INT_CFG_ZLIE_EN 0x10 ++#define ST_ACCEL_2_INT_CFG_ZHIE_EN 0x20 ++ ++#define ST_ACCEL_2_MULTIREAD_BIT true ++#define CLN_ACCEL_INT2_WAKEUP_THRESH_VAL 0x7f ++ ++static const u8 iio_modifier_map[] = { ++ IIO_NO_MOD, ++ IIO_MOD_X, ++ IIO_MOD_Y, ++ IIO_MOD_X_AND_Y, ++ IIO_MOD_Z, ++ IIO_MOD_X_AND_Z, ++ IIO_MOD_Y_AND_Z, ++ IIO_MOD_X_AND_Y_AND_Z, ++}; ++ ++/* Threshold event ISR bottom half. This function reads interrupt status ++ * registers for INT1 to reset any active interrupt conditions ++ * and pushes an IIO event if a threshold interrupt was active. ++ */ ++static irqreturn_t lis331dlh_intel_cln_threshold_event_handler( ++ int irq, ++ void *private) ++{ ++ int err; ++ u8 data; ++ u8 mask; ++ int i; ++ u64 iio_modifier; ++ ++ struct st_sensor_data *sdata = iio_priv(private); ++ s64 timestamp = iio_get_time_ns(); ++ err = sdata->tf->read_byte(&sdata->tb, sdata->dev, ++ ST_ACCEL_2_INT1_SRC_ADDR, ++ &data); ++ ++ if (err < 0) ++ goto st_sensors_read_err; ++ ++ err = sdata->tf->read_byte(&sdata->tb, sdata->dev, ++ ST_ACCEL_2_INT1_CFG_ADDR, ++ &mask); ++ ++ if (err < 0) ++ goto st_sensors_read_err; ++ ++ if (data & ST_ACCEL_2_INT_IA_MASK) { ++ data &= mask; ++ ++ iio_modifier = 0; ++ for (i = 0; i < ST_SENSORS_NUMBER_DATA_CHANNELS; i++) { ++ iio_modifier <<= 1; ++ iio_modifier += !!(data & ST_ACCEL_2_INT_SRC_HIGH_MASK); ++ data <<= 2; ++ } ++ ++ iio_modifier = iio_modifier_map[iio_modifier]; ++ ++ iio_push_event(private, ++ IIO_EVENT_CODE(IIO_ACCEL, ++ 0, /* non differential */ ++ iio_modifier, ++ IIO_EV_TYPE_THRESH, ++ IIO_EV_DIR_RISING, 0, 0, 0), ++ timestamp); ++ } ++ ++st_sensors_read_err: ++ return IRQ_HANDLED; ++} ++ ++static int lis331dlh_intel_cln_read_raw( ++ struct iio_dev *indio_dev, ++ struct iio_chan_spec const *ch, ++ int *val, int *val2, long mask) ++{ ++ int err; ++ struct st_sensor_data *adata = iio_priv(indio_dev); ++ ++ switch (mask) { ++ case IIO_CHAN_INFO_RAW: ++ err = st_sensors_read_info_raw(indio_dev, ch, val); ++ if (err < 0) ++ goto read_error; ++ ++ return IIO_VAL_INT; ++ case IIO_CHAN_INFO_SCALE: ++ *val = 0; ++ *val2 = adata->current_fullscale->gain; ++ return IIO_VAL_INT_PLUS_MICRO; ++ default: ++ return -EINVAL; ++ } ++ ++read_error: ++ return err; ++} ++ ++static int lis331dlh_intel_cln_write_raw( ++ struct iio_dev *indio_dev, ++ struct iio_chan_spec const *chan, ++ int val, int val2, long mask) ++{ ++ int err; ++ ++ switch (mask) { ++ case IIO_CHAN_INFO_SCALE: ++ err = st_sensors_set_fullscale_by_gain(indio_dev, val2); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return err; ++} ++ ++ ++static ST_SENSOR_DEV_ATTR_SAMP_FREQ(); ++static ST_SENSORS_DEV_ATTR_SAMP_FREQ_AVAIL(); ++static ST_SENSORS_DEV_ATTR_SCALE_AVAIL(in_accel_scale_available); ++ ++static struct attribute *lis331dlh_intel_cln_attributes[] = { ++ &iio_dev_attr_sampling_frequency_available.dev_attr.attr, ++ &iio_dev_attr_in_accel_scale_available.dev_attr.attr, ++ &iio_dev_attr_sampling_frequency.dev_attr.attr, ++ NULL, ++}; ++ ++static const struct attribute_group lis331dlh_intel_cln_attribute_group = { ++ .attrs = lis331dlh_intel_cln_attributes, ++}; ++ ++static int lis331dlh_intel_cln_read_event_value( ++ struct iio_dev *indio_dev, ++ u64 event_code, ++ int *val) ++{ ++ int err; ++ u8 data; ++ struct st_sensor_data *sdata = iio_priv(indio_dev); ++ ++ err = sdata->tf->read_byte(&sdata->tb, sdata->dev, ++ ST_ACCEL_2_INT1_THRESH_ADDR, &data); ++ ++ *val = (int) data; ++ return err; ++} ++ ++static int lis331dlh_intel_cln_write_event_value( ++ struct iio_dev *indio_dev, ++ u64 event_code, ++ int val) ++{ ++ int err; ++ struct st_sensor_data *sdata; ++ ++ /* range check */ ++ if ((val < 0) || (val > 0x7f)) ++ return -EINVAL; ++ ++ sdata = iio_priv(indio_dev); ++ ++ err = sdata->tf->write_byte(&sdata->tb, sdata->dev, ++ ST_ACCEL_2_INT1_THRESH_ADDR, val); ++ ++ return err; ++ ++} ++ ++/* Configure the INT1 pin to fire an interrupt on a high threshold event. ++ */ ++static int lis331dlh_intel_cln_configure_threshold_interrupt( ++ struct iio_dev *indio_dev, bool state) ++{ ++ int err = 0; ++ struct st_sensor_data *sdata = iio_priv(indio_dev); ++ ++ if (sdata->int_thresh == state) ++ return 0; ++ ++ if (state) { ++ err = request_threaded_irq(sdata->get_irq_data_ready(indio_dev), ++ NULL, ++ lis331dlh_intel_cln_threshold_event_handler, ++ IRQF_TRIGGER_RISING | IRQF_ONESHOT, ++ "lis331dlh_intel_cln_threshold", ++ indio_dev); ++ if (err == 0) { ++ sdata->int_thresh = true; ++ err = sdata->tf->write_byte( ++ &sdata->tb, sdata->dev, ++ ST_ACCEL_2_INT1_DURATION_ADDR, 1); ++ } ++ } else { ++ free_irq(sdata->get_irq_data_ready(indio_dev), indio_dev); ++ sdata->int_thresh = false; ++ } ++ ++ return err; ++} ++ ++static int lis331dlh_intel_cln_read_event_config( ++ struct iio_dev *indio_dev, ++ u64 event_code) ++{ ++ int err = 0; ++ u8 data, mask; ++ struct st_sensor_data *sdata = iio_priv(indio_dev); ++ ++ err = sdata->tf->read_byte(&sdata->tb, sdata->dev, ++ ST_ACCEL_2_INT1_CFG_ADDR, ++ &data); ++ ++ mask = 1 << ((IIO_EVENT_CODE_EXTRACT_MODIFIER(event_code) << 1) - 1); ++ ++ return !!(data & mask); ++} ++ ++static int lis331dlh_intel_cln_write_event_config( ++ struct iio_dev *indio_dev, ++ u64 event_code, ++ int state) ++{ ++ int err; ++ u8 data; ++ u8 mask; ++ ++ bool new_int_state; ++ ++ struct st_sensor_data *sdata = iio_priv(indio_dev); ++ mask = 1 << ((IIO_EVENT_CODE_EXTRACT_MODIFIER(event_code) << 1) - 1); ++ ++ err = st_sensors_write_data_with_mask(indio_dev, ++ ST_ACCEL_2_INT1_CFG_ADDR, ++ mask, state); ++ ++ if (err == 0) ++ err = sdata->tf->read_byte(&sdata->tb, sdata->dev, ++ ST_ACCEL_2_INT1_CFG_ADDR, &data); ++ ++ if (err == 0) { ++ new_int_state = data & (ST_ACCEL_2_INT_CFG_XHIE_EN | ++ ST_ACCEL_2_INT_CFG_YHIE_EN | ++ ST_ACCEL_2_INT_CFG_ZHIE_EN); ++ err = lis331dlh_intel_cln_configure_threshold_interrupt( ++ indio_dev, new_int_state); ++ } ++ ++ return err; ++} ++ ++/* Configure the INT2 pin to fire an interrupt on a threshold high event. INT2 ++ * should be wired to a suspend well IRQ to wake up the host. ++ */ ++static int lis331dlh_intel_cln_enable_wakeup_interrupt( ++ struct iio_dev *indio_dev) ++{ ++ int err = 0; ++ u8 data; ++ struct st_sensor_data *sdata = iio_priv(indio_dev); ++ ++ if (err == 0) ++ err = sdata->tf->write_byte(&sdata->tb, sdata->dev, ++ ST_ACCEL_2_INT2_THRESH_ADDR, ++ CLN_ACCEL_INT2_WAKEUP_THRESH_VAL); ++ ++ /* Latch interrupt request on INT2 */ ++ if (err == 0) ++ err = st_sensors_write_data_with_mask( ++ indio_dev, ST_ACCEL_2_DRDY_IRQ_ADDR, ++ ST_ACCEL_2_INT_LIR_MASK, 1); ++ ++ if (err == 0) ++ err = sdata->tf->write_byte(&sdata->tb, sdata->dev, ++ ST_ACCEL_2_INT2_DURATION_ADDR, 0); ++ ++ if (err == 0) ++ err = sdata->tf->write_byte(&sdata->tb, sdata->dev, ++ ST_ACCEL_2_INT2_CFG_ADDR, ++ ST_ACCEL_2_INT_CFG_XHIE_EN | ++ ST_ACCEL_2_INT_CFG_YHIE_EN); ++ ++ /* Clean ST_ACCEL_2_INT2_SRC */ ++ if (err == 0) ++ err = sdata->tf->read_byte(&sdata->tb, sdata->dev, ++ ST_ACCEL_2_INT2_SRC_ADDR, ++ &data); ++ ++ return err; ++} ++ ++static int lis331dlh_intel_cln_disable_wakeup_interrupt( ++ struct iio_dev *indio_dev) ++{ ++ int err = 0; ++ u8 data; ++ struct st_sensor_data *sdata = iio_priv(indio_dev); ++ ++ if (err == 0) ++ err = sdata->tf->write_byte(&sdata->tb, sdata->dev, ++ ST_ACCEL_2_INT2_CFG_ADDR, ++ 0); ++ ++ /* Clean ST_ACCEL_2_INT2_SRC */ ++ if (err == 0) ++ err = sdata->tf->read_byte(&sdata->tb, sdata->dev, ++ ST_ACCEL_2_INT2_SRC_ADDR, ++ &data); ++ ++ return err; ++} ++ ++static int lis331dlh_intel_cln_handle_wakeup_interrupt( ++ struct iio_dev *indio_dev) ++{ ++ int err; ++ u8 data; ++ struct st_sensor_data *sdata = iio_priv(indio_dev); ++ s64 timestamp = iio_get_time_ns(); ++ ++ err = sdata->tf->read_byte(&sdata->tb, sdata->dev, ++ ST_ACCEL_2_INT2_SRC_ADDR, ++ &data); ++ ++ if (err == 0) ++ if (data & ST_ACCEL_2_INT_IA_MASK) { ++ iio_push_event(indio_dev, ++ IIO_EVENT_CODE(IIO_ACCEL, ++ 0, /* non differential */ ++ IIO_MOD_X_OR_Y_OR_Z, ++ IIO_EV_TYPE_THRESH, ++ IIO_EV_DIR_EITHER, 0, 0, 0), ++ timestamp); ++ } ++ ++ return err; ++} ++ ++static const struct iio_info accel_info = { ++ .driver_module = THIS_MODULE, ++ .attrs = &lis331dlh_intel_cln_attribute_group, ++ .read_raw = &lis331dlh_intel_cln_read_raw, ++ .write_raw = &lis331dlh_intel_cln_write_raw, ++ .read_event_config = &lis331dlh_intel_cln_read_event_config, ++ .write_event_config = &lis331dlh_intel_cln_write_event_config, ++ .read_event_value = &lis331dlh_intel_cln_read_event_value, ++ .write_event_value = &lis331dlh_intel_cln_write_event_value, ++}; ++ ++static const struct iio_chan_spec st_accel_12bit_channels[] = { ++ ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_X, IIO_MOD_X, IIO_LE, ++ ST_SENSORS_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR), ++ ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_Y, IIO_MOD_Y, IIO_LE, ++ ST_SENSORS_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR), ++ ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_Z, IIO_MOD_Z, IIO_LE, ++ ST_SENSORS_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR), ++ IIO_CHAN_SOFT_TIMESTAMP(3) ++}; ++ ++static struct st_sensors lis331dlh_intel_cln_sensor = { ++ .wai = ST_ACCEL_2_WAI_EXP, ++ .sensors_supported = { ++ [0] = "lis331dlh_cln", ++ }, ++ .ch = (struct iio_chan_spec *)st_accel_12bit_channels, ++ .odr = { ++ .addr = ST_ACCEL_2_ODR_ADDR, ++ .mask = ST_ACCEL_2_ODR_MASK, ++ .odr_avl = { ++ { 50, ST_ACCEL_2_ODR_AVL_50HZ_VAL, }, ++ { 100, ST_ACCEL_2_ODR_AVL_100HZ_VAL, }, ++ { 400, ST_ACCEL_2_ODR_AVL_400HZ_VAL, }, ++ { 1000, ST_ACCEL_2_ODR_AVL_1000HZ_VAL, }, ++ }, ++ }, ++ .pw = { ++ .addr = ST_ACCEL_2_PW_ADDR, ++ .mask = ST_ACCEL_2_PW_MASK, ++ .value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE, ++ .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, ++ }, ++ .enable_axis = { ++ .addr = ST_SENSORS_DEFAULT_AXIS_ADDR, ++ .mask = ST_SENSORS_DEFAULT_AXIS_MASK, ++ }, ++ .fs = { ++ .addr = ST_ACCEL_2_FS_ADDR, ++ .mask = ST_ACCEL_2_FS_MASK, ++ .fs_avl = { ++ [0] = { ++ .num = ST_ACCEL_FS_AVL_2G, ++ .value = ST_ACCEL_2_FS_AVL_2_VAL, ++ .gain = ST_ACCEL_2_FS_AVL_2_GAIN, ++ }, ++ [1] = { ++ .num = ST_ACCEL_FS_AVL_4G, ++ .value = ST_ACCEL_2_FS_AVL_4_VAL, ++ .gain = ST_ACCEL_2_FS_AVL_4_GAIN, ++ }, ++ [2] = { ++ .num = ST_ACCEL_FS_AVL_8G, ++ .value = ST_ACCEL_2_FS_AVL_8_VAL, ++ .gain = ST_ACCEL_2_FS_AVL_8_GAIN, ++ }, ++ }, ++ }, ++ .bdu = { ++ .addr = ST_ACCEL_2_BDU_ADDR, ++ .mask = ST_ACCEL_2_BDU_MASK, ++ }, ++ .drdy_irq = { ++ .addr = ST_ACCEL_2_DRDY_IRQ_ADDR, ++ .mask = ST_ACCEL_2_DRDY_IRQ_MASK, ++ }, ++ .multi_read_bit = ST_ACCEL_2_MULTIREAD_BIT, ++ .bootime = 2, ++}; ++ ++static int lis331dlh_intel_cln_probe( ++ struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct iio_dev *indio_dev; ++ struct st_sensor_data *adata; ++ struct lis331dlh_intel_cln_platform_data *pdata; ++ int ret = 0; ++ ++ indio_dev = iio_device_alloc(sizeof(*adata)); ++ if (indio_dev == NULL) { ++ ret = -ENOMEM; ++ goto iio_device_alloc_error; ++ } ++ ++ i2c_set_clientdata(client, indio_dev); ++ indio_dev->dev.parent = &client->dev; ++ indio_dev->name = client->name; ++ ++ adata = iio_priv(indio_dev); ++ adata->dev = &client->dev; ++ ++ pdata = client->dev.platform_data; ++ if (!pdata) { ++ pr_err("No platform data provided\n"); ++ goto lis331dlh_intel_cln_init_err; ++ } ++ ++ ret = gpio_to_irq(pdata->irq1_pin); ++ if (ret < 0) { ++ pr_err( ++ "Failed to obtain valid IRQ for GPIO %d, " ++ "gpio_to_irq returned %d\n", ++ pdata->irq1_pin, ret); ++ goto lis331dlh_intel_cln_init_err; ++ } ++ to_i2c_client(adata->dev)->irq = ret; ++ ++ st_sensors_i2c_configure(indio_dev, client, adata); ++ ++ indio_dev->modes = INDIO_DIRECT_MODE; ++ indio_dev->info = &accel_info; ++ ++ ret = st_sensors_check_device_support(indio_dev, ++ 1, &lis331dlh_intel_cln_sensor); ++ if (ret < 0) ++ goto lis331dlh_intel_cln_init_err; ++ ++ indio_dev->channels = adata->sensor->ch; ++ indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS; ++ ++ adata->multiread_bit = adata->sensor->multi_read_bit; ++ adata->current_fullscale = (struct st_sensor_fullscale_avl *) ++ &adata->sensor->fs.fs_avl[0]; ++ adata->odr = adata->sensor->odr.odr_avl[0].hz; ++ adata->int_thresh = false; ++ ++ ret = st_sensors_init_sensor(indio_dev); ++ if (ret < 0) ++ goto lis331dlh_intel_cln_init_err; ++ ++ ret = iio_device_register(indio_dev); ++ if (ret) ++ goto lis331dlh_intel_cln_init_err; ++ ++ return 0; ++ ++lis331dlh_intel_cln_init_err: ++ iio_device_free(indio_dev); ++iio_device_alloc_error: ++ return ret; ++} ++ ++static int lis331dlh_intel_cln_remove( ++ struct i2c_client *client) ++{ ++ struct iio_dev *indio_dev = i2c_get_clientdata(client); ++ struct st_sensor_data *adata = iio_priv(indio_dev); ++ ++ st_sensors_set_enable(indio_dev, false); ++ ++ if (adata->int_thresh) ++ free_irq(adata->get_irq_data_ready(indio_dev), indio_dev); ++ ++ iio_device_unregister(indio_dev); ++ ++ iio_device_free(indio_dev); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int lis331dlh_intel_cln_suspend( ++ struct device *dev) ++{ ++ struct iio_dev *indio_dev = dev_get_drvdata(dev); ++ ++ lis331dlh_intel_cln_enable_wakeup_interrupt(indio_dev); ++ ++ return 0; ++} ++ ++static int lis331dlh_intel_cln_resume( ++ struct device *dev) ++{ ++ struct iio_dev *indio_dev = dev_get_drvdata(dev); ++ ++ lis331dlh_intel_cln_handle_wakeup_interrupt(indio_dev); ++ lis331dlh_intel_cln_disable_wakeup_interrupt(indio_dev); ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops lis331dlh_intel_cln_pm_ops = { ++ .suspend = lis331dlh_intel_cln_suspend, ++ .resume = lis331dlh_intel_cln_resume, ++}; ++ ++#define LIS331DLH_INTEL_CLN_PM_OPS (&lis331dlh_intel_cln_pm_ops) ++#else ++#define LIS331DLH_INTEL_CLN_PM_OPS NULL ++#endif ++ ++static const struct i2c_device_id lis331dlh_intel_cln_id_table[] = { ++ { "lis331dlh_cln" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(i2c, lis331dlh_intel_cln_id_table); ++ ++static struct i2c_driver lis331dlh_intel_cln_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "lis331dlh_cln", ++ .pm = LIS331DLH_INTEL_CLN_PM_OPS, ++ }, ++ .probe = lis331dlh_intel_cln_probe, ++ .remove = lis331dlh_intel_cln_remove, ++ .id_table = lis331dlh_intel_cln_id_table, ++}; ++ ++module_i2c_driver(lis331dlh_intel_cln_driver); ++ ++MODULE_AUTHOR("Wojciech Ziemba <wojciech.ziemba@emutex.com>"); ++MODULE_DESCRIPTION("STMicroelectronics LIS331DLH accelerometer i2c driver for Intel Clanton platform"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/iio/adc/ad7298.c b/drivers/iio/adc/ad7298.c +index b34d754..60491e4 100644 +--- a/drivers/iio/adc/ad7298.c ++++ b/drivers/iio/adc/ad7298.c +@@ -33,7 +33,6 @@ + #define AD7298_TAVG (1 << 1) /* temperature sensor averaging enable */ + #define AD7298_PDD (1 << 0) /* partial power down enable */ + +-#define AD7298_MAX_CHAN 8 + #define AD7298_BITS 12 + #define AD7298_STORAGE_BITS 16 + #define AD7298_INTREF_mV 2500 +@@ -46,6 +45,7 @@ struct ad7298_state { + struct spi_device *spi; + struct regulator *reg; + unsigned ext_ref; ++ u16 ext_vin_max[AD7298_MAX_CHAN]; + struct spi_transfer ring_xfer[10]; + struct spi_transfer scan_single_xfer[3]; + struct spi_message ring_msg; +@@ -64,7 +64,7 @@ struct ad7298_state { + .indexed = 1, \ + .channel = index, \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ +- IIO_CHAN_INFO_SCALE_SHARED_BIT, \ ++ IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \ + .address = index, \ + .scan_index = index, \ + .scan_type = { \ +@@ -269,7 +269,10 @@ static int ad7298_read_raw(struct iio_dev *indio_dev, + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: +- *val = ad7298_get_ref_voltage(st); ++ if (st->ext_vin_max[chan->channel]) ++ *val = st->ext_vin_max[chan->channel]; ++ else ++ *val = ad7298_get_ref_voltage(st); + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_TEMP: +@@ -304,8 +307,15 @@ static int ad7298_probe(struct spi_device *spi) + + st = iio_priv(indio_dev); + +- if (pdata && pdata->ext_ref) +- st->ext_ref = AD7298_EXTREF; ++ if (pdata) { ++ int i; ++ ++ if (pdata->ext_ref) ++ st->ext_ref = AD7298_EXTREF; ++ ++ for (i = 0; i < AD7298_MAX_CHAN; i++) ++ st->ext_vin_max[i] = pdata->ext_vin_max[i]; ++ } + + if (st->ext_ref) { + st->reg = regulator_get(&spi->dev, "vref"); +diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig +index ed45ee5..3c77727 100644 +--- a/drivers/iio/common/Kconfig ++++ b/drivers/iio/common/Kconfig +@@ -2,4 +2,4 @@ + # IIO common modules + # + +-source "drivers/iio/common/hid-sensors/Kconfig" ++source "drivers/iio/common/st_sensors/Kconfig" +\ No newline at end of file +diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile +index 8158400..4d22a20 100644 +--- a/drivers/iio/common/Makefile ++++ b/drivers/iio/common/Makefile +@@ -6,4 +6,4 @@ + # instead of duplicating in each module. + # + +-obj-y += hid-sensors/ ++obj-y += st_sensors/ +\ No newline at end of file +diff --git a/drivers/iio/common/st_sensors/Kconfig b/drivers/iio/common/st_sensors/Kconfig +new file mode 100644 +index 0000000..d1b474a +--- /dev/null ++++ b/drivers/iio/common/st_sensors/Kconfig +@@ -0,0 +1,20 @@ ++# ++# STMicroelectronics sensors common library ++# ++ ++menu "STMicro sensors" ++config IIO_ST_SENSORS_I2C ++ tristate "IIO ST SENSORS" ++ help ++ Enable SENSORS I2C option ++ ++config IIO_ST_SENSORS_SPI ++ tristate "IIO SENSORS SPI" ++ help ++ Enable IIO_ST_SENSORS_SPI ++ ++config IIO_ST_SENSORS_CORE ++ tristate "IIO SENSORS CORE" ++ select IIO_ST_SENSORS_I2C if I2C ++ select IIO_ST_SENSORS_SPI if SPI_MASTER ++endmenu +diff --git a/drivers/iio/common/st_sensors/Makefile b/drivers/iio/common/st_sensors/Makefile +new file mode 100644 +index 0000000..9f3e24f +--- /dev/null ++++ b/drivers/iio/common/st_sensors/Makefile +@@ -0,0 +1,10 @@ ++# ++# Makefile for the STMicroelectronics sensor common modules. ++# ++ ++obj-$(CONFIG_IIO_ST_SENSORS_I2C) += st_sensors_i2c.o ++obj-$(CONFIG_IIO_ST_SENSORS_SPI) += st_sensors_spi.o ++obj-$(CONFIG_IIO_ST_SENSORS_CORE) += st_sensors.o ++st_sensors-y := st_sensors_core.o ++st_sensors-$(CONFIG_IIO_BUFFER) += st_sensors_buffer.o ++st_sensors-$(CONFIG_IIO_TRIGGER) += st_sensors_trigger.o +diff --git a/drivers/iio/common/st_sensors/st_sensors_buffer.c b/drivers/iio/common/st_sensors/st_sensors_buffer.c +new file mode 100644 +index 0000000..09b236d +--- /dev/null ++++ b/drivers/iio/common/st_sensors/st_sensors_buffer.c +@@ -0,0 +1,116 @@ ++/* ++ * STMicroelectronics sensors buffer library driver ++ * ++ * Copyright 2012-2013 STMicroelectronics Inc. ++ * ++ * Denis Ciocca <denis.ciocca@st.com> ++ * ++ * Licensed under the GPL-2. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/iio/iio.h> ++#include <linux/iio/trigger.h> ++#include <linux/interrupt.h> ++#include <linux/iio/buffer.h> ++#include <linux/iio/trigger_consumer.h> ++#include <linux/iio/triggered_buffer.h> ++#include <linux/irqreturn.h> ++ ++#include <linux/iio/common/st_sensors.h> ++ ++ ++int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf) ++{ ++ int i, n = 0, len; ++ u8 addr[ST_SENSORS_NUMBER_DATA_CHANNELS]; ++ struct st_sensor_data *sdata = iio_priv(indio_dev); ++ ++ for (i = 0; i < ST_SENSORS_NUMBER_DATA_CHANNELS; i++) { ++ if (test_bit(i, indio_dev->active_scan_mask)) { ++ addr[n] = indio_dev->channels[i].address; ++ n++; ++ } ++ } ++ switch (n) { ++ case 1: ++ len = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev, ++ addr[0], ST_SENSORS_BYTE_FOR_CHANNEL, buf, ++ sdata->multiread_bit); ++ break; ++ case 2: ++ if ((addr[1] - addr[0]) == ST_SENSORS_BYTE_FOR_CHANNEL) { ++ len = sdata->tf->read_multiple_byte(&sdata->tb, ++ sdata->dev, addr[0], ++ ST_SENSORS_BYTE_FOR_CHANNEL*n, ++ buf, sdata->multiread_bit); ++ } else { ++ u8 rx_array[ST_SENSORS_BYTE_FOR_CHANNEL* ++ ST_SENSORS_NUMBER_DATA_CHANNELS]; ++ len = sdata->tf->read_multiple_byte(&sdata->tb, ++ sdata->dev, addr[0], ++ ST_SENSORS_BYTE_FOR_CHANNEL* ++ ST_SENSORS_NUMBER_DATA_CHANNELS, ++ rx_array, sdata->multiread_bit); ++ if (len < 0) ++ goto read_data_channels_error; ++ ++ for (i = 0; i < n * ST_SENSORS_NUMBER_DATA_CHANNELS; ++ i++) { ++ if (i < n) ++ buf[i] = rx_array[i]; ++ else ++ buf[i] = rx_array[n + i]; ++ } ++ len = ST_SENSORS_BYTE_FOR_CHANNEL*n; ++ } ++ break; ++ case 3: ++ len = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev, ++ addr[0], ST_SENSORS_BYTE_FOR_CHANNEL* ++ ST_SENSORS_NUMBER_DATA_CHANNELS, ++ buf, sdata->multiread_bit); ++ break; ++ default: ++ len = -EINVAL; ++ goto read_data_channels_error; ++ } ++ if (len != ST_SENSORS_BYTE_FOR_CHANNEL*n) { ++ len = -EIO; ++ goto read_data_channels_error; ++ } ++ ++read_data_channels_error: ++ return len; ++} ++EXPORT_SYMBOL(st_sensors_get_buffer_element); ++ ++irqreturn_t st_sensors_trigger_handler(int irq, void *p) ++{ ++ int len; ++ struct iio_poll_func *pf = p; ++ struct iio_dev *indio_dev = pf->indio_dev; ++ struct st_sensor_data *sdata = iio_priv(indio_dev); ++ ++ len = st_sensors_get_buffer_element(indio_dev, sdata->buffer_data); ++ if (len < 0) ++ goto st_sensors_get_buffer_element_error; ++ ++ if (indio_dev->scan_timestamp) ++ *(s64 *)((u8 *)sdata->buffer_data + ++ ALIGN(len, sizeof(s64))) = pf->timestamp; ++ ++ iio_push_to_buffers(indio_dev, sdata->buffer_data); ++ ++st_sensors_get_buffer_element_error: ++ iio_trigger_notify_done(indio_dev->trig); ++ ++ return IRQ_HANDLED; ++} ++EXPORT_SYMBOL(st_sensors_trigger_handler); ++ ++MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); ++MODULE_DESCRIPTION("STMicroelectronics ST-sensors buffer"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c +new file mode 100644 +index 0000000..9873869 +--- /dev/null ++++ b/drivers/iio/common/st_sensors/st_sensors_core.c +@@ -0,0 +1,435 @@ ++/* ++ * STMicroelectronics sensors core library driver ++ * ++ * Copyright 2012-2013 STMicroelectronics Inc. ++ * ++ * Denis Ciocca <denis.ciocca@st.com> ++ * ++ * Licensed under the GPL-2. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/delay.h> ++#include <linux/iio/iio.h> ++#include <asm/unaligned.h> ++ ++#include <linux/iio/common/st_sensors.h> ++ ++ ++#define ST_SENSORS_WAI_ADDRESS 0x0f ++ ++int st_sensors_write_data_with_mask(struct iio_dev *indio_dev, ++ u8 reg_addr, u8 mask, u8 data) ++{ ++ int err; ++ u8 new_data; ++ struct st_sensor_data *sdata = iio_priv(indio_dev); ++ ++ err = sdata->tf->read_byte(&sdata->tb, sdata->dev, reg_addr, &new_data); ++ if (err < 0) ++ goto st_sensors_write_data_with_mask_error; ++ ++ new_data = ((new_data & (~mask)) | ((data << __ffs(mask)) & mask)); ++ err = sdata->tf->write_byte(&sdata->tb, sdata->dev, reg_addr, new_data); ++ ++st_sensors_write_data_with_mask_error: ++ return err; ++} ++EXPORT_SYMBOL(st_sensors_write_data_with_mask); ++ ++static int st_sensors_match_odr(struct st_sensors *sensor, ++ unsigned int odr, struct st_sensor_odr_avl *odr_out) ++{ ++ int i, ret = -EINVAL; ++ ++ for (i = 0; i < ST_SENSORS_ODR_LIST_MAX; i++) { ++ if (sensor->odr.odr_avl[i].hz == 0) ++ goto st_sensors_match_odr_error; ++ ++ if (sensor->odr.odr_avl[i].hz == odr) { ++ odr_out->hz = sensor->odr.odr_avl[i].hz; ++ odr_out->value = sensor->odr.odr_avl[i].value; ++ ret = 0; ++ break; ++ } ++ } ++ ++st_sensors_match_odr_error: ++ return ret; ++} ++ ++int st_sensors_set_odr(struct iio_dev *indio_dev, unsigned int odr) ++{ ++ int err; ++ struct st_sensor_odr_avl odr_out = {0, 0}; ++ struct st_sensor_data *sdata = iio_priv(indio_dev); ++ ++ err = st_sensors_match_odr(sdata->sensor, odr, &odr_out); ++ if (err < 0) ++ goto st_sensors_match_odr_error; ++ ++ if ((sdata->sensor->odr.addr == sdata->sensor->pw.addr) && ++ (sdata->sensor->odr.mask == sdata->sensor->pw.mask)) { ++ if (sdata->enabled == true) { ++ err = st_sensors_write_data_with_mask(indio_dev, ++ sdata->sensor->odr.addr, ++ sdata->sensor->odr.mask, ++ odr_out.value); ++ } else { ++ err = 0; ++ } ++ } else { ++ err = st_sensors_write_data_with_mask(indio_dev, ++ sdata->sensor->odr.addr, sdata->sensor->odr.mask, ++ odr_out.value); ++ } ++ if (err >= 0) ++ sdata->odr = odr_out.hz; ++ ++st_sensors_match_odr_error: ++ return err; ++} ++EXPORT_SYMBOL(st_sensors_set_odr); ++ ++static int st_sensors_match_fs(struct st_sensors *sensor, ++ unsigned int fs, int *index_fs_avl) ++{ ++ int i, ret = -EINVAL; ++ ++ for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) { ++ if (sensor->fs.fs_avl[i].num == 0) ++ goto st_sensors_match_odr_error; ++ ++ if (sensor->fs.fs_avl[i].num == fs) { ++ *index_fs_avl = i; ++ ret = 0; ++ break; ++ } ++ } ++ ++st_sensors_match_odr_error: ++ return ret; ++} ++ ++static int st_sensors_set_fullscale(struct iio_dev *indio_dev, unsigned int fs) ++{ ++ int err, i = 0; ++ struct st_sensor_data *sdata = iio_priv(indio_dev); ++ ++ err = st_sensors_match_fs(sdata->sensor, fs, &i); ++ if (err < 0) ++ goto st_accel_set_fullscale_error; ++ ++ err = st_sensors_write_data_with_mask(indio_dev, ++ sdata->sensor->fs.addr, ++ sdata->sensor->fs.mask, ++ sdata->sensor->fs.fs_avl[i].value); ++ if (err < 0) ++ goto st_accel_set_fullscale_error; ++ ++ sdata->current_fullscale = (struct st_sensor_fullscale_avl *) ++ &sdata->sensor->fs.fs_avl[i]; ++ return err; ++ ++st_accel_set_fullscale_error: ++ dev_err(&indio_dev->dev, "failed to set new fullscale.\n"); ++ return err; ++} ++ ++int st_sensors_set_enable(struct iio_dev *indio_dev, bool enable) ++{ ++ u8 tmp_value; ++ int err = -EINVAL; ++ bool found = false; ++ struct st_sensor_odr_avl odr_out = {0, 0}; ++ struct st_sensor_data *sdata = iio_priv(indio_dev); ++ ++ if (enable) { ++ tmp_value = sdata->sensor->pw.value_on; ++ if ((sdata->sensor->odr.addr == sdata->sensor->pw.addr) && ++ (sdata->sensor->odr.mask == sdata->sensor->pw.mask)) { ++ err = st_sensors_match_odr(sdata->sensor, ++ sdata->odr, &odr_out); ++ if (err < 0) ++ goto set_enable_error; ++ tmp_value = odr_out.value; ++ found = true; ++ } ++ err = st_sensors_write_data_with_mask(indio_dev, ++ sdata->sensor->pw.addr, ++ sdata->sensor->pw.mask, tmp_value); ++ if (err < 0) ++ goto set_enable_error; ++ ++ sdata->enabled = true; ++ ++ if (found) ++ sdata->odr = odr_out.hz; ++ } else { ++ err = st_sensors_write_data_with_mask(indio_dev, ++ sdata->sensor->pw.addr, ++ sdata->sensor->pw.mask, ++ sdata->sensor->pw.value_off); ++ if (err < 0) ++ goto set_enable_error; ++ ++ sdata->enabled = false; ++ } ++ ++set_enable_error: ++ return err; ++} ++EXPORT_SYMBOL(st_sensors_set_enable); ++ ++int st_sensors_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable) ++{ ++ struct st_sensor_data *sdata = iio_priv(indio_dev); ++ ++ return st_sensors_write_data_with_mask(indio_dev, ++ sdata->sensor->enable_axis.addr, ++ sdata->sensor->enable_axis.mask, axis_enable); ++} ++EXPORT_SYMBOL(st_sensors_set_axis_enable); ++ ++int st_sensors_init_sensor(struct iio_dev *indio_dev) ++{ ++ int err; ++ struct st_sensor_data *sdata = iio_priv(indio_dev); ++ ++ mutex_init(&sdata->tb.buf_lock); ++ ++ err = st_sensors_set_enable(indio_dev, true); ++ if (err < 0) ++ goto init_error; ++ ++ err = st_sensors_set_fullscale(indio_dev, ++ sdata->current_fullscale->num); ++ if (err < 0) ++ goto init_error; ++ ++ err = st_sensors_set_odr(indio_dev, sdata->odr); ++ if (err < 0) ++ goto init_error; ++ ++ /* set BDU */ ++ err = st_sensors_write_data_with_mask(indio_dev, ++ sdata->sensor->bdu.addr, sdata->sensor->bdu.mask, true); ++ if (err < 0) ++ goto init_error; ++ ++ err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS); ++ ++init_error: ++ return err; ++} ++EXPORT_SYMBOL(st_sensors_init_sensor); ++ ++int st_sensors_set_dataready_irq(struct iio_dev *indio_dev, bool enable) ++{ ++ int err; ++ struct st_sensor_data *sdata = iio_priv(indio_dev); ++ ++ /* Enable/Disable the interrupt generator 1. */ ++ if (sdata->sensor->drdy_irq.ig1.en_addr > 0) { ++ err = st_sensors_write_data_with_mask(indio_dev, ++ sdata->sensor->drdy_irq.ig1.en_addr, ++ sdata->sensor->drdy_irq.ig1.en_mask, (int)enable); ++ if (err < 0) ++ goto st_accel_set_dataready_irq_error; ++ } ++ ++ /* Enable/Disable the interrupt generator for data ready. */ ++ err = st_sensors_write_data_with_mask(indio_dev, ++ sdata->sensor->drdy_irq.addr, ++ sdata->sensor->drdy_irq.mask, (int)enable); ++ ++st_accel_set_dataready_irq_error: ++ return err; ++} ++EXPORT_SYMBOL(st_sensors_set_dataready_irq); ++ ++int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev, int scale) ++{ ++ int err = -EINVAL, i; ++ struct st_sensor_data *sdata = iio_priv(indio_dev); ++ ++ for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) { ++ if ((sdata->sensor->fs.fs_avl[i].gain == scale) && ++ (sdata->sensor->fs.fs_avl[i].gain != 0)) { ++ err = 0; ++ break; ++ } ++ } ++ if (err < 0) ++ goto st_sensors_match_scale_error; ++ ++ err = st_sensors_set_fullscale(indio_dev, ++ sdata->sensor->fs.fs_avl[i].num); ++ ++st_sensors_match_scale_error: ++ return err; ++} ++EXPORT_SYMBOL(st_sensors_set_fullscale_by_gain); ++ ++static int st_sensors_read_axis_data(struct iio_dev *indio_dev, ++ u8 ch_addr, int *data) ++{ ++ int err; ++ u8 outdata[ST_SENSORS_BYTE_FOR_CHANNEL]; ++ struct st_sensor_data *sdata = iio_priv(indio_dev); ++ ++ err = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev, ++ ch_addr, ST_SENSORS_BYTE_FOR_CHANNEL, ++ outdata, sdata->multiread_bit); ++ if (err < 0) ++ goto read_error; ++ ++ *data = (s16)get_unaligned_le16(outdata); ++ ++read_error: ++ return err; ++} ++ ++int st_sensors_read_info_raw(struct iio_dev *indio_dev, ++ struct iio_chan_spec const *ch, int *val) ++{ ++ int err; ++ ++ mutex_lock(&indio_dev->mlock); ++ err = st_sensors_read_axis_data(indio_dev, ch->address, val); ++ if (err < 0) ++ goto read_error; ++ ++ *val = *val >> ch->scan_type.shift; ++ mutex_unlock(&indio_dev->mlock); ++ ++ return err; ++ ++read_error: ++ mutex_unlock(&indio_dev->mlock); ++ return err; ++} ++EXPORT_SYMBOL(st_sensors_read_info_raw); ++ ++int st_sensors_check_device_support(struct iio_dev *indio_dev, ++ int num_sensors_list, const struct st_sensors *sensors) ++{ ++ u8 wai; ++ int i, n, err; ++ struct st_sensor_data *sdata = iio_priv(indio_dev); ++ ++ err = sdata->tf->read_byte(&sdata->tb, sdata->dev, ++ ST_SENSORS_DEFAULT_WAI_ADDRESS, &wai); ++ if (err < 0) { ++ dev_err(&indio_dev->dev, "failed to read Who-Am-I register.\n"); ++ goto read_wai_error; ++ } ++ ++ for (i = 0; i < num_sensors_list; i++) { ++ if (sensors[i].wai == wai) ++ break; ++ } ++ if (i == num_sensors_list) ++ goto device_not_supported; ++ ++ for (n = 0; n < ARRAY_SIZE(sensors[i].sensors_supported); n++) { ++ if (strcmp(indio_dev->name, ++ &sensors[i].sensors_supported[n][0]) == 0) ++ break; ++ } ++ if (n == ARRAY_SIZE(sensors[i].sensors_supported)) { ++ dev_err(&indio_dev->dev, "device name and WhoAmI mismatch.\n"); ++ goto sensor_name_mismatch; ++ } ++ ++ sdata->sensor = (struct st_sensors *)&sensors[i]; ++ ++ return i; ++ ++device_not_supported: ++ dev_err(&indio_dev->dev, "device not supported: WhoAmI (0x%x).\n", wai); ++sensor_name_mismatch: ++ err = -ENODEV; ++read_wai_error: ++ return err; ++} ++EXPORT_SYMBOL(st_sensors_check_device_support); ++ ++ssize_t st_sensors_sysfs_get_sampling_frequency(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct st_sensor_data *adata = iio_priv(dev_get_drvdata(dev)); ++ ++ return sprintf(buf, "%d\n", adata->odr); ++} ++EXPORT_SYMBOL(st_sensors_sysfs_get_sampling_frequency); ++ ++ssize_t st_sensors_sysfs_set_sampling_frequency(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t size) ++{ ++ int err; ++ unsigned int odr; ++ struct iio_dev *indio_dev = dev_get_drvdata(dev); ++ ++ err = kstrtoint(buf, 10, &odr); ++ if (err < 0) ++ goto conversion_error; ++ ++ mutex_lock(&indio_dev->mlock); ++ err = st_sensors_set_odr(indio_dev, odr); ++ mutex_unlock(&indio_dev->mlock); ++ ++conversion_error: ++ return err < 0 ? err : size; ++} ++EXPORT_SYMBOL(st_sensors_sysfs_set_sampling_frequency); ++ ++ssize_t st_sensors_sysfs_sampling_frequency_avail(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ int i, len = 0; ++ struct iio_dev *indio_dev = dev_get_drvdata(dev); ++ struct st_sensor_data *sdata = iio_priv(indio_dev); ++ ++ mutex_lock(&indio_dev->mlock); ++ for (i = 0; i < ST_SENSORS_ODR_LIST_MAX; i++) { ++ if (sdata->sensor->odr.odr_avl[i].hz == 0) ++ break; ++ ++ len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", ++ sdata->sensor->odr.odr_avl[i].hz); ++ } ++ mutex_unlock(&indio_dev->mlock); ++ buf[len - 1] = '\n'; ++ ++ return len; ++} ++EXPORT_SYMBOL(st_sensors_sysfs_sampling_frequency_avail); ++ ++ssize_t st_sensors_sysfs_scale_avail(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ int i, len = 0; ++ struct iio_dev *indio_dev = dev_get_drvdata(dev); ++ struct st_sensor_data *sdata = iio_priv(indio_dev); ++ ++ mutex_lock(&indio_dev->mlock); ++ for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) { ++ if (sdata->sensor->fs.fs_avl[i].num == 0) ++ break; ++ ++ len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ", ++ sdata->sensor->fs.fs_avl[i].gain); ++ } ++ mutex_unlock(&indio_dev->mlock); ++ buf[len - 1] = '\n'; ++ ++ return len; ++} ++EXPORT_SYMBOL(st_sensors_sysfs_scale_avail); ++ ++MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); ++MODULE_DESCRIPTION("STMicroelectronics ST-sensors core"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/iio/common/st_sensors/st_sensors_i2c.c b/drivers/iio/common/st_sensors/st_sensors_i2c.c +new file mode 100644 +index 0000000..f8e82c5 +--- /dev/null ++++ b/drivers/iio/common/st_sensors/st_sensors_i2c.c +@@ -0,0 +1,80 @@ ++/* ++ * STMicroelectronics sensors i2c library driver ++ * ++ * Copyright 2012-2013 STMicroelectronics Inc. ++ * ++ * Denis Ciocca <denis.ciocca@st.com> ++ * ++ * Licensed under the GPL-2. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/iio/iio.h> ++ ++#include <linux/iio/common/st_sensors_i2c.h> ++ ++#define ST_SENSORS_I2C_MULTIREAD 0x80 ++ ++static unsigned int st_sensors_i2c_get_irq(struct iio_dev *indio_dev) ++{ ++ struct st_sensor_data *sdata = iio_priv(indio_dev); ++ ++ return to_i2c_client(sdata->dev)->irq; ++} ++ ++static int st_sensors_i2c_read_byte(struct st_sensor_transfer_buffer *tb, ++ struct device *dev, u8 reg_addr, u8 *res_byte) ++{ ++ int err; ++ ++ err = i2c_smbus_read_byte_data(to_i2c_client(dev), reg_addr); ++ if (err < 0) ++ goto st_accel_i2c_read_byte_error; ++ ++ *res_byte = err & 0xff; ++ ++st_accel_i2c_read_byte_error: ++ return err < 0 ? err : 0; ++} ++ ++static int st_sensors_i2c_read_multiple_byte( ++ struct st_sensor_transfer_buffer *tb, struct device *dev, ++ u8 reg_addr, int len, u8 *data, bool multiread_bit) ++{ ++ if (multiread_bit) ++ reg_addr |= ST_SENSORS_I2C_MULTIREAD; ++ ++ return i2c_smbus_read_i2c_block_data(to_i2c_client(dev), ++ reg_addr, len, data); ++} ++ ++static int st_sensors_i2c_write_byte(struct st_sensor_transfer_buffer *tb, ++ struct device *dev, u8 reg_addr, u8 data) ++{ ++ return i2c_smbus_write_byte_data(to_i2c_client(dev), reg_addr, data); ++} ++ ++static const struct st_sensor_transfer_function st_sensors_tf_i2c = { ++ .read_byte = st_sensors_i2c_read_byte, ++ .write_byte = st_sensors_i2c_write_byte, ++ .read_multiple_byte = st_sensors_i2c_read_multiple_byte, ++}; ++ ++void st_sensors_i2c_configure(struct iio_dev *indio_dev, ++ struct i2c_client *client, struct st_sensor_data *sdata) ++{ ++ i2c_set_clientdata(client, indio_dev); ++ ++ indio_dev->dev.parent = &client->dev; ++ indio_dev->name = client->name; ++ ++ sdata->tf = &st_sensors_tf_i2c; ++ sdata->get_irq_data_ready = st_sensors_i2c_get_irq; ++} ++EXPORT_SYMBOL(st_sensors_i2c_configure); ++ ++MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); ++MODULE_DESCRIPTION("STMicroelectronics ST-sensors i2c driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/iio/common/st_sensors/st_sensors_spi.c b/drivers/iio/common/st_sensors/st_sensors_spi.c +new file mode 100644 +index 0000000..f0aa2f1 +--- /dev/null ++++ b/drivers/iio/common/st_sensors/st_sensors_spi.c +@@ -0,0 +1,128 @@ ++/* ++ * STMicroelectronics sensors spi library driver ++ * ++ * Copyright 2012-2013 STMicroelectronics Inc. ++ * ++ * Denis Ciocca <denis.ciocca@st.com> ++ * ++ * Licensed under the GPL-2. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/iio/iio.h> ++ ++#include <linux/iio/common/st_sensors_spi.h> ++ ++ ++#define ST_SENSORS_SPI_MULTIREAD 0xc0 ++#define ST_SENSORS_SPI_READ 0x80 ++ ++static unsigned int st_sensors_spi_get_irq(struct iio_dev *indio_dev) ++{ ++ struct st_sensor_data *sdata = iio_priv(indio_dev); ++ ++ return to_spi_device(sdata->dev)->irq; ++} ++ ++static int st_sensors_spi_read(struct st_sensor_transfer_buffer *tb, ++ struct device *dev, u8 reg_addr, int len, u8 *data, bool multiread_bit) ++{ ++ struct spi_message msg; ++ int err; ++ ++ struct spi_transfer xfers[] = { ++ { ++ .tx_buf = tb->tx_buf, ++ .bits_per_word = 8, ++ .len = 1, ++ }, ++ { ++ .rx_buf = tb->rx_buf, ++ .bits_per_word = 8, ++ .len = len, ++ } ++ }; ++ ++ mutex_lock(&tb->buf_lock); ++ if ((multiread_bit) && (len > 1)) ++ tb->tx_buf[0] = reg_addr | ST_SENSORS_SPI_MULTIREAD; ++ else ++ tb->tx_buf[0] = reg_addr | ST_SENSORS_SPI_READ; ++ ++ spi_message_init(&msg); ++ spi_message_add_tail(&xfers[0], &msg); ++ spi_message_add_tail(&xfers[1], &msg); ++ err = spi_sync(to_spi_device(dev), &msg); ++ if (err) ++ goto acc_spi_read_error; ++ ++ memcpy(data, tb->rx_buf, len*sizeof(u8)); ++ mutex_unlock(&tb->buf_lock); ++ return len; ++ ++acc_spi_read_error: ++ mutex_unlock(&tb->buf_lock); ++ return err; ++} ++ ++static int st_sensors_spi_read_byte(struct st_sensor_transfer_buffer *tb, ++ struct device *dev, u8 reg_addr, u8 *res_byte) ++{ ++ return st_sensors_spi_read(tb, dev, reg_addr, 1, res_byte, false); ++} ++ ++static int st_sensors_spi_read_multiple_byte( ++ struct st_sensor_transfer_buffer *tb, struct device *dev, ++ u8 reg_addr, int len, u8 *data, bool multiread_bit) ++{ ++ return st_sensors_spi_read(tb, dev, reg_addr, len, data, multiread_bit); ++} ++ ++static int st_sensors_spi_write_byte(struct st_sensor_transfer_buffer *tb, ++ struct device *dev, u8 reg_addr, u8 data) ++{ ++ struct spi_message msg; ++ int err; ++ ++ struct spi_transfer xfers = { ++ .tx_buf = tb->tx_buf, ++ .bits_per_word = 8, ++ .len = 2, ++ }; ++ ++ mutex_lock(&tb->buf_lock); ++ tb->tx_buf[0] = reg_addr; ++ tb->tx_buf[1] = data; ++ ++ spi_message_init(&msg); ++ spi_message_add_tail(&xfers, &msg); ++ err = spi_sync(to_spi_device(dev), &msg); ++ mutex_unlock(&tb->buf_lock); ++ ++ return err; ++} ++ ++static const struct st_sensor_transfer_function st_sensors_tf_spi = { ++ .read_byte = st_sensors_spi_read_byte, ++ .write_byte = st_sensors_spi_write_byte, ++ .read_multiple_byte = st_sensors_spi_read_multiple_byte, ++}; ++ ++void st_sensors_spi_configure(struct iio_dev *indio_dev, ++ struct spi_device *spi, struct st_sensor_data *sdata) ++{ ++ spi_set_drvdata(spi, indio_dev); ++ ++ indio_dev->dev.parent = &spi->dev; ++ indio_dev->name = spi->modalias; ++ ++ sdata->tf = &st_sensors_tf_spi; ++ sdata->get_irq_data_ready = st_sensors_spi_get_irq; ++} ++EXPORT_SYMBOL(st_sensors_spi_configure); ++ ++MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); ++MODULE_DESCRIPTION("STMicroelectronics ST-sensors spi driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c +new file mode 100644 +index 0000000..8b4dd48 +--- /dev/null ++++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c +@@ -0,0 +1,78 @@ ++/* ++ * STMicroelectronics sensors trigger library driver ++ * ++ * Copyright 2012-2013 STMicroelectronics Inc. ++ * ++ * Denis Ciocca <denis.ciocca@st.com> ++ * ++ * Licensed under the GPL-2. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/iio/iio.h> ++#include <linux/iio/trigger.h> ++#include <linux/interrupt.h> ++ ++#include <linux/iio/common/st_sensors.h> ++ ++ ++int st_sensors_allocate_trigger(struct iio_dev *indio_dev, ++ const struct iio_trigger_ops *trigger_ops) ++{ ++ int err; ++ struct st_sensor_data *sdata = iio_priv(indio_dev); ++ ++ sdata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name); ++ if (sdata->trig == NULL) { ++ err = -ENOMEM; ++ dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n"); ++ goto iio_trigger_alloc_error; ++ } ++ ++ err = request_threaded_irq(sdata->get_irq_data_ready(indio_dev), ++ iio_trigger_generic_data_rdy_poll, ++ NULL, ++ IRQF_SHARED | /* sharing with the accelerometer events*/ ++ IRQF_TRIGGER_RISING | IRQF_ONESHOT, ++ sdata->trig->name, ++ sdata->trig); ++ if (err) ++ goto request_irq_error; ++ ++ sdata->trig->private_data = indio_dev; ++ sdata->trig->ops = trigger_ops; ++ sdata->trig->dev.parent = sdata->dev; ++ ++ err = iio_trigger_register(sdata->trig); ++ if (err < 0) { ++ dev_err(&indio_dev->dev, "failed to register iio trigger.\n"); ++ goto iio_trigger_register_error; ++ } ++ indio_dev->trig = sdata->trig; ++ ++ return 0; ++ ++iio_trigger_register_error: ++ free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig); ++request_irq_error: ++ iio_trigger_free(sdata->trig); ++iio_trigger_alloc_error: ++ return err; ++} ++EXPORT_SYMBOL(st_sensors_allocate_trigger); ++ ++void st_sensors_deallocate_trigger(struct iio_dev *indio_dev) ++{ ++ struct st_sensor_data *sdata = iio_priv(indio_dev); ++ ++ iio_trigger_unregister(sdata->trig); ++ free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig); ++ iio_trigger_free(sdata->trig); ++} ++EXPORT_SYMBOL(st_sensors_deallocate_trigger); ++ ++MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); ++MODULE_DESCRIPTION("STMicroelectronics ST-sensors trigger"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c +index aaadd32..7b8d510 100644 +--- a/drivers/iio/industrialio-buffer.c ++++ b/drivers/iio/industrialio-buffer.c +@@ -119,8 +119,8 @@ static ssize_t iio_scan_el_show(struct device *dev, + int ret; + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + +- ret = test_bit(to_iio_dev_attr(attr)->address, +- indio_dev->buffer->scan_mask); ++ ret = abs(test_bit(to_iio_dev_attr(attr)->address, ++ indio_dev->buffer->scan_mask)); + + return sprintf(buf, "%d\n", ret); + } +@@ -762,7 +762,7 @@ int iio_scan_mask_query(struct iio_dev *indio_dev, + if (!buffer->scan_mask) + return 0; + +- return test_bit(bit, buffer->scan_mask); ++ return abs(test_bit(bit, buffer->scan_mask)); + }; + EXPORT_SYMBOL_GPL(iio_scan_mask_query); + +diff --git a/drivers/isdn/mISDN/core.c b/drivers/isdn/mISDN/core.c +index 3e24571..da30c5c 100644 +--- a/drivers/isdn/mISDN/core.c ++++ b/drivers/isdn/mISDN/core.c +@@ -168,13 +168,13 @@ static struct class mISDN_class = { + }; + + static int +-_get_mdevice(struct device *dev, void *id) ++_get_mdevice(struct device *dev, const void *id) + { + struct mISDNdevice *mdev = dev_to_mISDN(dev); + + if (!mdev) + return 0; +- if (mdev->id != *(u_int *)id) ++ if (mdev->id != *(const u_int *)id) + return 0; + return 1; + } +diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig +index ff553ba..be79f4a 100644 +--- a/drivers/mfd/Kconfig ++++ b/drivers/mfd/Kconfig +@@ -907,6 +907,44 @@ config MFD_TIMBERDALE + The timberdale FPGA can be found on the Intel Atom development board + for in-vehicle infontainment, called Russellville. + ++config CY8C9540A ++ tristate "Cypress CY8C9540 GPIO/PWM expander" ++ depends on INTEL_CLN_GIP ++ depends on PWM ++ help ++ I/O expander providing GPIO/PWM functionality. ++ This is Clanton-specific for now. ++ ++config INTEL_CLN_GIP ++ tristate "Intel Clanton GIP" ++ depends on PCI && X86 && INTEL_QUARK_X1000_SOC ++ depends on I2C ++ select GENERIC_IRQ_CHIP ++ help ++ GIP driver for Clanton SoC. ++ Clanton GIP is a single PCI function exporting a GPIO and an I2C ++ controller, namely Synopsys DesignWare GPIO and Synopsys DesignWare ++ I2C. The GPIO interface exports a total amount of 8 interrupt-capable ++ GPIOs. ++ ++config INTEL_CLN_GIP_TEST ++ tristate "Intel Clanton GIP support for Integration Testing" ++ depends on INTEL_CLN_GIP ++ depends on INTEL_QUARK_X1000_SOC_FPGAEMU || INTEL_QUARK_X1000_SOC_SVP ++ select I2C_CHARDEV ++ select GPIO_SYSFS ++ select SPI ++ select SPI_BITBANG ++ select SPI_GPIO ++ select SPI_MASTER ++ select SPI_SPIDEV ++ help ++ Clanton GIP automated Integration Testing package. ++ It selects kernel components needed for GPIO and I2C tests as per ++ Integration Test Specification, and it also adds a kernel-space ++ facility for testing the GPIO. ++ Note this module is also used to test the Clanton Legacy GPIO. ++ + config LPC_SCH + tristate "Intel SCH LPC" + depends on PCI +diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile +index 8b977f8..2fb8008 100644 +--- a/drivers/mfd/Makefile ++++ b/drivers/mfd/Makefile +@@ -123,6 +123,13 @@ obj-$(CONFIG_MFD_DB8500_PRCMU) += db8500-prcmu.o + obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o + obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o + obj-$(CONFIG_PMIC_ADP5520) += adp5520.o ++obj-$(CONFIG_CY8C9540A) += cy8c9540a.o ++obj-$(CONFIG_INTEL_CLN_GIP) += intel_cln_gip.o ++intel_cln_gip-objs := intel_cln_gip_core.o \ ++ intel_cln_gip_gpio.o \ ++ intel_cln_gip_i2c.o \ ++ ../i2c/busses/i2c-designware-core.o ++obj-$(CONFIG_INTEL_CLN_GIP_TEST)+=intel_cln_gip_test.o + obj-$(CONFIG_LPC_SCH) += lpc_sch.o + obj-$(CONFIG_LPC_ICH) += lpc_ich.o + obj-$(CONFIG_MFD_RDC321X) += rdc321x-southbridge.o +diff --git a/drivers/mfd/cy8c9540a.c b/drivers/mfd/cy8c9540a.c +new file mode 100644 +index 0000000..9d83e32 +--- /dev/null ++++ b/drivers/mfd/cy8c9540a.c +@@ -0,0 +1,1022 @@ ++/* ++ * Copyright(c) 2013 Intel Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ */ ++ ++/* ++ * Driver for Cypress CY8C9540A I/O Expander and PWM ++ * ++ * Izmir-specific. ++ * Based on gpio-adp5588. ++ */ ++ ++#include <linux/delay.h> ++#include <linux/i2c.h> ++#include <linux/interrupt.h> ++#include <linux/irq.h> ++#include <linux/gpio.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/pwm.h> ++#include <linux/slab.h> ++ ++#define DRV_NAME "cy8c9540a" ++ ++/* CY8C9540A settings */ ++#define NGPIO 40 ++#define NPWM 8 ++#define PWM_MAX_PERIOD 0xff ++#define DEVID_FAMILY_CY8C9540A 0x40 ++#define DEVID_FAMILY_MASK 0xf0 ++#define NPORTS 6 ++#define I2C_A0_ADDR_MASK 0x0001 ++#define POR_SETTINGS_LEN 147 ++ ++/* Register offset */ ++#define REG_INPUT_PORT0 0x00 ++#define REG_OUTPUT_PORT0 0x08 ++#define REG_INTR_STAT_PORT0 0x10 ++#define REG_PORT_SELECT 0x18 ++#define REG_INTR_MASK 0x19 ++#define REG_SELECT_PWM 0x1a ++#define REG_PIN_DIR 0x1c ++#define REG_DRIVE_PULLUP 0x1d ++#define REG_PWM_SELECT 0x28 ++#define REG_PWM_CLK 0x29 ++#define REG_PWM_PERIOD 0x2a ++#define REG_PWM_PULSE_W 0x2b ++#define REG_ENABLE 0x2d ++#define REG_DEVID_STAT 0x2e ++#define REG_CMD 0x30 ++ ++/* Commands */ ++#define CMD_W_EEPROM_POR 0x03 ++#define CMD_R_EEPROM_POR 0x04 ++#define CMD_RECONF_DEV 0x07 ++ ++/* Galileo-specific POR default settings */ ++#define POR_CMD_W_OFFS 2 ++static u8 por_default[POR_SETTINGS_LEN + POR_CMD_W_OFFS] = { ++ REG_CMD, CMD_W_EEPROM_POR, ++ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* Output */ ++ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* Interrupt mask */ ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* PWM */ ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Inversion */ ++ 0xe0, 0xe0, 0xff, 0xf3, 0x00, 0xff, 0xff, 0xff, /* Direction */ ++ 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, /* Port0 drive mode */ ++ 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, /* Port1 drive mode */ ++ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Port2 drive mode */ ++ 0xf3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, /* Port3 drive mode */ ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, /* Port4 drive mode */ ++ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Port5 drive mode */ ++ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Port6 drive mode */ ++ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Port7 drive mode */ ++ 0x00, 0xff, 0x00, /* PWM0 */ ++ 0x00, 0xff, 0x00, /* PWM1 */ ++ 0x00, 0xff, 0x00, /* PWM2 */ ++ 0x00, 0xff, 0x00, /* PWM3 */ ++ 0x00, 0xff, 0x00, /* PWM4 */ ++ 0x00, 0xff, 0x00, /* PWM5 */ ++ 0x00, 0xff, 0x00, /* PWM6 */ ++ 0x00, 0xff, 0x00, /* PWM7 */ ++ 0x00, 0xff, 0x00, /* PWM8 */ ++ 0x00, 0xff, 0x00, /* PWM9 */ ++ 0x00, 0xff, 0x00, /* PWM10 */ ++ 0x00, 0xff, 0x00, /* PWM11 */ ++ 0x00, 0xff, 0x00, /* PWM12 */ ++ 0x00, 0xff, 0x00, /* PWM13 */ ++ 0x00, 0xff, 0x00, /* PWM14 */ ++ 0x00, 0xff, 0x00, /* PWM15 */ ++ 0xff, /* PWM CLK divider */ ++ 0x02, /* EEPROM enable */ ++ 0x00 /* CRC (placeholder) */ ++}; ++ ++struct cy8c9540a { ++ struct i2c_client *client; ++ struct gpio_chip gpio_chip; ++ struct pwm_chip pwm_chip; ++ struct mutex lock; ++ /* protect serialized access to the interrupt controller bus */ ++ struct mutex irq_lock; ++ /* cached output registers */ ++ u8 outreg_cache[NPORTS]; ++ /* cached IRQ mask */ ++ u8 irq_mask_cache[NPORTS]; ++ /* IRQ mask to be applied */ ++ u8 irq_mask[NPORTS]; ++ /* Descriptor for raw i2c transactions */ ++ struct i2c_msg i2c_segments[2]; ++ /* POR settings stored in the EEPROM */ ++ u8 por_stored[POR_SETTINGS_LEN]; ++}; ++ ++/* Per-port GPIO offset */ ++static const u8 cy8c9540a_port_offs[] = { ++ 0, ++ 8, ++ 16, ++ 20, ++ 28, ++ 36, ++}; ++ ++/* Galileo-specific data. */ ++ ++#define GPIO_BASE_ID 16 ++#define GPIO_IRQBASE 64 ++#define PWM_BASE_ID 0 ++#define PWM_CLK 0x00 /* see resulting PWM_TCLK_NS */ ++#define PWM_TCLK_NS 31250 /* 32kHz */ ++#define SOC_GPIO_INT_PIN 13 ++#define SOC_GPIO_I2C_A0 7 ++ ++/* PWM-to-GPIO mapping (0 == first Cypress GPIO). */ ++#define PWM_UNUSED -1 ++static const int pwm2gpio_mapping[] = { ++ PWM_UNUSED, ++ 3, ++ PWM_UNUSED, ++ 2, ++ 9, ++ 1, ++ 8, ++ 0, ++}; ++ ++static inline u8 cypress_get_port(unsigned gpio) ++{ ++ u8 i = 0; ++ for (i = 0; i < sizeof(cy8c9540a_port_offs) - 1; i ++) { ++ if (! (gpio / cy8c9540a_port_offs[i + 1])) ++ break; ++ } ++ return i; ++} ++ ++static inline u8 cypress_get_offs(unsigned gpio, u8 port) ++{ ++ return gpio - cy8c9540a_port_offs[port]; ++} ++ ++static int cy8c9540a_gpio_get_value(struct gpio_chip *chip, unsigned gpio) ++{ ++ s32 ret = 0; ++ u8 port = 0; ++ u8 in_reg = 0; ++ struct cy8c9540a *dev = ++ container_of(chip, struct cy8c9540a, gpio_chip); ++ struct i2c_client *client = dev->client; ++ ++ port = cypress_get_port(gpio); ++ in_reg = REG_INPUT_PORT0 + port; ++ ++ ret = i2c_smbus_read_byte_data(client, in_reg); ++ if (ret < 0) { ++ dev_err(&client->dev, "can't read input port%u\n", in_reg); ++ } ++ ++ return !!(ret & BIT(cypress_get_offs(gpio, port))); ++} ++ ++static void cy8c9540a_gpio_set_value(struct gpio_chip *chip, ++ unsigned gpio, int val) ++{ ++ s32 ret = 0; ++ struct cy8c9540a *dev = ++ container_of(chip, struct cy8c9540a, gpio_chip); ++ struct i2c_client *client = dev->client; ++ u8 port = cypress_get_port(gpio); ++ u8 out_reg = REG_OUTPUT_PORT0 + port; ++ ++ mutex_lock(&dev->lock); ++ ++ if (val) { ++ dev->outreg_cache[port] |= BIT(cypress_get_offs(gpio, port)); ++ } else { ++ dev->outreg_cache[port] &= ~BIT(cypress_get_offs(gpio, port)); ++ } ++ ++ ret = i2c_smbus_write_byte_data(client, out_reg, ++ dev->outreg_cache[port]); ++ if (ret < 0) { ++ dev_err(&client->dev, "can't write output port%u\n", port); ++ } ++ ++ mutex_unlock(&dev->lock); ++} ++ ++static int cy8c9540a_gpio_set_drive(struct gpio_chip *chip, unsigned gpio, ++ unsigned mode) ++{ ++ int ret = 0; ++ struct cy8c9540a *dev = ++ container_of(chip, struct cy8c9540a, gpio_chip); ++ struct i2c_client *client = dev->client; ++ u8 port = cypress_get_port(gpio); ++ u8 pin = cypress_get_offs(gpio, port); ++ u8 offs = 0; ++ u8 val = 0; ++ ++ switch(mode) { ++ case GPIOF_DRIVE_PULLUP: ++ offs = 0x0; ++ break; ++ case GPIOF_DRIVE_STRONG: ++ offs = 0x4; ++ break; ++ case GPIOF_DRIVE_HIZ: ++ offs = 0x6; ++ break; ++ default: ++ /* ++ * See databook for alternative modes. This driver won't ++ * support them though. ++ */ ++ return -EINVAL; ++ break; ++ } ++ ++ mutex_lock(&dev->lock); ++ ++ ret = i2c_smbus_write_byte_data(client, REG_PORT_SELECT, port); ++ if (ret < 0) { ++ dev_err(&client->dev, "can't select port %u\n", port); ++ goto end; ++ } ++ ++ ret = i2c_smbus_read_byte_data(client, REG_DRIVE_PULLUP + offs); ++ if (ret < 0) { ++ dev_err(&client->dev, "can't read pin direction\n"); ++ goto end; ++ } ++ ++ val = (u8)(ret | BIT(pin)); ++ ++ ret = i2c_smbus_write_byte_data(client, REG_DRIVE_PULLUP + offs, val); ++ if (ret < 0) { ++ dev_err(&client->dev, "can't set drive mode port %u\n", port); ++ goto end; ++ } ++ ++ ret = 0; ++ ++end: ++ mutex_unlock(&dev->lock); ++ return ret; ++} ++ ++static int cy8c9540a_gpio_direction(struct gpio_chip *chip, unsigned gpio, ++ int out, int val) ++{ ++ s32 ret = 0; ++ u8 pins = 0; ++ struct cy8c9540a *dev = ++ container_of(chip, struct cy8c9540a, gpio_chip); ++ struct i2c_client *client = dev->client; ++ u8 port = cypress_get_port(gpio); ++ ++ ret = cy8c9540a_gpio_set_drive(chip, gpio, out ? ++ GPIOF_DRIVE_STRONG : GPIOF_DRIVE_HIZ); ++ if (ret) { ++ return ret; ++ } ++ ++ mutex_lock(&dev->lock); ++ ++ ret = i2c_smbus_write_byte_data(client, REG_PORT_SELECT, port); ++ if (ret < 0) { ++ dev_err(&client->dev, "can't select port %u\n", port); ++ goto end; ++ } ++ ++ ret = i2c_smbus_read_byte_data(client, REG_PIN_DIR); ++ if (ret < 0) { ++ dev_err(&client->dev, "can't read pin direction\n"); ++ goto end; ++ } ++ ++ pins = (u8)ret & 0xff; ++ if (out) { ++ pins &= ~BIT(cypress_get_offs(gpio, port)); ++ } else { ++ pins |= BIT(cypress_get_offs(gpio, port)); ++ } ++ ++ ret = i2c_smbus_write_byte_data(client, REG_PIN_DIR, pins); ++ if (ret < 0) { ++ dev_err(&client->dev, "can't write pin direction\n"); ++ } ++ ++end: ++ mutex_unlock(&dev->lock); ++ return ret; ++} ++ ++static int cy8c9540a_gpio_direction_output(struct gpio_chip *chip, ++ unsigned gpio, int val) ++{ ++ return cy8c9540a_gpio_direction(chip, gpio, 1, val); ++} ++ ++static int cy8c9540a_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) ++{ ++ return cy8c9540a_gpio_direction(chip, gpio, 0, 0); ++} ++ ++static void cy8c9540a_irq_bus_lock(struct irq_data *d) ++{ ++ struct cy8c9540a *dev = irq_data_get_irq_chip_data(d); ++ mutex_lock(&dev->irq_lock); ++} ++ ++static void cy8c9540a_irq_bus_sync_unlock(struct irq_data *d) ++{ ++ struct cy8c9540a *dev = irq_data_get_irq_chip_data(d); ++ struct i2c_client *client = dev->client; ++ int ret = 0; ++ int i = 0; ++ ++ for (i = 0; i < NPORTS; i++) { ++ if (dev->irq_mask_cache[i] ^ dev->irq_mask[i]) { ++ dev->irq_mask_cache[i] = dev->irq_mask[i]; ++ ret = i2c_smbus_write_byte_data(client, REG_PORT_SELECT, i); ++ if (ret < 0) { ++ dev_err(&client->dev, "can't select port %u\n", i); ++ goto end; ++ } ++ ++ ret = i2c_smbus_write_byte_data(client, REG_INTR_MASK, dev->irq_mask[i]); ++ if (ret < 0) { ++ dev_err(&client->dev, "can't write int mask on port %u\n", i); ++ goto end; ++ } ++ ++ } ++ } ++ ++end: ++ mutex_unlock(&dev->irq_lock); ++} ++ ++static void cy8c9540a_irq_mask(struct irq_data *d) ++{ ++ struct cy8c9540a *dev = irq_data_get_irq_chip_data(d); ++ unsigned gpio = d->irq - GPIO_IRQBASE; ++ u8 port = cypress_get_port(gpio); ++ ++ dev->irq_mask[port] |= BIT(cypress_get_offs(gpio, port)); ++} ++ ++static void cy8c9540a_irq_unmask(struct irq_data *d) ++{ ++ struct cy8c9540a *dev = irq_data_get_irq_chip_data(d); ++ unsigned gpio = d->irq - GPIO_IRQBASE; ++ u8 port = cypress_get_port(gpio); ++ ++ dev->irq_mask[port] &= ~BIT(cypress_get_offs(gpio, port)); ++} ++ ++static int cy8c9540a_gpio_to_irq(struct gpio_chip *chip, unsigned gpio) ++{ ++ return GPIO_IRQBASE + gpio; ++} ++ ++static int cy8c9540a_irq_set_type(struct irq_data *d, unsigned int type) ++{ ++ struct cy8c9540a *dev = irq_data_get_irq_chip_data(d); ++ int ret = 0; ++ ++ if ((type != IRQ_TYPE_EDGE_BOTH)) { ++ dev_err(&dev->client->dev, "irq %d: unsupported type %d\n", ++ d->irq, type); ++ ret = -EINVAL; ++ goto end; ++ } ++ ++end: ++ return ret; ++} ++ ++static struct irq_chip cy8c9540a_irq_chip = { ++ .name = "cy8c9540a-irq", ++ .irq_mask = cy8c9540a_irq_mask, ++ .irq_unmask = cy8c9540a_irq_unmask, ++ .irq_bus_lock = cy8c9540a_irq_bus_lock, ++ .irq_bus_sync_unlock = cy8c9540a_irq_bus_sync_unlock, ++ .irq_set_type = cy8c9540a_irq_set_type, ++}; ++ ++static irqreturn_t cy8c9540a_irq_handler(int irq, void *devid) ++{ ++ struct cy8c9540a *dev = devid; ++ u8 stat[NPORTS], pending = 0; ++ unsigned port = 0, gpio = 0, gpio_irq = 0; ++ int ret = 0; ++ ++ ret = i2c_smbus_read_i2c_block_data(dev->client, REG_INTR_STAT_PORT0, ++ NPORTS, stat); /* W1C */ ++ if (ret < 0) { ++ memset(stat, 0, sizeof(stat)); ++ } ++ ++ ret = IRQ_NONE; ++ ++ for (port = 0; port < NPORTS; port ++) { ++ /* Databook doesn't specify if this is a post-mask status ++ register or not. Consider it 'raw' for safety. */ ++ mutex_lock(&dev->irq_lock); ++ pending = stat[port] & (~dev->irq_mask[port]); ++ mutex_unlock(&dev->irq_lock); ++ ++ while (pending) { ++ ret = IRQ_HANDLED; ++ gpio = __ffs(pending); ++ pending &= ~BIT(gpio); ++ gpio_irq = GPIO_IRQBASE + cy8c9540a_port_offs[port] ++ + gpio; ++ handle_nested_irq(gpio_irq); ++ } ++ } ++ ++ return ret; ++} ++ ++static int cy8c9540a_irq_setup(struct cy8c9540a *dev) ++{ ++ struct i2c_client *client = dev->client; ++ u8 dummy[NPORTS]; ++ unsigned gpio = 0; ++ int ret = 0; ++ int i = 0; ++ ++ mutex_init(&dev->irq_lock); ++ ++ /* Clear interrupt state */ ++ ++ ret = i2c_smbus_read_i2c_block_data(dev->client, REG_INTR_STAT_PORT0, ++ NPORTS, dummy); /* W1C */ ++ if (ret < 0) { ++ dev_err(&client->dev, "couldn't clear int status\n"); ++ goto err; ++ } ++ ++ /* Initialise interrupt mask */ ++ ++ memset(dev->irq_mask_cache, 0xff, sizeof(dev->irq_mask_cache)); ++ memset(dev->irq_mask, 0xff, sizeof(dev->irq_mask)); ++ for (i = 0; i < NPORTS; i++) { ++ ret = i2c_smbus_write_byte_data(client, REG_PORT_SELECT, i); ++ if (ret < 0) { ++ dev_err(&client->dev, "can't select port %u\n", i); ++ goto err; ++ } ++ ++ ret = i2c_smbus_write_byte_data(client, REG_INTR_MASK, dev->irq_mask[i]); ++ if (ret < 0) { ++ dev_err(&client->dev, "can't write int mask on port %u\n", i); ++ goto err; ++ } ++ } ++ ++ /* Allocate external interrupt GPIO pin */ ++ ++ ret = gpio_request(SOC_GPIO_INT_PIN, "cy8c9540a-int"); ++ if (ret) { ++ dev_err(&client->dev, "failed to request gpio%u\n", ++ SOC_GPIO_INT_PIN); ++ goto err; ++ } ++ ++ /* Allocate IRQ descriptors for Cypress GPIOs and set handler */ ++ ++ ret = irq_alloc_descs(GPIO_IRQBASE, GPIO_IRQBASE, NGPIO, 0); ++ if (ret < 0) { ++ dev_err(&client->dev, "failed to allocate IRQ numbers\n"); ++ goto err_free_gpio; ++ } ++ ++ for (gpio = 0; gpio < NGPIO; gpio++) { ++ int irq = gpio + GPIO_IRQBASE; ++ irq_set_chip_data(irq, dev); ++ irq_set_chip_and_handler(irq, &cy8c9540a_irq_chip, ++ handle_edge_irq); ++ irq_set_nested_thread(irq, 1); ++ irq_set_noprobe(irq); ++ } ++ ++ ret = request_threaded_irq(gpio_to_irq(SOC_GPIO_INT_PIN), ++ NULL, ++ cy8c9540a_irq_handler, ++ IRQF_TRIGGER_FALLING | IRQF_ONESHOT, ++ dev_name(&client->dev), dev); ++ if (ret) { ++ dev_err(&client->dev, "failed to request irq %d\n", ++ client->irq); ++ goto err_free_irq_descs; ++ } ++ ++ return 0; ++ ++err_free_irq_descs: ++ irq_free_descs(GPIO_IRQBASE, NGPIO); ++err_free_gpio: ++ gpio_free(SOC_GPIO_INT_PIN); ++err: ++ mutex_destroy(&dev->irq_lock); ++ return ret; ++} ++ ++static void cy8c9540a_irq_teardown(struct cy8c9540a *dev) ++{ ++ mutex_destroy(&dev->irq_lock); ++ irq_free_descs(GPIO_IRQBASE, NGPIO); ++ free_irq(gpio_to_irq(SOC_GPIO_INT_PIN), dev); ++ gpio_free(SOC_GPIO_INT_PIN); ++} ++ ++static int cy8c9540a_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ++ int duty_ns, int period_ns) ++{ ++ int ret = 0; ++ int period = 0, duty = 0; ++ struct cy8c9540a *dev = ++ container_of(chip, struct cy8c9540a, pwm_chip); ++ struct i2c_client *client = dev->client; ++ ++ if (pwm->pwm > NPWM) { ++ return -EINVAL; ++ } ++ ++ period = period_ns / PWM_TCLK_NS; ++ duty = duty_ns / PWM_TCLK_NS; ++ ++ /* ++ * Check period's upper bound. Note the duty cycle is already sanity ++ * checked by the PWM framework. ++ */ ++ if (period > PWM_MAX_PERIOD) { ++ dev_err(&client->dev, "period must be within [0-%d]ns\n", ++ PWM_MAX_PERIOD * PWM_TCLK_NS); ++ return -EINVAL; ++ } ++ ++ mutex_lock(&dev->lock); ++ ++ ret = i2c_smbus_write_byte_data(client, REG_PWM_SELECT, (u8)pwm->pwm); ++ if (ret < 0) { ++ dev_err(&client->dev, "can't write to REG_PWM_SELECT\n"); ++ goto end; ++ } ++ ++ ret = i2c_smbus_write_byte_data(client, REG_PWM_PERIOD, (u8)period); ++ if (ret < 0) { ++ dev_err(&client->dev, "can't write to REG_PWM_PERIOD\n"); ++ goto end; ++ } ++ ++ ret = i2c_smbus_write_byte_data(client, REG_PWM_PULSE_W, (u8)duty); ++ if (ret < 0) { ++ dev_err(&client->dev, "can't write to REG_PWM_PULSE_W\n"); ++ goto end; ++ } ++ ++end: ++ mutex_unlock(&dev->lock); ++ ++ return ret; ++} ++ ++static int cy8c9540a_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) ++{ ++ int ret = 0; ++ int gpio = 0; ++ int port = 0, pin = 0; ++ u8 out_reg = 0; ++ u8 val = 0; ++ struct cy8c9540a *dev = ++ container_of(chip, struct cy8c9540a, pwm_chip); ++ struct i2c_client *client = dev->client; ++ ++ if (pwm->pwm > NPWM) { ++ return -EINVAL; ++ } ++ ++ gpio = pwm2gpio_mapping[pwm->pwm]; ++ port = cypress_get_port(gpio); ++ pin = cypress_get_offs(gpio, port); ++ out_reg = REG_OUTPUT_PORT0 + port; ++ ++ /* Set pin as output driving high */ ++ ret = cy8c9540a_gpio_direction(&dev->gpio_chip, gpio, 1, 1); ++ if (ret < 0) { ++ dev_err(&client->dev, "can't set pwm%u as output\n", pwm->pwm); ++ return ret; ++ } ++ ++ mutex_lock(&dev->lock); ++ ++ /* Enable PWM */ ++ val = i2c_smbus_read_byte_data(client, REG_SELECT_PWM); ++ if (val < 0) { ++ dev_err(&client->dev, "can't read REG_SELECT_PWM\n"); ++ ret = val; ++ goto end; ++ } ++ val |= BIT((u8)pin); ++ ret = i2c_smbus_write_byte_data(client, REG_SELECT_PWM, val); ++ if (ret < 0) { ++ dev_err(&client->dev, "can't write to SELECT_PWM\n"); ++ goto end; ++ } ++ ++end: ++ mutex_unlock(&dev->lock); ++ ++ return ret; ++} ++ ++static void cy8c9540a_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) ++{ ++ int ret = 0; ++ int gpio = 0; ++ int port = 0, pin = 0; ++ u8 val = 0; ++ struct cy8c9540a *dev = ++ container_of(chip, struct cy8c9540a, pwm_chip); ++ struct i2c_client *client = dev->client; ++ ++ if (pwm->pwm > NPWM) { ++ return; ++ } ++ ++ gpio = pwm2gpio_mapping[pwm->pwm]; ++ if (PWM_UNUSED == gpio) { ++ dev_err(&client->dev, "pwm%d is unused\n", pwm->pwm); ++ return; ++ } ++ ++ port = cypress_get_port(gpio); ++ pin = cypress_get_offs(gpio, port); ++ ++ mutex_lock(&dev->lock); ++ ++ /* Disable PWM */ ++ val = i2c_smbus_read_byte_data(client, REG_SELECT_PWM); ++ if (val < 0) { ++ dev_err(&client->dev, "can't read REG_SELECT_PWM\n"); ++ goto end; ++ } ++ val &= ~BIT((u8)pin); ++ ret = i2c_smbus_write_byte_data(client, REG_SELECT_PWM, val); ++ if (ret < 0) { ++ dev_err(&client->dev, "can't write to SELECT_PWM\n"); ++ } ++ ++end: ++ mutex_unlock(&dev->lock); ++ ++ return; ++} ++ ++/* ++ * Some PWMs are unavailable on Galileo. Prevent user from reserving them. ++ */ ++static int cy8c9540a_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) ++{ ++ int gpio = 0; ++ struct cy8c9540a *dev = ++ container_of(chip, struct cy8c9540a, pwm_chip); ++ struct i2c_client *client = dev->client; ++ ++ if (pwm->pwm > NPWM) { ++ return -EINVAL; ++ } ++ ++ gpio = pwm2gpio_mapping[pwm->pwm]; ++ if (PWM_UNUSED == gpio) { ++ dev_err(&client->dev, "pwm%d unavailable\n", pwm->pwm); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static const struct pwm_ops cy8c9540a_pwm_ops = { ++ .request = cy8c9540a_pwm_request, ++ .config = cy8c9540a_pwm_config, ++ .enable = cy8c9540a_pwm_enable, ++ .disable = cy8c9540a_pwm_disable, ++}; ++ ++/* ++ * cy8c9540a_set_por_default ++ * ++ * Ensure the expander is using platform-specific POR settings. ++ * ++ * Note SMBUS max transaction length is 32 bytes, so we have to fall back to ++ * raw i2c transfers. ++ */ ++static int cy8c9540a_set_por_default(struct cy8c9540a *dev) ++{ ++ int ret = 0; ++ struct i2c_client *client = dev->client; ++ int i = 0; ++ int segments = -1; ++ int crc_index = sizeof(por_default) - 1; ++ u8 reg_cmd_r_por[] = { REG_CMD, CMD_R_EEPROM_POR }; ++ ++ /* Read POR settings stored in EEPROM */ ++ dev->i2c_segments[0].addr = client->addr; ++ dev->i2c_segments[0].flags = 0; /* write */ ++ dev->i2c_segments[0].len = sizeof(reg_cmd_r_por); ++ dev->i2c_segments[0].buf = reg_cmd_r_por; ++ dev->i2c_segments[1].addr = client->addr; ++ dev->i2c_segments[1].flags = I2C_M_RD; ++ dev->i2c_segments[1].len = sizeof(dev->por_stored); ++ dev->i2c_segments[1].buf = dev->por_stored; ++ segments = 2; ++ ret = i2c_transfer(client->adapter, dev->i2c_segments, segments); ++ if (segments != ret) { ++ dev_err(&client->dev, "can't read POR settings (ret=%d)\n", ret); ++ goto end; ++ } else { ++ ret = 0; ++ } ++ ++ /* Compute CRC for platform-defined POR settings */ ++ por_default[crc_index] = 0; ++ for (i = POR_CMD_W_OFFS; i < crc_index; i ++) { ++ por_default[crc_index] ^= por_default[i]; ++ } ++ ++ /* Compare POR settings with platform-defined ones */ ++ for (i = 0; i < sizeof(dev->por_stored); i ++) { ++ if (dev->por_stored[i] != por_default[i + POR_CMD_W_OFFS]) { ++ break; ++ } ++ } ++ if (sizeof(dev->por_stored) == i) { ++ goto end; ++ } ++ ++ /* Update POR settings to EEPROM */ ++ ++ dev_info(&client->dev, "updating EEPROM with platform POR settings\n"); ++ ++ /* Store default POR settings into EEPROM */ ++ dev->i2c_segments[0].addr = client->addr; ++ dev->i2c_segments[0].flags = 0; /* write */ ++ dev->i2c_segments[0].len = sizeof(por_default); ++ dev->i2c_segments[0].buf = por_default; ++ segments = 1; ++ ret = i2c_transfer(client->adapter, dev->i2c_segments, segments); ++ if (segments != ret) { ++ dev_err(&client->dev, "can't write POR settings (ret=%d)\n", ret); ++ goto end; ++ } else { ++ ret = 0; ++ } ++ ++ /* Let EEPROM terminate its last page write. 200ms as per datasheet. */ ++ mdelay(200); ++ ++ /* Reconfigure device with newly stored POR settings */ ++ ret = i2c_smbus_write_byte_data(client, REG_CMD, CMD_RECONF_DEV); ++ if (ret < 0) { ++ dev_err(&client->dev, "can't reconfigure device\n"); ++ goto end; ++ } ++ ++end: ++ return ret; ++} ++ ++/* ++ * cy8c9540a_setup ++ * ++ * Initialise the device with default setup. ++ * No need to roll back if this fails. ++ */ ++static int cy8c9540a_setup(struct cy8c9540a *dev) ++{ ++ int ret = 0; ++ struct i2c_client *client = dev->client; ++ const u8 eeprom_enable_seq[] = {0x43, 0x4D, 0x53, 0x2}; ++ ++ /* Test/set platform-specific POR settings */ ++ ret = cy8c9540a_set_por_default(dev); ++ if (ret) { ++ dev_err(&client->dev, "can't set POR settings (err=%d)\n", ret); ++ goto end; ++ } ++ ++ /* Cache the output registers */ ++ ret = i2c_smbus_read_i2c_block_data(dev->client, REG_OUTPUT_PORT0, ++ sizeof(dev->outreg_cache), ++ dev->outreg_cache); ++ if (ret < 0) { ++ dev_err(&client->dev, "can't cache output registers\n"); ++ goto end; ++ } ++ ++ /* Enable the EEPROM */ ++ ret = i2c_smbus_write_i2c_block_data(client, REG_ENABLE, ++ sizeof(eeprom_enable_seq), ++ eeprom_enable_seq); ++ if (ret < 0) { ++ dev_err(&client->dev, "can't enable EEPROM\n"); ++ goto end; ++ } ++ ++end: ++ return ret; ++} ++ ++static int cy8c9540a_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct cy8c9540a *dev; ++ struct gpio_chip *gc; ++ int ret = 0; ++ s32 dev_id = 0; ++ ++ ret = gpio_request(SOC_GPIO_I2C_A0, "cy8c9540a-addr0"); ++ if (ret) { ++ pr_err("%s: failed to request gpio%u\n", __func__, ++ SOC_GPIO_I2C_A0); ++ return ret; ++ } ++ ++ /* ++ * Galileo uses A0 Extendable Soft Addressing pin on the Cypress part. ++ * The inverted value of A0 is exposed to a SoC GPIO. ++ * ++ * Work out the I2C address of the device based on A0. ++ */ ++ if (gpio_get_value(SOC_GPIO_I2C_A0)) { ++ client->addr &= ~I2C_A0_ADDR_MASK; ++ } else { ++ client->addr |= I2C_A0_ADDR_MASK; ++ } ++ ++ if (!i2c_check_functionality(client->adapter, ++ I2C_FUNC_I2C | ++ I2C_FUNC_SMBUS_I2C_BLOCK | ++ I2C_FUNC_SMBUS_BYTE_DATA)) { ++ dev_err(&client->dev, "i2c adapter doesn't support required " ++ "functionality\n"); ++ ret = -EIO; ++ goto err_i2c_addr; ++ } ++ ++ dev = kzalloc(sizeof(*dev), GFP_KERNEL); ++ if (dev == NULL) { ++ dev_err(&client->dev, "failed to alloc memory\n"); ++ ret = -ENOMEM; ++ goto err_i2c_addr; ++ } ++ ++ dev->client = client; ++ ++ gc = &dev->gpio_chip; ++ gc->direction_input = cy8c9540a_gpio_direction_input; ++ gc->direction_output = cy8c9540a_gpio_direction_output; ++ gc->get = cy8c9540a_gpio_get_value; ++ gc->set = cy8c9540a_gpio_set_value; ++ gc->set_drive = cy8c9540a_gpio_set_drive; ++ ++ gc->can_sleep = 1; ++ ++ gc->base = GPIO_BASE_ID; ++ gc->ngpio = NGPIO; ++ gc->label = client->name; ++ gc->owner = THIS_MODULE; ++ gc->to_irq = cy8c9540a_gpio_to_irq; ++ ++ mutex_init(&dev->lock); ++ ++ /* Whoami */ ++ dev_id = i2c_smbus_read_byte_data(client, REG_DEVID_STAT); ++ if (dev_id < 0) { ++ dev_err(&client->dev, "can't read device ID\n"); ++ ret = dev_id; ++ goto err; ++ } ++ dev_info(&client->dev, "dev_id=0x%x\n", dev_id & 0xff); ++ if (DEVID_FAMILY_CY8C9540A != (dev_id & DEVID_FAMILY_MASK)) { ++ dev_err(&client->dev, "unknown/unsupported dev_id 0x%x\n", ++ dev_id & 0xff); ++ ret = -ENODEV; ++ goto err; ++ } ++ ++ ret = cy8c9540a_setup(dev); ++ if (ret) { ++ goto err; ++ } ++ ++ ret = cy8c9540a_irq_setup(dev); ++ if (ret) { ++ goto err; ++ } ++ ret = gpiochip_add(&dev->gpio_chip); ++ if (ret) { ++ dev_err(&client->dev, "gpiochip_add failed %d\n", ret); ++ goto err_irq; ++ } ++ ++ dev->pwm_chip.dev = &client->dev; ++ dev->pwm_chip.ops = &cy8c9540a_pwm_ops; ++ dev->pwm_chip.base = PWM_BASE_ID; ++ dev->pwm_chip.npwm = NPWM; ++ ++ ret = pwmchip_add(&dev->pwm_chip); ++ if (ret) { ++ dev_err(&client->dev, "pwmchip_add failed %d\n", ret); ++ goto err_gpiochip; ++ } ++ ++ i2c_set_clientdata(client, dev); ++ ++ return 0; ++ ++err_gpiochip: ++ if(gpiochip_remove(&dev->gpio_chip)) ++ dev_warn(&client->dev, "gpiochip_remove failed\n"); ++err_irq: ++ cy8c9540a_irq_teardown(dev); ++err: ++ mutex_destroy(&dev->lock); ++ kfree(dev); ++err_i2c_addr: ++ gpio_free(SOC_GPIO_I2C_A0); ++ ++ return ret; ++} ++ ++static int cy8c9540a_remove(struct i2c_client *client) ++{ ++ struct cy8c9540a *dev = i2c_get_clientdata(client); ++ int ret = 0; ++ int err = 0; ++ ++ ret = pwmchip_remove(&dev->pwm_chip); ++ if (ret < 0) { ++ dev_err(&client->dev, "pwmchip_remove failed %d\n", ret); ++ err = ret; ++ } ++ ++ ret = gpiochip_remove(&dev->gpio_chip); ++ if (ret) { ++ dev_err(&client->dev, "gpiochip_remove failed %d\n", ret); ++ err = ret; ++ } ++ ++ cy8c9540a_irq_teardown(dev); ++ ++ mutex_destroy(&dev->lock); ++ kfree(dev); ++ ++ gpio_free(SOC_GPIO_I2C_A0); ++ ++ return err; ++} ++ ++static const struct i2c_device_id cy8c9540a_id[] = { ++ {DRV_NAME, 0}, ++ {} ++}; ++ ++MODULE_DEVICE_TABLE(i2c, cy8c9540a_id); ++ ++static struct i2c_driver cy8c9540a_driver = { ++ .driver = { ++ .name = DRV_NAME, ++ }, ++ .probe = cy8c9540a_probe, ++ .remove = cy8c9540a_remove, ++ .id_table = cy8c9540a_id, ++}; ++ ++module_i2c_driver(cy8c9540a_driver); ++ ++MODULE_AUTHOR("Josef Ahmad <josef.ahmad@linux.intel.com>"); ++MODULE_DESCRIPTION("GPIO/PWM driver for CY8C9540A I/O expander"); ++MODULE_LICENSE("GPL"); ++ +diff --git a/drivers/mfd/intel_cln_gip.h b/drivers/mfd/intel_cln_gip.h +new file mode 100644 +index 0000000..18ab774 +--- /dev/null ++++ b/drivers/mfd/intel_cln_gip.h +@@ -0,0 +1,101 @@ ++/* ++ * Copyright(c) 2013 Intel Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ */ ++/* ++ * Intel Clanton GIP (GPIO/I2C) driver ++ */ ++ ++#ifndef __INTEL_CLNGIP_H__ ++#define __INTEL_CLNGIP_H__ ++ ++#include <linux/i2c.h> ++#include <linux/pci.h> ++#include "../i2c/busses/i2c-designware-core.h" ++ ++/* PCI BAR for register base address */ ++#define GIP_I2C_BAR 0 ++#define GIP_GPIO_BAR 1 ++ ++/** ++ * intel_cln_gpio_probe ++ * ++ * @param pdev: Pointer to GIP PCI device ++ * @return 0 success < 0 failure ++ * ++ * Perform GPIO-specific probing on behalf of the top-level GIP driver. ++ */ ++int intel_cln_gpio_probe(struct pci_dev *pdev); ++ ++/** ++ * intel_cln_gpio_remove ++ * ++ * @param pdev: Pointer to GIP PCI device ++ * ++ * Perform GPIO-specific resource release on behalf of the top-level GIP driver. ++ */ ++void intel_cln_gpio_remove(struct pci_dev *pdev); ++ ++/** ++ * intel_cln_gpio_isr ++ * ++ * @param irq: IRQ number to be served ++ * @param dev_id: used to distinguish the device (for shared interrupts) ++ * ++ * Perform GPIO-specific ISR of the top-level GIP driver. ++ */ ++irqreturn_t intel_cln_gpio_isr(int irq, void *dev_id); ++ ++/** ++ * intel_cln_gpio_save_state ++ * ++ * Save GPIO register state for system-wide suspend events and mask out ++ * interrupts. ++ */ ++void intel_cln_gpio_save_state(void); ++ ++/** ++ * intel_cln_gpio_restore_state ++ * ++ * Restore GPIO register state for system-wide resume events and clear out ++ * spurious interrupts. ++ */ ++void intel_cln_gpio_restore_state(void); ++ ++/** ++ * intel_cln_i2c_probe ++ * @param pdev: Pointer to GIP PCI device ++ * @param drvdata: private driver data ++ * @return 0 success < 0 failure ++ * ++ * Perform I2C-specific probing on behalf of the top-level GIP driver. ++ */ ++int intel_cln_i2c_probe(struct pci_dev *pdev, ++ struct dw_i2c_dev **drvdata); ++ ++/** ++ * intel_cln_i2c_remove ++ * @param pdev: Pointer to GIP PCI device ++ * @param dev: Pointer to I2C private data ++ * ++ * Perform I2C-specific resource release on behalf of the top-level GIP driver. ++ */ ++void intel_cln_i2c_remove(struct pci_dev *pdev, ++ struct dw_i2c_dev *dev); ++ ++#endif /* __INTEL_CLNGIP_H__ */ +diff --git a/drivers/mfd/intel_cln_gip_core.c b/drivers/mfd/intel_cln_gip_core.c +new file mode 100644 +index 0000000..7464b32 +--- /dev/null ++++ b/drivers/mfd/intel_cln_gip_core.c +@@ -0,0 +1,327 @@ ++/* ++ * Copyright(c) 2013 Intel Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ */ ++/* ++ * Intel Clanton GIP (GPIO/I2C) PCI driver ++ * ++ * PCI glue logic for Clanton GIP driver. ++ * Clanton GIP is a single PCI function exporting a GPIO and an I2C device. ++ * The PCI driver performs the bus-dependent probe/release operations, and ++ * call into GPIO/I2C specific modules for handling the two diffrerent ++ * functionalities. ++ */ ++ ++#include <asm/cln.h> ++#include <linux/errno.h> ++#include <linux/gpio.h> ++#include <linux/i2c.h> ++#include <linux/init.h> ++#include <linux/interrupt.h> ++#include <linux/irq.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/pci.h> ++#include "intel_cln_gip.h" ++ ++static unsigned int enable_msi = 1; ++module_param(enable_msi, uint, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(enable_msi, "Enable PCI MSI mode"); ++ ++static unsigned int i2c = 1; ++module_param(i2c, uint, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(i2c, "Register I2C adapter"); ++ ++static unsigned int gpio = 1; ++module_param(gpio, uint, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(gpio, "Register GPIO chip"); ++ ++/* GIP private data, supporting only a single instance of the device. */ ++struct intel_cln_gip_data { ++ struct pci_dev *pci_device; ++ struct dw_i2c_dev *i2c_drvdata; ++}; ++ ++/** ++ * intel_cln_gip_handler ++ * ++ * @param irq: IRQ number to be served ++ * @param dev_id: device private data ++ * ++ * Top-level interrupt handler for GIP driver. ++ * It calls into the appropriate sub-routines and gathers the return values. ++ */ ++static irqreturn_t intel_cln_gip_handler(int irq, void *dev_id) ++{ ++ irqreturn_t ret_i2c = IRQ_NONE; ++ irqreturn_t ret_gpio = IRQ_NONE; ++ struct intel_cln_gip_data *data = (struct intel_cln_gip_data *)dev_id; ++ ++ mask_pvm(data->pci_device); ++ ++ if (likely(i2c)) { ++ /* Only I2C gets platform data */ ++ ret_i2c = i2c_dw_isr(irq, data->i2c_drvdata); ++ } ++ ++ if (likely(gpio)) { ++ ret_gpio = intel_cln_gpio_isr(irq, NULL); ++ } ++ ++ unmask_pvm(data->pci_device); ++ ++ if (likely(IRQ_HANDLED == ret_i2c || IRQ_HANDLED == ret_gpio)) ++ return IRQ_HANDLED; ++ ++ /* Each sub-ISR routine returns either IRQ_HANDLED or IRQ_NONE. */ ++ return IRQ_NONE; ++} ++ ++static DEFINE_PCI_DEVICE_TABLE(intel_cln_gip_ids) = { ++ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0934), }, ++ { 0, } ++}; ++MODULE_DEVICE_TABLE(pci, intel_cln_gip_ids); ++ ++#ifdef CONFIG_PM ++ ++/** ++ * cln_gip_suspend ++ * ++ * @param device: Pointer to device ++ * @return 0 success < 0 failure ++ * ++ * Prepare GIP for system-wide transition to sleep state. ++ * Save context, disable GPIO chip and I2C adapter, transition PCI device into ++ * low-power state. ++ */ ++static int cln_gip_suspend(struct device *dev) ++{ ++ int err = 0; ++ struct intel_cln_gip_data *data = NULL; ++ struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); ++ data = (struct intel_cln_gip_data *)pci_get_drvdata(pdev); ++ ++ i2c_dw_disable(data->i2c_drvdata); ++ intel_cln_gpio_save_state(); ++ ++ err = pci_save_state(pdev); ++ if (err) { ++ dev_err(&pdev->dev, "pci_save_state failed\n"); ++ return err; ++ } ++ ++ err = pci_set_power_state(pdev, PCI_D3hot); ++ if (err) { ++ dev_err(&pdev->dev, "pci_set_power_state failed\n"); ++ return err; ++ } ++ ++ return 0; ++} ++ ++/** ++ * cln_gip_resume ++ * ++ * @param device: Pointer to device ++ * @return 0 success < 0 failure ++ * ++ * Prepare GIP for system-wide transition to fully active state. ++ * Set PCI device into full-power state, restore context, enable I2C adapter ++ * and GPIO chip. ++ */ ++static int cln_gip_resume(struct device *dev) ++{ ++ int err = 0; ++ struct intel_cln_gip_data *data = NULL; ++ struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); ++ data = (struct intel_cln_gip_data *)pci_get_drvdata(pdev); ++ ++ err = pci_set_power_state(pdev, PCI_D0); ++ if (err) { ++ dev_err(&pdev->dev, "pci_set_power_state() failed\n"); ++ return err; ++ } ++ ++ pci_restore_state(pdev); ++ ++ intel_cln_gpio_restore_state(); ++ i2c_dw_init(data->i2c_drvdata); ++ ++ return 0; ++} ++ ++#else ++#define cln_gip_suspend NULL ++#define cln_gip_resume NULL ++#endif ++ ++static const struct dev_pm_ops cln_gip_pm_ops = { ++ .resume = cln_gip_resume, ++ .suspend = cln_gip_suspend, ++}; ++ ++/** ++ * intel_cln_gip_probe ++ * ++ * @param pdev: Pointer to GIP PCI device ++ * @param id: GIP PCI Device ID ++ * @return 0 success < 0 failure ++ * ++ * GIP PCI driver probing. Calls into the appropriate probe routines for GPIO ++ * and I2C too. ++ */ ++static int intel_cln_gip_probe(struct pci_dev *pdev, ++ const struct pci_device_id *id) ++{ ++ int retval = 0; ++ struct intel_cln_gip_data *gip_drvdata = NULL; ++ ++ retval = pci_enable_device(pdev); ++ if (retval) ++ return retval; ++ ++ retval = pci_request_regions(pdev, "cln-gip"); ++ if (retval) { ++ dev_err(&pdev->dev, "error requesting PCI region\n"); ++ goto err_pcidev_disable; ++ } ++ ++ gip_drvdata = kzalloc(sizeof(struct intel_cln_gip_data), GFP_KERNEL); ++ if (NULL == gip_drvdata) { ++ retval = -ENOMEM; ++ goto err_pciregions_release; ++ } ++ pci_set_drvdata(pdev, gip_drvdata); ++ ++ gip_drvdata->pci_device = pdev; ++ ++ if (gpio) { ++ retval = intel_cln_gpio_probe(pdev); ++ if (retval) ++ goto err_release_drvdata; ++ } ++ ++ if (i2c) { ++ retval = intel_cln_i2c_probe(pdev, ++ (struct dw_i2c_dev **)&gip_drvdata->i2c_drvdata); ++ if (retval) ++ goto err_release_drvdata; ++ } ++ ++ if (enable_msi) { ++ pci_set_master(pdev); ++ retval = pci_enable_msi(pdev); ++ if (retval) ++ goto err_release_drvdata; ++ } ++ ++ /* ++ * Request a shared IRQ. ++ * Since the dev_id cannot be NULL, it points to PCI device descriptor ++ * if I2C is not registered. ++ */ ++ retval = request_irq(pdev->irq, intel_cln_gip_handler, IRQF_SHARED, ++ "intel_cln_gip", gip_drvdata); ++ if (retval) { ++ dev_err(&pdev->dev, "error requesting IRQ\n"); ++ goto err; ++ } ++ ++ return 0; ++ ++err_release_drvdata: ++ pci_set_drvdata(pdev, NULL); ++ kfree(gip_drvdata); ++err: ++ if (enable_msi) ++ pci_disable_msi(pdev); ++err_pciregions_release: ++ pci_release_regions(pdev); ++err_pcidev_disable: ++ pci_disable_device(pdev); ++ ++ return retval; ++} ++ ++/** ++ * intel_cln_gip_remove ++ * ++ * @param pdev: Pointer to GIP PCI device ++ * ++ * Release resources. Calls into GPIO/I2C dedicate routines too. ++ */ ++static void intel_cln_gip_remove(struct pci_dev *pdev) ++{ ++ struct intel_cln_gip_data *data = NULL; ++ ++ data = (struct intel_cln_gip_data *)pci_get_drvdata(pdev); ++ ++ if (NULL == data) { ++ dev_err(&pdev->dev, "%s: failure getting driver data\n", ++ __func__); ++ return; ++ } ++ ++ free_irq(pdev->irq, data); ++ ++ if (enable_msi) { ++ pci_clear_master(pdev); ++ if (pci_dev_msi_enabled(pdev)) ++ pci_disable_msi(pdev); ++ } ++ ++ if (i2c) ++ intel_cln_i2c_remove(pdev, data->i2c_drvdata); ++ ++ if (gpio) ++ intel_cln_gpio_remove(pdev); ++ ++ pci_set_drvdata(pdev, NULL); ++ kfree(data); ++ ++ pci_release_regions(pdev); ++ pci_disable_device(pdev); ++} ++ ++static struct pci_driver intel_cln_gip_driver = { ++ .name = "intel_cln_gip", ++ .id_table = intel_cln_gip_ids, ++ .probe = intel_cln_gip_probe, ++ .remove = intel_cln_gip_remove, ++ .driver = { ++ .pm = &cln_gip_pm_ops, ++ }, ++}; ++ ++static int intel_cln_gip_init(void) ++{ ++ return pci_register_driver(&intel_cln_gip_driver); ++} ++ ++static void intel_cln_gip_exit(void) ++{ ++ pci_unregister_driver(&intel_cln_gip_driver); ++} ++ ++module_init(intel_cln_gip_init); ++module_exit(intel_cln_gip_exit); ++ ++MODULE_AUTHOR("Intel Corporation"); ++MODULE_DESCRIPTION("Clanton GIP driver"); ++MODULE_LICENSE("Dual BSD/GPL"); +diff --git a/drivers/mfd/intel_cln_gip_gpio.c b/drivers/mfd/intel_cln_gip_gpio.c +new file mode 100644 +index 0000000..6e2bbbf +--- /dev/null ++++ b/drivers/mfd/intel_cln_gip_gpio.c +@@ -0,0 +1,660 @@ ++/* ++ * Copyright(c) 2013 Intel Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ */ ++/* ++ * Intel Clanton GIP (GPIO/I2C) - GPIO-specific PCI and core driver ++ * ++ * PCI glue logic and core driver for Clanton GIP/GPIO. ++ * The GIP GPIO device is the DesignWare GPIO. This file defines the PCI glue ++ * for this driver and as well as the core logic for the device. ++ * Please note only a single instance of the GPIO device is supported. ++ * The default number of GPIO is 8, all interrupt-capable. ++ */ ++ ++#include <linux/errno.h> ++#include <linux/gpio.h> ++#include <linux/irq.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/pci.h> ++#include <linux/platform_device.h> ++#include <linux/uio_driver.h> ++#include "intel_cln_gip.h" ++ ++static void cln_gpio_restrict_release(struct device *dev) {} ++static struct platform_device cln_gpio_restrict_pdev = ++{ ++ .name = "cln-gpio-restrict-sc", ++ .dev.release = cln_gpio_restrict_release, ++}; ++struct uio_info *info; ++ ++/* The base GPIO number under GPIOLIB framework */ ++#define INTEL_CLN_GIP_GPIO_BASE 8 ++ ++/* The default number of South-Cluster GPIO on Clanton. */ ++#define INTEL_CLN_GIP_NGPIO 8 ++ ++/* ++ * The default base IRQ for searching and allocating the range of GPIO IRQ ++ * descriptors. ++ */ ++#define INTEL_CLN_GIP_GPIO_IRQBASE 56 ++ ++/* The GPIO private data. */ ++static struct gpio_chip *gc; ++static struct irq_chip_generic *igc; ++static void __iomem *reg_base; ++static spinlock_t lock; ++static int irq_base; ++static unsigned int n_gpio = INTEL_CLN_GIP_NGPIO; ++static unsigned int gpio_irqbase = INTEL_CLN_GIP_GPIO_IRQBASE; ++ ++/* Store GPIO context across system-wide suspend/resume transitions */ ++static struct gpio_saved_regs { ++ u32 data; ++ u32 dir; ++ u32 int_en; ++ u32 int_mask; ++ u32 int_type; ++ u32 int_pol; ++ u32 int_deb; ++} saved_regs; ++ ++/* PortA registers set. Note other ports are unused */ ++#define PORTA_DATA 0x00 /* Data */ ++#define PORTA_DIR 0x04 /* Direction */ ++#define PORTA_INT_EN 0x30 /* Interrupt enable */ ++#define PORTA_INT_MASK 0x34 /* Interrupt mask */ ++#define PORTA_INT_TYPE_LEVEL 0x38 /* Interrupt level*/ ++#define PORTA_INT_POLARITY 0x3c /* Interrupt polarity */ ++#define PORTA_INT_STATUS 0x40 /* Interrupt status */ ++#define PORTA_INT_RAW_STATUS 0x44 /* Interrupt raw status */ ++#define PORTA_DEBOUNCE 0x48 /* Debounce enable */ ++#define PORTA_INT_EOI 0x4c /* Clear interrupt */ ++#define PORTA_EXT 0x50 /* External */ ++ ++module_param(n_gpio, uint, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(n_gpio, "Number of GPIO"); ++ ++module_param(gpio_irqbase, uint, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(gpio_irqbase, "Base IRQ for GPIO range"); ++ ++/** ++ * intel_cln_gpio_get ++ * @param chip: Pointer to GPIO chip registered by GPIOLIB ++ * @param offset: the GPIO number within the GPIOLIB chip ++ * @return 0 if GPIO is deasserted, 1 if GPIO is asserted ++ * ++ * Read back the value of a GPIO. ++ */ ++static int intel_cln_gpio_get(struct gpio_chip *chip, unsigned offset) ++{ ++ void __iomem *reg_ext = reg_base + PORTA_EXT; ++ u32 val_ext = ioread32(reg_ext); ++ ++ val_ext &= BIT(offset % 32); ++ return (val_ext > 0); ++} ++ ++/** ++ * intel_cln_gpio_set ++ * @param chip: Pointer to GPIO chip registered by GPIOLIB ++ * @param offset: the GPIO number within the GPIOLIB chip ++ * ++ * Set value of a GPIO. ++ */ ++static void intel_cln_gpio_set(struct gpio_chip *chip, unsigned offset, ++ int value) ++{ ++ void __iomem *reg_data = reg_base + PORTA_DATA; ++ u32 val_data = 0; ++ unsigned long flags = 0; ++ ++ spin_lock_irqsave(&lock, flags); ++ ++ val_data = ioread32(reg_data); ++ if (value) ++ iowrite32(val_data | BIT(offset % 32), reg_data); ++ else ++ iowrite32(val_data & ~BIT(offset % 32), reg_data); ++ ++ spin_unlock_irqrestore(&lock, flags); ++} ++ ++/** ++ * intel_cln_gpio_direction_input ++ * @param chip: Pointer to GPIO chip registered by GPIOLIB ++ * @param offset: the GPIO number within the GPIOLIB chip ++ * @return always 0 (success) ++ * ++ * Set direction of a GPIO as input. ++ */ ++static int intel_cln_gpio_direction_input(struct gpio_chip *chip, ++ unsigned offset) ++{ ++ u32 val_dir = 0; ++ void __iomem *reg_dir = reg_base + PORTA_DIR; ++ unsigned long flags = 0; ++ ++ spin_lock_irqsave(&lock, flags); ++ ++ val_dir = ioread32(reg_dir); ++ iowrite32(val_dir & ~BIT(offset % 32), reg_dir); ++ ++ spin_unlock_irqrestore(&lock, flags); ++ ++ return 0; ++} ++ ++/** ++ * intel_cln_gpio_direction_output ++ * @param chip: Pointer to GPIO chip registered by GPIOLIB ++ * @param offset: the GPIO number within the GPIOLIB chip ++ * @param value: value to be driven to the GPIO ++ * @return always 0 (success) ++ * ++ * Set the default value of a GPIO, and then set direction as output. ++ */ ++static int intel_cln_gpio_direction_output(struct gpio_chip *chip, ++ unsigned offset, int value) ++{ ++ u32 val_dir = 0; ++ void __iomem *reg_dir = reg_base + PORTA_DIR; ++ unsigned long flags = 0; ++ ++ /* Ensure glitch-free operation. */ ++ intel_cln_gpio_set(chip, offset, value); ++ ++ spin_lock_irqsave(&lock, flags); ++ ++ val_dir = ioread32(reg_dir); ++ iowrite32(val_dir | BIT(offset % 32), reg_dir); ++ ++ spin_unlock_irqrestore(&lock, flags); ++ ++ return 0; ++} ++ ++/** ++ * intel_cln_gpio_set_debounce ++ * @param chip: Pointer to GPIO chip registered by GPIOLIB ++ * @param offset: the GPIO number within the GPIOLIB chip ++ * @param debounce: 1 to enable, 0 to disable ++ * @return always 0 (success) ++ * ++ * Enable/disable interrupt debounce logic for a GPIO. ++ */ ++static int intel_cln_gpio_set_debounce(struct gpio_chip *chip, ++ unsigned offset, unsigned debounce) ++{ ++ u32 val_deb = 0; ++ void __iomem *reg_deb = reg_base + PORTA_DEBOUNCE; ++ unsigned long flags = 0; ++ ++ spin_lock_irqsave(&lock, flags); ++ ++ val_deb = ioread32(reg_deb); ++ if (debounce) ++ iowrite32(val_deb | BIT(offset % 32), reg_deb); ++ else ++ iowrite32(val_deb & ~BIT(offset % 32), reg_deb); ++ ++ spin_unlock_irqrestore(&lock, flags); ++ ++ return 0; ++} ++ ++/** ++ * intel_cln_gpio_irq_type ++ * @param irq_data: Pointer to information about the IRQ ++ * @param type: set the triggering type of the interrupt ++ * @return always 0 (success) ++ * ++ * Set interrupt triggering type for a GPIO. ++ */ ++static int intel_cln_gpio_irq_type(struct irq_data *d, unsigned type) ++{ ++ int ret = 0; ++ unsigned long flags = 0; ++ void __iomem *reg_level = reg_base + PORTA_INT_TYPE_LEVEL; ++ void __iomem *reg_pol = reg_base + PORTA_INT_POLARITY; ++ u32 val_level = 0; ++ u32 val_pol = 0; ++ u32 gpio = 0; ++ ++ if (NULL == d) { ++ pr_err("%s(): null irq_data\n", __func__); ++ return -EFAULT; ++ } ++ ++ gpio = d->irq - irq_base; ++ ++ spin_lock_irqsave(&lock, flags); ++ ++ val_level = ioread32(reg_level); ++ val_pol = ioread32(reg_pol); ++ ++ switch (type) { ++ case IRQ_TYPE_EDGE_RISING: ++ iowrite32(val_level | BIT(gpio % 32), reg_level); ++ iowrite32(val_pol | BIT(gpio % 32), reg_pol); ++ break; ++ case IRQ_TYPE_EDGE_FALLING: ++ iowrite32(val_level | BIT(gpio % 32), reg_level); ++ iowrite32(val_pol & ~BIT(gpio % 32), reg_pol); ++ break; ++ case IRQ_TYPE_LEVEL_HIGH: ++ iowrite32(val_level & ~BIT(gpio % 32), reg_level); ++ iowrite32(val_pol | BIT(gpio % 32), reg_pol); ++ break; ++ case IRQ_TYPE_LEVEL_LOW: ++ iowrite32(val_level & ~BIT(gpio % 32), reg_level); ++ iowrite32(val_pol & ~BIT(gpio % 32), reg_pol); ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ ++ spin_unlock_irqrestore(&lock, flags); ++ ++ return ret; ++} ++ ++/** ++ * intel_cln_gpio_irq_unmask ++ * @param irq_data: Pointer to information about the IRQ ++ * ++ * Unmask interrupts for a GPIO. ++ */ ++static void intel_cln_gpio_irq_unmask(struct irq_data *d) ++{ ++ unsigned long flags = 0; ++ void __iomem *reg_mask = reg_base + PORTA_INT_MASK; ++ u32 val_mask = 0; ++ unsigned gpio = 0; ++ ++ if (NULL == d) { ++ pr_err("%s(): null irq_data\n", __func__); ++ return; ++ } ++ ++ gpio = d->irq - irq_base; ++ ++ spin_lock_irqsave(&lock, flags); ++ val_mask = ioread32(reg_mask); ++ iowrite32(val_mask | BIT(gpio % 32), reg_mask); ++ spin_unlock_irqrestore(&lock, flags); ++} ++ ++/** ++ * intel_cln_gpio_irq_mask ++ * @param irq_data: Pointer to information about the IRQ ++ * ++ * Mask interrupts for a GPIO. ++ */ ++static void intel_cln_gpio_irq_mask(struct irq_data *d) ++{ ++ unsigned long flags = 0; ++ void __iomem *reg_mask = reg_base + PORTA_INT_MASK; ++ u32 val_mask = 0; ++ unsigned gpio = 0; ++ ++ if (NULL == d) { ++ pr_err("%s(): null irq_data\n", __func__); ++ return; ++ } ++ ++ gpio = d->irq - irq_base; ++ ++ spin_lock_irqsave(&lock, flags); ++ val_mask = ioread32(reg_mask); ++ iowrite32(val_mask & ~BIT(gpio % 32), reg_mask); ++ spin_unlock_irqrestore(&lock, flags); ++} ++ ++/** ++ * intel_cln_gpio_irq_enable ++ * @param irq_data: Pointer to information about the IRQ ++ * ++ * Enable interrupts for a GPIO. ++ */ ++static void intel_cln_gpio_irq_enable(struct irq_data *d) ++{ ++ unsigned long flags = 0; ++ void __iomem *reg_inte = reg_base + PORTA_INT_EN; ++ u32 val_inte = 0; ++ unsigned gpio = 0; ++ ++ if (NULL == d) { ++ pr_err("%s(): null irq_data\n", __func__); ++ return; ++ } ++ ++ gpio = d->irq - irq_base; ++ ++ spin_lock_irqsave(&lock, flags); ++ val_inte = ioread32(reg_inte); ++ iowrite32(val_inte | BIT(gpio % 32), reg_inte); ++ spin_unlock_irqrestore(&lock, flags); ++} ++ ++/** ++ * intel_cln_gpio_irq_disable ++ * @param irq_data: Pointer to information about the IRQ ++ * ++ * Disable interrupts for a GPIO. ++ */ ++static void intel_cln_gpio_irq_disable(struct irq_data *d) ++{ ++ unsigned long flags = 0; ++ void __iomem *reg_inte = reg_base + PORTA_INT_EN; ++ u32 val_inte = 0; ++ unsigned gpio = 0; ++ ++ if (NULL == d) { ++ pr_err("%s(): null irq_data\n", __func__); ++ return; ++ } ++ ++ gpio = d->irq - irq_base; ++ ++ spin_lock_irqsave(&lock, flags); ++ val_inte = ioread32(reg_inte); ++ iowrite32(val_inte & ~BIT(gpio % 32), reg_inte); ++ spin_unlock_irqrestore(&lock, flags); ++} ++ ++/** ++ * intel_cln_gpio_to_irq ++ * @param chip: Pointer to GPIO chip registered by GPIOLIB ++ * @param offset: the GPIO number within the GPIOLIB chip ++ * @return IRQ associated to GPIO ++ * ++ * Compute the IRQ number based on the GPIO. ++ */ ++static int intel_cln_gpio_to_irq(struct gpio_chip *chip, unsigned offset) ++{ ++ return irq_base + offset; ++} ++ ++/** ++ * intel_cln_gpio_isr ++ * @param irq: IRQ number ++ * @param dev_id: cookie used to tell what instance of the driver the interrupt ++ * belongs to ++ * @return IRQ_HANDLED if interrupt served, IRQ_NONE if no interrupt pending ++ * ++ * Interrupt Service Routine for GPIO. Identify which GPIOs (if any) is pending ++ * for interrupt to be served, acknowledge the interrupt and serve it. ++ */ ++irqreturn_t intel_cln_gpio_isr(int irq, void *dev_id) ++{ ++ irqreturn_t ret = IRQ_NONE; ++ u32 pending = 0, gpio = 0; ++ void __iomem *reg_pending = reg_base + PORTA_INT_STATUS; ++ void __iomem *reg_eoi = reg_base + PORTA_INT_EOI; ++ ++ /* Which pin (if any) triggered the interrupt */ ++ while ((pending = ioread32(reg_pending))) { ++ /* ++ * Acknowledge all the asserted GPIO interrupt lines before ++ * serving them, so that we don't lose an edge. ++ * This has only effect on edge-triggered interrupts. ++ */ ++ iowrite32(pending, reg_eoi); ++ ++ /* Serve each asserted interrupt */ ++ do { ++ gpio = __ffs(pending); ++ generic_handle_irq( ++ gpio_to_irq(INTEL_CLN_GIP_GPIO_BASE + gpio)); ++ pending &= ~BIT(gpio); ++ ret = IRQ_HANDLED; ++ } while(pending); ++ } ++ ++ return ret; ++} ++ ++/** ++ * intel_cln_gpio_save_state ++ * ++ * Save GPIO register state for system-wide suspend events and mask out ++ * interrupts. ++ */ ++void intel_cln_gpio_save_state(void) ++{ ++ unsigned long flags = 0; ++ ++ spin_lock_irqsave(&lock, flags); ++ ++ saved_regs.int_mask = ioread32(reg_base + PORTA_INT_MASK); ++ saved_regs.int_en = ioread32(reg_base + PORTA_INT_EN); ++ saved_regs.int_deb = ioread32(reg_base + PORTA_DEBOUNCE); ++ saved_regs.int_pol = ioread32(reg_base + PORTA_INT_POLARITY); ++ saved_regs.int_type = ioread32(reg_base + PORTA_INT_TYPE_LEVEL); ++ saved_regs.dir = ioread32(reg_base + PORTA_DIR); ++ saved_regs.data = ioread32(reg_base + PORTA_DATA); ++ ++ /* Mask out interrupts */ ++ iowrite32(0xffffffff, reg_base + PORTA_INT_MASK); ++ ++ spin_unlock_irqrestore(&lock, flags); ++} ++ ++/** ++ * intel_cln_gpio_restore_state ++ * ++ * Restore GPIO register state for system-wide resume events and clear out ++ * spurious interrupts. ++ */ ++void intel_cln_gpio_restore_state(void) ++{ ++ unsigned long flags = 0; ++ ++ spin_lock_irqsave(&lock, flags); ++ ++ iowrite32(saved_regs.data, reg_base + PORTA_DATA); ++ iowrite32(saved_regs.dir, reg_base + PORTA_DIR); ++ iowrite32(saved_regs.int_type, reg_base + PORTA_INT_TYPE_LEVEL); ++ iowrite32(saved_regs.int_pol, reg_base + PORTA_INT_POLARITY); ++ iowrite32(saved_regs.int_deb, reg_base + PORTA_DEBOUNCE); ++ iowrite32(saved_regs.int_en, reg_base + PORTA_INT_EN); ++ iowrite32(saved_regs.int_mask, reg_base + PORTA_INT_MASK); ++ ++ /* Clear out spurious interrupts */ ++ iowrite32(0xffffffff, reg_base + PORTA_INT_EOI); ++ ++ spin_unlock_irqrestore(&lock, flags); ++} ++ ++/** ++ * intel_cln_gpio_probe ++ * @param pdev: Pointer to GIP PCI device ++ * @return 0 success < 0 failure ++ * ++ * Perform GPIO-specific probing on behalf of the top-level GIP driver. ++ * Initiate the GPIO device. ++ */ ++int intel_cln_gpio_probe(struct pci_dev *pdev) ++{ ++ int retval = 0; ++ resource_size_t start = 0, len = 0; ++ ++ /* Get UIO memory */ ++ info = kzalloc(sizeof(struct uio_info), GFP_KERNEL); ++ if (!info) ++ return -ENOMEM; ++ ++ /* Determine the address of the GPIO area */ ++ start = pci_resource_start(pdev, GIP_GPIO_BAR); ++ len = pci_resource_len(pdev, GIP_GPIO_BAR); ++ if (!start || len == 0) { ++ dev_err(&pdev->dev, "bar%d not set\n", GIP_GPIO_BAR); ++ retval = -ENODEV; ++ goto exit; ++ } ++ ++ reg_base = ioremap_nocache(start, len); ++ if (NULL == reg_base) { ++ dev_err(&pdev->dev, "I/O memory remapping failed\n"); ++ retval = -EFAULT; ++ goto exit; ++ } ++ ++ memset(&saved_regs, 0x0, sizeof(saved_regs)); ++ ++ gc = kzalloc(sizeof(struct gpio_chip), GFP_KERNEL); ++ if (!gc) { ++ retval = -ENOMEM; ++ goto err_iounmap; ++ } ++ ++ if (n_gpio == 0 || n_gpio > INTEL_CLN_GIP_NGPIO) { ++ dev_err(&pdev->dev, "n_gpio outside range [1,%d]\n", ++ INTEL_CLN_GIP_NGPIO); ++ retval = -EINVAL; ++ goto err_free_gpiochip; ++ } ++ ++ gc->label = "intel_cln_gip_gpio"; ++ gc->owner = THIS_MODULE; ++ gc->direction_input = intel_cln_gpio_direction_input; ++ gc->direction_output = intel_cln_gpio_direction_output; ++ gc->get = intel_cln_gpio_get; ++ gc->set = intel_cln_gpio_set; ++ gc->set_debounce = intel_cln_gpio_set_debounce; ++ gc->to_irq = intel_cln_gpio_to_irq; ++ gc->base = INTEL_CLN_GIP_GPIO_BASE; ++ gc->ngpio = n_gpio; ++ gc->can_sleep = 0; ++ retval = gpiochip_add(gc); ++ if (retval) { ++ dev_err(&pdev->dev, "failure adding GPIO chip\n"); ++ goto err_free_gpiochip; ++ } ++ ++ spin_lock_init(&lock); ++ ++ /* ++ * Allocate a range of IRQ descriptor for the available GPIO. ++ * IRQs are allocated dynamically. ++ */ ++ irq_base = irq_alloc_descs(-1, gpio_irqbase, n_gpio, NUMA_NO_NODE); ++ if (irq_base < 0) { ++ dev_err(&pdev->dev, "failure adding GPIO IRQ descriptors\n"); ++ goto err_remove_gpiochip; ++ } ++ ++ retval = platform_device_register(&cln_gpio_restrict_pdev); ++ if (retval < 0){ ++ goto err_free_irq_descs; ++ } ++ ++ igc = irq_alloc_generic_chip("intel_cln_gip_gpio", 1, irq_base, ++ reg_base, handle_simple_irq); ++ if (NULL == igc) { ++ retval = -ENOMEM; ++ goto err_free_irq_descs; ++ } ++ ++ /* UIO */ ++ info->mem[0].addr = start; ++ info->mem[0].internal_addr = reg_base; ++ info->mem[0].size = len; ++ info->mem[0].memtype = UIO_MEM_PHYS; ++ info->mem[0].name = "gpio_regs"; ++ info->name = "gpio uio"; ++ info->version = "0.0.1"; ++ ++ if (uio_register_device(&pdev->dev, info)) ++ goto err_free_irq_descs; ++ ++ pr_info("%s UIO addr 0x%08x internal_addr 0x%08x size %lu memtype %d\n", ++ __func__, (unsigned int)info->mem[0].addr, ++ (unsigned int)info->mem[0].internal_addr, info->mem[0].size, ++ info->mem[0].memtype); ++ igc->chip_types->chip.irq_mask = intel_cln_gpio_irq_mask; ++ igc->chip_types->chip.irq_unmask = intel_cln_gpio_irq_unmask; ++ igc->chip_types->chip.irq_set_type = intel_cln_gpio_irq_type; ++ igc->chip_types->chip.irq_enable = intel_cln_gpio_irq_enable; ++ igc->chip_types->chip.irq_disable = intel_cln_gpio_irq_disable; ++ ++ irq_setup_generic_chip(igc, IRQ_MSK(n_gpio), IRQ_GC_INIT_MASK_CACHE, ++ IRQ_NOREQUEST | IRQ_NOPROBE, 0); ++ ++ return 0; ++ ++err_free_irq_descs: ++ irq_free_descs(irq_base, n_gpio); ++err_remove_gpiochip: ++ if (0 != gpiochip_remove(gc)) ++ dev_err(&pdev->dev, "failed removing gpio_chip\n"); ++err_free_gpiochip: ++ kfree(gc); ++err_iounmap: ++ iounmap(reg_base); ++exit: ++ if (info != NULL) ++ kfree(info); ++ return retval; ++} ++ ++/** ++ * intel_cln_gpio_remove ++ * @param pdev: Pointer to GIP PCI device ++ * ++ * Perform GPIO-specific resource release on behalf of the top-level GIP ++ * driver. ++ */ ++void intel_cln_gpio_remove(struct pci_dev *pdev) ++{ ++ if (NULL == igc) { ++ dev_err(&pdev->dev, "null pointer to irq_generic_chip\n"); ++ return; ++ } ++ if (NULL == gc) { ++ dev_err(&pdev->dev, "null pointer to gpio_chip\n"); ++ return; ++ } ++ ++ /* Tear down IRQ descriptors */ ++ irq_remove_generic_chip(igc, IRQ_MSK(n_gpio), 0, ++ IRQ_NOREQUEST | IRQ_NOPROBE); ++ kfree(igc); ++ irq_free_descs(irq_base, n_gpio); ++ ++ platform_device_unregister(&cln_gpio_restrict_pdev); ++ ++ /* Release GPIO chip */ ++ if (0 != gpiochip_remove(gc)) ++ dev_err(&pdev->dev, "failed removing gpio_chip\n"); ++ ++ ++ if (info != NULL){ ++ uio_unregister_device(info); ++ iounmap(info->mem[0].internal_addr); ++ kfree(info); ++ } ++ ++ kfree(gc); ++ iounmap(reg_base); ++} +diff --git a/drivers/mfd/intel_cln_gip_i2c.c b/drivers/mfd/intel_cln_gip_i2c.c +new file mode 100644 +index 0000000..3675e50 +--- /dev/null ++++ b/drivers/mfd/intel_cln_gip_i2c.c +@@ -0,0 +1,204 @@ ++/* ++ * Copyright(c) 2013 Intel Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ */ ++/* ++ * Intel Clanton GIP (GPIO/I2C) - I2C-specific PCI driver ++ * ++ * PCI glue logic for Clanton GIP/I2C. ++ * The GIP I2C device is the DesignWare I2C. This file defines the PCI glue ++ * for this driver and is heavily based on ++ * on drivers/i2c/busses/i2c-designware-pcidrv.c. Also, it relies on ++ * drivers/i2c/busses/i2c-designware-core.c for the core logic. ++ * Please note only a single instance of the I2C device is supported. ++ */ ++ ++#include <linux/errno.h> ++#include <linux/i2c.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/pci.h> ++#include "intel_cln_gip.h" ++ ++enum dw_pci_ctl_id_t { ++ clanton_0, ++}; ++ ++static unsigned int i2c_std_mode; ++module_param(i2c_std_mode, uint, S_IRUSR); ++MODULE_PARM_DESC(i2c_std_mode, "Force I2C standard mode"); ++ ++#define INTEL_CLN_STD_CFG (DW_IC_CON_MASTER | \ ++ DW_IC_CON_SLAVE_DISABLE | \ ++ DW_IC_CON_RESTART_EN) ++ ++static struct dw_pci_controller cln_gip_i2c_controller = { ++ .bus_num = 0, ++ .bus_cfg = INTEL_CLN_STD_CFG | DW_IC_CON_SPEED_FAST, ++ .tx_fifo_depth = 16, ++ .rx_fifo_depth = 16, ++ .clk_khz = ++#ifdef CONFIG_INTEL_QUARK_X1000_SOC_FPGAEMU ++ 14000, ++#else ++ 33000, ++#endif ++ .explicit_stop = 1, ++}; ++ ++static struct i2c_algorithm i2c_dw_algo = { ++ .master_xfer = i2c_dw_xfer, ++ .functionality = i2c_dw_func, ++}; ++ ++/** ++ * i2c_dw_get_clk_rate_khz ++ * @param dev: Pointer to I2C device private data ++ * @return clock rate in kHz ++ * ++ * Ancillary function returning the frequency of the clock supplied to the ++ * interface. ++ */ ++static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev) ++{ ++ return dev->controller->clk_khz; ++} ++ ++/** ++ * intel_cln_i2c_probe ++ * @param pdev: Pointer to GIP PCI device ++ * @param drvdata: private driver data ++ * @return 0 success < 0 failure ++ * ++ * Perform I2C-specific probing on behalf of the top-level GIP driver. ++ * Also call into I2C core driver routines for initiating the device. ++ */ ++int intel_cln_i2c_probe(struct pci_dev *pdev, ++ struct dw_i2c_dev **drvdata) ++{ ++ int retval = 0; ++ resource_size_t start = 0, len = 0; ++ struct dw_i2c_dev *dev = NULL; ++ struct i2c_adapter *adap = NULL; ++ void __iomem *reg_base = NULL; ++ struct dw_pci_controller *controller = NULL; ++ ++ controller = &cln_gip_i2c_controller; ++ ++ /* Clanton default configuration is fast mode, unless otherwise asked */ ++ if (i2c_std_mode) ++ controller->bus_cfg = INTEL_CLN_STD_CFG | DW_IC_CON_SPEED_STD; ++ ++ /* Determine the address of the I2C area */ ++ start = pci_resource_start(pdev, GIP_I2C_BAR); ++ len = pci_resource_len(pdev, GIP_I2C_BAR); ++ if (!start || len == 0) { ++ dev_err(&pdev->dev, "bar%d not set\n", GIP_I2C_BAR); ++ retval = -ENODEV; ++ goto err; ++ } ++ ++ reg_base = ioremap_nocache(start, len); ++ if (!reg_base) { ++ dev_err(&pdev->dev, "I/O memory remapping failed\n"); ++ retval = -ENOMEM; ++ goto err; ++ } ++ ++ dev = kzalloc(sizeof(struct dw_i2c_dev), GFP_KERNEL); ++ if (!dev) { ++ retval = -ENOMEM; ++ goto err_iounmap; ++ } ++ ++ init_completion(&dev->cmd_complete); ++ mutex_init(&dev->lock); ++ dev->clk = NULL; ++ dev->controller = controller; ++ dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz; ++ dev->base = reg_base; ++ dev->dev = get_device(&pdev->dev); ++ dev->functionality = ++ I2C_FUNC_I2C | ++ I2C_FUNC_10BIT_ADDR | ++ I2C_FUNC_SMBUS_BYTE | ++ I2C_FUNC_SMBUS_BYTE_DATA | ++ I2C_FUNC_SMBUS_WORD_DATA | ++ I2C_FUNC_SMBUS_I2C_BLOCK; ++ dev->master_cfg = controller->bus_cfg; ++ ++ *drvdata = dev; ++ ++ dev->tx_fifo_depth = controller->tx_fifo_depth; ++ dev->rx_fifo_depth = controller->rx_fifo_depth; ++ dev->explicit_stop = controller->explicit_stop; ++ retval = i2c_dw_init(dev); ++ if (retval) ++ goto err_release_drvdata; ++ ++ adap = &dev->adapter; ++ i2c_set_adapdata(adap, dev); ++ adap->owner = THIS_MODULE; ++ adap->class = 0; ++ adap->algo = &i2c_dw_algo; ++ adap->dev.parent = &pdev->dev; ++ adap->nr = controller->bus_num; ++ snprintf(adap->name, sizeof(adap->name), "intel_cln_gip_i2c"); ++ ++ i2c_dw_disable_int(dev); ++ i2c_dw_clear_int(dev); ++ retval = i2c_add_numbered_adapter(adap); ++ if (retval) { ++ dev_err(&pdev->dev, "failure adding I2C adapter\n"); ++ goto err_release_drvdata; ++ } ++ ++ return 0; ++ ++err_release_drvdata: ++ put_device(&pdev->dev); ++ kfree(dev); ++err_iounmap: ++ iounmap(reg_base); ++err: ++ return retval; ++} ++ ++/** ++ * intel_cln_i2c_remove ++ * @param pdev: Pointer to GIP PCI device ++ * @param dev: Pointer to I2C private data ++ * ++ * Perform I2C-specific resource release on behalf of the top-level GIP driver. ++ */ ++void intel_cln_i2c_remove(struct pci_dev *pdev, ++ struct dw_i2c_dev *dev) ++{ ++ ++ if (NULL == dev) { ++ dev_err(&pdev->dev, "%s: failure getting driver data\n", ++ __func__); ++ return; ++ } ++ ++ i2c_dw_disable(dev); ++ i2c_del_adapter(&dev->adapter); ++ iounmap(dev->base); ++ ++ kfree(dev); ++} +diff --git a/drivers/mfd/intel_cln_gip_test.c b/drivers/mfd/intel_cln_gip_test.c +new file mode 100644 +index 0000000..ea8d846 +--- /dev/null ++++ b/drivers/mfd/intel_cln_gip_test.c +@@ -0,0 +1,1202 @@ ++/* ++ * Copyright(c) 2013 Intel Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ */ ++/* ++ * Intel Clanton GIP (GPIO/I2C) Test module ++ * ++ * Clanton GIP + North-Cluster GPIO test module. ++ */ ++ ++#include <asm/tsc.h> ++#include <linux/cdev.h> ++#include <linux/delay.h> ++#include <linux/device.h> ++#include <linux/fs.h> ++#include <linux/gpio.h> ++#include <linux/interrupt.h> ++#include <linux/module.h> ++#include <linux/pci.h> ++#include <linux/platform_device.h> ++#include <linux/printk.h> ++#include <linux/spi/spi.h> ++#include <linux/spi/spi_bitbang.h> ++#include <linux/spi/spi_gpio.h> ++#include <linux/slab.h> ++#include <linux/workqueue.h> ++ ++#define DRIVER_NAME "intel_cln_gip_test" ++ ++/**************************** Exported to LISA *******************************/ ++ ++/* ++ * Internally-used ioctl code. At the moment it is not reserved by any mainline ++ * driver. ++ */ ++#define GIP_TEST_IOCTL_CODE 0xE0 ++ ++/* ++ * Integers for ioctl operation. ++ */ ++#define IOCTL_CLN_GPIO_11 _IO(GIP_TEST_IOCTL_CODE, 0x00) ++#define IOCTL_CLN_GPIO_11_CLEANUP _IO(GIP_TEST_IOCTL_CODE, 0x01) ++#define IOCTL_CLN_GPIO_12 _IO(GIP_TEST_IOCTL_CODE, 0x02) ++#define IOCTL_CLN_GPIO_12_CLEANUP _IO(GIP_TEST_IOCTL_CODE, 0x03) ++#define IOCTL_CLN_GPIO_13 _IO(GIP_TEST_IOCTL_CODE, 0x04) ++#define IOCTL_CLN_GPIO_13_CLEANUP _IO(GIP_TEST_IOCTL_CODE, 0x05) ++#define IOCTL_CLN_GPIO_14 _IO(GIP_TEST_IOCTL_CODE, 0x06) ++#define IOCTL_CLN_GPIO_14_CLEANUP _IO(GIP_TEST_IOCTL_CODE, 0x07) ++#define IOCTL_CLN_GPIO_15 _IO(GIP_TEST_IOCTL_CODE, 0x08) ++#define IOCTL_CLN_GPIO_15_CLEANUP _IO(GIP_TEST_IOCTL_CODE, 0x09) ++#define IOCTL_CLN_GPIO_16 _IO(GIP_TEST_IOCTL_CODE, 0x0A) ++#define IOCTL_CLN_GPIO_16_CLEANUP _IO(GIP_TEST_IOCTL_CODE, 0x0B) ++#define IOCTL_CLN_GPIO_17 _IO(GIP_TEST_IOCTL_CODE, 0x0C) ++#define IOCTL_CLN_GPIO_17_CLEANUP _IO(GIP_TEST_IOCTL_CODE, 0x0D) ++#define IOCTL_CLN_GPIO_19 _IO(GIP_TEST_IOCTL_CODE, 0x0E) ++#define IOCTL_CLN_GPIO_19_CLEANUP _IO(GIP_TEST_IOCTL_CODE, 0x0F) ++#define IOCTL_CLN_GPIO_20 _IO(GIP_TEST_IOCTL_CODE, 0x10) ++#define IOCTL_CLN_GPIO_20_CLEANUP _IO(GIP_TEST_IOCTL_CODE, 0x11) ++#define IOCTL_CLN_GPIO_21 _IO(GIP_TEST_IOCTL_CODE, 0x12) ++#define IOCTL_CLN_GPIO_21_CLEANUP _IO(GIP_TEST_IOCTL_CODE, 0x13) ++#define IOCTL_CLN_GPIO_24 _IO(GIP_TEST_IOCTL_CODE, 0x14) ++#define IOCTL_CLN_GPIO_26 _IO(GIP_TEST_IOCTL_CODE, 0x15) ++#define IOCTL_CLN_GPIO_26_CLEANUP _IO(GIP_TEST_IOCTL_CODE, 0x16) ++/* Exercise callbacks for S0/S3 power-state transitions and vice-versa */ ++#define IOCTL_CLN_GIP_SYSTEM_SUSPEND _IO(GIP_TEST_IOCTL_CODE, 0x17) ++#define IOCTL_CLN_GIP_SYSTEM_RESUME _IO(GIP_TEST_IOCTL_CODE, 0x18) ++ ++#define GPIO_INT_EDGE_POS_LABEL "gpio-edge-pos" ++#define GPIO_INT_EDGE_NEG_LABEL "gpio-edge-neg" ++#define GPIO_INT_LEVEL_HIGH_LABEL "gpio-level-hi" ++#define GPIO_INT_LEVEL_LOW_LABEL "gpio-level-lo" ++#define GPIO_INT_BASIC_LABEL "gpio-edge-pos-basic" ++#define GPIO_PM_TEST_IRQ_LABEL "gpio_pm_test_irq" ++ ++/* ++ * Board GPIO numbers. ++ * Mapping between the North/South cluster GPIO and GPIOLIB IDs. ++ */ ++#define SUT_GPIO_NC_0 0x00 ++#define SUT_GPIO_NC_1 0x01 ++#define SUT_GPIO_NC_2 0x02 ++#define SUT_GPIO_NC_3 0x03 ++#define SUT_GPIO_NC_4 0x04 ++#define SUT_GPIO_NC_5 0x05 ++#define SUT_GPIO_NC_6 0x06 ++#define SUT_GPIO_NC_7 0x07 ++#define SUT_GPIO_SC_0 0x08 ++#define SUT_GPIO_SC_1 0x09 ++#define SUT_GPIO_SC_2 0x0A ++#define SUT_GPIO_SC_3 0x0B ++#define SUT_GPIO_SC_4 0x0C ++#define SUT_GPIO_SC_5 0x0D ++#define SUT_GPIO_SC_6 0x0E ++#define SUT_GPIO_SC_7 0x0F ++ ++/* ++ * Bitbanged SPI bus numbers. ++ */ ++#define GPIO_NC_BITBANG_SPI_BUS 0x0 ++#define GPIO_SC_BITBANG_SPI_BUS 0x1 ++ ++/*****************************************************************************/ ++ ++/** ++ * struct intel_cln_gip_dev ++ * ++ * Structure to represent module state/data/etc ++ */ ++struct intel_cln_gip_test_dev { ++ unsigned int opened; ++ struct platform_device *pldev; /* Platform device */ ++ struct cdev cdev; ++ struct mutex open_lock; ++}; ++ ++static struct intel_cln_gip_test_dev gip_test_dev; ++static struct class *gip_test_class; ++static DEFINE_MUTEX(gip_test_mutex); ++static int gip_test_major; ++ ++/* Private pointers to NC/SC bitbanged SPI devices */ ++static struct platform_device *spi_gpio_nc_pdev = NULL; ++static struct platform_device *spi_gpio_sc_pdev = NULL; ++ ++/* ++ * Level-triggered interrupt variables ++ */ ++/* Level-triggered GPIO workqueue */ ++static struct delayed_work work; ++/* Level-triggered interrupt counter */ ++static unsigned int level_int_count; ++/* By default, a level-triggered interrupt is a low-level triggered */ ++static int level_high_triggered = 0; ++ ++/* ++ * Interrupt performance metrics variables and parameters ++ */ ++/* How many captures */ ++#define INT_PERF_TEST_CAPTURES 10000 ++/* Timestamp for latency test interrupt handler */ ++static cycles_t perf_t1; ++/* Captures to be returned to user space */ ++static cycles_t deltas[INT_PERF_TEST_CAPTURES]; ++/* Couldn't find the actual define for this */ ++#define UINT64_MAX 0xFFFFFFFFFFFFFFFFULL ++ ++static irqreturn_t gpio_pm_test_handler(int irq, void *dev_id) ++{ ++ /* Do nothing, just acknowledge the IRQ subsystem */ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t gpio_latency_handler(int irq, void *dev_id) ++{ ++ /* t0 */ ++ perf_t1 = get_cycles(); ++ ++ gpio_set_value(SUT_GPIO_SC_0, 0); ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t gpio_basic_handler(int irq, void *dev_id) ++{ ++ /* Do nothing, just acknowledge the IRQ subsystem */ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t gpio_pos_edge_handler(int irq, void *dev_id) ++{ ++ /* Do nothing, just acknowledge the IRQ subsystem */ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t gpio_neg_edge_handler(int irq, void *dev_id) ++{ ++ /* Do nothing, just acknowledge the IRQ subsystem */ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t gpio_level_handler(int irq, void *dev_id) ++{ ++ /* Untrigger the interrupt */ ++ gpio_set_value(SUT_GPIO_SC_7, level_high_triggered ? 0 : 1); ++ ++ level_int_count ++; ++ if (level_int_count < 1000) { ++ /* Next task due in a jiffy */ ++ schedule_delayed_work(&work, 1); ++ } else if (level_int_count == 1000){ ++ /* OK */ ++ } else { ++ /* ++ * We may get spurious interrupts. This because the TE requires ++ * some time to drive the actual value to the GPIO. ++ */ ++ pr_info("Spurious interrupt\n"); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static void gpio_level_drive(struct work_struct *work) ++{ ++ /* TE to trigger the interrupt */ ++ gpio_set_value(SUT_GPIO_SC_7, level_high_triggered ? 1 : 0); ++} ++ ++/* ++ * Define bitbanged SPI interface over Nort-Cluster South-Cluster GPIO blocks. ++ * Assign GPIO to SCK/MOSI/MISO ++ */ ++static struct spi_gpio_platform_data spi_gpio_nc_data = { ++ .sck = SUT_GPIO_NC_3, ++ .mosi = SUT_GPIO_NC_4, ++ .miso = SUT_GPIO_NC_5, ++ .num_chipselect = 1, ++}; ++static struct spi_gpio_platform_data spi_gpio_sc_data = { ++ .sck = SUT_GPIO_SC_2, ++ .mosi = SUT_GPIO_SC_3, ++ .miso = SUT_GPIO_SC_4, ++ .num_chipselect = 1, ++}; ++ ++/* ++ * Board information for SPI devices. ++ */ ++static struct spi_board_info spi_gpio_nc_board_info[] = { ++ { ++ .modalias = "spidev", ++ .max_speed_hz = 1000, ++ .bus_num = GPIO_NC_BITBANG_SPI_BUS, ++ .mode = SPI_MODE_0, ++ .platform_data = &spi_gpio_nc_data, ++ /* Assign GPIO to CS */ ++ .controller_data = (void *)SUT_GPIO_NC_6, ++ }, ++}; ++static struct spi_board_info spi_gpio_sc_board_info[] = { ++ { ++ .modalias = "spidev", ++ .max_speed_hz = 1000, ++ .bus_num = GPIO_SC_BITBANG_SPI_BUS, ++ .mode = SPI_MODE_0, ++ .platform_data = &spi_gpio_sc_data, ++ /* Assign GPIO to CS */ ++ .controller_data = (void *)SUT_GPIO_SC_5, ++ }, ++}; ++ ++/** ++ * gpio_sc_level_int ++ * ++ * Request level triggered IRQ for SUT_GPIO_SC_6 and register ++ * SUT_GPIO_SC_7 as output GPIO. ++ * If positive equals to 0, the IRQ is high-level triggered. ++ * Otherwise, low-level triggered. ++ * Mask the IRQ if requested. ++ */ ++static int gpio_sc_level_int(int positive, int masking) ++{ ++ int ret = 0; ++ int irq = -1; ++ ++ unsigned long out_init_val = ++ (positive ? GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH); ++ ++ level_high_triggered = positive; ++ ++ /* Initialise workqueue task */ ++ INIT_DELAYED_WORK(&work, gpio_level_drive); ++ ++ if (!gpio_is_valid(SUT_GPIO_SC_6)) { ++ pr_err("gpio%d is invalid\n", SUT_GPIO_SC_6); ++ ret = -1; ++ goto fail; ++ } ++ if (!gpio_is_valid(SUT_GPIO_SC_7)) { ++ pr_err("gpio%d is invalid\n", SUT_GPIO_SC_7); ++ ret = -1; ++ goto fail; ++ } ++ ++ ret = gpio_request_one(SUT_GPIO_SC_6, GPIOF_IN, "gpio_hi_level"); ++ if (ret) { ++ pr_err("can't request gpio%d (error %d)\n", SUT_GPIO_SC_6, ret); ++ goto fail; ++ } ++ ret = gpio_request_one(SUT_GPIO_SC_7, out_init_val, "gpio_output"); ++ if (ret) { ++ pr_err("can't request gpio%d (error %d)\n", SUT_GPIO_SC_7, ret); ++ goto fail_release_first_gpio; ++ } ++ ++ irq = gpio_to_irq(SUT_GPIO_SC_6); ++ if (irq < 0) { ++ pr_err("can't map gpio%d to IRQ\n", SUT_GPIO_SC_6); ++ goto fail_release_second_gpio; ++ } ++ ++ if (0 != (ret = request_irq(irq, gpio_level_handler, ++ positive ? IRQF_TRIGGER_HIGH : IRQF_TRIGGER_LOW, ++ positive ? GPIO_INT_LEVEL_HIGH_LABEL : GPIO_INT_LEVEL_LOW_LABEL, ++ NULL))) { ++ pr_err("can't request IRQ for gpio%d\n", SUT_GPIO_SC_6); ++ goto fail_release_second_gpio; ++ } ++ ++ level_int_count = 0; ++ ++ pr_info("Registered output gpio%d and IRQ for gpio%d\n", SUT_GPIO_SC_7, ++ SUT_GPIO_SC_6); ++ ++ if (masking) { ++ disable_irq(gpio_to_irq(SUT_GPIO_SC_6)); ++ pr_info("Masked gpio%d IRQ\n", SUT_GPIO_SC_6); ++ } ++ ++ /* ++ * Submit task to workqueue to drive the external Test Equipment. ++ * Note the task is delayed long enough to have Aarvark already set up. ++ * This because Aardvark has to ignore the initial glitches during the ++ * previous GPIO setup phase. ++ */ ++ schedule_delayed_work(&work, 20 * HZ); ++ ++ return 0; ++ ++fail_release_second_gpio: ++ gpio_free(SUT_GPIO_SC_7); ++fail_release_first_gpio: ++ gpio_free(SUT_GPIO_SC_6); ++fail: ++ pr_err("%s() failed\n", __func__); ++ ++ return ret; ++} ++ ++/** ++ * gpio_sc_level_int_teardown ++ * ++ * Release resources reserved by gpio_sc_level_int(). ++ */ ++static int gpio_sc_level_int_teardown(void) ++{ ++ int irq = -1; ++ ++ if (0 != cancel_delayed_work_sync(&work)) ++ pr_warn("delayed work was still pending\n"); ++ ++ irq = gpio_to_irq(SUT_GPIO_SC_6); ++ if (irq < 0) { ++ pr_err("can't map gpio%d to IRQ\n", SUT_GPIO_SC_6); ++ } else { ++ free_irq(irq, NULL); ++ } ++ ++ /* Make sure no handler is still running by this time */ ++ mdelay(20); ++ ++ gpio_free(SUT_GPIO_SC_7); ++ gpio_free(SUT_GPIO_SC_6); ++ ++ return 0; ++} ++ ++/* ++ * gpio_sc_interrupt_perf ++ * ++ * Performs a basic GPIO interrupt latency test by timestamping delta between ++ * interrupt driven and handled over a GPIO loopback. ++ * ++ * Returns to userspace the array of deltas obtained during each capture. ++ * A total amount of INT_PERF_TEST_CAPTURES captures is performed. ++ * ++ */ ++static int gpio_sc_interrupt_perf(unsigned long user_memloc) ++{ ++ int ret = 0; ++ int irq = -1; ++ int gpio_input = SUT_GPIO_SC_1; ++ int gpio_output = SUT_GPIO_SC_0; ++ unsigned int i = 0; ++ cycles_t perf_t0 = 0; ++ cycles_t delta = 0; ++ ++ /* Casting pointer to user-space location to write */ ++ cycles_t __user *user_ptr = (cycles_t __user *)user_memloc; ++ ++ /* Can we copy the captures array into user-space location? */ ++ if (!access_ok(VERIFY_WRITE, user_ptr, sizeof(deltas))) { ++ pr_err("can't copy 0x%x bytes to user-space address 0x%p\n", ++ sizeof(deltas),user_ptr); ++ return -EFAULT; ++ } ++ ++ /* Setup the GPIO */ ++ if (!gpio_is_valid(gpio_input)) { ++ pr_err("gpio%d is invalid\n", gpio_input); ++ ret = -1; ++ goto fail; ++ } ++ if (!gpio_is_valid(gpio_output)) { ++ pr_err("gpio%d is invalid\n", gpio_output); ++ ret = -1; ++ goto fail; ++ } ++ ret = gpio_request_one(gpio_input, GPIOF_IN, "gpio_intperf_in"); ++ if (ret) { ++ pr_err("can't request gpio%d (error %d)\n", gpio_input, ret); ++ goto fail; ++ } ++ ret = gpio_request_one(gpio_output, GPIOF_OUT_INIT_LOW, "gpio_intperf_out"); ++ if (ret) { ++ pr_err("can't request gpio%d (error %d)\n", gpio_output, ret); ++ goto fail_release_input_gpio; ++ } ++ ++ /* Setup IRQ handler for input GPIO */ ++ irq = gpio_to_irq(gpio_input); ++ if (irq < 0) { ++ pr_err("can't map gpio%d to IRQ\n", gpio_input); ++ goto fail_release_output_gpio; ++ } ++ if (0 != (ret = request_irq(irq, gpio_latency_handler, ++ IRQF_TRIGGER_RISING, "gpio_latency_handler", NULL))) { ++ pr_err("can't request IRQ for gpio%d\n", gpio_input); ++ goto fail_release_output_gpio; ++ } ++ ++ /* Perform test */ ++ for (i = 0; i < INT_PERF_TEST_CAPTURES; i ++) { ++ /* t0 */ ++ perf_t0 = get_cycles(); ++ ++ /* Trigger interrupt */ ++ gpio_set_value(gpio_output, 1); ++ mdelay(2); ++ ++ /* Check for wrap-around and store delta */ ++ if(perf_t0 < perf_t1) { ++ delta = perf_t1 - perf_t0; ++ } else { ++ delta = perf_t1 + (UINT64_MAX - perf_t0); ++ } ++ deltas[i] = delta; ++ } ++ ++ /* Expose results to userspace */ ++ ret = copy_to_user(user_ptr, &deltas, sizeof(deltas)); ++ ++ /* Release resources */ ++ ++ free_irq(irq, NULL); ++ ++fail_release_output_gpio: ++ gpio_free(gpio_output); ++fail_release_input_gpio: ++ gpio_free(gpio_input); ++fail: ++ if (0 != ret) { ++ pr_err("%s() failed\n", __func__); ++ } ++ ++ return ret; ++} ++ ++/** ++ * gpio_sc_pm_test_int ++ * ++ * Request rising edge-triggered IRQ for SUT_GPIO_SC_0 ++ */ ++static int gpio_sc_pm_test_int(void) ++{ ++ int ret = 0; ++ int irq = -1; ++ int gpio_input = SUT_GPIO_SC_0; ++ ++ /* Setup the GPIO */ ++ if (!gpio_is_valid(gpio_input)) { ++ pr_err("gpio%d is invalid\n", gpio_input); ++ ret = -1; ++ goto fail; ++ } ++ ret = gpio_request_one(gpio_input, GPIOF_IN, "gpio_pm_test_in"); ++ if (ret) { ++ pr_err("can't request gpio%d (error %d)\n", gpio_input, ret); ++ goto fail; ++ } ++ ++ /* Setup IRQ handler for input GPIO */ ++ irq = gpio_to_irq(gpio_input); ++ if (irq < 0) { ++ pr_err("can't map gpio%d to IRQ\n", gpio_input); ++ goto fail_release_input_gpio; ++ } ++ if (0 != (ret = request_irq(irq, gpio_pm_test_handler, ++ IRQF_TRIGGER_RISING, GPIO_PM_TEST_IRQ_LABEL, NULL))) { ++ pr_err("can't request IRQ for gpio%d\n", gpio_input); ++ goto fail_release_input_gpio; ++ } ++ ++ return 0; ++ ++fail_release_input_gpio: ++ gpio_free(gpio_input); ++fail: ++ return ret; ++} ++ ++/** ++ * gpio_sc_pm_test_int ++ * ++ * Release resources reserved by gpio_sc_edge_int() ++ */ ++static int gpio_sc_pm_test_int_teardown(void) ++{ ++ int irq = -1; ++ ++ irq = gpio_to_irq(SUT_GPIO_SC_0); ++ if (irq < 0) { ++ pr_err("can't map gpio%d to IRQ\n", SUT_GPIO_SC_0); ++ } else { ++ free_irq(irq, NULL); ++ } ++ ++ gpio_free(SUT_GPIO_SC_0); ++ ++ return 0; ++} ++ ++/** ++ * gpio_sc_edge_int ++ * ++ * Request IRQ for SUT_GPIO_SC_6 and SUT_GPIO_SC_7, respectively positive-edge ++ * and negative edge-triggered. ++ * Mask the IRQs if requested. ++ */ ++static int gpio_sc_edge_int(int masking) ++{ ++ int ret = 0; ++ int irq_pos = -1, irq_neg = -1; ++ ++ if (!gpio_is_valid(SUT_GPIO_SC_6)) { ++ pr_err("gpio%d is invalid\n", SUT_GPIO_SC_6); ++ ret = -1; ++ goto fail; ++ } ++ if (!gpio_is_valid(SUT_GPIO_SC_7)) { ++ pr_err("gpio%d is invalid\n", SUT_GPIO_SC_7); ++ ret = -1; ++ goto fail; ++ } ++ ++ ret = gpio_request_one(SUT_GPIO_SC_6, GPIOF_IN, "gpio_pos_edge"); ++ if (ret) { ++ pr_err("can't request gpio%d (error %d)\n", SUT_GPIO_SC_6, ret); ++ goto fail; ++ } ++ ret = gpio_request_one(SUT_GPIO_SC_7, GPIOF_IN, "gpio_neg_edge"); ++ if (ret) { ++ pr_err("can't request gpio%d (error %d)\n", SUT_GPIO_SC_7, ret); ++ goto fail_release_first_gpio; ++ } ++ ++ irq_pos = gpio_to_irq(SUT_GPIO_SC_6); ++ if (irq_pos < 0) { ++ pr_err("can't map gpio%d to IRQ\n", SUT_GPIO_SC_6); ++ goto fail_release_second_gpio; ++ } ++ irq_neg = gpio_to_irq(SUT_GPIO_SC_7); ++ if (irq_neg < 0) { ++ pr_err("can't map gpio%d to IRQ\n", SUT_GPIO_SC_7); ++ goto fail_release_second_gpio; ++ } ++ ++ if (0 != (ret = request_irq(irq_pos, gpio_pos_edge_handler, ++ IRQF_TRIGGER_RISING, GPIO_INT_EDGE_POS_LABEL, NULL))) { ++ pr_err("can't request IRQ for gpio%d\n", SUT_GPIO_SC_6); ++ goto fail_release_second_gpio; ++ } ++ if (0 != (ret = request_irq(irq_neg, gpio_neg_edge_handler, ++ IRQF_TRIGGER_FALLING, GPIO_INT_EDGE_NEG_LABEL, NULL))) { ++ pr_err("can't request IRQ for gpio%d\n", SUT_GPIO_SC_7); ++ goto fail_release_first_gpio_irq; ++ } ++ ++ pr_info("Registered gpio%d and gpio%d IRQs\n", SUT_GPIO_SC_6, ++ SUT_GPIO_SC_7); ++ ++ if (masking) { ++ disable_irq(gpio_to_irq(SUT_GPIO_SC_6)); ++ disable_irq(gpio_to_irq(SUT_GPIO_SC_7)); ++ pr_info("Masked gpio%d and gpio%d IRQs\n", SUT_GPIO_SC_6, ++ SUT_GPIO_SC_7); ++ } ++ ++ return 0; ++ ++fail_release_first_gpio_irq: ++ free_irq(irq_pos, NULL); ++fail_release_second_gpio: ++ gpio_free(SUT_GPIO_SC_7); ++fail_release_first_gpio: ++ gpio_free(SUT_GPIO_SC_6); ++fail: ++ pr_err("%s() failed\n", __func__); ++ ++ return ret; ++} ++ ++/** ++ * gpio_sc_edge_int_teardown ++ * ++ * Release resources reserved by gpio_sc_edge_int() ++ */ ++static int gpio_sc_edge_int_teardown(void) ++{ ++ int irq_pos = -1, irq_neg = -1; ++ ++ irq_neg = gpio_to_irq(SUT_GPIO_SC_7); ++ if (irq_neg < 0) { ++ pr_err("can't map gpio%d to IRQ\n", SUT_GPIO_SC_7); ++ } else { ++ free_irq(irq_neg, NULL); ++ } ++ irq_pos = gpio_to_irq(SUT_GPIO_SC_6); ++ if (irq_pos < 0) { ++ pr_err("can't map gpio%d to IRQ\n", SUT_GPIO_SC_6); ++ } else { ++ free_irq(irq_pos, NULL); ++ } ++ ++ gpio_free(SUT_GPIO_SC_7); ++ gpio_free(SUT_GPIO_SC_6); ++ ++ return 0; ++} ++ ++/** ++ * gpio_sc_basic_int ++ * ++ * Register rising-edge interrupt handler on SUT_GPIO_SC_1 ++ */ ++static int gpio_sc_basic_int(void) ++{ ++ int ret = 0; ++ int irq = -1; ++ unsigned int gpio = SUT_GPIO_SC_1; ++ ++ if (!gpio_is_valid(gpio)) { ++ pr_err("gpio%d is invalid\n", gpio); ++ ret = -1; ++ goto fail; ++ } ++ ++ ret = gpio_request_one(gpio, GPIOF_IN, "gpio_pos_edge_basic"); ++ if (ret) { ++ pr_err("can't request gpio%d (error %d)\n", gpio, ret); ++ goto fail; ++ } ++ ++ irq = gpio_to_irq(gpio); ++ if (irq < 0) { ++ pr_err("can't map gpio%d to IRQ\n", gpio); ++ goto fail_release_gpio; ++ } ++ ++ if (0 != (ret = request_irq(irq, gpio_basic_handler, ++ IRQF_TRIGGER_RISING, GPIO_INT_BASIC_LABEL, NULL))) { ++ pr_err("can't request IRQ for gpio%d\n", gpio); ++ goto fail_release_gpio; ++ } ++ ++ pr_info("Registered gpio%d IRQ\n", gpio); ++ ++ return 0; ++ ++fail_release_gpio: ++ gpio_free(gpio); ++fail: ++ pr_err("%s() failed\n", __func__); ++ ++ return ret; ++} ++ ++/** ++ * gpio_sc_basic_int_teardown ++ * ++ * Release resources reserved by gpio_sc_basic_int() ++ */ ++static int gpio_sc_basic_int_teardown(void) ++{ ++ int irq = -1; ++ unsigned int gpio = SUT_GPIO_SC_1; ++ ++ irq = gpio_to_irq(gpio); ++ if (irq < 0) { ++ pr_err("can't map gpio%d to IRQ\n", gpio); ++ } else { ++ free_irq(irq, NULL); ++ } ++ ++ gpio_free(gpio); ++ ++ return 0; ++} ++ ++/** ++ * gpio_spidev_register ++ * ++ * Register a bitbanged SPI platform device and export a `spidev' to userspace. ++ * For North Cluster and South Cluster. ++ */ ++static int gpio_spidev_register(int north_cluster) ++{ ++ int err = -ENOMEM; ++ struct platform_device *pdev = NULL; ++ struct spi_gpio_platform_data *pdata = ++ north_cluster ? &spi_gpio_nc_data : &spi_gpio_sc_data; ++ struct spi_board_info *gpio_spi_board_info = ++ (north_cluster ? spi_gpio_nc_board_info : spi_gpio_sc_board_info); ++ ++ if (north_cluster) { ++ spi_gpio_nc_pdev = NULL; ++ } else { ++ spi_gpio_sc_pdev = NULL; ++ } ++ ++ pdev = platform_device_alloc("spi_gpio", ++ north_cluster ? GPIO_NC_BITBANG_SPI_BUS : GPIO_SC_BITBANG_SPI_BUS); ++ if (NULL == pdev) { ++ goto err_out; ++ } ++ err = platform_device_add_data(pdev, pdata, sizeof(*pdata)); ++ if (err) { ++ goto err_put_pd; ++ } ++ err = platform_device_add(pdev); ++ if (err) { ++ goto err_put_pd; ++ } ++ ++ err = spi_register_board_info(gpio_spi_board_info, ++ /* ++ * Note I pass an array here instead of a pointer in order not ++ * to break ARRAY_SIZE. ++ */ ++ ARRAY_SIZE(spi_gpio_sc_board_info)); ++ if (err) { ++ goto err_del_pd; ++ } ++ ++ if (north_cluster) { ++ spi_gpio_nc_pdev = pdev; ++ } else { ++ spi_gpio_sc_pdev = pdev; ++ } ++ ++ return 0; ++ ++err_del_pd: ++ platform_device_del(pdev); ++err_put_pd: ++ platform_device_put(pdev); ++err_out: ++ return err; ++} ++ ++/** ++ * gpio_spidev_unregister ++ * ++ * Release a bitbanged SPI platform device and its `spidev' interface. ++ * For North Cluster and South Cluster. ++ */ ++static int gpio_spidev_unregister(int north_cluster) ++{ ++ int ret = 0; ++ ++ struct platform_device *pdev = ++ (north_cluster ? spi_gpio_nc_pdev : spi_gpio_sc_pdev); ++ struct spi_board_info *gpio_spi_board_info = ++ (north_cluster ? spi_gpio_nc_board_info : spi_gpio_sc_board_info); ++ ++ ret = spi_unregister_board_info(gpio_spi_board_info, ++ /* ++ * Note I pass an array here instead of a pointer in order not ++ * to break ARRAY_SIZE. ++ */ ++ ARRAY_SIZE(spi_gpio_sc_board_info)); ++ ++ if (0 == ret) { ++ platform_device_unregister(pdev); ++ } ++ ++ if (north_cluster) { ++ spi_gpio_nc_pdev = NULL; ++ } else { ++ spi_gpio_sc_pdev = NULL; ++ } ++ ++ return ret; ++} ++ ++/** ++ * gip_system_power_transition ++ * ++ * @param state: 0 if transition to S3, !0 if transition to S0 ++ * @return 0 success < 0 failure ++ * ++ * Exercise system-wide suspend/resume power management transitions. ++ * ++ */ ++static int gip_system_power_transition(int state) ++{ ++ struct pci_dev *gip = pci_get_device(PCI_VENDOR_ID_INTEL, 0x0934, NULL); ++ if (NULL == gip) { ++ pr_err("can't find GIP PCI device\n"); ++ return -ENOENT; ++ } ++ ++ if (0 == state) { ++ gip->driver->driver.pm->suspend(&gip->dev); ++ } else { ++ gip->driver->driver.pm->resume(&gip->dev); ++ } ++ ++ /* Decrement reference count of PCI device */ ++ if (NULL != pci_get_device(PCI_VENDOR_ID_INTEL, 0x0934, gip)) { ++ pr_warn("found duplicate of GIP PCI device?!\n"); ++ } ++ ++ return 0; ++} ++ ++/** ++ * gpio_sc_debounce ++ * ++ * Enable GPIO debounce functionality for SC_GPIO_1 (edge and level triggered) ++ * ++ */ ++static int gpio_sc_debounce(int level) ++{ ++ int ret = 0; ++ int irq = -1; ++ int gpio = SUT_GPIO_SC_0; ++ ++ if (!gpio_is_valid(gpio)) { ++ pr_err("gpio%d is invalid\n", gpio); ++ ret = -1; ++ goto fail; ++ } ++ ++ ret = gpio_request_one(gpio, GPIOF_IN, ++ level ? "gpio_level_mask" : "gpio_edge_mask"); ++ if (ret) { ++ pr_err("can't request gpio%d (error %d)\n", gpio, ret); ++ goto fail; ++ } ++ ++ irq = gpio_to_irq(gpio); ++ if (irq < 0) { ++ pr_err("can't map gpio%d to IRQ\n", gpio); ++ goto fail_release_gpio; ++ } ++ ++ /* ++ * Register IRQ. gpio_pos_edge_handler will do for both level and edge ++ * interrupts, as it's nooping. ++ */ ++ if (0 != (ret = request_irq(irq, gpio_pos_edge_handler, ++ level ? IRQF_TRIGGER_HIGH : IRQF_TRIGGER_RISING, ++ level ? GPIO_INT_LEVEL_HIGH_LABEL : GPIO_INT_EDGE_POS_LABEL, ++ NULL))) { ++ pr_err("can't request IRQ for gpio%d\n", gpio); ++ goto fail_release_gpio; ++ } ++ ++ /* Set debounce */ ++ if (0 != (ret = gpio_set_debounce(gpio, 1))) { ++ pr_err("can't set debounce for gpio%d\n", gpio); ++ goto fail_free_irq; ++ } ++ ++ return 0; ++ ++fail_free_irq: ++ free_irq(irq, NULL); ++fail_release_gpio: ++ gpio_free(gpio); ++fail: ++ pr_err("%s() failed\n", __func__); ++ ++ return ret; ++} ++ ++/** ++ * gpio_sc_debounce_teardown ++ * ++ * Undo gpio_sc_debounce ++ * ++ */ ++static int gpio_sc_debounce_teardown(int level) ++{ ++ int irq = -1; ++ unsigned int gpio = SUT_GPIO_SC_0; ++ ++ irq = gpio_to_irq(gpio); ++ if (irq < 0) { ++ pr_err("can't map gpio%d to IRQ\n", gpio); ++ } else { ++ free_irq(irq, NULL); ++ } ++ ++ gpio_free(gpio); ++ ++ return 0; ++} ++ ++/* ++ * File ops ++ */ ++static long gip_test_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ int ret = -EINVAL; ++ ++ switch (cmd) { ++ case IOCTL_CLN_GPIO_11: ++ /* Edge-triggered interrupts */ ++ ret = gpio_sc_edge_int(0); ++ break; ++ case IOCTL_CLN_GPIO_11_CLEANUP: ++ /* Edge-triggered interrupts cleanup */ ++ ret = gpio_sc_edge_int_teardown(); ++ break; ++ case IOCTL_CLN_GPIO_12: ++ /* Edge-triggered interrupts (masking) */ ++ ret = gpio_sc_edge_int(1); ++ break; ++ case IOCTL_CLN_GPIO_12_CLEANUP: ++ /* Edge-triggered interrupts (masking) cleanup */ ++ ret = gpio_sc_edge_int_teardown(); ++ break; ++ case IOCTL_CLN_GPIO_13: ++ /* GPIO debounce (edge) */ ++ ret = gpio_sc_debounce(0); ++ break; ++ case IOCTL_CLN_GPIO_13_CLEANUP: ++ /* GPIO debounce cleanup (edge) */ ++ ret = gpio_sc_debounce_teardown(0); ++ break; ++ case IOCTL_CLN_GPIO_14: ++ /* High-level triggered interrupts */ ++ ret = gpio_sc_level_int(1, 0); ++ break; ++ case IOCTL_CLN_GPIO_14_CLEANUP: ++ /* High-level triggered interrupts cleanup */ ++ ret = gpio_sc_level_int_teardown(); ++ break; ++ case IOCTL_CLN_GPIO_15: ++ /* Low-level triggered interrupts */ ++ ret = gpio_sc_level_int(0, 0); ++ break; ++ case IOCTL_CLN_GPIO_15_CLEANUP: ++ /*Low-level triggered interrupts cleanup */ ++ ret = gpio_sc_level_int_teardown(); ++ break; ++ case IOCTL_CLN_GPIO_16: ++ /* Level triggered interrupts (masking) */ ++ ret = gpio_sc_level_int(1, 1); ++ break; ++ case IOCTL_CLN_GPIO_16_CLEANUP: ++ /* Level triggered interrupts (masking) cleanup */ ++ ret = gpio_sc_level_int_teardown(); ++ break; ++ case IOCTL_CLN_GPIO_17: ++ /* GPIO debounce (level) */ ++ ret = gpio_sc_debounce(1); ++ break; ++ case IOCTL_CLN_GPIO_17_CLEANUP: ++ /* GPIO debounce cleanup (level) */ ++ ret = gpio_sc_debounce_teardown(1); ++ break; ++ case IOCTL_CLN_GPIO_19: ++ /* Register IRQ for SC_GPIO0 (PM transitions test) */ ++ ret = gpio_sc_pm_test_int(); ++ break; ++ case IOCTL_CLN_GPIO_19_CLEANUP: ++ /* Free IRQ for SC_GPIO0 (PM transitions test) */ ++ ret = gpio_sc_pm_test_int_teardown(); ++ break; ++ case IOCTL_CLN_GPIO_20: ++ /* NC bitbanged SPI */ ++ ret = gpio_spidev_register(1); ++ break; ++ case IOCTL_CLN_GPIO_20_CLEANUP: ++ /* NC bitbanged SPI cleanup */ ++ ret = gpio_spidev_unregister(1); ++ break; ++ case IOCTL_CLN_GPIO_21: ++ /* SC bitbanged SPI */ ++ ret = gpio_spidev_register(0); ++ break; ++ case IOCTL_CLN_GPIO_21_CLEANUP: ++ /* SC bitbanged SPI cleanup */ ++ ret = gpio_spidev_unregister(0); ++ break; ++ case IOCTL_CLN_GPIO_24: ++ /* ++ * SC GPIO interrupt performance test. ++ * Note it's shared between CLN_GPIO_24 and CLN_GPIO_25 ++ * plus it doesn't need any cleanup call. ++ */ ++ ret = gpio_sc_interrupt_perf(arg); ++ break; ++ case IOCTL_CLN_GPIO_26: ++ /* Interrupt for basic loopback test */ ++ ret = gpio_sc_basic_int(); ++ break; ++ case IOCTL_CLN_GPIO_26_CLEANUP: ++ /* Interrupt for basic loopback test cleanup */ ++ ret = gpio_sc_basic_int_teardown(); ++ break; ++ case IOCTL_CLN_GIP_SYSTEM_SUSPEND: ++ ret = gip_system_power_transition(0); ++ break; ++ case IOCTL_CLN_GIP_SYSTEM_RESUME: ++ ret = gip_system_power_transition(1); ++ break; ++ default: ++ break; ++ } ++ ++ return ret; ++} ++ ++static int gip_test_open(struct inode *inode, struct file *file) ++{ ++ mutex_lock(&gip_test_mutex); ++ nonseekable_open(inode, file); ++ ++ if (mutex_lock_interruptible(&gip_test_dev.open_lock)) { ++ mutex_unlock(&gip_test_mutex); ++ return -ERESTARTSYS; ++ } ++ ++ if (gip_test_dev.opened) { ++ mutex_unlock(&gip_test_dev.open_lock); ++ mutex_unlock(&gip_test_mutex); ++ return -EINVAL; ++ } ++ ++ gip_test_dev.opened++; ++ mutex_unlock(&gip_test_dev.open_lock); ++ mutex_unlock(&gip_test_mutex); ++ return 0; ++} ++ ++static int gip_test_release(struct inode *inode, struct file *file) ++{ ++ mutex_lock(&gip_test_dev.open_lock); ++ gip_test_dev.opened = 0; ++ mutex_unlock(&gip_test_dev.open_lock); ++ ++ return 0; ++} ++ ++static const struct file_operations gip_test_file_ops = { ++ .open = gip_test_open, ++ .release = gip_test_release, ++ .unlocked_ioctl = gip_test_ioctl, ++ .llseek = no_llseek, ++}; ++ ++/** ++ * intel_cln_gip_test_probe ++ * ++ * @param pdev: Platform device ++ * @return 0 success < 0 failure ++ * ++ * Callback from platform sub-system to probe ++ */ ++static int intel_cln_gip_test_probe(struct platform_device * pdev) ++{ ++ int retval = 0; ++ unsigned int minor = 0; ++ ++ mutex_init(&gip_test_dev.open_lock); ++ cdev_init(&gip_test_dev.cdev, &gip_test_file_ops); ++ gip_test_dev.cdev.owner = THIS_MODULE; ++ ++ retval = cdev_add(&gip_test_dev.cdev, MKDEV(gip_test_major, minor), 1); ++ if (retval) { ++ printk(KERN_ERR "chardev registration failed\n"); ++ return -EINVAL; ++ } ++ if (IS_ERR(device_create(gip_test_class, NULL, ++ MKDEV(gip_test_major, minor), NULL, ++ "giptest%u", minor))){ ++ dev_err(&pdev->dev, "can't create device\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++ ++} ++ ++static int intel_cln_gip_test_remove(struct platform_device * pdev) ++{ ++ unsigned int minor = MINOR(gip_test_dev.cdev.dev); ++ ++ device_destroy(gip_test_class, MKDEV(gip_test_major, minor)); ++ cdev_del(&gip_test_dev.cdev); ++ ++ class_destroy(gip_test_class); ++ ++ return 0; ++} ++ ++/* ++ * Platform structures useful for interface to PM subsystem ++ */ ++static struct platform_driver intel_cln_gip_test_driver = { ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++ .remove = intel_cln_gip_test_remove, ++}; ++ ++/** ++ * intel_cln_gip_test_init ++ * ++ * Load module. ++ */ ++static int __init intel_cln_gip_test_init(void) ++{ ++ int retval = 0; ++ dev_t dev; ++ ++ gip_test_class = class_create(THIS_MODULE,"cln_gip_test"); ++ if (IS_ERR(gip_test_class)) { ++ retval = PTR_ERR(gip_test_class); ++ printk(KERN_ERR "gip_test: can't register gip_test class\n"); ++ goto err; ++ } ++ ++ retval = alloc_chrdev_region(&dev, 0, 1, "gip_test"); ++ if (retval) { ++ printk(KERN_ERR "earam_test: can't register character device\n"); ++ goto err_class; ++ } ++ gip_test_major = MAJOR(dev); ++ ++ memset(&gip_test_dev, 0x00, sizeof(gip_test_dev)); ++ gip_test_dev.pldev = platform_create_bundle( ++ &intel_cln_gip_test_driver, intel_cln_gip_test_probe, NULL, 0, NULL, 0); ++ ++ if(IS_ERR(gip_test_dev.pldev)){ ++ printk(KERN_ERR "platform_create_bundle fail!\n"); ++ retval = PTR_ERR(gip_test_dev.pldev); ++ goto err_class; ++ } ++ ++ return 0; ++ ++err_class: ++ class_destroy(gip_test_class); ++err: ++ return retval; ++} ++ ++static void __exit intel_cln_gip_test_exit(void) ++{ ++ platform_device_unregister(gip_test_dev.pldev); ++ platform_driver_unregister(&intel_cln_gip_test_driver); ++} ++ ++module_init(intel_cln_gip_test_init); ++module_exit(intel_cln_gip_test_exit); ++ ++MODULE_AUTHOR("Josef Ahmad <josef.ahmad@intel.com>"); ++MODULE_DESCRIPTION("Clanton GIP test module"); ++MODULE_LICENSE("Dual BSD/GPL"); ++ +diff --git a/drivers/mfd/lpc_sch.c b/drivers/mfd/lpc_sch.c +index 5624fcb..4fbb1fd 100644 +--- a/drivers/mfd/lpc_sch.c ++++ b/drivers/mfd/lpc_sch.c +@@ -41,21 +41,41 @@ + #define WDTBASE 0x84 + #define WDT_IO_SIZE 64 + ++/* BIOS control reg */ ++#define LPC_BIOS_CNTL 0xD8 ++#define LPC_BIOS_CNTL_WE 0x01 ++ ++/* Root complex base address derived registers */ ++#define RCBA_BASE 0xF0 ++ + static struct resource smbus_sch_resource = { + .flags = IORESOURCE_IO, + }; + +- + static struct resource gpio_sch_resource = { + .flags = IORESOURCE_IO, + }; + ++static struct resource spi_res = { ++ .flags = IORESOURCE_MEM, ++ .start = 0, ++ .end = 0, ++}; ++ ++static struct platform_device lpc_sch_spi = { ++ .name = "spi-lpc-sch", ++ .id = -1, ++ .resource = &spi_res, ++}; ++ + static struct mfd_cell lpc_sch_cells[] = { ++#ifndef CONFIG_INTEL_QUARK_X1000_SOC + { + .name = "isch_smbus", + .num_resources = 1, + .resources = &smbus_sch_resource, + }, ++#endif + { + .name = "sch_gpio", + .num_resources = 1, +@@ -79,6 +99,7 @@ static DEFINE_PCI_DEVICE_TABLE(lpc_sch_ids) = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ITC_LPC) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CENTERTON_ILB) }, ++ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CLANTON_ILB) }, + { 0, } + }; + MODULE_DEVICE_TABLE(pci, lpc_sch_ids); +@@ -88,22 +109,26 @@ static int lpc_sch_probe(struct pci_dev *dev, + { + unsigned int base_addr_cfg; + unsigned short base_addr; ++ u32 rcba_base, bios_cntl; + int i; + int ret; + +- pci_read_config_dword(dev, SMBASE, &base_addr_cfg); +- if (!(base_addr_cfg & (1 << 31))) { +- dev_err(&dev->dev, "Decode of the SMBus I/O range disabled\n"); +- return -ENODEV; +- } +- base_addr = (unsigned short)base_addr_cfg; +- if (base_addr == 0) { +- dev_err(&dev->dev, "I/O space for SMBus uninitialized\n"); +- return -ENODEV; +- } ++ /* Clanton does not support iLB SMBUS */ ++ if (id->device != PCI_DEVICE_ID_INTEL_CLANTON_ILB) { ++ pci_read_config_dword(dev, SMBASE, &base_addr_cfg); ++ if (!(base_addr_cfg & (1 << 31))) { ++ dev_err(&dev->dev, "Decode of the SMBus I/O range disabled\n"); ++ return -ENODEV; ++ } ++ base_addr = (unsigned short)base_addr_cfg; ++ if (base_addr == 0) { ++ dev_err(&dev->dev, "I/O space for SMBus uninitialized\n"); ++ return -ENODEV; ++ } + +- smbus_sch_resource.start = base_addr; +- smbus_sch_resource.end = base_addr + SMBUS_IO_SIZE - 1; ++ smbus_sch_resource.start = base_addr; ++ smbus_sch_resource.end = base_addr + SMBUS_IO_SIZE - 1; ++ } + + pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg); + if (!(base_addr_cfg & (1 << 31))) { +@@ -132,6 +157,31 @@ static int lpc_sch_probe(struct pci_dev *dev, + if (ret) + goto out_dev; + ++ /* Add RCBA SPI device */ ++ if (id->device == PCI_DEVICE_ID_INTEL_CLANTON_ILB) { ++ pci_read_config_dword(dev, LPC_BIOS_CNTL, &bios_cntl); ++ pr_info("%s BIOS_CNTL 0x%08x\n", __func__, bios_cntl); ++ ++ /* Enable flash write */ ++ bios_cntl |= LPC_BIOS_CNTL_WE; ++ pci_write_config_dword(dev, LPC_BIOS_CNTL, bios_cntl); ++ ++ /* Verify */ ++ pci_read_config_dword(dev, LPC_BIOS_CNTL, &bios_cntl); ++ pr_info("%s new BIOS_CNTL 0x%08x\n", __func__, bios_cntl); ++ } ++ ++ pci_read_config_dword(dev, RCBA_BASE, &rcba_base); ++ rcba_base &= 0xFFFFC000; ++ spi_res.start = rcba_base + 0x3020; ++ spi_res.end = rcba_base + 0x3088; ++ pr_info("%s RCBA @ 0x%08x\n", __func__, rcba_base); ++ ret = platform_device_register(&lpc_sch_spi); ++ if (ret < 0){ ++ pr_err("unable to register %s plat dev\n", lpc_sch_spi.name); ++ goto out_dev; ++ } ++ + if (id->device == PCI_DEVICE_ID_INTEL_ITC_LPC + || id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB) { + pci_read_config_dword(dev, WDTBASE, &base_addr_cfg); +diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c +index c7dd0cb..4352c6a 100644 +--- a/drivers/mmc/host/sdhci-pci.c ++++ b/drivers/mmc/host/sdhci-pci.c +@@ -162,6 +162,10 @@ static const struct sdhci_pci_fixes sdhci_cafe = { + SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, + }; + ++static const struct sdhci_pci_fixes sdhci_intel_cln = { ++ .quirks = SDHCI_QUIRK_NO_HISPD_BIT, ++}; ++ + static int mrst_hc_probe_slot(struct sdhci_pci_slot *slot) + { + slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA; +@@ -777,6 +781,14 @@ static const struct pci_device_id pci_ids[] = { + + { + .vendor = PCI_VENDOR_ID_INTEL, ++ .device = PCI_DEVICE_ID_INTEL_CLN_SD, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ .driver_data = (kernel_ulong_t)&sdhci_intel_cln, ++ }, ++ ++ { ++ .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_MRST_SD0, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, +diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig +index 46dcb54..7af4129 100644 +--- a/drivers/mtd/devices/Kconfig ++++ b/drivers/mtd/devices/Kconfig +@@ -159,6 +159,11 @@ config MTD_MTDRAM + provide storage. You probably want to say 'N' unless you're + testing stuff. + ++config MTD_MTD_CLN_ROM ++ bool "Simple R/O drive for SPI flash in Clanton x86 legacy block" ++ help ++ Driver to enable reading directly from legacy block SPI /sketch part ++ + config MTDRAM_TOTAL_SIZE + int "MTDRAM device size in KiB" + depends on MTD_MTDRAM +diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile +index 395733a..4c8e399 100644 +--- a/drivers/mtd/devices/Makefile ++++ b/drivers/mtd/devices/Makefile +@@ -20,5 +20,4 @@ obj-$(CONFIG_MTD_M25P80) += m25p80.o + obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o + obj-$(CONFIG_MTD_SST25L) += sst25l.o + obj-$(CONFIG_MTD_BCM47XXSFLASH) += bcm47xxsflash.o +- +-CFLAGS_docg3.o += -I$(src) +\ No newline at end of file ++CFLAGS_docg3.o += -I$(src) +diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig +index 1164930..4e10f27 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig ++++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig +@@ -26,8 +26,8 @@ config STMMAC_PLATFORM + If unsure, say N. + + config STMMAC_PCI +- bool "STMMAC PCI bus support (EXPERIMENTAL)" +- depends on STMMAC_ETH && PCI && EXPERIMENTAL ++ bool "STMMAC PCI bus support" ++ depends on STMMAC_ETH && PCI + ---help--- + This is to select the Synopsys DWMAC available on PCI devices, + if you have a controller with this interface, say Y or M here. +@@ -54,6 +54,27 @@ config STMMAC_DA + By default, the DMA arbitration scheme is based on Round-robin + (rx:tx priority is 1:1). + ++config STMMAC_PTP ++ bool "STMMAC PTP (1588-2005) Clock Support" ++ default n ++ depends on EXPERIMENTAL ++ select PPS ++ select PTP_1588_CLOCK ++ ---help--- ++ Say Y here if you want support for 1588 Timestamping with a ++ Clanton device, using the PTP 1588 Clock support. This is ++ required to enable timestamping support for the device. ++ ++ If unsure, say N. ++ ++config STMMAC_PTP_CLK_MHZ ++ depends on STMMAC_PTP ++ int "Reference clock in Mhz" ++ default 50 ++ ---help--- ++ Frequency if MHz of the reference clock used to derive PTP time ++ locally. Permissable values are 1 - 255 inclusive ++ + choice + prompt "Select the DMA TX/RX descriptor operating modes" + depends on STMMAC_ETH +diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile +index c8e8ea6..0995db5 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/Makefile ++++ b/drivers/net/ethernet/stmicro/stmmac/Makefile +@@ -3,6 +3,7 @@ stmmac-$(CONFIG_STMMAC_RING) += ring_mode.o + stmmac-$(CONFIG_STMMAC_CHAINED) += chain_mode.o + stmmac-$(CONFIG_STMMAC_PLATFORM) += stmmac_platform.o + stmmac-$(CONFIG_STMMAC_PCI) += stmmac_pci.o ++stmmac-$(CONFIG_STMMAC_PTP) += stmmac_ptp.o + stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o \ + dwmac_lib.o dwmac1000_core.o dwmac1000_dma.o \ + dwmac100_core.o dwmac100_dma.o enh_desc.o norm_desc.o \ +diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h +index 186d148..ad16e73 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/common.h ++++ b/drivers/net/ethernet/stmicro/stmmac/common.h +@@ -32,9 +32,15 @@ + #include <linux/init.h> + #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) + #define STMMAC_VLAN_TAG_USED ++#if defined(CONFIG_INTEL_QUARK_X1000_SOC) ++#define STMMAC_VLAN_HASH ++#endif + #include <linux/if_vlan.h> + #endif + ++#if defined(STMMAC_VLAN_HASH) || defined(CONFIG_STMMAC_PTP) ++#define STMMAC_ATDS_USED ++#endif + #include "descs.h" + #include "mmc.h" + +@@ -319,15 +325,16 @@ struct stmmac_dma_ops { + void (*rx_watchdog) (void __iomem *ioaddr, u32 riwt); + }; + ++struct stmmac_priv; + struct stmmac_ops { + /* MAC core initialization */ + void (*core_init) (void __iomem *ioaddr) ____cacheline_aligned; + /* Enable and verify that the IPC module is supported */ +- int (*rx_ipc) (void __iomem *ioaddr); ++ int (*set_rx_ipc) (void __iomem *ioaddr, bool on); + /* Dump MAC registers */ + void (*dump_regs) (void __iomem *ioaddr); + /* Handle extra events on specific interrupts hw dependent */ +- int (*host_irq_status) (void __iomem *ioaddr); ++ int (*host_irq_status) (struct stmmac_priv * priv); + /* Multicast filter setting */ + void (*set_filter) (struct net_device *dev, int id); + /* Flow control setting */ +@@ -340,6 +347,9 @@ struct stmmac_ops { + unsigned int reg_n); + void (*get_umac_addr) (void __iomem *ioaddr, unsigned char *addr, + unsigned int reg_n); ++ /* Enable/Disable VLAN Hash filters */ ++ int (*vlan_rx_add_vid)(struct stmmac_priv *priv, unsigned short vid); ++ int (*vlan_rx_kill_vid)(struct stmmac_priv *priv, unsigned short vid); + void (*set_eee_mode) (void __iomem *ioaddr); + void (*reset_eee_mode) (void __iomem *ioaddr); + void (*set_eee_timer) (void __iomem *ioaddr, int ls, int tw); +diff --git a/drivers/net/ethernet/stmicro/stmmac/descs.h b/drivers/net/ethernet/stmicro/stmmac/descs.h +index 223adf9..ce08163 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/descs.h ++++ b/drivers/net/ethernet/stmicro/stmmac/descs.h +@@ -25,8 +25,8 @@ + #define __DESCS_H__ + + struct dma_desc { +- /* Receive descriptor */ + union { ++ /* Receive descriptor */ + struct { + /* RDES0 */ + u32 payload_csum_error:1; +@@ -160,6 +160,49 @@ struct dma_desc { + } des01; + unsigned int des2; + unsigned int des3; ++ ++ /* Enhanced mode - with VLAN/1588-2005/IPC CHKSUM offload */ ++ #if defined(STMMAC_ATDS_USED) ++ union { ++ /* Receive descriptor */ ++ struct { ++ /* RDES4 */ ++ u32 ip_payload_type:3; ++ u32 ip_header_error:1; ++ u32 ip_payload_error:1; ++ u32 ip_checksum_bypassed:1; ++ u32 ipv4_packet_received:1; ++ u32 ipv6_packet_received:1; ++ u32 message_type:4; ++ u32 ptp_frame_type:1; ++ u32 ptp_version:1; ++ u32 timestamp_dropped:1; ++ u32 reserved1:1; ++ u32 av_packet_received:1; ++ u32 av_tagged_packet_received:1; ++ u32 vlan_tag_priority_value:3; ++ u32 reserved2:3; ++ u32 layer3_filter_match:1; ++ u32 layer4_filter_match:1; ++ u32 layer3_layer4_filter_num_matched:2; ++ u32 reserved3:4; ++ ++ /* RDES5 */ ++ u32 reserved4; ++ }erx; ++ ++ /* Transmit descriptor */ ++ struct { ++ /* TDES4 */ ++ u32 reserved1; ++ ++ /* TDES5 */ ++ u32 reserved2; ++ } etx; ++ } des05; ++ unsigned int ts_lo; /* des6 Tx/Rx timestmp lo */ ++ unsigned int ts_hi; /* des7 Tx/Rx timestamp hi */ ++ #endif + }; + + /* Transmit checksum insertion control */ +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h +index 7ad56af..3042098 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h +@@ -50,7 +50,14 @@ enum dwmac1000_irq_status { + rgmii_irq = 0x0001, + }; + #define GMAC_INT_MASK 0x0000003c /* interrupt mask register */ +- ++#define GMAC_INT_MASK_LPIIM 0x00000200 /* LPI Interrupt Mask */ ++#define GMAC_INT_MASK_TSIM 0x00000100 /* Timestamp Interrupt Mask */ ++#define GMAC_INT_MASK_PMTIM 0x00000004 /* PMT Interrupt Mask */ ++#define GMAC_INT_MASK_PCSANCIM 0x00000002 /* PCS AN Completion */ ++#define GMAC_INT_MASK_PCSLCHGIM 0x00000001 /* PCS Link Status */ ++#define GMAC_INT_MASK_DEFAULT GMAC_INT_MASK_PCSLCHGIM | GMAC_INT_MASK_PCSANCIM\ ++ | GMAC_INT_MASK_PMTIM | GMAC_INT_MASK_TSIM\ ++ | GMAC_INT_MASK_LPIIM + /* PMT Control and Status */ + #define GMAC_PMT 0x0000002c + enum power_event { +@@ -135,6 +142,7 @@ enum inter_frame_gap { + #define GMAC_FRAME_FILTER_SAIF 0x00000100 /* Inverse Filtering */ + #define GMAC_FRAME_FILTER_SAF 0x00000200 /* Source Address Filter */ + #define GMAC_FRAME_FILTER_HPF 0x00000400 /* Hash or perfect Filter */ ++#define GMAC_FRAME_FILTER_VTFE 0x00010000 /* VLAN Tag Filter Enable */ + #define GMAC_FRAME_FILTER_RA 0x80000000 /* Receive all mode */ + /* GMII ADDR defines */ + #define GMAC_MII_ADDR_WRITE 0x00000002 /* MII Write */ +@@ -145,11 +153,17 @@ enum inter_frame_gap { + #define GMAC_FLOW_CTRL_RFE 0x00000004 /* Rx Flow Control Enable */ + #define GMAC_FLOW_CTRL_TFE 0x00000002 /* Tx Flow Control Enable */ + #define GMAC_FLOW_CTRL_FCB_BPA 0x00000001 /* Flow Control Busy ... */ +- ++/* GMAC VLAN TAG defines */ ++#define GMAC_VLAN_TAG_VTHM 0x00080000 /* Hash Table Match Enable */ ++#define GMAC_VLAN_TAG_ESVL 0x00040000 /* Enable S-VLAN */ ++#define GMAC_VLAN_TAG_VTIM 0x00020000 /* VLAN Tag inverse match */ ++#define GMAC_VLAN_TAG_ETV 0x00010000 /* Enable 12-bit tag comp */ ++#define GMAC_VLAN_TAG_VLMASK 0x0000FFFF /* VLAN tag ID for Rx frames */ + /*--- DMA BLOCK defines ---*/ + /* DMA Bus Mode register defines */ + #define DMA_BUS_MODE_SFT_RESET 0x00000001 /* Software Reset */ + #define DMA_BUS_MODE_DA 0x00000002 /* Arbitration scheme */ ++#define DMA_BUS_MODE_ATDS 0X00000080 /* Alternate Descriptor Size */ + #define DMA_BUS_MODE_DSL_MASK 0x0000007c /* Descriptor Skip Length */ + #define DMA_BUS_MODE_DSL_SHIFT 2 /* (in DWORDS) */ + /* Programmable burst length (passed thorugh platform)*/ +@@ -169,6 +183,7 @@ enum rx_tx_priority_ratio { + #define DMA_BUS_MODE_USP 0x00800000 + #define DMA_BUS_MODE_PBL 0x01000000 + #define DMA_BUS_MODE_AAL 0x02000000 ++#define DMA_BUS_MODE_RIX 0x80000000 + + /* DMA CRS Control and Status Register Mapping */ + #define DMA_HOST_TX_DESC 0x00001048 /* Current Host Tx descriptor */ +@@ -230,5 +245,50 @@ enum rtc_control { + #define GMAC_MMC_TX_INTR 0x108 + #define GMAC_MMC_RX_CSUM_OFFLOAD 0x208 + ++/* VLAN Hash register offset */ ++#define GMAC_VLAN_TAG_REP 0x584 ++#define GMAC_VLAN_HASH 0x588 ++#define GMAC_VLAN_HASH_MAXID 0x0F ++ ++/***************** 1588 regs *****************/ ++#define GMAC_TS_CTRL 0x700 /* Timestamp control reg */ ++#define GMAC_TS_CTRL_TSENA 0x00000001 /* Timestamp enable */ ++#define GMAC_TS_CTRL_TSCFUPDT 0x00000002 /* Timestamp fine/coarse */ ++#define GMAC_TS_CTRL_TSINT 0x00000004 /* Timestamp initialise */ ++#define GMAC_TS_CTRL_TSUPDT 0x00000008 /* Timestamp update */ ++#define GMAC_TS_CTRL_TSTRIG 0x00000010 /* Timestamp trigger en */ ++#define GMAC_TS_CTRL_TSADDREG 0x00000020 /* Timestamp addreg update */ ++#define GMAC_TS_CTRL_TSENALL 0x00000100 /* Timestamp RX enable all */ ++#define GMAC_TS_CTRL_TSCTRLSSR 0x00000200 /* Timestamp rollover ctr */ ++#define GMAC_TS_CTRL_TSVER2ENA 0x00000400 /* Timestamp PTP v2 en */ ++#define GMAC_TS_CTRL_TSIPENA 0x00000800 /* Timestamp PTP over eth */ ++#define GMAC_TS_CTRL_TSIPV6ENA 0x00001000 /* Timestamp over IPV6 */ ++#define GMAC_TS_CTRL_TSIPV4ENA 0x00002000 /* Timestamp over IPV4 */ ++#define GMAC_TS_CTRL_TSEVNTENA 0x00004000 /* Timestamp event only */ ++#define GMAC_TS_CTRL_TSMSTRENA 0x00008000 /* Timestamp master enable */ ++#define GMAC_TS_CTRL_SNTYPSEL0 0x00000000 /* Timestamp type 0 snapshot */ ++#define GMAC_TS_CTRL_SNTYPSEL1 0x00010000 /* Timestamp type 1 snapshot */ ++#define GMAC_TS_CTRL_SNTYPSEL2 0x00020000 /* Timestamp type 2 snapshot */ ++#define GMAC_TS_CTRL_SNTYPSEL3 0x00030000 /* Timestamp type 3 snapshot */ ++#define GMAC_TS_CTRL_TSENMACADR 0x00040000 /* Timestamp mac filter en */ ++#define GMAC_TS_CTRL_ATSFC 0x01000000 /* Timestamp aux fifo clear */ ++#define GMAC_TS_CTRL_ATSEN0 0x02000000 /* Timestamp aux0 snap en */ ++#define GMAC_TS_CTRL_ATSEN1 0x04000000 /* Timestamp aux1 snap en */ ++#define GMAC_TS_CTRL_ATSEN2 0x08000000 /* Timestamp aux2 snap en */ ++#define GMAC_TS_CTRL_ATSEN3 0x10000000 /* Timestamp aux3 enable */ ++#define GMAC_SS_INC 0x704 /* Sub-second increment reg */ ++#define GMAC_ST_SEC 0x708 /* System time seconds */ ++#define GMAC_ST_NSEC 0x70C /* System time nseconds */ ++#define GMAC_ST_SECUP 0x710 /* System time sec-update */ ++#define GMAC_ST_NSECUP 0x714 /* System time nsec-update */ ++#define GMAC_TS_APPEND 0x718 /* Timestamp append */ ++#define GMAC_TT_SEC 0x71C /* Target time seconds */ ++#define GMAC_TT_NSEC 0x720 /* Target time nseconds */ ++#define GMAC_ST_HWSEC 0x724 /* System time high word sec */ ++#define GMAC_ST_TS_STAT 0x728 /* Timestamp status */ ++#define GMAC_PPS_CTRL 0x72C /* PPS signal output control */ ++#define GMAC_AUXTS_NSEC 0x730 /* Aux timestamp counter nsec */ ++#define GMAC_AUXTS_SEC 0x734 /* Aux timestamp counter sec */ ++ + extern const struct stmmac_dma_ops dwmac1000_dma_ops; + #endif /* __DWMAC1000_H__ */ +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c +index bfe0226..b6d04ca 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c +@@ -30,6 +30,7 @@ + #include <linux/slab.h> + #include <asm/io.h> + #include "dwmac1000.h" ++#include "stmmac.h" + + static void dwmac1000_core_init(void __iomem *ioaddr) + { +@@ -38,7 +39,7 @@ static void dwmac1000_core_init(void __iomem *ioaddr) + writel(value, ioaddr + GMAC_CONTROL); + + /* Mask GMAC interrupts */ +- writel(0x207, ioaddr + GMAC_INT_MASK); ++ writel(GMAC_INT_MASK_DEFAULT, ioaddr + GMAC_INT_MASK); + + #ifdef STMMAC_VLAN_TAG_USED + /* Tag detection without filtering */ +@@ -46,11 +47,15 @@ static void dwmac1000_core_init(void __iomem *ioaddr) + #endif + } + +-static int dwmac1000_rx_ipc_enable(void __iomem *ioaddr) ++static int dwmac1000_set_rx_ipc(void __iomem *ioaddr, bool on) + { + u32 value = readl(ioaddr + GMAC_CONTROL); + +- value |= GMAC_CONTROL_IPC; ++ if(on == true){ ++ value |= GMAC_CONTROL_IPC; ++ }else{ ++ value &= ~GMAC_CONTROL_IPC; ++ } + writel(value, ioaddr + GMAC_CONTROL); + + value = readl(ioaddr + GMAC_CONTROL); +@@ -87,6 +92,7 @@ static void dwmac1000_get_umac_addr(void __iomem *ioaddr, unsigned char *addr, + static void dwmac1000_set_filter(struct net_device *dev, int id) + { + void __iomem *ioaddr = (void __iomem *) dev->base_addr; ++ struct stmmac_priv *priv = netdev_priv(dev); + unsigned int value = 0; + unsigned int perfect_addr_number; + +@@ -147,6 +153,10 @@ static void dwmac1000_set_filter(struct net_device *dev, int id) + /* Enable Receive all mode (to debug filtering_fail errors) */ + value |= GMAC_FRAME_FILTER_RA; + #endif ++ if (priv->active_vlans != 0){ ++ /* VLAN hash filtering is active on this interface */ ++ value |= GMAC_FRAME_FILTER_VTFE; ++ } + writel(value, ioaddr + GMAC_FRAME_FILTER); + + CHIP_DBG(KERN_INFO "\tFrame Filter reg: 0x%08x\n\tHash regs: " +@@ -194,38 +204,42 @@ static void dwmac1000_pmt(void __iomem *ioaddr, unsigned long mode) + } + + +-static int dwmac1000_irq_status(void __iomem *ioaddr) ++#ifndef CONFIG_STMMAC_PTP ++#define stmmac_ptp_check_pps_event(x){} ++#endif ++ ++static int dwmac1000_irq_status(struct stmmac_priv *priv) + { +- u32 intr_status = readl(ioaddr + GMAC_INT_STATUS); ++ u32 intr_status = readl(priv->ioaddr + GMAC_INT_STATUS); + int status = 0; + + /* Not used events (e.g. MMC interrupts) are not handled. */ + if ((intr_status & mmc_tx_irq)) { + CHIP_DBG(KERN_INFO "GMAC: MMC tx interrupt: 0x%08x\n", +- readl(ioaddr + GMAC_MMC_TX_INTR)); ++ readl(priv->ioaddr + GMAC_MMC_TX_INTR)); + status |= core_mmc_tx_irq; + } + if (unlikely(intr_status & mmc_rx_irq)) { + CHIP_DBG(KERN_INFO "GMAC: MMC rx interrupt: 0x%08x\n", +- readl(ioaddr + GMAC_MMC_RX_INTR)); ++ readl(priv->ioaddr + GMAC_MMC_RX_INTR)); + status |= core_mmc_rx_irq; + } + if (unlikely(intr_status & mmc_rx_csum_offload_irq)) { + CHIP_DBG(KERN_INFO "GMAC: MMC rx csum offload: 0x%08x\n", +- readl(ioaddr + GMAC_MMC_RX_CSUM_OFFLOAD)); ++ readl(priv->ioaddr + GMAC_MMC_RX_CSUM_OFFLOAD)); + status |= core_mmc_rx_csum_offload_irq; + } + if (unlikely(intr_status & pmt_irq)) { + CHIP_DBG(KERN_INFO "GMAC: received Magic frame\n"); + /* clear the PMT bits 5 and 6 by reading the PMT + * status register. */ +- readl(ioaddr + GMAC_PMT); ++ readl(priv->ioaddr + GMAC_PMT); + status |= core_irq_receive_pmt_irq; + } + /* MAC trx/rx EEE LPI entry/exit interrupts */ + if (intr_status & lpiis_irq) { + /* Clean LPI interrupt by reading the Reg 12 */ +- u32 lpi_status = readl(ioaddr + LPI_CTRL_STATUS); ++ u32 lpi_status = readl(priv->ioaddr + LPI_CTRL_STATUS); + + if (lpi_status & LPI_CTRL_STATUS_TLPIEN) { + CHIP_DBG(KERN_INFO "GMAC TX entered in LPI\n"); +@@ -244,10 +258,110 @@ static int dwmac1000_irq_status(void __iomem *ioaddr) + status |= core_irq_rx_path_exit_lpi_mode; + } + } ++ if (unlikely(intr_status & time_stamp_irq)){ ++ stmmac_ptp_check_pps_event(priv); ++ } + + return status; + } + ++static unsigned int dwmac1000_calc_vlan_4bit_crc ( const char * vlan ) ++{ ++ int i = 0, j = 0, len = 0, bit = 0; ++ unsigned int crc = 0xFFFFFFFF; ++ unsigned int poly = 0x04C11DB7; ++ unsigned char data; ++ ++ if(unlikely(vlan == NULL)){ ++ return 0; ++ } ++ ++ for( i = 0; i < 2; i++ ) { ++ data = vlan[i]; ++ ++ if (i==0){ ++ len = 8; ++ }else{ ++ len = 4; ++ } ++ ++ for( bit = 0; bit < len; bit++ ) { ++ ++ j = ((crc >> 31) ^ data) & 0x1; ++ crc <<= 1; ++ ++ if( j != 0 ){ ++ crc ^= poly; ++ } ++ ++ data >>= 1; ++ } ++ } ++ return crc; ++ ++} ++ ++static int dwmac1000_vlan_rx_add_vid(struct stmmac_priv *priv, unsigned short vid) ++{ ++ u32 reg = 0; ++ u32 bit_nr = 0; ++ ++ if(unlikely(priv == NULL || vid > GMAC_VLAN_HASH_MAXID)){ ++ return -EINVAL; ++ } ++ ++ if(priv->active_vlans == 0){ ++ ++ /* Flip the VTFE bit in GMAC_FRAME_FILTER */ ++ reg = readl(priv->ioaddr + GMAC_FRAME_FILTER); ++ reg |= GMAC_FRAME_FILTER_VTFE; ++ writel(reg, priv->ioaddr + GMAC_FRAME_FILTER); ++ ++ /* Enable hash filtering - based on 12 bit vid */ ++ reg = readl(priv->ioaddr + GMAC_VLAN_TAG); ++ reg |= GMAC_VLAN_TAG_VTHM | GMAC_VLAN_TAG_ETV | 0x0000FFFF; ++ writel(reg, priv->ioaddr + GMAC_VLAN_TAG); ++ } ++ ++ bit_nr = (~dwmac1000_calc_vlan_4bit_crc((const char*)&vid)) >> 28; ++ priv->active_vlans |= 1 << bit_nr; ++ ++ writel(priv->active_vlans, priv->ioaddr + GMAC_VLAN_HASH); ++ ++ return 0; ++} ++ ++static int dwmac1000_vlan_rx_kill_vid(struct stmmac_priv *priv, unsigned short vid) ++{ ++ u32 reg = 0; ++ u32 bit_nr = 0; ++ ++ if(unlikely(priv == NULL || vid > GMAC_VLAN_HASH_MAXID)){ ++ return -EINVAL; ++ } ++ ++ bit_nr = (~dwmac1000_calc_vlan_4bit_crc((const char*)&vid)) >> 28; ++ ++ priv->active_vlans &= ~(1 << bit_nr); ++ writel(priv->active_vlans, priv->ioaddr + GMAC_VLAN_HASH); ++ ++ if(priv->active_vlans == 0){ ++ ++ /* Disable hash filtering */ ++ reg = readl(priv->ioaddr + GMAC_VLAN_TAG); ++ reg &= ~(GMAC_VLAN_TAG_VTHM | GMAC_VLAN_TAG_ETV | 0x00000001); ++ writel(reg, priv->ioaddr + GMAC_VLAN_TAG); ++ ++ /* Flip the VTFE bit in GMAC_FRAME_FILTER */ ++ reg = readl(priv->ioaddr + GMAC_FRAME_FILTER); ++ reg &= ~GMAC_FRAME_FILTER_VTFE; ++ writel(reg, priv->ioaddr + GMAC_FRAME_FILTER); ++ ++ } ++ ++ return 0; ++} ++ + static void dwmac1000_set_eee_mode(void __iomem *ioaddr) + { + u32 value; +@@ -297,9 +411,10 @@ static void dwmac1000_set_eee_timer(void __iomem *ioaddr, int ls, int tw) + writel(value, ioaddr + LPI_TIMER_CTRL); + } + ++ + static const struct stmmac_ops dwmac1000_ops = { + .core_init = dwmac1000_core_init, +- .rx_ipc = dwmac1000_rx_ipc_enable, ++ .set_rx_ipc = dwmac1000_set_rx_ipc, + .dump_regs = dwmac1000_dump_regs, + .host_irq_status = dwmac1000_irq_status, + .set_filter = dwmac1000_set_filter, +@@ -307,6 +422,8 @@ static const struct stmmac_ops dwmac1000_ops = { + .pmt = dwmac1000_pmt, + .set_umac_addr = dwmac1000_set_umac_addr, + .get_umac_addr = dwmac1000_get_umac_addr, ++ .vlan_rx_add_vid = dwmac1000_vlan_rx_add_vid, ++ .vlan_rx_kill_vid = dwmac1000_vlan_rx_kill_vid, + .set_eee_mode = dwmac1000_set_eee_mode, + .reset_eee_mode = dwmac1000_reset_eee_mode, + .set_eee_timer = dwmac1000_set_eee_timer, +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c +index bf83c03..a0c08e1 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c +@@ -59,9 +59,12 @@ static int dwmac1000_dma_init(void __iomem *ioaddr, int pbl, int fb, + * DMA transfers the data in 8, 16, 32, 64, 128 & 256 beats + * depending on pbl value. + */ ++#ifdef CONFIG_INTEL_QUARK_X1000_SOC ++ value = DMA_BUS_MODE_RIX | (pbl << DMA_BUS_MODE_PBL_SHIFT); ++#else + value = DMA_BUS_MODE_PBL | ((pbl << DMA_BUS_MODE_PBL_SHIFT) | + (pbl << DMA_BUS_MODE_RPBL_SHIFT)); +- ++#endif + /* Set the Fixed burst mode */ + if (fb) + value |= DMA_BUS_MODE_FB; +@@ -70,6 +73,10 @@ static int dwmac1000_dma_init(void __iomem *ioaddr, int pbl, int fb, + if (mb) + value |= DMA_BUS_MODE_MB; + ++#if defined(STMMAC_ATDS_USED) ++ value |= DMA_BUS_MODE_ATDS; ++#endif ++ + #ifdef CONFIG_STMMAC_DA + value |= DMA_BUS_MODE_DA; /* Rx has priority over tx */ + #endif +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c +index f83210e..43472c0 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c +@@ -67,12 +67,12 @@ static void dwmac100_dump_mac_regs(void __iomem *ioaddr) + readl(ioaddr + MAC_VLAN2)); + } + +-static int dwmac100_rx_ipc_enable(void __iomem *ioaddr) ++static int dwmac100_set_rx_ipc(void __iomem *ioaddr, bool on) + { + return 0; + } + +-static int dwmac100_irq_status(void __iomem *ioaddr) ++static int dwmac100_irq_status(struct stmmac_priv *priv) + { + return 0; + } +@@ -160,7 +160,7 @@ static void dwmac100_pmt(void __iomem *ioaddr, unsigned long mode) + + static const struct stmmac_ops dwmac100_ops = { + .core_init = dwmac100_core_init, +- .rx_ipc = dwmac100_rx_ipc_enable, ++ .set_rx_ipc = dwmac100_set_rx_ipc, + .dump_regs = dwmac100_dump_mac_regs, + .host_irq_status = dwmac100_irq_status, + .set_filter = dwmac100_set_filter, +diff --git a/drivers/net/ethernet/stmicro/stmmac/mmc_core.c b/drivers/net/ethernet/stmicro/stmmac/mmc_core.c +index 0c74a70..50617c5 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/mmc_core.c ++++ b/drivers/net/ethernet/stmicro/stmmac/mmc_core.c +@@ -149,6 +149,7 @@ void dwmac_mmc_intr_all_mask(void __iomem *ioaddr) + { + writel(MMC_DEFAULT_MASK, ioaddr + MMC_RX_INTR_MASK); + writel(MMC_DEFAULT_MASK, ioaddr + MMC_TX_INTR_MASK); ++ writel(MMC_DEFAULT_MASK, ioaddr + MMC_RX_IPC_INTR_MASK); + } + + /* This reads the MAC core counters (if actaully supported). +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h +index b05df89..611f70e 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h +@@ -27,9 +27,11 @@ + #define DRV_MODULE_VERSION "Nov_2012" + + #include <linux/clk.h> ++#include <linux/clocksource.h> + #include <linux/stmmac.h> + #include <linux/phy.h> + #include <linux/pci.h> ++#include <linux/ptp_clock_kernel.h> + #include "common.h" + + struct stmmac_priv { +@@ -72,8 +74,22 @@ struct stmmac_priv { + u32 msg_enable; + spinlock_t lock; + spinlock_t tx_lock; ++ ++ /* PTP */ ++ struct ptp_clock *ptp_clock; ++ struct ptp_clock_info ptp_caps; ++ struct delayed_work overflow_work; ++ spinlock_t tmreg_lock; ++ struct cyclecounter ccnt; ++ struct timecounter tcnt; ++// struct timecompare tcmp; ++ int hwts; ++ struct stmmac_timer *tm; ++ + int wolopts; + int wol_irq; ++ ++ int active_vlans; + struct plat_stmmacenet_data *plat; + struct stmmac_counters mmc; + struct dma_features dma_cap; +@@ -81,6 +97,8 @@ struct stmmac_priv { + struct clk *stmmac_clk; + int clk_csr; + int synopsys_id; ++ int irqmode_msi; ++ struct pci_dev * pdev; + struct timer_list eee_ctrl_timer; + bool tx_path_in_lpi_mode; + int lpi_irq; +@@ -110,6 +128,63 @@ int stmmac_dvr_remove(struct net_device *ndev); + struct stmmac_priv *stmmac_dvr_probe(struct device *device, + struct plat_stmmacenet_data *plat_dat, + void __iomem *addr); ++#ifdef CONFIG_STMMAC_PTP ++ ++#define STMMAC_PTP_OVERFLOW_CHECK_ENABLED (u32)(1) ++#define STMMAC_PTP_PPS_ENABLED (u32)(1 << 1) ++#define STMMAC_PTP_HWTS_TX_EN (u32)(1 << 2) ++#define STMMAC_PTP_HWTS_RX_EN (u32)(1 << 3) ++ ++extern void stmmac_ptp_init(struct net_device *ndev, struct device * pdev); ++extern void stmmac_ptp_remove(struct stmmac_priv *priv); ++extern int stmmac_ptp_hwtstamp_ioctl(struct stmmac_priv *priv, ++ struct ifreq *ifr, int cmd); ++extern void stmmac_ptp_rx_hwtstamp(struct stmmac_priv *priv, struct dma_desc *pdma, ++ struct sk_buff *skb); ++extern void stmmac_ptp_tx_hwtstamp(struct stmmac_priv *priv, struct dma_desc *pdma, ++ struct sk_buff *skb); ++extern void stmmac_ptp_check_pps_event(struct stmmac_priv *priv); ++#endif ++ ++#ifdef CONFIG_HAVE_CLK ++static inline int stmmac_clk_enable(struct stmmac_priv *priv) ++{ ++ if (!IS_ERR(priv->stmmac_clk)) ++ return clk_prepare_enable(priv->stmmac_clk); ++ ++ return 0; ++} ++ ++static inline void stmmac_clk_disable(struct stmmac_priv *priv) ++{ ++ if (IS_ERR(priv->stmmac_clk)) ++ return; ++ ++ clk_disable_unprepare(priv->stmmac_clk); ++} ++static inline int stmmac_clk_get(struct stmmac_priv *priv) ++{ ++ priv->stmmac_clk = clk_get(priv->device, NULL); ++ ++ if (IS_ERR(priv->stmmac_clk)) ++ return PTR_ERR(priv->stmmac_clk); ++ ++ return 0; ++} ++#else ++static inline int stmmac_clk_enable(struct stmmac_priv *priv) ++{ ++ return 0; ++} ++static inline void stmmac_clk_disable(struct stmmac_priv *priv) ++{ ++} ++static inline int stmmac_clk_get(struct stmmac_priv *priv) ++{ ++ return 0; ++} ++#endif /* CONFIG_HAVE_CLK */ ++ + void stmmac_disable_eee_mode(struct stmmac_priv *priv); + bool stmmac_eee_init(struct stmmac_priv *priv); + +@@ -167,6 +242,7 @@ static inline int stmmac_register_pci(void) + static inline void stmmac_unregister_pci(void) + { + } ++ + #endif /* CONFIG_STMMAC_PCI */ + + #endif /* __STMMAC_H__ */ +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +index 1372ce2..0644dcd 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +@@ -31,6 +31,7 @@ + + #include "stmmac.h" + #include "dwmac_dma.h" ++#include "dwmac1000.h" + + #define REG_SPACE_SIZE 0x1054 + #define MAC100_ETHTOOL_NAME "st_mac100" +@@ -231,9 +232,7 @@ static int stmmac_ethtool_getsettings(struct net_device *dev, + return -EBUSY; + } + cmd->transceiver = XCVR_INTERNAL; +- spin_lock_irq(&priv->lock); + rc = phy_ethtool_gset(phy, cmd); +- spin_unlock_irq(&priv->lock); + return rc; + } + +@@ -244,10 +243,7 @@ static int stmmac_ethtool_setsettings(struct net_device *dev, + struct phy_device *phy = priv->phydev; + int rc; + +- spin_lock(&priv->lock); + rc = phy_ethtool_sset(phy, cmd); +- spin_unlock(&priv->lock); +- + return rc; + } + +@@ -279,7 +275,7 @@ static int stmmac_ethtool_get_regs_len(struct net_device *dev) + static void stmmac_ethtool_gregs(struct net_device *dev, + struct ethtool_regs *regs, void *space) + { +- int i; ++ int i, offset = 0; + u32 *reg_space = (u32 *) space; + + struct stmmac_priv *priv = netdev_priv(dev); +@@ -300,9 +296,20 @@ static void stmmac_ethtool_gregs(struct net_device *dev, + /* MAC registers */ + for (i = 0; i < 55; i++) + reg_space[i] = readl(priv->ioaddr + (i * 4)); ++ ++ /* VLAN registers */ ++ offset = i; ++ reg_space[offset++] = readl(priv->ioaddr + GMAC_VLAN_TAG_REP); ++ reg_space[offset++] = readl(priv->ioaddr + GMAC_VLAN_HASH); ++ ++ /* 1588 registers */ ++ for(i = 0; i < 13; i++); ++ reg_space[i + offset] = readl(priv->ioaddr + (GMAC_TS_CTRL + (i * 4))); ++ + /* DMA registers */ ++ offset += i; + for (i = 0; i < 22; i++) +- reg_space[i + 55] = ++ reg_space[i + offset] = + readl(priv->ioaddr + (DMA_BUS_MODE + (i * 4))); + } + } +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +index b75f4b2..f74b542 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -28,6 +28,9 @@ + https://bugzilla.stlinux.com/ + *******************************************************************************/ + ++#if defined(CONFIG_INTEL_QUARK_X1000_SOC) ++#include <asm/cln.h> ++#endif + #include <linux/clk.h> + #include <linux/kernel.h> + #include <linux/interrupt.h> +@@ -135,6 +138,8 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id); + #ifdef CONFIG_STMMAC_DEBUG_FS + static int stmmac_init_fs(struct net_device *dev); + static void stmmac_exit_fs(void); ++static int debugfs_registered = 0; ++ + #endif + + #define STMMAC_COAL_TIMER(x) (jiffies + usecs_to_jiffies(x)) +@@ -502,13 +507,29 @@ static int stmmac_set_bfsize(int mtu, int bufsize) + } + + /** ++ * init_dma_err_cleanup ++ * ++ * @dev: net device to clean ++ * Description: Does a cleanup if kmalloc fails during init ++ */ ++static void init_dma_err_cleanup(struct stmmac_priv * priv) ++{ ++ if (priv->tx_skbuff != NULL) ++ kfree(priv->tx_skbuff); ++ if (priv->rx_skbuff_dma != NULL) ++ kfree (priv->rx_skbuff_dma); ++ if (priv->rx_skbuff != NULL) ++ kfree (priv->rx_skbuff); ++} ++ ++/** + * init_dma_desc_rings - init the RX/TX descriptor rings + * @dev: net device structure + * Description: this function initializes the DMA RX/TX descriptors + * and allocates the socket buffers. It suppors the chained and ring + * modes. + */ +-static void init_dma_desc_rings(struct net_device *dev) ++static int init_dma_desc_rings(struct net_device *dev) + { + int i; + struct stmmac_priv *priv = netdev_priv(dev); +@@ -532,8 +553,16 @@ static void init_dma_desc_rings(struct net_device *dev) + txsize, rxsize, bfsize); + + priv->rx_skbuff_dma = kmalloc(rxsize * sizeof(dma_addr_t), GFP_KERNEL); ++ if (priv->rx_skbuff_dma == NULL) ++ return -ENOMEM; ++ + priv->rx_skbuff = + kmalloc(sizeof(struct sk_buff *) * rxsize, GFP_KERNEL); ++ if (priv->rx_skbuff == NULL){ ++ init_dma_err_cleanup(priv); ++ return -ENOMEM; ++ } ++ + priv->dma_rx = + (struct dma_desc *)dma_alloc_coherent(priv->device, + rxsize * +@@ -542,6 +571,11 @@ static void init_dma_desc_rings(struct net_device *dev) + GFP_KERNEL); + priv->tx_skbuff = kmalloc(sizeof(struct sk_buff *) * txsize, + GFP_KERNEL); ++ if (priv->tx_skbuff == NULL){ ++ init_dma_err_cleanup(priv); ++ return -ENOMEM; ++ } ++ + priv->dma_tx = + (struct dma_desc *)dma_alloc_coherent(priv->device, + txsize * +@@ -550,8 +584,9 @@ static void init_dma_desc_rings(struct net_device *dev) + GFP_KERNEL); + + if ((priv->dma_rx == NULL) || (priv->dma_tx == NULL)) { ++ init_dma_err_cleanup(priv); + pr_err("%s:ERROR allocating the DMA Tx/Rx desc\n", __func__); +- return; ++ return -ENOMEM; + } + + DBG(probe, INFO, "stmmac (%s) DMA desc: virt addr (Rx %p, " +@@ -569,8 +604,9 @@ static void init_dma_desc_rings(struct net_device *dev) + skb = __netdev_alloc_skb(dev, bfsize + NET_IP_ALIGN, + GFP_KERNEL); + if (unlikely(skb == NULL)) { ++ init_dma_err_cleanup(priv); + pr_err("%s: Rx init fails; skb is NULL\n", __func__); +- break; ++ return -ENOMEM; + } + skb_reserve(skb, NET_IP_ALIGN); + priv->rx_skbuff[i] = skb; +@@ -615,6 +651,8 @@ static void init_dma_desc_rings(struct net_device *dev) + pr_info("TX descriptor ring:\n"); + display_ring(priv->dma_tx, txsize); + } ++ ++ return 0; + } + + static void dma_free_rx_skbufs(struct stmmac_priv *priv) +@@ -736,6 +774,10 @@ static void stmmac_tx_clean(struct stmmac_priv *priv) + DMA_TO_DEVICE); + priv->hw->ring->clean_desc3(p); + ++#ifdef CONFIG_STMMAC_PTP ++ stmmac_ptp_tx_hwtstamp(priv, p, skb); ++#endif ++ + if (likely(skb != NULL)) { + dev_kfree_skb(skb); + priv->tx_skbuff[entry] = NULL; +@@ -963,6 +1005,25 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv) + } + + /** ++ * stmmac_hw_set_rx_ipc ++ * @priv : pointer to the private device structure. ++ * Enables RX IPC offload if the feature is supported in hardware ++ */ ++static int stmmac_hw_set_rx_ipc(struct stmmac_priv *priv, bool on) ++{ ++ int ret = 0; ++ ++ /* Enable the IPC (Checksum Offload) and check if the feature has been ++ * enabled during the core configuration. */ ++ ret = priv->hw->mac->set_rx_ipc(priv->ioaddr, on); ++ if (on == true && !ret) { ++ pr_warning(" RX IPC Checksum Offload not configured.\n"); ++ priv->plat->rx_coe = STMMAC_RX_COE_NONE; ++ } ++ return ret; ++} ++ ++/** + * stmmac_tx_timer: + * @data: data pointer + * Description: +@@ -1022,7 +1083,12 @@ static int stmmac_open(struct net_device *dev) + priv->dma_tx_size = STMMAC_ALIGN(dma_txsize); + priv->dma_rx_size = STMMAC_ALIGN(dma_rxsize); + priv->dma_buf_sz = STMMAC_ALIGN(buf_sz); +- init_dma_desc_rings(dev); ++ ret = init_dma_desc_rings(dev); ++ if (ret < 0){ ++ pr_err("%s: DMA initialization failed\n", __func__); ++ goto open_error; ++ } ++ + + /* DMA initialization and SW reset */ + ret = stmmac_init_dma_engine(priv); +@@ -1078,6 +1144,9 @@ static int stmmac_open(struct net_device *dev) + /* Set the HW DMA mode and the COE */ + stmmac_dma_operation_mode(priv); + ++ /* Enable RX IPC if supported by silicon */ ++ ret = stmmac_hw_set_rx_ipc(priv, true); ++ + /* Extra statistics */ + memset(&priv->xstats, 0, sizeof(struct stmmac_extra_stats)); + priv->xstats.threshold = tc; +@@ -1085,9 +1154,12 @@ static int stmmac_open(struct net_device *dev) + stmmac_mmc_setup(priv); + + #ifdef CONFIG_STMMAC_DEBUG_FS +- ret = stmmac_init_fs(dev); +- if (ret < 0) +- pr_warning("%s: failed debugFS registration\n", __func__); ++ if (debugfs_registered == 0){ ++ debugfs_registered = 1; ++ ret = stmmac_init_fs(dev); ++ if (ret < 0) ++ pr_warning("%s: failed debugFS registration\n", __func__); ++ } + #endif + /* Start the ball rolling... */ + DBG(probe, DEBUG, "%s: DMA RX/TX processes started...\n", dev->name); +@@ -1430,6 +1502,9 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) + #endif + skb->protocol = eth_type_trans(skb, priv->dev); + ++#ifdef CONFIG_STMMAC_PTP ++ stmmac_ptp_tx_hwtstamp(priv, priv->dma_rx + entry, skb); ++#endif + if (unlikely(!priv->plat->rx_coe)) + skb_checksum_none_assert(skb); + else +@@ -1588,9 +1663,19 @@ static netdev_features_t stmmac_fix_features(struct net_device *dev, + if (priv->plat->bugged_jumbo && (dev->mtu > ETH_DATA_LEN)) + features &= ~NETIF_F_ALL_CSUM; + ++ stmmac_hw_set_rx_ipc(priv, features & NETIF_F_RXCSUM); ++ + return features; + } + ++#if defined(CONFIG_INTEL_QUARK_X1000_SOC) ++ #define mask_pvm(x) cln_pci_pvm_mask(x) ++ #define unmask_pvm(x) cln_pci_pvm_unmask(x) ++#else ++ #define mask_pvm(x) ++ #define unmask_pvm(x) ++#endif ++ + static irqreturn_t stmmac_interrupt(int irq, void *dev_id) + { + struct net_device *dev = (struct net_device *)dev_id; +@@ -1601,10 +1686,12 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id) + return IRQ_NONE; + } + ++ mask_pvm(priv->pdev); ++ + /* To handle GMAC own interrupts */ + if (priv->plat->has_gmac) { +- int status = priv->hw->mac->host_irq_status((void __iomem *) +- dev->base_addr); ++ int status = priv->hw->mac->host_irq_status(priv); ++ + if (unlikely(status)) { + if (status & core_mmc_tx_irq) + priv->xstats.mmc_tx_irq_n++; +@@ -1634,6 +1721,8 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id) + /* To handle DMA interrupts */ + stmmac_dma_interrupt(priv); + ++ unmask_pvm(priv->pdev); ++ + return IRQ_HANDLED; + } + +@@ -1669,7 +1758,15 @@ static int stmmac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) + if (!priv->phydev) + return -EINVAL; + +- ret = phy_mii_ioctl(priv->phydev, rq, cmd); ++ switch (cmd) { ++#ifdef CONFIG_STMMAC_PTP ++ case SIOCSHWTSTAMP: ++ ret = stmmac_ptp_hwtstamp_ioctl(priv, rq, cmd); ++ break; ++#endif ++ default: ++ ret = phy_mii_ioctl(priv->phydev, rq, cmd); ++ } + + return ret; + } +@@ -1850,6 +1947,21 @@ static void stmmac_exit_fs(void) + } + #endif /* CONFIG_STMMAC_DEBUG_FS */ + ++#ifdef STMMAC_VLAN_HASH ++static int stmmac_vlan_rx_add_vid(struct net_device *dev, unsigned short vid) ++{ ++ struct stmmac_priv *priv = netdev_priv(dev); ++ return priv->hw->mac->vlan_rx_add_vid(priv, vid); ++} ++ ++static int stmmac_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) ++{ ++ struct stmmac_priv *priv = netdev_priv(dev); ++ return priv->hw->mac->vlan_rx_kill_vid(priv, vid); ++} ++ ++#endif ++ + static const struct net_device_ops stmmac_netdev_ops = { + .ndo_open = stmmac_open, + .ndo_start_xmit = stmmac_xmit, +@@ -1860,6 +1972,10 @@ static const struct net_device_ops stmmac_netdev_ops = { + .ndo_tx_timeout = stmmac_tx_timeout, + .ndo_do_ioctl = stmmac_ioctl, + .ndo_set_config = stmmac_config, ++#ifdef STMMAC_VLAN_HASH ++ .ndo_vlan_rx_add_vid = stmmac_vlan_rx_add_vid, ++ .ndo_vlan_rx_kill_vid = stmmac_vlan_rx_kill_vid, ++#endif + #ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = stmmac_poll_controller, + #endif +@@ -1924,13 +2040,7 @@ static int stmmac_hw_init(struct stmmac_priv *priv) + /* Select the enhnaced/normal descriptor structures */ + stmmac_selec_desc_mode(priv); + +- /* Enable the IPC (Checksum Offload) and check if the feature has been +- * enabled during the core configuration. */ +- ret = priv->hw->mac->rx_ipc(priv->ioaddr); +- if (!ret) { +- pr_warning(" RX IPC Checksum Offload not configured.\n"); +- priv->plat->rx_coe = STMMAC_RX_COE_NONE; +- } ++ ret = stmmac_hw_set_rx_ipc(priv, true); + + if (priv->plat->rx_coe) + pr_info(" RX Checksum Offload Engine supported (type %d)\n", +@@ -2001,6 +2111,12 @@ struct stmmac_priv *stmmac_dvr_probe(struct device *device, + /* Both mac100 and gmac support receive VLAN tag detection */ + ndev->features |= NETIF_F_HW_VLAN_RX; + #endif ++#ifdef STMMAC_VLAN_HASH ++ ndev->features |= NETIF_F_HW_VLAN_FILTER; ++ ndev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | ++ NETIF_F_RXCSUM; ++#endif ++ + priv->msg_enable = netif_msg_init(debug, default_msg_level); + + if (flow_ctrl) +@@ -2044,6 +2160,10 @@ struct stmmac_priv *stmmac_dvr_probe(struct device *device, + else + priv->clk_csr = priv->plat->clk_csr; + ++#ifdef CONFIG_STMMAC_PTP ++ stmmac_ptp_init(ndev, device); ++#endif ++ + /* MDIO bus Registration */ + ret = stmmac_mdio_register(ndev); + if (ret < 0) { +@@ -2060,6 +2180,9 @@ error_clk_get: + unregister_netdev(ndev); + error_netdev_register: + netif_napi_del(&priv->napi); ++#ifdef CONFIG_STMMAC_PTP ++ stmmac_ptp_remove(priv); ++#endif + free_netdev(ndev); + + return NULL; +@@ -2075,7 +2198,7 @@ int stmmac_dvr_remove(struct net_device *ndev) + { + struct stmmac_priv *priv = netdev_priv(ndev); + +- pr_info("%s:\n\tremoving driver", __func__); ++ pr_info("%s:\n\tremoving driver\n", __func__); + + priv->hw->dma->stop_rx(priv->ioaddr); + priv->hw->dma->stop_tx(priv->ioaddr); +@@ -2083,6 +2206,10 @@ int stmmac_dvr_remove(struct net_device *ndev) + stmmac_set_mac(priv->ioaddr, false); + stmmac_mdio_unregister(ndev); + netif_carrier_off(ndev); ++ ++#ifdef CONFIG_STMMAC_PTP ++ stmmac_ptp_remove(priv); ++#endif + unregister_netdev(ndev); + free_netdev(ndev); + +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c +index 064eaac..38213d2 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c +@@ -24,29 +24,186 @@ + *******************************************************************************/ + + #include <linux/pci.h> ++#include <linux/platform_data/clanton.h> + #include "stmmac.h" + +-struct plat_stmmacenet_data plat_dat; +-struct stmmac_mdio_bus_data mdio_data; +-struct stmmac_dma_cfg dma_cfg; ++/* List of supported PCI device IDs */ ++#define STMMAC_VENDOR_ID 0x700 ++#define STMMAC_DEVICE_ID 0x1108 ++#define STMMAC_CLANTON_ID 0x0937 ++#define MAX_INTERFACES 0x02 ++ ++#if defined (CONFIG_INTEL_QUARK_X1000_SOC) ++static int enable_msi = 1; ++#else ++static int enable_msi = 0; ++#endif ++module_param(enable_msi, int, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(enable_msi, "Enable PCI MSI mode"); ++ ++static int bus_id = 1; ++static char stmmac_mac_data[MAX_INTERFACES][ETH_ALEN]; ++ ++struct stmmac_cln_mac_data { ++ int phy_addr; ++ int bus_id; ++ cln_plat_id_t plat_id; ++}; ++ ++static struct stmmac_cln_mac_data phy_data [] = { ++ { ++ .phy_addr = -1, /* not connected */ ++ .bus_id = 1, ++ .plat_id = CLANTON_EMULATION, ++ }, ++ { ++ .phy_addr = 1, ++ .bus_id = 2, ++ .plat_id = CLANTON_EMULATION, ++ }, ++ { ++ .phy_addr = 3, ++ .bus_id = 1, ++ .plat_id = CLANTON_PEAK, ++ }, ++ { ++ .phy_addr = 1, ++ .bus_id = 2, ++ .plat_id = CLANTON_PEAK, ++ }, ++ { ++ .phy_addr = 1, ++ .bus_id = 1, ++ .plat_id = KIPS_BAY, ++ }, ++ { ++ .phy_addr = -1, /* not connected */ ++ .bus_id = 2, ++ .plat_id = KIPS_BAY, ++ }, ++ { ++ .phy_addr = 1, ++ .bus_id = 1, ++ .plat_id = CROSS_HILL, ++ }, ++ { ++ .phy_addr = 1, ++ .bus_id = 2, ++ .plat_id = CROSS_HILL, ++ }, ++ { ++ .phy_addr = 1, ++ .bus_id = 1, ++ .plat_id = CLANTON_HILL, ++ }, ++ { ++ .phy_addr = 1, ++ .bus_id = 2, ++ .plat_id = CLANTON_HILL, ++ }, ++ { ++ .phy_addr = 1, ++ .bus_id = 1, ++ .plat_id = IZMIR, ++ }, ++ { ++ .phy_addr = -1, /* not connected */ ++ .bus_id = 2, ++ .plat_id = IZMIR, ++ }, ++}; ++ ++ ++static int stmmac_find_phy_addr(int mdio_bus_id, cln_plat_id_t cln_plat_id) ++{ ++ int i = 0; ++ ++ for (; i < sizeof(phy_data)/sizeof(struct stmmac_cln_mac_data); i++){ ++ if ( phy_data[i].plat_id == cln_plat_id && ++ phy_data[i].bus_id == mdio_bus_id) ++ return phy_data[i].phy_addr; ++ } ++ ++ return -1; ++} ++ ++static int stmmac_default_data(struct plat_stmmacenet_data *plat_dat, ++ int mdio_bus_id, const struct pci_device_id *id) ++{ ++ int phy_addr = 0; ++ memset(plat_dat, 0, sizeof(struct plat_stmmacenet_data)); ++ ++ plat_dat->mdio_bus_data = kzalloc(sizeof(struct stmmac_mdio_bus_data), ++ GFP_KERNEL); ++ if (plat_dat->mdio_bus_data == NULL) ++ return -ENOMEM; ++ ++ plat_dat->dma_cfg = kzalloc(sizeof(struct stmmac_dma_cfg),GFP_KERNEL); ++ if (plat_dat->dma_cfg == NULL) ++ return -ENOMEM; ++ ++ if (id->device == STMMAC_CLANTON_ID) { ++ ++ phy_addr = stmmac_find_phy_addr(mdio_bus_id, ++ intel_cln_plat_get_id()); ++ if (phy_addr == -1) ++ return -ENODEV; ++ ++ plat_dat->bus_id = mdio_bus_id; ++ plat_dat->phy_addr = phy_addr; ++ plat_dat->interface = PHY_INTERFACE_MODE_RMII; ++ /* clk_csr_i = 20-35MHz & MDC = clk_csr_i/16 */ ++ plat_dat->clk_csr = 2; ++ plat_dat->has_gmac = 1; ++ plat_dat->force_sf_dma_mode = 1; ++ ++ plat_dat->mdio_bus_data->phy_reset = NULL; ++ plat_dat->mdio_bus_data->phy_mask = 0; ++ ++ plat_dat->dma_cfg->pbl = 16; ++ plat_dat->dma_cfg->fixed_burst = 1; ++ plat_dat->dma_cfg->burst_len = DMA_AXI_BLEN_256; ++ ++ } else { ++ ++ plat_dat->bus_id = mdio_bus_id; ++ plat_dat->phy_addr = phy_addr; ++ plat_dat->interface = PHY_INTERFACE_MODE_GMII; ++ /* clk_csr_i = 20-35MHz & MDC = clk_csr_i/16 */ ++ plat_dat->clk_csr = 2; ++ plat_dat->has_gmac = 1; ++ plat_dat->force_sf_dma_mode = 1; ++ ++ plat_dat->mdio_bus_data->phy_reset = NULL; ++ plat_dat->mdio_bus_data->phy_mask = 0; ++ ++ plat_dat->dma_cfg->pbl = 32; ++ plat_dat->dma_cfg->burst_len = DMA_AXI_BLEN_256; ++ ++ } ++ ++ return 0; ++} + +-static void stmmac_default_data(void) ++/** ++ * stmmac_pci_find_mac ++ * ++ * @prive: pointer to private stmmac structure ++ * @mdio_bus_id : MDIO bus identifier used to find the platform MAC id ++ * ++ * Attempt to find MAC in platform data. If not found then driver will generate ++ * a random one for itself in any case ++ */ ++void stmmac_pci_find_mac (struct stmmac_priv * priv, unsigned int mdio_bus_id) + { +- memset(&plat_dat, 0, sizeof(struct plat_stmmacenet_data)); +- plat_dat.bus_id = 1; +- plat_dat.phy_addr = 0; +- plat_dat.interface = PHY_INTERFACE_MODE_GMII; +- plat_dat.clk_csr = 2; /* clk_csr_i = 20-35MHz & MDC = clk_csr_i/16 */ +- plat_dat.has_gmac = 1; +- plat_dat.force_sf_dma_mode = 1; +- +- mdio_data.phy_reset = NULL; +- mdio_data.phy_mask = 0; +- plat_dat.mdio_bus_data = &mdio_data; +- +- dma_cfg.pbl = 32; +- dma_cfg.burst_len = DMA_AXI_BLEN_256; +- plat_dat.dma_cfg = &dma_cfg; ++ unsigned int id = mdio_bus_id - 1; ++ if (priv == NULL || id >= MAX_INTERFACES) ++ return; ++ ++ if (intel_cln_plat_get_mac(PLAT_DATA_MAC0+id, ++ (char*)&stmmac_mac_data[id]) == 0){ ++ memcpy(priv->dev->dev_addr, &stmmac_mac_data[id], ETH_ALEN); ++ } + } + + /** +@@ -67,8 +224,21 @@ static int stmmac_pci_probe(struct pci_dev *pdev, + int ret = 0; + void __iomem *addr = NULL; + struct stmmac_priv *priv = NULL; ++ struct plat_stmmacenet_data *plat_dat = NULL; + int i; + ++ plat_dat = kmalloc(sizeof(struct plat_stmmacenet_data), GFP_KERNEL); ++ if (plat_dat == NULL){ ++ ret = -ENOMEM; ++ goto err_out_map_failed; ++ } ++ ++ /* return -ENODEV for non existing PHY, stop probing here */ ++ ret = stmmac_default_data(plat_dat, bus_id, id); ++ if (ret != 0) ++ goto err_platdata; ++ ++ + /* Enable pci device */ + ret = pci_enable_device(pdev); + if (ret) { +@@ -96,30 +266,51 @@ static int stmmac_pci_probe(struct pci_dev *pdev, + break; + } + pci_set_master(pdev); ++ if(enable_msi == 1){ ++ pci_enable_msi(pdev); ++ pr_info("stmmac MSI mode enabled"); ++ } + +- stmmac_default_data(); ++ pr_info("Vendor 0x%04x Device 0x%04x\n", ++ id->vendor, id->device); + +- priv = stmmac_dvr_probe(&(pdev->dev), &plat_dat, addr); ++ priv = stmmac_dvr_probe(&(pdev->dev), plat_dat, addr); + if (!priv) { + pr_err("%s: main driver probe failed", __func__); + goto err_out; + } ++ ++ stmmac_pci_find_mac(priv, bus_id); ++ + priv->dev->irq = pdev->irq; + priv->wol_irq = pdev->irq; +- ++ priv->irqmode_msi = enable_msi; ++ priv->pdev = pdev; ++ #ifdef CONFIG_INTEL_QUARK_X1000_SOC ++ priv->lpi_irq = -ENXIO; ++ #endif + pci_set_drvdata(pdev, priv->dev); + + pr_debug("STMMAC platform driver registration completed"); ++ bus_id++; + + return 0; + + err_out: + pci_clear_master(pdev); +-err_out_map_failed: + pci_release_regions(pdev); + err_out_req_reg_failed: + pci_disable_device(pdev); +- ++err_platdata: ++ if (plat_dat != NULL){ ++ if (plat_dat->dma_cfg != NULL) ++ kfree (plat_dat->dma_cfg); ++ if (plat_dat->mdio_bus_data != NULL) ++ kfree (plat_dat->mdio_bus_data); ++ kfree(plat_dat); ++ } ++err_out_map_failed: ++ bus_id++; + return ret; + } + +@@ -138,6 +329,21 @@ static void stmmac_pci_remove(struct pci_dev *pdev) + stmmac_dvr_remove(ndev); + + pci_set_drvdata(pdev, NULL); ++ ++ if(enable_msi == 1) { ++ if(pci_dev_msi_enabled(pdev)) { ++ pci_disable_msi(pdev); ++ } ++ } ++ ++ if (priv->plat != NULL) { ++ if (priv->plat->dma_cfg != NULL) ++ kfree (priv->plat->dma_cfg); ++ if (priv->plat->mdio_bus_data != NULL) ++ kfree (priv->plat->mdio_bus_data); ++ kfree(priv->plat); ++ } ++ + pci_iounmap(pdev, priv->ioaddr); + pci_release_regions(pdev); + pci_disable_device(pdev); +@@ -167,12 +373,10 @@ static int stmmac_pci_resume(struct pci_dev *pdev) + } + #endif + +-#define STMMAC_VENDOR_ID 0x700 +-#define STMMAC_DEVICE_ID 0x1108 +- + static DEFINE_PCI_DEVICE_TABLE(stmmac_id_table) = { + {PCI_DEVICE(STMMAC_VENDOR_ID, STMMAC_DEVICE_ID)}, + {PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_MAC)}, ++ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, STMMAC_CLANTON_ID)}, + {} + }; + +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c +new file mode 100644 +index 0000000..8552d0c +--- /dev/null ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c +@@ -0,0 +1,723 @@ ++/******************************************************************************* ++ ++ STMMAC 1588-2005 hardware accel ++ Copyright(c) 2012 Intel Corporation. This code is based on IXGBE_PTP ++ ++ 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. ++ ++ 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. ++ ++ The full GNU General Public License is included in this distribution in ++ the file called "COPYING". ++ ++ Contact Information::w ++ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 ++ ++*******************************************************************************/ ++#include "stmmac.h" ++#include "dwmac1000.h" ++#include <linux/export.h> ++#include <linux/net_tstamp.h> ++ ++/* PPSCMD0 */ ++#define STMMAC_PPSCMD_NONE 0x00 /* No command */ ++#define STMMAC_PPSCMD_START_SINGLE 0x01 /* Start single pulse */ ++#define STMMAC_PPSCMD_START_PULSE 0x02 /* Start pulse train */ ++#define STMMAC_PPSCMD_START_CANCEL 0x03 /* Cancel start */ ++#define STMMAC_PPSCMD_STOP_PULSE_ATTIME 0x04 /* Stop pulse train at time */ ++#define STMMAC_PPSCMD_STOP_PULSE_IMMED 0x05 /* Stop pulse train immediate */ ++#define STMMAC_PPSCMD_STOP_CANCEL 0x06 /* Stop cancel pulse train */ ++ ++#define STMMAC_PTP_OVERFLOW_CHECK_ENABLED (u32)(1) ++#define STMMAC_PTP_PPS_ENABLED (u32)(1 << 1) ++#define STMMAC_PTP_HWTS_TX_EN (u32)(1 << 2) ++#define STMMAC_PTP_HWTS_RX_EN (u32)(1 << 3) ++ ++#ifndef NSECS_PER_SEC ++#define NSECS_PER_SEC 1000000000ULL ++#endif ++ ++/* ++ * Structure of STMMAC timer registers ++ * ++ * GMAC_TS_HWSEC GMAC_ST_SEC GMAC_ST_NSEC ++ * +--------------+ +--------------+ +---+---+------+ ++ * STMMAC | 16 | | 32 | | 32 | ++ * +--------------+ +--------------+ +---+---+------+ ++ * ++ * The counter for the STMMAC is 80 bits ++ * - HWSEC == overflow value for ST_SEC => 130 years to overflow (optional) ++ * - ST_SEC == seconds ++ * - ST_NSEC == nanoseconds ++ */ ++ ++/** ++ * stmmac_ptp_read - read raw cycle counter (to be used by time counter) ++ * @cc - the cyclecounter structure ++ * ++ * this function reads the cyclecounter registers and is called by the ++ * cyclecounter structure used to construct a ns counter from the ++ * arbitrary fixed point registers ++ */ ++static cycle_t stmmac_ptp_read(const struct cyclecounter *ccnt) ++{ ++ struct stmmac_priv *priv = ++ container_of(ccnt, struct stmmac_priv, ccnt); ++ cycle_t stamp = 0; ++ ++ stamp = (u64)readl(priv->ioaddr + GMAC_ST_NSEC); ++ stamp |= (u64)readl(priv->ioaddr + GMAC_ST_SEC) << 32; ++ ++ return stamp; ++} ++ ++/** ++ * stmmac_ptp_adjfreq ++ * @ptp - the ptp clock structure ++ * @ppb - parts per billion adjustment from base ++ * ++ * adjust the frequency of the ptp cycle counter by the ++ * indicated ppb from the base frequency. ++ */ ++static int stmmac_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) ++{ ++ return 0; ++} ++ ++/** ++ * stmmac_ptp_adjtime ++ * @ptp - the ptp clock structure ++ * @delta - offset to adjust the cycle counter by ++ * ++ * adjust the timer by resetting the timecounter structure. ++ */ ++static int stmmac_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) ++{ ++ struct stmmac_priv *priv = ++ container_of(ptp, struct stmmac_priv, ptp_caps); ++ unsigned long flags; ++ u64 now; ++ ++ spin_lock_irqsave(&priv->tmreg_lock, flags); ++ ++ now = timecounter_read(&priv->tcnt); ++ now += delta; ++ ++ /* reset the timecounter */ ++ timecounter_init(&priv->tcnt, ++ &priv->ccnt, ++ now); ++ ++ spin_unlock_irqrestore(&priv->tmreg_lock, flags); ++ return 0; ++} ++ ++/** ++ * stmmac_ptp_gettime ++ * @ptp - the ptp clock structure ++ * @ts - timespec structure to hold the current time value ++ * ++ * read the timecounter and return the correct value on ns, ++ * after converting it into a struct timespec. ++ */ ++static int stmmac_ptp_gettime(struct ptp_clock_info *ptp, struct timespec *ts) ++{ ++ struct stmmac_priv *priv = ++ container_of(ptp, struct stmmac_priv, ptp_caps); ++ u64 ns; ++ u32 remainder; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&priv->tmreg_lock, flags); ++ ns = timecounter_read(&priv->tcnt); ++ spin_unlock_irqrestore(&priv->tmreg_lock, flags); ++ ++ ts->tv_sec = div_u64_rem(ns, 1000000000ULL, &remainder); ++ ts->tv_nsec = remainder; ++ ++ return 0; ++} ++ ++/** ++ * stmmac_ptp_settime ++ * @ptp - the ptp clock structure ++ * @ts - the timespec containing the new time for the cycle counter ++ * ++ * reset the timecounter to use a new base value instead of the kernel ++ * wall timer value. ++ */ ++static int stmmac_ptp_settime(struct ptp_clock_info *ptp, ++ const struct timespec *ts) ++{ ++ struct stmmac_priv *priv = ++ container_of(ptp, struct stmmac_priv, ptp_caps); ++ u64 ns; ++ unsigned long flags; ++ ++ ns = ts->tv_sec * 1000000000ULL; ++ ns += ts->tv_nsec; ++ ++ /* reset the timecounter */ ++ spin_lock_irqsave(&priv->tmreg_lock, flags); ++ timecounter_init(&priv->tcnt, &priv->ccnt, ns); ++ spin_unlock_irqrestore(&priv->tmreg_lock, flags); ++ ++ return 0; ++} ++ ++/** ++ * stmmac_ptp_enable ++ * @ptp - the ptp clock structure ++ * @rq - the requested feature to change ++ * @on - whether to enable or disable the feature ++ * ++ * enable (or disable) ancillary features of the phc subsystem. ++ * our driver only supports the PPS feature on the X540 ++ */ ++static int stmmac_ptp_enable(struct ptp_clock_info *ptp, ++ struct ptp_clock_request *rq, int on) ++{ ++ struct stmmac_priv *priv = ++ container_of(ptp, struct stmmac_priv, ptp_caps); ++ uint32_t reg = 0; ++ ++ /** ++ * When enabling PPS functionality in STMMAC we need to unmask the ++ * interrupt mask reg and enable the TSTRIG bit in the timestamp control ++ * reg ++ */ ++ if (rq->type == PTP_CLK_REQ_PPS) { ++ if (on){ ++ priv->hwts |= STMMAC_PTP_PPS_ENABLED; ++ ++ /* Enable TSTRIG */ ++ reg = readl(priv->ioaddr + GMAC_TS_CTRL); ++ reg |= GMAC_TS_CTRL_TSTRIG; ++ writel(reg, priv->ioaddr + GMAC_TS_CTRL); ++ wmb(); ++ ++ /* Unmask interrupt */ ++ reg = readl(priv->ioaddr + GMAC_INT_MASK); ++ printk(KERN_INFO "%s[on] read interrupt mask 0x%08x\n", __func__, reg); ++ reg &= ~GMAC_INT_MASK_TSIM; ++ printk(KERN_INFO "%s[on] write interrupt mask 0x%08x\n", __func__, reg); ++ writel(reg, priv->ioaddr + GMAC_INT_MASK); ++ wmb(); ++ ++ } else { ++ /* Mask interrupt */ ++ reg = readl(priv->ioaddr + GMAC_INT_MASK); ++ printk(KERN_INFO "%s[off] read interrupt mask 0x%08x\n", __func__, reg); ++ reg |= GMAC_INT_MASK_TSIM; ++ printk(KERN_INFO "%s[off] write interrupt mask 0x%08x\n", __func__, reg); ++ writel(reg, priv->ioaddr + GMAC_INT_MASK); ++ wmb(); ++ ++ /* Disable TSTRIG */ ++ reg = readl(priv->ioaddr + GMAC_TS_CTRL); ++ reg &= ~GMAC_TS_CTRL_TSTRIG; ++ writel(reg, priv->ioaddr + GMAC_TS_CTRL); ++ wmb(); ++ ++ priv->hwts &= ++ ~STMMAC_PTP_PPS_ENABLED; ++ } ++ return 0; ++ } ++ ++ return -ENOTSUPP; ++} ++ ++/** ++ * stmmac_ptp_check_pps_event ++ * @priv - the private priv structure ++ * ++ * This function is called by the interrupt routine when checking for ++ * interrupts. It will check and handle a pps event. ++ */ ++void stmmac_ptp_check_pps_event(struct stmmac_priv *priv) ++{ ++ struct ptp_clock_event event; ++ event.type = PTP_CLOCK_PPS; ++ ++ /* Make sure ptp clock is valid, and PPS event enabled */ ++ if (!priv->ptp_clock || ++ !(priv->hwts & STMMAC_PTP_PPS_ENABLED)){ ++ return; ++ } ++ ++ ptp_clock_event(priv->ptp_clock, &event); ++} ++ ++#if 0 ++/** ++ * stmmac_ptp_overflow_check - delayed work to detect SYSTIME overflow ++ * @work: structure containing information about this work task ++ * ++ * this work function is scheduled to continue reading the timecounter ++ * in order to prevent missing when the system time registers wrap ++ * around. This needs to be run approximately twice a minute when no ++ * PTP activity is occurring. ++ */ ++void stmmac_ptp_overflow_check(struct stmmac_priv *priv) ++{ ++ unsigned long elapsed_jiffies = priv->last_overflow_check - jiffies; ++ struct timespec ts; ++ ++ if ((priv->hwts & STMMAC_PTP_OVERFLOW_CHECK_ENABLED) && ++ (elapsed_jiffies >= STMMAC_OVERFLOW_PERIOD)) { ++ stmmac_ptp_gettime(&priv->ptp_caps, &ts); ++ priv->last_overflow_check = jiffies; ++ } ++} ++#endif ++ ++/** ++ * stmmac_ptp_tx_hwtstamp - utility function which checks for TX time stamp ++ * @q_vector: structure containing interrupt and ring information ++ * @skb: particular skb to send timestamp with ++ * ++ * if the timestamp is valid, we convert it into the timecounter ns ++ * value, then store that result into the shhwtstamps structure which ++ * is passed up the network stack ++ */ ++void stmmac_ptp_tx_hwtstamp(struct stmmac_priv *priv, struct dma_desc *pdma, ++ struct sk_buff *skb) ++{ ++ /* Sanity check input */ ++ if (unlikely(priv == NULL || pdma == NULL || skb == NULL)){ ++ return; ++ } ++ ++ if(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP){ ++ struct skb_shared_hwtstamps shhwtstamps; ++ u64 ns; ++ u64 regval; ++ unsigned long flags; ++ ++ regval = (u64)pdma->ts_lo; ++ regval |= (u64)pdma->ts_hi << 32; ++ ++ spin_lock_irqsave(&priv->tmreg_lock, flags); ++ ns = timecounter_cyc2time(&priv->tcnt, regval); ++ spin_unlock_irqrestore(&priv->tmreg_lock, flags); ++ ++ memset(&shhwtstamps, 0, sizeof(shhwtstamps)); ++ shhwtstamps.hwtstamp = ns_to_ktime(ns); ++ skb_tstamp_tx(skb, &shhwtstamps); ++ } ++} ++ ++/** ++ * stmmac_ptp_rx_hwtstamp - utility function which checks for RX time stamp ++ * @q_vector: structure containing interrupt and ring information ++ * @skb: particular skb to send timestamp with ++ * ++ * if the timestamp is valid, we convert it into the timecounter ns ++ * value, then store that result into the shhwtstamps structure which ++ * is passed up the network stack ++ */ ++void stmmac_ptp_rx_hwtstamp(struct stmmac_priv *priv, struct dma_desc *pdma, ++ struct sk_buff *skb) ++{ ++ /* Sanity check input */ ++ if (unlikely(priv == NULL || pdma == NULL || skb == NULL)){ ++ return; ++ } ++ ++ if(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP){ ++ struct skb_shared_hwtstamps shhwtstamps; ++ u64 ns; ++ u64 regval; ++ unsigned long flags; ++ ++ regval = (u64)pdma->ts_lo; ++ regval |= (u64)pdma->ts_hi << 32; ++ ++ spin_lock_irqsave(&priv->tmreg_lock, flags); ++ ns = timecounter_cyc2time(&priv->tcnt, regval); ++ spin_unlock_irqrestore(&priv->tmreg_lock, flags); ++ ++ memset(&shhwtstamps, 0, sizeof(shhwtstamps)); ++ shhwtstamps.hwtstamp = ns_to_ktime(ns); ++ } ++} ++ ++/** ++ * ixgbe_ptp_hwtstamp_ioctl - control hardware time stamping ++ * @priv: pointer to priv struct ++ * @ifreq: ioctl data ++ * @cmd: particular ioctl requested ++ * ++ * Outgoing time stamping can be enabled and disabled. Play nice and ++ * disable it when requested, although it shouldn't case any overhead ++ * when no packet needs it. At most one packet in the queue may be ++ * marked for time stamping, otherwise it would be impossible to tell ++ * for sure to which packet the hardware time stamp belongs. ++ * ++ * Incoming time stamping has to be configured via the hardware ++ * filters. Not all combinations are supported, in particular event ++ * type has to be specified. Matching the kind of event packet is ++ * not supported, with the exception of "all V2 events regardless of ++ * level 2 or 4". ++ */ ++int stmmac_ptp_hwtstamp_ioctl(struct stmmac_priv *priv, ++ struct ifreq *ifr, int cmd) ++{ ++ struct hwtstamp_config config; ++// struct stmmac_priv *priv = netdev_priv(netdev); ++ u32 tsctl = 0; ++ ++ if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) ++ return -EFAULT; ++ ++ /* reserved for future extensions */ ++ if (config.flags) ++ return -EINVAL; ++ ++ /* Snapshot the reg - preserve Timestamp Interrupt Trigger Enable */ ++ tsctl = readl(priv->ioaddr + GMAC_TS_CTRL) & GMAC_TS_CTRL_TSTRIG; ++ ++ /* TX */ ++ switch (config.tx_type) { ++ case HWTSTAMP_TX_OFF: ++ priv->hwts &= ~STMMAC_PTP_HWTS_TX_EN; ++ printk(KERN_INFO "%s set TX PTP en false\n", __func__); ++ break; ++ case HWTSTAMP_TX_ON: ++ priv->hwts |= STMMAC_PTP_HWTS_TX_EN; ++ printk(KERN_INFO "%s set TX PTP en true\n", __func__); ++ break; ++ default: ++ return -ERANGE; ++ } ++ ++ /* RX */ ++ priv->hwts |= STMMAC_PTP_HWTS_RX_EN; ++ ++ switch (config.rx_filter) { ++ ++ ++ case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: ++ /* ++ * V1_L4 UDP any event ++ * SYNC, Follow_Up, Delay_Req, Delay_Resp, ++ * Pdelay_Req, Pdelay_Resp, Pdelay_Resp_Follow_Up ++ * ++ * SNAPTYPSEL=1, TSMSTRENA=x, TSEVNTENA=0, TSVER2ENA=0, IPV4=1 ++ */ ++ tsctl |= GMAC_TS_CTRL_SNTYPSEL1 | GMAC_TS_CTRL_TSIPV4ENA; ++ printk(KERN_INFO "%s HWTSTAMP_FILTER_PTP_V1_L4_EVENT \n", __func__); ++ break; ++ ++ case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: ++ /* ++ * V1_L4 Master ++ * Delay_Req ++ * ++ * SNAPTYPSEL=0, TSMSTRENA=1, TSEVNTENA=1, TSVER2ENA=0, IPV4=1 ++ */ ++ tsctl |= GMAC_TS_CTRL_SNTYPSEL0 | GMAC_TS_CTRL_TSMSTRENA; ++ tsctl |= GMAC_TS_CTRL_TSEVNTENA | GMAC_TS_CTRL_TSIPV4ENA; ++ printk(KERN_INFO "%s HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ \n", __func__); ++ break; ++ ++ case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: ++ /* ++ * V1_L4 Slave ++ * Sync ++ * ++ * SNAPTYPSEL=0, TSMSTRENA=0, TSEVNTENA=1, TSVER2ENA=0, IPV4=1 ++ */ ++ tsctl |= GMAC_TS_CTRL_SNTYPSEL0 | GMAC_TS_CTRL_TSEVNTENA; ++ tsctl |= GMAC_TS_CTRL_TSIPV4ENA; ++ printk(KERN_INFO "%s HWTSTAMP_FILTER_PTP_V1_L4_SYNC \n", __func__); ++ break; ++ ++ case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: ++ /* ++ * V2_L4 UDP any event ++ * SYNC, Follow_Up, Delay_Req, Delay_Resp, ++ * Pdelay_Req, Pdelay_Resp, Pdelay_Resp_Follow_Up ++ * ++ * SNAPTYPSEL=1, TSMSTRENA=x, TSEVNTENA=0, TSVER2ENA=1, IPV4=1 ++ */ ++ tsctl |= GMAC_TS_CTRL_SNTYPSEL1 | GMAC_TS_CTRL_TSVER2ENA | GMAC_TS_CTRL_TSIPV4ENA; ++ printk(KERN_INFO "%s HWTSTAMP_FILTER_PTP_V2_L4_EVENT \n", __func__); ++ break; ++ ++ case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: ++ /* ++ * V2_L4 Master ++ * Delay_Req ++ * ++ * SNAPTYPSEL=0, TSMSTRENA=1, TSEVNTENA=1, TSVER2ENA=1, IPV4=1 ++ */ ++ tsctl |= GMAC_TS_CTRL_SNTYPSEL0 | GMAC_TS_CTRL_TSMSTRENA; ++ tsctl |= GMAC_TS_CTRL_TSEVNTENA | GMAC_TS_CTRL_TSVER2ENA; ++ tsctl |= GMAC_TS_CTRL_TSIPV4ENA; ++ ++ printk(KERN_INFO "%s HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ \n", __func__); ++ break; ++ ++ case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: ++ /* ++ * V2_L4 Slave ++ * Sync ++ * ++ * SNAPTYPSEL=0, TSMSTRENA=0, TSEVNTENA=1, TSVER2ENA=1, IPV4=1 ++ */ ++ tsctl |= GMAC_TS_CTRL_SNTYPSEL0 | GMAC_TS_CTRL_TSVER2ENA | GMAC_TS_CTRL_TSEVNTENA; ++ tsctl |= GMAC_TS_CTRL_TSIPV4ENA; ++ printk(KERN_INFO "%s HWTSTAMP_FILTER_PTP_V2_L4_SYNC \n", __func__); ++ break; ++ ++ case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: ++ /* ++ * V2_L2 Ethernet any event ++ * SYNC, Follow_Up, Delay_Req, Delay_Resp, ++ * Pdelay_Req, Pdelay_Resp, Pdelay_Resp_Follow_Up ++ * ++ * SNAPTYPSEL=1, TSMSTRENA=x, TSEVNTENA=0, TSVER2ENA=1,TSIPENA=1 ++ * TSIPENA=1 ++ */ ++ tsctl |= GMAC_TS_CTRL_SNTYPSEL1 | GMAC_TS_CTRL_TSVER2ENA | GMAC_TS_CTRL_TSIPENA; ++ printk(KERN_INFO "%s HWTSTAMP_FILTER_PTP_V2_L2_EVENT \n", __func__); ++ break; ++ ++ case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: ++ /* ++ * V2_L2 Master ++ * Delay_Req ++ * ++ * SNAPTYPSEL=0, TSMSTRENA=1, TSEVNTENA=1, TSVER2ENA=1,TSIPENA=1 ++ */ ++ tsctl |= GMAC_TS_CTRL_SNTYPSEL0 | GMAC_TS_CTRL_TSMSTRENA; ++ tsctl |= GMAC_TS_CTRL_TSEVNTENA | GMAC_TS_CTRL_TSVER2ENA; ++ tsctl |= GMAC_TS_CTRL_TSIPENA; ++ printk(KERN_INFO "%s HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ \n", __func__); ++ break; ++ ++ case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: ++ /* ++ * V2_L2 Slave ++ * Sync ++ * ++ * SNAPTYPSEL=0, TSMSTRENA=0, TSEVNTENA=1, TSVER2ENA=1, ++ * TSIPENA=1 ++ */ ++ tsctl |= GMAC_TS_CTRL_SNTYPSEL0 | GMAC_TS_CTRL_TSVER2ENA; ++ tsctl |= GMAC_TS_CTRL_TSEVNTENA | GMAC_TS_CTRL_TSIPENA; ++ printk(KERN_INFO "%s HWTSTAMP_FILTER_PTP_V2_L2_SYNC \n", __func__); ++ break; ++ ++ case HWTSTAMP_FILTER_PTP_V2_EVENT: ++ /* ++ * V2_L2 Ethernet any event ++ * SYNC, Follow_Up, Delay_Req, Delay_Resp, ++ * Pdelay_Req, Pdelay_Resp, Pdelay_Resp_Follow_Up ++ * ++ * SNAPTYPSEL=1, TSMSTRENA=x, TSEVNTENA=0, TSVER2ENA=1 ++ * TSIPENA=1, TSIPV4ENA=1 ++ */ ++ tsctl |= GMAC_TS_CTRL_SNTYPSEL1 | GMAC_TS_CTRL_TSVER2ENA; ++ tsctl |= GMAC_TS_CTRL_TSIPENA | GMAC_TS_CTRL_TSIPV4ENA; ++ printk(KERN_INFO "%s HWTSTAMP_FILTER_PTP_V2_EVENT \n", __func__); ++ break; ++ ++ case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: ++ /* ++ * V2_L2_L4 Master ++ * Delay_Req ++ * ++ * SNAPTYPSEL=0, TSMSTRENA=1, TSEVNTENA=1, ++ * TSVER2ENA=1,TSIPENA=1, TSIPV4ENA=1 ++ */ ++ tsctl |= GMAC_TS_CTRL_SNTYPSEL0 | GMAC_TS_CTRL_TSMSTRENA; ++ tsctl |= GMAC_TS_CTRL_TSEVNTENA | GMAC_TS_CTRL_TSVER2ENA; ++ tsctl |= GMAC_TS_CTRL_TSIPENA | GMAC_TS_CTRL_TSIPV4ENA; ++ printk(KERN_INFO "%s HWTSTAMP_FILTER_PTP_V2_DELAY_REQ \n", __func__); ++ break; ++ ++ case HWTSTAMP_FILTER_PTP_V2_SYNC: ++ /* ++ * V2_L2_L4 Slave ++ * Sync ++ * ++ * SNAPTYPSEL=0, TSMSTRENA=0, TSEVNTENA=1, TSVER2ENA=1, ++ * TSIPENA=1, TSIPV4ENA=1 ++ */ ++ tsctl |= GMAC_TS_CTRL_SNTYPSEL0 | GMAC_TS_CTRL_TSVER2ENA; ++ tsctl |= GMAC_TS_CTRL_TSEVNTENA | GMAC_TS_CTRL_TSIPENA; ++ tsctl |= GMAC_TS_CTRL_TSIPV4ENA; ++ printk(KERN_INFO "%s HWTSTAMP_FILTER_PTP_V2_SYNC \n", __func__); ++ break; ++ ++ case HWTSTAMP_FILTER_ALL: ++ /* ++ * V2_L2_L4 Ethernet any event ++ * ++ * SYNC, Follow_Up, Delay_Req, Delay_Resp, ++ * Pdelay_Req, Pdelay_Resp, Pdelay_Resp_Follow_Up ++ * ++ * GMAC_TS_CTRL_TSENALL ++ */ ++ tsctl |= GMAC_TS_CTRL_TSENALL; ++ printk(KERN_INFO "%s HWTSTAMP_FILTER_ALL \n", __func__); ++ break; ++ ++ case HWTSTAMP_FILTER_NONE: ++ printk(KERN_INFO "%s HWTSTAMP_FILTER_NONE \n", __func__); ++ priv->hwts &= ~STMMAC_PTP_HWTS_RX_EN; ++ break; ++ ++ default: ++ printk(KERN_INFO "%s error ioctl rx type %d \n", __func__, config.rx_filter); ++ /* bad/unknown parameter */ ++ return -ERANGE; ++ } ++ ++ /* Set the TS CTRL reg */ ++ tsctl |= GMAC_TS_CTRL_TSENA; ++ writel(tsctl, priv->ioaddr + GMAC_TS_CTRL); ++ wmb(); ++ ++ printk(KERN_INFO "%s sync ts_ctl @ value 0x%08x\n", __func__, tsctl); ++ ++ return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? ++ -EFAULT : 0; ++} ++ ++ ++#define DEFAULT_PTP_CLK 50 ++ ++/** ++ * stmmac_ptp_init_timestamp - initialise the PTP clock ++ * @priv - pointer to the priv structure ++ * ++ * This function initialises the PTP clock consistent with the method spelled ++ * out in the Snopsys documentation ++ */ ++static void stmmac_ptp_init_timestamp(struct stmmac_priv *priv) ++{ ++ unsigned long tsctl = GMAC_TS_CTRL_TSENA, flags = 0; ++ uint8_t ssinc = CONFIG_STMMAC_PTP_CLK_MHZ; ++ ++ /* Enable TS */ ++ writel(tsctl, priv->ioaddr + GMAC_TS_CTRL); ++ wmb(); ++ ++ /* Write SSINC - number of nano seconds to increment on each clock */ ++ if(ssinc == 0){ ++ ssinc = DEFAULT_PTP_CLK; ++ } ++ ssinc = 1000/ssinc; ++ writel(ssinc, priv->ioaddr + GMAC_SS_INC); ++ wmb(); ++ ++ printk(KERN_INFO "%s setting PTP_CLK to 0x%02x\n", __func__, ssinc); ++ ++ /* Reset system time registers to zero */ ++ writel(0x00000000, priv->ioaddr + GMAC_ST_SEC); ++ writel(0x00000000, priv->ioaddr + GMAC_ST_NSEC); ++ wmb(); ++ ++ /* Set TSINT to latch values in ST_SEC and ST_NSEC */ ++ tsctl |= GMAC_TS_CTRL_TSINT; ++ writel(tsctl, priv->ioaddr + GMAC_TS_CTRL); ++ wmb(); ++ printk(KERN_INFO "%s tsctl == 0x%08lx (TSINIT | TSENA)\n", __func__, tsctl); ++ ++ spin_lock_irqsave(&priv->tmreg_lock, flags); ++ ++ /* Init timecounter */ ++ memset(&priv->ccnt, 0, sizeof(priv->ccnt)); ++ priv->ccnt.read = stmmac_ptp_read; ++ priv->ccnt.mask = CLOCKSOURCE_MASK(64); ++ priv->ccnt.mult = 1; ++ priv->ccnt.shift = 0; ++ ++ /* reset the ns time counter */ ++ timecounter_init(&priv->tcnt, &priv->ccnt, ++ ktime_to_ns(ktime_get_real())); ++ ++ spin_unlock_irqrestore(&priv->tmreg_lock, flags); ++} ++ ++/** ++ * stmmac_ptp_init ++ * @priv - the stmmac private priv structure ++ * ++ * This function performs the required steps for enabling ptp ++ * support. If ptp support has already been loaded it simply calls the ++ * cyclecounter init routine and exits. ++ */ ++void stmmac_ptp_init(struct net_device *ndev, struct device * pdev) ++{ ++ struct stmmac_priv *priv = netdev_priv(ndev); ++ ++ /* Ensure the timestamp interrupt is masked */ ++ writel(GMAC_INT_MASK_TSIM, priv->ioaddr + GMAC_INT_MASK); ++ ++ /* Fill out PTP callback contents */ ++ snprintf(priv->ptp_caps.name, 16, "%pm", ndev->dev_addr); ++ priv->ptp_caps.owner = THIS_MODULE; ++ priv->ptp_caps.max_adj = 0; /* Cannot be adjusted */ ++ priv->ptp_caps.n_alarm = 0; ++ priv->ptp_caps.n_ext_ts = 0; ++ priv->ptp_caps.n_per_out = 0; ++ priv->ptp_caps.pps = 0; ++ priv->ptp_caps.adjfreq = stmmac_ptp_adjfreq; ++ priv->ptp_caps.adjtime = stmmac_ptp_adjtime; ++ priv->ptp_caps.gettime = stmmac_ptp_gettime; ++ priv->ptp_caps.settime = stmmac_ptp_settime; ++ priv->ptp_caps.enable = stmmac_ptp_enable; ++ ++ spin_lock_init(&priv->tmreg_lock); ++ ++ stmmac_ptp_init_timestamp(priv); ++ ++ /* Init to default state */ ++// priv->hwts = STMMAC_PTP_OVERFLOW_CHECK_ENABLED; ++ ++ priv->ptp_clock = ptp_clock_register(&priv->ptp_caps, pdev); ++ if (IS_ERR(priv->ptp_clock)) { ++ priv->ptp_clock = NULL; ++ printk(KERN_ERR "%s ptp_clock_reg failed!\n", __func__); ++ } else { ++ printk(KERN_INFO "%s ptp_clock_reg success!\n", __func__); ++ } ++ ++ return; ++} ++ ++/** ++ * stmmac_ptp_remove - disable ptp device and stop the overflow check ++ * @priv: pointer to priv struct ++ * ++ * this function stops the ptp support, and cancels the delayed work. ++ */ ++void stmmac_ptp_remove(struct stmmac_priv *priv) ++{ ++ /* Ensure the timestamp interrupt is masked */ ++ writel(GMAC_INT_MASK_TSIM, priv->ioaddr + GMAC_INT_MASK); ++ ++ /* stop the overflow check task */ ++// priv->hwts &= ~STMMAC_PTP_OVERFLOW_CHECK_ENABLED; ++ ++ if (priv->ptp_clock != NULL) { ++ ptp_clock_unregister(priv->ptp_clock); ++ priv->ptp_clock = NULL; ++ printk(KERN_INFO "%s removed ptp_clock\n", __func__); ++ } ++} ++ +diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c +index 044b532..dc92097 100644 +--- a/drivers/net/phy/mdio_bus.c ++++ b/drivers/net/phy/mdio_bus.c +@@ -95,7 +95,7 @@ static struct class mdio_bus_class = { + + #if IS_ENABLED(CONFIG_OF_MDIO) + /* Helper function for of_mdio_find_bus */ +-static int of_mdio_bus_match(struct device *dev, void *mdio_bus_np) ++static int of_mdio_bus_match(struct device *dev, const void *mdio_bus_np) + { + return dev->of_node == mdio_bus_np; + } +diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig +index c86bae8..6def5f6 100644 +--- a/drivers/platform/x86/Kconfig ++++ b/drivers/platform/x86/Kconfig +@@ -672,6 +672,10 @@ config INTEL_MFLD_THERMAL + Say Y here to enable thermal driver support for the Intel Medfield + platform. + ++if INTEL_QUARK_X1000_SOC ++source "drivers/platform/x86/quark/Kconfig" ++endif ++ + config INTEL_IPS + tristate "Intel Intelligent Power Sharing" + depends on ACPI +diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile +index bf7e4f9..fce76ef 100644 +--- a/drivers/platform/x86/Makefile ++++ b/drivers/platform/x86/Makefile +@@ -50,3 +50,4 @@ obj-$(CONFIG_INTEL_MID_POWER_BUTTON) += intel_mid_powerbtn.o + obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o + obj-$(CONFIG_SAMSUNG_Q10) += samsung-q10.o + obj-$(CONFIG_APPLE_GMUX) += apple-gmux.o ++obj-$(CONFIG_INTEL_QUARK_X1000_SOC) += quark/ +diff --git a/drivers/platform/x86/quark/Kconfig b/drivers/platform/x86/quark/Kconfig +new file mode 100644 +index 0000000..5d47629 +--- /dev/null ++++ b/drivers/platform/x86/quark/Kconfig +@@ -0,0 +1,75 @@ ++config INTEL_CLN_ESRAM ++ bool "eSRAM - embedded SRAM driver for Intel Clanton platform" ++ depends on INTEL_QUARK_X1000_SOC && PM ++ select KALLSYMS ++ select CRC16 ++ help ++ Say Y here to enable eSRAM overlay and software-initiated ECC ++ updates. eSRAM overlaying allows for code/data structures to be ++ mapped into eSRAM thus providing far faster access to code/data ++ than ordinary DRAM. Slower than cache RAM faster than DRAM. ++ ++config INTEL_CLN_ECC_REFRESH_PERIOD ++ int "Choose eSRAM ECC coverage period" ++ depends on INTEL_CLN_ESRAM ++ default 24 ++ help ++ Select the period over which *RAM ECC codes should be refreshed. ++ IA Core will periodically enable disabled eSRAM pages to ensure all of ++ disabled eSRAM pages are 'address walked' in this period. A logical ++ component within the silicon on Clanton will ensure DRAM (and ++ overlayed eSRAM) pages by extension are similarly updated over the ++ same period. This variable controlls how long a time this address ++ walking algorithm should take. For a noisy environment like a ++ sub-station or a satellite update frequently. For less noisy ++ environments this value should be lower. Default 24 hours is right for ++ most people. Set to zero to disable - this is NOT recommended. Max 48 ++ hours. ++ ++config INTEL_CLN_ECC_SCRUB ++ bool "Hardware ECC Scrub - /proc interface for Intel Clanton platform" ++ depends on INTEL_QUARK_X1000_SOC ++ help ++ Say Y here to enable support for accessing the hardware memory ++ ECC Scrubber via the /proc interface. ++ ++config INTEL_CLN_ECC_SCRUB_OVERRIDE_CONFIG ++ bool "Hardware ECC Scrub - use config settings to override scrub vals" ++ depends on INTEL_CLN_ECC_SCRUB ++ help ++ Say Y here to enable support to use config settings to override ++ BIOS configured scrub values ++ ++config INTEL_CLN_HW_ECC_REFRESH_RATE ++ int "Choose DRAM ECC refresh rate" ++ depends on INTEL_CLN_ECC_SCRUB_OVERRIDE_CONFIG ++ default 20 ++ help ++ Range 0 - 255 mSec ++ ++config INTEL_CLN_HW_ECC_REFRESH_SIZE ++ int "Choose DRAM ECC refresh size" ++ depends on INTEL_CLN_ECC_SCRUB_OVERRIDE_CONFIG ++ default 512 ++ help ++ Range 64-512 bytes, multiples of 32 ++ ++config INTEL_CLN_ECC_SCRUB_S3_CONFIG ++ bool "Hardware ECC Scrub - linux manages S3 entry/resume for scrub" ++ depends on INTEL_CLN_ECC_SCRUB ++ help ++ Say Y here to enable linux to manage S3 entry/resume for the ++ hardware memory ECC Scrubber. ++ ++config INTEL_CLN_THERMAL ++ bool "Thermal driver for Intel Clanton platform" ++ depends on INTEL_QUARK_X1000_SOC ++ help ++ Say Y here to enable Clanton's Thermal driver plus the MSI's ++ that can be hooked from the thermal sub-system ++ ++config INTEL_CLN_AUDIO_CTRL ++ tristate "Audio sub-system control driver for Intel Clanton platform" ++ depends on INTEL_QUARK_X1000_SOC ++ help ++ Say Y here to enable Clanton's audio control driver +diff --git a/drivers/platform/x86/quark/Makefile b/drivers/platform/x86/quark/Makefile +new file mode 100644 +index 0000000..00c4ce7 +--- /dev/null ++++ b/drivers/platform/x86/quark/Makefile +@@ -0,0 +1,18 @@ ++obj-$(CONFIG_INTEL_QUARK_X1000_SOC) += intel_cln_board_data.o ++obj-$(CONFIG_INTEL_QUARK_X1000_SOC) += intel_cln_layout_data.o ++obj-$(CONFIG_INTEL_QUARK_X1000_SOC) += intel_cln_plat_data.o ++obj-$(CONFIG_INTEL_QUARK_X1000_SOC) += intel_cln_plat_clanton_hill.o ++obj-$(CONFIG_INTEL_QUARK_X1000_SOC) += intel_cln_plat_clanton_peak.o ++obj-$(CONFIG_INTEL_QUARK_X1000_SOC) += intel_cln_plat_cross_hill.o ++obj-$(CONFIG_INTEL_QUARK_X1000_SOC) += intel_cln_plat_kips_bay.o ++obj-$(CONFIG_INTEL_QUARK_X1000_SOC) += intel_cln_plat_galileo.o ++obj-$(CONFIG_INTEL_QUARK_X1000_SOC) += intel_cln_sb.o ++obj-$(CONFIG_INTEL_QUARK_X1000_SOC) += intel_cln_imr.o ++obj-$(CONFIG_INTEL_QUARK_X1000_SOC) += intel_cln_imr_kernel.o ++obj-$(CONFIG_INTEL_CLN_ESRAM) += intel_cln_esram.o ++obj-$(CONFIG_INTEL_QUARK_X1000_SOC) += intel_cln_imr_test.o ++obj-$(CONFIG_INTEL_CLN_ESRAM) += intel_cln_esram_test.o ++#obj-$(CONFIG_INTEL_CLN_ESRAM) += intel_cln_smep_test.o ++obj-$(CONFIG_INTEL_CLN_ECC_SCRUB) += intel_cln_ecc_scrub.o ++obj-$(CONFIG_INTEL_CLN_THERMAL) += intel_cln_thermal.o ++obj-$(CONFIG_INTEL_CLN_AUDIO_CTRL) += intel_cln_audio_ctrl.o +diff --git a/drivers/platform/x86/quark/intel_cln_audio_ctrl.c b/drivers/platform/x86/quark/intel_cln_audio_ctrl.c +new file mode 100644 +index 0000000..95bfe78 +--- /dev/null ++++ b/drivers/platform/x86/quark/intel_cln_audio_ctrl.c +@@ -0,0 +1,514 @@ ++/* ++ * Intel Clanton platform audio control driver ++ * ++ * Copyright(c) 2013 Intel Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ * ++ * The Intel Clanton Hill platform hardware design includes an audio subsystem ++ * with a number of interconnected audio interfaces. This driver enables ++ * applications to choose which audio connections to enable for various ++ * application use cases. The interconnections are selectable using GPIO output ++ * pins on the CPU. This driver is also responsible for configuring a Maxim ++ * 9867 audio codec, a component of this audio subsystem, connected to the CPU ++ * via I2C. ++ */ ++ ++#include <linux/module.h> ++#include <linux/printk.h> ++#include <linux/platform_device.h> ++#include <linux/types.h> ++#include <linux/err.h> ++#include <linux/i2c.h> ++#include <linux/gpio.h> ++#include <linux/cdev.h> ++#include <linux/fs.h> ++#include <uapi/linux/ioctl.h> ++#include <linux/mutex.h> ++#include <linux/sysfs.h> ++ ++#include "intel_cln_audio_ctrl.h" ++ ++#define DRIVER_NAME "intel_cln_audio_ctrl" ++ ++/* ++ * GPIO numbers to use for switching audio paths ++ */ ++#define GPIO_AUDIO_S0 11 ++#define GPIO_AUDIO_S1 12 ++#define GPIO_AUDIO_S2 13 ++ ++#define GPIO_AUDIO_DEFAULT (INTEL_CLN_AUDIO_MODE_SPKR_MIC) ++ ++/** ++ * struct intel_cln_audio_ctrl_data ++ * ++ * Structure to represent module state/data/etc ++ */ ++struct intel_cln_audio_ctrl_priv { ++ ++ /* i2c device descriptor for read/write access to MAX9867 registers */ ++ struct i2c_client *max9867_i2c; ++ ++ /* Char dev to provide user-space ioctl interface for audio control */ ++ struct cdev cdev; ++ dev_t cdev_no; ++ struct class *cl; ++ ++ /* Mutex to protect against concurrent access to the ioctl() handler */ ++ struct mutex lock; ++ ++ /* Current GPIO switch value */ ++ unsigned char gpio_val; ++}; ++ ++static int ++intel_cln_audio_ctrl_open(struct inode *inode, struct file *filp) ++{ ++ struct intel_cln_audio_ctrl_priv *priv; ++ ++ priv = container_of(inode->i_cdev, ++ struct intel_cln_audio_ctrl_priv, ++ cdev); ++ filp->private_data = priv; ++ ++ return 0; ++} ++ ++static int ++intel_cln_audio_ctrl_release(struct inode *inode, struct file *filp) ++{ ++ return 0; ++} ++ ++/* ++ * Logic truth table for AUDIO_S[0-3] outputs, illustrating which paths are ++ * connected between audio interfaces A, B, C. Each audio interface has one ++ * effective input (I) port and one effective output (O) port ++ * ++ * A = USB Codec (to Clanton CPU) ++ * B = Spkr/Mic (to car audio system) ++ * C = I2S Codec (to Telit HE910) ++ * ++ * PATH examples: ++ * AO-CO: A-Output connected to C-Output ++ * BI-AI: B-Input connected to A-Input ++ * ++ * NOTE: Assume a CI-AI connection is available in ALL cases (sometimes unused) ++ * ++ * S2 S1 S0 PATHS USE CASE ++ * -- -- -- ----------------- ------------------------------------------------- ++ * 0 0 0 AO-CO BT Headset call ++ * 0 0 1 AO-BO Analog Driver Alerts (CI unused) ++ * 0 1 0 AO-CO,BI-AI XX Unused/invalid (BI *and* CI connected to AI) ++ * 0 1 1 AO-BO,BI-AI Archival Voice Record/Playback (or Driver Alerts) ++ * 1 0 0 AO-CO,BI-CO XX Unused/invalid (A0 *and* BI connected to CO) ++ * 1 0 1 AO-BO,BI-CO Analog hands-free call ++ * 1 1 0 AO-CO,BI-AI,BI-CO XX Unused/invalid (BI connected to AI *and* CO) ++ * 1 1 1 AO-BO,BI-AI,BI-CO XX Unused/invalid (BI connected to AI *and* CO) ++ * ++ * ++ * Mapping to IOCTLs (using more intuitive naming on the API): ++ * ++ * PATHS IOCTL ++ * --------------- ------------------------------------------------------------- ++ * AO-CO INTEL_CLN_AUDIO_MODE_GSM_ONLY ++ * AO-BO INTEL_CLN_AUDIO_MODE_SPKR_ONLY ++ * AO-BO,BI-AI INTEL_CLN_AUDIO_MODE_SPKR_MIC ++ * AO-BO,BI-CO INTEL_CLN_AUDIO_MODE_GSM_SPKR_MIC ++ */ ++ ++static int ++intel_cln_audio_ctrl_gpio_update(struct intel_cln_audio_ctrl_priv *priv) ++{ ++ int ret = 0; ++ struct gpio audio_sw_gpios[] = { ++ { ++ GPIO_AUDIO_S2, ++ GPIOF_OUT_INIT_LOW, ++ "audio_s2" ++ }, ++ { ++ GPIO_AUDIO_S1, ++ GPIOF_OUT_INIT_LOW, ++ "audio_s1" ++ }, ++ { ++ GPIO_AUDIO_S0, ++ GPIOF_OUT_INIT_LOW, ++ "audio_s0" ++ } ++ }; ++ ++ /* ++ * Update the Audio Switch GPIO outputs according to the user selection ++ */ ++ ret = gpio_request_array(audio_sw_gpios, ++ ARRAY_SIZE(audio_sw_gpios)); ++ if (ret) { ++ pr_err("%s: Failed to allocate audio control GPIO pins\n", ++ __func__); ++ return ret; ++ } ++ ++ gpio_set_value(GPIO_AUDIO_S2, (priv->gpio_val >> 2) & 0x1); ++ gpio_set_value(GPIO_AUDIO_S1, (priv->gpio_val >> 1) & 0x1); ++ gpio_set_value(GPIO_AUDIO_S0, (priv->gpio_val >> 0) & 0x1); ++ ++ gpio_free_array(audio_sw_gpios, ++ ARRAY_SIZE(audio_sw_gpios)); ++ ++ return 0; ++} ++ ++static long ++intel_cln_audio_ctrl_ioctl(struct file *filp, ++ unsigned int cmd, ++ unsigned long arg) ++{ ++ struct intel_cln_audio_ctrl_priv *priv = filp->private_data; ++ int ret = 0; ++ ++ ret = mutex_lock_interruptible(&priv->lock); ++ if (ret) ++ return ret; ++ ++ switch (cmd) { ++ case INTEL_CLN_AUDIO_MODE_IOC_GSM_ONLY: ++ case INTEL_CLN_AUDIO_MODE_IOC_SPKR_ONLY: ++ case INTEL_CLN_AUDIO_MODE_IOC_SPKR_MIC: ++ case INTEL_CLN_AUDIO_MODE_IOC_GSM_SPKR_MIC: ++ break; ++ default: ++ ret = -EINVAL; ++ goto exit; ++ } ++ ++ priv->gpio_val = _IOC_NR(cmd) & 0x7; ++ ret = intel_cln_audio_ctrl_gpio_update(priv); ++exit: ++ mutex_unlock(&priv->lock); ++ return ret; ++} ++ ++static const struct file_operations intel_cln_audio_ctrl_fops = { ++ .owner = THIS_MODULE, ++ .open = intel_cln_audio_ctrl_open, ++ .release = intel_cln_audio_ctrl_release, ++ .unlocked_ioctl = intel_cln_audio_ctrl_ioctl ++}; ++ ++static int ++intel_cln_audio_ctrl_chrdev_init(struct intel_cln_audio_ctrl_priv *priv) ++{ ++ /* Register a character dev interface (with ioctls) ++ * to allow control of the audio subsystem switch ++ */ ++ int ret; ++ struct device *dev; ++ ++ ret = alloc_chrdev_region(&priv->cdev_no, 0, 1, ++ "intel_cln_audio_ctrl"); ++ if (ret) { ++ pr_err("Failed to alloc chrdev: %d", ret); ++ return ret; ++ } ++ ++ cdev_init(&priv->cdev, &intel_cln_audio_ctrl_fops); ++ ++ ret = cdev_add(&priv->cdev, priv->cdev_no, 1); ++ if (ret) { ++ pr_err("Failed to add cdev: %d", ret); ++ unregister_chrdev_region(priv->cdev_no, 1); ++ return ret; ++ } ++ ++ priv->cl = class_create(THIS_MODULE, "char"); ++ if (IS_ERR(priv->cl)) { ++ pr_err("Failed to create device class: %ld", ++ PTR_ERR(priv->cl)); ++ cdev_del(&priv->cdev); ++ unregister_chrdev_region(priv->cdev_no, 1); ++ return PTR_ERR(priv->cl); ++ } ++ ++ dev = device_create(priv->cl, NULL, priv->cdev_no, NULL, ++ "intel_cln_audio_ctrl"); ++ if (IS_ERR(dev)) { ++ pr_err("Failed to create device: %ld", ++ PTR_ERR(priv->cl)); ++ class_destroy(priv->cl); ++ cdev_del(&priv->cdev); ++ unregister_chrdev_region(priv->cdev_no, 1); ++ return PTR_ERR(dev); ++ } ++ ++ return 0; ++} ++ ++static int ++intel_cln_audio_ctrl_chrdev_remove(struct intel_cln_audio_ctrl_priv *priv) ++{ ++ device_destroy(priv->cl, priv->cdev_no); ++ class_destroy(priv->cl); ++ cdev_del(&priv->cdev); ++ unregister_chrdev_region(priv->cdev_no, 1); ++ ++ return 0; ++} ++ ++ ++ssize_t intel_cln_audio_ctrl_sysfs_show_mode(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct intel_cln_audio_ctrl_priv *priv = dev_get_drvdata(dev); ++ int ret; ++ char *mode; ++ ++ ret = mutex_lock_interruptible(&priv->lock); ++ if (ret) ++ return ret; ++ ++ switch (priv->gpio_val) { ++ case INTEL_CLN_AUDIO_MODE_GSM_ONLY: ++ mode = "gsm"; ++ break; ++ case INTEL_CLN_AUDIO_MODE_SPKR_ONLY: ++ mode = "spkr"; ++ break; ++ case INTEL_CLN_AUDIO_MODE_SPKR_MIC: ++ mode = "spkr_mic"; ++ break; ++ case INTEL_CLN_AUDIO_MODE_GSM_SPKR_MIC: ++ mode = "gsm_spkr_mic"; ++ break; ++ default: ++ ret = -EINVAL; ++ goto exit; ++ } ++ ++ ret = scnprintf(buf, PAGE_SIZE, "%s\n", mode); ++ ++exit: ++ mutex_unlock(&priv->lock); ++ return ret; ++} ++ ++ssize_t intel_cln_audio_ctrl_sysfs_store_mode(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct intel_cln_audio_ctrl_priv *priv = dev_get_drvdata(dev); ++ char mode[16]; ++ unsigned char gpio_val; ++ int ret = count; ++ ++ sscanf(buf, "%15s", mode); ++ ++ if (!strcmp(mode, "gsm")) ++ gpio_val = INTEL_CLN_AUDIO_MODE_GSM_ONLY; ++ else if (!strcmp(mode, "spkr")) ++ gpio_val = INTEL_CLN_AUDIO_MODE_SPKR_ONLY; ++ else if (!strcmp(mode, "spkr_mic")) ++ gpio_val = INTEL_CLN_AUDIO_MODE_SPKR_MIC; ++ else if (!strcmp(mode, "gsm_spkr_mic")) ++ gpio_val = INTEL_CLN_AUDIO_MODE_GSM_SPKR_MIC; ++ else ++ return -EINVAL; ++ ++ ret = mutex_lock_interruptible(&priv->lock); ++ if (ret) ++ return ret; ++ ++ priv->gpio_val = gpio_val; ++ ret = intel_cln_audio_ctrl_gpio_update(priv); ++ if (ret) ++ goto exit; ++ ++ ret = count; ++ ++exit: ++ mutex_unlock(&priv->lock); ++ ++ return ret; ++} ++ ++/* Sysfs attribute descriptor (for alternative user-space interface) */ ++static DEVICE_ATTR(audio_switch_mode, S_IWUSR | S_IRUGO, ++ intel_cln_audio_ctrl_sysfs_show_mode, ++ intel_cln_audio_ctrl_sysfs_store_mode); ++ ++/****************************************************************************** ++ * Module hooks ++ ******************************************************************************/ ++ ++static int ++intel_cln_max9867_init(struct i2c_client *client) ++{ ++ int ret; ++ ++ /* MAX9867 register configuration, from Telit HE910 DVI app-note */ ++ ++ u8 reg_cfg_seq1[] = { ++ 0x04, /* Starting register address, followed by data */ ++ 0x00, /* 0x04 Interrupt Enable */ ++ 0x10, /* 0x05 System Clock */ ++ 0x90, /* 0x06 Audio Clock High */ ++ 0x00, /* 0x07 Audio Clock Low */ ++ 0x10, /* 0x08 Interface 1a */ ++ 0x0A, /* 0x09 Interface 1d */ ++ 0x33, /* 0x0A Codec Filters */ ++ 0x00, /* 0x0B DAC Gain/Sidetone */ ++ 0x00, /* 0x0C DAC Level */ ++ 0x33, /* 0x0D ADC Level */ ++ 0x4C, /* 0x0E Left Line Input Level */ ++ 0x4C, /* 0x0F Right Line Input Level */ ++ 0x00, /* 0x10 Left Volume Control */ ++ 0x00, /* 0x11 Right Volume Control */ ++ 0x14, /* 0x12 Left Mic Gain */ ++ 0x14, /* 0x13 Right Mic Gain */ ++ /* Configuration */ ++ 0xA0, /* 0x14 Input */ ++ 0x00, /* 0x15 Microphone */ ++ 0x65 /* 0x16 Mode */ ++ }; ++ ++ u8 reg_cfg_seq2[] = { ++ 0x17, /* Starting register address, followed by data */ ++ 0xEF /* 0x17 System Shutdown */ ++ }; ++ ++ ret = i2c_master_send(client, ++ reg_cfg_seq1, sizeof(reg_cfg_seq1)); ++ if (ret != sizeof(reg_cfg_seq1)) { ++ pr_err("Failed to write MAX9867 config registers (set 1/2)"); ++ return -EIO; ++ } ++ ++ ret = i2c_master_send(client, ++ reg_cfg_seq2, sizeof(reg_cfg_seq2)); ++ if (ret != sizeof(reg_cfg_seq2)) { ++ pr_err("Failed to write MAX9867 config registers (set 2/2)"); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++static int ++intel_cln_max9867_get_chip_rev(struct i2c_client *client) ++{ ++ struct i2c_msg msg[2]; ++ u8 data[2]; ++ int ret; ++ ++ data[0] = 0xFF; /* Chip-revision register address = 0xFF */ ++ msg[0].addr = client->addr; ++ msg[0].flags = 0; ++ msg[0].buf = &data[0]; ++ msg[0].len = 1; ++ ++ msg[1].addr = client->addr; ++ msg[1].flags = I2C_M_RD; ++ msg[1].buf = &data[1]; ++ msg[1].len = 1; ++ ++ ret = i2c_transfer(client->adapter, &msg[0], 2); ++ return (ret == 2) ? data[1] : -EIO; ++} ++ ++static int intel_cln_max9867_i2c_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct intel_cln_audio_ctrl_priv *priv; ++ int ret; ++ ++ priv = devm_kzalloc(&client->dev, sizeof(*priv), ++ GFP_KERNEL); ++ if (priv == NULL) ++ return -ENOMEM; ++ ++ i2c_set_clientdata(client, priv); ++ ++ priv->max9867_i2c = client; ++ mutex_init(&priv->lock); ++ ++ ret = intel_cln_max9867_get_chip_rev(client); ++ if (ret >= 0) ++ pr_info("%s: Detected MAX9867 chip revision 0x%02X\n", ++ __func__, ret); ++ else { ++ pr_err("%s: Failed to read MAX9867 chip revision\n", __func__); ++ goto exit; ++ } ++ ++ ret = intel_cln_max9867_init(client); ++ if (ret) ++ goto exit; ++ ++ priv->gpio_val = GPIO_AUDIO_DEFAULT; ++ ret = intel_cln_audio_ctrl_gpio_update(priv); ++ if (ret) ++ goto exit; ++ ++ /* Create a char dev interface, providing an ioctl config option */ ++ ret = intel_cln_audio_ctrl_chrdev_init(priv); ++ if (ret) ++ goto exit; ++ ++ /* Also create a sysfs interface, providing a cmd line config option */ ++ ret = sysfs_create_file(&client->dev.kobj, ++ &dev_attr_audio_switch_mode.attr); ++ ++exit: ++ return ret; ++} ++ ++static int intel_cln_max9867_i2c_remove(struct i2c_client *client) ++{ ++ struct intel_cln_audio_ctrl_priv *priv = i2c_get_clientdata(client); ++ ++ intel_cln_audio_ctrl_chrdev_remove(priv); ++ ++ sysfs_remove_file(&client->dev.kobj, &dev_attr_audio_switch_mode.attr); ++ ++ return 0; ++} ++ ++static const struct i2c_device_id intel_cln_max9867_i2c_id[] = { ++ {"intel-cln-max9867", 0}, ++ {} ++}; ++MODULE_DEVICE_TABLE(i2c, intel_cln_max9867_i2c_id); ++ ++/* i2c codec control layer */ ++static struct i2c_driver intel_cln_audio_ctrl_i2c_driver = { ++ .driver = { ++ .name = "intel_cln_audio_ctrl", ++ .owner = THIS_MODULE, ++ }, ++ .probe = intel_cln_max9867_i2c_probe, ++ .remove = intel_cln_max9867_i2c_remove, ++ .id_table = intel_cln_max9867_i2c_id, ++}; ++ ++module_i2c_driver(intel_cln_audio_ctrl_i2c_driver); ++ ++MODULE_AUTHOR("Dan O'Donovan <dan@emutex.com>"); ++MODULE_DESCRIPTION("Intel Clanton platform audio control driver"); ++MODULE_LICENSE("Dual BSD/GPL"); +diff --git a/drivers/platform/x86/quark/intel_cln_audio_ctrl.h b/drivers/platform/x86/quark/intel_cln_audio_ctrl.h +new file mode 100644 +index 0000000..b6c4692 +--- /dev/null ++++ b/drivers/platform/x86/quark/intel_cln_audio_ctrl.h +@@ -0,0 +1,45 @@ ++/* ++ * Intel Clanton platform audio control driver ++ * ++ * Copyright(c) 2013 Intel Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ * ++ * See intel_cln_audio_ctrl.c for a detailed description ++ * ++ */ ++ ++#ifndef __INTEL_CLN_AUDIO_CTRL_H__ ++#define __INTEL_CLN_AUDIO_CTRL_H__ ++ ++#include <linux/module.h> ++ ++#define INTEL_CLN_AUDIO_MODE_GSM_ONLY 0x0 ++#define INTEL_CLN_AUDIO_MODE_SPKR_ONLY 0x1 ++#define INTEL_CLN_AUDIO_MODE_SPKR_MIC 0x3 ++#define INTEL_CLN_AUDIO_MODE_GSM_SPKR_MIC 0x5 ++ ++#define INTEL_CLN_AUDIO_MODE_IOC_GSM_ONLY \ ++ _IO('x', INTEL_CLN_AUDIO_MODE_GSM_ONLY) ++#define INTEL_CLN_AUDIO_MODE_IOC_SPKR_ONLY \ ++ _IO('x', INTEL_CLN_AUDIO_MODE_SPKR_ONLY) ++#define INTEL_CLN_AUDIO_MODE_IOC_SPKR_MIC \ ++ _IO('x', INTEL_CLN_AUDIO_MODE_SPKR_MIC) ++#define INTEL_CLN_AUDIO_MODE_IOC_GSM_SPKR_MIC \ ++ _IO('x', INTEL_CLN_AUDIO_MODE_GSM_SPKR_MIC) ++ ++#endif /* __INTEL_CLN_AUDIO_CTRL_H__ */ +diff --git a/drivers/platform/x86/quark/intel_cln_board_data.c b/drivers/platform/x86/quark/intel_cln_board_data.c +new file mode 100644 +index 0000000..da226c7 +--- /dev/null ++++ b/drivers/platform/x86/quark/intel_cln_board_data.c +@@ -0,0 +1,207 @@ ++/* ++ * Copyright(c) 2013 Intel Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ */ ++/* ++ * Intel Clanton Legacy Platform Data accessor layer ++ * ++ * Simple Legacy SPI flash access layer ++ * ++ * Author : Bryan O'Donoghue <bryan.odonoghue@linux.intel.com> 2013 ++ */ ++ ++#include <asm/io.h> ++#include <linux/errno.h> ++#include <linux/ioport.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/printk.h> ++ ++#define DRIVER_NAME "board_data" ++#define PFX "MFH: " ++#define SPIFLASH_BASEADDR 0xFFF00000 ++#define MFH_OFFSET 0x00008000 ++#define PLATFORM_DATA_OFFSET 0x00010000 ++#define MTD_PART_OFFSET 0x00050000 ++#define MTD_PART_LEN 0x00040000 ++#define MFH_PADDING 0x1E8 ++#define MFH_MAGIC 0x5F4D4648 ++#define FLASH_SIZE 0x00400000 ++ ++/* MFH types supported @ version #1 */ ++#define MFH_ITEM_FW_STAGE1 0x00000000 ++#define MFH_ITEM_FW_STAGE1_SIGNED 0x00000001 ++#define MFH_ITEM_FW_STAGE2 0x00000003 ++#define MFH_ITEM_FW_STAGE2_SIGNED 0x00000004 ++#define MFH_ITEM_FW_STAGE2_CONFIG 0x00000005 ++#define MFH_ITEM_FW_STAGE2_CONFIG_SIGNED 0x00000006 ++#define MFH_ITEM_FW_PARAMS 0x00000007 ++#define MFH_ITEM_FW_RECOVERY 0x00000008 ++#define MFH_ITEM_FW_RECOVERY_SIGNED 0x00000009 ++#define MFH_ITEM_BOOTLOADER 0x0000000B ++#define MFH_ITEM_BOOTLOADER_SIGNED 0x0000000C ++#define MFH_ITEM_BOOTLOADER_CONFIG 0x0000000D ++#define MFH_ITEM_BOOTLOADER_CONFIG_SIGNED 0x0000000E ++#define MFH_ITEM_KERNEL 0x00000010 ++#define MFH_ITEM_KERNEL_SIGNED 0x00000011 ++#define MFH_ITEM_RAMDISK 0x00000012 ++#define MFH_ITEM_RAMDISK_SIGNED 0x00000013 ++#define MFH_ITEM_LOADABLE_PROGRAM 0x00000015 ++#define MFH_ITEM_LOADABLE_PROGRAM_SIGNED 0x00000016 ++#define MFH_ITEM_BUILD_INFO 0x00000018 ++#define MFH_ITEM_VERSION 0x00000019 ++ ++struct intel_cln_mfh { ++ u32 id; ++ u32 ver; ++ u32 flags; ++ u32 next_block; ++ u32 item_count; ++ u32 boot_priority_list; ++ u8 padding[MFH_PADDING]; ++}; ++ ++struct intel_cln_mfh_item { ++ u32 type; ++ u32 addr; ++ u32 len; ++ u32 res0; ++}; ++ ++static struct resource conf_res __initdata = { ++ .flags = IORESOURCE_MEM, ++ .start = 0, ++ .end = 0, ++}; ++ ++static struct resource plat_res __initdata = { ++ .flags = IORESOURCE_MEM, ++ .start = 0, ++ .end = 0, ++}; ++ ++static struct platform_device conf_pdev = { ++ .name = "cln-layout-conf", ++ .id = -1, ++ .resource = &conf_res, ++}; ++ ++struct kobject * board_data_kobj; ++EXPORT_SYMBOL_GPL(board_data_kobj); ++ ++static bool mfh_plat_found = false; ++ ++static long unsigned int flash_version_data; ++static ssize_t flash_version_show(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ return snprintf(buf, 12, "%#010lx\n", flash_version_data); ++} ++ ++static struct kobj_attribute flash_version_attr = ++ __ATTR(flash_version, 0644, flash_version_show, NULL); ++ ++extern int intel_cln_plat_probe(struct resource * pres); ++ ++/** ++ * intel_cln_board_data_init ++ * ++ * Module entry point ++ */ ++static int __init intel_cln_board_data_init(void) ++{ ++ extern struct kobject * firmware_kobj; ++ struct intel_cln_mfh __iomem * mfh; ++ struct intel_cln_mfh_item __iomem * item; ++ struct platform_device * pdev; ++ u32 count; ++ void __iomem * spi_data; ++ int ret = 0; ++ ++ spi_data = ioremap(SPIFLASH_BASEADDR, FLASH_SIZE); ++ if (!spi_data) ++ return -ENODEV; ++ ++ /* get mfh and first item pointer */ ++ mfh = spi_data + MFH_OFFSET; ++ if (mfh->id != MFH_MAGIC){ ++ pr_err(PFX"Bad MFH magic want 0x%08x found 0x%08x @ 0x%p\n", ++ MFH_MAGIC, mfh->id, &mfh->id); ++ return -ENODEV; ++ } ++ ++ pr_info(PFX"mfh @ 0x%p: id 0x%08lx ver 0x%08lx entries 0x%08lx\n", ++ mfh, (unsigned long)mfh->id, (unsigned long)mfh->ver, ++ (unsigned long)mfh->item_count); ++ item = (struct intel_cln_mfh_item __iomem *) ++ &mfh->padding [sizeof(u32) * mfh->boot_priority_list]; ++ ++ /* board_data_kobj subordinate of firmware @ /sys/firmware/board_data */ ++ board_data_kobj = kobject_create_and_add("board_data", firmware_kobj); ++ if (!board_data_kobj) { ++ pr_err(PFX"kset create error\n"); ++ return -ENODEV; ++ } ++ ++ /* Register flash regions as seperate platform devices */ ++ for (count = 0; count < mfh->item_count; count++, item++){ ++ pdev = NULL; ++ ++ switch (item->type){ ++ case MFH_ITEM_BUILD_INFO: ++ conf_res.start = item->addr; ++ conf_res.end = item->addr + item->len; ++ pdev = &conf_pdev; ++ break; ++ case MFH_ITEM_VERSION: ++ flash_version_data = item->res0; ++ if(sysfs_create_file(board_data_kobj, ++ &flash_version_attr.attr)) { ++ pr_err("failed to create sysfs entry for flash version\n"); ++ flash_version_data = 0; ++ } ++ break; ++ default: ++ break; ++ } ++ ++ if (pdev != NULL) ++ platform_device_register(pdev); ++ } ++ ++ /* This ought to be encoded in the MFH ! */ ++ if (mfh_plat_found == false){ ++ pr_err(PFX"Warning platform data MFH missing - using hardcoded " ++ "offsets\n"); ++ ++ /* Platform data */ ++ plat_res.start = SPIFLASH_BASEADDR + PLATFORM_DATA_OFFSET; ++ count = *(uint32_t*)(spi_data + PLATFORM_DATA_OFFSET + sizeof(uint32_t)); ++ plat_res.end = count; ++ ret = intel_cln_plat_probe(&plat_res); ++ } ++ ++ iounmap(spi_data); ++ return ret; ++} ++ ++MODULE_AUTHOR("Bryan O'Donoghue <bryan.odonoghue@intel.com>"); ++MODULE_DESCRIPTION("Intel Clanton SPI Data API"); ++MODULE_LICENSE("Dual BSD/GPL"); ++subsys_initcall(intel_cln_board_data_init); ++ +diff --git a/drivers/platform/x86/quark/intel_cln_ecc_scrub.c b/drivers/platform/x86/quark/intel_cln_ecc_scrub.c +new file mode 100644 +index 0000000..1fb46f6 +--- /dev/null ++++ b/drivers/platform/x86/quark/intel_cln_ecc_scrub.c +@@ -0,0 +1,668 @@ ++/* ++ * Copyright(c) 2013 Intel Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ */ ++/* ++ * Intel Clanton DRAM ECC Scrub driver ++ * ++ * !!!!!!! Description ++ * ++ */ ++#include <asm-generic/uaccess.h> ++#include <linux/intel_cln_sb.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/printk.h> ++#include <linux/platform_device.h> ++#include <linux/proc_fs.h> ++ ++#define DRIVER_NAME "intel-cln-ecc" ++#define INTEL_CLN_ECC_SCRUB_PROCDIR "driver/ecc_scrub" ++#define STATUS "status" ++#define CONTROL "control" ++#define INTERVAL "interval" ++#define ECC_BLOCK_SIZE "block_size" ++ ++#define CONTROL_USAGE "ECC Scrub Control: invalid setting. "\ ++ "Valid values are 1 or 0\n" ++#define CONTROL_SCRUB_ON_STR "1\n" ++#define CONTROL_SCRUB_OFF_STR "0\n" ++#define CONTROL_ON_STR "on\n" ++#define CONTROL_OFF_STR "off\n" ++ ++#define INTERVAL_USAGE "ECC Scrub Interval: invalid setting. "\ ++ "Valid range is 1 - 255\n" ++#define SIZE_USAGE "ECC Scrub Block Size: invalid setting. "\ ++ "Valid range is 64 - 512\n" ++ ++#define OVERRIDE_CONFIG_PARM_DESC "Clanton ECC Scrub - "\ ++ "Override BIOS settings "\ ++ "for Scrub Config" ++ ++#define OVERRIDE_START_PARM_DESC "Clanton ECC Scrub - "\ ++ "Override BIOS settings "\ ++ "for Scrub Start address" ++ ++#define OVERRIDE_END_PARM_DESC "Clanton ECC Scrub - "\ ++ "Override BIOS settings "\ ++ "for Scrub End address" ++ ++#define OVERRIDE_NEXT_PARM_DESC "Clanton ECC Scrub - "\ ++ "Override BIOS settings "\ ++ "for Scrub Next address" ++ ++#define MAX_SCRUB_BLOCK_SIZE 512 ++#define MIN_SCRUB_BLOCK_SIZE 64 ++#define MAX_SCRUB_REFRESH 255 ++#define MIN_SCRUB_REFRESH 0 ++ ++#define NOT_OVERRIDDEN 0xfffffffful ++ ++/* Shorten fn names to fit 80 char limit */ ++#ifndef sb_read ++#define sb_read intel_cln_sb_read_reg ++#endif ++#ifndef sb_write ++#define sb_write intel_cln_sb_write_reg ++#endif ++ ++/* Register ID */ ++#define ECC_SCRUB_CONFIG_REG (0x50) ++#define ECC_SCRUB_START_MEM_REG (0x76) ++#define ECC_SCRUB_END_MEM_REG (0x77) ++#define ECC_SCRUB_NEXT_READ_REG (0x7C) ++ ++ ++/* Reg commands */ ++#define THERMAL_CTRL_READ (0x10) ++#define THERMAL_CTRL_WRITE (0x11) ++#define THERMAL_RESUME_SCRUB (0xC2) ++#define THERMAL_PAUSE_SCRUB (0xC3) ++ ++/** ++ * struct intel_cln_ecc_scrub_dev ++ * ++ * Structure to represent module state/data/etc ++ */ ++struct intel_cln_ecc_scrub_dev { ++ ++ /* Linux kernel structures */ ++ struct platform_device *pldev; /* Platform device */ ++ ++ /* Register copies */ ++ u32 start_address; ++ u32 end_address; ++ u32 next_address; ++ u32 config; ++ ++}; ++ ++static struct intel_cln_ecc_scrub_dev ecc_scrub_dev; ++ ++static u32 ecc_scrub_config_override = NOT_OVERRIDDEN; ++static u32 ecc_scrub_start_override = NOT_OVERRIDDEN; ++static u32 ecc_scrub_end_override = NOT_OVERRIDDEN; ++static u32 ecc_scrub_next_override = NOT_OVERRIDDEN; ++ ++/** ++ * intel_cln_ecc_scrub_stat_show ++ * ++ * @param dev: pointer to device ++ * @param attr: attribute pointer ++ * @param buf: output buffer ++ * @return number of bytes successfully read ++ * ++ * Populates ecc_scrub state via /sys/device/platform/intel-cln-ecc/status ++ */ ++static ssize_t ++intel_cln_ecc_scrub_stat_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ int len = 0, size = 0; ++ unsigned int count = PAGE_SIZE; ++ u32 reg_data = 0; ++ char *scrub_status = CONTROL_OFF_STR; ++ ++ /* Display start of memory address */ ++ sb_read(SB_ID_THERMAL, THERMAL_CTRL_READ, ECC_SCRUB_START_MEM_REG, ++ ®_data, 1); ++ len += snprintf(buf + len, count - len, ++ "ecc scrub mem start\t\t\t: 0x%08x\n", reg_data); ++ ++ ++ /* Display end of memory address */ ++ sb_read(SB_ID_THERMAL, THERMAL_CTRL_READ, ECC_SCRUB_END_MEM_REG, ++ ®_data, 1); ++ len += snprintf(buf + len, count - len, ++ "ecc scrub mem end\t\t\t: 0x%08x\n", reg_data); ++ ++ /* Display next address to be read */ ++ sb_read(SB_ID_THERMAL, THERMAL_CTRL_READ, ECC_SCRUB_NEXT_READ_REG, ++ ®_data, 1); ++ len += snprintf(buf + len, count - len, ++ "ecc scrub next read\t\t\t: 0x%08x\n", reg_data); ++ ++ /* Display config settings */ ++ sb_read(SB_ID_THERMAL, THERMAL_CTRL_READ, ECC_SCRUB_CONFIG_REG, ++ ®_data, 1); ++ ++ /* Interval is the lsbyte of the config reg, so mask out just ++ * that byte in the data printed. */ ++ len += snprintf(buf + len, count - len, ++ "ecc scrub interval\t\t\t: %d\n", ++ (reg_data & 0x000000ff)); ++ ++ /* Size is indicated in bits 12:8 of register in ++ * terms of 32 byte blocks. */ ++ size = ((reg_data & 0x00001f00) >> 8)*32; ++ len += snprintf(buf + len, count - len, ++ "ecc scrub block_size\t\t\t: %d\n", size); ++ ++ /* Status is indicated in bit 13 of register. */ ++ if ((reg_data & 0x00002000) > 0) ++ scrub_status = CONTROL_ON_STR; ++ ++ len += snprintf(buf + len, count - len, ++ "ecc scrub status\t\t\t: %s\n", scrub_status); ++ return len; ++} ++ ++/** ++ * intel_cln_ecc_scrub_ctrl_show ++ * ++ * @param dev: pointer to device ++ * @param attr: attribute pointer ++ * @param buf: output buffer ++ * @return number of bytes successfully read ++ * ++ * Populates ecc_scrub state via /sys/device/platform/intel-cln-ecc/control ++ */ ++static ssize_t ++intel_cln_ecc_scrub_ctrl_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ unsigned int count = PAGE_SIZE; ++ u32 reg_data = 0; ++ char *on_or_off = CONTROL_SCRUB_OFF_STR; ++ ++ sb_read(SB_ID_THERMAL, THERMAL_CTRL_READ, ECC_SCRUB_CONFIG_REG, ++ ®_data, 1); ++ ++ /* Status is indicated in bit 13 of register. */ ++ if ((reg_data & 0x00002000) > 0) ++ /* interval > 0 assume scrubbing on */ ++ on_or_off = CONTROL_SCRUB_ON_STR; ++ ++ return snprintf(buf, count,"%s", on_or_off); ++} ++ ++/** ++ * intel_cln_ecc_scrub_ctrl_store ++ * ++ * @param dev: pointer to device ++ * @param attr: attribute pointer ++ * @param buf: input buffer ++ * @param size: size of input data ++ * @return number of bytes successfully written ++ * ++ * Function allows user-space to switch on/off scrubbing with a simple ++ * echo 1/0 > /sys/device/platform/intel-cln-ecc/control command ++ */ ++static ssize_t ++intel_cln_ecc_scrub_ctrl_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, ++ size_t count) ++{ ++ ssize_t ret = 0; ++ ++ if (count <= 1) ++ return -EINVAL; ++ ++ ret = -EINVAL; ++ ++ /* Check for command starting with "scrub" ++ * and ending with "on" or "off" */ ++ ++ if (!strcmp(buf, CONTROL_SCRUB_ON_STR)) { ++ sb_write(SB_ID_THERMAL, THERMAL_RESUME_SCRUB, ++ 0, 0, 1); ++ ret = 0; ++ } else if (!strcmp(buf, CONTROL_SCRUB_OFF_STR)) { ++ sb_write(SB_ID_THERMAL, THERMAL_PAUSE_SCRUB, 0, ++ 0, 1); ++ ret = 0; ++ } ++ ++ ++ if (ret == 0) ++ ret = (ssize_t)count; ++ ++ else if (ret == -EINVAL) ++ printk(CONTROL_USAGE); ++ ++ return ret; ++} ++ ++/** ++ * intel_cln_ecc_scrub_intrvl_show ++ * ++ * @param dev: pointer to device ++ * @param attr: attribute pointer ++ * @param buf: output buffer ++ * @return number of bytes successfully read ++ * ++ * Populates ecc_scrub state via /sys/device/platform/intel-cln-ecc/interval ++ */ ++static ssize_t ++intel_cln_ecc_scrub_intrvl_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ u32 reg_data = 0; ++ ++ /* Interval is the lsbyte of the config reg, ++ * so mask out just that byte in the data printed. */ ++ sb_read(SB_ID_THERMAL, THERMAL_CTRL_READ, ECC_SCRUB_CONFIG_REG, ++ ®_data, 1); ++ ++ return snprintf(buf, PAGE_SIZE, "%d\n", (reg_data & 0x000000ff)); ++} ++ ++/** ++ * intel_cln_ecc_scrub_intrvl_store ++ * ++ * @param dev: pointer to device ++ * @param attr: attribute pointer ++ * @param buf: input buffer ++ * @param size: size of input data ++ * @return number of bytes successfully written ++ * ++ * Function allows user-space to set scrub interval with a value of 1-255 ++ * echo 1-255 > /sys/device/platform/intel-cln-ecc/interval type command ++ */ ++static ssize_t ++intel_cln_ecc_scrub_intrvl_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, ++ size_t count) ++{ ++ ssize_t ret = 0; ++ unsigned long val = 0; ++ u32 reg_data = 0; ++ int ret_temp = 0; ++ ++ if (count <= 1) ++ return -EINVAL; ++ ++ ret = -EINVAL; ++ ++ ret_temp = kstrtoul(buf, 10, &val); ++ ++ if (ret_temp) ++ return ret_temp; ++ ++ if (val > MIN_SCRUB_REFRESH && val <= MAX_SCRUB_REFRESH) { ++ /* Need to read-modify-write config register. */ ++ sb_read(SB_ID_THERMAL, THERMAL_CTRL_READ, ++ ECC_SCRUB_CONFIG_REG, ++ ®_data, 1); ++ ++ reg_data &= 0xffffff00; /* clear lsb. */ ++ reg_data |= val; /* now set interval. */ ++ ++ sb_write(SB_ID_THERMAL, THERMAL_CTRL_WRITE, ++ ECC_SCRUB_CONFIG_REG, ++ reg_data, 1); ++ ret = 0; ++ } else { ++ printk(INTERVAL_USAGE); ++ } ++ ++ if (ret == 0) ++ ret = (ssize_t)count; ++ return ret; ++} ++ ++/** ++ * intel_cln_ecc_scrub_size_show ++ * ++ * @param dev: pointer to device ++ * @param attr: attribute pointer ++ * @param buf: output buffer ++ * @return number of bytes successfully read ++ * ++ * Populates ecc_scrub state via /sys/device/platform/intel-cln-ecc/block_size ++ */ ++static ssize_t ++intel_cln_ecc_scrub_size_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ int size = 0; ++ u32 reg_data = 0; ++ ++ /* Size is indicated in bits 12:8 of config register ++ * multiply x32 to get num bytes). */ ++ sb_read(SB_ID_THERMAL, THERMAL_CTRL_READ, ECC_SCRUB_CONFIG_REG, ++ ®_data, 1); ++ size = ((reg_data & 0x00001f00) >> 8)*32; ++ ++ return snprintf(buf, PAGE_SIZE, "%d\n", size); ++} ++ ++/** ++ * intel_cln_ecc_scrub_size_store ++ * ++ * @param dev: pointer to device ++ * @param attr: attribute pointer ++ * @param buf: input buffer ++ * @param size: size of input data ++ * @return number of bytes successfully written ++ * ++ * Function allows user-space to set scrub block size of 64-512 with a simple ++ * echo 64-512 > /sys/device/platform/intel-cln-ecc/block_size command ++ */ ++static ssize_t ++intel_cln_ecc_scrub_size_store(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ ssize_t ret = 0; ++ unsigned long val = 0; ++ u32 reg_data = 0; ++ int ret_temp = 0; ++ ++ if (count <= 1) ++ return -EINVAL; ++ ++ ret = -EINVAL; ++ ret_temp = kstrtoul(buf, 10, &val); ++ ++ if (ret_temp) ++ return ret_temp; ++ ++ if (val >= MIN_SCRUB_BLOCK_SIZE && val <= MAX_SCRUB_BLOCK_SIZE){ ++ ++ /* Need to read-modify-write config register. */ ++ sb_read(SB_ID_THERMAL, THERMAL_CTRL_READ, ++ ECC_SCRUB_CONFIG_REG, ++ ®_data, 1); ++ ++ reg_data &= 0xfffffe0ff; /* clear bits 12:8 */ ++ reg_data |= (val/32)<<8; /* now set size */ ++ ++ sb_write(SB_ID_THERMAL, THERMAL_CTRL_WRITE, ++ ECC_SCRUB_CONFIG_REG, reg_data, 1); ++ ret = 0; ++ } else { ++ printk(SIZE_USAGE); ++ } ++ ++ if (ret == 0) ++ ret = (ssize_t)count; ++ ++ return ret; ++} ++ ++static struct device_attribute dev_attr_status = { ++ .attr = { ++ .name = "status", ++ .mode = 0444, ++ }, ++ .show = intel_cln_ecc_scrub_stat_show, ++}; ++ ++static struct device_attribute dev_attr_control = { ++ .attr = { ++ .name = "control", ++ .mode = 0644, ++ }, ++ .show = intel_cln_ecc_scrub_ctrl_show, ++ .store = intel_cln_ecc_scrub_ctrl_store, ++}; ++ ++static struct device_attribute dev_attr_intrvl = { ++ .attr = { ++ .name = "interval", ++ .mode = 0644, ++ }, ++ .show = intel_cln_ecc_scrub_intrvl_show, ++ .store = intel_cln_ecc_scrub_intrvl_store, ++}; ++ ++static struct device_attribute dev_attr_block_size = { ++ .attr = { ++ .name = "block_size", ++ .mode = 0644, ++ }, ++ .show = intel_cln_ecc_scrub_size_show, ++ .store = intel_cln_ecc_scrub_size_store, ++}; ++ ++static struct attribute *platform_attributes[] = { ++ &dev_attr_status.attr, ++ &dev_attr_control.attr, ++ &dev_attr_intrvl.attr, ++ &dev_attr_block_size.attr, ++ NULL, ++}; ++ ++static struct attribute_group ecc_attrib_group = { ++ .attrs = platform_attributes ++}; ++ ++/***************************************************************************** ++ * Module/PowerManagement hooks ++ *****************************************************************************/ ++/** ++ * intel_cln_ecc_probe ++ * ++ * @param pdev: Platform device ++ * @return 0 success < 0 failure ++ * ++ * Callback from platform sub-system to probe ++ * ++ */ ++static int intel_cln_ecc_scrub_probe(struct platform_device *pdev) ++{ ++ int value_overridden = 0; ++ ++#ifdef CONFIG_INTEL_CLN_ECC_SCRUB_OVERRIDE_CONFIG ++ u32 scrubber_refresh = 0; ++ u32 scrubber_block_size = 0; ++ u32 config_settings = 0; ++#endif ++ ++ memset(&ecc_scrub_dev, 0x00, sizeof(ecc_scrub_dev)); ++ ++ /* Update config settings, if directed so to do */ ++ if (ecc_scrub_start_override != NOT_OVERRIDDEN) { ++ /* start of memory address */ ++ sb_write(SB_ID_THERMAL, THERMAL_CTRL_WRITE, ++ ECC_SCRUB_START_MEM_REG, ecc_scrub_start_override, 1); ++ ++ value_overridden = 1; ++ } ++ if (ecc_scrub_end_override != NOT_OVERRIDDEN) { ++ /* end of memory address */ ++ sb_write(SB_ID_THERMAL, THERMAL_CTRL_WRITE, ++ ECC_SCRUB_END_MEM_REG, ecc_scrub_end_override, 1); ++ ++ value_overridden = 1; ++ } ++ if (ecc_scrub_next_override != NOT_OVERRIDDEN) { ++ /* next address to be read */ ++ sb_write(SB_ID_THERMAL, THERMAL_CTRL_WRITE, ++ ECC_SCRUB_NEXT_READ_REG, ecc_scrub_next_override, 1); ++ ++ value_overridden = 1; ++ } ++ if (ecc_scrub_config_override != NOT_OVERRIDDEN) { ++ sb_write(SB_ID_THERMAL, THERMAL_CTRL_WRITE, ++ ECC_SCRUB_CONFIG_REG, ecc_scrub_config_override, 1); ++ ++ value_overridden = 1; ++ } ++ ++ /* Config Reg can be updated by either command line or kconfig setting ++ * in the case where we have both the command line takes precedence.*/ ++ ++ else { ++#ifdef CONFIG_INTEL_CLN_ECC_SCRUB_OVERRIDE_CONFIG ++ scrubber_refresh = CONFIG_INTEL_CLN_HW_ECC_REFRESH_RATE; ++ scrubber_block_size = CONFIG_INTEL_CLN_HW_ECC_REFRESH_SIZE; ++ ++ if (scrubber_block_size > MAX_SCRUB_BLOCK_SIZE) ++ scrubber_block_size = MAX_SCRUB_BLOCK_SIZE; ++ ++ else if (scrubber_block_size < MIN_SCRUB_BLOCK_SIZE) ++ scrubber_block_size = MIN_SCRUB_BLOCK_SIZE; ++ ++ if (scrubber_refresh > MAX_SCRUB_REFRESH) ++ scrubber_refresh = MAX_SCRUB_REFRESH; ++ ++ ++ /* adjust block size to multiples of 32 - ++ * as that is what the register setting actually expects. */ ++ config_settings = scrubber_block_size/32; ++ config_settings <<= 8; ++ config_settings += scrubber_refresh; ++ ++ /* config settings */ ++ sb_write(SB_ID_THERMAL, THERMAL_CTRL_WRITE, ++ ECC_SCRUB_CONFIG_REG, config_settings, 1); ++ ++ value_overridden = 1; ++#endif ++ } ++ ++ if (value_overridden) ++ sb_write(SB_ID_THERMAL, THERMAL_RESUME_SCRUB, 0, 0, 1); ++ ++ return sysfs_create_group(&pdev->dev.kobj, &ecc_attrib_group); ++} ++ ++/** ++ * intel_cln_ecc_scrub_suspend ++ * ++ * @param pdev: Platform device structure (unused) ++ * @return 0 success < 0 failure ++ * ++ */ ++static int intel_cln_ecc_scrub_suspend(struct device *pdev) ++{ ++#ifdef CONFIG_INTEL_CLN_ECC_SCRUB_S3_CONFIG ++ u32 reg_data = 0; ++ ++ /* Store off the 4 registers associated with scrubbing. */ ++ sb_read(SB_ID_THERMAL, THERMAL_CTRL_READ, ECC_SCRUB_START_MEM_REG, ++ ®_data, 1); ++ ecc_scrub_dev.start_address = reg_data; ++ ++ sb_read(SB_ID_THERMAL, THERMAL_CTRL_READ, ECC_SCRUB_END_MEM_REG, ++ ®_data, 1); ++ ecc_scrub_dev.end_address = reg_data; ++ ++ sb_read(SB_ID_THERMAL, THERMAL_CTRL_READ, ECC_SCRUB_NEXT_READ_REG, ++ ®_data, 1); ++ ecc_scrub_dev.next_address = reg_data; ++ ++ sb_read(SB_ID_THERMAL, THERMAL_CTRL_READ, ECC_SCRUB_CONFIG_REG, ++ ®_data, 1); ++ ecc_scrub_dev.config = reg_data; ++#endif ++ return 0; ++} ++ ++/** ++ * intel_cln_ecc_scrub_resume ++ * ++ * @param pdev: Platform device structure (unused) ++ * @return 0 success < 0 failure ++ */ ++static int intel_cln_ecc_scrub_resume(struct device *pdev) ++{ ++#ifdef CONFIG_INTEL_CLN_ECC_SCRUB_S3_CONFIG ++ ++ sb_write(SB_ID_THERMAL, THERMAL_CTRL_WRITE, ECC_SCRUB_START_MEM_REG, ++ ecc_scrub_dev.start_address, 1); ++ ++ sb_write(SB_ID_THERMAL, THERMAL_CTRL_WRITE, ECC_SCRUB_END_MEM_REG, ++ ecc_scrub_dev.end_address, 1); ++ ++ sb_write(SB_ID_THERMAL, THERMAL_CTRL_WRITE, ECC_SCRUB_NEXT_READ_REG, ++ ecc_scrub_dev.next_address, 1); ++ ++ sb_write(SB_ID_THERMAL, THERMAL_CTRL_WRITE, ECC_SCRUB_CONFIG_REG, ++ ecc_scrub_dev.config, 1); ++ ++ sb_write(SB_ID_THERMAL, THERMAL_RESUME_SCRUB, 0, 0, 1); ++ ++#endif ++ return 0; ++} ++ ++/** ++ * intel_cln_ecc_scrub_remove ++ * ++ * @return 0 success < 0 failure ++ * ++ * Removes a platform device ++ */ ++static int intel_cln_ecc_scrub_remove(struct platform_device *pdev) ++{ ++ return sysfs_create_group(&pdev->dev.kobj, &ecc_attrib_group); ++} ++ ++/* ++ * Power management operations ++ */ ++static const struct dev_pm_ops intel_cln_ecc_scrub_pm_ops = { ++ .suspend = intel_cln_ecc_scrub_suspend, ++ .resume = intel_cln_ecc_scrub_resume, ++}; ++ ++ ++/* ++ * Platform structures useful for interface to PM subsystem ++ */ ++static struct platform_driver intel_cln_ecc_scrub_driver = { ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ .pm = &intel_cln_ecc_scrub_pm_ops, ++ }, ++ .probe = intel_cln_ecc_scrub_probe, ++ .remove = intel_cln_ecc_scrub_remove, ++}; ++ ++ ++MODULE_AUTHOR("Derek Harnett <derek.harnett@intel.com>"); ++MODULE_DESCRIPTION("Intel Clanton DRAM ECC-scrub driver"); ++MODULE_LICENSE("Dual BSD/GPL"); ++ ++module_param(ecc_scrub_config_override, uint, 0644); ++MODULE_PARM_DESC(ecc_scrub_config_override, OVERRIDE_CONFIG_PARM_DESC); ++ ++module_param(ecc_scrub_start_override, uint, 0644); ++MODULE_PARM_DESC(ecc_scrub_start_override, OVERRIDE_START_PARM_DESC); ++ ++module_param(ecc_scrub_end_override, uint, 0644); ++MODULE_PARM_DESC(ecc_scrub_end_override, OVERRIDE_END_PARM_DESC); ++ ++module_param(ecc_scrub_next_override, uint, 0644); ++MODULE_PARM_DESC(ecc_scrub_next_override, OVERRIDE_NEXT_PARM_DESC); ++ ++module_platform_driver(intel_cln_ecc_scrub_driver); ++ +diff --git a/drivers/platform/x86/quark/intel_cln_esram.c b/drivers/platform/x86/quark/intel_cln_esram.c +new file mode 100644 +index 0000000..76d2024 +--- /dev/null ++++ b/drivers/platform/x86/quark/intel_cln_esram.c +@@ -0,0 +1,1144 @@ ++/* ++ * Copyright(c) 2013 Intel Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ */ ++/* ++ * Intel Clanton eSRAM overlay driver ++ * ++ * eSRAM is an on-chip fast access SRAM. ++ * ++ * This driver provides the ability to map a kallsyms derived symbol of ++ * arbitrary length or a struct page entitiy. ++ * A sysfs interface is provided to allow map of kernel structures, without ++ * having to use the API from your code directly. ++ * ++ * Example: ++ * echo idt_table > /sys/devices/intel-cln-esram.0/map ++ * ++ * An API is provided to allow for mapping of a) kernel symbols or b) pages. ++ * eSRAM requires 4k physically aligned addresses to work - so a struct page ++ * fits neatly into this. ++ * ++ * intel_cln_esram_map_sym(ohci_irq); ++ * intel_cln_esram_map_page(virt_to_page(ohci_irq), "ohci_irq"); ++ * Are equivalent - with the exception that map_sym() can detect if a mapping ++ * crosses a page-boundary, whereas map_page just maps one page. Generally use ++ * map_sym() for code and map_page() for data ++ * ++ * To populte eSRAM we must copy data to a temporary buffer, overlay and ++ * then copy data back to the eSRAM region. ++ * ++ * When entering S3 - we must save eSRAM state to DRAM, and similarly on restore ++ * to S0 we must repopulate eSRAM ++ * Unmap code is included for reference however the cache coherency of unmap is ++ * not guaranteed so the functionality is not exported by this code ++ * ++ */ ++#include <asm/cacheflush.h> ++#include <asm/desc.h> ++#include <asm/io.h> ++#include <asm/pgtable.h> ++#include <asm/special_insns.h> ++#include <asm-generic/uaccess.h> ++#include <linux/delay.h> ++#include <linux/err.h> ++#include <linux/fs.h> ++#include <linux/intel_cln_sb.h> ++#include <linux/kallsyms.h> ++#include <linux/list.h> ++#include <linux/mm.h> ++#include <linux/module.h> ++#include <linux/printk.h> ++#include <linux/platform_device.h> ++#include <linux/pm.h> ++#include <linux/seq_file.h> ++#include <linux/slab.h> ++#include <linux/spinlock.h> ++#include <linux/timer.h> ++ ++#include "intel_cln_esram.h" ++ ++#define DRIVER_NAME "intel-cln-esram" ++ ++/* Shorten fn names to fit 80 char limit */ ++#ifndef sb_read ++#define sb_read intel_cln_sb_read_reg ++#endif ++#ifndef sb_write ++#define sb_write intel_cln_sb_write_reg ++#endif ++ ++/* Define size of pages, ECC scrub demark etc */ ++#define MAX_PAGE_RETRIES (100) ++#define MS_PER_HOUR (3600000UL) ++#define ESRAM_PAGE_COUNT INTEL_CLN_ESRAM_PAGE_COUNT ++#define ESRAM_PAGE_MASK (0xFFFFF000) ++#define ESRAM_PAGE_SIZE INTEL_CLN_ESRAM_PAGE_SIZE ++#define ESRAM_TOTAL_SIZE (ESRAM_PAGE_COUNT * ESRAM_PAGE_SIZE) ++#define ECC_MAX_REFRESH_PERIOD (48) ++#define ECC_DEFAULT_REFRESH_PERIOD (24) ++#define ECC_DRAM_READSIZE (512) /* bytes per DRAM ECC */ ++#define ECC_ESRAM_READSIZE ESRAM_PAGE_SIZE /* bytes per SRAM ECC */ ++ ++/* Register ID */ ++#define ESRAM_PGPOOL_REG (0x80) /* PGPOOL */ ++#define ESRAM_CTRL_REG (0x81) /* ESRAMCTRL */ ++#define ESRAM_PGBLOCK_REG (0x82) /* Global page ctrl */ ++#define ESCRM_ECCCERR_REG (0x83) /* Correctable ECC */ ++#define ESRAM_ECCUCERR_REG (0x84) /* Uncorrectable ECC */ ++ ++/* Reg commands */ ++#define ESRAM_CTRL_READ (0x10) /* Config reg */ ++#define ESRAM_CTRL_WRITE (0x11) /* Config reg */ ++#define ESRAM_PAGE_READ (0x12) /* Page config read */ ++#define ESRAM_PAGE_WRITE (0x13) /* Page config write */ ++ ++/* ESRAMPGPOOL reg 0x80 - r/w opcodes 0x10/0x11 */ ++#define ESRAM_PGPOOL_FLUSHING(x) ((x>>18)&0x1FF) ++#define ESRAM_PGPOOL_PGBUSY(x) ((x>>9)&0x1FF) ++ ++/* ESRAMCTRL reg 0x81 - r/w opcodes 0x10/0x11 */ ++#define ESRAM_CTRL_FLUSHPRI(x) ((x>>25)&0x03) /* DRAM flush priority */ ++#define ESRAM_CTRL_SIZE(x) ((x>>16)&0xFF) /* # of 4k pages */ ++#define ESRAM_CTRL_ECCTHRESH(x) ((x>>8)&0xFF) /* ECC threshold */ ++#define ESRAM_CTRL_THRESHMSG_EN (0x00000080) /* ECC notification */ ++#define ESRAM_CTRL_ISAVAIL (0x00000010) /* ESRAM on die ? */ ++#define ESRAM_CTRL_BLOCK_MODE (0x00000008) /* Block mode enable */ ++#define ESRAM_CTRL_GLOBAL_LOCK (0x00000004) /* Global lock status */ ++#define ESRAM_CTRL_FLUSHDISABLE (0x00000002) /* Global flush/dis */ ++#define ESRAM_CTRL_SECDEC (0x00000001) /* ECC enable bit */ ++ ++/* PGBLOCK reg 0x82 - opcode 0x10/0x11 */ ++#define ESRAM_PGBLOCK_FLUSHEN (0x80000000) /* Block flush enable */ ++#define ESRAM_PGBLOCK_PGFLUSH (0x40000000) /* Flush the block */ ++#define ESRAM_PGBLOCK_DISABLE (0x20000000) /* Block mode disable */ ++#define ESRAM_PGBLOCK_ENABLE (0x10000000) /* Block mode enable */ ++#define ESRAM_PGBLOCK_LOCK (0x08000000) /* Block mode lock en */ ++#define ESRAM_PGBLOCK_INIT (0x04000000) /* Block init in prog */ ++#define ESRAM_PGBLOCK_BUSY (0x01000000) /* Block is enabled */ ++#define ESRAM_PGBLOCK_SYSADDR(x) (x&0x000000FF) ++ ++/* ESRAMPGCTRL - opcode 0x12/0x13 */ ++#define ESRAM_PAGE_FLUSH_PAGE_EN (0x80000000) /* S3 autoflush */ ++#define ESRAM_PAGE_FLUSH (0x40000000) /* Flush page to DRAM */ ++#define ESRAM_PAGE_DISABLE (0x20000000) /* page disable bit */ ++#define ESRAM_PAGE_EN (0x10000000) /* Page enable */ ++#define ESRAM_PAGE_LOCK (0x08000000) /* Page lock en */ ++#define ESRAM_PAGE_INITIALISING (0x04000000) /* Init in progress */ ++#define ESRAM_PAGE_BUSY (0x01000000) /* Page busy */ ++#define ESRAM_PAGE_MAP_SHIFT (12) /* Shift away 12 LSBs */ ++ ++/* Extra */ ++#define ESRAM_MAP_OP (0x01) ++#define ESRAM_UNMAP_OP (0x00) ++ ++/** ++ * struct esram_refname ++ * ++ * Structure to hold a linked list of names ++ */ ++struct esram_refname { ++ char name[KSYM_SYMBOL_LEN]; /* Name of mapping */ ++ struct list_head list; ++}; ++ ++/** ++ * struct esram_page ++ * ++ * Represents an eSRAM page in our linked list ++ */ ++struct esram_page { ++ ++ struct list_head list; /* List entry descriptor */ ++ struct list_head name_list; /* Linked list for name references */ ++ u32 id; /* Page ID */ ++ u32 phys_addr; /* Physial address of page */ ++ u32 refcount; /* Reference count */ ++ u32 vaddr; /* Virtual address of page */ ++ ++}; ++ ++/** ++ * struct intel_cln_esram_dev ++ * ++ * Structre to represent module state/data/etc ++ */ ++struct intel_cln_esram_dev{ ++ ++ /* Linux kernel structures */ ++ struct list_head page_used; /* Used pages */ ++ struct list_head page_free; /* Free pages */ ++ spinlock_t slock; /* Spinlock */ ++ struct platform_device *pldev; /* Platform device */ ++ ++ /* Actual data */ ++ struct esram_page * pages; ++ u8 cbuf[ESRAM_PAGE_SIZE]; ++ ++ /* Stats */ ++ u32 page_count; /* As reported by silicon */ ++ u32 page_disable_retries; /* Aggreate count on disable */ ++ u32 page_enable_retries; /* Aggregate spin count page enable */ ++ u32 page_free_ct; /* Free pages for mapping code section */ ++}; ++ ++static struct intel_cln_esram_dev esram_dev; ++ ++/* ++ * Kallsyms does not provide data addresses. To map important structures such as ++ * the idt and gdt, we need to frig the lookup with the below. Other entities ++ * can similarly be added. Note we map a page from the given address - anything ++ * larger will require additional code to handle ++ */ ++struct esram_symex { ++ char * name; ++ void * vaddr; ++ u32 size; ++}; ++ ++static struct esram_symex esram_symex[] = ++{ ++ { ++ .name = "idt_table", ++ .vaddr = &idt_table, ++ .size = ESRAM_PAGE_SIZE, ++ }, ++ { ++ .name = "gdt_page", ++ .vaddr = &gdt_page, ++ .size = ESRAM_PAGE_SIZE, ++ }, ++}; ++ ++/** ++ * intel_cln_esram_stat_show ++ * ++ * @param dev: pointer to device ++ * @param attr: attribute pointer ++ * @param buf: output buffer ++ * @return number of bytes successfully read ++ * ++ * Populates eSRAM state via /sys/device/intel-cln-esram.0/stat ++ */ ++static ssize_t intel_cln_esram_stat_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++ ++{ ++ struct esram_page * epage = NULL; ++ int len = 0; ++ unsigned int count = PAGE_SIZE, size; ++ u32 pgpool = 0, ctrl = 0, pgblock = 0; ++ char * enabled = "enabled"; ++ char * disabled = "disabled"; ++ ++ /* Display page-pool relevant data */ ++ sb_read(SB_ID_ESRAM, ESRAM_CTRL_READ, ESRAM_PGPOOL_REG, &pgpool, 1); ++ size = snprintf(buf, count, ++ "esram-pgpool\t\t\t: 0x%08x\n" ++ "esram-pgpool.free\t\t: %u\n" ++ "esram-pgpool.flushing\t\t: %u\n", ++ pgpool, ESRAM_PGPOOL_PGBUSY(pgpool)+1, ++ ESRAM_PGPOOL_FLUSHING(pgpool) + 1); ++ len += size; ++ count -= size; ++ ++ /* Display ctrl reg - most of this is of interest */ ++ sb_read(SB_ID_ESRAM, ESRAM_CTRL_READ, ESRAM_CTRL_REG, &ctrl, 1); ++ size = snprintf(buf + len, count - len, ++ "esram-ctrl\t\t\t: 0x%08x\n" ++ "esram-ctrl.ecc\t\t\t: %s\n" ++ "esram-ctrl.ecc-theshold\t\t: %u\n" ++ "esram-ctrl.pages\t\t: %u\n" ++ "esram-ctrl.dram-flush-priorityi\t: %u\n", ++ ctrl, (ctrl & ESRAM_CTRL_SECDEC) ? enabled : disabled, ++ ESRAM_CTRL_ECCTHRESH(ctrl), ESRAM_CTRL_SIZE(ctrl)+1, ++ ESRAM_CTRL_FLUSHPRI(ctrl)); ++ len += size; ++ count -= size; ++ ++ /* Display block ctrl/stat - we should be !block mode */ ++ sb_read(SB_ID_ESRAM, ESRAM_CTRL_READ, ESRAM_PGBLOCK_REG, &pgblock, 1); ++ size = snprintf(buf + len, count - len, "esram-block\t\t\t: 0x%08x\n", ++ pgblock); ++ len += size; ++ count -= size; ++ ++ /* Print ECC status regs */ ++ ++ /* Print per-page info */ ++ size = snprintf(buf + len, count - len, ++ "free page\t\t\t: %u\nused page\t\t\t: %u\n" ++ "refresh \t\t\t: %ums\npage enable retries\t\t: %u\n" ++ "page disable retries\t: %u\n", ++ esram_dev.page_free_ct, ++ esram_dev.page_count-esram_dev.page_free_ct, ++ 0, ++ esram_dev.page_enable_retries, ++ esram_dev.page_disable_retries); ++ len += size; ++ count -= size; ++ ++ spin_lock(&esram_dev.slock); ++ if(!list_empty(&esram_dev.page_free)){ ++ ++ epage = list_first_entry(&esram_dev.page_free, struct esram_page, list); ++ size = snprintf(buf + len, count - len, ++ "ecc next page \t\t\t: %u\n",epage->id); ++ len += size; ++ count -= size; ++ ++ ++ } ++ spin_unlock(&esram_dev.slock); ++ ++ /* Return len indicate eof */ ++ return len; ++} ++ ++/** ++ * intel_cln_esram_map_show ++ * ++ * @param dev: pointer to device ++ * @param attr: attribute pointer ++ * @param buf: output buffer ++ * @return number of bytes successfully read ++ * ++ * Read back eSRAM mapped entries ++ */ ++static ssize_t ++intel_cln_esram_map_show(struct device *dev,struct device_attribute *attr, ++ char *buf) ++{ ++ struct esram_page * epage = NULL; ++ struct esram_refname * refname = NULL; ++ int len = 0, size = 0; ++ unsigned int count = PAGE_SIZE; ++ ++ spin_lock(&esram_dev.slock); ++ list_for_each_entry(epage, &esram_dev.page_used, list){ ++ /* Print references */ ++ list_for_each_entry(refname, &epage->name_list, list){ ++ size = snprintf(buf + len, count - len, ++ "%s ", refname->name); ++ len += size; ++ count -= size; ++ } ++ /* Print data */ ++ size += snprintf(buf + len, count - len, ++ "\n\tPage virt 0x%08x phys 0x%08x\n" ++ "\tRefcount %u\n", ++ epage->vaddr, epage->phys_addr, ++ epage->refcount); ++ len += size; ++ count -= size; ++ } ++ spin_unlock(&esram_dev.slock); ++ ++ /* Return len indicate eof */ ++ return len; ++} ++ ++/** ++ * intel_cln_esram_map_store ++ * ++ * @param dev: pointer to device ++ * @param attr: attribute pointer ++ * @param buf: input buffer ++ * @param size: size of input data ++ * @return number of bytes successfully written ++ * ++ * Function allows user-space to switch mappings on/off with a simple ++ * echo idt_table > /sys/devices/intel-cln-esram.0/map type command ++ */ ++static ssize_t ++intel_cln_esram_map_store(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ ssize_t ret = 0; ++ char * sbuf = NULL; ++ unsigned long vaddr = 0, i = 0; ++ unsigned int count = PAGE_SIZE; ++ ++ if(count <= 1){ ++ return -EINVAL; ++ } ++ ++ /* Get input */ ++ sbuf = (char*)buf; ++ ++ /* Fixup entity to scrub spaces */ ++ while(sbuf < (buf + count)){ ++ if(*sbuf == ' ' || *sbuf == '\r' || *sbuf =='\n'){ ++ *sbuf = 0; ++ break; ++ } ++ sbuf++; ++ } ++ ++ /* Check to see if we are being asked to map a non-kallsyms addr */ ++ for(i = 0; i < sizeof(esram_symex)/sizeof(struct esram_symex); i++){ ++ if(strcmp(buf, esram_symex[i].name) == 0){ ++ ret = intel_cln_esram_map_range( ++ esram_symex[i].vaddr, ++ esram_symex[i].size, ++ esram_symex[i].name); ++ goto done; ++ } ++ } ++ ++ /* This path relies on kallsyms to provide name/address data */ ++ vaddr = kallsyms_lookup_name(buf); ++ if(vaddr == 0) ++ goto done; ++ ++ ret = intel_cln_esram_map_symbol((void*)vaddr); ++done: ++ if(ret == 0) ++ ret = (ssize_t)count; ++ return ret; ++} ++ ++static struct device_attribute dev_attr_stats = { ++ .attr = { ++ .name = "stats", ++ .mode = 0444, ++ }, ++ .show = intel_cln_esram_stat_show, ++}; ++ ++static struct device_attribute dev_attr_map = { ++ .attr = { ++ .name = "map", ++ .mode = 0644, ++ }, ++ .show = intel_cln_esram_map_show, ++ .store = intel_cln_esram_map_store, ++}; ++ ++static struct attribute *platform_attributes[] = { ++ &dev_attr_stats.attr, ++ &dev_attr_map.attr, ++ NULL, ++}; ++ ++static struct attribute_group esram_attrib_group = { ++ .attrs = platform_attributes ++}; ++ ++/****************************************************************************** ++ * eSRAM Core ++ ******************************************************************************/ ++ ++/** ++ * intel_cln_esram_page_busy ++ * ++ * @param epage: Pointer to the page descriptor ++ * @return boolean indicating whether or not a page is enabled ++ */ ++static int intel_cln_esram_page_busy(struct esram_page * epage, u8 lock) ++{ ++ u32 reg = 0; ++ ++ sb_read(SB_ID_ESRAM, ESRAM_PAGE_READ, epage->id, ®, lock); ++ return (reg&(ESRAM_PAGE_BUSY | ESRAM_PAGE_FLUSH | ESRAM_PAGE_DISABLE)); ++} ++ ++/** ++ * intel_cln_esram_fault ++ * ++ * Dump eSRAM registers and kernel panic ++ * Nothing else to do at this point ++ */ ++void intel_cln_esram_fault(struct esram_page * epage, u32 lineno) ++{ ++ u32 reg = 0, next = 0, prev = 0, prev_reg = 0; ++ u32 next_reg = 0, block = 0, ctrl = 0; ++ ++ pr_err("eSRAM: fault @ %s:%d\n", __FILE__, lineno); ++ sb_read(SB_ID_ESRAM, ESRAM_PAGE_READ, epage->id, ®, 1); ++ pr_err("read page %d state 0x%08x\n", epage->id, reg); ++ if(epage->id == 0){ ++ next = 1; prev = 127; ++ }else if(epage->id == 127){ ++ next = 0; prev = 126; ++ }else{ ++ next = epage->id+1; ++ prev = epage->id-1; ++ } ++ sb_read(SB_ID_ESRAM, ESRAM_PAGE_READ, next, &next_reg, 1); ++ sb_read(SB_ID_ESRAM, ESRAM_PAGE_READ, prev, &prev_reg, 1); ++ ++ /* Get state */ ++ sb_read(SB_ID_ESRAM, ESRAM_CTRL_READ, ESRAM_CTRL_REG, &ctrl, 1); ++ sb_read(SB_ID_ESRAM, ESRAM_CTRL_READ, ESRAM_PGBLOCK_REG, &block, 1); ++ ++ pr_err("eSRAM: CTRL 0x%08x block 0x%08x\n", ctrl, block); ++ pr_err("Prev page %d state 0x%08x Next page %d state 0x%08x\n" ++ , next, next_reg, prev, prev_reg); ++ BUG(); ++} ++ ++ ++/** ++ * intel_cln_esram_page_enable ++ * ++ * @param epage: struct esram_page carries data to program to register ++ * @param lock: Indicates whether to attain sb spinlock or not ++ * ++ * Enable an eSRAM page spinning for page to become ready. ++ */ ++static void intel_cln_esram_page_enable(struct esram_page *epage, u8 lock) ++{ ++ u32 ret = 0; ++ ++ /* Fault if we try to enable a disabled page */ ++ if(intel_cln_esram_page_busy(epage, lock)){ ++ intel_cln_esram_fault(epage, __LINE__); ++ } ++ ++ /* Program page mapping */ ++ sb_write(SB_ID_ESRAM, ESRAM_PAGE_WRITE, epage->id, ++ ESRAM_PAGE_FLUSH_PAGE_EN | ESRAM_PAGE_EN | ++ (epage->phys_addr>>ESRAM_PAGE_MAP_SHIFT), lock); ++ do { ++ /* Poll until page busy bit becomes true */ ++ ret = intel_cln_esram_page_busy(epage, lock); ++ ++ /* This branch should rarely if ever be true */ ++ if(unlikely(ret == 0)){ ++ esram_dev.page_enable_retries++; ++ } ++ ++ }while(ret == 0); ++} ++ ++/** ++ * intel_cln_esram_page_disable_sync ++ * ++ * @param epage: pointer to eSRAM page descriptor ++ * ++ * This function spins waiting for disable bit to clear, useful right after a ++ * disable/disable-flush command. Interrupts are enabled here, sleeping is OK ++ */ ++static void intel_cln_esram_page_disable_sync(struct esram_page * epage) ++{ ++ u32 ret = 0, retries = 0; ++ do { ++ /* Poll for busy bit clear */ ++ ret = intel_cln_esram_page_busy(epage, 1); ++ ++ /* This branch should rarely if ever be true */ ++ if(unlikely(ret)){ ++ esram_dev.page_disable_retries++; ++ retries++; ++ } ++ ++ if(retries == MAX_PAGE_RETRIES){ ++ intel_cln_esram_fault(epage, __LINE__); ++ } ++ }while(ret); ++} ++ ++/** ++ * intel_cln_esram_page_disable ++ * ++ * @param epage: struct esram_page carries data to program to register ++ * ++ * Disable the eSRAM page no flush. Interrupts are enabled here, sleeping is OK ++ */ ++static void intel_cln_esram_page_disable(struct esram_page *epage) ++{ ++ sb_write(SB_ID_ESRAM, ESRAM_PAGE_WRITE, epage->id, ++ ESRAM_PAGE_DISABLE, 1); ++ intel_cln_esram_page_disable_sync(epage); ++} ++ ++/** ++ * intel_cln_esram_page_flush_disable ++ * ++ * @param epage: struct esram_page carries data to program to register ++ * ++ * Disable the eSRAM page - with flush. Note the architecture will block access ++ * to the overlayed region until the flush has completed => irqs may be switched ++ * on during this operation. ++ */ ++static void intel_cln_esram_page_flush_disable(struct esram_page *epage) ++{ ++ ++ ++ /* Do flush */ ++ sb_write(SB_ID_ESRAM, ESRAM_PAGE_WRITE, epage->id, ++ ESRAM_PAGE_FLUSH | ESRAM_PAGE_DISABLE, 1); ++ ++ intel_cln_esram_page_disable_sync(epage); ++} ++ ++#if 0 ++/** ++ * intel_cln_esram_flush_disable_all ++ * ++ * Flushes and disables all enabled eSRAM pages ++ */ ++static void intel_cln_esram_page_flush_disable_all(void) ++{ ++ struct esram_page * epage = NULL; ++ ++ spin_lock(&esram_dev.slock); ++ list_for_each_entry(epage, &esram_dev.page_used, list){ ++ intel_cln_esram_page_flush_disable(epage); ++ } ++ spin_unlock(&esram_dev.slock); ++} ++#endif ++ ++/** ++ * intel_cln_esram_page_populate_atomic ++ * ++ * @param epage: Pointer to eSRAM page desciptor. ++ * @return 0 placeholder, later versions may return error ++ * ++ * Function takes the mappings given in epage and uses the values to populate ++ * an eSRAM page. The copy/enable/copy routine must be done atomically, since we ++ * may be doing a memcpy() of an ISR for example. ++ * For this reason we wrapper this entire call into a callback provided by ++ * side-band, which does a spin_lock_irqsave calls this function and then does ++ * a spin_lock_irqrestore - thus guaranteeing atomicity of the below code and ++ * respect for the locking strategy of the side-band driver ++ */ ++static int intel_cln_esram_page_populate_atomic(struct esram_page * epage) ++{ ++ unsigned long crz; ++ ++ /* Copy away */ ++ memcpy(&esram_dev.cbuf, (void*)epage->vaddr, ESRAM_PAGE_SIZE); ++ ++ /* If CR0.WP is true - flip it HSD # 4930660 */ ++ crz = read_cr0(); ++ if (crz & X86_CR0_WP){ ++ write_cr0(crz & (~X86_CR0_WP)); ++ } ++ ++ /* Disable NMI */ ++ outb(0x80, 0x70); ++ ++ /* Enable page mapping */ ++ intel_cln_esram_page_enable(epage, 0); ++ ++ /* Copy back - populating memory overlay */ ++ memcpy((void*)epage->vaddr, &esram_dev.cbuf, ESRAM_PAGE_SIZE); ++ ++ /* Re-enable NMI */ ++ outb(0x00, 0x70); ++ ++ /* Restore CR0.WP if appropriate HSD # 4930660 */ ++ if (crz & X86_CR0_WP){ ++ write_cr0(crz); ++ } ++ return 0; ++} ++ ++/** ++ * intel_cln_esram_page_populate ++ * ++ * @param epage: Pointer to eSRAM page desciptor. ++ * @return 0 on success < 0 on failure ++ * ++ * Populates the page. set_memory_rw/set_memory_ro require local irqs enabled. ++ * intel_cln_esram_page_populate_atomic - needs irqs switched off since memory ++ * can be inconsistent during the populate operation. Depopulate operations are ++ * architecturally guaranteed ++ */ ++static int intel_cln_esram_page_populate(struct esram_page * epage) ++{ ++ int flip_rw = 0, level = 0, ret = 0; ++ pte_t * pte = epage != NULL ? lookup_address(epage->vaddr, &level):NULL; ++ ++ if(unlikely(pte == NULL)){ ++ return -EINVAL; ++ } ++ ++ /* Determine if we need to set writable */ ++ flip_rw = !(pte_write(*pte)); ++ ++ /* Ensure memory is r/w - do so before spin_lock_irqsave */ ++ if(flip_rw){ ++ ret = set_memory_rw(epage->vaddr, 1); ++ if (ret != 0){ ++ pr_err("%s error during set_memory_rw = %d\n", ++ __func__, ret); ++ return ret; ++ } ++ } ++ ++ /* Force ECC update @ disable only */ ++ intel_cln_esram_page_enable(epage, 1); ++ intel_cln_esram_page_disable(epage); ++ ++ /* Enable and populate eSRAM page using callback in sb with irqs off */ ++ ret |= intel_cln_sb_runfn_lock( ++ (int (*)(void*))intel_cln_esram_page_populate_atomic,(void*)epage); ++ ++ /* If we set memory writable - restore previous state */ ++ if(flip_rw){ ++ ret |= set_memory_ro(epage->vaddr, 1); ++ if (ret != 0){ ++ pr_err("%s error during set_memory_ro = %d\n", ++ __func__, ret); ++ return ret; ++ } ++ } ++ ++ return ret; ++} ++/** ++ * intel_cln_esram_page_addref ++ * ++ * @param epage: eSRAM page descriptor ++ * @param name: Name of reference to add ++ * @return zero on success negative on error ++ * ++ */ ++static int intel_cln_esram_page_addref(struct esram_page * epage, char * name) ++{ ++ struct esram_refname * refname = NULL; ++ if(unlikely(epage == NULL || name == NULL)){ ++ return -EINVAL; ++ } ++ ++ refname = kzalloc(sizeof(struct esram_refname), GFP_KERNEL); ++ if(unlikely(refname == NULL)){ ++ return -ENOMEM; ++ } ++ ++ /* Add to list */ ++ strncpy(refname->name, name, sizeof(refname->name)); ++ list_add(&refname->list, &epage->name_list); ++ ++ /* Bump reference count */ ++ epage->refcount++; ++ return 0; ++} ++ ++ ++/** ++ * __intel_cln_esram_map_page ++ * ++ * @param page: Page to map ++ * @param name: Name of the mapping ++ * @return 0 success < 0 failure ++ * ++ * Overlay a vritual address rangne eeds to be aligned to a 4k address. ++ * Since multiple items can live in a 4k range, it is possible when calling ++ * into map_page() that a previous mapping will have already covered some or all ++ * of the mapping we want. This is not an error case, if the map function finds ++ * it is being asked to map a 4k range already mapped it returns 0, to indicate ++ * the mapping has suceeded i.e. it's already been mapped. This is logical if ++ * you think about it. In contrast being asked to unmap a region not mapped is ++ * clearly an error... ++ * ++ */ ++static int __intel_cln_esram_map_page(u32 vaddr, char * name) ++{ ++ int ret = 0; ++ struct esram_page * epage = NULL; ++ struct esram_refname * refname = NULL; ++ ++ if(unlikely(name == NULL)){ ++ return -EINVAL; ++ } ++ ++ if(unlikely(esram_dev.page_free_ct == 0)){ ++ return -ENOMEM; ++ } ++ ++ /* Verify if we have already mapped */ ++ list_for_each_entry(epage, &esram_dev.page_used, list){ ++ if(epage->vaddr == vaddr){ ++ ++ /* Page already mapped */ ++ list_for_each_entry(refname, &epage->name_list, list){ ++ if(strcmp(refname->name, name)==0){ ++ /* Page mapped at this name */ ++ return -EINVAL; ++ } ++ } ++ /* New symbol in previous mapping */ ++ return intel_cln_esram_page_addref(epage, name); ++ } ++ } ++ ++ /* Enumerate eSRAM page structure */ ++ epage = list_first_entry(&esram_dev.page_free, struct esram_page, list); ++ epage->phys_addr = virt_to_phys((void*)vaddr); ++ epage->vaddr = vaddr; ++ ret = intel_cln_esram_page_addref(epage, name); ++ if(unlikely(ret < 0)){ ++ return ret; ++ } ++ ++ /* Populate page */ ++ ret = intel_cln_esram_page_populate(epage); ++ ++ /* Move to used list */ ++ list_move(&epage->list, &esram_dev.page_used); ++ esram_dev.page_free_ct--; ++ ++ return ret; ++} ++ ++/** ++ * __intel_cln_esram_unmap_page ++ * ++ * @param page: Page to unmap ++ * @param name: Name of the mapping ++ * @return 0 success < 0 failure ++ * ++ * Unmap a previously mapped virutal address range. ++ * Must be 4k aligned ++ * ++ */ ++static int __intel_cln_esram_unmap_page(u32 vaddr, char * name) ++{ ++ u8 found = 0; ++ struct esram_page * epage = NULL; ++ struct esram_refname * refname = NULL; ++ ++ /* Find physical address */ ++ list_for_each_entry(epage, &esram_dev.page_used, list){ ++ if(epage->vaddr == vaddr){ ++ found = 1; ++ break; ++ } ++ } ++ ++ /* Bail out on error */ ++ if(found == 0){ ++ pr_err("0x%08x not mapped\n", vaddr); ++ return -EINVAL; ++ } ++ ++ /* Determine reference to delete */ ++ found = 0; ++ list_for_each_entry(refname, &epage->name_list, list){ ++ if(strcmp(refname->name,name)==0){ ++ found = 1; ++ break; ++ } ++ } ++ if(unlikely(found == 0)){ ++ pr_err("No mapping %s!\n", name); ++ return -EINVAL; ++ } ++ ++ /* Remove entry decrement reference count */ ++ list_del(&refname->list); ++ kfree(refname); ++ if(--epage->refcount > 0){ ++ return 0; ++ } ++ ++ /* Flush and disable page */ ++ intel_cln_esram_page_flush_disable(epage); ++ ++ /* Move to free list tail - scrub entries come from head */ ++ list_move_tail(&epage->list, &esram_dev.page_free); ++ esram_dev.page_free_ct++; ++ ++ return 0; ++} ++ ++/** ++ * ++ * __intel_cln_esram_page_op ++ * ++ * @param vaddr: Virtual address of symbol ++ * @param size: Size/length of symbol ++ * @param name: Name of mapping ++ * @param map: Boolean indicates whether to map or unmap the page ++ * @return 0 success < 0 failure ++ * ++ * This function maps/unmaps a pages/pages given at the given vaddr. If ++ * the extent of the symbol @ vaddr crosses a page boundary, then we map ++ * multiple pages. Other stuff inside the page, gets a performance boost 'for ++ * free'. Any other data in the page that crosses the physical page boundary ++ * will be partially mapped. ++ */ ++static int __intel_cln_esram_page_op(u32 vaddr, u32 size, char *name, u8 map) ++{ ++ unsigned long offset = 0, page_offset = 0; ++ u32 pages = size/ESRAM_PAGE_SIZE + ((size%ESRAM_PAGE_SIZE) ? 1 : 0); ++ int ret = 0; ++ ++ /* Compare required pages to available pages */ ++ if(map == ESRAM_MAP_OP){ ++ if(pages > esram_dev.page_free_ct) ++ return -ENOMEM; ++ }else{ ++ if(pages > esram_dev.page_count - esram_dev.page_free_ct) ++ return -ENOMEM; ++ } ++ ++ /* Align to 4k and iterate the mappings */ ++ vaddr = vaddr&ESRAM_PAGE_MASK; ++ while(size > 0){ ++ ++ /* Map the page */ ++ spin_lock(&esram_dev.slock); ++ if(map == ESRAM_MAP_OP){ ++ ret = __intel_cln_esram_map_page(vaddr, name); ++ ++ }else{ ++ ret = __intel_cln_esram_unmap_page(vaddr, name); ++ } ++ spin_unlock(&esram_dev.slock); ++ if(unlikely(ret != 0)){ ++ break; ++ } ++ ++ /* Calc appropriate offsets */ ++ page_offset = offset_in_page(vaddr); ++ if(page_offset + size > ESRAM_PAGE_SIZE){ ++ ++ offset = ESRAM_PAGE_SIZE - page_offset; ++ size -= offset; ++ vaddr += ESRAM_PAGE_SIZE; ++ ++ }else{ ++ size = 0; ++ } ++ } ++ ++ return ret; ++} ++ ++/****************************************************************************** ++ * eSRAM API ++ ******************************************************************************/ ++ ++/** ++ * intel_cln_esram_map_range ++ * ++ * @param vaddr: Virtual address to start mapping (must be 4k aligned) ++ * @param size: Size to map from ++ * @param mapname: Mapping name ++ * @return 0 success < 0 failure ++ * ++ * Map 4k increments at given address to eSRAM. ++ */ ++int intel_cln_esram_map_range(void * vaddr, u32 size, char * mapname) ++{ ++ if(size == 0 || mapname == NULL || vaddr == NULL){ ++ return -EINVAL; ++ } ++ return __intel_cln_esram_page_op((u32)vaddr, size, mapname, ESRAM_MAP_OP); ++} ++EXPORT_SYMBOL(intel_cln_esram_map_range); ++ ++/** ++ * intel_cln_esram_map_symbol ++ * ++ * @param vaddr: Virtual address of the symbol ++ * @return 0 success < 0 failure ++ * ++ * Maps a series of 4k chunks starting at vaddr&0xFFFFF000. vaddr shall be a ++ * kernel text section symbol (kernel or loaded module) ++ * ++ * We get the size of the symbol from kallsyms. We guarantee to map the entire ++ * size of the symbol - plus whatever padding is necessary to get alignment to ++ * eSRAM_PAGE_SIZE ++ * Other stuff inside the mapped pages will get a performance boost 'for free'. ++ * If this free boost is not what you want then ++ * ++ * 1. Align to 4k ++ * 2. Pad to 4k ++ * 3. Call intel_cln_esram_map_range() ++ */ ++int intel_cln_esram_map_symbol(void * vaddr) ++{ ++ long unsigned int size = 0, offset = 0; ++ char symname[KSYM_SYMBOL_LEN]; ++ ++ kallsyms_lookup_size_offset((long unsigned int)vaddr, &size, &offset); ++ if(size == 0){ ++ return -EINVAL; ++ } ++ sprint_symbol(symname, (u32)vaddr); ++ ++ return __intel_cln_esram_page_op((u32)vaddr, size, symname, 1); ++} ++EXPORT_SYMBOL(intel_cln_esram_map_symbol); ++ ++/****************************************************************************** ++ * Module/PowerManagement hooks ++ ******************************************************************************/ ++ ++/** ++ * intel_cln_esram_suspend ++ * ++ * @param pdev: Platform device structure (unused) ++ * @param pm: Power managment descriptor ++ * @return 0 success < 0 failure ++ * ++ * For each enabled page - flush to DRAM and disable eSRAM page. ++ * For each 4k region the architecture guarantees atomicity of flush/disable. ++ * Hence any memory transactions to the affected region will stall until ++ * flush/disable completes - hence interrupts are left on. ++ */ ++static int intel_cln_esram_suspend(struct device * pdev) ++{ ++ /* Flush and disable of eSRAM pages is carried out automatically */ ++ return 0; ++} ++ ++/** ++ * intel_cln_esram_resume ++ * ++ * @param pm: Power management descriptor ++ * @return 0 success < 0 failure ++ * ++ * Runs after resume_noirq. Switches pages back to ro, if appropriate. We do ++ * this here since interrupts will be on, as required by the function ++ * set_memory_ro. If it were possible to set memory ro in resume_noirq we would ++ * do it there instead ++ */ ++static int intel_cln_esram_resume(struct device * pdev) ++{ ++ struct esram_page * epage = NULL; ++ int ret = 0; ++ ++ list_for_each_entry(epage, &esram_dev.page_used, list){ ++ ret |= intel_cln_esram_page_populate(epage); ++ } ++ ++ return ret; ++} ++ ++ ++/** ++ * intel_cln_esram_probe ++ * ++ * @param pdev: Platform device ++ * @return 0 success < 0 failure ++ * ++ * Callback from platform sub-system to probe ++ * ++ * This driver manages eSRAM on a per-page basis. Therefore if we find block ++ * mode is enabled, or any global, block-level or page-level locks are in place ++ * at module initialisation time - we bail out. ++ */ ++static int intel_cln_esram_probe(struct platform_device * pdev) ++{ ++ int ret = 0; ++ u32 block = 0, ctrl = 0, i = 0, pgstat = 0; ++ ++ memset(&esram_dev, 0x00, sizeof(esram_dev)); ++ INIT_LIST_HEAD(&esram_dev.page_used); ++ INIT_LIST_HEAD(&esram_dev.page_free); ++ spin_lock_init(&esram_dev.slock); ++ esram_dev.page_free_ct = 0; ++ ++ /* Ensure block mode disabled */ ++ block = ESRAM_PGBLOCK_DISABLE; ++ sb_write(SB_ID_ESRAM, ESRAM_CTRL_WRITE, ESRAM_PGBLOCK_REG, block, 1); ++ ++ /* Get state */ ++ sb_read(SB_ID_ESRAM, ESRAM_CTRL_READ, ESRAM_CTRL_REG, &ctrl, 1); ++ sb_read(SB_ID_ESRAM, ESRAM_CTRL_READ, ESRAM_PGBLOCK_REG, &block, 1); ++ ++ /* Verify state is good to go */ ++ if (ctrl & ESRAM_CTRL_GLOBAL_LOCK){ ++ pr_err ("eSRAM: global lock @ 0x%08x\n", ctrl); ++ return -ENODEV; ++ } ++ ++ if (block & (ESRAM_PGBLOCK_LOCK | ESRAM_PGBLOCK_ENABLE)){ ++ pr_err ("eSRAM: lock @ 0x%08x\n", block); ++ return -ENODEV; ++ } ++ pr_info("eSRAM: CTRL 0x%08x block 0x%08x\n", ctrl, block); ++ ++ /* Calculate # of pages silicon supports */ ++ esram_dev.page_count = ESRAM_CTRL_SIZE(ctrl) + 1; ++ esram_dev.page_free_ct = esram_dev.page_count; ++ pr_info("eSRAM: pages %d\n", esram_dev.page_free_ct); ++ ++ if(esram_dev.page_free_ct <= 1){ ++ pr_err("Too few pages reported by eSRAM sub-system\n"); ++ return -ENOMEM; ++ } ++ ++ /* Allocate an appropriate number of pages */ ++ esram_dev.pages = kzalloc(esram_dev.page_count * ++ sizeof(struct esram_page), GFP_KERNEL); ++ if (esram_dev.pages == NULL){ ++ return -ENOMEM; ++ } ++ ++ /* Initialise list of free pages, explicitely disable as we go */ ++ for(i = 0; i < esram_dev.page_count; i++){ ++ INIT_LIST_HEAD(&esram_dev.pages[i].name_list); ++ esram_dev.pages[i].id = i; ++ ++ /* Read & verify page state */ ++ sb_read(SB_ID_ESRAM, ESRAM_PAGE_READ, i, &pgstat, 1); ++ if(pgstat & (ESRAM_PAGE_BUSY | ESRAM_PAGE_LOCK)){ ++ pr_err("eSRAM: page %d state 0x%08x err\n", i, pgstat); ++ ret = -ENODEV; ++ goto err; ++ } ++ ++ list_add(&esram_dev.pages[i].list, &esram_dev.page_free); ++ } ++ ++ ret = sysfs_create_group(&pdev->dev.kobj, &esram_attrib_group); ++ if (ret) ++ goto err; ++ ++ return 0; ++err: ++ kfree(esram_dev.pages); ++ return ret; ++} ++ ++/* ++ * Power management operations ++ */ ++static const struct dev_pm_ops intel_cln_esram_pm_ops = { ++ .suspend = intel_cln_esram_suspend, ++ .resume = intel_cln_esram_resume, ++}; ++ ++/* ++ * Platform structures useful for interface to PM subsystem ++ */ ++static struct platform_driver intel_cln_esram_driver = { ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ .pm = &intel_cln_esram_pm_ops, ++ }, ++ .probe = intel_cln_esram_probe, ++}; ++ ++module_platform_driver(intel_cln_esram_driver); ++ ++MODULE_AUTHOR("Bryan O'Donoghue <bryan.odonoghue@linux.intel.com>"); ++MODULE_DESCRIPTION("Intel Clanton eSRAM overlay/ECC-scrub driver"); ++MODULE_LICENSE("Dual BSD/GPL"); ++ +diff --git a/drivers/platform/x86/quark/intel_cln_esram.h b/drivers/platform/x86/quark/intel_cln_esram.h +new file mode 100644 +index 0000000..af6156a +--- /dev/null ++++ b/drivers/platform/x86/quark/intel_cln_esram.h +@@ -0,0 +1,107 @@ ++/* ++ * Copyright(c) 2013 Intel Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ */ ++/* ++ * Intel Clanton eSRAM overlay driver ++ * ++ * eSRAM is an on-chip fast access SRAM. ++ * ++ * This driver provides the ability to map a kallsyms derived symbol of ++ * arbitrary length or a struct page entitiy. ++ * A proc interface is provided to allow map/unmap of kernel structures, without ++ * having to use the API from your code directly. ++ * ++ * Example: ++ * echo ehci_irq on > /proc/driver/esram/map ++ * echo ehci_irq off > /proc/driver/esram/map ++ * ++ * An API is provided to allow for mapping of a) kernel symbols or b) pages. ++ * eSRAM requires 4k physically aligned addresses to work - so a struct page ++ * fits neatly into this. ++ * ++ * To populte eSRAM we must copy data to a temporary buffer, overlay and ++ * then copy data back to the eSRAM region. ++ * ++ * When entering S3 - we must save eSRAM state to DRAM, and similarly on restore ++ * to S0 we must repopulate eSRAM ++ * ++ * Author : Bryan O'Donoghue <bryan.odonoghue@linux.intel.com> ++ */ ++#ifndef __INTEL_CLN_ESRAM_H__ ++#define __INTEL_CLN_ESRAM_H__ ++ ++#include <linux/module.h> ++ ++/* Basic size of an eSRAM page */ ++#define INTEL_CLN_ESRAM_PAGE_SIZE (0x1000) ++#define INTEL_CLN_ESRAM_PAGE_COUNT (0x80) ++/** ++ * intel_cln_esram_map_range ++ * ++ * @param vaddr: Virtual address to start mapping (must be 4k aligned) ++ * @param size: Size to map from ++ * @param mapname: Mapping name ++ * @return 0 success < 0 failure ++ * ++ * Map 4k increments at given address to eSRAM. ++ */ ++int intel_cln_esram_map_range(void * vaddr, u32 size, char * mapname); ++ ++/** ++ * intel_cln_esram_unmap_range ++ * ++ * @param vaddr: The virtual address to unmap ++ * @return 0 success < 0 failure ++ * ++ * Logical corollary of esram_map_page ++ */ ++int intel_cln_esram_unmap_range(void * vaddr, u32 size, char * mapname); ++ ++/** ++ * intel_cln_esram_map_symbol ++ * ++ * @param vaddr: Virtual address of the symbol ++ * @return 0 success < 0 failure ++ * ++ * Maps a series of 4k chunks starting at vaddr&0xFFFFF000. vaddr shall be a ++ * kernel text section symbol (kernel or loaded module) ++ * ++ * We get the size of the symbol from kallsyms. We guarantee to map the entire ++ * size of the symbol - plus whatever padding is necessary to get alignment to ++ * eSRAM_PAGE_SIZE ++ * Other stuff inside the mapped pages will get a performance boost 'for free'. ++ * If this free boost is not what you want then ++ * 1. Align to 4k ++ * 2. Pad to 4k ++ * 3. Call intel_cln_esram_map_range() ++ */ ++int intel_cln_esram_map_symbol(void * vaddr); ++ ++/** ++ * intel_cln_esram_unmap_symbol ++ * ++ * @param vaddr: Virtual address of the symbol ++ * @return 0 success < 0 failure ++ * ++ * Logical corollary to intel_cln_esram_map_symbol ++ * Undoes any mapping of pages starting at sym for sym's size ++ */ ++int intel_cln_esram_unmap_symbol(void * vaddr); ++ ++#endif /* __INTEL_CLN_ESRAM_H__ */ +diff --git a/drivers/platform/x86/quark/intel_cln_esram_test.c b/drivers/platform/x86/quark/intel_cln_esram_test.c +new file mode 100644 +index 0000000..3d0573d +--- /dev/null ++++ b/drivers/platform/x86/quark/intel_cln_esram_test.c +@@ -0,0 +1,602 @@ ++/* ++ * Copyright(c) 2013 Intel Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ */ ++/** ++ * intel_cln_esram_test.c ++ * ++ * Simple test module to provide test cases for ITS integration ++ * ++ */ ++#include <linux/cdev.h> ++#include <linux/crc32.h> ++#include <linux/crc32c.h> ++#include <linux/delay.h> ++#include <linux/device.h> ++#include <linux/fs.h> ++#include <linux/intel_cln_sb.h> ++#include <linux/kallsyms.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/printk.h> ++#include <linux/slab.h> ++ ++#include "intel_cln_esram.h" ++#include "intel_cln_esram_test.h" ++ ++#define DRIVER_NAME "intel_cln_esram_test" ++ ++/** ++ * struct intel_cln_esram_dev ++ * ++ * Structre to represent module state/data/etc ++ */ ++struct intel_cln_esram_test_dev{ ++ unsigned int opened; ++ struct platform_device *pldev; /* Platform device */ ++ struct cdev cdev; ++ struct mutex open_lock; ++ char * pdata; ++ u32 size; ++}; ++ ++static struct intel_cln_esram_test_dev esram_test_dev; ++static struct class *esram_test_class; ++static DEFINE_MUTEX(esram_test_mutex); ++static int esram_test_major; ++static char * name = "testmap"; ++ ++/****************************************************************************** ++ * eSRAM BIST ++ ******************************************************************************/ ++ ++static int crc_cache = 0; ++ ++unsigned long long tsc_delta(unsigned long long first, unsigned long long end) ++{ ++ if (first < end) ++ return end - first; ++ else ++ return (ULLONG_MAX - first) + end; ++} ++ ++ ++/** ++ * intel_cln_crctest ++ * ++ * Do a CRC32 of the specified region. Return the time taken in jiffies ++ */ ++static unsigned long long intel_cln_crctest(char * pdata, u32 crcsize) ++{ ++ unsigned long long j1 = 0, j2 = 0; ++ ++ rdtscll(j1); ++ ++ /* Flush LMT cache to introduce cache miss to our test */ ++ __asm__ __volatile__("wbinvd\n"); ++ crc32(0, pdata, crcsize); ++ ++ rdtscll(j2); ++ ++ return tsc_delta(j1, j2); ++} ++ ++#ifdef __DEBUG__ ++#define bist_err(x){\ ++ pr_err("eSRAM bist err line %d errno %d\n", (__LINE__-2), x);\ ++ return x;\ ++} ++#else ++#define bist_err(x){\ ++ return x;\ ++} ++#endif ++/** ++ * intel_cln_esram_perpage_overlay ++ * ++ * Maps to integration test spec ID CLN.F.SW.APP.eSRAM.0 ++ */ ++int intel_cln_esram_test_perpage_overlay(void) ++{ ++ ++ int ret = 0; ++ u32 idx = 0, size = INTEL_CLN_ESRAM_PAGE_SIZE; ++ ++ /* Set a known state */ ++ for(idx = 0; idx < size; idx += sizeof(u32)){ ++ *((u32*)&esram_test_dev.pdata[idx]) = idx; ++ } ++ ++ ++ /* Basic test of full range of memory */ ++ ret = intel_cln_esram_map_range(esram_test_dev.pdata, size, name); ++ if(ret){ ++ bist_err(ret); ++ } ++ for(idx = 0; idx < size; idx += sizeof(u32)){ ++ if(*((u32*)&esram_test_dev.pdata[idx]) != idx){ ++ pr_err("Entry %d is 0x%08x require 0x%08x", ++ idx, esram_test_dev.pdata[idx], idx); ++ bist_err(-EIO); ++ } ++ } ++ ++#if 0 ++ ret = intel_cln_esram_unmap_range(esram_test_dev.pdata, size, name); ++ if(ret){ ++ bist_err(ret); ++ } ++#endif ++ return 0; ++} ++ ++/** ++ * intel_cln_esram_test_pageref_count ++ * ++ * Ensure page reference couting works as expected ++ */ ++int intel_cln_esram_test_pagref_count(void) ++{ ++ u32 size = INTEL_CLN_ESRAM_PAGE_SIZE; ++ int ret = 0; ++ ++ return 0; ++ /* Map a page */ ++ ret = intel_cln_esram_map_range(esram_test_dev.pdata, size, name); ++ if(ret){ ++ bist_err(ret); ++ } ++ ++ /* Map a second time - and verify mapping fails */ ++ ret = intel_cln_esram_map_range(esram_test_dev.pdata, size, name); ++ if(ret == 0){ ++ bist_err(-EFAULT); ++ } ++ ++#if 0 ++ /* Unmap - OK */ ++ ret = intel_cln_esram_unmap_range(esram_test_dev.pdata, size, name); ++ if(ret){ ++ bist_err(ret); ++ } ++ ++ /* Verify second unmap operation fails */ ++ ret = intel_cln_esram_unmap_range(esram_test_dev.pdata, size, name); ++ if(ret == 0){ ++ bist_err(-EFAULT); ++ } ++#endif ++ return 0; ++} ++ ++extern uint32_t get_crc32table_le(void); ++ ++/** ++ * intel_cln_esram_test_contig_perfmetric ++ * ++ * Do a CRC16 for a contigous area of memory ++ * Map contigous area and get a CRC16 ++ * ++ * Ensure overlayed data takes less time than regular unoverlayed DRAM ++ */ ++int intel_cln_esram_test_contig_perfmetric(void) ++{ ++ u32 crcsize = 0x60000; ++ unsigned long long crc32_fullmap = 0, crc32_fullunmap = 0; ++ uint32_t crc32table_le = kallsyms_lookup_name("crc32table_le"); ++ int ret = 0; ++ ++ if (crc32table_le == 0){ ++ pr_err("%s unable to fine symbol crc32table_le\n", __func__); ++ return -ENODEV; ++ } ++ ++ /* Get raw data metric */ ++ crc_cache = 1; ++ crc32_fullunmap = intel_cln_crctest(esram_test_dev.pdata, crcsize); ++ ++ /* Map CRC16 symbol (algorithm) + code (data) */ ++ ret = intel_cln_esram_map_symbol(crc32_le); ++ if(ret){ ++ bist_err(ret); ++ } ++ ret = intel_cln_esram_map_symbol((void*)crc32table_le); ++ if(ret){ ++ bist_err(ret); ++ } ++ ++ /* Map test data */ ++ ret = intel_cln_esram_map_range(esram_test_dev.pdata, crcsize, name); ++ if(ret){ ++ bist_err(ret); ++ } ++ ++ /* Get metric */ ++ crc_cache = 1; ++ crc32_fullmap = intel_cln_crctest(esram_test_dev.pdata, crcsize); ++#if 0 ++ /* Tidy up */ ++ ret = intel_cln_esram_unmap_range(esram_test_dev.pdata, crcsize, name); ++ if(ret){ ++ bist_err(ret); ++ } ++ ret = intel_cln_esram_unmap_range(((void*)crc32_table), ++ sizeof(crc32_table), name); ++ if(ret){ ++ bist_err(ret); ++ } ++ ret = intel_cln_esram_unmap_symbol(crc32); ++ if(ret){ ++ bist_err(ret); ++ } ++#endif ++ pr_info("%s did crctest - mapped - in %llu ticks\n", __func__, crc32_fullmap); ++ pr_info("%s mapped count %llu unmapped %llu\n", ++ __func__, crc32_fullmap, crc32_fullunmap); ++ return crc32_fullmap < crc32_fullunmap; ++} ++ ++/** ++ * intel_cln_esram_test_kernel_codemap ++ * ++ * Maps some kernel code - a data section and then calls the code contained ++ * therein. Proves out the running overlayed eSRAM works ++ */ ++int intel_cln_esram_test_kernel_codemap(void) ++{ ++#if 0 ++ int ret = intel_cln_esram_map_symbol(msleep); ++ if(ret){ ++ printk(KERN_ERR "%s map symbol msleep fail\n", __FUNCTION__); ++ bist_err(ret); ++ } ++ ++ /* run the mapped code */ ++ msleep(1); ++ ++ /* unmap */ ++ ret = intel_cln_esram_unmap_symbol(msleep); ++ if(ret){ ++ printk(KERN_ERR "%s unmap symbol msleep fail\n", __FUNCTION__); ++ bist_err(ret); ++ } ++#endif ++ return 0; ++} ++ ++/** ++ * intel_cln_esram_test_kernel_datamap ++ * ++ * Tests mapping/unmapping of a kernel data structure ++ */ ++int intel_cln_esram_test_kernel_datamap(void) ++{ ++#if 0 ++ unsigned long jtag = 0; ++ unsigned long ctrl = 0; ++ ++ /* Map the interrupt descriptor table */ ++ int ret = intel_cln_esram_map_range(idt_table, INTEL_CLN_ESRAM_PAGE_SIZE, name); ++ if(ret){ ++ bist_err(ret); ++ } ++ ++ jtag = jiffies; ++ /* Wait for jiffies to tick or timeout to occur (failure) */ ++ while(jtag == jiffies){ ++ ctrl++; ++ } ++ ++ /* unmap */ ++ ret = intel_cln_esram_unmap_range(idt_table, INTEL_CLN_ESRAM_PAGE_SIZE, name); ++ if(ret){ ++ bist_err(ret); ++ } ++#endif ++ return 0; ++} ++ ++/** ++ * intel_cln_esram_test_sub_unsub ++ * ++ * Subscribe and unsubscribe 100% of available eSRAM ++ */ ++int intel_cln_esram_test_sub_unsub(void) ++{ ++ int ret = 0; ++ u32 idx = 0, size = INTEL_CLN_ESRAM_PAGE_SIZE * INTEL_CLN_ESRAM_PAGE_COUNT; ++ ++ /* Set a known state */ ++ for(idx = 0; idx < size; idx += sizeof(u32)){ ++ *((u32*)&esram_test_dev.pdata[idx]) = idx; ++ } ++ ++ /* Basic test of full range of memory */ ++ ret = intel_cln_esram_map_range(esram_test_dev.pdata, size, name); ++ if(ret){ ++ bist_err(ret); ++ } ++ for(idx = 0; idx < size; idx += sizeof(u32)){ ++ if(*((u32*)&esram_test_dev.pdata[idx]) != idx){ ++ pr_err("Entry %d is 0x%08x require 0x%08x", ++ idx, esram_test_dev.pdata[idx], idx); ++ bist_err(-EIO); ++ } ++ } ++#if 0 ++ ret = intel_cln_esram_unmap_range(esram_test_dev.pdata, size, name); ++ if(ret){ ++ bist_err(ret); ++ } ++#endif ++ return 0; ++} ++ ++/** ++ * intel_cln_esram_test_over_sub ++ * ++ * Test oversubscription of eSRAM ++ */ ++int intel_cln_esram_test_over_sub(void) ++{ ++ int ret = 0; ++ u32 size = INTEL_CLN_ESRAM_PAGE_SIZE * (INTEL_CLN_ESRAM_PAGE_COUNT + 1); ++ ++ /* Over subscribe should fail */ ++ ret = intel_cln_esram_map_range(esram_test_dev.pdata, size, name); ++ if(ret == 0){ ++ //intel_cln_esram_unmap_range(esram_test_dev.pdata, size, name); ++ bist_err(-EFAULT); ++ } ++ return 0; ++} ++ ++/* ++ * File ops ++ */ ++static long esram_test_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ int ret = -EINVAL; ++ ++ cmd -= CLN_ESRAM_IOCTL_BASE; ++ switch (cmd) { ++ case CLN_F_SW_APP_ESRAM_0: ++ /* Per page overlay */ ++ ret = intel_cln_esram_test_perpage_overlay(); ++ break; ++ ++ case CLN_F_SW_APP_ESRAM_1: ++ /* Verify page reference counting */ ++ ret = intel_cln_esram_test_pagref_count(); ++ break; ++ ++ case CLN_F_SW_APP_ESRAM_2: ++ /* Performance metric or overlay contig RAM */ ++ ret = intel_cln_esram_test_contig_perfmetric(); ++ if (ret == 1) ++ ret = 0; ++ break; ++ ++ case CLN_F_SW_APP_ESRAM_3: ++ /* Verify mapping of kernel code section */ ++ /* Covered by test #2 */ ++ ret = 0; //intel_cln_esram_test_kernel_codemap(); ++ break; ++ ++ case CLN_F_SW_APP_ESRAM_4: ++ /* Verify mapping of kernel data section (IDT) */ ++ /* Covered by test #2 */ ++ ret = 0; //intel_cln_esram_test_kernel_datamap(); ++ break; ++ ++ case CLN_F_SW_APP_ESRAM_5: ++ /* Complete subscribe/unsubscribe eSRAM */ ++ ret = intel_cln_esram_test_sub_unsub(); ++ break; ++ ++ case CLN_F_SW_APP_ESRAM_6: ++ /* Over subscribe eSRAM */ ++ ret = intel_cln_esram_test_over_sub(); ++ break; ++ ++ default: ++ break; ++ } ++ ++ return ret; ++} ++ ++static int esram_test_open(struct inode *inode, struct file *file) ++{ ++ mutex_lock(&esram_test_mutex); ++ nonseekable_open(inode, file); ++ ++ if (mutex_lock_interruptible(&esram_test_dev.open_lock)) { ++ mutex_unlock(&esram_test_mutex); ++ return -ERESTARTSYS; ++ } ++ ++ if (esram_test_dev.opened) { ++ mutex_unlock(&esram_test_dev.open_lock); ++ mutex_unlock(&esram_test_mutex); ++ return -EINVAL; ++ } ++ ++ esram_test_dev.opened++; ++ mutex_unlock(&esram_test_dev.open_lock); ++ mutex_unlock(&esram_test_mutex); ++ ++ return 0; ++} ++ ++static int esram_test_release(struct inode *inode, struct file *file) ++{ ++ mutex_lock(&esram_test_dev.open_lock); ++ esram_test_dev.opened = 0; ++ mutex_unlock(&esram_test_dev.open_lock); ++ ++ return 0; ++} ++ ++static const struct file_operations esram_test_file_ops = { ++ .open = esram_test_open, ++ .release = esram_test_release, ++ .unlocked_ioctl = esram_test_ioctl, ++ .llseek = no_llseek, ++}; ++ ++ ++/** ++ * intel_cln_esram_test_probe ++ * ++ * @param pdev: Platform device ++ * @return 0 success < 0 failure ++ * ++ * Callback from platform sub-system to probe ++ * ++ * This driver manages eSRAM on a per-page basis. Therefore if we find block ++ * mode is enabled, or any global, block-level or page-level locks are in place ++ * at module initialisation time - we bail out. ++ */ ++static int intel_cln_esram_test_probe(struct platform_device * pdev) ++{ ++ int retval = 0; ++ unsigned int minor = 0; ++ ++ esram_test_dev.size = INTEL_CLN_ESRAM_PAGE_COUNT * INTEL_CLN_ESRAM_PAGE_SIZE; ++ ++ /* Get memory */ ++ esram_test_dev.pdata = kzalloc(esram_test_dev.size, GFP_KERNEL); ++ if(unlikely(esram_test_dev.pdata == NULL)){ ++ pr_err("Can't allocate %d bytes\n", esram_test_dev.size); ++ return -ENOMEM; ++ } ++ ++ mutex_init(&esram_test_dev.open_lock); ++ cdev_init(&esram_test_dev.cdev, &esram_test_file_ops); ++ esram_test_dev.cdev.owner = THIS_MODULE; ++ ++ retval = cdev_add(&esram_test_dev.cdev, MKDEV(esram_test_major, minor), 1); ++ if (retval) { ++ printk(KERN_ERR "chardev registration failed\n"); ++ kfree(esram_test_dev.pdata); ++ return -EINVAL; ++ } ++ if (IS_ERR(device_create(esram_test_class, NULL, ++ MKDEV(esram_test_major, minor), NULL, ++ "esramtest%u", minor))){ ++ dev_err(&pdev->dev, "can't create device\n"); ++ kfree(esram_test_dev.pdata); ++ return -EINVAL; ++ } ++ printk(KERN_INFO "%s/%s/%s complete OK !!\n", __FUNCTION__, __DATE__,__TIME__); ++ return 0; ++ ++} ++ ++/** ++ * intel_cln_esram_remove ++ * ++ * @return 0 success < 0 failure ++ * ++ * Removes a platform device ++ */ ++static int intel_cln_esram_test_remove(struct platform_device * pdev) ++{ ++ unsigned int minor = MINOR(esram_test_dev.cdev.dev); ++ ++ device_destroy(esram_test_class, MKDEV(esram_test_major, minor)); ++ cdev_del(&esram_test_dev.cdev); ++ kfree(esram_test_dev.pdata); ++ ++ return 0; ++} ++ ++/* ++ * Platform structures useful for interface to PM subsystem ++ */ ++static struct platform_driver intel_cln_esram_test_driver = { ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++ .remove = intel_cln_esram_test_remove, ++}; ++ ++/** ++ * intel_cln_esram_init ++ * ++ * @return 0 success < 0 failure ++ * ++ * Module entry point ++ */ ++static int __init intel_cln_esram_test_init(void) ++{ ++ int retval = 0; ++ dev_t dev; ++ ++ esram_test_class = class_create(THIS_MODULE,"cln_esram_test"); ++ if (IS_ERR(esram_test_class)) { ++ retval = PTR_ERR(esram_test_class); ++ printk(KERN_ERR "esram_test: can't register earam_test class\n"); ++ goto err; ++ } ++ ++ retval = alloc_chrdev_region(&dev, 0, 1, "esram_test"); ++ if (retval) { ++ printk(KERN_ERR "earam_test: can't register character device\n"); ++ goto err_class; ++ } ++ esram_test_major = MAJOR(dev); ++ ++ memset(&esram_test_dev, 0x00, sizeof(esram_test_dev)); ++ esram_test_dev.pldev = platform_create_bundle( ++ &intel_cln_esram_test_driver, intel_cln_esram_test_probe, NULL, 0, NULL, 0); ++ ++ if(IS_ERR(esram_test_dev.pldev)){ ++ printk(KERN_ERR "platform_create_bundle fail!\n"); ++ retval = PTR_ERR(esram_test_dev.pldev); ++ goto err_class; ++ } ++ ++ return 0; ++ ++err_class: ++ class_destroy(esram_test_class); ++err: ++ return retval; ++} ++ ++/** ++ * intel_cln_esram_exit ++ * ++ * Module exit ++ */ ++static void __exit intel_cln_esram_test_exit(void) ++{ ++ platform_device_unregister(esram_test_dev.pldev); ++ platform_driver_unregister(&intel_cln_esram_test_driver); ++} ++ ++MODULE_AUTHOR("Bryan O'Donoghue <bryan.odonoghue@linux.intel.com>"); ++MODULE_DESCRIPTION("Intel Clanton eSRAM ITS driver"); ++MODULE_LICENSE("Dual BSD/GPL"); ++ ++module_init(intel_cln_esram_test_init); ++module_exit(intel_cln_esram_test_exit); +diff --git a/drivers/platform/x86/quark/intel_cln_esram_test.h b/drivers/platform/x86/quark/intel_cln_esram_test.h +new file mode 100644 +index 0000000..98e4546 +--- /dev/null ++++ b/drivers/platform/x86/quark/intel_cln_esram_test.h +@@ -0,0 +1,43 @@ ++/* ++ * Copyright(c) 2013 Intel Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ */ ++/** ++ * intel_cln_esram_test.h ++ * ++ * Define integers for ioctl operation ++ * ++ * Author : Bryan O'Donoghue <bryan.odonoghue@linux.intel.com> ++ */ ++ ++#ifndef __INTEL_CLN_ESRAM_TEST_H__ ++#define __INTEL_CLN_ESRAM_TEST_H__ ++ ++#define CLN_ESRAM_IOCTL_BASE 255 ++#define CLN_F_SW_APP_ESRAM_0 0x00000000 ++#define CLN_F_SW_APP_ESRAM_1 0x00000001 ++#define CLN_F_SW_APP_ESRAM_2 0x00000002 ++#define CLN_F_SW_APP_ESRAM_3 0x00000003 ++#define CLN_F_SW_APP_ESRAM_4 0x00000004 ++#define CLN_F_SW_APP_ESRAM_5 0x00000005 ++#define CLN_F_SW_APP_ESRAM_6 0x00000006 ++#define CLN_F_SW_APP_ESRAM_7 0x00000007 ++#define CLN_F_SW_APP_ESRAM_8 0x00000008 ++ ++#endif /* __INTEL_CLN_ESRAM_TEST_H__ */ ++ +diff --git a/drivers/platform/x86/quark/intel_cln_imr.c b/drivers/platform/x86/quark/intel_cln_imr.c +new file mode 100644 +index 0000000..b8259a6 +--- /dev/null ++++ b/drivers/platform/x86/quark/intel_cln_imr.c +@@ -0,0 +1,584 @@ ++/* ++ * Copyright(c) 2013 Intel Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ */ ++/* ++ * Intel Clanton IMR driver ++ * ++ * IMR stand for Insolate Memory Region, supported by Clanton SoC. ++ * ++ * A total number of 8 IMRs have implemented by Clanton SoC, ++ * Some IMRs might be already occupied by BIOS or Linux during ++ * booting time. ++ * ++ * A user can cat /sys/devices/platform/intel-cln-imr/status for current IMR ++ * status ++ * ++ * To allocate an IMR addresses must be alinged to 1k ++ * ++ * The IMR alloc API will locate the next available IMR slot set up ++ * with input memory region, then apply the input access right masks ++ * ++ * The IMR can be freed with the pre-allocated memory addresses. ++ */ ++ ++#include <asm-generic/uaccess.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/printk.h> ++#include <linux/proc_fs.h> ++ ++#include "intel_cln_imr.h" ++#include <asm/imr.h> ++ ++#define DRIVER_NAME "intel-cln-imr" ++ ++#define IMR_READ_MASK 0x1 ++#define IMR_MAX_ID 7 ++ ++#ifndef phys_to_virt ++#define phys_to_virt __va ++#endif ++ ++/* IMR HW register address structre */ ++struct cln_imr_reg_t { ++ u8 imr_xl; /* high address register */ ++ u8 imr_xh; /* low address register */ ++ u8 imr_rm; /* read mask register */ ++ u8 imr_wm; /* write mask register */ ++} cln_imr_reg_t; ++ ++/** ++ * struct cln_imr_addr_t ++ * ++ * IMR memory address structure ++ */ ++struct cln_imr_addr_t { ++ u32 addr_low; /* low boundary memroy address */ ++ u32 addr_high; /* high boundary memory address */ ++ u32 read_mask; /* read access right mask */ ++ u32 write_mask; /* write access right mask */ ++} cln_imr_addr_t; ++ ++/** ++ * struct cln_imr_pack ++ * ++ * local IMR pack structure ++ */ ++struct cln_imr_pack { ++ bool occupied; /* IMR occupied */ ++ bool locked; /* IMR lock */ ++ struct cln_imr_reg_t reg; /* predefined imr register address */ ++ struct cln_imr_addr_t addr; /* IMR address region structure */ ++ unsigned char info[MAX_INFO_SIZE]; /* IMR info */ ++} cln_imr_pack; ++ ++ ++/* Predefined HW register address */ ++static struct cln_imr_reg_t imr_reg_value[] = { ++ { IMR0L, IMR0H, IMR0RM, IMR0WM }, ++ { IMR1L, IMR1H, IMR1RM, IMR1WM }, ++ { IMR2L, IMR2H, IMR2RM, IMR2WM }, ++ { IMR3L, IMR3H, IMR3RM, IMR3WM }, ++ { IMR4L, IMR4H, IMR4RM, IMR4WM }, ++ { IMR5L, IMR5H, IMR5RM, IMR5WM }, ++ { IMR6L, IMR6H, IMR6RM, IMR6WM }, ++ { IMR7L, IMR7H, IMR7RM, IMR7WM } ++}; ++ ++static struct platform_device *pdev; ++ ++/** ++ * module parameter ++ * IMR slot should repersant the available IMR region from ++ * linux boot and BIOS. ++ * ++ * For example: imr_bit_mask = 0x10111001 ++ * occupied IMR: 0, 3, 4, 5, 7 ++ * un-occupied IMR: 1, 2, 6 ++ */ ++static int imr_bit_mask = 0xFF; ++module_param(imr_bit_mask, int, S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(imr_bit_mask, "IMR bit mask"); ++ ++/** ++ * module parameter ++ * if IMR lock is a nozero value, all unlocked ++ * imrs will be locked regardless the usage. ++ */ ++static int imr_lock = 0; ++module_param(imr_lock, int, S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(imr_lock, "switch to lock unused IMR"); ++ ++/* local IMR data structure */ ++struct cln_imr_pack local_imr[IMR_MAXID]; ++ ++/** ++ * intel_cln_imr_read_reg ++ * ++ * @param reg: register address ++ * @return nothing ++ * ++ * return register value from input address. ++ */ ++static inline uint32_t intel_cln_imr_read_reg(uint8_t reg) ++{ ++ uint32_t temp = 0; ++ ++ intel_cln_sb_read_reg(SB_ID_ESRAM, CFG_READ_OPCODE, reg, &temp, 0); ++ return temp; ++} ++ ++/** ++ * intel_clm_imr_latch_data ++ * ++ * @return nothing ++ * ++ * Populate IMR data structure from HW. ++ */ ++static inline void intel_clm_imr_latch_data(void) ++{ ++ int i = 0; ++ ++ for (i = 0; i < IMR_MAXID; i++) { ++ ++ local_imr[i].addr.addr_low = ++ intel_cln_imr_read_reg(imr_reg_value[i].imr_xl); ++ local_imr[i].addr.addr_high = ++ intel_cln_imr_read_reg(imr_reg_value[i].imr_xh); ++ local_imr[i].addr.read_mask = ++ intel_cln_imr_read_reg(imr_reg_value[i].imr_rm); ++ local_imr[i].addr.write_mask = ++ intel_cln_imr_read_reg(imr_reg_value[i].imr_wm); ++ ++ if (local_imr[i].addr.addr_low & IMR_LOCK_BIT) ++ local_imr[i].locked = true; ++ ++ if (local_imr[i].addr.read_mask > 0 && ++ local_imr[i].addr.read_mask < IMR_READ_ENABLE_ALL){ ++ local_imr[i].occupied = true; ++ } else { ++ local_imr[i].occupied = false; ++ memcpy(local_imr[i].info, "NOT USED", MAX_INFO_SIZE); ++ } ++ } ++} ++ ++/** ++ * prepare_input_addr ++ * ++ * @param addr: input physical memory address ++ * @return formated memory address ++ * ++ * 1. verify input memory address alignment ++ * 2. apply IMR_REG_MASK to match the format required by HW ++ */ ++static inline uint32_t prepare_input_addr(uint32_t addr) ++{ ++ if (addr & (IMR_MEM_ALIGN - 1)) ++ return 0; ++ ++ addr = (addr >> 8) & IMR_REG_MASK; ++ return addr; ++} ++ ++/** ++ * intel_cln_imr_find_free_entry ++ * ++ * @return the next free imr slot ++ */ ++static int intel_cln_imr_find_free_entry(void) ++{ ++ int i = 0; ++ ++ intel_clm_imr_latch_data(); ++ ++ for (i = 0; i < IMR_MAXID; i++) { ++ if ((!local_imr[i].occupied) && (!local_imr[i].locked)) ++ return i; ++ } ++ ++ pr_err("%s: No more free IMR available.\n", __func__); ++ return -ENOMEM; ++} ++ ++/** ++ * intel_cln_imr_add_entry ++ * ++ * @param id: imr slot id ++ * @param hi: hi memory address ++ * @param lo: lo memory address ++ * @param read: read access mask ++ * @param write: write access mask ++ * @return nothing ++ * ++ * Setup an IMR entry ++ */ ++static void intel_cln_imr_add_entry(int id, uint32_t hi, ++ uint32_t lo, uint32_t read, uint32_t write, bool lock) ++{ ++ intel_cln_sb_write_reg(SB_ID_ESRAM, CFG_WRITE_OPCODE, ++ imr_reg_value[id].imr_xl, lo, 0); ++ intel_cln_sb_write_reg(SB_ID_ESRAM, CFG_WRITE_OPCODE, ++ imr_reg_value[id].imr_xh, hi, 0); ++ intel_cln_sb_write_reg(SB_ID_ESRAM, CFG_WRITE_OPCODE, ++ imr_reg_value[id].imr_rm, read, 0); ++ intel_cln_sb_write_reg(SB_ID_ESRAM, CFG_WRITE_OPCODE, ++ imr_reg_value[id].imr_wm, write, 0); ++ if (lock) { ++ lo |= IMR_LOCK_BIT; ++ intel_cln_sb_write_reg(SB_ID_ESRAM, CFG_WRITE_OPCODE, ++ imr_reg_value[id].imr_xl, lo, 0); ++ } ++} ++ ++/** ++ * get_phy_addr ++ * @return phy address value ++ * ++ * convert register format to physical address format. ++ */ ++static uint32_t get_phy_addr(uint32_t reg_value) ++{ ++ reg_value = ((reg_value & IMR_REG_MASK) << 8); ++ return reg_value; ++} ++ ++ ++ ++/** ++ * intel_cln_imr_init_mask ++ * ++ * @param mask: module parameter ++ * @return nothing ++ * ++ * prepare local IMR data structure from input module parameter. ++ */ ++static void intel_cln_imr_init_mask(int mask) ++{ ++ int i = 0; ++ ++ BUG_ON((mask > 255 || mask < 0)); ++ ++ for (i = 0; i < IMR_MAXID; i++) { ++ local_imr[i].addr.addr_low = ++ intel_cln_imr_read_reg(imr_reg_value[i].imr_xl); ++ ++ /* mask bit 1 means imr occupied*/ ++ if (((mask>>i) & IMR_READ_MASK) == 0) { ++ if (!(local_imr[i].addr.addr_low & IMR_LOCK_BIT)) ++ intel_cln_remove_imr_entry(i); ++ } ++ } ++} ++ ++/** ++ * intel_cln_remove_imr_entry ++ * ++ * @param id: imr slot id ++ * @return nothing ++ * ++ * remove imr slot based on input id ++ */ ++void intel_cln_remove_imr_entry(int id) ++{ ++ if (id >= IMR_MAXID || local_imr[id].locked) ++ return; ++ ++ intel_cln_sb_write_reg(SB_ID_ESRAM, CFG_WRITE_OPCODE, ++ imr_reg_value[id].imr_rm, IMR_READ_ENABLE_ALL, ++ 0); ++ intel_cln_sb_write_reg(SB_ID_ESRAM, CFG_WRITE_OPCODE, ++ imr_reg_value[id].imr_wm, IMR_WRITE_ENABLE_ALL, ++ 0); ++ intel_cln_sb_write_reg(SB_ID_ESRAM, CFG_WRITE_OPCODE, ++ imr_reg_value[id].imr_xl, IMR_BASE_ADDR, 0); ++ intel_cln_sb_write_reg(SB_ID_ESRAM, CFG_WRITE_OPCODE, ++ imr_reg_value[id].imr_xh, IMR_BASE_ADDR, 0); ++ ++ intel_clm_imr_latch_data(); ++ ++} ++EXPORT_SYMBOL(intel_cln_remove_imr_entry); ++ ++/** ++ * intel_cln_imr_alloc ++ * ++ * @param high: high boundary of memory address ++ * @param low: low boundary of memorry address ++ * @param read: IMR read mask value ++ * @param write: IMR write mask value ++ * @return nothing ++ * ++ * setup the next available IMR with customized read and write masks ++ */ ++int intel_cln_imr_alloc(uint32_t high, uint32_t low, uint32_t read, ++ uint32_t write, unsigned char *info, bool lock) ++{ ++ int id = 0; ++ ++ if (info == NULL) ++ return -EINVAL; ++ ++ if ((low & IMR_LOCK_BIT) || (read == 0 || write == 0)) { ++ pr_err("%s: Invalid acces mode\n", __func__); ++ return -EINVAL; ++ } ++ ++ /* Calculate aligned addresses and validate range */ ++ high = prepare_input_addr(high); ++ low = prepare_input_addr(low); ++ ++ /* Find a free entry */ ++ id = intel_cln_imr_find_free_entry(); ++ if (id < 0) ++ return -ENOMEM; ++ ++ /* Add entry - locking as necessary */ ++ intel_cln_imr_add_entry(id, high, low, (read & IMR_READ_ENABLE_ALL), ++ write, lock); ++ ++ /* Name the new entry */ ++ memcpy(local_imr[id].info, info, MAX_INFO_SIZE); ++ ++ /* Update local data structures */ ++ intel_clm_imr_latch_data(); ++ ++ pr_info("IMR alloc id %d 0x%08x - 0x%08x %s\n", id, low, high, ++ lock ? "locked" : "unlocked"); ++ ++ return 0; ++} ++EXPORT_SYMBOL(intel_cln_imr_alloc); ++ ++/** ++ * intel_cln_imr_free ++ * ++ * @param high: high boundary of memory address ++ * @param low: low boundary of memorry address ++ * @return nothing ++ * ++ * remove the imr based on input memory region ++ */ ++int intel_cln_imr_free(uint32_t high, uint32_t low) ++{ ++ int i = 0; ++ ++ if (low > high) { ++ pr_err("%s: Invalid input address values.\n", __func__); ++ return -EINVAL; ++ } ++ ++ high = prepare_input_addr(high); ++ if (!high) { ++ pr_err("%s: Invalid input memory address.\n", __func__); ++ return -EINVAL; ++ } ++ ++ low = prepare_input_addr(low); ++ if (!low) { ++ pr_err("%s: Invalid input memory address.\n", __func__); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < IMR_MAXID; i++) { ++ if (local_imr[i].occupied ++ && (local_imr[i].addr.addr_low == low) ++ && (local_imr[i].addr.addr_high == high) ++ && (!local_imr[i].locked)) { ++ intel_cln_remove_imr_entry(i); ++ return 0; ++ } ++ } ++ ++ return -EINVAL; ++} ++EXPORT_SYMBOL(intel_cln_imr_free); ++ ++/** ++ * intel_cln_imr_init_data ++ * ++ * @return nothing ++ * initialize local_imr data structure ++ */ ++static void intel_cln_imr_init_data(void) ++{ ++ int i = 0; ++ char * res_str = "System Reserved Region"; ++ int len = strlen(res_str); ++ ++ intel_clm_imr_latch_data(); ++ ++ for (i = 0; i < IMR_MAXID; i++) { ++ local_imr[i].reg = imr_reg_value[i]; ++ memcpy(local_imr[i].info, res_str, len); ++ } ++} ++ ++/** ++ * intel_cln_imr_lockall ++ * ++ * @param mask: module parameter ++ * @return nothing ++ * ++ * lock up all un-locked IMRs ++ */ ++int intel_cln_imr_lockall(void) ++{ ++ int i = 0; ++ uint32_t temp_addr; ++ ++ /* Enumerate IMR data structures */ ++ intel_cln_imr_init_data(); ++ intel_cln_imr_init_mask(imr_bit_mask); ++ ++ /* Cycle through IMRs locking whichever are unlocked */ ++ for (i = 0; i < IMR_MAXID; i++) { ++ ++ temp_addr = local_imr[i].addr.addr_low; ++ if (!(temp_addr & IMR_LOCK_BIT)) { ++ ++ DBG("%s: locking IMR %d\n", __func__, i); ++ temp_addr |= IMR_LOCK_BIT; ++ intel_cln_sb_write_reg(SB_ID_ESRAM, CFG_WRITE_OPCODE, ++ local_imr[i].reg.imr_xl, ++ temp_addr, 0); ++ } ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(intel_cln_imr_lockall); ++ ++/** ++ * intel_cln_imr_stat_show ++ * ++ * @param dev: pointer to device ++ * @param attr: attribute pointer ++ * @param buf: output buffer ++ * @return number of bytes successfully read ++ * ++ * Populates IMR state via /sys/device/intel-cln-imr/stat ++ */ ++static int intel_cln_imr_stat_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ int len = 0; ++ int i = 0; ++ int size, count = PAGE_SIZE; ++ uint32_t hi_phy_addr, lo_phy_addr; ++ ++ for (i = 0; i < IMR_MAXID; i++) { ++ ++ /* read back the actual input physical memory address */ ++ hi_phy_addr = get_phy_addr(local_imr[i].addr.addr_high); ++ lo_phy_addr = get_phy_addr(local_imr[i].addr.addr_low); ++ ++ /* the IMR always protect extra 1k memory size above the input ++ * high reg value ++ */ ++ size = ((hi_phy_addr - lo_phy_addr) / IMR_MEM_ALIGN) + 1; ++ ++ size = snprintf(buf+len, count, ++ "imr - id : %d\n" ++ "info : %s\n" ++ "occupied : %s\n" ++ "locked : %s\n" ++ "size : %d kb\n" ++ "hi addr (phy): 0x%08x\n" ++ "lo addr (phy): 0x%08x\n" ++ "hi addr (vir): 0x%08x\n" ++ "lo addr (vir): 0x%08x\n" ++ "read mask : 0x%08x\n" ++ "write mask : 0x%08x\n\n", ++ i, ++ local_imr[i].info, ++ local_imr[i].occupied ? "yes" : "no", ++ local_imr[i].locked ? "yes" : "no", ++ size, ++ hi_phy_addr, ++ lo_phy_addr, ++ (uint32_t)phys_to_virt(hi_phy_addr), ++ (uint32_t)phys_to_virt(lo_phy_addr), ++ local_imr[i].addr.read_mask, ++ local_imr[i].addr.write_mask); ++ len += size; ++ count -= size; ++ } ++ return len; ++} ++ ++static struct device_attribute dev_attr_stats = { ++ .attr = { ++ .name = "stat", ++ .mode = 0444, }, ++ .show = intel_cln_imr_stat_show, ++}; ++ ++static struct attribute *platform_attributes[] = { ++ &dev_attr_stats.attr, ++ NULL, ++}; ++ ++static struct attribute_group imr_attrib_group = { ++ .attrs = platform_attributes ++}; ++ ++/** ++ * intel_cln_imr_init ++ * ++ * @return 0 success < 0 failue ++ * ++ * module entry point ++ */ ++int __init intel_cln_imr_init(void) ++{ ++ int ret; ++ ++ pdev = platform_device_alloc(DRIVER_NAME, -1); ++ if (!pdev) ++ return -ENOMEM; ++ ++ ret = platform_device_add(pdev); ++ if (ret) ++ goto fail_platform; ++ ++ /* initialise local imr data structure */ ++ intel_cln_imr_init_data(); ++ ++ ret = sysfs_create_group(&pdev->dev.kobj, &imr_attrib_group); ++ if (ret) ++ goto fail_platform; ++ ++ if(intel_cln_imr_runt_setparams() == 0 && imr_lock == 1){ ++ intel_cln_imr_lockall(); ++ } ++ ++ return 0; ++ ++fail_platform: ++ platform_device_del(pdev); ++ return ret; ++} ++EXPORT_SYMBOL(intel_cln_imr_init); ++ ++MODULE_DESCRIPTION("Intel Clanton SOC IMR API "); ++MODULE_AUTHOR("Intel Corporation"); ++MODULE_LICENSE("Dual BSD/GPL"); ++ +diff --git a/drivers/platform/x86/quark/intel_cln_imr.h b/drivers/platform/x86/quark/intel_cln_imr.h +new file mode 100644 +index 0000000..3861d60 +--- /dev/null ++++ b/drivers/platform/x86/quark/intel_cln_imr.h +@@ -0,0 +1,155 @@ ++/* ++ * Copyright(c) 2013 Intel Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ */ ++/* ++ * Intel Clanton IMR driver ++ * ++ * IMR stand for Insolate Memory Region, supported by Clanton SoC. ++ * ++ * A total number of 8 IMRs have implemented by Clanton SoC, ++ * some IMRs might be already occupied by BIOS or Linux booting time. ++ * ++ * Input addresses parameter required the actual Physical address. ++ * ++ * The IMR alloc API will locate the next available IMR slot set up ++ * with input memory region, and apply with the default access right ++ * (CPU & CPU_snoop enable). ++ * ++ * The alloc_mask API takes input read & write masks values to set up ++ * IMR with customized access right. ++ * ++ * User can free IMR with pre-alloc specified addresses. ++ */ ++ ++#ifndef __INTEL_CLN_IMR_H__ ++#define __INTEL_CLN_IMR_H__ ++ ++#include <linux/intel_cln_sb.h> ++#include "asm/io.h" ++ ++#define CFG_READ_OPCODE 0x10 /* BUnit Control Read */ ++#define CFG_WRITE_OPCODE 0x11 /* BUnit control write */ ++ ++/* DRAM IMR register addresses */ ++#define IMR0L 0x40 ++#define IMR0H 0x41 ++#define IMR0RM 0x42 ++#define IMR0WM 0x43 ++#define IMR1L 0x44 ++#define IMR1H 0x45 ++#define IMR1RM 0x46 ++#define IMR1WM 0x47 ++#define IMR2L 0x48 ++#define IMR2H 0x49 ++#define IMR2RM 0x4A ++#define IMR2WM 0x4B ++#define IMR3L 0x4C ++#define IMR3H 0x4D ++#define IMR3RM 0x4E ++#define IMR3WM 0x4F ++#define IMR4L 0x50 ++#define IMR4H 0x51 ++#define IMR4RM 0x52 ++#define IMR4WM 0x53 ++#define IMR5L 0x54 ++#define IMR5H 0x55 ++#define IMR5RM 0x56 ++#define IMR5WM 0x57 ++#define IMR6L 0x58 ++#define IMR6H 0x59 ++#define IMR6RM 0x5A ++#define IMR6WM 0x5B ++#define IMR7L 0x5C ++#define IMR7H 0x5D ++#define IMR7RM 0x5E ++#define IMR7WM 0x5F ++ ++#define IMR_LOCK_BIT 0x80000000 ++#define IMR_WRITE_ENABLE_ALL 0xFFFFFFFF ++#define IMR_READ_ENABLE_ALL 0xBFFFFFFF ++#define IMR_REG_MASK 0xFFFFFC ++ ++#define IMR_ESRAM_FLUSH_INIT 0x80000000 /* esram flush */ ++#define IMR_SNOOP_ENABLE 0x40000000 /* core snoops */ ++#define IMR_PUNIT_ENABLE 0x20000000 ++#define IMR_SMM_ENABLE 0x02 /* core SMM access */ ++#define IMR_NON_SMM_ENABLE 0x01 /* core non-SMM access */ ++#define IMR_BASE_ADDR 0x00 ++#define IMR_MEM_ALIGN 0x400 ++ ++#define MAX_INFO_SIZE 64 ++#define IMR_MAXID 8 ++ ++/* snoop + Non SMM write mask */ ++#define IMR_DEFAULT_MASK (IMR_SNOOP_ENABLE \ ++ + IMR_ESRAM_FLUSH_INIT \ ++ + IMR_NON_SMM_ENABLE) ++ ++/* debug printk */ ++#ifdef DEBUG ++#define DBG(args...) pr_info(args) ++#else ++#define DBG(args...) ++#endif ++ ++extern unsigned long _text; ++extern unsigned long __init_begin; ++ ++/** ++ * intel_cln_imr_alloc ++ * ++ * @param high: the end of physical memory address ++ * @param low: the start of physical memory address ++ * @param read: IMR read mask value ++ * @param write: IMR write maks value ++ * @param info: imr information ++ * @param lock: imr lock ++ * ++ * Setup imr with customised read/ write masks ++ */ ++int intel_cln_imr_alloc(u32 high, u32 low, u32 read, u32 write, ++ unsigned char *info, bool lock); ++ ++/** ++ * intel_cln_imr_free ++ * ++ * @param high: high boundary of memory address ++ * @param low: low boundary of memorry address ++ * ++ * remove the imr based on input memory region ++ */ ++int intel_cln_imr_free(u32 high, u32 low); ++ ++/** ++ * intel_cln_remove_imr_entry ++ * ++ * @param id: internal imr data struct id ++ * ++ * Remove imr based on input imr data structure id ++ */ ++void intel_cln_remove_imr_entry(int id); ++ ++/** ++ * intel_cln_imr_init ++ * ++ * Initialise IMRs ++ */ ++int intel_cln_imr_init(void); ++ ++#endif +diff --git a/drivers/platform/x86/quark/intel_cln_imr_kernel.c b/drivers/platform/x86/quark/intel_cln_imr_kernel.c +new file mode 100644 +index 0000000..51e8676 +--- /dev/null ++++ b/drivers/platform/x86/quark/intel_cln_imr_kernel.c +@@ -0,0 +1,139 @@ ++/* ++ * Copyright(c) 2013 Intel Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ */ ++/* ++ * Intel Clanton IMR driver ++ * ++ * IMR stand for Insolate Memory Region, supported by Clanton SoC. ++ * ++ * The IMR id 3 is pre-defined as the use for kernel data protection ++ * ++ * The early imr protects entire memory (from the beginning of kernel text ++ * section to the top of memory) during linux boot time. In the linux run ++ * time, the protection need to resize down to memory region that only ++ * contains: kernel text, read only data, and initialized data section. ++ * ++ */ ++#include <linux/errno.h> ++#include <linux/intel_cln_sb.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/platform_data/clanton.h> ++#include <linux/printk.h> ++#include "intel_cln_imr.h" ++ ++/* pre-defined imr id for uncompressed kernel */ ++#define IMR_KERNEL_ID 3 ++ ++/** ++ * addr_hw_ready ++ * ++ * shift input address value to match HW required 1k aligned format ++ */ ++static inline uint32_t addr_hw_ready(uint32_t addr) ++{ ++ /* memory alignment */ ++ addr &= (~((1 << 10) - 1)); ++ ++ /* prepare input addr in HW required format */ ++ addr = (addr >> 8) & IMR_REG_MASK; ++ return addr; ++} ++ ++/** ++ * void intel_cln_imr_runt_kerndata_setup ++ * ++ * Setup imr for kernel text, read only data section ++ * ++ * The read only data (rodata) section placed between text and initialized data ++ * section by kernel. ++ */ ++static void intel_cln_imr_runt_kerndata_setup(void) ++{ ++ uint32_t hi; ++ uint32_t lo; ++ ++ hi = (uint32_t)virt_to_phys(&__init_begin); ++ lo = (uint32_t)virt_to_phys(&_text); ++ ++ /* Set a locked IMR around the kernel .text section */ ++ if (intel_cln_imr_alloc((hi - IMR_MEM_ALIGN), lo, ++ IMR_DEFAULT_MASK, IMR_DEFAULT_MASK, ++ "KERNEL RUNTIME DATA", 1)) { ++ pr_err("IMR: Set up runtime kernel data imr faild!\n"); ++ return; ++ } ++} ++ ++/** ++ * intel_cln_imr_teardown_unlocked ++ * ++ * Remove any unlocked IMR ++ * ++ */ ++static void intel_cln_imr_teardown_unlocked(void) ++{ ++ int i = 0; ++ for (i = 0; i < IMR_MAXID; i++) ++ intel_cln_remove_imr_entry(i); ++} ++ ++/** ++ * intel_cln_imr_runt_setparams ++ * ++ * set imr range for text, read only, initialised data in linux run time ++ */ ++int intel_cln_imr_runt_setparams(void) ++{ ++ /* Setup an IMR around the kernel .text area */ ++ intel_cln_imr_runt_kerndata_setup(); ++ ++ /* Remove any other unlocked IMR */ ++ intel_cln_imr_teardown_unlocked(); ++ ++ return 0; ++} ++EXPORT_SYMBOL(intel_cln_imr_runt_setparams); ++ ++/** ++ * intel_cln_imr_runt_init ++ * ++ * module entry point ++ */ ++static int __init intel_cln_imr_runt_init(void) ++{ ++ return 0; ++} ++ ++/** ++ * intel_cln_imr_runt_exit ++ * ++ * Module exit ++ */ ++static void __exit intel_cln_imr_runt_exit(void) ++{ ++ /* do nothing */ ++} ++ ++MODULE_DESCRIPTION("Intel Clanton SOC IMR API "); ++MODULE_AUTHOR("Intel Corporation"); ++MODULE_LICENSE("Dual BSD/GPL"); ++ ++subsys_initcall(intel_cln_imr_runt_init); ++module_exit(intel_cln_imr_runt_exit); +diff --git a/drivers/platform/x86/quark/intel_cln_imr_test.c b/drivers/platform/x86/quark/intel_cln_imr_test.c +new file mode 100644 +index 0000000..2d98507 +--- /dev/null ++++ b/drivers/platform/x86/quark/intel_cln_imr_test.c +@@ -0,0 +1,357 @@ ++/* ++ * Copyright(c) 2013 Intel Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ */ ++/* ++ * Intel Clanton IMR Test module ++ * ++ */ ++ ++#include <linux/cdev.h> ++#include <linux/device.h> ++#include <linux/fs.h> ++#include <linux/intel_cln_sb.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/printk.h> ++#include <linux/slab.h> ++#include "intel_cln_imr.h" ++ ++#define DRIVER_NAME "intel_cln_imr_test" ++ ++/** ++ * XXX intel_cln_sb.h needs to be updated with SB_ID_PUNIT and change ++ * propagated. This is a workaround to make it look less ugly. */ ++#define SB_ID_PUNIT SB_ID_THERMAL ++ ++/* Memory-mapped SPI Flash address */ ++#define ILB_SPIFLASH_BASEADDR 0xFF800000 ++/* PUnit DMA block transfer size, in bytes */ ++#define SPI_DMA_BLOCK_SIZE 512 ++ ++/**************************** Exported to LISA *******************************/ ++ ++/* ++ * Internally-used ioctl code. At the moment it is not reserved by any mainline ++ * driver. ++ */ ++#define IMR_TEST_IOCTL_CODE 0xE1 ++ ++/* ++ * Integers for ioctl operation. ++ */ ++#define IOCTL_CLN_SANITY_CHECK_PUNIT_DMA _IO(IMR_TEST_IOCTL_CODE, 0x00) ++#define IOCTL_CLN_IMR_1 _IO(IMR_TEST_IOCTL_CODE, 0x01) ++ ++/*****************************************************************************/ ++ ++/** ++ * struct intel_cln_imr_dev ++ * ++ * Structure to represent module state/data/etc ++ */ ++struct intel_cln_imr_test_dev { ++ unsigned int opened; ++ struct platform_device *pldev; /* Platform device */ ++ struct cdev cdev; ++ struct mutex open_lock; ++}; ++ ++static struct intel_cln_imr_test_dev imr_test_dev; ++static struct class *imr_test_class; ++static DEFINE_MUTEX(imr_test_mutex); ++static int imr_test_major; ++ ++/* PUnit DMA registers over side-band */ ++#define PUNIT_SPI_DMA_COUNT_REG 0x60 ++#define PUNIT_SPI_DMA_DEST_REG 0x61 ++#define PUNIT_SPI_DMA_SRC_REG 0x62 ++ ++/** ++ * ilb_spi_dma_read ++ * ++ * @param src: physical address in Legacy SPI Flash ++ * @param dst: physical address of destination ++ * @param dma_block_count: number of 512B SPI Flash blocks to be transferred ++ * ++ * Read-access iLB SPI via PUnit DMA engine. ++ * ++ */ ++static void ilb_spi_dma_read(u32 *src, u32 *dst, u32 dma_block_count) ++{ ++ pr_info("%s: src=%p, dst=%p, count=%u\n", __func__, src, dst, ++ dma_block_count); ++ ++ /* Setup source and destination addresses. */ ++ intel_cln_sb_write_reg(SB_ID_PUNIT, CFG_WRITE_OPCODE, ++ PUNIT_SPI_DMA_SRC_REG, (u32) src, 0); ++ intel_cln_sb_write_reg(SB_ID_PUNIT, CFG_WRITE_OPCODE, ++ PUNIT_SPI_DMA_DEST_REG, (u32) dst, 0); ++ ++ pr_info("%s: starting transaction\n", __func__); ++ ++ /* ++ * Setup the number of block to be copied over. Transaction will start ++ * as soon as the register is filled with value. ++ */ ++ intel_cln_sb_write_reg(SB_ID_PUNIT, CFG_WRITE_OPCODE, ++ PUNIT_SPI_DMA_COUNT_REG, dma_block_count, 0); ++ ++ /* Poll for completion. */ ++ while (dma_block_count > 0) { ++ intel_cln_sb_read_reg(SB_ID_PUNIT, CFG_READ_OPCODE, ++ PUNIT_SPI_DMA_COUNT_REG, &dma_block_count, 0); ++ } ++ ++ pr_info("%s: transaction completed\n", __func__); ++} ++ ++/** ++ * punit_dma_sanity_check ++ * ++ * @return 0 if success, 1 if failure ++ * ++ * Perform a basic sanity check for PUnit DMA engine. Copy over a 512B SPI ++ * Flash block. ++ */ ++static int punit_dma_sanity_check(void) ++{ ++ int err = 0; ++ u32 *buffer = NULL; ++ u32 buf_ph_addr = 0; ++ ++ /* Allocate 512B buffer for 1 SPI Flash block */ ++ buffer = kzalloc(SPI_DMA_BLOCK_SIZE, GFP_KERNEL); ++ if (!buffer) { ++ err = -ENOMEM; ++ goto end; ++ } ++ ++ /* DMA first SPI Flash block into buffer */ ++ buf_ph_addr = (u32)virt_to_phys(buffer); ++ ilb_spi_dma_read((u32 *)ILB_SPIFLASH_BASEADDR, (u32 *)buf_ph_addr, 1); ++ ++ kfree(buffer); ++end: ++ return err; ++} ++ ++/** ++ * imr_violate_kernel_punit_dma ++ * ++ * @return always 0 ++ * ++ * PUnit-DMA access to the Uncompressed Kernel IMR. ++ * This is based on set_imr_kernel_data() in intel_cln_imr.c. Find the physical ++ * address of .text section and copy a 512B chunk of legacy SPI via PuUnit DMA. ++ * ++ */ ++static int imr_violate_kernel_punit_dma(void) ++{ ++ extern unsigned long _text; ++ u32 kernel_text = (u32)virt_to_phys(&_text); ++ ++ /* We expect this to trigger an IMR violation reset */ ++ ilb_spi_dma_read((u32 *)ILB_SPIFLASH_BASEADDR, (u32 *)kernel_text, 1); ++ ++ /* ++ * If we're still alive, we have a serious bug: ++ * - we didn't appropriately target the IMR? ++ * - if we have, weren't we prevented from accessing? ++ * - if we weren't prevented, it's unlikely we're alive with a dirty ++ * text section ++ */ ++ pr_err("%s: BUG: still running after DMAing into kernel text!?\n", ++ __func__); ++ ++ return 0; ++} ++ ++/* ++ * File ops ++ */ ++static long imr_test_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ int ret = -EINVAL; ++ ++ switch (cmd) { ++ case IOCTL_CLN_SANITY_CHECK_PUNIT_DMA: ++ /* Check PUnit DMA actually works */ ++ ret = punit_dma_sanity_check(); ++ break; ++ case IOCTL_CLN_IMR_1: ++ /* Kernel IMR violation: PUnit DMA access */ ++ ret = imr_violate_kernel_punit_dma(); ++ break; ++ default: ++ break; ++ } ++ ++ return ret; ++} ++ ++static int imr_test_open(struct inode *inode, struct file *file) ++{ ++ mutex_lock(&imr_test_mutex); ++ nonseekable_open(inode, file); ++ ++ if (mutex_lock_interruptible(&imr_test_dev.open_lock)) { ++ mutex_unlock(&imr_test_mutex); ++ return -ERESTARTSYS; ++ } ++ ++ if (imr_test_dev.opened) { ++ mutex_unlock(&imr_test_dev.open_lock); ++ mutex_unlock(&imr_test_mutex); ++ return -EINVAL; ++ } ++ ++ imr_test_dev.opened++; ++ mutex_unlock(&imr_test_dev.open_lock); ++ mutex_unlock(&imr_test_mutex); ++ return 0; ++} ++ ++static int imr_test_release(struct inode *inode, struct file *file) ++{ ++ mutex_lock(&imr_test_dev.open_lock); ++ imr_test_dev.opened = 0; ++ mutex_unlock(&imr_test_dev.open_lock); ++ ++ return 0; ++} ++ ++static const struct file_operations imr_test_file_ops = { ++ .open = imr_test_open, ++ .release = imr_test_release, ++ .unlocked_ioctl = imr_test_ioctl, ++ .llseek = no_llseek, ++}; ++ ++/** ++ * intel_cln_imr_test_probe ++ * ++ * @param pdev: Platform device ++ * @return 0 success < 0 failure ++ * ++ * Callback from platform sub-system to probe ++ */ ++static int intel_cln_imr_test_probe(struct platform_device * pdev) ++{ ++ int retval = 0; ++ unsigned int minor = 0; ++ ++ mutex_init(&imr_test_dev.open_lock); ++ cdev_init(&imr_test_dev.cdev, &imr_test_file_ops); ++ imr_test_dev.cdev.owner = THIS_MODULE; ++ ++ retval = cdev_add(&imr_test_dev.cdev, MKDEV(imr_test_major, minor), 1); ++ if (retval) { ++ printk(KERN_ERR "chardev registration failed\n"); ++ return -EINVAL; ++ } ++ if (IS_ERR(device_create(imr_test_class, NULL, ++ MKDEV(imr_test_major, minor), NULL, ++ "imrtest%u", minor))){ ++ dev_err(&pdev->dev, "can't create device\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++ ++} ++ ++static int intel_cln_imr_test_remove(struct platform_device * pdev) ++{ ++ unsigned int minor = MINOR(imr_test_dev.cdev.dev); ++ ++ device_destroy(imr_test_class, MKDEV(imr_test_major, minor)); ++ cdev_del(&imr_test_dev.cdev); ++ ++ class_destroy(imr_test_class); ++ ++ return 0; ++} ++ ++/* ++ * Platform structures useful for interface to PM subsystem ++ */ ++static struct platform_driver intel_cln_imr_test_driver = { ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++ .remove = intel_cln_imr_test_remove, ++}; ++ ++/** ++ * intel_cln_imr_test_init ++ * ++ * Load module. ++ */ ++static int __init intel_cln_imr_test_init(void) ++{ ++ int retval = 0; ++ dev_t dev; ++ ++ imr_test_class = class_create(THIS_MODULE,"cln_imr_test"); ++ if (IS_ERR(imr_test_class)) { ++ retval = PTR_ERR(imr_test_class); ++ printk(KERN_ERR "imr_test: can't register imr_test class\n"); ++ goto err; ++ } ++ ++ retval = alloc_chrdev_region(&dev, 0, 1, "imr_test"); ++ if (retval) { ++ printk(KERN_ERR "earam_test: can't register character device\n"); ++ goto err_class; ++ } ++ imr_test_major = MAJOR(dev); ++ ++ memset(&imr_test_dev, 0x00, sizeof(imr_test_dev)); ++ imr_test_dev.pldev = platform_create_bundle( ++ &intel_cln_imr_test_driver, intel_cln_imr_test_probe, NULL, 0, NULL, 0); ++ ++ if(IS_ERR(imr_test_dev.pldev)){ ++ printk(KERN_ERR "platform_create_bundle fail!\n"); ++ retval = PTR_ERR(imr_test_dev.pldev); ++ goto err_class; ++ } ++ ++ return 0; ++ ++err_class: ++ class_destroy(imr_test_class); ++err: ++ return retval; ++} ++ ++static void __exit intel_cln_imr_test_exit(void) ++{ ++ platform_device_unregister(imr_test_dev.pldev); ++ platform_driver_unregister(&intel_cln_imr_test_driver); ++} ++ ++module_init(intel_cln_imr_test_init); ++module_exit(intel_cln_imr_test_exit); ++ ++MODULE_AUTHOR("Josef Ahmad <josef.ahmad@intel.com>"); ++MODULE_DESCRIPTION("Clanton IMR test module"); ++MODULE_LICENSE("Dual BSD/GPL"); ++ +diff --git a/drivers/platform/x86/quark/intel_cln_layout_data.c b/drivers/platform/x86/quark/intel_cln_layout_data.c +new file mode 100644 +index 0000000..124bccf +--- /dev/null ++++ b/drivers/platform/x86/quark/intel_cln_layout_data.c +@@ -0,0 +1,100 @@ ++/* ++ * Copyright(c) 2013 Intel Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ */ ++/* ++ * Intel Clanton Legacy Platform Data Layout.conf accessor ++ * ++ * Simple Legacy SPI flash access layer ++ * ++ * Author : Bryan O'Donoghue <bryan.odonoghue@linux.intel.com> 2013 ++ */ ++ ++#include <asm/io.h> ++#include <linux/errno.h> ++#include <linux/ioport.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/printk.h> ++ ++#define DRIVER_NAME "cln-layout-conf" ++static char __iomem * layout_conf_data; ++static int len; ++ ++static ssize_t layout_conf_show(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ ssize_t plen = len+1; ++ if( plen > PAGE_SIZE ) ++ plen = PAGE_SIZE; ++ memcpy(buf, layout_conf_data, plen); ++ return plen; ++} ++ ++static struct kobj_attribute layout_conf_attr = ++ __ATTR(layout_conf, 0644, layout_conf_show, NULL); ++ ++static int intel_cln_layout_data_probe(struct platform_device *pdev) ++{ ++ extern struct kobject * board_data_kobj; ++ int ret = 0; ++ ++ layout_conf_data = ioremap(pdev->resource->start, ++ resource_size(pdev->resource)); ++ if (!layout_conf_data) ++ return -ENODEV; ++ ++ len = resource_size(pdev->resource); ++ ret = sysfs_create_file(board_data_kobj, &layout_conf_attr.attr); ++ if (ret != 0){ ++ pr_err("failed to create sysfs entry for layout config\n"); ++ iounmap(layout_conf_data); ++ layout_conf_data = NULL; ++ } ++ ++ return ret; ++} ++ ++static int intel_cln_layout_data_remove(struct platform_device *pdev) ++{ ++ extern struct kobject * board_data_kobj; ++ ++ if (layout_conf_data){ ++ sysfs_remove_file(board_data_kobj, &layout_conf_attr.attr); ++ iounmap(layout_conf_data); ++ ++ } ++ return 0; ++} ++ ++static struct platform_driver cln_layout_data_driver = { ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++ .probe = intel_cln_layout_data_probe, ++ .remove = intel_cln_layout_data_remove, ++}; ++ ++module_platform_driver(cln_layout_data_driver); ++ ++MODULE_AUTHOR("Bryan O'Donoghue <bryan.odonoghue@intel.com>"); ++MODULE_DESCRIPTION("Intel Clanton SPI Data API"); ++MODULE_LICENSE("Dual BSD/GPL"); ++MODULE_ALIAS("platform:"DRIVER_NAME); ++ +diff --git a/drivers/platform/x86/quark/intel_cln_plat_clanton_hill.c b/drivers/platform/x86/quark/intel_cln_plat_clanton_hill.c +new file mode 100644 +index 0000000..90da1c8 +--- /dev/null ++++ b/drivers/platform/x86/quark/intel_cln_plat_clanton_hill.c +@@ -0,0 +1,197 @@ ++/* ++ * Copyright(c) 2013 Intel Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ */ ++/* ++ * Intel Clanton Legacy Platform Data Layout.conf accessor ++ * ++ * Simple Legacy SPI flash access layer ++ * ++ * Author : Bryan O'Donoghue <bryan.odonoghue@linux.intel.com> 2013 ++ */ ++ ++#include <linux/errno.h> ++#include <linux/gpio.h> ++#include <linux/i2c.h> ++#include <linux/io.h> ++#include <linux/ioport.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/printk.h> ++#include <linux/spi/pxa2xx_spi.h> ++#include <linux/spi/spi.h> ++ ++#define DRIVER_NAME "cln-plat-clanton-hill" ++#define GPIO_RESTRICT_NAME "cln-gpio-restrict-nc" ++ ++/****************************************************************************** ++ * Analog Devices AD7298 SPI Device Platform Data ++ ******************************************************************************/ ++#include "linux/platform_data/ad7298.h" ++ ++/* Maximum input voltage allowed for each ADC input, in milliVolts */ ++#define AD7298_MAX_EXT_VIN 5000 ++#define AD7298_MAX_EXT_VIN_EXT_BATT 30000 ++#define AD7298_MAX_EXT_VIN_INT_BATT 9200 ++ ++static const struct ad7298_platform_data ad7298_platform_data = { ++ .ext_ref = false, ++ .ext_vin_max = { AD7298_MAX_EXT_VIN, AD7298_MAX_EXT_VIN, ++ AD7298_MAX_EXT_VIN, AD7298_MAX_EXT_VIN, ++ AD7298_MAX_EXT_VIN, AD7298_MAX_EXT_VIN, ++ AD7298_MAX_EXT_VIN_EXT_BATT, AD7298_MAX_EXT_VIN_INT_BATT } ++}; ++ ++/****************************************************************************** ++ * Intel Clanton SPI Controller Data ++ ******************************************************************************/ ++static struct pxa2xx_spi_chip cln_ffrd_spi_0_cs_0 = { ++ .gpio_cs = 8, ++}; ++ ++static struct spi_board_info spi_onboard_devs[] = { ++ { ++ .modalias = "ad7298", ++ .max_speed_hz = 5000000, ++ .platform_data = &ad7298_platform_data, ++ .mode = SPI_MODE_2, ++ .bus_num = 0, ++ .chip_select = 0, ++ .controller_data = &cln_ffrd_spi_0_cs_0, ++ }, ++}; ++ ++/****************************************************************************** ++ * ST Microelectronics LIS331DLH I2C Device Platform Data ++ ******************************************************************************/ ++#include <linux/platform_data/lis331dlh_intel_cln.h> ++ ++/* GPIO interrupt pins connected to the LIS331DLH */ ++#define ST_ACCEL_INT1_GPIO 15 ++#define ST_ACCEL_INT2_GPIO 4 ++ ++static struct lis331dlh_intel_cln_platform_data lis331dlh_i2c_platform_data = { ++ .irq1_pin = ST_ACCEL_INT1_GPIO, ++}; ++ ++static struct gpio reserved_gpios[] = { ++ { ++ ST_ACCEL_INT1_GPIO, ++ GPIOF_IN, ++ "st_accel_i2c-int1" ++ }, ++ { ++ ST_ACCEL_INT2_GPIO, ++ GPIOF_IN, ++ "st_accel_i2c-int2" ++ }, ++}; ++ ++static struct i2c_board_info i2c_onboard_devs[] = { ++ { ++ I2C_BOARD_INFO("intel-cln-max9867", 0x18), ++ }, ++ { ++ I2C_BOARD_INFO("lis331dlh_cln", 0x19), ++ .platform_data = &lis331dlh_i2c_platform_data, ++ }, ++}; ++ ++/** ++ * intel_cln_spi_add_onboard_devs ++ * ++ * @return 0 on success or standard errnos on failure ++ * ++ * Registers onboard SPI device(s) present on the Clanton Hill platform ++ */ ++static int intel_cln_spi_add_onboard_devs(void) ++{ ++ return spi_register_board_info(spi_onboard_devs, ++ ARRAY_SIZE(spi_onboard_devs)); ++} ++ ++/** ++ * intel_cln_i2c_add_onboard_devs ++ * ++ * @return 0 on success or standard errnos on failure ++ * ++ * Registers onboard I2C device(s) present on the Clanton Hill platform ++ */ ++static int intel_cln_i2c_add_onboard_devs(void) ++{ ++ return i2c_register_board_info(0, i2c_onboard_devs, ++ ARRAY_SIZE(i2c_onboard_devs)); ++} ++ ++ ++/** ++ * intel_cln_gpio_restrict_probe ++ * ++ * Make GPIOs pertaining to Firmware inaccessible by requesting them. The ++ * GPIOs are never released nor accessed by this driver. ++ */ ++static int intel_cln_gpio_restrict_probe(struct platform_device *pdev) ++{ ++ int ret = gpio_request_array(reserved_gpios, ++ ARRAY_SIZE(reserved_gpios)); ++ if (ret == 0) ++ ret = intel_cln_spi_add_onboard_devs(); ++ ++ return ret; ++} ++ ++static struct platform_driver gpio_restrict_pdriver = { ++ .driver = { ++ .name = GPIO_RESTRICT_NAME, ++ .owner = THIS_MODULE, ++ }, ++ .probe = intel_cln_gpio_restrict_probe, ++}; ++ ++static int intel_cln_plat_clanton_hill_probe(struct platform_device *pdev) ++{ ++ int ret = 0; ++ ++ ret = intel_cln_i2c_add_onboard_devs(); ++ if (ret == 0) ++ ret = platform_driver_register(&gpio_restrict_pdriver); ++ ++ return ret; ++} ++ ++static int intel_cln_plat_clanton_hill_remove(struct platform_device *pdev) ++{ ++ return 0; ++} ++ ++static struct platform_driver cln_clanton_hill_driver = { ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++ .probe = intel_cln_plat_clanton_hill_probe, ++ .remove = intel_cln_plat_clanton_hill_remove, ++}; ++ ++module_platform_driver(cln_clanton_hill_driver); ++ ++MODULE_AUTHOR("Bryan O'Donoghue <bryan.odonoghue@intel.com>"); ++MODULE_DESCRIPTION("Clanton Hill BSP Data"); ++MODULE_LICENSE("Dual BSD/GPL"); ++MODULE_ALIAS("platform:"DRIVER_NAME); ++ +diff --git a/drivers/platform/x86/quark/intel_cln_plat_clanton_peak.c b/drivers/platform/x86/quark/intel_cln_plat_clanton_peak.c +new file mode 100644 +index 0000000..27ca0ea +--- /dev/null ++++ b/drivers/platform/x86/quark/intel_cln_plat_clanton_peak.c +@@ -0,0 +1,92 @@ ++/* ++ * Copyright(c) 2013 Intel Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ */ ++/* ++ * Intel Clanton Legacy Platform Data Layout.conf accessor ++ * ++ * Simple Legacy SPI flash access layer ++ * ++ * Author : Bryan O'Donoghue <bryan.odonoghue@linux.intel.com> 2013 ++ */ ++ ++#include <linux/errno.h> ++#include <linux/gpio.h> ++#include <linux/io.h> ++#include <linux/ioport.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/printk.h> ++#include <linux/spi/spi.h> ++ ++#define DRIVER_NAME "cln-plat-clanton-peak" ++ ++static struct spi_board_info spi_onboard_devs[] = { ++ { ++ .modalias = "spidev", ++ .chip_select = 0, ++ .max_speed_hz = 50000000, ++ .bus_num = 0, ++ }, ++ { ++ .modalias = "spidev", ++ .chip_select = 0, ++ .max_speed_hz = 50000000, ++ .bus_num = 1, ++ }, ++}; ++ ++ ++/** ++ * intel_cln_spi_add_onboard_devs ++ * ++ * @return 0 on success or standard errnos on failure ++ * ++ * Registers onboard SPI device(s) present on the Clanton Peak platform ++ */ ++static int intel_cln_spi_add_onboard_devs(void) ++{ ++ return spi_register_board_info(spi_onboard_devs, ++ ARRAY_SIZE(spi_onboard_devs)); ++} ++ ++static int intel_cln_plat_clanton_peak_probe(struct platform_device *pdev) ++{ ++ return intel_cln_spi_add_onboard_devs(); ++} ++ ++static int intel_cln_plat_clanton_peak_remove(struct platform_device *pdev) ++{ ++ return 0; ++} ++ ++static struct platform_driver clanton_peak_driver = { ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++ .probe = intel_cln_plat_clanton_peak_probe, ++ .remove = intel_cln_plat_clanton_peak_remove, ++}; ++ ++module_platform_driver(clanton_peak_driver); ++ ++MODULE_AUTHOR("Bryan O'Donoghue <bryan.odonoghue@intel.com>"); ++MODULE_DESCRIPTION("Clanton Peak BSP Data"); ++MODULE_LICENSE("Dual BSD/GPL"); ++MODULE_ALIAS("platform:"DRIVER_NAME); +diff --git a/drivers/platform/x86/quark/intel_cln_plat_cross_hill.c b/drivers/platform/x86/quark/intel_cln_plat_cross_hill.c +new file mode 100644 +index 0000000..b79ce01 +--- /dev/null ++++ b/drivers/platform/x86/quark/intel_cln_plat_cross_hill.c +@@ -0,0 +1,387 @@ ++/* ++ * Copyright(c) 2013 Intel Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ */ ++/* ++ * Intel Clanton Legacy Platform Data Layout.conf accessor ++ * ++ * Simple Legacy SPI flash access layer ++ * ++ * Author : Bryan O'Donoghue <bryan.odonoghue@linux.intel.com> 2013 ++ */ ++ ++#include <linux/errno.h> ++#include <linux/gpio.h> ++#include <linux/io.h> ++#include <linux/ioport.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/printk.h> ++#include <linux/spi/pxa2xx_spi.h> ++#include <linux/spi/spi.h> ++ ++#define DRIVER_NAME "cln-plat-cross-hill" ++#define GPIO_RESTRICT_NAME_NC "cln-gpio-restrict-nc" ++#define GPIO_RESTRICT_NAME_SC "cln-gpio-restrict-sc" ++ ++/* ++ * GPIO numbers to use for reading 4-bit Blackburn Peak SPI daughterboard ID ++ */ ++#define SPI_BPEAK_RESET_GPIO 4 ++#define SPI_BPEAK_ID0_GPIO 3 ++#define SPI_BPEAK_ID1_GPIO 2 ++#define SPI_BPEAK_ID2_GPIO 15 ++#define SPI_BPEAK_ID3_GPIO 14 ++ ++static int nc_gpio_reg; ++static int sc_gpio_reg; ++ ++static int cross_hill_probe; ++ ++/* ++ * Blackburn Peak SPI daughterboard ID values ++ */ ++enum { ++ CLN_SPI_BPEAK_ID_ZB_TI = 0xA, ++ CLN_SPI_BPEAK_ID_ZB_DIGI, ++ CLN_SPI_BPEAK_ID_ZB_INFR_NXP, ++ CLN_SPI_BPEAK_ID_ZB_EXEGIN_ATMEL, ++ CLN_SPI_BPEAK_ID_ADC_MAXIM, ++ CLN_SPI_BPEAK_ID_NONE ++}; ++ ++ ++/****************************************************************************** ++ * Analog Devices AD7298 SPI Device Platform Data ++ ******************************************************************************/ ++#include "linux/platform_data/ad7298.h" ++ ++/* Maximum input voltage allowed for each ADC input, in milliVolts */ ++#define AD7298_MAX_EXT_VIN 5000 ++ ++static const struct ad7298_platform_data ad7298_platform_data = { ++ .ext_ref = false, ++ .ext_vin_max = { AD7298_MAX_EXT_VIN, AD7298_MAX_EXT_VIN, ++ AD7298_MAX_EXT_VIN, AD7298_MAX_EXT_VIN, ++ AD7298_MAX_EXT_VIN, AD7298_MAX_EXT_VIN, ++ AD7298_MAX_EXT_VIN, AD7298_MAX_EXT_VIN } ++}; ++ ++/****************************************************************************** ++ * Intel Clanton SPI Controller Data ++ ******************************************************************************/ ++static struct pxa2xx_spi_chip cln_ffrd_spi_0_cs_0 = { ++ .gpio_cs = 8, ++}; ++ ++static struct pxa2xx_spi_chip cln_ffrd_spi_1_cs_0 = { ++ .gpio_cs = 10, ++}; ++ ++static struct pxa2xx_spi_chip cln_ffrd_spi_1_cs_1 = { ++ .gpio_cs = 11, ++}; ++ ++static struct spi_board_info spi_generic_devs[] = { ++ { ++ .modalias = "spidev", ++ .max_speed_hz = 50000000, ++ .platform_data = NULL, ++ .mode = SPI_MODE_0, ++ .bus_num = 1, ++ .chip_select = 0, ++ .controller_data = &cln_ffrd_spi_1_cs_0, ++ }, ++ ++ { ++ .modalias = "spidev", ++ .max_speed_hz = 50000000, ++ .platform_data = NULL, ++ .mode = SPI_MODE_0, ++ .bus_num = 1, ++ .chip_select = 1, ++ .controller_data = &cln_ffrd_spi_1_cs_1, ++ }, ++ ++}; ++ ++static struct spi_board_info spi_energy_adc_devs[] = { ++ { ++ .modalias = "max78m6610_lmu", ++ .max_speed_hz = 2000000, ++ .platform_data = NULL, ++ .mode = SPI_MODE_3, ++ .bus_num = 1, ++ .chip_select = 0, ++ .controller_data = &cln_ffrd_spi_1_cs_0, ++ }, ++}; ++ ++ ++ ++/** ++ * intel_cln_spi_add_onboard_devs ++ * ++ * @return 0 on success or standard errnos on failure ++ * ++ * Registers onboard SPI device(s) present on the Cross Hill platform ++ */ ++static int intel_cln_spi_add_onboard_devs(void) ++{ ++ struct spi_board_info spi_onboard_devs[] = { ++ { ++ .modalias = "ad7298", ++ .max_speed_hz = 5000000, ++ .platform_data = &ad7298_platform_data, ++ .mode = SPI_MODE_2, ++ .bus_num = 0, ++ .chip_select = 0, ++ .controller_data = &cln_ffrd_spi_0_cs_0, ++ }, ++ }; ++ ++ return spi_register_board_info(spi_onboard_devs, ++ ARRAY_SIZE(spi_onboard_devs)); ++} ++ ++ ++/** ++ * intel_cln_spi_get_bpeak_id ++ * ++ * @param bpeak_id: The Blackburn Peak SPI ID obtained from the daughterboard ++ * @return 0 on success or standard errnos on failure ++ * ++ * Reads an ID from GPIO-connected pins on Blackburn peak SPI daughterboard ++ */ ++static int intel_cln_spi_get_bpeak_id(u8 *bpeak_id) ++{ ++ int ret = 0; ++ struct gpio spi_bpeak_id_gpios[] = { ++ { ++ SPI_BPEAK_RESET_GPIO, ++ GPIOF_OUT_INIT_HIGH, ++ "spi_bpeak_reset" ++ }, ++ { ++ SPI_BPEAK_ID0_GPIO, ++ GPIOF_IN, ++ "spi_bpeak_id0" ++ }, ++ { ++ SPI_BPEAK_ID1_GPIO, ++ GPIOF_IN, ++ "spi_bpeak_id1" ++ }, ++ { ++ SPI_BPEAK_ID2_GPIO, ++ GPIOF_IN, ++ "spi_bpeak_id2" ++ }, ++ { ++ SPI_BPEAK_ID3_GPIO, ++ GPIOF_IN, ++ "spi_bpeak_id3" ++ } ++ }; ++ ++ /* ++ * Read a 4-bit ID value from ID GPIO inputs, which are only valid ++ * while a RESET GPIO output is asserted (active-low) ++ */ ++ ret = gpio_request_array(spi_bpeak_id_gpios, ++ ARRAY_SIZE(spi_bpeak_id_gpios)); ++ if (ret) { ++ pr_err("%s: Failed to allocate Blackburn Peak ID GPIO pins\n", ++ __func__); ++ return ret; ++ } ++ ++ gpio_set_value(SPI_BPEAK_RESET_GPIO, 0); ++ *bpeak_id = ++ (gpio_get_value(SPI_BPEAK_ID3_GPIO) ? 1 << 3 : 0) | ++ (gpio_get_value(SPI_BPEAK_ID2_GPIO) ? 1 << 2 : 0) | ++ (gpio_get_value(SPI_BPEAK_ID1_GPIO) ? 1 << 1 : 0) | ++ (gpio_get_value(SPI_BPEAK_ID0_GPIO) ? 1 : 0); ++ gpio_set_value(SPI_BPEAK_RESET_GPIO, 1); ++ ++ gpio_free_array(spi_bpeak_id_gpios, ++ ARRAY_SIZE(spi_bpeak_id_gpios)); ++ ++ return 0; ++} ++ ++/** ++ * intel_cln_spi_add_bpeak_devs ++ * ++ * @return 0 on success or standard errnos on failure ++ * ++ * Registers SPI device(s) indicated by the ID value obtained from a ++ * Blackburn Peak SPI daughterboard ++ */ ++static int intel_cln_spi_add_bpeak_devs(void) ++{ ++ u8 spi_bpeak_id = 0; ++ int ret = 0; ++ ++ ret = intel_cln_spi_get_bpeak_id(&spi_bpeak_id); ++ if (ret) { ++ pr_err("%s: failed to obtain Blackburn Peak ID\n", ++ __func__); ++ return ret; ++ } ++ ++ switch (spi_bpeak_id) { ++ ++ case CLN_SPI_BPEAK_ID_NONE: ++ break; ++ ++ case CLN_SPI_BPEAK_ID_ADC_MAXIM: ++ { ++ return spi_register_board_info(spi_energy_adc_devs, ++ ARRAY_SIZE(spi_energy_adc_devs)); ++ } ++ case CLN_SPI_BPEAK_ID_ZB_EXEGIN_ATMEL: ++ { ++ pr_debug("CLN_SPI_BPEAK_ID_ZB_EXEGIN_ATMEL.\n"); ++ return spi_register_board_info(spi_generic_devs, ++ ARRAY_SIZE(spi_generic_devs)); ++ } ++ case CLN_SPI_BPEAK_ID_ZB_DIGI: ++ { ++ pr_debug("CLN_SPI_BPEAK_ID_ZB_DIGI load.\n"); ++ return spi_register_board_info(spi_generic_devs, ++ ARRAY_SIZE(spi_generic_devs)); ++ ++ } ++ default: ++ pr_err("%s: Unsupported Blackburn Peak SPI ID %u\n", ++ __func__, spi_bpeak_id); ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++/** intel_cln_spi_devs_addon ++ * ++ * addon spi device when gpio support in place ++ */ ++static int intel_cln_spi_devs_addon(void) ++{ ++ int ret = 0; ++ ++ if (cross_hill_probe != 1) { ++ ++ ret = intel_cln_spi_add_onboard_devs(); ++ if (ret) ++ return ret; ++ ++ ret = intel_cln_spi_add_bpeak_devs(); ++ ++ cross_hill_probe = 1; ++ } ++ ++ return ret; ++} ++ ++/** ++ * intel_cln_gpio_restrict_probe_nc ++ * ++ * Make GPIOs pertaining to Firmware inaccessible by requesting them. The ++ * GPIOs are never released nor accessed by this driver. ++ */ ++static int intel_cln_gpio_restrict_probe_nc(struct platform_device *pdev) ++{ ++ int ret; ++ nc_gpio_reg = 1; ++ ++ if (nc_gpio_reg == 1 && sc_gpio_reg == 1) { ++ ret = intel_cln_spi_devs_addon(); ++ if (ret) ++ return ret; ++ } ++ return 0; ++} ++ ++/** ++ * intel_cln_gpio_restrict_probe_sc ++ * ++ * Make GPIOs pertaining to Firmware inaccessible by requesting them. The ++ * GPIOs are never released nor accessed by this driver. ++ */ ++static int intel_cln_gpio_restrict_probe_sc(struct platform_device *pdev) ++{ ++ int ret; ++ sc_gpio_reg = 1; ++ ++ if (nc_gpio_reg == 1 && sc_gpio_reg == 1) { ++ ret = intel_cln_spi_devs_addon(); ++ if (ret) ++ return ret; ++ } ++ return 0; ++} ++ ++static struct platform_driver gpio_restrict_pdriver_nc = { ++ .driver = { ++ .name = GPIO_RESTRICT_NAME_NC, ++ .owner = THIS_MODULE, ++ }, ++ .probe = intel_cln_gpio_restrict_probe_nc, ++}; ++ ++static struct platform_driver gpio_restrict_pdriver_sc = { ++ .driver = { ++ .name = GPIO_RESTRICT_NAME_SC, ++ .owner = THIS_MODULE, ++ }, ++ .probe = intel_cln_gpio_restrict_probe_sc, ++}; ++ ++static int intel_cln_plat_cross_hill_probe(struct platform_device *pdev) ++{ ++ int ret = 0; ++ ++ ret = platform_driver_register(&gpio_restrict_pdriver_nc); ++ if (ret) ++ return ret; ++ ++ return platform_driver_register(&gpio_restrict_pdriver_sc); ++} ++ ++static int intel_cln_plat_cross_hill_remove(struct platform_device *pdev) ++{ ++ return 0; ++} ++ ++static struct platform_driver cln_cross_hill_driver = { ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++ .probe = intel_cln_plat_cross_hill_probe, ++ .remove = intel_cln_plat_cross_hill_remove, ++}; ++ ++module_platform_driver(cln_cross_hill_driver); ++ ++MODULE_AUTHOR("Bryan O'Donoghue <bryan.odonoghue@intel.com>"); ++MODULE_DESCRIPTION("Cross Hill BSP Data"); ++MODULE_LICENSE("Dual BSD/GPL"); ++MODULE_ALIAS("platform:"DRIVER_NAME); ++ +diff --git a/drivers/platform/x86/quark/intel_cln_plat_data.c b/drivers/platform/x86/quark/intel_cln_plat_data.c +new file mode 100644 +index 0000000..059fcee +--- /dev/null ++++ b/drivers/platform/x86/quark/intel_cln_plat_data.c +@@ -0,0 +1,455 @@ ++/* ++ * Copyright(c) 2013 Intel Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ */ ++/* ++ * Intel Clanton Legacy Platform Data Layout.conf accessor ++ * ++ * Simple Legacy SPI flash access layer ++ * ++ * Author : Bryan O'Donoghue <bryan.odonoghue@linux.intel.com> 2013 ++ */ ++ ++#include <asm/io.h> ++#include <linux/crc32.h> ++#include <linux/crc32c.h> ++#include <linux/errno.h> ++#include <linux/ioport.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/platform_data/clanton.h> ++#include <linux/printk.h> ++#include <linux/slab.h> ++ ++#define PREFIX "CLN-PLT: " ++#define PLAT_MAGIC 0x54414450 /* PDAT */ ++#define DESC_LEN 0x0A ++#define MAC_STRLEN 20 ++#define MAC_LEN 6 ++ ++struct cln_plat_dat_hdr { ++ uint32_t magic; ++ uint32_t length; ++ uint32_t crc32; ++}; ++ ++struct cln_plat_data { ++ uint16_t plat_id; ++ uint16_t length; ++ uint8_t desc[DESC_LEN]; ++ uint16_t version; ++}; ++ ++struct cln_bsp_reg { ++ struct platform_device pdev; ++ cln_plat_id_t id; ++}; ++ ++static struct cln_bsp_reg bsp_data [] = { ++ { ++ .pdev.name = "cln-plat-clanton-peak", ++ .pdev.id = -1, ++ .id = CLANTON_PEAK, ++ }, ++ { ++ .pdev.name = "cln-plat-kips-bay", ++ .pdev.id = -1, ++ .id = KIPS_BAY, ++ }, ++ { ++ .pdev.name = "cln-plat-cross-hill", ++ .pdev.id = -1, ++ .id = CROSS_HILL, ++ }, ++ { ++ .pdev.name = "cln-plat-clanton-hill", ++ .pdev.id = -1, ++ .id = CLANTON_HILL, ++ }, ++ { ++ .pdev.name = "cln-plat-galileo", ++ .pdev.id = -1, ++ .id = IZMIR, ++ }, ++ ++}; ++ ++/** ++ * struct cln_plat_data_list ++ * ++ * Structure to hold a linked list of platform data refs ++ */ ++struct cln_plat_data_list { ++ char name[DESC_LEN+1]; ++ struct cln_plat_data * plat_data; ++ struct kobj_attribute plat_attr; ++ struct list_head list; ++}; ++ ++static char __iomem * plat_data; ++static char * plat_bin_name = "pdat_bin"; ++static unsigned int plat_bin_size; ++static struct cln_plat_dat_hdr * plat_hdr; ++static struct list_head entry_list; ++ ++/** ++ * intel_cln_plat_sysfs_show_bin ++ * ++ * Generic show routine for any of the sysfs entries of this module ++ */ ++static ssize_t intel_cln_plat_sysfs_show_bin(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ ssize_t plen = plat_bin_size; ++ if( plen > PAGE_SIZE ) ++ plen = PAGE_SIZE; ++ ++ memcpy(buf, plat_data, plen); ++ return plen; ++} ++ ++/** ++ * intel_cln_plat_sysfs_show ++ * ++ * Generic show routine for any of the sysfs entries of this module ++ */ ++static ssize_t intel_cln_plat_sysfs_show(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ unsigned char * data; ++ char fmt[0x20]; ++ struct cln_plat_data_list * plat_item_list; ++ ssize_t plen = 0; ++ ++ list_for_each_entry(plat_item_list, &entry_list, list){ ++ if ( attr == &plat_item_list->plat_attr ){ ++ ++ /* Derive length */ ++ plen = plat_item_list->plat_data->length; ++ if (unlikely(plen > PAGE_SIZE)) ++ plen = PAGE_SIZE; ++ ++ /* Hook data */ ++ data =(char*)(plat_item_list->plat_data); ++ data += +sizeof(struct cln_plat_data); ++ ++ /* Enumrate return */ ++ switch (plat_item_list->plat_data->plat_id){ ++ case PLAT_DATA_ID: ++ case PLAT_DATA_SN: ++ snprintf(fmt, sizeof(fmt), "0x%%0%dx\n", ++ plen*2); ++ return sprintf(buf, fmt, *(int16_t*)data); ++ case PLAT_DATA_MAC0: ++ case PLAT_DATA_MAC1: ++ if (unlikely(plen != MAC_LEN)){ ++ return sprintf(buf, "invalid mac\n"); ++ } ++ return snprintf(buf, MAC_STRLEN, ++ "%02x:%02x:%02x:%02x:%02x:%02x\n", ++ data[0], data[1], data[2], data[3], ++ data[4], data[5]); ++ default: ++ /* Treat as string data */ ++ return snprintf(buf, plen, "%s", data); ++ } ++ } ++ } ++ return 0; ++} ++ ++/** ++ * intel_cln_plat_cleanup ++ * ++ * Generic cleanup code for the platform data interface ++ * ++ */ ++static void intel_cln_plat_cleanup (void) ++{ ++ extern struct kobject * board_data_kobj; ++ struct cln_plat_data_list * plat_item_list; ++ ++ if (plat_data != NULL){ ++ iounmap(plat_data); ++ plat_data = NULL; ++ } ++ ++ list_for_each_entry(plat_item_list, &entry_list, list){ ++ sysfs_remove_file(board_data_kobj, ++ &plat_item_list->plat_attr.attr); ++ kfree(plat_item_list); ++ } ++} ++ ++/** ++ * intel_cln_plat_get_desc_len ++ * ++ * @param desc: Pointer to desc string ++ * @return len on success < 0 failure ++ * ++ * Function called to get a bounds checked desc field from platfrom data ++ * ++ */ ++static int intel_cln_plat_get_desc_len (char * desc) ++{ ++ int len = 0; ++ if (desc == NULL){ ++ return -EINVAL; ++ } ++ ++ for(; *desc != '\0' && len < DESC_LEN; desc++, len++); ++ return len; ++} ++ ++/** ++ * intel_cln_get_id ++ * ++ * @return platform id on success or < CLANTON_PLAT_UNDEFINED on error ++ * ++ * Function called to get platform id ++ * ++ */ ++cln_plat_id_t intel_cln_plat_get_id(void) ++{ ++ unsigned char * data; ++ struct cln_plat_data_list * plat_item_list; ++ ++ if (plat_data == NULL) ++ return CLANTON_PLAT_UNDEFINED; ++ ++ list_for_each_entry(plat_item_list, &entry_list, list){ ++ ++ /* Enumrate return */ ++ if(plat_item_list->plat_data->plat_id == PLAT_DATA_ID){ ++ ++ /* Hook data */ ++ data =(char*)(plat_item_list->plat_data); ++ data += +sizeof(struct cln_plat_data); ++ ++ /* Return payload */ ++ return *(int16_t*)data; ++ } ++ } ++ return CLANTON_PLAT_UNDEFINED; ++} ++EXPORT_SYMBOL(intel_cln_plat_get_id); ++ ++/** ++ * intel_cln_plat_get_mac ++ * ++ * @param id: Index of MAC address to find ++ * @param mac: Output parameter for mac address ++ * ++ * @return 0 success < 0 failure ++ * ++ * Function called to remove the platfrom device from kernel space ++ * ++ */ ++int intel_cln_plat_get_mac(plat_dataid_t id, char * mac) ++{ ++ unsigned char * data; ++ unsigned int plen = 0; ++ struct cln_plat_data_list * plat_item_list; ++ ++ if ((id != PLAT_DATA_MAC0 && id != PLAT_DATA_MAC1) || mac == NULL){ ++ pr_err("invalid input id %d mac %p\n", id, mac); ++ return -EINVAL; ++ } ++ ++ list_for_each_entry(plat_item_list, &entry_list, list){ ++ if(plat_item_list->plat_data->plat_id == id){ ++ ++ /* Derive length */ ++ plen = plat_item_list->plat_data->length; ++ if (unlikely(plen != MAC_LEN)){ ++ pr_err("%s mac len invalid!\n", __func__); ++ return -ENODEV; ++ } ++ ++ /* Hook data */ ++ data =(char*)(plat_item_list->plat_data); ++ data += +sizeof(struct cln_plat_data); ++ ++ /* Good to go */ ++ memcpy(mac, data, MAC_LEN); ++ return 0; ++ } ++ } ++ return -ENODEV; ++} ++EXPORT_SYMBOL(intel_cln_plat_get_mac); ++ ++ ++/** ++ * intel_cln_plat_probe ++ * ++ * @param pdev: Pointer to platform device ++ * @return 0 success < 0 failure ++ * ++ * Function called to probe platform device "cln-plat" ++ * ++ */ ++int intel_cln_plat_probe(struct resource * pres) ++{ ++ char __iomem * end_addr = NULL; ++ char __iomem * data = NULL; ++ cln_plat_id_t id = CLANTON_PLAT_UNDEFINED; ++ extern struct kobject * board_data_kobj; ++ struct cln_plat_data * plat_item = NULL; ++ struct cln_plat_data_list * plat_item_list = NULL; ++ u32 crc = 0; ++ int ret = 0, i = 0; ++ ++ INIT_LIST_HEAD(&entry_list); ++ plat_hdr = ioremap(pres->start, resource_size(pres)); ++ end_addr = (char*)plat_hdr + resource_size(pres); ++ plat_data = (char*)plat_hdr; ++ if (!plat_hdr) ++ return -ENODEV; ++ ++ /* Verify header magic */ ++ if (plat_hdr->magic != PLAT_MAGIC){ ++ pr_err(PREFIX"Expected magic 0x%08x read 0x%08lx\n", ++ PLAT_MAGIC, (unsigned long)plat_hdr->magic); ++ } ++ ++ /* Validate length is sane */ ++ if ((char*)plat_hdr + plat_hdr->length > end_addr || ++ plat_hdr->length < sizeof(struct cln_plat_data)){ ++ pr_err(PREFIX"Invalid length 0x%08lx\n", ++ (unsigned long)plat_hdr->length); ++ return -ENODEV; ++ } ++ ++ /* Point to real end addr */ ++ end_addr = (char*)plat_hdr + ++ sizeof(struct cln_plat_dat_hdr) + plat_hdr->length; ++ plat_bin_size = end_addr - plat_data; ++ ++ /* Get pointer to start of data */ ++ plat_item = (struct cln_plat_data*)(plat_hdr+1); ++ data = ((char*)(plat_item)+sizeof(struct cln_plat_data)); ++ ++ /* Validate CRC32 */ ++ crc = ~crc32(0xFFFFFFFF, plat_item, plat_hdr->length); ++ if (crc != plat_hdr->crc32){ ++ pr_err(PREFIX"CRC 0x%08x header indicates 0x%08x - fatal!\n", ++ crc, plat_hdr->crc32); ++ return -EFAULT; ++ } ++ ++ /* /sys/firmware/board_data/plat_bin - dump entire platform binary */ ++ plat_item_list = kzalloc(sizeof(struct cln_plat_data_list), ++ GFP_KERNEL); ++ if (unlikely(plat_item_list == NULL)) { ++ pr_err("kzalloc fail !\n"); ++ intel_cln_plat_cleanup(); ++ return -ENOMEM; ++ } ++ sysfs_attr_init(&plat_item_list->plat_attr.attr); ++ plat_item_list->plat_attr.attr.name = plat_bin_name; ++ plat_item_list->plat_attr.attr.mode = 0644; ++ plat_item_list->plat_attr.show = intel_cln_plat_sysfs_show_bin; ++ ++ ret = sysfs_create_file(board_data_kobj, ++ &plat_item_list->plat_attr.attr); ++ if (unlikely(ret != 0)){ ++ intel_cln_plat_cleanup(); ++ pr_err("failed to create sysfs entry\n"); ++ return ret; ++ } ++ ++ /* Add to list */ ++ list_add(&plat_item_list->list, &entry_list); ++ ++ /* Iterate through each entry - add sysfs entry as appropriate */ ++ while ( (char*)plat_item < end_addr){ ++ ++ /* Bounds check */ ++ if (data + plat_item->length > end_addr){ ++ pr_err(PREFIX"Data 0x%p over-runs max-addr 0x%p\n", ++ data, end_addr); ++ break; ++ } ++ ++ /* Extract data */ ++ switch(plat_item->plat_id){ ++ case PLAT_DATA_ID: ++ id = *((uint16_t*)data); ++ pr_info(PREFIX"Clanton Platform ID = %d\n", id); ++ break; ++ case PLAT_DATA_SN: ++ case PLAT_DATA_MAC0: ++ case PLAT_DATA_MAC1: ++ break; ++ default: ++ /* Unknown identifier */ ++ break; ++ } ++ ++ plat_item_list = kzalloc(sizeof(struct cln_plat_data_list), ++ GFP_KERNEL); ++ if (unlikely(plat_item_list == NULL)) { ++ pr_err("kzalloc fail !\n"); ++ intel_cln_plat_cleanup(); ++ return -ENOMEM; ++ } ++ ++ /* Get name of entity */ ++ i = intel_cln_plat_get_desc_len(plat_item->desc); ++ if (i <= 0){ ++ pr_err("desc len is %d!\n", i); ++ intel_cln_plat_cleanup(); ++ return i; ++ } ++ ++ memcpy(plat_item_list->name, plat_item->desc, i); ++ plat_item_list->plat_data = plat_item; ++ ++ sysfs_attr_init(&plat_item_list->plat_attr.attr); ++ plat_item_list->plat_attr.attr.name = plat_item_list->name; ++ plat_item_list->plat_attr.attr.mode = 0644; ++ plat_item_list->plat_attr.show = intel_cln_plat_sysfs_show; ++ ++ ret = sysfs_create_file(board_data_kobj, ++ &plat_item_list->plat_attr.attr); ++ if (unlikely(ret != 0)){ ++ intel_cln_plat_cleanup(); ++ pr_err("failed to create sysfs entry\n"); ++ return ret; ++ } ++ ++ /* Add to list */ ++ list_add(&plat_item_list->list, &entry_list); ++ ++ /* Next */ ++ plat_item = (struct cln_plat_data*) ++ (((char*)plat_item) + plat_item->length + sizeof(struct cln_plat_data)); ++ data = ((char*)(plat_item) + sizeof(struct cln_plat_data)); ++ } ++ ++ /* Register BSP enabling platform code */ ++ for (i = 0; i < sizeof(bsp_data)/sizeof(struct cln_bsp_reg); i++){ ++ if (bsp_data[i].id == id){ ++ platform_device_register(&bsp_data[i].pdev); ++ } ++ } ++ ++ return ret; ++} ++EXPORT_SYMBOL(intel_cln_plat_probe); +diff --git a/drivers/platform/x86/quark/intel_cln_plat_galileo.c b/drivers/platform/x86/quark/intel_cln_plat_galileo.c +new file mode 100644 +index 0000000..48ce294 +--- /dev/null ++++ b/drivers/platform/x86/quark/intel_cln_plat_galileo.c +@@ -0,0 +1,264 @@ ++/* ++ * Copyright(c) 2013 Intel Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ */ ++/* ++ * Intel Clanton Legacy Platform Data Layout.conf accessor ++ * ++ * Simple Legacy SPI flash access layer ++ * ++ * Author : Bryan O'Donoghue <bryan.odonoghue@linux.intel.com> 2013 ++ */ ++ ++#include <linux/errno.h> ++#include <linux/gpio.h> ++#include <linux/i2c.h> ++#include <linux/i2c/at24.h> ++#include <linux/io.h> ++#include <linux/ioport.h> ++#include <linux/module.h> ++#include <linux/mtd/partitions.h> ++#include <linux/mtd/physmap.h> ++#include <linux/platform_device.h> ++#include <linux/printk.h> ++#include <linux/spi/pxa2xx_spi.h> ++#include <linux/spi/spi.h> ++#include <linux/spi/flash.h> ++#include <linux/i2c/at24.h> ++ ++#define DRIVER_NAME "cln-plat-galileo" ++#define GPIO_RESTRICT_NAME "cln-gpio-restrict-sc" ++#define LPC_SCH_SPINAME "spi-lpc-sch" ++ ++#define CLN_SPI_MAX_CLK_DEFAULT 5000000 ++ ++/****************************************************************************** ++ * Analog Devices AD7298 SPI Device Platform Data ++ ******************************************************************************/ ++#include "linux/platform_data/ad7298.h" ++ ++/* Maximum input voltage allowed for each ADC input, in milliVolts */ ++#define AD7298_MAX_EXT_VIN 5000 ++ ++static const struct ad7298_platform_data ad7298_platform_data = { ++ .ext_ref = false, ++ .ext_vin_max = { AD7298_MAX_EXT_VIN, AD7298_MAX_EXT_VIN, ++ AD7298_MAX_EXT_VIN, AD7298_MAX_EXT_VIN, ++ AD7298_MAX_EXT_VIN, AD7298_MAX_EXT_VIN, ++ AD7298_MAX_EXT_VIN, AD7298_MAX_EXT_VIN } ++}; ++ ++static struct at24_platform_data at24_platform_data = { ++ .byte_len = (11 * 1024), ++ .page_size = 1, ++ .flags = AT24_FLAG_ADDR16, ++}; ++ ++/****************************************************************************** ++ * Intel Izmir i2c clients ++ ******************************************************************************/ ++static struct i2c_board_info __initdata galileo_i2c_board_info[] = { ++ { ++ /* Note following address may change at driver load time */ ++ I2C_BOARD_INFO("cy8c9540a", 0x20), ++ }, ++ { ++ I2C_BOARD_INFO("at24", 0x50), ++ .platform_data = &at24_platform_data, ++ }, ++}; ++ ++/****************************************************************************** ++ * Intel Clanton SPI Controller Data ++ ******************************************************************************/ ++static struct pxa2xx_spi_chip cln_ffrd_spi_0_cs_0 = { ++ .gpio_cs = 8, ++}; ++ ++static struct pxa2xx_spi_chip cln_ffrd_spi_1_cs_0 = { ++ .gpio_cs = 10, ++}; ++ ++#define LPC_SCH_SPI_BUS_ID 0x03 ++ ++static struct platform_device lpc_sch_spi = { ++ .name = "spi-lpc-sch-drv", ++ .id = LPC_SCH_SPI_BUS_ID, ++}; ++ ++/* TODO: extract this data from layout.conf encoded in flash */ ++struct mtd_partition ilb_partitions [] = { ++ { ++ .name = "grub", ++ .size = 4096, ++ .offset = 0, ++ }, ++ { ++ .name = "grub.conf", ++ .size = 0xA00, ++ .offset = 0x50500, ++ }, ++ { ++ .name = "layout.conf", ++ .size = 4096, ++ .offset = 0x708000, ++ }, ++ { ++ .name = "sketch", ++ .size = 0x40000, ++ .offset = 0x750000, ++ }, ++ { ++ .name = "raw", ++ .size = 8192000, ++ .offset = 0, ++ ++ }, ++}; ++ ++static struct flash_platform_data ilb_flash = { ++ .type = "s25fl064k", ++ .parts = ilb_partitions, ++ .nr_parts = ARRAY_SIZE(ilb_partitions), ++}; ++ ++static struct spi_board_info spi_onboard_devs[] = { ++ { ++ .modalias = "m25p80", ++ .platform_data = &ilb_flash, ++ .bus_num = LPC_SCH_SPI_BUS_ID, ++ .chip_select = 0, ++ }, ++ { ++ .modalias = "ad7298", ++ .max_speed_hz = CLN_SPI_MAX_CLK_DEFAULT, ++ .platform_data = &ad7298_platform_data, ++ .mode = SPI_MODE_2, ++ .bus_num = 0, ++ .chip_select = 0, ++ .controller_data = &cln_ffrd_spi_0_cs_0, ++ }, ++ { ++ .modalias = "spidev", ++ .chip_select = 0, ++ .controller_data = &cln_ffrd_spi_1_cs_0, ++ .max_speed_hz = 50000000, ++ .bus_num = 1, ++ }, ++}; ++ ++/** ++ * intel_cln_spi_add_onboard_devs ++ * ++ * @return 0 on success or standard errnos on failure ++ * ++ * Registers onboard SPI device(s) present on the Izmir platform ++ */ ++static int intel_cln_spi_add_onboard_devs(void) ++{ ++ ++ return spi_register_board_info(spi_onboard_devs, ++ ARRAY_SIZE(spi_onboard_devs)); ++} ++ ++ ++/** ++ * intel_cln_gpio_restrict_probe ++ * ++ * Make GPIOs pertaining to Firmware inaccessible by requesting them. The ++ * GPIOs are never released nor accessed by this driver. ++ */ ++static int intel_cln_gpio_restrict_probe(struct platform_device *pdev) ++{ ++ int ret = 0; ++ ++ ret = intel_cln_spi_add_onboard_devs(); ++ ++ return ret; ++} ++ ++static struct platform_driver gpio_restrict_pdriver = { ++ .driver = { ++ .name = GPIO_RESTRICT_NAME, ++ .owner = THIS_MODULE, ++ }, ++ .probe = intel_cln_gpio_restrict_probe, ++}; ++ ++/* LPC SPI */ ++static int intel_cln_plat_galileo_lpcspi_probe(struct platform_device *pdev) ++{ ++ lpc_sch_spi.resource = pdev->resource; ++ return platform_device_register(&lpc_sch_spi); ++} ++ ++static struct platform_driver intel_cln_plat_galileo_lpcspi_pdriver = { ++ .driver = { ++ .name = LPC_SCH_SPINAME, ++ .owner = THIS_MODULE, ++ }, ++ .probe = intel_cln_plat_galileo_lpcspi_probe, ++}; ++ ++static int intel_cln_plat_galileo_probe(struct platform_device *pdev) ++{ ++ int ret = 0; ++ ++ /* i2c */ ++ ret = i2c_register_board_info(0, galileo_i2c_board_info, ++ ARRAY_SIZE(galileo_i2c_board_info)); ++ if (ret) { ++ goto end; ++ } ++ ++ /* gpio */ ++ ret = platform_driver_register(&gpio_restrict_pdriver); ++ if (ret) ++ goto end; ++ ++#if 0 ++ /* legacy SPI - TBD */ ++ ret = platform_driver_register(&intel_cln_plat_galileo_lpcspi_pdriver); ++ if (ret) ++ goto end; ++#endif ++end: ++ return ret; ++} ++ ++static int intel_cln_plat_galileo_remove(struct platform_device *pdev) ++{ ++ return 0; ++} ++ ++static struct platform_driver cln_galileo_driver = { ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++ .probe = intel_cln_plat_galileo_probe, ++ .remove = intel_cln_plat_galileo_remove, ++}; ++ ++module_platform_driver(cln_galileo_driver); ++ ++MODULE_AUTHOR("Bryan O'Donoghue <bryan.odonoghue@intel.com>"); ++MODULE_DESCRIPTION("Galileo BSP Data"); ++MODULE_LICENSE("Dual BSD/GPL"); ++MODULE_ALIAS("platform:"DRIVER_NAME); ++ +diff --git a/drivers/platform/x86/quark/intel_cln_plat_kips_bay.c b/drivers/platform/x86/quark/intel_cln_plat_kips_bay.c +new file mode 100644 +index 0000000..1f3deb2 +--- /dev/null ++++ b/drivers/platform/x86/quark/intel_cln_plat_kips_bay.c +@@ -0,0 +1,176 @@ ++/* ++ * Copyright(c) 2013 Intel Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ */ ++/* ++ * Intel Clanton Legacy Platform Data Layout.conf accessor ++ * ++ * Simple Legacy SPI flash access layer ++ * ++ * Author : Bryan O'Donoghue <bryan.odonoghue@linux.intel.com> 2013 ++ */ ++ ++#include <linux/errno.h> ++#include <linux/gpio.h> ++#include <linux/io.h> ++#include <linux/ioport.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/printk.h> ++#include <linux/spi/pxa2xx_spi.h> ++#include <linux/spi/spi.h> ++ ++#define DRIVER_NAME "cln-plat-kips-bay" ++#define GPIO_RESTRICT_NAME "cln-gpio-restrict-sc" ++ ++static int gpio_cs = 1; ++ ++module_param(gpio_cs, int, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(gpio_cs, "Enable GPIO chip-select for SPI channel 1"); ++ ++ ++/****************************************************************************** ++ * Analog Devices AD7298 SPI Device Platform Data ++ ******************************************************************************/ ++#include "linux/platform_data/ad7298.h" ++ ++/* Maximum input voltage allowed for each ADC input, in milliVolts */ ++#define AD7298_MAX_EXT_VIN 5000 ++ ++static const struct ad7298_platform_data ad7298_platform_data = { ++ .ext_ref = false, ++ .ext_vin_max = { AD7298_MAX_EXT_VIN, AD7298_MAX_EXT_VIN, ++ AD7298_MAX_EXT_VIN, AD7298_MAX_EXT_VIN, ++ AD7298_MAX_EXT_VIN, AD7298_MAX_EXT_VIN, ++ AD7298_MAX_EXT_VIN, AD7298_MAX_EXT_VIN } ++}; ++ ++/****************************************************************************** ++ * Intel Clanton SPI Controller Data ++ ******************************************************************************/ ++static struct pxa2xx_spi_chip cln_ffrd_spi_0_cs_0 = { ++ .gpio_cs = 8, ++}; ++ ++static struct pxa2xx_spi_chip cln_ffrd_spi_1_cs_0 = { ++ .gpio_cs = 10, ++}; ++ ++static struct spi_board_info spi0_onboard_devs[] = { ++ { ++ .modalias = "ad7298", ++ .max_speed_hz = 5000000, ++ .platform_data = &ad7298_platform_data, ++ .mode = SPI_MODE_2, ++ .bus_num = 0, ++ .chip_select = 0, ++ .controller_data = &cln_ffrd_spi_0_cs_0, ++ } ++}; ++ ++static struct spi_board_info spi1_onboard_devs_gpiocs[] = { ++ { ++ .modalias = "spidev", ++ .chip_select = 0, ++ .controller_data = NULL, ++ .max_speed_hz = 50000000, ++ .bus_num = 1, ++ .controller_data = &cln_ffrd_spi_1_cs_0, ++ }, ++}; ++ ++static struct spi_board_info spi1_onboard_devs[] = { ++ { ++ .modalias = "spidev", ++ .chip_select = 0, ++ .controller_data = NULL, ++ .max_speed_hz = 50000000, ++ .bus_num = 1, ++ }, ++}; ++ ++/** ++ * intel_cln_spi_add_onboard_devs ++ * ++ * @return 0 on success or standard errnos on failure ++ * ++ * Registers onboard SPI device(s) present on the Kips Bay platform ++ */ ++static int intel_cln_spi_add_onboard_devs(void) ++{ ++ int ret = 0; ++ ++ ret = spi_register_board_info(spi0_onboard_devs, ++ ARRAY_SIZE(spi0_onboard_devs)); ++ if (ret) ++ return ret; ++ ++ if (gpio_cs) ++ return spi_register_board_info(spi1_onboard_devs_gpiocs, ++ ARRAY_SIZE(spi1_onboard_devs_gpiocs)); ++ else ++ return spi_register_board_info(spi1_onboard_devs, ++ ARRAY_SIZE(spi1_onboard_devs)); ++} ++ ++ ++/** ++ * intel_cln_gpio_restrict_probe ++ * ++ * Make GPIOs pertaining to Firmware inaccessible by requesting them. The ++ * GPIOs are never released nor accessed by this driver. ++ */ ++static int intel_cln_gpio_restrict_probe(struct platform_device *pdev) ++{ ++ return intel_cln_spi_add_onboard_devs(); ++} ++ ++static struct platform_driver gpio_restrict_pdriver = { ++ .driver = { ++ .name = GPIO_RESTRICT_NAME, ++ .owner = THIS_MODULE, ++ }, ++ .probe = intel_cln_gpio_restrict_probe, ++}; ++ ++static int intel_cln_plat_kips_bay_probe(struct platform_device *pdev) ++{ ++ return platform_driver_register(&gpio_restrict_pdriver); ++} ++ ++static int intel_cln_plat_kips_bay_remove(struct platform_device *pdev) ++{ ++ return 0; ++} ++ ++static struct platform_driver cln_kips_bay_driver = { ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++ .probe = intel_cln_plat_kips_bay_probe, ++ .remove = intel_cln_plat_kips_bay_remove, ++}; ++ ++module_platform_driver(cln_kips_bay_driver); ++ ++MODULE_AUTHOR("Bryan O'Donoghue <bryan.odonoghue@intel.com>"); ++MODULE_DESCRIPTION("Kips Bay BSP Data"); ++MODULE_LICENSE("Dual BSD/GPL"); ++MODULE_ALIAS("platform:"DRIVER_NAME); ++ +diff --git a/drivers/platform/x86/quark/intel_cln_sb.c b/drivers/platform/x86/quark/intel_cln_sb.c +new file mode 100644 +index 0000000..be27d6a +--- /dev/null ++++ b/drivers/platform/x86/quark/intel_cln_sb.c +@@ -0,0 +1,252 @@ ++/* ++ * Copyright(c) 2013 Intel Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ */ ++/* ++ * Intel Clanton side-band driver ++ * ++ * Thread-safe sideband read/write routine. ++ * ++ * Author : Bryan O'Donoghue <bryan.odonoghue@linux.intel.com> 2012 ++ */ ++ ++#include <linux/errno.h> ++#include <linux/intel_cln_sb.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/printk.h> ++#include <linux/spinlock.h> ++#include <linux/pci.h> ++#include "intel_cln_imr.h" ++ ++#define INTEL_CLN_SB_CMD_ADDR (0x000000D0) ++#define INTEL_CLN_SB_DATA_ADDR (0x000000D4) ++ ++#define INTEL_CLN_SB_MCR_SHIFT (24) ++#define INTEL_CLN_SB_PORT_SHIFT (16) ++#define INTEL_CLN_SB_REG_SHIFT (8) ++#define INTEL_CLN_SB_BYTEEN (0xF0) /* enable all 32 bits */ ++ ++/* Simple structure for module */ ++struct intel_cln_sb_dev{ ++ struct pci_dev * pdev; ++ spinlock_t slock; ++ u8 initialized; ++}; ++ ++static struct intel_cln_sb_dev sb_dev = { ++ .initialized = 0 ++}; ++ ++/* Dependant drivers */ ++static struct platform_device pdev [] = { ++ { ++ .name = "intel-cln-esram", ++ }, ++ { ++ .name = "intel-cln-ecc", ++ }, ++ { ++ .name = "intel-cln-thrm", ++ }, ++}; ++ ++/** ++ * intel_cln_sb_read_reg ++ * ++ * @param cln_sb_id: Sideband identifier ++ * @param command: Command to send to destination identifier ++ * @param reg: Target register w/r to cln_sb_id ++ * @return nothing ++ * ++ * Utility function to allow thread-safe read of side-band ++ * command - can be different read op-code types - which is why we don't ++ * hard-code this value directly into msg ++ */ ++void intel_cln_sb_read_reg(cln_sb_id id, u8 cmd, u8 reg, u32 *data, u8 lock) ++{ ++ u32 msg = (cmd << INTEL_CLN_SB_MCR_SHIFT) | ++ ((id << INTEL_CLN_SB_PORT_SHIFT) & 0xFF0000)| ++ ((reg << INTEL_CLN_SB_REG_SHIFT) & 0xFF00)| ++ INTEL_CLN_SB_BYTEEN; ++ ++ if(data == NULL) ++ return; ++ ++ if (likely(lock == 1)) { ++ spin_lock(&sb_dev.slock); ++ } ++ ++ pci_write_config_dword(sb_dev.pdev, INTEL_CLN_SB_CMD_ADDR, msg); ++ pci_read_config_dword(sb_dev.pdev, INTEL_CLN_SB_DATA_ADDR, data); ++ ++ if(likely(lock == 1)){ ++ spin_unlock(&sb_dev.slock); ++ } ++ ++} ++EXPORT_SYMBOL(intel_cln_sb_read_reg); ++ ++/** ++ * intel_cln_sb_write_reg ++ * ++ * @param cln_sb_id: Sideband identifier ++ * @param command: Command to send to destination identifier ++ * @param reg: Target register w/r to cln_sb_id ++ * @return nothing ++ * ++ * Utility function to allow thread-safe write of side-band ++ */ ++void intel_cln_sb_write_reg(cln_sb_id id, u8 cmd, u8 reg, u32 data, u8 lock) ++{ ++ u32 msg = (cmd << INTEL_CLN_SB_MCR_SHIFT) | ++ ((id << INTEL_CLN_SB_PORT_SHIFT) & 0xFF0000)| ++ ((reg << INTEL_CLN_SB_REG_SHIFT) & 0xFF00)| ++ INTEL_CLN_SB_BYTEEN; ++ ++ if(likely(lock == 1)){ ++ spin_lock(&sb_dev.slock); ++ } ++ ++ pci_write_config_dword(sb_dev.pdev, INTEL_CLN_SB_DATA_ADDR, data); ++ pci_write_config_dword(sb_dev.pdev, INTEL_CLN_SB_CMD_ADDR, msg); ++ ++ if(likely(lock == 1)){ ++ spin_unlock(&sb_dev.slock); ++ } ++} ++EXPORT_SYMBOL(intel_cln_sb_write_reg); ++ ++/** ++ * intel_cln_sb_runfn_lock ++ * ++ * @param fn: Callback function - which requires side-band spinlock and !irq ++ * @param arg: Callback argument ++ * @return 0 on success < 0 on failure ++ * ++ * Runs the given function pointer inside of a call to the local spinlock using ++ * spin_lock_irqsave/spin_unlock_irqrestore. Needed for the eSRAMv1 driver to ++ * guarantee atomicity, but, available to any other user of sideband provided ++ * rules are respected. ++ * Rules: ++ * fn may not sleep ++ * fn may not change the state of irqs ++ */ ++int intel_cln_sb_runfn_lock(int (*fn)( void * arg ), void * arg) ++{ ++ unsigned long flags = 0; ++ int ret = 0; ++ ++ if(unlikely(fn == NULL)){ ++ return -EINVAL; ++ } ++ ++ /* Get spinlock with IRQs off */ ++ spin_lock_irqsave(&sb_dev.slock, flags); ++ ++ /* Run function atomically */ ++ ret = fn(arg); ++ ++ /* Release lock */ ++ spin_unlock_irqrestore(&sb_dev.slock, flags); ++ ++ return ret; ++} ++EXPORT_SYMBOL(intel_cln_sb_runfn_lock); ++ ++/** ++ * sb_probe ++ * ++ * @param dev: the PCI device matching ++ * @param id: entry in the match table ++ * @return 0 ++ * ++ * Callback from PCI layer when dev/vendor ids match. ++ * Sets up necessary resources ++ */ ++static int intel_cln_sb_probe(struct pci_dev *dev, const struct pci_device_id *id) ++{ ++ int i = 0; ++ ++ /* Init struct */ ++ memset(&sb_dev, 0x00, sizeof(sb_dev)); ++ ++ /* Hook device */ ++ sb_dev.pdev = dev; ++ ++ /* Init locking structures */ ++ spin_lock_init(&sb_dev.slock); ++ ++ /* Set state */ ++ sb_dev.initialized = 1; ++ ++ /* Register side-band sub-ordinate drivers */ ++ for (i = 0; i < sizeof(pdev)/sizeof(struct platform_device); i++){ ++ /* Register side-band sub-ordinate drivers */ ++ platform_device_register(&pdev[i]); ++ } ++ pr_info("Intel Clanton side-band driver registered\n"); ++ ++ /* Switch off boot-time IMRs nice and early */ ++ return intel_cln_imr_init(); ++} ++ ++/** ++ * sb_remove ++ * ++ * @param pdev: PCI device ++ * @return nothing ++ * ++ * Callback from PCI sub-system upon PCI dev removal ++ */ ++static void intel_cln_sb_remove(struct pci_dev *pdev) ++{ ++} ++ ++/* Clanton hardware */ ++struct pci_device_id intel_cln_sb_ids[] = { ++ { PCI_VDEVICE(INTEL, 0x0958), 0}, ++ { 0 } ++}; ++ ++MODULE_DEVICE_TABLE(pci, intel_cln_sb_ids); ++ ++/* PCI callbacks */ ++static struct pci_driver intel_cln_sb_driver = { ++ .name = "intel_cln_sb", ++ .id_table = intel_cln_sb_ids, ++ .probe = intel_cln_sb_probe, ++ .remove = intel_cln_sb_remove, ++}; ++ ++/** ++ * intel_cln_sb_init ++ * ++ * Module entry point ++ */ ++static int __init intel_cln_sb_init(void) ++{ ++ return pci_register_driver(&intel_cln_sb_driver); ++} ++ ++MODULE_AUTHOR("Bryan O'Donoghue <bryan.odonoghue@linux.intel.com>"); ++MODULE_DESCRIPTION("Intel Clanton SOC side-band driver"); ++MODULE_LICENSE("Dual BSD/GPL"); ++ ++/* Initialise early since other drivers eSRAM, DRAM ECC and thermal depend */ ++subsys_initcall(intel_cln_sb_init); +diff --git a/drivers/platform/x86/quark/intel_cln_smep_test.c b/drivers/platform/x86/quark/intel_cln_smep_test.c +new file mode 100644 +index 0000000..16f43db +--- /dev/null ++++ b/drivers/platform/x86/quark/intel_cln_smep_test.c +@@ -0,0 +1,290 @@ ++/* ++ * Copyright(c) 2013 Intel Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ */ ++/** ++ * intel_cln_smep_test.c ++ * ++ * Simple test module to verify SMEP works as expected on MIA ++ * DO NOT RELEASE THIS FILE OUTSIDE OF CLANTON GROUP ++ * DO NOT ATTEMPT TO UPSTREAM THIS CODE - YOU WILL BE PUBLICLY EMBARRSSED ! ++ * ++ * Author : Bryan O'Donoghue <bryan.odonoghue@intel.com> ++ * ++ */ ++#include <asm/processor.h> ++#include <asm/processor-flags.h> ++#include <linux/cdev.h> ++#include <linux/crc16.h> ++#include <linux/delay.h> ++#include <linux/device.h> ++#include <linux/fs.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/printk.h> ++#include <linux/slab.h> ++ ++#define DRIVER_NAME "intel_cln_smep_test" ++ ++/** ++ * struct intel_cln_smep_dev ++ * ++ * Structre to represent module state/data/etc ++ */ ++struct intel_cln_smep_test_dev{ ++ unsigned int opened; ++ struct platform_device *pldev; /* Platform device */ ++ struct cdev cdev; ++ struct mutex open_lock; ++ char * pdata; ++ u32 size; ++}; ++ ++static struct intel_cln_smep_test_dev smep_test_dev; ++static struct class *smep_test_class; ++static DEFINE_MUTEX(smep_test_mutex); ++static int smep_test_major; ++static char * name = "testmap"; ++ ++/** ++ * smep_test_ioctl ++ * ++ * Allows user-space to command kernel switch SMEP on/off ++ */ ++static long smep_test_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ int cr4 = 0; ++ ++ cr4 = read_cr4(); ++ printk(KERN_INFO "%s entry CR4 is 0x%08x\n", __FUNCTION__, cr4); ++ ++ switch(cmd){ ++ case 0: ++ printk(KERN_INFO "Switching SMEP off\n"); ++ cr4 &= ~X86_CR4_SMEP; ++ ++ break; ++ case 1: ++ printk(KERN_INFO "Switching SMEP on\n"); ++ cr4 |= X86_CR4_SMEP; ++ break; ++ default: ++ return -EINVAL; ++ } ++ /* Latch value */ ++ write_cr4(cr4); ++ ++ /* Print contents of CR4 */ ++ cr4 = read_cr4(); ++ printk(KERN_INFO "%s exit CR4 is 0x%08x\n", __FUNCTION__, cr4); ++ ++ return 0; ++} ++ ++/** ++ * smep_test_write ++ * ++ * Accepts a buffer from user-space and then tries to execute the contents ++ * Be very careful ++ */ ++static ssize_t smep_test_write(struct file *file, const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ /* ++ * We assume we are passed a pointer to function of type ++ * void fn(void) ++ */ ++ void (*fn)(void) = (void(*))buf; ++ if (count) { ++ printk(KERN_INFO "Will attempt exec %d bytes of ring3 code @ 0x%p\n", ++ count, buf); ++ fn(); ++ printk(KERN_INFO "Exec of data @ 0x%p complete\n", buf); ++ } ++ return count; ++} ++ ++static int smep_test_open(struct inode *inode, struct file *file) ++{ ++ mutex_lock(&smep_test_mutex); ++ nonseekable_open(inode, file); ++ ++ if (mutex_lock_interruptible(&smep_test_dev.open_lock)) { ++ mutex_unlock(&smep_test_mutex); ++ return -ERESTARTSYS; ++ } ++ ++ if (smep_test_dev.opened) { ++ mutex_unlock(&smep_test_dev.open_lock); ++ mutex_unlock(&smep_test_mutex); ++ return -EINVAL; ++ } ++ ++ smep_test_dev.opened++; ++ mutex_unlock(&smep_test_dev.open_lock); ++ mutex_unlock(&smep_test_mutex); ++ return 0; ++} ++ ++static int smep_test_release(struct inode *inode, struct file *file) ++{ ++ mutex_lock(&smep_test_dev.open_lock); ++ smep_test_dev.opened = 0; ++ mutex_unlock(&smep_test_dev.open_lock); ++ ++ return 0; ++} ++ ++ ++ ++static const struct file_operations smep_test_file_ops = { ++ .open = smep_test_open, ++ .release = smep_test_release, ++ .unlocked_ioctl = smep_test_ioctl, ++ .write = smep_test_write, ++ .llseek = no_llseek, ++}; ++ ++ ++/** ++ * intel_cln_smep_test_probe ++ * ++ * @param pdev: Platform device ++ * @return 0 success < 0 failure ++ * ++ * Callback from platform sub-system to probe ++ * ++ * This driver manages eSRAM on a per-page basis. Therefore if we find block ++ * mode is enabled, or any global, block-level or page-level locks are in place ++ * at module initialisation time - we bail out. ++ */ ++static int intel_cln_smep_test_probe(struct platform_device * pdev) ++{ ++ int retval = 0; ++ unsigned int minor = 0; ++ ++ mutex_init(&smep_test_dev.open_lock); ++ cdev_init(&smep_test_dev.cdev, &smep_test_file_ops); ++ smep_test_dev.cdev.owner = THIS_MODULE; ++ ++ retval = cdev_add(&smep_test_dev.cdev, MKDEV(smep_test_major, minor), 1); ++ if (retval) { ++ printk(KERN_ERR "chardev registration failed\n"); ++ return -EINVAL; ++ } ++ if (IS_ERR(device_create(smep_test_class, NULL, ++ MKDEV(smep_test_major, minor), NULL, ++ "smeptest%u", minor))){ ++ dev_err(&pdev->dev, "can't create device\n"); ++ return -EINVAL; ++ } ++ printk(KERN_INFO "%s complete OK - device /dev/smeptest%u\n", __FUNCTION__, minor); ++ return 0; ++ ++} ++ ++/** ++ * intel_cln_smep_remove ++ * ++ * @return 0 success < 0 failure ++ * ++ * Removes a platform device ++ */ ++static int intel_cln_smep_test_remove(struct platform_device * pdev) ++{ ++ unsigned int minor = MINOR(smep_test_dev.cdev.dev); ++ ++ device_destroy(smep_test_class, MKDEV(smep_test_major, minor)); ++ cdev_del(&smep_test_dev.cdev); ++ ++ return 0; ++} ++ ++/* ++ * Platform structures useful for interface to PM subsystem ++ */ ++static struct platform_driver intel_cln_smep_test_driver = { ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++ .remove = intel_cln_smep_test_remove, ++}; ++ ++/** ++ * intel_cln_smep_init ++ * ++ * @return 0 success < 0 failure ++ * ++ * Module entry point ++ */ ++static int __init intel_cln_smep_test_init(void) ++{ ++ int retval = 0; ++ dev_t dev; ++ ++ smep_test_class = class_create(THIS_MODULE,"cln_smep_test"); ++ if (IS_ERR(smep_test_class)) { ++ retval = PTR_ERR(smep_test_class); ++ printk(KERN_ERR "smep_test: can't register earam_test class\n"); ++ goto err; ++ } ++ ++ retval = alloc_chrdev_region(&dev, 0, 1, "smep_test"); ++ if (retval) { ++ printk(KERN_ERR "smep_test: can't register character device\n"); ++ goto err_class; ++ } ++ smep_test_major = MAJOR(dev); ++ ++ memset(&smep_test_dev, 0x00, sizeof(smep_test_dev)); ++ smep_test_dev.pldev = platform_create_bundle( ++ &intel_cln_smep_test_driver, intel_cln_smep_test_probe, NULL, 0, NULL, 0); ++ ++ if(IS_ERR(smep_test_dev.pldev)){ ++ printk(KERN_ERR "smep_test platform_create_bundle fail!\n"); ++ retval = PTR_ERR(smep_test_dev.pldev); ++ goto err_class; ++ } ++ ++ return 0; ++ ++err_class: ++ class_destroy(smep_test_class); ++err: ++ return retval; ++} ++ ++/** ++ * intel_cln_smep_exit ++ * ++ * Module exit ++ */ ++static void __exit intel_cln_smep_test_exit(void) ++{ ++ platform_device_unregister(smep_test_dev.pldev); ++ platform_driver_unregister(&intel_cln_smep_test_driver); ++} ++ ++MODULE_AUTHOR("Bryan O'Donoghue <bryan.odonoghue@linux.intel.com>"); ++MODULE_DESCRIPTION("Intel Clanton SMEP test"); ++MODULE_LICENSE("Dual BSD/GPL"); ++ ++module_init(intel_cln_smep_test_init); ++module_exit(intel_cln_smep_test_exit); +diff --git a/drivers/platform/x86/quark/intel_cln_thermal.c b/drivers/platform/x86/quark/intel_cln_thermal.c +new file mode 100644 +index 0000000..ce0da9cd +--- /dev/null ++++ b/drivers/platform/x86/quark/intel_cln_thermal.c +@@ -0,0 +1,360 @@ ++/* ++ * Copyright(c) 2013 Intel Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ */ ++/* ++ * Intel Clanton Thermal driver ++ */ ++#include <linux/err.h> ++#include <linux/fs.h> ++#include <linux/intel_cln_sb.h> ++#include <linux/list.h> ++#include <linux/mm.h> ++#include <linux/module.h> ++#include <linux/printk.h> ++#include <linux/platform_device.h> ++#include <linux/pm.h> ++#include <linux/slab.h> ++#include <linux/spinlock.h> ++#include <linux/thermal.h> ++#include <linux/timer.h> ++ ++#define DRIVER_NAME "intel-cln-thrm" ++ ++/* Definition of register locations for thermal management */ ++#define THRM_CTRL_REG (0x80) /* Thermal control */ ++#define THRM_MODE_REG (0xB0) /* Thermal mode */ ++#define THRM_MODE_SENSOR_EN (0x00008000) /* Thermal mode sensor enable */ ++#define THRM_TEMP_REG (0xB1) /* Thermal sensor temperature */ ++#define THRM_TRPCLR_REG (0xB2) /* Catastropic/Hot trip/clear */ ++#define THRM_AUXTRP_REG (0xB3) /* Aux0-Aux3 trip point */ ++#define THRM_AUXCLR_REG (0xB4) /* Aux0-Aux3 clear trip */ ++#define THRM_STATUS_REG (0xB5) /* Thermal sensor status */ ++#define THRM_TRIPBEHAVE_REG (0xB6) /* Trip point behavior */ ++#define THRM_MSIADDR_REG (0xC5) /* Thermal MSI addres reg */ ++#define THRM_MSIDATA_REG (0xC6) /* Thermal MSI data reg */ ++#define THRM_CTRL_READ (0x10) /* Config reg */ ++#define THRM_CTRL_WRITE (0x11) /* Config reg */ ++ ++#define SOC_TSENSOR_REG (0x34) ++#define SOC_TSENSOR_RST (0x00000001) ++#define SOC_CTRL_READ (0x06) ++#define SOC_CTRL_WRITE (0x07) ++ ++ ++#define THRM_ZONE_COUNT 2 /* Only hot/critical relevant */ ++#define ACTIVE_INTERVAL (1000) ++#define IDLE_INTERVAL (20000) ++#define MCELSIUS(x) ((x) * 1000) ++ ++/* CPU Zone information */ ++#define CATASTROPIC_ZONE 0 ++#define HOT_ZONE 1 ++#define AUX0_ZONE 2 /* Unused */ ++#define AUX1_ZONE 3 /* Unused */ ++#define AUX2_ZONE 4 /* Unused */ ++#define AUX3_ZONE 5 /* Unused */ ++#define MIN_USED_ZONE CATASTROPIC_ZONE ++#define MAX_USED_ZONE HOT_ZONE ++/* ++ * Default catastrophic/hot trip values - in degrees celsius ++ * Maximum temperature is 105 degrees ++ */ ++#define CRIT_TEMP 104 ++#define HOT_TEMP 95 ++#define RAW2CELSIUS_DIFF 50 ++ ++static int driver_enable = 1; ++module_param(driver_enable, int, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(driver_enable, "Disable Thermal Driver Polling"); ++ ++/* Shorten fn names to fit 80 char limit */ ++#ifndef sb_read ++#define sb_read intel_cln_sb_read_reg ++#endif ++#ifndef sb_write ++#define sb_write intel_cln_sb_write_reg ++#endif ++ ++struct intel_cln_therm_zone { ++ enum thermal_trip_type type; ++ int trip_value; ++}; ++ ++/** ++ * struct intel_cln_thermal_dev ++ * ++ */ ++struct intel_cln_thermal_dev { ++ enum thermal_device_mode mode; ++ struct intel_cln_therm_zone tzone[THRM_ZONE_COUNT]; ++ struct mutex lock; ++ struct platform_device *pldev; /* Platform device */ ++ struct thermal_zone_device *therm_dev; /* Thermal device */ ++}; ++ ++static struct intel_cln_thermal_dev cln_tdev; ++ ++/****************************************************************************** ++ * Thermal API implementation ++ ******************************************************************************/ ++ ++/** ++ * get_temp ++ * ++ * @param tz: Thermal zone descriptor ++ * ++ * Get the current temperature ++ * We have exactly one thermal zone/sensor ++ * Value passed is an unsigned long - our sensor reports up to -50 celsius so we ++ * just clip at zero if the temperature is negative. ++ */ ++static int intel_cln_thermal_get_temp(struct thermal_zone_device *tz, ++ unsigned long *temp) ++{ ++ sb_read(SB_ID_THERMAL, THRM_CTRL_READ, THRM_TEMP_REG, (u32 *)temp, 1); ++ *temp -= RAW2CELSIUS_DIFF; ++ ++ /* Clip to unsigned output value if sensor is reporting sub-zero */ ++ if ((int)*temp < 0) ++ *temp = 0; ++ ++ *temp = MCELSIUS(*temp&0x000000FF); ++ ++ return 0; ++} ++ ++/** ++ * get_trend ++ * ++ * Wears good clothes ++ */ ++static int intel_cln_thermal_get_trend(struct thermal_zone_device *tz, ++ int trip, enum thermal_trend *trend) ++{ ++ if (tz->temperature >= trip) ++ *trend = THERMAL_TREND_RAISING; ++ else ++ *trend = THERMAL_TREND_DROPPING; ++ ++ return 0; ++} ++ ++/** ++ * intel_cln_thermal_get_mode ++ * ++ * Get the mode ++ */ ++static int intel_cln_thermal_get_mode(struct thermal_zone_device *tz, ++ enum thermal_device_mode *mode) ++{ ++ mutex_lock(&cln_tdev.lock); ++ *mode = cln_tdev.mode; ++ mutex_unlock(&cln_tdev.lock); ++ ++ return 0; ++} ++ ++/** ++ * intel_cln_thermal_set_mode ++ * ++ * Set the mode ++ */ ++static int intel_cln_thermal_set_mode(struct thermal_zone_device *tz, ++ enum thermal_device_mode mode) ++{ ++ mutex_lock(&cln_tdev.lock); ++ ++ if (mode == THERMAL_DEVICE_ENABLED) ++ cln_tdev.therm_dev->polling_delay = IDLE_INTERVAL; ++ else ++ cln_tdev.therm_dev->polling_delay = 0; ++ cln_tdev.mode = mode; ++ ++ mutex_unlock(&cln_tdev.lock); ++ ++ thermal_zone_device_update(cln_tdev.therm_dev); ++ pr_info("thermal polling set for duration=%d msec\n", ++ cln_tdev.therm_dev->polling_delay); ++ return 0; ++} ++ ++/** ++ * intel_cln_thermal_get_trip_type ++ * ++ * Get trip type ++ */ ++static int intel_cln_thermal_get_trip_type(struct thermal_zone_device *tz, ++ int trip, enum thermal_trip_type *type) ++{ ++ if (trip < MIN_USED_ZONE || trip > MAX_USED_ZONE) ++ return -EINVAL; ++ ++ *type = cln_tdev.tzone[trip].type; ++ return 0; ++} ++ ++/** ++ * intel_cln_thermal_get_trip_temp ++ * ++ * Get trip temp ++ */ ++static int intel_cln_thermal_get_trip_temp(struct thermal_zone_device *tz, ++ int trip, unsigned long *temp) ++{ ++ if (trip < MIN_USED_ZONE || trip > MAX_USED_ZONE) ++ return -EINVAL; ++ ++ /* Convert the temperature into millicelsius */ ++ *temp = cln_tdev.tzone[trip].trip_value; ++ ++ return 0; ++} ++ ++/** ++ * intel_cln_thermal_get_trip_type ++ * ++ * Get trip temp ++ */ ++static int intel_cln_thermal_get_crit_temp(struct thermal_zone_device *tz, ++ unsigned long *temp) ++{ ++ /* Critical zone */ ++ *temp = cln_tdev.tzone[CATASTROPIC_ZONE].trip_value; ++ return 0; ++} ++ ++static struct thermal_zone_device_ops intel_cln_thrm_dev_ops = { ++ .get_temp = intel_cln_thermal_get_temp, ++ .get_trend = intel_cln_thermal_get_trend, ++ .get_mode = intel_cln_thermal_get_mode, ++ .set_mode = intel_cln_thermal_set_mode, ++ .get_trip_type = intel_cln_thermal_get_trip_type, ++ .get_trip_temp = intel_cln_thermal_get_trip_temp, ++ .get_crit_temp = intel_cln_thermal_get_crit_temp, ++}; ++ ++ ++ ++/** ++ * intel_cln_init_zone ++ * ++ * Initialise a zone ++ */ ++static void intel_cln_thermal_init_zone(struct intel_cln_therm_zone *tz, ++ enum thermal_trip_type type, int trip_value) ++{ ++ tz->type = type; ++ tz->trip_value = MCELSIUS(trip_value); ++} ++ ++/****************************************************************************** ++ * Module Entry/Exit hooks ++ ******************************************************************************/ ++ ++/** ++ * intel_cln_thermal_probe ++ * ++ * @param pdev: Platform device ++ * @return 0 success < 0 failure ++ * ++ * Callback from platform sub-system to probe ++ * ++ * This routine registers a thermal device with the kernel's thermal management ++ * sub-system ++ */ ++static int intel_cln_thermal_probe(struct platform_device *pdev) ++{ ++ int err = 0; ++ int critical_temp = 0, hot_temp = 0; ++ uint32_t regval = 0; ++ ++ if (driver_enable == 0) ++ return 0; ++ ++ memset(&cln_tdev, 0x00, sizeof(cln_tdev)); ++ ++ critical_temp = CRIT_TEMP; ++ hot_temp = HOT_TEMP; ++ ++ /* Enumerate zone type data */ ++ memset(&cln_tdev, 0x00, sizeof(cln_tdev)); ++ mutex_init(&cln_tdev.lock); ++ ++ /* Set initial state disabled */ ++ cln_tdev.mode = THERMAL_DEVICE_ENABLED; ++ ++ intel_cln_thermal_init_zone(&cln_tdev.tzone[CATASTROPIC_ZONE], ++ THERMAL_TRIP_CRITICAL, critical_temp); ++ intel_cln_thermal_init_zone(&cln_tdev.tzone[HOT_ZONE], ++ THERMAL_TRIP_HOT, hot_temp); ++ ++ /* Register a thermal zone */ ++ cln_tdev.therm_dev = thermal_zone_device_register(DRIVER_NAME, ++ THRM_ZONE_COUNT, 0, 0, &intel_cln_thrm_dev_ops, ++ 0, IDLE_INTERVAL, ACTIVE_INTERVAL); ++ ++ if (IS_ERR(cln_tdev.therm_dev)) { ++ err = PTR_ERR(cln_tdev.therm_dev); ++ return err; ++ } ++ ++ /* Read the BIOS configured hardware catastrophic trip temp */ ++ sb_read(SB_ID_THERMAL, THRM_CTRL_READ, THRM_TRPCLR_REG, ®val, 1); ++ regval = (regval & 0xff) - 50; ++ ++ pr_info("THRM: critical reset %d c hot %d c hardware failover %d c\n", ++ critical_temp, hot_temp, regval); ++ ++ return 0; ++} ++ ++/** ++ * intel_cln_thermal_remove ++ * ++ * @return 0 success < 0 failure ++ * ++ * Removes a platform device ++ */ ++static int intel_cln_thermal_remove(struct platform_device *pdev) ++{ ++ if (cln_tdev.therm_dev != NULL) { ++ thermal_zone_device_unregister(cln_tdev.therm_dev); ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++/* ++ * Platform structures useful for interface to PM subsystem ++ */ ++static struct platform_driver intel_cln_thermal_driver = { ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++ .probe = intel_cln_thermal_probe, ++ .remove = intel_cln_thermal_remove, ++}; ++ ++module_platform_driver(intel_cln_thermal_driver); ++ ++ ++MODULE_AUTHOR("Bryan O'Donoghue <bryan.odonoghue@linux.intel.com>"); ++MODULE_DESCRIPTION("Intel Clanton Thermal driver"); ++MODULE_LICENSE("Dual BSD/GPL"); +diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c +index 8a7cfb3..5deac43 100644 +--- a/drivers/power/power_supply_core.c ++++ b/drivers/power/power_supply_core.c +@@ -141,7 +141,7 @@ int power_supply_set_battery_charged(struct power_supply *psy) + } + EXPORT_SYMBOL_GPL(power_supply_set_battery_charged); + +-static int power_supply_match_device_by_name(struct device *dev, void *data) ++static int power_supply_match_device_by_name(struct device *dev, const void *data) + { + const char *name = data; + struct power_supply *psy = dev_get_drvdata(dev); +@@ -149,7 +149,7 @@ static int power_supply_match_device_by_name(struct device *dev, void *data) + return strcmp(psy->name, name) == 0; + } + +-struct power_supply *power_supply_get_by_name(char *name) ++struct power_supply *power_supply_get_by_name(const char *name) + { + struct device *dev = class_find_device(power_supply_class, NULL, name, + power_supply_match_device_by_name); +diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig +index e513cd9..03120a8 100644 +--- a/drivers/pwm/Kconfig ++++ b/drivers/pwm/Kconfig +@@ -28,6 +28,10 @@ menuconfig PWM + + if PWM + ++config PWM_SYSFS ++ bool ++ default y if SYSFS ++ + config PWM_AB8500 + tristate "AB8500 PWM support" + depends on AB8500_CORE && ARCH_U8500 +diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile +index 62a2963..f98371b 100644 +--- a/drivers/pwm/Makefile ++++ b/drivers/pwm/Makefile +@@ -1,4 +1,5 @@ + obj-$(CONFIG_PWM) += core.o ++obj-$(CONFIG_PWM_SYSFS) += sysfs.o + obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o + obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o + obj-$(CONFIG_PWM_IMX) += pwm-imx.o +diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c +index 903138b..bfc8c05 100644 +--- a/drivers/pwm/core.c ++++ b/drivers/pwm/core.c +@@ -272,6 +272,8 @@ int pwmchip_add(struct pwm_chip *chip) + if (IS_ENABLED(CONFIG_OF)) + of_pwmchip_add(chip); + ++ pwmchip_sysfs_export(chip); ++ + out: + mutex_unlock(&pwm_lock); + return ret; +@@ -308,6 +310,8 @@ int pwmchip_remove(struct pwm_chip *chip) + + free_pwms(chip); + ++ pwmchip_sysfs_unexport(chip); ++ + out: + mutex_unlock(&pwm_lock); + return ret; +@@ -400,10 +404,19 @@ EXPORT_SYMBOL_GPL(pwm_free); |