From 086ea1807dfba53245b98b96d3f41b08bbe195e5 Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Sun, 7 Jan 2018 11:46:35 +0200 Subject: [PATCH 01/37] arm: npcm: add configuration for EVB based NPCM7xx BMC add configuration definition for evaluation board based NPCM7xx (Poleg) BMC Signed-off-by: Tomer Maimon --- arch/arm/configs/PolegSVB_defconfig | 115 ++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 arch/arm/configs/PolegSVB_defconfig diff --git a/arch/arm/configs/PolegSVB_defconfig b/arch/arm/configs/PolegSVB_defconfig new file mode 100644 index 00000000000000..1e31042a5ed6c8 --- /dev/null +++ b/arch/arm/configs/PolegSVB_defconfig @@ -0,0 +1,115 @@ +CONFIG_SYSVIPC=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_LOG_BUF_SHIFT=21 +CONFIG_CGROUPS=y +CONFIG_SYSFS_DEPRECATED=y +CONFIG_SYSFS_DEPRECATED_V2=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_KERNEL_XZ=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_EMBEDDED=y +CONFIG_SLAB=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_DEFAULT_DEADLINE=y +CONFIG_ARCH_NPCM=y +CONFIG_ARCH_NPCM7XX=y +CONFIG_SMP=y +CONFIG_VMSPLIT_3G_OPT=y +CONFIG_AEABI=y +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_BINFMT_MISC=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_MTD=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_RAM=y +CONFIG_MTD_COMPLEX_MAPPINGS=y +CONFIG_MTD_SPI_NOR=y +CONFIG_SPI_NPCM=y +CONFIG_OF_OVERLAY=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_NBD=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=1 +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_NETDEVICES=y +CONFIG_NPCM7XX_EMC_ETH=y +CONFIG_STMMAC_ETH=y +CONFIG_BROADCOM_PHY=y +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_NPCM750_OTP=y +CONFIG_NPCM750_OTP_WRITE_ENABLE=y +CONFIG_NPCM7XX_KCS_IPMI_BMC=y +CONFIG_HW_RANDOM=y +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_NPCM7XX=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_GENERIC_PLATFORM=y +CONFIG_SENSORS_LM75=y +CONFIG_SENSORS_TMP102=y +CONFIG_SENSORS_NPCM7XX=y +CONFIG_WATCHDOG=y +CONFIG_USB_HIDDEV=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_STORAGE=y +CONFIG_USB_CHIPIDEA=y +CONFIG_USB_CHIPIDEA_UDC=y +CONFIG_USB_GADGET=y +CONFIG_USB_GADGET_NPCMX50_USB2=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_EHCI=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB=y +CONFIG_USB_MASS_STORAGE=m +CONFIG_USB_EDM_KBD_MOUSE=m +CONFIG_MMC=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_NPCM750=y +CONFIG_IIO=y +CONFIG_NPCM7XX_ADC=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_ROMFS_FS=y +CONFIG_NFS_FS=y +CONFIG_ROOT_NFS=y +CONFIG_CIFS=y +CONFIG_CIFS_XATTR=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_DYNAMIC_DEBUG=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_INFO_REDUCED=y +CONFIG_READABLE_ASM=y +CONFIG_DEBUG_SECTION_MISMATCH=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_FUNCTION_TRACER=y +CONFIG_EARLY_PRINTK=y +CONFIG_CRYPTO_CBC=y +CONFIG_CRYPTO_CTR=y +CONFIG_CRYPTO_CMAC=y +CONFIG_CRYPTO_CRC32C=y +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_LZO=y +CONFIG_CRYPTO_USER_API_SKCIPHER=y +CONFIG_CRYPTO_DEV_NPCMX50=y +CONFIG_ARM_CRYPTO=y From 2032945998042c608b4fdf316542d7079b91eb43 Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Sun, 7 Jan 2018 11:54:45 +0200 Subject: [PATCH 02/37] arm: dts: add Nuvoton NPCM750 device tree Add full device tree for all Nuvoton NPCM750 BMCs and a board specific device tree for the NPCM750 (Poleg) evaluation board. Based on [v8,2/3] arm: dts: add Nuvoton NPCM750 device tree patch: Brendan Higgins : - https://patchwork.kernel.org/patch/10063497/ Signed-off-by: Tomer Maimon --- .../arm/cpu-enable-method/nuvoton,npcm7xx-smp | 42 + .../devicetree/bindings/arm/npcm/npcm.txt | 6 + arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi | 1158 +++++++++++++ arch/arm/boot/dts/nuvoton-npcm730.dtsi | 46 + arch/arm/boot/dts/nuvoton-npcm750-evb.dts | 467 +++++ arch/arm/boot/dts/nuvoton-npcm750.dtsi | 179 ++ arch/arm/boot/dts/nuvoton-npcm7xx-gpio.dtsi | 1529 +++++++++++++++++ arch/arm/boot/dts/rockaway-npcm730-evb.dts | 454 +++++ 8 files changed, 3881 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/cpu-enable-method/nuvoton,npcm7xx-smp create mode 100644 Documentation/devicetree/bindings/arm/npcm/npcm.txt create mode 100644 arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi create mode 100644 arch/arm/boot/dts/nuvoton-npcm730.dtsi create mode 100644 arch/arm/boot/dts/nuvoton-npcm750-evb.dts create mode 100644 arch/arm/boot/dts/nuvoton-npcm750.dtsi create mode 100644 arch/arm/boot/dts/nuvoton-npcm7xx-gpio.dtsi create mode 100644 arch/arm/boot/dts/rockaway-npcm730-evb.dts diff --git a/Documentation/devicetree/bindings/arm/cpu-enable-method/nuvoton,npcm7xx-smp b/Documentation/devicetree/bindings/arm/cpu-enable-method/nuvoton,npcm7xx-smp new file mode 100644 index 00000000000000..e81f85b400cf48 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/cpu-enable-method/nuvoton,npcm7xx-smp @@ -0,0 +1,42 @@ +========================================================= +Secondary CPU enable-method "nuvoton,npcm7xx-smp" binding +========================================================= + +To apply to all CPUs, a single "nuvoton,npcm7xx-smp" enable method should be +defined in the "cpus" node. + +Enable method name: "nuvoton,npcm7xx-smp" +Compatible machines: "nuvoton,npcm750" +Compatible CPUs: "arm,cortex-a9" +Related properties: (none) + +Note: +This enable method needs valid nodes compatible with "arm,cortex-a9-scu" and +"nuvoton,npcm750-gcr". + +Example: + + cpus { + #address-cells = <1>; + #size-cells = <0>; + enable-method = "nuvoton,npcm7xx-smp"; + + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a9"; + clocks = <&clk NPCM7XX_CLK_CPU>; + clock-names = "clk_cpu"; + reg = <0>; + next-level-cache = <&L2>; + }; + + cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a9"; + clocks = <&clk NPCM7XX_CLK_CPU>; + clock-names = "clk_cpu"; + reg = <1>; + next-level-cache = <&L2>; + }; + }; + diff --git a/Documentation/devicetree/bindings/arm/npcm/npcm.txt b/Documentation/devicetree/bindings/arm/npcm/npcm.txt new file mode 100644 index 00000000000000..2d87d9ecea85b6 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/npcm/npcm.txt @@ -0,0 +1,6 @@ +NPCM Platforms Device Tree Bindings +----------------------------------- +NPCM750 SoC +Required root node properties: + - compatible = "nuvoton,npcm750"; + diff --git a/arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi b/arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi new file mode 100644 index 00000000000000..4dbca114e5cfc7 --- /dev/null +++ b/arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi @@ -0,0 +1,1158 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018 Nuvoton Technology tomer.maimon@nuvoton.com +// Copyright 2018 Google, Inc. + +#include "skeleton.dtsi" +#include +#include + +/ { + #address-cells = <1>; + #size-cells = <1>; + interrupt-parent = <&gic>; + + /* external reference clock */ + clk_refclk: clk_refclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <25000000>; + clock-output-names = "refclk"; + }; + + /* external reference clock for cpu. float in normal operation */ + clk_sysbypck: clk_sysbypck { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <800000000>; + clock-output-names = "sysbypck"; + }; + + /* external reference clock for MC. float in normal operation */ + clk_mcbypck: clk_mcbypck { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <800000000>; + clock-output-names = "mcbypck"; + }; + + /* external clock signal rg1refck, supplied by the phy */ + clk_rg1refck: clk_rg1refck { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <125000000>; + clock-output-names = "clk_rg1refck"; + }; + + /* external clock signal rg2refck, supplied by the phy */ + clk_rg2refck: clk_rg2refck { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <125000000>; + clock-output-names = "clk_rg2refck"; + }; + + clk_xin: clk_xin { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <50000000>; + clock-output-names = "clk_xin"; + }; + + soc { + #address-cells = <1>; + #size-cells = <1>; + compatible = "simple-bus"; + interrupt-parent = <&gic>; + ranges = <0x0 0xf0000000 0x00900000>; + + gcr: gcr@800000 { + compatible = "nuvoton,npcm750-gcr", "syscon", + "simple-mfd"; + reg = <0x800000 0x1000>; + }; + + rst: rst@f0801000 { + compatible = "nuvoton,npcm750-rst", "syscon", + "simple-mfd"; + reg = <0x801000 0x1000>; + }; + + scu: scu@3fe000 { + compatible = "arm,cortex-a9-scu"; + reg = <0x3fe000 0x1000>; + }; + + l2: cache-controller@3fc000 { + compatible = "arm,pl310-cache"; + reg = <0x3fc000 0x1000>; + interrupts = ; + cache-unified; + cache-level = <2>; + clocks = <&clk NPCM7XX_CLK_AXI>; + arm,shared-override; + }; + + gic: interrupt-controller@3ff000 { + compatible = "arm,cortex-a9-gic"; + interrupt-controller; + #interrupt-cells = <3>; + reg = <0x3ff000 0x1000>, + <0x3fe100 0x100>; + }; + }; + + ahb { + #address-cells = <1>; + #size-cells = <1>; + compatible = "simple-bus"; + interrupt-parent = <&gic>; + ranges; + + clk: clock-controller@f0801000 { + compatible = "nuvoton,npcm750-clk", "syscon"; + #clock-cells = <1>; + clock-controller; + reg = <0xf0801000 0x1000>; + clock-names = "refclk", "sysbypck", "mcbypck"; + clocks = <&clk_refclk>, <&clk_sysbypck>, <&clk_mcbypck>; + }; + + gmac0: eth@f0802000 { + device_type = "network"; + compatible = "snps,dwmac"; + reg = <0xf0802000 0x2000>; + interrupts = ; + interrupt-names = "macirq"; + ethernet = <0>; + clocks = <&clk_rg1refck>, <&clk NPCM7XX_CLK_AHB>; + clock-names = "stmmaceth", "clk_gmac"; + pinctrl-names = "default"; + pinctrl-0 = <&rg1_pins + &rg1mdio_pins>; + phy-mode = "rgmii-id"; + status = "disabled"; + }; + + emc0: eth@f0825000 { + device_type = "network"; + compatible = "nuvoton,npcm750-emc"; + reg = <0xf0825000 0x1000>; + interrupts = , + ; + clocks = <&clk NPCM7XX_CLK_AHB>; + clock-names = "clk_emc"; + pinctrl-names = "default"; + pinctrl-0 = <&r1_pins + &r1err_pins + &r1md_pins>; + }; + + ehci1:ehci@f0806000 { + compatible = "nuvoton,npcm750-ehci"; + reg = <0xf0806000 0x1000>; + interrupts = ; + status = "disabled"; + }; + + ohci1: ohci@f0807000 { + compatible = "nuvoton,npcm750-ohci"; + reg = <0xf0807000 0x1000>; + interrupts = ; + status = "disabled"; + }; + + sdhci0: sdhci@f0840000 { + compatible = "nuvoton,npcm750-sdhci"; + status = "disabled"; + reg = <0xf0840000 0x200>; + interrupts = ; + clocks = <&clk NPCM7XX_CLK_AHB>; /*, <&clk_xin>;*/ + clock-names = "clk_sdhc"; /* ,"clk_xin"; */ + pinctrl-names = "default"; + pinctrl-0 = <&sd1_pins>; + }; + + sdhci1: sdhci@f0842000 { + compatible = "nuvoton,npcm750-sdhci"; + status = "disabled"; + reg = <0xf0842000 0x200>; + interrupts = ; + clocks = <&clk NPCM7XX_CLK_AHB>; /*, <&clk_xin>;*/ + clock-names = "clk_mmc"; /* ,"clk_xin"; */ + pinctrl-names = "default"; + pinctrl-0 = <&mmc8_pins + &mmc_pins>; + }; + + aes:aes@f0858000 { + compatible = "nuvoton,npcm750-aes"; + reg = <0xf0858000 0x1000>; + status = "disabled"; + clocks = <&clk NPCM7XX_CLK_AHB>; + clock-names = "clk_ahb"; + }; + + sha:sha@f085a000 { + compatible = "nuvoton,npcm750-sha"; + reg = <0xf085a000 0x1000>; + status = "disabled"; + clocks = <&clk NPCM7XX_CLK_AHB>; + clock-names = "clk_ahb"; + }; + + copr: copr@0 { + compatible = "nuvoton,npcm750-copr"; + interrupts = ; + clocks = <&clk NPCM7XX_CLK_AHB>; + clock-names = "clk_ahb"; + }; + + vdma: vdma@e0800000 { + compatible = "nuvoton,npcm750-vdm"; + reg = <0xe0800000 0x1000 + 0xf0822000 0x1000>; + interrupts = ; + }; + + spi0: spi@fb000000 { + compatible = "nuvoton,npcm750-spi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0xfb000000 0x1000>, <0x80000000 0x10000000>; + reg-names = "control", "memory"; + chip-max-address-map = <0x8000000>; + clocks = <&clk NPCM7XX_CLK_AHB>; + clock-names = "clk_ahb"; + spi-nor@0 { + compatible = "jedec,spi-nor"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0>; + }; + }; + spi3: spi@c0000000 { + compatible = "nuvoton,npcm750-spi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0xc0000000 0x1000>, <0xA0000000 0x20000000>; + reg-names = "control", "memory"; + chip-max-address-map = <0x8000000>; + clocks = <&clk NPCM7XX_CLK_AHB>; + clock-names = "clk_ahb"; + pinctrl-names = "default"; + pinctrl-0 = <&spi3_pins &spi3quad_pins>; + spi-nor@0 { + compatible = "jedec,spi-nor"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0>; + }; + }; + + pci_rc: axi-pcie@E1000000 { + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + compatible = "nuvoton,npcm750-pcirc"; + reg = < 0xE1000000 0x1000 >; + device_type = "pci"; + interrupts = ; + bus-range = <0x00 0xff>; + ranges = <0x02000000 0 0xEA000000 + 0xEA000000 0 0x02000000>; + status = "disabled"; + }; + + dvc: dvc@f0808000 { + compatible = "nuvoton,npcm750-dvc"; + reg = <0xf0808000 0x1000>; + interrupts = <0 23 4>; + }; + + vcd: vcd@0 { + compatible = "nuvoton,npcm750-vcd"; + reg = <0xf0810000 0x10000 + 0xf0820000 0x2000>; + interrupts = , + ; + }; + + pcimbx: pcimbx@f0848000 { + compatible = "nuvoton,npcm750-pcimbx"; + reg = <0xf0848000 0x10000>; + interrupts = ; + }; + + apb { + #address-cells = <1>; + #size-cells = <1>; + compatible = "simple-bus"; + interrupt-parent = <&gic>; + ranges = <0x0 0xf0000000 0x00300000>; + + lpc_kcs: lpc_kcs@7000 { + compatible = "nuvoton,npcm750-lpc-kcs", + "simple-mfd", "syscon"; + reg = <0x7000 0x40>; + reg-io-width = <1>; + + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x7000 0x40>; + + kcs1: kcs1@0 { + compatible = "nuvoton,npcm750-kcs-bmc"; + reg = <0x0 0x40>; + interrupts = ; + kcs_chan = <1>; + status = "disabled"; + }; + + kcs2: kcs2@0 { + compatible = "nuvoton,npcm750-kcs-bmc"; + reg = <0x0 0x40>; + interrupts = ; + kcs_chan = <2>; + status = "disabled"; + }; + + kcs3: kcs3@0 { + compatible = "nuvoton,npcm750-kcs-bmc"; + reg = <0x0 0x40>; + interrupts = ; + kcs_chan = <3>; + status = "disabled"; + }; + }; + + pspi: pspi@0 { + compatible = "nuvoton,npcm750-pspi"; + reg = <0x200000 0x2000>; + interrupts = , + ; + clocks = <&clk NPCM7XX_CLK_APB5>; + clock-names = "clk_apb5"; + }; + + fan: fan@0 { + compatible = "nuvoton,npcm750-fan"; + reg = <0x180000 0x8000>; + interrupts = , + , + , + , + , + , + , + ; + clocks = <&clk NPCM7XX_CLK_APB4>; + clock-names = "clk_apb4"; + }; + + timer0: timer@8000 { + compatible = "nuvoton,npcm750-timer"; + interrupts = ; + reg = <0x8000 0x50>; + clocks = <&clk NPCM7XX_CLK_TIMER>; + }; + + watchdog0: watchdog@801C { + compatible = "nuvoton,npcm750-wdt"; + interrupts = ; + reg = <0x801C 0x4>; + status = "disabled"; + clocks = <&clk NPCM7XX_CLK_TIMER>; + }; + + watchdog1: watchdog@901C { + compatible = "nuvoton,npcm750-wdt"; + interrupts = ; + reg = <0x901C 0x4>; + status = "disabled"; + clocks = <&clk NPCM7XX_CLK_TIMER>; + }; + + watchdog2: watchdog@a01C { + compatible = "nuvoton,npcm750-wdt"; + interrupts = ; + reg = <0xa01C 0x4>; + status = "disabled"; + clocks = <&clk NPCM7XX_CLK_TIMER>; + }; + + serial0: serial@1000 { + compatible = "nuvoton,npcm750-uart"; + reg = <0x1000 0x1000>; + clocks = <&clk NPCM7XX_CLK_UART>; + interrupts = ; + reg-shift = <2>; + status = "disabled"; + }; + + serial1: serial@2000 { + compatible = "nuvoton,npcm750-uart"; + reg = <0x2000 0x1000>; + clocks = <&clk NPCM7XX_CLK_UART>; + interrupts = ; + reg-shift = <2>; + status = "disabled"; + }; + + serial2: serial@3000 { + compatible = "nuvoton,npcm750-uart"; + reg = <0x3000 0x1000>; + clocks = <&clk NPCM7XX_CLK_UART>; + interrupts = ; + reg-shift = <2>; + status = "disabled"; + }; + + serial3: serial@4000 { + compatible = "nuvoton,npcm750-uart"; + reg = <0x4000 0x1000>; + clocks = <&clk NPCM7XX_CLK_UART>; + interrupts = ; + reg-shift = <2>; + status = "disabled"; + }; + + rng: rng@b000 { + compatible = "nuvoton,npcm750-rng"; + reg = <0xb000 0x1000>; + clocks = <&clk NPCM7XX_CLK_APB1>; + clock-names = "clk_apb1"; + status = "disabled"; + }; + + adc: adc@c000 { + compatible = "nuvoton,npcm750-adc"; + reg = <0xc000 0x1000>; + clocks = <&clk NPCM7XX_CLK_ADC>; + clock-names = "clk_adc"; + vref = <2048>; + }; + + otp:otp@189000 { + compatible = "nuvoton,npcm750-otp"; + reg = <0x189000 0x1000 + 0x18a000 0x1000>; + status = "disabled"; + clocks = <&clk NPCM7XX_CLK_APB4>; + clock-names = "clk_apb4"; + }; + + pwm:pwm@103000 { + compatible = "nuvoton,npcm750-pwm"; + reg = <0x103000 0x1000 + 0x104000 0x1000>; + clocks = <&clk NPCM7XX_CLK_APB3>; + clock-names = "clk_apb3"; + }; + + i2c0: i2c-bus@80000 { + reg = <0x80000 0x1000>; + compatible = "nuvoton,npcm750-i2c-bus"; + clocks = <&clk NPCM7XX_CLK_APB2>; + bus-frequency = <100000>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&smb0_pins>; + status = "disabled"; + }; + + i2c1: i2c-bus@81000 { + reg = <0x81000 0x1000>; + compatible = "nuvoton,npcm750-i2c-bus"; + clocks = <&clk NPCM7XX_CLK_APB2>; + bus-frequency = <100000>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&smb1_pins>; + status = "disabled"; + }; + + i2c2: i2c-bus@82000 { + reg = <0x82000 0x1000>; + compatible = "nuvoton,npcm750-i2c-bus"; + clocks = <&clk NPCM7XX_CLK_APB2>; + bus-frequency = <100000>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&smb2_pins>; + status = "disabled"; + }; + + i2c3: i2c-bus@83000 { + reg = <0x83000 0x1000>; + compatible = "nuvoton,npcm750-i2c-bus"; + clocks = <&clk NPCM7XX_CLK_APB2>; + bus-frequency = <100000>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&smb3_pins>; + status = "disabled"; + }; + + i2c4: i2c-bus@84000 { + reg = <0x84000 0x1000>; + compatible = "nuvoton,npcm750-i2c-bus"; + clocks = <&clk NPCM7XX_CLK_APB2>; + bus-frequency = <100000>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&smb4_pins>; + status = "disabled"; + }; + + i2c5: i2c-bus@85000 { + reg = <0x85000 0x1000>; + compatible = "nuvoton,npcm750-i2c-bus"; + clocks = <&clk NPCM7XX_CLK_APB2>; + bus-frequency = <100000>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&smb5_pins>; + status = "disabled"; + }; + + i2c6: i2c-bus@86000 { + reg = <0x86000 0x1000>; + compatible = "nuvoton,npcm750-i2c-bus"; + clocks = <&clk NPCM7XX_CLK_APB2>; + bus-frequency = <100000>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&smb6_pins>; + status = "disabled"; + }; + + i2c7: i2c-bus@87000 { + reg = <0x87000 0x1000>; + compatible = "nuvoton,npcm750-i2c-bus"; + clocks = <&clk NPCM7XX_CLK_APB2>; + bus-frequency = <100000>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&smb7_pins>; + status = "disabled"; + }; + + i2c8: i2c-bus@88000 { + reg = <0x88000 0x1000>; + compatible = "nuvoton,npcm750-i2c-bus"; + clocks = <&clk NPCM7XX_CLK_APB2>; + bus-frequency = <100000>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&smb8_pins>; + status = "disabled"; + }; + + i2c9: i2c-bus@89000 { + reg = <0x89000 0x1000>; + compatible = "nuvoton,npcm750-i2c-bus"; + clocks = <&clk NPCM7XX_CLK_APB2>; + bus-frequency = <100000>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&smb9_pins>; + status = "disabled"; + }; + + i2c10: i2c-bus@8a000 { + reg = <0x8a000 0x1000>; + compatible = "nuvoton,npcm750-i2c-bus"; + clocks = <&clk NPCM7XX_CLK_APB2>; + bus-frequency = <100000>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&smb10_pins>; + status = "disabled"; + }; + + i2c11: i2c-bus@8b000 { + reg = <0x8b000 0x1000>; + compatible = "nuvoton,npcm750-i2c-bus"; + clocks = <&clk NPCM7XX_CLK_APB2>; + bus-frequency = <100000>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&smb11_pins>; + status = "disabled"; + }; + + i2c12: i2c-bus@8c000 { + reg = <0x8c000 0x1000>; + compatible = "nuvoton,npcm750-i2c-bus"; + clocks = <&clk NPCM7XX_CLK_APB2>; + bus-frequency = <100000>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&smb12_pins>; + status = "disabled"; + }; + + i2c13: i2c-bus@8d000 { + reg = <0x8d000 0x1000>; + compatible = "nuvoton,npcm750-i2c-bus"; + clocks = <&clk NPCM7XX_CLK_APB2>; + bus-frequency = <100000>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&smb13_pins>; + status = "disabled"; + }; + + i2c14: i2c-bus@8e000 { + reg = <0x8e000 0x1000>; + compatible = "nuvoton,npcm750-i2c-bus"; + clocks = <&clk NPCM7XX_CLK_APB2>; + bus-frequency = <100000>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&smb14_pins>; + status = "disabled"; + }; + + i2c15: i2c-bus@8f000 { + reg = <0x8f000 0x1000>; + compatible = "nuvoton,npcm750-i2c-bus"; + clocks = <&clk NPCM7XX_CLK_APB2>; + bus-frequency = <100000>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&smb15_pins>; + status = "disabled"; + }; + }; + }; + + pinctrl: pinctrl@0 { + compatible = "nuvoton,npcm7xx-pinctrl", "syscon", "simple-mfd"; + clocks = <&clk NPCM7XX_CLK_APB1>; + clock-names = "clk_apb1"; + ranges = <0 0xf0010000 0x8000>; + status = "okay"; + gpio0: gpio@f0010000 { + gpio-controller; + #gpio-cells = <2>; + reg = <0x0 0x80>; + interrupts = ; + gpio-ranges = <&pinctrl 0 0 32>; + }; + gpio1: gpio@f0011000 { + gpio-controller; + #gpio-cells = <2>; + reg = <0x1000 0x80>; + interrupts = ; + gpio-ranges = <&pinctrl 0 32 32>; + }; + gpio2: gpio@f0012000 { + gpio-controller; + #gpio-cells = <2>; + reg = <0x2000 0x80>; + interrupts = ; + gpio-ranges = <&pinctrl 0 64 32>; + }; + gpio3: gpio@f0013000 { + gpio-controller; + #gpio-cells = <2>; + reg = <0x3000 0x80>; + interrupts = ; + gpio-ranges = <&pinctrl 0 96 32>; + }; + gpio4: gpio@f0014000 { + gpio-controller; + #gpio-cells = <2>; + reg = <0x4000 0x80>; + interrupts = ; + gpio-ranges = <&pinctrl 0 128 32>; + }; + gpio5: gpio@f0015000 { + gpio-controller; + #gpio-cells = <2>; + reg = <0x5000 0x80>; + interrupts = ; + gpio-ranges = <&pinctrl 0 160 32>; + }; + gpio6: gpio@f0016000 { + gpio-controller; + #gpio-cells = <2>; + reg = <0x6000 0x80>; + interrupts = ; + gpio-ranges = <&pinctrl 0 192 32>; + }; + gpio7: gpio@f0017000 { + gpio-controller; + #gpio-cells = <2>; + reg = <0x7000 0x80>; + interrupts = ; + gpio-ranges = <&pinctrl 0 224 32>; + }; + + iox1_pins: iox1_pins { + groups = "iox1"; + function = "iox1"; + }; + iox2_pins: iox2_pins { + groups = "iox2"; + function = "iox2"; + }; + smb1d_pins: smb1d_pins { + groups = "smb1d"; + function = "smb1d"; + }; + smb2d_pins: smb2d_pins { + groups = "smb2d"; + function = "smb2d"; + }; + lkgpo1_pins: lkgpo1_pins { + groups = "lkgpo1"; + function = "lkgpo1"; + }; + lkgpo2_pins: lkgpo2_pins { + groups = "lkgpo2"; + function = "lkgpo2"; + }; + ioxh_pins: ioxh_pins { + groups = "ioxh"; + function = "ioxh"; + }; + gspi_pins: gspi_pins { + groups = "gspi"; + function = "gspi"; + }; + smb5b_pins: smb5b_pins { + groups = "smb5b"; + function = "smb5b"; + }; + smb5c_pins: smb5c_pins { + groups = "smb5c"; + function = "smb5c"; + }; + lkgpo0_pins: lkgpo0_pins { + groups = "lkgpo0"; + function = "lkgpo0"; + }; + pspi2_pins: pspi2_pins { + groups = "pspi2"; + function = "pspi2"; + }; + smb4den_pins: smb4den_pins { + groups = "smb4den"; + function = "smb4den"; + }; + smb4b_pins: smb4b_pins { + groups = "smb4b"; + function = "smb4b"; + }; + smb4c_pins: smb4c_pins { + groups = "smb4c"; + function = "smb4c"; + }; + smb15_pins: smb15_pins { + groups = "smb15"; + function = "smb15"; + }; + smb4d_pins: smb4d_pins { + groups = "smb4d"; + function = "smb4d"; + }; + smb14_pins: smb14_pins { + groups = "smb14"; + function = "smb14"; + }; + smb5_pins: smb5_pins { + groups = "smb5"; + function = "smb5"; + }; + smb4_pins: smb4_pins { + groups = "smb4"; + function = "smb4"; + }; + smb3_pins: smb3_pins { + groups = "smb3"; + function = "smb3"; + }; + spi0cs1_pins: spi0cs1_pins { + groups = "spi0cs1"; + function = "spi0cs1"; + }; + spi0cs2_pins: spi0cs2_pins { + groups = "spi0cs2"; + function = "spi0cs2"; + }; + spi0cs3_pins: spi0cs3_pins { + groups = "spi0cs3"; + function = "spi0cs3"; + }; + smb3c_pins: smb3c_pins { + groups = "smb3c"; + function = "smb3c"; + }; + smb3b_pins: smb3b_pins { + groups = "smb3b"; + function = "smb3b"; + }; + bmcuart0a_pins: bmcuart0a_pins { + groups = "bmcuart0a"; + function = "bmcuart0a"; + }; + uart1_pins: uart1_pins { + groups = "uart1"; + function = "uart1"; + }; + jtag2_pins: jtag2_pins { + groups = "jtag2"; + function = "jtag2"; + }; + bmcuart1_pins: bmcuart1_pins { + groups = "bmcuart1"; + function = "bmcuart1"; + }; + uart2_pins: uart2_pins { + groups = "uart2"; + function = "uart2"; + }; + bmcuart0b_pins: bmcuart0b_pins { + groups = "bmcuart0b"; + function = "bmcuart0b"; + }; + r1err_pins: r1err_pins { + groups = "r1err"; + function = "r1err"; + }; + r1md_pins: r1md_pins { + groups = "r1md"; + function = "r1md"; + }; + smb3d_pins: smb3d_pins { + groups = "smb3d"; + function = "smb3d"; + }; + fanin0_pins: fanin0_pins { + groups = "fanin0"; + function = "fanin0"; + }; + fanin1_pins: fanin1_pins { + groups = "fanin1"; + function = "fanin1"; + }; + fanin2_pins: fanin2_pins { + groups = "fanin2"; + function = "fanin2"; + }; + fanin3_pins: fanin3_pins { + groups = "fanin3"; + function = "fanin3"; + }; + fanin4_pins: fanin4_pins { + groups = "fanin4"; + function = "fanin4"; + }; + fanin5_pins: fanin5_pins { + groups = "fanin5"; + function = "fanin5"; + }; + fanin6_pins: fanin6_pins { + groups = "fanin6"; + function = "fanin6"; + }; + fanin7_pins: fanin7_pins { + groups = "fanin7"; + function = "fanin7"; + }; + fanin8_pins: fanin8_pins { + groups = "fanin8"; + function = "fanin8"; + }; + fanin9_pins: fanin9_pins { + groups = "fanin9"; + function = "fanin9"; + }; + fanin10_pins: fanin10_pins { + groups = "fanin10"; + function = "fanin10"; + }; + fanin11_pins: fanin11_pins { + groups = "fanin11"; + function = "fanin11"; + }; + fanin12_pins: fanin12_pins { + groups = "fanin12"; + function = "fanin12"; + }; + fanin13_pins: fanin13_pins { + groups = "fanin13"; + function = "fanin13"; + }; + fanin14_pins: fanin14_pins { + groups = "fanin14"; + function = "fanin14"; + }; + fanin15_pins: fanin15_pins { + groups = "fanin15"; + function = "fanin15"; + }; + pwm0_pins: pwm0_pins { + groups = "pwm0"; + function = "pwm0"; + }; + pwm1_pins: pwm1_pins { + groups = "pwm1"; + function = "pwm1"; + }; + pwm2_pins: pwm2_pins { + groups = "pwm2"; + function = "pwm2"; + }; + pwm3_pins: pwm3_pins { + groups = "pwm3"; + function = "pwm3"; + }; + r2_pins: r2_pins { + groups = "r2"; + function = "r2"; + }; + r2err_pins: r2err_pins { + groups = "r2err"; + function = "r2err"; + }; + r2md_pins: r2md_pins { + groups = "r2md"; + function = "r2md"; + }; + ga20kbc_pins: ga20kbc_pins { + groups = "ga20kbc"; + function = "ga20kbc"; + }; + smb5d_pins: smb5d_pins { + groups = "smb5d"; + function = "smb5d"; + }; + lpc_pins: lpc_pins { + groups = "lpc"; + function = "lpc"; + }; + espi_pins: espi_pins { + groups = "espi"; + function = "espi"; + }; + rg1_pins: rg1_pins { + groups = "rg1"; + function = "rg1"; + }; + rg1mdio_pins: rg1mdio_pins { + groups = "rg1mdio"; + function = "rg1mdio"; + }; + rg2_pins: rg2_pins { + groups = "rg2"; + function = "rg2"; + }; + ddr_pins: ddr_pins { + groups = "ddr"; + function = "ddr"; + }; + smb0_pins: smb0_pins { + groups = "smb0"; + function = "smb0"; + }; + smb1_pins: smb1_pins { + groups = "smb1"; + function = "smb1"; + }; + smb2_pins: smb2_pins { + groups = "smb2"; + function = "smb2"; + }; + smb2c_pins: smb2c_pins { + groups = "smb2c"; + function = "smb2c"; + }; + smb2b_pins: smb2b_pins { + groups = "smb2b"; + function = "smb2b"; + }; + smb1c_pins: smb1c_pins { + groups = "smb1c"; + function = "smb1c"; + }; + smb1b_pins: smb1b_pins { + groups = "smb1b"; + function = "smb1b"; + }; + smb8_pins: smb8_pins { + groups = "smb8"; + function = "smb8"; + }; + smb9_pins: smb9_pins { + groups = "smb9"; + function = "smb9"; + }; + smb10_pins: smb10_pins { + groups = "smb10"; + function = "smb10"; + }; + smb11_pins: smb11_pins { + groups = "smb11"; + function = "smb11"; + }; + sd1_pins: sd1_pins { + groups = "sd1"; + function = "sd1"; + }; + sd1pwr_pins: sd1pwr_pins { + groups = "sd1pwr"; + function = "sd1pwr"; + }; + pwm4_pins: pwm4_pins { + groups = "pwm4"; + function = "pwm4"; + }; + pwm5_pins: pwm5_pins { + groups = "pwm5"; + function = "pwm5"; + }; + pwm6_pins: pwm6_pins { + groups = "pwm6"; + function = "pwm6"; + }; + pwm7_pins: pwm7_pins { + groups = "pwm7"; + function = "pwm7"; + }; + mmc8_pins: mmc8_pins { + groups = "mmc8"; + function = "mmc8"; + }; + mmc_pins: mmc_pins { + groups = "mmc"; + function = "mmc"; + }; + mmcwp_pins: mmcwp_pins { + groups = "mmcwp"; + function = "mmcwp"; + }; + mmccd_pins: mmccd_pins { + groups = "mmccd"; + function = "mmccd"; + }; + mmcrst_pins: mmcrst_pins { + groups = "mmcrst"; + function = "mmcrst"; + }; + clkout_pins: clkout_pins { + groups = "clkout"; + function = "clkout"; + }; + serirq_pins: serirq_pins { + groups = "serirq"; + function = "serirq"; + }; + scipme_pins: scipme_pins { + groups = "scipme"; + function = "scipme"; + }; + sci_pins: sci_pins { + groups = "sci"; + function = "sci"; + }; + smb6_pins: smb6_pins { + groups = "smb6"; + function = "smb6"; + }; + smb7_pins: smb7_pins { + groups = "smb7"; + function = "smb7"; + }; + pspi1_pins: pspi1_pins { + groups = "pspi1"; + function = "pspi1"; + }; + faninx_pins: faninx_pins { + groups = "faninx"; + function = "faninx"; + }; + r1_pins: r1_pins { + groups = "r1"; + function = "r1"; + }; + spi3_pins: spi3_pins { + groups = "spi3"; + function = "spi3"; + }; + spi3cs1_pins: spi3cs1_pins { + groups = "spi3cs1"; + function = "spi3cs1"; + }; + spi3quad_pins: spi3quad_pins { + groups = "spi3quad"; + function = "spi3quad"; + }; + spi3cs2_pins: spi3cs2_pins { + groups = "spi3cs2"; + function = "spi3cs2"; + }; + spi3cs3_pins: spi3cs3_pins { + groups = "spi3cs3"; + function = "spi3cs3"; + }; + nprd_smi_pins: nprd_smi_pins { + groups = "nprd_smi"; + function = "nprd_smi"; + }; + smb0b_pins: smb0b_pins { + groups = "smb0b"; + function = "smb0b"; + }; + smb0c_pins: smb0c_pins { + groups = "smb0c"; + function = "smb0c"; + }; + smb0den_pins: smb0den_pins { + groups = "smb0den"; + function = "smb0den"; + }; + smb0d_pins: smb0d_pins { + groups = "smb0d"; + function = "smb0d"; + }; + ddc_pins: ddc_pins { + groups = "ddc"; + function = "ddc"; + }; + rg2mdio_pins: rg2mdio_pins { + groups = "rg2mdio"; + function = "rg2mdio"; + }; + wdog1_pins: wdog1_pins { + groups = "wdog1"; + function = "wdog1"; + }; + wdog2_pins: wdog2_pins { + groups = "wdog2"; + function = "wdog2"; + }; + smb12_pins: smb12_pins { + groups = "smb12"; + function = "smb12"; + }; + smb13_pins: smb13_pins { + groups = "smb13"; + function = "smb13"; + }; + spix_pins: spix_pins { + groups = "spix"; + function = "spix"; + }; + spixcs1_pins: spixcs1_pins { + groups = "spixcs1"; + function = "spixcs1"; + }; + clkreq_pins: clkreq_pins { + groups = "clkreq"; + function = "clkreq"; + }; + }; +}; diff --git a/arch/arm/boot/dts/nuvoton-npcm730.dtsi b/arch/arm/boot/dts/nuvoton-npcm730.dtsi new file mode 100644 index 00000000000000..ae32d6a1d5fc33 --- /dev/null +++ b/arch/arm/boot/dts/nuvoton-npcm730.dtsi @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018 Nuvoton Technology tomer.maimon@nuvoton.com +// Copyright 2018 Google, Inc. + +#include "nuvoton-common-npcm7xx.dtsi" +#include "nuvoton-npcm7xx-gpio.dtsi" + +/ { + #address-cells = <1>; + #size-cells = <1>; + interrupt-parent = <&gic>; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + enable-method = "nuvoton,npcm750-smp"; + + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a9"; + clocks = <&clk NPCM7XX_CLK_CPU>; + clock-names = "clk_cpu"; + reg = <0>; + next-level-cache = <&l2>; + }; + + cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a9"; + clocks = <&clk NPCM7XX_CLK_CPU>; + clock-names = "clk_cpu"; + reg = <1>; + next-level-cache = <&l2>; + }; + }; + + soc { + timer@3fe600 { + compatible = "arm,cortex-a9-twd-timer"; + reg = <0x3fe600 0x20>; + interrupts = ; + clocks = <&clk NPCM7XX_CLK_AHB>; + }; + }; +}; diff --git a/arch/arm/boot/dts/nuvoton-npcm750-evb.dts b/arch/arm/boot/dts/nuvoton-npcm750-evb.dts new file mode 100644 index 00000000000000..d263cf7c60009d --- /dev/null +++ b/arch/arm/boot/dts/nuvoton-npcm750-evb.dts @@ -0,0 +1,467 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018 Nuvoton Technology tomer.maimon@nuvoton.com +// Copyright 2018 Google, Inc. + +/dts-v1/; +#include "nuvoton-npcm750.dtsi" + +/ { + model = "Nuvoton npcm750 Development Board (Device Tree)"; + compatible = "nuvoton,npcm750"; + + aliases { + ethernet0 = &emc0; + ethernet1 = &emc1; + ethernet2 = &gmac0; + ethernet3 = &gmac1; + serial0 = &serial0; + serial1 = &serial1; + serial2 = &serial2; + serial3 = &serial3; + udc0 = &udc0; + udc1 = &udc1; + udc2 = &udc2; + udc3 = &udc3; + udc4 = &udc4; + udc5 = &udc5; + udc6 = &udc6; + udc7 = &udc7; + udc8 = &udc8; + udc9 = &udc9; + emmc0 = &sdhci0; + emmc1 = &sdhci1; + i2c0 = &i2c0; + i2c1 = &i2c1; + i2c2 = &i2c2; + i2c3 = &i2c3; + i2c4 = &i2c4; + i2c5 = &i2c5; + i2c6 = &i2c6; + i2c7 = &i2c7; + i2c8 = &i2c8; + i2c9 = &i2c9; + i2c10 = &i2c10; + i2c11 = &i2c11; + i2c12 = &i2c12; + i2c13 = &i2c13; + i2c14 = &i2c14; + i2c15 = &i2c15; + }; + + chosen { + stdout-path = &serial3; + }; + + memory { + reg = <0 0x40000000>; + }; + + ahb { + gmac0: eth@f0802000 { + status = "okay"; + }; + + gmac1: eth@f0804000 { + status = "okay"; + }; + + emc0: eth@f0825000 { + phy-mode = "rmii"; + #use-ncsi; /* add this to support ncsi */ + status = "okay"; + }; + + emc1: eth@f0826000 { + phy-mode = "rmii"; + #use-ncsi; /* add this to support ncsi */ + status = "okay"; + }; + + ehci1: ehci@f0806000 { + status = "okay"; + }; + + ohci1: ohci@f0807000 { + status = "okay"; + }; + + udc0:udc@f0830000 { + status = "okay"; + }; + + udc1:udc@f0831000 { + status = "okay"; + }; + + udc2:udc@f0832000 { + status = "okay"; + }; + + udc3:udc@f0833000 { + status = "okay"; + }; + + udc4:udc@f0834000 { + status = "okay"; + }; + + udc5:udc@f0835000 { + status = "okay"; + }; + + udc6:udc@f0836000 { + status = "okay"; + }; + + udc7:udc@f0837000 { + status = "okay"; + }; + + udc8:udc@f0838000 { + status = "okay"; + }; + + udc9:udc@f0839000 { + status = "okay"; + }; + + aes:aes@f0858000 { + status = "okay"; + }; + + sha:sha@f085a000 { + status = "okay"; + }; + + spi0: spi@fb000000 { + spi-nor@0 { + partitions@80000000 { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + bbuboot1@0 { + label = "bb-uboot-1"; + reg = <0x0000000 0x80000>; + read-only; + }; + bbuboot2@80000 { + label = "bb-uboot-2"; + reg = <0x0080000 0x80000>; + read-only; + }; + envparam@100000 { + label = "env-param"; + reg = <0x0100000 0x40000>; + read-only; + }; + spare@140000 { + label = "spare"; + reg = <0x0140000 0xC0000>; + }; + kernel@200000 { + label = "kernel"; + reg = <0x0200000 0x400000>; + }; + rootfs@600000 { + label = "rootfs"; + reg = <0x0600000 0x700000>; + }; + spare1@D00000 { + label = "spare1"; + reg = <0x0D00000 0x200000>; + }; + spare2@0F00000 { + label = "spare2"; + reg = <0x0F00000 0x200000>; + }; + spare3@1100000 { + label = "spare3"; + reg = <0x1100000 0x200000>; + }; + spare4@1300000 { + label = "spare4"; + reg = <0x1300000 0x0>; + }; + }; + }; + }; + + spi3: spi@c0000000 { + spi-nor@0 { + partitions@A0000000 { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + system1@0 { + label = "spi3-system1"; + reg = <0x0 0x800000>; + }; + system2@800000 { + label = "spi3-system2"; + reg = <0x800000 0x0>; + }; + }; + }; + }; + + sdhci0: sdhci@f0840000 { + status = "okay"; + }; + + sdhci1: sdhci@f0842000 { + status = "okay"; + }; + + apb { + + watchdog1: watchdog@901C { + status = "okay"; + }; + + rng: rng@b000 { + status = "okay"; + }; + + serial0: serial@1000 { + status = "okay"; + }; + + serial1: serial@2000 { + status = "okay"; + }; + + serial2: serial@3000 { + status = "okay"; + }; + + serial3: serial@4000 { + status = "okay"; + }; + + otp:otp@189000 { + status = "okay"; + }; + + lpc_kcs: lpc_kcs@7000 { + kcs1: kcs1@0 { + status = "okay"; + }; + + kcs2: kcs2@0 { + status = "okay"; + }; + + kcs3: kcs3@0 { + status = "okay"; + }; + }; + + /* lm75 on SVB */ + i2c0: i2c-bus@80000 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + lm75@48 { + compatible = "lm75"; + reg = <0x48>; + status = "okay"; + }; + }; + + /* lm75 on EB */ + i2c1: i2c-bus@81000 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + lm75@48 { + compatible = "lm75"; + reg = <0x48>; + status = "okay"; + }; + }; + + /* tmp100 on EB */ + i2c2: i2c-bus@82000 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + tmp100@48 { + compatible = "tmp100"; + reg = <0x48>; + status = "okay"; + }; + }; + + /* tmp100 on SVB */ + i2c6: i2c-bus@86000 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + tmp100@48 { + compatible = "tmp100"; + reg = <0x48>; + status = "okay"; + }; + }; + i2c3: i2c-bus@83000 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + }; + + i2c4: i2c-bus@84000 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + }; + + i2c5: i2c-bus@85000 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + }; + + i2c7: i2c-bus@87000 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + }; + + i2c8: i2c-bus@88000 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + }; + + i2c9: i2c-bus@89000 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + }; + + i2c10: i2c-bus@8a000 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + }; + + i2c11: i2c-bus@8b000 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + }; + + i2c14: i2c-bus@8e000 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + }; + + i2c15: i2c-bus@8f000 { + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; /* SVB conflict with pspi2 cs gpio20o_pins */ + }; + + fan: fan@0 { + pinctrl-names = "default"; + pinctrl-0 = ; + status = "okay"; + }; + + pwm: pwm@103000 { + pinctrl-names = "default"; + pinctrl-0 = < &pwm0_pins + &pwm1_pins + &pwm2_pins + &pwm3_pins + &pwm4_pins + &pwm5_pins + &pwm6_pins + &pwm7_pins>; + status = "okay"; + }; + + /* Here is an example for future pspi binding */ + /* + pspi: pspi@0 { + pinctrl-names = "default"; + pinctrl-0 = <&pspi1_pins &pspi2_pins &gpio20o_pins &gpio203o_pins> ; + cs-gpios = <&gpio 20 1>, <&gpio 203 1>; + status = "okay"; + }; + */ + }; + }; + + pinctrl: pinctrl@0 { + pinctrl-names = "default"; + pinctrl-0 = < &iox1_pins + &gpio8_pins + &gpio9o_pins + &gpio10_pins + &gpio11o_pins + &gpio16_pins + &gpio24o_pins + &gpio25ol_pins + &gpio32o_pins + &jtag2_pins + &gpio61o_pins + &gpio62o_pins + &gpio63o_pins + &gpio64o_pins /* SVB pspi1 enable */ + &gpio80_pins + &gpio81_pins + &gpio82_pins + &gpio83_pins + &lpc_pins + &gpio132o_pins + &gpio133_pins + &gpio134_pins + &gpio135_pins + &gpio144_pins + &gpio145_pins + &gpio146_pins + &gpio147_pins + &gpio160_pins + &gpio162_pins + &gpio168_pins + &gpio169_pins + &gpio170_pins + &gpio187o_pins + &gpio190_pins + &gpio191o_pins + &gpio192o_pins + &gpio197ol_pins + &ddc_pins + &gpio218_pins + &gpio219ol_pins + &gpio220ol_pins + &gpio221o_pins + &gpio222_pins + &gpio223ol_pins + &spix_pins + &gpio228ol_pins + &gpio231o_pins + &gpio255_pins>; + }; +}; diff --git a/arch/arm/boot/dts/nuvoton-npcm750.dtsi b/arch/arm/boot/dts/nuvoton-npcm750.dtsi new file mode 100644 index 00000000000000..56496dc587122a --- /dev/null +++ b/arch/arm/boot/dts/nuvoton-npcm750.dtsi @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018 Nuvoton Technology tomer.maimon@nuvoton.com +// Copyright 2018 Google, Inc. + +#include "nuvoton-common-npcm7xx.dtsi" +#include "nuvoton-npcm7xx-gpio.dtsi" + +/ { + #address-cells = <1>; + #size-cells = <1>; + interrupt-parent = <&gic>; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + enable-method = "nuvoton,npcm750-smp"; + + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a9"; + clocks = <&clk NPCM7XX_CLK_CPU>; + clock-names = "clk_cpu"; + reg = <0>; + next-level-cache = <&l2>; + }; + + cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a9"; + clocks = <&clk NPCM7XX_CLK_CPU>; + clock-names = "clk_cpu"; + reg = <1>; + next-level-cache = <&l2>; + }; + }; + + soc { + timer@3fe600 { + compatible = "arm,cortex-a9-twd-timer"; + reg = <0x3fe600 0x20>; + interrupts = ; + clocks = <&clk NPCM7XX_CLK_AHB>; + }; + }; + + + ahb { + gmac1: eth@f0804000 { + device_type = "network"; + compatible = "snps,dwmac"; + reg = <0xf0804000 0x2000>; + interrupts = ; + interrupt-names = "macirq"; + ethernet = <1>; + clocks = <&clk_rg2refck>, <&clk NPCM7XX_CLK_AHB>; + clock-names = "stmmaceth", "clk_gmac"; + pinctrl-names = "default"; + pinctrl-0 = <&rg2_pins + &rg2mdio_pins>; + phy-mode = "rgmii-id"; + status = "disabled"; + }; + + emc1: eth@f0826000 { + device_type = "network"; + compatible = "nuvoton,npcm750-emc"; + reg = <0xf0826000 0x1000>; + interrupts = , + ; + clocks = <&clk NPCM7XX_CLK_AHB>; + clock-names = "clk_emc"; + pinctrl-names = "default"; + pinctrl-0 = <&r2_pins + &r2err_pins + &r2md_pins>; + }; + + udc0:udc@f0830000 { + compatible = "nuvoton,npcm750-udc"; + reg = <0xf0830000 0x1000 + 0xfffd0000 0x800>; + interrupts = ; + status = "disabled"; + clocks = <&clk NPCM7XX_CLK_SU>; + clock-names = "clk_usb_bridge"; + }; + + udc1:udc@f0831000 { + compatible = "nuvoton,npcm750-udc"; + reg = <0xf0831000 0x1000 + 0xfffd0800 0x800>; + interrupts = ; + status = "disabled"; + clocks = <&clk NPCM7XX_CLK_SU>; + clock-names = "clk_usb_bridge"; + }; + + udc2:udc@f0832000 { + compatible = "nuvoton,npcm750-udc"; + reg = <0xf0832000 0x1000 + 0xfffd1000 0x800>; + interrupts = ; + status = "disabled"; + clocks = <&clk NPCM7XX_CLK_SU>; + clock-names = "clk_usb_bridge"; + }; + + udc3:udc@f0833000 { + compatible = "nuvoton,npcm750-udc"; + reg = <0xf0833000 0x1000 + 0xfffd1800 0x800>; + interrupts = ; + status = "disabled"; + clocks = <&clk NPCM7XX_CLK_SU>; + clock-names = "clk_usb_bridge"; + }; + + udc4:udc@f0834000 { + compatible = "nuvoton,npcm750-udc"; + reg = <0xf0834000 0x1000 + 0xfffd2000 0x800>; + interrupts = ; + status = "disabled"; + clocks = <&clk NPCM7XX_CLK_SU>; + clock-names = "clk_usb_bridge"; + }; + + udc5:udc@f0835000 { + compatible = "nuvoton,npcm750-udc"; + reg = <0xf0835000 0x1000 + 0xfffd2800 0x800>; + interrupts = ; + status = "disabled"; + clocks = <&clk NPCM7XX_CLK_SU>; + clock-names = "clk_usb_bridge"; + }; + + udc6:udc@f0836000 { + compatible = "nuvoton,npcm750-udc"; + reg = <0xf0836000 0x1000 + 0xfffd3000 0x800>; + interrupts = ; + status = "disabled"; + clocks = <&clk NPCM7XX_CLK_SU>; + clock-names = "clk_usb_bridge"; + }; + + udc7:udc@f0837000 { + compatible = "nuvoton,npcm750-udc"; + reg = <0xf0837000 0x1000 + 0xfffd3800 0x800>; + interrupts = ; + status = "disabled"; + clocks = <&clk NPCM7XX_CLK_SU>; + clock-names = "clk_usb_bridge"; + }; + + udc8:udc@f0838000 { + compatible = "nuvoton,npcm750-udc"; + reg = <0xf0838000 0x1000 + 0xfffd4000 0x800>; + interrupts = ; + status = "disabled"; + clocks = <&clk NPCM7XX_CLK_SU>; + clock-names = "clk_usb_bridge"; + }; + + udc9:udc@f0839000 { + compatible = "nuvoton,npcm750-udc"; + reg = <0xf0839000 0x1000 + 0xfffd4800 0x800>; + interrupts = ; + status = "disabled"; + clocks = <&clk NPCM7XX_CLK_SU>; + clock-names = "clk_usb_bridge"; + }; + }; +}; diff --git a/arch/arm/boot/dts/nuvoton-npcm7xx-gpio.dtsi b/arch/arm/boot/dts/nuvoton-npcm7xx-gpio.dtsi new file mode 100644 index 00000000000000..567fbe1892e227 --- /dev/null +++ b/arch/arm/boot/dts/nuvoton-npcm7xx-gpio.dtsi @@ -0,0 +1,1529 @@ +/* + * DTSi file for the NPCM750 pin controller + * + * Copyright (c) 2014-2017 Nuvoton Technology corporation. + * + * Released under the GPLv2 only. + * SPDX-License-Identifier: GPL-2.0 + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + + +/ { + pinctrl: pinctrl@0 { + gpio0o_pins: gpio0o_pins { + pins = "GPIO0/IOX1DI"; + output-high; + }; + gpio1_pins: gpio1_pins { + pins = "GPIO1/IOX1LD"; + input-enable; + }; + gpio2_pins: gpio2_pins { + pins = "GPIO2/IOX1CK"; + input-enable; + }; + gpio2o_pins: gpio2o_pins { + pins = "GPIO2/IOX1CK"; + output_high; + }; + gpio3_pins: gpio3_pins { + pins = "GPIO3/IOX1D0"; + input-enable; + }; + gpio3o_pins: gpio3o_pins { + pins = "GPIO3/IOX1D0"; + output-high; + }; + gpio4_pins: gpio4_pins { + pins = "GPIO4/IOX2DI/SMB1DSDA"; + input-enable; + }; + gpio5_pins: gpio5_pins { + pins = "GPIO5/IOX2LD/SMB1DSCL"; + input-enable; + }; + gpio6_pins: gpio6_pins { + pins = "GPIO6/IOX2CK/SMB2DSDA"; + input-enable; + }; + gpio6o_pins: gpio6o_pins { + pins = "GPIO6/IOX2CK/SMB2DSDA"; + output-high; + }; + gpio6ol_pins: gpio6ol_pins { + pins = "GPIO6/IOX2CK/SMB2DSDA"; + output-low; + }; + gpio7_pins: gpio7_pins { + pins = "GPIO7/IOX2D0/SMB2DSCL"; + input-enable; + }; + gpio7o_pins: gpio7o_pins { + pins = "GPIO7/IOX2D0/SMB2DSCL"; + output-high; + }; + gpio7ol_pins: gpio7ol_pins { + pins = "GPIO7/IOX2D0/SMB2DSCL"; + output-low; + }; + gpio8_pins: gpio8_pins { + pins = "GPIO8/LKGPO1"; + input-enable; + }; + gpio8ol_pins: gpio8ol_pins { + pins = "GPIO8/LKGPO1"; + output-low; + }; + gpio9_pins: gpio9_pins { + pins = "GPIO9/LKGPO2"; + input-enable; + }; + gpio9o_pins: gpio9o_pins { + pins = "GPIO9/LKGPO2"; + output-high; + }; + gpio9ol_pins: gpio9ol_pins { + pins = "GPIO9/LKGPO2"; + output-low; + }; + gpio10_pins: gpio10_pins { + pins = "GPIO10/IOXHLD"; + input-enable; + }; + gpio10ol_pins: gpio10ol_pins { + pins = "GPIO10/IOXHLD"; + output-low; + }; + gpio11_pins: gpio11_pins { + pins = "GPIO11/IOXHCK"; + input-enable; + }; + gpio11o_pins: gpio11o_pins { + pins = "GPIO11/IOXHCK"; + output-high; + }; + gpio11ol_pins: gpio11ol_pins { + pins = "GPIO11/IOXHCK"; + output-low; + }; + gpio12_pins: gpio12_pins { + pins = "GPIO12/GSPICK/SMB5BSCL"; + input-enable; + }; + gpio12o_pins: gpio12o_pins { + pins = "GPIO12/GSPICK/SMB5BSCL"; + output-high; + }; + gpio13_pins: gpio13_pins { + pins = "GPIO13/GSPIDO/SMB5BSDA"; + input-enable; + }; + gpio13ol_pins: gpio13ol_pins { + pins = "GPIO13/GSPIDO/SMB5BSDA"; + output-low; + }; + gpio14_pins: gpio14_pins { + pins = "GPIO14/GSPIDI/SMB5CSCL"; + input-enable; + }; + gpio14ol_pins: gpio14ol_pins { + pins = "GPIO14/GSPIDI/SMB5CSCL"; + output-low; + }; + gpio15_pins: gpio15_pins { + pins = "GPIO15/GSPICS/SMB5CSDA"; + input-enable; + }; + gpio15o_pins: gpio15o_pins { + pins = "GPIO15/GSPICS/SMB5CSDA"; + output-high; + }; + gpio16_pins: gpio16_pins { + pins = "GPIO16/LKGPO0"; + input-enable; + }; + gpio16o_pins: gpio16o_pins { + pins = "GPIO16/LKGPO0"; + output-high; + }; + gpio16ol_pins: gpio16ol_pins { + pins = "GPIO16/LKGPO0"; + output-low; + }; + gpio17_pins: gpio17_pins { + pins = "GPIO17/PSPI2DI/SMB4DEN"; + input-enable; + }; + gpio17o_pins: gpio17o_pins { + pins = "GPIO17/PSPI2DI/SMB4DEN"; + output-high; + }; + gpio17ol_pins: gpio17ol_pins { + pins = "GPIO17/PSPI2DI/SMB4DEN"; + output-low; + }; + gpio18_pins: gpio18_pins { + pins = "GPIO18/PSPI2D0/SMB4BSDA"; + input-enable; + }; + gpio18ol_pins: gpio18ol_pins { + pins = "GPIO18/PSPI2D0/SMB4BSDA"; + output-low; + }; + gpio19_pins: gpio19_pins { + pins = "GPIO19/PSPI2CK/SMB4BSCL"; + input-enable; + }; + gpio19ol_pins: gpio19ol_pins { + pins = "GPIO19/PSPI2CK/SMB4BSCL"; + output-low; + }; + gpio20_pins: gpio20_pins { + pins = "GPIO20/SMB4CSDA/SMB15SDA"; + input-enable; + }; + gpio20o_pins: gpio20o_pins { + pins = "GPIO20/SMB4CSDA/SMB15SDA"; + output-high; + }; + gpio20ol_pins: gpio20ol_pins { + pins = "GPIO20/SMB4CSDA/SMB15SDA"; + output-low; + }; + gpio21_pins: gpio21_pins { + pins = "GPIO21/SMB4CSCL/SMB15SCL"; + input-enable; + }; + gpio21ol_pins: gpio21ol_pins { + pins = "GPIO21/SMB4CSCL/SMB15SCL"; + output-low; + }; + gpio22_pins: gpio22_pins { + pins = "GPIO22/SMB4DSDA/SMB14SDA"; + input-enable; + }; + gpio22ol_pins: gpio22ol_pins { + pins = "GPIO22/SMB4DSDA/SMB14SDA"; + output-low; + }; + gpio23_pins: gpio23_pins { + pins = "GPIO23/SMB4DSCL/SMB14SCL"; + input-enable; + }; + gpio23ol_pins: gpio23ol_pins { + pins = "GPIO23/SMB4DSCL/SMB14SCL"; + output-low; + }; + gpio24_pins: gpio24_pins { + pins = "GPIO24/IOXHDO"; + input-enable; + }; + gpio24o_pins: gpio24o_pins { + pins = "GPIO24/IOXHDO"; + output-high; + }; + gpio24ol_pins: gpio24ol_pins { + pins = "GPIO24/IOXHDO"; + output-low; + }; + gpio25_pins: gpio25_pins { + pins = "GPIO25/IOXHDI"; + input-enable; + }; + gpio25o_pins: gpio25o_pins { + pins = "GPIO25/IOXHDI"; + output-high; + }; + gpio25ol_pins: gpio25ol_pins { + pins = "GPIO25/IOXHDI"; + output-low; + }; + gpio26_pins: gpio26_pins { + pins = "GPIO26/SMB5SDA"; + input-enable; + }; + gpio27_pins: gpio27_pins { + pins = "GPIO27/SMB5SCL"; + input-enable; + }; + gpio32_pins: gpio32_pins { + pins = "GPIO32/nSPI0CS1"; + input-enable; + }; + gpio32o_pins: gpio32o_pins { + pins = "GPIO32/nSPI0CS1"; + output-high; + }; + gpio32ol_pins: gpio32ol_pins { + pins = "GPIO32/nSPI0CS1"; + output-low; + }; + gpio37_pins: gpio37_pins { + pins = "GPIO37/SMB3CSDA"; + input-enable; + }; + gpio37ol_pins: gpio37ol_pins { + pins = "GPIO37/SMB3CSDA"; + output-low; + }; + gpio38_pins: gpio38_pins { + pins = "GPIO38/SMB3CSCL"; + input-enable; + }; + gpio38ol_pins: gpio38ol_pins { + pins = "GPIO38/SMB3CSCL"; + output-low; + }; + gpio39_pins: gpio39_pins { + pins = "GPIO39/SMB3BSDA"; + input-enable; + }; + gpio39ol_pins: gpio39ol_pins { + pins = "GPIO39/SMB3BSDA"; + output-low; + }; + gpio40_pins: gpio40_pins { + pins = "GPIO40/SMB3BSCL"; + input-enable; + }; + gpio40ol_pins: gpio40ol_pins { + pins = "GPIO40/SMB3BSCL"; + output-low; + }; + gpio41_pins: gpio41_pins { + pins = "GPIO41/BSPRXD"; + input-enable; + }; + gpio42_pins: gpio42_pins { + pins = "GPO42/BSPTXD/STRAP11"; + input-enable; + }; + gpio43_pins: gpio43_pins { + pins = "GPIO43/RXD1/JTMS2/BU1RXD"; + input-enable; + }; + gpio44_pins: gpio44_pins { + pins = "GPIO44/nCTS1/JTDI2/BU1CTS"; + input-enable; + }; + gpio45_pins: gpio45_pins { + pins = "GPIO45/nDCD1/JTDO2"; + input-enable; + }; + gpio46_pins: gpio46_pins { + pins = "GPIO46/nDSR1/JTCK2"; + input-enable; + }; + gpio47_pins: gpio47_pins { + pins = "GPIO47/nRI1/JCP_RDY2"; + input-enable; + }; + gpio48_pins: gpio48_pins { + pins = "GPIO48/TXD2/BSPTXD"; + input-enable; + }; + gpio49_pins: gpio49_pins { + pins = "GPIO49/RXD2/BSPRXD"; + input-enable; + }; + gpio50_pins: gpio50_pins { + pins = "GPIO50/nCTS2"; + input-enable; + }; + gpio50ol_pins: gpio50ol_pins { + pins = "GPIO50/nCTS2"; + output-low; + }; + gpio51_pins: gpio51_pins { + pins = "GPO51/nRTS2/STRAP2"; + input-enable; + }; + gpio51o_pins: gpio51o_pins { + pins = "GPO51/nRTS2/STRAP2"; + output-high; + }; + gpio52_pins: gpio52_pins { + pins = "GPIO52/nDCD2"; + input-enable; + }; + gpio52ol_pins: gpio52ol_pins { + pins = "GPIO52/nDCD2"; + output-low; + }; + gpio53_pins: gpio53_pins { + pins = "GPIO53/nDTR2_BOUT2/STRAP1"; + input-enable; + }; + gpio53o_pins: gpio53o_pins { + pins = "GPIO53/nDTR2_BOUT2/STRAP1"; + output-high; + }; + gpio54_pins: gpio54_pins { + pins = "GPIO54/nDSR2"; + input-enable; + }; + gpio54ol_pins: gpio54ol_pins { + pins = "GPIO54/nDSR2"; + output-low; + }; + gpio55_pins: gpio55_pins { + pins = "GPIO55/nRI2"; + input-enable; + }; + gpio55ol_pins: gpio55ol_pins { + pins = "GPIO55/nRI2"; + output-low; + }; + gpio57_pins: gpio57_pins { + pins = "GPIO57/R1MDC"; + input-enable; + }; + gpio57ol_pins: gpio57ol_pins { + pins = "GPIO57/R1MDC"; + output-low; + }; + gpio58_pins: gpio58_pins { + pins = "GPIO58/R1MDIO"; + input-enable; + }; + gpio58ol_pins: gpio58ol_pins { + pins = "GPIO58/R1MDIO"; + output-low; + }; + gpio59_pins: gpio59_pins { + pins = "GPIO59/SMB3DSDA"; + input-enable; + }; + gpio59o_pins: gpio59o_pins { + pins = "GPIO59/SMB3DSDA"; + output-high; + }; + gpio59ol_pins: gpio59ol_pins { + pins = "GPIO59/SMB3DSDA"; + output-low; + }; + gpio60_pins: gpio60_pins { + pins = "GPIO60/SMB3DSCL"; + input-enable; + }; + gpio60o_pins: gpio60o_pins { + pins = "GPIO60/SMB3DSCL"; + output-high; + }; + gpio60ol_pins: gpio60ol_pins { + pins = "GPIO60/SMB3DSCL"; + output-low; + }; + gpio61_pins: gpio61_pins { + pins = "GPO61/nDTR1_BOUT1/STRAP6"; + input-enable; + }; + gpio61o_pins: gpio61o_pins { + pins = "GPO61/nDTR1_BOUT1/STRAP6"; + output-high; + }; + gpio62_pins: gpio62_pins { + pins = "GPO62/nRTST1/STRAP5"; + input-enable; + }; + gpio62o_pins: gpio62o_pins { + pins = "GPO62/nRTST1/STRAP5"; + output-high; + }; + gpio63_pins: gpio63_pins { + pins = "GPO63/TXD1/STRAP4"; + input-enable; + }; + gpio63o_pins: gpio63o_pins { + pins = "GPO63/TXD1/STRAP4"; + output-high; + }; + gpio64_pins: gpio64_pins { + pins = "GPIO64/FANIN0"; + input-enable; + }; + gpio64o_pins: gpio64o_pins { + pins = "GPIO64/FANIN0"; + output-high; + }; + gpio65_pins: gpio65_pins { + pins = "GPIO65/FANIN1"; + input-enable; + }; + gpio66_pins: gpio66_pins { + pins = "GPIO66/FANIN2"; + input-enable; + }; + gpio67_pins: gpio67_pins { + pins = "GPIO67/FANIN3"; + input-enable; + }; + gpio68_pins: gpio68_pins { + pins = "GPIO68/FANIN4"; + input-enable; + }; + gpio69_pins: gpio69_pins { + pins = "GPIO69/FANIN5"; + input-enable; + }; + gpio69ol_pins: gpio69ol_pins { + pins = "GPIO69/FANIN5"; + output-low; + }; + gpio70_pins: gpio70_pins { + pins = "GPIO70/FANIN6"; + input-enable; + }; + gpio71_pins: gpio71_pins { + pins = "GPIO71/FANIN7"; + input-enable; + }; + gpio72_pins: gpio72_pins { + pins = "GPIO72/FANIN8"; + input-enable; + }; + gpio72ol_pins: gpio72ol_pins { + pins = "GPIO72/FANIN8"; + output-low; + }; + gpio73_pins: gpio73_pins { + pins = "GPIO73/FANIN9"; + input-enable; + }; + gpio73ol_pins: gpio73ol_pins { + pins = "GPIO73/FANIN9"; + output-low; + }; + gpio74_pins: gpio74_pins { + pins = "GPIO74/FANIN10"; + input-enable; + }; + gpio74ol_pins: gpio74ol_pins { + pins = "GPIO74/FANIN10"; + output-low; + }; + gpio75_pins: gpio75_pins { + pins = "GPIO75/FANIN11"; + input-enable; + }; + gpio75ol_pins: gpio75ol_pins { + pins = "GPIO75/FANIN11"; + output-low; + }; + gpio76_pins: gpio76_pins { + pins = "GPIO76/FANIN12"; + input-enable; + }; + gpio76ol_pins: gpio76ol_pins { + pins = "GPIO76/FANIN12"; + output-low; + }; + gpio77_pins: gpio77_pins { + pins = "GPIO77/FANIN13"; + input-enable; + }; + gpio77ol_pins: gpio77ol_pins { + pins = "GPIO77/FANIN13"; + output-low; + }; + gpio78_pins: gpio78_pins { + pins = "GPIO78/FANIN14"; + input-enable; + }; + gpio78ol_pins: gpio78ol_pins { + pins = "GPIO78/FANIN14"; + output-low; + }; + gpio79_pins: gpio79_pins { + pins = "GPIO79/FANIN15"; + input-enable; + }; + gpio79ol_pins: gpio79ol_pins { + pins = "GPIO79/FANIN15"; + output-low; + }; + gpio80_pins: gpio80_pins { + pins = "GPIO80/PWM0"; + input-enable; + }; + gpio81_pins: gpio81_pins { + pins = "GPIO81/PWM1"; + input-enable; + }; + gpio82_pins: gpio82_pins { + pins = "GPIO82/PWM2"; + input-enable; + }; + gpio83_pins: gpio83_pins { + pins = "GPIO83/PWM3"; + input-enable; + }; + gpio84_pins: gpio84_pins { + pins = "GPIO84/R2TXD0"; + input-enable; + }; + gpio84o_pins: gpio84ol_pins { + pins = "GPIO84/R2TXD0"; + output-high; + }; + gpio85_pins: gpio85_pins { + pins = "GPIO85/R2TXD1"; + input-enable; + }; + gpio85o_pins: gpio85o_pins { + pins = "GPIO85/R2TXD1"; + output-high; + }; + gpio86_pins: gpio86_pins { + pins = "GPIO86/R2TXEN"; + input-enable; + }; + gpio86o_pins: gpio86o_pins { + pins = "GPIO86/R2TXEN"; + output-high; + }; + gpio87_pins: gpio87_pins { + pins = "GPIO87/R2RXD0"; + input-enable; + }; + gpio87o_pins: gpio87o_pins { + pins = "GPIO87/R2RXD0"; + output-high; + }; + gpio88_pins: gpio88_pins { + pins = "GPIO88/R2RXD1"; + input-enable; + }; + gpio88ol_pins: gpio88ol_pins { + pins = "GPIO88/R2RXD1"; + output-low; + }; + gpio89_pins: gpio89_pins { + pins = "GPIO89/R2CRSDV"; + input-enable; + }; + gpio89ol_pins: gpio89ol_pins { + pins = "GPIO89/R2CRSDV"; + output-low; + }; + gpio90_pins: gpio90_pins { + pins = "GPIO90/R2RXERR"; + input-enable; + }; + gpio90o_pins: gpio90o0_pins { + pins = "GPIO90/R2RXERR"; + output-high; + }; + gpio90ol_pins: gpio90ol_pins { + pins = "GPIO90/R2RXERR"; + output-low; + }; + gpio91_pins: gpio91_pins { + pins = "GPIO91/R2MDC"; + input-enable; + }; + gpio91o_pins: gpio91o_pins { + pins = "GPIO91/R2MDC"; + output-high; + }; + gpio91ol_pins: gpio91ol_pins { + pins = "GPIO91/R2MDC"; + output-low; + }; + gpio92_pins: gpio92_pins { + pins = "GPIO92/R2MDIO"; + input-enable; + }; + gpio92o_pins: gpio92o_pins { + pins = "GPIO92/R2MDIO"; + output-high; + }; + gpio92ol_pins: gpio92ol_pins { + pins = "GPIO92/R2MDIO"; + output-low; + }; + gpio93_pins: gpio93_pins { + pins = "GPIO93/GA20/SMB5DSCL"; + input-enable; + }; + gpio93ol_pins: gpio93ol_pins { + pins = "GPIO93/GA20/SMB5DSCL"; + output-low; + }; + gpio94_pins: gpio94_pins { + pins = "GPIO94/nKBRST/SMB5DSDA"; + input-enable; + }; + gpio94o_pins: gpio94o_pins { + pins = "GPIO94/nKBRST/SMB5DSDA"; + output-high; + }; + gpio95_pins: gpio95_pins { + pins = "GPIO95/nLRESET/nESPIRST"; + input-enable; + }; + gpio96_pins: gpio96_pins { + pins = "GPIO96/RG1TXD0"; + input-enable; + }; + gpio96ol_pins: gpio96ol_pins { + pins = "GPIO96/RG1TXD0"; + output-low; + }; + gpio97_pins: gpio97_pins { + pins = "GPIO97/RG1TXD1"; + input-enable; + }; + gpio97ol_pins: gpio97ol_pins { + pins = "GPIO97/RG1TXD1"; + output-low; + }; + gpio98_pins: gpio98_pins { + pins = "GPIO98/RG1TXD2"; + input-enable; + }; + gpio98ol_pins: gpio98ol_pins { + pins = "GPIO98/RG1TXD2"; + output-low; + }; + gpio99_pins: gpio99_pins { + pins = "GPIO99/RG1TXD3"; + input-enable; + }; + gpio99ol_pins: gpio99ol_pins { + pins = "GPIO99/RG1TXD3"; + output-low; + }; + gpio100_pins: gpio100_pins { + pins = "GPIO100/RG1TXC"; + input-enable; + }; + gpio100ol_pins: gpio100ol_pins { + pins = "GPIO100/RG1TXC"; + output-low; + }; + gpio101_pins: gpio101_pins { + pins = "GPIO101/RG1TXCTL"; + input-enable; + }; + gpio101ol_pins: gpio101ol_pins { + pins = "GPIO101/RG1TXCTL"; + output-low; + }; + gpio102_pins: gpio102_pins { + pins = "GPIO102/RG1RXD0"; + input-enable; + }; + gpio102ol_pins: gpio102ol_pins { + pins = "GPIO102/RG1RXD0"; + output-low; + }; + gpio103_pins: gpio103_pins { + pins = "GPIO103/RG1RXD1"; + input-enable; + }; + gpio103ol_pins: gpio103ol_pins { + pins = "GPIO103/RG1RXD1"; + output-low; + }; + gpio104_pins: gpio104_pins { + pins = "GPIO104/RG1RXD2"; + input-enable; + }; + gpio104ol_pins: gpio104ol_pins { + pins = "GPIO104/RG1RXD2"; + output-low; + }; + gpio105_pins: gpio105_pins { + pins = "GPIO105/RG1RXD3"; + input-enable; + }; + gpio105ol_pins: gpio105ol_pins { + pins = "GPIO105/RG1RXD3"; + output-low; + }; + gpio106_pins: gpio106_pins { + pins = "GPIO106/RG1RXC"; + input-enable; + }; + gpio106ol_pins: gpio106ol_pins { + pins = "GPIO106/RG1RXC"; + output-low; + }; + gpio107_pins: gpio107_pins { + pins = "GPIO107/RG1RXCTL"; + input-enable; + }; + gpio107ol_pins: gpio107ol_pins { + pins = "GPIO107/RG1RXCTL"; + output-low; + }; + gpio108_pins: gpio108_pins { + pins = "GPIO108/RG1MDC"; + input-enable; + }; + gpio108ol_pins: gpio108ol_pins { + pins = "GPIO108/RG1MDC"; + output-low; + }; + gpio109_pins: gpio109_pins { + pins = "GPIO109/RG1MDIO"; + input-enable; + }; + gpio109ol_pins: gpio109ol_pins { + pins = "GPIO109/RG1MDIO"; + output-low; + }; + gpio110_pins: gpio110_pins { + pins = "GPIO110/RG2TXD0/DDRV0"; + input-enable; + }; + gpio110ol_pins: gpio110ol_pins { + pins = "GPIO110/RG2TXD0/DDRV0"; + output-low; + }; + gpio111_pins: gpio111_pins { + pins = "GPIO111/RG2TXD1/DDRV1"; + input-enable; + }; + gpio111ol_pins: gpio111ol_pins { + pins = "GPIO111/RG2TXD1/DDRV1"; + output-low; + }; + gpio112_pins: gpio112_pins { + pins = "GPIO112/RG2TXD2/DDRV2"; + input-enable; + }; + gpio112ol_pins: gpio112ol_pins { + pins = "GPIO112/RG2TXD2/DDRV2"; + output-low; + }; + gpio113_pins: gpio113_pins { + pins = "GPIO113/RG2TXD3/DDRV3"; + input-enable; + }; + gpio113ol_pins: gpio113ol_pins { + pins = "GPIO113/RG2TXD3/DDRV3"; + output-low; + }; + gpio118_pins: gpio118_pins { + pins = "GPIO118/SMB2SCL"; + input-enable; + }; + gpio119_pins: gpio119_pins { + pins = "GPIO119/SMB2SDA"; + input-enable; + }; + gpio120_pins: gpio120_pins { + pins = "GPIO120/SMB2CSDA"; + input-enable; + }; + gpio121_pins: gpio121_pins { + pins = "GPIO121/SMB2CSCL"; + input-enable; + }; + gpio122_pins: gpio122_pins { + pins = "GPIO122/SMB2BSDA"; + input-enable; + }; + gpio123_pins: gpio123_pins { + pins = "GPIO123/SMB2BSCL"; + input-enable; + }; + gpio123_pins: gpio123_pins { + pins = "GPIO123/SMB2BSCL"; + input-enable; + }; + gpio124_pins: gpio124_pins { + pins = "GPIO124/SMB1CSDA"; + input-enable; + }; + gpio125_pins: gpio125_pins { + pins = "GPIO125/SMB1CSCL"; + input-enable; + }; + gpio126_pins: gpio126_pins { + pins = "GPIO126/SMB1BSDA"; + input-enable; + }; + gpio127_pins: gpio127_pins { + pins = "GPIO127/SMB1BSCL"; + input-enable; + }; + gpio128o_pins: gpio128o_pins { + pins = "GPIO128/SMB8SCL"; + output-high; + }; + gpio130_pins: gpio130_pins { + pins = "GPIO130/SMB9SCL"; + input-enable; + }; + gpio131_pins: gpio131_pins { + pins = "GPIO131/SMB9SDA"; + input-enable; + }; + gpio132o_pins: gpio132o_pins { + pins = "GPIO132/SMB10SCL"; + output-high; + }; + gpio133_pins: gpio133_pins { + pins = "GPIO133/SMB10SDA"; + input-enable; + }; + gpio134_pins: gpio134_pins { + pins = "GPIO134/SMB11SCL"; + input-enable; + }; + gpio135_pins: gpio135_pins { + pins = "GPIO135/SMB11SDA"; + input-enable; + }; + gpio136_pins: gpio136_pins { + pins = "GPIO136/SD1DT0"; + input-enable; + }; + gpio136o_pins: gpio136o_pins { + pins = "GPIO136/SD1DT0"; + output-high; + }; + gpio137_pins: gpio137_pins { + pins = "GPIO137/SD1DT1"; + input-enable; + }; + gpio137o_pins: gpio137o_pins { + pins = "GPIO137/SD1DT1"; + output-high; + }; + gpio138_pins: gpio138_pins { + pins = "GPIO138/SD1DT2"; + input-enable; + }; + gpio138o_pins: gpio138o_pins { + pins = "GPIO138/SD1DT2"; + output-high; + }; + gpio139_pins: gpio139_pins { + pins = "GPIO139/SD1DT3"; + input-enable; + }; + gpio139o_pins: gpio139o_pins { + pins = "GPIO139/SD1DT3"; + output-high; + }; + gpio140_pins: gpio140_pins { + pins = "GPIO140/SD1CLK"; + input-enable; + }; + gpio140o_pins: gpio140o_pins { + pins = "GPIO140/SD1CLK"; + output-high; + }; + gpio141_pins: gpio141_pins { + pins = "GPIO141/SD1WP"; + input-enable; + }; + gpio141o_pins: gpio141o_pins { + pins = "GPIO141/SD1WP"; + output-high; + }; + gpio142_pins: gpio142_pins { + pins = "GPIO142/SD1CMD"; + input-enable; + }; + gpio142o_pins: gpio142o_pins { + pins = "GPIO142/SD1CMD"; + output-high; + }; + gpio143_pins: gpio143_pins { + pins = "GPIO143/SD1CD/SD1PWR"; + input-enable; + }; + gpio143o_pins: gpio143o_pins { + pins = "GPIO143/SD1CD/SD1PWR"; + output-high; + }; + gpio143ol_pins: gpio143ol_pins { + pins = "GPIO143/SD1CD/SD1PWR"; + output-low; + }; + gpio144_pins: gpio144_pins { + pins = "GPIO144/PWM4"; + input-enable; + }; + gpio145_pins: gpio145_pins { + pins = "GPIO145/PWM5"; + input-enable; + }; + gpio146_pins: gpio146_pins { + pins = "GPIO146/PWM6"; + input-enable; + }; + gpio147_pins: gpio147_pins { + pins = "GPIO147/PWM7"; + input-enable; + }; + gpio148_pins: gpio148_pins { + pins = "GPIO148/MMCDT4"; + input-enable; + }; + gpio148ol_pins: gpio148ol_pins { + pins = "GPIO148/MMCDT4"; + output-low; + }; + gpio149_pins: gpio149_pins { + pins = "GPIO149/MMCDT5"; + input-enable; + }; + gpio149ol_pins: gpio149ol_pins { + pins = "GPIO149/MMCDT5"; + output-low; + }; + gpio150_pins: gpio150_pins { + pins = "GPIO150/MMCDT6"; + input-enable; + }; + gpio150ol_pins: gpio150ol_pins { + pins = "GPIO150/MMCDT6"; + output-low; + }; + gpio151_pins: gpio151_pins { + pins = "GPIO151/MMCDT7"; + input-enable; + }; + gpio151ol_pins: gpio151ol_pins { + pins = "GPIO151/MMCDT7"; + output-low; + }; + gpio152_pins: gpio152_pins { + pins = "GPIO152/MMCCLK"; + input-enable; + }; + gpio152ol_pins: gpio152ol_pins { + pins = "GPIO152/MMCCLK"; + output-low; + }; + gpio153_pins: gpio153_pins { + pins = "GPIO153/MMCWP"; + input-enable; + }; + gpio153ol_pins: gpio153ol_pins { + pins = "GPIO153/MMCWP"; + output-low; + }; + gpio154_pins: gpio154_pins { + pins = "GPIO154/MMCCMD"; + input-enable; + }; + gpio154ol_pins: gpio154ol_pins { + pins = "GPIO154/MMCCMD"; + output-low; + }; + gpio155_pins: gpio155_pins { + pins = "GPIO155/nMMCCD/nMMCRST"; + input-enable; + }; + gpio155ol_pins: gpio155ol_pins { + pins = "GPIO155/nMMCCD/nMMCRST"; + output-low; + }; + gpio156_pins: gpio156_pins { + pins = "GPIO156/MMCDT0"; + input-enable; + }; + gpio156ol_pins: gpio156ol_pins { + pins = "GPIO156/MMCDT0"; + output-low; + }; + gpio157_pins: gpio157_pins { + pins = "GPIO157/MMCDT1"; + input-enable; + }; + gpio157ol_pins: gpio157ol_pins { + pins = "GPIO157/MMCDT1"; + output-low; + }; + gpio158_pins: gpio158_pins { + pins = "GPIO158/MMCDT2"; + input-enable; + }; + gpio158ol_pins: gpio158ol_pins { + pins = "GPIO158/MMCDT2"; + output-low; + }; + gpio159_pins: gpio159_pins { + pins = "GPIO159/MMCDT3"; + input-enable; + }; + gpio159ol_pins: gpio159ol_pins { + pins = "GPIO159/MMCDT3"; + output-low; + }; + gpio160_pins: gpio160_pins { + pins = "GPIO160/CLKOUT/RNGOSCOUT"; + input-enable; + }; + gpio160o_pins: gpio160o_pins { + pins = "GPIO160/CLKOUT/RNGOSCOUT"; + output-high; + }; + gpio160ol_pins: gpio160ol_pins { + pins = "GPIO160/CLKOUT/RNGOSCOUT"; + output-low; + }; + gpio161_pins: gpio161_pins { + pins = "GPIO161/nLFRAME/nESPICS"; + input-enable; + }; + gpio162_pins: gpio162_pins { + pins = "GPIO162/SERIRQ"; + input-enable; + }; + gpio163_pins: gpio163_pins { + pins = "GPIO163/LCLK/ESPICLK"; + input-enable; + }; + gpio164_pins: gpio164_pins { + pins = "GPIO164/LAD0/ESPI_IO0"; + input-enable; + }; + gpio165_pins: gpio165_pins { + pins = "GPIO165/LAD1/ESPI_IO1"; + input-enable; + }; + gpio166_pins: gpio166_pins { + pins = "GPIO166/LAD2/ESPI_IO2"; + input-enable; + }; + gpio167_pins: gpio167_pins { + pins = "GPIO167/LAD3/ESPI_IO3"; + input-enable; + }; + gpio168_pins: gpio168_pins { + pins = "GPIO168/nCLKRUN/nESPIALERT"; + input-enable; + }; + gpio168ol_pins: gpio168ol_pins { + pins = "GPIO168/nCLKRUN/nESPIALERT"; + output-low; + }; + gpio169_pins: gpio169_pins { + pins = "GPIO169/nSCIPME"; + input-enable; + }; + gpio169o_pins: gpio169o_pins { + pins = "GPIO169/nSCIPME"; + output-high; + }; + gpio169ol_pins: gpio169ol_pins { + pins = "GPIO169/nSCIPME"; + output-low; + }; + gpio170_pins: gpio170_pins { + pins = "GPIO170/nSMI"; + input-enable; + }; + gpio170ol_pins: gpio170ol_pins { + pins = "GPIO170/nSMI"; + output-low; + }; + gpio173o_pins: gpio173o_pins { + pins = "GPIO173/SMB7SCL"; + output-high; + }; + gpio174_pins: gpio174_pins { + pins = "GPIO174/SMB7SDA"; + input-enable; + }; + gpio175_pins: gpio175_pins { + pins = "GPIO175/PSPI1CK/FANIN19"; + input-enable; + }; + gpio175o_pins: gpio175o_pins { + pins = "GPIO175/PSPI1CK/FANIN19"; + output-high; + }; + gpio175ol_pins: gpio175ol_pins { + pins = "GPIO175/PSPI1CK/FANIN19"; + output-low; + }; + gpio176_pins: gpio176_pins { + pins = "GPIO176/PSPI1DO/FANIN18"; + input-enable; + }; + gpio176o_pins: gpio176o_pins { + pins = "GPIO176/PSPI1DO/FANIN18"; + output-high; + }; + gpio176ol_pins: gpio176ol_pins { + pins = "GPIO176/PSPI1DO/FANIN18"; + output-low; + }; + gpio177_pins: gpio177_pins { + pins = "GPIO177/PSPI1DI/FANIN17"; + input-enable; + }; + gpio177o_pins: gpio177o_pins { + pins = "GPIO177/PSPI1DI/FANIN17"; + output-high; + }; + gpio177ol_pins: gpio177ol_pins { + pins = "GPIO177/PSPI1DI/FANIN17"; + output-low; + }; + gpio187_pins: gpio187_pins { + pins = "GPIO187/nSPI3CS1"; + input-enable; + }; + gpio187o_pins: gpio187o_pins { + pins = "GPIO187/nSPI3CS1"; + output-high; + }; + gpio187ol_pins: gpio187ol_pins { + pins = "GPIO187/nSPI3CS1"; + output-low; + }; + gpio188_pins: gpio188_pins { + pins = "GPIO188/SPI3D2/nSPI3CS2"; + input-enable; + }; + gpio188o_pins: gpio188o_pins { + pins = "GPIO188/SPI3D2/nSPI3CS2"; + output-high; + }; + gpio189o_pins: gpio189o_pins { + pins = "GPIO189/SPI3D3/nSPI3CS3"; + output-high; + }; + gpio190_pins: gpio190_pins { + pins = "GPIO190/nPRD_SMI"; + input-enable; + }; + gpio190o_pins: gpio190o_pins { + pins = "GPIO190/nPRD_SMI"; + output-high; + }; + gpio190ol_pins: gpio190ol_pins { + pins = "GPIO190/nPRD_SMI"; + output-low; + }; + gpio191o_pins: gpio191o_pins { + pins = "GPIO191"; + output-high; + }; + gpio191ol_pins: gpio191ol_pins { + pins = "GPIO191"; + output-low; + }; + gpio192_pins: gpio192_pins { + pins = "GPIO192"; + input-enable; + }; + gpio192o_pins: gpio192o_pins { + pins = "GPIO192"; + output-high; + }; + gpio192ol_pins: gpio192ol_pins { + pins = "GPIO192"; + output-low; + }; + gpio194_pins: gpio194_pins { + pins = "GPIO194/SMB0BSCL"; + input-enable; + }; + gpio194o_pins: gpio194o_pins { + pins = "GPIO194/SMB0BSCL"; + output-high; + }; + gpio195_pins: gpio195_pins { + pins = "GPIO195/SMB0BSDA"; + input-enable; + }; + gpio196_pins: gpio196_pins { + pins = "GPIO196/SMB0CSCL"; + input-enable; + }; + gpio197_pins: gpio197_pins { + pins = "GPIO197/SMB0DEN"; + input-enable; + }; + gpio197o_pins: gpio197o_pins { + pins = "GPIO197/SMB0DEN"; + output-high; + }; + gpio197ol_pins: gpio197ol_pins { + pins = "GPIO197/SMB0DEN"; + output-low; + }; + gpio198o_pins: gpio198o_pins { + pins = "GPIO198/SMB0DSDA"; + output-high; + }; + gpio198ol_pins: gpio198ol_pins { + pins = "GPIO198/SMB0DSDA"; + output-low; + }; + gpio199_pins: gpio199_pins { + pins = "GPIO199/SMB0DSCL"; + input-enable; + }; + gpio200_pins: gpio200_pins { + pins = "GPIO200/R2CK"; + input-enable; + }; + gpio200ol_pins: gpio200ol_pins { + pins = "GPIO200/R2CK"; + output-low; + }; + gpio201ol_pins: gpio201ol_pins { + pins = "GPIO200/R2CK"; + output-low; + }; + gpio202_pins: gpio202_pins { + pins = "GPIO202/SMB0CSDA"; + input-enable; + }; + gpio203_pins: gpio203_pins { + pins = "GPIO203/FANIN16"; + input-enable; + }; + gpio203o_pins: gpio203o_pins { + pins = "GPIO203/FANIN16"; + output-high; + }; + gpio203ol_pins: gpio203ol_pins { + pins = "GPIO203/FANIN16"; + output-low; + }; + gpio204_pins: gpio204_pins { + pins = "GPIO204/DDC2SCL"; + input-enable; + }; + gpio204o_pins: gpio204o_pins { + pins = "GPIO204/DDC2SCL"; + output-high; + }; + gpio205_pins: gpio205_pins { + pins = "GPIO205/DDC2SDA"; + input-enable; + }; + gpio205o_pins: gpio205o_pins { + pins = "GPIO205/DDC2SDA"; + output-high; + }; + gpio206_pins: gpio206_pins { + pins = "GPIO206/HSYNC2"; + input-enable; + }; + gpio206o_pins: gpio206o_pins { + pins = "GPIO206/HSYNC2"; + output-high; + }; + gpio207_pins: gpio207_pins { + pins = "GPIO207/VSYNC2"; + input-enable; + }; + gpio207o_pins: gpio207o_pins { + pins = "GPIO207/VSYNC2"; + output-high; + }; + gpio208_pins: gpio208_pins { + pins = "GPIO208/RG2TXC/DVCK"; + input-enable; + }; + gpio208ol_pins: gpio208ol_pins { + pins = "GPIO208/RG2TXC/DVCK"; + output-low; + }; + gpio209_pins: gpio209_pins { + pins = "GPIO209/RG2TXCTL/DDRV4"; + input-enable; + }; + gpio209ol_pins: gpio209ol_pins { + pins = "GPIO209/RG2TXCTL/DDRV4"; + output-low; + }; + gpio210_pins: gpio210_pins { + pins = "GPIO210/RG2RXD0/DDRV5"; + input-enable; + }; + gpio210ol_pins: gpio210ol_pins { + pins = "GPIO210/RG2RXD0/DDRV5"; + output-low; + }; + gpio211_pins: gpio211_pins { + pins = "GPIO211/RG2RXD1/DDRV6"; + input-enable; + }; + gpio211ol_pins: gpio211ol_pins { + pins = "GPIO211/RG2RXD1/DDRV6"; + output-low; + }; + gpio212_pins: gpio212_pins { + pins = "GPIO212/RG2RXD2/DDRV7"; + input-enable; + }; + gpio212o_pins: gpio212o_pins { + pins = "GPIO212/RG2RXD2/DDRV7"; + output-high; + }; + gpio212ol_pins: gpio212ol_pins { + pins = "GPIO212/RG2RXD2/DDRV7"; + output-low; + }; + gpio213_pins: gpio213_pins { + pins = "GPIO213/RG2RXD3/DDRV8"; + input-enable; + }; + gpio213o_pins: gpio213o_pins { + pins = "GPIO213/RG2RXD3/DDRV8"; + output-high; + }; + gpio213ol_pins: gpio213ol_pins { + pins = "GPIO213/RG2RXD3/DDRV8"; + output-low; + }; + gpio214_pins: gpio214_pins { + pins = "GPIO214/RG2RXC/DDRV9"; + input-enable; + }; + gpio214ol_pins: gpio214ol_pins { + pins = "GPIO214/RG2RXC/DDRV9"; + output-low; + }; + gpio215_pins: gpio215_pins { + pins = "GPIO215/RG2RXCTL/DDRV10"; + input-enable; + }; + gpio215ol_pins: gpio215ol_pins { + pins = "GPIO215/RG2RXCTL/DDRV10"; + output-low; + }; + gpio216_pins: gpio216_pins { + pins = "GPIO216/RG2MDC/DDRV11"; + input-enable; + }; + gpio216ol_pins: gpio216ol_pins { + pins = "GPIO216/RG2MDC/DDRV11"; + output-low; + }; + gpio217_pins: gpio217_pins { + pins = "GPIO217/RG2MDIO/DVHSYNC"; + input-enable; + }; + gpio217ol_pins: gpio217ol_pins { + pins = "GPIO217/RG2MDIO/DVHSYNC"; + output-low; + }; + gpio218_pins: gpio218_pins { + pins = "GPIO218/nWDO1"; + input-enable; + }; + gpio218ol_pins: gpio218ol_pins { + pins = "GPIO218/nWDO1"; + output-low; + }; + gpio219_pins: gpio219_pins { + pins = "GPIO219/nWDO2"; + input-enable; + }; + gpio219ol_pins: gpio219ol_pins { + pins = "GPIO219/nWDO2"; + output-low; + }; + gpio220ol_pins: gpio220ol_pins { + pins = "GPIO220/SMB12SCL"; + output-low; + }; + gpio221o_pins: gpio221o_pins { + pins = "GPIO221/SMB12SDA"; + output-high; + }; + gpio222_pins: gpio222_pins { + pins = "GPIO222/SMB13SCL"; + input-enable; + }; + gpio222o_pins: gpio222o_pins { + pins = "GPIO222/SMB13SCL"; + output-high; + }; + gpio223_pins: gpio223_pins { + pins = "GPIO223/SMB13SDA"; + input-enable; + }; + gpio223ol_pins: gpio223ol_pins { + pins = "GPIO223/SMB13SDA"; + output-low; + }; + gpio224_pins: gpio224_pins { + pins = "GPIO224/SPIXCK"; + input-enable; + }; + gpio224ol_pins: gpio224ol_pins { + pins = "GPIO224/SPIXCK"; + output-low; + }; + gpio225_pins: gpio225_pins { + pins = "GPO225/SPIXD0/STRAP12"; + input-enable; + }; + gpio225o_pins: gpio225o_pins { + pins = "GPO225/SPIXD0/STRAP12"; + output-high; + }; + gpio226_pins: gpio226_pins { + pins = "GPO226/SPIXD1/STRAP13"; + input-enable; + }; + gpio226o_pins: gpio226o_pins { + pins = "GPO226/SPIXD1/STRAP13"; + output-high; + }; + gpio227_pins: gpio227_pins { + pins = "GPIO227/nSPIXCS0"; + input-enable; + }; + gpio227ol_pins: gpio227ol_pins { + pins = "GPIO227/nSPIXCS0"; + output-low; + }; + gpio228_pins: gpio228_pins { + pins = "GPIO228/nSPIXCS1"; + input-enable; + }; + gpio228ol_pins: gpio228ol_pins { + pins = "GPIO228/nSPIXCS1"; + output-low; + }; + gpio229_pins: gpio229_pins { + pins = "GPIO229/SPIXD2/STRAP3"; + input-enable; + }; + gpio229o_pins: gpio229o_pins { + pins = "GPIO229/SPIXD2/STRAP3"; + output-high; + }; + gpio230_pins: gpio230_pins { + pins = "GPIO230/SPIXD3"; + input-enable; + }; + gpio230ol_pins: gpio230ol_pins { + pins = "GPIO230/SPIXD3"; + output-low; + }; + gpio231_pins: gpio231_pins { + pins = "GPIO231/nCLKREQ"; + input-enable; + }; + gpio231o_pins: gpio231o_pins { + pins = "GPIO231/nCLKREQ"; + output-high; + }; + gpio255_pins: gpio255_pins { + pins = "GPI255/DACOSEL"; + input-enable; + }; + }; +}; diff --git a/arch/arm/boot/dts/rockaway-npcm730-evb.dts b/arch/arm/boot/dts/rockaway-npcm730-evb.dts new file mode 100644 index 00000000000000..cd41b92938b8c2 --- /dev/null +++ b/arch/arm/boot/dts/rockaway-npcm730-evb.dts @@ -0,0 +1,454 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018 Nuvoton Technology tomer.maimon@nuvoton.com +// Copyright 2018 Google, Inc. + +/dts-v1/; +#include "nuvoton-npcm730.dtsi" + +/ { + model = "Rockaway npcm730 Development Board (Device Tree)"; + compatible = "nuvoton,npcm750"; + + aliases { + ethernet0 = &emc0; + ethernet1 = &gmac0; + serial0 = &serial0; + serial1 = &serial1; + serial2 = &serial2; + serial3 = &serial3; + i2c0 = &i2c0; + i2c1 = &i2c1; + i2c2 = &i2c2; + i2c3 = &i2c3; + i2c4 = &i2c4; + i2c5 = &i2c5; + i2c6 = &i2c6; + i2c7 = &i2c7; + i2c8 = &i2c8; + i2c9 = &i2c9; + i2c10 = &i2c10; + i2c11 = &i2c11; + i2c12 = &i2c12; + i2c13 = &i2c13; + i2c14 = &i2c14; + i2c15 = &i2c15; + }; + + chosen { + stdout-path = &serial3; + }; + + memory { + reg = <0 0x40000000>; + }; + + ahb { + gmac0: eth@f0802000 { + status = "okay"; + }; + + + emc0: eth@f0825000 { + phy-mode = "rmii"; + #use-ncsi; /* add this to support ncsi */ + status = "okay"; + }; + + ehci1: ehci@f0806000 { + status = "okay"; + }; + + ohci1: ohci@f0807000 { + status = "okay"; + }; + + aes:aes@f0858000 { + status = "okay"; + }; + + sha:sha@f085a000 { + status = "okay"; + }; + + spi0: spi@fb000000 { + spi-nor@0 { + partitions@80000000 { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + bbuboot1@0 { + label = "bb-uboot-1"; + reg = <0x0000000 0x80000>; + read-only; + }; + bbuboot2@80000 { + label = "bb-uboot-2"; + reg = <0x0080000 0x80000>; + read-only; + }; + envparam@100000 { + label = "env-param"; + reg = <0x0100000 0x40000>; + read-only; + }; + spare@140000 { + label = "spare"; + reg = <0x0140000 0xC0000>; + }; + kernel@200000 { + label = "kernel"; + reg = <0x0200000 0x400000>; + }; + rootfs@600000 { + label = "rootfs"; + reg = <0x0600000 0x700000>; + }; + spare1@D00000 { + label = "spare1"; + reg = <0x0D00000 0x200000>; + }; + spare2@0F00000 { + label = "spare2"; + reg = <0x0F00000 0x200000>; + }; + spare3@1100000 { + label = "spare3"; + reg = <0x1100000 0x200000>; + }; + spare4@1300000 { + label = "spare4"; + reg = <0x1300000 0x0>; + }; + }; + }; + }; + + spi3: spi@c0000000 { + spi-nor@0 { + partitions@A0000000 { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + system1@0 { + label = "spi3-system1"; + reg = <0x0 0x800000>; + }; + system2@800000 { + label = "spi3-system2"; + reg = <0x800000 0x0>; + }; + }; + }; + }; + + sdhci0: sdhci@f0840000 { + status = "okay"; + }; + + sdhci1: sdhci@f0842000 { + status = "okay"; + }; + + apb { + + watchdog1: watchdog@901C { + status = "okay"; + }; + + rng: rng@b000 { + status = "okay"; + }; + + serial0: serial@1000 { + status = "okay"; + }; + + serial1: serial@2000 { + status = "okay"; + }; + + serial2: serial@3000 { + status = "okay"; + }; + + serial3: serial@4000 { + status = "okay"; + }; + + otp:otp@189000 { + status = "okay"; + }; + + lpc_kcs: lpc_kcs@7000 { + kcs1: kcs1@0 { + status = "okay"; + }; + + kcs2: kcs2@0 { + status = "okay"; + }; + + kcs3: kcs3@0 { + status = "okay"; + }; + }; + + /* lm75 on SVB */ + i2c0: i2c-bus@80000 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + }; + + /* lm75 on EB */ + i2c1: i2c-bus@81000 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + }; + + i2c2: i2c-bus@82000 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + }; + + i2c6: i2c-bus@86000 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + }; + + i2c3: i2c-bus@83000 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + }; + + i2c4: i2c-bus@84000 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + }; + + i2c5: i2c-bus@85000 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + }; + + i2c7: i2c-bus@87000 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + }; + + i2c8: i2c-bus@88000 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + }; + + i2c9: i2c-bus@89000 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + }; + + i2c10: i2c-bus@8a000 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + }; + + i2c11: i2c-bus@8b000 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + }; + + i2c14: i2c-bus@8e000 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + }; + + + fan: fan@0 { + pinctrl-names = "default"; + pinctrl-0 = < &fanin0_pins + &fanin1_pins + &fanin2_pins + &fanin3_pins + &fanin4_pins + &fanin5_pins + &fanin6_pins + &fanin7_pins>; + status = "okay"; + }; + + pwm: pwm@103000 { + pinctrl-names = "default"; + pinctrl-0 = < &pwm0_pins + &pwm1_pins + &pwm2_pins + &pwm3_pins + &pwm4_pins + &pwm5_pins + &pwm6_pins + &pwm7_pins>; + status = "okay"; + }; + + /* Here is an example for future pspi binding */ + /* + pspi: pspi@0 { + pinctrl-names = "default"; + pinctrl-0 = <&pspi1_pins &pspi2_pins &gpio20o_pins &gpio203o_pins> ; + cs-gpios = <&gpio 20 1>, <&gpio 203 1>; + status = "okay"; + }; + */ + }; + }; + + pinctrl: pinctrl@0 { + pinctrl-names = "default"; + pinctrl-0 = < &iox1_pins + &gpio4_pins + &gpio5_pins + &gpio6o_pins + &gpio7o_pins + &gpio8_pins + &gpio9_pins + &gpio10_pins + &gpio11_pins + &gpio12o_pins + &gpio13_pins + &gpio14_pins + &gpio15o_pins + &gpio16o_pins + &gpio17o_pins + &gpio18ol_pins + &gpio19ol_pins + &gpio20_pins + &gpio21_pins + &gpio24_pins + &gpio25_pins + &gpio26_pins + &gpio27_pins + &gpio32_pins + &gpio37ol_pins + &gpio38ol_pins + &gpio39ol_pins + &gpio40ol_pins + &jtag2_pins + &gpio59_pins + &gpio60_pins + &gpio61_pins + &gpio62_pins + &gpio63_pins + &gpio72_pins + &gpio73_pins + &gpio74_pins + &gpio75_pins + &gpio76_pins + &gpio77_pins + &gpio78_pins + &gpio79_pins + &gpio84o_pins + &gpio85o_pins + &gpio86o_pins + &gpio87o_pins + &gpio88_pins + &gpio89_pins + &gpio90_pins + &gpio91_pins + &gpio92_pins + &gpio93_pins + &gpio94o_pins + &gpio110ol_pins + &gpio111ol_pins + &gpio112ol_pins + &gpio113ol_pins + &gpio120_pins + &gpio121_pins + &gpio122_pins + &gpio123_pins + &gpio124_pins + &gpio125_pins + &gpio126_pins + &gpio127_pins + &gpio136_pins + &gpio137_pins + &gpio138_pins + &gpio139_pins + &gpio140_pins + &gpio141_pins + &gpio142_pins + &gpio143_pins + &gpio148ol_pins + &gpio149ol_pins + &gpio150ol_pins + &gpio151ol_pins + &gpio152ol_pins + &gpio153_pins + &gpio154ol_pins + &gpio155_pins + &gpio156_pins + &gpio157ol_pins + &gpio158ol_pins + &gpio159ol_pins + &gpio160ol_pins + &serirq_pins + &lpc_pins + &gpio168ol_pins + &gpio169o_pins + &gpio170_pins + &gpio173o_pins + &gpio174_pins + &gpio175o_pins + &gpio176o_pins + &gpio177ol_pins + &gpio187o_pins + &gpio188o_pins + &gpio189o_pins + &gpio190_pins + &gpio191o_pins + &gpio192ol_pins + &gpio194_pins + &gpio195_pins + &gpio196_pins + &gpio197o_pins + &gpio198o_pins + &gpio199_pins + &gpio200_pins + &gpio202_pins + &gpio203ol_pins + &gpio204o_pins + &gpio205o_pins + &gpio206o_pins + &gpio207o_pins + &ddc_pins + &gpio208ol_pins + &gpio209_pins + &gpio210ol_pins + &gpio211ol_pins + &gpio212o_pins + &gpio213o_pins + &gpio214ol_pins + &gpio215ol_pins + &gpio216ol_pins + &gpio217ol_pins + &gpio218_pins + &gpio219_pins + &gpio224ol_pins + &gpio225_pins + &gpio226_pins + &gpio227ol_pins + &gpio228_pins + &gpio229_pins + &gpio230ol_pins + &clkreq_pins>; + }; +}; From b89a26f78b2681fd78b454649b5c8ec2f774451c Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Sun, 7 Jan 2018 11:57:56 +0200 Subject: [PATCH 03/37] MAINTAINERS: Add entry for Nuvoton NPCM architecture Add maintainers and reviewers for Nuvoton NPCM architecture Based on [v8,3/3] MAINTAINERS: Add entry for Nuvoton NPCM architecture patch: Brendan Higgins : - https://patchwork.kernel.org/patch/10063413/ Signed-off-by: Tomer Maimon --- MAINTAINERS | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 1c3feffb1c1cfd..c92628f9237cdf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1598,6 +1598,20 @@ F: drivers/pinctrl/nomadik/ F: drivers/i2c/busses/i2c-nomadik.c T: git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-nomadik.git +ARM/NUVOTON NPCM ARCHITECTURE +M: Avi Fishman +M: Tomer Maimon +R: Patrick Venture +R: Nancy Yuen +R: Brendan Higgins +L: openbmc@lists.ozlabs.org (moderated for non-subscribers) +S: Supported +F: arch/arm/mach-npcm/ +F: arch/arm/boot/dts/nuvoton-npcm* +F: include/dt-bindings/clock/nuvoton,npcm7xx-clks.h +F: drivers/*/*npcm* +F: Documentation/*/*npcm* + ARM/NUVOTON W90X900 ARM ARCHITECTURE M: Wan ZongShun L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) From 1aaa11ad73eedd478e0a527335a0a565cedca5ff Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Mon, 18 Dec 2017 14:15:18 +0200 Subject: [PATCH 04/37] dt-binding: timer: document NPCM7xx timer DT bindings Added device tree binding documentation for Nuvoton NPCM7xx timer. Signed-off-by: Tomer Maimon --- .../bindings/timer/nuvoton,npcm7xx-timer.txt | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 Documentation/devicetree/bindings/timer/nuvoton,npcm7xx-timer.txt diff --git a/Documentation/devicetree/bindings/timer/nuvoton,npcm7xx-timer.txt b/Documentation/devicetree/bindings/timer/nuvoton,npcm7xx-timer.txt new file mode 100644 index 00000000000000..ea22dfe485beec --- /dev/null +++ b/Documentation/devicetree/bindings/timer/nuvoton,npcm7xx-timer.txt @@ -0,0 +1,21 @@ +Nuvoton NPCM7xx timer + +Nuvoton NPCM7xx have three timer modules, each timer module provides five 24-bit +timer counters. + +Required properties: +- compatible : "nuvoton,npcm750-timer" for Poleg NPCM750. +- reg : Offset and length of the register set for the device. +- interrupts : Contain the timer interrupt with flags for + falling edge. +- clocks : phandle of timer reference clock (usually a 25 MHz clock). + +Example: + +timer@f0008000 { + compatible = "nuvoton,npcm750-timer"; + interrupts = ; + reg = <0xf0008000 0x50>; + clocks = <&clk NPCM7XX_CLK_TIMER>; +}; + From cab0468795977a9d53a7b54b8e479c05d0e04037 Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Mon, 18 Dec 2017 14:18:01 +0200 Subject: [PATCH 05/37] clocksource: npcm: add NPCM7xx timer driver Add Nuvoton BMC NPCM7xx timer driver. the clocksource Enable 24-bit TIMER0 and TIMER1 counters, while TIMER0 serves as clockevent and TIMER1 serves as clocksource. Signed-off-by: Tomer Maimon --- drivers/clocksource/Kconfig | 8 ++ drivers/clocksource/Makefile | 1 + drivers/clocksource/timer-npcm7xx.c | 215 ++++++++++++++++++++++++++++ 3 files changed, 224 insertions(+) create mode 100644 drivers/clocksource/timer-npcm7xx.c diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 54a67f8a28ebfb..79df794bbbf27d 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -140,6 +140,14 @@ config VT8500_TIMER help Enables support for the VT8500 driver. +config NPCM7XX_TIMER + bool "NPCM7xx timer driver" if COMPILE_TEST + depends on HAS_IOMEM + select CLKSRC_MMIO + help + Enable 24-bit TIMER0 and TIMER1 counters in the NPCM7xx architecture, + While TIMER0 serves as clockevent and TIMER1 serves as clocksource. + config CADENCE_TTC_TIMER bool "Cadence TTC timer driver" if COMPILE_TEST depends on COMMON_CLK diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 6df949402dfc13..c07986a7d5000e 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_CLKSRC_TI_32K) += timer-ti-32k.o obj-$(CONFIG_CLKSRC_NPS) += timer-nps.o obj-$(CONFIG_OXNAS_RPS_TIMER) += timer-oxnas-rps.o obj-$(CONFIG_OWL_TIMER) += owl-timer.o +obj-$(CONFIG_NPCM7XX_TIMER) += timer-npcm7xx.o obj-$(CONFIG_ARC_TIMERS) += arc_timer.o obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o diff --git a/drivers/clocksource/timer-npcm7xx.c b/drivers/clocksource/timer-npcm7xx.c new file mode 100644 index 00000000000000..7a9bb5532d9921 --- /dev/null +++ b/drivers/clocksource/timer-npcm7xx.c @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2014-2018 Nuvoton Technologies tomer.maimon@nuvoton.com + * All rights reserved. + * + * Copyright 2017 Google, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "timer-of.h" + +/* Timers registers */ +#define NPCM7XX_REG_TCSR0 0x0 /* Timer 0 Control and Status Register */ +#define NPCM7XX_REG_TICR0 0x8 /* Timer 0 Initial Count Register */ +#define NPCM7XX_REG_TCSR1 0x4 /* Timer 1 Control and Status Register */ +#define NPCM7XX_REG_TICR1 0xc /* Timer 1 Initial Count Register */ +#define NPCM7XX_REG_TDR1 0x14 /* Timer 1 Data Register */ +#define NPCM7XX_REG_TISR 0x18 /* Timer Interrupt Status Register */ + +/* Timers control */ +#define NPCM7XX_Tx_RESETINT 0x1f +#define NPCM7XX_Tx_PERIOD BIT(27) +#define NPCM7XX_Tx_INTEN BIT(29) +#define NPCM7XX_Tx_COUNTEN BIT(30) +#define NPCM7XX_Tx_ONESHOT 0x0 +#define NPCM7XX_Tx_OPER GENMASK(3, 27) +#define NPCM7XX_Tx_MIN_PRESCALE 0x1 +#define NPCM7XX_Tx_TDR_MASK_BITS 24 +#define NPCM7XX_Tx_MAX_CNT 0xFFFFFF +#define NPCM7XX_T0_CLR_INT 0x1 +#define NPCM7XX_Tx_CLR_CSR 0x0 + +/* Timers operating mode */ +#define NPCM7XX_START_PERIODIC_Tx (NPCM7XX_Tx_PERIOD | NPCM7XX_Tx_COUNTEN | \ + NPCM7XX_Tx_INTEN | \ + NPCM7XX_Tx_MIN_PRESCALE) + +#define NPCM7XX_START_ONESHOT_Tx (NPCM7XX_Tx_ONESHOT | NPCM7XX_Tx_COUNTEN | \ + NPCM7XX_Tx_INTEN | \ + NPCM7XX_Tx_MIN_PRESCALE) + +#define NPCM7XX_START_Tx (NPCM7XX_Tx_COUNTEN | NPCM7XX_Tx_PERIOD | \ + NPCM7XX_Tx_MIN_PRESCALE) + +#define NPCM7XX_DEFAULT_CSR (NPCM7XX_Tx_CLR_CSR | NPCM7XX_Tx_MIN_PRESCALE) + +static int npcm7xx_timer_resume(struct clock_event_device *evt) +{ + struct timer_of *to = to_timer_of(evt); + u32 val; + + val = readl(timer_of_base(to) + NPCM7XX_REG_TCSR0); + val |= NPCM7XX_Tx_COUNTEN; + writel(val, timer_of_base(to) + NPCM7XX_REG_TCSR0); + + return 0; +} + +static int npcm7xx_timer_shutdown(struct clock_event_device *evt) +{ + struct timer_of *to = to_timer_of(evt); + u32 val; + + val = readl(timer_of_base(to) + NPCM7XX_REG_TCSR0); + val &= ~NPCM7XX_Tx_COUNTEN; + writel(val, timer_of_base(to) + NPCM7XX_REG_TCSR0); + + return 0; +} + +static int npcm7xx_timer_oneshot(struct clock_event_device *evt) +{ + struct timer_of *to = to_timer_of(evt); + u32 val; + + val = readl(timer_of_base(to) + NPCM7XX_REG_TCSR0); + val &= ~NPCM7XX_Tx_OPER; + + val = readl(timer_of_base(to) + NPCM7XX_REG_TCSR0); + val |= NPCM7XX_START_ONESHOT_Tx; + writel(val, timer_of_base(to) + NPCM7XX_REG_TCSR0); + + return 0; +} + +static int npcm7xx_timer_periodic(struct clock_event_device *evt) +{ + struct timer_of *to = to_timer_of(evt); + u32 val; + + val = readl(timer_of_base(to) + NPCM7XX_REG_TCSR0); + val &= ~NPCM7XX_Tx_OPER; + + writel(timer_of_period(to), timer_of_base(to) + NPCM7XX_REG_TICR0); + val |= NPCM7XX_START_PERIODIC_Tx; + + writel(val, timer_of_base(to) + NPCM7XX_REG_TCSR0); + + return 0; +} + +static int npcm7xx_clockevent_set_next_event(unsigned long evt, + struct clock_event_device *clk) +{ + struct timer_of *to = to_timer_of(clk); + u32 val; + + writel(evt, timer_of_base(to) + NPCM7XX_REG_TICR0); + val = readl(timer_of_base(to) + NPCM7XX_REG_TCSR0); + val |= NPCM7XX_START_Tx; + writel(val, timer_of_base(to) + NPCM7XX_REG_TCSR0); + + return 0; +} + +static irqreturn_t npcm7xx_timer0_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = (struct clock_event_device *)dev_id; + struct timer_of *to = to_timer_of(evt); + + writel(NPCM7XX_T0_CLR_INT, timer_of_base(to) + NPCM7XX_REG_TISR); + + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static struct timer_of npcm7xx_to = { + .flags = TIMER_OF_IRQ | TIMER_OF_BASE | TIMER_OF_CLOCK, + + .clkevt = { + .name = "npcm7xx-timer0", + .features = CLOCK_EVT_FEAT_PERIODIC | + CLOCK_EVT_FEAT_ONESHOT, + .set_next_event = npcm7xx_clockevent_set_next_event, + .set_state_shutdown = npcm7xx_timer_shutdown, + .set_state_periodic = npcm7xx_timer_periodic, + .set_state_oneshot = npcm7xx_timer_oneshot, + .tick_resume = npcm7xx_timer_resume, + .rating = 300, + }, + + .of_irq = { + .handler = npcm7xx_timer0_interrupt, + .flags = IRQF_TIMER | IRQF_IRQPOLL, + }, +}; + +static void __init npcm7xx_clockevents_init(void) +{ + writel(NPCM7XX_DEFAULT_CSR, + timer_of_base(&npcm7xx_to) + NPCM7XX_REG_TCSR0); + + writel(NPCM7XX_Tx_RESETINT, + timer_of_base(&npcm7xx_to) + NPCM7XX_REG_TISR); + + npcm7xx_to.clkevt.cpumask = cpumask_of(0); + clockevents_config_and_register(&npcm7xx_to.clkevt, + timer_of_rate(&npcm7xx_to), + 0x1, NPCM7XX_Tx_MAX_CNT); +} + +static void __init npcm7xx_clocksource_init(void) +{ + u32 val; + + writel(NPCM7XX_DEFAULT_CSR, + timer_of_base(&npcm7xx_to) + NPCM7XX_REG_TCSR1); + writel(NPCM7XX_Tx_MAX_CNT, + timer_of_base(&npcm7xx_to) + NPCM7XX_REG_TICR1); + + val = readl(timer_of_base(&npcm7xx_to) + NPCM7XX_REG_TCSR1); + val |= NPCM7XX_START_Tx; + writel(val, timer_of_base(&npcm7xx_to) + NPCM7XX_REG_TCSR1); + + clocksource_mmio_init(timer_of_base(&npcm7xx_to) + + NPCM7XX_REG_TDR1, + "npcm7xx-timer1", timer_of_rate(&npcm7xx_to), + 200, (unsigned int)NPCM7XX_Tx_TDR_MASK_BITS, + clocksource_mmio_readl_down); +} + +static int __init npcm7xx_timer_init(struct device_node *np) +{ + int ret; + + ret = timer_of_init(np, &npcm7xx_to); + if (ret) + return ret; + + /* Clock input is divided by PRESCALE + 1 before it is fed */ + /* to the counter */ + npcm7xx_to.of_clk.rate = npcm7xx_to.of_clk.rate / + (NPCM7XX_Tx_MIN_PRESCALE + 1); + + npcm7xx_clocksource_init(); + npcm7xx_clockevents_init(); + + pr_info("Enabling NPCM7xx clocksource timer base: %px, IRQ: %d ", + timer_of_base(&npcm7xx_to), timer_of_irq(&npcm7xx_to)); + + return 0; +} + +TIMER_OF_DECLARE(npcm7xx, "nuvoton,npcm750-timer", npcm7xx_timer_init); + From 89db191ee1046ad26d45e8fb0e1062525ccd8375 Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Mon, 18 Dec 2017 14:47:34 +0200 Subject: [PATCH 06/37] dt-binding: watchdog: document NPCM7xx watchdog DT bindings Added device tree binding documentation for Nuvoton NPCM7xx watchdog. --- .../bindings/watchdog/nuvoton,npcm7xx-wdt.txt | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 Documentation/devicetree/bindings/watchdog/nuvoton,npcm7xx-wdt.txt diff --git a/Documentation/devicetree/bindings/watchdog/nuvoton,npcm7xx-wdt.txt b/Documentation/devicetree/bindings/watchdog/nuvoton,npcm7xx-wdt.txt new file mode 100644 index 00000000000000..211f93cab1416f --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/nuvoton,npcm7xx-wdt.txt @@ -0,0 +1,23 @@ +Nuvoton NPCM7xx watchdog timer + +Nuvoton NPCM7xx have three watchdog timer modules, each watchdog timer is a free-running timer +with programmable timeout intervals. + +Required properties: +- compatible : "nuvoton,npcm750-wdt" for Poleg NPCM750. +- reg : Offset and length of the register set for the device. +- interrupts : Contain the timer interrupt with flags for + falling edge. + +Optional: +- clocks : phandle of watchdog timer reference clock. + +Example: + +watchdog0: watchdog@f0008000 { + compatible = "nuvoton,npcm750-wdt"; + interrupts = ; + reg = <0xf0008000 0x1000>; + status = "disabled"; + clocks = <&clk NPCM7XX_CLK_TIMER>; +}; \ No newline at end of file From dec96090d5f5b1c6470fc0b73b4d1d39815ed4a3 Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Mon, 18 Dec 2017 14:57:10 +0200 Subject: [PATCH 07/37] watchdog: npcm: add NPCM7xx watchdog driver Add Nuvoton BMC NPCM7xx watchdog driver. Nuvoton NPCM7xx have three watchdog timer modules, each watchdog timer is a free-running timer with programmable timeout intervals. Signed-off-by: Tomer Maimon --- drivers/watchdog/Kconfig | 11 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/npcm_wdt.c | 254 ++++++++++++++++++++++++++++++++++++ 3 files changed, 266 insertions(+) create mode 100644 drivers/watchdog/npcm_wdt.c diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index c722cbfdc7e64b..fa7870aee02af4 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -504,6 +504,17 @@ config COH901327_WATCHDOG This watchdog is used to reset the system and thus cannot be compiled as a module. +config NPCM7XX_WATCHDOG + bool "Nuvoton NPCM750 watchdog" + depends on ARCH_NPCM || COMPILE_TEST + default y if ARCH_NPCM7XX + select WATCHDOG_CORE + help + Say Y here to include Watchdog timer support for the + watchdog embedded into the NPCM7xx. + This watchdog is used to reset the system and thus cannot be + compiled as a module. + config TWL4030_WATCHDOG tristate "TWL4030 Watchdog" depends on TWL4030_CORE diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 56adf9fa67d04b..af6cee155aafb1 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -60,6 +60,7 @@ obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o obj-$(CONFIG_SUNXI_WATCHDOG) += sunxi_wdt.o obj-$(CONFIG_RN5T618_WATCHDOG) += rn5t618_wdt.o obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o +obj-$(CONFIG_NPCM7XX_WATCHDOG) += npcm_wdt.o obj-$(CONFIG_STMP3XXX_RTC_WATCHDOG) += stmp3xxx_rtc_wdt.o obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o obj-$(CONFIG_TS4800_WATCHDOG) += ts4800_wdt.o diff --git a/drivers/watchdog/npcm_wdt.c b/drivers/watchdog/npcm_wdt.c new file mode 100644 index 00000000000000..0d4213652ecc91 --- /dev/null +++ b/drivers/watchdog/npcm_wdt.c @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018 Nuvoton Technology corporation. +// Copyright (c) 2018 IBM Corp. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NPCM_WTCR 0x1C + +#define NPCM_WTCLK (BIT(10) | BIT(11)) /* Clock divider */ +#define NPCM_WTE BIT(7) /* Enable */ +#define NPCM_WTIE BIT(6) /* Enable irq */ +#define NPCM_WTIS (BIT(4) | BIT(5)) /* Interval selection */ +#define NPCM_WTIF BIT(3) /* Interrupt flag*/ +#define NPCM_WTRF BIT(2) /* Reset flag */ +#define NPCM_WTRE BIT(1) /* Reset enable */ +#define NPCM_WTR BIT(0) /* Reset counter */ + +/* + * Watchdog timeouts + * + * 170 msec: WTCLK=01 WTIS=00 VAL= 0x400 + * 670 msec: WTCLK=01 WTIS=01 VAL= 0x410 + * 1360 msec: WTCLK=10 WTIS=00 VAL= 0x800 + * 2700 msec: WTCLK=01 WTIS=10 VAL= 0x420 + * 5360 msec: WTCLK=10 WTIS=01 VAL= 0x810 + * 10700 msec: WTCLK=01 WTIS=11 VAL= 0x430 + * 21600 msec: WTCLK=10 WTIS=10 VAL= 0x820 + * 43000 msec: WTCLK=11 WTIS=00 VAL= 0xC00 + * 85600 msec: WTCLK=10 WTIS=11 VAL= 0x830 + * 172000 msec: WTCLK=11 WTIS=01 VAL= 0xC10 + * 687000 msec: WTCLK=11 WTIS=10 VAL= 0xC20 + * 2750000 msec: WTCLK=11 WTIS=11 VAL= 0xC30 + */ + +struct npcm_wdt { + struct watchdog_device wdd; + void __iomem *reg; +}; + +static inline struct npcm_wdt *to_npcm_wdt(struct watchdog_device *wdd) +{ + return container_of(wdd, struct npcm_wdt, wdd); +} + +static int npcm_wdt_ping(struct watchdog_device *wdd) +{ + struct npcm_wdt *wdt = to_npcm_wdt(wdd); + u32 val; + + val = readl(wdt->reg); + writel(val | NPCM_WTR, wdt->reg); + + return 0; +} + +static int npcm_wdt_start(struct watchdog_device *wdd) +{ + struct npcm_wdt *wdt = to_npcm_wdt(wdd); + u32 val; + + if (wdd->timeout < 2) + val = 0x800; + else if (wdd->timeout < 3) + val = 0x420; + else if (wdd->timeout < 6) + val = 0x810; + else if (wdd->timeout < 11) + val = 0x430; + else if (wdd->timeout < 22) + val = 0x820; + else if (wdd->timeout < 44) + val = 0xC00; + else if (wdd->timeout < 87) + val = 0x830; + else if (wdd->timeout < 173) + val = 0xC10; + else if (wdd->timeout < 688) + val = 0xC20; + else + val = 0xC30; + + val |= NPCM_WTRE | NPCM_WTE | NPCM_WTR | NPCM_WTIE; + + writel(val, wdt->reg); + + return 0; +} + +static int npcm_wdt_stop(struct watchdog_device *wdd) +{ + struct npcm_wdt *wdt = to_npcm_wdt(wdd); + + writel(0, wdt->reg); + + return 0; +} + + +static int npcm_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + if (timeout < 2) + wdd->timeout = 1; + else if (timeout < 3) + wdd->timeout = 2; + else if (timeout < 6) + wdd->timeout = 5; + else if (timeout < 11) + wdd->timeout = 10; + else if (timeout < 22) + wdd->timeout = 21; + else if (timeout < 44) + wdd->timeout = 43; + else if (timeout < 87) + wdd->timeout = 86; + else if (timeout < 173) + wdd->timeout = 172; + else if (timeout < 688) + wdd->timeout = 687; + else + wdd->timeout = 2750; + + if (watchdog_active(wdd)) + npcm_wdt_start(wdd); + + return 0; +} + +static irqreturn_t npcm_wdt_interrupt(int irq, void *data) +{ + struct npcm_wdt *wdt = data; + + watchdog_notify_pretimeout(&wdt->wdd); + + return IRQ_HANDLED; +} + +static int npcm_wdt_restart(struct watchdog_device *wdd, + unsigned long action, void *data) +{ + struct npcm_wdt *wdt = to_npcm_wdt(wdd); + + writel(NPCM_WTR | NPCM_WTRE | NPCM_WTE, wdt->reg); + udelay(1000); + + return 0; +} + +static bool npcm_is_running(struct watchdog_device *wdd) +{ + struct npcm_wdt *wdt = to_npcm_wdt(wdd); + + return readl(wdt->reg) & NPCM_WTE; +} + +static const struct watchdog_info npcm_wdt_info = { + .identity = KBUILD_MODNAME, + .options = WDIOF_SETTIMEOUT + | WDIOF_KEEPALIVEPING + | WDIOF_MAGICCLOSE, +}; + +static const struct watchdog_ops npcm_wdt_ops = { + .owner = THIS_MODULE, + .start = npcm_wdt_start, + .stop = npcm_wdt_stop, + .ping = npcm_wdt_ping, + .set_timeout = npcm_wdt_set_timeout, + .restart = npcm_wdt_restart, +}; + +static int npcm_wdt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct npcm_wdt *wdt; + struct resource *res; + int irq; + int ret; + + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + wdt->reg = devm_ioremap_resource(dev, res); + if (IS_ERR(wdt->reg)) + return PTR_ERR(wdt->reg); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + wdt->wdd.info = &npcm_wdt_info; + wdt->wdd.ops = &npcm_wdt_ops; + wdt->wdd.min_timeout = 1; + wdt->wdd.max_timeout = 2750; + wdt->wdd.parent = dev; + + wdt->wdd.timeout = 86; + watchdog_init_timeout(&wdt->wdd, 0, dev); + + /* Ensure timeout is able to be represented by the hardware */ + npcm_wdt_set_timeout(&wdt->wdd, wdt->wdd.timeout); + + if (npcm_is_running(&wdt->wdd)) { + /* Restart with the default or device-tree specified timeout */ + npcm_wdt_start(&wdt->wdd); + set_bit(WDOG_HW_RUNNING, &wdt->wdd.status); + } + + ret = devm_request_irq(dev, irq, npcm_wdt_interrupt, 0, + "watchdog", wdt); + if (ret) + return ret; + + ret = devm_watchdog_register_device(dev, &wdt->wdd); + if (ret) { + dev_err(dev, "failed to register watchdog\n"); + return ret; + } + + dev_info(dev, "NPCM watchdog driver enabled\n"); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id npcm_wdt_match[] = { + {.compatible = "nuvoton,npcm750-wdt"}, + {}, +}; +MODULE_DEVICE_TABLE(of, npcm_wdt_match); +#endif + +static struct platform_driver npcm_wdt_driver = { + .probe = npcm_wdt_probe, + .driver = { + .name = "npcm-wdt", + .of_match_table = of_match_ptr(npcm_wdt_match), + }, +}; +module_platform_driver(npcm_wdt_driver); + +MODULE_AUTHOR("Joel Stanley"); +MODULE_DESCRIPTION("Watchdog driver for NPCM"); +MODULE_LICENSE("GPL v2"); From b24196e0d903d163013e13f8312487e892ebe0ca Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Tue, 23 Jan 2018 17:00:41 +0200 Subject: [PATCH 08/37] dt-binding: serial: document NPCM UART DT bindings Added device tree binding documentation for Nuvoton NPCM UART. Signed-off-by: Tomer Maimon --- .../bindings/serial/nuvoton,npcm-uart.txt | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 Documentation/devicetree/bindings/serial/nuvoton,npcm-uart.txt diff --git a/Documentation/devicetree/bindings/serial/nuvoton,npcm-uart.txt b/Documentation/devicetree/bindings/serial/nuvoton,npcm-uart.txt new file mode 100644 index 00000000000000..deb663c58002f5 --- /dev/null +++ b/Documentation/devicetree/bindings/serial/nuvoton,npcm-uart.txt @@ -0,0 +1,34 @@ +Nuvoton NPCM Universal Asynchronous Receiver/Transmitter (UART) + +Required properties: +- compatible : "nuvoton,npcm750-uart" for Poleg NPCM750. +- reg : Offset and length of the register set for the device. +- interrupts : Contain the UART interrupt with flags for falling edge. + +Required clocking property, have to be one of: +- clocks : phandle of UART reference clock. +- clock-frequency : The frequency in Hz of the clock that drives the NPCM + UART (usually 24000000). + +Optional properties: +- reg-shift : quantity to shift the register offsets by (default 2). +- fifo-size : the fifo size of the UART (default 1). + +Note: Each uart controller should have an alias correctly numbered +in "aliases" node. + +Example: + +aliases { + serial0 = &serial0; +}; + +serial0: serial0@f0001000 { + compatible = "nuvoton,npcm750-uart"; + reg = <0xf0001000 0x1000>; + clocks = <&clk NPCM7XX_CLK_UART_CORE>; + interrupts = ; + reg-shift = <2>; + fifo-size = <14>; + status = "disabled"; +}; \ No newline at end of file From b966d8e31a5f977d6737bf3dd7e6e5fa98aec72d Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Tue, 23 Jan 2018 17:03:03 +0200 Subject: [PATCH 09/37] serial: npcm: add NPCM UART driver Add Nuvoton BMC NPCM UART driver. The NPCM7xx BMC contains four UART blocks and accessory logic. NPCM UART based on 8250 driver. Signed-off-by: Tomer Maimon Remove NPCM UART based on 8250 driver. and add to 8250 driver support to NPCM7xx serial Signed-off-by: Tomer Maimon --- drivers/tty/serial/8250/8250_of.c | 1 + drivers/tty/serial/8250/8250_port.c | 33 +++++++++++++++++++++++++++++ include/uapi/linux/serial_core.h | 3 +++ 3 files changed, 37 insertions(+) diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index 0cf95fddccfcd9..a947e1e36930b4 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -298,6 +298,7 @@ static const struct of_device_id of_platform_serial_table[] = { { .compatible = "mrvl,mmp-uart", .data = (void *)PORT_XSCALE, }, { .compatible = "ti,da830-uart", .data = (void *)PORT_DA830, }, + { .compatible = "nuvoton,npcm750-uart", .data = (void *)PORT_NPCM, }, { /* end of list */ }, }; MODULE_DEVICE_TABLE(of, of_platform_serial_table); diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index a5fe0e66c60725..fe30f46f7c9ad2 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -51,6 +51,10 @@ #define UART_EXAR_SLEEP 0x8b /* Sleep mode */ #define UART_EXAR_DVID 0x8d /* Device identification */ +/* Nuvoton NPCM timeout register */ +#define UART_NPCM_TOR 7 +#define UART_NPCM_TOIE BIT(7) /* Timeout Interrupt Enable */ + /* * Debugging. */ @@ -289,6 +293,15 @@ static const struct serial8250_config uart_config[] = { .rxtrig_bytes = {1, 4, 8, 14}, .flags = UART_CAP_FIFO | UART_CAP_AFE, }, + [PORT_NPCM] = { + .name = "Nuvoton 16550", + .fifo_size = 16, + .tx_loadsz = 16, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT, + .rxtrig_bytes = {1, 4, 8, 14}, + .flags = UART_CAP_FIFO, + }, }; /* Uart divisor latch read */ @@ -2143,6 +2156,15 @@ int serial8250_do_startup(struct uart_port *port) UART_DA830_PWREMU_MGMT_FREE); } + if (port->type == PORT_NPCM) { + /* + * Nuvoton calls the scratch register 'UART_TOR' (timeout + * register). Enable it, and set TIOC (timeout interrupt + * comparator) to be 0x20 for correct operation. + */ + serial_port_out(port, UART_NPCM_TOR, UART_NPCM_TOIE | 0x20); + } + #ifdef CONFIG_SERIAL_8250_RSA /* * If this is an RSA port, see if we can kick it up to the @@ -2465,6 +2487,15 @@ static unsigned int xr17v35x_get_divisor(struct uart_8250_port *up, return quot_16 >> 4; } +/* Nuvoton NPCM UARTs have a custom divisor calculation */ +static unsigned int npcm_get_divisor(struct uart_8250_port *up, + unsigned int baud) +{ + struct uart_port *port = &up->port; + + return DIV_ROUND_CLOSEST(port->uartclk, 16 * baud + 2) - 2; +} + static unsigned int serial8250_get_divisor(struct uart_8250_port *up, unsigned int baud, unsigned int *frac) @@ -2485,6 +2516,8 @@ static unsigned int serial8250_get_divisor(struct uart_8250_port *up, quot = 0x8002; else if (up->port.type == PORT_XR17V35X) quot = xr17v35x_get_divisor(up, baud, frac); + else if (up->port.type == PORT_NPCM) + quot = npcm_get_divisor(up, baud); else quot = uart_get_divisor(port, baud); diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index c34a2a3eeff568..ae23b3e22048e5 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -76,6 +76,9 @@ #define PORT_SUNZILOG 38 #define PORT_SUNSAB 39 +/* Nuvoton UART */ +#define PORT_NPCM 40 + /* DEC */ #define PORT_DZ 46 #define PORT_ZS 47 From 599c3e9214152c653578b1a53e7b3f72012327df Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Sun, 7 Jan 2018 10:38:27 +0200 Subject: [PATCH 10/37] dt-binding: iio: document NPCM7xx ADC DT bindings Added device tree binding documentation for Nuvoton NPCM7xx ADC. Signed-off-by: Tomer Maimon --- .../bindings/iio/adc/npcm7xx-adc.txt | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/npcm7xx-adc.txt diff --git a/Documentation/devicetree/bindings/iio/adc/npcm7xx-adc.txt b/Documentation/devicetree/bindings/iio/adc/npcm7xx-adc.txt new file mode 100644 index 00000000000000..c4eb433563b687 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/npcm7xx-adc.txt @@ -0,0 +1,23 @@ +Nuvoton NPCM7XX Analog to Digital Converter (ADC) + +The NPCM7XX ADC is a 10-bit converter for eight channel inputs, +The ADC module includes an eight-to-one multiplexer. + +Required properties: +- compatible : "nuvoton,npcm750-adc" for Poleg NPCM750. +- reg : Offset and length of the register set for the device. + +Required clocking property, have to be one of: +- clocks : phandle of timer reference clock. +- clock-names : Must contain "clk_adc", matching entry in the clocks property. +- vref : ADC Reference voltage, defualt 2048. + +Example: + +adc: adc@f000c000 { + compatible = "nuvoton,npcm750-adc"; + reg = <0xf000c000 0x1000>; + clocks = <&clk NPCM7XX_CLK_ADC>; + clock-names = "clk_adc"; + vref = <2048>; +}; From 59140f343763ce52404d3bac0b4dd5d3959249ab Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Sun, 7 Jan 2018 10:44:40 +0200 Subject: [PATCH 11/37] ADC: npcm: add NPCM7xx ADC driver Add Nuvoton BMC NPCM7xx Analog-to-digital converter (ADC) driver The NPCM7xx ADC is a 10-bit converter for eight channel inputs, the ADC module includes an eight-to-one multiplexer. Signed-off-by: Tomer Maimon --- drivers/iio/adc/Kconfig | 9 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/npcm7xx-adc.c | 352 ++++++++++++++++++++++++++++++++++ 3 files changed, 362 insertions(+) create mode 100644 drivers/iio/adc/npcm7xx-adc.c diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 614fa41559b130..77705fde21ca9e 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -857,4 +857,13 @@ config XILINX_XADC The driver can also be build as a module. If so, the module will be called xilinx-xadc. +config NPCM7XX_ADC + tristate "NPCM7XX ADC driver" + depends on ARCH_NPCM7XX || COMPILE_TEST + depends on HAS_IOMEM + help + Say yes here to build support for NPCM7XX ADC. + This driver can also be built as a module. If so, the module will be + called npcm750_adc. + endmenu diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index b546736a55413a..871074d7b0cd2e 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -78,3 +78,4 @@ obj-$(CONFIG_VF610_ADC) += vf610_adc.o obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o xilinx-xadc-y := xilinx-xadc-core.o xilinx-xadc-events.o obj-$(CONFIG_XILINX_XADC) += xilinx-xadc.o +obj-$(CONFIG_NPCM7XX_ADC) += npcm7xx-adc.o diff --git a/drivers/iio/adc/npcm7xx-adc.c b/drivers/iio/adc/npcm7xx-adc.c new file mode 100644 index 00000000000000..ee218a39598225 --- /dev/null +++ b/drivers/iio/adc/npcm7xx-adc.c @@ -0,0 +1,352 @@ +/* + * Copyright (c) 2014-2017 Nuvoton Technology corporation. + * + * Released under the GPLv2 only. + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +static struct regmap *rst_regmap; + +#define IPSRST1_OFFSET 0x020 + +struct npcm7xx_adc { + struct device *dev; + void __iomem *regs; + struct clk *adc_clk; + u32 vref; + u32 adc_clk_rate; + u32 ADCReading; + u8 ADCChannelNum; +}; + +/* ADC registers */ +#define NPCM7XX_ADCCON 0x00 +#define NPCM7XX_ADCDATA 0x04 + +/* ADCCON Register Bits */ +#define NPCM7XX_ADCCON_ADC_INT_EN BIT(21) +#define NPCM7XX_ADCCON_REFSEL BIT(19) +#define NPCM7XX_ADCCON_ADC_INT BIT(18) +#define NPCM7XX_ADCCON_ADC_EN BIT(17) +#define NPCM7XX_ADCCON_ADC_RST BIT(16) +#define NPCM7XX_ADCCON_ADC_CONV BIT(13) + +#define NPCM7XX_ADCCON_ADCMUX(x) (((x) & 0x0F)<<24) +#define NPCM7XX_ADCCON_ADC_DIV(x) (((x) & 0xFF)<<24) +#define NPCM7XX_ADCCON_ADC_DATA_MASK(x) ((x) & 0x3FF) +#define NPCM7XX_ADCCON_MUXMASK (0x0F<<24) + +/* ADC General Defintion */ +#define NPCM7XX_ADC_INPUT_CLK_DIV 0 +#define NPCM7XX_ADC_CONVERT_MAX_RETRY_CNT 1000 + +#define NPCM7XX_ADC_MAX_CHNL_NUM 8 + +#define NPCM7XX_ADC_CHNL0_ADCI0 0 +#define NPCM7XX_ADC_CHNL1_ADCI1 1 +#define NPCM7XX_ADC_CHNL2_ADCI2 2 +#define NPCM7XX_ADC_CHNL3_ADCI3 3 +#define NPCM7XX_ADC_CHNL4_ADCI4 4 +#define NPCM7XX_ADC_CHNL5_ADCI5 5 +#define NPCM7XX_ADC_CHNL6_ADCI6 6 +#define NPCM7XX_ADC_CHNL7_ADCI7 7 + +#define ADC_MAX_CLOCK 12500000 +#define VREF_MVOLT 2048 //vref = 2.000v + +#define NPCM7XX_ADC_CHAN(_idx) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_idx), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ +} + +static const struct iio_chan_spec npcm7xx_adc_iio_channels[] = { + NPCM7XX_ADC_CHAN(0), + NPCM7XX_ADC_CHAN(1), + NPCM7XX_ADC_CHAN(2), + NPCM7XX_ADC_CHAN(3), + NPCM7XX_ADC_CHAN(4), + NPCM7XX_ADC_CHAN(5), + NPCM7XX_ADC_CHAN(6), + NPCM7XX_ADC_CHAN(7), +}; + +//#define ADC_DEBUG + +#ifdef ADC_DEBUG +static char *S_ADCChnlString[] = { + "ADCI0", "ADCI1", "ADCI2", "ADCI3", "ADCI4", "ADCI5", "ADCI6", "ADCI7" +}; +#define PDEBUG(fmt, args...) pr_info("aess_adcdrv %s() " fmt, __func__, ##args) +#else +#define PDEBUG(fmt, args...) +#endif +#define PERROR(fmt, args...) pr_err("aess_adcdrv %s(): " fmt, __func__, ##args) + +static int adcsensor_read(struct npcm7xx_adc *info) +{ + u8 u8ChannelNum = info->ADCChannelNum; + u32 regtemp = 0; + int cnt = 0; + + /* Select ADC channal */ + regtemp = ioread32(info->regs + NPCM7XX_ADCCON); + regtemp &= ~NPCM7XX_ADCCON_MUXMASK; + + iowrite32((u32) (regtemp | NPCM7XX_ADCCON_ADCMUX(u8ChannelNum) | + NPCM7XX_ADCCON_ADC_EN | NPCM7XX_ADCCON_REFSEL), + info->regs + NPCM7XX_ADCCON); + + /* Activate convert the ADC input */ + regtemp = ioread32(info->regs + NPCM7XX_ADCCON); + iowrite32((u32) (regtemp | NPCM7XX_ADCCON_ADC_CONV), + info->regs + NPCM7XX_ADCCON); + + /* Wait value */ + while (((regtemp = ioread32(info->regs + NPCM7XX_ADCCON)) & + NPCM7XX_ADCCON_ADC_CONV) != 0) { + if (cnt < NPCM7XX_ADC_CONVERT_MAX_RETRY_CNT) + cnt++; + else { + PERROR("ADC CONVERT FAIL - Timeout\n"); + PERROR("NPCM7XX_ADCCON=0x%08X, ADC_MUX=%d u8ChannelNum=" + "%d!!\n", regtemp, (regtemp>>24)&0xF, + u8ChannelNum); + if (((regtemp>>24) & 0xF) != u8ChannelNum) + PERROR("ADC_MUX != u8ChannelNum, I suspect that" + " 2 threads are trying to access this " + "read and it is not protected " + "by mutex\n"); + + /* if convertion failed - reset ADC module */ + regmap_write(rst_regmap, IPSRST1_OFFSET, 0x08000000); + msleep(100); + regmap_write(rst_regmap, IPSRST1_OFFSET, 0x0); + msleep(100); + PERROR("RESET ADC Complete\n"); + return (-EAGAIN); + } + } + +/* When an ADC conversion operation finished, a delay must be added before + * the next conversion operation. + * The delay depend on the ADC clock: + * When ADC clock is 0.5 MHz: delay is 4 us. + * When ADC clock is 12.5 MHz: delay is 160 ns. + * + * In the current driver the ADC clock is 12.5MHz, so delay is not needed. + * (already the R/W register take more than 160ns) + * If the ADC clock will be lower than 12.5MHz please add delay according + * the details above + * udelay(conv_delay); + */ + + /* finish to convert */ + info->ADCReading = NPCM7XX_ADCCON_ADC_DATA_MASK + (ioread32(info->regs + NPCM7XX_ADCDATA)); + + PDEBUG("[%d_%s] ADCReading=%ld [%ldmV]\n", + u8ChannelNum, S_ADCChnlString[u8ChannelNum], + (long int)info->ADCReading, + (long int)(info->ADCReading * info->vref / 1024)); + + return 0; +} + +static int npcm7xx_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, + long mask) +{ + int err_check; + struct npcm7xx_adc *info = iio_priv(indio_dev); + + info->ADCChannelNum = chan->channel; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + + switch (info->ADCChannelNum) { + case NPCM7XX_ADC_CHNL0_ADCI0: + case NPCM7XX_ADC_CHNL1_ADCI1: + case NPCM7XX_ADC_CHNL2_ADCI2: + case NPCM7XX_ADC_CHNL3_ADCI3: + case NPCM7XX_ADC_CHNL4_ADCI4: + case NPCM7XX_ADC_CHNL5_ADCI5: + case NPCM7XX_ADC_CHNL6_ADCI6: + case NPCM7XX_ADC_CHNL7_ADCI7: + mutex_lock(&indio_dev->mlock); + err_check = adcsensor_read(info); + PDEBUG("%d = aess_adcsensor_read()\n", err_check); + if (err_check) { + PERROR("err_check %d\n", err_check); + mutex_unlock(&indio_dev->mlock); + return err_check; + } + *val = info->ADCReading; + mutex_unlock(&indio_dev->mlock); + return IIO_VAL_INT; + default: + PERROR("aess_adcsensor_ioctl, Unsupport channel number" + " [%d]!\n", info->ADCChannelNum); + err_check = -ENODEV; + break; + } + break; + + default: + PERROR("aess_adcsensor_ioctl, command error!!!\n"); + err_check = -EINVAL; + } + + /* 0->ok, minus->fail */ + return err_check; +} + +static const struct iio_info npcm7xx_adc_iio_info = { + .driver_module = THIS_MODULE, + .read_raw = &npcm7xx_adc_read_raw, +}; + +static const struct of_device_id npcm7xx_adc_match[] = { + { .compatible = "nuvoton,npcm750-adc", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, npcm7xx_adc_match); + + +static int npcm7xx_adc_probe(struct platform_device *pdev) +{ + struct npcm7xx_adc *info; + struct iio_dev *indio_dev; + struct resource *mem; + struct device *dev = &pdev->dev; + int ret; + u32 regtemp = 0; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info)); + if (!indio_dev) { + dev_err(&pdev->dev, "Failed allocating iio device\n"); + return -ENOMEM; + } + + info = iio_priv(indio_dev); + info->dev = &pdev->dev; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + info->regs = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(info->regs)) { + ret = PTR_ERR(info->regs); + dev_err(&pdev->dev, "Failed to remap adc memory, err = %d\n", + ret); + return ret; + } + + ret = of_property_read_u32(dev->of_node, "vref", &info->vref); + if (ret) { + dev_err(&pdev->dev, "Failed getting reference voltage, Assuming" + " reference voltage 2V(2048)\n"); + info->vref = VREF_MVOLT; + ret = 0; + } + + info->adc_clk = devm_clk_get(&pdev->dev, "clk_adc"); + if (IS_ERR(info->adc_clk)) { + dev_err(&pdev->dev, "ADC clock failed: can't read clk. " + "Assuming ADC clock Rate 12.5MHz\n"); + info->adc_clk_rate = ADC_MAX_CLOCK; + } else { + /* calculate ADC clock divider */ + regtemp = ioread32(info->regs + NPCM7XX_ADCCON); + regtemp = regtemp >> 1; + regtemp &= 0xff; + + info->adc_clk_rate = clk_get_rate(info->adc_clk) / + ((regtemp+1)*2); + } + + rst_regmap = syscon_regmap_lookup_by_compatible("nuvoton,npcm750-rst"); + if (IS_ERR(rst_regmap)) { + pr_err("%s: failed to find nuvoton,npcm750-rst\n", __func__); + return IS_ERR(rst_regmap); + } + + pr_info("ADC clock Rate %d\n", info->adc_clk_rate); + + /** Enable the ADC Module **/ + iowrite32((u32) NPCM7XX_ADCCON_ADC_EN, info->regs + NPCM7XX_ADCCON); + + platform_set_drvdata(pdev, indio_dev); + + indio_dev->name = dev_name(&pdev->dev); + indio_dev->dev.parent = &pdev->dev; + indio_dev->info = &npcm7xx_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = npcm7xx_adc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(npcm7xx_adc_iio_channels); + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "Couldn't register the device.\n"); + clk_disable_unprepare(info->adc_clk); + return ret; + } + + pr_info("NPCM7XX ADC driver probed\n"); + + return 0; +} + +static int npcm7xx_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct npcm7xx_adc *info = iio_priv(indio_dev); + u32 regtemp = 0; + + regtemp = ioread32(info->regs + NPCM7XX_ADCCON); + + /* Disable the ADC Module */ + iowrite32((u32) (regtemp & ~NPCM7XX_ADCCON_ADC_EN), + info->regs + NPCM7XX_ADCCON); + + iio_device_unregister(indio_dev); + + pr_info("NPCM7XX ADC driver removed\n"); + + return 0; +} + +static struct platform_driver npcm7xx_adc_driver = { + .probe = npcm7xx_adc_probe, + .remove = npcm7xx_adc_remove, + .driver = { + .name = "npcm7xx_adc", + .of_match_table = npcm7xx_adc_match, + }, +}; + +module_platform_driver(npcm7xx_adc_driver); + +MODULE_DESCRIPTION("NPCM7XX ADC Sensor Driver"); +MODULE_AUTHOR("Tomer Maimon "); +MODULE_LICENSE("GPL v2"); From 14e70f8aba03860e471e2504bb44439c677fd8b9 Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Sun, 7 Jan 2018 11:11:52 +0200 Subject: [PATCH 12/37] dt-binding: mtd: document NPCM7xx SPI DT bindings Added device tree binding documentation for Nuvoton NPCM7xx Serial Peripheral Interface (SPI) NOR Signed-off-by: Tomer Maimon --- .../devicetree/bindings/mtd/npcm-spi.txt | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 Documentation/devicetree/bindings/mtd/npcm-spi.txt diff --git a/Documentation/devicetree/bindings/mtd/npcm-spi.txt b/Documentation/devicetree/bindings/mtd/npcm-spi.txt new file mode 100644 index 00000000000000..3c30271c3712c2 --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/npcm-spi.txt @@ -0,0 +1,31 @@ +* Nuvoton Serial Peripheral Interface(SPI) + +Required properties: + - compatible : "nuvoton,npcm750-spi" for NPCM750 BMC + - #address-cells : should be 1. + - #size-cells : should be 0. + - reg : the first contains the register location and length, + the second contains the memory mapping address and length + - reg-names: Should contain the reg names "control" and "memory" + - clocks : phandle of SPI reference clock. + +Optional properties: + - chip-max-address-map: Chip maximum address mapping for direct use. + the maximum address map size: + NPCM7xx - 0x8000000 (128Mb) + +Example: + +spi0: spi@fb000000 { + compatible = "nuvoton,npcm750-spi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0xfb000000 0x1000>, <0x80000000 0x10000000>; + reg-names = "control", "memory"; + chip-max-address-map = <0x8000000>; + clocks = <&clk NPCM7XX_CLK_AHB>; + spi-nor@0 { + ... + }; +}; + From cc071c9529554ab1853a66fda8366b451ecfcd02 Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Sun, 7 Jan 2018 11:19:45 +0200 Subject: [PATCH 13/37] mtd: npcm: add NPCM7xx spi driver Add Nuvoton BMC NPCM7xx serial Peripheral interface (SPI) Flash interface unit (FIU) driver Nuvoton NPCM7xx have two SPI nor modules, first module support 2 chip select second module support 4 chip select Signed-off-by: Tomer Maimon --- drivers/mtd/spi-nor/Kconfig | 7 + drivers/mtd/spi-nor/Makefile | 3 +- drivers/mtd/spi-nor/npcm-spi.c | 1217 ++++++++++++++++++++++++++++++++ 3 files changed, 1226 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/spi-nor/npcm-spi.c diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index 293c8a4d1e4966..f23a6a77d4661a 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -113,4 +113,11 @@ config SPI_STM32_QUADSPI This enables support for the STM32 Quad SPI controller. We only connect the NOR to this controller. +config SPI_NPCM + tristate "NPCM SPI controller" + depends on ARCH_NPCM || COMPILE_TEST + help + This enables support for the SPI controller in master mode. + We only connect the NOR to this controller now. + endif # MTD_SPI_NOR diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index 285aab86c7ca14..728de1e3d478cf 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -8,4 +8,5 @@ obj-$(CONFIG_MTD_MT81xx_NOR) += mtk-quadspi.o obj-$(CONFIG_SPI_NXP_SPIFI) += nxp-spifi.o obj-$(CONFIG_SPI_INTEL_SPI) += intel-spi.o obj-$(CONFIG_SPI_INTEL_SPI_PLATFORM) += intel-spi-platform.o -obj-$(CONFIG_SPI_STM32_QUADSPI) += stm32-quadspi.o \ No newline at end of file +obj-$(CONFIG_SPI_STM32_QUADSPI) += stm32-quadspi.o +obj-$(CONFIG_SPI_NPCM) += npcm-spi.o diff --git a/drivers/mtd/spi-nor/npcm-spi.c b/drivers/mtd/spi-nor/npcm-spi.c new file mode 100644 index 00000000000000..e69a79d7b49209 --- /dev/null +++ b/drivers/mtd/spi-nor/npcm-spi.c @@ -0,0 +1,1217 @@ +/* + * Copyright (c) 2014-2017 Nuvoton Technology corporation. + * + * Released under the GPLv2 only. + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +typedef enum { + FIU_MODULE_0 = 0, + FIU_MODULE_3 = 1, + FIU_MODULE_X = 2, + FIU_MAX_MODULE_NUM = 3 +} FIU_MODULE_T; + +typedef struct bit_field { + u8 offset; + u8 size; +} bit_field_t; + +/* + * Flash Interface Unit (FIU) Registers + */ + +/* + * FIU Direct Read Configuration Register + */ +#define FIU_DRD_CFG(n) (fiu_base[n] + 0x00) +static const bit_field_t FIU_DRD_CFG_R_BURST = { 24, 2 }; +static const bit_field_t FIU_DRD_CFG_ADDSIZ = { 16, 2 }; +static const bit_field_t FIU_DRD_CFG_DBW = { 12, 2 }; +static const bit_field_t FIU_DRD_CFG_ACCTYPE = { 8, 2 }; +static const bit_field_t FIU_DRD_CFG_RDCMD = { 0, 8 }; + +/* + * FIU Direct Write Configuration Register + */ +#define FIU_DWR_CFG(n) (fiu_base[n] + 0x04) +static const bit_field_t FIU_DWR_CFG_LCK = { 31, 1 }; +static const bit_field_t FIU_DWR_CFG_W_BURST = { 24, 2 }; +static const bit_field_t FIU_DWR_CFG_ADDSIZ = { 16, 2 }; +static const bit_field_t FIU_DWR_CFG_ABPCK = { 10, 2 }; +static const bit_field_t FIU_DWR_CFG_DBPCK = { 8, 2 }; +static const bit_field_t FIU_DWR_CFG_WRCMD = { 0, 8 }; + +/* + * FIU UMA Configuration Register + */ +#define FIU_UMA_CFG(n) (fiu_base[n] + 0x08) +static const bit_field_t FIU_UMA_CFG_LCK = { 31, 1 }; +static const bit_field_t FIU_UMA_CFG_CMMLCK = { 30, 1 }; +static const bit_field_t FIU_UMA_CFG_RDATSIZ = { 24, 5 }; +static const bit_field_t FIU_UMA_CFG_DBSIZ = { 21, 3 }; +static const bit_field_t FIU_UMA_CFG_WDATSIZ = { 16, 5 }; +static const bit_field_t FIU_UMA_CFG_ADDSIZ = { 11, 3 }; +static const bit_field_t FIU_UMA_CFG_CMDSIZ = { 10, 1 }; +static const bit_field_t FIU_UMA_CFG_RDBPCK = { 8, 2 }; +static const bit_field_t FIU_UMA_CFG_DBPCK = { 6, 2 }; +static const bit_field_t FIU_UMA_CFG_WDBPCK = { 4, 2 }; +static const bit_field_t FIU_UMA_CFG_ADBPCK = { 2, 2 }; +static const bit_field_t FIU_UMA_CFG_CMBPCK = { 0, 2 }; + +/* + * FIU UMA Control and Status Register + */ +#define FIU_UMA_CTS(n) (fiu_base[n] + 0x0C) +static const bit_field_t FIU_UMA_CTS_RDYIE = { 25, 1}; +static const bit_field_t FIU_UMA_CTS_RDYST = { 24, 1}; +static const bit_field_t FIU_UMA_CTS_SW_CS = { 16, 1}; +static const bit_field_t FIU_UMA_CTS_DEV_NUM = { 8, 2 }; +static const bit_field_t FIU_UMA_CTS_EXEC_DONE = { 0, 1 }; + +/* + * FIU UMA Command Register + */ +#define FIU_UMA_CMD(n) (fiu_base[n] + 0x10) +static const bit_field_t FIU_UMA_CMD_DUM3 = { 24, 8}; +static const bit_field_t FIU_UMA_CMD_DUM2 = { 16, 8}; +static const bit_field_t FIU_UMA_CMD_DUM1 = { 8, 8 }; +static const bit_field_t FIU_UMA_CMD_CMD = { 0, 8 }; + +/* + * FIU UMA Address Register + */ +#define FIU_UMA_ADDR(n) (fiu_base[n] + 0x14) +static const bit_field_t FIU_UMA_ADDR_UMA_ADDR = { 0, 32}; +static const bit_field_t FIU_UMA_ADDR_AB3 = { 24, 8}; +static const bit_field_t FIU_UMA_ADDR_AB2 = { 16, 8}; +static const bit_field_t FIU_UMA_ADDR_AB1 = { 8, 8 }; +static const bit_field_t FIU_UMA_ADDR_AB0 = { 0, 8 }; + +/* + * FIU UMA Write Data Bytes 0-3 Register + */ +#define FIU_UMA_DW0(n) (fiu_base[n] + 0x20) +static const bit_field_t FIU_UMA_DW0_WB3 = { 24, 8}; +static const bit_field_t FIU_UMA_DW0_WB2 = { 16, 8}; +static const bit_field_t FIU_UMA_DW0_WB1 = { 8, 8 }; +static const bit_field_t FIU_UMA_DW0_WB0 = { 0, 8 }; + +/* + * FIU UMA Write Data Bytes 4-7 Register + */ +#define FIU_UMA_DW1(n) (fiu_base[n] + 0x24) +static const bit_field_t FIU_UMA_DW1_WB7 = { 24, 8}; +static const bit_field_t FIU_UMA_DW1_WB6 = { 16, 8}; +static const bit_field_t FIU_UMA_DW1_WB5 = { 8, 8 }; +static const bit_field_t FIU_UMA_DW1_WB4 = { 0, 8 }; + +/* + * FIU UMA Write Data Bytes 8-11 Register + */ +#define FIU_UMA_DW2(n) (fiu_base[n] + 0x28) +static const bit_field_t FIU_UMA_DW2_WB11 = { 24, 8}; +static const bit_field_t FIU_UMA_DW2_WB10 = { 16, 8}; +static const bit_field_t FIU_UMA_DW2_WB9 = { 8, 8 }; +static const bit_field_t FIU_UMA_DW2_WB8 = { 0, 8 }; + +/* + * FIU UMA Write Data Bytes 12-15 Register + */ +#define FIU_UMA_DW3(n) (fiu_base[n] + 0x2C) +static const bit_field_t FIU_UMA_DW3_WB15 = { 24, 8}; +static const bit_field_t FIU_UMA_DW3_WB14 = { 16, 8}; +static const bit_field_t FIU_UMA_DW3_WB13 = { 8, 8 }; +static const bit_field_t FIU_UMA_DW3_WB12 = { 0, 8 }; + +/* + * FIU UMA Read Data Bytes 0-3 Register + */ +#define FIU_UMA_DR0(n) (fiu_base[n] + 0x30) +static const bit_field_t FIU_UMA_DR0_RB3 = { 24, 8}; +static const bit_field_t FIU_UMA_DR0_RB2 = { 16, 8}; +static const bit_field_t FIU_UMA_DR0_RB1 = { 8, 8 }; +static const bit_field_t FIU_UMA_DR0_RB0 = { 0, 8 }; + +/* + * FIU UMA Read Data Bytes 4-7 Register + */ +#define FIU_UMA_DR1(n) (fiu_base[n] + 0x34) +static const bit_field_t FIU_UMA_DR1_RB15 = { 24, 8}; +static const bit_field_t FIU_UMA_DR1_RB14 = { 16, 8}; +static const bit_field_t FIU_UMA_DR1_RB13 = { 8, 8 }; +static const bit_field_t FIU_UMA_DR1_RB12 = { 0, 8 }; + +/* + * FIU UMA Read Data Bytes 8-11 Register + */ +#define FIU_UMA_DR2(n) (fiu_base[n] + 0x38) +static const bit_field_t FIU_UMA_DR2_RB15 = { 24, 8}; +static const bit_field_t FIU_UMA_DR2_RB14 = { 16, 8}; +static const bit_field_t FIU_UMA_DR2_RB13 = { 8, 8 }; +static const bit_field_t FIU_UMA_DR2_RB12 = { 0, 8 }; + +/* + * FIU UMA Read Data Bytes 12-15 Register + */ +#define FIU_UMA_DR3(n) (fiu_base[n] + 0x3C) +static const bit_field_t FIU_UMA_DR3_RB15 = { 24, 8}; +static const bit_field_t FIU_UMA_DR3_RB14 = { 16, 8}; +static const bit_field_t FIU_UMA_DR3_RB13 = { 8, 8 }; +static const bit_field_t FIU_UMA_DR3_RB12 = { 0, 8 }; + +/* + * FIU Protection Configuration Register + */ +#define FIU_PRT_CFG(n) (fiu_base[n] + 0x18) +static const bit_field_t FIU_PRT_CFG_LCK = { 31, 1}; +static const bit_field_t FIU_PRT_CFG_PEN = { 30, 1}; +static const bit_field_t FIU_PRT_CFG_DEVSIZ = { 8, 3 }; +static const bit_field_t FIU_PRT_CFG_OCALWD = { 4, 1 }; +static const bit_field_t FIU_PRT_CFG_PRTASIZ = { 0, 2 }; + +/* + * FIU Protection Command Register + */ +#define FIU_PRT_CMD0(n) (fiu_base[n] + 0x40) +#define FIU_PRT_CMD1(n) (fiu_base[n] + 0x44) +#define FIU_PRT_CMD2(n) (fiu_base[n] + 0x48) +#define FIU_PRT_CMD3(n) (fiu_base[n] + 0x4C) +#define FIU_PRT_CMD4(n) (fiu_base[n] + 0x50) +#define FIU_PRT_CMD5(n) (fiu_base[n] + 0x54) +#define FIU_PRT_CMD6(n) (fiu_base[n] + 0x58) +#define FIU_PRT_CMD7(n) (fiu_base[n] + 0x5C) +#define FIU_PRT_CMD8(n) (fiu_base[n] + 0x60) +#define FIU_PRT_CMD9(n) (fiu_base[n] + 0x64) +static const bit_field_t FIU_PRT_CMD9_ADBPCKB = { 29, 2}; +static const bit_field_t FIU_PRT_CMD9_CMBPCKB = { 27, 2}; +static const bit_field_t FIU_PRT_CMD9_ADDSZB = { 26, 1}; +static const bit_field_t FIU_PRT_CMD9_FRBDCB = { 24, 2}; +static const bit_field_t FIU_PRT_CMD9_CMDB = { 16, 8}; +static const bit_field_t FIU_PRT_CMD9_ADBPCKA = { 13, 2}; +static const bit_field_t FIU_PRT_CMD9_CMBPCKA = { 11, 2}; +static const bit_field_t FIU_PRT_CMD9_ADDSZA = { 10, 1}; +static const bit_field_t FIU_PRT_CMD9_FRBDCA = { 8, 2 }; +static const bit_field_t FIU_PRT_CMD9_CMDA = { 0, 8 }; + +/* + * FIU Status Polling Configuration Register + */ +#define FIU_STPL_CFG(n) (fiu_base[n] + 0x1C) +static const bit_field_t FIU_STPL_CFG_LCK = { 31, 1 }; +static const bit_field_t FIU_STPL_CFG_BUST = { 30, 1 }; +static const bit_field_t FIU_STPL_CFG_RDYIE = { 29, 1 }; +static const bit_field_t FIU_STPL_CFG_RDY = { 28, 1 }; +static const bit_field_t FIU_STPL_CFG_SPDWR = { 27, 1 }; +static const bit_field_t FIU_STPL_CFG_POLLPER = { 16, 11}; +static const bit_field_t FIU_STPL_CFG_ENPOL = { 12, 1 }; +static const bit_field_t FIU_STPL_CFG_BUSYPOL = { 11, 1 }; +static const bit_field_t FIU_STPL_CFG_BUSYBS = { 8, 3 }; +static const bit_field_t FIU_STPL_CFG_CMD = { 0, 8 }; + +/* + * FIU Configuration Register + */ +#define FIU_CFG(n) (fiu_base[n] + 0x78) +static const bit_field_t FIU_CFG_SPI_CS_INACT = { 1, 3}; +static const bit_field_t FIU_CFG_INCRSING = { 0, 1}; + +/* + * FIU Version Register (FIU_VER) updated + */ +#define FIU_VER(n) (fiu_base[n] + 0x7C) +static const bit_field_t FIU_VER_FIU_VER = { 0, 8}; + +/* + * Defines + */ +#define FIU_CAPABILITY_QUAD_READ +#define FIU_CAPABILITY_CHIP_SELECT + +#define WIN_LIMIT_4K_SHIFT 12 +#define BITS_7_0 0xFF +#define BITS_15_8 0xFF00 +#define BITS_23_16 0xFF0000 + + +/* + * Typedef Definitions + */ +typedef enum _spi_w_burst_t { + FIU_W_BURST_ONE_BYTE = 0, + FIU_W_BURST_FOUR_BYTE = 2, + FIU_W_BURST_SIXTEEN_BYTE = 3 +} SPI_w_burst_t; + +typedef enum _spi_r_burst_t { + FIU_R_BURST_ONE_BYTE = 0, + FIU_R_BURST_FOUR_BYTE = 2, + FIU_R_BURST_SIXTEEN_BYTE = 3 +} SPI_r_burst_t; + +typedef enum _spi_w_protect_int_t { + SPI_W_PROTECT_INT_DISABLE = 0, + SPI_W_PROTECT_INT_ENABLE = 1 +} SPI_w_protect_int_t; + +typedef enum _spi_incorect_access_int_t { + SPI_INCORECT_ACCESS_INT_DISABLE = 0, + SPI_INCORECT_ACCESS_INT_ENABLE = 1 +} SPI_incorect_access_int_t; + +/* + * FIU Read Mode + */ +typedef enum _spi_read_mode_t { + FIU_NORMAL_READ = 0, + FIU_FAST_READ = 1, + FIU_FAST_READ_DUAL_OUTPUT = 2, + FIU_FAST_READ_DUAL_IO = 3, + FIU_FAST_READ_QUAD_IO = 4, + FIU_FAST_READ_SPI_X = 5, + FIU_READ_MODE_NUM +} SPI_read_mode_t; + +/* + * FIU UMA data size + */ +typedef enum _spi_uma_data_size_t { + FIU_UMA_DATA_SIZE_0 = 0, + FIU_UMA_DATA_SIZE_1 = 1, + FIU_UMA_DATA_SIZE_2 = 2, + FIU_UMA_DATA_SIZE_3 = 3, + FIU_UMA_DATA_SIZE_4 = 4, + FIU_UMA_DATA_SIZE_5 = 5, + FIU_UMA_DATA_SIZE_6 = 6, + FIU_UMA_DATA_SIZE_7 = 7, + FIU_UMA_DATA_SIZE_8 = 8, + FIU_UMA_DATA_SIZE_9 = 9, + FIU_UMA_DATA_SIZE_10 = 10, + FIU_UMA_DATA_SIZE_11 = 11, + FIU_UMA_DATA_SIZE_12 = 12, + FIU_UMA_DATA_SIZE_13 = 13, + FIU_UMA_DATA_SIZE_14 = 14, + FIU_UMA_DATA_SIZE_15 = 15, + FIU_UMA_DATA_SIZE_16 = 16 +} SPI_uma_data_size_t; + +/* + * FIU Field value enumeration + */ +typedef enum _spi_drd_cfg_addsiz_t { + FIU_DRD_CFG_ADDSIZE_24BIT = 0, + FIU_DRD_CFG_ADDSIZE_32BIT = 1 +} SPI_drd_cfg_addsiz_t; + +typedef enum _spi_trans_status_t { + FIU_TRANS_STATUS_DONE = 0, + FIU_TRANS_STATUS_IN_PROG = 1 +} SPI_trans_status_t; + +/* + * SPI commands + */ +#define NPCM_SPI_RD_STATUS_3_REG_CMD 0x15 +#define NPCM_SPI_EN_RST_CMD 0x66 +#define NPCM_SPI_RST_DEVICE_CMD 0x99 +#define NPCM_SPI_WR_EXT_ADDR_REG_CMD 0xC5 + +#ifdef REG_READ +#undef REG_READ +#endif +static inline u32 REG_READ(unsigned int __iomem *mem) +{ + return ioread32(mem); +} + +#ifdef REG_WRITE +#undef REG_WRITE +#endif +static inline void REG_WRITE(unsigned int __iomem *mem, u32 val) +{ + iowrite32(val, mem); +} + +#ifdef SET_REG_FIELD +#undef SET_REG_FIELD +#endif +/* + * Set field of a register / variable according to the field offset and size + */ +static inline void SET_REG_FIELD(unsigned int __iomem *mem, + bit_field_t bit_field, u32 val) +{ + u32 tmp = ioread32(mem); + + tmp &= ~(((1 << bit_field.size) - 1) << bit_field.offset); + tmp |= val << bit_field.offset; + iowrite32(tmp, mem); +} + +#ifdef SET_VAR_FIELD +#undef SET_VAR_FIELD +#endif +#define SET_VAR_FIELD(var, bit_field, value) { \ + typeof(var) tmp = var; \ + tmp &= ~(((1 << bit_field.size) - 1) << bit_field.offset); \ + tmp |= value << bit_field.offset; \ + var = tmp; \ +} + +#ifdef READ_REG_FIELD +#undef READ_REG_FIELD +#endif +/* + * Get field of a register / variable according to the field offset and size + */ +static inline u8 READ_REG_FIELD(unsigned int __iomem *mem, + bit_field_t bit_field) +{ + u8 tmp = ioread32(mem); + + tmp = tmp >> bit_field.offset; + tmp &= (1 << bit_field.size) - 1; + return tmp; +} + +#ifdef READ_VAR_FIELD +#undef READ_VAR_FIELD +#endif +#define READ_VAR_FIELD(var, bit_field) ({ \ + typeof(var) tmp = var; \ + tmp = tmp >> bit_field.offset; \ + tmp &= (1 << bit_field.size) - 1; \ + tmp; \ +}) + +#ifdef MASK_FIELD +#undef MASK_FIELD +#endif +/* + * Build a mask of a register / variable field + */ +#define MASK_FIELD(bit_field) (((1 << bit_field.size) - 1) << bit_field.offset) + +#ifdef BUILD_FIELD_VAL +#undef BUILD_FIELD_VAL +#endif +/* + * Expand the value of the given field into its correct position + */ +#define BUILD_FIELD_VAL(bit_field, value) \ + ((((1 << bit_field.size) - 1) & (value)) << bit_field.offset) + + +#ifdef SET_REG_MASK +#undef SET_REG_MASK +#endif +/* + * Set field of a register / variable according to the field offset and size + */ +static inline void SET_REG_MASK(unsigned int __iomem *mem, u32 val) +{ + iowrite32(ioread32(mem) | val, mem); +} + +#define DRIVER_NAME "npcm7xx_spinor" + +#define SIZE_16MB 0x1000000 +#define MAX_READY_WAIT_COUNT 1000000 + +#define WRITE_TRANSFER_16_BYTES + +#ifdef WRITE_TRANSFER_16_BYTES +#define CHUNK_SIZE 16 +#endif + +#ifdef WRITE_TRANSFER_17_BYTES +#define CHUNK_SIZE 17 +#endif + +struct npcm7xx_spi_bus; + +struct npcm7xx_chip { + u32 fiu_num; + u32 clkrate; + u32 chipselect; + struct spi_nor nor; + struct npcm7xx_spi_bus *host; +}; + +#define NPCM7XX_MAX_CHIP_NUM 4 + +struct npcm7xx_spi_bus { + struct device *dev; + struct mutex lock; + + void __iomem *regbase; + struct resource *res_mem; + u32 MaxChipAddMap; + resource_size_t iosize; + struct clk *clk; + u32 fiu_num; + bool Direct_Read; + + struct npcm7xx_chip *chip[NPCM7XX_MAX_CHIP_NUM]; +}; + +void __iomem *fiu_base[FIU_MAX_MODULE_NUM]; + +/* #define NPCMX50_MTD_SPINOR_DEBUG */ +#ifdef NPCMX50_MTD_SPINOR_DEBUG + #define DEBUG_FLASH(format, args...) printk(format, ##args) + #define DUMP_MSG(format, args...) dump_msg(format, ## args) +#else + #define DEBUG_FLASH(format, args...) + #define DUMP_MSG(format, args...) +#endif + + +/* + * MTD static functions: + */ +#ifdef NPCMX50_MTD_SPINOR_DEBUG +static void dump_msg(const char *label, const unsigned char *buf, + unsigned int length); +#endif +//static void npcm7xx_spi_flash_unlock_protection(struct spi_nor *nor); + +/* + * FIU Functions + */ +static int npcm7xx_fiu_uma_read(struct spi_nor *nor, u8 transaction_code, + u32 address, bool is_address_size, u8 *data, + u32 data_size) +{ + u32 data_reg[4]; + u32 uma_cfg = 0x0; + int ret = 0; + u32 address_size = 0; + + struct npcm7xx_chip *chip = nor->priv; + struct npcm7xx_spi_bus *host = chip->host; + + switch (chip->chipselect) { + case 0: + case 1: + case 2: + case 3: + SET_REG_FIELD(FIU_UMA_CTS(host->fiu_num), + FIU_UMA_CTS_DEV_NUM, (u32)chip->chipselect); + break; + default: + return -ENODEV; + } + + SET_REG_FIELD(FIU_UMA_CMD(host->fiu_num), FIU_UMA_CMD_CMD, + transaction_code); + SET_VAR_FIELD(uma_cfg, FIU_UMA_CFG_CMDSIZ, 1); + + if (is_address_size) + address_size = nor->addr_width; + + SET_VAR_FIELD(uma_cfg, FIU_UMA_CFG_ADDSIZ, address_size); + + REG_WRITE(FIU_UMA_ADDR(host->fiu_num), address); + SET_VAR_FIELD(uma_cfg, FIU_UMA_CFG_RDATSIZ, data_size); + SET_VAR_FIELD(uma_cfg, FIU_UMA_CFG_WDATSIZ, 0); + + REG_WRITE(FIU_UMA_CFG(host->fiu_num), uma_cfg); + SET_REG_FIELD(FIU_UMA_CTS(host->fiu_num), FIU_UMA_CTS_EXEC_DONE, 1); + + /* + * wait for indication that transaction has terminated + */ + while (READ_REG_FIELD(FIU_UMA_CTS(host->fiu_num), FIU_UMA_CTS_EXEC_DONE) + == FIU_TRANS_STATUS_IN_PROG){ + } + + if (data_size >= FIU_UMA_DATA_SIZE_1) + data_reg[0] = REG_READ(FIU_UMA_DR0(host->fiu_num)); + if (data_size >= FIU_UMA_DATA_SIZE_5) + data_reg[1] = REG_READ(FIU_UMA_DR1(host->fiu_num)); + if (data_size >= FIU_UMA_DATA_SIZE_9) + data_reg[2] = REG_READ(FIU_UMA_DR2(host->fiu_num)); + if (data_size >= FIU_UMA_DATA_SIZE_13) + data_reg[3] = REG_READ(FIU_UMA_DR3(host->fiu_num)); + + memcpy(data, data_reg, data_size); + + return ret; +} + + +static int npcm7xx_fiu_uma_write(struct spi_nor *nor, u8 transaction_code, + u32 address, bool is_address_size, u8 *data, + u32 data_size) +{ + u32 data_reg[4] = {0}; + u32 uma_reg = 0x0; + int ret = 0; + u32 address_size = 0; + + struct npcm7xx_chip *chip = nor->priv; + struct npcm7xx_spi_bus *host = chip->host; + + switch (chip->chipselect) { + case 0: + case 1: + case 2: + case 3: + SET_REG_FIELD(FIU_UMA_CTS(host->fiu_num), + FIU_UMA_CTS_DEV_NUM, (u32)chip->chipselect); + break; + default: + return -ENODEV; + } + + SET_REG_FIELD(FIU_UMA_CMD(host->fiu_num), + FIU_UMA_CMD_CMD, transaction_code); + SET_VAR_FIELD(uma_reg, FIU_UMA_CFG_CMDSIZ, 1); + + if (is_address_size) + address_size = nor->addr_width; + + SET_VAR_FIELD(uma_reg, FIU_UMA_CFG_ADDSIZ, address_size); + REG_WRITE(FIU_UMA_ADDR(host->fiu_num), address); + + memcpy(data_reg, data, data_size); + + if (data_size >= FIU_UMA_DATA_SIZE_1) + REG_WRITE(FIU_UMA_DW0(host->fiu_num), data_reg[0]); + if (data_size >= FIU_UMA_DATA_SIZE_5) + REG_WRITE(FIU_UMA_DW1(host->fiu_num), data_reg[1]); + if (data_size >= FIU_UMA_DATA_SIZE_9) + REG_WRITE(FIU_UMA_DW2(host->fiu_num), data_reg[2]); + if (data_size >= FIU_UMA_DATA_SIZE_13) + REG_WRITE(FIU_UMA_DW3(host->fiu_num), data_reg[3]); + + SET_VAR_FIELD(uma_reg, FIU_UMA_CFG_WDATSIZ, data_size); + SET_VAR_FIELD(uma_reg, FIU_UMA_CFG_RDATSIZ, 0); + + REG_WRITE(FIU_UMA_CFG(host->fiu_num), uma_reg); + SET_REG_FIELD(FIU_UMA_CTS(host->fiu_num), FIU_UMA_CTS_EXEC_DONE, 1); + + /* + * wait for indication that transaction has terminated + */ + while (READ_REG_FIELD(FIU_UMA_CTS(host->fiu_num), FIU_UMA_CTS_EXEC_DONE) + == FIU_TRANS_STATUS_IN_PROG) { + } + + return ret; +} + + +static int npcm7xx_fiu_manualwrite(struct spi_nor *nor, u8 transaction_code, + u32 address, u8 *data, u32 data_size) +{ + u8 uma_cfg = 0x0; + u32 num_data_chunks; + u32 remain_data; + u32 idx = 0; + + struct npcm7xx_chip *chip = nor->priv; + struct npcm7xx_spi_bus *host = chip->host; + + SET_VAR_FIELD(uma_cfg, FIU_UMA_CFG_WDATSIZ, 16); + + num_data_chunks = data_size / CHUNK_SIZE; + remain_data = data_size % CHUNK_SIZE; + + SET_REG_FIELD(FIU_UMA_CTS(host->fiu_num), FIU_UMA_CTS_DEV_NUM, + (u32)chip->chipselect); + SET_REG_FIELD(FIU_UMA_CTS(host->fiu_num), FIU_UMA_CTS_SW_CS, 0); + + npcm7xx_fiu_uma_write(nor, transaction_code, address, true, NULL, 0); +/* + * Starting the data writing loop in multiples of 8 + */ + for (idx = 0; idx < num_data_chunks; ++idx) { + npcm7xx_fiu_uma_write(nor, data[0], (u32)NULL, false, &data[1], + CHUNK_SIZE-1); + data += CHUNK_SIZE; + } + +/* + * Handling chunk remains + */ + if (remain_data > 0) + npcm7xx_fiu_uma_write(nor, data[0], (u32)NULL, false, &data[1], + remain_data-1); + + SET_REG_FIELD(FIU_UMA_CTS(host->fiu_num), FIU_UMA_CTS_SW_CS, 1); + + return 0; +} + +/* + * SPI Functions + */ +static void npcm7xx_spi_flash_high_addr_wr(struct spi_nor *nor, u8 HighAddr) +{ + npcm7xx_fiu_uma_write(nor, SPINOR_OP_WREN, 0, false, NULL, 0); + npcm7xx_fiu_uma_write(nor, NPCM_SPI_WR_EXT_ADDR_REG_CMD, 0, false, + &HighAddr, sizeof(u8)); +} + +static void npcm7xx_spi_flash_common_getstatus(struct spi_nor *nor, u8 *status) +{ + npcm7xx_fiu_uma_read(nor, SPINOR_OP_RDSR, 0, 0, status, 1); +} + +static void npcm7xx_spi_flash_common_waittillready(struct spi_nor *nor) +{ + u8 busy = 1; + + do { + npcm7xx_spi_flash_common_getstatus(nor, &busy); + /* Keep only "busy" bit 0 */ + busy &= 0x01; + } while (busy); +} + +static void npcm7xx_spi_flash_common_write(struct spi_nor *nor, u32 destAddr, + u8 *data, u32 size) +{ + if ((destAddr >> 24) && (nor->addr_width == 3)) { + npcm7xx_spi_flash_high_addr_wr(nor, destAddr >> 24); + npcm7xx_fiu_uma_write(nor, SPINOR_OP_WREN, 0, false, NULL, 0); + npcm7xx_fiu_manualwrite(nor, SPINOR_OP_PP, + (destAddr & 0xFFFFFF), data, size); + npcm7xx_spi_flash_common_waittillready(nor); + npcm7xx_spi_flash_high_addr_wr(nor, 0); + } else { + npcm7xx_fiu_uma_write(nor, SPINOR_OP_WREN, 0, false, NULL, 0); + npcm7xx_fiu_manualwrite(nor, SPINOR_OP_PP, destAddr, data, + size); + npcm7xx_spi_flash_common_waittillready(nor); + } +} + +static void npcm7xx_spi_flash_unlock_protection(struct spi_nor *nor) +{ + u8 status_reg_val = 0; + + npcm7xx_fiu_uma_write(nor, SPINOR_OP_WREN, 0, false, NULL, 0); + npcm7xx_fiu_uma_write(nor, SPINOR_OP_WRSR, 0, false, + &status_reg_val, sizeof(u8)); + npcm7xx_spi_flash_common_waittillready(nor); +} + +static ssize_t npcm7xx_spi_write(struct spi_nor *nor, loff_t to, + size_t len, const u_char *write_buf) +{ + u32 local_addr = (u32) to; + u32 cnt = (u32) len; + u32 actual_size = 0; + struct mtd_info *mtd; + + mtd = &nor->mtd; + + DEBUG_FLASH("mtd_spinor: %s %s 0x%08x, len %zd\n", __func__, + "to", (u32)to, len); + + /* sanity checks */ + if (!len) + return(0); + + if (to + len > mtd->size) + return -EINVAL; + + if (cnt != 0) { + while (cnt) { + actual_size = ((((local_addr)/mtd->writesize) + 1) + *mtd->writesize) - (local_addr); + if (actual_size > cnt) + actual_size = cnt; + + npcm7xx_spi_flash_common_write(nor, local_addr, + (u_char *)write_buf, + actual_size); + + write_buf += actual_size; + local_addr += actual_size; + cnt -= actual_size; + } + } + + return (len - cnt); +} + +static ssize_t npcm7xx_spi_read(struct spi_nor *nor, loff_t from, + size_t len, u_char *read_buf) +{ + struct npcm7xx_chip *chip = nor->priv; + struct npcm7xx_spi_bus *host = chip->host; + void __iomem *flash_region_mapped_ptr = NULL; + struct mtd_info *mtd; + int i, readlen, currlen; + u32 addr; + u8 *buf_ptr; + u32 retlen = 0; + + mtd = &nor->mtd; + + DEBUG_FLASH("mtd_spinor: %s %s 0x%08x, len %zd\n", __func__, "from", + (u32)from, len); + + if (!len) + return 0; + + if (from + len > (u32)mtd->size) + return -EINVAL; + + DEBUG_FLASH("mtd_spinor: %s , mtd->size 0x%08x %p\n", __func__, + (u32) mtd->size, read_buf); + + + if (host->Direct_Read) { + if (!request_mem_region((host->res_mem->start + + (host->MaxChipAddMap * + chip->chipselect)) + from, + len, mtd->name)) + return -EBUSY; + + flash_region_mapped_ptr = (u32 *)ioremap_nocache( + (host->res_mem->start + + (host->MaxChipAddMap * chip->chipselect)) + from, len); + + if (!flash_region_mapped_ptr) { + pr_err(": Failed to ioremap window!\n"); + release_mem_region((host->res_mem->start + + (host->MaxChipAddMap * + chip->chipselect)) + from, len); + return -ENOMEM; + } + + if (mtd->size > SIZE_16MB) { + if (nor->addr_width == 3) + SET_REG_FIELD(FIU_DRD_CFG(host->fiu_num), + FIU_DRD_CFG_RDCMD, + SPINOR_OP_READ_1_2_2_4B); + + else + SET_REG_FIELD(FIU_DRD_CFG(host->fiu_num), + FIU_DRD_CFG_RDCMD, + SPINOR_OP_WRSR); + SET_REG_FIELD(FIU_DRD_CFG(host->fiu_num), + FIU_DRD_CFG_ADDSIZ, 0x1); + } else { + SET_REG_FIELD(FIU_DRD_CFG(host->fiu_num), + FIU_DRD_CFG_RDCMD, SPINOR_OP_READ_1_2_2); + SET_REG_FIELD(FIU_DRD_CFG(host->fiu_num), + FIU_DRD_CFG_ADDSIZ, 0x0); + } + + memcpy(read_buf, flash_region_mapped_ptr, len); + wmb(); + + npcm7xx_spi_flash_high_addr_wr(nor, 0); + + if (flash_region_mapped_ptr) + iounmap((void __iomem *)flash_region_mapped_ptr); + + release_mem_region((host->res_mem->start + + (host->MaxChipAddMap * chip->chipselect)) + + from, len); + + retlen = len; + } else { + /* NOTE: OPCODE_FAST_READ (if available) is faster... */ + i = 0; + currlen = (int) len; + + do { + addr = ((u32) from + i); + if (currlen < 4) + readlen = currlen; + else + readlen = 4; + + DEBUG_FLASH("mtd_spinor: ori_addr=0x%x, addr=0x%x\n", + ((u32) from + i), addr); + + buf_ptr = read_buf + i; + + if ((addr >> 24) && (nor->addr_width == 3)) + npcm7xx_spi_flash_high_addr_wr(nor, addr >> 24); + + npcm7xx_fiu_uma_read(nor, SPINOR_OP_READ, + (addr&0xFFFFFF), true, buf_ptr, + readlen); + + if ((addr >> 24) && (nor->addr_width == 3)) + npcm7xx_spi_flash_high_addr_wr(nor, 0); + + DEBUG_FLASH("mtd_spinor: buf_ptr=0x%x buf_val=0x%x " + "i=%d readlen =%d\n", (u32)buf_ptr, + *((u32 *)buf_ptr), i, readlen); + + i += readlen; + currlen -= 4; + } while (currlen > 0); + + retlen = i; + } + + DUMP_MSG("MTD_READ", read_buf, i); + return retlen; +} + +static int npcm7xx_spi_erase(struct spi_nor *nor, loff_t offs) +{ + u32 addr = (u32)offs; + struct mtd_info *mtd; + mtd = &nor->mtd; + + if ((addr >> 24) && (nor->addr_width == 3)) { + npcm7xx_spi_flash_high_addr_wr(nor, addr >> 24); + + npcm7xx_fiu_uma_write(nor, SPINOR_OP_WREN, 0, false, NULL, + 0); + if (mtd->erasesize == 4096) + npcm7xx_fiu_uma_write(nor, SPINOR_OP_BE_4K, + (addr & 0xFFFFFF), true, NULL, 0); + else + npcm7xx_fiu_uma_write(nor, SPINOR_OP_SE, + (addr & 0xFFFFFF), true, NULL, 0); + npcm7xx_spi_flash_common_waittillready(nor); + npcm7xx_spi_flash_high_addr_wr(nor, 0); + } else { + npcm7xx_fiu_uma_write(nor, SPINOR_OP_WREN, 0, false, NULL, + 0); + if (mtd->erasesize == 4096) + npcm7xx_fiu_uma_write(nor, SPINOR_OP_BE_4K, addr, + true, NULL, 0); + else + npcm7xx_fiu_uma_write(nor, SPINOR_OP_SE, addr, + true, NULL, 0); + npcm7xx_spi_flash_common_waittillready(nor); + } + + return 0; +} + +static int npcm7xx_spi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, + int len) +{ + return npcm7xx_fiu_uma_read(nor, opcode, 0, 0, buf, len); +} + +static int npcm7xx_spi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, + int len) +{ + return npcm7xx_fiu_uma_write(nor, opcode, (u32)NULL, false, buf, len); +} + +static int npcm7xx_spi_nor_prep(struct spi_nor *nor, enum spi_nor_ops ops) +{ + struct npcm7xx_chip *chip = nor->priv; + struct npcm7xx_spi_bus *host = chip->host; + + mutex_lock(&host->lock); + + return 0; +} + +static void npcm7xx_spi_nor_unprep(struct spi_nor *nor, enum spi_nor_ops ops) +{ + struct npcm7xx_chip *chip = nor->priv; + struct npcm7xx_spi_bus *host = chip->host; + + mutex_unlock(&host->lock); +} + +#ifdef NPCMX50_MTD_SPINOR_DEBUG +static void dump_msg(const char *label, const unsigned char *buf, + unsigned int length) +{ + unsigned int start, num, i; + char line[52], *p; + + if (length > 32) + length = 32; + + DEBUG_FLASH("MTD_SPI: %s, length %u:\n", label, length); + + start = 0; + while (length > 0) { + num = min(length, 16u); + p = line; + for (i = 0; i < num; ++i) { + if (i == 8) + *p++ = ' '; + sprintf(p, " %02x", buf[i]); + p += 3; + } + *p = 0; + pr_info("%6x: %s\n", start, line); + buf += num; + start += num; + length -= num; + } +} +#endif + +/* + * Get spi flash device information and register it as a mtd device. + */ +static int npcm7xx_spi_nor_register(struct device_node *np, + struct npcm7xx_spi_bus *host) +{ + struct device *dev = host->dev; + struct spi_nor *nor; + struct npcm7xx_chip *chip; + struct mtd_info *mtd; + u32 chipselect; + int ret; + const struct spi_nor_hwcaps hwcaps = { + .mask = SNOR_HWCAPS_READ | + SNOR_HWCAPS_READ_FAST | + SNOR_HWCAPS_READ_1_1_2 | + SNOR_HWCAPS_READ_1_2_2 | + SNOR_HWCAPS_READ_2_2_2 | + SNOR_HWCAPS_READ_1_1_4 | + SNOR_HWCAPS_READ_1_4_4 | + SNOR_HWCAPS_READ_4_4_4 | + SNOR_HWCAPS_PP | + SNOR_HWCAPS_PP_1_1_4 | + SNOR_HWCAPS_PP_1_4_4 | + SNOR_HWCAPS_PP_4_4_4, + }; + + /* This driver does not support NAND or NOR flash devices. */ + if (!of_device_is_compatible(np, "jedec,spi-nor")) { + dev_err(dev, "The device is no compatible to jedec,spi-nor\n"); + return -ENOMEM; + } + + ret = of_property_read_u32(np, "reg", &chipselect); + if (ret) { + dev_err(dev, "There's no reg property for %s\n", np->full_name); + return ret; + } + + if (chipselect >= NPCM7XX_MAX_CHIP_NUM) { + dev_warn(dev, "Flash device number exceeds the maximum " + "chipselect number\n"); + return -ENOMEM; + } + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->host = host; + chip->chipselect = chipselect; + + nor = &chip->nor; + mtd = &nor->mtd; + + nor->dev = dev; + nor->priv = chip; + + spi_nor_set_flash_node(nor, np); + +/* ret = of_property_read_u32(np, "spi-max-frequency", + * &chip->clkrate); + * if (ret) { + * dev_err(dev, "There's no spi-max-frequency property for %s\n", + * np->full_name); + * return ret; + * } + */ + nor->prepare = npcm7xx_spi_nor_prep; + nor->unprepare = npcm7xx_spi_nor_unprep; + + nor->read_reg = npcm7xx_spi_read_reg; + nor->write_reg = npcm7xx_spi_write_reg; + nor->read = npcm7xx_spi_read; + nor->write = npcm7xx_spi_write; + nor->erase = npcm7xx_spi_erase; + + ret = spi_nor_scan(nor, NULL, &hwcaps); + if (ret) + return ret; + + if (mtd->size > SIZE_16MB) { + /* If Flash size is over 16MB the spi_nor_scan sets + automatically the FLASH to work with 4 byte addressing. + Our driver handle Flash size over 16MB with 3 byte address. + Revert back to 3 byte address size cause issues so the + sequence below resets WINBOND and MACRONIX FLASH to work + again with 3 byte address (From Kernel 4.14 and above + the address width statically configured by the driver)*/ + npcm7xx_spi_flash_common_waittillready(nor); + npcm7xx_fiu_uma_write(nor, NPCM_SPI_EN_RST_CMD, 0, + false, NULL, 0); + npcm7xx_fiu_uma_write(nor, NPCM_SPI_RST_DEVICE_CMD, 0, + false, NULL, 0); + npcm7xx_spi_flash_common_waittillready(nor); + nor->addr_width = 3; + } + + npcm7xx_spi_flash_unlock_protection(nor); + + //mtd->name = np->name; + ret = mtd_device_register(mtd, NULL, 0); + if (ret) + return ret; + + host->chip[chip->chipselect] = chip; + return 0; +} + +static void npcm7xx_spi_nor_unregister_all(struct npcm7xx_spi_bus *host) +{ + struct npcm7xx_chip *chip; + int n; + + for (n = 0; n < NPCM7XX_MAX_CHIP_NUM; n++) { + chip = host->chip[n]; + if (chip) + mtd_device_unregister(&chip->nor.mtd); + } +} + +static int npcm7xx_spi_nor_register_all(struct npcm7xx_spi_bus *host) +{ + struct device *dev = host->dev; + struct device_node *np; + int ret; + + for_each_available_child_of_node(dev->of_node, np) { + ret = npcm7xx_spi_nor_register(np, host); + if (ret) + goto fail; + } + + return 0; + +fail: + npcm7xx_spi_nor_unregister_all(host); + return ret; +} + +static int npcm7xx_spi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct npcm7xx_spi_bus *host; + static u32 fiu_num; + struct device_node *np = pdev->dev.of_node; + int ret; + + host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); + if (!host) + return -ENOMEM; + + platform_set_drvdata(pdev, host); + host->dev = dev; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "control"); + host->regbase = devm_ioremap_resource(dev, res); + if (IS_ERR(host->regbase)) + return PTR_ERR(host->regbase); + + host->res_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "memory"); + host->clk = devm_clk_get(dev, NULL); + if (IS_ERR(host->clk)) + return PTR_ERR(host->clk); + + if (of_device_is_compatible(np, "nuvoton,npcm750-spi")) + host->MaxChipAddMap = 0x8000000; + else { + ret = of_property_read_u32(dev->of_node, "chip-max-address-map", + &host->MaxChipAddMap); + if (ret) { + pr_info("There's no chip-max-address-map property " + "for %s adjust 128Mb\n", + dev->of_node->full_name); + host->MaxChipAddMap = 0x8000000; + } + } + + mutex_init(&host->lock); + //clk_prepare_enable(host->clk); + fiu_base[fiu_num] = host->regbase; + host->fiu_num = fiu_num; + + if (host->res_mem) { + /* set access to Dual I/O */ + SET_REG_FIELD(FIU_DRD_CFG(host->fiu_num), FIU_DRD_CFG_ACCTYPE, + 1); + /* set dummy byte to 1 */ + SET_REG_FIELD(FIU_DRD_CFG(host->fiu_num), FIU_DRD_CFG_DBW, 1); + host->Direct_Read = true; + } else + host->Direct_Read = false; + + ret = npcm7xx_spi_nor_register_all(host); + if (ret) { + mutex_destroy(&host->lock); + //clk_disable_unprepare(host->clk); + pr_info("npcm7xx_spi_nor_register_all failed\n"); + } + + fiu_num++; + + DEBUG_FLASH("mtd_spinor: %s() date=%s time=%s\n\n", __func, __date__, + __time__); + + return ret; +} + +static int npcm7xx_spi_remove(struct platform_device *pdev) +{ + struct npcm7xx_spi_bus *host = platform_get_drvdata(pdev); + + npcm7xx_spi_nor_unregister_all(host); + mutex_destroy(&host->lock); + clk_disable_unprepare(host->clk); + return 0; +} + +static const struct of_device_id npcm7xx_spi_dt_ids[] = { + { .compatible = "nuvoton,npcm750-spi" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, npcm7xx_spi_dt_ids); + +static struct platform_driver npcm7xx_spi_driver = { + .driver = { + .name = "npcm7xx-spi", + .bus = &platform_bus_type, + .of_match_table = npcm7xx_spi_dt_ids, + }, + .probe = npcm7xx_spi_probe, + .remove = npcm7xx_spi_remove, +}; +module_platform_driver(npcm7xx_spi_driver); + +MODULE_DESCRIPTION("Nuvoton SPI Controller Driver"); +MODULE_AUTHOR("Tomer Maimon "); +MODULE_LICENSE("GPL v2"); From 28abb3dbfe425bbd620e2da126c1ad624e53208e Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Wed, 27 Dec 2017 15:50:18 +0200 Subject: [PATCH 14/37] dt-binding: hwmon: document NPCM7xx sensors DT bindings Added device tree binding documentation for Nuvoton NPCM7xx Pulse Width Modulation (PWM) and Fan Tachometer devices. Signed-off-by: Tomer Maimon --- .../devicetree/bindings/hwmon/npcm7xx-fan.txt | 18 ++++++++++++++++++ .../devicetree/bindings/hwmon/npcm7xx-pwm.txt | 16 ++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/npcm7xx-fan.txt create mode 100644 Documentation/devicetree/bindings/hwmon/npcm7xx-pwm.txt diff --git a/Documentation/devicetree/bindings/hwmon/npcm7xx-fan.txt b/Documentation/devicetree/bindings/hwmon/npcm7xx-fan.txt new file mode 100644 index 00000000000000..cb456632a99fbd --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/npcm7xx-fan.txt @@ -0,0 +1,18 @@ +Nuvoton NPCM7xx fan tachometer (Fan) controller device driver + +The NPCM7xx fan tachometer controller supports upto 16 Fan inputs. + +Required properties: +- compatible : "nuvoton,npcm750-fan" for Poleg NPCM750. +- reg : Offset and length of the register set for the device. +- clocks : phandle of fan reference clock. +- interrupts : Contain the fan interrupts with flags for + falling edge. + +fan: fan@0 { + compatible = "nuvoton,npcm750-fan"; + reg = <0xf0180000 0x8000>; + interrupts = <0 96 4>, <0 97 4>, <0 98 4>, <0 99 4>, + <0 100 4>, <0 101 4>, <0 102 4>, <0 103 4>; + clocks = <&clk NPCM7XX_CLK_APB4>; +}; diff --git a/Documentation/devicetree/bindings/hwmon/npcm7xx-pwm.txt b/Documentation/devicetree/bindings/hwmon/npcm7xx-pwm.txt new file mode 100644 index 00000000000000..8b54a59c272aaa --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/npcm7xx-pwm.txt @@ -0,0 +1,16 @@ +Nuvoton NPCM7xx Pulse-width modulation (PWM) controller device driver + +The NPCM7xx PWM controller supports upto 8 PWM outputs. +Each PWM output module have watchdog. + +Required properties: +- compatible : "nuvoton,npcm750-pwm" for Poleg NPCM750. +- reg : Offset and length of the registers set for the device. +- clocks : phandle of pwm reference clock. + +pwm:pwm@f0103000 { + compatible = "nuvoton,npcm750-pwm"; + reg = <0xf0103000 0x1000 + 0xf0104000 0x1000>; + clocks = <&clk NPCM7XX_CLK_APB3>; +}; From df06c325762f2120bcc310a4651f7d51b559e949 Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Wed, 27 Dec 2017 15:50:19 +0200 Subject: [PATCH 15/37] hwmon: npcm: add NPCM7xx PWM and Fan driver Add Nuvoton BMC NPCM7xx Pulse Width Modulation (PWM) Fan tachometer (Fan) drivers Nuvoton NPCM7xx support upto 16 Fan tachometer inputs and upto 8 PWM outputs. Each PWM output module have watchdog. Signed-off-by: Tomer Maimon --- drivers/hwmon/Kconfig | 6 + drivers/hwmon/Makefile | 1 + drivers/hwmon/npcm7xx-fan.c | 756 ++++++++++++++++++++++++++++++++++++ drivers/hwmon/npcm7xx-pwm.c | 451 +++++++++++++++++++++ 4 files changed, 1214 insertions(+) create mode 100644 drivers/hwmon/npcm7xx-fan.c create mode 100644 drivers/hwmon/npcm7xx-pwm.c diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 5ef2814345ef7f..cedfbf36e18703 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1861,6 +1861,12 @@ config SENSORS_XGENE If you say yes here you get support for the temperature and power sensors for APM X-Gene SoC. +config SENSORS_NPCM7XX + tristate "Nuvoton NPCM7XX PWM and Fan driver" + help + This driver provides support for Nuvoton NPCM7XX PWM and Fan + controllers. + if ACPI comment "ACPI drivers" diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index d4641a9f16c197..ff4212e8274094 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -169,6 +169,7 @@ obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o obj-$(CONFIG_SENSORS_XGENE) += xgene-hwmon.o +obj-$(CONFIG_SENSORS_NPCM7XX) += npcm7xx-pwm.o npcm7xx-fan.o obj-$(CONFIG_PMBUS) += pmbus/ diff --git a/drivers/hwmon/npcm7xx-fan.c b/drivers/hwmon/npcm7xx-fan.c new file mode 100644 index 00000000000000..d0354edf13d1ea --- /dev/null +++ b/drivers/hwmon/npcm7xx-fan.c @@ -0,0 +1,756 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2014-2018 Nuvoton Technology corporation. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + u8 u8ChannelNum; + u8 u8FanPulsePerRev; + u16 u16FanSpeedReading; + u32 u32InputClock; +} sFanTachData; + +#define NPCM750_MFT_CLKPS 255 + +/* + * Get Fan Tach Timeout (base on clock 214843.75Hz, 1 cnt = 4.654us) + * Timeout 94ms ~= 0x5000 + * (The minimum FAN speed could to support ~640RPM/pulse 1, + * 320RPM/pulse 2, ...-- 10.6Hz) + */ +#define FAN_TACH_TIMEOUT ((u16) 0x5000) + +/* + * Enable a background timer to poll fan tach value, (200ms * 4) + * to polling all fan) + */ + +/* 1 = 1 jiffies = 10 ms */ +#define FAN_TACH_POLLING_INTERVAL 20 + +/* MFT General Defintion */ +#define NPCM750_MFT_MAX_MODULE 8 +#define NPCM750_CMPA 0 +#define NPCM750_CMPB 1 + +#define NPCM750_MFT_MODE_5 4 /* Dual Independent Input Capture */ + +#define NPCM750_MFT_TCNT ((u16) 0xFFFF) +#define NPCM750_MFT_TCPA ((u16) (NPCM750_MFT_TCNT - FAN_TACH_TIMEOUT)) +#define NPCM750_MFT_TCPB ((u16) (NPCM750_MFT_TCNT - FAN_TACH_TIMEOUT)) + +#define NPCM750_MFT_NO_CLOCK_MODE 0 +#define NPCM750_MFT_APB_CLOCK_MODE 1 + +#define DEFAULT_PULSE_PER_REVOLUTION 2 + +/* Fantach MFT registers */ +#define MFT_REG_TCNT1(n) ((void *) (MFT_REGS_BASE(n) + 0x00)) +#define MFT_REG_TCRA(n) ((void *) (MFT_REGS_BASE(n) + 0x02)) +#define MFT_REG_TCRB(n) ((void *) (MFT_REGS_BASE(n) + 0x04)) +#define MFT_REG_TCNT2(n) ((void *) (MFT_REGS_BASE(n) + 0x06)) +#define MFT_REG_TPRSC(n) ((void *) (MFT_REGS_BASE(n) + 0x08)) +#define MFT_REG_TCKC(n) ((void *) (MFT_REGS_BASE(n) + 0x0A)) +#define MFT_REG_TMCTRL(n) ((void *) (MFT_REGS_BASE(n) + 0x0C)) +#define MFT_REG_TICTRL(n) ((void *) (MFT_REGS_BASE(n) + 0x0E)) +#define MFT_REG_TICLR(n) ((void *) (MFT_REGS_BASE(n) + 0x10)) +#define MFT_REG_TIEN(n) ((void *) (MFT_REGS_BASE(n) + 0x12)) +#define MFT_REG_TCPA(n) ((void *) (MFT_REGS_BASE(n) + 0x14)) +#define MFT_REG_TCPB(n) ((void *) (MFT_REGS_BASE(n) + 0x16)) +#define MFT_REG_TCPCFG(n) ((void *) (MFT_REGS_BASE(n) + 0x18)) +#define MFT_REG_TINASEL(n) ((void *) (MFT_REGS_BASE(n) + 0x1A)) +#define MFT_REG_TINBSEL(n) ((void *) (MFT_REGS_BASE(n) + 0x1C)) + +#define NPCM750_TCKC_C2CSEL(mode) (((mode) & GENMASK(2,0)) << 3) +#define NPCM750_TCKC_C1CSEL(mode) ((mode) & GENMASK(2,0)) + +#define NPCM750_TMCTRL_TBEN BIT(6) +#define NPCM750_TMCTRL_TAEN BIT(5) +#define NPCM750_TMCTRL_TBEDG BIT(4) +#define NPCM750_TMCTRL_TAEDG BIT(3) +#define NPCM750_TMCTRL_MDSEL(mode) ((mode) & GENMASK(2,0)) + +#define NPCM750_TICLR_CLEAR_ALL GENMASK(5,0) +#define NPCM750_TICLR_TFCLR BIT(5) +#define NPCM750_TICLR_TECLR BIT(4) +#define NPCM750_TICLR_TDCLR BIT(3) +#define NPCM750_TICLR_TCCLR BIT(2) +#define NPCM750_TICLR_TBCLR BIT(1) +#define NPCM750_TICLR_TACLR BIT(0) + +#define NPCM750_TIEN_ENABLE_ALL GENMASK(5,0) +#define NPCM750_TIEN_TFIEN BIT(5) +#define NPCM750_TIEN_TEIEN BIT(4) +#define NPCM750_TIEN_TDIEN BIT(3) +#define NPCM750_TIEN_TCIEN BIT(2) +#define NPCM750_TIEN_TBIEN BIT(1) +#define NPCM750_TIEN_TAIEN BIT(0) + +#define NPCM750_TICTRL_TFPND BIT(5) +#define NPCM750_TICTRL_TEPND BIT(4) +#define NPCM750_TICTRL_TDPND BIT(3) +#define NPCM750_TICTRL_TCPND BIT(2) +#define NPCM750_TICTRL_TBPND BIT(1) +#define NPCM750_TICTRL_TAPND BIT(0) + +#define NPCM750_TCPCFG_HIBEN BIT(7) +#define NPCM750_TCPCFG_EQBEN BIT(6) +#define NPCM750_TCPCFG_LOBEN BIT(5) +#define NPCM750_TCPCFG_CPBSEL BIT(4) +#define NPCM750_TCPCFG_HIAEN BIT(3) +#define NPCM750_TCPCFG_EQAEN BIT(2) +#define NPCM750_TCPCFG_LOAEN BIT(1) +#define NPCM750_TCPCFG_CPASEL BIT(0) + +#define NPCM750_TINASEL_FANIN_DEFAULT (0x0) + +#define FAN_TACH_DISABLE 0xFF +#define FAN_TACH_INIT 0x00 +#define FAN_TACH_PREPARE_TO_GET_FIRST_CAPTURE 0x01 +#define FAN_TACH_ENOUGH_SAMPLE 0x02 + +/* maximum fan tach input support */ +#define NPCM750_MAX_FAN_TACH 16 + +/* Obtain the fan number */ +#define NPCM750_FAN_TACH_INPUT(mft, cmp) ((mft << 1) + (cmp)) + +typedef struct { + u8 u8FanStatusFlag; + u8 u8FanPulsePerRev; + u16 u16FanTachCnt; + u32 u32FanTachCntTemp; +} sFanTachDev; + +static int npcm750_fan_read(sFanTachData *pFanTachData); +static int mft_virt_addr; + +#define MFT_REGS_BASE(n) (mft_virt_addr + ((n) * 0x1000L)) + +/* for request irq use */ +static u8 u8dummy; +int mft_irq[8]; + +/* Input clock */ +static u32 u32InputClock; +static sFanTachDev S_npcm750_fantach[NPCM750_MAX_FAN_TACH]; +static u8 S_npcm750_fantach_select; +static struct timer_list npcm750_fantach_timer; +static struct clk *mft_clk; + +struct npcm750_fan_data { + unsigned long clk_freq; +}; + +static inline void npcm750_fantach_start_capture(u8 mft, u8 cmp) +{ + u8 fan_id = 0; + u8 reg_mode = 0; + u8 reg_int = 0; + + fan_id = NPCM750_FAN_TACH_INPUT(mft, cmp); + + /* to check whether any fan tach is enable */ + if (S_npcm750_fantach[fan_id].u8FanStatusFlag != FAN_TACH_DISABLE) { + /* reset status */ + S_npcm750_fantach[fan_id].u8FanStatusFlag = FAN_TACH_INIT; + reg_int = ioread8((void *)MFT_REG_TIEN(mft)); + + if (cmp == NPCM750_CMPA) { + /* enable interrupt */ + iowrite8((u8) (reg_int | (NPCM750_TIEN_TAIEN | + NPCM750_TIEN_TEIEN)), + (void *)MFT_REG_TIEN(mft)); + + reg_mode = + NPCM750_TCKC_C1CSEL(NPCM750_MFT_APB_CLOCK_MODE) + | ioread8((void *)MFT_REG_TCKC(mft)); + + /* start to Capture */ + iowrite8(reg_mode, (void *)MFT_REG_TCKC(mft)); + } else { + /* enable interrupt */ + iowrite8((u8) (reg_int | (NPCM750_TIEN_TBIEN | + NPCM750_TIEN_TFIEN)), + (void *)MFT_REG_TIEN(mft)); + + reg_mode = + NPCM750_TCKC_C2CSEL(NPCM750_MFT_APB_CLOCK_MODE) + | ioread8((void *)MFT_REG_TCKC(mft)); + + /* start to Capture */ + iowrite8(reg_mode, (void *)MFT_REG_TCKC(mft)); + } + } +} + +static void npcm750_fantach_polling(unsigned long data) +{ + int i; + + /* Polling two module per one round, + * MFT0 & MFT4 / MFT1 & MFT5 / MFT2 & MFT6 / MFT3 & MFT7 + */ + //pr_info("npcm750_fantach_polling \n"); + for (i = S_npcm750_fantach_select; i < NPCM750_MFT_MAX_MODULE; + i = i+4) { + /* clear the flag and reset the counter (TCNT) */ + iowrite8((u8) NPCM750_TICLR_CLEAR_ALL, + (void *) MFT_REG_TICLR(i)); + + iowrite16(NPCM750_MFT_TCNT, (void *)MFT_REG_TCNT1(i)); + iowrite16(NPCM750_MFT_TCNT, (void *)MFT_REG_TCNT2(i)); + + npcm750_fantach_start_capture(i, NPCM750_CMPA); + npcm750_fantach_start_capture(i, NPCM750_CMPB); + } + + S_npcm750_fantach_select++; + S_npcm750_fantach_select &= 0x3; + + /* reset the timer interval */ + npcm750_fantach_timer.expires = jiffies + msecs_to_jiffies(20); + add_timer(&npcm750_fantach_timer); +} + +static int npcm750_fan_read(sFanTachData *pFanTachData) +{ + u8 fan_id = 0; + + fan_id = pFanTachData->u8ChannelNum; + + if (S_npcm750_fantach[fan_id].u16FanTachCnt != 0) + pFanTachData->u16FanSpeedReading = + S_npcm750_fantach[fan_id].u16FanTachCnt; + else + pFanTachData->u16FanSpeedReading = 0; + + return 0; +} + +static inline void npcm750_fantach_compute(u8 mft, u8 cmp, u8 fan_id, + u8 flag_int, u8 flag_mode, + u8 flag_clear) +{ + u8 reg_int = 0; + u8 reg_mode = 0; + u16 fan_cap = 0; + + if (cmp == NPCM750_CMPA) + fan_cap = ioread16((void *) MFT_REG_TCRA(mft)); + else + fan_cap = ioread16((void *) MFT_REG_TCRB(mft)); + + /* clear capature flag, H/W will auto reset the NPCM750_TCNTx */ + iowrite8((u8) flag_clear, (void *) MFT_REG_TICLR(mft)); + + if (S_npcm750_fantach[fan_id].u8FanStatusFlag == FAN_TACH_INIT) { + /* First capture, drop it */ + S_npcm750_fantach[fan_id].u8FanStatusFlag = + FAN_TACH_PREPARE_TO_GET_FIRST_CAPTURE; + + /* reset counter */ + S_npcm750_fantach[fan_id].u32FanTachCntTemp = 0; + } else if (S_npcm750_fantach[fan_id].u8FanStatusFlag < + FAN_TACH_ENOUGH_SAMPLE) { + /* + * collect the enough sample, + * (ex: 2 pulse fan need to get 2 sample) + */ + S_npcm750_fantach[fan_id].u32FanTachCntTemp += + (NPCM750_MFT_TCNT - fan_cap); + /* + * DEBUG_MSG("step 1, fan %d cnt %d total %x\n", + * fan_id, (NPCM750_MFT_TCNT - fan_cap), + * (u32) S_npcm750_fantach[fan_id].u32FanTachCntTemp); + */ + S_npcm750_fantach[fan_id].u8FanStatusFlag++; + } else { + /* get enough sample or fan disable */ + if (S_npcm750_fantach[fan_id].u8FanStatusFlag == + FAN_TACH_ENOUGH_SAMPLE) { + S_npcm750_fantach[fan_id].u32FanTachCntTemp += + (NPCM750_MFT_TCNT - fan_cap); + /* + * DEBUG_MSG("step 2, fan %d cnt %d total %x\n", + * fan_id, (NPCM750_MFT_TCNT - fan_cap), + * (u32)S_npcm750_fantach[fan_id].u32FanTachCntTemp); + */ + + /* compute finial average cnt per pulse */ + S_npcm750_fantach[fan_id].u16FanTachCnt + = S_npcm750_fantach[fan_id].u32FanTachCntTemp / + FAN_TACH_ENOUGH_SAMPLE; + + /* + * DEBUG_MSG("step 3 fan %d avg %d\n\n", + * fan_id, S_npcm750_fantach[fan_id].u16FanTachCnt); + */ + S_npcm750_fantach[fan_id].u8FanStatusFlag = + FAN_TACH_INIT; + } + + reg_int = ioread8((void *)MFT_REG_TIEN(mft)); + + /* disable interrupt */ + iowrite8((u8) (reg_int & ~flag_int), (void *)MFT_REG_TIEN(mft)); + reg_mode = ioread8((void *)MFT_REG_TCKC(mft)); + + /* stop capturing */ + iowrite8((u8) (reg_mode & ~flag_mode), + (void *) MFT_REG_TCKC(mft)); + } +} + +static inline void npcm750_check_cmp(u8 mft, u8 cmp, u8 flag) +{ + u8 reg_int = 0; + u8 reg_mode = 0; + u8 flag_timeout; + u8 flag_cap; + u8 flag_clear; + u8 flag_int; + u8 flag_mode; + u8 fan_id; + + fan_id = NPCM750_FAN_TACH_INPUT(mft, cmp); + + if (cmp == NPCM750_CMPA) { + flag_cap = NPCM750_TICTRL_TAPND; + flag_timeout = NPCM750_TICTRL_TEPND; + flag_int = (NPCM750_TIEN_TAIEN | NPCM750_TIEN_TEIEN); + flag_mode = NPCM750_TCKC_C1CSEL(NPCM750_MFT_APB_CLOCK_MODE); + flag_clear = NPCM750_TICLR_TACLR | NPCM750_TICLR_TECLR; + } else { + flag_cap = NPCM750_TICTRL_TBPND; + flag_timeout = NPCM750_TICTRL_TFPND; + flag_int = (NPCM750_TIEN_TBIEN | NPCM750_TIEN_TFIEN); + flag_mode = NPCM750_TCKC_C2CSEL(NPCM750_MFT_APB_CLOCK_MODE); + flag_clear = NPCM750_TICLR_TBCLR | NPCM750_TICLR_TFCLR; + } + + if (flag & flag_timeout) { + reg_int = ioread8((void *)MFT_REG_TIEN(mft)); + + /** disable interrupt */ + iowrite8((u8) (reg_int & ~flag_int), (void *)MFT_REG_TIEN(mft)); + + /** clear interrup flag */ + iowrite8((u8) flag_clear, (void *) MFT_REG_TICLR(mft)); + + reg_mode = ioread8((void *)MFT_REG_TCKC(mft)); + + /** stop capturing */ + iowrite8((u8) (reg_mode & ~flag_mode), + (void *) MFT_REG_TCKC(mft)); + + /* + * If timeout occurs (FAN_TACH_TIMEOUT), the fan doesn't + * connect or speed is lower than 10.6Hz (320RPM/pulse2). + * In these situation, the RPM output should be zero. + */ + S_npcm750_fantach[fan_id].u16FanTachCnt = 0; + //DEBUG_MSG("%s : it is timeout fan_id %d\n", __func__, fan_id); + } else { + /** input capture is occurred */ + if (flag & flag_cap) + npcm750_fantach_compute(mft, cmp, fan_id, flag_int, + flag_mode, flag_clear); + } +} + +static irqreturn_t npcm750_mft0_isr(int irq, void *dev_id) +{ + u8 flag = 0; + int module; + + module = irq - mft_irq[0]; + flag = ioread8((void *)(void *) MFT_REG_TICTRL(module)); + if (flag > 0) { + npcm750_check_cmp(module, NPCM750_CMPA, flag); + npcm750_check_cmp(module, NPCM750_CMPB, flag); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int npcm7xx_read_fan(struct device *dev, u32 attr, int channel, + long *val) +{ + sFanTachData FanTachData; + + FanTachData.u8ChannelNum = (u8)channel; + + switch (attr) { + case hwmon_fan_input: + npcm750_fan_read(&FanTachData); + if (FanTachData.u16FanSpeedReading <= 0) + { + *val = 0; + return FanTachData.u16FanSpeedReading; + } + + /*Convert the raw reading to RPM*/ + if ((FanTachData.u16FanSpeedReading > 0) && + S_npcm750_fantach[channel].u8FanPulsePerRev > 0) + *val = (long)((u32InputClock * 60)/ + (FanTachData.u16FanSpeedReading * + S_npcm750_fantach[channel].u8FanPulsePerRev)); + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int npcm7xx_write_fan(struct device *dev, u32 attr, int channel, + long val) +{ + //struct npcm7xx_pwm_fan_data *data = dev_get_drvdata(dev); + int err = 0; + + switch (attr) { + case hwmon_fan_target: + //err = npcm7xx_pwm_config_set(data, channel, (u16)val); + break; + default: + err = -EOPNOTSUPP; + break; + } + + return err; +} + +static umode_t npcm7xx_fan_is_visible(const void *_data, u32 attr, int channel) +{ + switch (attr) { + case hwmon_fan_input: + return 0644; + default: + return 0; + } +} + +static int npcm7xx_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + switch (type) { + case hwmon_fan: + return npcm7xx_read_fan(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static int npcm7xx_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + switch (type) { + case hwmon_fan: + return npcm7xx_write_fan(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static umode_t npcm7xx_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_fan: + return npcm7xx_fan_is_visible(data, attr, channel); + default: + return 0; + } +} + +static const u32 npcm7xx_fan_config[] = { + HWMON_F_INPUT | HWMON_F_TARGET, + HWMON_F_INPUT | HWMON_F_TARGET, + HWMON_F_INPUT | HWMON_F_TARGET, + HWMON_F_INPUT | HWMON_F_TARGET, + HWMON_F_INPUT | HWMON_F_TARGET, + HWMON_F_INPUT | HWMON_F_TARGET, + HWMON_F_INPUT | HWMON_F_TARGET, + HWMON_F_INPUT | HWMON_F_TARGET, + HWMON_F_INPUT, + HWMON_F_INPUT, + HWMON_F_INPUT, + HWMON_F_INPUT, + HWMON_F_INPUT, + HWMON_F_INPUT, + HWMON_F_INPUT, + HWMON_F_INPUT, + 0 +}; + +static const struct hwmon_channel_info npcm7xx_fan = { + .type = hwmon_fan, + .config = npcm7xx_fan_config, +}; + +static const struct hwmon_channel_info *npcm7xx_info[] = { + &npcm7xx_fan, + NULL +}; + +static const struct hwmon_ops npcm7xx_hwmon_ops = { + .is_visible = npcm7xx_is_visible, + .read = npcm7xx_read, + .write = npcm7xx_write, +}; + +static const struct hwmon_chip_info npcm7xx_chip_info = { + .ops = &npcm7xx_hwmon_ops, + .info = npcm7xx_info, +}; + +static int npcm750_fan_probe(struct platform_device *pdev) +{ + u32 apb_clk_src; + int ret = 0; + struct device *dev = &pdev->dev; + struct device_node *np; + struct npcm750_fan_data *priv; + struct resource res; + struct device *hwmon; + int i; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + np = dev->of_node; + + ret = of_address_to_resource(np, 0, &res); + if (ret) { + pr_err("\t\t\t of_address_to_resource fail ret %d\n", ret); + return -EINVAL; + } + + mft_virt_addr = (int)ioremap(res.start, resource_size(&res)); + + if (!mft_virt_addr) { + pr_err("\t\t\t mft_virt_addr fail\n"); + return -ENOMEM; + } + + /*DEBUG_MSG("MFT base is 0x%08X ,res.start 0x%08X\n", + (u32)mft_virt_addr, res.start);*/ + + mft_clk = devm_clk_get(&pdev->dev, NULL); + + if (IS_ERR(mft_clk)) { + pr_err(" MFT (FAN) probe failed: can't read clk.\n"); + return -ENODEV; + } + + clk_prepare_enable(mft_clk); + + for (i = 0; i < NPCM750_MFT_MAX_MODULE; i++) { + /* stop MFT0~7 clock */ + iowrite8((u8) NPCM750_MFT_NO_CLOCK_MODE, + (void *)MFT_REG_TCKC(i)); + + /* disable all interrupt */ + iowrite8((u8) 0x00, (void *)MFT_REG_TIEN(i)); + + /* clear all interrupt */ + iowrite8((u8) NPCM750_TICLR_CLEAR_ALL, + (void *)MFT_REG_TICLR(i)); + + /* set MFT0~7 clock prescaler */ + iowrite8((u8) NPCM750_MFT_CLKPS, (void *)MFT_REG_TPRSC(i)); + + /* set MFT0~7 mode (high-to-low transition) */ + iowrite8( + (u8) ( + NPCM750_TMCTRL_MDSEL(NPCM750_MFT_MODE_5) | + NPCM750_TMCTRL_TBEN | + NPCM750_TMCTRL_TAEN + ), + (void *) MFT_REG_TMCTRL(i) + ); + + /* set MFT0~7 Initial Count/Cap */ + iowrite16(NPCM750_MFT_TCNT, (void *)MFT_REG_TCNT1(i)); + iowrite16(NPCM750_MFT_TCNT, (void *)MFT_REG_TCNT2(i)); + + /* set MFT0~7 compare (equal to count) */ + iowrite8((u8)(NPCM750_TCPCFG_EQAEN | NPCM750_TCPCFG_EQBEN), + (void *)MFT_REG_TCPCFG(i)); + + /* set MFT0~7 compare value */ + iowrite16(NPCM750_MFT_TCPA, (void *)MFT_REG_TCPA(i)); + iowrite16(NPCM750_MFT_TCPB, (void *)MFT_REG_TCPB(i)); + + /* set MFT0~7 fan input FANIN 0~15 */ + iowrite8((u8) NPCM750_TINASEL_FANIN_DEFAULT, + (void *)MFT_REG_TINASEL(i)); + iowrite8((u8) NPCM750_TINASEL_FANIN_DEFAULT, + (void *)MFT_REG_TINBSEL(i)); + } + + /** fan tach structure initialization */ + S_npcm750_fantach_select = 0; + for (i = 0; i < NPCM750_MAX_FAN_TACH; i++) { + S_npcm750_fantach[i].u8FanStatusFlag = FAN_TACH_DISABLE; + S_npcm750_fantach[i].u8FanPulsePerRev = + DEFAULT_PULSE_PER_REVOLUTION; + S_npcm750_fantach[i].u16FanTachCnt = 0; + } + + for (i = 0; i < 8; i++) { + mft_irq[i] = platform_get_irq(pdev, i); + if (!mft_irq[i]) { + pr_err("%s - failed to map irq %d\n", __func__, i); + return (-EAGAIN); + } + } + + if (request_irq(mft_irq[0], (irq_handler_t) npcm750_mft0_isr, 0, + "NPCM750-MFT0", (void *) &u8dummy)) { + pr_err("NPCM750: register irq MFT0 failed\n"); + return (-EAGAIN); + } + + if (request_irq(mft_irq[1], (irq_handler_t) npcm750_mft0_isr, 0, + "NPCM750-MFT1", (void *) &u8dummy)) { + pr_err("NPCM750: register irq MFT1 failed\n"); + free_irq(mft_irq[0], (void *) &u8dummy); + return (-EAGAIN); + } + + if (request_irq(mft_irq[2], (irq_handler_t) npcm750_mft0_isr, 0, + "NPCM750-MFT2", (void *) &u8dummy)) { + pr_err("NPCM750: register irq MFT2 failed\n"); + free_irq(mft_irq[0], (void *) &u8dummy); + free_irq(mft_irq[1], (void *) &u8dummy); + return (-EAGAIN); + } + + if (request_irq(mft_irq[3], (irq_handler_t) npcm750_mft0_isr, 0, + "NPCM750-MFT3", (void *) &u8dummy)) { + pr_err("NPCM750: register irq MFT3 failed\n"); + free_irq(mft_irq[0], (void *) &u8dummy); + free_irq(mft_irq[1], (void *) &u8dummy); + free_irq(mft_irq[2], (void *) &u8dummy); + return (-EAGAIN); + } + + if (request_irq(mft_irq[4], (irq_handler_t) npcm750_mft0_isr, 0, + "NPCM750-MFT4", (void *) &u8dummy)) { + pr_err("NPCM750: register irq MFT4 failed\n"); + free_irq(mft_irq[0], (void *) &u8dummy); + free_irq(mft_irq[1], (void *) &u8dummy); + free_irq(mft_irq[2], (void *) &u8dummy); + free_irq(mft_irq[3], (void *) &u8dummy); + return (-EAGAIN); + } + + if (request_irq(mft_irq[5], (irq_handler_t) npcm750_mft0_isr, 0, + "NPCM750-MFT5", (void *) &u8dummy)) { + pr_err("NPCM750: register irq MFT5 failed\n"); + free_irq(mft_irq[0], (void *) &u8dummy); + free_irq(mft_irq[1], (void *) &u8dummy); + free_irq(mft_irq[2], (void *) &u8dummy); + free_irq(mft_irq[3], (void *) &u8dummy); + free_irq(mft_irq[4], (void *) &u8dummy); + return (-EAGAIN); + } + + if (request_irq(mft_irq[6], (irq_handler_t) npcm750_mft0_isr, 0, + "NPCM750-MFT6", (void *) &u8dummy)) { + pr_err("NPCM750: register irq MFT6 failed\n"); + free_irq(mft_irq[0], (void *) &u8dummy); + free_irq(mft_irq[1], (void *) &u8dummy); + free_irq(mft_irq[2], (void *) &u8dummy); + free_irq(mft_irq[3], (void *) &u8dummy); + free_irq(mft_irq[4], (void *) &u8dummy); + free_irq(mft_irq[5], (void *) &u8dummy); + return (-EAGAIN); + } + + if (request_irq(mft_irq[7], (irq_handler_t) npcm750_mft0_isr, 0, + "NPCM750-MFT7", (void *) &u8dummy)) { + pr_err("NPCM750: register irq MFT7 failed\n"); + free_irq(mft_irq[0], (void *) &u8dummy); + free_irq(mft_irq[1], (void *) &u8dummy); + free_irq(mft_irq[2], (void *) &u8dummy); + free_irq(mft_irq[3], (void *) &u8dummy); + free_irq(mft_irq[4], (void *) &u8dummy); + free_irq(mft_irq[5], (void *) &u8dummy); + free_irq(mft_irq[6], (void *) &u8dummy); + return (-EAGAIN); + } + + /** initialize fan tach polling timer */ + npcm750_fantach_timer.data = 0; + npcm750_fantach_timer.function = &npcm750_fantach_polling; + + /** set timer interval */ + npcm750_fantach_timer.expires = jiffies + msecs_to_jiffies(20); + + init_timer(&npcm750_fantach_timer); + add_timer(&npcm750_fantach_timer); + + apb_clk_src = clk_get_rate(mft_clk); + + pr_info("[FAN] APB4: %d\n", (int)apb_clk_src); + + /* Fan tach input clock = APB clock / prescalar, default is 255. */ + u32InputClock = apb_clk_src / (NPCM750_MFT_CLKPS + 1); + + pr_info("[FAN] PWM: %d\n", (int) u32InputClock); + pr_info("[FAN] InputClock: %d\n", (int) u32InputClock); + + hwmon = devm_hwmon_device_register_with_info(dev, "npcm7xx_fan", priv, + &npcm7xx_chip_info, NULL); + + if (IS_ERR(hwmon)) { + pr_err("FAN Driver failed - " + "devm_hwmon_device_register_with_groups failed\n"); + return PTR_ERR(hwmon); + } + + pr_info("NPCM750 FAN Driver probed\n"); + + for (i = 0; i < NPCM750_MAX_FAN_TACH; i++) + S_npcm750_fantach[i].u8FanStatusFlag = FAN_TACH_INIT; + + return 0; +} + +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:npcm750-fan"); + +static const struct of_device_id of_fan_match_table[] = { + { .compatible = "nuvoton,npcm750-fan", }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_fan_match_table); + +static struct platform_driver npcm750_fan_driver = { + .probe = npcm750_fan_probe, + .driver = { + .name = "npcm750_fan", + .of_match_table = of_fan_match_table, + }, +}; + +module_platform_driver(npcm750_fan_driver); + +MODULE_DESCRIPTION("Nuvoton NPCM750 FAN Driver"); +MODULE_AUTHOR("Tomer Maimon "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hwmon/npcm7xx-pwm.c b/drivers/hwmon/npcm7xx-pwm.c new file mode 100644 index 00000000000000..6f8eda05ffc859 --- /dev/null +++ b/drivers/hwmon/npcm7xx-pwm.c @@ -0,0 +1,451 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2014-2018 Nuvoton Technology corporation. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* NPCM7XX PWM port base address */ +#define NPCM7XX_PWM_REG_PR 0x0 +#define NPCM7XX_PWM_REG_CSR 0x4 +#define NPCM7XX_PWM_REG_CR 0x8 +#define NPCM7XX_PWM_REG_CNRx(PORT) (0xC + (12 * PORT)) +#define NPCM7XX_PWM_REG_CMRx(PORT) (0x10 + (12 * PORT)) +#define NPCM7XX_PWM_REG_PDRx(PORT) (0x14 + (12 * PORT)) +#define NPCM7XX_PWM_REG_PIER 0x3C +#define NPCM7XX_PWM_REG_PIIR 0x40 + +#define NPCM7XX_PWM_CTRL_CH0_MODE_BIT BIT(3) +#define NPCM7XX_PWM_CTRL_CH1_MODE_BIT BIT(11) +#define NPCM7XX_PWM_CTRL_CH2_MODE_BIT BIT(15) +#define NPCM7XX_PWM_CTRL_CH3_MODE_BIT BIT(19) + +#define NPCM7XX_PWM_CTRL_CH0_INV_BIT BIT(2) +#define NPCM7XX_PWM_CTRL_CH1_INV_BIT BIT(10) +#define NPCM7XX_PWM_CTRL_CH2_INV_BIT BIT(14) +#define NPCM7XX_PWM_CTRL_CH3_INV_BIT BIT(18) + +#define NPCM7XX_PWM_CTRL_CH0_EN_BIT BIT(0) +#define NPCM7XX_PWM_CTRL_CH1_EN_BIT BIT(8) +#define NPCM7XX_PWM_CTRL_CH2_EN_BIT BIT(12) +#define NPCM7XX_PWM_CTRL_CH3_EN_BIT BIT(16) + +/* Define the maximum PWM channel number */ +#define NPCM7XX_PWM_MAX_CHN_NUM 8 +#define NPCM7XX_PWM_MAX_CHN_NUM_IN_A_MODULE 4 +#define NPCM7XX_PWM_MAX_MODULES 2 + +/* Define the Counter Register, value = 100 for match 100% */ +#define NPCM7XX_PWM_COUNTER_DEFALUT_NUM 255 +#define NPCM7XX_PWM_COMPARATOR_DEFALUT_NUM 127 + +#define NPCM7XX_PWM_COMPARATOR_MAX 255 + + +/* default all PWM channels PRESCALE2 = 1 */ +#define NPCM7XX_PWM_PRESCALE2_DEFALUT_CH0 0x4 +#define NPCM7XX_PWM_PRESCALE2_DEFALUT_CH1 0x40 +#define NPCM7XX_PWM_PRESCALE2_DEFALUT_CH2 0x400 +#define NPCM7XX_PWM_PRESCALE2_DEFALUT_CH3 0x4000 + +#define PWM_OUTPUT_FREQ_25KHZ 25000 +#define PWN_CNT_DEFAULT 256 +#define MIN_PRESCALE1 2 +#define NPCM7XX_PWM_PRESCALE_SHIFT_CH01 8 + +#define NPCM7XX_PWM_PRESCALE2_DEFALUT (NPCM7XX_PWM_PRESCALE2_DEFALUT_CH0 | \ + NPCM7XX_PWM_PRESCALE2_DEFALUT_CH1 | \ + NPCM7XX_PWM_PRESCALE2_DEFALUT_CH2 | \ + NPCM7XX_PWM_PRESCALE2_DEFALUT_CH3) + +#define NPCM7XX_PWM_CTRL_MODE_DEFALUT (NPCM7XX_PWM_CTRL_CH0_MODE_BIT | \ + NPCM7XX_PWM_CTRL_CH1_MODE_BIT | \ + NPCM7XX_PWM_CTRL_CH2_MODE_BIT | \ + NPCM7XX_PWM_CTRL_CH3_MODE_BIT) + +#define NPCM7XX_PWM_CTRL_EN_DEFALUT (NPCM7XX_PWM_CTRL_CH0_EN_BIT | \ + NPCM7XX_PWM_CTRL_CH1_EN_BIT | \ + NPCM7XX_PWM_CTRL_CH2_EN_BIT | \ + NPCM7XX_PWM_CTRL_CH3_EN_BIT) + +struct npcm7xx_pwm_fan_data { + unsigned long clk_freq; + void __iomem *pwm_base[NPCM7XX_PWM_MAX_MODULES]; + struct mutex npcm7xx_pwm_lock[NPCM7XX_PWM_MAX_CHN_NUM]; +}; + +static const struct of_device_id pwm_fan_dt_id[]; + +static int npcm7xx_pwm_config_set(struct npcm7xx_pwm_fan_data *data, int channel, + u16 val) +{ + u32 PWMChannel = (channel % NPCM7XX_PWM_MAX_CHN_NUM_IN_A_MODULE); + u32 n_module = (channel / NPCM7XX_PWM_MAX_CHN_NUM_IN_A_MODULE); + u32 u32TmpBuf = 0, ctrl_en_bit, env_bit; + + /* + * Config PWM Comparator register for setting duty cycle + */ + if (val < 0 || val > NPCM7XX_PWM_COMPARATOR_MAX) + return -EINVAL; + + /* write new CMR value */ + iowrite32(val, data->pwm_base[n_module] + + NPCM7XX_PWM_REG_CMRx(PWMChannel)); + + u32TmpBuf = ioread32(data->pwm_base[n_module] + NPCM7XX_PWM_REG_CR); + + switch (PWMChannel) { + case 0: + ctrl_en_bit = NPCM7XX_PWM_CTRL_CH0_EN_BIT; + env_bit = NPCM7XX_PWM_CTRL_CH0_INV_BIT; + break; + case 1: + ctrl_en_bit = NPCM7XX_PWM_CTRL_CH1_EN_BIT; + env_bit = NPCM7XX_PWM_CTRL_CH1_INV_BIT; + break; + case 2: + ctrl_en_bit = NPCM7XX_PWM_CTRL_CH2_EN_BIT; + env_bit = NPCM7XX_PWM_CTRL_CH2_INV_BIT; + break; + case 3: + ctrl_en_bit = NPCM7XX_PWM_CTRL_CH3_EN_BIT; + env_bit = NPCM7XX_PWM_CTRL_CH3_INV_BIT; + break; + default: + return -ENODEV; + } + + if (val == 0) { + /* Disable PWM */ + u32TmpBuf &= ~(ctrl_en_bit); + u32TmpBuf |= env_bit; + } + else { + /* Enable PWM */ + u32TmpBuf |= ctrl_en_bit; + u32TmpBuf &= ~(env_bit); + } + + mutex_lock(&data->npcm7xx_pwm_lock[n_module]); + iowrite32(u32TmpBuf, data->pwm_base[n_module] + NPCM7XX_PWM_REG_CR); + mutex_unlock(&data->npcm7xx_pwm_lock[n_module]); + + return 0; +} + +static int npcm7xx_read_pwm(struct device *dev, u32 attr, int channel, + long *val) +{ + struct npcm7xx_pwm_fan_data *data = dev_get_drvdata(dev); + u32 PWMChannel = (channel % NPCM7XX_PWM_MAX_CHN_NUM_IN_A_MODULE); + u32 n_module = (channel / NPCM7XX_PWM_MAX_CHN_NUM_IN_A_MODULE); + + if (IS_ERR(data)) + return PTR_ERR(data); + + switch (attr) { + case hwmon_pwm_input: + *val = (long)ioread32(data->pwm_base[n_module] + + NPCM7XX_PWM_REG_CMRx(PWMChannel)); + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int npcm7xx_write_pwm(struct device *dev, u32 attr, int channel, + long val) +{ + struct npcm7xx_pwm_fan_data *data = dev_get_drvdata(dev); + int err = 0; + + switch (attr) { + case hwmon_pwm_input: + err = npcm7xx_pwm_config_set(data, channel, (u16)val); + break; + default: + err = -EOPNOTSUPP; + break; + } + + return err; +} + +static umode_t npcm7xx_pwm_is_visible(const void *_data, u32 attr, int channel) +{ + switch (attr) { + case hwmon_pwm_input: + return 0644; + default: + return 0; + } +} + +static int npcm7xx_read_fan(struct device *dev, u32 attr, int channel, + long *val) +{ + struct npcm7xx_pwm_fan_data *data = dev_get_drvdata(dev); + u32 PWMChannel = (channel % NPCM7XX_PWM_MAX_CHN_NUM_IN_A_MODULE); + u32 n_module = (channel / NPCM7XX_PWM_MAX_CHN_NUM_IN_A_MODULE); + + if (IS_ERR(data)) + return PTR_ERR(data); + + switch (attr) { + case hwmon_fan_input: + *val = (long)ioread32(data->pwm_base[n_module] + + NPCM7XX_PWM_REG_CMRx(PWMChannel)); + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int npcm7xx_write_fan(struct device *dev, u32 attr, int channel, + long val) +{ + struct npcm7xx_pwm_fan_data *data = dev_get_drvdata(dev); + int err = 0; + + switch (attr) { + case hwmon_fan_target: + err = npcm7xx_pwm_config_set(data, channel, (u16)val); + break; + default: + err = -EOPNOTSUPP; + break; + } + + return err; +} + +static umode_t npcm7xx_fan_is_visible(const void *_data, u32 attr, int channel) +{ + switch (attr) { + case hwmon_fan_input: + return 0644; + default: + return 0; + } +} + +static int npcm7xx_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + switch (type) { + case hwmon_pwm: + return npcm7xx_read_pwm(dev, attr, channel, val); + case hwmon_fan: + return npcm7xx_read_fan(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static int npcm7xx_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + switch (type) { + case hwmon_pwm: + return npcm7xx_write_pwm(dev, attr, channel, val); + case hwmon_fan: + return npcm7xx_write_fan(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static umode_t npcm7xx_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_pwm: + return npcm7xx_pwm_is_visible(data, attr, channel); + case hwmon_fan: + return npcm7xx_fan_is_visible(data, attr, channel); + default: + return 0; + } +} + +static const u32 npcm7xx_pwm_config[] = { + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + 0 +}; + +static const struct hwmon_channel_info npcm7xx_pwm = { + .type = hwmon_pwm, + .config = npcm7xx_pwm_config, +}; + +static const u32 npcm7xx_fan_config[] = { + HWMON_F_INPUT | HWMON_F_TARGET, + HWMON_F_INPUT | HWMON_F_TARGET, + HWMON_F_INPUT | HWMON_F_TARGET, + HWMON_F_INPUT | HWMON_F_TARGET, + HWMON_F_INPUT | HWMON_F_TARGET, + HWMON_F_INPUT | HWMON_F_TARGET, + HWMON_F_INPUT | HWMON_F_TARGET, + HWMON_F_INPUT | HWMON_F_TARGET, + HWMON_F_INPUT, + HWMON_F_INPUT, + HWMON_F_INPUT, + HWMON_F_INPUT, + HWMON_F_INPUT, + HWMON_F_INPUT, + HWMON_F_INPUT, + HWMON_F_INPUT, + 0 +}; + +static const struct hwmon_channel_info npcm7xx_fan = { + .type = hwmon_fan, + .config = npcm7xx_fan_config, +}; + +static const struct hwmon_channel_info *npcm7xx_info[] = { + &npcm7xx_pwm, + &npcm7xx_fan, + NULL +}; + +static const struct hwmon_ops npcm7xx_hwmon_ops = { + .is_visible = npcm7xx_is_visible, + .read = npcm7xx_read, + .write = npcm7xx_write, +}; + +static const struct hwmon_chip_info npcm7xx_chip_info = { + .ops = &npcm7xx_hwmon_ops, + .info = npcm7xx_info, +}; + +static int npcm7xx_pwm_fan_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct npcm7xx_pwm_fan_data *data; + struct resource res[NPCM7XX_PWM_MAX_MODULES]; + struct device *hwmon; + struct clk *clk; + int m, ch, res_cnt, ret; + u32 Prescale_val, output_freq; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + for (res_cnt = 0; res_cnt < NPCM7XX_PWM_MAX_MODULES ; res_cnt++) { + ret = of_address_to_resource(dev->of_node, res_cnt, + &res[res_cnt]); + if (ret) { + pr_err("PWM of_address_to_resource fail ret %d\n", + ret); + return -EINVAL; + } + + data->pwm_base[res_cnt] = + devm_ioremap_resource(dev, &(res[res_cnt])); + pr_debug("pwm%d base is 0x%08X, res.start 0x%08X , size 0x%08X\n", + res_cnt, (u32)data->pwm_base[res_cnt], + res[res_cnt].start, resource_size(&(res[res_cnt]))); + + if (!data->pwm_base[res_cnt]) { + pr_err("pwm probe failed: can't read pwm base address for resource %d.\n", + res_cnt); + return -ENOMEM; + } + + mutex_init(&data->npcm7xx_pwm_lock[res_cnt]); + } + + clk = devm_clk_get(dev, NULL); + if (IS_ERR(clk)) + return -ENODEV; + + data->clk_freq = clk_get_rate(clk); + + /* Adjust NPCM7xx PWMs output frequency to ~25Khz */ + output_freq = data->clk_freq / PWN_CNT_DEFAULT; + Prescale_val = DIV_ROUND_CLOSEST(output_freq, PWM_OUTPUT_FREQ_25KHZ); + + /* If Prescale_val = 0, then the prescale output clock is stopped */ + if (Prescale_val < MIN_PRESCALE1) + Prescale_val = MIN_PRESCALE1; + /* + * Prescale_val need to decrement in one because in the PWM Prescale + * register the Prescale value increment by one + */ + Prescale_val--; + + /* Setting PWM Prescale Register value register to both modules */ + Prescale_val |= (Prescale_val << NPCM7XX_PWM_PRESCALE_SHIFT_CH01); + + for (m = 0; m < NPCM7XX_PWM_MAX_MODULES ; m++) { + iowrite32(Prescale_val, + data->pwm_base[m] + NPCM7XX_PWM_REG_PR); + iowrite32(NPCM7XX_PWM_PRESCALE2_DEFALUT, + data->pwm_base[m] + NPCM7XX_PWM_REG_CSR); + iowrite32(NPCM7XX_PWM_CTRL_MODE_DEFALUT, + data->pwm_base[m] + NPCM7XX_PWM_REG_CR); + + for (ch = 0; ch < NPCM7XX_PWM_MAX_CHN_NUM; ch++) { + iowrite32(NPCM7XX_PWM_COUNTER_DEFALUT_NUM, + data->pwm_base[m] + NPCM7XX_PWM_REG_CNRx(ch)); + iowrite32(NPCM7XX_PWM_COMPARATOR_DEFALUT_NUM, + data->pwm_base[m] + NPCM7XX_PWM_REG_CMRx(ch)); + } + + iowrite32(NPCM7XX_PWM_CTRL_MODE_DEFALUT | + NPCM7XX_PWM_CTRL_EN_DEFALUT, + data->pwm_base[m] + NPCM7XX_PWM_REG_CR); + } + + hwmon = devm_hwmon_device_register_with_info(dev, "npcm7xx_pwm", data, + &npcm7xx_chip_info, NULL); + + if (IS_ERR(hwmon)) { + pr_err("PWM Driver failed - devm_hwmon_device_register_with_groups failed\n"); + return PTR_ERR(hwmon); + } + + pr_info("NPCM7XX PWM Driver probed, PWM output Freq %dHz\n", + output_freq / ((Prescale_val & 0xf) + 1)); + + return 0; +} + +static const struct of_device_id of_pwm_fan_match_table[] = { + { .compatible = "nuvoton,npcm750-pwm", }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_pwm_fan_match_table); + +static struct platform_driver npcm7xx_pwm_fan_driver = { + .probe = npcm7xx_pwm_fan_probe, + .driver = { + .name = "npcm7xx_pwm_fan", + .of_match_table = of_pwm_fan_match_table, + }, +}; + +module_platform_driver(npcm7xx_pwm_fan_driver); + +MODULE_DESCRIPTION("Nuvoton NPCM7XX PWM and Fan Tacho driver"); +MODULE_AUTHOR("Tomer Maimon "); +MODULE_LICENSE("GPL v2"); From e4c6b79c3cd1ab2b29866dbf7dbd9c79f19f4797 Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Thu, 28 Dec 2017 12:43:01 +0200 Subject: [PATCH 16/37] dt-binding: pinctrl: document NPCM7xx pin controller DT bindings Added device tree binding documentation for Nuvoton NPCM7xx pin controller Signed-off-by: Tomer Maimon --- .../pinctrl/nuvoton,npcm7xx-pinctrl.txt | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 Documentation/devicetree/bindings/pinctrl/nuvoton,npcm7xx-pinctrl.txt diff --git a/Documentation/devicetree/bindings/pinctrl/nuvoton,npcm7xx-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/nuvoton,npcm7xx-pinctrl.txt new file mode 100644 index 00000000000000..a2bba4d5893e45 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/nuvoton,npcm7xx-pinctrl.txt @@ -0,0 +1,70 @@ +Nuvoton NPCM7XX Pin Controllers + +The NPCM7XX Pin Controller multi-function routed through +the multiplexing block, Each pin supports GPIO functionality (GPIOx) +and multiple functions that directly connect the pin to different +hardware blocks. + +Required properties: +- compatible : "nuvoton,npcm750-pinctrl" for Poleg NPCM750. + +Contents of function subnode node +--------------------------------- +Required subnode-properties: +- groups : An array of strings. Each string contains the name of a group. +- function: A string containing the name of the function to mux to the + group. + + Valid values for group and function names can be found from looking at the + group and function arrays in driver files: + drivers/pinctrl/nuvoton/pinctrl-npcm7xx.c + +For example, pinctrl might have subnodes like the following: + r1err_pins: r1err_pins { + groups = "r1err"; + function = "r1err"; + }; + r1md_pins: r1md_pins { + groups = "r1md"; + function = "r1md"; + }; + r1_pins: r1_pins { + groups = "r1"; + function = "r1"; + }; + +For a specific board, if it wants to use EMC (10/100 network), +it can add the following to its board-specific .dts file. +emc0: eth@f0825000 { + pinctrl-names = "default"; + pinctrl-0 = <&r1_pins + &r1err_pins + &r1md_pins>; + phy-mode = "rmii"; + +if EMC hardware is not used the EMC pin can used for GPIO56 + pinctrl-names = "default"; + pinctrl-0 = <&gpio56_pins> + +Examples +======== + +pinctrl: pinctrl@0 { + compatible = "nuvoton,npcm7xx-pinctrl"; + status = "okay"; + iox1_pins: iox1_pins { + groups = "iox1"; + function = "iox1"; + }; + iox2_pins: iox2_pins { + groups = "iox2"; + function = "iox2"; + }; + + .... + + clkreq_pins: clkreq_pins { + groups = "clkreq"; + function = "clkreq"; + }; +}; From 51e70a25402859a6475671ebeaf96a37ad66c1c2 Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Thu, 28 Dec 2017 12:43:02 +0200 Subject: [PATCH 17/37] pinctrl: npcm: add NPCM7xx pin controller driver Add Nuvoton BMC NPCM7xx pin controller driver. The NPCM7XX Pin Controller multi-function routed through the multiplexing block, Each pin supports GPIO functionality (GPIOx) and multiple functions that directly connect the pin to different hardware blocks. Signed-off-by: Tomer Maimon --- drivers/pinctrl/Kconfig | 1 + drivers/pinctrl/Makefile | 2 + drivers/pinctrl/nuvoton/Kconfig | 12 + drivers/pinctrl/nuvoton/Makefile | 1 + drivers/pinctrl/nuvoton/pinctrl-npcm7xx.c | 2112 +++++++++++++++++++++ 5 files changed, 2128 insertions(+) create mode 100644 drivers/pinctrl/nuvoton/Kconfig create mode 100644 drivers/pinctrl/nuvoton/Makefile create mode 100644 drivers/pinctrl/nuvoton/pinctrl-npcm7xx.c diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 8c054f5ccc1189..c7884332b58655 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -352,6 +352,7 @@ source "drivers/pinctrl/uniphier/Kconfig" source "drivers/pinctrl/vt8500/Kconfig" source "drivers/pinctrl/mediatek/Kconfig" source "drivers/pinctrl/zte/Kconfig" +source "drivers/pinctrl/nuvoton/Kconfig" config PINCTRL_XWAY bool diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 2bc641d62400b8..925d99b05e4730 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -62,3 +62,5 @@ obj-$(CONFIG_PINCTRL_UNIPHIER) += uniphier/ obj-$(CONFIG_ARCH_VT8500) += vt8500/ obj-$(CONFIG_PINCTRL_MTK) += mediatek/ obj-$(CONFIG_PINCTRL_ZX) += zte/ +obj-$(CONFIG_ARCH_NPCM7XX) += nuvoton/ + diff --git a/drivers/pinctrl/nuvoton/Kconfig b/drivers/pinctrl/nuvoton/Kconfig new file mode 100644 index 00000000000000..d849074c8e1e90 --- /dev/null +++ b/drivers/pinctrl/nuvoton/Kconfig @@ -0,0 +1,12 @@ +config PINCTRL_NPCM7XX + bool "Pinctrl driver for Nuvoton NPCM7XX" + depends on (ARCH_NPCM7XX || COMPILE_TEST) && OF + select MFD_SYSCON + select GPIOLIB + select GPIOLIB_IRQCHIP + select PINMUX + select PINCONF + select GENERIC_PINCONF + help + Say Y here to enable pin controller and GPIO support + for Nuvoton NPCM7xx SoCs. diff --git a/drivers/pinctrl/nuvoton/Makefile b/drivers/pinctrl/nuvoton/Makefile new file mode 100644 index 00000000000000..244dcd978d6a5a --- /dev/null +++ b/drivers/pinctrl/nuvoton/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_PINCTRL_NPCM7XX) += pinctrl-npcm7xx.o diff --git a/drivers/pinctrl/nuvoton/pinctrl-npcm7xx.c b/drivers/pinctrl/nuvoton/pinctrl-npcm7xx.c new file mode 100644 index 00000000000000..0c6f6a04441bf7 --- /dev/null +++ b/drivers/pinctrl/nuvoton/pinctrl-npcm7xx.c @@ -0,0 +1,2112 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2016-2018 Nuvoton Technology corporation. +// Copyright (c) 2016, Dell Inc + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GPIO_BANK_NUM 8 + +/* GCR registers */ +#define NPCM7XX_GCR_PDID 0x00 +#define NPCM7XX_GCR_MFSEL1 0x0C +#define NPCM7XX_GCR_MFSEL2 0x10 +#define NPCM7XX_GCR_MFSEL3 0x64 +#define NPCM7XX_GCR_MFSEL4 0xb0 +#define NPCM7XX_GCR_CPCTL 0xD0 +#define NPCM7XX_GCR_CP2BST 0xD4 +#define NPCM7XX_GCR_B2CPNT 0xD8 +#define NPCM7XX_GCR_I2CSEGSEL 0xE0 +#define NPCM7XX_GCR_I2CSEGCTL 0xE4 + +#define SMBXX_BITS 2 +#define SMB0SS_SHIFT 0 +#define SMB1SS_SHIFT 2 +#define SMB2SS_SHIFT 4 +#define SMB3SS_SHIFT 6 +#define SMB4SS_SHIFT 8 +#define SMB5SS_SHIFT 10 +#define WEN0_SS BIT(12) +#define WEN1_SS BIT(13) +#define WEN2_SS BIT(14) +#define WEN3_SS BIT(15) +#define WEN4_SS BIT(16) +#define WEN5_SS BIT(17) + +#define NPCM7XX_GCR_SRCNT 0x68 +#define SRCNT_ESPI BIT(3) +/* SPI0D = 1:1 + * SPI0C = 2:1 + * ESPI = 3:1 + * TDO = 4:1 + */ +#define NPCM7XX_GCR_FLOCKR1 0x74 +#define NPCM7XX_GCR_DSCNT 0x78 +/* SPI0D = 1:1 (8,12) + * SPI0C = 2:1 (8,12) + * SYNC1 = 3:1 (4,8) + * ESPI = 6:2 (8,12,16,24) + * SPLD = 9:1 (2,4) + */ + +#define NPCM7XX_GCR_NONE 0 + +/* GPIO module */ +#define GPIO_PER_BANK 32 + +#define NPCM_GP_N_TLOCK1 0x00 +#define NPCM_GP_N_DIN 0x04 /* Data IN */ +#define NPCM_GP_N_POL 0x08 /* Polarity */ +#define NPCM_GP_N_DOUT 0x0c /* Data OUT */ +#define NPCM_GP_N_OE 0x10 /* Output Enable */ +#define NPCM_GP_N_OTYP 0x14 +#define NPCM_GP_N_MP 0x18 +#define NPCM_GP_N_PU 0x1c /* Pull-up */ +#define NPCM_GP_N_PD 0x20 /* Pull-down */ +#define NPCM_GP_N_DBNC 0x24 /* Debounce */ +#define NPCM_GP_N_EVTYP 0x28 /* Event Type */ +#define NPCM_GP_N_EVBE 0x2c /* Event Both Edge */ +#define NPCM_GP_N_OBL0 0x30 +#define NPCM_GP_N_OBL1 0x34 +#define NPCM_GP_N_OBL2 0x38 +#define NPCM_GP_N_OBL3 0x3c +#define NPCM_GP_N_EVEN 0x40 /* Event Enable */ +#define NPCM_GP_N_EVENS 0x44 /* Event Set (enable) */ +#define NPCM_GP_N_EVENC 0x48 /* Event Clear (disable) */ +#define NPCM_GP_N_EVST 0x4c /* Event Status */ +#define NPCM_GP_N_SPLCK 0x50 +#define NPCM_GP_N_MPLCK 0x54 +#define NPCM_GP_N_IEM 0x58 /* Input Enable */ +#define NPCM_GP_N_OSRC 0x5c +#define NPCM_GP_N_ODSC 0x60 +#define NPCM_GP_N_DOS 0x68 /* Data OUT Set */ +#define NPCM_GP_N_DOC 0x6c /* Data OUT Clear */ +#define NPCM_GP_N_OES 0x70 /* Output Enable Set */ +#define NPCM_GP_N_OEC 0x74 /* Output Enable Clear */ +#define NPCM_GP_N_TLOCK2 0x7c + +/* Structure for register banks */ +struct NPCM_GPIO { + void __iomem *base; + struct gpio_chip gc; + int irqbase; + int irq; + spinlock_t lock; + void *priv; + struct irq_chip irq_chip; + u32 pinctrl_id; +}; + +struct NPCM7xx_pinctrl { + struct pinctrl_dev *pctldev; + struct device *dev; + struct NPCM_GPIO gpio_bank[GPIO_BANK_NUM]; + struct irq_domain *domain; + struct regmap *gcr_regmap; + void __iomem *regs; + u32 bank_num; +}; + +enum operand{ + opSET, + opGETBIT, + opSETBIT, + opCLRBIT, +}; + +/* Perform locked bit operations on GPIO registers */ +static int gpio_bitop(struct NPCM_GPIO *bank, int op, unsigned int offset, + int reg) +{ + unsigned long flags; + u32 mask, val; + + mask = (1L << offset); + spin_lock_irqsave(&bank->lock, flags); + switch (op) { + case opSET: + iowrite32(mask, bank->base + reg); + break; + case opGETBIT: + mask &= ioread32(bank->base + reg); + break; + case opSETBIT: + val = ioread32(bank->base + reg); + iowrite32(val|mask, bank->base + reg); + break; + case opCLRBIT: + val = ioread32(bank->base + reg); + iowrite32(val&(~mask), bank->base + reg); + break; + } + spin_unlock_irqrestore(&bank->lock, flags); + return !!mask; +} + +/* + * GPIO code + */ + +/* Dump GPIO and GCR registers */ +static void npcmgpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) +{ + struct NPCM_GPIO *bank = gpiochip_get_data(chip); + u8 *base; + + base = bank->base; + seq_printf(s, "-- module %d [gpio%d - %d]\n", + bank->gc.base / bank->gc.ngpio, + bank->gc.base, + bank->gc.base + bank->gc.ngpio); + seq_printf(s, "DIN :%.8x DOUT:%.8x IE :%.8x OE :%.8x\n", + ioread32(base + NPCM_GP_N_DIN), + ioread32(base + NPCM_GP_N_DOUT), + ioread32(base + NPCM_GP_N_IEM), + ioread32(base + NPCM_GP_N_OE)); + seq_printf(s, "PU :%.8x PD :%.8x DB :%.8x POL :%.8x\n", + ioread32(base + NPCM_GP_N_PU), + ioread32(base + NPCM_GP_N_PD), + ioread32(base + NPCM_GP_N_DBNC), + ioread32(base + NPCM_GP_N_POL)); + seq_printf(s, "ETYP:%.8x EVBE:%.8x EVEN:%.8x EVST:%.8x\n", + ioread32(base + NPCM_GP_N_EVTYP), + ioread32(base + NPCM_GP_N_EVBE), + ioread32(base + NPCM_GP_N_EVEN), + ioread32(base + NPCM_GP_N_EVST)); + seq_printf(s, "OTYP:%.8x OSRC:%.8x ODSC:%.8x\n", + ioread32(base + NPCM_GP_N_OTYP), + ioread32(base + NPCM_GP_N_OSRC), + ioread32(base + NPCM_GP_N_ODSC)); + seq_printf(s, "OBL0:%.8x OBL1:%.8x OBL2:%.8x OBL3:%.8x\n", + ioread32(base + NPCM_GP_N_OBL0), + ioread32(base + NPCM_GP_N_OBL1), + ioread32(base + NPCM_GP_N_OBL2), + ioread32(base + NPCM_GP_N_OBL3)); + seq_printf(s, "SLCK:%.8x MLCK:%.8x\n", + ioread32(base + NPCM_GP_N_SPLCK), + ioread32(base + NPCM_GP_N_MPLCK)); +} + +/* Get direction of GPIO pin */ +static int npcmgpio_get_direction(struct gpio_chip *chip, unsigned int offset) +{ + struct NPCM_GPIO *bank = gpiochip_get_data(chip); + u32 oe, ie; + + /* Get Input & Output state */ + ie = gpio_bitop(bank, opGETBIT, offset, NPCM_GP_N_IEM); + oe = gpio_bitop(bank, opGETBIT, offset, NPCM_GP_N_OE); + if (ie && !oe) + return GPIOF_DIR_IN; + else if (oe && !ie) + return GPIOF_DIR_OUT; + return -EINVAL; +} + +/* Set GPIO to Input */ +static int npcmgpio_direction_input(struct gpio_chip *chip, unsigned int offset) +{ + dev_dbg(chip->parent, "%s: %d\n", __func__, offset); + return pinctrl_gpio_direction_input(offset + chip->base); +} + +/* Set GPIO to Output with initial value */ +static int npcmgpio_direction_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct NPCM_GPIO *bank = gpiochip_get_data(chip); + + dev_dbg(chip->parent, "gpio_direction_output: offset%d = %x\n", offset, + value); + /* Check if we're enabled as an interrupt.. */ + if (gpio_bitop(bank, opGETBIT, offset, NPCM_GP_N_EVEN) && + gpio_bitop(bank, opGETBIT, offset, NPCM_GP_N_IEM)) { + dev_dbg(chip->parent, + "gpio_direction_output: IRQ enabled on offset%d\n", + offset); + return -EINVAL; + } + + gpio_bitop(bank, opSETBIT, offset, value ? NPCM_GP_N_DOS : + NPCM_GP_N_DOC); + return pinctrl_gpio_direction_output(offset + chip->base); +} + +/* Retrieve value of GPIO */ +static int npcmgpio_get_value(struct gpio_chip *chip, unsigned int offset) +{ + struct NPCM_GPIO *bank = gpiochip_get_data(chip); + int dir; + + dev_dbg(chip->parent, "gpio_get: gpio%d\n", offset); + dir = npcmgpio_get_direction(chip, offset); + return gpio_bitop(bank, opGETBIT, offset, dir == GPIOF_DIR_OUT ? + NPCM_GP_N_DOUT : NPCM_GP_N_DIN); +} + +/* Set value of Output GPIO */ +static void npcmgpio_set_value(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct NPCM_GPIO *bank = gpiochip_get_data(chip); + + dev_dbg(chip->parent, "gpio_set: gpio%d = %x\n", offset, value); + if (npcmgpio_get_direction(chip, offset) == GPIOF_DIR_OUT) + gpio_bitop(bank, opSETBIT, offset, value ? NPCM_GP_N_DOS : + NPCM_GP_N_DOC); +} + +/* Request GPIO */ +static int npcmgpio_gpio_request(struct gpio_chip *chip, unsigned int offset) +{ + dev_dbg(chip->parent, "gpio_request: offset%d\n", offset); + return pinctrl_request_gpio(offset+chip->base); +} + +/* Release GPIO */ +static void npcmgpio_gpio_free(struct gpio_chip *chip, unsigned int offset) +{ + dev_dbg(chip->parent, "gpio_free: offset%d\n", offset); + pinctrl_free_gpio(offset+chip->base); +} + +/* + * IRQ code + */ +static void npcmgpio_irq_handler(struct irq_desc *desc) +{ + struct gpio_chip *gc; + struct irq_chip *chip; + struct NPCM_GPIO *bank; + u32 sts, en, bit; + + gc = irq_desc_get_handler_data(desc); + bank = gpiochip_get_data(gc); + chip = irq_desc_get_chip(desc); + + chained_irq_enter(chip, desc); + sts = ioread32(bank->base + NPCM_GP_N_EVST); + en = ioread32(bank->base + NPCM_GP_N_EVEN); + dev_dbg(chip->parent_device, "==> got irq sts %.8x %.8x\n", sts, + en); + + sts &= en; + for_each_set_bit(bit, (const void *)&sts, GPIO_PER_BANK) + generic_handle_irq(irq_linear_revmap(gc->irqdomain, bit)); + chained_irq_exit(chip, desc); +} + +/* Set trigger type of GPIO interrupt */ +static int npcmgpio_set_irq_type(struct irq_data *d, unsigned int type) +{ + struct NPCM_GPIO *bank = + gpiochip_get_data(irq_data_get_irq_chip_data(d)); + unsigned int gpio = d->hwirq; + + dev_dbg(d->chip->parent_device, "setirqtype: %u.%u = %u\n", gpio, + d->irq, type); + switch (type) { + case IRQ_TYPE_EDGE_RISING: + /* EVTYP=1, POL=0, EVBE=0 */ + dev_dbg(d->chip->parent_device, "edge.rising\n"); + gpio_bitop(bank, opCLRBIT, gpio, NPCM_GP_N_EVBE); + gpio_bitop(bank, opCLRBIT, gpio, NPCM_GP_N_POL); + break; + case IRQ_TYPE_EDGE_FALLING: + /* EVTYP=1, POL=1, EVBE=1 */ + dev_dbg(d->chip->parent_device, "edge.falling\n"); + gpio_bitop(bank, opCLRBIT, gpio, NPCM_GP_N_EVBE); + gpio_bitop(bank, opSETBIT, gpio, NPCM_GP_N_POL); + break; + case IRQ_TYPE_EDGE_BOTH: + /* EVTYP=1, POL=0, EVBE=1 */ + dev_dbg(d->chip->parent_device, "edge.both\n"); + gpio_bitop(bank, opSETBIT, gpio, NPCM_GP_N_EVBE); + break; + case IRQ_TYPE_LEVEL_LOW: + /* EVTYP=0, POL=1 */ + dev_dbg(d->chip->parent_device, "level.low\n"); + gpio_bitop(bank, opSETBIT, gpio, NPCM_GP_N_POL); + break; + case IRQ_TYPE_LEVEL_HIGH: + /* EVTYP=0, POL=0 */ + dev_dbg(d->chip->parent_device, "level.high\n"); + gpio_bitop(bank, opCLRBIT, gpio, NPCM_GP_N_POL); + break; + default: + dev_dbg(d->chip->parent_device, "invalid irq type\n"); + return -EINVAL; + } + if (type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) { + gpio_bitop(bank, opCLRBIT, gpio, NPCM_GP_N_EVTYP); + irq_set_handler_locked(d, handle_level_irq); + } else if (type & (IRQ_TYPE_EDGE_BOTH | IRQ_TYPE_EDGE_RISING + | IRQ_TYPE_EDGE_FALLING)) { + gpio_bitop(bank, opSETBIT, gpio, NPCM_GP_N_EVTYP); + irq_set_handler_locked(d, handle_edge_irq); + } + return 0; +} + +/* ACK GPIO interrupt */ +static void npcmgpio_irq_ack(struct irq_data *d) +{ + struct NPCM_GPIO *bank = + gpiochip_get_data(irq_data_get_irq_chip_data(d)); + unsigned int gpio = d->hwirq; + + dev_dbg(d->chip->parent_device, "irq_ack: %u.%u\n", gpio, d->irq); + gpio_bitop(bank, opSET, gpio, NPCM_GP_N_EVST); +} + +/* Disable GPIO interrupt */ +static void npcmgpio_irq_mask(struct irq_data *d) +{ + struct NPCM_GPIO *bank = + gpiochip_get_data(irq_data_get_irq_chip_data(d)); + unsigned int gpio = d->hwirq; + + /* Clear events */ + dev_dbg(d->chip->parent_device, "irq_mask: %u.%u\n", gpio, d->irq); + gpio_bitop(bank, opSET, gpio, NPCM_GP_N_EVENC); +} + +/* Enable GPIO interrupt */ +static void npcmgpio_irq_unmask(struct irq_data *d) +{ + struct NPCM_GPIO *bank = + gpiochip_get_data(irq_data_get_irq_chip_data(d)); + unsigned int gpio = d->hwirq; + + /* Enable events */ + dev_dbg(d->chip->parent_device, "irq_unmask: %u.%u\n", gpio, d->irq); + gpio_bitop(bank, opSET, gpio, NPCM_GP_N_EVENS); +} + +/* Initialize GPIO interrupt */ +static unsigned int npcmgpio_irq_startup(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + unsigned int gpio = d->hwirq; + + /* active-high, input, clear interrupt, enable interrupt */ + dev_dbg(d->chip->parent_device, "startup: %u.%u\n", gpio, d->irq); + npcmgpio_direction_output(gc, gpio, 1); + npcmgpio_direction_input(gc, gpio); + npcmgpio_irq_ack(d); + npcmgpio_irq_unmask(d); + return 0; +} + +static struct irq_chip npcmgpio_irqchip = { + .name = "NPCM7XX-GPIO-IRQ", + .irq_ack = npcmgpio_irq_ack, + .irq_unmask = npcmgpio_irq_unmask, + .irq_mask = npcmgpio_irq_mask, + .irq_set_type = npcmgpio_set_irq_type, + .irq_startup = npcmgpio_irq_startup, +}; + + +/* + * PINCTRL code + */ +static const int smb0_pins[] = { 115, 114 }; +static const int smb0b_pins[] = { 195, 194 }; +static const int smb0c_pins[] = { 202, 196 }; +static const int smb0d_pins[] = { 198, 199 }; +static const int smb0den_pins[] = { 197 }; + +static const int smb1_pins[] = { 117, 116 }; +static const int smb1b_pins[] = { 126, 127 }; +static const int smb1c_pins[] = { 124, 125 }; +static const int smb1d_pins[] = { 4, 5 }; + +static const int smb2_pins[] = { 119, 118 }; +static const int smb2b_pins[] = { 122, 123 }; +static const int smb2c_pins[] = { 120, 121 }; +static const int smb2d_pins[] = { 6, 7 }; + +static const int smb3_pins[] = { 30, 31 }; +static const int smb3b_pins[] = { 39, 40 }; +static const int smb3c_pins[] = { 37, 38 }; +static const int smb3d_pins[] = { 59, 60 }; + +static const int smb4_pins[] = { 28, 29 }; +static const int smb4b_pins[] = { 18, 19 }; +static const int smb4c_pins[] = { 20, 21 }; +static const int smb4d_pins[] = { 22, 23 }; +static const int smb4den_pins[] = { 17 }; + +static const int smb5_pins[] = { 26, 27 }; +static const int smb5b_pins[] = { 13, 12 }; +static const int smb5c_pins[] = { 15, 14 }; +static const int smb5d_pins[] = { 94, 93 }; +static const int ga20kbc_pins[] = { 94, 93 }; + +static const int smb6_pins[] = { 172, 171 }; +static const int smb7_pins[] = { 174, 173 }; +static const int smb8_pins[] = { 129, 128 }; +static const int smb9_pins[] = { 131, 130 }; +static const int smb10_pins[] = { 133, 132 }; +static const int smb11_pins[] = { 135, 134 }; +static const int smb12_pins[] = { 221, 220 }; +static const int smb13_pins[] = { 223, 222 }; +static const int smb14_pins[] = { 22, 23 }; +static const int smb15_pins[] = { 20, 21 }; + +static const int fanin0_pins[] = { 64 }; +static const int fanin1_pins[] = { 65 }; +static const int fanin2_pins[] = { 66 }; +static const int fanin3_pins[] = { 67 }; +static const int fanin4_pins[] = { 68 }; +static const int fanin5_pins[] = { 69 }; +static const int fanin6_pins[] = { 70 }; +static const int fanin7_pins[] = { 71 }; +static const int fanin8_pins[] = { 72 }; +static const int fanin9_pins[] = { 73 }; +static const int fanin10_pins[] = { 74 }; +static const int fanin11_pins[] = { 75 }; +static const int fanin12_pins[] = { 76 }; +static const int fanin13_pins[] = { 77 }; +static const int fanin14_pins[] = { 78 }; +static const int fanin15_pins[] = { 79 }; +static const int faninx_pins[] = { 175, 176, 177, 203 }; + +static const int pwm0_pins[] = { 80 }; +static const int pwm1_pins[] = { 81 }; +static const int pwm2_pins[] = { 82 }; +static const int pwm3_pins[] = { 83 }; +static const int pwm4_pins[] = { 144 }; +static const int pwm5_pins[] = { 145 }; +static const int pwm6_pins[] = { 146 }; +static const int pwm7_pins[] = { 147 }; + +static const int uart1_pins[] = { 43, 44, 45, 46, 47, 61, 62, 63 }; +static const int uart2_pins[] = { 48, 49, 50, 51, 52, 53, 54, 55 }; + +static const int rg1_pins[] = { 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + 106, 107 }; +static const int rg1mdio_pins[] = { 108, 109 }; + +static const int rg2_pins[] = { 110, 111, 112, 113, 208, 209, 210, 211, 212, + 213, 214, 215 }; +static const int rg2mdio_pins[] = { 216, 217 }; +static const int ddr_pins[] = { 110, 111, 112, 113, 208, 209, 210, 211, 212, + 213, 214, 215, 216, 217 }; + +static const int iox1_pins[] = { 0, 1, 2, 3 }; +static const int iox2_pins[] = { 4, 5, 6, 7 }; +static const int ioxh_pins[] = { 10, 11, 24, 25 }; + +static const int mmc_pins[] = { 152, 154, 156, 157, 158, 159 }; +static const int mmcwp_pins[] = { 153 }; +static const int mmccd_pins[] = { 155 }; +static const int mmcrst_pins[] = { 155 }; +static const int mmc8_pins[] = { 148, 149, 150, 151 }; + +static const int r1_pins[] = { 178, 179, 180, 181, 182, 193, 201 }; +static const int r1err_pins[] = { 56 }; +static const int r1md_pins[] = { 57, 58 }; + +static const int r2_pins[] = { 84, 85, 86, 87, 88, 89, 200 }; +static const int r2err_pins[] = { 90 }; +static const int r2md_pins[] = { 91, 92 }; + +static const int sd1_pins[] = { 136, 137, 138, 139, 140, 141, 142, 143 }; +static const int sd1pwr_pins[] = { 143 }; + +static const int wdog1_pins[] = { 218 }; +static const int wdog2_pins[] = { 219 }; + +static const int bmcuart0a_pins[] = { 41, 42 }; +static const int bmcuart0b_pins[] = { 48, 49 }; + +static const int bmcuart1_pins[] = { 43, 44, 62, 63 }; + +static const int scipme_pins[] = { 169 }; +static const int sci_pins[] = { 170 }; +static const int serirq_pins[] = { 162 }; + +static const int clkout_pins[] = { 160 }; +static const int clkreq_pins[] = { 231 }; + +static const int jtag2_pins[] = { 43, 44, 45, 46, 47 }; + +static const int gspi_pins[] = { 12, 13, 14, 15 }; + +static const int spix_pins[] = { 224, 225, 226, 227, 229, 230 }; +static const int spixcs1_pins[] = { 228 }; + +static const int pspi1_pins[] = { 175, 176, 177 }; +static const int pspi2_pins[] = { 17, 18, 19 }; + +static const int spi0cs1_pins[] = { 32 }; + +static const int spi3_pins[] = { 183, 184, 185, 186 }; +static const int spi3cs1_pins[] = { 187 }; +static const int spi3quad_pins[] = { 188, 189 }; +static const int spi3cs2_pins[] = { 188 }; +static const int spi3cs3_pins[] = { 189 }; + +static const int ddc_pins[] = { 204, 205, 206, 207 }; + +static const int lpc_pins[] = { 95, 161, 163, 164, 165, 166, 167 }; +static const int lpcclk_pins[] = { 168 }; +static const int espi_pins[] = { 95, 161, 163, 164, 165, 166, 167, 168 }; + +static const int lkgpo0_pins[] = { 16 }; +static const int lkgpo1_pins[] = { 8 }; +static const int lkgpo2_pins[] = { 9 }; + +static const int nprd_smi_pins[] = { 190 }; + +/* + * pin: name, number + * group: name, npins, pins + * function: name, ngroups, groups + */ +struct npcm_group { + const char *name; + const unsigned int *pins; + int npins; +}; + +#define NPCM_GRPS \ + GRP(smb0), \ + GRP(smb0b), \ + GRP(smb0c), \ + GRP(smb0d), \ + GRP(smb0den), \ + GRP(smb1), \ + GRP(smb1b), \ + GRP(smb1c), \ + GRP(smb1d), \ + GRP(smb2), \ + GRP(smb2b), \ + GRP(smb2c), \ + GRP(smb2d), \ + GRP(smb3), \ + GRP(smb3b), \ + GRP(smb3c), \ + GRP(smb3d), \ + GRP(smb4), \ + GRP(smb4b), \ + GRP(smb4c), \ + GRP(smb4d), \ + GRP(smb4den), \ + GRP(smb5), \ + GRP(smb5b), \ + GRP(smb5c), \ + GRP(smb5d), \ + GRP(ga20kbc), \ + GRP(smb6), \ + GRP(smb7), \ + GRP(smb8), \ + GRP(smb9), \ + GRP(smb10), \ + GRP(smb11), \ + GRP(smb12), \ + GRP(smb13), \ + GRP(smb14), \ + GRP(smb15), \ + GRP(fanin0), \ + GRP(fanin1), \ + GRP(fanin2), \ + GRP(fanin3), \ + GRP(fanin4), \ + GRP(fanin5), \ + GRP(fanin6), \ + GRP(fanin7), \ + GRP(fanin8), \ + GRP(fanin9), \ + GRP(fanin10), \ + GRP(fanin11), \ + GRP(fanin12), \ + GRP(fanin13), \ + GRP(fanin14), \ + GRP(fanin15), \ + GRP(faninx), \ + GRP(pwm0), \ + GRP(pwm1), \ + GRP(pwm2), \ + GRP(pwm3), \ + GRP(pwm4), \ + GRP(pwm5), \ + GRP(pwm6), \ + GRP(pwm7), \ + GRP(rg1), \ + GRP(rg1mdio), \ + GRP(rg2), \ + GRP(rg2mdio), \ + GRP(ddr), \ + GRP(uart1), \ + GRP(uart2), \ + GRP(bmcuart0a), \ + GRP(bmcuart0b), \ + GRP(bmcuart1), \ + GRP(iox1), \ + GRP(iox2), \ + GRP(ioxh), \ + GRP(gspi), \ + GRP(mmc), \ + GRP(mmcwp), \ + GRP(mmccd), \ + GRP(mmcrst), \ + GRP(mmc8), \ + GRP(r1), \ + GRP(r1err), \ + GRP(r1md), \ + GRP(r2), \ + GRP(r2err), \ + GRP(r2md), \ + GRP(sd1), \ + GRP(sd1pwr), \ + GRP(wdog1), \ + GRP(wdog2), \ + GRP(scipme), \ + GRP(sci), \ + GRP(serirq), \ + GRP(jtag2), \ + GRP(spix), \ + GRP(spixcs1), \ + GRP(pspi1), \ + GRP(pspi2), \ + GRP(ddc), \ + GRP(clkreq), \ + GRP(clkout), \ + GRP(spi3), \ + GRP(spi3cs1), \ + GRP(spi3quad), \ + GRP(spi3cs2), \ + GRP(spi3cs3), \ + GRP(spi0cs1), \ + GRP(lpc), \ + GRP(lpcclk), \ + GRP(espi), \ + GRP(lkgpo0), \ + GRP(lkgpo1), \ + GRP(lkgpo2), \ + GRP(nprd_smi), \ + \ + +/* Group enums */ +enum { +#define GRP(x) fn_ ## x + NPCM_GRPS + /* add placeholder for none/gpio */ + GRP(none), + GRP(gpio), +#undef GRP +}; + +/* Group names/pins */ +static struct npcm_group npcm_groups[] = { +#define GRP(x) { .name = #x, .pins = x ## _pins, .npins = ARRAY_SIZE(x ## _pins) } + NPCM_GRPS +#undef GRP +}; + +#define NPCM_SFUNC(a) NPCM_FUNC(a, #a) +#define NPCM_FUNC(a, b...) static const char *a ## _grp[] = { b } +#define NPCM_MKFUNC(nm) { .name = #nm, .ngroups = ARRAY_SIZE(nm ## _grp), .groups = nm ## _grp } +struct npcm_func { + const char *name; + const unsigned int ngroups; + const char *const *groups; +}; + +NPCM_SFUNC(smb0); +NPCM_SFUNC(smb0b); +NPCM_SFUNC(smb0c); +NPCM_SFUNC(smb0d); +NPCM_SFUNC(smb0den); +NPCM_SFUNC(smb1); +NPCM_SFUNC(smb1b); +NPCM_SFUNC(smb1c); +NPCM_SFUNC(smb1d); +NPCM_SFUNC(smb2); +NPCM_SFUNC(smb2b); +NPCM_SFUNC(smb2c); +NPCM_SFUNC(smb2d); +NPCM_SFUNC(smb3); +NPCM_SFUNC(smb3b); +NPCM_SFUNC(smb3c); +NPCM_SFUNC(smb3d); +NPCM_SFUNC(smb4); +NPCM_SFUNC(smb4b); +NPCM_SFUNC(smb4c); +NPCM_SFUNC(smb4d); +NPCM_SFUNC(smb4den); +NPCM_SFUNC(smb5); +NPCM_SFUNC(smb5b); +NPCM_SFUNC(smb5c); +NPCM_SFUNC(smb5d); +NPCM_SFUNC(ga20kbc); +NPCM_SFUNC(smb6); +NPCM_SFUNC(smb7); +NPCM_SFUNC(smb8); +NPCM_SFUNC(smb9); +NPCM_SFUNC(smb10); +NPCM_SFUNC(smb11); +NPCM_SFUNC(smb12); +NPCM_SFUNC(smb13); +NPCM_SFUNC(smb14); +NPCM_SFUNC(smb15); +NPCM_SFUNC(fanin0); +NPCM_SFUNC(fanin1); +NPCM_SFUNC(fanin2); +NPCM_SFUNC(fanin3); +NPCM_SFUNC(fanin4); +NPCM_SFUNC(fanin5); +NPCM_SFUNC(fanin6); +NPCM_SFUNC(fanin7); +NPCM_SFUNC(fanin8); +NPCM_SFUNC(fanin9); +NPCM_SFUNC(fanin10); +NPCM_SFUNC(fanin11); +NPCM_SFUNC(fanin12); +NPCM_SFUNC(fanin13); +NPCM_SFUNC(fanin14); +NPCM_SFUNC(fanin15); +NPCM_SFUNC(faninx); +NPCM_SFUNC(pwm0); +NPCM_SFUNC(pwm1); +NPCM_SFUNC(pwm2); +NPCM_SFUNC(pwm3); +NPCM_SFUNC(pwm4); +NPCM_SFUNC(pwm5); +NPCM_SFUNC(pwm6); +NPCM_SFUNC(pwm7); +NPCM_SFUNC(rg1); +NPCM_SFUNC(rg1mdio); +NPCM_SFUNC(rg2); +NPCM_SFUNC(rg2mdio); +NPCM_SFUNC(ddr); +NPCM_SFUNC(uart1); +NPCM_SFUNC(uart2); +NPCM_SFUNC(bmcuart0a); +NPCM_SFUNC(bmcuart0b); +NPCM_SFUNC(bmcuart1); +NPCM_SFUNC(iox1); +NPCM_SFUNC(iox2); +NPCM_SFUNC(ioxh); +NPCM_SFUNC(gspi); +NPCM_SFUNC(mmc); +NPCM_SFUNC(mmcwp); +NPCM_SFUNC(mmccd); +NPCM_SFUNC(mmcrst); +NPCM_SFUNC(mmc8); +NPCM_SFUNC(r1); +NPCM_SFUNC(r1err); +NPCM_SFUNC(r1md); +NPCM_SFUNC(r2); +NPCM_SFUNC(r2err); +NPCM_SFUNC(r2md); +NPCM_SFUNC(sd1); +NPCM_SFUNC(sd1pwr); +NPCM_SFUNC(wdog1); +NPCM_SFUNC(wdog2); +NPCM_SFUNC(scipme); +NPCM_SFUNC(sci); +NPCM_SFUNC(serirq); +NPCM_SFUNC(jtag2); +NPCM_SFUNC(spix); +NPCM_SFUNC(spixcs1); +NPCM_SFUNC(pspi1); +NPCM_SFUNC(pspi2); +NPCM_SFUNC(ddc); +NPCM_SFUNC(clkreq); +NPCM_SFUNC(clkout); +NPCM_SFUNC(spi3); +NPCM_SFUNC(spi3cs1); +NPCM_SFUNC(spi3quad); +NPCM_SFUNC(spi3cs2); +NPCM_SFUNC(spi3cs3); +NPCM_SFUNC(spi0cs1); +NPCM_SFUNC(lpc); +NPCM_SFUNC(lpcclk); +NPCM_SFUNC(espi); +NPCM_SFUNC(lkgpo0); +NPCM_SFUNC(lkgpo1); +NPCM_SFUNC(lkgpo2); +NPCM_SFUNC(nprd_smi); + +/* Function names */ +static struct npcm_func npcm_funcs[] = { + NPCM_MKFUNC(smb0), + NPCM_MKFUNC(smb0b), + NPCM_MKFUNC(smb0c), + NPCM_MKFUNC(smb0d), + NPCM_MKFUNC(smb0den), + NPCM_MKFUNC(smb1), + NPCM_MKFUNC(smb1b), + NPCM_MKFUNC(smb1c), + NPCM_MKFUNC(smb1d), + NPCM_MKFUNC(smb2), + NPCM_MKFUNC(smb2b), + NPCM_MKFUNC(smb2c), + NPCM_MKFUNC(smb2d), + NPCM_MKFUNC(smb3), + NPCM_MKFUNC(smb3b), + NPCM_MKFUNC(smb3c), + NPCM_MKFUNC(smb3d), + NPCM_MKFUNC(smb4), + NPCM_MKFUNC(smb4b), + NPCM_MKFUNC(smb4c), + NPCM_MKFUNC(smb4d), + NPCM_MKFUNC(smb4den), + NPCM_MKFUNC(smb5), + NPCM_MKFUNC(smb5b), + NPCM_MKFUNC(smb5c), + NPCM_MKFUNC(smb5d), + NPCM_MKFUNC(ga20kbc), + NPCM_MKFUNC(smb6), + NPCM_MKFUNC(smb7), + NPCM_MKFUNC(smb8), + NPCM_MKFUNC(smb9), + NPCM_MKFUNC(smb10), + NPCM_MKFUNC(smb11), + NPCM_MKFUNC(smb12), + NPCM_MKFUNC(smb13), + NPCM_MKFUNC(smb14), + NPCM_MKFUNC(smb15), + NPCM_MKFUNC(fanin0), + NPCM_MKFUNC(fanin1), + NPCM_MKFUNC(fanin2), + NPCM_MKFUNC(fanin3), + NPCM_MKFUNC(fanin4), + NPCM_MKFUNC(fanin5), + NPCM_MKFUNC(fanin6), + NPCM_MKFUNC(fanin7), + NPCM_MKFUNC(fanin8), + NPCM_MKFUNC(fanin9), + NPCM_MKFUNC(fanin10), + NPCM_MKFUNC(fanin11), + NPCM_MKFUNC(fanin12), + NPCM_MKFUNC(fanin13), + NPCM_MKFUNC(fanin14), + NPCM_MKFUNC(fanin15), + NPCM_MKFUNC(faninx), + NPCM_MKFUNC(pwm0), + NPCM_MKFUNC(pwm1), + NPCM_MKFUNC(pwm2), + NPCM_MKFUNC(pwm3), + NPCM_MKFUNC(pwm4), + NPCM_MKFUNC(pwm5), + NPCM_MKFUNC(pwm6), + NPCM_MKFUNC(pwm7), + NPCM_MKFUNC(rg1), + NPCM_MKFUNC(rg1mdio), + NPCM_MKFUNC(rg2), + NPCM_MKFUNC(rg2mdio), + NPCM_MKFUNC(ddr), + NPCM_MKFUNC(uart1), + NPCM_MKFUNC(uart2), + NPCM_MKFUNC(bmcuart0a), + NPCM_MKFUNC(bmcuart0b), + NPCM_MKFUNC(bmcuart1), + NPCM_MKFUNC(iox1), + NPCM_MKFUNC(iox2), + NPCM_MKFUNC(ioxh), + NPCM_MKFUNC(gspi), + NPCM_MKFUNC(mmc), + NPCM_MKFUNC(mmcwp), + NPCM_MKFUNC(mmccd), + NPCM_MKFUNC(mmcrst), + NPCM_MKFUNC(mmc8), + NPCM_MKFUNC(r1), + NPCM_MKFUNC(r1err), + NPCM_MKFUNC(r1md), + NPCM_MKFUNC(r2), + NPCM_MKFUNC(r2err), + NPCM_MKFUNC(r2md), + NPCM_MKFUNC(sd1), + NPCM_MKFUNC(sd1pwr), + NPCM_MKFUNC(wdog1), + NPCM_MKFUNC(wdog2), + NPCM_MKFUNC(scipme), + NPCM_MKFUNC(sci), + NPCM_MKFUNC(serirq), + NPCM_MKFUNC(jtag2), + NPCM_MKFUNC(spix), + NPCM_MKFUNC(spixcs1), + NPCM_MKFUNC(pspi1), + NPCM_MKFUNC(pspi2), + NPCM_MKFUNC(ddc), + NPCM_MKFUNC(clkreq), + NPCM_MKFUNC(clkout), + NPCM_MKFUNC(spi3), + NPCM_MKFUNC(spi3cs1), + NPCM_MKFUNC(spi3quad), + NPCM_MKFUNC(spi3cs2), + NPCM_MKFUNC(spi3cs3), + NPCM_MKFUNC(spi0cs1), + NPCM_MKFUNC(lpc), + NPCM_MKFUNC(lpcclk), + NPCM_MKFUNC(espi), + NPCM_MKFUNC(lkgpo0), + NPCM_MKFUNC(lkgpo1), + NPCM_MKFUNC(lkgpo2), + NPCM_MKFUNC(nprd_smi), +}; + +#define PINCFG(a, b, c, d, e, f, g, h, i, j, k) \ + [a] { .fn0 = fn_ ## b, .reg0 = NPCM7XX_GCR_ ## c, .bit0 = d, \ + .fn1 = fn_ ## e, .reg1 = NPCM7XX_GCR_ ## f, .bit1 = g, \ + .fn2 = fn_ ## h, .reg2 = NPCM7XX_GCR_ ## i, .bit2 = j, \ + .flag = k } + +/* Drive strength controlled by NPCM_GP_N_ODSC */ +#define DRIVE_STRENGTH_LO_SHIFT 8 +#define DRIVE_STRENGTH_HI_SHIFT 12 +#define DRIVE_STRENGTH_MASK 0x0000FF00 + +#define DS(lo, hi) (((lo) << DRIVE_STRENGTH_LO_SHIFT) | \ + ((hi) << DRIVE_STRENGTH_HI_SHIFT)) +#define DSLO(x) (((x) >> DRIVE_STRENGTH_LO_SHIFT) & 0xF) +#define DSHI(x) (((x) >> DRIVE_STRENGTH_HI_SHIFT) & 0xF) + +#define GPI 0x1 /* Not GPO */ +#define GPO 0x2 /* Not GPI */ +#define SLEW 0x4 /* Has Slew Control, NPCM_GP_N_OSRC */ +#define SLEWLPC 0x8 /* Has Slew Control, SRCNT.3 */ + +struct npcm_pincfg { + int flag; + int fn0, reg0, bit0; + int fn1, reg1, bit1; + int fn2, reg2, bit2; +}; + +static const struct npcm_pincfg pincfg[] = { + /* PIN FUNCTION 1 FUNCTION 2 FUNCTION 3 FLAGS */ + PINCFG(0, iox1, MFSEL1, 30, none, NONE, 0, none, NONE, 0, 0), + PINCFG(1, iox1, MFSEL1, 30, none, NONE, 0, none, NONE, 0, DS(8, 12)), + PINCFG(2, iox1, MFSEL1, 30, none, NONE, 0, none, NONE, 0, DS(8, 12)), + PINCFG(3, iox1, MFSEL1, 30, none, NONE, 0, none, NONE, 0, 0), + PINCFG(4, iox2, MFSEL3, 14, smb1d, I2CSEGSEL, 7, none, NONE, 0, SLEW), + PINCFG(5, iox2, MFSEL3, 14, smb1d, I2CSEGSEL, 7, none, NONE, 0, SLEW), + PINCFG(6, iox2, MFSEL3, 14, smb2d, I2CSEGSEL, 10, none, NONE, 0, SLEW), + PINCFG(7, iox2, MFSEL3, 14, smb2d, I2CSEGSEL, 10, none, NONE, 0, SLEW), + PINCFG(8, lkgpo1, FLOCKR1, 4, none, NONE, 0, none, NONE, 0, DS(8, 12)), + PINCFG(9, lkgpo2, FLOCKR1, 8, none, NONE, 0, none, NONE, 0, DS(8, 12)), + PINCFG(10, ioxh, MFSEL3, 18, none, NONE, 0, none, NONE, 0, DS(8, 12)), + PINCFG(11, ioxh, MFSEL3, 18, none, NONE, 0, none, NONE, 0, DS(8, 12)), + PINCFG(12, gspi, MFSEL1, 24, smb5b, I2CSEGSEL, 19, none, NONE, 0, SLEW), + PINCFG(13, gspi, MFSEL1, 24, smb5b, I2CSEGSEL, 19, none, NONE, 0, SLEW), + PINCFG(14, gspi, MFSEL1, 24, smb5c, I2CSEGSEL, 20, none, NONE, 0, SLEW), + PINCFG(15, gspi, MFSEL1, 24, smb5c, I2CSEGSEL, 20, none, NONE, 0, SLEW), + PINCFG(16, lkgpo0, FLOCKR1, 0, none, NONE, 0, none, NONE, 0, DS(8, 12)), + PINCFG(17, pspi2, MFSEL3, 13, smb4den, I2CSEGSEL, 23, none, NONE, 0, DS(8, 12)), + PINCFG(18, pspi2, MFSEL3, 13, smb4b, I2CSEGSEL, 14, none, NONE, 0, DS(8, 12)), + PINCFG(19, pspi2, MFSEL3, 13, smb4b, I2CSEGSEL, 14, none, NONE, 0, DS(8, 12)), + PINCFG(20, smb4c, I2CSEGSEL, 15, smb15, MFSEL3, 8, none, NONE, 0, 0), + PINCFG(21, smb4c, I2CSEGSEL, 15, smb15, MFSEL3, 8, none, NONE, 0, 0), + PINCFG(22, smb4d, I2CSEGSEL, 16, smb14, MFSEL3, 7, none, NONE, 0, 0), + PINCFG(23, smb4d, I2CSEGSEL, 16, smb14, MFSEL3, 7, none, NONE, 0, 0), + PINCFG(24, ioxh, MFSEL3, 18, none, NONE, 0, none, NONE, 0, DS(8, 12)), + PINCFG(25, ioxh, MFSEL3, 18, none, NONE, 0, none, NONE, 0, DS(8, 12)), + PINCFG(26, smb5, MFSEL1, 2, none, NONE, 0, none, NONE, 0, 0), + PINCFG(27, smb5, MFSEL1, 2, none, NONE, 0, none, NONE, 0, 0), + PINCFG(28, smb4, MFSEL1, 1, none, NONE, 0, none, NONE, 0, 0), + PINCFG(29, smb4, MFSEL1, 1, none, NONE, 0, none, NONE, 0, 0), + PINCFG(30, smb3, MFSEL1, 0, none, NONE, 0, none, NONE, 0, 0), + PINCFG(31, smb3, MFSEL1, 0, none, NONE, 0, none, NONE, 0, 0), + + PINCFG(32, spi0cs1, MFSEL1, 3, none, NONE, 0, none, NONE, 0, 0), + PINCFG(33, none, NONE, 0, none, NONE, 0, none, NONE, 0, SLEW), + PINCFG(34, none, NONE, 0, none, NONE, 0, none, NONE, 0, SLEW), + PINCFG(37, smb3c, I2CSEGSEL, 12, none, NONE, 0, none, NONE, 0, SLEW), + PINCFG(38, smb3c, I2CSEGSEL, 12, none, NONE, 0, none, NONE, 0, SLEW), + PINCFG(39, smb3b, I2CSEGSEL, 11, none, NONE, 0, none, NONE, 0, SLEW), + PINCFG(40, smb3b, I2CSEGSEL, 11, none, NONE, 0, none, NONE, 0, SLEW), + PINCFG(41, bmcuart0a, MFSEL1, 9, none, NONE, 0, none, NONE, 0, 0), + PINCFG(42, bmcuart0a, MFSEL1, 9, none, NONE, 0, none, NONE, 0, DS(2, 4) | GPO), + PINCFG(43, uart1, MFSEL1, 10, jtag2, MFSEL4, 0, bmcuart1, MFSEL3, 24, 0), + PINCFG(44, uart1, MFSEL1, 10, jtag2, MFSEL4, 0, bmcuart1, MFSEL3, 24, 0), + PINCFG(45, uart1, MFSEL1, 10, jtag2, MFSEL4, 0, none, NONE, 0, 0), + PINCFG(46, uart1, MFSEL1, 10, jtag2, MFSEL4, 0, none, NONE, 0, DS(2, 8)), + PINCFG(47, uart1, MFSEL1, 10, jtag2, MFSEL4, 0, none, NONE, 0, DS(2, 8)), + PINCFG(48, uart2, MFSEL1, 11, bmcuart0b, MFSEL4, 1, none, NONE, 0, GPO), + PINCFG(49, uart2, MFSEL1, 11, bmcuart0b, MFSEL4, 1, none, NONE, 0, 0), + PINCFG(50, uart2, MFSEL1, 11, none, NONE, 0, none, NONE, 0, 0), + PINCFG(51, uart2, MFSEL1, 11, none, NONE, 0, none, NONE, 0, GPO), + PINCFG(52, uart2, MFSEL1, 11, none, NONE, 0, none, NONE, 0, 0), + PINCFG(53, uart2, MFSEL1, 11, none, NONE, 0, none, NONE, 0, GPO), + PINCFG(54, uart2, MFSEL1, 11, none, NONE, 0, none, NONE, 0, 0), + PINCFG(55, uart2, MFSEL1, 11, none, NONE, 0, none, NONE, 0, 0), + PINCFG(56, r1err, MFSEL1, 12, none, NONE, 0, none, NONE, 0, 0), + PINCFG(57, r1md, MFSEL1, 13, none, NONE, 0, none, NONE, 0, DS(2, 4)), + PINCFG(58, r1md, MFSEL1, 13, none, NONE, 0, none, NONE, 0, DS(2, 4)), + PINCFG(59, smb3d, I2CSEGSEL, 13, none, NONE, 0, none, NONE, 0, 0), + PINCFG(60, smb3d, I2CSEGSEL, 13, none, NONE, 0, none, NONE, 0, 0), + PINCFG(61, uart1, MFSEL1, 10, none, NONE, 0, none, NONE, 0, GPO), + PINCFG(62, uart1, MFSEL1, 10, bmcuart1, MFSEL3, 24, none, NONE, 0, GPO), + PINCFG(63, uart1, MFSEL1, 10, bmcuart1, MFSEL3, 24, none, NONE, 0, GPO), + + PINCFG(64, fanin0, MFSEL2, 0, none, NONE, 0, none, NONE, 0, 0), + PINCFG(65, fanin1, MFSEL2, 1, none, NONE, 0, none, NONE, 0, 0), + PINCFG(66, fanin2, MFSEL2, 2, none, NONE, 0, none, NONE, 0, 0), + PINCFG(67, fanin3, MFSEL2, 3, none, NONE, 0, none, NONE, 0, 0), + PINCFG(68, fanin4, MFSEL2, 4, none, NONE, 0, none, NONE, 0, 0), + PINCFG(69, fanin5, MFSEL2, 5, none, NONE, 0, none, NONE, 0, 0), + PINCFG(70, fanin6, MFSEL2, 6, none, NONE, 0, none, NONE, 0, 0), + PINCFG(71, fanin7, MFSEL2, 7, none, NONE, 0, none, NONE, 0, 0), + PINCFG(72, fanin8, MFSEL2, 8, none, NONE, 0, none, NONE, 0, 0), + PINCFG(73, fanin9, MFSEL2, 9, none, NONE, 0, none, NONE, 0, 0), + PINCFG(74, fanin10, MFSEL2, 10, none, NONE, 0, none, NONE, 0, 0), + PINCFG(75, fanin11, MFSEL2, 11, none, NONE, 0, none, NONE, 0, 0), + PINCFG(76, fanin12, MFSEL2, 12, none, NONE, 0, none, NONE, 0, 0), + PINCFG(77, fanin13, MFSEL2, 13, none, NONE, 0, none, NONE, 0, 0), + PINCFG(78, fanin14, MFSEL2, 14, none, NONE, 0, none, NONE, 0, 0), + PINCFG(79, fanin15, MFSEL2, 15, none, NONE, 0, none, NONE, 0, 0), + PINCFG(80, pwm0, MFSEL2, 16, none, NONE, 0, none, NONE, 0, DS(4, 8)), + PINCFG(81, pwm1, MFSEL2, 17, none, NONE, 0, none, NONE, 0, DS(4, 8)), + PINCFG(82, pwm2, MFSEL2, 18, none, NONE, 0, none, NONE, 0, DS(4, 8)), + PINCFG(83, pwm3, MFSEL2, 19, none, NONE, 0, none, NONE, 0, DS(4, 8)), + PINCFG(84, r2, MFSEL1, 14, none, NONE, 0, none, NONE, 0, DS(8, 12) | SLEW), + PINCFG(85, r2, MFSEL1, 14, none, NONE, 0, none, NONE, 0, DS(8, 12) | SLEW), + PINCFG(86, r2, MFSEL1, 14, none, NONE, 0, none, NONE, 0, DS(8, 12) | SLEW), + PINCFG(87, r2, MFSEL1, 14, none, NONE, 0, none, NONE, 0, 0), + PINCFG(88, r2, MFSEL1, 14, none, NONE, 0, none, NONE, 0, 0), + PINCFG(89, r2, MFSEL1, 14, none, NONE, 0, none, NONE, 0, 0), + PINCFG(90, r2err, MFSEL1, 15, none, NONE, 0, none, NONE, 0, 0), + PINCFG(91, r2md, MFSEL1, 16, none, NONE, 0, none, NONE, 0, DS(2, 4)), + PINCFG(92, r2md, MFSEL1, 16, none, NONE, 0, none, NONE, 0, DS(2, 4)), + PINCFG(93, ga20kbc, MFSEL1, 17, smb5d, I2CSEGSEL, 21, none, NONE, 0, 0), + PINCFG(94, ga20kbc, MFSEL1, 17, smb5d, I2CSEGSEL, 21, none, NONE, 0, 0), + PINCFG(95, lpc, NONE, 0, espi, MFSEL4, 8, gpio, MFSEL1, 26, 0), + + PINCFG(96, rg1, MFSEL4, 22, none, NONE, 0, none, NONE, 0, 0), + PINCFG(97, rg1, MFSEL4, 22, none, NONE, 0, none, NONE, 0, 0), + PINCFG(98, rg1, MFSEL4, 22, none, NONE, 0, none, NONE, 0, 0), + PINCFG(99, rg1, MFSEL4, 22, none, NONE, 0, none, NONE, 0, 0), + PINCFG(100, rg1, MFSEL4, 22, none, NONE, 0, none, NONE, 0, 0), + PINCFG(101, rg1, MFSEL4, 22, none, NONE, 0, none, NONE, 0, 0), + PINCFG(102, rg1, MFSEL4, 22, none, NONE, 0, none, NONE, 0, 0), + PINCFG(103, rg1, MFSEL4, 22, none, NONE, 0, none, NONE, 0, 0), + PINCFG(104, rg1, MFSEL4, 22, none, NONE, 0, none, NONE, 0, 0), + PINCFG(105, rg1, MFSEL4, 22, none, NONE, 0, none, NONE, 0, 0), + PINCFG(106, rg1, MFSEL4, 22, none, NONE, 0, none, NONE, 0, 0), + PINCFG(107, rg1, MFSEL4, 22, none, NONE, 0, none, NONE, 0, 0), + PINCFG(108, rg1mdio, MFSEL4, 21, none, NONE, 0, none, NONE, 0, 0), + PINCFG(109, rg1mdio, MFSEL4, 21, none, NONE, 0, none, NONE, 0, 0), + PINCFG(110, rg2, MFSEL4, 24, ddr, MFSEL3, 26, none, NONE, 0, 0), + PINCFG(111, rg2, MFSEL4, 24, ddr, MFSEL3, 26, none, NONE, 0, 0), + PINCFG(112, rg2, MFSEL4, 24, ddr, MFSEL3, 26, none, NONE, 0, 0), + PINCFG(113, rg2, MFSEL4, 24, ddr, MFSEL3, 26, none, NONE, 0, 0), + PINCFG(114, smb0, MFSEL1, 6, none, NONE, 0, none, NONE, 0, 0), + PINCFG(115, smb0, MFSEL1, 6, none, NONE, 0, none, NONE, 0, 0), + PINCFG(116, smb1, MFSEL1, 7, none, NONE, 0, none, NONE, 0, 0), + PINCFG(117, smb1, MFSEL1, 7, none, NONE, 0, none, NONE, 0, 0), + PINCFG(118, smb2, MFSEL1, 8, none, NONE, 0, none, NONE, 0, 0), + PINCFG(119, smb2, MFSEL1, 8, none, NONE, 0, none, NONE, 0, 0), + PINCFG(120, smb2c, I2CSEGSEL, 9, none, NONE, 0, none, NONE, 0, SLEW), + PINCFG(121, smb2c, I2CSEGSEL, 9, none, NONE, 0, none, NONE, 0, SLEW), + PINCFG(122, smb2b, I2CSEGSEL, 8, none, NONE, 0, none, NONE, 0, SLEW), + PINCFG(123, smb2b, I2CSEGSEL, 8, none, NONE, 0, none, NONE, 0, SLEW), + PINCFG(124, smb1c, I2CSEGSEL, 6, none, NONE, 0, none, NONE, 0, SLEW), + PINCFG(125, smb1c, I2CSEGSEL, 6, none, NONE, 0, none, NONE, 0, SLEW), + PINCFG(126, smb1b, I2CSEGSEL, 5, none, NONE, 0, none, NONE, 0, SLEW), + PINCFG(127, smb1b, I2CSEGSEL, 5, none, NONE, 0, none, NONE, 0, SLEW), + + PINCFG(128, smb8, MFSEL4, 11, none, NONE, 0, none, NONE, 0, 0), + PINCFG(129, smb8, MFSEL4, 11, none, NONE, 0, none, NONE, 0, 0), + PINCFG(130, smb9, MFSEL4, 12, none, NONE, 0, none, NONE, 0, 0), + PINCFG(131, smb9, MFSEL4, 12, none, NONE, 0, none, NONE, 0, 0), + PINCFG(132, smb10, MFSEL4, 13, none, NONE, 0, none, NONE, 0, 0), + PINCFG(133, smb10, MFSEL4, 13, none, NONE, 0, none, NONE, 0, 0), + PINCFG(134, smb11, MFSEL4, 14, none, NONE, 0, none, NONE, 0, 0), + PINCFG(135, smb11, MFSEL4, 14, none, NONE, 0, none, NONE, 0, 0), + PINCFG(136, sd1, MFSEL3, 12, none, NONE, 0, none, NONE, 0, DS(8, 12) | SLEW), + PINCFG(137, sd1, MFSEL3, 12, none, NONE, 0, none, NONE, 0, DS(8, 12) | SLEW), + PINCFG(138, sd1, MFSEL3, 12, none, NONE, 0, none, NONE, 0, DS(8, 12) | SLEW), + PINCFG(139, sd1, MFSEL3, 12, none, NONE, 0, none, NONE, 0, DS(8, 12) | SLEW), + PINCFG(140, sd1, MFSEL3, 12, none, NONE, 0, none, NONE, 0, DS(8, 12) | SLEW), + PINCFG(141, sd1, MFSEL3, 12, none, NONE, 0, none, NONE, 0, 0), + PINCFG(142, sd1, MFSEL3, 12, none, NONE, 0, none, NONE, 0, DS(8, 12) | SLEW), + PINCFG(143, sd1, MFSEL3, 12, sd1pwr, MFSEL4, 5, none, NONE, 0, 0), + PINCFG(144, pwm4, MFSEL2, 20, none, NONE, 0, none, NONE, 0, DS(4, 8)), + PINCFG(145, pwm5, MFSEL2, 21, none, NONE, 0, none, NONE, 0, DS(4, 8)), + PINCFG(146, pwm6, MFSEL2, 22, none, NONE, 0, none, NONE, 0, DS(4, 8)), + PINCFG(147, pwm7, MFSEL2, 23, none, NONE, 0, none, NONE, 0, DS(4, 8)), + PINCFG(148, mmc8, MFSEL3, 11, none, NONE, 0, none, NONE, 0, DS(8, 12) | SLEW), + PINCFG(149, mmc8, MFSEL3, 11, none, NONE, 0, none, NONE, 0, DS(8, 12) | SLEW), + PINCFG(150, mmc8, MFSEL3, 11, none, NONE, 0, none, NONE, 0, DS(8, 12) | SLEW), + PINCFG(151, mmc8, MFSEL3, 11, none, NONE, 0, none, NONE, 0, DS(8, 12) | SLEW), + PINCFG(152, mmc, MFSEL3, 10, none, NONE, 0, none, NONE, 0, DS(8, 12) | SLEW), + PINCFG(153, mmcwp, FLOCKR1, 24, none, NONE, 0, none, NONE, 0, 0), /* Z1/A1 */ + PINCFG(154, mmc, MFSEL3, 10, none, NONE, 0, none, NONE, 0, DS(8, 12) | SLEW), + PINCFG(155, mmccd, MFSEL3, 25, mmcrst, MFSEL4, 6, none, NONE, 0, 0), /* Z1/A1 */ + PINCFG(156, mmc, MFSEL3, 10, none, NONE, 0, none, NONE, 0, DS(8, 12) | SLEW), + PINCFG(157, mmc, MFSEL3, 10, none, NONE, 0, none, NONE, 0, DS(8, 12) | SLEW), + PINCFG(158, mmc, MFSEL3, 10, none, NONE, 0, none, NONE, 0, DS(8, 12) | SLEW), + PINCFG(159, mmc, MFSEL3, 10, none, NONE, 0, none, NONE, 0, DS(8, 12) | SLEW), + + PINCFG(160, clkout, MFSEL1, 21, none, NONE, 0, none, NONE, 0, DS(8, 12) | SLEW), + PINCFG(161, lpc, NONE, 0, espi, MFSEL4, 8, gpio, MFSEL1, 26, DS(8, 12)), + PINCFG(162, serirq, NONE, 0, gpio, MFSEL1, 31, none, NONE, 0, DS(8, 12)), + PINCFG(163, lpc, NONE, 0, espi, MFSEL4, 8, gpio, MFSEL1, 26, 0), + PINCFG(164, lpc, NONE, 0, espi, MFSEL4, 8, gpio, MFSEL1, 26, SLEWLPC), + PINCFG(165, lpc, NONE, 0, espi, MFSEL4, 8, gpio, MFSEL1, 26, SLEWLPC), + PINCFG(166, lpc, NONE, 0, espi, MFSEL4, 8, gpio, MFSEL1, 26, SLEWLPC), + PINCFG(167, lpc, NONE, 0, espi, MFSEL4, 8, gpio, MFSEL1, 26, SLEWLPC), + PINCFG(168, lpcclk, NONE, 0, espi, MFSEL4, 8, gpio, MFSEL3, 16, 0), + PINCFG(169, scipme, MFSEL3, 0, none, NONE, 0, none, NONE, 0, 0), + PINCFG(170, sci, MFSEL1, 22, none, NONE, 0, none, NONE, 0, 0), + PINCFG(171, smb6, MFSEL3, 1, none, NONE, 0, none, NONE, 0, 0), + PINCFG(172, smb6, MFSEL3, 1, none, NONE, 0, none, NONE, 0, 0), + PINCFG(173, smb7, MFSEL3, 2, none, NONE, 0, none, NONE, 0, 0), + PINCFG(174, smb7, MFSEL3, 2, none, NONE, 0, none, NONE, 0, 0), + PINCFG(175, pspi1, MFSEL3, 4, faninx, MFSEL3, 3, none, NONE, 0, DS(8, 12)), + PINCFG(176, pspi1, MFSEL3, 4, faninx, MFSEL3, 3, none, NONE, 0, DS(8, 12)), + PINCFG(177, pspi1, MFSEL3, 4, faninx, MFSEL3, 3, none, NONE, 0, DS(8, 12)), + PINCFG(178, r1, MFSEL3, 9, none, NONE, 0, none, NONE, 0, DS(8, 12) | SLEW), + PINCFG(179, r1, MFSEL3, 9, none, NONE, 0, none, NONE, 0, DS(8, 12) | SLEW), + PINCFG(180, r1, MFSEL3, 9, none, NONE, 0, none, NONE, 0, DS(8, 12) | SLEW), + PINCFG(181, r1, MFSEL3, 9, none, NONE, 0, none, NONE, 0, 0), + PINCFG(182, r1, MFSEL3, 9, none, NONE, 0, none, NONE, 0, 0), + PINCFG(183, spi3, MFSEL4, 16, none, NONE, 0, none, NONE, 0, DS(8, 12) | SLEW), + PINCFG(184, spi3, MFSEL4, 16, none, NONE, 0, none, NONE, 0, DS(8, 12) | SLEW | GPO), + PINCFG(185, spi3, MFSEL4, 16, none, NONE, 0, none, NONE, 0, DS(8, 12) | SLEW | GPO), + PINCFG(186, spi3, MFSEL4, 16, none, NONE, 0, none, NONE, 0, DS(8, 12)), + PINCFG(187, spi3cs1, MFSEL4, 17, none, NONE, 0, none, NONE, 0, DS(8, 12)), + PINCFG(188, spi3quad, MFSEL4, 20, spi3cs2, MFSEL4, 18, none, NONE, 0, DS(8, 12) | SLEW), + PINCFG(189, spi3quad, MFSEL4, 20, spi3cs3, MFSEL4, 19, none, NONE, 0, DS(8, 12) | SLEW), + PINCFG(190, gpio, FLOCKR1, 20, nprd_smi, NONE, 0, none, NONE, 0, DS(2, 4)), + PINCFG(191, none, NONE, 0, none, NONE, 0, none, NONE, 0, DS(8, 12)), /* XX */ + + PINCFG(192, none, NONE, 0, none, NONE, 0, none, NONE, 0, DS(8, 12)), /* XX */ + PINCFG(193, r1, MFSEL3, 9, none, NONE, 0, none, NONE, 0, 0), + PINCFG(194, smb0b, I2CSEGSEL, 0, none, NONE, 0, none, NONE, 0, 0), + PINCFG(195, smb0b, I2CSEGSEL, 0, none, NONE, 0, none, NONE, 0, 0), + PINCFG(196, smb0c, I2CSEGSEL, 1, none, NONE, 0, none, NONE, 0, 0), + PINCFG(197, smb0den, I2CSEGSEL, 22, none, NONE, 0, none, NONE, 0, SLEW), + PINCFG(198, smb0d, I2CSEGSEL, 2, none, NONE, 0, none, NONE, 0, 0), + PINCFG(199, smb0d, I2CSEGSEL, 2, none, NONE, 0, none, NONE, 0, 0), + PINCFG(200, r2, MFSEL1, 14, none, NONE, 0, none, NONE, 0, 0), + PINCFG(201, r1, MFSEL3, 9, none, NONE, 0, none, NONE, 0, 0), + PINCFG(202, smb0c, I2CSEGSEL, 1, none, NONE, 0, none, NONE, 0, 0), + PINCFG(203, faninx, MFSEL3, 3, none, NONE, 0, none, NONE, 0, DS(8, 12)), + PINCFG(204, ddc, NONE, 0, gpio, MFSEL3, 22, none, NONE, 0, SLEW), + PINCFG(205, ddc, NONE, 0, gpio, MFSEL3, 22, none, NONE, 0, SLEW), + PINCFG(206, ddc, NONE, 0, gpio, MFSEL3, 22, none, NONE, 0, DS(4, 8)), + PINCFG(207, ddc, NONE, 0, gpio, MFSEL3, 22, none, NONE, 0, DS(4, 8)), + PINCFG(208, rg2, MFSEL4, 24, ddr, MFSEL3, 26, none, NONE, 0, 0), + PINCFG(209, rg2, MFSEL4, 24, ddr, MFSEL3, 26, none, NONE, 0, 0), + PINCFG(210, rg2, MFSEL4, 24, ddr, MFSEL3, 26, none, NONE, 0, 0), + PINCFG(211, rg2, MFSEL4, 24, ddr, MFSEL3, 26, none, NONE, 0, 0), + PINCFG(212, rg2, MFSEL4, 24, ddr, MFSEL3, 26, none, NONE, 0, 0), + PINCFG(213, rg2, MFSEL4, 24, ddr, MFSEL3, 26, none, NONE, 0, 0), + PINCFG(214, rg2, MFSEL4, 24, ddr, MFSEL3, 26, none, NONE, 0, 0), + PINCFG(215, rg2, MFSEL4, 24, ddr, MFSEL3, 26, none, NONE, 0, 0), + PINCFG(216, rg2mdio, MFSEL4, 23, ddr, MFSEL3, 26, none, NONE, 0, 0), + PINCFG(217, rg2mdio, MFSEL4, 23, ddr, MFSEL3, 26, none, NONE, 0, 0), + PINCFG(218, wdog1, MFSEL3, 19, none, NONE, 0, none, NONE, 0, 0), + PINCFG(219, wdog2, MFSEL3, 20, none, NONE, 0, none, NONE, 0, DS(4, 8)), + PINCFG(220, smb12, MFSEL3, 5, none, NONE, 0, none, NONE, 0, 0), + PINCFG(221, smb12, MFSEL3, 5, none, NONE, 0, none, NONE, 0, 0), + PINCFG(222, smb13, MFSEL3, 6, none, NONE, 0, none, NONE, 0, 0), + PINCFG(223, smb13, MFSEL3, 6, none, NONE, 0, none, NONE, 0, 0), + + PINCFG(224, spix, MFSEL4, 27, none, NONE, 0, none, NONE, 0, SLEW), + PINCFG(225, spix, MFSEL4, 27, none, NONE, 0, none, NONE, 0, DS(8, 12) | SLEW | GPO), + PINCFG(226, spix, MFSEL4, 27, none, NONE, 0, none, NONE, 0, DS(8, 12) | SLEW | GPO), + PINCFG(227, spix, MFSEL4, 27, none, NONE, 0, none, NONE, 0, DS(8, 12) | SLEW), + PINCFG(228, spixcs1, MFSEL4, 28, none, NONE, 0, none, NONE, 0, DS(8, 12) | SLEW), + PINCFG(229, spix, MFSEL4, 27, none, NONE, 0, none, NONE, 0, DS(8, 12) | SLEW), + PINCFG(230, spix, MFSEL4, 27, none, NONE, 0, none, NONE, 0, DS(8, 12) | SLEW), + PINCFG(231, clkreq, MFSEL4, 9, none, NONE, 0, none, NONE, 0, DS(8, 12)), + PINCFG(253, none, NONE, 0, none, NONE, 0, none, NONE, 0, GPI), /* SDHC1 power */ + PINCFG(254, none, NONE, 0, none, NONE, 0, none, NONE, 0, GPI), /* SDHC2 power */ + PINCFG(255, none, NONE, 0, none, NONE, 0, none, NONE, 0, GPI), /* DACOSEL */ +}; + +/* number, name, drv_data */ +static const struct pinctrl_pin_desc npcm_pins[] = { + PINCTRL_PIN(0, "GPIO0/IOX1DI"), + PINCTRL_PIN(1, "GPIO1/IOX1LD"), + PINCTRL_PIN(2, "GPIO2/IOX1CK"), + PINCTRL_PIN(3, "GPIO3/IOX1D0"), + PINCTRL_PIN(4, "GPIO4/IOX2DI/SMB1DSDA"), + PINCTRL_PIN(5, "GPIO5/IOX2LD/SMB1DSCL"), + PINCTRL_PIN(6, "GPIO6/IOX2CK/SMB2DSDA"), + PINCTRL_PIN(7, "GPIO7/IOX2D0/SMB2DSCL"), + PINCTRL_PIN(8, "GPIO8/LKGPO1"), + PINCTRL_PIN(9, "GPIO9/LKGPO2"), + PINCTRL_PIN(10, "GPIO10/IOXHLD"), + PINCTRL_PIN(11, "GPIO11/IOXHCK"), + PINCTRL_PIN(12, "GPIO12/GSPICK/SMB5BSCL"), + PINCTRL_PIN(13, "GPIO13/GSPIDO/SMB5BSDA"), + PINCTRL_PIN(14, "GPIO14/GSPIDI/SMB5CSCL"), + PINCTRL_PIN(15, "GPIO15/GSPICS/SMB5CSDA"), + PINCTRL_PIN(16, "GPIO16/LKGPO0"), + PINCTRL_PIN(17, "GPIO17/PSPI2DI/SMB4DEN"), + PINCTRL_PIN(18, "GPIO18/PSPI2D0/SMB4BSDA"), + PINCTRL_PIN(19, "GPIO19/PSPI2CK/SMB4BSCL"), + PINCTRL_PIN(20, "GPIO20/SMB4CSDA/SMB15SDA"), + PINCTRL_PIN(21, "GPIO21/SMB4CSCL/SMB15SCL"), + PINCTRL_PIN(22, "GPIO22/SMB4DSDA/SMB14SDA"), + PINCTRL_PIN(23, "GPIO23/SMB4DSCL/SMB14SCL"), + PINCTRL_PIN(24, "GPIO24/IOXHDO"), + PINCTRL_PIN(25, "GPIO25/IOXHDI"), + PINCTRL_PIN(26, "GPIO26/SMB5SDA"), + PINCTRL_PIN(27, "GPIO27/SMB5SCL"), + PINCTRL_PIN(28, "GPIO28/SMB4SDA"), + PINCTRL_PIN(29, "GPIO29/SMB4SCL"), + PINCTRL_PIN(30, "GPIO30/SMB3SDA"), + PINCTRL_PIN(31, "GPIO31/SMB3SCL"), + + PINCTRL_PIN(32, "GPIO32/nSPI0CS1"), + PINCTRL_PIN(33, "SPI0D2"), + PINCTRL_PIN(34, "SPI0D3"), + PINCTRL_PIN(37, "GPIO37/SMB3CSDA"), + PINCTRL_PIN(38, "GPIO38/SMB3CSCL"), + PINCTRL_PIN(39, "GPIO39/SMB3BSDA"), + PINCTRL_PIN(40, "GPIO40/SMB3BSCL"), + PINCTRL_PIN(41, "GPIO41/BSPRXD"), + PINCTRL_PIN(42, "GPO42/BSPTXD/STRAP11"), + PINCTRL_PIN(43, "GPIO43/RXD1/JTMS2/BU1RXD"), + PINCTRL_PIN(44, "GPIO44/nCTS1/JTDI2/BU1CTS"), + PINCTRL_PIN(45, "GPIO45/nDCD1/JTDO2"), + PINCTRL_PIN(46, "GPIO46/nDSR1/JTCK2"), + PINCTRL_PIN(47, "GPIO47/nRI1/JCP_RDY2"), + PINCTRL_PIN(48, "GPIO48/TXD2/BSPTXD"), + PINCTRL_PIN(49, "GPIO49/RXD2/BSPRXD"), + PINCTRL_PIN(50, "GPIO50/nCTS2"), + PINCTRL_PIN(51, "GPO51/nRTS2/STRAP2"), + PINCTRL_PIN(52, "GPIO52/nDCD2"), + PINCTRL_PIN(53, "GPIO53/nDTR2_BOUT2/STRAP1"), + PINCTRL_PIN(54, "GPIO54/nDSR2"), + PINCTRL_PIN(55, "GPIO55/nRI2"), + PINCTRL_PIN(56, "GPIO56/R1RXERR"), + PINCTRL_PIN(57, "GPIO57/R1MDC"), + PINCTRL_PIN(58, "GPIO58/R1MDIO"), + PINCTRL_PIN(59, "GPIO59/SMB3DSDA"), + PINCTRL_PIN(60, "GPIO60/SMB3DSCL"), + PINCTRL_PIN(61, "GPO61/nDTR1_BOUT1/STRAP6"), + PINCTRL_PIN(62, "GPO62/nRTST1/STRAP5"), + PINCTRL_PIN(63, "GPO63/TXD1/STRAP4"), + + PINCTRL_PIN(64, "GPIO64/FANIN0"), + PINCTRL_PIN(65, "GPIO65/FANIN1"), + PINCTRL_PIN(66, "GPIO66/FANIN2"), + PINCTRL_PIN(67, "GPIO67/FANIN3"), + PINCTRL_PIN(68, "GPIO68/FANIN4"), + PINCTRL_PIN(69, "GPIO69/FANIN5"), + PINCTRL_PIN(70, "GPIO70/FANIN6"), + PINCTRL_PIN(71, "GPIO71/FANIN7"), + PINCTRL_PIN(72, "GPIO72/FANIN8"), + PINCTRL_PIN(73, "GPIO73/FANIN9"), + PINCTRL_PIN(74, "GPIO74/FANIN10"), + PINCTRL_PIN(75, "GPIO75/FANIN11"), + PINCTRL_PIN(76, "GPIO76/FANIN12"), + PINCTRL_PIN(77, "GPIO77/FANIN13"), + PINCTRL_PIN(78, "GPIO78/FANIN14"), + PINCTRL_PIN(79, "GPIO79/FANIN15"), + PINCTRL_PIN(80, "GPIO80/PWM0"), + PINCTRL_PIN(81, "GPIO81/PWM1"), + PINCTRL_PIN(82, "GPIO82/PWM2"), + PINCTRL_PIN(83, "GPIO83/PWM3"), + PINCTRL_PIN(84, "GPIO84/R2TXD0"), + PINCTRL_PIN(85, "GPIO85/R2TXD1"), + PINCTRL_PIN(86, "GPIO86/R2TXEN"), + PINCTRL_PIN(87, "GPIO87/R2RXD0"), + PINCTRL_PIN(88, "GPIO88/R2RXD1"), + PINCTRL_PIN(89, "GPIO89/R2CRSDV"), + PINCTRL_PIN(90, "GPIO90/R2RXERR"), + PINCTRL_PIN(91, "GPIO91/R2MDC"), + PINCTRL_PIN(92, "GPIO92/R2MDIO"), + PINCTRL_PIN(93, "GPIO93/GA20/SMB5DSCL"), + PINCTRL_PIN(94, "GPIO94/nKBRST/SMB5DSDA"), + PINCTRL_PIN(95, "GPIO95/nLRESET/nESPIRST"), + + PINCTRL_PIN(96, "GPIO96/RG1TXD0"), + PINCTRL_PIN(97, "GPIO97/RG1TXD1"), + PINCTRL_PIN(98, "GPIO98/RG1TXD2"), + PINCTRL_PIN(99, "GPIO99/RG1TXD3"), + PINCTRL_PIN(100, "GPIO100/RG1TXC"), + PINCTRL_PIN(101, "GPIO101/RG1TXCTL"), + PINCTRL_PIN(102, "GPIO102/RG1RXD0"), + PINCTRL_PIN(103, "GPIO103/RG1RXD1"), + PINCTRL_PIN(104, "GPIO104/RG1RXD2"), + PINCTRL_PIN(105, "GPIO105/RG1RXD3"), + PINCTRL_PIN(106, "GPIO106/RG1RXC"), + PINCTRL_PIN(107, "GPIO107/RG1RXCTL"), + PINCTRL_PIN(108, "GPIO108/RG1MDC"), + PINCTRL_PIN(109, "GPIO109/RG1MDIO"), + PINCTRL_PIN(110, "GPIO110/RG2TXD0/DDRV0"), + PINCTRL_PIN(111, "GPIO111/RG2TXD1/DDRV1"), + PINCTRL_PIN(112, "GPIO112/RG2TXD2/DDRV2"), + PINCTRL_PIN(113, "GPIO113/RG2TXD3/DDRV3"), + PINCTRL_PIN(114, "GPIO114/SMB0SCL"), + PINCTRL_PIN(115, "GPIO115/SMB0SDA"), + PINCTRL_PIN(116, "GPIO116/SMB1SCL"), + PINCTRL_PIN(117, "GPIO117/SMB1SDA"), + PINCTRL_PIN(118, "GPIO118/SMB2SCL"), + PINCTRL_PIN(119, "GPIO119/SMB2SDA"), + PINCTRL_PIN(120, "GPIO120/SMB2CSDA"), + PINCTRL_PIN(121, "GPIO121/SMB2CSCL"), + PINCTRL_PIN(122, "GPIO122/SMB2BSDA"), + PINCTRL_PIN(123, "GPIO123/SMB2BSCL"), + PINCTRL_PIN(124, "GPIO124/SMB1CSDA"), + PINCTRL_PIN(125, "GPIO125/SMB1CSCL"), + PINCTRL_PIN(126, "GPIO126/SMB1BSDA"), + PINCTRL_PIN(127, "GPIO127/SMB1BSCL"), + + PINCTRL_PIN(128, "GPIO128/SMB8SCL"), + PINCTRL_PIN(129, "GPIO129/SMB8SDA"), + PINCTRL_PIN(130, "GPIO130/SMB9SCL"), + PINCTRL_PIN(131, "GPIO131/SMB9SDA"), + PINCTRL_PIN(132, "GPIO132/SMB10SCL"), + PINCTRL_PIN(133, "GPIO133/SMB10SDA"), + PINCTRL_PIN(134, "GPIO134/SMB11SCL"), + PINCTRL_PIN(135, "GPIO135/SMB11SDA"), + PINCTRL_PIN(136, "GPIO136/SD1DT0"), + PINCTRL_PIN(137, "GPIO137/SD1DT1"), + PINCTRL_PIN(138, "GPIO138/SD1DT2"), + PINCTRL_PIN(139, "GPIO139/SD1DT3"), + PINCTRL_PIN(140, "GPIO140/SD1CLK"), + PINCTRL_PIN(141, "GPIO141/SD1WP"), + PINCTRL_PIN(142, "GPIO142/SD1CMD"), + PINCTRL_PIN(143, "GPIO143/SD1CD/SD1PWR"), + PINCTRL_PIN(144, "GPIO144/PWM4"), + PINCTRL_PIN(145, "GPIO145/PWM5"), + PINCTRL_PIN(146, "GPIO146/PWM6"), + PINCTRL_PIN(147, "GPIO147/PWM7"), + PINCTRL_PIN(148, "GPIO148/MMCDT4"), + PINCTRL_PIN(149, "GPIO149/MMCDT5"), + PINCTRL_PIN(150, "GPIO150/MMCDT6"), + PINCTRL_PIN(151, "GPIO151/MMCDT7"), + PINCTRL_PIN(152, "GPIO152/MMCCLK"), + PINCTRL_PIN(153, "GPIO153/MMCWP"), + PINCTRL_PIN(154, "GPIO154/MMCCMD"), + PINCTRL_PIN(155, "GPIO155/nMMCCD/nMMCRST"), + PINCTRL_PIN(156, "GPIO156/MMCDT0"), + PINCTRL_PIN(157, "GPIO157/MMCDT1"), + PINCTRL_PIN(158, "GPIO158/MMCDT2"), + PINCTRL_PIN(159, "GPIO159/MMCDT3"), + + PINCTRL_PIN(160, "GPIO160/CLKOUT/RNGOSCOUT"), + PINCTRL_PIN(161, "GPIO161/nLFRAME/nESPICS"), + PINCTRL_PIN(162, "GPIO162/SERIRQ"), + PINCTRL_PIN(163, "GPIO163/LCLK/ESPICLK"), + PINCTRL_PIN(164, "GPIO164/LAD0/ESPI_IO0"/*dscnt6*/), + PINCTRL_PIN(165, "GPIO165/LAD1/ESPI_IO1"/*dscnt6*/), + PINCTRL_PIN(166, "GPIO166/LAD2/ESPI_IO2"/*dscnt6*/), + PINCTRL_PIN(167, "GPIO167/LAD3/ESPI_IO3"/*dscnt6*/), + PINCTRL_PIN(168, "GPIO168/nCLKRUN/nESPIALERT"), + PINCTRL_PIN(169, "GPIO169/nSCIPME"), + PINCTRL_PIN(170, "GPIO170/nSMI"), + PINCTRL_PIN(171, "GPIO171/SMB6SCL"), + PINCTRL_PIN(172, "GPIO172/SMB6SDA"), + PINCTRL_PIN(173, "GPIO173/SMB7SCL"), + PINCTRL_PIN(174, "GPIO174/SMB7SDA"), + PINCTRL_PIN(175, "GPIO175/PSPI1CK/FANIN19"), + PINCTRL_PIN(176, "GPIO176/PSPI1DO/FANIN18"), + PINCTRL_PIN(177, "GPIO177/PSPI1DI/FANIN17"), + PINCTRL_PIN(178, "GPIO178/R1TXD0"), + PINCTRL_PIN(179, "GPIO179/R1TXD1"), + PINCTRL_PIN(180, "GPIO180/R1TXEN"), + PINCTRL_PIN(181, "GPIO181/R1RXD0"), + PINCTRL_PIN(182, "GPIO182/R1RXD1"), + PINCTRL_PIN(183, "GPIO183/SPI3CK"), + PINCTRL_PIN(184, "GPO184/SPI3D0/STRAP9"), + PINCTRL_PIN(185, "GPO185/SPI3D1/STRAP10"), + PINCTRL_PIN(186, "GPIO186/nSPI3CS0"), + PINCTRL_PIN(187, "GPIO187/nSPI3CS1"), + PINCTRL_PIN(188, "GPIO188/SPI3D2/nSPI3CS2"), + PINCTRL_PIN(189, "GPIO189/SPI3D3/nSPI3CS3"), + PINCTRL_PIN(190, "GPIO190/nPRD_SMI"), + PINCTRL_PIN(191, "GPIO191"), + + PINCTRL_PIN(192, "GPIO192"), + PINCTRL_PIN(193, "GPIO193/R1CRSDV"), + PINCTRL_PIN(194, "GPIO194/SMB0BSCL"), + PINCTRL_PIN(195, "GPIO195/SMB0BSDA"), + PINCTRL_PIN(196, "GPIO196/SMB0CSCL"), + PINCTRL_PIN(197, "GPIO197/SMB0DEN"), + PINCTRL_PIN(198, "GPIO198/SMB0DSDA"), + PINCTRL_PIN(199, "GPIO199/SMB0DSCL"), + PINCTRL_PIN(200, "GPIO200/R2CK"), + PINCTRL_PIN(201, "GPIO201/R1CK"), + PINCTRL_PIN(202, "GPIO202/SMB0CSDA"), + PINCTRL_PIN(203, "GPIO203/FANIN16"), + PINCTRL_PIN(204, "GPIO204/DDC2SCL"), + PINCTRL_PIN(205, "GPIO205/DDC2SDA"), + PINCTRL_PIN(206, "GPIO206/HSYNC2"), + PINCTRL_PIN(207, "GPIO207/VSYNC2"), + PINCTRL_PIN(208, "GPIO208/RG2TXC/DVCK"), + PINCTRL_PIN(209, "GPIO209/RG2TXCTL/DDRV4"), + PINCTRL_PIN(210, "GPIO210/RG2RXD0/DDRV5"), + PINCTRL_PIN(211, "GPIO211/RG2RXD1/DDRV6"), + PINCTRL_PIN(212, "GPIO212/RG2RXD2/DDRV7"), + PINCTRL_PIN(213, "GPIO213/RG2RXD3/DDRV8"), + PINCTRL_PIN(214, "GPIO214/RG2RXC/DDRV9"), + PINCTRL_PIN(215, "GPIO215/RG2RXCTL/DDRV10"), + PINCTRL_PIN(216, "GPIO216/RG2MDC/DDRV11"), + PINCTRL_PIN(217, "GPIO217/RG2MDIO/DVHSYNC"), + PINCTRL_PIN(218, "GPIO218/nWDO1"), + PINCTRL_PIN(219, "GPIO219/nWDO2"), + PINCTRL_PIN(220, "GPIO220/SMB12SCL"), + PINCTRL_PIN(221, "GPIO221/SMB12SDA"), + PINCTRL_PIN(222, "GPIO222/SMB13SCL"), + PINCTRL_PIN(223, "GPIO223/SMB13SDA"), + + PINCTRL_PIN(224, "GPIO224/SPIXCK"), + PINCTRL_PIN(225, "GPO225/SPIXD0/STRAP12"), + PINCTRL_PIN(226, "GPO226/SPIXD1/STRAP13"), + PINCTRL_PIN(227, "GPIO227/nSPIXCS0"), + PINCTRL_PIN(228, "GPIO228/nSPIXCS1"), + PINCTRL_PIN(229, "GPIO229/SPIXD2/STRAP3"), + PINCTRL_PIN(230, "GPIO230/SPIXD3"), + PINCTRL_PIN(231, "GPIO231/nCLKREQ"), + PINCTRL_PIN(255, "GPI255/DACOSEL"), +}; + +static const char *gcr_regname(int reg) +{ + switch (reg) { + case NPCM7XX_GCR_MFSEL1: return "MFSEL1"; + case NPCM7XX_GCR_MFSEL2: return "MFSEL2"; + case NPCM7XX_GCR_MFSEL3: return "MFSEL3"; + case NPCM7XX_GCR_MFSEL4: return "MFSEL4"; + case NPCM7XX_GCR_I2CSEGSEL: return "I2CSEGSEL"; + case NPCM7XX_GCR_FLOCKR1: return "FLOCKR1"; + } + return "xx"; +} + +static void npcm_setmode(struct regmap *gcr_regmap, int reg, int bit, int mode, int pin) +{ + u32 val, mask = (1L << 1)-1; + + if (reg) { + pr_debug("Pin %d: setting reg:%x[%s.%d] = %d\n", pin, + reg, gcr_regname(reg), bit, mode); + + regmap_read(gcr_regmap, reg, &val); + val = val & ~(mask << bit); + regmap_write(gcr_regmap, reg, val | ((mode & mask) << bit)); + } +} + +/* Enable mode in pin group */ +static void npcm_setfunc(struct regmap *gcr_regmap, int pin, int n, int mode) +{ + const struct npcm_pincfg *cfg; + + while (n--) { + cfg = &pincfg[pin++]; + if (mode == fn_gpio || cfg->fn0 == mode || cfg->fn1 == mode + || cfg->fn2 == mode) { + npcm_setmode(gcr_regmap, cfg->reg0, cfg->bit0, + !!(cfg->fn0 == mode), pin-1); + npcm_setmode(gcr_regmap, cfg->reg1, cfg->bit1, + !!(cfg->fn1 == mode), pin-1); + npcm_setmode(gcr_regmap, cfg->reg2, cfg->bit2, + !!(cfg->fn2 == mode), pin-1); + } + } +} + +/* Get slew rate of pin (high/low) */ +static int npcm_get_slew_rate(struct NPCM_GPIO *bank, + struct regmap *gcr_regmap, unsigned int pin) +{ + u32 val; + int gpio = (pin % bank->gc.ngpio); + + if (pincfg[pin].flag & SLEW) + return gpio_bitop(bank, opGETBIT, gpio, NPCM_GP_N_OSRC); + /* LPC Slew rate in SRCNT register */ + if (pincfg[pin].flag & SLEWLPC) + { + regmap_read(gcr_regmap, NPCM7XX_GCR_SRCNT, &val); + return !!(val & SRCNT_ESPI); + } + + return -EINVAL; +} + +/* Get drive strength for a pin, if supported */ +static int npcm_get_drive_strength(struct pinctrl_dev *pctldev, + unsigned int pin) +{ + struct NPCM7xx_pinctrl *npcm = pinctrl_dev_get_drvdata(pctldev); + struct NPCM_GPIO *bank = &npcm->gpio_bank[pin/GPIO_PER_BANK]; + int gpio = (pin % bank->gc.ngpio); + u32 ds = 0; + int flg, val; + + flg = pincfg[pin].flag; + if (flg & DRIVE_STRENGTH_MASK) { + /* Get standard reading */ + val = gpio_bitop(bank, opGETBIT, gpio, NPCM_GP_N_ODSC); + ds = val ? DSHI(flg) : DSLO(flg); + dev_dbg(bank->gc.parent, " pin %d strength %d = %d\n", pin, val, ds); + return ds; + } + + return -EINVAL; +} + +/* Set drive strength for a pin, if supported */ +static int npcm_set_drive_strength(struct NPCM7xx_pinctrl *npcm, + unsigned int pin, int nval) +{ + int v; + struct NPCM_GPIO *bank = &npcm->gpio_bank[pin/GPIO_PER_BANK]; + int gpio = (pin % bank->gc.ngpio); + + v = (pincfg[pin].flag & DRIVE_STRENGTH_MASK); + if (!nval || !v) + return 0; + if (DSLO(v) == nval) { + dev_dbg(bank->gc.parent, " setting pin %d to low strength " + "[%d]\n", pin, nval); + gpio_bitop(bank, opCLRBIT, gpio, NPCM_GP_N_ODSC); + return 1; + } else if (DSHI(v) == nval) { + dev_dbg(bank->gc.parent, " setting pin %d to high strength " + "[%d]\n", pin, nval); + gpio_bitop(bank, opSETBIT, gpio, NPCM_GP_N_ODSC); + return 1; + } + + return 0; +} + +/* ================= pinctrl_ops ========================= */ +static void npcm_pin_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned int offset) +{ + seq_printf(s, "pinctrl_ops.dbg: %d", offset); +} + +static int npcm_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct NPCM7xx_pinctrl *npcm = pinctrl_dev_get_drvdata(pctldev); + + dev_dbg(npcm->dev, "group size: %d\n", ARRAY_SIZE(npcm_groups)); + return ARRAY_SIZE(npcm_groups); +} + +static const char *npcm_get_group_name(struct pinctrl_dev *pctldev, + unsigned int selector) +{ + return npcm_groups[selector].name; +} + +static int npcm_get_group_pins(struct pinctrl_dev *pctldev, + unsigned int selector, + const unsigned int **pins, + unsigned int *npins) +{ + *npins = npcm_groups[selector].npins; + *pins = npcm_groups[selector].pins; + return 0; +} + +static int npcm_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np_config, + struct pinctrl_map **map, + u32 *num_maps) +{ + pr_debug("dt_node_to_map: %s\n", np_config->name); + return pinconf_generic_dt_node_to_map(pctldev, np_config, + map, num_maps, + PIN_MAP_TYPE_INVALID); +} + +static void npcm_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *map, u32 num_maps) +{ + kfree(map); +} + +static struct pinctrl_ops npcm_pinctrl_ops = { + .get_groups_count = npcm_get_groups_count, + .get_group_name = npcm_get_group_name, + .get_group_pins = npcm_get_group_pins, + .pin_dbg_show = npcm_pin_dbg_show, + .dt_node_to_map = npcm_dt_node_to_map, + .dt_free_map = npcm_dt_free_map, +}; + +/* ================= pinmux_ops ========================= */ +static int npcm_get_functions_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(npcm_funcs); +} + +static const char *npcm_get_function_name(struct pinctrl_dev *pctldev, + unsigned int function) +{ + return npcm_funcs[function].name; +} + +static int npcm_get_function_groups(struct pinctrl_dev *pctldev, + unsigned int function, + const char * const **groups, + unsigned int * const ngroups) +{ + *ngroups = npcm_funcs[function].ngroups; + *groups = npcm_funcs[function].groups; + return 0; +} + +static int npcm_pinmux_set_mux(struct pinctrl_dev *pctldev, + unsigned int function, + unsigned int group) +{ + struct NPCM7xx_pinctrl *npcm = pinctrl_dev_get_drvdata(pctldev); + + dev_dbg(npcm->dev, "set_mux: %d, %d[%s]\n", function, group, + npcm_groups[group].name); + npcm_setfunc(npcm->gcr_regmap, 0, ARRAY_SIZE(pincfg), group); + return 0; +} + +static int npcm_gpio_request_enable(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int offset) +{ + struct NPCM7xx_pinctrl *npcm = pinctrl_dev_get_drvdata(pctldev); + + if (!range) { + dev_err(npcm->dev, "invalid range\n"); + return -EINVAL; + } + if (!range->gc) { + dev_err(npcm->dev, "invalid gpiochip\n"); + return -EINVAL; + } + /*dev_dbg(npcm->gc.parent, "Enable GPIO %d\n", offset);*/ + npcm_setfunc(npcm->gcr_regmap, offset, 1, fn_gpio); + return 0; +} + +/* Release GPIO back to pinctrl mode */ +static void npcm_gpio_request_free(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int offset) +{ + struct NPCM7xx_pinctrl *npcm = pinctrl_dev_get_drvdata(pctldev); + int virq; + + virq = irq_find_mapping(npcm->domain, offset); + /*dev_dbg(npcm->gc.parent, "Free GPIO %d, irq=%d\n", offset, virq);*/ + if (virq) + irq_dispose_mapping(virq); +} + +/* Set GPIO direction */ +static int npcm_gpio_set_direction(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int offset, bool input) +{ + struct NPCM7xx_pinctrl *npcm = pinctrl_dev_get_drvdata(pctldev); + struct NPCM_GPIO *bank = &npcm->gpio_bank[offset/GPIO_PER_BANK]; + int gpio = (offset % bank->gc.ngpio); + + dev_dbg(bank->gc.parent, "GPIO Set Direction: %d = %d\n", offset, + input); + if (input) { + gpio_bitop(bank, opSET, gpio, NPCM_GP_N_OEC); + gpio_bitop(bank, opSETBIT, gpio, NPCM_GP_N_IEM); + } else { + gpio_bitop(bank, opCLRBIT, gpio, NPCM_GP_N_IEM); + gpio_bitop(bank, opSET, gpio, NPCM_GP_N_OES); + } + return 0; +} + +static struct pinmux_ops npcm_pinmux_ops = { + .get_functions_count = npcm_get_functions_count, + .get_function_name = npcm_get_function_name, + .get_function_groups = npcm_get_function_groups, + + .set_mux = npcm_pinmux_set_mux, + + .gpio_request_enable = npcm_gpio_request_enable, + .gpio_disable_free = npcm_gpio_request_free, + .gpio_set_direction = npcm_gpio_set_direction, +}; + +/* ================= pinconf_ops ========================= */ + +/* Get configuration setting for a pin */ +static int npcm_config_get(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *config) +{ + enum pin_config_param param = pinconf_to_config_param(*config); + struct NPCM7xx_pinctrl *npcm = pinctrl_dev_get_drvdata(pctldev); + struct NPCM_GPIO *bank = &npcm->gpio_bank[pin/GPIO_PER_BANK]; + int gpio = (pin % bank->gc.ngpio); + u32 ie, oe, pu, pd; + int rc; + + rc = 0; + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_BIAS_PULL_DOWN: + pu = gpio_bitop(bank, opGETBIT, gpio, NPCM_GP_N_PU); + pd = gpio_bitop(bank, opGETBIT, gpio, NPCM_GP_N_PD); + if (param == PIN_CONFIG_BIAS_DISABLE) + rc = (!pu && !pd); + else if (param == PIN_CONFIG_BIAS_PULL_UP) + rc = (pu && !pd); + else if (param == PIN_CONFIG_BIAS_PULL_DOWN) + rc = (!pu && pd); + break; + case PIN_CONFIG_OUTPUT: + case PIN_CONFIG_INPUT_ENABLE: + ie = gpio_bitop(bank, opGETBIT, gpio, NPCM_GP_N_IEM); + oe = gpio_bitop(bank, opGETBIT, gpio, NPCM_GP_N_OE); + if (param == PIN_CONFIG_INPUT_ENABLE) + rc = (ie && !oe); + else if (param == PIN_CONFIG_OUTPUT) + rc = (!ie && oe); + break; + case PIN_CONFIG_DRIVE_PUSH_PULL: + rc = !gpio_bitop(bank, opGETBIT, gpio, NPCM_GP_N_OTYP); + break; + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + rc = gpio_bitop(bank, opGETBIT, gpio, NPCM_GP_N_OTYP); + break; + case PIN_CONFIG_INPUT_DEBOUNCE: + rc = gpio_bitop(bank, opGETBIT, gpio, NPCM_GP_N_DBNC); + break; + case PIN_CONFIG_DRIVE_STRENGTH: + rc = npcm_get_drive_strength(pctldev, pin); + if (rc) + *config = pinconf_to_config_packed(param, rc * 1000); + break; + case PIN_CONFIG_SLEW_RATE: + rc = npcm_get_slew_rate(bank, npcm->gcr_regmap, pin); + if (rc >= 0) + *config = pinconf_to_config_packed(param, rc); + break; + default: + return -EINVAL; + } + if (!rc) + return -EINVAL; + return 0; +} + +/* Set configuration setting for a pin */ +static int npcm_config_set_one(struct NPCM7xx_pinctrl *npcm, unsigned int pin, + unsigned long config) +{ + enum pin_config_param param = pinconf_to_config_param(config); + u16 arg = pinconf_to_config_argument(config); + struct NPCM_GPIO *bank = &npcm->gpio_bank[pin/GPIO_PER_BANK]; + int gpio = (pin % bank->gc.ngpio); + int rc; + + dev_dbg(bank->gc.parent, "param=%d %d[GPIO]\n", param, pin); + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + npcm_setfunc(npcm->gcr_regmap, pin, 1, fn_gpio); + gpio_bitop(bank, opCLRBIT, gpio, NPCM_GP_N_PU); + gpio_bitop(bank, opCLRBIT, gpio, NPCM_GP_N_PD); + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + /* arg: 0=GND, !0=enabled */ + npcm_setfunc(npcm->gcr_regmap, pin, 1, fn_gpio); + gpio_bitop(bank, opCLRBIT, gpio, NPCM_GP_N_PU); + gpio_bitop(bank, opSETBIT, gpio, NPCM_GP_N_PD); + break; + case PIN_CONFIG_BIAS_PULL_UP: + /* arg: 0=VDD, !0=enabled */ + npcm_setfunc(npcm->gcr_regmap, pin, 1, fn_gpio); + gpio_bitop(bank, opCLRBIT, gpio, NPCM_GP_N_PD); + gpio_bitop(bank, opSETBIT, gpio, NPCM_GP_N_PU); + break; + case PIN_CONFIG_INPUT_ENABLE: + /* arg: 0=disable, 1=enable */ + npcm_setfunc(npcm->gcr_regmap, pin, 1, fn_gpio); + gpio_bitop(bank, opSET, gpio, NPCM_GP_N_OEC); + gpio_bitop(bank, opSETBIT, gpio, NPCM_GP_N_IEM); + break; + case PIN_CONFIG_OUTPUT: + /* arg: 0=low, 1=high */ + npcm_setfunc(npcm->gcr_regmap, pin, 1, fn_gpio); + gpio_bitop(bank, opCLRBIT, gpio, NPCM_GP_N_IEM); + gpio_bitop(bank, opSET, gpio, arg ? NPCM_GP_N_DOS : + NPCM_GP_N_DOC); + gpio_bitop(bank, opSET, gpio, NPCM_GP_N_OES); + break; + case PIN_CONFIG_DRIVE_PUSH_PULL: + npcm_setfunc(npcm->gcr_regmap, pin, 1, fn_gpio); + gpio_bitop(bank, opCLRBIT, gpio, NPCM_GP_N_OTYP); + break; + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + npcm_setfunc(npcm->gcr_regmap, pin, 1, fn_gpio); + gpio_bitop(bank, opSETBIT, gpio, NPCM_GP_N_OTYP); + break; + case PIN_CONFIG_INPUT_DEBOUNCE: + npcm_setfunc(npcm->gcr_regmap, pin, 1, fn_gpio); + gpio_bitop(bank, opSETBIT, gpio, NPCM_GP_N_DBNC); + break; + case PIN_CONFIG_DRIVE_STRENGTH: + /* arg is mA */ + npcm_setfunc(npcm->gcr_regmap, pin, 1, fn_gpio); + rc = npcm_set_drive_strength(npcm, gpio, arg / 1000); + if (!rc) + return -EINVAL; + break; + default: + return -EINVAL; + } + return 0; +} + +/* Set multiple configuration settings for a pin */ +static int npcm_config_set(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *configs, + unsigned int num_configs) +{ + struct NPCM7xx_pinctrl *npcm = pinctrl_dev_get_drvdata(pctldev); + int rc; + + while (num_configs--) { + rc = npcm_config_set_one(npcm, pin, *configs++); + if (rc) + return rc; + } + return 0; +} + +static struct pinconf_ops npcm_pinconf_ops = { + .is_generic = true, + .pin_config_get = npcm_config_get, + .pin_config_set = npcm_config_set, +}; + +/* ================= pinctrl_desc ========================= */ +static struct pinctrl_desc npcm_pinctrl_desc = { + .name = "npcm-pinctrl", + .pins = npcm_pins, + .npins = ARRAY_SIZE(npcm_pins), + .pctlops = &npcm_pinctrl_ops, + .pmxops = &npcm_pinmux_ops, + .confops = &npcm_pinconf_ops, +// .owner = THIS_MODULE, +}; + +static struct gpio_chip npcm_gc = { + .owner = THIS_MODULE, + .label = "npcmgpio", + .request = npcmgpio_gpio_request, + .free = npcmgpio_gpio_free, + .get_direction = npcmgpio_get_direction, + .direction_input = npcmgpio_direction_input, + .direction_output = npcmgpio_direction_output, + .get = npcmgpio_get_value, + .set = npcmgpio_set_value, + .dbg_show = npcmgpio_dbg_show, +}; + +static int npcm_gpio_register(struct NPCM7xx_pinctrl *pctrl) +{ + int ret; + struct resource res; + int id=0,irq; + struct device_node *np; + struct of_phandle_args pinspec; + + for_each_available_child_of_node(pctrl->dev->of_node, np) { + if (of_find_property(np, "gpio-controller", NULL)) { + + ret = of_address_to_resource(np, 0, &res); + if (ret < 0) { + dev_err(pctrl->dev, "Resource fail for GPIO " + "bank %u: %d\n", id, ret); + goto err; + } + + pctrl->gpio_bank[id].base = ioremap(res.start, + resource_size(&res)); + + irq = irq_of_parse_and_map(np, 0); + if (irq < 0) { + dev_err(pctrl->dev, "No IRQ for GPIO bank %u: " + "%d\n", id, irq); + ret = irq; + goto err; + } + + ret = of_parse_phandle_with_fixed_args(np, + "gpio-ranges", 3, + 0, &pinspec); + if (ret < 0) { + dev_err(pctrl->dev, "gpio-ranges fail for GPIO " + "bank %u: %d\n", id, ret); + goto err; + } + + if (ret) + break; + + spin_lock_init(&pctrl->gpio_bank[id].lock); + pctrl->gpio_bank[id].irq = irq; + pctrl->gpio_bank[id].gc = npcm_gc; + pctrl->gpio_bank[id].irq_chip = npcmgpio_irqchip; + pctrl->gpio_bank[id].gc.parent = pctrl->dev; + pctrl->gpio_bank[id].irqbase = id * GPIO_PER_BANK; + pctrl->gpio_bank[id].pinctrl_id = pinspec.args[0]; + pctrl->gpio_bank[id].gc.base = pinspec.args[1]; + pctrl->gpio_bank[id].gc.ngpio = pinspec.args[2]; + + ret = gpiochip_add_data(&pctrl->gpio_bank[id].gc, + &pctrl->gpio_bank[id]); + if (ret < 0) { + dev_err(pctrl->dev, + "Failed to add GPIO chip %u: %d\n", + id, ret); + goto err; + } + + ret = gpiochip_irqchip_add(&pctrl->gpio_bank[id].gc, + &pctrl->gpio_bank[id].irq_chip, + 0, handle_level_irq, + IRQ_TYPE_NONE); + if (ret < 0) { + dev_err(pctrl->dev, + "Failed to add IRQ chip %u: %d\n", + id, ret); + gpiochip_remove(&pctrl->gpio_bank[id].gc); + goto err; + } + + gpiochip_set_chained_irqchip(&pctrl->gpio_bank[id].gc, + &pctrl->gpio_bank[id].irq_chip, + irq, npcmgpio_irq_handler); + + id++; + } + else + break; + } + + pctrl->bank_num = id; + return 0; + +err: + for (; id > 0; id--) + gpiochip_remove(&pctrl->gpio_bank[id-1].gc); + + pctrl->bank_num = 0; + + return ret; +} + +static int npcm_pinctrl_probe(struct platform_device *pdev) +{ + struct NPCM7xx_pinctrl *pctrl; + int i,ret; + + pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL); + if (!pctrl) + return -ENOMEM; + + pctrl->dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, pctrl); + + pctrl->gcr_regmap = + syscon_regmap_lookup_by_compatible("nuvoton,npcm750-gcr"); + if (IS_ERR(pctrl->gcr_regmap)) { + pr_err("%s: didn't find nuvoton,npcm750-gcr\n", __func__); + return IS_ERR(pctrl->gcr_regmap); + } + + ret = npcm_gpio_register(pctrl); + if (ret < 0) { + dev_err(pctrl->dev, "Failed to register gpio %u\n", ret); + return ret; + } + + pctrl->pctldev = devm_pinctrl_register(&pdev->dev, &npcm_pinctrl_desc, + pctrl); + if (IS_ERR(pctrl->pctldev)) { + dev_err(&pdev->dev, "Failed to register pinctrl device\n"); + ret = PTR_ERR(pctrl->pctldev); + i = pctrl->bank_num; + goto err_range; + } + + + for (i = 0 ; i < pctrl->bank_num ; i++) { + ret = gpiochip_add_pin_range(&pctrl->gpio_bank[i].gc, + dev_name(pctrl->dev), + pctrl->gpio_bank[i].pinctrl_id, + pctrl->gpio_bank[i].gc.base, + pctrl->gpio_bank[i].gc.ngpio); + + if (ret < 0) { + dev_err(pctrl->dev, "Failed to add GPIO range %u: %d\n", + i, ret); + gpiochip_remove(&pctrl->gpio_bank[i].gc); + goto err_range; + } + } + + pr_info("Nuvoton Pinctrl driver probed\n"); + return 0; + +err_range: + for (; i > 0; i--) + gpiochip_remove(&pctrl->gpio_bank[i-1].gc); + + return ret; +} + +static const struct of_device_id npcm_pinctrl_match[] = { + { .compatible = "nuvoton,npcm7xx-pinctrl" }, + { }, +}; +MODULE_DEVICE_TABLE(of, npcm_pinctrl_match); + +static struct platform_driver npcm_pinctrl_driver = { + .probe = npcm_pinctrl_probe, + .driver = { + .name = "npcm-pinctrl", + .of_match_table = npcm_pinctrl_match, + .suppress_bind_attrs = true, + }, +}; + +static int __init npcm_pinctrl_register(void) +{ + return platform_driver_register(&npcm_pinctrl_driver); +} +arch_initcall(npcm_pinctrl_register); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("jordan_hargrave@dell.com"); +MODULE_AUTHOR("tomer.maimon@nuvoton.com"); +MODULE_VERSION("1.00"); +MODULE_DESCRIPTION("Provide Pinctrl/GPIO methods for NPCM7XX"); From 113796355b729b71e6c76bb0b30807cb3a98cd77 Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Thu, 4 Jan 2018 11:44:50 +0200 Subject: [PATCH 18/37] dt-binding: rng: document NPCM7xx rng DT bindings Added device tree binding documentation for Nuvoton NPCM7xx random number generator. Signed-off-by: Tomer Maimon --- .../bindings/rng/nuvoton,npcm7xx-rng.txt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 Documentation/devicetree/bindings/rng/nuvoton,npcm7xx-rng.txt diff --git a/Documentation/devicetree/bindings/rng/nuvoton,npcm7xx-rng.txt b/Documentation/devicetree/bindings/rng/nuvoton,npcm7xx-rng.txt new file mode 100644 index 00000000000000..3b4e8103726319 --- /dev/null +++ b/Documentation/devicetree/bindings/rng/nuvoton,npcm7xx-rng.txt @@ -0,0 +1,16 @@ +NPCM7XX SoC random number generator. + +Required properties: +- compatible : "nuvoton,npcm750-rng" for Poleg NPCM750 +- reg : Offset and length of the registers set for the rng device. + +Optional: +- clocks : phandle of rng reference clock. + +Example: + +rng: rng@f000b000 { + compatible = "nuvoton,npcm750-rng"; + reg = <0xf000b000 0x1000>; + clocks = <&clk NPCM7XX_CLK_APB1>; +}; From 3514d28f9e7fe77e632bb001435d320d66560196 Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Thu, 4 Jan 2018 11:48:17 +0200 Subject: [PATCH 19/37] hw_random: npcm: add NPCM7xx random number generator Add Nuvoton BMC NPCM7xx random number generator driver. Signed-off-by: Tomer Maimon --- drivers/char/hw_random/Kconfig | 13 ++ drivers/char/hw_random/Makefile | 1 + drivers/char/hw_random/npcm7xx-rng.c | 203 +++++++++++++++++++++++++++ 3 files changed, 217 insertions(+) create mode 100644 drivers/char/hw_random/npcm7xx-rng.c diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index 1b223c32a8ae72..e7ba0cb04744a0 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -180,6 +180,19 @@ config HW_RANDOM_OMAP If unsure, say Y. +config HW_RANDOM_NPCM7XX + tristate "NPCM7XX Random Number Generator support" + depends on HW_RANDOM && ARCH_NPCM7XX + default HW_RANDOM + ---help--- + This driver provides kernel-side support for the Random Number + Generator hardware found on npcm7xx processor. + + To compile this driver as a module, choose M here: the + module will be called npcm7xx-rng. + + If unsure, say Y. + config HW_RANDOM_OMAP3_ROM tristate "OMAP3 ROM Random Number Generator support" depends on ARCH_OMAP3 diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile index b085975ec1d2ff..c612f1a4bd7640 100644 --- a/drivers/char/hw_random/Makefile +++ b/drivers/char/hw_random/Makefile @@ -37,3 +37,4 @@ obj-$(CONFIG_HW_RANDOM_MESON) += meson-rng.o obj-$(CONFIG_HW_RANDOM_CAVIUM) += cavium-rng.o cavium-rng-vf.o obj-$(CONFIG_HW_RANDOM_MTK) += mtk-rng.o obj-$(CONFIG_HW_RANDOM_S390) += s390-trng.o +obj-$(CONFIG_HW_RANDOM_NPCM7XX) += npcm7xx-rng.o diff --git a/drivers/char/hw_random/npcm7xx-rng.c b/drivers/char/hw_random/npcm7xx-rng.c new file mode 100644 index 00000000000000..b881e61c5a14e5 --- /dev/null +++ b/drivers/char/hw_random/npcm7xx-rng.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2014-2017 Nuvoton Technology corporation. + * + * Released under the GPLv2 only. + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RNGCS_REG 0x00 /* Control and status register */ +#define RNGD_REG 0x04 /* DATA register */ +#define RNGTST_REG 0x08 /* TEST register */ + +#define RNG_CLK_SET (0x06 << 2) /* 20-25 MHz */ +#define RNG_DATA_VALID (0x01 << 1) +#define RNG_ENABLE 0x01 + +#define RNG_DEBUG + +static void __iomem *rng_base; +static struct platform_device *rng_dev; + +static inline u32 npcm750_rng_read_reg(int reg) +{ + return __raw_readb(rng_base + reg); +} + +static inline void npcm750_rng_write_reg(int reg, u32 val) +{ + __raw_writeb(val, rng_base + reg); +} + +static int npcm750_rng_data_present(struct hwrng *rng, int wait) +{ + int data, i; + + for (i = 0; i < 20; i++) { + data = (npcm750_rng_read_reg(RNGCS_REG) & RNG_DATA_VALID) ? + 1 : 0; + if (data || !wait) + break; + /* RNG produces data fast enough (2+ MBit/sec, even + * during "rngtest" loads, that these delays don't + * seem to trigger. We *could* use the RNG IRQ, but + * that'd be higher overhead ... so why bother? + */ + udelay(10); + } + + return data; +} + +static int npcm750_rng_data_read(struct hwrng *rng, u32 *data) +{ + *data = npcm750_rng_read_reg(RNGD_REG); + + return 1; +} + +static struct hwrng npcm750_rng_ops = { + .name = "npcm750", + .data_present = npcm750_rng_data_present, + .data_read = npcm750_rng_data_read, +}; + +static int npcm750_rng_probe(struct platform_device *pdev) +{ + struct resource *res; + int ret; + + /* + * A bit ugly, and it will never actually happen but there can + * be only one RNG and this catches any bork + */ + if (rng_dev) + return -EBUSY; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if (!res) { + ret = -ENOENT; + goto err_region; + } + + if (!request_mem_region(res->start, resource_size(res), pdev->name)) { + ret = -EBUSY; + goto err_region; + } + + dev_set_drvdata(&pdev->dev, res); + rng_base = ioremap(res->start, resource_size(res)); + if (!rng_base) { + ret = -ENOMEM; + goto err_ioremap; + } + + ret = hwrng_register(&npcm750_rng_ops); + if (ret) + goto err_register; + + npcm750_rng_write_reg(RNGTST_REG, 0x02); + npcm750_rng_write_reg(RNGCS_REG, RNG_CLK_SET | RNG_ENABLE); + + rng_dev = pdev; + +#ifdef RNG_DEBUG + mdelay(10); + pr_info("RNG-Random Number Generator 0x%x\n", + npcm750_rng_read_reg(RNGD_REG)); +#else + pr_info("RNG-Random Number Generator\n"); +#endif + + return 0; + +err_register: + iounmap(rng_base); + rng_base = NULL; +err_ioremap: + release_mem_region(res->start, resource_size(res)); +err_region: + + return ret; +} + +static int __exit npcm750_rng_remove(struct platform_device *pdev) +{ + struct resource *res = dev_get_drvdata(&pdev->dev); + + hwrng_unregister(&npcm750_rng_ops); + npcm750_rng_write_reg(RNGCS_REG, 0x0); + iounmap(rng_base); + release_mem_region(res->start, resource_size(res)); + + rng_base = NULL; + + return 0; +} + +#ifdef CONFIG_PM + +static int npcm750_rng_suspend(struct platform_device *pdev, + pm_message_t message) +{ + u32 val; + + val = npcm750_rng_read_reg(RNGCS_REG); + val &= 0xfffffffe; + npcm750_rng_write_reg(RNGCS_REG, val); + return 0; +} + +static int npcm750_rng_resume(struct platform_device *pdev) +{ + u32 val; + + val = npcm750_rng_read_reg(RNGCS_REG); + val |= 0x00000001; + npcm750_rng_write_reg(RNGCS_REG, val); + return 0; +} + +#else + +#define npcm750_rng_suspend NULL +#define npcm750_rng_resume NULL + +#endif + +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:npcm750-rng"); + +static const struct of_device_id rng_dt_id[] = { + { .compatible = "nuvoton,npcm750-rng", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rng_dt_id); + +static struct platform_driver npcm750_rng_driver = { + .driver = { + .name = "npcm750-rng", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(rng_dt_id), + }, + .probe = npcm750_rng_probe, + .remove = __exit_p(npcm750_rng_remove), + .suspend = npcm750_rng_suspend, + .resume = npcm750_rng_resume +}; + +module_platform_driver(npcm750_rng_driver); + +MODULE_AUTHOR("Deepak Saxena (and others)"); +MODULE_LICENSE("GPL"); From e88dd7ef68178edc2d645a0c201261c9be790404 Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Thu, 4 Jan 2018 11:55:38 +0200 Subject: [PATCH 20/37] dt-binding: net: document NPCM7xx EMC DT bindings Added device tree binding documentation for Nuvoton NPCM7xx Ethernet MAC Controller (EMC). Signed-off-by: Tomer Maimon --- .../bindings/net/nuvoton,npcm7xx-emc.txt | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/nuvoton,npcm7xx-emc.txt diff --git a/Documentation/devicetree/bindings/net/nuvoton,npcm7xx-emc.txt b/Documentation/devicetree/bindings/net/nuvoton,npcm7xx-emc.txt new file mode 100644 index 00000000000000..812d8481a6fb15 --- /dev/null +++ b/Documentation/devicetree/bindings/net/nuvoton,npcm7xx-emc.txt @@ -0,0 +1,24 @@ +Nuvoton NPCM7XX 10/100 Ethernet MAC Controller (EMC) + +The NPCM750x provides two identical Ethernet MAC Controllers +for WAN/LAN applications + +Required properties: +- compatible : "nuvoton,npcm750-emc" for Poleg NPCM750. +- reg : Offset and length of the register set for the device. +- interrupts : Contain the emc interrupts with flags for falling edge. + first interrupt dedicated to Txirq + second interrupt dedicated to Rxirq +- clocks : phandle of emc reference clock. +- device_type : Should be "network" + +Example: + +emc0: eth@f0825000 { + device_type = "network"; + compatible = "nuvoton,npcm750-emc"; + reg = <0xf0825000 0x1000>; + interrupts = <0 16 4>, <0 15 4>; + clocks = <&clk NPCM7XX_CLK_EMC>; +}; + From cdab01fe08894a7acec2986e3ff64e2c9f4b356a Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Thu, 4 Jan 2018 12:20:56 +0200 Subject: [PATCH 21/37] net: npcm: add NPCM7xx Ethernet MAC controller Add Nuvoton BMC NPCM7xx Ethernet MAC controller (EMC) driver. Signed-off-by: Tomer Maimon --- drivers/net/ethernet/nuvoton/Kconfig | 23 +- drivers/net/ethernet/nuvoton/Makefile | 2 + drivers/net/ethernet/nuvoton/npcm7xx_emc.c | 2311 ++++++++++++++++++++ 3 files changed, 2335 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/nuvoton/npcm7xx_emc.c diff --git a/drivers/net/ethernet/nuvoton/Kconfig b/drivers/net/ethernet/nuvoton/Kconfig index 71c973f8e50f44..700701e109fac2 100644 --- a/drivers/net/ethernet/nuvoton/Kconfig +++ b/drivers/net/ethernet/nuvoton/Kconfig @@ -5,7 +5,7 @@ config NET_VENDOR_NUVOTON bool "Nuvoton devices" default y - depends on ARM && ARCH_W90X900 + depends on ARM && (ARCH_W90X900 || ARCH_NPCM7XX) ---help--- If you have a network (Ethernet) card belonging to this class, say Y. @@ -25,4 +25,25 @@ config W90P910_ETH Say Y here if you want to use built-in Ethernet ports on w90p910 processor. +config NPCM7XX_EMC_ETH + bool "Nuvoton NPCM7XX Ethernet EMC" + depends on ARM && ARCH_NPCM7XX + select PHYLIB + select MII + ---help--- + Say Y here if you want to use built-in Ethernet MAC + on NPCM750 MCU. + +config NPCM7XX_EMC_ETH_DEBUG + bool "Nuvoton NPCM7XX Ethernet EMC debug" + depends on NPCM7XX_EMC_ETH + ---help--- + Say Y here if you want debug info via /proc/driver/npcm7xx_emc.x + +config NPCM7XX_EMC_ETH_DEBUG_EXT + bool "Nuvoton NPCM7XX Ethernet EMC extra debug" + depends on NPCM7XX_EMC_ETH_DEBUG + ---help--- + Say Y here if you want extra debug info via /proc/driver/npcm7xx_emc.x + endif # NET_VENDOR_NUVOTON diff --git a/drivers/net/ethernet/nuvoton/Makefile b/drivers/net/ethernet/nuvoton/Makefile index 171aa044bd3b23..513a60647c5516 100644 --- a/drivers/net/ethernet/nuvoton/Makefile +++ b/drivers/net/ethernet/nuvoton/Makefile @@ -2,4 +2,6 @@ # Makefile for the Nuvoton network device drivers. # +#Eternet 10/100 EMC obj-$(CONFIG_W90P910_ETH) += w90p910_ether.o +obj-$(CONFIG_NPCM7XX_EMC_ETH) += npcm7xx_emc.o diff --git a/drivers/net/ethernet/nuvoton/npcm7xx_emc.c b/drivers/net/ethernet/nuvoton/npcm7xx_emc.c new file mode 100644 index 00000000000000..739705b6f40bdd --- /dev/null +++ b/drivers/net/ethernet/nuvoton/npcm7xx_emc.c @@ -0,0 +1,2311 @@ +/* + * Copyright (c) 2014-2017 Nuvoton Technology corporation. + * + * Released under the GPLv2 only. + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +static struct regmap *gcr_regmap; + +#define MFSEL1_OFFSET 0x00C +#define MFSEL3_OFFSET 0x064 +#define INTCR_OFFSET 0x03C + +static struct regmap *rst_regmap; + +#define IPSRST1_OFFSET 0x020 + +#define DRV_MODULE_NAME "npcm7xx-emc" +#define DRV_MODULE_VERSION "3.90" + +//#define CONFIG_NPCM7XX_EMC_ETH_DEBUG +#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG + #define dev_err(a, f, x...) pr_err("NPCM7XX-EMC: %s() dev_err:" \ + f, __func__, ## x) + #define EMC_DEBUG(f, x...) pr_info("NPCM7XX-EMC: %s():%s " f, \ + __func__, ether->ndev->name, \ + ## x) +#else + #define EMC_DEBUG(f, x...) +#endif + + +/* Ethernet MAC Registers */ +#define REG_CAMCMR (ether->reg + 0x00) +#define REG_CAMEN (ether->reg + 0x04) +#define REG_CAMM_BASE (ether->reg + 0x08) +#define REG_CAML_BASE (ether->reg + 0x0c) +#define REG_TXDLSA (ether->reg + 0x88) +#define REG_RXDLSA (ether->reg + 0x8C) +#define REG_MCMDR (ether->reg + 0x90) +#define REG_MIID (ether->reg + 0x94) +#define REG_MIIDA (ether->reg + 0x98) +#define REG_FFTCR (ether->reg + 0x9C) +#define REG_TSDR (ether->reg + 0xa0) +#define REG_RSDR (ether->reg + 0xa4) +#define REG_DMARFC (ether->reg + 0xa8) +#define REG_MIEN (ether->reg + 0xac) +#define REG_MISTA (ether->reg + 0xb0) +#define REG_MGSTA (ether->reg + 0xb4) +#define REG_MPCNT (ether->reg + 0xb8) +#define REG_MRPC (ether->reg + 0xbc) +#define REG_MRPCC (ether->reg + 0xc0) +#define REG_MREPC (ether->reg + 0xc4) +#define REG_DMARFS (ether->reg + 0xc8) +#define REG_CTXDSA (ether->reg + 0xcc) +#define REG_CTXBSA (ether->reg + 0xd0) +#define REG_CRXDSA (ether->reg + 0xd4) +#define REG_CRXBSA (ether->reg + 0xd8) + +/* EMC Diagnostic Registers */ +#define REG_RXFSM (ether->reg + 0x200) +#define REG_TXFSM (ether->reg + 0x204) +#define REG_FSM0 (ether->reg + 0x208) +#define REG_FSM1 (ether->reg + 0x20c) +#define REG_DCR (ether->reg + 0x210) +#define REG_DMMIR (ether->reg + 0x214) +#define REG_BISTR (ether->reg + 0x300) + +/* mac controller bit */ +#define MCMDR_RXON (0x01 << 0) +#define MCMDR_ALP (0x01 << 1) +#define MCMDR_ACP (0x01 << 3) +#define MCMDR_SPCRC (0x01 << 5) +#define MCMDR_TXON (0x01 << 8) +#define MCMDR_NDEF (0x01 << 9) +#define MCMDR_FDUP (0x01 << 18) +#define MCMDR_ENMDC (0x01 << 19) +#define MCMDR_OPMOD (0x01 << 20) +#define SWR (0x01 << 24) + +/* cam command regiser */ +#define CAMCMR_AUP 0x01 +#define CAMCMR_AMP (0x01 << 1) +#define CAMCMR_ABP (0x01 << 2) +#define CAMCMR_CCAM (0x01 << 3) +#define CAMCMR_ECMP (0x01 << 4) +#define CAM0EN 0x01 + +/* mac mii controller bit */ +#define MDCON (0x01 << 19) +#define PHYAD (0x01 << 8) +#define PHYWR (0x01 << 16) +#define PHYBUSY (0x01 << 17) +#define PHYPRESP (0x01 << 18) +#define CAM_ENTRY_SIZE 0x08 + +/* rx and tx status */ +#define TXDS_TXCP (0x01 << 19) +#define RXDS_CRCE (0x01 << 17) +#define RXDS_PTLE (0x01 << 19) +#define RXDS_RXGD (0x01 << 20) +#define RXDS_ALIE (0x01 << 21) +#define RXDS_RP (0x01 << 22) + +/* mac interrupt status*/ +#define MISTA_RXINTR (0x01 << 0) +#define MISTA_CRCE (0x01 << 1) +#define MISTA_RXOV (0x01 << 2) +#define MISTA_PTLE (0x01 << 3) +#define MISTA_RXGD (0x01 << 4) +#define MISTA_ALIE (0x01 << 5) +#define MISTA_RP (0x01 << 6) +#define MISTA_MMP (0x01 << 7) +#define MISTA_DFOI (0x01 << 8) +#define MISTA_DENI (0x01 << 9) +#define MISTA_RDU (0x01 << 10) +#define MISTA_RXBERR (0x01 << 11) +#define MISTA_CFR (0x01 << 14) +#define MISTA_TXINTR (0x01 << 16) +#define MISTA_TXEMP (0x01 << 17) +#define MISTA_TXCP (0x01 << 18) +#define MISTA_EXDEF (0x01 << 19) +#define MISTA_NCS (0x01 << 20) +#define MISTA_TXABT (0x01 << 21) +#define MISTA_LC (0x01 << 22) +#define MISTA_TDU (0x01 << 23) +#define MISTA_TXBERR (0x01 << 24) + +#define ENSTART 0x01 +#define ENRXINTR (0x01 << 0) +#define ENCRCE (0x01 << 1) +#define EMRXOV (0x01 << 2) +#define ENPTLE (0x01 << 3) +#define ENRXGD (0x01 << 4) +#define ENALIE (0x01 << 5) +#define ENRP (0x01 << 6) +#define ENMMP (0x01 << 7) +#define ENDFO (0x01 << 8) +#define ENDENI (0x01 << 9) +#define ENRDU (0x01 << 10) +#define ENRXBERR (0x01 << 11) +#define ENCFR (0x01 << 14) +#define ENTXINTR (0x01 << 16) +#define ENTXEMP (0x01 << 17) +#define ENTXCP (0x01 << 18) +#define ENTXDEF (0x01 << 19) +#define ENNCS (0x01 << 20) +#define ENTXABT (0x01 << 21) +#define ENLC (0x01 << 22) +#define ENTDU (0x01 << 23) +#define ENTXBERR (0x01 << 24) + +/* rx and tx owner bit */ +#define RX_OWEN_DMA (0x01 << 31) +#define RX_OWEN_CPU (~(0x03 << 30)) +#define TX_OWEN_DMA (0x01 << 31) +#define TX_OWEN_CPU (~(0x01 << 31)) + +/* tx frame desc controller bit */ +#define MACTXINTEN 0x04 +#define CRCMODE 0x02 +#define PADDINGMODE 0x01 + +/* fftcr controller bit */ +#define RXTHD (0x03 << 0) +#define TXTHD (0x02 << 8) +#define BLENGTH (0x02 << 20) + +/* global setting for driver */ +#define RX_DESC_SIZE 128 +#define TX_DESC_SIZE 64 +#define MAX_RBUFF_SZ 0x600 +#define MAX_TBUFF_SZ 0x600 +#define TX_TIMEOUT 50 +#define DELAY 1000 +#define CAM0 0x0 +#define RX_POLL_SIZE 16 + +#ifdef CONFIG_VLAN_8021Q +#define VLAN_SUPPORT +#endif + +#ifdef VLAN_SUPPORT +#define MAX_PACKET_SIZE 1518 +#define MAX_PACKET_SIZE_W_CRC (MAX_PACKET_SIZE + 4) // 1522 +#else +#define MAX_PACKET_SIZE 1514 +#define MAX_PACKET_SIZE_W_CRC (MAX_PACKET_SIZE + 4) // 1518 +#endif +#define MII_TIMEOUT 100 + + +#define ETH_TRIGGER_RX do { __raw_writel(ENSTART, REG_RSDR); } while (0) +#define ETH_TRIGGER_TX do { __raw_writel(ENSTART, REG_TSDR); } while (0) +#define ETH_ENABLE_TX do { __raw_writel(__raw_readl(REG_MCMDR) | \ + MCMDR_TXON, REG_MCMDR); } while (0) +#define ETH_ENABLE_RX do { __raw_writel(__raw_readl(REG_MCMDR) | \ + MCMDR_RXON, REG_MCMDR); } while (0) +#define ETH_DISABLE_TX do { __raw_writel(__raw_readl(REG_MCMDR) & \ + ~MCMDR_TXON, REG_MCMDR); } while (0) +#define ETH_DISABLE_RX do { __raw_writel(__raw_readl(REG_MCMDR) & \ + ~MCMDR_RXON, REG_MCMDR); } while (0) + +struct plat_npcm7xx_emc_data { + char *phy_bus_name; + int phy_addr; + unsigned char mac_addr[ETH_ALEN]; +}; + +struct npcm7xx_rxbd { + unsigned int sl; + unsigned int buffer; + unsigned int reserved; + unsigned int next; +#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG_EXT + unsigned int diff; + unsigned int ts; + unsigned int r2; + unsigned int r3; +#endif +}; + +struct npcm7xx_txbd { + unsigned int mode; /* Ownership bit and some other bits */ + unsigned int buffer; /* Transmit Buffer Starting Address */ + unsigned int sl; /* Transmit Byte Count and status bits */ + unsigned int next; /* Next Tx Descriptor Starting Address */ +#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG_EXT + unsigned int diff; + unsigned int ts; + unsigned int t2; + unsigned int t3; +#endif +}; + + +struct npcm7xx_ether { + struct sk_buff *rx_skb[RX_DESC_SIZE]; + struct sk_buff *tx_skb[TX_DESC_SIZE]; + spinlock_t lock; + struct npcm7xx_rxbd *rdesc; + struct npcm7xx_txbd *tdesc; + dma_addr_t rdesc_phys; + dma_addr_t tdesc_phys; + struct net_device_stats stats; + struct platform_device *pdev; + struct net_device *ndev; + struct resource *res; + //struct sk_buff *skb; + unsigned int msg_enable; + struct mii_bus *mii_bus; + struct phy_device *phy_dev; + struct napi_struct napi; + struct ncsi_dev *ncsidev; + bool use_ncsi; + void __iomem *reg; + int rxirq; + int txirq; + unsigned int cur_tx; + unsigned int cur_rx; + unsigned int finish_tx; + unsigned int pending_tx; + unsigned int start_tx_ptr; + unsigned int start_rx_ptr; + unsigned int rx_berr; + unsigned int rx_err; + unsigned int rdu; + unsigned int rxov; + unsigned int camcmr; + unsigned int rx_stuck; + int link; + int speed; + int duplex; + int need_reset; + char *dump_buf; + + // debug counters + unsigned int max_waiting_rx; + unsigned int rx_count_pool; + unsigned int count_xmit; + unsigned int rx_int_count; + unsigned int rx_err_count; + unsigned int tx_int_count; + unsigned int tx_tdu; + unsigned int tx_tdu_i; + unsigned int tx_cp_i; + unsigned int count_finish; +}; + +static void npcm7xx_ether_set_multicast_list(struct net_device *dev); +static int npcm7xx_info_dump(char *buf, int count, struct net_device *dev); +#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG +static void npcm7xx_info_print(struct net_device *dev); +#endif +#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG_EXT +void npcm7xx_clk_GetTimeStamp(u32 time_quad[2]); +#endif + +static void npcm7xx_opmode(struct net_device *dev, int speed, int duplex) +{ + unsigned int val; + struct npcm7xx_ether *ether = netdev_priv(dev); + + val = __raw_readl(REG_MCMDR); + + if (speed == 100) { + val |= MCMDR_OPMOD; + } else { + val &= ~MCMDR_OPMOD; + } + + if(duplex == DUPLEX_FULL) { + val |= MCMDR_FDUP; + } else { + val &= ~MCMDR_FDUP; + } + + __raw_writel(val, REG_MCMDR); +} + +static void adjust_link(struct net_device *dev) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + struct phy_device *phydev = ether->phy_dev; + bool status_change = false; + unsigned long flags; + + // clear GPIO interrupt status whihc indicates PHY statu change? + + spin_lock_irqsave(ðer->lock, flags); + + if (phydev->link) { + if ((ether->speed != phydev->speed) || + (ether->duplex != phydev->duplex)) { + ether->speed = phydev->speed; + ether->duplex = phydev->duplex; + status_change = true; + } + } else { + ether->speed = 0; + ether->duplex = -1; + } + + if (phydev->link != ether->link) { + + ether->link = phydev->link; + + status_change = true; + } + + spin_unlock_irqrestore(ðer->lock, flags); + + if (status_change) { + npcm7xx_opmode(dev, ether->speed, ether->duplex); + } +} + + + +static void npcm7xx_write_cam(struct net_device *dev, + unsigned int x, unsigned char *pval) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + unsigned int msw, lsw; + + msw = (pval[0] << 24) | (pval[1] << 16) | (pval[2] << 8) | pval[3]; + + lsw = (pval[4] << 24) | (pval[5] << 16); + + __raw_writel(lsw, REG_CAML_BASE + x * CAM_ENTRY_SIZE); + __raw_writel(msw, REG_CAMM_BASE + x * CAM_ENTRY_SIZE); + //EMC_DEBUG("REG_CAML_BASE = 0x%08X REG_CAMH_BASE = 0x%08X", lsw, msw); +} + + +static struct sk_buff *get_new_skb(struct net_device *dev, u32 i) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + struct sk_buff *skb = dev_alloc_skb(roundup(MAX_PACKET_SIZE_W_CRC, 4)); + + if (skb == NULL) + return NULL; + + /* Do not unmark the following skb_reserve() Receive Buffer Starting + * Address must be aligned to 4 bytes and the following line + * if unmarked will make it align to 2 and this likely will + * hult the RX and crash the linux skb_reserve(skb, NET_IP_ALIGN); + */ + skb->dev = dev; + + (ether->rdesc + i)->buffer = dma_map_single(&dev->dev, skb->data, + roundup( + MAX_PACKET_SIZE_W_CRC, + 4), DMA_FROM_DEVICE); + ether->rx_skb[i] = skb; + + return skb; +} + +static int npcm7xx_init_desc(struct net_device *dev) +{ + struct npcm7xx_ether *ether; + struct npcm7xx_txbd *tdesc; + struct npcm7xx_rxbd *rdesc; + struct platform_device *pdev; + unsigned int i; + + ether = netdev_priv(dev); + pdev = ether->pdev; + + if (ether->tdesc == NULL) { + ether->tdesc = (struct npcm7xx_txbd *) + dma_alloc_coherent(&pdev->dev, + sizeof(struct npcm7xx_txbd) * + TX_DESC_SIZE, + ðer->tdesc_phys, + GFP_KERNEL); + + if (!ether->tdesc) { + dev_err(&pdev->dev, "Failed to allocate memory for " + "tx desc\n"); + return -ENOMEM; + } + } + + if (ether->rdesc == NULL) { + ether->rdesc = (struct npcm7xx_rxbd *) + dma_alloc_coherent(&pdev->dev, + sizeof(struct npcm7xx_rxbd) * + RX_DESC_SIZE, + ðer->rdesc_phys, + GFP_KERNEL); + + if (!ether->rdesc) { + dev_err(&pdev->dev, "Failed to allocate memory for " + "rx desc\n"); + dma_free_coherent(&pdev->dev, + sizeof(struct npcm7xx_txbd) * + TX_DESC_SIZE, ether->tdesc, + ether->tdesc_phys); + ether->tdesc = NULL; + return -ENOMEM; + } + } + + for (i = 0; i < TX_DESC_SIZE; i++) { + unsigned int offset; + + tdesc = (ether->tdesc + i); + + if (i == TX_DESC_SIZE - 1) + offset = 0; + else + offset = sizeof(struct npcm7xx_txbd) * (i + 1); + + tdesc->next = ether->tdesc_phys + offset; + tdesc->buffer = (unsigned int)NULL; + tdesc->sl = 0; + tdesc->mode = 0; + } + + ether->start_tx_ptr = ether->tdesc_phys; + + for (i = 0; i < RX_DESC_SIZE; i++) { + unsigned int offset; + + rdesc = (ether->rdesc + i); + + if (i == RX_DESC_SIZE - 1) + offset = 0; + else + offset = sizeof(struct npcm7xx_rxbd) * (i + 1); + + rdesc->next = ether->rdesc_phys + offset; + rdesc->sl = RX_OWEN_DMA; + + if (get_new_skb(dev, i) == NULL) { + dev_err(&pdev->dev, "get_new_skb() failed\n"); + + for (; i != 0; i--) { + dma_unmap_single(&dev->dev, (dma_addr_t) + ((ether->rdesc + i)->buffer), + roundup(MAX_PACKET_SIZE_W_CRC, + 4), DMA_FROM_DEVICE); + dev_kfree_skb_any(ether->rx_skb[i]); + ether->rx_skb[i] = NULL; + } + + dma_free_coherent(&pdev->dev, + sizeof(struct npcm7xx_txbd) * + TX_DESC_SIZE, + ether->tdesc, ether->tdesc_phys); + dma_free_coherent(&pdev->dev, + sizeof(struct npcm7xx_rxbd) * + RX_DESC_SIZE, + ether->rdesc, ether->rdesc_phys); + + return -ENOMEM; + } + } + + ether->start_rx_ptr = ether->rdesc_phys; + wmb(); + for (i = 0; i < TX_DESC_SIZE; i++) + ether->tx_skb[i] = NULL; + + return 0; +} + +// This API must call with Tx/Rx stopped +static void npcm7xx_free_desc(struct net_device *dev, bool free_also_descriptors) +{ + struct sk_buff *skb; + u32 i; + struct npcm7xx_ether *ether = netdev_priv(dev); + struct platform_device *pdev = ether->pdev; + + for (i = 0; i < TX_DESC_SIZE; i++) { + skb = ether->tx_skb[i]; + if (skb != NULL) { + dma_unmap_single(&dev->dev, (dma_addr_t)((ether->tdesc + + i)->buffer), + skb->len, DMA_TO_DEVICE); + dev_kfree_skb_any(skb); + ether->tx_skb[i] = NULL; + } + } + + for (i = 0; i < RX_DESC_SIZE; i++) { + skb = ether->rx_skb[i]; + if (skb != NULL) { + dma_unmap_single(&dev->dev, (dma_addr_t)((ether->rdesc + + i)->buffer), + roundup(MAX_PACKET_SIZE_W_CRC, 4), + DMA_FROM_DEVICE); + dev_kfree_skb_any(skb); + ether->rx_skb[i] = NULL; + } + } + + if (free_also_descriptors) { + if (ether->tdesc) + dma_free_coherent(&pdev->dev, + sizeof(struct npcm7xx_txbd) * + TX_DESC_SIZE, + ether->tdesc, ether->tdesc_phys); + ether->tdesc = NULL; + + if (ether->rdesc) + dma_free_coherent(&pdev->dev, + sizeof(struct npcm7xx_rxbd) * + RX_DESC_SIZE, + ether->rdesc, ether->rdesc_phys); + ether->rdesc = NULL; + } + +} + +static void npcm7xx_set_fifo_threshold(struct net_device *dev) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + unsigned int val; + + val = RXTHD | TXTHD | BLENGTH; + __raw_writel(val, REG_FFTCR); +} + +static void npcm7xx_return_default_idle(struct net_device *dev) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + unsigned int val; + unsigned int saved_bits; + + val = __raw_readl(REG_MCMDR); + saved_bits = val & (MCMDR_FDUP | MCMDR_OPMOD); + //EMC_DEBUG("REG_MCMDR = 0x%08X\n", val); + val |= SWR; + __raw_writel(val, REG_MCMDR); + + /* During the EMC reset the AHB will read 0 from all registers, + * so in order to see if the reset finished we can't count on + * REG_MCMDR.SWR to become 0, instead we read another + * register that its reset value is not 0, we choos REG_FFTCR. + */ + do { + val = __raw_readl(REG_FFTCR); + } while (val == 0); + + // Now we can verify if REG_MCMDR.SWR became 0 (probably it will be 0 on the first read). + do { + val = __raw_readl(REG_MCMDR); + } while (val & SWR); + + //EMC_DEBUG("REG_MCMDR = 0x%08X\n", val); + // restore values + __raw_writel(saved_bits, REG_MCMDR); +} + + +static void npcm7xx_enable_mac_interrupt(struct net_device *dev) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + unsigned int val; + + val = ENRXINTR | // Start of RX interrupts + ENCRCE | + EMRXOV | +#ifndef VLAN_SUPPORT + ENPTLE | // Since we don't support VLAN we want interrupt on long packets +#endif + ENRXGD | + ENALIE | + ENRP | + ENMMP | + ENDFO | + /* ENDENI | */ // We don't need interrupt on DMA Early Notification + ENRDU | // We don't need interrupt on Receive Descriptor Unavailable Interrupt + ENRXBERR | + /* ENCFR | */ + ENTXINTR | // Start of TX interrupts + ENTXEMP | + ENTXCP | + ENTXDEF | + ENNCS | + ENTXABT | + ENLC | + /* ENTDU | */ // We don't need interrupt on Transmit Descriptor Unavailable at start of operation + ENTXBERR; + __raw_writel(val, REG_MIEN); +} + +static void npcm7xx_get_and_clear_int(struct net_device *dev, + unsigned int *val, unsigned int mask) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + + *val = __raw_readl(REG_MISTA) & mask; + __raw_writel(*val, REG_MISTA); +} + +static void npcm7xx_set_global_maccmd(struct net_device *dev) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + unsigned int val; + + val = __raw_readl(REG_MCMDR); + //EMC_DEBUG("REG_MCMDR = 0x%08X\n", val); + + val |= MCMDR_SPCRC | MCMDR_ENMDC | MCMDR_ACP | MCMDR_NDEF; +#ifdef VLAN_SUPPORT + // we set ALP accept long packets since VLAN packets are 4 bytes longer than 1518 + val |= MCMDR_ALP; + // limit receive length to 1522 bytes due to VLAN + __raw_writel(MAX_PACKET_SIZE_W_CRC, REG_DMARFC); +#endif + __raw_writel(val, REG_MCMDR); +} + +static void npcm7xx_enable_cam(struct net_device *dev) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + unsigned int val; + + npcm7xx_write_cam(dev, CAM0, dev->dev_addr); + + val = __raw_readl(REG_CAMEN); + val |= CAM0EN; + __raw_writel(val, REG_CAMEN); +} + + +static void npcm7xx_set_curdest(struct net_device *dev) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + + __raw_writel(ether->start_rx_ptr, REG_RXDLSA); + __raw_writel(ether->start_tx_ptr, REG_TXDLSA); +} + +static void npcm7xx_reset_mac(struct net_device *dev, int need_free) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + + netif_tx_lock(dev); + + ETH_DISABLE_TX; + ETH_DISABLE_RX; + + npcm7xx_return_default_idle(dev); + npcm7xx_set_fifo_threshold(dev); + + /*if (!netif_queue_stopped(dev)) + netif_stop_queue(dev);*/ + + if (need_free) + npcm7xx_free_desc(dev, false); + + npcm7xx_init_desc(dev); + + //dev->trans_start = jiffies; /* prevent tx timeout */ + ether->cur_tx = 0x0; + ether->finish_tx = 0x0; + ether->pending_tx = 0x0; + ether->cur_rx = 0x0; + ether->tx_tdu = 0; + ether->tx_tdu_i = 0; + ether->tx_cp_i = 0; + + npcm7xx_set_curdest(dev); + npcm7xx_enable_cam(dev); + npcm7xx_ether_set_multicast_list(dev); + npcm7xx_enable_mac_interrupt(dev); + npcm7xx_set_global_maccmd(dev); + ETH_ENABLE_TX; + ETH_ENABLE_RX; + + ETH_TRIGGER_RX; + + //dev->trans_start = jiffies; /* prevent tx timeout */ + + /*if (netif_queue_stopped(dev)) + netif_wake_queue(dev);*/ + + //EMC_DEBUG("REG_MCMDR = 0x%08X\n", __raw_readl(REG_MCMDR)); + + ether->need_reset = 0; + + netif_wake_queue(dev); + netif_tx_unlock(dev); +} + +static int npcm7xx_mdio_write(struct mii_bus *bus, int phy_id, int regnum, + u16 value) +{ + struct npcm7xx_ether *ether = bus->priv; + unsigned long timeout = jiffies + msecs_to_jiffies(MII_TIMEOUT * 100); + + __raw_writel(value, REG_MIID); + __raw_writel((phy_id << 0x08) | regnum | PHYBUSY | PHYWR, REG_MIIDA); + + + /* Wait for completion */ + while (__raw_readl(REG_MIIDA) & PHYBUSY) { + if (time_after(jiffies, timeout)) { + EMC_DEBUG("mdio read timed out\n" + "ether->reg = 0x%x phy_id=0x%x " + "REG_MIIDA=0x%x\n", + (unsigned int)ether->reg, phy_id + , __raw_readl(REG_MIIDA)); + return -ETIMEDOUT; + } + cpu_relax(); + } + + return 0; + +} + +static int npcm7xx_mdio_read(struct mii_bus *bus, int phy_id, int regnum) +{ + struct npcm7xx_ether *ether = bus->priv; + unsigned long timeout = jiffies + msecs_to_jiffies(MII_TIMEOUT * 100); + + + __raw_writel((phy_id << 0x08) | regnum | PHYBUSY, REG_MIIDA); + + /* Wait for completion */ + while (__raw_readl(REG_MIIDA) & PHYBUSY) { + if (time_after(jiffies, timeout)) { + EMC_DEBUG("mdio read timed out\n" + "ether->reg = 0x%x phy_id=0x%x " + "REG_MIIDA=0x%x\n", + (unsigned int)ether->reg, phy_id + , __raw_readl(REG_MIIDA)); + return -ETIMEDOUT; + } + cpu_relax(); + } + + return __raw_readl(REG_MIID); +} + +static int npcm7xx_mdio_reset(struct mii_bus *bus) +{ + + // reser ENAC engine?? + return 0; +} + +static int npcm7xx_set_mac_address(struct net_device *dev, void *addr) +{ + struct sockaddr *address = addr; + + if (!is_valid_ether_addr((u8 *)address->sa_data)) + return -EADDRNOTAVAIL; + + memcpy(dev->dev_addr, address->sa_data, dev->addr_len); + npcm7xx_write_cam(dev, CAM0, dev->dev_addr); + + return 0; +} + +static int npcm7xx_ether_close(struct net_device *dev) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + + npcm7xx_return_default_idle(dev); + + if (ether->phy_dev) + phy_stop(ether->phy_dev); + else if (ether->use_ncsi) + ncsi_stop_dev(ether->ncsidev); + + msleep(10); + + free_irq(ether->txirq, dev); + free_irq(ether->rxirq, dev); + + netif_stop_queue(dev); + napi_disable(ðer->napi); + + npcm7xx_free_desc(dev, true); + + if (ether->dump_buf) { + kfree(ether->dump_buf); + ether->dump_buf = NULL; + } + + return 0; +} + +static struct net_device_stats *npcm7xx_ether_stats(struct net_device *dev) +{ + struct npcm7xx_ether *ether; + + ether = netdev_priv(dev); + + return ðer->stats; +} + + +static int npcm7xx_clean_tx(struct net_device *dev, bool from_xmit) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + struct npcm7xx_txbd *txbd; + struct sk_buff *s; + unsigned int cur_entry, entry, sl; + + if (ether->pending_tx == 0) + return (0); + + cur_entry = __raw_readl(REG_CTXDSA); + + // Release old used buffers + entry = ether->tdesc_phys + sizeof(struct npcm7xx_txbd) * + (ether->finish_tx); + + while (entry != cur_entry) { + txbd = (ether->tdesc + ether->finish_tx); + s = ether->tx_skb[ether->finish_tx]; + if (s == NULL) + break; + + ether->count_finish++; + + dma_unmap_single(&dev->dev, txbd->buffer, s->len, + DMA_TO_DEVICE); + consume_skb(s); + ether->tx_skb[ether->finish_tx] = NULL; + + if (++ether->finish_tx >= TX_DESC_SIZE) + ether->finish_tx = 0; + ether->pending_tx--; + + sl = txbd->sl; + if (sl & TXDS_TXCP) { + ether->stats.tx_packets++; + ether->stats.tx_bytes += (sl & 0xFFFF); + } else { + ether->stats.tx_errors++; + } + + entry = ether->tdesc_phys + sizeof(struct npcm7xx_txbd) * + (ether->finish_tx); + } + + if (!from_xmit && unlikely(netif_queue_stopped(dev) && + (TX_DESC_SIZE - ether->pending_tx) > 1)) { + netif_tx_lock(dev); + if (netif_queue_stopped(dev) && + (TX_DESC_SIZE - ether->pending_tx) > 1) { + netif_wake_queue(dev); + } + netif_tx_unlock(dev); + } + + return(0); +} + +static int npcm7xx_ether_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + struct npcm7xx_txbd *txbd; + unsigned long flags; + + /* This is a hard error log it. */ + if (ether->pending_tx >= (TX_DESC_SIZE-1)) { + dev_err(ðer->pdev->dev, "%s: BUG! Tx Ring full when queue " + "awake!\n", dev->name); +#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG + npcm7xx_info_print(dev); +#endif + netif_stop_queue(dev); + return NETDEV_TX_BUSY; + } + + ether->count_xmit++; + + // Insert new buffer + + txbd = (ether->tdesc + ether->cur_tx); + + txbd->buffer = dma_map_single(&dev->dev, skb->data, + skb->len, DMA_TO_DEVICE); + + ether->tx_skb[ether->cur_tx] = skb; + + if (skb->len > MAX_PACKET_SIZE) + dev_err(ðer->pdev->dev, "skb->len (= %d) > MAX_PACKET_SIZE " + "(= %d)\n", skb->len, + MAX_PACKET_SIZE); + + txbd->sl = skb->len > MAX_PACKET_SIZE ? MAX_PACKET_SIZE : skb->len; + wmb(); + + txbd->mode = TX_OWEN_DMA | PADDINGMODE | CRCMODE; + wmb(); + + ETH_TRIGGER_TX; + + if (++ether->cur_tx >= TX_DESC_SIZE) + ether->cur_tx = 0; + spin_lock_irqsave(ðer->lock, flags); + ether->pending_tx++; + +#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG_EXT + { + static u32 last_iUsCnt1[2] = {0}; + u32 iUsCnt2[2]; + + //spin_lock_irqsave(ðer->lock, flags); + npcm7xx_clk_GetTimeStamp(iUsCnt2); + //pin_unlock_irqrestore(ðer->lock, flags); + txbd->diff = (_1MHz_ * (iUsCnt2[1] - last_iUsCnt1[1])) + + iUsCnt2[0]/25 - last_iUsCnt1[0]/25; + txbd->ts = (_1MHz_ * iUsCnt2[1]) + iUsCnt2[0]/25; + txbd->t2 = __raw_readl(REG_MISTA); + txbd->t3 = __raw_readl(REG_MIEN); + last_iUsCnt1[0] = iUsCnt2[0]; + last_iUsCnt1[1] = iUsCnt2[1]; + } +#endif + + npcm7xx_clean_tx(dev, true); + + if (ether->pending_tx >= TX_DESC_SIZE-1) { + unsigned int reg_mien; + unsigned int index_to_wake = ether->cur_tx + (TX_DESC_SIZE*3/4); + + if (index_to_wake >= TX_DESC_SIZE) + index_to_wake -= TX_DESC_SIZE; + + txbd = (ether->tdesc + index_to_wake); + txbd->mode = TX_OWEN_DMA | PADDINGMODE | CRCMODE | MACTXINTEN; + wmb(); + + __raw_writel(MISTA_TDU, REG_MISTA); // Clear TDU interrupt + reg_mien = __raw_readl(REG_MIEN); + + if (reg_mien != 0) + __raw_writel(reg_mien | ENTDU, REG_MIEN); // Enable TDU interrupt + + ether->tx_tdu++; + netif_stop_queue(dev); + } + + spin_unlock_irqrestore(ðer->lock, flags); + + return 0; +} + +static irqreturn_t npcm7xx_tx_interrupt(int irq, void *dev_id) +{ + struct npcm7xx_ether *ether; + struct platform_device *pdev; + struct net_device *dev; + unsigned int status; + unsigned long flags; + + dev = dev_id; + ether = netdev_priv(dev); + pdev = ether->pdev; + + spin_lock_irqsave(ðer->lock, flags); + npcm7xx_get_and_clear_int(dev, &status, 0xFFFF0000); + spin_unlock_irqrestore(ðer->lock, flags); + + ether->tx_int_count++; + + if (status & MISTA_EXDEF) + dev_err(&pdev->dev, "emc defer exceed interrupt status=0x%08X\n" + , status); + else if (status & MISTA_TXBERR) { + dev_err(&pdev->dev, "emc bus error interrupt status=0x%08X\n", + status); +#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG + npcm7xx_info_print(dev); +#endif + spin_lock_irqsave(ðer->lock, flags); + __raw_writel(0, REG_MIEN); // disable any interrupt + spin_unlock_irqrestore(ðer->lock, flags); + ether->need_reset = 1; + } else if (status & ~(MISTA_TXINTR | MISTA_TXCP | MISTA_TDU)) + dev_err(&pdev->dev, "emc other error interrupt status=0x%08X\n", + status); + + // if we got MISTA_TXCP | MISTA_TDU remove those interrupt and call napi + if (status & (MISTA_TXCP | MISTA_TDU) & __raw_readl(REG_MIEN)) { + unsigned int reg_mien; + + spin_lock_irqsave(ðer->lock, flags); + reg_mien = __raw_readl(REG_MIEN); + if (reg_mien & ENTDU) + __raw_writel(reg_mien & (~ENTDU), REG_MIEN); // Disable TDU interrupt + + spin_unlock_irqrestore(ðer->lock, flags); + + if (status & MISTA_TXCP) + ether->tx_cp_i++; + if (status & MISTA_TDU) + ether->tx_tdu_i++; + } else + EMC_DEBUG("status=0x%08X\n", status); + + napi_schedule(ðer->napi); + + return IRQ_HANDLED; +} + +static int npcm7xx_poll(struct napi_struct *napi, int budget) +{ + struct npcm7xx_ether *ether = + container_of(napi, struct npcm7xx_ether, napi); + struct npcm7xx_rxbd *rxbd; + struct net_device *dev = ether->ndev; + struct platform_device *pdev = ether->pdev; + struct sk_buff *skb, *s; + unsigned int length, status; + unsigned long flags; + int rx_cnt = 0; + int complete = 0; + unsigned int rx_offset = (__raw_readl(REG_CRXDSA) - + ether->start_rx_ptr)/ + sizeof(struct npcm7xx_txbd); + unsigned int local_count = (rx_offset >= ether->cur_rx) ? + rx_offset - ether->cur_rx : rx_offset + + RX_DESC_SIZE - ether->cur_rx; + + if (local_count > ether->max_waiting_rx) + ether->max_waiting_rx = local_count; + + if (local_count > (4*RX_POLL_SIZE)) + // we are porbably in a storm of short packets and we don't want to get + // into RDU since short packets in RDU cause many RXOV which may cause + // EMC halt, so we filter out all comming packets + __raw_writel(0, REG_CAMCMR); + + if (local_count <= budget) + // we can restore accepting of packets + __raw_writel(ether->camcmr, REG_CAMCMR); + + spin_lock_irqsave(ðer->lock, flags); + npcm7xx_clean_tx(dev, false); + spin_unlock_irqrestore(ðer->lock, flags); + + rxbd = (ether->rdesc + ether->cur_rx); + + while (rx_cnt < budget) { + + status = rxbd->sl; + if ((status & RX_OWEN_DMA) == RX_OWEN_DMA) { + complete = 1; + break; + } +#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG_EXT + { + static u32 last_iUsCnt1[2] = {0}; + u32 iUsCnt2[2]; + + spin_lock_irqsave(ðer->lock, flags); + npcm7xx_clk_GetTimeStamp(iUsCnt2); + spin_unlock_irqrestore(ðer->lock, flags); + rxbd->diff = ((rx_cnt+1)<<28) + + (_1MHz_ * (iUsCnt2[1] - last_iUsCnt1[1])) + + iUsCnt2[0]/25 - last_iUsCnt1[0]/25; + rxbd->ts = (_1MHz_ * iUsCnt2[1]) + iUsCnt2[0]/25; + last_iUsCnt1[0] = iUsCnt2[0]; + last_iUsCnt1[1] = iUsCnt2[1]; + } +#endif + rxbd->reserved = status; // for debug puposes we save the previous value + s = ether->rx_skb[ether->cur_rx]; + length = status & 0xFFFF; + +#ifdef VLAN_SUPPORT + if (likely((status & (RXDS_RXGD|RXDS_CRCE|RXDS_ALIE|RXDS_RP)) + == RXDS_RXGD) && likely(length <= MAX_PACKET_SIZE)) { +#else + if (likely((status & (RXDS_RXGD|RXDS_CRCE|RXDS_ALIE|RXDS_RP + |RXDS_PTLE)) == RXDS_RXGD) && + likely(length <= MAX_PACKET_SIZE)) { +#endif + dma_unmap_single(&dev->dev, (dma_addr_t)rxbd->buffer, + roundup(MAX_PACKET_SIZE_W_CRC, 4), + DMA_FROM_DEVICE); + + skb_put(s, length); + s->protocol = eth_type_trans(s, dev); + netif_receive_skb(s); + ether->stats.rx_packets++; + ether->stats.rx_bytes += length; + rx_cnt++; + ether->rx_count_pool++; + + // now we allocate new skb instead if the used one. + skb = dev_alloc_skb(roundup(MAX_PACKET_SIZE_W_CRC, 4)); + + if (!skb) { + dev_err(&pdev->dev, "get skb buffer error\n"); + ether->stats.rx_dropped++; + goto rx_out; + } + + /* Do not unmark the following skb_reserve() Receive + * Buffer Starting Address must be aligned + * to 4 bytes and the following line if unmarked + * will make it align to 2 and this likely + * will hult the RX and crash the linux + * skb_reserve(skb, NET_IP_ALIGN); + */ + skb->dev = dev; + + rxbd->buffer = dma_map_single(&dev->dev, skb->data, + roundup(MAX_PACKET_SIZE_W_CRC, 4), + DMA_FROM_DEVICE); + ether->rx_skb[ether->cur_rx] = skb; + } else { + ether->rx_err_count++; + ether->stats.rx_errors++; + EMC_DEBUG("rx_errors = %lu status = 0x%08X\n", + ether->stats.rx_errors, status); + + if (status & RXDS_RP) { + ether->stats.rx_length_errors++; + EMC_DEBUG("rx_length_errors = %lu\n", + ether->stats.rx_length_errors); + } else if (status & RXDS_CRCE) { + ether->stats.rx_crc_errors++; + EMC_DEBUG("rx_crc_errors = %lu\n", + ether->stats.rx_crc_errors); + } else if (status & RXDS_ALIE) { + ether->stats.rx_frame_errors++; + EMC_DEBUG("rx_frame_errors = %lu\n", + ether->stats.rx_frame_errors); + } +#ifndef VLAN_SUPPORT + else if (status & RXDS_PTLE) { + ether->stats.rx_length_errors++; + EMC_DEBUG("rx_length_errors = %lu\n", + ether->stats.rx_length_errors); + } +#endif + else if (length > MAX_PACKET_SIZE) { + ether->stats.rx_length_errors++; + EMC_DEBUG("rx_length_errors = %lu\n", + ether->stats.rx_length_errors); + } + } + + wmb(); + rxbd->sl = RX_OWEN_DMA; + wmb(); + + if (++ether->cur_rx >= RX_DESC_SIZE) + ether->cur_rx = 0; + + rxbd = (ether->rdesc + ether->cur_rx); + + } + + if (complete) { + napi_complete(napi); + + if (ether->need_reset) { + EMC_DEBUG("Reset\n"); + npcm7xx_reset_mac(dev, 1); + } + + spin_lock_irqsave(ðer->lock, flags); + __raw_writel(__raw_readl(REG_MIEN) | ENRXGD, REG_MIEN); + spin_unlock_irqrestore(ðer->lock, flags); + } else { + rx_offset = (__raw_readl(REG_CRXDSA)-ether->start_rx_ptr)/ + sizeof(struct npcm7xx_txbd); + local_count = (rx_offset >= ether->cur_rx) ? rx_offset - + ether->cur_rx : rx_offset + RX_DESC_SIZE - + ether->cur_rx; + + if (local_count > ether->max_waiting_rx) + ether->max_waiting_rx = local_count; + + if (local_count > (3*RX_POLL_SIZE)) + // we are porbably in a storm of short packets and we don't want to get + // into RDU since short packets in RDU cause many RXOV which may cause + // EMC halt, so we filter out all comming packets + __raw_writel(0, REG_CAMCMR); + if (local_count <= RX_POLL_SIZE) + // we can restore accepting of packets + __raw_writel(ether->camcmr, REG_CAMCMR); + } +rx_out: + + ETH_TRIGGER_RX; + return rx_cnt; +} + +static irqreturn_t npcm7xx_rx_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct npcm7xx_ether *ether = netdev_priv(dev); + struct platform_device *pdev = ether->pdev; + unsigned int status; + unsigned long flags; + unsigned int any_err = 0; + u32 RXFSM; + + spin_lock_irqsave(ðer->lock, flags); + npcm7xx_get_and_clear_int(dev, &status, 0xFFFF); + spin_unlock_irqrestore(ðer->lock, flags); + + ether->rx_int_count++; + + + if (unlikely(status & MISTA_RXBERR)) { + ether->rx_berr++; + dev_err(&pdev->dev, "emc rx bus error status=0x%08X\n", status); +#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG + npcm7xx_info_print(dev); +#endif + spin_lock_irqsave(ðer->lock, flags); + __raw_writel(0, REG_MIEN); // disable any interrupt + spin_unlock_irqrestore(ðer->lock, flags); + ether->need_reset = 1; + napi_schedule(ðer->napi); + return IRQ_HANDLED; + } + + if (unlikely(status & (MISTA_RXOV | MISTA_RDU))) { + // filter out all received packets until we have enough avaiable buffer descriptors + __raw_writel(0, REG_CAMCMR); + any_err = 1; + if (status & (MISTA_RXOV)) + ether->rxov++; + if (status & (MISTA_RDU)) + ether->rdu++; + + // workaround Errata 1.36: EMC Hangs on receiving 253-256 byte packet + RXFSM = __raw_readl(REG_RXFSM); + + if ((RXFSM & 0xFFFFF000) == 0x08044000) { + int i; + for (i = 0; i < 32; i++) { + RXFSM = __raw_readl(REG_RXFSM); + if ((RXFSM & 0xFFFFF000) != 0x08044000) + break; + } + if (i == 32) { + ether->rx_stuck++; + spin_lock_irqsave(ðer->lock, flags); +#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG + npcm7xx_info_print(dev); +#endif + __raw_writel(0, REG_MIEN); + spin_unlock_irqrestore(ðer->lock, flags); + ether->need_reset = 1; + napi_schedule(ðer->napi); + dev_err(&pdev->dev, "stuck on REG_RXFSM = " + "0x%08X status=%08X doing " + "reset!\n", RXFSM, status); + return IRQ_HANDLED; + } + } + } + + // echo MISTA status on unexpected flags although we don't do anithing with them + if (unlikely(status & ( + // MISTA_RXINTR | // Receive - all RX interrupt set this + MISTA_CRCE | // CRC Error + // MISTA_RXOV | // Receive FIFO Overflow - we alread handled it +#ifndef VLAN_SUPPORT + MISTA_PTLE | // Packet Too Long is needed since VLAN is not supported +#endif + // MISTA_RXGD | // Receive Good - this is the common good case + MISTA_ALIE | // Alignment Error + MISTA_RP | // Runt Packet + MISTA_MMP | // More Missed Packet + MISTA_DFOI | // Maximum Frame Length + // MISTA_DENI | // DMA Early Notification - every packet get this + // MISTA_RDU | // Receive Descriptor Unavailable + // MISTA_RXBERR | // Receive Bus Error Interrupt - we alread handled it + // MISTA_CFR | // Control Frame Receive - not an error + 0))) { + EMC_DEBUG("emc rx MISTA status=0x%08X\n", status); + any_err = 1; + ether->rx_err++; + } + + if ((any_err == 0) && ((status & MISTA_RXGD) == 0)) + dev_err(&pdev->dev, "emc rx MISTA status=0x%08X\n", status); + + spin_lock_irqsave(ðer->lock, flags); +#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG_EXT + { + struct npcm7xx_rxbd *rxbd = (ether->rdesc + ether->cur_rx); + static u32 last_iUsCnt1[2] = {0}; + u32 iUsCnt2[2]; + + npcm7xx_clk_GetTimeStamp(iUsCnt2); + /* rxbd->r2 = (_1MHz_ * (iUsCnt2[1] - last_iUsCnt1[1])) + + *iUsCnt2[0]/25 - last_iUsCnt1[0]/25; + */ + rxbd->r2 = status; + rxbd->r3 = (_1MHz_ * iUsCnt2[1]) + iUsCnt2[0]/25; + last_iUsCnt1[0] = iUsCnt2[0]; + last_iUsCnt1[1] = iUsCnt2[1]; + } +#endif + __raw_writel(__raw_readl(REG_MIEN) & ~ENRXGD, REG_MIEN); + spin_unlock_irqrestore(ðer->lock, flags); + napi_schedule(ðer->napi); + + return IRQ_HANDLED; +} + + +static int npcm7xx_ether_open(struct net_device *dev) +{ + struct npcm7xx_ether *ether; + struct platform_device *pdev; + + ether = netdev_priv(dev); + pdev = ether->pdev; + + if (ether->use_ncsi) + { + ether->speed = 100; + ether->duplex = DUPLEX_FULL; + npcm7xx_opmode(dev, 100, DUPLEX_FULL); + } + npcm7xx_reset_mac(dev, 0); + + if (request_irq(ether->txirq, npcm7xx_tx_interrupt, + 0x0, pdev->name, dev)) { + dev_err(&pdev->dev, "register irq tx failed\n"); + npcm7xx_ether_close(dev); + return -EAGAIN; + } + + if (request_irq(ether->rxirq, npcm7xx_rx_interrupt, + 0x0, pdev->name, dev)) { + dev_err(&pdev->dev, "register irq rx failed\n"); + npcm7xx_ether_close(dev); + return -EAGAIN; + } + + if (ether->phy_dev) + phy_start(ether->phy_dev); + else if (ether->use_ncsi) + netif_carrier_on(dev); + + netif_start_queue(dev); + napi_enable(ðer->napi); + + ETH_TRIGGER_RX; + + /* Start the NCSI device */ + if (ether->use_ncsi) { + int err = ncsi_start_dev(ether->ncsidev); + if (err) + { + npcm7xx_ether_close(dev); + return err; + } + } + + dev_info(&pdev->dev, "%s is OPENED\n", dev->name); + + return 0; +} + +static void npcm7xx_ether_set_multicast_list(struct net_device *dev) +{ + struct npcm7xx_ether *ether; + unsigned int rx_mode; + + ether = netdev_priv(dev); + + EMC_DEBUG("%s CAMCMR_AUP\n", (dev->flags & IFF_PROMISC) ? + "Set" : "Clear"); + if (dev->flags & IFF_PROMISC) + rx_mode = CAMCMR_AUP | CAMCMR_AMP | CAMCMR_ABP | CAMCMR_ECMP; + else if ((dev->flags & IFF_ALLMULTI) || !netdev_mc_empty(dev)) + rx_mode = CAMCMR_AMP | CAMCMR_ABP | CAMCMR_ECMP; + else + rx_mode = CAMCMR_ECMP | CAMCMR_ABP; + __raw_writel(rx_mode, REG_CAMCMR); + ether->camcmr = rx_mode; +} + +static int npcm7xx_ether_ioctl(struct net_device *dev, + struct ifreq *ifr, int cmd) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + struct phy_device *phydev = ether->phy_dev; + + if (!netif_running(dev)) + return -EINVAL; + + if (!phydev) + return -ENODEV; + + return phy_mii_ioctl(phydev, ifr, cmd); +} + +static void npcm7xx_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version)); + strlcpy(info->fw_version, "N/A", sizeof(info->fw_version)); + strlcpy(info->bus_info, "N/A", sizeof(info->bus_info)); +} + +static int npcm7xx_get_settings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + struct phy_device *phydev = ether->phy_dev; + + if (phydev == NULL) + return -ENODEV; + + pr_info("\n\nnpcm7xx_get_settings\n"); + phy_ethtool_ksettings_get(phydev, cmd); + + return 0; +} + +static int npcm7xx_set_settings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + struct phy_device *phydev = ether->phy_dev; + int ret; + unsigned long flags; + + if (phydev == NULL) + return -ENODEV; + + pr_info("\n\nnpcm7xx_set_settings\n"); + spin_lock_irqsave(ðer->lock, flags); + ret = phy_ethtool_ksettings_set(phydev, cmd); + spin_unlock_irqrestore(ðer->lock, flags); + + return ret; +} + +static u32 npcm7xx_get_msglevel(struct net_device *dev) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + + return ether->msg_enable; +} + +static void npcm7xx_set_msglevel(struct net_device *dev, u32 level) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + + ether->msg_enable = level; +} + +static const struct ethtool_ops npcm7xx_ether_ethtool_ops = { + .get_link_ksettings = npcm7xx_get_settings, + .set_link_ksettings = npcm7xx_set_settings, + .get_drvinfo = npcm7xx_get_drvinfo, + .get_msglevel = npcm7xx_get_msglevel, + .set_msglevel = npcm7xx_set_msglevel, + .get_link = ethtool_op_get_link, +}; + +static const struct net_device_ops npcm7xx_ether_netdev_ops = { + .ndo_open = npcm7xx_ether_open, + .ndo_stop = npcm7xx_ether_close, + .ndo_start_xmit = npcm7xx_ether_start_xmit, + .ndo_get_stats = npcm7xx_ether_stats, + .ndo_set_rx_mode = npcm7xx_ether_set_multicast_list, + .ndo_set_mac_address = npcm7xx_set_mac_address, + .ndo_do_ioctl = npcm7xx_ether_ioctl, + .ndo_validate_addr = eth_validate_addr, + .ndo_change_mtu = eth_change_mtu, +}; + +#ifndef CONFIG_OF +static unsigned char char2hex(unsigned char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + + return 0; +} + +static void _mac_setup(char *line, u8 *mac) +{ + int i; + + for (i = 0; i < 6; i++) + mac[i] = (char2hex(line[i*3])<<4) + char2hex(line[i*3+1]); +} + +static int find_option(char *str, char *mac) +{ + extern char *saved_command_line; + char *p; + + p = strstr(saved_command_line, str) + + if (!p) + return 0; + + p += strlen(str); + _mac_setup(p, mac); + + return 1; +} +#endif + +static void get_mac_address(struct net_device *dev) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + struct platform_device *pdev = ether->pdev; + struct device_node *np = ether->pdev->dev.of_node; + const u8 *mac_address = NULL; +#ifndef CONFIG_OF + struct plat_npcm7xx_emc_data *plat_dat = pdev->dev.platform_data; +#endif + +#ifdef CONFIG_OF + mac_address = of_get_mac_address(np); + + if (mac_address != 0) + ether_addr_copy(dev->dev_addr, mac_address); +#else + if (find_option("basemac=", mac_address)) { + memcpy(dev->dev_addr, mac_address, ETH_ALEN); + if (pdev->id == 1) + dev->dev_addr[5] += 1; + } else + memcpy(dev->dev_addr, (const void *)plat_dat->mac_addr, + ETH_ALEN); +#endif + + if (is_valid_ether_addr(dev->dev_addr)) { + pr_info("%s: device MAC address : %pM\n", pdev->name, + dev->dev_addr); + } else { + eth_hw_addr_random(dev); + pr_info("%s: device MAC address (random generator) %pM\n", + dev->name, dev->dev_addr); + } + +} + + +static int npcm7xx_mii_setup(struct net_device *dev) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + struct platform_device *pdev; + struct phy_device *phydev = NULL; + int i, err = 0; + + pdev = ether->pdev; + + ether->mii_bus = mdiobus_alloc(); + if (!ether->mii_bus) { + err = -ENOMEM; + dev_err(&pdev->dev, "mdiobus_alloc() failed\n"); + goto out0; + } + + ether->mii_bus->name = "npcm7xx_rmii"; + ether->mii_bus->read = &npcm7xx_mdio_read; + ether->mii_bus->write = &npcm7xx_mdio_write; + ether->mii_bus->reset = &npcm7xx_mdio_reset; + snprintf(ether->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", + ether->pdev->name, ether->pdev->id); + EMC_DEBUG("%s ether->mii_bus->id=%s\n", __func__, ether->mii_bus->id); + ether->mii_bus->priv = ether; + ether->mii_bus->parent = ðer->pdev->dev; + + for (i = 0; i < PHY_MAX_ADDR; i++) + ether->mii_bus->irq[i] = PHY_POLL; + + platform_set_drvdata(ether->pdev, ether->mii_bus); + + /* Enable MDIO Clock */ + __raw_writel(__raw_readl(REG_MCMDR) | MCMDR_ENMDC, REG_MCMDR); + + + if (mdiobus_register(ether->mii_bus)) { + dev_err(&pdev->dev, "mdiobus_register() failed\n"); + goto out2; + } + + + phydev = phy_find_first(ether->mii_bus); + if (phydev == NULL) { + dev_err(&pdev->dev, "phy_find_first() failed\n"); + goto out3; + } + + dev_info(&pdev->dev, " name = %s ETH-Phy-Id = 0x%x\n", + phydev_name(phydev), phydev->phy_id); + + phydev = phy_connect(dev, phydev_name(phydev), + &adjust_link, + PHY_INTERFACE_MODE_RMII); + + dev_info(&pdev->dev, " ETH-Phy-Id = 0x%x name = %s\n", + phydev->phy_id, phydev->drv->name); + + if (IS_ERR(phydev)) { + err = PTR_ERR(phydev); + dev_err(&pdev->dev, "phy_connect() failed - %d\n", err); + goto out3; + } + + phydev->supported &= PHY_BASIC_FEATURES; + phydev->advertising = phydev->supported; + ether->phy_dev = phydev; + + return 0; + +out3: + mdiobus_unregister(ether->mii_bus); +out2: + kfree(ether->mii_bus->irq); + mdiobus_free(ether->mii_bus); +out0: + + return err; +} + +#include +#include + +#define PROC_FILENAME "driver/npcm7xx_emc" + +#define REG_PRINT(reg_name) {t = scnprintf(next, size, "%-10s = %08X\n", \ + #reg_name, __raw_readl(reg_name)); size -= t; next += t; } +#define PROC_PRINT(f, x...) {t = scnprintf(next, size, f, ## x); size -= t; \ + next += t; } + +static int npcm7xx_info_dump(char *buf, int count, struct net_device *dev) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + struct npcm7xx_txbd *txbd; + struct npcm7xx_rxbd *rxbd; + unsigned long flags; + unsigned int i, cur, txd_offset, rxd_offset; + char *next = buf; + unsigned int size = count; + int t; + int is_locked = spin_is_locked(ðer->lock); + + if (!is_locked) + spin_lock_irqsave(ðer->lock, flags); + + /* ------basic driver information ---- */ + PROC_PRINT("NPCM7XX EMC %s driver version: %s\n", dev->name, + DRV_MODULE_VERSION); + + REG_PRINT(REG_CAMCMR); + REG_PRINT(REG_CAMEN); + REG_PRINT(REG_CAMM_BASE); + REG_PRINT(REG_CAML_BASE); + REG_PRINT(REG_TXDLSA); + REG_PRINT(REG_RXDLSA); + REG_PRINT(REG_MCMDR); + REG_PRINT(REG_MIID); + REG_PRINT(REG_MIIDA); + REG_PRINT(REG_FFTCR); + REG_PRINT(REG_TSDR); + REG_PRINT(REG_RSDR); + REG_PRINT(REG_DMARFC); + REG_PRINT(REG_MIEN); + REG_PRINT(REG_MISTA); + REG_PRINT(REG_MGSTA); + REG_PRINT(REG_MPCNT); + __raw_writel(0x7FFF, REG_MPCNT); + REG_PRINT(REG_MRPC); + REG_PRINT(REG_MRPCC); + REG_PRINT(REG_MREPC); + REG_PRINT(REG_DMARFS); + REG_PRINT(REG_CTXDSA); + REG_PRINT(REG_CTXBSA); + REG_PRINT(REG_CRXDSA); + REG_PRINT(REG_CRXBSA); + REG_PRINT(REG_RXFSM); + REG_PRINT(REG_TXFSM); + REG_PRINT(REG_FSM0); + REG_PRINT(REG_FSM1); + REG_PRINT(REG_DCR); + REG_PRINT(REG_DMMIR); + REG_PRINT(REG_BISTR); + PROC_PRINT("\n"); + + PROC_PRINT("netif_queue %s\n\n", netif_queue_stopped(dev) ? + "Stopped" : "Running"); + if (ether->rdesc) + PROC_PRINT("napi is %s\n\n", test_bit(NAPI_STATE_SCHED, + ðer->napi.state) ? + "scheduled" : + "not scheduled"); + + txd_offset = (__raw_readl(REG_CTXDSA) - + __raw_readl(REG_TXDLSA))/sizeof(struct npcm7xx_txbd); + PROC_PRINT("TXD offset %6d\n", txd_offset); + PROC_PRINT("cur_tx %6d\n", ether->cur_tx); + PROC_PRINT("finish_tx %6d\n", ether->finish_tx); + PROC_PRINT("pending_tx %6d\n", ether->pending_tx); + /* debug counters */ + PROC_PRINT("tx_tdu %6d\n", ether->tx_tdu); + ether->tx_tdu = 0; + PROC_PRINT("tx_tdu_i %6d\n", ether->tx_tdu_i); + ether->tx_tdu_i = 0; + PROC_PRINT("tx_cp_i %6d\n", ether->tx_cp_i); + ether->tx_cp_i = 0; + PROC_PRINT("tx_int_count %6d\n", ether->tx_int_count); + ether->tx_int_count = 0; + PROC_PRINT("count_xmit tx %6d\n", ether->count_xmit); + ether->count_xmit = 0; + PROC_PRINT("count_finish %6d\n", ether->count_finish); + ether->count_finish = 0; + PROC_PRINT("\n"); + + rxd_offset = (__raw_readl(REG_CRXDSA)-__raw_readl(REG_RXDLSA)) + /sizeof(struct npcm7xx_txbd); + PROC_PRINT("RXD offset %6d\n", rxd_offset); + PROC_PRINT("cur_rx %6d\n", ether->cur_rx); + PROC_PRINT("rx_err %6d\n", ether->rx_err); + ether->rx_err = 0; + PROC_PRINT("rx_berr %6d\n", ether->rx_berr); + ether->rx_berr = 0; + PROC_PRINT("rx_stuck %6d\n", ether->rx_stuck); + ether->rx_stuck = 0; + PROC_PRINT("rdu %6d\n", ether->rdu); + ether->rdu = 0; + PROC_PRINT("rxov rx %6d\n", ether->rxov); + ether->rxov = 0; + // debug counters + PROC_PRINT("rx_int_count %6d\n", ether->rx_int_count); + ether->rx_int_count = 0; + PROC_PRINT("rx_err_count %6d\n", ether->rx_err_count); + ether->rx_err_count = 0; + PROC_PRINT("rx_count_pool %6d\n", ether->rx_count_pool); + ether->rx_count_pool = 0; + PROC_PRINT("max_waiting_rx %5d\n", ether->max_waiting_rx); + ether->max_waiting_rx = 0; + PROC_PRINT("\n"); + PROC_PRINT("need_reset %5d\n", ether->need_reset); + + if (ether->tdesc && ether->rdesc) { + cur = ether->finish_tx - 2; + for (i = 0; i < 3; i++) { + cur = (cur + 1)%TX_DESC_SIZE; + txbd = (ether->tdesc + cur); + PROC_PRINT("finish %3d txbd mode %08X buffer %08X sl " + "%08X next %08X tx_skb %p\n", cur, + txbd->mode, txbd->buffer, txbd->sl, + txbd->next, ether->tx_skb[cur]); + } + PROC_PRINT("\n"); + + cur = txd_offset - 2; + for (i = 0; i < 3; i++) { + cur = (cur + 1)%TX_DESC_SIZE; + txbd = (ether->tdesc + cur); + PROC_PRINT("txd_of %3d txbd mode %08X buffer %08X sl " + "%08X next %08X\n", cur, txbd->mode, + txbd->buffer, txbd->sl, txbd->next); + } + PROC_PRINT("\n"); + + cur = ether->cur_tx - 63; + for (i = 0; i < 64; i++) { + cur = (cur + 1)%TX_DESC_SIZE; + txbd = (ether->tdesc + cur); + PROC_PRINT("cur_tx %3d txbd mode %08X buffer %08X sl " + "%08X next %08X ", cur, txbd->mode, + txbd->buffer, txbd->sl, txbd->next); +#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG_EXT + PROC_PRINT("diff %08X ts %08X MISTA %08X MIEN %08X\n", + txbd->diff, txbd->ts, txbd->t2, txbd->t3); +#else + PROC_PRINT("\n"); +#endif + } + PROC_PRINT("\n"); + + cur = ether->cur_rx - 63; + for (i = 0; i < 64; i++) { + cur = (cur + 1)%RX_DESC_SIZE; + rxbd = (ether->rdesc + cur); + PROC_PRINT("cur_rx %3d rxbd sl %08X buffer %08X sl " + "%08X next %08X ", cur, rxbd->sl, + rxbd->buffer, rxbd->reserved, rxbd->next); +#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG_EXT + PROC_PRINT("diff %08X ts %08X i_diff %08X i_ts %08X\n", + rxbd->diff, rxbd->ts, rxbd->r2, rxbd->r3); +#else + PROC_PRINT("\n"); +#endif + } + PROC_PRINT("\n"); + + cur = rxd_offset - 2; + for (i = 0; i < 3; i++) { + cur = (cur + 1)%RX_DESC_SIZE; + rxbd = (ether->rdesc + cur); + PROC_PRINT("rxd_of %3d rxbd sl %08X buffer %08X sl %08X" + " next %08X\n", cur, rxbd->sl, rxbd->buffer, + rxbd->reserved, rxbd->next); + } + PROC_PRINT("\n"); + } + + if (!is_locked) + spin_unlock_irqrestore(ðer->lock, flags); + + return count - size; +} + +#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG +static void npcm7xx_info_print(struct net_device *dev) +{ + char *emc_dump_buf; + int count; + struct npcm7xx_ether *ether; + struct platform_device *pdev; + const size_t print_size = 5*PAGE_SIZE; + + ether = netdev_priv(dev); + pdev = ether->pdev; + + emc_dump_buf = kmalloc(print_size, GFP_KERNEL); + if (!emc_dump_buf) + dev_err(&pdev->dev, "emc_dump_buf = kmalloc(PAGE_SIZE, " + "GFP_KERNEL) failed\n"); + else { + char c; + char *tmp_buf = emc_dump_buf; + + count = npcm7xx_info_dump(emc_dump_buf, print_size, dev); + while (count > 512) { + c = tmp_buf[512]; + tmp_buf[512] = 0; + pr_info("%s", tmp_buf); + tmp_buf += 512; + tmp_buf[0] = c; + count -= 512; + } + printk("%s", tmp_buf); + kfree(emc_dump_buf); + } +} +#endif + +static int npcm7xx_proc_read(struct seq_file *sf, void *v) +{ + struct net_device *dev = (struct net_device *)sf->private; + struct npcm7xx_ether *ether = netdev_priv(dev); + const size_t print_size = 5*PAGE_SIZE; + + if (ether->dump_buf == NULL) { + ether->dump_buf = kmalloc(print_size, GFP_KERNEL); + if (!ether->dump_buf) + return -1; + npcm7xx_info_dump(ether->dump_buf, print_size, dev); + } + + seq_printf(sf, "%s", ether->dump_buf); + + if (sf->count < sf->size) { + kfree(ether->dump_buf); + ether->dump_buf = NULL; + } + + return 0; +} + +static int npcm7xx_ether_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, npcm7xx_proc_read, PDE_DATA(inode)); +} + +static const struct file_operations npcm7xx_ether_proc_fops = { + .open = npcm7xx_ether_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int npcm7xx_proc_reset(struct seq_file *sf, void *v) +{ + struct net_device *dev = (struct net_device *)sf->private; + struct npcm7xx_ether *ether = netdev_priv(dev); + unsigned long flags; + + seq_printf(sf, "Ask to reset the module\n"); + spin_lock_irqsave(ðer->lock, flags); + __raw_writel(0, REG_MIEN); + spin_unlock_irqrestore(ðer->lock, flags); + ether->need_reset = 1; + napi_schedule(ðer->napi); + + return 0; +} + +static int npcm7xx_ether_proc_reset(struct inode *inode, struct file *file) +{ + return single_open(file, npcm7xx_proc_reset, PDE_DATA(inode)); +} + +static const struct file_operations npcm7xx_ether_proc_fops_reset = { + .open = npcm7xx_ether_proc_reset, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static const struct of_device_id emc_dt_id[] = { + { .compatible = "nuvoton,npcm750-emc", }, + {}, +}; +MODULE_DEVICE_TABLE(of, emc_dt_id); + + +static void npcm7xx_ncsi_handler(struct ncsi_dev *nd) +{ + if (unlikely(nd->state != ncsi_dev_state_functional)) + return; + + netdev_info(nd->dev, "NCSI interface %s\n", + nd->link_up ? "up" : "down"); +} + +static int npcm7xx_ether_probe(struct platform_device *pdev) +{ + struct npcm7xx_ether *ether; + struct net_device *dev; + int error; + char proc_filename[32]; + + +#ifdef CONFIG_OF + struct clk *emc_clk = NULL; + const struct of_device_id *of_id; + struct device_node *np = pdev->dev.of_node; + + pdev->id = of_alias_get_id(np, "ethernet"); + if (pdev->id < 0) + pdev->id = 0; + + emc_clk = devm_clk_get(&pdev->dev, NULL); + + if (IS_ERR(emc_clk)) + return PTR_ERR(emc_clk); + + /* Enable Clock */ + clk_prepare_enable(emc_clk); +#endif + + + /* disable for now - need to check if necessary */ + gcr_regmap = syscon_regmap_lookup_by_compatible("nuvoton,npcm750-gcr"); + if (IS_ERR(gcr_regmap)) { + pr_err("%s: failed to find nuvoton,npcm750-gcr\n", __func__); + return IS_ERR(gcr_regmap); + } + + rst_regmap = syscon_regmap_lookup_by_compatible("nuvoton,npcm750-rst"); + if (IS_ERR(rst_regmap)) { + pr_err("%s: failed to find nuvoton,npcm750-rst\n", __func__); + return IS_ERR(rst_regmap); + } + + /* Muxing RMII MDIO */ + if (pdev->id == 0) { + regmap_update_bits(gcr_regmap, MFSEL3_OFFSET, (0x1 << 9), + (0x1 << 9)); + regmap_update_bits(gcr_regmap, MFSEL1_OFFSET, (0x1 << 13), + (0x1 << 13)); + regmap_update_bits(gcr_regmap, MFSEL1_OFFSET, (0x1 << 12), + (0x1 << 12)); + regmap_update_bits(gcr_regmap, INTCR_OFFSET, (0x1 << 5), + (0x1 << 5)); + } + if (pdev->id == 1) { + regmap_update_bits(gcr_regmap, MFSEL1_OFFSET, (0x1 << 14), + (0x1 << 14)); + regmap_update_bits(gcr_regmap, MFSEL1_OFFSET, (0x1 << 16), + (0x1 << 16)); + regmap_update_bits(gcr_regmap, MFSEL1_OFFSET, (0x1 << 15), + (0x1 << 15)); + } + + /* Reset EMC module */ + if (pdev->id == 0) { + regmap_update_bits(rst_regmap, IPSRST1_OFFSET, (0x1 << 6), + (0x1 << 6)); + regmap_update_bits(rst_regmap, IPSRST1_OFFSET, (0x1 << 6), 0); + } + if (pdev->id == 1) { + regmap_update_bits(rst_regmap, IPSRST1_OFFSET, (0x1 << 21), + (0x1 << 21)); + regmap_update_bits(rst_regmap, IPSRST1_OFFSET, (0x1 << 21), 0); + } + + #ifdef CONFIG_OF + of_id = of_match_device(emc_dt_id, &pdev->dev); + if (!of_id) { + dev_err(&pdev->dev, "Error: No device match found\n"); + return -ENODEV; + } + /* + * Right now device-tree probed devices don't get dma_mask set. + * Since shared usb code relies on it, set it here for now. + * Once we have dma capability bindings this can go away. + */ + error = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (error) + return -ENODEV; + #endif + + dev = alloc_etherdev(sizeof(struct npcm7xx_ether)); + if (!dev) + return -ENOMEM; + + snprintf(dev->name, IFNAMSIZ, "eth%d", pdev->id); + + ether = netdev_priv(dev); + + ether->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (ether->res == NULL) { + dev_err(&pdev->dev, "failed to get I/O memory\n"); + error = -ENXIO; + goto failed_free; + } + + if (!request_mem_region(ether->res->start, + resource_size(ether->res), pdev->name)) { + dev_err(&pdev->dev, "failed to request I/O memory\n"); + error = -EBUSY; + goto failed_free; + } + + + ether->reg = ioremap(ether->res->start, resource_size(ether->res)); + EMC_DEBUG(" ether->reg = 0x%x\n", __func__, (unsigned int)ether->reg); + + if (ether->reg == NULL) { + dev_err(&pdev->dev, "failed to remap I/O memory\n"); + error = -ENXIO; + goto failed_free_mem; + } + + ether->txirq = platform_get_irq(pdev, 0); + if (ether->txirq < 0) { + dev_err(&pdev->dev, "failed to get ether tx irq\n"); + error = -ENXIO; + goto failed_free_io; + } + + ether->rxirq = platform_get_irq(pdev, 1); + if (ether->rxirq < 0) { + dev_err(&pdev->dev, "failed to get ether rx irq\n"); + error = -ENXIO; + goto failed_free_io; + } + + SET_NETDEV_DEV(dev, &pdev->dev); + platform_set_drvdata(pdev, dev); + ether->ndev = dev; + + ether->pdev = pdev; + ether->msg_enable = NETIF_MSG_LINK; + + dev->netdev_ops = &npcm7xx_ether_netdev_ops; + dev->ethtool_ops = &npcm7xx_ether_ethtool_ops; + + dev->tx_queue_len = TX_DESC_SIZE; + dev->dma = 0x0; + dev->watchdog_timeo = TX_TIMEOUT; + + get_mac_address(dev); + + ether->cur_tx = 0x0; + ether->cur_rx = 0x0; + ether->finish_tx = 0x0; + ether->pending_tx = 0x0; + ether->link = 0; + ether->speed = 100; + ether->duplex = DUPLEX_FULL; + ether->need_reset = 0; + ether->dump_buf = NULL; + ether->rx_berr = 0; + ether->rx_err = 0; + ether->rdu = 0; + ether->rxov = 0; + ether->rx_stuck = 0; + // debug counters + ether->max_waiting_rx = 0; + ether->rx_count_pool = 0; + ether->count_xmit = 0; + ether->rx_int_count = 0; + ether->rx_err_count = 0; + ether->tx_int_count = 0; + ether->count_finish = 0; + ether->tx_tdu = 0; + ether->tx_tdu_i = 0; + ether->tx_cp_i = 0; + + spin_lock_init(ðer->lock); + + netif_napi_add(dev, ðer->napi, npcm7xx_poll, RX_POLL_SIZE); + + ether_setup(dev); + if (pdev->dev.of_node && + of_get_property(pdev->dev.of_node, "use-ncsi", NULL)) { + if (!IS_ENABLED(CONFIG_NET_NCSI)) { + dev_err(&pdev->dev, "CONFIG_NET_NCSI not enabled\n"); + error = -ENODEV; + goto failed_free_napi; + } + + dev_info(&pdev->dev, "Using NCSI interface\n"); + ether->use_ncsi = true; + ether->ncsidev= ncsi_register_dev(dev, npcm7xx_ncsi_handler); + if (!ether->ncsidev){ + error = -ENODEV; + goto failed_free_napi; + } + } else { + ether->use_ncsi = false; + error = npcm7xx_mii_setup(dev); + if (error < 0) { + dev_err(&pdev->dev, "npcm7xx_mii_setup err\n"); + goto failed_free_napi; + } + } + + error = register_netdev(dev); + if (error != 0) { + dev_err(&pdev->dev, "register_netdev() failed\n"); + error = -ENODEV; + goto failed_free_napi; + } + snprintf(proc_filename, sizeof(proc_filename), "%s.%d", PROC_FILENAME, + pdev->id); + proc_create_data(proc_filename, 0000, NULL, &npcm7xx_ether_proc_fops, + dev); + + snprintf(proc_filename, sizeof(proc_filename), "%s.%d.reset", + PROC_FILENAME, pdev->id); + proc_create_data(proc_filename, 0000, NULL, + &npcm7xx_ether_proc_fops_reset, dev); + + return 0; + +failed_free_napi: + netif_napi_del(ðer->napi); + platform_set_drvdata(pdev, NULL); +failed_free_io: + iounmap(ether->reg); +failed_free_mem: + release_mem_region(ether->res->start, resource_size(ether->res)); +failed_free: + free_netdev(dev); + + return error; +} + +static int npcm7xx_ether_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct npcm7xx_ether *ether = netdev_priv(dev); + char proc_filename[32]; + + snprintf(proc_filename, sizeof(proc_filename), "%s.%d", PROC_FILENAME, + pdev->id); + remove_proc_entry(proc_filename, NULL); + snprintf(proc_filename, sizeof(proc_filename), "%s.%d.reset", + PROC_FILENAME, pdev->id); + remove_proc_entry(proc_filename, NULL); + + unregister_netdev(dev); + + + free_irq(ether->txirq, dev); + free_irq(ether->rxirq, dev); + + if (ether->phy_dev) + phy_disconnect(ether->phy_dev); + + mdiobus_unregister(ether->mii_bus); + kfree(ether->mii_bus->irq); + mdiobus_free(ether->mii_bus); + + platform_set_drvdata(pdev, NULL); + + free_netdev(dev); + return 0; +} + +static struct platform_driver npcm7xx_ether_driver = { + .probe = npcm7xx_ether_probe, + .remove = npcm7xx_ether_remove, + .driver = { + .name = DRV_MODULE_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(emc_dt_id), + }, +}; + +#ifdef CONFIG_OF +module_platform_driver(npcm7xx_ether_driver); +#else +static int __init npcm7xx_ether_init(void) +{ + + return platform_driver_register(&npcm7xx_ether_driver); +} + +static void __exit npcm7xx_ether_exit(void) +{ + platform_driver_unregister(&npcm7xx_ether_driver); +} + +module_init(npcm7xx_ether_init); +module_exit(npcm7xx_ether_exit); +#endif + +MODULE_AUTHOR("Nuvoton Technology Corp."); +MODULE_DESCRIPTION("NPCM750 EMC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:npcm750-emc"); +MODULE_VERSION(DRV_MODULE_VERSION); From 8b2a69e91f7a71239bafe7ab1328783b1872ddac Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Thu, 4 Jan 2018 12:41:52 +0200 Subject: [PATCH 22/37] dt-binding: i2c: document NPCM7xx I2C bus DT bindings Added device tree binding documentation for Nuvoton NPCM7xx I2C bus. Signed-off-by: Tomer Maimon --- .../devicetree/bindings/i2c/i2c-npcm7xx.txt | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/i2c-npcm7xx.txt diff --git a/Documentation/devicetree/bindings/i2c/i2c-npcm7xx.txt b/Documentation/devicetree/bindings/i2c/i2c-npcm7xx.txt new file mode 100644 index 00000000000000..513584d33cab65 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-npcm7xx.txt @@ -0,0 +1,23 @@ +Nuvoton NPCM7XX I2C bus + +The NPCM750x includes sixteen I2C busses + +Required properties: +- compatible : "nuvoton,npcm750-i2c-bus" for Poleg NPCM750. +- reg : Offset and length of the register set for the device. +- interrupts : Contain the I2C interrupt with flags for falling edge. +- clocks : phandle of I2C reference clock. + +Optional: +- bus-frequency : Contain the I2C bus frequency, + the defualt I2C bus frequency is 100000. + +Example: + +i2c0: i2c-bus@f0080000 { + reg = <0xf0080000 0x1000>; + compatible = "nuvoton,npcm750-i2c-bus"; + clocks = <&clk NPCM7XX_CLK_APB2>; + bus-frequency = <100000>; + interrupts = <0 64 4>; +}; From 7efff0d1775f4dd99f1cba7bd6d03d5a9f5a188a Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Thu, 4 Jan 2018 13:20:57 +0200 Subject: [PATCH 23/37] i2c: npcm: add NPCM7xx I2C bus driver Add Nuvoton BMC NPCM7xx I2C bus driver. Signed-off-by: Avi Fishman Signed-off-by: Tomer Maimon --- drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-npcm7xx.c | 3582 ++++++++++++++++++++++++++++++ 3 files changed, 3593 insertions(+) create mode 100644 drivers/i2c/busses/i2c-npcm7xx.c diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 65fa29591d2164..5971789814dfa9 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -728,6 +728,16 @@ config I2C_NOMADIK I2C interface from ST-Ericsson's Nomadik and Ux500 architectures, as well as the STA2X11 PCIe I/O HUB. +config I2C_NPCM7XX + tristate "Nuvoton I2C Controller" + depends on ARCH_NPCM7XX + help + If you say yes to this option, support will be included for the + Nuvoton I2C controller. + + This driver can also be built as a module. If so, the module + will be called i2c-npcm7xx. + config I2C_OCORES tristate "OpenCores I2C Controller" help diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 1b2fc815a4d838..80acad9903ff22 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -72,6 +72,7 @@ obj-$(CONFIG_I2C_MT65XX) += i2c-mt65xx.o obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o obj-$(CONFIG_I2C_MXS) += i2c-mxs.o obj-$(CONFIG_I2C_NOMADIK) += i2c-nomadik.o +obj-$(CONFIG_I2C_NPCM7XX) += i2c-npcm7xx.o obj-$(CONFIG_I2C_OCORES) += i2c-ocores.o obj-$(CONFIG_I2C_OMAP) += i2c-omap.o obj-$(CONFIG_I2C_PASEMI) += i2c-pasemi.o diff --git a/drivers/i2c/busses/i2c-npcm7xx.c b/drivers/i2c/busses/i2c-npcm7xx.c new file mode 100644 index 00000000000000..5c917985e815de --- /dev/null +++ b/drivers/i2c/busses/i2c-npcm7xx.c @@ -0,0 +1,3582 @@ +/* + * Copyright (c) 2014-2017 Nuvoton Technology corporation. + * + * Released under the GPLv2 only. + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +static struct regmap *gcr_regmap = NULL; +static struct regmap *clk_regmap = NULL; + +#define NPCM7XX_SECCNT (0x68) +#define NPCM7XX_CNTR25M (0x6C) + +#define I2CSEGCTL_OFFSET 0xE4 +#define I2CSEGCTL_VAL 0x0333F000 + +#define ENABLE 1 +#define DISABLE 0 + +#define _1Hz_ 1UL +#define _1KHz_ (1000 * _1Hz_) +#define _1MHz_ (1000 * _1KHz_) +#define _1GHz_ (1000 * _1MHz_) + +#ifndef ASSERT +#ifdef DEBUG +#define ASSERT(cond) {if (!(cond)) for (;;) ; } /* infinite loop */ +#else +#define ASSERT(cond) +#endif +#endif + +#define ROUND_UP(val, n) (((val)+(n)-1) & ~((n)-1)) +#define DIV_CEILING(a, b) (((a) + ((b)-1)) / (b)) + +#define I2C_VERSION "0.0.1" + +//#define CONFIG_NPCM750_I2C_DEBUG +#ifdef CONFIG_NPCM750_I2C_DEBUG +#define dev_err(a, f, x...) pr_err("NPCM750-I2C: %s() dev_err:" f, __func__, \ + ## x) +#define I2C_DEBUG(f, x...) pr_info("NPCM750-I2C: %s():%d " f, __func__, \ + __LINE__, ## x) +#else +#define I2C_DEBUG(f, x...) +#endif +#define HAL_PRINT(f, x...) printk(f, ## x) + + +typedef struct bit_field { + u8 offset; + u8 size; +} bit_field_t; + +#ifdef REG_READ +#undef REG_READ +#endif +static inline u8 REG_READ(unsigned char __iomem *mem) +{ + return ioread8(mem); +} + +#ifdef REG_WRITE +#undef REG_WRITE +#endif +static inline void REG_WRITE(unsigned char __iomem *mem, u8 val) +{ + iowrite8(val, mem); +} + +#ifdef SET_REG_FIELD +#undef SET_REG_FIELD +#endif +static inline void SET_REG_FIELD(unsigned char __iomem *mem, + bit_field_t bit_field, u8 val) +{ + u8 tmp = ioread8(mem); + tmp &= ~(((1 << bit_field.size) - 1) << bit_field.offset); // mask the field size + tmp |= val << bit_field.offset; // or with the requested value + iowrite8(tmp, mem); +} + +#ifdef SET_VAR_FIELD +#undef SET_VAR_FIELD +#endif +// bit_field should be of bit_field_t type +#define SET_VAR_FIELD(var, bit_field, value) { \ + typeof(var) tmp = var; \ + tmp &= ~(((1 << bit_field.size) - 1) << bit_field.offset); /* mask the field size */ \ + tmp |= value << bit_field.offset; /* or with the requested value */ \ + var = tmp; \ +} + +#ifdef READ_REG_FIELD +#undef READ_REG_FIELD +#endif +static inline u8 READ_REG_FIELD(unsigned char __iomem *mem, + bit_field_t bit_field) +{ + u8 tmp = ioread8(mem); + tmp = tmp >> bit_field.offset; // shift right the offset + tmp &= (1 << bit_field.size) - 1; // mask the size + return tmp; +} + +#ifdef READ_VAR_FIELD +#undef READ_VAR_FIELD +#endif +// bit_field should be of bit_field_t type +#define READ_VAR_FIELD(var, bit_field) ({ \ + typeof(var) tmp = var; \ + tmp = tmp >> bit_field.offset; /* shift right the offset */ \ + tmp &= (1 << bit_field.size) - 1; /* mask the size */ \ + tmp; \ +}) + +#ifdef MASK_FIELD +#undef MASK_FIELD +#endif +#define MASK_FIELD(bit_field) \ + (((1 << bit_field.size) - 1) << bit_field.offset) /* mask the field size */ + +#ifdef BUILD_FIELD_VAL +#undef BUILD_FIELD_VAL +#endif +#define BUILD_FIELD_VAL(bit_field, value) \ + ((((1 << bit_field.size) - 1) & (value)) << bit_field.offset) + + +#ifdef SET_REG_MASK +#undef SET_REG_MASK +#endif +static inline void SET_REG_MASK(unsigned char __iomem *mem, u8 val) +{ + iowrite8(ioread8(mem) | val, mem); +} + +#ifndef CONFIG_I2C_SLAVE +#define SMB_MASTER_ONLY +#endif + +//#define SMB_CAPABILITY_WAKEUP_SUPPORT +#define SMB_CAPABILITY_FAST_MODE_SUPPORT +#define SMB_CAPABILITY_FAST_MODE_PLUS_SUPPORT +#define SMB_CAPABILITY_END_OF_BUSY_SUPPORT +#define SMB_CAPABILITY_TIMEOUT_SUPPORT + +// Using SW PEC instead of HW PEC: +//#define SMB_CAPABILITY_HW_PEC_SUPPORT +//#define SMB_STALL_TIMEOUT_SUPPORT +#define SMB_RECOVERY_SUPPORT + +// override issue #614: TITLE :CP_FW: SMBus may fail to supply stop condition in Master Write operation +#define SMB_SW_BYPASS_HW_ISSUE_SMB_STOP + +// if end device reads more data than avalilable, ask issuer or request for more data. +#define SMB_WRAP_AROUND_BUFFER + +#define SMB_BYTES_QUICK_PROT 0xFFFF +#define SMB_BYTES_BLOCK_PROT 0xFFFE +#define SMB_BYTES_EXCLUDE_BLOCK_SIZE_FROM_BUFFER 0xFFFD + +#define ARP_ADDRESS_VAL 0x61 + +typedef enum { + SMB_SLAVE = 1, + SMB_MASTER +} SMB_MODE_T; + +/* + * External SMB Interface driver states values, which indicate to the + * upper-level layer the status of the + * operation it initiated or wake up events from one of the buses + */ +typedef enum { + SMB_NO_STATUS_IND, + SMB_SLAVE_RCV_IND, + SMB_SLAVE_XMIT_IND, +#ifdef SMB_WRAP_AROUND_BUFFER + SMB_SLAVE_XMIT_MISSING_DATA_IND, +#endif + SMB_SLAVE_RESTART_IND, + SMB_SLAVE_DONE_IND, + SMB_MASTER_DONE_IND, + SMB_NO_DATA_IND, + SMB_NACK_IND, + SMB_BUS_ERR_IND, + SMB_WAKE_UP_IND, + SMB_MASTER_PEC_ERR_IND, + SMB_MASTER_BLOCK_BYTES_ERR_IND, + SMB_SLAVE_PEC_ERR_IND +} SMB_STATE_IND_T; + +typedef enum { + SMB_SLAVE_ADDR1, + SMB_SLAVE_ADDR2, + SMB_SLAVE_ADDR3, + SMB_SLAVE_ADDR4, + SMB_SLAVE_ADDR5, + SMB_SLAVE_ADDR6, + SMB_SLAVE_ADDR7, + SMB_SLAVE_ADDR8, + SMB_SLAVE_ADDR9, + SMB_SLAVE_ADDR10, + SMB_GC_ADDR, + SMB_ARP_ADDR +} SMB_ADDR_T; + +#ifdef SMB_CAPABILITY_FORCE_SCL_SDA +typedef enum { + SMB_LEVEL_LOW = 0, + SMB_LEVEL_HIGH = 1 +} SMB_LEVEL_T; +#endif // SMB_CAPABILITY_FORCE_SCL_SDA + + +// Common registers +#define SMBSDA(bus) (bus->base + 0x000) +#define SMBST(bus) (bus->base + 0x002) +#define SMBCST(bus) (bus->base + 0x004) +#define SMBCTL1(bus) (bus->base + 0x006) +#define SMBADDR1(bus) (bus->base + 0x008) +#define SMBCTL2(bus) (bus->base + 0x00A) +#define SMBADDR2(bus) (bus->base + 0x00C) +#define SMBCTL3(bus) (bus->base + 0x00E) +#define SMBCST2(bus) (bus->base + 0x018) // Control Status 2 +#define SMBCST3(bus) (bus->base + 0x019) // Control Status 3 Register +#define SMB_VER(bus) (bus->base + 0x01F) // SMB Version Register + +// BANK 0 registers +#define SMBADDR3(bus) (bus->base + 0x010) +#define SMBADDR7(bus) (bus->base + 0x011) +#define SMBADDR4(bus) (bus->base + 0x012) +#define SMBADDR8(bus) (bus->base + 0x013) +#define SMBADDR5(bus) (bus->base + 0x014) +#define SMBADDR9(bus) (bus->base + 0x015) +#define SMBADDR6(bus) (bus->base + 0x016) +#define SMBADDR10(bus) (bus->base + 0x017) + +#define SMBADDR(bus, i) (bus->base + 0x008 + (u32)(((int)i*4) + (((int)i < 2) ? 0 : ((int)i-2)*(-2)) + (((int)i < 6) ? 0 : (-7)))) + +#define SMBCTL4(bus) (bus->base + 0x01A) +#define SMBCTL5(bus) (bus->base + 0x01B) +#define SMBSCLLT(bus) (bus->base + 0x01C) // SMB SCL Low Time (Fast-Mode) +#define SMBFIF_CTL(bus) (bus->base + 0x01D) // FIFO Control +#define SMBSCLHT(bus) (bus->base + 0x01E) // SMB SCL High Time (Fast-Mode) + +// BANK 1 registers +#define SMBFIF_CTS(bus) (bus->base + 0x010) // FIFO Control and Status +#define SMBTXF_CTL(bus) (bus->base + 0x012) // Tx-FIFO Control +#if defined (SMB_CAPABILITY_TIMEOUT_SUPPORT) +#define SMBT_OUT(bus) (bus->base + 0x014) // Bus Time-Out +#endif +#if defined (SMB_CAPABILITY_HW_PEC_SUPPORT) +#define SMBPEC(bus) (bus->base + 0x016) // PEC Data +#endif +#define SMBTXF_STS(bus) (bus->base + 0x01A) // Tx-FIFO Status +#define SMBRXF_STS(bus) (bus->base + 0x01C) // Rx-FIFO Status +#define SMBRXF_CTL(bus) (bus->base + 0x01E) // Rx-FIFO Control + +#ifdef SMB_CAPABILITY_WAKEUP_SUPPORT +#define SMB_SBD ((GLUE_BASE_ADDR + 0x002), GLUE_ACCESS, 8) +#define SMB_EEN ((GLUE_BASE_ADDR + 0x003), GLUE_ACCESS, 8) +#endif + + +/* SMBST register fields */ +static const bit_field_t SMBST_XMIT = { 0, 1 }; +static const bit_field_t SMBST_MASTER = { 1, 1 }; +static const bit_field_t SMBST_NMATCH = { 2, 1 }; +static const bit_field_t SMBST_STASTR = { 3, 1 }; +static const bit_field_t SMBST_NEGACK = { 4, 1 }; +static const bit_field_t SMBST_BER = { 5, 1 }; +static const bit_field_t SMBST_SDAST = { 6, 1 }; +static const bit_field_t SMBST_SLVSTP = { 7, 1 }; + +/* SMBCST register fields */ +static const bit_field_t SMBCST_BUSY = { 0, 1 }; +static const bit_field_t SMBCST_BB = { 1, 1 }; +static const bit_field_t SMBCST_MATCH = { 2, 1 }; +static const bit_field_t SMBCST_GCMATCH = { 3, 1 }; +static const bit_field_t SMBCST_TSDA = { 4, 1 }; +static const bit_field_t SMBCST_TGSCL = { 5, 1 }; +static const bit_field_t SMBCST_MATCHAF = { 6, 1 }; +static const bit_field_t SMBCST_ARPMATCH = { 7, 1 }; + +/* SMBCTL1 register fields */ +static const bit_field_t SMBCTL1_START = { 0, 1 }; +static const bit_field_t SMBCTL1_STOP = { 1, 1 }; +static const bit_field_t SMBCTL1_INTEN = { 2, 1 }; +#ifdef SMB_CAPABILITY_END_OF_BUSY_SUPPORT +static const bit_field_t SMBCTL1_EOBINTE = { 3, 1 }; +#endif +static const bit_field_t SMBCTL1_ACK = { 4, 1 }; +static const bit_field_t SMBCTL1_GCMEN = { 5, 1 }; +static const bit_field_t SMBCTL1_NMINTE = { 6, 1 }; +static const bit_field_t SMBCTL1_STASTRE = { 7, 1 }; + +/* SMBADDRx register fields */ +static const bit_field_t SMBADDRx_ADDR = { 0, 7 }; +static const bit_field_t SMBADDRx_SAEN = { 7, 1 }; + +/* SMBCTL2 register fields */ +static const bit_field_t SMBCTL2_ENABLE = { 0, 1 }; +static const bit_field_t SMBCTL2_SCLFRQ6_0 = { 1, 7 }; + +/* SMBCTL3 register fields */ +static const bit_field_t SMBCTL3_SCLFRQ8_7 = { 0, 2 }; +static const bit_field_t SMBCTL3_ARPMEN = { 2, 1 }; +static const bit_field_t SMBCTL3_IDL_START = { 3, 1 }; +static const bit_field_t SMBCTL3_400K_MODE = { 4, 1 }; +static const bit_field_t SMBCTL3_BNK_SEL = { 5, 1 }; +static const bit_field_t SMBCTL3_SDA_LVL = { 6, 1 }; +static const bit_field_t SMBCTL3_SCL_LVL = { 7, 1 }; + +/* SMBCST2 register fields */ +static const bit_field_t SMBCST2_MATCHA1F = { 0, 1 }; +static const bit_field_t SMBCST2_MATCHA2F = { 1, 1 }; +static const bit_field_t SMBCST2_MATCHA3F = { 2, 1 }; +static const bit_field_t SMBCST2_MATCHA4F = { 3, 1 }; +static const bit_field_t SMBCST2_MATCHA5F = { 4, 1 }; +static const bit_field_t SMBCST2_MATCHA6F = { 5, 1 }; +static const bit_field_t SMBCST2_MATCHA7F = { 5, 1 }; +static const bit_field_t SMBCST2_INTSTS = { 7, 1 }; + +/* SMBCST3 register fields */ +static const bit_field_t SMBCST3_MATCHA8F = { 0, 1 }; +static const bit_field_t SMBCST3_MATCHA9F = { 1, 1 }; +static const bit_field_t SMBCST3_MATCHA10F = { 2, 1 }; +#ifdef SMB_CAPABILITY_END_OF_BUSY_SUPPORT +static const bit_field_t SMBCST3_EO_BUSY = { 7, 1 }; +#endif + +/* SMBCTL4 register fields */ +static const bit_field_t SMBCTL4_HLDT = { 0, 6 }; +#ifdef SMB_CAPABILITY_FORCE_SCL_SDA +static const bit_field_t SMBCTL4_LVL_WE = { 7, 1 }; +#endif + +/* SMBCTL5 register fields */ +static const bit_field_t SMBCTL5_DBNCT = { 0, 4 }; + +/* SMBFIF_CTS register fields */ +static const bit_field_t SMBFIF_CTS_RXF_TXE = { 1, 1 }; +static const bit_field_t SMBFIF_CTS_RFTE_IE = { 3, 1 }; +static const bit_field_t SMBFIF_CTS_CLR_FIFO = { 6, 1 }; +static const bit_field_t SMBFIF_CTS_SLVRSTR = { 7, 1 }; + +/* SMBTXF_CTL register fields */ +#ifdef SMB_CAPABILITY_32B_FIFO +static const bit_field_t SMBTXF_CTL_TX_THR = { 0, 6 }; +#else +static const bit_field_t SMBTXF_CTL_TX_THR = { 0, 5 }; +#endif +static const bit_field_t SMBTXF_CTL_THR_TXIE = { 6, 1 }; + +#if defined (SMB_CAPABILITY_TIMEOUT_SUPPORT) + +/* SMBT_OUT register fields */ +static const bit_field_t SMBT_OUT_TO_CKDIV = { 0, 6 }; +static const bit_field_t SMBT_OUT_T_OUTIE = { 6, 1 }; +static const bit_field_t SMBT_OUT_T_OUTST = { 7, 1 }; +#endif + +/* SMBTXF_STS register fields */ +#ifdef SMB_CAPABILITY_32B_FIFO +static const bit_field_t SMBTXF_STS_TX_BYTES = { 0, 6 }; +#else +static const bit_field_t SMBTXF_STS_TX_BYTES = { 0, 5 }; +#endif +static const bit_field_t SMBTXF_STS_TX_THST = { 6, 1 }; + +/* SMBRXF_STS register fields */ +#ifdef SMB_CAPABILITY_32B_FIFO +static const bit_field_t SMBRXF_STS_RX_BYTES = { 0, 6 }; +#else +static const bit_field_t SMBRXF_STS_RX_BYTES = { 0, 5 }; +#endif +static const bit_field_t SMBRXF_STS_RX_THST = { 6, 1 }; + +/* SMBFIF_CTL register fields */ +static const bit_field_t SMBFIF_CTL_FIFO_EN = { 4, 1 }; + +/* SMBRXF_CTL register fields */ +#ifdef SMB_CAPABILITY_32B_FIFO +static const bit_field_t SMBRXF_CTL_RX_THR = { 0, 6 }; +static const bit_field_t SMBRXF_CTL_THR_RXIE = { 6, 1 }; +static const bit_field_t SMBRXF_CTL_LAST_PEC = { 7, 1 }; +#else +static const bit_field_t SMBRXF_CTL_RX_THR = { 0, 5 }; +static const bit_field_t SMBRXF_CTL_LAST_PEC = { 5, 1 }; +static const bit_field_t SMBRXF_CTL_THR_RXIE = { 6, 1 }; +#endif + +/* SMB_VER register fields */ +static const bit_field_t SMB_VER_VERSION = { 0, 7 }; +static const bit_field_t SMB_VER_FIFO_EN = { 7, 1 }; + +#ifdef SMB_CAPABILITY_WAKEUP_SUPPORT + +/* SMB_SBD register fields */ +#define SMB_SBD_SMBnSBD(n) ((n), 1) + + +/* SMB_EEN register fields */ +#define SMB_EEN_SMBnEEN(n) ((n), 1) +#endif // SMB_CAPABILITY_WAKEUP_SUPPORT + + +/* Module Dependencies */ +#if defined (MIWU_MODULE_TYPE) && defined (SMB_CAPABILITY_WAKEUP_SUPPORT) +#include __MODULE_IF_HEADER_FROM_DRV(miwu) +#endif + + +#if defined (CLK_MODULE_TYPE) +#include __MODULE_IF_HEADER_FROM_DRV(clk) +#endif + + + + + +/* TYPES & DEFINITIONS */ +#ifdef SMB_SLAVE_ONLY +//The Stall Timeout feature is only relevant in Master mode. +#undef SMB_STALL_TIMEOUT_SUPPORT +#endif + +#ifdef SMB_STALL_TIMEOUT_SUPPORT + +/* stall/stuck timeout */ +#define DEFAULT_STALL_COUNT 25 +#endif + +/* Data abort timeout */ +#define ABORT_TIMEOUT 1000 + +/* SMBus spec. values in KHz */ +#define SMBUS_FREQ_MIN 10 + +#if defined SMB_CAPABILITY_FAST_MODE_PLUS_SUPPORT +#define SMBUS_FREQ_MAX 1000 +#elif defined SMB_CAPABILITY_FAST_MODE_SUPPORT +#define SMBUS_FREQ_MAX 400 +#else +#define SMBUS_FREQ_MAX 100 +#endif + +#define SMBUS_FREQ_100KHz 100 +#define SMBUS_FREQ_400KHz 400 +#define SMBUS_FREQ_1MHz 1000 + + + +/* SMBus FIFO SIZE (when FIFO hardware exist) */ +#ifdef SMB_CAPABILITY_32B_FIFO +#define SMBUS_FIFO_SIZE 32 +#else +#define SMBUS_FIFO_SIZE 16 +#endif + + +/* SCLFRQ min/max field values */ +#define SCLFRQ_MIN 10 +#define SCLFRQ_MAX 511 + +/* SCLFRQ field position */ +static const bit_field_t SCLFRQ_0_TO_6 = { 0, 7 }; +static const bit_field_t SCLFRQ_7_TO_8 = { 7, 2 }; + +/* SMB Maximum Retry Trials (on Bus Arbitration Loss) */ +#define SMB_RETRY_MAX_COUNT 3 + +/* SMBus Operation type values */ +typedef enum { + SMB_NO_OPER = 0, + SMB_WRITE_OPER = 1, + SMB_READ_OPER = 2 +} SMB_OPERATION_T; + + + +/* SMBus Bank (FIFO mode) */ + +typedef enum { + SMB_BANK_0 = 0, + SMB_BANK_1 = 1 +} SMB_BANK_T; + +/* Internal SMBus Interface driver states values, which reflect events which occurred on the bus */ +typedef enum { + SMB_DISABLE, + SMB_IDLE, + SMB_MASTER_START, + SMB_SLAVE_MATCH, + SMB_OPER_STARTED, + SMB_REPEATED_START, + SMB_STOP_PENDING +} SMB_OPERATION_STATE_T; + + +#define SMB_NUM_OF_ADDR 10 // TBD move to device tree +#define SMB_FIFO(bus) true /* All modules support FIFO */ +void npcm750_clk_GetTimeStamp(u32 time_quad[2]); + + +/* Status of one SMBus module */ +typedef struct nuvoton_i2c_bus { + struct i2c_adapter adap; + struct device *dev; + unsigned char __iomem *base; + /* Synchronizes I/O mem access to base. */ + spinlock_t lock; + spinlock_t bank_lock; + struct completion cmd_complete; + int irq; + int cmd_err; + struct i2c_msg *msgs; + int msgs_num; + int module__num; + u32 apb_clk; +#ifdef CONFIG_I2C_SLAVE + struct i2c_client *slave; +#endif /* CONFIG_I2C_SLAVE */ + + /* Current state of SMBus */ + volatile SMB_OPERATION_STATE_T operation_state; + + /* Type of the last SMBus operation */ + SMB_OPERATION_T operation; + + /* Mode of operation on SMBus */ + SMB_MODE_T master_or_slave; +#ifdef SMB_CAPABILITY_END_OF_BUSY_SUPPORT + /* The indication to the hi level after Master Stop */ + SMB_STATE_IND_T stop_indication; +#endif + /* SMBus slave device's Slave Address in 8-bit format - for master transactions */ + u8 dest_addr; + + /* Buffer where read data should be placed */ + u8 *read_data_buf; + + /* Number of bytes to be read */ + u16 read_size; + + /* Number of bytes already read */ + u16 read_index; + + /* Buffer with data to be written */ + u8 *write_data_buf; + + /* Number of bytes to write */ + u16 write_size; + + /* Number of bytes already written */ + u16 write_index; + + /* use fifo hardware or not */ + bool fifo_use; + + /* fifo threshold size */ + u8 threshold_fifo; + + /* PEC bit mask per slave address. + 1: use PEC for this address, + 0: do not use PEC for this address */ + u16 PEC_mask; + + /* Use PEC CRC */ + bool PEC_use; + + /* PEC CRC data */ + u8 crc_data; + + /* Use read block */ + bool read_block_use; + + /* Number of retries remaining */ + u8 retry_count; + +#if !defined SMB_MASTER_ONLY + u8 SMB_CurSlaveAddr; +#endif + +#ifdef SMB_STALL_TIMEOUT_SUPPORT + u8 stall_counter; + u8 stall_threshold; +#endif + + +// override issue #614: TITLE :CP_FW: SMBus may fail to supply stop condition in Master Write operation. If needed : define it at hal_cfg.h +#ifdef SMB_SW_BYPASS_HW_ISSUE_SMB_STOP + /* The indication to the hi level after Master Stop */ + u32 clk_period_us; + u32 interrupt_time_stamp[2]; +#endif +} nuvoton_i2c_bus_t; + + +#ifdef SMB_SW_BYPASS_HW_ISSUE_SMB_STOP + +static void inline _npcm7xx_get_time_stamp(u32 time_quad[2]); +static u32 inline _npcm7xx_delay_relative(u32 microSecDelay, u32 t0_time[2]); + + + +static void inline _npcm7xx_get_time_stamp(u32 time_quad[2]) +{ + u32 seconds, seconds_last; + u32 ref_clock; + + regmap_read(clk_regmap, NPCM7XX_SECCNT, &seconds_last); + + do{ + regmap_read(clk_regmap, NPCM7XX_SECCNT, &seconds); + regmap_read(clk_regmap, NPCM7XX_CNTR25M, &ref_clock); + regmap_read(clk_regmap, NPCM7XX_SECCNT, &seconds_last); + } while (seconds_last != seconds); + + time_quad[0] = ref_clock; + time_quad[1] = seconds; +} + +#define EXT_CLOCK_FREQUENCY_MHZ 25 +#define CNTR25M_ACCURECY EXT_CLOCK_FREQUENCY_MHZ /* minimum accurecy 1us which is 5 cycles */ + + + +// Function: _npcm7xx_delay_relative +// Parameters: +// microSecDelay - number of microseconds to delay since t0_time. if zero: no delay. +// t0_time - start time , to measure time from. +// get a time stamp, delay microSecDelay from it. If microSecDelay has already passed +// since the time stamp , then no delay is executed. returns the time that elapsed since +// t0_time . +static u32 inline _npcm7xx_delay_relative(u32 microSecDelay, u32 t0_time[2]) +{ + u32 iUsCnt2[2]; + u32 timeElapsedSince; // Acctual delay generated by FW + u32 minimum_delay = (microSecDelay * EXT_CLOCK_FREQUENCY_MHZ) + CNTR25M_ACCURECY; /* this is equivalent to microSec/0.64 + minimal tic length.*/ + + do { + _npcm7xx_get_time_stamp(iUsCnt2); + timeElapsedSince = ((EXT_CLOCK_FREQUENCY_MHZ * _1MHz_) * (iUsCnt2[1] - t0_time[1])) + (iUsCnt2[0] - t0_time[0]); + } + while(timeElapsedSince < minimum_delay); + + // return elapsed time + return (u32)(timeElapsedSince / EXT_CLOCK_FREQUENCY_MHZ); +} + + + +#endif // SMB_SW_BYPASS_HW_ISSUE_SMB_STOP + + +/* GLOBAL VARIABLES */ +/* Callback function provided by next-higher level driver or application, + implementing operation handling */ +/* state-machine */ + +//static SMB_CALLBACK_T SMB_callback; + + + +/* INTERFACE FUNCTIONS */ + + + +static bool SMB_InitModule(nuvoton_i2c_bus_t *bus, SMB_MODE_T mode, u16 bus_freq); + +#if defined (MIWU_MODULE_TYPE) && defined (SMB_CAPABILITY_WAKEUP_SUPPORT) +static void SMB_WakeupEnable(nuvoton_i2c_bus_t *bus, bool enable); +#endif /* (MIWU_MODULE_TYPE) && (SMB_CAPABILITY_WAKEUP_SUPPORT) */ + +#if !defined SMB_SLAVE_ONLY +static bool SMB_StartMasterTransaction(nuvoton_i2c_bus_t *bus, u8 slave_addr, u16 nwrite, u16 nread, + u8 *write_data, u8 *read_data, bool use_PEC); +static void SMB_MasterAbort(nuvoton_i2c_bus_t *bus); + +#ifdef TBD +static void SMB_Recovery(nuvoton_i2c_bus_t *bus); +#endif //TBD + +#endif /* !SMB_SLAVE_ONLY */ + +#if !defined SMB_MASTER_ONLY +static DEFS_STATUS SMB_SlaveGlobalCallEnable(nuvoton_i2c_bus_t *bus, bool enable); +static DEFS_STATUS SMB_SlaveARPEnable(nuvoton_i2c_bus_t *bus, bool enable); +static bool SMB_StartSlaveReceive(nuvoton_i2c_bus_t *bus, u16 nread, u8 *read_data); +static bool SMB_StartSlaveTransmit(nuvoton_i2c_bus_t *bus, u16 nwrite, u8 *write_data); +static DEFS_STATUS SMB_GetCurrentSlaveAddress(nuvoton_i2c_bus_t *bus, u8 *currSlaveAddr); +static DEFS_STATUS SMB_RemSlaveAddress(nuvoton_i2c_bus_t *bus, u8 slaveAddrToRemove); +static DEFS_STATUS SMB_AddSlaveAddress(nuvoton_i2c_bus_t *bus, u8 slaveAddrToAssign, bool use_PEC); +static bool SMB_IsSlaveAddressExist(nuvoton_i2c_bus_t *bus, u8 addr); +static void SMB_Disable(nuvoton_i2c_bus_t *bus); +#if defined (SMB_CAPABILITY_TIMEOUT_SUPPORT) +static void SMB_EnableTimeout(nuvoton_i2c_bus_t *bus, bool enable); +#endif +#if defined (SMB_CAPABILITY_WAKEUP_SUPPORT) +static void SMB_SetStallAfterStartIdle(nuvoton_i2c_bus_t *bus, bool enable); +#endif +#endif /* !SMB_MASTER_ONLY */ + +#ifdef SMB_STALL_TIMEOUT_SUPPORT +static void SMB_ConfigStallThreshold(nuvoton_i2c_bus_t *bus, u8 threshold); +static void SMB_StallHandler(nuvoton_i2c_bus_t *bus); +#endif + +#ifdef TBD +static void SMB_Init(SMB_CALLBACK_T operation_done); +static bool SMB_ModuleIsBusy(nuvoton_i2c_bus_t *bus); +static bool SMB_BusIsBusy(nuvoton_i2c_bus_t *bus); +static void SMB_ReEnableModule(nuvoton_i2c_bus_t *bus); +static bool SMB_InterruptIsPending(void); +#endif + +#ifdef SMB_CAPABILITY_FORCE_SCL_SDA +static void SMB_WriteSCL(nuvoton_i2c_bus_t *bus, SMB_LEVEL_T level); +static void SMB_WriteSDA(nuvoton_i2c_bus_t *bus, SMB_LEVEL_T level); +#endif // SMB_CAPABILITY_FORCE_SCL_SDA + +#ifdef CONFIG_NPCM750_I2C_DEBUG_PRINT +static void SMB_PrintRegs(nuvoton_i2c_bus_t *bus); +static void SMB_PrintModuleRegs(nuvoton_i2c_bus_t *bus); +static void SMB_PrintVersion(void); +#endif + + +typedef void (*SMB_CALLBACK_T)(nuvoton_i2c_bus_t *bus, SMB_STATE_IND_T op_status, u16 info); + +#ifdef SMB_SAMPLE +void SMB_callback(nuvoton_i2c_bus_t *bus, SMB_STATE_IND_T op_status, u16 info) +{ + switch (op_status) { + case SMB_SLAVE_RCV_IND: + // Slave got an address match with direction bit clear so it should receive data + // the interrupt must call SMB_StartSlaveReceive() + // info: the enum SMB_ADDR_T address match + extern u16 read_size; + extern u8 *read_data_buf; + SMB_StartSlaveReceive(bus, read_size, read_data_buf); + break; + case SMB_SLAVE_XMIT_IND: + // Slave got an address match with direction bit set so it should transmit data + // the interrupt must call SMB_StartSlaveTransmit() + // info: the enum SMB_ADDR_T address match + extern u16 write_size; + extern u8 *write_data_buf; + SMB_StartSlaveTransmit(bus, write_size, write_data_buf); + break; + case SMB_SLAVE_DONE_IND: + // Slave done transmitting or receiving + // info: + // on receive: number of actual bytes received + // on transmit: number of actual bytes transmitted, + // when PEC is used 'info' should be (nwrite+1) which means that 'nwrite' bytes + // were sent + the PEC byte + // 'nwrite' is the second parameter SMB_StartSlaveTransmit() + break; + case SMB_MASTER_DONE_IND: + // Master transaction finished and all transmit bytes were sent + // info: number of bytes actually received after the Master receive operation + // (if Master didn't issue receive it should be 0) + break; + case SMB_NO_DATA_IND: + // Notify that not all data was received on Master or Slave + // info: + // on receive: number of actual bytes received + // when PEC is used even if 'info' is the expected number of bytes, + // it means that PEC error occured. + break; + case SMB_NACK_IND: + // MASTER transmit got a NAK before transmitting all bytes + // info: number of transmitted bytes + break; + case SMB_BUS_ERR_IND: + // Bus error occured + // info: has no meaning + break; + case SMB_WAKE_UP_IND: + // SMBus wake up occured + // info: has no meaning + break; + default: + break; + } +} +#endif /* SMB_SAMPLE */ + + + + + +/* LOCAL FUNCTIONS FORWARD DECLARATIONS */ + + +static inline void SMB_WriteByte(nuvoton_i2c_bus_t *bus, u8 data); +static inline bool SMB_ReadByte(nuvoton_i2c_bus_t *bus, u8 *data); +static inline void SMB_SelectBank(nuvoton_i2c_bus_t *bus, SMB_BANK_T bank); +static inline u16 SMB_GetIndex(nuvoton_i2c_bus_t *bus); + +#if !defined SMB_SLAVE_ONLY +static inline void SMB_Start(nuvoton_i2c_bus_t *bus); +static inline void SMB_Stop(nuvoton_i2c_bus_t *bus); +static inline void SMB_AbortData(nuvoton_i2c_bus_t *bus); +static inline void SMB_StallAfterStart(nuvoton_i2c_bus_t *bus, bool stall); +static inline void SMB_Nack(nuvoton_i2c_bus_t *bus); +#endif /* !SMB_SLAVE_ONLY */ + +static void SMB_Reset(nuvoton_i2c_bus_t *bus); +static void SMB_InterruptEnable(nuvoton_i2c_bus_t *bus, bool enable); +#if defined (MIWU_MODULE_TYPE) && defined (SMB_CAPABILITY_WAKEUP_SUPPORT) +static void SMB_WakeupHandler(MIWU_SRC_T source); +#endif + +static bool SMB_InitClock(nuvoton_i2c_bus_t *bus, SMB_MODE_T mode, + u16 bus_freq); +static void SMB_InterruptHandler(nuvoton_i2c_bus_t *bus); +#if !defined SMB_MASTER_ONLY +static u8 SMB_GetSlaveAddress_l(nuvoton_i2c_bus_t *bus, + SMB_ADDR_T addrEnum); +#endif //!defined SMB_MASTER_ONLY +static void SMB_WriteToFifo(nuvoton_i2c_bus_t *bus, + u16 max_bytes_to_send); + +static void SMB_CalcPEC(nuvoton_i2c_bus_t *bus, u8 data); +#ifdef CONFIG_NPCM750_I2C_DEBUG_PRINT +static void SMBF_PrintModuleRegs(nuvoton_i2c_bus_t *bus); +#endif +static void SMB_callback(nuvoton_i2c_bus_t *bus, + SMB_STATE_IND_T op_status, u16 info); + + +/* SMB Recovery of the SMBus interface driver */ +#if !defined SMB_MASTER_ONLY && defined SMB_RECOVERY_SUPPORT +static void SMB_SlaveAbort(nuvoton_i2c_bus_t *bus); /* SMB slave abort data */ +#endif + + + +/* INTERFACE FUNCTIONS */ + +static inline void SMB_WriteByte(nuvoton_i2c_bus_t *bus, u8 data) +{ + REG_WRITE(SMBSDA(bus), data); + SMB_CalcPEC(bus, data); +#ifdef SMB_STALL_TIMEOUT_SUPPORT + bus->stall_counter = 0; +#endif +} + +static inline bool SMB_ReadByte(nuvoton_i2c_bus_t *bus, u8 *data) +{ + /* Read data */ + *data = REG_READ(SMBSDA(bus)); + SMB_CalcPEC(bus, *data); +#ifdef SMB_STALL_TIMEOUT_SUPPORT + bus->stall_counter = 0; +#endif + + return true; +} + +static inline void SMB_SelectBank(nuvoton_i2c_bus_t *bus, SMB_BANK_T bank) +{ + if (bus->fifo_use == true) + SET_REG_FIELD(SMBCTL3(bus), SMBCTL3_BNK_SEL, bank); +} + +static inline u16 SMB_GetIndex(nuvoton_i2c_bus_t *bus) +{ + u16 index = 0; + + if (bus->operation == SMB_READ_OPER) + index = bus->read_index; + else + if (bus->operation == SMB_WRITE_OPER) + index = bus->write_index; + + return index; +} + +#if !defined SMB_SLAVE_ONLY + +static inline void SMB_Start(nuvoton_i2c_bus_t *bus) +{ + SET_REG_FIELD(SMBCTL1(bus), SMBCTL1_START, true); +#ifdef SMB_STALL_TIMEOUT_SUPPORT + bus->stall_counter = 0; +#endif +} +static inline void SMB_Stop(nuvoton_i2c_bus_t *bus) +{ +#ifdef SMB_SW_BYPASS_HW_ISSUE_SMB_STOP + // override issue #614: TITLE :CP_FW: SMBus may fail to supply stop condition in Master Write operation. If needed : define it at hal_cfg.h + bus->clk_period_us = 0; + _npcm7xx_delay_relative(bus->clk_period_us, bus->interrupt_time_stamp); +#endif // SMB_SW_BYPASS_HW_ISSUE_SMB_STOP + + SET_REG_FIELD(SMBCTL1(bus), SMBCTL1_STOP, true); + + if (bus->fifo_use) { + u8 smbfif_cts; + SET_REG_FIELD(SMBRXF_STS(bus), SMBRXF_STS_RX_THST, 1); + smbfif_cts = REG_READ(SMBFIF_CTS(bus)); + SET_VAR_FIELD(smbfif_cts, SMBFIF_CTS_SLVRSTR, 1); + SET_VAR_FIELD(smbfif_cts, SMBFIF_CTS_RXF_TXE, 1); + REG_WRITE(SMBFIF_CTS(bus), smbfif_cts); + SET_REG_MASK(SMBFIF_CTS(bus), MASK_FIELD(SMBFIF_CTS_SLVRSTR) | + MASK_FIELD(SMBFIF_CTS_RXF_TXE)); + + REG_WRITE(SMBTXF_CTL(bus), 0); + } + +#ifdef SMB_STALL_TIMEOUT_SUPPORT + bus->stall_counter = 0; +#endif + +} + +static inline void SMB_AbortData(nuvoton_i2c_bus_t *bus) +{ + unsigned int timeout = ABORT_TIMEOUT; + + /* Generate a STOP condition */ + SMB_Stop(bus); + + /* Clear NEGACK, STASTR and BER bits */ + REG_WRITE(SMBST(bus), (MASK_FIELD(SMBST_STASTR) | + MASK_FIELD(SMBST_NEGACK) | + MASK_FIELD(SMBST_BER))); + + /* Wait till STOP condition is generated */ + while (--timeout) + if (!READ_REG_FIELD(SMBCTL1(bus), SMBCTL1_STOP)) + break; + + /* Clear BB (BUS BUSY) bit */ + REG_WRITE(SMBCST(bus), MASK_FIELD(SMBCST_BB)); +} + +static inline void SMB_StallAfterStart(nuvoton_i2c_bus_t *bus, bool stall) +{ + SET_REG_FIELD(SMBCTL1(bus), SMBCTL1_STASTRE, stall); +} + +static inline void SMB_Nack(nuvoton_i2c_bus_t *bus) +{ + SET_REG_FIELD(SMBCTL1(bus), SMBCTL1_ACK, true); +} +#endif /* !SMB_SLAVE_ONLY */ + +static void SMB_Disable(nuvoton_i2c_bus_t *bus) +{ + int i; + + /* Slave Addresses Removal */ + for (i = SMB_SLAVE_ADDR1; i < SMB_NUM_OF_ADDR; i++) + REG_WRITE(SMBADDR(bus, i), 0); + + /* Disable module. */ + SET_REG_FIELD(SMBCTL2(bus), SMBCTL2_ENABLE, DISABLE); + + + /* Set module disable */ + bus->operation_state = SMB_DISABLE; +} + +static bool SMB_Enable(nuvoton_i2c_bus_t *bus) +{ + SET_REG_FIELD(SMBCTL2(bus), SMBCTL2_ENABLE, ENABLE); + return true; +} + +static bool SMB_InitModule(nuvoton_i2c_bus_t *bus, SMB_MODE_T mode, + u16 bus_freq) +{ + unsigned long bank_flags; + int i; + + /* Check whether module already enabled or frequency is out of bounds */ + if (((bus->operation_state != SMB_DISABLE) && + (bus->operation_state != SMB_IDLE)) || + (bus_freq < SMBUS_FREQ_MIN) || (bus_freq > SMBUS_FREQ_MAX)) + return false; + + /* Mux SMB module pins */ + //lint -e{792} suppress PC-Lint warning on 'void cast of void expression' +#ifdef TBD + SMB_MUX(bus); +#endif + /* Configure FIFO mode */ + //lint -e{774, 506} suppress PC-Lint warning on 'bool within 'left side of && within if' always evaluates to true' + if (SMB_FIFO(bus) && READ_REG_FIELD(SMB_VER(bus), SMB_VER_FIFO_EN)) { + bus->fifo_use = true; + bus->threshold_fifo = SMBUS_FIFO_SIZE; + SET_REG_FIELD(SMBFIF_CTL(bus), SMBFIF_CTL_FIFO_EN, 1); + } else + bus->fifo_use = false; + + /* Configure SMB module clock frequency */ + if (!SMB_InitClock(bus, mode, bus_freq)) { + I2C_DEBUG("SMB_InitClock failed\n"); + return false; + } + + spin_lock_irqsave(&bus->bank_lock, bank_flags); + SMB_SelectBank(bus, SMB_BANK_0); // select bank 0 for SMB addresses + + /* Configure slave addresses (by default they are disabled) */ + for (i = 0; i < SMB_NUM_OF_ADDR; i++) + REG_WRITE(SMBADDR(bus, i), 0); + + SMB_SelectBank(bus, SMB_BANK_1); // by default most access is in bank 1 + spin_unlock_irqrestore(&bus->bank_lock, bank_flags); + + /* Enable module - before configuring CTL1 ! */ + if (!SMB_Enable(bus)) + return false; + else + bus->operation_state = SMB_IDLE; + + /* Enable SMB interrupt and New Address Match interrupt source */ + SET_REG_FIELD(SMBCTL1(bus), SMBCTL1_NMINTE, ENABLE); + SMB_InterruptEnable(bus, true); + + return true; +} + +#if defined (MIWU_MODULE_TYPE) && defined (SMB_CAPABILITY_WAKEUP_SUPPORT) +static void SMB_WakeupEnable(nuvoton_i2c_bus_t *bus, bool enable) +{ + const MIWU_SRC_T SMbusToMiwu[] = SMB_WAKEUP_SRC; + MIWU_SRC_T miwu_src = SMbusToMiwu[bus]; + + if (enable) { + /* Configure MIWU module to generate an interrupt to the ICU following SMBus wake-up conditions */ + MIWU_Config(miwu_src, MIWU_RISING_EDGE, SMB_WakeupHandler); + + /* Configure SMBus Wake-up (in System Glue Function) */ + REG_WRITE(SMB_SBD, MASK_BIT(bus)); /* Clear Start condition detection */ + SET_REG_BIT(SMB_EEN, bus); /* Enable Event assertion */ + SET_REG_FIELD(SMBCTL3(bus), SMBCTL3_IDL_START, 1); /* Enable start detect in IDLE */ + } else { /* Disable */ + /* Disable SMB Wake-Up indication via MIWU */ + MIWU_EnableChannel(miwu_src, false); + + /* Disable SMBus Wake-up (in System Glue Function) */ + CLEAR_REG_BIT(SMB_EEN, bus); /* Disable Event assertion */ + + SET_REG_FIELD(SMBCTL3(bus), SMBCTL3_IDL_START, 0); /* Disable start detect in IDLE */ + } +} +#endif /* (MIWU_MODULE_TYPE) && (SMB_CAPABILITY_WAKEUP_SUPPORT) */ + + +#if !defined SMB_MASTER_ONLY +static DEFS_STATUS SMB_SlaveEnable_l(nuvoton_i2c_bus_t *bus, + SMB_ADDR_T addr_type, u8 addr, bool enable) +{ + unsigned long bank_flags; + u8 SmbAddrX_Addr = BUILD_FIELD_VAL(SMBADDRx_ADDR, addr) | + BUILD_FIELD_VAL(SMBADDRx_SAEN, enable); + + if (addr_type == SMB_GC_ADDR) { + SET_REG_FIELD(SMBCTL1(bus), SMBCTL1_GCMEN, enable); + return DEFS_STATUS_OK; + } + if (addr_type == SMB_ARP_ADDR) { + SET_REG_FIELD(SMBCTL3(bus), SMBCTL3_ARPMEN, enable); + return DEFS_STATUS_OK; + } + if (addr_type >= SMB_NUM_OF_ADDR) + return DEFS_STATUS_FAIL; + + /* Disable interrupts and select bank 0 for address 3 to ... */ + spin_lock_irqsave(&bus->bank_lock, bank_flags); + SMB_SelectBank(bus, SMB_BANK_0); + + /* Set and enable the address */ + REG_WRITE(SMBADDR(bus, addr_type), SmbAddrX_Addr); + + /* return to bank 1 and enable interrupts (if needed) */ + SMB_SelectBank(bus, SMB_BANK_1); + spin_unlock_irqrestore(&bus->bank_lock, bank_flags); + + return DEFS_STATUS_OK; +} + +static u8 SMB_GetSlaveAddress_l(nuvoton_i2c_bus_t *bus, SMB_ADDR_T addrEnum) +{ + unsigned long bank_flags; + u8 slaveAddress; + + /* disable interrupts and select bank 0 for address 3 to ... */ + spin_lock_irqsave(&bus->bank_lock, bank_flags); + SMB_SelectBank(bus, SMB_BANK_0); + + slaveAddress = REG_READ(SMBADDR(bus, addrEnum)); + + /* return to bank 1 and enable interrupts (if needed) */ + SMB_SelectBank(bus, SMB_BANK_1); + spin_unlock_irqrestore(&bus->bank_lock, bank_flags); + + return slaveAddress; +} + +static bool SMB_IsSlaveAddressExist(nuvoton_i2c_bus_t *bus, u8 addr) +{ + int i; + + addr |= 0x80; //Set the enable bit + + for (i = SMB_SLAVE_ADDR1; i < SMB_NUM_OF_ADDR; i++) + if (addr == SMB_GetSlaveAddress_l(bus, (SMB_ADDR_T)i)) + return true; + + return false; +} + +static DEFS_STATUS SMB_AddSlaveAddress(nuvoton_i2c_bus_t *bus, + u8 slaveAddrToAssign, bool use_PEC) +{ + int i; + DEFS_STATUS ret = DEFS_STATUS_FAIL; + + slaveAddrToAssign |= 0x80; //set the enable bit + + for (i = SMB_SLAVE_ADDR1; i < SMB_NUM_OF_ADDR; i++) { + u8 currentSlaveAddr = SMB_GetSlaveAddress_l(bus, (SMB_ADDR_T)i); + if (currentSlaveAddr == slaveAddrToAssign) { + ret = DEFS_STATUS_OK; + break; + } else if ((currentSlaveAddr & 0x7F) == 0) { + ret = SMB_SlaveEnable_l(bus, (SMB_ADDR_T)i, slaveAddrToAssign, true); + break; + } + } + + if (ret == DEFS_STATUS_OK) { + if (use_PEC) + SET_VAR_BIT(bus->PEC_mask, i); + else + CLEAR_VAR_BIT(bus->PEC_mask, i); + } + return ret; +} + +static DEFS_STATUS SMB_RemSlaveAddress(nuvoton_i2c_bus_t *bus, + u8 slaveAddrToRemove) +{ + int i; + unsigned long bank_flags; + + slaveAddrToRemove |= 0x80; //Set the enable bit + + /* disable interrupts and select bank 0 for address 3 to ... */ + spin_lock_irqsave(&bus->bank_lock, bank_flags); + SMB_SelectBank(bus, SMB_BANK_0); + + for (i = SMB_SLAVE_ADDR1; i < SMB_NUM_OF_ADDR; i++) { + if (REG_READ(SMBADDR(bus, i)) == slaveAddrToRemove) + REG_WRITE(SMBADDR(bus, i), 0); + } + + /* return to bank 1 and enable interrupts (if needed) */ + SMB_SelectBank(bus, SMB_BANK_1); + spin_unlock_irqrestore(&bus->bank_lock, bank_flags); + + return DEFS_STATUS_OK; +} + +static DEFS_STATUS SMB_SlaveGlobalCallEnable(nuvoton_i2c_bus_t *bus, + bool enable) +{ + return SMB_SlaveEnable_l(bus, SMB_GC_ADDR, 0, enable); +} + +static DEFS_STATUS SMB_SlaveARPEnable(nuvoton_i2c_bus_t *bus, bool enable) +{ + return SMB_SlaveEnable_l(bus, SMB_ARP_ADDR, 0, enable); +} + +#endif /* !SMB_MASTER_ONLY */ + + +#if !defined SMB_SLAVE_ONLY + +static bool SMB_StartMasterTransaction(nuvoton_i2c_bus_t *bus, u8 slave_addr, + u16 nwrite, u16 nread, u8 *write_data, + u8 *read_data, bool use_PEC) +{ + unsigned long lock_flags; + +#ifdef CONFIG_NPCM750_I2C_DEBUG + I2C_DEBUG("bus=%d slave_addr=%x nwrite=%d nread=%d write_data=%p " + "read_data=%p use_PEC=%d\n", bus, slave_addr, nwrite, nread, + write_data, read_data, use_PEC); + + if (nwrite && nwrite != SMB_BYTES_QUICK_PROT) { + int i; + char str[32 * 3 + 4]; + char *s = str; + + for (i = 0; (i < nwrite && i < 32); i++) + s += sprintf(s, "%02x ", write_data[i]); + + printk("write_data = %s\n", str); + } +#endif + + + /* Allow only if bus is not busy */ + if ((bus->operation_state != SMB_IDLE) +#if defined SMBUS_SIZE_CHECK + || + ((nwrite >= _32KB_) && (nwrite != SMB_BYTES_QUICK_PROT)) || + ((nread >= _32KB_) && (nread != SMB_BYTES_BLOCK_PROT) && + (nread != SMB_BYTES_QUICK_PROT)) +#endif + ) + return false; + + spin_lock_irqsave(&bus->lock, lock_flags); + + + /* Update driver state */ + bus->master_or_slave = SMB_MASTER; + bus->operation_state = SMB_MASTER_START; + if (nwrite > 0) + bus->operation = SMB_WRITE_OPER; + else + bus->operation = SMB_READ_OPER; + + bus->dest_addr = (u8)(slave_addr << 1); /* Translate 7-bit to 8-bit format */ + bus->write_data_buf = write_data; + bus->write_size = nwrite; + bus->write_index = 0; + bus->read_data_buf = read_data; + bus->read_size = nread; + bus->read_index = 0; + bus->PEC_use = use_PEC; + bus->read_block_use = false; + bus->retry_count = SMB_RETRY_MAX_COUNT; + + /* Check if transaction uses Block read protocol */ + if ((bus->read_size == SMB_BYTES_BLOCK_PROT) || + (bus->read_size == SMB_BYTES_EXCLUDE_BLOCK_SIZE_FROM_BUFFER)) { + bus->read_block_use = true; + + /* Change nread in order to configure recieve threshold to 1 */ + nread = 1; + } + + /* clear BER just in case it is set due to a previous transaction */ + REG_WRITE(SMBST(bus), MASK_FIELD(SMBST_BER)); + + /* Initiate SMBus master transaction */ + /* Generate a Start condition on the SMBus */ + if (bus->fifo_use == true) { + unsigned long bank_flags; + /* select bank 1 for FIFO registers */ + spin_lock_irqsave(&bus->bank_lock, bank_flags); + SMB_SelectBank(bus, SMB_BANK_1); + spin_unlock_irqrestore(&bus->bank_lock, bank_flags); + + /* clear FIFO and relevant status bits. */ + SET_REG_MASK(SMBFIF_CTS(bus), MASK_FIELD(SMBFIF_CTS_SLVRSTR) | + MASK_FIELD(SMBFIF_CTS_CLR_FIFO) | + MASK_FIELD(SMBFIF_CTS_RXF_TXE)); + + if (nwrite == 0) { + /* This is a read only operation. Configure the FIFO */ + /* threshold according to the needed number of bytes to read. */ + if (nread > SMBUS_FIFO_SIZE) + SET_REG_FIELD(SMBRXF_CTL(bus), SMBRXF_CTL_RX_THR, SMBUS_FIFO_SIZE); + else { + SET_REG_FIELD(SMBRXF_CTL(bus), SMBRXF_CTL_RX_THR, (u8)(nread)); + + if ((bus->read_size != SMB_BYTES_BLOCK_PROT) && + (bus->read_size != SMB_BYTES_EXCLUDE_BLOCK_SIZE_FROM_BUFFER)) + SET_REG_FIELD(SMBRXF_CTL(bus), SMBRXF_CTL_LAST_PEC, 1); + } + } + } + + SMB_Start(bus); + + spin_unlock_irqrestore(&bus->lock, lock_flags); + + return true; +} +#endif /* !SMB_SLAVE_ONLY */ + + +#if !defined SMB_MASTER_ONLY +static bool SMB_StartSlaveReceive(nuvoton_i2c_bus_t *bus, u16 nread, + u8 *read_data) +{ + + /* Allow only if bus is not busy */ + if ((bus->operation_state != SMB_SLAVE_MATCH) +#if defined SMBUS_SIZE_CHECK + || + ((nread >= _32KB_) && (nread != SMB_BYTES_BLOCK_PROT) && (nread != SMB_BYTES_QUICK_PROT)) +#endif + ) + return false; + + /* Update driver state */ + bus->operation_state = SMB_OPER_STARTED; + bus->operation = SMB_READ_OPER; + bus->read_data_buf = read_data; + bus->read_size = nread; + bus->read_index = 0; + bus->write_size = 0; + bus->write_index = 0; + + if (bus->fifo_use == true) { + if (nread > 0) { + u8 smbrxf_ctl; + + if (nread <= SMBUS_FIFO_SIZE) { + smbrxf_ctl = BUILD_FIELD_VAL(SMBRXF_CTL_THR_RXIE, 0); + smbrxf_ctl |= BUILD_FIELD_VAL(SMBRXF_CTL_RX_THR, nread); + } else { + /* if threshold_fifo != SMBUS_FIFO_SIZE set SMBRXF_CTL.THR_RXIE to 1 otherwise to 0 */ + smbrxf_ctl = BUILD_FIELD_VAL(SMBRXF_CTL_RX_THR, bus->threshold_fifo) | + BUILD_FIELD_VAL(SMBRXF_CTL_THR_RXIE, (bool)(bus->threshold_fifo != SMBUS_FIFO_SIZE)); + } + REG_WRITE(SMBRXF_CTL(bus), smbrxf_ctl); + } + + /* triggers new data reception */ + REG_WRITE(SMBST(bus), MASK_FIELD(SMBST_NMATCH)); + } + + return true; +} + +static bool SMB_StartSlaveTransmit(nuvoton_i2c_bus_t *bus, u16 nwrite, + u8 *write_data) +{ + + /* Allow only if bus is not busy */ + if ((bus->operation_state != SMB_SLAVE_MATCH) || (nwrite == 0)) + return false; + + + /* Update driver state */ + if (bus->PEC_use) + nwrite++; + + bus->operation_state = SMB_OPER_STARTED; + bus->operation = SMB_WRITE_OPER; + bus->write_data_buf = write_data; + bus->write_size = nwrite; + bus->write_index = 0; + + if (bus->fifo_use == true) { + /* triggers new data reception */ + REG_WRITE(SMBST(bus), MASK_FIELD(SMBST_NMATCH)); + + if (nwrite > 0) { + u8 smbtxf_ctl; + + if (nwrite <= SMBUS_FIFO_SIZE) + smbtxf_ctl = BUILD_FIELD_VAL + (SMBTXF_CTL_THR_TXIE, 0) | + BUILD_FIELD_VAL(SMBTXF_CTL_TX_THR, 0); + else + /* if threshold_fifo != SMBUS_FIFO_SIZE set SMBTXF_CTL.THR_TXIE to 1 otherwise to 0 */ + smbtxf_ctl = BUILD_FIELD_VAL + (SMBTXF_CTL_THR_TXIE, + (bool)(bus->threshold_fifo != SMBUS_FIFO_SIZE)) + | BUILD_FIELD_VAL(SMBTXF_CTL_TX_THR, + SMBUS_FIFO_SIZE - + bus->threshold_fifo); + + REG_WRITE(SMBTXF_CTL(bus), smbtxf_ctl); + + /* Fill the FIFO with data */ + SMB_WriteToFifo(bus, MIN(SMBUS_FIFO_SIZE, nwrite)); + } + } + + return true; +} +#endif /* !SMB_MASTER_ONLY */ + +#ifdef TBD +static bool SMB_ModuleIsBusy(nuvoton_i2c_bus_t *bus) +{ + return (READ_REG_FIELD(SMBCST(bus), SMBCST_BUSY) || + READ_REG_FIELD(SMBST(bus), SMBST_SLVSTP)); +} + +static bool SMB_BusIsBusy(nuvoton_i2c_bus_t *bus) +{ + return READ_REG_FIELD(SMBCST(bus), SMBCST_BB); +} +#endif //TBD + +#if !defined SMB_MASTER_ONLY +static DEFS_STATUS SMB_GetCurrentSlaveAddress(nuvoton_i2c_bus_t *bus, + u8 *currSlaveAddr) +{ + if (currSlaveAddr != NULL) { + *currSlaveAddr = bus->SMB_CurSlaveAddr; + return DEFS_STATUS_OK; + } + + return DEFS_STATUS_INVALID_PARAMETER; +} +#endif + +#if defined (MIWU_MODULE_TYPE) && defined (SMB_CAPABILITY_WAKEUP_SUPPORT) + +static void SMB_WakeupHandler(MIWU_SRC_T source) +{ + nuvoton_i2c_bus_t *bus; /* Module whose bus generated the wake-up */ + + /* SMB module is enabled already (otherwise a wakeup signal isn't generated) - */ + /* so no need to enable the SMB. SMB HW responds with a negative acknowledge to the Start Condition - */ + /* so either the Master Device will re-issue a Start Condition, or the upper layer will initiate a */ + /* transaction as a master */ + + /* Check wake-up source */ + for (bus = 0; bus < SMB_NUM_OF_MODULES; bus++) { + if (READ_REG_BIT(SMB_SBD, bus) && READ_REG_BIT(SMB_EEN, bus)) { + /* Clear start-bit-detected status */ + REG_WRITE(SMB_SBD, MASK_BIT(bus)); + + /* Restore SMBnCTL1 register values, because the register is being reseted when the core */ + /* switches to Idle or Deep-Idle mode. */ + SET_REG_FIELD(SMBCTL1(bus), SMBCTL1_NMINTE, ENABLE); + SMB_InterruptEnable(bus, true); + + /* Notify upper layer of wake-up */ + SMB_callback(bus, SMB_WAKE_UP_IND, 0); + } + } + +} +#endif /* (MIWU_MODULE_TYPE) && (SMB_CAPABILITY_WAKEUP_SUPPORT) */ + +static void SMB_ReadFromFifo(nuvoton_i2c_bus_t *bus, u8 bytes_in_fifo) +{ + while (bytes_in_fifo--) { + /* Keep read data */ + u8 data = REG_READ(SMBSDA(bus)); + + SMB_CalcPEC(bus, data); + if (bus->read_index < bus->read_size) { + bus->read_data_buf[bus->read_index++] = data; + if ((bus->read_index == 1) && bus->read_size == SMB_BYTES_BLOCK_PROT) + /* First byte indicates length in block protocol */ + bus->read_size = data; + } + } +} + +static void SMB_MasterFifoRead(nuvoton_i2c_bus_t *bus) +{ + u16 rcount; + u8 fifo_bytes; + SMB_STATE_IND_T ind = SMB_MASTER_DONE_IND; + + rcount = bus->read_size - bus->read_index; + + + /* In order not to change the RX_TRH during transaction (we found that this might */ + /* be problematic if it takes too much time to read the FIFO) we read the data in the */ + /* following way. If the number of bytes to read == FIFO Size + C (where C < FIFO Size) */ + /* then first read C bytes and in the next interrupt we read rest of the data. */ + if ((rcount < (2 * SMBUS_FIFO_SIZE)) && (rcount > SMBUS_FIFO_SIZE)) + fifo_bytes = (u8)(rcount - SMBUS_FIFO_SIZE); + else + fifo_bytes = READ_REG_FIELD(SMBRXF_STS(bus), SMBRXF_STS_RX_BYTES); + + if (rcount - fifo_bytes == 0) { + /* last byte is about to be read - end of transaction. */ + /* Stop should be set before reading last byte. */ +#ifdef SMB_CAPABILITY_END_OF_BUSY_SUPPORT + /* Enable "End of Busy" interrupt. */ + SET_REG_FIELD(SMBCTL1(bus), SMBCTL1_EOBINTE, 1); +#endif + SMB_Stop(bus); + + SMB_ReadFromFifo(bus, fifo_bytes); + +#if defined (SMB_CAPABILITY_HW_PEC_SUPPORT) + if ((bus->PEC_use == true) && (REG_READ(SMBPEC(bus)) != 0)) +#else + if ((bus->PEC_use == true) && (bus->crc_data != 0)) +#endif + ind = SMB_MASTER_PEC_ERR_IND; + +#ifdef SMB_CAPABILITY_END_OF_BUSY_SUPPORT + bus->operation_state = SMB_STOP_PENDING; + bus->stop_indication = ind; +#else + /* Reset state for new transaction */ + bus->operation_state = SMB_IDLE; + + /* Notify upper layer of transaction completion */ + SMB_callback(bus, ind, bus->read_index); +#endif + } else { + SMB_ReadFromFifo(bus, fifo_bytes); + rcount = bus->read_size - bus->read_index; + + if (rcount > 0) { + SET_REG_FIELD(SMBRXF_STS(bus), SMBRXF_STS_RX_THST, 1); + SET_REG_FIELD(SMBFIF_CTS(bus), SMBFIF_CTS_RXF_TXE, 1); + + if (rcount > SMBUS_FIFO_SIZE) + SET_REG_FIELD(SMBRXF_CTL(bus), SMBRXF_CTL_RX_THR, SMBUS_FIFO_SIZE); + else { + SET_REG_FIELD(SMBRXF_CTL(bus), SMBRXF_CTL_RX_THR, (u8)(rcount)); + SET_REG_FIELD(SMBRXF_CTL(bus), SMBRXF_CTL_LAST_PEC, 1); + } + } + } + +#ifdef SMB_STALL_TIMEOUT_SUPPORT + bus->stall_counter = 0; +#endif +} + +static void SMB_WriteToFifo(nuvoton_i2c_bus_t *bus, u16 max_bytes_to_send) +{ + + /* Fill the FIFO , while the FIFO is not full and there are more bytes to write */ + while ((max_bytes_to_send--) && (SMBUS_FIFO_SIZE - + READ_REG_FIELD(SMBTXF_STS(bus), + SMBTXF_STS_TX_BYTES))) { + /* write the data */ + if (bus->write_index < bus->write_size) { + if ((bus->PEC_use == true) && + ((bus->write_index + 1) == bus->write_size) && + ((bus->read_size == 0) || + (bus->master_or_slave == SMB_SLAVE))) { + /* Master send PEC in write protocol, Slave send PEC in read protocol. */ +#if defined (SMB_CAPABILITY_HW_PEC_SUPPORT) + REG_WRITE(SMBSDA(bus), REG_READ(SMBPEC(bus))); +#else + REG_WRITE(SMBSDA(bus), bus->crc_data); +#endif + bus->write_index++; + } else + SMB_WriteByte(bus, bus->write_data_buf[bus->write_index++]); + } else { + +/* define this at hal_cfg or chip file, if one wishes to use this feature. Otherwise driver will xmit 0xFF */ +#ifdef SMB_WRAP_AROUND_BUFFER + /* We're out of bytes. Ask the higher level for more bytes. Let it know that driver used all its' bytes */ + + /* clear the status bits */ + SET_REG_FIELD(SMBTXF_STS(bus), SMBTXF_STS_TX_THST, 1); + + /* Reset state for the remaining bytes transaction */ + bus->operation_state = SMB_SLAVE_MATCH; + + /* Notify upper layer of transaction completion */ + SMB_callback(bus, SMB_SLAVE_XMIT_MISSING_DATA_IND, + bus->write_index); + + REG_WRITE(SMBST(bus), MASK_FIELD(SMBST_SDAST)); +#else + SMB_WriteByte(bus, 0xFF); +#endif + } + } +} + +static bool SMB_InitClock(nuvoton_i2c_bus_t *bus, SMB_MODE_T mode, u16 bus_freq) +{ +#if defined (SMB_CAPABILITY_FAST_MODE_SUPPORT) || defined (SMB_CAPABILITY_FAST_MODE_PLUS_SUPPORT) + u16 k1 = 0; + u16 k2 = 0; + u8 dbnct = 0; +#endif + u16 sclfrq = 0; + u8 hldt = 7; + bool fastMode = false; + unsigned long bank_flags; + u32 source_clock_freq; + + source_clock_freq = bus->apb_clk; + + + /* Frequency is less or equal to 100 KHz */ + if (bus_freq <= SMBUS_FREQ_100KHz) { + /* Set frequency: */ + /* SCLFRQ = T(SCL)/4/T(CLK) = FREQ(CLK)/4/FREQ(SCL) = FREQ(CLK) / ( FREQ(SCL)*4 ) */ + sclfrq = (u16)((source_clock_freq / ((u32)bus_freq * _1KHz_ * 4))); // bus_freq is KHz + + /* Check whether requested frequency can be achieved in current CLK */ + if ((sclfrq < SCLFRQ_MIN) || (sclfrq > SCLFRQ_MAX)) + return false; + + if (source_clock_freq >= 40000000) + hldt = 17; + else if (source_clock_freq >= 12500000) + hldt = 15; + else + hldt = 7; + } + +#ifdef SMB_CAPABILITY_FAST_MODE_SUPPORT + + /* Frequency equal to 400 KHz */ + + else if (bus_freq == SMBUS_FREQ_400KHz) { + sclfrq = 0; + fastMode = true; + + if ((mode == SMB_MASTER && source_clock_freq < 7500000) || + (mode == SMB_SLAVE && source_clock_freq < 10000000)) + /* 400KHz cannot be supported for master core clock < 7.5 MHz or slave core clock < 10 MHz */ + return false; + + /* Master or Slave with frequency > 25 MHz */ + if (mode == SMB_MASTER || source_clock_freq > 25000000) { + /* Set HLDT: */ + /* SDA hold time: (HLDT-7) * T(CLK) >= 300 */ + /* HLDT = 300/T(CLK) + 7 = 300 * FREQ(CLK) + 7 */ + hldt = (u8)DIV_CEILING((300 * (source_clock_freq / _1KHz_)), ((u32)_1MHz_)) + 7; + + if (mode == SMB_MASTER) { + /* Set k1: */ + /* Clock low time: k1 * T(CLK) - T(SMBFO) >= 1300 */ + /* T(SMBRO) = T(SMBFO) = 300 */ + /* k1 = (1300 + T(SMBFO)) / T(CLK) = 1600 * FREQ(CLK) */ + k1 = ROUND_UP(((u16)DIV_CEILING((1600 * (source_clock_freq / _1KHz_)), ((u32)_1MHz_))), 2); + + /* Set k2: */ + /* START setup: (k2 - 1) * T(CLK) - T(SMBFO) >= 600 */ + /* T(SMBRO) = T(SMBFO) = 300 */ + /* k2 = (600 + T(SMBFO)) / T(CLK) + 1 = 900 * FREQ(CLK) + 1 */ + k2 = ROUND_UP(((u16)DIV_CEILING((900 * (source_clock_freq / _1KHz_)), ((u32)_1MHz_)) + 1), 2); + + /* Check whether requested frequency can be achieved in current CLK */ + if ((k1 < SCLFRQ_MIN) || (k1 > SCLFRQ_MAX) || (k2 < SCLFRQ_MIN) || (k2 > SCLFRQ_MAX)) + return false; + } + } + + /* Slave with frequency 10-25 MHz */ + else { + hldt = 7; + dbnct = 2; + } + } +#endif //SMB_CAPABILITY_FAST_MODE_SUPPORT + +#ifdef SMB_CAPABILITY_FAST_MODE_PLUS_SUPPORT + + /* Frequency equal to 1 MHz */ + else if (bus_freq == SMBUS_FREQ_1MHz) { + sclfrq = 0; + fastMode = true; + + if ((mode == SMB_MASTER && source_clock_freq < 15000000) || + (mode == SMB_SLAVE && source_clock_freq < 24000000)) + + /* 1MHz cannot be supported for master core clock < 15 MHz or slave core clock < 24 MHz */ + return false; + + /* Master or Slave with frequency > 40 MHz */ + if (mode == SMB_MASTER || source_clock_freq > 40000000) { + + /* Set HLDT: */ + /* SDA hold time: (HLDT-7) * T(CLK) >= 120 */ + /* HLDT = 120/T(CLK) + 7 = 120 * FREQ(CLK) + 7 */ + hldt = (u8)DIV_CEILING((120 * (source_clock_freq / _1KHz_)), ((u32)_1MHz_)) + 7; + + if (mode == SMB_MASTER) { + + /* Set k1: */ + /* Clock low time: k1 * T(CLK) - T(SMBFO) >= 500 */ + /* T(SMBRO) = T(SMBFO) = 120 */ + /* k1 = (500 + T(SMBFO)) / T(CLK) = 620 * FREQ(CLK) */ + k1 = ROUND_UP(((u16)DIV_CEILING((620 * (source_clock_freq / _1KHz_)), ((u32)_1MHz_))), 2); + + + /* Set k2: */ + /* START setup: (k2 - 1) * T(CLK) - T(SMBFO) >= 260 */ + /* T(SMBRO) = T(SMBFO) = 120 */ + /* k2 = (260 + T(SMBFO)) / T(CLK) + 1 = 380 * FREQ(CLK) + 1 */ + k2 = ROUND_UP(((u16)DIV_CEILING((380 * (source_clock_freq / _1KHz_)), ((u32)_1MHz_)) + 1), 2); + + + /* Check whether requested frequency can be achieved in current CLK */ + if ((k1 < SCLFRQ_MIN) || (k1 > SCLFRQ_MAX) || (k2 < SCLFRQ_MIN) || (k2 > SCLFRQ_MAX)) { + return false; + } + } + } + + /* Slave with frequency 24-40 MHz */ + else { + hldt = 7; + dbnct = 2; + } + } +#endif //SMB_CAPABILITY_FAST_MODE_PLUS_SUPPORT + + + /* Frequency larger than 1 MHz */ + else + return false; + + + + /* After clock parameters calculation update the register */ + SET_REG_FIELD(SMBCTL2(bus), SMBCTL2_SCLFRQ6_0, + READ_VAR_FIELD(sclfrq, SCLFRQ_0_TO_6)); + SET_REG_FIELD(SMBCTL3(bus), SMBCTL3_SCLFRQ8_7, + READ_VAR_FIELD(sclfrq, SCLFRQ_7_TO_8)); + SET_REG_FIELD(SMBCTL3(bus), SMBCTL3_400K_MODE, fastMode); + + + /* Select Bank 0 to access SMBCTL4/SMBCTL5 */ + spin_lock_irqsave(&bus->bank_lock, bank_flags); + SMB_SelectBank(bus, SMB_BANK_0); + +#if defined (SMB_CAPABILITY_FAST_MODE_SUPPORT) || defined (SMB_CAPABILITY_FAST_MODE_PLUS_SUPPORT) + if (bus_freq >= SMBUS_FREQ_400KHz) { + + /* k1 and k2 are relevant for master mode only */ + if (mode == SMB_MASTER) { + + /* Set SCL Low/High Time: */ + /* k1 = 2 * SCLLT7-0 -> Low Time = k1 / 2 */ + /* k2 = 2 * SCLLT7-0 -> High Time = k2 / 2 */ + REG_WRITE(SMBSCLLT(bus), (u8)k1 / 2); + REG_WRITE(SMBSCLHT(bus), (u8)k2 / 2); + } + + /* DBNCT is relevant for slave mode only */ + else + SET_REG_FIELD(SMBCTL5(bus), SMBCTL5_DBNCT, dbnct); + } +#endif + + SET_REG_FIELD(SMBCTL4(bus), SMBCTL4_HLDT, hldt); + + + /* Return to Bank 1 */ + SMB_SelectBank(bus, SMB_BANK_1); + spin_unlock_irqrestore(&bus->bank_lock, bank_flags); + + return true; +} + + +#if defined (SMB_CAPABILITY_TIMEOUT_SUPPORT) + +static void SMB_EnableTimeout(nuvoton_i2c_bus_t *bus, bool enable) +{ + u8 toCkDiv; + u8 smbEnabled; + u8 smbctl1 = 0; + + if (enable) { + + /* TO_CKDIV may be changed only when the SMB is disabled */ + smbEnabled = READ_REG_FIELD(SMBCTL2(bus), SMBCTL2_ENABLE); + + /* If SMB is enabled - disable the SMB module */ + if (smbEnabled) { + + /* Save smbctl1 relevant bits. It is being cleared when the module is disabled */ + smbctl1 = REG_READ(SMBCTL1(bus)) & (MASK_FIELD(SMBCTL1_GCMEN) | MASK_FIELD(SMBCTL1_INTEN) | MASK_FIELD(SMBCTL1_NMINTE)); + + /* Disable the SMB module */ + SET_REG_FIELD(SMBCTL2(bus), SMBCTL2_ENABLE, DISABLE); + } + + /* Clear EO_BUSY pending bit */ + SET_REG_FIELD(SMBT_OUT(bus), SMBT_OUT_T_OUTST, 1); + + /* Configure the division of the SMB Module Basic clock (BCLK) to generate the 1 KHz clock of the */ + /* timeout detector. */ + /* The timeout detector has an “n+1” divider, controlled by TO_CKDIV and a fixed divider by 1000. */ + /* Together they generate the 1 ms clock cycle */ + toCkDiv = (u8)(((bus->apb_clk / _1KHz_) / 1000) - 1); + + /* Set the bus timeout clock divisor */ + SET_REG_FIELD(SMBT_OUT(bus), SMBT_OUT_TO_CKDIV, toCkDiv); + + /* If SMB was enabled - re-enable the SMB module */ + if (smbEnabled) { + + /* Enable the SMB module */ + (void)SMB_Enable(bus); + + /* Restore smbctl1 status */ + REG_WRITE(SMBCTL1(bus), smbctl1); + } + } + + + /* Enable/Disable the bus timeout interrupt */ + SET_REG_FIELD(SMBT_OUT(bus), SMBT_OUT_T_OUTIE, enable); +} +#endif + +static void SMB_InterruptHandler(nuvoton_i2c_bus_t *bus) +{ + + /* A negative acknowledge has occurred */ + if (READ_REG_FIELD(SMBST(bus), SMBST_NEGACK)) { + if (bus->fifo_use) { + + /* if there are still untransmitted bytes in TX FIFO reduce them from write_index */ + bus->write_index -= READ_REG_FIELD(SMBTXF_STS(bus), SMBTXF_STS_TX_BYTES); + + /* clear the FIFO */ + REG_WRITE(SMBFIF_CTS(bus), MASK_FIELD(SMBFIF_CTS_CLR_FIFO)); + } + + /* In slave write operation, NACK is OK, otherwise it is a problem */ + if (!((bus->master_or_slave == SMB_SLAVE) && + (bus->write_index != 0) && + (bus->write_index == bus->write_size))) + /* Either not slave, or number of bytes sent to master less than required */ + /* In either case notify upper layer. If we are slave - the upper layer */ + /* should still wait for a Slave Stop. */ + { +#if !defined SMB_SLAVE_ONLY + if ((bus->master_or_slave == SMB_MASTER) && + READ_REG_FIELD(SMBST(bus), SMBST_MASTER)) { + /* Only current master is allowed to issue Stop Condition */ + SMB_MasterAbort(bus); + } +#endif /* !SMB_SLAVE_ONLY */ + + //REG_WRITE(SMBST(bus), MASK_FIELD(SMBST_NEGACK)); + bus->operation_state = SMB_IDLE; + SMB_callback(bus, SMB_NACK_IND, bus->write_index); + } + + /* else: */ + /* Slave has to wait for SMB_STOP to decide this is the end of the transaction. */ + /* Therefore transaction is not yet considered as done */ + /* */ + /* In Master mode, NEGACK should be cleared only after generating STOP. */ + /* In such case, the bus is released from stall only after the software clears NEGACK */ + /* bit. Then a Stop condition is sent. */ + REG_WRITE(SMBST(bus), MASK_FIELD(SMBST_NEGACK)); + + return; + } + + + /* A Bus Error has been identified */ + if (READ_REG_FIELD(SMBST(bus), SMBST_BER)) { + + /* Check whether bus arbitration or Start or Stop during data transfer */ +#if !defined SMB_SLAVE_ONLY + + /* Bus arbitration problem should not result in recovery */ + if ((bus->master_or_slave == SMB_MASTER)) { + if (READ_REG_FIELD(SMBST(bus), SMBST_MASTER)) { + + /* Only current master is allowed to issue Stop Condition */ + SMB_MasterAbort(bus); + } else { + + /* Bus arbitration loss */ + if (--bus->retry_count > 0) { + /* Perform a retry (generate a Start condition as soon as the SMBus is free) */ + REG_WRITE(SMBST(bus), MASK_FIELD(SMBST_BER)); + SMB_Start(bus); + return; + } + } + } +#if !defined SMB_MASTER_ONLY +else +#endif +#endif /* !SMB_SLAVE_ONLY */ +#if !defined SMB_MASTER_ONLY + if (bus->master_or_slave == SMB_SLAVE) + + /* Reset the module */ + SMB_Reset(bus); +#endif + + REG_WRITE(SMBST(bus), MASK_FIELD(SMBST_BER)); + bus->operation_state = SMB_IDLE; + SMB_callback(bus, SMB_BUS_ERR_IND, SMB_GetIndex(bus)); + return; + } + +#if defined (SMB_CAPABILITY_TIMEOUT_SUPPORT) + + /* A Bus Timeout has been identified */ + if ((READ_REG_FIELD(SMBT_OUT(bus), SMBT_OUT_T_OUTIE) == 1) && /* bus timeout interrupt is on */ + (READ_REG_FIELD(SMBT_OUT(bus), SMBT_OUT_T_OUTST))) { /* and bus timeout status is set */ +#if !defined SMB_SLAVE_ONLY + if (bus->master_or_slave == SMB_MASTER) { + + /* Only current master is allowed to issue Stop Condition */ + SMB_MasterAbort(bus); + } +#if !defined SMB_MASTER_ONLY +else +#endif +#endif /* !SMB_SLAVE_ONLY */ +#if !defined SMB_MASTER_ONLY + if (bus->master_or_slave == SMB_SLAVE) { + + /* Reset the module */ + + SMB_Reset(bus); + } +#endif + + SET_REG_FIELD(SMBT_OUT(bus), SMBT_OUT_T_OUTST, 1); /* Clear EO_BUSY pending bit */ + bus->operation_state = SMB_IDLE; + SMB_callback(bus, SMB_BUS_ERR_IND, SMB_GetIndex(bus)); + return; + } +#endif + +#ifdef SMB_CAPABILITY_END_OF_BUSY_SUPPORT + + /* A Master End of Busy (meaning Stop Condition happened) */ + + if ((READ_REG_FIELD(SMBCTL1(bus), SMBCTL1_EOBINTE) == 1) && /* End of Busy interrupt is on */ + (READ_REG_FIELD(SMBCST3(bus), SMBCST3_EO_BUSY))) { /* and End of Busy is set */ + SET_REG_FIELD(SMBCTL1(bus), SMBCTL1_EOBINTE, 0); /* Disable "End of Busy" interrupt */ + SET_REG_FIELD(SMBCST3(bus), SMBCST3_EO_BUSY, 1); /* Clear EO_BUSY pending bit */ + + bus->operation_state = SMB_IDLE; + + if ((bus->write_size == SMB_BYTES_QUICK_PROT) || + (bus->read_size == SMB_BYTES_QUICK_PROT) || + (bus->read_size == 0)) { + SMB_callback(bus, bus->stop_indication, 0); + } else { + SMB_callback(bus, bus->stop_indication, + bus->read_index); + } + return; + } +#endif + +#if !defined SMB_MASTER_ONLY + + /* A Slave Stop Condition has been identified */ + + if (READ_REG_FIELD(SMBST(bus), SMBST_SLVSTP)) { + SMB_STATE_IND_T ind; + ASSERT(bus->master_or_slave == SMB_SLAVE); + REG_WRITE(SMBST(bus), MASK_FIELD(SMBST_SLVSTP)); + + + /* Check whether bus arbitration or Start or Stop during data transfer */ + bus->operation_state = SMB_IDLE; + if (bus->fifo_use) { + if (bus->operation == SMB_READ_OPER) { + SMB_ReadFromFifo(bus, READ_REG_FIELD(SMBRXF_STS(bus), SMBRXF_STS_RX_BYTES)); + + /* Be prepared for new transactions */ + //bus->operation_state = SMB_IDLE; + + /* if PEC is not used or PEC is used and PEC is correct */ + if (bus->PEC_use == false || +#if defined (SMB_CAPABILITY_HW_PEC_SUPPORT) + (REG_READ(SMBPEC(bus)) == 0) +#else + (bus->crc_data == 0) +#endif + ){ + ind = SMB_SLAVE_DONE_IND; + } + + /* PEC value is not correct */ + else { + ind = SMB_SLAVE_PEC_ERR_IND; + } + SMB_callback(bus, + /* Notify upper layer that illegal data received */ + ind, + bus->read_index); + } + if (bus->operation == SMB_WRITE_OPER) { + //bus->operation_state = SMB_IDLE; + SMB_callback(bus, SMB_SLAVE_DONE_IND, + bus->write_index); + } + + SET_REG_MASK(SMBFIF_CTS(bus), MASK_FIELD(SMBFIF_CTS_SLVRSTR) | + MASK_FIELD(SMBFIF_CTS_CLR_FIFO) | MASK_FIELD(SMBFIF_CTS_RXF_TXE)); + } + + /* FIFO is not used */ + else { + if (bus->operation == SMB_READ_OPER) { + + /* if PEC is not used or PEC is used and PEC is correct */ +#if defined (SMB_CAPABILITY_HW_PEC_SUPPORT) + if (bus->PEC_use == false || + (REG_READ(SMBPEC(bus)) == 0)) +#else + if (bus->PEC_use == false || + (bus->crc_data == 0)) +#endif + /* Notify upper layer of missing data or all data received */ + ind = SMB_SLAVE_DONE_IND; + /* PEC value is not correct */ + else + ind = SMB_SLAVE_PEC_ERR_IND; + + SMB_callback(bus, ind, bus->read_index); + } else + //bus->operation_state = SMB_IDLE; + SMB_callback(bus, SMB_SLAVE_DONE_IND, + bus->write_index); + } + + return; + } + + /* A Slave restart Condition has been identified */ + if (bus->fifo_use && READ_REG_FIELD(SMBFIF_CTS(bus), SMBFIF_CTS_SLVRSTR)) { + ASSERT(bus->master_or_slave == SMB_SLAVE); + + if (bus->operation == SMB_READ_OPER) { + SMB_ReadFromFifo(bus, READ_REG_FIELD(SMBRXF_STS(bus), SMBRXF_STS_RX_BYTES)); + } + REG_WRITE(SMBFIF_CTS(bus), MASK_FIELD(SMBFIF_CTS_SLVRSTR)); + } + + /* A Slave Address Match has been identified */ + if (READ_REG_FIELD(SMBST(bus), SMBST_NMATCH)) { + bool slave_tx; + SMB_STATE_IND_T ind = SMB_NO_STATUS_IND; + u8 info = 0; + + if (bus->fifo_use == false) + REG_WRITE(SMBST(bus), MASK_FIELD(SMBST_NMATCH)); + + if (READ_REG_FIELD(SMBST(bus), SMBST_XMIT)) + slave_tx = true; + else + slave_tx = false; + + if (bus->operation_state == SMB_IDLE) { + /* Indicate Slave Mode */ + if (slave_tx) + ind = SMB_SLAVE_XMIT_IND; + else + ind = SMB_SLAVE_RCV_IND; + + /* Check which type of address match */ + if (READ_REG_FIELD(SMBCST(bus), SMBCST_MATCH)) { + u16 address_match = ((REG_READ(SMBCST3(bus)) & 0x7) << 7) | + (REG_READ(SMBCST2(bus)) & 0x7F); + info = 0; + ASSERT(address_match); + while (address_match) { + if (address_match & 1) + break; + info++; + address_match = address_match >> 1; + } + + bus->SMB_CurSlaveAddr = READ_VAR_FIELD(SMB_GetSlaveAddress_l(bus, (SMB_ADDR_T)info), SMBADDRx_ADDR); + if (READ_VAR_BIT(bus->PEC_mask, info) == 1) { + bus->PEC_use = true; + bus->crc_data = 0; + if (slave_tx) + SMB_CalcPEC(bus, (bus->SMB_CurSlaveAddr & 0x7F) << 1 | 1); + else + SMB_CalcPEC(bus, (bus->SMB_CurSlaveAddr & 0x7F) << 1); + } else + bus->PEC_use = false; + } else { + if (READ_REG_FIELD(SMBCST(bus), SMBCST_GCMATCH)) { + info = (u8)SMB_GC_ADDR; + bus->SMB_CurSlaveAddr = 0; + } else { + if (READ_REG_FIELD(SMBCST(bus), SMBCST_ARPMATCH)) { + info = (u8)SMB_ARP_ADDR; + bus->SMB_CurSlaveAddr = 0x61; + } + } + } + } else { + /* Slave match can happen in two options: */ + /* 1. Start, SA, read ( slave read without further ado). */ + /* 2. Start, SA, read , data , restart, SA, read, ... ( salve read in fragmented mode) */ + /* 3. Start, SA, write, data, restart, SA, read, .. ( regular write-read mode) */ + if (((bus->operation_state == SMB_OPER_STARTED) && + (bus->operation == SMB_READ_OPER) && + (bus->master_or_slave == SMB_SLAVE) && + slave_tx) || + ((bus->master_or_slave == SMB_SLAVE) && + !slave_tx)) + /* slave transmit after slave receive w/o Slave Stop implies repeated start */ + { + ind = SMB_SLAVE_RESTART_IND; + info = (u8)(bus->read_index); + SMB_CalcPEC(bus, (bus->SMB_CurSlaveAddr & 0x7F) << 1 | 1); + } + } + + /* Address match automatically implies slave mode */ + ASSERT(!READ_REG_FIELD(SMBST(bus), SMBST_MASTER)); + bus->master_or_slave = SMB_SLAVE; + bus->operation_state = SMB_SLAVE_MATCH; + + /* Notify upper layer */ + /* Upper layer must at this stage call the driver routine for slave tx or rx, */ + /* to eliminate a condition of slave being notified but not yet starting */ + /* transaction - and thus an endless interrupt from SDAST for the slave RCV or TX ! */ + SMB_callback(bus, ind, info); + +#ifdef SMB_RECOVERY_SUPPORT + + /* By now, SMB operation state should have been changed from MATCH to SMB_OPER_STARTED. */ + /* If state hasn't been changed already, this may suggest that the SMB slave is not ready to */ + /* transmit or receive data. */ + /* */ + /* In addition, when using FIFO, NMATCH bit is cleared only when moving to SMB_OPER_STARTED state. */ + /* If NMATCH is not cleared, we would get an endless SMB interrupt. */ + /* Therefore, Abort the slave, such that SMB HW and state machine return to a default, functional */ + /* state. */ + if (bus->operation_state == SMB_SLAVE_MATCH) { + SMB_SlaveAbort(bus); + SMB_callback(bus, SMB_BUS_ERR_IND, SMB_GetIndex(bus)); + return; + } + + /* Slave abort data */ + /* if the SMBus's status is not match current status register of XMIT */ + /* the Slave device will enter dead-lock and stall bus forever */ + /* Add this check rule to avoid this condition */ + if ((bus->operation == SMB_READ_OPER && ind == SMB_SLAVE_XMIT_IND) || + (bus->operation == SMB_WRITE_OPER && ind == SMB_SLAVE_RCV_IND)) { + SMB_SlaveAbort(bus); + SMB_callback(bus, SMB_BUS_ERR_IND, SMB_GetIndex(bus)); + return; + } +#endif + + /* If none of the above - BER should occur */ + } +#endif /* !SMB_MASTER_ONLY */ + +#if !defined SMB_SLAVE_ONLY + + /* Address sent and requested stall occurred (Master mode) */ + if (READ_REG_FIELD(SMBST(bus), SMBST_STASTR)) { + ASSERT(READ_REG_FIELD(SMBST(bus), SMBST_MASTER)); + ASSERT(bus->master_or_slave == SMB_MASTER); + + /* Check for Quick Command SMBus protocol */ + if ((bus->write_size == SMB_BYTES_QUICK_PROT) || + (bus->read_size == SMB_BYTES_QUICK_PROT)) { + + /* No need to write any data bytes - reached here only in Quick Command */ +#ifdef SMB_CAPABILITY_END_OF_BUSY_SUPPORT + + /* Enable "End of Busy" interrupt before issuing a STOP condition. */ + SET_REG_FIELD(SMBCTL1(bus), SMBCTL1_EOBINTE, 1); +#endif + SMB_Stop(bus); + + + /* Update status */ +#ifdef SMB_CAPABILITY_END_OF_BUSY_SUPPORT + bus->operation_state = SMB_STOP_PENDING; + bus->stop_indication = SMB_MASTER_DONE_IND; +#else + bus->operation_state = SMB_IDLE; + + + /* Notify upper layer */ + SMB_callback(bus, SMB_MASTER_DONE_IND, 0); +#endif + } else if (bus->read_size == 1) + + /* Receiving one byte only - set NACK after ensuring slave ACKed the address byte */ + SMB_Nack(bus); + + + /* Reset stall-after-address-byte */ + SMB_StallAfterStart(bus, false); + + + /* Clear stall only after setting STOP */ + REG_WRITE(SMBST(bus), MASK_FIELD(SMBST_STASTR)); + return; + } +#endif /* !SMB_SLAVE_ONLY */ + + + /* SDA status is set - transmit or receive, master or slave */ + if (READ_REG_FIELD(SMBST(bus), SMBST_SDAST) || + (bus->fifo_use && + (READ_REG_FIELD(SMBRXF_STS(bus), SMBRXF_STS_RX_THST) || READ_REG_FIELD(SMBTXF_STS(bus), SMBTXF_STS_TX_THST)))) { + /* Status Bit is cleared by writing to or reading from SDA (depending on current direction) */ +#if !defined SMB_SLAVE_ONLY + + /* Handle successful bus mastership */ + if (bus->master_or_slave == SMB_MASTER) { + if (bus->operation_state == SMB_IDLE) { + + /* Perform SMB recovery in Master mode, where state is IDLE, which is an illegal state */ + SMB_MasterAbort(bus); + SMB_callback(bus, SMB_BUS_ERR_IND, 0); + return; + } else if (bus->operation_state == SMB_MASTER_START) { + if (READ_REG_FIELD(SMBST(bus), SMBST_MASTER)) { + u8 addr_byte = bus->dest_addr; + + bus->crc_data = 0; + /* Check for Quick Command SMBus protocol */ + if ((bus->write_size == SMB_BYTES_QUICK_PROT) || + (bus->read_size == SMB_BYTES_QUICK_PROT)) + /* Need to stall after successful completion of sending address byte */ + SMB_StallAfterStart(bus, true); + /* Prepare address byte */ + if (bus->write_size == 0) { + if (bus->read_size == 1) + /* Receiving one byte only - stall after successful completion of sending */ + /* address byte. If we NACK here, and slave doesn't ACK the address, we might */ + /* unintentionally NACK the next multi-byte read */ + SMB_StallAfterStart(bus, true); + + /* Set direction to Read */ + addr_byte |= (u8)0x1; + bus->operation = SMB_READ_OPER; + } else + bus->operation = SMB_WRITE_OPER; + /* Write the address to the bus */ + SMB_WriteByte(bus, addr_byte); + bus->operation_state = SMB_OPER_STARTED; + } + } else + + /* SDA status is set - transmit or receive: Handle master mode */ + if (bus->operation_state == SMB_OPER_STARTED) { + if (bus->operation == SMB_WRITE_OPER) { + u16 wcount; + + if ((bus->fifo_use == true)) + SET_REG_FIELD(SMBTXF_STS(bus), SMBTXF_STS_TX_THST, 1); + + + /* Master write operation - perform write of required number of bytes */ + if (bus->write_index == bus->write_size) { + if ((bus->fifo_use == true) && (READ_REG_FIELD(SMBTXF_STS(bus), SMBTXF_STS_TX_BYTES) > 0)) + /* No more bytes to send (to add to the FIFO), however the FIFO is not empty */ + /* yet. It is still in the middle of transmitting. Currency there is nothing */ + /* to do except for waiting to the end of the transmission. */ + /* We will get an interrupt when the FIFO will get empty. */ + return; + + if (bus->read_size == 0) { + /* all bytes have been written, in a pure write operation */ +#ifdef SMB_CAPABILITY_END_OF_BUSY_SUPPORT + /* Enable "End of Busy" interrupt. */ + SET_REG_FIELD(SMBCTL1(bus), SMBCTL1_EOBINTE, 1); +#endif + // Issue a STOP condition on the bus + SMB_Stop(bus); + // Clear SDA Status bit (by writing dummy byte) + SMB_WriteByte(bus, 0xFF); + +#ifdef SMB_CAPABILITY_END_OF_BUSY_SUPPORT + bus->operation_state = SMB_STOP_PENDING; + bus->stop_indication = SMB_MASTER_DONE_IND; +#else + // Reset state for new transaction + bus->operation_state = SMB_IDLE; + // Notify upper layer of transaction completion + SMB_callback(bus, SMB_MASTER_DONE_IND, 0); +#endif + } else { + /* last write-byte written on previous interrupt - need to restart & send slave address */ + if ((bus->PEC_use == true) && + (bus->read_size < SMB_BYTES_EXCLUDE_BLOCK_SIZE_FROM_BUFFER)) // PEC is used but the protocol is not block read protocol + // then we add extra bytes for PEC support + bus->read_size += 1; + + if (bus->fifo_use == true) { + if (((bus->read_size == 1) || + bus->read_size == SMB_BYTES_EXCLUDE_BLOCK_SIZE_FROM_BUFFER || + bus->read_size == SMB_BYTES_BLOCK_PROT)) { // SMBus Block read transaction. + + REG_WRITE(SMBTXF_CTL(bus), 0); + REG_WRITE(SMBRXF_CTL(bus), 1); + } else { + + if (bus->read_size > SMBUS_FIFO_SIZE) + SET_REG_FIELD(SMBRXF_CTL(bus), SMBRXF_CTL_RX_THR, SMBUS_FIFO_SIZE); + else { + // clear the status bits + SET_REG_FIELD(SMBRXF_CTL(bus), SMBRXF_CTL_RX_THR, (u8)bus->read_size); + SET_REG_FIELD(SMBRXF_CTL(bus), SMBRXF_CTL_LAST_PEC, 1); + } + } + } + + + /* Generate (Repeated) Start upon next write to SDA */ + SMB_Start(bus); + + if (bus->read_size == 1) + + /* Receiving one byte only - stall after successful completion of sending */ + /* address byte. If we NACK here, and slave doesn't ACK the address, we */ + /* might unintentionally NACK the next multi-byte read */ + + SMB_StallAfterStart(bus, true); + + /* send the slave address in read direction */ + SMB_WriteByte(bus, bus->dest_addr | 0x1); + + /* Next interrupt will occur on read */ + bus->operation = SMB_READ_OPER; + + } + } else { + if ((bus->PEC_use == true) && (bus->write_index == 0) + && (bus->read_size == 0))// extra bytes for PEC support + bus->write_size += 1; + + /* write next byte not last byte and not slave address */ + if ((bus->fifo_use == false) || (bus->write_size == 1)) { + if ((bus->PEC_use == true) && (bus->read_size == 0) && + (bus->write_index + 1 == bus->write_size)) { // Master write protocol to send PEC byte. +#if defined (SMB_CAPABILITY_HW_PEC_SUPPORT) + REG_WRITE(SMBSDA(bus), REG_READ(SMBPEC(bus))); +#else + REG_WRITE(SMBSDA(bus), bus->crc_data); +#endif + bus->write_index++; + } else + SMB_WriteByte(bus, bus->write_data_buf[bus->write_index++]); + } + // FIFO is used + else { + wcount = bus->write_size - bus->write_index; + if (wcount > SMBUS_FIFO_SIZE) + /* data to send is more then FIFO size. */ + /* Configure the FIFO interrupt to be mid of FIFO. */ + REG_WRITE(SMBTXF_CTL(bus), BUILD_FIELD_VAL(SMBTXF_CTL_THR_TXIE, 1) | (SMBUS_FIFO_SIZE / 2)); + else if ((wcount > SMBUS_FIFO_SIZE / 2) && (bus->write_index != 0)) + /* write_index != 0 means that this is not the first write. */ + /* since interrupt is in the mid of FIFO, only half of the fifo is empty. */ + /* Continue to configure the FIFO interrupt to be mid of FIFO. */ + REG_WRITE(SMBTXF_CTL(bus), BUILD_FIELD_VAL(SMBTXF_CTL_THR_TXIE, 1) | (SMBUS_FIFO_SIZE / 2)); + else { +#if defined (SMB_CAPABILITY_HW_PEC_SUPPORT) + if ((bus->PEC_use) && (wcount > 1)) + wcount--; //put the PEC byte last after the FIFO becomes empty. +#endif + /* This is the first write (write_index = 0) and data to send is less or */ + /* equal to FIFO size. */ + /* Or this is the last write and data to send is less or equal half FIFO */ + /* size. */ + /* In both cases disable the FIFO threshold interrupt. */ + /* The next interrupt will happen after the FIFO will get empty. */ + REG_WRITE(SMBTXF_CTL(bus), (u8)0); + } + + SMB_WriteToFifo(bus, wcount); + SET_REG_FIELD(SMBTXF_STS(bus), SMBTXF_STS_TX_THST, 1); //clear status bit +#ifdef SMB_STALL_TIMEOUT_SUPPORT + bus->stall_counter = 0; +#endif + } + } + } else if (bus->operation == SMB_READ_OPER) { + u16 block_zero_bytes; + /* Master read operation (pure read or following a write operation). */ + + /* Initialize number of bytes to include only the first byte (presents a case where */ + /* number of bytes to read is zero); add PEC if applicable */ + block_zero_bytes = 1; + if (bus->PEC_use == true) + block_zero_bytes++; + + /* Perform master read, distinguishing between last byte and the rest of the */ + /* bytes. The last byte should be read when the clock is stopped */ + if ((bus->read_index < (bus->read_size - 1)) || + bus->fifo_use == true) { + u8 data; + + /* byte to be read is not the last one */ + /* Check if byte-before-last is about to be read */ + if ((bus->read_index == (bus->read_size - 2)) && + bus->fifo_use == false) + + /* Set nack before reading byte-before-last, so that nack will be generated */ + /* after receive of last byte */ + SMB_Nack(bus); + + //if (!SMB_ReadByte(bus, &data)) + if (!READ_REG_FIELD(SMBST(bus), SMBST_SDAST)) { + /* No data available - reset state for new transaction */ + bus->operation_state = SMB_IDLE; + + /* Notify upper layer of transaction completion */ + SMB_callback(bus, SMB_NO_DATA_IND, bus->read_index); + } else if (bus->read_index == 0) { + if (bus->read_size == SMB_BYTES_EXCLUDE_BLOCK_SIZE_FROM_BUFFER || + bus->read_size == SMB_BYTES_BLOCK_PROT) { + (void)SMB_ReadByte(bus, &data); + + /* First byte indicates length in block protocol */ + if (bus->read_size == SMB_BYTES_EXCLUDE_BLOCK_SIZE_FROM_BUFFER) + bus->read_size = data; + else { + bus->read_data_buf[bus->read_index++] = data; + bus->read_size = data + 1; + } + + if (bus->PEC_use == true) { + bus->read_size += 1; + data += 1; + } + + if (bus->fifo_use == true) { + SET_REG_FIELD(SMBRXF_STS(bus), SMBRXF_STS_RX_THST, 1); + SET_REG_FIELD(SMBTXF_STS(bus), SMBTXF_STS_TX_THST, 1); + //SET_REG_FIELD(SMBFIF_CTS(bus), SMBFIF_CTS_CLR_FIFO, 1); + SET_REG_FIELD(SMBFIF_CTS(bus), SMBFIF_CTS_RXF_TXE, 1); + if (data > SMBUS_FIFO_SIZE) + SET_REG_FIELD(SMBRXF_CTL(bus), SMBRXF_CTL_RX_THR, SMBUS_FIFO_SIZE); + else { + if (data == 0) + data = 1; + + /* clear the status bits */ + SET_REG_FIELD(SMBRXF_CTL(bus), SMBRXF_CTL_RX_THR, (u8)data); + SET_REG_FIELD(SMBRXF_CTL(bus), SMBRXF_CTL_LAST_PEC, 1); + } + } + } else { + if (bus->fifo_use == false) { + (void)SMB_ReadByte(bus, &data); + bus->read_data_buf[bus->read_index++] = data; + } else { + SET_REG_FIELD(SMBTXF_STS(bus), SMBTXF_STS_TX_THST, 1); + SMB_MasterFifoRead(bus); + } + } + + } else { + if (bus->fifo_use == true) { // FIFO in used. + if ((bus->read_size == block_zero_bytes) && (bus->read_block_use == true)) { +#ifdef SMB_CAPABILITY_END_OF_BUSY_SUPPORT + /* Enable "End of Busy" interrupt */ + SET_REG_FIELD(SMBCTL1(bus), SMBCTL1_EOBINTE, 1); +#endif + SMB_Stop(bus); + + SMB_ReadFromFifo(bus, READ_REG_FIELD(SMBRXF_CTL(bus), SMBRXF_CTL_RX_THR)); + +#ifdef SMB_CAPABILITY_END_OF_BUSY_SUPPORT + bus->operation_state = SMB_STOP_PENDING; + bus->stop_indication = SMB_MASTER_BLOCK_BYTES_ERR_IND; +#else + /* Reset state for new transaction */ + bus->operation_state = SMB_IDLE; + + /* Notify upper layer of transaction completion */ + SMB_callback(bus, SMB_MASTER_BLOCK_BYTES_ERR_IND, bus->read_index); +#endif + } else + SMB_MasterFifoRead(bus); + } else { + (void)SMB_ReadByte(bus, &data); + bus->read_data_buf[bus->read_index++] = data; + } + } + } else { + /* last byte is about to be read - end of transaction. */ + /* Stop should be set before reading last byte. */ + u8 data; + SMB_STATE_IND_T ind = SMB_MASTER_DONE_IND; + +#ifdef SMB_CAPABILITY_END_OF_BUSY_SUPPORT + /* Enable "End of Busy" interrupt. */ + SET_REG_FIELD(SMBCTL1(bus), SMBCTL1_EOBINTE, 1); +#endif + SMB_Stop(bus); + + (void)SMB_ReadByte(bus, &data); + + if ((bus->read_size == block_zero_bytes) && (bus->read_block_use == true)) + ind = SMB_MASTER_BLOCK_BYTES_ERR_IND; + else { + bus->read_data_buf[bus->read_index++] = data; +#if defined (SMB_CAPABILITY_HW_PEC_SUPPORT) + if ((bus->PEC_use == true) && (REG_READ(SMBPEC(bus)) != 0)) +#else + if ((bus->PEC_use == true) && (bus->crc_data != 0)) +#endif + ind = SMB_MASTER_PEC_ERR_IND; + + } + +#ifdef SMB_CAPABILITY_END_OF_BUSY_SUPPORT + bus->operation_state = SMB_STOP_PENDING; + bus->stop_indication = ind; +#else + /* Reset state for new transaction */ + bus->operation_state = SMB_IDLE; + + /* Notify upper layer of transaction completion */ + SMB_callback(bus, ind, bus->read_index); +#endif + } /* last read byte */ + } /* read operation */ + // Edward 2014/6/17 masked. + } /* Master mode: if (bus->operation_state == SMB_MASTER_START) */ + // End of Edward 2014/6/17 masked. + } // End of master operation: SDA status is set - transmit or receive. +#if !defined SMB_MASTER_ONLY +else +#endif +#endif /* !SMB_SLAVE_ONLY */ + +#if !defined SMB_MASTER_ONLY + /* SDA status is set - transmit or receive: Handle slave mode */ + if (bus->master_or_slave == SMB_SLAVE) { + /* Perform slave read. No need to distinguish between last byte and the rest of the bytes. */ + if ((bus->operation == SMB_READ_OPER)) { + if (bus->fifo_use == false) { + u8 data; + + (void)SMB_ReadByte(bus, &data); + if (bus->read_index < bus->read_size) { + /* Keep read data */ + bus->read_data_buf[bus->read_index++] = data; + if ((bus->read_index == 1) && bus->read_size == SMB_BYTES_BLOCK_PROT) + /* First byte indicates length in block protocol */ + bus->read_size = data; + } + } + // FIFO is used + else { + if (READ_REG_FIELD(SMBRXF_STS(bus), SMBRXF_STS_RX_THST)) { + SMB_ReadFromFifo(bus, READ_REG_FIELD(SMBRXF_CTL(bus), SMBRXF_CTL_RX_THR)); + + /* clear the status bits */ + SET_REG_FIELD(SMBRXF_STS(bus), SMBRXF_STS_RX_THST, 1); + } + } + } + /* Perform slave write. */ + else { + /* More bytes to write */ + if ((bus->operation == SMB_WRITE_OPER) && + (bus->write_index < bus->write_size)) { + if (bus->fifo_use == false) { + if ((bus->write_index + 1 == bus->write_size) && + bus->PEC_use == true) { // Send PEC byte +#if defined SMB_CAPABILITY_HW_PEC_SUPPORT + REG_WRITE(SMBSDA(bus), REG_READ(SMBPEC(bus))); +#else + REG_WRITE(SMBSDA(bus), bus->crc_data); +#endif + } else if (bus->write_index < bus->write_size) + SMB_WriteByte(bus, bus->write_data_buf[bus->write_index]); + bus->write_index++; + } + // FIFO is used + else { + u16 wcount; + wcount = (bus->write_size - bus->write_index); + if (wcount >= SMBUS_FIFO_SIZE) + wcount = SMBUS_FIFO_SIZE; + + REG_WRITE(SMBTXF_CTL(bus), (u8)wcount); + SMB_WriteToFifo(bus, wcount); + + /* clear the status bits */ + SET_REG_FIELD(SMBTXF_STS(bus), SMBTXF_STS_TX_THST, 1); + } + } + + /* If all bytes were written, ignore further master read requests. */ + else { +#if !defined(SMB_WRAP_AROUND_BUFFER) + ASSERT(false); +#endif + if (bus->fifo_use == false) { + /* Clear SDA Status bit */ + if (bus->write_index != 0) + /* Was writing */ + SMB_WriteByte(bus, 0xFF); + else { + u8 data; + /* Was reading */ + (void)SMB_ReadByte(bus, &data); + } + } + /* write\read redundant bytes with FIFO (if there are any bytes to write) */ + else { + /* Set threshold size */ + REG_WRITE(SMBTXF_CTL(bus), (u8)SMBUS_FIFO_SIZE); + + SMB_WriteToFifo(bus, SMBUS_FIFO_SIZE); + + /* Clear the status bits */ + SET_REG_FIELD(SMBTXF_STS(bus), SMBTXF_STS_TX_THST, 1); + } + /* Notify upper layer of transaction completion */ + SMB_callback(bus, SMB_NO_DATA_IND, bus->read_index); + } // All bytes sent/received + } + } // slave mode +#endif /* !SMB_MASTER_ONLY */ + } //SDAST +} + +static void SMB_Reset(nuvoton_i2c_bus_t *bus) +{ + /* Save smbctl1 relevant bits. It is being cleared when the module is disabled */ + u8 smbctl1 = REG_READ(SMBCTL1(bus)) & (MASK_FIELD(SMBCTL1_GCMEN) | + MASK_FIELD(SMBCTL1_INTEN) | + MASK_FIELD(SMBCTL1_NMINTE)); + + /* Disable the SMB module */ + SET_REG_FIELD(SMBCTL2(bus), SMBCTL2_ENABLE, DISABLE); + + /* Enable the SMB module */ + (void)SMB_Enable(bus); + + /* Restore smbctl1 status */ + REG_WRITE(SMBCTL1(bus), smbctl1); + + /* Reset driver status */ + bus->operation_state = SMB_IDLE; +} + + +#if !defined SMB_SLAVE_ONLY +static void SMB_MasterAbort(nuvoton_i2c_bus_t *bus) +{ + SMB_AbortData(bus); + SMB_Reset(bus); +} + +#ifdef TBD +static void SMB_Recovery(nuvoton_i2c_bus_t *bus) +{ + + /* Disable interrupt */ + SMB_InterruptEnable(bus, false); + + /* Check If the SDA line is active (low) */ + if (READ_REG_FIELD(SMBCST(bus), SMBCST_TSDA) == 0) { + u8 iter = 9; // Allow one byte to be sent by the Slave + u16 timeout; + bool done = false; + + /* Repeat the following sequence until SDA becomes inactive (high) */ + do { + /* Issue a single SCL cycle */ + REG_WRITE(SMBCST(bus), MASK_FIELD(SMBCST_TGSCL)); + timeout = ABORT_TIMEOUT; + while (READ_REG_FIELD(SMBCST(bus), SMBCST_TGSCL) && (--timeout != 0)) + ; + /* If SDA line is inactive (high), stop */ + if (READ_REG_FIELD(SMBCST(bus), SMBCST_TSDA) == 1) + done = true; + } while ((done == false) && (--iter != 0)); + + /* If SDA line is released (high) */ + if (done) { + /* Clear BB (BUS BUSY) bit */ + REG_WRITE(SMBCST(bus), MASK_FIELD(SMBCST_BB)); + + /* Generate a START condition, to synchronize Master and Slave */ + SMB_Start(bus); + + /* Wait until START condition is sent, or timeout */ + timeout = ABORT_TIMEOUT; + while (!READ_REG_FIELD(SMBST(bus), SMBST_MASTER) && (--timeout != 0)) + ; + + /* If START condition was sent */ + if (timeout > 0) { + /* Send an address byte */ + SMB_WriteByte(bus, bus->dest_addr); + + /* Generate a STOP condition */ + SMB_Stop(bus); + } + } + } + + /* Enable interrupt */ + SMB_InterruptEnable(bus, true); +} +#endif // TBD +#endif /* !SMB_SLAVE_ONLY */ + +static void SMB_InterruptEnable(nuvoton_i2c_bus_t *bus, bool enable) +{ + SET_REG_FIELD(SMBCTL1(bus), SMBCTL1_INTEN, enable); +} + +#if !defined SMB_MASTER_ONLY && defined SMB_RECOVERY_SUPPORT +static void SMB_SlaveAbort(nuvoton_i2c_bus_t *bus) +{ + volatile u8 temp; + + /* Disable interrupt. */ + SMB_InterruptEnable(bus, false); + + /* Dummy read to clear interface. */ + temp = REG_READ(SMBSDA(bus)); + + /* Clear NMATCH and BER bits by writing 1s to them. */ + SET_REG_FIELD(SMBST(bus), SMBST_BER, true); + SET_REG_FIELD(SMBST(bus), SMBST_NMATCH, true); + +#ifdef SMB_STALL_TIMEOUT_SUPPORT + bus->stall_counter = 0; +#endif + + /* Reset driver status */ + bus->operation_state = SMB_IDLE; + + /* Disable SMB Module */ + SET_REG_FIELD(SMBCTL2(bus), SMBCTL2_ENABLE, DISABLE); + + /* Delay 100 us */ + udelay(10); // TBD must be out of interrupt + + /* Enable SMB Module */ + (void)SMB_Enable(bus); + + /* Enable interrupt. */ + SMB_InterruptEnable(bus, true); + + //lint -e{550} suppress PC-Lint warning on Symbol 'temp' not accessed +} +#endif //!defined SMB_MASTER_ONLY && defined SMB_RECOVERY_SUPPORT + +#if defined (SMB_CAPABILITY_WAKEUP_SUPPORT) +static void SMB_SetStallAfterStartIdle(nuvoton_i2c_bus_t *bus, bool enable) +{ + SET_REG_FIELD(SMBCTL3(bus), SMBCTL3_IDL_START, enable); +} +#endif //SMB_CAPABILITY_WAKEUP_SUPPORT + +#if !defined (SMB_CAPABILITY_HW_PEC_SUPPORT) +static const u8 crc8_table[256] = { + 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, + 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, + 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, + 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, + 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, + 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, + 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, + 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, + 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, + 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, + 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, + 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, + 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, + 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, + 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, + 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, + 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, + 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, + 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, + 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, + 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, + 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, + 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, + 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, + 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, + 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, + 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, + 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, + 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, + 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, + 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, + 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 +}; + +static u8 SMB_CalculateCRC8(u8 crc_data, u8 data) +{ + u8 tmp = crc_data ^ data; + + crc_data = crc8_table[tmp]; + + return crc_data; +} +#endif // #if !defined (SMB_CAPABILITY_HW_PEC_SUPPORT) + +static void SMB_CalcPEC(nuvoton_i2c_bus_t *bus, u8 data) +{ +#if !defined (SMB_CAPABILITY_HW_PEC_SUPPORT) + if (bus->PEC_use) + bus->crc_data = SMB_CalculateCRC8(bus->crc_data, data); +#endif +} + + +#ifdef SMB_STALL_TIMEOUT_SUPPORT +static void SMB_ConfigStallThreshold(nuvoton_i2c_bus_t *bus, u8 threshold) +{ + bus->stall_threshold = threshold; +} + +static void SMB_StallHandler(nuvoton_i2c_bus_t *bus) +{ + if ((bus->operation_state == SMB_IDLE) || + (bus->operation_state == SMB_DISABLE) || + (bus->master_or_slave == SMB_SLAVE)) + ; // ignore this bus + else { + /* increase timeout counter */ + bus->stall_counter++; + + /* time expired, execute recovery */ + if ((bus->stall_counter) >= bus->stall_threshold) { + SMB_MasterAbort(bus); + SMB_callback(bus, SMB_BUS_ERR_IND, SMB_GetIndex(bus)); + return; + } + } +} +#endif + +#ifdef TBD +static void SMB_ReEnableModule(nuvoton_i2c_bus_t *bus) +{ + /* Enable SMB interrupt and New Address Match interrupt source */ + SET_REG_FIELD(SMBCTL1(bus), SMBCTL1_NMINTE, ENABLE); + SET_REG_FIELD(SMBCTL1(bus), SMBCTL1_INTEN, ENABLE); +} + +static bool SMB_InterruptIsPending(void) +{ + nuvoton_i2c_bus_t *bus; + bool InterruptIsPending = false; + + for (bus = 0; bus < SMB_NUM_OF_MODULES; bus++) + InterruptIsPending |= INTERRUPT_PENDING(SMB_INTERRUPT_PROVIDER, + SMB_INTERRUPT(bus)); + + return InterruptIsPending; +} +#endif + +#ifdef SMB_CAPABILITY_FORCE_SCL_SDA +static void SMB_WriteSCL(nuvoton_i2c_bus_t *bus, SMB_LEVEL_T level) +{ + unsigned long bank_flags; + + /* Select Bank 0 to access SMBCTL4 */ + spin_lock_irqsave(&bus->bank_lock, bank_flags); + SMB_SelectBank(bus, SMB_BANK_0); + + /* Set SCL_LVL, SDA_LVL bits as Read/Write (R/W) */ + SET_REG_FIELD(SMBCTL4(bus), SMBCTL4_LVL_WE, 1); + + /* Set level */ + SET_REG_FIELD(SMBCTL3(bus), SMBCTL3_SCL_LVL, level); + + /* Set SCL_LVL, SDA_LVL bits as Read Only (RO) */ + SET_REG_FIELD(SMBCTL4(bus), SMBCTL4_LVL_WE, 0); + + /* Return to Bank 1 */ + SMB_SelectBank(bus, SMB_BANK_1); + spin_unlock_irqrestore(&bus->bank_lock, bank_flags); +} + +static void SMB_WriteSDA(nuvoton_i2c_bus_t *bus, SMB_LEVEL_T level) +{ + unsigned long bank_flags; + + /* Select Bank 0 to access SMBCTL4 */ + spin_lock_irqsave(&bus->bank_lock, bank_flags); + SMB_SelectBank(bus, SMB_BANK_0); + + /* Set SCL_LVL, SDA_LVL bits as Read/Write (R/W) */ + SET_REG_FIELD(SMBCTL4(bus), SMBCTL4_LVL_WE, 1); + + /* Set level */ + SET_REG_FIELD(SMBCTL3(bus), SMBCTL3_SDA_LVL, level); + + /* Set SCL_LVL, SDA_LVL bits as Read Only (RO) */ + SET_REG_FIELD(SMBCTL4(bus), SMBCTL4_LVL_WE, 0); + + /* Return to Bank 1 */ + SMB_SelectBank(bus, SMB_BANK_1); + spin_unlock_irqrestore(&bus->bank_lock, bank_flags); +} +#endif // SMB_CAPABILITY_FORCE_SCL_SDA + +#ifdef CONFIG_NPCM750_I2C_DEBUG_PRINT +static void SMB_PrintRegs(nuvoton_i2c_bus_t *bus) +{ + unsigned int i; + + HAL_PRINT("/*--------------*/\n"); + HAL_PRINT("/* SMB */\n"); + HAL_PRINT("/*--------------*/\n\n"); + + SMB_PrintModuleRegs(bus); + + //lint -e{774, 506} suppress PC-Lint warning on ''if' always evaluates to true' + if (SMB_FIFO(bus)) { + HAL_PRINT("/*--------------*/\n"); + HAL_PRINT("/* SMBF */\n"); + HAL_PRINT("/*--------------*/\n\n"); + + SMBF_PrintModuleRegs(bus); + } +} + +static void SMB_PrintModuleRegs(nuvoton_i2c_bus_t *bus) +{ + HAL_PRINT("SMB%d:\n", bus->module__num); + HAL_PRINT("------\n"); + HAL_PRINT("SMB%dSDA = 0x%02X\n", bus->module__num, + REG_READ(SMBSDA(bus))); + HAL_PRINT("SMB%dST = 0x%02X\n", bus->module__num, + REG_READ(SMBST(bus))); + HAL_PRINT("SMB%dCST = 0x%02X\n", bus->module__num, + REG_READ(SMBCST(bus))); + HAL_PRINT("SMB%dCTL1 = 0x%02X\n", bus->module__num, + REG_READ(SMBCTL1(bus))); + HAL_PRINT("SMB%dADDR1 = 0x%02X\n", bus->module__num, + REG_READ(SMBADDR1(bus))); + HAL_PRINT("SMB%dCTL2 = 0x%02X\n", bus->module__num, + REG_READ(SMBCTL2(bus))); + HAL_PRINT("SMB%dADDR2 = 0x%02X\n", bus->module__num, + REG_READ(SMBADDR2(bus))); + HAL_PRINT("SMB%dCTL3 = 0x%02X\n", bus->module__num, + REG_READ(SMBCTL3(bus))); +#if defined (SMB_CAPABILITY_TIMEOUT_SUPPORT) + HAL_PRINT("SMB%dT_OUT = 0x%02X\n", bus->module__num, + REG_READ(SMBT_OUT(bus))); +#endif + HAL_PRINT("SMB%dADDR3 = 0x%02X\n", bus->module__num, + REG_READ(SMBADDR3(bus))); + HAL_PRINT("SMB%dADDR7 = 0x%02X\n", bus->module__num, + REG_READ(SMBADDR7(bus))); + HAL_PRINT("SMB%dADDR4 = 0x%02X\n", bus->module__num, + REG_READ(SMBADDR4(bus))); + HAL_PRINT("SMB%dADDR8 = 0x%02X\n", bus->module__num, + REG_READ(SMBADDR8(bus))); + HAL_PRINT("SMB%dADDR5 = 0x%02X\n", bus->module__num, + REG_READ(SMBADDR5(bus))); + HAL_PRINT("SMB%dADDR9 = 0x%02X\n", bus->module__num, + REG_READ(SMBADDR9(bus))); + HAL_PRINT("SMB%dADDR6 = 0x%02X\n", bus->module__num, + REG_READ(SMBADDR6(bus))); + HAL_PRINT("SMB%dADDR10 = 0x%02X\n", bus->module__num, + REG_READ(SMBADDR10(bus))); + HAL_PRINT("SMB%dCST2 = 0x%02X\n", bus->module__num, + REG_READ(SMBCST2(bus))); + HAL_PRINT("SMB%dCST3 = 0x%02X\n", bus->module__num, + REG_READ(SMBCST3(bus))); + HAL_PRINT("SMB%dCTL4 = 0x%02X\n", bus->module__num, + REG_READ(SMBCTL4(bus))); + HAL_PRINT("SMB%dCTL5 = 0x%02X\n", bus->module__num, + REG_READ(SMBCTL5(bus))); + HAL_PRINT("SMB%dSCLLT = 0x%02X\n", bus->module__num, + REG_READ(SMBSCLLT(bus))); + HAL_PRINT("SMB%dSCLHT = 0x%02X\n", bus->module__num, + REG_READ(SMBSCLHT(bus))); + HAL_PRINT("SMB%dVER = 0x%02X\n", bus->module__num, + REG_READ(SMB_VER(bus))); + + HAL_PRINT("\n"); +} + +static void SMBF_PrintModuleRegs(nuvoton_i2c_bus_t *bus) +{ + + /* Common Registers */ + HAL_PRINT("SMB%1X:\n", bus->module__num); + HAL_PRINT("------\n"); + HAL_PRINT("SMB%1XSDA = 0x%02X\n", bus->module__num, + REG_READ(SMBSDA(bus))); + HAL_PRINT("SMB%1XST = 0x%02X\n", bus->module__num, + REG_READ(SMBST(bus))); + HAL_PRINT("SMB%1XCST = 0x%02X\n", bus->module__num, + REG_READ(SMBCST(bus))); + HAL_PRINT("SMB%1XCTL1 = 0x%02X\n", bus->module__num, + REG_READ(SMBCTL1(bus))); + HAL_PRINT("SMB%1XADDR1 = 0x%02X\n", bus->module__num, + REG_READ(SMBADDR1(bus))); + HAL_PRINT("SMB%1XCTL2 = 0x%02X\n", bus->module__num, + REG_READ(SMBCTL2(bus))); + HAL_PRINT("SMB%1XADDR2 = 0x%02X\n", bus->module__num, + REG_READ(SMBADDR2(bus))); + HAL_PRINT("SMB%1XCTL3 = 0x%02X\n", bus->module__num, + REG_READ(SMBCTL3(bus))); +#if defined (SMB_CAPABILITY_TIMEOUT_SUPPORT) + HAL_PRINT("SMB%1XT_OUT = 0x%02X\n", bus->module__num, + REG_READ(SMBT_OUT(bus))); +#endif + + + /* Bank 0 Registers */ + spin_lock_irqsave(&bus->bank_lock, bank_flags); + SMB_SelectBank(bus, SMB_BANK_0); + + HAL_PRINT("SMB%1XADDR3 = 0x%02X\n", bus->module__num, + REG_READ(SMBADDR3(bus))); + HAL_PRINT("SMB%1XADDR7 = 0x%02X\n", bus->module__num, + REG_READ(SMBADDR7(bus))); + HAL_PRINT("SMB%1XADDR4 = 0x%02X\n", bus->module__num, + REG_READ(SMBADDR4(bus))); + HAL_PRINT("SMB%1XADDR8 = 0x%02X\n", bus->module__num, + REG_READ(SMBADDR8(bus))); + HAL_PRINT("SMB%1XADDR5 = 0x%02X\n", bus->module__num, + REG_READ(SMBADDR5(bus))); + HAL_PRINT("SMB%1XADDR9 = 0x%02X\n", bus->module__num, + REG_READ(SMBADDR9(bus))); + HAL_PRINT("SMB%1XADDR6 = 0x%02X\n", bus->module__num, + REG_READ(SMBADDR6(bus))); + HAL_PRINT("SMB%1XADDR10 = 0x%02X\n", bus->module__num, + REG_READ(SMBADDR10(bus))); + HAL_PRINT("SMB%1XCST2 = 0x%02X\n", bus->module__num, + REG_READ(SMBCST2(bus))); + HAL_PRINT("SMB%1XCST3 = 0x%02X\n", bus->module__num, + REG_READ(SMBCST3(bus))); + HAL_PRINT("SMB%1XCTL4 = 0x%02X\n", bus->module__num, + REG_READ(SMBCTL4(bus))); + HAL_PRINT("SMB%1XCTL5 = 0x%02X\n", bus->module__num, + REG_READ(SMBCTL5(bus))); + HAL_PRINT("SMB%1XSCLLT = 0x%02X\n", bus->module__num, + REG_READ(SMBSCLLT(bus))); + HAL_PRINT("SMB%1XFIF_CTL = 0x%02X\n", bus->module__num, + REG_READ(SMBFIF_CTL(bus))); + HAL_PRINT("SMB%1XSCLHT = 0x%02X\n", bus->module__num, + REG_READ(SMBSCLHT(bus))); + HAL_PRINT("SMB%1X_VER = 0x%02X\n", bus->module__num, + REG_READ(SMB_VER(bus))); + + + /* Bank 1 Registers */ + + SMB_SelectBank(bus, SMB_BANK_1); + spin_unlock_irqrestore(&bus->bank_lock, bank_flags); + + HAL_PRINT("SMB%1XFIF_CTS = 0x%02X\n", bus->module__num, + REG_READ(SMBFIF_CTS(bus))); + HAL_PRINT("SMB%1XTXF_CTL = 0x%02X\n", bus->module__num, + REG_READ(SMBTXF_CTL(bus))); +#if defined (SMB_CAPABILITY_HW_PEC_SUPPORT) + HAL_PRINT("SMB%1XPEC = 0x%02X\n", bus->module__num, + REG_READ(SMBPEC(bus))); +#endif + HAL_PRINT("SMB%1XTXF_STS = 0x%02X\n", bus->module__num, + REG_READ(SMBTXF_STS(bus))); + HAL_PRINT("SMB%1XRXF_STS = 0x%02X\n", bus->module__num, + REG_READ(SMBRXF_STS(bus))); + HAL_PRINT("SMB%1XRXF_CTL = 0x%02X\n", bus->module__num, + REG_READ(SMBRXF_CTL(bus))); + + HAL_PRINT("\n"); +} + +static void SMB_PrintVersion(void) +{ + HAL_PRINT("SMB = %s\n", I2C_VERSION); +} +#endif //CONFIG_NPCM750_I2C_DEBUG_PRINT + +static void SMB_callback(nuvoton_i2c_bus_t *bus, SMB_STATE_IND_T op_status, u16 info) +{ + switch (op_status) { +#if !defined SMB_MASTER_ONLY + extern u8 read_data_buf[PAGE_SIZE]; + extern u16 read_size; + extern u8 write_data_buf[32]; + extern u16 write_size; + case SMB_SLAVE_RCV_IND: + // Slave got an address match with direction bit clear so it should receive data + // the interrupt must call SMB_StartSlaveReceive() + // info: the enum SMB_ADDR_T address match + SMB_StartSlaveReceive(bus, read_size, read_data_buf); + break; + case SMB_SLAVE_XMIT_IND: + // Slave got an address match with direction bit set so it should transmit data + // the interrupt must call SMB_StartSlaveTransmit() + // info: the enum SMB_ADDR_T address match + SMB_StartSlaveTransmit(bus, write_size, write_data_buf); + break; + case SMB_SLAVE_DONE_IND: + // Slave done transmitting or receiving + // info: + // on receive: number of actual bytes received + // on transmit: number of actual bytes transmitted, + // when PEC is used 'info' should be (nwrite+1) which means that 'nwrite' bytes + // were sent + the PEC byte + // 'nwrite' is the second parameter SMB_StartSlaveTransmit() + break; +#endif // !defined SMB_MASTER_ONLY + case SMB_MASTER_DONE_IND: + // Master transaction finished and all transmit bytes were sent + // info: number of bytes actually received after the Master receive operation + // (if Master didn't issue receive it should be 0) + case SMB_NO_DATA_IND: + // Notify that not all data was received on Master or Slave + // info: + // on receive: number of actual bytes received + // when PEC is used even if 'info' is the expected number of bytes, + // it means that PEC error occured. + { + struct i2c_msg *msgs = bus->msgs; + int msgs_num = bus->msgs_num; + + if (msgs[0].flags & I2C_M_RD) + msgs[0].len = info; + else if (msgs_num == 2 && msgs[1].flags & I2C_M_RD) + msgs[1].len = info; + + bus->cmd_err = 0; + complete(&bus->cmd_complete); + } + break; + case SMB_NACK_IND: + // MASTER transmit got a NAK before transmitting all bytes + // info: number of transmitted bytes + bus->cmd_err = -EAGAIN; + complete(&bus->cmd_complete); + break; + case SMB_BUS_ERR_IND: + // Bus error occured + // info: has no meaning + bus->cmd_err = -EIO; + complete(&bus->cmd_complete); + break; + case SMB_WAKE_UP_IND: + // SMBus wake up occured + // info: has no meaning + break; + default: + break; + } +} + + +static int __nuvoton_i2c_init(struct nuvoton_i2c_bus *bus, + struct platform_device *pdev) +{ + u32 clk_freq; + int ret; + + /* Initialize the internal data structures */ + bus->operation_state = SMB_DISABLE; + bus->master_or_slave = SMB_SLAVE; +#ifdef SMB_STALL_TIMEOUT_SUPPORT + bus->stall_counter = 0; + bus->stall_threshold = DEFAULT_STALL_COUNT; +#endif + + + ret = of_property_read_u32(pdev->dev.of_node, + "bus-frequency", &clk_freq); + if (ret < 0) { + dev_err(&pdev->dev, + "Could not read bus-frequency property\n"); + clk_freq = 100000; + } + I2C_DEBUG("clk_freq = %d\n", clk_freq); + ret = SMB_InitModule(bus, SMB_MASTER, clk_freq / 1000); + if (ret == false) { + dev_err(&pdev->dev, + "SMB_InitModule() failed\n"); + return -1; + } +#if defined (SMB_CAPABILITY_TIMEOUT_SUPPORT) + SMB_EnableTimeout(bus, true); +#endif //SMB_CAPABILITY_TIMEOUT_SUPPORT + +#ifdef CONFIG_I2C_SLAVE + /* If slave has already been registered, re-enable it. */ + if (bus->slave) + __nuvoton_i2c_reg_slave(bus, bus->slave->addr); +#endif /* CONFIG_I2C_SLAVE */ + + return 0; +} + + +static irqreturn_t nuvoton_i2c_bus_irq(int irq, void *dev_id) +{ + struct nuvoton_i2c_bus *bus = dev_id; + +#ifdef SMB_SW_BYPASS_HW_ISSUE_SMB_STOP + _npcm7xx_get_time_stamp(bus->interrupt_time_stamp); +#endif + SMB_InterruptHandler(bus); + +#ifdef CONFIG_I2C_SLAVE + if (nuvoton_i2c_slave_irq(bus)) { + dev_dbg(bus->dev, "irq handled by slave.\n"); + return IRQ_HANDLED; + } +#endif /* CONFIG_I2C_SLAVE */ + + return IRQ_HANDLED; +} + + +static int nuvoton_i2c_master_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + struct nuvoton_i2c_bus *bus = adap->algo_data; + struct i2c_msg *msg0, *msg1; + unsigned long time_left, lock_flags; + u16 nwrite, nread; + u8 *write_data, *read_data; + u8 slave_addr; + int ret = 0; + + //spin_lock_irqsave(&bus->lock, flags); + bus->cmd_err = 0; + + if (num > 2 || num < 1) { + pr_err(" num = %d > 2.\n", num); + return -1; + } + + msg0 = &msgs[0]; + slave_addr = msg0->addr; + if (msg0->flags & I2C_M_RD) { // read + if (num == 2) { + pr_err(" num = 2 but first msg is read instead of " + "write.\n"); + return -1; + } + nwrite = 0; + write_data = NULL; + if (msg0->flags & I2C_M_RECV_LEN) + nread = SMB_BYTES_BLOCK_PROT; + else + nread = msg0->len; + + read_data = msg0->buf; + } else { // write + nwrite = msg0->len; + write_data = msg0->buf; + nread = 0; + read_data = NULL; + if (num == 2) { + msg1 = &msgs[1]; + if (slave_addr != msg1->addr) { + pr_err(" slave_addr == %02x but msg1->addr == " + "%02x\n", slave_addr, msg1->addr); + return -1; + } + if ((msg1->flags & I2C_M_RD) == 0) { + pr_err(" num = 2 but both msg are write.\n"); + return -1; + } + if (msg1->flags & I2C_M_RECV_LEN) + nread = SMB_BYTES_BLOCK_PROT; + else + nread = msg1->len; + + read_data = msg1->buf; + } + } + + bus->msgs = msgs; + bus->msgs_num = num; + + if (nwrite == 0 && nread == 0) + nwrite = nread = SMB_BYTES_QUICK_PROT; + + reinit_completion(&bus->cmd_complete); + SMB_StartMasterTransaction(bus, slave_addr, nwrite, nread, write_data, + read_data, 0); + //spin_unlock_irqrestore(&bus->lock, flags); + + time_left = wait_for_completion_timeout(&bus->cmd_complete, + bus->adap.timeout); + + if (time_left == 0){ + SMB_MasterAbort(bus); + ret = -ETIMEDOUT; + } + else + ret = bus->cmd_err; + + spin_lock_irqsave(&bus->lock, lock_flags); +#ifdef CONFIG_NPCM750_I2C_DEBUG + if (bus->msgs[0].flags & I2C_M_RD) + nread = bus->msgs[0].len; + else if (bus->msgs_num == 2 && bus->msgs[1].flags & I2C_M_RD) + nread = bus->msgs[1].len; + if (nread && nread != SMB_BYTES_QUICK_PROT) { + int i; + char str[32 * 3 + 4]; + char *s = str; + + for (i = 0; (i < nread && i < 32); i++) + s += sprintf(s, "%02x ", read_data[i]); + + printk("read_data = %s\n", str); + } +#endif + + + bus->msgs = NULL; + bus->msgs_num = 0; + spin_unlock_irqrestore(&bus->lock, lock_flags); + + /* If nothing went wrong, return number of messages transferred. */ + if (ret >= 0) + return num; + else + return ret; +} + +static u32 nuvoton_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA; +} + +#ifdef CONFIG_I2C_SLAVE +static void __nuvoton_i2c_reg_slave(struct nuvoton_i2c_bus *bus, u16 slave_addr) +{ + u32 addr_reg_val, func_ctrl_reg_val; + + /* Set slave addr. */ + addr_reg_val = readl(bus->base + ASPEED_I2C_DEV_ADDR_REG); + addr_reg_val &= ~ASPEED_I2CD_DEV_ADDR_MASK; + addr_reg_val |= slave_addr & ASPEED_I2CD_DEV_ADDR_MASK; + writel(addr_reg_val, bus->base + ASPEED_I2C_DEV_ADDR_REG); + + /* Turn on slave mode. */ + func_ctrl_reg_val = readl(bus->base + ASPEED_I2C_FUN_CTRL_REG); + func_ctrl_reg_val |= ASPEED_I2CD_SLAVE_EN; + writel(func_ctrl_reg_val, bus->base + ASPEED_I2C_FUN_CTRL_REG); +} + +static int nuvoton_i2c_reg_slave(struct i2c_client *client) +{ + struct nuvoton_i2c_bus *bus; + unsigned long lock_flags; + + bus = client->adapter->algo_data; + spin_lock_irqsave(&bus->lock, lock_flags); + if (bus->slave) { + spin_unlock_irqrestore(&bus->lock, lock_flags); + return -EINVAL; + } + + __nuvoton_i2c_reg_slave(bus, client->addr); + + bus->slave = client; + bus->slave_state = ASPEED_I2C_SLAVE_STOP; + spin_unlock_irqrestore(&bus->lock, lock_flags); + + return 0; +} + +static int nuvoton_i2c_unreg_slave(struct i2c_client *client) +{ + struct nuvoton_i2c_bus *bus = client->adapter->algo_data; + u32 func_ctrl_reg_val; + unsigned long lock_flags; + + spin_lock_irqsave(&bus->lock, lock_flags); + + if (!bus->slave) { + spin_unlock_irqrestore(&bus->lock, lock_flags); + return -EINVAL; + } + + /* Turn off slave mode. */ + func_ctrl_reg_val = readl(bus->base + ASPEED_I2C_FUN_CTRL_REG); + func_ctrl_reg_val &= ~ASPEED_I2CD_SLAVE_EN; + writel(func_ctrl_reg_val, bus->base + ASPEED_I2C_FUN_CTRL_REG); + + bus->slave = NULL; + spin_unlock_irqrestore(&bus->lock, lock_flags); + + return 0; +} +#endif /* CONFIG_I2C_SLAVE */ + +static const struct i2c_algorithm nuvoton_i2c_algo = { + .master_xfer = nuvoton_i2c_master_xfer, + .functionality = nuvoton_i2c_functionality, +#ifdef CONFIG_I2C_SLAVE + .reg_slave = nuvoton_i2c_reg_slave, + .unreg_slave = nuvoton_i2c_unreg_slave, +#endif /* CONFIG_I2C_SLAVE */ +}; + +static int nuvoton_i2c_probe_bus(struct platform_device *pdev) +{ + struct nuvoton_i2c_bus *bus; + struct resource *res; + struct clk *i2c_clk; + int ret; + int module__num; + + bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL); + if (!bus) + return -ENOMEM; + +#ifdef CONFIG_OF + module__num = of_alias_get_id(pdev->dev.of_node, "i2c"); + bus->module__num = module__num; + + I2C_DEBUG("module__num = %d\n", module__num); + + i2c_clk = devm_clk_get(&pdev->dev, NULL); + + if (IS_ERR(i2c_clk)) { + pr_err(" I2C probe failed: can't read clk.\n"); + return -EPROBE_DEFER; // this error will cause the probing to run again after clk is ready. + } + //clk_prepare_enable(otp_clk); + bus->apb_clk = clk_get_rate(i2c_clk); + I2C_DEBUG("I2C APB clock is %d\n", bus->apb_clk); +#endif // CONFIG_OF + + if (gcr_regmap == NULL) { + gcr_regmap = syscon_regmap_lookup_by_compatible("nuvoton,npcm750-gcr"); + if (IS_ERR(gcr_regmap)) { + pr_err("%s: failed to find nuvoton," + "npcm750-gcr\n", __func__); + return IS_ERR(gcr_regmap); + } + regmap_write(gcr_regmap, I2CSEGCTL_OFFSET, I2CSEGCTL_VAL); + printk("I2C%d: gcr mapped\n", bus->module__num); + } + + if (clk_regmap == NULL) { + clk_regmap = syscon_regmap_lookup_by_compatible("nuvoton,npcm750-clk"); + if (IS_ERR(clk_regmap)) { + pr_err("%s: failed to find nuvoton," + "npcm750-clk\n", __func__); + return IS_ERR(clk_regmap); + } + printk("I2C%d: clk mapped\n", bus->module__num); + } + + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + bus->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(bus->base)) + return PTR_ERR(bus->base); + + I2C_DEBUG("base = %p\n", bus->base); + + /* Initialize the I2C adapter */ + spin_lock_init(&bus->lock); + spin_lock_init(&bus->bank_lock); + init_completion(&bus->cmd_complete); + bus->adap.owner = THIS_MODULE; + bus->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + bus->adap.retries = 0; + bus->adap.timeout = 50 * HZ / 1000; + bus->adap.algo = &nuvoton_i2c_algo; + bus->adap.algo_data = bus; + bus->adap.dev.parent = &pdev->dev; + bus->adap.dev.of_node = pdev->dev.of_node; + snprintf(bus->adap.name, sizeof(bus->adap.name), "Nuvoton i2c"); + + bus->dev = &pdev->dev; + + ret = __nuvoton_i2c_init(bus, pdev); + if (ret < 0) + return ret; + + bus->irq = platform_get_irq(pdev, 0); + if (bus->irq < 0) { + pr_err("I2C platform_get_irq error."); + return -ENODEV; + } + //bus->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); + I2C_DEBUG("irq = %d\n", bus->irq); + + ret = request_irq(bus->irq, nuvoton_i2c_bus_irq, 0, + dev_name(&pdev->dev), (void *)bus); + if (ret) + return ret; + + ret = i2c_add_adapter(&bus->adap); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, bus); + + dev_info(bus->dev, "i2c bus %d registered, irq %d\n", + bus->adap.nr, bus->irq); + + return 0; +} + +static int nuvoton_i2c_remove_bus(struct platform_device *pdev) +{ + struct nuvoton_i2c_bus *bus = platform_get_drvdata(pdev); + unsigned long lock_flags; + + spin_lock_irqsave(&bus->lock, lock_flags); + + /* Disable everything. */ + SMB_Disable(bus); + + spin_unlock_irqrestore(&bus->lock, lock_flags); + + i2c_del_adapter(&bus->adap); + + return 0; +} + +static const struct of_device_id nuvoton_i2c_bus_of_table[] = { + { .compatible = "nuvoton,npcm750-i2c-bus", }, + { }, +}; +MODULE_DEVICE_TABLE(of, nuvoton_i2c_bus_of_table); + +static struct platform_driver nuvoton_i2c_bus_driver = { + .probe = nuvoton_i2c_probe_bus, + .remove = nuvoton_i2c_remove_bus, + .driver = { + .name = "nuvoton-i2c-bus", + .of_match_table = nuvoton_i2c_bus_of_table, + }, +}; +module_platform_driver(nuvoton_i2c_bus_driver); + +MODULE_AUTHOR("Avi Fishman "); +MODULE_DESCRIPTION("Nuvoton I2C Bus Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(I2C_VERSION); + + From a9b84ae797abf9643579ed827b0bf81a052943d0 Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Tue, 9 Jan 2018 00:30:37 +0200 Subject: [PATCH 24/37] include: clock: add NPCM7XX clock include file add NPCM7XX clock include file to building DTS Signed-off-by: Tomer Maimon --- .../dt-bindings/clock/nuvoton,npcm7xx-clks.h | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 include/dt-bindings/clock/nuvoton,npcm7xx-clks.h diff --git a/include/dt-bindings/clock/nuvoton,npcm7xx-clks.h b/include/dt-bindings/clock/nuvoton,npcm7xx-clks.h new file mode 100644 index 00000000000000..c69d3bbf7e420b --- /dev/null +++ b/include/dt-bindings/clock/nuvoton,npcm7xx-clks.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2016 Nuvoton Technologies, tali.perry@nuvoton.com + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + */ + +#ifndef _DT_BINDINGS_CLK_NPCM7XX_H +#define _DT_BINDINGS_CLK_NPCM7XX_H + +#define NPCM7XX_CLK_PLL0 0 +#define NPCM7XX_CLK_PLL1 1 +#define NPCM7XX_CLK_PLL2 2 +#define NPCM7XX_CLK_GFX 3 +#define NPCM7XX_CLK_APB1 4 +#define NPCM7XX_CLK_APB2 5 +#define NPCM7XX_CLK_APB3 6 +#define NPCM7XX_CLK_APB4 7 +#define NPCM7XX_CLK_APB5 8 +#define NPCM7XX_CLK_MC 9 +#define NPCM7XX_CLK_CPU 10 +#define NPCM7XX_CLK_SPI0 11 +#define NPCM7XX_CLK_SPI3 12 +#define NPCM7XX_CLK_SPIX 13 +#define NPCM7XX_CLK_UART_CORE 14 +#define NPCM7XX_CLK_TIMER 15 +#define NPCM7XX_CLK_HOST_UART 16 +#define NPCM7XX_CLK_MMC 17 +#define NPCM7XX_CLK_SDHC 18 +#define NPCM7XX_CLK_ADC 19 +#define NPCM7XX_CLK_GFX_MEM 20 +#define NPCM7XX_CLK_USB_BRIDGE 21 +#define NPCM7XX_CLK_AXI 22 +#define NPCM7XX_CLK_AHB 23 +#define NPCM7XX_CLK_EMC 24 +#define NPCM7XX_CLK_GMAC 25 + +#endif From 9034700e8351afe6445c4805bf7a4f83a7a0e202 Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Tue, 9 Jan 2018 02:58:09 +0200 Subject: [PATCH 25/37] dt-binding: clock: document NPCM7xx clock DT bindings Added device tree binding documentation for Nuvoton NPCM7xx clock. Signed-off-by: Tomer Maimon --- .../bindings/clock/nuvoton,npcm750-clk.txt | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/nuvoton,npcm750-clk.txt diff --git a/Documentation/devicetree/bindings/clock/nuvoton,npcm750-clk.txt b/Documentation/devicetree/bindings/clock/nuvoton,npcm750-clk.txt new file mode 100644 index 00000000000000..f82064546d1117 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/nuvoton,npcm750-clk.txt @@ -0,0 +1,100 @@ +* Nuvoton NPCM7XX Clock Controller + +Nuvoton Poleg BMC NPCM7XX contains an integrated clock controller, which +generates and supplies clocks to all modules within the BMC. + +External clocks: + +There are six fixed clocks that are generated outside the BMC. All clocks are of +a known fixed value that cannot be changed. clk_refclk, clk_mcbypck and +clk_sysbypck are inputs to the clock controller. +clk_rg1refck, clk_rg2refck and clk_xin are external clocks suppling the +network. They are set on the device tree, but not used by the clock module. The +network devices use them directly. +Example can be found below. + +All available clocks are defined as preprocessor macros in: +dt-bindings/clock/nuvoton,npcm7xx-clock.h +and can be reused as DT sources. + +Required Properties of clock controller: + + - compatible: "nuvoton,npcm750-clk" : for clock controller of Nuvoton + Poleg BMC NPCM750 + + - reg: physical base address of the clock controller and length of + memory mapped region. + + - #clock-cells: should be 1. + +Example: Clock controller node: + + clk: clock-controller@f0801000 { + compatible = "nuvoton,npcm750-clk"; + #clock-cells = <1>; + reg = <0xf0801000 0x1000>; + clock-names = "refclk", "sysbypck", "mcbypck"; + clocks = <&clk_refclk>, <&clk_sysbypck>, <&clk_mcbypck>; + }; + +Example: Required external clocks for network: + + /* external reference clock */ + clk_refclk: clk-refclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <25000000>; + clock-output-names = "refclk"; + }; + + /* external reference clock for cpu. float in normal operation */ + clk_sysbypck: clk-sysbypck { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <800000000>; + clock-output-names = "sysbypck"; + }; + + /* external reference clock for MC. float in normal operation */ + clk_mcbypck: clk-mcbypck { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <800000000>; + clock-output-names = "mcbypck"; + }; + + /* external clock signal rg1refck, supplied by the phy */ + clk_rg1refck: clk-rg1refck { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <125000000>; + clock-output-names = "clk_rg1refck"; + }; + + /* external clock signal rg2refck, supplied by the phy */ + clk_rg2refck: clk-rg2refck { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <125000000>; + clock-output-names = "clk_rg2refck"; + }; + + clk_xin: clk-xin { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <50000000>; + clock-output-names = "clk_xin"; + }; + + +Example: GMAC controller node that consumes two clocks: a generated clk by the +clock controller and a fixed clock from DT (clk_rg1refck). + + ethernet0: ethernet@f0802000 { + compatible = "snps,dwmac"; + reg = <0xf0802000 0x2000>; + interrupts = <0 14 4>; + interrupt-names = "macirq"; + clocks = <&clk_rg1refck>, <&clk NPCM7XX_CLK_AHB>; + clock-names = "stmmaceth", "clk_gmac"; + }; From e1c84e95deb2e3cdb29183c6fbefb785bbd9ec85 Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Tue, 9 Jan 2018 03:06:04 +0200 Subject: [PATCH 26/37] dt-binding: clock: add NPCM7XX clock include file add NPCM7XX clock include file for DTS use. Signed-off-by: Tomer Maimon --- .../dt-bindings/clock/nuvoton,npcm7xx-clks.h | 39 ---------------- .../dt-bindings/clock/nuvoton,npcm7xx-clock.h | 44 +++++++++++++++++++ 2 files changed, 44 insertions(+), 39 deletions(-) delete mode 100644 include/dt-bindings/clock/nuvoton,npcm7xx-clks.h create mode 100644 include/dt-bindings/clock/nuvoton,npcm7xx-clock.h diff --git a/include/dt-bindings/clock/nuvoton,npcm7xx-clks.h b/include/dt-bindings/clock/nuvoton,npcm7xx-clks.h deleted file mode 100644 index c69d3bbf7e420b..00000000000000 --- a/include/dt-bindings/clock/nuvoton,npcm7xx-clks.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2016 Nuvoton Technologies, tali.perry@nuvoton.com - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - */ - -#ifndef _DT_BINDINGS_CLK_NPCM7XX_H -#define _DT_BINDINGS_CLK_NPCM7XX_H - -#define NPCM7XX_CLK_PLL0 0 -#define NPCM7XX_CLK_PLL1 1 -#define NPCM7XX_CLK_PLL2 2 -#define NPCM7XX_CLK_GFX 3 -#define NPCM7XX_CLK_APB1 4 -#define NPCM7XX_CLK_APB2 5 -#define NPCM7XX_CLK_APB3 6 -#define NPCM7XX_CLK_APB4 7 -#define NPCM7XX_CLK_APB5 8 -#define NPCM7XX_CLK_MC 9 -#define NPCM7XX_CLK_CPU 10 -#define NPCM7XX_CLK_SPI0 11 -#define NPCM7XX_CLK_SPI3 12 -#define NPCM7XX_CLK_SPIX 13 -#define NPCM7XX_CLK_UART_CORE 14 -#define NPCM7XX_CLK_TIMER 15 -#define NPCM7XX_CLK_HOST_UART 16 -#define NPCM7XX_CLK_MMC 17 -#define NPCM7XX_CLK_SDHC 18 -#define NPCM7XX_CLK_ADC 19 -#define NPCM7XX_CLK_GFX_MEM 20 -#define NPCM7XX_CLK_USB_BRIDGE 21 -#define NPCM7XX_CLK_AXI 22 -#define NPCM7XX_CLK_AHB 23 -#define NPCM7XX_CLK_EMC 24 -#define NPCM7XX_CLK_GMAC 25 - -#endif diff --git a/include/dt-bindings/clock/nuvoton,npcm7xx-clock.h b/include/dt-bindings/clock/nuvoton,npcm7xx-clock.h new file mode 100644 index 00000000000000..f21522605b94b7 --- /dev/null +++ b/include/dt-bindings/clock/nuvoton,npcm7xx-clock.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Nuvoton NPCM7xx Clock Generator binding + * clock binding number for all clocks supportted by nuvoton,npcm7xx-clk + * + * Copyright (C) 2018 Nuvoton Technologies tali.perry@nuvoton.com + * + */ + +#ifndef __DT_BINDINGS_CLOCK_NPCM7XX_H +#define __DT_BINDINGS_CLOCK_NPCM7XX_H + + +#define NPCM7XX_CLK_CPU 0 +#define NPCM7XX_CLK_GFX_PIXEL 1 +#define NPCM7XX_CLK_MC 2 +#define NPCM7XX_CLK_ADC 3 +#define NPCM7XX_CLK_AHB 4 +#define NPCM7XX_CLK_TIMER 5 +#define NPCM7XX_CLK_UART 6 +#define NPCM7XX_CLK_MMC 7 +#define NPCM7XX_CLK_SPI3 8 +#define NPCM7XX_CLK_PCI 9 +#define NPCM7XX_CLK_AXI 10 +#define NPCM7XX_CLK_APB4 11 +#define NPCM7XX_CLK_APB3 12 +#define NPCM7XX_CLK_APB2 13 +#define NPCM7XX_CLK_APB1 14 +#define NPCM7XX_CLK_APB5 15 +#define NPCM7XX_CLK_CLKOUT 16 +#define NPCM7XX_CLK_GFX 17 +#define NPCM7XX_CLK_SU 18 +#define NPCM7XX_CLK_SU48 19 +#define NPCM7XX_CLK_SDHC 20 +#define NPCM7XX_CLK_SPI0 21 +#define NPCM7XX_CLK_SPIX 22 + +#define NPCM7XX_CLK_REFCLK 23 +#define NPCM7XX_CLK_SYSBYPCK 24 +#define NPCM7XX_CLK_MCBYPCK 25 + +#define NPCM7XX_NUM_CLOCKS (NPCM7XX_CLK_MCBYPCK+1) + +#endif From 64f89d870579c186f233f046480609436d6e4692 Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Wed, 10 Jan 2018 00:46:43 +0200 Subject: [PATCH 27/37] clk: npcm: add NPCM7xx clock driver Add Nuvoton BMC NPCM7xx clock bus driver. Signed-off-by: Tomer Maimon --- drivers/clk/Makefile | 1 + drivers/clk/clk-npcm7xx.c | 729 ++++++++++++++++++++++++++++++++++++ include/linux/clk/nuvoton.h | 13 + 3 files changed, 743 insertions(+) create mode 100644 drivers/clk/clk-npcm7xx.c create mode 100644 include/linux/clk/nuvoton.h diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index cd376b3fb47adc..381aadc4e6886e 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o obj-$(CONFIG_ARCH_MB86S7X) += clk-mb86s7x.o obj-$(CONFIG_ARCH_MOXART) += clk-moxart.o obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o +obj-$(CONFIG_ARCH_NPCM7XX) += clk-npcm7xx.o obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o obj-$(CONFIG_COMMON_CLK_OXNAS) += clk-oxnas.o obj-$(CONFIG_COMMON_CLK_PALMAS) += clk-palmas.o diff --git a/drivers/clk/clk-npcm7xx.c b/drivers/clk/clk-npcm7xx.c new file mode 100644 index 00000000000000..8ee7786e54ad50 --- /dev/null +++ b/drivers/clk/clk-npcm7xx.c @@ -0,0 +1,729 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Nuvoton NPCM7xx Clock Generator + * All the clocks are initialized by the bootloader, so this driver allow only + * reading of current settings directly from the hardware. + * + * Copyright (C) 2018 Nuvoton Technologies tali.perry@nuvoton.com + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +struct npcm7xx_clk_pll { + struct clk_hw hw; + void __iomem *pllcon; + u8 flags; +}; + +#define to_npcm7xx_clk_pll(_hw) container_of(_hw, struct npcm7xx_clk_pll, hw) + +struct clk_hw *npcm7xx_clk_register_pll(void __iomem *pllcon, const char *name, + const char *parent_name, unsigned long flags); + +#define PLLCON_LOKI BIT(31) +#define PLLCON_LOKS BIT(30) +#define PLLCON_FBDV GENMASK(27, 16) +#define PLLCON_OTDV2 GENMASK(15, 13) +#define PLLCON_PWDEN BIT(12) +#define PLLCON_OTDV1 GENMASK(10, 8) +#define PLLCON_INDV GENMASK(5, 0) + +static unsigned long npcm7xx_clk_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct npcm7xx_clk_pll *pll = to_npcm7xx_clk_pll(hw); + unsigned long fbdv, indv, otdv1, otdv2; + unsigned int val; + u64 ret; + + if (parent_rate == 0) { + pr_err("%s: parent rate is zero. reg=%x\n", __func__, + (u32)(pll->pllcon)); + return 0; + } + + val = readl_relaxed(pll->pllcon); + + indv = FIELD_GET(PLLCON_INDV, val); + fbdv = FIELD_GET(PLLCON_FBDV, val); + otdv1 = FIELD_GET(PLLCON_OTDV1, val); + otdv2 = FIELD_GET(PLLCON_OTDV2, val); + + ret = (u64)parent_rate * fbdv; + do_div(ret, indv * otdv1 * otdv2); + + return ret; +} + +const struct clk_ops npcm7xx_clk_pll_ops = { + .recalc_rate = npcm7xx_clk_pll_recalc_rate, +}; + + +struct clk_hw *npcm7xx_clk_register_pll(void __iomem *pllcon, const char *name, + const char *parent_name, unsigned long flags) +{ + struct npcm7xx_clk_pll *pll; + struct clk_init_data init; + struct clk_hw *hw; + int ret; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + pr_debug("npcm7xx_clk_register_pll reg, reg=0x%x, name=%s, p=%s\n", + (unsigned int)pllcon, name, parent_name); + + init.name = name; + init.ops = &npcm7xx_clk_pll_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + init.flags = flags; + + pll->pllcon = pllcon; + pll->hw.init = &init; + + hw = &pll->hw; + + ret = clk_hw_register(NULL, hw); + if (ret) { + kfree(pll); + hw = ERR_PTR(ret); + } + + return hw; +} + + +#define NPCM7XX_CLKEN1 (0x00) +#define NPCM7XX_CLKEN2 (0x28) +#define NPCM7XX_CLKEN3 (0x30) +#define NPCM7XX_CLKSEL (0x04) +#define NPCM7XX_CLKDIV1 (0x08) +#define NPCM7XX_CLKDIV2 (0x2C) +#define NPCM7XX_CLKDIV3 (0x58) +#define NPCM7XX_PLLCON0 (0x0C) +#define NPCM7XX_PLLCON1 (0x10) +#define NPCM7XX_PLLCON2 (0x54) +#define NPCM7XX_SWRSTR (0x14) +#define NPCM7XX_IRQWAKECON (0x18) +#define NPCM7XX_IRQWAKEFLAG (0x1C) +#define NPCM7XX_IPSRST1 (0x20) +#define NPCM7XX_IPSRST2 (0x24) +#define NPCM7XX_IPSRST3 (0x34) +#define NPCM7XX_WD0RCR (0x38) +#define NPCM7XX_WD1RCR (0x3C) +#define NPCM7XX_WD2RCR (0x40) +#define NPCM7XX_SWRSTC1 (0x44) +#define NPCM7XX_SWRSTC2 (0x48) +#define NPCM7XX_SWRSTC3 (0x4C) +#define NPCM7XX_SWRSTC4 (0x50) +#define NPCM7XX_CORSTC (0x5C) +#define NPCM7XX_PLLCONG (0x60) +#define NPCM7XX_AHBCKFI (0x64) +#define NPCM7XX_SECCNT (0x68) +#define NPCM7XX_CNTR25M (0x6C) + + +struct npcm7xx_clk_gate_data { + u32 reg; + u8 bit_idx; + const char *name; + const char *parent_name; + unsigned long flags; + /* + * If this clock is exported via DT, set onecell_idx to constant + * defined in include/dt-bindings/clock/nuvoton, NPCM7XX-clock.h for + * this specific clock. Otherwise, set to -1. + */ + int onecell_idx; +}; + +struct npcm7xx_clk_mux_data { + u8 shift; + u8 mask; + u32 *table; + const char *name; + const char * const *parent_names; + u8 num_parents; + unsigned long flags; + /* + * If this clock is exported via DT, set onecell_idx to constant + * defined in include/dt-bindings/clock/nuvoton, NPCM7XX-clock.h for + * this specific clock. Otherwise, set to -1. + */ + int onecell_idx; + +}; + +struct npcm7xx_clk_div_fixed_data { + u8 mult; + u8 div; + const char *name; + const char *parent_name; + u8 clk_divider_flags; + /* + * If this clock is exported via DT, set onecell_idx to constant + * defined in include/dt-bindings/clock/nuvoton, NPCM7XX-clock.h for + * this specific clock. Otherwise, set to -1. + */ + int onecell_idx; +}; + + +struct npcm7xx_clk_div_data { + u32 reg; + u8 shift; + u8 width; + const char *name; + const char *parent_name; + u8 clk_divider_flags; + unsigned long flags; + /* + * If this clock is exported via DT, set onecell_idx to constant + * defined in include/dt-bindings/clock/nuvoton, NPCM7XX-clock.h for + * this specific clock. Otherwise, set to -1. + */ + int onecell_idx; +}; + +struct npcm7xx_clk_pll_data { + u32 reg; + const char *name; + const char *parent_name; + unsigned long flags; + /* + * If this clock is exported via DT, set onecell_idx to constant + * defined in include/dt-bindings/clock/nuvoton, NPCM7XX-clock.h for + * this specific clock. Otherwise, set to -1. + */ + int onecell_idx; +}; + + +/* + * Single copy of strings used to refer to clocks within this driver indexed by + * above enum. + */ +#define NPCM7XX_CLK_S_REFCLK "refclk" +#define NPCM7XX_CLK_S_SYSBYPCK "sysbypck" +#define NPCM7XX_CLK_S_MCBYPCK "mcbypck" +#define NPCM7XX_CLK_S_GFXBYPCK "gfxbypck" +#define NPCM7XX_CLK_S_PLL0 "pll0" +#define NPCM7XX_CLK_S_PLL1 "pll1" +#define NPCM7XX_CLK_S_PLL1_DIV2 "pll1_div2" +#define NPCM7XX_CLK_S_PLL2 "pll2" +#define NPCM7XX_CLK_S_PLL_GFX "pll_gfx" +#define NPCM7XX_CLK_S_PLL2_DIV2 "pll2_div2" +#define NPCM7XX_CLK_S_PIX_MUX "gfx_pixel" +#define NPCM7XX_CLK_S_GPRFSEL_MUX "gprfsel_mux" +#define NPCM7XX_CLK_S_MC_MUX "mc_phy" +#define NPCM7XX_CLK_S_CPU_MUX "cpu" /*AKA system clock.*/ +#define NPCM7XX_CLK_S_MC "mc" +#define NPCM7XX_CLK_S_AXI "axi" /*AKA CLK2*/ +#define NPCM7XX_CLK_S_AHB "ahb" /*AKA CLK4*/ +#define NPCM7XX_CLK_S_CLKOUT_MUX "clkout_mux" +#define NPCM7XX_CLK_S_UART_MUX "uart_mux" +#define NPCM7XX_CLK_S_TIM_MUX "timer_mux" +#define NPCM7XX_CLK_S_SD_MUX "sd_mux" +#define NPCM7XX_CLK_S_GFXM_MUX "gfxm_mux" +#define NPCM7XX_CLK_S_SU_MUX "serial_usb_mux" +#define NPCM7XX_CLK_S_DVC_MUX "dvc_mux" +#define NPCM7XX_CLK_S_GFX_MUX "gfx_mux" +#define NPCM7XX_CLK_S_GFX_PIXEL "gfx_pixel" +#define NPCM7XX_CLK_S_SPI0 "spi0" +#define NPCM7XX_CLK_S_SPI3 "spi3" +#define NPCM7XX_CLK_S_SPIX "spix" +#define NPCM7XX_CLK_S_APB1 "apb1" +#define NPCM7XX_CLK_S_APB2 "apb2" +#define NPCM7XX_CLK_S_APB3 "apb3" +#define NPCM7XX_CLK_S_APB4 "apb4" +#define NPCM7XX_CLK_S_APB5 "apb5" +#define NPCM7XX_CLK_S_TOCK "tock" +#define NPCM7XX_CLK_S_CLKOUT "clkout" +#define NPCM7XX_CLK_S_UART "uart" +#define NPCM7XX_CLK_S_TIMER "timer" +#define NPCM7XX_CLK_S_MMC "mmc" +#define NPCM7XX_CLK_S_SDHC "sdhc" +#define NPCM7XX_CLK_S_ADC "adc" +#define NPCM7XX_CLK_S_GFX "gfx0_gfx1_mem" +#define NPCM7XX_CLK_S_USBIF "serial_usbif" +#define NPCM7XX_CLK_S_USB_HOST "usb_host" +#define NPCM7XX_CLK_S_USB_BRIDGE "usb_bridge" +#define NPCM7XX_CLK_S_PCI "pci" + + +static u32 pll_mux_table[] = {0, 1, 2, 3}; +static const char * const pll_mux_parents[] __initconst = { + NPCM7XX_CLK_S_PLL0, + NPCM7XX_CLK_S_PLL1_DIV2, + NPCM7XX_CLK_S_REFCLK, + NPCM7XX_CLK_S_PLL2_DIV2, +}; + +static u32 cpuck_mux_table[] = {0, 1, 2, 3}; +static const char * const cpuck_mux_parents[] __initconst = { + NPCM7XX_CLK_S_PLL0, + NPCM7XX_CLK_S_PLL1_DIV2, + NPCM7XX_CLK_S_REFCLK, + NPCM7XX_CLK_S_SYSBYPCK, +}; + +static u32 pixcksel_mux_table[] = {0, 2}; +static const char * const pixcksel_mux_parents[] __initconst = { + NPCM7XX_CLK_S_PLL_GFX, + NPCM7XX_CLK_S_REFCLK, +}; + +static u32 sucksel_mux_table[] = {2, 3}; +static const char * const sucksel_mux_parents[] __initconst = { + NPCM7XX_CLK_S_REFCLK, + NPCM7XX_CLK_S_PLL2_DIV2, +}; + +static u32 mccksel_mux_table[] = {0, 2, 3}; +static const char * const mccksel_mux_parents[] __initconst = { + NPCM7XX_CLK_S_PLL1_DIV2, + NPCM7XX_CLK_S_REFCLK, + NPCM7XX_CLK_S_MCBYPCK, +}; + +static u32 clkoutsel_mux_table[] = {0, 1, 2, 3, 4}; +static const char * const clkoutsel_mux_parents[] __initconst = { + NPCM7XX_CLK_S_PLL0, + NPCM7XX_CLK_S_PLL1_DIV2, + NPCM7XX_CLK_S_REFCLK, + NPCM7XX_CLK_S_PLL_GFX, // divided by 2 + NPCM7XX_CLK_S_PLL2_DIV2, +}; + +static u32 gfxmsel_mux_table[] = {2, 3}; +static const char * const gfxmsel_mux_parents[] __initconst = { + NPCM7XX_CLK_S_REFCLK, + NPCM7XX_CLK_S_PLL2_DIV2, +}; + +static u32 dvcssel_mux_table[] = {2, 3}; +static const char * const dvcssel_mux_parents[] __initconst = { + NPCM7XX_CLK_S_REFCLK, + NPCM7XX_CLK_S_PLL2, +}; + + +static const struct npcm7xx_clk_pll_data npcm7xx_plls[] __initconst = { + {NPCM7XX_PLLCON0, NPCM7XX_CLK_S_PLL0, NPCM7XX_CLK_S_REFCLK, 0, -1}, + + {NPCM7XX_PLLCON1, NPCM7XX_CLK_S_PLL1, + NPCM7XX_CLK_S_REFCLK, 0, -1}, + + {NPCM7XX_PLLCON2, NPCM7XX_CLK_S_PLL2, + NPCM7XX_CLK_S_REFCLK, 0, -1}, + + {NPCM7XX_PLLCONG, NPCM7XX_CLK_S_PLL_GFX, + NPCM7XX_CLK_S_REFCLK, 0, -1}, +}; + + +static const struct npcm7xx_clk_mux_data npcm7xx_muxes[] __initconst = { + {0, GENMASK(1, 0), cpuck_mux_table, NPCM7XX_CLK_S_CPU_MUX, + cpuck_mux_parents, ARRAY_SIZE(cpuck_mux_parents), CLK_IS_CRITICAL, + NPCM7XX_CLK_CPU}, + + {4, GENMASK(1, 0), pixcksel_mux_table, NPCM7XX_CLK_S_PIX_MUX, + pixcksel_mux_parents, ARRAY_SIZE(pixcksel_mux_parents), 0, + NPCM7XX_CLK_GFX_PIXEL}, + + {6, GENMASK(1, 0), pll_mux_table, NPCM7XX_CLK_S_SD_MUX, + pll_mux_parents, ARRAY_SIZE(pll_mux_parents), 0, -1}, + + {8, GENMASK(1, 0), pll_mux_table, NPCM7XX_CLK_S_UART_MUX, + pll_mux_parents, ARRAY_SIZE(pll_mux_parents), 0, -1}, + + {10, GENMASK(1, 0), sucksel_mux_table, NPCM7XX_CLK_S_SU_MUX, + sucksel_mux_parents, ARRAY_SIZE(sucksel_mux_parents), 0, -1}, + + {12, GENMASK(1, 0), mccksel_mux_table, NPCM7XX_CLK_S_MC_MUX, + mccksel_mux_parents, ARRAY_SIZE(mccksel_mux_parents), 0, -1}, + + {14, GENMASK(1, 0), pll_mux_table, NPCM7XX_CLK_S_TIM_MUX, + pll_mux_parents, ARRAY_SIZE(pll_mux_parents), 0, -1}, + + {16, GENMASK(1, 0), pll_mux_table, NPCM7XX_CLK_S_GFX_MUX, + pll_mux_parents, ARRAY_SIZE(pll_mux_parents), 0, -1}, + + {18, GENMASK(2, 0), clkoutsel_mux_table, NPCM7XX_CLK_S_CLKOUT_MUX, + clkoutsel_mux_parents, ARRAY_SIZE(clkoutsel_mux_parents), 0, -1}, + + {21, GENMASK(1, 0), gfxmsel_mux_table, NPCM7XX_CLK_S_GFXM_MUX, + gfxmsel_mux_parents, ARRAY_SIZE(gfxmsel_mux_parents), 0, -1}, + + {23, GENMASK(1, 0), dvcssel_mux_table, NPCM7XX_CLK_S_DVC_MUX, + dvcssel_mux_parents, ARRAY_SIZE(dvcssel_mux_parents), 0, -1}, +}; + +/* fixed ratio dividers (no register): */ +static const struct npcm7xx_clk_div_fixed_data npcm7xx_divs_fx[] __initconst = { + { 1, 2, NPCM7XX_CLK_S_MC, NPCM7XX_CLK_S_MC_MUX, 0, NPCM7XX_CLK_MC}, + { 1, 2, NPCM7XX_CLK_S_PLL1_DIV2, NPCM7XX_CLK_S_PLL1, 0, -1}, + { 1, 2, NPCM7XX_CLK_S_PLL2_DIV2, NPCM7XX_CLK_S_PLL2, 0, -1}, +}; + + +/* configurable dividers: */ +static const struct npcm7xx_clk_div_data npcm7xx_divs[] __initconst = { + {NPCM7XX_CLKDIV1, 28, 3, NPCM7XX_CLK_S_ADC, + NPCM7XX_CLK_S_TIMER, CLK_DIVIDER_POWER_OF_TWO, 0, NPCM7XX_CLK_ADC}, + /*30-28 ADCCKDIV*/ + {NPCM7XX_CLKDIV1, 26, 2, NPCM7XX_CLK_S_AHB, + NPCM7XX_CLK_S_AXI, 0, CLK_IS_CRITICAL, NPCM7XX_CLK_AHB}, + /*27-26 CLK4DIV*/ + {NPCM7XX_CLKDIV1, 21, 5, NPCM7XX_CLK_S_TIMER, + NPCM7XX_CLK_S_TIM_MUX, 0, 0, NPCM7XX_CLK_TIMER}, + /*25-21 TIMCKDIV*/ + {NPCM7XX_CLKDIV1, 16, 5, NPCM7XX_CLK_S_UART, + NPCM7XX_CLK_S_UART_MUX, 0, 0, NPCM7XX_CLK_UART}, + /*20-16 UARTDIV*/ + {NPCM7XX_CLKDIV1, 11, 5, NPCM7XX_CLK_S_MMC, + NPCM7XX_CLK_S_SD_MUX, 0, 0, NPCM7XX_CLK_MMC}, + /*15-11 MMCCKDIV*/ + {NPCM7XX_CLKDIV1, 6, 5, NPCM7XX_CLK_S_SPI3, + NPCM7XX_CLK_S_AHB, 0, 0, NPCM7XX_CLK_SPI3}, + /*10-6 AHB3CKDIV*/ + {NPCM7XX_CLKDIV1, 2, 4, NPCM7XX_CLK_S_PCI, + NPCM7XX_CLK_S_GFX_MUX, 0, 0, NPCM7XX_CLK_PCI}, + /*5-2 PCICKDIV*/ + {NPCM7XX_CLKDIV1, 0, 1, NPCM7XX_CLK_S_AXI, + NPCM7XX_CLK_S_CPU_MUX, CLK_DIVIDER_POWER_OF_TWO, CLK_IS_CRITICAL, + NPCM7XX_CLK_AXI},/*0 CLK2DIV*/ + + {NPCM7XX_CLKDIV2, 30, 2, NPCM7XX_CLK_S_APB4, + NPCM7XX_CLK_S_AHB, CLK_DIVIDER_POWER_OF_TWO, 0, NPCM7XX_CLK_APB4}, + /*31-30 APB4CKDIV*/ + {NPCM7XX_CLKDIV2, 28, 2, NPCM7XX_CLK_S_APB3, + NPCM7XX_CLK_S_AHB, CLK_DIVIDER_POWER_OF_TWO, 0, NPCM7XX_CLK_APB3}, + /*29-28 APB3CKDIV*/ + {NPCM7XX_CLKDIV2, 26, 2, NPCM7XX_CLK_S_APB2, + NPCM7XX_CLK_S_AHB, CLK_DIVIDER_POWER_OF_TWO, 0, NPCM7XX_CLK_APB2}, + /*27-26 APB2CKDIV*/ + {NPCM7XX_CLKDIV2, 24, 2, NPCM7XX_CLK_S_APB1, + NPCM7XX_CLK_S_AHB, CLK_DIVIDER_POWER_OF_TWO, 0, NPCM7XX_CLK_APB1}, + /*25-24 APB1CKDIV*/ + {NPCM7XX_CLKDIV2, 22, 2, NPCM7XX_CLK_S_APB5, + NPCM7XX_CLK_S_AHB, CLK_DIVIDER_POWER_OF_TWO, 0, NPCM7XX_CLK_APB5}, + /*23-22 APB5CKDIV*/ + {NPCM7XX_CLKDIV2, 16, 5, NPCM7XX_CLK_S_CLKOUT, + NPCM7XX_CLK_S_CLKOUT_MUX, 0, 0, NPCM7XX_CLK_CLKOUT}, + /*20-16 CLKOUTDIV*/ + {NPCM7XX_CLKDIV2, 13, 3, NPCM7XX_CLK_S_GFX, + NPCM7XX_CLK_S_GFX_MUX, 0, 0, NPCM7XX_CLK_GFX}, + /*15-13 GFXCKDIV*/ + {NPCM7XX_CLKDIV2, 8, 5, NPCM7XX_CLK_S_USB_BRIDGE, + NPCM7XX_CLK_S_SU_MUX, 0, 0, NPCM7XX_CLK_SU}, + /*12-8 SUCKDIV*/ + {NPCM7XX_CLKDIV2, 4, 4, NPCM7XX_CLK_S_USB_HOST, + NPCM7XX_CLK_S_SU_MUX, 0, 0, NPCM7XX_CLK_SU48}, + /*7-4 SU48CKDIV*/ + {NPCM7XX_CLKDIV2, 0, 4, NPCM7XX_CLK_S_SDHC, + NPCM7XX_CLK_S_SD_MUX, 0, 0, NPCM7XX_CLK_SDHC} + ,/*3-0 SD1CKDIV*/ + + {NPCM7XX_CLKDIV3, 6, 5, NPCM7XX_CLK_S_SPI0, + NPCM7XX_CLK_S_AHB, 0, 0, NPCM7XX_CLK_SPI0}, + /*10-6 SPI0CKDV*/ + {NPCM7XX_CLKDIV3, 1, 5, NPCM7XX_CLK_S_SPIX, + NPCM7XX_CLK_S_AHB, 0, 0, NPCM7XX_CLK_SPIX}, + /*5-1 SPIXCKDV*/ + +}; + + +static const struct npcm7xx_clk_gate_data npcm7xx_gates[] __initconst = { + {NPCM7XX_CLKEN1, 31, "smb1-gate", NPCM7XX_CLK_S_APB2, 0}, + {NPCM7XX_CLKEN1, 30, "smb0-gate", NPCM7XX_CLK_S_APB2, 0}, + {NPCM7XX_CLKEN1, 29, "smb7-gate", NPCM7XX_CLK_S_APB2, 0}, + {NPCM7XX_CLKEN1, 28, "smb6-gate", NPCM7XX_CLK_S_APB2, 0}, + {NPCM7XX_CLKEN1, 27, "adc-gate", NPCM7XX_CLK_S_APB1, 0}, + {NPCM7XX_CLKEN1, 26, "wdt-gate", NPCM7XX_CLK_S_TIMER, 0}, + {NPCM7XX_CLKEN1, 25, "usbdev3-gate", NPCM7XX_CLK_S_AHB, 0}, + {NPCM7XX_CLKEN1, 24, "usbdev6-gate", NPCM7XX_CLK_S_AHB, 0}, + {NPCM7XX_CLKEN1, 23, "usbdev5-gate", NPCM7XX_CLK_S_AHB, 0}, + {NPCM7XX_CLKEN1, 22, "usbdev4-gate", NPCM7XX_CLK_S_AHB, 0}, + {NPCM7XX_CLKEN1, 21, "emc2-gate", NPCM7XX_CLK_S_AHB, 0}, + {NPCM7XX_CLKEN1, 20, "timer5_9-gate", NPCM7XX_CLK_S_APB1, 0}, + {NPCM7XX_CLKEN1, 19, "timer0_4-gate", NPCM7XX_CLK_S_APB1, 0}, + {NPCM7XX_CLKEN1, 18, "pwmm0-gate", NPCM7XX_CLK_S_APB3, 0}, + {NPCM7XX_CLKEN1, 17, "huart-gate", NPCM7XX_CLK_S_UART, 0}, + {NPCM7XX_CLKEN1, 16, "smb5-gate", NPCM7XX_CLK_S_APB2, 0}, + {NPCM7XX_CLKEN1, 15, "smb4-gate", NPCM7XX_CLK_S_APB2, 0}, + {NPCM7XX_CLKEN1, 14, "smb3-gate", NPCM7XX_CLK_S_APB2, 0}, + {NPCM7XX_CLKEN1, 13, "smb2-gate", NPCM7XX_CLK_S_APB2, 0}, + {NPCM7XX_CLKEN1, 12, "mc-gate", NPCM7XX_CLK_S_MC, 0}, + {NPCM7XX_CLKEN1, 11, "uart01-gate", NPCM7XX_CLK_S_APB1, 0}, + {NPCM7XX_CLKEN1, 10, "aes-gate", NPCM7XX_CLK_S_AHB, 0}, + {NPCM7XX_CLKEN1, 9, "peci-gate", NPCM7XX_CLK_S_APB3, 0}, + {NPCM7XX_CLKEN1, 8, "usbdev2-gate", NPCM7XX_CLK_S_AHB, 0}, + {NPCM7XX_CLKEN1, 7, "uart23-gate", NPCM7XX_CLK_S_APB1, 0}, + {NPCM7XX_CLKEN1, 6, "emc1-gate", NPCM7XX_CLK_S_AHB, 0}, + {NPCM7XX_CLKEN1, 5, "usbdev1-gate", NPCM7XX_CLK_S_AHB, 0}, + {NPCM7XX_CLKEN1, 4, "shm-gate", NPCM7XX_CLK_S_AHB, 0}, + /* bit 3 is reserved */ + {NPCM7XX_CLKEN1, 2, "kcs-gate", NPCM7XX_CLK_S_APB1, 0}, + {NPCM7XX_CLKEN1, 1, "spi3-gate", NPCM7XX_CLK_S_AHB, 0}, + {NPCM7XX_CLKEN1, 0, "spi0-gate", NPCM7XX_CLK_S_AHB, 0}, + + {NPCM7XX_CLKEN2, 31, "cp-gate", NPCM7XX_CLK_S_AHB, 0}, + {NPCM7XX_CLKEN2, 30, "tock-gate", NPCM7XX_CLK_S_TOCK, 0}, + /* bit 29 is reserved */ + {NPCM7XX_CLKEN2, 28, "gmac1-gate", NPCM7XX_CLK_S_AHB, 0}, + {NPCM7XX_CLKEN2, 27, "usbif-gate", NPCM7XX_CLK_S_USBIF, 0}, + {NPCM7XX_CLKEN2, 26, "usbhost-gate", NPCM7XX_CLK_S_AHB, 0}, + {NPCM7XX_CLKEN2, 25, "gmac2-gate", NPCM7XX_CLK_S_AHB, 0}, + /* bit 24 is reserved */ + {NPCM7XX_CLKEN2, 23, "pspi2-gate", NPCM7XX_CLK_S_APB5, 0}, + {NPCM7XX_CLKEN2, 22, "pspi1-gate", NPCM7XX_CLK_S_APB5, 0}, + {NPCM7XX_CLKEN2, 21, "3des-gate", NPCM7XX_CLK_S_AHB, 0}, + /* bit 20 is reserved */ + {NPCM7XX_CLKEN2, 19, "siox2-gate", NPCM7XX_CLK_S_APB3, 0}, + {NPCM7XX_CLKEN2, 18, "siox1-gate", NPCM7XX_CLK_S_APB3, 0}, + /* bit 17 is reserved */ + {NPCM7XX_CLKEN2, 16, "fuse-gate", NPCM7XX_CLK_S_APB4, 0}, + /* bit 15 is reserved */ + {NPCM7XX_CLKEN2, 14, "vcd-gate", NPCM7XX_CLK_S_AHB, 0}, + {NPCM7XX_CLKEN2, 13, "ece-gate", NPCM7XX_CLK_S_AHB, 0}, + {NPCM7XX_CLKEN2, 12, "vdma-gate", NPCM7XX_CLK_S_AHB, 0}, + {NPCM7XX_CLKEN2, 11, "ahbpcibrg-gate", NPCM7XX_CLK_S_AHB, 0}, + {NPCM7XX_CLKEN2, 10, "gfxsys-gate", NPCM7XX_CLK_S_APB1, 0}, + {NPCM7XX_CLKEN2, 9, "sdhc-gate", NPCM7XX_CLK_S_AHB, 0}, + {NPCM7XX_CLKEN2, 8, "mmc-gate", NPCM7XX_CLK_S_AHB, 0}, + {NPCM7XX_CLKEN2, 7, "mft7-gate", NPCM7XX_CLK_S_APB4, 0}, + {NPCM7XX_CLKEN2, 6, "mft6-gate", NPCM7XX_CLK_S_APB4, 0}, + {NPCM7XX_CLKEN2, 5, "mft5-gate", NPCM7XX_CLK_S_APB4, 0}, + {NPCM7XX_CLKEN2, 4, "mft4-gate", NPCM7XX_CLK_S_APB4, 0}, + {NPCM7XX_CLKEN2, 3, "mft3-gate", NPCM7XX_CLK_S_APB4, 0}, + {NPCM7XX_CLKEN2, 2, "mft2-gate", NPCM7XX_CLK_S_APB4, 0}, + {NPCM7XX_CLKEN2, 1, "mft1-gate", NPCM7XX_CLK_S_APB4, 0}, + {NPCM7XX_CLKEN2, 0, "mft0-gate", NPCM7XX_CLK_S_APB4, 0}, + + {NPCM7XX_CLKEN3, 31, "gpiom7-gate", NPCM7XX_CLK_S_APB1, 0}, + {NPCM7XX_CLKEN3, 30, "gpiom6-gate", NPCM7XX_CLK_S_APB1, 0}, + {NPCM7XX_CLKEN3, 29, "gpiom5-gate", NPCM7XX_CLK_S_APB1, 0}, + {NPCM7XX_CLKEN3, 28, "gpiom4-gate", NPCM7XX_CLK_S_APB1, 0}, + {NPCM7XX_CLKEN3, 27, "gpiom3-gate", NPCM7XX_CLK_S_APB1, 0}, + {NPCM7XX_CLKEN3, 26, "gpiom2-gate", NPCM7XX_CLK_S_APB1, 0}, + {NPCM7XX_CLKEN3, 25, "gpiom1-gate", NPCM7XX_CLK_S_APB1, 0}, + {NPCM7XX_CLKEN3, 24, "gpiom0-gate", NPCM7XX_CLK_S_APB1, 0}, + {NPCM7XX_CLKEN3, 23, "espi-gate", NPCM7XX_CLK_S_APB2, 0}, + {NPCM7XX_CLKEN3, 22, "smb11-gate", NPCM7XX_CLK_S_APB2, 0}, + {NPCM7XX_CLKEN3, 21, "smb10-gate", NPCM7XX_CLK_S_APB2, 0}, + {NPCM7XX_CLKEN3, 20, "smb9-gate", NPCM7XX_CLK_S_APB2, 0}, + {NPCM7XX_CLKEN3, 19, "smb8-gate", NPCM7XX_CLK_S_APB2, 0}, + {NPCM7XX_CLKEN3, 18, "smb15-gate", NPCM7XX_CLK_S_APB2, 0}, + {NPCM7XX_CLKEN3, 17, "rng-gate", NPCM7XX_CLK_S_APB1, 0}, + {NPCM7XX_CLKEN3, 16, "timer10_14-gate", NPCM7XX_CLK_S_APB1, 0}, + {NPCM7XX_CLKEN3, 15, "pcirc-gate", NPCM7XX_CLK_S_AHB, 0}, + {NPCM7XX_CLKEN3, 14, "sececc-gate", NPCM7XX_CLK_S_AHB, 0}, + {NPCM7XX_CLKEN3, 13, "sha-gate", NPCM7XX_CLK_S_AHB, 0}, + {NPCM7XX_CLKEN3, 12, "smb14-gate", NPCM7XX_CLK_S_APB2, 0}, + /* bit 11 is reserved */ + /* bit 10 is reserved */ + {NPCM7XX_CLKEN3, 9, "pcimbx-gate", NPCM7XX_CLK_S_AHB, 0}, + /* bit 8 is reserved */ + {NPCM7XX_CLKEN3, 7, "usbdev9-gate", NPCM7XX_CLK_S_AHB, 0}, + {NPCM7XX_CLKEN3, 6, "usbdev8-gate", NPCM7XX_CLK_S_AHB, 0}, + {NPCM7XX_CLKEN3, 5, "usbdev7-gate", NPCM7XX_CLK_S_AHB, 0}, + {NPCM7XX_CLKEN3, 4, "usbdev0-gate", NPCM7XX_CLK_S_AHB, 0}, + {NPCM7XX_CLKEN3, 3, "smb13-gate", NPCM7XX_CLK_S_APB2, 0}, + {NPCM7XX_CLKEN3, 2, "spix-gate", NPCM7XX_CLK_S_AHB, 0}, + {NPCM7XX_CLKEN3, 1, "smb12-gate", NPCM7XX_CLK_S_APB2, 0}, + {NPCM7XX_CLKEN3, 0, "pwmm1-gate", NPCM7XX_CLK_S_APB3, 0}, +}; + + + +static DEFINE_SPINLOCK(npcm7xx_clk_lock); + + +static void __init npcm7xx_clk_init(struct device_node *clk_np) +{ + struct clk_hw_onecell_data *npcm7xx_clk_data; + void __iomem *clk_base; + struct resource res; + struct clk_hw *hw; + struct clk *clk; + int ret; + int i; + + clk_base = NULL; + + ret = of_address_to_resource(clk_np, 0, &res); + if (ret) { + pr_err("%s: failed to get resource, ret %d\n", clk_np->name, + ret); + return; + } + + + clk_base = ioremap(res.start, resource_size(&res)); + if (clk_base == NULL) { + goto npcm7xx_init_error; + } + + + npcm7xx_clk_data = kzalloc(sizeof(*npcm7xx_clk_data->hws) * + NPCM7XX_NUM_CLOCKS + sizeof(npcm7xx_clk_data), GFP_KERNEL); + + npcm7xx_clk_data->num = 0; + + if (!npcm7xx_clk_data->hws) { + pr_err("Can't alloc npcm7xx_clk_data\n"); + goto npcm7xx_init_np_err; + } + + npcm7xx_clk_data->num = NPCM7XX_NUM_CLOCKS; + + for (i = 0; i < NPCM7XX_NUM_CLOCKS; i++) + npcm7xx_clk_data->hws[i] = ERR_PTR(-EPROBE_DEFER); + + /* Read fixed clocks. These 3 clocks must be defined in DT */ + clk = of_clk_get_by_name(clk_np, NPCM7XX_CLK_S_REFCLK); + if (IS_ERR(clk)) { + pr_err("failed to find external REFCLK on device tree, err=%ld\n", + PTR_ERR(clk)); + clk_put(clk); + goto npcm7xx_init_fail_no_clk_on_dt; + } + + clk = of_clk_get_by_name(clk_np, NPCM7XX_CLK_S_SYSBYPCK); + if (IS_ERR(clk)) { + pr_err("failed to find external SYSBYPCK on device tree, err=%ld\n", + PTR_ERR(clk)); + clk_put(clk); + goto npcm7xx_init_fail_no_clk_on_dt; + } + + clk = of_clk_get_by_name(clk_np, NPCM7XX_CLK_S_MCBYPCK); + if (IS_ERR(clk)) { + pr_err("failed to find external MCBYPCK on device tree, err=%ld\n", + PTR_ERR(clk)); + clk_put(clk); + goto npcm7xx_init_fail_no_clk_on_dt; + } + + /* Register plls */ + for (i = 0; i < ARRAY_SIZE(npcm7xx_plls); i++) { + const struct npcm7xx_clk_pll_data *pll_data = &npcm7xx_plls[i]; + + hw = npcm7xx_clk_register_pll(clk_base + pll_data->reg, + pll_data->name, pll_data->parent_name, pll_data->flags); + if (IS_ERR(hw)) { + pr_err("npcm7xx_clk: Can't register pll\n"); + goto npcm7xx_init_fail; + } + + if (pll_data->onecell_idx >= 0) + npcm7xx_clk_data->hws[pll_data->onecell_idx] = hw; + } + + /* Register fixed dividers */ + clk = clk_register_fixed_factor(NULL, NPCM7XX_CLK_S_PLL1_DIV2, + NPCM7XX_CLK_S_PLL1, 0, 1, 2); + if (IS_ERR(clk)) { + pr_err("npcm7xx_clk: Can't register fixed div\n"); + goto npcm7xx_init_fail; + } + + + clk = clk_register_fixed_factor(NULL, NPCM7XX_CLK_S_PLL2_DIV2, + NPCM7XX_CLK_S_PLL2, 0, 1, 2); + + if (IS_ERR(clk)) { + pr_err("npcm7xx_clk: Can't register div2\n"); + goto npcm7xx_init_fail; + } + + /* Register muxes */ + for (i = 0; i < ARRAY_SIZE(npcm7xx_muxes); i++) { + const struct npcm7xx_clk_mux_data *mux_data = &npcm7xx_muxes[i]; + + hw = clk_hw_register_mux_table(NULL, + mux_data->name, + mux_data->parent_names, mux_data->num_parents, + mux_data->flags, clk_base + NPCM7XX_CLKSEL, + mux_data->shift, mux_data->mask, 0, + mux_data->table, &npcm7xx_clk_lock); + + if (IS_ERR(hw)) { + pr_err("npcm7xx_clk: Can't register mux\n"); + goto npcm7xx_init_fail; + } + + if (mux_data->onecell_idx >= 0) + npcm7xx_clk_data->hws[mux_data->onecell_idx] = hw; + } + + /* Register clock dividers specified in npcm7xx_divs. */ + for (i = 0; i < ARRAY_SIZE(npcm7xx_divs); i++) { + const struct npcm7xx_clk_div_data *div_data = &npcm7xx_divs[i]; + + hw = clk_hw_register_divider(NULL, div_data->name, + div_data->parent_name, + div_data->flags, + clk_base + div_data->reg, + div_data->shift, div_data->width, + div_data->clk_divider_flags, &npcm7xx_clk_lock); + if (IS_ERR(hw)) { + pr_err("npcm7xx_clk: Can't register div table\n"); + goto npcm7xx_init_fail; + } + + if (div_data->onecell_idx >= 0) + npcm7xx_clk_data->hws[div_data->onecell_idx] = hw; + } + + ret = of_clk_add_hw_provider(clk_np, of_clk_hw_onecell_get, + npcm7xx_clk_data); + if (ret) + pr_err("failed to add DT provider: %d\n", ret); + + + of_node_put(clk_np); + + return; + +npcm7xx_init_fail_no_clk_on_dt: + pr_err("see Documentation/devicetree/bindings/clock/" + "nuvoton,npcm750-clk.txt for details\n"); +npcm7xx_init_fail: + pr_err("clk setup fail\n"); + if (npcm7xx_clk_data->num) + kfree(npcm7xx_clk_data->hws); +npcm7xx_init_np_err: + if (clk_base != NULL) + iounmap(clk_base); +npcm7xx_init_error: + of_node_put(clk_np); +} + +CLK_OF_DECLARE(npcm7xx_clk_init, "nuvoton,npcm750-clk", npcm7xx_clk_init); + + + diff --git a/include/linux/clk/nuvoton.h b/include/linux/clk/nuvoton.h new file mode 100644 index 00000000000000..65eb26e1e5d183 --- /dev/null +++ b/include/linux/clk/nuvoton.h @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2014-2018 Nuvoton Technology corporation. + * + * Released under the GPLv2 only. + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef __LINUX_CLK_NUVOTON_H_ +#define __LINUX_CLK_NUVOTON_H_ + +void nuvoton_npcm750_clock_init(void); + +#endif From ec75a819ecd5c1351463f17a6f812913ab51e830 Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Thu, 4 Jan 2018 16:21:52 +0200 Subject: [PATCH 28/37] net: stmmac: bypass for lpi eee hang issue Bypass for lpi eee hang issue in the STMicroelectronics 10/100/1000 Ethernet driver Signed-off-by: Tomer Maimon --- drivers/net/ethernet/stmicro/stmmac/stmmac.h | 1 + .../net/ethernet/stmicro/stmmac/stmmac_main.c | 27 ++++++++++++++----- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index a916e13624ebe0..c19bec963e288e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -136,6 +136,7 @@ struct stmmac_priv { int use_riwt; int irq_wake; spinlock_t ptp_lock; + spinlock_t lpi_lock; void __iomem *mmcaddr; void __iomem *ptpaddr; u32 mss; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 1763e48c84e209..4c8c7a463f4017 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -333,9 +333,13 @@ static void stmmac_enable_eee_mode(struct stmmac_priv *priv) */ void stmmac_disable_eee_mode(struct stmmac_priv *priv) { + unsigned long flags; + priv->hw->mac->reset_eee_mode(priv->hw); del_timer_sync(&priv->eee_ctrl_timer); + spin_lock_irqsave(&priv->lpi_lock, flags); priv->tx_path_in_lpi_mode = false; + spin_unlock_irqrestore(&priv->lpi_lock, flags); } /** @@ -1872,6 +1876,7 @@ static void stmmac_tx_clean(struct stmmac_priv *priv, u32 queue) netdev_tx_completed_queue(netdev_get_tx_queue(priv->dev, queue), pkts_compl, bytes_compl); + netif_tx_unlock(priv->dev); if (unlikely(netif_tx_queue_stopped(netdev_get_tx_queue(priv->dev, queue))) && @@ -1882,11 +1887,16 @@ static void stmmac_tx_clean(struct stmmac_priv *priv, u32 queue) netif_tx_wake_queue(netdev_get_tx_queue(priv->dev, queue)); } - if ((priv->eee_enabled) && (!priv->tx_path_in_lpi_mode)) { - stmmac_enable_eee_mode(priv); - mod_timer(&priv->eee_ctrl_timer, STMMAC_LPI_T(eee_timer)); + if (priv->eee_enabled) { + unsigned long flags; + + spin_lock_irqsave(&priv->lpi_lock, flags); + if (!priv->tx_path_in_lpi_mode) + mod_timer(&priv->eee_ctrl_timer, + STMMAC_LPI_T(eee_timer)); + + spin_unlock_irqrestore(&priv->lpi_lock, flags); } - netif_tx_unlock(priv->dev); } static inline void stmmac_enable_dma_irq(struct stmmac_priv *priv, u32 chan) @@ -2968,6 +2978,9 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) unsigned int enh_desc; unsigned int des; + if (priv->tx_path_in_lpi_mode) + stmmac_disable_eee_mode(priv); + tx_q = &priv->tx_queue[queue]; /* Manage oversized TCP frames for GMAC4 device */ @@ -2988,9 +3001,6 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_BUSY; } - if (priv->tx_path_in_lpi_mode) - stmmac_disable_eee_mode(priv); - entry = tx_q->cur_tx; first_entry = entry; @@ -3638,11 +3648,13 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id) &priv->xstats); if (unlikely(status)) { + spin_lock(&priv->lpi_lock); /* For LPI we need to save the tx status */ if (status & CORE_IRQ_TX_PATH_IN_LPI_MODE) priv->tx_path_in_lpi_mode = true; if (status & CORE_IRQ_TX_PATH_EXIT_LPI_MODE) priv->tx_path_in_lpi_mode = false; + spin_unlock(&priv->lpi_lock); } if (priv->synopsys_id >= DWMAC_CORE_4_00) { @@ -4196,6 +4208,7 @@ int stmmac_dvr_probe(struct device *device, } spin_lock_init(&priv->lock); + spin_lock_init(&priv->lpi_lock); /* If a specific clk_csr value is passed from the platform * this means that the CSR Clock Range selection cannot be From fedd6f206c7b7cb6924fac5a0b01f0977cbe4f77 Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Wed, 25 Apr 2018 15:02:32 +0300 Subject: [PATCH 29/37] ipmi: add a KCS IPMI BMC driver Signed-off-by: Tomer Maimon --- drivers/char/ipmi/Kconfig | 8 + drivers/char/ipmi/Makefile | 1 + drivers/char/ipmi/kcs_bmc.c | 464 ++++++++++++++++++++++++++++++++++ drivers/char/ipmi/kcs_bmc.h | 106 ++++++++ include/uapi/linux/ipmi_bmc.h | 14 + 5 files changed, 593 insertions(+) create mode 100644 drivers/char/ipmi/kcs_bmc.c create mode 100644 drivers/char/ipmi/kcs_bmc.h create mode 100644 include/uapi/linux/ipmi_bmc.h diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig index f6fa056a52fcfa..985e3e6b20b2fb 100644 --- a/drivers/char/ipmi/Kconfig +++ b/drivers/char/ipmi/Kconfig @@ -81,6 +81,14 @@ config IPMI_POWEROFF endif # IPMI_HANDLER +config IPMI_KCS_BMC + tristate 'IPMI KCS BMC Interface' + help + Provides a device driver for the KCS (Keyboard Controller Style) + IPMI interface which meets the requirement of the BMC (Baseboard + Management Controllers) side for handling the IPMI request from + host system software. + config ASPEED_BT_IPMI_BMC depends on ARCH_ASPEED || COMPILE_TEST depends on REGMAP && REGMAP_MMIO && MFD_SYSCON diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile index eefb0b301e836b..f80cf54006a639 100644 --- a/drivers/char/ipmi/Makefile +++ b/drivers/char/ipmi/Makefile @@ -12,4 +12,5 @@ obj-$(CONFIG_IPMI_SSIF) += ipmi_ssif.o obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o +obj-$(CONFIG_IPMI_KCS_BMC) += kcs_bmc.o obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += bt-bmc.o diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c new file mode 100644 index 00000000000000..3a3498afa427e1 --- /dev/null +++ b/drivers/char/ipmi/kcs_bmc.c @@ -0,0 +1,464 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2015-2018, Intel Corporation. + +#define pr_fmt(fmt) "kcs-bmc: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kcs_bmc.h" + +#define KCS_MSG_BUFSIZ 1000 + +#define KCS_ZERO_DATA 0 + + +/* IPMI 2.0 - Table 9-1, KCS Interface Status Register Bits */ +#define KCS_STATUS_STATE(state) (state << 6) +#define KCS_STATUS_STATE_MASK GENMASK(7, 6) +#define KCS_STATUS_CMD_DAT BIT(3) +#define KCS_STATUS_SMS_ATN BIT(2) +#define KCS_STATUS_IBF BIT(1) +#define KCS_STATUS_OBF BIT(0) + +/* IPMI 2.0 - Table 9-2, KCS Interface State Bits */ +enum kcs_states { + IDLE_STATE = 0, + READ_STATE = 1, + WRITE_STATE = 2, + ERROR_STATE = 3, +}; + +/* IPMI 2.0 - Table 9-3, KCS Interface Control Codes */ +#define KCS_CMD_GET_STATUS_ABORT 0x60 +#define KCS_CMD_WRITE_START 0x61 +#define KCS_CMD_WRITE_END 0x62 +#define KCS_CMD_READ_BYTE 0x68 + +static inline u8 read_data(struct kcs_bmc *kcs_bmc) +{ + return kcs_bmc->io_inputb(kcs_bmc, kcs_bmc->ioreg.idr); +} + +static inline void write_data(struct kcs_bmc *kcs_bmc, u8 data) +{ + kcs_bmc->io_outputb(kcs_bmc, kcs_bmc->ioreg.odr, data); +} + +static inline u8 read_status(struct kcs_bmc *kcs_bmc) +{ + return kcs_bmc->io_inputb(kcs_bmc, kcs_bmc->ioreg.str); +} + +static inline void write_status(struct kcs_bmc *kcs_bmc, u8 data) +{ + kcs_bmc->io_outputb(kcs_bmc, kcs_bmc->ioreg.str, data); +} + +static void update_status_bits(struct kcs_bmc *kcs_bmc, u8 mask, u8 val) +{ + u8 tmp = read_status(kcs_bmc); + + tmp &= ~mask; + tmp |= val & mask; + + write_status(kcs_bmc, tmp); +} + +static inline void set_state(struct kcs_bmc *kcs_bmc, u8 state) +{ + update_status_bits(kcs_bmc, KCS_STATUS_STATE_MASK, + KCS_STATUS_STATE(state)); +} + +static void kcs_force_abort(struct kcs_bmc *kcs_bmc) +{ + set_state(kcs_bmc, ERROR_STATE); + read_data(kcs_bmc); + write_data(kcs_bmc, KCS_ZERO_DATA); + + kcs_bmc->phase = KCS_PHASE_ERROR; + kcs_bmc->data_in_avail = false; + kcs_bmc->data_in_idx = 0; +} + +static void kcs_bmc_handle_data(struct kcs_bmc *kcs_bmc) +{ + u8 data; + + switch (kcs_bmc->phase) { + case KCS_PHASE_WRITE_START: + kcs_bmc->phase = KCS_PHASE_WRITE_DATA; + + case KCS_PHASE_WRITE_DATA: + if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) { + set_state(kcs_bmc, WRITE_STATE); + write_data(kcs_bmc, KCS_ZERO_DATA); + kcs_bmc->data_in[kcs_bmc->data_in_idx++] = + read_data(kcs_bmc); + } else { + kcs_force_abort(kcs_bmc); + kcs_bmc->error = KCS_LENGTH_ERROR; + } + break; + + case KCS_PHASE_WRITE_END_CMD: + if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) { + set_state(kcs_bmc, READ_STATE); + kcs_bmc->data_in[kcs_bmc->data_in_idx++] = + read_data(kcs_bmc); + kcs_bmc->phase = KCS_PHASE_WRITE_DONE; + kcs_bmc->data_in_avail = true; + wake_up_interruptible(&kcs_bmc->queue); + } else { + kcs_force_abort(kcs_bmc); + kcs_bmc->error = KCS_LENGTH_ERROR; + } + break; + + case KCS_PHASE_READ: + if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len) + set_state(kcs_bmc, IDLE_STATE); + + data = read_data(kcs_bmc); + if (data != KCS_CMD_READ_BYTE) { + set_state(kcs_bmc, ERROR_STATE); + write_data(kcs_bmc, KCS_ZERO_DATA); + break; + } + + if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len) { + write_data(kcs_bmc, KCS_ZERO_DATA); + kcs_bmc->phase = KCS_PHASE_IDLE; + break; + } + + write_data(kcs_bmc, + kcs_bmc->data_out[kcs_bmc->data_out_idx++]); + break; + + case KCS_PHASE_ABORT_ERROR1: + set_state(kcs_bmc, READ_STATE); + read_data(kcs_bmc); + write_data(kcs_bmc, kcs_bmc->error); + kcs_bmc->phase = KCS_PHASE_ABORT_ERROR2; + break; + + case KCS_PHASE_ABORT_ERROR2: + set_state(kcs_bmc, IDLE_STATE); + read_data(kcs_bmc); + write_data(kcs_bmc, KCS_ZERO_DATA); + kcs_bmc->phase = KCS_PHASE_IDLE; + break; + + default: + kcs_force_abort(kcs_bmc); + break; + } +} + +static void kcs_bmc_handle_cmd(struct kcs_bmc *kcs_bmc) +{ + u8 cmd; + + set_state(kcs_bmc, WRITE_STATE); + write_data(kcs_bmc, KCS_ZERO_DATA); + + cmd = read_data(kcs_bmc); + switch (cmd) { + case KCS_CMD_WRITE_START: + kcs_bmc->phase = KCS_PHASE_WRITE_START; + kcs_bmc->error = KCS_NO_ERROR; + kcs_bmc->data_in_avail = false; + kcs_bmc->data_in_idx = 0; + break; + + case KCS_CMD_WRITE_END: + if (kcs_bmc->phase != KCS_PHASE_WRITE_DATA) { + kcs_force_abort(kcs_bmc); + break; + } + + kcs_bmc->phase = KCS_PHASE_WRITE_END_CMD; + break; + + case KCS_CMD_GET_STATUS_ABORT: + if (kcs_bmc->error == KCS_NO_ERROR) + kcs_bmc->error = KCS_ABORTED_BY_COMMAND; + + kcs_bmc->phase = KCS_PHASE_ABORT_ERROR1; + kcs_bmc->data_in_avail = false; + kcs_bmc->data_in_idx = 0; + break; + + default: + kcs_force_abort(kcs_bmc); + kcs_bmc->error = KCS_ILLEGAL_CONTROL_CODE; + break; + } +} + +int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc) +{ + unsigned long flags; + int ret = 0; + u8 status; + + spin_lock_irqsave(&kcs_bmc->lock, flags); + + if (!kcs_bmc->running) { + kcs_force_abort(kcs_bmc); + ret = -ENODEV; + goto out_unlock; + } + + status = read_status(kcs_bmc) & (KCS_STATUS_IBF | KCS_STATUS_CMD_DAT); + + switch (status) { + case KCS_STATUS_IBF | KCS_STATUS_CMD_DAT: + kcs_bmc_handle_cmd(kcs_bmc); + break; + + case KCS_STATUS_IBF: + kcs_bmc_handle_data(kcs_bmc); + break; + + default: + ret = -ENODATA; + break; + } + +out_unlock: + spin_unlock_irqrestore(&kcs_bmc->lock, flags); + + return ret; +} +EXPORT_SYMBOL(kcs_bmc_handle_event); + +static inline struct kcs_bmc *file_to_kcs_bmc(struct file *filp) +{ + return container_of(filp->private_data, struct kcs_bmc, miscdev); +} + +static int kcs_bmc_open(struct inode *inode, struct file *filp) +{ + struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp); + int ret = 0; + + spin_lock_irq(&kcs_bmc->lock); + if (!kcs_bmc->running) + kcs_bmc->running = 1; + else + ret = -EBUSY; + spin_unlock_irq(&kcs_bmc->lock); + + return ret; +} + +static unsigned int kcs_bmc_poll(struct file *filp, poll_table *wait) +{ + struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp); + unsigned int mask = 0; + + poll_wait(filp, &kcs_bmc->queue, wait); + + spin_lock_irq(&kcs_bmc->lock); + if (kcs_bmc->data_in_avail) + mask |= POLLIN; + spin_unlock_irq(&kcs_bmc->lock); + + return mask; +} + +static ssize_t kcs_bmc_read(struct file *filp, char *buf, + size_t count, loff_t *offset) +{ + struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp); + bool data_avail; + size_t data_len; + ssize_t ret; + + if (!(filp->f_flags & O_NONBLOCK)) + wait_event_interruptible(kcs_bmc->queue, + kcs_bmc->data_in_avail); + + mutex_lock(&kcs_bmc->mutex); + + spin_lock_irq(&kcs_bmc->lock); + data_avail = kcs_bmc->data_in_avail; + if (data_avail) { + data_len = kcs_bmc->data_in_idx; + memcpy(kcs_bmc->kbuffer, kcs_bmc->data_in, data_len); + } + spin_unlock_irq(&kcs_bmc->lock); + + if (!data_avail) { + ret = -EAGAIN; + goto out_unlock; + } + + if (count < data_len) { + pr_err("channel=%u with too large data : %zu\n", + kcs_bmc->channel, data_len); + + spin_lock_irq(&kcs_bmc->lock); + kcs_force_abort(kcs_bmc); + spin_unlock_irq(&kcs_bmc->lock); + + ret = -EOVERFLOW; + goto out_unlock; + } + + if (copy_to_user(buf, kcs_bmc->kbuffer, data_len)) { + ret = -EFAULT; + goto out_unlock; + } + + ret = data_len; + + spin_lock_irq(&kcs_bmc->lock); + if (kcs_bmc->phase == KCS_PHASE_WRITE_DONE) { + kcs_bmc->phase = KCS_PHASE_WAIT_READ; + kcs_bmc->data_in_avail = false; + kcs_bmc->data_in_idx = 0; + } else { + ret = -EAGAIN; + } + spin_unlock_irq(&kcs_bmc->lock); + +out_unlock: + mutex_unlock(&kcs_bmc->mutex); + + return ret; +} + +static ssize_t kcs_bmc_write(struct file *filp, const char *buf, + size_t count, loff_t *offset) +{ + struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp); + ssize_t ret; + + /* a minimum response size '3' : netfn + cmd + ccode */ + if (count < 3 || count > KCS_MSG_BUFSIZ) + return -EINVAL; + + mutex_lock(&kcs_bmc->mutex); + + if (copy_from_user(kcs_bmc->kbuffer, buf, count)) { + ret = -EFAULT; + goto out_unlock; + } + + spin_lock_irq(&kcs_bmc->lock); + if (kcs_bmc->phase == KCS_PHASE_WAIT_READ) { + kcs_bmc->phase = KCS_PHASE_READ; + kcs_bmc->data_out_idx = 1; + kcs_bmc->data_out_len = count; + memcpy(kcs_bmc->data_out, kcs_bmc->kbuffer, count); + write_data(kcs_bmc, kcs_bmc->data_out[0]); + ret = count; + } else { + ret = -EINVAL; + } + spin_unlock_irq(&kcs_bmc->lock); + +out_unlock: + mutex_unlock(&kcs_bmc->mutex); + + return ret; +} + +static long kcs_bmc_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp); + long ret = 0; + + spin_lock_irq(&kcs_bmc->lock); + + switch (cmd) { + case IPMI_BMC_IOCTL_SET_SMS_ATN: + update_status_bits(kcs_bmc, KCS_STATUS_SMS_ATN, + KCS_STATUS_SMS_ATN); + break; + + case IPMI_BMC_IOCTL_CLEAR_SMS_ATN: + update_status_bits(kcs_bmc, KCS_STATUS_SMS_ATN, + 0); + break; + + case IPMI_BMC_IOCTL_FORCE_ABORT: + kcs_force_abort(kcs_bmc); + break; + + default: + ret = -EINVAL; + break; + } + + spin_unlock_irq(&kcs_bmc->lock); + + return ret; +} + +static int kcs_bmc_release(struct inode *inode, struct file *filp) +{ + struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp); + + spin_lock_irq(&kcs_bmc->lock); + kcs_bmc->running = 0; + kcs_force_abort(kcs_bmc); + spin_unlock_irq(&kcs_bmc->lock); + + return 0; +} + +static const struct file_operations kcs_bmc_fops = { + .owner = THIS_MODULE, + .open = kcs_bmc_open, + .read = kcs_bmc_read, + .write = kcs_bmc_write, + .release = kcs_bmc_release, + .poll = kcs_bmc_poll, + .unlocked_ioctl = kcs_bmc_ioctl, +}; + +struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv, u32 channel) +{ + struct kcs_bmc *kcs_bmc; + + kcs_bmc = devm_kzalloc(dev, sizeof(*kcs_bmc) + sizeof_priv, GFP_KERNEL); + if (!kcs_bmc) + return NULL; + + dev_set_name(dev, "ipmi-kcs%u", channel); + + spin_lock_init(&kcs_bmc->lock); + kcs_bmc->channel = channel; + + mutex_init(&kcs_bmc->mutex); + init_waitqueue_head(&kcs_bmc->queue); + + kcs_bmc->data_in = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL); + kcs_bmc->data_out = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL); + kcs_bmc->kbuffer = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL); + if (!kcs_bmc->data_in || !kcs_bmc->data_out || !kcs_bmc->kbuffer) + return NULL; + + kcs_bmc->miscdev.minor = MISC_DYNAMIC_MINOR; + kcs_bmc->miscdev.name = dev_name(dev); + kcs_bmc->miscdev.fops = &kcs_bmc_fops; + + return kcs_bmc; +} +EXPORT_SYMBOL(kcs_bmc_alloc); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Haiyue Wang "); +MODULE_DESCRIPTION("KCS BMC to handle the IPMI request from system software"); diff --git a/drivers/char/ipmi/kcs_bmc.h b/drivers/char/ipmi/kcs_bmc.h new file mode 100644 index 00000000000000..c19501db0236a8 --- /dev/null +++ b/drivers/char/ipmi/kcs_bmc.h @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2015-2018, Intel Corporation. + +#ifndef __KCS_BMC_H__ +#define __KCS_BMC_H__ + +#include + +/* Different phases of the KCS BMC module : + * KCS_PHASE_IDLE : + * BMC should not be expecting nor sending any data. + * KCS_PHASE_WRITE_START : + * BMC is receiving a WRITE_START command from system software. + * KCS_PHASE_WRITE_DATA : + * BMC is receiving a data byte from system software. + * KCS_PHASE_WRITE_END_CMD : + * BMC is waiting a last data byte from system software. + * KCS_PHASE_WRITE_DONE : + * BMC has received the whole request from system software. + * KCS_PHASE_WAIT_READ : + * BMC is waiting the response from the upper IPMI service. + * KCS_PHASE_READ : + * BMC is transferring the response to system software. + * KCS_PHASE_ABORT_ERROR1 : + * BMC is waiting error status request from system software. + * KCS_PHASE_ABORT_ERROR2 : + * BMC is waiting for idle status afer error from system software. + * KCS_PHASE_ERROR : + * BMC has detected a protocol violation at the interface level. + */ +enum kcs_phases { + KCS_PHASE_IDLE, + + KCS_PHASE_WRITE_START, + KCS_PHASE_WRITE_DATA, + KCS_PHASE_WRITE_END_CMD, + KCS_PHASE_WRITE_DONE, + + KCS_PHASE_WAIT_READ, + KCS_PHASE_READ, + + KCS_PHASE_ABORT_ERROR1, + KCS_PHASE_ABORT_ERROR2, + KCS_PHASE_ERROR +}; + +/* IPMI 2.0 - Table 9-4, KCS Interface Status Codes */ +enum kcs_errors { + KCS_NO_ERROR = 0x00, + KCS_ABORTED_BY_COMMAND = 0x01, + KCS_ILLEGAL_CONTROL_CODE = 0x02, + KCS_LENGTH_ERROR = 0x06, + KCS_UNSPECIFIED_ERROR = 0xFF +}; + +/* IPMI 2.0 - 9.5, KCS Interface Registers + * @idr : Input Data Register + * @odr : Output Data Register + * @str : Status Register + */ +struct kcs_ioreg { + u32 idr; + u32 odr; + u32 str; +}; + +struct kcs_bmc { + spinlock_t lock; + + u32 channel; + int running; + + /* Setup by BMC KCS controller driver */ + struct kcs_ioreg ioreg; + u8 (*io_inputb)(struct kcs_bmc *kcs_bmc, u32 reg); + void (*io_outputb)(struct kcs_bmc *kcs_bmc, u32 reg, u8 b); + + enum kcs_phases phase; + enum kcs_errors error; + + wait_queue_head_t queue; + bool data_in_avail; + int data_in_idx; + u8 *data_in; + + int data_out_idx; + int data_out_len; + u8 *data_out; + + struct mutex mutex; + u8 *kbuffer; + + struct miscdevice miscdev; + + unsigned long priv[]; +}; + +static inline void *kcs_bmc_priv(struct kcs_bmc *kcs_bmc) +{ + return kcs_bmc->priv; +} + +int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc); +struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv, + u32 channel); +#endif diff --git a/include/uapi/linux/ipmi_bmc.h b/include/uapi/linux/ipmi_bmc.h new file mode 100644 index 00000000000000..2f9f97e6123a1e --- /dev/null +++ b/include/uapi/linux/ipmi_bmc.h @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2015-2018, Intel Corporation. + +#ifndef _UAPI_LINUX_IPMI_BMC_H +#define _UAPI_LINUX_IPMI_BMC_H + +#include + +#define __IPMI_BMC_IOCTL_MAGIC 0xB1 +#define IPMI_BMC_IOCTL_SET_SMS_ATN _IO(__IPMI_BMC_IOCTL_MAGIC, 0x00) +#define IPMI_BMC_IOCTL_CLEAR_SMS_ATN _IO(__IPMI_BMC_IOCTL_MAGIC, 0x01) +#define IPMI_BMC_IOCTL_FORCE_ABORT _IO(__IPMI_BMC_IOCTL_MAGIC, 0x02) + +#endif /* _UAPI_LINUX_KCS_BMC_H */ From d6ff98b2d8e05513f7818bc93b1d41f5a0c60135 Mon Sep 17 00:00:00 2001 From: Haiyue Wang Date: Fri, 2 Feb 2018 10:16:11 +0800 Subject: [PATCH 30/37] ipmi: add an Aspeed KCS IPMI BMC driver The KCS (Keyboard Controller Style) interface is used to perform in-band IPMI communication between a server host and its BMC (BaseBoard Management Controllers). This driver exposes the KCS interface on ASpeed SOCs (AST2400 and AST2500) as a character device. Such SOCs are commonly used as BMCs and this driver implements the BMC side of the KCS interface. Signed-off-by: Haiyue Wang --- .../bindings/ipmi/aspeed-kcs-bmc.txt | 25 ++ drivers/char/ipmi/Kconfig | 12 + drivers/char/ipmi/Makefile | 1 + drivers/char/ipmi/kcs_bmc_aspeed.c | 319 ++++++++++++++++++ 4 files changed, 357 insertions(+) create mode 100644 Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt create mode 100644 drivers/char/ipmi/kcs_bmc_aspeed.c diff --git a/Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt b/Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt new file mode 100644 index 00000000000000..d98a9bf45d6cba --- /dev/null +++ b/Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt @@ -0,0 +1,25 @@ +* Aspeed KCS (Keyboard Controller Style) IPMI interface + +The Aspeed SOCs (AST2400 and AST2500) are commonly used as BMCs +(Baseboard Management Controllers) and the KCS interface can be +used to perform in-band IPMI communication with their host. + +Required properties: +- compatible : should be one of + "aspeed,ast2400-kcs-bmc" + "aspeed,ast2500-kcs-bmc" +- interrupts : interrupt generated by the controller +- kcs_chan : The LPC channel number in the controller +- kcs_addr : The host CPU IO map address + + +Example: + + kcs3: kcs3@0 { + compatible = "aspeed,ast2500-kcs-bmc"; + reg = <0x0 0x80>; + interrupts = <8>; + kcs_chan = <3>; + kcs_addr = <0xCA2>; + status = "okay"; + }; diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig index 985e3e6b20b2fb..cbfe1c7510d5ed 100644 --- a/drivers/char/ipmi/Kconfig +++ b/drivers/char/ipmi/Kconfig @@ -89,6 +89,18 @@ config IPMI_KCS_BMC Management Controllers) side for handling the IPMI request from host system software. +config ASPEED_KCS_IPMI_BMC + depends on ARCH_ASPEED || COMPILE_TEST + select IPMI_KCS_BMC + select REGMAP_MMIO + tristate "Aspeed KCS IPMI BMC driver" + help + Provides a driver for the KCS (Keyboard Controller Style) IPMI + interface found on Aspeed SOCs (AST2400 and AST2500). + + The driver implements the BMC side of the KCS contorller, it + provides the access of KCS IO space for BMC side. + config ASPEED_BT_IPMI_BMC depends on ARCH_ASPEED || COMPILE_TEST depends on REGMAP && REGMAP_MMIO && MFD_SYSCON diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile index f80cf54006a639..000aa11514816a 100644 --- a/drivers/char/ipmi/Makefile +++ b/drivers/char/ipmi/Makefile @@ -14,3 +14,4 @@ obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o obj-$(CONFIG_IPMI_KCS_BMC) += kcs_bmc.o obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += bt-bmc.o +obj-$(CONFIG_ASPEED_KCS_IPMI_BMC) += kcs_bmc_aspeed.o diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c new file mode 100644 index 00000000000000..0c4d1a36dae438 --- /dev/null +++ b/drivers/char/ipmi/kcs_bmc_aspeed.c @@ -0,0 +1,319 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2015-2018, Intel Corporation. + +#define pr_fmt(fmt) "aspeed-kcs-bmc: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kcs_bmc.h" + + +#define DEVICE_NAME "ast-kcs-bmc" + +#define KCS_CHANNEL_MAX 4 + +/* mapped to lpc-bmc@0 IO space */ +#define LPC_HICR0 0x000 +#define LPC_HICR0_LPC3E BIT(7) +#define LPC_HICR0_LPC2E BIT(6) +#define LPC_HICR0_LPC1E BIT(5) +#define LPC_HICR2 0x008 +#define LPC_HICR2_IBFIF3 BIT(3) +#define LPC_HICR2_IBFIF2 BIT(2) +#define LPC_HICR2_IBFIF1 BIT(1) +#define LPC_HICR4 0x010 +#define LPC_HICR4_LADR12AS BIT(7) +#define LPC_HICR4_KCSENBL BIT(2) +#define LPC_LADR3H 0x014 +#define LPC_LADR3L 0x018 +#define LPC_LADR12H 0x01C +#define LPC_LADR12L 0x020 +#define LPC_IDR1 0x024 +#define LPC_IDR2 0x028 +#define LPC_IDR3 0x02C +#define LPC_ODR1 0x030 +#define LPC_ODR2 0x034 +#define LPC_ODR3 0x038 +#define LPC_STR1 0x03C +#define LPC_STR2 0x040 +#define LPC_STR3 0x044 + +/* mapped to lpc-host@80 IO space */ +#define LPC_HICRB 0x080 +#define LPC_HICRB_IBFIF4 BIT(1) +#define LPC_HICRB_LPC4E BIT(0) +#define LPC_LADR4 0x090 +#define LPC_IDR4 0x094 +#define LPC_ODR4 0x098 +#define LPC_STR4 0x09C + +struct aspeed_kcs_bmc { + struct regmap *map; +}; + + +static u8 aspeed_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg) +{ + struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc); + u32 val = 0; + int rc; + + rc = regmap_read(priv->map, reg, &val); + WARN(rc != 0, "regmap_read() failed: %d\n", rc); + + return rc == 0 ? (u8) val : 0; +} + +static void aspeed_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data) +{ + struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc); + int rc; + + rc = regmap_write(priv->map, reg, data); + WARN(rc != 0, "regmap_write() failed: %d\n", rc); +} + + +/* + * AST_usrGuide_KCS.pdf + * 2. Background: + * we note D for Data, and C for Cmd/Status, default rules are + * A. KCS1 / KCS2 ( D / C:X / X+4 ) + * D / C : CA0h / CA4h + * D / C : CA8h / CACh + * B. KCS3 ( D / C:XX2h / XX3h ) + * D / C : CA2h / CA3h + * D / C : CB2h / CB3h + * C. KCS4 + * D / C : CA4h / CA5h + */ +static void aspeed_kcs_set_address(struct kcs_bmc *kcs_bmc, u16 addr) +{ + struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc); + + switch (kcs_bmc->channel) { + case 1: + regmap_update_bits(priv->map, LPC_HICR4, + LPC_HICR4_LADR12AS, 0); + regmap_write(priv->map, LPC_LADR12H, addr >> 8); + regmap_write(priv->map, LPC_LADR12L, addr & 0xFF); + break; + + case 2: + regmap_update_bits(priv->map, LPC_HICR4, + LPC_HICR4_LADR12AS, LPC_HICR4_LADR12AS); + regmap_write(priv->map, LPC_LADR12H, addr >> 8); + regmap_write(priv->map, LPC_LADR12L, addr & 0xFF); + break; + + case 3: + regmap_write(priv->map, LPC_LADR3H, addr >> 8); + regmap_write(priv->map, LPC_LADR3L, addr & 0xFF); + break; + + case 4: + regmap_write(priv->map, LPC_LADR4, ((addr + 1) << 16) | + addr); + break; + + default: + break; + } +} + +static void aspeed_kcs_enable_channel(struct kcs_bmc *kcs_bmc, bool enable) +{ + struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc); + + switch (kcs_bmc->channel) { + case 1: + if (enable) { + regmap_update_bits(priv->map, LPC_HICR2, + LPC_HICR2_IBFIF1, LPC_HICR2_IBFIF1); + regmap_update_bits(priv->map, LPC_HICR0, + LPC_HICR0_LPC1E, LPC_HICR0_LPC1E); + } else { + regmap_update_bits(priv->map, LPC_HICR0, + LPC_HICR0_LPC1E, 0); + regmap_update_bits(priv->map, LPC_HICR2, + LPC_HICR2_IBFIF1, 0); + } + break; + + case 2: + if (enable) { + regmap_update_bits(priv->map, LPC_HICR2, + LPC_HICR2_IBFIF2, LPC_HICR2_IBFIF2); + regmap_update_bits(priv->map, LPC_HICR0, + LPC_HICR0_LPC2E, LPC_HICR0_LPC2E); + } else { + regmap_update_bits(priv->map, LPC_HICR0, + LPC_HICR0_LPC2E, 0); + regmap_update_bits(priv->map, LPC_HICR2, + LPC_HICR2_IBFIF2, 0); + } + break; + + case 3: + if (enable) { + regmap_update_bits(priv->map, LPC_HICR2, + LPC_HICR2_IBFIF3, LPC_HICR2_IBFIF3); + regmap_update_bits(priv->map, LPC_HICR0, + LPC_HICR0_LPC3E, LPC_HICR0_LPC3E); + regmap_update_bits(priv->map, LPC_HICR4, + LPC_HICR4_KCSENBL, LPC_HICR4_KCSENBL); + } else { + regmap_update_bits(priv->map, LPC_HICR0, + LPC_HICR0_LPC3E, 0); + regmap_update_bits(priv->map, LPC_HICR4, + LPC_HICR4_KCSENBL, 0); + regmap_update_bits(priv->map, LPC_HICR2, + LPC_HICR2_IBFIF3, 0); + } + break; + + case 4: + if (enable) + regmap_update_bits(priv->map, LPC_HICRB, + LPC_HICRB_IBFIF4 | LPC_HICRB_LPC4E, + LPC_HICRB_IBFIF4 | LPC_HICRB_LPC4E); + else + regmap_update_bits(priv->map, LPC_HICRB, + LPC_HICRB_IBFIF4 | LPC_HICRB_LPC4E, + 0); + break; + + default: + break; + } +} + +static irqreturn_t aspeed_kcs_irq(int irq, void *arg) +{ + struct kcs_bmc *kcs_bmc = arg; + + if (!kcs_bmc_handle_event(kcs_bmc)) + return IRQ_HANDLED; + + return IRQ_NONE; +} + +static int aspeed_kcs_config_irq(struct kcs_bmc *kcs_bmc, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int irq; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + return devm_request_irq(dev, irq, aspeed_kcs_irq, IRQF_SHARED, + dev_name(dev), kcs_bmc); +} + +static const struct kcs_ioreg ast_kcs_bmc_ioregs[KCS_CHANNEL_MAX] = { + { .idr = LPC_IDR1, .odr = LPC_ODR1, .str = LPC_STR1 }, + { .idr = LPC_IDR2, .odr = LPC_ODR2, .str = LPC_STR2 }, + { .idr = LPC_IDR3, .odr = LPC_ODR3, .str = LPC_STR3 }, + { .idr = LPC_IDR4, .odr = LPC_ODR4, .str = LPC_STR4 }, +}; + +static int aspeed_kcs_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct aspeed_kcs_bmc *priv; + struct kcs_bmc *kcs_bmc; + u32 chan, addr; + int rc; + + rc = of_property_read_u32(dev->of_node, "kcs_chan", &chan); + if ((rc != 0) || (chan == 0 || chan > KCS_CHANNEL_MAX)) { + dev_err(dev, "no valid 'kcs_chan' configured\n"); + return -ENODEV; + } + + rc = of_property_read_u32(dev->of_node, "kcs_addr", &addr); + if (rc) { + dev_err(dev, "no valid 'kcs_addr' configured\n"); + return -ENODEV; + } + + kcs_bmc = kcs_bmc_alloc(dev, sizeof(*priv), chan); + if (!kcs_bmc) + return -ENOMEM; + + priv = kcs_bmc_priv(kcs_bmc); + priv->map = syscon_node_to_regmap(dev->parent->of_node); + if (IS_ERR(priv->map)) { + dev_err(dev, "Couldn't get regmap\n"); + return -ENODEV; + } + + kcs_bmc->ioreg = ast_kcs_bmc_ioregs[chan - 1]; + kcs_bmc->io_inputb = aspeed_kcs_inb; + kcs_bmc->io_outputb = aspeed_kcs_outb; + + dev_set_drvdata(dev, kcs_bmc); + + aspeed_kcs_set_address(kcs_bmc, addr); + aspeed_kcs_enable_channel(kcs_bmc, true); + rc = aspeed_kcs_config_irq(kcs_bmc, pdev); + if (rc) + return rc; + + rc = misc_register(&kcs_bmc->miscdev); + if (rc) { + dev_err(dev, "Unable to register device\n"); + return rc; + } + + pr_info("channel=%u addr=0x%x idr=0x%x odr=0x%x str=0x%x\n", + chan, addr, + kcs_bmc->ioreg.idr, kcs_bmc->ioreg.odr, kcs_bmc->ioreg.str); + + return 0; +} + +static int aspeed_kcs_remove(struct platform_device *pdev) +{ + struct kcs_bmc *kcs_bmc = dev_get_drvdata(&pdev->dev); + + misc_deregister(&kcs_bmc->miscdev); + + return 0; +} + +static const struct of_device_id ast_kcs_bmc_match[] = { + { .compatible = "aspeed,ast2400-kcs-bmc" }, + { .compatible = "aspeed,ast2500-kcs-bmc" }, + { } +}; + +static struct platform_driver ast_kcs_bmc_driver = { + .driver = { + .name = DEVICE_NAME, + .of_match_table = ast_kcs_bmc_match, + }, + .probe = aspeed_kcs_probe, + .remove = aspeed_kcs_remove, +}; + +module_platform_driver(ast_kcs_bmc_driver); + +MODULE_DEVICE_TABLE(of, ast_kcs_bmc_match); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Haiyue Wang "); +MODULE_DESCRIPTION("Aspeed device interface to the KCS BMC device"); From 11633aa712315ca44cd9429437c7618e69c87b53 Mon Sep 17 00:00:00 2001 From: Haiyue Wang Date: Fri, 9 Mar 2018 10:00:24 +0800 Subject: [PATCH 31/37] ipmi: add a Nuvoton (NPCM7XX) KCS IPMI BMC driver This driver exposes the Keyboard Controller Style (KCS) interface on Novoton NPCM7xx SoCs as a character device. Such SOCs are commonly used as a BaseBoard Management Controller (BMC) on a server board, and KCS interface is commonly used to perform the in-band IPMI communication between the server and its BMC. Signed-off-by: Avi Fishman Signed-off-by: Haiyue Wang --- .../bindings/ipmi/npcm7xx-kcs-bmc.txt | 39 ++++ drivers/char/ipmi/Kconfig | 15 ++ drivers/char/ipmi/Makefile | 1 + drivers/char/ipmi/kcs_bmc_npcm7xx.c | 204 ++++++++++++++++++ 4 files changed, 259 insertions(+) create mode 100644 Documentation/devicetree/bindings/ipmi/npcm7xx-kcs-bmc.txt create mode 100644 drivers/char/ipmi/kcs_bmc_npcm7xx.c diff --git a/Documentation/devicetree/bindings/ipmi/npcm7xx-kcs-bmc.txt b/Documentation/devicetree/bindings/ipmi/npcm7xx-kcs-bmc.txt new file mode 100644 index 00000000000000..3538a214fff156 --- /dev/null +++ b/Documentation/devicetree/bindings/ipmi/npcm7xx-kcs-bmc.txt @@ -0,0 +1,39 @@ +* Nuvoton NPCM7xx KCS (Keyboard Controller Style) IPMI interface + +The Nuvoton SOCs (NPCM7xx) are commonly used as BMCs +(Baseboard Management Controllers) and the KCS interface can be +used to perform in-band IPMI communication with their host. + +Required properties: +- compatible : should be one of + "nuvoton,npcm750-kcs-bmc" +- interrupts : interrupt generated by the controller +- kcs_chan : The KCS channel number in the controller + +Example: + + lpc_kcs: lpc_kcs@f0007000 { + compatible = "nuvoton,npcm750-lpc-kcs", "simple-mfd", "syscon"; + reg = <0xf0007000 0x40>; + reg-io-width = <1>; + + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0xf0007000 0x40>; + + kcs1: kcs1@0 { + compatible = "nuvoton,npcm750-kcs-bmc"; + reg = <0x0 0x40>; + interrupts = <0 9 4>; + kcs_chan = <1>; + status = "disabled"; + }; + + kcs2: kcs2@0 { + compatible = "nuvoton,npcm750-kcs-bmc"; + reg = <0x0 0x40>; + interrupts = <0 9 4>; + kcs_chan = <2>; + status = "disabled"; + }; + }; \ No newline at end of file diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig index cbfe1c7510d5ed..ffce5ca07cd812 100644 --- a/drivers/char/ipmi/Kconfig +++ b/drivers/char/ipmi/Kconfig @@ -101,6 +101,21 @@ config ASPEED_KCS_IPMI_BMC The driver implements the BMC side of the KCS contorller, it provides the access of KCS IO space for BMC side. +config NPCM7XX_KCS_IPMI_BMC + depends on ARCH_NPCM7XX || COMPILE_TEST + select IPMI_KCS_BMC + select REGMAP_MMIO + tristate "NPCM7xx KCS IPMI BMC driver" + help + Provides a driver for the KCS (Keyboard Controller Style) IPMI + interface found on Nuvoton NPCM7xx SOCs. + + The driver implements the BMC side of the KCS contorller, it + provides the access of KCS IO space for BMC side. + + This support is also available as a module. If so, the module + will be called kcs_bmc_npcm7xx. + config ASPEED_BT_IPMI_BMC depends on ARCH_ASPEED || COMPILE_TEST depends on REGMAP && REGMAP_MMIO && MFD_SYSCON diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile index 000aa11514816a..1ff86fd1237268 100644 --- a/drivers/char/ipmi/Makefile +++ b/drivers/char/ipmi/Makefile @@ -15,3 +15,4 @@ obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o obj-$(CONFIG_IPMI_KCS_BMC) += kcs_bmc.o obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += bt-bmc.o obj-$(CONFIG_ASPEED_KCS_IPMI_BMC) += kcs_bmc_aspeed.o +obj-$(CONFIG_NPCM7XX_KCS_IPMI_BMC) += kcs_bmc_npcm7xx.o diff --git a/drivers/char/ipmi/kcs_bmc_npcm7xx.c b/drivers/char/ipmi/kcs_bmc_npcm7xx.c new file mode 100644 index 00000000000000..7bc898c5d37206 --- /dev/null +++ b/drivers/char/ipmi/kcs_bmc_npcm7xx.c @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018, Nuvoton Corporation. + * Copyright (c) 2018, Intel Corporation. + */ + +#define pr_fmt(fmt) "nuvoton-kcs-bmc: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kcs_bmc.h" + +#define DEVICE_NAME "npcm-kcs-bmc" +#define KCS_CHANNEL_MAX 3 + +#define KCS1ST 0x0C +#define KCS2ST 0x1E +#define KCS3ST 0x30 + +#define KCS1DO 0x0E +#define KCS2DO 0x20 +#define KCS3DO 0x32 + +#define KCS1DI 0x10 +#define KCS2DI 0x22 +#define KCS3DI 0x34 + +#define KCS1CTL 0x18 +#define KCS2CTL 0x2A +#define KCS3CTL 0x3C +#define KCS_CTL_IBFIE BIT(0) + +/* + * 7.2.4 Core KCS Registers + * Registers in this module are 8 bits. An 8-bit register must be accessed + * by an 8-bit read or write. + * + * sts: KCS Channel n Status Register (KCSnST). + * dob: KCS Channel n Data Out Buffer Register (KCSnDO). + * dib: KCS Channel n Data In Buffer Register (KCSnDI). + * ctl: KCS Channel n Control Register (KCSnCTL). + */ +struct npcm7xx_kcs_reg { + u32 sts; + u32 dob; + u32 dib; + u32 ctl; +}; + +struct npcm7xx_kcs_bmc { + struct regmap *map; + + const struct npcm7xx_kcs_reg *reg; +}; + +static const struct npcm7xx_kcs_reg npcm7xx_kcs_reg_tbl[KCS_CHANNEL_MAX] = { + { .sts = KCS1ST, .dob = KCS1DO, .dib = KCS1DI, .ctl = KCS1CTL }, + { .sts = KCS2ST, .dob = KCS2DO, .dib = KCS2DI, .ctl = KCS2CTL }, + { .sts = KCS3ST, .dob = KCS3DO, .dib = KCS3DI, .ctl = KCS3CTL }, +}; + +static u8 npcm7xx_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg) +{ + struct npcm7xx_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc); + u32 val = 0; + int rc; + + rc = regmap_read(priv->map, reg, &val); + WARN(rc != 0, "regmap_read() failed: %d\n", rc); + + return rc == 0 ? (u8)val : 0; +} + +static void npcm7xx_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data) +{ + struct npcm7xx_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc); + int rc; + + rc = regmap_write(priv->map, reg, data); + WARN(rc != 0, "regmap_write() failed: %d\n", rc); +} + +static void npcm7xx_kcs_enable_channel(struct kcs_bmc *kcs_bmc, bool enable) +{ + struct npcm7xx_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc); + + regmap_update_bits(priv->map, priv->reg->ctl, KCS_CTL_IBFIE, + enable ? KCS_CTL_IBFIE : 0); +} + +static irqreturn_t npcm7xx_kcs_irq(int irq, void *arg) +{ + struct kcs_bmc *kcs_bmc = arg; + + if (!kcs_bmc_handle_event(kcs_bmc)) + return IRQ_HANDLED; + + return IRQ_NONE; +} + +static int npcm7xx_kcs_config_irq(struct kcs_bmc *kcs_bmc, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int irq; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + return devm_request_irq(dev, irq, npcm7xx_kcs_irq, IRQF_SHARED, + dev_name(dev), kcs_bmc); +} + +static int npcm7xx_kcs_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct npcm7xx_kcs_bmc *priv; + struct kcs_bmc *kcs_bmc; + u32 chan; + int rc; + + rc = of_property_read_u32(dev->of_node, "kcs_chan", &chan); + if (rc != 0 || chan == 0 || chan > KCS_CHANNEL_MAX) { + dev_err(dev, "no valid 'kcs_chan' configured\n"); + return -ENODEV; + } + + kcs_bmc = kcs_bmc_alloc(dev, sizeof(*priv), chan); + if (!kcs_bmc) + return -ENOMEM; + + priv = kcs_bmc_priv(kcs_bmc); + priv->map = syscon_node_to_regmap(dev->parent->of_node); + if (IS_ERR(priv->map)) { + dev_err(dev, "Couldn't get regmap\n"); + return -ENODEV; + } + priv->reg = &npcm7xx_kcs_reg_tbl[chan - 1]; + + kcs_bmc->ioreg.idr = priv->reg->dib; + kcs_bmc->ioreg.odr = priv->reg->dob; + kcs_bmc->ioreg.str = priv->reg->sts; + kcs_bmc->io_inputb = npcm7xx_kcs_inb; + kcs_bmc->io_outputb = npcm7xx_kcs_outb; + + dev_set_drvdata(dev, kcs_bmc); + + npcm7xx_kcs_enable_channel(kcs_bmc, true); + rc = npcm7xx_kcs_config_irq(kcs_bmc, pdev); + if (rc) + return rc; + + rc = misc_register(&kcs_bmc->miscdev); + if (rc) { + dev_err(dev, "Unable to register device\n"); + return rc; + } + + pr_info("channel=%u idr=0x%x odr=0x%x str=0x%x\n", + chan, + kcs_bmc->ioreg.idr, kcs_bmc->ioreg.odr, kcs_bmc->ioreg.str); + + return 0; +} + +static int npcm7xx_kcs_remove(struct platform_device *pdev) +{ + struct kcs_bmc *kcs_bmc = dev_get_drvdata(&pdev->dev); + + misc_deregister(&kcs_bmc->miscdev); + + return 0; +} + +static const struct of_device_id npcm_kcs_bmc_match[] = { + { .compatible = "nuvoton,npcm750-kcs-bmc" }, + { } +}; +MODULE_DEVICE_TABLE(of, npcm_kcs_bmc_match); + +static struct platform_driver npcm_kcs_bmc_driver = { + .driver = { + .name = DEVICE_NAME, + .of_match_table = npcm_kcs_bmc_match, + }, + .probe = npcm7xx_kcs_probe, + .remove = npcm7xx_kcs_remove, +}; +module_platform_driver(npcm_kcs_bmc_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Avi Fishman "); +MODULE_AUTHOR("Haiyue Wang "); +MODULE_DESCRIPTION("NPCM7xx device interface to the KCS BMC device"); From f62b32009f31109305a13736e9112b45e4c2cfec Mon Sep 17 00:00:00 2001 From: Avi Fishman Date: Tue, 15 May 2018 15:47:48 +0300 Subject: [PATCH 32/37] ipmi: NPCM7xx KCS BMC: enable interrupt to the host Original kcs_bmc_npcm7xx.c was missing enabling to send interrupt to the host on writes to output buffer. This patch fixes it by setting the bits that enables the generation of IRQn events by hardware control based on the status of the OBF flag. Signed-off-by: Avi Fishman --- drivers/char/ipmi/kcs_bmc_npcm7xx.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/char/ipmi/kcs_bmc_npcm7xx.c b/drivers/char/ipmi/kcs_bmc_npcm7xx.c index 7bc898c5d37206..722f7391fe1f69 100644 --- a/drivers/char/ipmi/kcs_bmc_npcm7xx.c +++ b/drivers/char/ipmi/kcs_bmc_npcm7xx.c @@ -39,6 +39,12 @@ #define KCS3CTL 0x3C #define KCS_CTL_IBFIE BIT(0) +#define KCS1IE 0x1C +#define KCS2IE 0x2E +#define KCS3IE 0x40 +#define KCS_IE_IRQE BIT(0) +#define KCS_IE_HIRQE BIT(3) + /* * 7.2.4 Core KCS Registers * Registers in this module are 8 bits. An 8-bit register must be accessed @@ -48,12 +54,14 @@ * dob: KCS Channel n Data Out Buffer Register (KCSnDO). * dib: KCS Channel n Data In Buffer Register (KCSnDI). * ctl: KCS Channel n Control Register (KCSnCTL). + * ie : KCS Channel n Interrupt Enable Register (KCSnIE). */ struct npcm7xx_kcs_reg { u32 sts; u32 dob; u32 dib; u32 ctl; + u32 ie; }; struct npcm7xx_kcs_bmc { @@ -63,9 +71,9 @@ struct npcm7xx_kcs_bmc { }; static const struct npcm7xx_kcs_reg npcm7xx_kcs_reg_tbl[KCS_CHANNEL_MAX] = { - { .sts = KCS1ST, .dob = KCS1DO, .dib = KCS1DI, .ctl = KCS1CTL }, - { .sts = KCS2ST, .dob = KCS2DO, .dib = KCS2DI, .ctl = KCS2CTL }, - { .sts = KCS3ST, .dob = KCS3DO, .dib = KCS3DI, .ctl = KCS3CTL }, + { .sts = KCS1ST, .dob = KCS1DO, .dib = KCS1DI, .ctl = KCS1CTL, .ie = KCS1IE }, + { .sts = KCS2ST, .dob = KCS2DO, .dib = KCS2DI, .ctl = KCS2CTL, .ie = KCS2IE }, + { .sts = KCS3ST, .dob = KCS3DO, .dib = KCS3DI, .ctl = KCS3CTL, .ie = KCS3IE }, }; static u8 npcm7xx_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg) @@ -95,6 +103,9 @@ static void npcm7xx_kcs_enable_channel(struct kcs_bmc *kcs_bmc, bool enable) regmap_update_bits(priv->map, priv->reg->ctl, KCS_CTL_IBFIE, enable ? KCS_CTL_IBFIE : 0); + + regmap_update_bits(priv->map, priv->reg->ie, KCS_IE_IRQE | KCS_IE_HIRQE, + enable ? KCS_IE_IRQE | KCS_IE_HIRQE : 0); } static irqreturn_t npcm7xx_kcs_irq(int irq, void *arg) From 1e62772a77193322faf6de120ae6fd87c4967c89 Mon Sep 17 00:00:00 2001 From: Avi Fishman Date: Wed, 16 May 2018 11:47:41 +0300 Subject: [PATCH 33/37] arm: dts: add npcm7xx dtbs to Makefile --- arch/arm/boot/dts/Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index 4b17f35dc9a716..927ce7df14dbb9 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -307,6 +307,9 @@ dtb-$(CONFIG_ARCH_MPS2) += \ mps2-an399.dtb dtb-$(CONFIG_ARCH_MOXART) += \ moxart-uc7112lx.dtb +dtb-$(CONFIG_ARCH_NPCM7XX) += \ + nuvoton-npcm750-evb.dtb \ + rockaway-npcm730-evb.dtb dtb-$(CONFIG_SOC_IMX1) += \ imx1-ads.dtb \ imx1-apf9328.dtb From 25e3ed51e8b77ecd4717556323d09ebed095e27c Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Thu, 17 May 2018 18:23:47 +0300 Subject: [PATCH 34/37] clocksource: npcm: enable NPCM7XX_TIMER by defualt enable NPCM7XX_TIMER by defualt when the NPCM7XX architecture is selected Signed-off-by: Tomer Maimon --- drivers/clocksource/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 79df794bbbf27d..48c6e94d2fd3aa 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -142,6 +142,7 @@ config VT8500_TIMER config NPCM7XX_TIMER bool "NPCM7xx timer driver" if COMPILE_TEST + default y if ARCH_NPCM7XX depends on HAS_IOMEM select CLKSRC_MMIO help From 663734d639b315d5dc28440844eb01337d2cbb85 Mon Sep 17 00:00:00 2001 From: Tali Perry Date: Wed, 23 May 2018 15:50:52 +0300 Subject: [PATCH 35/37] clk: npcm7xx: update clk_base error handling and dmesg --- drivers/clk/clk-npcm7xx.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/clk/clk-npcm7xx.c b/drivers/clk/clk-npcm7xx.c index 8ee7786e54ad50..6ff97f79fcd7dd 100644 --- a/drivers/clk/clk-npcm7xx.c +++ b/drivers/clk/clk-npcm7xx.c @@ -90,8 +90,8 @@ struct clk_hw *npcm7xx_clk_register_pll(void __iomem *pllcon, const char *name, if (!pll) return ERR_PTR(-ENOMEM); - pr_debug("npcm7xx_clk_register_pll reg, reg=0x%x, name=%s, p=%s\n", - (unsigned int)pllcon, name, parent_name); + pr_debug("%s reg, reg=0x%x, name=%s, p=%s\n", + __func__, (unsigned int)pllcon, name, parent_name); init.name = name; init.ops = &npcm7xx_clk_pll_ops; @@ -583,9 +583,8 @@ static void __init npcm7xx_clk_init(struct device_node *clk_np) clk_base = ioremap(res.start, resource_size(&res)); - if (clk_base == NULL) { + if (!clk_base) goto npcm7xx_init_error; - } npcm7xx_clk_data = kzalloc(sizeof(*npcm7xx_clk_data->hws) * @@ -713,7 +712,6 @@ static void __init npcm7xx_clk_init(struct device_node *clk_np) pr_err("see Documentation/devicetree/bindings/clock/" "nuvoton,npcm750-clk.txt for details\n"); npcm7xx_init_fail: - pr_err("clk setup fail\n"); if (npcm7xx_clk_data->num) kfree(npcm7xx_clk_data->hws); npcm7xx_init_np_err: @@ -721,6 +719,7 @@ static void __init npcm7xx_clk_init(struct device_node *clk_np) iounmap(clk_base); npcm7xx_init_error: of_node_put(clk_np); + pr_err("clk setup fail\n"); } CLK_OF_DECLARE(npcm7xx_clk_init, "nuvoton,npcm750-clk", npcm7xx_clk_init); From 33be98a867143945aa770be48aa34dcae0356d46 Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Tue, 5 Jun 2018 11:12:07 +0300 Subject: [PATCH 36/37] dts: npcm750: control SPSWC register using a mux controller Adding multiplexer support for handling control SPSWC register Signed-off-by: Tomer Maimon --- arch/arm/boot/dts/nuvoton-npcm750-evb.dts | 10 ++++++++++ arch/arm/mach-npcm/Kconfig | 1 + 2 files changed, 11 insertions(+) diff --git a/arch/arm/boot/dts/nuvoton-npcm750-evb.dts b/arch/arm/boot/dts/nuvoton-npcm750-evb.dts index d263cf7c60009d..e974814b9642a5 100644 --- a/arch/arm/boot/dts/nuvoton-npcm750-evb.dts +++ b/arch/arm/boot/dts/nuvoton-npcm750-evb.dts @@ -465,3 +465,13 @@ &gpio255_pins>; }; }; + +&gcr { + serial_port_mux: mux-controller { + compatible = "mmio-mux"; + #mux-control-cells = <1>; + + mux-reg-masks = <0x38 0x07>; + idle-states = <2>; /* Serial port mode 3 (takeover) */ + }; +}; diff --git a/arch/arm/mach-npcm/Kconfig b/arch/arm/mach-npcm/Kconfig index 684c9c9a32bd6f..034e77a05da236 100644 --- a/arch/arm/mach-npcm/Kconfig +++ b/arch/arm/mach-npcm/Kconfig @@ -22,6 +22,7 @@ config ARCH_NPCM7XX select PL310_ERRATA_588369 select PL310_ERRATA_727915 select MFD_SYSCON + select MULTIPLEXER help General support for NPCM7xx BMC (Poleg). From 3a241f9a180ec356705b8a59f25800e64d1c491e Mon Sep 17 00:00:00 2001 From: Kun Yi Date: Fri, 8 Jun 2018 10:49:54 -0700 Subject: [PATCH 37/37] net: npcm7xx: EMAC driver should not directly use seq_read. Procfs read should be npcm7xx_proc_read function so that the read operation correctly allocates dump memory. Change-Id: I2bd72fbc5c1668c062c6eb5b95057c7f050b6009 Signed-off-by: Kun Yi --- drivers/net/ethernet/nuvoton/npcm7xx_emc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/nuvoton/npcm7xx_emc.c b/drivers/net/ethernet/nuvoton/npcm7xx_emc.c index 739705b6f40bdd..2be932387e5377 100644 --- a/drivers/net/ethernet/nuvoton/npcm7xx_emc.c +++ b/drivers/net/ethernet/nuvoton/npcm7xx_emc.c @@ -1959,7 +1959,7 @@ static int npcm7xx_ether_proc_open(struct inode *inode, struct file *file) static const struct file_operations npcm7xx_ether_proc_fops = { .open = npcm7xx_ether_proc_open, - .read = seq_read, + .read = npcm7xx_proc_read, .llseek = seq_lseek, .release = single_release, }; @@ -1987,7 +1987,7 @@ static int npcm7xx_ether_proc_reset(struct inode *inode, struct file *file) static const struct file_operations npcm7xx_ether_proc_fops_reset = { .open = npcm7xx_ether_proc_reset, - .read = seq_read, + .read = npcm7xx_proc_read, .llseek = seq_lseek, .release = single_release, };