diff options
author | Olof Johansson <olof@lixom.net> | 2019-04-28 23:43:07 -0700 |
---|---|---|
committer | Olof Johansson <olof@lixom.net> | 2019-04-28 23:43:07 -0700 |
commit | 1895ef4ef229ec1873a78ce31fe50b38cfd2b58a (patch) | |
tree | 0db44f6e89cfa0bb732a262362d9748a27788b5b | |
parent | 6a508f98d95e355a40064e23643756773f4092bd (diff) | |
parent | 1fae0ad1e2032a603f93d4ad752bfa6fe7c9b887 (diff) | |
download | linux-stable-1895ef4ef229ec1873a78ce31fe50b38cfd2b58a.tar.gz linux-stable-1895ef4ef229ec1873a78ce31fe50b38cfd2b58a.tar.bz2 linux-stable-1895ef4ef229ec1873a78ce31fe50b38cfd2b58a.zip |
Merge tag 'ixp4xx-for-armsoc' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-nomadik into arm/soc
This modernizes the IXP4xx platform and adds initial Device Tree
Support. We migrate to MULTI_IRQ_HANDLER, bumps the IRQs to
offset 16, converts to SPARSE_IRQ, then we add proper subsystem
drivers in each subsystem for irqchip, GPIO and clocksource and
switch over to using these new drivers.
Next we modernize the NPE and QMGR drivers and push them down
into drivers/soc.
This has been tested on the IXP4xx NSLU2 and the Gateworks
GW2358-4.
* tag 'ixp4xx-for-armsoc' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-nomadik: (31 commits)
ARM: dts: Add queue manager and NPE to the IXP4xx DTSI
soc: ixp4xx: qmgr: Add DT probe code
soc: ixp4xx: qmgr: Add DT bindings for IXP4xx qmgr
soc: ixp4xx: npe: Add DT probe code
soc: ixp4xx: Add DT bindings for IXP4xx NPE
soc: ixp4xx: qmgr: Pass resources
soc: ixp4xx: Remove unused functions
soc: ixp4xx: Uninline several functions
soc: ixp4xx: npe: Pass addresses as resources
ARM: ixp4xx: Turn the QMGR into a platform device
ARM: ixp4xx: Turn the NPE into a platform device
ARM: ixp4xx: Move IXP4xx QMGR and NPE headers
ARM: ixp4xx: Move NPE and QMGR to drivers/soc
ARM: dts: Add some initial IXP4xx device trees
ARM: ixp4xx: Add device tree boot support
ARM: ixp4xx: Add DT bindings
gpio: ixp4xx: Add OF probing support
gpio: ixp4xx: Add DT bindings
clocksource/drivers/ixp4xx: Add OF initialization support
clocksource/drivers/ixp4xx: Add DT bindings
...
Signed-off-by: Olof Johansson <olof@lixom.net>
68 files changed, 2415 insertions, 913 deletions
diff --git a/Documentation/devicetree/bindings/arm/intel-ixp4xx.yaml b/Documentation/devicetree/bindings/arm/intel-ixp4xx.yaml new file mode 100644 index 000000000000..f4f7451e5e8a --- /dev/null +++ b/Documentation/devicetree/bindings/arm/intel-ixp4xx.yaml @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/arm/intel-ixp4xx.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Intel IXP4xx Device Tree Bindings + +maintainers: + - Linus Walleij <linus.walleij@linaro.org> + +properties: + compatible: + oneOf: + - items: + - enum: + - linksys,nslu2 + - const: intel,ixp42x + - items: + - enum: + - gateworks,gw2358 + - const: intel,ixp43x diff --git a/Documentation/devicetree/bindings/firmware/intel,ixp4xx-network-processing-engine.yaml b/Documentation/devicetree/bindings/firmware/intel,ixp4xx-network-processing-engine.yaml new file mode 100644 index 000000000000..8cb136c376fb --- /dev/null +++ b/Documentation/devicetree/bindings/firmware/intel,ixp4xx-network-processing-engine.yaml @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright 2019 Linaro Ltd. +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/firmware/intel-ixp4xx-network-processing-engine.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Intel IXP4xx Network Processing Engine + +maintainers: + - Linus Walleij <linus.walleij@linaro.org> + +description: | + On the IXP4xx SoCs, the Network Processing Engine (NPE) is a small + processor that can load a firmware to perform offloading of networking + and crypto tasks. It also manages the MDIO bus to the ethernet PHYs + on the IXP4xx platform. All IXP4xx platforms have three NPEs at + consecutive memory locations. They are all included in the same + device node since they are not independent of each other. + +properties: + compatible: + oneOf: + - items: + - const: intel,ixp4xx-network-processing-engine + + reg: + minItems: 3 + maxItems: 3 + items: + - description: NPE0 register range + - description: NPE1 register range + - description: NPE2 register range + +required: + - compatible + - reg + +examples: + - | + npe@c8006000 { + compatible = "intel,ixp4xx-network-processing-engine"; + reg = <0xc8006000 0x1000>, <0xc8007000 0x1000>, <0xc8008000 0x1000>; + }; diff --git a/Documentation/devicetree/bindings/interrupt-controller/intel,ixp4xx-interrupt.yaml b/Documentation/devicetree/bindings/interrupt-controller/intel,ixp4xx-interrupt.yaml new file mode 100644 index 000000000000..bae10e261fa9 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/intel,ixp4xx-interrupt.yaml @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright 2018 Linaro Ltd. +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/interrupt/intel-ixp4xx-interrupt.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Intel IXP4xx XScale Networking Processors Interrupt Controller + +maintainers: + - Linus Walleij <linus.walleij@linaro.org> + +description: | + This interrupt controller is found in the Intel IXP4xx processors. + Some processors have 32 interrupts, some have up to 64 interrupts. + The exact number of interrupts is determined from the compatible + string. + + The distinct IXP4xx families with different interrupt controller + variations are IXP42x, IXP43x, IXP45x and IXP46x. Those four + families were the only ones to reach the developer and consumer + market. + +properties: + compatible: + items: + - enum: + - intel,ixp42x-interrupt + - intel,ixp43x-interrupt + - intel,ixp45x-interrupt + - intel,ixp46x-interrupt + + reg: + maxItems: 1 + + interrupt-controller: true + + '#interrupt-cells': + const: 2 + +required: + - compatible + - reg + - interrupt-controller + - '#interrupt-cells' + +examples: + - | + intcon: interrupt-controller@c8003000 { + compatible = "intel,ixp43x-interrupt"; + reg = <0xc8003000 0x100>; + interrupt-controller; + #interrupt-cells = <2>; + }; diff --git a/Documentation/devicetree/bindings/misc/intel,ixp4xx-queue-manager.yaml b/Documentation/devicetree/bindings/misc/intel,ixp4xx-queue-manager.yaml new file mode 100644 index 000000000000..d2313b1d9405 --- /dev/null +++ b/Documentation/devicetree/bindings/misc/intel,ixp4xx-queue-manager.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright 2019 Linaro Ltd. +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/misc/intel-ixp4xx-ahb-queue-manager.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Intel IXP4xx AHB Queue Manager + +maintainers: + - Linus Walleij <linus.walleij@linaro.org> + +description: | + The IXP4xx AHB Queue Manager maintains queues as circular buffers in + an 8KB embedded SRAM along with hardware pointers. It is used by both + the XScale processor and the NPEs (Network Processing Units) in the + IXP4xx for accelerating queues, especially for networking. Clients pick + queues from the queue manager with foo-queue = <&qmgr N> where the + &qmgr is a phandle to the queue manager and N is the queue resource + number. The queue resources available and their specific purpose + on a certain IXP4xx system will vary. + +properties: + compatible: + items: + - const: intel,ixp4xx-ahb-queue-manager + + reg: + maxItems: 1 + + interrupts: + items: + - description: Interrupt for queues 0-31 + - description: Interrupt for queues 32-63 + +required: + - compatible + - reg + - interrupts + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + + qmgr: queue-manager@60000000 { + compatible = "intel,ixp4xx-ahb-queue-manager"; + reg = <0x60000000 0x4000>; + interrupts = <3 IRQ_TYPE_LEVEL_HIGH>, <4 IRQ_TYPE_LEVEL_HIGH>; + }; diff --git a/Documentation/devicetree/bindings/timer/intel,ixp4xx-timer.yaml b/Documentation/devicetree/bindings/timer/intel,ixp4xx-timer.yaml new file mode 100644 index 000000000000..a36a0746c056 --- /dev/null +++ b/Documentation/devicetree/bindings/timer/intel,ixp4xx-timer.yaml @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright 2018 Linaro Ltd. +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/timer/intel-ixp4xx-timer.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Intel IXP4xx XScale Networking Processors Timers + +maintainers: + - Linus Walleij <linus.walleij@linaro.org> + +description: This timer is found in the Intel IXP4xx processors. + +properties: + compatible: + items: + - const: intel,ixp4xx-timer + + reg: + description: Should contain registers location and length + + interrupts: + minItems: 1 + maxItems: 2 + items: + - description: Timer 1 interrupt + - description: Timer 2 interrupt + +required: + - compatible + - reg + - interrupts + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + timer@c8005000 { + compatible = "intel,ixp4xx-timer"; + reg = <0xc8005000 0x100>; + interrupts = <5 IRQ_TYPE_LEVEL_HIGH>; + }; diff --git a/MAINTAINERS b/MAINTAINERS index c0e3287e097d..e827333d5404 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1685,11 +1685,21 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained ARM/INTEL IXP4XX ARM ARCHITECTURE +M: Linus Walleij <linusw@kernel.org> M: Imre Kaloz <kaloz@openwrt.org> M: Krzysztof Halasa <khalasa@piap.pl> L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained +F: Documentation/devicetree/bindings/arm/intel-ixp4xx.yaml +F: Documentation/devicetree/bindings/gpio/intel,ixp4xx-gpio.txt +F: Documentation/devicetree/bindings/interrupt-controller/intel,ixp4xx-interrupt.yaml +F: Documentation/devicetree/bindings/timer/intel,ixp4xx-timer.yaml F: arch/arm/mach-ixp4xx/ +F: drivers/clocksource/timer-ixp4xx.c +F: drivers/gpio/gpio-ixp4xx.c +F: drivers/irqchip/irq-ixp4xx.c +F: include/linux/irqchip/irq-ixp4xx.h +F: include/linux/platform_data/timer-ixp4xx.h ARM/INTEL RESEARCH IMOTE/STARGATE 2 MACHINE SUPPORT M: Jonathan Cameron <jic23@cam.ac.uk> @@ -7876,10 +7886,10 @@ F: Documentation/media/v4l-drivers/ipu3.rst INTEL IXP4XX QMGR, NPE, ETHERNET and HSS SUPPORT M: Krzysztof Halasa <khalasa@piap.pl> S: Maintained -F: arch/arm/mach-ixp4xx/include/mach/qmgr.h -F: arch/arm/mach-ixp4xx/include/mach/npe.h -F: arch/arm/mach-ixp4xx/ixp4xx_qmgr.c -F: arch/arm/mach-ixp4xx/ixp4xx_npe.c +F: include/linux/soc/ixp4xx/qmgr.h +F: include/linux/soc/ixp4xx/npe.h +F: drivers/soc/ixp4xx/ixp4xx-qmgr.c +F: drivers/soc/ixp4xx/ixp4xx-npe.c F: drivers/net/ethernet/xscale/ixp4xx_eth.c F: drivers/net/wan/ixp4xx_hss.c diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 61d949e9233f..29bb2aca02d4 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -429,12 +429,15 @@ config ARCH_IXP4XX depends on MMU select ARCH_HAS_DMA_SET_COHERENT_MASK select ARCH_SUPPORTS_BIG_ENDIAN - select CLKSRC_MMIO select CPU_XSCALE select DMABOUNCE if PCI select GENERIC_CLOCKEVENTS + select GENERIC_IRQ_MULTI_HANDLER + select GPIO_IXP4XX select GPIOLIB select HAVE_PCI + select IXP4XX_IRQ + select IXP4XX_TIMER select NEED_MACH_IO_H select USB_EHCI_BIG_ENDIAN_DESC select USB_EHCI_BIG_ENDIAN_MMIO diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index f4f5aeaf3298..7af4e3289a89 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -229,6 +229,9 @@ dtb-$(CONFIG_ARCH_HIX5HD2) += \ dtb-$(CONFIG_ARCH_INTEGRATOR) += \ integratorap.dtb \ integratorcp.dtb +dtb-$(CONFIG_ARCH_IXP4XX) += \ + intel-ixp42x-linksys-nslu2.dtb \ + intel-ixp43x-gateworks-gw2358.dtb dtb-$(CONFIG_ARCH_KEYSTONE) += \ keystone-k2hk-evm.dtb \ keystone-k2l-evm.dtb \ diff --git a/arch/arm/boot/dts/intel-ixp42x-linksys-nslu2.dts b/arch/arm/boot/dts/intel-ixp42x-linksys-nslu2.dts new file mode 100644 index 000000000000..8fcd95805ff4 --- /dev/null +++ b/arch/arm/boot/dts/intel-ixp42x-linksys-nslu2.dts @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: ISC +/* + * Device Tree file for Linksys NSLU2 + */ + +/dts-v1/; + +#include "intel-ixp42x.dtsi" +#include <dt-bindings/input/input.h> + +/ { + model = "Linksys NSLU2 (Network Storage Link for USB 2.0 Disk Drives)"; + compatible = "linksys,nslu2", "intel,ixp42x"; + #address-cells = <1>; + #size-cells = <1>; + + memory@0 { + /* 32 MB SDRAM */ + device_type = "memory"; + reg = <0x00000000 0x2000000>; + }; + + chosen { + bootargs = "console=ttyS0,115200n8 root=/dev/mtdblock2 rw rootfstype=squashfs,jffs2 rootwait"; + stdout-path = "uart0:115200n8"; + }; + + aliases { + serial0 = &uart0; + }; + + leds { + compatible = "gpio-leds"; + led-status { + label = "nslu2:red:status"; + gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>; + default-state = "on"; + linux,default-trigger = "heartbeat"; + }; + led-ready { + label = "nslu2:green:ready"; + gpios = <&gpio0 1 GPIO_ACTIVE_HIGH>; + default-state = "on"; + }; + led-disk-1 { + label = "nslu2:green:disk-1"; + gpios = <&gpio0 3 GPIO_ACTIVE_LOW>; + default-state = "off"; + }; + led-disk-2 { + label = "nslu2:green:disk-2"; + gpios = <&gpio0 2 GPIO_ACTIVE_LOW>; + default-state = "off"; + }; + }; + + gpio_keys { + compatible = "gpio-keys"; + + button-power { + wakeup-source; + linux,code = <KEY_POWER>; + label = "power"; + gpios = <&gpio0 5 GPIO_ACTIVE_HIGH>; + }; + button-reset { + wakeup-source; + linux,code = <KEY_ESC>; + label = "reset"; + gpios = <&gpio0 12 GPIO_ACTIVE_LOW>; + }; + }; + + i2c { + compatible = "i2c-gpio"; + sda-gpios = <&gpio0 7 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio0 6 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>; + #address-cells = <1>; + #size-cells = <0>; + + rtc@6f { + compatible = "xicor,x1205"; + reg = <0x6f>; + }; + }; + + gpio-poweroff { + compatible = "gpio-poweroff"; + gpios = <&gpio0 8 GPIO_ACTIVE_HIGH>; + timeout-ms = <5000>; + }; + + /* The first 16MB region on the expansion bus */ + flash@50000000 { + compatible = "intel,ixp4xx-flash", "cfi-flash"; + bank-width = <2>; + /* + * 8 MB of Flash in 0x20000 byte blocks + * mapped in at 0x50000000 + */ + reg = <0x50000000 0x800000>; + + partitions { + compatible = "redboot-fis"; + /* Eraseblock at 0x7e0000 */ + fis-index-block = <0x3f>; + }; + }; +}; diff --git a/arch/arm/boot/dts/intel-ixp42x.dtsi b/arch/arm/boot/dts/intel-ixp42x.dtsi new file mode 100644 index 000000000000..a9622ca850cc --- /dev/null +++ b/arch/arm/boot/dts/intel-ixp42x.dtsi @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: ISC +/* + * Device Tree file for Intel XScale Network Processors + * in the IXP 42x series. This series has 32 interrupts. + */ +#include "intel-ixp4xx.dtsi" + +/ { + soc { + interrupt-controller@c8003000 { + compatible = "intel,ixp42x-interrupt"; + }; + + /* + * This is the USB Device Mode (UDC) controller, which is used + * to present the IXP4xx as a device on a USB bus. + */ + usb@c800b000 { + compatible = "intel,ixp4xx-udc"; + reg = <0xc800b000 0x1000>; + interrupts = <12 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + }; + }; +}; diff --git a/arch/arm/boot/dts/intel-ixp43x-gateworks-gw2358.dts b/arch/arm/boot/dts/intel-ixp43x-gateworks-gw2358.dts new file mode 100644 index 000000000000..ba1163a1e1e7 --- /dev/null +++ b/arch/arm/boot/dts/intel-ixp43x-gateworks-gw2358.dts @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: ISC +/* + * Device Tree file for Gateworks IXP43x-based Cambria GW2358 + */ + +/dts-v1/; + +#include "intel-ixp43x.dtsi" + +/ { + model = "Gateworks Cambria GW2358"; + compatible = "gateworks,gw2358", "intel,ixp43x"; + #address-cells = <1>; + #size-cells = <1>; + + memory@0 { + /* 128 MB SDRAM */ + device_type = "memory"; + reg = <0x00000000 0x8000000>; + }; + + chosen { + bootargs = "console=ttyS0,115200n8 root=/dev/mtdblock2 rw rootfstype=squashfs,jffs2 rootwait"; + stdout-path = "uart0:115200n8"; + }; + + aliases { + serial0 = &uart0; + }; + + leds { + compatible = "gpio-leds"; + led-user { + label = "gw2358:green:LED"; + gpios = <&pld1 0 GPIO_ACTIVE_LOW>; + default-state = "on"; + linux,default-trigger = "heartbeat"; + }; + }; + + + i2c { + compatible = "i2c-gpio"; + sda-gpios = <&gpio0 7 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio0 6 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>; + #address-cells = <1>; + #size-cells = <0>; + + hwmon@28 { + compatible = "adi,ad7418"; + reg = <0x28>; + }; + rtc: ds1672@68 { + compatible = "dallas,ds1672"; + reg = <0x68>; + }; + eeprom@51 { + compatible = "atmel,24c08"; + reg = <0x51>; + pagesize = <16>; + size = <1024>; + read-only; + }; + pld0: pld@56 { + compatible = "gateworks,pld-gpio"; + reg = <0x56>; + gpio-controller; + #gpio-cells = <2>; + }; + /* This PLD just handles the LED and user button */ + pld1: pld@57 { + compatible = "gateworks,pld-gpio"; + reg = <0x57>; + gpio-controller; + #gpio-cells = <2>; + }; + }; + + flash@50000000 { + compatible = "intel,ixp4xx-flash", "cfi-flash"; + bank-width = <2>; + /* + * 32 MB of Flash in 0x20000 byte blocks + * mapped in at 0x50000000 + */ + reg = <0x50000000 0x2000000>; + + partitions { + compatible = "redboot-fis"; + /* Eraseblock at 0x1fe0000 */ + fis-index-block = <0xff>; + }; + }; +}; diff --git a/arch/arm/boot/dts/intel-ixp43x.dtsi b/arch/arm/boot/dts/intel-ixp43x.dtsi new file mode 100644 index 000000000000..494fb2ff57a0 --- /dev/null +++ b/arch/arm/boot/dts/intel-ixp43x.dtsi @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: ISC +/* + * Device Tree file for Intel XScale Network Processors + * in the IXP 43x series. This series has 64 interrupts and adds a few more + * peripherals over the 42x series. + */ +#include "intel-ixp4xx.dtsi" + +/ { + soc { + interrupt-controller@c8003000 { + compatible = "intel,ixp43x-interrupt"; + }; + }; +}; diff --git a/arch/arm/boot/dts/intel-ixp45x-ixp46x.dtsi b/arch/arm/boot/dts/intel-ixp45x-ixp46x.dtsi new file mode 100644 index 000000000000..f8cd506659dc --- /dev/null +++ b/arch/arm/boot/dts/intel-ixp45x-ixp46x.dtsi @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: ISC +/* + * Device Tree file for Intel XScale Network Processors + * in the IXP45x and IXP46x series. This series has 64 interrupts and adds a + * few more peripherals over the 42x and 43x series so this extends the + * basic IXP4xx DTSI. + */ +#include "intel-ixp4xx.dtsi" + +/ { + soc { + interrupt-controller@c8003000 { + compatible = "intel,ixp43x-interrupt"; + }; + + /* + * This is the USB Device Mode (UDC) controller, which is used + * to present the IXP4xx as a device on a USB bus. + */ + usb@c800b000 { + compatible = "intel,ixp4xx-udc"; + reg = <0xc800b000 0x1000>; + interrupts = <12 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + }; + + i2c@c8011000 { + compatible = "intel,ixp4xx-i2c"; + reg = <0xc8011000 0x18>; + interrupts = <33 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + }; + }; +}; diff --git a/arch/arm/boot/dts/intel-ixp4xx.dtsi b/arch/arm/boot/dts/intel-ixp4xx.dtsi new file mode 100644 index 000000000000..d4a09584f417 --- /dev/null +++ b/arch/arm/boot/dts/intel-ixp4xx.dtsi @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: ISC +/* + * Device Tree file for Intel XScale Network Processors + * in the IXP 4xx series. + */ +#include <dt-bindings/interrupt-controller/irq.h> +#include <dt-bindings/gpio/gpio.h> + +/ { + soc { + #address-cells = <1>; + #size-cells = <1>; + ranges; + compatible = "simple-bus"; + interrupt-parent = <&intcon>; + + qmgr: queue-manager@60000000 { + compatible = "intel,ixp4xx-ahb-queue-manager"; + reg = <0x60000000 0x4000>; + interrupts = <3 IRQ_TYPE_LEVEL_HIGH>, <4 IRQ_TYPE_LEVEL_HIGH>; + }; + + uart0: serial@c8000000 { + compatible = "intel,xscale-uart"; + reg = <0xc8000000 0x1000>; + /* + * The reg-offset and reg-shift is a side effect + * of running the platform in big endian mode. + */ + reg-offset = <3>; + reg-shift = <2>; + interrupts = <15 IRQ_TYPE_LEVEL_HIGH>; + clock-frequency = <14745600>; + no-loopback-test; + }; + + gpio0: gpio@c8004000 { + compatible = "intel,ixp4xx-gpio"; + reg = <0xc8004000 0x1000>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + intcon: interrupt-controller@c8003000 { + /* + * Note: no compatible string. The subvariant of the + * chip needs to define what version it is. The + * location of the interrupt controller is fixed in + * memory across all variants. + */ + reg = <0xc8003000 0x100>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + timer@c8005000 { + compatible = "intel,ixp4xx-timer"; + reg = <0xc8005000 0x100>; + interrupts = <5 IRQ_TYPE_LEVEL_HIGH>; + }; + + npe@c8006000 { + compatible = "intel,ixp4xx-network-processing-engine"; + reg = <0xc8006000 0x1000>, <0xc8007000 0x1000>, <0xc8008000 0x1000>; + }; + }; +}; diff --git a/arch/arm/mach-ixp4xx/Kconfig b/arch/arm/mach-ixp4xx/Kconfig index fea008123eb1..83afb80d38a8 100644 --- a/arch/arm/mach-ixp4xx/Kconfig +++ b/arch/arm/mach-ixp4xx/Kconfig @@ -4,6 +4,20 @@ menu "Intel IXP4xx Implementation Options" comment "IXP4xx Platforms" +config MACH_IXP4XX_OF + bool + prompt "Devce Tree IXP4xx boards" + default y + select ARM_APPENDED_DTB # Old Redboot bootloaders deployed + select I2C + select I2C_IOP3XX + select PCI + select SERIAL_OF_PLATFORM + select TIMER_OF + select USE_OF + help + Say 'Y' here to support Device Tree-based IXP4xx platforms. + config MACH_NSLU2 bool prompt "Linksys NSLU2" @@ -222,19 +236,6 @@ config IXP4XX_INDIRECT_PCI need to use the indirect method instead. If you don't know what you need, leave this option unselected. -config IXP4XX_QMGR - tristate "IXP4xx Queue Manager support" - help - This driver supports IXP4xx built-in hardware queue manager - and is automatically selected by Ethernet and HSS drivers. - -config IXP4XX_NPE - tristate "IXP4xx Network Processor Engine support" - select FW_LOADER - help - This driver supports IXP4xx built-in network coprocessors - and is automatically selected by Ethernet and HSS drivers. - endmenu endif diff --git a/arch/arm/mach-ixp4xx/Makefile b/arch/arm/mach-ixp4xx/Makefile index f09994500a34..1fa4e6605782 100644 --- a/arch/arm/mach-ixp4xx/Makefile +++ b/arch/arm/mach-ixp4xx/Makefile @@ -6,6 +6,9 @@ obj-pci-y := obj-pci-n := +# Device tree platform +obj-pci-$(CONFIG_MACH_IXP4XX_OF) += ixp4xx-of.o + obj-pci-$(CONFIG_ARCH_IXDP4XX) += ixdp425-pci.o obj-pci-$(CONFIG_MACH_AVILA) += avila-pci.o obj-pci-$(CONFIG_MACH_IXDPG425) += ixdpg425-pci.o @@ -40,5 +43,3 @@ obj-$(CONFIG_MACH_GORAMO_MLR) += goramo_mlr.o obj-$(CONFIG_MACH_ARCOM_VULCAN) += vulcan-setup.o obj-$(CONFIG_PCI) += $(obj-pci-$(CONFIG_PCI)) common-pci.o -obj-$(CONFIG_IXP4XX_QMGR) += ixp4xx_qmgr.o -obj-$(CONFIG_IXP4XX_NPE) += ixp4xx_npe.o diff --git a/arch/arm/mach-ixp4xx/avila-pci.c b/arch/arm/mach-ixp4xx/avila-pci.c index 548c7d43ade6..9c834f0f4231 100644 --- a/arch/arm/mach-ixp4xx/avila-pci.c +++ b/arch/arm/mach-ixp4xx/avila-pci.c @@ -27,6 +27,8 @@ #include <mach/hardware.h> #include <asm/mach-types.h> +#include "irqs.h" + #define AVILA_MAX_DEV 4 #define LOFT_MAX_DEV 6 #define IRQ_LINES 4 diff --git a/arch/arm/mach-ixp4xx/avila-setup.c b/arch/arm/mach-ixp4xx/avila-setup.c index 44cbbce6bda6..1981b33109cb 100644 --- a/arch/arm/mach-ixp4xx/avila-setup.c +++ b/arch/arm/mach-ixp4xx/avila-setup.c @@ -28,6 +28,8 @@ #include <asm/mach/arch.h> #include <asm/mach/flash.h> +#include "irqs.h" + #define AVILA_SDA_PIN 7 #define AVILA_SCL_PIN 6 diff --git a/arch/arm/mach-ixp4xx/common.c b/arch/arm/mach-ixp4xx/common.c index 846e033c56fa..cc5f15679d29 100644 --- a/arch/arm/mach-ixp4xx/common.c +++ b/arch/arm/mach-ixp4xx/common.c @@ -22,41 +22,30 @@ #include <linux/serial_core.h> #include <linux/interrupt.h> #include <linux/bitops.h> -#include <linux/time.h> -#include <linux/clocksource.h> -#include <linux/clockchips.h> #include <linux/io.h> #include <linux/export.h> -#include <linux/gpio/driver.h> #include <linux/cpu.h> #include <linux/pci.h> #include <linux/sched_clock.h> +#include <linux/bitops.h> +#include <linux/irqchip/irq-ixp4xx.h> +#include <linux/platform_data/timer-ixp4xx.h> #include <mach/udc.h> #include <mach/hardware.h> #include <mach/io.h> #include <linux/uaccess.h> #include <asm/pgtable.h> #include <asm/page.h> +#include <asm/exception.h> #include <asm/irq.h> #include <asm/system_misc.h> #include <asm/mach/map.h> #include <asm/mach/irq.h> #include <asm/mach/time.h> -#define IXP4XX_TIMER_FREQ 66666000 - -/* - * The timer register doesn't allow to specify the two least significant bits of - * the timeout value and assumes them being zero. So make sure IXP4XX_LATCH is - * the best value with the two least significant bits unset. - */ -#define IXP4XX_LATCH DIV_ROUND_CLOSEST(IXP4XX_TIMER_FREQ, \ - (IXP4XX_OST_RELOAD_MASK + 1) * HZ) * \ - (IXP4XX_OST_RELOAD_MASK + 1) +#include "irqs.h" -static void __init ixp4xx_clocksource_init(void); -static void __init ixp4xx_clockevent_init(void); -static struct clock_event_device clockevent_ixp4xx; +#define IXP4XX_TIMER_FREQ 66666000 /************************************************************************* * IXP4xx chipset I/O mapping @@ -77,11 +66,6 @@ static struct map_desc ixp4xx_io_desc[] __initdata = { .pfn = __phys_to_pfn(IXP4XX_PCI_CFG_BASE_PHYS), .length = IXP4XX_PCI_CFG_REGION_SIZE, .type = MT_DEVICE - }, { /* Queue Manager */ - .virtual = (unsigned long)IXP4XX_QMGR_BASE_VIRT, - .pfn = __phys_to_pfn(IXP4XX_QMGR_BASE_PHYS), - .length = IXP4XX_QMGR_REGION_SIZE, - .type = MT_DEVICE }, }; @@ -90,258 +74,23 @@ void __init ixp4xx_map_io(void) iotable_init(ixp4xx_io_desc, ARRAY_SIZE(ixp4xx_io_desc)); } -/* - * GPIO-functions - */ -/* - * The following converted to the real HW bits the gpio_line_config - */ -/* GPIO pin types */ -#define IXP4XX_GPIO_OUT 0x1 -#define IXP4XX_GPIO_IN 0x2 - -/* GPIO signal types */ -#define IXP4XX_GPIO_LOW 0 -#define IXP4XX_GPIO_HIGH 1 - -/* GPIO Clocks */ -#define IXP4XX_GPIO_CLK_0 14 -#define IXP4XX_GPIO_CLK_1 15 - -static void gpio_line_config(u8 line, u32 direction) -{ - if (direction == IXP4XX_GPIO_IN) - *IXP4XX_GPIO_GPOER |= (1 << line); - else - *IXP4XX_GPIO_GPOER &= ~(1 << line); -} - -static void gpio_line_get(u8 line, int *value) -{ - *value = (*IXP4XX_GPIO_GPINR >> line) & 0x1; -} - -static void gpio_line_set(u8 line, int value) -{ - if (value == IXP4XX_GPIO_HIGH) - *IXP4XX_GPIO_GPOUTR |= (1 << line); - else if (value == IXP4XX_GPIO_LOW) - *IXP4XX_GPIO_GPOUTR &= ~(1 << line); -} - -/************************************************************************* - * IXP4xx chipset IRQ handling - * - * TODO: GPIO IRQs should be marked invalid until the user of the IRQ - * (be it PCI or something else) configures that GPIO line - * as an IRQ. - **************************************************************************/ -enum ixp4xx_irq_type { - IXP4XX_IRQ_LEVEL, IXP4XX_IRQ_EDGE -}; - -/* Each bit represents an IRQ: 1: edge-triggered, 0: level triggered */ -static unsigned long long ixp4xx_irq_edge = 0; - -/* - * IRQ -> GPIO mapping table - */ -static signed char irq2gpio[32] = { - -1, -1, -1, -1, -1, -1, 0, 1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 2, 3, 4, 5, 6, - 7, 8, 9, 10, 11, 12, -1, -1, -}; - -static int ixp4xx_gpio_to_irq(struct gpio_chip *chip, unsigned gpio) -{ - int irq; - - for (irq = 0; irq < 32; irq++) { - if (irq2gpio[irq] == gpio) - return irq; - } - return -EINVAL; -} - -static int ixp4xx_set_irq_type(struct irq_data *d, unsigned int type) -{ - int line = irq2gpio[d->irq]; - u32 int_style; - enum ixp4xx_irq_type irq_type; - volatile u32 *int_reg; - - /* - * Only for GPIO IRQs - */ - if (line < 0) - return -EINVAL; - - switch (type){ - case IRQ_TYPE_EDGE_BOTH: - int_style = IXP4XX_GPIO_STYLE_TRANSITIONAL; - irq_type = IXP4XX_IRQ_EDGE; - break; - case IRQ_TYPE_EDGE_RISING: - int_style = IXP4XX_GPIO_STYLE_RISING_EDGE; - irq_type = IXP4XX_IRQ_EDGE; - break; - case IRQ_TYPE_EDGE_FALLING: - int_style = IXP4XX_GPIO_STYLE_FALLING_EDGE; - irq_type = IXP4XX_IRQ_EDGE; - break; - case IRQ_TYPE_LEVEL_HIGH: - int_style = IXP4XX_GPIO_STYLE_ACTIVE_HIGH; - irq_type = IXP4XX_IRQ_LEVEL; - break; - case IRQ_TYPE_LEVEL_LOW: - int_style = IXP4XX_GPIO_STYLE_ACTIVE_LOW; - irq_type = IXP4XX_IRQ_LEVEL; - break; - default: - return -EINVAL; - } - - if (irq_type == IXP4XX_IRQ_EDGE) - ixp4xx_irq_edge |= (1 << d->irq); - else - ixp4xx_irq_edge &= ~(1 << d->irq); - - if (line >= 8) { /* pins 8-15 */ - line -= 8; - int_reg = IXP4XX_GPIO_GPIT2R; - } else { /* pins 0-7 */ - int_reg = IXP4XX_GPIO_GPIT1R; - } - - /* Clear the style for the appropriate pin */ - *int_reg &= ~(IXP4XX_GPIO_STYLE_CLEAR << - (line * IXP4XX_GPIO_STYLE_SIZE)); - - *IXP4XX_GPIO_GPISR = (1 << line); - - /* Set the new style */ - *int_reg |= (int_style << (line * IXP4XX_GPIO_STYLE_SIZE)); - - /* Configure the line as an input */ - gpio_line_config(irq2gpio[d->irq], IXP4XX_GPIO_IN); - - return 0; -} - -static void ixp4xx_irq_mask(struct irq_data *d) -{ - if ((cpu_is_ixp46x() || cpu_is_ixp43x()) && d->irq >= 32) - *IXP4XX_ICMR2 &= ~(1 << (d->irq - 32)); - else - *IXP4XX_ICMR &= ~(1 << d->irq); -} - -static void ixp4xx_irq_ack(struct irq_data *d) -{ - int line = (d->irq < 32) ? irq2gpio[d->irq] : -1; - - if (line >= 0) - *IXP4XX_GPIO_GPISR = (1 << line); -} - -/* - * Level triggered interrupts on GPIO lines can only be cleared when the - * interrupt condition disappears. - */ -static void ixp4xx_irq_unmask(struct irq_data *d) -{ - if (!(ixp4xx_irq_edge & (1 << d->irq))) - ixp4xx_irq_ack(d); - - if ((cpu_is_ixp46x() || cpu_is_ixp43x()) && d->irq >= 32) - *IXP4XX_ICMR2 |= (1 << (d->irq - 32)); - else - *IXP4XX_ICMR |= (1 << d->irq); -} - -static struct irq_chip ixp4xx_irq_chip = { - .name = "IXP4xx", - .irq_ack = ixp4xx_irq_ack, - .irq_mask = ixp4xx_irq_mask, - .irq_unmask = ixp4xx_irq_unmask, - .irq_set_type = ixp4xx_set_irq_type, -}; - void __init ixp4xx_init_irq(void) { - int i = 0; - /* * ixp4xx does not implement the XScale PWRMODE register * so it must not call cpu_do_idle(). */ cpu_idle_poll_ctrl(true); - /* Route all sources to IRQ instead of FIQ */ - *IXP4XX_ICLR = 0x0; - - /* Disable all interrupt */ - *IXP4XX_ICMR = 0x0; - - if (cpu_is_ixp46x() || cpu_is_ixp43x()) { - /* Route upper 32 sources to IRQ instead of FIQ */ - *IXP4XX_ICLR2 = 0x00; - - /* Disable upper 32 interrupts */ - *IXP4XX_ICMR2 = 0x00; - } - - /* Default to all level triggered */ - for(i = 0; i < NR_IRQS; i++) { - irq_set_chip_and_handler(i, &ixp4xx_irq_chip, - handle_level_irq); - irq_clear_status_flags(i, IRQ_NOREQUEST); - } + ixp4xx_irq_init(IXP4XX_INTC_BASE_PHYS, + (cpu_is_ixp46x() || cpu_is_ixp43x())); } - -/************************************************************************* - * IXP4xx timer tick - * We use OS timer1 on the CPU for the timer tick and the timestamp - * counter as a source of real clock ticks to account for missed jiffies. - *************************************************************************/ - -static irqreturn_t ixp4xx_timer_interrupt(int irq, void *dev_id) -{ - struct clock_event_device *evt = dev_id; - - /* Clear Pending Interrupt by writing '1' to it */ - *IXP4XX_OSST = IXP4XX_OSST_TIMER_1_PEND; - - evt->event_handler(evt); - - return IRQ_HANDLED; -} - -static struct irqaction ixp4xx_timer_irq = { - .name = "timer1", - .flags = IRQF_TIMER | IRQF_IRQPOLL, - .handler = ixp4xx_timer_interrupt, - .dev_id = &clockevent_ixp4xx, -}; - void __init ixp4xx_timer_init(void) { - /* Reset/disable counter */ - *IXP4XX_OSRT1 = 0; - - /* Clear Pending Interrupt by writing '1' to it */ - *IXP4XX_OSST = IXP4XX_OSST_TIMER_1_PEND; - - /* Reset time-stamp counter */ - *IXP4XX_OSTS = 0; - - /* Connect the interrupt handler and enable the interrupt */ - setup_irq(IRQ_IXP4XX_TIMER1, &ixp4xx_timer_irq); - - ixp4xx_clocksource_init(); - ixp4xx_clockevent_init(); + return ixp4xx_timer_setup(IXP4XX_TIMER_BASE_PHYS, + IRQ_IXP4XX_TIMER1, + IXP4XX_TIMER_FREQ); } static struct pxa2xx_udc_mach_info ixp4xx_udc_info; @@ -364,6 +113,24 @@ static struct resource ixp4xx_udc_resources[] = { }, }; +static struct resource ixp4xx_gpio_resource[] = { + { + .start = IXP4XX_GPIO_BASE_PHYS, + .end = IXP4XX_GPIO_BASE_PHYS + 0xfff, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device ixp4xx_gpio_device = { + .name = "ixp4xx-gpio", + .id = -1, + .dev = { + .coherent_dma_mask = DMA_BIT_MASK(32), + }, + .resource = ixp4xx_gpio_resource, + .num_resources = ARRAY_SIZE(ixp4xx_gpio_resource), +}; + /* * USB device controller. The IXP4xx uses the same controller as PXA25X, * so we just use the same device. @@ -378,7 +145,61 @@ static struct platform_device ixp4xx_udc_device = { }, }; +static struct resource ixp4xx_npe_resources[] = { + { + .start = IXP4XX_NPEA_BASE_PHYS, + .end = IXP4XX_NPEA_BASE_PHYS + 0xfff, + .flags = IORESOURCE_MEM, + }, + { + .start = IXP4XX_NPEB_BASE_PHYS, + .end = IXP4XX_NPEB_BASE_PHYS + 0xfff, + .flags = IORESOURCE_MEM, + }, + { + .start = IXP4XX_NPEC_BASE_PHYS, + .end = IXP4XX_NPEC_BASE_PHYS + 0xfff, + .flags = IORESOURCE_MEM, + }, + +}; + +static struct platform_device ixp4xx_npe_device = { + .name = "ixp4xx-npe", + .id = -1, + .num_resources = ARRAY_SIZE(ixp4xx_npe_resources), + .resource = ixp4xx_npe_resources, +}; + +static struct resource ixp4xx_qmgr_resources[] = { + { + .start = IXP4XX_QMGR_BASE_PHYS, + .end = IXP4XX_QMGR_BASE_PHYS + 0x3fff, + .flags = IORESOURCE_MEM, + }, + { + .start = IRQ_IXP4XX_QM1, + .end = IRQ_IXP4XX_QM1, + .flags = IORESOURCE_IRQ, + }, + { + .start = IRQ_IXP4XX_QM2, + .end = IRQ_IXP4XX_QM2, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device ixp4xx_qmgr_device = { + .name = "ixp4xx-qmgr", + .id = -1, + .num_resources = ARRAY_SIZE(ixp4xx_qmgr_resources), + .resource = ixp4xx_qmgr_resources, +}; + static struct platform_device *ixp4xx_devices[] __initdata = { + &ixp4xx_npe_device, + &ixp4xx_qmgr_device, + &ixp4xx_gpio_device, &ixp4xx_udc_device, }; @@ -413,56 +234,12 @@ static struct platform_device *ixp46x_devices[] __initdata = { unsigned long ixp4xx_exp_bus_size; EXPORT_SYMBOL(ixp4xx_exp_bus_size); -static int ixp4xx_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) -{ - gpio_line_config(gpio, IXP4XX_GPIO_IN); - - return 0; -} - -static int ixp4xx_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, - int level) -{ - gpio_line_set(gpio, level); - gpio_line_config(gpio, IXP4XX_GPIO_OUT); - - return 0; -} - -static int ixp4xx_gpio_get_value(struct gpio_chip *chip, unsigned gpio) -{ - int value; - - gpio_line_get(gpio, &value); - - return value; -} - -static void ixp4xx_gpio_set_value(struct gpio_chip *chip, unsigned gpio, - int value) -{ - gpio_line_set(gpio, value); -} - -static struct gpio_chip ixp4xx_gpio_chip = { - .label = "IXP4XX_GPIO_CHIP", - .direction_input = ixp4xx_gpio_direction_input, - .direction_output = ixp4xx_gpio_direction_output, - .get = ixp4xx_gpio_get_value, - .set = ixp4xx_gpio_set_value, - .to_irq = ixp4xx_gpio_to_irq, - .base = 0, - .ngpio = 16, -}; - void __init ixp4xx_sys_init(void) { ixp4xx_exp_bus_size = SZ_16M; platform_add_devices(ixp4xx_devices, ARRAY_SIZE(ixp4xx_devices)); - gpiochip_add_data(&ixp4xx_gpio_chip, NULL); - if (cpu_is_ixp46x()) { int region; @@ -481,103 +258,8 @@ void __init ixp4xx_sys_init(void) ixp4xx_exp_bus_size >> 20); } -/* - * sched_clock() - */ -static u64 notrace ixp4xx_read_sched_clock(void) -{ - return *IXP4XX_OSTS; -} - -/* - * clocksource - */ - -static u64 ixp4xx_clocksource_read(struct clocksource *c) -{ - return *IXP4XX_OSTS; -} - unsigned long ixp4xx_timer_freq = IXP4XX_TIMER_FREQ; EXPORT_SYMBOL(ixp4xx_timer_freq); -static void __init ixp4xx_clocksource_init(void) -{ - sched_clock_register(ixp4xx_read_sched_clock, 32, ixp4xx_timer_freq); - - clocksource_mmio_init(NULL, "OSTS", ixp4xx_timer_freq, 200, 32, - ixp4xx_clocksource_read); -} - -/* - * clockevents - */ -static int ixp4xx_set_next_event(unsigned long evt, - struct clock_event_device *unused) -{ - unsigned long opts = *IXP4XX_OSRT1 & IXP4XX_OST_RELOAD_MASK; - - *IXP4XX_OSRT1 = (evt & ~IXP4XX_OST_RELOAD_MASK) | opts; - - return 0; -} - -static int ixp4xx_shutdown(struct clock_event_device *evt) -{ - unsigned long opts = *IXP4XX_OSRT1 & IXP4XX_OST_RELOAD_MASK; - unsigned long osrt = *IXP4XX_OSRT1 & ~IXP4XX_OST_RELOAD_MASK; - - opts &= ~IXP4XX_OST_ENABLE; - *IXP4XX_OSRT1 = osrt | opts; - return 0; -} - -static int ixp4xx_set_oneshot(struct clock_event_device *evt) -{ - unsigned long opts = IXP4XX_OST_ENABLE | IXP4XX_OST_ONE_SHOT; - unsigned long osrt = 0; - - /* period set by 'set next_event' */ - *IXP4XX_OSRT1 = osrt | opts; - return 0; -} - -static int ixp4xx_set_periodic(struct clock_event_device *evt) -{ - unsigned long opts = IXP4XX_OST_ENABLE; - unsigned long osrt = IXP4XX_LATCH & ~IXP4XX_OST_RELOAD_MASK; - - *IXP4XX_OSRT1 = osrt | opts; - return 0; -} - -static int ixp4xx_resume(struct clock_event_device *evt) -{ - unsigned long opts = *IXP4XX_OSRT1 & IXP4XX_OST_RELOAD_MASK; - unsigned long osrt = *IXP4XX_OSRT1 & ~IXP4XX_OST_RELOAD_MASK; - - opts |= IXP4XX_OST_ENABLE; - *IXP4XX_OSRT1 = osrt | opts; - return 0; -} - -static struct clock_event_device clockevent_ixp4xx = { - .name = "ixp4xx timer1", - .features = CLOCK_EVT_FEAT_PERIODIC | - CLOCK_EVT_FEAT_ONESHOT, - .rating = 200, - .set_state_shutdown = ixp4xx_shutdown, - .set_state_periodic = ixp4xx_set_periodic, - .set_state_oneshot = ixp4xx_set_oneshot, - .tick_resume = ixp4xx_resume, - .set_next_event = ixp4xx_set_next_event, -}; - -static void __init ixp4xx_clockevent_init(void) -{ - clockevent_ixp4xx.cpumask = cpumask_of(0); - clockevents_config_and_register(&clockevent_ixp4xx, IXP4XX_TIMER_FREQ, - 0xf, 0xfffffffe); -} void ixp4xx_restart(enum reboot_mode mode, const char *cmd) { diff --git a/arch/arm/mach-ixp4xx/coyote-pci.c b/arch/arm/mach-ixp4xx/coyote-pci.c index 5d14ce2aee6d..a16c35d2bb96 100644 --- a/arch/arm/mach-ixp4xx/coyote-pci.c +++ b/arch/arm/mach-ixp4xx/coyote-pci.c @@ -23,6 +23,8 @@ #include <asm/irq.h> #include <asm/mach/pci.h> +#include "irqs.h" + #define SLOT0_DEVID 14 #define SLOT1_DEVID 15 diff --git a/arch/arm/mach-ixp4xx/coyote-setup.c b/arch/arm/mach-ixp4xx/coyote-setup.c index 7e40fe70933b..7ca43ca2816d 100644 --- a/arch/arm/mach-ixp4xx/coyote-setup.c +++ b/arch/arm/mach-ixp4xx/coyote-setup.c @@ -25,6 +25,8 @@ #include <asm/mach/arch.h> #include <asm/mach/flash.h> +#include "irqs.h" + #define COYOTE_IDE_BASE_PHYS IXP4XX_EXP_BUS_BASE(3) #define COYOTE_IDE_BASE_VIRT 0xFFFE1000 #define COYOTE_IDE_REGION_SIZE 0x1000 diff --git a/arch/arm/mach-ixp4xx/dsmg600-pci.c b/arch/arm/mach-ixp4xx/dsmg600-pci.c index 8dca76937723..6899023bd1b7 100644 --- a/arch/arm/mach-ixp4xx/dsmg600-pci.c +++ b/arch/arm/mach-ixp4xx/dsmg600-pci.c @@ -22,6 +22,8 @@ #include <asm/mach/pci.h> #include <asm/mach-types.h> +#include "irqs.h" + #define MAX_DEV 4 #define IRQ_LINES 3 diff --git a/arch/arm/mach-ixp4xx/dsmg600-setup.c b/arch/arm/mach-ixp4xx/dsmg600-setup.c index 397190f3a8da..4d4c62fced71 100644 --- a/arch/arm/mach-ixp4xx/dsmg600-setup.c +++ b/arch/arm/mach-ixp4xx/dsmg600-setup.c @@ -35,6 +35,8 @@ #include <asm/mach/flash.h> #include <asm/mach/time.h> +#include "irqs.h" + #define DSMG600_SDA_PIN 5 #define DSMG600_SCL_PIN 4 @@ -268,9 +270,6 @@ static void __init dsmg600_init(void) { ixp4xx_sys_init(); - /* Make sure that GPIO14 and GPIO15 are not used as clocks */ - *IXP4XX_GPIO_GPCLKR = 0; - dsmg600_flash_resource.start = IXP4XX_EXP_BUS_BASE(0); dsmg600_flash_resource.end = IXP4XX_EXP_BUS_BASE(0) + ixp4xx_exp_bus_size - 1; diff --git a/arch/arm/mach-ixp4xx/fsg-pci.c b/arch/arm/mach-ixp4xx/fsg-pci.c index fd4a8625b4ae..6c08bb9d9807 100644 --- a/arch/arm/mach-ixp4xx/fsg-pci.c +++ b/arch/arm/mach-ixp4xx/fsg-pci.c @@ -22,6 +22,8 @@ #include <asm/mach/pci.h> #include <asm/mach-types.h> +#include "irqs.h" + #define MAX_DEV 3 #define IRQ_LINES 3 diff --git a/arch/arm/mach-ixp4xx/fsg-setup.c b/arch/arm/mach-ixp4xx/fsg-setup.c index f0a152e365b1..648932d8d7a8 100644 --- a/arch/arm/mach-ixp4xx/fsg-setup.c +++ b/arch/arm/mach-ixp4xx/fsg-setup.c @@ -29,6 +29,8 @@ #include <asm/mach/arch.h> #include <asm/mach/flash.h> +#include "irqs.h" + #define FSG_SDA_PIN 12 #define FSG_SCL_PIN 13 diff --git a/arch/arm/mach-ixp4xx/gateway7001-pci.c b/arch/arm/mach-ixp4xx/gateway7001-pci.c index d9d6cc089707..903c75330b76 100644 --- a/arch/arm/mach-ixp4xx/gateway7001-pci.c +++ b/arch/arm/mach-ixp4xx/gateway7001-pci.c @@ -27,6 +27,8 @@ #include <asm/mach/pci.h> +#include "irqs.h" + void __init gateway7001_pci_preinit(void) { irq_set_irq_type(IRQ_IXP4XX_GPIO10, IRQ_TYPE_LEVEL_LOW); diff --git a/arch/arm/mach-ixp4xx/gateway7001-setup.c b/arch/arm/mach-ixp4xx/gateway7001-setup.c index 1be6faf6da9a..678e7dfff0e5 100644 --- a/arch/arm/mach-ixp4xx/gateway7001-setup.c +++ b/arch/arm/mach-ixp4xx/gateway7001-setup.c @@ -28,6 +28,8 @@ #include <asm/mach/arch.h> #include <asm/mach/flash.h> +#include "irqs.h" + static struct flash_platform_data gateway7001_flash_data = { .map_name = "cfi_probe", .width = 2, diff --git a/arch/arm/mach-ixp4xx/gtwx5715-pci.c b/arch/arm/mach-ixp4xx/gtwx5715-pci.c index 551d114c9e14..1223d160448f 100644 --- a/arch/arm/mach-ixp4xx/gtwx5715-pci.c +++ b/arch/arm/mach-ixp4xx/gtwx5715-pci.c @@ -30,6 +30,8 @@ #include <mach/hardware.h> #include <asm/mach/pci.h> +#include "irqs.h" + #define SLOT0_DEVID 0 #define SLOT1_DEVID 1 #define INTA 10 /* slot 1 has INTA and INTB crossed */ diff --git a/arch/arm/mach-ixp4xx/gtwx5715-setup.c b/arch/arm/mach-ixp4xx/gtwx5715-setup.c index 16a12994fb53..5dbdde8e2338 100644 --- a/arch/arm/mach-ixp4xx/gtwx5715-setup.c +++ b/arch/arm/mach-ixp4xx/gtwx5715-setup.c @@ -36,6 +36,8 @@ #include <asm/mach/arch.h> #include <asm/mach/flash.h> +#include "irqs.h" + /* GPIO 5,6,7 and 12 are hard wired to the Kendin KS8995M Switch and operate as an SPI type interface. The details of the interface are available on Kendin/Micrel's web site. */ diff --git a/arch/arm/mach-ixp4xx/include/mach/entry-macro.S b/arch/arm/mach-ixp4xx/include/mach/entry-macro.S deleted file mode 100644 index 79adf83e2c3d..000000000000 --- a/arch/arm/mach-ixp4xx/include/mach/entry-macro.S +++ /dev/null @@ -1,41 +0,0 @@ -/* - * arch/arm/mach-ixp4xx/include/mach/entry-macro.S - * - * Low-level IRQ helper macros for IXP4xx-based platforms - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ -#include <mach/hardware.h> - - .macro get_irqnr_preamble, base, tmp - .endm - - .macro get_irqnr_and_base, irqnr, irqstat, base, tmp - ldr \irqstat, =(IXP4XX_INTC_BASE_VIRT+IXP4XX_ICIP_OFFSET) - ldr \irqstat, [\irqstat] @ get interrupts - cmp \irqstat, #0 - beq 1001f @ upper IRQ? - clz \irqnr, \irqstat - mov \base, #31 - sub \irqnr, \base, \irqnr - b 1002f @ lower IRQ being - @ handled - -1001: - /* - * IXP465/IXP435 has an upper IRQ status register - */ -#if defined(CONFIG_CPU_IXP46X) || defined(CONFIG_CPU_IXP43X) - ldr \irqstat, =(IXP4XX_INTC_BASE_VIRT+IXP4XX_ICIP2_OFFSET) - ldr \irqstat, [\irqstat] @ get upper interrupts - mov \irqnr, #63 - clz \irqstat, \irqstat - cmp \irqstat, #32 - subne \irqnr, \irqnr, \irqstat -#endif -1002: - .endm - - diff --git a/arch/arm/mach-ixp4xx/include/mach/irqs.h b/arch/arm/mach-ixp4xx/include/mach/irqs.h deleted file mode 100644 index 7e6d4cce7c27..000000000000 --- a/arch/arm/mach-ixp4xx/include/mach/irqs.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * arch/arm/mach-ixp4xx/include/mach/irqs.h - * - * IRQ definitions for IXP4XX based systems - * - * Copyright (C) 2002 Intel Corporation. - * Copyright (C) 2003 MontaVista Software, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -#ifndef _ARCH_IXP4XX_IRQS_H_ -#define _ARCH_IXP4XX_IRQS_H_ - -#define IRQ_IXP4XX_NPEA 0 -#define IRQ_IXP4XX_NPEB 1 -#define IRQ_IXP4XX_NPEC 2 -#define IRQ_IXP4XX_QM1 3 -#define IRQ_IXP4XX_QM2 4 -#define IRQ_IXP4XX_TIMER1 5 -#define IRQ_IXP4XX_GPIO0 6 -#define IRQ_IXP4XX_GPIO1 7 -#define IRQ_IXP4XX_PCI_INT 8 -#define IRQ_IXP4XX_PCI_DMA1 9 -#define IRQ_IXP4XX_PCI_DMA2 10 -#define IRQ_IXP4XX_TIMER2 11 -#define IRQ_IXP4XX_USB 12 -#define IRQ_IXP4XX_UART2 13 -#define IRQ_IXP4XX_TIMESTAMP 14 -#define IRQ_IXP4XX_UART1 15 -#define IRQ_IXP4XX_WDOG 16 -#define IRQ_IXP4XX_AHB_PMU 17 -#define IRQ_IXP4XX_XSCALE_PMU 18 -#define IRQ_IXP4XX_GPIO2 19 -#define IRQ_IXP4XX_GPIO3 20 -#define IRQ_IXP4XX_GPIO4 21 -#define IRQ_IXP4XX_GPIO5 22 -#define IRQ_IXP4XX_GPIO6 23 -#define IRQ_IXP4XX_GPIO7 24 -#define IRQ_IXP4XX_GPIO8 25 -#define IRQ_IXP4XX_GPIO9 26 -#define IRQ_IXP4XX_GPIO10 27 -#define IRQ_IXP4XX_GPIO11 28 -#define IRQ_IXP4XX_GPIO12 29 -#define IRQ_IXP4XX_SW_INT1 30 -#define IRQ_IXP4XX_SW_INT2 31 -#define IRQ_IXP4XX_USB_HOST 32 -#define IRQ_IXP4XX_I2C 33 -#define IRQ_IXP4XX_SSP 34 -#define IRQ_IXP4XX_TSYNC 35 -#define IRQ_IXP4XX_EAU_DONE 36 -#define IRQ_IXP4XX_SHA_DONE 37 -#define IRQ_IXP4XX_SWCP_PE 58 -#define IRQ_IXP4XX_QM_PE 60 -#define IRQ_IXP4XX_MCU_ECC 61 -#define IRQ_IXP4XX_EXP_PE 62 - -#define _IXP4XX_GPIO_IRQ(n) (IRQ_IXP4XX_GPIO ## n) -#define IXP4XX_GPIO_IRQ(n) _IXP4XX_GPIO_IRQ(n) - -/* - * Only first 32 sources are valid if running on IXP42x systems - */ -#if defined(CONFIG_CPU_IXP46X) || defined(CONFIG_CPU_IXP43X) -#define NR_IRQS 64 -#else -#define NR_IRQS 32 -#endif - -#define XSCALE_PMU_IRQ (IRQ_IXP4XX_XSCALE_PMU) - -#endif diff --git a/arch/arm/mach-ixp4xx/include/mach/ixp4xx-regs.h b/arch/arm/mach-ixp4xx/include/mach/ixp4xx-regs.h index b7ddd27419c2..588b76651085 100644 --- a/arch/arm/mach-ixp4xx/include/mach/ixp4xx-regs.h +++ b/arch/arm/mach-ixp4xx/include/mach/ixp4xx-regs.h @@ -43,8 +43,6 @@ * Queue Manager */ #define IXP4XX_QMGR_BASE_PHYS 0x60000000 -#define IXP4XX_QMGR_BASE_VIRT IOMEM(0xFEF15000) -#define IXP4XX_QMGR_REGION_SIZE 0x00004000 /* * Peripheral space, including debug UART. Must be section-aligned so that @@ -132,9 +130,6 @@ #define IXP4XX_INTC_BASE_VIRT (IXP4XX_PERIPHERAL_BASE_VIRT + 0x3000) #define IXP4XX_GPIO_BASE_VIRT (IXP4XX_PERIPHERAL_BASE_VIRT + 0x4000) #define IXP4XX_TIMER_BASE_VIRT (IXP4XX_PERIPHERAL_BASE_VIRT + 0x5000) -#define IXP4XX_NPEA_BASE_VIRT (IXP4XX_PERIPHERAL_BASE_VIRT + 0x6000) -#define IXP4XX_NPEB_BASE_VIRT (IXP4XX_PERIPHERAL_BASE_VIRT + 0x7000) -#define IXP4XX_NPEC_BASE_VIRT (IXP4XX_PERIPHERAL_BASE_VIRT + 0x8000) #define IXP4XX_EthB_BASE_VIRT (IXP4XX_PERIPHERAL_BASE_VIRT + 0x9000) #define IXP4XX_EthC_BASE_VIRT (IXP4XX_PERIPHERAL_BASE_VIRT + 0xA000) #define IXP4XX_USB_BASE_VIRT (IXP4XX_PERIPHERAL_BASE_VIRT + 0xB000) @@ -148,95 +143,6 @@ #define IXP4XX_SSP_BASE_VIRT (IXP4XX_PERIPHERAL_BASE_VIRT + 0x12000) /* - * Constants to make it easy to access Interrupt Controller registers - */ -#define IXP4XX_ICPR_OFFSET 0x00 /* Interrupt Status */ -#define IXP4XX_ICMR_OFFSET 0x04 /* Interrupt Enable */ -#define IXP4XX_ICLR_OFFSET 0x08 /* Interrupt IRQ/FIQ Select */ -#define IXP4XX_ICIP_OFFSET 0x0C /* IRQ Status */ -#define IXP4XX_ICFP_OFFSET 0x10 /* FIQ Status */ -#define IXP4XX_ICHR_OFFSET 0x14 /* Interrupt Priority */ -#define IXP4XX_ICIH_OFFSET 0x18 /* IRQ Highest Pri Int */ -#define IXP4XX_ICFH_OFFSET 0x1C /* FIQ Highest Pri Int */ - -/* - * IXP465-only - */ -#define IXP4XX_ICPR2_OFFSET 0x20 /* Interrupt Status 2 */ -#define IXP4XX_ICMR2_OFFSET 0x24 /* Interrupt Enable 2 */ -#define IXP4XX_ICLR2_OFFSET 0x28 /* Interrupt IRQ/FIQ Select 2 */ -#define IXP4XX_ICIP2_OFFSET 0x2C /* IRQ Status */ -#define IXP4XX_ICFP2_OFFSET 0x30 /* FIQ Status */ -#define IXP4XX_ICEEN_OFFSET 0x34 /* Error High Pri Enable */ - - -/* - * Interrupt Controller Register Definitions. - */ - -#define IXP4XX_INTC_REG(x) ((volatile u32 *)(IXP4XX_INTC_BASE_VIRT+(x))) - -#define IXP4XX_ICPR IXP4XX_INTC_REG(IXP4XX_ICPR_OFFSET) -#define IXP4XX_ICMR IXP4XX_INTC_REG(IXP4XX_ICMR_OFFSET) -#define IXP4XX_ICLR IXP4XX_INTC_REG(IXP4XX_ICLR_OFFSET) -#define IXP4XX_ICIP IXP4XX_INTC_REG(IXP4XX_ICIP_OFFSET) -#define IXP4XX_ICFP IXP4XX_INTC_REG(IXP4XX_ICFP_OFFSET) -#define IXP4XX_ICHR IXP4XX_INTC_REG(IXP4XX_ICHR_OFFSET) -#define IXP4XX_ICIH IXP4XX_INTC_REG(IXP4XX_ICIH_OFFSET) -#define IXP4XX_ICFH IXP4XX_INTC_REG(IXP4XX_ICFH_OFFSET) -#define IXP4XX_ICPR2 IXP4XX_INTC_REG(IXP4XX_ICPR2_OFFSET) -#define IXP4XX_ICMR2 IXP4XX_INTC_REG(IXP4XX_ICMR2_OFFSET) -#define IXP4XX_ICLR2 IXP4XX_INTC_REG(IXP4XX_ICLR2_OFFSET) -#define IXP4XX_ICIP2 IXP4XX_INTC_REG(IXP4XX_ICIP2_OFFSET) -#define IXP4XX_ICFP2 IXP4XX_INTC_REG(IXP4XX_ICFP2_OFFSET) -#define IXP4XX_ICEEN IXP4XX_INTC_REG(IXP4XX_ICEEN_OFFSET) - -/* - * Constants to make it easy to access GPIO registers - */ -#define IXP4XX_GPIO_GPOUTR_OFFSET 0x00 -#define IXP4XX_GPIO_GPOER_OFFSET 0x04 -#define IXP4XX_GPIO_GPINR_OFFSET 0x08 -#define IXP4XX_GPIO_GPISR_OFFSET 0x0C -#define IXP4XX_GPIO_GPIT1R_OFFSET 0x10 -#define IXP4XX_GPIO_GPIT2R_OFFSET 0x14 -#define IXP4XX_GPIO_GPCLKR_OFFSET 0x18 -#define IXP4XX_GPIO_GPDBSELR_OFFSET 0x1C - -/* - * GPIO Register Definitions. - * [Only perform 32bit reads/writes] - */ -#define IXP4XX_GPIO_REG(x) ((volatile u32 *)(IXP4XX_GPIO_BASE_VIRT+(x))) - -#define IXP4XX_GPIO_GPOUTR IXP4XX_GPIO_REG(IXP4XX_GPIO_GPOUTR_OFFSET) -#define IXP4XX_GPIO_GPOER IXP4XX_GPIO_REG(IXP4XX_GPIO_GPOER_OFFSET) -#define IXP4XX_GPIO_GPINR IXP4XX_GPIO_REG(IXP4XX_GPIO_GPINR_OFFSET) -#define IXP4XX_GPIO_GPISR IXP4XX_GPIO_REG(IXP4XX_GPIO_GPISR_OFFSET) -#define IXP4XX_GPIO_GPIT1R IXP4XX_GPIO_REG(IXP4XX_GPIO_GPIT1R_OFFSET) -#define IXP4XX_GPIO_GPIT2R IXP4XX_GPIO_REG(IXP4XX_GPIO_GPIT2R_OFFSET) -#define IXP4XX_GPIO_GPCLKR IXP4XX_GPIO_REG(IXP4XX_GPIO_GPCLKR_OFFSET) -#define IXP4XX_GPIO_GPDBSELR IXP4XX_GPIO_REG(IXP4XX_GPIO_GPDBSELR_OFFSET) - -/* - * GPIO register bit definitions - */ - -/* Interrupt styles - */ -#define IXP4XX_GPIO_STYLE_ACTIVE_HIGH 0x0 -#define IXP4XX_GPIO_STYLE_ACTIVE_LOW 0x1 -#define IXP4XX_GPIO_STYLE_RISING_EDGE 0x2 -#define IXP4XX_GPIO_STYLE_FALLING_EDGE 0x3 -#define IXP4XX_GPIO_STYLE_TRANSITIONAL 0x4 - -/* - * Mask used to clear interrupt styles - */ -#define IXP4XX_GPIO_STYLE_CLEAR 0x7 -#define IXP4XX_GPIO_STYLE_SIZE 3 - -/* * Constants to make it easy to access Timer Control/Status registers */ #define IXP4XX_OSTS_OFFSET 0x00 /* Continious TimeStamp */ diff --git a/arch/arm/mach-ixp4xx/include/mach/qmgr.h b/arch/arm/mach-ixp4xx/include/mach/qmgr.h deleted file mode 100644 index 4de8da536dbb..000000000000 --- a/arch/arm/mach-ixp4xx/include/mach/qmgr.h +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright (C) 2007 Krzysztof Halasa <khc@pm.waw.pl> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License - * as published by the Free Software Foundation. - */ - -#ifndef IXP4XX_QMGR_H -#define IXP4XX_QMGR_H - -#include <linux/io.h> -#include <linux/kernel.h> - -#define DEBUG_QMGR 0 - -#define HALF_QUEUES 32 -#define QUEUES 64 -#define MAX_QUEUE_LENGTH 4 /* in dwords */ - -#define QUEUE_STAT1_EMPTY 1 /* queue status bits */ -#define QUEUE_STAT1_NEARLY_EMPTY 2 -#define QUEUE_STAT1_NEARLY_FULL 4 -#define QUEUE_STAT1_FULL 8 -#define QUEUE_STAT2_UNDERFLOW 1 -#define QUEUE_STAT2_OVERFLOW 2 - -#define QUEUE_WATERMARK_0_ENTRIES 0 -#define QUEUE_WATERMARK_1_ENTRY 1 -#define QUEUE_WATERMARK_2_ENTRIES 2 -#define QUEUE_WATERMARK_4_ENTRIES 3 -#define QUEUE_WATERMARK_8_ENTRIES 4 -#define QUEUE_WATERMARK_16_ENTRIES 5 -#define QUEUE_WATERMARK_32_ENTRIES 6 -#define QUEUE_WATERMARK_64_ENTRIES 7 - -/* queue interrupt request conditions */ -#define QUEUE_IRQ_SRC_EMPTY 0 -#define QUEUE_IRQ_SRC_NEARLY_EMPTY 1 -#define QUEUE_IRQ_SRC_NEARLY_FULL 2 -#define QUEUE_IRQ_SRC_FULL 3 -#define QUEUE_IRQ_SRC_NOT_EMPTY 4 -#define QUEUE_IRQ_SRC_NOT_NEARLY_EMPTY 5 -#define QUEUE_IRQ_SRC_NOT_NEARLY_FULL 6 -#define QUEUE_IRQ_SRC_NOT_FULL 7 - -struct qmgr_regs { - u32 acc[QUEUES][MAX_QUEUE_LENGTH]; /* 0x000 - 0x3FF */ - u32 stat1[4]; /* 0x400 - 0x40F */ - u32 stat2[2]; /* 0x410 - 0x417 */ - u32 statne_h; /* 0x418 - queue nearly empty */ - u32 statf_h; /* 0x41C - queue full */ - u32 irqsrc[4]; /* 0x420 - 0x42F IRC source */ - u32 irqen[2]; /* 0x430 - 0x437 IRQ enabled */ - u32 irqstat[2]; /* 0x438 - 0x43F - IRQ access only */ - u32 reserved[1776]; - u32 sram[2048]; /* 0x2000 - 0x3FFF - config and buffer */ -}; - -void qmgr_set_irq(unsigned int queue, int src, - void (*handler)(void *pdev), void *pdev); -void qmgr_enable_irq(unsigned int queue); -void qmgr_disable_irq(unsigned int queue); - -/* request_ and release_queue() must be called from non-IRQ context */ - -#if DEBUG_QMGR -extern char qmgr_queue_descs[QUEUES][32]; - -int qmgr_request_queue(unsigned int queue, unsigned int len /* dwords */, - unsigned int nearly_empty_watermark, - unsigned int nearly_full_watermark, - const char *desc_format, const char* name); -#else -int __qmgr_request_queue(unsigned int queue, unsigned int len /* dwords */, - unsigned int nearly_empty_watermark, - unsigned int nearly_full_watermark); -#define qmgr_request_queue(queue, len, nearly_empty_watermark, \ - nearly_full_watermark, desc_format, name) \ - __qmgr_request_queue(queue, len, nearly_empty_watermark, \ - nearly_full_watermark) -#endif - -void qmgr_release_queue(unsigned int queue); - - -static inline void qmgr_put_entry(unsigned int queue, u32 val) -{ - struct qmgr_regs __iomem *qmgr_regs = IXP4XX_QMGR_BASE_VIRT; -#if DEBUG_QMGR - BUG_ON(!qmgr_queue_descs[queue]); /* not yet requested */ - - printk(KERN_DEBUG "Queue %s(%i) put %X\n", - qmgr_queue_descs[queue], queue, val); -#endif - __raw_writel(val, &qmgr_regs->acc[queue][0]); -} - -static inline u32 qmgr_get_entry(unsigned int queue) -{ - u32 val; - const struct qmgr_regs __iomem *qmgr_regs = IXP4XX_QMGR_BASE_VIRT; - val = __raw_readl(&qmgr_regs->acc[queue][0]); -#if DEBUG_QMGR - BUG_ON(!qmgr_queue_descs[queue]); /* not yet requested */ - - printk(KERN_DEBUG "Queue %s(%i) get %X\n", - qmgr_queue_descs[queue], queue, val); -#endif - return val; -} - -static inline int __qmgr_get_stat1(unsigned int queue) -{ - const struct qmgr_regs __iomem *qmgr_regs = IXP4XX_QMGR_BASE_VIRT; - return (__raw_readl(&qmgr_regs->stat1[queue >> 3]) - >> ((queue & 7) << 2)) & 0xF; -} - -static inline int __qmgr_get_stat2(unsigned int queue) -{ - const struct qmgr_regs __iomem *qmgr_regs = IXP4XX_QMGR_BASE_VIRT; - BUG_ON(queue >= HALF_QUEUES); - return (__raw_readl(&qmgr_regs->stat2[queue >> 4]) - >> ((queue & 0xF) << 1)) & 0x3; -} - -/** - * qmgr_stat_empty() - checks if a hardware queue is empty - * @queue: queue number - * - * Returns non-zero value if the queue is empty. - */ -static inline int qmgr_stat_empty(unsigned int queue) -{ - BUG_ON(queue >= HALF_QUEUES); - return __qmgr_get_stat1(queue) & QUEUE_STAT1_EMPTY; -} - -/** - * qmgr_stat_below_low_watermark() - checks if a queue is below low watermark - * @queue: queue number - * - * Returns non-zero value if the queue is below low watermark. - */ -static inline int qmgr_stat_below_low_watermark(unsigned int queue) -{ - const struct qmgr_regs __iomem *qmgr_regs = IXP4XX_QMGR_BASE_VIRT; - if (queue >= HALF_QUEUES) - return (__raw_readl(&qmgr_regs->statne_h) >> - (queue - HALF_QUEUES)) & 0x01; - return __qmgr_get_stat1(queue) & QUEUE_STAT1_NEARLY_EMPTY; -} - -/** - * qmgr_stat_above_high_watermark() - checks if a queue is above high watermark - * @queue: queue number - * - * Returns non-zero value if the queue is above high watermark - */ -static inline int qmgr_stat_above_high_watermark(unsigned int queue) -{ - BUG_ON(queue >= HALF_QUEUES); - return __qmgr_get_stat1(queue) & QUEUE_STAT1_NEARLY_FULL; -} - -/** - * qmgr_stat_full() - checks if a hardware queue is full - * @queue: queue number - * - * Returns non-zero value if the queue is full. - */ -static inline int qmgr_stat_full(unsigned int queue) -{ - const struct qmgr_regs __iomem *qmgr_regs = IXP4XX_QMGR_BASE_VIRT; - if (queue >= HALF_QUEUES) - return (__raw_readl(&qmgr_regs->statf_h) >> - (queue - HALF_QUEUES)) & 0x01; - return __qmgr_get_stat1(queue) & QUEUE_STAT1_FULL; -} - -/** - * qmgr_stat_underflow() - checks if a hardware queue experienced underflow - * @queue: queue number - * - * Returns non-zero value if the queue experienced underflow. - */ -static inline int qmgr_stat_underflow(unsigned int queue) -{ - return __qmgr_get_stat2(queue) & QUEUE_STAT2_UNDERFLOW; -} - -/** - * qmgr_stat_overflow() - checks if a hardware queue experienced overflow - * @queue: queue number - * - * Returns non-zero value if the queue experienced overflow. - */ -static inline int qmgr_stat_overflow(unsigned int queue) -{ - return __qmgr_get_stat2(queue) & QUEUE_STAT2_OVERFLOW; -} - -#endif diff --git a/arch/arm/mach-ixp4xx/irqs.h b/arch/arm/mach-ixp4xx/irqs.h new file mode 100644 index 000000000000..6b7f220cf9e0 --- /dev/null +++ b/arch/arm/mach-ixp4xx/irqs.h @@ -0,0 +1,68 @@ +/* + * arch/arm/mach-ixp4xx/include/mach/irqs.h + * + * IRQ definitions for IXP4XX based systems + * + * Copyright (C) 2002 Intel Corporation. + * Copyright (C) 2003 MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef _ARCH_IXP4XX_IRQS_H_ +#define _ARCH_IXP4XX_IRQS_H_ + +#define IRQ_IXP4XX_BASE 16 + +#define IRQ_IXP4XX_NPEA (IRQ_IXP4XX_BASE + 0) +#define IRQ_IXP4XX_NPEB (IRQ_IXP4XX_BASE + 1) +#define IRQ_IXP4XX_NPEC (IRQ_IXP4XX_BASE + 2) +#define IRQ_IXP4XX_QM1 (IRQ_IXP4XX_BASE + 3) +#define IRQ_IXP4XX_QM2 (IRQ_IXP4XX_BASE + 4) +#define IRQ_IXP4XX_TIMER1 (IRQ_IXP4XX_BASE + 5) +#define IRQ_IXP4XX_GPIO0 (IRQ_IXP4XX_BASE + 6) +#define IRQ_IXP4XX_GPIO1 (IRQ_IXP4XX_BASE + 7) +#define IRQ_IXP4XX_PCI_INT (IRQ_IXP4XX_BASE + 8) +#define IRQ_IXP4XX_PCI_DMA1 (IRQ_IXP4XX_BASE + 9) +#define IRQ_IXP4XX_PCI_DMA2 (IRQ_IXP4XX_BASE + 10) +#define IRQ_IXP4XX_TIMER2 (IRQ_IXP4XX_BASE + 11) +#define IRQ_IXP4XX_USB (IRQ_IXP4XX_BASE + 12) +#define IRQ_IXP4XX_UART2 (IRQ_IXP4XX_BASE + 13) +#define IRQ_IXP4XX_TIMESTAMP (IRQ_IXP4XX_BASE + 14) +#define IRQ_IXP4XX_UART1 (IRQ_IXP4XX_BASE + 15) +#define IRQ_IXP4XX_WDOG (IRQ_IXP4XX_BASE + 16) +#define IRQ_IXP4XX_AHB_PMU (IRQ_IXP4XX_BASE + 17) +#define IRQ_IXP4XX_XSCALE_PMU (IRQ_IXP4XX_BASE + 18) +#define IRQ_IXP4XX_GPIO2 (IRQ_IXP4XX_BASE + 19) +#define IRQ_IXP4XX_GPIO3 (IRQ_IXP4XX_BASE + 20) +#define IRQ_IXP4XX_GPIO4 (IRQ_IXP4XX_BASE + 21) +#define IRQ_IXP4XX_GPIO5 (IRQ_IXP4XX_BASE + 22) +#define IRQ_IXP4XX_GPIO6 (IRQ_IXP4XX_BASE + 23) +#define IRQ_IXP4XX_GPIO7 (IRQ_IXP4XX_BASE + 24) +#define IRQ_IXP4XX_GPIO8 (IRQ_IXP4XX_BASE + 25) +#define IRQ_IXP4XX_GPIO9 (IRQ_IXP4XX_BASE + 26) +#define IRQ_IXP4XX_GPIO10 (IRQ_IXP4XX_BASE + 27) +#define IRQ_IXP4XX_GPIO11 (IRQ_IXP4XX_BASE + 28) +#define IRQ_IXP4XX_GPIO12 (IRQ_IXP4XX_BASE + 29) +#define IRQ_IXP4XX_SW_INT1 (IRQ_IXP4XX_BASE + 30) +#define IRQ_IXP4XX_SW_INT2 (IRQ_IXP4XX_BASE + 31) +#define IRQ_IXP4XX_USB_HOST (IRQ_IXP4XX_BASE + 32) +#define IRQ_IXP4XX_I2C (IRQ_IXP4XX_BASE + 33) +#define IRQ_IXP4XX_SSP (IRQ_IXP4XX_BASE + 34) +#define IRQ_IXP4XX_TSYNC (IRQ_IXP4XX_BASE + 35) +#define IRQ_IXP4XX_EAU_DONE (IRQ_IXP4XX_BASE + 36) +#define IRQ_IXP4XX_SHA_DONE (IRQ_IXP4XX_BASE + 37) +#define IRQ_IXP4XX_SWCP_PE (IRQ_IXP4XX_BASE + 58) +#define IRQ_IXP4XX_QM_PE (IRQ_IXP4XX_BASE + 60) +#define IRQ_IXP4XX_MCU_ECC (IRQ_IXP4XX_BASE + 61) +#define IRQ_IXP4XX_EXP_PE (IRQ_IXP4XX_BASE + 62) + +#define _IXP4XX_GPIO_IRQ(n) (IRQ_IXP4XX_GPIO ## n) +#define IXP4XX_GPIO_IRQ(n) _IXP4XX_GPIO_IRQ(n) + +#define XSCALE_PMU_IRQ (IRQ_IXP4XX_XSCALE_PMU) + +#endif diff --git a/arch/arm/mach-ixp4xx/ixdp425-pci.c b/arch/arm/mach-ixp4xx/ixdp425-pci.c index 318424dd3c50..c1340465b2ea 100644 --- a/arch/arm/mach-ixp4xx/ixdp425-pci.c +++ b/arch/arm/mach-ixp4xx/ixdp425-pci.c @@ -24,6 +24,8 @@ #include <mach/hardware.h> #include <asm/mach-types.h> +#include "irqs.h" + #define MAX_DEV 4 #define IRQ_LINES 4 diff --git a/arch/arm/mach-ixp4xx/ixdp425-setup.c b/arch/arm/mach-ixp4xx/ixdp425-setup.c index 57d7df79d838..6f0f7ed18ea8 100644 --- a/arch/arm/mach-ixp4xx/ixdp425-setup.c +++ b/arch/arm/mach-ixp4xx/ixdp425-setup.c @@ -32,6 +32,8 @@ #include <asm/mach/arch.h> #include <asm/mach/flash.h> +#include "irqs.h" + #define IXDP425_SDA_PIN 7 #define IXDP425_SCL_PIN 6 diff --git a/arch/arm/mach-ixp4xx/ixdpg425-pci.c b/arch/arm/mach-ixp4xx/ixdpg425-pci.c index 1f8717ba13dc..ac0e9bc6eb4d 100644 --- a/arch/arm/mach-ixp4xx/ixdpg425-pci.c +++ b/arch/arm/mach-ixp4xx/ixdpg425-pci.c @@ -23,6 +23,8 @@ #include <asm/mach/pci.h> +#include "irqs.h" + void __init ixdpg425_pci_preinit(void) { irq_set_irq_type(IRQ_IXP4XX_GPIO6, IRQ_TYPE_LEVEL_LOW); diff --git a/arch/arm/mach-ixp4xx/ixp4xx-of.c b/arch/arm/mach-ixp4xx/ixp4xx-of.c new file mode 100644 index 000000000000..7449b8319c8a --- /dev/null +++ b/arch/arm/mach-ixp4xx/ixp4xx-of.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IXP4xx Device Tree boot support + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/io.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> + +#include <mach/hardware.h> +#include <mach/ixp4xx-regs.h> + +static struct map_desc ixp4xx_of_io_desc[] __initdata = { + /* + * This is needed for runtime system configuration checks, + * such as reading if hardware so-and-so is present. This + * could eventually be converted into a syscon once all boards + * are converted to device tree. + */ + { + .virtual = IXP4XX_EXP_CFG_BASE_VIRT, + .pfn = __phys_to_pfn(IXP4XX_EXP_CFG_BASE_PHYS), + .length = SZ_4K, + .type = MT_DEVICE, + }, +#ifdef CONFIG_DEBUG_UART_8250 + /* This is needed for LL-debug/earlyprintk/debug-macro.S */ + { + .virtual = CONFIG_DEBUG_UART_VIRT, + .pfn = __phys_to_pfn(CONFIG_DEBUG_UART_PHYS), + .length = SZ_4K, + .type = MT_DEVICE, + }, +#endif +}; + +static void __init ixp4xx_of_map_io(void) +{ + iotable_init(ixp4xx_of_io_desc, ARRAY_SIZE(ixp4xx_of_io_desc)); +} + +/* + * We handle 4 differen SoC families. These compatible strings are enough + * to provide the core so that different boards can add their more detailed + * specifics. + */ +static const char *ixp4xx_of_board_compat[] = { + "intel,ixp42x", + "intel,ixp43x", + "intel,ixp45x", + "intel,ixp46x", + NULL, +}; + +DT_MACHINE_START(IXP4XX_DT, "IXP4xx (Device Tree)") + .map_io = ixp4xx_of_map_io, + .dt_compat = ixp4xx_of_board_compat, +MACHINE_END diff --git a/arch/arm/mach-ixp4xx/nas100d-pci.c b/arch/arm/mach-ixp4xx/nas100d-pci.c index 8f0eba0a6800..925ef805f966 100644 --- a/arch/arm/mach-ixp4xx/nas100d-pci.c +++ b/arch/arm/mach-ixp4xx/nas100d-pci.c @@ -21,6 +21,8 @@ #include <asm/mach/pci.h> #include <asm/mach-types.h> +#include "irqs.h" + #define MAX_DEV 3 #define IRQ_LINES 3 diff --git a/arch/arm/mach-ixp4xx/nas100d-setup.c b/arch/arm/mach-ixp4xx/nas100d-setup.c index 4138d6aa4c52..c142cfa8c5d6 100644 --- a/arch/arm/mach-ixp4xx/nas100d-setup.c +++ b/arch/arm/mach-ixp4xx/nas100d-setup.c @@ -34,6 +34,8 @@ #include <asm/mach/arch.h> #include <asm/mach/flash.h> +#include "irqs.h" + #define NAS100D_SDA_PIN 5 #define NAS100D_SCL_PIN 6 @@ -279,9 +281,6 @@ static void __init nas100d_init(void) ixp4xx_sys_init(); - /* gpio 14 and 15 are _not_ clocks */ - *IXP4XX_GPIO_GPCLKR = 0; - nas100d_flash_resource.start = IXP4XX_EXP_BUS_BASE(0); nas100d_flash_resource.end = IXP4XX_EXP_BUS_BASE(0) + ixp4xx_exp_bus_size - 1; diff --git a/arch/arm/mach-ixp4xx/nslu2-pci.c b/arch/arm/mach-ixp4xx/nslu2-pci.c index 032defe111aa..d69ee4066d20 100644 --- a/arch/arm/mach-ixp4xx/nslu2-pci.c +++ b/arch/arm/mach-ixp4xx/nslu2-pci.c @@ -21,6 +21,8 @@ #include <asm/mach/pci.h> #include <asm/mach-types.h> +#include "irqs.h" + #define MAX_DEV 3 #define IRQ_LINES 3 diff --git a/arch/arm/mach-ixp4xx/nslu2-setup.c b/arch/arm/mach-ixp4xx/nslu2-setup.c index 341b263482ef..ee1877fcfafe 100644 --- a/arch/arm/mach-ixp4xx/nslu2-setup.c +++ b/arch/arm/mach-ixp4xx/nslu2-setup.c @@ -32,6 +32,8 @@ #include <asm/mach/flash.h> #include <asm/mach/time.h> +#include "irqs.h" + #define NSLU2_SDA_PIN 7 #define NSLU2_SCL_PIN 6 @@ -125,10 +127,18 @@ static struct platform_device nslu2_i2c_gpio = { }, }; +static struct resource nslu2_beeper_resources[] = { + { + .start = IRQ_IXP4XX_TIMER2, + .flags = IORESOURCE_IRQ, + }, +}; + static struct platform_device nslu2_beeper = { .name = "ixp4xx-beeper", .id = NSLU2_GPIO_BUZZ, - .num_resources = 0, + .resource = nslu2_beeper_resources, + .num_resources = ARRAY_SIZE(nslu2_beeper_resources), }; static struct resource nslu2_uart_resources[] = { diff --git a/arch/arm/mach-ixp4xx/wg302v2-pci.c b/arch/arm/mach-ixp4xx/wg302v2-pci.c index c92e5b82af36..cf83f7e24179 100644 --- a/arch/arm/mach-ixp4xx/wg302v2-pci.c +++ b/arch/arm/mach-ixp4xx/wg302v2-pci.c @@ -27,6 +27,8 @@ #include <asm/mach/pci.h> +#include "irqs.h" + void __init wg302v2_pci_preinit(void) { irq_set_irq_type(IRQ_IXP4XX_GPIO8, IRQ_TYPE_LEVEL_LOW); diff --git a/arch/arm/mach-ixp4xx/wg302v2-setup.c b/arch/arm/mach-ixp4xx/wg302v2-setup.c index 90b3c604e8b6..8711e299229b 100644 --- a/arch/arm/mach-ixp4xx/wg302v2-setup.c +++ b/arch/arm/mach-ixp4xx/wg302v2-setup.c @@ -29,6 +29,8 @@ #include <asm/mach/arch.h> #include <asm/mach/flash.h> +#include "irqs.h" + static struct flash_platform_data wg302v2_flash_data = { .map_name = "cfi_probe", .width = 2, diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 171502a356aa..6d2b0d821c27 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -69,6 +69,13 @@ config FTTMR010_TIMER Enables support for the Faraday Technology timer block FTTMR010. +config IXP4XX_TIMER + bool "Intel XScale IXP4xx timer driver" if COMPILE_TEST + depends on HAS_IOMEM + select CLKSRC_MMIO + help + Enables support for the Intel XScale IXP4xx SoC timer. + config ROCKCHIP_TIMER bool "Rockchip timer driver" if COMPILE_TEST depends on ARM || ARM64 diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index be6e0fbc7489..dba4eff880de 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_OMAP_DM_TIMER) += timer-ti-dm.o obj-$(CONFIG_DW_APB_TIMER) += dw_apb_timer.o obj-$(CONFIG_DW_APB_TIMER_OF) += dw_apb_timer_of.o obj-$(CONFIG_FTTMR010_TIMER) += timer-fttmr010.o +obj-$(CONFIG_IXP4XX_TIMER) += timer-ixp4xx.o obj-$(CONFIG_ROCKCHIP_TIMER) += timer-rockchip.o obj-$(CONFIG_CLKSRC_NOMADIK_MTU) += nomadik-mtu.o obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o diff --git a/drivers/clocksource/timer-ixp4xx.c b/drivers/clocksource/timer-ixp4xx.c new file mode 100644 index 000000000000..404445bc11ea --- /dev/null +++ b/drivers/clocksource/timer-ixp4xx.c @@ -0,0 +1,284 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IXP4 timer driver + * Copyright (C) 2019 Linus Walleij <linus.walleij@linaro.org> + * + * Based on arch/arm/mach-ixp4xx/common.c + * Copyright 2002 (C) Intel Corporation + * Copyright 2003-2004 (C) MontaVista, Software, Inc. + * Copyright (C) Deepak Saxena <dsaxena@plexity.net> + */ +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/clockchips.h> +#include <linux/clocksource.h> +#include <linux/sched_clock.h> +#include <linux/slab.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +/* Goes away with OF conversion */ +#include <linux/platform_data/timer-ixp4xx.h> + +/* + * Constants to make it easy to access Timer Control/Status registers + */ +#define IXP4XX_OSTS_OFFSET 0x00 /* Continuous Timestamp */ +#define IXP4XX_OST1_OFFSET 0x04 /* Timer 1 Timestamp */ +#define IXP4XX_OSRT1_OFFSET 0x08 /* Timer 1 Reload */ +#define IXP4XX_OST2_OFFSET 0x0C /* Timer 2 Timestamp */ +#define IXP4XX_OSRT2_OFFSET 0x10 /* Timer 2 Reload */ +#define IXP4XX_OSWT_OFFSET 0x14 /* Watchdog Timer */ +#define IXP4XX_OSWE_OFFSET 0x18 /* Watchdog Enable */ +#define IXP4XX_OSWK_OFFSET 0x1C /* Watchdog Key */ +#define IXP4XX_OSST_OFFSET 0x20 /* Timer Status */ + +/* + * Timer register values and bit definitions + */ +#define IXP4XX_OST_ENABLE 0x00000001 +#define IXP4XX_OST_ONE_SHOT 0x00000002 +/* Low order bits of reload value ignored */ +#define IXP4XX_OST_RELOAD_MASK 0x00000003 +#define IXP4XX_OST_DISABLED 0x00000000 +#define IXP4XX_OSST_TIMER_1_PEND 0x00000001 +#define IXP4XX_OSST_TIMER_2_PEND 0x00000002 +#define IXP4XX_OSST_TIMER_TS_PEND 0x00000004 +#define IXP4XX_OSST_TIMER_WDOG_PEND 0x00000008 +#define IXP4XX_OSST_TIMER_WARM_RESET 0x00000010 + +#define IXP4XX_WDT_KEY 0x0000482E +#define IXP4XX_WDT_RESET_ENABLE 0x00000001 +#define IXP4XX_WDT_IRQ_ENABLE 0x00000002 +#define IXP4XX_WDT_COUNT_ENABLE 0x00000004 + +struct ixp4xx_timer { + void __iomem *base; + unsigned int tick_rate; + u32 latch; + struct clock_event_device clkevt; +#ifdef CONFIG_ARM + struct delay_timer delay_timer; +#endif +}; + +/* + * A local singleton used by sched_clock and delay timer reads, which are + * fast and stateless + */ +static struct ixp4xx_timer *local_ixp4xx_timer; + +static inline struct ixp4xx_timer * +to_ixp4xx_timer(struct clock_event_device *evt) +{ + return container_of(evt, struct ixp4xx_timer, clkevt); +} + +static u64 notrace ixp4xx_read_sched_clock(void) +{ + return __raw_readl(local_ixp4xx_timer->base + IXP4XX_OSTS_OFFSET); +} + +static u64 ixp4xx_clocksource_read(struct clocksource *c) +{ + return __raw_readl(local_ixp4xx_timer->base + IXP4XX_OSTS_OFFSET); +} + +static irqreturn_t ixp4xx_timer_interrupt(int irq, void *dev_id) +{ + struct ixp4xx_timer *tmr = dev_id; + struct clock_event_device *evt = &tmr->clkevt; + + /* Clear Pending Interrupt */ + __raw_writel(IXP4XX_OSST_TIMER_1_PEND, + tmr->base + IXP4XX_OSST_OFFSET); + + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static int ixp4xx_set_next_event(unsigned long cycles, + struct clock_event_device *evt) +{ + struct ixp4xx_timer *tmr = to_ixp4xx_timer(evt); + u32 val; + + val = __raw_readl(tmr->base + IXP4XX_OSRT1_OFFSET); + /* Keep enable/oneshot bits */ + val &= IXP4XX_OST_RELOAD_MASK; + __raw_writel((cycles & ~IXP4XX_OST_RELOAD_MASK) | val, + tmr->base + IXP4XX_OSRT1_OFFSET); + + return 0; +} + +static int ixp4xx_shutdown(struct clock_event_device *evt) +{ + struct ixp4xx_timer *tmr = to_ixp4xx_timer(evt); + u32 val; + + val = __raw_readl(tmr->base + IXP4XX_OSRT1_OFFSET); + val &= ~IXP4XX_OST_ENABLE; + __raw_writel(val, tmr->base + IXP4XX_OSRT1_OFFSET); + + return 0; +} + +static int ixp4xx_set_oneshot(struct clock_event_device *evt) +{ + struct ixp4xx_timer *tmr = to_ixp4xx_timer(evt); + + __raw_writel(IXP4XX_OST_ENABLE | IXP4XX_OST_ONE_SHOT, + tmr->base + IXP4XX_OSRT1_OFFSET); + + return 0; +} + +static int ixp4xx_set_periodic(struct clock_event_device *evt) +{ + struct ixp4xx_timer *tmr = to_ixp4xx_timer(evt); + u32 val; + + val = tmr->latch & ~IXP4XX_OST_RELOAD_MASK; + val |= IXP4XX_OST_ENABLE; + __raw_writel(val, tmr->base + IXP4XX_OSRT1_OFFSET); + + return 0; +} + +static int ixp4xx_resume(struct clock_event_device *evt) +{ + struct ixp4xx_timer *tmr = to_ixp4xx_timer(evt); + u32 val; + + val = __raw_readl(tmr->base + IXP4XX_OSRT1_OFFSET); + val |= IXP4XX_OST_ENABLE; + __raw_writel(val, tmr->base + IXP4XX_OSRT1_OFFSET); + + return 0; +} + +/* + * IXP4xx timer tick + * We use OS timer1 on the CPU for the timer tick and the timestamp + * counter as a source of real clock ticks to account for missed jiffies. + */ +static __init int ixp4xx_timer_register(void __iomem *base, + int timer_irq, + unsigned int timer_freq) +{ + struct ixp4xx_timer *tmr; + int ret; + + tmr = kzalloc(sizeof(*tmr), GFP_KERNEL); + if (!tmr) + return -ENOMEM; + tmr->base = base; + tmr->tick_rate = timer_freq; + + /* + * The timer register doesn't allow to specify the two least + * significant bits of the timeout value and assumes them being zero. + * So make sure the latch is the best value with the two least + * significant bits unset. + */ + tmr->latch = DIV_ROUND_CLOSEST(timer_freq, + (IXP4XX_OST_RELOAD_MASK + 1) * HZ) + * (IXP4XX_OST_RELOAD_MASK + 1); + + local_ixp4xx_timer = tmr; + + /* Reset/disable counter */ + __raw_writel(0, tmr->base + IXP4XX_OSRT1_OFFSET); + + /* Clear any pending interrupt on timer 1 */ + __raw_writel(IXP4XX_OSST_TIMER_1_PEND, + tmr->base + IXP4XX_OSST_OFFSET); + + /* Reset time-stamp counter */ + __raw_writel(0, tmr->base + IXP4XX_OSTS_OFFSET); + + clocksource_mmio_init(NULL, "OSTS", timer_freq, 200, 32, + ixp4xx_clocksource_read); + + tmr->clkevt.name = "ixp4xx timer1"; + tmr->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; + tmr->clkevt.rating = 200; + tmr->clkevt.set_state_shutdown = ixp4xx_shutdown; + tmr->clkevt.set_state_periodic = ixp4xx_set_periodic; + tmr->clkevt.set_state_oneshot = ixp4xx_set_oneshot; + tmr->clkevt.tick_resume = ixp4xx_resume; + tmr->clkevt.set_next_event = ixp4xx_set_next_event; + tmr->clkevt.cpumask = cpumask_of(0); + tmr->clkevt.irq = timer_irq; + ret = request_irq(timer_irq, ixp4xx_timer_interrupt, + IRQF_TIMER, "IXP4XX-TIMER1", tmr); + if (ret) { + pr_crit("no timer IRQ\n"); + return -ENODEV; + } + clockevents_config_and_register(&tmr->clkevt, timer_freq, + 0xf, 0xfffffffe); + +#ifdef CONFIG_ARM + sched_clock_register(ixp4xx_read_sched_clock, 32, timer_freq); +#endif + + return 0; +} + +/** + * ixp4xx_timer_setup() - Timer setup function to be called from boardfiles + * @timerbase: physical base of timer block + * @timer_irq: Linux IRQ number for the timer + * @timer_freq: Fixed frequency of the timer + */ +void __init ixp4xx_timer_setup(resource_size_t timerbase, + int timer_irq, + unsigned int timer_freq) +{ + void __iomem *base; + + base = ioremap(timerbase, 0x100); + if (!base) { + pr_crit("IXP4xx: can't remap timer\n"); + return; + } + ixp4xx_timer_register(base, timer_irq, timer_freq); +} +EXPORT_SYMBOL_GPL(ixp4xx_timer_setup); + +#ifdef CONFIG_OF +static __init int ixp4xx_of_timer_init(struct device_node *np) +{ + void __iomem *base; + int irq; + int ret; + + base = of_iomap(np, 0); + if (!base) { + pr_crit("IXP4xx: can't remap timer\n"); + return -ENODEV; + } + + irq = irq_of_parse_and_map(np, 0); + if (irq <= 0) { + pr_err("Can't parse IRQ\n"); + ret = -EINVAL; + goto out_unmap; + } + + /* TODO: get some fixed clocks into the device tree */ + ret = ixp4xx_timer_register(base, irq, 66666000); + if (ret) + goto out_unmap; + return 0; + +out_unmap: + iounmap(base); + return ret; +} +TIMER_OF_DECLARE(ixp4xx, "intel,ixp4xx-timer", ixp4xx_of_timer_init); +#endif diff --git a/drivers/crypto/ixp4xx_crypto.c b/drivers/crypto/ixp4xx_crypto.c index 5c4659b04d70..5522d64ecfda 100644 --- a/drivers/crypto/ixp4xx_crypto.c +++ b/drivers/crypto/ixp4xx_crypto.c @@ -30,8 +30,8 @@ #include <crypto/authenc.h> #include <crypto/scatterwalk.h> -#include <mach/npe.h> -#include <mach/qmgr.h> +#include <linux/soc/ixp4xx/npe.h> +#include <linux/soc/ixp4xx/qmgr.h> #define MAX_KEYLEN 32 diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 3f50526a771f..a77b3b25a779 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -287,6 +287,19 @@ config GPIO_IOP If unsure, say N. +config GPIO_IXP4XX + bool "Intel IXP4xx GPIO" + depends on ARM # For <asm/mach-types.h> + depends on ARCH_IXP4XX + select GPIO_GENERIC + select IRQ_DOMAIN + select IRQ_DOMAIN_HIERARCHY + help + Say yes here to support the GPIO functionality of a number of Intel + IXP4xx series of chips. + + If unsure, say N. + config GPIO_LOONGSON bool "Loongson-2/3 GPIO support" depends on CPU_LOONGSON2 || CPU_LOONGSON3 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 54d55274b93a..1ee5dab9c3cf 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -61,6 +61,7 @@ obj-$(CONFIG_GPIO_HLWD) += gpio-hlwd.o obj-$(CONFIG_HTC_EGPIO) += gpio-htc-egpio.o obj-$(CONFIG_GPIO_ICH) += gpio-ich.o obj-$(CONFIG_GPIO_IOP) += gpio-iop.o +obj-$(CONFIG_GPIO_IXP4XX) += gpio-ixp4xx.o obj-$(CONFIG_GPIO_IT87) += gpio-it87.o obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o obj-$(CONFIG_GPIO_KEMPLD) += gpio-kempld.o diff --git a/drivers/gpio/gpio-ixp4xx.c b/drivers/gpio/gpio-ixp4xx.c new file mode 100644 index 000000000000..4b1cf7ea858d --- /dev/null +++ b/drivers/gpio/gpio-ixp4xx.c @@ -0,0 +1,474 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// IXP4 GPIO driver +// Copyright (C) 2019 Linus Walleij <linus.walleij@linaro.org> +// +// based on previous work and know-how from: +// Deepak Saxena <dsaxena@plexity.net> + +#include <linux/gpio/driver.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/irqchip.h> +#include <linux/of_irq.h> +#include <linux/platform_device.h> +#include <linux/bitops.h> +/* Include that go away with DT transition */ +#include <linux/irqchip/irq-ixp4xx.h> + +#include <asm/mach-types.h> + +#define IXP4XX_REG_GPOUT 0x00 +#define IXP4XX_REG_GPOE 0x04 +#define IXP4XX_REG_GPIN 0x08 +#define IXP4XX_REG_GPIS 0x0C +#define IXP4XX_REG_GPIT1 0x10 +#define IXP4XX_REG_GPIT2 0x14 +#define IXP4XX_REG_GPCLK 0x18 +#define IXP4XX_REG_GPDBSEL 0x1C + +/* + * The hardware uses 3 bits to indicate interrupt "style". + * we clear and set these three bits accordingly. The lower 24 + * bits in two registers (GPIT1 and GPIT2) are used to set up + * the style for 8 lines each for a total of 16 GPIO lines. + */ +#define IXP4XX_GPIO_STYLE_ACTIVE_HIGH 0x0 +#define IXP4XX_GPIO_STYLE_ACTIVE_LOW 0x1 +#define IXP4XX_GPIO_STYLE_RISING_EDGE 0x2 +#define IXP4XX_GPIO_STYLE_FALLING_EDGE 0x3 +#define IXP4XX_GPIO_STYLE_TRANSITIONAL 0x4 +#define IXP4XX_GPIO_STYLE_MASK GENMASK(2, 0) +#define IXP4XX_GPIO_STYLE_SIZE 3 + +/** + * struct ixp4xx_gpio - IXP4 GPIO state container + * @dev: containing device for this instance + * @fwnode: the fwnode for this GPIO chip + * @gc: gpiochip for this instance + * @domain: irqdomain for this chip instance + * @base: remapped I/O-memory base + * @irq_edge: Each bit represents an IRQ: 1: edge-triggered, + * 0: level triggered + */ +struct ixp4xx_gpio { + struct device *dev; + struct fwnode_handle *fwnode; + struct gpio_chip gc; + struct irq_domain *domain; + void __iomem *base; + unsigned long long irq_edge; +}; + +/** + * struct ixp4xx_gpio_map - IXP4 GPIO to parent IRQ map + * @gpio_offset: offset of the IXP4 GPIO line + * @parent_hwirq: hwirq on the parent IRQ controller + */ +struct ixp4xx_gpio_map { + int gpio_offset; + int parent_hwirq; +}; + +/* GPIO lines 0..12 have corresponding IRQs, GPIOs 13..15 have no IRQs */ +const struct ixp4xx_gpio_map ixp4xx_gpiomap[] = { + { .gpio_offset = 0, .parent_hwirq = 6 }, + { .gpio_offset = 1, .parent_hwirq = 7 }, + { .gpio_offset = 2, .parent_hwirq = 19 }, + { .gpio_offset = 3, .parent_hwirq = 20 }, + { .gpio_offset = 4, .parent_hwirq = 21 }, + { .gpio_offset = 5, .parent_hwirq = 22 }, + { .gpio_offset = 6, .parent_hwirq = 23 }, + { .gpio_offset = 7, .parent_hwirq = 24 }, + { .gpio_offset = 8, .parent_hwirq = 25 }, + { .gpio_offset = 9, .parent_hwirq = 26 }, + { .gpio_offset = 10, .parent_hwirq = 27 }, + { .gpio_offset = 11, .parent_hwirq = 28 }, + { .gpio_offset = 12, .parent_hwirq = 29 }, +}; + +static void ixp4xx_gpio_irq_ack(struct irq_data *d) +{ + struct ixp4xx_gpio *g = irq_data_get_irq_chip_data(d); + + __raw_writel(BIT(d->hwirq), g->base + IXP4XX_REG_GPIS); +} + +static void ixp4xx_gpio_irq_unmask(struct irq_data *d) +{ + struct ixp4xx_gpio *g = irq_data_get_irq_chip_data(d); + + /* ACK when unmasking if not edge-triggered */ + if (!(g->irq_edge & BIT(d->hwirq))) + ixp4xx_gpio_irq_ack(d); + + irq_chip_unmask_parent(d); +} + +static int ixp4xx_gpio_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct ixp4xx_gpio *g = irq_data_get_irq_chip_data(d); + int line = d->hwirq; + unsigned long flags; + u32 int_style; + u32 int_reg; + u32 val; + + switch (type) { + case IRQ_TYPE_EDGE_BOTH: + irq_set_handler_locked(d, handle_edge_irq); + int_style = IXP4XX_GPIO_STYLE_TRANSITIONAL; + g->irq_edge |= BIT(d->hwirq); + break; + case IRQ_TYPE_EDGE_RISING: + irq_set_handler_locked(d, handle_edge_irq); + int_style = IXP4XX_GPIO_STYLE_RISING_EDGE; + g->irq_edge |= BIT(d->hwirq); + break; + case IRQ_TYPE_EDGE_FALLING: + irq_set_handler_locked(d, handle_edge_irq); + int_style = IXP4XX_GPIO_STYLE_FALLING_EDGE; + g->irq_edge |= BIT(d->hwirq); + break; + case IRQ_TYPE_LEVEL_HIGH: + irq_set_handler_locked(d, handle_level_irq); + int_style = IXP4XX_GPIO_STYLE_ACTIVE_HIGH; + g->irq_edge &= ~BIT(d->hwirq); + break; + case IRQ_TYPE_LEVEL_LOW: + irq_set_handler_locked(d, handle_level_irq); + int_style = IXP4XX_GPIO_STYLE_ACTIVE_LOW; + g->irq_edge &= ~BIT(d->hwirq); + break; + default: + return -EINVAL; + } + + if (line >= 8) { + /* pins 8-15 */ + line -= 8; + int_reg = IXP4XX_REG_GPIT2; + } else { + /* pins 0-7 */ + int_reg = IXP4XX_REG_GPIT1; + } + + spin_lock_irqsave(&g->gc.bgpio_lock, flags); + + /* Clear the style for the appropriate pin */ + val = __raw_readl(g->base + int_reg); + val &= ~(IXP4XX_GPIO_STYLE_MASK << (line * IXP4XX_GPIO_STYLE_SIZE)); + __raw_writel(val, g->base + int_reg); + + __raw_writel(BIT(line), g->base + IXP4XX_REG_GPIS); + + /* Set the new style */ + val = __raw_readl(g->base + int_reg); + val |= (int_style << (line * IXP4XX_GPIO_STYLE_SIZE)); + __raw_writel(val, g->base + int_reg); + + /* Force-configure this line as an input */ + val = __raw_readl(g->base + IXP4XX_REG_GPOE); + val |= BIT(d->hwirq); + __raw_writel(val, g->base + IXP4XX_REG_GPOE); + + spin_unlock_irqrestore(&g->gc.bgpio_lock, flags); + + /* This parent only accept level high (asserted) */ + return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH); +} + +static struct irq_chip ixp4xx_gpio_irqchip = { + .name = "IXP4GPIO", + .irq_ack = ixp4xx_gpio_irq_ack, + .irq_mask = irq_chip_mask_parent, + .irq_unmask = ixp4xx_gpio_irq_unmask, + .irq_set_type = ixp4xx_gpio_irq_set_type, +}; + +static int ixp4xx_gpio_to_irq(struct gpio_chip *gc, unsigned int offset) +{ + struct ixp4xx_gpio *g = gpiochip_get_data(gc); + struct irq_fwspec fwspec; + + fwspec.fwnode = g->fwnode; + fwspec.param_count = 2; + fwspec.param[0] = offset; + fwspec.param[1] = IRQ_TYPE_NONE; + + return irq_create_fwspec_mapping(&fwspec); +} + +static int ixp4xx_gpio_irq_domain_translate(struct irq_domain *domain, + struct irq_fwspec *fwspec, + unsigned long *hwirq, + unsigned int *type) +{ + + /* We support standard DT translation */ + if (is_of_node(fwspec->fwnode) && fwspec->param_count == 2) { + *hwirq = fwspec->param[0]; + *type = fwspec->param[1]; + return 0; + } + + /* This goes away when we transition to DT */ + if (is_fwnode_irqchip(fwspec->fwnode)) { + if (fwspec->param_count != 2) + return -EINVAL; + *hwirq = fwspec->param[0]; + *type = fwspec->param[1]; + WARN_ON(*type == IRQ_TYPE_NONE); + return 0; + } + return -EINVAL; +} + +static int ixp4xx_gpio_irq_domain_alloc(struct irq_domain *d, + unsigned int irq, unsigned int nr_irqs, + void *data) +{ + struct ixp4xx_gpio *g = d->host_data; + irq_hw_number_t hwirq; + unsigned int type = IRQ_TYPE_NONE; + struct irq_fwspec *fwspec = data; + int ret; + int i; + + ret = ixp4xx_gpio_irq_domain_translate(d, fwspec, &hwirq, &type); + if (ret) + return ret; + + dev_dbg(g->dev, "allocate IRQ %d..%d, hwirq %lu..%lu\n", + irq, irq + nr_irqs - 1, + hwirq, hwirq + nr_irqs - 1); + + for (i = 0; i < nr_irqs; i++) { + struct irq_fwspec parent_fwspec; + const struct ixp4xx_gpio_map *map; + int j; + + /* Not all lines support IRQs */ + for (j = 0; j < ARRAY_SIZE(ixp4xx_gpiomap); j++) { + map = &ixp4xx_gpiomap[j]; + if (map->gpio_offset == hwirq) + break; + } + if (j == ARRAY_SIZE(ixp4xx_gpiomap)) { + dev_err(g->dev, "can't look up hwirq %lu\n", hwirq); + return -EINVAL; + } + dev_dbg(g->dev, "found parent hwirq %u\n", map->parent_hwirq); + + /* + * We set handle_bad_irq because the .set_type() should + * always be invoked and set the right type of handler. + */ + irq_domain_set_info(d, + irq + i, + hwirq + i, + &ixp4xx_gpio_irqchip, + g, + handle_bad_irq, + NULL, NULL); + irq_set_probe(irq + i); + + /* + * Create a IRQ fwspec to send up to the parent irqdomain: + * specify the hwirq we address on the parent and tie it + * all together up the chain. + */ + parent_fwspec.fwnode = d->parent->fwnode; + parent_fwspec.param_count = 2; + parent_fwspec.param[0] = map->parent_hwirq; + /* This parent only handles asserted level IRQs */ + parent_fwspec.param[1] = IRQ_TYPE_LEVEL_HIGH; + dev_dbg(g->dev, "alloc_irqs_parent for %d parent hwirq %d\n", + irq + i, map->parent_hwirq); + ret = irq_domain_alloc_irqs_parent(d, irq + i, 1, + &parent_fwspec); + if (ret) + dev_err(g->dev, + "failed to allocate parent hwirq %d for hwirq %lu\n", + map->parent_hwirq, hwirq); + } + + return 0; +} + +static const struct irq_domain_ops ixp4xx_gpio_irqdomain_ops = { + .translate = ixp4xx_gpio_irq_domain_translate, + .alloc = ixp4xx_gpio_irq_domain_alloc, + .free = irq_domain_free_irqs_common, +}; + +static int ixp4xx_gpio_probe(struct platform_device *pdev) +{ + unsigned long flags; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct irq_domain *parent; + struct resource *res; + struct ixp4xx_gpio *g; + int ret; + int i; + + g = devm_kzalloc(dev, sizeof(*g), GFP_KERNEL); + if (!g) + return -ENOMEM; + g->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + g->base = devm_ioremap_resource(dev, res); + if (IS_ERR(g->base)) { + dev_err(dev, "ioremap error\n"); + return PTR_ERR(g->base); + } + + /* + * Make sure GPIO 14 and 15 are NOT used as clocks but GPIO on + * specific machines. + */ + if (machine_is_dsmg600() || machine_is_nas100d()) + __raw_writel(0x0, g->base + IXP4XX_REG_GPCLK); + + /* + * This is a very special big-endian ARM issue: when the IXP4xx is + * run in big endian mode, all registers in the machine are switched + * around to the CPU-native endianness. As you see mostly in the + * driver we use __raw_readl()/__raw_writel() to access the registers + * in the appropriate order. With the GPIO library we need to specify + * byte order explicitly, so this flag needs to be set when compiling + * for big endian. + */ +#if defined(CONFIG_CPU_BIG_ENDIAN) + flags = BGPIOF_BIG_ENDIAN_BYTE_ORDER; +#else + flags = 0; +#endif + + /* Populate and register gpio chip */ + ret = bgpio_init(&g->gc, dev, 4, + g->base + IXP4XX_REG_GPIN, + g->base + IXP4XX_REG_GPOUT, + NULL, + NULL, + g->base + IXP4XX_REG_GPOE, + flags); + if (ret) { + dev_err(dev, "unable to init generic GPIO\n"); + return ret; + } + g->gc.to_irq = ixp4xx_gpio_to_irq; + g->gc.ngpio = 16; + g->gc.label = "IXP4XX_GPIO_CHIP"; + /* + * TODO: when we have migrated to device tree and all GPIOs + * are fetched using phandles, set this to -1 to get rid of + * the fixed gpiochip base. + */ + g->gc.base = 0; + g->gc.parent = &pdev->dev; + g->gc.owner = THIS_MODULE; + + ret = devm_gpiochip_add_data(dev, &g->gc, g); + if (ret) { + dev_err(dev, "failed to add SoC gpiochip\n"); + return ret; + } + + /* + * When we convert to device tree we will simply look up the + * parent irqdomain using irq_find_host(parent) as parent comes + * from IRQCHIP_DECLARE(), then use of_node_to_fwnode() to get + * the fwnode. For now we need this boardfile style code. + */ + if (np) { + struct device_node *irq_parent; + + irq_parent = of_irq_find_parent(np); + if (!irq_parent) { + dev_err(dev, "no IRQ parent node\n"); + return -ENODEV; + } + parent = irq_find_host(irq_parent); + if (!parent) { + dev_err(dev, "no IRQ parent domain\n"); + return -ENODEV; + } + g->fwnode = of_node_to_fwnode(np); + } else { + parent = ixp4xx_get_irq_domain(); + g->fwnode = irq_domain_alloc_fwnode(g->base); + if (!g->fwnode) { + dev_err(dev, "no domain base\n"); + return -ENODEV; + } + } + g->domain = irq_domain_create_hierarchy(parent, + IRQ_DOMAIN_FLAG_HIERARCHY, + ARRAY_SIZE(ixp4xx_gpiomap), + g->fwnode, + &ixp4xx_gpio_irqdomain_ops, + g); + if (!g->domain) { + irq_domain_free_fwnode(g->fwnode); + dev_err(dev, "no hierarchical irq domain\n"); + return ret; + } + + /* + * After adding OF support, this is no longer needed: irqs + * will be allocated for the respective fwnodes. + */ + if (!np) { + for (i = 0; i < ARRAY_SIZE(ixp4xx_gpiomap); i++) { + const struct ixp4xx_gpio_map *map = &ixp4xx_gpiomap[i]; + struct irq_fwspec fwspec; + + fwspec.fwnode = g->fwnode; + /* This is the hwirq for the GPIO line side of things */ + fwspec.param[0] = map->gpio_offset; + fwspec.param[1] = IRQ_TYPE_EDGE_RISING; + fwspec.param_count = 2; + ret = __irq_domain_alloc_irqs(g->domain, + -1, /* just pick something */ + 1, + NUMA_NO_NODE, + &fwspec, + false, + NULL); + if (ret < 0) { + irq_domain_free_fwnode(g->fwnode); + dev_err(dev, + "can not allocate irq for GPIO line %d parent hwirq %d in hierarchy domain: %d\n", + map->gpio_offset, map->parent_hwirq, + ret); + return ret; + } + } + } + + platform_set_drvdata(pdev, g); + dev_info(dev, "IXP4 GPIO @%p registered\n", g->base); + + return 0; +} + +static const struct of_device_id ixp4xx_gpio_of_match[] = { + { + .compatible = "intel,ixp4xx-gpio", + }, + {}, +}; + + +static struct platform_driver ixp4xx_gpio_driver = { + .driver = { + .name = "ixp4xx-gpio", + .of_match_table = of_match_ptr(ixp4xx_gpio_of_match), + }, + .probe = ixp4xx_gpio_probe, +}; +builtin_platform_driver(ixp4xx_gpio_driver); diff --git a/drivers/input/misc/ixp4xx-beeper.c b/drivers/input/misc/ixp4xx-beeper.c index 1fe149f3def2..4776273fa10b 100644 --- a/drivers/input/misc/ixp4xx-beeper.c +++ b/drivers/input/misc/ixp4xx-beeper.c @@ -30,6 +30,8 @@ MODULE_ALIAS("platform:ixp4xx-beeper"); static DEFINE_SPINLOCK(beep_lock); +static int ixp4xx_timer2_irq; + static void ixp4xx_spkr_control(unsigned int pin, unsigned int count) { unsigned long flags; @@ -90,6 +92,7 @@ static irqreturn_t ixp4xx_spkr_interrupt(int irq, void *dev_id) static int ixp4xx_spkr_probe(struct platform_device *dev) { struct input_dev *input_dev; + int irq; int err; input_dev = input_allocate_device(); @@ -110,15 +113,22 @@ static int ixp4xx_spkr_probe(struct platform_device *dev) input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE); input_dev->event = ixp4xx_spkr_event; + irq = platform_get_irq(dev, 0); + if (irq < 0) { + err = irq; + goto err_free_device; + } + err = gpio_request(dev->id, "ixp4-beeper"); if (err) goto err_free_device; - err = request_irq(IRQ_IXP4XX_TIMER2, &ixp4xx_spkr_interrupt, + err = request_irq(irq, &ixp4xx_spkr_interrupt, IRQF_NO_SUSPEND, "ixp4xx-beeper", (void *) dev->id); if (err) goto err_free_gpio; + ixp4xx_timer2_irq = irq; err = input_register_device(input_dev); if (err) @@ -129,7 +139,7 @@ static int ixp4xx_spkr_probe(struct platform_device *dev) return 0; err_free_irq: - free_irq(IRQ_IXP4XX_TIMER2, (void *)dev->id); + free_irq(irq, (void *)dev->id); err_free_gpio: gpio_free(dev->id); err_free_device: @@ -146,10 +156,10 @@ static int ixp4xx_spkr_remove(struct platform_device *dev) input_unregister_device(input_dev); /* turn the speaker off */ - disable_irq(IRQ_IXP4XX_TIMER2); + disable_irq(ixp4xx_timer2_irq); ixp4xx_spkr_control(pin, 0); - free_irq(IRQ_IXP4XX_TIMER2, (void *)dev->id); + free_irq(ixp4xx_timer2_irq, (void *)dev->id); gpio_free(dev->id); return 0; @@ -161,7 +171,7 @@ static void ixp4xx_spkr_shutdown(struct platform_device *dev) unsigned int pin = (unsigned int) input_get_drvdata(input_dev); /* turn off the speaker */ - disable_irq(IRQ_IXP4XX_TIMER2); + disable_irq(ixp4xx_timer2_irq); ixp4xx_spkr_control(pin, 0); } diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 5438abb1baba..cf7984991062 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -160,6 +160,12 @@ config IMGPDC_IRQ select GENERIC_IRQ_CHIP select IRQ_DOMAIN +config IXP4XX_IRQ + bool + select IRQ_DOMAIN + select GENERIC_IRQ_MULTI_HANDLER + select SPARSE_IRQ + config MADERA_IRQ tristate diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 85972ae1bd7f..f8c66e958a64 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_ATMEL_AIC5_IRQ) += irq-atmel-aic-common.o irq-atmel-aic5.o obj-$(CONFIG_I8259) += irq-i8259.o obj-$(CONFIG_IMGPDC_IRQ) += irq-imgpdc.o obj-$(CONFIG_IRQ_MIPS_CPU) += irq-mips-cpu.o +obj-$(CONFIG_IXP4XX_IRQ) += irq-ixp4xx.o obj-$(CONFIG_SIRF_IRQ) += irq-sirfsoc.o obj-$(CONFIG_JCORE_AIC) += irq-jcore-aic.o obj-$(CONFIG_RDA_INTC) += irq-rda-intc.o diff --git a/drivers/irqchip/irq-ixp4xx.c b/drivers/irqchip/irq-ixp4xx.c new file mode 100644 index 000000000000..d576809429ac --- /dev/null +++ b/drivers/irqchip/irq-ixp4xx.c @@ -0,0 +1,403 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * irqchip for the IXP4xx interrupt controller + * Copyright (C) 2019 Linus Walleij <linus.walleij@linaro.org> + * + * Based on arch/arm/mach-ixp4xx/common.c + * Copyright 2002 (C) Intel Corporation + * Copyright 2003-2004 (C) MontaVista, Software, Inc. + * Copyright (C) Deepak Saxena <dsaxena@plexity.net> + */ +#include <linux/bitops.h> +#include <linux/gpio/driver.h> +#include <linux/irq.h> +#include <linux/io.h> +#include <linux/irqchip.h> +#include <linux/irqchip/irq-ixp4xx.h> +#include <linux/irqdomain.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/platform_device.h> +#include <linux/cpu.h> + +#include <asm/exception.h> +#include <asm/mach/irq.h> + +#define IXP4XX_ICPR 0x00 /* Interrupt Status */ +#define IXP4XX_ICMR 0x04 /* Interrupt Enable */ +#define IXP4XX_ICLR 0x08 /* Interrupt IRQ/FIQ Select */ +#define IXP4XX_ICIP 0x0C /* IRQ Status */ +#define IXP4XX_ICFP 0x10 /* FIQ Status */ +#define IXP4XX_ICHR 0x14 /* Interrupt Priority */ +#define IXP4XX_ICIH 0x18 /* IRQ Highest Pri Int */ +#define IXP4XX_ICFH 0x1C /* FIQ Highest Pri Int */ + +/* IXP43x and IXP46x-only */ +#define IXP4XX_ICPR2 0x20 /* Interrupt Status 2 */ +#define IXP4XX_ICMR2 0x24 /* Interrupt Enable 2 */ +#define IXP4XX_ICLR2 0x28 /* Interrupt IRQ/FIQ Select 2 */ +#define IXP4XX_ICIP2 0x2C /* IRQ Status */ +#define IXP4XX_ICFP2 0x30 /* FIQ Status */ +#define IXP4XX_ICEEN 0x34 /* Error High Pri Enable */ + +/** + * struct ixp4xx_irq - state container for the Faraday IRQ controller + * @irqbase: IRQ controller memory base in virtual memory + * @is_356: if this is an IXP43x, IXP45x or IX46x SoC (with 64 IRQs) + * @irqchip: irqchip for this instance + * @domain: IRQ domain for this instance + */ +struct ixp4xx_irq { + void __iomem *irqbase; + bool is_356; + struct irq_chip irqchip; + struct irq_domain *domain; +}; + +/* Local static state container */ +static struct ixp4xx_irq ixirq; + +/* GPIO Clocks */ +#define IXP4XX_GPIO_CLK_0 14 +#define IXP4XX_GPIO_CLK_1 15 + +static int ixp4xx_set_irq_type(struct irq_data *d, unsigned int type) +{ + /* All are level active high (asserted) here */ + if (type != IRQ_TYPE_LEVEL_HIGH) + return -EINVAL; + return 0; +} + +static void ixp4xx_irq_mask(struct irq_data *d) +{ + struct ixp4xx_irq *ixi = irq_data_get_irq_chip_data(d); + u32 val; + + if (ixi->is_356 && d->hwirq >= 32) { + val = __raw_readl(ixi->irqbase + IXP4XX_ICMR2); + val &= ~BIT(d->hwirq - 32); + __raw_writel(val, ixi->irqbase + IXP4XX_ICMR2); + } else { + val = __raw_readl(ixi->irqbase + IXP4XX_ICMR); + val &= ~BIT(d->hwirq); + __raw_writel(val, ixi->irqbase + IXP4XX_ICMR); + } +} + +/* + * Level triggered interrupts on GPIO lines can only be cleared when the + * interrupt condition disappears. + */ +static void ixp4xx_irq_unmask(struct irq_data *d) +{ + struct ixp4xx_irq *ixi = irq_data_get_irq_chip_data(d); + u32 val; + + if (ixi->is_356 && d->hwirq >= 32) { + val = __raw_readl(ixi->irqbase + IXP4XX_ICMR2); + val |= BIT(d->hwirq - 32); + __raw_writel(val, ixi->irqbase + IXP4XX_ICMR2); + } else { + val = __raw_readl(ixi->irqbase + IXP4XX_ICMR); + val |= BIT(d->hwirq); + __raw_writel(val, ixi->irqbase + IXP4XX_ICMR); + } +} + +asmlinkage void __exception_irq_entry ixp4xx_handle_irq(struct pt_regs *regs) +{ + struct ixp4xx_irq *ixi = &ixirq; + unsigned long status; + int i; + + status = __raw_readl(ixi->irqbase + IXP4XX_ICIP); + for_each_set_bit(i, &status, 32) + handle_domain_irq(ixi->domain, i, regs); + + /* + * IXP465/IXP435 has an upper IRQ status register + */ + if (ixi->is_356) { + status = __raw_readl(ixi->irqbase + IXP4XX_ICIP2); + for_each_set_bit(i, &status, 32) + handle_domain_irq(ixi->domain, i + 32, regs); + } +} + +static int ixp4xx_irq_domain_translate(struct irq_domain *domain, + struct irq_fwspec *fwspec, + unsigned long *hwirq, + unsigned int *type) +{ + /* We support standard DT translation */ + if (is_of_node(fwspec->fwnode) && fwspec->param_count == 2) { + *hwirq = fwspec->param[0]; + *type = fwspec->param[1]; + return 0; + } + + if (is_fwnode_irqchip(fwspec->fwnode)) { + if (fwspec->param_count != 2) + return -EINVAL; + *hwirq = fwspec->param[0]; + *type = fwspec->param[1]; + WARN_ON(*type == IRQ_TYPE_NONE); + return 0; + } + + return -EINVAL; +} + +static int ixp4xx_irq_domain_alloc(struct irq_domain *d, + unsigned int irq, unsigned int nr_irqs, + void *data) +{ + struct ixp4xx_irq *ixi = d->host_data; + irq_hw_number_t hwirq; + unsigned int type = IRQ_TYPE_NONE; + struct irq_fwspec *fwspec = data; + int ret; + int i; + + ret = ixp4xx_irq_domain_translate(d, fwspec, &hwirq, &type); + if (ret) + return ret; + + for (i = 0; i < nr_irqs; i++) { + /* + * TODO: after converting IXP4xx to only device tree, set + * handle_bad_irq as default handler and assume all consumers + * call .set_type() as this is provided in the second cell in + * the device tree phandle. + */ + irq_domain_set_info(d, + irq + i, + hwirq + i, + &ixi->irqchip, + ixi, + handle_level_irq, + NULL, NULL); + irq_set_probe(irq + i); + } + + return 0; +} + +/* + * This needs to be a hierarchical irqdomain to work well with the + * GPIO irqchip (which is lower in the hierarchy) + */ +static const struct irq_domain_ops ixp4xx_irqdomain_ops = { + .translate = ixp4xx_irq_domain_translate, + .alloc = ixp4xx_irq_domain_alloc, + .free = irq_domain_free_irqs_common, +}; + +/** + * ixp4xx_get_irq_domain() - retrieve the ixp4xx irq domain + * + * This function will go away when we transition to DT probing. + */ +struct irq_domain *ixp4xx_get_irq_domain(void) +{ + struct ixp4xx_irq *ixi = &ixirq; + + return ixi->domain; +} +EXPORT_SYMBOL_GPL(ixp4xx_get_irq_domain); + +/* + * This is the Linux IRQ to hwirq mapping table. This goes away when + * we have DT support as all IRQ resources are defined in the device + * tree. It will register all the IRQs that are not used by the hierarchical + * GPIO IRQ chip. The "holes" inbetween these IRQs will be requested by + * the GPIO driver using . This is a step-gap solution. + */ +struct ixp4xx_irq_chunk { + int irq; + int hwirq; + int nr_irqs; +}; + +static const struct ixp4xx_irq_chunk ixp4xx_irq_chunks[] = { + { + .irq = 16, + .hwirq = 0, + .nr_irqs = 6, + }, + { + .irq = 24, + .hwirq = 8, + .nr_irqs = 11, + }, + { + .irq = 46, + .hwirq = 30, + .nr_irqs = 2, + }, + /* Only on the 436 variants */ + { + .irq = 48, + .hwirq = 32, + .nr_irqs = 10, + }, +}; + +/** + * ixp4x_irq_setup() - Common setup code for the IXP4xx interrupt controller + * @ixi: State container + * @irqbase: Virtual memory base for the interrupt controller + * @fwnode: Corresponding fwnode abstraction for this controller + * @is_356: if this is an IXP43x, IXP45x or IXP46x SoC variant + */ +static int ixp4xx_irq_setup(struct ixp4xx_irq *ixi, + void __iomem *irqbase, + struct fwnode_handle *fwnode, + bool is_356) +{ + int nr_irqs; + + ixi->irqbase = irqbase; + ixi->is_356 = is_356; + + /* Route all sources to IRQ instead of FIQ */ + __raw_writel(0x0, ixi->irqbase + IXP4XX_ICLR); + + /* Disable all interrupts */ + __raw_writel(0x0, ixi->irqbase + IXP4XX_ICMR); + + if (is_356) { + /* Route upper 32 sources to IRQ instead of FIQ */ + __raw_writel(0x0, ixi->irqbase + IXP4XX_ICLR2); + + /* Disable upper 32 interrupts */ + __raw_writel(0x0, ixi->irqbase + IXP4XX_ICMR2); + + nr_irqs = 64; + } else { + nr_irqs = 32; + } + + ixi->irqchip.name = "IXP4xx"; + ixi->irqchip.irq_mask = ixp4xx_irq_mask; + ixi->irqchip.irq_unmask = ixp4xx_irq_unmask; + ixi->irqchip.irq_set_type = ixp4xx_set_irq_type; + + ixi->domain = irq_domain_create_linear(fwnode, nr_irqs, + &ixp4xx_irqdomain_ops, + ixi); + if (!ixi->domain) { + pr_crit("IXP4XX: can not add primary irqdomain\n"); + return -ENODEV; + } + + set_handle_irq(ixp4xx_handle_irq); + + return 0; +} + +/** + * ixp4xx_irq_init() - Function to initialize the irqchip from boardfiles + * @irqbase: physical base for the irq controller + * @is_356: if this is an IXP43x, IXP45x or IXP46x SoC variant + */ +void __init ixp4xx_irq_init(resource_size_t irqbase, + bool is_356) +{ + struct ixp4xx_irq *ixi = &ixirq; + void __iomem *base; + struct fwnode_handle *fwnode; + struct irq_fwspec fwspec; + int nr_chunks; + int ret; + int i; + + base = ioremap(irqbase, 0x100); + if (!base) { + pr_crit("IXP4XX: could not ioremap interrupt controller\n"); + return; + } + fwnode = irq_domain_alloc_fwnode(base); + if (!fwnode) { + pr_crit("IXP4XX: no domain handle\n"); + return; + } + ret = ixp4xx_irq_setup(ixi, base, fwnode, is_356); + if (ret) { + pr_crit("IXP4XX: failed to set up irqchip\n"); + irq_domain_free_fwnode(fwnode); + } + + nr_chunks = ARRAY_SIZE(ixp4xx_irq_chunks); + if (!is_356) + nr_chunks--; + + /* + * After adding OF support, this is no longer needed: irqs + * will be allocated for the respective fwnodes. + */ + for (i = 0; i < nr_chunks; i++) { + const struct ixp4xx_irq_chunk *chunk = &ixp4xx_irq_chunks[i]; + + pr_info("Allocate Linux IRQs %d..%d HW IRQs %d..%d\n", + chunk->irq, chunk->irq + chunk->nr_irqs - 1, + chunk->hwirq, chunk->hwirq + chunk->nr_irqs - 1); + fwspec.fwnode = fwnode; + fwspec.param[0] = chunk->hwirq; + fwspec.param[1] = IRQ_TYPE_LEVEL_HIGH; + fwspec.param_count = 2; + ret = __irq_domain_alloc_irqs(ixi->domain, + chunk->irq, + chunk->nr_irqs, + NUMA_NO_NODE, + &fwspec, + false, + NULL); + if (ret < 0) { + pr_crit("IXP4XX: can not allocate irqs in hierarchy %d\n", + ret); + return; + } + } +} +EXPORT_SYMBOL_GPL(ixp4xx_irq_init); + +#ifdef CONFIG_OF +int __init ixp4xx_of_init_irq(struct device_node *np, + struct device_node *parent) +{ + struct ixp4xx_irq *ixi = &ixirq; + void __iomem *base; + struct fwnode_handle *fwnode; + bool is_356; + int ret; + + base = of_iomap(np, 0); + if (!base) { + pr_crit("IXP4XX: could not ioremap interrupt controller\n"); + return -ENODEV; + } + fwnode = of_node_to_fwnode(np); + + /* These chip variants have 64 interrupts */ + is_356 = of_device_is_compatible(np, "intel,ixp43x-interrupt") || + of_device_is_compatible(np, "intel,ixp45x-interrupt") || + of_device_is_compatible(np, "intel,ixp46x-interrupt"); + + ret = ixp4xx_irq_setup(ixi, base, fwnode, is_356); + if (ret) + pr_crit("IXP4XX: failed to set up irqchip\n"); + + return ret; +} +IRQCHIP_DECLARE(ixp42x, "intel,ixp42x-interrupt", + ixp4xx_of_init_irq); +IRQCHIP_DECLARE(ixp43x, "intel,ixp43x-interrupt", + ixp4xx_of_init_irq); +IRQCHIP_DECLARE(ixp45x, "intel,ixp45x-interrupt", + ixp4xx_of_init_irq); +IRQCHIP_DECLARE(ixp46x, "intel,ixp46x-interrupt", + ixp4xx_of_init_irq); +#endif diff --git a/drivers/net/ethernet/xscale/ixp4xx_eth.c b/drivers/net/ethernet/xscale/ixp4xx_eth.c index ed6623a9801e..319db3ece263 100644 --- a/drivers/net/ethernet/xscale/ixp4xx_eth.c +++ b/drivers/net/ethernet/xscale/ixp4xx_eth.c @@ -31,14 +31,15 @@ #include <linux/io.h> #include <linux/kernel.h> #include <linux/net_tstamp.h> +#include <linux/of.h> #include <linux/phy.h> #include <linux/platform_device.h> #include <linux/ptp_classify.h> #include <linux/slab.h> #include <linux/module.h> #include <mach/ixp46x_ts.h> -#include <mach/npe.h> -#include <mach/qmgr.h> +#include <linux/soc/ixp4xx/npe.h> +#include <linux/soc/ixp4xx/qmgr.h> #define DEBUG_DESC 0 #define DEBUG_RX 0 @@ -1497,6 +1498,15 @@ static struct platform_driver ixp4xx_eth_driver = { static int __init eth_init_module(void) { int err; + + /* + * FIXME: we bail out on device tree boot but this really needs + * to be fixed in a nicer way: this registers the MDIO bus before + * even matching the driver infrastructure, we should only probe + * detected hardware. + */ + if (of_have_populated_dt()) + return -ENODEV; if ((err = ixp4xx_mdio_register())) return err; return platform_driver_register(&ixp4xx_eth_driver); diff --git a/drivers/net/wan/ixp4xx_hss.c b/drivers/net/wan/ixp4xx_hss.c index 5c60dc60a8e6..46a05b6540b8 100644 --- a/drivers/net/wan/ixp4xx_hss.c +++ b/drivers/net/wan/ixp4xx_hss.c @@ -22,8 +22,8 @@ #include <linux/platform_device.h> #include <linux/poll.h> #include <linux/slab.h> -#include <mach/npe.h> -#include <mach/qmgr.h> +#include <linux/soc/ixp4xx/npe.h> +#include <linux/soc/ixp4xx/qmgr.h> #define DEBUG_DESC 0 #define DEBUG_RX 0 diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig index c07b4a85253f..ae9bf20b26fa 100644 --- a/drivers/soc/Kconfig +++ b/drivers/soc/Kconfig @@ -6,6 +6,7 @@ source "drivers/soc/atmel/Kconfig" source "drivers/soc/bcm/Kconfig" source "drivers/soc/fsl/Kconfig" source "drivers/soc/imx/Kconfig" +source "drivers/soc/ixp4xx/Kconfig" source "drivers/soc/mediatek/Kconfig" source "drivers/soc/qcom/Kconfig" source "drivers/soc/renesas/Kconfig" diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index 90b686e586c6..c7c1a139ad8d 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_MACH_DOVE) += dove/ obj-y += fsl/ obj-$(CONFIG_ARCH_GEMINI) += gemini/ obj-$(CONFIG_ARCH_MXC) += imx/ +obj-$(CONFIG_ARCH_IXP4XX) += ixp4xx/ obj-$(CONFIG_SOC_XWAY) += lantiq/ obj-y += mediatek/ obj-y += amlogic/ diff --git a/drivers/soc/ixp4xx/Kconfig b/drivers/soc/ixp4xx/Kconfig new file mode 100644 index 000000000000..de6becdc78a2 --- /dev/null +++ b/drivers/soc/ixp4xx/Kconfig @@ -0,0 +1,16 @@ +menu "IXP4xx SoC drivers" + +config IXP4XX_QMGR + tristate "IXP4xx Queue Manager support" + help + This driver supports IXP4xx built-in hardware queue manager + and is automatically selected by Ethernet and HSS drivers. + +config IXP4XX_NPE + tristate "IXP4xx Network Processor Engine support" + select FW_LOADER + help + This driver supports IXP4xx built-in network coprocessors + and is automatically selected by Ethernet and HSS drivers. + +endmenu diff --git a/drivers/soc/ixp4xx/Makefile b/drivers/soc/ixp4xx/Makefile new file mode 100644 index 000000000000..d20d99e6df65 --- /dev/null +++ b/drivers/soc/ixp4xx/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_IXP4XX_QMGR) += ixp4xx-qmgr.o +obj-$(CONFIG_IXP4XX_NPE) += ixp4xx-npe.o diff --git a/arch/arm/mach-ixp4xx/ixp4xx_npe.c b/drivers/soc/ixp4xx/ixp4xx-npe.c index d4eb09a62863..15979d4376ab 100644 --- a/arch/arm/mach-ixp4xx/ixp4xx_npe.c +++ b/drivers/soc/ixp4xx/ixp4xx-npe.c @@ -20,7 +20,9 @@ #include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> -#include <mach/npe.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/soc/ixp4xx/npe.h> #define DEBUG_MSG 0 #define DEBUG_FW 0 @@ -153,16 +155,10 @@ static struct { static struct npe npe_tab[NPE_COUNT] = { { .id = 0, - .regs = (struct npe_regs __iomem *)IXP4XX_NPEA_BASE_VIRT, - .regs_phys = IXP4XX_NPEA_BASE_PHYS, }, { .id = 1, - .regs = (struct npe_regs __iomem *)IXP4XX_NPEB_BASE_VIRT, - .regs_phys = IXP4XX_NPEB_BASE_PHYS, }, { .id = 2, - .regs = (struct npe_regs __iomem *)IXP4XX_NPEC_BASE_VIRT, - .regs_phys = IXP4XX_NPEC_BASE_PHYS, } }; @@ -682,28 +678,37 @@ void npe_release(struct npe *npe) module_put(THIS_MODULE); } - -static int __init npe_init_module(void) +static int ixp4xx_npe_probe(struct platform_device *pdev) { - int i, found = 0; + struct device *dev = &pdev->dev; + struct resource *res; for (i = 0; i < NPE_COUNT; i++) { struct npe *npe = &npe_tab[i]; + + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (!res) + return -ENODEV; + if (!(ixp4xx_read_feature_bits() & - (IXP4XX_FEATURE_RESET_NPEA << i))) + (IXP4XX_FEATURE_RESET_NPEA << i))) { + dev_info(dev, "NPE%d at 0x%08x-0x%08x not available\n", + i, res->start, res->end); continue; /* NPE already disabled or not present */ - if (!(npe->mem_res = request_mem_region(npe->regs_phys, - REGS_SIZE, - npe_name(npe)))) { - print_npe(KERN_ERR, npe, - "failed to request memory region\n"); - continue; } + npe->regs = devm_ioremap_resource(dev, res); + if (!npe->regs) + return -ENOMEM; - if (npe_reset(npe)) + if (npe_reset(npe)) { + dev_info(dev, "NPE%d at 0x%08x-0x%08x does not reset\n", + i, res->start, res->end); continue; + } npe->valid = 1; + dev_info(dev, "NPE%d at 0x%08x-0x%08x registered\n", + i, res->start, res->end); found++; } @@ -712,19 +717,34 @@ static int __init npe_init_module(void) return 0; } -static void __exit npe_cleanup_module(void) +static int ixp4xx_npe_remove(struct platform_device *pdev) { int i; for (i = 0; i < NPE_COUNT; i++) - if (npe_tab[i].mem_res) { + if (npe_tab[i].regs) { npe_reset(&npe_tab[i]); - release_resource(npe_tab[i].mem_res); } + + return 0; } -module_init(npe_init_module); -module_exit(npe_cleanup_module); +static const struct of_device_id ixp4xx_npe_of_match[] = { + { + .compatible = "intel,ixp4xx-network-processing-engine", + }, + {}, +}; + +static struct platform_driver ixp4xx_npe_driver = { + .driver = { + .name = "ixp4xx-npe", + .of_match_table = of_match_ptr(ixp4xx_npe_of_match), + }, + .probe = ixp4xx_npe_probe, + .remove = ixp4xx_npe_remove, +}; +module_platform_driver(ixp4xx_npe_driver); MODULE_AUTHOR("Krzysztof Halasa"); MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-ixp4xx/ixp4xx_qmgr.c b/drivers/soc/ixp4xx/ixp4xx-qmgr.c index 9d1b6b7c394c..13a8a13c9b01 100644 --- a/arch/arm/mach-ixp4xx/ixp4xx_qmgr.c +++ b/drivers/soc/ixp4xx/ixp4xx-qmgr.c @@ -12,10 +12,13 @@ #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/module.h> -#include <mach/qmgr.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/soc/ixp4xx/qmgr.h> -static struct qmgr_regs __iomem *qmgr_regs = IXP4XX_QMGR_BASE_VIRT; -static struct resource *mem_res; +static struct qmgr_regs __iomem *qmgr_regs; +static int qmgr_irq_1; +static int qmgr_irq_2; static spinlock_t qmgr_lock; static u32 used_sram_bitmap[4]; /* 128 16-dword pages */ static void (*irq_handlers[QUEUES])(void *pdev); @@ -25,6 +28,94 @@ static void *irq_pdevs[QUEUES]; char qmgr_queue_descs[QUEUES][32]; #endif +void qmgr_put_entry(unsigned int queue, u32 val) +{ +#if DEBUG_QMGR + BUG_ON(!qmgr_queue_descs[queue]); /* not yet requested */ + + printk(KERN_DEBUG "Queue %s(%i) put %X\n", + qmgr_queue_descs[queue], queue, val); +#endif + __raw_writel(val, &qmgr_regs->acc[queue][0]); +} + +u32 qmgr_get_entry(unsigned int queue) +{ + u32 val; + val = __raw_readl(&qmgr_regs->acc[queue][0]); +#if DEBUG_QMGR + BUG_ON(!qmgr_queue_descs[queue]); /* not yet requested */ + + printk(KERN_DEBUG "Queue %s(%i) get %X\n", + qmgr_queue_descs[queue], queue, val); +#endif + return val; +} + +static int __qmgr_get_stat1(unsigned int queue) +{ + return (__raw_readl(&qmgr_regs->stat1[queue >> 3]) + >> ((queue & 7) << 2)) & 0xF; +} + +static int __qmgr_get_stat2(unsigned int queue) +{ + BUG_ON(queue >= HALF_QUEUES); + return (__raw_readl(&qmgr_regs->stat2[queue >> 4]) + >> ((queue & 0xF) << 1)) & 0x3; +} + +/** + * qmgr_stat_empty() - checks if a hardware queue is empty + * @queue: queue number + * + * Returns non-zero value if the queue is empty. + */ +int qmgr_stat_empty(unsigned int queue) +{ + BUG_ON(queue >= HALF_QUEUES); + return __qmgr_get_stat1(queue) & QUEUE_STAT1_EMPTY; +} + +/** + * qmgr_stat_below_low_watermark() - checks if a queue is below low watermark + * @queue: queue number + * + * Returns non-zero value if the queue is below low watermark. + */ +int qmgr_stat_below_low_watermark(unsigned int queue) +{ + if (queue >= HALF_QUEUES) + return (__raw_readl(&qmgr_regs->statne_h) >> + (queue - HALF_QUEUES)) & 0x01; + return __qmgr_get_stat1(queue) & QUEUE_STAT1_NEARLY_EMPTY; +} + +/** + * qmgr_stat_full() - checks if a hardware queue is full + * @queue: queue number + * + * Returns non-zero value if the queue is full. + */ +int qmgr_stat_full(unsigned int queue) +{ + if (queue >= HALF_QUEUES) + return (__raw_readl(&qmgr_regs->statf_h) >> + (queue - HALF_QUEUES)) & 0x01; + return __qmgr_get_stat1(queue) & QUEUE_STAT1_FULL; +} + +/** + * qmgr_stat_overflow() - checks if a hardware queue experienced overflow + * @queue: queue number + * + * Returns non-zero value if the queue experienced overflow. + */ +int qmgr_stat_overflow(unsigned int queue) +{ + return __qmgr_get_stat2(queue) & QUEUE_STAT2_OVERFLOW; +} + void qmgr_set_irq(unsigned int queue, int src, void (*handler)(void *pdev), void *pdev) { @@ -95,7 +186,7 @@ static irqreturn_t qmgr_irq2_a0(int irq, void *pdev) static irqreturn_t qmgr_irq(int irq, void *pdev) { - int i, half = (irq == IRQ_IXP4XX_QM1 ? 0 : 1); + int i, half = (irq == qmgr_irq_1 ? 0 : 1); u32 req_bitmap = __raw_readl(&qmgr_regs->irqstat[half]); if (!req_bitmap) @@ -282,16 +373,29 @@ void qmgr_release_queue(unsigned int queue) module_put(THIS_MODULE); } -static int qmgr_init(void) +static int ixp4xx_qmgr_probe(struct platform_device *pdev) { int i, err; irq_handler_t handler1, handler2; + struct device *dev = &pdev->dev; + struct resource *res; + int irq1, irq2; - mem_res = request_mem_region(IXP4XX_QMGR_BASE_PHYS, - IXP4XX_QMGR_REGION_SIZE, - "IXP4xx Queue Manager"); - if (mem_res == NULL) - return -EBUSY; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + qmgr_regs = devm_ioremap_resource(dev, res); + if (!qmgr_regs) + return -ENOMEM; + + irq1 = platform_get_irq(pdev, 0); + if (irq1 <= 0) + return irq1 ? irq1 : -EINVAL; + qmgr_irq_1 = irq1; + irq2 = platform_get_irq(pdev, 1); + if (irq2 <= 0) + return irq2 ? irq2 : -EINVAL; + qmgr_irq_2 = irq2; /* reset qmgr registers */ for (i = 0; i < 4; i++) { @@ -316,50 +420,62 @@ static int qmgr_init(void) } else handler1 = handler2 = qmgr_irq; - err = request_irq(IRQ_IXP4XX_QM1, handler1, 0, "IXP4xx Queue Manager", - NULL); + err = devm_request_irq(dev, irq1, handler1, 0, "IXP4xx Queue Manager", + NULL); if (err) { - printk(KERN_ERR "qmgr: failed to request IRQ%i (%i)\n", - IRQ_IXP4XX_QM1, err); - goto error_irq; + dev_err(dev, "failed to request IRQ%i (%i)\n", + irq1, err); + return err; } - err = request_irq(IRQ_IXP4XX_QM2, handler2, 0, "IXP4xx Queue Manager", - NULL); + err = devm_request_irq(dev, irq2, handler2, 0, "IXP4xx Queue Manager", + NULL); if (err) { - printk(KERN_ERR "qmgr: failed to request IRQ%i (%i)\n", - IRQ_IXP4XX_QM2, err); - goto error_irq2; + dev_err(dev, "failed to request IRQ%i (%i)\n", + irq2, err); + return err; } used_sram_bitmap[0] = 0xF; /* 4 first pages reserved for config */ spin_lock_init(&qmgr_lock); - printk(KERN_INFO "IXP4xx Queue Manager initialized.\n"); + dev_info(dev, "IXP4xx Queue Manager initialized.\n"); return 0; - -error_irq2: - free_irq(IRQ_IXP4XX_QM1, NULL); -error_irq: - release_mem_region(IXP4XX_QMGR_BASE_PHYS, IXP4XX_QMGR_REGION_SIZE); - return err; } -static void qmgr_remove(void) +static int ixp4xx_qmgr_remove(struct platform_device *pdev) { - free_irq(IRQ_IXP4XX_QM1, NULL); - free_irq(IRQ_IXP4XX_QM2, NULL); - synchronize_irq(IRQ_IXP4XX_QM1); - synchronize_irq(IRQ_IXP4XX_QM2); - release_mem_region(IXP4XX_QMGR_BASE_PHYS, IXP4XX_QMGR_REGION_SIZE); + synchronize_irq(qmgr_irq_1); + synchronize_irq(qmgr_irq_2); + return 0; } -module_init(qmgr_init); -module_exit(qmgr_remove); +static const struct of_device_id ixp4xx_qmgr_of_match[] = { + { + .compatible = "intel,ixp4xx-ahb-queue-manager", + }, + {}, +}; + +static struct platform_driver ixp4xx_qmgr_driver = { + .driver = { + .name = "ixp4xx-qmgr", + .of_match_table = of_match_ptr(ixp4xx_qmgr_of_match), + }, + .probe = ixp4xx_qmgr_probe, + .remove = ixp4xx_qmgr_remove, +}; +module_platform_driver(ixp4xx_qmgr_driver); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Krzysztof Halasa"); +EXPORT_SYMBOL(qmgr_put_entry); +EXPORT_SYMBOL(qmgr_get_entry); +EXPORT_SYMBOL(qmgr_stat_empty); +EXPORT_SYMBOL(qmgr_stat_below_low_watermark); +EXPORT_SYMBOL(qmgr_stat_full); +EXPORT_SYMBOL(qmgr_stat_overflow); EXPORT_SYMBOL(qmgr_set_irq); EXPORT_SYMBOL(qmgr_enable_irq); EXPORT_SYMBOL(qmgr_disable_irq); diff --git a/drivers/watchdog/ixp4xx_wdt.c b/drivers/watchdog/ixp4xx_wdt.c index f20cc53ff719..a80449bb36f0 100644 --- a/drivers/watchdog/ixp4xx_wdt.c +++ b/drivers/watchdog/ixp4xx_wdt.c @@ -21,6 +21,7 @@ #include <linux/kernel.h> #include <linux/fs.h> #include <linux/miscdevice.h> +#include <linux/of.h> #include <linux/watchdog.h> #include <linux/init.h> #include <linux/bitops.h> @@ -176,6 +177,14 @@ static int __init ixp4xx_wdt_init(void) { int ret; + /* + * FIXME: we bail out on device tree boot but this really needs + * to be fixed in a nicer way: this registers the MDIO bus before + * even matching the driver infrastructure, we should only probe + * detected hardware. + */ + if (of_have_populated_dt()) + return -ENODEV; if (!(read_cpuid_id() & 0xf) && !cpu_is_ixp46x()) { pr_err("Rev. A0 IXP42x CPU detected - watchdog disabled\n"); diff --git a/include/linux/irqchip/irq-ixp4xx.h b/include/linux/irqchip/irq-ixp4xx.h new file mode 100644 index 000000000000..9395917d6936 --- /dev/null +++ b/include/linux/irqchip/irq-ixp4xx.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __IRQ_IXP4XX_H +#define __IRQ_IXP4XX_H + +#include <linux/ioport.h> +struct irq_domain; + +void ixp4xx_irq_init(resource_size_t irqbase, + bool is_356); +struct irq_domain *ixp4xx_get_irq_domain(void); + +#endif /* __IRQ_IXP4XX_H */ diff --git a/include/linux/platform_data/timer-ixp4xx.h b/include/linux/platform_data/timer-ixp4xx.h new file mode 100644 index 000000000000..ee92ae7edaed --- /dev/null +++ b/include/linux/platform_data/timer-ixp4xx.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __TIMER_IXP4XX_H +#define __TIMER_IXP4XX_H + +#include <linux/ioport.h> + +void __init ixp4xx_timer_setup(resource_size_t timerbase, + int timer_irq, + unsigned int timer_freq); + +#endif diff --git a/arch/arm/mach-ixp4xx/include/mach/npe.h b/include/linux/soc/ixp4xx/npe.h index 3a980845e557..2a91f465d456 100644 --- a/arch/arm/mach-ixp4xx/include/mach/npe.h +++ b/include/linux/soc/ixp4xx/npe.h @@ -16,9 +16,7 @@ struct npe_regs { }; struct npe { - struct resource *mem_res; struct npe_regs __iomem *regs; - u32 regs_phys; int id; int valid; }; diff --git a/include/linux/soc/ixp4xx/qmgr.h b/include/linux/soc/ixp4xx/qmgr.h new file mode 100644 index 000000000000..bed8ee94fa57 --- /dev/null +++ b/include/linux/soc/ixp4xx/qmgr.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2007 Krzysztof Halasa <khc@pm.waw.pl> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + */ + +#ifndef IXP4XX_QMGR_H +#define IXP4XX_QMGR_H + +#include <linux/io.h> +#include <linux/kernel.h> + +#define DEBUG_QMGR 0 + +#define HALF_QUEUES 32 +#define QUEUES 64 +#define MAX_QUEUE_LENGTH 4 /* in dwords */ + +#define QUEUE_STAT1_EMPTY 1 /* queue status bits */ +#define QUEUE_STAT1_NEARLY_EMPTY 2 +#define QUEUE_STAT1_NEARLY_FULL 4 +#define QUEUE_STAT1_FULL 8 +#define QUEUE_STAT2_UNDERFLOW 1 +#define QUEUE_STAT2_OVERFLOW 2 + +#define QUEUE_WATERMARK_0_ENTRIES 0 +#define QUEUE_WATERMARK_1_ENTRY 1 +#define QUEUE_WATERMARK_2_ENTRIES 2 +#define QUEUE_WATERMARK_4_ENTRIES 3 +#define QUEUE_WATERMARK_8_ENTRIES 4 +#define QUEUE_WATERMARK_16_ENTRIES 5 +#define QUEUE_WATERMARK_32_ENTRIES 6 +#define QUEUE_WATERMARK_64_ENTRIES 7 + +/* queue interrupt request conditions */ +#define QUEUE_IRQ_SRC_EMPTY 0 +#define QUEUE_IRQ_SRC_NEARLY_EMPTY 1 +#define QUEUE_IRQ_SRC_NEARLY_FULL 2 +#define QUEUE_IRQ_SRC_FULL 3 +#define QUEUE_IRQ_SRC_NOT_EMPTY 4 +#define QUEUE_IRQ_SRC_NOT_NEARLY_EMPTY 5 +#define QUEUE_IRQ_SRC_NOT_NEARLY_FULL 6 +#define QUEUE_IRQ_SRC_NOT_FULL 7 + +struct qmgr_regs { + u32 acc[QUEUES][MAX_QUEUE_LENGTH]; /* 0x000 - 0x3FF */ + u32 stat1[4]; /* 0x400 - 0x40F */ + u32 stat2[2]; /* 0x410 - 0x417 */ + u32 statne_h; /* 0x418 - queue nearly empty */ + u32 statf_h; /* 0x41C - queue full */ + u32 irqsrc[4]; /* 0x420 - 0x42F IRC source */ + u32 irqen[2]; /* 0x430 - 0x437 IRQ enabled */ + u32 irqstat[2]; /* 0x438 - 0x43F - IRQ access only */ + u32 reserved[1776]; + u32 sram[2048]; /* 0x2000 - 0x3FFF - config and buffer */ +}; + +void qmgr_put_entry(unsigned int queue, u32 val); +u32 qmgr_get_entry(unsigned int queue); +int qmgr_stat_empty(unsigned int queue); +int qmgr_stat_below_low_watermark(unsigned int queue); +int qmgr_stat_full(unsigned int queue); +int qmgr_stat_overflow(unsigned int queue); +void qmgr_release_queue(unsigned int queue); +void qmgr_set_irq(unsigned int queue, int src, + void (*handler)(void *pdev), void *pdev); +void qmgr_enable_irq(unsigned int queue); +void qmgr_disable_irq(unsigned int queue); + +/* request_ and release_queue() must be called from non-IRQ context */ + +#if DEBUG_QMGR +extern char qmgr_queue_descs[QUEUES][32]; + +int qmgr_request_queue(unsigned int queue, unsigned int len /* dwords */, + unsigned int nearly_empty_watermark, + unsigned int nearly_full_watermark, + const char *desc_format, const char* name); +#else +int __qmgr_request_queue(unsigned int queue, unsigned int len /* dwords */, + unsigned int nearly_empty_watermark, + unsigned int nearly_full_watermark); +#define qmgr_request_queue(queue, len, nearly_empty_watermark, \ + nearly_full_watermark, desc_format, name) \ + __qmgr_request_queue(queue, len, nearly_empty_watermark, \ + nearly_full_watermark) +#endif + +#endif |