diff options
author | Kaczanowski Mateusz | 2020-01-08 14:26:55 +0100 |
---|---|---|
committer | Kaczanowski Mateusz | 2020-01-08 14:29:17 +0100 |
commit | 68ecd2668c735031ed3bdfbcb749fb131f459dd2 (patch) | |
tree | c53ff32ab986b8d9c4c18e8dfbba13d27318faa2 | |
download | aur-68ecd2668c735031ed3bdfbcb749fb131f459dd2.tar.gz |
initial commit
-rw-r--r-- | .SRCINFO | 44 | ||||
-rw-r--r-- | 0001-kernel-add-epiphany-kconfig.patch | 27 | ||||
-rw-r--r-- | 0002-kernel-add-epiphany-makefile.patch | 9 | ||||
-rw-r--r-- | 60-linux.hook | 12 | ||||
-rw-r--r-- | 90-linux.hook | 11 | ||||
-rw-r--r-- | PKGBUILD | 188 | ||||
-rw-r--r-- | config | 508 | ||||
-rw-r--r-- | epiphany.c | 2896 | ||||
-rw-r--r-- | epiphany.h | 131 | ||||
-rw-r--r-- | epiphany_uapi.h | 115 | ||||
-rw-r--r-- | linux-parallella.install | 3 | ||||
-rw-r--r-- | linux.preset | 10 |
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="" |