linux-pci.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH V6 0/5] LPC: legacy ISA I/O support
@ 2017-01-24  7:05 zhichang.yuan
  2017-01-24  7:05 ` [PATCH V6 1/5] LIB: Indirect ISA/LPC port IO introduced zhichang.yuan
                   ` (4 more replies)
  0 siblings, 5 replies; 37+ messages in thread
From: zhichang.yuan @ 2017-01-24  7:05 UTC (permalink / raw)
  To: catalin.marinas, will.deacon, robh+dt, frowand.list, bhelgaas,
	rafael, mark.rutland, brian.starkey, olof, arnd,
	linux-arm-kernel
  Cc: lorenzo.pieralisi, benh, linux-kernel, linuxarm, devicetree,
	linux-pci, linux-serial, minyard, liviu.dudau, zourongrong,
	john.garry, gabriele.paoloni, zhichang.yuan02, kantyzc, xuwei5,
	zhichang.yuan

This patch 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 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 LPC.
To solve this issue and keep the relevant existing peripherals' driver
untouched, this patch set redefines the in/out() pair to support both the IO
operations for Hip06 LPC and the original MMIO. The way specific to Hip06 is
named as indirect-IO in this patchset.

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;


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 (5):
  LIB: Indirect ISA/LPC port IO introduced
  PCI: Adapt pci_register_io_range() for indirect-IO and PCI I/O
    translation
  OF: Add missing I/O range exception for indirect-IO devices
  LPC: Support the device-tree LPC host on Hip06/Hip07
  LPC: Add the ACPI LPC support

 .../arm/hisilicon/hisilicon-low-pin-count.txt      |  33 ++
 MAINTAINERS                                        |   9 +
 arch/arm64/boot/dts/hisilicon/hip06-d03.dts        |   4 +
 arch/arm64/boot/dts/hisilicon/hip06.dtsi           |  14 +
 drivers/acpi/pci_root.c                            |  12 +-
 drivers/bus/Kconfig                                |   8 +
 drivers/bus/Makefile                               |   1 +
 drivers/bus/hisi_lpc.c                             | 625 +++++++++++++++++++++
 drivers/of/address.c                               |  95 +++-
 drivers/pci/pci.c                                  |  44 +-
 include/asm-generic/io.h                           |  50 ++
 include/linux/extio.h                              |  89 +++
 include/linux/io.h                                 |   1 +
 include/linux/pci.h                                |   7 +-
 lib/Kconfig                                        |   8 +
 lib/Makefile                                       |   2 +
 lib/extio.c                                        | 375 +++++++++++++
 17 files changed, 1342 insertions(+), 35 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/extio.h
 create mode 100644 lib/extio.c

-- 
1.9.1


^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH V6 1/5] LIB: Indirect ISA/LPC port IO introduced
  2017-01-24  7:05 [PATCH V6 0/5] LPC: legacy ISA I/O support zhichang.yuan
@ 2017-01-24  7:05 ` zhichang.yuan
  2017-01-30 17:12   ` Alexander Graf
  2017-01-31  0:09   ` Bjorn Helgaas
  2017-01-24  7:05 ` [PATCH V6 2/5] PCI: Adapt pci_register_io_range() for indirect-IO and PCI I/O translation zhichang.yuan
                   ` (3 subsequent siblings)
  4 siblings, 2 replies; 37+ messages in thread
From: zhichang.yuan @ 2017-01-24  7:05 UTC (permalink / raw)
  To: catalin.marinas, will.deacon, robh+dt, frowand.list, bhelgaas,
	rafael, mark.rutland, brian.starkey, olof, arnd,
	linux-arm-kernel
  Cc: lorenzo.pieralisi, benh, linux-kernel, linuxarm, devicetree,
	linux-pci, linux-serial, minyard, liviu.dudau, zourongrong,
	john.garry, gabriele.paoloni, zhichang.yuan02, kantyzc, xuwei5,
	zhichang.yuan

Low-pin-count interface is integrated into some SoCs. The accesses to those
peripherals under LPC make use of I/O ports rather than the memory mapped I/O.

To drive these devices, this patch introduces a method named indirect-IO.
In this method the in/out() accessor in include/asm-generic/io.h will be
redefined. When upper layer drivers call in/out() with those known legacy port
addresses to access the peripherals, the I/O operations will be routed to the
right hooks which are registered specific to the host device, such as LPC.
Then the hardware relevant manupulations are finished by the corresponding
host.

According to the comments on V5, this patch adds a common indirect-IO driver
which support this I/O indirection to the generic directory.

In the later pathches, some host-relevant drivers are implemented to support
the specific I/O hooks and register them.
Based on these, the upper layer drivers which depend on in/out() can work well
without any extra work or any changes.

Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
Signed-off-by: John Garry <john.garry@huawei.com>
---
 include/asm-generic/io.h |  50 ++++++++++++++++
 include/linux/extio.h    |  85 +++++++++++++++++++++++++++
 include/linux/io.h       |   1 +
 lib/Kconfig              |   8 +++
 lib/Makefile             |   2 +
 lib/extio.c              | 147 +++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 293 insertions(+)
 create mode 100644 include/linux/extio.h
 create mode 100644 lib/extio.c

diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h
index 7ef015e..c0d6db1 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/extio.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_INDIRECT_PIO
+#define inb extio_inb
+#else
 #define inb inb
 static inline u8 inb(unsigned long addr)
 {
 	return readb(PCI_IOBASE + addr);
 }
+#endif /* CONFIG_INDIRECT_PIO */
 #endif
 
 #ifndef inw
+#ifdef CONFIG_INDIRECT_PIO
+#define inw extio_inw
+#else
 #define inw inw
 static inline u16 inw(unsigned long addr)
 {
 	return readw(PCI_IOBASE + addr);
 }
+#endif /* CONFIG_INDIRECT_PIO */
 #endif
 
 #ifndef inl
+#ifdef CONFIG_INDIRECT_PIO
+#define inl extio_inl
+#else
 #define inl inl
 static inline u32 inl(unsigned long addr)
 {
 	return readl(PCI_IOBASE + addr);
 }
+#endif /* CONFIG_INDIRECT_PIO */
 #endif
 
 #ifndef outb
+#ifdef CONFIG_INDIRECT_PIO
+#define outb extio_outb
+#else
 #define outb outb
 static inline void outb(u8 value, unsigned long addr)
 {
 	writeb(value, PCI_IOBASE + addr);
 }
+#endif /* CONFIG_INDIRECT_PIO */
 #endif
 
 #ifndef outw
+#ifdef CONFIG_INDIRECT_PIO
+#define outw extio_outw
+#else
 #define outw outw
 static inline void outw(u16 value, unsigned long addr)
 {
 	writew(value, PCI_IOBASE + addr);
 }
+#endif /* CONFIG_INDIRECT_PIO */
 #endif
 
 #ifndef outl
+#ifdef CONFIG_INDIRECT_PIO
+#define outl extio_outl
+#else
 #define outl outl
 static inline void outl(u32 value, unsigned long addr)
 {
 	writel(value, PCI_IOBASE + addr);
 }
+#endif /* CONFIG_INDIRECT_PIO */
 #endif
 
 #ifndef inb_p
@@ -459,54 +485,78 @@ static inline void outl_p(u32 value, unsigned long addr)
  */
 
 #ifndef insb
+#ifdef CONFIG_INDIRECT_PIO
+#define insb extio_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_INDIRECT_PIO */
 #endif
 
 #ifndef insw
+#ifdef CONFIG_INDIRECT_PIO
+#define insw extio_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_INDIRECT_PIO */
 #endif
 
 #ifndef insl
+#ifdef CONFIG_INDIRECT_PIO
+#define insl extio_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_INDIRECT_PIO */
 #endif
 
 #ifndef outsb
+#ifdef CONFIG_INDIRECT_PIO
+#define outsb extio_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_INDIRECT_PIO */
 #endif
 
 #ifndef outsw
+#ifdef CONFIG_INDIRECT_PIO
+#define outsw extio_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_INDIRECT_PIO */
 #endif
 
 #ifndef outsl
+#ifdef CONFIG_INDIRECT_PIO
+#define outsl extio_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_INDIRECT_PIO */
 #endif
 
 #ifndef insb_p
diff --git a/include/linux/extio.h b/include/linux/extio.h
new file mode 100644
index 0000000..2ca7eab
--- /dev/null
+++ b/include/linux/extio.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2016 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_EXTIO_H
+#define __LINUX_EXTIO_H
+
+#ifdef __KERNEL__
+
+#include <linux/fwnode.h>
+
+struct extio_ops {
+	u64 (*pfin)(void *devobj, unsigned long ptaddr,	size_t dlen);
+	void (*pfout)(void *devobj, unsigned long ptaddr, u32 outval,
+					size_t dlen);
+	u64 (*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);
+};
+
+struct extio_node {
+	unsigned long bus_start;	/* bus start address */
+	unsigned long io_start;	/* io port token corresponding to bus_start */
+	size_t range_size;	/* size of the extio node operating range */
+	struct fwnode_handle *fwnode;
+	struct list_head list;
+	struct extio_ops *ops;	/* ops operating on this node */
+	void *devpara;	/* private parameter of the host device */
+};
+
+extern u8 extio_inb(unsigned long addr);
+extern void extio_outb(u8 value, unsigned long addr);
+extern void extio_outw(u16 value, unsigned long addr);
+extern void extio_outl(u32 value, unsigned long addr);
+extern u16 extio_inw(unsigned long addr);
+extern u32 extio_inl(unsigned long addr);
+extern void extio_outb(u8 value, unsigned long addr);
+extern void extio_outw(u16 value, unsigned long addr);
+extern void extio_outl(u32 value, unsigned long addr);
+extern void extio_insb(unsigned long addr, void *buffer, unsigned int count);
+extern void extio_insl(unsigned long addr, void *buffer, unsigned int count);
+extern void extio_insw(unsigned long addr, void *buffer, unsigned int count);
+extern void extio_outsb(unsigned long addr, const void *buffer,
+			unsigned int count);
+extern void extio_outsw(unsigned long addr, const void *buffer,
+			unsigned int count);
+extern void extio_outsl(unsigned long addr, const void *buffer,
+			unsigned int count);
+
+#ifdef CONFIG_INDIRECT_PIO
+extern struct extio_node *extio_find_node(struct fwnode_handle *node);
+
+extern unsigned long
+extio_translate(struct fwnode_handle *node, unsigned long bus_addr);
+#else
+static inline struct extio_node *extio_find_node(struct fwnode_handle *node)
+{
+	return NULL;
+}
+
+static inline unsigned long
+extio_translate(struct fwnode_handle *node, unsigned long bus_addr)
+{
+	return -1;
+}
+#endif
+extern void register_extio(struct extio_node *node);
+
+#endif /* __KERNEL__ */
+#endif /* __LINUX_EXTIO_H */
diff --git a/include/linux/io.h b/include/linux/io.h
index 82ef36e..6c68478 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/extio.h>
 
 struct device;
 struct resource;
diff --git a/lib/Kconfig b/lib/Kconfig
index 260a80e..1d27c44 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -59,6 +59,14 @@ config ARCH_USE_CMPXCHG_LOCKREF
 config ARCH_HAS_FAST_MULTIPLIER
 	bool
 
+config INDIRECT_PIO
+	bool "access peripherals with legacy I/O port"
+	depends on PCI
+	help
+	  Support special accessors for ISA I/O devices. This is needed for
+	  SoCs that have not I/O space and do not support standard MMIO
+	  read/write for the ISA range.
+
 config CRC_CCITT
 	tristate "CRC-CCITT functions"
 	help
diff --git a/lib/Makefile b/lib/Makefile
index bc4073a..bf03e05 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -70,6 +70,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_INDIRECT_PIO)	+= extio.o
+
 obj-$(CONFIG_GENERIC_HWEIGHT) += hweight.o
 
 obj-$(CONFIG_BTREE) += btree.o
diff --git a/lib/extio.c b/lib/extio.c
new file mode 100644
index 0000000..46228de
--- /dev/null
+++ b/lib/extio.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2016 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/io.h>
+#include <linux/spinlock.h>
+
+static LIST_HEAD(extio_dev_list);
+static DEFINE_RWLOCK(extio_list_lock);
+
+void register_extio(struct extio_node *node)
+{
+	write_lock(&extio_list_lock);
+	list_add_tail(&node->list, &extio_dev_list);
+	write_unlock(&extio_list_lock);
+}
+
+static struct extio_node *find_extio_token(unsigned long addr)
+{
+	struct extio_node *extio_entry;
+
+	read_lock(&extio_list_lock);
+	list_for_each_entry(extio_entry, &extio_dev_list, list) {
+		if ((addr < extio_entry->io_start + extio_entry->range_size) &&
+			(addr >= extio_entry->io_start))
+			break;
+	}
+	read_unlock(&extio_list_lock);
+	return (&extio_entry->list == &extio_dev_list) ? NULL : extio_entry;
+}
+
+struct extio_node *extio_find_node(struct fwnode_handle *node)
+{
+	struct extio_node *entry;
+
+	read_lock(&extio_list_lock);
+	list_for_each_entry(entry, &extio_dev_list, list) {
+		if (entry->fwnode == node)
+			break;
+	}
+	read_unlock(&extio_list_lock);
+
+	return (&entry->list == &extio_dev_list) ? NULL : entry;
+}
+
+unsigned long extio_translate(struct fwnode_handle *node,
+		unsigned long bus_addr)
+{
+	struct extio_node *entry;
+	unsigned long port_id = -1;
+
+	read_lock(&extio_list_lock);
+	list_for_each_entry(entry, &extio_dev_list, list) {
+		if (entry->fwnode == node &&
+			bus_addr >= entry->bus_start &&
+			bus_addr - entry->bus_start < entry->range_size)
+			port_id = entry->io_start + bus_addr -
+					entry->bus_start;
+	}
+	read_unlock(&extio_list_lock);
+
+	return port_id;
+}
+
+#ifdef PCI_IOBASE
+
+#define BUILD_EXTIO(bw, type)						\
+type extio_in##bw(unsigned long addr)					\
+{									\
+	struct extio_node *extio_entry = find_extio_token(addr);	\
+									\
+	if (!extio_entry)						\
+		return read##bw(PCI_IOBASE + addr);			\
+	return extio_entry->ops->pfin ?					\
+			extio_entry->ops->pfin(extio_entry->devpara,	\
+			addr, sizeof(type)) : -1;			\
+}									\
+									\
+void extio_out##bw(type value, unsigned long addr)			\
+{									\
+	struct extio_node *extio_entry = find_extio_token(addr);	\
+									\
+	if (!extio_entry)						\
+		write##bw(value, PCI_IOBASE + addr);			\
+	else if (extio_entry->ops->pfout)				\
+		extio_entry->ops->pfout(extio_entry->devpara,		\
+				addr, value, sizeof(type));		\
+}									\
+									\
+void extio_ins##bw(unsigned long addr, void *buffer, unsigned int count)\
+{									\
+	struct extio_node *extio_entry = find_extio_token(addr);	\
+									\
+	if (!extio_entry)						\
+		reads##bw(PCI_IOBASE + addr, buffer, count);		\
+	else if (extio_entry->ops->pfins)				\
+		extio_entry->ops->pfins(extio_entry->devpara,		\
+				addr, buffer, sizeof(type), count);	\
+}									\
+									\
+void extio_outs##bw(unsigned long addr, const void *buffer,		\
+		    unsigned int count)					\
+{									\
+	struct extio_node *extio_entry = find_extio_token(addr);	\
+									\
+	if (!extio_entry)						\
+		writes##bw(PCI_IOBASE + addr, buffer, count);		\
+	else if (extio_entry->ops->pfouts)				\
+		extio_entry->ops->pfouts(extio_entry->devpara,		\
+				addr, buffer, sizeof(type), count);	\
+}
+
+BUILD_EXTIO(b, u8)
+
+EXPORT_SYMBOL(extio_inb);
+EXPORT_SYMBOL(extio_outb);
+EXPORT_SYMBOL(extio_insb);
+EXPORT_SYMBOL(extio_outsb);
+
+BUILD_EXTIO(w, u16)
+
+EXPORT_SYMBOL(extio_inw);
+EXPORT_SYMBOL(extio_outw);
+EXPORT_SYMBOL(extio_insw);
+EXPORT_SYMBOL(extio_outsw);
+
+BUILD_EXTIO(l, u32)
+
+EXPORT_SYMBOL(extio_inl);
+EXPORT_SYMBOL(extio_outl);
+EXPORT_SYMBOL(extio_insl);
+EXPORT_SYMBOL(extio_outsl);
+
+#endif /* PCI_IOBASE */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH V6 2/5] PCI: Adapt pci_register_io_range() for indirect-IO and  PCI I/O translation
  2017-01-24  7:05 [PATCH V6 0/5] LPC: legacy ISA I/O support zhichang.yuan
  2017-01-24  7:05 ` [PATCH V6 1/5] LIB: Indirect ISA/LPC port IO introduced zhichang.yuan
@ 2017-01-24  7:05 ` zhichang.yuan
  2017-01-31  0:10   ` Bjorn Helgaas
                     ` (2 more replies)
  2017-01-24  7:05 ` [PATCH V6 3/5] OF: Add missing I/O range exception for indirect-IO devices zhichang.yuan
                   ` (2 subsequent siblings)
  4 siblings, 3 replies; 37+ messages in thread
From: zhichang.yuan @ 2017-01-24  7:05 UTC (permalink / raw)
  To: catalin.marinas, will.deacon, robh+dt, frowand.list, bhelgaas,
	rafael, mark.rutland, brian.starkey, olof, arnd,
	linux-arm-kernel
  Cc: lorenzo.pieralisi, benh, linux-kernel, linuxarm, devicetree,
	linux-pci, linux-serial, minyard, liviu.dudau, zourongrong,
	john.garry, gabriele.paoloni, zhichang.yuan02, kantyzc, xuwei5,
	zhichang.yuan

After indirect-IO is introduced, system must can assigned indirect-IO devices
with logical I/O ranges which are different from those for PCI I/O devices.
Otherwise, I/O accessors can't identify whether the I/O port is for memory
mapped I/O or indirect-IO.
As current helper, pci_register_io_range(), is used for PCI I/O ranges
registration and translation, indirect-IO devices should also apply these
helpers to manage the I/O ranges. It will be easy to ensure the assigned
logical I/O ranges unique.
But for indirect-IO devices, there is no cpu address. The current
pci_register_io_range() can not work for this case.

This patch makes some changes on the pci_register_io_range() to support the
I/O range registration with device's fwnode also. After this, the indirect-IO
devices can register the device-local I/O range to system logical I/O and
easily perform the translation between device-local I/O range and sytem
logical I/O range.

Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
 drivers/acpi/pci_root.c | 12 +++++-------
 drivers/of/address.c    |  8 ++------
 drivers/pci/pci.c       | 44 ++++++++++++++++++++++++++++++++++++++++----
 include/linux/pci.h     |  7 +++++--
 4 files changed, 52 insertions(+), 19 deletions(-)

diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
index bf601d4..6cadf05 100644
--- a/drivers/acpi/pci_root.c
+++ b/drivers/acpi/pci_root.c
@@ -730,7 +730,8 @@ static void acpi_pci_root_validate_resources(struct device *dev,
 	}
 }
 
-static void acpi_pci_root_remap_iospace(struct resource_entry *entry)
+static void acpi_pci_root_remap_iospace(struct fwnode_handle *node,
+					struct resource_entry *entry)
 {
 #ifdef PCI_IOBASE
 	struct resource *res = entry->res;
@@ -739,11 +740,7 @@ static void acpi_pci_root_remap_iospace(struct resource_entry *entry)
 	resource_size_t length = resource_size(res);
 	unsigned long port;
 
-	if (pci_register_io_range(cpu_addr, length))
-		goto err;
-
-	port = pci_address_to_pio(cpu_addr);
-	if (port == (unsigned long)-1)
+	if (pci_register_io_range(node, cpu_addr, length, &port))
 		goto err;
 
 	res->start = port;
@@ -781,7 +778,8 @@ int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info)
 	else {
 		resource_list_for_each_entry_safe(entry, tmp, list) {
 			if (entry->res->flags & IORESOURCE_IO)
-				acpi_pci_root_remap_iospace(entry);
+				acpi_pci_root_remap_iospace(&device->fwnode,
+							    entry);
 
 			if (entry->res->flags & IORESOURCE_DISABLED)
 				resource_list_destroy_entry(entry);
diff --git a/drivers/of/address.c b/drivers/of/address.c
index 02b2903..d85d228 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -2,6 +2,7 @@
 #define pr_fmt(fmt)	"OF: " fmt
 
 #include <linux/device.h>
+#include <linux/fwnode.h>
 #include <linux/io.h>
 #include <linux/ioport.h>
 #include <linux/module.h>
@@ -323,14 +324,9 @@ int of_pci_range_to_resource(struct of_pci_range *range,
 
 	if (res->flags & IORESOURCE_IO) {
 		unsigned long port;
-		err = pci_register_io_range(range->cpu_addr, range->size);
+		err = pci_register_io_range(&np->fwnode, range->cpu_addr, range->size, &port);
 		if (err)
 			goto invalid_range;
-		port = pci_address_to_pio(range->cpu_addr);
-		if (port == (unsigned long)-1) {
-			err = -EINVAL;
-			goto invalid_range;
-		}
 		res->start = port;
 	} else {
 		if ((sizeof(resource_size_t) < 8) &&
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index a881c0d..5289221 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -3241,6 +3241,7 @@ int pci_request_regions_exclusive(struct pci_dev *pdev, const char *res_name)
 #ifdef PCI_IOBASE
 struct io_range {
 	struct list_head list;
+	struct fwnode_handle *node;
 	phys_addr_t start;
 	resource_size_t size;
 };
@@ -3253,7 +3254,8 @@ struct io_range {
  * Record the PCI IO range (expressed as CPU physical address + size).
  * Return a negative value if an error has occured, zero otherwise
  */
-int __weak pci_register_io_range(phys_addr_t addr, resource_size_t size)
+int __weak pci_register_io_range(struct fwnode_handle *node, phys_addr_t addr,
+				 resource_size_t size, unsigned long *port)
 {
 	int err = 0;
 
@@ -3261,10 +3263,31 @@ int __weak pci_register_io_range(phys_addr_t addr, resource_size_t size)
 	struct io_range *range;
 	resource_size_t allocated_size = 0;
 
+	/*
+	 * As indirect-IO which support multiple bus instances is introduced,
+	 * the input 'addr' is probably not page-aligned. If the PCI I/O
+	 * ranges are registered after indirect-IO, there is risk that the
+	 * start logical PIO assigned to PCI I/O is not page-aligned.
+	 * This will cause some I/O subranges are not remapped or overlapped
+	 * in pci_remap_iospace() handling.
+	 */
+	WARN_ON(addr != IO_RANGE_IOEXT && !(addr & PAGE_MASK));
+	/*
+	 * MMIO will call ioremap, it is better to align size with PAGE_SIZE,
+	 * then the return linux virtual PIO is page-aligned.
+	 */
+	if (size & PAGE_MASK)
+		size = PAGE_ALIGN(size);
+
 	/* check if the range hasn't been previously recorded */
 	spin_lock(&io_range_lock);
 	list_for_each_entry(range, &io_range_list, list) {
-		if (addr >= range->start && addr + size <= range->start + size) {
+		if (node == range->node)
+			goto end_register;
+
+		if (addr != IO_RANGE_IOEXT &&
+		    addr >= range->start &&
+		    addr + size <= range->start + size) {
 			/* range already registered, bail out */
 			goto end_register;
 		}
@@ -3290,6 +3313,7 @@ int __weak pci_register_io_range(phys_addr_t addr, resource_size_t size)
 		goto end_register;
 	}
 
+	range->node = node;
 	range->start = addr;
 	range->size = size;
 
@@ -3297,6 +3321,14 @@ int __weak pci_register_io_range(phys_addr_t addr, resource_size_t size)
 
 end_register:
 	spin_unlock(&io_range_lock);
+
+	*port = allocated_size;
+#else
+	/*
+	 * powerpc and microblaze have their own registration,
+	 * just look up the value here
+	 */
+	*port = pci_address_to_pio(addr);
 #endif
 
 	return err;
@@ -3315,7 +3347,9 @@ phys_addr_t pci_pio_to_address(unsigned long pio)
 
 	spin_lock(&io_range_lock);
 	list_for_each_entry(range, &io_range_list, list) {
-		if (pio >= allocated_size && pio < allocated_size + range->size) {
+		if (range->start != IO_RANGE_IOEXT &&
+			pio >= allocated_size &&
+			pio < allocated_size + range->size) {
 			address = range->start + pio - allocated_size;
 			break;
 		}
@@ -3336,7 +3370,9 @@ unsigned long __weak pci_address_to_pio(phys_addr_t address)
 
 	spin_lock(&io_range_lock);
 	list_for_each_entry(res, &io_range_list, list) {
-		if (address >= res->start && address < res->start + res->size) {
+		if (res->start != IO_RANGE_IOEXT &&
+			address >= res->start &&
+			address < res->start + res->size) {
 			addr = address - res->start + offset;
 			break;
 		}
diff --git a/include/linux/pci.h b/include/linux/pci.h
index e2d1a12..8d91af8 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -34,6 +34,9 @@
 
 #include <linux/pci_ids.h>
 
+/* the macro below flags an invalid cpu address
+ * and is used by IO special hosts              */
+#define IO_RANGE_IOEXT (resource_size_t)(-1ull)
 /*
  * The PCI interface treats multi-function devices as independent
  * devices.  The slot/function address of each device is encoded
@@ -1197,8 +1200,8 @@ int __must_check pci_bus_alloc_resource(struct pci_bus *bus,
 						  resource_size_t),
 			void *alignf_data);
 
-
-int pci_register_io_range(phys_addr_t addr, resource_size_t size);
+int pci_register_io_range(struct fwnode_handle *node, phys_addr_t addr,
+			  resource_size_t size, unsigned long *port);
 unsigned long pci_address_to_pio(phys_addr_t addr);
 phys_addr_t pci_pio_to_address(unsigned long pio);
 int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH V6 3/5] OF: Add missing I/O range exception for indirect-IO  devices
  2017-01-24  7:05 [PATCH V6 0/5] LPC: legacy ISA I/O support zhichang.yuan
  2017-01-24  7:05 ` [PATCH V6 1/5] LIB: Indirect ISA/LPC port IO introduced zhichang.yuan
  2017-01-24  7:05 ` [PATCH V6 2/5] PCI: Adapt pci_register_io_range() for indirect-IO and PCI I/O translation zhichang.yuan
@ 2017-01-24  7:05 ` zhichang.yuan
  2017-01-27 22:03   ` Rob Herring
  2017-01-24  7:05 ` [PATCH V6 4/5] LPC: Support the device-tree LPC host on Hip06/Hip07 zhichang.yuan
  2017-01-24  7:05 ` [PATCH V5 5/5] LPC: Add the ACPI LPC support zhichang.yuan
  4 siblings, 1 reply; 37+ messages in thread
From: zhichang.yuan @ 2017-01-24  7:05 UTC (permalink / raw)
  To: catalin.marinas, will.deacon, robh+dt, frowand.list, bhelgaas,
	rafael, mark.rutland, brian.starkey, olof, arnd,
	linux-arm-kernel
  Cc: lorenzo.pieralisi, benh, linux-kernel, linuxarm, devicetree,
	linux-pci, linux-serial, minyard, liviu.dudau, zourongrong,
	john.garry, gabriele.paoloni, zhichang.yuan02, kantyzc, xuwei5,
	zhichang.yuan

There are some special ISA/LPC devices that work on a specific I/O range where
it is not correct to specify a 'ranges' property in DTS parent node as cpu
addresses translated from DTS node are only for memory space on some
architectures, such as Arm64. Without the parent 'ranges' property, current
of_translate_address() return an error.
Here we add special handlings for this case.
During the OF address translation, some checkings will be perfromed to
identify whether the device node is registered as indirect-IO. If yes, the I/O
translation will be done in a different way from that one of PCI MMIO.
In this way, the I/O 'reg' property of the special ISA/LPC devices will be
parsed correctly.

Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
 drivers/of/address.c | 87 ++++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 71 insertions(+), 16 deletions(-)

diff --git a/drivers/of/address.c b/drivers/of/address.c
index d85d228..6a424ec 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -545,9 +545,14 @@ static int of_translate_one(struct device_node *parent, struct of_bus *bus,
  * that translation is impossible (that is we are not dealing with a value
  * that can be mapped to a cpu physical address). This is not really specified
  * that way, but this is traditionally the way IBM at least do things
+ *
+ * Whenever the translation fails, the *host pointer will be set to the
+ * device that lacks a tranlation, and the return code is relative to
+ * that node.
  */
 static u64 __of_translate_address(struct device_node *dev,
-				  const __be32 *in_addr, const char *rprop)
+				  const __be32 *in_addr, const char *rprop,
+				  struct device_node **host)
 {
 	struct device_node *parent = NULL;
 	struct of_bus *bus, *pbus;
@@ -560,6 +565,7 @@ static u64 __of_translate_address(struct device_node *dev,
 	/* Increase refcount at current level */
 	of_node_get(dev);
 
+	*host = NULL;
 	/* Get parent & match bus type */
 	parent = of_get_parent(dev);
 	if (parent == NULL)
@@ -592,6 +598,18 @@ static u64 __of_translate_address(struct device_node *dev,
 			break;
 		}
 
+		/*
+		 * For indirectIO device which has no ranges property, get
+		 * the address from reg directly.
+		 */
+		if (extio_find_node(&dev->fwnode)) {
+			result = of_read_number(addr + 1, na - 1);
+			pr_debug("indirectIO matched(%s) 0x%llx\n",
+					of_node_full_name(dev), result);
+			*host = of_node_get(dev);
+			break;
+		}
+
 		/* Get new parent bus and counts */
 		pbus = of_match_bus(parent);
 		pbus->count_cells(dev, &pna, &pns);
@@ -624,13 +642,32 @@ static u64 __of_translate_address(struct device_node *dev,
 
 u64 of_translate_address(struct device_node *dev, const __be32 *in_addr)
 {
-	return __of_translate_address(dev, in_addr, "ranges");
+	struct device_node *host;
+	u64 ret;
+
+	ret =  __of_translate_address(dev, in_addr, "ranges", &host);
+	if (host) {
+		of_node_put(host);
+		return OF_BAD_ADDR;
+	}
+
+	return ret;
 }
 EXPORT_SYMBOL(of_translate_address);
 
 u64 of_translate_dma_address(struct device_node *dev, const __be32 *in_addr)
 {
-	return __of_translate_address(dev, in_addr, "dma-ranges");
+	struct device_node *host;
+	u64 ret;
+
+	ret = __of_translate_address(dev, in_addr, "dma-ranges", &host);
+
+	if (host) {
+		of_node_put(host);
+		return OF_BAD_ADDR;
+	}
+
+	return ret;
 }
 EXPORT_SYMBOL(of_translate_dma_address);
 
@@ -672,29 +709,47 @@ const __be32 *of_get_address(struct device_node *dev, int index, u64 *size,
 }
 EXPORT_SYMBOL(of_get_address);
 
+static u64 of_translate_ioport(struct device_node *dev, const __be32 *in_addr)
+{
+	u64 taddr;
+	unsigned long port;
+	struct device_node *host;
+
+	taddr = __of_translate_address(dev, in_addr, "ranges", &host);
+	if (host) {
+		/* host specific port access */
+		port = extio_translate(&host->fwnode, taddr);
+		of_node_put(host);
+	} else {
+		/* memory mapped I/O range */
+		port = pci_address_to_pio(taddr);
+	}
+
+	if (port == (unsigned long)-1)
+		return OF_BAD_ADDR;
+
+	return port;
+}
+
 static int __of_address_to_resource(struct device_node *dev,
 		const __be32 *addrp, u64 size, unsigned int flags,
 		const char *name, struct resource *r)
 {
 	u64 taddr;
 
-	if ((flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0)
+	if (flags & IORESOURCE_MEM)
+		taddr = of_translate_address(dev, addrp);
+	else if (flags & IORESOURCE_IO)
+		taddr = of_translate_ioport(dev, addrp);
+	else
 		return -EINVAL;
-	taddr = of_translate_address(dev, addrp);
+
 	if (taddr == OF_BAD_ADDR)
 		return -EINVAL;
 	memset(r, 0, sizeof(struct resource));
-	if (flags & IORESOURCE_IO) {
-		unsigned long port;
-		port = pci_address_to_pio(taddr);
-		if (port == (unsigned long)-1)
-			return -EINVAL;
-		r->start = port;
-		r->end = port + size - 1;
-	} else {
-		r->start = taddr;
-		r->end = taddr + size - 1;
-	}
+
+	r->start = taddr;
+	r->end = taddr + size - 1;
 	r->flags = flags;
 	r->name = name ? name : dev->full_name;
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH V6 4/5] LPC: Support the device-tree LPC host on Hip06/Hip07
  2017-01-24  7:05 [PATCH V6 0/5] LPC: legacy ISA I/O support zhichang.yuan
                   ` (2 preceding siblings ...)
  2017-01-24  7:05 ` [PATCH V6 3/5] OF: Add missing I/O range exception for indirect-IO devices zhichang.yuan
@ 2017-01-24  7:05 ` zhichang.yuan
  2017-01-27 22:12   ` Rob Herring
  2017-01-30 20:08   ` Alexander Graf
  2017-01-24  7:05 ` [PATCH V5 5/5] LPC: Add the ACPI LPC support zhichang.yuan
  4 siblings, 2 replies; 37+ messages in thread
From: zhichang.yuan @ 2017-01-24  7:05 UTC (permalink / raw)
  To: catalin.marinas, will.deacon, robh+dt, frowand.list, bhelgaas,
	rafael, mark.rutland, brian.starkey, olof, arnd,
	linux-arm-kernel
  Cc: lorenzo.pieralisi, benh, linux-kernel, linuxarm, devicetree,
	linux-pci, linux-serial, minyard, liviu.dudau, zourongrong,
	john.garry, gabriele.paoloni, zhichang.yuan02, kantyzc, xuwei5,
	zhichang.yuan

The low-pin-count(LPC) interface of Hip06/Hip07 accesses the peripherals in
I/O port addresses. This patch implements the LPC host controller driver which
perform the I/O operations on the underlying hardware.
We don't want to touch those existing peripherals' driver, such as ipmi-bt. So
this driver applies the indirect-IO introduced in the previous patch after
registering an indirect-IO node to the indirect-IO devices list which will be
searched in the I/O accessors.
As the I/O translations for LPC children depend on the host I/O registration,
we should ensure the host I/O registration is finished before all the LPC
children scanning. That is why an arch_init() hook was added in this patch.

Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
---
 .../arm/hisilicon/hisilicon-low-pin-count.txt      |  33 ++
 MAINTAINERS                                        |   9 +
 arch/arm64/boot/dts/hisilicon/hip06-d03.dts        |   4 +
 arch/arm64/boot/dts/hisilicon/hip06.dtsi           |  14 +
 drivers/bus/Kconfig                                |   8 +
 drivers/bus/Makefile                               |   1 +
 drivers/bus/hisi_lpc.c                             | 599 +++++++++++++++++++++
 7 files changed, 668 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
 create mode 100644 drivers/bus/hisi_lpc.c

diff --git a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
new file mode 100644
index 0000000..213181f
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
@@ -0,0 +1,33 @@
+Hisilicon Hip06 low-pin-count device
+  Hisilicon Hip06 SoCs implement a Low Pin Count (LPC) controller, which
+  provides I/O access to some legacy ISA devices.
+  Hip06 is based on arm64 architecture where there is no I/O space. So, the
+  I/O ports here are not cpu addresses, and there is no 'ranges' property in
+  LPC device node.
+
+Required properties:
+- compatible:  value should be as follows:
+	(a) "hisilicon,hip06-lpc"
+	(b) "hisilicon,hip07-lpc"
+- #address-cells: must be 2 which stick to the ISA/EISA binding doc.
+- #size-cells: must be 1 which stick to the ISA/EISA binding doc.
+- reg: base memory range where the LPC register set is mapped.
+
+Note:
+  The node name before '@' must be "isa" to represent the binding stick to the
+  ISA/EISA binding specification.
+
+Example:
+
+isa@a01b0000 {
+	compatible = "hisilicon,hip06-lpc";
+	#address-cells = <2>;
+	#size-cells = <1>;
+	reg = <0x0 0xa01b0000 0x0 0x1000>;
+
+	ipmi0: bt@e4 {
+		compatible = "ipmi-bt";
+		device_type = "ipmi";
+		reg = <0x01 0xe4 0x04>;
+	};
+};
diff --git a/MAINTAINERS b/MAINTAINERS
index 26edd83..0153707 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5855,6 +5855,15 @@ F:	include/uapi/linux/if_hippi.h
 F:	net/802/hippi.c
 F:	drivers/net/hippi/
 
+HISILICON LPC BUS DRIVER
+M:	Zhichang Yuan <yuanzhichang@hisilicon.com>
+L:	linux-arm-kernel@lists.infradead.org
+W:	http://www.hisilicon.com
+S:	Maintained
+F:	drivers/bus/hisi_lpc.c
+F:	lib/extio.c
+F:	Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
+
 HISILICON NETWORK SUBSYSTEM DRIVER
 M:	Yisen Zhuang <yisen.zhuang@huawei.com>
 M:	Salil Mehta <salil.mehta@huawei.com>
diff --git a/arch/arm64/boot/dts/hisilicon/hip06-d03.dts b/arch/arm64/boot/dts/hisilicon/hip06-d03.dts
index 7c4114a..75b2b5c 100644
--- a/arch/arm64/boot/dts/hisilicon/hip06-d03.dts
+++ b/arch/arm64/boot/dts/hisilicon/hip06-d03.dts
@@ -52,3 +52,7 @@
 &usb_ehci {
 	status = "ok";
 };
+
+&ipmi0 {
+	status = "ok";
+};
diff --git a/arch/arm64/boot/dts/hisilicon/hip06.dtsi b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
index a049b64..c450f8d 100644
--- a/arch/arm64/boot/dts/hisilicon/hip06.dtsi
+++ b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
@@ -318,6 +318,20 @@
 		#size-cells = <2>;
 		ranges;
 
+		isa@a01b0000 {
+			compatible = "hisilicon,hip06-lpc";
+			#size-cells = <1>;
+			#address-cells = <2>;
+			reg = <0x0 0xa01b0000 0x0 0x1000>;
+
+			ipmi0: bt@e4 {
+				compatible = "ipmi-bt";
+				device_type = "ipmi";
+				reg = <0x01 0xe4 0x04>;
+				status = "disabled";
+			};
+		};
+
 		refclk: refclk {
 			compatible = "fixed-clock";
 			clock-frequency = <50000000>;
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
index b9e8cfc..58cee84 100644
--- a/drivers/bus/Kconfig
+++ b/drivers/bus/Kconfig
@@ -64,6 +64,14 @@ config BRCMSTB_GISB_ARB
 	  arbiter. This driver provides timeout and target abort error handling
 	  and internal bus master decoding.
 
+config HISILICON_LPC
+	bool "Workaround for nonstandard ISA I/O space on Hisilicon Hip0X"
+	depends on (ARM64 && ARCH_HISI && PCI) || COMPILE_TEST
+	select INDIRECT_PIO
+	help
+	  Driver needed for some legacy ISA devices attached to Low-Pin-Count
+	  on Hisilicon Hip0X SoC.
+
 config IMX_WEIM
 	bool "Freescale EIM DRIVER"
 	depends on ARCH_MXC
diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
index cc6364b..28e3862 100644
--- a/drivers/bus/Makefile
+++ b/drivers/bus/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_ARM_CCI)		+= arm-cci.o
 obj-$(CONFIG_ARM_CCN)		+= arm-ccn.o
 
 obj-$(CONFIG_BRCMSTB_GISB_ARB)	+= brcmstb_gisb.o
+obj-$(CONFIG_HISILICON_LPC)	+= hisi_lpc.o
 obj-$(CONFIG_IMX_WEIM)		+= imx-weim.o
 obj-$(CONFIG_MIPS_CDMM)		+= mips_cdmm.o
 obj-$(CONFIG_MVEBU_MBUS) 	+= mvebu-mbus.o
diff --git a/drivers/bus/hisi_lpc.c b/drivers/bus/hisi_lpc.c
new file mode 100644
index 0000000..a96e384
--- /dev/null
+++ b/drivers/bus/hisi_lpc.c
@@ -0,0 +1,599 @@
+/*
+ * Copyright (C) 2016 Hisilicon Limited, All Rights Reserved.
+ * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
+ * Author: Zou Rongrong <zourongrong@huawei.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/acpi.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/pci.h>
+#include <linux/serial_8250.h>
+#include <linux/slab.h>
+
+/*
+ * Setting this bit means each IO operation will target to a
+ * different port address:
+ * 0 means repeatedly IO operations will stick on the same port,
+ * such as BT;
+ */
+#define FG_INCRADDR_LPC		0x02
+
+struct lpc_cycle_para {
+	unsigned int opflags;
+	unsigned int csize; /* the data length of each operation */
+};
+
+struct hisilpc_dev {
+	spinlock_t cycle_lock;
+	void __iomem  *membase;
+	struct extio_node *extio;
+};
+
+/* bounds of the LPC bus address range */
+#define LPC_MIN_BUS_RANGE	0x0
+
+/*
+ * The maximal IO size for each leagcy bus.
+ * The port size of legacy I/O devices is normally less than 0x400.
+ * Defining the I/O range size as 0x400 here should be sufficient for
+ * all peripherals under one bus.
+ */
+#define LPC_BUS_IO_SIZE		0x400
+
+/* The maximum continuous operations */
+#define LPC_MAX_OPCNT	16
+/* only support IO data unit length is four at maximum */
+#define LPC_MAX_DULEN	4
+#if LPC_MAX_DULEN > LPC_MAX_OPCNT
+#error "LPC.. MAX_DULEN must be not bigger than MAX_OPCNT!"
+#endif
+
+#define LPC_REG_START		0x00 /* start a new LPC cycle */
+#define LPC_REG_OP_STATUS	0x04 /* the current LPC status */
+#define LPC_REG_IRQ_ST		0x08 /* interrupt enable&status */
+#define LPC_REG_OP_LEN		0x10 /* how many LPC cycles each start */
+#define LPC_REG_CMD		0x14 /* command for the required LPC cycle */
+#define LPC_REG_ADDR		0x20 /* LPC target address */
+#define LPC_REG_WDATA		0x24 /* data to be written */
+#define LPC_REG_RDATA		0x28 /* data coming from peer */
+
+
+/* The command register fields */
+#define LPC_CMD_SAMEADDR	0x08
+#define LPC_CMD_TYPE_IO		0x00
+#define LPC_CMD_WRITE		0x01
+#define LPC_CMD_READ		0x00
+/* the bit attribute is W1C. 1 represents OK. */
+#define LPC_STAT_BYIRQ		0x02
+
+#define LPC_STATUS_IDLE		0x01
+#define LPC_OP_FINISHED		0x02
+
+#define START_WORK		0x01
+
+/*
+ * The minimal waiting interval... Suggest it is not less than 10.
+ * Bigger value probably will lower the performance.
+ */
+#define LPC_NSEC_PERWAIT	100
+/*
+ * The maximum waiting time is about 128us.
+ * The fastest IO cycle time is about 390ns, but the worst case will wait
+ * for extra 256 lpc clocks, so (256 + 13) * 30ns = 8 us. The maximum
+ * burst cycles is 16. So, the maximum waiting time is about 128us under
+ * worst case.
+ * choose 1300 as the maximum.
+ */
+#define LPC_MAX_WAITCNT		1300
+/* About 10us. This is specific for single IO operation, such as inb. */
+#define LPC_PEROP_WAITCNT	100
+
+
+static inline int wait_lpc_idle(unsigned char *mbase,
+				unsigned int waitcnt) {
+	u32 opstatus;
+
+	while (waitcnt--) {
+		ndelay(LPC_NSEC_PERWAIT);
+		opstatus = readl(mbase + LPC_REG_OP_STATUS);
+		if (opstatus & LPC_STATUS_IDLE)
+			return (opstatus & LPC_OP_FINISHED) ? 0 : (-EIO);
+	}
+	return -ETIME;
+}
+
+/*
+ * hisilpc_target_in - trigger a series of lpc cycles to read required data
+ *		       from target peripheral.
+ * @pdev: pointer to hisi lpc device
+ * @para: some parameters used to control the lpc I/O operations
+ * @ptaddr: the lpc I/O target port address
+ * @buf: where the read back data is stored
+ * @opcnt: how many I/O operations required in this calling
+ *
+ * Only one byte data is read each I/O operation.
+ *
+ * Returns 0 on success, non-zero on fail.
+ *
+ */
+static int
+hisilpc_target_in(struct hisilpc_dev *lpcdev, struct lpc_cycle_para *para,
+		  unsigned long ptaddr, unsigned char *buf,
+		  unsigned long opcnt)
+{
+	unsigned long cnt_per_trans;
+	unsigned int cmd_word;
+	unsigned int waitcnt;
+	int ret;
+
+	if (!buf || !opcnt || !para || !para->csize || !lpcdev)
+		return -EINVAL;
+
+	if (opcnt  > LPC_MAX_OPCNT)
+		return -EINVAL;
+
+	cmd_word = LPC_CMD_TYPE_IO | LPC_CMD_READ;
+	waitcnt = LPC_PEROP_WAITCNT;
+	if (!(para->opflags & FG_INCRADDR_LPC)) {
+		cmd_word |= LPC_CMD_SAMEADDR;
+		waitcnt = LPC_MAX_WAITCNT;
+	}
+
+	ret = 0;
+	cnt_per_trans = (para->csize == 1) ? opcnt : para->csize;
+	for (; opcnt && !ret; cnt_per_trans = para->csize) {
+		unsigned long flags;
+
+		/* whole operation must be atomic */
+		spin_lock_irqsave(&lpcdev->cycle_lock, flags);
+
+		writel(cnt_per_trans, lpcdev->membase + LPC_REG_OP_LEN);
+
+		writel(cmd_word, lpcdev->membase + LPC_REG_CMD);
+
+		writel(ptaddr, lpcdev->membase + LPC_REG_ADDR);
+
+		writel(START_WORK, lpcdev->membase + LPC_REG_START);
+
+		/* whether the operation is finished */
+		ret = wait_lpc_idle(lpcdev->membase, waitcnt);
+		if (!ret) {
+			opcnt -= cnt_per_trans;
+			for (; cnt_per_trans--; buf++)
+				*buf = readl(lpcdev->membase + LPC_REG_RDATA);
+		}
+
+		spin_unlock_irqrestore(&lpcdev->cycle_lock, flags);
+	}
+
+	return ret;
+}
+
+/**
+ * hisilpc_target_out - trigger a series of lpc cycles to write required
+ *			data to target peripheral.
+ * @pdev: pointer to hisi lpc device
+ * @para: some parameters used to control the lpc I/O operations
+ * @ptaddr: the lpc I/O target port address
+ * @buf: where the data to be written is stored
+ * @opcnt: how many I/O operations required
+ *
+ * Only one byte data is read each I/O operation.
+ *
+ * Returns 0 on success, non-zero on fail.
+ *
+ */
+static int
+hisilpc_target_out(struct hisilpc_dev *lpcdev, struct lpc_cycle_para *para,
+		   unsigned long ptaddr, const unsigned char *buf,
+		   unsigned long opcnt)
+{
+	unsigned long cnt_per_trans;
+	unsigned int cmd_word;
+	unsigned int waitcnt;
+	int ret;
+
+	if (!buf || !opcnt || !para || !lpcdev)
+		return -EINVAL;
+
+	if (opcnt > LPC_MAX_OPCNT)
+		return -EINVAL;
+	/* default is increasing address */
+	cmd_word = LPC_CMD_TYPE_IO | LPC_CMD_WRITE;
+	waitcnt = LPC_PEROP_WAITCNT;
+	if (!(para->opflags & FG_INCRADDR_LPC)) {
+		cmd_word |= LPC_CMD_SAMEADDR;
+		waitcnt = LPC_MAX_WAITCNT;
+	}
+
+	ret = 0;
+	cnt_per_trans = (para->csize == 1) ? opcnt : para->csize;
+	for (; opcnt && !ret; cnt_per_trans = para->csize) {
+		unsigned long flags;
+
+		spin_lock_irqsave(&lpcdev->cycle_lock, flags);
+
+		writel(cnt_per_trans, lpcdev->membase + LPC_REG_OP_LEN);
+		opcnt -= cnt_per_trans;
+		for (; cnt_per_trans--; buf++)
+			writel(*buf, lpcdev->membase + LPC_REG_WDATA);
+
+		writel(cmd_word, lpcdev->membase + LPC_REG_CMD);
+
+		writel(ptaddr, lpcdev->membase + LPC_REG_ADDR);
+
+		writel(START_WORK, lpcdev->membase + LPC_REG_START);
+
+		/* whether the operation is finished */
+		ret = wait_lpc_idle(lpcdev->membase, waitcnt);
+
+		spin_unlock_irqrestore(&lpcdev->cycle_lock, flags);
+	}
+
+	return ret;
+}
+
+static inline unsigned long
+hisi_lpc_pio_to_addr(struct hisilpc_dev *lpcdev, unsigned long pio)
+{
+	return pio - lpcdev->extio->io_start + lpcdev->extio->bus_start;
+}
+
+
+/**
+ * hisilpc_comm_in - read/input the data from the I/O peripheral
+ *		     through LPC.
+ * @devobj: pointer to the device information relevant to LPC controller.
+ * @pio: the target I/O port address.
+ * @dlen: the data length required to read from the target I/O port.
+ *
+ * when succeed, the data read back is stored in buffer pointed by inbuf.
+ * For inb, return the data read from I/O or -1 when error occur.
+ */
+static u64 hisilpc_comm_in(void *devobj, unsigned long pio, size_t dlen)
+{
+	struct hisilpc_dev *lpcdev = devobj;
+	struct lpc_cycle_para iopara;
+	u32 rd_data;
+	unsigned char *newbuf;
+	int ret = 0;
+	unsigned long ptaddr;
+
+	if (!lpcdev || !dlen || dlen > LPC_MAX_DULEN ||	(dlen & (dlen - 1)))
+		return -1;
+
+	/* the local buffer must be enough for one data unit */
+	if (sizeof(rd_data) < dlen)
+		return -1;
+
+	newbuf = (unsigned char *)&rd_data;
+
+	ptaddr = hisi_lpc_pio_to_addr(lpcdev, pio);
+
+	iopara.opflags = FG_INCRADDR_LPC;
+	iopara.csize = dlen;
+
+	ret = hisilpc_target_in(lpcdev, &iopara, ptaddr, newbuf, dlen);
+	if (ret)
+		return -1;
+
+	return le32_to_cpu(rd_data);
+}
+
+/**
+ * hisilpc_comm_out - output the data whose maximum length is four bytes
+		      to the I/O peripheral through the LPC host.
+ * @devobj: pointer to the device information relevant to LPC controller.
+ * @outval: a value to be outputted from caller, maximum is four bytes.
+ * @pio: the target I/O port address.
+ * @dlen: the data length required writing to the target I/O port.
+ *
+ * This function is corresponding to out(b,w,l) only
+ *
+ */
+static void hisilpc_comm_out(void *devobj, unsigned long pio,
+			     u32 outval, size_t dlen)
+{
+	struct hisilpc_dev *lpcdev = devobj;
+	struct lpc_cycle_para iopara;
+	const unsigned char *newbuf;
+	unsigned long ptaddr;
+
+	if (!lpcdev || !dlen || dlen > LPC_MAX_DULEN)
+		return;
+
+	if (sizeof(outval) < dlen)
+		return;
+
+	outval = cpu_to_le32(outval);
+
+	newbuf = (const unsigned char *)&outval;
+
+	ptaddr = hisi_lpc_pio_to_addr(lpcdev, pio);
+
+	iopara.opflags = FG_INCRADDR_LPC;
+	iopara.csize = dlen;
+
+	hisilpc_target_out(lpcdev, &iopara, ptaddr, newbuf, dlen);
+}
+
+/*
+ * hisilpc_comm_ins - read/input the data in buffer to the I/O
+ *		peripheral through LPC, it corresponds to ins(b,w,l)
+ * @devobj: pointer to the device information relevant to LPC controller.
+ * @pio: the target I/O port address.
+ * @inbuf: a buffer where read/input data bytes are stored.
+ * @dlen: the data length required writing to the target I/O port.
+ * @count: how many data units whose length is dlen will be read.
+ *
+ */
+static u64
+hisilpc_comm_ins(void *devobj, unsigned long pio, void *inbuf,
+		 size_t dlen, unsigned int count)
+{
+	struct hisilpc_dev *lpcdev = devobj;
+	struct lpc_cycle_para iopara;
+	unsigned char *newbuf;
+	unsigned int loopcnt, cntleft;
+	unsigned int max_perburst;
+	unsigned long ptaddr;
+
+	if (!lpcdev || !inbuf || !count || !dlen ||
+			dlen > LPC_MAX_DULEN || (dlen & (dlen - 1)))
+		return -1;
+
+	iopara.opflags = 0;
+	if (dlen > 1)
+		iopara.opflags |= FG_INCRADDR_LPC;
+	iopara.csize = dlen;
+
+	ptaddr = hisi_lpc_pio_to_addr(lpcdev, pio);
+	newbuf = (unsigned char *)inbuf;
+	/*
+	 * ensure data stream whose length is multiple of dlen to be processed
+	 * each IO input
+	 */
+	max_perburst = LPC_MAX_OPCNT & (~(dlen - 1));
+	cntleft = count * dlen;
+	do {
+		int ret;
+
+		loopcnt = (cntleft >= max_perburst) ? max_perburst : cntleft;
+		ret = hisilpc_target_in(lpcdev, &iopara, ptaddr,
+					newbuf, loopcnt);
+		if (ret)
+			return ret;
+		newbuf += loopcnt;
+		cntleft -= loopcnt;
+	} while (cntleft);
+
+	return 0;
+}
+
+/*
+ * hisilpc_comm_outs - write/output the data in buffer to the I/O
+ *		peripheral through LPC, it corresponds to outs(b,w,l)
+ * @devobj: pointer to the device information relevant to LPC controller.
+ * @pio: the target I/O port address.
+ * @outbuf: a buffer where write/output data bytes are stored.
+ * @dlen: the data length required writing to the target I/O port .
+ * @count: how many data units whose length is dlen will be written.
+ *
+ */
+static void
+hisilpc_comm_outs(void *devobj, unsigned long pio, const void *outbuf,
+		  size_t dlen, unsigned int count)
+{
+	struct hisilpc_dev *lpcdev = devobj;
+	struct lpc_cycle_para iopara;
+	const unsigned char *newbuf;
+	unsigned int loopcnt, cntleft;
+	unsigned int max_perburst;
+	unsigned long ptaddr;
+
+	if (!lpcdev || !outbuf || !count || !dlen ||
+			dlen > LPC_MAX_DULEN || (dlen & (dlen - 1)))
+		return;
+
+	iopara.opflags = 0;
+	if (dlen > 1)
+		iopara.opflags |= FG_INCRADDR_LPC;
+	iopara.csize = dlen;
+
+	ptaddr = hisi_lpc_pio_to_addr(lpcdev, pio);
+	newbuf = (unsigned char *)outbuf;
+	/*
+	 * ensure data stream whose length is multiple of dlen to be processed
+	 * each IO input
+	 */
+	max_perburst = LPC_MAX_OPCNT & (~(dlen - 1));
+	cntleft = count * dlen;
+	do {
+		loopcnt = (cntleft >= max_perburst) ? max_perburst : cntleft;
+		if (hisilpc_target_out(lpcdev, &iopara, ptaddr, newbuf,
+						loopcnt))
+			break;
+		newbuf += loopcnt;
+		cntleft -= loopcnt;
+	} while (cntleft);
+}
+
+static struct extio_ops hisi_lpc_ops = {
+	.pfin = hisilpc_comm_in,
+	.pfout = hisilpc_comm_out,
+	.pfins = hisilpc_comm_ins,
+	.pfouts = hisilpc_comm_outs,
+};
+
+/**
+ * hisilpc_probe - the probe callback function for hisi lpc device,
+ *		   will finish all the initialization.
+ * @pdev: the platform device corresponding to hisi lpc
+ *
+ * Returns 0 on success, non-zero on fail.
+ *
+ */
+static int hisilpc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	struct hisilpc_dev *lpcdev;
+	int ret = 0;
+
+	dev_info(dev, "probing...\n");
+
+	lpcdev = devm_kzalloc(dev, sizeof(struct hisilpc_dev), GFP_KERNEL);
+	if (!lpcdev)
+		return -ENOMEM;
+
+	spin_lock_init(&lpcdev->cycle_lock);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev, "no MEM resource\n");
+		return -ENODEV;
+	}
+
+	lpcdev->membase = devm_ioremap_resource(dev, res);
+	if (IS_ERR(lpcdev->membase)) {
+		dev_err(dev, "remap failed\n");
+		return PTR_ERR(lpcdev->membase);
+	}
+
+	/* get the linux virtual IO node registered before. */
+	lpcdev->extio = extio_find_node(dev->fwnode);
+	if (!lpcdev->extio) {
+		dev_err(dev, "No extio node registered!\n");
+		return -EFAULT;
+	}
+
+	lpcdev->extio->devpara = lpcdev;
+	lpcdev->extio->ops = &hisi_lpc_ops;
+
+	platform_set_drvdata(pdev, lpcdev);
+
+	/*
+	 * The children scanning is only for dts. For ACPI children, the
+	 * corresponding devices had be created during ACPI scanning.
+	 */
+	if (!has_acpi_companion(dev)) {
+		ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
+		if (ret)
+			dev_err(dev, "OF: enumerate LPC bus fail(%d)\n", ret);
+	}
+
+	if (!ret)
+		dev_info(dev, "hslpc end probing. range[0x%lx - %lx]\n",
+			 lpcdev->extio->io_start,
+			 lpcdev->extio->io_start + lpcdev->extio->range_size);
+	else
+		dev_info(dev, "hslpc probing is fail(%d)\n", ret);
+
+	return ret;
+}
+
+static const struct of_device_id hisilpc_of_match[] = {
+	{ .compatible = "hisilicon,hip06-lpc", },
+	{ .compatible = "hisilicon,hip07-lpc", },
+	{},
+};
+
+static const struct acpi_device_id hisilpc_acpi_match[] = {
+	{"HISI0191", },
+	{},
+};
+
+static struct platform_driver hisilpc_driver = {
+	.driver = {
+		.name           = "hisi_lpc",
+		.of_match_table = hisilpc_of_match,
+		.acpi_match_table = hisilpc_acpi_match,
+	},
+	.probe = hisilpc_probe,
+};
+
+
+builtin_platform_driver(hisilpc_driver);
+
+/*
+ * hisilpc_bus_platform_notify -- notify callback function specific for
+ *			hisi-lpc bus. Here, will register linux virtual
+ *			PIO for the bus detected, then the bus children
+ *			can translate their bus-local IO to linux PIO.
+ */
+static int hisilpc_bus_platform_notify(struct notifier_block *nb,
+			unsigned long action, void *data)
+{
+	struct extio_node *io_node;
+	struct device *dev = data;
+	int ret;
+
+	if (!is_of_node(dev->fwnode) && !is_acpi_node(dev->fwnode))
+		return NOTIFY_DONE;
+
+	if (action != BUS_NOTIFY_ADD_DEVICE)
+		return NOTIFY_DONE;
+
+	/* whether the device notified is hisi-lpc? */
+	if (has_acpi_companion(dev)) {
+		if (!acpi_match_device(hisilpc_acpi_match, dev))
+			return NOTIFY_DONE;
+	} else {
+		if (!of_match_node(hisilpc_of_match, dev->of_node))
+			return NOTIFY_DONE;
+	}
+
+	/*
+	 * indirectIO bus was detected, time to request the linux virtual
+	 * IO.
+	 */
+	io_node = kzalloc(sizeof(*io_node), GFP_KERNEL);
+	if (!io_node)
+		return NOTIFY_DONE;
+
+	io_node->bus_start = LPC_MIN_BUS_RANGE;
+	io_node->range_size = LPC_BUS_IO_SIZE;
+
+	ret = pci_register_io_range(dev->fwnode, IO_RANGE_IOEXT,
+			io_node->range_size, &io_node->io_start);
+	if (ret) {
+		dev_err(dev, "register indirectIO range FAIL!\n");
+		kfree(io_node);
+		return NOTIFY_DONE;
+	}
+	io_node->fwnode = dev->fwnode;
+	/* register the linux virtual IO range node to list. */
+	register_extio(io_node);
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block hisilpc_preinit_nb = {
+	.notifier_call = hisilpc_bus_platform_notify,
+};
+
+static int __init hisilpc_init(void)
+{
+	return bus_register_notifier(&platform_bus_type, &hisilpc_preinit_nb);
+}
+
+/* This initial funtion must be called before the platform bus scanning. */
+arch_initcall(hisilpc_init);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH V5 5/5] LPC: Add the ACPI LPC support
  2017-01-24  7:05 [PATCH V6 0/5] LPC: legacy ISA I/O support zhichang.yuan
                   ` (3 preceding siblings ...)
  2017-01-24  7:05 ` [PATCH V6 4/5] LPC: Support the device-tree LPC host on Hip06/Hip07 zhichang.yuan
@ 2017-01-24  7:05 ` zhichang.yuan
  2017-02-04 13:20   ` John Garry
  4 siblings, 1 reply; 37+ messages in thread
From: zhichang.yuan @ 2017-01-24  7:05 UTC (permalink / raw)
  To: catalin.marinas, will.deacon, robh+dt, frowand.list, bhelgaas,
	rafael, mark.rutland, brian.starkey, olof, arnd,
	linux-arm-kernel
  Cc: lorenzo.pieralisi, benh, linux-kernel, linuxarm, devicetree,
	linux-pci, linux-serial, minyard, liviu.dudau, zourongrong,
	john.garry, gabriele.paoloni, zhichang.yuan02, kantyzc, xuwei5,
	zhichang.yuan

The patch update the _CRS of LPC children with the system logical I/O resource
after the translation from LPC-local I/O. Then the ACPI platform device
enumeration for LPC can apply the right I/O resource to request the system I/O
space.

Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
---
 drivers/bus/hisi_lpc.c |  26 ++++++
 include/linux/extio.h  |   4 +
 lib/extio.c            | 228 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 258 insertions(+)

diff --git a/drivers/bus/hisi_lpc.c b/drivers/bus/hisi_lpc.c
index a96e384..8d52666 100644
--- a/drivers/bus/hisi_lpc.c
+++ b/drivers/bus/hisi_lpc.c
@@ -583,6 +583,32 @@ static int hisilpc_bus_platform_notify(struct notifier_block *nb,
 	/* register the linux virtual IO range node to list. */
 	register_extio(io_node);
 
+	/*
+	 * For ACPI children, translate the bus-local I/O range to logical
+	 * I/O range and set it as the current resource before the children
+	 * are enumerated.
+	 */
+	if (has_acpi_companion(dev)) {
+		struct acpi_device *root, *child;
+
+		root = to_acpi_device_node(dev->fwnode);
+		/* For hisilpc, only care about the sons of host. */
+		list_for_each_entry(child, &root->children, node) {
+			ret = acpi_set_extio_resource(child, root);
+			if (ret) {
+				dev_err(dev, "set resource failed..\n");
+				break;
+			}
+		}
+
+		if (ret) {
+			list_del(&io_node->list);
+			kfree(io_node);
+			dev_err(dev, "notify handling failed..\n");
+			return NOTIFY_DONE;
+		}
+	}
+
 	return NOTIFY_OK;
 }
 
diff --git a/include/linux/extio.h b/include/linux/extio.h
index 2ca7eab..c07607e 100644
--- a/include/linux/extio.h
+++ b/include/linux/extio.h
@@ -20,6 +20,7 @@
 
 #ifdef __KERNEL__
 
+#include <linux/acpi.h>
 #include <linux/fwnode.h>
 
 struct extio_ops {
@@ -81,5 +82,8 @@ static inline struct extio_node *extio_find_node(struct fwnode_handle *node)
 #endif
 extern void register_extio(struct extio_node *node);
 
+extern int acpi_set_extio_resource(struct acpi_device *adev,
+		struct acpi_device *host);
+
 #endif /* __KERNEL__ */
 #endif /* __LINUX_EXTIO_H */
diff --git a/lib/extio.c b/lib/extio.c
index 46228de..47f5913 100644
--- a/lib/extio.c
+++ b/lib/extio.c
@@ -75,6 +75,234 @@ unsigned long extio_translate(struct fwnode_handle *node,
 	return port_id;
 }
 
+static inline bool acpi_extio_supported_resource(struct acpi_resource *res)
+{
+	switch (res->type) {
+	case ACPI_RESOURCE_TYPE_ADDRESS16:
+	case ACPI_RESOURCE_TYPE_ADDRESS32:
+	case ACPI_RESOURCE_TYPE_ADDRESS64:
+		return true;
+	}
+	return false;
+}
+
+static acpi_status acpi_count_extiores(struct acpi_resource *res,
+					   void *data)
+{
+	int *res_cnt = data;
+
+	if (acpi_extio_supported_resource(res) &&
+		!acpi_dev_filter_resource_type(res, IORESOURCE_IO))
+		(*res_cnt)++;
+
+	return AE_OK;
+}
+
+static acpi_status acpi_read_one_extiores(struct acpi_resource *res,
+		void *data)
+{
+	struct acpi_resource **resource = data;
+
+	if (acpi_extio_supported_resource(res) &&
+		!acpi_dev_filter_resource_type(res, IORESOURCE_IO)) {
+		memcpy((*resource), res, sizeof(struct acpi_resource));
+		(*resource)->length = sizeof(struct acpi_resource);
+		(*resource)->type = res->type;
+		(*resource)++;
+	}
+
+	return AE_OK;
+}
+
+static acpi_status
+acpi_build_extiores_template(struct acpi_device *adev,
+			struct acpi_buffer *buffer)
+{
+	acpi_handle handle = adev->handle;
+	struct acpi_resource *resource;
+	acpi_status status;
+	int res_cnt = 0;
+
+	status = acpi_walk_resources(handle, METHOD_NAME__PRS,
+				     acpi_count_extiores, &res_cnt);
+	if (ACPI_FAILURE(status) || !res_cnt) {
+		dev_err(&adev->dev, "can't evaluate _CRS: %d\n", status);
+		return -EINVAL;
+	}
+
+	buffer->length = sizeof(struct acpi_resource) * (res_cnt + 1) + 1;
+	buffer->pointer = kzalloc(buffer->length - 1, GFP_KERNEL);
+	if (!buffer->pointer)
+		return -ENOMEM;
+
+	resource = (struct acpi_resource *)buffer->pointer;
+	status = acpi_walk_resources(handle, METHOD_NAME__PRS,
+				     acpi_read_one_extiores, &resource);
+	if (ACPI_FAILURE(status)) {
+		kfree(buffer->pointer);
+		dev_err(&adev->dev, "can't evaluate _PRS: %d\n", status);
+		return -EINVAL;
+	}
+
+	resource->type = ACPI_RESOURCE_TYPE_END_TAG;
+	resource->length = sizeof(struct acpi_resource);
+
+	return 0;
+}
+
+static int acpi_translate_extiores(struct acpi_device *adev,
+		struct acpi_device *host, struct acpi_buffer *buffer)
+{
+	int res_cnt = (buffer->length - 1) / sizeof(struct acpi_resource) - 1;
+	struct acpi_resource *resource = buffer->pointer;
+	struct acpi_resource_address64 addr;
+	unsigned long sys_port;
+	struct device *dev = &adev->dev;
+
+	/* only one I/O resource now */
+	if (res_cnt != 1) {
+		dev_err(dev, "encode %d resources whose type is(%d)!\n",
+			res_cnt, resource->type);
+		return -EINVAL;
+	}
+
+	if (ACPI_FAILURE(acpi_resource_to_address64(resource, &addr))) {
+		dev_err(dev, "convert acpi resource(%d) as addr64 FAIL!\n",
+			resource->type);
+		return -EFAULT;
+	}
+
+	/* For indirect-IO, addr length must be fixed. (>0, 0, 0) */
+	if (!addr.address.address_length || addr.min_address_fixed ||
+		addr.max_address_fixed) {
+		dev_warn(dev, "variable I/O resource is invalid!\n");
+		return -EINVAL;
+	}
+
+	sys_port = extio_translate(&host->fwnode, addr.address.minimum);
+	if (sys_port == -1) {
+		dev_err(dev, "translate bus-addr(0x%llx) fail!\n",
+			addr.address.minimum);
+		return -EFAULT;
+	}
+
+	switch (resource->type) {
+	case ACPI_RESOURCE_TYPE_ADDRESS16:
+	{
+		struct acpi_resource_address16 *out_res;
+
+		out_res = &resource->data.address16;
+		out_res->address.minimum = sys_port;
+		out_res->address.maximum = sys_port +
+			addr.address.address_length - 1;
+
+		dev_info(dev, "_SRS 16IO: [0x%x - 0x%x]\n",
+			out_res->address.minimum,
+			out_res->address.maximum);
+
+		break;
+	}
+
+	case ACPI_RESOURCE_TYPE_ADDRESS32:
+	{
+		struct acpi_resource_address32 *out_res;
+
+		out_res = &resource->data.address32;
+		out_res->address.minimum = sys_port;
+		out_res->address.maximum = sys_port +
+			addr.address.address_length - 1;
+
+		dev_info(dev, "_SRS 32IO: [0x%x - 0x%x]\n",
+			out_res->address.minimum,
+			out_res->address.maximum);
+
+		break;
+	}
+
+	case ACPI_RESOURCE_TYPE_ADDRESS64:
+	{
+		struct acpi_resource_address64 *out_res;
+
+		out_res = &resource->data.address64;
+		out_res->address.minimum = sys_port;
+		out_res->address.maximum = sys_port +
+			addr.address.address_length - 1;
+
+		dev_info(dev, "_SRS 64IO: [0x%llx - 0x%llx]\n",
+			out_res->address.minimum,
+			out_res->address.maximum);
+
+		break;
+	}
+
+	default:
+		return -EINVAL;
+
+	}
+
+	return 0;
+}
+
+/*
+ * update/set the current I/O resource of the designated device node.
+ * after this calling, the enumeration can be started as the I/O resource
+ * had been translated to logicial I/O from bus-local I/O.
+ *
+ * @adev: the device node to be updated the I/O resource;
+ * @host: the device node where 'adev' is attached, which can be not
+ *	the parent of 'adev';
+ *
+ * return 0 when successful, negative is for failure.
+ */
+int acpi_set_extio_resource(struct acpi_device *adev,
+		struct acpi_device *host)
+{
+	struct device *dev = &adev->dev;
+	struct acpi_buffer buffer;
+	acpi_status status;
+	int ret;
+
+	if (!host)
+		return -EINVAL;
+
+	/* check the device state */
+	if (!adev->status.present) {
+		dev_info(dev, "ACPI: device is not present!\n");
+		return 0;
+	}
+	/* whether the child had been enumerated? */
+	if (acpi_device_enumerated(adev)) {
+		dev_info(dev, "ACPI: had been enumerated!\n");
+		return 0;
+	}
+
+	/* read the _PRS and convert as acpi_buffer */
+	status = acpi_build_extiores_template(adev, &buffer);
+	if (ACPI_FAILURE(status)) {
+		dev_warn(dev, "Failure evaluating %s\n",
+				METHOD_NAME__PRS);
+		return -ENODEV;
+	}
+
+	/* translate the I/O resources */
+	ret = acpi_translate_extiores(adev, host, &buffer);
+	if (ret) {
+		kfree(buffer.pointer);
+		dev_err(dev, "Translate I/O range FAIL!\n");
+		return ret;
+	}
+
+	/* set current resource... */
+	status = acpi_set_current_resources(adev->handle, &buffer);
+	kfree(buffer.pointer);
+	if (ACPI_FAILURE(status)) {
+		dev_err(dev, "Error evaluating _SRS (0x%x)\n", status);
+		ret = -EIO;
+	}
+
+	return ret;
+}
+
 #ifdef PCI_IOBASE
 
 #define BUILD_EXTIO(bw, type)						\
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* Re: [PATCH V6 3/5] OF: Add missing I/O range exception for indirect-IO  devices
  2017-01-24  7:05 ` [PATCH V6 3/5] OF: Add missing I/O range exception for indirect-IO devices zhichang.yuan
@ 2017-01-27 22:03   ` Rob Herring
  2017-01-30  8:57     ` John Garry
  0 siblings, 1 reply; 37+ messages in thread
From: Rob Herring @ 2017-01-27 22:03 UTC (permalink / raw)
  To: zhichang.yuan
  Cc: catalin.marinas, will.deacon, frowand.list, bhelgaas, rafael,
	mark.rutland, brian.starkey, olof, arnd, linux-arm-kernel,
	lorenzo.pieralisi, benh, linux-kernel, linuxarm, devicetree,
	linux-pci, linux-serial, minyard, liviu.dudau, zourongrong,
	john.garry, gabriele.paoloni, zhichang.yuan02, kantyzc, xuwei5

On Tue, Jan 24, 2017 at 03:05:23PM +0800, zhichang.yuan wrote:
> There are some special ISA/LPC devices that work on a specific I/O range where
> it is not correct to specify a 'ranges' property in DTS parent node as cpu
> addresses translated from DTS node are only for memory space on some
> architectures, such as Arm64. Without the parent 'ranges' property, current
> of_translate_address() return an error.
> Here we add special handlings for this case.
> During the OF address translation, some checkings will be perfromed to
> identify whether the device node is registered as indirect-IO. If yes, the I/O
> translation will be done in a different way from that one of PCI MMIO.
> In this way, the I/O 'reg' property of the special ISA/LPC devices will be
> parsed correctly.
> 
> Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
> Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>

Shouldn't this be Reviewed-by?

Otherwise, looks fine to me.

Acked-by: Rob Herring <robh@kernel.org>

> ---
>  drivers/of/address.c | 87 ++++++++++++++++++++++++++++++++++++++++++----------
>  1 file changed, 71 insertions(+), 16 deletions(-)
> 

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH V6 4/5] LPC: Support the device-tree LPC host on Hip06/Hip07
  2017-01-24  7:05 ` [PATCH V6 4/5] LPC: Support the device-tree LPC host on Hip06/Hip07 zhichang.yuan
@ 2017-01-27 22:12   ` Rob Herring
  2017-01-30 20:08   ` Alexander Graf
  1 sibling, 0 replies; 37+ messages in thread
From: Rob Herring @ 2017-01-27 22:12 UTC (permalink / raw)
  To: zhichang.yuan
  Cc: mark.rutland, benh, gabriele.paoloni, rafael, linux-pci,
	will.deacon, linuxarm, frowand.list, lorenzo.pieralisi, arnd,
	xuwei5, linux-serial, catalin.marinas, devicetree, minyard,
	liviu.dudau, john.garry, zourongrong, bhelgaas, kantyzc,
	zhichang.yuan02, linux-arm-kernel, linux-kernel, olof,
	brian.starkey

On Tue, Jan 24, 2017 at 03:05:24PM +0800, zhichang.yuan wrote:
> The low-pin-count(LPC) interface of Hip06/Hip07 accesses the peripherals in
> I/O port addresses. This patch implements the LPC host controller driver which
> perform the I/O operations on the underlying hardware.
> We don't want to touch those existing peripherals' driver, such as ipmi-bt. So
> this driver applies the indirect-IO introduced in the previous patch after
> registering an indirect-IO node to the indirect-IO devices list which will be
> searched in the I/O accessors.
> As the I/O translations for LPC children depend on the host I/O registration,
> we should ensure the host I/O registration is finished before all the LPC
> children scanning. That is why an arch_init() hook was added in this patch.
> 
> Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
> Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
> ---
>  .../arm/hisilicon/hisilicon-low-pin-count.txt      |  33 ++
>  MAINTAINERS                                        |   9 +
>  arch/arm64/boot/dts/hisilicon/hip06-d03.dts        |   4 +
>  arch/arm64/boot/dts/hisilicon/hip06.dtsi           |  14 +

Acked-by: Rob Herring <robh@kernel.org>

>  drivers/bus/Kconfig                                |   8 +
>  drivers/bus/Makefile                               |   1 +
>  drivers/bus/hisi_lpc.c                             | 599 +++++++++++++++++++++
>  7 files changed, 668 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
>  create mode 100644 drivers/bus/hisi_lpc.c

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH V6 3/5] OF: Add missing I/O range exception for indirect-IO devices
  2017-01-27 22:03   ` Rob Herring
@ 2017-01-30  8:57     ` John Garry
  2017-01-30 10:08       ` Arnd Bergmann
  0 siblings, 1 reply; 37+ messages in thread
From: John Garry @ 2017-01-30  8:57 UTC (permalink / raw)
  To: Rob Herring, zhichang.yuan
  Cc: mark.rutland, benh, gabriele.paoloni, rafael, linux-pci,
	will.deacon, linuxarm, frowand.list, lorenzo.pieralisi, arnd,
	xuwei5, linux-serial, catalin.marinas, devicetree, minyard,
	liviu.dudau, zhichang.yuan02, zourongrong, bhelgaas, kantyzc,
	linux-arm-kernel, linux-kernel, olof, brian.starkey

On 27/01/2017 22:03, Rob Herring wrote:
> On Tue, Jan 24, 2017 at 03:05:23PM +0800, zhichang.yuan wrote:
>> There are some special ISA/LPC devices that work on a specific I/O range where
>> it is not correct to specify a 'ranges' property in DTS parent node as cpu
>> addresses translated from DTS node are only for memory space on some
>> architectures, such as Arm64. Without the parent 'ranges' property, current
>> of_translate_address() return an error.
>> Here we add special handlings for this case.
>> During the OF address translation, some checkings will be perfromed to
>> identify whether the device node is registered as indirect-IO. If yes, the I/O
>> translation will be done in a different way from that one of PCI MMIO.
>> In this way, the I/O 'reg' property of the special ISA/LPC devices will be
>> parsed correctly.
>>
>> Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
>> Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
>> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
>
> Shouldn't this be Reviewed-by?
>

The background is that Zhichang is following a sketch of a re-worked 
driver from Arnd, which Arnd gave Signed-off-by. But this patch does not 
follow it verbatim.

I think this patchset should be resent without Arnd's signature. Or Arnd 
may kindly review and say it's ok.

John

> Otherwise, looks fine to me.
>
> Acked-by: Rob Herring <robh@kernel.org>
>
>> ---
>>  drivers/of/address.c | 87 ++++++++++++++++++++++++++++++++++++++++++----------
>>  1 file changed, 71 insertions(+), 16 deletions(-)
>>
>
> .
>



_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH V6 3/5] OF: Add missing I/O range exception for indirect-IO devices
  2017-01-30  8:57     ` John Garry
@ 2017-01-30 10:08       ` Arnd Bergmann
  0 siblings, 0 replies; 37+ messages in thread
From: Arnd Bergmann @ 2017-01-30 10:08 UTC (permalink / raw)
  To: John Garry
  Cc: Mark Rutland, Benjamin Herrenschmidt, gabriele.paoloni, rafael,
	linux-pci, Will Deacon, linuxarm, frowand.list, Rob Herring,
	lorenzo.pieralisi, Wei Xu, linux-serial, catalin.marinas,
	devicetree, minyard, liviu.dudau, zhichang.yuan02, zourongrong,
	bhelgaas, kantyzc, Linux ARM, Linux Kernel Mailing List,
	zhichang.yuan, Olof Johansson, brian.starkey

On Mon, Jan 30, 2017 at 9:57 AM, John Garry <john.garry@huawei.com> wrote:
> On 27/01/2017 22:03, Rob Herring wrote:
>> On Tue, Jan 24, 2017 at 03:05:23PM +0800, zhichang.yuan wrote:
>>> Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
>>> Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
>>> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
>>
>>
>> Shouldn't this be Reviewed-by?
>>
>
> The background is that Zhichang is following a sketch of a re-worked driver
> from Arnd, which Arnd gave Signed-off-by. But this patch does not follow it
> verbatim.
>
> I think this patchset should be resent without Arnd's signature. Or Arnd may
> kindly review and say it's ok.

My general recommendation is that whenever authorship  is not 100% obvious,
then the changelog comment should explain in free form who did what. When
you do that, please leave my Signed-off-by intact and put it on top.
You can also (in addition to explaining it in the text) add an
explanantion behind
the address, such as

Signed-off-by: Arnd Bergmann <arnd@arndb.de> # earlier draft

I'll try to get around to do a full review of the series later today.

    Arnd

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH V6 1/5] LIB: Indirect ISA/LPC port IO introduced
  2017-01-24  7:05 ` [PATCH V6 1/5] LIB: Indirect ISA/LPC port IO introduced zhichang.yuan
@ 2017-01-30 17:12   ` Alexander Graf
  2017-01-31 13:32     ` John Garry
  2017-02-13 14:05     ` zhichang.yuan
  2017-01-31  0:09   ` Bjorn Helgaas
  1 sibling, 2 replies; 37+ messages in thread
From: Alexander Graf @ 2017-01-30 17:12 UTC (permalink / raw)
  To: zhichang.yuan, catalin.marinas, will.deacon, robh+dt,
	frowand.list, bhelgaas, rafael, mark.rutland, brian.starkey,
	olof, arnd, linux-arm-kernel
  Cc: devicetree, lorenzo.pieralisi, gabriele.paoloni, minyard, benh,
	john.garry, liviu.dudau, linux-kernel, xuwei5, linuxarm,
	linux-serial, linux-pci, zourongrong, kantyzc, zhichang.yuan02

On 01/24/2017 08:05 AM, zhichang.yuan wrote:
> Low-pin-count interface is integrated into some SoCs. The accesses to those
> peripherals under LPC make use of I/O ports rather than the memory mapped I/O.
>
> To drive these devices, this patch introduces a method named indirect-IO.
> In this method the in/out() accessor in include/asm-generic/io.h will be
> redefined. When upper layer drivers call in/out() with those known legacy port
> addresses to access the peripherals, the I/O operations will be routed to the
> right hooks which are registered specific to the host device, such as LPC.
> Then the hardware relevant manupulations are finished by the corresponding
> host.
>
> According to the comments on V5, this patch adds a common indirect-IO driver
> which support this I/O indirection to the generic directory.
>
> In the later pathches, some host-relevant drivers are implemented to support
> the specific I/O hooks and register them.
> Based on these, the upper layer drivers which depend on in/out() can work well
> without any extra work or any changes.
>
> Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
> Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
> Signed-off-by: John Garry <john.garry@huawei.com>

I like the extio idea. That allows us to handle all PIO requests on 
platforms that don't have native PIO support via different routes 
depending on the region they're in. Unfortunately we now we have 2 
frameworks for handling sparse PIO regions: One in extio, one in PCI.

Why don't we just merge the two? Most of the code that has #ifdef 
PCI_IOBASE throughout the code base sounds like an ideal candidate to 
get migrated to extio instead. Then we only have a single framework to 
worry about ...

> ---
>   include/asm-generic/io.h |  50 ++++++++++++++++
>   include/linux/extio.h    |  85 +++++++++++++++++++++++++++
>   include/linux/io.h       |   1 +
>   lib/Kconfig              |   8 +++
>   lib/Makefile             |   2 +
>   lib/extio.c              | 147 +++++++++++++++++++++++++++++++++++++++++++++++
>   6 files changed, 293 insertions(+)
>   create mode 100644 include/linux/extio.h
>   create mode 100644 lib/extio.c
>
> diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h
> index 7ef015e..c0d6db1 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/extio.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_INDIRECT_PIO
> +#define inb extio_inb
> +#else
>   #define inb inb
>   static inline u8 inb(unsigned long addr)
>   {
>   	return readb(PCI_IOBASE + addr);
>   }
> +#endif /* CONFIG_INDIRECT_PIO */

... we would also get rid of all of these constructs. Instead, every 
architecture that doesn't implement native PIO defines would end up in 
extio and we would enable it unconditionally for them.

>   #endif
>   
>   #ifndef inw
> +#ifdef CONFIG_INDIRECT_PIO
> +#define inw extio_inw
> +#else
>   #define inw inw
>   static inline u16 inw(unsigned long addr)
>   {
>   	return readw(PCI_IOBASE + addr);
>   }
> +#endif /* CONFIG_INDIRECT_PIO */
>   #endif
>   
>   #ifndef inl
> +#ifdef CONFIG_INDIRECT_PIO
> +#define inl extio_inl
> +#else
>   #define inl inl
>   static inline u32 inl(unsigned long addr)
>   {
>   	return readl(PCI_IOBASE + addr);
>   }
> +#endif /* CONFIG_INDIRECT_PIO */
>   #endif
>   
>   #ifndef outb
> +#ifdef CONFIG_INDIRECT_PIO
> +#define outb extio_outb
> +#else
>   #define outb outb
>   static inline void outb(u8 value, unsigned long addr)
>   {
>   	writeb(value, PCI_IOBASE + addr);
>   }
> +#endif /* CONFIG_INDIRECT_PIO */
>   #endif
>   
>   #ifndef outw
> +#ifdef CONFIG_INDIRECT_PIO
> +#define outw extio_outw
> +#else
>   #define outw outw
>   static inline void outw(u16 value, unsigned long addr)
>   {
>   	writew(value, PCI_IOBASE + addr);
>   }
> +#endif /* CONFIG_INDIRECT_PIO */
>   #endif
>   
>   #ifndef outl
> +#ifdef CONFIG_INDIRECT_PIO
> +#define outl extio_outl
> +#else
>   #define outl outl
>   static inline void outl(u32 value, unsigned long addr)
>   {
>   	writel(value, PCI_IOBASE + addr);
>   }
> +#endif /* CONFIG_INDIRECT_PIO */
>   #endif
>   
>   #ifndef inb_p
> @@ -459,54 +485,78 @@ static inline void outl_p(u32 value, unsigned long addr)
>    */
>   
>   #ifndef insb
> +#ifdef CONFIG_INDIRECT_PIO
> +#define insb extio_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_INDIRECT_PIO */
>   #endif
>   
>   #ifndef insw
> +#ifdef CONFIG_INDIRECT_PIO
> +#define insw extio_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_INDIRECT_PIO */
>   #endif
>   
>   #ifndef insl
> +#ifdef CONFIG_INDIRECT_PIO
> +#define insl extio_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_INDIRECT_PIO */
>   #endif
>   
>   #ifndef outsb
> +#ifdef CONFIG_INDIRECT_PIO
> +#define outsb extio_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_INDIRECT_PIO */
>   #endif
>   
>   #ifndef outsw
> +#ifdef CONFIG_INDIRECT_PIO
> +#define outsw extio_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_INDIRECT_PIO */
>   #endif
>   
>   #ifndef outsl
> +#ifdef CONFIG_INDIRECT_PIO
> +#define outsl extio_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_INDIRECT_PIO */
>   #endif
>   
>   #ifndef insb_p
> diff --git a/include/linux/extio.h b/include/linux/extio.h
> new file mode 100644
> index 0000000..2ca7eab
> --- /dev/null
> +++ b/include/linux/extio.h
> @@ -0,0 +1,85 @@
> +/*
> + * Copyright (C) 2016 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_EXTIO_H
> +#define __LINUX_EXTIO_H
> +
> +#ifdef __KERNEL__
> +
> +#include <linux/fwnode.h>
> +
> +struct extio_ops {
> +	u64 (*pfin)(void *devobj, unsigned long ptaddr,	size_t dlen);
> +	void (*pfout)(void *devobj, unsigned long ptaddr, u32 outval,
> +					size_t dlen);
> +	u64 (*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);
> +};
> +
> +struct extio_node {
> +	unsigned long bus_start;	/* bus start address */
> +	unsigned long io_start;	/* io port token corresponding to bus_start */
> +	size_t range_size;	/* size of the extio node operating range */
> +	struct fwnode_handle *fwnode;
> +	struct list_head list;
> +	struct extio_ops *ops;	/* ops operating on this node */
> +	void *devpara;	/* private parameter of the host device */
> +};
> +
> +extern u8 extio_inb(unsigned long addr);
> +extern void extio_outb(u8 value, unsigned long addr);
> +extern void extio_outw(u16 value, unsigned long addr);
> +extern void extio_outl(u32 value, unsigned long addr);
> +extern u16 extio_inw(unsigned long addr);
> +extern u32 extio_inl(unsigned long addr);
> +extern void extio_outb(u8 value, unsigned long addr);
> +extern void extio_outw(u16 value, unsigned long addr);
> +extern void extio_outl(u32 value, unsigned long addr);
> +extern void extio_insb(unsigned long addr, void *buffer, unsigned int count);
> +extern void extio_insl(unsigned long addr, void *buffer, unsigned int count);
> +extern void extio_insw(unsigned long addr, void *buffer, unsigned int count);
> +extern void extio_outsb(unsigned long addr, const void *buffer,
> +			unsigned int count);
> +extern void extio_outsw(unsigned long addr, const void *buffer,
> +			unsigned int count);
> +extern void extio_outsl(unsigned long addr, const void *buffer,
> +			unsigned int count);
> +
> +#ifdef CONFIG_INDIRECT_PIO
> +extern struct extio_node *extio_find_node(struct fwnode_handle *node);
> +
> +extern unsigned long
> +extio_translate(struct fwnode_handle *node, unsigned long bus_addr);
> +#else
> +static inline struct extio_node *extio_find_node(struct fwnode_handle *node)
> +{
> +	return NULL;
> +}
> +
> +static inline unsigned long
> +extio_translate(struct fwnode_handle *node, unsigned long bus_addr)
> +{
> +	return -1;
> +}
> +#endif
> +extern void register_extio(struct extio_node *node);
> +
> +#endif /* __KERNEL__ */
> +#endif /* __LINUX_EXTIO_H */
> diff --git a/include/linux/io.h b/include/linux/io.h
> index 82ef36e..6c68478 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/extio.h>
>   
>   struct device;
>   struct resource;
> diff --git a/lib/Kconfig b/lib/Kconfig
> index 260a80e..1d27c44 100644
> --- a/lib/Kconfig
> +++ b/lib/Kconfig
> @@ -59,6 +59,14 @@ config ARCH_USE_CMPXCHG_LOCKREF
>   config ARCH_HAS_FAST_MULTIPLIER
>   	bool
>   
> +config INDIRECT_PIO
> +	bool "access peripherals with legacy I/O port"
> +	depends on PCI
> +	help
> +	  Support special accessors for ISA I/O devices. This is needed for
> +	  SoCs that have not I/O space and do not support standard MMIO
> +	  read/write for the ISA range.
> +
>   config CRC_CCITT
>   	tristate "CRC-CCITT functions"
>   	help
> diff --git a/lib/Makefile b/lib/Makefile
> index bc4073a..bf03e05 100644
> --- a/lib/Makefile
> +++ b/lib/Makefile
> @@ -70,6 +70,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_INDIRECT_PIO)	+= extio.o
> +
>   obj-$(CONFIG_GENERIC_HWEIGHT) += hweight.o
>   
>   obj-$(CONFIG_BTREE) += btree.o
> diff --git a/lib/extio.c b/lib/extio.c
> new file mode 100644
> index 0000000..46228de
> --- /dev/null
> +++ b/lib/extio.c
> @@ -0,0 +1,147 @@
> +/*
> + * Copyright (C) 2016 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/io.h>
> +#include <linux/spinlock.h>
> +
> +static LIST_HEAD(extio_dev_list);
> +static DEFINE_RWLOCK(extio_list_lock);

Why not just make the list an RCU list? Then you don't need read locks. 
We also wouldn't create potential lock contention between devices that 
could easily have parallel PIO operations (say a PCI device and an LPC 
device).

> +
> +void register_extio(struct extio_node *node)
> +{
> +	write_lock(&extio_list_lock);
> +	list_add_tail(&node->list, &extio_dev_list);
> +	write_unlock(&extio_list_lock);
> +}
> +
> +static struct extio_node *find_extio_token(unsigned long addr)
> +{
> +	struct extio_node *extio_entry;
> +
> +	read_lock(&extio_list_lock);
> +	list_for_each_entry(extio_entry, &extio_dev_list, list) {
> +		if ((addr < extio_entry->io_start + extio_entry->range_size) &&
> +			(addr >= extio_entry->io_start))
> +			break;
> +	}
> +	read_unlock(&extio_list_lock);
> +	return (&extio_entry->list == &extio_dev_list) ? NULL : extio_entry;
> +}
> +
> +struct extio_node *extio_find_node(struct fwnode_handle *node)
> +{
> +	struct extio_node *entry;
> +
> +	read_lock(&extio_list_lock);
> +	list_for_each_entry(entry, &extio_dev_list, list) {
> +		if (entry->fwnode == node)
> +			break;
> +	}
> +	read_unlock(&extio_list_lock);
> +
> +	return (&entry->list == &extio_dev_list) ? NULL : entry;
> +}
> +
> +unsigned long extio_translate(struct fwnode_handle *node,
> +		unsigned long bus_addr)
> +{
> +	struct extio_node *entry;
> +	unsigned long port_id = -1;
> +
> +	read_lock(&extio_list_lock);
> +	list_for_each_entry(entry, &extio_dev_list, list) {
> +		if (entry->fwnode == node &&
> +			bus_addr >= entry->bus_start &&
> +			bus_addr - entry->bus_start < entry->range_size)
> +			port_id = entry->io_start + bus_addr -
> +					entry->bus_start;
> +	}
> +	read_unlock(&extio_list_lock);
> +
> +	return port_id;
> +}
> +
> +#ifdef PCI_IOBASE
> +
> +#define BUILD_EXTIO(bw, type)						\
> +type extio_in##bw(unsigned long addr)					\
> +{									\
> +	struct extio_node *extio_entry = find_extio_token(addr);	\
> +									\
> +	if (!extio_entry)						\
> +		return read##bw(PCI_IOBASE + addr);			\
> +	return extio_entry->ops->pfin ?					\
> +			extio_entry->ops->pfin(extio_entry->devpara,	\
> +			addr, sizeof(type)) : -1;			\
> +}									\
> +									\
> +void extio_out##bw(type value, unsigned long addr)			\
> +{									\
> +	struct extio_node *extio_entry = find_extio_token(addr);	\
> +									\
> +	if (!extio_entry)						\
> +		write##bw(value, PCI_IOBASE + addr);			\

All of the fallback code would also disappear as a nice side effect of 
making pci pio handling a user of extio :).


Alex


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH V6 4/5] LPC: Support the device-tree LPC host on Hip06/Hip07
  2017-01-24  7:05 ` [PATCH V6 4/5] LPC: Support the device-tree LPC host on Hip06/Hip07 zhichang.yuan
  2017-01-27 22:12   ` Rob Herring
@ 2017-01-30 20:08   ` Alexander Graf
  2017-01-31 10:07     ` John Garry
  2017-02-13 14:39     ` zhichang.yuan
  1 sibling, 2 replies; 37+ messages in thread
From: Alexander Graf @ 2017-01-30 20:08 UTC (permalink / raw)
  To: zhichang.yuan, catalin.marinas, will.deacon, robh+dt,
	frowand.list, bhelgaas, rafael, mark.rutland, brian.starkey,
	olof, arnd, linux-arm-kernel
  Cc: devicetree, lorenzo.pieralisi, gabriele.paoloni, minyard, benh,
	john.garry, liviu.dudau, linux-kernel, xuwei5, linuxarm,
	linux-serial, linux-pci, zourongrong, kantyzc, zhichang.yuan02



On 24/01/2017 08:05, zhichang.yuan wrote:
> The low-pin-count(LPC) interface of Hip06/Hip07 accesses the peripherals in
> I/O port addresses. This patch implements the LPC host controller driver which
> perform the I/O operations on the underlying hardware.
> We don't want to touch those existing peripherals' driver, such as ipmi-bt. So
> this driver applies the indirect-IO introduced in the previous patch after
> registering an indirect-IO node to the indirect-IO devices list which will be
> searched in the I/O accessors.
> As the I/O translations for LPC children depend on the host I/O registration,
> we should ensure the host I/O registration is finished before all the LPC
> children scanning. That is why an arch_init() hook was added in this patch.
>
> Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
> Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
> ---
>  .../arm/hisilicon/hisilicon-low-pin-count.txt      |  33 ++
>  MAINTAINERS                                        |   9 +
>  arch/arm64/boot/dts/hisilicon/hip06-d03.dts        |   4 +
>  arch/arm64/boot/dts/hisilicon/hip06.dtsi           |  14 +
>  drivers/bus/Kconfig                                |   8 +
>  drivers/bus/Makefile                               |   1 +
>  drivers/bus/hisi_lpc.c                             | 599 +++++++++++++++++++++
>  7 files changed, 668 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
>  create mode 100644 drivers/bus/hisi_lpc.c
>
> diff --git a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
> new file mode 100644
> index 0000000..213181f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
> @@ -0,0 +1,33 @@
> +Hisilicon Hip06 low-pin-count device
> +  Hisilicon Hip06 SoCs implement a Low Pin Count (LPC) controller, which
> +  provides I/O access to some legacy ISA devices.
> +  Hip06 is based on arm64 architecture where there is no I/O space. So, the
> +  I/O ports here are not cpu addresses, and there is no 'ranges' property in
> +  LPC device node.
> +
> +Required properties:
> +- compatible:  value should be as follows:
> +	(a) "hisilicon,hip06-lpc"
> +	(b) "hisilicon,hip07-lpc"
> +- #address-cells: must be 2 which stick to the ISA/EISA binding doc.
> +- #size-cells: must be 1 which stick to the ISA/EISA binding doc.
> +- reg: base memory range where the LPC register set is mapped.
> +
> +Note:
> +  The node name before '@' must be "isa" to represent the binding stick to the
> +  ISA/EISA binding specification.
> +
> +Example:
> +
> +isa@a01b0000 {
> +	compatible = "hisilicon,hip06-lpc";
> +	#address-cells = <2>;
> +	#size-cells = <1>;
> +	reg = <0x0 0xa01b0000 0x0 0x1000>;
> +
> +	ipmi0: bt@e4 {
> +		compatible = "ipmi-bt";
> +		device_type = "ipmi";
> +		reg = <0x01 0xe4 0x04>;
> +	};
> +};
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 26edd83..0153707 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -5855,6 +5855,15 @@ F:	include/uapi/linux/if_hippi.h
>  F:	net/802/hippi.c
>  F:	drivers/net/hippi/
>
> +HISILICON LPC BUS DRIVER
> +M:	Zhichang Yuan <yuanzhichang@hisilicon.com>
> +L:	linux-arm-kernel@lists.infradead.org
> +W:	http://www.hisilicon.com
> +S:	Maintained
> +F:	drivers/bus/hisi_lpc.c
> +F:	lib/extio.c
> +F:	Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
> +
>  HISILICON NETWORK SUBSYSTEM DRIVER
>  M:	Yisen Zhuang <yisen.zhuang@huawei.com>
>  M:	Salil Mehta <salil.mehta@huawei.com>
> diff --git a/arch/arm64/boot/dts/hisilicon/hip06-d03.dts b/arch/arm64/boot/dts/hisilicon/hip06-d03.dts
> index 7c4114a..75b2b5c 100644
> --- a/arch/arm64/boot/dts/hisilicon/hip06-d03.dts
> +++ b/arch/arm64/boot/dts/hisilicon/hip06-d03.dts
> @@ -52,3 +52,7 @@
>  &usb_ehci {
>  	status = "ok";
>  };
> +
> +&ipmi0 {
> +	status = "ok";
> +};
> diff --git a/arch/arm64/boot/dts/hisilicon/hip06.dtsi b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
> index a049b64..c450f8d 100644
> --- a/arch/arm64/boot/dts/hisilicon/hip06.dtsi
> +++ b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
> @@ -318,6 +318,20 @@
>  		#size-cells = <2>;
>  		ranges;
>
> +		isa@a01b0000 {
> +			compatible = "hisilicon,hip06-lpc";
> +			#size-cells = <1>;
> +			#address-cells = <2>;
> +			reg = <0x0 0xa01b0000 0x0 0x1000>;
> +
> +			ipmi0: bt@e4 {
> +				compatible = "ipmi-bt";
> +				device_type = "ipmi";
> +				reg = <0x01 0xe4 0x04>;
> +				status = "disabled";
> +			};
> +		};
> +
>  		refclk: refclk {
>  			compatible = "fixed-clock";
>  			clock-frequency = <50000000>;
> diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
> index b9e8cfc..58cee84 100644
> --- a/drivers/bus/Kconfig
> +++ b/drivers/bus/Kconfig
> @@ -64,6 +64,14 @@ config BRCMSTB_GISB_ARB
>  	  arbiter. This driver provides timeout and target abort error handling
>  	  and internal bus master decoding.
>
> +config HISILICON_LPC
> +	bool "Workaround for nonstandard ISA I/O space on Hisilicon Hip0X"

It's not a workaround, it's support. Better word it like

   "Support for ISA I/O space on Hisilicon HIP0X"

> +	depends on (ARM64 && ARCH_HISI && PCI) || COMPILE_TEST
> +	select INDIRECT_PIO
> +	help
> +	  Driver needed for some legacy ISA devices attached to Low-Pin-Count
> +	  on Hisilicon Hip0X SoC.
> +
>  config IMX_WEIM
>  	bool "Freescale EIM DRIVER"
>  	depends on ARCH_MXC
> diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
> index cc6364b..28e3862 100644
> --- a/drivers/bus/Makefile
> +++ b/drivers/bus/Makefile
> @@ -7,6 +7,7 @@ obj-$(CONFIG_ARM_CCI)		+= arm-cci.o
>  obj-$(CONFIG_ARM_CCN)		+= arm-ccn.o
>
>  obj-$(CONFIG_BRCMSTB_GISB_ARB)	+= brcmstb_gisb.o
> +obj-$(CONFIG_HISILICON_LPC)	+= hisi_lpc.o
>  obj-$(CONFIG_IMX_WEIM)		+= imx-weim.o
>  obj-$(CONFIG_MIPS_CDMM)		+= mips_cdmm.o
>  obj-$(CONFIG_MVEBU_MBUS) 	+= mvebu-mbus.o
> diff --git a/drivers/bus/hisi_lpc.c b/drivers/bus/hisi_lpc.c
> new file mode 100644
> index 0000000..a96e384
> --- /dev/null
> +++ b/drivers/bus/hisi_lpc.c
> @@ -0,0 +1,599 @@
> +/*
> + * Copyright (C) 2016 Hisilicon Limited, All Rights Reserved.
> + * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
> + * Author: Zou Rongrong <zourongrong@huawei.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/acpi.h>
> +#include <linux/console.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_platform.h>
> +#include <linux/pci.h>
> +#include <linux/serial_8250.h>
> +#include <linux/slab.h>
> +
> +/*
> + * Setting this bit means each IO operation will target to a
> + * different port address:
> + * 0 means repeatedly IO operations will stick on the same port,
> + * such as BT;
> + */
> +#define FG_INCRADDR_LPC		0x02
> +
> +struct lpc_cycle_para {
> +	unsigned int opflags;
> +	unsigned int csize; /* the data length of each operation */
> +};
> +
> +struct hisilpc_dev {
> +	spinlock_t cycle_lock;
> +	void __iomem  *membase;
> +	struct extio_node *extio;
> +};
> +
> +/* bounds of the LPC bus address range */
> +#define LPC_MIN_BUS_RANGE	0x0
> +
> +/*
> + * The maximal IO size for each leagcy bus.

legacy?

I don't really understand why this bus is legacy though. It looks like a 
simple MMIO-to-LPC bridge to me.

> + * The port size of legacy I/O devices is normally less than 0x400.
> + * Defining the I/O range size as 0x400 here should be sufficient for
> + * all peripherals under one bus.
> + */

This comment doesn't make a lot of sense. What is the limit? Is there a 
hardware limit?

We don't dynamically allocate devices on the lpc bus, so why imply a 
limit at all?

> +#define LPC_BUS_IO_SIZE		0x400
> +
> +/* The maximum continuous operations */
> +#define LPC_MAX_OPCNT	16
> +/* only support IO data unit length is four at maximum */
> +#define LPC_MAX_DULEN	4
> +#if LPC_MAX_DULEN > LPC_MAX_OPCNT
> +#error "LPC.. MAX_DULEN must be not bigger than MAX_OPCNT!"
> +#endif
> +
> +#define LPC_REG_START		0x00 /* start a new LPC cycle */
> +#define LPC_REG_OP_STATUS	0x04 /* the current LPC status */
> +#define LPC_REG_IRQ_ST		0x08 /* interrupt enable&status */
> +#define LPC_REG_OP_LEN		0x10 /* how many LPC cycles each start */
> +#define LPC_REG_CMD		0x14 /* command for the required LPC cycle */
> +#define LPC_REG_ADDR		0x20 /* LPC target address */
> +#define LPC_REG_WDATA		0x24 /* data to be written */
> +#define LPC_REG_RDATA		0x28 /* data coming from peer */
> +
> +
> +/* The command register fields */
> +#define LPC_CMD_SAMEADDR	0x08
> +#define LPC_CMD_TYPE_IO		0x00
> +#define LPC_CMD_WRITE		0x01
> +#define LPC_CMD_READ		0x00
> +/* the bit attribute is W1C. 1 represents OK. */
> +#define LPC_STAT_BYIRQ		0x02
> +
> +#define LPC_STATUS_IDLE		0x01
> +#define LPC_OP_FINISHED		0x02
> +
> +#define START_WORK		0x01
> +
> +/*
> + * The minimal waiting interval... Suggest it is not less than 10.
> + * Bigger value probably will lower the performance.

Are you sure you want this comment to be upstream? :)

> + */
> +#define LPC_NSEC_PERWAIT	100
> +/*
> + * The maximum waiting time is about 128us.
> + * The fastest IO cycle time is about 390ns, but the worst case will wait
> + * for extra 256 lpc clocks, so (256 + 13) * 30ns = 8 us. The maximum
> + * burst cycles is 16. So, the maximum waiting time is about 128us under
> + * worst case.
> + * choose 1300 as the maximum.
> + */
> +#define LPC_MAX_WAITCNT		1300
> +/* About 10us. This is specific for single IO operation, such as inb. */
> +#define LPC_PEROP_WAITCNT	100
> +
> +
> +static inline int wait_lpc_idle(unsigned char *mbase,

No need to specify inline.

> +				unsigned int waitcnt) {
> +	u32 opstatus;
> +
> +	while (waitcnt--) {
> +		ndelay(LPC_NSEC_PERWAIT);
> +		opstatus = readl(mbase + LPC_REG_OP_STATUS);
> +		if (opstatus & LPC_STATUS_IDLE)
> +			return (opstatus & LPC_OP_FINISHED) ? 0 : (-EIO);

It's a shame we have to busy loop, but I guess no calling code outside 
is prepared for rescheduling at this point.

> +	}
> +	return -ETIME;
> +}
> +
> +/*
> + * hisilpc_target_in - trigger a series of lpc cycles to read required data
> + *		       from target peripheral.
> + * @pdev: pointer to hisi lpc device
> + * @para: some parameters used to control the lpc I/O operations
> + * @ptaddr: the lpc I/O target port address
> + * @buf: where the read back data is stored
> + * @opcnt: how many I/O operations required in this calling
> + *
> + * Only one byte data is read each I/O operation.
> + *
> + * Returns 0 on success, non-zero on fail.
> + *
> + */
> +static int
> +hisilpc_target_in(struct hisilpc_dev *lpcdev, struct lpc_cycle_para *para,
> +		  unsigned long ptaddr, unsigned char *buf,
> +		  unsigned long opcnt)
> +{
> +	unsigned long cnt_per_trans;
> +	unsigned int cmd_word;
> +	unsigned int waitcnt;
> +	int ret;
> +
> +	if (!buf || !opcnt || !para || !para->csize || !lpcdev)
> +		return -EINVAL;
> +
> +	if (opcnt  > LPC_MAX_OPCNT)
> +		return -EINVAL;
> +
> +	cmd_word = LPC_CMD_TYPE_IO | LPC_CMD_READ;
> +	waitcnt = LPC_PEROP_WAITCNT;
> +	if (!(para->opflags & FG_INCRADDR_LPC)) {
> +		cmd_word |= LPC_CMD_SAMEADDR;
> +		waitcnt = LPC_MAX_WAITCNT;
> +	}
> +
> +	ret = 0;
> +	cnt_per_trans = (para->csize == 1) ? opcnt : para->csize;
> +	for (; opcnt && !ret; cnt_per_trans = para->csize) {
> +		unsigned long flags;
> +
> +		/* whole operation must be atomic */
> +		spin_lock_irqsave(&lpcdev->cycle_lock, flags);

Ouch. This is going to kill your RT jitter. Is there no better way?

> +
> +		writel(cnt_per_trans, lpcdev->membase + LPC_REG_OP_LEN);
> +
> +		writel(cmd_word, lpcdev->membase + LPC_REG_CMD);
> +
> +		writel(ptaddr, lpcdev->membase + LPC_REG_ADDR);
> +
> +		writel(START_WORK, lpcdev->membase + LPC_REG_START);
> +
> +		/* whether the operation is finished */
> +		ret = wait_lpc_idle(lpcdev->membase, waitcnt);
> +		if (!ret) {
> +			opcnt -= cnt_per_trans;
> +			for (; cnt_per_trans--; buf++)
> +				*buf = readl(lpcdev->membase + LPC_REG_RDATA);
> +		}
> +
> +		spin_unlock_irqrestore(&lpcdev->cycle_lock, flags);
> +	}
> +
> +	return ret;
> +}
> +
> +/**
> + * hisilpc_target_out - trigger a series of lpc cycles to write required
> + *			data to target peripheral.
> + * @pdev: pointer to hisi lpc device
> + * @para: some parameters used to control the lpc I/O operations
> + * @ptaddr: the lpc I/O target port address
> + * @buf: where the data to be written is stored
> + * @opcnt: how many I/O operations required
> + *
> + * Only one byte data is read each I/O operation.
> + *
> + * Returns 0 on success, non-zero on fail.
> + *
> + */
> +static int
> +hisilpc_target_out(struct hisilpc_dev *lpcdev, struct lpc_cycle_para *para,
> +		   unsigned long ptaddr, const unsigned char *buf,
> +		   unsigned long opcnt)
> +{
> +	unsigned long cnt_per_trans;
> +	unsigned int cmd_word;
> +	unsigned int waitcnt;
> +	int ret;
> +
> +	if (!buf || !opcnt || !para || !lpcdev)
> +		return -EINVAL;
> +
> +	if (opcnt > LPC_MAX_OPCNT)
> +		return -EINVAL;
> +	/* default is increasing address */
> +	cmd_word = LPC_CMD_TYPE_IO | LPC_CMD_WRITE;
> +	waitcnt = LPC_PEROP_WAITCNT;
> +	if (!(para->opflags & FG_INCRADDR_LPC)) {
> +		cmd_word |= LPC_CMD_SAMEADDR;
> +		waitcnt = LPC_MAX_WAITCNT;
> +	}
> +
> +	ret = 0;
> +	cnt_per_trans = (para->csize == 1) ? opcnt : para->csize;
> +	for (; opcnt && !ret; cnt_per_trans = para->csize) {
> +		unsigned long flags;
> +
> +		spin_lock_irqsave(&lpcdev->cycle_lock, flags);

Same thing here

> +
> +		writel(cnt_per_trans, lpcdev->membase + LPC_REG_OP_LEN);
> +		opcnt -= cnt_per_trans;
> +		for (; cnt_per_trans--; buf++)
> +			writel(*buf, lpcdev->membase + LPC_REG_WDATA);
> +
> +		writel(cmd_word, lpcdev->membase + LPC_REG_CMD);
> +
> +		writel(ptaddr, lpcdev->membase + LPC_REG_ADDR);
> +
> +		writel(START_WORK, lpcdev->membase + LPC_REG_START);
> +
> +		/* whether the operation is finished */
> +		ret = wait_lpc_idle(lpcdev->membase, waitcnt);
> +
> +		spin_unlock_irqrestore(&lpcdev->cycle_lock, flags);
> +	}
> +
> +	return ret;
> +}
> +
> +static inline unsigned long

Don't explicitly mention inline, the compiler will figure that out for you.

> +hisi_lpc_pio_to_addr(struct hisilpc_dev *lpcdev, unsigned long pio)
> +{
> +	return pio - lpcdev->extio->io_start + lpcdev->extio->bus_start;
> +}
> +
> +
> +/**
> + * hisilpc_comm_in - read/input the data from the I/O peripheral
> + *		     through LPC.
> + * @devobj: pointer to the device information relevant to LPC controller.
> + * @pio: the target I/O port address.
> + * @dlen: the data length required to read from the target I/O port.
> + *
> + * when succeed, the data read back is stored in buffer pointed by inbuf.
> + * For inb, return the data read from I/O or -1 when error occur.
> + */
> +static u64 hisilpc_comm_in(void *devobj, unsigned long pio, size_t dlen)
> +{
> +	struct hisilpc_dev *lpcdev = devobj;
> +	struct lpc_cycle_para iopara;
> +	u32 rd_data;

rd_data needs to be initialized to 0. Otherwise it may contain stale 
stack contents and corrupt non-32bit dlen returns.

> +	unsigned char *newbuf;
> +	int ret = 0;
> +	unsigned long ptaddr;
> +
> +	if (!lpcdev || !dlen || dlen > LPC_MAX_DULEN ||	(dlen & (dlen - 1)))
> +		return -1;

Isn't this -EINVAL?

> +
> +	/* the local buffer must be enough for one data unit */
> +	if (sizeof(rd_data) < dlen)
> +		return -1;

Same here.

Also, the above seems a very convoluted way of saying

   switch (dlen) {
   case 1:
   case 2:
   case 4:
     break;
   default:
     return -EINVAL;
   }

But I guess the way you write it doesn't hurt ;)


Alex

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH V6 1/5] LIB: Indirect ISA/LPC port IO introduced
  2017-01-24  7:05 ` [PATCH V6 1/5] LIB: Indirect ISA/LPC port IO introduced zhichang.yuan
  2017-01-30 17:12   ` Alexander Graf
@ 2017-01-31  0:09   ` Bjorn Helgaas
  2017-01-31 13:34     ` John Garry
  1 sibling, 1 reply; 37+ messages in thread
From: Bjorn Helgaas @ 2017-01-31  0:09 UTC (permalink / raw)
  To: zhichang.yuan
  Cc: mark.rutland, benh, gabriele.paoloni, rafael, linux-pci,
	will.deacon, linuxarm, frowand.list, lorenzo.pieralisi, arnd,
	xuwei5, linux-serial, catalin.marinas, devicetree, minyard,
	liviu.dudau, john.garry, zourongrong, robh+dt, bhelgaas, kantyzc,
	zhichang.yuan02, linux-arm-kernel, linux-kernel, olof,
	brian.starkey

On Tue, Jan 24, 2017 at 03:05:21PM +0800, zhichang.yuan wrote:
> Low-pin-count interface is integrated into some SoCs. The accesses to those
> peripherals under LPC make use of I/O ports rather than the memory mapped I/O.
> 
> To drive these devices, this patch introduces a method named indirect-IO.

It's slightly confusing to call this "indirect I/O" and then use
"extio" for the filename and function prefix.  It'd be nice to use
related names.

> +struct extio_node {
> +	unsigned long bus_start;	/* bus start address */
> +	unsigned long io_start;	/* io port token corresponding to bus_start */
> +	size_t range_size;	/* size of the extio node operating range */
> +	struct fwnode_handle *fwnode;
> +	struct list_head list;
> +	struct extio_ops *ops;	/* ops operating on this node */
> +	void *devpara;	/* private parameter of the host device */
> +};

I wish we didn't have both struct io_range and struct extio_node.  It
seems like they're both sort of trying to do the same thing.  Maybe
this is the same as what Alex is saying.

Bjorn

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH V6 2/5] PCI: Adapt pci_register_io_range() for indirect-IO and  PCI I/O translation
  2017-01-24  7:05 ` [PATCH V6 2/5] PCI: Adapt pci_register_io_range() for indirect-IO and PCI I/O translation zhichang.yuan
@ 2017-01-31  0:10   ` Bjorn Helgaas
  2017-01-31 13:39     ` John Garry
  2017-01-31  0:15   ` Bjorn Helgaas
  2017-02-02 17:36   ` John Garry
  2 siblings, 1 reply; 37+ messages in thread
From: Bjorn Helgaas @ 2017-01-31  0:10 UTC (permalink / raw)
  To: zhichang.yuan
  Cc: mark.rutland, benh, gabriele.paoloni, rafael, linux-pci,
	will.deacon, linuxarm, frowand.list, lorenzo.pieralisi, arnd,
	xuwei5, linux-serial, catalin.marinas, devicetree, minyard,
	liviu.dudau, john.garry, zourongrong, robh+dt, bhelgaas, kantyzc,
	zhichang.yuan02, linux-arm-kernel, linux-kernel, olof,
	brian.starkey

On Tue, Jan 24, 2017 at 03:05:22PM +0800, zhichang.yuan wrote:
> After indirect-IO is introduced, system must can assigned indirect-IO devices
> with logical I/O ranges which are different from those for PCI I/O devices.
> Otherwise, I/O accessors can't identify whether the I/O port is for memory
> mapped I/O or indirect-IO.

Maybe:

  We must assign logical I/O port space for indirect I/O such that the
  I/O accessors can tell whether a logical I/O port refers to memory-
  mapped I/O space or indirect I/O space.

> As current helper, pci_register_io_range(), is used for PCI I/O ranges
> registration and translation, indirect-IO devices should also apply these
> helpers to manage the I/O ranges. It will be easy to ensure the assigned
> logical I/O ranges unique.
> But for indirect-IO devices, there is no cpu address. The current
> pci_register_io_range() can not work for this case.
> 
> This patch makes some changes on the pci_register_io_range() to support the
> I/O range registration with device's fwnode also. After this, the indirect-IO
> devices can register the device-local I/O range to system logical I/O and
> easily perform the translation between device-local I/O range and sytem
> logical I/O range.

> -int __weak pci_register_io_range(phys_addr_t addr, resource_size_t size)
> +int __weak pci_register_io_range(struct fwnode_handle *node, phys_addr_t addr,
> +				 resource_size_t size, unsigned long *port)

Why is this __weak?  It looks like it's been __weak since its
introduction by 41f8bba7f555 ("of/pci: Add pci_register_io_range() and
pci_pio_to_address()"), but I don't see any other implementations of
it.

Can you add a patch that does nothing but make this non-weak?

> +#else
> +	/*
> +	 * powerpc and microblaze have their own registration,
> +	 * just look up the value here

Can you include a pointer to the powerpc and microblaze registration
code here?  It's conceivable that somebody could generalize this
enough to support powerpc and microblaze as well.

> --- a/include/linux/pci.h
> +++ b/include/linux/pci.h
> @@ -34,6 +34,9 @@
>  
>  #include <linux/pci_ids.h>
>  
> +/* the macro below flags an invalid cpu address
> + * and is used by IO special hosts              */

s/cpu/CPU/

Use conventional multi-line comment style:

/*
 * IO_RANGE_IOEXT flags an invalid CPU address ...
 */

> +#define IO_RANGE_IOEXT (resource_size_t)(-1ull)

And put this close to related things, e.g., pci_register_io_range(),
instead of just dropping it in at the top of the file.

>  /*
>   * The PCI interface treats multi-function devices as independent
>   * devices.  The slot/function address of each device is encoded
> @@ -1197,8 +1200,8 @@ int __must_check pci_bus_alloc_resource(struct pci_bus *bus,
>  						  resource_size_t),
>  			void *alignf_data);
>  
> -
> -int pci_register_io_range(phys_addr_t addr, resource_size_t size);
> +int pci_register_io_range(struct fwnode_handle *node, phys_addr_t addr,
> +			  resource_size_t size, unsigned long *port);
>  unsigned long pci_address_to_pio(phys_addr_t addr);
>  phys_addr_t pci_pio_to_address(unsigned long pio);
>  int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr);
> -- 
> 1.9.1
> 

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH V6 2/5] PCI: Adapt pci_register_io_range() for indirect-IO and  PCI I/O translation
  2017-01-24  7:05 ` [PATCH V6 2/5] PCI: Adapt pci_register_io_range() for indirect-IO and PCI I/O translation zhichang.yuan
  2017-01-31  0:10   ` Bjorn Helgaas
@ 2017-01-31  0:15   ` Bjorn Helgaas
  2017-02-04 12:59     ` John Garry
  2017-02-02 17:36   ` John Garry
  2 siblings, 1 reply; 37+ messages in thread
From: Bjorn Helgaas @ 2017-01-31  0:15 UTC (permalink / raw)
  To: zhichang.yuan
  Cc: mark.rutland, benh, gabriele.paoloni, rafael, linux-pci,
	will.deacon, linuxarm, frowand.list, lorenzo.pieralisi, arnd,
	xuwei5, linux-serial, catalin.marinas, devicetree, minyard,
	liviu.dudau, john.garry, zourongrong, robh+dt, bhelgaas, kantyzc,
	zhichang.yuan02, linux-arm-kernel, linux-kernel, olof,
	brian.starkey

On Tue, Jan 24, 2017 at 03:05:22PM +0800, zhichang.yuan wrote:
> After indirect-IO is introduced, system must can assigned indirect-IO devices
> with logical I/O ranges which are different from those for PCI I/O devices.
> Otherwise, I/O accessors can't identify whether the I/O port is for memory
> mapped I/O or indirect-IO.
> As current helper, pci_register_io_range(), is used for PCI I/O ranges
> registration and translation, indirect-IO devices should also apply these
> helpers to manage the I/O ranges. It will be easy to ensure the assigned
> logical I/O ranges unique.
> But for indirect-IO devices, there is no cpu address. The current
> pci_register_io_range() can not work for this case.
> 
> This patch makes some changes on the pci_register_io_range() to support the
> I/O range registration with device's fwnode also. After this, the indirect-IO
> devices can register the device-local I/O range to system logical I/O and
> easily perform the translation between device-local I/O range and sytem
> logical I/O range.
> 
> Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
> Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>

I had a couple trivial comments, but you can include my:

Acked-by: Bjorn Helgaas <bhelgaas@google.com>	# drivers/pci parts

in your next revision.  I don't know who you have in mind to merge this;
it doesn't really touch much of PCI.  But let me know if you need anything
else from me.

> ---
>  drivers/acpi/pci_root.c | 12 +++++-------
>  drivers/of/address.c    |  8 ++------
>  drivers/pci/pci.c       | 44 ++++++++++++++++++++++++++++++++++++++++----
>  include/linux/pci.h     |  7 +++++--
>  4 files changed, 52 insertions(+), 19 deletions(-)
> 
> diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
> index bf601d4..6cadf05 100644
> --- a/drivers/acpi/pci_root.c
> +++ b/drivers/acpi/pci_root.c
> @@ -730,7 +730,8 @@ static void acpi_pci_root_validate_resources(struct device *dev,
>  	}
>  }
>  
> -static void acpi_pci_root_remap_iospace(struct resource_entry *entry)
> +static void acpi_pci_root_remap_iospace(struct fwnode_handle *node,
> +					struct resource_entry *entry)
>  {
>  #ifdef PCI_IOBASE
>  	struct resource *res = entry->res;
> @@ -739,11 +740,7 @@ static void acpi_pci_root_remap_iospace(struct resource_entry *entry)
>  	resource_size_t length = resource_size(res);
>  	unsigned long port;
>  
> -	if (pci_register_io_range(cpu_addr, length))
> -		goto err;
> -
> -	port = pci_address_to_pio(cpu_addr);
> -	if (port == (unsigned long)-1)
> +	if (pci_register_io_range(node, cpu_addr, length, &port))
>  		goto err;
>  
>  	res->start = port;
> @@ -781,7 +778,8 @@ int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info)
>  	else {
>  		resource_list_for_each_entry_safe(entry, tmp, list) {
>  			if (entry->res->flags & IORESOURCE_IO)
> -				acpi_pci_root_remap_iospace(entry);
> +				acpi_pci_root_remap_iospace(&device->fwnode,
> +							    entry);
>  
>  			if (entry->res->flags & IORESOURCE_DISABLED)
>  				resource_list_destroy_entry(entry);
> diff --git a/drivers/of/address.c b/drivers/of/address.c
> index 02b2903..d85d228 100644
> --- a/drivers/of/address.c
> +++ b/drivers/of/address.c
> @@ -2,6 +2,7 @@
>  #define pr_fmt(fmt)	"OF: " fmt
>  
>  #include <linux/device.h>
> +#include <linux/fwnode.h>
>  #include <linux/io.h>
>  #include <linux/ioport.h>
>  #include <linux/module.h>
> @@ -323,14 +324,9 @@ int of_pci_range_to_resource(struct of_pci_range *range,
>  
>  	if (res->flags & IORESOURCE_IO) {
>  		unsigned long port;
> -		err = pci_register_io_range(range->cpu_addr, range->size);
> +		err = pci_register_io_range(&np->fwnode, range->cpu_addr, range->size, &port);
>  		if (err)
>  			goto invalid_range;
> -		port = pci_address_to_pio(range->cpu_addr);
> -		if (port == (unsigned long)-1) {
> -			err = -EINVAL;
> -			goto invalid_range;
> -		}
>  		res->start = port;
>  	} else {
>  		if ((sizeof(resource_size_t) < 8) &&
> diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
> index a881c0d..5289221 100644
> --- a/drivers/pci/pci.c
> +++ b/drivers/pci/pci.c
> @@ -3241,6 +3241,7 @@ int pci_request_regions_exclusive(struct pci_dev *pdev, const char *res_name)
>  #ifdef PCI_IOBASE
>  struct io_range {
>  	struct list_head list;
> +	struct fwnode_handle *node;
>  	phys_addr_t start;
>  	resource_size_t size;
>  };
> @@ -3253,7 +3254,8 @@ struct io_range {
>   * Record the PCI IO range (expressed as CPU physical address + size).
>   * Return a negative value if an error has occured, zero otherwise
>   */
> -int __weak pci_register_io_range(phys_addr_t addr, resource_size_t size)
> +int __weak pci_register_io_range(struct fwnode_handle *node, phys_addr_t addr,
> +				 resource_size_t size, unsigned long *port)
>  {
>  	int err = 0;
>  
> @@ -3261,10 +3263,31 @@ int __weak pci_register_io_range(phys_addr_t addr, resource_size_t size)
>  	struct io_range *range;
>  	resource_size_t allocated_size = 0;
>  
> +	/*
> +	 * As indirect-IO which support multiple bus instances is introduced,
> +	 * the input 'addr' is probably not page-aligned. If the PCI I/O
> +	 * ranges are registered after indirect-IO, there is risk that the
> +	 * start logical PIO assigned to PCI I/O is not page-aligned.
> +	 * This will cause some I/O subranges are not remapped or overlapped
> +	 * in pci_remap_iospace() handling.
> +	 */
> +	WARN_ON(addr != IO_RANGE_IOEXT && !(addr & PAGE_MASK));
> +	/*
> +	 * MMIO will call ioremap, it is better to align size with PAGE_SIZE,
> +	 * then the return linux virtual PIO is page-aligned.
> +	 */
> +	if (size & PAGE_MASK)
> +		size = PAGE_ALIGN(size);
> +
>  	/* check if the range hasn't been previously recorded */
>  	spin_lock(&io_range_lock);
>  	list_for_each_entry(range, &io_range_list, list) {
> -		if (addr >= range->start && addr + size <= range->start + size) {
> +		if (node == range->node)
> +			goto end_register;
> +
> +		if (addr != IO_RANGE_IOEXT &&
> +		    addr >= range->start &&
> +		    addr + size <= range->start + size) {
>  			/* range already registered, bail out */
>  			goto end_register;
>  		}
> @@ -3290,6 +3313,7 @@ int __weak pci_register_io_range(phys_addr_t addr, resource_size_t size)
>  		goto end_register;
>  	}
>  
> +	range->node = node;
>  	range->start = addr;
>  	range->size = size;
>  
> @@ -3297,6 +3321,14 @@ int __weak pci_register_io_range(phys_addr_t addr, resource_size_t size)
>  
>  end_register:
>  	spin_unlock(&io_range_lock);
> +
> +	*port = allocated_size;
> +#else
> +	/*
> +	 * powerpc and microblaze have their own registration,
> +	 * just look up the value here
> +	 */
> +	*port = pci_address_to_pio(addr);
>  #endif
>  
>  	return err;
> @@ -3315,7 +3347,9 @@ phys_addr_t pci_pio_to_address(unsigned long pio)
>  
>  	spin_lock(&io_range_lock);
>  	list_for_each_entry(range, &io_range_list, list) {
> -		if (pio >= allocated_size && pio < allocated_size + range->size) {
> +		if (range->start != IO_RANGE_IOEXT &&
> +			pio >= allocated_size &&
> +			pio < allocated_size + range->size) {
>  			address = range->start + pio - allocated_size;
>  			break;
>  		}
> @@ -3336,7 +3370,9 @@ unsigned long __weak pci_address_to_pio(phys_addr_t address)
>  
>  	spin_lock(&io_range_lock);
>  	list_for_each_entry(res, &io_range_list, list) {
> -		if (address >= res->start && address < res->start + res->size) {
> +		if (res->start != IO_RANGE_IOEXT &&
> +			address >= res->start &&
> +			address < res->start + res->size) {
>  			addr = address - res->start + offset;
>  			break;
>  		}
> diff --git a/include/linux/pci.h b/include/linux/pci.h
> index e2d1a12..8d91af8 100644
> --- a/include/linux/pci.h
> +++ b/include/linux/pci.h
> @@ -34,6 +34,9 @@
>  
>  #include <linux/pci_ids.h>
>  
> +/* the macro below flags an invalid cpu address
> + * and is used by IO special hosts              */
> +#define IO_RANGE_IOEXT (resource_size_t)(-1ull)
>  /*
>   * The PCI interface treats multi-function devices as independent
>   * devices.  The slot/function address of each device is encoded
> @@ -1197,8 +1200,8 @@ int __must_check pci_bus_alloc_resource(struct pci_bus *bus,
>  						  resource_size_t),
>  			void *alignf_data);
>  
> -
> -int pci_register_io_range(phys_addr_t addr, resource_size_t size);
> +int pci_register_io_range(struct fwnode_handle *node, phys_addr_t addr,
> +			  resource_size_t size, unsigned long *port);
>  unsigned long pci_address_to_pio(phys_addr_t addr);
>  phys_addr_t pci_pio_to_address(unsigned long pio);
>  int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr);
> -- 
> 1.9.1
> 

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH V6 4/5] LPC: Support the device-tree LPC host on Hip06/Hip07
  2017-01-30 20:08   ` Alexander Graf
@ 2017-01-31 10:07     ` John Garry
  2017-01-31 11:03       ` Alexander Graf
  2017-02-13 14:39     ` zhichang.yuan
  1 sibling, 1 reply; 37+ messages in thread
From: John Garry @ 2017-01-31 10:07 UTC (permalink / raw)
  To: Alexander Graf, zhichang.yuan, catalin.marinas, will.deacon,
	robh+dt, frowand.list, bhelgaas, rafael, mark.rutland,
	brian.starkey, olof, arnd, linux-arm-kernel
  Cc: lorenzo.pieralisi, benh, linux-kernel, linuxarm, devicetree,
	linux-pci, linux-serial, minyard, liviu.dudau, zourongrong,
	gabriele.paoloni, zhichang.yuan02, kantyzc, xuwei5

On 30/01/2017 20:08, Alexander Graf wrote:
>

Alex,

Thanks for checking.

>
> On 24/01/2017 08:05, zhichang.yuan wrote:
>> The low-pin-count(LPC) interface of Hip06/Hip07 accesses the
>> peripherals in
>> I/O port addresses. This patch implements the LPC host controller
>> driver which
>> perform the I/O operations on the underlying hardware.
>> We don't want to touch those existing peripherals' driver, such as
>> ipmi-bt. So
>> this driver applies the indirect-IO introduced in the previous patch
>> after
>> registering an indirect-IO node to the indirect-IO devices list which
>> will be
>> searched in the I/O accessors.
>> As the I/O translations for LPC children depend on the host I/O
>> registration,
>> we should ensure the host I/O registration is finished before all the LPC
>> children scanning. That is why an arch_init() hook was added in this
>> patch.
>>
>> Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
>> Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
>> ---
>>  .../arm/hisilicon/hisilicon-low-pin-count.txt      |  33 ++
>>  MAINTAINERS                                        |   9 +
>>  arch/arm64/boot/dts/hisilicon/hip06-d03.dts        |   4 +
>>  arch/arm64/boot/dts/hisilicon/hip06.dtsi           |  14 +
>>  drivers/bus/Kconfig                                |   8 +
>>  drivers/bus/Makefile                               |   1 +
>>  drivers/bus/hisi_lpc.c                             | 599
>> +++++++++++++++++++++
>>  7 files changed, 668 insertions(+)
>>  create mode 100644
>> Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
>>
>>  create mode 100644 drivers/bus/hisi_lpc.c
>>
>> diff --git
>> a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
>> b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
>>
>> new file mode 100644
>> index 0000000..213181f
>> --- /dev/null
>> +++
>> b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
>>
>> @@ -0,0 +1,33 @@
>> +Hisilicon Hip06 low-pin-count device
>> +  Hisilicon Hip06 SoCs implement a Low Pin Count (LPC) controller, which
>> +  provides I/O access to some legacy ISA devices.
>> +  Hip06 is based on arm64 architecture where there is no I/O space.
>> So, the
>> +  I/O ports here are not cpu addresses, and there is no 'ranges'
>> property in
>> +  LPC device node.
>> +
>> +Required properties:
>> +- compatible:  value should be as follows:
>> +    (a) "hisilicon,hip06-lpc"
>> +    (b) "hisilicon,hip07-lpc"
>> +- #address-cells: must be 2 which stick to the ISA/EISA binding doc.
>> +- #size-cells: must be 1 which stick to the ISA/EISA binding doc.
>> +- reg: base memory range where the LPC register set is mapped.
>> +
>> +Note:
>> +  The node name before '@' must be "isa" to represent the binding
>> stick to the
>> +  ISA/EISA binding specification.
>> +
>> +Example:
>> +
>> +isa@a01b0000 {
>> +    compatible = "hisilicon,hip06-lpc";
>> +    #address-cells = <2>;
>> +    #size-cells = <1>;
>> +    reg = <0x0 0xa01b0000 0x0 0x1000>;
>> +
>> +    ipmi0: bt@e4 {
>> +        compatible = "ipmi-bt";
>> +        device_type = "ipmi";
>> +        reg = <0x01 0xe4 0x04>;
>> +    };
>> +};
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 26edd83..0153707 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -5855,6 +5855,15 @@ F:    include/uapi/linux/if_hippi.h
>>  F:    net/802/hippi.c
>>  F:    drivers/net/hippi/
>>
>> +HISILICON LPC BUS DRIVER
>> +M:    Zhichang Yuan <yuanzhichang@hisilicon.com>
>> +L:    linux-arm-kernel@lists.infradead.org
>> +W:    http://www.hisilicon.com
>> +S:    Maintained
>> +F:    drivers/bus/hisi_lpc.c
>> +F:    lib/extio.c
>> +F:
>> Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
>>
>> +
>>  HISILICON NETWORK SUBSYSTEM DRIVER
>>  M:    Yisen Zhuang <yisen.zhuang@huawei.com>
>>  M:    Salil Mehta <salil.mehta@huawei.com>
>> diff --git a/arch/arm64/boot/dts/hisilicon/hip06-d03.dts
>> b/arch/arm64/boot/dts/hisilicon/hip06-d03.dts
>> index 7c4114a..75b2b5c 100644
>> --- a/arch/arm64/boot/dts/hisilicon/hip06-d03.dts
>> +++ b/arch/arm64/boot/dts/hisilicon/hip06-d03.dts
>> @@ -52,3 +52,7 @@
>>  &usb_ehci {
>>      status = "ok";
>>  };
>> +
>> +&ipmi0 {
>> +    status = "ok";
>> +};
>> diff --git a/arch/arm64/boot/dts/hisilicon/hip06.dtsi
>> b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
>> index a049b64..c450f8d 100644
>> --- a/arch/arm64/boot/dts/hisilicon/hip06.dtsi
>> +++ b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
>> @@ -318,6 +318,20 @@
>>          #size-cells = <2>;
>>          ranges;
>>
>> +        isa@a01b0000 {
>> +            compatible = "hisilicon,hip06-lpc";
>> +            #size-cells = <1>;
>> +            #address-cells = <2>;
>> +            reg = <0x0 0xa01b0000 0x0 0x1000>;
>> +
>> +            ipmi0: bt@e4 {
>> +                compatible = "ipmi-bt";
>> +                device_type = "ipmi";
>> +                reg = <0x01 0xe4 0x04>;
>> +                status = "disabled";
>> +            };
>> +        };
>> +
>>          refclk: refclk {
>>              compatible = "fixed-clock";
>>              clock-frequency = <50000000>;
>> diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
>> index b9e8cfc..58cee84 100644
>> --- a/drivers/bus/Kconfig
>> +++ b/drivers/bus/Kconfig
>> @@ -64,6 +64,14 @@ config BRCMSTB_GISB_ARB
>>        arbiter. This driver provides timeout and target abort error
>> handling
>>        and internal bus master decoding.
>>
>> +config HISILICON_LPC
>> +    bool "Workaround for nonstandard ISA I/O space on Hisilicon Hip0X"
>
> It's not a workaround, it's support. Better word it like
>
>   "Support for ISA I/O space on Hisilicon HIP0X"
>

Agreed

>> +    depends on (ARM64 && ARCH_HISI && PCI) || COMPILE_TEST
>> +    select INDIRECT_PIO
>> +    help
>> +      Driver needed for some legacy ISA devices attached to
>> Low-Pin-Count
>> +      on Hisilicon Hip0X SoC.
>> +
>>  config IMX_WEIM
>>      bool "Freescale EIM DRIVER"
>>      depends on ARCH_MXC
>> diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
>> index cc6364b..28e3862 100644
>> --- a/drivers/bus/Makefile
>> +++ b/drivers/bus/Makefile
>> @@ -7,6 +7,7 @@ obj-$(CONFIG_ARM_CCI)        += arm-cci.o
>>  obj-$(CONFIG_ARM_CCN)        += arm-ccn.o
>>
>>  obj-$(CONFIG_BRCMSTB_GISB_ARB)    += brcmstb_gisb.o
>> +obj-$(CONFIG_HISILICON_LPC)    += hisi_lpc.o
>>  obj-$(CONFIG_IMX_WEIM)        += imx-weim.o
>>  obj-$(CONFIG_MIPS_CDMM)        += mips_cdmm.o
>>  obj-$(CONFIG_MVEBU_MBUS)     += mvebu-mbus.o
>> diff --git a/drivers/bus/hisi_lpc.c b/drivers/bus/hisi_lpc.c
>> new file mode 100644
>> index 0000000..a96e384
>> --- /dev/null
>> +++ b/drivers/bus/hisi_lpc.c
>> @@ -0,0 +1,599 @@
>> +/*
>> + * Copyright (C) 2016 Hisilicon Limited, All Rights Reserved.
>> + * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
>> + * Author: Zou Rongrong <zourongrong@huawei.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/acpi.h>
>> +#include <linux/console.h>
>> +#include <linux/delay.h>
>> +#include <linux/io.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_address.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/pci.h>
>> +#include <linux/serial_8250.h>
>> +#include <linux/slab.h>
>> +
>> +/*
>> + * Setting this bit means each IO operation will target to a
>> + * different port address:
>> + * 0 means repeatedly IO operations will stick on the same port,
>> + * such as BT;
>> + */
>> +#define FG_INCRADDR_LPC        0x02
>> +
>> +struct lpc_cycle_para {
>> +    unsigned int opflags;
>> +    unsigned int csize; /* the data length of each operation */
>> +};
>> +
>> +struct hisilpc_dev {
>> +    spinlock_t cycle_lock;
>> +    void __iomem  *membase;
>> +    struct extio_node *extio;
>> +};
>> +
>> +/* bounds of the LPC bus address range */
>> +#define LPC_MIN_BUS_RANGE    0x0
>> +
>> +/*
>> + * The maximal IO size for each leagcy bus.
>
> legacy?
>
> I don't really understand why this bus is legacy though. It looks like a
> simple MMIO-to-LPC bridge to me.
>

I think that he means legacy ISA.

>> + * The port size of legacy I/O devices is normally less than 0x400.
>> + * Defining the I/O range size as 0x400 here should be sufficient for
>> + * all peripherals under one bus.
>> + */
>
> This comment doesn't make a lot of sense. What is the limit? Is there a
> hardware limit?
>
> We don't dynamically allocate devices on the lpc bus, so why imply a
> limit at all?
>

IIRC from previously asking Zhichang this before, this is the upper 
range we can address devices on the LPC bus. But the value was 0x1000 then.

>> +#define LPC_BUS_IO_SIZE        0x400
>> +
>> +/* The maximum continuous operations */
>> +#define LPC_MAX_OPCNT    16
>> +/* only support IO data unit length is four at maximum */
>> +#define LPC_MAX_DULEN    4
>> +#if LPC_MAX_DULEN > LPC_MAX_OPCNT
>> +#error "LPC.. MAX_DULEN must be not bigger than MAX_OPCNT!"
>> +#endif
>> +
>> +#define LPC_REG_START        0x00 /* start a new LPC cycle */
>> +#define LPC_REG_OP_STATUS    0x04 /* the current LPC status */
>> +#define LPC_REG_IRQ_ST        0x08 /* interrupt enable&status */
>> +#define LPC_REG_OP_LEN        0x10 /* how many LPC cycles each start */
>> +#define LPC_REG_CMD        0x14 /* command for the required LPC cycle */
>> +#define LPC_REG_ADDR        0x20 /* LPC target address */
>> +#define LPC_REG_WDATA        0x24 /* data to be written */
>> +#define LPC_REG_RDATA        0x28 /* data coming from peer */
>> +
>> +
>> +/* The command register fields */
>> +#define LPC_CMD_SAMEADDR    0x08
>> +#define LPC_CMD_TYPE_IO        0x00
>> +#define LPC_CMD_WRITE        0x01
>> +#define LPC_CMD_READ        0x00
>> +/* the bit attribute is W1C. 1 represents OK. */
>> +#define LPC_STAT_BYIRQ        0x02
>> +
>> +#define LPC_STATUS_IDLE        0x01
>> +#define LPC_OP_FINISHED        0x02
>> +
>> +#define START_WORK        0x01
>> +
>> +/*
>> + * The minimal waiting interval... Suggest it is not less than 10.
>> + * Bigger value probably will lower the performance.
>
> Are you sure you want this comment to be upstream? :)
>

We will remove or refine

>> + */
>> +#define LPC_NSEC_PERWAIT    100
>> +/*
>> + * The maximum waiting time is about 128us.
>> + * The fastest IO cycle time is about 390ns, but the worst case will
>> wait
>> + * for extra 256 lpc clocks, so (256 + 13) * 30ns = 8 us. The maximum
>> + * burst cycles is 16. So, the maximum waiting time is about 128us under
>> + * worst case.
>> + * choose 1300 as the maximum.
>> + */
>> +#define LPC_MAX_WAITCNT        1300
>> +/* About 10us. This is specific for single IO operation, such as inb. */
>> +#define LPC_PEROP_WAITCNT    100
>> +
>> +
>> +static inline int wait_lpc_idle(unsigned char *mbase,
>
> No need to specify inline.
>

Agreed

>> +                unsigned int waitcnt) {
>> +    u32 opstatus;
>> +
>> +    while (waitcnt--) {
>> +        ndelay(LPC_NSEC_PERWAIT);
>> +        opstatus = readl(mbase + LPC_REG_OP_STATUS);
>> +        if (opstatus & LPC_STATUS_IDLE)
>> +            return (opstatus & LPC_OP_FINISHED) ? 0 : (-EIO);
>
> It's a shame we have to busy loop, but I guess no calling code outside
> is prepared for rescheduling at this point.
>
>> +    }
>> +    return -ETIME;
>> +}
>> +
>> +/*
>> + * hisilpc_target_in - trigger a series of lpc cycles to read
>> required data
>> + *               from target peripheral.
>> + * @pdev: pointer to hisi lpc device
>> + * @para: some parameters used to control the lpc I/O operations
>> + * @ptaddr: the lpc I/O target port address
>> + * @buf: where the read back data is stored
>> + * @opcnt: how many I/O operations required in this calling
>> + *
>> + * Only one byte data is read each I/O operation.
>> + *
>> + * Returns 0 on success, non-zero on fail.
>> + *
>> + */
>> +static int
>> +hisilpc_target_in(struct hisilpc_dev *lpcdev, struct lpc_cycle_para
>> *para,
>> +          unsigned long ptaddr, unsigned char *buf,
>> +          unsigned long opcnt)
>> +{
>> +    unsigned long cnt_per_trans;
>> +    unsigned int cmd_word;
>> +    unsigned int waitcnt;
>> +    int ret;
>> +
>> +    if (!buf || !opcnt || !para || !para->csize || !lpcdev)
>> +        return -EINVAL;
>> +
>> +    if (opcnt  > LPC_MAX_OPCNT)
>> +        return -EINVAL;
>> +
>> +    cmd_word = LPC_CMD_TYPE_IO | LPC_CMD_READ;
>> +    waitcnt = LPC_PEROP_WAITCNT;
>> +    if (!(para->opflags & FG_INCRADDR_LPC)) {
>> +        cmd_word |= LPC_CMD_SAMEADDR;
>> +        waitcnt = LPC_MAX_WAITCNT;
>> +    }
>> +
>> +    ret = 0;
>> +    cnt_per_trans = (para->csize == 1) ? opcnt : para->csize;
>> +    for (; opcnt && !ret; cnt_per_trans = para->csize) {
>> +        unsigned long flags;
>> +
>> +        /* whole operation must be atomic */
>> +        spin_lock_irqsave(&lpcdev->cycle_lock, flags);
>
> Ouch. This is going to kill your RT jitter. Is there no better way?
>

Obviously the bus register driving is non-atomic, so we need some way to 
lock out.

I think that it is not so critical for low-speed/infrequent-access bus.

If we were going to use virtual UART in the BMC on the LPC bus then we 
could consider more.

>> +
>> +        writel(cnt_per_trans, lpcdev->membase + LPC_REG_OP_LEN);
>> +
>> +        writel(cmd_word, lpcdev->membase + LPC_REG_CMD);
>> +
>> +        writel(ptaddr, lpcdev->membase + LPC_REG_ADDR);
>> +
>> +        writel(START_WORK, lpcdev->membase + LPC_REG_START);
>> +
>> +        /* whether the operation is finished */
>> +        ret = wait_lpc_idle(lpcdev->membase, waitcnt);
>> +        if (!ret) {
>> +            opcnt -= cnt_per_trans;
>> +            for (; cnt_per_trans--; buf++)
>> +                *buf = readl(lpcdev->membase + LPC_REG_RDATA);
>> +        }
>> +
>> +        spin_unlock_irqrestore(&lpcdev->cycle_lock, flags);
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +/**
>> + * hisilpc_target_out - trigger a series of lpc cycles to write required
>> + *            data to target peripheral.
>> + * @pdev: pointer to hisi lpc device
>> + * @para: some parameters used to control the lpc I/O operations
>> + * @ptaddr: the lpc I/O target port address
>> + * @buf: where the data to be written is stored
>> + * @opcnt: how many I/O operations required
>> + *
>> + * Only one byte data is read each I/O operation.
>> + *
>> + * Returns 0 on success, non-zero on fail.
>> + *
>> + */
>> +static int
>> +hisilpc_target_out(struct hisilpc_dev *lpcdev, struct lpc_cycle_para
>> *para,
>> +           unsigned long ptaddr, const unsigned char *buf,
>> +           unsigned long opcnt)
>> +{
>> +    unsigned long cnt_per_trans;
>> +    unsigned int cmd_word;
>> +    unsigned int waitcnt;
>> +    int ret;
>> +
>> +    if (!buf || !opcnt || !para || !lpcdev)
>> +        return -EINVAL;
>> +
>> +    if (opcnt > LPC_MAX_OPCNT)
>> +        return -EINVAL;
>> +    /* default is increasing address */
>> +    cmd_word = LPC_CMD_TYPE_IO | LPC_CMD_WRITE;
>> +    waitcnt = LPC_PEROP_WAITCNT;
>> +    if (!(para->opflags & FG_INCRADDR_LPC)) {
>> +        cmd_word |= LPC_CMD_SAMEADDR;
>> +        waitcnt = LPC_MAX_WAITCNT;
>> +    }
>> +
>> +    ret = 0;
>> +    cnt_per_trans = (para->csize == 1) ? opcnt : para->csize;
>> +    for (; opcnt && !ret; cnt_per_trans = para->csize) {
>> +        unsigned long flags;
>> +
>> +        spin_lock_irqsave(&lpcdev->cycle_lock, flags);
>
> Same thing here
>

As above

>> +
>> +        writel(cnt_per_trans, lpcdev->membase + LPC_REG_OP_LEN);
>> +        opcnt -= cnt_per_trans;
>> +        for (; cnt_per_trans--; buf++)
>> +            writel(*buf, lpcdev->membase + LPC_REG_WDATA);
>> +
>> +        writel(cmd_word, lpcdev->membase + LPC_REG_CMD);
>> +
>> +        writel(ptaddr, lpcdev->membase + LPC_REG_ADDR);
>> +
>> +        writel(START_WORK, lpcdev->membase + LPC_REG_START);
>> +
>> +        /* whether the operation is finished */
>> +        ret = wait_lpc_idle(lpcdev->membase, waitcnt);
>> +
>> +        spin_unlock_irqrestore(&lpcdev->cycle_lock, flags);
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +static inline unsigned long
>
> Don't explicitly mention inline, the compiler will figure that out for you.
>

Sure

>> +hisi_lpc_pio_to_addr(struct hisilpc_dev *lpcdev, unsigned long pio)
>> +{
>> +    return pio - lpcdev->extio->io_start + lpcdev->extio->bus_start;
>> +}
>> +
>> +
>> +/**
>> + * hisilpc_comm_in - read/input the data from the I/O peripheral
>> + *             through LPC.
>> + * @devobj: pointer to the device information relevant to LPC
>> controller.
>> + * @pio: the target I/O port address.
>> + * @dlen: the data length required to read from the target I/O port.
>> + *
>> + * when succeed, the data read back is stored in buffer pointed by
>> inbuf.
>> + * For inb, return the data read from I/O or -1 when error occur.
>> + */
>> +static u64 hisilpc_comm_in(void *devobj, unsigned long pio, size_t dlen)
>> +{
>> +    struct hisilpc_dev *lpcdev = devobj;
>> +    struct lpc_cycle_para iopara;
>> +    u32 rd_data;
>
> rd_data needs to be initialized to 0. Otherwise it may contain stale
> stack contents and corrupt non-32bit dlen returns.
>

I think so, since we read into this value byte-by-byte. We also seem to 
return a 32b value but should return 64b value according to the prototype.

>> +    unsigned char *newbuf;
>> +    int ret = 0;
>> +    unsigned long ptaddr;
>> +
>> +    if (!lpcdev || !dlen || dlen > LPC_MAX_DULEN ||    (dlen & (dlen
>> - 1)))
>> +        return -1;
>
> Isn't this -EINVAL?

Not sure. This value is returned directly to the inb/outb caller, which 
would not check this value for error.

It could be argued that the checking is paranoia. If not, we should 
treat the failure as a more severe event.

>
>> +
>> +    /* the local buffer must be enough for one data unit */
>> +    if (sizeof(rd_data) < dlen)
>> +        return -1;
>
> Same here.
>
> Also, the above seems a very convoluted way of saying
>
>   switch (dlen) {
>   case 1:
>   case 2:
>   case 4:

It's better

>     break;
>   default:
>     return -EINVAL;
>   }
>
> But I guess the way you write it doesn't hurt ;)
>
>
> Alex
>
> .
>



^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH V6 4/5] LPC: Support the device-tree LPC host on Hip06/Hip07
  2017-01-31 10:07     ` John Garry
@ 2017-01-31 11:03       ` Alexander Graf
  2017-01-31 11:49         ` John Garry
  2017-01-31 11:51         ` Gabriele Paoloni
  0 siblings, 2 replies; 37+ messages in thread
From: Alexander Graf @ 2017-01-31 11:03 UTC (permalink / raw)
  To: John Garry, zhichang.yuan, catalin.marinas, will.deacon, robh+dt,
	frowand.list, bhelgaas, rafael, mark.rutland, brian.starkey,
	olof, arnd, linux-arm-kernel
  Cc: devicetree, lorenzo.pieralisi, gabriele.paoloni, minyard, benh,
	zhichang.yuan02, liviu.dudau, linux-kernel, xuwei5, linuxarm,
	linux-serial, linux-pci, zourongrong, kantyzc



On 31/01/2017 11:07, John Garry wrote:
> On 30/01/2017 20:08, Alexander Graf wrote:
>>
>
> Alex,
>
> Thanks for checking.
>
>>
>> On 24/01/2017 08:05, zhichang.yuan wrote:
>>> The low-pin-count(LPC) interface of Hip06/Hip07 accesses the
>>> peripherals in
>>> I/O port addresses. This patch implements the LPC host controller
>>> driver which
>>> perform the I/O operations on the underlying hardware.
>>> We don't want to touch those existing peripherals' driver, such as
>>> ipmi-bt. So
>>> this driver applies the indirect-IO introduced in the previous patch
>>> after
>>> registering an indirect-IO node to the indirect-IO devices list which
>>> will be
>>> searched in the I/O accessors.
>>> As the I/O translations for LPC children depend on the host I/O
>>> registration,
>>> we should ensure the host I/O registration is finished before all the
>>> LPC
>>> children scanning. That is why an arch_init() hook was added in this
>>> patch.
>>>
>>> Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
>>> Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
>>> ---
>>>  .../arm/hisilicon/hisilicon-low-pin-count.txt      |  33 ++
>>>  MAINTAINERS                                        |   9 +
>>>  arch/arm64/boot/dts/hisilicon/hip06-d03.dts        |   4 +
>>>  arch/arm64/boot/dts/hisilicon/hip06.dtsi           |  14 +
>>>  drivers/bus/Kconfig                                |   8 +
>>>  drivers/bus/Makefile                               |   1 +
>>>  drivers/bus/hisi_lpc.c                             | 599
>>> +++++++++++++++++++++
>>>  7 files changed, 668 insertions(+)
>>>  create mode 100644
>>> Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
>>>
>>>
>>>  create mode 100644 drivers/bus/hisi_lpc.c
>>>
>>> diff --git
>>> a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
>>>
>>> b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
>>>
>>>
>>> new file mode 100644
>>> index 0000000..213181f
>>> --- /dev/null
>>> +++
>>> b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
>>>
>>>
>>> @@ -0,0 +1,33 @@
>>> +Hisilicon Hip06 low-pin-count device
>>> +  Hisilicon Hip06 SoCs implement a Low Pin Count (LPC) controller,
>>> which
>>> +  provides I/O access to some legacy ISA devices.
>>> +  Hip06 is based on arm64 architecture where there is no I/O space.
>>> So, the
>>> +  I/O ports here are not cpu addresses, and there is no 'ranges'
>>> property in
>>> +  LPC device node.
>>> +
>>> +Required properties:
>>> +- compatible:  value should be as follows:
>>> +    (a) "hisilicon,hip06-lpc"
>>> +    (b) "hisilicon,hip07-lpc"
>>> +- #address-cells: must be 2 which stick to the ISA/EISA binding doc.
>>> +- #size-cells: must be 1 which stick to the ISA/EISA binding doc.
>>> +- reg: base memory range where the LPC register set is mapped.
>>> +
>>> +Note:
>>> +  The node name before '@' must be "isa" to represent the binding
>>> stick to the
>>> +  ISA/EISA binding specification.
>>> +
>>> +Example:
>>> +
>>> +isa@a01b0000 {
>>> +    compatible = "hisilicon,hip06-lpc";
>>> +    #address-cells = <2>;
>>> +    #size-cells = <1>;
>>> +    reg = <0x0 0xa01b0000 0x0 0x1000>;
>>> +
>>> +    ipmi0: bt@e4 {
>>> +        compatible = "ipmi-bt";
>>> +        device_type = "ipmi";
>>> +        reg = <0x01 0xe4 0x04>;
>>> +    };
>>> +};
>>> diff --git a/MAINTAINERS b/MAINTAINERS
>>> index 26edd83..0153707 100644
>>> --- a/MAINTAINERS
>>> +++ b/MAINTAINERS
>>> @@ -5855,6 +5855,15 @@ F:    include/uapi/linux/if_hippi.h
>>>  F:    net/802/hippi.c
>>>  F:    drivers/net/hippi/
>>>
>>> +HISILICON LPC BUS DRIVER
>>> +M:    Zhichang Yuan <yuanzhichang@hisilicon.com>
>>> +L:    linux-arm-kernel@lists.infradead.org
>>> +W:    http://www.hisilicon.com
>>> +S:    Maintained
>>> +F:    drivers/bus/hisi_lpc.c
>>> +F:    lib/extio.c
>>> +F:
>>> Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
>>>
>>>
>>> +
>>>  HISILICON NETWORK SUBSYSTEM DRIVER
>>>  M:    Yisen Zhuang <yisen.zhuang@huawei.com>
>>>  M:    Salil Mehta <salil.mehta@huawei.com>
>>> diff --git a/arch/arm64/boot/dts/hisilicon/hip06-d03.dts
>>> b/arch/arm64/boot/dts/hisilicon/hip06-d03.dts
>>> index 7c4114a..75b2b5c 100644
>>> --- a/arch/arm64/boot/dts/hisilicon/hip06-d03.dts
>>> +++ b/arch/arm64/boot/dts/hisilicon/hip06-d03.dts
>>> @@ -52,3 +52,7 @@
>>>  &usb_ehci {
>>>      status = "ok";
>>>  };
>>> +
>>> +&ipmi0 {
>>> +    status = "ok";
>>> +};
>>> diff --git a/arch/arm64/boot/dts/hisilicon/hip06.dtsi
>>> b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
>>> index a049b64..c450f8d 100644
>>> --- a/arch/arm64/boot/dts/hisilicon/hip06.dtsi
>>> +++ b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
>>> @@ -318,6 +318,20 @@
>>>          #size-cells = <2>;
>>>          ranges;
>>>
>>> +        isa@a01b0000 {
>>> +            compatible = "hisilicon,hip06-lpc";
>>> +            #size-cells = <1>;
>>> +            #address-cells = <2>;
>>> +            reg = <0x0 0xa01b0000 0x0 0x1000>;
>>> +
>>> +            ipmi0: bt@e4 {
>>> +                compatible = "ipmi-bt";
>>> +                device_type = "ipmi";
>>> +                reg = <0x01 0xe4 0x04>;
>>> +                status = "disabled";
>>> +            };
>>> +        };
>>> +
>>>          refclk: refclk {
>>>              compatible = "fixed-clock";
>>>              clock-frequency = <50000000>;
>>> diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
>>> index b9e8cfc..58cee84 100644
>>> --- a/drivers/bus/Kconfig
>>> +++ b/drivers/bus/Kconfig
>>> @@ -64,6 +64,14 @@ config BRCMSTB_GISB_ARB
>>>        arbiter. This driver provides timeout and target abort error
>>> handling
>>>        and internal bus master decoding.
>>>
>>> +config HISILICON_LPC
>>> +    bool "Workaround for nonstandard ISA I/O space on Hisilicon Hip0X"
>>
>> It's not a workaround, it's support. Better word it like
>>
>>   "Support for ISA I/O space on Hisilicon HIP0X"
>>
>
> Agreed
>
>>> +    depends on (ARM64 && ARCH_HISI && PCI) || COMPILE_TEST
>>> +    select INDIRECT_PIO
>>> +    help
>>> +      Driver needed for some legacy ISA devices attached to
>>> Low-Pin-Count
>>> +      on Hisilicon Hip0X SoC.
>>> +
>>>  config IMX_WEIM
>>>      bool "Freescale EIM DRIVER"
>>>      depends on ARCH_MXC
>>> diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
>>> index cc6364b..28e3862 100644
>>> --- a/drivers/bus/Makefile
>>> +++ b/drivers/bus/Makefile
>>> @@ -7,6 +7,7 @@ obj-$(CONFIG_ARM_CCI)        += arm-cci.o
>>>  obj-$(CONFIG_ARM_CCN)        += arm-ccn.o
>>>
>>>  obj-$(CONFIG_BRCMSTB_GISB_ARB)    += brcmstb_gisb.o
>>> +obj-$(CONFIG_HISILICON_LPC)    += hisi_lpc.o
>>>  obj-$(CONFIG_IMX_WEIM)        += imx-weim.o
>>>  obj-$(CONFIG_MIPS_CDMM)        += mips_cdmm.o
>>>  obj-$(CONFIG_MVEBU_MBUS)     += mvebu-mbus.o
>>> diff --git a/drivers/bus/hisi_lpc.c b/drivers/bus/hisi_lpc.c
>>> new file mode 100644
>>> index 0000000..a96e384
>>> --- /dev/null
>>> +++ b/drivers/bus/hisi_lpc.c
>>> @@ -0,0 +1,599 @@
>>> +/*
>>> + * Copyright (C) 2016 Hisilicon Limited, All Rights Reserved.
>>> + * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
>>> + * Author: Zou Rongrong <zourongrong@huawei.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/acpi.h>
>>> +#include <linux/console.h>
>>> +#include <linux/delay.h>
>>> +#include <linux/io.h>
>>> +#include <linux/module.h>
>>> +#include <linux/of.h>
>>> +#include <linux/of_address.h>
>>> +#include <linux/of_platform.h>
>>> +#include <linux/pci.h>
>>> +#include <linux/serial_8250.h>
>>> +#include <linux/slab.h>
>>> +
>>> +/*
>>> + * Setting this bit means each IO operation will target to a
>>> + * different port address:
>>> + * 0 means repeatedly IO operations will stick on the same port,
>>> + * such as BT;
>>> + */
>>> +#define FG_INCRADDR_LPC        0x02
>>> +
>>> +struct lpc_cycle_para {
>>> +    unsigned int opflags;
>>> +    unsigned int csize; /* the data length of each operation */
>>> +};
>>> +
>>> +struct hisilpc_dev {
>>> +    spinlock_t cycle_lock;
>>> +    void __iomem  *membase;
>>> +    struct extio_node *extio;
>>> +};
>>> +
>>> +/* bounds of the LPC bus address range */
>>> +#define LPC_MIN_BUS_RANGE    0x0
>>> +
>>> +/*
>>> + * The maximal IO size for each leagcy bus.
>>
>> legacy?
>>
>> I don't really understand why this bus is legacy though. It looks like a
>> simple MMIO-to-LPC bridge to me.
>>
>
> I think that he means legacy ISA.
>
>>> + * The port size of legacy I/O devices is normally less than 0x400.
>>> + * Defining the I/O range size as 0x400 here should be sufficient for
>>> + * all peripherals under one bus.
>>> + */
>>
>> This comment doesn't make a lot of sense. What is the limit? Is there a
>> hardware limit?
>>
>> We don't dynamically allocate devices on the lpc bus, so why imply a
>> limit at all?
>>
>
> IIRC from previously asking Zhichang this before, this is the upper
> range we can address devices on the LPC bus. But the value was 0x1000 then.

Well, all devices that we want to address are defined by firmware (via 
device tree or dsdt). So I'm not quite sure what this arbitrary limit 
buys us.

>
>>> +#define LPC_BUS_IO_SIZE        0x400
>>> +
>>> +/* The maximum continuous operations */
>>> +#define LPC_MAX_OPCNT    16
>>> +/* only support IO data unit length is four at maximum */
>>> +#define LPC_MAX_DULEN    4
>>> +#if LPC_MAX_DULEN > LPC_MAX_OPCNT
>>> +#error "LPC.. MAX_DULEN must be not bigger than MAX_OPCNT!"
>>> +#endif
>>> +
>>> +#define LPC_REG_START        0x00 /* start a new LPC cycle */
>>> +#define LPC_REG_OP_STATUS    0x04 /* the current LPC status */
>>> +#define LPC_REG_IRQ_ST        0x08 /* interrupt enable&status */
>>> +#define LPC_REG_OP_LEN        0x10 /* how many LPC cycles each start */
>>> +#define LPC_REG_CMD        0x14 /* command for the required LPC
>>> cycle */
>>> +#define LPC_REG_ADDR        0x20 /* LPC target address */
>>> +#define LPC_REG_WDATA        0x24 /* data to be written */
>>> +#define LPC_REG_RDATA        0x28 /* data coming from peer */
>>> +
>>> +
>>> +/* The command register fields */
>>> +#define LPC_CMD_SAMEADDR    0x08
>>> +#define LPC_CMD_TYPE_IO        0x00
>>> +#define LPC_CMD_WRITE        0x01
>>> +#define LPC_CMD_READ        0x00
>>> +/* the bit attribute is W1C. 1 represents OK. */
>>> +#define LPC_STAT_BYIRQ        0x02
>>> +
>>> +#define LPC_STATUS_IDLE        0x01
>>> +#define LPC_OP_FINISHED        0x02
>>> +
>>> +#define START_WORK        0x01
>>> +
>>> +/*
>>> + * The minimal waiting interval... Suggest it is not less than 10.
>>> + * Bigger value probably will lower the performance.
>>
>> Are you sure you want this comment to be upstream? :)
>>
>
> We will remove or refine
>
>>> + */
>>> +#define LPC_NSEC_PERWAIT    100
>>> +/*
>>> + * The maximum waiting time is about 128us.
>>> + * The fastest IO cycle time is about 390ns, but the worst case will
>>> wait
>>> + * for extra 256 lpc clocks, so (256 + 13) * 30ns = 8 us. The maximum
>>> + * burst cycles is 16. So, the maximum waiting time is about 128us
>>> under
>>> + * worst case.
>>> + * choose 1300 as the maximum.
>>> + */
>>> +#define LPC_MAX_WAITCNT        1300
>>> +/* About 10us. This is specific for single IO operation, such as
>>> inb. */
>>> +#define LPC_PEROP_WAITCNT    100
>>> +
>>> +
>>> +static inline int wait_lpc_idle(unsigned char *mbase,
>>
>> No need to specify inline.
>>
>
> Agreed
>
>>> +                unsigned int waitcnt) {
>>> +    u32 opstatus;
>>> +
>>> +    while (waitcnt--) {
>>> +        ndelay(LPC_NSEC_PERWAIT);
>>> +        opstatus = readl(mbase + LPC_REG_OP_STATUS);
>>> +        if (opstatus & LPC_STATUS_IDLE)
>>> +            return (opstatus & LPC_OP_FINISHED) ? 0 : (-EIO);
>>
>> It's a shame we have to busy loop, but I guess no calling code outside
>> is prepared for rescheduling at this point.
>>
>>> +    }
>>> +    return -ETIME;
>>> +}
>>> +
>>> +/*
>>> + * hisilpc_target_in - trigger a series of lpc cycles to read
>>> required data
>>> + *               from target peripheral.
>>> + * @pdev: pointer to hisi lpc device
>>> + * @para: some parameters used to control the lpc I/O operations
>>> + * @ptaddr: the lpc I/O target port address
>>> + * @buf: where the read back data is stored
>>> + * @opcnt: how many I/O operations required in this calling
>>> + *
>>> + * Only one byte data is read each I/O operation.
>>> + *
>>> + * Returns 0 on success, non-zero on fail.
>>> + *
>>> + */
>>> +static int
>>> +hisilpc_target_in(struct hisilpc_dev *lpcdev, struct lpc_cycle_para
>>> *para,
>>> +          unsigned long ptaddr, unsigned char *buf,
>>> +          unsigned long opcnt)
>>> +{
>>> +    unsigned long cnt_per_trans;
>>> +    unsigned int cmd_word;
>>> +    unsigned int waitcnt;
>>> +    int ret;
>>> +
>>> +    if (!buf || !opcnt || !para || !para->csize || !lpcdev)
>>> +        return -EINVAL;
>>> +
>>> +    if (opcnt  > LPC_MAX_OPCNT)
>>> +        return -EINVAL;
>>> +
>>> +    cmd_word = LPC_CMD_TYPE_IO | LPC_CMD_READ;
>>> +    waitcnt = LPC_PEROP_WAITCNT;
>>> +    if (!(para->opflags & FG_INCRADDR_LPC)) {
>>> +        cmd_word |= LPC_CMD_SAMEADDR;
>>> +        waitcnt = LPC_MAX_WAITCNT;
>>> +    }
>>> +
>>> +    ret = 0;
>>> +    cnt_per_trans = (para->csize == 1) ? opcnt : para->csize;
>>> +    for (; opcnt && !ret; cnt_per_trans = para->csize) {
>>> +        unsigned long flags;
>>> +
>>> +        /* whole operation must be atomic */
>>> +        spin_lock_irqsave(&lpcdev->cycle_lock, flags);
>>
>> Ouch. This is going to kill your RT jitter. Is there no better way?
>>
>
> Obviously the bus register driving is non-atomic, so we need some way to
> lock out.
>
> I think that it is not so critical for low-speed/infrequent-access bus.
>
> If we were going to use virtual UART in the BMC on the LPC bus then we
> could consider more.

Well, it basically means that an arbitrary daemon running in user space 
that checks your temperature readings via the ipmi interface could 
create a lot of jitter. That could be very critical if you want to use 
this hardware for real time critical applications, such as telecom.

I bet that if you leave it like that and postpone the decision to fix it 
to "later", in 1 or 2 years you will cause someone weeks of debugging to 
track down why their voip gateway loses packets from time to time.

>
>>> +
>>> +        writel(cnt_per_trans, lpcdev->membase + LPC_REG_OP_LEN);
>>> +
>>> +        writel(cmd_word, lpcdev->membase + LPC_REG_CMD);
>>> +
>>> +        writel(ptaddr, lpcdev->membase + LPC_REG_ADDR);
>>> +
>>> +        writel(START_WORK, lpcdev->membase + LPC_REG_START);
>>> +
>>> +        /* whether the operation is finished */
>>> +        ret = wait_lpc_idle(lpcdev->membase, waitcnt);
>>> +        if (!ret) {
>>> +            opcnt -= cnt_per_trans;
>>> +            for (; cnt_per_trans--; buf++)
>>> +                *buf = readl(lpcdev->membase + LPC_REG_RDATA);
>>> +        }
>>> +
>>> +        spin_unlock_irqrestore(&lpcdev->cycle_lock, flags);
>>> +    }
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +/**
>>> + * hisilpc_target_out - trigger a series of lpc cycles to write
>>> required
>>> + *            data to target peripheral.
>>> + * @pdev: pointer to hisi lpc device
>>> + * @para: some parameters used to control the lpc I/O operations
>>> + * @ptaddr: the lpc I/O target port address
>>> + * @buf: where the data to be written is stored
>>> + * @opcnt: how many I/O operations required
>>> + *
>>> + * Only one byte data is read each I/O operation.
>>> + *
>>> + * Returns 0 on success, non-zero on fail.
>>> + *
>>> + */
>>> +static int
>>> +hisilpc_target_out(struct hisilpc_dev *lpcdev, struct lpc_cycle_para
>>> *para,
>>> +           unsigned long ptaddr, const unsigned char *buf,
>>> +           unsigned long opcnt)
>>> +{
>>> +    unsigned long cnt_per_trans;
>>> +    unsigned int cmd_word;
>>> +    unsigned int waitcnt;
>>> +    int ret;
>>> +
>>> +    if (!buf || !opcnt || !para || !lpcdev)
>>> +        return -EINVAL;
>>> +
>>> +    if (opcnt > LPC_MAX_OPCNT)
>>> +        return -EINVAL;
>>> +    /* default is increasing address */
>>> +    cmd_word = LPC_CMD_TYPE_IO | LPC_CMD_WRITE;
>>> +    waitcnt = LPC_PEROP_WAITCNT;
>>> +    if (!(para->opflags & FG_INCRADDR_LPC)) {
>>> +        cmd_word |= LPC_CMD_SAMEADDR;
>>> +        waitcnt = LPC_MAX_WAITCNT;
>>> +    }
>>> +
>>> +    ret = 0;
>>> +    cnt_per_trans = (para->csize == 1) ? opcnt : para->csize;
>>> +    for (; opcnt && !ret; cnt_per_trans = para->csize) {
>>> +        unsigned long flags;
>>> +
>>> +        spin_lock_irqsave(&lpcdev->cycle_lock, flags);
>>
>> Same thing here
>>
>
> As above
>
>>> +
>>> +        writel(cnt_per_trans, lpcdev->membase + LPC_REG_OP_LEN);
>>> +        opcnt -= cnt_per_trans;
>>> +        for (; cnt_per_trans--; buf++)
>>> +            writel(*buf, lpcdev->membase + LPC_REG_WDATA);
>>> +
>>> +        writel(cmd_word, lpcdev->membase + LPC_REG_CMD);
>>> +
>>> +        writel(ptaddr, lpcdev->membase + LPC_REG_ADDR);
>>> +
>>> +        writel(START_WORK, lpcdev->membase + LPC_REG_START);
>>> +
>>> +        /* whether the operation is finished */
>>> +        ret = wait_lpc_idle(lpcdev->membase, waitcnt);
>>> +
>>> +        spin_unlock_irqrestore(&lpcdev->cycle_lock, flags);
>>> +    }
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static inline unsigned long
>>
>> Don't explicitly mention inline, the compiler will figure that out for
>> you.
>>
>
> Sure
>
>>> +hisi_lpc_pio_to_addr(struct hisilpc_dev *lpcdev, unsigned long pio)
>>> +{
>>> +    return pio - lpcdev->extio->io_start + lpcdev->extio->bus_start;
>>> +}
>>> +
>>> +
>>> +/**
>>> + * hisilpc_comm_in - read/input the data from the I/O peripheral
>>> + *             through LPC.
>>> + * @devobj: pointer to the device information relevant to LPC
>>> controller.
>>> + * @pio: the target I/O port address.
>>> + * @dlen: the data length required to read from the target I/O port.
>>> + *
>>> + * when succeed, the data read back is stored in buffer pointed by
>>> inbuf.
>>> + * For inb, return the data read from I/O or -1 when error occur.
>>> + */
>>> +static u64 hisilpc_comm_in(void *devobj, unsigned long pio, size_t
>>> dlen)
>>> +{
>>> +    struct hisilpc_dev *lpcdev = devobj;
>>> +    struct lpc_cycle_para iopara;
>>> +    u32 rd_data;
>>
>> rd_data needs to be initialized to 0. Otherwise it may contain stale
>> stack contents and corrupt non-32bit dlen returns.
>>
>
> I think so, since we read into this value byte-by-byte. We also seem to
> return a 32b value but should return 64b value according to the prototype.

IIRC LPC (well, PIO) doesn't support bigger requests than 32bit. At 
least I can't think of an x86 instruction that would allow bigger 
transactions. So there's no need to make it 64bit. However, the question 
is why the prototype is 64bit then. Hm. :)

Maybe the prototype should be only 32bit.

>
>>> +    unsigned char *newbuf;
>>> +    int ret = 0;
>>> +    unsigned long ptaddr;
>>> +
>>> +    if (!lpcdev || !dlen || dlen > LPC_MAX_DULEN ||    (dlen & (dlen
>>> - 1)))
>>> +        return -1;
>>
>> Isn't this -EINVAL?
>
> Not sure. This value is returned directly to the inb/outb caller, which
> would not check this value for error.
>
> It could be argued that the checking is paranoia. If not, we should
> treat the failure as a more severe event.

Oh, I see. In that case -1 makes a lot of sense since it's the default 
read value on x86 for unallocated space.

This probably deserves a comment (and/or maybe a #define)


Alex

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH V6 4/5] LPC: Support the device-tree LPC host on Hip06/Hip07
  2017-01-31 11:03       ` Alexander Graf
@ 2017-01-31 11:49         ` John Garry
  2017-01-31 11:51         ` Gabriele Paoloni
  1 sibling, 0 replies; 37+ messages in thread
From: John Garry @ 2017-01-31 11:49 UTC (permalink / raw)
  To: Alexander Graf, zhichang.yuan, catalin.marinas, will.deacon,
	robh+dt, frowand.list, bhelgaas, rafael, mark.rutland,
	brian.starkey, olof, arnd, linux-arm-kernel
  Cc: devicetree, lorenzo.pieralisi, gabriele.paoloni, minyard, benh,
	zhichang.yuan02, liviu.dudau, linux-kernel, xuwei5, linuxarm,
	linux-serial, linux-pci, zourongrong, kantyzc

>>>> + * The port size of legacy I/O devices is normally less than 0x400.
>>>> + * Defining the I/O range size as 0x400 here should be sufficient for
>>>> + * all peripherals under one bus.
>>>> + */
>>>
>>> This comment doesn't make a lot of sense. What is the limit? Is there a
>>> hardware limit?
>>>
>>> We don't dynamically allocate devices on the lpc bus, so why imply a
>>> limit at all?
>>>
>>
>> IIRC from previously asking Zhichang this before, this is the upper
>> range we can address devices on the LPC bus. But the value was 0x1000
>> then.
>
> Well, all devices that we want to address are defined by firmware (via
> device tree or dsdt). So I'm not quite sure what this arbitrary limit
> buys us.
>

Will check with Zhichang.

>>
>>>> +#define LPC_BUS_IO_SIZE        0x400
>>>> +

<snip>

>>>> +    ret = 0;
>>>> +    cnt_per_trans = (para->csize == 1) ? opcnt : para->csize;
>>>> +    for (; opcnt && !ret; cnt_per_trans = para->csize) {
>>>> +        unsigned long flags;
>>>> +
>>>> +        /* whole operation must be atomic */
>>>> +        spin_lock_irqsave(&lpcdev->cycle_lock, flags);
>>>
>>> Ouch. This is going to kill your RT jitter. Is there no better way?
>>>
>>
>> Obviously the bus register driving is non-atomic, so we need some way to
>> lock out.
>>
>> I think that it is not so critical for low-speed/infrequent-access bus.
>>
>> If we were going to use virtual UART in the BMC on the LPC bus then we
>> could consider more.
>
> Well, it basically means that an arbitrary daemon running in user space
> that checks your temperature readings via the ipmi interface could
> create a lot of jitter. That could be very critical if you want to use
> this hardware for real time critical applications, such as telecom.
>
> I bet that if you leave it like that and postpone the decision to fix it
> to "later", in 1 or 2 years you will cause someone weeks of debugging to
> track down why their voip gateway loses packets from time to time.
>
>>

We need to consider this more. There may a way to access the registers 
and drive the bus without requiring a lock, but I doubt it.

Note: I think that we could make some readl/writel relaxed, which would 
help.

>>>> +
>>>> +        writel(cnt_per_trans, lpcdev->membase + LPC_REG_OP_LEN);
>>>> +
>>>> +        writel(cmd_word, lpcdev->membase + LPC_REG_CMD);
>>>> +
>>>> +        writel(ptaddr, lpcdev->membase + LPC_REG_ADDR);
>>>> +
>>>> +        writel(START_WORK, lpcdev->membase + LPC_REG_START);
>>>> +
>>>> +        /* whether the operation is finished */
>>>> +        ret = wait_lpc_idle(lpcdev->membase, waitcnt);
>>>> +        if (!ret) {
>>>> +            opcnt -= cnt_per_trans;
>>>> +            for (; cnt_per_trans--; buf++)
>>>> +                *buf = readl(lpcdev->membase + LPC_REG_RDATA);
>>>> +        }
>>>> +
>>>> +        spin_unlock_irqrestore(&lpcdev->cycle_lock, flags);
>>>> +    }
>>>> +
>>>> +    return ret;
>>>> +}

<snip>

>>>> + * hisilpc_comm_in - read/input the data from the I/O peripheral
>>>> + *             through LPC.
>>>> + * @devobj: pointer to the device information relevant to LPC
>>>> controller.
>>>> + * @pio: the target I/O port address.
>>>> + * @dlen: the data length required to read from the target I/O port.
>>>> + *
>>>> + * when succeed, the data read back is stored in buffer pointed by
>>>> inbuf.
>>>> + * For inb, return the data read from I/O or -1 when error occur.
>>>> + */
>>>> +static u64 hisilpc_comm_in(void *devobj, unsigned long pio, size_t
>>>> dlen)
>>>> +{
>>>> +    struct hisilpc_dev *lpcdev = devobj;
>>>> +    struct lpc_cycle_para iopara;
>>>> +    u32 rd_data;
>>>
>>> rd_data needs to be initialized to 0. Otherwise it may contain stale
>>> stack contents and corrupt non-32bit dlen returns.
>>>
>>
>> I think so, since we read into this value byte-by-byte. We also seem to
>> return a 32b value but should return 64b value according to the
>> prototype.
>
> IIRC LPC (well, PIO) doesn't support bigger requests than 32bit. At
> least I can't think of an x86 instruction that would allow bigger
> transactions. So there's no need to make it 64bit. However, the question
> is why the prototype is 64bit then. Hm. :)
>
> Maybe the prototype should be only 32bit.
>

Will check with Zhichang.

>>
>>>> +    unsigned char *newbuf;
>>>> +    int ret = 0;
>>>> +    unsigned long ptaddr;
>>>> +
>>>> +    if (!lpcdev || !dlen || dlen > LPC_MAX_DULEN ||    (dlen & (dlen
>>>> - 1)))
>>>> +        return -1;
>>>
>>> Isn't this -EINVAL?
>>
>> Not sure. This value is returned directly to the inb/outb caller, which
>> would not check this value for error.
>>
>> It could be argued that the checking is paranoia. If not, we should
>> treat the failure as a more severe event.
>
> Oh, I see. In that case -1 makes a lot of sense since it's the default
> read value on x86 for unallocated space.
>
> This probably deserves a comment (and/or maybe a #define)
>

We can add a comment

>
> Alex
>
> .
>



_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 37+ messages in thread

* RE: [PATCH V6 4/5] LPC: Support the device-tree LPC host on Hip06/Hip07
  2017-01-31 11:03       ` Alexander Graf
  2017-01-31 11:49         ` John Garry
@ 2017-01-31 11:51         ` Gabriele Paoloni
  1 sibling, 0 replies; 37+ messages in thread
From: Gabriele Paoloni @ 2017-01-31 11:51 UTC (permalink / raw)
  To: Alexander Graf, John Garry, Yuanzhichang, catalin.marinas,
	will.deacon, robh+dt, frowand.list, bhelgaas, rafael,
	mark.rutland, brian.starkey, olof, arnd, linux-arm-kernel
  Cc: devicetree, lorenzo.pieralisi, minyard, benh, zhichang.yuan02,
	liviu.dudau, linux-kernel, xuwei (O),
	Linuxarm, linux-serial, linux-pci, zourongrong, kantyzc

Hi Alex thanks for reviewing

[...]

> >
> >>> + * The port size of legacy I/O devices is normally less than
> 0x400.
> >>> + * Defining the I/O range size as 0x400 here should be sufficient
> for
> >>> + * all peripherals under one bus.
> >>> + */
> >>
> >> This comment doesn't make a lot of sense. What is the limit? Is
> there a
> >> hardware limit?
> >>
> >> We don't dynamically allocate devices on the lpc bus, so why imply a
> >> limit at all?
> >>
> >
> > IIRC from previously asking Zhichang this before, this is the upper
> > range we can address devices on the LPC bus. But the value was 0x1000
> then.
> 
> Well, all devices that we want to address are defined by firmware (via
> device tree or dsdt). So I'm not quite sure what this arbitrary limit
> buys us.

Following a previous discussion with Arnd:
<< We know that the ISA/LPC bus can only have up to 65536 ports,
so you can register all of those, or possibly limit it further to
1024 or 4096 ports, whichever matches the bus implementation. >>
(https://lkml.org/lkml/2016/11/10/95)

We decided to register a fixed IO range for our LPC.
About the specific reason for choosing 0x400 I think Zhichang can
clarify once he's back (he's OOO now)   

> 

[...]

> >>> +
> >>> +        /* whole operation must be atomic */
> >>> +        spin_lock_irqsave(&lpcdev->cycle_lock, flags);
> >>
> >> Ouch. This is going to kill your RT jitter. Is there no better way?
> >>
> >
> > Obviously the bus register driving is non-atomic, so we need some way
> to
> > lock out.
> >
> > I think that it is not so critical for low-speed/infrequent-access
> bus.
> >
> > If we were going to use virtual UART in the BMC on the LPC bus then
> we
> > could consider more.
> 
> Well, it basically means that an arbitrary daemon running in user space
> that checks your temperature readings via the ipmi interface could
> create a lot of jitter. That could be very critical if you want to use
> this hardware for real time critical applications, such as telecom.
> 
> I bet that if you leave it like that and postpone the decision to fix
> it
> to "later", in 1 or 2 years you will cause someone weeks of debugging
> to
> track down why their voip gateway loses packets from time to time.

Thanks for the heads-up. We'll discuss internally after Chinese holidays
to understand if this implementation is compatible with the intended
deployment scenarions 

> 
> >

[...]

> >>> +static u64 hisilpc_comm_in(void *devobj, unsigned long pio, size_t
> >>> dlen)
> >>> +{
> >>> +    struct hisilpc_dev *lpcdev = devobj;
> >>> +    struct lpc_cycle_para iopara;
> >>> +    u32 rd_data;
> >>
> >> rd_data needs to be initialized to 0. Otherwise it may contain stale
> >> stack contents and corrupt non-32bit dlen returns.
> >>
> >
> > I think so, since we read into this value byte-by-byte. We also seem
> to
> > return a 32b value but should return 64b value according to the
> prototype.
> 
> IIRC LPC (well, PIO) doesn't support bigger requests than 32bit. At
> least I can't think of an x86 instruction that would allow bigger
> transactions. So there's no need to make it 64bit. However, the
> question
> is why the prototype is 64bit then. Hm. :)
> 
> Maybe the prototype should be only 32bit.

Maybe you're right :)

Looking at extio.c we never return a 64b value.
I'll double check with Zhichang once he's back...

> 
> >
> >>> +    unsigned char *newbuf;
> >>> +    int ret = 0;
> >>> +    unsigned long ptaddr;
> >>> +
> >>> +    if (!lpcdev || !dlen || dlen > LPC_MAX_DULEN ||    (dlen &
> (dlen
> >>> - 1)))
> >>> +        return -1;
> >>
> >> Isn't this -EINVAL?
> >
> > Not sure. This value is returned directly to the inb/outb caller,
> which
> > would not check this value for error.
> >
> > It could be argued that the checking is paranoia. If not, we should
> > treat the failure as a more severe event.
> 
> Oh, I see. In that case -1 makes a lot of sense since it's the default
> read value on x86 for unallocated space.
> 
> This probably deserves a comment (and/or maybe a #define)
> 
> 
> Alex

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH V6 1/5] LIB: Indirect ISA/LPC port IO introduced
  2017-01-30 17:12   ` Alexander Graf
@ 2017-01-31 13:32     ` John Garry
  2017-01-31 19:37       ` Alexander Graf
  2017-02-13 14:05     ` zhichang.yuan
  1 sibling, 1 reply; 37+ messages in thread
From: John Garry @ 2017-01-31 13:32 UTC (permalink / raw)
  To: Alexander Graf, zhichang.yuan, catalin.marinas, will.deacon,
	robh+dt, frowand.list, bhelgaas, rafael, mark.rutland,
	brian.starkey, olof, arnd, linux-arm-kernel
  Cc: devicetree, lorenzo.pieralisi, gabriele.paoloni, minyard, benh,
	zhichang.yuan02, liviu.dudau, linux-kernel, xuwei5, linuxarm,
	linux-serial, linux-pci, zourongrong, kantyzc

On 30/01/2017 17:12, Alexander Graf wrote:
> On 01/24/2017 08:05 AM, zhichang.yuan wrote:
>> Low-pin-count interface is integrated into some SoCs. The accesses to
>> those
>> peripherals under LPC make use of I/O ports rather than the memory
>> mapped I/O.
>>
>> To drive these devices, this patch introduces a method named indirect-IO.
>> In this method the in/out() accessor in include/asm-generic/io.h will be
>> redefined. When upper layer drivers call in/out() with those known
>> legacy port
>> addresses to access the peripherals, the I/O operations will be routed
>> to the
>> right hooks which are registered specific to the host device, such as
>> LPC.
>> Then the hardware relevant manupulations are finished by the
>> corresponding
>> host.
>>
>> According to the comments on V5, this patch adds a common indirect-IO
>> driver
>> which support this I/O indirection to the generic directory.
>>
>> In the later pathches, some host-relevant drivers are implemented to
>> support
>> the specific I/O hooks and register them.
>> Based on these, the upper layer drivers which depend on in/out() can
>> work well
>> without any extra work or any changes.
>>
>> Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
>> Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
>> Signed-off-by: John Garry <john.garry@huawei.com>
>
> I like the extio idea. That allows us to handle all PIO requests on
> platforms that don't have native PIO support via different routes
> depending on the region they're in. Unfortunately we now we have 2
> frameworks for handling sparse PIO regions: One in extio, one in PCI.
>
> Why don't we just merge the two? Most of the code that has #ifdef
> PCI_IOBASE throughout the code base sounds like an ideal candidate to
> get migrated to extio instead. Then we only have a single framework to
> worry about ...

To be clear, are you suggesting we merge the functionality from 
pci_register_io_range(), pci_pio_to_address(), pci_address_to_pio() into 
extio, so extio manages all PIO? And having a single type of node to 
register PIO ranges, by amalgamating struct extio_node and io_range (as 
Bjorn mentioned)?

It would make sense. We would be somewhat decoupling PIO from PCI.

I think that other architectures, like PPC, and other code would need to 
be fixed up to handle this.

We need to consider all the other challenges/obstacles to this.

>
>> ---
>>   include/asm-generic/io.h |  50 ++++++++++++++++
>>   include/linux/extio.h    |  85 +++++++++++++++++++++++++++
>>   include/linux/io.h       |   1 +
>>   lib/Kconfig              |   8 +++
>>   lib/Makefile             |   2 +
>>   lib/extio.c              | 147
>> +++++++++++++++++++++++++++++++++++++++++++++++ xc>>   create mode 100644 include/linux/extio.h
>>   create mode 100644 lib/extio.c
>>

<snip>

>> + * Copyright (C) 2016 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/io.h>
>> +#include <linux/spinlock.h>
>> +
>> +static LIST_HEAD(extio_dev_list);
>> +static DEFINE_RWLOCK(extio_list_lock);
>
> Why not just make the list an RCU list? Then you don't need read locks.
> We also wouldn't create potential lock contention between devices that
> could easily have parallel PIO operations (say a PCI device and an LPC
> device).
>

OK

>> +
>> +void register_extio(struct extio_node *node)
>> +{
>> +    write_lock(&extio_list_lock);
>> +    list_add_tail(&node->list, &extio_dev_list);
>> +    write_unlock(&extio_list_lock);
>> +}
>> +
>> +static struct extio_node *find_extio_token(unsigned long addr)
>> +{
>> +    struct extio_node *extio_entry;
>> +
>> +    read_lock(&extio_list_lock);
>> +    list_for_each_entry(extio_entry, &extio_dev_list, list) {
>> +        if ((addr < extio_entry->io_start + extio_entry->range_size) &&
>> +            (addr >= extio_entry->io_start))
>> +            break;
>> +    }
>> +    read_unlock(&extio_list_lock);
>> +    return (&extio_entry->list == &extio_dev_list) ? NULL : extio_entry;
>> +}
>> +
>> +struct extio_node *extio_find_node(struct fwnode_handle *node)
>> +{
>> +    struct extio_node *entry;
>> +
>> +    read_lock(&extio_list_lock);
>> +    list_for_each_entry(entry, &extio_dev_list, list) {
>> +        if (entry->fwnode == node)
>> +            break;
>> +    }
>> +    read_unlock(&extio_list_lock);
>> +
>> +    return (&entry->list == &extio_dev_list) ? NULL : entry;
>> +}
>> +
>> +unsigned long extio_translate(struct fwnode_handle *node,
>> +        unsigned long bus_addr)
>> +{
>> +    struct extio_node *entry;
>> +    unsigned long port_id = -1;
>> +
>> +    read_lock(&extio_list_lock);
>> +    list_for_each_entry(entry, &extio_dev_list, list) {
>> +        if (entry->fwnode == node &&
>> +            bus_addr >= entry->bus_start &&
>> +            bus_addr - entry->bus_start < entry->range_size)
>> +            port_id = entry->io_start + bus_addr -
>> +                    entry->bus_start;
>> +    }
>> +    read_unlock(&extio_list_lock);
>> +
>> +    return port_id;
>> +}
>> +
>> +#ifdef PCI_IOBASE
>> +
>> +#define BUILD_EXTIO(bw, type)                        \
>> +type extio_in##bw(unsigned long addr)                    \
>> +{                                    \
>> +    struct extio_node *extio_entry = find_extio_token(addr);    \
>> +                                    \
>> +    if (!extio_entry)                        \
>> +        return read##bw(PCI_IOBASE + addr);            \
>> +    return extio_entry->ops->pfin ?                    \
>> +            extio_entry->ops->pfin(extio_entry->devpara,    \
>> +            addr, sizeof(type)) : -1;            \
>> +}                                    \
>> +                                    \
>> +void extio_out##bw(type value, unsigned long addr)            \
>> +{                                    \
>> +    struct extio_node *extio_entry = find_extio_token(addr);    \
>> +                                    \
>> +    if (!extio_entry)                        \
>> +        write##bw(value, PCI_IOBASE + addr);            \
>
> All of the fallback code would also disappear as a nice side effect of
> making pci pio handling a user of extio :).

Is your idea that PCI IO space will also register accessors, which would 
be the same read{b,w,l}/write{b,w,l}?

>

It would be nice to have a quicker way to so the lookup from address to 
node, as we loop all nodes in find_extio_token() every single time.

>
> Alex
>
Thanks,
John

>
> .
>



_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH V6 1/5] LIB: Indirect ISA/LPC port IO introduced
  2017-01-31  0:09   ` Bjorn Helgaas
@ 2017-01-31 13:34     ` John Garry
  0 siblings, 0 replies; 37+ messages in thread
From: John Garry @ 2017-01-31 13:34 UTC (permalink / raw)
  To: Bjorn Helgaas, zhichang.yuan
  Cc: catalin.marinas, will.deacon, robh+dt, frowand.list, bhelgaas,
	rafael, mark.rutland, brian.starkey, olof, arnd,
	linux-arm-kernel, lorenzo.pieralisi, benh, linux-kernel,
	linuxarm, devicetree, linux-pci, linux-serial, minyard,
	liviu.dudau, zourongrong, gabriele.paoloni, zhichang.yuan02,
	kantyzc, xuwei5

On 31/01/2017 00:09, Bjorn Helgaas wrote:
> On Tue, Jan 24, 2017 at 03:05:21PM +0800, zhichang.yuan wrote:
>> Low-pin-count interface is integrated into some SoCs. The accesses to those
>> peripherals under LPC make use of I/O ports rather than the memory mapped I/O.
>>
>> To drive these devices, this patch introduces a method named indirect-IO.
>
> It's slightly confusing to call this "indirect I/O" and then use
> "extio" for the filename and function prefix.  It'd be nice to use
> related names.

We will consider something more consistent.

>
>> +struct extio_node {
>> +	unsigned long bus_start;	/* bus start address */
>> +	unsigned long io_start;	/* io port token corresponding to bus_start */
>> +	size_t range_size;	/* size of the extio node operating range */
>> +	struct fwnode_handle *fwnode;
>> +	struct list_head list;
>> +	struct extio_ops *ops;	/* ops operating on this node */
>> +	void *devpara;	/* private parameter of the host device */
>> +};
>
> I wish we didn't have both struct io_range and struct extio_node.  It
> seems like they're both sort of trying to do the same thing.  Maybe
> this is the same as what Alex is saying.
>

I think so. I have just replied to Alex regarding this.

> Bjorn
>

Thanks,
John

> .
>

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH V6 2/5] PCI: Adapt pci_register_io_range() for indirect-IO and PCI I/O translation
  2017-01-31  0:10   ` Bjorn Helgaas
@ 2017-01-31 13:39     ` John Garry
  0 siblings, 0 replies; 37+ messages in thread
From: John Garry @ 2017-01-31 13:39 UTC (permalink / raw)
  To: Bjorn Helgaas, zhichang.yuan
  Cc: catalin.marinas, will.deacon, robh+dt, frowand.list, bhelgaas,
	rafael, mark.rutland, brian.starkey, olof, arnd,
	linux-arm-kernel, lorenzo.pieralisi, benh, linux-kernel,
	linuxarm, devicetree, linux-pci, linux-serial, minyard,
	liviu.dudau, zourongrong, gabriele.paoloni, zhichang.yuan02,
	kantyzc, xuwei5

On 31/01/2017 00:10, Bjorn Helgaas wrote:
> On Tue, Jan 24, 2017 at 03:05:22PM +0800, zhichang.yuan wrote:
>> After indirect-IO is introduced, system must can assigned indirect-IO devices
>> with logical I/O ranges which are different from those for PCI I/O devices.
>> Otherwise, I/O accessors can't identify whether the I/O port is for memory
>> mapped I/O or indirect-IO.
>
> Maybe:
>
>   We must assign logical I/O port space for indirect I/O such that the
>   I/O accessors can tell whether a logical I/O port refers to memory-
>   mapped I/O space or indirect I/O space.
>

It's better

>> As current helper, pci_register_io_range(), is used for PCI I/O ranges
>> registration and translation, indirect-IO devices should also apply these
>> helpers to manage the I/O ranges. It will be easy to ensure the assigned
>> logical I/O ranges unique.
>> But for indirect-IO devices, there is no cpu address. The current
>> pci_register_io_range() can not work for this case.
>>
>> This patch makes some changes on the pci_register_io_range() to support the
>> I/O range registration with device's fwnode also. After this, the indirect-IO
>> devices can register the device-local I/O range to system logical I/O and
>> easily perform the translation between device-local I/O range and sytem
>> logical I/O range.
>
>> -int __weak pci_register_io_range(phys_addr_t addr, resource_size_t size)
>> +int __weak pci_register_io_range(struct fwnode_handle *node, phys_addr_t addr,
>> +				 resource_size_t size, unsigned long *port)
>
> Why is this __weak?  It looks like it's been __weak since its
> introduction by 41f8bba7f555 ("of/pci: Add pci_register_io_range() and
> pci_pio_to_address()"), but I don't see any other implementations of
> it.
>
> Can you add a patch that does nothing but make this non-weak?
>

OK

>> +#else
>> +	/*
>> +	 * powerpc and microblaze have their own registration,
>> +	 * just look up the value here
>
> Can you include a pointer to the powerpc and microblaze registration
> code here?  It's conceivable that somebody could generalize this
> enough to support powerpc and microblaze as well.
>

It should be no problem

>> --- a/include/linux/pci.h
>> +++ b/include/linux/pci.h
>> @@ -34,6 +34,9 @@
>>
>>  #include <linux/pci_ids.h>
>>
>> +/* the macro below flags an invalid cpu address
>> + * and is used by IO special hosts              */
>
> s/cpu/CPU/
>

OK

> Use conventional multi-line comment style:
>
> /*
>  * IO_RANGE_IOEXT flags an invalid CPU address ...
>  */
>
>> +#define IO_RANGE_IOEXT (resource_size_t)(-1ull)
>
> And put this close to related things, e.g., pci_register_io_range(),
> instead of just dropping it in at the top of the file.

OK

>
>>  /*
>>   * The PCI interface treats multi-function devices as independent
>>   * devices.  The slot/function address of each device is encoded
>> @@ -1197,8 +1200,8 @@ int __must_check pci_bus_alloc_resource(struct pci_bus *bus,
>>  						  resource_size_t),
>>  			void *alignf_data);
>>
>> -
>> -int pci_register_io_range(phys_addr_t addr, resource_size_t size);
>> +int pci_register_io_range(struct fwnode_handle *node, phys_addr_t addr,
>> +			  resource_size_t size, unsigned long *port);
>>  unsigned long pci_address_to_pio(phys_addr_t addr);
>>  phys_addr_t pci_pio_to_address(unsigned long pio);
>>  int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr);
>> --
>> 1.9.1
>>

Thanks,
John

>
> .
>

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH V6 1/5] LIB: Indirect ISA/LPC port IO introduced
  2017-01-31 13:32     ` John Garry
@ 2017-01-31 19:37       ` Alexander Graf
  2017-02-01 12:29         ` Gabriele Paoloni
  2017-02-13 14:17         ` zhichang.yuan
  0 siblings, 2 replies; 37+ messages in thread
From: Alexander Graf @ 2017-01-31 19:37 UTC (permalink / raw)
  To: John Garry, zhichang.yuan, catalin.marinas, will.deacon, robh+dt,
	frowand.list, bhelgaas, rafael, mark.rutland, brian.starkey,
	olof, arnd, linux-arm-kernel
  Cc: devicetree, lorenzo.pieralisi, gabriele.paoloni, minyard, benh,
	zhichang.yuan02, liviu.dudau, linux-kernel, xuwei5, linuxarm,
	linux-serial, linux-pci, zourongrong, kantyzc



On 31/01/2017 14:32, John Garry wrote:
> On 30/01/2017 17:12, Alexander Graf wrote:
>> On 01/24/2017 08:05 AM, zhichang.yuan wrote:
>>> Low-pin-count interface is integrated into some SoCs. The accesses to
>>> those
>>> peripherals under LPC make use of I/O ports rather than the memory
>>> mapped I/O.
>>>
>>> To drive these devices, this patch introduces a method named
>>> indirect-IO.
>>> In this method the in/out() accessor in include/asm-generic/io.h will be
>>> redefined. When upper layer drivers call in/out() with those known
>>> legacy port
>>> addresses to access the peripherals, the I/O operations will be routed
>>> to the
>>> right hooks which are registered specific to the host device, such as
>>> LPC.
>>> Then the hardware relevant manupulations are finished by the
>>> corresponding
>>> host.
>>>
>>> According to the comments on V5, this patch adds a common indirect-IO
>>> driver
>>> which support this I/O indirection to the generic directory.
>>>
>>> In the later pathches, some host-relevant drivers are implemented to
>>> support
>>> the specific I/O hooks and register them.
>>> Based on these, the upper layer drivers which depend on in/out() can
>>> work well
>>> without any extra work or any changes.
>>>
>>> Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
>>> Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
>>> Signed-off-by: John Garry <john.garry@huawei.com>
>>
>> I like the extio idea. That allows us to handle all PIO requests on
>> platforms that don't have native PIO support via different routes
>> depending on the region they're in. Unfortunately we now we have 2
>> frameworks for handling sparse PIO regions: One in extio, one in PCI.
>>
>> Why don't we just merge the two? Most of the code that has #ifdef
>> PCI_IOBASE throughout the code base sounds like an ideal candidate to
>> get migrated to extio instead. Then we only have a single framework to
>> worry about ...
>
> To be clear, are you suggesting we merge the functionality from
> pci_register_io_range(), pci_pio_to_address(), pci_address_to_pio() into
> extio, so extio manages all PIO?

Yes, I guess so.

> And having a single type of node to
> register PIO ranges, by amalgamating struct extio_node and io_range (as
> Bjorn mentioned)?

I'm not quite sure I follow you here. Basically I think you want a 
generic "non-x86 PIO" framework that PCI just plugs into.

I don't think that necessarily means you want to statically allocate 
regions of that PIO space to separate (pseudo-)devices. Instead, 
everyone shares that space and should be able to fail gracefully if some 
space is already occupied.

> It would make sense. We would be somewhat decoupling PIO from PCI.

Yes :).

> I think that other architectures, like PPC, and other code would need to
> be fixed up to handle this.

I think only PPC, Microblaze and ARM are using this. Grep for 
PCI_IOBASE. It's not that many.

> We need to consider all the other challenges/obstacles to this.

Well, getting our abstraction levels right to me sounds like it's worth 
the obstacles.

>
>>
>>> ---
>>>   include/asm-generic/io.h |  50 ++++++++++++++++
>>>   include/linux/extio.h    |  85 +++++++++++++++++++++++++++
>>>   include/linux/io.h       |   1 +
>>>   lib/Kconfig              |   8 +++
>>>   lib/Makefile             |   2 +
>>>   lib/extio.c              | 147
>>> +++++++++++++++++++++++++++++++++++++++++++++++ xc>>   create mode
>>> 100644 include/linux/extio.h
>>>   create mode 100644 lib/extio.c
>>>
>
> <snip>
>
>>> + * Copyright (C) 2016 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/io.h>
>>> +#include <linux/spinlock.h>
>>> +
>>> +static LIST_HEAD(extio_dev_list);
>>> +static DEFINE_RWLOCK(extio_list_lock);
>>
>> Why not just make the list an RCU list? Then you don't need read locks.
>> We also wouldn't create potential lock contention between devices that
>> could easily have parallel PIO operations (say a PCI device and an LPC
>> device).
>>
>
> OK
>
>>> +
>>> +void register_extio(struct extio_node *node)
>>> +{
>>> +    write_lock(&extio_list_lock);
>>> +    list_add_tail(&node->list, &extio_dev_list);
>>> +    write_unlock(&extio_list_lock);
>>> +}
>>> +
>>> +static struct extio_node *find_extio_token(unsigned long addr)
>>> +{
>>> +    struct extio_node *extio_entry;
>>> +
>>> +    read_lock(&extio_list_lock);
>>> +    list_for_each_entry(extio_entry, &extio_dev_list, list) {
>>> +        if ((addr < extio_entry->io_start + extio_entry->range_size) &&
>>> +            (addr >= extio_entry->io_start))
>>> +            break;
>>> +    }
>>> +    read_unlock(&extio_list_lock);
>>> +    return (&extio_entry->list == &extio_dev_list) ? NULL :
>>> extio_entry;
>>> +}
>>> +
>>> +struct extio_node *extio_find_node(struct fwnode_handle *node)
>>> +{
>>> +    struct extio_node *entry;
>>> +
>>> +    read_lock(&extio_list_lock);
>>> +    list_for_each_entry(entry, &extio_dev_list, list) {
>>> +        if (entry->fwnode == node)
>>> +            break;
>>> +    }
>>> +    read_unlock(&extio_list_lock);
>>> +
>>> +    return (&entry->list == &extio_dev_list) ? NULL : entry;
>>> +}
>>> +
>>> +unsigned long extio_translate(struct fwnode_handle *node,
>>> +        unsigned long bus_addr)
>>> +{
>>> +    struct extio_node *entry;
>>> +    unsigned long port_id = -1;
>>> +
>>> +    read_lock(&extio_list_lock);
>>> +    list_for_each_entry(entry, &extio_dev_list, list) {
>>> +        if (entry->fwnode == node &&
>>> +            bus_addr >= entry->bus_start &&
>>> +            bus_addr - entry->bus_start < entry->range_size)
>>> +            port_id = entry->io_start + bus_addr -
>>> +                    entry->bus_start;
>>> +    }
>>> +    read_unlock(&extio_list_lock);
>>> +
>>> +    return port_id;
>>> +}
>>> +
>>> +#ifdef PCI_IOBASE
>>> +
>>> +#define BUILD_EXTIO(bw, type)                        \
>>> +type extio_in##bw(unsigned long addr)                    \
>>> +{                                    \
>>> +    struct extio_node *extio_entry = find_extio_token(addr);    \
>>> +                                    \
>>> +    if (!extio_entry)                        \
>>> +        return read##bw(PCI_IOBASE + addr);            \
>>> +    return extio_entry->ops->pfin ?                    \
>>> +            extio_entry->ops->pfin(extio_entry->devpara,    \
>>> +            addr, sizeof(type)) : -1;            \
>>> +}                                    \
>>> +                                    \
>>> +void extio_out##bw(type value, unsigned long addr)            \
>>> +{                                    \
>>> +    struct extio_node *extio_entry = find_extio_token(addr);    \
>>> +                                    \
>>> +    if (!extio_entry)                        \
>>> +        write##bw(value, PCI_IOBASE + addr);            \
>>
>> All of the fallback code would also disappear as a nice side effect of
>> making pci pio handling a user of extio :).
>
> Is your idea that PCI IO space will also register accessors, which would
> be the same read{b,w,l}/write{b,w,l}?

Yes. If you need to later on accelerate that bit, you can always do 
something like

   if (extio_entry->ops->pfin == pci_extio_in)
     return pci_extio_in(...);

which should get you all the prefetcher and branch prediction benefits 
that the current version gives you. But for starters I'd leave that out, 
since I doubt it'll have measurable performance impact to go via an 
indirect function call.

>
>>
>
> It would be nice to have a quicker way to so the lookup from address to
> node, as we loop all nodes in find_extio_token() every single time.

You can always replace the search with a tree. But to me that's an 
implementation detail that's easy enough to replace in a follow-up patch 
series.


Alex

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 37+ messages in thread

* RE: [PATCH V6 1/5] LIB: Indirect ISA/LPC port IO introduced
  2017-01-31 19:37       ` Alexander Graf
@ 2017-02-01 12:29         ` Gabriele Paoloni
  2017-02-13 14:17         ` zhichang.yuan
  1 sibling, 0 replies; 37+ messages in thread
From: Gabriele Paoloni @ 2017-02-01 12:29 UTC (permalink / raw)
  To: Alexander Graf, John Garry, Yuanzhichang, catalin.marinas,
	will.deacon, robh+dt, frowand.list, bhelgaas, rafael,
	mark.rutland, brian.starkey, olof, arnd, linux-arm-kernel
  Cc: devicetree, lorenzo.pieralisi, minyard, benh, zhichang.yuan02,
	liviu.dudau, linux-kernel, xuwei (O),
	Linuxarm, linux-serial, linux-pci, zourongrong, kantyzc

Hi Alex

> -----Original Message-----
> From: Alexander Graf [mailto:agraf@suse.de]

[...]

> >>
> >> I like the extio idea. That allows us to handle all PIO requests on
> >> platforms that don't have native PIO support via different routes
> >> depending on the region they're in. Unfortunately we now we have 2
> >> frameworks for handling sparse PIO regions: One in extio, one in
> PCI.
> >>
> >> Why don't we just merge the two? Most of the code that has #ifdef
> >> PCI_IOBASE throughout the code base sounds like an ideal candidate
> to
> >> get migrated to extio instead. Then we only have a single framework
> to
> >> worry about ...
> >
> > To be clear, are you suggesting we merge the functionality from
> > pci_register_io_range(), pci_pio_to_address(), pci_address_to_pio()
> into
> > extio, so extio manages all PIO?
> 
> Yes, I guess so.
> 
> > And having a single type of node to
> > register PIO ranges, by amalgamating struct extio_node and io_range
> (as
> > Bjorn mentioned)?
> 
> I'm not quite sure I follow you here. Basically I think you want a
> generic "non-x86 PIO" framework that PCI just plugs into.
> 
> I don't think that necessarily means you want to statically allocate
> regions of that PIO space to separate (pseudo-)devices. Instead,
> everyone shares that space and should be able to fail gracefully if
> some
> space is already occupied.
> 
> > It would make sense. We would be somewhat decoupling PIO from PCI.
> 
> Yes :).
> 
> > I think that other architectures, like PPC, and other code would need
> to
> > be fixed up to handle this.
> 
> I think only PPC, Microblaze and ARM are using this. Grep for
> PCI_IOBASE. It's not that many.
> 
> > We need to consider all the other challenges/obstacles to this.
> 
> Well, getting our abstraction levels right to me sounds like it's worth
> the obstacles.
> 

I have had a quick look and I think it should not be too difficult to
unify the two frameworks.

I'll follow up soon on this thread with a code sketch

Thanks
Gab

[...]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH V6 2/5] PCI: Adapt pci_register_io_range() for indirect-IO and PCI I/O translation
  2017-01-24  7:05 ` [PATCH V6 2/5] PCI: Adapt pci_register_io_range() for indirect-IO and PCI I/O translation zhichang.yuan
  2017-01-31  0:10   ` Bjorn Helgaas
  2017-01-31  0:15   ` Bjorn Helgaas
@ 2017-02-02 17:36   ` John Garry
  2017-02-02 23:00     ` Rafael J. Wysocki
  2 siblings, 1 reply; 37+ messages in thread
From: John Garry @ 2017-02-02 17:36 UTC (permalink / raw)
  To: zhichang.yuan, catalin.marinas, will.deacon, robh+dt,
	frowand.list, bhelgaas, rafael, mark.rutland, brian.starkey,
	olof, arnd, linux-arm-kernel
  Cc: lorenzo.pieralisi, benh, linux-kernel, linuxarm, devicetree,
	linux-pci, linux-serial, minyard, liviu.dudau, zourongrong,
	gabriele.paoloni, zhichang.yuan02, kantyzc, xuwei5

Hi Rafael,

Could you kindly check the minor changes to drivers/acpi/pci_root.c?  It 
would also be appreciated if you could review the ACPI-relevant parts in 
lib/extio.c, in [PATCH V5 5/5] LPC: Add the ACPI LPC support

Thanks,
John

On 24/01/2017 07:05, zhichang.yuan wrote:
> After indirect-IO is introduced, system must can assigned indirect-IO devices
> with logical I/O ranges which are different from those for PCI I/O devices.
> Otherwise, I/O accessors can't identify whether the I/O port is for memory
> mapped I/O or indirect-IO.
> As current helper, pci_register_io_range(), is used for PCI I/O ranges
> registration and translation, indirect-IO devices should also apply these
> helpers to manage the I/O ranges. It will be easy to ensure the assigned
> logical I/O ranges unique.
> But for indirect-IO devices, there is no cpu address. The current
> pci_register_io_range() can not work for this case.
>
> This patch makes some changes on the pci_register_io_range() to support the
> I/O range registration with device's fwnode also. After this, the indirect-IO
> devices can register the device-local I/O range to system logical I/O and
> easily perform the translation between device-local I/O range and sytem
> logical I/O range.
>
> Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
> Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
> ---
>  drivers/acpi/pci_root.c | 12 +++++-------
>  drivers/of/address.c    |  8 ++------
>  drivers/pci/pci.c       | 44 ++++++++++++++++++++++++++++++++++++++++----
>  include/linux/pci.h     |  7 +++++--
>  4 files changed, 52 insertions(+), 19 deletions(-)
>
> diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
> index bf601d4..6cadf05 100644
> --- a/drivers/acpi/pci_root.c
> +++ b/drivers/acpi/pci_root.c
> @@ -730,7 +730,8 @@ static void acpi_pci_root_validate_resources(struct device *dev,
>  	}
>  }
>
> -static void acpi_pci_root_remap_iospace(struct resource_entry *entry)
> +static void acpi_pci_root_remap_iospace(struct fwnode_handle *node,
> +					struct resource_entry *entry)
>  {
>  #ifdef PCI_IOBASE
>  	struct resource *res = entry->res;
> @@ -739,11 +740,7 @@ static void acpi_pci_root_remap_iospace(struct resource_entry *entry)
>  	resource_size_t length = resource_size(res);
>  	unsigned long port;
>
> -	if (pci_register_io_range(cpu_addr, length))
> -		goto err;
> -
> -	port = pci_address_to_pio(cpu_addr);
> -	if (port == (unsigned long)-1)
> +	if (pci_register_io_range(node, cpu_addr, length, &port))
>  		goto err;
>
>  	res->start = port;
> @@ -781,7 +778,8 @@ int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info)
>  	else {
>  		resource_list_for_each_entry_safe(entry, tmp, list) {
>  			if (entry->res->flags & IORESOURCE_IO)
> -				acpi_pci_root_remap_iospace(entry);
> +				acpi_pci_root_remap_iospace(&device->fwnode,
> +							    entry);
>
>  			if (entry->res->flags & IORESOURCE_DISABLED)
>  				resource_list_destroy_entry(entry);
> diff --git a/drivers/of/address.c b/drivers/of/address.c
> index 02b2903..d85d228 100644
> --- a/drivers/of/address.c
> +++ b/drivers/of/address.c
> @@ -2,6 +2,7 @@
>  #define pr_fmt(fmt)	"OF: " fmt
>
>  #include <linux/device.h>
> +#include <linux/fwnode.h>
>  #include <linux/io.h>
>  #include <linux/ioport.h>
>  #include <linux/module.h>
> @@ -323,14 +324,9 @@ int of_pci_range_to_resource(struct of_pci_range *range,
>
>  	if (res->flags & IORESOURCE_IO) {
>  		unsigned long port;
> -		err = pci_register_io_range(range->cpu_addr, range->size);
> +		err = pci_register_io_range(&np->fwnode, range->cpu_addr, range->size, &port);
>  		if (err)
>  			goto invalid_range;
> -		port = pci_address_to_pio(range->cpu_addr);
> -		if (port == (unsigned long)-1) {
> -			err = -EINVAL;
> -			goto invalid_range;
> -		}
>  		res->start = port;
>  	} else {
>  		if ((sizeof(resource_size_t) < 8) &&
> diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
> index a881c0d..5289221 100644
> --- a/drivers/pci/pci.c
> +++ b/drivers/pci/pci.c
> @@ -3241,6 +3241,7 @@ int pci_request_regions_exclusive(struct pci_dev *pdev, const char *res_name)
>  #ifdef PCI_IOBASE
>  struct io_range {
>  	struct list_head list;
> +	struct fwnode_handle *node;
>  	phys_addr_t start;
>  	resource_size_t size;
>  };
> @@ -3253,7 +3254,8 @@ struct io_range {
>   * Record the PCI IO range (expressed as CPU physical address + size).
>   * Return a negative value if an error has occured, zero otherwise
>   */
> -int __weak pci_register_io_range(phys_addr_t addr, resource_size_t size)
> +int __weak pci_register_io_range(struct fwnode_handle *node, phys_addr_t addr,
> +				 resource_size_t size, unsigned long *port)
>  {
>  	int err = 0;
>
> @@ -3261,10 +3263,31 @@ int __weak pci_register_io_range(phys_addr_t addr, resource_size_t size)
>  	struct io_range *range;
>  	resource_size_t allocated_size = 0;
>
> +	/*
> +	 * As indirect-IO which support multiple bus instances is introduced,
> +	 * the input 'addr' is probably not page-aligned. If the PCI I/O
> +	 * ranges are registered after indirect-IO, there is risk that the
> +	 * start logical PIO assigned to PCI I/O is not page-aligned.
> +	 * This will cause some I/O subranges are not remapped or overlapped
> +	 * in pci_remap_iospace() handling.
> +	 */
> +	WARN_ON(addr != IO_RANGE_IOEXT && !(addr & PAGE_MASK));
> +	/*
> +	 * MMIO will call ioremap, it is better to align size with PAGE_SIZE,
> +	 * then the return linux virtual PIO is page-aligned.
> +	 */
> +	if (size & PAGE_MASK)
> +		size = PAGE_ALIGN(size);
> +
>  	/* check if the range hasn't been previously recorded */
>  	spin_lock(&io_range_lock);
>  	list_for_each_entry(range, &io_range_list, list) {
> -		if (addr >= range->start && addr + size <= range->start + size) {
> +		if (node == range->node)
> +			goto end_register;
> +
> +		if (addr != IO_RANGE_IOEXT &&
> +		    addr >= range->start &&
> +		    addr + size <= range->start + size) {
>  			/* range already registered, bail out */
>  			goto end_register;
>  		}
> @@ -3290,6 +3313,7 @@ int __weak pci_register_io_range(phys_addr_t addr, resource_size_t size)
>  		goto end_register;
>  	}
>
> +	range->node = node;
>  	range->start = addr;
>  	range->size = size;
>
> @@ -3297,6 +3321,14 @@ int __weak pci_register_io_range(phys_addr_t addr, resource_size_t size)
>
>  end_register:
>  	spin_unlock(&io_range_lock);
> +
> +	*port = allocated_size;
> +#else
> +	/*
> +	 * powerpc and microblaze have their own registration,
> +	 * just look up the value here
> +	 */
> +	*port = pci_address_to_pio(addr);
>  #endif
>
>  	return err;
> @@ -3315,7 +3347,9 @@ phys_addr_t pci_pio_to_address(unsigned long pio)
>
>  	spin_lock(&io_range_lock);
>  	list_for_each_entry(range, &io_range_list, list) {
> -		if (pio >= allocated_size && pio < allocated_size + range->size) {
> +		if (range->start != IO_RANGE_IOEXT &&
> +			pio >= allocated_size &&
> +			pio < allocated_size + range->size) {
>  			address = range->start + pio - allocated_size;
>  			break;
>  		}
> @@ -3336,7 +3370,9 @@ unsigned long __weak pci_address_to_pio(phys_addr_t address)
>
>  	spin_lock(&io_range_lock);
>  	list_for_each_entry(res, &io_range_list, list) {
> -		if (address >= res->start && address < res->start + res->size) {
> +		if (res->start != IO_RANGE_IOEXT &&
> +			address >= res->start &&
> +			address < res->start + res->size) {
>  			addr = address - res->start + offset;
>  			break;
>  		}
> diff --git a/include/linux/pci.h b/include/linux/pci.h
> index e2d1a12..8d91af8 100644
> --- a/include/linux/pci.h
> +++ b/include/linux/pci.h
> @@ -34,6 +34,9 @@
>
>  #include <linux/pci_ids.h>
>
> +/* the macro below flags an invalid cpu address
> + * and is used by IO special hosts              */
> +#define IO_RANGE_IOEXT (resource_size_t)(-1ull)
>  /*
>   * The PCI interface treats multi-function devices as independent
>   * devices.  The slot/function address of each device is encoded
> @@ -1197,8 +1200,8 @@ int __must_check pci_bus_alloc_resource(struct pci_bus *bus,
>  						  resource_size_t),
>  			void *alignf_data);
>
> -
> -int pci_register_io_range(phys_addr_t addr, resource_size_t size);
> +int pci_register_io_range(struct fwnode_handle *node, phys_addr_t addr,
> +			  resource_size_t size, unsigned long *port);
>  unsigned long pci_address_to_pio(phys_addr_t addr);
>  phys_addr_t pci_pio_to_address(unsigned long pio);
>  int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr);
>

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH V6 2/5] PCI: Adapt pci_register_io_range() for indirect-IO and PCI I/O translation
  2017-02-02 17:36   ` John Garry
@ 2017-02-02 23:00     ` Rafael J. Wysocki
  0 siblings, 0 replies; 37+ messages in thread
From: Rafael J. Wysocki @ 2017-02-02 23:00 UTC (permalink / raw)
  To: John Garry
  Cc: zhichang.yuan, Catalin Marinas, Will Deacon, Rob Herring,
	Frank Rowand, Bjorn Helgaas, Rafael J. Wysocki, Mark Rutland,
	brian.starkey, Olof Johansson, Arnd Bergmann, linux-arm-kernel,
	Lorenzo Pieralisi, Benjamin Herrenschmidt,
	Linux Kernel Mailing List, linuxarm, devicetree, Linux PCI,
	linux-serial, Corey Minyard, liviu.dudau, zourongrong,
	Gabriele Paoloni, zhichang.yuan02, kantyzc, xuwei5

On Thu, Feb 2, 2017 at 6:36 PM, John Garry <john.garry@huawei.com> wrote:
> Hi Rafael,
>
> Could you kindly check the minor changes to drivers/acpi/pci_root.c?  It
> would also be appreciated if you could review the ACPI-relevant parts in
> lib/extio.c, in [PATCH V5 5/5] LPC: Add the ACPI LPC support

If you want patches to be reviewed from the ACPI perspective, please
CC them to linux-acpi.  I'm not the only person who may be interested
in them.

The change is pci_root.c looks OK to me, but it's Bjorn's call anyway.

Thanks,
Rafael



> On 24/01/2017 07:05, zhichang.yuan wrote:
>>
>> After indirect-IO is introduced, system must can assigned indirect-IO
>> devices
>> with logical I/O ranges which are different from those for PCI I/O
>> devices.
>> Otherwise, I/O accessors can't identify whether the I/O port is for memory
>> mapped I/O or indirect-IO.
>> As current helper, pci_register_io_range(), is used for PCI I/O ranges
>> registration and translation, indirect-IO devices should also apply these
>> helpers to manage the I/O ranges. It will be easy to ensure the assigned
>> logical I/O ranges unique.
>> But for indirect-IO devices, there is no cpu address. The current
>> pci_register_io_range() can not work for this case.
>>
>> This patch makes some changes on the pci_register_io_range() to support
>> the
>> I/O range registration with device's fwnode also. After this, the
>> indirect-IO
>> devices can register the device-local I/O range to system logical I/O and
>> easily perform the translation between device-local I/O range and sytem
>> logical I/O range.
>>
>> Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
>> Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
>> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
>> ---
>>  drivers/acpi/pci_root.c | 12 +++++-------
>>  drivers/of/address.c    |  8 ++------
>>  drivers/pci/pci.c       | 44 ++++++++++++++++++++++++++++++++++++++++----
>>  include/linux/pci.h     |  7 +++++--
>>  4 files changed, 52 insertions(+), 19 deletions(-)
>>
>> diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
>> index bf601d4..6cadf05 100644
>> --- a/drivers/acpi/pci_root.c
>> +++ b/drivers/acpi/pci_root.c
>> @@ -730,7 +730,8 @@ static void acpi_pci_root_validate_resources(struct
>> device *dev,
>>         }
>>  }
>>
>> -static void acpi_pci_root_remap_iospace(struct resource_entry *entry)
>> +static void acpi_pci_root_remap_iospace(struct fwnode_handle *node,
>> +                                       struct resource_entry *entry)
>>  {
>>  #ifdef PCI_IOBASE
>>         struct resource *res = entry->res;
>> @@ -739,11 +740,7 @@ static void acpi_pci_root_remap_iospace(struct
>> resource_entry *entry)
>>         resource_size_t length = resource_size(res);
>>         unsigned long port;
>>
>> -       if (pci_register_io_range(cpu_addr, length))
>> -               goto err;
>> -
>> -       port = pci_address_to_pio(cpu_addr);
>> -       if (port == (unsigned long)-1)
>> +       if (pci_register_io_range(node, cpu_addr, length, &port))
>>                 goto err;
>>
>>         res->start = port;
>> @@ -781,7 +778,8 @@ int acpi_pci_probe_root_resources(struct
>> acpi_pci_root_info *info)
>>         else {
>>                 resource_list_for_each_entry_safe(entry, tmp, list) {
>>                         if (entry->res->flags & IORESOURCE_IO)
>> -                               acpi_pci_root_remap_iospace(entry);
>> +
>> acpi_pci_root_remap_iospace(&device->fwnode,
>> +                                                           entry);
>>
>>                         if (entry->res->flags & IORESOURCE_DISABLED)
>>                                 resource_list_destroy_entry(entry);
>> diff --git a/drivers/of/address.c b/drivers/of/address.c
>> index 02b2903..d85d228 100644
>> --- a/drivers/of/address.c
>> +++ b/drivers/of/address.c
>> @@ -2,6 +2,7 @@
>>  #define pr_fmt(fmt)    "OF: " fmt
>>
>>  #include <linux/device.h>
>> +#include <linux/fwnode.h>
>>  #include <linux/io.h>
>>  #include <linux/ioport.h>
>>  #include <linux/module.h>
>> @@ -323,14 +324,9 @@ int of_pci_range_to_resource(struct of_pci_range
>> *range,
>>
>>         if (res->flags & IORESOURCE_IO) {
>>                 unsigned long port;
>> -               err = pci_register_io_range(range->cpu_addr, range->size);
>> +               err = pci_register_io_range(&np->fwnode, range->cpu_addr,
>> range->size, &port);
>>                 if (err)
>>                         goto invalid_range;
>> -               port = pci_address_to_pio(range->cpu_addr);
>> -               if (port == (unsigned long)-1) {
>> -                       err = -EINVAL;
>> -                       goto invalid_range;
>> -               }
>>                 res->start = port;
>>         } else {
>>                 if ((sizeof(resource_size_t) < 8) &&
>> diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
>> index a881c0d..5289221 100644
>> --- a/drivers/pci/pci.c
>> +++ b/drivers/pci/pci.c
>> @@ -3241,6 +3241,7 @@ int pci_request_regions_exclusive(struct pci_dev
>> *pdev, const char *res_name)
>>  #ifdef PCI_IOBASE
>>  struct io_range {
>>         struct list_head list;
>> +       struct fwnode_handle *node;
>>         phys_addr_t start;
>>         resource_size_t size;
>>  };
>> @@ -3253,7 +3254,8 @@ struct io_range {
>>   * Record the PCI IO range (expressed as CPU physical address + size).
>>   * Return a negative value if an error has occured, zero otherwise
>>   */
>> -int __weak pci_register_io_range(phys_addr_t addr, resource_size_t size)
>> +int __weak pci_register_io_range(struct fwnode_handle *node, phys_addr_t
>> addr,
>> +                                resource_size_t size, unsigned long
>> *port)
>>  {
>>         int err = 0;
>>
>> @@ -3261,10 +3263,31 @@ int __weak pci_register_io_range(phys_addr_t addr,
>> resource_size_t size)
>>         struct io_range *range;
>>         resource_size_t allocated_size = 0;
>>
>> +       /*
>> +        * As indirect-IO which support multiple bus instances is
>> introduced,
>> +        * the input 'addr' is probably not page-aligned. If the PCI I/O
>> +        * ranges are registered after indirect-IO, there is risk that the
>> +        * start logical PIO assigned to PCI I/O is not page-aligned.
>> +        * This will cause some I/O subranges are not remapped or
>> overlapped
>> +        * in pci_remap_iospace() handling.
>> +        */
>> +       WARN_ON(addr != IO_RANGE_IOEXT && !(addr & PAGE_MASK));
>> +       /*
>> +        * MMIO will call ioremap, it is better to align size with
>> PAGE_SIZE,
>> +        * then the return linux virtual PIO is page-aligned.
>> +        */
>> +       if (size & PAGE_MASK)
>> +               size = PAGE_ALIGN(size);
>> +
>>         /* check if the range hasn't been previously recorded */
>>         spin_lock(&io_range_lock);
>>         list_for_each_entry(range, &io_range_list, list) {
>> -               if (addr >= range->start && addr + size <= range->start +
>> size) {
>> +               if (node == range->node)
>> +                       goto end_register;
>> +
>> +               if (addr != IO_RANGE_IOEXT &&
>> +                   addr >= range->start &&
>> +                   addr + size <= range->start + size) {
>>                         /* range already registered, bail out */
>>                         goto end_register;
>>                 }
>> @@ -3290,6 +3313,7 @@ int __weak pci_register_io_range(phys_addr_t addr,
>> resource_size_t size)
>>                 goto end_register;
>>         }
>>
>> +       range->node = node;
>>         range->start = addr;
>>         range->size = size;
>>
>> @@ -3297,6 +3321,14 @@ int __weak pci_register_io_range(phys_addr_t addr,
>> resource_size_t size)
>>
>>  end_register:
>>         spin_unlock(&io_range_lock);
>> +
>> +       *port = allocated_size;
>> +#else
>> +       /*
>> +        * powerpc and microblaze have their own registration,
>> +        * just look up the value here
>> +        */
>> +       *port = pci_address_to_pio(addr);
>>  #endif
>>
>>         return err;
>> @@ -3315,7 +3347,9 @@ phys_addr_t pci_pio_to_address(unsigned long pio)
>>
>>         spin_lock(&io_range_lock);
>>         list_for_each_entry(range, &io_range_list, list) {
>> -               if (pio >= allocated_size && pio < allocated_size +
>> range->size) {
>> +               if (range->start != IO_RANGE_IOEXT &&
>> +                       pio >= allocated_size &&
>> +                       pio < allocated_size + range->size) {
>>                         address = range->start + pio - allocated_size;
>>                         break;
>>                 }
>> @@ -3336,7 +3370,9 @@ unsigned long __weak pci_address_to_pio(phys_addr_t
>> address)
>>
>>         spin_lock(&io_range_lock);
>>         list_for_each_entry(res, &io_range_list, list) {
>> -               if (address >= res->start && address < res->start +
>> res->size) {
>> +               if (res->start != IO_RANGE_IOEXT &&
>> +                       address >= res->start &&
>> +                       address < res->start + res->size) {
>>                         addr = address - res->start + offset;
>>                         break;
>>                 }
>> diff --git a/include/linux/pci.h b/include/linux/pci.h
>> index e2d1a12..8d91af8 100644
>> --- a/include/linux/pci.h
>> +++ b/include/linux/pci.h
>> @@ -34,6 +34,9 @@
>>
>>  #include <linux/pci_ids.h>
>>
>> +/* the macro below flags an invalid cpu address
>> + * and is used by IO special hosts              */
>> +#define IO_RANGE_IOEXT (resource_size_t)(-1ull)
>>  /*
>>   * The PCI interface treats multi-function devices as independent
>>   * devices.  The slot/function address of each device is encoded
>> @@ -1197,8 +1200,8 @@ int __must_check pci_bus_alloc_resource(struct
>> pci_bus *bus,
>>                                                   resource_size_t),
>>                         void *alignf_data);
>>
>> -
>> -int pci_register_io_range(phys_addr_t addr, resource_size_t size);
>> +int pci_register_io_range(struct fwnode_handle *node, phys_addr_t addr,
>> +                         resource_size_t size, unsigned long *port);
>>  unsigned long pci_address_to_pio(phys_addr_t addr);
>>  phys_addr_t pci_pio_to_address(unsigned long pio);
>>  int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr);
>>
>
>

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH V6 2/5] PCI: Adapt pci_register_io_range() for indirect-IO and PCI I/O translation
  2017-01-31  0:15   ` Bjorn Helgaas
@ 2017-02-04 12:59     ` John Garry
  0 siblings, 0 replies; 37+ messages in thread
From: John Garry @ 2017-02-04 12:59 UTC (permalink / raw)
  To: Bjorn Helgaas, zhichang.yuan
  Cc: catalin.marinas, will.deacon, robh+dt, frowand.list, bhelgaas,
	rafael, mark.rutland, brian.starkey, olof, arnd,
	linux-arm-kernel, lorenzo.pieralisi, benh, linux-kernel,
	linuxarm, devicetree, linux-pci, linux-serial, minyard,
	liviu.dudau, zourongrong, gabriele.paoloni, zhichang.yuan02,
	kantyzc, xuwei5, linux-acpi

On 31/01/2017 00:15, Bjorn Helgaas wrote:
> On Tue, Jan 24, 2017 at 03:05:22PM +0800, zhichang.yuan wrote:
>> After indirect-IO is introduced, system must can assigned indirect-IO devices
>> with logical I/O ranges which are different from those for PCI I/O devices.
>> Otherwise, I/O accessors can't identify whether the I/O port is for memory
>> mapped I/O or indirect-IO.
>> As current helper, pci_register_io_range(), is used for PCI I/O ranges
>> registration and translation, indirect-IO devices should also apply these
>> helpers to manage the I/O ranges. It will be easy to ensure the assigned
>> logical I/O ranges unique.
>> But for indirect-IO devices, there is no cpu address. The current
>> pci_register_io_range() can not work for this case.
>>
>> This patch makes some changes on the pci_register_io_range() to support the
>> I/O range registration with device's fwnode also. After this, the indirect-IO
>> devices can register the device-local I/O range to system logical I/O and
>> easily perform the translation between device-local I/O range and sytem
>> logical I/O range.
>>
>> Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
>> Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
>> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
>
> I had a couple trivial comments, but you can include my:
>
> Acked-by: Bjorn Helgaas <bhelgaas@google.com>	# drivers/pci parts
>
> in your next revision.  I don't know who you have in mind to merge this;
> it doesn't really touch much of PCI.  But let me know if you need anything
> else from me.

Thanks Bjorn.

The current status is that we plan to send a new patchset early next 
week with the suggested change to combine PCI+extio IO framework. The IO 
space range stuff (pci_register_io_range() et al) will move from 
drivers/pci/pci.c to new lib/libio.c (renamed from extio.c).

We still need someone to merge (we are praying to make 4.11), and also 
Arnd to kindly check the update from his sketch and also more reviews of 
the ACPI part of what was extio.c 
(http://www.spinics.net/lists/devicetree/msg160611.html, 5/5)

Much appreciated,
John

>
>> ---
>>  drivers/acpi/pci_root.c | 12 +++++-------
>>  drivers/of/address.c    |  8 ++------
>>  drivers/pci/pci.c       | 44 ++++++++++++++++++++++++++++++++++++++++----
>>  include/linux/pci.h     |  7 +++++--
>>  4 files changed, 52 insertions(+), 19 deletions(-)
>>

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH V5 5/5] LPC: Add the ACPI LPC support
  2017-01-24  7:05 ` [PATCH V5 5/5] LPC: Add the ACPI LPC support zhichang.yuan
@ 2017-02-04 13:20   ` John Garry
  0 siblings, 0 replies; 37+ messages in thread
From: John Garry @ 2017-02-04 13:20 UTC (permalink / raw)
  To: zhichang.yuan, catalin.marinas, will.deacon, robh+dt,
	frowand.list, bhelgaas, rafael, mark.rutland, brian.starkey,
	olof, arnd, linux-arm-kernel
  Cc: devicetree, lorenzo.pieralisi, gabriele.paoloni, minyard, benh,
	zhichang.yuan02, liviu.dudau, linux-kernel, xuwei5, linuxarm,
	linux-acpi, linux-serial, linux-pci, zourongrong, kantyzc

+ linux-acpi

Adding linux-acpi list, which should have been originally included.

The patchset threads have had much discussion, here is a pointer: 
http://www.spinics.net/lists/devicetree/msg160611.html

John

On 24/01/2017 07:05, zhichang.yuan wrote:
> The patch update the _CRS of LPC children with the system logical I/O resource
> after the translation from LPC-local I/O. Then the ACPI platform device
> enumeration for LPC can apply the right I/O resource to request the system I/O
> space.
>
> Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
> ---
>  drivers/bus/hisi_lpc.c |  26 ++++++
>  include/linux/extio.h  |   4 +
>  lib/extio.c            | 228 +++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 258 insertions(+)
>
> diff --git a/drivers/bus/hisi_lpc.c b/drivers/bus/hisi_lpc.c
> index a96e384..8d52666 100644
> --- a/drivers/bus/hisi_lpc.c
> +++ b/drivers/bus/hisi_lpc.c
> @@ -583,6 +583,32 @@ static int hisilpc_bus_platform_notify(struct notifier_block *nb,
>  	/* register the linux virtual IO range node to list. */
>  	register_extio(io_node);
>
> +	/*
> +	 * For ACPI children, translate the bus-local I/O range to logical
> +	 * I/O range and set it as the current resource before the children
> +	 * are enumerated.
> +	 */
> +	if (has_acpi_companion(dev)) {
> +		struct acpi_device *root, *child;
> +
> +		root = to_acpi_device_node(dev->fwnode);
> +		/* For hisilpc, only care about the sons of host. */
> +		list_for_each_entry(child, &root->children, node) {
> +			ret = acpi_set_extio_resource(child, root);
> +			if (ret) {
> +				dev_err(dev, "set resource failed..\n");
> +				break;
> +			}
> +		}
> +
> +		if (ret) {
> +			list_del(&io_node->list);
> +			kfree(io_node);
> +			dev_err(dev, "notify handling failed..\n");
> +			return NOTIFY_DONE;
> +		}
> +	}
> +
>  	return NOTIFY_OK;
>  }
>
> diff --git a/include/linux/extio.h b/include/linux/extio.h
> index 2ca7eab..c07607e 100644
> --- a/include/linux/extio.h
> +++ b/include/linux/extio.h
> @@ -20,6 +20,7 @@
>
>  #ifdef __KERNEL__
>
> +#include <linux/acpi.h>
>  #include <linux/fwnode.h>
>
>  struct extio_ops {
> @@ -81,5 +82,8 @@ static inline struct extio_node *extio_find_node(struct fwnode_handle *node)
>  #endif
>  extern void register_extio(struct extio_node *node);
>
> +extern int acpi_set_extio_resource(struct acpi_device *adev,
> +		struct acpi_device *host);
> +
>  #endif /* __KERNEL__ */
>  #endif /* __LINUX_EXTIO_H */
> diff --git a/lib/extio.c b/lib/extio.c
> index 46228de..47f5913 100644
> --- a/lib/extio.c
> +++ b/lib/extio.c
> @@ -75,6 +75,234 @@ unsigned long extio_translate(struct fwnode_handle *node,
>  	return port_id;
>  }
>
> +static inline bool acpi_extio_supported_resource(struct acpi_resource *res)
> +{
> +	switch (res->type) {
> +	case ACPI_RESOURCE_TYPE_ADDRESS16:
> +	case ACPI_RESOURCE_TYPE_ADDRESS32:
> +	case ACPI_RESOURCE_TYPE_ADDRESS64:
> +		return true;
> +	}
> +	return false;
> +}
> +
> +static acpi_status acpi_count_extiores(struct acpi_resource *res,
> +					   void *data)
> +{
> +	int *res_cnt = data;
> +
> +	if (acpi_extio_supported_resource(res) &&
> +		!acpi_dev_filter_resource_type(res, IORESOURCE_IO))
> +		(*res_cnt)++;
> +
> +	return AE_OK;
> +}
> +
> +static acpi_status acpi_read_one_extiores(struct acpi_resource *res,
> +		void *data)
> +{
> +	struct acpi_resource **resource = data;
> +
> +	if (acpi_extio_supported_resource(res) &&
> +		!acpi_dev_filter_resource_type(res, IORESOURCE_IO)) {
> +		memcpy((*resource), res, sizeof(struct acpi_resource));
> +		(*resource)->length = sizeof(struct acpi_resource);
> +		(*resource)->type = res->type;
> +		(*resource)++;
> +	}
> +
> +	return AE_OK;
> +}
> +
> +static acpi_status
> +acpi_build_extiores_template(struct acpi_device *adev,
> +			struct acpi_buffer *buffer)
> +{
> +	acpi_handle handle = adev->handle;
> +	struct acpi_resource *resource;
> +	acpi_status status;
> +	int res_cnt = 0;
> +
> +	status = acpi_walk_resources(handle, METHOD_NAME__PRS,
> +				     acpi_count_extiores, &res_cnt);
> +	if (ACPI_FAILURE(status) || !res_cnt) {
> +		dev_err(&adev->dev, "can't evaluate _CRS: %d\n", status);
> +		return -EINVAL;
> +	}
> +
> +	buffer->length = sizeof(struct acpi_resource) * (res_cnt + 1) + 1;
> +	buffer->pointer = kzalloc(buffer->length - 1, GFP_KERNEL);
> +	if (!buffer->pointer)
> +		return -ENOMEM;
> +
> +	resource = (struct acpi_resource *)buffer->pointer;
> +	status = acpi_walk_resources(handle, METHOD_NAME__PRS,
> +				     acpi_read_one_extiores, &resource);
> +	if (ACPI_FAILURE(status)) {
> +		kfree(buffer->pointer);
> +		dev_err(&adev->dev, "can't evaluate _PRS: %d\n", status);
> +		return -EINVAL;
> +	}
> +
> +	resource->type = ACPI_RESOURCE_TYPE_END_TAG;
> +	resource->length = sizeof(struct acpi_resource);
> +
> +	return 0;
> +}
> +
> +static int acpi_translate_extiores(struct acpi_device *adev,
> +		struct acpi_device *host, struct acpi_buffer *buffer)
> +{
> +	int res_cnt = (buffer->length - 1) / sizeof(struct acpi_resource) - 1;
> +	struct acpi_resource *resource = buffer->pointer;
> +	struct acpi_resource_address64 addr;
> +	unsigned long sys_port;
> +	struct device *dev = &adev->dev;
> +
> +	/* only one I/O resource now */
> +	if (res_cnt != 1) {
> +		dev_err(dev, "encode %d resources whose type is(%d)!\n",
> +			res_cnt, resource->type);
> +		return -EINVAL;
> +	}
> +
> +	if (ACPI_FAILURE(acpi_resource_to_address64(resource, &addr))) {
> +		dev_err(dev, "convert acpi resource(%d) as addr64 FAIL!\n",
> +			resource->type);
> +		return -EFAULT;
> +	}
> +
> +	/* For indirect-IO, addr length must be fixed. (>0, 0, 0) */
> +	if (!addr.address.address_length || addr.min_address_fixed ||
> +		addr.max_address_fixed) {
> +		dev_warn(dev, "variable I/O resource is invalid!\n");
> +		return -EINVAL;
> +	}
> +
> +	sys_port = extio_translate(&host->fwnode, addr.address.minimum);
> +	if (sys_port == -1) {
> +		dev_err(dev, "translate bus-addr(0x%llx) fail!\n",
> +			addr.address.minimum);
> +		return -EFAULT;
> +	}
> +
> +	switch (resource->type) {
> +	case ACPI_RESOURCE_TYPE_ADDRESS16:
> +	{
> +		struct acpi_resource_address16 *out_res;
> +
> +		out_res = &resource->data.address16;
> +		out_res->address.minimum = sys_port;
> +		out_res->address.maximum = sys_port +
> +			addr.address.address_length - 1;
> +
> +		dev_info(dev, "_SRS 16IO: [0x%x - 0x%x]\n",
> +			out_res->address.minimum,
> +			out_res->address.maximum);
> +
> +		break;
> +	}
> +
> +	case ACPI_RESOURCE_TYPE_ADDRESS32:
> +	{
> +		struct acpi_resource_address32 *out_res;
> +
> +		out_res = &resource->data.address32;
> +		out_res->address.minimum = sys_port;
> +		out_res->address.maximum = sys_port +
> +			addr.address.address_length - 1;
> +
> +		dev_info(dev, "_SRS 32IO: [0x%x - 0x%x]\n",
> +			out_res->address.minimum,
> +			out_res->address.maximum);
> +
> +		break;
> +	}
> +
> +	case ACPI_RESOURCE_TYPE_ADDRESS64:
> +	{
> +		struct acpi_resource_address64 *out_res;
> +
> +		out_res = &resource->data.address64;
> +		out_res->address.minimum = sys_port;
> +		out_res->address.maximum = sys_port +
> +			addr.address.address_length - 1;
> +
> +		dev_info(dev, "_SRS 64IO: [0x%llx - 0x%llx]\n",
> +			out_res->address.minimum,
> +			out_res->address.maximum);
> +
> +		break;
> +	}
> +
> +	default:
> +		return -EINVAL;
> +
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * update/set the current I/O resource of the designated device node.
> + * after this calling, the enumeration can be started as the I/O resource
> + * had been translated to logicial I/O from bus-local I/O.
> + *
> + * @adev: the device node to be updated the I/O resource;
> + * @host: the device node where 'adev' is attached, which can be not
> + *	the parent of 'adev';
> + *
> + * return 0 when successful, negative is for failure.
> + */
> +int acpi_set_extio_resource(struct acpi_device *adev,
> +		struct acpi_device *host)
> +{
> +	struct device *dev = &adev->dev;
> +	struct acpi_buffer buffer;
> +	acpi_status status;
> +	int ret;
> +
> +	if (!host)
> +		return -EINVAL;
> +
> +	/* check the device state */
> +	if (!adev->status.present) {
> +		dev_info(dev, "ACPI: device is not present!\n");
> +		return 0;
> +	}
> +	/* whether the child had been enumerated? */
> +	if (acpi_device_enumerated(adev)) {
> +		dev_info(dev, "ACPI: had been enumerated!\n");
> +		return 0;
> +	}
> +
> +	/* read the _PRS and convert as acpi_buffer */
> +	status = acpi_build_extiores_template(adev, &buffer);
> +	if (ACPI_FAILURE(status)) {
> +		dev_warn(dev, "Failure evaluating %s\n",
> +				METHOD_NAME__PRS);
> +		return -ENODEV;
> +	}
> +
> +	/* translate the I/O resources */
> +	ret = acpi_translate_extiores(adev, host, &buffer);
> +	if (ret) {
> +		kfree(buffer.pointer);
> +		dev_err(dev, "Translate I/O range FAIL!\n");
> +		return ret;
> +	}
> +
> +	/* set current resource... */
> +	status = acpi_set_current_resources(adev->handle, &buffer);
> +	kfree(buffer.pointer);
> +	if (ACPI_FAILURE(status)) {
> +		dev_err(dev, "Error evaluating _SRS (0x%x)\n", status);
> +		ret = -EIO;
> +	}
> +
> +	return ret;
> +}
> +
>  #ifdef PCI_IOBASE
>
>  #define BUILD_EXTIO(bw, type)						\
>



_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH V6 1/5] LIB: Indirect ISA/LPC port IO introduced
  2017-01-30 17:12   ` Alexander Graf
  2017-01-31 13:32     ` John Garry
@ 2017-02-13 14:05     ` zhichang.yuan
  2017-02-14 13:15       ` Alexander Graf
  1 sibling, 1 reply; 37+ messages in thread
From: zhichang.yuan @ 2017-02-13 14:05 UTC (permalink / raw)
  To: Alexander Graf, catalin.marinas, will.deacon, robh+dt,
	frowand.list, bhelgaas, rafael, mark.rutland, brian.starkey,
	olof, arnd, linux-arm-kernel
  Cc: devicetree, lorenzo.pieralisi, gabriele.paoloni, minyard, benh,
	john.garry, liviu.dudau, linux-kernel, xuwei5, linuxarm,
	linux-serial, linux-pci, zourongrong, kantyzc, zhichang.yuan02

Hi, Alex,

Thanks for your review!
Sorry for the late response!
As this patch-set was two weeks ago, it must be painful to check this thread again.

I thought John had discussed with you about most of the comments when I was back from ten days' leave, and I have no more to supplement,
so...
But when I started the work on V7, met somethings need to clarify with you.
Please kindly check the below.


On 2017/1/31 1:12, Alexander Graf wrote:
> On 01/24/2017 08:05 AM, zhichang.yuan wrote:
>> Low-pin-count interface is integrated into some SoCs. The accesses to those
>> peripherals under LPC make use of I/O ports rather than the memory mapped I/O.
>>
>> To drive these devices, this patch introduces a method named indirect-IO.
>> In this method the in/out() accessor in include/asm-generic/io.h will be
>> redefined. When upper layer drivers call in/out() with those known legacy port
>> addresses to access the peripherals, the I/O operations will be routed to the
>> right hooks which are registered specific to the host device, such as LPC.
>> Then the hardware relevant manupulations are finished by the corresponding
>> host.
>>
>> According to the comments on V5, this patch adds a common indirect-IO driver
>> which support this I/O indirection to the generic directory.
>>
>> In the later pathches, some host-relevant drivers are implemented to support
>> the specific I/O hooks and register them.
>> Based on these, the upper layer drivers which depend on in/out() can work well
>> without any extra work or any changes.
>>
>> Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
>> Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
>> Signed-off-by: John Garry <john.garry@huawei.com>
> 
> I like the extio idea. That allows us to handle all PIO requests on platforms that don't have native PIO support via different routes depending on the region they're in. Unfortunately we now we have 2 frameworks for handling sparse PIO regions: One in extio, one in PCI.
> 
> Why don't we just merge the two? Most of the code that has #ifdef PCI_IOBASE throughout the code base sounds like an ideal candidate to get migrated to extio instead. Then we only have a single framework to worry about ...
> 
>> ---
>>   include/asm-generic/io.h |  50 ++++++++++++++++
>>   include/linux/extio.h    |  85 +++++++++++++++++++++++++++
>>   include/linux/io.h       |   1 +
>>   lib/Kconfig              |   8 +++
>>   lib/Makefile             |   2 +
>>   lib/extio.c              | 147 +++++++++++++++++++++++++++++++++++++++++++++++
>>   6 files changed, 293 insertions(+)
>>   create mode 100644 include/linux/extio.h
>>   create mode 100644 lib/extio.c
>>
>> diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h
>> index 7ef015e..c0d6db1 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/extio.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_INDIRECT_PIO
>> +#define inb extio_inb
>> +#else
>>   #define inb inb
>>   static inline u8 inb(unsigned long addr)
>>   {
>>       return readb(PCI_IOBASE + addr);
>>   }
>> +#endif /* CONFIG_INDIRECT_PIO */
> 
> ... we would also get rid of all of these constructs. Instead, every architecture that doesn't implement native PIO defines would end up in extio and we would enable it unconditionally for them.

Do you want to implement like these?

#ifndef inb
#define inb extio_inb
#endif

In this way, we don't need the CONFIG_INDIRECT_PIO and make extio as kernel built-in.
I thought these are your ideas, right?


Best,
Zhichang

> 
>>   #endif
>>     #ifndef inw
>> +#ifdef CONFIG_INDIRECT_PIO
>> +#define inw extio_inw
>> +#else
>>   #define inw inw
>>   static inline u16 inw(unsigned long addr)
>>   {
>>       return readw(PCI_IOBASE + addr);
>>   }
>> +#endif /* CONFIG_INDIRECT_PIO */
>>   #endif
>>     #ifndef inl
>> +#ifdef CONFIG_INDIRECT_PIO
>> +#define inl extio_inl
>> +#else
>>   #define inl inl
>>   static inline u32 inl(unsigned long addr)
>>   {
>>       return readl(PCI_IOBASE + addr);
>>   }
>> +#endif /* CONFIG_INDIRECT_PIO */
>>   #endif
>>     #ifndef outb
>> +#ifdef CONFIG_INDIRECT_PIO
>> +#define outb extio_outb
>> +#else
>>   #define outb outb
>>   static inline void outb(u8 value, unsigned long addr)
>>   {
>>       writeb(value, PCI_IOBASE + addr);
>>   }
>> +#endif /* CONFIG_INDIRECT_PIO */
>>   #endif
>>     #ifndef outw
>> +#ifdef CONFIG_INDIRECT_PIO
>> +#define outw extio_outw
>> +#else
>>   #define outw outw
>>   static inline void outw(u16 value, unsigned long addr)
>>   {
>>       writew(value, PCI_IOBASE + addr);
>>   }
>> +#endif /* CONFIG_INDIRECT_PIO */
>>   #endif
>>     #ifndef outl
>> +#ifdef CONFIG_INDIRECT_PIO
>> +#define outl extio_outl
>> +#else
>>   #define outl outl
>>   static inline void outl(u32 value, unsigned long addr)
>>   {
>>       writel(value, PCI_IOBASE + addr);
>>   }
>> +#endif /* CONFIG_INDIRECT_PIO */
>>   #endif
>>     #ifndef inb_p
>> @@ -459,54 +485,78 @@ static inline void outl_p(u32 value, unsigned long addr)
>>    */
>>     #ifndef insb
>> +#ifdef CONFIG_INDIRECT_PIO
>> +#define insb extio_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_INDIRECT_PIO */
>>   #endif
>>     #ifndef insw
>> +#ifdef CONFIG_INDIRECT_PIO
>> +#define insw extio_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_INDIRECT_PIO */
>>   #endif
>>     #ifndef insl
>> +#ifdef CONFIG_INDIRECT_PIO
>> +#define insl extio_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_INDIRECT_PIO */
>>   #endif
>>     #ifndef outsb
>> +#ifdef CONFIG_INDIRECT_PIO
>> +#define outsb extio_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_INDIRECT_PIO */
>>   #endif
>>     #ifndef outsw
>> +#ifdef CONFIG_INDIRECT_PIO
>> +#define outsw extio_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_INDIRECT_PIO */
>>   #endif
>>     #ifndef outsl
>> +#ifdef CONFIG_INDIRECT_PIO
>> +#define outsl extio_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_INDIRECT_PIO */
>>   #endif
>>     #ifndef insb_p
>> diff --git a/include/linux/extio.h b/include/linux/extio.h
>> new file mode 100644
>> index 0000000..2ca7eab
>> --- /dev/null
>> +++ b/include/linux/extio.h
>> @@ -0,0 +1,85 @@
>> +/*
>> + * Copyright (C) 2016 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_EXTIO_H
>> +#define __LINUX_EXTIO_H
>> +
>> +#ifdef __KERNEL__
>> +
>> +#include <linux/fwnode.h>
>> +
>> +struct extio_ops {
>> +    u64 (*pfin)(void *devobj, unsigned long ptaddr,    size_t dlen);
>> +    void (*pfout)(void *devobj, unsigned long ptaddr, u32 outval,
>> +                    size_t dlen);
>> +    u64 (*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);
>> +};
>> +
>> +struct extio_node {
>> +    unsigned long bus_start;    /* bus start address */
>> +    unsigned long io_start;    /* io port token corresponding to bus_start */
>> +    size_t range_size;    /* size of the extio node operating range */
>> +    struct fwnode_handle *fwnode;
>> +    struct list_head list;
>> +    struct extio_ops *ops;    /* ops operating on this node */
>> +    void *devpara;    /* private parameter of the host device */
>> +};
>> +
>> +extern u8 extio_inb(unsigned long addr);
>> +extern void extio_outb(u8 value, unsigned long addr);
>> +extern void extio_outw(u16 value, unsigned long addr);
>> +extern void extio_outl(u32 value, unsigned long addr);
>> +extern u16 extio_inw(unsigned long addr);
>> +extern u32 extio_inl(unsigned long addr);
>> +extern void extio_outb(u8 value, unsigned long addr);
>> +extern void extio_outw(u16 value, unsigned long addr);
>> +extern void extio_outl(u32 value, unsigned long addr);
>> +extern void extio_insb(unsigned long addr, void *buffer, unsigned int count);
>> +extern void extio_insl(unsigned long addr, void *buffer, unsigned int count);
>> +extern void extio_insw(unsigned long addr, void *buffer, unsigned int count);
>> +extern void extio_outsb(unsigned long addr, const void *buffer,
>> +            unsigned int count);
>> +extern void extio_outsw(unsigned long addr, const void *buffer,
>> +            unsigned int count);
>> +extern void extio_outsl(unsigned long addr, const void *buffer,
>> +            unsigned int count);
>> +
>> +#ifdef CONFIG_INDIRECT_PIO
>> +extern struct extio_node *extio_find_node(struct fwnode_handle *node);
>> +
>> +extern unsigned long
>> +extio_translate(struct fwnode_handle *node, unsigned long bus_addr);
>> +#else
>> +static inline struct extio_node *extio_find_node(struct fwnode_handle *node)
>> +{
>> +    return NULL;
>> +}
>> +
>> +static inline unsigned long
>> +extio_translate(struct fwnode_handle *node, unsigned long bus_addr)
>> +{
>> +    return -1;
>> +}
>> +#endif
>> +extern void register_extio(struct extio_node *node);
>> +
>> +#endif /* __KERNEL__ */
>> +#endif /* __LINUX_EXTIO_H */
>> diff --git a/include/linux/io.h b/include/linux/io.h
>> index 82ef36e..6c68478 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/extio.h>
>>     struct device;
>>   struct resource;
>> diff --git a/lib/Kconfig b/lib/Kconfig
>> index 260a80e..1d27c44 100644
>> --- a/lib/Kconfig
>> +++ b/lib/Kconfig
>> @@ -59,6 +59,14 @@ config ARCH_USE_CMPXCHG_LOCKREF
>>   config ARCH_HAS_FAST_MULTIPLIER
>>       bool
>>   +config INDIRECT_PIO
>> +    bool "access peripherals with legacy I/O port"
>> +    depends on PCI
>> +    help
>> +      Support special accessors for ISA I/O devices. This is needed for
>> +      SoCs that have not I/O space and do not support standard MMIO
>> +      read/write for the ISA range.
>> +
>>   config CRC_CCITT
>>       tristate "CRC-CCITT functions"
>>       help
>> diff --git a/lib/Makefile b/lib/Makefile
>> index bc4073a..bf03e05 100644
>> --- a/lib/Makefile
>> +++ b/lib/Makefile
>> @@ -70,6 +70,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_INDIRECT_PIO)    += extio.o
>> +
>>   obj-$(CONFIG_GENERIC_HWEIGHT) += hweight.o
>>     obj-$(CONFIG_BTREE) += btree.o
>> diff --git a/lib/extio.c b/lib/extio.c
>> new file mode 100644
>> index 0000000..46228de
>> --- /dev/null
>> +++ b/lib/extio.c
>> @@ -0,0 +1,147 @@
>> +/*
>> + * Copyright (C) 2016 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/io.h>
>> +#include <linux/spinlock.h>
>> +
>> +static LIST_HEAD(extio_dev_list);
>> +static DEFINE_RWLOCK(extio_list_lock);
> 
> Why not just make the list an RCU list? Then you don't need read locks. We also wouldn't create potential lock contention between devices that could easily have parallel PIO operations (say a PCI device and an LPC device).
> 
>> +
>> +void register_extio(struct extio_node *node)
>> +{
>> +    write_lock(&extio_list_lock);
>> +    list_add_tail(&node->list, &extio_dev_list);
>> +    write_unlock(&extio_list_lock);
>> +}
>> +
>> +static struct extio_node *find_extio_token(unsigned long addr)
>> +{
>> +    struct extio_node *extio_entry;
>> +
>> +    read_lock(&extio_list_lock);
>> +    list_for_each_entry(extio_entry, &extio_dev_list, list) {
>> +        if ((addr < extio_entry->io_start + extio_entry->range_size) &&
>> +            (addr >= extio_entry->io_start))
>> +            break;
>> +    }
>> +    read_unlock(&extio_list_lock);
>> +    return (&extio_entry->list == &extio_dev_list) ? NULL : extio_entry;
>> +}
>> +
>> +struct extio_node *extio_find_node(struct fwnode_handle *node)
>> +{
>> +    struct extio_node *entry;
>> +
>> +    read_lock(&extio_list_lock);
>> +    list_for_each_entry(entry, &extio_dev_list, list) {
>> +        if (entry->fwnode == node)
>> +            break;
>> +    }
>> +    read_unlock(&extio_list_lock);
>> +
>> +    return (&entry->list == &extio_dev_list) ? NULL : entry;
>> +}
>> +
>> +unsigned long extio_translate(struct fwnode_handle *node,
>> +        unsigned long bus_addr)
>> +{
>> +    struct extio_node *entry;
>> +    unsigned long port_id = -1;
>> +
>> +    read_lock(&extio_list_lock);
>> +    list_for_each_entry(entry, &extio_dev_list, list) {
>> +        if (entry->fwnode == node &&
>> +            bus_addr >= entry->bus_start &&
>> +            bus_addr - entry->bus_start < entry->range_size)
>> +            port_id = entry->io_start + bus_addr -
>> +                    entry->bus_start;
>> +    }
>> +    read_unlock(&extio_list_lock);
>> +
>> +    return port_id;
>> +}
>> +
>> +#ifdef PCI_IOBASE
>> +
>> +#define BUILD_EXTIO(bw, type)                        \
>> +type extio_in##bw(unsigned long addr)                    \
>> +{                                    \
>> +    struct extio_node *extio_entry = find_extio_token(addr);    \
>> +                                    \
>> +    if (!extio_entry)                        \
>> +        return read##bw(PCI_IOBASE + addr);            \
>> +    return extio_entry->ops->pfin ?                    \
>> +            extio_entry->ops->pfin(extio_entry->devpara,    \
>> +            addr, sizeof(type)) : -1;            \
>> +}                                    \
>> +                                    \
>> +void extio_out##bw(type value, unsigned long addr)            \
>> +{                                    \
>> +    struct extio_node *extio_entry = find_extio_token(addr);    \
>> +                                    \
>> +    if (!extio_entry)                        \
>> +        write##bw(value, PCI_IOBASE + addr);            \
> 
> All of the fallback code would also disappear as a nice side effect of making pci pio handling a user of extio :).
> 
> 
> Alex
> 
> 
> .
> 


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH V6 1/5] LIB: Indirect ISA/LPC port IO introduced
  2017-01-31 19:37       ` Alexander Graf
  2017-02-01 12:29         ` Gabriele Paoloni
@ 2017-02-13 14:17         ` zhichang.yuan
  2017-02-14 13:17           ` Alexander Graf
  1 sibling, 1 reply; 37+ messages in thread
From: zhichang.yuan @ 2017-02-13 14:17 UTC (permalink / raw)
  To: Alexander Graf, John Garry, catalin.marinas, will.deacon,
	robh+dt, frowand.list, bhelgaas, rafael, mark.rutland,
	brian.starkey, olof, arnd, linux-arm-kernel
  Cc: devicetree, lorenzo.pieralisi, gabriele.paoloni, minyard, benh,
	zhichang.yuan02, liviu.dudau, linux-kernel, xuwei5, linuxarm,
	linux-serial, linux-pci, zourongrong, kantyzc

Hi, Alex,


On 2017/2/1 3:37, Alexander Graf wrote:
> 
> 
> On 31/01/2017 14:32, John Garry wrote:
>> On 30/01/2017 17:12, Alexander Graf wrote:
>>> On 01/24/2017 08:05 AM, zhichang.yuan wrote:
>>>> Low-pin-count interface is integrated into some SoCs. The accesses to
>>>> those
>>>> peripherals under LPC make use of I/O ports rather than the memory
>>>> mapped I/O.
>>>>
>>>> To drive these devices, this patch introduces a method named
>>>> indirect-IO.
>>>> In this method the in/out() accessor in include/asm-generic/io.h will be
>>>> redefined. When upper layer drivers call in/out() with those known
>>>> legacy port
>>>> addresses to access the peripherals, the I/O operations will be routed
>>>> to the
>>>> right hooks which are registered specific to the host device, such as
>>>> LPC.
>>>> Then the hardware relevant manupulations are finished by the
>>>> corresponding
>>>> host.
>>>>
>>>> According to the comments on V5, this patch adds a common indirect-IO
>>>> driver
>>>> which support this I/O indirection to the generic directory.
>>>>
>>>> In the later pathches, some host-relevant drivers are implemented to
>>>> support
>>>> the specific I/O hooks and register them.
>>>> Based on these, the upper layer drivers which depend on in/out() can
>>>> work well
>>>> without any extra work or any changes.
>>>>
>>>> Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
>>>> Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
>>>> Signed-off-by: John Garry <john.garry@huawei.com>
>>>
>>> I like the extio idea. That allows us to handle all PIO requests on
>>> platforms that don't have native PIO support via different routes
>>> depending on the region they're in. Unfortunately we now we have 2
>>> frameworks for handling sparse PIO regions: One in extio, one in PCI.
>>>
>>> Why don't we just merge the two? Most of the code that has #ifdef
>>> PCI_IOBASE throughout the code base sounds like an ideal candidate to
>>> get migrated to extio instead. Then we only have a single framework to
>>> worry about ...
>>
>> To be clear, are you suggesting we merge the functionality from
>> pci_register_io_range(), pci_pio_to_address(), pci_address_to_pio() into
>> extio, so extio manages all PIO?
> 
> Yes, I guess so.
> 
>> And having a single type of node to
>> register PIO ranges, by amalgamating struct extio_node and io_range (as
>> Bjorn mentioned)?
> 
> I'm not quite sure I follow you here. Basically I think you want a generic "non-x86 PIO" framework that PCI just plugs into.
> 
> I don't think that necessarily means you want to statically allocate regions of that PIO space to separate (pseudo-)devices. Instead, everyone shares that space and should be able to fail gracefully if some space is already occupied.
> 
>> It would make sense. We would be somewhat decoupling PIO from PCI.
> 
> Yes :).
> 
>> I think that other architectures, like PPC, and other code would need to
>> be fixed up to handle this.
> 
> I think only PPC, Microblaze and ARM are using this. Grep for PCI_IOBASE. It's not that many.
> 
>> We need to consider all the other challenges/obstacles to this.
> 
> Well, getting our abstraction levels right to me sounds like it's worth the obstacles.
> 
>>
>>>
>>>> ---
>>>>   include/asm-generic/io.h |  50 ++++++++++++++++
>>>>   include/linux/extio.h    |  85 +++++++++++++++++++++++++++
>>>>   include/linux/io.h       |   1 +
>>>>   lib/Kconfig              |   8 +++
>>>>   lib/Makefile             |   2 +
>>>>   lib/extio.c              | 147
>>>> +++++++++++++++++++++++++++++++++++++++++++++++ xc>>   create mode
>>>> 100644 include/linux/extio.h
>>>>   create mode 100644 lib/extio.c
>>>>
>>
>> <snip>
>>
>>>> + * Copyright (C) 2016 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/io.h>
>>>> +#include <linux/spinlock.h>
>>>> +
>>>> +static LIST_HEAD(extio_dev_list);
>>>> +static DEFINE_RWLOCK(extio_list_lock);
>>>
>>> Why not just make the list an RCU list? Then you don't need read locks.
>>> We also wouldn't create potential lock contention between devices that
>>> could easily have parallel PIO operations (say a PCI device and an LPC
>>> device).
>>>
>>
>> OK
>>
>>>> +
>>>> +void register_extio(struct extio_node *node)
>>>> +{
>>>> +    write_lock(&extio_list_lock);
>>>> +    list_add_tail(&node->list, &extio_dev_list);
>>>> +    write_unlock(&extio_list_lock);
>>>> +}
>>>> +
>>>> +static struct extio_node *find_extio_token(unsigned long addr)
>>>> +{
>>>> +    struct extio_node *extio_entry;
>>>> +
>>>> +    read_lock(&extio_list_lock);
>>>> +    list_for_each_entry(extio_entry, &extio_dev_list, list) {
>>>> +        if ((addr < extio_entry->io_start + extio_entry->range_size) &&
>>>> +            (addr >= extio_entry->io_start))
>>>> +            break;
>>>> +    }
>>>> +    read_unlock(&extio_list_lock);
>>>> +    return (&extio_entry->list == &extio_dev_list) ? NULL :
>>>> extio_entry;
>>>> +}
>>>> +
>>>> +struct extio_node *extio_find_node(struct fwnode_handle *node)
>>>> +{
>>>> +    struct extio_node *entry;
>>>> +
>>>> +    read_lock(&extio_list_lock);
>>>> +    list_for_each_entry(entry, &extio_dev_list, list) {
>>>> +        if (entry->fwnode == node)
>>>> +            break;
>>>> +    }
>>>> +    read_unlock(&extio_list_lock);
>>>> +
>>>> +    return (&entry->list == &extio_dev_list) ? NULL : entry;
>>>> +}
>>>> +
>>>> +unsigned long extio_translate(struct fwnode_handle *node,
>>>> +        unsigned long bus_addr)
>>>> +{
>>>> +    struct extio_node *entry;
>>>> +    unsigned long port_id = -1;
>>>> +
>>>> +    read_lock(&extio_list_lock);
>>>> +    list_for_each_entry(entry, &extio_dev_list, list) {
>>>> +        if (entry->fwnode == node &&
>>>> +            bus_addr >= entry->bus_start &&
>>>> +            bus_addr - entry->bus_start < entry->range_size)
>>>> +            port_id = entry->io_start + bus_addr -
>>>> +                    entry->bus_start;
>>>> +    }
>>>> +    read_unlock(&extio_list_lock);
>>>> +
>>>> +    return port_id;
>>>> +}
>>>> +
>>>> +#ifdef PCI_IOBASE
>>>> +
>>>> +#define BUILD_EXTIO(bw, type)                        \
>>>> +type extio_in##bw(unsigned long addr)                    \
>>>> +{                                    \
>>>> +    struct extio_node *extio_entry = find_extio_token(addr);    \
>>>> +                                    \
>>>> +    if (!extio_entry)                        \
>>>> +        return read##bw(PCI_IOBASE + addr);            \
>>>> +    return extio_entry->ops->pfin ?                    \
>>>> +            extio_entry->ops->pfin(extio_entry->devpara,    \
>>>> +            addr, sizeof(type)) : -1;            \
>>>> +}                                    \
>>>> +                                    \
>>>> +void extio_out##bw(type value, unsigned long addr)            \
>>>> +{                                    \
>>>> +    struct extio_node *extio_entry = find_extio_token(addr);    \
>>>> +                                    \
>>>> +    if (!extio_entry)                        \
>>>> +        write##bw(value, PCI_IOBASE + addr);            \
>>>
>>> All of the fallback code would also disappear as a nice side effect of
>>> making pci pio handling a user of extio :).
>>
>> Is your idea that PCI IO space will also register accessors, which would
>> be the same read{b,w,l}/write{b,w,l}?

I am not so sure what is your ideas on this. Do you mean the snippet like these:

#define BUILD_IO(bw, type)					\
type extio_in##bw(unsigned long addr) 				\
{ 								\
	struct io_range *entry = find_io_range(addr); 		\
								\
	if (entry) 						\
		return entry->ops->pfin(entry->devpara, 	\
			addr, sizeof(type)); 			\
	return read##bw(PCI_IOBASE + addr);			\
}

we add the last 'return read##bw(PCI_IOBASE + addr);' to keep the original logic of inX() in asm-generic/io.h;
In above snippet, all the hosts applied extio should register their own ops->pfin().


Thanks,
Zhichang


> 
> Yes. If you need to later on accelerate that bit, you can always do something like
> 
>   if (extio_entry->ops->pfin == pci_extio_in)
>     return pci_extio_in(...);
> 
> which should get you all the prefetcher and branch prediction benefits that the current version gives you. But for starters I'd leave that out, since I doubt it'll have measurable performance impact to go via an indirect function call.
> 
>>
>>>
>>
>> It would be nice to have a quicker way to so the lookup from address to
>> node, as we loop all nodes in find_extio_token() every single time.
> 
> You can always replace the search with a tree. But to me that's an implementation detail that's easy enough to replace in a follow-up patch series.
> 
> 
> Alex
> 
> .
> 


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH V6 4/5] LPC: Support the device-tree LPC host on Hip06/Hip07
  2017-01-30 20:08   ` Alexander Graf
  2017-01-31 10:07     ` John Garry
@ 2017-02-13 14:39     ` zhichang.yuan
  2017-02-14 13:29       ` Alexander Graf
  1 sibling, 1 reply; 37+ messages in thread
From: zhichang.yuan @ 2017-02-13 14:39 UTC (permalink / raw)
  To: Alexander Graf, catalin.marinas, will.deacon, robh+dt,
	frowand.list, bhelgaas, rafael, mark.rutland, brian.starkey,
	olof, arnd, linux-arm-kernel
  Cc: devicetree, lorenzo.pieralisi, gabriele.paoloni, minyard, benh,
	john.garry, liviu.dudau, linux-kernel, xuwei5, linuxarm,
	linux-serial, linux-pci, zourongrong, kantyzc, zhichang.yuan02

Hi, Alex,

Thanks for your review!

John had replied most of your comments, I only mentioned those which haven't made clear.


On 2017/1/31 4:08, Alexander Graf wrote:
> 
> 
> On 24/01/2017 08:05, zhichang.yuan wrote:
>> The low-pin-count(LPC) interface of Hip06/Hip07 accesses the peripherals in
>> I/O port addresses. This patch implements the LPC host controller driver which
>> perform the I/O operations on the underlying hardware.
>> We don't want to touch those existing peripherals' driver, such as ipmi-bt. So
>> this driver applies the indirect-IO introduced in the previous patch after
>> registering an indirect-IO node to the indirect-IO devices list which will be
>> searched in the I/O accessors.
>> As the I/O translations for LPC children depend on the host I/O registration,
>> we should ensure the host I/O registration is finished before all the LPC
>> children scanning. That is why an arch_init() hook was added in this patch.
>>
>> Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
>> Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
>> ---
>>  .../arm/hisilicon/hisilicon-low-pin-count.txt      |  33 ++
>>  MAINTAINERS                                        |   9 +
>>  arch/arm64/boot/dts/hisilicon/hip06-d03.dts        |   4 +
>>  arch/arm64/boot/dts/hisilicon/hip06.dtsi           |  14 +
>>  drivers/bus/Kconfig                                |   8 +
>>  drivers/bus/Makefile                               |   1 +
>>  drivers/bus/hisi_lpc.c                             | 599 +++++++++++++++++++++
>>  7 files changed, 668 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
>>  create mode 100644 drivers/bus/hisi_lpc.c
>>
>> diff --git a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
>> new file mode 100644
>> index 0000000..213181f
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
>> @@ -0,0 +1,33 @@
>> +Hisilicon Hip06 low-pin-count device
>> +  Hisilicon Hip06 SoCs implement a Low Pin Count (LPC) controller, which
>> +  provides I/O access to some legacy ISA devices.
>> +  Hip06 is based on arm64 architecture where there is no I/O space. So, the
>> +  I/O ports here are not cpu addresses, and there is no 'ranges' property in
>> +  LPC device node.
>> +
>> +Required properties:
>> +- compatible:  value should be as follows:
>> +    (a) "hisilicon,hip06-lpc"
>> +    (b) "hisilicon,hip07-lpc"
>> +- #address-cells: must be 2 which stick to the ISA/EISA binding doc.
>> +- #size-cells: must be 1 which stick to the ISA/EISA binding doc.
>> +- reg: base memory range where the LPC register set is mapped.
>> +
>> +Note:
>> +  The node name before '@' must be "isa" to represent the binding stick to the
>> +  ISA/EISA binding specification.
>> +
>> +Example:
>> +
>> +isa@a01b0000 {
>> +    compatible = "hisilicon,hip06-lpc";
>> +    #address-cells = <2>;
>> +    #size-cells = <1>;
>> +    reg = <0x0 0xa01b0000 0x0 0x1000>;
>> +
>> +    ipmi0: bt@e4 {
>> +        compatible = "ipmi-bt";
>> +        device_type = "ipmi";
>> +        reg = <0x01 0xe4 0x04>;
>> +    };
>> +};
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 26edd83..0153707 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -5855,6 +5855,15 @@ F:    include/uapi/linux/if_hippi.h
>>  F:    net/802/hippi.c
>>  F:    drivers/net/hippi/
>>
>> +HISILICON LPC BUS DRIVER
>> +M:    Zhichang Yuan <yuanzhichang@hisilicon.com>
>> +L:    linux-arm-kernel@lists.infradead.org
>> +W:    http://www.hisilicon.com
>> +S:    Maintained
>> +F:    drivers/bus/hisi_lpc.c
>> +F:    lib/extio.c
>> +F:    Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
>> +
>>  HISILICON NETWORK SUBSYSTEM DRIVER
>>  M:    Yisen Zhuang <yisen.zhuang@huawei.com>
>>  M:    Salil Mehta <salil.mehta@huawei.com>
>> diff --git a/arch/arm64/boot/dts/hisilicon/hip06-d03.dts b/arch/arm64/boot/dts/hisilicon/hip06-d03.dts
>> index 7c4114a..75b2b5c 100644
>> --- a/arch/arm64/boot/dts/hisilicon/hip06-d03.dts
>> +++ b/arch/arm64/boot/dts/hisilicon/hip06-d03.dts
>> @@ -52,3 +52,7 @@
>>  &usb_ehci {
>>      status = "ok";
>>  };
>> +
>> +&ipmi0 {
>> +    status = "ok";
>> +};
>> diff --git a/arch/arm64/boot/dts/hisilicon/hip06.dtsi b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
>> index a049b64..c450f8d 100644
>> --- a/arch/arm64/boot/dts/hisilicon/hip06.dtsi
>> +++ b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
>> @@ -318,6 +318,20 @@
>>          #size-cells = <2>;
>>          ranges;
>>
>> +        isa@a01b0000 {
>> +            compatible = "hisilicon,hip06-lpc";
>> +            #size-cells = <1>;
>> +            #address-cells = <2>;
>> +            reg = <0x0 0xa01b0000 0x0 0x1000>;
>> +
>> +            ipmi0: bt@e4 {
>> +                compatible = "ipmi-bt";
>> +                device_type = "ipmi";
>> +                reg = <0x01 0xe4 0x04>;
>> +                status = "disabled";
>> +            };
>> +        };
>> +
>>          refclk: refclk {
>>              compatible = "fixed-clock";
>>              clock-frequency = <50000000>;
>> diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
>> index b9e8cfc..58cee84 100644
>> --- a/drivers/bus/Kconfig
>> +++ b/drivers/bus/Kconfig
>> @@ -64,6 +64,14 @@ config BRCMSTB_GISB_ARB
>>        arbiter. This driver provides timeout and target abort error handling
>>        and internal bus master decoding.
>>
>> +config HISILICON_LPC
>> +    bool "Workaround for nonstandard ISA I/O space on Hisilicon Hip0X"
> 
> It's not a workaround, it's support. Better word it like
> 
>   "Support for ISA I/O space on Hisilicon HIP0X"
> 
>> +    depends on (ARM64 && ARCH_HISI && PCI) || COMPILE_TEST
>> +    select INDIRECT_PIO
>> +    help
>> +      Driver needed for some legacy ISA devices attached to Low-Pin-Count
>> +      on Hisilicon Hip0X SoC.
>> +
>>  config IMX_WEIM
>>      bool "Freescale EIM DRIVER"
>>      depends on ARCH_MXC
>> diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
>> index cc6364b..28e3862 100644
>> --- a/drivers/bus/Makefile
>> +++ b/drivers/bus/Makefile
>> @@ -7,6 +7,7 @@ obj-$(CONFIG_ARM_CCI)        += arm-cci.o
>>  obj-$(CONFIG_ARM_CCN)        += arm-ccn.o
>>
>>  obj-$(CONFIG_BRCMSTB_GISB_ARB)    += brcmstb_gisb.o
>> +obj-$(CONFIG_HISILICON_LPC)    += hisi_lpc.o
>>  obj-$(CONFIG_IMX_WEIM)        += imx-weim.o
>>  obj-$(CONFIG_MIPS_CDMM)        += mips_cdmm.o
>>  obj-$(CONFIG_MVEBU_MBUS)     += mvebu-mbus.o
>> diff --git a/drivers/bus/hisi_lpc.c b/drivers/bus/hisi_lpc.c
>> new file mode 100644
>> index 0000000..a96e384
>> --- /dev/null
>> +++ b/drivers/bus/hisi_lpc.c
>> @@ -0,0 +1,599 @@
>> +/*
>> + * Copyright (C) 2016 Hisilicon Limited, All Rights Reserved.
>> + * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
>> + * Author: Zou Rongrong <zourongrong@huawei.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/acpi.h>
>> +#include <linux/console.h>
>> +#include <linux/delay.h>
>> +#include <linux/io.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_address.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/pci.h>
>> +#include <linux/serial_8250.h>
>> +#include <linux/slab.h>
>> +
>> +/*
>> + * Setting this bit means each IO operation will target to a
>> + * different port address:
>> + * 0 means repeatedly IO operations will stick on the same port,
>> + * such as BT;
>> + */
>> +#define FG_INCRADDR_LPC        0x02
>> +
>> +struct lpc_cycle_para {
>> +    unsigned int opflags;
>> +    unsigned int csize; /* the data length of each operation */
>> +};
>> +
>> +struct hisilpc_dev {
>> +    spinlock_t cycle_lock;
>> +    void __iomem  *membase;
>> +    struct extio_node *extio;
>> +};
>> +
>> +/* bounds of the LPC bus address range */
>> +#define LPC_MIN_BUS_RANGE    0x0
>> +
>> +/*
>> + * The maximal IO size for each leagcy bus.
> 
> legacy?
> 
> I don't really understand why this bus is legacy though. It looks like a simple MMIO-to-LPC bridge to me.

yes. The LPC bus is MMIO-to-LPC bridge.
My comment is not clear.

How about "The maximal IO size of LPC bus"?

> 
>> + * The port size of legacy I/O devices is normally less than 0x400.
>> + * Defining the I/O range size as 0x400 here should be sufficient for
>> + * all peripherals under one bus.
>> + */
> 
> This comment doesn't make a lot of sense. What is the limit? Is there a hardware limit?
> 
> We don't dynamically allocate devices on the lpc bus, so why imply a limit at all?

The limit here is to define an IO window for LPC. 
It is not acceptable to describe the IO window with 'ranges' property in LPC host node, so we choose a fixable upper IO limit
for the static LPC IO space. Without this, LPC can't register its IO space through extio helpers.

Why 0x400 is chosen? For our LPC, 0x400 cover all the peripherals under the LPC.




> 
>> +#define LPC_BUS_IO_SIZE        0x400
>> +
>> +/* The maximum continuous operations */
>> +#define LPC_MAX_OPCNT    16
>> +/* only support IO data unit length is four at maximum */
>> +#define LPC_MAX_DULEN    4
>> +#if LPC_MAX_DULEN > LPC_MAX_OPCNT
>> +#error "LPC.. MAX_DULEN must be not bigger than MAX_OPCNT!"
>> +#endif
>> +
>> +#define LPC_REG_START        0x00 /* start a new LPC cycle */
>> +#define LPC_REG_OP_STATUS    0x04 /* the current LPC status */
>> +#define LPC_REG_IRQ_ST        0x08 /* interrupt enable&status */
>> +#define LPC_REG_OP_LEN        0x10 /* how many LPC cycles each start */
>> +#define LPC_REG_CMD        0x14 /* command for the required LPC cycle */
>> +#define LPC_REG_ADDR        0x20 /* LPC target address */
>> +#define LPC_REG_WDATA        0x24 /* data to be written */
>> +#define LPC_REG_RDATA        0x28 /* data coming from peer */
>> +
>> +
>> +/* The command register fields */
>> +#define LPC_CMD_SAMEADDR    0x08
>> +#define LPC_CMD_TYPE_IO        0x00
>> +#define LPC_CMD_WRITE        0x01
>> +#define LPC_CMD_READ        0x00
>> +/* the bit attribute is W1C. 1 represents OK. */
>> +#define LPC_STAT_BYIRQ        0x02
>> +
>> +#define LPC_STATUS_IDLE        0x01
>> +#define LPC_OP_FINISHED        0x02
>> +
>> +#define START_WORK        0x01
>> +
>> +/*
>> + * The minimal waiting interval... Suggest it is not less than 10.
>> + * Bigger value probably will lower the performance.
> 
> Are you sure you want this comment to be upstream? :)
> 
>> + */
>> +#define LPC_NSEC_PERWAIT    100
>> +/*
>> + * The maximum waiting time is about 128us.
>> + * The fastest IO cycle time is about 390ns, but the worst case will wait
>> + * for extra 256 lpc clocks, so (256 + 13) * 30ns = 8 us. The maximum
>> + * burst cycles is 16. So, the maximum waiting time is about 128us under
>> + * worst case.
>> + * choose 1300 as the maximum.
>> + */
>> +#define LPC_MAX_WAITCNT        1300
>> +/* About 10us. This is specific for single IO operation, such as inb. */
>> +#define LPC_PEROP_WAITCNT    100
>> +
>> +
>> +static inline int wait_lpc_idle(unsigned char *mbase,
> 
> No need to specify inline.
> 
>> +                unsigned int waitcnt) {
>> +    u32 opstatus;
>> +
>> +    while (waitcnt--) {
>> +        ndelay(LPC_NSEC_PERWAIT);
>> +        opstatus = readl(mbase + LPC_REG_OP_STATUS);
>> +        if (opstatus & LPC_STATUS_IDLE)
>> +            return (opstatus & LPC_OP_FINISHED) ? 0 : (-EIO);
> 
> It's a shame we have to busy loop, but I guess no calling code outside is prepared for rescheduling at this point.
> 
>> +    }
>> +    return -ETIME;
>> +}
>> +
>> +/*
>> + * hisilpc_target_in - trigger a series of lpc cycles to read required data
>> + *               from target peripheral.
>> + * @pdev: pointer to hisi lpc device
>> + * @para: some parameters used to control the lpc I/O operations
>> + * @ptaddr: the lpc I/O target port address
>> + * @buf: where the read back data is stored
>> + * @opcnt: how many I/O operations required in this calling
>> + *
>> + * Only one byte data is read each I/O operation.
>> + *
>> + * Returns 0 on success, non-zero on fail.
>> + *
>> + */
>> +static int
>> +hisilpc_target_in(struct hisilpc_dev *lpcdev, struct lpc_cycle_para *para,
>> +          unsigned long ptaddr, unsigned char *buf,
>> +          unsigned long opcnt)
>> +{
>> +    unsigned long cnt_per_trans;
>> +    unsigned int cmd_word;
>> +    unsigned int waitcnt;
>> +    int ret;
>> +
>> +    if (!buf || !opcnt || !para || !para->csize || !lpcdev)
>> +        return -EINVAL;
>> +
>> +    if (opcnt  > LPC_MAX_OPCNT)
>> +        return -EINVAL;
>> +
>> +    cmd_word = LPC_CMD_TYPE_IO | LPC_CMD_READ;
>> +    waitcnt = LPC_PEROP_WAITCNT;
>> +    if (!(para->opflags & FG_INCRADDR_LPC)) {
>> +        cmd_word |= LPC_CMD_SAMEADDR;
>> +        waitcnt = LPC_MAX_WAITCNT;
>> +    }
>> +
>> +    ret = 0;
>> +    cnt_per_trans = (para->csize == 1) ? opcnt : para->csize;
>> +    for (; opcnt && !ret; cnt_per_trans = para->csize) {
>> +        unsigned long flags;
>> +
>> +        /* whole operation must be atomic */
>> +        spin_lock_irqsave(&lpcdev->cycle_lock, flags);
> 
> Ouch. This is going to kill your RT jitter. Is there no better way?

I think there is no other choices. We have to ensure the I/O cycles triggered in serial mode....

Best,
Zhichang
> 
>> +
>> +        writel(cnt_per_trans, lpcdev->membase + LPC_REG_OP_LEN);
>> +
>> +        writel(cmd_word, lpcdev->membase + LPC_REG_CMD);
>> +
>> +        writel(ptaddr, lpcdev->membase + LPC_REG_ADDR);
>> +
>> +        writel(START_WORK, lpcdev->membase + LPC_REG_START);
>> +
>> +        /* whether the operation is finished */
>> +        ret = wait_lpc_idle(lpcdev->membase, waitcnt);
>> +        if (!ret) {
>> +            opcnt -= cnt_per_trans;
>> +            for (; cnt_per_trans--; buf++)
>> +                *buf = readl(lpcdev->membase + LPC_REG_RDATA);
>> +        }
>> +
>> +        spin_unlock_irqrestore(&lpcdev->cycle_lock, flags);
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +/**
>> + * hisilpc_target_out - trigger a series of lpc cycles to write required
>> + *            data to target peripheral.
>> + * @pdev: pointer to hisi lpc device
>> + * @para: some parameters used to control the lpc I/O operations
>> + * @ptaddr: the lpc I/O target port address
>> + * @buf: where the data to be written is stored
>> + * @opcnt: how many I/O operations required
>> + *
>> + * Only one byte data is read each I/O operation.
>> + *
>> + * Returns 0 on success, non-zero on fail.
>> + *
>> + */
>> +static int
>> +hisilpc_target_out(struct hisilpc_dev *lpcdev, struct lpc_cycle_para *para,
>> +           unsigned long ptaddr, const unsigned char *buf,
>> +           unsigned long opcnt)
>> +{
>> +    unsigned long cnt_per_trans;
>> +    unsigned int cmd_word;
>> +    unsigned int waitcnt;
>> +    int ret;
>> +
>> +    if (!buf || !opcnt || !para || !lpcdev)
>> +        return -EINVAL;
>> +
>> +    if (opcnt > LPC_MAX_OPCNT)
>> +        return -EINVAL;
>> +    /* default is increasing address */
>> +    cmd_word = LPC_CMD_TYPE_IO | LPC_CMD_WRITE;
>> +    waitcnt = LPC_PEROP_WAITCNT;
>> +    if (!(para->opflags & FG_INCRADDR_LPC)) {
>> +        cmd_word |= LPC_CMD_SAMEADDR;
>> +        waitcnt = LPC_MAX_WAITCNT;
>> +    }
>> +
>> +    ret = 0;
>> +    cnt_per_trans = (para->csize == 1) ? opcnt : para->csize;
>> +    for (; opcnt && !ret; cnt_per_trans = para->csize) {
>> +        unsigned long flags;
>> +
>> +        spin_lock_irqsave(&lpcdev->cycle_lock, flags);
> 
> Same thing here
> 
>> +
>> +        writel(cnt_per_trans, lpcdev->membase + LPC_REG_OP_LEN);
>> +        opcnt -= cnt_per_trans;
>> +        for (; cnt_per_trans--; buf++)
>> +            writel(*buf, lpcdev->membase + LPC_REG_WDATA);
>> +
>> +        writel(cmd_word, lpcdev->membase + LPC_REG_CMD);
>> +
>> +        writel(ptaddr, lpcdev->membase + LPC_REG_ADDR);
>> +
>> +        writel(START_WORK, lpcdev->membase + LPC_REG_START);
>> +
>> +        /* whether the operation is finished */
>> +        ret = wait_lpc_idle(lpcdev->membase, waitcnt);
>> +
>> +        spin_unlock_irqrestore(&lpcdev->cycle_lock, flags);
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +static inline unsigned long
> 
> Don't explicitly mention inline, the compiler will figure that out for you.
> 
>> +hisi_lpc_pio_to_addr(struct hisilpc_dev *lpcdev, unsigned long pio)
>> +{
>> +    return pio - lpcdev->extio->io_start + lpcdev->extio->bus_start;
>> +}
>> +
>> +
>> +/**
>> + * hisilpc_comm_in - read/input the data from the I/O peripheral
>> + *             through LPC.
>> + * @devobj: pointer to the device information relevant to LPC controller.
>> + * @pio: the target I/O port address.
>> + * @dlen: the data length required to read from the target I/O port.
>> + *
>> + * when succeed, the data read back is stored in buffer pointed by inbuf.
>> + * For inb, return the data read from I/O or -1 when error occur.
>> + */
>> +static u64 hisilpc_comm_in(void *devobj, unsigned long pio, size_t dlen)
>> +{
>> +    struct hisilpc_dev *lpcdev = devobj;
>> +    struct lpc_cycle_para iopara;
>> +    u32 rd_data;
> 
> rd_data needs to be initialized to 0. Otherwise it may contain stale stack contents and corrupt non-32bit dlen returns.
> 
>> +    unsigned char *newbuf;
>> +    int ret = 0;
>> +    unsigned long ptaddr;
>> +
>> +    if (!lpcdev || !dlen || dlen > LPC_MAX_DULEN ||    (dlen & (dlen - 1)))
>> +        return -1;
> 
> Isn't this -EINVAL?
> 
>> +
>> +    /* the local buffer must be enough for one data unit */
>> +    if (sizeof(rd_data) < dlen)
>> +        return -1;
> 
> Same here.
> 
> Also, the above seems a very convoluted way of saying
> 
>   switch (dlen) {
>   case 1:
>   case 2:
>   case 4:
>     break;
>   default:
>     return -EINVAL;
>   }
> 
> But I guess the way you write it doesn't hurt ;)
> 
> 
> Alex
> 
> .
> 


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH V6 1/5] LIB: Indirect ISA/LPC port IO introduced
  2017-02-13 14:05     ` zhichang.yuan
@ 2017-02-14 13:15       ` Alexander Graf
  0 siblings, 0 replies; 37+ messages in thread
From: Alexander Graf @ 2017-02-14 13:15 UTC (permalink / raw)
  To: zhichang.yuan, catalin.marinas, will.deacon, robh+dt,
	frowand.list, bhelgaas, rafael, mark.rutland, brian.starkey,
	olof, arnd, linux-arm-kernel
  Cc: devicetree, lorenzo.pieralisi, gabriele.paoloni, minyard, benh,
	john.garry, liviu.dudau, linux-kernel, xuwei5, linuxarm,
	linux-serial, linux-pci, zourongrong, kantyzc, zhichang.yuan02



On 13/02/2017 15:05, zhichang.yuan wrote:
> Hi, Alex,
>
> Thanks for your review!
> Sorry for the late response!
> As this patch-set was two weeks ago, it must be painful to check this thread again.
>
> I thought John had discussed with you about most of the comments when I was back from ten days' leave, and I have no more to supplement,
> so...
> But when I started the work on V7, met somethings need to clarify with you.
> Please kindly check the below.
>
>
> On 2017/1/31 1:12, Alexander Graf wrote:
>> On 01/24/2017 08:05 AM, zhichang.yuan wrote:
>>> Low-pin-count interface is integrated into some SoCs. The accesses to those
>>> peripherals under LPC make use of I/O ports rather than the memory mapped I/O.
>>>
>>> To drive these devices, this patch introduces a method named indirect-IO.
>>> In this method the in/out() accessor in include/asm-generic/io.h will be
>>> redefined. When upper layer drivers call in/out() with those known legacy port
>>> addresses to access the peripherals, the I/O operations will be routed to the
>>> right hooks which are registered specific to the host device, such as LPC.
>>> Then the hardware relevant manupulations are finished by the corresponding
>>> host.
>>>
>>> According to the comments on V5, this patch adds a common indirect-IO driver
>>> which support this I/O indirection to the generic directory.
>>>
>>> In the later pathches, some host-relevant drivers are implemented to support
>>> the specific I/O hooks and register them.
>>> Based on these, the upper layer drivers which depend on in/out() can work well
>>> without any extra work or any changes.
>>>
>>> Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
>>> Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
>>> Signed-off-by: John Garry <john.garry@huawei.com>
>>
>> I like the extio idea. That allows us to handle all PIO requests on platforms that don't have native PIO support via different routes depending on the region they're in. Unfortunately we now we have 2 frameworks for handling sparse PIO regions: One in extio, one in PCI.
>>
>> Why don't we just merge the two? Most of the code that has #ifdef PCI_IOBASE throughout the code base sounds like an ideal candidate to get migrated to extio instead. Then we only have a single framework to worry about ...
>>
>>> ---
>>>   include/asm-generic/io.h |  50 ++++++++++++++++
>>>   include/linux/extio.h    |  85 +++++++++++++++++++++++++++
>>>   include/linux/io.h       |   1 +
>>>   lib/Kconfig              |   8 +++
>>>   lib/Makefile             |   2 +
>>>   lib/extio.c              | 147 +++++++++++++++++++++++++++++++++++++++++++++++
>>>   6 files changed, 293 insertions(+)
>>>   create mode 100644 include/linux/extio.h
>>>   create mode 100644 lib/extio.c
>>>
>>> diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h
>>> index 7ef015e..c0d6db1 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/extio.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_INDIRECT_PIO
>>> +#define inb extio_inb
>>> +#else
>>>   #define inb inb
>>>   static inline u8 inb(unsigned long addr)
>>>   {
>>>       return readb(PCI_IOBASE + addr);
>>>   }
>>> +#endif /* CONFIG_INDIRECT_PIO */
>>
>> ... we would also get rid of all of these constructs. Instead, every architecture that doesn't implement native PIO defines would end up in extio and we would enable it unconditionally for them.
>
> Do you want to implement like these?
>
> #ifndef inb
> #define inb extio_inb
> #endif
>
> In this way, we don't need the CONFIG_INDIRECT_PIO and make extio as kernel built-in.
> I thought these are your ideas, right?

That's definitely one way of doing it, yes :).

You still don't need extio on architectures that have native PIO or no 
PIO devices at all. So I wouldn't mind if you keep it as a config 
option. That way archs that don't care can reduce their code size.

But I don't feel strongly about it either way.


Alex

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH V6 1/5] LIB: Indirect ISA/LPC port IO introduced
  2017-02-13 14:17         ` zhichang.yuan
@ 2017-02-14 13:17           ` Alexander Graf
  0 siblings, 0 replies; 37+ messages in thread
From: Alexander Graf @ 2017-02-14 13:17 UTC (permalink / raw)
  To: zhichang.yuan, John Garry, catalin.marinas, will.deacon, robh+dt,
	frowand.list, bhelgaas, rafael, mark.rutland, brian.starkey,
	olof, arnd, linux-arm-kernel
  Cc: devicetree, lorenzo.pieralisi, gabriele.paoloni, minyard, benh,
	zhichang.yuan02, liviu.dudau, linux-kernel, xuwei5, linuxarm,
	linux-serial, linux-pci, zourongrong, kantyzc



On 13/02/2017 15:17, zhichang.yuan wrote:
> Hi, Alex,
>
>
> On 2017/2/1 3:37, Alexander Graf wrote:
>>
>>
>> On 31/01/2017 14:32, John Garry wrote:
>>> On 30/01/2017 17:12, Alexander Graf wrote:
>>>> On 01/24/2017 08:05 AM, zhichang.yuan wrote:
>>>>> Low-pin-count interface is integrated into some SoCs. The accesses to
>>>>> those
>>>>> peripherals under LPC make use of I/O ports rather than the memory
>>>>> mapped I/O.
>>>>>
>>>>> To drive these devices, this patch introduces a method named
>>>>> indirect-IO.
>>>>> In this method the in/out() accessor in include/asm-generic/io.h will be
>>>>> redefined. When upper layer drivers call in/out() with those known
>>>>> legacy port
>>>>> addresses to access the peripherals, the I/O operations will be routed
>>>>> to the
>>>>> right hooks which are registered specific to the host device, such as
>>>>> LPC.
>>>>> Then the hardware relevant manupulations are finished by the
>>>>> corresponding
>>>>> host.
>>>>>
>>>>> According to the comments on V5, this patch adds a common indirect-IO
>>>>> driver
>>>>> which support this I/O indirection to the generic directory.
>>>>>
>>>>> In the later pathches, some host-relevant drivers are implemented to
>>>>> support
>>>>> the specific I/O hooks and register them.
>>>>> Based on these, the upper layer drivers which depend on in/out() can
>>>>> work well
>>>>> without any extra work or any changes.
>>>>>
>>>>> Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
>>>>> Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
>>>>> Signed-off-by: John Garry <john.garry@huawei.com>
>>>>
>>>> I like the extio idea. That allows us to handle all PIO requests on
>>>> platforms that don't have native PIO support via different routes
>>>> depending on the region they're in. Unfortunately we now we have 2
>>>> frameworks for handling sparse PIO regions: One in extio, one in PCI.
>>>>
>>>> Why don't we just merge the two? Most of the code that has #ifdef
>>>> PCI_IOBASE throughout the code base sounds like an ideal candidate to
>>>> get migrated to extio instead. Then we only have a single framework to
>>>> worry about ...
>>>
>>> To be clear, are you suggesting we merge the functionality from
>>> pci_register_io_range(), pci_pio_to_address(), pci_address_to_pio() into
>>> extio, so extio manages all PIO?
>>
>> Yes, I guess so.
>>
>>> And having a single type of node to
>>> register PIO ranges, by amalgamating struct extio_node and io_range (as
>>> Bjorn mentioned)?
>>
>> I'm not quite sure I follow you here. Basically I think you want a generic "non-x86 PIO" framework that PCI just plugs into.
>>
>> I don't think that necessarily means you want to statically allocate regions of that PIO space to separate (pseudo-)devices. Instead, everyone shares that space and should be able to fail gracefully if some space is already occupied.
>>
>>> It would make sense. We would be somewhat decoupling PIO from PCI.
>>
>> Yes :).
>>
>>> I think that other architectures, like PPC, and other code would need to
>>> be fixed up to handle this.
>>
>> I think only PPC, Microblaze and ARM are using this. Grep for PCI_IOBASE. It's not that many.
>>
>>> We need to consider all the other challenges/obstacles to this.
>>
>> Well, getting our abstraction levels right to me sounds like it's worth the obstacles.
>>
>>>
>>>>
>>>>> ---
>>>>>   include/asm-generic/io.h |  50 ++++++++++++++++
>>>>>   include/linux/extio.h    |  85 +++++++++++++++++++++++++++
>>>>>   include/linux/io.h       |   1 +
>>>>>   lib/Kconfig              |   8 +++
>>>>>   lib/Makefile             |   2 +
>>>>>   lib/extio.c              | 147
>>>>> +++++++++++++++++++++++++++++++++++++++++++++++ xc>>   create mode
>>>>> 100644 include/linux/extio.h
>>>>>   create mode 100644 lib/extio.c
>>>>>
>>>
>>> <snip>
>>>
>>>>> + * Copyright (C) 2016 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/io.h>
>>>>> +#include <linux/spinlock.h>
>>>>> +
>>>>> +static LIST_HEAD(extio_dev_list);
>>>>> +static DEFINE_RWLOCK(extio_list_lock);
>>>>
>>>> Why not just make the list an RCU list? Then you don't need read locks.
>>>> We also wouldn't create potential lock contention between devices that
>>>> could easily have parallel PIO operations (say a PCI device and an LPC
>>>> device).
>>>>
>>>
>>> OK
>>>
>>>>> +
>>>>> +void register_extio(struct extio_node *node)
>>>>> +{
>>>>> +    write_lock(&extio_list_lock);
>>>>> +    list_add_tail(&node->list, &extio_dev_list);
>>>>> +    write_unlock(&extio_list_lock);
>>>>> +}
>>>>> +
>>>>> +static struct extio_node *find_extio_token(unsigned long addr)
>>>>> +{
>>>>> +    struct extio_node *extio_entry;
>>>>> +
>>>>> +    read_lock(&extio_list_lock);
>>>>> +    list_for_each_entry(extio_entry, &extio_dev_list, list) {
>>>>> +        if ((addr < extio_entry->io_start + extio_entry->range_size) &&
>>>>> +            (addr >= extio_entry->io_start))
>>>>> +            break;
>>>>> +    }
>>>>> +    read_unlock(&extio_list_lock);
>>>>> +    return (&extio_entry->list == &extio_dev_list) ? NULL :
>>>>> extio_entry;
>>>>> +}
>>>>> +
>>>>> +struct extio_node *extio_find_node(struct fwnode_handle *node)
>>>>> +{
>>>>> +    struct extio_node *entry;
>>>>> +
>>>>> +    read_lock(&extio_list_lock);
>>>>> +    list_for_each_entry(entry, &extio_dev_list, list) {
>>>>> +        if (entry->fwnode == node)
>>>>> +            break;
>>>>> +    }
>>>>> +    read_unlock(&extio_list_lock);
>>>>> +
>>>>> +    return (&entry->list == &extio_dev_list) ? NULL : entry;
>>>>> +}
>>>>> +
>>>>> +unsigned long extio_translate(struct fwnode_handle *node,
>>>>> +        unsigned long bus_addr)
>>>>> +{
>>>>> +    struct extio_node *entry;
>>>>> +    unsigned long port_id = -1;
>>>>> +
>>>>> +    read_lock(&extio_list_lock);
>>>>> +    list_for_each_entry(entry, &extio_dev_list, list) {
>>>>> +        if (entry->fwnode == node &&
>>>>> +            bus_addr >= entry->bus_start &&
>>>>> +            bus_addr - entry->bus_start < entry->range_size)
>>>>> +            port_id = entry->io_start + bus_addr -
>>>>> +                    entry->bus_start;
>>>>> +    }
>>>>> +    read_unlock(&extio_list_lock);
>>>>> +
>>>>> +    return port_id;
>>>>> +}
>>>>> +
>>>>> +#ifdef PCI_IOBASE
>>>>> +
>>>>> +#define BUILD_EXTIO(bw, type)                        \
>>>>> +type extio_in##bw(unsigned long addr)                    \
>>>>> +{                                    \
>>>>> +    struct extio_node *extio_entry = find_extio_token(addr);    \
>>>>> +                                    \
>>>>> +    if (!extio_entry)                        \
>>>>> +        return read##bw(PCI_IOBASE + addr);            \
>>>>> +    return extio_entry->ops->pfin ?                    \
>>>>> +            extio_entry->ops->pfin(extio_entry->devpara,    \
>>>>> +            addr, sizeof(type)) : -1;            \
>>>>> +}                                    \
>>>>> +                                    \
>>>>> +void extio_out##bw(type value, unsigned long addr)            \
>>>>> +{                                    \
>>>>> +    struct extio_node *extio_entry = find_extio_token(addr);    \
>>>>> +                                    \
>>>>> +    if (!extio_entry)                        \
>>>>> +        write##bw(value, PCI_IOBASE + addr);            \
>>>>
>>>> All of the fallback code would also disappear as a nice side effect of
>>>> making pci pio handling a user of extio :).
>>>
>>> Is your idea that PCI IO space will also register accessors, which would
>>> be the same read{b,w,l}/write{b,w,l}?
>
> I am not so sure what is your ideas on this. Do you mean the snippet like these:
>
> #define BUILD_IO(bw, type)					\
> type extio_in##bw(unsigned long addr) 				\
> { 								\
> 	struct io_range *entry = find_io_range(addr); 		\
> 								\
> 	if (entry) 						\
> 		return entry->ops->pfin(entry->devpara, 	\
> 			addr, sizeof(type)); 			\
> 	return read##bw(PCI_IOBASE + addr);			\
> }
>
> we add the last 'return read##bw(PCI_IOBASE + addr);' to keep the original logic of inX() in asm-generic/io.h;
> In above snippet, all the hosts applied extio should register their own ops->pfin().

Right, PCI would just register its own ops->pfin() which then calls an 
MMIO read function relative to *its own* PCI PIO window. There's no 
reason we couldn't have 2 PCI root complexes in a system. Then you would 
end up with 2 PIO spaces - one for each PCI bus.


Alex

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH V6 4/5] LPC: Support the device-tree LPC host on Hip06/Hip07
  2017-02-13 14:39     ` zhichang.yuan
@ 2017-02-14 13:29       ` Alexander Graf
  2017-02-15 11:35         ` zhichang.yuan
  0 siblings, 1 reply; 37+ messages in thread
From: Alexander Graf @ 2017-02-14 13:29 UTC (permalink / raw)
  To: zhichang.yuan, catalin.marinas, will.deacon, robh+dt,
	frowand.list, bhelgaas, rafael, mark.rutland, brian.starkey,
	olof, arnd, linux-arm-kernel
  Cc: devicetree, lorenzo.pieralisi, gabriele.paoloni, minyard, benh,
	john.garry, liviu.dudau, linux-kernel, xuwei5, linuxarm,
	linux-serial, linux-pci, zourongrong, kantyzc, zhichang.yuan02



On 13/02/2017 15:39, zhichang.yuan wrote:
> Hi, Alex,
>
> Thanks for your review!
>
> John had replied most of your comments, I only mentioned those which haven't made clear.
>
>
> On 2017/1/31 4:08, Alexander Graf wrote:
>>
>>
>> On 24/01/2017 08:05, zhichang.yuan wrote:
>>> The low-pin-count(LPC) interface of Hip06/Hip07 accesses the peripherals in
>>> I/O port addresses. This patch implements the LPC host controller driver which
>>> perform the I/O operations on the underlying hardware.
>>> We don't want to touch those existing peripherals' driver, such as ipmi-bt. So
>>> this driver applies the indirect-IO introduced in the previous patch after
>>> registering an indirect-IO node to the indirect-IO devices list which will be
>>> searched in the I/O accessors.
>>> As the I/O translations for LPC children depend on the host I/O registration,
>>> we should ensure the host I/O registration is finished before all the LPC
>>> children scanning. That is why an arch_init() hook was added in this patch.
>>>
>>> Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
>>> Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
>>> ---
>>>  .../arm/hisilicon/hisilicon-low-pin-count.txt      |  33 ++
>>>  MAINTAINERS                                        |   9 +
>>>  arch/arm64/boot/dts/hisilicon/hip06-d03.dts        |   4 +
>>>  arch/arm64/boot/dts/hisilicon/hip06.dtsi           |  14 +
>>>  drivers/bus/Kconfig                                |   8 +
>>>  drivers/bus/Makefile                               |   1 +
>>>  drivers/bus/hisi_lpc.c                             | 599 +++++++++++++++++++++
>>>  7 files changed, 668 insertions(+)
>>>  create mode 100644 Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
>>>  create mode 100644 drivers/bus/hisi_lpc.c
>>>
>>> diff --git a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
>>> new file mode 100644
>>> index 0000000..213181f
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
>>> @@ -0,0 +1,33 @@
>>> +Hisilicon Hip06 low-pin-count device
>>> +  Hisilicon Hip06 SoCs implement a Low Pin Count (LPC) controller, which
>>> +  provides I/O access to some legacy ISA devices.
>>> +  Hip06 is based on arm64 architecture where there is no I/O space. So, the
>>> +  I/O ports here are not cpu addresses, and there is no 'ranges' property in
>>> +  LPC device node.
>>> +
>>> +Required properties:
>>> +- compatible:  value should be as follows:
>>> +    (a) "hisilicon,hip06-lpc"
>>> +    (b) "hisilicon,hip07-lpc"
>>> +- #address-cells: must be 2 which stick to the ISA/EISA binding doc.
>>> +- #size-cells: must be 1 which stick to the ISA/EISA binding doc.
>>> +- reg: base memory range where the LPC register set is mapped.
>>> +
>>> +Note:
>>> +  The node name before '@' must be "isa" to represent the binding stick to the
>>> +  ISA/EISA binding specification.
>>> +
>>> +Example:
>>> +
>>> +isa@a01b0000 {
>>> +    compatible = "hisilicon,hip06-lpc";
>>> +    #address-cells = <2>;
>>> +    #size-cells = <1>;
>>> +    reg = <0x0 0xa01b0000 0x0 0x1000>;
>>> +
>>> +    ipmi0: bt@e4 {
>>> +        compatible = "ipmi-bt";
>>> +        device_type = "ipmi";
>>> +        reg = <0x01 0xe4 0x04>;
>>> +    };
>>> +};
>>> diff --git a/MAINTAINERS b/MAINTAINERS
>>> index 26edd83..0153707 100644
>>> --- a/MAINTAINERS
>>> +++ b/MAINTAINERS
>>> @@ -5855,6 +5855,15 @@ F:    include/uapi/linux/if_hippi.h
>>>  F:    net/802/hippi.c
>>>  F:    drivers/net/hippi/
>>>
>>> +HISILICON LPC BUS DRIVER
>>> +M:    Zhichang Yuan <yuanzhichang@hisilicon.com>
>>> +L:    linux-arm-kernel@lists.infradead.org
>>> +W:    http://www.hisilicon.com
>>> +S:    Maintained
>>> +F:    drivers/bus/hisi_lpc.c
>>> +F:    lib/extio.c
>>> +F:    Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
>>> +
>>>  HISILICON NETWORK SUBSYSTEM DRIVER
>>>  M:    Yisen Zhuang <yisen.zhuang@huawei.com>
>>>  M:    Salil Mehta <salil.mehta@huawei.com>
>>> diff --git a/arch/arm64/boot/dts/hisilicon/hip06-d03.dts b/arch/arm64/boot/dts/hisilicon/hip06-d03.dts
>>> index 7c4114a..75b2b5c 100644
>>> --- a/arch/arm64/boot/dts/hisilicon/hip06-d03.dts
>>> +++ b/arch/arm64/boot/dts/hisilicon/hip06-d03.dts
>>> @@ -52,3 +52,7 @@
>>>  &usb_ehci {
>>>      status = "ok";
>>>  };
>>> +
>>> +&ipmi0 {
>>> +    status = "ok";
>>> +};
>>> diff --git a/arch/arm64/boot/dts/hisilicon/hip06.dtsi b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
>>> index a049b64..c450f8d 100644
>>> --- a/arch/arm64/boot/dts/hisilicon/hip06.dtsi
>>> +++ b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
>>> @@ -318,6 +318,20 @@
>>>          #size-cells = <2>;
>>>          ranges;
>>>
>>> +        isa@a01b0000 {
>>> +            compatible = "hisilicon,hip06-lpc";
>>> +            #size-cells = <1>;
>>> +            #address-cells = <2>;
>>> +            reg = <0x0 0xa01b0000 0x0 0x1000>;
>>> +
>>> +            ipmi0: bt@e4 {
>>> +                compatible = "ipmi-bt";
>>> +                device_type = "ipmi";
>>> +                reg = <0x01 0xe4 0x04>;
>>> +                status = "disabled";
>>> +            };
>>> +        };
>>> +
>>>          refclk: refclk {
>>>              compatible = "fixed-clock";
>>>              clock-frequency = <50000000>;
>>> diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
>>> index b9e8cfc..58cee84 100644
>>> --- a/drivers/bus/Kconfig
>>> +++ b/drivers/bus/Kconfig
>>> @@ -64,6 +64,14 @@ config BRCMSTB_GISB_ARB
>>>        arbiter. This driver provides timeout and target abort error handling
>>>        and internal bus master decoding.
>>>
>>> +config HISILICON_LPC
>>> +    bool "Workaround for nonstandard ISA I/O space on Hisilicon Hip0X"
>>
>> It's not a workaround, it's support. Better word it like
>>
>>   "Support for ISA I/O space on Hisilicon HIP0X"
>>
>>> +    depends on (ARM64 && ARCH_HISI && PCI) || COMPILE_TEST
>>> +    select INDIRECT_PIO
>>> +    help
>>> +      Driver needed for some legacy ISA devices attached to Low-Pin-Count
>>> +      on Hisilicon Hip0X SoC.
>>> +
>>>  config IMX_WEIM
>>>      bool "Freescale EIM DRIVER"
>>>      depends on ARCH_MXC
>>> diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
>>> index cc6364b..28e3862 100644
>>> --- a/drivers/bus/Makefile
>>> +++ b/drivers/bus/Makefile
>>> @@ -7,6 +7,7 @@ obj-$(CONFIG_ARM_CCI)        += arm-cci.o
>>>  obj-$(CONFIG_ARM_CCN)        += arm-ccn.o
>>>
>>>  obj-$(CONFIG_BRCMSTB_GISB_ARB)    += brcmstb_gisb.o
>>> +obj-$(CONFIG_HISILICON_LPC)    += hisi_lpc.o
>>>  obj-$(CONFIG_IMX_WEIM)        += imx-weim.o
>>>  obj-$(CONFIG_MIPS_CDMM)        += mips_cdmm.o
>>>  obj-$(CONFIG_MVEBU_MBUS)     += mvebu-mbus.o
>>> diff --git a/drivers/bus/hisi_lpc.c b/drivers/bus/hisi_lpc.c
>>> new file mode 100644
>>> index 0000000..a96e384
>>> --- /dev/null
>>> +++ b/drivers/bus/hisi_lpc.c
>>> @@ -0,0 +1,599 @@
>>> +/*
>>> + * Copyright (C) 2016 Hisilicon Limited, All Rights Reserved.
>>> + * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
>>> + * Author: Zou Rongrong <zourongrong@huawei.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/acpi.h>
>>> +#include <linux/console.h>
>>> +#include <linux/delay.h>
>>> +#include <linux/io.h>
>>> +#include <linux/module.h>
>>> +#include <linux/of.h>
>>> +#include <linux/of_address.h>
>>> +#include <linux/of_platform.h>
>>> +#include <linux/pci.h>
>>> +#include <linux/serial_8250.h>
>>> +#include <linux/slab.h>
>>> +
>>> +/*
>>> + * Setting this bit means each IO operation will target to a
>>> + * different port address:
>>> + * 0 means repeatedly IO operations will stick on the same port,
>>> + * such as BT;
>>> + */
>>> +#define FG_INCRADDR_LPC        0x02
>>> +
>>> +struct lpc_cycle_para {
>>> +    unsigned int opflags;
>>> +    unsigned int csize; /* the data length of each operation */
>>> +};
>>> +
>>> +struct hisilpc_dev {
>>> +    spinlock_t cycle_lock;
>>> +    void __iomem  *membase;
>>> +    struct extio_node *extio;
>>> +};
>>> +
>>> +/* bounds of the LPC bus address range */
>>> +#define LPC_MIN_BUS_RANGE    0x0
>>> +
>>> +/*
>>> + * The maximal IO size for each leagcy bus.
>>
>> legacy?
>>
>> I don't really understand why this bus is legacy though. It looks like a simple MMIO-to-LPC bridge to me.
>
> yes. The LPC bus is MMIO-to-LPC bridge.
> My comment is not clear.
>
> How about "The maximal IO size of LPC bus"?

That sounds better, but doesn't really tell me what it's about yet ;).

>
>>
>>> + * The port size of legacy I/O devices is normally less than 0x400.
>>> + * Defining the I/O range size as 0x400 here should be sufficient for
>>> + * all peripherals under one bus.
>>> + */
>>
>> This comment doesn't make a lot of sense. What is the limit? Is there a hardware limit?
>>
>> We don't dynamically allocate devices on the lpc bus, so why imply a limit at all?
>
> The limit here is to define an IO window for LPC.
> It is not acceptable to describe the IO window with 'ranges' property in LPC host node, so we choose a fixable upper IO limit
> for the static LPC IO space. Without this, LPC can't register its IO space through extio helpers.
>
> Why 0x400 is chosen? For our LPC, 0x400 cover all the peripherals under the LPC.

This is information that should come from firmware then. Whether by 
coincidence addresses 0x0 - 0x400 include all devices is an 
implementation detail that the driver shouldn't have to know about.

That said, as mentioned elsewhere, we don't need to define single 
windows at all. Instead, can't we just do it like PCI BARs and declare 
all PIO regions we get from firmware as readily allocated? Then we only 
need to explicitly assign ports 0x80, 0x81, 0x99, etc to the LPC bridge 
and leave the rest for grabs (by PCI hotplug for example)

>
>
>
>
>>
>>> +#define LPC_BUS_IO_SIZE        0x400
>>> +
>>> +/* The maximum continuous operations */
>>> +#define LPC_MAX_OPCNT    16
>>> +/* only support IO data unit length is four at maximum */
>>> +#define LPC_MAX_DULEN    4
>>> +#if LPC_MAX_DULEN > LPC_MAX_OPCNT
>>> +#error "LPC.. MAX_DULEN must be not bigger than MAX_OPCNT!"
>>> +#endif
>>> +
>>> +#define LPC_REG_START        0x00 /* start a new LPC cycle */
>>> +#define LPC_REG_OP_STATUS    0x04 /* the current LPC status */
>>> +#define LPC_REG_IRQ_ST        0x08 /* interrupt enable&status */
>>> +#define LPC_REG_OP_LEN        0x10 /* how many LPC cycles each start */
>>> +#define LPC_REG_CMD        0x14 /* command for the required LPC cycle */
>>> +#define LPC_REG_ADDR        0x20 /* LPC target address */
>>> +#define LPC_REG_WDATA        0x24 /* data to be written */
>>> +#define LPC_REG_RDATA        0x28 /* data coming from peer */
>>> +
>>> +
>>> +/* The command register fields */
>>> +#define LPC_CMD_SAMEADDR    0x08
>>> +#define LPC_CMD_TYPE_IO        0x00
>>> +#define LPC_CMD_WRITE        0x01
>>> +#define LPC_CMD_READ        0x00
>>> +/* the bit attribute is W1C. 1 represents OK. */
>>> +#define LPC_STAT_BYIRQ        0x02
>>> +
>>> +#define LPC_STATUS_IDLE        0x01
>>> +#define LPC_OP_FINISHED        0x02
>>> +
>>> +#define START_WORK        0x01
>>> +
>>> +/*
>>> + * The minimal waiting interval... Suggest it is not less than 10.
>>> + * Bigger value probably will lower the performance.
>>
>> Are you sure you want this comment to be upstream? :)
>>
>>> + */
>>> +#define LPC_NSEC_PERWAIT    100
>>> +/*
>>> + * The maximum waiting time is about 128us.
>>> + * The fastest IO cycle time is about 390ns, but the worst case will wait
>>> + * for extra 256 lpc clocks, so (256 + 13) * 30ns = 8 us. The maximum
>>> + * burst cycles is 16. So, the maximum waiting time is about 128us under
>>> + * worst case.
>>> + * choose 1300 as the maximum.
>>> + */
>>> +#define LPC_MAX_WAITCNT        1300
>>> +/* About 10us. This is specific for single IO operation, such as inb. */
>>> +#define LPC_PEROP_WAITCNT    100
>>> +
>>> +
>>> +static inline int wait_lpc_idle(unsigned char *mbase,
>>
>> No need to specify inline.
>>
>>> +                unsigned int waitcnt) {
>>> +    u32 opstatus;
>>> +
>>> +    while (waitcnt--) {
>>> +        ndelay(LPC_NSEC_PERWAIT);
>>> +        opstatus = readl(mbase + LPC_REG_OP_STATUS);
>>> +        if (opstatus & LPC_STATUS_IDLE)
>>> +            return (opstatus & LPC_OP_FINISHED) ? 0 : (-EIO);
>>
>> It's a shame we have to busy loop, but I guess no calling code outside is prepared for rescheduling at this point.
>>
>>> +    }
>>> +    return -ETIME;
>>> +}
>>> +
>>> +/*
>>> + * hisilpc_target_in - trigger a series of lpc cycles to read required data
>>> + *               from target peripheral.
>>> + * @pdev: pointer to hisi lpc device
>>> + * @para: some parameters used to control the lpc I/O operations
>>> + * @ptaddr: the lpc I/O target port address
>>> + * @buf: where the read back data is stored
>>> + * @opcnt: how many I/O operations required in this calling
>>> + *
>>> + * Only one byte data is read each I/O operation.
>>> + *
>>> + * Returns 0 on success, non-zero on fail.
>>> + *
>>> + */
>>> +static int
>>> +hisilpc_target_in(struct hisilpc_dev *lpcdev, struct lpc_cycle_para *para,
>>> +          unsigned long ptaddr, unsigned char *buf,
>>> +          unsigned long opcnt)
>>> +{
>>> +    unsigned long cnt_per_trans;
>>> +    unsigned int cmd_word;
>>> +    unsigned int waitcnt;
>>> +    int ret;
>>> +
>>> +    if (!buf || !opcnt || !para || !para->csize || !lpcdev)
>>> +        return -EINVAL;
>>> +
>>> +    if (opcnt  > LPC_MAX_OPCNT)
>>> +        return -EINVAL;
>>> +
>>> +    cmd_word = LPC_CMD_TYPE_IO | LPC_CMD_READ;
>>> +    waitcnt = LPC_PEROP_WAITCNT;
>>> +    if (!(para->opflags & FG_INCRADDR_LPC)) {
>>> +        cmd_word |= LPC_CMD_SAMEADDR;
>>> +        waitcnt = LPC_MAX_WAITCNT;
>>> +    }
>>> +
>>> +    ret = 0;
>>> +    cnt_per_trans = (para->csize == 1) ? opcnt : para->csize;
>>> +    for (; opcnt && !ret; cnt_per_trans = para->csize) {
>>> +        unsigned long flags;
>>> +
>>> +        /* whole operation must be atomic */
>>> +        spin_lock_irqsave(&lpcdev->cycle_lock, flags);
>>
>> Ouch. This is going to kill your RT jitter. Is there no better way?
>
> I think there is no other choices. We have to ensure the I/O cycles triggered in serial mode....

As nobody else accesses the LPC device in the meantime thanks to the 
lock, you don't need to disable interrupts, right?

IIRC in PREEMPT_RT, a normal spinlock becomes a mutex and allows for 
resched during the LPC transaction.


Alex

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH V6 4/5] LPC: Support the device-tree LPC host on Hip06/Hip07
  2017-02-14 13:29       ` Alexander Graf
@ 2017-02-15 11:35         ` zhichang.yuan
  2017-02-15 11:53           ` Alexander Graf
  0 siblings, 1 reply; 37+ messages in thread
From: zhichang.yuan @ 2017-02-15 11:35 UTC (permalink / raw)
  To: Alexander Graf, catalin.marinas, will.deacon, robh+dt,
	frowand.list, bhelgaas, rafael, mark.rutland, brian.starkey,
	olof, arnd, linux-arm-kernel
  Cc: devicetree, lorenzo.pieralisi, gabriele.paoloni, minyard, benh,
	john.garry, liviu.dudau, linux-kernel, xuwei5, linuxarm,
	Daode Huang(allen),
	linux-serial, linux-pci, zourongrong, kantyzc, zhichang.yuan02

Hi, Alex,


On 2017/2/14 21:29, Alexander Graf wrote:
> 
> 
> On 13/02/2017 15:39, zhichang.yuan wrote:
>> Hi, Alex,
>>
>> Thanks for your review!
>>
>> John had replied most of your comments, I only mentioned those which haven't made clear.
>>
>>
>> On 2017/1/31 4:08, Alexander Graf wrote:
>>>
>>>
>>> On 24/01/2017 08:05, zhichang.yuan wrote:
>>>> The low-pin-count(LPC) interface of Hip06/Hip07 accesses the peripherals in
>>>> I/O port addresses. This patch implements the LPC host controller driver which
>>>> perform the I/O operations on the underlying hardware.
>>>> We don't want to touch those existing peripherals' driver, such as ipmi-bt. So
>>>> this driver applies the indirect-IO introduced in the previous patch after
>>>> registering an indirect-IO node to the indirect-IO devices list which will be
>>>> searched in the I/O accessors.
>>>> As the I/O translations for LPC children depend on the host I/O registration,
>>>> we should ensure the host I/O registration is finished before all the LPC
>>>> children scanning. That is why an arch_init() hook was added in this patch.
>>>>
>>>> Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
>>>> Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
>>>> ---
snip
>>>> +
>>>> +struct lpc_cycle_para {
>>>> +    unsigned int opflags;
>>>> +    unsigned int csize; /* the data length of each operation */
>>>> +};
>>>> +
>>>> +struct hisilpc_dev {
>>>> +    spinlock_t cycle_lock;
>>>> +    void __iomem  *membase;
>>>> +    struct extio_node *extio;
>>>> +};
>>>> +
>>>> +/* bounds of the LPC bus address range */
>>>> +#define LPC_MIN_BUS_RANGE    0x0
>>>> +
>>>> +/*
>>>> + * The maximal IO size for each leagcy bus.
>>>
>>> legacy?
>>>
>>> I don't really understand why this bus is legacy though. It looks like a simple MMIO-to-LPC bridge to me.
>>
>> yes. The LPC bus is MMIO-to-LPC bridge.
>> My comment is not clear.
>>
>> How about "The maximal IO size of LPC bus"?
> 
> That sounds better, but doesn't really tell me what it's about yet ;).
> 
>>
>>>
>>>> + * The port size of legacy I/O devices is normally less than 0x400.
>>>> + * Defining the I/O range size as 0x400 here should be sufficient for
>>>> + * all peripherals under one bus.
>>>> + */
>>>
>>> This comment doesn't make a lot of sense. What is the limit? Is there a hardware limit?
>>>
>>> We don't dynamically allocate devices on the lpc bus, so why imply a limit at all?
>>
>> The limit here is to define an IO window for LPC.
>> It is not acceptable to describe the IO window with 'ranges' property in LPC host node, so we choose a fixable upper IO limit
>> for the static LPC IO space. Without this, LPC can't register its IO space through extio helpers.
>>
>> Why 0x400 is chosen? For our LPC, 0x400 cover all the peripherals under the LPC.
> 
> This is information that should come from firmware then. Whether by coincidence addresses 0x0 - 0x400 include all devices is an implementation detail that the driver shouldn't have to know about.


> 
> That said, as mentioned elsewhere, we don't need to define single windows at all. Instead, can't we just do it like PCI BARs and declare all PIO regions we get from firmware as readily allocated? Then we only need to explicitly assign ports 0x80, 0x81, 0x99, etc to the LPC bridge and leave the rest for grabs (by PCI hotplug for example)
> 

I worry I don't completely catch your idea.
>From my understanding, each PCI host can parse its resource windows from firmware(such as ACPI, dts). But for our LPC, firmware configurations only tell kernel what is the IO resource of the peripherals under LPC. But to register the io range in the similar way of pci_register_io_range(), at least, we need to know the IO range size.
So, how to know this?
There is no similar BAR design in LPC, do we traverse all the peripherals to calculate that?


>>
>>
>>
>>
>>>
>>>> +#define LPC_BUS_IO_SIZE        0x400
>>>> +
>>>> +/* The maximum continuous operations */
>>>> +#define LPC_MAX_OPCNT    16
>>>> +/* only support IO data unit length is four at maximum */
>>>> +#define LPC_MAX_DULEN    4
snip
>>>> + */
>>>> +static int
>>>> +hisilpc_target_in(struct hisilpc_dev *lpcdev, struct lpc_cycle_para *para,
>>>> +          unsigned long ptaddr, unsigned char *buf,
>>>> +          unsigned long opcnt)
>>>> +{
>>>> +    unsigned long cnt_per_trans;
>>>> +    unsigned int cmd_word;
>>>> +    unsigned int waitcnt;
>>>> +    int ret;
>>>> +
>>>> +    if (!buf || !opcnt || !para || !para->csize || !lpcdev)
>>>> +        return -EINVAL;
>>>> +
>>>> +    if (opcnt  > LPC_MAX_OPCNT)
>>>> +        return -EINVAL;
>>>> +
>>>> +    cmd_word = LPC_CMD_TYPE_IO | LPC_CMD_READ;
>>>> +    waitcnt = LPC_PEROP_WAITCNT;
>>>> +    if (!(para->opflags & FG_INCRADDR_LPC)) {
>>>> +        cmd_word |= LPC_CMD_SAMEADDR;
>>>> +        waitcnt = LPC_MAX_WAITCNT;
>>>> +    }
>>>> +
>>>> +    ret = 0;
>>>> +    cnt_per_trans = (para->csize == 1) ? opcnt : para->csize;
>>>> +    for (; opcnt && !ret; cnt_per_trans = para->csize) {
>>>> +        unsigned long flags;
>>>> +
>>>> +        /* whole operation must be atomic */
>>>> +        spin_lock_irqsave(&lpcdev->cycle_lock, flags);
>>>
>>> Ouch. This is going to kill your RT jitter. Is there no better way?
>>
>> I think there is no other choices. We have to ensure the I/O cycles triggered in serial mode....
> 
> As nobody else accesses the LPC device in the meantime thanks to the lock, you don't need to disable interrupts, right?

O. Do you mean that we can replace spin_lock_irqsave with spin_lock?

As we can connect UART to LPC, I worry the LPC can be accessed in the interrupt context.


Thanks,
Zhichang

> 
> IIRC in PREEMPT_RT, a normal spinlock becomes a mutex and allows for resched during the LPC transaction.
> 
> 
> Alex
> 
> .
> 


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH V6 4/5] LPC: Support the device-tree LPC host on Hip06/Hip07
  2017-02-15 11:35         ` zhichang.yuan
@ 2017-02-15 11:53           ` Alexander Graf
  2017-02-16  8:59             ` zhichang.yuan
  0 siblings, 1 reply; 37+ messages in thread
From: Alexander Graf @ 2017-02-15 11:53 UTC (permalink / raw)
  To: zhichang.yuan, catalin.marinas, will.deacon, robh+dt,
	frowand.list, bhelgaas, rafael, mark.rutland, brian.starkey,
	olof, arnd, linux-arm-kernel
  Cc: devicetree, lorenzo.pieralisi, gabriele.paoloni, minyard, benh,
	john.garry, liviu.dudau, linux-kernel, xuwei5, linuxarm,
	Daode Huang(allen),
	linux-serial, linux-pci, zourongrong, kantyzc, zhichang.yuan02



On 15/02/2017 12:35, zhichang.yuan wrote:
> Hi, Alex,
>
>
> On 2017/2/14 21:29, Alexander Graf wrote:
>>
>>
>> On 13/02/2017 15:39, zhichang.yuan wrote:
>>> Hi, Alex,
>>>
>>> Thanks for your review!
>>>
>>> John had replied most of your comments, I only mentioned those which haven't made clear.
>>>
>>>
>>> On 2017/1/31 4:08, Alexander Graf wrote:
>>>>
>>>>
>>>> On 24/01/2017 08:05, zhichang.yuan wrote:
>>>>> The low-pin-count(LPC) interface of Hip06/Hip07 accesses the peripherals in
>>>>> I/O port addresses. This patch implements the LPC host controller driver which
>>>>> perform the I/O operations on the underlying hardware.
>>>>> We don't want to touch those existing peripherals' driver, such as ipmi-bt. So
>>>>> this driver applies the indirect-IO introduced in the previous patch after
>>>>> registering an indirect-IO node to the indirect-IO devices list which will be
>>>>> searched in the I/O accessors.
>>>>> As the I/O translations for LPC children depend on the host I/O registration,
>>>>> we should ensure the host I/O registration is finished before all the LPC
>>>>> children scanning. That is why an arch_init() hook was added in this patch.
>>>>>
>>>>> Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
>>>>> Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
>>>>> ---
> snip
>>>>> +
>>>>> +struct lpc_cycle_para {
>>>>> +    unsigned int opflags;
>>>>> +    unsigned int csize; /* the data length of each operation */
>>>>> +};
>>>>> +
>>>>> +struct hisilpc_dev {
>>>>> +    spinlock_t cycle_lock;
>>>>> +    void __iomem  *membase;
>>>>> +    struct extio_node *extio;
>>>>> +};
>>>>> +
>>>>> +/* bounds of the LPC bus address range */
>>>>> +#define LPC_MIN_BUS_RANGE    0x0
>>>>> +
>>>>> +/*
>>>>> + * The maximal IO size for each leagcy bus.
>>>>
>>>> legacy?
>>>>
>>>> I don't really understand why this bus is legacy though. It looks like a simple MMIO-to-LPC bridge to me.
>>>
>>> yes. The LPC bus is MMIO-to-LPC bridge.
>>> My comment is not clear.
>>>
>>> How about "The maximal IO size of LPC bus"?
>>
>> That sounds better, but doesn't really tell me what it's about yet ;).
>>
>>>
>>>>
>>>>> + * The port size of legacy I/O devices is normally less than 0x400.
>>>>> + * Defining the I/O range size as 0x400 here should be sufficient for
>>>>> + * all peripherals under one bus.
>>>>> + */
>>>>
>>>> This comment doesn't make a lot of sense. What is the limit? Is there a hardware limit?
>>>>
>>>> We don't dynamically allocate devices on the lpc bus, so why imply a limit at all?
>>>
>>> The limit here is to define an IO window for LPC.
>>> It is not acceptable to describe the IO window with 'ranges' property in LPC host node, so we choose a fixable upper IO limit
>>> for the static LPC IO space. Without this, LPC can't register its IO space through extio helpers.
>>>
>>> Why 0x400 is chosen? For our LPC, 0x400 cover all the peripherals under the LPC.
>>
>> This is information that should come from firmware then. Whether by coincidence addresses 0x0 - 0x400 include all devices is an implementation detail that the driver shouldn't have to know about.
>
>
>>
>> That said, as mentioned elsewhere, we don't need to define single windows at all. Instead, can't we just do it like PCI BARs and declare all PIO regions we get from firmware as readily allocated? Then we only need to explicitly assign ports 0x80, 0x81, 0x99, etc to the LPC bridge and leave the rest for grabs (by PCI hotplug for example)
>>
>
> I worry I don't completely catch your idea.
> From my understanding, each PCI host can parse its resource windows from firmware(such as ACPI, dts). But for our LPC, firmware configurations only tell kernel what is the IO resource of the peripherals under LPC. But to register the io range in the similar way of pci_register_io_range(), at least, we need to know the IO range size.
> So, how to know this?
> There is no similar BAR design in LPC, do we traverse all the peripherals to calculate that?

Yes, that's probably the most flexible approach. In fact, I would just 
traverse all peripherals (or have peripherals call register functions 
for PIO regions) and then dynamically allocate the PIO space for only 
those regions. I don't see why you should only have 1 window for your 
LPC bridge.

Alternatively you could just have firmware indicate the region size, 
similar to how it's done for PCI. Whatever you do, don't hard code it in 
the driver :).

>
>
>>>
>>>
>>>
>>>
>>>>
>>>>> +#define LPC_BUS_IO_SIZE        0x400
>>>>> +
>>>>> +/* The maximum continuous operations */
>>>>> +#define LPC_MAX_OPCNT    16
>>>>> +/* only support IO data unit length is four at maximum */
>>>>> +#define LPC_MAX_DULEN    4
> snip
>>>>> + */
>>>>> +static int
>>>>> +hisilpc_target_in(struct hisilpc_dev *lpcdev, struct lpc_cycle_para *para,
>>>>> +          unsigned long ptaddr, unsigned char *buf,
>>>>> +          unsigned long opcnt)
>>>>> +{
>>>>> +    unsigned long cnt_per_trans;
>>>>> +    unsigned int cmd_word;
>>>>> +    unsigned int waitcnt;
>>>>> +    int ret;
>>>>> +
>>>>> +    if (!buf || !opcnt || !para || !para->csize || !lpcdev)
>>>>> +        return -EINVAL;
>>>>> +
>>>>> +    if (opcnt  > LPC_MAX_OPCNT)
>>>>> +        return -EINVAL;
>>>>> +
>>>>> +    cmd_word = LPC_CMD_TYPE_IO | LPC_CMD_READ;
>>>>> +    waitcnt = LPC_PEROP_WAITCNT;
>>>>> +    if (!(para->opflags & FG_INCRADDR_LPC)) {
>>>>> +        cmd_word |= LPC_CMD_SAMEADDR;
>>>>> +        waitcnt = LPC_MAX_WAITCNT;
>>>>> +    }
>>>>> +
>>>>> +    ret = 0;
>>>>> +    cnt_per_trans = (para->csize == 1) ? opcnt : para->csize;
>>>>> +    for (; opcnt && !ret; cnt_per_trans = para->csize) {
>>>>> +        unsigned long flags;
>>>>> +
>>>>> +        /* whole operation must be atomic */
>>>>> +        spin_lock_irqsave(&lpcdev->cycle_lock, flags);
>>>>
>>>> Ouch. This is going to kill your RT jitter. Is there no better way?
>>>
>>> I think there is no other choices. We have to ensure the I/O cycles triggered in serial mode....
>>
>> As nobody else accesses the LPC device in the meantime thanks to the lock, you don't need to disable interrupts, right?
>
> O. Do you mean that we can replace spin_lock_irqsave with spin_lock?

That would be a good step into the right direction I think, yes.

> As we can connect UART to LPC, I worry the LPC can be accessed in the interrupt context.

In that case IRQs are already disabled for the non-preempt-rt case and 
in preempt-rt, interrupt context should mostly live in threads which are 
preemptible again.


Alex

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH V6 4/5] LPC: Support the device-tree LPC host on Hip06/Hip07
  2017-02-15 11:53           ` Alexander Graf
@ 2017-02-16  8:59             ` zhichang.yuan
  0 siblings, 0 replies; 37+ messages in thread
From: zhichang.yuan @ 2017-02-16  8:59 UTC (permalink / raw)
  To: Alexander Graf, catalin.marinas, will.deacon, robh+dt,
	frowand.list, bhelgaas, rafael, mark.rutland, brian.starkey,
	olof, arnd, linux-arm-kernel
  Cc: devicetree, lorenzo.pieralisi, gabriele.paoloni, minyard, benh,
	john.garry, liviu.dudau, linux-kernel, xuwei5, linuxarm,
	Daode Huang(allen),
	linux-serial, linux-pci, zourongrong, kantyzc, zhichang.yuan02

Hi, Alex,

On 2017/2/15 19:53, Alexander Graf wrote:
> 
> 
> On 15/02/2017 12:35, zhichang.yuan wrote:
>> Hi, Alex,
>>
>>
>> On 2017/2/14 21:29, Alexander Graf wrote:
>>>
>>>
>>> On 13/02/2017 15:39, zhichang.yuan wrote:
>>>> Hi, Alex,
>>>>
>>>> Thanks for your review!
>>>>
>>>> John had replied most of your comments, I only mentioned those which haven't made clear.
>>>>
>>>>
>>>> On 2017/1/31 4:08, Alexander Graf wrote:
>>>>>
>>>>>
>>>>> On 24/01/2017 08:05, zhichang.yuan wrote:
>>>>>> The low-pin-count(LPC) interface of Hip06/Hip07 accesses the peripherals in
>>>>>> I/O port addresses. This patch implements the LPC host controller driver which
>>>>>> perform the I/O operations on the underlying hardware.
>>>>>> We don't want to touch those existing peripherals' driver, such as ipmi-bt. So
>>>>>> this driver applies the indirect-IO introduced in the previous patch after
>>>>>> registering an indirect-IO node to the indirect-IO devices list which will be
>>>>>> searched in the I/O accessors.
>>>>>> As the I/O translations for LPC children depend on the host I/O registration,
>>>>>> we should ensure the host I/O registration is finished before all the LPC
>>>>>> children scanning. That is why an arch_init() hook was added in this patch.
>>>>>>
>>>>>> Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
>>>>>> Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
>>>>>> ---
>> snip
>>>>>> +
>>>>>> +struct lpc_cycle_para {
>>>>>> +    unsigned int opflags;
>>>>>> +    unsigned int csize; /* the data length of each operation */
>>>>>> +};
>>>>>> +
>>>>>> +struct hisilpc_dev {
>>>>>> +    spinlock_t cycle_lock;
>>>>>> +    void __iomem  *membase;
>>>>>> +    struct extio_node *extio;
>>>>>> +};
>>>>>> +
>>>>>> +/* bounds of the LPC bus address range */
>>>>>> +#define LPC_MIN_BUS_RANGE    0x0
>>>>>> +
>>>>>> +/*
>>>>>> + * The maximal IO size for each leagcy bus.
>>>>>
>>>>> legacy?
>>>>>
>>>>> I don't really understand why this bus is legacy though. It looks like a simple MMIO-to-LPC bridge to me.
>>>>
>>>> yes. The LPC bus is MMIO-to-LPC bridge.
>>>> My comment is not clear.
>>>>
>>>> How about "The maximal IO size of LPC bus"?
>>>
>>> That sounds better, but doesn't really tell me what it's about yet ;).
>>>
>>>>
>>>>>
>>>>>> + * The port size of legacy I/O devices is normally less than 0x400.
>>>>>> + * Defining the I/O range size as 0x400 here should be sufficient for
>>>>>> + * all peripherals under one bus.
>>>>>> + */
>>>>>
>>>>> This comment doesn't make a lot of sense. What is the limit? Is there a hardware limit?
>>>>>
>>>>> We don't dynamically allocate devices on the lpc bus, so why imply a limit at all?
>>>>
>>>> The limit here is to define an IO window for LPC.
>>>> It is not acceptable to describe the IO window with 'ranges' property in LPC host node, so we choose a fixable upper IO limit
>>>> for the static LPC IO space. Without this, LPC can't register its IO space through extio helpers.
>>>>
>>>> Why 0x400 is chosen? For our LPC, 0x400 cover all the peripherals under the LPC.
>>>
>>> This is information that should come from firmware then. Whether by coincidence addresses 0x0 - 0x400 include all devices is an implementation detail that the driver shouldn't have to know about.
>>
>>
>>>
>>> That said, as mentioned elsewhere, we don't need to define single windows at all. Instead, can't we just do it like PCI BARs and declare all PIO regions we get from firmware as readily allocated? Then we only need to explicitly assign ports 0x80, 0x81, 0x99, etc to the LPC bridge and leave the rest for grabs (by PCI hotplug for example)
>>>
>>
>> I worry I don't completely catch your idea.
>> From my understanding, each PCI host can parse its resource windows from firmware(such as ACPI, dts). But for our LPC, firmware configurations only tell kernel what is the IO resource of the peripherals under LPC. But to register the io range in the similar way of pci_register_io_range(), at least, we need to know the IO range size.
>> So, how to know this?
>> There is no similar BAR design in LPC, do we traverse all the peripherals to calculate that?
> 
> Yes, that's probably the most flexible approach. In fact, I would just traverse all peripherals (or have peripherals call register functions for PIO regions) and then dynamically allocate the PIO space for only those regions. I don't see why you should only have 1 window for your LPC bridge.
> 
> Alternatively you could just have firmware indicate the region size, similar to how it's done for PCI. Whatever you do, don't hard code it in the driver :).
> 
>>
>>
>>>>
>>>>
>>>>
>>>>
>>>>>
>>>>>> +#define LPC_BUS_IO_SIZE        0x400
>>>>>> +
>>>>>> +/* The maximum continuous operations */
>>>>>> +#define LPC_MAX_OPCNT    16
>>>>>> +/* only support IO data unit length is four at maximum */
>>>>>> +#define LPC_MAX_DULEN    4
>> snip
>>>>>> + */
>>>>>> +static int
>>>>>> +hisilpc_target_in(struct hisilpc_dev *lpcdev, struct lpc_cycle_para *para,
>>>>>> +          unsigned long ptaddr, unsigned char *buf,
>>>>>> +          unsigned long opcnt)
>>>>>> +{
>>>>>> +    unsigned long cnt_per_trans;
>>>>>> +    unsigned int cmd_word;
>>>>>> +    unsigned int waitcnt;
>>>>>> +    int ret;
>>>>>> +
>>>>>> +    if (!buf || !opcnt || !para || !para->csize || !lpcdev)
>>>>>> +        return -EINVAL;
>>>>>> +
>>>>>> +    if (opcnt  > LPC_MAX_OPCNT)
>>>>>> +        return -EINVAL;
>>>>>> +
>>>>>> +    cmd_word = LPC_CMD_TYPE_IO | LPC_CMD_READ;
>>>>>> +    waitcnt = LPC_PEROP_WAITCNT;
>>>>>> +    if (!(para->opflags & FG_INCRADDR_LPC)) {
>>>>>> +        cmd_word |= LPC_CMD_SAMEADDR;
>>>>>> +        waitcnt = LPC_MAX_WAITCNT;
>>>>>> +    }
>>>>>> +
>>>>>> +    ret = 0;
>>>>>> +    cnt_per_trans = (para->csize == 1) ? opcnt : para->csize;
>>>>>> +    for (; opcnt && !ret; cnt_per_trans = para->csize) {
>>>>>> +        unsigned long flags;
>>>>>> +
>>>>>> +        /* whole operation must be atomic */
>>>>>> +        spin_lock_irqsave(&lpcdev->cycle_lock, flags);
>>>>>
>>>>> Ouch. This is going to kill your RT jitter. Is there no better way?
>>>>
>>>> I think there is no other choices. We have to ensure the I/O cycles triggered in serial mode....
>>>
>>> As nobody else accesses the LPC device in the meantime thanks to the lock, you don't need to disable interrupts, right?
>>
>> O. Do you mean that we can replace spin_lock_irqsave with spin_lock?
> 
> That would be a good step into the right direction I think, yes.
> 
>> As we can connect UART to LPC, I worry the LPC can be accessed in the interrupt context.
> 
> In that case IRQs are already disabled for the non-preempt-rt case and in preempt-rt, interrupt context should mostly live in threads which are preemptible again.
> 

When the IRQs are disabled? Do you mean the interrupt disable just before kernel enters the interrupt routine?
It seems this processing can not protect a thread which had owned the LPC spin lock from being preempted.


Zhichang

> 
> Alex
> 
> .
> 


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 37+ messages in thread

end of thread, other threads:[~2017-02-16  8:59 UTC | newest]

Thread overview: 37+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-01-24  7:05 [PATCH V6 0/5] LPC: legacy ISA I/O support zhichang.yuan
2017-01-24  7:05 ` [PATCH V6 1/5] LIB: Indirect ISA/LPC port IO introduced zhichang.yuan
2017-01-30 17:12   ` Alexander Graf
2017-01-31 13:32     ` John Garry
2017-01-31 19:37       ` Alexander Graf
2017-02-01 12:29         ` Gabriele Paoloni
2017-02-13 14:17         ` zhichang.yuan
2017-02-14 13:17           ` Alexander Graf
2017-02-13 14:05     ` zhichang.yuan
2017-02-14 13:15       ` Alexander Graf
2017-01-31  0:09   ` Bjorn Helgaas
2017-01-31 13:34     ` John Garry
2017-01-24  7:05 ` [PATCH V6 2/5] PCI: Adapt pci_register_io_range() for indirect-IO and PCI I/O translation zhichang.yuan
2017-01-31  0:10   ` Bjorn Helgaas
2017-01-31 13:39     ` John Garry
2017-01-31  0:15   ` Bjorn Helgaas
2017-02-04 12:59     ` John Garry
2017-02-02 17:36   ` John Garry
2017-02-02 23:00     ` Rafael J. Wysocki
2017-01-24  7:05 ` [PATCH V6 3/5] OF: Add missing I/O range exception for indirect-IO devices zhichang.yuan
2017-01-27 22:03   ` Rob Herring
2017-01-30  8:57     ` John Garry
2017-01-30 10:08       ` Arnd Bergmann
2017-01-24  7:05 ` [PATCH V6 4/5] LPC: Support the device-tree LPC host on Hip06/Hip07 zhichang.yuan
2017-01-27 22:12   ` Rob Herring
2017-01-30 20:08   ` Alexander Graf
2017-01-31 10:07     ` John Garry
2017-01-31 11:03       ` Alexander Graf
2017-01-31 11:49         ` John Garry
2017-01-31 11:51         ` Gabriele Paoloni
2017-02-13 14:39     ` zhichang.yuan
2017-02-14 13:29       ` Alexander Graf
2017-02-15 11:35         ` zhichang.yuan
2017-02-15 11:53           ` Alexander Graf
2017-02-16  8:59             ` zhichang.yuan
2017-01-24  7:05 ` [PATCH V5 5/5] LPC: Add the ACPI LPC support zhichang.yuan
2017-02-04 13:20   ` John Garry

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).