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/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"; + }; 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>; +}; 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>; +}; 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>; +}; 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/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/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 { + ... + }; +}; + 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>; +}; + 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"; + }; +}; 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>; +}; 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 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>; +}; + 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 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) 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 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..e974814b9642a5 --- /dev/null +++ b/arch/arm/boot/dts/nuvoton-npcm750-evb.dts @@ -0,0 +1,477 @@ +// 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>; + }; +}; + +&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/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>; + }; +}; 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 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). 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"); diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig index f6fa056a52fcfa..ffce5ca07cd812 100644 --- a/drivers/char/ipmi/Kconfig +++ b/drivers/char/ipmi/Kconfig @@ -81,6 +81,41 @@ 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_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 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 eefb0b301e836b..1ff86fd1237268 100644 --- a/drivers/char/ipmi/Makefile +++ b/drivers/char/ipmi/Makefile @@ -12,4 +12,7 @@ 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 +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.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/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"); diff --git a/drivers/char/ipmi/kcs_bmc_npcm7xx.c b/drivers/char/ipmi/kcs_bmc_npcm7xx.c new file mode 100644 index 00000000000000..722f7391fe1f69 --- /dev/null +++ b/drivers/char/ipmi/kcs_bmc_npcm7xx.c @@ -0,0 +1,215 @@ +// 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) + +#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 + * 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). + * 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 { + 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, .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) +{ + 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); + + 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) +{ + 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"); 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..6ff97f79fcd7dd --- /dev/null +++ b/drivers/clk/clk-npcm7xx.c @@ -0,0 +1,728 @@ +// 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("%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; + 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) + 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: + 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); + pr_err("clk setup fail\n"); +} + +CLK_OF_DECLARE(npcm7xx_clk_init, "nuvoton,npcm750-clk", npcm7xx_clk_init); + + + diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 54a67f8a28ebfb..48c6e94d2fd3aa 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -140,6 +140,15 @@ config VT8500_TIMER help Enables support for the VT8500 driver. +config NPCM7XX_TIMER + bool "NPCM7xx timer driver" if COMPILE_TEST + default y if ARCH_NPCM7XX + 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); + 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"); 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); + + 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"); 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"); 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..2be932387e5377 --- /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 = npcm7xx_proc_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 = npcm7xx_proc_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); 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 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"); 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/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"); 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 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 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 */ 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