Skip to contentSkip to conversation

s390/pci: PCI support on System z

RFC PATCH In review Jan Glauber 3 participants 15 messages Nov 14, 2012 – Dec 18, 2012

Subsystems (10)
lore
Share & tools
lore
#0 lore raw

cover s390/pci: PCI support on System z

Hi,

this patchset based on 3.7-rc4 aims at adding PCI(e) support to the System z (s390) architecture. PCI is currently not available on s390, so this is early prototype code.

I'm posting in the hope to get some feedback and direction on the more humble aspects of it.

Patches 01-05 are only split to ease the review, they belong functional together and don't make much sense without each other.

PCI on the mainframe differs in some aspects from PCI on other platforms.

The most notable difference is the use of instructions for memory mapped IO. So on s390 the BAR spaces cannot be mapped to memory. To access the BAR spaces privileged instructions must be used (how that will be addressed for user-space is not part of this patchset). See patch 01 for how the read/write pci primitives are mapped to the s390 instructions.

Another difference is that the addresses of BARs from two different PCI functions may be identical. That means we can't distinguish from an iomem address which PCI device the address belongs to. Therefore s390 needs a custom implementation of pci_iomap to remap iomem addresses using the bar and device information provided by pci_iomap.

Device naming - the topology on System z is that every PCI function is attached directly to a PCI root complex. Therefore we decided to use a domain number per function and leave the bus, slot and function 0.

Next one - IRQs. s390 will use its existing adapter interrupt infrastructure to deliver MSI/MSI-X interrupts. Legacy INTs are _not_ supported at all. The only thing that s390 needs is to close the gap between adapter interrupts and MSI, in other words to locate a struct msi_desc from an IRQ number. For that I've implemented a simple hash, see patch 04.

Then there is Kconfig, see patch 09, which gives s390 a bunch of options that we would rather avoid. Like VGA support, sound subsystem, etc. I do not have a solution how to disable these subsystems. We would like to limit the subsystems that become available on s390 because the usable PCI hardware will be limited to a small number of hand-picked and supported PCI cards.

There are several options I can imagine:

a) Don't disable anything - that would result in lots of drivers builtable on s390 that are not supported and weird error reports

b) Introduce negative options for whole subsystems like we have already with NO_IOPORT, so maybe NO_USB, NO_SOUND, ...

c) Introduce a special CONFIG_PCI_S390 and add that explicitely to all subsystems that we want to enable on s390

None of the above looks like the obvious winner to me.

Hope to get some feedback,
TIA, Jan

Jan Glauber (10):
s390/pci: base support
s390/pci: CLP interface
s390/bitops: find leftmost bit instruction support s390/pci: PCI adapter interrupts for MSI/MSI-X s390/pci: DMA support
s390/pci: CHSC PCI support for error and availability events s390/pci: PCI hotplug support via SCLP
s390/pci: s390 specific PCI sysfs attributes s390/pci: add PCI Kconfig options
vga: compile fix, disable vga for s390

 arch/s390/Kbuild                    |    1 +
 arch/s390/Kconfig                   |   56 +-
 arch/s390/include/asm/bitops.h      |   81 +++
 arch/s390/include/asm/clp.h         |   28 +
 arch/s390/include/asm/dma-mapping.h |   76 +++
 arch/s390/include/asm/dma.h         |   19 +-
 arch/s390/include/asm/hw_irq.h      |   22 +
 arch/s390/include/asm/io.h          |   55 +-
 arch/s390/include/asm/irq.h         |   12 +
 arch/s390/include/asm/isc.h         |    1 +
 arch/s390/include/asm/pci.h         |  156 ++++-
 arch/s390/include/asm/pci_clp.h     |  182 ++++++
 arch/s390/include/asm/pci_dma.h     |  196 +++++++
 arch/s390/include/asm/pci_insn.h    |  280 +++++++++
 arch/s390/include/asm/pci_io.h      |  194 +++++++
 arch/s390/include/asm/sclp.h        |    2 +
 arch/s390/kernel/dis.c              |   15 +
 arch/s390/kernel/irq.c              |    2 +
 arch/s390/pci/Makefile              |    6 +
 arch/s390/pci/pci.c                 | 1098 +++++++++++++++++++++++++++++++++++
 arch/s390/pci/pci_clp.c             |  324 +++++++++++
 arch/s390/pci/pci_dma.c             |  505 ++++++++++++++++
 arch/s390/pci/pci_event.c           |   93 +++
 arch/s390/pci/pci_msi.c             |  141 +++++
 arch/s390/pci/pci_sysfs.c           |   86 +++
 drivers/gpu/vga/Kconfig             |    2 +-
 drivers/pci/hotplug/Kconfig         |   11 +
 drivers/pci/hotplug/Makefile        |    1 +
 drivers/pci/hotplug/s390_pci_hpc.c  |  252 ++++++++
 drivers/pci/msi.c                   |   10 +-
 drivers/s390/char/sclp.h            |    3 +-
 drivers/s390/char/sclp_cmd.c        |   64 +-
 drivers/s390/cio/chsc.c             |  156 +++--
 include/asm-generic/io.h            |   21 +-
 include/linux/irq.h                 |   10 +-
 include/video/vga.h                 |    2 +
 36 files changed, 4084 insertions(+), 79 deletions(-)
 create mode 100644 arch/s390/include/asm/clp.h
 create mode 100644 arch/s390/include/asm/dma-mapping.h
 create mode 100644 arch/s390/include/asm/hw_irq.h
 create mode 100644 arch/s390/include/asm/pci_clp.h
 create mode 100644 arch/s390/include/asm/pci_dma.h
 create mode 100644 arch/s390/include/asm/pci_insn.h
 create mode 100644 arch/s390/include/asm/pci_io.h
 create mode 100644 arch/s390/pci/Makefile
 create mode 100644 arch/s390/pci/pci.c
 create mode 100644 arch/s390/pci/pci_clp.c
 create mode 100644 arch/s390/pci/pci_dma.c
 create mode 100644 arch/s390/pci/pci_event.c
 create mode 100644 arch/s390/pci/pci_msi.c
 create mode 100644 arch/s390/pci/pci_sysfs.c
 create mode 100644 drivers/pci/hotplug/s390_pci_hpc.c
Signature · 1 line
1.7.12.4
#7 lore raw

RFC PATCH 3/10 s390/bitops: find leftmost bit instruction support

The flogr instruction scans a bitmap starting from the leftmost bit. Implement support for these bitops. This could be useful to scan bitmaps like an interrupt vector set by the hardware starting at the leftmost bit.

---
 arch/s390/include/asm/bitops.h | 81 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 81 insertions(+)
arch/s390/include/asm/bitops.h +81 -0
index 6f57389..1542293 100644
@@ -640,6 +640,87 @@ static inline unsigned long find_first_bit(const unsigned long * addr,
 }
 #define find_first_bit find_first_bit
 
+/*
+ * Big endian variant whichs starts bit counting from left using
+ * the flogr (find leftmost one) instruction.
+ */
+static inline unsigned long __flo_word(unsigned long nr, unsigned long val)
+{
+	register unsigned long bit asm("2") = val;
+	register unsigned long out asm("3");
+
+	asm volatile (
+		"	.insn	rre,0xb9830000,%[bit],%[bit]\n"
+		: [bit] "+d" (bit), [out] "=d" (out) : : "cc");
+	return nr + bit;
+}
+
+/*
+ * 64 bit special left bitops format:
+ * order in memory:
+ *    00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
+ *    10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
+ *    20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f
+ *    30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f
+ * after that follows the next long with bit numbers
+ *    40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f
+ *    50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f
+ *    60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f
+ *    70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f
+ * The reason for this bit ordering is the fact that
+ * the hardware sets bits in a bitmap starting at bit 0
+ * and we don't want to scan the bitmap from the 'wrong
+ * end'.
+ */
+static inline unsigned long find_first_bit_left(const unsigned long *addr,
+						unsigned long size)
+{
+	unsigned long bytes, bits;
+
+	if (!size)
+		return 0;
+	bytes = __ffs_word_loop(addr, size);
+	bits = __flo_word(bytes * 8, __load_ulong_be(addr, bytes));
+	return (bits < size) ? bits : size;
+}
+
+static inline int find_next_bit_left(const unsigned long *addr,
+				     unsigned long size,
+				     unsigned long offset)
+{
+	const unsigned long *p;
+	unsigned long bit, set;
+
+	if (offset >= size)
+		return size;
+	bit = offset & (__BITOPS_WORDSIZE - 1);
+	offset -= bit;
+	size -= offset;
+	p = addr + offset / __BITOPS_WORDSIZE;
+	if (bit) {
+		set = __flo_word(0, *p & (~0UL << bit));
+		if (set >= size)
+			return size + offset;
+		if (set < __BITOPS_WORDSIZE)
+			return set + offset;
+		offset += __BITOPS_WORDSIZE;
+		size -= __BITOPS_WORDSIZE;
+		p++;
+	}
+	return offset + find_first_bit_left(p, size);
+}
+
+#define for_each_set_bit_left(bit, addr, size)				\
+	for ((bit) = find_first_bit_left((addr), (size));		\
+	     (bit) < (size);						\
+	     (bit) = find_next_bit_left((addr), (size), (bit) + 1))
+
+/* same as for_each_set_bit() but use bit as value to start with */
+#define for_each_set_bit_left_cont(bit, addr, size)			\
+	for ((bit) = find_next_bit_left((addr), (size), (bit));		\
+	     (bit) < (size);						\
+	     (bit) = find_next_bit_left((addr), (size), (bit) + 1))
+
 /**
  * find_next_zero_bit - find the first zero bit in a memory region
  * @addr: The address to base the search on
Signature · 1 line
1.7.12.4
#8 lore raw

RFC PATCH 4/10 s390/pci: PCI adapter interrupts for MSI/MSI-X

In reply to Jan Glauber

Support PCI adapter interrupts using the Single-IRQ-mode. Single-IRQ-mode disables an adapter IRQ automatically after delivering it until the SIC instruction enables it again. This is used to reduce the number of IRQs for streaming workloads.

Up to 64 MSI handlers can be registered per PCI function. A hash table is used to map interrupt numbers to MSI descriptors. The interrupt vector is scanned using the flogr instruction. Only MSI/MSI-X interrupts are supported, no legacy INTs.

---
 arch/s390/include/asm/hw_irq.h |  22 ++
 arch/s390/include/asm/irq.h    |  12 ++
 arch/s390/include/asm/isc.h    |   1 +
 arch/s390/include/asm/pci.h    |  27 +++
 arch/s390/kernel/irq.c         |   2 +
 arch/s390/pci/Makefile         |   2 +-
 arch/s390/pci/pci.c            | 464 ++++++++++++++++++++++++++++++++++++++++-
 arch/s390/pci/pci_clp.c        |   3 +
 arch/s390/pci/pci_msi.c        | 141 +++++++++++++
 drivers/pci/msi.c              |  10 +-
 include/linux/irq.h            |  10 +-
 11 files changed, 685 insertions(+), 9 deletions(-)
 create mode 100644 arch/s390/include/asm/hw_irq.h
 create mode 100644 arch/s390/pci/pci_msi.c
arch/s390/include/asm/hw_irq.h new file +22 -0
new file mode 100644
index 0000000..7e3d258
@@ -0,0 +1,22 @@
+#ifndef _HW_IRQ_H
+#define _HW_IRQ_H
+
+#include <linux/msi.h>
+#include <linux/pci.h>
+
+static inline struct msi_desc *irq_get_msi_desc(unsigned int irq)
+{
+	return __irq_get_msi_desc(irq);
+}
+
+/* Must be called with msi map lock held */
+static inline int irq_set_msi_desc(unsigned int irq, struct msi_desc *msi)
+{
+	if (!msi)
+		return -EINVAL;
+
+	msi->irq = irq;
+	return 0;
+}
+
+#endif
arch/s390/include/asm/irq.h +12 -0
index 6703dd9..e6972f8 100644
@@ -33,6 +33,8 @@ enum interruption_class {
 	IOINT_APB,
 	IOINT_ADM,
 	IOINT_CSC,
+	IOINT_PCI,
+	IOINT_MSI,
 	NMI_NMI,
 	NR_IRQS,
 };
