diff options
author | Olof Johansson <olof@lixom.net> | 2021-06-23 18:47:38 -0700 |
---|---|---|
committer | Olof Johansson <olof@lixom.net> | 2021-06-23 18:47:40 -0700 |
commit | 95ef7152366382619e99c09a2c59a341f01ea29e (patch) | |
tree | 62b386e9f3c83675d404db0786f23c1a978b7f35 | |
parent | 6b4670af9f49c76f9be7f3ef2d1654dfee04a4de (diff) | |
parent | 0da518dae87dd18e5f23e406e15575f393e4a97c (diff) | |
download | linux-95ef7152366382619e99c09a2c59a341f01ea29e.tar.bz2 |
Merge tag 'ixp4xx-arm-soc-v5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-nomadik into arm/soc
This is a major chunk of IXP4xx modernization:
- Fist we move some registers around to make room for
the predetermined PCI I/O space.
- Then we add some Kconfig options to make it possible
to use the old PCI driver in parallell with the new
shiny one.
- Then we add the new PCI driver and some bindings for
it.
- On top of this we add an (ages old) patch from Arnd
that centralize the CPU/SoC detection in drivers/soc
and make the header a standard Linux header to avoid
the <mach/*> business in drivers.
- Then we split out and modernize some platform data
headers for pata, and hwrandom, and top it up with
DT bindings and support for hwrandom.
* tag 'ixp4xx-arm-soc-v5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-nomadik:
ixp4xx: fix spelling mistake in Kconfig "Devce" -> "Device"
hw_random: ixp4xx: Add OF support
hw_random: ixp4xx: Add DT bindings
hw_random: ixp4xx: Turn into a module
hw_random: ixp4xx: Use SPDX license tag
hw_random: ixp4xx: enable compile-testing
pata: ixp4xx: split platform data to its own header
soc: ixp4xx: move cpu detection to linux/soc/ixp4xx/cpu.h
PCI: ixp4xx: Add a new driver for IXP4xx
PCI: ixp4xx: Add device tree bindings for IXP4xx
ARM/ixp4xx: Make NEED_MACH_IO_H optional
ARM/ixp4xx: Move the virtual IObases
Link: https://lore.kernel.org/r/CACRpkdbw6HSpp7k6q1FYGmtafLmdAu8bFnpHQOdfBDYYsdLbkw@mail.gmail.com
Signed-off-by: Olof Johansson <olof@lixom.net>
31 files changed, 1079 insertions, 175 deletions
diff --git a/Documentation/devicetree/bindings/pci/intel,ixp4xx-pci.yaml b/Documentation/devicetree/bindings/pci/intel,ixp4xx-pci.yaml new file mode 100644 index 000000000000..debfb54a8042 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/intel,ixp4xx-pci.yaml @@ -0,0 +1,100 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/intel,ixp4xx-pci.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Intel IXP4xx PCI controller + +maintainers: + - Linus Walleij <linus.walleij@linaro.org> + +description: PCI host controller found in the Intel IXP4xx SoC series. + +allOf: + - $ref: /schemas/pci/pci-bus.yaml# + +properties: + compatible: + items: + - enum: + - intel,ixp42x-pci + - intel,ixp43x-pci + description: The two supported variants are ixp42x and ixp43x, + though more variants may exist. + + reg: + items: + - description: IXP4xx-specific registers + + interrupts: + items: + - description: Main PCI interrupt + - description: PCI DMA interrupt 1 + - description: PCI DMA interrupt 2 + + ranges: + maxItems: 2 + description: Typically one memory range of 64MB and one IO + space range of 64KB. + + dma-ranges: + maxItems: 1 + description: The DMA range tells the PCI host which addresses + the RAM is at. It can map only 64MB so if the RAM is bigger + than 64MB the DMA access has to be restricted to these + addresses. + + "#interrupt-cells": true + + interrupt-map: true + + interrupt-map-mask: + items: + - const: 0xf800 + - const: 0 + - const: 0 + - const: 7 + +required: + - compatible + - reg + - dma-ranges + - "#interrupt-cells" + - interrupt-map + - interrupt-map-mask + +unevaluatedProperties: false + +examples: + - | + pci@c0000000 { + compatible = "intel,ixp43x-pci"; + reg = <0xc0000000 0x1000>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + bus-range = <0x00 0xff>; + + ranges = + <0x02000000 0 0x48000000 0x48000000 0 0x04000000>, + <0x01000000 0 0x00000000 0x4c000000 0 0x00010000>; + dma-ranges = + <0x02000000 0 0x00000000 0x00000000 0 0x04000000>; + + #interrupt-cells = <1>; + interrupt-map-mask = <0xf800 0 0 7>; + interrupt-map = + <0x0800 0 0 1 &gpio0 11 3>, /* INT A on slot 1 is irq 11 */ + <0x0800 0 0 2 &gpio0 10 3>, /* INT B on slot 1 is irq 10 */ + <0x0800 0 0 3 &gpio0 9 3>, /* INT C on slot 1 is irq 9 */ + <0x0800 0 0 4 &gpio0 8 3>, /* INT D on slot 1 is irq 8 */ + <0x1000 0 0 1 &gpio0 10 3>, /* INT A on slot 2 is irq 10 */ + <0x1000 0 0 2 &gpio0 9 3>, /* INT B on slot 2 is irq 9 */ + <0x1000 0 0 3 &gpio0 8 3>, /* INT C on slot 2 is irq 8 */ + <0x1000 0 0 4 &gpio0 11 3>, /* INT D on slot 2 is irq 11 */ + <0x1800 0 0 1 &gpio0 9 3>, /* INT A on slot 3 is irq 9 */ + <0x1800 0 0 2 &gpio0 8 3>, /* INT B on slot 3 is irq 8 */ + <0x1800 0 0 3 &gpio0 11 3>, /* INT C on slot 3 is irq 11 */ + <0x1800 0 0 4 &gpio0 10 3>; /* INT D on slot 3 is irq 10 */ + }; diff --git a/Documentation/devicetree/bindings/rng/intel,ixp46x-rng.yaml b/Documentation/devicetree/bindings/rng/intel,ixp46x-rng.yaml new file mode 100644 index 000000000000..61963fa9347e --- /dev/null +++ b/Documentation/devicetree/bindings/rng/intel,ixp46x-rng.yaml @@ -0,0 +1,36 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/rng/intel,ixp46x-rng.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Intel IXP46x RNG bindings + +description: | + The Intel IXP46x has a random number generator at a fixed physical + location in memory. Each read is guaranteed to provide a unique + 32 bit random number. + +maintainers: + - Linus Walleij <linus.walleij@linaro.org> + +properties: + compatible: + const: intel,ixp46x-rng + + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + rng@70002100 { + compatible = "intel,ixp46x-rng"; + reg = <0x70002100 4>; + }; + diff --git a/MAINTAINERS b/MAINTAINERS index 10816e58134c..a085be183b68 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9252,6 +9252,7 @@ F: include/linux/soc/ixp4xx/qmgr.h INTEL IXP4XX RANDOM NUMBER GENERATOR SUPPORT M: Deepak Saxena <dsaxena@plexity.net> S: Maintained +F: Documentation/devicetree/bindings/display/intel,ixp46x-rng.yaml F: drivers/char/hw_random/ixp4xx-rng.c INTEL KEEM BAY DRM DRIVER @@ -14015,6 +14016,12 @@ S: Maintained F: Documentation/devicetree/bindings/pci/sifive,fu740-pcie.yaml F: drivers/pci/controller/dwc/pcie-fu740.c +PCI DRIVER FOR INTEL IXP4XX +M: Linus Walleij <linus.walleij@linaro.org> +S: Maintained +F: Documentation/devicetree/bindings/pci/intel,ixp4xx-pci.yaml +F: drivers/pci/controller/pci-ixp4xx.c + PCI DRIVER FOR INTEL VOLUME MANAGEMENT DEVICE (VMD) M: Jonathan Derrick <jonathan.derrick@intel.com> L: linux-pci@vger.kernel.org diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 24804f11302d..819f0d08401e 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -398,7 +398,8 @@ config ARCH_IXP4XX select HAVE_PCI select IXP4XX_IRQ select IXP4XX_TIMER - select NEED_MACH_IO_H + # With the new PCI driver this is not needed + select NEED_MACH_IO_H if PCI_IXP4XX_LEGACY select USB_EHCI_BIG_ENDIAN_DESC select USB_EHCI_BIG_ENDIAN_MMIO help diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug index 1c673762ce7c..ba8514468df8 100644 --- a/arch/arm/Kconfig.debug +++ b/arch/arm/Kconfig.debug @@ -1813,8 +1813,8 @@ config DEBUG_UART_VIRT default 0xfedc0000 if DEBUG_EP93XX default 0xfee003f8 if DEBUG_FOOTBRIDGE_COM1 default 0xfee20000 if DEBUG_NSPIRE_CLASSIC_UART || DEBUG_NSPIRE_CX_UART - default 0xfef00000 if ARCH_IXP4XX && !CPU_BIG_ENDIAN - default 0xfef00003 if ARCH_IXP4XX && CPU_BIG_ENDIAN + default 0xfec00000 if ARCH_IXP4XX && !CPU_BIG_ENDIAN + default 0xfec00003 if ARCH_IXP4XX && CPU_BIG_ENDIAN default 0xfef36000 if DEBUG_HIGHBANK_UART default 0xfefb0000 if DEBUG_OMAP1UART1 || DEBUG_OMAP7XXUART1 default 0xfefb0800 if DEBUG_OMAP1UART2 || DEBUG_OMAP7XXUART2 diff --git a/arch/arm/mach-ixp4xx/Kconfig b/arch/arm/mach-ixp4xx/Kconfig index 165c184801e1..bf14d65120b9 100644 --- a/arch/arm/mach-ixp4xx/Kconfig +++ b/arch/arm/mach-ixp4xx/Kconfig @@ -7,7 +7,7 @@ comment "IXP4xx Platforms" config MACH_IXP4XX_OF bool - prompt "Devce Tree IXP4xx boards" + prompt "Device Tree IXP4xx boards" default y select ARM_APPENDED_DTB # Old Redboot bootloaders deployed select I2C @@ -20,7 +20,7 @@ config MACH_IXP4XX_OF config MACH_NSLU2 bool prompt "Linksys NSLU2" - select FORCE_PCI + depends on IXP4XX_PCI_LEGACY help Say 'Y' here if you want your kernel to support Linksys's NSLU2 NAS device. For more information on this platform, @@ -28,7 +28,7 @@ config MACH_NSLU2 config MACH_AVILA bool "Avila" - select FORCE_PCI + depends on IXP4XX_PCI_LEGACY help Say 'Y' here if you want your kernel to support the Gateworks Avila Network Platform. For more information on this platform, @@ -44,7 +44,7 @@ config MACH_LOFT config ARCH_ADI_COYOTE bool "Coyote" - select FORCE_PCI + depends on IXP4XX_PCI_LEGACY help Say 'Y' here if you want your kernel to support the ADI Engineering Coyote Gateway Reference Platform. For more @@ -52,7 +52,7 @@ config ARCH_ADI_COYOTE config MACH_GATEWAY7001 bool "Gateway 7001" - select FORCE_PCI + depends on IXP4XX_PCI_LEGACY help Say 'Y' here if you want your kernel to support Gateway's 7001 Access Point. For more information on this platform, @@ -60,7 +60,7 @@ config MACH_GATEWAY7001 config MACH_WG302V2 bool "Netgear WG302 v2 / WAG302 v2" - select FORCE_PCI + depends on IXP4XX_PCI_LEGACY help Say 'Y' here if you want your kernel to support Netgear's WG302 v2 or WAG302 v2 Access Points. For more information @@ -68,6 +68,7 @@ config MACH_WG302V2 config ARCH_IXDP425 bool "IXDP425" + depends on IXP4XX_PCI_LEGACY help Say 'Y' here if you want your kernel to support Intel's IXDP425 Development Platform (Also known as Richfield). @@ -75,6 +76,7 @@ config ARCH_IXDP425 config MACH_IXDPG425 bool "IXDPG425" + depends on IXP4XX_PCI_LEGACY help Say 'Y' here if you want your kernel to support Intel's IXDPG425 Development Platform (Also known as Montajade). @@ -120,7 +122,7 @@ config ARCH_PRPMC1100 config MACH_NAS100D bool prompt "NAS100D" - select FORCE_PCI + depends on IXP4XX_PCI_LEGACY help Say 'Y' here if you want your kernel to support Iomega's NAS 100d device. For more information on this platform, @@ -129,7 +131,7 @@ config MACH_NAS100D config MACH_DSMG600 bool prompt "D-Link DSM-G600 RevA" - select FORCE_PCI + depends on IXP4XX_PCI_LEGACY help Say 'Y' here if you want your kernel to support D-Link's DSM-G600 RevA device. For more information on this platform, @@ -143,7 +145,7 @@ config ARCH_IXDP4XX config MACH_FSG bool prompt "Freecom FSG-3" - select FORCE_PCI + depends on IXP4XX_PCI_LEGACY help Say 'Y' here if you want your kernel to support Freecom's FSG-3 device. For more information on this platform, @@ -152,7 +154,7 @@ config MACH_FSG config MACH_ARCOM_VULCAN bool prompt "Arcom/Eurotech Vulcan" - select FORCE_PCI + depends on IXP4XX_PCI_LEGACY help Say 'Y' here if you want your kernel to support Arcom's Vulcan board. @@ -173,7 +175,7 @@ config CPU_IXP43X config MACH_GTWX5715 bool "Gemtek WX5715 (Linksys WRV54G)" depends on ARCH_IXP4XX - select FORCE_PCI + depends on IXP4XX_PCI_LEGACY help This board is currently inside the Linksys WRV54G Gateways. @@ -196,7 +198,7 @@ config MACH_DEVIXP config MACH_MICCPT bool "Omicron MICCPT" - select FORCE_PCI + depends on IXP4XX_PCI_LEGACY help Say 'Y' here if you want your kernel to support the MICCPT board from OMICRON electronics GmbH. @@ -209,9 +211,16 @@ config MACH_MIC256 comment "IXP4xx Options" +config IXP4XX_PCI_LEGACY + bool "IXP4xx legacy PCI driver support" + depends on PCI + help + Selects legacy PCI driver. + Not recommended for new development. + config IXP4XX_INDIRECT_PCI bool "Use indirect PCI memory access" - depends on PCI + depends on IXP4XX_PCI_LEGACY help IXP4xx provides two methods of accessing PCI memory space: diff --git a/arch/arm/mach-ixp4xx/avila-setup.c b/arch/arm/mach-ixp4xx/avila-setup.c index 1981b33109cb..ec1d3029f80c 100644 --- a/arch/arm/mach-ixp4xx/avila-setup.c +++ b/arch/arm/mach-ixp4xx/avila-setup.c @@ -19,6 +19,7 @@ #include <linux/tty.h> #include <linux/serial_8250.h> #include <linux/gpio/machine.h> +#include <linux/platform_data/pata_ixp4xx_cf.h> #include <asm/types.h> #include <asm/setup.h> #include <asm/memory.h> diff --git a/arch/arm/mach-ixp4xx/common.c b/arch/arm/mach-ixp4xx/common.c index 000f672a94c9..aeaaa65ad757 100644 --- a/arch/arm/mach-ixp4xx/common.c +++ b/arch/arm/mach-ixp4xx/common.c @@ -27,12 +27,12 @@ #include <linux/cpu.h> #include <linux/pci.h> #include <linux/sched_clock.h> +#include <linux/soc/ixp4xx/cpu.h> #include <linux/irqchip/irq-ixp4xx.h> #include <linux/platform_data/timer-ixp4xx.h> #include <linux/dma-map-ops.h> #include <mach/udc.h> #include <mach/hardware.h> -#include <mach/io.h> #include <linux/uaccess.h> #include <asm/page.h> #include <asm/exception.h> @@ -44,6 +44,27 @@ #include "irqs.h" +u32 ixp4xx_read_feature_bits(void) +{ + u32 val = ~__raw_readl(IXP4XX_EXP_CFG2); + + if (cpu_is_ixp42x_rev_a0()) + return IXP42X_FEATURE_MASK & ~(IXP4XX_FEATURE_RCOMP | + IXP4XX_FEATURE_AES); + if (cpu_is_ixp42x()) + return val & IXP42X_FEATURE_MASK; + if (cpu_is_ixp43x()) + return val & IXP43X_FEATURE_MASK; + return val & IXP46X_FEATURE_MASK; +} +EXPORT_SYMBOL(ixp4xx_read_feature_bits); + +void ixp4xx_write_feature_bits(u32 value) +{ + __raw_writel(~value, IXP4XX_EXP_CFG2); +} +EXPORT_SYMBOL(ixp4xx_write_feature_bits); + #define IXP4XX_TIMER_FREQ 66666000 /************************************************************************* @@ -215,6 +236,27 @@ static struct resource ixp46x_i2c_resources[] = { } }; +/* A single 32-bit register on IXP46x */ +#define IXP4XX_HWRANDOM_BASE_PHYS 0x70002100 + +static struct resource ixp46x_hwrandom_resource[] = { + { + .start = IXP4XX_HWRANDOM_BASE_PHYS, + .end = IXP4XX_HWRANDOM_BASE_PHYS + 0x3, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device ixp46x_hwrandom_device = { + .name = "ixp4xx-hwrandom", + .id = -1, + .dev = { + .coherent_dma_mask = DMA_BIT_MASK(32), + }, + .resource = ixp46x_hwrandom_resource, + .num_resources = ARRAY_SIZE(ixp46x_hwrandom_resource), +}; + /* * I2C controller. The IXP46x uses the same block as the IOP3xx, so * we just use the same device name. @@ -227,7 +269,8 @@ static struct platform_device ixp46x_i2c_controller = { }; static struct platform_device *ixp46x_devices[] __initdata = { - &ixp46x_i2c_controller + &ixp46x_hwrandom_device, + &ixp46x_i2c_controller, }; unsigned long ixp4xx_exp_bus_size; diff --git a/arch/arm/mach-ixp4xx/fsg-setup.c b/arch/arm/mach-ixp4xx/fsg-setup.c index 507ee3878769..844329c5610d 100644 --- a/arch/arm/mach-ixp4xx/fsg-setup.c +++ b/arch/arm/mach-ixp4xx/fsg-setup.c @@ -28,6 +28,7 @@ #include <asm/mach-types.h> #include <asm/mach/arch.h> #include <asm/mach/flash.h> +#include <mach/hardware.h> #include "irqs.h" diff --git a/arch/arm/mach-ixp4xx/include/mach/cpu.h b/arch/arm/mach-ixp4xx/include/mach/cpu.h deleted file mode 100644 index b872a5354ddd..000000000000 --- a/arch/arm/mach-ixp4xx/include/mach/cpu.h +++ /dev/null @@ -1,54 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * arch/arm/mach-ixp4xx/include/mach/cpu.h - * - * IXP4XX cpu type detection - * - * Copyright (C) 2007 MontaVista Software, Inc. - */ - -#ifndef __ASM_ARCH_CPU_H__ -#define __ASM_ARCH_CPU_H__ - -#include <linux/io.h> -#include <asm/cputype.h> - -/* Processor id value in CP15 Register 0 */ -#define IXP42X_PROCESSOR_ID_VALUE 0x690541c0 /* including unused 0x690541Ex */ -#define IXP42X_PROCESSOR_ID_MASK 0xffffffc0 - -#define IXP43X_PROCESSOR_ID_VALUE 0x69054040 -#define IXP43X_PROCESSOR_ID_MASK 0xfffffff0 - -#define IXP46X_PROCESSOR_ID_VALUE 0x69054200 /* including IXP455 */ -#define IXP46X_PROCESSOR_ID_MASK 0xfffffff0 - -#define cpu_is_ixp42x_rev_a0() ((read_cpuid_id() & (IXP42X_PROCESSOR_ID_MASK | 0xF)) == \ - IXP42X_PROCESSOR_ID_VALUE) -#define cpu_is_ixp42x() ((read_cpuid_id() & IXP42X_PROCESSOR_ID_MASK) == \ - IXP42X_PROCESSOR_ID_VALUE) -#define cpu_is_ixp43x() ((read_cpuid_id() & IXP43X_PROCESSOR_ID_MASK) == \ - IXP43X_PROCESSOR_ID_VALUE) -#define cpu_is_ixp46x() ((read_cpuid_id() & IXP46X_PROCESSOR_ID_MASK) == \ - IXP46X_PROCESSOR_ID_VALUE) - -static inline u32 ixp4xx_read_feature_bits(void) -{ - u32 val = ~__raw_readl(IXP4XX_EXP_CFG2); - - if (cpu_is_ixp42x_rev_a0()) - return IXP42X_FEATURE_MASK & ~(IXP4XX_FEATURE_RCOMP | - IXP4XX_FEATURE_AES); - if (cpu_is_ixp42x()) - return val & IXP42X_FEATURE_MASK; - if (cpu_is_ixp43x()) - return val & IXP43X_FEATURE_MASK; - return val & IXP46X_FEATURE_MASK; -} - -static inline void ixp4xx_write_feature_bits(u32 value) -{ - __raw_writel(~value, IXP4XX_EXP_CFG2); -} - -#endif /* _ASM_ARCH_CPU_H */ diff --git a/arch/arm/mach-ixp4xx/include/mach/hardware.h b/arch/arm/mach-ixp4xx/include/mach/hardware.h index b884eedcd0fc..b2b7301ce503 100644 --- a/arch/arm/mach-ixp4xx/include/mach/hardware.h +++ b/arch/arm/mach-ixp4xx/include/mach/hardware.h @@ -23,7 +23,7 @@ #include "ixp4xx-regs.h" #ifndef __ASSEMBLER__ -#include <mach/cpu.h> +#include <linux/soc/ixp4xx/cpu.h> #endif /* Platform helper functions and definitions */ diff --git a/arch/arm/mach-ixp4xx/include/mach/ixp4xx-regs.h b/arch/arm/mach-ixp4xx/include/mach/ixp4xx-regs.h index 708d085ce39f..abb07f105515 100644 --- a/arch/arm/mach-ixp4xx/include/mach/ixp4xx-regs.h +++ b/arch/arm/mach-ixp4xx/include/mach/ixp4xx-regs.h @@ -45,21 +45,21 @@ * it can be used with the low-level debug code. */ #define IXP4XX_PERIPHERAL_BASE_PHYS 0xC8000000 -#define IXP4XX_PERIPHERAL_BASE_VIRT IOMEM(0xFEF00000) +#define IXP4XX_PERIPHERAL_BASE_VIRT IOMEM(0xFEC00000) #define IXP4XX_PERIPHERAL_REGION_SIZE 0x00013000 /* * PCI Config registers */ #define IXP4XX_PCI_CFG_BASE_PHYS 0xC0000000 -#define IXP4XX_PCI_CFG_BASE_VIRT IOMEM(0xFEF13000) +#define IXP4XX_PCI_CFG_BASE_VIRT IOMEM(0xFEC13000) #define IXP4XX_PCI_CFG_REGION_SIZE 0x00001000 /* * Expansion BUS Configuration registers */ #define IXP4XX_EXP_CFG_BASE_PHYS 0xC4000000 -#define IXP4XX_EXP_CFG_BASE_VIRT 0xFEF14000 +#define IXP4XX_EXP_CFG_BASE_VIRT 0xFEC14000 #define IXP4XX_EXP_CFG_REGION_SIZE 0x00001000 #define IXP4XX_EXP_CS0_OFFSET 0x00 @@ -120,6 +120,7 @@ #define IXP4XX_SSP_BASE_PHYS (IXP4XX_PERIPHERAL_BASE_PHYS + 0x12000) +/* The UART is explicitly put in the beginning of fixmap */ #define IXP4XX_UART1_BASE_VIRT (IXP4XX_PERIPHERAL_BASE_VIRT + 0x0000) #define IXP4XX_UART2_BASE_VIRT (IXP4XX_PERIPHERAL_BASE_VIRT + 0x1000) #define IXP4XX_PMU_BASE_VIRT (IXP4XX_PERIPHERAL_BASE_VIRT + 0x2000) @@ -299,58 +300,4 @@ #define DCMD_LENGTH 0x01fff /* length mask (max = 8K - 1) */ -/* "fuse" bits of IXP_EXP_CFG2 */ -/* All IXP4xx CPUs */ -#define IXP4XX_FEATURE_RCOMP (1 << 0) -#define IXP4XX_FEATURE_USB_DEVICE (1 << 1) -#define IXP4XX_FEATURE_HASH (1 << 2) -#define IXP4XX_FEATURE_AES (1 << 3) -#define IXP4XX_FEATURE_DES (1 << 4) -#define IXP4XX_FEATURE_HDLC (1 << 5) -#define IXP4XX_FEATURE_AAL (1 << 6) -#define IXP4XX_FEATURE_HSS (1 << 7) -#define IXP4XX_FEATURE_UTOPIA (1 << 8) -#define IXP4XX_FEATURE_NPEB_ETH0 (1 << 9) -#define IXP4XX_FEATURE_NPEC_ETH (1 << 10) -#define IXP4XX_FEATURE_RESET_NPEA (1 << 11) -#define IXP4XX_FEATURE_RESET_NPEB (1 << 12) -#define IXP4XX_FEATURE_RESET_NPEC (1 << 13) -#define IXP4XX_FEATURE_PCI (1 << 14) -#define IXP4XX_FEATURE_UTOPIA_PHY_LIMIT (3 << 16) -#define IXP4XX_FEATURE_XSCALE_MAX_FREQ (3 << 22) -#define IXP42X_FEATURE_MASK (IXP4XX_FEATURE_RCOMP | \ - IXP4XX_FEATURE_USB_DEVICE | \ - IXP4XX_FEATURE_HASH | \ - IXP4XX_FEATURE_AES | \ - IXP4XX_FEATURE_DES | \ - IXP4XX_FEATURE_HDLC | \ - IXP4XX_FEATURE_AAL | \ - IXP4XX_FEATURE_HSS | \ - IXP4XX_FEATURE_UTOPIA | \ - IXP4XX_FEATURE_NPEB_ETH0 | \ - IXP4XX_FEATURE_NPEC_ETH | \ - IXP4XX_FEATURE_RESET_NPEA | \ - IXP4XX_FEATURE_RESET_NPEB | \ - IXP4XX_FEATURE_RESET_NPEC | \ - IXP4XX_FEATURE_PCI | \ - IXP4XX_FEATURE_UTOPIA_PHY_LIMIT | \ - IXP4XX_FEATURE_XSCALE_MAX_FREQ) - - -/* IXP43x/46x CPUs */ -#define IXP4XX_FEATURE_ECC_TIMESYNC (1 << 15) -#define IXP4XX_FEATURE_USB_HOST (1 << 18) -#define IXP4XX_FEATURE_NPEA_ETH (1 << 19) -#define IXP43X_FEATURE_MASK (IXP42X_FEATURE_MASK | \ - IXP4XX_FEATURE_ECC_TIMESYNC | \ - IXP4XX_FEATURE_USB_HOST | \ - IXP4XX_FEATURE_NPEA_ETH) - -/* IXP46x CPU (including IXP455) only */ -#define IXP4XX_FEATURE_NPEB_ETH_1_TO_3 (1 << 20) -#define IXP4XX_FEATURE_RSA (1 << 21) -#define IXP46X_FEATURE_MASK (IXP43X_FEATURE_MASK | \ - IXP4XX_FEATURE_NPEB_ETH_1_TO_3 | \ - IXP4XX_FEATURE_RSA) - #endif diff --git a/arch/arm/mach-ixp4xx/include/mach/platform.h b/arch/arm/mach-ixp4xx/include/mach/platform.h index 6d403fe0bf52..d8b4df96db08 100644 --- a/arch/arm/mach-ixp4xx/include/mach/platform.h +++ b/arch/arm/mach-ixp4xx/include/mach/platform.h @@ -80,20 +80,6 @@ extern unsigned long ixp4xx_exp_bus_size; #define IXP4XX_UART_XTAL 14745600 /* - * This structure provide a means for the board setup code - * to give information to th pata_ixp4xx driver. It is - * passed as platform_data. - */ -struct ixp4xx_pata_data { - volatile u32 *cs0_cfg; - volatile u32 *cs1_cfg; - unsigned long cs0_bits; - unsigned long cs1_bits; - void __iomem *cs0; - void __iomem *cs1; -}; - -/* * Frequency of clock used for primary clocksource */ extern unsigned long ixp4xx_timer_freq; diff --git a/arch/arm/mach-ixp4xx/ixp4xx-of.c b/arch/arm/mach-ixp4xx/ixp4xx-of.c index 7449b8319c8a..f9904716ec7f 100644 --- a/arch/arm/mach-ixp4xx/ixp4xx-of.c +++ b/arch/arm/mach-ixp4xx/ixp4xx-of.c @@ -9,8 +9,12 @@ #include <asm/mach/arch.h> #include <asm/mach/map.h> -#include <mach/hardware.h> -#include <mach/ixp4xx-regs.h> +/* + * These are the only fixed phys to virt mappings we ever need + * we put it right after the UART mapping at 0xffc80000-0xffc81fff + */ +#define IXP4XX_EXP_CFG_BASE_PHYS 0xC4000000 +#define IXP4XX_EXP_CFG_BASE_VIRT 0xFEC14000 static struct map_desc ixp4xx_of_io_desc[] __initdata = { /* diff --git a/arch/arm/mach-ixp4xx/nas100d-setup.c b/arch/arm/mach-ixp4xx/nas100d-setup.c index 6959ad2e3aec..6133cf01cbe4 100644 --- a/arch/arm/mach-ixp4xx/nas100d-setup.c +++ b/arch/arm/mach-ixp4xx/nas100d-setup.c @@ -33,6 +33,7 @@ #include <asm/mach-types.h> #include <asm/mach/arch.h> #include <asm/mach/flash.h> +#include <mach/hardware.h> #include "irqs.h" diff --git a/arch/arm/mach-ixp4xx/nslu2-setup.c b/arch/arm/mach-ixp4xx/nslu2-setup.c index a428bb918703..8526a70e401b 100644 --- a/arch/arm/mach-ixp4xx/nslu2-setup.c +++ b/arch/arm/mach-ixp4xx/nslu2-setup.c @@ -31,6 +31,7 @@ #include <asm/mach/arch.h> #include <asm/mach/flash.h> #include <asm/mach/time.h> +#include <mach/hardware.h> #include "irqs.h" diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 030cb32da980..d17c83319e70 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -1058,7 +1058,7 @@ config PATA_ISAPNP config PATA_IXP4XX_CF tristate "IXP4XX Compact Flash support" - depends on ARCH_IXP4XX + depends on ARCH_IXP4XX || COMPILE_TEST help This option enables support for a Compact Flash connected on the ixp4xx expansion bus. This driver had been written for diff --git a/drivers/ata/pata_ixp4xx_cf.c b/drivers/ata/pata_ixp4xx_cf.c index 43215a4c1e54..5881d64af943 100644 --- a/drivers/ata/pata_ixp4xx_cf.c +++ b/drivers/ata/pata_ixp4xx_cf.c @@ -17,6 +17,7 @@ #include <linux/libata.h> #include <linux/irq.h> #include <linux/platform_device.h> +#include <linux/platform_data/pata_ixp4xx_cf.h> #include <scsi/scsi_host.h> #define DRV_NAME "pata_ixp4xx_cf" diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index 1fe006f3f12f..f033a11cc90d 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -152,7 +152,7 @@ config HW_RANDOM_VIA config HW_RANDOM_IXP4XX tristate "Intel IXP4xx NPU HW Pseudo-Random Number Generator support" - depends on ARCH_IXP4XX + depends on ARCH_IXP4XX || COMPILE_TEST default HW_RANDOM help This driver provides kernel-side support for the Pseudo-Random diff --git a/drivers/char/hw_random/ixp4xx-rng.c b/drivers/char/hw_random/ixp4xx-rng.c index beec1627db3c..188854dd16a9 100644 --- a/drivers/char/hw_random/ixp4xx-rng.c +++ b/drivers/char/hw_random/ixp4xx-rng.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * drivers/char/hw_random/ixp4xx-rng.c * @@ -8,23 +9,20 @@ * Copyright 2005 (c) MontaVista Software, Inc. * * Fixes by Michael Buesch - * - * 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 <linux/kernel.h> #include <linux/types.h> #include <linux/module.h> #include <linux/moduleparam.h> +#include <linux/platform_device.h> #include <linux/init.h> #include <linux/bitops.h> #include <linux/hw_random.h> +#include <linux/of.h> +#include <linux/soc/ixp4xx/cpu.h> #include <asm/io.h> -#include <mach/hardware.h> - static int ixp4xx_rng_data_read(struct hwrng *rng, u32 *buffer) { @@ -40,35 +38,40 @@ static struct hwrng ixp4xx_rng_ops = { .data_read = ixp4xx_rng_data_read, }; -static int __init ixp4xx_rng_init(void) +static int ixp4xx_rng_probe(struct platform_device *pdev) { void __iomem * rng_base; - int err; + struct device *dev = &pdev->dev; + struct resource *res; if (!cpu_is_ixp46x()) /* includes IXP455 */ return -ENOSYS; - rng_base = ioremap(0x70002100, 4); - if (!rng_base) - return -ENOMEM; - ixp4xx_rng_ops.priv = (unsigned long)rng_base; - err = hwrng_register(&ixp4xx_rng_ops); - if (err) - iounmap(rng_base); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + rng_base = devm_ioremap_resource(dev, res); + if (IS_ERR(rng_base)) + return PTR_ERR(rng_base); - return err; + ixp4xx_rng_ops.priv = (unsigned long)rng_base; + return devm_hwrng_register(dev, &ixp4xx_rng_ops); } -static void __exit ixp4xx_rng_exit(void) -{ - void __iomem * rng_base = (void __iomem *)ixp4xx_rng_ops.priv; - - hwrng_unregister(&ixp4xx_rng_ops); - iounmap(rng_base); -} +static const struct of_device_id ixp4xx_rng_of_match[] = { + { + .compatible = "intel,ixp46x-rng", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, ixp4xx_rng_of_match); -module_init(ixp4xx_rng_init); -module_exit(ixp4xx_rng_exit); +static struct platform_driver ixp4xx_rng_driver = { + .driver = { + .name = "ixp4xx-hwrandom", + .of_match_table = ixp4xx_rng_of_match, + }, + .probe = ixp4xx_rng_probe, +}; +module_platform_driver(ixp4xx_rng_driver); MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net>"); MODULE_DESCRIPTION("H/W Pseudo-Random Number Generator (RNG) driver for IXP45x/46x"); diff --git a/drivers/crypto/ixp4xx_crypto.c b/drivers/crypto/ixp4xx_crypto.c index 0616e369522e..590689a23b35 100644 --- a/drivers/crypto/ixp4xx_crypto.c +++ b/drivers/crypto/ixp4xx_crypto.c @@ -30,6 +30,10 @@ #include <linux/soc/ixp4xx/npe.h> #include <linux/soc/ixp4xx/qmgr.h> +/* Intermittent includes, delete this after v5.14-rc1 */ +#include <linux/soc/ixp4xx/cpu.h> +#include <mach/ixp4xx-regs.h> + #define MAX_KEYLEN 32 /* hash: cfgword + 2 * digestlen; crypt: keylen + cfgword */ diff --git a/drivers/net/ethernet/xscale/ixp4xx_eth.c b/drivers/net/ethernet/xscale/ixp4xx_eth.c index cb89323855d8..468cb5c909c6 100644 --- a/drivers/net/ethernet/xscale/ixp4xx_eth.c +++ b/drivers/net/ethernet/xscale/ixp4xx_eth.c @@ -37,6 +37,8 @@ #include <linux/module.h> #include <linux/soc/ixp4xx/npe.h> #include <linux/soc/ixp4xx/qmgr.h> +#include <mach/hardware.h> +#include <linux/soc/ixp4xx/cpu.h> #include "ixp46x_ts.h" diff --git a/drivers/net/ethernet/xscale/ptp_ixp46x.c b/drivers/net/ethernet/xscale/ptp_ixp46x.c index 9ecc395239e9..99d4d9439d05 100644 --- a/drivers/net/ethernet/xscale/ptp_ixp46x.c +++ b/drivers/net/ethernet/xscale/ptp_ixp46x.c @@ -12,9 +12,8 @@ #include <linux/io.h> #include <linux/irq.h> #include <linux/kernel.h> -#include <linux/module.h> - #include <linux/ptp_clock_kernel.h> +#include <linux/soc/ixp4xx/cpu.h> #include "ixp46x_ts.h" diff --git a/drivers/net/wan/ixp4xx_hss.c b/drivers/net/wan/ixp4xx_hss.c index ecea09fd21cb..7ebe627e9392 100644 --- a/drivers/net/wan/ixp4xx_hss.c +++ b/drivers/net/wan/ixp4xx_hss.c @@ -22,6 +22,7 @@ #include <linux/slab.h> #include <linux/soc/ixp4xx/npe.h> #include <linux/soc/ixp4xx/qmgr.h> +#include <linux/soc/ixp4xx/cpu.h> #define DEBUG_DESC 0 #define DEBUG_RX 0 diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig index 2f2c8a1729f9..5e1e3796efa4 100644 --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig @@ -37,6 +37,14 @@ config PCI_FTPCI100 depends on OF default ARCH_GEMINI +config PCI_IXP4XX + bool "Intel IXP4xx PCI controller" + depends on ARM && OF + default ARCH_IXP4XX + help + Say Y here if you want support for the PCI host controller found + in the Intel IXP4xx XScale-based network processor SoC. + config PCI_TEGRA bool "NVIDIA Tegra PCIe controller" depends on ARCH_TEGRA || COMPILE_TEST diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile index 63e3880a3e87..aaf30b3dcc14 100644 --- a/drivers/pci/controller/Makefile +++ b/drivers/pci/controller/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_PCIE_CADENCE) += cadence/ obj-$(CONFIG_PCI_FTPCI100) += pci-ftpci100.o +obj-$(CONFIG_PCI_IXP4XX) += pci-ixp4xx.o obj-$(CONFIG_PCI_HYPERV) += pci-hyperv.o obj-$(CONFIG_PCI_HYPERV_INTERFACE) += pci-hyperv-intf.o obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o diff --git a/drivers/pci/controller/pci-ixp4xx.c b/drivers/pci/controller/pci-ixp4xx.c new file mode 100644 index 000000000000..896a45b24236 --- /dev/null +++ b/drivers/pci/controller/pci-ixp4xx.c @@ -0,0 +1,671 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Support for Intel IXP4xx PCI host controller + * + * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org> + * + * Based on the IXP4xx arch/arm/mach-ixp4xx/common-pci.c driver + * Copyright (C) 2002 Intel Corporation + * Copyright (C) 2003 Greg Ungerer <gerg@linux-m68k.org> + * Copyright (C) 2003-2004 MontaVista Software, Inc. + * Copyright (C) 2005 Deepak Saxena <dsaxena@plexity.net> + * Copyright (C) 2005 Alessandro Zummo <a.zummo@towertech.it> + * + * TODO: + * - Test IO-space access + * - DMA support + */ + +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/of_pci.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/bits.h> + +/* Register offsets */ +#define IXP4XX_PCI_NP_AD 0x00 +#define IXP4XX_PCI_NP_CBE 0x04 +#define IXP4XX_PCI_NP_WDATA 0x08 +#define IXP4XX_PCI_NP_RDATA 0x0c +#define IXP4XX_PCI_CRP_AD_CBE 0x10 +#define IXP4XX_PCI_CRP_WDATA 0x14 +#define IXP4XX_PCI_CRP_RDATA 0x18 +#define IXP4XX_PCI_CSR 0x1c +#define IXP4XX_PCI_ISR 0x20 +#define IXP4XX_PCI_INTEN 0x24 +#define IXP4XX_PCI_DMACTRL 0x28 +#define IXP4XX_PCI_AHBMEMBASE 0x2c +#define IXP4XX_PCI_AHBIOBASE 0x30 +#define IXP4XX_PCI_PCIMEMBASE 0x34 +#define IXP4XX_PCI_AHBDOORBELL 0x38 +#define IXP4XX_PCI_PCIDOORBELL 0x3c +#define IXP4XX_PCI_ATPDMA0_AHBADDR 0x40 +#define IXP4XX_PCI_ATPDMA0_PCIADDR 0x44 +#define IXP4XX_PCI_ATPDMA0_LENADDR 0x48 +#define IXP4XX_PCI_ATPDMA1_AHBADDR 0x4c +#define IXP4XX_PCI_ATPDMA1_PCIADDR 0x50 +#define IXP4XX_PCI_ATPDMA1_LENADDR 0x54 + +/* CSR bit definitions */ +#define IXP4XX_PCI_CSR_HOST BIT(0) +#define IXP4XX_PCI_CSR_ARBEN BIT(1) +#define IXP4XX_PCI_CSR_ADS BIT(2) +#define IXP4XX_PCI_CSR_PDS BIT(3) +#define IXP4XX_PCI_CSR_ABE BIT(4) +#define IXP4XX_PCI_CSR_DBT BIT(5) +#define IXP4XX_PCI_CSR_ASE BIT(8) +#define IXP4XX_PCI_CSR_IC BIT(15) +#define IXP4XX_PCI_CSR_PRST BIT(16) + +/* ISR (Interrupt status) Register bit definitions */ +#define IXP4XX_PCI_ISR_PSE BIT(0) +#define IXP4XX_PCI_ISR_PFE BIT(1) +#define IXP4XX_PCI_ISR_PPE BIT(2) +#define IXP4XX_PCI_ISR_AHBE BIT(3) +#define IXP4XX_PCI_ISR_APDC BIT(4) +#define IXP4XX_PCI_ISR_PADC BIT(5) +#define IXP4XX_PCI_ISR_ADB BIT(6) +#define IXP4XX_PCI_ISR_PDB BIT(7) + +/* INTEN (Interrupt Enable) Register bit definitions */ +#define IXP4XX_PCI_INTEN_PSE BIT(0) +#define IXP4XX_PCI_INTEN_PFE BIT(1) +#define IXP4XX_PCI_INTEN_PPE BIT(2) +#define IXP4XX_PCI_INTEN_AHBE BIT(3) +#define IXP4XX_PCI_INTEN_APDC BIT(4) +#define IXP4XX_PCI_INTEN_PADC BIT(5) +#define IXP4XX_PCI_INTEN_ADB BIT(6) +#define IXP4XX_PCI_INTEN_PDB BIT(7) + +/* Shift value for byte enable on NP cmd/byte enable register */ +#define IXP4XX_PCI_NP_CBE_BESL 4 + +/* PCI commands supported by NP access unit */ +#define NP_CMD_IOREAD 0x2 +#define NP_CMD_IOWRITE 0x3 +#define NP_CMD_CONFIGREAD 0xa +#define NP_CMD_CONFIGWRITE 0xb +#define NP_CMD_MEMREAD 0x6 +#define NP_CMD_MEMWRITE 0x7 + +/* Constants for CRP access into local config space */ +#define CRP_AD_CBE_BESL 20 +#define CRP_AD_CBE_WRITE 0x00010000 + +/* Special PCI configuration space registers for this controller */ +#define IXP4XX_PCI_RTOTTO 0x40 + +struct ixp4xx_pci { + struct device *dev; + void __iomem *base; + bool errata_hammer; + bool host_mode; +}; + +/* + * The IXP4xx has a peculiar address bus that will change the + * byte order on SoC peripherals depending on whether the device + * operates in big-endian or little-endian mode. That means that + * readl() and writel() that always use little-endian access + * will not work for SoC peripherals such as the PCI controller + * when used in big-endian mode. The accesses to the individual + * PCI devices on the other hand, are always little-endian and + * can use readl() and writel(). + * + * For local AHB bus access we need to use __raw_[readl|writel]() + * to make sure that we access the SoC devices in the CPU native + * endianness. + */ +static inline u32 ixp4xx_readl(struct ixp4xx_pci *p, u32 reg) +{ + return __raw_readl(p->base + reg); +} + +static inline void ixp4xx_writel(struct ixp4xx_pci *p, u32 reg, u32 val) +{ + __raw_writel(val, p->base + reg); +} + +static int ixp4xx_pci_check_master_abort(struct ixp4xx_pci *p) +{ + u32 isr = ixp4xx_readl(p, IXP4XX_PCI_ISR); + + if (isr & IXP4XX_PCI_ISR_PFE) { + /* Make sure the master abort bit is reset */ + ixp4xx_writel(p, IXP4XX_PCI_ISR, IXP4XX_PCI_ISR_PFE); + dev_dbg(p->dev, "master abort detected\n"); + return -EINVAL; + } + + return 0; +} + +static int ixp4xx_pci_read(struct ixp4xx_pci *p, u32 addr, u32 cmd, u32 *data) +{ + ixp4xx_writel(p, IXP4XX_PCI_NP_AD, addr); + + if (p->errata_hammer) { + int i; + + /* + * PCI workaround - only works if NP PCI space reads have + * no side effects. Hammer the register and read twice 8 + * times. last one will be good. + */ + for (i = 0; i < 8; i++) { + ixp4xx_writel(p, IXP4XX_PCI_NP_CBE, cmd); + *data = ixp4xx_readl(p, IXP4XX_PCI_NP_RDATA); + *data = ixp4xx_readl(p, IXP4XX_PCI_NP_RDATA); + } + } else { + ixp4xx_writel(p, IXP4XX_PCI_NP_CBE, cmd); + *data = ixp4xx_readl(p, IXP4XX_PCI_NP_RDATA); + } + + return ixp4xx_pci_check_master_abort(p); +} + +static int ixp4xx_pci_write(struct ixp4xx_pci *p, u32 addr, u32 cmd, u32 data) +{ + ixp4xx_writel(p, IXP4XX_PCI_NP_AD, addr); + + /* Set up the write */ + ixp4xx_writel(p, IXP4XX_PCI_NP_CBE, cmd); + + /* Execute the write by writing to NP_WDATA */ + ixp4xx_writel(p, IXP4XX_PCI_NP_WDATA, data); + + return ixp4xx_pci_check_master_abort(p); +} + +static u32 ixp4xx_config_addr(u8 bus_num, u16 devfn, int where) +{ + /* Root bus is always 0 in this hardware */ + if (bus_num == 0) { + /* type 0 */ + return BIT(32-PCI_SLOT(devfn)) | ((PCI_FUNC(devfn)) << 8) | + (where & ~3); + } else { + /* type 1 */ + return (bus_num << 16) | ((PCI_SLOT(devfn)) << 11) | + ((PCI_FUNC(devfn)) << 8) | (where & ~3) | 1; + } +} + +/* + * CRP functions are "Controller Configuration Port" accesses + * initiated from within this driver itself to read/write PCI + * control information in the config space. + */ +static u32 ixp4xx_crp_byte_lane_enable_bits(u32 n, int size) +{ + if (size == 1) + return (0xf & ~BIT(n)) << CRP_AD_CBE_BESL; + if (size == 2) + return (0xf & ~(BIT(n) | BIT(n+1))) << CRP_AD_CBE_BESL; + if (size == 4) + return 0; + return 0xffffffff; +} + +static int ixp4xx_crp_read_config(struct ixp4xx_pci *p, int where, int size, + u32 *value) +{ + u32 n, cmd, val; + + n = where % 4; + cmd = where & ~3; + + dev_dbg(p->dev, "%s from %d size %d cmd %08x\n", + __func__, where, size, cmd); + + ixp4xx_writel(p, IXP4XX_PCI_CRP_AD_CBE, cmd); + val = ixp4xx_readl(p, IXP4XX_PCI_CRP_RDATA); + + val >>= (8*n); + switch (size) { + case 1: + val &= U8_MAX; + dev_dbg(p->dev, "%s read byte %02x\n", __func__, val); + break; + case 2: + val &= U16_MAX; + dev_dbg(p->dev, "%s read word %04x\n", __func__, val); + break; + case 4: + val &= U32_MAX; + dev_dbg(p->dev, "%s read long %08x\n", __func__, val); + break; + default: + /* Should not happen */ + dev_err(p->dev, "%s illegal size\n", __func__); + return PCIBIOS_DEVICE_NOT_FOUND; + } + *value = val; + + return PCIBIOS_SUCCESSFUL; +} + +static int ixp4xx_crp_write_config(struct ixp4xx_pci *p, int where, int size, + u32 value) +{ + u32 n, cmd, val; + + n = where % 4; + cmd = ixp4xx_crp_byte_lane_enable_bits(n, size); + if (cmd == 0xffffffff) + return PCIBIOS_BAD_REGISTER_NUMBER; + cmd |= where & ~3; + cmd |= CRP_AD_CBE_WRITE; + + val = value << (8*n); + + dev_dbg(p->dev, "%s to %d size %d cmd %08x val %08x\n", + __func__, where, size, cmd, val); + + ixp4xx_writel(p, IXP4XX_PCI_CRP_AD_CBE, cmd); + ixp4xx_writel(p, IXP4XX_PCI_CRP_WDATA, val); + + return PCIBIOS_SUCCESSFUL; +} + +/* + * Then follows the functions that read and write from the common PCI + * configuration space. + */ +static u32 ixp4xx_byte_lane_enable_bits(u32 n, int size) +{ + if (size == 1) + return (0xf & ~BIT(n)) << 4; + if (size == 2) + return (0xf & ~(BIT(n) | BIT(n+1))) << 4; + if (size == 4) + return 0; + return 0xffffffff; +} + +static int ixp4xx_pci_read_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *value) +{ + struct ixp4xx_pci *p = bus->sysdata; + u32 n, addr, val, cmd; + u8 bus_num = bus->number; + int ret; + + *value = 0xffffffff; + n = where % 4; + cmd = ixp4xx_byte_lane_enable_bits(n, size); + if (cmd == 0xffffffff) + return PCIBIOS_BAD_REGISTER_NUMBER; + + addr = ixp4xx_config_addr(bus_num, devfn, where); + cmd |= NP_CMD_CONFIGREAD; + dev_dbg(p->dev, "read_config from %d size %d dev %d:%d:%d address: %08x cmd: %08x\n", + where, size, bus_num, PCI_SLOT(devfn), PCI_FUNC(devfn), addr, cmd); + + ret = ixp4xx_pci_read(p, addr, cmd, &val); + if (ret) + return PCIBIOS_DEVICE_NOT_FOUND; + + val >>= (8*n); + switch (size) { + case 1: + val &= U8_MAX; + dev_dbg(p->dev, "%s read byte %02x\n", __func__, val); + break; + case 2: + val &= U16_MAX; + dev_dbg(p->dev, "%s read word %04x\n", __func__, val); + break; + case 4: + val &= U32_MAX; + dev_dbg(p->dev, "%s read long %08x\n", __func__, val); + break; + default: + /* Should not happen */ + dev_err(p->dev, "%s illegal size\n", __func__); + return PCIBIOS_DEVICE_NOT_FOUND; + } + *value = val; + + return PCIBIOS_SUCCESSFUL; +} + +static int ixp4xx_pci_write_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 value) +{ + struct ixp4xx_pci *p = bus->sysdata; + u32 n, addr, val, cmd; + u8 bus_num = bus->number; + int ret; + + n = where % 4; + cmd = ixp4xx_byte_lane_enable_bits(n, size); + if (cmd == 0xffffffff) + return PCIBIOS_BAD_REGISTER_NUMBER; + + addr = ixp4xx_config_addr(bus_num, devfn, where); + cmd |= NP_CMD_CONFIGWRITE; + val = value << (8*n); + + dev_dbg(p->dev, "write_config_byte %#x to %d size %d dev %d:%d:%d addr: %08x cmd %08x\n", + value, where, size, bus_num, PCI_SLOT(devfn), PCI_FUNC(devfn), addr, cmd); + + ret = ixp4xx_pci_write(p, addr, cmd, val); + if (ret) + return PCIBIOS_DEVICE_NOT_FOUND; + + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops ixp4xx_pci_ops = { + .read = ixp4xx_pci_read_config, + .write = ixp4xx_pci_write_config, +}; + +static u32 ixp4xx_pci_addr_to_64mconf(phys_addr_t addr) +{ + u8 base; + + base = ((addr & 0xff000000) >> 24); + return (base << 24) | ((base + 1) << 16) + | ((base + 2) << 8) | (base + 3); +} + +static int ixp4xx_pci_parse_map_ranges(struct ixp4xx_pci *p) +{ + struct device *dev = p->dev; + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(p); + struct resource_entry *win; + struct resource *res; + phys_addr_t addr; + + win = resource_list_first_type(&bridge->windows, IORESOURCE_MEM); + if (win) { + u32 pcimembase; + + res = win->res; + addr = res->start - win->offset; + + if (res->flags & IORESOURCE_PREFETCH) + res->name = "IXP4xx PCI PRE-MEM"; + else + res->name = "IXP4xx PCI NON-PRE-MEM"; + + dev_dbg(dev, "%s window %pR, bus addr %pa\n", + res->name, res, &addr); + if (resource_size(res) != SZ_64M) { + dev_err(dev, "memory range is not 64MB\n"); + return -EINVAL; + } + + pcimembase = ixp4xx_pci_addr_to_64mconf(addr); + /* Commit configuration */ + ixp4xx_writel(p, IXP4XX_PCI_PCIMEMBASE, pcimembase); + } else { + dev_err(dev, "no AHB memory mapping defined\n"); + } + + win = resource_list_first_type(&bridge->windows, IORESOURCE_IO); + if (win) { + res = win->res; + + addr = pci_pio_to_address(res->start); + if (addr & 0xff) { + dev_err(dev, "IO mem at uneven address: %pa\n", &addr); + return -EINVAL; + } + + res->name = "IXP4xx PCI IO MEM"; + /* + * Setup I/O space location for PCI->AHB access, the + * upper 24 bits of the address goes into the lower + * 24 bits of this register. + */ + ixp4xx_writel(p, IXP4XX_PCI_AHBIOBASE, (addr >> 8)); + } else { + dev_info(dev, "no IO space AHB memory mapping defined\n"); + } + + return 0; +} + +static int ixp4xx_pci_parse_map_dma_ranges(struct ixp4xx_pci *p) +{ + struct device *dev = p->dev; + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(p); + struct resource_entry *win; + struct resource *res; + phys_addr_t addr; + u32 ahbmembase; + + win = resource_list_first_type(&bridge->dma_ranges, IORESOURCE_MEM); + if (win) { + res = win->res; + addr = res->start - win->offset; + + if (resource_size(res) != SZ_64M) { + dev_err(dev, "DMA memory range is not 64MB\n"); + return -EINVAL; + } + + dev_dbg(dev, "DMA MEM BASE: %pa\n", &addr); + /* + * 4 PCI-to-AHB windows of 16 MB each, write the 8 high bits + * into each byte of the PCI_AHBMEMBASE register. + */ + ahbmembase = ixp4xx_pci_addr_to_64mconf(addr); + /* Commit AHB membase */ + ixp4xx_writel(p, IXP4XX_PCI_AHBMEMBASE, ahbmembase); + } else { + dev_err(dev, "no DMA memory range defined\n"); + } + + return 0; +} + +/* Only used to get context for abort handling */ +static struct ixp4xx_pci *ixp4xx_pci_abort_singleton; + +static int ixp4xx_pci_abort_handler(unsigned long addr, unsigned int fsr, + struct pt_regs *regs) +{ + struct ixp4xx_pci *p = ixp4xx_pci_abort_singleton; + u32 isr, status; + int ret; + + isr = ixp4xx_readl(p, IXP4XX_PCI_ISR); + ret = ixp4xx_crp_read_config(p, PCI_STATUS, 2, &status); + if (ret) { + dev_err(p->dev, "unable to read abort status\n"); + return -EINVAL; + } + + dev_err(p->dev, + "PCI: abort_handler addr = %#lx, isr = %#x, status = %#x\n", + addr, isr, status); + + /* Make sure the Master Abort bit is reset */ + ixp4xx_writel(p, IXP4XX_PCI_ISR, IXP4XX_PCI_ISR_PFE); + status |= PCI_STATUS_REC_MASTER_ABORT; + ret = ixp4xx_crp_write_config(p, PCI_STATUS, 2, status); + if (ret) + dev_err(p->dev, "unable to clear abort status bit\n"); + + /* + * If it was an imprecise abort, then we need to correct the + * return address to be _after_ the instruction. + */ + if (fsr & (1 << 10)) { + dev_err(p->dev, "imprecise abort\n"); + regs->ARM_pc += 4; + } + + return 0; +} + +static int __init ixp4xx_pci_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct ixp4xx_pci *p; + struct pci_host_bridge *host; + int ret; + u32 val; + phys_addr_t addr; + u32 basereg[4] = { + PCI_BASE_ADDRESS_0, + PCI_BASE_ADDRESS_1, + PCI_BASE_ADDRESS_2, + PCI_BASE_ADDRESS_3, + }; + int i; + + host = devm_pci_alloc_host_bridge(dev, sizeof(*p)); + if (!host) + return -ENOMEM; + + host->ops = &ixp4xx_pci_ops; + p = pci_host_bridge_priv(host); + host->sysdata = p; + p->dev = dev; + dev_set_drvdata(dev, p); + + /* + * Set up quirk for erratic behaviour in the 42x variant + * when accessing config space. + */ + if (of_device_is_compatible(np, "intel,ixp42x-pci")) { + p->errata_hammer = true; + dev_info(dev, "activate hammering errata\n"); + } + + p->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(p->base)) + return PTR_ERR(p->base); + + val = ixp4xx_readl(p, IXP4XX_PCI_CSR); + p->host_mode = !!(val & IXP4XX_PCI_CSR_HOST); + dev_info(dev, "controller is in %s mode\n", + p->host_mode ? "host" : "option"); + + /* Hook in our fault handler for PCI errors */ + ixp4xx_pci_abort_singleton = p; + hook_fault_code(16+6, ixp4xx_pci_abort_handler, SIGBUS, 0, + "imprecise external abort"); + + ret = ixp4xx_pci_parse_map_ranges(p); + if (ret) + return ret; + + ret = ixp4xx_pci_parse_map_dma_ranges(p); + if (ret) + return ret; + + /* This is only configured in host mode */ + if (p->host_mode) { + addr = __pa(PAGE_OFFSET); + /* This is a noop (0x00) but explains what is going on */ + addr |= PCI_BASE_ADDRESS_SPACE_MEMORY; + + for (i = 0; i < 4; i++) { + /* Write this directly into the config space */ + ret = ixp4xx_crp_write_config(p, basereg[i], 4, addr); + if (ret) + dev_err(dev, "failed to set up PCI_BASE_ADDRESS_%d\n", i); + else + dev_info(dev, "set PCI_BASE_ADDR_%d to %pa\n", i, &addr); + addr += SZ_16M; + } + + /* + * Enable CSR window at 64 MiB to allow PCI masters to continue + * prefetching past the 64 MiB boundary, if all AHB to PCI + * windows are consecutive. + */ + ret = ixp4xx_crp_write_config(p, PCI_BASE_ADDRESS_4, 4, addr); + if (ret) + dev_err(dev, "failed to set up PCI_BASE_ADDRESS_4\n"); + else + dev_info(dev, "set PCI_BASE_ADDR_4 to %pa\n", &addr); + + /* + * Put the IO memory window at the very end of physical memory + * at 0xfffffc00. This is when the system is trying to access IO + * memory over AHB. + */ + addr = 0xfffffc00; + addr |= PCI_BASE_ADDRESS_SPACE_IO; + ret = ixp4xx_crp_write_config(p, PCI_BASE_ADDRESS_5, 4, addr); + if (ret) + dev_err(dev, "failed to set up PCI_BASE_ADDRESS_5\n"); + else + dev_info(dev, "set PCI_BASE_ADDR_5 to %pa\n", &addr); + + /* + * Retry timeout to 0x80 + * Transfer ready timeout to 0xff + */ + ret = ixp4xx_crp_write_config(p, IXP4XX_PCI_RTOTTO, 4, + 0x000080ff); + if (ret) + dev_err(dev, "failed to set up TRDY limit\n"); + else + dev_info(dev, "set TRDY limit to 0x80ff\n"); + } + + /* Clear interrupts */ + val = IXP4XX_PCI_ISR_PSE | IXP4XX_PCI_ISR_PFE | IXP4XX_PCI_ISR_PPE | IXP4XX_PCI_ISR_AHBE; + ixp4xx_writel(p, IXP4XX_PCI_ISR, val); + + /* + * Set Initialize Complete in PCI Control Register: allow IXP4XX to + * generate PCI configuration cycles. Specify that the AHB bus is + * operating in big-endian mode. Set up byte lane swapping between + * little-endian PCI and the big-endian AHB bus. + */ + val = IXP4XX_PCI_CSR_IC | IXP4XX_PCI_CSR_ABE; + if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) + val |= (IXP4XX_PCI_CSR_PDS | IXP4XX_PCI_CSR_ADS); + ixp4xx_writel(p, IXP4XX_PCI_CSR, val); + + ret = ixp4xx_crp_write_config(p, PCI_COMMAND, 2, PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY); + if (ret) + dev_err(dev, "unable to initialize master and command memory\n"); + else + dev_info(dev, "initialized as master\n"); + + pci_host_probe(host); + + return 0; +} + +static const struct of_device_id ixp4xx_pci_of_match[] = { + { + .compatible = "intel,ixp42x-pci", + }, + { + .compatible = "intel,ixp43x-pci", + }, + {}, +}; + +/* + * This driver needs to be a builtin module with suppressed bind + * attributes since the probe() is initializing a hard exception + * handler and this can only be done from __init-tagged code + * sections. This module cannot be removed and inserted at all. + */ +static struct platform_driver ixp4xx_pci_driver = { + .driver = { + .name = "ixp4xx-pci", + .suppress_bind_attrs = true, + .of_match_table = ixp4xx_pci_of_match, + }, +}; +builtin_platform_driver_probe(ixp4xx_pci_driver, ixp4xx_pci_probe); diff --git a/drivers/soc/ixp4xx/ixp4xx-npe.c b/drivers/soc/ixp4xx/ixp4xx-npe.c index ec90b44fa0cd..925b49152c77 100644 --- a/drivers/soc/ixp4xx/ixp4xx-npe.c +++ b/drivers/soc/ixp4xx/ixp4xx-npe.c @@ -20,6 +20,8 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/soc/ixp4xx/npe.h> +#include <mach/hardware.h> +#include <linux/soc/ixp4xx/cpu.h> #define DEBUG_MSG 0 #define DEBUG_FW 0 diff --git a/drivers/soc/ixp4xx/ixp4xx-qmgr.c b/drivers/soc/ixp4xx/ixp4xx-qmgr.c index 8c968382cea7..7149510b307e 100644 --- a/drivers/soc/ixp4xx/ixp4xx-qmgr.c +++ b/drivers/soc/ixp4xx/ixp4xx-qmgr.c @@ -12,6 +12,8 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/soc/ixp4xx/qmgr.h> +#include <mach/hardware.h> +#include <linux/soc/ixp4xx/cpu.h> static struct qmgr_regs __iomem *qmgr_regs; static int qmgr_irq_1; diff --git a/include/linux/platform_data/pata_ixp4xx_cf.h b/include/linux/platform_data/pata_ixp4xx_cf.h new file mode 100644 index 000000000000..601ba97fef57 --- /dev/null +++ b/include/linux/platform_data/pata_ixp4xx_cf.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __PLATFORM_DATA_PATA_IXP4XX_H +#define __PLATFORM_DATA_PATA_IXP4XX_H + +#include <linux/types.h> + +/* + * This structure provide a means for the board setup code + * to give information to th pata_ixp4xx driver. It is + * passed as platform_data. + */ +struct ixp4xx_pata_data { + volatile u32 *cs0_cfg; + volatile u32 *cs1_cfg; + unsigned long cs0_bits; + unsigned long cs1_bits; + void __iomem *cs0; + void __iomem *cs1; +}; + +#endif diff --git a/include/linux/soc/ixp4xx/cpu.h b/include/linux/soc/ixp4xx/cpu.h new file mode 100644 index 000000000000..88bd8de0e803 --- /dev/null +++ b/include/linux/soc/ixp4xx/cpu.h @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * IXP4XX cpu type detection + * + * Copyright (C) 2007 MontaVista Software, Inc. + */ + +#ifndef __SOC_IXP4XX_CPU_H__ +#define __SOC_IXP4XX_CPU_H__ + +#include <linux/io.h> +#ifdef CONFIG_ARM +#include <asm/cputype.h> +#endif + +/* Processor id value in CP15 Register 0 */ +#define IXP42X_PROCESSOR_ID_VALUE 0x690541c0 /* including unused 0x690541Ex */ +#define IXP42X_PROCESSOR_ID_MASK 0xffffffc0 + +#define IXP43X_PROCESSOR_ID_VALUE 0x69054040 +#define IXP43X_PROCESSOR_ID_MASK 0xfffffff0 + +#define IXP46X_PROCESSOR_ID_VALUE 0x69054200 /* including IXP455 */ +#define IXP46X_PROCESSOR_ID_MASK 0xfffffff0 + +/* "fuse" bits of IXP_EXP_CFG2 */ +/* All IXP4xx CPUs */ +#define IXP4XX_FEATURE_RCOMP (1 << 0) +#define IXP4XX_FEATURE_USB_DEVICE (1 << 1) +#define IXP4XX_FEATURE_HASH (1 << 2) +#define IXP4XX_FEATURE_AES (1 << 3) +#define IXP4XX_FEATURE_DES (1 << 4) +#define IXP4XX_FEATURE_HDLC (1 << 5) +#define IXP4XX_FEATURE_AAL (1 << 6) +#define IXP4XX_FEATURE_HSS (1 << 7) +#define IXP4XX_FEATURE_UTOPIA (1 << 8) +#define IXP4XX_FEATURE_NPEB_ETH0 (1 << 9) +#define IXP4XX_FEATURE_NPEC_ETH (1 << 10) +#define IXP4XX_FEATURE_RESET_NPEA (1 << 11) +#define IXP4XX_FEATURE_RESET_NPEB (1 << 12) +#define IXP4XX_FEATURE_RESET_NPEC (1 << 13) +#define IXP4XX_FEATURE_PCI (1 << 14) +#define IXP4XX_FEATURE_UTOPIA_PHY_LIMIT (3 << 16) +#define IXP4XX_FEATURE_XSCALE_MAX_FREQ (3 << 22) +#define IXP42X_FEATURE_MASK (IXP4XX_FEATURE_RCOMP | \ + IXP4XX_FEATURE_USB_DEVICE | \ + IXP4XX_FEATURE_HASH | \ + IXP4XX_FEATURE_AES | \ + IXP4XX_FEATURE_DES | \ + IXP4XX_FEATURE_HDLC | \ + IXP4XX_FEATURE_AAL | \ + IXP4XX_FEATURE_HSS | \ + IXP4XX_FEATURE_UTOPIA | \ + IXP4XX_FEATURE_NPEB_ETH0 | \ + IXP4XX_FEATURE_NPEC_ETH | \ + IXP4XX_FEATURE_RESET_NPEA | \ + IXP4XX_FEATURE_RESET_NPEB | \ + IXP4XX_FEATURE_RESET_NPEC | \ + IXP4XX_FEATURE_PCI | \ + IXP4XX_FEATURE_UTOPIA_PHY_LIMIT | \ + IXP4XX_FEATURE_XSCALE_MAX_FREQ) + + +/* IXP43x/46x CPUs */ +#define IXP4XX_FEATURE_ECC_TIMESYNC (1 << 15) +#define IXP4XX_FEATURE_USB_HOST (1 << 18) +#define IXP4XX_FEATURE_NPEA_ETH (1 << 19) +#define IXP43X_FEATURE_MASK (IXP42X_FEATURE_MASK | \ + IXP4XX_FEATURE_ECC_TIMESYNC | \ + IXP4XX_FEATURE_USB_HOST | \ + IXP4XX_FEATURE_NPEA_ETH) + +/* IXP46x CPU (including IXP455) only */ +#define IXP4XX_FEATURE_NPEB_ETH_1_TO_3 (1 << 20) +#define IXP4XX_FEATURE_RSA (1 << 21) +#define IXP46X_FEATURE_MASK (IXP43X_FEATURE_MASK | \ + IXP4XX_FEATURE_NPEB_ETH_1_TO_3 | \ + IXP4XX_FEATURE_RSA) + +#ifdef CONFIG_ARCH_IXP4XX +#define cpu_is_ixp42x_rev_a0() ((read_cpuid_id() & (IXP42X_PROCESSOR_ID_MASK | 0xF)) == \ + IXP42X_PROCESSOR_ID_VALUE) +#define cpu_is_ixp42x() ((read_cpuid_id() & IXP42X_PROCESSOR_ID_MASK) == \ + IXP42X_PROCESSOR_ID_VALUE) +#define cpu_is_ixp43x() ((read_cpuid_id() & IXP43X_PROCESSOR_ID_MASK) == \ + IXP43X_PROCESSOR_ID_VALUE) +#define cpu_is_ixp46x() ((read_cpuid_id() & IXP46X_PROCESSOR_ID_MASK) == \ + IXP46X_PROCESSOR_ID_VALUE) + +u32 ixp4xx_read_feature_bits(void); +void ixp4xx_write_feature_bits(u32 value); +#else +#define cpu_is_ixp42x_rev_a0() 0 +#define cpu_is_ixp42x() 0 +#define cpu_is_ixp43x() 0 +#define cpu_is_ixp46x() 0 +static inline u32 ixp4xx_read_feature_bits(void) +{ + return 0; +} +static inline void ixp4xx_write_feature_bits(u32 value) +{ +} +#endif + +#endif /* _ASM_ARCH_CPU_H */ |