* [RFC PATCH 0/5] PCI on the Amiga 4000
@ 2025-10-07 9:23 Daniel Palmer
2025-10-07 9:23 ` [RFC PATCH 1/5] m68k: Adjust the pci io range Daniel Palmer
` (4 more replies)
0 siblings, 5 replies; 11+ messages in thread
From: Daniel Palmer @ 2025-10-07 9:23 UTC (permalink / raw)
To: linux-m68k, linux-pci; +Cc: linux-kernel, Daniel Palmer
This series adds a driver for the Mediator 4000 PCI
bridge for the Amiga 4000.
Since this is my first PCI driver it's probably awful
so this is an RFC and also there is one interesting
unsolved bit:
As far as I can tell the Mediator 4000 cannot do DMA
between the normal memory and the PCI cards but PCI cards
can DMA between themselves. In the AmigaOS drivers a
bounce buffer is allocated on one of the cards that contains
memory, like a graphics card, and that is used for PCI
DMA. I'm not sure if that's even possible to do in Linux?
I've managed to use a network card that doesn't need DMA
so far, but I'm having trouble getting a Voodoo 3000 or
Radeon 9250 graphics card to come up properly. I guess
no one tests their cutting edge graphics drivers on non-x86
machines. ;)
Daniel Palmer (5):
m68k: Adjust the pci io range
m68k: Increase number of IRQs for Amiga to allow for PCI
m68k: amiga: Allow PCI
zorro: Add ids for Elbox Mediator 4000
PCI: Add driver for Elbox Mediator 4000 Zorro->PCI bridge
arch/m68k/Kconfig.machine | 1 +
arch/m68k/include/asm/io_mm.h | 9 +-
arch/m68k/include/asm/irq.h | 4 +-
drivers/pci/controller/Kconfig | 11 +
drivers/pci/controller/Makefile | 1 +
drivers/pci/controller/pci-mediator4000.c | 314 ++++++++++++++++++++++
drivers/zorro/zorro.ids | 2 +
7 files changed, 338 insertions(+), 4 deletions(-)
create mode 100644 drivers/pci/controller/pci-mediator4000.c
--
2.51.0
^ permalink raw reply [flat|nested] 11+ messages in thread* [RFC PATCH 1/5] m68k: Adjust the pci io range 2025-10-07 9:23 [RFC PATCH 0/5] PCI on the Amiga 4000 Daniel Palmer @ 2025-10-07 9:23 ` Daniel Palmer 2025-10-07 9:23 ` [RFC PATCH 2/5] m68k: Increase number of IRQs for Amiga to allow for PCI Daniel Palmer ` (3 subsequent siblings) 4 siblings, 0 replies; 11+ messages in thread From: Daniel Palmer @ 2025-10-07 9:23 UTC (permalink / raw) To: linux-m68k, linux-pci; +Cc: linux-kernel, Daniel Palmer For the Amiga at least the zorro->PCI bridges are in the zorro address space so currently the PCI IO range is not big enough for something in the zorro area to be in there. Signed-off-by: Daniel Palmer <daniel@thingy.jp> --- arch/m68k/include/asm/io_mm.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/arch/m68k/include/asm/io_mm.h b/arch/m68k/include/asm/io_mm.h index 090aec54b8fa..47aff2ba75f6 100644 --- a/arch/m68k/include/asm/io_mm.h +++ b/arch/m68k/include/asm/io_mm.h @@ -379,10 +379,13 @@ static inline void isa_delay(void) #define writesw(port, buf, nr) raw_outsw((port), (u16 *)(buf), (nr)) #define writesl(port, buf, nr) raw_outsl((port), (u32 *)(buf), (nr)) -#ifndef CONFIG_SUN3 -#define IO_SPACE_LIMIT 0xffff -#else +#if defined(CONFIG_SUN3) #define IO_SPACE_LIMIT 0x0fffffff +/* For the mediator we have io space somewhere in the Zorro 3 space */ +#elif defined(CONFIG_PCI_MEDIATOR4000) +#define IO_SPACE_LIMIT 0x7fffffff +#else +#define IO_SPACE_LIMIT 0xffff #endif #endif /* __KERNEL__ */ -- 2.51.0 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC PATCH 2/5] m68k: Increase number of IRQs for Amiga to allow for PCI 2025-10-07 9:23 [RFC PATCH 0/5] PCI on the Amiga 4000 Daniel Palmer 2025-10-07 9:23 ` [RFC PATCH 1/5] m68k: Adjust the pci io range Daniel Palmer @ 2025-10-07 9:23 ` Daniel Palmer 2025-10-07 9:23 ` [RFC PATCH 3/5] m68k: amiga: Allow PCI Daniel Palmer ` (2 subsequent siblings) 4 siblings, 0 replies; 11+ messages in thread From: Daniel Palmer @ 2025-10-07 9:23 UTC (permalink / raw) To: linux-m68k, linux-pci; +Cc: linux-kernel, Daniel Palmer According to the comment the Amiga has 24 interrupts but I needed 4 more to allocate irqs for the 4 PCI interrupts. Signed-off-by: Daniel Palmer <daniel@thingy.jp> --- arch/m68k/include/asm/irq.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/m68k/include/asm/irq.h b/arch/m68k/include/asm/irq.h index 2263e92d418a..ec944dc27710 100644 --- a/arch/m68k/include/asm/irq.h +++ b/arch/m68k/include/asm/irq.h @@ -24,7 +24,9 @@ #define NR_IRQS 72 #elif defined(CONFIG_Q40) #define NR_IRQS 43 -#elif defined(CONFIG_AMIGA) || !defined(CONFIG_MMU) +#elif defined(CONFIG_AMIGA) +#define NR_IRQS (32 + 4) +#elif !defined(CONFIG_MMU) #define NR_IRQS 32 #elif defined(CONFIG_APOLLO) #define NR_IRQS 24 -- 2.51.0 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC PATCH 3/5] m68k: amiga: Allow PCI 2025-10-07 9:23 [RFC PATCH 0/5] PCI on the Amiga 4000 Daniel Palmer 2025-10-07 9:23 ` [RFC PATCH 1/5] m68k: Adjust the pci io range Daniel Palmer 2025-10-07 9:23 ` [RFC PATCH 2/5] m68k: Increase number of IRQs for Amiga to allow for PCI Daniel Palmer @ 2025-10-07 9:23 ` Daniel Palmer 2025-10-07 9:37 ` Geert Uytterhoeven 2025-10-07 9:23 ` [RFC PATCH 4/5] zorro: Add ids for Elbox Mediator 4000 Daniel Palmer 2025-10-07 9:23 ` [RFC PATCH 5/5] PCI: Add driver for Elbox Mediator 4000 Zorro->PCI bridge Daniel Palmer 4 siblings, 1 reply; 11+ messages in thread From: Daniel Palmer @ 2025-10-07 9:23 UTC (permalink / raw) To: linux-m68k, linux-pci; +Cc: linux-kernel, Daniel Palmer The Amiga has various options for adding a PCI bus so select HAVE_PCI. Signed-off-by: Daniel Palmer <daniel@thingy.jp> --- arch/m68k/Kconfig.machine | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/m68k/Kconfig.machine b/arch/m68k/Kconfig.machine index de39f23b180e..b170ebf39273 100644 --- a/arch/m68k/Kconfig.machine +++ b/arch/m68k/Kconfig.machine @@ -7,6 +7,7 @@ config AMIGA bool "Amiga support" depends on MMU select LEGACY_TIMER_TICK + select HAVE_PCI help This option enables support for the Amiga series of computers. If you plan to use this kernel on an Amiga, say Y here and browse the -- 2.51.0 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [RFC PATCH 3/5] m68k: amiga: Allow PCI 2025-10-07 9:23 ` [RFC PATCH 3/5] m68k: amiga: Allow PCI Daniel Palmer @ 2025-10-07 9:37 ` Geert Uytterhoeven 2025-10-07 9:41 ` John Paul Adrian Glaubitz 0 siblings, 1 reply; 11+ messages in thread From: Geert Uytterhoeven @ 2025-10-07 9:37 UTC (permalink / raw) To: Daniel Palmer; +Cc: linux-m68k, linux-pci, linux-kernel Hi Daniel, On Tue, 7 Oct 2025 at 11:33, Daniel Palmer <daniel@thingy.jp> wrote: > The Amiga has various options for adding a PCI bus so select HAVE_PCI. > > Signed-off-by: Daniel Palmer <daniel@thingy.jp> Thanks for your patch! > --- a/arch/m68k/Kconfig.machine > +++ b/arch/m68k/Kconfig.machine > @@ -7,6 +7,7 @@ config AMIGA > bool "Amiga support" > depends on MMU > select LEGACY_TIMER_TICK > + select HAVE_PCI > help > This option enables support for the Amiga series of computers. If > you plan to use this kernel on an Amiga, say Y here and browse the This doesn't make much sense without upstream support for actual PCI host bridge controllers. Gr{oetje,eeting}s, Geert -- Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that. -- Linus Torvalds ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC PATCH 3/5] m68k: amiga: Allow PCI 2025-10-07 9:37 ` Geert Uytterhoeven @ 2025-10-07 9:41 ` John Paul Adrian Glaubitz 2025-10-07 10:22 ` Geert Uytterhoeven 0 siblings, 1 reply; 11+ messages in thread From: John Paul Adrian Glaubitz @ 2025-10-07 9:41 UTC (permalink / raw) To: Geert Uytterhoeven, Daniel Palmer; +Cc: linux-m68k, linux-pci, linux-kernel Hi Geert, On Tue, 2025-10-07 at 11:37 +0200, Geert Uytterhoeven wrote: > Hi Daniel, > > On Tue, 7 Oct 2025 at 11:33, Daniel Palmer <daniel@thingy.jp> wrote: > > The Amiga has various options for adding a PCI bus so select HAVE_PCI. > > > > Signed-off-by: Daniel Palmer <daniel@thingy.jp> > > Thanks for your patch! > > > --- a/arch/m68k/Kconfig.machine > > +++ b/arch/m68k/Kconfig.machine > > @@ -7,6 +7,7 @@ config AMIGA > > bool "Amiga support" > > depends on MMU > > select LEGACY_TIMER_TICK > > + select HAVE_PCI > > help > > This option enables support for the Amiga series of computers. If > > you plan to use this kernel on an Amiga, say Y here and browse the > > This doesn't make much sense without upstream support for actual > PCI host bridge controllers. Isn't this what patch 5 does? Adrian -- .''`. John Paul Adrian Glaubitz : :' : Debian Developer `. `' Physicist `- GPG: 62FF 8A75 84E0 2956 9546 0006 7426 3B37 F5B5 F913 ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC PATCH 3/5] m68k: amiga: Allow PCI 2025-10-07 9:41 ` John Paul Adrian Glaubitz @ 2025-10-07 10:22 ` Geert Uytterhoeven 2025-10-07 11:23 ` Daniel Palmer 0 siblings, 1 reply; 11+ messages in thread From: Geert Uytterhoeven @ 2025-10-07 10:22 UTC (permalink / raw) To: John Paul Adrian Glaubitz Cc: Daniel Palmer, linux-m68k, linux-pci, linux-kernel Hi Adrian, On Tue, 7 Oct 2025 at 11:41, John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de> wrote: > On Tue, 2025-10-07 at 11:37 +0200, Geert Uytterhoeven wrote: > > On Tue, 7 Oct 2025 at 11:33, Daniel Palmer <daniel@thingy.jp> wrote: > > > The Amiga has various options for adding a PCI bus so select HAVE_PCI. > > > > > > Signed-off-by: Daniel Palmer <daniel@thingy.jp> > > > > Thanks for your patch! > > > > > --- a/arch/m68k/Kconfig.machine > > > +++ b/arch/m68k/Kconfig.machine > > > @@ -7,6 +7,7 @@ config AMIGA > > > bool "Amiga support" > > > depends on MMU > > > select LEGACY_TIMER_TICK > > > + select HAVE_PCI > > > help > > > This option enables support for the Amiga series of computers. If > > > you plan to use this kernel on an Amiga, say Y here and browse the > > > > This doesn't make much sense without upstream support for actual > > PCI host bridge controllers. > > Isn't this what patch 5 does? Oops, sorry, I hadn't realized this is part of a series, as I somehow haven't received the other patches from the series yet... Gr{oetje,eeting}s, Geert -- Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that. -- Linus Torvalds ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC PATCH 3/5] m68k: amiga: Allow PCI 2025-10-07 10:22 ` Geert Uytterhoeven @ 2025-10-07 11:23 ` Daniel Palmer 0 siblings, 0 replies; 11+ messages in thread From: Daniel Palmer @ 2025-10-07 11:23 UTC (permalink / raw) To: Geert Uytterhoeven Cc: John Paul Adrian Glaubitz, linux-m68k, linux-pci, linux-kernel Hi Geert, Adrian, On Tue, 7 Oct 2025 at 19:22, Geert Uytterhoeven <geert@linux-m68k.org> wrote: > > Hi Adrian, > > On Tue, 7 Oct 2025 at 11:41, John Paul Adrian Glaubitz > <glaubitz@physik.fu-berlin.de> wrote: > > On Tue, 2025-10-07 at 11:37 +0200, Geert Uytterhoeven wrote: > > > On Tue, 7 Oct 2025 at 11:33, Daniel Palmer <daniel@thingy.jp> wrote: > > Isn't this what patch 5 does? Sorry, I guess the ordering could have been better... There is also a reference to the Kconfig symbol added in patch 5 in one of the earlier patches. If this has a hope of being merged I'll fix that for the next try. Thanks, Daniel ^ permalink raw reply [flat|nested] 11+ messages in thread
* [RFC PATCH 4/5] zorro: Add ids for Elbox Mediator 4000 2025-10-07 9:23 [RFC PATCH 0/5] PCI on the Amiga 4000 Daniel Palmer ` (2 preceding siblings ...) 2025-10-07 9:23 ` [RFC PATCH 3/5] m68k: amiga: Allow PCI Daniel Palmer @ 2025-10-07 9:23 ` Daniel Palmer 2025-10-07 9:23 ` [RFC PATCH 5/5] PCI: Add driver for Elbox Mediator 4000 Zorro->PCI bridge Daniel Palmer 4 siblings, 0 replies; 11+ messages in thread From: Daniel Palmer @ 2025-10-07 9:23 UTC (permalink / raw) To: linux-m68k, linux-pci; +Cc: linux-kernel, Daniel Palmer Add the two ids for the Elbox Mediator 4000. There are two because it presents as two boards: - One that contains the control registers, PCI config + io space - Another that contains a window that contains the PCI memory space Signed-off-by: Daniel Palmer <daniel@thingy.jp> --- drivers/zorro/zorro.ids | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/zorro/zorro.ids b/drivers/zorro/zorro.ids index 119abea8c6cb..fda1db905412 100644 --- a/drivers/zorro/zorro.ids +++ b/drivers/zorro/zorro.ids @@ -369,6 +369,8 @@ 1900 FastATA 4000 [IDE Interface] 1d00 FastATA 4000 [IDE Interface] 1e00 FastATA ZIV [IDE Interface] + 2100 Mediator 4000 (Control) [PCI bridge] + a100 Mediator 4000 (Window) [PCI bridge] 0a00 Harms Professional 1000 030 Plus [Accelerator] d000 3500 Professional [Accelerator and RAM Expansion] -- 2.51.0 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC PATCH 5/5] PCI: Add driver for Elbox Mediator 4000 Zorro->PCI bridge 2025-10-07 9:23 [RFC PATCH 0/5] PCI on the Amiga 4000 Daniel Palmer ` (3 preceding siblings ...) 2025-10-07 9:23 ` [RFC PATCH 4/5] zorro: Add ids for Elbox Mediator 4000 Daniel Palmer @ 2025-10-07 9:23 ` Daniel Palmer 2025-10-08 10:35 ` Ilpo Järvinen 4 siblings, 1 reply; 11+ messages in thread From: Daniel Palmer @ 2025-10-07 9:23 UTC (permalink / raw) To: linux-m68k, linux-pci; +Cc: linux-kernel, Daniel Palmer The Mediator 4000 is pretty simple: - There is one "board" in the zorro space that has 2 registers to set the base address of the PCI memory address space in the machine address space and handle interrupts, and then windows to address the PCI config space and PCI IO space. - Another board that contains the PCI memory address space inside of a window of 256MB or 512MB (configured by a jumper). Since the hardware has no official documentation I cooked this up using the WinUAE emulated version to work out how it mostly works then confirmed it still worked on my real Amiga 4000. Signed-off-by: Daniel Palmer <daniel@thingy.jp> --- drivers/pci/controller/Kconfig | 11 + drivers/pci/controller/Makefile | 1 + drivers/pci/controller/pci-mediator4000.c | 314 ++++++++++++++++++++++ 3 files changed, 326 insertions(+) create mode 100644 drivers/pci/controller/pci-mediator4000.c diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig index 41748d083b93..3fb977318123 100644 --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig @@ -215,6 +215,17 @@ config PCIE_MEDIATEK_GEN3 Say Y here if you want to enable Gen3 PCIe controller support on MediaTek SoCs. +config PCI_MEDIATOR4000 + tristate "Elbox Mediator 4000 Zorro->PCI bridge" + depends on AMIGA + select IRQ_DOMAIN + help + Adds support for the Elbox Mediator 4000 Zorro->PCI bridge for + the Amiga 4000. + + Say Y here if you are one of the few people that has this hardware + and run Linux on it. + config PCIE_MT7621 tristate "MediaTek MT7621 PCIe controller" depends on SOC_MT7621 || COMPILE_TEST diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile index 038ccbd9e3ba..03cd529716ec 100644 --- a/drivers/pci/controller/Makefile +++ b/drivers/pci/controller/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_PCI_LOONGSON) += pci-loongson.o obj-$(CONFIG_PCIE_HISI_ERR) += pcie-hisi-error.o obj-$(CONFIG_PCIE_APPLE) += pcie-apple.o obj-$(CONFIG_PCIE_MT7621) += pcie-mt7621.o +obj-$(CONFIG_PCI_MEDIATOR4000) += pci-mediator4000.o # pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW obj-y += dwc/ diff --git a/drivers/pci/controller/pci-mediator4000.c b/drivers/pci/controller/pci-mediator4000.c new file mode 100644 index 000000000000..106cde263e2c --- /dev/null +++ b/drivers/pci/controller/pci-mediator4000.c @@ -0,0 +1,314 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 Daniel Palmer <daniel@thingy.jp> + */ + +#include <asm/amigaints.h> +#include <linux/irqdomain.h> +#include <linux/pci.h> +#include <linux/slab.h> +#include <linux/zorro.h> + +/* Offsets etc */ +#define MEDIATOR4000_CONTROL 0x0 +#define MEDIATOR4000_CONTROL_SIZE 0x4 +#define MEDIATOR4000_CONTROL_WINDOW 0x0 +#define MEDIATOR4000_CONTROL_WINDOW_SHIFT 0x18 +#define MEDIATOR4000_CONTROL_IRQ 0x4 +#define MEDIATOR4000_CONTROL_IRQ_MASK_SHIFT 0x4 +#define MEDIATOR4000_PCICONF 0x800000 +#define MEDIATOR4000_PCICONF_SIZE 0x400000 +#define MEDIATOR4000_PCICONF_DEV_STRIDE 0x800 +#define MEDIATOR4000_PCICONF_FUNC_STRIDE 0x100 +#define MEDIATOR4000_PCIIO 0xc00000 +#define MEDIATOR4000_PCIIO_SIZE 0x100000 + +/* How the Amiga sees the mediator 4000 */ +#define MEDIATOR4000_IRQ IRQ_AMIGA_PORTS +#define MEDIATOR4000_ID_CONTROL ZORRO_ID(ELBOX_COMPUTER, 0x21, 0) +#define MEDIATOR4000_ID_WINDOW ZORRO_ID(ELBOX_COMPUTER, 0x21 | 0x80, 0) + +/* + * There are a few versions of the PCI backplane board, + * at most there are 6 slots it seems. + */ +#define MEDIATOR4000_MAX_SLOTS 6 + +#define MEDIATOR4000_PCICONF_DEVFUNC_OFF(priv, devfn) \ + (priv->config_base + \ + (MEDIATOR4000_PCICONF_DEV_STRIDE * PCI_SLOT(devfn)) + \ + (MEDIATOR4000_PCICONF_FUNC_STRIDE * PCI_FUNC(devfn))) + +struct pci_mediator4000_priv { + struct resource pciio_res; + unsigned long config_base; + unsigned long setup_base; + struct irq_domain *irqdomain; + int irqmap[PCI_NUM_INTX]; +}; + +static int pci_mediator4000_readconfig(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *value) +{ + struct pci_mediator4000_priv *priv = bus->sysdata; + unsigned long addr = MEDIATOR4000_PCICONF_DEVFUNC_OFF(priv, devfn) + where; + u32 v; + + if (PCI_SLOT(devfn) >= MEDIATOR4000_MAX_SLOTS) + return PCIBIOS_FUNC_NOT_SUPPORTED; + + /* Apparently only reading longs works */ + v = z_readl(addr & ~0x3); + + switch (size) { + case 4: + v = le32_to_cpu(v); + break; + case 2: + v = le16_to_cpu(((u16 *)(&v))[(addr & 0x3) >> 1]); + break; + case 1: + v = ((u8 *)&v)[addr & 0x3]; + break; + default: + return PCIBIOS_FUNC_NOT_SUPPORTED; + } + + *value = v; + + return PCIBIOS_SUCCESSFUL; +} + +static int pci_mediator4000_writeconfig(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 value) +{ + struct pci_mediator4000_priv *priv = bus->sysdata; + unsigned long addr = MEDIATOR4000_PCICONF_DEVFUNC_OFF(priv, devfn) + where; + u32 v; + + if (PCI_SLOT(devfn) >= MEDIATOR4000_MAX_SLOTS) + return PCIBIOS_FUNC_NOT_SUPPORTED; + + /* If its a long just write it */ + if (size == 4) { + v = cpu_to_le32(value); + z_writel(v, addr); + return PCIBIOS_SUCCESSFUL; + } + + /* Not a long, do RMW */ + v = z_readl(addr & ~0x3); + + switch (size) { + case 2: + ((u16 *)(&v))[(addr & 0x3) >> 1] = cpu_to_le16((u16)value); + break; + case 1: + ((u8 *)(&v))[addr & 0x3] = value; + break; + default: + return PCIBIOS_FUNC_NOT_SUPPORTED; + } + + z_writel(v, addr & ~0x3); + + return PCIBIOS_SUCCESSFUL; +} + +static irqreturn_t pci_mediator4000_irq(int irq, void *dev_id) +{ + struct pci_mediator4000_priv *priv = dev_id; + u8 v = z_readb(priv->setup_base + MEDIATOR4000_CONTROL_IRQ); + unsigned long mask = v & 0xf; + int i; + + for_each_set_bit(i, &mask, PCI_NUM_INTX) + generic_handle_domain_irq(priv->irqdomain, i); + + return IRQ_HANDLED; +} + +static int pci_mediator4000_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +{ + struct pci_mediator4000_priv *priv = dev->bus->sysdata; + + /* + * I'm not actually sure how the lines are wired on + * mediators with more than 4 slots. My 4 slot board seems to just + * have slot number == irq. + */ + return priv->irqmap[slot % PCI_NUM_INTX]; +} + +static struct pci_ops pci_mediator4000_ops = { + .read = pci_mediator4000_readconfig, + .write = pci_mediator4000_writeconfig, +}; + +static struct resource mediator4000_busn = { + .name = "mediator4000 busn", + .start = 0, + .end = 0, + .flags = IORESOURCE_BUS, +}; + +static void pci_mediator4000_mask_irq(struct irq_data *d) +{ + struct pci_mediator4000_priv *priv = irq_data_get_irq_chip_data(d); + u8 v = z_readb(priv->setup_base + MEDIATOR4000_CONTROL_IRQ); + + v &= ~(BIT(irqd_to_hwirq(d)) << MEDIATOR4000_CONTROL_IRQ_MASK_SHIFT); + z_writeb(v, priv->setup_base + MEDIATOR4000_CONTROL_IRQ); +} + +static void pci_mediator4000_unmask_irq(struct irq_data *d) +{ + struct pci_mediator4000_priv *priv = irq_data_get_irq_chip_data(d); + u8 v = z_readb(priv->setup_base + MEDIATOR4000_CONTROL_IRQ); + + v |= (BIT(irqd_to_hwirq(d)) << MEDIATOR4000_CONTROL_IRQ_MASK_SHIFT); + z_writeb(v, priv->setup_base + MEDIATOR4000_CONTROL_IRQ); +} + +static struct irq_chip pci_mediator4000_irq_chip = { + .name = "mediator4000", + .irq_mask = pci_mediator4000_mask_irq, + .irq_unmask = pci_mediator4000_unmask_irq, +}; + +static int pci_mediator4000_irq_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_chip_and_handler(irq, &pci_mediator4000_irq_chip, handle_level_irq); + irq_set_chip_data(irq, domain->host_data); + + return 0; +} + +static const struct irq_domain_ops pci_mediator4000_irqdomain_ops = { + .map = pci_mediator4000_irq_map, +}; + +static int mediator4000_setup(struct device *dev, + struct zorro_dev *m4k_control, + struct zorro_dev *m4k_window) +{ + unsigned long control_base = m4k_control->resource.start; + struct pci_mediator4000_priv *priv; + struct pci_host_bridge *bridge; + struct fwnode_handle *fwnode; + struct resource *res; + int i, ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + ret = devm_request_irq(dev, MEDIATOR4000_IRQ, pci_mediator4000_irq, + IRQF_SHARED, "mediator4000", priv); + if (ret) + return -ENODEV; + + bridge = devm_pci_alloc_host_bridge(dev, 0); + if (!bridge) + return -ENOMEM; + + res = devm_request_mem_region(dev, control_base + MEDIATOR4000_CONTROL, + MEDIATOR4000_CONTROL_SIZE, "mediator4000-registers"); + if (!res) + return -ENOMEM; + + priv->setup_base = res->start; + + /* Setup the window base */ + z_writeb((m4k_window->resource.start >> MEDIATOR4000_CONTROL_WINDOW_SHIFT) & 0xf0, + priv->setup_base + MEDIATOR4000_CONTROL_WINDOW); + + res = devm_request_mem_region(dev, control_base + MEDIATOR4000_PCICONF, + MEDIATOR4000_PCICONF_SIZE, "mediator4000-pci-config"); + if (!res) + return -ENOMEM; + + priv->config_base = res->start; + + res = devm_request_mem_region(dev, control_base + MEDIATOR4000_PCIIO, + MEDIATOR4000_PCIIO_SIZE, "mediator4000-pci-io"); + if (!res) + return -ENOMEM; + + priv->pciio_res.name = "mediator4000-pci-io", + priv->pciio_res.flags = IORESOURCE_IO, + priv->pciio_res.start = res->start; + priv->pciio_res.end = res->end; + res = insert_resource_conflict(&ioport_resource, &priv->pciio_res); + if (res) + return -ENOMEM; + + pci_add_resource_offset(&bridge->windows, &priv->pciio_res, priv->pciio_res.start); + pci_add_resource(&bridge->windows, &m4k_window->resource); + pci_add_resource(&bridge->windows, &mediator4000_busn); + + /* fixme: PCI DMA can only happen inside the window? How to enforce that? */ + + bridge->sysdata = priv; + bridge->ops = &pci_mediator4000_ops; + bridge->swizzle_irq = pci_common_swizzle; + bridge->map_irq = pci_mediator4000_map_irq; + + fwnode = irq_domain_alloc_named_fwnode("mediator4000"); + if (!fwnode) + return -ENOMEM; + + priv->irqdomain = irq_domain_create_linear(fwnode, + PCI_NUM_INTX, + &pci_mediator4000_irqdomain_ops, + priv); + if (!priv->irqdomain) { + ret = -ENOMEM; + goto err; + } + + for (i = 0; i < PCI_NUM_INTX; i++) + priv->irqmap[i] = irq_create_mapping(priv->irqdomain, i); + + ret = pci_host_probe(bridge); + if (!ret) + return 0; + +err: + irq_domain_free_fwnode(fwnode); + return ret; +} + +static int pci_mediator4000_probe(struct zorro_dev *m4k_control, + const struct zorro_device_id *ent) +{ + struct device *dev = &m4k_control->dev; + struct zorro_dev *m4k_window; + + m4k_window = zorro_find_device(MEDIATOR4000_ID_WINDOW, m4k_control); + if (!m4k_window) { + dev_err(&m4k_control->dev, "Could not find window board\n"); + return -ENODEV; + } + + return mediator4000_setup(dev, m4k_control, m4k_window); +} + +static const struct zorro_device_id pci_mediator4000_zorro_tbl[] = { + { + .id = MEDIATOR4000_ID_CONTROL, + }, + { 0 } +}; +MODULE_DEVICE_TABLE(zorro, pci_mediator4000_zorro_tbl); + +static struct zorro_driver pci_mediator4000_driver = { + .name = "pci_mediator4000", + .id_table = pci_mediator4000_zorro_tbl, + .probe = pci_mediator4000_probe, +}; + +module_driver(pci_mediator4000_driver, + zorro_register_driver, + zorro_unregister_driver); -- 2.51.0 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [RFC PATCH 5/5] PCI: Add driver for Elbox Mediator 4000 Zorro->PCI bridge 2025-10-07 9:23 ` [RFC PATCH 5/5] PCI: Add driver for Elbox Mediator 4000 Zorro->PCI bridge Daniel Palmer @ 2025-10-08 10:35 ` Ilpo Järvinen 0 siblings, 0 replies; 11+ messages in thread From: Ilpo Järvinen @ 2025-10-08 10:35 UTC (permalink / raw) To: Daniel Palmer; +Cc: linux-m68k, linux-pci, LKML On Tue, 7 Oct 2025, Daniel Palmer wrote: > The Mediator 4000 is pretty simple: > - There is one "board" in the zorro space that has 2 registers to > set the base address of the PCI memory address space in the > machine address space and handle interrupts, and then windows > to address the PCI config space and PCI IO space. > > - Another board that contains the PCI memory address space inside > of a window of 256MB or 512MB (configured by a jumper). > > Since the hardware has no official documentation I cooked this > up using the WinUAE emulated version to work out how it mostly > works then confirmed it still worked on my real Amiga 4000. > > Signed-off-by: Daniel Palmer <daniel@thingy.jp> > --- > drivers/pci/controller/Kconfig | 11 + > drivers/pci/controller/Makefile | 1 + > drivers/pci/controller/pci-mediator4000.c | 314 ++++++++++++++++++++++ > 3 files changed, 326 insertions(+) > create mode 100644 drivers/pci/controller/pci-mediator4000.c > > diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig > index 41748d083b93..3fb977318123 100644 > --- a/drivers/pci/controller/Kconfig > +++ b/drivers/pci/controller/Kconfig > @@ -215,6 +215,17 @@ config PCIE_MEDIATEK_GEN3 > Say Y here if you want to enable Gen3 PCIe controller support on > MediaTek SoCs. > > +config PCI_MEDIATOR4000 > + tristate "Elbox Mediator 4000 Zorro->PCI bridge" > + depends on AMIGA > + select IRQ_DOMAIN > + help > + Adds support for the Elbox Mediator 4000 Zorro->PCI bridge for > + the Amiga 4000. > + > + Say Y here if you are one of the few people that has this hardware > + and run Linux on it. > + > config PCIE_MT7621 > tristate "MediaTek MT7621 PCIe controller" > depends on SOC_MT7621 || COMPILE_TEST > diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile > index 038ccbd9e3ba..03cd529716ec 100644 > --- a/drivers/pci/controller/Makefile > +++ b/drivers/pci/controller/Makefile > @@ -39,6 +39,7 @@ obj-$(CONFIG_PCI_LOONGSON) += pci-loongson.o > obj-$(CONFIG_PCIE_HISI_ERR) += pcie-hisi-error.o > obj-$(CONFIG_PCIE_APPLE) += pcie-apple.o > obj-$(CONFIG_PCIE_MT7621) += pcie-mt7621.o > +obj-$(CONFIG_PCI_MEDIATOR4000) += pci-mediator4000.o > > # pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW > obj-y += dwc/ > diff --git a/drivers/pci/controller/pci-mediator4000.c b/drivers/pci/controller/pci-mediator4000.c > new file mode 100644 > index 000000000000..106cde263e2c > --- /dev/null > +++ b/drivers/pci/controller/pci-mediator4000.c > @@ -0,0 +1,314 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (C) 2025 Daniel Palmer <daniel@thingy.jp> > + */ > + > +#include <asm/amigaints.h> > +#include <linux/irqdomain.h> > +#include <linux/pci.h> > +#include <linux/slab.h> > +#include <linux/zorro.h> > + > +/* Offsets etc */ > +#define MEDIATOR4000_CONTROL 0x0 > +#define MEDIATOR4000_CONTROL_SIZE 0x4 > +#define MEDIATOR4000_CONTROL_WINDOW 0x0 > +#define MEDIATOR4000_CONTROL_WINDOW_SHIFT 0x18 Define the field with GENMASK(). Usually SHIFT defines are unnecessary. Down- and upshifting can be done with FIELD_GET/PREP() if needed. > +#define MEDIATOR4000_CONTROL_IRQ 0x4 > +#define MEDIATOR4000_CONTROL_IRQ_MASK_SHIFT 0x4 GENMASK()? > +#define MEDIATOR4000_PCICONF 0x800000 > +#define MEDIATOR4000_PCICONF_SIZE 0x400000 > +#define MEDIATOR4000_PCICONF_DEV_STRIDE 0x800 > +#define MEDIATOR4000_PCICONF_FUNC_STRIDE 0x100 > +#define MEDIATOR4000_PCIIO 0xc00000 > +#define MEDIATOR4000_PCIIO_SIZE 0x100000 Use SZ_* from linux/sizes.h > +/* How the Amiga sees the mediator 4000 */ > +#define MEDIATOR4000_IRQ IRQ_AMIGA_PORTS > +#define MEDIATOR4000_ID_CONTROL ZORRO_ID(ELBOX_COMPUTER, 0x21, 0) > +#define MEDIATOR4000_ID_WINDOW ZORRO_ID(ELBOX_COMPUTER, 0x21 | 0x80, 0) > + > +/* > + * There are a few versions of the PCI backplane board, > + * at most there are 6 slots it seems. > + */ > +#define MEDIATOR4000_MAX_SLOTS 6 > + > +#define MEDIATOR4000_PCICONF_DEVFUNC_OFF(priv, devfn) \ > + (priv->config_base + \ > + (MEDIATOR4000_PCICONF_DEV_STRIDE * PCI_SLOT(devfn)) + \ > + (MEDIATOR4000_PCICONF_FUNC_STRIDE * PCI_FUNC(devfn))) > + > +struct pci_mediator4000_priv { > + struct resource pciio_res; > + unsigned long config_base; > + unsigned long setup_base; > + struct irq_domain *irqdomain; > + int irqmap[PCI_NUM_INTX]; > +}; > + > +static int pci_mediator4000_readconfig(struct pci_bus *bus, unsigned int devfn, > + int where, int size, u32 *value) > +{ > + struct pci_mediator4000_priv *priv = bus->sysdata; > + unsigned long addr = MEDIATOR4000_PCICONF_DEVFUNC_OFF(priv, devfn) + where; > + u32 v; > + > + if (PCI_SLOT(devfn) >= MEDIATOR4000_MAX_SLOTS) > + return PCIBIOS_FUNC_NOT_SUPPORTED; > + > + /* Apparently only reading longs works */ > + v = z_readl(addr & ~0x3); > + > + switch (size) { > + case 4: > + v = le32_to_cpu(v); > + break; > + case 2: > + v = le16_to_cpu(((u16 *)(&v))[(addr & 0x3) >> 1]); > + break; > + case 1: > + v = ((u8 *)&v)[addr & 0x3]; > + break; > + default: > + return PCIBIOS_FUNC_NOT_SUPPORTED; > + } > + > + *value = v; > + > + return PCIBIOS_SUCCESSFUL; > +} > + > +static int pci_mediator4000_writeconfig(struct pci_bus *bus, unsigned int devfn, > + int where, int size, u32 value) > +{ > + struct pci_mediator4000_priv *priv = bus->sysdata; > + unsigned long addr = MEDIATOR4000_PCICONF_DEVFUNC_OFF(priv, devfn) + where; > + u32 v; > + > + if (PCI_SLOT(devfn) >= MEDIATOR4000_MAX_SLOTS) > + return PCIBIOS_FUNC_NOT_SUPPORTED; > + > + /* If its a long just write it */ > + if (size == 4) { > + v = cpu_to_le32(value); > + z_writel(v, addr); > + return PCIBIOS_SUCCESSFUL; > + } > + > + /* Not a long, do RMW */ > + v = z_readl(addr & ~0x3); > + > + switch (size) { > + case 2: > + ((u16 *)(&v))[(addr & 0x3) >> 1] = cpu_to_le16((u16)value); > + break; > + case 1: > + ((u8 *)(&v))[addr & 0x3] = value; > + break; > + default: > + return PCIBIOS_FUNC_NOT_SUPPORTED; > + } > + > + z_writel(v, addr & ~0x3); > + > + return PCIBIOS_SUCCESSFUL; > +} > + > +static irqreturn_t pci_mediator4000_irq(int irq, void *dev_id) > +{ > + struct pci_mediator4000_priv *priv = dev_id; > + u8 v = z_readb(priv->setup_base + MEDIATOR4000_CONTROL_IRQ); > + unsigned long mask = v & 0xf; What's this magic 0xf, should it use FIELD_GET() instead and name that field? I personally find this way of hiding logic into the variable declarations harder to read than if it would appear after them separately, it costs a few lines but this is simple and short function so it doesn't make things hard to follow. > + int i; > + > + for_each_set_bit(i, &mask, PCI_NUM_INTX) > + generic_handle_domain_irq(priv->irqdomain, i); > + > + return IRQ_HANDLED; > +} > + > +static int pci_mediator4000_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) > +{ > + struct pci_mediator4000_priv *priv = dev->bus->sysdata; > + > + /* > + * I'm not actually sure how the lines are wired on > + * mediators with more than 4 slots. My 4 slot board seems to just > + * have slot number == irq. > + */ > + return priv->irqmap[slot % PCI_NUM_INTX]; > +} > + > +static struct pci_ops pci_mediator4000_ops = { > + .read = pci_mediator4000_readconfig, > + .write = pci_mediator4000_writeconfig, > +}; > + > +static struct resource mediator4000_busn = { > + .name = "mediator4000 busn", > + .start = 0, > + .end = 0, > + .flags = IORESOURCE_BUS, > +}; > + > +static void pci_mediator4000_mask_irq(struct irq_data *d) > +{ > + struct pci_mediator4000_priv *priv = irq_data_get_irq_chip_data(d); > + u8 v = z_readb(priv->setup_base + MEDIATOR4000_CONTROL_IRQ); > + > + v &= ~(BIT(irqd_to_hwirq(d)) << MEDIATOR4000_CONTROL_IRQ_MASK_SHIFT); Use FIELD_PREP() instead of the SHIFT define. I'd also put these z_readb() calls right before this line (and not on the variable declaration line) as it's part of this RMW sequence (which is easy to pick up when they're grouped together). > + z_writeb(v, priv->setup_base + MEDIATOR4000_CONTROL_IRQ); > +} > + > +static void pci_mediator4000_unmask_irq(struct irq_data *d) > +{ > + struct pci_mediator4000_priv *priv = irq_data_get_irq_chip_data(d); > + u8 v = z_readb(priv->setup_base + MEDIATOR4000_CONTROL_IRQ); > + > + v |= (BIT(irqd_to_hwirq(d)) << MEDIATOR4000_CONTROL_IRQ_MASK_SHIFT); FIELD_PREP() > + z_writeb(v, priv->setup_base + MEDIATOR4000_CONTROL_IRQ); > +} > + > +static struct irq_chip pci_mediator4000_irq_chip = { > + .name = "mediator4000", > + .irq_mask = pci_mediator4000_mask_irq, > + .irq_unmask = pci_mediator4000_unmask_irq, > +}; > + > +static int pci_mediator4000_irq_map(struct irq_domain *domain, unsigned int irq, > + irq_hw_number_t hwirq) > +{ > + irq_set_chip_and_handler(irq, &pci_mediator4000_irq_chip, handle_level_irq); > + irq_set_chip_data(irq, domain->host_data); > + > + return 0; > +} > + > +static const struct irq_domain_ops pci_mediator4000_irqdomain_ops = { > + .map = pci_mediator4000_irq_map, > +}; > + > +static int mediator4000_setup(struct device *dev, > + struct zorro_dev *m4k_control, > + struct zorro_dev *m4k_window) > +{ > + unsigned long control_base = m4k_control->resource.start; > + struct pci_mediator4000_priv *priv; > + struct pci_host_bridge *bridge; > + struct fwnode_handle *fwnode; > + struct resource *res; > + int i, ret; > + > + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + > + ret = devm_request_irq(dev, MEDIATOR4000_IRQ, pci_mediator4000_irq, > + IRQF_SHARED, "mediator4000", priv); > + if (ret) > + return -ENODEV; > + > + bridge = devm_pci_alloc_host_bridge(dev, 0); > + if (!bridge) > + return -ENOMEM; > + > + res = devm_request_mem_region(dev, control_base + MEDIATOR4000_CONTROL, > + MEDIATOR4000_CONTROL_SIZE, "mediator4000-registers"); > + if (!res) > + return -ENOMEM; > + > + priv->setup_base = res->start; > + > + /* Setup the window base */ > + z_writeb((m4k_window->resource.start >> MEDIATOR4000_CONTROL_WINDOW_SHIFT) & 0xf0, FIELD_GET(). I'm not sure what that 0xf0 does, is it aligning for which we've ALIGN_DOWN()? > + priv->setup_base + MEDIATOR4000_CONTROL_WINDOW); > + > + res = devm_request_mem_region(dev, control_base + MEDIATOR4000_PCICONF, > + MEDIATOR4000_PCICONF_SIZE, "mediator4000-pci-config"); > + if (!res) > + return -ENOMEM; > + > + priv->config_base = res->start; > + > + res = devm_request_mem_region(dev, control_base + MEDIATOR4000_PCIIO, > + MEDIATOR4000_PCIIO_SIZE, "mediator4000-pci-io"); > + if (!res) > + return -ENOMEM; > + > + priv->pciio_res.name = "mediator4000-pci-io", > + priv->pciio_res.flags = IORESOURCE_IO, > + priv->pciio_res.start = res->start; > + priv->pciio_res.end = res->end; DEFINE_RES() > + res = insert_resource_conflict(&ioport_resource, &priv->pciio_res); > + if (res) > + return -ENOMEM; > + > + pci_add_resource_offset(&bridge->windows, &priv->pciio_res, priv->pciio_res.start); > + pci_add_resource(&bridge->windows, &m4k_window->resource); > + pci_add_resource(&bridge->windows, &mediator4000_busn); > + > + /* fixme: PCI DMA can only happen inside the window? How to enforce that? */ > + > + bridge->sysdata = priv; > + bridge->ops = &pci_mediator4000_ops; > + bridge->swizzle_irq = pci_common_swizzle; > + bridge->map_irq = pci_mediator4000_map_irq; > + > + fwnode = irq_domain_alloc_named_fwnode("mediator4000"); > + if (!fwnode) > + return -ENOMEM; > + > + priv->irqdomain = irq_domain_create_linear(fwnode, > + PCI_NUM_INTX, > + &pci_mediator4000_irqdomain_ops, > + priv); > + if (!priv->irqdomain) { > + ret = -ENOMEM; > + goto err; > + } > + > + for (i = 0; i < PCI_NUM_INTX; i++) > + priv->irqmap[i] = irq_create_mapping(priv->irqdomain, i); > + > + ret = pci_host_probe(bridge); > + if (!ret) Please reverse the logic. > + return 0; > + > +err: > + irq_domain_free_fwnode(fwnode); > + return ret; > +} > + > +static int pci_mediator4000_probe(struct zorro_dev *m4k_control, > + const struct zorro_device_id *ent) > +{ > + struct device *dev = &m4k_control->dev; > + struct zorro_dev *m4k_window; > + > + m4k_window = zorro_find_device(MEDIATOR4000_ID_WINDOW, m4k_control); > + if (!m4k_window) { > + dev_err(&m4k_control->dev, "Could not find window board\n"); > + return -ENODEV; > + } > + > + return mediator4000_setup(dev, m4k_control, m4k_window); > +} > + > +static const struct zorro_device_id pci_mediator4000_zorro_tbl[] = { > + { > + .id = MEDIATOR4000_ID_CONTROL, > + }, > + { 0 } > +}; > +MODULE_DEVICE_TABLE(zorro, pci_mediator4000_zorro_tbl); > + > +static struct zorro_driver pci_mediator4000_driver = { > + .name = "pci_mediator4000", > + .id_table = pci_mediator4000_zorro_tbl, > + .probe = pci_mediator4000_probe, > +}; > + > +module_driver(pci_mediator4000_driver, > + zorro_register_driver, > + zorro_unregister_driver); > -- i. ^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2025-10-08 10:35 UTC | newest] Thread overview: 11+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-10-07 9:23 [RFC PATCH 0/5] PCI on the Amiga 4000 Daniel Palmer 2025-10-07 9:23 ` [RFC PATCH 1/5] m68k: Adjust the pci io range Daniel Palmer 2025-10-07 9:23 ` [RFC PATCH 2/5] m68k: Increase number of IRQs for Amiga to allow for PCI Daniel Palmer 2025-10-07 9:23 ` [RFC PATCH 3/5] m68k: amiga: Allow PCI Daniel Palmer 2025-10-07 9:37 ` Geert Uytterhoeven 2025-10-07 9:41 ` John Paul Adrian Glaubitz 2025-10-07 10:22 ` Geert Uytterhoeven 2025-10-07 11:23 ` Daniel Palmer 2025-10-07 9:23 ` [RFC PATCH 4/5] zorro: Add ids for Elbox Mediator 4000 Daniel Palmer 2025-10-07 9:23 ` [RFC PATCH 5/5] PCI: Add driver for Elbox Mediator 4000 Zorro->PCI bridge Daniel Palmer 2025-10-08 10:35 ` Ilpo Järvinen
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).