diff --git a/README.md b/README.md index 3f76e4ca3..e0e01552f 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# Digi Embedded Yocto (DEY) 2.4 -## Release 2.4-r2.2 +# Digi Embedded Yocto (DEY) 2.6 +## Release 2.6-r1.3 This document provides information about Digi Embedded Yocto, Digi International's professional embedded Yocto development environment. -Digi Embedded Yocto 2.4 is based on the Yocto Project(TM) 2.4 (Rocko) release. +Digi Embedded Yocto 2.6 is based on the Yocto Project(TM) 2.6 (Thud) release. For a full list of supported features and interfaces please refer to the online documentation. @@ -32,32 +32,6 @@ Software for the following hardware platforms is in production support: * [CC-WMX6UL-KIT](https://www.digi.com/products/models/cc-wmx6ul-kit) ([Get Started](https://www.digi.com/resources/documentation/digidocs/90001547/default.htm#concept/yocto/c_get_started_with_yocto.htm)) * [CC-SBP-WMX-JN58](https://www.digi.com/products/models/cc-sbp-wmx-jn58) -## ConnectCore 6 -* ConnectCore 6 System-on-Module (SOM) - * [CC-WMX-J97C-TN](https://www.digi.com/products/models/cc-wmx-j97c-tn) - * [CC-WMX-L96C-TE](https://www.digi.com/products/models/cc-wmx-l96c-te) - * [CC-WMX-L87C-TE](https://www.digi.com/products/models/cc-wmx-l87c-te) - * [CC-MX-L76C-Z1](https://www.digi.com/products/models/cc-mx-l76c-z1) - * [CC-MX-L86C-Z1](https://www.digi.com/products/models/cc-mx-l86c-z1) - * [CC-MX-L96C-Z1](https://www.digi.com/products/models/cc-mx-l96c-z1) - * [CC-WMX-L76C-TE](https://www.digi.com/products/models/cc-wmx-l76c-te) - * CC-WMX-K87C-FJA - * CC-WMX-K77C-TE - * CC-WMX-L97D-TN - * CC-WMX-J98C-FJA - * CC-WMX-J98C-FJA-1 -* ConnectCore 6 SBC - * [CC-WMX6-KIT](https://www.digi.com/products/models/cc-wmx6-kit) ([Get Started](https://www.digi.com/resources/documentation/digidocs/90001546/default.htm#concept/yocto/c_get_started_with_yocto.htm)) - * [CC-SB-WMX-J97C-1](https://www.digi.com/products/models/cc-sb-wmx-j97c-1) - * [CC-SB-WMX-L87C-1](https://www.digi.com/products/models/cc-sb-wmx-l87c-1) - * [CC-SB-WMX-L76C-1](https://www.digi.com/products/models/cc-sb-wmx-l76c-1) - -## ConnectCore 6 Plus -* ConnectCore 6 Plus System-on-Module (SOM) - * CC-WMX-KK8D-TN -* ConnectCore 6 Plus SBC - * CC-SB-WMX-KK8D - ## ConnectCore 8X * ConnectCore 8X System-on-Module (SOM) * [CC-WMX-JM7D-NN](https://www.digi.com/cc8x) @@ -76,7 +50,7 @@ hardware. # Installation Digi Embedded Yocto is composed of a set of different Yocto layers that work in -parallel. The layers are specified on a [manifest](https://github.com/digi-embedded/dey-manifest/blob/rocko/default.xml) file. +parallel. The layers are specified on a [manifest](https://github.com/digi-embedded/dey-manifest/blob/thud/default.xml) file. To install, please follow the instructions at the dey-manifest [README](https://github.com/digi-embedded/dey-manifest) @@ -86,42 +60,34 @@ Documentation is available online at https://www.digi.com/resources/documentatio # Downloads -* Demo images: ftp://ftp1.digi.com/support/digiembeddedyocto/2.4/r2/images/ -* Software Development Kit (SDK): ftp://ftp1.digi.com/support/digiembeddedyocto/2.4/r2/sdk/ +* Demo images: ftp://ftp1.digi.com/support/digiembeddedyocto/2.6/r1/images/ +* Software Development Kit (SDK): ftp://ftp1.digi.com/support/digiembeddedyocto/2.6/r1/sdk/ # Release Changelog -## 2.4-r2.1 +## 2.6-r1 -* Added support for ConnectCore 8X platform. Supported features: - * Bluetooth and Wireless interfaces (STA, SoftAP, P2P, Concurrence) - * MCA: Reset, Watchdog, RTC, GPIOs and ADCs -* Updated U-Boot to version 2017.03-r2 -* Fixed XBee RESET_N polarity assertion. Requires updating polarity of the -``digi,reset-gpio`` property of node ``xbee`` on device tree files derived from -ConnectCore 6UL SBC Pro to use polarity **GPIO_ACTIVE_LOW** -* Updated AWS Greengrass core to version 1.7.0 -* Updated kernel version to v4.9.135 for i.MX6 and i.MX6UL platforms. - -## 2.4-r1 - -* Release based on [Yocto 2.4 (Rocko)](https://www.yoctoproject.org/downloads/core/rocko24) including: - * New toolchain based on GCC-7.2.0 and GLIBC-2.26 - * Updated Qt 5.9.4 +* Release based on [Yocto 2.6 (Thud)](https://www.yoctoproject.org/software-overview/downloads) including: + * New toolchain based on GLIBC-2.28 + * Updated Qt 5.11.3 + * Updated NetworkManager to v1.14.4 + * Updated Wpa-supplicant to v2.6 + * Updated gstreamer1.0 to v1.14.0 + * Updated busybox to v1.29.2 + * Updated bluez5 to v5.50 + * Updated OpenSSL to v1.1.1a * Package upgrades and security fixes -* Updated kernel version to v4.9 for ConnectCore 6 platform -* Updated U-Boot to version 2015.04-r12 -* Updated Cryptoauthlib version to v3.0 -* Added support for Goodix 10" display -* Updated documentation +* Updated kernel version to v4.14.111 for i.MX8X and i.MX6UL platforms +* Updated U-Boot to version 2018.03-r1 for i.MX8X platform +* Updated AWS Greengrass core to version 1.8.0 +* Added support for Code Signing Tool 3.1.0 +* Changed initialization manager in ConnectCore 8X platforms to systemd # Known Issues and Limitations This is a list of known issues and limitations at the time of release. An updated list can be found on the online documentation. -* If TrustFence (TM) image encryption support is enabled, the uSD image will -boot a signed U-Boot only. * Firmware update * The software update package must be located in the root level of the update media (subfolders are not yet supported). @@ -129,9 +95,9 @@ boot a signed U-Boot only. * Remote file system management fails with long file names and paths (over 255 characters). * For P2P connections Digi recommends "Negotiated GO" modes. The QCA6564 - devices (ConnectCore 6UL and ConnectCore 6 Plus) running a 4.9 kernel - version fail to join to autonomous groups. -* Trustfence secure console is not supported on U-Boot v2017.03. In order to use this functionality, U-Boot v2015.04 will be required. + devices (ConnectCore 6UL) running a 4.14 kernel version fail to join to + autonomous groups. +* Trustfence is not yet supported on U-Boot v2018.03. ## ConnectCore 6UL @@ -152,39 +118,9 @@ boot a signed U-Boot only. currently supported. * The QCA6564 wireless chip does not support Wake On Wireless LAN. -## ConnectCore 6 - -* ConnectCore 6 System-on-Module (SOM) - * NXP i.MX6 processor has a documented errata (ERR004512) whereby the maximum - performance of the Gigabit FEC is limited to 400Mbps (total for Tx and Rx). - * When using softAP mode on Band A on the Qualcomm AR6233, channels used for - Dynamic Frequency Selection (DFS) are not supported. - * The Qualcomm AR6233 firmware does not support the following configuration - modes: - * Concurrent modes involving P2P mode, such as P2P + softAP or P2P + STA. - * Bluetooth + softAP + STA concurrent mode. - * A maximum of five clients are supported when using Qualcomm's AR6233 in - softAP mode. - * A maximum of ten connected devices are supported when using Qualcomm's AR6233 - Bluetooth Low Energy mode. -* ConnectCore 6 SBC - * The Micrel PHY KSZ9031 may take between five and six seconds to - auto-negotiate with Gigabit switches. - -## ConnectCore 6 Plus - -* ConnectCore 6 Plus System-on-Module (SOM) - * NXP i.MX6QP processor has a documented errata (ERR004512) whereby the maximum - performance of the Gigabit FEC is limited to 400Mbps (total for Tx and Rx). -* ConnectCore 6 Plus SBC - * The Micrel PHY KSZ9031 may take between five and six seconds to - auto-negotiate with Gigabit switches. - ## ConnectCore 8X * i.MX8QXP Processor - * The following processor features are not supported in this BSP release: - Power Management, PWM, ADC, RS485. They will be implemented in future releases. * GPU maximum performance reduced. The maximum frequency targets are 850 MHz for the shaders and 700 MHz for the core. However, in this hardware release the maximum frequency is limited to 650 MHz for the shaders and 600 MHz for @@ -192,10 +128,8 @@ boot a signed U-Boot only. be met in future releases of the hardware. * BSDL operation is not supported. It will be available in future releases of the hardware. -* ConnectCore 8X SBC Pro - * USB 3.0 not supported. * Digi Embedded Yocto - * The following features are not supported in this release for the ConnectCore 8X plaform: + * The following features are not supported in this release for the ConnectCore 8X platform: * Trustfence (TM) * Digi Remote Manager diff --git a/meta-digi-arm/conf/machine/ccimx6ulsbc.conf b/meta-digi-arm/conf/machine/ccimx6ulsbc.conf index ecf1f5cf2..9df9a23d7 100644 --- a/meta-digi-arm/conf/machine/ccimx6ulsbc.conf +++ b/meta-digi-arm/conf/machine/ccimx6ulsbc.conf @@ -29,7 +29,6 @@ KERNEL_DEVICETREE ?= " \ KERNEL_DEVICETREE_use-mainline-bsp ?= "imx6ul-ccimx6ulsbcpro.dtb" KERNEL_DEFCONFIG ?= "arch/arm/configs/ccimx6ul_defconfig" -KERNEL_DEFCONFIG_use-mainline-bsp ?= "arch/arm/configs/imx_v6_v7_defconfig" SERIAL_CONSOLES ?= "115200;ttymxc4" diff --git a/meta-digi-arm/conf/machine/ccimx6ulstarter.conf b/meta-digi-arm/conf/machine/ccimx6ulstarter.conf index 10cb62a92..cef942ef0 100644 --- a/meta-digi-arm/conf/machine/ccimx6ulstarter.conf +++ b/meta-digi-arm/conf/machine/ccimx6ulstarter.conf @@ -28,7 +28,6 @@ KERNEL_DEVICETREE ?= " \ KERNEL_DEVICETREE_use-mainline-bsp ?= "imx6ul-ccimx6ulsbcexpress.dtb" KERNEL_DEFCONFIG ?= "arch/arm/configs/ccimx6ul_defconfig" -KERNEL_DEFCONFIG_use-mainline-bsp ?= "arch/arm/configs/imx_v6_v7_defconfig" SERIAL_CONSOLES ?= "115200;ttymxc4" diff --git a/meta-digi-arm/conf/machine/ccimx8x-sbc-express.conf b/meta-digi-arm/conf/machine/ccimx8x-sbc-express.conf index b480f3e32..e15fd9bd2 100644 --- a/meta-digi-arm/conf/machine/ccimx8x-sbc-express.conf +++ b/meta-digi-arm/conf/machine/ccimx8x-sbc-express.conf @@ -32,6 +32,10 @@ SERIAL_CONSOLES ?= "115200;ttyLP2" # Bluetooth tty BT_TTY ?= "ttyLP1" +# XBee +XBEE_RESET_N_GPIO ?= "397" +XBEE_SLEEP_RQ_GPIO ?= "400" + # U-Boot script to be copied to the boot image BOOT_SCRIPTS = "boot.scr:boot.scr" diff --git a/meta-digi-arm/conf/machine/ccimx8x-sbc-pro.conf b/meta-digi-arm/conf/machine/ccimx8x-sbc-pro.conf index 77422889e..60d23fa3f 100644 --- a/meta-digi-arm/conf/machine/ccimx8x-sbc-pro.conf +++ b/meta-digi-arm/conf/machine/ccimx8x-sbc-pro.conf @@ -32,6 +32,10 @@ SERIAL_CONSOLES ?= "115200;ttyLP2" # Bluetooth tty BT_TTY ?= "ttyLP1" +# XBee +XBEE_RESET_N_GPIO ?= "220" +XBEE_SLEEP_RQ_GPIO ?= "216" + # U-Boot script to be copied to the boot image BOOT_SCRIPTS = "boot.scr:boot.scr" diff --git a/meta-digi-arm/conf/machine/include/ccimx8x.inc b/meta-digi-arm/conf/machine/include/ccimx8x.inc index be801a056..045fe8b10 100644 --- a/meta-digi-arm/conf/machine/include/ccimx8x.inc +++ b/meta-digi-arm/conf/machine/include/ccimx8x.inc @@ -34,6 +34,7 @@ MACHINE_EXTRA_RDEPENDS += " \ mca-tool \ parted \ u-boot-fw-utils \ + xbee-init \ " MACHINE_EXTRA_RRECOMMENDS += " \ diff --git a/meta-digi-arm/conf/machine/include/digi-defaults.inc b/meta-digi-arm/conf/machine/include/digi-defaults.inc index cda93f0a4..3d229dd1e 100644 --- a/meta-digi-arm/conf/machine/include/digi-defaults.inc +++ b/meta-digi-arm/conf/machine/include/digi-defaults.inc @@ -3,7 +3,7 @@ PREFERRED_PROVIDER_jpeg-native ?= "jpeg-native" PREFERRED_PROVIDER_u-boot ??= "u-boot-dey" PREFERRED_PROVIDER_virtual/bootloader ??= "u-boot-dey" PREFERRED_PROVIDER_virtual/kernel ??= "linux-dey" -PREFERRED_PROVIDER_virtual/kernel_use-mainline-bsp ??= "linux-4.20.y" +PREFERRED_PROVIDER_virtual/kernel_use-mainline-bsp ??= "linux-fslc" PREFERRED_PROVIDER_virtual/xserver = "xserver-xorg" # diff --git a/meta-digi-arm/recipes-bsp/imx-mkimage/imx-boot_0.2.bbappend b/meta-digi-arm/recipes-bsp/imx-mkimage/imx-boot_0.2.bbappend index 650b543e8..5bdb21502 100644 --- a/meta-digi-arm/recipes-bsp/imx-mkimage/imx-boot_0.2.bbappend +++ b/meta-digi-arm/recipes-bsp/imx-mkimage/imx-boot_0.2.bbappend @@ -14,6 +14,7 @@ IMX_M4_DEMOS = "imx-m4-demos" # so set the appropriate dependencies do_compile[depends] += " \ ${@' '.join('%s:do_deploy' % r for r in '${IMX_M4_DEMOS}'.split() )} \ + firmware-imx:do_deploy \ " # This package aggregates dependencies with other packages, @@ -23,6 +24,7 @@ do_populate_lic[depends] += " \ ${@' '.join('%s:do_populate_lic' % r for r in '${IMX_EXTRA_FIRMWARE}'.split() )} \ imx-atf:do_populate_lic \ ${@' '.join('%s:do_populate_lic' % r for r in '${IMX_M4_DEMOS}'.split() )} \ + firmware-imx:do_populate_lic \ " UBOOT_NAME = "u-boot-${MACHINE}.bin" diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ulsbc/boot.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ulsbc/boot.txt index a63e46900..dcd797ba7 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ulsbc/boot.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ulsbc/boot.txt @@ -11,7 +11,7 @@ else # # Set device tree filename depending on the hardware variant # - if test "${module_variant}" = "0x02" || test "${module_variant}" = "0x04"; then + if test "${module_variant}" = "0x02" || test "${module_variant}" = "0x04" || test "${module_variant}" = "0x06"; then setenv fdt_file imx6ul-ccimx6ulsbc-wb.dtb elif test "${module_variant}" = "0x03" || test "${module_variant}" = "0x05"; then setenv fdt_file imx6ul-ccimx6ulsbc.dtb diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ulsbc/install_linux_fw_sd.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ulsbc/install_linux_fw_sd.txt index 2a63489e5..3ecb9ed69 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ulsbc/install_linux_fw_sd.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ulsbc/install_linux_fw_sd.txt @@ -21,7 +21,7 @@ fi # Determine U-Boot file to program basing on module variant if test -n "${module_variant}"; then - if test "${module_variant}" = "0x02" || test "${module_variant}" = "0x03"; then + if test "${module_variant}" = "0x02" || test "${module_variant}" = "0x03" || test "${module_variant}" = "0x06"; then setenv INSTALL_UBOOT_FILENAME u-boot-ccimx6ulsbc.imx; elif test "${module_variant}" = "0x04" || test "${module_variant}" = "0x05"; then setenv INSTALL_UBOOT_FILENAME u-boot-ccimx6ulsbc1GB.imx; diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ulstarter/boot.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ulstarter/boot.txt index b24e54ccd..0200b0d50 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ulstarter/boot.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ulstarter/boot.txt @@ -11,7 +11,7 @@ else # # Set device tree filename depending on the hardware variant # - if test "${module_variant}" = "0x02" || test "${module_variant}" = "0x04"; then + if test "${module_variant}" = "0x02" || test "${module_variant}" = "0x04" || test "${module_variant}" = "0x06"; then setenv fdt_file imx6ul-ccimx6ulstarter-wb.dtb elif test "${module_variant}" = "0x03" || test "${module_variant}" = "0x05"; then setenv fdt_file imx6ul-ccimx6ulstarter.dtb diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ulstarter/install_linux_fw_sd.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ulstarter/install_linux_fw_sd.txt index b9d1c97c2..1788b9904 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ulstarter/install_linux_fw_sd.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ulstarter/install_linux_fw_sd.txt @@ -21,7 +21,7 @@ fi # Determine U-Boot file to program basing on module variant if test -n "${module_variant}"; then - if test "${module_variant}" = "0x02" || test "${module_variant}" = "0x03"; then + if test "${module_variant}" = "0x02" || test "${module_variant}" = "0x03" || test "${module_variant}" = "0x06"; then setenv INSTALL_UBOOT_FILENAME u-boot-ccimx6ulstarter.imx; elif test "${module_variant}" = "0x04" || test "${module_variant}" = "0x05"; then setenv INSTALL_UBOOT_FILENAME u-boot-ccimx6ulstarter1GB.imx; diff --git a/meta-digi-arm/recipes-devtools/imx-usb-loader/imx-usb-loader_git.bbappend b/meta-digi-arm/recipes-devtools/imx-usb-loader/imx-usb-loader_git.bbappend new file mode 100644 index 000000000..c9ce7846a --- /dev/null +++ b/meta-digi-arm/recipes-devtools/imx-usb-loader/imx-usb-loader_git.bbappend @@ -0,0 +1,5 @@ +# Copyright (C) 2019 Digi International. + +# The most recent version of this tool is unable to boot closed devices. +# Revert to the version that was used in DEY 2.4. +SRCREV = "349286e25c3fd9b2d31b31e962340123bbc62d44" diff --git a/meta-digi-arm/recipes-kernel/linux/linux-4.20.y.bb b/meta-digi-arm/recipes-kernel/linux/linux-4.20.y.bb deleted file mode 100644 index 4d7ff652a..000000000 --- a/meta-digi-arm/recipes-kernel/linux/linux-4.20.y.bb +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (C) 2019 Digi International - -require recipes-kernel/linux/linux-dey.inc - -SRCBRANCH = "v4.20.y" -SRCREV = "${AUTOREV}" - -DEPENDS += "openssl-native" -HOST_EXTRACFLAGS += "-I${STAGING_INCDIR_NATIVE}" - -COMPATIBLE_MACHINE = "(ccimx6ul)" diff --git a/meta-digi-arm/recipes-kernel/linux/linux-dey.inc b/meta-digi-arm/recipes-kernel/linux/linux-dey.inc index 88d9d0171..4e9520d9c 100644 --- a/meta-digi-arm/recipes-kernel/linux/linux-dey.inc +++ b/meta-digi-arm/recipes-kernel/linux/linux-dey.inc @@ -3,7 +3,6 @@ SUMMARY = "Linux kernel for Digi boards" LICENSE = "GPLv2" LIC_FILES_CHKSUM = "file://COPYING;md5=d7810fab7487fb0aad327b76f1be7cd7" -LIC_FILES_CHKSUM_use-mainline-bsp = "file://COPYING;md5=bbea815ee2795b2f4230826c0c6b8814" DEPENDS += "lzop-native bc-native" DEPENDS += "${@oe.utils.conditional('TRUSTFENCE_SIGN', '1', 'trustfence-sign-tools-native', '', d)}" diff --git a/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ul/0001-ARM-Add-support-for-the-ConnectCore-6UL-System-On-Mo.patch b/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ul/0001-ARM-Add-support-for-the-ConnectCore-6UL-System-On-Mo.patch new file mode 100644 index 000000000..76709c900 --- /dev/null +++ b/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ul/0001-ARM-Add-support-for-the-ConnectCore-6UL-System-On-Mo.patch @@ -0,0 +1,167 @@ +From 2313935f3b195aa7d930961bcd44a5fac61a945e Mon Sep 17 00:00:00 2001 +From: Alex Gonzalez +Date: Fri, 20 Apr 2018 19:13:02 +0200 +Subject: [PATCH] ARM: Add support for the ConnectCore 6UL System-On-Module + +* Getter functions for hwid information +* Define "digi,ccimx6ul" in the compatible property so the MCA can be + managed using the available tools. + +Signed-off-by: Alex Gonzalez +--- + arch/arm/boot/dts/imx6ul-ccimx6ulsbcexpress.dts | 2 +- + arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts | 3 +- + arch/arm/configs/imx_v6_v7_defconfig | 2 +- + arch/arm/mach-imx/Kconfig | 8 +++ + arch/arm/mach-imx/Makefile | 2 +- + arch/arm/mach-imx/som-ccimx6ul.c | 69 +++++++++++++++++++++++++ + 6 files changed, 82 insertions(+), 4 deletions(-) + create mode 100644 arch/arm/mach-imx/som-ccimx6ul.c + +diff --git a/arch/arm/boot/dts/imx6ul-ccimx6ulsbcexpress.dts b/arch/arm/boot/dts/imx6ul-ccimx6ulsbcexpress.dts +index 3792679c0c90..dc773f350999 100644 +--- a/arch/arm/boot/dts/imx6ul-ccimx6ulsbcexpress.dts ++++ b/arch/arm/boot/dts/imx6ul-ccimx6ulsbcexpress.dts +@@ -15,7 +15,7 @@ + / { + model = "Digi International ConnectCore 6UL SBC Express."; + compatible = "digi,ccimx6ulsbcexpress", "digi,ccimx6ulsom", +- "fsl,imx6ul"; ++ "digi,ccimx6ul", "fsl,imx6ul"; + }; + + &adc1 { +diff --git a/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts b/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts +index 3749fdda3611..aed1db57ed3b 100644 +--- a/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts ++++ b/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts +@@ -14,7 +14,8 @@ + + / { + model = "Digi International ConnectCore 6UL SBC Pro."; +- compatible = "digi,ccimx6ulsbcpro", "digi,ccimx6ulsom", "fsl,imx6ul"; ++ compatible = "digi,ccimx6ulsbcpro", "digi,ccimx6ulsom", ++ "digi,ccimx6ul", "fsl,imx6ul"; + + lcd_backlight: backlight { + compatible = "pwm-backlight"; +diff --git a/arch/arm/configs/imx_v6_v7_defconfig b/arch/arm/configs/imx_v6_v7_defconfig +index 6985d61eddb3..2d32ab117fb5 100644 +--- a/arch/arm/configs/imx_v6_v7_defconfig ++++ b/arch/arm/configs/imx_v6_v7_defconfig +@@ -36,7 +36,7 @@ CONFIG_SOC_IMX6Q=y + CONFIG_SOC_IMX6SL=y + CONFIG_SOC_IMX6SLL=y + CONFIG_SOC_IMX6SX=y +-CONFIG_SOC_IMX6UL=y ++CONFIG_SOM_CC6UL=y + CONFIG_SOC_IMX7D=y + CONFIG_SOC_VF610=y + CONFIG_PCI=y +diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig +index abc337111eff..2451ee423496 100644 +--- a/arch/arm/mach-imx/Kconfig ++++ b/arch/arm/mach-imx/Kconfig +@@ -523,6 +523,14 @@ config SOC_IMX6UL + help + This enables support for Freescale i.MX6 UltraLite processor. + ++config SOM_CC6UL ++ bool "Digi ConnectCore 6UL System-On-Module support" ++ select SOC_IMX6UL ++ select SOC_IMX6 ++ ++ help ++ This enables support for Digi ConnectCore 6UL System-On-Module. ++ + config SOC_LS1021A + bool "Freescale LS1021A support" + select ARM_GIC +diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile +index bae179af21f6..0cd7cdfc5e49 100644 +--- a/arch/arm/mach-imx/Makefile ++++ b/arch/arm/mach-imx/Makefile +@@ -83,7 +83,7 @@ obj-$(CONFIG_SOC_IMX6SX) += mach-imx6sx.o + obj-$(CONFIG_SOC_IMX6UL) += mach-imx6ul.o + obj-$(CONFIG_SOC_IMX7D_CA7) += mach-imx7d.o + obj-$(CONFIG_SOC_IMX7D_CM4) += mach-imx7d-cm4.o +- ++obj-$(CONFIG_SOM_CC6UL) += som-ccimx6ul.o + ifeq ($(CONFIG_SUSPEND),y) + AFLAGS_suspend-imx6.o :=-Wa,-march=armv7-a + obj-$(CONFIG_SOC_IMX6) += suspend-imx6.o +diff --git a/arch/arm/mach-imx/som-ccimx6ul.c b/arch/arm/mach-imx/som-ccimx6ul.c +new file mode 100644 +index 000000000000..269b526ee020 +--- /dev/null ++++ b/arch/arm/mach-imx/som-ccimx6ul.c +@@ -0,0 +1,69 @@ ++/* ++ * Copyright 2018 Digi International Inc ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++static int digi_board_version = -EINVAL; ++ ++int digi_get_board_version(void) ++{ ++ struct device_node *np = NULL; ++ const char *boardver_str; ++ char buf[4]; ++ ++ /* Only need to read the carrier board once */ ++ if (digi_board_version > 0) ++ return digi_board_version; ++ ++ np = of_find_node_by_path("/"); ++ if (!np) ++ return -EPERM; ++ ++ if (!of_property_read_string(np, "digi,carrierboard,version", ++ &boardver_str)) { ++ strncpy(buf, boardver_str, sizeof(buf)); ++ if (!kstrtoint(boardver_str, 10, &digi_board_version)) ++ pr_debug("Board version: %d\n", digi_board_version); ++ } ++ of_node_put(np); ++ ++ return digi_board_version; ++} ++EXPORT_SYMBOL(digi_get_board_version); ++ ++static int digi_som_hv = -EINVAL; ++ ++int digi_get_som_hv(void) ++{ ++ struct device_node *np = NULL; ++ const char *som_hv_str; ++ char buf[4]; ++ ++ /* Only need to read the HV once */ ++ if (digi_som_hv > 0) ++ return digi_som_hv; ++ ++ np = of_find_node_by_path("/"); ++ if (!np) ++ return -EPERM; ++ ++ if (!of_property_read_string(np, "digi,hwid,hv", &som_hv_str)) { ++ strncpy(buf, som_hv_str, sizeof(buf)); ++ if (!kstrtoint(som_hv_str, 16, &digi_som_hv)) ++ pr_debug("SOM HV: %d\n", digi_som_hv); ++ } ++ of_node_put(np); ++ ++ return digi_som_hv; ++} ++EXPORT_SYMBOL(digi_get_som_hv); diff --git a/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ul/0002-mach-imx-pm-imx6-Add-hooks-for-board-specific-implem.patch b/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ul/0002-mach-imx-pm-imx6-Add-hooks-for-board-specific-implem.patch new file mode 100644 index 000000000..725ea67f5 --- /dev/null +++ b/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ul/0002-mach-imx-pm-imx6-Add-hooks-for-board-specific-implem.patch @@ -0,0 +1,65 @@ +From d0520a166cdeccf2821a45483602fc24a1772569 Mon Sep 17 00:00:00 2001 +From: Alex Gonzalez +Date: Fri, 20 Apr 2018 20:20:30 +0200 +Subject: [PATCH] mach-imx: pm-imx6: Add hooks for board specific + implementation + +This commit implements two new pm hooks in pm_imx6.c (begin & end) that, +optionally, can be implemented by the platform code. +This is needed on platforms like the CC6UL where the i.MX6UL has to +notify the MCA when suspending the system or resuming from suspend. + +Signed-off-by: Alex Gonzalez +Signed-off-by: Pedro Perez de Heredia +--- + arch/arm/mach-imx/pm-imx6.c | 24 ++++++++++++++++++++++++ + 1 file changed, 24 insertions(+) + +diff --git a/arch/arm/mach-imx/pm-imx6.c b/arch/arm/mach-imx/pm-imx6.c +index 87f45b926c78..1a4d1ea92687 100644 +--- a/arch/arm/mach-imx/pm-imx6.c ++++ b/arch/arm/mach-imx/pm-imx6.c +@@ -69,6 +69,14 @@ static void __iomem *suspend_ocram_base; + static void (*imx6_suspend_in_ocram_fn)(void __iomem *ocram_vbase); + + /* ++ * Function pointers to optional board pm functions ++ */ ++int (*imx6_board_pm_begin)(suspend_state_t); ++void (*imx6_board_pm_end)(void); ++EXPORT_SYMBOL(imx6_board_pm_begin); ++EXPORT_SYMBOL(imx6_board_pm_end); ++ ++/* + * suspend ocram space layout: + * ======================== high address ====================== + * . +@@ -427,12 +435,28 @@ static int imx6q_pm_enter(suspend_state_t state) + return 0; + } + ++static int imx6q_pm_begin(suspend_state_t state) ++{ ++ if (imx6_board_pm_begin) ++ return imx6_board_pm_begin(state); ++ ++ return 0; ++} ++ ++static void imx6q_pm_end(void) ++{ ++ if (imx6_board_pm_end) ++ imx6_board_pm_end(); ++} ++ + static int imx6q_pm_valid(suspend_state_t state) + { + return (state == PM_SUSPEND_STANDBY || state == PM_SUSPEND_MEM); + } + + static const struct platform_suspend_ops imx6q_pm_ops = { ++ .begin = imx6q_pm_begin, ++ .end = imx6q_pm_end, + .enter = imx6q_pm_enter, + .valid = imx6q_pm_valid, + }; diff --git a/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ul/0003-imx6ul-Add-MCA-core-I2C-driver-support.patch b/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ul/0003-imx6ul-Add-MCA-core-I2C-driver-support.patch new file mode 100644 index 000000000..da5b0e8b0 --- /dev/null +++ b/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ul/0003-imx6ul-Add-MCA-core-I2C-driver-support.patch @@ -0,0 +1,4452 @@ +From 2269c7056da80faf5821787373d72e1d9b3edadc Mon Sep 17 00:00:00 2001 +From: Alex Gonzalez +Date: Mon, 1 Apr 2019 13:52:06 +0200 +Subject: [PATCH] imx6ul: Add MCA core I2C driver support + +Synched with v4.14.78/master at +3f8b03950b323db4ca89b1cdc1c2288f79facaa3 + +Signed-off-by: Alex Gonzalez +--- + arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi | 12 + + drivers/mfd/Kconfig | 24 + + drivers/mfd/Makefile | 4 + + drivers/mfd/mca-cc6ul-core.c | 989 +++++++++++++++++++++ + drivers/mfd/mca-cc6ul-i2c.c | 219 +++++ + drivers/mfd/mca-cc6ul-irq.c | 113 +++ + drivers/mfd/mca-cc8x-core.c | 1040 ++++++++++++++++++++++ + drivers/mfd/mca-cc8x-i2c.c | 222 +++++ + drivers/mfd/mca-cc8x-irq.c | 143 +++ + include/linux/mfd/mca-cc6ul/core.h | 56 ++ + include/linux/mfd/mca-cc6ul/registers.h | 61 ++ + include/linux/mfd/mca-common/core.h | 71 ++ + include/linux/mfd/mca-common/registers.h | 1373 +++++++++++++++++++++++++++++ + 13 files changed, 4327 insertions(+) + create mode 100644 drivers/mfd/mca-cc6ul-core.c + create mode 100644 drivers/mfd/mca-cc6ul-i2c.c + create mode 100644 drivers/mfd/mca-cc6ul-irq.c + create mode 100644 drivers/mfd/mca-cc8x-core.c + create mode 100644 drivers/mfd/mca-cc8x-i2c.c + create mode 100644 drivers/mfd/mca-cc8x-irq.c + create mode 100644 include/linux/mfd/mca-cc6ul/core.h + create mode 100644 include/linux/mfd/mca-cc6ul/registers.h + create mode 100644 include/linux/mfd/mca-common/core.h + create mode 100644 include/linux/mfd/mca-common/registers.h + +diff --git a/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi b/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi +index 82fa176bf138..03c62926ca2b 100644 +--- a/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi ++++ b/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi +@@ -48,6 +48,18 @@ + pinctrl-0 = <&pinctrl_i2c1>; + status = "okay"; + ++ mca_cc6ul: mca@7e { ++ compatible = "digi,mca_cc6ul_dt_ids"; ++ reg = <0x7e>; ++ interrupt-parent = <&gpio5>; ++ interrupts = <4 IRQ_TYPE_EDGE_FALLING>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ fw-update-gpio = <&gpio4 14 GPIO_ACTIVE_LOW>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_mca_cc6ul>; ++ }; ++ + pfuze3000: pmic@8 { + compatible = "fsl,pfuze3000"; + reg = <0x08>; +diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig +index 8c5dfdce4326..25f1acff2d0a 100644 +--- a/drivers/mfd/Kconfig ++++ b/drivers/mfd/Kconfig +@@ -434,6 +434,30 @@ config MFD_MXS_LRADC + This driver can also be built as a module. If so, the module will be + called mxs-lradc. + ++config MFD_MCA_CC6UL ++ bool "Digi Micro Controller Assist for ConnectCore 6UL" ++ select MFD_CORE ++ select REGMAP_I2C ++ select REGMAP_IRQ ++ depends on I2C=y ++ help ++ Select this option to enable support for MCA on the ConnectCore 6UL. ++ This includes the I2C driver and core APIs. ++ Additional drivers must be enabled in order to use the functionality ++ of the device (RTC, watchdog, ...). ++ ++config MFD_MCA_CC8X ++ bool "Digi Micro Controller Assist for ConnectCore 8X" ++ select MFD_CORE ++ select REGMAP_I2C ++ select REGMAP_IRQ ++ depends on I2C=y ++ help ++ Select this option to enable support for MCA on the ConnectCore 8X. ++ This includes the I2C driver and core APIs. ++ Additional drivers must be enabled in order to use the functionality ++ of the device (RTC, watchdog, ...). ++ + config MFD_MX25_TSADC + tristate "Freescale i.MX25 integrated Touchscreen and ADC unit" + select REGMAP_MMIO +diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile +index 12980a4ad460..28998c947184 100644 +--- a/drivers/mfd/Makefile ++++ b/drivers/mfd/Makefile +@@ -242,3 +242,7 @@ obj-$(CONFIG_MFD_SC27XX_PMIC) += sprd-sc27xx-spi.o + obj-$(CONFIG_RAVE_SP_CORE) += rave-sp.o + obj-$(CONFIG_MFD_ROHM_BD718XX) += rohm-bd718x7.o + ++mca-cc6ul-objs := mca-cc6ul-core.o mca-cc6ul-irq.o mca-cc6ul-i2c.o ++obj-$(CONFIG_MFD_MCA_CC6UL) += mca-cc6ul.o ++mca-cc8x-objs := mca-cc8x-core.o mca-cc8x-irq.o mca-cc8x-i2c.o ++obj-$(CONFIG_MFD_MCA_CC8X) += mca-cc8x.o +diff --git a/drivers/mfd/mca-cc6ul-core.c b/drivers/mfd/mca-cc6ul-core.c +new file mode 100644 +index 000000000000..443cfe7e3ca1 +--- /dev/null ++++ b/drivers/mfd/mca-cc6ul-core.c +@@ -0,0 +1,989 @@ ++/* ++ * Copyright 2016 - 2019 Digi International Inc ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++ ++#define MCA_CC6UL_NVRAM_SIZE (MCA_CC6UL_MPU_NVRAM_END - MCA_CC6UL_MPU_NVRAM_START + 1) ++ ++extern int digi_get_som_hv(void); ++ ++struct dyn_attribute { ++ u16 since; /* Minimum firmware version required */ ++ struct attribute *attr; ++}; ++ ++struct mca_reason { ++ u32 flag; ++ const char *text; ++}; ++ ++static const struct mca_reason last_mca_reset[] = { ++ {MCA_CC6UL_LAST_MCA_RST_LLW, "LL Wakeup"}, ++ {MCA_CC6UL_LAST_MCA_RST_LVD, "Low Voltage"}, ++ {MCA_CC6UL_LAST_MCA_RST_WD, "Watchdog"}, ++ {MCA_CC6UL_LAST_MCA_RST_PIN, "Reset Pin"}, ++ {MCA_CC6UL_LAST_MCA_RST_PWRON, "Power On"}, ++ {MCA_CC6UL_LAST_MCA_RST_LOCKUP, "Core Lockup"}, ++ {MCA_CC6UL_LAST_MCA_RST_SW, "Software"}, ++ {MCA_CC6UL_LAST_MCA_RST_MDMAPP, "MDM-APP debuger"}, ++ {MCA_CC6UL_LAST_MCA_RST_SMAE, "Stop Mode Ack Error"}, ++}; ++ ++static const struct mca_reason last_mpu_reset[] = { ++ {MCA_CC6UL_LAST_MPU_RST_PWRON, "Power On"}, ++ {MCA_CC6UL_LAST_MPU_RST_SYSR, "System Reset"}, ++ {MCA_CC6UL_LAST_MPU_RST_WD, "Watchdog"}, ++ {MCA_CC6UL_LAST_MPU_RST_OFFWAKE,"Off wakeup"}, ++ {MCA_CC6UL_LAST_MPU_RST_MCARST, "MCA reset"}, ++}; ++ ++static const struct mca_reason last_wakeup[] = { ++ {MCA_CC6UL_LAST_WAKEUP_PWRIO, "Power IO"}, ++ {MCA_CC6UL_LAST_WAKEUP_TIMER, "Timer"}, ++ {MCA_CC6UL_LAST_WAKEUP_RTC, "RTC"}, ++ {MCA_CC6UL_LAST_WAKEUP_LPUART, "LP UART"}, ++ {MCA_CC6UL_LAST_WAKEUP_TAMPER0, "Tamper0"}, ++ {MCA_CC6UL_LAST_WAKEUP_TAMPER1, "Tamper1"}, ++ {MCA_CC6UL_LAST_WAKEUP_TAMPER2, "Tamper2"}, ++ {MCA_CC6UL_LAST_WAKEUP_TAMPER3, "Tamper3"}, ++ {MCA_CC6UL_LAST_WAKEUP_IO0, "IO0"}, ++ {MCA_CC6UL_LAST_WAKEUP_IO1, "IO1"}, ++ {MCA_CC6UL_LAST_WAKEUP_IO2, "IO2"}, ++ {MCA_CC6UL_LAST_WAKEUP_IO3, "IO3"}, ++ {MCA_CC6UL_LAST_WAKEUP_IO4, "IO4"}, ++ {MCA_CC6UL_LAST_WAKEUP_IO5, "IO5"}, ++ {MCA_CC6UL_LAST_WAKEUP_IO6, "IO6"}, ++ {MCA_CC6UL_LAST_WAKEUP_IO7, "IO7"}, ++ {MCA_CC6UL_LAST_WAKEUP_VCC, "Vcc"}, ++ {MCA_CC6UL_LAST_WAKEUP_CPU, "CPU"}, ++}; ++ ++static struct mca_drv *pmca; ++ ++static const char _enabled[] = "enabled"; ++static const char _disabled[] = "disabled"; ++ ++static struct resource mca_cc6ul_rtc_resources[] = { ++ { ++ .name = MCA_IRQ_RTC_ALARM_NAME, ++ .start = MCA_CC6UL_IRQ_RTC_ALARM, ++ .end = MCA_CC6UL_IRQ_RTC_ALARM, ++ .flags = IORESOURCE_IRQ, ++ }, ++ { ++ .name = MCA_IRQ_RTC_1HZ_NAME, ++ .start = MCA_CC6UL_IRQ_RTC_1HZ, ++ .end = MCA_CC6UL_IRQ_RTC_1HZ, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static struct resource mca_cc6ul_watchdog_resources[] = { ++ { ++ .name = MCA_IRQ_WATCHDOG_NAME, ++ .start = MCA_CC6UL_IRQ_WATCHDOG, ++ .end = MCA_CC6UL_IRQ_WATCHDOG, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static struct resource mca_cc6ul_pwrkey_resources[] = { ++ { ++ .name = MCA_IRQ_PWR_SLEEP_NAME, ++ .start = MCA_CC6UL_IRQ_PWR_SLEEP, ++ .end = MCA_CC6UL_IRQ_PWR_SLEEP, ++ .flags = IORESOURCE_IRQ, ++ }, ++ { ++ .name = MCA_IRQ_PWR_OFF_NAME, ++ .start = MCA_CC6UL_IRQ_PWR_OFF, ++ .end = MCA_CC6UL_IRQ_PWR_OFF, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static struct resource mca_cc6ul_adc_resources[] = { ++ { ++ .name = MCA_IRQ_ADC_NAME, ++ .start = MCA_CC6UL_IRQ_ADC, ++ .end = MCA_CC6UL_IRQ_ADC, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static struct resource mca_cc6ul_tamper_resources[] = { ++ { ++ .name = MCA_IRQ_TAMPER0_NAME, ++ .start = MCA_CC6UL_IRQ_TAMPER0, ++ .end = MCA_CC6UL_IRQ_TAMPER0, ++ .flags = IORESOURCE_IRQ, ++ }, ++ { ++ .name = MCA_IRQ_TAMPER1_NAME, ++ .start = MCA_CC6UL_IRQ_TAMPER1, ++ .end = MCA_CC6UL_IRQ_TAMPER1, ++ .flags = IORESOURCE_IRQ, ++ }, ++ { ++ .name = MCA_IRQ_TAMPER2_NAME, ++ .start = MCA_CC6UL_IRQ_TAMPER2, ++ .end = MCA_CC6UL_IRQ_TAMPER2, ++ .flags = IORESOURCE_IRQ, ++ }, ++ { ++ .name = MCA_IRQ_TAMPER3_NAME, ++ .start = MCA_CC6UL_IRQ_TAMPER3, ++ .end = MCA_CC6UL_IRQ_TAMPER3, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static struct resource mca_cc6ul_gpios_resources[] = { ++ { ++ .name = MCA_IRQ_GPIO_BANK_0_NAME, ++ .start = MCA_CC6UL_IRQ_GPIO_BANK_0, ++ .end = MCA_CC6UL_IRQ_GPIO_BANK_0, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static struct resource mca_cc6ul_uart_resources[] = { ++ { ++ .name = MCA_IRQ_UART_NAME, ++ .start = MCA_CC6UL_IRQ_UART, ++ .end = MCA_CC6UL_IRQ_UART, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static const struct mfd_cell mca_cc6ul_devs[] = { ++ { ++ .name = MCA_CC6UL_DRVNAME_RTC, ++ .num_resources = ARRAY_SIZE(mca_cc6ul_rtc_resources), ++ .resources = mca_cc6ul_rtc_resources, ++ .of_compatible = "digi,mca-cc6ul-rtc", ++ }, ++ { ++ .name = MCA_CC6UL_DRVNAME_WATCHDOG, ++ .num_resources = ARRAY_SIZE(mca_cc6ul_watchdog_resources), ++ .resources = mca_cc6ul_watchdog_resources, ++ .of_compatible = "digi,mca-cc6ul-watchdog", ++ }, ++ { ++ .name = MCA_CC6UL_DRVNAME_GPIO, ++ .num_resources = ARRAY_SIZE(mca_cc6ul_gpios_resources), ++ .resources = mca_cc6ul_gpios_resources, ++ .of_compatible = "digi,mca-cc6ul-gpio", ++ }, ++ { ++ .name = MCA_CC6UL_DRVNAME_PWRKEY, ++ .num_resources = ARRAY_SIZE(mca_cc6ul_pwrkey_resources), ++ .resources = mca_cc6ul_pwrkey_resources, ++ .of_compatible = "digi,mca-cc6ul-pwrkey", ++ }, ++ { ++ .name = MCA_CC6UL_DRVNAME_ADC, ++ .of_compatible = "digi,mca-cc6ul-adc", ++ .num_resources = ARRAY_SIZE(mca_cc6ul_adc_resources), ++ .resources = mca_cc6ul_adc_resources, ++ }, ++ { ++ .name = MCA_CC6UL_DRVNAME_TAMPER, ++ .num_resources = ARRAY_SIZE(mca_cc6ul_tamper_resources), ++ .resources = mca_cc6ul_tamper_resources, ++ .of_compatible = "digi,mca-cc6ul-tamper", ++ }, ++ { ++ .name = MCA_CC6UL_DRVNAME_UART, ++ .num_resources = ARRAY_SIZE(mca_cc6ul_uart_resources), ++ .resources = mca_cc6ul_uart_resources, ++ .of_compatible = "digi,mca-cc6ul-uart", ++ }, ++}; ++ ++/* Read a block of registers */ ++int mca_cc6ul_read_block(struct mca_drv *mca, u16 addr, u8 *data, ++ size_t nregs) ++{ ++ int ret; ++ ++ /* TODO, check limits nregs... */ ++ ++ ret = regmap_raw_read(mca->regmap, addr, data, nregs); ++ if (ret != 0) ++ return ret; ++ ++ return ret; ++ ++} ++EXPORT_SYMBOL_GPL(mca_cc6ul_read_block); ++ ++/* Write a block of data into MCA registers */ ++int mca_cc6ul_write_block(struct mca_drv *mca , u16 addr, u8 *data, ++ size_t nregs) ++{ ++ u8 *frame; /* register address + payload */ ++ u8 *payload; ++ int ret; ++ ++ /* TODO, check limits nregs... */ ++ ++ frame = kzalloc(sizeof(addr) + nregs, GFP_KERNEL | GFP_DMA); ++ if (!frame) ++ return -ENOMEM; ++ ++ payload = frame + sizeof(addr); ++ memcpy(payload, data, nregs); ++ ++ /* Write payload */ ++ ret = regmap_raw_write(mca->regmap, addr, payload, nregs); ++ ++ kfree(frame); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(mca_cc6ul_write_block); ++ ++static int mca_cc6ul_unlock_ctrl(struct mca_drv *mca) ++{ ++ int ret; ++ const uint8_t unlock_pattern[] = {'C', 'T', 'R', 'U'}; ++ ++ ret = regmap_bulk_write(mca->regmap, MCA_CTRL_UNLOCK_0, ++ unlock_pattern, sizeof(unlock_pattern)); ++ if (ret) ++ dev_warn(mca->dev, "failed to unlock CTRL registers (%d)\n", ++ ret); ++ ++ return ret; ++} ++ ++static int mca_cc6ul_get_tick_cnt(struct mca_drv *mca, u32 *tick) ++{ ++ return regmap_bulk_read(mca->regmap, MCA_TIMER_TICK_0, ++ tick, sizeof(*tick)); ++} ++ ++/* sysfs attributes */ ++static ssize_t ext_32khz_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct mca_drv *mca = dev_get_drvdata(dev); ++ unsigned int val; ++ int ret; ++ ++ ret = regmap_read(mca->regmap, MCA_CTRL_0, &val); ++ if (ret) { ++ dev_err(mca->dev, "Cannot read MCA CTRL_0 register(%d)\n", ++ ret); ++ return 0; ++ } ++ ++ return sprintf(buf, "%s\n", val & MCA_EXT32K_EN ? ++ _enabled : _disabled); ++} ++ ++static ssize_t ext_32khz_store(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct mca_drv *mca = dev_get_drvdata(dev); ++ bool enable; ++ int ret; ++ ++ if (!strncmp(buf, _enabled, sizeof(_enabled) - 1)) ++ enable = true; ++ else if (!strncmp(buf, _disabled, sizeof(_disabled) - 1)) ++ enable = false; ++ else ++ return -EINVAL; ++ ++ ret = mca_cc6ul_unlock_ctrl(mca); ++ if (ret) ++ return ret; ++ ++ ret = regmap_update_bits(mca->regmap, MCA_CTRL_0, ++ MCA_EXT32K_EN, ++ enable ? MCA_EXT32K_EN : 0); ++ if (ret) { ++ dev_err(mca->dev, "Cannot update MCA CTRL_0 register (%d)\n", ret); ++ return ret; ++ } ++ ++ return count; ++} ++static DEVICE_ATTR(ext_32khz, 0600, ext_32khz_show, ext_32khz_store); ++ ++static ssize_t vref_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct mca_drv *mca = dev_get_drvdata(dev); ++ unsigned int val; ++ int ret; ++ ++ ret = regmap_read(mca->regmap, MCA_CTRL_0, &val); ++ if (ret) { ++ dev_err(mca->dev, "Cannot read MCA CTRL_0 register(%d)\n", ++ ret); ++ return 0; ++ } ++ ++ return sprintf(buf, "%s\n", val & MCA_VREF_EN ? ++ _enabled : _disabled); ++} ++ ++static ssize_t vref_store(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct mca_drv *mca = dev_get_drvdata(dev); ++ bool enable; ++ int ret; ++ ++ if (!strncmp(buf, _enabled, sizeof(_enabled) - 1)) ++ enable = true; ++ else if (!strncmp(buf, _disabled, sizeof(_disabled) - 1)) ++ enable = false; ++ else ++ return -EINVAL; ++ ++ ret = mca_cc6ul_unlock_ctrl(mca); ++ if (ret) ++ return ret; ++ ++ ret = regmap_update_bits(mca->regmap, MCA_CTRL_0, ++ MCA_VREF_EN, ++ enable ? MCA_VREF_EN : 0); ++ if (ret) { ++ dev_err(mca->dev, "Cannot update MCA CTRL_0 register (%d)\n", ret); ++ return ret; ++ } ++ ++ return count; ++} ++static DEVICE_ATTR(vref, 0600, vref_show, vref_store); ++ ++static ssize_t hwver_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct mca_drv *mca = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%d\n", mca->hw_version); ++} ++static DEVICE_ATTR(hw_version, S_IRUGO, hwver_show, NULL); ++ ++static ssize_t fwver_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct mca_drv *mca = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%d.%02d %s\n", MCA_FW_VER_MAJOR(mca->fw_version), ++ MCA_FW_VER_MINOR(mca->fw_version), ++ mca->fw_is_alpha ? "(alpha)" : ""); ++} ++static DEVICE_ATTR(fw_version, S_IRUGO, fwver_show, NULL); ++ ++static ssize_t tick_cnt_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct mca_drv *mca = dev_get_drvdata(dev); ++ u32 tick_cnt; ++ int ret; ++ ++ ret = mca_cc6ul_get_tick_cnt(mca, &tick_cnt); ++ if (ret) { ++ dev_err(mca->dev, "Cannot read MCA tick counter(%d)\n", ret); ++ return ret; ++ } ++ ++ return sprintf(buf, "%u\n", tick_cnt); ++} ++static DEVICE_ATTR(tick_cnt, S_IRUGO, tick_cnt_show, NULL); ++ ++static ssize_t fw_update_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct mca_drv *mca = dev_get_drvdata(dev); ++ ++ if (!gpio_is_valid(mca->fw_update_gpio)) ++ return -EINVAL; ++ ++ return sprintf(buf, "%d\n", ++ gpio_get_value_cansleep(mca->fw_update_gpio)); ++} ++ ++static ssize_t fw_update_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct mca_drv *mca = dev_get_drvdata(dev); ++ ssize_t status; ++ long value; ++ ++ if (!gpio_is_valid(mca->fw_update_gpio)) ++ return -EINVAL; ++ ++ status = kstrtol(buf, 0, &value); ++ if (status == 0) { ++ gpio_set_value_cansleep(mca->fw_update_gpio, value); ++ status = count; ++ } ++ ++ return status; ++} ++static DEVICE_ATTR(fw_update, 0600, fw_update_show, fw_update_store); ++ ++static ssize_t last_wakeup_reason_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct mca_drv *mca = dev_get_drvdata(dev); ++ bool comma = false; ++ u32 last_wakeup_val; ++ int ret, i; ++ ++ ret = regmap_bulk_read(mca->regmap, MCA_LAST_WAKEUP_REASON_0, ++ &last_wakeup_val, sizeof(last_wakeup_val)); ++ if (ret) { ++ dev_err(mca->dev, ++ "Cannot read last MCA wakeup reason (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ buf[0] = 0; ++ ++ for (i = 0; i < ARRAY_SIZE(last_wakeup); i++) { ++ if (last_wakeup[i].flag & last_wakeup_val) { ++ if (comma) ++ strcat(buf, ", "); ++ strcat(buf, last_wakeup[i].text); ++ comma = true; ++ } ++ } ++ ++ if (comma) ++ strcat(buf, "\n"); ++ ++ return strlen(buf); ++} ++static DEVICE_ATTR(last_wakeup_reason, S_IRUGO, last_wakeup_reason_show, NULL); ++ ++static ssize_t last_mca_reset_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct mca_drv *mca = dev_get_drvdata(dev); ++ bool comma = false; ++ int i; ++ ++ buf[0] = 0; ++ ++ for (i = 0; i < ARRAY_SIZE(last_mca_reset); i++) { ++ if (last_mca_reset[i].flag & mca->last_mca_reset) { ++ if (comma) ++ strcat(buf, ", "); ++ strcat(buf, last_mca_reset[i].text); ++ comma = true; ++ } ++ } ++ ++ if (comma) ++ strcat(buf, "\n"); ++ ++ return strlen(buf); ++} ++static DEVICE_ATTR(last_mca_reset, S_IRUGO, last_mca_reset_show, NULL); ++ ++static ssize_t last_mpu_reset_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct mca_drv *mca = dev_get_drvdata(dev); ++ bool comma = false; ++ int i; ++ ++ buf[0] = 0; ++ ++ for (i = 0; i < ARRAY_SIZE(last_mpu_reset); i++) { ++ if (last_mpu_reset[i].flag & mca->last_mpu_reset) { ++ if (comma) ++ strcat(buf, ", "); ++ strcat(buf, last_mpu_reset[i].text); ++ comma = true; ++ } ++ } ++ ++ if (comma) ++ strcat(buf, "\n"); ++ ++ return strlen(buf); ++} ++static DEVICE_ATTR(last_mpu_reset, S_IRUGO, last_mpu_reset_show, NULL); ++ ++static ssize_t nvram_read(struct file *filp, struct kobject *kobj, ++ struct bin_attribute *attr, char *buf, loff_t off, ++ size_t count) ++{ ++ struct device *dev = kobj_to_dev(kobj); ++ struct mca_drv *mca; ++ int ret; ++ ++ if (!dev || (mca = dev_get_drvdata(dev)) == NULL) ++ return -ENODEV; ++ ++ if (unlikely(off >= MCA_CC6UL_NVRAM_SIZE) || unlikely(!count)) ++ return 0; ++ if ((off + count) > MCA_CC6UL_NVRAM_SIZE) ++ count = MCA_CC6UL_NVRAM_SIZE - off; ++ ++ ret = regmap_bulk_read(mca->regmap, ++ MCA_CC6UL_MPU_NVRAM_START + off, buf, count); ++ if (ret) { ++ dev_err(mca->dev, "%s error (%d)\n", __func__, ret); ++ return ret; ++ } ++ ++ return count; ++} ++ ++static ssize_t nvram_write(struct file *filp, struct kobject *kobj, ++ struct bin_attribute *attr, char *buf, loff_t off, ++ size_t count) ++{ ++ struct device *dev = kobj_to_dev(kobj); ++ struct mca_drv *mca; ++ int ret; ++ ++ if (!dev || (mca = dev_get_drvdata(dev)) == NULL) ++ return -ENODEV; ++ ++ if (unlikely(off >= MCA_CC6UL_NVRAM_SIZE)) ++ return -EFBIG; ++ if ((off + count) > MCA_CC6UL_NVRAM_SIZE) ++ count = MCA_CC6UL_NVRAM_SIZE - off; ++ if (unlikely(!count)) ++ return count; ++ ++ ret = regmap_bulk_write(mca->regmap, ++ MCA_CC6UL_MPU_NVRAM_START + off, buf, count); ++ if (ret) { ++ dev_err(mca->dev, "%s error (%d)\n", __func__, ret); ++ return ret; ++ } ++ ++ return count; ++} ++ ++static struct attribute *mca_cc6ul_sysfs_entries[] = { ++ &dev_attr_ext_32khz.attr, ++ &dev_attr_hw_version.attr, ++ &dev_attr_fw_version.attr, ++ &dev_attr_fw_update.attr, ++ NULL, ++}; ++ ++static struct attribute_group mca_cc6ul_attr_group = { ++ .name = NULL, /* put in device directory */ ++ .attrs = mca_cc6ul_sysfs_entries, ++}; ++ ++static struct dyn_attribute mca_cc6ul_sysfs_dyn_entries[] = { ++ { ++ .since = MCA_MAKE_FW_VER(0,15), ++ .attr = &dev_attr_tick_cnt.attr, ++ }, ++ { ++ .since = MCA_MAKE_FW_VER(0,15), ++ .attr = &dev_attr_vref.attr, ++ }, ++ { ++ .since = MCA_MAKE_FW_VER(1,2), ++ .attr = &dev_attr_last_wakeup_reason.attr, ++ }, ++ { ++ .since = MCA_MAKE_FW_VER(1,2), ++ .attr = &dev_attr_last_mca_reset.attr, ++ }, ++ { ++ .since = MCA_MAKE_FW_VER(1,2), ++ .attr = &dev_attr_last_mpu_reset.attr, ++ }, ++}; ++ ++int mca_cc6ul_suspend(struct device *dev) ++{ ++ struct mca_drv *mca = dev_get_drvdata(dev); ++ ++ if (!mca) { ++ dev_err(dev, " mca was null in %s\n", __func__); ++ return -ENODEV; ++ } ++ ++ /* Set the suspend bit in PWR_CTRL_0 */ ++ return regmap_update_bits(pmca->regmap, MCA_PWR_CTRL_0, ++ MCA_PWR_GO_SUSPEND, ++ MCA_PWR_GO_SUSPEND); ++} ++ ++#define MCA_MAX_RESUME_RD_RETRIES 10 ++int mca_cc6ul_resume(struct device *dev) ++{ ++ struct mca_drv *mca = dev_get_drvdata(dev); ++ unsigned int val; ++ int ret, retries = 0; ++ ++ if (!mca) { ++ dev_err(dev, " mca was null in %s\n", __func__); ++ return -ENODEV; ++ } ++ ++ /* ++ * Generate traffic on the i2c bus to wakeup the MCA, in case it was in ++ * low power ++ */ ++ do { ++ ret = regmap_read(mca->regmap, MCA_DEVICE_ID, &val); ++ if (!ret && mca->dev_id == (u8)val) ++ break; ++ udelay(50); ++ } while (++retries < MCA_MAX_RESUME_RD_RETRIES); ++ ++ if (retries == MCA_MAX_RESUME_RD_RETRIES) { ++ dev_err(mca->dev, "unable to wake up MCA (%d)\n", ret); ++ return ret; ++ } ++ ++ /* Reset the suspend bit in PWR_CTRL_0 */ ++ return regmap_update_bits(pmca->regmap, MCA_PWR_CTRL_0, ++ MCA_PWR_GO_SUSPEND, ++ 0); ++} ++ ++#define MCA_MAX_PWROFF_TRIES 5 ++static void mca_cc6ul_power_off(void) ++{ ++ int try = 0; ++ int ret; ++ ++ if (!pmca) { ++ printk(KERN_ERR "ERROR: unable to power off [%s:%d/%s()]!\n", ++ __FILE__, __LINE__, __func__); ++ return; ++ } ++ ++ do { ++ /* Set power off bit in PWR_CTRL_0 register to shutdown */ ++ ret = regmap_update_bits(pmca->regmap, MCA_PWR_CTRL_0, ++ MCA_PWR_GO_OFF, ++ MCA_PWR_GO_OFF); ++ if (ret) ++ printk(KERN_ERR "ERROR: accesing PWR_CTRL_0 register " ++ "[%s:%d/%s()]!\n", __FILE__, __LINE__, __func__); ++ ++ /* ++ * Even if the regmap update returned with success, retry... ++ * we are powering off, so there is nothing bad by doing it. ++ */ ++ mdelay(50); ++ } while (++try < MCA_MAX_PWROFF_TRIES); ++ ++ /* Print a warning and return, so at least userland can log the issue */ ++ printk(KERN_ERR "ERROR: unable to power off [%s:%d/%s()]!\n", ++ __FILE__, __LINE__, __func__); ++} ++ ++#define MCA_MAX_RESET_TRIES 5 ++static int mca_cc6ul_restart_handler(struct notifier_block *nb, ++ unsigned long mode, void *cmd) ++{ ++ int ret; ++ int try = 0; ++ struct mca_drv *mca = container_of(nb, struct mca_drv, ++ restart_handler); ++ const uint8_t unlock_pattern[] = {'C', 'T', 'R', 'U'}; ++ ++ do { ++ ret = regmap_bulk_write(mca->regmap, MCA_CTRL_UNLOCK_0, ++ unlock_pattern, sizeof(unlock_pattern)); ++ if (ret) { ++ dev_err(mca->dev, "failed to unlock ctrl regs (%d)\n", ++ ret); ++ goto reset_retry; ++ } ++ ++ ret = regmap_write(pmca->regmap, MCA_CTRL_0, ++ MCA_RESET); ++ if (ret) ++ dev_err(mca->dev, "failed to reset (%d)\n", ret); ++ ++ /* ++ * The MCA will reset the cpu, so the retry should not happen... ++ * and if it happens, something went wrong, and retrying is the ++ * right thing to do. ++ */ ++reset_retry: ++ mdelay(10); ++ } while (++try < MCA_MAX_RESET_TRIES); ++ ++ dev_err(mca->dev, "failed to reboot!\n"); ++ ++ return NOTIFY_DONE; ++} ++ ++static int mca_cc6ul_add_dyn_sysfs_entries(struct mca_drv *mca, ++ const struct dyn_attribute *dattr, ++ int num_entries, ++ const struct attribute_group *grp) ++{ ++ int ret, i; ++ ++ if (!mca || !dattr || !grp) ++ return -EINVAL; ++ ++ for (i = 0; i < num_entries; i++, dattr++) { ++ if (!dattr->attr) ++ continue; ++ ++ /* Create the sysfs files if the MCA fw supports the feature*/ ++ if (mca->fw_version >= dattr->since) { ++ ret = sysfs_add_file_to_group(&mca->dev->kobj, ++ dattr->attr, ++ grp->name); ++ if (ret) ++ dev_warn(mca->dev, ++ "Cannot create sysfs file %s (%d)\n", ++ dattr->attr->name, ret); ++ } ++ } ++ ++ return 0; ++} ++ ++int mca_cc6ul_device_init(struct mca_drv *mca, u32 irq) ++{ ++ int ret; ++ unsigned int val; ++ ++ ret = regmap_read(mca->regmap, MCA_DEVICE_ID, &val); ++ if (ret != 0) { ++ dev_err(mca->dev, "Cannot read MCA Device ID (%d)\n", ret); ++ return ret; ++ } ++ mca->dev_id = (u8)val; ++ ++ if (mca->dev_id != MCA_CC6UL_DEVICE_ID_VAL) { ++ dev_err(mca->dev, "Invalid MCA Device ID (%x)\n", mca->dev_id); ++ return -ENODEV; ++ } ++ ++ ret = regmap_read(mca->regmap, MCA_HW_VER, &val); ++ if (ret != 0) { ++ dev_err(mca->dev, "Cannot read MCA Hardware Version (%d)\n", ++ ret); ++ return ret; ++ } ++ mca->hw_version = (u8)val; ++ ++ ret = regmap_bulk_read(mca->regmap, MCA_FW_VER_L, &val, 2); ++ if (ret != 0) { ++ dev_err(mca->dev, "Cannot read MCA Firmware Version (%d)\n", ++ ret); ++ return ret; ++ } ++ mca->fw_version = (u16)(val & ~MCA_FW_VER_ALPHA_MASK); ++ mca->fw_is_alpha = val & MCA_FW_VER_ALPHA_MASK ? true : false; ++ ++ if (mca->fw_version >= MCA_MAKE_FW_VER(1, 2)) { ++ ret = regmap_bulk_read(mca->regmap, MCA_LAST_MCA_RESET_0, ++ &mca->last_mca_reset, ++ sizeof(mca->last_mca_reset)); ++ if (ret) { ++ dev_err(mca->dev, ++ "Cannot read MCA last reset (%d)\n", ret); ++ return ret; ++ } ++ ++ ret = regmap_bulk_read(mca->regmap, MCA_LAST_MPU_RESET_0, ++ &mca->last_mpu_reset, ++ sizeof(mca->last_mpu_reset)); ++ if (ret) { ++ dev_err(mca->dev, ++ "Cannot read MPU last reset (%d)\n", ret); ++ return ret; ++ } ++ } ++ ++ /* Write the SOM hardware version to MCA register */ ++ mca->som_hv = digi_get_som_hv(); ++ if (mca->som_hv > 0) { ++ ret = regmap_write(mca->regmap, MCA_HWVER_SOM, ++ mca->som_hv); ++ if (ret != 0) ++ dev_warn(mca->dev, ++ "Cannot set SOM hardware version (%d)\n", ret); ++ } ++ ++ mca->fw_update_gpio = of_get_named_gpio(mca->dev->of_node, ++ "fw-update-gpio", 0); ++ if (gpio_is_valid(mca->fw_update_gpio) && mca->som_hv >= 4) { ++ /* ++ * On the CC6UL HV >= 4 this GPIO must be driven low ++ * so that the CPU resets together with the reset button. ++ */ ++ if (devm_gpio_request_one(mca->dev, mca->fw_update_gpio, ++ GPIOF_OUT_INIT_LOW, "mca-fw-update")) ++ dev_warn(mca->dev, "failed to get fw-update-gpio: %d\n", ++ ret); ++ } else { ++ /* Invalidate GPIO */ ++ mca->fw_update_gpio = -EINVAL; ++ } ++ ++ mca->chip_irq = irq; ++ mca->gpio_base = -1; ++ ++ ret = mca_cc6ul_irq_init(mca); ++ if (ret != 0) { ++ dev_err(mca->dev, "Cannot initialize interrupts (%d)\n", ret); ++ return ret; ++ } ++ ++ ret = mfd_add_devices(mca->dev, -1, mca_cc6ul_devs, ++ ARRAY_SIZE(mca_cc6ul_devs), NULL, mca->irq_base, ++ regmap_irq_get_domain(mca->regmap_irq)); ++ if (ret) { ++ dev_err(mca->dev, "Cannot add MFD cells (%d)\n", ret); ++ goto out_irq; ++ } ++ ++ ret = sysfs_create_group(&mca->dev->kobj, &mca_cc6ul_attr_group); ++ if (ret) { ++ dev_err(mca->dev, "Cannot create sysfs entries (%d)\n", ret); ++ goto out_dev; ++ } ++ if (mca->fw_update_gpio == -EINVAL) { ++ /* Remove fw_update entry */ ++ sysfs_remove_file(&mca->dev->kobj, &dev_attr_fw_update.attr); ++ } ++ ++ ret = mca_cc6ul_add_dyn_sysfs_entries(mca, mca_cc6ul_sysfs_dyn_entries, ++ ARRAY_SIZE(mca_cc6ul_sysfs_dyn_entries), ++ &mca_cc6ul_attr_group); ++ if (ret) { ++ dev_err(mca->dev, "Cannot create sysfs dynamic entries (%d)\n", ++ ret); ++ goto out_sysfs_remove; ++ } ++ ++ pmca = mca; ++ ++ if (pm_power_off != NULL) { ++ dev_warn(mca->dev, "pm_power_off function already registered. " ++ "Will be override by MCA function.\n"); ++ } ++ pm_power_off = mca_cc6ul_power_off; ++ ++ /* ++ * To avoid error messages when resuming from suspend, increase the I2C ++ * bus' usage counter so the linux pm_runtime framework wakes it from ++ * suspend before trying to read the MCA's IRQ status. This indicates that ++ * the bus is in use when the system is going to suspend, making linux wake ++ * it up as soon as possible so any operations that were halted continue ++ * without issues after resuming. ++ * ++ * The device hierarchy is the following: ++ * ++ * mca_cc8x 0-0063 -> i2c i2c-0 -> imx-lpi2c 5a800000.i2c ++ */ ++ pm_runtime_get_noresume(mca->dev->parent->parent); ++ ++ /* ++ * Register the MCA restart handler with high priority to ensure it is ++ * called first ++ */ ++ mca->restart_handler.notifier_call = mca_cc6ul_restart_handler; ++ mca->restart_handler.priority = 200; ++ ret = register_restart_handler(&mca->restart_handler); ++ if (ret) { ++ dev_err(mca->dev, ++ "failed to register restart handler (%d)\n", ret); ++ goto out_pwr_off; ++ } ++ ++ if (mca->fw_version >= MCA_MAKE_FW_VER(1, 2)) { ++ mca->nvram = devm_kzalloc(mca->dev, sizeof(struct bin_attribute), ++ GFP_KERNEL); ++ if (!mca->nvram) { ++ dev_err(mca->dev, "Cannot allocate memory for nvram\n"); ++ goto out_pwr_off; ++ } ++ ++ sysfs_bin_attr_init(mca->nvram); ++ ++ mca->nvram->attr.name = "nvram"; ++ mca->nvram->attr.mode = S_IRUGO | S_IWUSR; ++ ++ mca->nvram->read = nvram_read; ++ mca->nvram->write = nvram_write; ++ ++ ret = sysfs_create_bin_file(&mca->dev->kobj, mca->nvram); ++ if (ret) { ++ dev_err(mca->dev, "Cannot create sysfs file: %s\n", ++ mca->nvram->attr.name); ++ goto out_nvram; ++ } ++ } ++ ++ return 0; ++ ++out_nvram: ++ kfree(mca->nvram); ++out_pwr_off: ++ pm_power_off = NULL; ++out_sysfs_remove: ++ pmca = NULL; ++ sysfs_remove_group(&mca->dev->kobj, &mca_cc6ul_attr_group); ++out_dev: ++ mfd_remove_devices(mca->dev); ++out_irq: ++ mca_cc6ul_irq_exit(mca); ++ ++ return ret; ++} ++ ++void mca_cc6ul_device_exit(struct mca_drv *mca) ++{ ++ unregister_restart_handler(&mca->restart_handler); ++ pm_power_off = NULL; ++ pmca = NULL; ++ sysfs_remove_group(&mca->dev->kobj, &mca_cc6ul_attr_group); ++ mfd_remove_devices(mca->dev); ++ mca_cc6ul_irq_exit(mca); ++ kfree(mca->nvram); ++} ++ ++MODULE_AUTHOR("Digi International Inc"); ++MODULE_DESCRIPTION("MCA driver for ConnectCore 6UL"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/mfd/mca-cc6ul-i2c.c b/drivers/mfd/mca-cc6ul-i2c.c +new file mode 100644 +index 000000000000..59ba56a97d9d +--- /dev/null ++++ b/drivers/mfd/mca-cc6ul-i2c.c +@@ -0,0 +1,219 @@ ++/* ++ * Copyright 2016 - 2019 Digi International Inc ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++ ++static const struct regmap_range mca_cc6ul_readable_ranges[] = { ++}; ++ ++static const struct regmap_range mca_cc6ul_writeable_ranges[] = { ++ regmap_reg_range(MCA_HWVER_SOM, MCA_HWVER_SOM), ++ regmap_reg_range(MCA_IRQ_STATUS_0, MCA_IRQ_MASK_3), ++ regmap_reg_range(MCA_PWR_CTRL_0, MCA_PWR_KEY_GUARD), ++ regmap_reg_range(MCA_CTRL_UNLOCK_0, MCA_CTRL_UNLOCK_3), ++ regmap_reg_range(MCA_CTRL_0, MCA_CTRL_0), ++ regmap_reg_range(MCA_TAMPER0_CFG0, MCA_TAMPER0_EVENT), ++ regmap_reg_range(MCA_TAMPER1_CFG0, MCA_TAMPER1_EVENT), ++ regmap_reg_range(MCA_TAMPER2_CFG0, MCA_TAMPER2_THRESH_HI_H), ++ regmap_reg_range(MCA_TAMPER3_CFG0, MCA_TAMPER3_THRESH_HI_H), ++ regmap_reg_range(MCA_RTC_CONTROL, MCA_RTC_CONTROL), ++ regmap_reg_range(MCA_RTC_COUNT_YEAR_L, MCA_RTC_ALARM_SEC), ++ regmap_reg_range(MCA_WDT_CONTROL, MCA_WDT_REFRESH_3), ++ regmap_reg_range(MCA_GPIO_DIR_0, MCA_GPIO_DEB_CNT_63), ++ regmap_reg_range(MCA_REG_ADC_CFG0_0, MCA_REG_ADC_CFG0_7), ++ regmap_reg_range(MCA_REG_ADC_CFG1_0, MCA_REG_ADC_CFG1_7), ++ regmap_reg_range(MCA_REG_ADC_CFG2_0, MCA_REG_ADC_CFG2_7), ++ regmap_reg_range(MCA_REG_ADC_SAMPLES_CNT_0, MCA_REG_ADC_SAMPLES_CNT_7), ++ regmap_reg_range(MCA_REG_ADC_THRESH_LO_L_0, MCA_REG_ADC_THRESH_LO_H_7), ++ regmap_reg_range(MCA_REG_ADC_THRESH_HI_L_0, MCA_REG_ADC_THRESH_HI_H_7), ++ regmap_reg_range(MCA_REG_ADC_TICKS_L_0, MCA_REG_ADC_TICKS_H_7), ++ regmap_reg_range(MCA_REG_ADC_IRQ_0, MCA_REG_ADC_IRQ_7), ++ regmap_reg_range(MCA_REG_ADC_CFG_0, MCA_REG_ADC_CFG_2), ++ regmap_reg_range(MCA_REG_ADC_BUFF_CH, MCA_REG_ADC_BUFF_SAMPLE_7), ++ regmap_reg_range(MCA_REG_UART_THR, MCA_REG_UART_RTSPIN), ++ regmap_reg_range(MCA_CC6UL_MPU_NVRAM_START, MCA_CC6UL_MPU_NVRAM_END), ++}; ++ ++static const struct regmap_range mca_cc6ul_volatile_ranges[] = { ++ /* Real volatile registers */ ++ regmap_reg_range(MCA_IRQ_STATUS_0, MCA_IRQ_STATUS_3), ++ regmap_reg_range(MCA_TAMPER0_DATE_START, MCA_TAMPER0_EVENT), ++ regmap_reg_range(MCA_TAMPER1_DATE_START, MCA_TAMPER1_EVENT), ++ regmap_reg_range(MCA_TAMPER2_DATE_START, MCA_TAMPER2_EVENT), ++ regmap_reg_range(MCA_TAMPER3_DATE_START, MCA_TAMPER3_EVENT), ++ regmap_reg_range(MCA_TIMER_TICK_0, MCA_TIMER_TICK_3), ++ regmap_reg_range(MCA_LAST_MCA_RESET_0, MCA_LAST_MCA_RESET_3), ++ regmap_reg_range(MCA_LAST_MPU_RESET_0, MCA_LAST_MPU_RESET_3), ++ regmap_reg_range(MCA_LAST_WAKEUP_REASON_0, MCA_LAST_WAKEUP_REASON_3), ++ regmap_reg_range(MCA_CC6UL_MPU_NVRAM_START, MCA_CC6UL_MPU_NVRAM_END), ++ regmap_reg_range(MCA_RTC_COUNT_YEAR_L, MCA_RTC_COUNT_SEC), ++ regmap_reg_range(MCA_GPIO_DATA_0, MCA_GPIO_DATA_7), ++ regmap_reg_range(MCA_GPIO_IRQ_STATUS_0, MCA_GPIO_IRQ_STATUS_7), ++ regmap_reg_range(MCA_PWR_CTRL_0, MCA_PWR_STATUS_0), ++ regmap_reg_range(MCA_REG_ADC_VAL_L_0, MCA_REG_ADC_VAL_H_7), ++ ++ /* ++ * Fake volatile registers. ++ * ++ * These registers could be cached but non-volatile registers makes ++ * regmap access each register one by one which has some drawbacks: ++ * - Breaks CRC in the protocol. ++ * - Requires the MCA firmware to process each access as a separate ++ * access, even when the data requested must be returned in bulk. ++ * ++ * For this reasons we will consider all registers volatile. ++ */ ++ regmap_reg_range(MCA_HWVER_SOM, MCA_HWVER_SOM), ++ regmap_reg_range(MCA_DEVICE_ID, MCA_UID_9), ++ regmap_reg_range(MCA_IRQ_MASK_0, MCA_IRQ_MASK_3), ++ regmap_reg_range(MCA_PWR_KEY_DEBOUNCE, MCA_PWR_KEY_GUARD), ++ regmap_reg_range(MCA_CTRL_0, MCA_CTRL_0), ++ regmap_reg_range(MCA_TAMPER0_CFG0, MCA_TAMPER0_DELAY_PWROFF), ++ regmap_reg_range(MCA_TAMPER1_CFG0, MCA_TAMPER1_DELAY_PWROFF), ++ regmap_reg_range(MCA_TAMPER2_CFG0, MCA_TAMPER2_THRESH_HI_H), ++ regmap_reg_range(MCA_TAMPER3_CFG0, MCA_TAMPER3_THRESH_HI_H), ++ regmap_reg_range(MCA_RTC_CONTROL, MCA_RTC_CONTROL), ++ regmap_reg_range(MCA_RTC_ALARM_YEAR_L, MCA_RTC_ALARM_SEC), ++ regmap_reg_range(MCA_WDT_CONTROL, MCA_WDT_TIMEOUT), ++ regmap_reg_range(MCA_GPIO_NUM, MCA_GPIO_DIR_7), ++ regmap_reg_range(MCA_GPIO_IRQ_CFG_0, MCA_GPIO_IRQ_CFG_63), ++ regmap_reg_range(MCA_REG_ADC_NUM_CH, MCA_REG_ADC_NUM_BYTES), ++ regmap_reg_range(MCA_REG_ADC_CFG0_0, MCA_REG_ADC_CFG0_7), ++ regmap_reg_range(MCA_REG_ADC_CFG1_0, MCA_REG_ADC_CFG1_7), ++ regmap_reg_range(MCA_REG_ADC_CFG2_0, MCA_REG_ADC_CFG2_7), ++ regmap_reg_range(MCA_REG_ADC_SAMPLES_CNT_0, MCA_REG_ADC_SAMPLES_CNT_7), ++ regmap_reg_range(MCA_REG_ADC_THRESH_LO_L_0, MCA_REG_ADC_THRESH_LO_H_7), ++ regmap_reg_range(MCA_REG_ADC_THRESH_HI_L_0, MCA_REG_ADC_THRESH_HI_H_7), ++ regmap_reg_range(MCA_REG_ADC_TICKS_L_0, MCA_REG_ADC_TICKS_H_7), ++ regmap_reg_range(MCA_REG_ADC_IRQ_0, MCA_REG_ADC_IRQ_7), ++ regmap_reg_range(MCA_REG_ADC_CFG_0, MCA_REG_ADC_CFG_2), ++ regmap_reg_range(MCA_REG_ADC_BUFF_CH, MCA_REG_ADC_BUFF_SAMPLE_7), ++ regmap_reg_range(MCA_REG_UART_RHR, MCA_REG_UART_RTSPIN), ++}; ++ ++static const struct regmap_access_table mca_cc6ul_readable_table = { ++ .yes_ranges = mca_cc6ul_readable_ranges, ++ .n_yes_ranges = ARRAY_SIZE(mca_cc6ul_readable_ranges), ++}; ++ ++static const struct regmap_access_table mca_cc6ul_writeable_table = { ++ .yes_ranges = mca_cc6ul_writeable_ranges, ++ .n_yes_ranges = ARRAY_SIZE(mca_cc6ul_writeable_ranges), ++}; ++ ++static const struct regmap_access_table mca_cc6ul_volatile_table = { ++ .yes_ranges = mca_cc6ul_volatile_ranges, ++ .n_yes_ranges = ARRAY_SIZE(mca_cc6ul_volatile_ranges), ++}; ++ ++static struct regmap_config mca_cc6ul_regmap_config = { ++ .reg_bits = 16, ++ .val_bits = 8, ++ .max_register = 0xFFFF, ++ ++ .rd_table = &mca_cc6ul_readable_table, ++ .wr_table = &mca_cc6ul_writeable_table, ++ .volatile_table = &mca_cc6ul_volatile_table, ++ ++ .cache_type = REGCACHE_NONE, ++}; ++ ++static const struct of_device_id mca_cc6ul_dt_ids[] = { ++ { .compatible = "digi,mca_cc6ul_dt_ids", }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, mca_cc6ul_dt_ids); ++ ++static int mca_cc6ul_i2c_probe(struct i2c_client *i2c, ++ const struct i2c_device_id *id) ++{ ++ struct mca_drv *mca; ++ int ret; ++ ++ mca = devm_kzalloc(&i2c->dev, sizeof(struct mca_drv), GFP_KERNEL); ++ if (mca == NULL) ++ return -ENOMEM; ++ ++ i2c_set_clientdata(i2c, mca); ++ mca->dev = &i2c->dev; ++ mca->chip_irq = i2c->irq; ++ ++ mca->regmap = devm_regmap_init_i2c(i2c, &mca_cc6ul_regmap_config); ++ if (IS_ERR(mca->regmap)) { ++ ret = PTR_ERR(mca->regmap); ++ dev_err(mca->dev, "Failed to allocate register map: %d\n", ret); ++ return ret; ++ } ++ ++ return mca_cc6ul_device_init(mca, i2c->irq); ++} ++ ++static int mca_cc6ul_i2c_remove(struct i2c_client *i2c) ++{ ++ struct mca_drv *mca = i2c_get_clientdata(i2c); ++ ++ mca_cc6ul_device_exit(mca); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int mca_cc6ul_i2c_suspend(struct device *dev) ++{ ++ return mca_cc6ul_suspend(dev); ++} ++ ++static int mca_cc6ul_i2c_resume(struct device *dev) ++{ ++ return mca_cc6ul_resume(dev); ++} ++ ++/* ++ * Use suspend_late/resume_early so the mca_cc6ul continues being functional ++ * during the regular suspend/resume callbacks of other drivers, just in case ++ * they use any functionality of the mca. ++ */ ++static const struct dev_pm_ops mca_cc6ul_i2c_pm_ops = { ++ SET_LATE_SYSTEM_SLEEP_PM_OPS(mca_cc6ul_i2c_suspend, mca_cc6ul_i2c_resume) ++}; ++#endif ++ ++static const struct i2c_device_id mca_cc6ul_i2c_id[] = { ++ {"mca_cc6ul", 0}, ++ {}, ++}; ++MODULE_DEVICE_TABLE(i2c, mca_cc6ul_i2c_id); ++ ++static struct i2c_driver mca_cc6ul_i2c_driver = { ++ .driver = { ++ .name = "mca_cc6ul", ++ .of_match_table = of_match_ptr(mca_cc6ul_dt_ids), ++#ifdef CONFIG_PM ++ .pm = &mca_cc6ul_i2c_pm_ops, ++#endif ++ }, ++ .probe = mca_cc6ul_i2c_probe, ++ .remove = mca_cc6ul_i2c_remove, ++ .id_table = mca_cc6ul_i2c_id, ++}; ++ ++module_i2c_driver(mca_cc6ul_i2c_driver); +diff --git a/drivers/mfd/mca-cc6ul-irq.c b/drivers/mfd/mca-cc6ul-irq.c +new file mode 100644 +index 000000000000..0807e974cb60 +--- /dev/null ++++ b/drivers/mfd/mca-cc6ul-irq.c +@@ -0,0 +1,113 @@ ++/* ++ * Copyright 2016 - 2019 Digi International Inc ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define MCA_IRQ_0_OFFSET 0 ++#define MCA_IRQ_1_OFFSET 1 ++#define MCA_IRQ_2_OFFSET 2 ++#define MCA_IRQ_3_OFFSET 3 ++ ++static const struct regmap_irq mca_cc6ul_irqs[] = { ++ /* MCA irqs A register */ ++ [MCA_CC6UL_IRQ_RTC_ALARM] = { ++ .reg_offset = MCA_IRQ_0_OFFSET, ++ .mask = MCA_M_RTC_ALARM, ++ }, ++ [MCA_CC6UL_IRQ_RTC_1HZ] = { ++ .reg_offset = MCA_IRQ_0_OFFSET, ++ .mask = MCA_M_RTC_1HZ, ++ }, ++ [MCA_CC6UL_IRQ_WATCHDOG] = { ++ .reg_offset = MCA_IRQ_0_OFFSET, ++ .mask = MCA_M_WATCHDOG, ++ }, ++ [MCA_CC6UL_IRQ_PWR_SLEEP] = { ++ .reg_offset = MCA_IRQ_0_OFFSET, ++ .mask = MCA_M_PWR_SLEEP, ++ }, ++ [MCA_CC6UL_IRQ_PWR_OFF] = { ++ .reg_offset = MCA_IRQ_0_OFFSET, ++ .mask = MCA_M_PWR_OFF, ++ }, ++ [MCA_CC6UL_IRQ_TAMPER0] = { ++ .reg_offset = MCA_IRQ_0_OFFSET, ++ .mask = MCA_M_TAMPER0, ++ }, ++ [MCA_CC6UL_IRQ_TAMPER1] = { ++ .reg_offset = MCA_IRQ_0_OFFSET, ++ .mask = MCA_M_TAMPER1, ++ }, ++ [MCA_CC6UL_IRQ_ADC] = { ++ .reg_offset = MCA_IRQ_0_OFFSET, ++ .mask = MCA_M_ADC, ++ }, ++ [MCA_CC6UL_IRQ_GPIO_BANK_0] = { ++ .reg_offset = MCA_IRQ_1_OFFSET, ++ .mask = MCA_GPIO_BANK_0, ++ }, ++ [MCA_CC6UL_IRQ_TAMPER2] = { ++ .reg_offset = MCA_IRQ_2_OFFSET, ++ .mask = MCA_M_TAMPER2, ++ }, ++ [MCA_CC6UL_IRQ_TAMPER3] = { ++ .reg_offset = MCA_IRQ_2_OFFSET, ++ .mask = MCA_M_TAMPER3, ++ }, ++ [MCA_CC6UL_IRQ_UART] = { ++ .reg_offset = MCA_IRQ_2_OFFSET, ++ .mask = MCA_M_UART, ++ }, ++}; ++ ++static const struct regmap_irq_chip mca_cc6ul_irq_chip = { ++ .name = "mca-cc6ul-irq", ++ .irqs = mca_cc6ul_irqs, ++ .num_irqs = ARRAY_SIZE(mca_cc6ul_irqs), ++ .num_regs = MCA_NUM_IRQ_REGS, ++ .status_base = MCA_IRQ_STATUS_0, ++ .mask_base = MCA_IRQ_MASK_0, ++ .ack_base = MCA_IRQ_STATUS_0, ++ .init_ack_masked = true, ++}; ++ ++int mca_cc6ul_irq_init(struct mca_drv *mca) ++{ ++ int ret; ++ ++ if (!mca->chip_irq) { ++ dev_err(mca->dev, "No IRQ configured\n"); ++ return -EINVAL; ++ } ++ ++ mca->irq_base = -1; ++ ret = regmap_add_irq_chip(mca->regmap, mca->chip_irq, ++ IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_SHARED, ++ mca->irq_base, &mca_cc6ul_irq_chip, ++ &mca->regmap_irq); ++ if (ret) { ++ dev_err(mca->dev, "Failed to reguest IRQ %d: %d\n", ++ mca->chip_irq, ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++void mca_cc6ul_irq_exit(struct mca_drv *mca) ++{ ++ regmap_del_irq_chip(mca->chip_irq, mca->regmap_irq); ++} +diff --git a/drivers/mfd/mca-cc8x-core.c b/drivers/mfd/mca-cc8x-core.c +new file mode 100644 +index 000000000000..19931618b683 +--- /dev/null ++++ b/drivers/mfd/mca-cc8x-core.c +@@ -0,0 +1,1040 @@ ++/* ++ * Copyright 2018 - 2019 Digi International Inc ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++ ++#define MCA_CC8X_NVRAM_SIZE (MCA_CC8X_MPU_NVRAM_END - MCA_CC8X_MPU_NVRAM_START + 1) ++ ++struct dyn_attribute { ++ u16 since; /* Minimum firmware version required */ ++ struct attribute *attr; ++}; ++ ++struct mca_reason { ++ u32 flag; ++ const char *text; ++}; ++ ++enum lpi2c_imx_mode { ++ STANDARD, /* 100+Kbps */ ++ FAST, /* 400+Kbps */ ++ FAST_PLUS, /* 1.0+Mbps */ ++ HS, /* 3.4+Mbps */ ++ ULTRA_FAST, /* 5.0+Mbps */ ++}; ++ ++struct lpi2c_imx_struct { ++ struct i2c_adapter adapter; ++ int irq; ++ struct clk *clk_per; ++ struct clk *clk_ipg; ++ void __iomem *base; ++ __u8 *rx_buf; ++ __u8 *tx_buf; ++ struct completion complete; ++ unsigned int msglen; ++ unsigned int delivered; ++ unsigned int block_data; ++ unsigned int bitrate; ++ enum lpi2c_imx_mode mode; ++ unsigned int hold_time; ++}; ++ ++static const struct mca_reason last_mca_reset[] = { ++ {MCA_CC8X_LAST_MCA_RST_LLW, "LL Wakeup"}, ++ {MCA_CC8X_LAST_MCA_RST_LVD, "Low Voltage"}, ++ {MCA_CC8X_LAST_MCA_RST_WD, "Watchdog"}, ++ {MCA_CC8X_LAST_MCA_RST_PIN, "Reset Pin"}, ++ {MCA_CC8X_LAST_MCA_RST_PWRON, "Power On"}, ++ {MCA_CC8X_LAST_MCA_RST_LOCKUP, "Core Lockup"}, ++ {MCA_CC8X_LAST_MCA_RST_SW, "Software"}, ++ {MCA_CC8X_LAST_MCA_RST_MDMAPP, "MDM-APP debuger"}, ++ {MCA_CC8X_LAST_MCA_RST_SMAE, "Stop Mode Ack Error"}, ++}; ++ ++static const struct mca_reason last_mpu_reset[] = { ++ {MCA_CC8X_LAST_MPU_RST_PWRON, "Power On"}, ++ {MCA_CC8X_LAST_MPU_RST_SYSR, "System Reset"}, ++ {MCA_CC8X_LAST_MPU_RST_WD, "Watchdog"}, ++ {MCA_CC8X_LAST_MPU_RST_OFFWAKE,"Off wakeup"}, ++ {MCA_CC8X_LAST_MPU_RST_MCARST, "MCA reset"}, ++}; ++ ++static const struct mca_reason last_wakeup[] = { ++ {MCA_CC8X_LAST_WAKEUP_PWRIO, "Power IO"}, ++ {MCA_CC8X_LAST_WAKEUP_TIMER, "Timer"}, ++ {MCA_CC8X_LAST_WAKEUP_RTC, "RTC"}, ++ {MCA_CC8X_LAST_WAKEUP_LPUART, "LP UART"}, ++ {MCA_CC8X_LAST_WAKEUP_TAMPER0, "Tamper0"}, ++ {MCA_CC8X_LAST_WAKEUP_TAMPER1, "Tamper1"}, ++ {MCA_CC8X_LAST_WAKEUP_TAMPER2, "Tamper2"}, ++ {MCA_CC8X_LAST_WAKEUP_TAMPER3, "Tamper3"}, ++ {MCA_CC8X_LAST_WAKEUP_IO0, "IO0"}, ++ {MCA_CC8X_LAST_WAKEUP_IO1, "IO1"}, ++ {MCA_CC8X_LAST_WAKEUP_IO2, "IO2"}, ++ {MCA_CC8X_LAST_WAKEUP_IO3, "IO3"}, ++ {MCA_CC8X_LAST_WAKEUP_IO4, "IO4"}, ++ {MCA_CC8X_LAST_WAKEUP_IO5, "IO5"}, ++ {MCA_CC8X_LAST_WAKEUP_IO6, "IO6"}, ++ {MCA_CC8X_LAST_WAKEUP_IO7, "IO7"}, ++ {MCA_CC8X_LAST_WAKEUP_IO8, "IO8"}, ++ {MCA_CC8X_LAST_WAKEUP_IO9, "IO9"}, ++ {MCA_CC8X_LAST_WAKEUP_IO10, "IO10"}, ++ {MCA_CC8X_LAST_WAKEUP_IO11, "IO11"}, ++ {MCA_CC8X_LAST_WAKEUP_IO12, "IO12"}, ++ {MCA_CC8X_LAST_WAKEUP_IO13, "IO13"}, ++ {MCA_CC8X_LAST_WAKEUP_IO14, "IO14"}, ++ {MCA_CC8X_LAST_WAKEUP_IO15, "IO15"}, ++ {MCA_CC8X_LAST_WAKEUP_IO16, "IO16"}, ++ {MCA_CC8X_LAST_WAKEUP_IO17, "IO17"}, ++ {MCA_CC8X_LAST_WAKEUP_IO18, "IO18"}, ++ {MCA_CC8X_LAST_WAKEUP_IO19, "IO19"}, ++ {MCA_CC8X_LAST_WAKEUP_IO20, "IO20"}, ++ {MCA_CC8X_LAST_WAKEUP_IO21, "IO21"}, ++ {MCA_CC8X_LAST_WAKEUP_VCC, "Vcc"}, ++ {MCA_CC8X_LAST_WAKEUP_CPU, "CPU"}, ++}; ++ ++static struct mca_drv *pmca; ++ ++static const char _enabled[] = "enabled"; ++static const char _disabled[] = "disabled"; ++ ++static struct resource mca_cc8x_rtc_resources[] = { ++ { ++ .name = MCA_IRQ_RTC_ALARM_NAME, ++ .start = MCA_CC8X_IRQ_RTC_ALARM, ++ .end = MCA_CC8X_IRQ_RTC_ALARM, ++ .flags = IORESOURCE_IRQ, ++ }, ++ { ++ .name = MCA_IRQ_RTC_1HZ_NAME, ++ .start = MCA_CC8X_IRQ_RTC_1HZ, ++ .end = MCA_CC8X_IRQ_RTC_1HZ, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static struct resource mca_cc8x_watchdog_resources[] = { ++ { ++ .name = MCA_IRQ_WATCHDOG_NAME, ++ .start = MCA_CC8X_IRQ_WATCHDOG, ++ .end = MCA_CC8X_IRQ_WATCHDOG, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static struct resource mca_cc8x_pwrkey_resources[] = { ++ { ++ .name = MCA_IRQ_PWR_SLEEP_NAME, ++ .start = MCA_CC8X_IRQ_PWR_SLEEP, ++ .end = MCA_CC8X_IRQ_PWR_SLEEP, ++ .flags = IORESOURCE_IRQ, ++ }, ++ { ++ .name = MCA_IRQ_PWR_OFF_NAME, ++ .start = MCA_CC8X_IRQ_PWR_OFF, ++ .end = MCA_CC8X_IRQ_PWR_OFF, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static struct resource mca_cc8x_adc_resources[] = { ++ { ++ .name = MCA_IRQ_ADC_NAME, ++ .start = MCA_CC8X_IRQ_ADC, ++ .end = MCA_CC8X_IRQ_ADC, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static struct resource mca_cc8x_tamper_resources[] = { ++ { ++ .name = MCA_IRQ_TAMPER0_NAME, ++ .start = MCA_CC8X_IRQ_TAMPER0, ++ .end = MCA_CC8X_IRQ_TAMPER0, ++ .flags = IORESOURCE_IRQ, ++ }, ++ { ++ .name = MCA_IRQ_TAMPER1_NAME, ++ .start = MCA_CC8X_IRQ_TAMPER1, ++ .end = MCA_CC8X_IRQ_TAMPER1, ++ .flags = IORESOURCE_IRQ, ++ }, ++ { ++ .name = MCA_IRQ_TAMPER2_NAME, ++ .start = MCA_CC8X_IRQ_TAMPER2, ++ .end = MCA_CC8X_IRQ_TAMPER2, ++ .flags = IORESOURCE_IRQ, ++ }, ++ { ++ .name = MCA_IRQ_TAMPER3_NAME, ++ .start = MCA_CC8X_IRQ_TAMPER3, ++ .end = MCA_CC8X_IRQ_TAMPER3, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static struct resource mca_cc8x_gpios_resources[] = { ++ { ++ .name = MCA_IRQ_GPIO_BANK_0_NAME, ++ .start = MCA_CC8X_IRQ_GPIO_BANK_0, ++ .end = MCA_CC8X_IRQ_GPIO_BANK_0, ++ .flags = IORESOURCE_IRQ, ++ }, ++ { ++ .name = MCA_IRQ_GPIO_BANK_1_NAME, ++ .start = MCA_CC8X_IRQ_GPIO_BANK_1, ++ .end = MCA_CC8X_IRQ_GPIO_BANK_1, ++ .flags = IORESOURCE_IRQ, ++ }, ++ { ++ .name = MCA_IRQ_GPIO_BANK_2_NAME, ++ .start = MCA_CC8X_IRQ_GPIO_BANK_2, ++ .end = MCA_CC8X_IRQ_GPIO_BANK_2, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static struct resource mca_cc8x_uart_resources[] = { ++ { ++ .name = MCA_IRQ_UART_NAME, ++ .start = MCA_CC8X_IRQ_UART, ++ .end = MCA_CC8X_IRQ_UART, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static const struct mfd_cell mca_cc8x_devs[] = { ++ { ++ .name = MCA_CC8X_DRVNAME_RTC, ++ .num_resources = ARRAY_SIZE(mca_cc8x_rtc_resources), ++ .resources = mca_cc8x_rtc_resources, ++ .of_compatible = "digi,mca-cc8x-rtc", ++ }, ++ { ++ .name = MCA_CC8X_DRVNAME_WATCHDOG, ++ .num_resources = ARRAY_SIZE(mca_cc8x_watchdog_resources), ++ .resources = mca_cc8x_watchdog_resources, ++ .of_compatible = "digi,mca-cc8x-watchdog", ++ }, ++ { ++ .name = MCA_CC8X_DRVNAME_GPIO, ++ .num_resources = ARRAY_SIZE(mca_cc8x_gpios_resources), ++ .resources = mca_cc8x_gpios_resources, ++ .of_compatible = "digi,mca-cc8x-gpio", ++ }, ++ { ++ .name = MCA_CC8X_DRVNAME_PWRKEY, ++ .num_resources = ARRAY_SIZE(mca_cc8x_pwrkey_resources), ++ .resources = mca_cc8x_pwrkey_resources, ++ .of_compatible = "digi,mca-cc8x-pwrkey", ++ }, ++ { ++ .name = MCA_CC8X_DRVNAME_ADC, ++ .of_compatible = "digi,mca-cc8x-adc", ++ .num_resources = ARRAY_SIZE(mca_cc8x_adc_resources), ++ .resources = mca_cc8x_adc_resources, ++ }, ++ { ++ .name = MCA_CC8X_DRVNAME_TAMPER, ++ .num_resources = ARRAY_SIZE(mca_cc8x_tamper_resources), ++ .resources = mca_cc8x_tamper_resources, ++ .of_compatible = "digi,mca-cc8x-tamper", ++ }, ++ { ++ .name = MCA_CC8X_DRVNAME_UART, ++ .num_resources = ARRAY_SIZE(mca_cc8x_uart_resources), ++ .resources = mca_cc8x_uart_resources, ++ .of_compatible = "digi,mca-cc8x-uart", ++ }, ++ { ++ .name = MCA_CC8X_DRVNAME_PWM, ++ .of_compatible = "digi,mca-pwm", ++ }, ++}; ++ ++/* Read a block of registers */ ++int mca_cc8x_read_block(struct mca_drv *mca, u16 addr, u8 *data, ++ size_t nregs) ++{ ++ int ret; ++ ++ /* TODO, check limits nregs... */ ++ ++ ret = regmap_raw_read(mca->regmap, addr, data, nregs); ++ if (ret != 0) ++ return ret; ++ ++ return ret; ++ ++} ++EXPORT_SYMBOL_GPL(mca_cc8x_read_block); ++ ++/* Write a block of data into MCA registers */ ++int mca_cc8x_write_block(struct mca_drv *mca , u16 addr, u8 *data, ++ size_t nregs) ++{ ++ u8 *frame; /* register address + payload */ ++ u8 *payload; ++ int ret; ++ ++ /* TODO, check limits nregs... */ ++ ++ frame = kzalloc(sizeof(addr) + nregs, GFP_KERNEL | GFP_DMA); ++ if (!frame) ++ return -ENOMEM; ++ ++ payload = frame + sizeof(addr); ++ memcpy(payload, data, nregs); ++ ++ /* Write payload */ ++ ret = regmap_raw_write(mca->regmap, addr, payload, nregs); ++ ++ kfree(frame); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(mca_cc8x_write_block); ++ ++static int mca_cc8x_unlock_ctrl(struct mca_drv *mca) ++{ ++ int ret; ++ const uint8_t unlock_pattern[] = {'C', 'T', 'R', 'U'}; ++ ++ ret = regmap_bulk_write(mca->regmap, MCA_CTRL_UNLOCK_0, ++ unlock_pattern, sizeof(unlock_pattern)); ++ if (ret) ++ dev_warn(mca->dev, "failed to unlock CTRL registers (%d)\n", ++ ret); ++ ++ return ret; ++} ++ ++static int mca_cc8x_get_tick_cnt(struct mca_drv *mca, u32 *tick) ++{ ++ return regmap_bulk_read(mca->regmap, MCA_TIMER_TICK_0, ++ tick, sizeof(*tick)); ++} ++ ++/* sysfs attributes */ ++static ssize_t ext_32khz_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct mca_drv *mca = dev_get_drvdata(dev); ++ unsigned int val; ++ int ret; ++ ++ ret = regmap_read(mca->regmap, MCA_CTRL_0, &val); ++ if (ret) { ++ dev_err(mca->dev, "Cannot read MCA CTRL_0 register(%d)\n", ++ ret); ++ return 0; ++ } ++ ++ return sprintf(buf, "%s\n", val & MCA_EXT32K_EN ? ++ _enabled : _disabled); ++} ++ ++static ssize_t ext_32khz_store(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct mca_drv *mca = dev_get_drvdata(dev); ++ bool enable; ++ int ret; ++ ++ if (!strncmp(buf, _enabled, sizeof(_enabled) - 1)) ++ enable = true; ++ else if (!strncmp(buf, _disabled, sizeof(_disabled) - 1)) ++ enable = false; ++ else ++ return -EINVAL; ++ ++ ret = mca_cc8x_unlock_ctrl(mca); ++ if (ret) ++ return ret; ++ ++ ret = regmap_update_bits(mca->regmap, MCA_CTRL_0, ++ MCA_EXT32K_EN, ++ enable ? MCA_EXT32K_EN : 0); ++ if (ret) { ++ dev_err(mca->dev, "Cannot update MCA CTRL_0 register (%d)\n", ret); ++ return ret; ++ } ++ ++ return count; ++} ++static DEVICE_ATTR(ext_32khz, 0600, ext_32khz_show, ext_32khz_store); ++ ++static ssize_t vref_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct mca_drv *mca = dev_get_drvdata(dev); ++ unsigned int val; ++ int ret; ++ ++ ret = regmap_read(mca->regmap, MCA_CTRL_0, &val); ++ if (ret) { ++ dev_err(mca->dev, "Cannot read MCA CTRL_0 register(%d)\n", ++ ret); ++ return 0; ++ } ++ ++ return sprintf(buf, "%s\n", val & MCA_VREF_EN ? ++ _enabled : _disabled); ++} ++ ++static ssize_t vref_store(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct mca_drv *mca = dev_get_drvdata(dev); ++ bool enable; ++ int ret; ++ ++ if (!strncmp(buf, _enabled, sizeof(_enabled) - 1)) ++ enable = true; ++ else if (!strncmp(buf, _disabled, sizeof(_disabled) - 1)) ++ enable = false; ++ else ++ return -EINVAL; ++ ++ ret = mca_cc8x_unlock_ctrl(mca); ++ if (ret) ++ return ret; ++ ++ ret = regmap_update_bits(mca->regmap, MCA_CTRL_0, ++ MCA_VREF_EN, ++ enable ? MCA_VREF_EN : 0); ++ if (ret) { ++ dev_err(mca->dev, "Cannot update MCA CTRL_0 register (%d)\n", ret); ++ return ret; ++ } ++ ++ return count; ++} ++static DEVICE_ATTR(vref, 0600, vref_show, vref_store); ++ ++static ssize_t hwver_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct mca_drv *mca = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%d\n", mca->hw_version); ++} ++static DEVICE_ATTR(hw_version, S_IRUGO, hwver_show, NULL); ++ ++static ssize_t fwver_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct mca_drv *mca = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%d.%02d %s\n", MCA_FW_VER_MAJOR(mca->fw_version), ++ MCA_FW_VER_MINOR(mca->fw_version), ++ mca->fw_is_alpha ? "(alpha)" : ""); ++} ++static DEVICE_ATTR(fw_version, S_IRUGO, fwver_show, NULL); ++ ++static ssize_t tick_cnt_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct mca_drv *mca = dev_get_drvdata(dev); ++ u32 tick_cnt; ++ int ret; ++ ++ ret = mca_cc8x_get_tick_cnt(mca, &tick_cnt); ++ if (ret) { ++ dev_err(mca->dev, "Cannot read MCA tick counter(%d)\n", ret); ++ return ret; ++ } ++ ++ return sprintf(buf, "%u\n", tick_cnt); ++} ++static DEVICE_ATTR(tick_cnt, S_IRUGO, tick_cnt_show, NULL); ++ ++static ssize_t fw_update_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct mca_drv *mca = dev_get_drvdata(dev); ++ ++ if (!gpio_is_valid(mca->fw_update_gpio)) ++ return -EINVAL; ++ ++ return sprintf(buf, "%d\n", ++ gpio_get_value_cansleep(mca->fw_update_gpio)); ++} ++ ++static ssize_t fw_update_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct mca_drv *mca = dev_get_drvdata(dev); ++ ssize_t status; ++ long value; ++ struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(mca->i2c_adapter_dev); ++ ++ if (!gpio_is_valid(mca->fw_update_gpio)) ++ return -EINVAL; ++ ++ /* Set i2c bus speed to 100kbps during firmware update */ ++ lpi2c_imx->bitrate = 100000; ++ ++ status = kstrtol(buf, 0, &value); ++ if (status == 0) { ++ gpio_set_value_cansleep(mca->fw_update_gpio, value); ++ status = count; ++ } ++ ++ return status; ++} ++static DEVICE_ATTR(fw_update, 0600, fw_update_show, fw_update_store); ++ ++static ssize_t last_wakeup_reason_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct mca_drv *mca = dev_get_drvdata(dev); ++ bool comma = false; ++ u32 last_wakeup_val; ++ int ret, i; ++ ++ ret = regmap_bulk_read(mca->regmap, MCA_LAST_WAKEUP_REASON_0, ++ &last_wakeup_val, sizeof(last_wakeup_val)); ++ if (ret) { ++ dev_err(mca->dev, ++ "Cannot read last MCA wakeup reason (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ buf[0] = 0; ++ ++ for (i = 0; i < ARRAY_SIZE(last_wakeup); i++) { ++ if (last_wakeup[i].flag & last_wakeup_val) { ++ if (comma) ++ strcat(buf, ", "); ++ strcat(buf, last_wakeup[i].text); ++ comma = true; ++ } ++ } ++ ++ if (comma) ++ strcat(buf, "\n"); ++ ++ return strlen(buf); ++} ++static DEVICE_ATTR(last_wakeup_reason, S_IRUGO, last_wakeup_reason_show, NULL); ++ ++static ssize_t last_mca_reset_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct mca_drv *mca = dev_get_drvdata(dev); ++ bool comma = false; ++ int i; ++ ++ buf[0] = 0; ++ ++ for (i = 0; i < ARRAY_SIZE(last_mca_reset); i++) { ++ if (last_mca_reset[i].flag & mca->last_mca_reset) { ++ if (comma) ++ strcat(buf, ", "); ++ strcat(buf, last_mca_reset[i].text); ++ comma = true; ++ } ++ } ++ ++ if (comma) ++ strcat(buf, "\n"); ++ ++ return strlen(buf); ++} ++static DEVICE_ATTR(last_mca_reset, S_IRUGO, last_mca_reset_show, NULL); ++ ++static ssize_t last_mpu_reset_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct mca_drv *mca = dev_get_drvdata(dev); ++ bool comma = false; ++ int i; ++ ++ buf[0] = 0; ++ ++ for (i = 0; i < ARRAY_SIZE(last_mpu_reset); i++) { ++ if (last_mpu_reset[i].flag & mca->last_mpu_reset) { ++ if (comma) ++ strcat(buf, ", "); ++ strcat(buf, last_mpu_reset[i].text); ++ comma = true; ++ } ++ } ++ ++ if (comma) ++ strcat(buf, "\n"); ++ ++ return strlen(buf); ++} ++static DEVICE_ATTR(last_mpu_reset, S_IRUGO, last_mpu_reset_show, NULL); ++ ++static ssize_t nvram_read(struct file *filp, struct kobject *kobj, ++ struct bin_attribute *attr, char *buf, loff_t off, ++ size_t count) ++{ ++ struct device *dev = kobj_to_dev(kobj); ++ struct mca_drv *mca; ++ int ret; ++ ++ if (!dev || (mca = dev_get_drvdata(dev)) == NULL) ++ return -ENODEV; ++ ++ if (unlikely(off >= MCA_CC8X_NVRAM_SIZE) || unlikely(!count)) ++ return 0; ++ if ((off + count) > MCA_CC8X_NVRAM_SIZE) ++ count = MCA_CC8X_NVRAM_SIZE - off; ++ ++ ret = regmap_bulk_read(mca->regmap, ++ MCA_CC8X_MPU_NVRAM_START + off, buf, count); ++ if (ret) { ++ dev_err(mca->dev, "%s error (%d)\n", __func__, ret); ++ return ret; ++ } ++ ++ return count; ++} ++ ++static ssize_t nvram_write(struct file *filp, struct kobject *kobj, ++ struct bin_attribute *attr, char *buf, loff_t off, ++ size_t count) ++{ ++ struct device *dev = kobj_to_dev(kobj); ++ struct mca_drv *mca; ++ int ret; ++ ++ if (!dev || (mca = dev_get_drvdata(dev)) == NULL) ++ return -ENODEV; ++ ++ if (unlikely(off >= MCA_CC8X_NVRAM_SIZE)) ++ return -EFBIG; ++ if ((off + count) > MCA_CC8X_NVRAM_SIZE) ++ count = MCA_CC8X_NVRAM_SIZE - off; ++ if (unlikely(!count)) ++ return count; ++ ++ ret = regmap_bulk_write(mca->regmap, ++ MCA_CC8X_MPU_NVRAM_START + off, buf, count); ++ if (ret) { ++ dev_err(mca->dev, "%s error (%d)\n", __func__, ret); ++ return ret; ++ } ++ ++ return count; ++} ++ ++static struct attribute *mca_cc8x_sysfs_entries[] = { ++ &dev_attr_ext_32khz.attr, ++ &dev_attr_hw_version.attr, ++ &dev_attr_fw_version.attr, ++ &dev_attr_fw_update.attr, ++ NULL, ++}; ++ ++static struct attribute_group mca_cc8x_attr_group = { ++ .name = NULL, /* put in device directory */ ++ .attrs = mca_cc8x_sysfs_entries, ++}; ++ ++static struct dyn_attribute mca_cc8x_sysfs_dyn_entries[] = { ++ { ++ .since = MCA_MAKE_FW_VER(0,0), ++ .attr = &dev_attr_tick_cnt.attr, ++ }, ++ { ++ .since = MCA_MAKE_FW_VER(0,11), ++ .attr = &dev_attr_vref.attr, ++ }, ++ { ++ .since = MCA_MAKE_FW_VER(0,4), ++ .attr = &dev_attr_last_wakeup_reason.attr, ++ }, ++ { ++ .since = MCA_MAKE_FW_VER(0,4), ++ .attr = &dev_attr_last_mca_reset.attr, ++ }, ++ { ++ .since = MCA_MAKE_FW_VER(0,4), ++ .attr = &dev_attr_last_mpu_reset.attr, ++ }, ++}; ++ ++int mca_cc8x_suspend(struct device *dev) ++{ ++ struct mca_drv *mca = dev_get_drvdata(dev); ++ ++ if (!mca) { ++ dev_err(dev, " mca was null in %s\n", __func__); ++ return -ENODEV; ++ } ++ ++ /* Set the suspend bit in PWR_CTRL_0 */ ++ return regmap_update_bits(pmca->regmap, MCA_PWR_CTRL_0, ++ MCA_PWR_GO_SUSPEND, ++ MCA_PWR_GO_SUSPEND); ++} ++ ++#define MCA_MAX_RESUME_RD_RETRIES 10 ++int mca_cc8x_resume(struct device *dev) ++{ ++ struct mca_drv *mca = dev_get_drvdata(dev); ++ unsigned int val; ++ int ret, retries = 0; ++ ++ if (!mca) { ++ dev_err(dev, " mca was null in %s\n", __func__); ++ return -ENODEV; ++ } ++ ++ /* ++ * Generate traffic on the i2c bus to wakeup the MCA, in case it was in ++ * low power ++ */ ++ do { ++ ret = regmap_read(mca->regmap, MCA_DEVICE_ID, &val); ++ if (!ret && mca->dev_id == (u8)val) ++ break; ++ udelay(50); ++ } while (++retries < MCA_MAX_RESUME_RD_RETRIES); ++ ++ if (retries == MCA_MAX_RESUME_RD_RETRIES) { ++ dev_err(mca->dev, "unable to wake up MCA (%d)\n", ret); ++ return ret; ++ } ++ ++ /* Reset the suspend bit in PWR_CTRL_0 */ ++ return regmap_update_bits(pmca->regmap, MCA_PWR_CTRL_0, ++ MCA_PWR_GO_SUSPEND, ++ 0); ++} ++ ++#define MCA_MAX_PWROFF_TRIES 5 ++static void mca_cc8x_power_off(void) ++{ ++ int try = 0; ++ int ret; ++ ++ if (!pmca) { ++ printk(KERN_ERR "ERROR: unable to power off [%s:%d/%s()]!\n", ++ __FILE__, __LINE__, __func__); ++ return; ++ } ++ ++ do { ++ /* Set power off bit in PWR_CTRL_0 register to shutdown */ ++ ret = regmap_update_bits(pmca->regmap, MCA_PWR_CTRL_0, ++ MCA_PWR_GO_OFF, ++ MCA_PWR_GO_OFF); ++ if (ret) ++ printk(KERN_ERR "ERROR: accesing PWR_CTRL_0 register " ++ "[%s:%d/%s()]!\n", __FILE__, __LINE__, __func__); ++ ++ /* ++ * Even if the regmap update returned with success, retry... ++ * we are powering off, so there is nothing bad by doing it. ++ */ ++ mdelay(50); ++ } while (++try < MCA_MAX_PWROFF_TRIES); ++ ++ /* Print a warning and return, so at least userland can log the issue */ ++ printk(KERN_ERR "ERROR: unable to power off [%s:%d/%s()]!\n", ++ __FILE__, __LINE__, __func__); ++} ++ ++#define MCA_MAX_RESET_TRIES 5 ++static int mca_cc8x_restart_handler(struct notifier_block *nb, ++ unsigned long mode, void *cmd) ++{ ++ int ret; ++ int try = 0; ++ struct mca_drv *mca = container_of(nb, struct mca_drv, ++ restart_handler); ++ const uint8_t unlock_pattern[] = {'C', 'T', 'R', 'U'}; ++ ++ do { ++ ret = regmap_bulk_write(mca->regmap, MCA_CTRL_UNLOCK_0, ++ unlock_pattern, sizeof(unlock_pattern)); ++ if (ret) { ++ dev_err(mca->dev, "failed to unlock ctrl regs (%d)\n", ++ ret); ++ goto reset_retry; ++ } ++ ++ ret = regmap_write(pmca->regmap, MCA_CTRL_0, ++ MCA_RESET); ++ if (ret) ++ dev_err(mca->dev, "failed to reset (%d)\n", ret); ++ ++ /* ++ * The MCA will reset the cpu, so the retry should not happen... ++ * and if it happens, something went wrong, and retrying is the ++ * right thing to do. ++ */ ++reset_retry: ++ mdelay(10); ++ } while (++try < MCA_MAX_RESET_TRIES); ++ ++ dev_err(mca->dev, "failed to reboot!\n"); ++ ++ return NOTIFY_DONE; ++} ++ ++static int mca_cc8x_add_dyn_sysfs_entries(struct mca_drv *mca, ++ const struct dyn_attribute *dattr, ++ int num_entries, ++ const struct attribute_group *grp) ++{ ++ int ret, i; ++ ++ if (!mca || !dattr || !grp) ++ return -EINVAL; ++ ++ for (i = 0; i < num_entries; i++, dattr++) { ++ if (!dattr->attr) ++ continue; ++ ++ /* Create the sysfs files if the MCA fw supports the feature*/ ++ if (mca->fw_version >= dattr->since) { ++ ret = sysfs_add_file_to_group(&mca->dev->kobj, ++ dattr->attr, ++ grp->name); ++ if (ret) ++ dev_warn(mca->dev, ++ "Cannot create sysfs file %s (%d)\n", ++ dattr->attr->name, ret); ++ } ++ } ++ ++ return 0; ++} ++ ++int mca_cc8x_device_init(struct mca_drv *mca, u32 irq) ++{ ++ int ret; ++ unsigned int val; ++ ++ ret = regmap_read(mca->regmap, MCA_DEVICE_ID, &val); ++ if (ret != 0) { ++ dev_err(mca->dev, "Cannot read MCA Device ID (%d)\n", ret); ++ return ret; ++ } ++ mca->dev_id = (u8)val; ++ ++ if (mca->dev_id != MCA_CC8X_DEVICE_ID_VAL) { ++ dev_err(mca->dev, "Invalid MCA Device ID (%x)\n", mca->dev_id); ++ return -ENODEV; ++ } ++ ++ ret = regmap_read(mca->regmap, MCA_HW_VER, &val); ++ if (ret != 0) { ++ dev_err(mca->dev, "Cannot read MCA Hardware Version (%d)\n", ++ ret); ++ return ret; ++ } ++ mca->hw_version = (u8)val; ++ ++ ret = regmap_bulk_read(mca->regmap, MCA_FW_VER_L, &val, 2); ++ if (ret != 0) { ++ dev_err(mca->dev, "Cannot read MCA Firmware Version (%d)\n", ++ ret); ++ return ret; ++ } ++ mca->fw_version = (u16)(val & ~MCA_FW_VER_ALPHA_MASK); ++ mca->fw_is_alpha = val & MCA_FW_VER_ALPHA_MASK ? true : false; ++ ++ if (mca->fw_version >= MCA_MAKE_FW_VER(0, 4)) { ++ ret = regmap_bulk_read(mca->regmap, MCA_LAST_MCA_RESET_0, ++ &mca->last_mca_reset, ++ sizeof(mca->last_mca_reset)); ++ if (ret) { ++ dev_err(mca->dev, ++ "Cannot read MCA last reset (%d)\n", ret); ++ return ret; ++ } ++ ++ ret = regmap_bulk_read(mca->regmap, MCA_LAST_MPU_RESET_0, ++ &mca->last_mpu_reset, ++ sizeof(mca->last_mpu_reset)); ++ if (ret) { ++ dev_err(mca->dev, ++ "Cannot read MPU last reset (%d)\n", ret); ++ return ret; ++ } ++ } ++ ++ /* ++ * Read the SOM hardware version the MCA is using. For CC8X module it ++ * is set by uboot ++ */ ++ ret = regmap_read(mca->regmap, MCA_HWVER_SOM, &mca->som_hv); ++ if (ret != 0) ++ dev_warn(mca->dev, ++ "Cannot read SOM hardware version (%d)\n", ret); ++ ++ mca->fw_update_gpio = of_get_named_gpio(mca->dev->of_node, ++ "fw-update-gpio", 0); ++ if (!gpio_is_valid(mca->fw_update_gpio) || ++ devm_gpio_request_one(mca->dev, mca->fw_update_gpio, ++ GPIOF_OUT_INIT_LOW, "mca-fw-update")) { ++ dev_warn(mca->dev, "failed to get fw-update-gpio: %d\n", ret); ++ mca->fw_update_gpio = -EINVAL; ++ } ++ ++ mca->chip_irq = irq; ++ mca->gpio_base = -1; ++ ++ ret = mca_cc8x_irq_init(mca); ++ if (ret != 0) { ++ dev_err(mca->dev, "Cannot initialize interrupts (%d)\n", ret); ++ return ret; ++ } ++ ++ ret = mfd_add_devices(mca->dev, -1, mca_cc8x_devs, ++ ARRAY_SIZE(mca_cc8x_devs), NULL, mca->irq_base, ++ regmap_irq_get_domain(mca->regmap_irq)); ++ if (ret) { ++ dev_err(mca->dev, "Cannot add MFD cells (%d)\n", ret); ++ goto out_irq; ++ } ++ ++ ret = sysfs_create_group(&mca->dev->kobj, &mca_cc8x_attr_group); ++ if (ret) { ++ dev_err(mca->dev, "Cannot create sysfs entries (%d)\n", ret); ++ goto out_dev; ++ } ++ if (mca->fw_update_gpio == -EINVAL) { ++ /* Remove fw_update entry */ ++ sysfs_remove_file(&mca->dev->kobj, &dev_attr_fw_update.attr); ++ } ++ ++ ret = mca_cc8x_add_dyn_sysfs_entries(mca, mca_cc8x_sysfs_dyn_entries, ++ ARRAY_SIZE(mca_cc8x_sysfs_dyn_entries), ++ &mca_cc8x_attr_group); ++ if (ret) { ++ dev_err(mca->dev, "Cannot create sysfs dynamic entries (%d)\n", ++ ret); ++ goto out_sysfs_remove; ++ } ++ ++ pmca = mca; ++ ++ if (pm_power_off != NULL) { ++ dev_warn(mca->dev, "pm_power_off function already registered. " ++ "Will be override by MCA function.\n"); ++ } ++ pm_power_off = mca_cc8x_power_off; ++ ++ /* ++ * To avoid error messages when resuming from suspend, increase the I2C ++ * bus' usage counter so the linux pm_runtime framework wakes it from ++ * suspend before trying to read the MCA's IRQ status. This indicates that ++ * the bus is in use when the system is going to suspend, making linux wake ++ * it up as soon as possible so any operations that were halted continue ++ * without issues after resuming. ++ * ++ * The device hierarchy is the following: ++ * ++ * mca_cc8x 0-0063 -> i2c i2c-0 -> imx-lpi2c 5a800000.i2c ++ */ ++ pm_runtime_get_noresume(mca->dev->parent->parent); ++ ++ /* ++ * Register the MCA restart handler with high priority to ensure it is ++ * called first ++ */ ++ mca->restart_handler.notifier_call = mca_cc8x_restart_handler; ++ mca->restart_handler.priority = 200; ++ ret = register_restart_handler(&mca->restart_handler); ++ if (ret) { ++ dev_err(mca->dev, ++ "failed to register restart handler (%d)\n", ret); ++ goto out_pwr_off; ++ } ++ ++ if (mca->fw_version >= MCA_MAKE_FW_VER(0, 8)) { ++ mca->nvram = devm_kzalloc(mca->dev, sizeof(struct bin_attribute), ++ GFP_KERNEL); ++ if (!mca->nvram) { ++ dev_err(mca->dev, "Cannot allocate memory for nvram\n"); ++ goto out_pwr_off; ++ } ++ ++ sysfs_bin_attr_init(mca->nvram); ++ ++ mca->nvram->attr.name = "nvram"; ++ mca->nvram->attr.mode = S_IRUGO | S_IWUSR; ++ ++ mca->nvram->read = nvram_read; ++ mca->nvram->write = nvram_write; ++ ++ ret = sysfs_create_bin_file(&mca->dev->kobj, mca->nvram); ++ if (ret) { ++ dev_err(mca->dev, "Cannot create sysfs file: %s\n", ++ mca->nvram->attr.name); ++ goto out_nvram; ++ } ++ } ++ ++ return 0; ++ ++out_nvram: ++ kfree(mca->nvram); ++out_pwr_off: ++ pm_power_off = NULL; ++out_sysfs_remove: ++ pmca = NULL; ++ sysfs_remove_group(&mca->dev->kobj, &mca_cc8x_attr_group); ++out_dev: ++ mfd_remove_devices(mca->dev); ++out_irq: ++ mca_cc8x_irq_exit(mca); ++ ++ return ret; ++} ++ ++void mca_cc8x_device_exit(struct mca_drv *mca) ++{ ++ unregister_restart_handler(&mca->restart_handler); ++ pm_power_off = NULL; ++ pmca = NULL; ++ pm_runtime_put_noidle(mca->dev->parent->parent); ++ sysfs_remove_group(&mca->dev->kobj, &mca_cc8x_attr_group); ++ mfd_remove_devices(mca->dev); ++ mca_cc8x_irq_exit(mca); ++ kfree(mca->nvram); ++} ++ ++MODULE_AUTHOR("Digi International Inc"); ++MODULE_DESCRIPTION("MCA driver for ConnectCore 8X"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/mfd/mca-cc8x-i2c.c b/drivers/mfd/mca-cc8x-i2c.c +new file mode 100644 +index 000000000000..43e1b9e9d9d8 +--- /dev/null ++++ b/drivers/mfd/mca-cc8x-i2c.c +@@ -0,0 +1,222 @@ ++/* ++ * Copyright 2018 - 2019 Digi International Inc ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++ ++static const struct regmap_range mca_cc8x_readable_ranges[] = { ++}; ++ ++static const struct regmap_range mca_cc8x_writeable_ranges[] = { ++ regmap_reg_range(MCA_HWVER_SOM, MCA_HWVER_SOM), ++ regmap_reg_range(MCA_IRQ_STATUS_0, MCA_IRQ_MASK_3), ++ regmap_reg_range(MCA_PWR_CTRL_0, MCA_PWR_KEY_GUARD), ++ regmap_reg_range(MCA_CTRL_UNLOCK_0, MCA_CTRL_UNLOCK_3), ++ regmap_reg_range(MCA_CTRL_0, MCA_CTRL_0), ++ regmap_reg_range(MCA_TAMPER0_CFG0, MCA_TAMPER0_EVENT), ++ regmap_reg_range(MCA_TAMPER1_CFG0, MCA_TAMPER1_EVENT), ++ regmap_reg_range(MCA_TAMPER2_CFG0, MCA_TAMPER2_THRESH_HI_H), ++ regmap_reg_range(MCA_TAMPER3_CFG0, MCA_TAMPER3_THRESH_HI_H), ++ regmap_reg_range(MCA_RTC_CONTROL, MCA_RTC_CONTROL), ++ regmap_reg_range(MCA_RTC_COUNT_YEAR_L, MCA_RTC_ALARM_SEC), ++ regmap_reg_range(MCA_WDT_CONTROL, MCA_WDT_REFRESH_3), ++ regmap_reg_range(MCA_GPIO_DIR_0, MCA_GPIO_DEB_CNT_63), ++ regmap_reg_range(MCA_REG_ADC_CFG0_0, MCA_REG_ADC_CFG0_21), ++ regmap_reg_range(MCA_REG_ADC_CFG1_0, MCA_REG_ADC_CFG1_21), ++ regmap_reg_range(MCA_REG_ADC_CFG2_0, MCA_REG_ADC_CFG2_21), ++ regmap_reg_range(MCA_REG_ADC_SAMPLES_CNT_0, MCA_REG_ADC_SAMPLES_CNT_21), ++ regmap_reg_range(MCA_REG_ADC_THRESH_LO_L_0, MCA_REG_ADC_THRESH_LO_H_21), ++ regmap_reg_range(MCA_REG_ADC_THRESH_HI_L_0, MCA_REG_ADC_THRESH_HI_H_21), ++ regmap_reg_range(MCA_REG_ADC_TICKS_L_0, MCA_REG_ADC_TICKS_H_21), ++ regmap_reg_range(MCA_REG_ADC_IRQ_0, MCA_REG_ADC_IRQ_7), ++ regmap_reg_range(MCA_REG_ADC_CFG_0, MCA_REG_ADC_CFG_2), ++ regmap_reg_range(MCA_REG_ADC_BUFF_CH, MCA_REG_ADC_BUFF_SAMPLE_21), ++ regmap_reg_range(MCA_REG_UART_THR, MCA_REG_UART_RTSPIN), ++ regmap_reg_range(MCA_CC8X_MPU_NVRAM_START, MCA_CC8X_MPU_NVRAM_END), ++ regmap_reg_range(MCA_REG_TPM0_CFG0, MCA_REG_TPM2_CH7_CNT1), ++}; ++ ++static const struct regmap_range mca_cc8x_volatile_ranges[] = { ++ /* Real volatile registers */ ++ regmap_reg_range(MCA_IRQ_STATUS_0, MCA_IRQ_STATUS_3), ++ regmap_reg_range(MCA_TAMPER0_DATE_START, MCA_TAMPER0_EVENT), ++ regmap_reg_range(MCA_TAMPER1_DATE_START, MCA_TAMPER1_EVENT), ++ regmap_reg_range(MCA_TAMPER2_DATE_START, MCA_TAMPER2_EVENT), ++ regmap_reg_range(MCA_TAMPER3_DATE_START, MCA_TAMPER3_EVENT), ++ regmap_reg_range(MCA_TIMER_TICK_0, MCA_TIMER_TICK_3), ++ regmap_reg_range(MCA_LAST_MCA_RESET_0, MCA_LAST_MCA_RESET_3), ++ regmap_reg_range(MCA_LAST_MPU_RESET_0, MCA_LAST_MPU_RESET_3), ++ regmap_reg_range(MCA_LAST_WAKEUP_REASON_0, MCA_LAST_WAKEUP_REASON_3), ++ regmap_reg_range(MCA_CC8X_MPU_NVRAM_START, MCA_CC8X_MPU_NVRAM_END), ++ regmap_reg_range(MCA_RTC_COUNT_YEAR_L, MCA_RTC_COUNT_SEC), ++ regmap_reg_range(MCA_GPIO_DATA_0, MCA_GPIO_DATA_7), ++ regmap_reg_range(MCA_GPIO_IRQ_STATUS_0, MCA_GPIO_IRQ_STATUS_7), ++ regmap_reg_range(MCA_PWR_CTRL_0, MCA_PWR_STATUS_0), ++ regmap_reg_range(MCA_REG_ADC_VAL_L_0, MCA_REG_ADC_VAL_H_21), ++ ++ /* ++ * Fake volatile registers. ++ * ++ * These registers could be cached but non-volatile registers makes ++ * regmap access each register one by one which has some drawbacks: ++ * - Breaks CRC in the protocol. ++ * - Requires the MCA firmware to process each access as a separate ++ * access, even when the data requested must be returned in bulk. ++ * ++ * For this reasons we will consider all registers volatile. ++ */ ++ regmap_reg_range(MCA_HWVER_SOM, MCA_HWVER_SOM), ++ regmap_reg_range(MCA_DEVICE_ID, MCA_UID_9), ++ regmap_reg_range(MCA_IRQ_MASK_0, MCA_IRQ_MASK_3), ++ regmap_reg_range(MCA_PWR_KEY_DEBOUNCE, MCA_PWR_KEY_GUARD), ++ regmap_reg_range(MCA_CTRL_0, MCA_CTRL_0), ++ regmap_reg_range(MCA_TAMPER0_CFG0, MCA_TAMPER0_DELAY_PWROFF), ++ regmap_reg_range(MCA_TAMPER1_CFG0, MCA_TAMPER1_DELAY_PWROFF), ++ regmap_reg_range(MCA_TAMPER2_CFG0, MCA_TAMPER2_THRESH_HI_H), ++ regmap_reg_range(MCA_TAMPER3_CFG0, MCA_TAMPER3_THRESH_HI_H), ++ regmap_reg_range(MCA_RTC_CONTROL, MCA_RTC_CONTROL), ++ regmap_reg_range(MCA_RTC_ALARM_YEAR_L, MCA_RTC_ALARM_SEC), ++ regmap_reg_range(MCA_WDT_CONTROL, MCA_WDT_TIMEOUT), ++ regmap_reg_range(MCA_GPIO_NUM, MCA_GPIO_DIR_7), ++ regmap_reg_range(MCA_GPIO_IRQ_CFG_0, MCA_GPIO_IRQ_CFG_63), ++ regmap_reg_range(MCA_REG_ADC_NUM_CH, MCA_REG_ADC_NUM_BYTES), ++ regmap_reg_range(MCA_REG_ADC_CFG0_0, MCA_REG_ADC_CFG0_21), ++ regmap_reg_range(MCA_REG_ADC_CFG1_0, MCA_REG_ADC_CFG1_21), ++ regmap_reg_range(MCA_REG_ADC_CFG2_0, MCA_REG_ADC_CFG2_21), ++ regmap_reg_range(MCA_REG_ADC_SAMPLES_CNT_0, MCA_REG_ADC_SAMPLES_CNT_21), ++ regmap_reg_range(MCA_REG_ADC_THRESH_LO_L_0, MCA_REG_ADC_THRESH_LO_H_21), ++ regmap_reg_range(MCA_REG_ADC_THRESH_HI_L_0, MCA_REG_ADC_THRESH_HI_H_21), ++ regmap_reg_range(MCA_REG_ADC_TICKS_L_0, MCA_REG_ADC_TICKS_H_21), ++ regmap_reg_range(MCA_REG_ADC_IRQ_0, MCA_REG_ADC_IRQ_7), ++ regmap_reg_range(MCA_REG_ADC_CFG_0, MCA_REG_ADC_CFG_2), ++ regmap_reg_range(MCA_REG_ADC_BUFF_CH, MCA_REG_ADC_BUFF_SAMPLE_21), ++ regmap_reg_range(MCA_REG_UART_RHR, MCA_REG_UART_RTSPIN), ++ regmap_reg_range(MCA_REG_TPM0_CFG0, MCA_REG_TPM2_CH7_CNT1), ++}; ++ ++static const struct regmap_access_table mca_cc8x_readable_table = { ++ .yes_ranges = mca_cc8x_readable_ranges, ++ .n_yes_ranges = ARRAY_SIZE(mca_cc8x_readable_ranges), ++}; ++ ++static const struct regmap_access_table mca_cc8x_writeable_table = { ++ .yes_ranges = mca_cc8x_writeable_ranges, ++ .n_yes_ranges = ARRAY_SIZE(mca_cc8x_writeable_ranges), ++}; ++ ++static const struct regmap_access_table mca_cc8x_volatile_table = { ++ .yes_ranges = mca_cc8x_volatile_ranges, ++ .n_yes_ranges = ARRAY_SIZE(mca_cc8x_volatile_ranges), ++}; ++ ++static struct regmap_config mca_cc8x_regmap_config = { ++ .reg_bits = 16, ++ .val_bits = 8, ++ .max_register = 0xFFFF, ++ ++ .rd_table = &mca_cc8x_readable_table, ++ .wr_table = &mca_cc8x_writeable_table, ++ .volatile_table = &mca_cc8x_volatile_table, ++ ++ .cache_type = REGCACHE_NONE, ++}; ++ ++static const struct of_device_id mca_cc8x_dt_ids[] = { ++ { .compatible = "digi,mca_cc8x", }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, mca_cc8x_dt_ids); ++ ++static int mca_cc8x_i2c_probe(struct i2c_client *i2c, ++ const struct i2c_device_id *id) ++{ ++ struct mca_drv *mca; ++ int ret; ++ ++ mca = devm_kzalloc(&i2c->dev, sizeof(struct mca_drv), GFP_KERNEL); ++ if (mca == NULL) ++ return -ENOMEM; ++ ++ i2c_set_clientdata(i2c, mca); ++ mca->dev = &i2c->dev; ++ mca->chip_irq = i2c->irq; ++ mca->i2c_adapter_dev = &i2c->adapter->dev; ++ ++ mca->regmap = devm_regmap_init_i2c(i2c, &mca_cc8x_regmap_config); ++ if (IS_ERR(mca->regmap)) { ++ ret = PTR_ERR(mca->regmap); ++ dev_err(mca->dev, "Failed to allocate register map: %d\n", ret); ++ return ret; ++ } ++ ++ return mca_cc8x_device_init(mca, i2c->irq); ++} ++ ++static int mca_cc8x_i2c_remove(struct i2c_client *i2c) ++{ ++ struct mca_drv *mca = i2c_get_clientdata(i2c); ++ ++ mca_cc8x_device_exit(mca); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int mca_cc8x_i2c_suspend(struct device *dev) ++{ ++ return mca_cc8x_suspend(dev); ++} ++ ++static int mca_cc8x_i2c_resume(struct device *dev) ++{ ++ return mca_cc8x_resume(dev); ++} ++ ++/* ++ * Use suspend_late/resume_early so the mca_drv continues being functional ++ * during the regular suspend/resume callbacks of other drivers, just in case ++ * they use any functionality of the mca. ++ */ ++static const struct dev_pm_ops mca_cc8x_i2c_pm_ops = { ++ SET_LATE_SYSTEM_SLEEP_PM_OPS(mca_cc8x_i2c_suspend, mca_cc8x_i2c_resume) ++}; ++#endif ++ ++static const struct i2c_device_id mca_cc8x_i2c_id[] = { ++ {"mca_cc8x", 0}, ++ {}, ++}; ++MODULE_DEVICE_TABLE(i2c, mca_cc8x_i2c_id); ++ ++static struct i2c_driver mca_cc8x_i2c_driver = { ++ .driver = { ++ .name = "mca_cc8x", ++ .of_match_table = of_match_ptr(mca_cc8x_dt_ids), ++#ifdef CONFIG_PM ++ .pm = &mca_cc8x_i2c_pm_ops, ++#endif ++ }, ++ .probe = mca_cc8x_i2c_probe, ++ .remove = mca_cc8x_i2c_remove, ++ .id_table = mca_cc8x_i2c_id, ++}; ++ ++module_i2c_driver(mca_cc8x_i2c_driver); +diff --git a/drivers/mfd/mca-cc8x-irq.c b/drivers/mfd/mca-cc8x-irq.c +new file mode 100644 +index 000000000000..1036e602d1f9 +--- /dev/null ++++ b/drivers/mfd/mca-cc8x-irq.c +@@ -0,0 +1,143 @@ ++/* ++ * Copyright 2018 - 2019 Digi International Inc ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define MCA_IRQ_0_OFFSET 0 ++#define MCA_IRQ_1_OFFSET 1 ++#define MCA_IRQ_2_OFFSET 2 ++#define MCA_IRQ_3_OFFSET 3 ++ ++static const struct regmap_irq mca_cc8x_irqs[] = { ++ /* MCA irqs A register */ ++ [MCA_CC8X_IRQ_RTC_ALARM] = { ++ .reg_offset = MCA_IRQ_0_OFFSET, ++ .mask = MCA_M_RTC_ALARM, ++ }, ++ [MCA_CC8X_IRQ_RTC_1HZ] = { ++ .reg_offset = MCA_IRQ_0_OFFSET, ++ .mask = MCA_M_RTC_1HZ, ++ }, ++ [MCA_CC8X_IRQ_WATCHDOG] = { ++ .reg_offset = MCA_IRQ_0_OFFSET, ++ .mask = MCA_M_WATCHDOG, ++ }, ++ [MCA_CC8X_IRQ_PWR_SLEEP] = { ++ .reg_offset = MCA_IRQ_0_OFFSET, ++ .mask = MCA_M_PWR_SLEEP, ++ }, ++ [MCA_CC8X_IRQ_PWR_OFF] = { ++ .reg_offset = MCA_IRQ_0_OFFSET, ++ .mask = MCA_M_PWR_OFF, ++ }, ++ [MCA_CC8X_IRQ_TAMPER0] = { ++ .reg_offset = MCA_IRQ_0_OFFSET, ++ .mask = MCA_M_TAMPER0, ++ }, ++ [MCA_CC8X_IRQ_TAMPER1] = { ++ .reg_offset = MCA_IRQ_0_OFFSET, ++ .mask = MCA_M_TAMPER1, ++ }, ++ [MCA_CC8X_IRQ_ADC] = { ++ .reg_offset = MCA_IRQ_0_OFFSET, ++ .mask = MCA_M_ADC, ++ }, ++ [MCA_CC8X_IRQ_GPIO_BANK_0] = { ++ .reg_offset = MCA_IRQ_1_OFFSET, ++ .mask = MCA_GPIO_BANK_0, ++ }, ++ [MCA_CC8X_IRQ_GPIO_BANK_1] = { ++ .reg_offset = MCA_IRQ_1_OFFSET, ++ .mask = MCA_GPIO_BANK_1, ++ }, ++ [MCA_CC8X_IRQ_GPIO_BANK_2] = { ++ .reg_offset = MCA_IRQ_1_OFFSET, ++ .mask = MCA_GPIO_BANK_2, ++ }, ++ [MCA_CC8X_IRQ_TAMPER2] = { ++ .reg_offset = MCA_IRQ_2_OFFSET, ++ .mask = MCA_M_TAMPER2, ++ }, ++ [MCA_CC8X_IRQ_TAMPER3] = { ++ .reg_offset = MCA_IRQ_2_OFFSET, ++ .mask = MCA_M_TAMPER3, ++ }, ++ [MCA_CC8X_IRQ_UART] = { ++ .reg_offset = MCA_IRQ_2_OFFSET, ++ .mask = MCA_M_UART, ++ }, ++}; ++ ++/* ++ * This hook is used to call the SCU MU interrupt handler to manage the SC ++ * interrupts that could have been signaled by the SCU. ++ * This is needed because the MCA ISR replaces the original SCU MU ISR because ++ * both can not be registered as shared irq handlers. ++ */ ++static int mca_cc8x_pre_irq(void *irq_drv_data) ++{ ++ return imx8_scu_mu_isr(0, NULL); ++} ++ ++static const struct regmap_irq_chip mca_cc8x_irq_chip = { ++ .name = "mca-cc8x-irq", ++ .irqs = mca_cc8x_irqs, ++ .num_irqs = ARRAY_SIZE(mca_cc8x_irqs), ++ .num_regs = MCA_NUM_IRQ_REGS, ++ .status_base = MCA_IRQ_STATUS_0, ++ .mask_base = MCA_IRQ_MASK_0, ++ .ack_base = MCA_IRQ_STATUS_0, ++ .handle_pre_irq = mca_cc8x_pre_irq, ++ .init_ack_masked = true, ++}; ++ ++int mca_cc8x_irq_init(struct mca_drv *mca) ++{ ++ int ret; ++ ++ if (!mca->chip_irq) { ++ dev_err(mca->dev, "No IRQ configured\n"); ++ return -EINVAL; ++ } ++ ++ mca->irq_base = -1; ++ ret = regmap_add_irq_chip(mca->regmap, mca->chip_irq, ++ IRQF_ONESHOT | IRQF_SHARED, ++ mca->irq_base, &mca_cc8x_irq_chip, ++ &mca->regmap_irq); ++ if (ret) { ++ dev_err(mca->dev, "Failed to reguest IRQ %d: %d\n", ++ mca->chip_irq, ret); ++ return ret; ++ } ++ ++ ret = imx8_mu_enable_sc_irqs(SC_IRQ_TEMP_MCA | SC_IRQ_TEMP_PMIC0_HIGH | \ ++ SC_IRQ_TEMP_PMIC1_HIGH, ++ SC_IRQ_RTC, ++ SC_IRQ_BUTTON, ++ SC_IRQ_WDOG); ++ if (ret) ++ dev_err(mca->dev, "Failed to reguest SC MU IRQs (%d)\n", ret); ++ ++ return ret; ++} ++ ++void mca_cc8x_irq_exit(struct mca_drv *mca) ++{ ++ regmap_del_irq_chip(mca->chip_irq, mca->regmap_irq); ++} +diff --git a/include/linux/mfd/mca-cc6ul/core.h b/include/linux/mfd/mca-cc6ul/core.h +new file mode 100644 +index 000000000000..497a79dfb419 +--- /dev/null ++++ b/include/linux/mfd/mca-cc6ul/core.h +@@ -0,0 +1,56 @@ ++/* ++ * Copyright 2016 - 2019 Digi International Inc ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#ifndef MFD_MCA_CC6UL_CORE_H_ ++#define MFD_MCA_CC6UL_CORE_H_ ++ ++#include ++#include ++#include ++ ++/* MCA CC6UL modules */ ++#define MCA_CC6UL_DRVNAME_CORE "mca-cc6ul-core" ++#define MCA_CC6UL_DRVNAME_RTC "mca-cc6ul-rtc" ++#define MCA_CC6UL_DRVNAME_WATCHDOG "mca-cc6ul-watchdog" ++#define MCA_CC6UL_DRVNAME_GPIO "mca-cc6ul-gpio" ++#define MCA_CC6UL_DRVNAME_PWRKEY "mca-cc6ul-pwrkey" ++#define MCA_CC6UL_DRVNAME_ADC "mca-cc6ul-adc" ++#define MCA_CC6UL_DRVNAME_TAMPER "mca-cc6ul-tamper" ++#define MCA_CC6UL_DRVNAME_COMPARATOR "mca-cc6ul-comparator" ++#define MCA_CC6UL_DRVNAME_UART "mca-cc6ul-uart" ++ ++#define MCA_CC6UL_DEVICE_ID_VAL 0x61 ++ ++/* Interrupts */ ++enum mca_cc6ul_irqs { ++ MCA_CC6UL_IRQ_RTC_ALARM, ++ MCA_CC6UL_IRQ_RTC_1HZ, ++ MCA_CC6UL_IRQ_WATCHDOG, ++ MCA_CC6UL_IRQ_PWR_SLEEP, ++ MCA_CC6UL_IRQ_PWR_OFF, ++ MCA_CC6UL_IRQ_TAMPER0, ++ MCA_CC6UL_IRQ_TAMPER1, ++ MCA_CC6UL_IRQ_ADC, ++ MCA_CC6UL_IRQ_GPIO_BANK_0, ++ MCA_CC6UL_IRQ_TAMPER2, ++ MCA_CC6UL_IRQ_TAMPER3, ++ MCA_CC6UL_IRQ_UART, ++ /* ... */ ++ ++ MCA_CC6UL_NUM_IRQS, ++}; ++ ++int mca_cc6ul_device_init(struct mca_drv *mca, u32 irq); ++int mca_cc6ul_irq_init(struct mca_drv *mca); ++void mca_cc6ul_device_exit(struct mca_drv *mca); ++void mca_cc6ul_irq_exit(struct mca_drv *mca); ++int mca_cc6ul_suspend(struct device *dev); ++int mca_cc6ul_resume(struct device *dev); ++ ++#endif /* MFD_MCA_CC6UL_CORE_H_ */ +diff --git a/include/linux/mfd/mca-cc6ul/registers.h b/include/linux/mfd/mca-cc6ul/registers.h +new file mode 100644 +index 000000000000..21a1dd3e5168 +--- /dev/null ++++ b/include/linux/mfd/mca-cc6ul/registers.h +@@ -0,0 +1,61 @@ ++/* ++ * Copyright 2017 - 2019 Digi International Inc ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#ifndef MCA_CC6UL_REGISTERS_H_ ++#define MCA_CC6UL_REGISTERS_H_ ++ ++#include ++ ++/* EP0: Control and status */ ++#define MCA_CC6UL_MPU_NVRAM_START 0x00b0 ++#define MCA_CC6UL_MPU_NVRAM_END 0x00b7 ++ ++/* ++ * MCA registers bitfields ++ */ ++ ++/* MCA_CC6UL_LAST_MCA_RESET_n (addr=0x0098 & 0x009b) */ ++#define MCA_CC6UL_LAST_MCA_RST_LLW BIT(0) ++#define MCA_CC6UL_LAST_MCA_RST_LVD BIT(1) ++#define MCA_CC6UL_LAST_MCA_RST_WD BIT(5) ++#define MCA_CC6UL_LAST_MCA_RST_PIN BIT(6) ++#define MCA_CC6UL_LAST_MCA_RST_PWRON BIT(7) ++#define MCA_CC6UL_LAST_MCA_RST_LOCKUP BIT(9) ++#define MCA_CC6UL_LAST_MCA_RST_SW BIT(10) ++#define MCA_CC6UL_LAST_MCA_RST_MDMAPP BIT(11) ++#define MCA_CC6UL_LAST_MCA_RST_SMAE BIT(13) ++ ++/* MCA_CC6UL_LAST_MPU_RESET_n (addr=0x009c & 0x009f) */ ++#define MCA_CC6UL_LAST_MPU_RST_PWRON BIT(0) ++#define MCA_CC6UL_LAST_MPU_RST_SYSR BIT(1) ++#define MCA_CC6UL_LAST_MPU_RST_WD BIT(2) ++#define MCA_CC6UL_LAST_MPU_RST_OFFWAKE BIT(3) ++#define MCA_CC6UL_LAST_MPU_RST_MCARST BIT(4) ++ ++/* MCA_CC6UL_LAST_WAKEUP_REASON_n (addr=0x00a0 & 0x00a3) */ ++#define MCA_CC6UL_LAST_WAKEUP_PWRIO BIT(0) ++#define MCA_CC6UL_LAST_WAKEUP_TIMER BIT(1) ++#define MCA_CC6UL_LAST_WAKEUP_RTC BIT(2) ++#define MCA_CC6UL_LAST_WAKEUP_LPUART BIT(3) ++#define MCA_CC6UL_LAST_WAKEUP_TAMPER0 BIT(4) ++#define MCA_CC6UL_LAST_WAKEUP_TAMPER1 BIT(5) ++#define MCA_CC6UL_LAST_WAKEUP_TAMPER2 BIT(6) ++#define MCA_CC6UL_LAST_WAKEUP_TAMPER3 BIT(7) ++#define MCA_CC6UL_LAST_WAKEUP_IO0 BIT(8) ++#define MCA_CC6UL_LAST_WAKEUP_IO1 BIT(9) ++#define MCA_CC6UL_LAST_WAKEUP_IO2 BIT(10) ++#define MCA_CC6UL_LAST_WAKEUP_IO3 BIT(11) ++#define MCA_CC6UL_LAST_WAKEUP_IO4 BIT(12) ++#define MCA_CC6UL_LAST_WAKEUP_IO5 BIT(13) ++#define MCA_CC6UL_LAST_WAKEUP_IO6 BIT(14) ++#define MCA_CC6UL_LAST_WAKEUP_IO7 BIT(15) ++#define MCA_CC6UL_LAST_WAKEUP_VCC BIT(16) ++#define MCA_CC6UL_LAST_WAKEUP_CPU BIT(17) ++ ++#endif /* MCA_CC6UL_REGISTERS_H_ */ +diff --git a/include/linux/mfd/mca-common/core.h b/include/linux/mfd/mca-common/core.h +new file mode 100644 +index 000000000000..f8b06dfab66b +--- /dev/null ++++ b/include/linux/mfd/mca-common/core.h +@@ -0,0 +1,71 @@ ++/* ++ * Copyright 2017 - 2019 Digi International Inc ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#ifndef MFD_MCA_COMMON_CORE_H_ ++#define MFD_MCA_COMMON_CORE_H_ ++ ++#include ++#include ++#include ++#include ++ ++#define MCA_MAKE_FW_VER(a,b) (u16)(((a) << 8) | ((b) & 0xff)) ++#define MCA_FW_VER_MAJOR(v) (((v) >> 8) & 0xff) ++#define MCA_FW_VER_MINOR(v) ((v) & 0xff) ++#define MCA_FW_VER_ALPHA_MASK BIT(15) ++ ++#define MCA_IRQ_GPIO_BANK_0_NAME "GPIO_BANK0" ++#define MCA_IRQ_GPIO_BANK_1_NAME "GPIO_BANK1" ++#define MCA_IRQ_GPIO_BANK_2_NAME "GPIO_BANK2" ++#define MCA_IRQ_GPIO_BANK_3_NAME "GPIO_BANK3" ++#define MCA_IRQ_GPIO_BANK_4_NAME "GPIO_BANK4" ++#define MCA_IRQ_GPIO_BANK_5_NAME "GPIO_BANK5" ++#define MCA_IRQ_RTC_ALARM_NAME "RTC ALARM" ++#define MCA_IRQ_RTC_1HZ_NAME "RTC 1HZ" ++#define MCA_IRQ_PWR_SLEEP_NAME "SLEEP" ++#define MCA_IRQ_PWR_OFF_NAME "PWR OFF" ++#define MCA_IRQ_WATCHDOG_NAME "WATCHDOG" ++#define MCA_IRQ_TAMPER0_NAME "TAMPER0" ++#define MCA_IRQ_TAMPER1_NAME "TAMPER1" ++#define MCA_IRQ_TAMPER2_NAME "TAMPER2" ++#define MCA_IRQ_TAMPER3_NAME "TAMPER3" ++#define MCA_IRQ_ADC_NAME "ADC" ++#define MCA_IRQ_UART_NAME "UART" ++ ++/* Number of interrupt registers */ ++#define MCA_NUM_IRQ_REGS 4 ++/* Max number of IOs */ ++#define MCA_MAX_IOS 64 ++ ++#define MCA_MAX_GPIO_IRQ_BANKS 6 ++ ++#define MCA_MAX_IO_BYTES ((MCA_MAX_IOS + 7) / 8) ++ ++struct mca_drv { ++ struct device *dev; ++ u8 dev_id; ++ u8 hw_version; ++ bool fw_is_alpha; ++ u16 fw_version; ++ u32 flags; ++ struct regmap *regmap; ++ struct regmap_irq_chip_data *regmap_irq; ++ struct notifier_block restart_handler; ++ int chip_irq; ++ u32 irq_base; ++ int gpio_base; ++ int fw_update_gpio; ++ int som_hv; ++ u32 last_mca_reset; ++ u32 last_mpu_reset; ++ struct bin_attribute *nvram; ++ struct device *i2c_adapter_dev; ++}; ++ ++#endif /* MFD_MCA_COMMON_CORE_H_ */ +diff --git a/include/linux/mfd/mca-common/registers.h b/include/linux/mfd/mca-common/registers.h +new file mode 100644 +index 000000000000..913ba71c9738 +--- /dev/null ++++ b/include/linux/mfd/mca-common/registers.h +@@ -0,0 +1,1373 @@ ++/* ++ * Copyright 2017-2019 Digi International Inc ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#ifndef MCA_COMMON_REGISTERS_H_ ++#define MCA_COMMON_REGISTERS_H_ ++ ++#include ++ ++/* EP0: Control and status */ ++#define MCA_DEVICE_ID 0x0001 ++#define MCA_HW_VER 0x0002 ++#define MCA_FW_VER_L 0x0003 ++#define MCA_FW_VER_H 0x0004 ++#define MCA_UID_0 0x0005 ++#define MCA_UID_1 0x0006 ++#define MCA_UID_2 0x0007 ++#define MCA_UID_3 0x0008 ++#define MCA_UID_4 0x0009 ++#define MCA_UID_5 0x000A ++#define MCA_UID_6 0x000B ++#define MCA_UID_7 0x000C ++#define MCA_UID_8 0x000D ++#define MCA_UID_9 0x000E ++#define MCA_HWVER_SOM 0x000F ++ ++#define MCA_IRQ_STATUS_0 0x0020 ++#define MCA_IRQ_STATUS_1 0x0021 ++#define MCA_IRQ_STATUS_2 0x0022 ++#define MCA_IRQ_STATUS_3 0x0023 ++ ++#define MCA_IRQ_MASK_0 0x0024 ++#define MCA_IRQ_MASK_1 0x0025 ++#define MCA_IRQ_MASK_2 0x0026 ++#define MCA_IRQ_MASK_3 0x0027 ++ ++#define MCA_PWR_CTRL_0 0x0028 ++#define MCA_PWR_STATUS_0 0x0029 ++#define MCA_PWR_KEY_DEBOUNCE 0x002a ++#define MCA_PWR_KEY_DELAY 0x002b ++#define MCA_PWR_KEY_GUARD 0x002c ++ ++#define MCA_CTRL_UNLOCK_0 0x002d ++#define MCA_CTRL_UNLOCK_1 0x002e ++#define MCA_CTRL_UNLOCK_2 0x002f ++#define MCA_CTRL_UNLOCK_3 0x0030 ++ ++#define MCA_CTRL_0 0x0031 ++#define MCA_CTRL_1 0x0032 ++ ++#define MCA_TAMPER0_CFG0 0x0037 ++#define MCA_TAMPER0_CFG1 0x0038 ++#define MCA_TAMPER0_IO_IN 0x0039 ++#define MCA_TAMPER0_IO_OUT 0x003a ++#define MCA_TAMPER0_DELAY_PWROFF 0x003b ++/* 1 byte for padding */ ++#define MCA_TAMPER0_DATE_START 0x003d ++#define MCA_TAMPER0_DATE_END 0x0043 ++/* 1 byte for padding */ ++#define MCA_TAMPER0_EVENT 0x0045 ++ ++#define MCA_TAMPER1_CFG0 0x0046 ++#define MCA_TAMPER1_CFG1 0x0047 ++#define MCA_TAMPER1_IO_IN 0x0048 ++#define MCA_TAMPER1_IO_OUT 0x0049 ++#define MCA_TAMPER1_DELAY_PWROFF 0x004a ++/* 1 byte for padding */ ++#define MCA_TAMPER1_DATE_START 0x004c ++#define MCA_TAMPER1_DATE_END 0x0052 ++/* 1 byte for padding */ ++#define MCA_TAMPER1_EVENT 0x0054 ++ ++#define MCA_TAMPER_REGS_LEN (MCA_TAMPER1_CFG0 - \ ++ MCA_TAMPER0_CFG0) ++ ++#define MCA_TIMER_TICK_0 0x0057 ++#define MCA_TIMER_TICK_1 0x0058 ++#define MCA_TIMER_TICK_2 0x0059 ++#define MCA_TIMER_TICK_3 0x005a ++ ++#define MCA_TAMPER2_CFG0 0x0060 ++#define MCA_TAMPER2_CFG1 0x0061 ++#define MCA_TAMPER2_IO_IN 0x0062 ++#define MCA_TAMPER2_IO_OUT 0x0063 ++#define MCA_TAMPER2_DELAY_PWROFF 0x0064 ++/* 1 byte for padding */ ++/* Tamper event time-stamp follows the same format used by the RTC registers*/ ++#define MCA_TAMPER2_DATE_START 0x0066 ++/* ... */ ++#define MCA_TAMPER2_DATE_END 0x006C ++/* 1 byte for padding */ ++#define MCA_TAMPER2_EVENT 0x006E ++/* 1 byte for padding */ ++#define MCA_TAMPER2_TICKS_L 0x0070 ++#define MCA_TAMPER2_TICKS_H 0x0071 ++#define MCA_TAMPER2_THRESH_LO_L 0x0072 ++#define MCA_TAMPER2_THRESH_LO_H 0x0073 ++#define MCA_TAMPER2_THRESH_HI_L 0x0074 ++#define MCA_TAMPER2_THRESH_HI_H 0x0075 ++ ++#define MCA_TAMPER3_CFG0 0x0080 ++#define MCA_TAMPER3_CFG1 0x0081 ++#define MCA_TAMPER3_IO_IN 0x0082 ++#define MCA_TAMPER3_IO_OUT 0x0083 ++#define MCA_TAMPER3_DELAY_PWROFF 0x0084 ++/* 1 byte for padding */ ++/* Tamper event time-stamp follows the same format used by the RTC registers*/ ++#define MCA_TAMPER3_DATE_START 0x0086 ++/* ... */ ++#define MCA_TAMPER3_DATE_END 0x008C ++/* 1 byte for padding */ ++#define MCA_TAMPER3_EVENT 0x008E ++/* 1 byte for padding */ ++#define MCA_TAMPER3_TICKS_L 0x0090 ++#define MCA_TAMPER3_TICKS_H 0x0091 ++#define MCA_TAMPER3_THRESH_LO_L 0x0092 ++#define MCA_TAMPER3_THRESH_LO_H 0x0093 ++#define MCA_TAMPER3_THRESH_HI_L 0x0094 ++#define MCA_TAMPER3_THRESH_HI_H 0x0095 ++ ++#define MCA_LAST_MCA_RESET_0 0x0098 ++#define MCA_LAST_MCA_RESET_3 0x009b ++#define MCA_LAST_MPU_RESET_0 0x009c ++#define MCA_LAST_MPU_RESET_3 0x009f ++#define MCA_LAST_WAKEUP_REASON_0 0x00a0 ++#define MCA_LAST_WAKEUP_REASON_3 0x00a3 ++ ++/* EP1: RTC */ ++#define MCA_RTC_CONTROL 0x0101 ++ ++#define MCA_RTC_COUNT_YEAR_L 0x0103 ++#define MCA_RTC_COUNT_YEAR_H 0x0104 ++#define MCA_RTC_COUNT_MONTH 0x0105 ++#define MCA_RTC_COUNT_DAY 0x0106 ++#define MCA_RTC_COUNT_HOUR 0x0107 ++#define MCA_RTC_COUNT_MIN 0x0108 ++#define MCA_RTC_COUNT_SEC 0x0109 ++#define MCA_RTC_ALARM_YEAR_L 0x010A ++#define MCA_RTC_ALARM_YEAR_H 0x010B ++#define MCA_RTC_ALARM_MONTH 0x010C ++#define MCA_RTC_ALARM_DAY 0x010D ++#define MCA_RTC_ALARM_HOUR 0x010E ++#define MCA_RTC_ALARM_MIN 0x010F ++#define MCA_RTC_ALARM_SEC 0x0110 ++ ++ ++/* EP2: Watchdog */ ++#define MCA_WDT_CONTROL 0x0201 ++#define MCA_WDT_TIMEOUT 0x0202 ++#define MCA_WDT_REFRESH_0 0x0203 ++#define MCA_WDT_REFRESH_1 0x0204 ++#define MCA_WDT_REFRESH_2 0x0205 ++#define MCA_WDT_REFRESH_3 0x0206 ++ ++ ++/* EP3: GPIO */ ++#define MCA_GPIO_NUM 0x0301 ++#define MCA_GPIO_DIR_0 0x0302 ++#define MCA_GPIO_DIR_1 0x0303 ++#define MCA_GPIO_DIR_2 0x0304 ++#define MCA_GPIO_DIR_3 0x0305 ++#define MCA_GPIO_DIR_4 0x0306 ++#define MCA_GPIO_DIR_5 0x0307 ++#define MCA_GPIO_DIR_6 0x0308 ++#define MCA_GPIO_DIR_7 0x0309 ++#define MCA_GPIO_DATA_0 0x030A ++#define MCA_GPIO_DATA_1 0x030B ++#define MCA_GPIO_DATA_2 0x030C ++#define MCA_GPIO_DATA_3 0x030D ++#define MCA_GPIO_DATA_4 0x030E ++#define MCA_GPIO_DATA_5 0x030F ++#define MCA_GPIO_DATA_6 0x0310 ++#define MCA_GPIO_DATA_7 0x0311 ++#define MCA_GPIO_SET_0 0x0312 ++#define MCA_GPIO_SET_1 0x0313 ++#define MCA_GPIO_SET_2 0x0314 ++#define MCA_GPIO_SET_3 0x0315 ++#define MCA_GPIO_SET_4 0x0316 ++#define MCA_GPIO_SET_5 0x0317 ++#define MCA_GPIO_SET_6 0x0318 ++#define MCA_GPIO_SET_7 0x0319 ++#define MCA_GPIO_CLEAR_0 0x031A ++#define MCA_GPIO_CLEAR_1 0x031B ++#define MCA_GPIO_CLEAR_2 0x031C ++#define MCA_GPIO_CLEAR_3 0x031D ++#define MCA_GPIO_CLEAR_4 0x031E ++#define MCA_GPIO_CLEAR_5 0x031F ++#define MCA_GPIO_CLEAR_6 0x0320 ++#define MCA_GPIO_CLEAR_7 0x0321 ++#define MCA_GPIO_TOGGLE_0 0x0322 ++#define MCA_GPIO_TOGGLE_1 0x0323 ++#define MCA_GPIO_TOGGLE_2 0x0324 ++#define MCA_GPIO_TOGGLE_3 0x0325 ++#define MCA_GPIO_TOGGLE_4 0x0326 ++#define MCA_GPIO_TOGGLE_5 0x0327 ++#define MCA_GPIO_TOGGLE_6 0x0328 ++#define MCA_GPIO_TOGGLE_7 0x0329 ++#define MCA_GPIO_IRQ_STATUS_0 0x032a ++#define MCA_GPIO_IRQ_STATUS_1 0x032b ++#define MCA_GPIO_IRQ_STATUS_2 0x032c ++#define MCA_GPIO_IRQ_STATUS_3 0x032d ++#define MCA_GPIO_IRQ_STATUS_4 0x032e ++#define MCA_GPIO_IRQ_STATUS_5 0x032f ++#define MCA_GPIO_IRQ_STATUS_6 0x0330 ++#define MCA_GPIO_IRQ_STATUS_7 0x0331 ++/* Note, there is one IRQ configuration register per GPIO pin */ ++#define MCA_GPIO_IRQ_CFG_0 0x0332 ++#define MCA_GPIO_IRQ_CFG_1 0x0333 ++#define MCA_GPIO_IRQ_CFG_2 0x0334 ++#define MCA_GPIO_IRQ_CFG_3 0x0335 ++#define MCA_GPIO_IRQ_CFG_4 0x0336 ++#define MCA_GPIO_IRQ_CFG_5 0x0337 ++#define MCA_GPIO_IRQ_CFG_6 0x0338 ++#define MCA_GPIO_IRQ_CFG_7 0x0339 ++/* ... */ ++#define MCA_GPIO_IRQ_CFG_63 0x0371 ++ ++#define MCA_GPIO_DEB_CFG_0 0x0372 ++/* ... */ ++#define MCA_GPIO_DEB_CFG_7 0x0379 ++ ++#define MCA_GPIO_DEB_CNT_0 0x037a ++/* ... */ ++#define MCA_GPIO_DEB_CNT_63 0x03b9 ++ ++/* EP4, ADCs Configuration (0 - 31) */ ++#define MCA_REG_ADC_NUM_CH 0x0401 ++#define MCA_REG_ADC_NUM_BYTES 0x0402 ++#define MCA_REG_ADC_CFG0_0 0x0404 ++#define MCA_REG_ADC_CFG0_1 0x0405 ++#define MCA_REG_ADC_CFG0_2 0x0406 ++#define MCA_REG_ADC_CFG0_3 0x0407 ++#define MCA_REG_ADC_CFG0_4 0x0408 ++#define MCA_REG_ADC_CFG0_5 0x0409 ++#define MCA_REG_ADC_CFG0_6 0x040a ++#define MCA_REG_ADC_CFG0_7 0x040b ++#define MCA_REG_ADC_CFG0_8 0x040c ++#define MCA_REG_ADC_CFG0_9 0x040d ++#define MCA_REG_ADC_CFG0_10 0x040e ++#define MCA_REG_ADC_CFG0_11 0x040f ++#define MCA_REG_ADC_CFG0_12 0x0410 ++#define MCA_REG_ADC_CFG0_13 0x0411 ++#define MCA_REG_ADC_CFG0_14 0x0412 ++#define MCA_REG_ADC_CFG0_15 0x0413 ++#define MCA_REG_ADC_CFG0_16 0x0414 ++#define MCA_REG_ADC_CFG0_17 0x0415 ++#define MCA_REG_ADC_CFG0_18 0x0416 ++#define MCA_REG_ADC_CFG0_19 0x0417 ++#define MCA_REG_ADC_CFG0_20 0x0418 ++#define MCA_REG_ADC_CFG0_21 0x0419 ++#define MCA_REG_ADC_CFG0_22 0x041a ++#define MCA_REG_ADC_CFG0_23 0x041b ++#define MCA_REG_ADC_CFG0_24 0x041c ++#define MCA_REG_ADC_CFG0_25 0x041d ++#define MCA_REG_ADC_CFG0_26 0x041e ++#define MCA_REG_ADC_CFG0_27 0x041f ++#define MCA_REG_ADC_CFG0_28 0x0420 ++#define MCA_REG_ADC_CFG0_29 0x0421 ++#define MCA_REG_ADC_CFG0_30 0x0422 ++#define MCA_REG_ADC_CFG0_31 0x0423 ++ ++#define MCA_REG_ADC_CFG1_0 0x0424 ++#define MCA_REG_ADC_CFG1_1 0x0425 ++#define MCA_REG_ADC_CFG1_2 0x0426 ++#define MCA_REG_ADC_CFG1_3 0x0427 ++#define MCA_REG_ADC_CFG1_4 0x0428 ++#define MCA_REG_ADC_CFG1_5 0x0429 ++#define MCA_REG_ADC_CFG1_6 0x042a ++#define MCA_REG_ADC_CFG1_7 0x042b ++#define MCA_REG_ADC_CFG1_8 0x042c ++#define MCA_REG_ADC_CFG1_9 0x042d ++#define MCA_REG_ADC_CFG1_10 0x042e ++#define MCA_REG_ADC_CFG1_11 0x042f ++#define MCA_REG_ADC_CFG1_12 0x0430 ++#define MCA_REG_ADC_CFG1_13 0x0431 ++#define MCA_REG_ADC_CFG1_14 0x0432 ++#define MCA_REG_ADC_CFG1_15 0x0433 ++#define MCA_REG_ADC_CFG1_16 0x0434 ++#define MCA_REG_ADC_CFG1_17 0x0435 ++#define MCA_REG_ADC_CFG1_18 0x0436 ++#define MCA_REG_ADC_CFG1_19 0x0437 ++#define MCA_REG_ADC_CFG1_20 0x0438 ++#define MCA_REG_ADC_CFG1_21 0x0439 ++#define MCA_REG_ADC_CFG1_22 0x043a ++#define MCA_REG_ADC_CFG1_23 0x043b ++#define MCA_REG_ADC_CFG1_24 0x043c ++#define MCA_REG_ADC_CFG1_25 0x043d ++#define MCA_REG_ADC_CFG1_26 0x043e ++#define MCA_REG_ADC_CFG1_27 0x043f ++#define MCA_REG_ADC_CFG1_28 0x0440 ++#define MCA_REG_ADC_CFG1_29 0x0441 ++#define MCA_REG_ADC_CFG1_30 0x0442 ++#define MCA_REG_ADC_CFG1_31 0x0443 ++ ++#define MCA_REG_ADC_CFG2_0 0x0444 ++#define MCA_REG_ADC_CFG2_1 0x0445 ++#define MCA_REG_ADC_CFG2_2 0x0446 ++#define MCA_REG_ADC_CFG2_3 0x0447 ++#define MCA_REG_ADC_CFG2_4 0x0448 ++#define MCA_REG_ADC_CFG2_5 0x0449 ++#define MCA_REG_ADC_CFG2_6 0x044a ++#define MCA_REG_ADC_CFG2_7 0x044b ++#define MCA_REG_ADC_CFG2_8 0x044c ++#define MCA_REG_ADC_CFG2_9 0x044d ++#define MCA_REG_ADC_CFG2_10 0x044e ++#define MCA_REG_ADC_CFG2_11 0x044f ++#define MCA_REG_ADC_CFG2_12 0x0450 ++#define MCA_REG_ADC_CFG2_13 0x0451 ++#define MCA_REG_ADC_CFG2_14 0x0452 ++#define MCA_REG_ADC_CFG2_15 0x0453 ++#define MCA_REG_ADC_CFG2_16 0x0454 ++#define MCA_REG_ADC_CFG2_17 0x0455 ++#define MCA_REG_ADC_CFG2_18 0x0456 ++#define MCA_REG_ADC_CFG2_19 0x0457 ++#define MCA_REG_ADC_CFG2_20 0x0458 ++#define MCA_REG_ADC_CFG2_21 0x0459 ++#define MCA_REG_ADC_CFG2_22 0x045a ++#define MCA_REG_ADC_CFG2_23 0x045b ++#define MCA_REG_ADC_CFG2_24 0x045c ++#define MCA_REG_ADC_CFG2_25 0x045d ++#define MCA_REG_ADC_CFG2_26 0x045e ++#define MCA_REG_ADC_CFG2_27 0x045f ++#define MCA_REG_ADC_CFG2_28 0x0460 ++#define MCA_REG_ADC_CFG2_29 0x0461 ++#define MCA_REG_ADC_CFG2_30 0x0462 ++#define MCA_REG_ADC_CFG2_31 0x0463 ++ ++#define MCA_REG_ADC_VAL_L_0 0x464 ++#define MCA_REG_ADC_VAL_H_0 0x465 ++#define MCA_REG_ADC_VAL_L_1 0x466 ++#define MCA_REG_ADC_VAL_H_1 0x467 ++#define MCA_REG_ADC_VAL_L_2 0x468 ++#define MCA_REG_ADC_VAL_H_2 0x469 ++#define MCA_REG_ADC_VAL_L_3 0x46a ++#define MCA_REG_ADC_VAL_H_3 0x46b ++#define MCA_REG_ADC_VAL_L_4 0x46c ++#define MCA_REG_ADC_VAL_H_4 0x46d ++#define MCA_REG_ADC_VAL_L_5 0x46e ++#define MCA_REG_ADC_VAL_H_5 0x46f ++#define MCA_REG_ADC_VAL_L_6 0x470 ++#define MCA_REG_ADC_VAL_H_6 0x471 ++#define MCA_REG_ADC_VAL_L_7 0x472 ++#define MCA_REG_ADC_VAL_H_7 0x473 ++#define MCA_REG_ADC_VAL_L_8 0x474 ++#define MCA_REG_ADC_VAL_H_8 0x475 ++#define MCA_REG_ADC_VAL_L_9 0x476 ++#define MCA_REG_ADC_VAL_H_9 0x477 ++#define MCA_REG_ADC_VAL_L_10 0x478 ++#define MCA_REG_ADC_VAL_H_10 0x479 ++#define MCA_REG_ADC_VAL_L_11 0x47a ++#define MCA_REG_ADC_VAL_H_11 0x47b ++#define MCA_REG_ADC_VAL_L_12 0x47c ++#define MCA_REG_ADC_VAL_H_12 0x47d ++#define MCA_REG_ADC_VAL_L_13 0x47e ++#define MCA_REG_ADC_VAL_H_13 0x47f ++#define MCA_REG_ADC_VAL_L_14 0x480 ++#define MCA_REG_ADC_VAL_H_14 0x481 ++#define MCA_REG_ADC_VAL_L_15 0x482 ++#define MCA_REG_ADC_VAL_H_15 0x483 ++#define MCA_REG_ADC_VAL_L_16 0x484 ++#define MCA_REG_ADC_VAL_H_16 0x485 ++#define MCA_REG_ADC_VAL_L_17 0x486 ++#define MCA_REG_ADC_VAL_H_17 0x487 ++#define MCA_REG_ADC_VAL_L_18 0x488 ++#define MCA_REG_ADC_VAL_H_18 0x489 ++#define MCA_REG_ADC_VAL_L_19 0x48a ++#define MCA_REG_ADC_VAL_H_19 0x48b ++#define MCA_REG_ADC_VAL_L_20 0x48c ++#define MCA_REG_ADC_VAL_H_20 0x48d ++#define MCA_REG_ADC_VAL_L_21 0x48e ++#define MCA_REG_ADC_VAL_H_21 0x48f ++#define MCA_REG_ADC_VAL_L_22 0x490 ++#define MCA_REG_ADC_VAL_H_22 0x491 ++#define MCA_REG_ADC_VAL_L_23 0x492 ++#define MCA_REG_ADC_VAL_H_23 0x493 ++#define MCA_REG_ADC_VAL_L_24 0x494 ++#define MCA_REG_ADC_VAL_H_24 0x495 ++#define MCA_REG_ADC_VAL_L_25 0x496 ++#define MCA_REG_ADC_VAL_H_25 0x497 ++#define MCA_REG_ADC_VAL_L_26 0x498 ++#define MCA_REG_ADC_VAL_H_26 0x499 ++#define MCA_REG_ADC_VAL_L_27 0x49a ++#define MCA_REG_ADC_VAL_H_27 0x49b ++#define MCA_REG_ADC_VAL_L_28 0x49c ++#define MCA_REG_ADC_VAL_H_28 0x49d ++#define MCA_REG_ADC_VAL_L_29 0x49e ++#define MCA_REG_ADC_VAL_H_29 0x49f ++#define MCA_REG_ADC_VAL_L_30 0x4a0 ++#define MCA_REG_ADC_VAL_H_30 0x4a1 ++#define MCA_REG_ADC_VAL_L_31 0x4a2 ++#define MCA_REG_ADC_VAL_H_31 0x4a3 ++ ++#define MCA_REG_ADC_SAMPLES_CNT_0 0x04a4 ++#define MCA_REG_ADC_SAMPLES_CNT_1 0x04a5 ++#define MCA_REG_ADC_SAMPLES_CNT_2 0x04a6 ++#define MCA_REG_ADC_SAMPLES_CNT_3 0x04a7 ++#define MCA_REG_ADC_SAMPLES_CNT_4 0x04a8 ++#define MCA_REG_ADC_SAMPLES_CNT_5 0x04a9 ++#define MCA_REG_ADC_SAMPLES_CNT_6 0x04aa ++#define MCA_REG_ADC_SAMPLES_CNT_7 0x04ab ++#define MCA_REG_ADC_SAMPLES_CNT_8 0x04ac ++#define MCA_REG_ADC_SAMPLES_CNT_9 0x04ad ++#define MCA_REG_ADC_SAMPLES_CNT_10 0x04ae ++#define MCA_REG_ADC_SAMPLES_CNT_11 0x04af ++#define MCA_REG_ADC_SAMPLES_CNT_12 0x04b0 ++#define MCA_REG_ADC_SAMPLES_CNT_13 0x04b1 ++#define MCA_REG_ADC_SAMPLES_CNT_14 0x04b2 ++#define MCA_REG_ADC_SAMPLES_CNT_15 0x04b3 ++#define MCA_REG_ADC_SAMPLES_CNT_16 0x04b4 ++#define MCA_REG_ADC_SAMPLES_CNT_17 0x04b5 ++#define MCA_REG_ADC_SAMPLES_CNT_18 0x04b6 ++#define MCA_REG_ADC_SAMPLES_CNT_19 0x04b7 ++#define MCA_REG_ADC_SAMPLES_CNT_20 0x04b8 ++#define MCA_REG_ADC_SAMPLES_CNT_21 0x04b9 ++#define MCA_REG_ADC_SAMPLES_CNT_22 0x04ba ++#define MCA_REG_ADC_SAMPLES_CNT_23 0x04bb ++#define MCA_REG_ADC_SAMPLES_CNT_24 0x04bc ++#define MCA_REG_ADC_SAMPLES_CNT_25 0x04bd ++#define MCA_REG_ADC_SAMPLES_CNT_26 0x04be ++#define MCA_REG_ADC_SAMPLES_CNT_27 0x04bf ++#define MCA_REG_ADC_SAMPLES_CNT_28 0x04c0 ++#define MCA_REG_ADC_SAMPLES_CNT_29 0x04c1 ++#define MCA_REG_ADC_SAMPLES_CNT_30 0x04c2 ++#define MCA_REG_ADC_SAMPLES_CNT_31 0x04c3 ++ ++/* EP5, ADCs Thresholds (0 - 31) */ ++#define MCA_REG_ADC_THRESH_LO_L_0 0x0500 ++#define MCA_REG_ADC_THRESH_LO_H_0 0x0501 ++#define MCA_REG_ADC_THRESH_LO_L_1 0x0502 ++#define MCA_REG_ADC_THRESH_LO_H_1 0x0503 ++#define MCA_REG_ADC_THRESH_LO_L_2 0x0504 ++#define MCA_REG_ADC_THRESH_LO_H_2 0x0505 ++#define MCA_REG_ADC_THRESH_LO_L_3 0x0506 ++#define MCA_REG_ADC_THRESH_LO_H_3 0x0507 ++#define MCA_REG_ADC_THRESH_LO_L_4 0x0508 ++#define MCA_REG_ADC_THRESH_LO_H_4 0x0509 ++#define MCA_REG_ADC_THRESH_LO_L_5 0x050a ++#define MCA_REG_ADC_THRESH_LO_H_5 0x050b ++#define MCA_REG_ADC_THRESH_LO_L_6 0x050c ++#define MCA_REG_ADC_THRESH_LO_H_6 0x050d ++#define MCA_REG_ADC_THRESH_LO_L_7 0x050e ++#define MCA_REG_ADC_THRESH_LO_H_7 0x050f ++#define MCA_REG_ADC_THRESH_LO_L_8 0x0510 ++#define MCA_REG_ADC_THRESH_LO_H_8 0x0511 ++#define MCA_REG_ADC_THRESH_LO_L_9 0x0512 ++#define MCA_REG_ADC_THRESH_LO_H_9 0x0513 ++#define MCA_REG_ADC_THRESH_LO_L_10 0x0514 ++#define MCA_REG_ADC_THRESH_LO_H_10 0x0515 ++#define MCA_REG_ADC_THRESH_LO_L_11 0x0516 ++#define MCA_REG_ADC_THRESH_LO_H_11 0x0517 ++#define MCA_REG_ADC_THRESH_LO_L_12 0x0518 ++#define MCA_REG_ADC_THRESH_LO_H_12 0x0519 ++#define MCA_REG_ADC_THRESH_LO_L_13 0x051a ++#define MCA_REG_ADC_THRESH_LO_H_13 0x051b ++#define MCA_REG_ADC_THRESH_LO_L_14 0x051c ++#define MCA_REG_ADC_THRESH_LO_H_14 0x051d ++#define MCA_REG_ADC_THRESH_LO_L_15 0x051e ++#define MCA_REG_ADC_THRESH_LO_H_15 0x051f ++#define MCA_REG_ADC_THRESH_LO_L_16 0x0520 ++#define MCA_REG_ADC_THRESH_LO_H_16 0x0521 ++#define MCA_REG_ADC_THRESH_LO_L_17 0x0522 ++#define MCA_REG_ADC_THRESH_LO_H_17 0x0523 ++#define MCA_REG_ADC_THRESH_LO_L_18 0x0524 ++#define MCA_REG_ADC_THRESH_LO_H_18 0x0525 ++#define MCA_REG_ADC_THRESH_LO_L_19 0x0526 ++#define MCA_REG_ADC_THRESH_LO_H_19 0x0527 ++#define MCA_REG_ADC_THRESH_LO_L_20 0x0528 ++#define MCA_REG_ADC_THRESH_LO_H_20 0x0529 ++#define MCA_REG_ADC_THRESH_LO_L_21 0x052a ++#define MCA_REG_ADC_THRESH_LO_H_21 0x052b ++#define MCA_REG_ADC_THRESH_LO_L_22 0x052c ++#define MCA_REG_ADC_THRESH_LO_H_22 0x052d ++#define MCA_REG_ADC_THRESH_LO_L_23 0x052e ++#define MCA_REG_ADC_THRESH_LO_H_23 0x052f ++#define MCA_REG_ADC_THRESH_LO_L_24 0x0530 ++#define MCA_REG_ADC_THRESH_LO_H_24 0x0531 ++#define MCA_REG_ADC_THRESH_LO_L_25 0x0532 ++#define MCA_REG_ADC_THRESH_LO_H_25 0x0533 ++#define MCA_REG_ADC_THRESH_LO_L_26 0x0534 ++#define MCA_REG_ADC_THRESH_LO_H_26 0x0535 ++#define MCA_REG_ADC_THRESH_LO_L_27 0x0536 ++#define MCA_REG_ADC_THRESH_LO_H_27 0x0537 ++#define MCA_REG_ADC_THRESH_LO_L_28 0x0538 ++#define MCA_REG_ADC_THRESH_LO_H_28 0x0539 ++#define MCA_REG_ADC_THRESH_LO_L_29 0x053a ++#define MCA_REG_ADC_THRESH_LO_H_29 0x053b ++#define MCA_REG_ADC_THRESH_LO_L_30 0x053c ++#define MCA_REG_ADC_THRESH_LO_H_30 0x053d ++#define MCA_REG_ADC_THRESH_LO_L_31 0x053e ++#define MCA_REG_ADC_THRESH_LO_H_31 0x053f ++ ++#define MCA_REG_ADC_THRESH_HI_L_0 0x0540 ++#define MCA_REG_ADC_THRESH_HI_H_0 0x0541 ++#define MCA_REG_ADC_THRESH_HI_L_1 0x0542 ++#define MCA_REG_ADC_THRESH_HI_H_1 0x0543 ++#define MCA_REG_ADC_THRESH_HI_L_2 0x0544 ++#define MCA_REG_ADC_THRESH_HI_H_2 0x0545 ++#define MCA_REG_ADC_THRESH_HI_L_3 0x0546 ++#define MCA_REG_ADC_THRESH_HI_H_3 0x0547 ++#define MCA_REG_ADC_THRESH_HI_L_4 0x0548 ++#define MCA_REG_ADC_THRESH_HI_H_4 0x0549 ++#define MCA_REG_ADC_THRESH_HI_L_5 0x054a ++#define MCA_REG_ADC_THRESH_HI_H_5 0x054b ++#define MCA_REG_ADC_THRESH_HI_L_6 0x054c ++#define MCA_REG_ADC_THRESH_HI_H_6 0x054d ++#define MCA_REG_ADC_THRESH_HI_L_7 0x054e ++#define MCA_REG_ADC_THRESH_HI_H_7 0x054f ++#define MCA_REG_ADC_THRESH_HI_L_8 0x0550 ++#define MCA_REG_ADC_THRESH_HI_H_8 0x0551 ++#define MCA_REG_ADC_THRESH_HI_L_9 0x0552 ++#define MCA_REG_ADC_THRESH_HI_H_9 0x0553 ++#define MCA_REG_ADC_THRESH_HI_L_10 0x0554 ++#define MCA_REG_ADC_THRESH_HI_H_10 0x0555 ++#define MCA_REG_ADC_THRESH_HI_L_11 0x0556 ++#define MCA_REG_ADC_THRESH_HI_H_11 0x0557 ++#define MCA_REG_ADC_THRESH_HI_L_12 0x0558 ++#define MCA_REG_ADC_THRESH_HI_H_12 0x0559 ++#define MCA_REG_ADC_THRESH_HI_L_13 0x055a ++#define MCA_REG_ADC_THRESH_HI_H_13 0x055b ++#define MCA_REG_ADC_THRESH_HI_L_14 0x055c ++#define MCA_REG_ADC_THRESH_HI_H_14 0x055d ++#define MCA_REG_ADC_THRESH_HI_L_15 0x055e ++#define MCA_REG_ADC_THRESH_HI_H_15 0x055f ++#define MCA_REG_ADC_THRESH_HI_L_16 0x0560 ++#define MCA_REG_ADC_THRESH_HI_H_16 0x0561 ++#define MCA_REG_ADC_THRESH_HI_L_17 0x0562 ++#define MCA_REG_ADC_THRESH_HI_H_17 0x0563 ++#define MCA_REG_ADC_THRESH_HI_L_18 0x0564 ++#define MCA_REG_ADC_THRESH_HI_H_18 0x0565 ++#define MCA_REG_ADC_THRESH_HI_L_19 0x0566 ++#define MCA_REG_ADC_THRESH_HI_H_19 0x0567 ++#define MCA_REG_ADC_THRESH_HI_L_20 0x0568 ++#define MCA_REG_ADC_THRESH_HI_H_20 0x0569 ++#define MCA_REG_ADC_THRESH_HI_L_21 0x056a ++#define MCA_REG_ADC_THRESH_HI_H_21 0x056b ++#define MCA_REG_ADC_THRESH_HI_L_22 0x056c ++#define MCA_REG_ADC_THRESH_HI_H_22 0x056d ++#define MCA_REG_ADC_THRESH_HI_L_23 0x056e ++#define MCA_REG_ADC_THRESH_HI_H_23 0x056f ++#define MCA_REG_ADC_THRESH_HI_L_24 0x0570 ++#define MCA_REG_ADC_THRESH_HI_H_24 0x0571 ++#define MCA_REG_ADC_THRESH_HI_L_25 0x0572 ++#define MCA_REG_ADC_THRESH_HI_H_25 0x0573 ++#define MCA_REG_ADC_THRESH_HI_L_26 0x0574 ++#define MCA_REG_ADC_THRESH_HI_H_26 0x0575 ++#define MCA_REG_ADC_THRESH_HI_L_27 0x0576 ++#define MCA_REG_ADC_THRESH_HI_H_27 0x0577 ++#define MCA_REG_ADC_THRESH_HI_L_28 0x0578 ++#define MCA_REG_ADC_THRESH_HI_H_28 0x0579 ++#define MCA_REG_ADC_THRESH_HI_L_29 0x057a ++#define MCA_REG_ADC_THRESH_HI_H_29 0x057b ++#define MCA_REG_ADC_THRESH_HI_L_30 0x057c ++#define MCA_REG_ADC_THRESH_HI_H_30 0x057d ++#define MCA_REG_ADC_THRESH_HI_L_31 0x057e ++#define MCA_REG_ADC_THRESH_HI_H_31 0x057f ++ ++#define MCA_REG_ADC_TICKS_L_0 0x0580 ++#define MCA_REG_ADC_TICKS_H_0 0x0581 ++#define MCA_REG_ADC_TICKS_L_1 0x0582 ++#define MCA_REG_ADC_TICKS_H_1 0x0583 ++#define MCA_REG_ADC_TICKS_L_2 0x0584 ++#define MCA_REG_ADC_TICKS_H_2 0x0585 ++#define MCA_REG_ADC_TICKS_L_3 0x0586 ++#define MCA_REG_ADC_TICKS_H_3 0x0587 ++#define MCA_REG_ADC_TICKS_L_4 0x0588 ++#define MCA_REG_ADC_TICKS_H_4 0x0589 ++#define MCA_REG_ADC_TICKS_L_5 0x058a ++#define MCA_REG_ADC_TICKS_H_5 0x058b ++#define MCA_REG_ADC_TICKS_L_6 0x058c ++#define MCA_REG_ADC_TICKS_H_6 0x058d ++#define MCA_REG_ADC_TICKS_L_7 0x058e ++#define MCA_REG_ADC_TICKS_H_7 0x058f ++#define MCA_REG_ADC_TICKS_L_8 0x0590 ++#define MCA_REG_ADC_TICKS_H_8 0x0591 ++#define MCA_REG_ADC_TICKS_L_9 0x0592 ++#define MCA_REG_ADC_TICKS_H_9 0x0593 ++#define MCA_REG_ADC_TICKS_L_10 0x0594 ++#define MCA_REG_ADC_TICKS_H_10 0x0595 ++#define MCA_REG_ADC_TICKS_L_11 0x0596 ++#define MCA_REG_ADC_TICKS_H_11 0x0597 ++#define MCA_REG_ADC_TICKS_L_12 0x0598 ++#define MCA_REG_ADC_TICKS_H_12 0x0599 ++#define MCA_REG_ADC_TICKS_L_13 0x059a ++#define MCA_REG_ADC_TICKS_H_13 0x059b ++#define MCA_REG_ADC_TICKS_L_14 0x059c ++#define MCA_REG_ADC_TICKS_H_14 0x059d ++#define MCA_REG_ADC_TICKS_L_15 0x059e ++#define MCA_REG_ADC_TICKS_H_15 0x059f ++#define MCA_REG_ADC_TICKS_L_16 0x05a0 ++#define MCA_REG_ADC_TICKS_H_16 0x05a1 ++#define MCA_REG_ADC_TICKS_L_17 0x05a2 ++#define MCA_REG_ADC_TICKS_H_17 0x05a3 ++#define MCA_REG_ADC_TICKS_L_18 0x05a4 ++#define MCA_REG_ADC_TICKS_H_18 0x05a5 ++#define MCA_REG_ADC_TICKS_L_19 0x05a6 ++#define MCA_REG_ADC_TICKS_H_19 0x05a7 ++#define MCA_REG_ADC_TICKS_L_20 0x05a8 ++#define MCA_REG_ADC_TICKS_H_20 0x05a9 ++#define MCA_REG_ADC_TICKS_L_21 0x05aa ++#define MCA_REG_ADC_TICKS_H_21 0x05ab ++#define MCA_REG_ADC_TICKS_L_22 0x05ac ++#define MCA_REG_ADC_TICKS_H_22 0x05ad ++#define MCA_REG_ADC_TICKS_L_23 0x05ae ++#define MCA_REG_ADC_TICKS_H_23 0x05af ++#define MCA_REG_ADC_TICKS_L_24 0x05b0 ++#define MCA_REG_ADC_TICKS_H_24 0x05b1 ++#define MCA_REG_ADC_TICKS_L_25 0x05b2 ++#define MCA_REG_ADC_TICKS_H_25 0x05b3 ++#define MCA_REG_ADC_TICKS_L_26 0x05b4 ++#define MCA_REG_ADC_TICKS_H_26 0x05b5 ++#define MCA_REG_ADC_TICKS_L_27 0x05b6 ++#define MCA_REG_ADC_TICKS_H_27 0x05b7 ++#define MCA_REG_ADC_TICKS_L_28 0x05b8 ++#define MCA_REG_ADC_TICKS_H_28 0x05b9 ++#define MCA_REG_ADC_TICKS_L_29 0x05ba ++#define MCA_REG_ADC_TICKS_H_29 0x05bb ++#define MCA_REG_ADC_TICKS_L_30 0x05bc ++#define MCA_REG_ADC_TICKS_H_30 0x05bd ++#define MCA_REG_ADC_TICKS_L_31 0x05be ++#define MCA_REG_ADC_TICKS_H_31 0x05bf ++ ++/* EP4, ADCs Configuration (32 - 63) */ ++#define MCA_REG_ADC_CFG0_32 0x0604 ++#define MCA_REG_ADC_CFG0_33 0x0605 ++#define MCA_REG_ADC_CFG0_34 0x0606 ++#define MCA_REG_ADC_CFG0_35 0x0607 ++#define MCA_REG_ADC_CFG0_36 0x0608 ++#define MCA_REG_ADC_CFG0_37 0x0609 ++#define MCA_REG_ADC_CFG0_38 0x060a ++#define MCA_REG_ADC_CFG0_39 0x060b ++#define MCA_REG_ADC_CFG0_40 0x060c ++#define MCA_REG_ADC_CFG0_41 0x060d ++#define MCA_REG_ADC_CFG0_42 0x060e ++#define MCA_REG_ADC_CFG0_43 0x060f ++#define MCA_REG_ADC_CFG0_44 0x0610 ++#define MCA_REG_ADC_CFG0_45 0x0611 ++#define MCA_REG_ADC_CFG0_46 0x0612 ++#define MCA_REG_ADC_CFG0_47 0x0613 ++#define MCA_REG_ADC_CFG0_48 0x0614 ++#define MCA_REG_ADC_CFG0_49 0x0615 ++#define MCA_REG_ADC_CFG0_50 0x0616 ++#define MCA_REG_ADC_CFG0_51 0x0617 ++#define MCA_REG_ADC_CFG0_52 0x0618 ++#define MCA_REG_ADC_CFG0_53 0x0619 ++#define MCA_REG_ADC_CFG0_54 0x061a ++#define MCA_REG_ADC_CFG0_55 0x061b ++#define MCA_REG_ADC_CFG0_56 0x061c ++#define MCA_REG_ADC_CFG0_57 0x061d ++#define MCA_REG_ADC_CFG0_58 0x061e ++#define MCA_REG_ADC_CFG0_59 0x061f ++#define MCA_REG_ADC_CFG0_60 0x0620 ++#define MCA_REG_ADC_CFG0_61 0x0621 ++#define MCA_REG_ADC_CFG0_62 0x0622 ++#define MCA_REG_ADC_CFG0_63 0x0623 ++ ++#define MCA_REG_ADC_CFG1_32 0x0644 ++#define MCA_REG_ADC_CFG1_33 0x0645 ++#define MCA_REG_ADC_CFG1_34 0x0646 ++#define MCA_REG_ADC_CFG1_35 0x0647 ++#define MCA_REG_ADC_CFG1_36 0x0648 ++#define MCA_REG_ADC_CFG1_37 0x0649 ++#define MCA_REG_ADC_CFG1_38 0x064a ++#define MCA_REG_ADC_CFG1_39 0x064b ++#define MCA_REG_ADC_CFG1_40 0x064c ++#define MCA_REG_ADC_CFG1_41 0x064d ++#define MCA_REG_ADC_CFG1_42 0x064e ++#define MCA_REG_ADC_CFG1_43 0x064f ++#define MCA_REG_ADC_CFG1_44 0x0650 ++#define MCA_REG_ADC_CFG1_45 0x0651 ++#define MCA_REG_ADC_CFG1_46 0x0652 ++#define MCA_REG_ADC_CFG1_47 0x0653 ++#define MCA_REG_ADC_CFG1_48 0x0654 ++#define MCA_REG_ADC_CFG1_49 0x0655 ++#define MCA_REG_ADC_CFG1_50 0x0656 ++#define MCA_REG_ADC_CFG1_51 0x0657 ++#define MCA_REG_ADC_CFG1_52 0x0658 ++#define MCA_REG_ADC_CFG1_53 0x0659 ++#define MCA_REG_ADC_CFG1_54 0x065a ++#define MCA_REG_ADC_CFG1_55 0x065b ++#define MCA_REG_ADC_CFG1_56 0x065c ++#define MCA_REG_ADC_CFG1_57 0x065d ++#define MCA_REG_ADC_CFG1_58 0x065e ++#define MCA_REG_ADC_CFG1_59 0x065f ++#define MCA_REG_ADC_CFG1_60 0x0660 ++#define MCA_REG_ADC_CFG1_61 0x0661 ++#define MCA_REG_ADC_CFG1_62 0x0662 ++#define MCA_REG_ADC_CFG1_63 0x0663 ++ ++#define MCA_REG_ADC_CFG2_32 0x0664 ++#define MCA_REG_ADC_CFG2_33 0x0665 ++#define MCA_REG_ADC_CFG2_34 0x0666 ++#define MCA_REG_ADC_CFG2_35 0x0667 ++#define MCA_REG_ADC_CFG2_36 0x0668 ++#define MCA_REG_ADC_CFG2_37 0x0669 ++#define MCA_REG_ADC_CFG2_38 0x066a ++#define MCA_REG_ADC_CFG2_39 0x066b ++#define MCA_REG_ADC_CFG2_40 0x066c ++#define MCA_REG_ADC_CFG2_41 0x066d ++#define MCA_REG_ADC_CFG2_42 0x066e ++#define MCA_REG_ADC_CFG2_43 0x066f ++#define MCA_REG_ADC_CFG2_44 0x0670 ++#define MCA_REG_ADC_CFG2_45 0x0671 ++#define MCA_REG_ADC_CFG2_46 0x0672 ++#define MCA_REG_ADC_CFG2_47 0x0673 ++#define MCA_REG_ADC_CFG2_48 0x0674 ++#define MCA_REG_ADC_CFG2_49 0x0675 ++#define MCA_REG_ADC_CFG2_50 0x0676 ++#define MCA_REG_ADC_CFG2_51 0x0677 ++#define MCA_REG_ADC_CFG2_52 0x0678 ++#define MCA_REG_ADC_CFG2_53 0x0679 ++#define MCA_REG_ADC_CFG2_54 0x067a ++#define MCA_REG_ADC_CFG2_55 0x067b ++#define MCA_REG_ADC_CFG2_56 0x067c ++#define MCA_REG_ADC_CFG2_57 0x067d ++#define MCA_REG_ADC_CFG2_58 0x067e ++#define MCA_REG_ADC_CFG2_59 0x067f ++#define MCA_REG_ADC_CFG2_60 0x0680 ++#define MCA_REG_ADC_CFG2_61 0x0681 ++#define MCA_REG_ADC_CFG2_62 0x0682 ++#define MCA_REG_ADC_CFG2_63 0x0683 ++ ++#define MCA_REG_ADC_SAMPLES_CNT_32 0x06a4 ++#define MCA_REG_ADC_SAMPLES_CNT_33 0x06a5 ++#define MCA_REG_ADC_SAMPLES_CNT_34 0x06a6 ++#define MCA_REG_ADC_SAMPLES_CNT_35 0x06a7 ++#define MCA_REG_ADC_SAMPLES_CNT_36 0x06a8 ++#define MCA_REG_ADC_SAMPLES_CNT_37 0x06a9 ++#define MCA_REG_ADC_SAMPLES_CNT_38 0x06aa ++#define MCA_REG_ADC_SAMPLES_CNT_39 0x06ab ++#define MCA_REG_ADC_SAMPLES_CNT_40 0x06ac ++#define MCA_REG_ADC_SAMPLES_CNT_41 0x06ad ++#define MCA_REG_ADC_SAMPLES_CNT_42 0x06ae ++#define MCA_REG_ADC_SAMPLES_CNT_43 0x06af ++#define MCA_REG_ADC_SAMPLES_CNT_44 0x06b0 ++#define MCA_REG_ADC_SAMPLES_CNT_45 0x06b1 ++#define MCA_REG_ADC_SAMPLES_CNT_46 0x06b2 ++#define MCA_REG_ADC_SAMPLES_CNT_47 0x06b3 ++#define MCA_REG_ADC_SAMPLES_CNT_48 0x06b4 ++#define MCA_REG_ADC_SAMPLES_CNT_49 0x06b5 ++#define MCA_REG_ADC_SAMPLES_CNT_50 0x06b6 ++#define MCA_REG_ADC_SAMPLES_CNT_51 0x06b7 ++#define MCA_REG_ADC_SAMPLES_CNT_52 0x06b8 ++#define MCA_REG_ADC_SAMPLES_CNT_53 0x06b9 ++#define MCA_REG_ADC_SAMPLES_CNT_54 0x06ba ++#define MCA_REG_ADC_SAMPLES_CNT_55 0x06bb ++#define MCA_REG_ADC_SAMPLES_CNT_56 0x06bc ++#define MCA_REG_ADC_SAMPLES_CNT_57 0x06bd ++#define MCA_REG_ADC_SAMPLES_CNT_58 0x06be ++#define MCA_REG_ADC_SAMPLES_CNT_59 0x06bf ++#define MCA_REG_ADC_SAMPLES_CNT_60 0x06c0 ++#define MCA_REG_ADC_SAMPLES_CNT_61 0x06c1 ++#define MCA_REG_ADC_SAMPLES_CNT_62 0x06c2 ++#define MCA_REG_ADC_SAMPLES_CNT_63 0x06c3 ++ ++/* EP7, ADCs Thresholds (32 - 63) */ ++#define MCA_REG_ADC_THRESH_LO_L_32 0x0700 ++#define MCA_REG_ADC_THRESH_LO_H_32 0x0701 ++#define MCA_REG_ADC_THRESH_LO_L_33 0x0702 ++#define MCA_REG_ADC_THRESH_LO_H_33 0x0703 ++#define MCA_REG_ADC_THRESH_LO_L_34 0x0704 ++#define MCA_REG_ADC_THRESH_LO_H_34 0x0705 ++#define MCA_REG_ADC_THRESH_LO_L_35 0x0706 ++#define MCA_REG_ADC_THRESH_LO_H_35 0x0707 ++#define MCA_REG_ADC_THRESH_LO_L_36 0x0708 ++#define MCA_REG_ADC_THRESH_LO_H_36 0x0709 ++#define MCA_REG_ADC_THRESH_LO_L_37 0x070a ++#define MCA_REG_ADC_THRESH_LO_H_37 0x070b ++#define MCA_REG_ADC_THRESH_LO_L_38 0x070c ++#define MCA_REG_ADC_THRESH_LO_H_38 0x070d ++#define MCA_REG_ADC_THRESH_LO_L_39 0x070e ++#define MCA_REG_ADC_THRESH_LO_H_39 0x070f ++#define MCA_REG_ADC_THRESH_LO_L_40 0x0710 ++#define MCA_REG_ADC_THRESH_LO_H_40 0x0711 ++#define MCA_REG_ADC_THRESH_LO_L_41 0x0712 ++#define MCA_REG_ADC_THRESH_LO_H_41 0x0713 ++#define MCA_REG_ADC_THRESH_LO_L_42 0x0714 ++#define MCA_REG_ADC_THRESH_LO_H_42 0x0715 ++#define MCA_REG_ADC_THRESH_LO_L_43 0x0716 ++#define MCA_REG_ADC_THRESH_LO_H_43 0x0717 ++#define MCA_REG_ADC_THRESH_LO_L_44 0x0718 ++#define MCA_REG_ADC_THRESH_LO_H_44 0x0719 ++#define MCA_REG_ADC_THRESH_LO_L_45 0x071a ++#define MCA_REG_ADC_THRESH_LO_H_45 0x071b ++#define MCA_REG_ADC_THRESH_LO_L_46 0x071c ++#define MCA_REG_ADC_THRESH_LO_H_46 0x071d ++#define MCA_REG_ADC_THRESH_LO_L_47 0x071e ++#define MCA_REG_ADC_THRESH_LO_H_47 0x071f ++#define MCA_REG_ADC_THRESH_LO_L_48 0x0720 ++#define MCA_REG_ADC_THRESH_LO_H_48 0x0721 ++#define MCA_REG_ADC_THRESH_LO_L_49 0x0722 ++#define MCA_REG_ADC_THRESH_LO_H_49 0x0723 ++#define MCA_REG_ADC_THRESH_LO_L_50 0x0724 ++#define MCA_REG_ADC_THRESH_LO_H_50 0x0725 ++#define MCA_REG_ADC_THRESH_LO_L_51 0x0726 ++#define MCA_REG_ADC_THRESH_LO_H_51 0x0727 ++#define MCA_REG_ADC_THRESH_LO_L_52 0x0728 ++#define MCA_REG_ADC_THRESH_LO_H_52 0x0729 ++#define MCA_REG_ADC_THRESH_LO_L_53 0x072a ++#define MCA_REG_ADC_THRESH_LO_H_53 0x072b ++#define MCA_REG_ADC_THRESH_LO_L_54 0x072c ++#define MCA_REG_ADC_THRESH_LO_H_54 0x072d ++#define MCA_REG_ADC_THRESH_LO_L_55 0x072e ++#define MCA_REG_ADC_THRESH_LO_H_55 0x072f ++#define MCA_REG_ADC_THRESH_LO_L_56 0x0730 ++#define MCA_REG_ADC_THRESH_LO_H_56 0x0731 ++#define MCA_REG_ADC_THRESH_LO_L_57 0x0732 ++#define MCA_REG_ADC_THRESH_LO_H_57 0x0733 ++#define MCA_REG_ADC_THRESH_LO_L_58 0x0734 ++#define MCA_REG_ADC_THRESH_LO_H_58 0x0735 ++#define MCA_REG_ADC_THRESH_LO_L_59 0x0736 ++#define MCA_REG_ADC_THRESH_LO_H_59 0x0737 ++#define MCA_REG_ADC_THRESH_LO_L_60 0x0738 ++#define MCA_REG_ADC_THRESH_LO_H_60 0x0739 ++#define MCA_REG_ADC_THRESH_LO_L_61 0x073a ++#define MCA_REG_ADC_THRESH_LO_H_61 0x073b ++#define MCA_REG_ADC_THRESH_LO_L_62 0x073c ++#define MCA_REG_ADC_THRESH_LO_H_62 0x073d ++#define MCA_REG_ADC_THRESH_LO_L_63 0x073e ++#define MCA_REG_ADC_THRESH_LO_H_63 0x073f ++ ++#define MCA_REG_ADC_THRESH_HI_L_32 0x0740 ++#define MCA_REG_ADC_THRESH_HI_H_32 0x0741 ++#define MCA_REG_ADC_THRESH_HI_L_33 0x0742 ++#define MCA_REG_ADC_THRESH_HI_H_33 0x0743 ++#define MCA_REG_ADC_THRESH_HI_L_34 0x0744 ++#define MCA_REG_ADC_THRESH_HI_H_34 0x0745 ++#define MCA_REG_ADC_THRESH_HI_L_35 0x0746 ++#define MCA_REG_ADC_THRESH_HI_H_35 0x0747 ++#define MCA_REG_ADC_THRESH_HI_L_36 0x0748 ++#define MCA_REG_ADC_THRESH_HI_H_36 0x0749 ++#define MCA_REG_ADC_THRESH_HI_L_37 0x074a ++#define MCA_REG_ADC_THRESH_HI_H_37 0x074b ++#define MCA_REG_ADC_THRESH_HI_L_38 0x074c ++#define MCA_REG_ADC_THRESH_HI_H_38 0x074d ++#define MCA_REG_ADC_THRESH_HI_L_39 0x074e ++#define MCA_REG_ADC_THRESH_HI_H_39 0x074f ++#define MCA_REG_ADC_THRESH_HI_L_40 0x0750 ++#define MCA_REG_ADC_THRESH_HI_H_40 0x0751 ++#define MCA_REG_ADC_THRESH_HI_L_41 0x0752 ++#define MCA_REG_ADC_THRESH_HI_H_41 0x0753 ++#define MCA_REG_ADC_THRESH_HI_L_42 0x0754 ++#define MCA_REG_ADC_THRESH_HI_H_42 0x0755 ++#define MCA_REG_ADC_THRESH_HI_L_43 0x0756 ++#define MCA_REG_ADC_THRESH_HI_H_43 0x0757 ++#define MCA_REG_ADC_THRESH_HI_L_44 0x0758 ++#define MCA_REG_ADC_THRESH_HI_H_44 0x0759 ++#define MCA_REG_ADC_THRESH_HI_L_45 0x075a ++#define MCA_REG_ADC_THRESH_HI_H_45 0x075b ++#define MCA_REG_ADC_THRESH_HI_L_46 0x075c ++#define MCA_REG_ADC_THRESH_HI_H_46 0x075d ++#define MCA_REG_ADC_THRESH_HI_L_47 0x075e ++#define MCA_REG_ADC_THRESH_HI_H_47 0x075f ++#define MCA_REG_ADC_THRESH_HI_L_48 0x0760 ++#define MCA_REG_ADC_THRESH_HI_H_48 0x0761 ++#define MCA_REG_ADC_THRESH_HI_L_49 0x0762 ++#define MCA_REG_ADC_THRESH_HI_H_49 0x0763 ++#define MCA_REG_ADC_THRESH_HI_L_50 0x0764 ++#define MCA_REG_ADC_THRESH_HI_H_50 0x0765 ++#define MCA_REG_ADC_THRESH_HI_L_51 0x0766 ++#define MCA_REG_ADC_THRESH_HI_H_51 0x0767 ++#define MCA_REG_ADC_THRESH_HI_L_52 0x0768 ++#define MCA_REG_ADC_THRESH_HI_H_52 0x0769 ++#define MCA_REG_ADC_THRESH_HI_L_53 0x076a ++#define MCA_REG_ADC_THRESH_HI_H_53 0x076b ++#define MCA_REG_ADC_THRESH_HI_L_54 0x076c ++#define MCA_REG_ADC_THRESH_HI_H_54 0x076d ++#define MCA_REG_ADC_THRESH_HI_L_55 0x076e ++#define MCA_REG_ADC_THRESH_HI_H_55 0x076f ++#define MCA_REG_ADC_THRESH_HI_L_56 0x0770 ++#define MCA_REG_ADC_THRESH_HI_H_56 0x0771 ++#define MCA_REG_ADC_THRESH_HI_L_57 0x0772 ++#define MCA_REG_ADC_THRESH_HI_H_57 0x0773 ++#define MCA_REG_ADC_THRESH_HI_L_58 0x0774 ++#define MCA_REG_ADC_THRESH_HI_H_58 0x0775 ++#define MCA_REG_ADC_THRESH_HI_L_59 0x0776 ++#define MCA_REG_ADC_THRESH_HI_H_59 0x0777 ++#define MCA_REG_ADC_THRESH_HI_L_60 0x0778 ++#define MCA_REG_ADC_THRESH_HI_H_60 0x0779 ++#define MCA_REG_ADC_THRESH_HI_L_61 0x077a ++#define MCA_REG_ADC_THRESH_HI_H_61 0x077b ++#define MCA_REG_ADC_THRESH_HI_L_62 0x077c ++#define MCA_REG_ADC_THRESH_HI_H_62 0x077d ++#define MCA_REG_ADC_THRESH_HI_L_63 0x077e ++#define MCA_REG_ADC_THRESH_HI_H_63 0x077f ++ ++#define MCA_REG_ADC_TICKS_L_32 0x0780 ++#define MCA_REG_ADC_TICKS_H_32 0x0781 ++#define MCA_REG_ADC_TICKS_L_33 0x0782 ++#define MCA_REG_ADC_TICKS_H_33 0x0783 ++#define MCA_REG_ADC_TICKS_L_34 0x0784 ++#define MCA_REG_ADC_TICKS_H_34 0x0785 ++#define MCA_REG_ADC_TICKS_L_35 0x0786 ++#define MCA_REG_ADC_TICKS_H_35 0x0787 ++#define MCA_REG_ADC_TICKS_L_36 0x0788 ++#define MCA_REG_ADC_TICKS_H_36 0x0789 ++#define MCA_REG_ADC_TICKS_L_37 0x078a ++#define MCA_REG_ADC_TICKS_H_37 0x078b ++#define MCA_REG_ADC_TICKS_L_38 0x078c ++#define MCA_REG_ADC_TICKS_H_38 0x078d ++#define MCA_REG_ADC_TICKS_L_39 0x078e ++#define MCA_REG_ADC_TICKS_H_39 0x078f ++#define MCA_REG_ADC_TICKS_L_40 0x0790 ++#define MCA_REG_ADC_TICKS_H_40 0x0791 ++#define MCA_REG_ADC_TICKS_L_41 0x0792 ++#define MCA_REG_ADC_TICKS_H_41 0x0793 ++#define MCA_REG_ADC_TICKS_L_42 0x0794 ++#define MCA_REG_ADC_TICKS_H_42 0x0795 ++#define MCA_REG_ADC_TICKS_L_43 0x0796 ++#define MCA_REG_ADC_TICKS_H_43 0x0797 ++#define MCA_REG_ADC_TICKS_L_44 0x0798 ++#define MCA_REG_ADC_TICKS_H_44 0x0799 ++#define MCA_REG_ADC_TICKS_L_45 0x079a ++#define MCA_REG_ADC_TICKS_H_45 0x079b ++#define MCA_REG_ADC_TICKS_L_46 0x079c ++#define MCA_REG_ADC_TICKS_H_46 0x079d ++#define MCA_REG_ADC_TICKS_L_47 0x079e ++#define MCA_REG_ADC_TICKS_H_47 0x079f ++#define MCA_REG_ADC_TICKS_L_48 0x07a0 ++#define MCA_REG_ADC_TICKS_H_48 0x07a1 ++#define MCA_REG_ADC_TICKS_L_49 0x07a2 ++#define MCA_REG_ADC_TICKS_H_49 0x07a3 ++#define MCA_REG_ADC_TICKS_L_50 0x07a4 ++#define MCA_REG_ADC_TICKS_H_50 0x07a5 ++#define MCA_REG_ADC_TICKS_L_51 0x07a6 ++#define MCA_REG_ADC_TICKS_H_51 0x07a7 ++#define MCA_REG_ADC_TICKS_L_52 0x07a8 ++#define MCA_REG_ADC_TICKS_H_52 0x07a9 ++#define MCA_REG_ADC_TICKS_L_53 0x07aa ++#define MCA_REG_ADC_TICKS_H_53 0x07ab ++#define MCA_REG_ADC_TICKS_L_54 0x07ac ++#define MCA_REG_ADC_TICKS_H_54 0x07ad ++#define MCA_REG_ADC_TICKS_L_55 0x07ae ++#define MCA_REG_ADC_TICKS_H_55 0x07af ++#define MCA_REG_ADC_TICKS_L_56 0x07b0 ++#define MCA_REG_ADC_TICKS_H_56 0x07b1 ++#define MCA_REG_ADC_TICKS_L_57 0x07b2 ++#define MCA_REG_ADC_TICKS_H_57 0x07b3 ++#define MCA_REG_ADC_TICKS_L_58 0x07b4 ++#define MCA_REG_ADC_TICKS_H_58 0x07b5 ++#define MCA_REG_ADC_TICKS_L_59 0x07b6 ++#define MCA_REG_ADC_TICKS_H_59 0x07b7 ++#define MCA_REG_ADC_TICKS_L_60 0x07b8 ++#define MCA_REG_ADC_TICKS_H_60 0x07b9 ++#define MCA_REG_ADC_TICKS_L_61 0x07ba ++#define MCA_REG_ADC_TICKS_H_61 0x07bb ++#define MCA_REG_ADC_TICKS_L_62 0x07bc ++#define MCA_REG_ADC_TICKS_H_62 0x07bd ++#define MCA_REG_ADC_TICKS_L_63 0x07be ++#define MCA_REG_ADC_TICKS_H_63 0x07bf ++ ++/* EP8, ADCs Buffered samples */ ++#define MCA_REG_ADC_IRQ_0 0x0800 ++#define MCA_REG_ADC_IRQ_1 0x0801 ++#define MCA_REG_ADC_IRQ_2 0x0802 ++#define MCA_REG_ADC_IRQ_3 0x0803 ++#define MCA_REG_ADC_IRQ_4 0x0804 ++#define MCA_REG_ADC_IRQ_5 0x0805 ++#define MCA_REG_ADC_IRQ_6 0x0806 ++#define MCA_REG_ADC_IRQ_7 0x0807 ++ ++#define MCA_REG_ADC_CFG_0 0x0808 ++#define MCA_REG_ADC_CFG_1 0x0809 ++#define MCA_REG_ADC_CFG_2 0x080A ++ ++#define MCA_REG_ADC_BUFF_CH 0x0810 ++#define MCA_REG_ADC_BUFF_CNT 0x0811 ++#define MCA_REG_ADC_BUFF_SAMPLE_0 0x8010 ++#define MCA_REG_ADC_BUFF_SAMPLE_1 0x8011 ++#define MCA_REG_ADC_BUFF_SAMPLE_2 0x8012 ++#define MCA_REG_ADC_BUFF_SAMPLE_3 0x8013 ++#define MCA_REG_ADC_BUFF_SAMPLE_4 0x8014 ++#define MCA_REG_ADC_BUFF_SAMPLE_5 0x8015 ++#define MCA_REG_ADC_BUFF_SAMPLE_6 0x8016 ++#define MCA_REG_ADC_BUFF_SAMPLE_7 0x8017 ++#define MCA_REG_ADC_BUFF_SAMPLE_8 0x8018 ++#define MCA_REG_ADC_BUFF_SAMPLE_9 0x8019 ++#define MCA_REG_ADC_BUFF_SAMPLE_10 0x801a ++#define MCA_REG_ADC_BUFF_SAMPLE_11 0x801b ++#define MCA_REG_ADC_BUFF_SAMPLE_12 0x801c ++#define MCA_REG_ADC_BUFF_SAMPLE_13 0x801d ++#define MCA_REG_ADC_BUFF_SAMPLE_14 0x801e ++#define MCA_REG_ADC_BUFF_SAMPLE_15 0x801f ++#define MCA_REG_ADC_BUFF_SAMPLE_16 0x8020 ++#define MCA_REG_ADC_BUFF_SAMPLE_17 0x8021 ++#define MCA_REG_ADC_BUFF_SAMPLE_18 0x8022 ++#define MCA_REG_ADC_BUFF_SAMPLE_19 0x8023 ++#define MCA_REG_ADC_BUFF_SAMPLE_20 0x8024 ++#define MCA_REG_ADC_BUFF_SAMPLE_21 0x8025 ++#define MCA_REG_ADC_BUFF_SAMPLE_22 0x8026 ++#define MCA_REG_ADC_BUFF_SAMPLE_23 0x8027 ++#define MCA_REG_ADC_BUFF_SAMPLE_24 0x8028 ++#define MCA_REG_ADC_BUFF_SAMPLE_25 0x8029 ++#define MCA_REG_ADC_BUFF_SAMPLE_26 0x802a ++#define MCA_REG_ADC_BUFF_SAMPLE_27 0x802b ++#define MCA_REG_ADC_BUFF_SAMPLE_28 0x802c ++#define MCA_REG_ADC_BUFF_SAMPLE_29 0x802d ++#define MCA_REG_ADC_BUFF_SAMPLE_30 0x802e ++#define MCA_REG_ADC_BUFF_SAMPLE_31 0x802f ++ ++/* EP9, UART */ ++#define MCA_REG_UART_THR 0x0900 ++#define MCA_REG_UART_RHR MCA_REG_UART_THR ++#define MCA_REG_UART_IER 0x0901 ++#define MCA_REG_UART_IIR 0x0902 ++#define MCA_REG_UART_LSR 0x0903 ++#define MCA_REG_UART_MSR 0x0904 ++#define MCA_REG_UART_RX_ERRORS 0x0905 ++#define MCA_REG_UART_TXLVL 0x0906 ++#define MCA_REG_UART_RXLVL 0x0907 ++#define MCA_REG_UART_RXWM 0x0908 ++#define MCA_REG_UART_TXWM 0x0909 ++#define MCA_REG_UART_RXSIZE 0x090A ++#define MCA_REG_UART_TXSIZE 0x090B ++#define MCA_REG_UART_CFG0 0x090C ++#define MCA_REG_UART_CFG1 0x090D ++#define MCA_REG_UART_BAUD 0x090E ++#define MCA_REG_UART_RXPIN 0x090F ++#define MCA_REG_UART_TXPIN 0x0910 ++#define MCA_REG_UART_CTSPIN 0x0911 ++#define MCA_REG_UART_RTSPIN 0x0912 ++ ++#define MCA_REG_UART_IER_RHR BIT(0) ++#define MCA_REG_UART_IER_THR BIT(1) ++#define MCA_REG_UART_IER_RLSE BIT(2) ++#define MCA_REG_UART_IER_MSR BIT(3) ++ ++#define MCA_REG_UART_IIR_RHR MCA_REG_UART_IER_RHR ++#define MCA_REG_UART_IIR_THR MCA_REG_UART_IER_THR ++#define MCA_REG_UART_IIR_RLSE MCA_REG_UART_IER_RLSE ++#define MCA_REG_UART_IIR_MSR MCA_REG_UART_IER_MSR ++ ++#define MCA_REG_UART_LSR_NO_ERROR 0 ++#define MCA_REG_UART_LSR_FRAMING_ERROR BIT(0) ++#define MCA_REG_UART_LSR_PARITY_ERROR BIT(1) ++#define MCA_REG_UART_LSR_FIFO_OR_ERROR BIT(2) ++#define MCA_REG_UART_LSR_HW_OR_ERROR BIT(3) ++#define MCA_REG_UART_LSR_BREAK BIT(4) ++ ++#define MCA_REG_UART_MSR_CTS BIT(0) ++#define MCA_REG_UART_MSR_RTS BIT(1) ++ ++#define MCA_REG_UART_CFG0_ENABLE BIT(0) ++#define MCA_REG_UART_CFG0_WAKEUP BIT(1) ++#define MCA_REG_UART_CFG0_FORCE_WAKEUP BIT(2) ++#define MCA_REG_UART_CFG0_PWR_ON BIT(3) ++#define MCA_REG_UART_CFG0_RXEN BIT(4) ++#define MCA_REG_UART_CFG0_TXEN BIT(5) ++#define MCA_REG_UART_CFG0_CRX BIT(6) ++#define MCA_REG_UART_CFG0_CTX BIT(7) ++ ++#define MCA_REG_UART_CFG1_PARITY_EN BIT(0) ++#define MCA_REG_UART_CFG1_PARITY_ODD BIT(1) ++#define MCA_REG_UART_CFG1_TWO_STOPBITS BIT(2) ++#define MCA_REG_UART_CFG1_CTS_EN BIT(3) ++#define MCA_REG_UART_CFG1_RTS_EN BIT(4) ++#define MCA_REG_UART_CFG1_THROTTLE BIT(5) ++ ++#define MCA_REG_UART_BAUD_1200 0 ++#define MCA_REG_UART_BAUD_2400 1 ++#define MCA_REG_UART_BAUD_4800 2 ++#define MCA_REG_UART_BAUD_9600 3 ++#define MCA_REG_UART_BAUD_19200 4 ++#define MCA_REG_UART_BAUD_38400 5 ++#define MCA_REG_UART_BAUD_57600 6 ++#define MCA_REG_UART_BAUD_115200 7 ++#define MCA_REG_UART_BAUD_230400 8 ++ ++/* EP10, TPMs */ ++/* Reserved 4 bytes */ ++#define MCA_REG_TPM0_CFG0 0x0a04 ++#define MCA_REG_TPM0_CFG1 0x0a05 ++#define MCA_REG_TPM0_CFG2 0x0a06 ++#define MCA_REG_TPM0_PRESCALER 0x0a07 ++/* ... */ ++#define MCA_REG_TPM0_FREQ_0 0x0a0c ++#define MCA_REG_TPM0_FREQ_1 0x0a0d ++#define MCA_REG_TPM0_FREQ_2 0x0a0e ++#define MCA_REG_TPM0_FREQ_3 0x0a0f ++#define MCA_REG_TPM0_CH0_CFG 0x0a10 ++#define MCA_REG_TPM0_CH0_DUTY 0x0a11 ++#define MCA_REG_TPM0_CH0_CNT0 0x0a12 ++#define MCA_REG_TPM0_CH0_CNT1 0x0a13 ++#define MCA_REG_TPM0_CH1_CFG 0x0a14 ++#define MCA_REG_TPM0_CH1_DUTY 0x0a15 ++#define MCA_REG_TPM0_CH1_CNT0 0x0a16 ++#define MCA_REG_TPM0_CH1_CNT1 0x0a17 ++#define MCA_REG_TPM0_CH2_CFG 0x0a18 ++#define MCA_REG_TPM0_CH2_DUTY 0x0a19 ++#define MCA_REG_TPM0_CH2_CNT0 0x0a1a ++#define MCA_REG_TPM0_CH2_CNT1 0x0a1b ++#define MCA_REG_TPM0_CH3_CFG 0x0a1c ++#define MCA_REG_TPM0_CH3_DUTY 0x0a1d ++#define MCA_REG_TPM0_CH3_CNT0 0x0a1e ++#define MCA_REG_TPM0_CH3_CNT1 0x0a1f ++#define MCA_REG_TPM0_CH4_CFG 0x0a20 ++#define MCA_REG_TPM0_CH4_DUTY 0x0a21 ++#define MCA_REG_TPM0_CH4_CNT0 0x0a22 ++#define MCA_REG_TPM0_CH4_CNT1 0x0a23 ++#define MCA_REG_TPM0_CH5_CFG 0x0a24 ++#define MCA_REG_TPM0_CH5_DUTY 0x0a25 ++#define MCA_REG_TPM0_CH5_CNT0 0x0a26 ++#define MCA_REG_TPM0_CH5_CNT1 0x0a27 ++#define MCA_REG_TPM0_CH6_CFG 0x0a28 ++#define MCA_REG_TPM0_CH6_DUTY 0x0a29 ++#define MCA_REG_TPM0_CH6_CNT0 0x0a2a ++#define MCA_REG_TPM0_CH6_CNT1 0x0a2b ++#define MCA_REG_TPM0_CH7_CFG 0x0a2c ++#define MCA_REG_TPM0_CH7_DUTY 0x0a2d ++#define MCA_REG_TPM0_CH7_CNT0 0x0a2e ++#define MCA_REG_TPM0_CH7_CNT1 0x0a2f ++#define MCA_REG_TPM1_CFG0 0x0a34 ++#define MCA_REG_TPM1_CFG1 0x0a35 ++#define MCA_REG_TPM1_CFG2 0x0a36 ++#define MCA_REG_TPM1_PRESCALER 0x0a37 ++/* ... */ ++#define MCA_REG_TPM1_FREQ_0 0x0a3c ++#define MCA_REG_TPM1_FREQ_1 0x0a3d ++#define MCA_REG_TPM1_FREQ_2 0x0a3e ++#define MCA_REG_TPM1_FREQ_3 0x0a3f ++#define MCA_REG_TPM1_CH0_CFG 0x0a40 ++#define MCA_REG_TPM1_CH0_DUTY 0x0a41 ++#define MCA_REG_TPM1_CH0_CNT0 0x0a42 ++#define MCA_REG_TPM1_CH0_CNT1 0x0a43 ++#define MCA_REG_TPM1_CH1_CFG 0x0a44 ++#define MCA_REG_TPM1_CH1_DUTY 0x0a45 ++#define MCA_REG_TPM1_CH1_CNT0 0x0a46 ++#define MCA_REG_TPM1_CH1_CNT1 0x0a47 ++#define MCA_REG_TPM1_CH2_CFG 0x0a48 ++#define MCA_REG_TPM1_CH2_DUTY 0x0a49 ++#define MCA_REG_TPM1_CH2_CNT0 0x0a4a ++#define MCA_REG_TPM1_CH2_CNT1 0x0a4b ++#define MCA_REG_TPM1_CH3_CFG 0x0a4c ++#define MCA_REG_TPM1_CH3_DUTY 0x0a4d ++#define MCA_REG_TPM1_CH3_CNT0 0x0a4e ++#define MCA_REG_TPM1_CH3_CNT1 0x0a4f ++#define MCA_REG_TPM1_CH4_CFG 0x0a50 ++#define MCA_REG_TPM1_CH4_DUTY 0x0a51 ++#define MCA_REG_TPM1_CH4_CNT0 0x0a52 ++#define MCA_REG_TPM1_CH4_CNT1 0x0a53 ++#define MCA_REG_TPM1_CH5_CFG 0x0a54 ++#define MCA_REG_TPM1_CH5_DUTY 0x0a55 ++#define MCA_REG_TPM1_CH5_CNT0 0x0a56 ++#define MCA_REG_TPM1_CH5_CNT1 0x0a57 ++#define MCA_REG_TPM1_CH6_CFG 0x0a58 ++#define MCA_REG_TPM1_CH6_DUTY 0x0a59 ++#define MCA_REG_TPM1_CH6_CNT0 0x0a5a ++#define MCA_REG_TPM1_CH6_CNT1 0x0a5b ++#define MCA_REG_TPM1_CH7_CFG 0x0a5c ++#define MCA_REG_TPM1_CH7_DUTY 0x0a5d ++#define MCA_REG_TPM1_CH7_CNT0 0x0a5e ++#define MCA_REG_TPM1_CH7_CNT1 0x0a5f ++#define MCA_REG_TPM2_CFG0 0x0a64 ++#define MCA_REG_TPM2_CFG1 0x0a65 ++#define MCA_REG_TPM2_CFG2 0x0a66 ++#define MCA_REG_TPM2_PRESCALER 0x0a67 ++/* ... */ ++#define MCA_REG_TPM2_FREQ_0 0x0a6c ++#define MCA_REG_TPM2_FREQ_1 0x0a6d ++#define MCA_REG_TPM2_FREQ_2 0x0a6e ++#define MCA_REG_TPM2_FREQ_3 0x0a6f ++#define MCA_REG_TPM2_CH0_CFG 0x0a70 ++#define MCA_REG_TPM2_CH0_DUTY 0x0a71 ++#define MCA_REG_TPM2_CH0_CNT0 0x0a72 ++#define MCA_REG_TPM2_CH0_CNT1 0x0a73 ++#define MCA_REG_TPM2_CH1_CFG 0x0a74 ++#define MCA_REG_TPM2_CH1_DUTY 0x0a75 ++#define MCA_REG_TPM2_CH1_CNT0 0x0a76 ++#define MCA_REG_TPM2_CH1_CNT1 0x0a77 ++#define MCA_REG_TPM2_CH2_CFG 0x0a78 ++#define MCA_REG_TPM2_CH2_DUTY 0x0a79 ++#define MCA_REG_TPM2_CH2_CNT0 0x0a7a ++#define MCA_REG_TPM2_CH2_CNT1 0x0a7b ++#define MCA_REG_TPM2_CH3_CFG 0x0a7c ++#define MCA_REG_TPM2_CH3_DUTY 0x0a7d ++#define MCA_REG_TPM2_CH3_CNT0 0x0a7e ++#define MCA_REG_TPM2_CH3_CNT1 0x0a7f ++#define MCA_REG_TPM2_CH4_CFG 0x0a80 ++#define MCA_REG_TPM2_CH4_DUTY 0x0a81 ++#define MCA_REG_TPM2_CH4_CNT0 0x0a82 ++#define MCA_REG_TPM2_CH4_CNT1 0x0a83 ++#define MCA_REG_TPM2_CH5_CFG 0x0a84 ++#define MCA_REG_TPM2_CH5_DUTY 0x0a85 ++#define MCA_REG_TPM2_CH5_CNT0 0x0a86 ++#define MCA_REG_TPM2_CH5_CNT1 0x0a87 ++#define MCA_REG_TPM2_CH6_CFG 0x0a88 ++#define MCA_REG_TPM2_CH6_DUTY 0x0a89 ++#define MCA_REG_TPM2_CH6_CNT0 0x0a8a ++#define MCA_REG_TPM2_CH6_CNT1 0x0a8b ++#define MCA_REG_TPM2_CH7_CFG 0x0a8c ++#define MCA_REG_TPM2_CH7_DUTY 0x0a8d ++#define MCA_REG_TPM2_CH7_CNT0 0x0a8e ++#define MCA_REG_TPM2_CH7_CNT1 0x0a8f ++ ++#define MCA_PWM_TPM_LEN (MCA_REG_TPM1_CFG0 - MCA_REG_TPM0_CFG0) ++#define MCA_PWM_CH_LEN (MCA_REG_TPM0_CH1_CFG - MCA_REG_TPM0_CH0_CFG) ++ ++/* ++ * MCA registers bitfields ++ */ ++ ++/* MCA_IRQ_STATUS_0 (addr=0x0020) */ ++#define MCA_RTC_ALARM BIT(0) ++#define MCA_RTC_1HZ BIT(1) ++#define MCA_WATCHDOG BIT(2) ++#define MCA_PWR_SLEEP BIT(3) ++#define MCA_PWR_OFF BIT(4) ++#define MCA_TAMPER0 BIT(5) ++#define MCA_TAMPER1 BIT(6) ++#define MCA_ADC BIT(7) ++ ++/* MCA_IRQ_STATUS_1 (addr=0x0021) */ ++#define MCA_GPIO_BANK_0 BIT(0) ++#define MCA_GPIO_BANK_1 BIT(1) ++#define MCA_GPIO_BANK_2 BIT(2) ++#define MCA_GPIO_BANK_3 BIT(3) ++#define MCA_GPIO_BANK_4 BIT(4) ++#define MCA_GPIO_BANK_5 BIT(5) ++#define MCA_GPIO_BANK_6 BIT(6) ++#define MCA_GPIO_BANK_7 BIT(7) ++ ++/* MCA_GPIO_IRQ_CFG_n (addr=0x0332... 0x0369) */ ++#define MCA_GPIO_IRQ_EN BIT(0) ++#define MCA_GPIO_IRQ_LEVEL BIT(1) ++#define MCA_GPIO_IRQ_EDGE_RISE BIT(2) ++#define MCA_GPIO_IRQ_EDGE_FALL BIT(3) ++#define MCA_GPIO_IRQ_EDGE_BOTH (MCA_GPIO_IRQ_EDGE_RISE | \ ++ MCA_GPIO_IRQ_EDGE_FALL) ++#define MCA_M_GPIO_IRQ_CFG (MCA_GPIO_IRQ_LEVEL | \ ++ MCA_GPIO_IRQ_EDGE_BOTH) ++#define MCA_GPIO_IRQ_CAPABLE BIT(7) ++ ++/* MCA_IRQ_MASK_0 (addr=0x0024) */ ++#define MCA_M_RTC_ALARM BIT(0) ++#define MCA_M_RTC_1HZ BIT(1) ++#define MCA_M_WATCHDOG BIT(2) ++#define MCA_M_PWR_SLEEP BIT(3) ++#define MCA_M_PWR_OFF BIT(4) ++#define MCA_M_TAMPER0 BIT(5) ++#define MCA_M_TAMPER1 BIT(6) ++#define MCA_M_ADC BIT(7) ++ ++/* MCA_IRQ_MASK_1 (addr=0x0025) */ ++#define MCA_M_GPIO_BANK_0 BIT(0) ++#define MCA_M_GPIO_BANK_1 BIT(1) ++#define MCA_M_GPIO_BANK_2 BIT(2) ++#define MCA_M_GPIO_BANK_3 BIT(3) ++#define MCA_M_GPIO_BANK_4 BIT(4) ++#define MCA_M_GPIO_BANK_5 BIT(5) ++#define MCA_M_GPIO_BANK_6 BIT(6) ++#define MCA_M_GPIO_BANK_7 BIT(7) ++ ++/* MCA_IRQ_MASK_2 */ ++#define MCA_M_TAMPER2 BIT(0) ++#define MCA_M_TAMPER3 BIT(1) ++#define MCA_M_UART BIT(2) ++ ++/* MCA_PWR_CTRL_0 (addr=0x0028) */ ++#define MCA_PWR_KEY_SLEEP_EN BIT(0) ++#define MCA_PWR_KEY_OFF_EN BIT(1) ++#define MCA_PWR_GUARD_EN BIT(2) ++#define MCA_PWR_GO_OFF BIT(3) ++#define MCA_PWR_GO_SUSPEND BIT(4) ++ ++/* MCA_CTRL_0 (addr=0x0031) */ ++#define MCA_RESET BIT(0) ++#define MCA_EXT32K_EN BIT(1) ++#define MCA_JMP_BL BIT(2) ++#define MCA_VREF_EN BIT(5) ++ ++/* MCA_TAMPERn_CFG0 (addr=0x0037 & 0x0046) */ ++#define MCA_TAMPER_DET_EN BIT(0) ++#define MCA_TAMPER_RECONF_EN BIT(1) ++#define MCA_TAMPER_IN_ACT_HIGH BIT(2) ++#define MCA_TAMPER_IRQ_EN BIT(3) ++#define MCA_TAMPER_OUT_EN BIT(4) ++#define MCA_TAMPER_OUT_ACT_HIGH BIT(5) ++#define MCA_TAMPER_PWROFF_EN BIT(6) ++#define MCA_TAMPER_CLR_EV_EN BIT(7) ++ ++/* MCA_TAMPERm_EVENT (addr=0x0045 & 0x0054) */ ++#define MCA_TAMPER_SIGNALED BIT(0) ++#define MCA_TAMPER_ACKED BIT(1) ++#define MCA_TAMPER_CLEAR 0 ++ ++/* MCA_WDT_CONTROL (addr=0x0201) */ ++#define MCA_WDT_ENABLE BIT(0) ++#define MCA_WDT_NOWAYOUT BIT(1) ++#define MCA_WDT_IRQNORESET BIT(2) ++#define MCA_WDT_PRETIMEOUT BIT(3) ++#define MCA_WDT_FULLRESET BIT(4) ++ ++/* MCA_WDT_TIMEOUT (addr=0x0202) */ ++#define MCA_WDT_TIMEOUT_MASK 0xFF ++ ++/* MCA_WDT_REFRESH_X (addr=0x0203..0x0206) */ ++#define MCA_WDT_REFRESH_X_MASK 0xFF ++ ++/* MCA_GPIO_NUM (addr=0x0302) */ ++#define MCA_GPIO_NUM_MASK 0x7F ++ ++/* MCA_ADC_CFG0 (addr=0x0404..0x040b) */ ++#define MCA_REG_ADC_CFG0_EN BIT(0) ++#define MCA_REG_ADC_CFG0_RUNS_LP BIT(1) ++#define MCA_REG_ADC_CFG0_MODE_0 BIT(2) ++#define MCA_REG_ADC_CFG0_MODE_1 BIT(3) ++#define MCA_REG_ADC_CFG0_AVG_0 BIT(4) ++#define MCA_REG_ADC_CFG0_AVG_1 BIT(5) ++#define MCA_REG_ADC_CFG0_IRQ_EN BIT(6) ++#define MCA_REG_ADC_CFG0_CAPABLE BIT(7) ++ ++#define MCA_REG_ADC_CFG0_AVG_MASK (MCA_REG_ADC_CFG0_AVG_0 |\ ++ MCA_REG_ADC_CFG0_AVG_1) ++#define MCA_REG_ADC_CFG0_AVG_SHIFT 4 ++ ++/* ADC CFG1 flags */ ++#define MCA_REG_ADC_CFG1_EN_RISING BIT(0) ++#define MCA_REG_ADC_CFG1_EN_FALLING BIT(1) ++#define MCA_REG_ADC_CFG1_LEVEL_EDGE BIT(2) ++#define MCA_REG_ADC_CFG1_INVERT_CMP BIT(3) ++#define MCA_REG_ADC_CFG1_CMP_OUT BIT(4) ++#define MCA_REG_ADC_CFG1_RISING_MASK BIT(5) ++#define MCA_REG_ADC_CFG1_FALLING_MASK BIT(6) ++#define MCA_REG_ADC_CFG1_IRQ BIT(7) ++#define MCA_REG_ADC_CFG1_IRQ_FLAGS_MASK 0xE0 ++#define MCA_REG_ADC_CFG1_RO_MASK 0xF0 ++ ++/* Global ADC CFG_0 flags */ ++#define MCA_REG_ADC_CFG_0_INT_VREF (1 << 0) ++ ++/* MCA_RTC_CONTROL (addr=0x0101) */ ++#define MCA_RTC_EN BIT(0) ++#define MCA_RTC_ALARM_EN BIT(1) ++#define MCA_RTC_1HZ_EN BIT(2) ++#define MCA_RTC_32KHZ_OUT_EN BIT(3) ++ ++/* MCA_RTC_COUNT_YEAR_L (addr=0x0103) */ ++/* MCA_RTC_ALARM_YEAR_L (addr=0x010A) */ ++#define MCA_RTC_YEAR_L_MASK 0xFF ++ ++/* MCA_RTC_COUNT_YEAR_H (addr=0x0104) */ ++/* MCA_RTC_ALARM_YEAR_H (addr=0x010B) */ ++#define MCA_RTC_YEAR_H_MASK 0xFF ++ ++/* MCA_RTC_COUNT_MONTH (addr=0x0105) */ ++/* MCA_RTC_ALARM_MONTH (addr=0x010C) */ ++#define MCA_RTC_MONTH_MASK 0x0F ++ ++/* MCA_RTC_COUNT_DAY (addr=0x0106) */ ++/* MCA_RTC_ALARM_DAY (addr=0x010D) */ ++#define MCA_RTC_DAY_MASK 0x1F ++ ++/* MCA_RTC_COUNT_HOUR (addr=0x0107) */ ++/* MCA_RTC_ALARM_HOUR (addr=0x010E) */ ++#define MCA_RTC_HOUR_MASK 0x1F ++ ++/* MCA_RTC_COUNT_MIN (addr=0x0108) */ ++/* MCA_RTC_ALARM_MIN (addr=0x010F) */ ++#define MCA_RTC_MIN_MASK 0x3F ++ ++/* MCA_RTC_COUNT_SEC (addr=0x0109) */ ++/* MCA_RTC_ALARM_SEC (addr=0x0110) */ ++#define MCA_RTC_SEC_MASK 0x3F ++ ++/* MCA_REG_TPMx_CH0_CFG (addr=0x0a10...) */ ++#define MCA_TPM_CH_EN BIT(0) ++#define MCA_TPM_CH_POL_HIGH BIT(1) ++ ++#endif /* MCA_COMMON_REGISTERS_H_ */ diff --git a/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ul/0004-imx6ul-Add-MCA-GPIO-support-for-the-ConnectCore-6UL-.patch b/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ul/0004-imx6ul-Add-MCA-GPIO-support-for-the-ConnectCore-6UL-.patch new file mode 100644 index 000000000..f3896f554 --- /dev/null +++ b/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ul/0004-imx6ul-Add-MCA-GPIO-support-for-the-ConnectCore-6UL-.patch @@ -0,0 +1,701 @@ +From a2b055852d963729002f48155d8bbee7e2858e0a Mon Sep 17 00:00:00 2001 +From: Alex Gonzalez +Date: Mon, 23 Apr 2018 11:37:46 +0200 +Subject: [PATCH] imx6ul: Add MCA GPIO support for the ConnectCore 6UL SOM + +Synched with v4.14.78/master at: +3f8b03950b323db4ca89b1cdc1c2288f79facaa3 + +Signed-off-by: Alex Gonzalez +--- + arch/arm/boot/dts/imx6ul-ccimx6ulsbcexpress.dts | 2 + + arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts | 2 + + arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi | 11 + + arch/arm/configs/imx_v6_v7_defconfig | 1 + + drivers/gpio/Kconfig | 9 + + drivers/gpio/Makefile | 1 + + drivers/gpio/gpio-mca.c | 583 ++++++++++++++++++++++++ + 7 files changed, 609 insertions(+) + create mode 100644 drivers/gpio/gpio-mca.c + +diff --git a/arch/arm/boot/dts/imx6ul-ccimx6ulsbcexpress.dts b/arch/arm/boot/dts/imx6ul-ccimx6ulsbcexpress.dts +index 3792679c0c90..148f1b95e46d 100644 +--- a/arch/arm/boot/dts/imx6ul-ccimx6ulsbcexpress.dts ++++ b/arch/arm/boot/dts/imx6ul-ccimx6ulsbcexpress.dts +@@ -43,6 +43,8 @@ + pinctrl-0 = <&pinctrl_enet1>; + phy-mode = "rmii"; + phy-handle = <ðphy0>; ++ phy-reset-gpios = <&mca_gpio 7 GPIO_ACTIVE_LOW>; ++ phy-reset-duration = <26>; + status = "okay"; + + mdio { +diff --git a/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts b/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts +index 3749fdda3611..5ad2c61276bc 100644 +--- a/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts ++++ b/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts +@@ -79,6 +79,8 @@ + pinctrl-0 = <&pinctrl_enet1>; + phy-mode = "rmii"; + phy-handle = <ðphy0>; ++ phy-reset-gpios = <&mca_gpio 7 GPIO_ACTIVE_LOW>; ++ phy-reset-duration = <26>; + status = "okay"; + }; + +diff --git a/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi b/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi +index 03c62926ca2b..8d475051acf2 100644 +--- a/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi ++++ b/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi +@@ -58,6 +58,17 @@ + fw-update-gpio = <&gpio4 14 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_mca_cc6ul>; ++ ++ mca_gpio: gpio { ++ compatible = "digi,mca-cc6ul-gpio"; ++ gpio-controller; ++ #gpio-cells = <2>; ++ ++ interrupt-parent = <&mca_cc6ul>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ }; ++ + }; + + pfuze3000: pmic@8 { +diff --git a/arch/arm/configs/imx_v6_v7_defconfig b/arch/arm/configs/imx_v6_v7_defconfig +index 6985d61eddb3..6c3ede35e643 100644 +--- a/arch/arm/configs/imx_v6_v7_defconfig ++++ b/arch/arm/configs/imx_v6_v7_defconfig +@@ -207,6 +207,7 @@ CONFIG_SPI_GPIO=y + CONFIG_SPI_IMX=y + CONFIG_SPI_FSL_DSPI=y + CONFIG_GPIO_SYSFS=y ++CONFIG_GPIO_MCA=y + CONFIG_GPIO_MAX732X=y + CONFIG_GPIO_MC9S08DZ60=y + CONFIG_GPIO_PCA953X=y +diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig +index 833a1b51c948..f7daae95b2a7 100644 +--- a/drivers/gpio/Kconfig ++++ b/drivers/gpio/Kconfig +@@ -305,6 +305,15 @@ config GPIO_MB86S7X + help + Say yes here to support the GPIO controller in Fujitsu MB86S70 SoCs. + ++config GPIO_MCA ++ tristate "Digi ConnectCore SOMs Micro Controller Assist GPIO support" ++ select MFD_MCA_CC6UL if SOC_IMX6UL ++ select MFD_MCA_CC8X if ARCH_FSL_IMX8QXP ++ select GPIOLIB_IRQCHIP ++ help ++ If you say yes here you will get support for the GPIOs in the ++ Micro Controller Assist of Digi ConnectCore system-on-modules. ++ + config GPIO_MENZ127 + tristate "MEN 16Z127 GPIO support" + depends on MCB +diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile +index 671c4477c951..481b23e8c27e 100644 +--- a/drivers/gpio/Makefile ++++ b/drivers/gpio/Makefile +@@ -82,6 +82,7 @@ obj-$(CONFIG_GPIO_MENZ127) += gpio-menz127.o + obj-$(CONFIG_GPIO_MERRIFIELD) += gpio-merrifield.o + obj-$(CONFIG_GPIO_MC33880) += gpio-mc33880.o + obj-$(CONFIG_GPIO_MC9S08DZ60) += gpio-mc9s08dz60.o ++obj-$(CONFIG_GPIO_MCA) += gpio-mca.o + obj-$(CONFIG_GPIO_ML_IOH) += gpio-ml-ioh.o + obj-$(CONFIG_GPIO_MM_LANTIQ) += gpio-mm-lantiq.o + obj-$(CONFIG_GPIO_MOCKUP) += gpio-mockup.o +diff --git a/drivers/gpio/gpio-mca.c b/drivers/gpio/gpio-mca.c +new file mode 100644 +index 000000000000..75dd8af9f76e +--- /dev/null ++++ b/drivers/gpio/gpio-mca.c +@@ -0,0 +1,583 @@ ++/* gpio-mca.c - GPIO driver for MCA devices. ++ * ++ * Copyright (C) 2017 - 2019 Digi International Inc ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Library General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Library General Public License for more details. ++ * ++ * You should have received a copy of the GNU Library General Public ++ * License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define MCA_DRVNAME_GPIO "mca-gpio" ++ ++/* ++ * The following macros return the register address to read/write for a given ++ * gpio number. ++ */ ++#define GPIO_DIR_REG(x) (MCA_GPIO_DIR_0 + ((x) / 8)) ++#define GPIO_DATA_REG(x) (MCA_GPIO_DATA_0 + ((x) / 8)) ++#define GPIO_SET_REG(x) (MCA_GPIO_SET_0 + ((x) / 8)) ++#define GPIO_CLEAR_REG(x) (MCA_GPIO_CLEAR_0 + ((x) / 8)) ++#define GPIO_TOGGLE_REG(x) (MCA_GPIO_TOGGLE_0 + ((x) / 8)) ++#define GPIO_IRQ_STATUS_REG(x) (MCA_GPIO_IRQ_STATUS_0 + (x)) ++#define GPIO_IRQ_CFG_REG(x) (MCA_GPIO_IRQ_CFG_0 + (x)) ++#define GPIO_DEB_CFG_REG(x) (MCA_GPIO_DEB_CFG_0 + ((x) / 8)) ++#define GPIO_DEB_CNT_REG(x) (MCA_GPIO_DEB_CNT_0 + (x)) ++ ++#define GPIO_CFG_UPDATE BIT(6) ++#define GPIO_BYTE(i) ((i) / 8) ++#define BYTE_OFFSET(i) ((i) % 8) ++#define BIT_OFFSET(i) ((i) % 8) ++ ++#ifdef CONFIG_OF ++enum mca_gpio_type { ++ CC6UL_MCA_GPIO, ++ CC8X_MCA_GPIO, ++ IOEXP_MCA_GPIO, ++}; ++ ++struct mca_gpio_data { ++ enum mca_gpio_type devtype; ++}; ++#endif ++ ++struct mca_gpio { ++ void * parent; ++ struct regmap *regmap; ++ struct device *dev; ++ struct gpio_chip gc; ++ struct mutex irq_lock; ++ uint8_t irq_cfg[MCA_MAX_IOS]; ++ uint8_t irq_capable[MCA_MAX_IO_BYTES]; ++ int irq[MCA_MAX_GPIO_IRQ_BANKS]; ++ uint8_t deb_timer_cfg[MCA_MAX_GPIO_IRQ_BANKS]; ++}; ++ ++static char const *const irq_gpio_bank_name[] = { ++ MCA_IRQ_GPIO_BANK_0_NAME, ++ MCA_IRQ_GPIO_BANK_1_NAME, ++ MCA_IRQ_GPIO_BANK_2_NAME, ++ MCA_IRQ_GPIO_BANK_3_NAME, ++ MCA_IRQ_GPIO_BANK_4_NAME, ++ MCA_IRQ_GPIO_BANK_5_NAME, ++}; ++ ++static inline struct mca_gpio *to_mca_gpio(struct gpio_chip *chip) ++{ ++ return gpiochip_get_data(chip); ++} ++ ++static inline bool mca_gpio_is_irq_capable(struct mca_gpio *gpio, ++ u32 offset) ++{ ++ return ((gpio->irq_capable[GPIO_BYTE(offset)] & ++ (1 << BYTE_OFFSET(offset))) != 0); ++} ++ ++static int mca_gpio_get(struct gpio_chip *gc, unsigned num) ++{ ++ struct mca_gpio *gpio = to_mca_gpio(gc); ++ unsigned int val; ++ int ret; ++ ++ ret = regmap_read(gpio->regmap, GPIO_DATA_REG(num), &val); ++ if (ret < 0) ++ return ret; ++ ++ return (val & (1 << BIT_OFFSET(num)) ? 1 : 0); ++} ++ ++static void mca_gpio_set(struct gpio_chip *gc, unsigned num, int val) ++{ ++ struct mca_gpio *gpio = to_mca_gpio(gc); ++ unsigned int reg = val ? GPIO_SET_REG(num) : GPIO_CLEAR_REG(num); ++ ++ regmap_write(gpio->regmap, reg, 1 << BIT_OFFSET(num)); ++} ++ ++static int mca_gpio_direction_input(struct gpio_chip *gc, unsigned num) ++{ ++ struct mca_gpio *gpio = to_mca_gpio(gc); ++ ++ return regmap_update_bits(gpio->regmap, GPIO_DIR_REG(num), ++ 1 << BIT_OFFSET(num), 0); ++} ++ ++static int mca_gpio_direction_output(struct gpio_chip *gc, unsigned num, ++ int val) ++{ ++ struct mca_gpio *gpio = to_mca_gpio(gc); ++ int ret; ++ ++ /* Set value before setting direction */ ++ mca_gpio_set(gc, num, val); ++ ++ ret = regmap_update_bits(gpio->regmap, GPIO_DIR_REG(num), ++ 1 << BIT_OFFSET(num), 1 << BIT_OFFSET(num)); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++#define MCA_GPIO_MAX_DEB_VAL_TIMER_50MS (50 * 1000 * 255) ++#define MCA_GPIO_MAX_DEB_VAL_TIMER_1MS (255 * 1000) ++ ++static int mca_gpio_set_debounce(struct gpio_chip *gc, unsigned int usecs, ++ unsigned int debounce) ++{ ++ struct mca_gpio *mca_gc = to_mca_gpio(gc); ++ u8 deb_cnt; ++ int ret; ++ ++ if (debounce > MCA_GPIO_MAX_DEB_VAL_TIMER_50MS) { ++ dev_warn(mca_gc->dev, "Value out of range %u, setting %u instead\n", ++ debounce, MCA_GPIO_MAX_DEB_VAL_TIMER_50MS); ++ debounce = MCA_GPIO_MAX_DEB_VAL_TIMER_50MS; ++ } ++ ++ if (debounce > MCA_GPIO_MAX_DEB_VAL_TIMER_1MS) { ++ /* Set timer cfg period to 50ms */ ++ mca_gc->deb_timer_cfg[GPIO_BYTE(usecs)] |= 1 << BIT_OFFSET(usecs); ++ deb_cnt = (debounce + 49999) / 50000; ++ } else { ++ /* Set timer cfg period to 1ms */ ++ mca_gc->deb_timer_cfg[GPIO_BYTE(usecs)] &= ~(1 << BIT_OFFSET(usecs)); ++ deb_cnt = (debounce + 999) / 1000; ++ } ++ ++ ret = regmap_write(mca_gc->regmap, GPIO_DEB_CFG_REG(usecs), ++ mca_gc->deb_timer_cfg[GPIO_BYTE(usecs)]); ++ if (ret) { ++ dev_err(mca_gc->dev, "Failed to write GPIO_DEB_CFG_REG(%d) (%d)\n", ++ usecs, ret); ++ } else { ++ ret = regmap_write(mca_gc->regmap, GPIO_DEB_CNT_REG(usecs), deb_cnt); ++ if (ret) ++ dev_err(mca_gc->dev, ++ "Failed to write GPIO_DEB_CNT_REG(%d) (%d)\n", ++ usecs, ret); ++ } ++ ++ return ret; ++} ++ ++static int mca_gpio_set_config(struct gpio_chip *gc, unsigned int num, ++ unsigned long config) ++{ ++ enum pin_config_param param = pinconf_to_config_param(config); ++ u32 arg = pinconf_to_config_argument(config); ++ ++ if (param != PIN_CONFIG_INPUT_DEBOUNCE) ++ return -ENOTSUPP; ++ ++ return mca_gpio_set_debounce(gc, num, arg); ++} ++ ++static irqreturn_t mca_gpio_irq_handler(int irq, void *data) ++{ ++ struct mca_gpio *gpio = data; ++ unsigned int pending_irqs, mask, this_irq; ++ int ret, i, j; ++ ++ for (i = 0; i < (gpio->gc.ngpio + 7) / 8; i++) { ++ ret = regmap_read(gpio->regmap, GPIO_IRQ_STATUS_REG(i), &pending_irqs); ++ if (ret < 0) { ++ dev_err(gpio->dev, ++ "IRQ %d: Failed to read GPIO_IRQ_STATUS_REG (%d)\n", ++ irq, ret); ++ continue; ++ } ++ ++ for (j = 0; j < 8; j++) { ++ mask = 1 << j; ++ if (pending_irqs & mask) { ++ /* Ack the irq and call the handler */ ++ this_irq = irq_find_mapping(gpio->gc.irq.domain, j + i * 8); ++ ret = regmap_write(gpio->regmap, ++ GPIO_IRQ_STATUS_REG(i), ++ mask); ++ if (ret) { ++ dev_err(gpio->dev, ++ "Failed to ack IRQ %d (%d)\n", ++ this_irq, ret); ++ continue; ++ } ++ ++ handle_nested_irq(this_irq); ++ } ++ } ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static void mca_gpio_irq_disable(struct irq_data *d) ++{ ++ struct gpio_chip *gc = irq_data_get_irq_chip_data(d); ++ struct mca_gpio *gpio = to_mca_gpio(gc); ++ ++ /* ++ * Update the IRQ_EN bit and also set the CFG_UPDATE flag to mark what ++ * registers have to be written later to the MCA, once we are out of ++ * atomic context. Note that this flag is not cleared before writing ++ * the MCA regsister. ++ */ ++ gpio->irq_cfg[d->hwirq] |= GPIO_CFG_UPDATE; ++ gpio->irq_cfg[d->hwirq] &= ~MCA_GPIO_IRQ_EN; ++} ++ ++static void mca_gpio_irq_enable(struct irq_data *d) ++{ ++ struct gpio_chip *gc = irq_data_get_irq_chip_data(d); ++ struct mca_gpio *gpio = to_mca_gpio(gc); ++ ++ /* ++ * Update the IRQ_EN bit and also set the CFG_UPDATE flag to mark what ++ * registers have to be written later to the MCA, once we are out of ++ * atomic context. Note that this flag is not cleared before writing ++ * the MCA regsister. ++ */ ++ gpio->irq_cfg[d->hwirq] |= GPIO_CFG_UPDATE | MCA_GPIO_IRQ_EN; ++} ++ ++static void mca_gpio_irq_bus_lock(struct irq_data *d) ++{ ++ struct gpio_chip *gc = irq_data_get_irq_chip_data(d); ++ struct mca_gpio *gpio = to_mca_gpio(gc); ++ ++ mutex_lock(&gpio->irq_lock); ++} ++ ++static void mca_gpio_irq_bus_sync_unlock(struct irq_data *d) ++{ ++ struct gpio_chip *gc = irq_data_get_irq_chip_data(d); ++ struct mca_gpio *gpio = to_mca_gpio(gc); ++ int i, ret; ++ ++ for (i = 0; i < gc->ngpio; i++) { ++ /* Update only those registers that were flagged (modified) */ ++ if (!(gpio->irq_cfg[i] & GPIO_CFG_UPDATE)) ++ continue; ++ ++ gpio->irq_cfg[i] &= ~GPIO_CFG_UPDATE; ++ ++ ret = regmap_write(gpio->regmap, ++ GPIO_IRQ_CFG_REG(i), ++ gpio->irq_cfg[i]); ++ if (ret) { ++ dev_err(gpio->dev, ++ "Failed to configure IRQ %d\n", d->irq); ++ } ++ } ++ ++ mutex_unlock(&gpio->irq_lock); ++} ++ ++static int mca_gpio_irq_set_type(struct irq_data *d, unsigned int type) ++{ ++ struct gpio_chip *gc = irq_data_get_irq_chip_data(d); ++ struct mca_gpio *gpio = to_mca_gpio(gc); ++ u32 gpio_idx = d->hwirq; ++ ++ if ((type & IRQ_TYPE_LEVEL_HIGH) || (type & IRQ_TYPE_LEVEL_LOW)) { ++ dev_err(gpio->dev, ++ "IRQ %d: level IRQs are not supported\n", d->irq); ++ return -EINVAL; ++ } ++ ++ /* ++ * Update the edge flags based on type and set CFG_UPDATE to note that ++ * the register was modified and has to be written back to the MCA in ++ * mca_gpio_irq_bus_sync_unlock(). ++ */ ++ gpio->irq_cfg[gpio_idx] &= ~MCA_M_GPIO_IRQ_CFG; ++ gpio->irq_cfg[gpio_idx] |= GPIO_CFG_UPDATE; ++ ++ if (type & IRQ_TYPE_EDGE_RISING) ++ gpio->irq_cfg[gpio_idx] |= MCA_GPIO_IRQ_EDGE_RISE; ++ ++ if (type & IRQ_TYPE_EDGE_FALLING) ++ gpio->irq_cfg[gpio_idx] |= MCA_GPIO_IRQ_EDGE_FALL; ++ ++ return 0; ++} ++ ++static int mca_gpio_to_irq(struct gpio_chip *gc, u32 offset) ++{ ++ struct mca_gpio *gpio = to_mca_gpio(gc); ++ ++ if (GPIO_BYTE(offset) >= MCA_MAX_IO_BYTES) ++ return -EINVAL; ++ ++ /* Discard non irq capable gpios */ ++ if (!mca_gpio_is_irq_capable(gpio, offset)) ++ return -EINVAL; ++ ++ return irq_create_mapping(gc->irq.domain, offset); ++} ++ ++static struct irq_chip mca_gpio_irq_chip = { ++ .name = "mca-gpio-irq", ++ .irq_disable = mca_gpio_irq_disable, ++ .irq_enable = mca_gpio_irq_enable, ++ .irq_bus_lock = mca_gpio_irq_bus_lock, ++ .irq_bus_sync_unlock = mca_gpio_irq_bus_sync_unlock, ++ .irq_set_type = mca_gpio_irq_set_type, ++}; ++ ++static int mca_gpio_irq_setup(struct mca_gpio *gpio) ++{ ++ unsigned int val; ++ int ret, i; ++ ++ mutex_init(&gpio->irq_lock); ++ ++ for (i = 0; i < gpio->gc.ngpio; i++) { ++ gpio->irq_cfg[i] = 0; ++ ++ ret = regmap_read(gpio->regmap, GPIO_IRQ_CFG_REG(i), &val); ++ if (ret) { ++ dev_err(gpio->dev, ++ "Failed to read GPIO[%d] irq config (%d)\n", ++ i, ret); ++ continue; ++ } ++ ++ if (val & MCA_GPIO_IRQ_CAPABLE) ++ gpio->irq_capable[GPIO_BYTE(i)] |= 1 << BYTE_OFFSET(i); ++ else ++ gpio->irq_capable[GPIO_BYTE(i)] &= ~(1 << BYTE_OFFSET(i)); ++ } ++ ++ for (i = 0; i < MCA_MAX_GPIO_IRQ_BANKS; i++) { ++ if (gpio->irq[i] < 0) ++ continue; ++ ret = devm_request_threaded_irq(gpio->dev, gpio->irq[i], ++ NULL, mca_gpio_irq_handler, ++ IRQF_TRIGGER_FALLING | IRQF_ONESHOT, ++ mca_gpio_irq_chip.name, ++ gpio); ++ if (ret) { ++ dev_err(gpio->dev, "Failed to request %s IRQ (%d)\n", ++ irq_gpio_bank_name[i], gpio->irq[i]); ++ return ret; ++ } ++ } ++ ++ ret = gpiochip_irqchip_add_nested(&gpio->gc, ++ &mca_gpio_irq_chip, ++ 0, ++ handle_edge_irq, ++ IRQ_TYPE_NONE); ++ if (ret) { ++ dev_err(gpio->dev, ++ "Failed to connect irqchip to gpiochip (%d)\n", ret); ++ return ret; ++ } ++ ++ /* ++ * gpiochip_irqchip_add_nested() sets .to_irq with its own implementation but ++ * we have to use our own version because not all GPIOs are irq capable. ++ * Therefore, we overwrite it. ++ */ ++ gpio->gc.to_irq = mca_gpio_to_irq; ++ ++ for (i = 0; i < MCA_MAX_GPIO_IRQ_BANKS; i++) { ++ if (gpio->irq[i] < 0) ++ continue; ++ gpiochip_set_nested_irqchip(&gpio->gc, ++ &mca_gpio_irq_chip, ++ gpio->irq[i]); ++ } ++ ++ return 0; ++} ++ ++static struct gpio_chip reference_gc = { ++ .label = "mca-gpio", ++ .owner = THIS_MODULE, ++ .get = mca_gpio_get, ++ .set = mca_gpio_set, ++ .direction_input = mca_gpio_direction_input, ++ .direction_output = mca_gpio_direction_output, ++ .to_irq = mca_gpio_to_irq, ++ .set_config = mca_gpio_set_config, ++ .can_sleep = 1, ++ .base = -1, ++}; ++ ++static int mca_gpio_probe(struct platform_device *pdev) ++{ ++ struct mca_drv *mca = dev_get_drvdata(pdev->dev.parent); ++ struct device *mca_dev = mca->dev; ++ struct regmap *regmap = mca->regmap; ++ int *gpio_base = &mca->gpio_base; ++ struct mca_gpio *gpio; ++ struct device_node *np; ++ unsigned int val; ++ int ret, i; ++ ++ gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); ++ if (!gpio) { ++ dev_err(mca_dev, "Failed to allocate GPIO device\n"); ++ return -ENOMEM; ++ } ++ ++ if (!mca_dev) ++ return -EPROBE_DEFER; ++ ++ gpio->dev = mca_dev; ++ gpio->regmap = regmap; ++ ++ for (i = 0; i < MCA_MAX_GPIO_IRQ_BANKS; i++) { ++ gpio->irq[i] = platform_get_irq_byname(pdev, ++ irq_gpio_bank_name[i]); ++ } ++ gpio->gc = reference_gc; ++ gpio->gc.of_node = pdev->dev.of_node; ++ gpio->gc.parent = &pdev->dev; ++ platform_set_drvdata(pdev, gpio); ++ ++ /* Find entry in device-tree */ ++ if (mca_dev->of_node) { ++ const struct mca_gpio_data *devdata = ++ of_device_get_match_data(&pdev->dev); ++ const char * compatible = pdev->dev.driver-> ++ of_match_table[devdata->devtype].compatible; ++ ++ /* Return if node does not exist or if it is disabled */ ++ np = of_find_compatible_node(mca_dev->of_node, NULL, compatible); ++ if (!np) { ++ ret = -ENODEV; ++ goto err; ++ } ++ if (!of_device_is_available(np)) { ++ ret = -ENODEV; ++ goto err; ++ } ++ } ++ ++ /* Get number of GPIOs from MCA firmware */ ++ if (regmap_read(regmap, MCA_GPIO_NUM, &val)) { ++ ret = -EINVAL; ++ dev_err(mca_dev, "Could not read number of gpios.\n"); ++ goto err; ++ } ++ gpio->gc.ngpio = val & MCA_GPIO_NUM_MASK; ++ if (gpio->gc.ngpio < 1 || gpio->gc.ngpio > MCA_MAX_IOS) { ++ ret = -EINVAL; ++ dev_err(mca_dev, "Read invalid number of gpios (%d). " ++ "Valid range is 1..%d.\n", gpio->gc.ngpio, ++ MCA_MAX_IOS); ++ goto err; ++ } ++ ++ ret = gpiochip_add_data(&gpio->gc, gpio); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret); ++ goto err; ++ } ++ ++ ret = mca_gpio_irq_setup(gpio); ++ if (ret) { ++ gpiochip_remove(&gpio->gc); ++ goto err; ++ } ++ ++ if (gpio_base) ++ *gpio_base = gpio->gc.base; ++ ++ return 0; ++ ++err: ++ gpio = NULL; ++ return ret; ++} ++ ++static int mca_gpio_remove(struct platform_device *pdev) ++{ ++ struct mca_gpio *gpio = platform_get_drvdata(pdev); ++ struct mca_drv *mca = (struct mca_drv *)gpio->parent; /* TODO */ ++ ++ mca->gpio_base = -1; ++ gpiochip_remove(&gpio->gc); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_OF ++static struct mca_gpio_data mca_gpio_devdata[] = { ++ [CC6UL_MCA_GPIO] = { ++ .devtype = CC6UL_MCA_GPIO, ++ }, ++ [CC8X_MCA_GPIO] = { ++ .devtype = CC8X_MCA_GPIO, ++ }, ++ [IOEXP_MCA_GPIO] = { ++ .devtype = IOEXP_MCA_GPIO, ++ }, ++}; ++ ++static const struct of_device_id mca_gpio_dt_ids[] = { ++ { .compatible = "digi,mca-cc6ul-gpio", ++ .data = &mca_gpio_devdata[CC6UL_MCA_GPIO]}, ++ { .compatible = "digi,mca-cc8x-gpio", ++ .data = &mca_gpio_devdata[CC8X_MCA_GPIO]}, ++ { .compatible = "digi,mca-ioexp-gpio", ++ .data = &mca_gpio_devdata[IOEXP_MCA_GPIO]}, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, mca_gpio_dt_ids); ++#endif ++ ++static struct platform_driver mca_gpio_driver = { ++ .probe = mca_gpio_probe, ++ .remove = mca_gpio_remove, ++ .driver = { ++ .name = MCA_DRVNAME_GPIO, ++ .owner = THIS_MODULE, ++#ifdef CONFIG_OF ++ .of_match_table = mca_gpio_dt_ids, ++#endif ++ }, ++}; ++ ++static int __init mca_gpio_init(void) ++{ ++ return platform_driver_register(&mca_gpio_driver); ++} ++module_init(mca_gpio_init); ++ ++static void __exit mca_gpio_exit(void) ++{ ++ platform_driver_unregister(&mca_gpio_driver); ++} ++module_exit(mca_gpio_exit); ++ ++/* Module information */ ++MODULE_AUTHOR("Digi International Inc."); ++MODULE_DESCRIPTION("GPIO device driver for MCA of ConnectCore Modules"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:" MCA_DRVNAME_GPIO); ++ diff --git a/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ul/0005-imx6ul-Add-MCA-IOMUX-support-to-the-ConnectCore-6UL-.patch b/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ul/0005-imx6ul-Add-MCA-IOMUX-support-to-the-ConnectCore-6UL-.patch new file mode 100644 index 000000000..61f0d5167 --- /dev/null +++ b/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ul/0005-imx6ul-Add-MCA-IOMUX-support-to-the-ConnectCore-6UL-.patch @@ -0,0 +1,33 @@ +From 7350cbc80f98eacb84a67049c2181f758ca3add6 Mon Sep 17 00:00:00 2001 +From: Alex Gonzalez +Date: Fri, 15 Jun 2018 09:18:15 +0200 +Subject: [PATCH] imx6ul: Add MCA IOMUX support to the ConnectCore 6UL SOM + +Synched with v4.14.78/master at. +3f8b03950b323db4ca89b1cdc1c2288f79facaa3 + +Signed-off-by: Alex Gonzalez +--- + arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi b/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi +index 8d475051acf2..b96a0873cd88 100644 +--- a/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi ++++ b/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi +@@ -286,6 +286,15 @@ + MX6UL_PAD_SNVS_TAMPER9__GPIO5_IO09 0x3000 + >; + }; ++ ++ pinctrl_mca_cc6ul: mcagrp { ++ fsl,pins = < ++ /* MCA_nINT */ ++ MX6UL_PAD_SNVS_TAMPER4__GPIO5_IO04 0xb0b1 ++ /* MCA_FW_UPDATE */ ++ MX6UL_PAD_NAND_CE1_B__GPIO4_IO14 0x30 ++ >; ++ }; + }; + + ®_arm { diff --git a/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ul/0006-imx6ul-Add-MCA-watchdog-support-for-the-ConnectCore-.patch b/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ul/0006-imx6ul-Add-MCA-watchdog-support-for-the-ConnectCore-.patch new file mode 100644 index 000000000..abeae07e4 --- /dev/null +++ b/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ul/0006-imx6ul-Add-MCA-watchdog-support-for-the-ConnectCore-.patch @@ -0,0 +1,467 @@ +From 1440e9a7f2812ebe7ac1d74e8a3c7515bcd67fa8 Mon Sep 17 00:00:00 2001 +From: Alex Gonzalez +Date: Mon, 23 Apr 2018 11:39:40 +0200 +Subject: [PATCH] imx6ul: Add MCA watchdog support for the ConnectCore 6UL SOM + +Synched with v4.14.78/master at: +3f8b03950b323db4ca89b1cdc1c2288f79facaa3 + +Signed-off-by: Alex Gonzalez +--- + .../devicetree/bindings/watchdog/mca-cc6ul-wdt.txt | 22 ++ + arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi | 5 + + arch/arm/configs/imx_v6_v7_defconfig | 1 + + drivers/watchdog/Kconfig | 8 + + drivers/watchdog/Makefile | 1 + + drivers/watchdog/mca_wdt.c | 354 +++++++++++++++++++++ + 6 files changed, 391 insertions(+) + create mode 100644 Documentation/devicetree/bindings/watchdog/mca-cc6ul-wdt.txt + create mode 100644 drivers/watchdog/mca_wdt.c + +diff --git a/Documentation/devicetree/bindings/watchdog/mca-cc6ul-wdt.txt b/Documentation/devicetree/bindings/watchdog/mca-cc6ul-wdt.txt +new file mode 100644 +index 000000000000..c6dd2cfdd316 +--- /dev/null ++++ b/Documentation/devicetree/bindings/watchdog/mca-cc6ul-wdt.txt +@@ -0,0 +1,22 @@ ++* Digi Watchdog Timer for MCA of ConnectCore 6UL ++ ++Required properties: ++- compatible: must be "digi,mca-cc6ul-wdt". ++ ++Optional properties: ++- digi,timeout-sec: contains the watchdog timeout in seconds. ++- digi,irq-no-reset: if present, the watchdog will generate an interrupt instead ++ of a system reset. ++- digi,full-reset: if present, the watchdog will perform a full system reset, ++ including the MCA. Otherwise, only the microprocessor is reset. Note ++ that this option requires the system to be configured to generate a ++ reset and not an interrupt. ++ ++Example: ++ mca: mca-cc6ul@7e { ++ watchdog { ++ compatible = "digi,mca-cc6ul-wdt"; ++ digi,timeout-sec = <15>; ++ digi,irq-no-reset; ++ }; ++ }; +diff --git a/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi b/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi +index b96a0873cd88..848bf78dfceb 100644 +--- a/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi ++++ b/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi +@@ -69,6 +69,11 @@ + #interrupt-cells = <2>; + }; + ++ watchdog { ++ compatible = "digi,mca-cc6ul-wdt"; ++ digi,full-reset; ++ }; ++ + }; + + pfuze3000: pmic@8 { +diff --git a/arch/arm/configs/imx_v6_v7_defconfig b/arch/arm/configs/imx_v6_v7_defconfig +index 6c3ede35e643..9c693c7778a4 100644 +--- a/arch/arm/configs/imx_v6_v7_defconfig ++++ b/arch/arm/configs/imx_v6_v7_defconfig +@@ -226,6 +226,7 @@ CONFIG_IMX_THERMAL=y + CONFIG_WATCHDOG=y + CONFIG_DA9062_WATCHDOG=y + CONFIG_RN5T618_WATCHDOG=y ++CONFIG_MCA_WATCHDOG=y + CONFIG_IMX2_WDT=y + CONFIG_MFD_DA9052_I2C=y + CONFIG_MFD_DA9062=y +diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig +index 2d64333f4782..800821bfdd7d 100644 +--- a/drivers/watchdog/Kconfig ++++ b/drivers/watchdog/Kconfig +@@ -171,6 +171,14 @@ config MENZ069_WATCHDOG + This driver can also be built as a module. If so the module + will be called menz069_wdt. + ++config MCA_WATCHDOG ++ tristate "Digi ConnectCore SOMs Micro Controller Assist Watchdog" ++ select WATCHDOG_CORE ++ select MFD_MCA_CC6UL if SOC_IMX6UL ++ select MFD_MCA_CC8X if ARCH_FSL_IMX8QXP ++ help ++ If you say yes here you will get support for the watchdog in the Micro Controller Assist of Digi ConnectCore system-on-modules. ++ + config TANGOX_WATCHDOG + tristate "Sigma Designs SMP86xx/SMP87xx watchdog" + select WATCHDOG_CORE +diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile +index f69cdff5ad7f..36a275937216 100644 +--- a/drivers/watchdog/Makefile ++++ b/drivers/watchdog/Makefile +@@ -80,6 +80,7 @@ obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o + obj-$(CONFIG_MESON_GXBB_WATCHDOG) += meson_gxbb_wdt.o + obj-$(CONFIG_MESON_WATCHDOG) += meson_wdt.o + obj-$(CONFIG_MEDIATEK_WATCHDOG) += mtk_wdt.o ++obj-$(CONFIG_MCA_WATCHDOG) += mca_wdt.o + obj-$(CONFIG_DIGICOLOR_WATCHDOG) += digicolor_wdt.o + obj-$(CONFIG_LPC18XX_WATCHDOG) += lpc18xx_wdt.o + obj-$(CONFIG_BCM7038_WDT) += bcm7038_wdt.o +diff --git a/drivers/watchdog/mca_wdt.c b/drivers/watchdog/mca_wdt.c +new file mode 100644 +index 000000000000..7d5e8b303ed5 +--- /dev/null ++++ b/drivers/watchdog/mca_wdt.c +@@ -0,0 +1,354 @@ ++/* ++ * Watchdog driver for MCA on ConnectCore modules ++ * ++ * Copyright(c) 2016 - 2018 Digi International Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define MCA_DRVNAME_WATCHDOG "mca-watchdog" ++ ++#define WDT_REFRESH_LEN (MCA_WDT_REFRESH_3 - \ ++ MCA_WDT_REFRESH_0 + 1) ++#define WDT_REFRESH_PATTERN "WDTP" ++#define WATCHDOG_NAME "MCA Watchdog" ++#define DEFAULT_TIMEOUT 30 /* 30 sec default timeout */ ++ ++#ifdef CONFIG_OF ++enum mca_wdt_type { ++ CC6UL_MCA_WDT, ++ CC8X_MCA_WDT, ++}; ++ ++struct mca_wdt_data { ++ enum mca_wdt_type devtype; ++}; ++#endif ++ ++struct mca_wdt { ++ struct watchdog_device wdd; ++ struct mca_drv *mca; ++ struct kref kref; ++ unsigned int default_timeout; ++ bool irqnoreset; ++ bool nowayout; ++ bool fullreset; ++ unsigned int irq_timeout; ++}; ++ ++static bool nowayout = WATCHDOG_NOWAYOUT; ++module_param(nowayout, bool, 0); ++MODULE_PARM_DESC(nowayout, ++ "Watchdog cannot be stopped once started (default=" ++ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); ++ ++static int mca_wdt_set_timeout(struct watchdog_device *wdd, ++ unsigned int timeout) ++{ ++ struct mca_wdt *wdt = watchdog_get_drvdata(wdd); ++ struct mca_drv *mca = wdt->mca; ++ int ret; ++ ++ if (timeout < wdt->wdd.min_timeout || ++ timeout > wdt->wdd.max_timeout) { ++ ret = -EINVAL; ++ } else { ++ ret = regmap_write(mca->regmap, MCA_WDT_TIMEOUT, timeout); ++ } ++ ++ if (ret < 0) { ++ dev_err(mca->dev, "Failed to set timeout, %d\n", ret); ++ return ret; ++ } ++ ++ wdd->timeout = timeout; ++ ++ return 0; ++} ++ ++static int mca_config_options(struct mca_wdt *wdt) ++{ ++ int ret = 0; ++ u8 control = 0; ++ ++ control |= wdt->nowayout ? MCA_WDT_NOWAYOUT : 0; ++ control |= wdt->irqnoreset ? MCA_WDT_IRQNORESET : 0; ++ control |= wdt->fullreset ? MCA_WDT_FULLRESET : 0; ++ ++ ret = regmap_update_bits(wdt->mca->regmap, MCA_WDT_CONTROL, ++ MCA_WDT_NOWAYOUT | MCA_WDT_IRQNORESET | ++ MCA_WDT_FULLRESET, control); ++ if (ret) ++ goto err; ++ ++ /* Set timeout */ ++ ret = mca_wdt_set_timeout(&wdt->wdd, wdt->default_timeout); ++ if (ret) { ++ dev_err(wdt->mca->dev, "Could not set watchdog timeout (%d)\n", ++ ret); ++ goto err; ++ } ++ ++err: ++ return ret; ++} ++ ++static int mca_wdt_ping(struct watchdog_device *wdd) ++{ ++ struct mca_wdt *wdt = watchdog_get_drvdata(wdd); ++ struct mca_drv *mca = wdt->mca; ++ const char *pattern = WDT_REFRESH_PATTERN; ++ ++ /* ++ * Refresh the watchdog timer by writing refresh pattern to REFRESH_x ++ * registers ++ */ ++ return regmap_bulk_write(mca->regmap, MCA_WDT_REFRESH_0, ++ pattern, WDT_REFRESH_LEN); ++} ++ ++static void mca_wdt_release_resources(struct kref *r) ++{ ++} ++ ++static int mca_wdt_start(struct watchdog_device *wdd) ++{ ++ struct mca_wdt *wdt = watchdog_get_drvdata(wdd); ++ int ret = 0; ++ ++ /* Enable watchdog */ ++ ret = regmap_update_bits(wdt->mca->regmap, MCA_WDT_CONTROL, ++ MCA_WDT_ENABLE, MCA_WDT_ENABLE); ++ if (ret) { ++ dev_err(wdt->mca->dev, "Could not enable watchdog (%d)\n", ret); ++ goto err; ++ } ++ ++err: ++ return ret; ++} ++ ++static int mca_wdt_stop(struct watchdog_device *wdd) ++{ ++ struct mca_wdt *wdt = watchdog_get_drvdata(wdd); ++ ++ /* Disable watchdog */ ++ return regmap_update_bits(wdt->mca->regmap, MCA_WDT_CONTROL, ++ MCA_WDT_ENABLE, 0); ++} ++ ++static irqreturn_t mca_wdt_timeout_event(int irq, void *data) ++{ ++ return IRQ_HANDLED; ++} ++ ++static struct watchdog_info mca_wdt_info = { ++ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | \ ++ WDIOF_MAGICCLOSE, ++ .identity = WATCHDOG_NAME, ++}; ++ ++static const struct watchdog_ops mca_wdt_ops = { ++ .owner = THIS_MODULE, ++ .start = mca_wdt_start, ++ .stop = mca_wdt_stop, ++ .ping = mca_wdt_ping, ++ .set_timeout = mca_wdt_set_timeout, ++}; ++ ++static int of_mca_wdt_init(struct device_node *np, ++ struct mca_wdt *wdt) ++{ ++ unsigned int timeout; ++ ++ /* parse options */ ++ wdt->irqnoreset = of_property_read_bool(np, "digi,irq-no-reset"); ++ wdt->fullreset = of_property_read_bool(np, "digi,full-reset"); ++ ++ if (!of_property_read_u32_index(np, "digi,timeout-sec", 0, &timeout)) { ++ if (timeout < wdt->wdd.min_timeout || ++ timeout > wdt->wdd.max_timeout) ++ dev_warn(wdt->mca->dev, ++ "Invalid timeout-sec value. Using default.\n"); ++ else ++ wdt->default_timeout = timeout; ++ } ++ ++ return 0; ++} ++ ++static int mca_wdt_probe(struct platform_device *pdev) ++{ ++ struct mca_drv *mca = dev_get_drvdata(pdev->dev.parent); ++ struct mca_wdt *wdt; ++ const struct mca_wdt_data *devdata = (struct mca_wdt_data *)pdev->id_entry->driver_data; ++ struct device_node *np; ++ int ret; ++ ++ wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); ++ if (!wdt) { ++ dev_err(mca->dev, "Failed to allocate watchdog device\n"); ++ return -ENOMEM; ++ } ++ ++ wdt->mca = mca; ++ wdt->default_timeout = DEFAULT_TIMEOUT; ++ wdt->nowayout = nowayout; ++ wdt->wdd.min_timeout = 0; ++ wdt->wdd.max_timeout = 0xff; ++ wdt->wdd.info = &mca_wdt_info; ++ wdt->wdd.ops = &mca_wdt_ops; ++ wdt->wdd.parent = &pdev->dev; ++ ++ watchdog_set_drvdata(&wdt->wdd, wdt); ++ kref_init(&wdt->kref); ++ platform_set_drvdata(pdev, wdt); ++ ++ /* Find entry in device-tree */ ++ if (mca->dev->of_node) { ++ const char * compatible = pdev->dev.driver-> ++ of_match_table[devdata->devtype].compatible; ++ ++ /* ++ * Return silently if watchdog node does not exist ++ * or if it is disabled ++ */ ++ np = of_find_compatible_node(mca->dev->of_node, NULL, compatible); ++ if (!np) { ++ ret = -ENODEV; ++ goto err; ++ } ++ if (!of_device_is_available(np)) { ++ ret = -ENODEV; ++ goto err; ++ } ++ ++ /* Parse DT properties */ ++ ret = of_mca_wdt_init(np, wdt); ++ if (ret) ++ goto err; ++ } ++ ++ /* Configure WDT options */ ++ ret = mca_config_options(wdt); ++ if (ret) { ++ dev_err(&pdev->dev, "Failed to configure WDT options\n"); ++ goto err; ++ } ++ ++ /* Set nowayout option into watchdog device */ ++ watchdog_set_nowayout(&wdt->wdd, nowayout); ++ ++ /* Register interrupt if so configured */ ++ if (wdt->irqnoreset) { ++ wdt->irq_timeout = platform_get_irq_byname(pdev, ++ MCA_IRQ_WATCHDOG_NAME); ++ ret = devm_request_threaded_irq(&pdev->dev, wdt->irq_timeout, ++ NULL, mca_wdt_timeout_event, ++ IRQF_TRIGGER_LOW | IRQF_ONESHOT, ++ MCA_IRQ_WATCHDOG_NAME, wdt); ++ if (ret) { ++ dev_err(&pdev->dev, ++ "Failed to request %s IRQ. (%d)\n", ++ MCA_IRQ_WATCHDOG_NAME, wdt->irq_timeout); ++ wdt->irq_timeout = -ENXIO; ++ goto err; ++ } ++ } ++ ++ ret = watchdog_register_device(&wdt->wdd); ++ if (ret != 0) { ++ dev_err(wdt->mca->dev, ++ "watchdog_register_device() failed: %d\n", ret); ++ goto err; ++ } ++ ++ pr_info("Watchdog driver for MCA (timeout=%d sec, nowayout=%d, %s%s)\n", ++ wdt->default_timeout, nowayout, ++ wdt->irqnoreset ? "interrupt (no reset)" : "reset", ++ wdt->irqnoreset ? "" : wdt->fullreset ? " (full)" : " (MPU only)"); ++ return 0; ++ ++err: ++ wdt = NULL; ++ return ret; ++} ++ ++static int mca_wdt_remove(struct platform_device *pdev) ++{ ++ struct mca_wdt *wdt = platform_get_drvdata(pdev); ++ ++ if(wdt->irq_timeout) ++ devm_free_irq(&pdev->dev, wdt->irq_timeout, wdt); ++ watchdog_unregister_device(&wdt->wdd); ++ kref_put(&wdt->kref, mca_wdt_release_resources); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_OF ++static struct mca_wdt_data mca_wdt_devdata[] = { ++ [CC6UL_MCA_WDT] = { ++ .devtype = CC6UL_MCA_WDT, ++ }, ++ [CC8X_MCA_WDT] = { ++ .devtype = CC8X_MCA_WDT, ++ }, ++}; ++ ++static const struct platform_device_id mca_wdt_devtype[] = { ++ { ++ .name = "mca-cc6ul-watchdog", ++ .driver_data = (kernel_ulong_t)&mca_wdt_devdata[CC6UL_MCA_WDT], ++ }, { ++ .name = "mca-cc8x-watchdog", ++ .driver_data = (kernel_ulong_t)&mca_wdt_devdata[CC8X_MCA_WDT], ++ }, { ++ /* sentinel */ ++ } ++}; ++MODULE_DEVICE_TABLE(platform, mca_wdt_devtype); ++ ++static const struct of_device_id mca_wdt_match[] = { ++ { .compatible = "digi,mca-cc6ul-wdt", ++ .data = &mca_wdt_devdata[CC6UL_MCA_WDT]}, ++ { .compatible = "digi,mca-cc8x-wdt", ++ .data = &mca_wdt_devdata[CC8X_MCA_WDT]}, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, mca_wdt_match); ++#endif ++ ++static struct platform_driver mca_wdt_driver = { ++ .probe = mca_wdt_probe, ++ .remove = mca_wdt_remove, ++ .id_table = mca_wdt_devtype, ++ .driver = { ++ .name = MCA_DRVNAME_WATCHDOG, ++ .of_match_table = of_match_ptr(mca_wdt_match), ++ }, ++}; ++ ++module_platform_driver(mca_wdt_driver); ++ ++MODULE_AUTHOR("Digi International Inc."); ++MODULE_DESCRIPTION("Watchdog device driver for MCA of ConnectCore Modules"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:" MCA_DRVNAME_WATCHDOG); diff --git a/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ul/0007-imx6ul-Add-MCA-ADC-support-for-ConnectCore-6UL-SOM-a.patch b/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ul/0007-imx6ul-Add-MCA-ADC-support-for-ConnectCore-6UL-SOM-a.patch new file mode 100644 index 000000000..bc8fb9413 --- /dev/null +++ b/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ul/0007-imx6ul-Add-MCA-ADC-support-for-ConnectCore-6UL-SOM-a.patch @@ -0,0 +1,1066 @@ +From 01ad85bbe21de97cf560825d85523717e82e6979 Mon Sep 17 00:00:00 2001 +From: Alex Gonzalez +Date: Mon, 23 Apr 2018 11:40:45 +0200 +Subject: [PATCH] imx6ul: Add MCA ADC support for ConnectCore 6UL SOM and SBCs + +Synched with v4.14.78/master at: +3f8b03950b323db4ca89b1cdc1c2288f79facaa3 + +Signed-off-by: Alex Gonzalez +--- + arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts | 11 + + arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi | 5 + + arch/arm/configs/imx_v6_v7_defconfig | 1 + + drivers/iio/adc/Kconfig | 11 + + drivers/iio/adc/Makefile | 1 + + drivers/iio/adc/mca-adc.c | 957 ++++++++++++++++++++++++++++ + 6 files changed, 986 insertions(+) + create mode 100644 drivers/iio/adc/mca-adc.c + +diff --git a/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts b/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts +index 5ad2c61276bc..acea9a56971e 100644 +--- a/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts ++++ b/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts +@@ -151,6 +151,17 @@ + regulator-max-microvolt = <1800000>; + }; + ++/* ++ * Enable MCA ADC channels on GPIO connector: ++ * - MCA_IO1 (channel 1) ++ * - MCA_IO3 (channel 3) ++ * Edit adc-ch-list to include the ADC channels that you want to enable. ++ */ ++&mca_adc { ++ digi,adc-ch-list = <1 3>; ++ digi,adc-vref = <3000000>; ++}; ++ + &pwm1 { + status = "okay"; + }; +diff --git a/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi b/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi +index 848bf78dfceb..b5efc58d362d 100644 +--- a/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi ++++ b/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi +@@ -74,6 +74,11 @@ + digi,full-reset; + }; + ++ mca_adc: adc { ++ compatible = "digi,mca-cc6ul-adc"; ++ digi,adc-vref = <3000000>; ++ }; ++ + }; + + pfuze3000: pmic@8 { +diff --git a/arch/arm/configs/imx_v6_v7_defconfig b/arch/arm/configs/imx_v6_v7_defconfig +index 9c693c7778a4..060c1de356ca 100644 +--- a/arch/arm/configs/imx_v6_v7_defconfig ++++ b/arch/arm/configs/imx_v6_v7_defconfig +@@ -385,6 +385,7 @@ CONFIG_COMMON_CLK_PWM=y + CONFIG_IIO=y + CONFIG_MMA8452=y + CONFIG_IMX7D_ADC=y ++CONFIG_MCA_ADC=y + CONFIG_VF610_ADC=y + CONFIG_SENSORS_ISL29018=y + CONFIG_MAG3110=y +diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig +index a52fea8749a9..5fe1cd036999 100644 +--- a/drivers/iio/adc/Kconfig ++++ b/drivers/iio/adc/Kconfig +@@ -468,6 +468,17 @@ config MAX1363 + To compile this driver as a module, choose M here: the module will be + called max1363. + ++config MCA_ADC ++ tristate "Digi ConnectCore SOMs Micro Controller Assist ADC" ++ select MFD_MCA_CC6UL if SOC_IMX6UL ++ select MFD_MCA_CC8X if ARCH_FSL_IMX8QXP ++ help ++ If you say Y here you will get support for the ADCs in the Micro ++ Controller Assist of Digi ConnectCore system-on-modules. ++ ++ This driver can also be built as a module, if so, the module ++ will be called "mca-adc". ++ + config MAX9611 + tristate "Maxim max9611/max9612 ADC driver" + depends on I2C +diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile +index a6e6a0b659e2..f132f8639002 100644 +--- a/drivers/iio/adc/Makefile ++++ b/drivers/iio/adc/Makefile +@@ -44,6 +44,7 @@ obj-$(CONFIG_MAX1027) += max1027.o + obj-$(CONFIG_MAX11100) += max11100.o + obj-$(CONFIG_MAX1118) += max1118.o + obj-$(CONFIG_MAX1363) += max1363.o ++obj-$(CONFIG_MCA_ADC) += mca-adc.o + obj-$(CONFIG_MAX9611) += max9611.o + obj-$(CONFIG_MCP320X) += mcp320x.o + obj-$(CONFIG_MCP3422) += mcp3422.o +diff --git a/drivers/iio/adc/mca-adc.c b/drivers/iio/adc/mca-adc.c +new file mode 100644 +index 000000000000..24dde433da20 +--- /dev/null ++++ b/drivers/iio/adc/mca-adc.c +@@ -0,0 +1,957 @@ ++/* mca-adc.c - ADC driver for MCA devices. ++ * ++ * Copyright (C) 2017 - 2019 Digi International Inc ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Library General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Library General Public License for more details. ++ * ++ * You should have received a copy of the GNU Library General Public ++ * License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define MCA_VREF_uV(v) ((u32)((v) * 1000 * 1000)) ++#define MCA_ADC_MIN_VREF MCA_VREF_uV(1.8) ++#define MCA_ADC_MAX_VREF MCA_VREF_uV(3.3) ++#define MCA_ADC_INT_VREF MCA_VREF_uV(1.2) ++#define MCA_ADC_DEF_VREF MCA_VREF_uV(3) ++ ++#define MCA_DRVNAME_ADC "mca-adc" ++ ++#ifdef CONFIG_OF ++enum mca_adc_type { ++ CC6UL_MCA_ADC, ++ CC8X_MCA_ADC, ++ IOEXP_MCA_ADC, ++}; ++ ++struct mca_adc_data { ++ enum mca_adc_type devtype; ++}; ++#endif ++ ++struct mca_adc { ++ struct device *dev; ++ struct regmap *regmap; ++ u32 vref; ++ int irq; ++}; ++ ++int mca_adc_read_raw(struct iio_dev *indio_dev, ++ struct iio_chan_spec const *channel, int *value, ++ int *shift, long mask) ++{ ++ struct mca_adc *adc = iio_priv(indio_dev); ++ const int val_reg = MCA_REG_ADC_VAL_L_0 + channel->channel * 2; ++ u16 val; ++ int ret; ++ ++ switch (mask) { ++ case IIO_CHAN_INFO_RAW: ++ ret = regmap_bulk_read(adc->regmap, val_reg, &val, sizeof(val)); ++ if (ret < 0) { ++ dev_err(adc->dev, "Error reading ADC%d value (%d)\n", ++ channel->channel, ret); ++ return ret; ++ } ++ ++ dev_dbg(adc->dev, "ADC%d = 0x%04x\n", channel->channel, val); ++ *value = val; ++ return IIO_VAL_INT; ++ ++ case IIO_CHAN_INFO_SCALE: ++ *value = adc->vref / 1000; ++ *shift = channel->scan_type.realbits; ++ return IIO_VAL_FRACTIONAL_LOG2; ++ ++ default: ++ break; ++ } ++ ++ return -EINVAL; ++} ++ ++static const struct iio_info mca_adc_info = { ++ .read_raw = mca_adc_read_raw, ++}; ++ ++static u64 generate_comparator_event(struct mca_adc *adc, int ch) ++{ ++ ++ const u8 rising_mask = (MCA_REG_ADC_CFG1_EN_RISING | ++ MCA_REG_ADC_CFG1_RISING_MASK); ++ const u8 falling_mask = (MCA_REG_ADC_CFG1_EN_FALLING | ++ MCA_REG_ADC_CFG1_FALLING_MASK); ++ const u8 both_mask = rising_mask | falling_mask; ++ ++ u16 cfg1_base = ch < 32 ? MCA_REG_ADC_CFG1_0 : MCA_REG_ADC_CFG1_32; ++ u8 ch_offset = ch < 32 ? ch : ch - 32; ++ u8 cfg1; ++ enum iio_event_direction edge; ++ int ret; ++ ++ ret = regmap_raw_read(adc->regmap, cfg1_base + ch_offset, &cfg1, ++ sizeof(cfg1)); ++ if (ret) { ++ dev_err(adc->dev, "Error reading ADC%d CFG0 register (%d)\n", ++ ch, ret); ++ return ~0; ++ } ++ ++ if ((cfg1 & both_mask) == both_mask) ++ edge = IIO_EV_DIR_EITHER; ++ else if ((cfg1 & rising_mask) == rising_mask) ++ edge = IIO_EV_DIR_RISING; ++ else if ((cfg1 & falling_mask) == falling_mask) ++ edge = IIO_EV_DIR_FALLING; ++ else ++ return ~0; ++ ++ return IIO_EVENT_CODE(IIO_ACTIVITY, 0, IIO_NO_MOD, edge, ++ IIO_EV_TYPE_CHANGE, ch, 0, 0); ++} ++ ++static irqreturn_t comparator_irq_handler(int irq, void *private) ++{ ++ struct iio_dev *indio_dev = private; ++ struct mca_adc *adc = iio_priv(indio_dev); ++ int ret; ++ int i; ++ u8 adc_irqs[8]; ++ ++ ret = regmap_bulk_read(adc->regmap, MCA_REG_ADC_IRQ_0, ++ &adc_irqs, ARRAY_SIZE(adc_irqs)); ++ if (ret) { ++ dev_err(adc->dev, "Error reading ADC IRQ registers (%d)\n", ++ ret); ++ goto exit; ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(adc_irqs); i++) { ++ const u8 adc_irq = adc_irqs[i]; ++ int j; ++ ++ for (j = 0; j < 8; j++) { ++ const u8 mask = 1 << j; ++ ++ if (mask & adc_irq) { ++ u8 ch = i * 8 + j; ++ u64 event = generate_comparator_event(adc, ch); ++ ++ if (event == ~0) ++ continue; ++ /* Notify the event */ ++ iio_push_event(indio_dev, event, ++ iio_get_time_ns(indio_dev)); ++ } ++ } ++ /* ACK the IRQs by writing a 1 in the flags */ ++ ret = regmap_write(adc->regmap, MCA_REG_ADC_IRQ_0 + i, adc_irq); ++ if (ret) { ++ dev_err(adc->dev, "Error ACKing IRQ %d (%d)\n", ++ i, ret); ++ continue; ++ } ++ } ++ ++exit: ++ return IRQ_HANDLED; ++} ++ ++static const struct iio_event_spec comparator_events[] = { ++ { ++ .type = IIO_EV_TYPE_THRESH, ++ .dir = IIO_EV_DIR_RISING, ++ .mask_separate = BIT(IIO_EV_INFO_VALUE) | ++ BIT(IIO_EV_INFO_ENABLE), ++ }, ++ { ++ .type = IIO_EV_TYPE_THRESH, ++ .dir = IIO_EV_DIR_FALLING, ++ .mask_separate = BIT(IIO_EV_INFO_VALUE) | ++ BIT(IIO_EV_INFO_ENABLE), ++ }, ++ { ++ .type = IIO_EV_TYPE_THRESH, ++ .dir = IIO_EV_DIR_EITHER, ++ .mask_separate = BIT(IIO_EV_INFO_VALUE) | ++ BIT(IIO_EV_INFO_ENABLE), ++ }, ++}; ++ ++static ssize_t cmp_thr_lo_read(struct iio_dev *indio_dev, ++ uintptr_t private, ++ struct iio_chan_spec const *iio_ch, ++ char *buf) ++{ ++ struct mca_adc *adc = iio_priv(indio_dev); ++ int ch = iio_ch->channel; ++ u16 threshold; ++ int ret; ++ ++ ret = regmap_bulk_read(adc->regmap, MCA_REG_ADC_THRESH_LO_L_0 + ch * 2, ++ &threshold, sizeof(threshold)); ++ if (ret) ++ dev_err(adc->dev, ++ "Error reading MCA_REG_ADC_THRESH_LO_L_%d (%d)\n", ++ ch, ret); ++ ++ return sprintf(buf, "%d\n", threshold); ++} ++ ++static ssize_t cmp_thr_lo_write(struct iio_dev *indio_dev, ++ uintptr_t private, ++ struct iio_chan_spec const *iio_ch, ++ const char *buf, ++ size_t len) ++{ ++ struct mca_adc *adc = iio_priv(indio_dev); ++ int ch = iio_ch->channel; ++ unsigned long threshold; ++ int ret; ++ ++ ret = kstrtoul(buf, 0, &threshold); ++ if (ret) { ++ dev_err(adc->dev, ++ "%s: error parsing input (%s)\n", ++ __func__, buf); ++ return ret; ++ } ++ ++ ret = regmap_bulk_write(adc->regmap, MCA_REG_ADC_THRESH_LO_L_0 + ch * 2, ++ (u16 *)&threshold, sizeof(u16)); ++ if (ret) ++ dev_err(adc->dev, ++ "Error writing MCA_REG_ADC_THRESH_LO_L_%d (%d)\n", ++ ch, ret); ++ ++ return len; ++} ++ ++static ssize_t cmp_thr_hi_read(struct iio_dev *indio_dev, ++ uintptr_t private, ++ struct iio_chan_spec const *iio_ch, ++ char *buf) ++{ ++ struct mca_adc *adc = iio_priv(indio_dev); ++ int ch = iio_ch->channel; ++ u16 threshold; ++ int ret; ++ ++ ret = regmap_bulk_read(adc->regmap, MCA_REG_ADC_THRESH_HI_L_0 + ch * 2, ++ &threshold, sizeof(threshold)); ++ if (ret) ++ dev_err(adc->dev, ++ "Error reading MCA_REG_ADC_THRESH_HI_L_%d (%d)\n", ++ ch, ret); ++ ++ return sprintf(buf, "%d\n", threshold); ++} ++ ++static ssize_t cmp_thr_hi_write(struct iio_dev *indio_dev, ++ uintptr_t private, ++ struct iio_chan_spec const *iio_ch, ++ const char *buf, ++ size_t len) ++{ ++ struct mca_adc *adc = iio_priv(indio_dev); ++ int ch = iio_ch->channel; ++ unsigned long threshold; ++ int ret; ++ ++ ret = kstrtoul(buf, 0, &threshold); ++ if (ret) { ++ dev_err(adc->dev, ++ "%s: error parsing input (%s)\n", ++ __func__, buf); ++ return ret; ++ } ++ ++ ret = regmap_bulk_write(adc->regmap, MCA_REG_ADC_THRESH_HI_L_0 + ch * 2, ++ (u16 *)&threshold, sizeof(u16)); ++ if (ret) ++ dev_err(adc->dev, ++ "Error writing MCA_REG_ADC_THRESH_HI_L_%d (%d)\n", ++ ch, ret); ++ ++ return len; ++} ++ ++static ssize_t cmp_sample_rate_read(struct iio_dev *indio_dev, ++ uintptr_t private, ++ struct iio_chan_spec const *iio_ch, ++ char *buf) ++{ ++ struct mca_adc *adc = iio_priv(indio_dev); ++ int ch = iio_ch->channel; ++ u16 ticks; ++ int ret; ++ ++ ret = regmap_bulk_read(adc->regmap, MCA_REG_ADC_TICKS_L_0 + ch * 2, ++ &ticks, sizeof(ticks)); ++ if (ret) ++ dev_err(adc->dev, "Error reading MCA_REG_ADC_TICKS_L_%d (%d)\n", ++ ch, ret); ++ ++ return sprintf(buf, "%d\n", ticks); ++} ++ ++static ssize_t cmp_sample_rate_write(struct iio_dev *indio_dev, ++ uintptr_t private, ++ struct iio_chan_spec const *iio_ch, ++ const char *buf, ++ size_t len) ++{ ++ struct mca_adc *adc = iio_priv(indio_dev); ++ int ch = iio_ch->channel; ++ unsigned long ticks; ++ int ret; ++ ++ ret = kstrtoul(buf, 0, &ticks); ++ if (ret) { ++ dev_err(adc->dev, ++ "%s: error parsing input (%s)\n", ++ __func__, buf); ++ return ret; ++ } ++ ++ ret = regmap_bulk_write(adc->regmap, MCA_REG_ADC_TICKS_L_0 + ch * 2, ++ (u16 *)&ticks, sizeof(u16)); ++ if (ret) ++ dev_err(adc->dev, "Error writing MCA_REG_ADC_TICKS_L_%d (%d)\n", ++ ch, ret); ++ ++ return len; ++} ++ ++static ssize_t cmp_irq_edge_read(struct iio_dev *indio_dev, ++ uintptr_t private, ++ struct iio_chan_spec const *iio_ch, ++ char *buf) ++{ ++ struct mca_adc *adc = iio_priv(indio_dev); ++ int ch = iio_ch->channel; ++ unsigned int cfg0; ++ int ret; ++ char const *edge_str; ++ ++ ret = regmap_read(adc->regmap, MCA_REG_ADC_CFG0_0 + ch, &cfg0); ++ if (ret) ++ dev_err(adc->dev, "Error reading MCA_REG_ADC_CFG0_%d (%d)\n", ++ ch, ret); ++ ++ if (cfg0 & MCA_REG_ADC_CFG0_IRQ_EN) { ++ unsigned int cfg1; ++ ++ ret = regmap_read(adc->regmap, MCA_REG_ADC_CFG1_0 + ch, &cfg1); ++ if (ret) ++ dev_err(adc->dev, ++ "Error reading MCA_REG_ADC_CFG1_%d (%d)\n", ++ ch, ret); ++ ++ if ((cfg1 & MCA_REG_ADC_CFG1_EN_RISING) && ++ (cfg1 & MCA_REG_ADC_CFG1_EN_FALLING)) ++ edge_str = "both"; ++ else if (cfg1 & MCA_REG_ADC_CFG1_EN_RISING) ++ edge_str = "rising"; ++ else if (cfg1 & MCA_REG_ADC_CFG1_EN_FALLING) ++ edge_str = "falling"; ++ else ++ edge_str = "none"; ++ } else { ++ edge_str = "none"; ++ } ++ ++ return sprintf(buf, "%s\n", edge_str); ++} ++ ++static ssize_t cmp_irq_edge_write(struct iio_dev *indio_dev, ++ uintptr_t private, ++ struct iio_chan_spec const *iio_ch, ++ const char *buf, ++ size_t len) ++{ ++ struct mca_adc *adc = iio_priv(indio_dev); ++ int ch = iio_ch->channel; ++ int ret; ++ unsigned int cfg1_val; ++ ++ if (!strncmp(buf, "both", strlen("both"))) ++ cfg1_val = MCA_REG_ADC_CFG1_EN_RISING | ++ MCA_REG_ADC_CFG1_EN_FALLING; ++ else if (!strncmp(buf, "rising", strlen("rising"))) ++ cfg1_val = MCA_REG_ADC_CFG1_EN_RISING; ++ else if (!strncmp(buf, "falling", strlen("falling"))) ++ cfg1_val = MCA_REG_ADC_CFG1_EN_FALLING; ++ else if (!strncmp(buf, "none", strlen("none"))) ++ cfg1_val = 0; ++ else ++ return -EINVAL; ++ ++ ret = regmap_update_bits(adc->regmap, MCA_REG_ADC_CFG1_0 + ch, ++ MCA_REG_ADC_CFG1_EN_RISING | ++ MCA_REG_ADC_CFG1_EN_FALLING, ++ cfg1_val); ++ if (ret) ++ dev_err(adc->dev, "Error writing MCA_REG_ADC_CFG1_%d (%d)\n", ++ ch, ret); ++ ++ /* Enable the IRQ if an edge is configured */ ++ ret = regmap_update_bits(adc->regmap, MCA_REG_ADC_CFG0_0 + ch, ++ MCA_REG_ADC_CFG0_IRQ_EN, ++ cfg1_val ? MCA_REG_ADC_CFG0_IRQ_EN : 0); ++ ++ if (ret) ++ dev_err(adc->dev, "Error writing MCA_REG_ADC_CFG0_%d (%d)\n", ++ ch, ret); ++ ++ return len; ++} ++ ++static ssize_t wakeup_read(struct iio_dev *indio_dev, ++ uintptr_t private, ++ struct iio_chan_spec const *iio_ch, ++ char *buf) ++{ ++ struct mca_adc *adc = iio_priv(indio_dev); ++ int ch = iio_ch->channel; ++ unsigned int cfg0; ++ int ret; ++ ++ ret = regmap_read(adc->regmap, MCA_REG_ADC_CFG0_0 + ch, &cfg0); ++ if (ret) ++ dev_err(adc->dev, "Error reading MCA_REG_ADC_CFG0_%d (%d)\n", ++ ch, ret); ++ ++ return sprintf(buf, "%s\n", ++ !!(cfg0 & MCA_REG_ADC_CFG0_RUNS_LP) ? ++ "enabled" : "disabled"); ++ ++} ++ ++static ssize_t wakeup_write(struct iio_dev *indio_dev, ++ uintptr_t private, ++ struct iio_chan_spec const *iio_ch, ++ const char *buf, ++ size_t len) ++{ ++ struct mca_adc *adc = iio_priv(indio_dev); ++ int ch = iio_ch->channel; ++ int ret; ++ ++ if (!strncmp(buf, "enabled", strlen("enabled"))) ++ ret = regmap_update_bits(adc->regmap, MCA_REG_ADC_CFG0_0 + ch, ++ MCA_REG_ADC_CFG0_RUNS_LP, ++ MCA_REG_ADC_CFG0_RUNS_LP); ++ else if (!strncmp(buf, "disabled", strlen("disabled"))) ++ ret = regmap_update_bits(adc->regmap, MCA_REG_ADC_CFG0_0 + ch, ++ MCA_REG_ADC_CFG0_RUNS_LP, ++ 0); ++ else ++ return -EINVAL; ++ ++ if (ret) ++ dev_err(adc->dev, "Error reading MCA_REG_ADC_CFG0_%d (%d)\n", ++ ch, ret); ++ ++ return len; ++} ++ ++static ssize_t averager_read(struct iio_dev *indio_dev, ++ uintptr_t private, ++ struct iio_chan_spec const *iio_ch, ++ char *buf) ++{ ++ struct mca_adc *adc = iio_priv(indio_dev); ++ int ch = iio_ch->channel; ++ u8 averager_samples; ++ unsigned int cfg0; ++ int ret; ++ ++ ret = regmap_read(adc->regmap, MCA_REG_ADC_CFG0_0 + ch, &cfg0); ++ if (ret) ++ dev_err(adc->dev, "Error reading MCA_REG_ADC_CFG0_%d (%d)\n", ++ ch, ret); ++ ++ averager_samples = (cfg0 & MCA_REG_ADC_CFG0_AVG_MASK) >> ++ MCA_REG_ADC_CFG0_AVG_SHIFT; ++ averager_samples = 1 << averager_samples; ++ ++ return sprintf(buf, "%d\n", averager_samples); ++} ++ ++static ssize_t averager_write(struct iio_dev *indio_dev, ++ uintptr_t private, ++ struct iio_chan_spec const *iio_ch, ++ const char *buf, ++ size_t len) ++{ ++ struct mca_adc *adc = iio_priv(indio_dev); ++ int ch = iio_ch->channel; ++ unsigned long averager_samples; ++ unsigned int cfg0_val; ++ int ret; ++ ++ ret = kstrtoul(buf, 0, &averager_samples); ++ if (ret) { ++ dev_err(adc->dev, ++ "%s: error parsing input (%s)\n", ++ __func__, buf); ++ return ret; ++ } ++ ++ switch (averager_samples) { ++ case 1: ++ cfg0_val = 0 << MCA_REG_ADC_CFG0_AVG_SHIFT; ++ break; ++ case 2: ++ cfg0_val = 1 << MCA_REG_ADC_CFG0_AVG_SHIFT; ++ break; ++ case 4: ++ cfg0_val = 2 << MCA_REG_ADC_CFG0_AVG_SHIFT; ++ break; ++ case 8: ++ cfg0_val = 3 << MCA_REG_ADC_CFG0_AVG_SHIFT; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ ret = regmap_update_bits(adc->regmap, MCA_REG_ADC_CFG0_0 + ch, ++ MCA_REG_ADC_CFG0_AVG_MASK, ++ cfg0_val); ++ if (ret) ++ dev_err(adc->dev, "Error reading MCA_REG_ADC_CFG0_%d (%d)\n", ++ ch, ret); ++ return len; ++} ++ ++static ssize_t cmp_out_read(struct iio_dev *indio_dev, ++ uintptr_t private, ++ struct iio_chan_spec const *iio_ch, ++ char *buf) ++{ ++ struct mca_adc *adc = iio_priv(indio_dev); ++ int ch = iio_ch->channel; ++ unsigned int cfg1; ++ int ret; ++ ++ ret = regmap_read(adc->regmap, MCA_REG_ADC_CFG1_0 + ch, &cfg1); ++ if (ret) ++ dev_err(adc->dev, "Error reading MCA_REG_ADC_CFG1_%d (%d)\n", ++ ch, ret); ++ ++ return sprintf(buf, "%d\n", !!(cfg1 & MCA_REG_ADC_CFG1_CMP_OUT)); ++} ++ ++static const struct iio_chan_spec_ext_info adc_comp_attr[] = { ++ { ++ .name = "cmp_thr_l", ++ .shared = IIO_SEPARATE, ++ .read = &cmp_thr_lo_read, ++ .write = &cmp_thr_lo_write, ++ }, ++ { ++ .name = "cmp_thr_h", ++ .shared = IIO_SEPARATE, ++ .read = &cmp_thr_hi_read, ++ .write = &cmp_thr_hi_write, ++ }, ++ { ++ .name = "cmp_rate", ++ .shared = IIO_SEPARATE, ++ .read = &cmp_sample_rate_read, ++ .write = &cmp_sample_rate_write, ++ }, ++ { ++ .name = "cmp_edge", ++ .shared = IIO_SEPARATE, ++ .read = &cmp_irq_edge_read, ++ .write = &cmp_irq_edge_write, ++ }, ++ { ++ .name = "cmp_out", ++ .shared = IIO_SEPARATE, ++ .read = &cmp_out_read, ++ .write = NULL, ++ }, ++ { ++ .name = "wakeup", ++ .shared = IIO_SEPARATE, ++ .read = &wakeup_read, ++ .write = &wakeup_write, ++ }, ++ { ++ .name = "averager", ++ .shared = IIO_SEPARATE, ++ .read = &averager_read, ++ .write = &averager_write, ++ }, ++ { ++ /* Centinel */ ++ .name = NULL, ++ }, ++ ++}; ++ ++static void init_adc_channels(struct platform_device *pdev, ++ struct regmap *regmap, ++ int gpio_base, struct iio_chan_spec *iio_ch, ++ u8 *ch_list, u8 cnt, bool is_comparator) ++{ ++ struct iio_chan_spec *chan; ++ u8 i; ++ ++ for (i = 0, chan = iio_ch; i < cnt; i++, chan++) { ++ unsigned int cfg0_mask = MCA_REG_ADC_CFG0_EN; ++ u8 ch = ch_list[i]; ++ int ret; ++ ++ /* ++ * Request the corresponding MCA GPIO so it can not be ++ * used as GPIO by user space ++ */ ++ if (gpio_base >= 0) { ++ ret = devm_gpio_request(&pdev->dev, gpio_base + ch, ++ "ADC"); ++ if (ret) { ++ dev_warn(&pdev->dev, ++ "Error requesting GPIO %d. Cannot use ADC%d (%d)\n", ++ gpio_base + ch, ch, ret); ++ continue; ++ } ++ } ++ ++ if (is_comparator) ++ cfg0_mask |= MCA_REG_ADC_CFG0_MODE_1; ++ ++ /* Enable the ADC channel as a comparator */ ++ ret = regmap_update_bits(regmap, MCA_REG_ADC_CFG0_0 + ch, ++ cfg0_mask, cfg0_mask); ++ if (ret) ++ dev_err(&pdev->dev, ++ "Error writing MCA_REG_ADC_CFG0_%d (%d)\n", ++ ch, ret); ++ ++ chan->type = IIO_VOLTAGE; ++ chan->indexed = 1; ++ chan->channel = ch; ++ chan->scan_index = i; ++ chan->scan_type.sign = 'u'; ++ chan->scan_type.realbits = 12; ++ chan->scan_type.storagebits = 16; ++ chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); ++ chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE); ++ if (is_comparator) { ++ chan->event_spec = comparator_events; ++ chan->num_event_specs = ARRAY_SIZE(comparator_events); ++ chan->ext_info = adc_comp_attr; ++ } ++ } ++} ++ ++static u32 get_vref(struct device *mca_dev, struct regmap *regmap, ++ struct device_node *np) ++{ ++ int ret; ++ u32 vref = MCA_ADC_DEF_VREF; ++ ++ if (of_property_read_bool(np, "digi,internal-vref")) { ++ ret = regmap_write(regmap, MCA_REG_ADC_CFG_0, ++ MCA_REG_ADC_CFG_0_INT_VREF); ++ if (ret) ++ dev_err(mca_dev, ++ "Error configuring ADC internal VREF (%d)\n", ++ ret); ++ else ++ vref = MCA_ADC_INT_VREF; ++ } else { ++ ret = of_property_read_u32(np, "digi,adc-vref", &vref); ++ if (ret || vref < MCA_ADC_MIN_VREF || vref > MCA_ADC_MAX_VREF) ++ dev_warn(mca_dev, "adc-vref %s, using default %u uV\n", ++ ret ? "not provided" : "out of range", vref); ++ } ++ ++ return vref; ++} ++ ++static int mca_adc_probe(struct platform_device *pdev) ++{ ++ struct mca_drv *mca = dev_get_drvdata(pdev->dev.parent); ++ struct device *mca_dev = mca->dev; ++ int gpio_base = mca->gpio_base; ++ struct regmap *regmap = mca->regmap; ++ struct mca_adc *mca_adc; ++ struct iio_dev *indio_dev; ++ struct device_node *np; ++ struct iio_chan_spec *iio_ch_list; ++ u8 adc_ch_list[MCA_MAX_IOS]; ++ u8 num_adcs = 0; ++ u8 num_comps = 0; ++ struct property *prop; ++ const __be32 *cur; ++ u32 ch, cfg; ++ int ret = 0; ++ u8 adc_comp_ch_list[MCA_MAX_IOS]; ++ ++ if (!mca_dev || !mca_dev->parent || !mca_dev->parent->of_node) ++ return -EPROBE_DEFER; ++ ++ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*mca_adc)); ++ if (!indio_dev) { ++ dev_err(&pdev->dev, "Failed to allocate indio_dev device\n"); ++ return -ENOMEM; ++ } ++ mca_adc = iio_priv(indio_dev); ++ mca_adc->regmap = regmap; ++ mca_adc->dev = mca_dev; ++ mca_adc->irq = -1; ++ ++ /* Find entry in device-tree */ ++ if (mca_dev->of_node) { ++ const struct mca_adc_data *devdata = ++ of_device_get_match_data(&pdev->dev); ++ const char * compatible = pdev->dev.driver-> ++ of_match_table[devdata->devtype].compatible; ++ ++ /* Return if mca_adc node does not exist or if it is disabled */ ++ np = of_find_compatible_node(mca_dev->of_node, NULL, compatible); ++ if (!np || !of_device_is_available(np)) ++ return -ENODEV; ++ ++ mca_adc->vref = get_vref(mca_dev, regmap, np); ++ ++ of_property_for_each_u32(np, "digi,adc-ch-list", ++ prop, cur, ch) { ++ if (ch >= MCA_MAX_IOS) ++ continue; ++ ++ /* ++ * Verify that the requested IOs are ADC capable and ++ * enable the channel for ADC operation ++ */ ++ ret = regmap_read(regmap, MCA_REG_ADC_CFG0_0 + ch, ++ &cfg); ++ if (ret) { ++ dev_err(mca_dev, ++ "Error reading ADC%d CFG register (%d)\n", ++ ch, ret); ++ goto error_dev_free; ++ } ++ ++ /* Remove the channel from the list if not capable */ ++ if (!(cfg & MCA_REG_ADC_CFG0_CAPABLE)) { ++ dev_warn(mca_dev, ++ "Removing ADC%d, IO not ADC capable\n", ++ ch); ++ continue; ++ } ++ ++ adc_ch_list[num_adcs] = (u8)ch; ++ num_adcs++; ++ } ++ ++ of_property_for_each_u32(np, "digi,comparator-ch-list", ++ prop, cur, ch) { ++ u32 cfg; ++ ++ if (ch >= MCA_MAX_IOS) ++ continue; ++ ++ /* ++ * Verify that the requested IOs are ADC capable ++ */ ++ ret = regmap_read(regmap, MCA_REG_ADC_CFG0_0 + ch, ++ &cfg); ++ if (ret) { ++ dev_err(&pdev->dev, ++ "Failed read ADC%d CFG register (%d)\n", ++ ch, ret); ++ continue; ++ } ++ ++ /* Remove the channel from the list if not capable */ ++ if (!(cfg & MCA_REG_ADC_CFG0_CAPABLE)) { ++ dev_warn(&pdev->dev, ++ "Removing ADC%d, IO no ADC capable\n", ++ ch); ++ continue; ++ } ++ ++ adc_comp_ch_list[num_comps] = (u8)ch; ++ num_comps++; ++ } ++ } ++ ++ if (!num_adcs && !num_comps) ++ goto error_dev_free; ++ ++ indio_dev->dev.parent = &pdev->dev; ++ indio_dev->name = dev_name(&pdev->dev); ++ indio_dev->modes = INDIO_DIRECT_MODE; ++ indio_dev->info = &mca_adc_info; ++ indio_dev->num_channels = num_adcs + num_comps; ++ iio_ch_list = devm_kzalloc(&pdev->dev, ++ indio_dev->num_channels * ++ sizeof(indio_dev->channels[0]), ++ GFP_KERNEL); ++ if (!iio_ch_list) ++ goto error_dev_free; ++ ++ indio_dev->channels = iio_ch_list; ++ ++ init_adc_channels(pdev, regmap, gpio_base, &iio_ch_list[0], adc_ch_list, ++ num_adcs, false); ++ init_adc_channels(pdev, regmap, gpio_base, &iio_ch_list[num_adcs], ++ adc_comp_ch_list, num_comps, true); ++ ++ if (num_comps) { ++ mca_adc->irq = platform_get_irq_byname(pdev, "ADC"); ++ if (mca_adc->irq) { ++ ret = devm_request_threaded_irq(&pdev->dev, ++ mca_adc->irq, ++ NULL, ++ &comparator_irq_handler, ++ IRQF_TRIGGER_LOW | ++ IRQF_ONESHOT, ++ dev_name(&pdev->dev), ++ indio_dev); ++ if (ret) { ++ dev_err(&pdev->dev, ++ "Requested Comparator IRQ (%d).\n", ++ mca_adc->irq); ++ goto error_free_ch; ++ } ++ } ++ } ++ ++ ret = iio_device_register(indio_dev); ++ if (ret) { ++ dev_err(&pdev->dev, "Failed to register mca adc indio_dev device\n"); ++ goto error_free_ch; ++ } ++ ++ platform_set_drvdata(pdev, indio_dev); ++ ++ pr_info("ADC driver for MCA\n"); ++ ++ return 0; ++ ++error_free_ch: ++ devm_kfree(&pdev->dev, (void *)iio_ch_list); ++ ++error_dev_free: ++ while (num_adcs && gpio_base >= 0) { ++ devm_gpio_free(&pdev->dev, ++ gpio_base + adc_ch_list[num_adcs - 1]); ++ num_adcs--; ++ } ++ devm_iio_device_free(&pdev->dev, indio_dev); ++ ++ return ret; ++} ++ ++static int mca_adc_remove(struct platform_device *pdev) ++{ ++ struct mca_drv *mca = dev_get_drvdata(pdev->dev.parent); ++ struct iio_dev *indio_dev = platform_get_drvdata(pdev); ++ struct iio_chan_spec *chan; ++ int i; ++ struct mca_adc *adc = iio_priv(indio_dev); ++ ++ if (!mca) { ++ dev_err(&pdev->dev, "Received a NULL pointer in mca\n"); ++ return -EINVAL; ++ } ++ ++ /* Release allocated resources */ ++ if (mca->gpio_base >= 0) { ++ for (i = 0, chan = (struct iio_chan_spec *)indio_dev->channels; ++ i < indio_dev->num_channels; ++ i++) { ++ devm_gpio_free(&pdev->dev, mca->gpio_base + chan->channel); ++ } ++ } ++ ++ if (adc->irq != -1) ++ devm_free_irq(&pdev->dev, adc->irq, indio_dev); ++ ++ devm_kfree(&pdev->dev, (void *)indio_dev->channels); ++ iio_device_unregister(indio_dev); ++ devm_iio_device_free(&pdev->dev, indio_dev); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_OF ++static struct mca_adc_data mca_adc_devdata[] = { ++ [CC6UL_MCA_ADC] = { ++ .devtype = CC6UL_MCA_ADC, ++ }, ++ [CC8X_MCA_ADC] = { ++ .devtype = CC8X_MCA_ADC, ++ }, ++ [IOEXP_MCA_ADC] = { ++ .devtype = IOEXP_MCA_ADC, ++ }, ++}; ++ ++static const struct of_device_id mca_adc_dt_ids[] = { ++ { .compatible = "digi,mca-cc6ul-adc", ++ .data = &mca_adc_devdata[CC6UL_MCA_ADC]}, ++ { .compatible = "digi,mca-cc8x-adc", ++ .data = &mca_adc_devdata[CC8X_MCA_ADC]}, ++ { .compatible = "digi,mca-ioexp-adc", ++ .data = &mca_adc_devdata[IOEXP_MCA_ADC]}, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, mca_adc_dt_ids); ++#endif ++ ++static struct platform_driver mca_adc_driver = { ++ .probe = mca_adc_probe, ++ .remove = mca_adc_remove, ++ .driver = { ++ .name = MCA_DRVNAME_ADC, ++ .owner = THIS_MODULE, ++#ifdef CONFIG_OF ++ .of_match_table = mca_adc_dt_ids, ++#endif ++ }, ++}; ++ ++static int __init mca_adc_init(void) ++{ ++ return platform_driver_register(&mca_adc_driver); ++} ++module_init(mca_adc_init); ++ ++static void __exit mca_adc_exit(void) ++{ ++ platform_driver_unregister(&mca_adc_driver); ++} ++module_exit(mca_adc_exit); ++ ++/* Module information */ ++MODULE_AUTHOR("Digi International Inc."); ++MODULE_DESCRIPTION("ADC device driver for MCA of ConnectCore Modules"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:" MCA_DRVNAME_ADC); diff --git a/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ul/0008-imx6ul-Add-MCA-tamper-support-for-ConnectCore-6UL-SO.patch b/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ul/0008-imx6ul-Add-MCA-tamper-support-for-ConnectCore-6UL-SO.patch new file mode 100644 index 000000000..f3ed23fac --- /dev/null +++ b/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ul/0008-imx6ul-Add-MCA-tamper-support-for-ConnectCore-6UL-SO.patch @@ -0,0 +1,937 @@ +From bfc63af24c96ca3206e3f8e6b0fd437944e6519e Mon Sep 17 00:00:00 2001 +From: Alex Gonzalez +Date: Mon, 23 Apr 2018 11:42:42 +0200 +Subject: [PATCH] imx6ul: Add MCA tamper support for ConnectCore 6UL SOM and + SBCs + +Synched with v4.14.78/master at: +3f8b03950b323db4ca89b1cdc1c2288f79facaa3 + +Signed-off-by: Alex Gonzalez +--- + arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts | 7 + + arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi | 4 + + arch/arm/configs/imx_v6_v7_defconfig | 1 + + drivers/iio/Kconfig | 1 + + drivers/iio/Makefile | 1 + + drivers/iio/tamper/Kconfig | 20 + + drivers/iio/tamper/Makefile | 6 + + drivers/iio/tamper/mca_tamper.c | 801 ++++++++++++++++++++++++++++ + 8 files changed, 841 insertions(+) + create mode 100644 drivers/iio/tamper/Kconfig + create mode 100644 drivers/iio/tamper/Makefile + create mode 100644 drivers/iio/tamper/mca_tamper.c + +diff --git a/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts b/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts +index acea9a56971e..7cbc14d56680 100644 +--- a/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts ++++ b/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts +@@ -162,6 +162,13 @@ + digi,adc-vref = <3000000>; + }; + ++/* Enable Tamper detection. There are 2 digital (0 and 1) and 2 ++ * analog (2 and 3) tamper interfaces. ++ */ ++&mca_tamper { ++ digi,tamper-if-list = <0 1 2 3>; ++}; ++ + &pwm1 { + status = "okay"; + }; +diff --git a/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi b/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi +index b5efc58d362d..a20fb5ffc98b 100644 +--- a/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi ++++ b/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi +@@ -79,6 +79,10 @@ + digi,adc-vref = <3000000>; + }; + ++ mca_tamper: tamper { ++ compatible = "digi,mca-cc6ul-tamper"; ++ }; ++ + }; + + pfuze3000: pmic@8 { +diff --git a/arch/arm/configs/imx_v6_v7_defconfig b/arch/arm/configs/imx_v6_v7_defconfig +index 060c1de356ca..188fb4309851 100644 +--- a/arch/arm/configs/imx_v6_v7_defconfig ++++ b/arch/arm/configs/imx_v6_v7_defconfig +@@ -390,6 +390,7 @@ CONFIG_VF610_ADC=y + CONFIG_SENSORS_ISL29018=y + CONFIG_MAG3110=y + CONFIG_MPL3115=y ++CONFIG_TAMPER_MCA=y + CONFIG_PWM=y + CONFIG_PWM_FSL_FTM=y + CONFIG_PWM_IMX=y +diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig +index d08aeb41cd07..e4c3fd62ca92 100644 +--- a/drivers/iio/Kconfig ++++ b/drivers/iio/Kconfig +@@ -94,6 +94,7 @@ source "drivers/iio/potentiostat/Kconfig" + source "drivers/iio/pressure/Kconfig" + source "drivers/iio/proximity/Kconfig" + source "drivers/iio/resolver/Kconfig" ++source "drivers/iio/tamper/Kconfig" + source "drivers/iio/temperature/Kconfig" + + endif # IIO +diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile +index cb5993251381..004de1adaf82 100644 +--- a/drivers/iio/Makefile ++++ b/drivers/iio/Makefile +@@ -37,5 +37,6 @@ obj-y += potentiostat/ + obj-y += pressure/ + obj-y += proximity/ + obj-y += resolver/ ++obj-y += tamper/ + obj-y += temperature/ + obj-y += trigger/ +diff --git a/drivers/iio/tamper/Kconfig b/drivers/iio/tamper/Kconfig +new file mode 100644 +index 000000000000..2a9a395becf4 +--- /dev/null ++++ b/drivers/iio/tamper/Kconfig +@@ -0,0 +1,20 @@ ++# ++# Tamper Devices ++# ++ ++menu "Tamper devices" ++ ++config TAMPER_MCA ++ tristate "Tamper support for MCA on Digi ConnectCore SOMs" ++ select MFD_MCA_CC6UL if SOC_IMX6UL ++ select MFD_MCA_CC8X if ARCH_FSL_IMX8QXP ++ ++ help ++ Say Y here to build the tamper driver on the MCA of the ++ ConnectCore system-on-modules. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called mca_tamper ++ ++endmenu ++ +diff --git a/drivers/iio/tamper/Makefile b/drivers/iio/tamper/Makefile +new file mode 100644 +index 000000000000..9f422a829081 +--- /dev/null ++++ b/drivers/iio/tamper/Makefile +@@ -0,0 +1,6 @@ ++# ++# Makefile for IIO Tamper devices ++# ++ ++# When adding new entries keep the list in alphabetical order ++obj-$(CONFIG_TAMPER_MCA) += mca_tamper.o +diff --git a/drivers/iio/tamper/mca_tamper.c b/drivers/iio/tamper/mca_tamper.c +new file mode 100644 +index 000000000000..2c92624aed18 +--- /dev/null ++++ b/drivers/iio/tamper/mca_tamper.c +@@ -0,0 +1,801 @@ ++/* mca_tamper.c - Tamper driver for MCA on ConnectCore modules ++ * ++ * Copyright (C) 2016 - 2018 Digi International Inc ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Library General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Library General Public License for more details. ++ * ++ * You should have received a copy of the GNU Library General Public ++ * License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define MCA_DRVNAME_TAMPER "mca-tamper" ++ ++#ifdef CONFIG_OF ++enum mca_tamper_type { ++ CC6UL_MCA_TAMPER, ++ CC8X_MCA_TAMPER, ++}; ++ ++struct mca_tamper_data { ++ enum mca_tamper_type devtype; ++ u16 num_tamper_ifaces; ++ u16 digital_tamper_cnt; ++}; ++#endif ++ ++struct mca_tamper { ++ struct mca_drv *mca; ++ struct iio_dev *iio; ++ char name[10]; ++ uint8_t config0; ++ uint8_t config1; ++ uint8_t io_in; ++ uint8_t io_out; ++ uint8_t pwroff_delay_ms; ++ uint8_t event; ++ int irq; ++ int iface; ++}; ++ ++/* Tamper register offsets */ ++enum { ++ CFG0 = 0, ++ CFG1 = MCA_TAMPER0_CFG1 - MCA_TAMPER0_CFG0, ++ IO_IN = MCA_TAMPER0_IO_IN - MCA_TAMPER0_CFG0, ++ IO_OUT = MCA_TAMPER0_IO_OUT - MCA_TAMPER0_CFG0, ++ DELAY_PWROFF = MCA_TAMPER0_DELAY_PWROFF - MCA_TAMPER0_CFG0, ++ DATE_YEAR_L = MCA_TAMPER0_DATE_START - MCA_TAMPER0_CFG0, ++ DATE_YEAR_H, ++ DATE_MONTH, ++ DATE_DAY, ++ DATE_HOUR, ++ DATE_MIN, ++ DATE_SEC, ++ EVENT = MCA_TAMPER0_EVENT - MCA_TAMPER0_CFG0, ++ TICKS = MCA_TAMPER2_TICKS_L - MCA_TAMPER2_CFG0, ++ THR_L = MCA_TAMPER2_THRESH_LO_L - MCA_TAMPER2_CFG0, ++ THR_H = MCA_TAMPER2_THRESH_HI_L - MCA_TAMPER2_CFG0, ++}; ++ ++static int mca_tamper_read_raw(struct iio_dev *iio, ++ struct iio_chan_spec const *ch, ++ int *val, int *val2, long mask) ++{ ++ struct mca_tamper *tp = iio_priv(iio); ++ u32 value; ++ int ret; ++ unsigned int event_reg; ++ ++ switch (tp->iface) { ++ case 0: ++ event_reg = MCA_TAMPER0_EVENT; ++ break; ++ case 1: ++ event_reg = MCA_TAMPER1_EVENT; ++ break; ++ case 2: ++ event_reg = MCA_TAMPER2_EVENT; ++ break; ++ case 3: ++ event_reg = MCA_TAMPER3_EVENT; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (mask) { ++ case IIO_CHAN_INFO_RAW: ++ ret = regmap_read(tp->mca->regmap, event_reg, &value); ++ if (ret < 0) { ++ dev_err(tp->mca->dev, ++ "Error reading Tamper%d event register (%d)\n", ++ ch->channel, ret); ++ return ret; ++ } ++ ++ *val = value; ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ return IIO_VAL_INT; ++} ++ ++static const struct iio_event_spec mca_tamper_events[] = { ++ { ++ .type = IIO_EV_TYPE_CHANGE, ++ .dir = IIO_EV_DIR_NONE, ++ .mask_separate = BIT(IIO_EV_INFO_ENABLE), ++ }, ++}; ++ ++static void mca_tamper_get_time(u8 *data, struct rtc_time *tm) ++{ ++ tm->tm_year = ((data[DATE_YEAR_H] << 8) | data[DATE_YEAR_L]) - 1900; ++ tm->tm_mon = (data[DATE_MONTH] & MCA_RTC_MONTH_MASK) - 1; ++ tm->tm_mday = (data[DATE_DAY] & MCA_RTC_DAY_MASK); ++ tm->tm_hour = (data[DATE_HOUR] & MCA_RTC_HOUR_MASK); ++ tm->tm_min = (data[DATE_MIN] & MCA_RTC_MIN_MASK); ++ tm->tm_sec = (data[DATE_SEC] & MCA_RTC_SEC_MASK); ++} ++ ++static unsigned int get_tamper_base_reg(unsigned int iface) ++{ ++ switch (iface) { ++ case 0: ++ return MCA_TAMPER0_CFG0; ++ case 1: ++ return MCA_TAMPER1_CFG0; ++ case 2: ++ return MCA_TAMPER2_CFG0; ++ case 3: ++ return MCA_TAMPER3_CFG0; ++ default: ++ break; ++ } ++ ++ return ~0; ++} ++ ++/* Sysfs interface */ ++#define TAMPER_SYSFS_SHOW_REG(reg, addr, type) \ ++static ssize_t show_##reg(struct device *dev, \ ++ struct device_attribute *attr, char *buf) \ ++{ \ ++ struct iio_dev *iio = dev_to_iio_dev(dev); \ ++ struct mca_tamper *tp = iio_priv(iio); \ ++ int ret; \ ++ type val; \ ++ unsigned int tamper_base_reg = get_tamper_base_reg(tp->iface); \ ++ \ ++ if (tamper_base_reg == ~0) \ ++ return -1; \ ++ \ ++ ret = regmap_bulk_read(tp->mca->regmap, \ ++ tamper_base_reg + addr, \ ++ &val, sizeof(val)); \ ++ if (ret != 0) { \ ++ dev_err(tp->mca->dev, \ ++ "Failed reading Tamper%d #reg register (%d)\n", \ ++ tp->iface, ret); \ ++ return 0; \ ++ } \ ++ return sprintf(buf, "0x%04x\n", (type)val); \ ++} ++ ++#define TAMPER_SYSFS_STORE_REG(reg, addr, type) \ ++static ssize_t store_##reg(struct device *dev, \ ++ struct device_attribute *attr, \ ++ const char *buf, size_t count) \ ++{ \ ++ struct iio_dev *iio = dev_to_iio_dev(dev); \ ++ struct mca_tamper *tp = iio_priv(iio); \ ++ int ret; \ ++ unsigned long val; \ ++ unsigned int tamper_base_reg = get_tamper_base_reg(tp->iface); \ ++ \ ++ if (tamper_base_reg == ~0) \ ++ return -1; \ ++ \ ++ ret = kstrtoul(buf, 0, &val); \ ++ if (ret) { \ ++ dev_err(tp->mca->dev, \ ++ "%s: error parsing input (%s)\n", \ ++ __func__, buf); \ ++ return ret; \ ++ } \ ++ ret = regmap_bulk_write(tp->mca->regmap, \ ++ tamper_base_reg + addr, \ ++ &val, sizeof(type)); \ ++ if (ret != 0) { \ ++ dev_err(tp->mca->dev, \ ++ "Failed write Tamper%d #reg register (%d)\n", \ ++ tp->iface, ret); \ ++ return 0; \ ++ } \ ++ return count; \ ++} ++ ++#define MCA_TAMPER_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr) ++ ++static ssize_t show_tp_events(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct iio_dev *iio = dev_to_iio_dev(dev); ++ struct mca_tamper *tp = iio_priv(iio); ++ int ret; ++ unsigned int val; ++ unsigned int tamper_base_reg = get_tamper_base_reg(tp->iface); ++ ++ if (tamper_base_reg == ~0) ++ return -1; ++ ++ ret = regmap_read(tp->mca->regmap, tamper_base_reg + EVENT, &val); ++ if (ret != 0) { ++ dev_err(tp->mca->dev, ++ "Failed reading Tamper%d #reg register (%d)\n", ++ tp->iface, ret); ++ return 0; ++ } ++ ++ switch (val) { ++ case 0x00: ++ ret = sprintf(buf, "none\n"); ++ break; ++ case MCA_TAMPER_SIGNALED: ++ ret = sprintf(buf, "signaled\n"); ++ break; ++ case (MCA_TAMPER_ACKED | MCA_TAMPER_SIGNALED): ++ ret = sprintf(buf, "signaled+acked\n"); ++ break; ++ default: ++ ret = sprintf(buf, "unknown (0x%04x)\n", (u8)val); ++ break; ++ } ++ return ret; ++} ++ ++static ssize_t store_tp_events(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct iio_dev *iio = dev_to_iio_dev(dev); ++ struct mca_tamper *tp = iio_priv(iio); ++ int ret; ++ unsigned long val; ++ unsigned int tamper_base_reg = get_tamper_base_reg(tp->iface); ++ ++ if (tamper_base_reg == ~0) ++ return -1; ++ ++ if (!strncmp(buf, "ack", strlen("ack"))) ++ val = MCA_TAMPER_ACKED; ++ else if (!strncmp(buf, "clear", strlen("clear"))) ++ val = MCA_TAMPER_CLEAR; ++ else { ++ /* Check if string is a raw value */ ++ ret = kstrtoul(buf, 0, &val); ++ if (ret || (val != MCA_TAMPER_CLEAR && ++ val != MCA_TAMPER_ACKED)) { ++ dev_err(tp->mca->dev, ++ "%s: error parsing input (%s)\n", ++ __func__, buf); ++ return ret; ++ } ++ } ++ ++ ret = regmap_write(tp->mca->regmap, tamper_base_reg + EVENT, val); ++ if (ret != 0) { ++ dev_err(tp->mca->dev, ++ "Failed write Tamper%d #reg register (%d)\n", ++ tp->iface, ret); ++ return 0; ++ } ++ ++ return count; ++} ++ ++static ssize_t show_timestamp(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct iio_dev *iio = dev_to_iio_dev(dev); ++ struct mca_tamper *tp = iio_priv(iio); ++ unsigned int tamper_base_reg = get_tamper_base_reg(tp->iface); ++ time64_t tamper_t64 = -1; ++ struct rtc_time tm; ++ u8 data[MCA_TAMPER_REGS_LEN]; ++ int ret; ++ ++ if (tamper_base_reg == ~0) ++ return -1; ++ ++ ret = regmap_bulk_read(tp->mca->regmap, tamper_base_reg, data, ++ sizeof(data)); ++ if (ret != 0) { ++ dev_err(tp->mca->dev, "Failed reading Tamper%d registers (%d)\n", ++ tp->iface, ret); ++ return ret; ++ } ++ ++ /* Confirm the event and get the timestamp */ ++ if (data[EVENT] & MCA_TAMPER_SIGNALED) { ++ mca_tamper_get_time(data, &tm); ++ tamper_t64 = rtc_tm_to_time64(&tm); ++ } ++ ++ return sprintf(buf, "%lld\n", tamper_t64); ++} ++ ++static IIO_DEVICE_ATTR(tamper_events, S_IRUGO | S_IWUSR, show_tp_events, store_tp_events, 0); ++static IIO_DEVICE_ATTR(timestamp, S_IRUGO, show_timestamp, NULL, 0); ++ ++#ifdef MCA_TAMPER_HAS_EXTRA_SYSFS_ENTRIES ++TAMPER_SYSFS_SHOW_REG(config0, CFG0, u8) ++TAMPER_SYSFS_SHOW_REG(config1, CFG1, u8) ++TAMPER_SYSFS_SHOW_REG(io_in, IO_IN, u8) ++TAMPER_SYSFS_SHOW_REG(io_out, IO_OUT, u8) ++TAMPER_SYSFS_SHOW_REG(pwroff_delay_ms, DELAY_PWROFF, u8) ++TAMPER_SYSFS_SHOW_REG(srate, TICKS, u16) ++TAMPER_SYSFS_SHOW_REG(thr_l, THR_L, u16) ++TAMPER_SYSFS_SHOW_REG(thr_h, THR_H, u16) ++ ++TAMPER_SYSFS_STORE_REG(config0, CFG0, u8) ++TAMPER_SYSFS_STORE_REG(config1, CFG1, u8) ++TAMPER_SYSFS_STORE_REG(io_in, IO_IN, u8) ++TAMPER_SYSFS_STORE_REG(io_out, IO_OUT, u8) ++TAMPER_SYSFS_STORE_REG(pwroff_delay_ms, DELAY_PWROFF, u8) ++TAMPER_SYSFS_STORE_REG(srate, TICKS, u16) ++TAMPER_SYSFS_STORE_REG(thr_l, THR_L, u16) ++TAMPER_SYSFS_STORE_REG(thr_h, THR_H, u16) ++ ++static IIO_DEVICE_ATTR(config0, S_IRUGO | S_IWUSR, show_config0, store_config0, 0); ++static IIO_DEVICE_ATTR(config1, S_IRUGO | S_IWUSR, show_config1, store_config1, 0); ++static IIO_DEVICE_ATTR(io_in, S_IRUGO | S_IWUSR, show_io_in, store_io_in, 0); ++static IIO_DEVICE_ATTR(io_out, S_IRUGO | S_IWUSR, show_io_out, store_io_out, 0); ++static IIO_DEVICE_ATTR(pwroff_delay_ms, S_IRUGO | S_IWUSR, show_pwroff_delay_ms, store_pwroff_delay_ms, 0); ++static IIO_DEVICE_ATTR(srate, S_IRUGO | S_IWUSR, show_srate, store_srate, 0); ++static IIO_DEVICE_ATTR(thr_l, S_IRUGO | S_IWUSR, show_thr_l, store_thr_l, 0); ++static IIO_DEVICE_ATTR(thr_h, S_IRUGO | S_IWUSR, show_thr_h, store_thr_h, 0); ++ ++static struct attribute *mca_tamper_attributes[] = { ++ MCA_TAMPER_DEV_ATTR(config0), ++ MCA_TAMPER_DEV_ATTR(io_in), ++ MCA_TAMPER_DEV_ATTR(io_out), ++ MCA_TAMPER_DEV_ATTR(pwroff_delay_ms), ++ MCA_TAMPER_DEV_ATTR(tamper_events), ++ NULL, ++}; ++ ++static struct attribute *mca_tamper_analog_attributes[] = { ++ MCA_TAMPER_DEV_ATTR(config0), ++ MCA_TAMPER_DEV_ATTR(config1), ++ MCA_TAMPER_DEV_ATTR(io_in), ++ MCA_TAMPER_DEV_ATTR(io_out), ++ MCA_TAMPER_DEV_ATTR(pwroff_delay_ms), ++ MCA_TAMPER_DEV_ATTR(tamper_events), ++ MCA_TAMPER_DEV_ATTR(timestamp), ++ MCA_TAMPER_DEV_ATTR(srate), ++ MCA_TAMPER_DEV_ATTR(thr_l), ++ MCA_TAMPER_DEV_ATTR(thr_h), ++ NULL, ++}; ++ ++static const struct attribute_group mca_tamper_analog_attribute_group = { ++ .name = "tamper_analog", ++ .attrs = mca_tamper_analog_attributes, ++}; ++ ++static const struct iio_info mca_tamper_analog_info = { ++ .attrs = &mca_tamper_analog_attribute_group, ++ .read_raw = &mca_tamper_read_raw, ++}; ++ ++#else ++static struct attribute *mca_tamper_attributes[] = { ++ MCA_TAMPER_DEV_ATTR(tamper_events), ++ MCA_TAMPER_DEV_ATTR(timestamp), ++ NULL, ++}; ++#endif /* MCA_TAMPER_HAS_EXTRA_SYSFS_ENTRIES */ ++ ++static const struct attribute_group mca_tamper_attribute_group = { ++ .name = "tamper", ++ .attrs = mca_tamper_attributes, ++}; ++ ++static const struct iio_info mca_tamper_info = { ++ .attrs = &mca_tamper_attribute_group, ++ .read_raw = &mca_tamper_read_raw, ++}; ++ ++#ifdef MCA_TAMPER_DUMP_REGISTERS ++static void mca_tamper_regs_dump(u8 *data) ++{ ++ printk("Tamper CFG0 = 0x%02x\n", data[CFG0]); ++ printk("Tamper CFG1 = 0x%02x\n", data[CFG1]); ++ printk("Tamper IO_IN = 0x%02x\n", data[IO_IN]); ++ printk("Tamper IO_OUT = 0x%02x\n", data[IO_OUT]); ++ printk("Tamper DELAY_PWROFF = 0x%02x\n", data[DELAY_PWROFF]); ++ printk("Tamper DATE_YEAR_L = 0x%02x\n", data[DATE_YEAR_L]); ++ printk("Tamper DATE_YEAR_H = 0x%02x\n", data[DATE_YEAR_H]); ++ printk("Tamper DATE_MONTH = 0x%02x\n", data[DATE_MONTH]); ++ printk("Tamper DATE_DAY = 0x%02x\n", data[DATE_DAY]); ++ printk("Tamper DATE_HOUR = 0x%02x\n", data[DATE_HOUR]); ++ printk("Tamper DATE_MIN = 0x%02x\n", data[DATE_MIN]); ++ printk("Tamper DATE_SEC = 0x%02x\n", data[DATE_SEC]); ++ printk("Tamper EVENT = 0x%02x\n", data[EVENT]); ++} ++#else ++static void mca_tamper_regs_dump(u8 *data) {} ++#endif ++ ++static irqreturn_t mca_tamper_irq_handler(int irq, void *private) ++{ ++ struct iio_dev *iio = private; ++ struct mca_tamper *tp = iio_priv(iio); ++ struct rtc_time tm; ++ u8 data[MCA_TAMPER_REGS_LEN]; ++ time64_t tamper_t64; ++ u64 event; ++ int ret; ++ unsigned int tamper_base_reg = get_tamper_base_reg(tp->iface); ++ ++ if (tamper_base_reg == ~0) ++ goto irq_out; ++ ++ dev_dbg(tp->mca->dev, "Tamper %d IRQ (%d)\n", tp->iface, irq); ++ ++ ret = regmap_bulk_read(tp->mca->regmap, tamper_base_reg, data, ++ sizeof(data)); ++ if (ret != 0) { ++ dev_err(tp->mca->dev, "Failed reading Tamper%d registers (%d)\n", ++ tp->iface, ret); ++ goto irq_out; ++ } ++ ++ /* Confirm the event and get the timestamp */ ++ if (!(data[EVENT] & MCA_TAMPER_SIGNALED)) ++ goto irq_out; ++ ++ if (data[EVENT] & MCA_TAMPER_ACKED) ++ goto irq_out; ++ ++ mca_tamper_regs_dump(data); ++ mca_tamper_get_time(data, &tm); ++ tamper_t64 = rtc_tm_to_time64(&tm); ++ ++ /* Notify the event... */ ++ event = IIO_MOD_EVENT_CODE(IIO_ACTIVITY, tp->iface, IIO_NO_MOD, ++ IIO_EV_TYPE_CHANGE, IIO_EV_DIR_NONE); ++ iio_push_event(iio, event, tamper_t64); ++ ++irq_out: ++ return IRQ_HANDLED; ++} ++ ++static int mca_tamper_is_enabled(struct mca_drv *mca, int iface) ++{ ++ int ret; ++ u8 data[MCA_TAMPER_REGS_LEN]; ++ unsigned int tamper_base_reg = get_tamper_base_reg(iface); ++ ++ /* Get tamper configuration registers */ ++ ret = regmap_bulk_read(mca->regmap, ++ tamper_base_reg, ++ data, sizeof(data)); ++ if (ret != 0) { ++ dev_err(mca->dev, ++ "Failed reading tamper%d registers (%d)\n", ++ iface, ret); ++ return ret; ++ } ++ ++ /* Verify if the tamper interface is enabled */ ++ if (!(data[CFG0] & MCA_TAMPER_DET_EN)) ++ return -1; ++ return 0; ++} ++ ++static int mca_init_hardware(struct mca_tamper *tp) ++{ ++ int ret; ++ u8 data[MCA_TAMPER_REGS_LEN]; ++ unsigned int tamper_base_reg = get_tamper_base_reg(tp->iface); ++ ++ /* Verify if the tamper interface is enabled */ ++ ret = regmap_bulk_read(tp->mca->regmap, ++ tamper_base_reg, ++ data, sizeof(data)); ++ if (ret != 0) { ++ dev_err(tp->mca->dev, ++ "Failed reading tamper%d registers (%d)\n", ++ tp->iface, ret); ++ return ret; ++ } ++ ++ tp->config0 = data[CFG0]; ++ tp->config1 = data[CFG1]; ++ tp->io_in = data[IO_IN]; ++ tp->io_out = data[IO_OUT]; ++ tp->pwroff_delay_ms = data[DELAY_PWROFF]; ++ ++ if (!(data[CFG0] & MCA_TAMPER_DET_EN)) ++ return -ENODEV; ++ ++ if (tp->mca->gpio_base >= 0) { ++ ret = devm_gpio_request(tp->mca->dev, ++ tp->mca->gpio_base + tp->io_in, ++ "TAMPER IN"); ++ if (ret != 0) { ++ dev_warn(tp->mca->dev, ++ "Error requesting GPIO %d for tamper %d input. It might be in use by other device.\n", ++ tp->mca->gpio_base + tp->io_in, tp->iface); ++ } ++ ++ if (data[CFG0] & MCA_TAMPER_OUT_EN) { ++ ret = devm_gpio_request(tp->mca->dev, ++ tp->mca->gpio_base + tp->io_out, ++ "TAMPER OUT"); ++ if (ret != 0) { ++ dev_warn(tp->mca->dev, ++ "Error requesting GPIO %d for tamper %d output. It might be in use by other device.\n", ++ tp->mca->gpio_base + tp->io_out, ++ tp->iface); ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++static void mca_init_channel(struct iio_chan_spec *chan, int idx) ++{ ++ chan->type = IIO_ACTIVITY; ++ chan->indexed = 1; ++ chan->channel = idx; ++ chan->scan_index = idx; ++ chan->scan_type.sign = 'u'; ++ chan->scan_type.realbits = 1; ++ chan->scan_type.storagebits = 32; ++ chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); ++ chan->event_spec = mca_tamper_events; ++ chan->num_event_specs = ARRAY_SIZE(mca_tamper_events); ++} ++ ++static int mca_tamper_probe(struct platform_device *pdev) ++{ ++ struct mca_drv *mca = dev_get_drvdata(pdev->dev.parent); ++ struct mca_tamper **mca_tamper = NULL; ++ const struct mca_tamper_data *devdata = of_device_get_match_data(&pdev->dev); ++ struct device_node *np; ++ struct property *prop; ++ const __be32 *cur; ++ u32 iface; ++ int ret = 0; ++ u16 num_tamper_ifaces; ++ u16 digital_tamper_cnt; ++ ++ if (!mca || !mca->dev || !mca->dev->of_node || !devdata) ++ return -EPROBE_DEFER; ++ ++ pr_info("Tamper driver for MCA\n"); ++ ++ num_tamper_ifaces = devdata->num_tamper_ifaces; ++ digital_tamper_cnt = devdata->digital_tamper_cnt; ++ ++ mca_tamper = devm_kzalloc(&pdev->dev, ++ sizeof(*mca_tamper) * num_tamper_ifaces, ++ GFP_KERNEL); ++ if (!mca_tamper) { ++ dev_err(&pdev->dev, "Failed to allocate memory for mca_tamper\n"); ++ ret = -ENOMEM; ++ goto exit_error; ++ } ++ ++ platform_set_drvdata(pdev, mca_tamper); ++ ++ for (iface = 0; iface < num_tamper_ifaces; iface++) ++ mca_tamper[iface] = NULL; ++ ++ /* Return silently if RTC node does not exist or if it is disabled */ ++ { ++ const char * compatible = pdev->dev.driver-> ++ of_match_table[devdata->devtype].compatible; ++ np = of_find_compatible_node(mca->dev->of_node, NULL, compatible); ++ } ++ if (!np || !of_device_is_available(np)) { ++ ret = -ENODEV; ++ goto exit_error; ++ } ++ ++ of_property_for_each_u32(np, "digi,tamper-if-list", ++ prop, cur, iface) { ++ struct iio_chan_spec *channels; ++ struct iio_dev *iiod = NULL; ++ ++ if (iface >= num_tamper_ifaces) ++ continue; ++ ++ if (mca_tamper_is_enabled(mca, iface) != 0) { ++ dev_info(&pdev->dev, "Tamper %d disabled\n", iface); ++ continue; ++ } ++ ++ iiod = devm_iio_device_alloc(&pdev->dev, ++ sizeof(*mca_tamper)); ++ if (!iiod) { ++ dev_err(&pdev->dev, "Failed to allocate iio device\n"); ++ ret = -ENOMEM; ++ goto exit_error; ++ } ++ mca_tamper[iface] = iio_priv(iiod); ++ mca_tamper[iface]->mca = mca; ++ mca_tamper[iface]->iface = iface; ++ mca_tamper[iface]->iio = iiod; ++ ++ iiod->dev.parent = &pdev->dev; ++ sprintf(mca_tamper[iface]->name, "TAMPER%d", iface); ++ iiod->name = mca_tamper[iface]->name; ++ iiod->modes = INDIO_DIRECT_MODE; ++ channels = devm_kzalloc(&pdev->dev, ++ sizeof(struct iio_chan_spec), ++ GFP_KERNEL); ++ iiod->channels = channels; ++ if (!iiod->channels) { ++ dev_err(&pdev->dev, "Failed to allocate iio channels\n"); ++ ret = -ENOMEM; ++ goto exit_error; ++ } ++ ++#ifdef MCA_TAMPER_HAS_EXTRA_SYSFS_ENTRIES ++ if (iface < digital_tamper_cnt) ++ iiod->info = &mca_tamper_info; ++ else ++ iiod->info = &mca_tamper_analog_info; ++#else ++ iiod->info = &mca_tamper_info; ++#endif ++ iiod->num_channels = 1; ++ mca_init_channel(channels, iface); ++ ++ ret = mca_init_hardware(mca_tamper[iface]); ++ if (ret != 0) { ++ /* Skip the error msg if interface is not enabled */ ++ if (ret != -ENODEV) ++ dev_err(&pdev->dev, "Failed to init Tamper %d\n", ++ iface); ++ continue; ++ } ++ ++ mca_tamper[iface]->irq = platform_get_irq_byname(pdev, ++ mca_tamper[iface]->name); ++ if (mca_tamper[iface]->irq) { ++ ret = devm_request_threaded_irq(&pdev->dev, ++ mca_tamper[iface]->irq, ++ NULL, mca_tamper_irq_handler, ++ IRQF_TRIGGER_LOW | IRQF_ONESHOT, ++ mca_tamper[iface]->name, ++ iiod); ++ if (ret) { ++ dev_err(&pdev->dev, ++ "Requested TAMPER %d IRQ (%d).\n", ++ iface, mca_tamper[iface]->irq); ++ continue; ++ } ++ } ++ ++ ret = iio_device_register(iiod); ++ if (ret) { ++ dev_err(&pdev->dev, ++ "Failed to register mca tamper iio device\n"); ++ continue; ++ } ++ ++ dev_info(&pdev->dev, "Tamper %d enabled\n", iface); ++ } ++ ++ return 0; ++ ++exit_error: ++ for (iface = 0; iface < num_tamper_ifaces; iface++) { ++ if (mca_tamper[iface]) { ++ if (mca_tamper[iface]->iio) ++ kfree(mca_tamper[iface]->iio->channels); ++ kfree(mca_tamper[iface]->iio); ++ } ++ } ++ kfree(mca_tamper); ++ ++ return ret; ++} ++ ++static int mca_tamper_remove(struct platform_device *pdev) ++{ ++ struct mca_tamper **mca_tamper = dev_get_drvdata(pdev->dev.parent); ++ struct mca_drv *mca; ++ const struct mca_tamper_data *devdata = of_device_get_match_data(&pdev->dev); ++ u16 num_tamper_ifaces = devdata->num_tamper_ifaces; ++ u32 iface; ++ ++ for (iface = 0; iface < num_tamper_ifaces; iface++) { ++ if (mca_tamper[iface] == NULL) ++ continue; ++ /* Release the resources allocated */ ++ mca = mca_tamper[iface]->mca; ++ if (mca->gpio_base >= 0) { ++ devm_gpio_free(&pdev->dev, ++ mca->gpio_base + mca_tamper[iface]->io_in); ++ if (mca_tamper[iface]->config0 & MCA_TAMPER_OUT_EN) ++ devm_gpio_free(&pdev->dev, ++ mca->gpio_base + mca_tamper[iface]->io_out); ++ } ++ devm_kfree(&pdev->dev, (void *)mca_tamper[iface]->iio->channels); ++ if (mca_tamper[iface]->irq) ++ devm_free_irq(&pdev->dev, mca_tamper[iface]->irq, ++ mca_tamper[iface]->iio); ++ iio_device_unregister(mca_tamper[iface]->iio); ++ devm_iio_device_free(&pdev->dev, mca_tamper[iface]->iio); ++ } ++ kfree(mca_tamper); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_OF ++static struct mca_tamper_data mca_tamper_devdata[] = { ++ [CC6UL_MCA_TAMPER] = { ++ .devtype = CC6UL_MCA_TAMPER, ++ .num_tamper_ifaces = 4, ++ .digital_tamper_cnt = 2 ++ }, ++ [CC8X_MCA_TAMPER] = { ++ .devtype = CC8X_MCA_TAMPER, ++ .num_tamper_ifaces = 4, ++ .digital_tamper_cnt = 2 ++ }, ++}; ++ ++static const struct of_device_id mca_tamper_ids[] = { ++ { .compatible = "digi,mca-cc6ul-tamper", ++ .data = &mca_tamper_devdata[CC6UL_MCA_TAMPER]}, ++ { .compatible = "digi,mca-cc8x-tamper", ++ .data = &mca_tamper_devdata[CC8X_MCA_TAMPER]}, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, mca_tamper_ids); ++#endif ++ ++static struct platform_driver mca_tamper_driver = { ++ .probe = mca_tamper_probe, ++ .remove = mca_tamper_remove, ++ .driver = { ++ .name = MCA_DRVNAME_TAMPER, ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(mca_tamper_ids), ++ }, ++}; ++ ++static int __init mca_tamper_init(void) ++{ ++ return platform_driver_register(&mca_tamper_driver); ++} ++module_init(mca_tamper_init); ++ ++static void __exit mca_tamper_exit(void) ++{ ++ platform_driver_unregister(&mca_tamper_driver); ++} ++module_exit(mca_tamper_exit); ++ ++MODULE_AUTHOR("Digi International Inc"); ++MODULE_DESCRIPTION("Tamper driver for MCA of ConnectCore Modules"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:" MCA_DRVNAME_TAMPER); diff --git a/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ul/0009-imx6ul-Add-MCA-UART-support-for-ConnectCore-6UL-SOM-.patch b/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ul/0009-imx6ul-Add-MCA-UART-support-for-ConnectCore-6UL-SOM-.patch new file mode 100644 index 000000000..f7231ada6 --- /dev/null +++ b/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ul/0009-imx6ul-Add-MCA-UART-support-for-ConnectCore-6UL-SOM-.patch @@ -0,0 +1,1097 @@ +From d7195e862d6d57351d180711b7ae9194e18bdcc3 Mon Sep 17 00:00:00 2001 +From: Alex Gonzalez +Date: Mon, 23 Apr 2018 11:43:44 +0200 +Subject: [PATCH] imx6ul: Add MCA UART support for ConnectCore 6UL SOM and SBCs + +Synched with v4.14.78/master at: +3f8b03950b323db4ca89b1cdc1c2288f79facaa3 + +Signed-off-by: Alex Gonzalez +--- + arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts | 14 + + arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi | 4 + + arch/arm/configs/imx_v6_v7_defconfig | 1 + + drivers/tty/serial/Kconfig | 10 + + drivers/tty/serial/Makefile | 1 + + drivers/tty/serial/mca-cc6ul-uart.c | 983 ++++++++++++++++++++++++++++ + 6 files changed, 1013 insertions(+) + create mode 100644 drivers/tty/serial/mca-cc6ul-uart.c + +diff --git a/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts b/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts +index 7cbc14d56680..afc87b34f441 100644 +--- a/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts ++++ b/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts +@@ -169,6 +169,19 @@ + digi,tamper-if-list = <0 1 2 3>; + }; + ++/* ++ * Enable MCA UART: ++ * - MCA_IO1 (TX) ++ * - MCA_IO2 (RX) ++ */ ++&mca_uart { ++ status = "okay"; ++ ++ /* Edit to nable CTS and/or RTS in any MCA GPIO-capable line */ ++ rts-pin = <3>; ++ cts-pin = <5>; ++}; ++ + &pwm1 { + status = "okay"; + }; +@@ -445,3 +458,4 @@ + >; + }; + }; ++ +diff --git a/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi b/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi +index a20fb5ffc98b..c721320a4117 100644 +--- a/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi ++++ b/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi +@@ -83,6 +83,10 @@ + compatible = "digi,mca-cc6ul-tamper"; + }; + ++ mca_uart: uart { ++ compatible = "digi,mca-cc6ul-uart"; ++ status = "disabled"; ++ }; + }; + + pfuze3000: pmic@8 { +diff --git a/arch/arm/configs/imx_v6_v7_defconfig b/arch/arm/configs/imx_v6_v7_defconfig +index 188fb4309851..0a07b13ce593 100644 +--- a/arch/arm/configs/imx_v6_v7_defconfig ++++ b/arch/arm/configs/imx_v6_v7_defconfig +@@ -190,6 +190,7 @@ CONFIG_SERIO_SERPORT=m + # CONFIG_LEGACY_PTYS is not set + CONFIG_SERIAL_IMX=y + CONFIG_SERIAL_IMX_CONSOLE=y ++CONFIG_SERIAL_MCA_CC6UL=y + CONFIG_SERIAL_FSL_LPUART=y + CONFIG_SERIAL_FSL_LPUART_CONSOLE=y + CONFIG_SERIAL_DEV_BUS=y +diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig +index 32886c304641..9a113b96ad17 100644 +--- a/drivers/tty/serial/Kconfig ++++ b/drivers/tty/serial/Kconfig +@@ -745,6 +745,16 @@ config SERIAL_CORE_CONSOLE + config CONSOLE_POLL + bool + ++config SERIAL_MCA_CC6UL ++ tristate "Digi ConnectCore 6UL Micro Controller Assist UART" ++ select MFD_MCA_CC6UL ++ help ++ If you say Y here you will get support for the UART in the Micro ++ Controller Assist of Digi ConnectCore 6UL system-on-module. ++ ++ This driver can also be built as a module, if so, the module ++ will be called "mca-cc6ul-uart". ++ + config SERIAL_MCF + bool "Coldfire serial support" + depends on COLDFIRE +diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile +index daac675612df..00dadeaaf331 100644 +--- a/drivers/tty/serial/Makefile ++++ b/drivers/tty/serial/Makefile +@@ -85,6 +85,7 @@ obj-$(CONFIG_SERIAL_CONEXANT_DIGICOLOR) += digicolor-usart.o + obj-$(CONFIG_SERIAL_MEN_Z135) += men_z135_uart.o + obj-$(CONFIG_SERIAL_SPRD) += sprd_serial.o + obj-$(CONFIG_SERIAL_STM32) += stm32-usart.o ++obj-$(CONFIG_SERIAL_MCA_CC6UL) += mca-cc6ul-uart.o + obj-$(CONFIG_SERIAL_MVEBU_UART) += mvebu-uart.o + obj-$(CONFIG_SERIAL_PIC32) += pic32_uart.o + obj-$(CONFIG_SERIAL_MPS2_UART) += mps2-uart.o +diff --git a/drivers/tty/serial/mca-cc6ul-uart.c b/drivers/tty/serial/mca-cc6ul-uart.c +new file mode 100644 +index 000000000000..b7bd0b2a8149 +--- /dev/null ++++ b/drivers/tty/serial/mca-cc6ul-uart.c +@@ -0,0 +1,983 @@ ++/* mca-cc6ul-uart.c - UART driver for MCA devices. ++ * Based on sc16is7xx.c, by Jon Ringle ++ * ++ * Copyright (C) 2017 Digi International Inc ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Library General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Library General Public License for more details. ++ * ++ * You should have received a copy of the GNU Library General Public ++ * License along with this library. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define MCA_UART_DEV_NAME "ttyMCA" ++#define MCA_UART_DEFAULT_BRATE 9600 ++#define MCA_UART_DEFAULT_BAUD_REG MCA_REG_UART_BAUD_9600 ++#define MCA_UART_MIN_BAUD 1200 ++#define MCA_UART_MAX_BAUD 230400 ++#define MCA_UART_RX_FIFO_SIZE 128 ++#define MCA_UART_TX_FIFO_SIZE 128 ++#define MCA_UART_CLK 24000000 ++#define MCA_UART_MIN_FW_VERSION MCA_MAKE_FW_VER(1, 1) ++ ++#define MCA_UART_HAS_RTS BIT(0) ++#define MCA_UART_HAS_CTS BIT(1) ++ ++#define to_mca_uart(p, e) (container_of((p), struct mca_uart, e)) ++ ++enum { ++ WORK_STOP_RX = BIT(0), ++ WORK_STOP_TX = BIT(1), ++ WORK_SET_RTS = BIT(2), ++ WORK_CLEAR_RTS = BIT(3), ++}; ++ ++struct mca_uart { ++ struct mca_drv *mca; ++ struct device *dev; ++ struct uart_driver uart; ++ struct uart_port port; ++ struct mutex mutex; ++ unsigned int pending_work; ++ struct work_struct tx_work; ++ struct work_struct delayed_work; ++ unsigned int has_rtscts; ++ int rts_pin; ++ int cts_pin; ++ bool enable_power_on; ++}; ++ ++static void mca_uart_stop_tx(struct uart_port *port) ++{ ++ struct mca_uart *mca_uart = dev_get_drvdata(port->dev); ++ ++ dev_dbg(mca_uart->dev, "<%s>\n", __func__); ++ ++ /* Work is queued since we can't access I2C regmap in atomic context */ ++ mca_uart->pending_work |= WORK_STOP_TX; ++ if (!work_pending(&mca_uart->delayed_work)) ++ schedule_work(&mca_uart->delayed_work); ++} ++ ++static void mca_uart_stop_rx(struct uart_port *port) ++{ ++ struct mca_uart *mca_uart = dev_get_drvdata(port->dev); ++ ++ dev_dbg(mca_uart->dev, "<%s>\n", __func__); ++ ++ /* Work is queued since we can't access I2C regmap in atomic context */ ++ mca_uart->pending_work |= WORK_STOP_RX; ++ if (!work_pending(&mca_uart->delayed_work)) ++ schedule_work(&mca_uart->delayed_work); ++} ++ ++static void mca_uart_start_tx(struct uart_port *port) ++{ ++ struct mca_uart *mca_uart = to_mca_uart(port, port); ++ ++ dev_dbg(mca_uart->dev, "<%s>\n", __func__); ++ ++ if (!work_pending(&mca_uart->tx_work)) ++ schedule_work(&mca_uart->tx_work); ++} ++ ++static unsigned int mca_uart_tx_empty(struct uart_port *port) ++{ ++ struct mca_uart *mca_uart = to_mca_uart(port, port); ++ struct regmap *regmap = mca_uart->mca->regmap; ++ unsigned int txlvl; ++ int ret; ++ ++ dev_dbg(mca_uart->dev, "<%s>\n", __func__); ++ ++ ret = regmap_read(regmap, MCA_REG_UART_TXLVL, &txlvl); ++ if (ret) { ++ dev_err(mca_uart->dev, "Failed to read MCA_REG_UART_TXLVL\n"); ++ /* This is the behavior if not implemented */ ++ return TIOCSER_TEMT; ++ } ++ ++ return (txlvl == MCA_UART_TX_FIFO_SIZE) ? TIOCSER_TEMT : 0; ++} ++ ++static unsigned int mca_uart_get_mctrl(struct uart_port *port) ++{ ++ struct mca_uart *mca_uart = to_mca_uart(port, port); ++ ++ dev_dbg(mca_uart->dev, "<%s>\n", __func__); ++ /* ++ * DCD and DSR are not wired and CTS/RTS is handled automatically ++ * so just indicate DSR and CAR asserted. Also regmap cannot be called ++ * from atomic context, so reading the status of the lines here is not ++ * possible. ++ */ ++ return TIOCM_DSR | TIOCM_CAR; ++} ++ ++static void mca_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) ++{ ++ struct mca_uart *mca_uart = to_mca_uart(port, port); ++ ++ dev_dbg(mca_uart->dev, "<%s>\n", __func__); ++ /* ++ * Regmap cannot be called from atomic context, so this would require a ++ * work queue to set/clear RTS. However, that line is handled ++ * automatically by the hardware when using flow control, and the ++ * get_mctrl for reading CTS and RTS cannot be implemented for the same ++ * reason. If RTS/CTS are used for something different that hardware ++ * flow control, perhaps they should be declared as GPIOs. ++ */ ++} ++ ++static void mca_uart_break_ctl(struct uart_port *port, int break_state) ++{ ++ struct mca_uart *mca_uart = to_mca_uart(port, port); ++ ++ dev_dbg(mca_uart->dev, "<%s>\n", __func__); ++ dev_warn(mca_uart->dev, "BREAK condition not supported\n"); ++} ++ ++static void mca_uart_set_termios(struct uart_port *port, ++ struct ktermios *termios, ++ struct ktermios *old) ++{ ++ struct mca_uart *mca_uart = to_mca_uart(port, port); ++ struct regmap *regmap = mca_uart->mca->regmap; ++ unsigned int cfg1 = 0; ++ unsigned int baudrate; ++ unsigned int baud_reg_val; ++ int ret; ++ ++ dev_dbg(mca_uart->dev, "<%s>\n", __func__); ++ ++ /* Mask unsupported termios capabilities */ ++ if (!mca_uart->has_rtscts) ++ termios->c_cflag &= ~CRTSCTS; ++ ++ termios->c_iflag &= ~(IXON | IXOFF | IXANY | CMSPAR | CSIZE); ++ ++ /* Only 8-bit size supported */ ++ termios->c_cflag |= CS8; ++ ++ if (termios->c_cflag & CSTOPB) ++ cfg1 |= MCA_REG_UART_CFG1_TWO_STOPBITS; ++ if (termios->c_cflag & PARENB) ++ cfg1 |= MCA_REG_UART_CFG1_PARITY_EN; ++ if (termios->c_cflag & PARODD) ++ cfg1 |= MCA_REG_UART_CFG1_PARITY_ODD; ++ if (termios->c_cflag & CRTSCTS) { ++ if (mca_uart->has_rtscts & MCA_UART_HAS_CTS) { ++ cfg1 |= MCA_REG_UART_CFG1_CTS_EN; ++ port->status |= UPSTAT_AUTOCTS; ++ } ++ if (mca_uart->has_rtscts & MCA_UART_HAS_RTS) { ++ cfg1 |= MCA_REG_UART_CFG1_RTS_EN; ++ port->status |= UPSTAT_AUTORTS; ++ } ++ } else { ++ port->status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS); ++ } ++ ++ ret = regmap_write(regmap, MCA_REG_UART_CFG1, cfg1); ++ if (ret) { ++ dev_err(mca_uart->dev, "Failed to write MCA_REG_UART_CFG1\n"); ++ return; ++ } ++ ++ baudrate = uart_get_baud_rate(port, termios, old, MCA_UART_MIN_BAUD, ++ MCA_UART_MAX_BAUD); ++ uart_update_timeout(port, termios->c_cflag, baudrate); ++ ++ switch (baudrate) { ++ case 1200: ++ baud_reg_val = MCA_REG_UART_BAUD_1200; ++ break; ++ case 2400: ++ baud_reg_val = MCA_REG_UART_BAUD_2400; ++ break; ++ case 4800: ++ baud_reg_val = MCA_REG_UART_BAUD_4800; ++ break; ++ case 9600: ++ baud_reg_val = MCA_REG_UART_BAUD_9600; ++ break; ++ case 19200: ++ baud_reg_val = MCA_REG_UART_BAUD_19200; ++ break; ++ case 38400: ++ baud_reg_val = MCA_REG_UART_BAUD_38400; ++ break; ++ case 57600: ++ baud_reg_val = MCA_REG_UART_BAUD_57600; ++ break; ++ case 115200: ++ baud_reg_val = MCA_REG_UART_BAUD_115200; ++ break; ++ case 230400: ++ baud_reg_val = MCA_REG_UART_BAUD_230400; ++ break; ++ default: ++ dev_warn(mca_uart->dev, ++ "Baud rate %d not supported, using default %d\n", ++ baudrate, MCA_UART_DEFAULT_BRATE); ++ baud_reg_val = MCA_UART_DEFAULT_BAUD_REG; ++ break; ++ } ++ ++ ret = regmap_write(regmap, MCA_REG_UART_BAUD, baud_reg_val); ++ if (ret) { ++ dev_err(mca_uart->dev, "Failed to write MCA_REG_UART_BAUD\n"); ++ return; ++ } ++} ++ ++static int mca_uart_startup(struct uart_port *port) ++{ ++ struct mca_uart *mca_uart = dev_get_drvdata(port->dev); ++ struct regmap *regmap = mca_uart->mca->regmap; ++ int ret; ++ unsigned int cfg_mask; ++ unsigned int ier_mask; ++ ++ dev_dbg(mca_uart->dev, "<%s>\n", __func__); ++ ++ /* Reset RX and TX FIFOs and enable TX and RX */ ++ cfg_mask = MCA_REG_UART_CFG0_CTX | MCA_REG_UART_CFG0_CRX | ++ MCA_REG_UART_CFG0_TXEN | MCA_REG_UART_CFG0_RXEN; ++ ++ ret = regmap_update_bits(regmap, MCA_REG_UART_CFG0, cfg_mask, cfg_mask); ++ if (ret) { ++ dev_err(mca_uart->dev, "Failed to read MCA_REG_UART_CFG0\n"); ++ return ret; ++ } ++ ++ ier_mask = MCA_REG_UART_IER_THR | MCA_REG_UART_IER_RHR | ++ MCA_REG_UART_IER_RLSE; ++ ret = regmap_update_bits(regmap, MCA_REG_UART_IER, ier_mask, ier_mask); ++ if (ret) ++ dev_err(mca_uart->dev, "Failed to read MCA_REG_UART_IER\n"); ++ ++ return ret; ++} ++ ++static void mca_uart_shutdown(struct uart_port *port) ++{ ++ struct mca_uart *mca_uart = dev_get_drvdata(port->dev); ++ struct regmap *regmap = mca_uart->mca->regmap; ++ int ret; ++ unsigned int cfg_mask; ++ ++ dev_dbg(mca_uart->dev, "<%s>\n", __func__); ++ ++ /* Reset RX and TX FIFOs and disable TX and RX */ ++ cfg_mask = MCA_REG_UART_CFG0_CTX | MCA_REG_UART_CFG0_CRX | ++ MCA_REG_UART_CFG0_TXEN | MCA_REG_UART_CFG0_RXEN; ++ ++ ret = regmap_update_bits(regmap, MCA_REG_UART_CFG0, cfg_mask, ++ MCA_REG_UART_CFG0_CTX | MCA_REG_UART_CFG0_CRX); ++ if (ret) ++ dev_err(mca_uart->dev, "Failed to read MCA_REG_UART_CFG0\n"); ++ ++ /* Disable all IRQs */ ++ ret = regmap_write(regmap, MCA_REG_UART_IER, 0); ++ if (ret) ++ dev_err(mca_uart->dev, "Failed to write MCA_REG_UART_IER\n"); ++ cancel_work_sync(&mca_uart->tx_work); ++ cancel_work_sync(&mca_uart->delayed_work); ++} ++ ++static const char *mca_uart_type(struct uart_port *port) ++{ ++ struct mca_uart *mca_uart = dev_get_drvdata(port->dev); ++ ++ dev_dbg(mca_uart->dev, "<%s>\n", __func__); ++ ++ return "MCA UART"; ++} ++ ++static int mca_uart_request_port(struct uart_port *port) ++{ ++ struct mca_uart *mca_uart = dev_get_drvdata(port->dev); ++ ++ dev_dbg(mca_uart->dev, "<%s>\n", __func__); ++ ++ /* Do nothing */ ++ return 0; ++} ++ ++static void mca_uart_config_port(struct uart_port *port, int flags) ++{ ++ struct mca_uart *mca_uart = dev_get_drvdata(port->dev); ++ ++ dev_dbg(mca_uart->dev, "<%s>\n", __func__); ++ if (flags & UART_CONFIG_TYPE) ++ port->type = PORT_LPUART; ++} ++ ++static int mca_uart_verify_port(struct uart_port *port, ++ struct serial_struct *s) ++{ ++ struct mca_uart *mca_uart = dev_get_drvdata(port->dev); ++ ++ dev_dbg(mca_uart->dev, "<%s>\n", __func__); ++ if ((s->type != PORT_UNKNOWN) && (s->type != PORT_LPUART)) ++ return -EINVAL; ++ if (s->irq != port->irq) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static void mca_uart_release_port(struct uart_port *port) ++{ ++ struct mca_uart *mca_uart = dev_get_drvdata(port->dev); ++ ++ dev_dbg(mca_uart->dev, "<%s>\n", __func__); ++ /* Do nothing */ ++} ++ ++static void mca_uart_throttle(struct uart_port *port) ++{ ++ struct mca_uart *mca_uart = dev_get_drvdata(port->dev); ++ struct regmap *regmap = mca_uart->mca->regmap; ++ int ret; ++ ++ dev_dbg(mca_uart->dev, "<%s>\n", __func__); ++ mutex_lock(&mca_uart->mutex); ++ ret = regmap_update_bits(regmap, MCA_REG_UART_CFG1, ++ MCA_REG_UART_CFG1_THROTTLE, ++ MCA_REG_UART_CFG1_THROTTLE); ++ if (ret) ++ dev_err(mca_uart->dev, "Failed to write MCA_REG_UART_CFG1\n"); ++ mutex_unlock(&mca_uart->mutex); ++} ++ ++static void mca_uart_unthrottle(struct uart_port *port) ++{ ++ struct mca_uart *mca_uart = dev_get_drvdata(port->dev); ++ struct regmap *regmap = mca_uart->mca->regmap; ++ int ret; ++ ++ dev_dbg(mca_uart->dev, "<%s>\n", __func__); ++ mutex_lock(&mca_uart->mutex); ++ ret = regmap_update_bits(regmap, MCA_REG_UART_CFG1, ++ MCA_REG_UART_CFG1_THROTTLE, 0); ++ if (ret) ++ dev_err(mca_uart->dev, "Failed to write MCA_REG_UART_CFG1\n"); ++ mutex_unlock(&mca_uart->mutex); ++} ++ ++static const struct uart_ops mca_uart_ops = { ++ .tx_empty = mca_uart_tx_empty, ++ .set_mctrl = mca_uart_set_mctrl, ++ .get_mctrl = mca_uart_get_mctrl, ++ .stop_tx = mca_uart_stop_tx, ++ .start_tx = mca_uart_start_tx, ++ .stop_rx = mca_uart_stop_rx, ++ .break_ctl = mca_uart_break_ctl, ++ .startup = mca_uart_startup, ++ .shutdown = mca_uart_shutdown, ++ .set_termios = mca_uart_set_termios, ++ .type = mca_uart_type, ++ .request_port = mca_uart_request_port, ++ .release_port = mca_uart_release_port, ++ .config_port = mca_uart_config_port, ++ .verify_port = mca_uart_verify_port, ++ .throttle = mca_uart_throttle, ++ .unthrottle = mca_uart_unthrottle, ++ .pm = NULL, ++}; ++ ++static void mca_uart_handle_tx(struct uart_port *port) ++{ ++ struct mca_uart *mca_uart = dev_get_drvdata(port->dev); ++ struct regmap *regmap = mca_uart->mca->regmap; ++ struct circ_buf *xmit = &port->state->xmit; ++ unsigned int to_send; ++ uint8_t tx_buf[MCA_UART_TX_FIFO_SIZE]; ++ ++ /* ++ * There is a corner case in which the job is scheduled after the port ++ * has been shut down and port->state->port.tty is NULL. If not checked, ++ * uart_tx_stopped() would crash. ++ */ ++ if (!port->state->port.tty || uart_circ_empty(xmit) || ++ uart_tx_stopped(port)) ++ return; ++ ++ /* Get length of data pending in circular buffer */ ++ to_send = uart_circ_chars_pending(xmit); ++ if (likely(to_send)) { ++ unsigned int txlen; ++ unsigned int i; ++ int ret; ++ ++ /* Limit to size of TX FIFO */ ++ ret = regmap_read(regmap, MCA_REG_UART_TXLVL, &txlen); ++ if (ret) { ++ dev_err(mca_uart->dev, ++ "Failed to read MCA_REG_UART_TXLVL\n"); ++ txlen = 0; ++ } ++ ++ if (unlikely(!txlen)) { ++ dev_dbg(mca_uart->dev, "TX FIFO is full\n"); ++ if (!work_pending(&mca_uart->tx_work)) ++ schedule_work(&mca_uart->tx_work); ++ return; ++ } ++ ++ if (unlikely(txlen > sizeof(tx_buf))) { ++ dev_err(mca_uart->dev, ++ "Invalid MCA_REG_UART_TXLVL value %d\n", txlen); ++ if (!work_pending(&mca_uart->tx_work)) ++ schedule_work(&mca_uart->tx_work); ++ return; ++ } ++ ++ if (to_send > txlen) ++ to_send = txlen; ++ ++ port->icount.tx += to_send; ++ /* Convert to linear buffer */ ++ for (i = 0; i < to_send; ++i) { ++ tx_buf[i] = xmit->buf[xmit->tail]; ++ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); ++ } ++ ++ ret = regmap_bulk_write(regmap, MCA_REG_UART_THR, tx_buf, ++ to_send); ++ if (ret) ++ dev_err(mca_uart->dev, ++ "Failed to write MCA_REG_UART_THR\n"); ++ } ++ ++ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) ++ uart_write_wakeup(port); ++} ++ ++static void mca_uart_handle_rx(struct mca_uart *mca_uart, bool has_errors) ++{ ++ struct uart_port *port = &mca_uart->port; ++ struct regmap *regmap = mca_uart->mca->regmap; ++ unsigned int flag = TTY_NORMAL; ++ unsigned int lsr; ++ unsigned int rxlen; ++ unsigned int i; ++ int ret; ++ uint8_t rx_buf[MCA_UART_RX_FIFO_SIZE]; ++ uint8_t error_buf[MCA_UART_RX_FIFO_SIZE]; ++ ++ dev_dbg(mca_uart->dev, "<%s>\n", __func__); ++ ++ ret = regmap_read(regmap, MCA_REG_UART_RXLVL, &rxlen); ++ if (ret) { ++ dev_err(mca_uart->dev, "Failed to read MCA_REG_UART_RXLVL\n"); ++ return; ++ } ++ ++ if (unlikely(!rxlen)) ++ return; ++ ++ if (unlikely(has_errors)) { ++ ret = regmap_read(regmap, MCA_REG_UART_LSR, &lsr); ++ if (ret) { ++ dev_err(mca_uart->dev, ++ "Failed to read MCA_REG_UART_LSR\n"); ++ return; ++ } ++ if (likely(lsr)) { ++ ret = regmap_bulk_read(regmap, MCA_REG_UART_RX_ERRORS, ++ error_buf, rxlen); ++ if (ret) { ++ dev_err(mca_uart->dev, ++ "Failed to read MCA_REG_UART_RX_ERRORS\n"); ++ return; ++ } ++ } else { ++ /* No errors */ ++ has_errors = false; ++ } ++ } ++ ++ ret = regmap_bulk_read(regmap, MCA_REG_UART_RHR, rx_buf, rxlen); ++ if (ret) { ++ dev_warn(mca_uart->dev, ++ "Failed to read MCA_REG_UART_RHR %d, retrying\n", ret); ++ ret = regmap_bulk_read(regmap, MCA_REG_UART_RHR, rx_buf, rxlen); ++ if (ret) { ++ dev_err(mca_uart->dev, ++ "Failed to read MCA_REG_UART_RHR %d\n", ret); ++ goto exit; ++ } ++ } ++ ++ port->icount.rx += rxlen; ++ for (i = 0; i < rxlen; i++) { ++ uint8_t const ch = rx_buf[i]; ++ ++ if (uart_handle_sysrq_char(port, ch)) ++ continue; ++ ++ if (unlikely(has_errors)) { ++ switch (error_buf[i]) { ++ case MCA_REG_UART_LSR_FRAMING_ERROR: ++ flag = TTY_FRAME; ++ port->icount.frame++; ++ break; ++ case MCA_REG_UART_LSR_PARITY_ERROR: ++ flag = TTY_PARITY; ++ port->icount.parity++; ++ break; ++ case MCA_REG_UART_LSR_FIFO_OR_ERROR: ++ /* MCA didn't read its UART fast enough */ ++ flag = TTY_OVERRUN; ++ port->icount.overrun++; ++ break; ++ case MCA_REG_UART_LSR_BREAK: ++ case MCA_REG_UART_LSR_HW_OR_ERROR: ++ flag = TTY_BREAK; ++ port->icount.brk++; ++ break; ++ case MCA_REG_UART_LSR_NO_ERROR: ++ default: ++ flag = TTY_NORMAL; ++ break; ++ } ++ } ++ ret = tty_insert_flip_char(&port->state->port, ch, flag); ++ if (!ret) { ++ dev_err(mca_uart->dev, ++ "tty_insert_flip_char failed for %x\n", ch); ++ port->icount.overrun++; ++ break; ++ } ++ } ++exit: ++ tty_flip_buffer_push(&port->state->port); ++} ++ ++static irqreturn_t mca_uart_irq_handler(int irq, void *private) ++{ ++ struct mca_uart *mca_uart = private; ++ struct regmap *regmap = mca_uart->mca->regmap; ++ unsigned int iir; ++ int ret; ++ ++ dev_dbg(mca_uart->dev, "<%s>\n", __func__); ++ mutex_lock(&mca_uart->mutex); ++ ++ ret = regmap_read(regmap, MCA_REG_UART_IIR, &iir); ++ if (ret) { ++ dev_err(mca_uart->dev, "Failed to read MCA_REG_UART_IIR\n"); ++ return IRQ_HANDLED; ++ } ++ ++ if (iir & MCA_REG_UART_IIR_RHR) { ++ bool has_errors = iir & MCA_REG_UART_IIR_RLSE; ++ ++ mca_uart_handle_rx(mca_uart, has_errors); ++ } ++ ++ if (iir & MCA_REG_UART_IIR_THR) { ++ if (!work_pending(&mca_uart->tx_work)) ++ schedule_work(&mca_uart->tx_work); ++ } ++ ++ mutex_unlock(&mca_uart->mutex); ++ return IRQ_HANDLED; ++} ++ ++static void mca_uart_delayed_work_proc(struct work_struct *ws) ++{ ++ struct mca_uart *mca_uart = to_mca_uart(ws, delayed_work); ++ struct regmap *regmap = mca_uart->mca->regmap; ++ int ret; ++ unsigned int ier_mask = 0; ++ unsigned int cfg0_mask = 0; ++ ++ dev_dbg(mca_uart->dev, "<%s>\n", __func__); ++ ++ mutex_lock(&mca_uart->mutex); ++ ++ if (!mca_uart->pending_work) ++ return; ++ if (mca_uart->pending_work & WORK_STOP_RX) { ++ ier_mask |= MCA_REG_UART_IER_RHR; ++ cfg0_mask |= MCA_REG_UART_CFG0_CRX; ++ } ++ if (mca_uart->pending_work & WORK_STOP_TX) { ++ ier_mask |= MCA_REG_UART_IER_THR; ++ cfg0_mask |= MCA_REG_UART_CFG0_CTX; ++ } ++ ++ ret = regmap_update_bits(regmap, MCA_REG_UART_IER, ier_mask, 0); ++ if (ret) ++ dev_err(mca_uart->dev, "Failed to write MCA_REG_UART_IER\n"); ++ ++ ret = regmap_update_bits(regmap, MCA_REG_UART_CFG0, cfg0_mask, ++ MCA_REG_UART_CFG0_CRX | MCA_REG_UART_CFG0_CRX); ++ if (ret) ++ dev_err(mca_uart->dev, "Failed to write MCA_REG_UART_CFG0\n"); ++ ++ if ((mca_uart->pending_work & WORK_SET_RTS) || ++ (mca_uart->pending_work & WORK_CLEAR_RTS)) { ++ uint8_t msr_mask = mca_uart->pending_work & WORK_SET_RTS ? ++ MCA_REG_UART_MSR_RTS : 0; ++ ret = regmap_update_bits(regmap, MCA_REG_UART_MSR, ++ MCA_REG_UART_MSR_RTS, msr_mask); ++ if (ret) ++ dev_err(mca_uart->dev, ++ "Failed to write MCA_REG_UART_MSR\n"); ++ } ++ ++ mca_uart->pending_work = 0; ++ mutex_unlock(&mca_uart->mutex); ++} ++ ++static void mca_uart_tx_work_proc(struct work_struct *ws) ++{ ++ struct mca_uart *mca_uart = to_mca_uart(ws, tx_work); ++ ++ dev_dbg(mca_uart->dev, "<%s>\n", __func__); ++ ++ mutex_lock(&mca_uart->mutex); ++ mca_uart_handle_tx(&mca_uart->port); ++ mutex_unlock(&mca_uart->mutex); ++} ++ ++static ssize_t power_on_rx_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct tty_port *port = dev_get_drvdata(dev); ++ struct uart_state *state = container_of(port, struct uart_state, port); ++ struct uart_port *uart_port = state->uart_port; ++ struct mca_uart *mca_uart = container_of(uart_port, struct mca_uart, ++ port); ++ ++ dev_dbg(mca_uart->dev, "<%s>\n", __func__); ++ ++ return sprintf(buf, "%s\n", mca_uart->enable_power_on ? ++ "enabled" : "disabled"); ++} ++ ++static ssize_t power_on_rx_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct tty_port *port = dev_get_drvdata(dev); ++ struct uart_state *state = container_of(port, struct uart_state, port); ++ struct uart_port *uart_port = state->uart_port; ++ struct mca_uart *mca_uart = container_of(uart_port, struct mca_uart, ++ port); ++ struct regmap *regmap = mca_uart->mca->regmap; ++ int ret; ++ ++ dev_dbg(mca_uart->dev, "<%s>\n", __func__); ++ ++ if (!strncmp(buf, "enabled", sizeof("enabled") - 1)) ++ mca_uart->enable_power_on = true; ++ else if (!strncmp(buf, "disabled", sizeof("disabled") - 1)) ++ mca_uart->enable_power_on = false; ++ else ++ return -EINVAL; ++ ++ ret = regmap_update_bits(regmap, MCA_REG_UART_CFG0, ++ MCA_REG_UART_CFG0_PWR_ON, ++ mca_uart->enable_power_on ? ++ MCA_REG_UART_CFG0_PWR_ON : 0); ++ if (ret < 0) ++ dev_err(mca_uart->dev, "Failed to write MCA_REG_UART_CFG0\n"); ++ return count; ++} ++static DEVICE_ATTR(power_on_rx, 0600, power_on_rx_show, power_on_rx_store); ++ ++static struct attribute *uart_sysfs_entries[] = { ++ &dev_attr_power_on_rx.attr, ++ NULL, ++}; ++ ++static struct attribute_group uart_port_extra_attr = { ++ .name = "power_extra_opts", ++ .attrs = uart_sysfs_entries, ++}; ++ ++static int mca_uart_probe(struct platform_device *pdev) ++{ ++ struct mca_drv *mca = dev_get_drvdata(pdev->dev.parent); ++ struct regmap *regmap = mca->regmap; ++ struct mca_uart *mca_uart; ++ struct device_node *np; ++ int ret; ++ ++ dev_dbg(&pdev->dev, "<%s>\n", __func__); ++ ++ if (IS_ERR(mca)) ++ return PTR_ERR(mca); ++ ++ /* Find entry in device-tree */ ++ if (!mca->dev->of_node) ++ return -ENODEV; ++ ++ /* Check if node does not exist or if it is disabled */ ++ np = of_find_compatible_node(mca->dev->of_node, NULL, ++ "digi,mca-cc6ul-uart"); ++ if (!np || !of_device_is_available(np)) ++ return -ENODEV; ++ ++ if (mca->fw_version < MCA_UART_MIN_FW_VERSION) { ++ dev_err(&pdev->dev, ++ "UART is not supported in MCA firmware v%d.%02d.\n", ++ MCA_FW_VER_MAJOR(mca->fw_version), ++ MCA_FW_VER_MINOR(mca->fw_version)); ++ return -ENODEV; ++ } ++ ++ mca_uart = devm_kzalloc(&pdev->dev, sizeof(*mca_uart), GFP_KERNEL); ++ if (!mca_uart) ++ return -ENOMEM; ++ ++ mca_uart->mca = mca; ++ mca_uart->dev = &pdev->dev; ++ platform_set_drvdata(pdev, mca_uart); ++ ++ mca_uart->enable_power_on = false; ++ mca_uart->has_rtscts = 0; ++ ++ ret = of_property_read_u32(np, "rts-pin", &mca_uart->rts_pin); ++ if (ret) { ++ dev_dbg(&pdev->dev, "No RTS pin provided\n"); ++ } else { ++ const int gpio_base = mca_uart->mca->gpio_base; ++ ++ ret = devm_gpio_request(&pdev->dev, ++ gpio_base + mca_uart->rts_pin, ++ "MCA UART RTS"); ++ if (ret) { ++ dev_err(&pdev->dev, ++ "Failed to allocate RTS pin\n"); ++ } else { ++ ret = regmap_write(regmap, MCA_REG_UART_RTSPIN, ++ mca_uart->rts_pin); ++ if (ret) ++ dev_err(mca_uart->dev, ++ "Failed to write MCA_REG_UART_RTSPIN\n"); ++ else ++ mca_uart->has_rtscts |= MCA_UART_HAS_RTS; ++ } ++ } ++ ++ ret = of_property_read_u32(np, "cts-pin", &mca_uart->cts_pin); ++ if (ret) { ++ dev_dbg(&pdev->dev, "No CTS pin provided\n"); ++ } else { ++ const int gpio_base = mca_uart->mca->gpio_base; ++ ++ ret = devm_gpio_request(&pdev->dev, ++ gpio_base + mca_uart->cts_pin, ++ "MCA UART CTS"); ++ if (ret) { ++ dev_err(&pdev->dev, ++ "Failed to allocate CTS pin\n"); ++ } else { ++ ret = regmap_write(regmap, MCA_REG_UART_CTSPIN, ++ mca_uart->cts_pin); ++ if (ret) ++ dev_err(mca_uart->dev, ++ "Failed to write MCA_REG_UART_CTSPIN\n"); ++ else ++ mca_uart->has_rtscts |= MCA_UART_HAS_CTS; ++ } ++ } ++ ++ /* Register UART driver */ ++ mca_uart->uart.owner = THIS_MODULE; ++ mca_uart->uart.dev_name = MCA_UART_DEV_NAME; ++ mca_uart->uart.nr = 1; ++ ret = uart_register_driver(&mca_uart->uart); ++ if (ret) { ++ dev_err(&pdev->dev, "Registering UART driver failed\n"); ++ goto error; ++ } ++ ++ mutex_init(&mca_uart->mutex); ++ ++ /* Initialize port data */ ++ mca_uart->port.line = 0; ++ mca_uart->port.dev = &pdev->dev; ++ mca_uart->port.irq = platform_get_irq_byname(pdev, ++ MCA_IRQ_UART_NAME); ++ mca_uart->port.type = PORT_LPUART; ++ mca_uart->port.fifosize = max(MCA_UART_TX_FIFO_SIZE, ++ MCA_UART_RX_FIFO_SIZE); ++ mca_uart->port.flags = UPF_FIXED_TYPE | UPF_LOW_LATENCY; ++ mca_uart->port.iotype = UPIO_PORT; ++ mca_uart->port.uartclk = MCA_UART_CLK; ++ mca_uart->port.rs485_config = NULL; ++ mca_uart->port.ops = &mca_uart_ops; ++ mca_uart->port.attr_group = &uart_port_extra_attr; ++ ++ /* Initialize queue for start TX */ ++ INIT_WORK(&mca_uart->tx_work, mca_uart_tx_work_proc); ++ INIT_WORK(&mca_uart->delayed_work, mca_uart_delayed_work_proc); ++ ++ /* Register port */ ++ ret = uart_add_one_port(&mca_uart->uart, &mca_uart->port); ++ if (ret) { ++ dev_err(mca_uart->dev, "Failed adding a port (%d)\n", ret); ++ goto error; ++ } ++ ++ /* Setup interrupt */ ++ ret = devm_request_threaded_irq(&pdev->dev, ++ mca_uart->port.irq, ++ NULL, mca_uart_irq_handler, ++ IRQF_ONESHOT, ++ MCA_IRQ_UART_NAME, ++ mca_uart); ++ if (ret) { ++ dev_err(mca_uart->dev, "Failed to register IRQ\n"); ++ goto error; ++ } ++ ++ ret = regmap_write(regmap, MCA_REG_UART_CFG0, MCA_REG_UART_CFG0_ENABLE); ++ if (ret) { ++ dev_err(mca_uart->dev, "Failed to write MCA_REG_UART_CFG0\n"); ++ goto error; ++ } ++ ++ dev_info(mca_uart->dev, "Registered successfully\n"); ++ return 0; ++error: ++ mutex_destroy(&mca_uart->mutex); ++ uart_remove_one_port(&mca_uart->uart, &mca_uart->port); ++ uart_unregister_driver(&mca_uart->uart); ++ devm_kfree(&pdev->dev, mca_uart); ++ ++ return ret; ++} ++ ++static int mca_uart_remove(struct platform_device *pdev) ++{ ++ struct mca_uart *mca_uart = dev_get_drvdata(pdev->dev.parent); ++ ++ dev_dbg(mca_uart->dev, "<%s>\n", __func__); ++ ++ cancel_work_sync(&mca_uart->tx_work); ++ cancel_work_sync(&mca_uart->delayed_work); ++ mutex_destroy(&mca_uart->mutex); ++ uart_remove_one_port(&mca_uart->uart, &mca_uart->port); ++ uart_unregister_driver(&mca_uart->uart); ++ devm_kfree(&pdev->dev, mca_uart); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++ ++/* ++ * The code snippet below was grabbed from drivers/tty/serial/serial_core.c ++ * It is used for retrieving the TTY layer struct device. This struct is used to ++ * check the value of /sys/class/tty/ttyMCAx/power/wakeup which is more standard ++ * than the one at /sys/bus/i2c/devices/0-007e/mca-cc6ul-uart/power/wakeup. ++ */ ++struct uart_match { ++ struct uart_port *port; ++ struct uart_driver *driver; ++}; ++ ++static int serial_match_port(struct device *dev, void *data) ++{ ++ struct uart_match *match = data; ++ struct tty_driver *tty_drv = match->driver->tty_driver; ++ dev_t devt = MKDEV(tty_drv->major, tty_drv->minor_start) + ++ match->port->line; ++ ++ return dev->devt == devt; /* Actually, only one tty per port */ ++} ++ ++static int mca_cc6ul_uart_suspend(struct device *d) ++{ ++ int ret; ++ struct mca_uart *mca_uart = platform_get_drvdata(to_platform_device(d)); ++ struct regmap *regmap = mca_uart->mca->regmap; ++ struct uart_match match = {&mca_uart->port, &mca_uart->uart}; ++ struct device *tty_dev = device_find_child(mca_uart->port.dev, &match, ++ serial_match_port); ++ int mask = MCA_REG_UART_CFG0_WAKEUP; ++ unsigned int new_value = 0; ++ ++ if (tty_dev && device_may_wakeup(tty_dev)) ++ new_value |= MCA_REG_UART_CFG0_WAKEUP; ++ ++ ret = regmap_update_bits(regmap, MCA_REG_UART_CFG0, mask, new_value); ++ if (ret < 0) ++ dev_err(mca_uart->dev, "Failed to write MCA_REG_UART_CFG0\n"); ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops mca_cc6ul_uart_pm_ops = { ++ .suspend = mca_cc6ul_uart_suspend, ++ .resume = NULL, ++ .poweroff = NULL, ++}; ++#endif ++ ++#ifdef CONFIG_OF ++static const struct of_device_id mca_uart_ids[] = { ++ { .compatible = "digi,mca-cc6ul-uart", }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, mca_uart_ids); ++#endif ++ ++static struct platform_driver mca_uart_driver = { ++ .probe = mca_uart_probe, ++ .remove = mca_uart_remove, ++ .driver = { ++ .name = MCA_CC6UL_DRVNAME_UART, ++ .of_match_table = of_match_ptr(mca_uart_ids), ++#ifdef CONFIG_PM ++ .pm = &mca_cc6ul_uart_pm_ops, ++#endif ++ }, ++}; ++ ++static int __init mca_uart_init(void) ++{ ++ return platform_driver_register(&mca_uart_driver); ++} ++module_init(mca_uart_init); ++ ++static void __exit mca_uart_exit(void) ++{ ++ platform_driver_unregister(&mca_uart_driver); ++} ++module_exit(mca_uart_exit); ++ ++MODULE_AUTHOR("Digi International Inc"); ++MODULE_DESCRIPTION("UART for MCA of ConnectCore 6UL"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:" MCA_CC6UL_DRVNAME_UART); diff --git a/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ul/0010-imx6ul-Add-RTC-MCA-support-for-ConnectCore-6UL-SOM.patch b/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ul/0010-imx6ul-Add-RTC-MCA-support-for-ConnectCore-6UL-SOM.patch new file mode 100644 index 000000000..32698e587 --- /dev/null +++ b/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ul/0010-imx6ul-Add-RTC-MCA-support-for-ConnectCore-6UL-SOM.patch @@ -0,0 +1,540 @@ +From 33fb650af68bcc05f59181ae7a4f8424a0949f91 Mon Sep 17 00:00:00 2001 +From: Alex Gonzalez +Date: Mon, 23 Apr 2018 11:44:37 +0200 +Subject: [PATCH] imx6ul: Add RTC MCA support for ConnectCore 6UL SOM + +Synched with v4.14.78/master at: +3f8b03950b323db4ca89b1cdc1c2288f79facaa3 + +Signed-off-by: Alex Gonzalez +--- + arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi | 4 + + arch/arm/configs/imx_v6_v7_defconfig | 1 + + drivers/rtc/Kconfig | 12 + + drivers/rtc/Makefile | 1 + + drivers/rtc/rtc-mca.c | 454 ++++++++++++++++++++++++++++++ + 5 files changed, 472 insertions(+) + create mode 100644 drivers/rtc/rtc-mca.c + +diff --git a/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi b/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi +index c721320a4117..8163533c83b0 100644 +--- a/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi ++++ b/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi +@@ -69,6 +69,10 @@ + #interrupt-cells = <2>; + }; + ++ rtc { ++ compatible = "digi,mca-cc6ul-rtc"; ++ }; ++ + watchdog { + compatible = "digi,mca-cc6ul-wdt"; + digi,full-reset; +diff --git a/arch/arm/configs/imx_v6_v7_defconfig b/arch/arm/configs/imx_v6_v7_defconfig +index 0a07b13ce593..8a763ab6a365 100644 +--- a/arch/arm/configs/imx_v6_v7_defconfig ++++ b/arch/arm/configs/imx_v6_v7_defconfig +@@ -370,6 +370,7 @@ CONFIG_RTC_DRV_PCF8523=y + CONFIG_RTC_DRV_PCF8563=y + CONFIG_RTC_DRV_M41T80=y + CONFIG_RTC_DRV_DA9063=y ++CONFIG_RTC_DRV_MCA=y + CONFIG_RTC_DRV_MC13XXX=y + CONFIG_RTC_DRV_MXC=y + CONFIG_RTC_DRV_MXC_V2=y +diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig +index a819ef07b7ec..303c17e8a909 100644 +--- a/drivers/rtc/Kconfig ++++ b/drivers/rtc/Kconfig +@@ -1106,6 +1106,18 @@ config RTC_DRV_M48T59 + This driver can also be built as a module, if so, the module + will be called "rtc-m48t59". + ++config RTC_DRV_MCA ++ tristate "Digi ConnectCore SOMs Micro Controller Assist RTC" ++ select MFD_MCA_CC6UL if SOC_IMX6UL ++ select MFD_MCA_CC8X if ARCH_FSL_IMX8QXP ++ ++ help ++ If you say Y here you will get support for the RTC in the ++ Micro Controller Assist of Digi ConnectCore system-on-modules. ++ ++ This driver can also be built as a module, if so, the module ++ will be called "rtc-mca". ++ + config RTC_DRV_MSM6242 + tristate "Oki MSM6242" + depends on HAS_IOMEM +diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile +index 290c1730fb0a..cd1b13723479 100644 +--- a/drivers/rtc/Makefile ++++ b/drivers/rtc/Makefile +@@ -98,6 +98,7 @@ obj-$(CONFIG_RTC_DRV_MAX8925) += rtc-max8925.o + obj-$(CONFIG_RTC_DRV_MAX8997) += rtc-max8997.o + obj-$(CONFIG_RTC_DRV_MAX8998) += rtc-max8998.o + obj-$(CONFIG_RTC_DRV_MC13XXX) += rtc-mc13xxx.o ++obj-$(CONFIG_RTC_DRV_MCA) += rtc-mca.o + obj-$(CONFIG_RTC_DRV_MCP795) += rtc-mcp795.o + obj-$(CONFIG_RTC_DRV_MOXART) += rtc-moxart.o + obj-$(CONFIG_RTC_DRV_MPC5121) += rtc-mpc5121.o +diff --git a/drivers/rtc/rtc-mca.c b/drivers/rtc/rtc-mca.c +new file mode 100644 +index 000000000000..b0e3d17aa995 +--- /dev/null ++++ b/drivers/rtc/rtc-mca.c +@@ -0,0 +1,454 @@ ++/* rtc-mca.c - Real time clock device driver for MCA on ConnectCore modules ++ * Copyright (C) 2016 - 2018 Digi International ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Library General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Library General Public License for more details. ++ * ++ * You should have received a copy of the GNU Library General Public ++ * License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define MCA_DRVNAME_RTC "mca-rtc" ++ ++#define CLOCK_DATA_LEN (MCA_RTC_COUNT_SEC - MCA_RTC_COUNT_YEAR_L + 1) ++#define ALARM_DATA_LEN (MCA_RTC_ALARM_SEC - MCA_RTC_ALARM_YEAR_L + 1) ++ ++#ifdef CONFIG_OF ++enum mca_rtc_type { ++ CC6UL_MCA_RTC, ++ CC8X_MCA_RTC, ++}; ++ ++struct mca_rtc_data { ++ enum mca_rtc_type devtype; ++}; ++#endif ++ ++struct mca_rtc { ++ struct rtc_device *rtc_dev; ++ struct mca_drv *mca; ++ int irq_alarm; ++ bool alarm_enabled; ++}; ++ ++enum { ++ DATA_YEAR_L, ++ DATA_YEAR_H, ++ DATA_MONTH, ++ DATA_DAY, ++ DATA_HOUR, ++ DATA_MIN, ++ DATA_SEC, ++}; ++ ++static void mca_data_to_tm(u8 *data, struct rtc_time *tm) ++{ ++ /* conversion from MCA RTC to struct time is month-1 and year-1900 */ ++ tm->tm_year = (((data[DATA_YEAR_H] & ++ MCA_RTC_YEAR_H_MASK) << 8) | ++ (data[DATA_YEAR_L] & MCA_RTC_YEAR_L_MASK)) - ++ 1900; ++ tm->tm_mon = (data[DATA_MONTH] & MCA_RTC_MONTH_MASK) - 1; ++ tm->tm_mday = (data[DATA_DAY] & MCA_RTC_DAY_MASK); ++ tm->tm_hour = (data[DATA_HOUR] & MCA_RTC_HOUR_MASK); ++ tm->tm_min = (data[DATA_MIN] & MCA_RTC_MIN_MASK); ++ tm->tm_sec = (data[DATA_SEC] & MCA_RTC_SEC_MASK); ++} ++ ++static void mca_tm_to_data(struct rtc_time *tm, u8 *data) ++{ ++ /* conversion from struct time to MCA RTC is year+1900 */ ++ data[DATA_YEAR_L] &= (u8)~MCA_RTC_YEAR_L_MASK; ++ data[DATA_YEAR_H] &= (u8)~MCA_RTC_YEAR_H_MASK; ++ data[DATA_YEAR_L] |= (tm->tm_year + 1900) & ++ MCA_RTC_YEAR_L_MASK; ++ data[DATA_YEAR_H] |= ((tm->tm_year + 1900) >> 8) & ++ MCA_RTC_YEAR_H_MASK; ++ ++ /* conversion from struct time to MCA RTC is month+1 */ ++ data[DATA_MONTH] &= ~MCA_RTC_MONTH_MASK; ++ data[DATA_MONTH] |= (tm->tm_mon + 1) & MCA_RTC_MONTH_MASK; ++ ++ data[DATA_DAY] &= ~MCA_RTC_DAY_MASK; ++ data[DATA_DAY] |= tm->tm_mday & MCA_RTC_DAY_MASK; ++ ++ data[DATA_HOUR] &= ~MCA_RTC_HOUR_MASK; ++ data[DATA_HOUR] |= tm->tm_hour & MCA_RTC_HOUR_MASK; ++ ++ data[DATA_MIN] &= ~MCA_RTC_MIN_MASK; ++ data[DATA_MIN] |= tm->tm_min & MCA_RTC_MIN_MASK; ++ ++ data[DATA_SEC] &= ~MCA_RTC_SEC_MASK; ++ data[DATA_SEC] |= tm->tm_sec & MCA_RTC_SEC_MASK; ++} ++ ++static int mca_rtc_stop_alarm(struct device *dev) ++{ ++ struct mca_rtc *rtc = dev_get_drvdata(dev); ++ ++ return regmap_update_bits(rtc->mca->regmap, MCA_RTC_CONTROL, ++ MCA_RTC_ALARM_EN, 0); ++} ++ ++static int mca_rtc_start_alarm(struct device *dev) ++{ ++ struct mca_rtc *rtc = dev_get_drvdata(dev); ++ ++ return regmap_update_bits(rtc->mca->regmap, MCA_RTC_CONTROL, ++ MCA_RTC_ALARM_EN, ++ MCA_RTC_ALARM_EN); ++} ++ ++static int mca_rtc_read_time(struct device *dev, struct rtc_time *tm) ++{ ++ struct mca_rtc *rtc = dev_get_drvdata(dev); ++ u8 data[CLOCK_DATA_LEN] = { [0 ... (CLOCK_DATA_LEN - 1)] = 0 }; ++ int ret; ++ ++ ret = regmap_bulk_read(rtc->mca->regmap, MCA_RTC_COUNT_YEAR_L, ++ data, CLOCK_DATA_LEN); ++ if (ret < 0) { ++ dev_err(dev, "Failed to read RTC time data: %d\n", ret); ++ return ret; ++ } ++ ++ mca_data_to_tm(data, tm); ++ return rtc_valid_tm(tm); ++} ++ ++static int mca_rtc_set_time(struct device *dev, struct rtc_time *tm) ++{ ++ struct mca_rtc *rtc = dev_get_drvdata(dev); ++ u8 data[CLOCK_DATA_LEN] = { [0 ... (CLOCK_DATA_LEN - 1)] = 0 }; ++ int ret; ++ ++ mca_tm_to_data(tm, data); ++ ++ ret = regmap_bulk_write(rtc->mca->regmap, MCA_RTC_COUNT_YEAR_L, ++ data, CLOCK_DATA_LEN); ++ if (ret < 0) { ++ dev_err(dev, "Failed to set RTC time data: %d\n", ret); ++ return ret; ++ } ++ ++ return ret; ++} ++ ++/* ++ * The MCA RTC alarm expires (triggers the irq) when the RTC time matches the ++ * value programmed in the alarm register and the RTC counter increments. ++ * This means, one second after the programmed value. To correct this, the ++ * alarm value is adjusted when it is being written/read, decrementing/incremen- ++ * ting the value by 1 second. ++ */ ++static void mca_rtc_adjust_alarm_time(struct rtc_wkalrm *alrm, bool inc) ++{ ++ unsigned long time; ++ ++ rtc_tm_to_time(&alrm->time, &time); ++ time = inc ? time + 1 : time - 1; ++ rtc_time_to_tm(time, &alrm->time); ++} ++ ++static int mca_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) ++{ ++ struct mca_rtc *rtc = dev_get_drvdata(dev); ++ u8 data[CLOCK_DATA_LEN] = { [0 ... (CLOCK_DATA_LEN - 1)] = 0 }; ++ int ret; ++ unsigned int val; ++ ++ ret = regmap_bulk_read(rtc->mca->regmap, MCA_RTC_ALARM_YEAR_L, ++ data, ALARM_DATA_LEN); ++ if (ret < 0) ++ return ret; ++ ++ mca_data_to_tm(data, &alrm->time); ++ mca_rtc_adjust_alarm_time(alrm, true); ++ ++ /* Enable status */ ++ ret = regmap_read(rtc->mca->regmap, MCA_RTC_CONTROL, &val); ++ if (ret < 0) ++ return ret; ++ ++ /* Pending status */ ++ ret = regmap_read(rtc->mca->regmap, MCA_IRQ_STATUS_0, &val); ++ if (ret < 0) ++ return ret; ++ alrm->pending = (val & MCA_RTC_ALARM) ? 1 : 0; ++ ++ return 0; ++} ++ ++static int mca_rtc_alarm_irq_enable(struct device *dev, ++ unsigned int enabled) ++{ ++ struct mca_rtc *rtc = dev_get_drvdata(dev); ++ int ret; ++ ++ if (enabled) { ++ ret = mca_rtc_start_alarm(dev); ++ if (ret != 0) { ++ dev_err(dev, "Failed to enable alarm IRQ (%d)\n", ret); ++ goto exit_alarm_irq; ++ } ++ rtc->alarm_enabled = 1; ++ } else { ++ ret = mca_rtc_stop_alarm(dev); ++ if (ret != 0) { ++ dev_err(dev, "Failed to disable alarm IRQ (%d)\n", ret); ++ goto exit_alarm_irq; ++ } ++ rtc->alarm_enabled = 0; ++ } ++ ++exit_alarm_irq: ++ return ret; ++} ++ ++static int mca_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) ++{ ++ struct mca_rtc *rtc = dev_get_drvdata(dev); ++ u8 data[CLOCK_DATA_LEN] = { [0 ... (CLOCK_DATA_LEN - 1)] = 0 }; ++ int ret; ++ ++ mca_rtc_adjust_alarm_time(alrm, false); ++ mca_tm_to_data(&alrm->time, data); ++ ++ ret = regmap_bulk_write(rtc->mca->regmap, MCA_RTC_ALARM_YEAR_L, ++ data, ALARM_DATA_LEN); ++ if (ret < 0) ++ return ret; ++ ++ return mca_rtc_alarm_irq_enable(dev, alrm->enabled); ++} ++ ++static irqreturn_t mca_alarm_event(int irq, void *data) ++{ ++ struct mca_rtc *rtc = data; ++ ++ rtc_update_irq(rtc->rtc_dev, 1, RTC_IRQF | RTC_AF); ++ ++ return IRQ_HANDLED; ++} ++ ++static const struct rtc_class_ops mca_rtc_ops = { ++ .read_time = mca_rtc_read_time, ++ .set_time = mca_rtc_set_time, ++ .read_alarm = mca_rtc_read_alarm, ++ .set_alarm = mca_rtc_set_alarm, ++ .alarm_irq_enable = mca_rtc_alarm_irq_enable, ++}; ++ ++static int mca_rtc_probe(struct platform_device *pdev) ++{ ++ struct mca_drv *mca = dev_get_drvdata(pdev->dev.parent); ++ struct mca_rtc *rtc; ++ const struct mca_rtc_data *devdata = ++ of_device_get_match_data(&pdev->dev); ++ struct device_node *np = NULL; ++ int ret = 0; ++ ++ if (!mca || !mca->dev->parent->of_node) ++ return -EPROBE_DEFER; ++ ++ rtc = devm_kzalloc(&pdev->dev, sizeof *rtc, GFP_KERNEL); ++ if (!rtc) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, rtc); ++ device_init_wakeup(&pdev->dev, 1); ++ rtc->mca = mca; ++ ++ /* Find entry in device-tree */ ++ if (mca->dev->of_node) { ++ const char * compatible = pdev->dev.driver-> ++ of_match_table[devdata->devtype].compatible; ++ ++ /* ++ * Return silently if RTC node does not exist ++ * or if it is disabled ++ */ ++ np = of_find_compatible_node(mca->dev->of_node, NULL, compatible); ++ if (!np) { ++ ret = -ENODEV; ++ goto err; ++ } ++ if (!of_device_is_available(np)) { ++ ret = -ENODEV; ++ goto err; ++ } ++ } ++ ++ /* Enable RTC hardware */ ++ ret = regmap_update_bits(mca->regmap, MCA_RTC_CONTROL, ++ MCA_RTC_EN, MCA_RTC_EN); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "Failed to enable RTC.\n"); ++ goto err; ++ } ++ ++ /* Register RTC device */ ++ rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev); ++ ++ if (IS_ERR(rtc->rtc_dev)) { ++ dev_err(&pdev->dev, "Failed to allocate RTC device: %ld\n", ++ PTR_ERR(rtc->rtc_dev)); ++ ret = PTR_ERR(rtc->rtc_dev); ++ goto err; ++ } ++ ++ rtc->rtc_dev->ops = &mca_rtc_ops; ++ ret = rtc_register_device(rtc->rtc_dev); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "Failed to register RTC device.\n"); ++ goto err; ++ } ++ ++ ++ /* ++ * Register interrupts. Complain on errors but let device ++ * to be registered at least for date/time. ++ */ ++ rtc->irq_alarm = platform_get_irq_byname(pdev, ++ MCA_IRQ_RTC_ALARM_NAME); ++ ret = devm_request_threaded_irq(&pdev->dev, rtc->irq_alarm, NULL, ++ mca_alarm_event, ++ IRQF_TRIGGER_LOW | IRQF_ONESHOT, ++ MCA_IRQ_RTC_ALARM_NAME, rtc); ++ if (ret) { ++ dev_err(&pdev->dev, "Failed to request %s IRQ. (%d)\n", ++ MCA_IRQ_RTC_ALARM_NAME, rtc->irq_alarm); ++ rtc->irq_alarm = -ENXIO; ++ } ++ ++ return 0; ++ ++err: ++ rtc = NULL; ++ return ret; ++} ++ ++static int mca_rtc_remove(struct platform_device *pdev) ++{ ++ struct mca_rtc *rtc = platform_get_drvdata(pdev); ++ ++ if (rtc->irq_alarm >= 0) ++ devm_free_irq(&pdev->dev, rtc->irq_alarm, rtc); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int mca_rtc_suspend(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct mca_rtc *rtc = platform_get_drvdata(pdev); ++ int ret; ++ ++ if (!device_may_wakeup(&pdev->dev) && rtc->alarm_enabled) { ++ /* Disable the alarm irq to avoid unwanted wakeups */ ++ ret = mca_rtc_stop_alarm(&pdev->dev); ++ if (ret < 0) ++ dev_err(&pdev->dev, "Failed to disable RTC Alarm\n"); ++ } ++ ++ return 0; ++} ++ ++static int mca_rtc_resume(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct mca_rtc *rtc = platform_get_drvdata(pdev); ++ int ret; ++ ++ if (!device_may_wakeup(&pdev->dev) && rtc->alarm_enabled) { ++ /* Enable the alarm irq, just in case it was disabled suspending */ ++ ret = mca_rtc_start_alarm(&pdev->dev); ++ if (ret < 0) ++ dev_err(&pdev->dev, "Failed to restart RTC Alarm\n"); ++ } ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops mca_rtc_pm_ops = { ++ .suspend = mca_rtc_suspend, ++ .resume = mca_rtc_resume, ++ .poweroff = mca_rtc_suspend, ++}; ++#endif ++ ++#ifdef CONFIG_OF ++static struct mca_rtc_data mca_rtc_devdata[] = { ++ [CC6UL_MCA_RTC] = { ++ .devtype = CC6UL_MCA_RTC, ++ }, ++ [CC8X_MCA_RTC] = { ++ .devtype = CC8X_MCA_RTC, ++ }, ++}; ++ ++static const struct of_device_id mca_rtc_dt_ids[] = { ++ { .compatible = "digi,mca-cc6ul-rtc", ++ .data = &mca_rtc_devdata[CC6UL_MCA_RTC]}, ++ { .compatible = "digi,mca-cc8x-rtc", ++ .data = &mca_rtc_devdata[CC8X_MCA_RTC]}, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, mca_rtc_dt_ids); ++#endif ++ ++static struct platform_driver mca_rtc_driver = { ++ .probe = mca_rtc_probe, ++ .remove = mca_rtc_remove, ++ .driver = { ++ .name = MCA_DRVNAME_RTC, ++ .owner = THIS_MODULE, ++#ifdef CONFIG_PM ++ .pm = &mca_rtc_pm_ops, ++#endif ++#ifdef CONFIG_OF ++ .of_match_table = mca_rtc_dt_ids, ++#endif ++ }, ++}; ++ ++static int __init mca_rtc_init(void) ++{ ++ return platform_driver_register(&mca_rtc_driver); ++} ++module_init(mca_rtc_init); ++ ++static void __exit mca_rtc_exit(void) ++{ ++ platform_driver_unregister(&mca_rtc_driver); ++} ++module_exit(mca_rtc_exit); ++ ++/* Module information */ ++MODULE_AUTHOR("Digi International Inc."); ++MODULE_DESCRIPTION("Real time clock device driver for MCA of ConnectCore Modules"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:" MCA_DRVNAME_RTC); diff --git a/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ul/0011-imx6ul-Add-MCA-power-key-support-for-ConnectCore-6UL.patch b/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ul/0011-imx6ul-Add-MCA-power-key-support-for-ConnectCore-6UL.patch new file mode 100644 index 000000000..fcf1949c8 --- /dev/null +++ b/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ul/0011-imx6ul-Add-MCA-power-key-support-for-ConnectCore-6UL.patch @@ -0,0 +1,490 @@ +From 052464d671eb993264a78b3e35170c1d9fd44268 Mon Sep 17 00:00:00 2001 +From: Alex Gonzalez +Date: Mon, 23 Apr 2018 11:45:16 +0200 +Subject: [PATCH] imx6ul: Add MCA power key support for ConnectCore 6UL SOM + +Synched with v4.14.78/master at: +3f8b03950b323db4ca89b1cdc1c2288f79facaa3 + +Signed-off-by: Alex Gonzalez +--- + arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi | 9 + + arch/arm/configs/imx_v6_v7_defconfig | 1 + + drivers/input/misc/Kconfig | 12 + + drivers/input/misc/Makefile | 1 + + drivers/input/misc/pwrkey-mca.c | 401 ++++++++++++++++++++++++++++++ + 5 files changed, 424 insertions(+) + create mode 100644 drivers/input/misc/pwrkey-mca.c + +diff --git a/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi b/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi +index 8163533c83b0..f2a17f9b88d1 100644 +--- a/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi ++++ b/arch/arm/boot/dts/imx6ul-ccimx6ulsom.dtsi +@@ -78,6 +78,15 @@ + digi,full-reset; + }; + ++ pwrkey { ++ compatible = "digi,mca-cc6ul-pwrkey"; ++ digi,key-power; ++ digi,key-sleep; ++ digi,debounce-ms = <100>; ++ digi,pwroff-delay-sec = <6>; ++ digi,pwroff-guard-sec = <30>; ++ }; ++ + mca_adc: adc { + compatible = "digi,mca-cc6ul-adc"; + digi,adc-vref = <3000000>; +diff --git a/arch/arm/configs/imx_v6_v7_defconfig b/arch/arm/configs/imx_v6_v7_defconfig +index 8a763ab6a365..31d02719c51e 100644 +--- a/arch/arm/configs/imx_v6_v7_defconfig ++++ b/arch/arm/configs/imx_v6_v7_defconfig +@@ -186,6 +186,7 @@ CONFIG_TOUCHSCREEN_SX8654=y + CONFIG_TOUCHSCREEN_COLIBRI_VF50=y + CONFIG_INPUT_MISC=y + CONFIG_INPUT_MMA8450=y ++CONFIG_INPUT_MCA_PWRKEY=y + CONFIG_SERIO_SERPORT=m + # CONFIG_LEGACY_PTYS is not set + CONFIG_SERIAL_IMX=y +diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig +index ca59a2be9bc5..cef9795466f7 100644 +--- a/drivers/input/misc/Kconfig ++++ b/drivers/input/misc/Kconfig +@@ -851,4 +851,16 @@ config INPUT_SC27XX_VIBRA + To compile this driver as a module, choose M here. The module will + be called sc27xx_vibra. + ++config INPUT_MCA_PWRKEY ++ tristate "Digi ConnectCore SOMs Micro Controller Assist Power key" ++ select MFD_MCA_CC6UL if SOC_IMX6UL ++ select MFD_MCA_CC8X if ARCH_FSL_IMX8QXP ++ help ++ If you say Y here you will get support for the Power Key in the ++ Micro Controller Assist of Digi ConnectCore system-on-modules, ++ as an input device, reporting the power input/button status. ++ ++ This driver can also be built as a module, if so, the module ++ will be called "pwrkey-mca". ++ + endif +diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile +index 9d0f9d1ff68f..1ed17d1b0c68 100644 +--- a/drivers/input/misc/Makefile ++++ b/drivers/input/misc/Makefile +@@ -47,6 +47,7 @@ obj-$(CONFIG_INPUT_MAX77693_HAPTIC) += max77693-haptic.o + obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o + obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o + obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o ++obj-$(CONFIG_INPUT_MCA_PWRKEY) += pwrkey-mca.o + obj-$(CONFIG_INPUT_MMA8450) += mma8450.o + obj-$(CONFIG_INPUT_PALMAS_PWRBUTTON) += palmas-pwrbutton.o + obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o +diff --git a/drivers/input/misc/pwrkey-mca.c b/drivers/input/misc/pwrkey-mca.c +new file mode 100644 +index 000000000000..ddb83ef77045 +--- /dev/null ++++ b/drivers/input/misc/pwrkey-mca.c +@@ -0,0 +1,401 @@ ++/* pwrkey-mca.c - Power Key device driver for MCA on ConnectCore modules ++ * Copyright (C) 2016 - 2018 Digi International Inc ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Library General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Library General Public License for more details. ++ * ++ * You should have received a copy of the GNU Library General Public ++ * License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define MCA_DRVNAME_PWRKEY "mca-pwrkey" ++ ++#define DEFAULT_PWR_KEY_DEBOUNCE 150 /* 150 ms */ ++#define DEFAULT_PWR_KEY_DELAY 4 /* 4 seconds */ ++#define DEFAULT_PWR_KEY_GUARD 25 /* 25 seconds */ ++#define MAX_PWR_KEY_DEBOUNCE 255 ++#define MAX_PWR_KEY_DELAY 255 ++#define MAX_PWR_KEY_GUARD 255 ++ ++#ifdef CONFIG_OF ++enum mca_pwrkey_type { ++ CC6UL_MCA_PWRKEY, ++ CC8X_MCA_PWRKEY, ++}; ++ ++struct mca_pwrkey_data { ++ enum mca_pwrkey_type devtype; ++ char drv_name_phys[40]; ++}; ++#endif ++ ++struct mca_pwrkey { ++ struct mca_drv *mca; ++ struct input_dev *input; ++ int irq_power; ++ int irq_sleep; ++ bool key_power; ++ bool key_sleep; ++ bool suspended; ++ uint32_t debounce_ms; ++ uint32_t pwroff_delay_sec; ++ uint32_t pwroff_guard_sec; ++}; ++ ++#ifdef CONFIG_PM_SLEEP ++static DEFINE_SPINLOCK(lock); ++#endif ++ ++static irqreturn_t mca_pwrkey_power_off_irq_handler(int irq, void *data) ++{ ++ struct mca_pwrkey *pwrkey = data; ++ ++ dev_notice(&pwrkey->input->dev, "Power Button - KEY_POWER\n"); ++ ++ input_report_key(pwrkey->input, KEY_POWER, 1); ++ input_sync(pwrkey->input); ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t mca_pwrkey_sleep_irq_handler(int irq, void *data) ++{ ++ struct mca_pwrkey *pwrkey = data; ++ ++ /* Report the event only if not coming from suspend */ ++ if (!pwrkey->suspended) { ++ dev_notice(&pwrkey->input->dev, "Power button - KEY_SLEEP\n"); ++ ++ input_report_key(pwrkey->input, KEY_SLEEP, 1); ++ input_report_key(pwrkey->input, KEY_SLEEP, 0); ++ input_sync(pwrkey->input); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static int mca_pwrkey_initialize(struct mca_pwrkey *pwrkey) ++{ ++ int ret; ++ uint8_t pwrctrl0 = 0; ++ ++ ret = regmap_write(pwrkey->mca->regmap, MCA_PWR_KEY_DEBOUNCE, ++ (uint8_t)pwrkey->debounce_ms); ++ if (ret < 0) { ++ dev_err(pwrkey->mca->dev, ++ "Failed to set debounce time 0x%02x, %d\n", ++ (uint8_t)pwrkey->debounce_ms, ret); ++ return ret; ++ } ++ ++ ret = regmap_write(pwrkey->mca->regmap, MCA_PWR_KEY_DELAY, ++ (uint8_t)pwrkey->pwroff_delay_sec); ++ if (ret < 0) { ++ dev_err(pwrkey->mca->dev, ++ "Failed to set delay time 0x%02x, %d\n", ++ (uint8_t)pwrkey->pwroff_delay_sec, ret); ++ return ret; ++ } ++ ++ ret = regmap_write(pwrkey->mca->regmap, MCA_PWR_KEY_GUARD, ++ (uint8_t)pwrkey->pwroff_guard_sec); ++ if (ret < 0) { ++ dev_err(pwrkey->mca->dev, ++ "Failed to set guard time 0x%02x, %d\n", ++ (uint8_t)pwrkey->pwroff_guard_sec, ret); ++ return ret; ++ } ++ ++ if (pwrkey->key_power) ++ pwrctrl0 |= MCA_PWR_KEY_OFF_EN; ++ ++ if (pwrkey->key_sleep) ++ pwrctrl0 |= MCA_PWR_KEY_SLEEP_EN; ++ ++ if (pwrkey->pwroff_guard_sec != 0) ++ pwrctrl0 |= MCA_PWR_GUARD_EN; ++ ++ ret = regmap_write(pwrkey->mca->regmap, MCA_PWR_CTRL_0, pwrctrl0); ++ if (ret < 0) { ++ dev_err(pwrkey->mca->dev, ++ "Failed to set PWR_CTRL_0 0x%02x, %d\n", ++ pwrctrl0, ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int of_mca_pwrkey_read_settings(struct device_node *np, ++ struct mca_pwrkey *pwrkey) ++{ ++ uint32_t val; ++ ++ /* Get driver configuration data from device tree */ ++ pwrkey->debounce_ms = DEFAULT_PWR_KEY_DEBOUNCE; ++ pwrkey->pwroff_delay_sec = DEFAULT_PWR_KEY_DEBOUNCE; ++ pwrkey->pwroff_guard_sec = DEFAULT_PWR_KEY_GUARD; ++ ++ pwrkey->key_power = of_property_read_bool(np, "digi,key-power"); ++ pwrkey->key_sleep = of_property_read_bool(np, "digi,key-sleep"); ++ ++ if (!of_property_read_u32(np, "digi,debounce-ms", &val)) { ++ if (val <= MAX_PWR_KEY_DEBOUNCE) ++ pwrkey->debounce_ms = val; ++ else ++ dev_warn(pwrkey->mca->dev, ++ "Invalid debounce-ms value. Using default.\n"); ++ } ++ ++ if (!of_property_read_u32(np, "digi,pwroff-delay-sec", &val)) { ++ if (val <= MAX_PWR_KEY_DELAY) ++ pwrkey->pwroff_delay_sec = val; ++ else ++ dev_warn(pwrkey->mca->dev, ++ "Invalid pwroff-delay-sec value. Using default.\n"); ++ } ++ ++ if (!of_property_read_u32(np, "digi,pwroff-guard-sec", &val)) { ++ if (val <= MAX_PWR_KEY_GUARD) ++ pwrkey->pwroff_guard_sec = val; ++ else ++ dev_warn(pwrkey->mca->dev, ++ "Invalid pwroff-guard-sec value. Using default.\n"); ++ } ++ ++ return 0; ++} ++ ++static int mca_pwrkey_probe(struct platform_device *pdev) ++{ ++ struct mca_drv *mca = dev_get_drvdata(pdev->dev.parent); ++ struct mca_pwrkey *pwrkey; ++ const struct mca_pwrkey_data *devdata = ++ of_device_get_match_data(&pdev->dev); ++ struct device_node *np = NULL; ++ int ret = 0; ++ ++ if (!mca || !mca->dev || !mca->dev->parent || ++ !mca->dev->parent->of_node) ++ return -EPROBE_DEFER; ++ ++ /* Find entry in device-tree */ ++ if (mca->dev->of_node) { ++ const char * compatible = pdev->dev.driver-> ++ of_match_table[devdata->devtype].compatible; ++ ++ /* Return if pwrkey node does not exist or if it is disabled */ ++ np = of_find_compatible_node(mca->dev->of_node, NULL, compatible); ++ if (!np || !of_device_is_available(np)) ++ return -ENODEV; ++ } ++ ++ pwrkey = devm_kzalloc(&pdev->dev, sizeof(struct mca_pwrkey), ++ GFP_KERNEL); ++ if (!pwrkey) { ++ dev_err(&pdev->dev, "Failed to allocate memory.\n"); ++ return -ENOMEM; ++ } ++ ++ pwrkey->input = input_allocate_device(); ++ if (!pwrkey->input) { ++ dev_err(&pdev->dev, "Failed to allocated input device.\n"); ++ ret = -ENOMEM; ++ goto err_free; ++ } ++ ++ platform_set_drvdata(pdev, pwrkey); ++ pwrkey->mca = mca; ++ pwrkey->irq_power = platform_get_irq_byname(pdev, ++ MCA_IRQ_PWR_OFF_NAME); ++ pwrkey->irq_sleep = platform_get_irq_byname(pdev, ++ MCA_IRQ_PWR_SLEEP_NAME); ++ pwrkey->input->name = dev_name(&pdev->dev); ++ pwrkey->input->phys = devdata->drv_name_phys; ++ pwrkey->input->dev.parent = &pdev->dev; ++ ++ input_set_capability(pwrkey->input, EV_KEY, KEY_POWER); ++ input_set_capability(pwrkey->input, EV_KEY, KEY_SLEEP); ++ ++ /* Initialize driver settings from device tree */ ++ ret = of_mca_pwrkey_read_settings(np, pwrkey); ++ if (ret) { ++ dev_err(&pdev->dev, "Failed to get %s dtb settings\n", ++ dev_name(&pdev->dev)); ++ goto err_free_inputdev; ++ } ++ ++ ret = mca_pwrkey_initialize(pwrkey); ++ if (ret) { ++ dev_err(&pdev->dev, "Failed to initilize pwrkey registers\n"); ++ goto err_free_inputdev; ++ } ++ ++ if (pwrkey->key_power) { ++ ret = devm_request_threaded_irq(&pdev->dev, pwrkey->irq_power, NULL, ++ mca_pwrkey_power_off_irq_handler, ++ IRQF_TRIGGER_LOW | IRQF_ONESHOT, ++ MCA_IRQ_PWR_OFF_NAME, pwrkey); ++ if (ret) { ++ dev_err(&pdev->dev, "Failed to request %s IRQ (%d).\n", ++ MCA_IRQ_PWR_OFF_NAME, pwrkey->irq_power); ++ goto err_free_inputdev; ++ } ++ } ++ ++ if (pwrkey->key_sleep) { ++ ret = devm_request_threaded_irq(&pdev->dev, pwrkey->irq_sleep, NULL, ++ mca_pwrkey_sleep_irq_handler, ++ IRQF_TRIGGER_LOW | IRQF_ONESHOT, ++ MCA_IRQ_PWR_SLEEP_NAME, pwrkey); ++ if (ret) { ++ dev_err(&pdev->dev, "Failed to request %s IRQ (%d).\n", ++ MCA_IRQ_PWR_SLEEP_NAME, pwrkey->irq_sleep); ++ goto err_irq1; ++ } ++ enable_irq_wake(pwrkey->irq_sleep); ++ } ++ ++ ret = input_register_device(pwrkey->input); ++ if (ret) { ++ dev_err(&pdev->dev, "Failed to register input device (%d).\n", ++ ret); ++ goto err_irq2; ++ } ++ ++ return 0; ++ ++err_irq2: ++ if (pwrkey->key_sleep) ++ free_irq(pwrkey->mca->irq_base + pwrkey->irq_sleep, pwrkey); ++err_irq1: ++ if (pwrkey->key_power) ++ free_irq(pwrkey->mca->irq_base + pwrkey->irq_power, pwrkey); ++err_free_inputdev: ++ input_free_device(pwrkey->input); ++err_free: ++ kfree(pwrkey); ++ ++ return ret; ++} ++ ++static int mca_pwrkey_remove(struct platform_device *pdev) ++{ ++ struct mca_pwrkey *pwrkey = platform_get_drvdata(pdev); ++ ++ if (pwrkey->key_power) ++ free_irq(pwrkey->irq_power, pwrkey); ++ if (pwrkey->key_sleep) ++ free_irq(pwrkey->irq_sleep, pwrkey); ++ input_unregister_device(pwrkey->input); ++ kfree(pwrkey); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int __maybe_unused mca_pwrkey_resume(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct mca_pwrkey *pwrkey = platform_get_drvdata(pdev); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&lock, flags); ++ pwrkey->suspended = false; ++ spin_unlock_irqrestore(&lock, flags); ++ ++ return 0; ++} ++ ++static int __maybe_unused mca_pwrkey_suspend(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct mca_pwrkey *pwrkey = platform_get_drvdata(pdev); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&lock, flags); ++ pwrkey->suspended = true; ++ spin_unlock_irqrestore(&lock, flags); ++ ++ return 0; ++} ++ ++SIMPLE_DEV_PM_OPS(mca_pwrkey_pm_ops, mca_pwrkey_suspend, mca_pwrkey_resume); ++#endif /* CONFIG_PM_SLEEP */ ++ ++#ifdef CONFIG_OF ++static struct mca_pwrkey_data mca_pwrkey_devdata[] = { ++ [CC6UL_MCA_PWRKEY] = { ++ .devtype = CC6UL_MCA_PWRKEY, ++ .drv_name_phys= "mca-cc6ul-pwrkey/input0" ++ }, ++ [CC8X_MCA_PWRKEY] = { ++ .devtype = CC8X_MCA_PWRKEY, ++ .drv_name_phys= "mca-cc8x-pwrkey/input0" ++ }, ++}; ++ ++static const struct of_device_id mca_pwrkey_ids[] = { ++ { .compatible = "digi,mca-cc6ul-pwrkey", ++ .data = &mca_pwrkey_devdata[CC6UL_MCA_PWRKEY]}, ++ { .compatible = "digi,mca-cc8x-pwrkey", ++ .data = &mca_pwrkey_devdata[CC8X_MCA_PWRKEY]}, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, mca_pwrkey_ids); ++#endif ++ ++static struct platform_driver mca_pwrkey_driver = { ++ .probe = mca_pwrkey_probe, ++ .remove = mca_pwrkey_remove, ++ .driver = { ++ .name = MCA_DRVNAME_PWRKEY, ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(mca_pwrkey_ids), ++#ifdef CONFIG_PM_SLEEP ++ .pm = &mca_pwrkey_pm_ops, ++#endif ++ }, ++}; ++ ++static int __init mca_pwrkey_init(void) ++{ ++ return platform_driver_register(&mca_pwrkey_driver); ++} ++module_init(mca_pwrkey_init); ++ ++static void __exit mca_pwrkey_exit(void) ++{ ++ platform_driver_unregister(&mca_pwrkey_driver); ++} ++module_exit(mca_pwrkey_exit); ++ ++MODULE_AUTHOR("Digi International Inc"); ++MODULE_DESCRIPTION("pwrkey device driver for MCA of ConnectCore Modules"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:" MCA_DRVNAME_PWRKEY); diff --git a/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ulsbc/0001-ccimx6ulsbcpro-Add-IOEXP-core-I2C-support.patch b/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ulsbc/0001-ccimx6ulsbcpro-Add-IOEXP-core-I2C-support.patch new file mode 100644 index 000000000..71664b50d --- /dev/null +++ b/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ulsbc/0001-ccimx6ulsbcpro-Add-IOEXP-core-I2C-support.patch @@ -0,0 +1,892 @@ +From f56dbb1e8b6a501e84d69ac5f18ec7a66cb4faa7 Mon Sep 17 00:00:00 2001 +From: Alex Gonzalez +Date: Mon, 23 Apr 2018 11:46:52 +0200 +Subject: [PATCH] ccimx6ulsbcpro: Add IOEXP core I2C support + +Synched with v4.14.78/master at: +3f8b03950b323db4ca89b1cdc1c2288f79facaa3 + +Signed-off-by: Alex Gonzalez +--- + arch/arm/configs/imx_v6_v7_defconfig | 1 + + drivers/mfd/Kconfig | 10 + + drivers/mfd/Makefile | 4 +- + drivers/mfd/mca-ioexp-core.c | 415 ++++++++++++++++++++++++++++++++ + drivers/mfd/mca-ioexp-i2c.c | 186 ++++++++++++++ + drivers/mfd/mca-ioexp-irq.c | 89 +++++++ + include/linux/mfd/mca-ioexp/core.h | 87 +++++++ + include/linux/mfd/mca-ioexp/registers.h | 15 ++ + 8 files changed, 805 insertions(+), 2 deletions(-) + create mode 100644 drivers/mfd/mca-ioexp-core.c + create mode 100644 drivers/mfd/mca-ioexp-i2c.c + create mode 100644 drivers/mfd/mca-ioexp-irq.c + create mode 100644 include/linux/mfd/mca-ioexp/core.h + create mode 100644 include/linux/mfd/mca-ioexp/registers.h + +diff --git a/arch/arm/configs/imx_v6_v7_defconfig b/arch/arm/configs/imx_v6_v7_defconfig +index ed31623082ac..a269983e3cba 100644 +--- a/arch/arm/configs/imx_v6_v7_defconfig ++++ b/arch/arm/configs/imx_v6_v7_defconfig +@@ -242,6 +242,7 @@ CONFIG_MFD_DA9063=y + CONFIG_MFD_MC13XXX_SPI=y + CONFIG_MFD_MC13XXX_I2C=y + CONFIG_MFD_RN5T618=y ++CONFIG_MFD_MCA_IOEXP=y + CONFIG_MFD_STMPE=y + CONFIG_REGULATOR=y + CONFIG_REGULATOR_FIXED_VOLTAGE=y +diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig +index 42c72334d645..5b87d592aece 100644 +--- a/drivers/mfd/Kconfig ++++ b/drivers/mfd/Kconfig +@@ -458,6 +458,16 @@ config MFD_MCA_CC8X + Additional drivers must be enabled in order to use the functionality + of the device (RTC, watchdog, ...). + ++config MFD_MCA_IOEXP ++ bool "Digi IO Expander" ++ select MFD_CORE ++ select REGMAP_I2C ++ select REGMAP_IRQ ++ depends on I2C=y ++ help ++ Select this option to enable support for the Digi IO Expander. ++ This includes the GPIO and ADC drivers. ++ + config MFD_MX25_TSADC + tristate "Freescale i.MX25 integrated Touchscreen and ADC unit" + select REGMAP_MMIO +diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile +index 28443313ba92..bfbaaba0d74f 100644 +--- a/drivers/mfd/Makefile ++++ b/drivers/mfd/Makefile +@@ -249,5 +249,5 @@ obj-$(CONFIG_MFD_ROHM_BD718XX) += rohm-bd718x7.o + + mca-cc6ul-objs := mca-cc6ul-core.o mca-cc6ul-irq.o mca-cc6ul-i2c.o + obj-$(CONFIG_MFD_MCA_CC6UL) += mca-cc6ul.o +-mca-cc8x-objs := mca-cc8x-core.o mca-cc8x-irq.o mca-cc8x-i2c.o +-obj-$(CONFIG_MFD_MCA_CC8X) += mca-cc8x.o ++mca-ioexp-objs := mca-ioexp-core.o mca-ioexp-irq.o mca-ioexp-i2c.o ++obj-$(CONFIG_MFD_MCA_IOEXP) += mca-ioexp.o +diff --git a/drivers/mfd/mca-ioexp-core.c b/drivers/mfd/mca-ioexp-core.c +new file mode 100644 +index 000000000000..19b3c7f2b4be +--- /dev/null ++++ b/drivers/mfd/mca-ioexp-core.c +@@ -0,0 +1,415 @@ ++/* ++ * Copyright 2017 - 2019 Digi International Inc ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++ ++static struct resource mca_ioexp_gpios_resources[] = { ++ { ++ .name = MCA_IRQ_GPIO_BANK_0_NAME, ++ .start = MCA_IOEXP_IRQ_GPIO_BANK_0, ++ .end = MCA_IOEXP_IRQ_GPIO_BANK_0, ++ .flags = IORESOURCE_IRQ, ++ }, ++ { ++ .name = MCA_IRQ_GPIO_BANK_1_NAME, ++ .start = MCA_IOEXP_IRQ_GPIO_BANK_1, ++ .end = MCA_IOEXP_IRQ_GPIO_BANK_1, ++ .flags = IORESOURCE_IRQ, ++ }, ++ { ++ .name = MCA_IRQ_GPIO_BANK_2_NAME, ++ .start = MCA_IOEXP_IRQ_GPIO_BANK_2, ++ .end = MCA_IOEXP_IRQ_GPIO_BANK_2, ++ .flags = IORESOURCE_IRQ, ++ }, ++ { ++ .name = MCA_IRQ_GPIO_BANK_3_NAME, ++ .start = MCA_IOEXP_IRQ_GPIO_BANK_3, ++ .end = MCA_IOEXP_IRQ_GPIO_BANK_3, ++ .flags = IORESOURCE_IRQ, ++ }, ++ { ++ .name = MCA_IRQ_GPIO_BANK_4_NAME, ++ .start = MCA_IOEXP_IRQ_GPIO_BANK_4, ++ .end = MCA_IOEXP_IRQ_GPIO_BANK_4, ++ .flags = IORESOURCE_IRQ, ++ }, ++ { ++ .name = MCA_IRQ_GPIO_BANK_5_NAME, ++ .start = MCA_IOEXP_IRQ_GPIO_BANK_5, ++ .end = MCA_IOEXP_IRQ_GPIO_BANK_5, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static const struct mfd_cell mca_ioexp_devs[] = { ++ { ++ .name = MCA_IOEXP_DRVNAME_GPIO, ++ .num_resources = ARRAY_SIZE(mca_ioexp_gpios_resources), ++ .resources = mca_ioexp_gpios_resources, ++ .of_compatible = "digi,mca-ioexp-gpio", ++ }, ++ { ++ .name = MCA_IOEXP_DRVNAME_ADC, ++ .of_compatible = "digi,mca-ioexp-adc", ++ }, ++}; ++ ++static ssize_t hwver_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct mca_ioexp *ioexp = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%d\n", ioexp->hw_version); ++} ++static DEVICE_ATTR(hw_version, S_IRUGO, hwver_show, NULL); ++ ++static ssize_t fwver_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct mca_ioexp *ioexp = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%d.%02d %s\n", MCA_FW_VER_MAJOR(ioexp->fw_version), ++ MCA_FW_VER_MINOR(ioexp->fw_version), ++ ioexp->fw_is_alpha ? "(alpha)" : ""); ++} ++static DEVICE_ATTR(fw_version, S_IRUGO, fwver_show, NULL); ++ ++static struct attribute *mca_ioexp_sysfs_entries[] = { ++ &dev_attr_hw_version.attr, ++ &dev_attr_fw_version.attr, ++ NULL, ++}; ++ ++static struct attribute_group mca_ioexp_attr_group = { ++ .name = NULL, /* put in device directory */ ++ .attrs = mca_ioexp_sysfs_entries, ++}; ++ ++static int read_reg_group(struct mca_ioexp *ioexp, ++ unsigned int start_addr, ++ unsigned int count, ++ uint8_t *dest) ++{ ++ unsigned int i; ++ unsigned int error; ++ ++ if (count == 0 || !dest) ++ return -1; ++ ++ for (i = 0; i < count; i++) { ++ const unsigned int reg_addr = start_addr + i; ++ unsigned int value; ++ ++ error = regmap_read(ioexp->regmap, reg_addr, &value); ++ if (error) { ++ dev_err(ioexp->dev, ++ "Error reading register %02X (%d)\n", ++ reg_addr, error); ++ return -1; ++ } ++ dest[i] = value; ++ } ++ ++ return 0; ++} ++ ++int mca_ioexp_suspend(struct device *dev) ++{ ++ struct mca_ioexp *ioexp = dev_get_drvdata(dev); ++ int error; ++ ++ if (!ioexp->preserved_regs) ++ return 0; ++ ++ ++ error = read_reg_group(ioexp, MCA_GPIO_DIR_0, ++ ioexp->preserved_regs->gpio_dir.cnt, ++ ioexp->preserved_regs->gpio_dir.values); ++ if (error) { ++ dev_err(ioexp->dev, ++ "Failed to preserve MCA_GPIO_DIR registers.\n"); ++ goto exit; ++ } ++ ++ error = read_reg_group(ioexp, MCA_GPIO_DATA_0, ++ ioexp->preserved_regs->gpio_data.cnt, ++ ioexp->preserved_regs->gpio_data.values); ++ if (error) { ++ dev_err(ioexp->dev, ++ "Failed to preserve MCA_GPIO_DATA registers.\n"); ++ goto exit; ++ } ++ ++ error = read_reg_group(ioexp, MCA_GPIO_IRQ_CFG_0, ++ ioexp->preserved_regs->irq_cfg.cnt, ++ ioexp->preserved_regs->irq_cfg.values); ++ if (error) { ++ dev_err(ioexp->dev, ++ "Failed to preserve MCA_GPIO_IRQ_CFG registers.\n"); ++ goto exit; ++ } ++ ++ error = read_reg_group(ioexp, MCA_IRQ_MASK_0, ++ ioexp->preserved_regs->irq_mask.cnt, ++ ioexp->preserved_regs->irq_mask.values); ++ if (error) { ++ dev_err(ioexp->dev, ++ "Failed to preserve MCA_IRQ_MASK registers.\n"); ++ goto exit; ++ } ++ ++ error = read_reg_group(ioexp, MCA_REG_ADC_CFG0_0, ++ ioexp->preserved_regs->adc_cfg.cnt, ++ ioexp->preserved_regs->adc_cfg.values); ++ if (error) { ++ dev_err(ioexp->dev, ++ "Failed to preserve MCA_REG_ADC_CFG registers.\n"); ++ goto exit; ++ } ++ ++ disable_irq(ioexp->chip_irq); ++ return 0; ++ ++exit: ++ if (error) ++ dev_err(ioexp->dev, "Configuration will be lost on resume. The IOs in use might need to be reconfigured in order to work properly.\n"); ++ ++ disable_irq(ioexp->chip_irq); ++ ++ /* Do not return errors or the device will not go to sleep. */ ++ return 0; ++} ++ ++static int write_reg_group(struct mca_ioexp *ioexp, ++ unsigned int start_addr, ++ unsigned int count, ++ uint8_t *values) ++{ ++ unsigned int i; ++ unsigned int error; ++ ++ if (count == 0 || !values) ++ return -1; ++ ++ for (i = 0; i < count; i++) { ++ const unsigned int reg_addr = start_addr + i; ++ const unsigned int value = values[i]; ++ ++ error = regmap_write(ioexp->regmap, reg_addr, value); ++ if (error) { ++ dev_err(ioexp->dev, ++ "Error writing register %02X (%d)\n", ++ reg_addr, error); ++ } ++ } ++ ++ return error; ++} ++ ++int mca_ioexp_resume(struct device *dev) ++{ ++ struct mca_ioexp *ioexp = dev_get_drvdata(dev); ++ int error; ++ ++ if (!ioexp->preserved_regs) ++ return 0; ++ ++ error = write_reg_group(ioexp, MCA_GPIO_DIR_0, ++ ioexp->preserved_regs->gpio_dir.cnt, ++ ioexp->preserved_regs->gpio_dir.values); ++ if (error) ++ dev_err(ioexp->dev, ++ "Failed to restore MCA_GPIO_DIR registers.\n"); ++ ++ error = write_reg_group(ioexp, MCA_GPIO_DATA_0, ++ ioexp->preserved_regs->gpio_data.cnt, ++ ioexp->preserved_regs->gpio_data.values); ++ if (error) ++ dev_err(ioexp->dev, ++ "Failed to restore MCA_GPIO_DATA registers.\n"); ++ ++ error = write_reg_group(ioexp, MCA_GPIO_IRQ_CFG_0, ++ ioexp->preserved_regs->irq_cfg.cnt, ++ ioexp->preserved_regs->irq_cfg.values); ++ if (error) ++ dev_err(ioexp->dev, ++ "Failed to restore MCA_GPIO_IRQ_CFG registers.\n"); ++ ++ error = write_reg_group(ioexp, MCA_IRQ_MASK_0, ++ ioexp->preserved_regs->irq_mask.cnt, ++ ioexp->preserved_regs->irq_mask.values); ++ if (error) ++ dev_err(ioexp->dev, ++ "Failed to restore MCA_IRQ_MASK registers.\n"); ++ ++ error = write_reg_group(ioexp, MCA_REG_ADC_CFG0_0, ++ ioexp->preserved_regs->adc_cfg.cnt, ++ ioexp->preserved_regs->adc_cfg.values); ++ if (error) ++ dev_err(ioexp->dev, ++ "Failed to restore MCA_REG_ADC_CFG registers.\n"); ++ ++ enable_irq(ioexp->chip_irq); ++ return 0; ++} ++ ++int mca_ioexp_device_init(struct mca_ioexp *ioexp, u32 irq) ++{ ++ int ret; ++ unsigned int val; ++ ++ ret = regmap_read(ioexp->regmap, MCA_DEVICE_ID, &val); ++ if (ret) { ++ dev_err(ioexp->dev, ++ "Cannot read MCA IO Expander Device ID (%d)\n", ++ ret); ++ return ret; ++ } ++ ioexp->dev_id = (u8)val; ++ ++ if (ioexp->dev_id != MCA_IOEXP_DEVICE_ID_VAL) { ++ dev_err(ioexp->dev, "Invalid MCA IO Expander Device ID (%x)\n", ++ ioexp->dev_id); ++ return -ENODEV; ++ } ++ ++ ret = regmap_read(ioexp->regmap, MCA_HW_VER, &val); ++ if (ret) { ++ dev_err(ioexp->dev, "Cannot read MCA Hardware Version (%d)\n", ++ ret); ++ return ret; ++ } ++ ioexp->hw_version = (u8)val; ++ ++ ret = regmap_bulk_read(ioexp->regmap, MCA_FW_VER_L, &val, 2); ++ if (ret) { ++ dev_err(ioexp->dev, ++ "Cannot read MCA IO Expander Firmware Version (%d)\n", ++ ret); ++ return ret; ++ } ++ ioexp->fw_version = (u16)(val & ~MCA_FW_VER_ALPHA_MASK); ++ ioexp->fw_is_alpha = val & MCA_FW_VER_ALPHA_MASK ? true : false; ++ ++ ioexp->chip_irq = irq; ++ ioexp->gpio_base = -1; ++ ++ if (of_find_property(ioexp->dev->of_node, "restore-config-on-resume", ++ NULL)) { ++ unsigned int gpio_num; ++ ++ ioexp->preserved_regs = kzalloc(sizeof *ioexp->preserved_regs, ++ GFP_KERNEL | GFP_DMA); ++ if (!ioexp->preserved_regs) { ++ dev_err(ioexp->dev, ++ "Failed to allocate memory for preserved registers.\n"); ++ return -ENOMEM; ++ } ++ ++ ret = regmap_read(ioexp->regmap, MCA_GPIO_NUM, &gpio_num); ++ if (ret) { ++ dev_err(ioexp->dev, ++ "Error reading MCA_GPIO_NUM (%d)\n", ret); ++ return ret; ++ } ++ ++ ioexp->preserved_regs->gpio_dir.cnt = (gpio_num + 7) / 8; ++ ioexp->preserved_regs->gpio_dir.values = ++ kzalloc(ioexp->preserved_regs->gpio_dir.cnt, ++ GFP_KERNEL | GFP_DMA); ++ ioexp->preserved_regs->gpio_data.cnt = (gpio_num + 7) / 8; ++ ioexp->preserved_regs->gpio_data.values = ++ kzalloc(ioexp->preserved_regs->gpio_data.cnt, ++ GFP_KERNEL | GFP_DMA); ++ ioexp->preserved_regs->irq_cfg.cnt = gpio_num; ++ ioexp->preserved_regs->irq_cfg.values = ++ kzalloc(ioexp->preserved_regs->irq_cfg.cnt, ++ GFP_KERNEL | GFP_DMA); ++ ioexp->preserved_regs->irq_mask.cnt = 4; ++ ioexp->preserved_regs->irq_mask.values = ++ kzalloc(ioexp->preserved_regs->irq_mask.cnt, ++ GFP_KERNEL | GFP_DMA); ++ ioexp->preserved_regs->adc_cfg.cnt = gpio_num; ++ ioexp->preserved_regs->adc_cfg.values = ++ kzalloc(ioexp->preserved_regs->adc_cfg.cnt, ++ GFP_KERNEL | GFP_DMA); ++ ++ } else { ++ ioexp->preserved_regs = NULL; ++ } ++ ret = mca_ioexp_irq_init(ioexp); ++ if (ret) { ++ dev_err(ioexp->dev, "Cannot initialize interrupts (%d)\n", ret); ++ return ret; ++ } ++ ++ ret = mfd_add_devices(ioexp->dev, -1, mca_ioexp_devs, ++ ARRAY_SIZE(mca_ioexp_devs), NULL, ioexp->irq_base, ++ regmap_irq_get_domain(ioexp->regmap_irq)); ++ ++ if (ret) { ++ dev_err(ioexp->dev, "Cannot add MFD cells (%d)\n", ret); ++ goto out_irq; ++ } ++ ++ ret = sysfs_create_group(&ioexp->dev->kobj, &mca_ioexp_attr_group); ++ if (ret) { ++ dev_err(ioexp->dev, "Cannot create sysfs entries (%d)\n", ret); ++ goto out_dev; ++ } ++ ++ return 0; ++ ++out_dev: ++ mfd_remove_devices(ioexp->dev); ++out_irq: ++ mca_ioexp_irq_exit(ioexp); ++ ++ return ret; ++} ++ ++void mca_ioexp_device_exit(struct mca_ioexp *ioexp) ++{ ++ sysfs_remove_group(&ioexp->dev->kobj, &mca_ioexp_attr_group); ++ mfd_remove_devices(ioexp->dev); ++ mca_ioexp_irq_exit(ioexp); ++ ++ if (!ioexp->preserved_regs) { ++ kfree(ioexp->preserved_regs->gpio_dir.values); ++ kfree(ioexp->preserved_regs->gpio_data.values); ++ kfree(ioexp->preserved_regs->irq_cfg.values); ++ kfree(ioexp->preserved_regs->irq_mask.values); ++ kfree(ioexp->preserved_regs->adc_cfg.values); ++ kfree(ioexp->preserved_regs); ++ ++ ioexp->preserved_regs = NULL; ++ } ++} ++ ++MODULE_AUTHOR("Digi International Inc"); ++MODULE_DESCRIPTION("MCA IO Expander driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/mfd/mca-ioexp-i2c.c b/drivers/mfd/mca-ioexp-i2c.c +new file mode 100644 +index 000000000000..29a5587ce683 +--- /dev/null ++++ b/drivers/mfd/mca-ioexp-i2c.c +@@ -0,0 +1,186 @@ ++/* ++ * Copyright 2017 - 2019 Digi International Inc ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++static const struct regmap_range mca_ioexp_readable_ranges[] = { ++}; ++ ++static const struct regmap_range mca_ioexp_writeable_ranges[] = { ++ regmap_reg_range(MCA_IRQ_STATUS_0, MCA_IRQ_MASK_3), ++ regmap_reg_range(MCA_GPIO_DIR_0, MCA_GPIO_IRQ_CFG_63), ++ regmap_reg_range(MCA_REG_ADC_CFG0_0, MCA_REG_ADC_CFG0_31), ++ regmap_reg_range(MCA_REG_ADC_CFG1_0, MCA_REG_ADC_CFG1_31), ++ regmap_reg_range(MCA_REG_ADC_CFG2_0, MCA_REG_ADC_CFG2_31), ++}; ++ ++static const struct regmap_range mca_ioexp_volatile_ranges[] = { ++ /* Real volatile registers */ ++ regmap_reg_range(MCA_IRQ_STATUS_0, MCA_IRQ_STATUS_3), ++ regmap_reg_range(MCA_GPIO_DATA_0, MCA_GPIO_DATA_7), ++ regmap_reg_range(MCA_GPIO_IRQ_STATUS_0, MCA_GPIO_IRQ_STATUS_7), ++ regmap_reg_range(MCA_REG_ADC_VAL_L_0, MCA_REG_ADC_VAL_H_31), ++ ++ /* ++ * Fake volatile registers. ++ * ++ * These registers could be cached but non-volatile registers makes ++ * regmap access each register one by one which has some drawbacks: ++ * - Breaks CRC in the protocol. ++ * - Requires the MCA firmware to process each access as a separate ++ * access, even when the data requested must be returned in bulk. ++ * ++ * For this reasons we will consider all registers volatile. ++ */ ++ regmap_reg_range(MCA_DEVICE_ID, MCA_UID_9), ++ regmap_reg_range(MCA_IRQ_MASK_0, MCA_IRQ_MASK_3), ++ regmap_reg_range(MCA_GPIO_NUM, MCA_GPIO_DIR_7), ++ regmap_reg_range(MCA_GPIO_IRQ_CFG_0, MCA_GPIO_IRQ_CFG_63), ++ regmap_reg_range(MCA_REG_ADC_NUM_CH, MCA_REG_ADC_NUM_BYTES), ++ regmap_reg_range(MCA_REG_ADC_CFG0_0, MCA_REG_ADC_CFG0_31), ++ regmap_reg_range(MCA_REG_ADC_CFG1_0, MCA_REG_ADC_CFG1_31), ++ regmap_reg_range(MCA_REG_ADC_CFG2_0, MCA_REG_ADC_CFG2_31), ++}; ++ ++static const struct regmap_access_table mca_ioexp_readable_table = { ++ .yes_ranges = mca_ioexp_readable_ranges, ++ .n_yes_ranges = ARRAY_SIZE(mca_ioexp_readable_ranges), ++}; ++ ++static const struct regmap_access_table mca_ioexp_writeable_table = { ++ .yes_ranges = mca_ioexp_writeable_ranges, ++ .n_yes_ranges = ARRAY_SIZE(mca_ioexp_writeable_ranges), ++}; ++ ++static const struct regmap_access_table mca_ioexp_volatile_table = { ++ .yes_ranges = mca_ioexp_volatile_ranges, ++ .n_yes_ranges = ARRAY_SIZE(mca_ioexp_volatile_ranges), ++}; ++ ++static struct regmap_config mca_ioexp_regmap_config = { ++ .reg_bits = 16, ++ .val_bits = 8, ++ .max_register = 0xFFFF, ++ ++ .rd_table = &mca_ioexp_readable_table, ++ .wr_table = &mca_ioexp_writeable_table, ++ .volatile_table = &mca_ioexp_volatile_table, ++ ++ .cache_type = REGCACHE_RBTREE, ++}; ++ ++static const struct of_device_id mca_ioexp_dt_ids[] = { ++ { .compatible = "digi,mca_ioexp", }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, mca_ioexp_dt_ids); ++ ++static int mca_ioexp_i2c_probe(struct i2c_client *i2c, ++ const struct i2c_device_id *id) ++{ ++ struct mca_ioexp *ioexp; ++ int ret; ++ ++ ioexp = devm_kzalloc(&i2c->dev, sizeof(struct mca_ioexp), GFP_KERNEL); ++ if (ioexp == NULL) ++ return -ENOMEM; ++ ++ i2c_set_clientdata(i2c, ioexp); ++ ioexp->dev = &i2c->dev; ++ ioexp->chip_irq = i2c->irq; ++ ioexp->regmap = devm_regmap_init_i2c(i2c, &mca_ioexp_regmap_config); ++ if (IS_ERR(ioexp->regmap)) { ++ ret = PTR_ERR(ioexp->regmap); ++ dev_err(ioexp->dev, "Failed to allocate register map (%d)\n", ret); ++ goto err_regmap; ++ } ++ ++ ret = mca_ioexp_device_init(ioexp, i2c->irq); ++ if (ret) { ++ dev_err(ioexp->dev, "Failed to init i2c device (%d)\n", ret); ++ goto err_regmap; ++ } ++ ++ return 0; ++ ++err_regmap: ++ devm_kfree(ioexp->dev, ioexp); ++ ++ return ret; ++} ++ ++static int mca_ioexp_i2c_remove(struct i2c_client *i2c) ++{ ++ struct mca_ioexp *ioexp = i2c_get_clientdata(i2c); ++ ++ mca_ioexp_device_exit(ioexp); ++ devm_kfree(ioexp->dev, ioexp); ++ ++ return 0; ++} ++ ++static void mca_ioexp_i2c_shutdown(struct i2c_client *i2c) ++{ ++ struct mca_ioexp *ioexp = i2c_get_clientdata(i2c); ++ ++ /* ++ * Disable the IRQ so that the I/O Expander does not wake-up the MCA ++ * when powered off. ++ */ ++ disable_irq(ioexp->chip_irq); ++} ++ ++#ifdef CONFIG_PM ++static int mca_ioexp_i2c_suspend(struct device *dev) ++{ ++ return mca_ioexp_suspend(dev); ++} ++ ++static int mca_ioexp_i2c_resume(struct device *dev) ++{ ++ return mca_ioexp_resume(dev); ++} ++ ++static SIMPLE_DEV_PM_OPS(mca_ioexp_i2c_pm_ops, ++ mca_ioexp_i2c_suspend, ++ mca_ioexp_i2c_resume); ++#endif ++ ++static const struct i2c_device_id mca_ioexp_i2c_id[] = { ++ {"mca_ioexp", 0}, ++ {}, ++}; ++MODULE_DEVICE_TABLE(i2c, mca_ioexp_i2c_id); ++ ++static struct i2c_driver mca_ioexp_i2c_driver = { ++ .driver = { ++ .name = "mca_ioexp", ++ .of_match_table = of_match_ptr(mca_ioexp_dt_ids), ++#ifdef CONFIG_PM ++ .pm = &mca_ioexp_i2c_pm_ops, ++#endif ++ }, ++ .probe = mca_ioexp_i2c_probe, ++ .remove = mca_ioexp_i2c_remove, ++ .shutdown = mca_ioexp_i2c_shutdown, ++ .id_table = mca_ioexp_i2c_id, ++}; ++ ++module_i2c_driver(mca_ioexp_i2c_driver); +diff --git a/drivers/mfd/mca-ioexp-irq.c b/drivers/mfd/mca-ioexp-irq.c +new file mode 100644 +index 000000000000..439149765dd0 +--- /dev/null ++++ b/drivers/mfd/mca-ioexp-irq.c +@@ -0,0 +1,89 @@ ++/* ++ * Copyright 2017 - 2019 Digi International Inc ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define MCA_IOEXP_IRQ_0_OFFSET 0 ++#define MCA_IOEXP_IRQ_1_OFFSET 1 ++#define MCA_IOEXP_IRQ_2_OFFSET 2 ++#define MCA_IOEXP_IRQ_3_OFFSET 3 ++ ++static const struct regmap_irq mca_ioexp_irqs[] = { ++ [MCA_IOEXP_IRQ_GPIO_BANK_0] = { ++ .reg_offset = MCA_IOEXP_IRQ_1_OFFSET, ++ .mask = MCA_GPIO_BANK_0, ++ }, ++ [MCA_IOEXP_IRQ_GPIO_BANK_1] = { ++ .reg_offset = MCA_IOEXP_IRQ_1_OFFSET, ++ .mask = MCA_GPIO_BANK_1, ++ }, ++ [MCA_IOEXP_IRQ_GPIO_BANK_2] = { ++ .reg_offset = MCA_IOEXP_IRQ_1_OFFSET, ++ .mask = MCA_GPIO_BANK_2, ++ }, ++ [MCA_IOEXP_IRQ_GPIO_BANK_3] = { ++ .reg_offset = MCA_IOEXP_IRQ_1_OFFSET, ++ .mask = MCA_GPIO_BANK_3, ++ }, ++ [MCA_IOEXP_IRQ_GPIO_BANK_4] = { ++ .reg_offset = MCA_IOEXP_IRQ_1_OFFSET, ++ .mask = MCA_GPIO_BANK_4, ++ }, ++ [MCA_IOEXP_IRQ_GPIO_BANK_5] = { ++ .reg_offset = MCA_IOEXP_IRQ_1_OFFSET, ++ .mask = MCA_GPIO_BANK_5, ++ }, ++}; ++ ++static const struct regmap_irq_chip mca_ioexp_irq_chip = { ++ .name = "mca-ioexp-irq", ++ .irqs = mca_ioexp_irqs, ++ .num_irqs = ARRAY_SIZE(mca_ioexp_irqs), ++ .num_regs = MCA_IOEXP_NUM_IRQ_REGS, ++ .status_base = MCA_IRQ_STATUS_0, ++ .mask_base = MCA_IRQ_MASK_0, ++ .ack_base = MCA_IRQ_STATUS_0, ++ .init_ack_masked = true, ++}; ++ ++int mca_ioexp_irq_init(struct mca_ioexp *ioexp) ++{ ++ int ret; ++ const int irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_SHARED; ++ ++ if (!ioexp->chip_irq) { ++ dev_err(ioexp->dev, "No IRQ configured\n"); ++ return -EINVAL; ++ } ++ ++ ioexp->irq_base = -1; ++ ret = regmap_add_irq_chip(ioexp->regmap, ioexp->chip_irq, ++ irq_flags, ++ ioexp->irq_base, &mca_ioexp_irq_chip, ++ &ioexp->regmap_irq); ++ if (ret) { ++ dev_err(ioexp->dev, "Failed to request IRQ %d (%d)\n", ++ ioexp->chip_irq, ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++void mca_ioexp_irq_exit(struct mca_ioexp *ioexp) ++{ ++ regmap_del_irq_chip(ioexp->chip_irq, ioexp->regmap_irq); ++} +diff --git a/include/linux/mfd/mca-ioexp/core.h b/include/linux/mfd/mca-ioexp/core.h +new file mode 100644 +index 000000000000..cb9969262da5 +--- /dev/null ++++ b/include/linux/mfd/mca-ioexp/core.h +@@ -0,0 +1,87 @@ ++/* ++ * Copyright 2017 - 2019 Digi International Inc ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#ifndef MFD_MCA_IOEXP_CORE_H_ ++#define MFD_MCA_IOEXP_CORE_H_ ++ ++#include ++#include ++ ++#define MCA_IOEXP_DRVNAME_ADC "mca-ioexp-adc" ++#define MCA_IOEXP_DRVNAME_GPIO "mca-ioexp-gpio" ++ ++#define MCA_IOEXP_DEVICE_ID_VAL 0x37 ++#define MCA_IOEXP_ADDR_LEN 2 ++#define MCA_IOEXP_MAX_FRAME_DATA_LEN 256 ++ ++/* Interrupts */ ++enum mca_ioexp_irqs { ++ MCA_IOEXP_IRQ_GPIO_BANK_0, ++ MCA_IOEXP_IRQ_GPIO_BANK_1, ++ MCA_IOEXP_IRQ_GPIO_BANK_2, ++ MCA_IOEXP_IRQ_GPIO_BANK_3, ++ MCA_IOEXP_IRQ_GPIO_BANK_4, ++ MCA_IOEXP_IRQ_GPIO_BANK_5, ++ /* ... */ ++ ++ MCA_IOEXP_NUM_IRQS, ++}; ++ ++/* Number of interrupt registers */ ++#define MCA_IOEXP_NUM_IRQ_REGS 4 ++ ++struct mca_ioexp { ++ struct device *dev; ++ u8 dev_id; ++ u8 hw_version; ++ bool fw_is_alpha; ++ u16 fw_version; ++ u32 flags; ++ struct regmap *regmap; ++ struct regmap_irq_chip_data *regmap_irq; ++ int chip_irq; ++ u32 irq_base; ++ int gpio_base; ++ ++ struct { ++ struct { ++ uint8_t *values; ++ uint8_t cnt; ++ } gpio_dir; ++ ++ struct { ++ uint8_t *values; ++ uint8_t cnt; ++ } gpio_data; ++ ++ struct { ++ uint8_t *values; ++ uint8_t cnt; ++ } irq_cfg; ++ ++ struct { ++ uint8_t *values; ++ uint8_t cnt; ++ } irq_mask; ++ ++ struct { ++ uint8_t *values; ++ uint8_t cnt; ++ } adc_cfg; ++ } *preserved_regs; ++}; ++ ++int mca_ioexp_device_init(struct mca_ioexp *ioexp, u32 irq); ++int mca_ioexp_irq_init(struct mca_ioexp *ioexp); ++void mca_ioexp_device_exit(struct mca_ioexp *ioexp); ++void mca_ioexp_irq_exit(struct mca_ioexp *ioexp); ++int mca_ioexp_suspend(struct device *dev); ++int mca_ioexp_resume(struct device *dev); ++ ++#endif /* MFD_MCA_IOEXP_CORE_H_ */ +diff --git a/include/linux/mfd/mca-ioexp/registers.h b/include/linux/mfd/mca-ioexp/registers.h +new file mode 100644 +index 000000000000..9359c9953e0d +--- /dev/null ++++ b/include/linux/mfd/mca-ioexp/registers.h +@@ -0,0 +1,15 @@ ++/* ++ * Copyright 2017 - 2019 Digi International Inc ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#ifndef MCA_IOEXP_REGISTERS_H_ ++#define MCA_IOEXP_REGISTERS_H_ ++ ++#include ++ ++#endif /* MCA_IOEXP_REGISTERS_H_ */ diff --git a/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ulsbc/0002-ccimx6ulsbcpro-Add-IOEXP-GPIO-support.patch b/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ulsbc/0002-ccimx6ulsbcpro-Add-IOEXP-GPIO-support.patch new file mode 100644 index 000000000..bd237aa42 --- /dev/null +++ b/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ulsbc/0002-ccimx6ulsbcpro-Add-IOEXP-GPIO-support.patch @@ -0,0 +1,47 @@ +From 4a2907834799eda9bd681ccd85557d6433666903 Mon Sep 17 00:00:00 2001 +From: Alex Gonzalez +Date: Mon, 23 Apr 2018 11:47:38 +0200 +Subject: [PATCH] ccimx6ulsbcpro: Add IOEXP GPIO support + +Synched with v4.14.78/master at: +3f8b03950b323db4ca89b1cdc1c2288f79facaa3 + +Signed-off-by: Alex Gonzalez +--- + arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts | 23 +++++++++++++++++++++++ + 1 file changed, 23 insertions(+) + +diff --git a/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts b/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts +index afc87b34f441..a818aec68959 100644 +--- a/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts ++++ b/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts +@@ -130,6 +130,29 @@ + irq-gpios = <&gpio5 2 GPIO_ACTIVE_HIGH>; + status = "okay"; + }; ++ ++ mca_ioexp: mca_io@6e { ++ compatible = "digi,mca_ioexp"; ++ reg = <0x6e>; ++ interrupt-parent = <&mca_gpio>; ++ interrupts = <0 IRQ_TYPE_EDGE_FALLING>; ++ restore-config-on-resume; ++ status = "okay"; ++ ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ pinctrl-names = "default"; ++ ++ mca_ioexp_gpio: gpio { ++ compatible = "digi,mca-ioexp-gpio"; ++ gpio-controller; ++ #gpio-cells = <2>; ++ ++ interrupt-parent = <&mca_ioexp>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ }; ++ }; + }; + + &lcdif { diff --git a/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ulsbc/0003-ccimx6ulsbcpro-Add-IOEXP-ADC-support.patch b/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ulsbc/0003-ccimx6ulsbcpro-Add-IOEXP-ADC-support.patch new file mode 100644 index 000000000..f84a59121 --- /dev/null +++ b/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ulsbc/0003-ccimx6ulsbcpro-Add-IOEXP-ADC-support.patch @@ -0,0 +1,35 @@ +From d39028a304c0059ee16c65462e3e653670482aca Mon Sep 17 00:00:00 2001 +From: Alex Gonzalez +Date: Mon, 23 Apr 2018 11:48:15 +0200 +Subject: [PATCH] ccimx6ulsbcpro: Add IOEXP ADC support + +Synched with v4.14.78/master at: +3f8b03950b323db4ca89b1cdc1c2288f79facaa3 + +Signed-off-by: Alex Gonzalez +--- + arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts b/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts +index 7df31112cabf..b2b6a49391f4 100644 +--- a/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts ++++ b/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts +@@ -153,6 +153,12 @@ + interrupt-controller; + #interrupt-cells = <2>; + }; ++ ++ mca_ioexp_adc: adc { ++ compatible = "digi,mca-ioexp-adc"; ++ digi,adc-ch-list = <3 4 5>; ++ digi,adc-vref = <3300000>; ++ }; + }; + }; + +@@ -482,4 +488,3 @@ + >; + }; + }; +- diff --git a/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ulsbc/0004-ARM-dts-ccimx6ulsbcpro-Configure-touch-GPIO-reset-li.patch b/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ulsbc/0004-ARM-dts-ccimx6ulsbcpro-Configure-touch-GPIO-reset-li.patch new file mode 100644 index 000000000..10e92666a --- /dev/null +++ b/meta-digi-arm/recipes-kernel/linux/linux-fslc/ccimx6ulsbc/0004-ARM-dts-ccimx6ulsbcpro-Configure-touch-GPIO-reset-li.patch @@ -0,0 +1,38 @@ +From ac65a7be2a14e2c5f90319674527257a53e59402 Mon Sep 17 00:00:00 2001 +From: Alex Gonzalez +Date: Tue, 8 Jan 2019 16:42:40 +0100 +Subject: [PATCH] ARM: dts: ccimx6ulsbcpro: Configure touch GPIO reset line + +The Goodix touch requires both the INT and reset lines in order to fix +an I2C address at startup. + +This commit uses the IOEXP GPIO29 to control the DISP_5V supply which +controls the voltage source for the reset line pull-up. It also sets the +internal pull-up on the INT line so the line is not left floating. + +Signed-off-by: Alex Gonzalez +--- + arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts b/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts +index 14274a0d6435..62133ede2736 100644 +--- a/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts ++++ b/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts +@@ -128,6 +128,7 @@ + interrupt-parent = <&gpio5>; + interrupts = <2 IRQ_TYPE_EDGE_RISING>; + irq-gpios = <&gpio5 2 GPIO_ACTIVE_HIGH>; ++ reset-gpios = <&mca_ioexp_gpio 29 GPIO_ACTIVE_HIGH>; + status = "okay"; + }; + +@@ -366,7 +367,7 @@ + + pinctrl_goodix_touch: goodixgrp{ + fsl,pins = < +- MX6UL_PAD_SNVS_TAMPER2__GPIO5_IO02 0x1020 ++ MX6UL_PAD_SNVS_TAMPER2__GPIO5_IO02 0x1b0b0 + >; + }; + diff --git a/meta-digi-arm/recipes-kernel/linux/linux-fslc/defconfig b/meta-digi-arm/recipes-kernel/linux/linux-fslc/defconfig new file mode 100644 index 000000000..731c434b4 --- /dev/null +++ b/meta-digi-arm/recipes-kernel/linux/linux-fslc/defconfig @@ -0,0 +1,452 @@ +CONFIG_KERNEL_LZO=y +CONFIG_SYSVIPC=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_PREEMPT_VOLUNTARY=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=18 +CONFIG_CGROUPS=y +CONFIG_RELAY=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_EXPERT=y +CONFIG_PERF_EVENTS=y +# CONFIG_SLUB_DEBUG is not set +# CONFIG_COMPAT_BRK is not set +CONFIG_ARCH_MULTI_V6=y +CONFIG_ARCH_MXC=y +CONFIG_MACH_MX31LILLY=y +CONFIG_MACH_MX31LITE=y +CONFIG_MACH_PCM037=y +CONFIG_MACH_PCM037_EET=y +CONFIG_MACH_MX31_3DS=y +CONFIG_MACH_MX31MOBOARD=y +CONFIG_MACH_QONG=y +CONFIG_MACH_ARMADILLO5X0=y +CONFIG_MACH_KZM_ARM11_01=y +CONFIG_MACH_IMX31_DT=y +CONFIG_MACH_IMX35_DT=y +CONFIG_MACH_PCM043=y +CONFIG_MACH_MX35_3DS=y +CONFIG_MACH_VPR200=y +CONFIG_SOC_IMX50=y +CONFIG_SOC_IMX51=y +CONFIG_SOC_IMX53=y +CONFIG_SOC_IMX6Q=y +CONFIG_SOC_IMX6SL=y +CONFIG_SOC_IMX6SLL=y +CONFIG_SOC_IMX6SX=y +CONFIG_SOM_CC6UL=y +CONFIG_SOC_IMX7D=y +CONFIG_SOC_VF610=y +CONFIG_PCI=y +CONFIG_PCI_MSI=y +CONFIG_PCI_IMX6=y +CONFIG_SMP=y +CONFIG_ARM_PSCI=y +CONFIG_HIGHMEM=y +CONFIG_FORCE_MAX_ZONEORDER=14 +CONFIG_CMDLINE="noinitrd console=ttymxc0,115200" +CONFIG_KEXEC=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPUFREQ_DT=y +CONFIG_ARM_IMX6Q_CPUFREQ=y +CONFIG_CPU_IDLE=y +CONFIG_ARM_CPUIDLE=y +CONFIG_VFP=y +CONFIG_NEON=y +CONFIG_PM_DEBUG=y +CONFIG_PM_TEST_SUSPEND=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SRCVERSION_ALL=y +# CONFIG_BLK_DEV_BSG is not set +CONFIG_BINFMT_MISC=m +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +CONFIG_NETFILTER=y +CONFIG_CAN=y +CONFIG_CAN_FLEXCAN=y +CONFIG_BT=y +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_LL=y +CONFIG_CFG80211=y +CONFIG_CFG80211_WEXT=y +CONFIG_MAC80211=y +CONFIG_RFKILL=y +CONFIG_RFKILL_INPUT=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +# CONFIG_STANDALONE is not set +CONFIG_FW_LOADER_USER_HELPER=y +CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y +CONFIG_CMA_SIZE_MBYTES=64 +CONFIG_IMX_WEIM=y +CONFIG_CONNECTOR=y +CONFIG_MTD=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_CFI=y +CONFIG_MTD_JEDECPROBE=y +CONFIG_MTD_CFI_INTELEXT=y +CONFIG_MTD_CFI_AMDSTD=y +CONFIG_MTD_CFI_STAA=y +CONFIG_MTD_PHYSMAP_OF=y +CONFIG_MTD_DATAFLASH=y +CONFIG_MTD_M25P80=y +CONFIG_MTD_SST25L=y +CONFIG_MTD_NAND=y +CONFIG_MTD_NAND_GPMI_NAND=y +CONFIG_MTD_NAND_VF610_NFC=y +CONFIG_MTD_NAND_MXC=y +CONFIG_MTD_SPI_NOR=y +CONFIG_SPI_FSL_QUADSPI=y +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_FASTMAP=y +CONFIG_MTD_UBI_BLOCK=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=65536 +CONFIG_EEPROM_AT24=y +CONFIG_EEPROM_AT25=y +# CONFIG_SCSI_PROC_FS is not set +CONFIG_BLK_DEV_SD=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +# CONFIG_SCSI_LOWLEVEL is not set +CONFIG_ATA=y +CONFIG_SATA_AHCI_PLATFORM=y +CONFIG_AHCI_IMX=y +CONFIG_PATA_IMX=y +CONFIG_NETDEVICES=y +# CONFIG_NET_VENDOR_BROADCOM is not set +CONFIG_CS89x0=y +CONFIG_CS89x0_PLATFORM=y +# 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_SMC91X=y +CONFIG_SMC911X=y +CONFIG_SMSC911X=y +# CONFIG_NET_VENDOR_STMICRO is not set +CONFIG_AT803X_PHY=y +CONFIG_MICREL_PHY=y +CONFIG_SMSC_PHY=y +CONFIG_USB_PEGASUS=m +CONFIG_USB_RTL8150=m +CONFIG_USB_RTL8152=y +CONFIG_USB_LAN78XX=y +CONFIG_USB_USBNET=y +CONFIG_USB_NET_CDC_EEM=m +CONFIG_USB_NET_SMSC95XX=y +CONFIG_USB_NET_MCS7830=y +CONFIG_BRCMFMAC=m +CONFIG_MWIFIEX=m +CONFIG_MWIFIEX_SDIO=m +CONFIG_MWIFIEX_PCIE=m +CONFIG_WL12XX=m +CONFIG_WL18XX=m +CONFIG_WLCORE_SDIO=m +# CONFIG_WILINK_PLATFORM_DATA is not set +CONFIG_INPUT_EVDEV=y +CONFIG_KEYBOARD_GPIO=y +CONFIG_KEYBOARD_SNVS_PWRKEY=y +CONFIG_KEYBOARD_IMX=y +CONFIG_MOUSE_PS2=m +CONFIG_MOUSE_PS2_ELANTECH=y +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ADS7846=y +CONFIG_TOUCHSCREEN_EGALAX=y +CONFIG_TOUCHSCREEN_MAX11801=y +CONFIG_TOUCHSCREEN_IMX6UL_TSC=y +CONFIG_TOUCHSCREEN_EDT_FT5X06=y +CONFIG_TOUCHSCREEN_MC13783=y +CONFIG_TOUCHSCREEN_TSC2004=y +CONFIG_TOUCHSCREEN_TSC2007=y +CONFIG_TOUCHSCREEN_STMPE=y +CONFIG_TOUCHSCREEN_SX8654=y +CONFIG_TOUCHSCREEN_COLIBRI_VF50=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_MMA8450=y +CONFIG_INPUT_MCA_PWRKEY=y +CONFIG_SERIO_SERPORT=m +# CONFIG_LEGACY_PTYS is not set +CONFIG_SERIAL_IMX=y +CONFIG_SERIAL_IMX_CONSOLE=y +CONFIG_SERIAL_MCA_CC6UL=y +CONFIG_SERIAL_FSL_LPUART=y +CONFIG_SERIAL_FSL_LPUART_CONSOLE=y +CONFIG_SERIAL_DEV_BUS=y +# CONFIG_I2C_COMPAT is not set +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_MUX=y +CONFIG_I2C_MUX_GPIO=y +# CONFIG_I2C_HELPER_AUTO is not set +CONFIG_I2C_ALGOPCF=m +CONFIG_I2C_ALGOPCA=m +CONFIG_I2C_GPIO=y +CONFIG_I2C_IMX=y +CONFIG_SPI=y +CONFIG_SPI_GPIO=y +CONFIG_SPI_IMX=y +CONFIG_SPI_FSL_DSPI=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_MCA=y +CONFIG_GPIO_MAX732X=y +CONFIG_GPIO_MC9S08DZ60=y +CONFIG_GPIO_PCA953X=y +CONFIG_GPIO_STMPE=y +CONFIG_GPIO_74X164=y +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_POWER_RESET_SYSCON_POWEROFF=y +CONFIG_POWER_SUPPLY=y +CONFIG_SENSORS_MC13783_ADC=y +CONFIG_SENSORS_GPIO_FAN=y +CONFIG_SENSORS_IIO_HWMON=y +CONFIG_THERMAL_WRITABLE_TRIPS=y +CONFIG_CPU_THERMAL=y +CONFIG_IMX_THERMAL=y +CONFIG_WATCHDOG=y +CONFIG_DA9062_WATCHDOG=y +CONFIG_RN5T618_WATCHDOG=y +CONFIG_MCA_WATCHDOG=y +CONFIG_IMX2_WDT=y +CONFIG_MFD_DA9052_I2C=y +CONFIG_MFD_DA9062=y +CONFIG_MFD_MC13XXX_SPI=y +CONFIG_MFD_MC13XXX_I2C=y +CONFIG_MFD_RN5T618=y +CONFIG_MFD_MCA_IOEXP=y +CONFIG_MFD_STMPE=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_ANATOP=y +CONFIG_REGULATOR_DA9052=y +CONFIG_REGULATOR_DA9062=y +CONFIG_REGULATOR_GPIO=y +CONFIG_REGULATOR_MC13783=y +CONFIG_REGULATOR_MC13892=y +CONFIG_REGULATOR_PFUZE100=y +CONFIG_REGULATOR_RN5T618=y +CONFIG_RC_CORE=y +CONFIG_RC_DEVICES=y +CONFIG_IR_GPIO_CIR=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_V4L2_SUBDEV_API=y +CONFIG_MEDIA_USB_SUPPORT=y +CONFIG_USB_VIDEO_CLASS=m +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_VIDEO_MUX=y +CONFIG_SOC_CAMERA=y +CONFIG_V4L_MEM2MEM_DRIVERS=y +CONFIG_VIDEO_CODA=m +# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set +CONFIG_VIDEO_ADV7180=m +CONFIG_VIDEO_OV5640=m +CONFIG_IMX_IPUV3_CORE=y +CONFIG_DRM=y +CONFIG_DRM_PANEL_LVDS=y +CONFIG_DRM_PANEL_SIMPLE=y +CONFIG_DRM_PANEL_SEIKO_43WVF1G=y +CONFIG_DRM_DW_HDMI_AHB_AUDIO=m +CONFIG_DRM_DW_HDMI_CEC=y +CONFIG_DRM_IMX=y +CONFIG_DRM_IMX_PARALLEL_DISPLAY=y +CONFIG_DRM_IMX_TVE=y +CONFIG_DRM_IMX_LDB=y +CONFIG_DRM_IMX_HDMI=y +CONFIG_DRM_ETNAVIV=y +CONFIG_DRM_MXSFB=y +CONFIG_FB_MODE_HELPERS=y +CONFIG_LCD_CLASS_DEVICE=y +CONFIG_LCD_L4F00242T03=y +CONFIG_LCD_PLATFORM=y +CONFIG_BACKLIGHT_PWM=y +CONFIG_BACKLIGHT_GPIO=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_LOGO=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_USB_AUDIO=m +CONFIG_SND_SOC=y +CONFIG_SND_SOC_FSL_ASRC=y +CONFIG_SND_IMX_SOC=y +CONFIG_SND_SOC_PHYCORE_AC97=y +CONFIG_SND_SOC_EUKREA_TLV320=y +CONFIG_SND_SOC_IMX_ES8328=y +CONFIG_SND_SOC_IMX_SGTL5000=y +CONFIG_SND_SOC_IMX_SPDIF=y +CONFIG_SND_SOC_IMX_MC13783=y +CONFIG_SND_SOC_FSL_ASOC_CARD=y +CONFIG_SND_SOC_AC97_CODEC=y +CONFIG_SND_SOC_CS42XX8_I2C=y +CONFIG_SND_SOC_TLV320AIC3X=y +CONFIG_SND_SOC_WM8960=y +CONFIG_SND_SOC_WM8962=y +CONFIG_SND_SIMPLE_CARD=y +CONFIG_HID_MULTITOUCH=y +CONFIG_USB=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_MXC=y +CONFIG_USB_STORAGE=y +CONFIG_USB_CHIPIDEA=y +CONFIG_USB_CHIPIDEA_UDC=y +CONFIG_USB_CHIPIDEA_HOST=y +CONFIG_USB_SERIAL=m +CONFIG_USB_SERIAL_GENERIC=y +CONFIG_USB_SERIAL_FTDI_SIO=m +CONFIG_USB_SERIAL_OPTION=m +CONFIG_USB_TEST=m +CONFIG_USB_EHSET_TEST_FIXTURE=m +CONFIG_NOP_USB_XCEIV=y +CONFIG_USB_MXS_PHY=y +CONFIG_USB_GADGET=y +CONFIG_USB_FSL_USB2=y +CONFIG_USB_CONFIGFS=m +CONFIG_USB_CONFIGFS_SERIAL=y +CONFIG_USB_CONFIGFS_ACM=y +CONFIG_USB_CONFIGFS_OBEX=y +CONFIG_USB_CONFIGFS_NCM=y +CONFIG_USB_CONFIGFS_ECM=y +CONFIG_USB_CONFIGFS_ECM_SUBSET=y +CONFIG_USB_CONFIGFS_RNDIS=y +CONFIG_USB_CONFIGFS_EEM=y +CONFIG_USB_CONFIGFS_MASS_STORAGE=y +CONFIG_USB_CONFIGFS_F_LB_SS=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_USB_CONFIGFS_F_UAC1=y +CONFIG_USB_CONFIGFS_F_UAC2=y +CONFIG_USB_CONFIGFS_F_MIDI=y +CONFIG_USB_CONFIGFS_F_HID=y +CONFIG_USB_CONFIGFS_F_UVC=y +CONFIG_USB_CONFIGFS_F_PRINTER=y +CONFIG_USB_ZERO=m +CONFIG_USB_AUDIO=m +CONFIG_USB_ETH=m +CONFIG_USB_G_NCM=m +CONFIG_USB_GADGETFS=m +CONFIG_USB_FUNCTIONFS=m +CONFIG_USB_MASS_STORAGE=m +CONFIG_USB_G_SERIAL=m +CONFIG_MMC=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_ESDHC_IMX=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_PWM=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_ONESHOT=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_BACKLIGHT=y +CONFIG_LEDS_TRIGGER_GPIO=y +CONFIG_LEDS_TRIGGER_DEFAULT_ON=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_INTF_DEV_UIE_EMUL=y +CONFIG_RTC_DRV_DS1307=y +CONFIG_RTC_DRV_ISL1208=y +CONFIG_RTC_DRV_PCF8523=y +CONFIG_RTC_DRV_PCF8563=y +CONFIG_RTC_DRV_M41T80=y +CONFIG_RTC_DRV_DA9063=y +CONFIG_RTC_DRV_MCA=y +CONFIG_RTC_DRV_MC13XXX=y +CONFIG_RTC_DRV_MXC=y +CONFIG_RTC_DRV_MXC_V2=y +CONFIG_RTC_DRV_SNVS=y +CONFIG_DMADEVICES=y +CONFIG_FSL_EDMA=y +CONFIG_IMX_SDMA=y +CONFIG_MXS_DMA=y +CONFIG_DMATEST=m +CONFIG_STAGING=y +CONFIG_STAGING_MEDIA=y +CONFIG_VIDEO_IMX_MEDIA=y +CONFIG_COMMON_CLK_PWM=y +CONFIG_IIO=y +CONFIG_MMA8452=y +CONFIG_IMX7D_ADC=y +CONFIG_MCA_ADC=y +CONFIG_VF610_ADC=y +CONFIG_SENSORS_ISL29018=y +CONFIG_MAG3110=y +CONFIG_MPL3115=y +CONFIG_TAMPER_MCA=y +CONFIG_PWM=y +CONFIG_PWM_FSL_FTM=y +CONFIG_PWM_IMX=y +CONFIG_NVMEM_IMX_OCOTP=y +CONFIG_NVMEM_VF610_OCOTP=y +CONFIG_TEE=y +CONFIG_OPTEE=y +CONFIG_MUX_MMIO=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT2_FS_POSIX_ACL=y +CONFIG_EXT2_FS_SECURITY=y +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_POSIX_ACL=y +CONFIG_EXT3_FS_SECURITY=y +CONFIG_QUOTA=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +# CONFIG_PRINT_QUOTA_WARNING is not set +CONFIG_AUTOFS4_FS=y +CONFIG_FUSE_FS=y +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_UDF_FS=m +CONFIG_MSDOS_FS=m +CONFIG_VFAT_FS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_JFFS2_FS=y +CONFIG_UBIFS_FS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +CONFIG_ROOT_NFS=y +CONFIG_NLS_DEFAULT="cp437" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_ISO8859_15=m +CONFIG_NLS_UTF8=y +CONFIG_SECURITYFS=y +CONFIG_CRYPTO_DEV_FSL_CAAM=y +CONFIG_CRYPTO_DEV_SAHARA=y +CONFIG_CRC_CCITT=m +CONFIG_CRC_T10DIF=y +CONFIG_CRC7=m +CONFIG_LIBCRC32C=m +CONFIG_FONTS=y +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +CONFIG_PRINTK_TIME=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_SCHED_DEBUG is not set +CONFIG_PROVE_LOCKING=y +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_FTRACE is not set diff --git a/meta-digi-arm/recipes-kernel/linux/linux-fslc_%.bbappend b/meta-digi-arm/recipes-kernel/linux/linux-fslc_%.bbappend new file mode 100644 index 000000000..6eb422b06 --- /dev/null +++ b/meta-digi-arm/recipes-kernel/linux/linux-fslc_%.bbappend @@ -0,0 +1,22 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +SRC_URI_append_ccimx6ul_use-mainline-bsp = " \ + file://0001-ARM-Add-support-for-the-ConnectCore-6UL-System-On-Mo.patch \ + file://0002-mach-imx-pm-imx6-Add-hooks-for-board-specific-implem.patch \ + file://0003-imx6ul-Add-MCA-core-I2C-driver-support.patch \ + file://0004-imx6ul-Add-MCA-GPIO-support-for-the-ConnectCore-6UL-.patch \ + file://0005-imx6ul-Add-MCA-IOMUX-support-to-the-ConnectCore-6UL-.patch \ + file://0006-imx6ul-Add-MCA-watchdog-support-for-the-ConnectCore-.patch \ + file://0007-imx6ul-Add-MCA-ADC-support-for-ConnectCore-6UL-SOM-a.patch \ + file://0008-imx6ul-Add-MCA-tamper-support-for-ConnectCore-6UL-SO.patch \ + file://0009-imx6ul-Add-MCA-UART-support-for-ConnectCore-6UL-SOM-.patch \ + file://0010-imx6ul-Add-RTC-MCA-support-for-ConnectCore-6UL-SOM.patch \ + file://0011-imx6ul-Add-MCA-power-key-support-for-ConnectCore-6UL.patch \ +" + +SRC_URI_append_ccimx6ulsbc_use-mainline-bsp = " \ + file://0001-ccimx6ulsbcpro-Add-IOEXP-core-I2C-support.patch \ + file://0002-ccimx6ulsbcpro-Add-IOEXP-GPIO-support.patch \ + file://0003-ccimx6ulsbcpro-Add-IOEXP-ADC-support.patch \ + file://0004-ARM-dts-ccimx6ulsbcpro-Configure-touch-GPIO-reset-li.patch \ +" diff --git a/meta-digi-dey/recipes-connectivity/modemmanager/modemmanager/ccimx8x-sbc-pro/78-mm-digi-xbee-cellular.rules b/meta-digi-dey/recipes-connectivity/modemmanager/modemmanager/ccimx8x-sbc-pro/78-mm-digi-xbee-cellular.rules index 343df7940..b7fa7fab3 100644 --- a/meta-digi-dey/recipes-connectivity/modemmanager/modemmanager/ccimx8x-sbc-pro/78-mm-digi-xbee-cellular.rules +++ b/meta-digi-dey/recipes-connectivity/modemmanager/modemmanager/ccimx8x-sbc-pro/78-mm-digi-xbee-cellular.rules @@ -1,10 +1,10 @@ -#ACTION!="add|change|move", GOTO="mm_digi_xbee_cellular_modem_end" +ACTION!="add|change|move", GOTO="mm_digi_xbee_cellular_modem_end" # By default, ModemManager expects a default baudrate of 57600bps. Different # baudrates may be used by setting the ID_MM_TTY_BAUDRATE udev tag. # ModemManager documentation states that the best practice is to use the DEVPATH # this way rather than other rules such as KERNEL, so be careful when modifying -#DEVPATH=="/devices/platform/5a060000.serial/tty/ttyLP0", ENV{ID_MM_PLATFORM_DRIVER_PROBE}="1", ENV{ID_MM_PHYSDEV_UID}="Digi XBee Cellular" +DEVPATH=="/devices/platform/5a800000.i2c/i2c-0/0-0063/mca-cc8x-uart/tty/ttyMCA0", ENV{ID_MM_PLATFORM_DRIVER_PROBE}="1", ENV{ID_MM_PHYSDEV_UID}="Digi XBee Cellular" -#LABEL="mm_digi_xbee_cellular_modem_end" +LABEL="mm_digi_xbee_cellular_modem_end" diff --git a/meta-digi-dey/recipes-connectivity/modemmanager/modemmanager/late-modems-scan.service b/meta-digi-dey/recipes-connectivity/modemmanager/modemmanager/late-modems-scan.service new file mode 100644 index 000000000..35e36ece4 --- /dev/null +++ b/meta-digi-dey/recipes-connectivity/modemmanager/modemmanager/late-modems-scan.service @@ -0,0 +1,8 @@ +[Unit] +Description=Late modems scan +Requires=ModemManager.service +After=ModemManager.service xbee-init.service time-sync.target + +[Service] +Type=oneshot +ExecStart=mmcli --scan-modems 1>/dev/null 2>&1 diff --git a/meta-digi-dey/recipes-connectivity/modemmanager/modemmanager/late-modems-scan.timer b/meta-digi-dey/recipes-connectivity/modemmanager/modemmanager/late-modems-scan.timer new file mode 100644 index 000000000..02348a9af --- /dev/null +++ b/meta-digi-dey/recipes-connectivity/modemmanager/modemmanager/late-modems-scan.timer @@ -0,0 +1,8 @@ +[Unit] +Description=Late modems scan timer + +[Timer] +OnActiveSec=40s + +[Install] +WantedBy=multi-user.target diff --git a/meta-digi-dey/recipes-connectivity/modemmanager/modemmanager_%.bbappend b/meta-digi-dey/recipes-connectivity/modemmanager/modemmanager_%.bbappend index 85afe616e..8469412aa 100644 --- a/meta-digi-dey/recipes-connectivity/modemmanager/modemmanager_%.bbappend +++ b/meta-digi-dey/recipes-connectivity/modemmanager/modemmanager_%.bbappend @@ -5,6 +5,8 @@ FILESEXTRAPATHS_prepend := "${THISDIR}/${BPN}:" SRC_URI += " \ file://78-mm-digi-xbee-cellular.rules \ file://80-mm-net-device-blacklist.rules \ + file://late-modems-scan.service \ + file://late-modems-scan.timer \ " # 'polkit' depends on 'consolekit', and this requires 'x11' distro feature. So @@ -19,6 +21,14 @@ do_install_append() { # Install udev rules for ModemManager blacklist devices install -m 0644 ${WORKDIR}/80-mm-net-device-blacklist.rules ${D}${nonarch_base_libdir}/udev/rules.d/ + + # Install systemd service for scanning late modems + install -d ${D}${systemd_unitdir}/system/ + install -m 0644 ${WORKDIR}/late-modems-scan.service ${D}${systemd_unitdir}/system/ + install -m 0644 ${WORKDIR}/late-modems-scan.timer ${D}${systemd_unitdir}/system/ } +SYSTEMD_SERVICE_${PN}_append = " late-modems-scan.timer" +FILES_${PN}_append = " late-modems-scan.timer" + PACKAGE_ARCH = "${MACHINE_ARCH}" diff --git a/meta-digi-dey/recipes-connectivity/xbee/xbee.bb b/meta-digi-dey/recipes-connectivity/xbee/xbee.bb new file mode 100644 index 000000000..8bf090b99 --- /dev/null +++ b/meta-digi-dey/recipes-connectivity/xbee/xbee.bb @@ -0,0 +1,40 @@ +# Copyright (C) 2019 Digi International Inc. + +SUMMARY = "Digi XBee initialization" +DESCRIPTION = "Initialization scripts for XBee hardware of Digi boards" +SECTION = "base" +LICENSE = "GPL-2.0" +LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/GPL-2.0;md5=801f80980d171dd6425610833a22dbe6" + +SRC_URI = " \ + file://xbee-init \ + file://xbee-init.service \ +" +S = "${WORKDIR}" + +do_install() { + install -d ${D}${sysconfdir}/init.d/ + install -m 0755 ${WORKDIR}/xbee-init ${D}${sysconfdir}/ + ln -sf /etc/xbee-init ${D}${sysconfdir}/init.d/xbee-init + sed -i -e "s,##XBEE_RESET_N_GPIO##,${XBEE_RESET_N_GPIO},g" \ + -e "s,##XBEE_SLEEP_RQ_GPIO##,${XBEE_SLEEP_RQ_GPIO},g" \ + ${D}${sysconfdir}/xbee-init + + install -d ${D}${systemd_unitdir}/system/ + install -m 0644 ${WORKDIR}/xbee-init.service ${D}${systemd_unitdir}/system/ +} + +PACKAGES =+ "${PN}-init" +FILES_${PN}-init = " \ + ${sysconfdir}/xbee-init \ + ${sysconfdir}/init.d/xbee-init \ + ${systemd_unitdir}/system/xbee-init.service \ +" +INITSCRIPT_PACKAGES += "${PN}-init" +INITSCRIPT_NAME_${PN}-init = "xbee-init" +INITSCRIPT_PARAMS_${PN}-init = "start 19 2 3 4 5 . stop 21 0 1 6 ." + +SYSTEMD_SERVICE_${PN}-init = "xbee-init.service" + +PACKAGE_ARCH = "${MACHINE_ARCH}" +COMPATIBLE_MACHINE = "(ccimx8x)" diff --git a/meta-digi-dey/recipes-connectivity/xbee/xbee/xbee-init b/meta-digi-dey/recipes-connectivity/xbee/xbee/xbee-init new file mode 100644 index 000000000..9236a2dc6 --- /dev/null +++ b/meta-digi-dey/recipes-connectivity/xbee/xbee/xbee-init @@ -0,0 +1,70 @@ +#!/bin/sh + +# Linux GPIOs on XBee lines +XBEE_RESET_N_GPIO="##XBEE_RESET_N_GPIO##" +XBEE_SLEEP_RQ_GPIO="##XBEE_SLEEP_RQ_GPIO##" + +# request_gpio +request_gpio_out() { + local SG_GPIONR="${1}" + local SG_GPIOPATH="/sys/class/gpio/gpio${SG_GPIONR}" + + [ -d "${SG_GPIOPATH}" ] || printf "%s" "${SG_GPIONR}" > /sys/class/gpio/export + printf out > "${SG_GPIOPATH}/direction" && sleep .2 +} + +# free_gpio +free_gpio() { + local SG_GPIONR="${1}" + local SG_GPIOPATH="/sys/class/gpio/gpio${SG_GPIONR}" + + [ -d "${SG_GPIOPATH}" ] && printf "%s" "${SG_GPIONR}" > /sys/class/gpio/unexport +} + +# set_gpio_value +set_gpio_value() { + local SG_GPIONR="${1}" + local SG_GPIOVAL="${2}" + local SG_GPIOPATH="/sys/class/gpio/gpio${SG_GPIONR}" + + printf out > "${SG_GPIOPATH}/direction" && sleep .2 + printf "${SG_GPIOVAL}" > "${SG_GPIOPATH}/value" && sleep .2 +} + +xbee_init() { + # Power cycle XBEE_RESET_N + request_gpio_out ${XBEE_RESET_N_GPIO} + set_gpio_value ${XBEE_RESET_N_GPIO} 0 + set_gpio_value ${XBEE_RESET_N_GPIO} 1 + + # Set low XBEE_SLEEP_RQ + request_gpio_out ${XBEE_SLEEP_RQ_GPIO} + set_gpio_value ${XBEE_SLEEP_RQ_GPIO} 0 +} + +xbee_stop() { + free_gpio ${XBEE_RESET_N_GPIO} + free_gpio ${XBEE_SLEEP_RQ_GPIO} +} + +case "$1" in + start) + echo -n "Starting XBee hardware: " + xbee_init + echo "done." + ;; + stop) + echo -n "Stopping XBee hardware: " + xbee_stop + echo "done." + ;; + restart) + $0 stop + sleep 1 + $0 start + ;; + *) + echo "Usage: $0 {start|stop|restart}" + exit 1 + ;; +esac diff --git a/meta-digi-dey/recipes-connectivity/xbee/xbee/xbee-init.service b/meta-digi-dey/recipes-connectivity/xbee/xbee/xbee-init.service new file mode 100644 index 000000000..f56cc5580 --- /dev/null +++ b/meta-digi-dey/recipes-connectivity/xbee/xbee/xbee-init.service @@ -0,0 +1,12 @@ +[Unit] +Description=XBee GPIOs initialization +Before=ModemManager.service + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/etc/xbee-init start +ExecStop=/etc/xbee-init stop + +[Install] +WantedBy=multi-user.target diff --git a/meta-digi-dey/recipes-core/images/core-image-base.bbappend b/meta-digi-dey/recipes-core/images/core-image-base.bbappend index 083746b19..91211c1e2 100644 --- a/meta-digi-dey/recipes-core/images/core-image-base.bbappend +++ b/meta-digi-dey/recipes-core/images/core-image-base.bbappend @@ -13,9 +13,7 @@ IMAGE_FEATURES += " \ ${@bb.utils.contains('MACHINE_FEATURES', 'wifi', 'dey-wireless', '', d)} \ " -IMAGE_INSTALL = " \ - dey-examples-digiapix \ -" +CORE_IMAGE_BASE_INSTALL += "dey-examples-digiapix" # SDK features (for toolchains generated from an image with populate_sdk) SDKIMAGE_FEATURES ?= "dev-pkgs dbg-pkgs staticdev-pkgs" diff --git a/meta-digi-dey/recipes-core/systemd/systemd/0002-sd-resolve-forcefully-cancel-worker-threads-during-r.patch b/meta-digi-dey/recipes-core/systemd/systemd/0002-sd-resolve-forcefully-cancel-worker-threads-during-r.patch new file mode 100644 index 000000000..7f9d7f940 --- /dev/null +++ b/meta-digi-dey/recipes-core/systemd/systemd/0002-sd-resolve-forcefully-cancel-worker-threads-during-r.patch @@ -0,0 +1,30 @@ +From: Gabriel Valcazar +Date: Thu, 28 Mar 2019 17:40:00 +0100 +Subject: [PATCH] sd-resolve: forcefully cancel worker threads during + resolve_free + +For services that use sd-resolve, such as timesyncd, this prevents the system +from stalling whenever the service stops while a worker thread is busy calling +getaddrinfo(). + +Signed-off-by: Gabriel Valcazar +--- + src/libsystemd/sd-resolve/sd-resolve.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/src/libsystemd/sd-resolve/sd-resolve.c b/src/libsystemd/sd-resolve/sd-resolve.c +index a189f14..571604c 100644 +--- a/src/libsystemd/sd-resolve/sd-resolve.c ++++ b/src/libsystemd/sd-resolve/sd-resolve.c +@@ -577,8 +577,10 @@ static void resolve_free(sd_resolve *resolve) { + + /* Now terminate them and wait until they are gone. + If we get an error than most likely the thread already exited. */ +- for (i = 0; i < resolve->n_valid_workers; i++) ++ for (i = 0; i < resolve->n_valid_workers; i++) { ++ pthread_cancel(resolve->workers[i]); + (void) pthread_join(resolve->workers[i], NULL); ++ } + + /* Close all communication channels */ + close_many(resolve->fds, _FD_MAX); diff --git a/meta-digi-dey/recipes-core/systemd/systemd_%.bbappend b/meta-digi-dey/recipes-core/systemd/systemd_%.bbappend index 4efaad656..68c00a54e 100644 --- a/meta-digi-dey/recipes-core/systemd/systemd_%.bbappend +++ b/meta-digi-dey/recipes-core/systemd/systemd_%.bbappend @@ -1,6 +1,9 @@ FILESEXTRAPATHS_prepend := "${THISDIR}/${BPN}:" -SRC_URI += "file://0001-udev-use-the-usual-set-of-load-paths-for-udev-rules.patch" +SRC_URI += " \ + file://0001-udev-use-the-usual-set-of-load-paths-for-udev-rules.patch \ + file://0002-sd-resolve-forcefully-cancel-worker-threads-during-r.patch \ +" #FIX-it: Workaround as missing ending slash in FIRMWARE_PATH [YOCIMX-2831] EXTRA_OEMESON_remove = "-Dfirmware-path=${nonarch_base_libdir}/firmware " diff --git a/meta-digi-dey/recipes-devtools/python-numpy/python-numpy_%.bbappend b/meta-digi-dey/recipes-devtools/python-numpy/python-numpy_%.bbappend new file mode 100644 index 000000000..a7a260a70 --- /dev/null +++ b/meta-digi-dey/recipes-devtools/python-numpy/python-numpy_%.bbappend @@ -0,0 +1,25 @@ +# Copyright (C) 2019 Digi International Inc. + +# Re-add the base RDEPENDS to class-target since they are being overwritten in the +# original recipe +RDEPENDS_${PN}_class-target_append = " \ + ${PYTHON_PN}-unittest \ + ${PYTHON_PN}-difflib \ + ${PYTHON_PN}-pprint \ + ${PYTHON_PN}-pickle \ + ${PYTHON_PN}-shell \ + ${PYTHON_PN}-nose \ + ${PYTHON_PN}-doctest \ + ${PYTHON_PN}-datetime \ + ${PYTHON_PN}-distutils \ + ${PYTHON_PN}-misc \ + ${PYTHON_PN}-mmap \ + ${PYTHON_PN}-netclient \ + ${PYTHON_PN}-numbers \ + ${PYTHON_PN}-pydoc \ + ${PYTHON_PN}-pkgutil \ + ${PYTHON_PN}-email \ + ${PYTHON_PN}-compression \ + ${PYTHON_PN}-ctypes \ + ${PYTHON_PN}-threading \ +" diff --git a/meta-digi-dey/recipes-digi/dey-examples/dey-examples-hdp.bb b/meta-digi-dey/recipes-digi/dey-examples/dey-examples-hdp.bb index 4df25e0e1..e2b013939 100644 --- a/meta-digi-dey/recipes-digi/dey-examples/dey-examples-hdp.bb +++ b/meta-digi-dey/recipes-digi/dey-examples/dey-examples-hdp.bb @@ -14,4 +14,4 @@ do_install() { install -m 0755 hdp-test.py ${D}${bindir} } -RDEPENDS_${PN} = "python3 python3-argparse python3-crypt python3-dbus python3-pygobject" +RDEPENDS_${PN} = "python3 python3-crypt python3-dbus python3-pygobject" diff --git a/meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/99-digiapix.rules b/meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/99-digiapix.rules index 04ec742bb..f48d744cb 100644 --- a/meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/99-digiapix.rules +++ b/meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/99-digiapix.rules @@ -19,4 +19,4 @@ SUBSYSTEM=="spidev", GROUP="digiapix", MODE="0660" SUBSYSTEM=="gpio", KERNEL=="gpiochip0", ACTION=="add", RUN="/etc/udev/scripts/digiapix.sh" SUBSYSTEM=="gpio", KERNEL!="gpiochip*", ACTION=="add", RUN="/etc/udev/scripts/digiapix.sh" -SUBSYSTEM=="pwm", ACTION=="add", RUN="/etc/udev/scripts/digiapix.sh" +SUBSYSTEM=="pwm", ACTION=="add|change", RUN="/etc/udev/scripts/digiapix.sh" diff --git a/meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/ccimx6qpsbc/board.conf b/meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/ccimx6qpsbc/libdigiapix.conf similarity index 91% rename from meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/ccimx6qpsbc/board.conf rename to meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/ccimx6qpsbc/libdigiapix.conf index 31d0afd0b..42e862742 100644 --- a/meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/ccimx6qpsbc/board.conf +++ b/meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/ccimx6qpsbc/libdigiapix.conf @@ -5,12 +5,13 @@ model = Digi International ConnectCore 6 QuadPlus Single Board Computer. # USER LED (RED) - GPIO02_IO02 USER_LED = 34 +USER_LED0 = 34 # USER LED (ORANGE) - GPIO02_IO03 -USER_LED_2 = 35 +USER_LED1 = 35 # USER LED (GREEN) - GPIO02_IO04 -USER_LED_3 = 36 +USER_LED2 = 36 # USER BUTTON - GPIO02_IO05 USER_BUTTON = 37 diff --git a/meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/ccimx6sbc/board.conf b/meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/ccimx6sbc/libdigiapix.conf similarity index 91% rename from meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/ccimx6sbc/board.conf rename to meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/ccimx6sbc/libdigiapix.conf index 5d726f2e9..7025848c5 100644 --- a/meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/ccimx6sbc/board.conf +++ b/meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/ccimx6sbc/libdigiapix.conf @@ -5,12 +5,13 @@ model = Digi International ConnectCore 6 Single Board Computer. # USER LED (RED) - GPIO02_IO02 USER_LED = 34 +USER_LED0 = 34 # USER LED (ORANGE) - GPIO02_IO03 -USER_LED_2 = 35 +USER_LED1 = 35 # USER LED (GREEN) - GPIO02_IO04 -USER_LED_3 = 36 +USER_LED2 = 36 # USER BUTTON - GPIO02_IO05 USER_BUTTON = 37 diff --git a/meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/ccimx6ulsbc/board.conf b/meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/ccimx6ulsbc/libdigiapix.conf similarity index 100% rename from meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/ccimx6ulsbc/board.conf rename to meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/ccimx6ulsbc/libdigiapix.conf diff --git a/meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/ccimx6ulstarter/board.conf b/meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/ccimx6ulstarter/libdigiapix.conf similarity index 100% rename from meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/ccimx6ulstarter/board.conf rename to meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/ccimx6ulstarter/libdigiapix.conf diff --git a/meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/ccimx8x-sbc-express/board.conf b/meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/ccimx8x-sbc-express/libdigiapix.conf similarity index 81% rename from meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/ccimx8x-sbc-express/board.conf rename to meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/ccimx8x-sbc-express/libdigiapix.conf index d2e6acaf0..dd4e8f41b 100644 --- a/meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/ccimx8x-sbc-express/board.conf +++ b/meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/ccimx8x-sbc-express/libdigiapix.conf @@ -26,5 +26,5 @@ DEFAULT_PWM = 7,0 [ADC] -# TODO: only MCA ADCs are supported at the moment, with them being disabled by default +# ADC_IN0 on Expansion connector DEFAULT_ADC = 0,0 diff --git a/meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/ccimx8x-sbc-pro/board.conf b/meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/ccimx8x-sbc-pro/libdigiapix.conf similarity index 73% rename from meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/ccimx8x-sbc-pro/board.conf rename to meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/ccimx8x-sbc-pro/libdigiapix.conf index d316ef92e..5dc776e25 100644 --- a/meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/ccimx8x-sbc-pro/board.conf +++ b/meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/ccimx8x-sbc-pro/libdigiapix.conf @@ -4,10 +4,14 @@ model = Digi International ConnectCore 8X SBC Pro. [GPIO] # USER LED0 - MCA_IO18 -USER_LED = 222 +USER_LED = 223 +USER_LED0 = 223 + +# USER_LED1 - MCA_IO10 +USER_LED1 = 215 # USER BUTTON - MCA_IO05 -USER_BUTTON = 209 +USER_BUTTON = 210 [I2C] @@ -26,5 +30,5 @@ DEFAULT_PWM = 7,0 [ADC] -# TODO: only MCA ADCs are supported at the moment, with them being disabled by default +# ADC_IN0 on Expansion connector DEFAULT_ADC = 0,0 diff --git a/meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/digiapix.sh b/meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/digiapix.sh index bc3897c85..8a9bdd26e 100644 --- a/meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/digiapix.sh +++ b/meta-digi-dey/recipes-digi/libdigiapix/libdigiapix-git/digiapix.sh @@ -19,13 +19,17 @@ if basename "${DEVPATH}" | grep -qs "gpiochip0$"; then # Use 'gpiochip0' event to set group and mode for 'export/unexport' files chown root:digiapix /sys/class/gpio/export /sys/class/gpio/unexport chmod g+w /sys/class/gpio/export /sys/class/gpio/unexport -elif basename "${DEVPATH}" | grep -qs "pwmchip[0-9]\+$"; then +elif basename "${DEVPATH}" | grep -qs "pwmchip[0-9]\+$" && [ "${ACTION}" = "add" ] ; then # Set group and mode for pwmchip's 'export/unexport' files chown root:digiapix /sys${DEVPATH}/export /sys${DEVPATH}/unexport chmod g+w /sys${DEVPATH}/export /sys${DEVPATH}/unexport +elif basename "${DEVPATH}" | grep -qs "pwmchip[0-9]\+$" && [ "${ACTION}" = "change" ] ; then + # Set group and mode for 'pwmX' channel and all files inside it... + chown root:digiapix /sys${DEVPATH}/${EXPORT} /sys${DEVPATH}/${EXPORT}/* + chmod g+w /sys${DEVPATH}/${EXPORT}/* else # Change group and mode of the sysfs files created whenever a 'gpioX' - # or 'pwmX' is exported + # is exported chown -h root:digiapix /sys${DEVPATH}/* chmod g+w /sys${DEVPATH}/* fi diff --git a/meta-digi-dey/recipes-digi/libdigiapix/libdigiapix_git.bb b/meta-digi-dey/recipes-digi/libdigiapix/libdigiapix_git.bb index 4702ca7d8..9d6251efa 100644 --- a/meta-digi-dey/recipes-digi/libdigiapix/libdigiapix_git.bb +++ b/meta-digi-dey/recipes-digi/libdigiapix/libdigiapix_git.bb @@ -19,7 +19,7 @@ LIBDIGIAPIX_GIT_URI ?= "${@oe.utils.conditional('DIGI_INTERNAL_GIT', '1' , '${LI SRC_URI = " \ ${LIBDIGIAPIX_GIT_URI};branch=${SRCBRANCH} \ file://99-digiapix.rules \ - file://board.conf \ + file://libdigiapix.conf \ file://digiapix.sh \ " @@ -36,7 +36,7 @@ do_install() { install -m 0755 ${WORKDIR}/digiapix.sh ${D}${sysconfdir}/udev/scripts/ # Install board config file - install -m 0644 ${WORKDIR}/board.conf ${D}${sysconfdir}/libdigiapix.conf + install -m 0644 ${WORKDIR}/libdigiapix.conf ${D}${sysconfdir}/ } USERADD_PACKAGES = "${PN}" diff --git a/meta-digi-dey/recipes-digi/memwatch/memwatch/memwatch.c b/meta-digi-dey/recipes-digi/memwatch/memwatch/memwatch.c index f532a3407..449707c80 100644 --- a/meta-digi-dey/recipes-digi/memwatch/memwatch/memwatch.c +++ b/meta-digi-dey/recipes-digi/memwatch/memwatch/memwatch.c @@ -193,7 +193,7 @@ int main( int argc, char** argv ) } if( filename != NULL ) { - if( ( fd_file = open( filename, O_RDWR | O_CREAT ) ) < 0 ) { + if( ( fd_file = open( filename, O_RDWR | O_CREAT , 0600 ) ) < 0 ) { fprintf( stderr, "Error opening file: %s (%d)\n", filename, errno ); release_resources_and_exit( EXIT_FAILURE ); diff --git a/sdk/build-github.sh b/sdk/build-github.sh index fbb5750cb..eb9484621 100755 --- a/sdk/build-github.sh +++ b/sdk/build-github.sh @@ -22,7 +22,7 @@ set -e -AVAILABLE_PLATFORMS="ccimx8x-sbc-pro ccimx8x-sbc-express ccimx6qpsbc ccimx6sbc ccimx6ulsbc ccimx6ulstarter" +AVAILABLE_PLATFORMS="ccimx8x-sbc-pro ccimx8x-sbc-express ccimx6ulsbc ccimx6ulstarter" MANIFEST_URL="https://github.com/digi-embedded/dey-manifest.git" @@ -123,8 +123,6 @@ while read _pl _tgt; do done<<-_EOF_ ccimx8x-sbc-pro dey-image-qt ccimx8x-sbc-express dey-image-qt - ccimx6qpsbc dey-image-qt - ccimx6sbc dey-image-qt ccimx6ulsbc dey-image-qt ccimx6ulstarter core-image-base _EOF_ diff --git a/sdk/build.sh b/sdk/build.sh index 43177f2ce..3d8bddd91 100755 --- a/sdk/build.sh +++ b/sdk/build.sh @@ -173,8 +173,6 @@ while read _pl _var _tgt; do done<<-_EOF_ ccimx8x-sbc-pro DONTBUILDVARIANTS dey-image-qt,dey-image-aws ccimx8x-sbc-express DONTBUILDVARIANTS dey-image-qt,dey-image-aws - ccimx6qpsbc DONTBUILDVARIANTS dey-image-qt,dey-image-aws - ccimx6sbc DONTBUILDVARIANTS dey-image-qt,dey-image-aws ccimx6ulsbc DONTBUILDVARIANTS dey-image-qt,dey-image-aws ccimx6ulstarter DONTBUILDVARIANTS core-image-base,dey-image-aws ccimx6ulsom DONTBUILDVARIANTS dey-image-mft-module-min diff --git a/sdk/config/ccimx6qpsbc/bblayers.conf.sample b/sdk/config/ccimx6qpsbc/bblayers.conf.sample deleted file mode 100644 index bc2ef9645..000000000 --- a/sdk/config/ccimx6qpsbc/bblayers.conf.sample +++ /dev/null @@ -1,22 +0,0 @@ -# POKY_BBLAYERS_CONF_VERSION is increased each time build/conf/bblayers.conf -# changes incompatibly -POKY_BBLAYERS_CONF_VERSION = "2" - -BBPATH = "${TOPDIR}" -BBFILES ?= "" - -BBLAYERS ?= " \ - ##OEROOT##/meta \ - ##OEROOT##/meta-poky \ - ##OEROOT##/meta-yocto-bsp \ - ##DIGIBASE##/meta-openembedded/meta-oe \ - ##DIGIBASE##/meta-openembedded/meta-python \ - ##DIGIBASE##/meta-openembedded/meta-networking \ - ##DIGIBASE##/meta-openembedded/meta-webserver \ - ##DIGIBASE##/meta-qt5 \ - ##DIGIBASE##/meta-swupdate \ - ##DIGIBASE##/meta-freescale \ - ##DIGIBASE##/meta-fsl-demos \ - ##DIGIBASE##/meta-digi/meta-digi-arm \ - ##DIGIBASE##/meta-digi/meta-digi-dey \ - " diff --git a/sdk/config/ccimx6qpsbc/conf-notes.txt b/sdk/config/ccimx6qpsbc/conf-notes.txt deleted file mode 100644 index ed1cd5158..000000000 --- a/sdk/config/ccimx6qpsbc/conf-notes.txt +++ /dev/null @@ -1,18 +0,0 @@ -Digi Embedded Yocto provides the following image recipes: - - * dey-image-qt: graphical QT image - - By default the image is X11-based so it provides a full SATO theme - desktop environment. - - To compile the image for the framebuffer (instead of X11) add the - following line to the project's conf/local.conf: - - DISTRO_FEATURES_remove = "x11" - - * dey-image-aws: console-only image supporting Amazon Web Services IoT - - This image includes the AWS Greengrass Core and an AWS IoT platform - example application. For more information on AWS support in Digi - Embedded Yocto see the online documentation. - diff --git a/sdk/config/ccimx6qpsbc/local.conf.sample b/sdk/config/ccimx6qpsbc/local.conf.sample deleted file mode 100644 index 490703ef4..000000000 --- a/sdk/config/ccimx6qpsbc/local.conf.sample +++ /dev/null @@ -1,266 +0,0 @@ -# -# This file is your local configuration file and is where all local user settings -# are placed. The comments in this file give some guide to the options a new user -# to the system might want to change but pretty much any configuration option can -# be set in this file. More adventurous users can look at local.conf.extended -# which contains other examples of configuration which can be placed in this file -# but new users likely won't need any of them initially. -# -# Lines starting with the '#' character are commented out and in some cases the -# default values are provided as comments to show people example syntax. Enabling -# the option is a question of removing the # character and making any change to the -# variable as required. - -# -# Machine Selection -# -# You need to select a specific machine to target the build with. There are a selection -# of emulated machines available which can boot and run in the QEMU emulator: -# -#MACHINE ?= "qemuarm" -#MACHINE ?= "qemuarm64" -#MACHINE ?= "qemumips" -#MACHINE ?= "qemumips64" -#MACHINE ?= "qemuppc" -#MACHINE ?= "qemux86" -#MACHINE ?= "qemux86-64" -# -# There are also the following hardware board target machines included for -# demonstration purposes: -# -#MACHINE ?= "beaglebone-yocto" -#MACHINE ?= "genericx86" -#MACHINE ?= "genericx86-64" -#MACHINE ?= "mpc8315e-rdb" -#MACHINE ?= "edgerouter" -# -# This sets the default machine to be qemux86 if no other machine is selected: -#MACHINE ??= "qemux86" - -MACHINE = "ccimx6qpsbc" - -# -# Use Digi's internal git repositories -# -#DIGI_INTERNAL_GIT ?= "1" - -# -# Where to place downloads -# -# During a first build the system will download many different source code tarballs -# from various upstream projects. This can take a while, particularly if your network -# connection is slow. These are all stored in DL_DIR. When wiping and rebuilding you -# can preserve this directory to speed up this part of subsequent builds. This directory -# is safe to share between multiple builds on the same machine too. -# -# The default is a downloads directory under TOPDIR which is the build directory. -# -#DL_DIR ?= "${TOPDIR}/downloads" - -# -# Where to place shared-state files -# -# BitBake has the capability to accelerate builds based on previously built output. -# This is done using "shared state" files which can be thought of as cache objects -# and this option determines where those files are placed. -# -# You can wipe out TMPDIR leaving this directory intact and the build would regenerate -# from these files if no changes were made to the configuration. If changes were made -# to the configuration, only shared state files where the state was still valid would -# be used (done using checksums). -# -# The default is a sstate-cache directory under TOPDIR. -# -#SSTATE_DIR ?= "${TOPDIR}/sstate-cache" - -# -# Where to place the build output -# -# This option specifies where the bulk of the building work should be done and -# where BitBake should place its temporary files and output. Keep in mind that -# this includes the extraction and compilation of many applications and the toolchain -# which can use Gigabytes of hard disk space. -# -# The default is a tmp directory under TOPDIR. -# -#TMPDIR = "${TOPDIR}/tmp" - -# -# Default policy config -# -# The distribution setting controls which policy settings are used as defaults. -# The default value is fine for general Yocto project use, at least initially. -# Ultimately when creating custom policy, people will likely end up subclassing -# these defaults. -# -DISTRO ?= "dey" -# As an example of a subclass there is a "bleeding" edge policy configuration -# where many versions are set to the absolute latest code from the upstream -# source control systems. This is just mentioned here as an example, its not -# useful to most new users. -# DISTRO ?= "poky-bleeding" - -# -# Package Management configuration -# -# This variable lists which packaging formats to enable. Multiple package backends -# can be enabled at once and the first item listed in the variable will be used -# to generate the root filesystems. -# Options are: -# - 'package_deb' for debian style deb files -# - 'package_ipk' for ipk files are used by opkg (a debian style embedded package manager) -# - 'package_rpm' for rpm style packages -# E.g.: PACKAGE_CLASSES ?= "package_rpm package_deb package_ipk" -# We default to rpm: -PACKAGE_CLASSES ?= "package_rpm" - -# -# SDK target architecture -# -# This variable specifies the architecture to build SDK items for and means -# you can build the SDK packages for architectures other than the machine you are -# running the build on (i.e. building i686 packages on an x86_64 host). -# Supported values are i686 and x86_64 -#SDKMACHINE ?= "i686" - -# -# Extra image configuration defaults -# -# The EXTRA_IMAGE_FEATURES variable allows extra packages to be added to the generated -# images. Some of these options are added to certain image types automatically. The -# variable can contain the following options: -# "dbg-pkgs" - add -dbg packages for all installed packages -# (adds symbol information for debugging/profiling) -# "dev-pkgs" - add -dev packages for all installed packages -# (useful if you want to develop against libs in the image) -# "ptest-pkgs" - add -ptest packages for all ptest-enabled packages -# (useful if you want to run the package test suites) -# "tools-sdk" - add development tools (gcc, make, pkgconfig etc.) -# "tools-debug" - add debugging tools (gdb, strace) -# "eclipse-debug" - add Eclipse remote debugging support -# "tools-profile" - add profiling tools (oprofile, lttng, valgrind) -# "tools-testapps" - add useful testing tools (ts_print, aplay, arecord etc.) -# "debug-tweaks" - make an image suitable for development -# e.g. ssh root access has a blank password -# There are other application targets that can be used here too, see -# meta/classes/image.bbclass and meta/classes/core-image.bbclass for more details. -# We default to enabling the debugging tweaks. -EXTRA_IMAGE_FEATURES ?= "debug-tweaks" - -# -# Additional image features -# -# The following is a list of additional classes to use when building images which -# enable extra features. Some available options which can be included in this variable -# are: -# - 'buildstats' collect build statistics -# - 'image-mklibs' to reduce shared library files size for an image -# - 'image-prelink' in order to prelink the filesystem image -# NOTE: if listing mklibs & prelink both, then make sure mklibs is before prelink -# NOTE: mklibs also needs to be explicitly enabled for a given image, see local.conf.extended -USER_CLASSES ?= "buildstats image-mklibs image-prelink" - -# -# Runtime testing of images -# -# The build system can test booting virtual machine images under qemu (an emulator) -# after any root filesystems are created and run tests against those images. It can also -# run tests against any SDK that are built. To enable this uncomment these lines. -# See classes/test{image,sdk}.bbclass for further details. -#IMAGE_CLASSES += "testimage testsdk" -#TESTIMAGE_AUTO_qemuall = "1" - -# -# Interactive shell configuration -# -# Under certain circumstances the system may need input from you and to do this it -# can launch an interactive shell. It needs to do this since the build is -# multithreaded and needs to be able to handle the case where more than one parallel -# process may require the user's attention. The default is iterate over the available -# terminal types to find one that works. -# -# Examples of the occasions this may happen are when resolving patches which cannot -# be applied, to use the devshell or the kernel menuconfig -# -# Supported values are auto, gnome, xfce, rxvt, screen, konsole (KDE 3.x only), none -# Note: currently, Konsole support only works for KDE 3.x due to the way -# newer Konsole versions behave -#OE_TERMINAL = "auto" -# By default disable interactive patch resolution (tasks will just fail instead): -PATCHRESOLVE = "noop" - -# -# Disk Space Monitoring during the build -# -# Monitor the disk space during the build. If there is less that 1GB of space or less -# than 100K inodes in any key build location (TMPDIR, DL_DIR, SSTATE_DIR), gracefully -# shutdown the build. If there is less that 100MB or 1K inodes, perform a hard abort -# of the build. The reason for this is that running completely out of space can corrupt -# files and damages the build in ways which may not be easily recoverable. -# It's necesary to monitor /tmp, if there is no space left the build will fail -# with very exotic errors. -BB_DISKMON_DIRS ??= "\ - STOPTASKS,${TMPDIR},1G,100K \ - STOPTASKS,${DL_DIR},1G,100K \ - STOPTASKS,${SSTATE_DIR},1G,100K \ - STOPTASKS,/tmp,100M,100K \ - ABORT,${TMPDIR},100M,1K \ - ABORT,${DL_DIR},100M,1K \ - ABORT,${SSTATE_DIR},100M,1K \ - ABORT,/tmp,10M,1K" - -# -# Shared-state files from other locations -# -# As mentioned above, shared state files are prebuilt cache data objects which can -# used to accelerate build time. This variable can be used to configure the system -# to search other mirror locations for these objects before it builds the data itself. -# -# This can be a filesystem directory, or a remote url such as http or ftp. These -# would contain the sstate-cache results from previous builds (possibly from other -# machines). This variable works like fetcher MIRRORS/PREMIRRORS and points to the -# cache locations to check for the shared objects. -# NOTE: if the mirror uses the same structure as SSTATE_DIR, you need to add PATH -# at the end as shown in the examples below. This will be substituted with the -# correct path within the directory structure. -#SSTATE_MIRRORS ?= "\ -#file://.* http://someserver.tld/share/sstate/PATH;downloadfilename=PATH \n \ -#file://.* file:///some/local/dir/sstate/PATH" - -# -# Yocto Project SState Mirror -# -# The Yocto Project has prebuilt artefacts available for its releases, you can enable -# use of these by uncommenting the following line. This will mean the build uses -# the network to check for artefacts at the start of builds, which does slow it down -# equally, it will also speed up the builds by not having to build things if they are -# present in the cache. It assumes you can download something faster than you can build it -# which will depend on your network. -# -#SSTATE_MIRRORS ?= "file://.* http://sstate.yoctoproject.org/2.5/PATH;downloadfilename=PATH" - -# -# Qemu configuration -# -# By default qemu will build with a builtin VNC server where graphical output can be -# seen. The two lines below enable the SDL backend too. By default libsdl2-native will -# be built, if you want to use your host's libSDL instead of the minimal libsdl built -# by libsdl2-native then uncomment the ASSUME_PROVIDED line below. -PACKAGECONFIG_append_pn-qemu-native = " sdl" -PACKAGECONFIG_append_pn-nativesdk-qemu = " sdl" -#ASSUME_PROVIDED += "libsdl2-native" - -# CONF_VERSION is increased each time build/conf/ changes incompatibly and is used to -# track the version of this file when it was generated. This can safely be ignored if -# this doesn't mean anything to you. -CONF_VERSION = "1" - -# -# Enable local PR server -# -PRSERV_HOST = "localhost:0" - -# -# Some libraries and packages are covered by NXP EULA -# -#ACCEPT_FSL_EULA = "1" diff --git a/sdk/config/ccimx6sbc/bblayers.conf.sample b/sdk/config/ccimx6sbc/bblayers.conf.sample deleted file mode 100644 index bc2ef9645..000000000 --- a/sdk/config/ccimx6sbc/bblayers.conf.sample +++ /dev/null @@ -1,22 +0,0 @@ -# POKY_BBLAYERS_CONF_VERSION is increased each time build/conf/bblayers.conf -# changes incompatibly -POKY_BBLAYERS_CONF_VERSION = "2" - -BBPATH = "${TOPDIR}" -BBFILES ?= "" - -BBLAYERS ?= " \ - ##OEROOT##/meta \ - ##OEROOT##/meta-poky \ - ##OEROOT##/meta-yocto-bsp \ - ##DIGIBASE##/meta-openembedded/meta-oe \ - ##DIGIBASE##/meta-openembedded/meta-python \ - ##DIGIBASE##/meta-openembedded/meta-networking \ - ##DIGIBASE##/meta-openembedded/meta-webserver \ - ##DIGIBASE##/meta-qt5 \ - ##DIGIBASE##/meta-swupdate \ - ##DIGIBASE##/meta-freescale \ - ##DIGIBASE##/meta-fsl-demos \ - ##DIGIBASE##/meta-digi/meta-digi-arm \ - ##DIGIBASE##/meta-digi/meta-digi-dey \ - " diff --git a/sdk/config/ccimx6sbc/conf-notes.txt b/sdk/config/ccimx6sbc/conf-notes.txt deleted file mode 100644 index ed1cd5158..000000000 --- a/sdk/config/ccimx6sbc/conf-notes.txt +++ /dev/null @@ -1,18 +0,0 @@ -Digi Embedded Yocto provides the following image recipes: - - * dey-image-qt: graphical QT image - - By default the image is X11-based so it provides a full SATO theme - desktop environment. - - To compile the image for the framebuffer (instead of X11) add the - following line to the project's conf/local.conf: - - DISTRO_FEATURES_remove = "x11" - - * dey-image-aws: console-only image supporting Amazon Web Services IoT - - This image includes the AWS Greengrass Core and an AWS IoT platform - example application. For more information on AWS support in Digi - Embedded Yocto see the online documentation. - diff --git a/sdk/config/ccimx6sbc/local.conf.sample b/sdk/config/ccimx6sbc/local.conf.sample deleted file mode 100644 index e57efbd01..000000000 --- a/sdk/config/ccimx6sbc/local.conf.sample +++ /dev/null @@ -1,266 +0,0 @@ -# -# This file is your local configuration file and is where all local user settings -# are placed. The comments in this file give some guide to the options a new user -# to the system might want to change but pretty much any configuration option can -# be set in this file. More adventurous users can look at local.conf.extended -# which contains other examples of configuration which can be placed in this file -# but new users likely won't need any of them initially. -# -# Lines starting with the '#' character are commented out and in some cases the -# default values are provided as comments to show people example syntax. Enabling -# the option is a question of removing the # character and making any change to the -# variable as required. - -# -# Machine Selection -# -# You need to select a specific machine to target the build with. There are a selection -# of emulated machines available which can boot and run in the QEMU emulator: -# -#MACHINE ?= "qemuarm" -#MACHINE ?= "qemuarm64" -#MACHINE ?= "qemumips" -#MACHINE ?= "qemumips64" -#MACHINE ?= "qemuppc" -#MACHINE ?= "qemux86" -#MACHINE ?= "qemux86-64" -# -# There are also the following hardware board target machines included for -# demonstration purposes: -# -#MACHINE ?= "beaglebone-yocto" -#MACHINE ?= "genericx86" -#MACHINE ?= "genericx86-64" -#MACHINE ?= "mpc8315e-rdb" -#MACHINE ?= "edgerouter" -# -# This sets the default machine to be qemux86 if no other machine is selected: -#MACHINE ??= "qemux86" - -MACHINE = "ccimx6sbc" - -# -# Use Digi's internal git repositories -# -#DIGI_INTERNAL_GIT ?= "1" - -# -# Where to place downloads -# -# During a first build the system will download many different source code tarballs -# from various upstream projects. This can take a while, particularly if your network -# connection is slow. These are all stored in DL_DIR. When wiping and rebuilding you -# can preserve this directory to speed up this part of subsequent builds. This directory -# is safe to share between multiple builds on the same machine too. -# -# The default is a downloads directory under TOPDIR which is the build directory. -# -#DL_DIR ?= "${TOPDIR}/downloads" - -# -# Where to place shared-state files -# -# BitBake has the capability to accelerate builds based on previously built output. -# This is done using "shared state" files which can be thought of as cache objects -# and this option determines where those files are placed. -# -# You can wipe out TMPDIR leaving this directory intact and the build would regenerate -# from these files if no changes were made to the configuration. If changes were made -# to the configuration, only shared state files where the state was still valid would -# be used (done using checksums). -# -# The default is a sstate-cache directory under TOPDIR. -# -#SSTATE_DIR ?= "${TOPDIR}/sstate-cache" - -# -# Where to place the build output -# -# This option specifies where the bulk of the building work should be done and -# where BitBake should place its temporary files and output. Keep in mind that -# this includes the extraction and compilation of many applications and the toolchain -# which can use Gigabytes of hard disk space. -# -# The default is a tmp directory under TOPDIR. -# -#TMPDIR = "${TOPDIR}/tmp" - -# -# Default policy config -# -# The distribution setting controls which policy settings are used as defaults. -# The default value is fine for general Yocto project use, at least initially. -# Ultimately when creating custom policy, people will likely end up subclassing -# these defaults. -# -DISTRO ?= "dey" -# As an example of a subclass there is a "bleeding" edge policy configuration -# where many versions are set to the absolute latest code from the upstream -# source control systems. This is just mentioned here as an example, its not -# useful to most new users. -# DISTRO ?= "poky-bleeding" - -# -# Package Management configuration -# -# This variable lists which packaging formats to enable. Multiple package backends -# can be enabled at once and the first item listed in the variable will be used -# to generate the root filesystems. -# Options are: -# - 'package_deb' for debian style deb files -# - 'package_ipk' for ipk files are used by opkg (a debian style embedded package manager) -# - 'package_rpm' for rpm style packages -# E.g.: PACKAGE_CLASSES ?= "package_rpm package_deb package_ipk" -# We default to rpm: -PACKAGE_CLASSES ?= "package_rpm" - -# -# SDK target architecture -# -# This variable specifies the architecture to build SDK items for and means -# you can build the SDK packages for architectures other than the machine you are -# running the build on (i.e. building i686 packages on an x86_64 host). -# Supported values are i686 and x86_64 -#SDKMACHINE ?= "i686" - -# -# Extra image configuration defaults -# -# The EXTRA_IMAGE_FEATURES variable allows extra packages to be added to the generated -# images. Some of these options are added to certain image types automatically. The -# variable can contain the following options: -# "dbg-pkgs" - add -dbg packages for all installed packages -# (adds symbol information for debugging/profiling) -# "dev-pkgs" - add -dev packages for all installed packages -# (useful if you want to develop against libs in the image) -# "ptest-pkgs" - add -ptest packages for all ptest-enabled packages -# (useful if you want to run the package test suites) -# "tools-sdk" - add development tools (gcc, make, pkgconfig etc.) -# "tools-debug" - add debugging tools (gdb, strace) -# "eclipse-debug" - add Eclipse remote debugging support -# "tools-profile" - add profiling tools (oprofile, lttng, valgrind) -# "tools-testapps" - add useful testing tools (ts_print, aplay, arecord etc.) -# "debug-tweaks" - make an image suitable for development -# e.g. ssh root access has a blank password -# There are other application targets that can be used here too, see -# meta/classes/image.bbclass and meta/classes/core-image.bbclass for more details. -# We default to enabling the debugging tweaks. -EXTRA_IMAGE_FEATURES ?= "debug-tweaks" - -# -# Additional image features -# -# The following is a list of additional classes to use when building images which -# enable extra features. Some available options which can be included in this variable -# are: -# - 'buildstats' collect build statistics -# - 'image-mklibs' to reduce shared library files size for an image -# - 'image-prelink' in order to prelink the filesystem image -# NOTE: if listing mklibs & prelink both, then make sure mklibs is before prelink -# NOTE: mklibs also needs to be explicitly enabled for a given image, see local.conf.extended -USER_CLASSES ?= "buildstats image-mklibs image-prelink" - -# -# Runtime testing of images -# -# The build system can test booting virtual machine images under qemu (an emulator) -# after any root filesystems are created and run tests against those images. It can also -# run tests against any SDK that are built. To enable this uncomment these lines. -# See classes/test{image,sdk}.bbclass for further details. -#IMAGE_CLASSES += "testimage testsdk" -#TESTIMAGE_AUTO_qemuall = "1" - -# -# Interactive shell configuration -# -# Under certain circumstances the system may need input from you and to do this it -# can launch an interactive shell. It needs to do this since the build is -# multithreaded and needs to be able to handle the case where more than one parallel -# process may require the user's attention. The default is iterate over the available -# terminal types to find one that works. -# -# Examples of the occasions this may happen are when resolving patches which cannot -# be applied, to use the devshell or the kernel menuconfig -# -# Supported values are auto, gnome, xfce, rxvt, screen, konsole (KDE 3.x only), none -# Note: currently, Konsole support only works for KDE 3.x due to the way -# newer Konsole versions behave -#OE_TERMINAL = "auto" -# By default disable interactive patch resolution (tasks will just fail instead): -PATCHRESOLVE = "noop" - -# -# Disk Space Monitoring during the build -# -# Monitor the disk space during the build. If there is less that 1GB of space or less -# than 100K inodes in any key build location (TMPDIR, DL_DIR, SSTATE_DIR), gracefully -# shutdown the build. If there is less that 100MB or 1K inodes, perform a hard abort -# of the build. The reason for this is that running completely out of space can corrupt -# files and damages the build in ways which may not be easily recoverable. -# It's necesary to monitor /tmp, if there is no space left the build will fail -# with very exotic errors. -BB_DISKMON_DIRS ??= "\ - STOPTASKS,${TMPDIR},1G,100K \ - STOPTASKS,${DL_DIR},1G,100K \ - STOPTASKS,${SSTATE_DIR},1G,100K \ - STOPTASKS,/tmp,100M,100K \ - ABORT,${TMPDIR},100M,1K \ - ABORT,${DL_DIR},100M,1K \ - ABORT,${SSTATE_DIR},100M,1K \ - ABORT,/tmp,10M,1K" - -# -# Shared-state files from other locations -# -# As mentioned above, shared state files are prebuilt cache data objects which can -# used to accelerate build time. This variable can be used to configure the system -# to search other mirror locations for these objects before it builds the data itself. -# -# This can be a filesystem directory, or a remote url such as http or ftp. These -# would contain the sstate-cache results from previous builds (possibly from other -# machines). This variable works like fetcher MIRRORS/PREMIRRORS and points to the -# cache locations to check for the shared objects. -# NOTE: if the mirror uses the same structure as SSTATE_DIR, you need to add PATH -# at the end as shown in the examples below. This will be substituted with the -# correct path within the directory structure. -#SSTATE_MIRRORS ?= "\ -#file://.* http://someserver.tld/share/sstate/PATH;downloadfilename=PATH \n \ -#file://.* file:///some/local/dir/sstate/PATH" - -# -# Yocto Project SState Mirror -# -# The Yocto Project has prebuilt artefacts available for its releases, you can enable -# use of these by uncommenting the following line. This will mean the build uses -# the network to check for artefacts at the start of builds, which does slow it down -# equally, it will also speed up the builds by not having to build things if they are -# present in the cache. It assumes you can download something faster than you can build it -# which will depend on your network. -# -#SSTATE_MIRRORS ?= "file://.* http://sstate.yoctoproject.org/2.5/PATH;downloadfilename=PATH" - -# -# Qemu configuration -# -# By default qemu will build with a builtin VNC server where graphical output can be -# seen. The two lines below enable the SDL backend too. By default libsdl2-native will -# be built, if you want to use your host's libSDL instead of the minimal libsdl built -# by libsdl2-native then uncomment the ASSUME_PROVIDED line below. -PACKAGECONFIG_append_pn-qemu-native = " sdl" -PACKAGECONFIG_append_pn-nativesdk-qemu = " sdl" -#ASSUME_PROVIDED += "libsdl2-native" - -# CONF_VERSION is increased each time build/conf/ changes incompatibly and is used to -# track the version of this file when it was generated. This can safely be ignored if -# this doesn't mean anything to you. -CONF_VERSION = "1" - -# -# Enable local PR server -# -PRSERV_HOST = "localhost:0" - -# -# Some libraries and packages are covered by NXP EULA -# -#ACCEPT_FSL_EULA = "1"