Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1598822 > unrolled thread

[PATCH V7 0/7] LPC: legacy ISA I/O support

Started by"zhichang.yuan" <yuanzhichang@hisilicon.com>
First post2017-03-13 03:20 +0100
Last post2017-03-13 22:40 +0100
Articles 4 — 2 participants

Back to article view | Back to linux.kernel


Contents

  [PATCH V7 0/7] LPC: legacy ISA I/O support "zhichang.yuan" <yuanzhichang@hisilicon.com> - 2017-03-13 03:20 +0100
    [PATCH V7 1/7] LIBIO: Introduce a generic PIO mapping method "zhichang.yuan" <yuanzhichang@hisilicon.com> - 2017-03-13 03:30 +0100
    [PATCH V7 5/7] ACPI: Delay the enumeration on the devices whose  dependency has not met "zhichang.yuan" <yuanzhichang@hisilicon.com> - 2017-03-13 03:30 +0100
      Re: [PATCH V7 5/7] ACPI: Delay the enumeration on the devices whose  dependency has not met "Rafael J. Wysocki" <rjw@rjwysocki.net> - 2017-03-13 22:40 +0100

#1598822 — [PATCH V7 0/7] LPC: legacy ISA I/O support

From"zhichang.yuan" <yuanzhichang@hisilicon.com>
Date2017-03-13 03:20 +0100
Subject[PATCH V7 0/7] LPC: legacy ISA I/O support
Message-ID<tknKx-1UO-3@gated-at.bofh.it>
This patchset supports the IPMI-bt device attached to the Low-Pin-Count
interface implemented on Hisilicon Hip06/Hip07 SoC.
	                -----------
			| LPC host|
	                |         |
	                -----------
	                     |
 	        _____________V_______________LPC
                  |			  |
                  V	                  V
			             ------------
			             |  BT(ipmi)|
			             ------------

When master accesses those peripherals beneath the Hip06/Hip07 LPC, a specific
LPC driver is needed to make LPC host generate the standard LPC I/O cycles with
the target peripherals'I/O port addresses. But on curent arm64 world, there is
no real I/O accesses. All the I/O operations through in/out pair are based on
MMIO which is not satisfied the I/O mechanism on Hip06/Hip07 LPC.
To solve this issue and keep the relevant existing peripherals' driver
untouched, this patchset implements:
  - introduces a generic I/O space management framwork, LIBIO, to support I/O
    operations of both MMIO buses and the host controllers which access their
    peripherals with host local I/O addresses;
  - redefines the in/out accessors to provide unified interfaces for MMIO and
    legacy I/O. Based on the LIBIO, the calling of in/out() from upper-layer
    drivers, such as ipmi-si, will be redirected to the corresponding
    device-specific I/O hooks to perfrom the I/O accesses.
Based on this patch-set, all the I/O accesses to Hip06/Hip07 LPC peripherals
can be supported without any changes on the existing ipmi-si driver.

Changes from V6:
  - According to the comments from Bjorn and Alex, merge PCI IO and indirect-IO
    into a generic I/O space management, LIBIO;
  - Adopted the '_DEP' to replace the platform bus notifier. In this way, we
    can ensure the LPC peripherals' I/O resources had been translated to
    logical IO before the LPC peripheral enumeration;
  - Replaced the rwlock with rcu list based on Alex's suggestion;
  - Applied relaxed write/read to LPC driver;
  - Some bugs fixing and some optimazations based on the comments of V6;

Changes from V5:
  - Made the extio driver more generic and locate in lib/;
  - Supported multiple indirect-IO bus instances;
  - Extended the pci_register_io_range() to support indirect-IO, then dropped
  the I/O reservation used in previous patchset;
  - Reimplemented the ACPI LPC support;
  - Fixed some bugs, including the compile error on other archs, the module
  building failure found by Ming Lei, etc;

Changes from V4:
  - Some revises based on the comments from Bjorn, Rob on V4;
  - Fixed the compile error on some platforms, such as openrisc;

Changes from V3:
  - UART support deferred to a separate patchset; This patchset only support
  ipmi device under LPC;
  - LPC bus I/O range is fixed to 0 ~ (PCIBIOS_MIN_IO - 1), which is separeted
  from PCI/PCIE PIO space;
  - Based on Arnd's remarks, removed the ranges property from Hip06 lpc dts and
  added a new fixup function, of_isa_indirect_io(), to get the I/O address
  directly from LPC dts configurations;
  - Support in(w,l)/out(w,l) for Hip06 lpc I/O;
  - Decouple the header file dependency on the gerenic io.h by defining in/out
  as normal functions in c file;
  - removed unused macro definitions in the LPC driver;

