summarylogtreecommitdiffstats
diff options
context:
space:
mode:
authorKaczanowski Mateusz2020-01-08 14:26:55 +0100
committerKaczanowski Mateusz2020-01-08 14:29:17 +0100
commit68ecd2668c735031ed3bdfbcb749fb131f459dd2 (patch)
treec53ff32ab986b8d9c4c18e8dfbba13d27318faa2
downloadaur-68ecd2668c735031ed3bdfbcb749fb131f459dd2.tar.gz
initial commit
-rw-r--r--.SRCINFO44
-rw-r--r--0001-kernel-add-epiphany-kconfig.patch27
-rw-r--r--0002-kernel-add-epiphany-makefile.patch9
-rw-r--r--60-linux.hook12
-rw-r--r--90-linux.hook11
-rw-r--r--PKGBUILD188
-rw-r--r--config508
-rw-r--r--epiphany.c2896
-rw-r--r--epiphany.h131
-rw-r--r--epiphany_uapi.h115
-rw-r--r--linux-parallella.install3
-rw-r--r--linux.preset10
12 files changed, 3954 insertions, 0 deletions
diff --git a/.SRCINFO b/.SRCINFO
new file mode 100644
index 000000000000..43fb23d04095
--- /dev/null
+++ b/.SRCINFO
@@ -0,0 +1,44 @@
+pkgbase = linux-parallella
+ pkgver = 5.4.8
+ pkgrel = 1
+ url = http://www.kernel.org/
+ arch = armv7h
+ license = GPL2
+ makedepends = xmlto
+ makedepends = docbook-xsl
+ makedepends = kmod
+ makedepends = inetutils
+ makedepends = bc
+ makedepends = uboot-tools
+ makedepends = git
+ makedepends = dtc
+ options = !strip
+ source = http://www.kernel.org/pub/linux/kernel/v5.x/linux-5.4.tar.xz
+ source = http://www.kernel.org/pub/linux/kernel/v5.x/patch-5.4.8.xz
+ source = 0001-kernel-add-epiphany-kconfig.patch
+ source = 0002-kernel-add-epiphany-makefile.patch
+ source = epiphany.c
+ source = epiphany.h
+ source = epiphany_uapi.h
+ source = linux.preset
+ source = 60-linux.hook
+ source = 90-linux.hook
+ source = linux-parallella.install
+ source = config
+ sha256sums = bf338980b1670bca287f9994b7441c2361907635879169c64ae78364efc5f491
+ sha256sums = 5daea86d29246b5a8e193c097756bc833b33dd6fa6419f9cb52bcbf16a192a1b
+ sha256sums = 51a02268729e38a9566f24e7087301030cdb199ca8582f3360d83fda47d51567
+ sha256sums = 0df557a36cce828eb0dec398132fee5f5380aae409ca8b6029d1f110df0c9970
+ sha256sums = 17f7a780c879e3b1f37eee907111160bb8d228ceefa295f1439c2a9f97b06f36
+ sha256sums = 9e6cd80a98f25ec98582e2cd9352462898eae3c8422b1a65416056e08f37b1f1
+ sha256sums = efffd95b20a9a2099a9b32c5dbbef21c2e9bc27e70a238d7149d7340dd1d1c38
+ sha256sums = 66644820faa950a5fc59181f5aefcbed6d7ed652b29aee69979a2be2a032025d
+ sha256sums = ae2e95db94ef7176207c690224169594d49445e04249d2499e9d2fbc117a0b21
+ sha256sums = c55b4d592fae40af69977ada77deaa4b9df205f4906d367cf66521fa8f0ace9d
+ sha256sums = 0f6be7acb2b00866c96db4b37f10191843c06b02f6b53e9b9007529df1b55a04
+ sha256sums = b52136f49ef1c0c2ea02f1d35d0ecbc271a384abac99833c4d82c105499e9712
+
+pkgname = linux-parallella
+
+pkgname = linux-parallella-headers
+
diff --git a/0001-kernel-add-epiphany-kconfig.patch b/0001-kernel-add-epiphany-kconfig.patch
new file mode 100644
index 000000000000..35878c4cf3e1
--- /dev/null
+++ b/0001-kernel-add-epiphany-kconfig.patch
@@ -0,0 +1,27 @@
+diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
+index 7f0d48f406e3..5a49515dc0c7 100644
+--- a/drivers/misc/Kconfig
++++ b/drivers/misc/Kconfig
+@@ -453,6 +453,22 @@ config XILINX_SDFEC
+
+ If unsure, say N.
+
++config EPIPHANY
++ tristate "Adapteva Epiphany device driver"
++ depends on OF
++ default n
++
++ help
++ The epiphany device driver gives user space applications access to
++ the eMesh on-chip-network via an e-link interface connected on the
++ system bus. The driver is meant to be used in conjunction with
++ a user space API.
++
++ To compile this driver as a module, choose M here: the
++ module will be called epiphany.
++
++ If unsure, say N.
++
+ config MISC_RTSX
+ tristate
+ default MISC_RTSX_PCI || MISC_RTSX_USB
diff --git a/0002-kernel-add-epiphany-makefile.patch b/0002-kernel-add-epiphany-makefile.patch
new file mode 100644
index 000000000000..6fff00d33589
--- /dev/null
+++ b/0002-kernel-add-epiphany-makefile.patch
@@ -0,0 +1,9 @@
+diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
+index c1860d35dc7e..216032737c7b 100644
+--- a/drivers/misc/Makefile
++++ b/drivers/misc/Makefile
+@@ -57,3 +57,4 @@ obj-y += cardreader/
+ obj-$(CONFIG_PVPANIC) += pvpanic.o
+ obj-$(CONFIG_HABANA_AI) += habanalabs/
+ obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o
++obj-$(CONFIG_EPIPHANY) += epiphany.o
diff --git a/60-linux.hook b/60-linux.hook
new file mode 100644
index 000000000000..b33873c854fb
--- /dev/null
+++ b/60-linux.hook
@@ -0,0 +1,12 @@
+[Trigger]
+Type = File
+Operation = Install
+Operation = Upgrade
+Operation = Remove
+Target = usr/lib/modules/%KERNVER%/*
+Target = usr/lib/modules/%EXTRAMODULES%/*
+
+[Action]
+Description = Updating %PKGBASE% module dependencies...
+When = PostTransaction
+Exec = /usr/bin/depmod %KERNVER%
diff --git a/90-linux.hook b/90-linux.hook
new file mode 100644
index 000000000000..da27380dcf27
--- /dev/null
+++ b/90-linux.hook
@@ -0,0 +1,11 @@
+[Trigger]
+Type = File
+Operation = Install
+Operation = Upgrade
+Target = boot/uImage
+Target = usr/lib/initcpio/*
+
+[Action]
+Description = Updating %PKGBASE% initcpios...
+When = PostTransaction
+Exec = /usr/bin/mkinitcpio -p %PKGBASE%
diff --git a/PKGBUILD b/PKGBUILD
new file mode 100644
index 000000000000..c4413e6b33f4
--- /dev/null
+++ b/PKGBUILD
@@ -0,0 +1,188 @@
+# Maintainer: Mateusz Kaczanowski <kaczanowski.mateusz@gmail.com>
+
+pkgbase=linux-parallella
+_srcname=linux-5.4
+_kernelname=${pkgbase#linux}
+_desc="Parallella"
+pkgver=5.4.8
+pkgrel=1
+arch=('armv7h')
+url="http://www.kernel.org/"
+license=('GPL2')
+makedepends=('xmlto' 'docbook-xsl' 'kmod' 'inetutils' 'bc' 'uboot-tools' 'git' 'dtc')
+options=('!strip')
+source=("http://www.kernel.org/pub/linux/kernel/v5.x/${_srcname}.tar.xz"
+ "http://www.kernel.org/pub/linux/kernel/v5.x/patch-${pkgver}.xz"
+ '0001-kernel-add-epiphany-kconfig.patch'
+ '0002-kernel-add-epiphany-makefile.patch'
+ 'epiphany.c'
+ 'epiphany.h'
+ 'epiphany_uapi.h'
+ 'linux.preset'
+ '60-linux.hook'
+ '90-linux.hook'
+ 'linux-parallella.install'
+ 'config')
+sha256sums=('bf338980b1670bca287f9994b7441c2361907635879169c64ae78364efc5f491'
+ '5daea86d29246b5a8e193c097756bc833b33dd6fa6419f9cb52bcbf16a192a1b'
+ '51a02268729e38a9566f24e7087301030cdb199ca8582f3360d83fda47d51567'
+ '0df557a36cce828eb0dec398132fee5f5380aae409ca8b6029d1f110df0c9970'
+ '17f7a780c879e3b1f37eee907111160bb8d228ceefa295f1439c2a9f97b06f36'
+ '9e6cd80a98f25ec98582e2cd9352462898eae3c8422b1a65416056e08f37b1f1'
+ 'efffd95b20a9a2099a9b32c5dbbef21c2e9bc27e70a238d7149d7340dd1d1c38'
+ '66644820faa950a5fc59181f5aefcbed6d7ed652b29aee69979a2be2a032025d'
+ 'ae2e95db94ef7176207c690224169594d49445e04249d2499e9d2fbc117a0b21'
+ 'c55b4d592fae40af69977ada77deaa4b9df205f4906d367cf66521fa8f0ace9d'
+ '0f6be7acb2b00866c96db4b37f10191843c06b02f6b53e9b9007529df1b55a04'
+ 'b52136f49ef1c0c2ea02f1d35d0ecbc271a384abac99833c4d82c105499e9712')
+
+prepare() {
+ cd "${srcdir}/${_srcname}"
+
+ # add upstream patch
+ git apply --whitespace=nowarn ../patch-${pkgver}
+
+ # add epiphany driver
+ git apply ../0001-kernel-add-epiphany-kconfig.patch
+ git apply ../0002-kernel-add-epiphany-makefile.patch
+
+ cat "${srcdir}/config" > ./.config
+ cat "${srcdir}/epiphany.c" > ./drivers/misc/epiphany.c
+ cat "${srcdir}/epiphany.h" > ./drivers/misc/epiphany.h
+ cat "${srcdir}/epiphany_uapi.h" > ./include/uapi/misc/epiphany.h
+
+ # add pkgrel to extraversion
+ sed -ri "s|^(EXTRAVERSION =)(.*)|\1 -${pkgrel}|" Makefile
+
+ # don't run depmod on 'make install'. We'll do this ourselves in packaging
+ sed -i '2iexit 0' scripts/depmod.sh
+}
+
+build() {
+ cd "${srcdir}/${_srcname}"
+
+ # get kernel version
+ yes "" | make ${MAKEFLAGS} prepare
+
+ # build
+ make ${MAKEFLAGS} UIMAGE_LOADADDR=0x8000 uImage modules dtbs
+}
+
+_package() {
+ pkgdesc="The Linux Kernel and modules - ${_desc}"
+ depends=('coreutils' 'linux-firmware' 'kmod' 'mkinitcpio>=0.7')
+ optdepends=('crda: to set the correct wireless channels of your country')
+ provides=('kernel26' "linux=${pkgver}")
+ conflicts=('linux')
+ install=${pkgname}.install
+
+ cd "${srcdir}/${_srcname}"
+
+ KARCH=arm
+
+ # get kernel version
+ _kernver="$(make kernelrelease)"
+ _basekernel=${_kernver%%-*}
+ _basekernel=${_basekernel%.*}
+
+ mkdir -p "${pkgdir}"/{boot,usr/lib/modules}
+ make INSTALL_MOD_PATH="${pkgdir}/usr" modules_install
+ make INSTALL_DTBS_PATH="${pkgdir}/boot/dtbs" dtbs_install
+ cp arch/$KARCH/boot/uImage "${pkgdir}/boot/uImage"
+
+ # make room for external modules
+ local _extramodules="extramodules-${_basekernel}${_kernelname}"
+ ln -s "../${_extramodules}" "${pkgdir}/usr/lib/modules/${_kernver}/extramodules"
+
+ # add real version for building modules and running depmod from hook
+ echo "${_kernver}" |
+ install -Dm644 /dev/stdin "${pkgdir}/usr/lib/modules/${_extramodules}/version"
+
+ # remove build and source links
+ rm "${pkgdir}"/usr/lib/modules/${_kernver}/{source,build}
+
+ # now we call depmod...
+ depmod -b "${pkgdir}/usr" -F System.map "${_kernver}"
+
+ # sed expression for following substitutions
+ local _subst="
+ s|%PKGBASE%|${pkgbase}|g
+ s|%KERNVER%|${_kernver}|g
+ s|%EXTRAMODULES%|${_extramodules}|g
+ "
+
+ # install mkinitcpio preset file
+ sed "${_subst}" ../linux.preset |
+ install -Dm644 /dev/stdin "${pkgdir}/etc/mkinitcpio.d/${pkgbase}.preset"
+
+ # install pacman hooks
+ sed "${_subst}" ../60-linux.hook |
+ install -Dm644 /dev/stdin "${pkgdir}/usr/share/libalpm/hooks/60-${pkgbase}.hook"
+ sed "${_subst}" ../90-linux.hook |
+ install -Dm644 /dev/stdin "${pkgdir}/usr/share/libalpm/hooks/90-${pkgbase}.hook"
+}
+
+_package-headers() {
+ pkgdesc="Header files and scripts for building modules for linux kernel - ${_desc}"
+ provides=("linux-headers=${pkgver}")
+ conflicts=('linux-headers')
+
+ cd "${srcdir}/${_srcname}"
+ local _builddir="${pkgdir}/usr/lib/modules/${_kernver}/build"
+
+ install -Dt "${_builddir}" -m644 Makefile .config Module.symvers
+ install -Dt "${_builddir}/kernel" -m644 kernel/Makefile
+
+ mkdir "${_builddir}/.tmp_versions"
+
+ cp -t "${_builddir}" -a include scripts
+
+ install -Dt "${_builddir}/arch/${KARCH}" -m644 arch/${KARCH}/Makefile
+ install -Dt "${_builddir}/arch/${KARCH}/kernel" -m644 arch/${KARCH}/kernel/asm-offsets.s arch/$KARCH/kernel/module.lds
+
+ cp -t "${_builddir}/arch/${KARCH}" -a arch/${KARCH}/include
+
+ install -Dt "${_builddir}/drivers/md" -m644 drivers/md/*.h
+ install -Dt "${_builddir}/net/mac80211" -m644 net/mac80211/*.h
+
+ # add xfs and shmem for aufs building
+ mkdir -p "${_builddir}"/{fs/xfs,mm}
+
+ # copy in Kconfig files
+ find . -name Kconfig\* -exec install -Dm644 {} "${_builddir}/{}" \;
+
+ # remove unneeded architectures
+ local _arch
+ for _arch in "${_builddir}"/arch/*/; do
+ [[ ${_arch} == */${KARCH}/ ]] && continue
+ rm -r "${_arch}"
+ done
+
+ # remove files already in linux-docs package
+ rm -r "${_builddir}/Documentation"
+
+ # remove now broken symlinks
+ find -L "${_builddir}" -type l -printf 'Removing %P\n' -delete
+
+ # Fix permissions
+ chmod -R u=rwX,go=rX "${_builddir}"
+
+ # strip scripts directory
+ local _binary _strip
+ while read -rd '' _binary; do
+ case "$(file -bi "${_binary}")" in
+ *application/x-sharedlib*) _strip="${STRIP_SHARED}" ;; # Libraries (.so)
+ *application/x-archive*) _strip="${STRIP_STATIC}" ;; # Libraries (.a)
+ *application/x-executable*) _strip="${STRIP_BINARIES}" ;; # Binaries
+ *) continue ;;
+ esac
+ /usr/bin/strip ${_strip} "${_binary}"
+ done < <(find "${_builddir}/scripts" -type f -perm -u+w -print0 2>/dev/null)
+}
+
+pkgname=("${pkgbase}" "${pkgbase}-headers")
+for _p in ${pkgname[@]}; do
+ eval "package_${_p}() {
+ _package${_p#${pkgbase}}
+ }"
+done
diff --git a/config b/config
new file mode 100644
index 000000000000..53265431446b
--- /dev/null
+++ b/config
@@ -0,0 +1,508 @@
+# Begin zynq_xcomm_adv7511_defconfig
+CONFIG_SYSVIPC=y
+CONFIG_USELIB=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=15
+CONFIG_CGROUPS=y
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_RD_BZIP2 is not set
+# CONFIG_RD_LZMA is not set
+# CONFIG_RD_XZ is not set
+# CONFIG_RD_LZO is not set
+# CONFIG_RD_LZ4 is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_EMBEDDED=y
+CONFIG_PERF_EVENTS=y
+CONFIG_SLAB=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+# CONFIG_BLK_DEV_BSG is not set
+CONFIG_ARCH_ZYNQ=y
+CONFIG_PL310_ERRATA_588369=y
+CONFIG_PL310_ERRATA_727915=y
+CONFIG_PL310_ERRATA_769419=y
+# CONFIG_DEBUG_RODATA is not set
+# CONFIG_ARM_ERRATA_643719 is not set
+CONFIG_ARM_ERRATA_754322=y
+CONFIG_ARM_ERRATA_754327=y
+CONFIG_ARM_ERRATA_764369=y
+CONFIG_ARM_ERRATA_775420=y
+CONFIG_SMP=y
+CONFIG_SCHED_MC=y
+CONFIG_SCHED_SMT=y
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+CONFIG_HIGHMEM=y
+# CONFIG_HIGHPTE is not set
+# CONFIG_COMPACTION is not set
+CONFIG_CMA=y
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="console=ttyPS0,115200n8 root=/dev/ram rw initrd=0x00800000,16M earlyprintk mtdparts=physmap-flash.0:512K(nor-fsbl),512K(nor-u-boot),5M(nor-linux),9M(nor-user),1M(nor-scratch),-(nor-rootfs)"
+CONFIG_CPU_IDLE=y
+CONFIG_CPU_IDLE_GOV_MENU=y
+CONFIG_ARM_ZYNQ_CPUIDLE=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+CONFIG_NET_IPIP=m
+# CONFIG_IPV6 is not set
+CONFIG_VLAN_8021Q=m
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_EXTRA_FIRMWARE=""
+CONFIG_DMA_CMA=y
+CONFIG_CMA_SIZE_MBYTES=128
+CONFIG_MWIPCORE=y
+CONFIG_CONNECTOR=y
+CONFIG_MTD=y
+CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_CFI=y
+CONFIG_MTD_CFI_AMDSTD=y
+CONFIG_MTD_PHYSMAP=y
+CONFIG_MTD_PHYSMAP_OF=y
+CONFIG_MTD_M25P80=y
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_PL35X=y
+CONFIG_MTD_SPI_NOR=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=16384
+CONFIG_AD525X_DPOT=y
+CONFIG_AD525X_DPOT_SPI=y
+CONFIG_SRAM=y
+CONFIG_EEPROM_AT24=y
+CONFIG_EEPROM_AT25=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_NETDEVICES=y
+# CONFIG_NET_VENDOR_BROADCOM is not set
+# CONFIG_NET_VENDOR_CIRRUS is not set
+# CONFIG_NET_VENDOR_FARADAY is not set
+# CONFIG_NET_VENDOR_INTEL is not set
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_MICROCHIP is not set
+# CONFIG_NET_VENDOR_NATSEMI is not set
+# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SMSC is not set
+# CONFIG_NET_VENDOR_STMICRO is not set
+# CONFIG_NET_VENDOR_VIA is not set
+# CONFIG_NET_VENDOR_WIZNET is not set
+CONFIG_XILINX_PS_EMAC=y
+CONFIG_MARVELL_PHY=y
+CONFIG_MDIO_BITBANG=y
+# CONFIG_WLAN is not set
+CONFIG_INPUT_SPARSEKMAP=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_KEYBOARD_GPIO=y
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_XILINX_PS_UART=y
+CONFIG_SERIAL_XILINX_PS_UART_CONSOLE=y
+# CONFIG_HW_RANDOM is not set
+CONFIG_XILINX_DEVCFG=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_MUX_PCA954x=y
+CONFIG_I2C_CADENCE=y
+CONFIG_I2C_GPIO=y
+CONFIG_I2C_XILINX=y
+CONFIG_SPI=y
+CONFIG_SPI_AXI_SPI_ENGINE=y
+CONFIG_SPI_CADENCE=y
+CONFIG_SPI_XCOMM=y
+CONFIG_SPI_AD9250FMC=y
+CONFIG_SPI_XILINX=y
+CONFIG_SPI_ZYNQ_QSPI=y
+CONFIG_SPI_SPIDEV=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_ZYNQ=y
+CONFIG_GPIO_PCA953X=y
+CONFIG_PMBUS=y
+CONFIG_SENSORS_UCD9000=y
+CONFIG_SENSORS_UCD9200=y
+CONFIG_THERMAL=y
+CONFIG_WATCHDOG=y
+CONFIG_XILINX_WATCHDOG=y
+CONFIG_CADENCE_WATCHDOG=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_MEDIA_CAMERA_SUPPORT=y
+CONFIG_MEDIA_CONTROLLER=y
+CONFIG_VIDEO_V4L2_SUBDEV_API=y
+CONFIG_VIDEO_ADV_DEBUG=y
+CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_VIDEO_AXI_HDMI_RX=y
+CONFIG_VIDEO_IMAGEON_BRIDGE=y
+CONFIG_DRM=y
+CONFIG_DRM_I2C_ADV7511=y
+CONFIG_DRM_ADI_AXI_HDMI=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_LOGO=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+# CONFIG_SND_SUPPORT_OLD_API is not set
+# CONFIG_SND_VERBOSE_PROCFS is not set
+# CONFIG_SND_DRIVERS is not set
+# CONFIG_SND_ARM is not set
+# CONFIG_SND_SPI is not set
+# CONFIG_SND_USB is not set
+CONFIG_SND_SOC=y
+CONFIG_SND_SOC_ADI=y
+CONFIG_SND_SOC_ADV7511_HDMI=y
+CONFIG_SND_SOC_ZED_ADAU1761=y
+CONFIG_SND_SOC_ADAU1701=y
+CONFIG_SND_SIMPLE_CARD=y
+CONFIG_HIDRAW=y
+CONFIG_HID_A4TECH=y
+CONFIG_HID_ACRUX=y
+CONFIG_HID_ACRUX_FF=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_BELKIN=y
+CONFIG_HID_CHERRY=y
+CONFIG_HID_CHICONY=y
+CONFIG_HID_PRODIKEYS=y
+CONFIG_HID_CYPRESS=y
+CONFIG_HID_DRAGONRISE=y
+CONFIG_HID_EMS_FF=y
+CONFIG_HID_EZKEY=y
+CONFIG_HID_HOLTEK=y
+CONFIG_HOLTEK_FF=y
+CONFIG_HID_KEYTOUCH=y
+CONFIG_HID_KYE=y
+CONFIG_HID_UCLOGIC=y
+CONFIG_HID_WALTOP=y
+CONFIG_HID_GYRATION=y
+CONFIG_HID_TWINHAN=y
+CONFIG_HID_KENSINGTON=y
+CONFIG_HID_LCPOWER=y
+CONFIG_HID_LOGITECH=y
+CONFIG_HID_LOGITECH_DJ=y
+CONFIG_LOGITECH_FF=y
+CONFIG_LOGIRUMBLEPAD2_FF=y
+CONFIG_LOGIG940_FF=y
+CONFIG_HID_MICROSOFT=y
+CONFIG_HID_MONTEREY=y
+CONFIG_HID_MULTITOUCH=y
+CONFIG_HID_NTRIG=y
+CONFIG_HID_ORTEK=y
+CONFIG_HID_PANTHERLORD=y
+CONFIG_PANTHERLORD_FF=y
+CONFIG_HID_PETALYNX=y
+CONFIG_HID_PICOLCD=y
+CONFIG_HID_PICOLCD_FB=y
+CONFIG_HID_PRIMAX=y
+CONFIG_HID_ROCCAT=y
+CONFIG_HID_SAMSUNG=y
+CONFIG_HID_SONY=y
+CONFIG_HID_SPEEDLINK=y
+CONFIG_HID_SUNPLUS=y
+CONFIG_HID_GREENASIA=y
+CONFIG_GREENASIA_FF=y
+CONFIG_HID_SMARTJOYPLUS=y
+CONFIG_SMARTJOYPLUS_FF=y
+CONFIG_HID_TOPSEED=y
+CONFIG_HID_THRUSTMASTER=y
+CONFIG_THRUSTMASTER_FF=y
+CONFIG_HID_ZEROPLUS=y
+CONFIG_ZEROPLUS_FF=y
+CONFIG_HID_ZYDACRON=y
+CONFIG_USB_HIDDEV=y
+CONFIG_USB=y
+CONFIG_USB_OTG=y
+CONFIG_USB_ULPI_BUS=y
+CONFIG_USB_EHCI_HCD=y
+# CONFIG_USB_EHCI_TT_NEWSCHED is not set
+CONFIG_USB_STORAGE=y
+CONFIG_USB_CHIPIDEA=y
+CONFIG_USB_CHIPIDEA_UDC=y
+CONFIG_USB_CHIPIDEA_HOST=y
+CONFIG_USB_SERIAL=y
+CONFIG_USB_SERIAL_GENERIC=y
+CONFIG_USB_SERIAL_FTDI_SIO=y
+CONFIG_USB_ULPI=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_XILINX=y
+CONFIG_MMC=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_OF_ARASAN=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_ONESHOT=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_LEDS_TRIGGER_CPU=y
+CONFIG_EDAC=y
+CONFIG_EDAC_MM_EDAC=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_PCF8563=y
+CONFIG_DMADEVICES=y
+CONFIG_AXI_DMAC=y
+CONFIG_PL330_DMA=y
+CONFIG_XILINX_DMA=y
+CONFIG_UIO=y
+CONFIG_UIO_PDRV_GENIRQ=y
+CONFIG_UIO_DMEM_GENIRQ=y
+CONFIG_UIO_XILINX_APM=y
+CONFIG_STAGING=y
+CONFIG_ADIS16201=y
+CONFIG_ADIS16203=y
+CONFIG_ADIS16204=y
+CONFIG_ADIS16209=y
+CONFIG_ADIS16220=y
+CONFIG_ADIS16240=y
+CONFIG_AD7606=y
+CONFIG_AD7606_IFACE_SPI=y
+CONFIG_AD7780=y
+CONFIG_AD7816=y
+CONFIG_AD7192=y
+CONFIG_AD7280=y
+CONFIG_ADIS16060=y
+CONFIG_IIO_TRIGGER_HRTIMER=y
+CONFIG_COMMON_CLK_SI570=y
+CONFIG_COMMON_CLK_AXI_CLKGEN=y
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_MEMORY=y
+CONFIG_IIO=y
+CONFIG_AD7173=y
+CONFIG_AD7266=y
+CONFIG_AD7291=y
+CONFIG_AD7298=y
+CONFIG_AD7476=y
+CONFIG_AD7791=y
+CONFIG_AD7793=y
+CONFIG_AD7887=y
+CONFIG_AD799X=y
+CONFIG_AD9361=y
+CONFIG_AD6676=y
+CONFIG_AD9467=y
+CONFIG_ADMC=y
+CONFIG_CF_AXI_FFT=y
+CONFIG_AXI_JESD204B=y
+CONFIG_XILINX_XADC=y
+CONFIG_AD8366=y
+CONFIG_AD5064=y
+CONFIG_AD5360=y
+CONFIG_AD5380=y
+CONFIG_AD5421=y
+CONFIG_AD5446=y
+CONFIG_AD5449=y
+CONFIG_AD5592R=y
+CONFIG_AD5593R=y
+CONFIG_AD5504=y
+CONFIG_AD5624R_SPI=y
+CONFIG_AD5686=y
+CONFIG_AD5755=y
+CONFIG_AD5764=y
+CONFIG_AD5791=y
+CONFIG_AD7303=y
+CONFIG_AD9523=y
+CONFIG_AD9528=y
+CONFIG_AD9548=y
+CONFIG_AD9517=y
+CONFIG_CF_AXI_DDS=y
+CONFIG_CF_AXI_DDS_AD9122=y
+CONFIG_CF_AXI_DDS_AD9144=y
+CONFIG_CF_AXI_DDS_AD9739A=y
+CONFIG_ADF4350=y
+CONFIG_ADF5355=y
+CONFIG_ADIS16080=y
+CONFIG_ADIS16130=y
+CONFIG_ADIS16136=y
+CONFIG_ADIS16260=y
+CONFIG_ADXRS450=y
+CONFIG_ADIS16400=y
+CONFIG_ADIS16480=y
+# CONFIG_RESET_CONTROLLER is not set
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+# CONFIG_DNOTIFY is not set
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+# CONFIG_NETWORK_FILESYSTEMS is not set
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_DEBUG_INFO=y
+# CONFIG_ENABLE_WARN_DEPRECATED is not set
+# CONFIG_ENABLE_MUST_CHECK is not set
+CONFIG_DEBUG_FS=y
+CONFIG_DETECT_HUNG_TASK=y
+CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=20
+# CONFIG_SCHED_DEBUG is not set
+# CONFIG_DEBUG_PREEMPT is not set
+CONFIG_RCU_CPU_STALL_TIMEOUT=60
+# CONFIG_FTRACE is not set
+CONFIG_DEBUG_LL=y
+CONFIG_DEBUG_ZYNQ_UART1=y
+CONFIG_EARLY_PRINTK=y
+CONFIG_FONTS=y
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+# End zynq_xcomm_adv7511_defconfig
+
+
+###############################################################################
+# PARALLELLA #
+###############################################################################
+
+# Epiphany driver
+CONFIG_EPIPHANY=y
+
+# VDD_DSP regulator
+CONFIG_REGULATOR_ISL9305=y
+
+# Networking
+CONFIG_BRIDGE=y
+CONFIG_IPV6=y
+CONFIG_INET=y
+
+# AoE
+CONFIG_ATA_OVER_ETH=y
+
+# Eth0
+CONFIG_MACB=y
+CONFIG_MARVELL_PHY=y
+CONFIG_MDIO_BITBANG=y
+
+# USB extras
+CONFIG_USB_USBNET=y
+CONFIG_USB_DEVICEFS=y
+CONFIG_USB_SERIAL=y
+
+# WiFi
+CONFIG_WLAN=y
+CONFIG_RTL_CARDS=y
+CONFIG_RTL8192CE=y
+CONFIG_RTL8188EE=y
+CONFIG_RTL8192CU=y
+CONFIG_RTLWIFI=y
+CONFIG_RTLWIFI_PCI=y
+CONFIG_RTLWIFI_USB=y
+CONFIG_RTLWIFI_DEBUG=y
+CONFIG_RTL8192C_COMMON=y
+CONFIG_RFKILL=y
+CONFIG_RFKILL_LEDS=y
+
+# Cgroups
+CONFIG_CGROUPS=y
+
+# Misc devices
+CONFIG_INPUT_JOYSTICK=y
+CONFIG_INPUT_MOUSE=y
+CONFIG_INPUT_KEYBOARD=y
+CONFIG_INPUT_JOYDEV=y
+
+# Multimedia(webcam etc)
+CONFIG_MEDIA_CONTROLLER=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_USB_VIDEO_CLASS=y
+CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y
+CONFIG_USB_PWC=y
+CONFIG_USB_GSPCA=y
+CONFIG_MEDIA_CAMERA_SUPPORT=y
+CONFIG_VIDEO_DEV=y
+CONFIG_VIDEO_TUNER=y
+CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_VIDEO_V4L2_SUBDEV_API=y
+
+# Crypto
+CONFIG_CRYPTO_AES=y
+CONFIG_CRYPTO_ARC4=y
+CONFIG_CRYPTO_CRC32C=y
+CONFIG_CRYPTO_SHA256=y
+CONFIG_CRYPTO_SHA512=y
+CONFIG_KEYS=y
+CONFIG_DEFAULT_SECURITY_DAC=y
+CONFIG_DEFAULT_SECURITY=""
+CONFIG_CRYPTO=y
+
+# SOUND
+CONFIG_SND_JACK=y
+CONFIG_SND_USB=y
+CONFIG_SND_USB_AUDIO=y
+CONFIG_SND_SEQUENCER=y
+
+# BLUETOOTH
+CONFIG_BT=y
+CONFIG_BT_L2CAP=y
+CONFIG_BT_SCO=y
+CONFIG_BT_RFCOMM=y
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=y
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=y
+
+# Power savings
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_STAT_DETAILS=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_GENERIC_CPUFREQ_CPU0=y
+CONFIG_CPU_IDLE=y
+CONFIG_ARM_ZYNQ_CPUIDLE=y
+CONFIG_CPU_THERMAL=y
+
+# ZRAM
+CONFIG_ZSMALLOC=y
+CONFIG_ZRAM=y
+
+# XADC
+CONFIG_IIO=y
+CONFIG_XILINX_XADC=y
+
+# NFS support
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_ROOT_NFS=y
+CONFIG_NFSD=y
+CONFIG_NFSD_V3_ACL=y
+CONFIG_NFSD_V4=y
+
+# Might come in handy
+CONFIG_XILINX_TRAFGEN=y
+
+# Systemd wants these
+CONFIG_AUTOFS4_FS=y
+CONFIG_FUSE_FS=y
+CONFIG_NAMESPACES=y
+CONFIG_NET_NS=y
+
+# HWMON
+CONFIG_HWMON=y
+CONFIG_SENSORS_IIO_HWMON=y
+
+# FPGA /dev/xdevcfg
+CONFIG_XILINX_DEVCFG=y
+
+# TUN/TAP support
+CONFIG_TUN=y
diff --git a/epiphany.c b/epiphany.c
new file mode 100644
index 000000000000..0bed02e8e58a
--- /dev/null
+++ b/epiphany.c
@@ -0,0 +1,2896 @@
+/*
+ * Copyright (C) 2015 Adapteva Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ */
+
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/sched/mm.h>
+#include <linux/sched/task.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/clk.h>
+#include <linux/ioctl.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/list.h>
+#include <linux/uaccess.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+
+#include "epiphany.h"
+
+#define DRIVERNAME "epiphany"
+
+#define E_DEV_NUM_MINORS MINORMASK /* Total to reserve */
+
+#define COREID_SHIFT 20
+#define COREID_MASK ((1 << COREID_SHIFT) - 1)
+
+/* Be careful, no range check */
+#define COORDS(row, col) ((row) * 64 | (col))
+#define ROW(coreid) ((coreid) / 64)
+#define COL(coreid) ((coreid) % 64)
+
+struct epiphany_vma_entry {
+ struct list_head list;
+ struct vm_area_struct *vma;
+ struct pid *pid;
+ atomic_t in_use;
+};
+
+static struct epiphany {
+ struct class class;
+ int u_count; /* User count */
+
+ struct list_head elink_list;
+ struct list_head chip_array_list;
+ struct list_head mesh_list;
+
+ struct list_head vma_list;
+
+ dev_t devt;
+
+ struct idr minor_idr;
+ /* Used by minor_get() / minor_put() */
+ spinlock_t minor_idr_lock;
+
+ /* For device naming */
+ atomic_t elink_counter;
+ atomic_t mesh_counter;
+ atomic_t array_counter;
+
+ /* One big lock for everything */
+ struct mutex driver_lock;
+
+ /* For thermal daemon (protected by driver lock) */
+ bool thermal_disallow;
+
+ /* Module parameters */
+ bool param_unsafe_access; /* access to fpga regs */
+ bool param_nopm; /* disable power management */
+} epiphany = {};
+
+module_param_named(unsafe_access, epiphany.param_unsafe_access, bool, 0644);
+MODULE_PARM_DESC(unsafe_access, "Permit access to elink FPGA registers");
+
+module_param_named(nopm, epiphany.param_nopm, bool, 0444);
+MODULE_PARM_DESC(nopm, "Disable power management");
+
+static const u32 ctrlmode_hints[E_SIDE_MAX] = {
+ [E_SIDE_N] = E_CTRLMODE_NORTH,
+ [E_SIDE_E] = E_CTRLMODE_EAST,
+ [E_SIDE_S] = E_CTRLMODE_SOUTH,
+ [E_SIDE_W] = E_CTRLMODE_WEST
+};
+
+enum performance_state {
+ E_PS_HIGHEST = 0,
+ E_PS_LOWEST,
+ E_PS_NUM_STATES
+};
+
+struct performance_state_cfg {
+ int vdd_thresh;
+
+ u32 linkcfg_tx_divider;
+};
+
+struct epiphany_chip_info {
+ int rows;
+ int cols;
+ size_t core_mem;
+ u16 elink_coreid[E_SIDE_MAX]; /* relative */
+
+ /* In uVolts */
+ int vdd_min;
+ int vdd_max;
+
+ u32 linkcfg_tx_divider;
+
+ struct performance_state_cfg perf_state[E_PS_NUM_STATES];
+};
+static const struct epiphany_chip_info epiphany_chip_info[E_CHIP_MAX] = {
+ /* Safe values when chip is unknown. */
+ [E_CHIP_UNKNOWN] = {
+ .vdd_min = 900000,
+ .vdd_max = 1025000,
+
+ .perf_state = {
+ [E_PS_HIGHEST] = {
+ .vdd_thresh = 1000000,
+ .linkcfg_tx_divider = 1
+ },
+ [E_PS_LOWEST] = {
+ .vdd_thresh = 1000000,
+ .linkcfg_tx_divider = 1
+ }
+ }
+ },
+ [E_CHIP_E16G301] = {
+ .rows = 4,
+ .cols = 4,
+ .core_mem = 32768,
+
+ .elink_coreid[E_SIDE_N] = COORDS(0, 2),
+ .elink_coreid[E_SIDE_E] = COORDS(2, 3),
+ .elink_coreid[E_SIDE_S] = COORDS(3, 2),
+ .elink_coreid[E_SIDE_W] = COORDS(2, 0),
+
+ /* Recommended operating conditions */
+ .vdd_min = 900000,
+ .vdd_max = 1200000,
+
+ .linkcfg_tx_divider = 1,
+
+ .perf_state = {
+ [E_PS_HIGHEST] = {
+ .vdd_thresh = 1000000,
+ .linkcfg_tx_divider = 0
+ },
+ /* TODO: Verify */
+ [E_PS_LOWEST] = {
+ .vdd_thresh = 900000,
+ .linkcfg_tx_divider = 1
+ }
+ }
+ },
+ [E_CHIP_E64G401] = {
+ .rows = 8,
+ .cols = 8,
+ .core_mem = 32768,
+
+ .elink_coreid[E_SIDE_N] = COORDS(0, 2),
+ .elink_coreid[E_SIDE_E] = COORDS(2, 7),
+ .elink_coreid[E_SIDE_S] = COORDS(7, 2),
+ .elink_coreid[E_SIDE_W] = COORDS(2, 0),
+
+ /* Recommended operating conditions */
+ .vdd_min = 900000,
+ .vdd_max = 1100000,
+
+ .linkcfg_tx_divider = 0,
+
+ /* TODO: Verify */
+ .perf_state = {
+ [E_PS_HIGHEST] = {
+ .vdd_thresh = 1000000,
+ .linkcfg_tx_divider = 0
+ },
+ [E_PS_LOWEST] = {
+ .vdd_thresh = 900000,
+ .linkcfg_tx_divider = 1
+ }
+ }
+ }
+};
+
+enum elink_generation {
+ E_GEN_INVAL = 0,
+ E_GEN_PARALLELLA1,
+ E_GEN_MAX
+};
+
+enum elink_platform {
+ E_PLATF_UNKNOWN = 0,
+ E_PLATF_E16_7Z020_GPIO,
+ E_PLATF_E16_7Z020_NO_GPIO,
+ E_PLATF_E16_7Z010_GPIO,
+ E_PLATF_E16_7Z010_NO_GPIO,
+ E_PLATF_E64_7Z020_GPIO,
+ E_PLATF_MAX
+};
+
+static const enum e_chip_type elink_platform_chip_match[E_PLATF_MAX] = {
+ [E_PLATF_UNKNOWN] = E_CHIP_UNKNOWN,
+ [E_PLATF_E16_7Z020_GPIO] = E_CHIP_E16G301,
+ [E_PLATF_E16_7Z020_NO_GPIO] = E_CHIP_E16G301,
+ [E_PLATF_E16_7Z010_GPIO] = E_CHIP_E16G301,
+ [E_PLATF_E16_7Z010_NO_GPIO] = E_CHIP_E16G301,
+ [E_PLATF_E64_7Z020_GPIO] = E_CHIP_E64G401
+};
+
+struct connection {
+ enum e_connection_type type; /* remote type */
+ enum e_link_side side; /* remote side */
+ union {
+ struct elink_device *elink;
+ struct array_device *array;
+ };
+
+ phandle phandle;
+};
+
+struct elink_device {
+ struct list_head list;
+ struct device dev;
+
+ void __iomem *regs;
+ phys_addr_t regs_start;
+ size_t regs_size;
+
+ /* TODO: Rename */
+ /* Host --> emesh bus address range */
+ phys_addr_t emesh_start;
+ size_t emesh_size;
+
+ struct clk **clocks;
+
+ /* Power supply */
+ struct regulator *supply;
+ int vdd_wanted;
+
+ s16 coreid_pinout; /* core id pinout */
+ bool coreid_is_noop;
+
+ union elink_version version;
+ enum e_chip_type chip_type;
+
+ struct connection connection;
+
+ /* TODO: Have our own cdev */
+ struct cdev cdev;
+ int minor;
+
+ /* Available memory regions */
+ struct list_head mem_region_list;
+
+ /* Mapped memory regions */
+ struct list_head mappings_list;
+
+ wait_queue_head_t mailbox_wait;
+ struct work_struct mailbox_irq_work;
+ atomic_t mailbox_maybe_not_empty;
+
+ phandle phandle;
+};
+
+struct array_device {
+ struct list_head list;
+
+ struct device dev;
+
+ u16 id; /* north-west-most core */
+ unsigned int chip_rows;
+ unsigned int chip_cols;
+ enum e_chip_type chip_type;
+
+ enum e_link_side parent_side; /* Side of array array is connected to to
+ parent elink */
+ struct connection connections[E_SIDE_MAX];
+
+ struct mesh_device *mesh;
+
+ phandle phandle;
+};
+
+struct mesh_device {
+ struct list_head list;
+ struct device dev;
+
+ struct cdev cdev;
+ int minor;
+
+ struct array_device **arrays;
+};
+
+
+struct mem_region {
+ struct list_head list;
+ phys_addr_t start;
+ phys_addr_t emesh_start;
+ size_t size;
+
+ phandle phandle;
+};
+
+/* Return the maximum performance state the chip array can currently do, not
+ * taking its neighbours into account */
+static enum performance_state
+array_get_max_perf_state(const struct array_device *array)
+{
+ int curr_vdd;
+ enum performance_state i;
+ struct elink_device *elink;
+ const struct epiphany_chip_info *cinfo =
+ &epiphany_chip_info[array->chip_type];
+
+ elink = array->connections[array->parent_side].elink;
+
+ if (!elink || !elink->supply)
+ return E_PS_LOWEST;
+
+ curr_vdd = regulator_get_voltage(elink->supply);
+ for (i = E_PS_HIGHEST; i < E_PS_NUM_STATES; i++) {
+ if (curr_vdd >= cinfo->perf_state[i].vdd_thresh)
+ return i;
+ }
+
+ /* Out of spec */
+
+ return E_PS_LOWEST;
+}
+
+static inline void reg_write(u32 value, void __iomem *base, u32 offset)
+{
+ iowrite32(value, (u8 __iomem *)base + offset);
+}
+
+static inline void reg_write64(u64 value, void __iomem *base, u32 offset)
+{
+ reg_write((u32) (value & 0xffffffff), base, offset);
+ reg_write((u32) (value >> 32), base, offset + 4);
+}
+
+static inline u32 reg_read(void __iomem *base, u32 offset)
+{
+ return ioread32((u8 __iomem *)base + offset);
+}
+
+static inline struct elink_device *file_to_elink(struct file *file)
+{
+ return container_of(file->private_data, struct elink_device, cdev);
+}
+
+static inline struct elink_device *device_to_elink(struct device *dev)
+{
+ return container_of(dev, struct elink_device, dev);
+}
+
+static inline struct array_device *device_to_array(struct device *dev)
+{
+ return container_of(dev, struct array_device, dev);
+}
+
+static inline struct mesh_device *device_to_mesh(struct device *dev)
+{
+ return container_of(dev, struct mesh_device, dev);
+}
+
+static inline struct mesh_device *file_to_mesh(struct file *file)
+{
+ return container_of(file->private_data, struct mesh_device, cdev);
+}
+
+static inline struct elink_device *vma_to_elink(struct vm_area_struct *vma)
+{
+ return file_to_elink(vma->vm_file);
+}
+
+static inline struct epiphany_vma_entry *
+vma_to_epiphany_vma_entry(struct vm_area_struct *vma)
+{
+ return (struct epiphany_vma_entry *) vma->vm_private_data;
+}
+
+static int coreid_to_phys(struct elink_device *elink, u16 coreid,
+ phys_addr_t *out)
+{
+ u32 rel_coreid, rel_row, rel_col;
+ struct array_device *array = elink->connection.array;
+ const struct epiphany_chip_info *cinfo;
+ phys_addr_t offs;
+
+ if (elink->connection.type != E_CONN_ARRAY)
+ return -EINVAL;
+
+ if (coreid < array->id)
+ return -ERANGE;
+
+ cinfo = &epiphany_chip_info[array->chip_type];
+ rel_coreid = coreid - array->id;
+ rel_row = ROW(rel_coreid);
+ rel_col = COL(rel_coreid);
+
+ if (rel_row >= array->chip_rows * cinfo->rows)
+ return -ERANGE;
+
+ if (rel_col >= array->chip_cols * cinfo->cols)
+ return -ERANGE;
+
+ /* Offset from array start */
+ offs = ((phys_addr_t) rel_coreid) << ((phys_addr_t) COREID_SHIFT);
+
+ /* Adjust for offset from elink mem region (align by row) */
+ offs += ((phys_addr_t) COL(array->id)) << ((phys_addr_t) COREID_SHIFT);
+
+ if (offs >= elink->emesh_size)
+ return -ERANGE;
+
+ *out = offs + elink->emesh_start;
+
+ return 0;
+}
+
+/* Disable chip elink */
+static void elink_disable_chip_elink(struct elink_device *elink,
+ struct array_device *array,
+ u16 chipid,
+ enum e_link_side side)
+{
+ int err;
+ const struct epiphany_chip_info *cinfo =
+ &epiphany_chip_info[array->chip_type];
+ phys_addr_t core_phys, regs_phys;
+ u16 coreid;
+ void __iomem *regs;
+ union elink_txcfg txcfg;
+
+ coreid = chipid + cinfo->elink_coreid[side];
+
+ dev_dbg(&elink->dev,
+ "Disabling elink 0x%03x (%02u, %02u) in array 0x%03x.\n",
+ coreid, ROW(coreid), COL(coreid), array->id);
+
+ err = coreid_to_phys(elink, coreid, &core_phys);
+ WARN_ON(err);
+ if (err)
+ return;
+
+ regs_phys = (core_phys | E_REG_BASE) & PAGE_MASK;
+ regs = ioremap_nocache(regs_phys, PAGE_SIZE);
+ WARN_ON(!regs);
+ if (!regs)
+ return;
+
+ txcfg.reg = reg_read(elink->regs, ELINK_TXCFG);
+ txcfg.ctrlmode = ctrlmode_hints[side];
+ reg_write(txcfg.reg, elink->regs, ELINK_TXCFG);
+
+ reg_write(0xfff, regs, E_REG_LINKTXCFG & ~PAGE_MASK);
+ reg_write(0xfff, regs, E_REG_LINKRXCFG & ~PAGE_MASK);
+
+ txcfg.ctrlmode = 0;
+ reg_write(txcfg.reg, elink->regs, ELINK_TXCFG);
+
+ iounmap(regs);
+}
+
+static void array_disable_disconnected_elinks(struct elink_device *elink,
+ struct array_device *array)
+{
+ int i;
+ const struct epiphany_chip_info *cinfo =
+ &epiphany_chip_info[array->chip_type];
+ enum e_link_side side;
+ u32 mask = 0;
+ u16 north_chip, south_chip, east_chip, west_chip;
+
+ for (side = 0; side < ARRAY_SIZE(array->connections); side++) {
+ if (array->connections[side].type == E_CONN_DISCONNECTED)
+ mask |= 1 << side;
+
+ }
+
+ /* Walk north and south cols */
+ if (mask & ((1 << E_SIDE_N) | (1 << E_SIDE_S))) {
+ for (i = 0, north_chip = array->id;
+ i < array->chip_cols;
+ i++, north_chip += cinfo->cols) {
+ south_chip = north_chip +
+ COORDS((array->chip_rows - 1) * cinfo->rows, 0);
+
+ if (mask & (1 << E_SIDE_N)) {
+ elink_disable_chip_elink(elink, array,
+ north_chip, E_SIDE_N);
+ }
+ if (mask & (1 << E_SIDE_S)) {
+ elink_disable_chip_elink(elink, array,
+ south_chip, E_SIDE_S);
+ }
+ }
+ }
+
+ /* Walk east and west rows */
+ if (mask & ((1 << E_SIDE_E) | (1 << E_SIDE_W))) {
+ for (i = 0, west_chip = array->id;
+ i < array->chip_rows;
+ i++, west_chip += COORDS(1, 0)) {
+ east_chip = west_chip +
+ COORDS(0, (array->chip_cols - 1) * cinfo->cols);
+
+ if (mask & (1 << E_SIDE_W)) {
+ elink_disable_chip_elink(elink, array,
+ west_chip, E_SIDE_W);
+ }
+ if (mask & (1 << E_SIDE_E)) {
+ elink_disable_chip_elink(elink, array,
+ east_chip, E_SIDE_E);
+ }
+ }
+ }
+}
+
+static void array_enable_clock_gating(struct elink_device *elink,
+ struct array_device *array)
+{
+ int err, i, j, row0, col0, last_row, last_col;
+ const struct epiphany_chip_info *cinfo =
+ &epiphany_chip_info[array->chip_type];
+ phys_addr_t core, paddr;
+ void __iomem *core_mem;
+ u32 config, meshconfig;
+
+ row0 = ROW(array->id);
+ col0 = COL(array->id);
+ last_row = row0 + array->chip_rows * cinfo->rows;
+ last_col = col0 + array->chip_cols * cinfo->cols;
+
+ for (i = row0; i < last_row; i++) {
+ for (j = col0; j < last_col; j++) {
+ err = coreid_to_phys(elink, COORDS(i, j), &core);
+ WARN_ON(err);
+ if (err)
+ continue;
+
+ paddr = (core | E_REG_BASE) & PAGE_MASK;
+ core_mem = ioremap_nocache(paddr, PAGE_SIZE);
+ WARN_ON(!core_mem);
+ if (!core_mem)
+ continue;
+
+
+ config = E_REG_CONFIG & ~(PAGE_MASK);
+ reg_write(0x00400000, core_mem, config);
+
+ meshconfig = E_REG_MESHCONFIG & ~(PAGE_MASK);
+ reg_write(0x00000002, core_mem, meshconfig);
+
+ iounmap(core_mem);
+ }
+ }
+}
+
+static int configure_chip_tx_divider(struct elink_device *elink,
+ u16 chipid,
+ enum e_link_side side)
+{
+ int err;
+ struct array_device *array = elink->connection.array;
+ const struct epiphany_chip_info *cinfo =
+ &epiphany_chip_info[array->chip_type];
+ phys_addr_t core_phys, regs_phys;
+ u16 coreid;
+ u32 divider;
+ void __iomem *regs;
+ union elink_txcfg txcfg;
+ u32 offset;
+ enum performance_state ps;
+
+ ps = array_get_max_perf_state(elink->connection.array);
+ divider = cinfo->perf_state[ps].linkcfg_tx_divider;
+
+ coreid = chipid + cinfo->elink_coreid[side];
+
+ txcfg.reg = reg_read(elink->regs, ELINK_TXCFG);
+ txcfg.ctrlmode = ctrlmode_hints[side];
+
+ err = coreid_to_phys(elink, coreid, &core_phys);
+ WARN_ON(err);
+ if (err)
+ return err;
+
+ regs_phys = (core_phys | E_REG_BASE) & PAGE_MASK;
+ regs = ioremap_nocache(regs_phys, PAGE_SIZE);
+ offset = E_REG_LINKCFG & ~(PAGE_MASK);
+ WARN_ON(!regs);
+ if (!regs)
+ return -EIO;
+
+ txcfg.reg = reg_read(elink->regs, ELINK_TXCFG);
+ txcfg.ctrlmode = ctrlmode_hints[side];
+ reg_write(txcfg.reg, elink->regs, ELINK_TXCFG);
+
+ reg_write(divider, regs, offset);
+
+ txcfg.ctrlmode = 0;
+ reg_write(txcfg.reg, elink->regs, ELINK_TXCFG);
+
+ iounmap(regs);
+ return 0;
+}
+
+static int configure_adjacent_links(struct elink_device *elink)
+{
+ int i;
+ const struct epiphany_chip_info *cinfo;
+ struct array_device *array;
+ u16 north_chip, south_chip, east_chip, west_chip, the_chip;
+ enum e_link_side side;
+
+ if (elink->connection.type != E_CONN_ARRAY)
+ return 0;
+
+ BUG_ON(!elink->connection.array);
+
+ array = elink->connection.array;
+ cinfo = &epiphany_chip_info[array->chip_type];
+ side = elink->connection.side;
+
+ switch (side) {
+ case E_SIDE_N:
+ case E_SIDE_S:
+ for (i = 0, north_chip = array->id;
+ i < array->chip_cols;
+ i++, north_chip += cinfo->cols) {
+ south_chip = north_chip +
+ COORDS((array->chip_rows - 1) * cinfo->rows, 0);
+ the_chip = side == E_SIDE_N ? north_chip : south_chip;
+ return configure_chip_tx_divider(elink, the_chip, side);
+ }
+ case E_SIDE_E:
+ case E_SIDE_W:
+ for (i = 0, west_chip = array->id;
+ i < array->chip_rows;
+ i++, west_chip += COORDS(1, 0)) {
+ east_chip = west_chip +
+ COORDS(0, (array->chip_cols - 1) * cinfo->cols);
+ the_chip = side == E_SIDE_W ? west_chip : east_chip;
+ return configure_chip_tx_divider(elink, the_chip, side);
+ }
+ default:
+ WARN_ON(true);
+ return -EINVAL;
+ }
+}
+
+static void elink_update_mmu_mappings(struct elink_device *elink)
+{
+ const u32 mmu_base = 0xe8000;
+ struct mem_region *mapping;
+ u32 mmu_entry;
+ u64 phys_addr;
+
+ list_for_each_entry(mapping, &elink->mappings_list, list) {
+ mmu_entry = mmu_base + ((mapping->emesh_start >> 20) << 3);
+ phys_addr = mapping->start;
+ for (; phys_addr - mapping->start < mapping->size;
+ phys_addr += (1 << 20), mmu_entry += 8) {
+ dev_dbg(&elink->dev, "%s: mapping 0x%03x -> 0x%03llx\n",
+ __func__, (mmu_entry - mmu_base) >> 3,
+ phys_addr >> 20);
+ reg_write64(phys_addr >> 20, elink->regs, mmu_entry);
+ }
+ }
+ /* Map in the elink regs so the chip-array can access the mailbox
+ * registers. FIXME: Not always this simple (phys addr == epiphany
+ * addr) */
+ reg_write64(elink->regs_start >> 20, elink->regs,
+ mmu_base + (elink->regs_start >> (20 - 3)));
+}
+
+/* Reset the Epiphany platform */
+static int elink_reset(struct elink_device *elink)
+{
+ /* TODO: Should be able to provide via device tree */
+ const u32 rxdelay0 = 0xaaaaaaaa;
+ const u32 rxdelay1 = 0x0000000a;
+
+ int ret = 0;
+ union elink_reset reset = {0};
+ union elink_txcfg txcfg = {0};
+ union elink_rxcfg rxcfg = {0};
+
+ /* assert reset */
+ reset.tx_reset = 1;
+ reset.rx_reset = 1;
+ reg_write(reset.reg, elink->regs, ELINK_RESET);
+
+ usleep_range(500, 600);
+
+ /* de-assert reset */
+ reset.tx_reset = 0;
+ reset.rx_reset = 0;
+ reg_write(reset.reg, elink->regs, ELINK_RESET);
+
+ usleep_range(500, 600);
+
+ reg_write(elink->coreid_pinout, elink->regs, ELINK_CHIPID);
+
+ reg_write(rxdelay0, elink->regs, ELINK_RXDELAY0);
+ reg_write(rxdelay1, elink->regs, ELINK_RXDELAY1);
+
+ txcfg.enable = 1;
+ reg_write(txcfg.reg, elink->regs, ELINK_TXCFG);
+
+
+ /* HACK: Use static remapping until this is fixed:
+ * "Consecutive writes to rxmmu table results in system freeze"
+ * https://github.com/parallella/oh/issues/50
+ */
+#if 0
+ rxcfg.mmu_enable = 1;
+ rxcfg.remap_mode = 0; /* no remapping, only mmu */
+ reg_write(rxcfg.reg, elink->regs, ELINK_RXCFG);
+
+ elink_update_mmu_mappings(elink);
+#else
+ rxcfg.remap_mode = 1;
+ rxcfg.remap_sel = 0xfe0;
+ rxcfg.remap_pattern = 0x3e0;
+ reg_write(rxcfg.reg, elink->regs, ELINK_RXCFG);
+#endif
+
+ ret = configure_adjacent_links(elink);
+
+ return ret;
+}
+
+static void elink_disable(struct elink_device *elink)
+{
+ union elink_txcfg txcfg = { .enable = 0 };
+ union elink_reset reset = { .tx_reset = 1, .rx_reset = 1 };
+
+ reg_write(txcfg.reg, elink->regs, ELINK_TXCFG);
+ /* TODO: Don't we also need rxcfg.enable -> 0 ??? */
+
+ reg_write(reset.reg, elink->regs, ELINK_RESET);
+
+ usleep_range(500, 600);
+}
+
+static int elink_regulator_enable(struct elink_device *elink)
+{
+ int ret, i, old_vdd, new_vdd, step, wiggle;
+ bool extra_delay;
+ const struct epiphany_chip_info *cinfo =
+ &epiphany_chip_info[elink->chip_type];
+
+ if (!elink->supply)
+ return 0;
+
+ old_vdd = regulator_get_voltage(elink->supply);
+ if (old_vdd < 0)
+ return old_vdd;
+
+ new_vdd = min(old_vdd, cinfo->vdd_max);
+ step = regulator_get_linear_step(elink->supply);
+
+ ret = -EINVAL;
+
+ if (cinfo->vdd_min <= elink->vdd_wanted &&
+ elink->vdd_wanted <= cinfo->vdd_max) {
+ new_vdd = elink->vdd_wanted;
+ wiggle = min(new_vdd + step, cinfo->vdd_max);
+ ret = regulator_set_voltage(elink->supply, elink->vdd_wanted,
+ wiggle);
+ }
+
+ if (ret) {
+ for (i = 0; i < E_PS_NUM_STATES; i++) {
+ new_vdd = cinfo->perf_state[i].vdd_thresh;
+ wiggle = min(new_vdd + step, cinfo->vdd_max);
+ ret = regulator_set_voltage(elink->supply,
+ new_vdd, wiggle);
+ if (!ret)
+ break;
+ }
+ }
+ if (ret)
+ return ret;
+
+ /* Pessimistic sleep if regulator doesn't provide a ramp-up time, then
+ * it didn't block in regulator_set_voltage(). ???: And will also
+ * not block in regulator_enable() ??? */
+
+ extra_delay =
+ (0 >= regulator_set_voltage_time(elink->supply,
+ cinfo->vdd_min,
+ cinfo->vdd_max))
+ ? true : false;
+
+ if (extra_delay && old_vdd != new_vdd)
+ msleep(100);
+
+ ret = regulator_enable(elink->supply);
+
+ if (extra_delay)
+ usleep_range(20000, 20100);
+
+ return ret;
+}
+
+static void elink_regulator_disable(struct elink_device *elink)
+{
+ if (elink->supply)
+ regulator_disable(elink->supply);
+
+ usleep_range(2000, 2100);
+}
+
+static void elink_mailbox_irq_enable(struct elink_device *elink)
+{
+ union elink_rxcfg cfg;
+
+ cfg.reg = reg_read(elink->regs, ELINK_RXCFG);
+ cfg.mailbox_irq_en = 1;
+ reg_write(cfg.reg, elink->regs, ELINK_RXCFG);
+}
+
+static void elink_mailbox_irq_disable(struct elink_device *elink)
+{
+ union elink_rxcfg cfg;
+
+ cfg.reg = reg_read(elink->regs, ELINK_RXCFG);
+ cfg.mailbox_irq_en = 0;
+ reg_write(cfg.reg, elink->regs, ELINK_RXCFG);
+}
+
+static bool elink_mailbox_empty_p(struct elink_device *elink)
+{
+ union elink_mailboxstat status;
+
+ status.reg = reg_read(elink->regs, ELINK_MAILBOXSTAT);
+
+ return !status.not_empty;
+}
+
+static unsigned elink_mailbox_count(struct elink_device *elink)
+{
+ union elink_mailboxstat status;
+
+ status.reg = reg_read(elink->regs, ELINK_MAILBOXSTAT);
+
+ return status.count;
+}
+
+static void elink_mailbox_irq_handler_bh(struct work_struct *ws)
+{
+ bool empty;
+ struct elink_device *elink =
+ container_of(ws, struct elink_device, mailbox_irq_work);
+
+ mutex_lock(&epiphany.driver_lock);
+ empty = elink_mailbox_empty_p(elink);
+ if (!empty) {
+ elink_mailbox_irq_disable(elink);
+ atomic_set(&elink->mailbox_maybe_not_empty, 1);
+ }
+ mutex_unlock(&epiphany.driver_lock);
+
+ if (!empty)
+ wake_up(&elink->mailbox_wait);
+}
+
+static irqreturn_t elink_mailbox_irq_handler(int irq, void *dev_id)
+{
+ struct elink_device *elink = dev_id;
+
+ schedule_work(&elink->mailbox_irq_work);
+
+ /* We don't know if we caused interrupt */
+ return IRQ_HANDLED;
+}
+
+static int epiphany_vm_freeze(bool interruptible)
+{
+ unsigned long jiffies_expire = jiffies + HZ * 10;
+ struct epiphany_vma_entry *vma_entry;
+
+retry:
+ /* Give up after trying 10 seconds */
+ if (time_after(jiffies, jiffies_expire))
+ return -EBUSY;
+
+ if (interruptible) {
+ if (mutex_lock_interruptible(&epiphany.driver_lock))
+ return -ERESTARTSYS;
+ } else {
+ mutex_lock(&epiphany.driver_lock);
+ }
+
+ list_for_each_entry(vma_entry, &epiphany.vma_list, list) {
+ struct task_struct *task;
+ struct mm_struct *mm;
+
+ task = get_pid_task(vma_entry->pid, PIDTYPE_PID);
+ if (!task)
+ continue;
+
+ mm = get_task_mm(task);
+ if (!mm) {
+ put_task_struct(task);
+ continue;
+ }
+
+ if (!down_read_trylock(&mm->mmap_sem)) {
+ mmput(mm);
+ put_task_struct(task);
+ mutex_unlock(&epiphany.driver_lock);
+ schedule();
+ goto retry;
+ }
+ if (atomic_read(&vma_entry->in_use)) {
+ struct vm_area_struct *vma = vma_entry->vma;
+ zap_vma_ptes(vma, vma->vm_start,
+ vma->vm_end - vma->vm_start);
+ }
+ up_read(&mm->mmap_sem);
+
+ mmput(mm);
+ put_task_struct(task);
+ }
+
+ usleep_range(500, 600);
+
+ return 0;
+}
+
+static void epiphany_vm_unfreeze(void)
+{
+ usleep_range(500, 600);
+ mutex_unlock(&epiphany.driver_lock);
+}
+
+static int epiphany_reset(void)
+{
+ struct elink_device *elink;
+ int err;
+
+ /* Unsafe to manipulate power if already in use. At any rate we should
+ * not call regulator_enable() again since that would screw up the
+ * regulator's refcount */
+ if (!epiphany.u_count) {
+ list_for_each_entry(elink, &epiphany.elink_list, list) {
+ if (elink_regulator_enable(elink)) {
+ /* Not much else we can do? */
+ return -EIO;
+ }
+ }
+ }
+
+ list_for_each_entry(elink, &epiphany.elink_list, list) {
+ err = elink_reset(elink);
+ if (err)
+ return -EIO;
+ }
+
+ if (epiphany.param_nopm)
+ return 0;
+
+ list_for_each_entry(elink, &epiphany.elink_list, list) {
+ if (elink->connection.type != E_CONN_ARRAY)
+ continue;
+ array_enable_clock_gating(elink, elink->connection.array);
+ array_disable_disconnected_elinks(elink,
+ elink->connection.array);
+ }
+
+ return 0;
+}
+
+/* caller must hold epiphany.driver_lock */
+static int _epiphany_get(void)
+{
+ int ret;
+
+ if (!epiphany.u_count) {
+ /* if !epiphany.param_no_reset (or no power mgmt) */
+ ret = epiphany_reset();
+ if (ret)
+ return ret;
+ }
+
+ epiphany.u_count++;
+
+ return 0;
+}
+
+static int epiphany_get(void)
+{
+ int ret;
+
+ mutex_lock(&epiphany.driver_lock);
+ ret = _epiphany_get();
+ mutex_unlock(&epiphany.driver_lock);
+
+ return ret;
+}
+
+static int epiphany_get_interruptible(void)
+{
+ int ret;
+
+ if (mutex_lock_interruptible(&epiphany.driver_lock))
+ return -ERESTARTSYS;
+ ret = _epiphany_get();
+ mutex_unlock(&epiphany.driver_lock);
+
+ return ret;
+}
+
+static void epiphany_put(void)
+{
+ struct elink_device *elink;
+
+ mutex_lock(&epiphany.driver_lock);
+
+ epiphany.u_count--;
+
+ if (epiphany.u_count) {
+ mutex_unlock(&epiphany.driver_lock);
+ return;
+ }
+
+ list_for_each_entry(elink, &epiphany.elink_list, list)
+ elink_disable(elink);
+
+ list_for_each_entry(elink, &epiphany.elink_list, list)
+ elink_regulator_disable(elink);
+
+ pr_debug("epiphany: no users\n");
+
+ mutex_unlock(&epiphany.driver_lock);
+}
+
+static int char_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_cdev;
+
+ return epiphany_get_interruptible();
+}
+
+static int mesh_char_open(struct inode *inode, struct file *file)
+{
+ struct array_device *array;
+ struct elink_device *elink;
+ struct mesh_device *mesh;
+
+ mesh = container_of(inode->i_cdev, struct mesh_device, cdev);
+
+ array = mesh->arrays[0];
+ if (!array)
+ return -EINVAL;
+
+ elink = array->connections[array->parent_side].elink;
+ if (!elink)
+ return -EINVAL;
+
+ file->private_data = &elink->cdev;
+
+ return epiphany_get_interruptible();
+}
+
+static int char_release(struct inode *inode, struct file *file)
+{
+ epiphany_put();
+
+ return 0;
+}
+
+static void epiphany_vm_open(struct vm_area_struct *vma)
+{
+ struct epiphany_vma_entry *vma_entry;
+
+ mutex_lock(&epiphany.driver_lock);
+
+ vma_entry = kzalloc(sizeof(*vma_entry), GFP_KERNEL);
+ if (!vma_entry)
+ goto out;
+
+ vma_entry->vma = vma;
+ vma_entry->pid = get_task_pid(current, PIDTYPE_PID);
+ atomic_set(&vma_entry->in_use, 1);
+
+ vma->vm_private_data = vma_entry;
+
+ list_add_tail(&vma_entry->list, &epiphany.vma_list);
+
+out:
+ mutex_unlock(&epiphany.driver_lock);
+}
+
+static void epiphany_vm_close(struct vm_area_struct *vma)
+{
+ struct epiphany_vma_entry *vma_entry;
+
+ mutex_lock(&epiphany.driver_lock);
+
+ vma_entry = vma_to_epiphany_vma_entry(vma);
+
+ if (!vma_entry) {
+ mutex_unlock(&epiphany.driver_lock);
+ return;
+ }
+
+ if (atomic_dec_and_test(&vma_entry->in_use)) {
+ list_del(&vma_entry->list);
+ put_pid(vma_entry->pid);
+ kfree(vma_entry);
+ } else {
+ WARN_ON(true);
+ }
+ vma->vm_private_data = NULL;
+ mutex_unlock(&epiphany.driver_lock);
+}
+
+/* Epiphany mesh pfn to phys pfn
+ * &epiphany.driver_lock must be held by caller
+ * TODO: ???: Replace elink with vm_area_struct or mesh_device */
+static int mesh_pfn_to_phys_pfn(struct elink_device *elink, unsigned long pfn,
+ unsigned long *phys_pfn)
+{
+ const u16 coreid = pfn >> (COREID_SHIFT - PAGE_SHIFT);
+ const phys_addr_t core_offs = PFN_PHYS(pfn) & COREID_MASK;
+ const phys_addr_t core_end = core_offs + PAGE_SIZE;
+ const phys_addr_t start = PFN_PHYS(pfn);
+ const phys_addr_t end = PFN_PHYS(pfn + 1);
+ const struct epiphany_chip_info *cinfo =
+ &epiphany_chip_info[elink->chip_type];
+ unsigned offs;
+ phys_addr_t core_phys_addr;
+ struct mem_region *mapping;
+
+ if (end < start) {
+ WARN(1, "overflow");
+ return -EOVERFLOW;
+ }
+
+ if (!coreid_to_phys(elink, coreid, &core_phys_addr)) {
+ /* sram */
+ if (core_end <= cinfo->core_mem) {
+ *phys_pfn = PFN_DOWN(core_phys_addr + core_offs);
+ return 0;
+ }
+
+ /* registers.
+ * N.B: End is rounded up from 0xf0800 to page boundary */
+ if (0xf0000 <= core_offs && core_end <= 0xf1000) {
+ *phys_pfn = PFN_DOWN(core_phys_addr + core_offs);
+ return 0;
+ }
+
+ return -EINVAL;
+ }
+
+ list_for_each_entry(mapping, &elink->mappings_list, list) {
+ if (mapping->emesh_start <= start &&
+ end <= mapping->emesh_start + mapping->size) {
+ offs = start - mapping->emesh_start;
+ *phys_pfn = PFN_DOWN(mapping->start + offs);
+ return 0;
+ }
+ }
+
+ if (elink->regs_start <= start &&
+ end <= elink->regs_start + elink->regs_size) {
+ if (epiphany.param_unsafe_access) {
+ *phys_pfn = PFN_DOWN(start);
+ return 0;
+ } else
+ return -EACCES;
+ }
+
+ return -EINVAL;
+}
+
+static unsigned int epiphany_vm_fault(struct vm_fault *vmf)
+{
+ unsigned long phys_pfn;
+ struct elink_device *elink = vma_to_elink(vmf->vma);
+ int ret;
+
+ if (mutex_lock_interruptible(&epiphany.driver_lock)) {
+ ret = -ERESTARTSYS;
+ goto out;
+ }
+
+ if (epiphany.thermal_disallow) {
+ pr_info_ratelimited("%s: Temperature outside operating range. Sending SIGBUS to process %s (pid: %d)\n",
+ __func__, current->comm,
+ task_pid_nr(current));
+ ret = -EACCES;
+ goto out_unlock;
+ }
+
+ ret = mesh_pfn_to_phys_pfn(elink, vmf->pgoff, &phys_pfn);
+ if (ret)
+ goto out_unlock;
+
+ ret = vmf_insert_pfn(vmf->vma, (unsigned long)vmf->address, phys_pfn);
+
+out_unlock:
+ mutex_unlock(&epiphany.driver_lock);
+out:
+ switch (ret) {
+ case 0:
+ case -ERESTARTSYS:
+ case -EINTR:
+ case -EBUSY:
+ return VM_FAULT_NOPAGE;
+ default:
+ return VM_FAULT_SIGBUS;
+ }
+}
+
+static const struct vm_operations_struct epiphany_vm_ops = {
+ .open = epiphany_vm_open,
+ .close = epiphany_vm_close,
+ .fault = epiphany_vm_fault,
+#ifdef CONFIG_HAVE_IOREMAP_PROT
+ .access = generic_access_phys
+#endif
+};
+
+static int _elink_char_mmap(struct elink_device *elink,
+ struct vm_area_struct *vma)
+{
+ int ret;
+ unsigned long off, size, coreoff, phys_off;
+ struct mem_region *mapping;
+ phys_addr_t core_phys = 0;
+
+ vma->vm_ops = &epiphany_vm_ops;
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ vma->vm_flags |= VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP;
+
+ off = vma->vm_pgoff << PAGE_SHIFT;
+ size = vma->vm_end - vma->vm_start;
+
+ /* Check memory mappings first. These can be inside the Epiphany memory
+ * region of an elink. But they are flat 1D mappings with no holes so
+ * they are easy to check. */
+ list_for_each_entry(mapping, &elink->mappings_list, list) {
+ if (mapping->emesh_start <= off &&
+ off + size <= mapping->emesh_start + mapping->size) {
+ epiphany_vm_open(vma);
+ return 0;
+ }
+ }
+
+ if (elink->regs_start <= off &&
+ off + size <= elink->regs_start + elink->regs_size) {
+ if (epiphany.param_unsafe_access) {
+ vma->vm_flags |= VM_IO;
+ epiphany_vm_open(vma);
+ return 0;
+ }
+ else
+ return -EACCES;
+ }
+
+ /* TODO: Need a fault handler to make this safe. We want to allow
+ * mmapping an entire mesh/chip, which means there can be holes which
+ * should result in segfaults if accessed. */
+ coreoff = off >> COREID_SHIFT;
+ ret = coreid_to_phys(elink, (u16) coreoff, &core_phys);
+ phys_off = core_phys | (off & COREID_MASK);
+ if (!ret && phys_off - elink->emesh_start + size <= elink->emesh_size) {
+ vma->vm_flags |= VM_IO;
+ epiphany_vm_open(vma);
+ return 0;
+ }
+
+ dev_dbg(&elink->dev,
+ "elink_char_mmap: invalid request to map 0x%08lx, length 0x%08lx bytes\n",
+ off, size);
+
+ return -EINVAL;
+}
+
+static int elink_char_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct elink_device *elink = file_to_elink(file);
+
+ return _elink_char_mmap(elink, vma);
+}
+
+static int mesh_char_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct mesh_device *mesh = file_to_mesh(file);
+ struct array_device *array;
+ struct elink_device *elink;
+
+ array = mesh->arrays[0];
+ if (!array)
+ return -EINVAL;
+
+ elink = array->connections[array->parent_side].elink;
+ if (!elink)
+ return -EINVAL;
+
+ return _elink_char_mmap(elink, vma);
+}
+
+static long elink_char_ioctl_elink_get_mappings(struct elink_device *elink,
+ unsigned long arg)
+{
+ struct e_mappings_info *info;
+ struct e_mappings_info *dest = (struct e_mappings_info *) arg;
+ struct mem_region *mapping;
+ int ret = 0;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ list_for_each_entry(mapping, &elink->mappings_list, list) {
+ info->mappings[info->nmappings].emesh_addr =
+ mapping->emesh_start;
+ info->mappings[info->nmappings].size = mapping->size;
+ info->nmappings++;
+ }
+
+ ret = copy_to_user(dest, info, sizeof(*info));
+
+ kfree(info);
+
+ if (ret) {
+ dev_dbg(&elink->dev, "elink get mappings ioctl failed.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static long elink_char_ioctl_elink_reset(struct elink_device *elink)
+{
+ int ret;
+
+ if (epiphany_vm_freeze(true))
+ return -ERESTARTSYS;
+
+ if (epiphany.thermal_disallow) {
+ ret = -EACCES;
+ goto out;
+ }
+
+ ret = epiphany_reset();
+
+out:
+ epiphany_vm_unfreeze();
+ return ret;
+}
+
+static long elink_char_ioctl_elink_probe(struct elink_device *elink,
+ unsigned long arg)
+{
+ struct e_elink_info info = {};
+ struct e_elink_info *dest = (struct e_elink_info *) arg;
+ struct connection *conn;
+ int ret = 0, i;
+
+ info.dev = elink->cdev.dev;
+ info.version = elink->version.reg;
+ info.connection_type = elink->connection.type;
+ switch (elink->connection.type) {
+ case E_CONN_DISCONNECTED:
+ break;
+ case E_CONN_ARRAY:
+ info.array.id = elink->connection.array->id;
+ info.array.chip_type = elink->connection.array->chip_type;
+ info.array.chip_rows = elink->connection.array->chip_rows;
+ info.array.chip_cols = elink->connection.array->chip_cols;
+ info.array.parent_side = elink->connection.array->parent_side;
+ info.array.mesh_dev = elink->connection.array->mesh->cdev.dev;
+
+ for (i = 0; i < E_SIDE_MAX; i++) {
+ conn = &elink->connection.array->connections[i];
+ info.array.connections[i].type = conn->type;
+ switch (conn->type) {
+ case E_CONN_DISCONNECTED:
+ break;
+ case E_CONN_ELINK:
+ info.array.connections[i].dev =
+ conn->elink->cdev.dev;
+ break;
+ case E_CONN_ARRAY:
+ info.array.connections[i].id =
+ conn->array->id;
+ break;
+ default:
+ /* TODO: Implement other types */
+ WARN_ON(true);
+ break;
+ }
+ }
+ break;
+ default:
+ /* TODO: Implement other types */
+ WARN_ON(true);
+ break;
+ }
+
+ ret = copy_to_user(dest, &info, sizeof(info));
+ if (ret) {
+ dev_dbg(&elink->dev, "elink probe ioctl failed.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static long elink_char_ioctl_mailbox_read(struct file *file,
+ void __user *dst)
+{
+ int ret;
+ struct e_mailbox_msg msg;
+ struct elink_device *elink = file_to_elink(file);
+
+ if (mutex_lock_interruptible(&epiphany.driver_lock))
+ return -ERESTARTSYS;
+
+ if (epiphany.thermal_disallow) {
+ mutex_unlock(&epiphany.driver_lock);
+ return -EACCES;
+ }
+
+ while (!atomic_read(&elink->mailbox_maybe_not_empty)
+ || elink_mailbox_empty_p(elink)) {
+ atomic_set(&elink->mailbox_maybe_not_empty, 0);
+
+ mutex_unlock(&epiphany.driver_lock);
+
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+
+ if (mutex_lock_interruptible(&epiphany.driver_lock))
+ return -ERESTARTSYS;
+
+ elink_mailbox_irq_enable(elink);
+
+ mutex_unlock(&epiphany.driver_lock);
+
+ ret = wait_event_interruptible_timeout(
+ elink->mailbox_wait,
+ atomic_read(&elink->mailbox_maybe_not_empty),
+ msecs_to_jiffies(100));
+ if (ret == -ERESTARTSYS)
+ return -ERESTARTSYS;
+
+ if (mutex_lock_interruptible(&epiphany.driver_lock))
+ return -ERESTARTSYS;
+
+ if (epiphany.thermal_disallow) {
+ mutex_unlock(&epiphany.driver_lock);
+ return -EACCES;
+ }
+ }
+
+ msg.from = reg_read(elink->regs, ELINK_MAILBOXLO);
+ msg.data = reg_read(elink->regs, ELINK_MAILBOXHI);
+
+ mutex_unlock(&epiphany.driver_lock);
+
+ if (copy_to_user(dst, &msg, sizeof(msg)))
+ return -EACCES;
+
+ return 0;
+}
+
+static long elink_char_ioctl_mailbox_count(struct file *file)
+{
+ long count;
+ struct elink_device *elink = file_to_elink(file);
+
+ if (mutex_lock_interruptible(&epiphany.driver_lock))
+ return -ERESTARTSYS;
+
+ if (epiphany.thermal_disallow) {
+ mutex_unlock(&epiphany.driver_lock);
+ return -EACCES;
+ }
+
+ count = elink_mailbox_count(elink);
+
+ mutex_unlock(&epiphany.driver_lock);
+
+ return count;
+}
+
+static long elink_char_ioctl_thermal_disallow(struct file *file)
+{
+ struct elink_device *elink;
+ int ret = 0;
+
+ if (epiphany_vm_freeze(true))
+ return -ERESTARTSYS;
+
+ if (epiphany.thermal_disallow)
+ goto out;
+
+ epiphany.thermal_disallow = true;
+
+ /* Up refcount so it doesn't drop to 0 */
+ epiphany.u_count++;
+
+ list_for_each_entry(elink, &epiphany.elink_list, list)
+ elink_disable(elink);
+
+ list_for_each_entry(elink, &epiphany.elink_list, list)
+ elink_regulator_disable(elink);
+
+out:
+ epiphany_vm_unfreeze();
+ return ret;
+}
+
+static long elink_char_ioctl_thermal_allow(struct file *file)
+{
+ struct elink_device *elink;
+ long ret = 0;
+
+ if (epiphany_vm_freeze(true))
+ return -ERESTARTSYS;
+
+ if (!epiphany.thermal_disallow)
+ goto out;
+
+ epiphany.thermal_disallow = false;
+
+ /* Restore refcount */
+ epiphany.u_count--;
+
+ list_for_each_entry(elink, &epiphany.elink_list, list)
+ elink_regulator_enable(elink);
+
+ ret = epiphany_reset();
+
+out:
+ epiphany_vm_unfreeze();
+ return ret;
+}
+
+static long elink_char_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct elink_device *elink = file_to_elink(file);
+ int err = 0;
+
+ /* ??? TODO: Reset elink only instead of entire system ? */
+ /* struct elink_device *elink = file_to_elink(file)->epiphany; */
+
+ if (_IOC_TYPE(cmd) != E_IOCTL_MAGIC)
+ return -ENOTTY;
+
+ if (_IOC_NR(cmd) > E_IOCTL_MAXNR)
+ return -ENOTTY;
+
+ /* Do we really need to do this check?
+ * Isn't copy_to_user() already doing that? */
+ if (_IOC_DIR(cmd) & _IOC_READ) {
+ err =
+ !access_ok((void __user *)arg, _IOC_SIZE(cmd));
+ } else if (_IOC_DIR(cmd) & _IOC_WRITE) {
+ err =
+ !access_ok((void __user *)arg,
+ _IOC_SIZE(cmd));
+ }
+
+ if (err)
+ return -EFAULT;
+
+ switch (cmd) {
+ case E_IOCTL_RESET:
+ return elink_char_ioctl_elink_reset(elink);
+ case E_IOCTL_ELINK_PROBE:
+ return elink_char_ioctl_elink_probe(elink, arg);
+ case E_IOCTL_GET_MAPPINGS:
+ return elink_char_ioctl_elink_get_mappings(elink, arg);
+ case E_IOCTL_MAILBOX_READ:
+ return elink_char_ioctl_mailbox_read(file, (void __user *) arg);
+ case E_IOCTL_MAILBOX_COUNT:
+ return elink_char_ioctl_mailbox_count(file);
+ case E_IOCTL_THERMAL_DISALLOW:
+ return elink_char_ioctl_thermal_disallow(file);
+ case E_IOCTL_THERMAL_ALLOW:
+ return elink_char_ioctl_thermal_allow(file);
+
+ default:
+ return -ENOTTY;
+ }
+
+ return -ENOTTY;
+}
+
+/* TODO: Currently we only support meshes with one chip-array ... */
+static long mesh_char_ioctl_probe(struct mesh_device *mesh, unsigned long arg)
+{
+ struct array_device *array;
+ struct e_mesh_info *info;
+ struct e_mesh_info *dest = (struct e_mesh_info *) arg;
+ struct connection *conn;
+ int ret, i;
+
+ if (!mesh->arrays)
+ return -ENODEV;
+
+ array = mesh->arrays[0];
+ if (!array)
+ return -ENODEV;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->dev = mesh->cdev.dev;
+ info->chip_type = array->chip_type;
+
+ info->narrays = 1;
+ info->arrays[0].id = array->id;
+ info->arrays[0].chip_type = array->chip_type;
+ info->arrays[0].chip_rows = array->chip_rows;
+ info->arrays[0].chip_cols = array->chip_cols;
+ info->arrays[0].parent_side = array->parent_side;
+ info->arrays[0].mesh_dev = array->mesh->cdev.dev;
+
+ for (i = 0; i < E_SIDE_MAX; i++) {
+ conn = &array->connections[i];
+ info->arrays[0].connections[i].type = conn->type;
+ switch (conn->type) {
+ case E_CONN_DISCONNECTED:
+ break;
+ case E_CONN_ELINK:
+ info->arrays[0].connections[i].dev =
+ conn->elink->cdev.dev;
+ break;
+ case E_CONN_ARRAY:
+ info->arrays[0].connections[i].id =
+ conn->array->id;
+ break;
+ default:
+ /* TODO: Implement other types */
+ WARN_ON(true);
+ break;
+ }
+ }
+
+ ret = copy_to_user(dest, info, sizeof(*info));
+
+ kfree(info);
+
+ if (ret) {
+ dev_dbg(&mesh->dev, "mesh probe ioctl failed.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static long mesh_char_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct mesh_device *mesh = file_to_mesh(file);
+ struct array_device *array;
+ struct elink_device *elink;
+ int err = 0;
+
+ array = mesh->arrays[0];
+ if (!array)
+ return -EINVAL;
+
+ elink = array->connections[array->parent_side].elink;
+ if (!elink)
+ return -EINVAL;
+
+
+ /* ??? TODO: Reset elink only instead of entire system ? */
+ /* struct elink_device *elink = file_to_elink(file)->epiphany; */
+
+ if (_IOC_TYPE(cmd) != E_IOCTL_MAGIC)
+ return -ENOTTY;
+
+ if (_IOC_NR(cmd) > E_IOCTL_MAXNR)
+ return -ENOTTY;
+
+ /* Do we really need to do this check?
+ * Isn't copy_to_user() already doing that? */
+ if (_IOC_DIR(cmd) & _IOC_READ) {
+ err =
+ !access_ok((void __user *)arg, _IOC_SIZE(cmd));
+ } else if (_IOC_DIR(cmd) & _IOC_WRITE) {
+ err =
+ !access_ok((void __user *)arg,
+ _IOC_SIZE(cmd));
+ }
+
+ if (err)
+ return -EFAULT;
+
+ switch (cmd) {
+ case E_IOCTL_RESET:
+ return elink_char_ioctl_elink_reset(elink);
+ case E_IOCTL_MESH_PROBE:
+ return mesh_char_ioctl_probe(mesh, arg);
+
+ case E_IOCTL_GET_MAPPINGS:
+ return elink_char_ioctl_elink_get_mappings(elink, arg);
+
+ default:
+ return -ENOTTY;
+ }
+
+ return -ENOTTY;
+}
+
+static int minor_get(void *ptr)
+{
+ int retval;
+
+ idr_preload(GFP_KERNEL);
+ spin_lock(&epiphany.minor_idr_lock);
+ retval = idr_alloc(&epiphany.minor_idr, ptr, 0, E_DEV_NUM_MINORS,
+ GFP_NOWAIT);
+ spin_unlock(&epiphany.minor_idr_lock);
+ idr_preload_end();
+ return retval;
+}
+
+static void minor_put(int minor)
+{
+ spin_lock(&epiphany.minor_idr_lock);
+ idr_remove(&epiphany.minor_idr, minor);
+ spin_unlock(&epiphany.minor_idr_lock);
+}
+
+static const struct file_operations elink_char_driver_ops = {
+ .owner = THIS_MODULE,
+ .open = char_open,
+ .release = char_release,
+ .mmap = elink_char_mmap,
+ .unlocked_ioctl = elink_char_ioctl
+};
+
+static const struct file_operations mesh_char_driver_ops = {
+ .owner = THIS_MODULE,
+ .open = mesh_char_open,
+ .release = char_release,
+ .mmap = elink_char_mmap,
+ .unlocked_ioctl = elink_char_ioctl
+};
+
+static void mesh_device_release(struct device *dev)
+{
+ struct mesh_device *mesh = device_to_mesh(dev);
+
+ dev_dbg(dev, "release\n");
+ kfree(mesh->arrays);
+ kfree(mesh);
+}
+
+/* TODO: Idea here is that we should try attach array to an existing mesh if
+ * possible. Otherwise create a new mesh. Now we just create a new mesh for
+ * each chip array. */
+static int mesh_attach_or_register(struct array_device *array)
+{
+ struct mesh_device *mesh;
+ int ret;
+ dev_t devt;
+
+ mesh = kzalloc(sizeof(*mesh), GFP_KERNEL);
+ if (!mesh)
+ return -ENOMEM;
+
+ mesh->arrays = kcalloc(1 + 1, sizeof(*(mesh->arrays)), GFP_KERNEL);
+ if (!mesh->arrays) {
+ kfree(mesh);
+ return -ENOMEM;
+ }
+
+ mesh->arrays[0] = array;
+
+ ret = minor_get(mesh);
+ if (ret < 0)
+ goto err_minor;
+
+ mesh->minor = ret;
+ devt = MKDEV(MAJOR(epiphany.devt), mesh->minor);
+ cdev_init(&mesh->cdev, &mesh_char_driver_ops);
+ mesh->cdev.owner = THIS_MODULE;
+
+ ret = cdev_add(&mesh->cdev, devt, 1);
+ if (ret) {
+ dev_err(&array->dev,
+ "CHAR registration failed for mesh device\n");
+ goto err_cdev_add;
+ }
+
+ mesh->dev.class = &epiphany.class;
+ mesh->dev.parent = NULL;
+ mesh->dev.devt = devt;
+ mesh->dev.groups = NULL;
+ mesh->dev.release = mesh_device_release;
+ /* TODO: Use separate counter per char dev type */
+ dev_set_name(&mesh->dev, "mesh%d",
+ atomic_inc_return(&epiphany.mesh_counter) - 1);
+
+ ret = device_register(&mesh->dev);
+ if (ret) {
+ dev_err(&array->dev, "unable to create mesh device\n");
+ goto err_dev_create;
+ }
+
+ mutex_lock(&epiphany.driver_lock);
+ array->mesh = mesh;
+ list_add_tail(&mesh->list, &epiphany.mesh_list);
+ mutex_unlock(&epiphany.driver_lock);
+
+ dev_dbg(&mesh->dev, "mesh_attach_or_register: registered char device\n");
+ return 0;
+
+err_dev_create:
+ cdev_del(&mesh->cdev);
+err_cdev_add:
+ minor_put(mesh->minor);
+err_minor:
+ kfree(mesh->arrays);
+ kfree(mesh);
+
+ return ret;
+}
+
+void mesh_unregister(struct mesh_device *mesh)
+{
+ struct array_device **array;
+
+ mutex_lock(&epiphany.driver_lock);
+ for (array = &mesh->arrays[0]; *array; array++)
+ (*array)->mesh = NULL;
+ list_del(&mesh->list);
+ mutex_unlock(&epiphany.driver_lock);
+
+ cdev_del(&mesh->cdev);
+
+ device_unregister(&mesh->dev);
+
+ minor_put(mesh->minor);
+}
+
+
+/* Place holder */
+static const struct attribute_group *dev_attr_groups_array[] = {
+ NULL
+};
+
+static int array_register(struct array_device *array,
+ struct elink_device *elink)
+{
+ int ret;
+
+ array->chip_type = elink->chip_type;
+
+ array->connections[array->parent_side].type = E_CONN_ELINK;
+ array->connections[array->parent_side].elink = elink;
+
+ array->dev.class = &epiphany.class;
+ array->dev.parent = &elink->dev;
+ array->dev.groups = dev_attr_groups_array;
+ dev_set_name(&array->dev, "array%d",
+ atomic_inc_return(&epiphany.array_counter) - 1);
+
+ ret = device_register(&array->dev);
+ if (ret) {
+ dev_err(&array->dev, "unable to create device array device\n");
+ goto err_dev_create;
+ }
+
+ /* There can only be one array per elink, no name conflicts */
+ ret = sysfs_create_link(&elink->dev.kobj, &array->dev.kobj, "array");
+ if (ret)
+ dev_info(&array->dev, "arrays: failed creating symlink\n");
+
+ mutex_lock(&epiphany.driver_lock);
+ /* TODO: roll back if elink is not disconnected */
+ WARN_ON(elink->connection.type != E_CONN_DISCONNECTED);
+ if (elink->coreid_is_noop && array->id != 0x808) {
+ dev_warn(&array->dev,
+ "arrays: non default id and elink coreid is no-op\n");
+ }
+
+ if (elink->coreid_pinout == -1) {
+ dev_dbg(&array->dev,
+ "arrays: setting elink coreid to array id 0x%03x\n",
+ array->id);
+ elink->coreid_pinout = array->id;
+ }
+ elink->connection.type = E_CONN_ARRAY;
+ elink->connection.array = array;
+ elink->connection.side = array->parent_side;
+ list_add_tail(&array->list, &epiphany.chip_array_list);
+ mutex_unlock(&epiphany.driver_lock);
+
+ ret = mesh_attach_or_register(array);
+ if (ret) {
+ dev_info(&array->dev,
+ "array_register: could not attach to any mesh\n");
+ }
+
+ dev_dbg(&array->dev, "array_register: registered device\n");
+ return 0;
+
+err_dev_create:
+ return ret;
+}
+
+static void array_unregister(struct array_device *array)
+{
+ struct elink_device *elink = device_to_elink(array->dev.parent);
+ struct array_device **arrcurr, **arrprev;
+
+ WARN_ON(!elink);
+
+ if (elink)
+ sysfs_remove_link(&elink->dev.kobj, "array");
+
+ mutex_lock(&epiphany.driver_lock);
+ list_del(&array->list);
+ if (elink) {
+ elink->connection.type = E_CONN_DISCONNECTED;
+ elink->connection.array = NULL;
+ }
+ if (array->mesh) {
+ /* Delete this array from list */
+ arrprev = NULL;
+ for (arrcurr = &array->mesh->arrays[0]; *arrcurr; arrcurr++) {
+ if (!arrprev) {
+ if (*arrcurr == array)
+ *arrcurr = NULL;
+ arrprev = arrcurr;
+ } else {
+ *arrprev = *arrcurr;
+ arrprev++;
+ }
+ }
+ }
+ array->mesh = NULL;
+ mutex_unlock(&epiphany.driver_lock);
+
+ device_unregister(&array->dev);
+}
+
+static struct array_device *array_of_probe(struct platform_device *pdev)
+{
+ struct platform_device *ppdev =
+ to_platform_device(pdev->dev.parent);
+ struct elink_device *elink;
+ struct array_device *array;
+ enum e_link_side side;
+ u32 reg[4];
+ int ret;
+
+ elink = platform_get_drvdata(ppdev);
+ if (!elink) {
+ /* This is a bug. array device should never be instantiated
+ * unless parent elink probe did succeed. */
+ WARN_ON(true);
+ dev_err(&pdev->dev, "No parent elink\n");
+ return ERR_PTR(-ENXIO);
+ }
+
+ array = devm_kzalloc(&pdev->dev, sizeof(*array), GFP_KERNEL);
+ if (!array)
+ return ERR_PTR(-ENOMEM);
+
+ array->phandle = pdev->dev.of_node->phandle;
+
+ /* There is probably a better way for doing this */
+ ret = of_property_read_u32_array(pdev->dev.of_node, "reg", reg, 4);
+ if (ret) {
+ dev_err(&pdev->dev, "arrays: invalid reg property\n");
+ return ERR_PTR(ret);
+ }
+
+ array->id = (u16) reg[0];
+ side = reg[1];
+ array->chip_rows = reg[2];
+ array->chip_cols = reg[3];
+
+ switch (side) {
+ case E_SIDE_N ... E_SIDE_W:
+ array->parent_side = side;
+ break;
+
+ default:
+ dev_err(&pdev->dev, "Invalid side %u\n", (u32) side);
+ return ERR_PTR(-EINVAL);
+ }
+
+ ret = array_register(array, elink);
+ if (ret)
+ return ERR_PTR(ret);
+
+ dev_dbg(&pdev->dev, "arrays: added connection\n");
+ return array;
+}
+
+static int array_platform_probe(struct platform_device *pdev)
+{
+ struct array_device *array;
+ int ret;
+
+ array = devm_kzalloc(&pdev->dev, sizeof(*array), GFP_KERNEL);
+ if (!array)
+ return -ENOMEM;
+
+ array = array_of_probe(pdev);
+ if (IS_ERR(array)) {
+ ret = PTR_ERR(array);
+ if (ret == -EPROBE_DEFER)
+ dev_info(&pdev->dev, "Deferring probe.\n");
+ else
+ dev_warn(&pdev->dev, "Failed parsing device tree\n");
+
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, array);
+
+ return 0;
+}
+
+static int array_platform_remove(struct platform_device *pdev)
+{
+ struct array_device *array = platform_get_drvdata(pdev);
+
+ array_unregister(array);
+
+ dev_dbg(&pdev->dev, "device removed\n");
+
+ return 0;
+}
+
+static const struct of_device_id array_of_match[] = {
+ { .compatible = "adapteva,chip-array" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, array_of_match);
+
+static struct platform_driver array_driver = {
+ .probe = array_platform_probe,
+ .remove = array_platform_remove,
+ .driver = {
+ .name = "array",
+ .of_match_table = of_match_ptr(array_of_match)
+ }
+};
+
+ssize_t elink_attr_vdd_current_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret = 0, vdd_curr;
+ struct elink_device *elink = device_to_elink(dev);
+
+ if (mutex_lock_interruptible(&epiphany.driver_lock))
+ return -ERESTARTSYS;
+
+ if (!elink->supply) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ vdd_curr = regulator_get_voltage(elink->supply);
+ if (vdd_curr < 0) {
+ ret = vdd_curr;
+ goto out;
+ }
+
+ ret = sprintf(buf, "%d\n", vdd_curr);
+
+out:
+ mutex_unlock(&epiphany.driver_lock);
+ return ret;
+}
+
+ssize_t elink_attr_vdd_wanted_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret = 0;
+ struct elink_device *elink = device_to_elink(dev);
+
+ if (mutex_lock_interruptible(&epiphany.driver_lock))
+ return -ERESTARTSYS;
+
+ if (!elink->supply) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ ret = sprintf(buf, "%d\n", elink->vdd_wanted);
+
+out:
+ mutex_unlock(&epiphany.driver_lock);
+ return ret;
+}
+
+/* Must hold driver lock before calling this function */
+static int _elink_attr_vdd_wanted_store(struct elink_device *elink, int vdd)
+{
+ unsigned int step;
+ const struct epiphany_chip_info *cinfo =
+ &epiphany_chip_info[elink->chip_type];
+
+ if (!elink->supply)
+ return -ENODEV;
+
+ /* Zero or below resets to default vdd */
+ if (vdd <= 0) {
+ elink->vdd_wanted = -1;
+ return 0;
+ }
+
+ /* Round vdd down to closest step */
+ step = regulator_get_linear_step(elink->supply);
+ vdd = vdd - vdd % step;
+
+ if (vdd < cinfo->vdd_min || cinfo->vdd_max < vdd)
+ return -ERANGE;
+
+ elink->vdd_wanted = vdd;
+
+ return 0;
+}
+
+static ssize_t elink_attr_vdd_wanted_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct elink_device *elink = device_to_elink(dev);
+ int ret, data;
+
+ ret = kstrtoint(buf, 10, &data);
+ if (ret < 0)
+ return ret;
+
+ if (mutex_lock_interruptible(&epiphany.driver_lock))
+ return -ERESTARTSYS;
+
+ ret = _elink_attr_vdd_wanted_store(elink, data);
+ if (!ret)
+ ret = len;
+
+ mutex_unlock(&epiphany.driver_lock);
+ return ret;
+}
+
+#define DEVICE_ATTR_PFX(_pfx, _name, _mode, _show, _store) \
+ struct device_attribute dev_attr_##_pfx##_##_name = \
+ __ATTR(_name, _mode, _show, _store)
+
+static DEVICE_ATTR_PFX(elink, vdd_current, 0444, elink_attr_vdd_current_show,
+ NULL);
+static DEVICE_ATTR_PFX(elink, vdd_wanted, 0644, elink_attr_vdd_wanted_show,
+ elink_attr_vdd_wanted_store);
+
+static struct attribute *dev_attrs_elink[] = {
+ &dev_attr_elink_vdd_current.attr,
+ &dev_attr_elink_vdd_wanted.attr,
+ NULL
+};
+
+static struct attribute_group dev_attr_group_elink = {
+ .attrs = dev_attrs_elink
+};
+
+
+
+/* Place holder */
+static const struct attribute_group *dev_attr_groups_elink[] = {
+ &dev_attr_group_elink,
+ NULL
+};
+
+static int elink_clks_get(struct elink_device *elink)
+{
+ int ret, i;
+
+ /* We might not need clocks for e.g., PCI. Error should have been
+ * raised in probe */
+ if (!elink->clocks)
+ return 0;
+
+ for (i = 0; elink->clocks[i]; i++) {
+ ret = clk_prepare_enable(elink->clocks[i]);
+ if (ret)
+ goto err;
+ }
+
+ return 0;
+
+err:
+ dev_err(&elink->dev, "elink_clks_get: failed clk=%d, err=%d\n", i, ret);
+
+ for (i--; i >= 0; i--)
+ clk_disable_unprepare(elink->clocks[i]);
+
+ return ret;
+}
+
+static void elink_clks_put(struct elink_device *elink)
+{
+ int i;
+
+ if (!elink->clocks)
+ return;
+
+ /* Release in opposite order (not that it really matters atm). */
+
+ for (i = 0; elink->clocks[i]; i++)
+ ;
+
+ for (i--; i >= 0; i--)
+ clk_disable_unprepare(elink->clocks[i]);
+}
+
+static int elink_probe(struct elink_device *elink)
+{
+ union elink_version version;
+ int ret = 0;
+
+ /* We must use epiphany_get() / epiphany_put() here so that a release
+ * of another device (mesh/elink) doesn't drop epiphany.u_count to
+ * zero. However, since this elink is yet to be added to
+ * epiphany.elink_list at this stage, we must also explicitly
+ * enable/disable its power regulator. (The FPGA elink usually gets
+ * clock from the chip it's connected to) */
+
+ epiphany_get();
+
+ elink->chip_type = E_CHIP_UNKNOWN;
+ if (elink_regulator_enable(elink)) {
+ ret = -EIO;
+ goto err_regulator;
+ }
+
+ elink_reset(elink);
+
+ version.reg = reg_read(elink->regs, ELINK_VERSION);
+ /* HACK: This is 0 in current FPGA elink, guess default.*/
+ if (!version.reg) {
+ dev_warn(&elink->dev,
+ "elink: version field empty. Using default platform.\n");
+ version.platform = E_PLATF_E16_7Z020_GPIO;
+ }
+
+ if (!version.platform || version.platform >= E_PLATF_MAX) {
+ dev_err(&elink->dev, "elink: unsupported platform: 0x%x.\n",
+ version.platform);
+ ret = -EINVAL;
+ goto err_platform;
+ }
+
+ if (elink->coreid_is_noop)
+ elink->coreid_pinout = reg_read(elink->regs, ELINK_CHIPID);
+
+ elink->version = version;
+ elink->chip_type = elink_platform_chip_match[version.platform];
+
+ dev_info(&elink->dev, "Epiphany FPGA elink at address %pa\n",
+ &elink->regs_start);
+ dev_info(&elink->dev, "platform %02x revision %02x\n",
+ version.platform,
+ version.revision);
+
+err_platform:
+ elink_regulator_disable(elink);
+err_regulator:
+ epiphany_put();
+
+ return ret;
+}
+
+static int elink_register(struct elink_device *elink)
+{
+ int ret;
+ dev_t devt;
+
+ elink->vdd_wanted = -1;
+
+ ret = elink_clks_get(elink);
+ if (ret)
+ goto err_clks;
+
+ ret = minor_get(elink);
+ if (ret < 0)
+ goto err_minor;
+
+ elink->minor = ret;
+ devt = MKDEV(MAJOR(epiphany.devt), elink->minor);
+ cdev_init(&elink->cdev, &elink_char_driver_ops);
+ elink->cdev.owner = THIS_MODULE;
+
+ init_waitqueue_head(&elink->mailbox_wait);
+ atomic_set(&elink->mailbox_maybe_not_empty, 1);
+ INIT_WORK(&elink->mailbox_irq_work, elink_mailbox_irq_handler_bh);
+
+ ret = cdev_add(&elink->cdev, devt, 1);
+ if (ret) {
+ dev_err(&elink->dev,
+ "CHAR registration failed for elink driver\n");
+ goto err_cdev_add;
+ }
+
+ elink->dev.class = &epiphany.class;
+ elink->dev.parent = NULL;
+ elink->dev.devt = devt;
+ elink->dev.groups = dev_attr_groups_elink;
+ dev_set_name(&elink->dev, "elink%d",
+ atomic_inc_return(&epiphany.elink_counter) - 1);
+
+ ret = elink_probe(elink);
+ if (ret) {
+ dev_err(&elink->dev, "probing failed\n");
+ goto err_probe;
+ }
+
+ ret = device_register(&elink->dev);
+ if (ret) {
+ dev_err(&elink->dev, "unable to create elink device\n");
+ goto err_dev_create;
+ }
+
+ mutex_lock(&epiphany.driver_lock);
+ list_add_tail(&elink->list, &epiphany.elink_list);
+ mutex_unlock(&epiphany.driver_lock);
+
+ /* Increase reference count if power management should be disabled */
+ if (epiphany.param_nopm) {
+ ret = epiphany_get();
+ if (ret)
+ goto err_epiphany_get;
+ }
+
+
+ dev_dbg(&elink->dev, "elink_register: registered char device\n");
+ return 0;
+
+err_epiphany_get:
+err_dev_create:
+ cdev_del(&elink->cdev);
+err_probe:
+err_cdev_add:
+ minor_put(elink->minor);
+err_minor:
+ elink_clks_put(elink);
+err_clks:
+ return ret;
+}
+
+void elink_unregister(struct elink_device *elink)
+{
+ struct list_head *curr, *next;
+
+ /* Decrease reference count to zero if power management is disabled */
+ if (epiphany.param_nopm)
+ epiphany_put();
+
+ mutex_lock(&epiphany.driver_lock);
+ list_del(&elink->list);
+ mutex_unlock(&epiphany.driver_lock);
+
+ cdev_del(&elink->cdev);
+
+ device_unregister(&elink->dev);
+
+ minor_put(elink->minor);
+
+ elink_clks_put(elink);
+
+ list_for_each_safe(curr, next, &elink->mem_region_list)
+ list_del(curr);
+
+ list_for_each_safe(curr, next, &elink->mappings_list)
+ list_del(curr);
+
+ /* Everything else is allocated with devm_* */
+}
+
+static int elink_of_probe_default_mappings(struct platform_device *pdev,
+ struct elink_device *elink)
+{
+ struct property *prop;
+ u32 emesh_start, phys_start, size;
+ const __be32 *p = NULL;
+ struct mem_region *region, *mapping;
+ int i;
+
+ prop = of_find_property(pdev->dev.of_node, "adapteva,mmu", &i);
+ if (!prop) {
+ dev_dbg(&pdev->dev, "adapteva,mmu property is missing\n");
+ return 0;
+ }
+
+ i /= sizeof(u32);
+
+ if (i == 0 || i % 3) {
+ dev_err(&pdev->dev, "adapteva,mmu property is invalid\n");
+ return -EINVAL;
+ }
+
+ i /= 3;
+
+
+ for (; i > 0; i--) {
+ p = of_prop_next_u32(prop, p, &emesh_start);
+ if (!p)
+ return -EINVAL;
+ p = of_prop_next_u32(prop, p, &phys_start);
+ if (!p)
+ return -EINVAL;
+ p = of_prop_next_u32(prop, p, &size);
+ if (!p)
+ return -EINVAL;
+
+ list_for_each_entry(region, &elink->mem_region_list, list) {
+ if (region->start > phys_start ||
+ phys_start - region->start + size > region->size)
+ continue;
+ mapping = devm_kzalloc(&pdev->dev, sizeof(*mapping),
+ GFP_KERNEL);
+
+ mapping->size = size;
+ mapping->start = phys_start;
+ mapping->emesh_start = emesh_start;
+
+ list_add_tail(&mapping->list, &elink->mappings_list);
+ dev_dbg(&pdev->dev,
+ "added mapping: <0x%08x 0x%08x 0x%08x>\n",
+ emesh_start, phys_start, size);
+
+ break;
+ }
+ }
+ return 0;
+}
+
+static int elink_of_probe_reserved_mem(struct platform_device *pdev,
+ struct elink_device *elink)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *mem_node;
+ struct mem_region *mem_region;
+ struct resource res;
+ int ret = 0;
+
+ /* TODO: Only one memory region supported for now */
+ mem_node = of_parse_phandle(dev->of_node, "memory-region", 0);
+ if (!mem_node) {
+ /* TODO: When elink firmware has mmu we should accept no
+ * memory region and downgrade to a warning. */
+ dev_err(dev, "reserved-mem: no memory-region\n");
+ return -ENODEV;
+ }
+
+ ret = of_address_to_resource(mem_node, 0, &res);
+ if (ret) {
+ dev_warn(dev, "reserved-mem: no resource\n");
+ goto out;
+ }
+
+ if (!devm_request_mem_region(dev, res.start, resource_size(&res),
+ pdev->name)) {
+ dev_warn(dev, "reserved-mem: failed requesting mem region\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ mem_region = devm_kzalloc(dev, sizeof(*mem_region), GFP_KERNEL);
+ if (!mem_region) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ mem_region->phandle = mem_node->phandle;
+ mem_region->start = res.start;
+ mem_region->size = resource_size(&res);
+
+ list_add_tail(&mem_region->list, &elink->mem_region_list);
+ dev_dbg(dev, "reserved-mem: added mem region at 0x%x\n", res.start);
+
+out:
+ of_node_put(mem_node);
+ return ret;
+}
+
+static int elink_of_probe_clks(struct platform_device *pdev,
+ struct elink_device *elink)
+{
+ int ret = 0, i = 0;
+
+ static const char const *names[] = {
+ "fclk0", "fclk1", "fclk2", "fclk3"
+ };
+
+ elink->clocks = devm_kcalloc(&pdev->dev, ARRAY_SIZE(names) + 1,
+ sizeof(struct clock *), GFP_KERNEL);
+ if (!elink->clocks)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(names); i++) {
+ elink->clocks[i] = devm_clk_get(&pdev->dev, names[i]);
+ if (IS_ERR(elink->clocks[i])) {
+ ret = PTR_ERR(elink->clocks[i]);
+ goto err;
+ }
+
+ dev_dbg(&pdev->dev, "Added clock: %s\n", names[i]);
+ }
+
+ return 0;
+
+err:
+ for (i--; i >= 0; i--)
+ devm_clk_put(&pdev->dev, elink->clocks[i]);
+
+ devm_kfree(&pdev->dev, elink->clocks);
+ elink->clocks = NULL;
+
+ return ret;
+}
+
+static int elink_of_probe_supplies(struct platform_device *pdev,
+ struct elink_device *elink)
+{
+ int ret = 0;
+ struct regulator *supply;
+ struct device_node *supply_node;
+
+ /* TODO: Support more than one regulator per elink */
+ supply_node = of_parse_phandle(pdev->dev.of_node, "vdd-supply", 0);
+ if (!supply_node) {
+ dev_warn(&pdev->dev,
+ "elink: no supply node specified, no power management.\n");
+ return 0;
+ }
+ of_node_put(supply_node);
+
+ supply = devm_regulator_get_optional(&pdev->dev, "vdd");
+ if (IS_ERR(supply)) {
+ ret = PTR_ERR(supply);
+ if (ret == -EPROBE_DEFER) {
+ dev_info(&pdev->dev,
+ "elink: vdd regulator not ready, retry\n");
+ } else {
+ dev_info(&pdev->dev, "elink: no regulator: vdd: %d\n",
+ ret);
+ }
+ supply = NULL;
+ }
+
+ elink->supply = supply;
+ return ret;
+}
+
+static struct elink_device *elink_of_probe(struct platform_device *pdev)
+{
+ struct elink_device *elink;
+ struct property *prop;
+ struct resource res;
+ u16 coreid;
+ int ret, irq;
+
+ elink = devm_kzalloc(&pdev->dev, sizeof(*elink), GFP_KERNEL);
+ if (!elink)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&elink->mem_region_list);
+ INIT_LIST_HEAD(&elink->mappings_list);
+
+ elink->phandle = pdev->dev.of_node->phandle;
+ elink->coreid_pinout = -1;
+
+ /* Control regs */
+ ret = of_address_to_resource(pdev->dev.of_node, 0, &res);
+ if (ret) {
+ dev_err(&pdev->dev, "no control reg resource\n");
+ return ERR_PTR(ret);
+ }
+ elink->regs_start = res.start;
+ elink->regs_size = resource_size(&res);
+
+ /* Host bus slave address range for emesh */
+ ret = of_address_to_resource(pdev->dev.of_node, 1, &res);
+ if (ret) {
+ dev_err(&pdev->dev, "no bus resource\n");
+ return ERR_PTR(ret);
+ }
+ elink->emesh_start = res.start;
+ elink->emesh_size = resource_size(&res);
+
+
+ /* don't accept regs overlapping mesh region */
+ if (elink->regs_start < elink->emesh_start &&
+ elink->emesh_start < elink->regs_start + elink->regs_size) {
+ dev_err(&pdev->dev,
+ "elink regs overlapping emesh memory region\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ /* accept regs fully inside mesh region */
+ if (elink->regs_start + elink->regs_size <= elink->emesh_start ||
+ elink->emesh_start + elink->emesh_size <= elink->regs_start) {
+ if (!devm_request_mem_region(&pdev->dev, elink->regs_start,
+ elink->regs_size, pdev->name)) {
+ dev_err(&pdev->dev,
+ "failed requesting control reg mem region\n");
+ return ERR_PTR(-ENOMEM);
+ }
+ }
+
+ if (!devm_request_mem_region(&pdev->dev, elink->emesh_start,
+ elink->emesh_size, pdev->name)) {
+ dev_err(&pdev->dev, "failed requesting emesh mem region\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ elink->regs = devm_ioremap_nocache(&pdev->dev, elink->regs_start,
+ elink->regs_size);
+ if (!elink->regs) {
+ dev_err(&pdev->dev, "Mapping eLink registers failed.\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* Power regulators */
+ ret = elink_of_probe_supplies(pdev, elink);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not get power supplies\n");
+ return ERR_PTR(ret);
+ }
+
+ /* Clocks */
+ ret = elink_of_probe_clks(pdev, elink);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not get clocks\n");
+ return ERR_PTR(ret);
+ }
+
+ /* Mailbox interrupt */
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "Could not get mailbox IRQ from platform data\n");
+ return ERR_PTR(irq);
+ }
+
+ ret = devm_request_irq(&pdev->dev, irq, elink_mailbox_irq_handler, 0,
+ dev_name(&pdev->dev), elink);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not request mailbox IRQ\n");
+ return ERR_PTR(ret);
+ }
+
+ /* Chip-id pinout */
+ prop = of_find_property(pdev->dev.of_node,
+ "adapteva,no-coreid-pinout", NULL);
+ if (prop) {
+ elink->coreid_is_noop = true;
+ dev_dbg(&pdev->dev, "coreid is no-op\n");
+ }
+
+
+ /* Manually override coreid pinout. Set by array probe otherwise */
+ ret = of_property_read_u16(pdev->dev.of_node, "adapteva,coreid",
+ &coreid);
+ if (!ret) {
+ elink->coreid_pinout = (s16) coreid;
+ } else if (ret == -EINVAL) {
+ elink->coreid_pinout = -1;
+ } else {
+ dev_err(&pdev->dev, "Malformed coreid pinout dt property\n");
+ return ERR_PTR(ret);
+ }
+
+ ret = elink_of_probe_reserved_mem(pdev, elink);
+ if (ret) {
+ dev_err(&pdev->dev, "reserved mem probe failed\n");
+ return ERR_PTR(ret);
+ }
+
+ ret = elink_of_probe_default_mappings(pdev, elink);
+ if (ret) {
+ dev_err(&pdev->dev, "failed probing default mappings\n");
+ return ERR_PTR(ret);
+ }
+
+ ret = elink_register(elink);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register elink: %d\n", ret);
+ return ERR_PTR(ret);
+ }
+
+ return elink;
+}
+
+static int elink_platform_probe(struct platform_device *pdev)
+{
+ struct elink_device *elink;
+ int ret;
+
+ elink = elink_of_probe(pdev);
+ if (IS_ERR(elink)) {
+ ret = PTR_ERR(elink);
+ if (ret == -EPROBE_DEFER)
+ dev_info(&pdev->dev, "Deferring probe.\n");
+ else
+ dev_warn(&pdev->dev, "Failed parsing device tree\n");
+
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, elink);
+
+ ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to create DT children: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int elink_platform_remove(struct platform_device *pdev)
+{
+ struct elink_device *elink = platform_get_drvdata(pdev);
+
+ if (elink->connection.type == E_CONN_ARRAY)
+ array_unregister(elink->connection.array);
+
+ of_platform_depopulate(&pdev->dev);
+
+ elink_unregister(elink);
+
+ dev_dbg(&pdev->dev, "device removed\n");
+
+ return 0;
+}
+
+static const struct of_device_id elink_of_match[] = {
+ { .compatible = "adapteva,elink" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, elink_of_match);
+
+static struct platform_driver elink_driver = {
+ .probe = elink_platform_probe,
+ .remove = elink_platform_remove,
+ .driver = {
+ .name = "elink",
+ .of_match_table = of_match_ptr(elink_of_match)
+ }
+};
+
+static char *epiphany_devnode(struct device *dev, umode_t *mode)
+{
+ return kasprintf(GFP_KERNEL, "epiphany/%s", dev_name(dev));
+}
+
+static void epiphany_device_release(struct device *dev)
+{
+ /* No-op since we use devm_* */
+}
+
+static void __init init_epiphany(void)
+{
+ epiphany.class.name = "epiphany";
+ epiphany.class.owner = THIS_MODULE;
+ epiphany.class.devnode = epiphany_devnode;
+ epiphany.class.dev_release = epiphany_device_release;
+
+ epiphany.u_count = 0;
+
+ INIT_LIST_HEAD(&epiphany.elink_list);
+ INIT_LIST_HEAD(&epiphany.chip_array_list);
+ INIT_LIST_HEAD(&epiphany.mesh_list);
+
+ INIT_LIST_HEAD(&epiphany.vma_list);
+
+ epiphany.thermal_disallow = false;
+
+ idr_init(&epiphany.minor_idr);
+ spin_lock_init(&epiphany.minor_idr_lock);
+
+
+ atomic_set(&epiphany.elink_counter, 0);
+ atomic_set(&epiphany.mesh_counter, 0);
+ atomic_set(&epiphany.array_counter, 0);
+
+ mutex_init(&epiphany.driver_lock);
+}
+
+static int __init epiphany_module_init(void)
+{
+ int ret;
+
+ init_epiphany();
+
+ ret = class_register(&epiphany.class);
+ if (ret) {
+ pr_err("Unable to register epiphany class\n");
+ goto err_class;
+ }
+
+ ret = alloc_chrdev_region(&epiphany.devt, 0, E_DEV_NUM_MINORS,
+ "epiphany");
+ if (ret) {
+ pr_err("Failed allocating epiphany major number: %i\n", ret);
+ goto err_chrdev;
+ }
+ pr_devel("epiphany device allocated, major %i\n", MAJOR(epiphany.devt));
+
+ ret = platform_driver_register(&elink_driver);
+ if (ret) {
+ pr_err("Failed to register elink platform driver\n");
+ goto err_register_elink;
+ }
+
+ ret = platform_driver_register(&array_driver);
+ if (ret) {
+ pr_err("Failed to register elink platform driver\n");
+ goto err_register_array;
+ }
+
+ return 0;
+
+err_register_array:
+ platform_driver_unregister(&elink_driver);
+err_register_elink:
+ unregister_chrdev_region(epiphany.devt, E_DEV_NUM_MINORS);
+err_chrdev:
+ class_unregister(&epiphany.class);
+err_class:
+ return ret;
+}
+module_init(epiphany_module_init);
+
+static void __exit epiphany_module_exit(void)
+{
+ struct mesh_device *curr, *next;
+
+ if (epiphany.thermal_disallow) {
+ epiphany.thermal_disallow = false;
+ /* Fix refcount, elinks + regulators already disabled */
+ epiphany.u_count--;
+ }
+
+ list_for_each_entry_safe(curr, next, &epiphany.mesh_list, list)
+ mesh_unregister(curr);
+
+ platform_driver_unregister(&array_driver);
+ platform_driver_unregister(&elink_driver);
+ unregister_chrdev_region(epiphany.devt, E_DEV_NUM_MINORS);
+ class_unregister(&epiphany.class);
+
+ WARN_ON(!list_empty(&epiphany.chip_array_list));
+ WARN_ON(!list_empty(&epiphany.elink_list));
+ WARN_ON(!list_empty(&epiphany.mesh_list));
+}
+module_exit(epiphany_module_exit);
+
+MODULE_DESCRIPTION("Adapteva Epiphany driver");
+MODULE_VERSION("0.1");
+MODULE_LICENSE("GPL");
diff --git a/epiphany.h b/epiphany.h
new file mode 100644
index 000000000000..1378296d2643
--- /dev/null
+++ b/epiphany.h
@@ -0,0 +1,131 @@
+#ifndef __EPIPHANY_H__
+#define __EPIPHANY_H__
+
+#include <uapi/misc/epiphany.h>
+
+/* Epiphany system registers */
+enum e_elink_regs {
+ ELINK_RESET = 0xF0200,
+ ELINK_CLK = 0xF0204,
+ ELINK_CHIPID = 0xF0208,
+ ELINK_VERSION = 0xF020C,
+ ELINK_TXCFG = 0xF0210,
+ ELINK_TXSTATUS = 0xF0214,
+ ELINK_TXGPIO = 0xF0218,
+ ELINK_TXMONITOR = 0xF021C,
+ ELINK_TXPACKET = 0xF0220,
+ ELINK_RXCFG = 0xF0300,
+ ELINK_RXSTATUS = 0xF0304,
+ ELINK_RXGPIO = 0xF0308,
+ ELINK_RXOFFSET = 0xF030C,
+ ELINK_RXDELAY0 = 0xF0310,
+ ELINK_RXDELAY1 = 0xF0314,
+ ELINK_RXTESTDATA = 0xF0318,
+ ELINK_MAILBOXLO = 0xF0730,
+ ELINK_MAILBOXHI = 0xF0734,
+ ELINK_MAILBOXSTAT = 0xF0738,
+ ELINK_TXMMU = 0xE0000,
+ ELINK_RXMMU = 0xE8000,
+};
+
+union elink_reset {
+ u32 reg;
+ struct {
+ unsigned tx_reset:1;
+ unsigned rx_reset:1;
+ };
+} __packed;
+
+union elink_chipid {
+ u32 reg;
+ union {
+ struct {
+ unsigned corecol:6;
+ unsigned corerow:6;
+ };
+ struct {
+ unsigned:2;
+ unsigned chipcol;
+ unsigned:2;
+ unsigned chiprow;
+ };
+ };
+} __packed;
+
+union elink_version {
+ u32 reg;
+ struct {
+ unsigned platform:8;
+ unsigned revision:8;
+ };
+} __packed;
+
+union elink_txcfg {
+ u32 reg;
+ struct {
+ unsigned enable:1;
+ unsigned mmu_enable:1;
+ unsigned remap_mode:2;
+ unsigned ctrlmode:4;
+ unsigned:1;
+ unsigned ctrlmode_bypass:1;
+ unsigned burst_enable:1;
+ unsigned transmit_mode:2;
+ };
+} __packed;
+
+union elink_rxcfg {
+ u32 reg;
+ struct {
+ unsigned test_mode:1;
+ unsigned mmu_enable:1;
+ unsigned remap_mode:2;
+ unsigned remap_sel:12;
+ unsigned remap_pattern:12;
+ unsigned mailbox_irq_en:1;
+ };
+} __packed;
+
+union elink_mailboxstat {
+ u32 reg;
+ struct {
+ unsigned not_empty:1;
+ unsigned full:1;
+ unsigned half_full:1;
+ unsigned:13;
+ u16 count;
+ };
+} __packed;
+
+/* Chip registers */
+enum e_chip_regs {
+ E_REG_LINKCFG = 0xf0300,
+ E_REG_LINKTXCFG = 0xf0304,
+ E_REG_LINKRXCFG = 0xf0308,
+ E_REG_GPIOCFG = 0xf030c,
+ E_REG_FLAGCFG = 0xf0318,
+ E_REG_SYNC = 0xf031c,
+ E_REG_HALT = 0xf0320,
+ E_REG_RESET = 0xf0324,
+ E_REG_LINKDEBUG = 0xf0328
+};
+
+enum e_ctrlmode {
+ E_CTRLMODE_NORMAL = 0,
+ E_CTRLMODE_DMA0_LAST = 4,
+ E_CTRLMODE_DMA1_LAST = 8,
+ E_CTRLMODE_MSGMODE = 12,
+ E_CTRLMODE_MULTICAST = 3,
+ E_CTRLMODE_NORTH = 1,
+ E_CTRLMODE_EAST = 5,
+ E_CTRLMODE_SOUTH = 9,
+ E_CTRLMODE_WEST = 13
+};
+
+enum e_core_reg {
+ E_REG_BASE = 0xf0000,
+ E_REG_CONFIG = 0xf0400,
+ E_REG_MESHCONFIG = 0xf0700
+};
+
+#endif /* __EPIPHANY_H__ */
diff --git a/epiphany_uapi.h b/epiphany_uapi.h
new file mode 100644
index 000000000000..7666e735190a
--- /dev/null
+++ b/epiphany_uapi.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2015 Adapteva Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ */
+
+#ifndef _UAPI_MISC_EPIPHANY_H
+#define _UAPI_MISC_EPIPHANY_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define E_MESH_MAX_ARRAYS 128
+#define E_LINK_MAX_MEM_MAPPINGS 256
+
+enum e_link_side {
+ E_SIDE_N = 0,
+ E_SIDE_E,
+ E_SIDE_S,
+ E_SIDE_W,
+ E_SIDE_MAX
+};
+
+enum e_connection_type {
+ E_CONN_DISCONNECTED = 0,
+ E_CONN_ELINK,
+ E_CONN_ARRAY,
+ E_CONN_MAX
+};
+
+enum e_chip_type {
+ E_CHIP_UNKNOWN = 0,
+ E_CHIP_E16G301,
+ E_CHIP_E64G401,
+ E_CHIP_MAX
+};
+
+struct e_mappings_info {
+ __u64 nmappings;
+ struct {
+ __u64 emesh_addr;
+ __u64 size;
+ } mappings[E_LINK_MAX_MEM_MAPPINGS];
+} __attribute__((packed));
+
+struct e_array_info {
+ __u64 id;
+ __u64 chip_type;
+ __u64 chip_rows;
+ __u64 chip_cols;
+ __u64 parent_side;
+ __u64 mesh_dev;
+ struct {
+ __u64 type;
+ union {
+ __u64 dev;
+ __u64 id;
+ };
+ } connections[E_SIDE_MAX];
+} __attribute__((packed));
+
+struct e_elink_info {
+ __u64 dev;
+ __u32 version;
+ __u32 _pad;
+ __u64 connection_type;
+ union {
+ struct e_array_info array;
+ __u64 remote_elink_id;
+ };
+} __attribute__((packed));
+
+struct e_mesh_info {
+ __u64 dev;
+ __u64 chip_type;
+ __u64 narrays;
+#if 1
+ struct e_array_info arrays[E_MESH_MAX_ARRAYS];
+#endif
+} __attribute__((packed));
+
+struct e_mailbox_msg {
+ __u32 from;
+ __u32 data;
+} __attribute__((packed));
+
+
+#define E_IOCTL_MAGIC 'E'
+#define E_IO(nr) _IO(E_IOCTL_MAGIC, nr)
+#define E_IOR(nr, type) _IOR(E_IOCTL_MAGIC, nr, type)
+#define E_IOW(nr, type) _IOW(E_IOCTL_MAGIC, nr, type)
+#define E_IOWR(nr, type) _IOWR(E_IOCTL_MAGIC, nr, type)
+
+/**
+ * If you add an IOC command, please update the
+ * EPIPHANY_IOC_MAXNR macro
+ */
+
+#define E_IOCTL_RESET E_IO(0x00)
+#define E_IOCTL_ELINK_PROBE E_IOR(0x01, struct e_elink_info)
+#define E_IOCTL_MESH_PROBE E_IOR(0x02, struct e_mesh_info)
+#define E_IOCTL_GET_MAPPINGS E_IOR(0x03, struct e_mappings_info)
+#define E_IOCTL_MAILBOX_READ E_IOWR(0x04, struct e_mailbox_msg)
+#define E_IOCTL_MAILBOX_COUNT E_IO(0x05)
+#define E_IOCTL_THERMAL_DISALLOW E_IO(0x06)
+#define E_IOCTL_THERMAL_ALLOW E_IO(0x07)
+
+#define E_IOCTL_MAXNR 0x07
+
+#endif /* _UAPI_MISC_EPIPHANY_H */
diff --git a/linux-parallella.install b/linux-parallella.install
new file mode 100644
index 000000000000..127de9cfd3a1
--- /dev/null
+++ b/linux-parallella.install
@@ -0,0 +1,3 @@
+post_remove() {
+ rm -f boot/initramfs-linux.img
+}
diff --git a/linux.preset b/linux.preset
new file mode 100644
index 000000000000..8d7f379c9e04
--- /dev/null
+++ b/linux.preset
@@ -0,0 +1,10 @@
+# mkinitcpio preset file for the '%PKGBASE%' package
+
+ALL_config="/etc/mkinitcpio.conf"
+ALL_kver="%KERNVER%"
+
+PRESETS=('default')
+
+#default_config="/etc/mkinitcpio.conf"
+default_image="/boot/initramfs-linux.img"
+#default_options=""