@@ -51,4 +53,14 @@ void service_subclass_irq_unregister(void);
 void measurement_alert_subclass_register(void);
 void measurement_alert_subclass_unregister(void);
 
+#ifdef CONFIG_LOCKDEP
+#  define disable_irq_nosync_lockdep(irq)	disable_irq_nosync(irq)
+#  define disable_irq_nosync_lockdep_irqsave(irq, flags) \
+						disable_irq_nosync(irq)
+#  define disable_irq_lockdep(irq)		disable_irq(irq)
+#  define enable_irq_lockdep(irq)		enable_irq(irq)
+#  define enable_irq_lockdep_irqrestore(irq, flags) \
+						enable_irq(irq)
+#endif
+
 #endif /* _ASM_IRQ_H */
arch/s390/include/asm/isc.h +1 -0
index 5ae6064..68d7d68 100644
@@ -18,6 +18,7 @@
 #define CHSC_SCH_ISC 7			/* CHSC subchannels */
 /* Adapter interrupts. */
 #define QDIO_AIRQ_ISC IO_SCH_ISC	/* I/O subchannel in qdio mode */
+#define PCI_ISC 2			/* PCI I/O subchannels */
 #define AP_ISC 6			/* adjunct processor (crypto) devices */
 
 /* Functions for registration of I/O interruption subclasses */
arch/s390/include/asm/pci.h +27 -0
index 079b7e2..a9aadeb 100644
@@ -20,6 +20,10 @@ void pci_iounmap(struct pci_dev *, void __iomem *);
 int pci_domain_nr(struct pci_bus *);
 int pci_proc_domain(struct pci_bus *);
 
+/* MSI arch hooks */
+#define arch_setup_msi_irqs	arch_setup_msi_irqs
+#define arch_teardown_msi_irqs	arch_teardown_msi_irqs
+
 #define ZPCI_BUS_NR			0	/* default bus number */
 #define ZPCI_DEVFN			0	/* default device number */
 
@@ -29,6 +33,15 @@ int pci_proc_domain(struct pci_bus *);
 #define ZPCI_FC_BLOCKED			0x20
 #define ZPCI_FC_DMA_ENABLED		0x10
 