Changes from V2:
  - Support the PIO retrieval from the linux PIO generated by
  pci_address_to_pio. This method replace the 4K PIO reservation in V2;
  - Support the flat-tree earlycon;
  - Some revises based on Arnd's remarks;
  - Make sure the linux PIO range allocated to Hip06 LPC peripherals starts
  from non-ZERO;

Changes from V1:
  - Support the ACPI LPC device;
  - Optimize the dts LPC driver in ISA compatible mode;
  - Reserve the IO range below 4K in avoid the possible conflict with PCI host
  IO ranges;
  - Support the LPC uart and relevant earlycon;

v6 thread here: https://lkml.org/lkml/2017/1/24/25
v5 thread here: https://lkml.org/lkml/2016/11/7/955
v4 thread here: https://lkml.org/lkml/2016/10/20/149
v3 thread here: https://lkml.org/lkml/2016/9/14/326
v2 thread here: https://lkml.org/lkml/2016/9/7/356
v1 thread here: https://lkml.org/lkml/2015/12/29/154


Signed-off-by: Zhichang Yuan <yuanzhichang@hisilicon.com>
zhichang.yuan (7):
  LIBIO: Introduce a generic PIO mapping method
  PCI: Apply the new generic I/O management on PCI IO hosts
  OF: Add missing I/O range exception for indirect-IO  devices
  LPC: Support the device-tree LPC host on Hip06/Hip07
  ACPI: Delay the enumeration on the devices whose dependency has not
    met
  LIBIO: Support the dynamically logical PIO registration of ACPI host
    I/O
  LPC: Add the ACPI LPC support

 .../arm/hisilicon/hisilicon-low-pin-count.txt      |  33 ++
 MAINTAINERS                                        |   8 +
 arch/arm64/boot/dts/hisilicon/hip06-d03.dts        |   4 +
 arch/arm64/boot/dts/hisilicon/hip06.dtsi           |  14 +
 arch/arm64/boot/dts/hisilicon/hip07-d05.dts        |   4 +
 arch/arm64/boot/dts/hisilicon/hip07.dtsi           |  14 +
 drivers/acpi/battery.c                             |   3 -
 drivers/acpi/pci_root.c                            |   8 +-
 drivers/acpi/scan.c                                |   3 +
 drivers/bus/Kconfig                                |   8 +
 drivers/bus/Makefile                               |   1 +
 drivers/bus/hisi_lpc.c                             | 608 +++++++++++++++++++++
 drivers/of/address.c                               |  94 +++-
 drivers/pci/pci.c                                  |  96 +---
 include/asm-generic/io.h                           |  50 ++
 include/linux/io.h                                 |   1 +
 include/linux/libio.h                              |  98 ++++
 include/linux/pci.h                                |   3 +-
 lib/Kconfig                                        |  14 +
 lib/Makefile                                       |   2 +
 lib/libio.c                                        | 548 +++++++++++++++++++
 21 files changed, 1514 insertions(+), 100 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
 create mode 100644 drivers/bus/hisi_lpc.c
 create mode 100644 include/linux/libio.h
 create mode 100644 lib/libio.c

-- 
1.9.1

[toc] | [next] | [standalone]


#1598825 — [PATCH V7 1/7] LIBIO: Introduce a generic PIO mapping method

From"zhichang.yuan" <yuanzhichang@hisilicon.com>
Date2017-03-13 03:30 +0100
Subject[PATCH V7 1/7] LIBIO: Introduce a generic PIO mapping method
Message-ID<tknUd-1XV-3@gated-at.bofh.it>
In reply to#1598822
In commit 41f8bba7f55(of/pci: Add pci_register_io_range() and
pci_pio_to_address()), a new I/O space management was supported. With that
driver, the I/O ranges configured for PCI/PCIE hosts on some architectures can
be mapped to logical PIO, converted easily between CPU address and the
corresponding logicial PIO. Based on this, PCI I/O devices can be accessed in a
memory read/write way through the unified in/out accessors.

