diff --git a/.gitignore b/.gitignore index 86a1ba0d90353..64930994bd8ec 100644 --- a/.gitignore +++ b/.gitignore @@ -96,6 +96,11 @@ modules.order # /tar-install/ +# +# ni specific install directory +# +/ni-install/ + # # pacman files (make pacman-pkg) # diff --git a/Documentation/devicetree/bindings/auxdisplay/oel9m1027.txt b/Documentation/devicetree/bindings/auxdisplay/oel9m1027.txt new file mode 100644 index 0000000000000..ee0c1a59cadfc --- /dev/null +++ b/Documentation/devicetree/bindings/auxdisplay/oel9m1027.txt @@ -0,0 +1,15 @@ +Binding for OEL9M1027 OLED display module manufactured by Truly + +Required properties: + - compatible : Compatibility string. Must be "truly,oel9m1027". + +Example: + + oleddriver@3C { + compatible = "sinowealth,sh1107"; + reg = <0x3C>; + + oledmodule { + compatible = "truly,oel9m1027"; + }; + }; diff --git a/Documentation/devicetree/bindings/auxdisplay/sh1107.txt b/Documentation/devicetree/bindings/auxdisplay/sh1107.txt new file mode 100644 index 0000000000000..fa288b0c67058 --- /dev/null +++ b/Documentation/devicetree/bindings/auxdisplay/sh1107.txt @@ -0,0 +1,12 @@ +Binding for Sinowealth OLED driver + +Required properties: + - compatible: Compatibility string. Must be "sinowealth,sh1107". + - reg : i2c address of Truly OLED module (0x3C or 0x3D) + +Example: + + oleddriver@3C { + compatible = "sinowealth,sh1107"; + reg = <0x3C>; + }; diff --git a/Documentation/devicetree/bindings/leds/leds-ni793x.txt b/Documentation/devicetree/bindings/leds/leds-ni793x.txt new file mode 100644 index 0000000000000..5124e694cf88b --- /dev/null +++ b/Documentation/devicetree/bindings/leds/leds-ni793x.txt @@ -0,0 +1,21 @@ +LEDs connected to NI-793xR + +Required properties: +- compatible : should be : "ni,led-793x". +- reg: base address of LED control register space. + +The ni,led-793x device node only contains a single sub-node, 'user1'. + +LED sub-node properties: +- label : (optional) see leds/common.txt +- linux,default-trigger : (optional) see leds/common.txt + +Examples: + +leds-ni793x@4020F000 { + compatible = "ni,led-793x"; + reg = <0x4020F000 4>; + user1 { + label = "nilrt:user1:green"; + }; +}; diff --git a/Documentation/devicetree/bindings/misc/ni,cpld.txt b/Documentation/devicetree/bindings/misc/ni,cpld.txt new file mode 100644 index 0000000000000..a1abde6f5f0e9 --- /dev/null +++ b/Documentation/devicetree/bindings/misc/ni,cpld.txt @@ -0,0 +1,156 @@ +National Instruments CPLD + +National Instruments (NI) boards often have CPLDs on them to handle +board-specific tasks like LED control, board switches and buttons, +power sequencing, and CPU-independent watchdogs. + +NI's CPLDs are connected over i2c. As such, 'nicpld' must be a child +of an i2c node. + +nicpld properties: +- compatible : (required) + Should be "ni,cpld" +- reg: (required) + Address of the CPLD on the i2c bus + +Required sub-nodes: +- watchdogs : A node containing sub-nodes describing watchdogs + +Optional sub-nodes: +- switches : A node containing sub-nodes describing switches +- leds : A node containing sub-nodes describing LEDs + + +Watchdogs Child Node +==================== + +Each watchdog controlled by the CPLD is represented as a sub-node of +the nicpld's 'watchdogs' node. Each sub-node of 'watchdogs' has the +following properties. + +Required properties: +- interrupts : Should be the CPLD watchdog interrupt specifier + +Optional properties: +- label: A name for the CPLD switch + + +Switches Child Node +=================== + +Each switch or button controlled by the CPLD is represented as a +sub-node of the nicpld's 'switches' node. Each sub-node of 'switches' +has the following properties. + +Required properties: +- interrupts : The CPLD switch interrupt specifier + + +LEDs Child Node +=============== + +Each LED controlled by the CPLD is represented as a sub-node of the +nicpld's 'leds' node. The name of each LED node corresponds to an +LED type. The "-0" or "-1" suffixes specify an LED element (single +color) within a multi-color LED. + +Possible LED types: +- user1-0 +- user1-1 +- status-0 +- status-1 +- temp-alert-0 +- wifi-0 +- wifi-1 +- eth0-0 +- eth0-1 +- eth1-0 +- eth1-1 + +Different CPLDs support different LED types. The set of LEDs that +various CPLDs support can be determined by examining their product +code. The product code for a CPLD can be determined by reading the +"NICPLD_PRODUCT" (0x1D) register of the CPLD over i2c. Below is a +list of which CPLDs support which LED types. + +CPLD Products 0x00 and 0x01 +--------------------------- +user1-0 +user1-1 +status-0 +status-1 +wifi-0 +wifi-1 +eth0-0 +eth0-1 +eth1-0 +eth1-1 + +CPLD Product 0x02 +----------------- +status-0 +temp-alert-0 +eth0-0 +eth0-1 +eth1-0 +eth1-1 + +CPLD Products 0x03 and 0x04 +--------------------------- +user1-0 +status-0 +eth0-0 +eth0-1 +eth1-0 +eth1-1 + + +LED sub-node properties: +- label : (optional) + see bindings/leds/common.txt +- max-brightness : (optional) + The number of possible special blinking patterns. Selecting a + brightness will pick the blink pattern. +- linux,default-trigger : (optional) + see bindings/leds/common.txt + +Example: + +nicpld@40 { + compatible = "ni,cpld"; + reg = <0x40>; + + watchdogs { + boot-watchdog { + interrupt-parent = <&gpio>; + interrupts = <24 0>; + }; + }; + + switches { + wifi-switch { + interrupt-parent = <&gpio>; + interrupts = <25 0>; + }; + }; + + leds { + user1-0 { + label = "nilrt:user1:green"; + }; + status-0 { + label = "nilrt:status:yellow"; + max-brightness = <0xFFFF>; + }; + eth0-0 { + label = "nilrt:eth0:green"; + linux,default-trigger = + "e000b000.etherne:00:100Mb"; + }; + eth0-1 { + label = "nilrt:eth0:yellow"; + linux,default-trigger = + "e000b000.etherne:00:Gb"; + }; + }; +}; diff --git a/MAINTAINERS b/MAINTAINERS index 554e881b05bea..6161a4507e4ec 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18763,6 +18763,14 @@ F: drivers/misc/ocxl/ F: include/misc/ocxl* F: include/uapi/misc/ocxl.h +OEL9M1027 TRULY OLED MODULE DRIVER +M: Wilson Lee +S: Maintained +F: Documentation/devicetree/bindings/auxdisplay/oel9m1027.txt +F: drivers/auxdisplay/oel9m1027.c +F: drivers/auxdisplay/oel9m1027fb.c +F: drivers/auxdisplay/oel9m1027.h + OMAP AUDIO SUPPORT M: Peter Ujfalusi M: Jarkko Nikula @@ -23452,6 +23460,13 @@ S: Orphan F: drivers/media/platform/renesas/sh_vou.c F: include/media/drv-intf/sh_vou.h +SH1107 OLED CONTROLLER DRIVER +M: Wilson Lee +S: Maintained +F: Documentation/devicetree/bindings/auxdisplay/sh1107.txt +F: drivers/auxdisplay/sh1107.c +F: drivers/auxdisplay/sh1107.h + SI2157 MEDIA DRIVER L: linux-media@vger.kernel.org S: Orphan diff --git a/Makefile b/Makefile index 7f5abef39fd38..36cc213411ffd 100644 --- a/Makefile +++ b/Makefile @@ -1592,7 +1592,7 @@ endif # CONFIG_MODULES CLEAN_FILES += vmlinux.symvers modules-only.symvers \ modules.builtin modules.builtin.modinfo modules.nsdeps \ modules.builtin.ranges vmlinux.o.map vmlinux.unstripped \ - compile_commands.json rust/test \ + compile_commands.json rust/test ni-install \ rust-project.json .vmlinux.objs .vmlinux.export.c \ .builtin-dtbs-list .builtin-dtbs.S diff --git a/arch/arm/boot/dts/xilinx/Makefile b/arch/arm/boot/dts/xilinx/Makefile index 9233e539b192b..f44bc06d62858 100644 --- a/arch/arm/boot/dts/xilinx/Makefile +++ b/arch/arm/boot/dts/xilinx/Makefile @@ -14,4 +14,25 @@ dtb-$(CONFIG_ARCH_ZYNQ) += \ zynq-zturn.dtb \ zynq-zturn-v5.dtb \ zynq-zybo.dtb \ - zynq-zybo-z7.dtb + zynq-zybo-z7.dtb \ + ni-762F.dtb \ + ni-76D3.dtb \ + ni-76D6.dtb \ + ni-76F2.dtb \ + ni-7740.dtb \ + ni-7741.dtb \ + ni-7742.dtb \ + ni-7743.dtb \ + ni-7744.dtb \ + ni-774C.dtb \ + ni-774E.dtb \ + ni-775E.dtb \ + ni-77AC.dtb \ + ni-77B1.dtb \ + ni-77B2.dtb \ + ni-77D4.dtb \ + ni-77D5.dtb \ + ni-77D6.dtb \ + ni-7885.dtb \ + ni-793C.dtb \ + ni-7AAE.dtb diff --git a/arch/arm/boot/dts/xilinx/ni-762F.dts b/arch/arm/boot/dts/xilinx/ni-762F.dts new file mode 100644 index 0000000000000..5e9b94dd530e9 --- /dev/null +++ b/arch/arm/boot/dts/xilinx/ni-762F.dts @@ -0,0 +1,6 @@ +/dts-v1/; +/include/ "ni-myrio.dtsi" + +/ { + model = "NI myRIO-1900"; +}; diff --git a/arch/arm/boot/dts/xilinx/ni-76D3.dts b/arch/arm/boot/dts/xilinx/ni-76D3.dts new file mode 100644 index 0000000000000..c5fcc1d079d23 --- /dev/null +++ b/arch/arm/boot/dts/xilinx/ni-76D3.dts @@ -0,0 +1,6 @@ +/dts-v1/; +/include/ "ni-myrio.dtsi" + +/ { + model = "NI myRIO-1950"; +}; diff --git a/arch/arm/boot/dts/xilinx/ni-dosequis.dts b/arch/arm/boot/dts/xilinx/ni-76D6.dts similarity index 66% rename from arch/arm/boot/dts/xilinx/ni-dosequis.dts rename to arch/arm/boot/dts/xilinx/ni-76D6.dts index ea43df8ff569a..1b4f69deff8ac 100644 --- a/arch/arm/boot/dts/xilinx/ni-dosequis.dts +++ b/arch/arm/boot/dts/xilinx/ni-76D6.dts @@ -1,18 +1,17 @@ /dts-v1/; /include/ "ni-zynq.dtsi" -/* NIDEVCODE 76D6 */ - / { - model = "NI Dos Equis"; + model = "NI cRIO-9068"; compatible = "ni,zynq", "xlnx,zynq-7000"; +}; - amba@0 { - i2c0: i2c@e0004000 { + +&i2c0 { nicpld@40 { watchdogs { boot-watchdog { - interrupt-parent = <&gpio>; + interrupt-parent = <&gpio0>; interrupts = <46 2 /* IRQ_TYPE_EDGE_FALLING */>; }; }; @@ -31,31 +30,25 @@ label = "nilrt:status:yellow"; max-brightness = <0xFFFF>; }; - wifi-0 { - label = "nilrt:wifi:primary"; - }; - wifi-1 { - label = "nilrt:wifi:secondary"; - }; eth0-0 { label = "nilrt:eth0:green"; linux,default-trigger = - "e000b000.etherne:00:100Mb"; + "e000b000.ethernet-ffffffff:00:100Mbps"; }; eth0-1 { label = "nilrt:eth0:yellow"; linux,default-trigger = - "e000b000.etherne:00:Gb"; + "e000b000.ethernet-ffffffff:00:1Gbps"; }; eth1-0 { label = "nilrt:eth1:green"; linux,default-trigger = - "e000b000.etherne:01:100Mb"; + "e000b000.ethernet-ffffffff:01:100Mbps"; }; eth1-1 { label = "nilrt:eth1:yellow"; linux,default-trigger = - "e000b000.etherne:01:Gb"; + "e000b000.ethernet-ffffffff:01:1Gbps"; }; }; }; @@ -63,8 +56,6 @@ ds3231_rtc@68 { status = "okay"; }; - }; - }; }; &gem0 { @@ -75,27 +66,37 @@ #size-cells = <0x0>; phy0: phy@0 { - compatible = "marvell,88e1512"; + compatible = "marvell,88e1512","ethernet-phy-ieee802.3-c22"; device_type = "ethernet-phy"; reg = <0x0>; /* Interrupt on GPIO1, shared with phy1. */ interrupts = <1 8 /* IRQ_TYPE_LEVEL_LOW */>; - interrupt-parent = <&gpio>; - /* Page 3, Register 16, LED[2:0] Function - Control Register */ - leds = <0x1881>; + interrupt-parent = <&gpio0>; + + /* marvell,reg-init = + * + * reg-page = MII_MARVELL_LED_PAGE (3) + * reg = PHY_LED_CTRL (16) + * value = 0x1881 (LED[0] = on:link, blink:activity, off:no link) + */ + marvell,reg-init = <3 16 0 0x1881>; }; phy1: phy@1 { - compatible = "marvell,88e1512"; + compatible = "marvell,88e1512","ethernet-phy-ieee802.3-c22"; device_type = "ethernet-phy"; reg = <0x1>; /* Interrupt on GPIO1, shared with phy0. */ interrupts = <1 8 /* IRQ_TYPE_LEVEL_LOW */>; - interrupt-parent = <&gpio>; - /* Page 3, Register 16, LED[2:0] Function - Control Register */ - leds = <0x1881>; + interrupt-parent = <&gpio0>; + + /* marvell,reg-init = + * + * reg-page = MII_MARVELL_LED_PAGE (3) + * reg = PHY_LED_CTRL (16) + * value = 0x1881 (LED[0] = on:link, blink:activity, off:no link) + */ + marvell,reg-init = <3 16 0 0x1881>; }; }; @@ -107,7 +108,7 @@ phy-mode = "rgmii-id"; cdns,no_mdio_bus; emio-speed-gpios = <0>, - <&gpio 54 0>; + <&gpio0 54 0>; }; &ni_uart0 { diff --git a/arch/arm/boot/dts/xilinx/ni-roborio.dts b/arch/arm/boot/dts/xilinx/ni-76F2.dts similarity index 51% rename from arch/arm/boot/dts/xilinx/ni-roborio.dts rename to arch/arm/boot/dts/xilinx/ni-76F2.dts index d41d9ba1f3bbe..f7b640041f1f6 100644 --- a/arch/arm/boot/dts/xilinx/ni-roborio.dts +++ b/arch/arm/boot/dts/xilinx/ni-76F2.dts @@ -1,8 +1,6 @@ /dts-v1/; /include/ "ni-zynq.dtsi" -/* NIDEVCODE 76F2 */ - / { model = "NI roboRIO"; compatible = "ni,zynq", "xlnx,zynq-7000"; @@ -12,69 +10,74 @@ spi1 = &spi1; }; - amba@0 { - adc: adc@f8007100 { + leds { + compatible = "gpio-leds"; + wlan1 { + /* green */ + label = "nilrt:wifi:primary"; + gpios = <&gpio0 55 0>; + }; + + wlan2 { + /* red */ + label = "nilrt:wifi:secondary"; + gpios = <&gpio0 56 0>; + }; + }; +}; + +&adc { xlnx,channels { #address-cells = <1>; #size-cells = <0>; /* Channel 0 maps to VPVN */ channel@0 { reg = <0>; - xlnx,extend-name = "vin_v"; }; /* Channel 1 maps to VAUX0 */ channel@1 { reg = <1>; - xlnx,extend-name = "vin_c"; }; /* Channel 2 maps to VAUX1 */ channel@2 { reg = <2>; - xlnx,extend-name = "user5v_v"; }; /* Channel 3 maps to VAUX2 */ channel@3 { reg = <3>; - xlnx,extend-name = "user5v_c"; }; /* Channel 4 maps to VAUX3 */ channel@4 { reg = <4>; - xlnx,extend-name = "user3v3_v"; }; /* Channel 5 maps to VAUX4 */ channel@5 { reg = <5>; - xlnx,extend-name = "user3v3_c"; }; /* Channel 9 maps to VAUX8 */ channel@9 { reg = <9>; - xlnx,extend-name = "user6v_c"; }; /* Channel 10 maps to VAUX9 */ channel@10 { reg = <10>; - xlnx,extend-name = "bist_ao_v"; }; /* Channel 11 maps to VAUX10 */ channel@11 { reg = <11>; - xlnx,extend-name = "bist_dio_v"; }; /* Channel 12 maps to VAUX11 */ channel@12 { reg = <12>; - xlnx,extend-name = "user6v_v"; }; }; - }; +}; - i2c0: i2c@e0004000 { +&i2c0 { nicpld@40 { watchdogs { boot-watchdog { - interrupt-parent = <&gpio>; + interrupt-parent = <&gpio0>; interrupts = <24 2 /* IRQ_TYPE_EDGE_FALLING */>; }; }; @@ -86,56 +89,54 @@ }; }; }; - }; - - i2c1: i2c@e0005000 { - status = "okay"; - }; - - i2c2: i2c@81000000 { - status = "okay"; - }; +}; - spi0: spi@e0006000 { - status = "okay"; - num-cs = <7>; - is-decoded-cs = <1>; - speed-hz = <200000000>; +&i2c1 { + status = "okay"; +}; - spidev@0 { - compatible = "spidev"; - spi-max-frequency = <2000000>; - reg = <0>; - }; - spidev@1 { - compatible = "spidev"; - spi-max-frequency = <2000000>; - reg = <1>; - }; - spidev@2 { - compatible = "spidev"; - spi-max-frequency = <2000000>; - reg = <2>; - }; - spidev@3 { - compatible = "spidev"; - spi-max-frequency = <2000000>; - reg = <3>; - }; - }; +&i2c2 { + status = "okay"; +}; - spi1: spi@e0007000 { - status = "okay"; - num-cs = <3>; - is-decoded-cs = <0>; - speed-hz = <200000000>; +&spi0 { + status = "okay"; + num-cs = <7>; + is-decoded-cs = <1>; + speed-hz = <200000000>; + + spidev@0 { + compatible = "spidev"; + spi-max-frequency = <2000000>; + reg = <0>; + }; + spidev@1 { + compatible = "spidev"; + spi-max-frequency = <2000000>; + reg = <1>; + }; + spidev@2 { + compatible = "spidev"; + spi-max-frequency = <2000000>; + reg = <2>; + }; + spidev@3 { + compatible = "spidev"; + spi-max-frequency = <2000000>; + reg = <3>; + }; +}; - spidev@0 { - compatible = "spidev"; - spi-max-frequency = <2000000>; - reg = <0>; - }; - }; +&spi1 { + status = "okay"; + num-cs = <3>; + is-decoded-cs = <0>; + speed-hz = <200000000>; + + spidev@0 { + compatible = "spidev"; + spi-max-frequency = <2000000>; + reg = <0>; }; }; @@ -143,17 +144,18 @@ status = "okay"; phy-handle = <&phy0>; phy-mode = "rgmii-id"; - emio-speed-gpios = <&gpio 54 0>, + emio-speed-gpios = <&gpio0 54 0>, <0>; #address-cells = <0x1>; #size-cells = <0x0>; phy0: phy@0 { - compatible = "smsc,lan8720"; + compatible = "smsc,lan8720","ethernet-phy-ieee802.3-c22"; device_type = "ethernet-phy"; reg = <0x0>; /* Interrupt pin is connected from PL pin to FPGA IRQ 3 */ interrupts = <0 32 4>; + smsc,disable-energy-detect; }; }; diff --git a/arch/arm/boot/dts/xilinx/ni-7740.dts b/arch/arm/boot/dts/xilinx/ni-7740.dts new file mode 100644 index 0000000000000..8fa59d7c8dcd1 --- /dev/null +++ b/arch/arm/boot/dts/xilinx/ni-7740.dts @@ -0,0 +1,6 @@ +/dts-v1/; +/include/ "ni-solgood.dtsi" + +/ { + model = "NI cRIO-9063"; +}; diff --git a/arch/arm/boot/dts/xilinx/ni-7741.dts b/arch/arm/boot/dts/xilinx/ni-7741.dts new file mode 100644 index 0000000000000..c7f1e246456a7 --- /dev/null +++ b/arch/arm/boot/dts/xilinx/ni-7741.dts @@ -0,0 +1,6 @@ +/dts-v1/; +/include/ "ni-solbetter.dtsi" + +/ { + model = "NI cRIO-9064"; +}; diff --git a/arch/arm/boot/dts/xilinx/ni-7742.dts b/arch/arm/boot/dts/xilinx/ni-7742.dts new file mode 100644 index 0000000000000..88ca0b6450e83 --- /dev/null +++ b/arch/arm/boot/dts/xilinx/ni-7742.dts @@ -0,0 +1,6 @@ +/dts-v1/; +/include/ "ni-solbetter.dtsi" + +/ { + model = "NI cRIO-9065"; +}; diff --git a/arch/arm/boot/dts/xilinx/ni-7743.dts b/arch/arm/boot/dts/xilinx/ni-7743.dts new file mode 100644 index 0000000000000..95f70a8383b56 --- /dev/null +++ b/arch/arm/boot/dts/xilinx/ni-7743.dts @@ -0,0 +1,6 @@ +/dts-v1/; +/include/ "ni-solgood.dtsi" + +/ { + model = "NI cRIO-9066"; +}; diff --git a/arch/arm/boot/dts/xilinx/ni-7744.dts b/arch/arm/boot/dts/xilinx/ni-7744.dts new file mode 100644 index 0000000000000..9f5a652c20eae --- /dev/null +++ b/arch/arm/boot/dts/xilinx/ni-7744.dts @@ -0,0 +1,6 @@ +/dts-v1/; +/include/ "ni-solbetter.dtsi" + +/ { + model = "NI cRIO-9067"; +}; diff --git a/arch/arm/boot/dts/xilinx/ni-774C.dts b/arch/arm/boot/dts/xilinx/ni-774C.dts new file mode 100644 index 0000000000000..c80a63c642266 --- /dev/null +++ b/arch/arm/boot/dts/xilinx/ni-774C.dts @@ -0,0 +1,6 @@ +/dts-v1/; +/include/ "ni-solenetexp.dtsi" + +/ { + model = "NI 9147"; +}; diff --git a/arch/arm/boot/dts/xilinx/ni-774E.dts b/arch/arm/boot/dts/xilinx/ni-774E.dts new file mode 100644 index 0000000000000..1557e8c5f8b3e --- /dev/null +++ b/arch/arm/boot/dts/xilinx/ni-774E.dts @@ -0,0 +1,6 @@ +/dts-v1/; +/include/ "ni-solenetexp.dtsi" + +/ { + model = "NI 9149"; +}; diff --git a/arch/arm/boot/dts/xilinx/ni-tecate.dts b/arch/arm/boot/dts/xilinx/ni-775E.dts similarity index 80% rename from arch/arm/boot/dts/xilinx/ni-tecate.dts rename to arch/arm/boot/dts/xilinx/ni-775E.dts index 15124e4b12cad..b5dc0d2db518f 100644 --- a/arch/arm/boot/dts/xilinx/ni-tecate.dts +++ b/arch/arm/boot/dts/xilinx/ni-775E.dts @@ -1,18 +1,16 @@ /dts-v1/; /include/ "ni-zynq.dtsi" -/* NIDEVCODE 775E */ - / { - model = "NI Tecate"; + model = "NI sbRIO-9651"; compatible = "ni,zynq", "xlnx,zynq-7000"; +}; - amba@0 { - i2c0: i2c@e0004000 { +&i2c0 { nicpld@40 { watchdogs { boot-watchdog { - interrupt-parent = <&gpio>; + interrupt-parent = <&gpio0>; interrupts = <15 2 /* IRQ_TYPE_EDGE_FALLING */>; }; }; @@ -25,12 +23,12 @@ eth0-0 { label = "nilrt:eth0:green"; linux,default-trigger = - "e000b000.etherne:00:100Mb"; + "e000b000.ethernet-ffffffff:00:100Mbps"; }; eth0-1 { label = "nilrt:eth0:yellow"; linux,default-trigger = - "e000b000.etherne:00:Gb"; + "e000b000.ethernet-ffffffff:00:1Gbps"; }; }; }; @@ -38,8 +36,6 @@ ds3231_rtc@68 { status = "okay"; }; - }; - }; }; &gem0 { @@ -47,7 +43,8 @@ /* No fpga_clk specified because we want our FPGA clock * (fclk0) to always be 125 MHz. The bootloader sets - * fclk0 to 125 MHz and we just leave it like that. */ + * fclk0 to 125 MHz and we just leave it like that. + */ phy-handle = <&phy0>; phy-mode = "rgmii-id"; @@ -55,15 +52,15 @@ #size-cells = <0x0>; emio-speed-gpios = <0>, - <&gpio 54 0>; + <&gpio0 54 0>; phy0: phy@0 { - compatible = "micrel,KSZ9031"; + compatible = "micrel,KSZ9031","ethernet-phy-ieee802.3-c22"; device_type = "ethernet-phy"; reg = <0x0>; /* Interrupt on GPIO1. */ interrupts = <1 8 /* IRQ_TYPE_LEVEL_LOW */>; - interrupt-parent = <&gpio>; + interrupt-parent = <&gpio0>; /* Set RX_CLK Pad Skew [4:0] to 0b00000. */ rxc-skew-ps = <0>; @@ -75,23 +72,24 @@ /* No fpga_clk specified because we want our FPGA clock (fclk0) to * always be 125 MHz. The bootloader sets fclk0 to 125 MHz and we just - * leave it like that. */ + * leave it like that. + */ phy-handle = <&phy1>; phy-mode = "rgmii-id"; #address-cells = <0x1>; #size-cells = <0x0>; - emio-speed-gpios = <&gpio 56 0>, - <&gpio 55 0>; + emio-speed-gpios = <&gpio0 56 0>, + <&gpio0 55 0>; phy1: phy@1 { - compatible = "micrel,KSZ9031"; + compatible = "micrel,KSZ9031","ethernet-phy-ieee802.3-c22"; device_type = "ethernet-phy"; reg = <0x1>; /* Interrupt on GPIO57. */ interrupts = <57 8 /* IRQ_TYPE_LEVEL_LOW */>; - interrupt-parent = <&gpio>; + interrupt-parent = <&gpio0>; }; }; diff --git a/arch/arm/boot/dts/xilinx/ni-77AC.dts b/arch/arm/boot/dts/xilinx/ni-77AC.dts new file mode 100644 index 0000000000000..a2456ca3cbe36 --- /dev/null +++ b/arch/arm/boot/dts/xilinx/ni-77AC.dts @@ -0,0 +1,6 @@ +/dts-v1/; +/include/ "ni-793x.dtsi" + +/ { + model = "NI 7935R"; +}; diff --git a/arch/arm/boot/dts/xilinx/ni-77B1.dts b/arch/arm/boot/dts/xilinx/ni-77B1.dts new file mode 100644 index 0000000000000..b78d89e3e36d6 --- /dev/null +++ b/arch/arm/boot/dts/xilinx/ni-77B1.dts @@ -0,0 +1,6 @@ +/dts-v1/; +/include/ "ni-793x.dtsi" + +/ { + model = "NI 7931R"; +}; diff --git a/arch/arm/boot/dts/xilinx/ni-77B2.dts b/arch/arm/boot/dts/xilinx/ni-77B2.dts new file mode 100644 index 0000000000000..c9c371e7cc302 --- /dev/null +++ b/arch/arm/boot/dts/xilinx/ni-77B2.dts @@ -0,0 +1,6 @@ +/dts-v1/; +/include/ "ni-793x.dtsi" + +/ { + model = "NI 7932R"; +}; diff --git a/arch/arm/boot/dts/xilinx/ni-sbrio-9637.dts b/arch/arm/boot/dts/xilinx/ni-77D4.dts similarity index 83% rename from arch/arm/boot/dts/xilinx/ni-sbrio-9637.dts rename to arch/arm/boot/dts/xilinx/ni-77D4.dts index 7626db67b8073..4c9e32788fa29 100644 --- a/arch/arm/boot/dts/xilinx/ni-sbrio-9637.dts +++ b/arch/arm/boot/dts/xilinx/ni-77D4.dts @@ -1,18 +1,16 @@ /dts-v1/; /include/ "ni-zynq.dtsi" -/* NIDEVCODE 77D4 */ - / { model = "NI sbRIO-9637"; compatible = "ni,zynq", "xlnx,zynq-7000"; +}; - amba@0 { - i2c0: i2c@e0004000 { +&i2c0 { nicpld@40 { watchdogs { boot-watchdog { - interrupt-parent = <&gpio>; + interrupt-parent = <&gpio0>; interrupts = <1 2 /* IRQ_TYPE_EDGE_FALLING */>; }; }; @@ -28,12 +26,12 @@ eth0-0 { label = "nilrt:eth0:green"; linux,default-trigger = - "e000b000.etherne:00:100Mb"; + "e000b000.ethernet-ffffffff:00:100Mbps"; }; eth0-1 { label = "nilrt:eth0:yellow"; linux,default-trigger = - "e000b000.etherne:00:Gb"; + "e000b000.ethernet-ffffffff:00:1Gbps"; }; }; }; @@ -41,8 +39,6 @@ ds3231_rtc@68 { status = "okay"; }; - }; - }; }; &gem0 { @@ -52,32 +48,35 @@ phy-handle = <&phy0>; phy-mode = "rgmii-id"; emio-speed-gpios = <0>, - <&gpio 54 0>; + <&gpio0 54 0>; #address-cells = <0x1>; #size-cells = <0x0>; phy0: phy@0 { - compatible = "micrel,KSZ9031"; + compatible = "micrel,KSZ9031","ethernet-phy-ieee802.3-c22"; device_type = "ethernet-phy"; reg = <0x0>; /* Interrupt on GPIO25. */ interrupts = <25 8 /* IRQ_TYPE_LEVEL_LOW */>; - interrupt-parent = <&gpio>; + interrupt-parent = <&gpio0>; /* RX_DV Pad Skew [7:4] = +0.30ns (0xC0) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 + 300 is 720 ps. */ + * ps. 420 + 300 is 720 ps. + */ rxdv-skew-ps = <720>; /* TX_EN Pad Skew [3:0] = -0.30ns (0x02) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 - 300 is 120 ps. */ + * ps. 420 - 300 is 120 ps. + */ txen-skew-ps = <120>; /* RXD 0-3 Pad Skew = +0.30ns (0xC) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 + 300 is 720 ps. */ + * ps. 420 + 300 is 720 ps. + */ rxd0-skew-ps = <720>; rxd1-skew-ps = <720>; rxd2-skew-ps = <720>; @@ -85,7 +84,8 @@ /* TXD 0-3 Pad Skew = -0.30ns (0x2) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 - 300 is 120 ps. */ + * ps. 420 - 300 is 120 ps. + */ txd0-skew-ps = <120>; txd1-skew-ps = <120>; txd2-skew-ps = <120>; @@ -94,11 +94,13 @@ /* Write value to MMD Address 2h, Register 8h */ /* RX_CLK Pad Skew [4:0] = -0.9ns (0x0) */ /* Default is 0b01111, or 15. 15 * 60 ps = 900 ps. 0.90 ns is - * 900 ps. 900 - 900 is 0 ps. */ + * 900 ps. 900 - 900 is 0 ps. + */ rxc-skew-ps = <0>; /* GTX_CLK Pad Skew [9:5] = +0.96ns (0x3E) */ /* Default is 0b01111, or 15. 15 * 60 ps = 900 ps. 0.96 ns is - * 960 ps. 900 + 960 is 1860 ps. */ + * 960 ps. 900 + 960 is 1860 ps. + */ txc-skew-ps = <1860>; }; }; diff --git a/arch/arm/boot/dts/xilinx/ni-sbrio-9627.dts b/arch/arm/boot/dts/xilinx/ni-77D5.dts similarity index 82% rename from arch/arm/boot/dts/xilinx/ni-sbrio-9627.dts rename to arch/arm/boot/dts/xilinx/ni-77D5.dts index 435a6169e0604..9ac70682c992a 100644 --- a/arch/arm/boot/dts/xilinx/ni-sbrio-9627.dts +++ b/arch/arm/boot/dts/xilinx/ni-77D5.dts @@ -1,18 +1,16 @@ /dts-v1/; /include/ "ni-zynq.dtsi" -/* NIDEVCODE 77D5 */ - / { model = "NI sbRIO-9627"; compatible = "ni,zynq", "xlnx,zynq-7000"; +}; - amba@0 { - i2c0: i2c@e0004000 { +&i2c0 { nicpld@40 { watchdogs { boot-watchdog { - interrupt-parent = <&gpio>; + interrupt-parent = <&gpio0>; interrupts = <1 2 /* IRQ_TYPE_EDGE_FALLING */>; }; }; @@ -28,22 +26,22 @@ eth0-0 { label = "nilrt:eth0:green"; linux,default-trigger = - "e000b000.etherne:00:100Mb"; + "e000b000.ethernet-ffffffff:00:100Mbps"; }; eth0-1 { label = "nilrt:eth0:yellow"; linux,default-trigger = - "e000b000.etherne:00:Gb"; + "e000b000.ethernet-ffffffff:00:1Gbps"; }; eth1-0 { label = "nilrt:eth1:green"; linux,default-trigger = - "e000b000.etherne:01:100Mb"; + "e000b000.ethernet-ffffffff:01:100Mbps"; }; eth1-1 { label = "nilrt:eth1:yellow"; linux,default-trigger = - "e000b000.etherne:01:Gb"; + "e000b000.ethernet-ffffffff:01:1Gbps"; }; }; }; @@ -51,8 +49,6 @@ ds3231_rtc@68 { status = "okay"; }; - }; - }; }; &gem0 { @@ -62,32 +58,35 @@ phy-handle = <&phy0>; phy-mode = "rgmii-id"; emio-speed-gpios = <0>, - <&gpio 54 0>; + <&gpio0 54 0>; #address-cells = <0x1>; #size-cells = <0x0>; phy0: phy@0 { - compatible = "micrel,KSZ9031"; + compatible = "micrel,KSZ9031","ethernet-phy-ieee802.3-c22"; device_type = "ethernet-phy"; reg = <0x0>; /* Interrupt on GPIO25, shared with phy1. */ interrupts = <25 8 /* IRQ_TYPE_LEVEL_LOW */>; - interrupt-parent = <&gpio>; + interrupt-parent = <&gpio0>; /* RX_DV Pad Skew [7:4] = +0.30ns (0xC0) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 + 300 is 720 ps. */ + * ps. 420 + 300 is 720 ps. + */ rxdv-skew-ps = <720>; /* TX_EN Pad Skew [3:0] = -0.30ns (0x02) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 - 300 is 120 ps. */ + * ps. 420 - 300 is 120 ps. + */ txen-skew-ps = <120>; /* RXD 0-3 Pad Skew = +0.30ns (0xC) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 + 300 is 720 ps. */ + * ps. 420 + 300 is 720 ps. + */ rxd0-skew-ps = <720>; rxd1-skew-ps = <720>; rxd2-skew-ps = <720>; @@ -95,7 +94,8 @@ /* TXD 0-3 Pad Skew = -0.30ns (0x2) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 - 300 is 120 ps. */ + * ps. 420 - 300 is 120 ps. + */ txd0-skew-ps = <120>; txd1-skew-ps = <120>; txd2-skew-ps = <120>; @@ -104,35 +104,40 @@ /* Write value to MMD Address 2h, Register 8h */ /* RX_CLK Pad Skew [4:0] = -0.9ns (0x0) */ /* Default is 0b01111, or 15. 15 * 60 ps = 900 ps. 0.90 ns is - * 900 ps. 900 - 900 is 0 ps. */ + * 900 ps. 900 - 900 is 0 ps. + */ rxc-skew-ps = <0>; /* GTX_CLK Pad Skew [9:5] = +0.96ns (0x3E) */ /* Default is 0b01111, or 15. 15 * 60 ps = 900 ps. 0.96 ns is - * 960 ps. 900 + 960 is 1860 ps. */ + * 960 ps. 900 + 960 is 1860 ps. + */ txc-skew-ps = <1860>; }; phy1: phy@1 { - compatible = "micrel,KSZ9031"; + compatible = "micrel,KSZ9031","ethernet-phy-ieee802.3-c22"; device_type = "ethernet-phy"; reg = <0x1>; /* Interrupt on GPIO25, shared with phy0. */ interrupts = <25 8 /* IRQ_TYPE_LEVEL_LOW */>; - interrupt-parent = <&gpio>; + interrupt-parent = <&gpio0>; /* RX_DV Pad Skew [7:4] = +0.30ns (0xC0) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 + 300 is 720 ps. */ + * ps. 420 + 300 is 720 ps. + */ rxdv-skew-ps = <720>; /* TX_EN Pad Skew [3:0] = -0.30ns (0x02) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 - 300 is 120 ps. */ + * ps. 420 - 300 is 120 ps. + */ txen-skew-ps = <120>; /* RXD 0-3 Pad Skew = +0.30ns (0xC) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 + 300 is 720 ps. */ + * ps. 420 + 300 is 720 ps. + */ rxd0-skew-ps = <720>; rxd1-skew-ps = <720>; rxd2-skew-ps = <720>; @@ -140,7 +145,8 @@ /* TXD 0-3 Pad Skew = -0.30ns (0x2) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 - 300 is 120 ps. */ + * ps. 420 - 300 is 120 ps. + */ txd0-skew-ps = <120>; txd1-skew-ps = <120>; txd2-skew-ps = <120>; @@ -149,11 +155,13 @@ /* Write value to MMD Address 2h, Register 8h */ /* RX_CLK Pad Skew [4:0] = -0.9ns (0x0) */ /* Default is 0b01111, or 15. 15 * 60 ps = 900 ps. 0.90 ns is - * 900 ps. 900 - 900 is 0 ps. */ + * 900 ps. 900 - 900 is 0 ps. + */ rxc-skew-ps = <0>; /* GTX_CLK Pad Skew [9:5] = +0.96ns (0x3E) */ /* Default is 0b01111, or 15. 15 * 60 ps = 900 ps. 0.96 ns is - * 960 ps. 900 + 960 is 1860 ps. */ + * 960 ps. 900 + 960 is 1860 ps. + */ txc-skew-ps = <1860>; }; }; @@ -166,7 +174,7 @@ phy-mode = "rgmii-id"; cdns,no_mdio_bus; emio-speed-gpios = <0>, - <&gpio 55 0>; + <&gpio0 55 0>; }; &sdhci0 { diff --git a/arch/arm/boot/dts/xilinx/ni-sbrio-9607.dts b/arch/arm/boot/dts/xilinx/ni-77D6.dts similarity index 82% rename from arch/arm/boot/dts/xilinx/ni-sbrio-9607.dts rename to arch/arm/boot/dts/xilinx/ni-77D6.dts index e582fca748bf3..1f9ba5e003eb5 100644 --- a/arch/arm/boot/dts/xilinx/ni-sbrio-9607.dts +++ b/arch/arm/boot/dts/xilinx/ni-77D6.dts @@ -1,18 +1,16 @@ /dts-v1/; /include/ "ni-zynq.dtsi" -/* NIDEVCODE 77D6 */ - / { model = "NI sbRIO-9607"; compatible = "ni,zynq", "xlnx,zynq-7000"; +}; - amba@0 { - i2c0: i2c@e0004000 { +&i2c0 { nicpld@40 { watchdogs { boot-watchdog { - interrupt-parent = <&gpio>; + interrupt-parent = <&gpio0>; interrupts = <1 2 /* IRQ_TYPE_EDGE_FALLING */>; }; }; @@ -28,22 +26,22 @@ eth0-0 { label = "nilrt:eth0:green"; linux,default-trigger = - "e000b000.etherne:00:100Mb"; + "e000b000.ethernet-ffffffff:00:100Mbps"; }; eth0-1 { label = "nilrt:eth0:yellow"; linux,default-trigger = - "e000b000.etherne:00:Gb"; + "e000b000.ethernet-ffffffff:00:1Gbps"; }; eth1-0 { label = "nilrt:eth1:green"; linux,default-trigger = - "e000b000.etherne:01:100Mb"; + "e000b000.ethernet-ffffffff:01:100Mbps"; }; eth1-1 { label = "nilrt:eth1:yellow"; linux,default-trigger = - "e000b000.etherne:01:Gb"; + "e000b000.ethernet-ffffffff:01:1Gbps"; }; }; }; @@ -51,8 +49,6 @@ ds3231_rtc@68 { status = "okay"; }; - }; - }; }; &gem0 { @@ -62,32 +58,35 @@ phy-handle = <&phy0>; phy-mode = "rgmii-id"; emio-speed-gpios = <0>, - <&gpio 54 0>; + <&gpio0 54 0>; #address-cells = <0x1>; #size-cells = <0x0>; phy0: phy@0 { - compatible = "micrel,KSZ9031"; + compatible = "micrel,KSZ9031","ethernet-phy-ieee802.3-c22"; device_type = "ethernet-phy"; reg = <0x0>; /* Interrupt on GPIO25, shared with phy1. */ interrupts = <25 8 /* IRQ_TYPE_LEVEL_LOW */>; - interrupt-parent = <&gpio>; + interrupt-parent = <&gpio0>; /* RX_DV Pad Skew [7:4] = +0.30ns (0xC0) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 + 300 is 720 ps. */ + * ps. 420 + 300 is 720 ps. + */ rxdv-skew-ps = <720>; /* TX_EN Pad Skew [3:0] = -0.30ns (0x02) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 - 300 is 120 ps. */ + * ps. 420 - 300 is 120 ps. + */ txen-skew-ps = <120>; /* RXD 0-3 Pad Skew = +0.30ns (0xC) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 + 300 is 720 ps. */ + * ps. 420 + 300 is 720 ps. + */ rxd0-skew-ps = <720>; rxd1-skew-ps = <720>; rxd2-skew-ps = <720>; @@ -95,7 +94,8 @@ /* TXD 0-3 Pad Skew = -0.30ns (0x2) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 - 300 is 120 ps. */ + * ps. 420 - 300 is 120 ps. + */ txd0-skew-ps = <120>; txd1-skew-ps = <120>; txd2-skew-ps = <120>; @@ -104,35 +104,40 @@ /* Write value to MMD Address 2h, Register 8h */ /* RX_CLK Pad Skew [4:0] = -0.9ns (0x0) */ /* Default is 0b01111, or 15. 15 * 60 ps = 900 ps. 0.90 ns is - * 900 ps. 900 - 900 is 0 ps. */ + * 900 ps. 900 - 900 is 0 ps. + */ rxc-skew-ps = <0>; /* GTX_CLK Pad Skew [9:5] = +0.96ns (0x3E) */ /* Default is 0b01111, or 15. 15 * 60 ps = 900 ps. 0.96 ns is - * 960 ps. 900 + 960 is 1860 ps. */ + * 960 ps. 900 + 960 is 1860 ps. + */ txc-skew-ps = <1860>; }; phy1: phy@1 { - compatible = "micrel,KSZ9031"; + compatible = "micrel,KSZ9031","ethernet-phy-ieee802.3-c22"; device_type = "ethernet-phy"; reg = <0x1>; /* Interrupt on GPIO25, shared with phy0. */ interrupts = <25 8 /* IRQ_TYPE_LEVEL_LOW */>; - interrupt-parent = <&gpio>; + interrupt-parent = <&gpio0>; /* RX_DV Pad Skew [7:4] = +0.30ns (0xC0) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 + 300 is 720 ps. */ + * ps. 420 + 300 is 720 ps. + */ rxdv-skew-ps = <720>; /* TX_EN Pad Skew [3:0] = -0.30ns (0x02) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 - 300 is 120 ps. */ + * ps. 420 - 300 is 120 ps. + */ txen-skew-ps = <120>; /* RXD 0-3 Pad Skew = +0.30ns (0xC) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 + 300 is 720 ps. */ + * ps. 420 + 300 is 720 ps. + */ rxd0-skew-ps = <720>; rxd1-skew-ps = <720>; rxd2-skew-ps = <720>; @@ -140,7 +145,8 @@ /* TXD 0-3 Pad Skew = -0.30ns (0x2) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 - 300 is 120 ps. */ + * ps. 420 - 300 is 120 ps. + */ txd0-skew-ps = <120>; txd1-skew-ps = <120>; txd2-skew-ps = <120>; @@ -149,11 +155,13 @@ /* Write value to MMD Address 2h, Register 8h */ /* RX_CLK Pad Skew [4:0] = -0.9ns (0x0) */ /* Default is 0b01111, or 15. 15 * 60 ps = 900 ps. 0.90 ns is - * 900 ps. 900 - 900 is 0 ps. */ + * 900 ps. 900 - 900 is 0 ps. + */ rxc-skew-ps = <0>; /* GTX_CLK Pad Skew [9:5] = +0.96ns (0x3E) */ /* Default is 0b01111, or 15. 15 * 60 ps = 900 ps. 0.96 ns is - * 960 ps. 900 + 960 is 1860 ps. */ + * 960 ps. 900 + 960 is 1860 ps. + */ txc-skew-ps = <1860>; }; }; @@ -166,7 +174,7 @@ phy-mode = "rgmii-id"; cdns,no_mdio_bus; emio-speed-gpios = <0>, - <&gpio 55 0>; + <&gpio0 55 0>; }; &sdhci1 { diff --git a/arch/arm/boot/dts/xilinx/ni-7885.dts b/arch/arm/boot/dts/xilinx/ni-7885.dts new file mode 100644 index 0000000000000..5a5450c9c2513 --- /dev/null +++ b/arch/arm/boot/dts/xilinx/ni-7885.dts @@ -0,0 +1,50 @@ +/dts-v1/; +/include/ "ni-zynq.dtsi" + +/ { + model = "NI ELVIS RIO CM"; + compatible = "ni,zynq", "xlnx,zynq-7000"; +}; + +&i2c0 { + nicpld@40 { + watchdogs { + boot-watchdog { + interrupt-parent = <&gpio0>; + interrupts = <24 2 /* IRQ_TYPE_EDGE_FALLING */>; + }; + }; + + leds { + status-1 { + label = "nilrt:status:yellow"; + max-brightness = <0xFFFF>; + }; + }; + }; +}; + +&ni_uart0 { + status = "okay"; + transceiver = "RS-232"; +}; + +&ni_uart1 { + status = "okay"; + transceiver = "RS-232"; +}; + +&usb0 { + status = "okay"; + dr_mode = "host"; +}; + +&usb1 { + status = "okay"; + dr_mode = "peripheral"; +}; + +&clkc { + /* Enable fclk1 for serial. */ + fclk-enable = <0x2>; +}; diff --git a/arch/arm/boot/dts/xilinx/ni-793C.dts b/arch/arm/boot/dts/xilinx/ni-793C.dts new file mode 100644 index 0000000000000..cf6e8d3671622 --- /dev/null +++ b/arch/arm/boot/dts/xilinx/ni-793C.dts @@ -0,0 +1,153 @@ +/dts-v1/; +/include/ "ni-zynq.dtsi" + +/ { + model = "NI ELVIS III"; + compatible = "ni,zynq", "xlnx,zynq-7000"; +}; + +&i2c0 { + nicpld@40 { + watchdogs { + boot-watchdog { + interrupt-parent = <&gpio0>; + interrupts = <1 2 /* IRQ_TYPE_EDGE_FALLING */>; + }; + }; + + leds { + status-0 { + label = "nilrt:status:yellow"; + max-brightness = <0xFFFF>; + }; + eth0-0 { + label = "nilrt:eth0:green"; + linux,default-trigger = + "e000b000.ethernet-ffffffff:00:100Mbps"; + }; + eth0-1 { + label = "nilrt:eth0:yellow"; + linux,default-trigger = + "e000b000.ethernet-ffffffff:00:1Gbps"; + }; + wifi-0 { + label = "nilrt:wifi:primary"; + }; + wifi-1 { + label = "nilrt:wifi:secondary"; + }; + }; + }; + + oleddriver@3C { + compatible = "sinowealth,sh1107"; + reg = <0x3C>; + + oledmodule { + compatible = "truly,oel9m1027"; + }; + }; +}; + +&gem0 { + status = "okay"; + clocks = <&clkc 30>, <&clkc 13>, <&clkc 15>; + clock-names = "pclk", "hclk", "tx_clk"; + phy-handle = <&phy0>; + phy-mode = "rgmii-id"; + emio-speed-gpios = <0>, + <&gpio0 54 0>; + + #address-cells = <0x1>; + #size-cells = <0x0>; + + phy0: phy@0 { + compatible = "micrel,KSZ9031","ethernet-phy-ieee802.3-c22"; + device_type = "ethernet-phy"; + reg = <0x0>; + /* Interrupt on GPIO25, shared with phy1. */ + interrupts = <25 8 /* IRQ_TYPE_LEVEL_LOW */>; + interrupt-parent = <&gpio0>; + + /* RX_DV Pad Skew [7:4] = +0.30ns (0xC0) */ + /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 + * ps. 420 + 300 is 720 ps. + */ + rxdv-skew-ps = <720>; + + /* TX_EN Pad Skew [3:0] = -0.30ns (0x02) */ + /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 + * ps. 420 - 300 is 120 ps. + */ + txen-skew-ps = <120>; + + /* RXD 0-3 Pad Skew = +0.30ns (0xC) */ + /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 + * ps. 420 + 300 is 720 ps. + */ + rxd0-skew-ps = <720>; + rxd1-skew-ps = <720>; + rxd2-skew-ps = <720>; + rxd3-skew-ps = <720>; + + /* TXD 0-3 Pad Skew = -0.30ns (0x2) */ + /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 + * ps. 420 - 300 is 120 ps. + */ + txd0-skew-ps = <120>; + txd1-skew-ps = <120>; + txd2-skew-ps = <120>; + txd3-skew-ps = <120>; + + /* Write value to MMD Address 2h, Register 8h */ + /* RX_CLK Pad Skew [4:0] = -0.9ns (0x0) */ + /* Default is 0b01111, or 15. 15 * 60 ps = 900 ps. 0.90 ns is + * 900 ps. 900 - 900 is 0 ps. + */ + rxc-skew-ps = <0>; + /* GTX_CLK Pad Skew [9:5] = +0.96ns (0x3E) */ + /* Default is 0b01111, or 15. 15 * 60 ps = 900 ps. 0.96 ns is + * 960 ps. 900 + 960 is 1860 ps. + */ + txc-skew-ps = <1860>; + }; +}; + +&sdhci0 { + status = "okay"; + non-removable = <1>; + + wlan0: wlan@0 { + compatible = "atheros,ath6kl"; + status = "okay"; + atheros,board-id = ""; + atheros,region-code = ""; + }; +}; + +&ni_uart0 { + status = "okay"; + transceiver = "RS-232"; + emio-con-dio-multiplex-gpios = <0>, + <&gpio0 56 0>; +}; + +&ni_uart1 { + status = "okay"; + transceiver = "RS-232"; +}; + +&usb0 { + status = "okay"; + dr_mode = "host"; +}; + +&usb1 { + status = "okay"; + dr_mode = "peripheral"; +}; + +&clkc { + /* Enable fclk1 for serial. */ + fclk-enable = <0x2>; +}; diff --git a/arch/arm/boot/dts/xilinx/ni-793x.dts b/arch/arm/boot/dts/xilinx/ni-793x.dtsi similarity index 72% rename from arch/arm/boot/dts/xilinx/ni-793x.dts rename to arch/arm/boot/dts/xilinx/ni-793x.dtsi index 74e373ccad154..e293300c0a5b7 100644 --- a/arch/arm/boot/dts/xilinx/ni-793x.dts +++ b/arch/arm/boot/dts/xilinx/ni-793x.dtsi @@ -1,29 +1,24 @@ -/dts-v1/; /include/ "ni-zynq.dtsi" -/* NIDEVCODE 77AC */ -/* NIDEVCODE 77B2 */ -/* NIDEVCODE 77B1 */ - / { - model = "NI-793x"; compatible = "ni,zynq", "xlnx,zynq-7000"; +}; - amba@0 { - - leds-ni793x@4020F000 { - compatible = "ni,led-793x"; - reg = <0x4020F000 4>; - user1 { - label = "nilrt:user1:green"; - }; +&amba { + leds-ni793x@4020F000 { + compatible = "ni,led-793x"; + reg = <0x4020F000 4>; + user1 { + label = "nilrt:user1:green"; }; + }; +}; - i2c0: i2c@e0004000 { +&i2c0 { nicpld@40 { watchdogs { boot-watchdog { - interrupt-parent = <&gpio>; + interrupt-parent = <&gpio0>; interrupts = <15 2 /* IRQ_TYPE_EDGE_FALLING */>; }; }; @@ -36,12 +31,12 @@ eth0-0 { label = "nilrt:eth0:green"; linux,default-trigger = - "e000b000.etherne:00:100Mb"; + "e000b000.ethernet-ffffffff:00:100Mbps"; }; eth0-1 { label = "nilrt:eth0:yellow"; linux,default-trigger = - "e000b000.etherne:00:Gb"; + "e000b000.ethernet-ffffffff:00:1Gbps"; }; }; }; @@ -49,8 +44,6 @@ ds3231_rtc@68 { status = "okay"; }; - }; - }; }; &gem0 { @@ -58,7 +51,8 @@ /* No fpga_clk specified because we want our FPGA clock * (fclk0) to always be 125 MHz. The bootloader sets - * fclk0 to 125 MHz and we just leave it like that. */ + * fclk0 to 125 MHz and we just leave it like that. + */ phy-handle = <&phy0>; phy-mode = "rgmii-id"; @@ -66,15 +60,15 @@ #size-cells = <0x0>; emio-speed-gpios = <0>, - <&gpio 54 0>; + <&gpio0 54 0>; phy0: phy@0 { - compatible = "micrel,KSZ9031"; + compatible = "micrel,KSZ9031","ethernet-phy-ieee802.3-c22"; device_type = "ethernet-phy"; reg = <0x0>; /* Interrupt on GPIO1. */ interrupts = <1 8 /* IRQ_TYPE_LEVEL_LOW */>; - interrupt-parent = <&gpio>; + interrupt-parent = <&gpio0>; /* Set RX_CLK Pad Skew [4:0] to 0b00000. */ rxc-skew-ps = <0>; diff --git a/arch/arm/boot/dts/xilinx/ni-7AAE.dts b/arch/arm/boot/dts/xilinx/ni-7AAE.dts new file mode 100644 index 0000000000000..16c9ef28b38eb --- /dev/null +++ b/arch/arm/boot/dts/xilinx/ni-7AAE.dts @@ -0,0 +1,203 @@ +/dts-v1/; +/include/ "ni-zynq.dtsi" + +/ { + model = "NI roboRIO 2.0"; + compatible = "ni,zynq", "xlnx,zynq-7000", "ni,artemis"; + + aliases { + spi0 = &spi0; + spi1 = &spi1; + }; + + leds { + compatible = "gpio-leds"; + wlan1 { + /* green */ + label = "nilrt:wifi:primary"; + gpios = <&gpio0 55 0>; + }; + + wlan2 { + /* red */ + label = "nilrt:wifi:secondary"; + gpios = <&gpio0 56 0>; + }; + }; +}; + +&adc { + xlnx,channels { + #address-cells = <1>; + #size-cells = <0>; + /* Channel 0 maps to VPVN */ + channel@0 { + reg = <0>; + }; + /* Channel 1 maps to VAUX0 */ + channel@1 { + reg = <1>; + }; + /* Channel 2 maps to VAUX1 */ + channel@2 { + reg = <2>; + }; + /* Channel 3 maps to VAUX2 */ + channel@3 { + reg = <3>; + }; + /* Channel 4 maps to VAUX3 */ + channel@4 { + reg = <4>; + }; + /* Channel 5 maps to VAUX4 */ + channel@5 { + reg = <5>; + }; + /* Channel 9 maps to VAUX8 */ + channel@9 { + reg = <9>; + }; + /* Channel 10 maps to VAUX9 */ + channel@10 { + reg = <10>; + }; + /* Channel 11 maps to VAUX10 */ + channel@11 { + reg = <11>; + }; + /* Channel 12 maps to VAUX11 */ + channel@12 { + reg = <12>; + }; + }; +}; + +&smcc { + nand@e1000000 { + status = "disabled"; + }; +}; + +&i2c0 { + nicpld@40 { + watchdogs { + boot-watchdog { + interrupt-parent = <&gpio0>; + interrupts = <24 2 /* IRQ_TYPE_EDGE_FALLING */>; + }; + }; + + leds { + status-1 { + label = "nilrt:status:yellow"; + max-brightness = <0xFFFF>; + }; + }; + }; +}; + +&i2c1 { + status = "okay"; +}; + +&i2c2 { + status = "okay"; +}; + +&spi0 { + status = "okay"; + num-cs = <7>; + is-decoded-cs = <1>; + speed-hz = <200000000>; + + spidev@0 { + compatible = "spidev"; + spi-max-frequency = <2000000>; + reg = <0>; + }; + spidev@1 { + compatible = "spidev"; + spi-max-frequency = <2000000>; + reg = <1>; + }; + spidev@2 { + compatible = "spidev"; + spi-max-frequency = <2000000>; + reg = <2>; + }; + spidev@3 { + compatible = "spidev"; + spi-max-frequency = <2000000>; + reg = <3>; + }; +}; + +&spi1 { + status = "okay"; + num-cs = <3>; + is-decoded-cs = <0>; + speed-hz = <200000000>; + + spidev@0 { + compatible = "spidev"; + spi-max-frequency = <2000000>; + reg = <0>; + }; +}; + +&gem0 { + status = "okay"; + phy-handle = <&phy0>; + phy-mode = "rgmii-id"; + emio-speed-gpios = <&gpio0 54 0>, + <0>; + #address-cells = <0x1>; + #size-cells = <0x0>; + + phy0: phy@0 { + compatible = "smsc,lan8720","ethernet-phy-ieee802.3-c22"; + device_type = "ethernet-phy"; + reg = <0x0>; + /* Interrupt pin is connected from PL pin to FPGA IRQ 3 */ + interrupts = <0 32 4>; + smsc,disable-energy-detect; + }; +}; + +&sdhci1 { + status = "okay"; +}; + +&ni_uart0 { + status = "okay"; + transceiver = "RS-232"; +}; + +&ni_uart1 { + status = "okay"; + transceiver = "RS-232"; +}; + +&uart0 { + status = "okay"; +}; + +&can0 { + status = "okay"; +}; + +&usb0 { + status = "okay"; + dr_mode = "host"; +}; + +&usb1 { + status = "okay"; + dr_mode = "peripheral"; +}; + +&clkc { + /* Enable fclk1 for serial. */ + fclk-enable = <0x2>; +}; diff --git a/arch/arm/boot/dts/xilinx/ni-myrio.dts b/arch/arm/boot/dts/xilinx/ni-myrio.dtsi similarity index 70% rename from arch/arm/boot/dts/xilinx/ni-myrio.dts rename to arch/arm/boot/dts/xilinx/ni-myrio.dtsi index cd944323a6cda..5194a062545f1 100644 --- a/arch/arm/boot/dts/xilinx/ni-myrio.dts +++ b/arch/arm/boot/dts/xilinx/ni-myrio.dtsi @@ -1,25 +1,21 @@ -/dts-v1/; /include/ "ni-zynq.dtsi" -/* NIDEVCODE 762F */ - / { - model = "NI myRIO"; compatible = "ni,zynq", "xlnx,zynq-7000"; +}; - amba@0 { - i2c0: i2c@e0004000 { +&i2c0 { nicpld@40 { watchdogs { boot-watchdog { - interrupt-parent = <&gpio>; + interrupt-parent = <&gpio0>; interrupts = <24 2 /* IRQ_TYPE_EDGE_FALLING */>; }; }; switches { wifi-switch { - interrupt-parent = <&gpio>; + interrupt-parent = <&gpio0>; interrupts = <25 2 /* IRQ_TYPE_EDGE_FALLING */>; }; }; @@ -37,8 +33,6 @@ }; }; }; - }; - }; }; &sdhci0 { @@ -47,23 +41,21 @@ /* We must force the SDHCI into test mode, so that it * always pretends to have a card present. This is to * work-around a hardware bug where TiWi WiFi does not - * have its CD line connected correctly. */ - force-sd-cd-test-mode = <1>; + * have its CD line connected correctly. + */ + xlnx,fails-without-test-cd; non-removable = <1>; + #address-cells = <1>; + #size-cells = <0>; + wl12xx: wl12xx@0 { - compatible = "ti,wilink6"; - interrupt-parent = <&gpio>; + compatible = "ti,wl1271"; + reg = <2>; + interrupt-parent = <&gpio0>; interrupts = <15 0x4>; - clocks = <&refclock>; - clock-names = "refclock"; - - refclock: refclock { - compatible = "ti,wilink-clock"; - #clock-cells = <0>; - clock-frequency = <38400000>; - }; + ref-clock-frequency = <38400000>; }; }; diff --git a/arch/arm/boot/dts/xilinx/ni-solbetter.dts b/arch/arm/boot/dts/xilinx/ni-solbetter.dtsi similarity index 80% rename from arch/arm/boot/dts/xilinx/ni-solbetter.dts rename to arch/arm/boot/dts/xilinx/ni-solbetter.dtsi index b8315f0fe7506..a1801ff93774c 100644 --- a/arch/arm/boot/dts/xilinx/ni-solbetter.dts +++ b/arch/arm/boot/dts/xilinx/ni-solbetter.dtsi @@ -1,18 +1,14 @@ -/dts-v1/; /include/ "ni-zynq.dtsi" -/* NIDEVCODE 7744 */ - / { - model = "NI Sol"; compatible = "ni,zynq", "xlnx,zynq-7000"; +}; - amba@0 { - i2c0: i2c@e0004000 { +&i2c0 { nicpld@40 { watchdogs { boot-watchdog { - interrupt-parent = <&gpio>; + interrupt-parent = <&gpio0>; interrupts = <25 2 /* IRQ_TYPE_EDGE_FALLING */>; }; }; @@ -28,22 +24,22 @@ eth0-0 { label = "nilrt:eth0:green"; linux,default-trigger = - "e000b000.etherne:00:100Mb"; + "e000b000.ethernet-ffffffff:00:100Mbps"; }; eth0-1 { label = "nilrt:eth0:yellow"; linux,default-trigger = - "e000b000.etherne:00:Gb"; + "e000b000.ethernet-ffffffff:00:1Gbps"; }; eth1-0 { label = "nilrt:eth1:green"; linux,default-trigger = - "e000b000.etherne:01:100Mb"; + "e000b000.ethernet-ffffffff:01:100Mbps"; }; eth1-1 { label = "nilrt:eth1:yellow"; linux,default-trigger = - "e000b000.etherne:01:Gb"; + "e000b000.ethernet-ffffffff:01:1Gbps"; }; }; }; @@ -51,8 +47,6 @@ ds3231_rtc@68 { status = "okay"; }; - }; - }; }; &gem0 { @@ -63,32 +57,35 @@ phy-mode = "rgmii-id"; emio-speed-gpios = <0>, - <&gpio 54 0>; + <&gpio0 54 0>; #address-cells = <0x1>; #size-cells = <0x0>; phy0: phy@0 { - compatible = "micrel,KSZ9031"; + compatible = "micrel,KSZ9031","ethernet-phy-ieee802.3-c22"; device_type = "ethernet-phy"; reg = <0x0>; /* Interrupt on GPIO1. */ interrupts = <1 8 /* IRQ_TYPE_LEVEL_LOW */>; - interrupt-parent = <&gpio>; + interrupt-parent = <&gpio0>; /* RX_DV Pad Skew [7:4] = +0.30ns (0xC0) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 + 300 is 720 ps. */ + * ps. 420 + 300 is 720 ps. + */ rxdv-skew-ps = <720>; /* TX_EN Pad Skew [3:0] = -0.30ns (0x02) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 - 300 is 120 ps. */ + * ps. 420 - 300 is 120 ps. + */ txen-skew-ps = <120>; /* RXD 0-3 Pad Skew = +0.30ns (0xC) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 + 300 is 720 ps. */ + * ps. 420 + 300 is 720 ps. + */ rxd0-skew-ps = <720>; rxd1-skew-ps = <720>; rxd2-skew-ps = <720>; @@ -96,7 +93,8 @@ /* TXD 0-3 Pad Skew = -0.30ns (0x2) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 - 300 is 120 ps. */ + * ps. 420 - 300 is 120 ps. + */ txd0-skew-ps = <120>; txd1-skew-ps = <120>; txd2-skew-ps = <120>; @@ -105,35 +103,40 @@ /* Write value to MMD Address 2h, Register 8h */ /* RX_CLK Pad Skew [4:0] = -0.9ns (0x0) */ /* Default is 0b01111, or 15. 15 * 60 ps = 900 ps. 0.90 ns is - * 900 ps. 900 - 900 is 0 ps. */ + * 900 ps. 900 - 900 is 0 ps. + */ rxc-skew-ps = <0>; /* GTX_CLK Pad Skew [9:5] = +0.96ns (0x3E) */ /* Default is 0b01111, or 15. 15 * 60 ps = 900 ps. 0.96 ns is - * 960 ps. 900 + 960 is 1860 ps. */ + * 960 ps. 900 + 960 is 1860 ps. + */ txc-skew-ps = <1860>; }; phy1: phy@1 { - compatible = "micrel,KSZ9031"; + compatible = "micrel,KSZ9031","ethernet-phy-ieee802.3-c22"; device_type = "ethernet-phy"; reg = <0x1>; /* Interrupt on GPIO1, shared with phy0. */ interrupts = <1 8 /* IRQ_TYPE_LEVEL_LOW */>; - interrupt-parent = <&gpio>; + interrupt-parent = <&gpio0>; /* RX_DV Pad Skew [7:4] = +0.30ns (0xC0) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 + 300 is 720 ps. */ + * ps. 420 + 300 is 720 ps. + */ rxdv-skew-ps = <720>; /* TX_EN Pad Skew [3:0] = -0.30ns (0x02) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 - 300 is 120 ps. */ + * ps. 420 - 300 is 120 ps. + */ txen-skew-ps = <120>; /* RXD 0-3 Pad Skew = +0.30ns (0xC) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 + 300 is 720 ps. */ + * ps. 420 + 300 is 720 ps. + */ rxd0-skew-ps = <720>; rxd1-skew-ps = <720>; rxd2-skew-ps = <720>; @@ -141,7 +144,8 @@ /* TXD 0-3 Pad Skew = -0.30ns (0x2) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 - 300 is 120 ps. */ + * ps. 420 - 300 is 120 ps. + */ txd0-skew-ps = <120>; txd1-skew-ps = <120>; txd2-skew-ps = <120>; @@ -150,11 +154,13 @@ /* Write value to MMD Address 2h, Register 8h */ /* RX_CLK Pad Skew [4:0] = -0.9ns (0x0) */ /* Default is 0b01111, or 15. 15 * 60 ps = 900 ps. 0.90 ns is - * 900 ps. 900 - 900 is 0 ps. */ + * 900 ps. 900 - 900 is 0 ps. + */ rxc-skew-ps = <0>; /* GTX_CLK Pad Skew [9:5] = +0.96ns (0x3E) */ /* Default is 0b01111, or 15. 15 * 60 ps = 900 ps. 0.96 ns is - * 960 ps. 900 + 960 is 1860 ps. */ + * 960 ps. 900 + 960 is 1860 ps. + */ txc-skew-ps = <1860>; }; }; @@ -167,7 +173,7 @@ phy-mode = "rgmii-id"; cdns,no_mdio_bus; emio-speed-gpios = <0>, - <&gpio 55 0>; + <&gpio0 55 0>; }; &ni_uart0 { diff --git a/arch/arm/boot/dts/xilinx/ni-solenetexp.dts b/arch/arm/boot/dts/xilinx/ni-solenetexp.dtsi similarity index 80% rename from arch/arm/boot/dts/xilinx/ni-solenetexp.dts rename to arch/arm/boot/dts/xilinx/ni-solenetexp.dtsi index a495ca23e8861..b027ea61c9db6 100644 --- a/arch/arm/boot/dts/xilinx/ni-solenetexp.dts +++ b/arch/arm/boot/dts/xilinx/ni-solenetexp.dtsi @@ -1,18 +1,14 @@ -/dts-v1/; /include/ "ni-zynq.dtsi" -/* NIDEVCODE 774E */ - / { - model = "NI Sol"; compatible = "ni,zynq", "xlnx,zynq-7000"; +}; - amba@0 { - i2c0: i2c@e0004000 { +&i2c0 { nicpld@40 { watchdogs { boot-watchdog { - interrupt-parent = <&gpio>; + interrupt-parent = <&gpio0>; interrupts = <25 2 /* IRQ_TYPE_EDGE_FALLING */>; }; }; @@ -25,12 +21,12 @@ eth0-0 { label = "nilrt:eth0:green"; linux,default-trigger = - "e000b000.etherne:00:100Mb"; + "e000b000.ethernet-ffffffff:00:100Mbps"; }; eth0-1 { label = "nilrt:eth0:yellow"; linux,default-trigger = - "e000b000.etherne:00:Gb"; + "e000b000.ethernet-ffffffff:00:1Gbps"; }; }; }; @@ -38,8 +34,6 @@ ds3231_rtc@68 { status = "okay"; }; - }; - }; }; &gem0 { @@ -49,32 +43,35 @@ phy-handle = <&phy0>; phy-mode = "rgmii-id"; emio-speed-gpios = <0>, - <&gpio 54 0>; + <&gpio0 54 0>; #address-cells = <0x1>; #size-cells = <0x0>; phy0: phy@0 { - compatible = "micrel,KSZ9031"; + compatible = "micrel,KSZ9031","ethernet-phy-ieee802.3-c22"; device_type = "ethernet-phy"; reg = <0x0>; /* Interrupt on GPIO1. */ interrupts = <1 8 /* IRQ_TYPE_LEVEL_LOW */>; - interrupt-parent = <&gpio>; + interrupt-parent = <&gpio0>; /* RX_DV Pad Skew [7:4] = +0.30ns (0xC0) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 + 300 is 720 ps. */ + * ps. 420 + 300 is 720 ps. + */ rxdv-skew-ps = <720>; /* TX_EN Pad Skew [3:0] = -0.30ns (0x02) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 - 300 is 120 ps. */ + * ps. 420 - 300 is 120 ps. + */ txen-skew-ps = <120>; /* RXD 0-3 Pad Skew = +0.30ns (0xC) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 + 300 is 720 ps. */ + * ps. 420 + 300 is 720 ps. + */ rxd0-skew-ps = <720>; rxd1-skew-ps = <720>; rxd2-skew-ps = <720>; @@ -82,7 +79,8 @@ /* TXD 0-3 Pad Skew = -0.30ns (0x2) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 - 300 is 120 ps. */ + * ps. 420 - 300 is 120 ps. + */ txd0-skew-ps = <120>; txd1-skew-ps = <120>; txd2-skew-ps = <120>; @@ -91,11 +89,13 @@ /* Write value to MMD Address 2h, Register 8h */ /* RX_CLK Pad Skew [4:0] = -0.9ns (0x0) */ /* Default is 0b01111, or 15. 15 * 60 ps = 900 ps. 0.90 ns is - * 900 ps. 900 - 900 is 0 ps. */ + * 900 ps. 900 - 900 is 0 ps. + */ rxc-skew-ps = <0>; /* GTX_CLK Pad Skew [9:5] = +0.96ns (0x3E) */ /* Default is 0b01111, or 15. 15 * 60 ps = 900 ps. 0.96 ns is - * 960 ps. 900 + 960 is 1860 ps. */ + * 960 ps. 900 + 960 is 1860 ps. + */ txc-skew-ps = <1860>; }; }; diff --git a/arch/arm/boot/dts/xilinx/ni-solgood.dts b/arch/arm/boot/dts/xilinx/ni-solgood.dtsi similarity index 81% rename from arch/arm/boot/dts/xilinx/ni-solgood.dts rename to arch/arm/boot/dts/xilinx/ni-solgood.dtsi index 4b25334247076..c826eb54b8840 100644 --- a/arch/arm/boot/dts/xilinx/ni-solgood.dts +++ b/arch/arm/boot/dts/xilinx/ni-solgood.dtsi @@ -1,18 +1,14 @@ -/dts-v1/; /include/ "ni-zynq.dtsi" -/* NIDEVCODE 7743 */ - / { - model = "NI Sol"; compatible = "ni,zynq", "xlnx,zynq-7000"; +}; - amba@0 { - i2c0: i2c@e0004000 { +&i2c0 { nicpld@40 { watchdogs { boot-watchdog { - interrupt-parent = <&gpio>; + interrupt-parent = <&gpio0>; interrupts = <25 2 /* IRQ_TYPE_EDGE_FALLING */>; }; }; @@ -28,12 +24,12 @@ eth0-0 { label = "nilrt:eth0:green"; linux,default-trigger = - "e000b000.etherne:00:100Mb"; + "e000b000.ethernet-ffffffff:00:100Mbps"; }; eth0-1 { label = "nilrt:eth0:yellow"; linux,default-trigger = - "e000b000.etherne:00:Gb"; + "e000b000.ethernet-ffffffff:00:1Gbps"; }; }; }; @@ -41,8 +37,6 @@ ds3231_rtc@68 { status = "okay"; }; - }; - }; }; &gem0 { @@ -52,32 +46,35 @@ phy-handle = <&phy0>; phy-mode = "rgmii-id"; emio-speed-gpios = <0>, - <&gpio 54 0>; + <&gpio0 54 0>; #address-cells = <0x1>; #size-cells = <0x0>; phy0: phy@0 { - compatible = "micrel,KSZ9031"; + compatible = "micrel,KSZ9031","ethernet-phy-ieee802.3-c22"; device_type = "ethernet-phy"; reg = <0x0>; /* Interrupt on GPIO1. */ interrupts = <1 8 /* IRQ_TYPE_LEVEL_LOW */>; - interrupt-parent = <&gpio>; + interrupt-parent = <&gpio0>; /* RX_DV Pad Skew [7:4] = +0.30ns (0xC0) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 + 300 is 720 ps. */ + * ps. 420 + 300 is 720 ps. + */ rxdv-skew-ps = <720>; /* TX_EN Pad Skew [3:0] = -0.30ns (0x02) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 - 300 is 120 ps. */ + * ps. 420 - 300 is 120 ps. + */ txen-skew-ps = <120>; /* RXD 0-3 Pad Skew = +0.30ns (0xC) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 + 300 is 720 ps. */ + * ps. 420 + 300 is 720 ps. + */ rxd0-skew-ps = <720>; rxd1-skew-ps = <720>; rxd2-skew-ps = <720>; @@ -85,7 +82,8 @@ /* TXD 0-3 Pad Skew = -0.30ns (0x2) */ /* Default is 0b0111, or 7. 7 * 60 ps = 420 ps. 0.30 ns is 300 - * ps. 420 - 300 is 120 ps. */ + * ps. 420 - 300 is 120 ps. + */ txd0-skew-ps = <120>; txd1-skew-ps = <120>; txd2-skew-ps = <120>; @@ -94,11 +92,13 @@ /* Write value to MMD Address 2h, Register 8h */ /* RX_CLK Pad Skew [4:0] = -0.9ns (0x0) */ /* Default is 0b01111, or 15. 15 * 60 ps = 900 ps. 0.90 ns is - * 900 ps. 900 - 900 is 0 ps. */ + * 900 ps. 900 - 900 is 0 ps. + */ rxc-skew-ps = <0>; /* GTX_CLK Pad Skew [9:5] = +0.96ns (0x3E) */ /* Default is 0b01111, or 15. 15 * 60 ps = 900 ps. 0.96 ns is - * 960 ps. 900 + 960 is 1860 ps. */ + * 960 ps. 900 + 960 is 1860 ps. + */ txc-skew-ps = <1860>; }; }; diff --git a/arch/arm/boot/dts/xilinx/ni-zynq.dtsi b/arch/arm/boot/dts/xilinx/ni-zynq.dtsi index d694aedc9c5d6..f8a4850c093e1 100644 --- a/arch/arm/boot/dts/xilinx/ni-zynq.dtsi +++ b/arch/arm/boot/dts/xilinx/ni-zynq.dtsi @@ -156,6 +156,8 @@ &nfc0 { status = "okay"; + #address-cells = <1>; + #size-cells = <0>; nand@0 { reg = <0>; @@ -170,3 +172,34 @@ arm,dynamic-clock-gating = <0>; arm,standby-mode = <0>; }; + +/* + * The CoreSight debug/trace macrocells are not accessible on NI Zynq-based + * targets: reading their AMBA peripheral-ID registers (done unconditionally + * by the kernel as soon as the device is created, before any driver binds) + * hangs the SoC indefinitely. Disable them so of_platform_populate() never + * touches them. + */ +&{/replicator} { + status = "disabled"; +}; + +&{/axi/etb@f8801000} { + status = "disabled"; +}; + +&{/axi/tpiu@f8803000} { + status = "disabled"; +}; + +&{/axi/funnel@f8804000} { + status = "disabled"; +}; + +&{/axi/ptm@f889c000} { + status = "disabled"; +}; + +&{/axi/ptm@f889d000} { + status = "disabled"; +}; diff --git a/arch/arm/configs/nati_bluefin_defconfig b/arch/arm/configs/nati_bluefin_defconfig index 5ab4320d36ebb..fb049f1d8af57 100644 --- a/arch/arm/configs/nati_bluefin_defconfig +++ b/arch/arm/configs/nati_bluefin_defconfig @@ -209,3 +209,4 @@ CONFIG_DYNAMIC_DEBUG=y CONFIG_FRAME_WARN=1024 CONFIG_MAGIC_SYSRQ=y CONFIG_RCU_CPU_STALL_TIMEOUT=60 +CONFIG_RCU_NOCB_CPU_DEFAULT_ALL=y diff --git a/arch/arm/configs/nati_slsc_defconfig b/arch/arm/configs/nati_slsc_defconfig index 34d474f347025..d90943e290e35 100644 --- a/arch/arm/configs/nati_slsc_defconfig +++ b/arch/arm/configs/nati_slsc_defconfig @@ -223,3 +223,4 @@ CONFIG_DYNAMIC_DEBUG=y CONFIG_FRAME_WARN=1024 CONFIG_MAGIC_SYSRQ=y CONFIG_RCU_CPU_STALL_TIMEOUT=60 +CONFIG_RCU_NOCB_CPU_DEFAULT_ALL=y diff --git a/arch/arm/configs/nati_zynq_defconfig b/arch/arm/configs/nati_zynq_defconfig index 9f1004d03889a..40d1de56e1fde 100644 --- a/arch/arm/configs/nati_zynq_defconfig +++ b/arch/arm/configs/nati_zynq_defconfig @@ -1,7 +1,13 @@ CONFIG_LOCALVERSION="-ni" CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y CONFIG_AUDIT=y CONFIG_HIGH_RES_TIMERS=y +CONFIG_BPF_JIT=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_RCU_EXPERT=y +CONFIG_RCU_NOCB_CPU=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 @@ -10,13 +16,15 @@ CONFIG_CPUSETS=y CONFIG_CGROUP_CPUACCT=y CONFIG_BLK_DEV_INITRD=y CONFIG_CC_OPTIMIZE_FOR_SIZE=y -CONFIG_SYSFS_SYSCALL=y +CONFIG_EXPERT=y CONFIG_PERF_EVENTS=y +CONFIG_ARCH_VEXPRESS=y CONFIG_ARCH_ZYNQ=y CONFIG_ARM_ERRATA_754322=y CONFIG_ARM_ERRATA_764369=y CONFIG_ARM_ERRATA_775420=y CONFIG_SMP=y +CONFIG_PREEMPT_RT=y CONFIG_HIGHMEM=y CONFIG_CMDLINE="console=ttyPS0,115200n8 root=/dev/ram rw initrd=0x00800000,16M earlyprintk mtdparts=physmap-flash.0:512K(nor-fsbl),512K(nor-u-boot),5M(nor-linux),9M(nor-user),1M(nor-scratch),-(nor-rootfs)" CONFIG_VFP=y @@ -24,14 +32,14 @@ CONFIG_NEON=y # CONFIG_SUSPEND is not set CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y -CONFIG_MODULE_FORCE_UNLOAD=y CONFIG_MODVERSIONS=y -# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +# CONFIG_COMPAT_BRK is not set # CONFIG_COMPACTION is not set -CONFIG_CMA=y CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y +CONFIG_XFRM_USER=m +CONFIG_NET_KEY=m CONFIG_INET=y CONFIG_IP_MULTICAST=y CONFIG_IP_ADVANCED_ROUTER=y @@ -53,9 +61,11 @@ CONFIG_IP_PIMSM_V2=y CONFIG_INET_AH=m CONFIG_INET_ESP=m CONFIG_INET_IPCOMP=m +CONFIG_IPV6=m CONFIG_INET6_AH=m CONFIG_INET6_ESP=m CONFIG_INET6_IPCOMP=m +CONFIG_IPV6_MULTIPLE_TABLES=y CONFIG_NETFILTER=y CONFIG_NF_CONNTRACK=m CONFIG_NF_CONNTRACK_EVENTS=y @@ -74,6 +84,7 @@ CONFIG_NF_CT_NETLINK=m CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m CONFIG_NETFILTER_XT_TARGET_CONNMARK=m CONFIG_NETFILTER_XT_TARGET_IDLETIMER=m +CONFIG_NETFILTER_XT_TARGET_LOG=m CONFIG_NETFILTER_XT_TARGET_MARK=m CONFIG_NETFILTER_XT_TARGET_NFLOG=m CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m @@ -107,19 +118,17 @@ CONFIG_NETFILTER_XT_MATCH_QUOTA=m CONFIG_NETFILTER_XT_MATCH_RATEEST=m CONFIG_NETFILTER_XT_MATCH_REALM=m CONFIG_NETFILTER_XT_MATCH_RECENT=m -CONFIG_NETFILTER_XT_MATCH_SCTP=m CONFIG_NETFILTER_XT_MATCH_STATE=m CONFIG_NETFILTER_XT_MATCH_STATISTIC=m CONFIG_NETFILTER_XT_MATCH_STRING=m CONFIG_NETFILTER_XT_MATCH_TCPMSS=m CONFIG_NETFILTER_XT_MATCH_TIME=m CONFIG_NETFILTER_XT_MATCH_U32=m -CONFIG_NF_REJECT_IPV4=m +CONFIG_NETFILTER_XT_TARGET_LED=m CONFIG_IP_NF_IPTABLES=m CONFIG_IP_NF_MATCH_AH=m CONFIG_IP_NF_MATCH_ECN=m CONFIG_IP_NF_MATCH_TTL=m -CONFIG_NF_REJECT_IPV6=m CONFIG_IP6_NF_IPTABLES=m CONFIG_IP6_NF_MATCH_AH=m CONFIG_IP6_NF_MATCH_EUI64=m @@ -129,6 +138,8 @@ CONFIG_IP6_NF_MATCH_HL=m CONFIG_IP6_NF_MATCH_IPV6HEADER=m CONFIG_IP6_NF_MATCH_MH=m CONFIG_IP6_NF_MATCH_RT=m +CONFIG_IP_SCTP=m +CONFIG_SCTP_DEFAULT_COOKIE_HMAC_NONE=y CONFIG_L2TP=m CONFIG_L2TP_V3=y CONFIG_L2TP_IP=m @@ -136,6 +147,7 @@ CONFIG_L2TP_ETH=m CONFIG_BRIDGE=m CONFIG_VLAN_8021Q=m CONFIG_NET_SCHED=y +CONFIG_NET_SCH_NETEM=m CONFIG_CAN=y CONFIG_BT=m CONFIG_BT_HCIBTUSB=m @@ -149,6 +161,7 @@ CONFIG_DEVTMPFS_MOUNT=y CONFIG_CONNECTOR=y CONFIG_MTD=y CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_RESERVE_END=524288 CONFIG_MTD_BLOCK=y CONFIG_MTD_CFI=y CONFIG_MTD_CFI_AMDSTD=y @@ -158,18 +171,28 @@ CONFIG_MTD_RAW_NAND=y CONFIG_MTD_NAND_PL35X=y CONFIG_MTD_UBI=y CONFIG_MTD_UBI_GLUEBI=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_NI793X=y +CONFIG_NIZYNQ_CPLD=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=16384 +CONFIG_FPGA_PERIPHERAL=y CONFIG_SRAM=y CONFIG_EEPROM_AT24=y CONFIG_EEPROM_AT25=y -CONFIG_EEPROM_93CX6=m CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y CONFIG_CHR_DEV_SG=y # CONFIG_BLK_DEV_BSG is not set +CONFIG_MD=y +CONFIG_BLK_DEV_DM=m +CONFIG_DM_CRYPT=m CONFIG_NETDEVICES=y +CONFIG_BONDING=m CONFIG_TUN=y CONFIG_MACB=y # CONFIG_NET_VENDOR_CIRRUS is not set @@ -182,7 +205,7 @@ CONFIG_MACB=y # CONFIG_NET_VENDOR_QUALCOMM is not set # CONFIG_NET_VENDOR_SAMSUNG is not set # CONFIG_NET_VENDOR_SEEQ is not set -# CONFIG_NET_VENDOR_SMSC is not set +CONFIG_SMSC911X=m # CONFIG_NET_VENDOR_STMICRO is not set # CONFIG_NET_VENDOR_WIZNET is not set # CONFIG_NET_VENDOR_XILINX is not set @@ -190,6 +213,7 @@ CONFIG_MARVELL_PHY=y CONFIG_MICREL_PHY=y CONFIG_SMSC_PHY=y CONFIG_VITESSE_PHY=y +CONFIG_LED_TRIGGER_PHY=y CONFIG_MDIO_BITBANG=y CONFIG_PPP=m CONFIG_PPP_BSDCOMP=m @@ -197,6 +221,10 @@ CONFIG_PPP_DEFLATE=m CONFIG_PPP_ASYNC=m CONFIG_PPP_SYNC_TTY=m CONFIG_USB_NET_DRIVERS=m +CONFIG_USB_CATC=m +CONFIG_USB_KAWETH=m +CONFIG_USB_PEGASUS=m +CONFIG_USB_RTL8150=m CONFIG_USB_RTL8152=m CONFIG_USB_USBNET=m CONFIG_USB_NET_CDC_EEM=m @@ -205,7 +233,18 @@ CONFIG_USB_NET_SMSC95XX=m CONFIG_USB_NET_PLUSB=m CONFIG_USB_NET_RNDIS_HOST=m CONFIG_USB_IPHETH=m +CONFIG_USB_SIERRA_NET=m +CONFIG_USB_VL600=m +CONFIG_USB_NET_CH9200=m CONFIG_ATH9K=m +CONFIG_CARL9170=m +CONFIG_ATH6KL=m +CONFIG_ATH6KL_SDIO=m +CONFIG_ATH6KL_USB=m +CONFIG_ATH6KL_SILEX_FIRMWARE=y +CONFIG_AR5523=m +CONFIG_AT76C50X_USB=m +CONFIG_MT7601U=m CONFIG_RT2X00=m CONFIG_RT2500USB=m CONFIG_RT73USB=m @@ -213,55 +252,76 @@ CONFIG_RT2800USB=m CONFIG_RT2800USB_RT53XX=y CONFIG_RT2800USB_RT55XX=y CONFIG_RT2800USB_UNKNOWN=y +CONFIG_RTL8187=m CONFIG_RTL8192CU=m CONFIG_WL12XX=m CONFIG_WLCORE_SDIO=m +CONFIG_ZD1211RW=m CONFIG_INPUT_SPARSEKMAP=y CONFIG_INPUT_EVDEV=y +# CONFIG_LEGACY_PTYS is not set CONFIG_SERIAL_8250=y # CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set CONFIG_SERIAL_8250_CONSOLE=y CONFIG_SERIAL_8250_NR_UARTS=9 +CONFIG_SERIAL_8250_NI=y CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_AMBA_PL011=y +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y CONFIG_SERIAL_XILINX_PS_UART=y CONFIG_SERIAL_XILINX_PS_UART_CONSOLE=y # CONFIG_HW_RANDOM is not set +CONFIG_I2C=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_CADENCE=y CONFIG_I2C_XILINX=y CONFIG_SPI=y CONFIG_SPI_CADENCE=y CONFIG_SPI_SPIDEV=y -CONFIG_GPIOLIB=y +CONFIG_GPIO_SYSFS=y CONFIG_GPIO_ZYNQ=y CONFIG_MEDIA_SUPPORT=y CONFIG_MEDIA_CAMERA_SUPPORT=y CONFIG_MEDIA_USB_SUPPORT=y CONFIG_USB_VIDEO_CLASS=m +CONFIG_AUXDISPLAY=y +CONFIG_SH1107=m +CONFIG_OEL9M1027=m CONFIG_FB=y -CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_HIDRAW=y CONFIG_HID_MICROSOFT=m CONFIG_USB_HID=m +CONFIG_USB_HIDDEV=y CONFIG_USB=y CONFIG_USB_DYNAMIC_MINORS=y CONFIG_USB_EHCI_HCD=y -CONFIG_USB_ACM=m CONFIG_USB_STORAGE=m -CONFIG_USB_CHIPIDEA=y +CONFIG_USB_CHIPIDEA=m CONFIG_USB_CHIPIDEA_UDC=y CONFIG_USB_CHIPIDEA_HOST=y -CONFIG_USB_SERIAL=m +CONFIG_USB_SERIAL=y +CONFIG_USB_SERIAL_CONSOLE=y +CONFIG_USB_SERIAL_CH341=m +CONFIG_USB_SERIAL_WHITEHEAT=m CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m CONFIG_USB_SERIAL_CP210X=m CONFIG_USB_SERIAL_FTDI_SIO=m +CONFIG_USB_SERIAL_MOS7720=m +CONFIG_USB_SERIAL_MOS7840=m CONFIG_USB_SERIAL_PL2303=m -CONFIG_NOP_USB_XCEIV=y +CONFIG_USB_SERIAL_QUALCOMM=m +CONFIG_USB_SERIAL_SIERRAWIRELESS=m +CONFIG_NOP_USB_XCEIV=m CONFIG_USB_ULPI=y -CONFIG_USB_GADGET=y -CONFIG_USB_ETH=y +CONFIG_USB_GADGET=m +CONFIG_USB_CONFIGFS=m +CONFIG_USB_CONFIGFS_EEM=y +CONFIG_USB_CONFIGFS_MASS_STORAGE=y +CONFIG_USB_ETH=m # CONFIG_USB_ETH_RNDIS is not set CONFIG_USB_ETH_EEM=y CONFIG_MMC=y +CONFIG_MMC_ARMMMCI=y CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_PLTFM=y CONFIG_MMC_SDHCI_OF_ARASAN=y @@ -271,23 +331,30 @@ CONFIG_RTC_DRV_DS3232=y CONFIG_DMADEVICES=y CONFIG_PL330_DMA=y CONFIG_COMMON_CLK_SI570=y -CONFIG_ARM_GT_INITIAL_PRESCALER_VAL=2 CONFIG_MEMORY=y CONFIG_EXT2_FS=y CONFIG_EXT2_FS_XATTR=y CONFIG_EXT2_FS_SECURITY=y CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y # CONFIG_DNOTIFY is not set +CONFIG_FUSE_FS=m CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y +CONFIG_NTFS_FS=m CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y +CONFIG_CONFIGFS_FS=y CONFIG_UBIFS_FS=y CONFIG_SQUASHFS=m CONFIG_SQUASHFS_XATTR=y -CONFIG_NFS_FS=y -CONFIG_ROOT_NFS=y +CONFIG_NFS_FS=m +CONFIG_NFS_V4=m +CONFIG_NFS_V4_1=y CONFIG_CIFS=y +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ASCII=y CONFIG_NLS_ISO8859_1=y @@ -297,17 +364,13 @@ CONFIG_SECURITY_SELINUX=y CONFIG_SECURITY_SELINUX_BOOTPARAM=y # CONFIG_INTEGRITY is not set CONFIG_DEFAULT_SECURITY_DAC=y -CONFIG_CRYPTO_NULL=y -CONFIG_CRYPTO_HMAC=y -CONFIG_CRYPTO_MD5=y -CONFIG_CRYPTO_SHA256=y -CONFIG_CRYPTO_SHA512=y -CONFIG_CRYPTO_CRC32C=y -CONFIG_FONTS=y -CONFIG_FONT_8x8=y -CONFIG_FONT_8x16=y +CONFIG_CRYPTO_PCRYPT=m +CONFIG_CRYPTO_XTS=m +CONFIG_CRYPTO_USER_API_SKCIPHER=m CONFIG_PRINTK_TIME=y CONFIG_DYNAMIC_DEBUG=y -CONFIG_FRAME_WARN=1024 CONFIG_MAGIC_SYSRQ=y +CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x0 +CONFIG_PANIC_TIMEOUT=30 CONFIG_RCU_CPU_STALL_TIMEOUT=60 +CONFIG_RCU_NOCB_CPU_DEFAULT_ALL=y diff --git a/arch/arm/mach-zynq/common.c b/arch/arm/mach-zynq/common.c index 15e8a321a713b..9848f69734c15 100644 --- a/arch/arm/mach-zynq/common.c +++ b/arch/arm/mach-zynq/common.c @@ -152,6 +152,13 @@ static struct map_desc zynq_cortex_a9_scu_map __initdata = { .type = MT_DEVICE, }; +static struct map_desc pl353_space __initdata = { + .length = SZ_16M, + .type = MT_DEVICE, + .pfn = __phys_to_pfn(0xE1000000), + .virtual = 0xE1000000, +}; + static void __init zynq_scu_map_io(void) { unsigned long base; @@ -163,6 +170,8 @@ static void __init zynq_scu_map_io(void) iotable_init(&zynq_cortex_a9_scu_map, 1); zynq_scu_base = (void __iomem *)base; BUG_ON(!zynq_scu_base); + + iotable_init(&pl353_space, 1); } /** diff --git a/arch/arm/tools/syscall.tbl b/arch/arm/tools/syscall.tbl index b07e699aaa3c2..a176b55d1dc3e 100644 --- a/arch/arm/tools/syscall.tbl +++ b/arch/arm/tools/syscall.tbl @@ -484,3 +484,4 @@ 467 common open_tree_attr sys_open_tree_attr 468 common file_getattr sys_file_getattr 469 common file_setattr sys_file_setattr +470 common mcopy sys_mcopy diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig index bedc6133f970a..dc372cd227ad4 100644 --- a/drivers/auxdisplay/Kconfig +++ b/drivers/auxdisplay/Kconfig @@ -422,6 +422,19 @@ config KS0108_DELAY If you compile this as a module, you can still override this value using the module parameters. +config SH1107 + tristate "SH1107 LCD Controller" + depends on I2C + help + If you have a LCD controlled by one or more sh1107 + controller, say Y. You will need also another more specific + driver for your LCD. + + To compile this as a module, choose M here: + the module will be called sh1107. + + If unsure, say N. + config CFAG12864B tristate "CFAG12864B LCD" depends on X86 @@ -500,6 +513,30 @@ config HT16K33 Say yes here to add support for Holtek HT16K33, RAM mapping 16*8 LED controller driver with keyscan. +config OEL9M1027 + tristate "OEL9M1027 LCD" + depends on FB + depends on SH1107 + select FB_SYS_FILLRECT + select FB_SYS_COPYAREA + select FB_SYS_IMAGEBLIT + select FB_SYSMEM_FOPS + default n + help + If you have a Truly 64x128 2-color LCD, OEL9M1027 Series, + say Y. You also need the sh1107 LCD Controller driver. + + Depends on the framebuffer support. + + The LCD framebuffer driver can be attached to a console. + It will work fine. However, you can't attach it to the fbdev driver + of the xorg server. + + To compile this as a module, choose M here: + the modules will be called oel9m1027 and oel9m1027fb. + + If unsure, say N. + config MAX6959 tristate "Maxim MAX6958/6959 7-segment LED controller" depends on I2C diff --git a/drivers/auxdisplay/Makefile b/drivers/auxdisplay/Makefile index f5c13ed1cd4f5..c3e12065a2176 100644 --- a/drivers/auxdisplay/Makefile +++ b/drivers/auxdisplay/Makefile @@ -16,3 +16,6 @@ obj-$(CONFIG_LINEDISP) += line-display.o obj-$(CONFIG_MAX6959) += max6959.o obj-$(CONFIG_PARPORT_PANEL) += panel.o obj-$(CONFIG_SEG_LED_GPIO) += seg-led-gpio.o +obj-$(CONFIG_SH1107) += sh1107.o +obj-$(CONFIG_OEL9M1027) += oel9m1027.o +oel9m1027-y += oel9m1027_core.o oel9m1027_fb.o diff --git a/drivers/auxdisplay/oel9m1027.h b/drivers/auxdisplay/oel9m1027.h new file mode 100644 index 0000000000000..ba8fa8caacb0e --- /dev/null +++ b/drivers/auxdisplay/oel9m1027.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2017 National Instruments Corp. + * + * Based on cfag12864b driver (v0.1.0) + * Copyright (C) 2006 by Miguel Ojeda Sandonis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _OEL9M1027_H_ +#define _OEL9M1027_H_ + +#define OEL9M1027_WIDTH (64) +#define OEL9M1027_HEIGHT (128) + +#define OEL9M1027_PAGES (16) +#define OEL9M1027_ADDRESSES (64) +#define OEL9M1027_ADDRESSES_OFFSET (32) +#define OEL9M1027_SIZE ((OEL9M1027_PAGES) * \ + (OEL9M1027_ADDRESSES)) + +#define OEL9M1027_DISPLAYON 1 +#define OEL9M1027_DISPLAYOFF 0 +#define OEL9M1027_DISPNORMAL 0 +#define OEL9M1027_DISPINVERT 1 + +/* + * Default parameter that recommended by Truly manufacturer. + * + * Don't change if you unsure. This value is sufficient by + * default. + */ +#define OEL9M1027_DEF_MULTIRATIO 0x3F +#define OEL9M1027_DEF_DISPFREQ 0xF1 +#define OEL9M1027_DEF_SCANDIR 0x00 +#define OEL9M1027_DEF_DISPOFFSET 0x60 +#define OEL9M1027_DEF_STARTLINE 0x20 +#define OEL9M1027_DEF_ADDRMODE 0x00 +#define OEL9M1027_DEF_CONTRAST 0xFF +#define OEL9M1027_DEF_SEGREMAP 0x00 +#define OEL9M1027_DEF_DCCONTROL 0x0A +#define OEL9M1027_DEF_PHASEPERIOD 0x22 +#define OEL9M1027_DEF_VCOMCONTROL 0x35 + +/* + * OEL9M1027 device private data + */ +struct oel9m1027 { + struct device *dev; /* oel9m1027 device */ + struct sh1107 *sh; /* sh1107 privdata */ + + struct fb_info *fb; /* oel9m1027 framebuffer info */ + unsigned char *framebuffer; + + unsigned char contrast; /* oled display contrast */ + + struct delayed_work dwork; /* oled update worker */ + struct mutex lock; +}; + +/* + * Register OEL9M1027 framebuffer device + * + * Returns 0 if registered successfully, + * or != 0 if failed to register. + */ +int oel9m1027fb_init(struct oel9m1027 *oled); + +/* + * Remove OEL9M1027 framebuffer device + */ +void oel9m1027fb_exit(struct oel9m1027 *oled); + +#endif /* _OEL9M1027_H_ */ + diff --git a/drivers/auxdisplay/oel9m1027_core.c b/drivers/auxdisplay/oel9m1027_core.c new file mode 100644 index 0000000000000..4f30c320a89e9 --- /dev/null +++ b/drivers/auxdisplay/oel9m1027_core.c @@ -0,0 +1,323 @@ +/* + * Copyright (C) 2017 National Instruments Corp. + * + * Based on cfag12864b driver (v0.1.0) + * Copyright (C) 2006 by Miguel Ojeda Sandonis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sh1107.h" +#include "oel9m1027.h" + +/* + * OEL9M1027 Internal Commands + */ + +static int oel9m1027_clear(struct oel9m1027 *oled) +{ + unsigned char i, j; + int ret; + + mutex_lock(&oled->lock); + + for (i = 0; i < OEL9M1027_PAGES; i++) { + ret = sh1107_page(oled->sh, i); + if (ret) + goto fail; + + ret = sh1107_address(oled->sh, OEL9M1027_ADDRESSES_OFFSET); + if (ret) + goto fail; + + for (j = 0; j < OEL9M1027_ADDRESSES; j++) { + ret = sh1107_writedata(oled->sh, 0); + if (ret) + goto fail; + } + } + +fail: + mutex_unlock(&oled->lock); + return ret; +} + +static int oel9m1027_off(struct oel9m1027 *oled) +{ + int ret; + + mutex_lock(&oled->lock); + ret = sh1107_displaystate(oled->sh, OEL9M1027_DISPLAYOFF); + mutex_unlock(&oled->lock); + + return ret; +} + +static int oel9m1027_on(struct oel9m1027 *oled) +{ + int ret; + + mutex_lock(&oled->lock); + + ret = sh1107_multiplexratio(oled->sh, OEL9M1027_DEF_MULTIRATIO); + if (ret) + goto fail; + + ret = sh1107_displayfreq(oled->sh, OEL9M1027_DEF_DISPFREQ); + if (ret) + goto fail; + + ret = sh1107_scandir(oled->sh, OEL9M1027_DEF_SCANDIR); + if (ret) + goto fail; + + ret = sh1107_offset(oled->sh, OEL9M1027_DEF_DISPOFFSET); + if (ret) + goto fail; + + ret = sh1107_startline(oled->sh, OEL9M1027_DEF_STARTLINE); + if (ret) + goto fail; + + ret = sh1107_addressingmode(oled->sh, OEL9M1027_DEF_ADDRMODE); + if (ret) + goto fail; + + ret = sh1107_displaycontrast(oled->sh, oled->contrast); + if (ret) + goto fail; + + ret = sh1107_segremap(oled->sh, OEL9M1027_DEF_SEGREMAP); + if (ret) + goto fail; + + ret = sh1107_entiredisplaystate(oled->sh, OEL9M1027_DISPLAYOFF); + if (ret) + goto fail; + + ret = sh1107_displayinvert(oled->sh, OEL9M1027_DISPNORMAL); + if (ret) + goto fail; + + ret = sh1107_dccontrol(oled->sh, OEL9M1027_DEF_DCCONTROL); + if (ret) + goto fail; + + ret = sh1107_phaseperiod(oled->sh, OEL9M1027_DEF_PHASEPERIOD); + if (ret) + goto fail; + + ret = sh1107_vcomcontrol(oled->sh, OEL9M1027_DEF_VCOMCONTROL); + if (ret) + goto fail; + + ret = sh1107_displaystate(oled->sh, OEL9M1027_DISPLAYON); + if (ret) + goto fail; + +fail: + mutex_unlock(&oled->lock); + return ret; +} + +static void oel9m1027_update(struct work_struct *work) +{ + struct oel9m1027 *oled = container_of(to_delayed_work(work), + struct oel9m1027, + dwork); + unsigned short page, addr, bit; + unsigned char c_buff; + unsigned int pos; + int ret; + + mutex_lock(&oled->lock); + + for (page = 0; page < OEL9M1027_PAGES; page++) { + ret = sh1107_page(oled->sh, page); + if (ret) + goto fail; + + ret = sh1107_address(oled->sh, + OEL9M1027_ADDRESSES_OFFSET); + if (ret) + goto fail; + + for (addr = 0; addr < OEL9M1027_ADDRESSES; addr++) { + for (c_buff = 0, bit = 0; bit < 8; bit++) { + pos = addr / 8 + (page * 8 + bit) * + OEL9M1027_WIDTH / 8; + + if (oled->framebuffer[pos] & BIT(addr % 8)) + c_buff |= BIT(bit); + } + + ret = sh1107_writedata(oled->sh, c_buff); + if (ret) + goto fail; + } + } + + mutex_unlock(&oled->lock); + return; + +fail: + mutex_unlock(&oled->lock); + + /* Error occur, retry after 100ms */ + dev_warn(oled->dev, "ERROR: OLED update fail.\n"); + queue_delayed_work(system_wq, &oled->dwork, HZ / 10); +} + +static ssize_t oel9m1027_contrast_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct oel9m1027 *oled = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", oled->contrast); +} + +static ssize_t oel9m1027_contrast_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct oel9m1027 *oled = dev_get_drvdata(dev); + int ret; + + ret = kstrtou8(buf, 10, &oled->contrast); + if (ret) + return -EINVAL; + + /* Restart for new contrast setting to take effect */ + ret = oel9m1027_off(oled); + if (ret) + return ret; + + ret = oel9m1027_on(oled); + + return ret ? ret : count; +} + +static DEVICE_ATTR(contrast, 0644, oel9m1027_contrast_show, + oel9m1027_contrast_store); + +static int oel9m1027_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct oel9m1027 *oled; + int ret; + + oled = devm_kzalloc(dev, sizeof(*oled), GFP_KERNEL); + if (!oled) { + ret = -ENOMEM; + goto none; + } + + platform_set_drvdata(pdev, oled); + oled->dev = dev; + oled->sh = dev_get_drvdata(dev->parent); + mutex_init(&oled->lock); + oled->contrast = OEL9M1027_DEF_CONTRAST; + + ret = device_create_file(dev, &dev_attr_contrast); + if (ret) { + dev_warn(dev, "ERROR: OLED sysfs create fail.\n"); + goto mutexcreated; + } + + ret = oel9m1027_off(oled); + if (ret) { + dev_warn(dev, "ERROR: OLED switch off fail.\n"); + goto mutexcreated; + } + + ret = oel9m1027_clear(oled); + if (ret) { + dev_warn(dev, "ERROR: OLED buffer clear fail.\n"); + goto mutexcreated; + } + + ret = oel9m1027_on(oled); + if (ret) { + dev_err(dev, "ERROR: OLED switch on fail.\n"); + goto mutexcreated; + } + + INIT_DELAYED_WORK(&oled->dwork, oel9m1027_update); + + ret = oel9m1027fb_init(oled); + if (ret) { + dev_err(dev, "ERROR: can't initialize framebuffer\n"); + goto oledturnedon; + } + + return 0; + +oledturnedon: + oel9m1027_off(oled); + +mutexcreated: + mutex_destroy(&oled->lock); + +none: + return ret; +} + +static void oel9m1027_remove(struct platform_device *pdev) +{ + struct oel9m1027 *oled = platform_get_drvdata(pdev); + + oel9m1027fb_exit(oled); + flush_delayed_work(&oled->dwork); + cancel_delayed_work_sync(&oled->dwork); + oel9m1027_off(oled); + mutex_destroy(&oled->lock); +} + +static const struct of_device_id oel9m1027_of_match[] = { + { .compatible = "truly,oel9m1027" }, + { }, +}; +MODULE_DEVICE_TABLE(of, oel9m1027_of_match); + +static struct platform_driver oel9m1027_driver = { + .probe = oel9m1027_probe, + .remove = oel9m1027_remove, + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = oel9m1027_of_match, + }, +}; + +module_platform_driver(oel9m1027_driver); + +MODULE_DESCRIPTION("OEL9M1027 OLED driver"); +MODULE_AUTHOR("Wilson Lee "); +MODULE_LICENSE("GPL"); diff --git a/drivers/auxdisplay/oel9m1027_fb.c b/drivers/auxdisplay/oel9m1027_fb.c new file mode 100644 index 0000000000000..76f7bd5922e53 --- /dev/null +++ b/drivers/auxdisplay/oel9m1027_fb.c @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2017 National Instruments Corp. + * + * Based on cfag12864b driver (v0.1.0) + * Copyright (C) 2006 by Miguel Ojeda Sandonis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "oel9m1027.h" + +static ssize_t oel9m1027fb_write(struct fb_info *fb, + const char __user *buf, + size_t count, loff_t *ppos) +{ + struct oel9m1027 *oled = dev_get_drvdata(fb->device); + ssize_t ret; + + ret = fb_sys_write(fb, buf, count, ppos); + if (ret < 0) + return ret; + + queue_delayed_work(system_wq, &oled->dwork, 0); + return ret; +} + +static void oel9m1027fb_fillrect(struct fb_info *fb, + const struct fb_fillrect *rect) +{ + struct oel9m1027 *oled = dev_get_drvdata(fb->device); + + sys_fillrect(fb, rect); + queue_delayed_work(system_wq, &oled->dwork, 0); +} + +static void oel9m1027fb_copyarea(struct fb_info *fb, + const struct fb_copyarea *area) +{ + struct oel9m1027 *oled = dev_get_drvdata(fb->device); + + sys_copyarea(fb, area); + queue_delayed_work(system_wq, &oled->dwork, 0); +} + +static void oel9m1027fb_imageblit(struct fb_info *fb, + const struct fb_image *image) +{ + struct oel9m1027 *oled = dev_get_drvdata(fb->device); + + sys_imageblit(fb, image); + queue_delayed_work(system_wq, &oled->dwork, 0); +} + +static const struct fb_fix_screeninfo oel9m1027fb_fix = { + .id = "oel9m1027", + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_MONO10, + .xpanstep = 0, + .ypanstep = 0, + .ywrapstep = 0, + .line_length = OEL9M1027_WIDTH / 8, + .accel = FB_ACCEL_NONE, +}; + +static const struct fb_var_screeninfo oel9m1027fb_var = { + .xres = OEL9M1027_WIDTH, + .yres = OEL9M1027_HEIGHT, + .xres_virtual = OEL9M1027_WIDTH, + .yres_virtual = OEL9M1027_HEIGHT, + .bits_per_pixel = 1, + .red = { 0, 1, 0 }, + .green = { 0, 1, 0 }, + .blue = { 0, 1, 0 }, + .left_margin = 0, + .right_margin = 0, + .upper_margin = 0, + .lower_margin = 0, + .vmode = FB_VMODE_NONINTERLACED, +}; + +static struct fb_ops oel9m1027fb_ops = { + .owner = THIS_MODULE, + .fb_read = fb_sys_read, + .fb_write = oel9m1027fb_write, + .fb_fillrect = oel9m1027fb_fillrect, + .fb_copyarea = oel9m1027fb_copyarea, + .fb_imageblit = oel9m1027fb_imageblit, +}; + +int oel9m1027fb_init(struct oel9m1027 *oled) +{ + int ret; + struct fb_info *fb = framebuffer_alloc(0, oled->dev); + + if (!fb) { + ret = -EINVAL; + goto none; + } + + oled->framebuffer = vzalloc(sizeof(unsigned char) * + OEL9M1027_SIZE); + if (!oled->framebuffer) { + ret = -ENOMEM; + goto fbinfoalloced; + } + + fb->screen_base = (char __iomem *)oled->framebuffer; + fb->screen_size = OEL9M1027_SIZE; + fb->fbops = &oel9m1027fb_ops; + fb->fix = oel9m1027fb_fix; + fb->fix.smem_start = page_to_phys(vmalloc_to_page(oled->framebuffer)); + fb->fix.smem_len = sizeof(unsigned char) * OEL9M1027_SIZE; + fb->var = oel9m1027fb_var; + fb->pseudo_palette = NULL; + fb->par = NULL; + oled->fb = fb; + + if (register_framebuffer(fb) < 0) { + ret = -EINVAL; + goto fballoced; + } + + fb_info(fb, "%s framebuffer device\n", fb->fix.id); + + return 0; + +fballoced: + vfree(oled->framebuffer); + +fbinfoalloced: + framebuffer_release(fb); + +none: + return ret; +} + +void oel9m1027fb_exit(struct oel9m1027 *oled) +{ + unregister_framebuffer(oled->fb); + vfree(oled->framebuffer); + framebuffer_release(oled->fb); +} + +MODULE_DESCRIPTION("OEL9M1027 OLED frame buffer driver"); +MODULE_AUTHOR("Wilson Lee "); +MODULE_LICENSE("GPL"); diff --git a/drivers/auxdisplay/sh1107.c b/drivers/auxdisplay/sh1107.c new file mode 100644 index 0000000000000..352ef6f0e491e --- /dev/null +++ b/drivers/auxdisplay/sh1107.c @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2017 National Instruments Corp. + * + * Based on ks0108 driver (v0.1.0) + * Copyright (C) 2006 by Miguel Ojeda Sandonis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sh1107.h" + +#define SH1107_CMDBYTE 0x00 +#define SH1107_DATABYTE 0x40 + +#define SH1107_SET_DC_HI 0xAD +#define SH1107_SET_DC_LO 0x80 +#define SH1107_SET_DC_MASK 0x0F +#define SH1107_SET_VCOM 0xDB +#define SH1107_SET_PHASE_PERIOD 0xD9 +#define SH1107_SET_ENTIRE_DISP 0xA4 +#define SH1107_SET_DISP 0xAE +#define SH1107_SET_INVERT_DISP 0xA6 +#define SH1107_SET_DISP_CONTRAST 0x81 +#define SH1107_SET_SCANDIR 0xC0 +#define SH1107_SET_DISP_FREQ 0xD5 +#define SH1107_SET_MULTIPLEX_RATIO 0xA8 +#define SH1107_SET_MULTIPLEX_RATIO_MASK 0x7F +#define SH1107_SET_ADDR_MODE 0x20 +#define SH1107_SET_REMAP_SEG 0xA0 +#define SH1107_SET_STARTLINE 0xDC +#define SH1107_SET_STARTLINE_MASK 0x7F +#define SH1107_SET_COL_ADDR_LO 0x00 +#define SH1107_SET_COL_ADDR_HI 0x10 +#define SH1107_SET_COL_ADDR_LO_MASK 0x0F +#define SH1107_SET_COL_ADDR_HI_MASK 0x70 +#define SH1107_SET_PAGE 0xB0 +#define SH1107_SET_PAGE_MASK 0x0F +#define SH1107_SET_OFFSET 0xD3 +#define SH1107_SET_OFFSET_MASK 0x7F + +struct sh1107 { + struct device *dev; /* sh1107 device */ + struct i2c_client *client; +}; + +int sh1107_writedata(struct sh1107 *sh, unsigned char byte) +{ + unsigned char tdata[2] = { SH1107_DATABYTE, byte }; + int ret; + + /* write control byte, then data */ + struct i2c_msg msg = { + .addr = sh->client->addr, + .len = 2, + .buf = tdata, + }; + + ret = i2c_transfer(sh->client->adapter, &msg, 1); + + return ret == 1 ? 0 : ret; +} +EXPORT_SYMBOL_GPL(sh1107_writedata); + +int sh1107_writecontrol(struct sh1107 *sh, unsigned char byte) +{ + unsigned char tdata[2] = { SH1107_CMDBYTE, byte }; + int ret; + + /* write control byte, then data */ + struct i2c_msg msg = { + .addr = sh->client->addr, + .len = 2, + .buf = tdata, + }; + + ret = i2c_transfer(sh->client->adapter, &msg, 1); + + return ret == 1 ? 0 : ret; +} +EXPORT_SYMBOL_GPL(sh1107_writecontrol); + +int sh1107_dccontrol(struct sh1107 *sh, unsigned char value) +{ + int ret; + + ret = sh1107_writecontrol(sh, SH1107_SET_DC_HI); + if (ret) + return ret; + + return sh1107_writecontrol(sh, SH1107_SET_DC_LO | + (value & SH1107_SET_DC_MASK)); +} +EXPORT_SYMBOL_GPL(sh1107_dccontrol); + +int sh1107_vcomcontrol(struct sh1107 *sh, unsigned char vcom) +{ + int ret; + + ret = sh1107_writecontrol(sh, SH1107_SET_VCOM); + if (ret) + return ret; + + return sh1107_writecontrol(sh, vcom); +} +EXPORT_SYMBOL_GPL(sh1107_vcomcontrol); + +int sh1107_phaseperiod(struct sh1107 *sh, unsigned char period) +{ + int ret; + + ret = sh1107_writecontrol(sh, SH1107_SET_PHASE_PERIOD); + if (ret) + return ret; + + return sh1107_writecontrol(sh, period); +} +EXPORT_SYMBOL_GPL(sh1107_phaseperiod); + +int sh1107_entiredisplaystate(struct sh1107 *sh, unsigned char state) +{ + return sh1107_writecontrol(sh, SH1107_SET_ENTIRE_DISP | + (state ? BIT(0) : 0)); +} +EXPORT_SYMBOL_GPL(sh1107_entiredisplaystate); + +int sh1107_displaystate(struct sh1107 *sh, unsigned char state) +{ + return sh1107_writecontrol(sh, SH1107_SET_DISP | + (state ? BIT(0) : 0)); +} +EXPORT_SYMBOL_GPL(sh1107_displaystate); + +int sh1107_displayinvert(struct sh1107 *sh, unsigned char invert) +{ + return sh1107_writecontrol(sh, SH1107_SET_INVERT_DISP | + (invert ? BIT(0) : 0)); +} +EXPORT_SYMBOL_GPL(sh1107_displayinvert); + +int sh1107_displaycontrast(struct sh1107 *sh, unsigned char contrast) +{ + int ret; + + ret = sh1107_writecontrol(sh, SH1107_SET_DISP_CONTRAST); + if (ret) + return ret; + + return sh1107_writecontrol(sh, contrast); +} +EXPORT_SYMBOL_GPL(sh1107_displaycontrast); + +int sh1107_scandir(struct sh1107 *sh, unsigned char direction) +{ + return sh1107_writecontrol(sh, SH1107_SET_SCANDIR | + (direction ? BIT(3) : 0)); +} +EXPORT_SYMBOL_GPL(sh1107_scandir); + +int sh1107_displayfreq(struct sh1107 *sh, unsigned char frequency) +{ + int ret; + + ret = sh1107_writecontrol(sh, SH1107_SET_DISP_FREQ); + if (ret) + return ret; + + return sh1107_writecontrol(sh, frequency); +} +EXPORT_SYMBOL_GPL(sh1107_displayfreq); + +int sh1107_multiplexratio(struct sh1107 *sh, unsigned char ratio) +{ + int ret; + + ret = sh1107_writecontrol(sh, SH1107_SET_MULTIPLEX_RATIO); + if (ret) + return ret; + + return sh1107_writecontrol(sh, ratio & + SH1107_SET_MULTIPLEX_RATIO_MASK); +} +EXPORT_SYMBOL_GPL(sh1107_multiplexratio); + +int sh1107_addressingmode(struct sh1107 *sh, unsigned char memmode) +{ + return sh1107_writecontrol(sh, SH1107_SET_ADDR_MODE | + (memmode ? BIT(0) : 0)); +} +EXPORT_SYMBOL_GPL(sh1107_addressingmode); + +int sh1107_segremap(struct sh1107 *sh, unsigned char uprotation) +{ + return sh1107_writecontrol(sh, SH1107_SET_REMAP_SEG | + (uprotation ? BIT(0) : 0)); +} +EXPORT_SYMBOL_GPL(sh1107_segremap); + +int sh1107_startline(struct sh1107 *sh, unsigned char startline) +{ + int ret; + + ret = sh1107_writecontrol(sh, SH1107_SET_STARTLINE); + if (ret) + return ret; + + return sh1107_writecontrol(sh, startline & SH1107_SET_STARTLINE_MASK); +} +EXPORT_SYMBOL_GPL(sh1107_startline); + +int sh1107_address(struct sh1107 *sh, unsigned char address) +{ + int ret; + + ret = sh1107_writecontrol(sh, SH1107_SET_COL_ADDR_LO | + (address & SH1107_SET_COL_ADDR_LO_MASK)); + if (ret) + return ret; + + return sh1107_writecontrol(sh, SH1107_SET_COL_ADDR_HI | + ((address & SH1107_SET_COL_ADDR_HI_MASK) + >> 4)); +} +EXPORT_SYMBOL_GPL(sh1107_address); + +int sh1107_page(struct sh1107 *sh, unsigned char page) +{ + return sh1107_writecontrol(sh, SH1107_SET_PAGE | + (page & SH1107_SET_PAGE_MASK)); +} +EXPORT_SYMBOL_GPL(sh1107_page); + +int sh1107_offset(struct sh1107 *sh, unsigned char offset) +{ + int ret; + + ret = sh1107_writecontrol(sh, SH1107_SET_OFFSET); + if (ret) + return ret; + + return sh1107_writecontrol(sh, offset & SH1107_SET_OFFSET_MASK); +} +EXPORT_SYMBOL_GPL(sh1107_offset); + +static int sh1107_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct sh1107 *sh; + + sh = devm_kzalloc(dev, sizeof(*sh), GFP_KERNEL); + if (!sh) + return -ENOMEM; + + sh->dev = dev; + sh->client = client; + + /* Test write data into OLED module (RAM) */ + if (sh1107_writedata(sh, 0)) + return -ENODEV; + + dev_set_drvdata(dev, sh); + dev_info(dev, "%s OLED module found.\n", client->name); + + return devm_of_platform_populate(dev); +} + +static const struct of_device_id sh1107_dt_ids[] = { + { .compatible = "sinowealth,sh1107" }, + { }, +}; +MODULE_DEVICE_TABLE(of, sh1107_dt_ids); + +static const struct i2c_device_id sh1107_ids[] = { + { "sh1107", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, sh1107_ids); + +static struct i2c_driver sh1107_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = sh1107_dt_ids, + }, + .probe = sh1107_probe, + .id_table = sh1107_ids, +}; +module_i2c_driver(sh1107_driver); + +MODULE_DESCRIPTION("Driver for SH1107 LCD Driver"); +MODULE_AUTHOR("Wilson Lee "); +MODULE_LICENSE("GPL"); diff --git a/drivers/auxdisplay/sh1107.h b/drivers/auxdisplay/sh1107.h new file mode 100644 index 0000000000000..0e277a4923b76 --- /dev/null +++ b/drivers/auxdisplay/sh1107.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2017 National Instruments Corp. + * + * Based on ks0108 driver (v0.1.0) + * Copyright (C) 2006 by Miguel Ojeda Sandonis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _SH1107_H_ +#define _SH1107_H_ + +/* sh1107 device private data */ +struct sh1107; + +/* Write a byte to the data port */ +int sh1107_writedata(struct sh1107 *sh, unsigned char byte); + +/* Write a byte to the control port */ +int sh1107_writecontrol(struct sh1107 *sh, unsigned char byte); + +/* Set internal DC-DC control register (0..15) */ +int sh1107_dccontrol(struct sh1107 *sh, unsigned char value); + +/* Set controller VCOM level (0..255) */ +int sh1107_vcomcontrol(struct sh1107 *sh, unsigned char vcom); + +/* Set controller pre/dis-charge period (0..255) */ +int sh1107_phaseperiod(struct sh1107 *sh, unsigned char period); + +/* Set the forcibly turn entire display state (0..1) */ +int sh1107_entiredisplaystate(struct sh1107 *sh, unsigned char state); + +/* Set the controller current display state (0..1) */ +int sh1107_displaystate(struct sh1107 *sh, unsigned char state); + +/* Set invert display (0..1) */ +int sh1107_displayinvert(struct sh1107 *sh, unsigned char invert); + +/* Set display contrast level (0..255) */ +int sh1107_displaycontrast(struct sh1107 *sh, unsigned char contrast); + +/* Set the controller common output scan direction (0..1) */ +int sh1107_scandir(struct sh1107 *sh, unsigned char direction); + +/* Set the controller internal display clocks. (0..255) */ +int sh1107_displayfreq(struct sh1107 *sh, unsigned char frequency); + +/* Set the controller multiplex mode to any multiplex ratio (0..127) */ +int sh1107_multiplexratio(struct sh1107 *sh, unsigned char ratio); + +/* Set the controller memory addressing mode (0..1) */ +int sh1107_addressingmode(struct sh1107 *sh, unsigned char memmode); + +/* Set the controller segment remap (0..1) */ +int sh1107_segremap(struct sh1107 *sh, unsigned char uprotation); + +/* Set the controller current startline (0..127) */ +int sh1107_startline(struct sh1107 *sh, unsigned char startline); + +/* Set the controller current address (0..127) */ +int sh1107_address(struct sh1107 *sh, unsigned char address); + +/* Set the controller current page (0..15) */ +int sh1107_page(struct sh1107 *sh, unsigned char page); + +/* Set the controller offset (0..127) */ +int sh1107_offset(struct sh1107 *sh, unsigned char offset); + +#endif /* _SH1107_H_ */ diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index 1786d87b29e22..b6aa7ca2485c0 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -249,4 +249,11 @@ config FW_DEVLINK_SYNC_STATE_TIMEOUT command line option on every system/board your kernel is expected to work on. +config DEVICE_POLL + bool + +config DEVICE_POLL_NI_COMPAT + bool + default y if DEVICE_POLL + endmenu diff --git a/drivers/base/Makefile b/drivers/base/Makefile index 8074a10183dcb..34c5ee80ab1df 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_GENERIC_MSI_IRQ) += platform-msi.o obj-$(CONFIG_GENERIC_ARCH_TOPOLOGY) += arch_topology.o obj-$(CONFIG_GENERIC_ARCH_NUMA) += arch_numa.o obj-$(CONFIG_ACPI) += physical_location.o +obj-$(CONFIG_DEVICE_POLL) += poll.o obj-y += test/ diff --git a/drivers/base/poll.c b/drivers/base/poll.c new file mode 100644 index 0000000000000..27c480638b133 --- /dev/null +++ b/drivers/base/poll.c @@ -0,0 +1,380 @@ +/* + * Copyright (C) 2014 National Instruments Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* sysfs attributes */ + +#define to_ext_attr(x) container_of(x, struct dev_ext_attribute, attr) + +/* get sysfs attributes */ + +ssize_t device_poll_get_interval(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dev_ext_attribute *ea = to_ext_attr(attr); + struct device_poll *device_poll = ea->var; + + return sprintf(buf, "%d\n", device_poll->interval); +} + +static ssize_t device_poll_get_policy(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dev_ext_attribute *ea = to_ext_attr(attr); + struct device_poll *device_poll = ea->var; + + switch (device_poll->policy) { + case SCHED_NORMAL: + return sprintf(buf, "SCHED_NORMAL (SCHED_OTHER)\n"); + case SCHED_FIFO: + return sprintf(buf, "SCHED_FIFO\n"); + case SCHED_RR: + return sprintf(buf, "SCHED_RR\n"); + case SCHED_BATCH: + return sprintf(buf, "SCHED_BATCH\n"); + case SCHED_IDLE: + return sprintf(buf, "SCHED_IDLE\n"); + default: + return sprintf(buf, "unknown\n"); + } +} + +static ssize_t device_poll_get_priority(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct dev_ext_attribute *ea = to_ext_attr(attr); + struct device_poll *device_poll = ea->var; + + return sprintf(buf, "%d\n", device_poll->priority); +} + +/* set sysfs attributes */ + +static ssize_t device_poll_set_interval(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct dev_ext_attribute *ea = to_ext_attr(attr); + struct device_poll *device_poll = ea->var; + int interval; + int ret = 0; + + if (device_poll->use_capability && !capable(device_poll->capability)) + return -EPERM; + + if (kstrtoint(buf, 0, &interval) < 0) + return -EINVAL; + +#ifdef CONFIG_DEVICE_POLL_NI_COMPAT + /* For backwards compatibility with NI software. An interval of zero + * now indicates interrupt mode. Shipping NI software can get confused + * by this, so force 0 to -1. + */ + if (interval == 0) + interval = -1; +#endif + device_poll->ops->lock(device_poll); + if (device_poll->interval != interval) { + device_poll->interval = interval; + + ret = device_poll->ops->reinit(device_poll); + } + device_poll->ops->unlock(device_poll); + + if (ret) + return ret; + + return size; +} + +static ssize_t device_poll_set_policy(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct dev_ext_attribute *ea = to_ext_attr(attr); + struct device_poll *device_poll = ea->var; + char policy_str[16] = { 0 }; + int policy; + struct sched_param param; + + if (device_poll->use_capability && !capable(device_poll->capability)) + return -EPERM; + + if (sscanf(buf, "%15s", policy_str) != 1) + return -EINVAL; + + if ((strcmp(policy_str, "SCHED_NORMAL") == 0 || + (strcmp(policy_str, "SCHED_OTHER") == 0))) + policy = SCHED_NORMAL; + else if (strcmp(policy_str, "SCHED_FIFO") == 0) + policy = SCHED_FIFO; + else if (strcmp(policy_str, "SCHED_RR") == 0) + policy = SCHED_RR; + else if (strcmp(policy_str, "SCHED_BATCH") == 0) + policy = SCHED_BATCH; + else if (strcmp(policy_str, "SCHED_IDLE") == 0) + policy = SCHED_IDLE; + else + return -EINVAL; + + device_poll->ops->lock(device_poll); + if (device_poll->policy != policy) { + device_poll->policy = policy; + + if (device_poll->task) { + param.sched_priority = device_poll->priority; + sched_setscheduler(device_poll->task, + device_poll->policy, + ¶m); + } + } + device_poll->ops->unlock(device_poll); + + return size; +} + +static ssize_t device_poll_set_priority(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct dev_ext_attribute *ea = to_ext_attr(attr); + struct device_poll *device_poll = ea->var; + int priority; + struct sched_param param; + + if (device_poll->use_capability && !capable(device_poll->capability)) + return -EPERM; + + if (kstrtoint(buf, 0, &priority) < 0) + return -EINVAL; + + device_poll->ops->lock(device_poll); + if (device_poll->priority != priority) { + device_poll->priority = priority; + + if (device_poll->task) { + param.sched_priority = device_poll->priority; + sched_setscheduler(device_poll->task, + device_poll->policy, + ¶m); + } + } + device_poll->ops->unlock(device_poll); + + return size; +} + +/* sysfs attributes */ + +static const DEVICE_ATTR(interval, S_IWUSR | S_IRUGO, + device_poll_get_interval, device_poll_set_interval); + +static const DEVICE_ATTR(policy, S_IWUSR | S_IRUGO, + device_poll_get_policy, device_poll_set_policy); + +static const DEVICE_ATTR(priority, S_IWUSR | S_IRUGO, + device_poll_get_priority, device_poll_set_priority); + +#ifdef CONFIG_DEVICE_POLL_NI_COMPAT +/* For backwards compatibility with NI software. The interval attribute had a + * different name, and shipping NI software looks for this other name. + */ +static const DEVICE_ATTR(ni_polling_interval, S_IWUSR | S_IRUGO, + device_poll_get_interval, device_poll_set_interval); +#endif + +/* device_poll internal functions */ + +static int device_poll_thread(void *info) +{ + struct device_poll *device_poll = info; + int polling_interval; + int polling_interval_us; + struct sched_param param; + + polling_interval = device_poll->interval; + + /* If we got changed to interrupt mode before the polling thread + * started. + */ + if (unlikely(polling_interval <= 0)) { + while (!kthread_should_stop()) + usleep_range(1000, 1100); + return -EINTR; + } + + polling_interval_us = polling_interval * 1000; + + param.sched_priority = device_poll->priority; + sched_setscheduler(current, device_poll->policy, ¶m); + + while (!kthread_should_stop()) { + /* Ensure changes to device_poll->enabled made on other CPUs + * are seen here. + */ + smp_rmb(); + if (device_poll->enabled) + device_poll->ops->interrupt(device_poll); + + if (polling_interval < 20) + usleep_range(polling_interval_us, polling_interval_us + + 100); + else + msleep(polling_interval); + } + + return 0; +} + +/* device_poll external functions */ + +int device_poll_init(struct device_poll *device_poll) +{ + int ret; + + if (!device_poll || !device_poll->device || !device_poll->ops) + return -EINVAL; + + if (!device_poll->ops->reinit || !device_poll->ops->lock || + !device_poll->ops->unlock || !device_poll->ops->interrupt) + return -EINVAL; + + if (device_poll->use_capability && !cap_valid(device_poll->capability)) + return -EINVAL; + + device_poll->task = NULL; + device_poll->enabled = 0; + + device_poll->interval_attr.attr = dev_attr_interval; + device_poll->policy_attr.attr = dev_attr_policy; + device_poll->priority_attr.attr = dev_attr_priority; + + device_poll->interval_attr.var = device_poll; + device_poll->policy_attr.var = device_poll; + device_poll->priority_attr.var = device_poll; + + if (device_poll->use_capability) { + device_poll->interval_attr.attr.attr.mode |= + (S_IWUSR | S_IRUGO); + device_poll->policy_attr.attr.attr.mode |= + (S_IWUSR | S_IRUGO); + device_poll->priority_attr.attr.attr.mode |= + (S_IWUSR | S_IRUGO); + } + + sysfs_attr_init(&device_poll->interval_attr.attr.attr); + sysfs_attr_init(&device_poll->policy_attr.attr.attr); + sysfs_attr_init(&device_poll->priority_attr.attr.attr); + + device_poll->attrs[0] = &device_poll->interval_attr.attr.attr; + device_poll->attrs[1] = &device_poll->policy_attr.attr.attr; + device_poll->attrs[2] = &device_poll->priority_attr.attr.attr; + device_poll->attrs[3] = NULL; + + device_poll->attr_group.name = "device_poll"; + device_poll->attr_group.attrs = device_poll->attrs; + + ret = sysfs_create_group(&device_poll->device->kobj, + &device_poll->attr_group); + if (ret) + device_poll_exit(device_poll); + +#ifdef CONFIG_DEVICE_POLL_NI_COMPAT + /* For backwards compatibility with NI software. */ + + /* An interval of zero now indicates interrupt mode. Shipping NI + * software can get confused by this, so force 0 to -1. + */ + if (device_poll->interval == 0) + device_poll->interval = -1; + + /* The interval attribute originally had a different name and location, + * and shipping NI software looks for this other name in this other + * location. + */ + device_poll->ni_interval_attr.attr = dev_attr_ni_polling_interval; + device_poll->ni_interval_attr.var = device_poll; + + if (device_poll->use_capability) + device_poll->ni_interval_attr.attr.attr.mode |= S_IWUGO; + + sysfs_attr_init(&device_poll->ni_interval_attr.attr.attr); + + ret = sysfs_create_file(&device_poll->device->kobj, + &device_poll->ni_interval_attr.attr.attr); + if (ret) + device_poll_exit(device_poll); +#endif + return ret; +} +EXPORT_SYMBOL(device_poll_init); + +void device_poll_exit(struct device_poll *device_poll) +{ + if (!device_poll || !device_poll->device) + return; + +#ifdef CONFIG_DEVICE_POLL_NI_COMPAT + sysfs_remove_file(&device_poll->device->kobj, + &device_poll->ni_interval_attr.attr.attr); +#endif + sysfs_remove_group(&device_poll->device->kobj, + &device_poll->attr_group); +} +EXPORT_SYMBOL(device_poll_exit); + +int device_poll_request_irq(struct device_poll *device_poll) +{ + int err; + + if (!device_poll) + return -EINVAL; + + /* If interrupts are enabled. */ + if (device_poll->interval <= 0) + return -ERANGE; + + /* Start up the polling thread. */ + device_poll->task = kthread_run(device_poll_thread, + device_poll, "poll/%s", + dev_name(device_poll->device)); + if (IS_ERR(device_poll->task)) { + err = PTR_ERR(device_poll->task); + device_poll->task = NULL; + dev_err(device_poll->device, + "Unable to create polling thread: %d\n", err); + return err; + } + + return 0; +} +EXPORT_SYMBOL(device_poll_request_irq); + +void device_poll_free_irq(struct device_poll *device_poll) +{ + if (device_poll_is_active(device_poll)) { + kthread_stop(device_poll->task); + device_poll->task = NULL; + } +} +EXPORT_SYMBOL(device_poll_free_irq); diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c index 0fb728ade92e4..c397e4c8cdab1 100644 --- a/drivers/i2c/busses/i2c-cadence.c +++ b/drivers/i2c/busses/i2c-cadence.c @@ -1530,6 +1530,15 @@ static int cdns_i2c_probe(struct platform_device *pdev) return irq; id->adap.owner = THIS_MODULE; + + /* + * A bus number of -1 means dynamically assign, default to that if + * bus-id isn't specified in the device tree. + */ + ret = of_property_read_u32(pdev->dev.of_node, "bus-id", &id->adap.nr); + if (ret) + id->adap.nr = -1; + id->adap.dev.of_node = pdev->dev.of_node; id->adap.algo = &cdns_i2c_algo; id->adap.timeout = CDNS_I2C_TIMEOUT; @@ -1597,7 +1606,7 @@ static int cdns_i2c_probe(struct platform_device *pdev) } cdns_i2c_init(id); - ret = i2c_add_adapter(&id->adap); + ret = i2c_add_numbered_adapter(&id->adap); if (ret < 0) goto err_clk_notifier_unregister; diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-xiic.c index 28015d77599d1..2521012922ce8 100644 --- a/drivers/i2c/busses/i2c-xiic.c +++ b/drivers/i2c/busses/i2c-xiic.c @@ -1430,6 +1430,9 @@ static int xiic_i2c_probe(struct platform_device *pdev) int ret, irq; u8 i; u32 sr; +#ifdef CONFIG_OF + const unsigned int *prop; +#endif i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); if (!i2c) @@ -1461,14 +1464,32 @@ static int xiic_i2c_probe(struct platform_device *pdev) snprintf(i2c->adap.name, sizeof(i2c->adap.name), DRIVER_NAME " %s", pdev->name); + /* a bus number of -1 means dynamically assign, default to that if + * bus-id isn't specified in the device tree + */ + i2c->adap.nr = -1; +#ifdef CONFIG_OF + prop = of_get_property(pdev->dev.of_node, "bus-id", NULL); + if (prop) + i2c->adap.nr = be32_to_cpup(prop); +#endif + + xiic_reinit(i2c); + mutex_init(&i2c->lock); spin_lock_init(&i2c->atomic_lock); i2c->clk = devm_clk_get_enabled(&pdev->dev, NULL); - if (IS_ERR(i2c->clk)) - return dev_err_probe(&pdev->dev, PTR_ERR(i2c->clk), - "failed to enable input clock.\n"); - + if (IS_ERR(i2c->clk)) { + if (PTR_ERR(i2c->clk) != -ENOENT) { + dev_err(&pdev->dev, + "error while finding input clock. %ld\n", + PTR_ERR(i2c->clk)); + return PTR_ERR(i2c->clk); + } + dev_err(&pdev->dev, "Optional input clock not found.\n"); + i2c->clk = NULL; + } i2c->dev = &pdev->dev; pm_runtime_set_autosuspend_delay(i2c->dev, XIIC_PM_TIMEOUT); pm_runtime_use_autosuspend(i2c->dev); @@ -1514,7 +1535,7 @@ static int xiic_i2c_probe(struct platform_device *pdev) } /* add i2c adapter to i2c tree */ - ret = i2c_add_adapter(&i2c->adap); + ret = i2c_add_numbered_adapter(&i2c->adap); if (ret) { xiic_deinit(i2c); goto err_pm_disable; diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 1269ab8eb726a..18bbf16e5e171 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -1078,6 +1078,30 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, return 0; } +static int gic_irq_domain_xlate(struct irq_domain *d, + struct device_node *controller, + const u32 *intspec, unsigned int intsize, + unsigned long *out_hwirq, unsigned int *out_type) +{ + unsigned long ret = 0; + + if (irq_domain_get_of_node(d) != controller) + return -EINVAL; + if (intsize < 3) + return -EINVAL; + + /* Get the interrupt number and add 16 to skip over SGIs */ + *out_hwirq = intspec[1] + 16; + + /* For SPIs, we need to add 16 more to get the GIC irq ID number */ + if (!intspec[0]) + *out_hwirq += 16; + + *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK; + + return ret; +} + static int gic_irq_domain_translate(struct irq_domain *d, struct irq_fwspec *fwspec, unsigned long *hwirq, @@ -1158,6 +1182,7 @@ static const struct irq_domain_ops gic_irq_domain_hierarchy_ops = { .translate = gic_irq_domain_translate, .alloc = gic_irq_domain_alloc, .free = irq_domain_free_irqs_top, + .xlate = gic_irq_domain_xlate, }; static int gic_init_bases(struct gic_chip_data *gic, diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 06e6291be11b2..40a0a6116ffca 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -936,6 +936,16 @@ config LEDS_USER support in kernel. To compile this driver as a module, choose 'm' here: the module will be called uleds. +config LEDS_NI793X + tristate "LED support for NI 793xR devices" + depends on LEDS_CLASS + help + This option enables support for the User1 LED on the NI 793xR family + of devices. + + To compile this driver as a module, choose M here: the module will be + called leds-ni793x. + config LEDS_NIC78BX tristate "LED support for NI PXI NIC78bx devices" depends on LEDS_CLASS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 9a0333ec1a861..db2dd3c511410 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -72,6 +72,7 @@ obj-$(CONFIG_LEDS_MLXREG) += leds-mlxreg.o obj-$(CONFIG_LEDS_MT6323) += leds-mt6323.o obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o +obj-$(CONFIG_LEDS_NI793X) += leds-ni793x.o obj-$(CONFIG_LEDS_NIC78BX) += leds-nic78bx.o obj-$(CONFIG_LEDS_NS2) += leds-ns2.o obj-$(CONFIG_LEDS_OT200) += leds-ot200.o diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 6b9fa060c3a11..b5724e4c3bd48 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -84,6 +84,39 @@ static ssize_t max_brightness_show(struct device *dev, } static DEVICE_ATTR_RO(max_brightness); +static ssize_t inverted_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", led_cdev->inverted); +} + +static ssize_t inverted_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + u8 inverted; + int ret; + + ret = kstrtou8(buf, 10, &inverted); + if (ret < 0) + return ret; + + if (inverted > 1) + return -EINVAL; + + led_cdev->inverted = inverted; + + /* Note: The LED brightness is not immediately + * updated as a result of changing the inversion. + */ + + return size; +} + +static DEVICE_ATTR_RW(inverted); + #ifdef CONFIG_LEDS_TRIGGERS static const BIN_ATTR(trigger, 0644, led_trigger_read, led_trigger_write, 0); static const struct bin_attribute *const led_trigger_bin_attrs[] = { @@ -98,6 +131,7 @@ static const struct attribute_group led_trigger_group = { static struct attribute *led_class_attrs[] = { &dev_attr_brightness.attr, &dev_attr_max_brightness.attr, + &dev_attr_inverted.attr, NULL, }; diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c index 59473f286b31f..1e549f3bbf8ad 100644 --- a/drivers/leds/led-core.c +++ b/drivers/leds/led-core.c @@ -331,6 +331,9 @@ EXPORT_SYMBOL_GPL(led_set_brightness); void led_set_brightness_nopm(struct led_classdev *led_cdev, unsigned int value) { + if (led_cdev->inverted) + value = value ? LED_OFF : LED_FULL; + /* Use brightness_set op if available, it is guaranteed not to sleep */ if (!__led_set_brightness(led_cdev, value)) return; @@ -375,6 +378,9 @@ int led_set_brightness_sync(struct led_classdev *led_cdev, unsigned int value) if (led_cdev->blink_delay_on || led_cdev->blink_delay_off) return -EBUSY; + if (led_cdev->inverted) + value = value ? LED_OFF : LED_FULL; + led_cdev->brightness = min(value, led_cdev->max_brightness); if (led_cdev->flags & LED_SUSPENDED) diff --git a/drivers/leds/leds-ni793x.c b/drivers/leds/leds-ni793x.c new file mode 100644 index 0000000000000..b9edcc4bfb0ba --- /dev/null +++ b/drivers/leds/leds-ni793x.c @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2015 National Instruments Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include + +#define USER1_STATUS_BIT BIT(0) +#define USER1_OFF_CMD 0x1 +#define USER1_ON_CMD 0x2 + +struct ni793x_led { + void __iomem *user1; + struct led_classdev cdev; +}; + +static inline struct ni793x_led *to_ni793x_led(struct led_classdev *cdev) +{ + return container_of(cdev, struct ni793x_led, cdev); +} + +static void ni793x_led_set_brightness(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct ni793x_led *led = to_ni793x_led(cdev); + + if (brightness == LED_OFF) + writel_relaxed(USER1_OFF_CMD, led->user1); + else + writel_relaxed(USER1_ON_CMD, led->user1); +} + +static enum led_brightness +ni793x_led_get_brightness(struct led_classdev *cdev) +{ + struct ni793x_led *led = to_ni793x_led(cdev); + + return readl_relaxed(led->user1) & USER1_STATUS_BIT; +} + +static int ni793x_leds_probe(struct platform_device *pdev) +{ + struct device_node *node; + struct ni793x_led *led; + struct resource *res; + + led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL); + if (!led) + return -ENOMEM; + + platform_set_drvdata(pdev, led); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + led->user1 = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(led->user1)) + return PTR_ERR(led->user1); + + node = of_get_child_by_name(pdev->dev.of_node, "user1"); + if (!node) { + dev_err(&pdev->dev, "user1 LED description not found\n"); + return -ENOENT; + } + + led->cdev.name = of_get_property(node, "label", NULL) ? : node->name; + led->cdev.default_trigger = of_get_property(node, + "linux,default-trigger", + NULL); + led->cdev.max_brightness = 1; + led->cdev.brightness_set = ni793x_led_set_brightness; + led->cdev.brightness_get = ni793x_led_get_brightness; + + return led_classdev_register(&pdev->dev, &led->cdev); +} + +static void ni793x_leds_remove(struct platform_device *pdev) +{ + struct ni793x_led *led = platform_get_drvdata(pdev); + + led_classdev_unregister(&led->cdev); +} + +static const struct of_device_id ni793x_led_ids[] = { + { .compatible = "ni,led-793x" }, + { }, +}; +MODULE_DEVICE_TABLE(of, ni793x_led_ids); + +static struct platform_driver ni793x_leds_driver = { + .driver = { + .name = "leds-ni793x", + .of_match_table = of_match_ptr(ni793x_led_ids), + }, + .probe = ni793x_leds_probe, + .remove = ni793x_leds_remove, +}; +module_platform_driver(ni793x_leds_driver); + +MODULE_DESCRIPTION("Driver for RT User1 LED on FlexRIO NI-793xR Products"); +MODULE_AUTHOR("National Instruments"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 2504de4c99b8e..7f369157c4609 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -57,6 +57,15 @@ config DUMMY_IRQ The sole purpose of this module is to help with debugging of systems on which spurious IRQs would happen on disabled IRQ vector. +config FPGA_PERIPHERAL + bool "FPGA Peripheral Driver" + help + FPGA Peripheral driver to notify peripheral devices of an + impending download. + + Used to make sure the devices routed through the FPGA are + powered off when an FPGA download occurs. + config IBM_ASM tristate "Device driver for IBM RSA service processor" depends on X86 && PCI && INPUT @@ -93,6 +102,16 @@ config IBMVMC To compile this driver as a module, choose M here: the module will be called ibmvmc. +config NIZYNQ_CPLD + tristate "National Instruments Zynq CPLD driver" + depends on I2C && SYSFS && LEDS_CLASS + help + If you say yes here, you get support for the CPLD features + on National Instruments Zynq-based targets. + + This driver can also be built as a module. If so, the module + will be called nizynqcpld.ko. + config PHANTOM tristate "Sensable PHANToM (PCI)" depends on PCI diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 2ce11031cd58f..f80a181198c44 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_SMPRO_ERRMON) += smpro-errmon.o obj-$(CONFIG_SMPRO_MISC) += smpro-misc.o obj-$(CONFIG_CS5535_MFGPT) += cs5535-mfgpt.o obj-$(CONFIG_GEHC_ACHC) += gehc-achc.o +obj-$(CONFIG_NIZYNQ_CPLD) += nizynqcpld.o obj-$(CONFIG_HP_ILO) += hpilo.o obj-$(CONFIG_APDS9802ALS) += apds9802als.o obj-$(CONFIG_ISL29003) += isl29003.o @@ -77,3 +78,22 @@ obj-y += amd-sbi/ obj-$(CONFIG_MISC_RP1) += rp1/ obj-$(CONFIG_NI_RT_FEATURES) += nirtfeatures.o obj-$(CONFIG_NI_WATCHDOG) += niwatchdog.o +obj-$(CONFIG_FPGA_PERIPHERAL) += fpgaperipheral.o + +lkdtm-$(CONFIG_LKDTM) += lkdtm_core.o +lkdtm-$(CONFIG_LKDTM) += lkdtm_bugs.o +lkdtm-$(CONFIG_LKDTM) += lkdtm_heap.o +lkdtm-$(CONFIG_LKDTM) += lkdtm_perms.o +lkdtm-$(CONFIG_LKDTM) += lkdtm_refcount.o +lkdtm-$(CONFIG_LKDTM) += lkdtm_rodata_objcopy.o +lkdtm-$(CONFIG_LKDTM) += lkdtm_usercopy.o + +KCOV_INSTRUMENT_lkdtm_rodata.o := n + +OBJCOPYFLAGS := +OBJCOPYFLAGS_lkdtm_rodata_objcopy.o := \ + --rename-section .text=.rodata,alloc,readonly,load +targets += lkdtm_rodata.o lkdtm_rodata_objcopy.o +$(obj)/lkdtm_rodata_objcopy.o: $(obj)/lkdtm_rodata.o FORCE + $(call if_changed,objcopy) +>>>>>>> b00bd564209c (fpgaperipheral: Add driver to notify clients on FPGA downloads) diff --git a/drivers/misc/fpgaperipheral.c b/drivers/misc/fpgaperipheral.c new file mode 100644 index 0000000000000..e4015cb96bab7 --- /dev/null +++ b/drivers/misc/fpgaperipheral.c @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2012 National Instruments Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FPGAPERIPHERAL_NAME "fpgaperipheral" +#define DEVCFG_INT_STS_OFFSET 0x0C +#define INT_STS_FPGA_DONE_MASK 0x04 + +BLOCKING_NOTIFIER_HEAD(fpgaperipheral_notifier_list); +EXPORT_SYMBOL_GPL(fpgaperipheral_notifier_list); + +/** + * struct fpgaperipheral_drvdata - fpgaperipheral driver structure + * @devcfg_addr: Pointer (from ioremap) to the devcfg memory region + */ +struct fpgaperipheral_drvdata { + void __iomem *devcfg_addr; +}; + +/* + * drvdata given filewide scope instead of dynamically + * allocated and passed around so it's easier to share between the + * platform driver (probe/remove) and misc device driver (open/release). + * This is okay since there is only ever 1 device. + */ +static struct fpgaperipheral_drvdata drvdata; + +/* Access control: Only allow one open at a time */ +static atomic_t fpgaperipheral_available = ATOMIC_INIT(1); + +/** + * is_fpga_programmed() - Checks if the FPGA is currently programmed + * Returns non-zero iff FPGA is programmed + */ +static int is_fpga_programmed(void) +{ + return ioread32(drvdata.devcfg_addr + DEVCFG_INT_STS_OFFSET) & + INT_STS_FPGA_DONE_MASK; +} + +/** + * notify_clients() - Depending on up_or_down, notifies clients FPGA is about + * to be unprogrammed, or is now programmed. + * @up_or_down: Whether FPGA is about to go down, or is now up. + */ +static void notify_clients(unsigned long up_or_down) +{ + blocking_notifier_call_chain(&fpgaperipheral_notifier_list, + up_or_down, NULL); +} + +/** + * fpgaperipheral_misc_open() - Notifies clients that FPGA is about to go down. + * Only allows 1 open() at a time. + * @inode: Pointer to the inode structure of this device. + * @file: Pointer to the file structure. + * Returns 0 on success, negative error otherwise + */ +static int fpgaperipheral_misc_open(struct inode *inode, struct file *file) +{ + if (!atomic_dec_and_test(&fpgaperipheral_available)) { + atomic_inc(&fpgaperipheral_available); + return -EBUSY; + } + + notify_clients(FPGA_PERIPHERAL_DOWN); + return 0; +} + +/** + * fpgaperipheral_misc_release() - Notifies clients that FPGA is back up. + * @inode: Pointer to the inode structure of this device. + * @file: Pointer to the file structure. + * Returns 0 on success, negative error otherwise + */ +static int fpgaperipheral_misc_release(struct inode *inode, struct file *file) +{ + if (is_fpga_programmed()) + notify_clients(FPGA_PERIPHERAL_UP); + else + notify_clients(FPGA_PERIPHERAL_FAILED); + + atomic_inc(&fpgaperipheral_available); + return 0; +} + +static const struct file_operations fpgaperipheral_misc_fops = { + .owner = THIS_MODULE, + .open = fpgaperipheral_misc_open, + .release = fpgaperipheral_misc_release +}; + +static struct miscdevice fpgaperipheral_misc_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = FPGAPERIPHERAL_NAME, + .fops = &fpgaperipheral_misc_fops, +}; + +/** + * fpgaperipheral_probe - Platform driver probe + * @pdev: Pointer to the platform device structure + * Returns 0 on success, negative error otherwise + */ +static int fpgaperipheral_probe(struct platform_device *pdev) +{ + int retval; + void __iomem *devcfg_addr; + struct resource *devcfg_res; + + /* Get devcfg resource */ + devcfg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!devcfg_res) { + dev_err(&pdev->dev, "Couldn't get io resource\n"); + return -ENODEV; + } + + /* Get the mem region for devcfg */ + if (!request_mem_region(devcfg_res->start, + resource_size(devcfg_res), + FPGAPERIPHERAL_NAME)) { + dev_err(&pdev->dev, "Couldn't lock memory region at %Lx\n", + (unsigned long long) devcfg_res->start); + return -EBUSY; + } + + /* Map the devcfg mem region */ + devcfg_addr = ioremap(devcfg_res->start, resource_size(devcfg_res)); + if (!devcfg_addr) { + dev_err(&pdev->dev, "ioremap failed\n"); + retval = -EIO; + goto failed_after_request_mem_region; + } + + /* Set the drvdata where the misc device can find it */ + drvdata.devcfg_addr = devcfg_addr; + + /* Now register the misc device */ + retval = misc_register(&fpgaperipheral_misc_dev); + if (retval) { + dev_err(&pdev->dev, "Couldn't register misc device\n"); + goto failed_after_ioremap; + } + + dev_info(&pdev->dev, "probe succeeded\n"); + + return 0; + +failed_after_ioremap: + iounmap(drvdata.devcfg_addr); + +failed_after_request_mem_region: + release_mem_region(devcfg_res->start, resource_size(devcfg_res)); + + drvdata.devcfg_addr = NULL; + return retval; +} + +/** + * fpgaperipheral_remove - called when the platform driver is unregistered + * @pdev: Pointer to the platform device structure + * Returns 0 on success, negative error otherwise + */ +static int __exit fpgaperipheral_remove(struct platform_device *pdev) +{ + struct resource *devcfg_res; + + devcfg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + misc_deregister(&fpgaperipheral_misc_dev); + iounmap(drvdata.devcfg_addr); + release_mem_region(devcfg_res->start, resource_size(devcfg_res)); + + drvdata.devcfg_addr = NULL; + return 0; +} + +/* Match table for of_platform binding */ +#ifdef CONFIG_OF +static const struct of_device_id fpgaperipheral_of_match[] = { + { .compatible = FPGAPERIPHERAL_NAME, }, + {} +}; +MODULE_DEVICE_TABLE(of, fpgaperipheral_of_match); +#else +#define fpgaperipheral_of_match NULL +#endif + +static struct platform_driver fpgaperipheral_platform_driver = { + .probe = fpgaperipheral_probe, /* Probe method */ + .remove = __exit_p(fpgaperipheral_remove), /* Detach method */ + .driver = { + .owner = THIS_MODULE, + .name = FPGAPERIPHERAL_NAME, /* Driver name */ + .of_match_table = fpgaperipheral_of_match, + }, +}; + +/** + * fpgaperipheral_init - Initial driver registration call + * Returns 0 on success, negative error otherwise + */ +static int __init fpgaperipheral_init(void) +{ + return platform_driver_register(&fpgaperipheral_platform_driver); +} +module_init(fpgaperipheral_init); + +/** + * fpgaperipheral_exit - Driver unregistration call + * Returns 0 on success, negative error otherwise + */ +static void __exit fpgaperipheral_exit(void) +{ + platform_driver_unregister(&fpgaperipheral_platform_driver); +} +module_exit(fpgaperipheral_exit); + +MODULE_DESCRIPTION("Bus driver for FPGA peripherals on NI's Zynq-based controllers"); +MODULE_AUTHOR("Kyle Teske "); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/nizynqcpld.c b/drivers/misc/nizynqcpld.c new file mode 100644 index 0000000000000..ce990ac4a6c76 --- /dev/null +++ b/drivers/misc/nizynqcpld.c @@ -0,0 +1,1762 @@ +/* + * Copyright (C) 2012 National Instruments Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NIZYNQCPLD_VERSION 0x00 +#define NIZYNQCPLD_PRODUCT 0x1D + +#define PROTO_PROCESSORMODE 0x01 +#define PROTO_SWITCHANDLED 0x04 +#define PROTO_ETHERNETLED 0x05 +#define PROTO_SCRATCHPADSR 0xFE +#define PROTO_SCRATCHPADHR 0xFF + +#define DOSX_PROCESSORRESET 0x02 +#define DOSX_PROCRESETSOURCE 0x04 +#define DOSX_STATUSLEDSHIFTBYTE1 0x05 +#define DOSX_STATUSLEDSHIFTBYTE0 0x06 +#define DOSX_LED 0x07 +#define DOSX_ETHERNETLED 0x08 +#define DOSX_DEBUGSWITCH 0x09 +#define DOSX_POWERSTATUS 0x0C +#define DOSX_WATCHDOGCONTROL 0x13 +#define DOSX_WATCHDOGCOUNTER2 0x14 +#define DOSX_WATCHDOGCOUNTER1 0x15 +#define DOSX_WATCHDOGCOUNTER0 0x16 +#define DOSX_WATCHDOGSEED2 0x17 +#define DOSX_WATCHDOGSEED1 0x18 +#define DOSX_WATCHDOGSEED0 0x19 +#define DOSX_SCRATCHPADSR 0x1E +#define DOSX_SCRATCHPADHR 0x1F + +#define DOSX_WATCHDOGCONTROL_PROC_INTERRUPT 0x40 +#define DOSX_WATCHDOGCONTROL_PROC_RESET 0x20 +#define DOSX_WATCHDOGCONTROL_ENTER_USER_MODE 0x80 +#define DOSX_WATCHDOGCONTROL_PET 0x10 +#define DOSX_WATCHDOGCONTROL_RUNNING 0x08 +#define DOSX_WATCHDOGCONTROL_CAPTURECOUNTER 0x04 +#define DOSX_WATCHDOGCONTROL_RESET 0x02 +#define DOSX_WATCHDOGCONTROL_ALARM 0x01 + +#define DOSX_WATCHDOGCONTROL_RW_BITS \ + (DOSX_WATCHDOGCONTROL_PROC_INTERRUPT | DOSX_WATCHDOGCONTROL_PROC_RESET) + +#define DOSX_WATCHDOG_MAX_COUNTER 0x00FFFFFF +#define DOSX_WATCHDOG_COUNTER_BYTES 3 + +#define MYRIO_WIFISWCTRL_ADDR 0x0A +#define MYRIO_WIFISWCTRL_STATE 0x01 +#define MYRIO_WIFISWCTRL_ENPUSHIRQ 0x08 +#define MYRIO_WIFISWCTRL_PUSHIRQ 0x80 +#define MYRIO_WIFISWCTRL_ENRELIRQ 0x04 +#define MYRIO_WIFISWCTRL_RELIRQ 0x40 + +#define ELVISIII_WIFILEDCTRL_ADDR 0x0A + +struct nizynqcpld_led_desc { + const char *of_node_name; + u8 addr; + u8 bit; + u8 pattern_lo_addr; + u8 pattern_hi_addr; +}; + +struct nizynqcpld; + +struct nizynqcpld_led { + struct nizynqcpld *cpld; + struct nizynqcpld_led_desc *desc; + unsigned on:1; + struct led_classdev cdev; + struct work_struct deferred_work; + u16 blink_pattern; +}; + +#define to_nizynqcpld_led(x) \ + container_of(x, struct nizynqcpld_led, cdev) + +struct nizynqcpld_watchdog_desc { + u32 watchdog_period_ns; +}; + +struct nizynqcpld_watchdog { + struct miscdevice misc_dev; + struct nizynqcpld_watchdog_desc *desc; + atomic_t available; + wait_queue_head_t irq_event; + bool expired; +}; + +struct myrio_wifi_sw { + struct input_dev *idev; + struct work_struct deferred_work; + bool irq_registered; + int irq; +}; + +struct nizynqcpld_desc { + const struct attribute **attrs; + u8 supported_version; + u8 supported_product; + struct nizynqcpld_led_desc *led_descs; + unsigned int num_led_descs; + struct nizynqcpld_watchdog_desc *watchdog_desc; + u8 reboot_addr; + u8 scratch_hr_addr; + u8 scratch_sr_addr; + u8 switch_addr; + u8 watchdog_addr; + u8 wifi_sw_addr; +}; + +struct nizynqcpld { + struct device *dev; + int irq; + struct nizynqcpld_desc *desc; + struct nizynqcpld_led *leds; + struct nizynqcpld_watchdog watchdog; + struct i2c_client *client; + struct mutex lock; + struct notifier_block restart_nb; + struct myrio_wifi_sw wifi_sw; +}; + +static int nizynqcpld_write(struct nizynqcpld *cpld, u8 reg, u8 data); +static int nizynqcpld_read(struct nizynqcpld *cpld, u8 reg, u8 *data); + +static inline void nizynqcpld_lock(struct nizynqcpld *cpld) +{ + mutex_lock(&cpld->lock); +} +static inline void nizynqcpld_unlock(struct nizynqcpld *cpld) +{ + mutex_unlock(&cpld->lock); +} + +/* Can't issue i2c transfers in set_brightness, because + * they can sleep + */ +static void nizynqcpld_set_brightness_work(struct work_struct *work) +{ + struct nizynqcpld_led *led = container_of(work, struct nizynqcpld_led, + deferred_work); + struct nizynqcpld_led_desc *desc = led->desc; + struct nizynqcpld *cpld = led->cpld; + int err; + u8 tmp; + + nizynqcpld_lock(cpld); + + err = nizynqcpld_read(cpld, desc->addr, &tmp); + if (err) + goto unlock_out; + + tmp &= ~desc->bit; + if (led->on) + tmp |= desc->bit; + + nizynqcpld_write(cpld, desc->addr, tmp); + + if (desc->pattern_lo_addr && desc->pattern_hi_addr) { + /* spec says to write byte 1 first */ + nizynqcpld_write(cpld, desc->pattern_hi_addr, + led->blink_pattern >> 8); + nizynqcpld_write(cpld, desc->pattern_lo_addr, + led->blink_pattern & 0xff); + } + +unlock_out: + nizynqcpld_unlock(cpld); +} + +static void nizynqcpld_led_set_brightness(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct nizynqcpld_led *led = to_nizynqcpld_led(led_cdev); + + led->on = !!brightness; + /* some LED's support a blink pattern instead of a variable brightness, + * and blink_set isn't flexible enough for the supported patterns + */ + led->blink_pattern = brightness; + schedule_work(&led->deferred_work); +} + +static enum led_brightness +nizynqcpld_led_get_brightness(struct led_classdev *led_cdev) +{ + struct nizynqcpld_led *led = to_nizynqcpld_led(led_cdev); + struct nizynqcpld_led_desc *desc = led->desc; + struct nizynqcpld *cpld = led->cpld; + u8 tmp; + + nizynqcpld_lock(cpld); + /* can't handle an error here, so, roll with it. */ + nizynqcpld_read(cpld, desc->addr, &tmp); + nizynqcpld_unlock(cpld); + + /* for the status LED, the blink pattern used for brightness on write + * is write-only, so we just return on/off for all LED's + */ + return tmp & desc->bit ? LED_FULL : 0; +} + +static int nizynqcpld_write(struct nizynqcpld *cpld, u8 reg, u8 data) +{ + int err; + u8 tdata[2] = { reg, data }; + + /* write reg byte, then data */ + struct i2c_msg msg = { + .addr = cpld->client->addr, + .len = 2, + .buf = tdata, + }; + + err = i2c_transfer(cpld->client->adapter, &msg, 1); + + return err == 1 ? 0 : err; +} + +static int nizynqcpld_led_register(struct nizynqcpld *cpld, + struct nizynqcpld_led_desc *desc, + struct nizynqcpld_led *led) +{ + int err; + u8 tmp; + struct device_node *leds_node; + struct device_node *node; + + nizynqcpld_lock(cpld); + err = nizynqcpld_read(cpld, desc->addr, &tmp); + nizynqcpld_unlock(cpld); + + if (err) + goto err_out; + + led->cpld = cpld; + led->desc = desc; + led->on = !!(tmp & desc->bit); + INIT_WORK(&led->deferred_work, nizynqcpld_set_brightness_work); + + /* Find the LEDs node. */ + leds_node = of_find_node_by_name(cpld->client->dev.of_node, + "leds"); + if (!leds_node) + return -ENOENT; + + /* For the current led, get the device node under 'leds'. */ + node = of_get_child_by_name(leds_node, desc->of_node_name); + if (!node) { + /* Don't register the LED if the LED is not specified in device + * tree. + */ + err = -ENOENT; + goto err_out; + } + + /* Read the label from device tree. */ + led->cdev.name = of_get_property(node, "label", NULL) ? : node->name; + + /* Read the default trigger from device tree. */ + led->cdev.default_trigger = of_get_property(node, + "linux,default-trigger", NULL); + + /* Read the max brightness from device tree. */ + led->cdev.max_brightness = 1; /* Default to max of 1 */ + of_property_read_u32(node, "max-brightness", + &led->cdev.max_brightness); + + led->cdev.brightness_set = nizynqcpld_led_set_brightness; + led->cdev.brightness_get = nizynqcpld_led_get_brightness; + + err = led_classdev_register(cpld->dev, &led->cdev); + if (err) { + dev_err(cpld->dev, "error registering led.\n"); + goto err_led; + } + +err_led: + of_node_put(node); +err_out: + return err; +} + +static void nizynqcpld_led_unregister(struct nizynqcpld_led *led) +{ + led_classdev_unregister(&led->cdev); +} + + +static int nizynqcpld_read(struct nizynqcpld *cpld, u8 reg, u8 *data) +{ + int err; + + /* First, write the CPLD register offset, then read the data. */ + struct i2c_msg msgs[] = { + { + .addr = cpld->client->addr, + .len = 1, + .buf = ®, + }, + { + .addr = cpld->client->addr, + .flags = I2C_M_RD, + .len = 1, + .buf = data, + }, + }; + + err = i2c_transfer(cpld->client->adapter, msgs, ARRAY_SIZE(msgs)); + + return err == ARRAY_SIZE(msgs) ? 0 : err; +} + +static int nizynqcpld_restart(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct nizynqcpld *cpld = container_of(nb, struct nizynqcpld, + restart_nb); + struct nizynqcpld_desc *desc = cpld->desc; + + nizynqcpld_write(cpld, desc->reboot_addr, 0x80); + return 0; +} + +static inline ssize_t nizynqcpld_scratch_show(struct nizynqcpld *cpld, + struct device_attribute *attr, + char *buf, u8 reg_addr) +{ + u8 data; + int err; + + nizynqcpld_lock(cpld); + err = nizynqcpld_read(cpld, reg_addr, &data); + nizynqcpld_unlock(cpld); + + if (err) { + dev_err(cpld->dev, "Error reading scratch register state.\n"); + return err; + } + + return sprintf(buf, "%02x\n", data); +} + +static ssize_t nizynqcpld_scratchsr_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct nizynqcpld *cpld = dev_get_drvdata(dev); + struct nizynqcpld_desc *desc = cpld->desc; + + return nizynqcpld_scratch_show(cpld, attr, buf, desc->scratch_sr_addr); +} + +static ssize_t nizynqcpld_scratchhr_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nizynqcpld *cpld = dev_get_drvdata(dev); + struct nizynqcpld_desc *desc = cpld->desc; + + return nizynqcpld_scratch_show(cpld, attr, buf, desc->scratch_hr_addr); +} + +static inline ssize_t nizynqcpld_scratch_store(struct nizynqcpld *cpld, + struct device_attribute *attr, + const char *buf, size_t count, + u8 reg_addr) +{ + unsigned long tmp; + u8 data; + int err; + + err = kstrtoul(buf, 0, &tmp); + if (err) + return err; + + data = (u8) tmp; + + nizynqcpld_lock(cpld); + err = nizynqcpld_write(cpld, reg_addr, data); + nizynqcpld_unlock(cpld); + + if (err) { + dev_err(cpld->dev, + "Error writing to scratch register.\n"); + return err; + } + + return count; +} + +static ssize_t nizynqcpld_scratchsr_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nizynqcpld *cpld = dev_get_drvdata(dev); + struct nizynqcpld_desc *desc = cpld->desc; + + return nizynqcpld_scratch_store(cpld, attr, buf, count, + desc->scratch_sr_addr); +} + +static ssize_t nizynqcpld_scratchhr_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nizynqcpld *cpld = dev_get_drvdata(dev); + struct nizynqcpld_desc *desc = cpld->desc; + + return nizynqcpld_scratch_store(cpld, attr, buf, count, + desc->scratch_hr_addr); +} + +static DEVICE_ATTR(scratch_softreset, S_IRUSR|S_IWUSR, + nizynqcpld_scratchsr_show, nizynqcpld_scratchsr_store); +static DEVICE_ATTR(scratch_hardreset, S_IRUSR|S_IWUSR, + nizynqcpld_scratchhr_show, nizynqcpld_scratchhr_store); + +struct switch_attribute { + struct device_attribute dev_attr; + u8 bit; +}; + +static inline ssize_t nizynqcpld_switch_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct nizynqcpld *cpld = dev_get_drvdata(dev); + struct nizynqcpld_desc *desc = cpld->desc; + struct switch_attribute *sa = + container_of(attr, struct switch_attribute, dev_attr); + int err; + u8 data; + + nizynqcpld_lock(cpld); + err = nizynqcpld_read(cpld, desc->switch_addr, &data); + nizynqcpld_unlock(cpld); + + if (err) { + dev_err(dev, "Error reading switch state.\n"); + return err; + } + + return sprintf(buf, "%u\n", !!(data & sa->bit)); +} + +#define SWITCH_ATTR(_name, _bit) \ + struct switch_attribute dev_attr_##_name = { \ + .bit = _bit, \ + .dev_attr = \ + __ATTR(_name, 0444, nizynqcpld_switch_show, NULL), \ + } + +static SWITCH_ATTR(soft_reset, 1 << 5); +static SWITCH_ATTR(console_out, 1 << 2); +static SWITCH_ATTR(ip_reset, 1 << 1); +static SWITCH_ATTR(safe_mode, 1 << 0); + +static const char * const bootmode_strings[] = { + "runtime", "safemode", "install", +}; + +static ssize_t nizynqcpld_bootmode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct nizynqcpld *cpld = dev_get_drvdata(dev); + struct nizynqcpld_desc *desc = cpld->desc; + int err; + u8 tmp; + + nizynqcpld_lock(cpld); + err = nizynqcpld_read(cpld, desc->scratch_hr_addr, &tmp); + nizynqcpld_unlock(cpld); + + if (err) + return err; + + tmp &= 0x3; + if (tmp >= ARRAY_SIZE(bootmode_strings)) + return -EINVAL; + + return sprintf(buf, "%s\n", bootmode_strings[tmp]); +} + +static int nizynqcpld_set_bootmode(struct nizynqcpld *cpld, u8 mode) +{ + struct nizynqcpld_desc *desc = cpld->desc; + int err; + u8 tmp; + + nizynqcpld_lock(cpld); + + err = nizynqcpld_read(cpld, desc->scratch_hr_addr, &tmp); + if (err) + goto unlock_out; + + tmp &= ~0x3; + tmp |= mode; + + err = nizynqcpld_write(cpld, desc->scratch_hr_addr, tmp); + +unlock_out: + nizynqcpld_unlock(cpld); + return err; +} + +static ssize_t nizynqcpld_bootmode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nizynqcpld *cpld = dev_get_drvdata(dev); + u8 i; + + for (i = 0; i < ARRAY_SIZE(bootmode_strings); i++) + if (!strcmp(buf, bootmode_strings[i])) + return nizynqcpld_set_bootmode(cpld, i) ?: count; + + return -EINVAL; +} + +static DEVICE_ATTR(bootmode, S_IRUSR|S_IWUSR, nizynqcpld_bootmode_show, + nizynqcpld_bootmode_store); + + +struct powerstatus_attribute { + struct device_attribute dev_attr; + u8 bit; +}; + +static inline ssize_t nizynqcpld_powerstatus_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct nizynqcpld *cpld = dev_get_drvdata(dev); + struct powerstatus_attribute *pa = + container_of(attr, struct powerstatus_attribute, dev_attr); + int err; + u8 data; + + nizynqcpld_lock(cpld); + err = nizynqcpld_read(cpld, DOSX_POWERSTATUS, &data); + nizynqcpld_unlock(cpld); + + if (err) { + dev_err(dev, "Error reading power status.\n"); + return err; + } + + return sprintf(buf, "%u\n", !!(data & pa->bit)); +} + +#define POWERSTATUS_ATTR(_name, _bit) \ + struct powerstatus_attribute dev_attr_##_name = { \ + .bit = _bit, \ + .dev_attr = \ + __ATTR(_name, 0444, nizynqcpld_powerstatus_show, \ + NULL), \ + } + +static POWERSTATUS_ATTR(pwr_aux_valid, 1 << 4); +static POWERSTATUS_ATTR(pwr_primary_in_use, 1 << 0); + +static const struct attribute *nizynqcpld_pwr_attrs[] = { + &dev_attr_bootmode.attr, + &dev_attr_scratch_softreset.attr, + &dev_attr_scratch_hardreset.attr, + &dev_attr_soft_reset.dev_attr.attr, + &dev_attr_console_out.dev_attr.attr, + &dev_attr_ip_reset.dev_attr.attr, + &dev_attr_safe_mode.dev_attr.attr, + &dev_attr_pwr_aux_valid.dev_attr.attr, + &dev_attr_pwr_primary_in_use.dev_attr.attr, + NULL +}; + +static const struct attribute *nizynqcpld_attrs[] = { + &dev_attr_bootmode.attr, + &dev_attr_scratch_softreset.attr, + &dev_attr_scratch_hardreset.attr, + &dev_attr_soft_reset.dev_attr.attr, + &dev_attr_console_out.dev_attr.attr, + &dev_attr_ip_reset.dev_attr.attr, + &dev_attr_safe_mode.dev_attr.attr, + NULL +}; + +static ssize_t dosequiscpld_wdmode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct nizynqcpld *cpld = dev_get_drvdata(dev); + struct nizynqcpld_desc *desc = cpld->desc; + int err; + u8 tmp; + + nizynqcpld_lock(cpld); + err = nizynqcpld_read(cpld, desc->watchdog_addr, &tmp); + nizynqcpld_unlock(cpld); + + if (err) + return err; + + /* you write a 1 to the bit to enter user mode, but it reads as a + * 0 in user mode for backwards compatibility + */ + tmp &= DOSX_WATCHDOGCONTROL_ENTER_USER_MODE; + return sprintf(buf, "%s\n", tmp ? "boot" : "user"); +} + +static ssize_t dosequiscpld_wdmode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nizynqcpld *cpld = dev_get_drvdata(dev); + struct nizynqcpld_desc *desc = cpld->desc; + int err; + + /* you can only switch boot->user */ + if (strcmp(buf, "user")) + return -EINVAL; + + nizynqcpld_lock(cpld); + err = nizynqcpld_write(cpld, desc->watchdog_addr, + DOSX_WATCHDOGCONTROL_ENTER_USER_MODE); + nizynqcpld_unlock(cpld); + + return err ?: count; +} + +static DEVICE_ATTR(watchdog_mode, S_IRUSR|S_IWUSR, dosequiscpld_wdmode_show, + dosequiscpld_wdmode_store); + + +static const char * const resetsource_strings[] = { + "button", "processor", "fpga", "watchdog", "software", "softoff", +}; + +static ssize_t nizynqcpld_resetsource_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct nizynqcpld *cpld = dev_get_drvdata(dev); + int err; + u8 tmp; + int i; + + nizynqcpld_lock(cpld); + err = nizynqcpld_read(cpld, DOSX_PROCRESETSOURCE, &tmp); + nizynqcpld_unlock(cpld); + + if (err) + return err; + + for (i = 0; i < ARRAY_SIZE(resetsource_strings); i++) { + if ((1 << i) & tmp) + return sprintf(buf, "%s\n", resetsource_strings[i]); + } + + return sprintf(buf, "poweron\n"); +} + +static DEVICE_ATTR(reset_source, S_IRUSR, nizynqcpld_resetsource_show, NULL); + + +static const struct attribute *dosequis6_pwr_attrs[] = { + &dev_attr_bootmode.attr, + &dev_attr_scratch_softreset.attr, + &dev_attr_scratch_hardreset.attr, + &dev_attr_soft_reset.dev_attr.attr, + &dev_attr_console_out.dev_attr.attr, + &dev_attr_ip_reset.dev_attr.attr, + &dev_attr_safe_mode.dev_attr.attr, + &dev_attr_watchdog_mode.attr, + &dev_attr_reset_source.attr, + &dev_attr_pwr_aux_valid.dev_attr.attr, + &dev_attr_pwr_primary_in_use.dev_attr.attr, + NULL +}; + +static const struct attribute *dosequis6_attrs[] = { + &dev_attr_bootmode.attr, + &dev_attr_scratch_softreset.attr, + &dev_attr_scratch_hardreset.attr, + &dev_attr_soft_reset.dev_attr.attr, + &dev_attr_console_out.dev_attr.attr, + &dev_attr_ip_reset.dev_attr.attr, + &dev_attr_safe_mode.dev_attr.attr, + &dev_attr_watchdog_mode.attr, + &dev_attr_reset_source.attr, + NULL +}; + +/* + * CPLD Watchdog (only for dosequis) + */ +static int nizynqcpld_watchdog_counter_set(struct nizynqcpld *cpld, u32 counter) +{ + int err; + u8 data[DOSX_WATCHDOG_COUNTER_BYTES]; + + data[0] = ((0x00FF0000 & counter) >> 16); + data[1] = ((0x0000FF00 & counter) >> 8); + data[2] = (0x000000FF & counter); + + nizynqcpld_lock(cpld); + + err = i2c_smbus_write_i2c_block_data(cpld->client, + DOSX_WATCHDOGSEED2, + DOSX_WATCHDOG_COUNTER_BYTES, + data); + nizynqcpld_unlock(cpld); + if (err) + dev_err(cpld->dev, + "Error %d writing watchdog counter.\n", err); + return err; +} + +static int nizynqcpld_watchdog_check_action(u32 action) +{ + int err = 0; + + switch (action) { + case DOSX_WATCHDOGCONTROL_PROC_INTERRUPT: + case DOSX_WATCHDOGCONTROL_PROC_RESET: + break; + default: + err = -ENOTSUPP; + } + + return err; +} + +static int nizynqcpld_watchdog_add_action(struct nizynqcpld *cpld, u32 action) +{ + int err; + u8 action_bit; + + if (action == NIWATCHDOG_ACTION_INTERRUPT) + action_bit = DOSX_WATCHDOGCONTROL_PROC_INTERRUPT; + else if (action == NIWATCHDOG_ACTION_RESET) + action_bit = DOSX_WATCHDOGCONTROL_PROC_RESET; + else + return -ENOTSUPP; + + nizynqcpld_lock(cpld); + + err = nizynqcpld_write(cpld, DOSX_WATCHDOGCONTROL, action_bit); + + if (err) { + dev_err(cpld->dev, + "Error %d writing watchdog control.\n", err); + goto out_unlock; + } +out_unlock: + nizynqcpld_unlock(cpld); + return err; +} + +static int nizynqcpld_watchdog_start(struct nizynqcpld *cpld) +{ + int err; + u8 control; + + nizynqcpld_lock(cpld); + + cpld->watchdog.expired = false; + + err = nizynqcpld_read(cpld, DOSX_WATCHDOGCONTROL, &control); + if (err) { + dev_err(cpld->dev, + "Error %d reading watchdog control.\n", err); + goto out_unlock; + } + + /* Mask off all the read-only and write-only bits. */ + control &= DOSX_WATCHDOGCONTROL_RW_BITS; + + err = nizynqcpld_write(cpld, DOSX_WATCHDOGCONTROL, + control | DOSX_WATCHDOGCONTROL_RESET); + if (err) { + dev_err(cpld->dev, + "Error %d writing watchdog control.\n", err); + goto out_unlock; + } + + err = nizynqcpld_write(cpld, DOSX_WATCHDOGCONTROL, + control | DOSX_WATCHDOGCONTROL_PET); + if (err) { + dev_err(cpld->dev, + "Error %d writing watchdog control.\n", err); + goto out_unlock; + } +out_unlock: + nizynqcpld_unlock(cpld); + return err; +} + +static int nizynqcpld_watchdog_pet(struct nizynqcpld *cpld, u32 *state) +{ + int err; + u8 control; + + nizynqcpld_lock(cpld); + + if (cpld->watchdog.expired) { + err = 0; + *state = NIWATCHDOG_STATE_EXPIRED; + } else { + err = nizynqcpld_read(cpld, DOSX_WATCHDOGCONTROL, &control); + if (err) { + dev_err(cpld->dev, + "Error %d reading watchdog control.\n", err); + goto out_unlock; + } + + /* Mask off all the read-only and write-only bits. */ + control &= DOSX_WATCHDOGCONTROL_RW_BITS; + + control |= DOSX_WATCHDOGCONTROL_PET; + + err = nizynqcpld_write(cpld, DOSX_WATCHDOGCONTROL, control); + if (err) { + dev_err(cpld->dev, + "Error %d writing watchdog control.\n", err); + goto out_unlock; + } + + *state = NIWATCHDOG_STATE_RUNNING; + } + +out_unlock: + nizynqcpld_unlock(cpld); + return err; +} + +static int nizynqcpld_watchdog_reset(struct nizynqcpld *cpld) +{ + int err; + + nizynqcpld_lock(cpld); + + cpld->watchdog.expired = false; + + err = nizynqcpld_write(cpld, DOSX_WATCHDOGCONTROL, + DOSX_WATCHDOGCONTROL_RESET); + + nizynqcpld_unlock(cpld); + + if (err) + dev_err(cpld->dev, + "Error %d writing watchdog control.\n", err); + return err; +} + +static int nizynqcpld_watchdog_counter_get(struct nizynqcpld *cpld, + u32 *counter) +{ + int err; + u8 control; + u8 data[DOSX_WATCHDOG_COUNTER_BYTES]; + + nizynqcpld_lock(cpld); + + err = nizynqcpld_read(cpld, DOSX_WATCHDOGCONTROL, &control); + + if (err) { + dev_err(cpld->dev, + "Error %d reading watchdog control.\n", err); + goto out_unlock; + } + + /* Mask off all the read-only and write-only bits. */ + control &= DOSX_WATCHDOGCONTROL_RW_BITS; + + err = nizynqcpld_write(cpld, DOSX_WATCHDOGCONTROL, + control | DOSX_WATCHDOGCONTROL_CAPTURECOUNTER); + if (err) { + dev_err(cpld->dev, + "Error %d capturing watchdog counter.\n", err); + goto out_unlock; + } + + /* Returns the number of read bytes */ + err = i2c_smbus_read_i2c_block_data(cpld->client, + DOSX_WATCHDOGCOUNTER2, + DOSX_WATCHDOG_COUNTER_BYTES, + data); + if (err == DOSX_WATCHDOG_COUNTER_BYTES) + err = 0; + else { + dev_err(cpld->dev, + "Error %d reading watchdog counter.\n", err); + goto out_unlock; + } + + *counter = (data[0] << 16) | (data[1] << 8) | data[2]; + +out_unlock: + nizynqcpld_unlock(cpld); + return err; +} + +static irqreturn_t nizynqcpld_watchdog_irq(int irq, void *data) +{ + struct nizynqcpld *cpld = data; + irqreturn_t ret = IRQ_NONE; + u8 control; + int err; + + nizynqcpld_lock(cpld); + + cpld->watchdog.expired = true; + + /* Signal the watchdog event. */ + wake_up_all(&cpld->watchdog.irq_event); + + err = nizynqcpld_read(cpld, DOSX_WATCHDOGCONTROL, &control); + + if (err) { + dev_err(cpld->dev, + "Error %d reading watchdog control.\n", err); + goto out_unlock; + } else if (!(DOSX_WATCHDOGCONTROL_ALARM & control)) { + dev_err(cpld->dev, + "Spurious watchdog interrupt, 0x%02X\n", control); + goto out_unlock; + } + + /* Mask off all the read-only and write-only bits. */ + control &= DOSX_WATCHDOGCONTROL_RW_BITS; + + /* Acknowledge the interrupt. */ + control |= DOSX_WATCHDOGCONTROL_RESET; + err = nizynqcpld_write(cpld, DOSX_WATCHDOGCONTROL, control); + + ret = IRQ_HANDLED; + +out_unlock: + nizynqcpld_unlock(cpld); + return ret; +} + +static int nizynqcpld_watchdog_misc_open(struct inode *inode, + struct file *file) +{ + struct miscdevice *misc_dev = file->private_data; + struct nizynqcpld *cpld = container_of(misc_dev, struct nizynqcpld, + watchdog.misc_dev); + + file->private_data = cpld; + + if (!atomic_dec_and_test(&cpld->watchdog.available)) { + atomic_inc(&cpld->watchdog.available); + return -EBUSY; + } + + return 0; +} + +static int nizynqcpld_watchdog_misc_release(struct inode *inode, + struct file *file) +{ + struct nizynqcpld *cpld = file->private_data; + + atomic_inc(&cpld->watchdog.available); + return 0; +} + +static +long nizynqcpld_watchdog_misc_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct nizynqcpld *cpld = file->private_data; + struct nizynqcpld_watchdog_desc *desc; + int err; + + desc = cpld->watchdog.desc; + + switch (cmd) { + case NIWATCHDOG_IOCTL_PERIOD_NS: { + __u32 period = desc->watchdog_period_ns; + + err = copy_to_user((__u32 *)arg, &period, + sizeof(__u32)); + break; + } + case NIWATCHDOG_IOCTL_MAX_COUNTER: { + __u32 counter = DOSX_WATCHDOG_MAX_COUNTER; + + err = copy_to_user((__u32 *)arg, &counter, + sizeof(__u32)); + break; + } + case NIWATCHDOG_IOCTL_COUNTER_SET: { + __u32 counter; + + err = copy_from_user(&counter, (__u32 *)arg, + sizeof(__u32)); + if (!err) + err = nizynqcpld_watchdog_counter_set(cpld, counter); + break; + } + case NIWATCHDOG_IOCTL_CHECK_ACTION: { + __u32 action; + + err = copy_from_user(&action, (__u32 *)arg, + sizeof(__u32)); + if (!err) + err = nizynqcpld_watchdog_check_action(action); + break; + } + case NIWATCHDOG_IOCTL_ADD_ACTION: { + __u32 action; + + err = copy_from_user(&action, (__u32 *)arg, + sizeof(__u32)); + if (!err) + err = nizynqcpld_watchdog_add_action(cpld, action); + break; + } + case NIWATCHDOG_IOCTL_START: { + err = nizynqcpld_watchdog_start(cpld); + break; + } + case NIWATCHDOG_IOCTL_PET: { + __u32 state; + + err = nizynqcpld_watchdog_pet(cpld, &state); + if (!err) + err = copy_to_user((__u32 *)arg, &state, + sizeof(__u32)); + break; + } + case NIWATCHDOG_IOCTL_RESET: { + err = nizynqcpld_watchdog_reset(cpld); + break; + } + case NIWATCHDOG_IOCTL_COUNTER_GET: { + __u32 counter; + + err = nizynqcpld_watchdog_counter_get(cpld, &counter); + if (!err) { + err = copy_to_user((__u32 *)arg, &counter, + sizeof(__u32)); + } + break; + } + default: + err = -EINVAL; + break; + }; + + return err; +} + +static +unsigned int nizynqcpld_watchdog_misc_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct nizynqcpld *cpld = file->private_data; + poll_wait(file, &cpld->watchdog.irq_event, wait); + if (READ_ONCE(cpld->watchdog.expired)) + return POLLIN; + return 0; +} + +static const struct file_operations nizynqcpld_watchdog_misc_fops = { + .owner = THIS_MODULE, + .open = nizynqcpld_watchdog_misc_open, + .release = nizynqcpld_watchdog_misc_release, + .unlocked_ioctl = nizynqcpld_watchdog_misc_ioctl, + .poll = nizynqcpld_watchdog_misc_poll, +}; + +static struct nizynqcpld_led_desc proto_leds[] = { + { + .of_node_name = "user1-0", + .addr = PROTO_SWITCHANDLED, + .bit = 1 << 4, + }, + { + .of_node_name = "user1-1", + .addr = PROTO_SWITCHANDLED, + .bit = 1 << 3, + }, + { + .of_node_name = "status-0", + .addr = PROTO_SWITCHANDLED, + .bit = 1 << 2, + }, + { + .of_node_name = "eth1-0", + .addr = PROTO_ETHERNETLED, + .bit = 1 << 1, + }, + { + .of_node_name = "eth1-1", + .addr = PROTO_ETHERNETLED, + .bit = 1 << 0, + }, +}; + +static struct nizynqcpld_led_desc dosx_leds[] = { + { + .of_node_name = "user1-0", + .addr = DOSX_LED, + .bit = 1 << 5, + }, + { + .of_node_name = "user1-1", + .addr = DOSX_LED, + .bit = 1 << 4, + }, + { + .of_node_name = "status-0", + .addr = DOSX_LED, + .bit = 1 << 3, + }, + { + .of_node_name = "status-1", + .addr = DOSX_LED, + .bit = 1 << 2, + .pattern_lo_addr = DOSX_STATUSLEDSHIFTBYTE0, + .pattern_hi_addr = DOSX_STATUSLEDSHIFTBYTE1, + }, + { + .of_node_name = "wifi-0", + .addr = DOSX_ETHERNETLED, + .bit = 1 << 5, + }, + { + .of_node_name = "wifi-1", + .addr = DOSX_ETHERNETLED, + .bit = 1 << 4, + }, + { + .of_node_name = "eth1-0", + .addr = DOSX_ETHERNETLED, + .bit = 1 << 3, + }, + { + .of_node_name = "eth1-1", + .addr = DOSX_ETHERNETLED, + .bit = 1 << 2, + }, + { + .of_node_name = "eth0-0", + .addr = DOSX_ETHERNETLED, + .bit = 1 << 1, + }, + { + .of_node_name = "eth0-1", + .addr = DOSX_ETHERNETLED, + .bit = 1 << 0, + }, +}; + +static struct nizynqcpld_led_desc sol_leds[] = { + { + .of_node_name = "user1-0", + .addr = DOSX_LED, + .bit = 1 << 5, + }, + { + .of_node_name = "status-0", + .addr = DOSX_LED, + .bit = 1 << 2, + .pattern_lo_addr = DOSX_STATUSLEDSHIFTBYTE0, + /* write byte 1 first */ + .pattern_hi_addr = DOSX_STATUSLEDSHIFTBYTE1, + }, + { + .of_node_name = "eth1-0", + .addr = DOSX_ETHERNETLED, + .bit = 1 << 3, + }, + { + .of_node_name = "eth1-1", + .addr = DOSX_ETHERNETLED, + .bit = 1 << 2, + }, + { + .of_node_name = "eth0-0", + .addr = DOSX_ETHERNETLED, + .bit = 1 << 1, + }, + { + .of_node_name = "eth0-1", + .addr = DOSX_ETHERNETLED, + .bit = 1 << 0, + }, +}; + +static struct nizynqcpld_led_desc elvisiii_leds[] = { + { + .of_node_name = "status-0", + .addr = DOSX_LED, + .bit = 1 << 2, + .pattern_lo_addr = DOSX_STATUSLEDSHIFTBYTE0, + /* write byte 1 first */ + .pattern_hi_addr = DOSX_STATUSLEDSHIFTBYTE1, + }, + { + .of_node_name = "eth0-0", + .addr = DOSX_ETHERNETLED, + .bit = 1 << 1, + }, + { + .of_node_name = "eth0-1", + .addr = DOSX_ETHERNETLED, + .bit = 1 << 0, + }, + { + .of_node_name = "wifi-0", + .addr = ELVISIII_WIFILEDCTRL_ADDR, + .bit = 1 << 1, + }, + { + .of_node_name = "wifi-1", + .addr = ELVISIII_WIFILEDCTRL_ADDR, + .bit = 1 << 0, + }, +}; + +static struct nizynqcpld_watchdog_desc dosxv4_watchdog_desc = { + .watchdog_period_ns = 24000, +}; + +static struct nizynqcpld_watchdog_desc dosxv5_watchdog_desc = { + .watchdog_period_ns = 30720, +}; + +static struct nizynqcpld_desc nizynqcpld_descs[] = { + /* DosEquis and myRIO development CPLD */ + { + .attrs = nizynqcpld_attrs, + .supported_version = 3, + .supported_product = 0, + .led_descs = proto_leds, + .num_led_descs = ARRAY_SIZE(proto_leds), + .reboot_addr = PROTO_PROCESSORMODE, + .scratch_hr_addr = PROTO_SCRATCHPADHR, + .scratch_sr_addr = PROTO_SCRATCHPADSR, + .switch_addr = PROTO_PROCESSORMODE, + }, + /* DosEquis and myRIO development CPLD */ + { + .attrs = nizynqcpld_pwr_attrs, + .supported_version = 4, + .supported_product = 0, + .watchdog_desc = &dosxv4_watchdog_desc, + .led_descs = dosx_leds, + .num_led_descs = ARRAY_SIZE(dosx_leds), + .reboot_addr = DOSX_PROCESSORRESET, + .scratch_hr_addr = DOSX_SCRATCHPADHR, + .scratch_sr_addr = DOSX_SCRATCHPADSR, + .switch_addr = PROTO_PROCESSORMODE, + }, + /* DosEquis and myRIO development CPLD */ + { + .attrs = nizynqcpld_pwr_attrs, + .supported_version = 5, + .supported_product = 0, + .watchdog_desc = &dosxv5_watchdog_desc, + .led_descs = dosx_leds, + .num_led_descs = ARRAY_SIZE(dosx_leds), + .reboot_addr = DOSX_PROCESSORRESET, + .scratch_hr_addr = DOSX_SCRATCHPADHR, + .scratch_sr_addr = DOSX_SCRATCHPADSR, + .switch_addr = PROTO_PROCESSORMODE, + }, + /* DosEquis CPLD */ + { + .attrs = dosequis6_pwr_attrs, + .supported_version = 6, + .supported_product = 0, + .watchdog_desc = &dosxv5_watchdog_desc, + .led_descs = dosx_leds, + .num_led_descs = ARRAY_SIZE(dosx_leds), + .reboot_addr = DOSX_PROCESSORRESET, + .scratch_hr_addr = DOSX_SCRATCHPADHR, + .scratch_sr_addr = DOSX_SCRATCHPADSR, + .switch_addr = PROTO_PROCESSORMODE, + .watchdog_addr = DOSX_WATCHDOGCONTROL, + }, + /* myRIO CPLD */ + { + .attrs = dosequis6_attrs, + .supported_version = 6, + .supported_product = 1, + .watchdog_desc = &dosxv5_watchdog_desc, + .led_descs = dosx_leds, + .num_led_descs = ARRAY_SIZE(dosx_leds), + .reboot_addr = DOSX_PROCESSORRESET, + .scratch_hr_addr = DOSX_SCRATCHPADHR, + .scratch_sr_addr = DOSX_SCRATCHPADSR, + .switch_addr = PROTO_PROCESSORMODE, + .watchdog_addr = DOSX_WATCHDOGCONTROL, + .wifi_sw_addr = MYRIO_WIFISWCTRL_ADDR, + }, + /* Tecate */ + { + .attrs = dosequis6_attrs, + .supported_version = 1, + .supported_product = 2, + .watchdog_desc = &dosxv5_watchdog_desc, + .led_descs = sol_leds, + .num_led_descs = ARRAY_SIZE(sol_leds), + .reboot_addr = DOSX_PROCESSORRESET, + .scratch_hr_addr = DOSX_SCRATCHPADHR, + .scratch_sr_addr = DOSX_SCRATCHPADSR, + .switch_addr = PROTO_PROCESSORMODE, + .watchdog_addr = DOSX_WATCHDOGCONTROL, + }, + /* Sol CPLD */ + { + .attrs = dosequis6_attrs, + .supported_version = 1, + .supported_product = 3, + .watchdog_desc = &dosxv5_watchdog_desc, + .led_descs = sol_leds, + .num_led_descs = ARRAY_SIZE(sol_leds), + .reboot_addr = DOSX_PROCESSORRESET, + .scratch_hr_addr = DOSX_SCRATCHPADHR, + .scratch_sr_addr = DOSX_SCRATCHPADSR, + .switch_addr = PROTO_PROCESSORMODE, + .watchdog_addr = DOSX_WATCHDOGCONTROL, + }, + /* sbZynq CPLD */ + { + .attrs = dosequis6_attrs, + .supported_version = 1, + .supported_product = 4, + .watchdog_desc = &dosxv5_watchdog_desc, + .led_descs = sol_leds, + .num_led_descs = ARRAY_SIZE(sol_leds), + .reboot_addr = DOSX_PROCESSORRESET, + .scratch_hr_addr = DOSX_SCRATCHPADHR, + .scratch_sr_addr = DOSX_SCRATCHPADSR, + .switch_addr = PROTO_PROCESSORMODE, + .watchdog_addr = DOSX_WATCHDOGCONTROL, + }, + /* ELVIS III CPLD */ + { + .attrs = dosequis6_attrs, + .supported_version = 1, + .supported_product = 6, + .watchdog_desc = &dosxv5_watchdog_desc, + .led_descs = elvisiii_leds, + .num_led_descs = ARRAY_SIZE(elvisiii_leds), + .reboot_addr = DOSX_PROCESSORRESET, + .scratch_hr_addr = DOSX_SCRATCHPADHR, + .scratch_sr_addr = DOSX_SCRATCHPADSR, + .switch_addr = PROTO_PROCESSORMODE, + .watchdog_addr = DOSX_WATCHDOGCONTROL, + }, + /* roboRIO 2.0 CPLD */ + { + .attrs = dosequis6_attrs, + .supported_version = 1, + .supported_product = 7, + .watchdog_desc = &dosxv5_watchdog_desc, + .led_descs = dosx_leds, + .num_led_descs = ARRAY_SIZE(dosx_leds), + .reboot_addr = DOSX_PROCESSORRESET, + .scratch_hr_addr = DOSX_SCRATCHPADHR, + .scratch_sr_addr = DOSX_SCRATCHPADSR, + .switch_addr = PROTO_PROCESSORMODE, + .watchdog_addr = DOSX_WATCHDOGCONTROL, + }, +}; + +static void wifi_sw_work_func(struct work_struct *work) +{ + struct myrio_wifi_sw *wifi_sw = + container_of(work, struct myrio_wifi_sw, deferred_work); + struct nizynqcpld *cpld = + container_of(wifi_sw, struct nizynqcpld, wifi_sw); + u8 data; + int err; + + nizynqcpld_lock(cpld); + err = nizynqcpld_read(cpld, cpld->desc->wifi_sw_addr, &data); + nizynqcpld_unlock(cpld); + + if (err) { + dev_err(&cpld->client->dev, + "error %d reading wifi_sw control register\n", err); + return; + } + + /* check the interrupt flags and clear them */ + if (data & (MYRIO_WIFISWCTRL_PUSHIRQ | MYRIO_WIFISWCTRL_RELIRQ)) { + nizynqcpld_lock(cpld); + err = nizynqcpld_write(cpld, cpld->desc->wifi_sw_addr, data); + nizynqcpld_unlock(cpld); + + if (err) { + dev_err(&cpld->client->dev, + "err %d clearing wifi_sw irq flag\n", err); + return; + } + } + + input_event(wifi_sw->idev, + EV_KEY, BTN_0, !!(data & MYRIO_WIFISWCTRL_STATE)); + input_sync(wifi_sw->idev); +} + +static irqreturn_t wifi_sw_hnd(int irq, void *irq_data) +{ + struct myrio_wifi_sw *wifi_sw = (struct myrio_wifi_sw *)irq_data; + + schedule_work(&wifi_sw->deferred_work); + + return IRQ_HANDLED; +} + +static int wifi_sw_open(struct input_dev *dev) +{ + int err; + struct myrio_wifi_sw *wifi_sw = input_get_drvdata(dev); + struct nizynqcpld *cpld = + container_of(wifi_sw, struct nizynqcpld, wifi_sw); + + err = request_threaded_irq(wifi_sw->irq, NULL, wifi_sw_hnd, + IRQF_ONESHOT, "wifi_sw", wifi_sw); + if (err) { + dev_err(&cpld->client->dev, + "error %d registering irq handle for wifi_sw\n", err); + } + + wifi_sw->irq_registered = !err; + return err; +} + +static void wifi_sw_close(struct input_dev *dev) +{ + struct myrio_wifi_sw *wifi_sw = input_get_drvdata(dev); + + if (wifi_sw->irq_registered) + free_irq(wifi_sw->irq, wifi_sw); +} + +static int myrio_wifi_sw_init(struct nizynqcpld *cpld) +{ + u8 data; + int err = 0; + struct input_dev *input; + struct device_node *switches_node; + struct device_node *wifi_switch_node; + + if (!cpld->desc->wifi_sw_addr) + goto wifi_sw_init_exit; + + INIT_WORK(&cpld->wifi_sw.deferred_work, wifi_sw_work_func); + + /* Find the switches node. */ + switches_node = of_find_node_by_name(cpld->client->dev.of_node, + "switches"); + if (!switches_node) + goto wifi_sw_init_exit; + + /* Find the WiFi switch node. */ + wifi_switch_node = of_find_node_by_name(switches_node, "wifi-switch"); + if (!wifi_switch_node) + goto wifi_sw_init_exit; + + cpld->wifi_sw.irq = irq_of_parse_and_map(wifi_switch_node, 0); + + /* ensure that the interrupt was mapped */ + if (!cpld->wifi_sw.irq) + goto wifi_sw_init_exit; + + /* enable interrupts */ + nizynqcpld_lock(cpld); + err = nizynqcpld_read(cpld, cpld->desc->wifi_sw_addr, &data); + + if (!err) { + data |= (MYRIO_WIFISWCTRL_ENPUSHIRQ | + MYRIO_WIFISWCTRL_ENRELIRQ); + err = nizynqcpld_write(cpld, cpld->desc->wifi_sw_addr, data); + } + nizynqcpld_unlock(cpld); + + if (err) { + dev_err(&cpld->client->dev, + "error %d enabling wifi_sw irq\n", err); + goto wifi_sw_init_exit; + } + + input = input_allocate_device(); + cpld->wifi_sw.idev = input; + + if (!input) { + dev_err(&cpld->client->dev, + "error %d allocating input device for wifi_sw\n", err); + goto wifi_sw_init_exit; + } + + input->name = "wifi_btn"; + input->phys = "wifi_btn/wifibtn"; + input->open = wifi_sw_open; + input->close = wifi_sw_close; + input_set_capability(input, EV_KEY, BTN_0); + input_set_drvdata(input, &cpld->wifi_sw); + + err = input_register_device(cpld->wifi_sw.idev); + + /* report initial state */ + wifi_sw_work_func(&cpld->wifi_sw.deferred_work); + + if (err) { + dev_err(&cpld->client->dev, + "error %d registering input device for wifi_sw\n", err); + goto wifi_sw_init_exit_register; + } + + return err; + +wifi_sw_init_exit_register: + input_free_device(cpld->wifi_sw.idev); + +wifi_sw_init_exit: + return err; +} + +static void myrio_wifi_sw_uninit(struct nizynqcpld *cpld) +{ + if (cpld->desc->wifi_sw_addr) { + input_unregister_device(cpld->wifi_sw.idev); + input_free_device(cpld->wifi_sw.idev); + } +} + +static int nizynqcpld_probe(struct i2c_client *client) +{ + struct nizynqcpld_desc *desc; + struct nizynqcpld *cpld; + u8 version; + u8 product; + int err; + int i; + + cpld = kzalloc(sizeof(*cpld), GFP_KERNEL); + if (!cpld) { + err = -ENOMEM; + dev_err(&client->dev, "could not allocate private data.\n"); + goto err_cpld_alloc; + } + + cpld->dev = &client->dev; + cpld->client = client; + mutex_init(&cpld->lock); + + err = nizynqcpld_read(cpld, NIZYNQCPLD_VERSION, + &version); + if (err) { + dev_err(cpld->dev, "could not read version cpld version.\n"); + goto err_read_info; + } + + err = nizynqcpld_read(cpld, NIZYNQCPLD_PRODUCT, &product); + if (err) { + dev_err(cpld->dev, "could not read cpld product number.\n"); + goto err_read_info; + } + + for (i = 0, desc = NULL; i < ARRAY_SIZE(nizynqcpld_descs); i++) { + if (nizynqcpld_descs[i].supported_version == version && + nizynqcpld_descs[i].supported_product == product) { + desc = &nizynqcpld_descs[i]; + break; + } + } + + if (!desc) { + err = -ENODEV; + dev_err(cpld->dev, + "this driver does not support cpld with version %d and product %d.\n", + version, product); + goto err_no_version; + } + + cpld->desc = desc; + + cpld->leds = kcalloc(desc->num_led_descs, sizeof(*cpld->leds), + GFP_KERNEL); + if (!cpld->leds) { + err = -ENOMEM; + dev_err(cpld->dev, "could not allocate led state data\n"); + goto err_led_alloc; + } + + for (i = 0; i < desc->num_led_descs; i++) { + err = nizynqcpld_led_register(cpld, &desc->led_descs[i], + &cpld->leds[i]); + /* Skip LEDs that are missing from device tree, but continue + * adding other LEDs. + */ + if (err) + dev_dbg(&client->dev, + "Omitting %s LED\n", + desc->led_descs[i].of_node_name); + } + + /* don't care about errors */ + myrio_wifi_sw_init(cpld); + + err = sysfs_create_files(&cpld->dev->kobj, desc->attrs); + if (err) { + dev_err(cpld->dev, "could not register attrs for device.\n"); + goto err_sysfs_create_files; + } + + if (desc->watchdog_desc) { + struct nizynqcpld_watchdog *watchdog = &cpld->watchdog; + struct device_node *node; + + watchdog->desc = desc->watchdog_desc; + atomic_set(&watchdog->available, 1); + init_waitqueue_head(&watchdog->irq_event); + watchdog->expired = false; + + /* Find the watchdogs node. */ + node = of_find_node_by_name(cpld->client->dev.of_node, + "watchdogs"); + if (!node) { + err = -ENOENT; + goto err_watchdog_register; + } + + /* Get the boot watchdog node. */ + node = of_find_node_by_name(node, "boot-watchdog"); + if (!node) { + err = -ENOENT; + goto err_watchdog_register; + } + + cpld->irq = irq_of_parse_and_map(node, 0); + + err = devm_request_threaded_irq(&client->dev, cpld->irq, NULL, + nizynqcpld_watchdog_irq, + IRQF_ONESHOT, NIWATCHDOG_NAME, + cpld); + if (err) { + dev_err(cpld->dev, "failed to get irq\n"); + goto err_watchdog_register; + } + + watchdog->misc_dev.minor = MISC_DYNAMIC_MINOR; + watchdog->misc_dev.name = NIWATCHDOG_NAME; + watchdog->misc_dev.fops = &nizynqcpld_watchdog_misc_fops; + err = misc_register(&watchdog->misc_dev); + if (err) { + dev_err(cpld->dev, + "Couldn't register misc device\n"); + goto err_watchdog_register; + } + } + + cpld->restart_nb.notifier_call = nizynqcpld_restart; + cpld->restart_nb.priority = 255; + register_restart_handler(&cpld->restart_nb); + + i2c_set_clientdata(client, cpld); + + dev_info(&client->dev, + "%s NI Zynq-based target CPLD found.\n", + client->name); + + return 0; + +err_watchdog_register: + sysfs_remove_files(&client->dev.kobj, desc->attrs); +err_sysfs_create_files: + myrio_wifi_sw_uninit(cpld); + while (i--) + nizynqcpld_led_unregister(&cpld->leds[i]); + kfree(cpld->leds); +err_led_alloc: +err_no_version: +err_read_info: + kfree(cpld); +err_cpld_alloc: + return err; +} + +static void nizynqcpld_remove(struct i2c_client *client) +{ + struct nizynqcpld *cpld = i2c_get_clientdata(client); + struct nizynqcpld_desc *desc = cpld->desc; + int i; + + unregister_restart_handler(&cpld->restart_nb); + + if (desc->watchdog_desc) + misc_deregister(&cpld->watchdog.misc_dev); + + sysfs_remove_files(&cpld->dev->kobj, desc->attrs); + for (i = desc->num_led_descs - 1; i; --i) + nizynqcpld_led_unregister(&cpld->leds[i]); + kfree(cpld->leds); + + myrio_wifi_sw_uninit(cpld); + + kfree(cpld); +} + +static const struct of_device_id nizynqcpld_dt_ids[] = { + { .compatible = "ni,cpld" }, + { } +}; +MODULE_DEVICE_TABLE(of, nizynqcpld_dt_ids); + +static const struct i2c_device_id nizynqcpld_ids[] = { + { .name = "cpld" }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, nizynqcpld_ids); + +static struct i2c_driver nizynqcpld_driver = { + .driver = { + .name = "nicpld", + .of_match_table = nizynqcpld_dt_ids, + }, + .probe = nizynqcpld_probe, + .remove = nizynqcpld_remove, + .id_table = nizynqcpld_ids, +}; + +module_i2c_driver(nizynqcpld_driver); + +MODULE_DESCRIPTION("Driver for CPLD on NI's Zynq RIO products"); +MODULE_AUTHOR("National Instruments"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 47a0a738862b5..4419f0414bf1a 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -1137,6 +1137,10 @@ static int byt_sd_probe_slot(struct sdhci_pci_slot *slot) byt_needs_pwr_off(slot); + if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_BYT_SD) + slot->host->quirks2 |= SDHCI_QUIRK2_NEED_DELAY_AFTER_CLK_DISABLE | + SDHCI_QUIRK2_SPURIOUS_CARD_INSERT_INTERRUPT; + return 0; } diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 3d5e062b8cfd4..15f09f7795cfa 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2066,13 +2066,24 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) host->mmc->actual_clock = 0; - sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + if (clk & SDHCI_CLOCK_CARD_EN) + sdhci_writew(host, clk & ~SDHCI_CLOCK_CARD_EN, + SDHCI_CLOCK_CONTROL); - if (clock == 0) - return; + if (host->quirks2 & SDHCI_QUIRK2_NEED_DELAY_AFTER_CLK_DISABLE) + mdelay(1); + + if (clock == 0) { + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); + goto out_delay; + } clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock); sdhci_enable_clk(host, clk); +out_delay: + if (host->quirks2 & SDHCI_QUIRK2_SPURIOUS_CARD_INSERT_INTERRUPT) + mdelay(5); } EXPORT_SYMBOL_GPL(sdhci_set_clock); @@ -2416,6 +2427,9 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) else sdhci_set_power(host, ios->power_mode, ios->vdd); + if (ios->power_mode == MMC_POWER_OFF) + mdelay(15); + if (host->ops->platform_send_init_74_clocks) host->ops->platform_send_init_74_clocks(host, ios->power_mode); diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 3201d21af793a..ad5fb71cd729c 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -536,6 +536,10 @@ struct sdhci_host { #define SDHCI_QUIRK2_USE_32BIT_BLK_CNT (1<<18) /* Issue CMD and DATA reset together */ #define SDHCI_QUIRK2_ISSUE_CMD_DAT_RESET_TOGETHER (1<<19) +/* Controller requires delay between card clock disable and param change */ +#define SDHCI_QUIRK2_NEED_DELAY_AFTER_CLK_DISABLE (1<<20) +/* Controller may interrupt multiple times for card insert */ +#define SDHCI_QUIRK2_SPURIOUS_CARD_INSERT_INTERRUPT (1<<21) int irq; /* Device IRQ */ void __iomem *ioaddr; /* Mapped address */ diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 1e57c8de85782..495bd833ba68d 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -69,6 +69,13 @@ config MTD_NAND_ECC_REALTEK help This enables support for the hardware ECC engine from Realtek. +config MTD_NAND_PL353 + tristate "ARM Pl353 NAND flash driver" + depends on MTD_NAND && ARM + select PL353_SMC + help + This enables access to the NAND flash device on PL353 SMC controller. + endmenu endmenu diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 2e0e562677183..96281c82c09a1 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -4,6 +4,7 @@ nandcore-objs := core.o bbt.o obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o obj-$(CONFIG_MTD_NAND_ECC_MEDIATEK) += ecc-mtk.o obj-$(CONFIG_MTD_NAND_ECC_REALTEK) += ecc-realtek.o +obj-$(CONFIG_MTD_NAND_PL353) += pl353_nand.o obj-$(CONFIG_SPI_QPIC_SNAND) += qpic_common.o obj-$(CONFIG_MTD_NAND_QCOM) += qpic_common.o obj-y += onenand/ diff --git a/drivers/mtd/nand/pl353_nand.c b/drivers/mtd/nand/pl353_nand.c new file mode 100644 index 0000000000000..9aab45c54f1cb --- /dev/null +++ b/drivers/mtd/nand/pl353_nand.c @@ -0,0 +1,1316 @@ +/* + * ARM PL353 NAND Flash Controller Driver + * + * Copyright (C) 2009 - 2014 Xilinx, Inc. + * + * This driver is based on plat_nand.c and mxc_nand.c drivers + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PL353_NAND_DRIVER_NAME "xilinx_nand" + +/* NAND flash driver defines */ +#define PL353_NAND_CMD_PHASE 1 /* End command valid in command phase */ +#define PL353_NAND_DATA_PHASE 2 /* End command valid in data phase */ +#define PL353_NAND_ECC_SIZE 512 /* Size of data for ECC operation */ + +/* Flash memory controller operating parameters */ + +#define PL353_NAND_ECC_CONFIG (BIT(4) | /* ECC read at end of page */ \ + (0 << 5)) /* No Jumping */ + +/* AXI Address definitions */ +#define START_CMD_SHIFT 3 +#define END_CMD_SHIFT 11 +#define END_CMD_VALID_SHIFT 20 +#define ADDR_CYCLES_SHIFT 21 +#define CLEAR_CS_SHIFT 21 +#define ECC_LAST_SHIFT 10 +#define COMMAND_PHASE (0 << 19) +#define DATA_PHASE BIT(19) + +#define PL353_NAND_ECC_LAST BIT(ECC_LAST_SHIFT) /* Set ECC_Last */ +#define PL353_NAND_CLEAR_CS BIT(CLEAR_CS_SHIFT) /* Clear chip select */ + +#define ONDIE_ECC_FEATURE_ADDR 0x90 +#define PL353_NAND_ECC_BUSY_TIMEOUT (1 * HZ) +#define PL353_NAND_DEV_BUSY_TIMEOUT (1 * HZ) +#define PL353_NAND_LAST_TRANSFER_LENGTH 4 + +/* Inline function for the NAND controller register write */ +static inline void pl353_nand_write32(void __iomem *addr, u32 val) +{ + writel_relaxed((val), (addr)); +} + /* + * READ0 command only, for checking read status. Note that the real command + * here is 0x00, but we can't differentiate between READ0 where we need to + * send a READSTART after the address bytes, or a READ0 by itself, after + * a read status command to check the on-die ECC status. The high bit is + * written into the unused end_cmd field, so we don't need to mask it off. + */ +#define NAND_CMD_READ0_ONLY 0x100 + +/* + * Status bits + */ +/* Micron rewrite-recommended */ +#define NAND_STATUS_RR 0x08 + +/** + * struct pl353_nand_command_format - Defines NAND flash command format + * @start_cmd: First cycle command (Start command) + * @end_cmd: Second cycle command (Last command) + * @addr_cycles: Number of address cycles required to send the address + * @end_cmd_valid: The second cycle command is valid for cmd or data phase + */ +struct pl353_nand_command_format { + int start_cmd; + int end_cmd; + u8 addr_cycles; + u8 end_cmd_valid; +}; + +/** + * struct pl353_nand_info - Defines the NAND flash driver instance + * @chip: NAND chip information structure + * @mtd: MTD information structure + * @parts: Pointer to the mtd_partition structure + * @nand_base: Virtual address of the NAND flash device + * @end_cmd_pending: End command is pending + * @end_cmd: End command + */ +struct pl353_nand_info { + struct nand_chip chip; + struct mtd_partition *parts; + void __iomem *nand_base; + unsigned long end_cmd_pending; + unsigned long end_cmd; + int last_read_page; + int ondie_ecc_enabled; +}; + +/* + * The NAND flash operations command format + */ +static const struct pl353_nand_command_format pl353_nand_commands[] = { + {NAND_CMD_READ0, NAND_CMD_READSTART, 5, PL353_NAND_CMD_PHASE}, + {NAND_CMD_READ0_ONLY, NAND_CMD_NONE, 0, NAND_CMD_NONE}, + {NAND_CMD_RNDOUT, NAND_CMD_RNDOUTSTART, 2, PL353_NAND_CMD_PHASE}, + {NAND_CMD_READID, NAND_CMD_NONE, 1, NAND_CMD_NONE}, + {NAND_CMD_STATUS, NAND_CMD_NONE, 0, NAND_CMD_NONE}, + {NAND_CMD_SEQIN, NAND_CMD_PAGEPROG, 5, PL353_NAND_DATA_PHASE}, + {NAND_CMD_RNDIN, NAND_CMD_NONE, 2, NAND_CMD_NONE}, + {NAND_CMD_ERASE1, NAND_CMD_ERASE2, 3, PL353_NAND_CMD_PHASE}, + {NAND_CMD_RESET, NAND_CMD_NONE, 0, NAND_CMD_NONE}, + {NAND_CMD_PARAM, NAND_CMD_NONE, 1, NAND_CMD_NONE}, + {NAND_CMD_GET_FEATURES, NAND_CMD_NONE, 1, NAND_CMD_NONE}, + {NAND_CMD_SET_FEATURES, NAND_CMD_NONE, 1, NAND_CMD_NONE}, + {NAND_CMD_NONE, NAND_CMD_NONE, 0, 0}, + /* Add all the flash commands supported by the flash device and Linux */ + /* + * The cache program command is not supported by driver because driver + * cant differentiate between page program and cached page program from + * start command, these commands can be differentiated through end + * command, which doesn't fit in to the driver design. The cache program + * command is not supported by NAND subsystem also, look at 1612 line + * number (in nand_write_page function) of nand_base.c file. + * {NAND_CMD_SEQIN, NAND_CMD_CACHEDPROG, 5, PL353_NAND_YES}, + */ +}; + +/* Define default oob placement schemes for large and small page devices */ + +static int pl353_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section) + return -ERANGE; + + if (mtd->oobsize == 64) { + oobregion->offset = 52; + oobregion->length = 12; + } else { + oobregion->offset = 0; + oobregion->length = 3; + } + + return 0; +} + +static int pl353_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section) + return -ERANGE; + + if (mtd->oobsize == 64) { + oobregion->offset = 2; + oobregion->length = 50; + } else { + oobregion->offset = 8; + oobregion->length = 8; + } + + return 0; +} + +static const struct mtd_ooblayout_ops pl353_ooblayout_ops = { + .ecc = pl353_ooblayout_ecc, + .free = pl353_ooblayout_free, +}; + +static int pl353_ondie_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section > 3) + return -ERANGE; + + oobregion->length = 8; + oobregion->offset = 8 * (section * 2 + 1); + + return 0; +} + +static int pl353_ondie_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section > 3) + return -ERANGE; + + oobregion->length = 4; + oobregion->offset = 4 + section * 16; + + return 0; +} + +static const struct mtd_ooblayout_ops pl353_ondie_ooblayout_ops = { + .ecc = pl353_ondie_ooblayout_ecc, + .free = pl353_ondie_ooblayout_free, +}; + +/* Generic flash bbt decriptors */ +static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' }; +static uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 4, + .len = 4, + .veroffs = 20, + .maxblocks = 4, + .pattern = bbt_pattern +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 4, + .len = 4, + .veroffs = 20, + .maxblocks = 4, + .pattern = mirror_pattern +}; + +static bool enable_subpage_read = 1; +module_param(enable_subpage_read, bool, 0444); +MODULE_PARM_DESC(enable_subpage_read, "Load-time parameter to toggle subpage reads on supported nand chips. Enabled by default."); + +static bool enable_subpage_write = 1; +module_param(enable_subpage_write, bool, 0444); +MODULE_PARM_DESC(enable_subpage_write, "Load-time parameter to toggle subpage writes on supported nand chips. Enabled by default."); + +/** + * pl353_nand_calculate_hwecc - Calculate Hardware ECC + * @mtd: Pointer to the mtd_info structure + * @data: Pointer to the page data + * @ecc_code: Pointer to the ECC buffer where ECC data needs to be stored + * + * This function retrieves the Hardware ECC data from the controller and returns + * ECC data back to the MTD subsystem. + * + * Return: 0 on success or error value on failure + */ +static int pl353_nand_calculate_hwecc(struct mtd_info *mtd, + const u8 *data, u8 *ecc_code) +{ + u32 ecc_value, ecc_status; + u8 ecc_reg, ecc_byte; + unsigned long timeout = jiffies + PL353_NAND_ECC_BUSY_TIMEOUT; + + /* Wait till the ECC operation is complete or timeout */ + do { + if (pl353_smc_ecc_is_busy()) + cpu_relax(); + else + break; + } while (!time_after_eq(jiffies, timeout)); + + if (time_after_eq(jiffies, timeout)) { + pr_err("%s timed out\n", __func__); + return -ETIMEDOUT; + } + + for (ecc_reg = 0; ecc_reg < 4; ecc_reg++) { + /* Read ECC value for each block */ + ecc_value = pl353_smc_get_ecc_val(ecc_reg); + ecc_status = (ecc_value >> 24) & 0xFF; + /* ECC value valid */ + if (ecc_status & 0x40) { + for (ecc_byte = 0; ecc_byte < 3; ecc_byte++) { + /* Copy ECC bytes to MTD buffer */ + *ecc_code = ecc_value & 0xFF; + ecc_value = ecc_value >> 8; + ecc_code++; + } + } else { + pr_warn("%s status failed\n", __func__); + return -1; + } + } + return 0; +} + +/** + * onehot - onehot function + * @value: Value to check for onehot + * + * This function checks whether a value is onehot or not. + * onehot is if and only if onebit is set. + * + * Return: 1 if it is onehot else 0 + */ +static int onehot(unsigned short value) +{ + return (value & (value - 1)) == 0; +} + +/** + * pl353_nand_correct_data - ECC correction function + * @mtd: Pointer to the mtd_info structure + * @buf: Pointer to the page data + * @read_ecc: Pointer to the ECC value read from spare data area + * @calc_ecc: Pointer to the calculated ECC value + * + * This function corrects the ECC single bit errors & detects 2-bit errors. + * + * Return: 0 if no ECC errors found + * 1 if single bit error found and corrected. + * -1 if multiple ECC errors found. + */ +static int pl353_nand_correct_data(struct mtd_info *mtd, unsigned char *buf, + unsigned char *read_ecc, + unsigned char *calc_ecc) +{ + unsigned char bit_addr; + unsigned int byte_addr; + unsigned short ecc_odd, ecc_even, read_ecc_lower, read_ecc_upper; + unsigned short calc_ecc_lower, calc_ecc_upper; + + read_ecc_lower = (read_ecc[0] | (read_ecc[1] << 8)) & 0xfff; + read_ecc_upper = ((read_ecc[1] >> 4) | (read_ecc[2] << 4)) & 0xfff; + + calc_ecc_lower = (calc_ecc[0] | (calc_ecc[1] << 8)) & 0xfff; + calc_ecc_upper = ((calc_ecc[1] >> 4) | (calc_ecc[2] << 4)) & 0xfff; + + ecc_odd = read_ecc_lower ^ calc_ecc_lower; + ecc_even = read_ecc_upper ^ calc_ecc_upper; + + if ((ecc_odd == 0) && (ecc_even == 0)) + return 0; /* no error */ + + if (ecc_odd == (~ecc_even & 0xfff)) { + /* bits [11:3] of error code is byte offset */ + byte_addr = (ecc_odd >> 3) & 0x1ff; + /* bits [2:0] of error code is bit offset */ + bit_addr = ecc_odd & 0x7; + /* Toggling error bit */ + buf[byte_addr] ^= (1 << bit_addr); + return 1; + } + + if (onehot(ecc_odd | ecc_even) == 1) + return 1; /* one error in parity */ + + return -1; /* Uncorrectable error */ +} + +/** + * pl353_nand_read_oob - [REPLACABLE] the most common OOB data read function + * @mtd: Pointer to the mtd info structure + * @chip: Pointer to the NAND chip info structure + * @page: Page number to read + * + * Return: Always return zero + */ +static int pl353_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + unsigned long data_phase_addr; + uint8_t *p; + + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + + p = chip->oob_poi; + chip->read_buf(mtd, p, + (mtd->oobsize - PL353_NAND_LAST_TRANSFER_LENGTH)); + p += (mtd->oobsize - PL353_NAND_LAST_TRANSFER_LENGTH); + + data_phase_addr = (unsigned long __force)chip->IO_ADDR_R; + data_phase_addr |= PL353_NAND_CLEAR_CS; + chip->IO_ADDR_R = (void __iomem * __force)data_phase_addr; + chip->read_buf(mtd, p, PL353_NAND_LAST_TRANSFER_LENGTH); + + return 0; +} + +/** + * pl353_nand_write_oob - [REPLACABLE] the most common OOB data write function + * @mtd: Pointer to the mtd info structure + * @chip: Pointer to the NAND chip info structure + * @page: Page number to write + * + * Return: Zero on success and EIO on failure + */ +static int pl353_nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + int status = 0; + const uint8_t *buf = chip->oob_poi; + unsigned long data_phase_addr; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); + + chip->write_buf(mtd, buf, + (mtd->oobsize - PL353_NAND_LAST_TRANSFER_LENGTH)); + buf += (mtd->oobsize - PL353_NAND_LAST_TRANSFER_LENGTH); + + data_phase_addr = (unsigned long __force)chip->IO_ADDR_W; + data_phase_addr |= PL353_NAND_CLEAR_CS; + data_phase_addr |= (1 << END_CMD_VALID_SHIFT); + chip->IO_ADDR_W = (void __iomem * __force)data_phase_addr; + chip->write_buf(mtd, buf, PL353_NAND_LAST_TRANSFER_LENGTH); + + /* Send command to program the OOB data */ + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + + return status & NAND_STATUS_FAIL ? -EIO : 0; +} + +/** + * pl353_nand_read_page_raw - [Intern] read raw page data without ecc + * @mtd: Pointer to the mtd info structure + * @chip: Pointer to the NAND chip info structure + * @buf: Pointer to the data buffer + * @oob_required: Caller requires OOB data read to chip->oob_poi + * @page: Page number to read + * + * Return: Always return zero + */ +static int pl353_nand_read_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + unsigned long data_phase_addr; + uint8_t *p; + + chip->read_buf(mtd, buf, mtd->writesize); + + p = chip->oob_poi; + chip->read_buf(mtd, p, + (mtd->oobsize - PL353_NAND_LAST_TRANSFER_LENGTH)); + p += (mtd->oobsize - PL353_NAND_LAST_TRANSFER_LENGTH); + + data_phase_addr = (unsigned long __force)chip->IO_ADDR_R; + data_phase_addr |= PL353_NAND_CLEAR_CS; + chip->IO_ADDR_R = (void __iomem * __force)data_phase_addr; + + chip->read_buf(mtd, p, PL353_NAND_LAST_TRANSFER_LENGTH); + return 0; +} + +static int pl353_nand_read_subpage_raw(struct mtd_info *mtd, + struct nand_chip *chip, uint32_t data_offs, + uint32_t readlen, uint8_t *buf, int page) +{ + if (data_offs != 0) { + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_offs, -1); + buf += data_offs; + } + + /* readlen must be a multiple of 4. */ + readlen = (((readlen + 3) >> 2) << 2); + + chip->read_buf(mtd, buf, readlen); + return 0; +} + +/** + * pl353_nand_write_page_raw - [Intern] raw page write function + * @mtd: Pointer to the mtd info structure + * @chip: Pointer to the NAND chip info structure + * @buf: Pointer to the data buffer + * @oob_required: Caller requires OOB data read to chip->oob_poi + * + * Return: Always return zero + */ +static int pl353_nand_write_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf, + int oob_required, int page) +{ + unsigned long data_phase_addr; + uint8_t *p; + + chip->write_buf(mtd, buf, mtd->writesize); + + p = chip->oob_poi; + chip->write_buf(mtd, p, + (mtd->oobsize - PL353_NAND_LAST_TRANSFER_LENGTH)); + p += (mtd->oobsize - PL353_NAND_LAST_TRANSFER_LENGTH); + + data_phase_addr = (unsigned long __force)chip->IO_ADDR_W; + data_phase_addr |= PL353_NAND_CLEAR_CS; + data_phase_addr |= (1 << END_CMD_VALID_SHIFT); + chip->IO_ADDR_W = (void __iomem * __force)data_phase_addr; + + chip->write_buf(mtd, p, PL353_NAND_LAST_TRANSFER_LENGTH); + + return 0; +} + +/** + * pl353_nand_write_subpage_raw - [Intern] raw subpage write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @column: column address of subpage within the page + * @data_len: data length + * @data_buf: data buffer + * @oob_required: must write chip->oob_poi to OOB + * + */ +static int pl353_nand_write_subpage_raw(struct mtd_info *mtd, + struct nand_chip *chip, uint32_t offset, + uint32_t data_len, const uint8_t *data_buf, + int oob_required, int page) +{ + return pl353_nand_write_page_raw(mtd, chip, data_buf, oob_required, + page); +} + +/** + * nand_write_page_hwecc - Hardware ECC based page write function + * @mtd: Pointer to the mtd info structure + * @chip: Pointer to the NAND chip info structure + * @buf: Pointer to the data buffer + * @oob_required: Caller requires OOB data read to chip->oob_poi + * + * This functions writes data and hardware generated ECC values in to the page. + * + * Return: Always return zero + */ +static int pl353_nand_write_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf, + int oob_required, int page) +{ + int i, eccsize = chip->ecc.size; + int eccsteps = chip->ecc.steps; + uint8_t *ecc_calc = chip->buffers->ecccalc; + const uint8_t *p = buf; + struct mtd_oob_region oobregion = { }; + int section = 0; + unsigned long data_phase_addr; + uint8_t *oob_ptr; + + for ( ; (eccsteps - 1); eccsteps--) { + chip->write_buf(mtd, p, eccsize); + p += eccsize; + } + chip->write_buf(mtd, p, (eccsize - PL353_NAND_LAST_TRANSFER_LENGTH)); + p += (eccsize - PL353_NAND_LAST_TRANSFER_LENGTH); + + /* Set ECC Last bit to 1 */ + data_phase_addr = (unsigned long __force)chip->IO_ADDR_W; + data_phase_addr |= PL353_NAND_ECC_LAST; + chip->IO_ADDR_W = (void __iomem * __force)data_phase_addr; + chip->write_buf(mtd, p, PL353_NAND_LAST_TRANSFER_LENGTH); + + /* Wait for ECC to be calculated and read the error values */ + p = buf; + chip->ecc.calculate(mtd, p, &ecc_calc[0]); + + for (i = 0; i < chip->ecc.total; i++) { + if (!oobregion.length) + mtd_ooblayout_ecc(mtd, section++, &oobregion); + + chip->oob_poi[oobregion.offset] = ~(ecc_calc[i]); + oobregion.length--; + oobregion.offset++; + } + + /* Clear ECC last bit */ + data_phase_addr = (unsigned long __force)chip->IO_ADDR_W; + data_phase_addr &= ~PL353_NAND_ECC_LAST; + chip->IO_ADDR_W = (void __iomem * __force)data_phase_addr; + + /* Write the spare area with ECC bytes */ + oob_ptr = chip->oob_poi; + chip->write_buf(mtd, oob_ptr, + (mtd->oobsize - PL353_NAND_LAST_TRANSFER_LENGTH)); + + data_phase_addr = (unsigned long __force)chip->IO_ADDR_W; + data_phase_addr |= PL353_NAND_CLEAR_CS; + data_phase_addr |= (1 << END_CMD_VALID_SHIFT); + chip->IO_ADDR_W = (void __iomem * __force)data_phase_addr; + oob_ptr += (mtd->oobsize - PL353_NAND_LAST_TRANSFER_LENGTH); + chip->write_buf(mtd, oob_ptr, PL353_NAND_LAST_TRANSFER_LENGTH); + + return 0; +} + +/** + * pl353_nand_write_page_swecc - [REPLACABLE] software ecc based page write function + * @mtd: Pointer to the mtd info structure + * @chip: Pointer to the NAND chip info structure + * @buf: Pointer to the data buffer + * @oob_required: Caller requires OOB data read to chip->oob_poi + * + * Return: Always return zero + */ +static int pl353_nand_write_page_swecc(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf, + int oob_required, int page) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *ecc_calc = chip->buffers->ecccalc; + const uint8_t *p = buf; + struct mtd_oob_region oobregion = { }; + int section = 0; + + /* Software ecc calculation */ + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + + for (i = 0; i < chip->ecc.total; i++) { + if (!oobregion.length) + mtd_ooblayout_ecc(mtd, section++, &oobregion); + + chip->oob_poi[oobregion.offset] = ecc_calc[i]; + oobregion.length--; + oobregion.offset++; + } + + chip->ecc.write_page_raw(mtd, chip, buf, 1, page); + + return 0; +} + +/** + * pl353_nand_read_page_hwecc - Hardware ECC based page read function + * @mtd: Pointer to the mtd info structure + * @chip: Pointer to the NAND chip info structure + * @buf: Pointer to the buffer to store read data + * @oob_required: Caller requires OOB data read to chip->oob_poi + * @page: Page number to read + * + * This functions reads data and checks the data integrity by comparing hardware + * generated ECC values and read ECC values from spare area. + * + * Return: 0 always and updates ECC operation status in to MTD structure + */ +static int pl353_nand_read_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + int i, stat, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *ecc_calc = chip->buffers->ecccalc; + uint8_t *ecc_code = chip->buffers->ecccode; + struct mtd_oob_region oobregion = { }; + int section = 0; + unsigned long data_phase_addr; + uint8_t *oob_ptr; + + for ( ; (eccsteps - 1); eccsteps--) { + chip->read_buf(mtd, p, eccsize); + p += eccsize; + } + chip->read_buf(mtd, p, (eccsize - PL353_NAND_LAST_TRANSFER_LENGTH)); + p += (eccsize - PL353_NAND_LAST_TRANSFER_LENGTH); + + /* Set ECC Last bit to 1 */ + data_phase_addr = (unsigned long __force)chip->IO_ADDR_R; + data_phase_addr |= PL353_NAND_ECC_LAST; + chip->IO_ADDR_R = (void __iomem * __force)data_phase_addr; + chip->read_buf(mtd, p, PL353_NAND_LAST_TRANSFER_LENGTH); + + /* Read the calculated ECC value */ + p = buf; + chip->ecc.calculate(mtd, p, &ecc_calc[0]); + + /* Clear ECC last bit */ + data_phase_addr = (unsigned long __force)chip->IO_ADDR_R; + data_phase_addr &= ~PL353_NAND_ECC_LAST; + chip->IO_ADDR_R = (void __iomem * __force)data_phase_addr; + + /* Read the stored ECC value */ + oob_ptr = chip->oob_poi; + chip->read_buf(mtd, oob_ptr, + (mtd->oobsize - PL353_NAND_LAST_TRANSFER_LENGTH)); + + /* de-assert chip select */ + data_phase_addr = (unsigned long __force)chip->IO_ADDR_R; + data_phase_addr |= PL353_NAND_CLEAR_CS; + chip->IO_ADDR_R = (void __iomem * __force)data_phase_addr; + + oob_ptr += (mtd->oobsize - PL353_NAND_LAST_TRANSFER_LENGTH); + chip->read_buf(mtd, oob_ptr, PL353_NAND_LAST_TRANSFER_LENGTH); + + for (i = 0; i < chip->ecc.total; i++) { + if (!oobregion.length) + mtd_ooblayout_ecc(mtd, section++, &oobregion); + + ecc_code[i] = ~(chip->oob_poi[oobregion.offset]); + oobregion.length--; + oobregion.offset++; + } + + eccsteps = chip->ecc.steps; + p = buf; + + /* Check ECC error for all blocks and correct if it is correctable */ + for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + return 0; +} + +/** + * pl353_nand_read_page_swecc - [REPLACABLE] software ecc based page read function + * @mtd: Pointer to the mtd info structure + * @chip: Pointer to the NAND chip info structure + * @buf: Pointer to the buffer to store read data + * @oob_required: Caller requires OOB data read to chip->oob_poi + * @page: Page number to read + * + * Return: Always return zero + */ +static int pl353_nand_read_page_swecc(struct mtd_info *mtd, + struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *ecc_calc = chip->buffers->ecccalc; + uint8_t *ecc_code = chip->buffers->ecccode; + struct mtd_oob_region oobregion = { }; + int section = 0; + + chip->ecc.read_page_raw(mtd, chip, buf, page, 1); + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + + for (i = 0; i < chip->ecc.total; i++) { + if (!oobregion.length) + mtd_ooblayout_ecc(mtd, section++, &oobregion); + + ecc_code[i] = chip->oob_poi[oobregion.offset]; + oobregion.length--; + oobregion.offset++; + } + + eccsteps = chip->ecc.steps; + p = buf; + + for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + return 0; +} + +/** + * pl353_nand_select_chip - Select the flash device + * @mtd: Pointer to the mtd info structure + * @chip: Pointer to the NAND chip info structure + */ +static void pl353_nand_select_chip(struct mtd_info *mtd, int chip) +{ + /* CS is automatically asserted when a command or data access is + * started. In this function we only need to deassert CS when the + * chip is no longer in use. Unfortunately, the only way we can + * do this is to read a byte of data from the NAND chip. + */ + if (-1 == chip) { + struct nand_chip *nand_chip; + unsigned long data_phase_addr; + + nand_chip = mtd_to_nand(mtd); + data_phase_addr = (unsigned long __force)nand_chip->IO_ADDR_R; + data_phase_addr |= PL353_NAND_CLEAR_CS; + nand_chip->IO_ADDR_R = (void __iomem *__force)data_phase_addr; + + readb(nand_chip->IO_ADDR_R); + } + return; +} + +/** + * pl353_nand_cmd_function - Send command to NAND device + * @mtd: Pointer to the mtd_info structure + * @command: The command to be sent to the flash device + * @column: The column address for this command, -1 if none + * @page_addr: The page address for this command, -1 if none + */ +static void pl353_nand_cmd_function(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + const struct pl353_nand_command_format *curr_cmd = NULL; + struct pl353_nand_info *xnand = + container_of(mtd_to_nand(mtd), struct pl353_nand_info, chip); + void __iomem *cmd_addr; + unsigned long cmd_data = 0, end_cmd_valid = 0; + unsigned long cmd_phase_addr, data_phase_addr, end_cmd, i; + unsigned long timeout = jiffies + PL353_NAND_DEV_BUSY_TIMEOUT; + + if (xnand->end_cmd_pending) { + /* + * Check for end command if this command request is same as the + * pending command then return + */ + if (xnand->end_cmd == command) { + xnand->end_cmd = 0; + xnand->end_cmd_pending = 0; + return; + } + } + + /* Emulate NAND_CMD_READOOB for large page device */ + if ((mtd->writesize > PL353_NAND_ECC_SIZE) && + (command == NAND_CMD_READOOB)) { + column += mtd->writesize; + command = NAND_CMD_READ0; + } + + /* If we receive a READ0 for the page that we've most recently read, + * without an intervening command that would change the state of the + * NAND cache register, we can replace the READ0 with a RNDOUT. + */ + if ((command == NAND_CMD_READ0) && + (xnand->last_read_page == page_addr)) { + command = NAND_CMD_RNDOUT; + page_addr = -1; + } + + /* Get the command format */ + for (i = 0; (pl353_nand_commands[i].start_cmd != NAND_CMD_NONE || + pl353_nand_commands[i].end_cmd != NAND_CMD_NONE); i++) + if (command == pl353_nand_commands[i].start_cmd) + curr_cmd = &pl353_nand_commands[i]; + + if (curr_cmd == NULL) + return; + + /* We want to preserve the page address of the last READ0 + * when just changing the read column address. + */ + if (command != NAND_CMD_RNDOUT) + xnand->last_read_page = -1; + + /* Clear interrupt */ + pl353_smc_clr_nand_int(); + + /* Get the command phase address */ + if (curr_cmd->end_cmd_valid == PL353_NAND_CMD_PHASE) + end_cmd_valid = 1; + + if (curr_cmd->end_cmd == NAND_CMD_NONE) + end_cmd = 0x0; + else + end_cmd = curr_cmd->end_cmd; + + cmd_phase_addr = (unsigned long __force)xnand->nand_base | + (curr_cmd->addr_cycles << ADDR_CYCLES_SHIFT) | + (end_cmd_valid << END_CMD_VALID_SHIFT) | + (COMMAND_PHASE) | + (end_cmd << END_CMD_SHIFT) | + (curr_cmd->start_cmd << START_CMD_SHIFT); + + cmd_addr = (void __iomem * __force)cmd_phase_addr; + + /* Get the data phase address */ + end_cmd_valid = 0; + + data_phase_addr = (unsigned long __force)xnand->nand_base | + (0x0 << CLEAR_CS_SHIFT) | + (end_cmd_valid << END_CMD_VALID_SHIFT) | + (DATA_PHASE) | + (end_cmd << END_CMD_SHIFT) | + (0x0 << ECC_LAST_SHIFT); + + chip->IO_ADDR_R = (void __iomem * __force)data_phase_addr; + chip->IO_ADDR_W = chip->IO_ADDR_R; + + /* Command phase AXI write */ + /* Read & Write */ + if (column != -1 && page_addr != -1) { + /* Adjust columns for 16 bit bus width */ + if (chip->options & NAND_BUSWIDTH_16) + column >>= 1; + cmd_data = column; + if (mtd->writesize > PL353_NAND_ECC_SIZE) { + cmd_data |= page_addr << 16; + /* Another address cycle for devices > 128MiB */ + if (chip->chipsize > (128 << 20)) { + pl353_nand_write32(cmd_addr, cmd_data); + cmd_data = (page_addr >> 16); + } + } else { + cmd_data |= page_addr << 8; + } + } else if (page_addr != -1) { + /* Erase */ + cmd_data = page_addr; + } else if (column != -1) { + /* + * Change read/write column, read id etc + * Adjust columns for 16 bit bus width + */ + if ((chip->options & NAND_BUSWIDTH_16) && + ((command == NAND_CMD_READ0) || + (command == NAND_CMD_SEQIN) || + (command == NAND_CMD_RNDOUT) || + (command == NAND_CMD_RNDIN))) + column >>= 1; + cmd_data = column; + } + + pl353_nand_write32(cmd_addr, cmd_data); + + /* + * Ensure writes to the command register are observed by chip + * before proceeding to ndelay() and other reads/writes. + */ + dsb(st); + + if (curr_cmd->end_cmd_valid) { + xnand->end_cmd = curr_cmd->end_cmd; + xnand->end_cmd_pending = 1; + } + + ndelay(100); + + if ((command == NAND_CMD_READ0) || + (command == NAND_CMD_RESET) || + (command == NAND_CMD_PARAM) || + (command == NAND_CMD_GET_FEATURES)) { + + /* Wait till the device is ready or timeout */ + do { + if (chip->dev_ready(mtd)) + break; + else + cpu_relax(); + } while (!time_after_eq(jiffies, timeout)); + + if (time_after_eq(jiffies, timeout)) + pr_err("%s timed out\n", __func__); + return; + } + + if (command == NAND_CMD_READ0) { + /* If using on-die ECC, we must check the NAND status for an + * ECC warning or error after reading a page. + */ + if (xnand->ondie_ecc_enabled) { + uint8_t status; + + pl353_nand_cmd_function(mtd, NAND_CMD_STATUS, -1, -1); + status = chip->read_byte(mtd); + pl353_nand_cmd_function(mtd, NAND_CMD_READ0_ONLY, -1, + -1); + if (status & NAND_STATUS_RR) + ++mtd->ecc_stats.corrected; + else if (status & NAND_STATUS_FAIL) + ++mtd->ecc_stats.failed; + } + + /* Store the read page address so that we can detect consecutive + * reads of the same page and only send one read command. + */ + xnand->last_read_page = page_addr; + } +} + +/** + * pl353_nand_read_buf - read chip data into buffer + * @mtd: Pointer to the mtd info structure + * @buf: Pointer to the buffer to store read data + * @len: Number of bytes to read + */ +static void pl353_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i; + struct nand_chip *chip = mtd_to_nand(mtd); + unsigned long *ptr = (unsigned long *)buf; + + len >>= 2; + for (i = 0; i < len; i++) + ptr[i] = readl(chip->IO_ADDR_R); +} + +/** + * pl353_nand_write_buf - write buffer to chip + * @mtd: Pointer to the mtd info structure + * @buf: Pointer to the buffer to store read data + * @len: Number of bytes to write + */ +static void pl353_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, + int len) +{ + int i; + struct nand_chip *chip = mtd_to_nand(mtd); + unsigned long *ptr = (unsigned long *)buf; + + len >>= 2; + + for (i = 0; i < len; i++) + writel(ptr[i], chip->IO_ADDR_W); +} + +/** + * pl353_nand_device_ready - Check device ready/busy line + * @mtd: Pointer to the mtd_info structure + * + * Return: 0 on busy or 1 on ready state + */ +static int pl353_nand_device_ready(struct mtd_info *mtd) +{ + if (pl353_smc_get_nand_int_status_raw()) { + pl353_smc_clr_nand_int(); + return 1; + } + return 0; +} + +/** + * pl353_nand_detect_ondie_ecc - Get the flash ondie ecc state + * @mtd: Pointer to the mtd_info structure + * + * This function enables the ondie ecc for the Micron ondie ecc capable devices + * + * Return: 1 on detect, 0 if fail to detect + */ +static int pl353_nand_detect_ondie_ecc(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + u8 maf_id, dev_id, i, get_feature; + u8 set_feature[4] = { 0x08, 0x00, 0x00, 0x00 }; + + /* Check if On-Die ECC flash */ + nand_chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + nand_chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); + + /* Read manufacturer and device IDs */ + maf_id = readb(nand_chip->IO_ADDR_R); + dev_id = readb(nand_chip->IO_ADDR_R); + + if ((maf_id == NAND_MFR_MICRON) && + ((dev_id == 0xf1) || (dev_id == 0xa1) || + (dev_id == 0xb1) || (dev_id == 0xaa) || + (dev_id == 0xba) || (dev_id == 0xda) || + (dev_id == 0xca) || (dev_id == 0xac) || + (dev_id == 0xbc) || (dev_id == 0xdc) || + (dev_id == 0xcc) || (dev_id == 0xa3) || + (dev_id == 0xb3) || + (dev_id == 0xd3) || (dev_id == 0xc3))) { + + nand_chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, + ONDIE_ECC_FEATURE_ADDR, -1); + get_feature = readb(nand_chip->IO_ADDR_R); + + if (get_feature & 0x08) { + return 1; + } else { + nand_chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, + ONDIE_ECC_FEATURE_ADDR, -1); + for (i = 0; i < 4; i++) + writeb(set_feature[i], nand_chip->IO_ADDR_W); + + ndelay(1000); + + nand_chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, + ONDIE_ECC_FEATURE_ADDR, -1); + get_feature = readb(nand_chip->IO_ADDR_R); + + if (get_feature & 0x08) + return 1; + + } + } + + return 0; +} + +/** + * pl353_nand_ecc_init - Initialize the ecc information as per the ecc mode + * @mtd: Pointer to the mtd_info structure + * @ondie_ecc_state: ondie ecc status + * + * This function initializes the ecc block and functional pointers as per the + * ecc mode + */ +static void pl353_nand_ecc_init(struct mtd_info *mtd, int ondie_ecc_state) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + + nand_chip->ecc.mode = NAND_ECC_HW; + nand_chip->ecc.read_oob = pl353_nand_read_oob; + nand_chip->ecc.read_page_raw = pl353_nand_read_page_raw; + nand_chip->ecc.strength = 1; + nand_chip->ecc.write_oob = pl353_nand_write_oob; + nand_chip->ecc.write_page_raw = pl353_nand_write_page_raw; + + if (ondie_ecc_state) { + /* bypass the controller ECC block */ + pl353_smc_set_ecc_mode(PL353_SMC_ECCMODE_BYPASS); + + /* + * The software ECC routines won't work with the + * SMC controller + */ + nand_chip->ecc.bytes = 0; + nand_chip->ecc.read_page = pl353_nand_read_page_raw; + nand_chip->ecc.read_subpage = pl353_nand_read_subpage_raw; + nand_chip->ecc.write_page = pl353_nand_write_page_raw; + nand_chip->ecc.write_subpage = pl353_nand_write_subpage_raw; + nand_chip->ecc.size = mtd->writesize; + + /* NAND with on-die ECC supports subpage reads */ + if (enable_subpage_read) + nand_chip->options |= NAND_SUBPAGE_READ; + else + nand_chip->options &= ~(NAND_SUBPAGE_READ); + + /* NAND with on-die ECC may support subpage writes */ + if (nand_chip->onfi_version) + nand_chip->ecc.size /= + nand_chip->onfi_params.programs_per_page; + + if (enable_subpage_write) + nand_chip->options &= ~(NAND_NO_SUBPAGE_WRITE); + else + nand_chip->options |= NAND_NO_SUBPAGE_WRITE; + + /* + * On-Die ECC spare bytes offset 8 is used for ECC codes + * Use the BBT pattern descriptors + */ + nand_chip->bbt_td = &bbt_main_descr; + nand_chip->bbt_md = &bbt_mirror_descr; + + mtd_set_ooblayout(mtd, &pl353_ondie_ooblayout_ops); + } else { + /* Hardware ECC generates 3 bytes ECC code for each 512 bytes */ + nand_chip->ecc.bytes = 3; + nand_chip->ecc.calculate = pl353_nand_calculate_hwecc; + nand_chip->ecc.correct = pl353_nand_correct_data; + nand_chip->ecc.hwctl = NULL; + nand_chip->ecc.read_page = pl353_nand_read_page_hwecc; + nand_chip->ecc.size = PL353_NAND_ECC_SIZE; + nand_chip->ecc.write_page = pl353_nand_write_page_hwecc; + + pl353_smc_set_ecc_pg_size(mtd->writesize); + switch (mtd->writesize) { + case 512: + case 1024: + case 2048: + pl353_smc_set_ecc_mode(PL353_SMC_ECCMODE_APB); + break; + default: + /* + * The software ECC routines won't work with the + * SMC controller + */ + nand_chip->ecc.calculate = nand_calculate_ecc; + nand_chip->ecc.correct = nand_correct_data; + nand_chip->ecc.read_page = pl353_nand_read_page_swecc; + nand_chip->ecc.write_page = pl353_nand_write_page_swecc; + nand_chip->ecc.size = 256; + break; + } + + mtd_set_ooblayout(mtd, &pl353_ooblayout_ops); + } +} + +/** + * pl353_nand_probe - Probe method for the NAND driver + * @pdev: Pointer to the platform_device structure + * + * This function initializes the driver data structures and the hardware. + * + * Return: 0 on success or error value on failure + */ +static int pl353_nand_probe(struct platform_device *pdev) +{ + struct pl353_nand_info *xnand; + struct mtd_info *mtd; + struct nand_chip *nand_chip; + struct resource *res; + int ondie_ecc_state; + + xnand = devm_kzalloc(&pdev->dev, sizeof(*xnand), GFP_KERNEL); + if (!xnand) + return -ENOMEM; + + /* Map physical address of NAND flash */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + xnand->nand_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(xnand->nand_base)) + return PTR_ERR(xnand->nand_base); + + /* Link the private data with the MTD structure */ + nand_chip = &xnand->chip; + mtd = &xnand->chip.mtd; + + nand_chip->priv = xnand; + mtd->owner = THIS_MODULE; + mtd->name = PL353_NAND_DRIVER_NAME; + + /* Set address of NAND IO lines */ + nand_chip->IO_ADDR_R = xnand->nand_base; + nand_chip->IO_ADDR_W = xnand->nand_base; + + /* Set the driver entry points for MTD */ + nand_chip->cmdfunc = pl353_nand_cmd_function; + nand_chip->dev_ready = pl353_nand_device_ready; + nand_chip->select_chip = pl353_nand_select_chip; + + /* If we don't set this delay driver sets 20us by default */ + nand_chip->chip_delay = 30; + + /* Buffer read/write routines */ + nand_chip->read_buf = pl353_nand_read_buf; + nand_chip->write_buf = pl353_nand_write_buf; + + /* Set the device option and flash width */ + nand_chip->options = NAND_BUSWIDTH_AUTO; + nand_chip->bbt_options = NAND_BBT_USE_FLASH; + + platform_set_drvdata(pdev, xnand); + + ondie_ecc_state = pl353_nand_detect_ondie_ecc(mtd); + + /* first scan to find the device and get the page size */ + if (nand_scan_ident(mtd, 1, NULL)) { + dev_err(&pdev->dev, "nand_scan_ident for NAND failed\n"); + return -ENXIO; + } + + pl353_nand_ecc_init(mtd, ondie_ecc_state); + if (nand_chip->options & NAND_BUSWIDTH_16) + pl353_smc_set_buswidth(PL353_SMC_MEM_WIDTH_16); + + xnand->last_read_page = -1; + + /* second phase scan */ + if (nand_scan_tail(mtd)) { + dev_err(&pdev->dev, "nand_scan_tail for NAND failed\n"); + return -ENXIO; + } + + mtd_device_parse_register(&xnand->chip.mtd, NULL, NULL, NULL, 0); + + return 0; +} + +/** + * pl353_nand_remove - Remove method for the NAND driver + * @pdev: Pointer to the platform_device structure + * + * This function is called if the driver module is being unloaded. It frees all + * resources allocated to the device. + * + * Return: 0 on success or error value on failure + */ +static int pl353_nand_remove(struct platform_device *pdev) +{ + struct pl353_nand_info *xnand = platform_get_drvdata(pdev); + + /* Release resources, unregister device */ + nand_release(&xnand->chip.mtd); + /* kfree(NULL) is safe */ + kfree(xnand->parts); + + return 0; +} + +/* Match table for device tree binding */ +static const struct of_device_id pl353_nand_of_match[] = { + { .compatible = "arm,pl353-nand-r2p1" }, + {}, +}; +MODULE_DEVICE_TABLE(of, pl353_nand_of_match); + +/* + * pl353_nand_driver - This structure defines the NAND subsystem platform driver + */ +static struct platform_driver pl353_nand_driver = { + .probe = pl353_nand_probe, + .remove = pl353_nand_remove, + .driver = { + .name = PL353_NAND_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = pl353_nand_of_match, + }, +}; + +module_platform_driver(pl353_nand_driver); + +MODULE_AUTHOR("Xilinx, Inc."); +MODULE_ALIAS("platform:" PL353_NAND_DRIVER_NAME); +MODULE_DESCRIPTION("ARM PL353 NAND Flash Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index 0830c48973aa0..6c99417898860 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -1317,6 +1317,11 @@ struct macb { struct gem_stats gem; } hw_stats; +#ifdef CONFIG_FPGA_PERIPHERAL + struct notifier_block fpga_notifier; + bool fpga_down; +#endif + struct macb_or_gem_ops macbgem_ops; struct mii_bus *mii_bus; diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 17d4a3e039458..5824f9bbbcf8c 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -39,6 +39,13 @@ #include #include #include + +#ifdef CONFIG_FPGA_PERIPHERAL +#include +#include +#include +#endif + #include "macb.h" /* This structure is only used for MACB on SiFive FU540 devices */ @@ -2888,7 +2895,7 @@ static void macb_configure_dma(struct macb *bp) else dmacfg |= GEM_BIT(ENDIA_DESC); /* CPU in big endian */ - if (bp->dev->features & NETIF_F_HW_CSUM) + if (0 && bp->dev->features & NETIF_F_HW_CSUM) dmacfg |= GEM_BIT(TXCOEN); else dmacfg &= ~GEM_BIT(TXCOEN); @@ -3076,6 +3083,15 @@ static int macb_open(struct net_device *dev) if (err < 0) return err; +#ifdef CONFIG_FPGA_PERIPHERAL + /* If we're being opened while the FPGA is being reprogrammed, we can + * just return. The interface will be brought up when the FPGA is back + * up. + */ + if (bp->fpga_down) + return 0; +#endif + /* RX buffers initialization */ macb_init_rx_buffer_size(bp, bufsz); @@ -3133,6 +3149,14 @@ static int macb_close(struct net_device *dev) unsigned long flags; unsigned int q; +#ifdef CONFIG_FPGA_PERIPHERAL + /* If we're being closed while the FPGA is being reprogrammed, the + * interface is already down. We can just return. + */ + if (bp->fpga_down) + return 0; +#endif + netif_tx_stop_all_queues(dev); for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { @@ -4436,6 +4460,71 @@ static const struct net_device_ops macb_netdev_ops = { .ndo_setup_tc = macb_setup_tc, }; +#ifdef CONFIG_FPGA_PERIPHERAL +static int macb_fpga_notifier(struct notifier_block *nb, unsigned long val, void *data) +{ + struct macb *bp = container_of(nb, struct macb, fpga_notifier); + struct net_device *dev = bp->dev; + + switch (val) { + case FPGA_PERIPHERAL_DOWN: + netdev_dbg(bp->dev, + "macb_fpga_notifier: going down\n"); + + /* Synchronize with macb_open/close. */ + rtnl_lock(); + if (!bp->fpga_down) { + /* If the interface has been opened. */ + if (netif_running(bp->dev)) { + dev_deactivate(bp->dev); + macb_close(bp->dev); + if (phy_interrupt_is_valid(dev->phydev)) + phy_free_interrupt(dev->phydev); + phy_stop_machine_nolink(dev->phydev); + } + + bp->fpga_down = 1; + } + rtnl_unlock(); + break; + + case FPGA_PERIPHERAL_UP: + netdev_dbg(bp->dev, + "macb_fpga_notifier: coming up\n"); + + BUG_ON(!bp->fpga_down); + + /* Synchronize with macb_open/close. */ + rtnl_lock(); + + bp->fpga_down = 0; + + /* If the interface has been opened. */ + if (netif_running(bp->dev)) { + phy_start_machine(dev->phydev); + if (phy_interrupt_is_valid(dev->phydev)) + phy_request_interrupt(dev->phydev); + macb_open(bp->dev); + dev_activate(bp->dev); + } + + rtnl_unlock(); + break; + + case FPGA_PERIPHERAL_FAILED: + /* This interface is not coming back up. */ + break; + + default: + netdev_err(bp->dev, + "unsupported FPGA notifier value %lu\n", val); + break; + } + + return notifier_from_errno(0); +} +#endif + /* Configure peripheral capabilities according to device tree * and integration options used */ @@ -4617,6 +4706,14 @@ static int macb_init(struct platform_device *pdev) bp->tx_ring_size = DEFAULT_TX_RING_SIZE; bp->rx_ring_size = DEFAULT_RX_RING_SIZE; +#ifdef CONFIG_FPGA_PERIPHERAL + bp->fpga_notifier.notifier_call = macb_fpga_notifier; + + blocking_notifier_chain_register( + &fpgaperipheral_notifier_list, + &bp->fpga_notifier); +#endif + /* set the queue register mapping once for all: queue0 has a special * register mapping but we don't want to test the queue index then * compute the corresponding register offset at run time. @@ -4700,7 +4797,7 @@ static int macb_init(struct platform_device *pdev) dev->hw_features |= MACB_NETIF_LSO; /* Checksum offload is only available on gem with packet buffer */ - if (macb_is_gem(bp) && !(bp->caps & MACB_CAPS_FIFO_MODE)) + if (0 && macb_is_gem(bp) && !(bp->caps & MACB_CAPS_FIFO_MODE)) dev->hw_features |= NETIF_F_HW_CSUM | NETIF_F_RXCSUM; if (bp->caps & MACB_CAPS_SG_DISABLED) dev->hw_features &= ~NETIF_F_SG; @@ -5750,6 +5847,12 @@ static void macb_remove(struct platform_device *pdev) if (dev) { bp = netdev_priv(dev); + +#ifdef CONFIG_FPGA_PERIPHERAL + blocking_notifier_chain_unregister( + &fpgaperipheral_notifier_list, &bp->fpga_notifier); +#endif + unregister_netdev(dev); phy_exit(bp->sgmii_phy); mdiobus_unregister(bp->mii_bus); diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index c248c90510ae5..35da758f64852 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -181,6 +181,8 @@ #define MII_88E3016_PHY_SPEC_CTRL 0x10 #define MII_88E3016_DISABLE_SCRAMBLER 0x0200 #define MII_88E3016_AUTO_MDIX_CROSSOVER 0x0030 +#define MII_88E1512_LEDTimerControl_InterruptEnable 0x0080 + #define MII_88E1510_GEN_CTRL_REG_1 0x14 #define MII_88E1510_GEN_CTRL_REG_1_MODE_MASK 0x7 @@ -3682,10 +3684,45 @@ static const struct sfp_upstream_ops m88e1510_sfp_ops = { .disconnect_phy = phy_sfp_disconnect_phy, }; +static int m88e151x_setup_interrupt_pin(struct phy_device *phydev) +{ + u32 led_timer_control; + int err, old_page; + + old_page = marvell_read_page(phydev); + if (old_page < 0) + return old_page; + + /* Enable the interrupt output. This is just a pin configuration, + * we're not actually enabling any interrupts here. But the default + * configuration causes our interrupt pin to be asserted. We need + * to stop that as early as possible. + */ + err = marvell_set_page(phydev, MII_MARVELL_LED_PAGE); + + if (err < 0) + return err; + + led_timer_control = phy_read(phydev, MII_88E1318S_PHY_LED_TCR); + led_timer_control |= MII_88E1512_LEDTimerControl_InterruptEnable; + + err = phy_write(phydev, MII_88E1318S_PHY_LED_TCR, led_timer_control); + if (err < 0) + return err; + + err = marvell_set_page(phydev, old_page); + + return err; +} + static int m88e1510_probe(struct phy_device *phydev) { int err; + err = m88e151x_setup_interrupt_pin(phydev); + if (err) + return err; + err = marvell_probe(phydev); if (err) return err; diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 02da4a203ddd4..8201ec5db7b78 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -1347,6 +1347,25 @@ static void phy_error_precise(struct phy_device *phydev, phy_process_error(phydev); } +/** + * phy_stop_machine_nolink - stop the PHY state machine tracking + * @phydev: target phy_device struct + * + * Description: Stops the state machine timer, sets the state to UP + * (unless it wasn't up yet). This function must be called BEFORE + * phy_detach. Does not run the state machine to avoid taking the + * link down. + */ +void phy_stop_machine_nolink(struct phy_device *phydev) +{ + cancel_delayed_work_sync(&phydev->state_queue); + + mutex_lock(&phydev->lock); + if (phydev->state > PHY_UP && phydev->state != PHY_HALTED) + phydev->state = PHY_UP; + mutex_unlock(&phydev->lock); +} + /** * phy_error - enter ERROR state for this PHY device * @phydev: target phy_device struct diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index bdb06584d7e45..7ecd32672146a 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -32,6 +32,8 @@ #define WL1271_BOOT_RETRIES 3 #define WL1271_WAKEUP_TIMEOUT 500 +#define MAX_GLOBAL_80211_ISM_CENTER_FREQ 2462 + static char *fwlog_param; static int fwlog_mem_blocks = -1; static int bug_on_recovery = -1; @@ -67,6 +69,9 @@ static int wl12xx_set_authorized(struct wl1271 *wl, struct wl12xx_vif *wlvif) static void wl1271_reg_notify(struct wiphy *wiphy, struct regulatory_request *request) { + struct ieee80211_supported_band *band; + struct ieee80211_channel *ch; + int i; struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct wl1271 *wl = hw->priv; @@ -74,6 +79,18 @@ static void wl1271_reg_notify(struct wiphy *wiphy, if (request) wl->dfs_region = request->dfs_region; + /* Never speak first on channels 12, 13, and 14 even if + * the user hints that it's OK + */ + band = wiphy->bands[NL80211_BAND_2GHZ]; + for (i = 0; i < band->n_channels; i++) { + ch = &band->channels[i]; + if (ch->flags & IEEE80211_CHAN_DISABLED) + continue; + if (ch->center_freq > MAX_GLOBAL_80211_ISM_CENTER_FREQ) + ch->flags |= IEEE80211_CHAN_NO_IR; + } + wlcore_regdomain_config(wl); } @@ -5933,7 +5950,6 @@ static struct ieee80211_channel wl1271_channels[] = { { .hw_value = 11, .center_freq = 2462, .max_power = WLCORE_MAX_TXPWR }, { .hw_value = 12, .center_freq = 2467, .max_power = WLCORE_MAX_TXPWR }, { .hw_value = 13, .center_freq = 2472, .max_power = WLCORE_MAX_TXPWR }, - { .hw_value = 14, .center_freq = 2484, .max_power = WLCORE_MAX_TXPWR }, }; /* can't be const, mac80211 writes to this */ diff --git a/drivers/tty/serial/8250/8250_ni.c b/drivers/tty/serial/8250/8250_ni.c index b77dbcbdef303..e9975dfb93d3c 100644 --- a/drivers/tty/serial/8250/8250_ni.c +++ b/drivers/tty/serial/8250/8250_ni.c @@ -243,7 +243,19 @@ static int ni16550_get_regs(struct platform_device *pdev, port->iotype = UPIO_MEM; port->mapbase = regs->start; port->mapsize = resource_size(regs); - port->flags |= UPF_IOREMAP; + + /* + * Map the registers here (rather than relying on the generic + * UPF_IOREMAP deferred-mapping mechanism, which only ioremaps + * when the port is started) because this driver reads + * hardware registers (FIFO sizes, PMR, etc.) directly during + * probe(), before the port is ever started. + */ + port->membase = devm_ioremap(&pdev->dev, regs->start, + resource_size(regs)); + if (!port->membase) + return dev_err_probe(&pdev->dev, -ENOMEM, + "cannot map registers\n"); return 0; default: diff --git a/drivers/watchdog/nic7018_wdt.c b/drivers/watchdog/nic7018_wdt.c index e66f4866f94ff..3daf3e773f1d1 100644 --- a/drivers/watchdog/nic7018_wdt.c +++ b/drivers/watchdog/nic7018_wdt.c @@ -416,6 +416,15 @@ static int nic7018_probe(struct platform_device *pdev) return ret; } + ret = devm_request_threaded_irq(dev, irq, NULL, + nic7018_thread_isr, + IRQF_ONESHOT, + KBUILD_MODNAME, wdt); + if (ret) { + dev_err(dev, "failed to register interrupt handler\n"); + return ret; + } + /* Unlock WDT register */ outb(UNLOCK, wdt->io_base + WDT_REG_LOCK); diff --git a/include/linux/device_poll.h b/include/linux/device_poll.h new file mode 100644 index 0000000000000..633d36c78a37c --- /dev/null +++ b/include/linux/device_poll.h @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2014 National Instruments Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _LINUX_DEVICE_POLL_H_ +#define _LINUX_DEVICE_POLL_H_ + +#ifdef CONFIG_DEVICE_POLL + +#include + +struct device_poll; + +struct device_poll_ops { + /* Reinitialize, if the mode changes. */ + int (*reinit)(struct device_poll *device_poll); + + /* Lock and unlock, for consistency when changing settings. */ + void (*lock)(struct device_poll *device_poll); + void (*unlock)(struct device_poll *device_poll); + + /* Polled interrupt handler. */ + void (*interrupt)(struct device_poll *device_poll); +}; + +struct device_poll { + /* The following must be initialized by the driver before calling + * device_poll_init. + */ + + /* The device for which we're polling. */ + struct device *device; + + /* Device operations. */ + struct device_poll_ops *ops; + + /* A capability can be specified to allow non-root users to modify + * the sysfs attributes. + */ + bool use_capability; + int capability; + + /* Polling interval in milliseconds. A value of 0 or less means + * use interrupts. + */ + int interval; + + /* Polling task policy and priority, such as SCHED_FIFO 10. */ + int policy; + int priority; + + /* The following are internal struct members and should not be touched + * by drivers. + */ + + struct task_struct *task; + int enabled; + + struct dev_ext_attribute interval_attr; + struct dev_ext_attribute policy_attr; + struct dev_ext_attribute priority_attr; + struct attribute *attrs[4]; + struct attribute_group attr_group; + +#ifdef CONFIG_DEVICE_POLL_NI_COMPAT + /* For backwards compatibility with NI software. */ + struct dev_ext_attribute ni_interval_attr; +#endif +}; + +int device_poll_init(struct device_poll *device_poll); +void device_poll_exit(struct device_poll *device_poll); + +int device_poll_request_irq(struct device_poll *device_poll); +void device_poll_free_irq(struct device_poll *device_poll); + +static inline int device_poll_is_active(struct device_poll *device_poll) +{ + return likely(device_poll) && (device_poll->task != NULL); +} + +static inline void device_poll_enable_irq(struct device_poll *device_poll) +{ + if (device_poll_is_active(device_poll)) { + device_poll->enabled = 1; + /* Ensure changes to device_poll->enabled are seen by the + * polling thread. + */ + smp_wmb(); + } +} + +static inline void device_poll_disable_irq(struct device_poll *device_poll) +{ + if (device_poll_is_active(device_poll)) { + device_poll->enabled = 0; + /* Ensure changes to device_poll->enabled are seen by the + * polling thread. + */ + smp_wmb(); + } +} + +#endif + +#endif /* _LINUX_DEVICE_POLL_H_ */ diff --git a/include/linux/leds.h b/include/linux/leds.h index b16b803cc1ac5..f4c173f66153d 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -233,6 +233,7 @@ struct led_classdev { */ struct device *(*hw_control_get_device)(struct led_classdev *led_cdev); #endif + u8 inverted; #ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED int brightness_hw_changed; diff --git a/include/linux/netdev_poll.h b/include/linux/netdev_poll.h new file mode 100644 index 0000000000000..3785454de3eea --- /dev/null +++ b/include/linux/netdev_poll.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2014 National Instruments Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _LINUX_NETDEV_POLL_H_ +#define _LINUX_NETDEV_POLL_H_ + +#ifdef CONFIG_NETDEV_POLL + +#include + +int netdev_poll_init(struct device_poll *device_poll); + +#endif + +#endif /* _LINUX_NETDEV_POLL_H_ */ diff --git a/include/linux/phy.h b/include/linux/phy.h index 0bc00a4cceb24..6ad414f0bc712 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -2042,6 +2042,7 @@ void phy_trigger_machine(struct phy_device *phydev); void phy_mac_interrupt(struct phy_device *phydev); void phy_start_machine(struct phy_device *phydev); void phy_stop_machine(struct phy_device *phydev); +void phy_stop_machine_nolink(struct phy_device *phydev); void phy_ethtool_ksettings_get(struct phy_device *phydev, struct ethtool_link_ksettings *cmd); int phy_ethtool_ksettings_set(struct phy_device *phydev, diff --git a/include/misc/fpgaperipheral.h b/include/misc/fpgaperipheral.h new file mode 100644 index 0000000000000..37817cad66b64 --- /dev/null +++ b/include/misc/fpgaperipheral.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 National Instruments Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _FPGA_PERIPHERAL_H_ +#define _FPGA_PERIPHERAL_H_ + +#include + +/* + * The value passed to notifier callbacks will be one of these. + * FPGA_PERIPHERAL_DOWN: The FPGA is about to be unprogrammed. + * FPGA_PERIPHERAL_UP: The FPGA is now programmed. + * This may be called even if not preceded by an FPGA_PERIPHERAL_DOWN. + * FPGA_PERIPHERAL_FAILED: The FPGA programming failed. + * If this is called, clients should not use the FPGA as it might be in + * a bad state. This is to notify the clients to abort any waiting for + * the FPGA to come back up. + * + * Client callbacks should always return notifier_from_errno(0) so the + * call chain continues to all clients. + */ +#define FPGA_PERIPHERAL_DOWN 0 +#define FPGA_PERIPHERAL_UP 1 +#define FPGA_PERIPHERAL_FAILED 2 + +extern struct blocking_notifier_head fpgaperipheral_notifier_list; + +#endif /* _FPGA_PERIPHERAL_H_ */ diff --git a/net/Kconfig b/net/Kconfig index 1d3f757d4b07e..de5ee34ac7c38 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -452,6 +452,9 @@ config LWTUNNEL tunnels like mpls. There is no netdevice associated with a light weight tunnel endpoint. Tunnel encapsulation parameters are stored with light weight tunnel state associated with fib routes. +config NETDEV_POLL + bool + select DEVICE_POLL config LWTUNNEL_BPF bool "Execute BPF program as route nexthop action" diff --git a/net/core/Makefile b/net/core/Makefile index 9ef2099c54263..5fd6c429cd5e2 100644 --- a/net/core/Makefile +++ b/net/core/Makefile @@ -48,3 +48,4 @@ obj-$(CONFIG_NET_TEST) += net_test.o obj-$(CONFIG_NET_DEVMEM) += devmem.o obj-$(CONFIG_DEBUG_NET) += lock_debug.o obj-$(CONFIG_FAIL_SKB_REALLOC) += skb_fault_injection.o +obj-$(CONFIG_NETDEV_POLL) += dev_poll.o diff --git a/net/core/dev_poll.c b/net/core/dev_poll.c new file mode 100644 index 0000000000000..1f2d4559ddd52 --- /dev/null +++ b/net/core/dev_poll.c @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2014 National Instruments Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +/* netdev_poll internal functions */ + +static int netdev_poll_reinit(struct device_poll *device_poll) +{ + int ret = 0; + struct net_device *netdev = to_net_dev(device_poll->device); + + if (netif_running(netdev)) { + netdev->netdev_ops->ndo_stop(netdev); + ret = netdev->netdev_ops->ndo_open(netdev); + } + + return ret; +} + +static void netdev_poll_lock(struct device_poll *device_poll) +{ + rtnl_lock(); +} + +static void netdev_poll_unlock(struct device_poll *device_poll) +{ + rtnl_unlock(); +} + +/* netdev_poll external functions */ + +int netdev_poll_init(struct device_poll *device_poll) +{ + if (!device_poll || !device_poll->device || !device_poll->ops) + return -EINVAL; + + if (!device_poll->ops->reinit) + device_poll->ops->reinit = netdev_poll_reinit; + if (!device_poll->ops->lock) + device_poll->ops->lock = netdev_poll_lock; + if (!device_poll->ops->unlock) + device_poll->ops->unlock = netdev_poll_unlock; + + /* Allow changes from any process with CAP_NET_ADMIN. */ + device_poll->use_capability = 1; + device_poll->capability = CAP_NET_ADMIN; + + return device_poll_init(device_poll); +} +EXPORT_SYMBOL(netdev_poll_init); diff --git a/scripts/Makefile.package b/scripts/Makefile.package index 74bcb9e7f7a45..332e15b8dc115 100644 --- a/scripts/Makefile.package +++ b/scripts/Makefile.package @@ -189,6 +189,14 @@ tar-pkg: linux-$(KERNELRELEASE)-$(ARCH).tar tar%-pkg: linux-$(KERNELRELEASE)-$(ARCH).tar.% FORCE @: +# ni-pkg - generate an NI Zynq FIT image for all device models +# --------------------------------------------------------------------------- + +PHONY += ni-pkg +ni-pkg: FORCE + +$(Q)$(MAKE) -f $(srctree)/Makefile Image modules dtbs + $(Q)$(CONFIG_SHELL) $(srctree)/scripts/package/buildnipkg + # perf-tar*-src-pkg - generate a source tarball with perf source # --------------------------------------------------------------------------- @@ -245,6 +253,7 @@ help: @echo ' tarbz2-pkg - Build the kernel as a bzip2 compressed tarball' @echo ' tarxz-pkg - Build the kernel as a xz compressed tarball' @echo ' tarzst-pkg - Build the kernel as a zstd compressed tarball' + @echo ' ni-pkg - Build the kernel and needed files for NI targets' @echo ' perf-tar-src-pkg - Build the perf source tarball with no compression' @echo ' perf-targz-src-pkg - Build the perf source tarball with gzip compression' @echo ' perf-tarbz2-src-pkg - Build the perf source tarball with bz2 compression' diff --git a/scripts/mod/arm-nilrt-linux-gnueabi-elfconfig.h b/scripts/mod/arm-nilrt-linux-gnueabi-elfconfig.h new file mode 100644 index 0000000000000..807d67db5eed8 --- /dev/null +++ b/scripts/mod/arm-nilrt-linux-gnueabi-elfconfig.h @@ -0,0 +1,4 @@ +#define KERNEL_ELFCLASS ELFCLASS32 +#define KERNEL_ELFDATA ELFDATA2LSB +#define HOST_ELFCLASS ELFCLASS32 +#define HOST_ELFDATA ELFDATA2LSB diff --git a/scripts/mod/x86_64-nilrt-linux-elfconfig.h b/scripts/mod/x86_64-nilrt-linux-elfconfig.h new file mode 100644 index 0000000000000..6dff0d9351a82 --- /dev/null +++ b/scripts/mod/x86_64-nilrt-linux-elfconfig.h @@ -0,0 +1,4 @@ +#define KERNEL_ELFCLASS ELFCLASS64 +#define KERNEL_ELFDATA ELFDATA2LSB +#define HOST_ELFCLASS ELFCLASS64 +#define HOST_ELFDATA ELFDATA2LSB diff --git a/scripts/package/buildnipkg b/scripts/package/buildnipkg new file mode 100644 index 0000000000000..2999116012c80 --- /dev/null +++ b/scripts/package/buildnipkg @@ -0,0 +1,148 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-only + +set -eu + +if [ "${ARCH:-}" != "arm" ]; then + echo "error: ni-pkg requires ARCH=arm" >&2 + exit 1 +fi + +mkimage="${MKIMAGE:-mkimage}" +if ! command -v "${mkimage}" >/dev/null 2>&1; then + echo "error: ${mkimage} is required to build the NI FIT image" >&2 + exit 1 +fi + +bootdir="${objtree}/ni-install/arm/boot" +kernel="${objtree}/arch/arm/boot/Image" +dtsdir="${objtree}/arch/arm/boot/dts/xilinx" + +rm -rf -- "${bootdir}" +mkdir -p -- "${bootdir}" + +cp -- "${kernel}" "${bootdir}/vmlinux" +gzip -f -9 "${bootdir}/vmlinux" + +cat > "${bootdir}/bootscript.txt" <<'EOF' + +if test ${DeviceCode} -eq 0x7AAE; then + setenv set_args 'setenv bootargs ${consoleparam} root=/dev/mmcblk0p3 rw ${usb_gadget_args} rcu_nocbs=all rootdelay=1 $othbootargs' +else + setenv set_args 'setenv bootargs ${consoleparam} $mtdparts ubi.mtd=boot-config ubi.mtd=root root=ubi1:rootfs rw rootfstype=ubifs kthreadd_pri=25 ksoftirqd_pri=8 irqthread_pri=15 ${usb_gadget_args} $othbootargs' +fi + +# cRIO-9068 doesn't have USB gadget, so skip it on that device +if test ${DeviceCode} != 0x76D6; then + usb_gadget_args="g_ether.idVendor=${USBVendorID} g_ether.idProduct=${USBProductID} g_ether.iProduct=\"${USBProduct} [${hostname}]\" g_ether.iSerialNumber=${serial#} g_ether.dev_addr=${usbgadgetethaddr} g_ether.bcdDevice=${USBDevice}" +fi + +if test -n \"$bootscriptenvrun\" ; then run bootscriptenvrun; fi + +silent_tmp=${silent} +setenv silent +echo "Booting LabVIEW RT (custom kernel)..." +setenv silent ${silent_tmp} + +run set_args +bootm ${loadaddr}#config_${DeviceCode} +EOF + +dtb_list="" +device_codes="" +for dtb_path in "${dtsdir}"/ni-*.dtb; do + [ -e "${dtb_path}" ] || continue + dtb_name=$(basename "${dtb_path}") + devcode=${dtb_name#ni-} + devcode=${devcode%.dtb} + cp -- "${dtb_path}" "${bootdir}/${dtb_name}" + dtb_list="${dtb_list} ${devcode}" + device_codes="${device_codes}0x${devcode} " +done + +if [ -z "${dtb_list}" ]; then + echo "error: no ni-*.dtb files found in ${dtsdir}" >&2 + exit 1 +fi + +timestamp=$(date +%F_%R) +cat > "${bootdir}/linux.its" <; + + images { + kernel@1 { + description = "Linux kernel"; + version = "populate-kernel-export-version"; + data = /incbin/("./vmlinux.gz"); + type = "kernel"; + arch = "arm"; + os = "linux"; + compression = "gzip"; + load = <0x00008000>; + entry = <0x00008000>; + hash@1 { + algo = "crc32"; + }; + }; +EOF + +for devcode in ${dtb_list}; do + cat >> "${bootdir}/linux.its" <> "${bootdir}/linux.its" <> "${bootdir}/linux.its" <> "${bootdir}/linux.its" <