+struct msi_map {
+	unsigned long irq;
+	struct msi_desc *msi;
+	struct hlist_node msi_chain;
+};
+
+#define ZPCI_NR_MSI_VECS	64
+#define ZPCI_MSI_MASK		(ZPCI_NR_MSI_VECS - 1)
+
 enum zpci_state {
 	ZPCI_FN_STATE_RESERVED,
 	ZPCI_FN_STATE_STANDBY,
@@ -56,6 +69,12 @@ struct zpci_dev {
 	u8		pfgid;		/* function group ID */
 	u16		domain;
 
+	/* IRQ stuff */
+	u64		msi_addr;	/* MSI address */
+	struct zdev_irq_map *irq_map;
+	struct msi_map *msi_map[ZPCI_NR_MSI_VECS];
+	unsigned int	aisb;		/* number of the summary bit */
+
 	struct zpci_bar_struct bars[PCI_BAR_COUNT];
 
 	enum pci_bus_speed max_bus_speed;
@@ -83,6 +102,14 @@ int clp_add_pci_device(u32, u32, int);
 int clp_enable_fh(struct zpci_dev *, u8);
 int clp_disable_fh(struct zpci_dev *);
 
+/* MSI */
+struct msi_desc *__irq_get_msi_desc(unsigned int);
+int zpci_msi_set_mask_bits(struct msi_desc *, u32, u32);
+int zpci_setup_msi_irq(struct zpci_dev *, struct msi_desc *, unsigned int, int);
+void zpci_teardown_msi_irq(struct zpci_dev *, struct msi_desc *);
+int zpci_msihash_init(void);
+void zpci_msihash_exit(void);
+
 /* Helpers */
 struct zpci_dev *get_zdev(struct pci_dev *);
 struct zpci_dev *get_zdev_by_fid(u32);
arch/s390/kernel/irq.c +2 -0
index 6cdc55b..bf24293 100644
@@ -58,6 +58,8 @@ static const struct irq_class intrclass_names[] = {
 	[IOINT_APB]  = {.name = "APB", .desc = "[I/O] AP Bus"},
 	[IOINT_ADM]  = {.name = "ADM", .desc = "[I/O] EADM Subchannel"},
 	[IOINT_CSC]  = {.name = "CSC", .desc = "[I/O] CHSC Subchannel"},
+	[IOINT_PCI]  = {.name = "PCI", .desc = "[I/O] PCI Interrupt" },
+	[IOINT_MSI] =  {.name = "MSI", .desc = "[I/O] MSI Interrupt" },
 	[NMI_NMI]    = {.name = "NMI", .desc = "[NMI] Machine Check"},
 };
 
arch/s390/pci/Makefile +1 -1
index 1afd68c..628be7b 100644
@@ -2,4 +2,4 @@
 # Makefile for the s390 PCI subsystem.
 #
 
-obj-$(CONFIG_PCI)	+= pci.o pci_clp.o
+obj-$(CONFIG_PCI)	+= pci.o pci_clp.o pci_msi.o
arch/s390/pci/pci.c +463 -1

558 lines not on this page · raw message

arch/s390/pci/pci_clp.c +3 -0
index 291da1a..72694fb 100644
@@ -48,6 +48,9 @@ static void clp_free_block(void *ptr)
 static void clp_store_query_pci_fngrp(struct zpci_dev *zdev,
 				      struct clp_rsp_query_pci_grp *response)
 {
+	zdev->msi_addr = response->msia;
+
+	pr_debug("Supported number of MSI vectors: %u\n", response->noi);
 	switch (response->version) {
 	case 1:
 		zdev->max_bus_speed = PCIE_SPEED_5_0GT;
arch/s390/pci/pci_msi.c new file +141 -0

141 lines not on this page · raw message

drivers/pci/msi.c +8 -2
index a825d78..192e8ca 100644
@@ -207,6 +207,8 @@ static void msix_mask_irq(struct msi_desc *desc, u32 flag)
 	desc->masked = __msix_mask_irq(desc, flag);
 }
 
+#ifdef CONFIG_GENERIC_HARDIRQS
+
 static void msi_set_mask_bit(struct irq_data *data, u32 flag)
 {
 	struct msi_desc *desc = irq_data_get_msi(data);
@@ -230,6 +232,8 @@ void unmask_msi_irq(struct irq_data *data)
 	msi_set_mask_bit(data, 0);
 }
 
+#endif /* CONFIG_GENERIC_HARDIRQS */
+
 void __read_msi_msg(struct msi_desc *entry, struct msi_msg *msg)
 {
 	BUG_ON(entry->dev->current_state != PCI_D0);
@@ -333,12 +337,14 @@ static void free_msi_irqs(struct pci_dev *dev)
 	struct msi_desc *entry, *tmp;
 
 	list_for_each_entry(entry, &dev->msi_list, list) {
-		int i, nvec;
+		int nvec;
 		if (!entry->irq)
 			continue;
 		nvec = 1 << entry->msi_attrib.multiple;
-		for (i = 0; i < nvec; i++)
+#ifdef CONFIG_GENERIC_HARDIRQS
+		for (int i = 0; i < nvec; i++)
 			BUG_ON(irq_has_action(entry->irq + i));
+#endif
 	}
 
 	arch_teardown_msi_irqs(dev);
include/linux/irq.h +5 -5
index 216b0ba..e21ed83 100644
@@ -10,9 +10,6 @@
  */
 
 #include <linux/smp.h>
-
-#ifndef CONFIG_S390
-
 #include <linux/linkage.h>
 #include <linux/cache.h>
 #include <linux/spinlock.h>
@@ -737,8 +734,11 @@ static inline void irq_gc_lock(struct irq_chip_generic *gc) { }
 static inline void irq_gc_unlock(struct irq_chip_generic *gc) { }
 #endif
 
-#endif /* CONFIG_GENERIC_HARDIRQS */
+#else /* !CONFIG_GENERIC_HARDIRQS */
 
-#endif /* !CONFIG_S390 */
+extern struct msi_desc *irq_get_msi_desc(unsigned int irq);
+extern int irq_set_msi_desc(unsigned int irq, struct msi_desc *entry);
+
+#endif /* CONFIG_GENERIC_HARDIRQS */
 
 #endif /* _LINUX_IRQ_H */
Signature · 1 line
1.7.12.4
#9 lore raw

RFC PATCH 5/10 s390/pci: DMA support

In reply to Jan Glauber

Add DMA IOMMU support using 4K page table entries. Implement dma_map_ops.

---
 arch/s390/include/asm/dma-mapping.h |  76 ++++++
 arch/s390/include/asm/dma.h         |  19 +-
 arch/s390/include/asm/pci.h         |  21 ++
 arch/s390/include/asm/pci_dma.h     | 196 ++++++++++++++
 arch/s390/pci/Makefile              |   2 +-
 arch/s390/pci/pci.c                 |  36 +++
 arch/s390/pci/pci_clp.c             |   4 +
 arch/s390/pci/pci_dma.c             | 505 ++++++++++++++++++++++++++++++++++++
 8 files changed, 848 insertions(+), 11 deletions(-)
 create mode 100644 arch/s390/include/asm/dma-mapping.h
 create mode 100644 arch/s390/include/asm/pci_dma.h
 create mode 100644 arch/s390/pci/pci_dma.c
arch/s390/include/asm/dma-mapping.h new file +76 -0
new file mode 100644
index 0000000..aab2158
@@ -0,0 +1,76 @@
+#ifndef _ASM_S390_DMA_MAPPING_H
+#define _ASM_S390_DMA_MAPPING_H
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/scatterlist.h>
+#include <linux/dma-attrs.h>
+#include <linux/dma-debug.h>
+#include <linux/io.h>
+
+#define DMA_ERROR_CODE          (~(dma_addr_t) 0x0)
+
+extern struct dma_map_ops s390_dma_ops;
+
+static inline struct dma_map_ops *get_dma_ops(struct device *dev)
+{
+	return &s390_dma_ops;
+}
+
+extern int dma_set_mask(struct device *dev, u64 mask);
+extern int dma_is_consistent(struct device *dev, dma_addr_t dma_handle);
+extern void dma_cache_sync(struct device *dev, void *vaddr, size_t size,
+			   enum dma_data_direction direction);
+
+#define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f)
+#define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h)
+
+#include <asm-generic/dma-mapping-common.h>
+
+static inline int dma_supported(struct device *dev, u64 mask)
+{
+	struct dma_map_ops *dma_ops = get_dma_ops(dev);
+
+	if (dma_ops->dma_supported == NULL)
+		return 1;
+	return dma_ops->dma_supported(dev, mask);
+}
+
+static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
+{
+	if (!dev->dma_mask)
+		return 0;
+	return addr + size - 1 <= *dev->dma_mask;
+}
+
+static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
+{
+	struct dma_map_ops *dma_ops = get_dma_ops(dev);
+
+	if (dma_ops->mapping_error)
+		return dma_ops->mapping_error(dev, dma_addr);
+	return (dma_addr == 0UL);
+}
+
+static inline void *dma_alloc_coherent(struct device *dev, size_t size,
+				       dma_addr_t *dma_handle, gfp_t flag)
+{
+	struct dma_map_ops *ops = get_dma_ops(dev);
+	void *ret;
+
+	ret = ops->alloc(dev, size, dma_handle, flag, NULL);
+	debug_dma_alloc_coherent(dev, size, *dma_handle, ret);
+	return ret;
+}
+
+static inline void dma_free_coherent(struct device *dev, size_t size,
+				     void *cpu_addr, dma_addr_t dma_handle)
+{
+	struct dma_map_ops *dma_ops = get_dma_ops(dev);
+
+	dma_ops->free(dev, size, cpu_addr, dma_handle, NULL);
+	debug_dma_free_coherent(dev, size, cpu_addr, dma_handle);
+}
+
+#endif /* _ASM_S390_DMA_MAPPING_H */
arch/s390/include/asm/dma.h +9 -10
index 6fb6de4..de015d8 100644
@@ -1,14 +1,13 @@
-/*
- *  S390 version
- */
-
-#ifndef _ASM_DMA_H
-#define _ASM_DMA_H
+#ifndef _ASM_S390_DMA_H
+#define _ASM_S390_DMA_H
 
-#include <asm/io.h>		/* need byte IO */
+#include <asm/io.h>
 
+/*
+ * MAX_DMA_ADDRESS is ambiguous because on s390 its completely unrelated
+ * to DMA. It _is_ used for the s390 memory zone split at 2GB caused
+ * by the 31 bit heritage.
+ */
 #define MAX_DMA_ADDRESS         0x80000000
 
-#define free_dma(x)	do { } while (0)
-
-#endif /* _ASM_DMA_H */
+#endif /* _ASM_S390_DMA_H */
arch/s390/include/asm/pci.h +21 -0
index a9aadeb..ca48caf 100644
@@ -75,8 +75,23 @@ struct zpci_dev {
 	struct msi_map *msi_map[ZPCI_NR_MSI_VECS];
 	unsigned int	aisb;		/* number of the summary bit */
 
+	/* DMA stuff */
+	unsigned long	*dma_table;
+	spinlock_t	dma_table_lock;
+	int		tlb_refresh;
+
+	spinlock_t	iommu_bitmap_lock;
+	unsigned long	*iommu_bitmap;
+	unsigned long	iommu_size;
+	unsigned long	iommu_pages;
+	unsigned int	next_bit;
+
 	struct zpci_bar_struct bars[PCI_BAR_COUNT];
 
+	u64		start_dma;	/* Start of available DMA addresses */
+	u64		end_dma;	/* End of available DMA addresses */
+	u64		dma_mask;	/* DMA address space mask */
+
 	enum pci_bus_speed max_bus_speed;
 };
 
@@ -95,6 +110,8 @@ int zpci_enable_device(struct zpci_dev *);
 void zpci_stop_device(struct zpci_dev *);
 void zpci_free_device(struct zpci_dev *);
 int zpci_scan_device(struct zpci_dev *);
+int zpci_register_ioat(struct zpci_dev *, u8, u64, u64, u64);
+int zpci_unregister_ioat(struct zpci_dev *, u8);
 
 /* CLP */
 int clp_find_pci_devices(void);
@@ -115,4 +132,8 @@ struct zpci_dev *get_zdev(struct pci_dev *);
 struct zpci_dev *get_zdev_by_fid(u32);
 bool zpci_fid_present(u32);
 
+/* DMA */
+int zpci_dma_init(void);
+void zpci_dma_exit(void);
+
 #endif
arch/s390/include/asm/pci_dma.h new file +196 -0

196 lines not on this page · raw message

arch/s390/pci/Makefile +1 -1
index 628be7b..4590596 100644
@@ -2,4 +2,4 @@
 # Makefile for the s390 PCI subsystem.
 #
 
-obj-$(CONFIG_PCI)	+= pci.o pci_clp.o pci_msi.o
+obj-$(CONFIG_PCI)	+= pci.o pci_dma.o pci_clp.o pci_msi.o
arch/s390/pci/pci.c +36 -0
index d11dc8a..5a2ef9e 100644
@@ -34,6 +34,7 @@
 #include <asm/facility.h>
 #include <asm/pci_insn.h>
 #include <asm/pci_clp.h>
+#include <asm/pci_dma.h>
 
 #define DEBUG				/* enable pr_debug */
 
@@ -232,6 +233,25 @@ static int mod_pci(struct zpci_dev *zdev, int fn, u8 dmaas, struct mod_pci_args
 	return rc;
 }
 
+/* Modify PCI: Register I/O address translation parameters */
+int zpci_register_ioat(struct zpci_dev *zdev, u8 dmaas,
+		       u64 base, u64 limit, u64 iota)
+{
+	struct mod_pci_args args = { base, limit, iota };
+
+	WARN_ON_ONCE(iota & 0x3fff);
+	args.iota |= ZPCI_IOTA_RTTO_FLAG;
+	return mod_pci(zdev, ZPCI_MOD_FC_REG_IOAT, dmaas, &args);
+}
+
+/* Modify PCI: Unregister I/O address translation parameters */
+int zpci_unregister_ioat(struct zpci_dev *zdev, u8 dmaas)
+{
+	struct mod_pci_args args = { 0, 0, 0 };
+
+	return mod_pci(zdev, ZPCI_MOD_FC_DEREG_IOAT, dmaas, &args);
+}
+
 /* Modify PCI: Unregister adapter interruptions */
 static int zpci_unregister_airq(struct zpci_dev *zdev)
 {
@@ -602,6 +622,7 @@ static void zpci_remove_device(struct pci_dev *pdev)
 
 	dev_info(&pdev->dev, "Removing device %u\n", zdev->domain);
 	zdev->state = ZPCI_FN_STATE_CONFIGURED;
+	zpci_dma_exit_device(zdev);
 	zpci_unmap_resources(pdev);
 	list_del(&zdev->entry);		/* can be called from init */
 	zdev->pdev = NULL;
@@ -887,7 +908,14 @@ int zpci_enable_device(struct zpci_dev *zdev)
 	if (rc)
 		goto out;
 	pr_info("Enabled fh: 0x%x fid: 0x%x\n", zdev->fh, zdev->fid);
+
+	rc = zpci_dma_init_device(zdev);
+	if (rc)
+		goto out_dma;
 	return 0;
+
+out_dma:
+	clp_disable_fh(zdev);
 out:
 	return rc;
 }
@@ -929,6 +957,7 @@ out:
 
 void zpci_stop_device(struct zpci_dev *zdev)
 {
+	zpci_dma_exit_device(zdev);
 	/*
 	 * Note: SCLP disables fh via set-pci-fn so don't
 	 * do that here.
@@ -953,6 +982,7 @@ int zpci_scan_device(struct zpci_dev *zdev)
 	return 0;
 
 out:
+	zpci_dma_exit_device(zdev);
 	clp_disable_fh(zdev);
 	return -EIO;
 }
@@ -1028,6 +1058,10 @@ static int __init pci_base_init(void)
 	if (rc)
 		goto out_irq;
 
+	rc = zpci_dma_init();
+	if (rc)
+		goto out_dma;
+
 	rc = clp_find_pci_devices();
 	if (rc)
 		goto out_find;
@@ -1036,6 +1070,8 @@ static int __init pci_base_init(void)
 	return 0;
 
 out_find:
+	zpci_dma_exit();
+out_dma:
 	zpci_irq_exit();
 out_irq:
 	zpci_msihash_exit();
arch/s390/pci/pci_clp.c +4 -0
index 72694fb..7f4ce8d 100644
@@ -48,6 +48,8 @@ static void clp_free_block(void *ptr)
 static void clp_store_query_pci_fngrp(struct zpci_dev *zdev,
 				      struct clp_rsp_query_pci_grp *response)
 {
+	zdev->tlb_refresh = response->refresh;
+	zdev->dma_mask = response->dasm;
 	zdev->msi_addr = response->msia;
 
 	pr_debug("Supported number of MSI vectors: %u\n", response->noi);
@@ -97,6 +99,8 @@ static int clp_store_query_pci_fn(struct zpci_dev *zdev,
 		zdev->bars[i].val = le32_to_cpu(response->bar[i]);
 		zdev->bars[i].size = response->bar_size[i];
 	}
+	zdev->start_dma = response->sdma;
+	zdev->end_dma = response->edma;
 	zdev->pchid = response->pchid;
 	zdev->pfgid = response->pfgid;
 	return 0;
arch/s390/pci/pci_dma.c new file +505 -0

505 lines not on this page · raw message

Signature · 1 line
1.7.12.4
#10 lore raw

RFC PATCH 6/10 s390/pci: CHSC PCI support for error and availability events

In reply to Jan Glauber

Add CHSC store-event-information support for PCI (notfication type 2) and report error and availability events to the PCI architecture layer.

---
 arch/s390/include/asm/pci.h |   4 ++
 arch/s390/pci/Makefile      |   3 +-
 arch/s390/pci/pci_event.c   |  93 ++++++++++++++++++++++++++
 drivers/s390/cio/chsc.c     | 156 +++++++++++++++++++++++++++++++-------------
 4 files changed, 211 insertions(+), 45 deletions(-)
 create mode 100644 arch/s390/pci/pci_event.c
arch/s390/include/asm/pci.h +4 -0
index ca48caf..3dc2cb6 100644
@@ -127,6 +127,10 @@ void zpci_teardown_msi_irq(struct zpci_dev *, struct msi_desc *);
 int zpci_msihash_init(void);
 void zpci_msihash_exit(void);
 
+/* Error handling and recovery */
+void zpci_event_error(void *);
+void zpci_event_availability(void *);
+
 /* Helpers */
 struct zpci_dev *get_zdev(struct pci_dev *);
 struct zpci_dev *get_zdev_by_fid(u32);
arch/s390/pci/Makefile +2 -1
index 4590596..7e36f42 100644
@@ -2,4 +2,5 @@
 # Makefile for the s390 PCI subsystem.
 #
 
-obj-$(CONFIG_PCI)	+= pci.o pci_dma.o pci_clp.o pci_msi.o
+obj-$(CONFIG_PCI)	+= pci.o pci_dma.o pci_clp.o pci_msi.o \
+			   pci_event.o
arch/s390/pci/pci_event.c new file +93 -0

93 lines not on this page · raw message

drivers/s390/cio/chsc.c +112 -44

226 lines not on this page · raw message

Signature · 1 line
1.7.12.4
#11 lore raw

RFC PATCH 7/10 s390/pci: PCI hotplug support via SCLP

In reply to Jan Glauber

Add SCLP PCI configure/deconfigure and implement a PCI hotplug controller (s390_pci_hpc). The hotplug controller creates a slot for every PCI function in stand-by or configured state. The PCI functions are named after the PCI function ID (fid). By writing to the power attribute in /sys/bus/pci/slots/<fid>/power the PCI function is moved to stand-by or configured state. If moved to the configured state the device is automatically scanned by the s390 PCI layer.

---
 arch/s390/include/asm/pci.h        |  11 ++
 arch/s390/include/asm/sclp.h       |   2 +
 arch/s390/pci/pci.c                |   9 ++
 drivers/pci/hotplug/Kconfig        |  11 ++
 drivers/pci/hotplug/Makefile       |   1 +
 drivers/pci/hotplug/s390_pci_hpc.c | 252 +++++++++++++++++++++++++++++++++++++
 drivers/s390/char/sclp.h           |   3 +-
 drivers/s390/char/sclp_cmd.c       |  64 +++++++++-
 8 files changed, 351 insertions(+), 2 deletions(-)
 create mode 100644 drivers/pci/hotplug/s390_pci_hpc.c
arch/s390/include/asm/pci.h +11 -0
index 3dc2cb6..739d9f1 100644
@@ -95,6 +95,11 @@ struct zpci_dev {
 	enum pci_bus_speed max_bus_speed;
 };
 
+struct pci_hp_callback_ops {
+	int (*create_slot)	(struct zpci_dev *zdev);
+	void (*remove_slot)	(struct zpci_dev *zdev);
+};
+
 static inline bool zdev_enabled(struct zpci_dev *zdev)
 {
 	return (zdev->fh & (1UL << 31)) ? true : false;
@@ -140,4 +145,10 @@ bool zpci_fid_present(u32);
 int zpci_dma_init(void);
 void zpci_dma_exit(void);
 
+/* Hotplug */
+extern struct mutex zpci_list_lock;
+extern struct list_head zpci_list;
+extern struct pci_hp_callback_ops hotplug_ops;
+extern unsigned int pci_probe;
+
 #endif
arch/s390/include/asm/sclp.h +2 -0
index e62a555..8337886 100644
@@ -55,5 +55,7 @@ int sclp_chp_read_info(struct sclp_chp_info *info);
 void sclp_get_ipl_info(struct sclp_ipl_info *info);
 bool sclp_has_linemode(void);
 bool sclp_has_vt220(void);
+int sclp_pci_configure(u32 fid);
+int sclp_pci_deconfigure(u32 fid);
 
 #endif /* _ASM_S390_SCLP_H */
arch/s390/pci/pci.c +9 -0
index 5a2ef9e..c523594 100644
@@ -47,7 +47,12 @@
 
 /* list of all detected zpci devices */
 LIST_HEAD(zpci_list);
+EXPORT_SYMBOL_GPL(zpci_list);
 DEFINE_MUTEX(zpci_list_lock);
+EXPORT_SYMBOL_GPL(zpci_list_lock);
+
+struct pci_hp_callback_ops hotplug_ops;
+EXPORT_SYMBOL_GPL(hotplug_ops);
 
 static DECLARE_BITMAP(zpci_domain, ZPCI_NR_DEVICES);
 static DEFINE_SPINLOCK(zpci_domain_lock);
@@ -935,6 +940,8 @@ int zpci_create_device(struct zpci_dev *zdev)
 
 	mutex_lock(&zpci_list_lock);
 	list_add_tail(&zdev->entry, &zpci_list);
+	if (hotplug_ops.create_slot)
+		hotplug_ops.create_slot(zdev);
 	mutex_unlock(&zpci_list_lock);
 
 	if (zdev->state == ZPCI_FN_STATE_STANDBY)
@@ -948,6 +955,8 @@ int zpci_create_device(struct zpci_dev *zdev)
 out_start:
 	mutex_lock(&zpci_list_lock);
 	list_del(&zdev->entry);
+	if (hotplug_ops.remove_slot)
+		hotplug_ops.remove_slot(zdev);
 	mutex_unlock(&zpci_list_lock);
 out_bus:
 	zpci_free_domain(zdev);
drivers/pci/hotplug/Kconfig +11 -0
index b0e46de..0594c4a 100644
@@ -151,4 +151,15 @@ config HOTPLUG_PCI_SGI
 
 	  When in doubt, say N.
 
+config HOTPLUG_PCI_S390
+	tristate "System z PCI Hotplug Support"
+	depends on S390 && 64BIT
+	help
+	  Say Y here if you want to use the System z PCI Hotplug
+	  driver for PCI devices. Without this driver it is not
+          possible to access stand-by PCI functions nor to deconfigure
+          PCI functions.
+
+	  When in doubt, say Y.
+
 endif # HOTPLUG_PCI
drivers/pci/hotplug/Makefile +1 -0
index c459cd4..47ec8c8 100644
@@ -18,6 +18,7 @@ obj-$(CONFIG_HOTPLUG_PCI_RPA)		+= rpaphp.o
 obj-$(CONFIG_HOTPLUG_PCI_RPA_DLPAR)	+= rpadlpar_io.o
 obj-$(CONFIG_HOTPLUG_PCI_SGI)		+= sgi_hotplug.o
 obj-$(CONFIG_HOTPLUG_PCI_ACPI)		+= acpiphp.o
+obj-$(CONFIG_HOTPLUG_PCI_S390)		+= s390_pci_hpc.o
 
 # acpiphp_ibm extends acpiphp, so should be linked afterwards.
 
drivers/pci/hotplug/s390_pci_hpc.c new file +252 -0

252 lines not on this page · raw message

drivers/s390/char/sclp.h +2 -1
index d7e97ae..25bcd4c 100644
@@ -1,5 +1,5 @@
 /*
- * Copyright IBM Corp. 1999, 2009
+ * Copyright IBM Corp. 1999,2012
  *
  * Author(s): Martin Peschke <[email protected]>
  *	      Martin Schwidefsky <[email protected]>
@@ -103,6 +103,7 @@ extern u64 sclp_facilities;
 #define SCLP_HAS_CHP_RECONFIG	(sclp_facilities & 0x2000000000000000ULL)
 #define SCLP_HAS_CPU_INFO	(sclp_facilities & 0x0800000000000000ULL)
 #define SCLP_HAS_CPU_RECONFIG	(sclp_facilities & 0x0400000000000000ULL)
+#define SCLP_HAS_PCI_RECONFIG	(sclp_facilities & 0x0000000040000000ULL)
 
 
 struct gds_subvector {
drivers/s390/char/sclp_cmd.c +63 -1

80 lines not on this page · raw message

Signature · 1 line
1.7.12.4
#12 lore raw

RFC PATCH 8/10 s390/pci: s390 specific PCI sysfs attributes

In reply to Jan Glauber

Add some s390 specific sysfs attributes to the PCI device directory. The following attributes are introduced:
- function_id (PCI function ID)
- function_handle (PCI function handle)
- pchid (PCI channel ID)
- pfgid (PCI function group ID aka PCI root complex)

---
 arch/s390/include/asm/pci.h |  4 +++
 arch/s390/pci/Makefile      |  2 +-
 arch/s390/pci/pci.c         |  6 ++++
 arch/s390/pci/pci_sysfs.c   | 86 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 97 insertions(+), 1 deletion(-)
 create mode 100644 arch/s390/pci/pci_sysfs.c
arch/s390/include/asm/pci.h +4 -0
index 739d9f1..39057e4 100644
@@ -141,6 +141,10 @@ struct zpci_dev *get_zdev(struct pci_dev *);
 struct zpci_dev *get_zdev_by_fid(u32);
 bool zpci_fid_present(u32);
 
+/* sysfs */
+int zpci_sysfs_add_device(struct device *);
+void zpci_sysfs_remove_device(struct device *);
+
 /* DMA */
 int zpci_dma_init(void);
 void zpci_dma_exit(void);
arch/s390/pci/Makefile +1 -1
index 7e36f42..ab0827b 100644
@@ -3,4 +3,4 @@
 #
 
 obj-$(CONFIG_PCI)	+= pci.o pci_dma.o pci_clp.o pci_msi.o \
-			   pci_event.o
+			   pci_sysfs.o pci_event.o
arch/s390/pci/pci.c +6 -0
index c523594..0723b10 100644
@@ -628,6 +628,7 @@ static void zpci_remove_device(struct pci_dev *pdev)
 	dev_info(&pdev->dev, "Removing device %u\n", zdev->domain);
 	zdev->state = ZPCI_FN_STATE_CONFIGURED;
 	zpci_dma_exit_device(zdev);
+	zpci_sysfs_remove_device(&pdev->dev);
 	zpci_unmap_resources(pdev);
 	list_del(&zdev->entry);		/* can be called from init */
 	zdev->pdev = NULL;
@@ -676,6 +677,11 @@ void pcibios_disable_device(struct pci_dev *pdev)
 	pdev->sysdata = NULL;
 }
 
+int pcibios_add_platform_entries(struct pci_dev *pdev)
+{
+	return zpci_sysfs_add_device(&pdev->dev);
+}
+
 int zpci_request_irq(unsigned int irq, irq_handler_t handler, void *data)
 {
 	int msi_nr = irq_to_msi_nr(irq);
arch/s390/pci/pci_sysfs.c new file +86 -0

86 lines not on this page · raw message

Signature · 1 line
1.7.12.4
#6 lore raw

RFC PATCH 2/10 s390/pci: CLP interface

In reply to Jan Glauber

CLP instructions are used to query the firmware about detected PCI functions, the attributes of those functions and to enable or disable a PCI function. The CLP interface is the equivalent to a PCI bus scan.

---
 arch/s390/include/asm/clp.h     |  28 ++++
 arch/s390/include/asm/pci.h     |   7 +
 arch/s390/include/asm/pci_clp.h | 182 +++++++++++++++++++++++
 arch/s390/pci/Makefile          |   2 +-
 arch/s390/pci/pci.c             |  28 ++++
 arch/s390/pci/pci_clp.c         | 317 ++++++++++++++++++++++++++++++++++++++++
 6 files changed, 563 insertions(+), 1 deletion(-)
 create mode 100644 arch/s390/include/asm/clp.h
 create mode 100644 arch/s390/include/asm/pci_clp.h
 create mode 100644 arch/s390/pci/pci_clp.c
arch/s390/include/asm/clp.h new file +28 -0
new file mode 100644
index 0000000..6c3aecc
@@ -0,0 +1,28 @@
+#ifndef _ASM_S390_CLP_H
+#define _ASM_S390_CLP_H
+
+/* CLP common request & response block size */
+#define CLP_BLK_SIZE			(PAGE_SIZE * 2)
+
+struct clp_req_hdr {
+	u16 len;
+	u16 cmd;
+} __packed;
+
+struct clp_rsp_hdr {
+	u16 len;
+	u16 rsp;
+} __packed;
+
+/* CLP Response Codes */
+#define CLP_RC_OK			0x0010	/* Command request successfully */
+#define CLP_RC_CMD			0x0020	/* Command code not recognized */
+#define CLP_RC_PERM			0x0030	/* Command not authorized */
+#define CLP_RC_FMT			0x0040	/* Invalid command request format */
+#define CLP_RC_LEN			0x0050	/* Invalid command request length */
+#define CLP_RC_8K			0x0060	/* Command requires 8K LPCB */
+#define CLP_RC_RESNOT0			0x0070	/* Reserved field not zero */
+#define CLP_RC_NODATA			0x0080	/* No data available */
+#define CLP_RC_FC_UNKNOWN		0x0100	/* Function code not recognized */
+
+#endif
arch/s390/include/asm/pci.h +7 -0
index 5e286ce..079b7e2 100644
@@ -8,6 +8,7 @@
 
 #include <asm-generic/pci.h>
 #include <asm-generic/pci-dma-compat.h>
+#include <asm/pci_clp.h>
 
 #define PCIBIOS_MIN_IO		0x1000
 #define PCIBIOS_MIN_MEM		0x10000000
@@ -76,6 +77,12 @@ void zpci_stop_device(struct zpci_dev *);
 void zpci_free_device(struct zpci_dev *);
 int zpci_scan_device(struct zpci_dev *);
 
+/* CLP */
+int clp_find_pci_devices(void);
+int clp_add_pci_device(u32, u32, int);
+int clp_enable_fh(struct zpci_dev *, u8);
+int clp_disable_fh(struct zpci_dev *);
+
 /* Helpers */
 struct zpci_dev *get_zdev(struct pci_dev *);
 struct zpci_dev *get_zdev_by_fid(u32);
arch/s390/include/asm/pci_clp.h new file +182 -0

182 lines not on this page · raw message

arch/s390/pci/Makefile +1 -1
index 78a1344..1afd68c 100644
@@ -2,4 +2,4 @@
 # Makefile for the s390 PCI subsystem.
 #
 
-obj-$(CONFIG_PCI)	+= pci.o
+obj-$(CONFIG_PCI)	+= pci.o pci_clp.o
arch/s390/pci/pci.c +28 -0
index 0b80ac7..70f6c56 100644
@@ -29,6 +29,7 @@
 
 #include <asm/facility.h>
 #include <asm/pci_insn.h>
+#include <asm/pci_clp.h>
 
 #define DEBUG				/* enable pr_debug */
 
@@ -436,6 +437,20 @@ static void zpci_free_domain(struct zpci_dev *zdev)
 	spin_unlock(&zpci_domain_lock);
 }
 
+int zpci_enable_device(struct zpci_dev *zdev)
+{
+	int rc;
+
+	rc = clp_enable_fh(zdev, ZPCI_NR_DMA_SPACES);
+	if (rc)
+		goto out;
+	pr_info("Enabled fh: 0x%x fid: 0x%x\n", zdev->fh, zdev->fid);
+	return 0;
+out:
+	return rc;
+}
+EXPORT_SYMBOL_GPL(zpci_enable_device);
+
 int zpci_create_device(struct zpci_dev *zdev)
 {
 	int rc;
@@ -455,8 +470,15 @@ int zpci_create_device(struct zpci_dev *zdev)
 	if (zdev->state == ZPCI_FN_STATE_STANDBY)
 		return 0;
 
+	rc = zpci_enable_device(zdev);
+	if (rc)
+		goto out_start;
 	return 0;
 
+out_start:
+	mutex_lock(&zpci_list_lock);
+	list_del(&zdev->entry);
+	mutex_unlock(&zpci_list_lock);
 out_bus:
 	zpci_free_domain(zdev);
 out:
@@ -489,6 +511,7 @@ int zpci_scan_device(struct zpci_dev *zdev)
 	return 0;
 
 out:
+	clp_disable_fh(zdev);
 	return -EIO;
 }
 EXPORT_SYMBOL_GPL(zpci_scan_device);
@@ -547,9 +570,14 @@ static int __init pci_base_init(void)
 	if (rc)
 		goto out_mem;
 
+	rc = clp_find_pci_devices();
+	if (rc)
+		goto out_find;
+
 	zpci_scan_devices();
 	return 0;
 
+out_find:
 	zpci_mem_exit();
 out_mem:
 	return rc;
arch/s390/pci/pci_clp.c new file +317 -0

317 lines not on this page · raw message

Signature · 1 line
1.7.12.4
#13 lore raw

RFC PATCH 9/10 s390/pci: add PCI Kconfig options

In reply to Jan Glauber
---
 arch/s390/Kconfig | 56 +++++++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 50 insertions(+), 6 deletions(-)
arch/s390/Kconfig +50 -6
index 5dba755..e3dd4aec 100644
@@ -34,12 +34,6 @@ config GENERIC_BUG
 config GENERIC_BUG_RELATIVE_POINTERS
 	def_bool y
 
-config NO_IOMEM
-	def_bool y
-
-config NO_DMA
-	def_bool y
-
 config ARCH_DMA_ADDR_T_64BIT
 	def_bool 64BIT
 
@@ -58,6 +52,12 @@ config KEXEC
 config AUDIT_ARCH
 	def_bool y
 
+config NO_IOPORT
+	def_bool y
+
+config PCI_QUIRKS
+	def_bool n
+
 config S390
 	def_bool y
 	select USE_GENERIC_SMP_HELPERS if SMP
@@ -423,6 +423,50 @@ config QDIO
 
 	  If unsure, say Y.
 
+menuconfig PCI
+	bool "PCI support" 
+	default y
+	depends on 64BIT 
+ 	select ARCH_SUPPORTS_MSI
+ 	select PCI_MSI
+	help
+	  Enable PCI support.
+
+if PCI
+
+config PCI_NR_FUNCTIONS
+	int "Maximum number of PCI functions (1-4096)"
+	range 1 4096
+	default "64"
+	help
+	  This allows you to specify the maximum number of PCI functions which
+	  this kernel will support.
+
+source "drivers/pci/Kconfig"
+source "drivers/pci/pcie/Kconfig"
+source "drivers/pci/hotplug/Kconfig"
+
+endif	# PCI
+
+config PCI_DOMAINS
+	def_bool PCI
+
+config HAS_IOMEM
+	def_bool PCI
+
+config IOMMU_HELPER
+	def_bool PCI
+
+config HAS_DMA
+	def_bool PCI
+	select HAVE_DMA_API_DEBUG
+
+config NEED_SG_DMA_LENGTH
+	def_bool PCI
+
+config HAVE_DMA_ATTRS
+	def_bool PCI
+
 config CHSC_SCH
 	def_tristate m
 	prompt "Support for CHSC subchannels"
Signature · 1 line
1.7.12.4
#14 lore raw

RFC PATCH 10/10 vga: compile fix, disable vga for s390

In reply to Jan Glauber
---
 drivers/gpu/vga/Kconfig | 2 +-
 include/video/vga.h     | 2 ++
 2 files changed, 3 insertions(+), 1 deletion(-)
drivers/gpu/vga/Kconfig +1 -1
index f348388..29437ea 100644
@@ -1,7 +1,7 @@
 config VGA_ARB
 	bool "VGA Arbitration" if EXPERT
 	default y
-	depends on PCI
+	depends on (PCI && !S390)
 	help
 	  Some "legacy" VGA devices implemented on PCI typically have the same
 	  hard-decoded addresses as they did on ISA. When multiple PCI devices
include/video/vga.h +2 -0
index cac567f..7978d31 100644
@@ -19,7 +19,9 @@
 
 #include <linux/types.h>
 #include <asm/io.h>
+#ifndef __s390x__
 #include <asm/vga.h>
+#endif
 #include <asm/byteorder.h>
 
 
Signature · 1 line
1.7.12.4
#1 lore raw

RFC PATCH 1/10 s390/pci: base support

In reply to Jan Glauber

Add PCI support for s390, (only 64 bit mode is supported by hardware):
- PCI facility tests
- PCI instructions: pcilg, pcistg, pcistb, stpcifc, mpcifc, rpcit
- map readb/w/l/q and writeb/w/l/q to pcilg and pcistg instructions
- pci_iomap implementation
- memcpy_fromio/toio
- pci_root_ops using special pcilg/pcistg
- device, bus and domain allocation

---
 arch/s390/Kbuild                 |   1 +
 arch/s390/include/asm/io.h       |  55 +++-
 arch/s390/include/asm/pci.h      |  82 +++++-
 arch/s390/include/asm/pci_insn.h | 280 ++++++++++++++++++++
 arch/s390/include/asm/pci_io.h   | 194 ++++++++++++++
 arch/s390/kernel/dis.c           |  15 ++
 arch/s390/pci/Makefile           |   5 +
 arch/s390/pci/pci.c              | 557 +++++++++++++++++++++++++++++++++++++++
 include/asm-generic/io.h         |  21 +-
 9 files changed, 1201 insertions(+), 9 deletions(-)
 create mode 100644 arch/s390/include/asm/pci_insn.h
 create mode 100644 arch/s390/include/asm/pci_io.h
 create mode 100644 arch/s390/pci/Makefile
 create mode 100644 arch/s390/pci/pci.c
arch/s390/Kbuild +1 -0
index cc45d25..647c3ec 100644
@@ -6,3 +6,4 @@ obj-$(CONFIG_S390_HYPFS_FS)	+= hypfs/
 obj-$(CONFIG_APPLDATA_BASE)	+= appldata/
 obj-$(CONFIG_MATHEMU)		+= math-emu/
 obj-y				+= net/
+obj-$(CONFIG_PCI)		+= pci/
arch/s390/include/asm/io.h +51 -4

74 lines not on this page · raw message

arch/s390/include/asm/pci.h +78 -4

88 lines not on this page · raw message

arch/s390/include/asm/pci_insn.h new file +280 -0

280 lines not on this page · raw message

arch/s390/include/asm/pci_io.h new file +194 -0

194 lines not on this page · raw message

arch/s390/kernel/dis.c +15 -0
index f00286b..8c6c421 100644
@@ -320,6 +320,10 @@ enum {
 	LONG_INSN_TABORT,
 	LONG_INSN_TBEGIN,
 	LONG_INSN_TBEGINC,
+	LONG_INSN_PCISTG,
+	LONG_INSN_MPCIFC,
+	LONG_INSN_STPCIFC,
+	LONG_INSN_PCISTB,
 };
 
 static char *long_insn_name[] = {
@@ -340,6 +344,10 @@ static char *long_insn_name[] = {
 	[LONG_INSN_TABORT] = "tabort",
 	[LONG_INSN_TBEGIN] = "tbegin",
 	[LONG_INSN_TBEGINC] = "tbeginc",
+	[LONG_INSN_PCISTG] = "pcistg",
+	[LONG_INSN_MPCIFC] = "mpcifc",
+	[LONG_INSN_STPCIFC] = "stpcifc",
+	[LONG_INSN_PCISTB] = "pcistb",
 };
 
 static struct insn opcode[] = {
@@ -946,6 +954,9 @@ static struct insn opcode_b9[] = {
 	{ "slhhh", 0xcb, INSTR_RRF_R0RR2 },
 	{ "chhr ", 0xcd, INSTR_RRE_RR },
 	{ "clhhr", 0xcf, INSTR_RRE_RR },
+	{ { 0, LONG_INSN_PCISTG }, 0xd0, INSTR_RRE_RR },
+	{ "pcilg", 0xd2, INSTR_RRE_RR },
+	{ "rpcit", 0xd3, INSTR_RRE_RR },
 	{ "ahhlr", 0xd8, INSTR_RRF_R0RR2 },
 	{ "shhlr", 0xd9, INSTR_RRF_R0RR2 },
 	{ "slhhl", 0xdb, INSTR_RRF_R0RR2 },
@@ -1175,6 +1186,8 @@ static struct insn opcode_e3[] = {
 	{ "chf", 0xcd, INSTR_RXY_RRRD },
 	{ "clhf", 0xcf, INSTR_RXY_RRRD },
 	{ "ntstg", 0x25, INSTR_RXY_RRRD },
+	{ { 0, LONG_INSN_MPCIFC }, 0xd0, INSTR_RXY_RRRD },
+	{ { 0, LONG_INSN_STPCIFC }, 0xd4, INSTR_RXY_RRRD },
 #endif
 	{ "lrv", 0x1e, INSTR_RXY_RRRD },
 	{ "lrvh", 0x1f, INSTR_RXY_RRRD },
@@ -1254,6 +1267,8 @@ static struct insn opcode_eb[] = {
 	{ "alsi", 0x6e, INSTR_SIY_IRD },
 	{ "algsi", 0x7e, INSTR_SIY_IRD },
 	{ "ecag", 0x4c, INSTR_RSY_RRRD },
+	{ { 0, LONG_INSN_PCISTB }, 0xd0, INSTR_RSY_RRRD },
+	{ "sic", 0xd1, INSTR_RSY_RRRD },
 	{ "srak", 0xdc, INSTR_RSY_RRRD },
 	{ "slak", 0xdd, INSTR_RSY_RRRD },
 	{ "srlk", 0xde, INSTR_RSY_RRRD },
arch/s390/pci/Makefile new file +5 -0
new file mode 100644
index 0000000..78a1344
@@ -0,0 +1,5 @@
+#
+# Makefile for the s390 PCI subsystem.
+#
+
+obj-$(CONFIG_PCI)	+= pci.o
arch/s390/pci/pci.c new file +557 -0

557 lines not on this page · raw message

include/asm-generic/io.h +20 -1
index 448303b..9e0ebe0 100644
@@ -83,19 +83,25 @@ static inline void __raw_writel(u32 b, volatile void __iomem *addr)
 #define writel(b,addr) __raw_writel(__cpu_to_le32(b),addr)
 
 #ifdef CONFIG_64BIT
+#ifndef __raw_readq
 static inline u64 __raw_readq(const volatile void __iomem *addr)
 {
 	return *(const volatile u64 __force *) addr;
 }
+#endif
+
 #define readq(addr) __le64_to_cpu(__raw_readq(addr))
 
+#ifndef __raw_writeq
 static inline void __raw_writeq(u64 b, volatile void __iomem *addr)
 {
 	*(volatile u64 __force *) addr = b;
 }
-#define writeq(b,addr) __raw_writeq(__cpu_to_le64(b),addr)
 #endif
 
+#define writeq(b, addr) __raw_writeq(__cpu_to_le64(b), addr)
+#endif /* CONFIG_64BIT */
+
 #ifndef PCI_IOBASE
 #define PCI_IOBASE ((void __iomem *) 0)
 #endif
@@ -286,15 +292,20 @@ static inline void writesb(const void __iomem *addr, const void *buf, int len)
 
 #ifndef CONFIG_GENERIC_IOMAP
 struct pci_dev;
+extern void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max);
+
+#ifndef pci_iounmap
 static inline void pci_iounmap(struct pci_dev *dev, void __iomem *p)
 {
 }
+#endif
 #endif /* CONFIG_GENERIC_IOMAP */
 
 /*
  * Change virtual addresses to physical addresses and vv.
  * These are pretty trivial
  */
+#ifndef virt_to_phys
 static inline unsigned long virt_to_phys(volatile void *address)
 {
 	return __pa((unsigned long)address);
@@ -304,6 +315,7 @@ static inline void *phys_to_virt(unsigned long address)
 {
 	return __va(address);
 }
+#endif
 
 /*
  * Change "struct page" to physical address.
@@ -363,9 +375,16 @@ static inline void *bus_to_virt(unsigned long address)
 }
 #endif
 
+#ifndef memset_io
 #define memset_io(a, b, c)	memset(__io_virt(a), (b), (c))
+#endif
+
+#ifndef memcpy_fromio
 #define memcpy_fromio(a, b, c)	memcpy((a), __io_virt(b), (c))
+#endif
+#ifndef memcpy_toio
 #define memcpy_toio(a, b, c)	memcpy(__io_virt(a), (b), (c))
+#endif
 
 #endif /* __KERNEL__ */
 
Signature · 1 line
1.7.12.4
maintainer
#2 lore raw

Re: [RFC PATCH 01/10] s390/pci: base support

Quoted 10 lines · Jan Glauber

Add PCI support for s390, (only 64 bit mode is supported by hardware):
- PCI facility tests
- PCI instructions: pcilg, pcistg, pcistb, stpcifc, mpcifc, rpcit
- map readb/w/l/q and writeb/w/l/q to pcilg and pcistg instructions
- pci_iomap implementation
- memcpy_fromio/toio
- pci_root_ops using special pcilg/pcistg
- device, bus and domain allocation

I think these patches are in -next already, so I just have some general comments & questions.

My overall impression is that these are exceptionally well done. They're easy to read, well organized, and well documented. It's a refreshing change from a lot of the stuff that's posted.

As with other arches that run on top of hypervisors, you have arch-specific code that enumerates PCI devices using hypervisor calls, and you hook into the PCI core at a lower level than pci_scan_root_bus(). That is, you call pci_create_root_bus(), pci_scan_single_device(), pci_bus_add_devices(), etc., directly from the arch code. This is the typical approach, but it does make more dependencies between the arch code and the PCI core than I'd like.

Did you consider hiding any of the hypervisor details behind the PCI config accessor interface? If that could be done, the overall structure might be more similar to other architectures.

The current config accessors only work for dev/fn 00.0 (they fail when "devfn != ZPCI_DEVFN"). Why is that? It looks like it precludes multi-function devices and basically prevents you from building an arbitrary PCI hierarchy.

zpci_map_resources() is very unusual. The struct pci_dev resource[] table normally contains CPU physical addresses, but zpci_map_resources() fills it with virtual addresses. I suspect this has something to do with the "BAR spaces are not disjunctive on s390" comment. It almost sounds like that's describing host bridges where the PCI bus address is not equal to the CPU physical address -- in that case, device A and device B may have the same values in their BARs, but they can still be distinct if they're under host bridges that apply different CPU-to-bus address translations.

Bjorn

#3 lore raw

Re: [RFC PATCH 01/10] s390/pci: base support

Quoted 10 lines · Bjorn Helgaas
Quoted 9 lines · Jan Glauber

Add PCI support for s390, (only 64 bit mode is supported by hardware):
- PCI facility tests
- PCI instructions: pcilg, pcistg, pcistb, stpcifc, mpcifc, rpcit
- map readb/w/l/q and writeb/w/l/q to pcilg and pcistg instructions
- pci_iomap implementation
- memcpy_fromio/toio
- pci_root_ops using special pcilg/pcistg
- device, bus and domain allocation

Quoted 4 lines
Quoted 1 line

I think these patches are in -next already, so I just have some general comments & questions.

Yes, since the feedback was manageable we decided to give the patches some exposure in -next and if no one complains we'll just go for the next merge window. BTW, Sebastian & Gerald (on CC:) will continue the work on the PCI code.

Quoted 3 lines

My overall impression is that these are exceptionally well done. They're easy to read, well organized, and well documented. It's a refreshing change from a lot of the stuff that's posted.

Thanks Björn!

Quoted 11 lines

As with other arches that run on top of hypervisors, you have arch-specific code that enumerates PCI devices using hypervisor calls, and you hook into the PCI core at a lower level than pci_scan_root_bus(). That is, you call pci_create_root_bus(), pci_scan_single_device(), pci_bus_add_devices(), etc., directly from the arch code. This is the typical approach, but it does make more dependencies between the arch code and the PCI core than I'd like.

Did you consider hiding any of the hypervisor details behind the PCI config accessor interface? If that could be done, the overall structure might be more similar to other architectures.

You mean pci_root_ops? I'm not sure I understand how that can be used to hide hipervisor details. One reason why we use the lower level functions is that we need to create the root bus (and its resources) much earlier then the pci_dev. For instance pci_hp_register wants a pci_bus to create the PCI slot and the slot can exist without a pci_dev.

Quoted 4 lines

The current config accessors only work for dev/fn 00.0 (they fail when "devfn != ZPCI_DEVFN"). Why is that? It looks like it precludes multi-function devices and basically prevents you from building an arbitrary PCI hierarchy.

Our hypervisor does not support multi-function devices. In fact the hypervisor will limit the reported PCI devices to a hand-picked selection so we can be sure that there will be no unsupported devices. The PCI hierarchy is hidden by the hipervisor. We only use the PCI domain number, bus and devfn are always zero. So it looks like every function is directly attached to a PCI root complex.

That was the reason for the sanity check, but thinking about it I could remove it since although we don't support multi-function devices I think the s390 code should be more agnostic to these limitations.

Quoted 9 lines

zpci_map_resources() is very unusual. The struct pci_dev resource[] table normally contains CPU physical addresses, but zpci_map_resources() fills it with virtual addresses. I suspect this has something to do with the "BAR spaces are not disjunctive on s390" comment. It almost sounds like that's describing host bridges where the PCI bus address is not equal to the CPU physical address -- in that case, device A and device B may have the same values in their BARs, but they can still be distinct if they're under host bridges that apply different CPU-to-bus address translations.

Yeah, you've found it... I've had 3 or 4 tries on different implementations but all failed. If we use the resources as they are we cannot map them to the instructions (and ioremap does not help because there we cannot find out which device the resource belongs to). If we change the BARs on the card MMIO stops to work. I don't know about host bridges - if we would use a host bridge at which point in the translation process would it kick in?

Jan

Quoted 2 lines

Bjorn

maintainer
#4 lore raw

Re: [RFC PATCH 01/10] s390/pci: base support

Quoted 35 lines · Jan Glauber
Quoted 10 lines · Bjorn Helgaas
Quoted 9 lines · Jan Glauber

Add PCI support for s390, (only 64 bit mode is supported by hardware):
- PCI facility tests
- PCI instructions: pcilg, pcistg, pcistb, stpcifc, mpcifc, rpcit
- map readb/w/l/q and writeb/w/l/q to pcilg and pcistg instructions
- pci_iomap implementation
- memcpy_fromio/toio
- pci_root_ops using special pcilg/pcistg
- device, bus and domain allocation

Quoted 4 lines
Quoted 1 line

I think these patches are in -next already, so I just have some general comments & questions.

Yes, since the feedback was manageable we decided to give the patches some exposure in -next and if no one complains we'll just go for the next merge window. BTW, Sebastian & Gerald (on CC:) will continue the work on the PCI code.

Quoted 3 lines

My overall impression is that these are exceptionally well done. They're easy to read, well organized, and well documented. It's a refreshing change from a lot of the stuff that's posted.

Thanks Björn!

Quoted 8 lines

As with other arches that run on top of hypervisors, you have arch-specific code that enumerates PCI devices using hypervisor calls, and you hook into the PCI core at a lower level than pci_scan_root_bus(). That is, you call pci_create_root_bus(), pci_scan_single_device(), pci_bus_add_devices(), etc., directly from the arch code. This is the typical approach, but it does make more dependencies between the arch code and the PCI core than I'd like.

Quoted 6 lines
Quoted 3 lines

Did you consider hiding any of the hypervisor details behind the PCI config accessor interface? If that could be done, the overall structure might be more similar to other architectures.

You mean pci_root_ops? I'm not sure I understand how that can be used to hide hipervisor details.

The object of doing this would be to let you use pci_scan_root_bus(), so you wouldn't have to call pci_scan_single_device() and pci_bus_add_resources() directly. The idea is to make pci_read() and pci_write() smart enough that the PCI core can use them as though you have a normal PCI implementation. When pci_scan_root_bus() enumerates devices on the root bus using pci_scan_child_bus(), it does config reads on the VENDOR_ID of bb:00.0, bb:01.0, ..., bb:1f.0. Your pci_read() would return error or 0xffffffff data for everything except bb:00.0 (I guess it actually already does that).

Then some of the other init, e.g., zpci_enable_device(), could be done by the standard pcibios hooks such as pcibios_add_device() and pcibios_enable_device(), which would remove some of the PCI grunge from zpci_scan_devices() and the s390 hotplug driver.

Quoted 4 lines

One reason why we use the lower level
functions is that we need to create the root bus (and its resources) much earlier then the pci_dev. For instance pci_hp_register wants a pci_bus to create the PCI slot and the slot can exist without a pci_dev.

There must be something else going on here; a bus is *always* created before any of the pci_devs on the bus.

One thing that looks a little strange is that zpci_list seems to be sort of a cross between a list of PCI host bridges and a list of PCI devices. Understandable, since you usually have a one-to-one correspondence between them, but for your hotplug stuff, you can have the host bridge and slot without the pci_dev.

The hotplug slot support is really a function of the host bridge support, so it doesn't seem quite right to me to split it into a separate module (although that is the way most PCI hotplug drivers are currently structured). One reason is that it makes hot-add of a host bridge awkward: you have to have the "if (hotplug_ops.create_slot)" test in zpci_create_device().

Quoted 12 lines
Quoted 4 lines

The current config accessors only work for dev/fn 00.0 (they fail when "devfn != ZPCI_DEVFN"). Why is that? It looks like it precludes multi-function devices and basically prevents you from building an arbitrary PCI hierarchy.

Our hypervisor does not support multi-function devices. In fact the hypervisor will limit the reported PCI devices to a hand-picked selection so we can be sure that there will be no unsupported devices. The PCI hierarchy is hidden by the hipervisor. We only use the PCI domain number, bus and devfn are always zero. So it looks like every function is directly attached to a PCI root complex.

Quoted 3 lines

That was the reason for the sanity check, but thinking about it I could remove it since although we don't support multi-function devices I think the s390 code should be more agnostic to these limitations.

The config accessor interface should be defined so it works for all PCI devices that exist, and fails for devices that do not exist. The approach you've taken so far is to prevent the PCI core from even attempting access to non-existent devices, which requires you to use some lower-level PCI interfaces. The alternative I'm raising as a possibility is to allow the PCI core to attempt accesses to non-existent devices, but have the accessor be smart enough to use the hypervisor or other arch-specific data to determine whether the device exists or not, and act accordingly.

Basically the idea is that if we put more smarts in the config accessors, we can make the interface between the PCI core and the architecture thinner.

Quoted 10 lines
Quoted 9 lines

zpci_map_resources() is very unusual. The struct pci_dev resource[] table normally contains CPU physical addresses, but zpci_map_resources() fills it with virtual addresses. I suspect this has something to do with the "BAR spaces are not disjunctive on s390" comment. It almost sounds like that's describing host bridges where the PCI bus address is not equal to the CPU physical address -- in that case, device A and device B may have the same values in their BARs, but they can still be distinct if they're under host bridges that apply different CPU-to-bus address translations.

Quoted 7 lines

Yeah, you've found it... I've had 3 or 4 tries on different implementations but all failed. If we use the resources as they are we cannot map them to the instructions (and ioremap does not help because there we cannot find out which device the resource belongs to). If we change the BARs on the card MMIO stops to work. I don't know about host bridges - if we would use a host bridge at which point in the translation process would it kick in?

Here's how it works. The PCI host bridge claims regions of the CPU physical address space and forwards transactions in those regions to the PCI root bus. Some host bridges can apply an offset when forwarding, so the address on the bus may be different from the address from the CPU. The bus address is what matches a PCI BAR.

You can tell the PCI core about this translation by using pci_add_resource_offset() instead of pci_add_resource(). When the PCI core reads a BAR, it applies the offset to convert the BAR value into a CPU physical address.

For example, let's say you have two host bridges:

PCI host bridge to bus 0000:00
pci_bus 0000:00: root bus resource [mem 0x100000000-0x1ffffffff] (bus address [0x00000000-0xffffffff])
PCI host bridge to bus 0001:00
pci_bus 0001:00: root bus resource [mem 0x200000000-0x2ffffffff] (bus address [0x00000000-0xffffffff])

Both bridges use the same bus address range, and BARs of devices on bus 0000:00 can have the same values as those of devices on bus 0001:00. But there's no ambiguity because a CPU access to 0x1_0000_0000 will be claimed by the first bridge and translated to bus address 0x0 on bus 0000:00, while a CPU access to 0x2_0000_0000 will be claimed by the second bridge and translated to bus address 0x0 on bus 0001:00.

The pci_dev resources will contain CPU physical addresses, not the BAR values themselves. These addresses implicitly identify the host bridge (and, in your case, the pci_dev, since you have at most one pci_dev per host bridge).

Bjorn

#5 lore raw

Re: [RFC PATCH 01/10] s390/pci: base support

Quoted 155 lines · Bjorn Helgaas
Quoted 35 lines · Jan Glauber
Quoted 10 lines · Bjorn Helgaas
Quoted 9 lines · Jan Glauber

Add PCI support for s390, (only 64 bit mode is supported by hardware):
- PCI facility tests
- PCI instructions: pcilg, pcistg, pcistb, stpcifc, mpcifc, rpcit
- map readb/w/l/q and writeb/w/l/q to pcilg and pcistg instructions
- pci_iomap implementation
- memcpy_fromio/toio
- pci_root_ops using special pcilg/pcistg
- device, bus and domain allocation

Quoted 4 lines
Quoted 1 line

I think these patches are in -next already, so I just have some general comments & questions.

Yes, since the feedback was manageable we decided to give the patches some exposure in -next and if no one complains we'll just go for the next merge window. BTW, Sebastian & Gerald (on CC:) will continue the work on the PCI code.

Quoted 3 lines

My overall impression is that these are exceptionally well done. They're easy to read, well organized, and well documented. It's a refreshing change from a lot of the stuff that's posted.

Thanks Björn!

Quoted 8 lines

As with other arches that run on top of hypervisors, you have arch-specific code that enumerates PCI devices using hypervisor calls, and you hook into the PCI core at a lower level than pci_scan_root_bus(). That is, you call pci_create_root_bus(), pci_scan_single_device(), pci_bus_add_devices(), etc., directly from the arch code. This is the typical approach, but it does make more dependencies between the arch code and the PCI core than I'd like.

Quoted 6 lines
Quoted 3 lines

Did you consider hiding any of the hypervisor details behind the PCI config accessor interface? If that could be done, the overall structure might be more similar to other architectures.

You mean pci_root_ops? I'm not sure I understand how that can be used to hide hipervisor details.

The object of doing this would be to let you use pci_scan_root_bus(), so you wouldn't have to call pci_scan_single_device() and pci_bus_add_resources() directly. The idea is to make pci_read() and pci_write() smart enough that the PCI core can use them as though you have a normal PCI implementation. When pci_scan_root_bus() enumerates devices on the root bus using pci_scan_child_bus(), it does config reads on the VENDOR_ID of bb:00.0, bb:01.0, ..., bb:1f.0. Your pci_read() would return error or 0xffffffff data for everything except bb:00.0 (I guess it actually already does that).

Then some of the other init, e.g., zpci_enable_device(), could be done by the standard pcibios hooks such as pcibios_add_device() and pcibios_enable_device(), which would remove some of the PCI grunge from zpci_scan_devices() and the s390 hotplug driver.

Quoted 4 lines

One reason why we use the lower level
functions is that we need to create the root bus (and its resources) much earlier then the pci_dev. For instance pci_hp_register wants a pci_bus to create the PCI slot and the slot can exist without a pci_dev.

There must be something else going on here; a bus is *always* created before any of the pci_devs on the bus.

One thing that looks a little strange is that zpci_list seems to be sort of a cross between a list of PCI host bridges and a list of PCI devices. Understandable, since you usually have a one-to-one correspondence between them, but for your hotplug stuff, you can have the host bridge and slot without the pci_dev.

The hotplug slot support is really a function of the host bridge support, so it doesn't seem quite right to me to split it into a separate module (although that is the way most PCI hotplug drivers are currently structured). One reason is that it makes hot-add of a host bridge awkward: you have to have the "if (hotplug_ops.create_slot)" test in zpci_create_device().

Quoted 12 lines
Quoted 4 lines

The current config accessors only work for dev/fn 00.0 (they fail when "devfn != ZPCI_DEVFN"). Why is that? It looks like it precludes multi-function devices and basically prevents you from building an arbitrary PCI hierarchy.

Our hypervisor does not support multi-function devices. In fact the hypervisor will limit the reported PCI devices to a hand-picked selection so we can be sure that there will be no unsupported devices. The PCI hierarchy is hidden by the hipervisor. We only use the PCI domain number, bus and devfn are always zero. So it looks like every function is directly attached to a PCI root complex.

Quoted 3 lines

That was the reason for the sanity check, but thinking about it I could remove it since although we don't support multi-function devices I think the s390 code should be more agnostic to these limitations.

The config accessor interface should be defined so it works for all PCI devices that exist, and fails for devices that do not exist. The approach you've taken so far is to prevent the PCI core from even attempting access to non-existent devices, which requires you to use some lower-level PCI interfaces. The alternative I'm raising as a possibility is to allow the PCI core to attempt accesses to non-existent devices, but have the accessor be smart enough to use the hypervisor or other arch-specific data to determine whether the device exists or not, and act accordingly.

Basically the idea is that if we put more smarts in the config accessors, we can make the interface between the PCI core and the architecture thinner.

Quoted 10 lines
Quoted 9 lines

zpci_map_resources() is very unusual. The struct pci_dev resource[] table normally contains CPU physical addresses, but zpci_map_resources() fills it with virtual addresses. I suspect this has something to do with the "BAR spaces are not disjunctive on s390" comment. It almost sounds like that's describing host bridges where the PCI bus address is not equal to the CPU physical address -- in that case, device A and device B may have the same values in their BARs, but they can still be distinct if they're under host bridges that apply different CPU-to-bus address translations.

Quoted 7 lines

Yeah, you've found it... I've had 3 or 4 tries on different implementations but all failed. If we use the resources as they are we cannot map them to the instructions (and ioremap does not help because there we cannot find out which device the resource belongs to). If we change the BARs on the card MMIO stops to work. I don't know about host bridges - if we would use a host bridge at which point in the translation process would it kick in?

Here's how it works. The PCI host bridge claims regions of the CPU physical address space and forwards transactions in those regions to the PCI root bus. Some host bridges can apply an offset when forwarding, so the address on the bus may be different from the address from the CPU. The bus address is what matches a PCI BAR.

You can tell the PCI core about this translation by using pci_add_resource_offset() instead of pci_add_resource(). When the PCI core reads a BAR, it applies the offset to convert the BAR value into a CPU physical address.

For example, let's say you have two host bridges:

PCI host bridge to bus 0000:00
pci_bus 0000:00: root bus resource [mem 0x100000000-0x1ffffffff] (bus address [0x00000000-0xffffffff])
PCI host bridge to bus 0001:00
pci_bus 0001:00: root bus resource [mem 0x200000000-0x2ffffffff] (bus address [0x00000000-0xffffffff])

Both bridges use the same bus address range, and BARs of devices on bus 0000:00 can have the same values as those of devices on bus 0001:00. But there's no ambiguity because a CPU access to 0x1_0000_0000 will be claimed by the first bridge and translated to bus address 0x0 on bus 0000:00, while a CPU access to 0x2_0000_0000 will be claimed by the second bridge and translated to bus address 0x0 on bus 0001:00.

Quoted 4 lines

The pci_dev resources will contain CPU physical addresses, not the BAR values themselves. These addresses implicitly identify the host bridge (and, in your case, the pci_dev, since you have at most one pci_dev per host bridge).

Hi Bjorn,

thanks for the explanation. I'll look into it.

Regards,
Sebastian