But on some archs/platforms, there are bus hosts which access I/O peripherals
with host-local I/O port addresses rather than memory addresses after
memory-mapped.
To support those devices, a more generic I/O mapping method is introduced here.
Through this patch, both the CPU addresses and the host-local port can be
mapped into logical PIO, then all the I/O accesses to either PCI MMIO devices or
host-local I/O peripherals can be unified into the existing I/O accessors
defined asm-generic/io.h and be redirected to the right device-specific hooks
based on the input logical PIO.

Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
---
 include/asm-generic/io.h |  50 ++++++++
 include/linux/io.h       |   1 +
 include/linux/libio.h    |  94 ++++++++++++++
 lib/Kconfig              |  14 ++
 lib/Makefile             |   2 +
 lib/libio.c              | 324 +++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 485 insertions(+)
 create mode 100644 include/linux/libio.h
 create mode 100644 lib/libio.c

diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h
index 7ef015e..91a7ed4 100644
--- a/include/asm-generic/io.h
+++ b/include/asm-generic/io.h
@@ -21,6 +21,8 @@
 
 #include <asm-generic/pci_iomap.h>
 
+#include <linux/libio.h>
+
 #ifndef mmiowb
 #define mmiowb() do {} while (0)
 #endif
@@ -358,51 +360,75 @@ static inline void writesq(volatile void __iomem *addr, const void *buffer,
  */
 
 #ifndef inb
+#ifdef CONFIG_LIBIO
+#define inb libio_inb
+#else
 #define inb inb
 static inline u8 inb(unsigned long addr)
 {
 	return readb(PCI_IOBASE + addr);
 }
+#endif /* CONFIG_LIBIO */
 #endif
 
 #ifndef inw
+#ifdef CONFIG_LIBIO
+#define inw libio_inw
+#else
 #define inw inw
 static inline u16 inw(unsigned long addr)
 {
 	return readw(PCI_IOBASE + addr);
 }
+#endif /* CONFIG_LIBIO */
 #endif
 
 #ifndef inl
+#ifdef CONFIG_LIBIO
+#define inl libio_inl
+#else
 #define inl inl
 static inline u32 inl(unsigned long addr)
 {
 	return readl(PCI_IOBASE + addr);
 }
+#endif /* CONFIG_LIBIO */
 #endif
 
 #ifndef outb
+#ifdef CONFIG_LIBIO
+#define outb libio_outb
+#else
 #define outb outb
 static inline void outb(u8 value, unsigned long addr)
 {
 	writeb(value, PCI_IOBASE + addr);
 }
+#endif /* CONFIG_LIBIO */
 #endif
 
 #ifndef outw
+#ifdef CONFIG_LIBIO
+#define outw libio_outw
+#else
 #define outw outw
 static inline void outw(u16 value, unsigned long addr)
 {
 	writew(value, PCI_IOBASE + addr);
 }
+#endif /* CONFIG_LIBIO */
 #endif
 
 #ifndef outl
+#ifdef CONFIG_LIBIO
+#define outl libio_outl
+#else
 #define outl outl
 static inline void outl(u32 value, unsigned long addr)
 {
 	writel(value, PCI_IOBASE + addr);
 }
+#endif /* CONFIG_LIBIO */
 #endif
 
 #ifndef inb_p
@@ -459,54 +485,78 @@ static inline void outl_p(u32 value, unsigned long addr)
  */
 
 #ifndef insb
+#ifdef CONFIG_LIBIO
+#define insb libio_insb
+#else
 #define insb insb
 static inline void insb(unsigned long addr, void *buffer, unsigned int count)
 {
 	readsb(PCI_IOBASE + addr, buffer, count);
 }
+#endif /* CONFIG_LIBIO */
 #endif
 
 #ifndef insw
+#ifdef CONFIG_LIBIO
+#define insw libio_insw
+#else
 #define insw insw
 static inline void insw(unsigned long addr, void *buffer, unsigned int count)
 {
 	readsw(PCI_IOBASE + addr, buffer, count);
 }
+#endif /* CONFIG_LIBIO */
 #endif
 
 #ifndef insl
+#ifdef CONFIG_LIBIO
+#define insl libio_insl
+#else
 #define insl insl
 static inline void insl(unsigned long addr, void *buffer, unsigned int count)
 {
 	readsl(PCI_IOBASE + addr, buffer, count);
 }
+#endif /* CONFIG_LIBIO */
 #endif
 
 #ifndef outsb
+#ifdef CONFIG_LIBIO
+#define outsb libio_outsb
+#else
 #define outsb outsb
 static inline void outsb(unsigned long addr, const void *buffer,
 			 unsigned int count)
 {
 	writesb(PCI_IOBASE + addr, buffer, count);
 }
+#endif /* CONFIG_LIBIO */
 #endif
 
 #ifndef outsw
+#ifdef CONFIG_LIBIO
+#define outsw libio_outsw
+#else
 #define outsw outsw
 static inline void outsw(unsigned long addr, const void *buffer,
 			 unsigned int count)
 {
 	writesw(PCI_IOBASE + addr, buffer, count);
 }
+#endif /* CONFIG_LIBIO */
 #endif
 
 #ifndef outsl
+#ifdef CONFIG_LIBIO
+#define outsl libio_outsl
+#else
 #define outsl outsl
 static inline void outsl(unsigned long addr, const void *buffer,
 			 unsigned int count)
 {
 	writesl(PCI_IOBASE + addr, buffer, count);
 }
+#endif /* CONFIG_LIBIO */
 #endif
 
 #ifndef insb_p
diff --git a/include/linux/io.h b/include/linux/io.h
index 82ef36e..51ec1aa 100644
--- a/include/linux/io.h
+++ b/include/linux/io.h
@@ -24,6 +24,7 @@
 #include <linux/err.h>
 #include <asm/io.h>
 #include <asm/page.h>
+#include <linux/libio.h>
 
 struct device;
 struct resource;
diff --git a/include/linux/libio.h b/include/linux/libio.h
new file mode 100644
index 0000000..91038aa
--- /dev/null
+++ b/include/linux/libio.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2017 Hisilicon Limited, All Rights Reserved.
+ * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LINUX_LIBIO_H
+#define __LINUX_LIBIO_H
+
+#ifdef __KERNEL__
+
+#include <linux/fwnode.h>
+
+/* This is compatible to PCI MMIO. */
+#define IO_CPU_MMIO		0x01
+/* All hosts where there are no CPU addr */
+#define IO_HOST_INDIRECT	0x02
+
+struct libio_range {
+	struct list_head list;
+	struct fwnode_handle *node;
+	resource_size_t size; /* range size populated */
+	resource_size_t io_start;	/* logical pio start. inclusive */
+	resource_size_t hw_start;
+	unsigned long flags;
+	void *devpara;	/* private parameter of the host device */
+	struct libio_ops *ops;	/* ops operating on this node */
+};
+
+struct libio_ops {
+	u32 (*pfin)(void *devobj, unsigned long ptaddr,	size_t dlen);
+	void (*pfout)(void *devobj, unsigned long ptaddr, u32 outval,
+			size_t dlen);
+	u32 (*pfins)(void *devobj, unsigned long ptaddr, void *inbuf,
+			size_t dlen, unsigned int count);
+	void (*pfouts)(void *devobj, unsigned long ptaddr,
+			const void *outbuf, size_t dlen, unsigned int count);
+};
+
+extern u8 libio_inb(unsigned long addr);
+extern void libio_outb(u8 value, unsigned long addr);
+extern void libio_outw(u16 value, unsigned long addr);
+extern void libio_outl(u32 value, unsigned long addr);
+extern u16 libio_inw(unsigned long addr);
+extern u32 libio_inl(unsigned long addr);
+extern void libio_outb(u8 value, unsigned long addr);
+extern void libio_outw(u16 value, unsigned long addr);
+extern void libio_outl(u32 value, unsigned long addr);
+extern void libio_insb(unsigned long addr, void *buffer, unsigned int count);
+extern void libio_insl(unsigned long addr, void *buffer, unsigned int count);
+extern void libio_insw(unsigned long addr, void *buffer, unsigned int count);
+extern void libio_outsb(unsigned long addr, const void *buffer,
+			unsigned int count);
+extern void libio_outsw(unsigned long addr, const void *buffer,
+			unsigned int count);
+extern void libio_outsl(unsigned long addr, const void *buffer,
+			unsigned int count);
+#ifdef CONFIG_LIBIO
+extern struct libio_range
+*find_io_range_from_fwnode(struct fwnode_handle *fwnode);
+extern unsigned long libio_translate_hwaddr(struct fwnode_handle *fwnode,
+			resource_size_t hw_addr);
+#else
+static inline struct libio_range
+*find_io_range_from_fwnode(struct fwnode_handle *fwnode)
+{
+	return NULL;
+}
+
+static inline unsigned long libio_translate_hwaddr(struct fwnode_handle *fwnode,
+			resource_size_t hw_addr)
+{
+	return -1;
+}
+#endif
+
+extern struct libio_range *register_libio_range(struct libio_range *newrange);
+extern resource_size_t libio_to_hwaddr(unsigned long pio);
+
+extern unsigned long libio_translate_cpuaddr(resource_size_t hw_addr);
+
+#endif /* __KERNEL__ */
+#endif /* __LINUX_LIBIO_H */
diff --git a/lib/Kconfig b/lib/Kconfig
index 0c8b78a..ba9787d5 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -59,6 +59,20 @@ config ARCH_USE_CMPXCHG_LOCKREF
 config ARCH_HAS_FAST_MULTIPLIER
 	bool
 
+config LIBIO
+	bool "Generic logical IO management"
+	def_bool y if PCI && (ARM || ARC || UNICORE32 || SPARC || S390 || CRIS || BLACKFIN || XTENSA || ARM64)
+	help
+	  For some architectures, there are no IO space. To support the
+	  accesses to legacy I/O devices on those architectures, kernel
+	  implemented the memory mapped I/O mechanism based on bridge bus
+	  supports. But for some buses which do not support MMIO, the
+	  peripherals there should be accessed with device-specific way.
+	  To abstract those different I/O accesses into unified I/O accessors,
+	  this option provide a generic I/O space management way after mapping
+	  the device I/O to system logical/fake I/O and help to hide all the
+	  hardware detail.
+
 config CRC_CCITT
 	tristate "CRC-CCITT functions"
 	help
diff --git a/lib/Makefile b/lib/Makefile
index 320ac46a..9c4cd24 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -77,6 +77,8 @@ obj-$(CONFIG_HAS_IOMEM) += iomap_copy.o devres.o
 obj-$(CONFIG_CHECK_SIGNATURE) += check_signature.o
 obj-$(CONFIG_DEBUG_LOCKING_API_SELFTESTS) += locking-selftest.o
 
+obj-$(CONFIG_LIBIO) += libio.o
+
 obj-$(CONFIG_GENERIC_HWEIGHT) += hweight.o
 
 obj-$(CONFIG_BTREE) += btree.o
diff --git a/lib/libio.c b/lib/libio.c
new file mode 100644
index 0000000..e42f50b
--- /dev/null
+++ b/lib/libio.c
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2017 Hisilicon Limited, All Rights Reserved.
+ * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/of.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/rculist.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+
+/* A list of all the IO hosts registered. ONLY THE HOST nodes. */
+static LIST_HEAD(io_range_list);
+static DEFINE_MUTEX(io_range_mutex);
+
+/*
+ * allocate a free range for this registration.
+ *
+ * @new_range: point to the node awaiting this registration.
+ *		part of the fields are as input parameters. This node
+ *		is allocated and initialized by caller;
+ * @prev: points to the last node before the return;
+ *
+ * return 0 for success, other are fail.
+ */
+static int libio_alloc_range(struct libio_range *new_range,
+		struct list_head **prev)
+{
+	struct libio_range *entry;
+	unsigned long align = 1;
+	unsigned long tmp_start;
+	unsigned long idle_start, idle_end;
+
+	if (new_range->flags & IO_CPU_MMIO)
+		align = PAGE_SIZE;
+	idle_start = 0;
+	*prev = &io_range_list;
+	list_for_each_entry_rcu(entry, &io_range_list, list) {
+		if (idle_start > entry->io_start) {
+			WARN(1, "skip an invalid io range during traversal!\n");
+			goto nextentry;
+		}
+		/* set the end edge. */
+		if (idle_start == entry->io_start) {
+			struct libio_range *next;
+
+			idle_start = entry->io_start + entry->size;
+			next = list_next_or_null_rcu(&io_range_list,
+				&entry->list, struct libio_range, list);
+			if (next) {
+				entry = next;
+			} else {
+				*prev = &entry->list;
+				break;
+			}
+		}
+		idle_end = entry->io_start - 1;
+
+		/* contiguous range... */
+		if (idle_start > idle_end)
+			goto nextentry;
+
+		tmp_start = idle_start;
+		idle_start = ALIGN(idle_start, align);
+		if (idle_start >= tmp_start &&
+			idle_start + new_range->size <= idle_end) {
+			new_range->io_start = idle_start;
+			*prev = &entry->list;
+			return 0;
+		}
+
+nextentry:
+		idle_start = entry->io_start + entry->size;
+		*prev = &entry->list;
+	}
+	/* check the last free gap... */
+	idle_end = IO_SPACE_LIMIT;
+
+	tmp_start = idle_start;
+	idle_start = ALIGN(idle_start, align);
+	if (idle_start >= tmp_start &&
+		idle_start + new_range->size <= idle_end) {
+		new_range->io_start = idle_start;
+		return 0;
+	}
+
+	return -EBUSY;
+}
+
+/*
+ * traverse the io_range_list to find the registered node whose device node
+ * and/or physical IO address match to.
+ */
+struct libio_range *find_io_range_from_fwnode(struct fwnode_handle *fwnode)
+{
+	struct libio_range *range;
+
+	list_for_each_entry_rcu(range, &io_range_list, list) {
+		if (range->node == fwnode)
+			return range;
+	}
+	return NULL;
+}
+
+/*
+ * Search a io_range registered which match the fwnode and addr.
+ *
+ * @fwnode: the host fwnode which must be valid;
+ * @start: the start hardware address of this search;
+ * @end: the end hardware address of this search. can be equal to @start;
+ *
+ * return NULL when there is no matched node; IS_ERR() means ERROR;
+ * valid virtual address represent a matched node was found.
+ */
+static struct libio_range *
+libio_find_range_byaddr(struct fwnode_handle *fwnode,
+			resource_size_t start, resource_size_t end)
+{
+	struct libio_range *entry;
+
+	list_for_each_entry_rcu(entry, &io_range_list, list) {
+		if (entry->node != fwnode)
+			continue;
+		/* without any overlap with current range */
+		if (start >= entry->hw_start + entry->size ||
+			end < entry->hw_start)
+			continue;
+		/* overlap is not supported now. */
+		if (start < entry->hw_start ||
+			end >= entry->hw_start + entry->size)
+			return ERR_PTR(-EBUSY);
+		/* had been registered. */
+		return entry;
+	}
+
+	return NULL;
+}
+
+/*
+ * register a io range node in the io range list.
+ *
+ * @newrange: pointer to the io range to be registered.
+ *
+ * return 'newrange' when success, ERR_VALUE() is for failures.
+ * specially, return a valid pointer which is not equal to 'newrange' when
+ * the io range had been registered before.
+ */
+struct libio_range *register_libio_range(struct libio_range *newrange)
+{
+	int err;
+	struct libio_range *range;
+	struct list_head *prev;
+
+	if (!newrange || !newrange->node || !newrange->size)
+		return ERR_PTR(-EINVAL);
+
+	mutex_lock(&io_range_mutex);
+	range = libio_find_range_byaddr(newrange->node, newrange->hw_start,
+			newrange->hw_start + newrange->size - 1);
+	if (range) {
+		if (!IS_ERR(range))
+			pr_info("the request IO range had been registered!\n");
+		else
+			pr_err("registering IO[%pa - sz%pa) got failed!\n",
+				&newrange->hw_start, &newrange->size);
+		return range;
+	}
+
+	err = libio_alloc_range(newrange, &prev);
+	if (!err)
+		/* the bus IO range list is ordered by pio. */
+		list_add_rcu(&newrange->list, prev);
+	else
+		pr_err("can't find free %pa logical IO range!\n",
+			&newrange->size);
+
+	mutex_unlock(&io_range_mutex);
+	return err ? ERR_PTR(err) : newrange;
+}
+
+/*
+ * Translate the input logical pio to the corresponding hardware address.
+ * The input pio should be unique in the whole logical PIO space.
+ */
+resource_size_t libio_to_hwaddr(unsigned long pio)
+{
+	struct libio_range *range;
+
+	list_for_each_entry_rcu(range, &io_range_list, list) {
+		if (pio < range->io_start)
+			break;
+
+		if (pio < range->io_start + range->size)
+			return pio - range->io_start + range->hw_start;
+	}
+
+	return -1;
+}
+
+/*
+ * This function is generic for translating a hardware address to logical PIO.
+ * @hw_addr: the hardware address of host, can be CPU address or host-local
+ *		address;
+ */
+unsigned long
+libio_translate_hwaddr(struct fwnode_handle *fwnode, resource_size_t addr)
+{
+	struct libio_range *range;
+
+	range = libio_find_range_byaddr(fwnode, addr, addr);
+	if (!range)
+		return -1;
+
+	return addr - range->hw_start + range->io_start;
+}
+
+unsigned long
+libio_translate_cpuaddr(resource_size_t addr)
+{
+	struct libio_range *range;
+
+	list_for_each_entry_rcu(range, &io_range_list, list) {
+		if (!(range->flags & IO_CPU_MMIO))
+			continue;
+		if (addr >= range->hw_start &&
+			addr < range->hw_start + range->size)
+			return addr - range->hw_start + range->io_start;
+	}
+	return -1;
+}
+
+#ifdef PCI_IOBASE
+static struct libio_range *find_io_range(unsigned long pio)
+{
+	struct libio_range *range;
+
+	list_for_each_entry_rcu(range, &io_range_list, list) {
+		if (range->io_start > pio)
+			return NULL;
+		if (pio < range->io_start + range->size)
+			return range;
+	}
+	return NULL;
+}
+
+#define BUILD_IO(bw, type)						\
+type libio_in##bw(unsigned long addr)					\
+{									\
+	struct libio_range *entry = find_io_range(addr);		\
+									\
+	if (entry && entry->ops)					\
+		return entry->ops->pfin(entry->devpara,			\
+					addr, sizeof(type));		\
+	return read##bw(PCI_IOBASE + addr);				\
+}									\
+									\
+void libio_out##bw(type value, unsigned long addr)			\
+{									\
+	struct libio_range *entry = find_io_range(addr);		\
+									\
+	if (entry && entry->ops)					\
+		entry->ops->pfout(entry->devpara,			\
+					addr, value, sizeof(type));	\
+	else								\
+		write##bw(value, PCI_IOBASE + addr);			\
+}									\
+									\
+void libio_ins##bw(unsigned long addr, void *buffer, unsigned int count)\
+{									\
+	struct libio_range *entry = find_io_range(addr);		\
+									\
+	if (entry && entry->ops)					\
+		entry->ops->pfins(entry->devpara,			\
+				addr, buffer, sizeof(type), count);	\
+	else								\
+		reads##bw(PCI_IOBASE + addr, buffer, count);		\
+}									\
+									\
+void libio_outs##bw(unsigned long addr, const void *buffer,		\
+		    unsigned int count)					\
+{									\
+	struct libio_range *entry = find_io_range(addr);		\
+									\
+	if (entry && entry->ops)					\
+		entry->ops->pfouts(entry->devpara,			\
+				addr, buffer, sizeof(type), count);	\
+	else								\
+		writes##bw(PCI_IOBASE + addr, buffer, count);	\
+}
+
+BUILD_IO(b, u8)
+
+EXPORT_SYMBOL(libio_inb);
+EXPORT_SYMBOL(libio_outb);
+EXPORT_SYMBOL(libio_insb);
+EXPORT_SYMBOL(libio_outsb);
+
+BUILD_IO(w, u16)
+
+EXPORT_SYMBOL(libio_inw);
+EXPORT_SYMBOL(libio_outw);
+EXPORT_SYMBOL(libio_insw);
+EXPORT_SYMBOL(libio_outsw);
+
+BUILD_IO(l, u32)
+
+EXPORT_SYMBOL(libio_inl);
+EXPORT_SYMBOL(libio_outl);
+EXPORT_SYMBOL(libio_insl);
+EXPORT_SYMBOL(libio_outsl);
+#endif /* PCI_IOBASE */
-- 
1.9.1

[toc] | [prev] | [next] | [standalone]


#1598826 — [PATCH V7 5/7] ACPI: Delay the enumeration on the devices whose dependency has not met

From"zhichang.yuan" <yuanzhichang@hisilicon.com>
Date2017-03-13 03:30 +0100
Subject[PATCH V7 5/7] ACPI: Delay the enumeration on the devices whose dependency has not met
Message-ID<tknUe-1XV-7@gated-at.bofh.it>
In reply to#1598822
In commit 40e7fcb1929(ACPI: Add _DEP support to fix battery issue on Asus
T100TA), the '_DEP' was supported to solve the dependency of Asus battery. But
this patch is specific to Asus battery device.
In the real world, there are other devices which need the dependency to play the
role on the enumeration order. For example, all the Hip06 LPC
periperals(IPMI-BT, uart, etc) must be scanned after the LPC host driver
finished the probing. So, it makes sense to add a checking whether the ACPI
device meet all the dependencies during its enumeration slot, if not, the
enumeration will be delayed till all dependency master finish their work.

This patch adds the dependency checking in ACPI enumeration, also the
corresponding handling to retrigger the Hip06 LPC peripherals' scanning.

Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
---
 drivers/acpi/battery.c |  3 ---
 drivers/acpi/scan.c    |  3 +++
 drivers/bus/hisi_lpc.c | 12 +++++++++++-
 3 files changed, 14 insertions(+), 4 deletions(-)

diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c
index 4ef1e46..e8d1af1 100644
--- a/drivers/acpi/battery.c
+++ b/drivers/acpi/battery.c
@@ -1210,9 +1210,6 @@ static int acpi_battery_add(struct acpi_device *device)
 	if (!device)
 		return -EINVAL;
 
-	if (device->dep_unmet)
-		return -EPROBE_DEFER;
-
 	battery = kzalloc(sizeof(struct acpi_battery), GFP_KERNEL);
 	if (!battery)
 		return -ENOMEM;
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 1926918..97721b1 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -1843,6 +1843,9 @@ static void acpi_bus_attach(struct acpi_device *device)
 	if (device->handler)
 		goto ok;
 
+	if (device->dep_unmet)
+		return;
+
 	if (!device->flags.initialized) {
 		device->flags.power_manageable =
 			device->power.states[ACPI_STATE_D0].flags.valid;
diff --git a/drivers/bus/hisi_lpc.c b/drivers/bus/hisi_lpc.c
index 03cf19a..345ea12 100644
--- a/drivers/bus/hisi_lpc.c
+++ b/drivers/bus/hisi_lpc.c
@@ -516,8 +516,18 @@ static int hisilpc_probe(struct platform_device *pdev)
 
 	/*
 	 * It is time to start the children scannings....
+	 * For ACPI children, the corresponding devices will be created after
+	 * retriggering the ACPI scanning by removing the dependency blocking.
 	 */
-	if (!has_acpi_companion(dev)) {
+	if (has_acpi_companion(dev)) {
+		struct acpi_device *adev;
+
+		adev = to_acpi_device_node(dev->fwnode);
+		if (!adev)
+			ret = -ENODEV;
+		else
+			acpi_walk_dep_device_list(adev->handle);
+	} else {
 		ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
 		if (ret)
 			dev_err(dev, "OF: enumerate LPC bus fail(%d)\n", ret);
-- 
1.9.1

[toc] | [prev] | [next] | [standalone]


#1599842 — Re: [PATCH V7 5/7] ACPI: Delay the enumeration on the devices whose dependency has not met

From"Rafael J. Wysocki" <rjw@rjwysocki.net>
Date2017-03-13 22:40 +0100
SubjectRe: [PATCH V7 5/7] ACPI: Delay the enumeration on the devices whose dependency has not met
Message-ID<tkFR7-6IY-13@gated-at.bofh.it>
In reply to#1598826
On Monday, March 13, 2017 10:42:41 AM zhichang.yuan wrote:
> In commit 40e7fcb1929(ACPI: Add _DEP support to fix battery issue on Asus
> T100TA), the '_DEP' was supported to solve the dependency of Asus battery. But
> this patch is specific to Asus battery device.
> In the real world, there are other devices which need the dependency to play the
> role on the enumeration order. For example, all the Hip06 LPC
> periperals(IPMI-BT, uart, etc) must be scanned after the LPC host driver
> finished the probing. So, it makes sense to add a checking whether the ACPI
> device meet all the dependencies during its enumeration slot, if not, the
> enumeration will be delayed till all dependency master finish their work.
> 
> This patch adds the dependency checking in ACPI enumeration, also the
> corresponding handling to retrigger the Hip06 LPC peripherals' scanning.

AFAICS, _DEP is generally abused in the wild and cannot be made generic.  Sorry.

Thanks,
Rafael

[toc] | [prev] | [standalone]


Back to top | Article view | linux.kernel


csiph-web